FLUID: Adds initial MergeBack feature.
This commit is contained in:
parent
06d12892f9
commit
6d5021c00e
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
@ -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;
|
||||
|
||||
379
fluid/code.cxx
379
fluid/code.cxx
@ -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;
|
||||
}
|
||||
|
||||
/// \}
|
||||
|
||||
25
fluid/code.h
25
fluid/code.h
@ -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
|
||||
|
||||
@ -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;) {
|
||||
|
||||
@ -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]},
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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*);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user