FLUID: Adds initial MergeBack feature.

This commit is contained in:
Matthias Melcher 2023-10-26 00:38:58 +02:00
parent 06d12892f9
commit 6d5021c00e
17 changed files with 950 additions and 331 deletions

View File

@ -663,8 +663,9 @@ void Fl_Code_Type::write_code1(Fd_Code_Writer& f) {
if ( handle_editor_changes() == 1 ) {
main_window->redraw(); // tell fluid to redraw; edits may affect tree's contents
}
f.tag(FD_TAG_GENERIC, 0);
f.write_c_indented(name(), 0, '\n');
f.tag(FD_TAG_CODE, get_uid());
}
/**

View File

@ -277,6 +277,7 @@ void Fl_Menu_Item_Type::write_static(Fd_Code_Writer& f) {
f.write_c(", %s", ut);
if (use_v) f.write_c(" v");
f.write_c(") {\n");
f.tag(FD_TAG_GENERIC, 0);
f.write_c_indented(callback(), 1, 0);
if (*(d-1) != ';' && *(d-1) != '}') {
const char *p = strrchr(callback(), '\n');
@ -286,7 +287,9 @@ void Fl_Menu_Item_Type::write_static(Fd_Code_Writer& f) {
// statement...
if (*p != '#' && *p) f.write_c(";");
}
f.write_c("\n}\n");
f.write_c("\n");
f.tag(FD_TAG_MENU_CALLBACK, get_uid());
f.write_c("}\n");
if (k) {
f.write_c("void %s::%s(Fl_Menu_* o, %s v) {\n", k, cn, ut);
f.write_c("%s((%s*)(o", f.indent(1), k);

View File

@ -348,6 +348,7 @@ void update_visibility_flag(Fl_Type *p) {
Constructor and base for any node in the widget tree.
*/
Fl_Type::Fl_Type() :
uid_(0),
code_static_start(-1), code_static_end(-1),
code1_start(-1), code1_end(-1),
code2_start(-1), code2_end(-1),
@ -501,6 +502,14 @@ void Fl_Type::add(Fl_Type *p, Strategy strategy) {
last = end;
prev = end->next = 0;
}
{ // make sure that we have no duplicate uid's
Fl_Type *tp = this;
do {
tp->set_uid(tp->uid_);
tp = tp->next;
} while (tp!=end && tp!=NULL);
}
// tell this that it was added, so it can update itself
if (p) p->add_child(this,0);
open_ = 1;
@ -551,6 +560,13 @@ void Fl_Type::insert(Fl_Type *g) {
end->next = g;
g->prev = end;
update_visibility_flag(this);
{ // make sure that we have no duplicate uid's
Fl_Type *tp = this;
do {
tp->set_uid(tp->uid_);
tp = tp->next;
} while (tp!=end && tp!=NULL);
}
// tell parent that it has a new child, so it can update itself
if (parent) parent->add_child(this, g);
widget_browser->redraw();
@ -709,6 +725,10 @@ void Fl_Type::write(Fd_Project_Writer &f) {
void Fl_Type::write_properties(Fd_Project_Writer &f) {
// repeat this for each attribute:
if (g_project.write_mergeback_data && uid_) {
f.write_word("uid");
f.write_string("%d", uid_);
}
if (label()) {
f.write_indent(level+1);
f.write_word("label");
@ -738,7 +758,9 @@ void Fl_Type::write_properties(Fd_Project_Writer &f) {
}
void Fl_Type::read_property(Fd_Project_Reader &f, const char *c) {
if (!strcmp(c,"label"))
if (!strcmp(c,"uid"))
set_uid(f.read_int());
else if (!strcmp(c,"label"))
label(f.read_word());
else if (!strcmp(c,"user_data"))
user_data(f.read_word());
@ -1024,6 +1046,46 @@ void Fl_Type::write_code1(Fd_Code_Writer& f) {
void Fl_Type::write_code2(Fd_Code_Writer&) {
}
/** Set a uid that is unique within the project.
Try to set the given id as the unique id for this node. If the suggested id
is 0, or it is already taken inside this project, we try another random id
until we find one that is unique.
\param[in] suggested_uid the preferred uid for this node
\return the actualt uid that was given to the node
*/
unsigned short Fl_Type::set_uid(unsigned short suggested_uid) {
if (suggested_uid==0)
suggested_uid = (unsigned short)rand();
for (;;) {
Fl_Type *tp = Fl_Type::first;
for ( ; tp; tp = tp->next)
if (tp!=this && tp->uid_==suggested_uid)
break;
if (tp==NULL)
break;
suggested_uid = (unsigned short)rand();
}
uid_ = suggested_uid;
return suggested_uid;
}
/** Find a node by its unique id.
Every node in a type tree has an id that is unique for the current project.
Walk the tree and return the node with this uid.
\param[in] uid any number between 0 and 65535
\return the node with this uid, or NULL if not found
*/
Fl_Type *Fl_Type::find_by_uid(unsigned short uid) {
for (Fl_Type *tp = Fl_Type::first; tp; tp = tp->next) {
if (tp->uid_ == uid) return tp;
}
return NULL;
}
/** Find a type node by using the sourceview text positions.
\param[in] text_type 0=source file, 1=header, 2=.fl project file

View File

@ -121,6 +121,8 @@ protected:
/** Optional comment for every node in the graph. Visible in browser and
panels, and will also be copied to the source code. */
const char *comment_;
/** a unique ID within the project */
unsigned short uid_;
public: // things that should not be public:
@ -249,6 +251,10 @@ public:
int has_function(const char*, const char*) const;
unsigned short set_uid(unsigned short suggested_uid=0);
unsigned short get_uid() { return uid_; }
static Fl_Type *find_by_uid(unsigned short uid);
static Fl_Type *find_in_text(int text_type, int crsr);
};

View File

@ -2846,6 +2846,7 @@ void Fl_Widget_Type::write_static(Fd_Code_Writer& f) {
f.write_c(", %s", ut);
if (use_v) f.write_c(" v");
f.write_c(") {\n");
f.tag(FD_TAG_GENERIC, 0);
f.write_c_indented(callback(), 1, 0);
if (*(d-1) != ';' && *(d-1) != '}') {
const char *p = strrchr(callback(), '\n');
@ -2855,7 +2856,9 @@ void Fl_Widget_Type::write_static(Fd_Code_Writer& f) {
// statement...
if (*p != '#' && *p) f.write_c(";");
}
f.write_c("\n}\n");
f.write_c("\n");
f.tag(FD_TAG_WIDGET_CALLBACK, get_uid());
f.write_c("}\n");
if (k) {
f.write_c("void %s::%s(%s* o, %s v) {\n", k, cn, t, ut);
f.write_c("%s((%s*)(o", f.indent(1), k);

View File

@ -377,6 +377,7 @@ See Fl_Grid for an example.
Type Fl_Type <word>
"uid" <word> : since Oct 2023, optional, a unique id for this node within the project file
“label” <word> : text
“user_data” <word> : a value or an expression
“user_data_type” <word> : usually “void*” or “long”

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -74,6 +74,7 @@ extern Fl_Check_Button *include_H_from_C_button;
extern Fl_Check_Button *use_FL_COMMAND_button;
extern Fl_Check_Button *utf8_in_src_button;
extern Fl_Check_Button *avoid_early_includes_button;
extern Fl_Check_Button *w_proj_mergeback;
extern Fl_Group *w_settings_layout_tab;
#include <FL/Fl_Choice.H>
extern Fl_Choice *layout_choice;

View File

@ -21,9 +21,11 @@
#include "Fl_Function_Type.h"
#include "alignment_panel.h"
#include "file.h"
#include "undo.h"
#include <FL/Fl.H>
#include <FL/fl_string_functions.h>
#include <FL/fl_ask.H>
#include "fluid_filename.h"
#include "../src/flstring.h"
@ -31,6 +33,8 @@
#include <stdio.h>
#include <stdlib.h>
#include "zlib.h"
/// \defgroup cfile C Code File Operations
/// \{
@ -359,7 +363,7 @@ int Fd_Code_Writer::write_c_once(const char *format, ...) {
else if (i < 0) p = &((*p)->left);
else p = &((*p)->right);
}
fprintf(code_file,"%s\n",buf);
crc_printf("%s\n", buf);
*p = new Fd_Text_Tree(buf);
return 1;
}
@ -404,21 +408,21 @@ void Fd_Code_Writer::write_cstring(const char *s, int length) {
// longer than four lines, we only render a placeholder.
if (write_sourceview && ((s==NULL) || (length>300))) {
if (length>=0)
fprintf(code_file, "\" ... %d bytes of text... \"", length);
crc_printf("\" ... %d bytes of text... \"", length);
else
fprintf(code_file, "\" ... text... \"");
crc_puts("\" ... text... \"");
return;
}
if (length==-1 || s==0L) {
fprintf(code_file, "\n#error string not found\n");
fprintf(code_file, "\" ... undefined size text... \"");
crc_puts("\n#error string not found\n");
crc_puts("\" ... undefined size text... \"");
return;
}
const char *p = s;
const char *e = s+length;
int linelength = 1;
putc('\"', code_file);
crc_putc('\"');
for (; p < e;) {
int c = *p++;
switch (c) {
@ -431,9 +435,9 @@ void Fd_Code_Writer::write_cstring(const char *s, int length) {
case '\'':
case '\\':
QUOTED:
if (linelength >= 77) {fputs("\\\n",code_file); linelength = 0;}
putc('\\', code_file);
putc(c, code_file);
if (linelength >= 77) { crc_puts("\\\n"); linelength = 0; }
crc_putc('\\');
crc_putc(c);
linelength += 2;
break;
case '?': // prevent trigraphs by writing ?? as ?\?
@ -442,8 +446,8 @@ void Fd_Code_Writer::write_cstring(const char *s, int length) {
default:
if (c >= ' ' && c < 127) {
// a legal ASCII character
if (linelength >= 78) {fputs("\\\n",code_file); linelength = 0;}
putc(c, code_file);
if (linelength >= 78) { crc_puts("\\\n"); linelength = 0; }
crc_putc(c);
linelength++;
break;
}
@ -453,25 +457,25 @@ void Fd_Code_Writer::write_cstring(const char *s, int length) {
// This is the first character in a utf-8 sequence (0b11......).
// A line break would be ok here. Do not put linebreak in front of
// following characters (0b10......)
if (linelength >= 78) {fputs("\\\n",code_file); linelength = 0;}
if (linelength >= 78) { crc_puts("\\\n"); linelength = 0; }
}
putc(c, code_file);
crc_putc(c);
linelength++;
break;
}
// otherwise we must print it as an octal constant:
c &= 255;
if (c < 8) {
if (linelength >= 76) {fputs("\\\n",code_file); linelength = 0;}
fprintf(code_file, "\\%o",c);
if (linelength >= 76) { crc_puts("\\\n"); linelength = 0; }
crc_printf("\\%o", c);
linelength += 2;
} else if (c < 64) {
if (linelength >= 75) {fputs("\\\n",code_file); linelength = 0;}
fprintf(code_file, "\\%o",c);
if (linelength >= 75) { crc_puts("\\\n"); linelength = 0; }
crc_printf("\\%o", c);
linelength += 3;
} else {
if (linelength >= 74) {fputs("\\\n",code_file); linelength = 0;}
fprintf(code_file, "\\%o",c);
if (linelength >= 74) { crc_puts("\\\n"); linelength = 0; }
crc_printf("\\%o", c);
linelength += 4;
}
// We must not put more numbers after it, because some C compilers
@ -479,14 +483,14 @@ void Fd_Code_Writer::write_cstring(const char *s, int length) {
// pasting to avoid this:
c = *p;
if (p < e && ( (c>='0'&&c<='9') || (c>='a'&&c<='f') || (c>='A'&&c<='F') )) {
putc('\"', code_file); linelength++;
if (linelength >= 79) {fputs("\n",code_file); linelength = 0;}
putc('\"', code_file); linelength++;
crc_putc('\"'); linelength++;
if (linelength >= 79) { crc_puts("\n"); linelength = 0; }
crc_putc('\"'); linelength++;
}
break;
}
}
putc('\"', code_file);
crc_putc('\"');
}
/**
@ -510,30 +514,30 @@ void Fd_Code_Writer::write_cdata(const char *s, int length) {
}
if (write_sourceview) {
if (length>=0)
fprintf(code_file, "{ /* ... %d bytes of binary data... */ }", length);
crc_printf("{ /* ... %d bytes of binary data... */ }", length);
else
fprintf(code_file, "{ /* ... binary data... */ }");
crc_puts("{ /* ... binary data... */ }");
return;
}
if (length==-1) {
fprintf(code_file, "\n#error data not found\n");
fprintf(code_file, "{ /* ... undefined size binary data... */ }");
crc_puts("\n#error data not found\n");
crc_puts("{ /* ... undefined size binary data... */ }");
return;
}
const unsigned char *w = (const unsigned char *)s;
const unsigned char *e = w+length;
int linelength = 1;
putc('{', code_file);
crc_putc('{');
for (; w < e;) {
unsigned char c = *w++;
if (c>99) linelength += 4;
else if (c>9) linelength += 3;
else linelength += 2;
if (linelength >= 77) {fputs("\n",code_file); linelength = 0;}
fprintf(code_file, "%d", c);
if (w<e) putc(',', code_file);
if (linelength >= 77) {crc_puts("\n"); linelength = 0;}
crc_printf("%d", c);
if (w<e) crc_putc(',');
}
putc('}', code_file);
crc_putc('}');
}
/**
@ -546,7 +550,7 @@ void Fd_Code_Writer::vwrite_c(const char* format, va_list args) {
varused = 1;
return;
}
vfprintf(code_file, format, args);
crc_vprintf(format, args);
}
/**
@ -753,7 +757,7 @@ int Fd_Code_Writer::write_code(const char *s, const char *t, bool to_sourceview)
const char *hdr = "\
// generated by Fast Light User Interface Designer (fluid) version %.4f\n\n";
fprintf(header_file, hdr, FL_VERSION);
fprintf(code_file, hdr, FL_VERSION);
crc_printf(hdr, FL_VERSION);
{char define_name[102];
const char* a = fl_filename_name(t);
@ -899,11 +903,16 @@ Fd_Code_Writer::Fd_Code_Writer()
text_in_header(NULL),
text_in_code(NULL),
ptr_in_code(NULL),
block_crc_(0),
block_buffer_(NULL),
block_buffer_size_(0),
block_line_start_(true),
indentation(0),
write_sourceview(false),
varused_test(0),
varused(0)
{
block_crc_ = crc32(0, NULL, 0);
}
Fd_Code_Writer::~Fd_Code_Writer()
@ -912,6 +921,308 @@ Fd_Code_Writer::~Fd_Code_Writer()
delete ptr_in_code;
delete text_in_code;
delete text_in_header;
if (block_buffer_) ::free(block_buffer_);
}
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);
}
void Fd_Code_Writer::crc_add(const void *data, int n) {
if (!data) return;
if (n==-1) n = (int)strlen((const char*)data);
const char *s = (const char*)data;
for (int i=n; n>0; --n, ++s) {
if (block_line_start_) {
// don't count leading spaces and tabs in a line
while (n>0 && *s>0 && isspace(*s)) { s++; n--; }
if (*s) block_line_start_ = false;
}
// don't count '\r' that may be introduces by MSWindows
if (n>0 && *s=='\r') { s++; n--; }
if (n>0 && *s=='\n') block_line_start_ = true;
if (n>0) {
block_crc_ = crc32(block_crc_, (const Bytef*)s, 1);
}
}
}
int Fd_Code_Writer::crc_printf(const char *format, ...) {
va_list args;
va_start(args, format);
int ret = crc_vprintf(format, args);
va_end(args);
return ret;
}
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);
if (n > block_buffer_size_) {
block_buffer_size_ = n + 128;
if (block_buffer_) ::free(block_buffer_);
block_buffer_ = (char*)::malloc(block_buffer_size_+1);
n = vsnprintf(block_buffer_, block_buffer_size_, format, args);
}
crc_add(block_buffer_, n);
return fputs(block_buffer_, code_file);
} else {
return vfprintf(code_file, format, args);
}
}
int Fd_Code_Writer::crc_puts(const char *text) {
if (g_project.write_mergeback_data) {
crc_add(text);
}
return fputs(text, code_file);
}
int Fd_Code_Writer::crc_putc(int c) {
if (g_project.write_mergeback_data) {
uchar uc = (uchar)c;
crc_add(&uc, 1);
}
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);
fread(block, bsize, 1, f);
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 line is compared
to the crc32 that was calculate from the code lines since the previous tag.
If the crc's differ, the user has modified the source file, 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 caller must make sure that this code file was generated by the currently
loaded project.
Since this is an experimental function, the user is informed in detailed
dialogs what the function discovered, and FLUID offers the option to merge
or cancel to the user. 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 or
bit 2 if code blocks or callbacks were changed, and bit 3 if modified blocks
were found, but no Type node.
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 a list of options. Returns 0 if nothing changed, 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;
long here = 0;
int num_changed_code = 0;
int num_changed_callback = 0;
int num_changed_structure = 0;
int num_uid_not_found = 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()) {
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)) {
tp->name(unindent_block(code, block_start, block_end).c_str());
changed = true;
}
}
} else {
bool find_node = false;
// TODO: if we find a modification, we must check if it was already
// merged into the current project, or we will remerge over
// and over, even if the current code is modified.
switch (type) {
case FD_TAG_GENERIC: num_changed_structure++; break;
case FD_TAG_CODE: num_changed_code++; find_node = true; break;
case FD_TAG_MENU_CALLBACK: num_changed_callback++; find_node = true; break;
case FD_TAG_WIDGET_CALLBACK: num_changed_callback++; find_node = true; break;
}
if (find_node) {
if (Fl_Type::find_by_uid(uid)==NULL) num_uid_not_found++;
}
}
}
// 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_changed_callback) ret |= 4;
if (num_uid_not_found) 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. MergeBack not possible.", line_no);
ret = -1;
break;
}
if (!num_changed_code && !num_changed_callback && !num_changed_structure) {
ret = 0;
break;
}
if (num_changed_structure && (num_changed_code==0 && num_changed_callback==0)) {
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.", num_changed_structure);
ret = -1;
break;
}
Fl_String msg = "MergeBack found %1$d modifications in Code Blocks and %2$d\n"
"modifications in callbacks.";
if (num_uid_not_found)
msg += "\n\nWARNING: for %3$d of these modifications no Type node\n"
"can be found. The project diverged substantially from the\n"
"code file and these modification can't be merged back.";
if (num_changed_structure)
msg += "\n\nWARNING: %4$d modifications in the project structure\n"
"can no be merged back and will be lost.";
msg += "\n\nClick Cancel to abort the MergeBack operation.\n"
"Click Merge to move code and callback changes back into\n"
"the project.";
int c = fl_choice(msg.c_str(), "Cancel", "Merge", NULL,
num_changed_code, num_changed_callback,
num_uid_not_found, num_changed_structure);
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) {
ret = -1;
break;
}
if (num_changed_code==0 && num_changed_callback==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;
}
/// \}

View File

@ -31,6 +31,17 @@ 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:
@ -41,6 +52,16 @@ protected:
Fd_Text_Tree *text_in_code;
Fd_Pointer_Tree *ptr_in_code;
unsigned long block_crc_;
char *block_buffer_;
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);
int crc_puts(const char *text);
int crc_putc(int c);
public:
int indentation;
bool write_sourceview;
@ -74,6 +95,10 @@ public:
Fl_Type* write_code(Fl_Type* p);
int write_code(const char *cfile, const char *hfile, bool to_sourceview=false);
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);
};
#endif // _FLUID_CODE_H

View File

@ -333,6 +333,11 @@ void Fd_Project_Reader::read_children(Fl_Type *p, int paste, Strategy strategy,
}
goto CONTINUE;
}
if (!strcmp(c, "mergeback")) {
g_project.write_mergeback_data = read_int();
goto CONTINUE;
}
}
{
Fl_Type *t = add_new_widget_from_file(c, strategy);
@ -808,6 +813,8 @@ int Fd_Project_Writer::write_project(const char *filename, int selected_only, bo
g_layout_list.write(this);
if (g_shell_config)
g_shell_config->write(this);
if (g_project.write_mergeback_data)
write_string("\nmergeback %d", g_project.write_mergeback_data);
}
for (Fl_Type *p = Fl_Type::first; p;) {

View File

@ -285,6 +285,7 @@ Fluid_Project::Fluid_Project() :
avoid_early_includes(0),
header_file_set(0),
code_file_set(0),
write_mergeback_data(0),
header_file_name(".h"),
code_file_name(".cxx")
{ }
@ -314,6 +315,7 @@ void Fluid_Project::reset() {
code_file_set = 0;
header_file_name = ".h";
code_file_name = ".cxx";
write_mergeback_data = 0;
}
void Fluid_Project::update_settings_dialog() {
@ -1268,6 +1270,30 @@ void write_cb(Fl_Widget *, void *) {
write_code_files();
}
/**
Merge the possibly modified content of code files back into the project.
*/
int mergeback_code_files()
{
flush_text_widgets();
if (!filename) return 1;
// -- 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);
if (!batch_mode) leave_project_dir();
if (c==0) fl_message("MergeBack found no external modifications\n"
"in the source code.");
}
void mergeback_cb(Fl_Widget *, void *) {
mergeback_code_files();
}
/**
Write the strings that are used in i18n.
*/
@ -1619,6 +1645,7 @@ Fl_Menu_Item Main_Menu[] = {
{"Save As &Template...", 0, save_template_cb, 0, FL_MENU_DIVIDER},
{"&Print...", FL_COMMAND+'p', print_menu_cb},
{"Write &Code...", FL_COMMAND+FL_SHIFT+'c', write_cb, 0},
{"MergeBack Code", FL_COMMAND+FL_SHIFT+'m', mergeback_cb, 0},
{"&Write Strings...", FL_COMMAND+FL_SHIFT+'w', write_strings_cb, 0, FL_MENU_DIVIDER},
{relative_history[0], FL_COMMAND+'1', menu_file_open_history_cb, absolute_history[0]},
{relative_history[1], FL_COMMAND+'2', menu_file_open_history_cb, absolute_history[1]},

View File

@ -133,6 +133,7 @@ public:
int avoid_early_includes;
int header_file_set;
int code_file_set;
int write_mergeback_data;
Fl_String header_file_name;
Fl_String code_file_name;
};

View File

@ -23,40 +23,6 @@
#include <FL/Fl_Grid.H>
extern void set_modflag(int mf, int mfc=-1);
Fl_Double_Window* make_window() {
Fl_Double_Window* w;
{ Fl_Double_Window* o = new Fl_Double_Window(480, 320);
w = o; (void)w;
{ Fl_Grid* o = new Fl_Grid(25, 25, 262, 160);
o->labelsize(11);
o->layout(4, 4);
o->margin(10, 10, 16, 12);
static const int colwidths[] = { 0, 100, 0, 0 };
o->col_width(colwidths, 4);
static const int colweights[] = { 50, 40, 50, 50 };
o->col_weight(colweights, 4);
{ Fl_Button* o = new Fl_Button(70, 63, 129, 50, "Button");
o->labelsize(11);
} // Fl_Button* o
{ Fl_Group* o = new Fl_Group(224, 115, 40, 45);
o->box(FL_BORDER_BOX);
o->color((Fl_Color)11);
o->labelsize(11);
{ Fl_Button* o = new Fl_Button(234, 120, 24, 20, "Button");
o->labelsize(11);
} // Fl_Button* o
o->end();
} // Fl_Group* o
Fl_Grid::Cell *cell = NULL;
cell = o->widget(o->child(0), 1, 1, 1, 1, 48);
if (cell) cell->minimum_size(20, 20);
o->end();
} // Fl_Grid* o
o->end();
} // Fl_Double_Window* o
return w;
}
Fl_Tabs *widget_tabs=(Fl_Tabs *)0;
static void cb_widget_tabs(Fl_Tabs* o, void* v) {
@ -575,7 +541,6 @@ Fl_Double_Window* make_widget_panel() {
o->labelsize(11);
o->callback((Fl_Callback*)propagate_load);
o->when(FL_WHEN_NEVER);
o->hide();
{ Fl_Group* o = new Fl_Group(95, 40, 309, 20, "Label:");
o->labelfont(1);
o->labelsize(11);
@ -1449,6 +1414,7 @@ access the Widget pointer and \'v\' to access the user value.");
{ widget_tab_grid_child = new Fl_Group(10, 30, 400, 330, "Grid Child");
widget_tab_grid_child->labelsize(11);
widget_tab_grid_child->callback((Fl_Callback*)propagate_load);
widget_tab_grid_child->hide();
{ Fl_Group* o = new Fl_Group(95, 60, 315, 20, "Location:");
o->labelfont(1);
o->labelsize(11);

View File

@ -43,36 +43,6 @@ decl {\#include "custom_widgets.h"} {public global
decl {extern void set_modflag(int mf, int mfc=-1);} {private local
}
Function {make_window()} {open
} {
Fl_Window {} {open
xywh {867 441 480 320} type Double visible
} {
Fl_Grid {} {open
xywh {25 25 262 160} labelsize 11
dimensions {4 4} margin {10 10 16 12}
colwidths { 0 100 0 0 }
colweights { 50 40 50 50 }
} {
Fl_Button {} {
label Button
xywh {70 63 129 50} labelsize 11
parent_properties {
location {1 1}
}
}
Fl_Group {} {open
xywh {224 115 40 45} box BORDER_BOX color 11 labelsize 11
} {
Fl_Button {} {
label Button
xywh {234 120 24 20} labelsize 11
}
}
}
}
}
Function {make_widget_panel()} {
comment {Create a panel that can be used with all known widgets} open
} {
@ -89,7 +59,7 @@ Function {make_widget_panel()} {
Fl_Group {} {
label GUI
callback propagate_load
xywh {10 30 400 330} labelsize 11 when 0 hide resizable
xywh {10 30 400 330} labelsize 11 when 0 resizable
} {
Fl_Group {} {
label {Label:}
@ -865,7 +835,7 @@ wCallback->do_callback(wCallback, v);} open
Fl_Group widget_tab_grid_child {
label {Grid Child}
callback propagate_load open
xywh {10 30 400 330} labelsize 11
xywh {10 30 400 330} labelsize 11 hide
} {
Fl_Group {} {
label {Location:}
@ -896,7 +866,7 @@ wCallback->do_callback(wCallback, v);} open
}
Fl_Input widget_grid_col_input {
label {Column:}
callback grid_set_col_cb selected
callback grid_set_col_cb
xywh {175 60 40 20} labelsize 11 align 5 textsize 11
class Fluid_Coord_Input
}

View File

@ -21,12 +21,9 @@
#include <FL/Fl.H>
#include "custom_widgets.h"
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Grid.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Group.H>
Fl_Double_Window* make_window();
#include <FL/Fl_Tabs.H>
extern Fl_Tabs *widget_tabs;
#include <FL/Fl_Group.H>
extern void propagate_load(Fl_Group*, void*);
#include <FL/Fl_Input.H>
extern void label_cb(Fl_Input*, void*);
@ -34,6 +31,7 @@ extern void label_cb(Fl_Input*, void*);
extern Fl_Menu_Item labeltypemenu[];
extern void labeltype_cb(Fl_Choice*, void*);
extern void image_cb(Fl_Input*, void*);
#include <FL/Fl_Button.H>
extern void image_browse_cb(Fl_Button*, void*);
#include "pixmaps.h"
extern void compress_image_cb(Fl_Button*, void*);