mirror of
https://github.com/fltk/fltk.git
synced 2026-06-01 23:06:54 +08:00
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:
@@ -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
|
||||||
|
|||||||
@@ -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=='_'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
File diff suppressed because it is too large
Load Diff
+23
-17
@@ -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
@@ -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.");
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user