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
This commit is contained in:
Matthias Melcher
2023-10-26 23:31:12 +02:00
parent 8663b86749
commit 0b408792c0
10 changed files with 670 additions and 318 deletions
+2
View File
@@ -38,6 +38,7 @@ set (CPPFILES
file.cxx file.cxx
fluid_filename.cxx fluid_filename.cxx
function_panel.cxx function_panel.cxx
mergeback.cxx
pixmaps.cxx pixmaps.cxx
shell_command.cxx shell_command.cxx
sourceview_panel.cxx sourceview_panel.cxx
@@ -71,6 +72,7 @@ set (HEADERFILES
factory.h factory.h
file.h file.h
function_panel.h function_panel.h
mergeback.h
print_panel.h print_panel.h
pixmaps.h pixmaps.h
shell_command.h shell_command.h
+2
View File
@@ -24,6 +24,7 @@
#include "code.h" #include "code.h"
#include "function_panel.h" #include "function_panel.h"
#include "comments.h" #include "comments.h"
#include "mergeback.h"
#include <FL/fl_string_functions.h> #include <FL/fl_string_functions.h>
#include <FL/Fl_File_Chooser.H> #include <FL/Fl_File_Chooser.H>
@@ -353,6 +354,7 @@ static bool fd_isspace(int c) {
return (c>0 && c<128 && isspace(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) { static bool fd_iskeyword(int c) {
return (c>0 && c<128 && (isalnum(c) || c=='_')); return (c>0 && c<128 && (isalnum(c) || c=='_'));
} }
+1
View File
@@ -29,6 +29,7 @@
#include "code.h" #include "code.h"
#include "Fluid_Image.h" #include "Fluid_Image.h"
#include "custom_widgets.h" #include "custom_widgets.h"
#include "mergeback.h"
#include <FL/Fl.H> #include <FL/Fl.H>
#include <FL/fl_message.H> #include <FL/fl_message.H>
+2 -1
View File
@@ -27,6 +27,7 @@
#include "alignment_panel.h" #include "alignment_panel.h"
#include "widget_panel.h" #include "widget_panel.h"
#include "undo.h" #include "undo.h"
#include "mergeback.h"
#include <FL/Fl.H> #include <FL/Fl.H>
#include <FL/Fl_Group.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: // update the panel according to current widget set:
static void load_panel() { void load_panel() {
if (!the_panel) return; if (!the_panel) return;
// find all the Fl_Widget subclasses currently selected: // find all the Fl_Widget subclasses currently selected:
+1
View File
@@ -39,6 +39,7 @@ CPPFILES = \
fluid.cxx \ fluid.cxx \
fluid_filename.cxx \ fluid_filename.cxx \
function_panel.cxx \ function_panel.cxx \
mergeback.cxx \
pixmaps.cxx \ pixmaps.cxx \
shell_command.cxx \ shell_command.cxx \
sourceview_panel.cxx \ sourceview_panel.cxx \
+84 -295
View File
File diff suppressed because it is too large Load Diff
+23 -17
View File
@@ -31,31 +31,32 @@ struct Fd_Pointer_Tree;
int is_id(char c); int is_id(char c);
int write_strings(const Fl_String &filename); 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 class Fd_Code_Writer
{ {
protected: protected:
/// file pointer for the C++ code file
FILE *code_file; FILE *code_file;
/// file pointer for the C++ header file
FILE *header_file; FILE *header_file;
/// tree of unique but human-readable identifiers
Fd_Identifier_Tree* id_root; 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; 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; 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; Fd_Pointer_Tree *ptr_in_code;
/// crc32 for blocks of text written to the code file
unsigned long block_crc_; unsigned long block_crc_;
char *block_buffer_; /// if set, we are at the start of a line and can ignore leading spaces in crc
int block_buffer_size_;
bool block_line_start_; bool block_line_start_;
/// expanding buffer for vsnprintf
char *block_buffer_;
/// size of expanding buffer for vsnprintf
int block_buffer_size_;
void crc_add(const void *data, int n=-1); void crc_add(const void *data, int n=-1);
int crc_printf(const char *format, ...); int crc_printf(const char *format, ...);
int crc_vprintf(const char *format, va_list args); int crc_vprintf(const char *format, va_list args);
@@ -63,19 +64,25 @@ protected:
int crc_putc(int c); int crc_putc(int c);
public: public:
/// current level of source code indentation
int indentation; int indentation;
/// set if we write abbreviated file for the source code previewer
/// (disables binary data blocks, for example)
bool write_sourceview; bool write_sourceview;
// silly thing to prevent declaring unused variables: /// silly thing to prevent declaring unused variables:
// When this symbol is on, all attempts to write code don't write /// 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: /// anything, but set a variable if it looks like the variable "o" is used:
int varused_test; int varused_test;
/// set to 1 if varused_test found that a variable is actually used
int varused; int varused;
public: public:
Fd_Code_Writer(); Fd_Code_Writer();
~Fd_Code_Writer(); ~Fd_Code_Writer();
const char* unique_id(void* o, const char*, const char*, const char*); const char* unique_id(void* o, const char*, const char*, const char*);
/// Increment source code indentation level.
void indent_more() { indentation++; } void indent_more() { indentation++; }
/// Decrement source code indentation level.
void indent_less() { indentation--; } void indent_less() { indentation--; }
const char *indent(); const char *indent();
const char *indent(int set); const char *indent(int set);
@@ -97,7 +104,6 @@ public:
void write_public(int state); // writes pubic:/private: as needed void write_public(int state); // writes pubic:/private: as needed
void tag(int type, unsigned short uid); 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); static unsigned long block_crc(const void *data, int n=-1, unsigned long in_crc=0, bool *inout_line_start=NULL);
}; };
+2 -5
View File
@@ -27,6 +27,7 @@
#include "undo.h" #include "undo.h"
#include "file.h" #include "file.h"
#include "code.h" #include "code.h"
#include "mergeback.h"
#include "alignment_panel.h" #include "alignment_panel.h"
#include "function_panel.h" #include "function_panel.h"
@@ -1284,13 +1285,9 @@ int mergeback_code_files()
return 0; 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(); Fl_String code_filename = g_project.codefile_path() + g_project.codefile_name();
// -- write the code and header files
if (!batch_mode) enter_project_dir(); 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 (!batch_mode) leave_project_dir();
if (c==0) fl_message("MergeBack found no external modifications\n" if (c==0) fl_message("MergeBack found no external modifications\n"
"in the source code."); "in the source code.");
+476
View File
@@ -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;
}
+77
View File
@@ -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