diff --git a/fluid/Fluid.cxx b/fluid/Fluid.cxx index 8d1bd29d3..7d5ea5eaa 100644 --- a/fluid/Fluid.cxx +++ b/fluid/Fluid.cxx @@ -172,11 +172,11 @@ int Application::run(int argc,char **argv) { Fluid.layout_list.read(preferences, fld::Tool_Store::USER); main_window->show(argc,argv); toggle_widget_bin(); - toggle_codeview_cb(nullptr,nullptr); if (!c && openlast_button->value() && history.abspath[0][0] && args.autodoc_path.empty()) { // Open previous file when no file specified... open_project_file(history.abspath[0]); } + toggle_codeview_cb(nullptr,nullptr); } proj.undo.suspend(); if (c && !fld::io::read_file(proj, c,0)) { diff --git a/fluid/io/Code_Writer.cxx b/fluid/io/Code_Writer.cxx index 7f265bafa..53e82997b 100644 --- a/fluid/io/Code_Writer.cxx +++ b/fluid/io/Code_Writer.cxx @@ -151,7 +151,7 @@ int Code_Writer::write_h_once(const char *format, ...) { if (text_in_header.find(buf) != text_in_header.end()) { return 0; } - fprintf(header_file, "%s\n", buf); + header_buffer << buf << "\n"; text_in_header.insert(buf); return 1; } @@ -384,12 +384,25 @@ void Code_Writer::write_cc(const char *indent, int n, const char *c, const char Print a formatted line to the header file. \param[in] format printf-style formatting text, followed by a vararg list */ -void Code_Writer::write_h(const char* format,...) { +void Code_Writer::write_h(const char* format,...) +{ if (varused_test) return; + va_list args; va_start(args, format); - vfprintf(header_file, format, args); + int n = vsnprintf(block_buffer_, block_buffer_size_, format, args); + if (n > block_buffer_size_) { + // Buffer was too small, reallocate and try again with the copy + block_buffer_size_ = n + 128; + if (block_buffer_) ::free(block_buffer_); + block_buffer_ = (char*)::malloc(block_buffer_size_+1); + va_end(args); + va_start(args, format); + n = vsnprintf(block_buffer_, block_buffer_size_, format, args); + } va_end(args); + + header_buffer << block_buffer_; } /** @@ -491,11 +504,11 @@ bool is_comment_before_class_member(Node *q) { \return pointer to the next sibling */ Node* Code_Writer::write_static(Node* p) { - if (write_codeview) p->header_static_start = (int)ftell(header_file); - if (write_codeview) p->code_static_start = (int)ftell(code_file); + if (write_codeview) p->header_static_start = header_pos(); + if (write_codeview) p->code_static_start = code_pos(); p->write_static(*this); - if (write_codeview) p->code_static_end = (int)ftell(code_file); - if (write_codeview) p->header_static_end = (int)ftell(header_file); + if (write_codeview) p->code_static_end = code_pos(); + if (write_codeview) p->header_static_end = header_pos(); Node* q; for (q = p->next; q && q->level > p->level;) { @@ -516,11 +529,11 @@ Node* Code_Writer::write_code(Node* p) { // write all code that comes before the children code // (but don't write the last comment until the very end) if (!(p==Fluid.proj.tree.last && p->is_a(Type::Comment))) { - if (write_codeview) p->code1_start = (int)ftell(code_file); - if (write_codeview) p->header1_start = (int)ftell(header_file); + if (write_codeview) p->code1_start = code_pos(); + if (write_codeview) p->header1_start = header_pos(); p->write_code1(*this); - if (write_codeview) p->code1_end = (int)ftell(code_file); - if (write_codeview) p->header1_end = (int)ftell(header_file); + if (write_codeview) p->code1_end = code_pos(); + if (write_codeview) p->header1_end = header_pos(); } // recursively write the code of all children Node* q; @@ -539,11 +552,11 @@ Node* Code_Writer::write_code(Node* p) { } // write all code that come after the children - if (write_codeview) p->code2_start = (int)ftell(code_file); - if (write_codeview) p->header2_start = (int)ftell(header_file); + if (write_codeview) p->code2_start = code_pos(); + if (write_codeview) p->header2_start = header_pos(); p->write_code2(*this); - if (write_codeview) p->code2_end = (int)ftell(code_file); - if (write_codeview) p->header2_end = (int)ftell(header_file); + if (write_codeview) p->code2_end = code_pos(); + if (write_codeview) p->header2_end = header_pos(); for (q = p->next; q && q->level > p->level;) { if (is_class_member(q) || is_comment_before_class_member(q)) { @@ -561,11 +574,11 @@ Node* Code_Writer::write_code(Node* p) { } else { for (q = p->next; q && q->level > p->level;) q = write_code(q); // write all code that come after the children - if (write_codeview) p->code2_start = (int)ftell(code_file); - if (write_codeview) p->header2_start = (int)ftell(header_file); + if (write_codeview) p->code2_start = code_pos(); + if (write_codeview) p->header2_start = header_pos(); p->write_code2(*this); - if (write_codeview) p->code2_end = (int)ftell(code_file); - if (write_codeview) p->header2_end = (int)ftell(header_file); + if (write_codeview) p->code2_end = code_pos(); + if (write_codeview) p->header2_end = header_pos(); } return q; } @@ -573,7 +586,8 @@ Node* Code_Writer::write_code(Node* p) { /** Write the source and header files for the current design. - If the files already exist, they will be overwritten. + If the files already exist, they will be overwritten only if the content + has changed. This conservative approach helps reduce unnecessary recompilation. \note There is no true error checking here. @@ -587,18 +601,13 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) { indentation = 0; current_class = nullptr; current_widget_class = nullptr; - if (!s) code_file = stdout; - else { - FILE *f = fl_fopen(s, "wb"); - if (!f) return 0; - code_file = f; - } - if (!t) header_file = stdout; - else { - FILE *f = fl_fopen(t, "wb"); - if (!f) {fclose(code_file); return 0;} - header_file = f; - } + + // Always use string stream buffers for output + code_buffer.str(""); + code_buffer.clear(); + header_buffer.str(""); + header_buffer.clear(); + // Remember the last code file location for MergeBack if (s && proj_.write_mergeback_data && !to_codeview) { std::string filename = proj_.projectfile_path() + proj_.projectfile_name(); @@ -613,21 +622,21 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) { Node* first_node = Fluid.proj.tree.first; if (first_node && first_node->is_a(Type::Comment)) { if (write_codeview) { - first_node->code1_start = first_node->code2_start = (int)ftell(code_file); - first_node->header1_start = first_node->header2_start = (int)ftell(header_file); + first_node->code1_start = first_node->code2_start = code_pos(); + first_node->header1_start = first_node->header2_start = header_pos(); } // it is ok to write non-recursive code here, because comments have no children or code2 blocks first_node->write_code1(*this); if (write_codeview) { - first_node->code1_end = first_node->code2_end = (int)ftell(code_file); - first_node->header1_end = first_node->header2_end = (int)ftell(header_file); + first_node->code1_end = first_node->code2_end = code_pos(); + first_node->header1_end = first_node->header2_end = header_pos(); } first_node = first_node->next; } const char *hdr = "\ // generated by Fast Light User Interface Designer (fluid) version %.4f\n\n"; - fprintf(header_file, hdr, FL_VERSION); + write_h(hdr, FL_VERSION); crc_printf(hdr, FL_VERSION); { // Creating the include guard is more involved than it seems at first glance. @@ -672,8 +681,8 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) { } macro_name_str = macro_name.str(); } - fprintf(header_file, "#ifndef %s\n", macro_name_str.c_str()); - fprintf(header_file, "#define %s\n", macro_name_str.c_str()); + write_h("#ifndef %s\n", macro_name_str.c_str()); + write_h("#define %s\n", macro_name_str.c_str()); } if (proj_.avoid_early_includes==0) { @@ -746,31 +755,42 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) { p = write_code(p); } - if (!s) return 1; - - fprintf(header_file, "#endif\n"); + write_h("#endif\n"); Node* last_node = Fluid.proj.tree.last; if (last_node && (last_node != Fluid.proj.tree.first) && last_node->is_a(Type::Comment)) { if (write_codeview) { - last_node->code1_start = last_node->code2_start = (int)ftell(code_file); - last_node->header1_start = last_node->header2_start = (int)ftell(header_file); + last_node->code1_start = last_node->code2_start = code_pos(); + last_node->header1_start = last_node->header2_start = header_pos(); } last_node->write_code1(*this); if (write_codeview) { - last_node->code1_end = last_node->code2_end = (int)ftell(code_file); - last_node->header1_end = last_node->header2_end = (int)ftell(header_file); + last_node->code1_end = last_node->code2_end = code_pos(); + last_node->header1_end = last_node->header2_end = header_pos(); } } - int x = 0, y = 0; - if (code_file != stdout) - x = fclose(code_file); - code_file = nullptr; - if (header_file != stdout) - y = fclose(header_file); - header_file = nullptr; - return x >= 0 && y >= 0; + // For codeview mode, strings are available via code_string() / header_string() + if (write_codeview) + return 1; + + // Write code output: to file if filename provided, to stdout otherwise + bool code_ok = true; + if (s) { + code_ok = write_file_if_changed(s, code_buffer.str()); + } else { + fputs(code_buffer.str().c_str(), stdout); + } + + // Write header output: to file if filename provided, to stdout otherwise + bool header_ok = true; + if (t) { + header_ok = write_file_if_changed(t, header_buffer.str()); + } else { + fputs(header_buffer.str().c_str(), stdout); + } + + return code_ok && header_ok ? 1 : 0; } @@ -819,7 +839,7 @@ Code_Writer::~Code_Writer() */ void Code_Writer::tag(proj::Mergeback::Tag prev_type, proj::Mergeback::Tag next_type, unsigned short uid) { if (proj_.write_mergeback_data) { - Mergeback::print_tag(code_file, prev_type, next_type, uid, (uint32_t)block_crc_); + code_buffer << Mergeback::format_tag(prev_type, next_type, uid, (uint32_t)block_crc_); } block_crc_ = crc32(0, nullptr, 0); } @@ -866,10 +886,10 @@ void Code_Writer::crc_add(const void *data, int n) { block_crc_ = block_crc(data, n, block_crc_, &block_line_start_); } -/** Write formatted text to the code file. +/** Write formatted text to the code buffer. If MergeBack is enabled, the CRC calculation is continued. \param[in] format printf style formatting string - \return see fprintf(FILE *, *const char*, ...) + \return number of characters formatted */ int Code_Writer::crc_printf(const char *format, ...) { va_list args; @@ -879,51 +899,129 @@ int Code_Writer::crc_printf(const char *format, ...) { return ret; } -/** Write formatted text to the code file. +/** Write formatted text to the code buffer. If MergeBack is enabled, the CRC calculation is continued. \param[in] format printf style formatting string \param[in] args list of arguments - \return see fprintf(FILE *, *const char*, ...) + \return number of characters formatted */ int Code_Writer::crc_vprintf(const char *format, va_list args) { - if (proj_.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); + // Make a copy of args in case we need to call vsnprintf twice + // (the first call consumes args on some platforms) + va_list args_copy; + va_copy(args_copy, args); + int n = vsnprintf(block_buffer_, block_buffer_size_, format, args); + if (n > block_buffer_size_) { + // Buffer was too small, reallocate and try again with the copy + 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_copy); } + va_end(args_copy); + if (proj_.write_mergeback_data) { + crc_add(block_buffer_, n); + } + code_buffer << block_buffer_; + return n; } -/** Write some text to the code file. +/** Write some text to the code buffer. If MergeBack is enabled, the CRC calculation is continued. \param[in] text any text, no requirements to end in a newline or such - \return see fputs(const char*, FILE*) + \return always 0 */ int Code_Writer::crc_puts(const char *text) { if (proj_.write_mergeback_data) { crc_add(text); } - return fputs(text, code_file); + code_buffer << text; + return 0; } -/** Write a single ASCII character to the code file. +/** Write a single ASCII character to the code buffer. If MergeBack is enabled, the CRC calculation is continued. \note to write UTF-8 characters, use Code_Writer::crc_puts(const char *text) \param[in] c any character between 0 and 127 inclusive - \return see fputc(int, FILE*) + \return the character written */ int Code_Writer::crc_putc(int c) { if (proj_.write_mergeback_data) { uchar uc = (uchar)c; crc_add(&uc, 1); } - return fputc(c, code_file); + code_buffer << (char)c; + return c; +} + +/** + Check if the content of a file matches a given string. + \param[in] filename path to the file to compare + \param[in] content the string content to compare with + \return true if the file exists and its content matches exactly, false otherwise + */ +bool Code_Writer::file_content_matches(const char *filename, const std::string &content) { + FILE *f = fl_fopen(filename, "rb"); + if (!f) { + return false; // File doesn't exist + } + + // Get file size + if (fseek(f, 0, SEEK_END) != 0) { + fclose(f); + return false; // Seek error + } + long file_size = ftell(f); + if (file_size < 0) { + fclose(f); + return false; // ftell error + } + if (fseek(f, 0, SEEK_SET) != 0) { + fclose(f); + return false; // Seek error + } + + // Quick check: if sizes don't match, content is different + if ((size_t)file_size != content.size()) { + fclose(f); + return false; + } + + // Read file content and compare + std::string file_content((size_t)file_size, '\0'); + size_t bytes_read = fread(&file_content[0], 1, (size_t)file_size, f); + fclose(f); + + if (bytes_read != (size_t)file_size) { + return false; // Read error + } + + return file_content == content; +} + +/** + Write content to a file only if the content differs from the existing file. + This is a conservative write that avoids unnecessary file modifications, + which helps reduce recompilation in build systems. + \param[in] filename path to the file to write + \param[in] content the string content to write + \return true if the file was written or already up-to-date, false on write error + */ +bool Code_Writer::write_file_if_changed(const char *filename, const std::string &content) { + // If content matches, no need to write + if (file_content_matches(filename, content)) { + return true; // File is already up-to-date + } + + // Content differs or file doesn't exist - write the new content + FILE *f = fl_fopen(filename, "wb"); + if (!f) { + return false; // Cannot open file for writing + } + + size_t written = fwrite(content.c_str(), 1, content.size(), f); + int result = fclose(f); + + return (written == content.size()) && (result == 0); } diff --git a/fluid/io/Code_Writer.h b/fluid/io/Code_Writer.h index c199197ec..9008d6c27 100644 --- a/fluid/io/Code_Writer.h +++ b/fluid/io/Code_Writer.h @@ -26,6 +26,7 @@ #include #include #include +#include class Node; struct Fd_Identifier_Tree; @@ -46,10 +47,10 @@ private: /// Link Code_Writer class to the project. Project &proj_; - /// file pointer for the C++ code file - FILE *code_file = nullptr; - /// file pointer for the C++ header file - FILE *header_file = nullptr; + /// string stream buffer for generating C++ code file content + std::ostringstream code_buffer; + /// string stream buffer for generating C++ header file content + std::ostringstream header_buffer; /// tree of unique but human-readable identifiers std::map unique_id_list { }; @@ -75,6 +76,14 @@ private: int crc_puts(const char *text); int crc_putc(int c); + bool file_content_matches(const char *filename, const std::string &content); + bool write_file_if_changed(const char *filename, const std::string &content); + + /// Return the current write position in the code output stream. + int code_pos() { return (int)code_buffer.tellp(); } + /// Return the current write position in the header output stream. + int header_pos() { return (int)header_buffer.tellp(); } + public: /// current level of source code indentation int indentation = 0; @@ -116,6 +125,11 @@ public: int write_code(const char *cfile, const char *hfile, bool to_codeview=false); void write_public(int state); // writes pubic:/private: as needed + /// Return the generated source code as a string (valid after write_code() with to_codeview=true). + std::string code_string() const { return code_buffer.str(); } + /// Return the generated header code as a string (valid after write_code() with to_codeview=true). + std::string header_string() const { return header_buffer.str(); } + void tag(proj::Mergeback::Tag prev_type, proj::Mergeback::Tag next_type, unsigned short uid); static unsigned long block_crc(const void *data, int n=-1, unsigned long in_crc=0, bool *inout_line_start=nullptr); diff --git a/fluid/panels/codeview_panel.cxx b/fluid/panels/codeview_panel.cxx index 3c46715bd..93b71013e 100644 --- a/fluid/panels/codeview_panel.cxx +++ b/fluid/panels/codeview_panel.cxx @@ -26,8 +26,6 @@ #include #include #include "../src/flstring.h" -static char *cv_source_filename = nullptr; -static char *cv_header_filename = nullptr; static char *cv_design_filename = nullptr; int cv_code_choice; extern void select_only(Node *o); @@ -147,70 +145,60 @@ void update_codeview_position_cb(class Fl_Tabs*, void*) { } /** - Generate a header, source, strings, or design file in a temporary directory - and load those into the Code Viewer widgets. + Generate a header, source, strings, or design file and load the content into + the Code Viewer widgets. Source and header code are generated entirely in + memory (via ostringstream) and pushed directly into the text buffers — no + temporary files are written or read back for those tabs. */ void update_codeview_cb(class Fl_Button*, void*) { if (!codeview_panel || !codeview_panel->visible()) - return; + return; - if (!cv_source_filename) { - cv_source_filename = (char*)malloc(FL_PATH_MAX); - fl_strlcpy(cv_source_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX); - fl_strlcat(cv_source_filename, "codeview_tmp.cxx", FL_PATH_MAX); - } - if (!cv_header_filename) { - cv_header_filename = (char*)malloc(FL_PATH_MAX); - fl_strlcpy(cv_header_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX); - fl_strlcat(cv_header_filename, "codeview_tmp.h", FL_PATH_MAX); - } - if (!cv_design_filename) { - cv_design_filename = (char*)malloc(FL_PATH_MAX); - fl_strlcpy(cv_design_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX); - fl_strlcat(cv_design_filename, "codeview_tmp.fl", FL_PATH_MAX); - } + if (!cv_design_filename) { + cv_design_filename = (char*)malloc(FL_PATH_MAX); + fl_strlcpy(cv_design_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX); + fl_strlcat(cv_design_filename, "codeview_tmp.fl", FL_PATH_MAX); + } - if (cv_project->visible_r()) { - fld::io::write_file(Fluid.proj, cv_design_filename, false, true); - int top = cv_project->top_line(); - cv_project->buffer()->loadfile(cv_design_filename); - cv_project->scroll(top, 0); - } else if (cv_strings->visible_r()) { - static const char *exts[] = { ".txt", ".po", ".msg" }; - char fn[FL_PATH_MAX+1]; - fl_strlcpy(fn, Fluid.get_tmpdir().c_str(), FL_PATH_MAX); - fl_strlcat(fn, "strings", FL_PATH_MAX); - fl_filename_setext(fn, FL_PATH_MAX, exts[static_cast(Fluid.proj.i18n.type)]); - fld::io::write_strings(Fluid.proj, fn); - int top = cv_strings->top_line(); - cv_strings->buffer()->loadfile(fn); - cv_strings->scroll(top, 0); - } else if (cv_source->visible_r() || cv_header->visible_r()) { - std::string code_file_name_bak = Fluid.proj.code_file_name; - Fluid.proj.code_file_name = cv_source_filename; - std::string header_file_name_bak = Fluid.proj.header_file_name; - Fluid.proj.header_file_name = cv_header_filename; - - // generate the code and load the files - fld::io::Code_Writer f(Fluid.proj); - // generate files - if (f.write_code(cv_source_filename, cv_header_filename, true)) - { - // load file into source editor - int pos = cv_source->top_line(); - cv_source->buffer()->loadfile(cv_source_filename); - cv_source->scroll(pos, 0); - // load file into header editor - pos = cv_header->top_line(); - cv_header->buffer()->loadfile(cv_header_filename); - cv_header->scroll(pos, 0); - // update the source code highlighting - update_codeview_position(); - } - - Fluid.proj.code_file_name = code_file_name_bak; - Fluid.proj.header_file_name = header_file_name_bak; + if (cv_project->visible_r()) { + fld::io::write_file(Fluid.proj, cv_design_filename, false, true); + int top = cv_project->top_line(); + cv_project->buffer()->loadfile(cv_design_filename); + cv_project->scroll(top, 0); + } else if (cv_strings->visible_r()) { + static const char *exts[] = { ".txt", ".po", ".msg" }; + char fn[FL_PATH_MAX+1]; + fl_strlcpy(fn, Fluid.get_tmpdir().c_str(), FL_PATH_MAX); + fl_strlcat(fn, "strings", FL_PATH_MAX); + fl_filename_setext(fn, FL_PATH_MAX, exts[static_cast(Fluid.proj.i18n.type)]); + fld::io::write_strings(Fluid.proj, fn); + int top = cv_strings->top_line(); + cv_strings->buffer()->loadfile(fn); + cv_strings->scroll(top, 0); + } else if (cv_source->visible_r() || cv_header->visible_r()) { + // Generate code into in-memory ostringstream buffers (no temp files). + fld::io::Code_Writer f(Fluid.proj); + std::string code_filename = Fluid.proj.codefile_path() + Fluid.proj.codefile_name(); + std::string header_filename = Fluid.proj.headerfile_path() + Fluid.proj.headerfile_name(); + if (Fluid.proj.proj_filename) + Fluid.proj.enter_project_dir(); + // Setting code_view to true write code files directly into a string buffer + // and does not write over the code files on disk. + if (f.write_code(code_filename.c_str(), header_filename.c_str(), true)) + { + // Push generated text directly into the editor buffers. + int pos = cv_source->top_line(); + cv_source->buffer()->text(f.code_string().c_str()); + cv_source->scroll(pos, 0); + pos = cv_header->top_line(); + cv_header->buffer()->text(f.header_string().c_str()); + cv_header->scroll(pos, 0); + // update the source code highlighting + update_codeview_position(); } + if (Fluid.proj.proj_filename) + Fluid.proj.leave_project_dir(); + } } /** diff --git a/fluid/panels/codeview_panel.fl b/fluid/panels/codeview_panel.fl index 1d5a0cad6..0341224bd 100644 --- a/fluid/panels/codeview_panel.fl +++ b/fluid/panels/codeview_panel.fl @@ -48,12 +48,6 @@ decl {\#include } {private local decl {\#include "../src/flstring.h"} {private local } -decl {char *cv_source_filename = nullptr;} {private local -} - -decl {char *cv_header_filename = nullptr;} {private local -} - decl {char *cv_design_filename = nullptr;} {private local } @@ -178,69 +172,59 @@ Function {update_codeview_position_cb(class Fl_Tabs*, void*)} { } Function {update_codeview_cb(class Fl_Button*, void*)} { - comment {Generate a header, source, strings, or design file in a temporary directory -and load those into the Code Viewer widgets.} open return_type void + comment {Generate a header, source, strings, or design file and load the content into +the Code Viewer widgets. Source and header code are generated entirely in +memory (via ostringstream) and pushed directly into the text buffers — no +temporary files are written or read back for those tabs.} open return_type void } { code {if (!codeview_panel || !codeview_panel->visible()) - return; + return; - if (!cv_source_filename) { - cv_source_filename = (char*)malloc(FL_PATH_MAX); - fl_strlcpy(cv_source_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX); - fl_strlcat(cv_source_filename, "codeview_tmp.cxx", FL_PATH_MAX); +if (!cv_design_filename) { + cv_design_filename = (char*)malloc(FL_PATH_MAX); + fl_strlcpy(cv_design_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX); + fl_strlcat(cv_design_filename, "codeview_tmp.fl", FL_PATH_MAX); +} + +if (cv_project->visible_r()) { + fld::io::write_file(Fluid.proj, cv_design_filename, false, true); + int top = cv_project->top_line(); + cv_project->buffer()->loadfile(cv_design_filename); + cv_project->scroll(top, 0); +} else if (cv_strings->visible_r()) { + static const char *exts[] = { ".txt", ".po", ".msg" }; + char fn[FL_PATH_MAX+1]; + fl_strlcpy(fn, Fluid.get_tmpdir().c_str(), FL_PATH_MAX); + fl_strlcat(fn, "strings", FL_PATH_MAX); + fl_filename_setext(fn, FL_PATH_MAX, exts[static_cast(Fluid.proj.i18n.type)]); + fld::io::write_strings(Fluid.proj, fn); + int top = cv_strings->top_line(); + cv_strings->buffer()->loadfile(fn); + cv_strings->scroll(top, 0); +} else if (cv_source->visible_r() || cv_header->visible_r()) { + // Generate code into in-memory ostringstream buffers (no temp files). + fld::io::Code_Writer f(Fluid.proj); + std::string code_filename = Fluid.proj.codefile_path() + Fluid.proj.codefile_name(); + std::string header_filename = Fluid.proj.headerfile_path() + Fluid.proj.headerfile_name(); + if (Fluid.proj.proj_filename) + Fluid.proj.enter_project_dir(); + // Setting code_view to true write code files directly into a string buffer + // and does not write over the code files on disk. + if (f.write_code(code_filename.c_str(), header_filename.c_str(), true)) + { + // Push generated text directly into the editor buffers. + int pos = cv_source->top_line(); + cv_source->buffer()->text(f.code_string().c_str()); + cv_source->scroll(pos, 0); + pos = cv_header->top_line(); + cv_header->buffer()->text(f.header_string().c_str()); + cv_header->scroll(pos, 0); + // update the source code highlighting + update_codeview_position(); } - if (!cv_header_filename) { - cv_header_filename = (char*)malloc(FL_PATH_MAX); - fl_strlcpy(cv_header_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX); - fl_strlcat(cv_header_filename, "codeview_tmp.h", FL_PATH_MAX); - } - if (!cv_design_filename) { - cv_design_filename = (char*)malloc(FL_PATH_MAX); - fl_strlcpy(cv_design_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX); - fl_strlcat(cv_design_filename, "codeview_tmp.fl", FL_PATH_MAX); - } - - if (cv_project->visible_r()) { - fld::io::write_file(Fluid.proj, cv_design_filename, false, true); - int top = cv_project->top_line(); - cv_project->buffer()->loadfile(cv_design_filename); - cv_project->scroll(top, 0); - } else if (cv_strings->visible_r()) { - static const char *exts[] = { ".txt", ".po", ".msg" }; - char fn[FL_PATH_MAX+1]; - fl_strlcpy(fn, Fluid.get_tmpdir().c_str(), FL_PATH_MAX); - fl_strlcat(fn, "strings", FL_PATH_MAX); - fl_filename_setext(fn, FL_PATH_MAX, exts[static_cast(Fluid.proj.i18n.type)]); - fld::io::write_strings(Fluid.proj, fn); - int top = cv_strings->top_line(); - cv_strings->buffer()->loadfile(fn); - cv_strings->scroll(top, 0); - } else if (cv_source->visible_r() || cv_header->visible_r()) { - std::string code_file_name_bak = Fluid.proj.code_file_name; - Fluid.proj.code_file_name = cv_source_filename; - std::string header_file_name_bak = Fluid.proj.header_file_name; - Fluid.proj.header_file_name = cv_header_filename; - - // generate the code and load the files - fld::io::Code_Writer f(Fluid.proj); - // generate files - if (f.write_code(cv_source_filename, cv_header_filename, true)) - { - // load file into source editor - int pos = cv_source->top_line(); - cv_source->buffer()->loadfile(cv_source_filename); - cv_source->scroll(pos, 0); - // load file into header editor - pos = cv_header->top_line(); - cv_header->buffer()->loadfile(cv_header_filename); - cv_header->scroll(pos, 0); - // update the source code highlighting - update_codeview_position(); - } - - Fluid.proj.code_file_name = code_file_name_bak; - Fluid.proj.header_file_name = header_file_name_bak; - }} {selected + if (Fluid.proj.proj_filename) + Fluid.proj.leave_project_dir(); +}} {selected } } diff --git a/fluid/panels/codeview_panel.txt b/fluid/panels/codeview_panel.txt new file mode 100644 index 000000000..29b8d5b63 --- /dev/null +++ b/fluid/panels/codeview_panel.txt @@ -0,0 +1,25 @@ +# generated by Fast Light User Interface Designer (fluid) version 1.0500 +Code View +Source +Header +Strings +Project +aA +Find: +<< +>> +Reveal +Refresh +Auto-Refresh +Auto-Position +prolog +Include statements in header or source code +static +static declarations in source code +code +widget code block including children +code 1 +widget code block before children +code 2 +widget code block after children +Close diff --git a/fluid/proj/mergeback.cxx b/fluid/proj/mergeback.cxx index fca1f7296..869a9fe97 100644 --- a/fluid/proj/mergeback.cxx +++ b/fluid/proj/mergeback.cxx @@ -358,33 +358,49 @@ bool Mergeback::read_tag(const char *tag, Tag *prev_type, uint16_t *uid, uint32_ } void Mergeback::print_tag(FILE *out, Tag prev_type, Tag next_type, uint16_t uid, uint32_t crc) { + std::string tag = format_tag(prev_type, next_type, uid, crc); + fputs(tag.c_str(), out); +} + +/** + Format a MergeBack tag as a string. + \param[in] prev_type Type of the previous block. + \param[in] next_type Type of the next block. + \param[in] uid Unique ID of the node. + \param[in] crc CRC32 of the previous block. + \return The formatted tag string. + */ +std::string Mergeback::format_tag(Tag prev_type, Tag next_type, uint16_t uid, uint32_t crc) { static const char *tag_lut[] = { "----------", "-- code --", " callback ", " callback " }; - fputs("//fl ", out); // Distinct start of tag using utf8 + static const char *lut[] = { "--", "-~", "~-", "~~", "-=", "=-", "~=", "=~" }; + std::string result; + result += "//fl "; // Distinct start of tag using utf8 // Indicate that the text above can be edited - if (prev_type != Tag::GENERIC) fputs("▲", out); - if (prev_type != Tag::GENERIC && next_type != Tag::GENERIC) fputc('/', out); + if (prev_type != Tag::GENERIC) result += "▲"; + if (prev_type != Tag::GENERIC && next_type != Tag::GENERIC) result += '/'; // Indicate that the text below can be edited - if (next_type != Tag::GENERIC) fputs("▼", out); - fputc(' ', out); + if (next_type != Tag::GENERIC) result += "▼"; + result += ' '; // Write the first 32 bit word as an encoded divider line uint32_t pt = static_cast(prev_type); uint32_t nt = static_cast(next_type); uint32_t word = (0<<24) | (pt<<16) | (uid); // top 8 bit available for encoding type - print_trichar32(out, word); + for (int i=30; i>=0; i-=3) result += lut[(word>>i)&7]; // Write a string indicating the type of editable text if ( next_type != Tag::GENERIC) { - fputs(tag_lut[nt], out); + result += tag_lut[nt]; } else if (prev_type != Tag::GENERIC) { - fputs(tag_lut[nt], out); + result += tag_lut[nt]; } // Write the second 32 bit word as an encoded divider line - print_trichar32(out, crc); - // Repeat the intor pattern - fputc(' ', out); - if (prev_type != Tag::GENERIC) fputs("▲", out); - if (prev_type != Tag::GENERIC && next_type != Tag::GENERIC) fputc('/', out); - if (next_type != Tag::GENERIC) fputs("▼", out); - fputs(" fl//\n", out); + for (int i=30; i>=0; i-=3) result += lut[(crc>>i)&7]; + // Repeat the intro pattern + result += ' '; + if (prev_type != Tag::GENERIC) result += "▲"; + if (prev_type != Tag::GENERIC && next_type != Tag::GENERIC) result += '/'; + if (next_type != Tag::GENERIC) result += "▼"; + result += " fl//\n"; + return result; } /** Analyze the code file and return findings in class member variables. diff --git a/fluid/proj/mergeback.h b/fluid/proj/mergeback.h index a515b1815..7b4d5cf62 100644 --- a/fluid/proj/mergeback.h +++ b/fluid/proj/mergeback.h @@ -82,6 +82,7 @@ public: int analyse(); int apply(); static void print_tag(FILE *out, Tag prev_type, Tag next_type, uint16_t uid, uint32_t crc); + static std::string format_tag(Tag prev_type, Tag next_type, uint16_t uid, uint32_t crc); }; extern int merge_back(const std::string &s, const std::string &p, int task);