summaryrefslogtreecommitdiff
path: root/fluid
diff options
context:
space:
mode:
authorMatthias Melcher <github@matthiasm.com>2023-10-26 23:31:12 +0200
committerMatthias Melcher <github@matthiasm.com>2023-10-26 23:31:29 +0200
commit0b408792c083eb64a5ca737bdbcb30b1d64042e9 (patch)
tree51b791729ea8be5bb741088cba12c24a9c4034c5 /fluid
parent8663b867497de6d370b9c8376c4bdfc909744663 (diff)
FLUID: Refactors MergeBack
* moved functionality into its own files * refactored all methods to be less than a page * documented all calls * tested all situations I could think of
Diffstat (limited to 'fluid')
-rw-r--r--fluid/CMakeLists.txt2
-rw-r--r--fluid/Fl_Function_Type.cxx2
-rw-r--r--fluid/Fl_Menu_Type.cxx1
-rw-r--r--fluid/Fl_Widget_Type.cxx3
-rw-r--r--fluid/Makefile1
-rw-r--r--fluid/code.cxx379
-rw-r--r--fluid/code.h38
-rw-r--r--fluid/fluid.cxx7
-rw-r--r--fluid/mergeback.cxx476
-rw-r--r--fluid/mergeback.h77
10 files changed, 669 insertions, 317 deletions
diff --git a/fluid/CMakeLists.txt b/fluid/CMakeLists.txt
index c28b2780f..9fc01bebe 100644
--- a/fluid/CMakeLists.txt
+++ b/fluid/CMakeLists.txt
@@ -38,6 +38,7 @@ set (CPPFILES
file.cxx
fluid_filename.cxx
function_panel.cxx
+ mergeback.cxx
pixmaps.cxx
shell_command.cxx
sourceview_panel.cxx
@@ -71,6 +72,7 @@ set (HEADERFILES
factory.h
file.h
function_panel.h
+ mergeback.h
print_panel.h
pixmaps.h
shell_command.h
diff --git a/fluid/Fl_Function_Type.cxx b/fluid/Fl_Function_Type.cxx
index 883212e3e..33932cc93 100644
--- a/fluid/Fl_Function_Type.cxx
+++ b/fluid/Fl_Function_Type.cxx
@@ -24,6 +24,7 @@
#include "code.h"
#include "function_panel.h"
#include "comments.h"
+#include "mergeback.h"
#include <FL/fl_string_functions.h>
#include <FL/Fl_File_Chooser.H>
@@ -353,6 +354,7 @@ static bool fd_isspace(int c) {
return (c>0 && c<128 && isspace(c));
}
+// code duplication: see int is_id(char c) in code.cxx
static bool fd_iskeyword(int c) {
return (c>0 && c<128 && (isalnum(c) || c=='_'));
}
diff --git a/fluid/Fl_Menu_Type.cxx b/fluid/Fl_Menu_Type.cxx
index 938ca0f8f..6e65db871 100644
--- a/fluid/Fl_Menu_Type.cxx
+++ b/fluid/Fl_Menu_Type.cxx
@@ -29,6 +29,7 @@
#include "code.h"
#include "Fluid_Image.h"
#include "custom_widgets.h"
+#include "mergeback.h"
#include <FL/Fl.H>
#include <FL/fl_message.H>
diff --git a/fluid/Fl_Widget_Type.cxx b/fluid/Fl_Widget_Type.cxx
index fb76315fa..11bc15ee3 100644
--- a/fluid/Fl_Widget_Type.cxx
+++ b/fluid/Fl_Widget_Type.cxx
@@ -27,6 +27,7 @@
#include "alignment_panel.h"
#include "widget_panel.h"
#include "undo.h"
+#include "mergeback.h"
#include <FL/Fl.H>
#include <FL/Fl_Group.H>
@@ -2631,7 +2632,7 @@ void live_mode_cb(Fl_Button*o,void *) {
}
// update the panel according to current widget set:
-static void load_panel() {
+void load_panel() {
if (!the_panel) return;
// find all the Fl_Widget subclasses currently selected:
diff --git a/fluid/Makefile b/fluid/Makefile
index 23e44dcbf..0fa834ebf 100644
--- a/fluid/Makefile
+++ b/fluid/Makefile
@@ -39,6 +39,7 @@ CPPFILES = \
fluid.cxx \
fluid_filename.cxx \
function_panel.cxx \
+ mergeback.cxx \
pixmaps.cxx \
shell_command.cxx \
sourceview_panel.cxx \
diff --git a/fluid/code.cxx b/fluid/code.cxx
index ed9046b3c..da35d3abc 100644
--- a/fluid/code.cxx
+++ b/fluid/code.cxx
@@ -33,7 +33,7 @@
#include <stdio.h>
#include <stdlib.h>
-#include "zlib.h"
+#include <zlib.h>
/// \defgroup cfile C Code File Operations
/// \{
@@ -42,13 +42,19 @@
/**
Return true if c can be in a C identifier.
I needed this so it is not messed up by locale settings.
+ \param[in] c a character, or the start of a utf-8 sequence
+ \return 1 if c is alphanumeric or '_'
*/
int is_id(char c) {
return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
}
/**
- Write a file that contains all label and tooltip strings for internationalisation.
+ Write a file that contains all label and tooltip strings for internationalization.
+ The user is responsible to set the right file name extension. The file format
+ is determined by `g_project.i18n_type`.
+ \param[in] filename file path and name to a file that will hold the strings
+ \return 1 if the file could not be opened for writing, or the result of `fclose`.
*/
int write_strings(const Fl_String &filename) {
Fl_Type *p;
@@ -175,6 +181,7 @@ int write_strings(const Fl_String &filename) {
////////////////////////////////////////////////////////////////
// Generate unique but human-readable identifiers:
+/** A binary searchable tree storing identifiers for quick retrieval. */
struct Fd_Identifier_Tree {
char* text;
void* object;
@@ -286,6 +293,7 @@ const char *Fd_Code_Writer::indent_plus(int offset) {
// the header file. This is done by keeping a binary tree of all
// the calls so far and not printing it if it is in the tree.
+/** A binary searchable tree storing text for quick retrieval. */
struct Fd_Text_Tree {
char *text;
Fd_Text_Tree *left, *right;
@@ -302,6 +310,7 @@ Fd_Text_Tree::~Fd_Text_Tree() {
delete right;
}
+/** A binary searchable tree storing pointers for quick retrieval. */
struct Fd_Pointer_Tree {
void *ptr;
Fd_Pointer_Tree *left, *right;
@@ -319,7 +328,9 @@ Fd_Pointer_Tree::~Fd_Pointer_Tree() {
/**
Print a formatted line to the header file, unless the same line was produced before in this header file.
+ \note Resulting line is cropped at 1023 bytes.
\param[in] format printf-style formatting text, followed by a vararg list
+ \return 1 if the text was written to the file, 0 if it was previously written.
*/
int Fd_Code_Writer::write_h_once(const char *format, ...) {
va_list args;
@@ -341,7 +352,9 @@ int Fd_Code_Writer::write_h_once(const char *format, ...) {
/**
Print a formatted line to the source file, unless the same line was produced before in this code file.
+ \note Resulting line is cropped at 1023 bytes.
\param[in] format printf-style formatting text, followed by a vararg list
+ \return 1 if the text was written to the file, 0 if it was previously written.
*/
int Fd_Code_Writer::write_c_once(const char *format, ...) {
va_list args;
@@ -371,6 +384,8 @@ int Fd_Code_Writer::write_c_once(const char *format, ...) {
/**
Return true if this pointer was already included in the code file.
If it was not, add it to the list and return false.
+ \param[in] pp ay pointer
+ \return true if found in the tree, false if added to the tree
*/
bool Fd_Code_Writer::c_contains(void *pp) {
Fd_Pointer_Tree **p = &ptr_in_code;
@@ -506,6 +521,9 @@ void Fd_Code_Writer::write_cstring(const char *s) {
Write an array of C binary data (does not add a null).
The output is bracketed in { and }. The content is written
as decimal bytes, i.e. `{ 1, 2, 200 }`
+
+ \param[in] s a block of binary data, interpreted as unsigned bytes
+ \param[in] length size of the block in bytes
*/
void Fd_Code_Writer::write_cdata(const char *s, int length) {
if (varused_test) {
@@ -615,8 +633,11 @@ void Fd_Code_Writer::write_hc(const char *indent, int n, const char* c, const ch
/**
Write one or more lines of code, indenting each one of them.
\param[in] textlines one or more lines of text, separated by \\n
+ \param[in] inIndent increment indentation by this amount
+ \param[in] inTrailWith append this character if the last line did not end with
+ a newline, usually 0 or newline.
*/
-void Fd_Code_Writer::write_c_indented(const char *textlines, int inIndent, char inTrailwWith) {
+void Fd_Code_Writer::write_c_indented(const char *textlines, int inIndent, char inTrailWith) {
if (textlines) {
indentation += inIndent;
for (;;) {
@@ -638,8 +659,8 @@ void Fd_Code_Writer::write_c_indented(const char *textlines, int inIndent, char
if (newline) {
write_c("\n");
} else {
- if (inTrailwWith)
- write_c("%c", inTrailwWith);
+ if (inTrailWith)
+ write_c("%c", inTrailWith);
break;
}
textlines = newline+1;
@@ -651,9 +672,11 @@ void Fd_Code_Writer::write_c_indented(const char *textlines, int inIndent, char
/**
Recursively dump code, putting children between the two parts of the parent code.
+ \param[in] p write this type and all its children
+ \return pointer to the next sibling
*/
Fl_Type* Fd_Code_Writer::write_code(Fl_Type* p) {
- // write all code that come before the children code
+ // write all code that comes before the children code
// (but don't write the last comment until the very end)
if (!(p==Fl_Type::last && p->is_a(ID_Comment))) {
if (write_sourceview) p->code1_start = (int)ftell(code_file);
@@ -712,6 +735,8 @@ Fl_Type* Fd_Code_Writer::write_code(Fl_Type* p) {
If the files already exist, they will be overwritten.
+ \note There is no true error checking here.
+
\param[in] s filename of source code file
\param[in] t filename of the header file
\return 0 if the operation failed, 1 if it was successful
@@ -881,6 +906,7 @@ int Fd_Code_Writer::write_code(const char *s, const char *t, bool to_sourceview)
/**
Write the public/private/protected keywords inside the class.
This avoids repeating these words if the mode is already set.
+ \param[in] state 0 for private, 1 for public, 2 for protected
*/
void Fd_Code_Writer::write_public(int state) {
if (!current_class && !current_widget_class) return;
@@ -895,7 +921,9 @@ void Fd_Code_Writer::write_public(int state) {
}
}
-
+/**
+ Create and initialize a new C++ source code writer.
+ */
Fd_Code_Writer::Fd_Code_Writer()
: code_file(NULL),
header_file(NULL),
@@ -904,9 +932,9 @@ Fd_Code_Writer::Fd_Code_Writer()
text_in_code(NULL),
ptr_in_code(NULL),
block_crc_(0),
+ block_line_start_(true),
block_buffer_(NULL),
block_buffer_size_(0),
- block_line_start_(true),
indentation(0),
write_sourceview(false),
varused_test(0),
@@ -915,6 +943,9 @@ Fd_Code_Writer::Fd_Code_Writer()
block_crc_ = crc32(0, NULL, 0);
}
+/**
+ Release all resources.
+ */
Fd_Code_Writer::~Fd_Code_Writer()
{
delete id_root;
@@ -924,12 +955,31 @@ Fd_Code_Writer::~Fd_Code_Writer()
if (block_buffer_) ::free(block_buffer_);
}
+/**
+ Write a MergeBack tag as a separate line of C++ comment.
+ The tag contains information about the type of tag that we are writing, a
+ link back to the type using its unique id, and the CRC of all code written
+ after the previous tag up to this point.
+ \param[in] type FD_TAG_GENERIC, FD_TAG_CODE, FD_TAG_MENU_CALLBACK, or FD_TAG_WIDGET_CALLBACK
+ \param[in] uid the unique id of the current type
+ */
void Fd_Code_Writer::tag(int type, unsigned short uid) {
if (g_project.write_mergeback_data)
fprintf(code_file, "//~fl~%d~%04x~%08x~~\n", type, (int)uid, (unsigned int)block_crc_);
block_crc_ = crc32(0, NULL, 0);
}
+/**
+ Static function to calculate the CRC32 of a block of C source code.
+ Calculation of the CRC ignores leading whitespace in a line and all linefeed
+ characters ('\\r').
+ \param[in] data a pointer to the data block
+ \param[in] n the size of the data in bytes, or -1 to use strlen()
+ \param[in] in_crc add to this CRC, 0 by default to start a new block
+ \param[inout] inout_line_start optional pointer to flag that determines
+ if we are the start of a line, used to find leading whitespace
+ \return the new CRC
+ */
unsigned long Fd_Code_Writer::block_crc(const void *data, int n, unsigned long in_crc, bool *inout_line_start) {
if (!data) return 0;
if (n==-1) n = (int)strlen((const char*)data);
@@ -953,10 +1003,19 @@ unsigned long Fd_Code_Writer::block_crc(const void *data, int n, unsigned long i
return in_crc;
}
+/** Add the following block of text to the CRC of this class.
+ \param[in] data a pointer to the data block
+ \param[in] n the size of the data in bytes, or -1 to use strlen()
+ */
void Fd_Code_Writer::crc_add(const void *data, int n) {
block_crc_ = block_crc(data, n, block_crc_, &block_line_start_);
}
+/** Write formatted text to the code file.
+ If MergeBack is enabled, the CRC calculation is continued.
+ \param[in] format printf style formatting string
+ \return see fprintf(FILE *, *const char*, ...)
+ */
int Fd_Code_Writer::crc_printf(const char *format, ...) {
va_list args;
va_start(args, format);
@@ -965,6 +1024,12 @@ int Fd_Code_Writer::crc_printf(const char *format, ...) {
return ret;
}
+/** Write formatted text to the code file.
+ If MergeBack is enabled, the CRC calculation is continued.
+ \param[in] format printf style formatting string
+ \param[in] args list of arguments
+ \return see fprintf(FILE *, *const char*, ...)
+ */
int Fd_Code_Writer::crc_vprintf(const char *format, va_list args) {
if (g_project.write_mergeback_data) {
int n = vsnprintf(block_buffer_, block_buffer_size_, format, args);
@@ -981,6 +1046,11 @@ int Fd_Code_Writer::crc_vprintf(const char *format, va_list args) {
}
}
+/** Write some text to the code file.
+ If MergeBack is enabled, the CRC calculation is continued.
+ \param[in] text any text, no requirements to end in a newline or such
+ \return see fputs(const char*, FILE*)
+ */
int Fd_Code_Writer::crc_puts(const char *text) {
if (g_project.write_mergeback_data) {
crc_add(text);
@@ -988,6 +1058,12 @@ int Fd_Code_Writer::crc_puts(const char *text) {
return fputs(text, code_file);
}
+/** Write a single ASCII character to the code file.
+ If MergeBack is enabled, the CRC calculation is continued.
+ \note to wrote UTF8 characters, use Fd_Code_Writer::crc_puts(const char *text)
+ \param[in] c any character between 0 and 127 inclusive
+ \return see fputc(int, FILE*)
+ */
int Fd_Code_Writer::crc_putc(int c) {
if (g_project.write_mergeback_data) {
uchar uc = (uchar)c;
@@ -996,292 +1072,5 @@ int Fd_Code_Writer::crc_putc(int c) {
return fputc(c, code_file);
}
-extern Fl_Window *the_panel;
-
-/** Remove the first two spaces at every line start.
- \param[inout] s block of C code
- */
-static void unindent(char *s) {
- char *d = s;
- bool line_start = true;
- while (*s) {
- if (line_start) {
- if (*s>0 && isspace(*s)) s++;
- if (*s>0 && isspace(*s)) s++;
- line_start = false;
- }
- if (*s=='\r') s++;
- if (*s=='\n') line_start = true;
- *d++ = *s++;
- }
- *d = 0;
-}
-
-static Fl_String unindent_block(FILE *f, long start, long end) {
- long bsize = end-start;
- long here = ::ftell(f);
- ::fseek(f, start, SEEK_SET);
- char *block = (char*)::malloc(bsize+1);
- size_t n = ::fread(block, bsize, 1, f);
- if (n!=1)
- block[0] = 0; // read error
- else
- block[bsize] = 0;
- unindent(block);
- Fl_String str = block;
- ::free(block);
- ::fseek(f, here, SEEK_SET);
- return str;
-}
-
-// TODO: add level of mergeback support to user settings
-// TODO: command line option for mergeback
-// TODO: automatic mergeback when a new project is loaded
-// TODO: automatic mergeback when FLUID becomes app in focus
-// NOTE: automatic mergeback on timer when file changes if app focus doesn't work
-// NOTE: we could also let the user edit comment blocks
-
-/**
- Merge external changes in a source code file back into the current project.
-
- This experimental function reads a source code file line by line. When it
- encounters a special tag in a line, the crc32 stored in the tag is compared
- to the crc32 that was calculated from the code lines since the previous tag.
-
- If the crc's differ, the user has modified the source file externally, and the
- given block differs from the block as it was generated by FLUID. Depending on
- the block type, the user has modified the widget code (FD_TAG_GENERIC), which
- can not be transferred back into the project.
-
- Modifications to code blocks and callbacks (CODE, CALLBACK) can be merged back
- into the project. Their corresponding Fl_Type is found using the unique
- node id that is part of the tag. The block is only merged back if the crc's
- from the project and from the edited block differ.
-
- The caller must make sure that this code file was generated by the currently
- loaded project.
-
- The user is informed in detailed dialogs what the function discovered and
- offered to merge or cancel if appropriate. Just in case this function is
- destructive, "undo" restores the state before a MergeBack.
-
- Callers can set different task. FD_MERGEBACK_CHECK checks if there are any
- modifications in the code file and returns -1 if there was an error, or a
- bit field where bit 0 is set if internal structures were modified, bit 1 if
- code was changed, and bit 2 if modified blocks were found, but no Type node.
- Bit 3 is set, if code was changed in the code file *and* the project.
-
- FD_MERGEBACK_INTERACTIVE checks for changes and presents a status dialog box
- to the user if there were conflicting changes or if a mergeback is possible,
- presenting the user the option to merge or cancel. Returns 0 if the project
- remains unchanged, and 1 if the user merged changes back. -1 is returned if an
- invalid tag was found.
-
- FD_MERGEBACK_GO merges all changes back into the project without any
- interaction. Returns 0 if nothing changed, and 1 if it merged any changes back.
-
- FD_MERGEBACK_GO_SAFE merges changes back only if there are no conflicts.
- Returns 0 if nothing changed, and 1 if it merged any changes back, and -1 if
- there were conflicts.
-
- \note this function is currently part of Fd_Code_Writer to get easy access
- to our crc32 code that also wrote the code file originally.
-
- \param[in] s path and filename of the source code file
- \param[in] task see above
- \return see above
- */
-int Fd_Code_Writer::merge_back(const char *s, int task) {
- // nothing to be done if the mergeback option is disabled in the project
- if (!g_project.write_mergeback_data) return 0;
-
- int ret = 0;
- bool changed = false;
- FILE *code = fl_fopen(s, "r");
- if (!code) return 0;
- int iter = 0;
-
- for (iter = 0; ; ++iter) {
- int line_no = 0;
- long block_start = 0;
- long block_end = 0;
- int num_changed_code = 0;
- int num_changed_structure = 0;
- int num_uid_not_found = 0;
- int num_possible_override = 0;
- int tag_error = 0;
- if (task==FD_MERGEBACK_GO)
- undo_checkpoint();
- // NOTE: if we can get the CRC from the current callback, and it's the same
- // as the code file CRC, merging back is very safe.
- block_crc_ = crc32(0, NULL, 0);
- block_line_start_ = true;
- ::fseek(code, 0, SEEK_SET);
- changed = false;
- for (;;) {
- char line[1024];
- if (fgets(line, 1023, code)==0) break;
- line_no++;
- const char *tag = strstr(line, "//~fl~");
- if (!tag) {
- crc_add(line);
- block_end = ::ftell(code);
- } else {
- int type = -1;
- int uid = 0;
- unsigned long crc = 0;
- int n = sscanf(tag, "//~fl~%d~%04x~%08lx~~", &type, &uid, &crc);
- if (n!=3 || type<0 || type>FD_TAG_LAST ) { tag_error = 1; break; }
- if (block_crc_ != crc) {
- if (task==FD_MERGEBACK_GO) {
- if (type==FD_TAG_MENU_CALLBACK || type==FD_TAG_WIDGET_CALLBACK) {
- Fl_Type *tp = Fl_Type::find_by_uid(uid);
- if (tp && tp->is_true_widget()) {
- Fl_String cb = tp->callback(); cb += "\n";
- unsigned long proj_crc = block_crc(cb.c_str());
- if (proj_crc!=block_crc_)
- tp->callback(unindent_block(code, block_start, block_end).c_str());
- changed = true;
- }
- } else if (type==FD_TAG_CODE) {
- Fl_Type *tp = Fl_Type::find_by_uid(uid);
- if (tp && tp->is_a(ID_Code)) {
- Fl_String cb = tp->name(); cb += "\n";
- unsigned long proj_crc = block_crc(cb.c_str());
- if (proj_crc!=block_crc_)
- tp->name(unindent_block(code, block_start, block_end).c_str());
- changed = true;
- }
- }
- } else {
- if (type==FD_TAG_MENU_CALLBACK || type==FD_TAG_WIDGET_CALLBACK) {
- Fl_Type *tp = Fl_Type::find_by_uid(uid);
- if (tp && tp->is_true_widget()) {
- Fl_String cb = tp->callback(); cb += "\n";
- unsigned long proj_crc = block_crc(cb.c_str());
- // check if the code and project crc are the same, so this modification was already applied
- if (proj_crc!=block_crc_) {
- num_changed_code++;
- // check if the block change on the project side as well, so we may override changes
- if (proj_crc!=crc) {
- num_possible_override++;
- }
- }
- } else {
- num_uid_not_found++;
- num_changed_code++;
- }
- } else if (type==FD_TAG_CODE) {
- Fl_Type *tp = Fl_Type::find_by_uid(uid);
- if (tp && tp->is_a(ID_Code)) {
- Fl_String code = tp->name(); code += "\n";
- unsigned long proj_crc = block_crc(code.c_str());
- // check if the code and project crc are the same, so this modification was already applied
- if (proj_crc!=block_crc_) {
- num_changed_code++;
- // check if the block change on the project side as well, so we may override changes
- if (proj_crc!=crc) {
- num_possible_override++;
- }
- }
- } else {
- num_changed_code++;
- num_uid_not_found++;
- }
- } else if (type==FD_TAG_GENERIC) {
- num_changed_structure++;
- }
- }
- }
- // reset everything for the next block
- block_crc_ = crc32(0, NULL, 0);
- block_line_start_ = true;
- block_start = ::ftell(code);
- }
- }
- if (task==FD_MERGEBACK_CHECK) {
- if (tag_error) { ret = -1; break; }
- if (num_changed_structure) ret |= 1;
- if (num_changed_code) ret |= 2;
- if (num_uid_not_found) ret |= 4;
- if (num_possible_override) ret |= 8;
- break;
- } else if (task==FD_MERGEBACK_INTERACTIVE) {
- if (tag_error) {
- fl_message("MergeBack found an error in line %d while reading tags\n"
- "from the source code. Merging code back is not possible.", line_no);
- ret = -1;
- break;
- }
- if (!num_changed_code && !num_changed_structure) {
- ret = 0;
- break;
- }
- if (num_changed_structure && !num_changed_code) {
- fl_message("MergeBack found %d modifications in the project structure\n"
- "of the source code. These kind of changes can no be\n"
- "merged back and will be lost when the source code is\n"
- "generated again from the open project.", num_changed_structure);
- ret = -1;
- break;
- }
- Fl_String msg = "MergeBack found %1$d modifications in the source code.";
- if (num_possible_override)
- msg += "\n\nWARNING: %4$d of these modified blocks appear to also have\n"
- "changed in the project. Merging will override changes in\n"
- "the project with changes from the source code file.";
- if (num_uid_not_found)
- msg += "\n\nWARNING: for %2$d of these modifications no Type node\n"
- "can be found and these modification can't be merged back.";
- if (!num_possible_override && !num_uid_not_found)
- msg += "\nMerging these changes back appears to be safe.";
-
- if (num_changed_structure)
- msg += "\n\nWARNING: %3$d modifications were found in the project\n"
- "structure. These kind of changes can no be merged back\n"
- "and will be lost when the source code is generated again\n"
- "from the open project.";
-
- if (num_changed_code==num_uid_not_found) {
- fl_message(msg.c_str(),
- num_changed_code, num_uid_not_found,
- num_changed_structure, num_possible_override);
- ret = -1;
- break;
- } else {
- msg += "\n\nClick Cancel to abort the MergeBack operation.\n"
- "Click Merge to merge all code changes back into\n"
- "the open project.";
- int c = fl_choice(msg.c_str(), "Cancel", "Merge", NULL,
- num_changed_code, num_uid_not_found,
- num_changed_structure, num_possible_override);
- if (c==0) { ret = 1; break; }
- task = FD_MERGEBACK_GO;
- continue;
- }
- } else if (task==FD_MERGEBACK_GO) {
- if (changed) ret = 1;
- break;
- } else if (task==FD_MERGEBACK_GO_SAFE) {
- if (tag_error || num_changed_structure || num_possible_override) {
- ret = -1;
- break;
- }
- if (num_changed_code==0) {
- ret = 0;
- break;
- }
- task = FD_MERGEBACK_GO;
- continue;
- }
- }
- fclose(code);
- if (changed) {
- set_modflag(1);
- if (the_panel) propagate_load(the_panel, LOAD);
- }
- return ret;
-}
-
/// \}
diff --git a/fluid/code.h b/fluid/code.h
index 68b225183..65f6170b4 100644
--- a/fluid/code.h
+++ b/fluid/code.h
@@ -31,31 +31,32 @@ struct Fd_Pointer_Tree;
int is_id(char c);
int write_strings(const Fl_String &filename);
-const int FD_TAG_GENERIC = 0;
-const int FD_TAG_CODE = 1;
-const int FD_TAG_MENU_CALLBACK = 2;
-const int FD_TAG_WIDGET_CALLBACK = 3;
-const int FD_TAG_LAST = 3;
-
-const int FD_MERGEBACK_CHECK = 0;
-const int FD_MERGEBACK_INTERACTIVE = 1;
-const int FD_MERGEBACK_GO = 2;
-const int FD_MERGEBACK_GO_SAFE = 3;
-
class Fd_Code_Writer
{
protected:
+ /// file pointer for the C++ code file
FILE *code_file;
+ /// file pointer for the C++ header file
FILE *header_file;
+
+ /// tree of unique but human-readable identifiers
Fd_Identifier_Tree* id_root;
+ /// searchable text tree for text that is only written once to the header file
Fd_Text_Tree *text_in_header;
+ /// searchable text tree for text that is only written once to the code file
Fd_Text_Tree *text_in_code;
+ /// searchable tree for pointers that are only written once to the code file
Fd_Pointer_Tree *ptr_in_code;
+ /// crc32 for blocks of text written to the code file
unsigned long block_crc_;
+ /// if set, we are at the start of a line and can ignore leading spaces in crc
+ bool block_line_start_;
+ /// expanding buffer for vsnprintf
char *block_buffer_;
+ /// size of expanding buffer for vsnprintf
int block_buffer_size_;
- bool block_line_start_;
+
void crc_add(const void *data, int n=-1);
int crc_printf(const char *format, ...);
int crc_vprintf(const char *format, va_list args);
@@ -63,19 +64,25 @@ protected:
int crc_putc(int c);
public:
+ /// current level of source code indentation
int indentation;
+ /// set if we write abbreviated file for the source code previewer
+ /// (disables binary data blocks, for example)
bool write_sourceview;
- // silly thing to prevent declaring unused variables:
- // When this symbol is on, all attempts to write code don't write
- // anything, but set a variable if it looks like the variable "o" is used:
+ /// silly thing to prevent declaring unused variables:
+ /// When this symbol is on, all attempts to write code don't write
+ /// anything, but set a variable if it looks like the variable "o" is used:
int varused_test;
+ /// set to 1 if varused_test found that a variable is actually used
int varused;
public:
Fd_Code_Writer();
~Fd_Code_Writer();
const char* unique_id(void* o, const char*, const char*, const char*);
+ /// Increment source code indentation level.
void indent_more() { indentation++; }
+ /// Decrement source code indentation level.
void indent_less() { indentation--; }
const char *indent();
const char *indent(int set);
@@ -97,7 +104,6 @@ public:
void write_public(int state); // writes pubic:/private: as needed
void tag(int type, unsigned short uid);
- int merge_back(const char *s, int task);
static unsigned long block_crc(const void *data, int n=-1, unsigned long in_crc=0, bool *inout_line_start=NULL);
};
diff --git a/fluid/fluid.cxx b/fluid/fluid.cxx
index eaaaa53f0..e8a106d64 100644
--- a/fluid/fluid.cxx
+++ b/fluid/fluid.cxx
@@ -27,6 +27,7 @@
#include "undo.h"
#include "file.h"
#include "code.h"
+#include "mergeback.h"
#include "alignment_panel.h"
#include "function_panel.h"
@@ -1284,13 +1285,9 @@ int mergeback_code_files()
return 0;
}
- // -- generate the file names with absolute paths
- Fd_Code_Writer f;
Fl_String code_filename = g_project.codefile_path() + g_project.codefile_name();
-
- // -- write the code and header files
if (!batch_mode) enter_project_dir();
- int c = f.merge_back(code_filename.c_str(), FD_MERGEBACK_INTERACTIVE);
+ int c = merge_back(code_filename, FD_MERGEBACK_INTERACTIVE);
if (!batch_mode) leave_project_dir();
if (c==0) fl_message("MergeBack found no external modifications\n"
"in the source code.");
diff --git a/fluid/mergeback.cxx b/fluid/mergeback.cxx
new file mode 100644
index 000000000..ce0943a58
--- /dev/null
+++ b/fluid/mergeback.cxx
@@ -0,0 +1,476 @@
+//
+// Code output routines for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2023 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+// https://www.fltk.org/bugs.php
+//
+
+#include "mergeback.h"
+
+#include "fluid.h"
+#include "code.h"
+#include "undo.h"
+#include "Fl_Function_Type.h"
+#include "Fl_Widget_Type.h"
+
+#include <FL/Fl_Window.H>
+#include <FL/fl_ask.H>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <zlib.h>
+
+extern void propagate_load(Fl_Group*, void*);
+extern void load_panel();
+extern void redraw_browser();
+
+// TODO: add application user setting to control mergeback
+// [] new projects default to mergeback
+// [] check mergeback when loading project
+// [] check mergeback when app gets focus
+// [] always apply if safe
+// TODO: command line option for mergeback
+// -mb or --merge-back
+// -mbs or --merge-back-if-safe
+// NOTE: automatic mergeback on timer when file changes if app focus doesn't work
+// NOTE: allow the user to edit comment blocks
+
+/**
+ Merge external changes in a source code file back into the current project.
+
+ This experimental function reads a source code file line by line. When it
+ encounters a special tag in a line, the crc32 stored in the tag is compared
+ to the crc32 that was calculated from the code lines since the previous tag.
+
+ If the crc's differ, the user has modified the source file externally, and the
+ given block differs from the block as it was generated by FLUID. Depending on
+ the block type, the user has modified the widget code (FD_TAG_GENERIC), which
+ can not be transferred back into the project.
+
+ Modifications to code blocks and callbacks (CODE, CALLBACK) can be merged back
+ into the project. Their corresponding Fl_Type is found using the unique
+ node id that is part of the tag. The block is only merged back if the crc's
+ from the project and from the edited block differ.
+
+ The caller must make sure that this code file was generated by the currently
+ loaded project.
+
+ The user is informed in detailed dialogs what the function discovered and
+ offered to merge or cancel if appropriate. Just in case this function is
+ destructive, "undo" restores the state before a MergeBack.
+
+ Callers can set different task. FD_MERGEBACK_ANALYSE checks if there are any
+ modifications in the code file and returns -1 if there was an error, or a
+ bit field where bit 0 is set if internal structures were modified, bit 1 if
+ code was changed, and bit 2 if modified blocks were found, but no Type node.
+ Bit 3 is set, if code was changed in the code file *and* the project.
+
+ FD_MERGEBACK_INTERACTIVE checks for changes and presents a status dialog box
+ to the user if there were conflicting changes or if a mergeback is possible,
+ presenting the user the option to merge or cancel. Returns 0 if the project
+ remains unchanged, and 1 if the user merged changes back. -1 is returned if an
+ invalid tag was found.
+
+ FD_MERGEBACK_APPLY merges all changes back into the project without any
+ interaction. Returns 0 if nothing changed, and 1 if it merged any changes back.
+
+ FD_MERGEBACK_APPLY_IF_SAFE merges changes back only if there are no conflicts.
+ Returns 0 if nothing changed, and 1 if it merged any changes back, and -1 if
+ there were conflicts.
+
+ \note this function is currently part of Fd_Code_Writer to get easy access
+ to our crc32 code that also wrote the code file originally.
+
+ \param[in] s path and filename of the source code file
+ \param[in] task see above
+ \return see above
+ */
+int merge_back(const Fl_String &s, int task) {
+ if (g_project.write_mergeback_data) {
+ Fd_Mergeback mergeback;
+ return mergeback.merge_back(s, task);
+ } else {
+ // nothing to be done if the mergeback option is disabled in the project
+ return 0;
+ }
+}
+
+/** Allocate and initialize MergeBack class. */
+Fd_Mergeback::Fd_Mergeback() :
+ code(NULL),
+ line_no(0),
+ tag_error(0),
+ num_changed_code(0),
+ num_changed_structure(0),
+ num_uid_not_found(0),
+ num_possible_override(0)
+{
+}
+
+/** Release allocated resources. */
+Fd_Mergeback::~Fd_Mergeback()
+{
+ if (code) ::fclose(code);
+}
+
+/** Remove the first two spaces at every line start.
+ \param[inout] s block of C code
+ */
+void Fd_Mergeback::unindent(char *s) {
+ char *d = s;
+ bool line_start = true;
+ while (*s) {
+ if (line_start) {
+ if (*s>0 && isspace(*s)) s++;
+ if (*s>0 && isspace(*s)) s++;
+ line_start = false;
+ }
+ if (*s=='\r') s++;
+ if (*s=='\n') line_start = true;
+ *d++ = *s++;
+ }
+ *d = 0;
+}
+
+/**
+ Read a block of text from the source file and remove the leading two spaces in every line.
+ \param[in] start start of the block within the file
+ \param[in] end end of text within the file
+ \return a string holding the text that was found in the file
+ */
+Fl_String Fd_Mergeback::read_and_unindent_block(long start, long end) {
+ long bsize = end-start;
+ long here = ::ftell(code);
+ ::fseek(code, start, SEEK_SET);
+ char *block = (char*)::malloc(bsize+1);
+ size_t n = ::fread(block, bsize, 1, code);
+ if (n!=1)
+ block[0] = 0; // read error
+ else
+ block[bsize] = 0;
+ unindent(block);
+ Fl_String str = block;
+ ::free(block);
+ ::fseek(code, here, SEEK_SET);
+ return str;
+}
+
+/** Tell user the results of our MergeBack analysis and pop up a dialog to give
+ the user a choice to merge or cancel.
+ \return 1 if the user wants to merge (choice dialog was shown)
+ \return 0 if there is nothing to merge (no dialog was shown)
+ \return -1 if the user wants to cancel or an error occurred or an issue was presented
+ (message or choice dialog was shown)
+ */
+int Fd_Mergeback::ask_user_to_merge() {
+ if (tag_error) {
+ fl_message("MergeBack found an error in line %d while reading tags\n"
+ "from the source code. Merging code back is not possible.", line_no);
+ return -1;
+ }
+ if (!num_changed_code && !num_changed_structure) {
+ return 0;
+ }
+ if (num_changed_structure && !num_changed_code) {
+ fl_message("MergeBack found %d modifications in the project structure\n"
+ "of the source code. These kind of changes can no be\n"
+ "merged back and will be lost when the source code is\n"
+ "generated again from the open project.", num_changed_structure);
+ return -1;
+ }
+ Fl_String msg = "MergeBack found %1$d modifications in the source code.";
+ if (num_possible_override)
+ msg += "\n\nWARNING: %4$d of these modified blocks appear to also have\n"
+ "changed in the project. Merging will override changes in\n"
+ "the project with changes from the source code file.";
+ if (num_uid_not_found)
+ msg += "\n\nWARNING: for %2$d of these modifications no Type node\n"
+ "can be found and these modification can't be merged back.";
+ if (!num_possible_override && !num_uid_not_found)
+ msg += "\nMerging these changes back appears to be safe.";
+
+ if (num_changed_structure)
+ msg += "\n\nWARNING: %3$d modifications were found in the project\n"
+ "structure. These kind of changes can no be merged back\n"
+ "and will be lost when the source code is generated again\n"
+ "from the open project.";
+
+ if (num_changed_code==num_uid_not_found) {
+ fl_message(msg.c_str(),
+ num_changed_code, num_uid_not_found,
+ num_changed_structure, num_possible_override);
+ return -1;
+ } else {
+ msg += "\n\nClick Cancel to abort the MergeBack operation.\n"
+ "Click Merge to merge all code changes back into\n"
+ "the open project.";
+ int c = fl_choice(msg.c_str(), "Cancel", "Merge", NULL,
+ num_changed_code, num_uid_not_found,
+ num_changed_structure, num_possible_override);
+ if (c==0) return -1;
+ return 1;
+ }
+}
+
+/** Analyse the block and its corresponding widget callback.
+ Return findings in num_changed_code, num_changed_code, and num_uid_not_found.
+ */
+void Fd_Mergeback::analyse_callback(unsigned long code_crc, unsigned long tag_crc, int uid) {
+ Fl_Type *tp = Fl_Type::find_by_uid(uid);
+ if (tp && tp->is_true_widget()) {
+ Fl_String cb = tp->callback(); cb += "\n";
+ unsigned long proj_crc = Fd_Code_Writer::block_crc(cb.c_str());
+ // check if the code and project crc are the same, so this modification was already applied
+ if (proj_crc!=code_crc) {
+ num_changed_code++;
+ // check if the block change on the project side as well, so we may override changes
+ if (proj_crc!=tag_crc) {
+ num_possible_override++;
+ }
+ }
+ } else {
+ num_uid_not_found++;
+ num_changed_code++;
+ }
+}
+
+/** Analyse the block and its corresponding Code Type.
+ Return findings in num_changed_code, num_changed_code, and num_uid_not_found.
+ */
+void Fd_Mergeback::analyse_code(unsigned long code_crc, unsigned long tag_crc, int uid) {
+ Fl_Type *tp = Fl_Type::find_by_uid(uid);
+ if (tp && tp->is_a(ID_Code)) {
+ Fl_String code = tp->name(); code += "\n";
+ unsigned long proj_crc = Fd_Code_Writer::block_crc(code.c_str());
+ // check if the code and project crc are the same, so this modification was already applied
+ if (proj_crc!=code_crc) {
+ num_changed_code++;
+ // check if the block change on the project side as well, so we may override changes
+ if (proj_crc!=tag_crc) {
+ num_possible_override++;
+ }
+ }
+ } else {
+ num_changed_code++;
+ num_uid_not_found++;
+ }
+}
+
+
+/** Analyse the code file and return findings in class member variables.
+
+ The code file must be open for reading already.
+
+ * tag_error is set if a tag was found, but could not be read
+ * line_no returns the line where an error occured
+ * num_changed_code is set to the number of changed code blocks in the file.
+ Code changes can be merged back to the project.
+ * num_changed_structure is set to the number of structural changes.
+ Structural changes outside of code blocks can not be read back.
+ * num_uid_not_found number of blocks that were modified, but the corresponding
+ type or widget can not be found in the project
+ * num_possible_override number of blocks that were changed in the code file,
+ but also were changed in the project.
+
+ \return -1 if reading a tag failed, otherwise 0
+ */
+int Fd_Mergeback::analyse() {
+ // initialize local variables
+ unsigned long code_crc = 0;
+ bool line_start = true;
+ char line[1024];
+ // bail if the caller has not opened a file yet
+ if (!code) return 0;
+ // initialize member variables to return our findings
+ line_no = 0;
+ tag_error = 0;
+ num_changed_code = 0;
+ num_changed_structure = 0;
+ num_uid_not_found = 0;
+ num_possible_override = 0;
+ code_crc = 0;
+ // loop through all lines in the code file
+ ::fseek(code, 0, SEEK_SET);
+ for (;;) {
+ // get the next line until end of file
+ if (fgets(line, 1023, code)==0) break;
+ line_no++;
+ const char *tag = strstr(line, "//~fl~");
+ if (!tag) {
+ // if this line has no tag, add the contents to the CRC and continue
+ code_crc = Fd_Code_Writer::block_crc(line, -1, code_crc, &line_start);
+ } else {
+ // if this line has a tag, read all tag data
+ int tag_type = -1, uid = 0;
+ unsigned long tag_crc = 0;
+ int n = sscanf(tag, "//~fl~%d~%04x~%08lx~~", &tag_type, &uid, &tag_crc);
+ if (n!=3 || tag_type<0 || tag_type>FD_TAG_LAST ) { tag_error = 1; return -1; }
+ if (code_crc != tag_crc) {
+ switch (tag_type) {
+ case FD_TAG_GENERIC:
+ num_changed_structure++;
+ break;
+ case FD_TAG_MENU_CALLBACK:
+ case FD_TAG_WIDGET_CALLBACK:
+ analyse_callback(code_crc, tag_crc, uid);
+ break;
+ case FD_TAG_CODE:
+ analyse_code(code_crc, tag_crc, uid);
+ break;
+ }
+ }
+ // reset everything for the next block
+ code_crc = 0;
+ line_start = true;
+ }
+ }
+ return 0;
+}
+
+/** Apply callback mergebacks from the code file to the project.
+ \return 1 if the project changed
+ */
+int Fd_Mergeback::apply_callback(long block_end, long block_start, unsigned long code_crc, int uid) {
+ Fl_Type *tp = Fl_Type::find_by_uid(uid);
+ if (tp && tp->is_true_widget()) {
+ Fl_String cb = tp->callback(); cb += "\n";
+ unsigned long project_crc = Fd_Code_Writer::block_crc(cb.c_str());
+ if (project_crc!=code_crc) {
+ tp->callback(read_and_unindent_block(block_start, block_end).c_str());
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/** Apply callback mergebacks from the code file to the project.
+ \return 1 if the project changed
+ */
+int Fd_Mergeback::apply_code(long block_end, long block_start, unsigned long code_crc, int uid) {
+ Fl_Type *tp = Fl_Type::find_by_uid(uid);
+ if (tp && tp->is_a(ID_Code)) {
+ Fl_String cb = tp->name(); cb += "\n";
+ unsigned long project_crc = Fd_Code_Writer::block_crc(cb.c_str());
+ if (project_crc!=code_crc) {
+ tp->name(read_and_unindent_block(block_start, block_end).c_str());
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/** Apply all possible mergebacks from the code file to the project.
+ The code file must be open for reading already.
+ \return -1 if reading a tag failed, 0 if nothing changed, 1 if the project changed
+ */
+int Fd_Mergeback::apply() {
+ // initialize local variables
+ unsigned long code_crc = 0;
+ bool line_start = true;
+ char line[1024];
+ int changed = 0;
+ long block_start = 0;
+ long block_end = 0;
+ // bail if the caller has not opened a file yet
+ if (!code) return 0;
+ // initialize member variables to return our findings
+ line_no = 0;
+ tag_error = 0;
+ code_crc = 0;
+ // loop through all lines in the code file
+ ::fseek(code, 0, SEEK_SET);
+ for (;;) {
+ // get the next line until end of file
+ if (fgets(line, 1023, code)==0) break;
+ line_no++;
+ const char *tag = strstr(line, "//~fl~");
+ if (!tag) {
+ // if this line has no tag, add the contents to the CRC and continue
+ code_crc = Fd_Code_Writer::block_crc(line, -1, code_crc, &line_start);
+ block_end = ::ftell(code);
+ } else {
+ // if this line has a tag, read all tag data
+ int tag_type = -1, uid = 0;
+ unsigned long tag_crc = 0;
+ int n = sscanf(tag, "//~fl~%d~%04x~%08lx~~", &tag_type, &uid, &tag_crc);
+ if (n!=3 || tag_type<0 || tag_type>FD_TAG_LAST ) { tag_error = 1; return -1; }
+ if (code_crc != tag_crc) {
+ if (tag_type==FD_TAG_MENU_CALLBACK || tag_type==FD_TAG_WIDGET_CALLBACK) {
+ changed |= apply_callback(block_end, block_start, code_crc, uid);
+ } else if (tag_type==FD_TAG_CODE) {
+ changed |= apply_code(block_end, block_start, code_crc, uid);
+ }
+ }
+ // reset everything for the next block
+ code_crc = 0;
+ line_start = true;
+ block_start = ::ftell(code);
+ }
+ }
+ return changed;
+}
+
+/** Dispatch the MergeBack into analysis, interactive, or apply directly.
+ \param[in] s source code filename and path
+ \param[in] task one of FD_MERGEBACK_ANALYSE, FD_MERGEBACK_INTERACTIVE,
+ FD_MERGEBACK_APPLY_IF_SAFE, or FD_MERGEBACK_APPLY
+ \return see ::merge_back(const Fl_String &s, int task)
+ */
+int Fd_Mergeback::merge_back(const Fl_String &s, int task) {
+ int ret = 0;
+ code = fl_fopen(s.c_str(), "r");
+ if (!code) return -1;
+ do { // no actual loop, just make sure we close the code file
+ if (task == FD_MERGEBACK_ANALYSE) {
+ analyse();
+ if (tag_error) {ret = -1; break; }
+ if (num_changed_structure) ret |= 1;
+ if (num_changed_code) ret |= 2;
+ if (num_uid_not_found) ret |= 4;
+ if (num_possible_override) ret |= 8;
+ break;
+ }
+ if (task == FD_MERGEBACK_INTERACTIVE) {
+ analyse();
+ ret = ask_user_to_merge();
+ if (ret != 1)
+ return ret;
+ task = FD_MERGEBACK_APPLY; // fall through
+ }
+ if (task == FD_MERGEBACK_APPLY_IF_SAFE) {
+ analyse();
+ if (tag_error || num_changed_structure || num_possible_override) {
+ ret = -1;
+ break;
+ }
+ if (num_changed_code==0) {
+ ret = 0;
+ break;
+ }
+ task = FD_MERGEBACK_APPLY; // fall through
+ }
+ if (task == FD_MERGEBACK_APPLY) {
+ ret = apply();
+ if (ret == 1) {
+ set_modflag(1);
+ redraw_browser();
+ load_panel();
+ }
+ ret = 1; // avoid message box in caller
+ }
+ } while (0);
+ fclose(code);
+ code = NULL;
+ return ret;
+}
diff --git a/fluid/mergeback.h b/fluid/mergeback.h
new file mode 100644
index 000000000..28b5ea50d
--- /dev/null
+++ b/fluid/mergeback.h
@@ -0,0 +1,77 @@
+//
+// MergeBack routines for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2023 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+// https://www.fltk.org/bugs.php
+//
+
+#ifndef _FLUID_MERGEBACK_H
+#define _FLUID_MERGEBACK_H
+
+#include <FL/fl_attr.h>
+
+#include "../src/Fl_String.H"
+
+#include <stdio.h>
+
+const int FD_TAG_GENERIC = 0;
+const int FD_TAG_CODE = 1;
+const int FD_TAG_MENU_CALLBACK = 2;
+const int FD_TAG_WIDGET_CALLBACK = 3;
+const int FD_TAG_LAST = 3;
+
+const int FD_MERGEBACK_ANALYSE = 0;
+const int FD_MERGEBACK_INTERACTIVE = 1;
+const int FD_MERGEBACK_APPLY = 2;
+const int FD_MERGEBACK_APPLY_IF_SAFE = 3;
+
+/** Class that implements the MergeBack functionality.
+ \see merge_back(const Fl_String &s, int task)
+ */
+class Fd_Mergeback
+{
+protected:
+ /// Pointer to the C++ code file.
+ FILE *code;
+ /// Current line number in the C++ code file.
+ int line_no;
+ /// Set if there was an error reading a tag.
+ int tag_error;
+ /// Number of code blocks that were different than the CRC in their tag.
+ int num_changed_code;
+ /// Number of generic structure blocks that were different than the CRC in their tag.
+ int num_changed_structure;
+ /// Number of code block that were modified, but a type node by that uid was not found.
+ int num_uid_not_found;
+ /// Number of modified code block where the corresponding project block also changed.
+ int num_possible_override;
+
+ void unindent(char *s);
+ Fl_String read_and_unindent_block(long start, long end);
+ void analyse_callback(unsigned long code_crc, unsigned long tag_crc, int uid);
+ void analyse_code(unsigned long code_crc, unsigned long tag_crc, int uid);
+ int apply_callback(long block_end, long block_start, unsigned long code_crc, int uid);
+ int apply_code(long block_end, long block_start, unsigned long code_crc, int uid);
+
+public:
+ Fd_Mergeback();
+ ~Fd_Mergeback();
+ int merge_back(const Fl_String &s, int task);
+ int ask_user_to_merge();
+ int analyse();
+ int apply();
+};
+
+extern int merge_back(const Fl_String &s, int task);
+
+
+#endif // _FLUID_MERGEBACK_H