diff options
Diffstat (limited to 'fluid/io')
| -rw-r--r-- | fluid/io/Code_Writer.cxx | 430 | ||||
| -rw-r--r-- | fluid/io/Code_Writer.h | 53 | ||||
| -rw-r--r-- | fluid/io/Project_Reader.cxx | 197 | ||||
| -rw-r--r-- | fluid/io/Project_Reader.h | 32 | ||||
| -rw-r--r-- | fluid/io/Project_Writer.cxx | 82 | ||||
| -rw-r--r-- | fluid/io/Project_Writer.h | 22 | ||||
| -rw-r--r-- | fluid/io/String_Writer.cxx | 142 | ||||
| -rw-r--r-- | fluid/io/String_Writer.h | 33 |
8 files changed, 482 insertions, 509 deletions
diff --git a/fluid/io/Code_Writer.cxx b/fluid/io/Code_Writer.cxx index c88ebc147..c9e638c93 100644 --- a/fluid/io/Code_Writer.cxx +++ b/fluid/io/Code_Writer.cxx @@ -1,5 +1,5 @@ // -// Code output routines for the Fast Light Tool Kit (FLTK). +// Fluid C++ Code Writer code for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2025 by Bill Spitzak and others. // @@ -16,18 +16,16 @@ #include "io/Code_Writer.h" -#include "app/project.h" -#include "nodes/Fl_Window_Type.h" -#include "nodes/Fl_Function_Type.h" +#include "Fluid.h" +#include "Project.h" +#include "nodes/Window_Node.h" +#include "nodes/Function_Node.h" #include <FL/filename.H> #include "../src/flstring.h" #include <zlib.h> -/// \defgroup cfile C Code File Operations -/// \{ - using namespace fld; using namespace fld::io; @@ -41,146 +39,6 @@ int is_id(char c) { return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; } -/** - Write a string to a file, replacing all non-ASCII characters with octal codes. - \param[in] out output file - \param[in] text write this NUL terminated utf-8 string - \return EOF if any of the file access calls failed, 0 if OK - */ -int write_escaped_strings(FILE *out, const char *text) { - int ret = 0; - const unsigned char *utf8_text = (const unsigned char *)text; - for (const unsigned char *s = utf8_text; *s; ++s) { - unsigned char c = *s; - // escape control characters, delete, all utf-8, and the double quotes - // note: we should have an option in the project settings to allow utf-8 - // characters in the output text and not escape them - if (c < 32 || c > 126 || c == '\"') { - if (c == '\r') { - ret = fputs("\\r", out); - } else if (c == '\n') { - ret = fputs("\\n", out); - } else { - ret = fprintf(out, "\\%03o", c); - } - } else { - ret = putc((int)c, out); - } - } - return ret; -} - -/** - Write a file that contains all label and tooltip strings for internationalization. - The user is responsible to set the right file name extension. The file format - is determined by `g_project.i18n_type`. - \param[in] filename file path and name to a file that will hold the strings - \return 1 if the file could not be opened for writing, or the result of `fclose`. - */ -int write_strings(const std::string &filename) { - Fl_Type *p; - Fl_Widget_Type *w; - int i; - - FILE *fp = fl_fopen(filename.c_str(), "wb"); - if (!fp) return 1; - - switch (g_project.i18n_type) { - case FD_I18N_NONE : /* None, just put static text out */ - fprintf(fp, "# generated by Fast Light User Interface Designer (fluid) version %.4f\n", - FL_VERSION); - for (p = Fl_Type::first; p; p = p->next) { - if (p->is_widget()) { - w = (Fl_Widget_Type *)p; - - if (w->label()) { - write_escaped_strings(fp, w->label()); - putc('\n', fp); - } - - if (w->tooltip()) { - write_escaped_strings(fp, w->tooltip()); - putc('\n', fp); - } - } - } - break; - case FD_I18N_GNU : /* GNU gettext, put a .po file out */ - fprintf(fp, "# generated by Fast Light User Interface Designer (fluid) version %.4f\n", - FL_VERSION); - for (p = Fl_Type::first; p; p = p->next) { - if (p->is_widget()) { - w = (Fl_Widget_Type *)p; - - if (w->label()) { - fputs("msgid \"", fp); - write_escaped_strings(fp, w->label()); - fputs("\"\n", fp); - - fputs("msgstr \"", fp); - write_escaped_strings(fp, w->label()); - fputs("\"\n", fp); - } - - if (w->tooltip()) { - fputs("msgid \"", fp); - write_escaped_strings(fp, w->tooltip()); - fputs("\"\n", fp); - - fputs("msgstr \"", fp); - write_escaped_strings(fp, w->tooltip()); - fputs("\"\n", fp); - } - } - } - break; - case FD_I18N_POSIX : /* POSIX catgets, put a .msg file out */ - fprintf(fp, "$ generated by Fast Light User Interface Designer (fluid) version %.4f\n", - FL_VERSION); - fprintf(fp, "$set %s\n", g_project.i18n_pos_set.c_str()); - fputs("$quote \"\n", fp); - - for (i = 1, p = Fl_Type::first; p; p = p->next) { - if (p->is_widget()) { - w = (Fl_Widget_Type *)p; - - if (w->label()) { - fprintf(fp, "%d \"", i ++); - write_escaped_strings(fp, w->label()); - fputs("\"\n", fp); - } - - if (w->tooltip()) { - fprintf(fp, "%d \"", i ++); - write_escaped_strings(fp, w->tooltip()); - fputs("\"\n", fp); - } - } - } - break; - } - - return fclose(fp); -} - -//////////////////////////////////////////////////////////////// -// Generate unique but human-readable identifiers: - -/** A binary searchable tree storing identifiers for quick retrieval. */ -struct Fd_Identifier_Tree { - char* text; - void* object; - Fd_Identifier_Tree *left, *right; - Fd_Identifier_Tree (const char* t, void* o) : text(fl_strdup(t)), object(o) {left = right = 0;} - ~Fd_Identifier_Tree(); -}; - -Fd_Identifier_Tree::~Fd_Identifier_Tree() { - delete left; - free((void *)text); - delete right; -} - /** \brief Return a unique name for the given object. This function combines the name and label into an identifier. It then checks @@ -209,22 +67,21 @@ const char* Code_Writer::unique_id(void* o, const char* type, const char* name, } *q = 0; // okay, search the tree and see if the name was already used: - Fd_Identifier_Tree** p = &id_root; int which = 0; - while (*p) { - int i = strcmp(buffer, (*p)->text); - if (!i) { - if ((*p)->object == o) return (*p)->text; - // already used, we need to pick a new name: - sprintf(q,"%x",++which); - p = &id_root; - continue; + for (;;) { + auto it = unique_id_list.find(buffer); + // If the id does not exist, add it to the map + if (it == unique_id_list.end()) { + it = unique_id_list.insert(std::make_pair(buffer, o)).first; + return it->first.c_str(); } - else if (i < 0) p = &((*p)->left); - else p = &((*p)->right); + // If it does exist, and the pointers are the same, just return it. + if (it->second == o) { + return it->first.c_str(); + } + // Else repeat until we have a new id, + sprintf(q,"%x",++which); } - *p = new Fd_Identifier_Tree(buffer, o); - return (*p)->text; } //////////////////////////////////////////////////////////////// @@ -272,45 +129,6 @@ const char *Code_Writer::indent_plus(int offset) { } -//////////////////////////////////////////////////////////////// -// declarations/include files: -// Each string generated by write_h_once is written only once to -// the header file. This is done by keeping a binary tree of all -// the calls so far and not printing it if it is in the tree. - -/** A binary searchable tree storing text for quick retrieval. */ -struct Fd_Text_Tree { - char *text; - Fd_Text_Tree *left, *right; - Fd_Text_Tree(const char *t) { - text = fl_strdup(t); - left = right = 0; - } - ~Fd_Text_Tree(); -}; - -Fd_Text_Tree::~Fd_Text_Tree() { - delete left; - free((void *)text); - delete right; -} - -/** A binary searchable tree storing pointers for quick retrieval. */ -struct Fd_Pointer_Tree { - void *ptr; - Fd_Pointer_Tree *left, *right; - Fd_Pointer_Tree(void *p) { - ptr = p; - left = right = 0; - } - ~Fd_Pointer_Tree(); -}; - -Fd_Pointer_Tree::~Fd_Pointer_Tree() { - delete left; - delete right; -} - /** Print a formatted line to the header file, unless the same line was produced before in this header file. \note Resulting line is cropped at 1023 bytes. @@ -323,15 +141,11 @@ int Code_Writer::write_h_once(const char *format, ...) { va_start(args, format); vsnprintf(buf, sizeof(buf), format, args); va_end(args); - Fd_Text_Tree **p = &text_in_header; - while (*p) { - int i = strcmp(buf,(*p)->text); - if (!i) return 0; - else if (i < 0) p = &((*p)->left); - else p = &((*p)->right); + if (text_in_header.find(buf) != text_in_header.end()) { + return 0; } - fprintf(header_file,"%s\n",buf); - *p = new Fd_Text_Tree(buf); + fprintf(header_file, "%s\n", buf); + text_in_header.insert(buf); return 1; } @@ -347,22 +161,16 @@ int Code_Writer::write_c_once(const char *format, ...) { va_start(args, format); vsnprintf(buf, sizeof(buf), format, args); va_end(args); - Fd_Text_Tree **p = &text_in_header; - while (*p) { - int i = strcmp(buf,(*p)->text); - if (!i) return 0; - else if (i < 0) p = &((*p)->left); - else p = &((*p)->right); + // Return if the text was already printed to the header file. + if (text_in_header.find(buf) != text_in_header.end()) { + return 0; } - p = &text_in_code; - while (*p) { - int i = strcmp(buf,(*p)->text); - if (!i) return 0; - else if (i < 0) p = &((*p)->left); - else p = &((*p)->right); + // Return if the text was already printed to the source file. + if (text_in_code.find(buf) != text_in_code.end()) { + return 0; } crc_printf("%s\n", buf); - *p = new Fd_Text_Tree(buf); + text_in_code.insert(buf); return 1; } @@ -373,13 +181,10 @@ int Code_Writer::write_c_once(const char *format, ...) { \return true if found in the tree, false if added to the tree */ bool Code_Writer::c_contains(void *pp) { - Fd_Pointer_Tree **p = &ptr_in_code; - while (*p) { - if ((*p)->ptr == pp) return true; - else if ((*p)->ptr < pp) p = &((*p)->left); - else p = &((*p)->right); + if (ptr_in_code.find(pp) != ptr_in_code.end()) { + return true; } - *p = new Fd_Pointer_Tree(pp); + ptr_in_code.insert(pp); return false; } @@ -408,14 +213,14 @@ void Code_Writer::write_cstring(const char *s, int length) { } // if we are rendering to the source code preview window, and the text is // longer than four lines, we only render a placeholder. - if (write_codeview && ((s==NULL) || (length>300))) { + if (write_codeview && ((s==nullptr) || (length>300))) { if (length>=0) crc_printf("\" ... %d bytes of text... \"", length); else crc_puts("\" ... text... \""); return; } - if (length==-1 || s==0L) { + if (length==-1 || s==nullptr) { crc_puts("\n#error string not found\n"); crc_puts("\" ... undefined size text... \""); return; @@ -454,7 +259,7 @@ void Code_Writer::write_cstring(const char *s, int length) { break; } // if the UTF-8 option is checked, write unicode characters verbatim - if (g_project.utf8_in_src && (c&0x80)) { + if (proj_.utf8_in_src && (c&0x80)) { if ((c&0x40)) { // 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 @@ -644,13 +449,13 @@ void Code_Writer::write_c_indented(const char *textlines, int inIndent, char inT constructor whereas functions, declarations, and inline data are seen as members of the class itself. */ -bool is_class_member(Fl_Type *t) { - return t->is_a(ID_Function) - || t->is_a(ID_Decl) - || t->is_a(ID_Data); -// || t->is_a(ID_Class) // FLUID can't handle a class inside a class -// || t->is_a(ID_Widget_Class) -// || t->is_a(ID_DeclBlock) // Declaration blocks are generally not handled well +bool is_class_member(Node *t) { + return t->is_a(Type::Function) + || t->is_a(Type::Decl) + || t->is_a(Type::Data); +// || t->is_a(Type::Class) // FLUID can't handle a class inside a class +// || t->is_a(Type::Widget_Class) +// || t->is_a(Type::DeclBlock) // Declaration blocks are generally not handled well } /** @@ -661,11 +466,11 @@ bool is_class_member(Fl_Type *t) { \param[in] q should be a comment type \return true if this comment is followed by a class member \return false if it is followed by a widget or code - \see is_class_member(Fl_Type *t) + \see is_class_member(Node *t) */ -bool is_comment_before_class_member(Fl_Type *q) { - if (q->is_a(ID_Comment) && q->next && q->next->level==q->level) { - if (q->next->is_a(ID_Comment)) +bool is_comment_before_class_member(Node *q) { + if (q->is_a(Type::Comment) && q->next && q->next->level==q->level) { + if (q->next->is_a(Type::Comment)) return is_comment_before_class_member(q->next); if (is_class_member(q->next)) return true; @@ -678,14 +483,14 @@ bool is_comment_before_class_member(Fl_Type *q) { \param[in] p write this type and all its children \return pointer to the next sibling */ -Fl_Type* Code_Writer::write_static(Fl_Type* p) { +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); 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); - Fl_Type* q; + Node* q; for (q = p->next; q && q->level > p->level;) { q = write_static(q); } @@ -697,13 +502,13 @@ Fl_Type* Code_Writer::write_static(Fl_Type* p) { /** Recursively write code, putting children between the two parts of the parent code. - \param[in] p write this type and all its children + \param[in] p write this node and all its children \return pointer to the next sibling */ -Fl_Type* Code_Writer::write_code(Fl_Type* p) { +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==Fl_Type::last && p->is_a(ID_Comment))) { + 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); p->write_code1(*this); @@ -711,7 +516,7 @@ Fl_Type* Code_Writer::write_code(Fl_Type* p) { if (write_codeview) p->header1_end = (int)ftell(header_file); } // recursively write the code of all children - Fl_Type* q; + Node* q; if (p->is_widget() && p->is_class()) { // Handle widget classes specially for (q = p->next; q && q->level > p->level;) { @@ -745,7 +550,7 @@ Fl_Type* Code_Writer::write_code(Fl_Type* p) { } write_h("};\n"); - current_widget_class = 0L; + current_widget_class = nullptr; } else { for (q = p->next; q && q->level > p->level;) q = write_code(q); // write all code that come after the children @@ -771,10 +576,10 @@ Fl_Type* Code_Writer::write_code(Fl_Type* p) { */ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) { write_codeview = to_codeview; - delete id_root; id_root = 0; + unique_id_list.clear(); indentation = 0; - current_class = 0L; - current_widget_class = 0L; + current_class = nullptr; + current_widget_class = nullptr; if (!s) code_file = stdout; else { FILE *f = fl_fopen(s, "wb"); @@ -788,29 +593,29 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) { header_file = f; } // Remember the last code file location for MergeBack - if (s && g_project.write_mergeback_data && !to_codeview) { - std::string proj_filename = g_project.projectfile_path() + g_project.projectfile_name(); - int i, n = (int)proj_filename.size(); - for (i=0; i<n; i++) if (proj_filename[i]=='\\') proj_filename[i] = '/'; + if (s && proj_.write_mergeback_data && !to_codeview) { + std::string filename = proj_.projectfile_path() + proj_.projectfile_name(); + int i, n = (int)filename.size(); + for (i=0; i<n; i++) if (filename[i]=='\\') filename[i] = '/'; Fl_Preferences build_records(Fl_Preferences::USER_L, "fltk.org", "fluid-build"); - Fl_Preferences path(build_records, proj_filename.c_str()); + Fl_Preferences path(build_records, filename.c_str()); path.set("code", s); } // if the first entry in the Type tree is a comment, then it is probably // a copyright notice. We print that before anything else in the file! - Fl_Type* first_type = Fl_Type::first; - if (first_type && first_type->is_a(ID_Comment)) { + Node* first_node = Fluid.proj.tree.first; + if (first_node && first_node->is_a(Type::Comment)) { if (write_codeview) { - first_type->code1_start = first_type->code2_start = (int)ftell(code_file); - first_type->header1_start = first_type->header2_start = (int)ftell(header_file); + first_node->code1_start = first_node->code2_start = (int)ftell(code_file); + first_node->header1_start = first_node->header2_start = (int)ftell(header_file); } // it is ok to write non-recursive code here, because comments have no children or code2 blocks - first_type->write_code1(*this); + first_node->write_code1(*this); if (write_codeview) { - first_type->code1_end = first_type->code2_end = (int)ftell(code_file); - first_type->header1_end = first_type->header2_end = (int)ftell(header_file); + 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_type = first_type->next; + first_node = first_node->next; } const char *hdr = "\ @@ -828,27 +633,27 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) { fprintf(header_file, "#define %s\n", define_name); } - if (g_project.avoid_early_includes==0) { + if (proj_.avoid_early_includes==0) { write_h_once("#include <FL/Fl.H>"); } - if (t && g_project.include_H_from_C) { + if (t && proj_.include_H_from_C) { if (to_codeview) { write_c("#include \"CodeView.h\"\n"); - } else if (g_project.header_file_name[0] == '.' && strchr(g_project.header_file_name.c_str(), '/') == NULL) { + } else if (proj_.header_file_name[0] == '.' && strchr(proj_.header_file_name.c_str(), '/') == nullptr) { write_c("#include \"%s\"\n", fl_filename_name(t)); } else { - write_c("#include \"%s\"\n", g_project.header_file_name.c_str()); + write_c("#include \"%s\"\n", proj_.header_file_name.c_str()); } } std::string loc_include, loc_conditional; - if (g_project.i18n_type==FD_I18N_GNU) { - loc_include = g_project.i18n_gnu_include; - loc_conditional = g_project.i18n_gnu_conditional; + if (proj_.i18n_type==fld::I18n_Type::GNU) { + loc_include = proj_.i18n_gnu_include; + loc_conditional = proj_.i18n_gnu_conditional; } else { - loc_include = g_project.i18n_pos_include; - loc_conditional = g_project.i18n_pos_conditional; + loc_include = proj_.i18n_pos_include; + loc_conditional = proj_.i18n_pos_conditional; } - if (g_project.i18n_type && !loc_include.empty()) { + if ((proj_.i18n_type != fld::I18n_Type::NONE) && !loc_include.empty()) { int conditional = !loc_conditional.empty(); if (conditional) { write_c("#ifdef %s\n", loc_conditional.c_str()); @@ -858,26 +663,26 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) { write_c("#%sinclude \"%s\"\n", indent(), loc_include.c_str()); else write_c("#%sinclude %s\n", indent(), loc_include.c_str()); - if (g_project.i18n_type == FD_I18N_POSIX) { - if (!g_project.i18n_pos_file.empty()) { - write_c("extern nl_catd %s;\n", g_project.i18n_pos_file.c_str()); + if (proj_.i18n_type == fld::I18n_Type::POSIX) { + if (!proj_.i18n_pos_file.empty()) { + write_c("extern nl_catd %s;\n", proj_.i18n_pos_file.c_str()); } else { write_c("// Initialize I18N stuff now for menus...\n"); write_c("#%sinclude <locale.h>\n", indent()); write_c("static char *_locale = setlocale(LC_MESSAGES, \"\");\n"); - write_c("static nl_catd _catalog = catopen(\"%s\", 0);\n", g_project.basename().c_str()); + write_c("static nl_catd _catalog = catopen(\"%s\", 0);\n", proj_.basename().c_str()); } } if (conditional) { write_c("#else\n"); - if (g_project.i18n_type == FD_I18N_GNU) { - if (!g_project.i18n_gnu_function.empty()) { - write_c("#%sifndef %s\n", indent(), g_project.i18n_gnu_function.c_str()); - write_c("#%sdefine %s(text) text\n", indent_plus(1), g_project.i18n_gnu_function.c_str()); + if (proj_.i18n_type == fld::I18n_Type::GNU) { + if (!proj_.i18n_gnu_function.empty()) { + write_c("#%sifndef %s\n", indent(), proj_.i18n_gnu_function.c_str()); + write_c("#%sdefine %s(text) text\n", indent_plus(1), proj_.i18n_gnu_function.c_str()); write_c("#%sendif\n", indent()); } } - if (g_project.i18n_type == FD_I18N_POSIX) { + if (proj_.i18n_type == fld::I18n_Type::POSIX) { write_c("#%sifndef catgets\n", indent()); write_c("#%sdefine catgets(catalog, set, msgid, text) text\n", indent_plus(1)); write_c("#%sendif\n", indent()); @@ -885,13 +690,13 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) { indentation--; write_c("#endif\n"); } - if (g_project.i18n_type == FD_I18N_GNU && g_project.i18n_gnu_static_function[0]) { - write_c("#ifndef %s\n", g_project.i18n_gnu_static_function.c_str()); - write_c("#%sdefine %s(text) text\n", indent_plus(1), g_project.i18n_gnu_static_function.c_str()); + if (proj_.i18n_type == fld::I18n_Type::GNU && proj_.i18n_gnu_static_function[0]) { + write_c("#ifndef %s\n", proj_.i18n_gnu_static_function.c_str()); + write_c("#%sdefine %s(text) text\n", indent_plus(1), proj_.i18n_gnu_static_function.c_str()); write_c("#endif\n"); } } - for (Fl_Type* p = first_type; p;) { + for (Node* p = first_node; p;) { // write all static data for this & all children first write_static(p); // then write the nested code: @@ -902,26 +707,26 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) { fprintf(header_file, "#endif\n"); - Fl_Type* last_type = Fl_Type::last; - if (last_type && (last_type != Fl_Type::first) && last_type->is_a(ID_Comment)) { + 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_type->code1_start = last_type->code2_start = (int)ftell(code_file); - last_type->header1_start = last_type->header2_start = (int)ftell(header_file); + 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_type->write_code1(*this); + last_node->write_code1(*this); if (write_codeview) { - last_type->code1_end = last_type->code2_end = (int)ftell(code_file); - last_type->header1_end = last_type->header2_end = (int)ftell(header_file); + last_node->code1_end = last_node->code2_end = (int)ftell(code_file); + last_node->header1_end = last_node->header2_end = (int)ftell(header_file); } } int x = 0, y = 0; if (code_file != stdout) x = fclose(code_file); - code_file = 0; + code_file = nullptr; if (header_file != stdout) y = fclose(header_file); - header_file = 0; + header_file = nullptr; return x >= 0 && y >= 0; } @@ -947,23 +752,10 @@ void Code_Writer::write_public(int state) { /** Create and initialize a new C++ source code writer. */ -Code_Writer::Code_Writer() -: code_file(NULL), - header_file(NULL), - id_root(NULL), - text_in_header(NULL), - text_in_code(NULL), - ptr_in_code(NULL), - block_crc_(0), - block_line_start_(true), - block_buffer_(NULL), - block_buffer_size_(0), - indentation(0), - write_codeview(false), - varused_test(0), - varused(0) +Code_Writer::Code_Writer(Project &proj) +: proj_ { proj } { - block_crc_ = crc32(0, NULL, 0); + block_crc_ = crc32(0, nullptr, 0); } /** @@ -971,10 +763,6 @@ Code_Writer::Code_Writer() */ Code_Writer::~Code_Writer() { - delete id_root; - delete ptr_in_code; - delete text_in_code; - delete text_in_header; if (block_buffer_) ::free(block_buffer_); } @@ -987,9 +775,9 @@ Code_Writer::~Code_Writer() \param[in] uid the unique id of the current type */ void Code_Writer::tag(int type, unsigned short uid) { - if (g_project.write_mergeback_data) + if (proj_.write_mergeback_data) fprintf(code_file, "//~fl~%d~%04x~%08x~~\n", type, (int)uid, (unsigned int)block_crc_); - block_crc_ = crc32(0, NULL, 0); + block_crc_ = crc32(0, nullptr, 0); } /** @@ -1054,7 +842,7 @@ int Code_Writer::crc_printf(const char *format, ...) { \return see fprintf(FILE *, *const char*, ...) */ int Code_Writer::crc_vprintf(const char *format, va_list args) { - if (g_project.write_mergeback_data) { + 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; @@ -1075,7 +863,7 @@ int Code_Writer::crc_vprintf(const char *format, va_list args) { \return see fputs(const char*, FILE*) */ int Code_Writer::crc_puts(const char *text) { - if (g_project.write_mergeback_data) { + if (proj_.write_mergeback_data) { crc_add(text); } return fputs(text, code_file); @@ -1088,12 +876,10 @@ int Code_Writer::crc_puts(const char *text) { \return see fputc(int, FILE*) */ int Code_Writer::crc_putc(int c) { - if (g_project.write_mergeback_data) { + if (proj_.write_mergeback_data) { uchar uc = (uchar)c; crc_add(&uc, 1); } return fputc(c, code_file); } -/// \} - diff --git a/fluid/io/Code_Writer.h b/fluid/io/Code_Writer.h index b4a6518b4..ca23ca0a2 100644 --- a/fluid/io/Code_Writer.h +++ b/fluid/io/Code_Writer.h @@ -1,7 +1,7 @@ // -// Code output routines for the Fast Light Tool Kit (FLTK). +// Fluid C++ Code Writer header for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2021 by Bill Spitzak and others. +// Copyright 1998-2025 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -22,43 +22,50 @@ #include <stdarg.h> #include <stdio.h> #include <string> +#include <set> +#include <map> -class Fl_Type; +class Node; struct Fd_Identifier_Tree; struct Fd_Text_Tree; struct Fd_Pointer_Tree; int is_id(char c); -int write_strings(const std::string &filename); namespace fld { + +class Project; + namespace io { class Code_Writer { -protected: +private: + /// Link Code_Writer class to the project. + Project &proj_; + /// file pointer for the C++ code file - FILE *code_file; + FILE *code_file = nullptr; /// file pointer for the C++ header file - FILE *header_file; + FILE *header_file = nullptr; /// tree of unique but human-readable identifiers - Fd_Identifier_Tree* id_root; + std::map<std::string, void*> unique_id_list { }; /// searchable text tree for text that is only written once to the header file - Fd_Text_Tree *text_in_header; + std::set<std::string> text_in_header { }; /// searchable text tree for text that is only written once to the code file - Fd_Text_Tree *text_in_code; + std::set<std::string> text_in_code { }; /// searchable tree for pointers that are only written once to the code file - Fd_Pointer_Tree *ptr_in_code; + std::set<void*> ptr_in_code { }; /// crc32 for blocks of text written to the code file - unsigned long block_crc_; + unsigned long block_crc_ = 0; /// if set, we are at the start of a line and can ignore leading spaces in crc - bool block_line_start_; + bool block_line_start_ = true; /// expanding buffer for vsnprintf - char *block_buffer_; + char *block_buffer_ = nullptr; /// size of expanding buffer for vsnprintf - int block_buffer_size_; + int block_buffer_size_ = 0; void crc_add(const void *data, int n=-1); int crc_printf(const char *format, ...); @@ -68,19 +75,19 @@ protected: public: /// current level of source code indentation - int indentation; + int indentation = 0; /// set if we write abbreviated file for the source code previewer /// (disables binary data blocks, for example) - bool write_codeview; + bool write_codeview = false; /// silly thing to prevent declaring unused variables: /// When this symbol is on, all attempts to write code don't write /// anything, but set a variable if it looks like the variable "o" is used: - int varused_test; + int varused_test = 0; /// set to 1 if varused_test found that a variable is actually used - int varused; + int varused = 0; public: - Code_Writer(); + Code_Writer(Project &proj); ~Code_Writer(); const char* unique_id(void* o, const char*, const char*, const char*); /// Increment source code indentation level. @@ -102,14 +109,14 @@ public: void write_h(const char*, ...) __fl_attr((__format__ (__printf__, 2, 3))); void write_hc(const char *, int, const char*, const char*); void write_c_indented(const char *textlines, int inIndent, char inTrailwWith); - Fl_Type* write_static(Fl_Type* p); - Fl_Type* write_code(Fl_Type* p); + Node* write_static(Node* p); + Node* write_code(Node* p); int write_code(const char *cfile, const char *hfile, bool to_codeview=false); void write_public(int state); // writes pubic:/private: as needed void tag(int type, unsigned short uid); - static unsigned long block_crc(const void *data, int n=-1, unsigned long in_crc=0, bool *inout_line_start=NULL); + static unsigned long block_crc(const void *data, int n=-1, unsigned long in_crc=0, bool *inout_line_start=nullptr); }; } // namespace io diff --git a/fluid/io/Project_Reader.cxx b/fluid/io/Project_Reader.cxx index ff88238ba..4ef0c6523 100644 --- a/fluid/io/Project_Reader.cxx +++ b/fluid/io/Project_Reader.cxx @@ -1,12 +1,7 @@ // -// Fluid file routines for the Fast Light Tool Kit (FLTK). +// Fluid Project File Reader code for the Fast Light Tool Kit (FLTK). // -// You may find the basic read_* and write_* routines to -// be useful for other programs. I have used them many times. -// They are somewhat similar to tcl, using matching { and } -// to quote strings. -// -// Copyright 1998-2023 by Bill Spitzak and others. +// Copyright 1998-2025 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -19,18 +14,23 @@ // https://www.fltk.org/bugs.php // +// You may find the basic read_* and write_* routines to +// be useful for other programs. I have used them many times. +// They are somewhat similar to tcl, using matching { and } +// to quote strings. + #include "io/Project_Reader.h" -#include "app/fluid.h" -#include "app/project.h" +#include "Fluid.h" +#include "Project.h" #include "app/shell_command.h" -#include "app/undo.h" -#include "app/Fd_Snap_Action.h" +#include "proj/undo.h" +#include "app/Snap_Action.h" #include "nodes/factory.h" -#include "nodes/Fl_Function_Type.h" -#include "nodes/Fl_Widget_Type.h" -#include "nodes/Fl_Grid_Type.h" -#include "nodes/Fl_Window_Type.h" +#include "nodes/Function_Node.h" +#include "nodes/Widget_Node.h" +#include "nodes/Grid_Node.h" +#include "nodes/Window_Node.h" #include "widgets/Node_Browser.h" #include <FL/Fl_Window.H> @@ -53,12 +53,12 @@ int fld::io::fdesign_flip = 0; \param[in] filename read this file \param[in] merge if this is set, merge the file into an existing project - at Fl_Type::current + at Fluid.proj.tree.current \param[in] strategy add new nodes after current or as last child \return 0 if the operation failed, 1 if it succeeded */ -int fld::io::read_file(const char *filename, int merge, Strategy strategy) { - Project_Reader f; +int fld::io::read_file(Project &proj, const char *filename, int merge, Strategy strategy) { + Project_Reader f(proj); strategy.source(Strategy::FROM_FILE); return f.read_project(filename, merge, strategy); } @@ -97,13 +97,8 @@ void Project_Reader::expand_buffer(int length) { } /** \brief Construct local project reader. */ -Project_Reader::Project_Reader() -: fin(NULL), - lineno(0), - fname(NULL), - buffer(NULL), - buflen(0), - read_version(0.0) +Project_Reader::Project_Reader(Project &proj) +: proj_(proj) { } @@ -117,7 +112,7 @@ Project_Reader::~Project_Reader() /** Open an .fl file for reading. - \param[in] s filename, if NULL, read from stdin instead + \param[in] s filename, if nullptr, read from stdin instead \return 0 if the operation failed, 1 if it succeeded */ int Project_Reader::open_read(const char *s) { @@ -142,7 +137,7 @@ int Project_Reader::open_read(const char *s) { int Project_Reader::close_read() { if (fin != stdin) { int x = fclose(fin); - fin = 0; + fin = nullptr; return x >= 0; } return 1; @@ -200,17 +195,17 @@ int Project_Reader::read_quoted() { // read whatever character is after a \ If this is the first call, also read the global settings for this design. - \param[in] p parent node or NULL + \param[in] p parent node or nullptr \param[in] merge if set, merge into existing design, else replace design \param[in] strategy add nodes after current or as last child \param[in] skip_options this is set if the options were already found in a previous call, and there is no need to waste time searching for them. - \return the last type that was created + \return the last node that was created */ -Fl_Type *Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, char skip_options) { - Fl_Type::current = p; - Fl_Type *last_child_read = NULL; - Fl_Type *t = NULL; +Node *Project_Reader::read_children(Node *p, int merge, Strategy strategy, char skip_options) { + Fluid.proj.tree.current = p; + Node *last_child_read = nullptr; + Node *t = nullptr; for (;;) { const char *c = read_word(); REUSE_C: @@ -230,12 +225,12 @@ Fl_Type *Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, // this is the first word in a .fd file: if (!strcmp(c,"Magic:")) { read_fdesign(); - return NULL; + return nullptr; } if (!strcmp(c,"version")) { c = read_word(); - read_version = strtod(c,0); + read_version = strtod(c,nullptr); if (read_version<=0 || read_version>double(FL_VERSION+0.00001)) read_error("unknown version '%s'",c); continue; @@ -243,77 +238,77 @@ Fl_Type *Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, // back compatibility with Vincent Penne's original class code: if (!p && !strcmp(c,"define_in_struct")) { - Fl_Type *t = add_new_widget_from_file("class", Strategy::FROM_FILE_AS_LAST_CHILD); + Node *t = add_new_widget_from_file("class", Strategy::FROM_FILE_AS_LAST_CHILD); t->name(read_word()); - Fl_Type::current = p = t; + Fluid.proj.tree.current = p = t; merge = 1; // stops "missing }" error continue; } if (!strcmp(c,"do_not_include_H_from_C")) { - g_project.include_H_from_C=0; + proj_.include_H_from_C=0; goto CONTINUE; } if (!strcmp(c,"use_FL_COMMAND")) { - g_project.use_FL_COMMAND=1; + proj_.use_FL_COMMAND=1; goto CONTINUE; } if (!strcmp(c,"utf8_in_src")) { - g_project.utf8_in_src=1; + proj_.utf8_in_src=1; goto CONTINUE; } if (!strcmp(c,"avoid_early_includes")) { - g_project.avoid_early_includes=1; + proj_.avoid_early_includes=1; goto CONTINUE; } if (!strcmp(c,"i18n_type")) { - g_project.i18n_type = static_cast<Fd_I18n_Type>(atoi(read_word())); + proj_.i18n_type = static_cast<fld::I18n_Type>(atoi(read_word())); goto CONTINUE; } if (!strcmp(c,"i18n_gnu_function")) { - g_project.i18n_gnu_function = read_word(); + proj_.i18n_gnu_function = read_word(); goto CONTINUE; } if (!strcmp(c,"i18n_gnu_static_function")) { - g_project.i18n_gnu_static_function = read_word(); + proj_.i18n_gnu_static_function = read_word(); goto CONTINUE; } if (!strcmp(c,"i18n_pos_file")) { - g_project.i18n_pos_file = read_word(); + proj_.i18n_pos_file = read_word(); goto CONTINUE; } if (!strcmp(c,"i18n_pos_set")) { - g_project.i18n_pos_set = read_word(); + proj_.i18n_pos_set = read_word(); goto CONTINUE; } if (!strcmp(c,"i18n_include")) { - if (g_project.i18n_type == FD_I18N_GNU) - g_project.i18n_gnu_include = read_word(); - else if (g_project.i18n_type == FD_I18N_POSIX) - g_project.i18n_pos_include = read_word(); + if (proj_.i18n_type == fld::I18n_Type::GNU) + proj_.i18n_gnu_include = read_word(); + else if (proj_.i18n_type == fld::I18n_Type::POSIX) + proj_.i18n_pos_include = read_word(); goto CONTINUE; } if (!strcmp(c,"i18n_conditional")) { - if (g_project.i18n_type == FD_I18N_GNU) - g_project.i18n_gnu_conditional = read_word(); - else if (g_project.i18n_type == FD_I18N_POSIX) - g_project.i18n_pos_conditional = read_word(); + if (proj_.i18n_type == fld::I18n_Type::GNU) + proj_.i18n_gnu_conditional = read_word(); + else if (proj_.i18n_type == fld::I18n_Type::POSIX) + proj_.i18n_pos_conditional = read_word(); goto CONTINUE; } if (!strcmp(c,"header_name")) { - if (!g_project.header_file_set) g_project.header_file_name = read_word(); + if (!proj_.header_file_set) proj_.header_file_name = read_word(); else read_word(); goto CONTINUE; } if (!strcmp(c,"code_name")) { - if (!g_project.code_file_set) g_project.code_file_name = read_word(); + if (!proj_.code_file_set) proj_.code_file_name = read_word(); else read_word(); goto CONTINUE; } if (!strcmp(c, "snap")) { - g_layout_list.read(this); + Fluid.layout_list.read(this); goto CONTINUE; } @@ -333,7 +328,7 @@ Fl_Type *Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, } if (!strcmp(c, "mergeback")) { - g_project.write_mergeback_data = read_int(); + proj_.write_mergeback_data = read_int(); goto CONTINUE; } } @@ -350,7 +345,7 @@ Fl_Type *Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, c = read_word(1); if (strcmp(c,"{") && t->is_class()) { // <prefix> <name> - ((Fl_Class_Type*)t)->prefix(t->name()); + ((Class_Node*)t)->prefix(t->name()); t->name(c); c = read_word(1); } @@ -378,11 +373,11 @@ Fl_Type *Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, // FIXME: this has no business in the file reader! // TODO: this is called whenever something is pasted from the top level into a grid // It makes sense to make this more universal for other widget types too. - if (merge && t && t->parent && t->parent->is_a(ID_Grid)) { - if (Fl_Window_Type::popupx != 0x7FFFFFFF) { - ((Fl_Grid_Type*)t->parent)->insert_child_at(((Fl_Widget_Type*)t)->o, Fl_Window_Type::popupx, Fl_Window_Type::popupy); + if (merge && t && t->parent && t->parent->is_a(Type::Grid)) { + if (Window_Node::popupx != 0x7FFFFFFF) { + ((Grid_Node*)t->parent)->insert_child_at(((Widget_Node*)t)->o, Window_Node::popupx, Window_Node::popupy); } else { - ((Fl_Grid_Type*)t->parent)->insert_child_at_next_free_cell(((Fl_Widget_Type*)t)->o); + ((Grid_Node*)t->parent)->insert_child_at_next_free_cell(((Widget_Node*)t)->o); } } @@ -393,9 +388,9 @@ Fl_Type *Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, strategy.placement(Strategy::AFTER_CURRENT); } if (strategy.placement() == Strategy::AFTER_CURRENT) { - Fl_Type::current = t; + Fluid.proj.tree.current = t; } else { - Fl_Type::current = p; + Fluid.proj.tree.current = p; } CONTINUE:; @@ -410,46 +405,46 @@ Fl_Type *Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, /** \brief Read a .fl project file. \param[in] filename read this file \param[in] merge if this is set, merge the file into an existing project - at Fl_Type::current + at Fluid.proj.tree.current \param[in] strategy add new nodes after current or as last child \return 0 if the operation failed, 1 if it succeeded */ int Project_Reader::read_project(const char *filename, int merge, Strategy strategy) { - Fl_Type *o; - undo_suspend(); + Node *o; + proj_.undo.suspend(); read_version = 0.0; if (!open_read(filename)) { - undo_resume(); + proj_.undo.resume(); return 0; } if (merge) deselect(); else - g_project.reset(); - read_children(Fl_Type::current, merge, strategy); + proj_.reset(); + read_children(Fluid.proj.tree.current, merge, strategy); // clear this - Fl_Type::current = 0; + Fluid.proj.tree.current = nullptr; // Force menu items to be rebuilt... - for (o = Fl_Type::first; o; o = o->next) { - if (o->is_a(ID_Menu_Manager_)) { - o->add_child(0,0); + for (o = Fluid.proj.tree.first; o; o = o->next) { + if (o->is_a(Type::Menu_Manager_)) { + o->add_child(nullptr,nullptr); } } - for (o = Fl_Type::first; o; o = o->next) { + for (o = Fluid.proj.tree.first; o; o = o->next) { if (o->selected) { - Fl_Type::current = o; + Fluid.proj.tree.current = o; break; } } - selection_changed(Fl_Type::current); + selection_changed(Fluid.proj.tree.current); if (g_shell_config) { g_shell_config->rebuild_shell_menu(); g_shell_config->update_settings_dialog(); } - g_layout_list.update_dialogs(); - g_project.update_settings_dialog(); + Fluid.layout_list.update_dialogs(); + proj_.update_settings_dialog(); int ret = close_read(); - undo_resume(); + proj_.undo.resume(); return ret; } @@ -457,8 +452,8 @@ int Project_Reader::read_project(const char *filename, int merge, Strategy strat Display an error while reading the file. If the .fl file isn't opened for reading, pop up an FLTK dialog, otherwise print to stdout. - \note Matt: I am not sure why it is done this way. Shouldn't this depend on \c batch_mode? - \todo Not happy about this function. Output channel should depend on `batch_mode` + \note Matt: I am not sure why it is done this way. Shouldn't this depend on \c Fluid.batch_mode? + \todo Not happy about this function. Output channel should depend on `Fluid.batch_mode` as the note above already states. I want to make all file readers and writers depend on an error handling base class that outputs a useful analysis of file operations. @@ -480,7 +475,7 @@ void Project_Reader::read_error(const char *format, ...) { } /** - Return a word read from the .fl file, or NULL at the EOF. + Return a word read from the .fl file, or nullptr at the EOF. This will skip all comments (# to end of line), and evaluate all \\xxx sequences and use \\ at the end of line to remove the newline. @@ -504,7 +499,7 @@ const char *Project_Reader::read_word(int wantbrace) { for (;;) { x = nextchar(); if (x < 0 && feof(fin)) { // eof - return 0; + return nullptr; } else if (x == '#') { // comment do x = nextchar(); while (x >= 0 && x != '\n'); lineno++; @@ -650,7 +645,7 @@ static const char *class_matcher[] = { "2", "FL_BOX", // was FL_TEXT "62","FL_TIMER", "24","Fl_Value_Slider", - 0}; + nullptr}; /** @@ -712,13 +707,13 @@ static void forms_end(Fl_Group *g, int flip) { void Project_Reader::read_fdesign() { int fdesign_magic = atoi(read_word()); fdesign_flip = (fdesign_magic < 13000); - Fl_Widget_Type *window = 0; - Fl_Widget_Type *group = 0; - Fl_Widget_Type *widget = 0; - if (!Fl_Type::current) { - Fl_Type *t = add_new_widget_from_file("Function", Strategy::FROM_FILE_AS_LAST_CHILD); + Widget_Node *window = nullptr; + Widget_Node *group = nullptr; + Widget_Node *widget = nullptr; + if (!Fluid.proj.tree.current) { + Node *t = add_new_widget_from_file("Function", Strategy::FROM_FILE_AS_LAST_CHILD); t->name("create_the_forms()"); - Fl_Type::current = t; + Fluid.proj.tree.current = t; } for (;;) { const char *name; @@ -727,33 +722,33 @@ void Project_Reader::read_fdesign() { if (!strcmp(name,"Name")) { - window = (Fl_Widget_Type*)add_new_widget_from_file("Fl_Window", Strategy::FROM_FILE_AS_LAST_CHILD); + window = (Widget_Node*)add_new_widget_from_file("Fl_Window", Strategy::FROM_FILE_AS_LAST_CHILD); window->name(value); window->label(value); - Fl_Type::current = widget = window; + Fluid.proj.tree.current = widget = window; } else if (!strcmp(name,"class")) { if (!strcmp(value,"FL_BEGIN_GROUP")) { - group = widget = (Fl_Widget_Type*)add_new_widget_from_file("Fl_Group", Strategy::FROM_FILE_AS_LAST_CHILD); - Fl_Type::current = group; + group = widget = (Widget_Node*)add_new_widget_from_file("Fl_Group", Strategy::FROM_FILE_AS_LAST_CHILD); + Fluid.proj.tree.current = group; } else if (!strcmp(value,"FL_END_GROUP")) { if (group) { Fl_Group* g = (Fl_Group*)(group->o); g->begin(); forms_end(g, fdesign_flip); - Fl_Group::current(0); + Fl_Group::current(nullptr); } - group = widget = 0; - Fl_Type::current = window; + group = widget = nullptr; + Fluid.proj.tree.current = window; } else { for (int i = 0; class_matcher[i]; i += 2) if (!strcmp(value,class_matcher[i])) { value = class_matcher[i+1]; break;} - widget = (Fl_Widget_Type*)add_new_widget_from_file(value, Strategy::FROM_FILE_AS_LAST_CHILD); + widget = (Widget_Node*)add_new_widget_from_file(value, Strategy::FROM_FILE_AS_LAST_CHILD); if (!widget) { printf("class %s not found, using Fl_Button\n", value); - widget = (Fl_Widget_Type*)add_new_widget_from_file("Fl_Button", Strategy::FROM_FILE_AS_LAST_CHILD); + widget = (Widget_Node*)add_new_widget_from_file("Fl_Button", Strategy::FROM_FILE_AS_LAST_CHILD); } } diff --git a/fluid/io/Project_Reader.h b/fluid/io/Project_Reader.h index 20928d25f..b25b04ead 100644 --- a/fluid/io/Project_Reader.h +++ b/fluid/io/Project_Reader.h @@ -1,7 +1,7 @@ // -// Fluid file routines for the Fast Light Tool Kit (FLTK). +// Fluid Project File Reader header for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2023 by Bill Spitzak and others. +// Copyright 1998-2025 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -17,35 +17,41 @@ #ifndef FLUID_IO_PROJECT_READER_H #define FLUID_IO_PROJECT_READER_H -#include "nodes/Fl_Type.h" +#include "nodes/Node.h" #include <FL/fl_attr.h> #include <stdio.h> -class Fl_Type; +class Node; namespace fld { + +class Project; + namespace io { extern int fdesign_flip; -int read_file(const char *, int merge, Strategy strategy=Strategy::FROM_FILE_AS_LAST_CHILD); +int read_file(Project &proj, const char *, int merge, Strategy strategy=Strategy::FROM_FILE_AS_LAST_CHILD); class Project_Reader { protected: + /// Link Project_Reader class to the project. + Project &proj_; + /// Project input file - FILE *fin; + FILE *fin = nullptr; /// Number of most recently read line - int lineno; + int lineno = 0; /// Pointer to the file path and name (not copied!) - const char *fname; + const char *fname = nullptr; /// Expanding buffer to store the most recently read word - char *buffer; + char *buffer = nullptr; /// Exact size of the expanding buffer in bytes - int buflen; + int buflen = 0; void expand_buffer(int length); @@ -53,16 +59,16 @@ protected: public: /// Holds the file version number after reading the "version" tag - double read_version; + double read_version = 0.0; public: - Project_Reader(); + Project_Reader(Project &proj); ~Project_Reader(); int open_read(const char *s); int close_read(); const char *filename_name(); int read_quoted(); - Fl_Type *read_children(Fl_Type *p, int merge, Strategy strategy, char skip_options=0); + Node *read_children(Node *p, int merge, Strategy strategy, char skip_options=0); int read_project(const char *, int merge, Strategy strategy=Strategy::FROM_FILE_AS_LAST_CHILD); void read_error(const char *format, ...); const char *read_word(int wantbrace = 0); diff --git a/fluid/io/Project_Writer.cxx b/fluid/io/Project_Writer.cxx index 26af62f37..bfe6e0566 100644 --- a/fluid/io/Project_Writer.cxx +++ b/fluid/io/Project_Writer.cxx @@ -1,12 +1,12 @@ // -// Fluid file routines for the Fast Light Tool Kit (FLTK). +// Fluid Project File Writer code for the Fast Light Tool Kit (FLTK). // // You may find the basic read_* and write_* routines to // be useful for other programs. I have used them many times. // They are somewhat similar to tcl, using matching { and } // to quote strings. // -// Copyright 1998-2023 by Bill Spitzak and others. +// Copyright 1998-2025 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -21,11 +21,11 @@ #include "io/Project_Writer.h" -#include "app/fluid.h" -#include "app/project.h" +#include "Fluid.h" +#include "Project.h" #include "app/shell_command.h" -#include "app/undo.h" -#include "app/Fd_Snap_Action.h" +#include "proj/undo.h" +#include "app/Snap_Action.h" /// \defgroup flfile .fl Project File Operations /// \{ @@ -42,18 +42,16 @@ using namespace fld::io; is used to implement copy and paste. \return 0 if the operation failed, 1 if it succeeded */ -int fld::io::write_file(const char *filename, int selected_only, bool to_codeview) { - Project_Writer out; +int fld::io::write_file(Project &proj, const char *filename, int selected_only, bool to_codeview) { + Project_Writer out(proj); return out.write_project(filename, selected_only, to_codeview); } // ---- Project_Writer ---------------------------------------------- MARK: - /** \brief Construct local project writer. */ -Project_Writer::Project_Writer() -: fout(NULL), - needspace(0), - write_codeview_(false) +Project_Writer::Project_Writer(Project &proj) +: proj_(proj) { } @@ -64,8 +62,8 @@ Project_Writer::~Project_Writer() /** Open the .fl design file for writing. - If the filename is NULL, associate stdout instead. - \param[in] s the filename or NULL for stdout + If the filename is nullptr, associate stdout instead. + \param[in] s the filename or nullptr for stdout \return 1 if successful. 0 if the operation failed */ int Project_Writer::open_write(const char *s) { @@ -102,55 +100,55 @@ int Project_Writer::close_write() { */ int Project_Writer::write_project(const char *filename, int selected_only, bool sv) { write_codeview_ = sv; - undo_suspend(); + proj_.undo.suspend(); if (!open_write(filename)) { - undo_resume(); + proj_.undo.resume(); return 0; } write_string("# data file for the Fltk User Interface Designer (fluid)\n" "version %.4f",FL_VERSION); - if(!g_project.include_H_from_C) + if(!proj_.include_H_from_C) write_string("\ndo_not_include_H_from_C"); - if(g_project.use_FL_COMMAND) + if(proj_.use_FL_COMMAND) write_string("\nuse_FL_COMMAND"); - if (g_project.utf8_in_src) + if (proj_.utf8_in_src) write_string("\nutf8_in_src"); - if (g_project.avoid_early_includes) + if (proj_.avoid_early_includes) write_string("\navoid_early_includes"); - if (g_project.i18n_type) { - write_string("\ni18n_type %d", g_project.i18n_type); - switch (g_project.i18n_type) { - case FD_I18N_NONE: + if ((proj_.i18n_type != fld::I18n_Type::NONE)) { + write_string("\ni18n_type %d", static_cast<int>(proj_.i18n_type)); + switch (proj_.i18n_type) { + case fld::I18n_Type::NONE: break; - case FD_I18N_GNU : /* GNU gettext */ - write_string("\ni18n_include"); write_word(g_project.i18n_gnu_include.c_str()); - write_string("\ni18n_conditional"); write_word(g_project.i18n_gnu_conditional.c_str()); - write_string("\ni18n_gnu_function"); write_word(g_project.i18n_gnu_function.c_str()); - write_string("\ni18n_gnu_static_function"); write_word(g_project.i18n_gnu_static_function.c_str()); + case fld::I18n_Type::GNU : /* GNU gettext */ + write_string("\ni18n_include"); write_word(proj_.i18n_gnu_include.c_str()); + write_string("\ni18n_conditional"); write_word(proj_.i18n_gnu_conditional.c_str()); + write_string("\ni18n_gnu_function"); write_word(proj_.i18n_gnu_function.c_str()); + write_string("\ni18n_gnu_static_function"); write_word(proj_.i18n_gnu_static_function.c_str()); break; - case FD_I18N_POSIX : /* POSIX catgets */ - write_string("\ni18n_include"); write_word(g_project.i18n_pos_include.c_str()); - write_string("\ni18n_conditional"); write_word(g_project.i18n_pos_conditional.c_str()); - if (!g_project.i18n_pos_file.empty()) { + case fld::I18n_Type::POSIX : /* POSIX catgets */ + write_string("\ni18n_include"); write_word(proj_.i18n_pos_include.c_str()); + write_string("\ni18n_conditional"); write_word(proj_.i18n_pos_conditional.c_str()); + if (!proj_.i18n_pos_file.empty()) { write_string("\ni18n_pos_file"); - write_word(g_project.i18n_pos_file.c_str()); + write_word(proj_.i18n_pos_file.c_str()); } - write_string("\ni18n_pos_set"); write_word(g_project.i18n_pos_set.c_str()); + write_string("\ni18n_pos_set"); write_word(proj_.i18n_pos_set.c_str()); break; } } if (!selected_only) { - write_string("\nheader_name"); write_word(g_project.header_file_name.c_str()); - write_string("\ncode_name"); write_word(g_project.code_file_name.c_str()); - g_layout_list.write(this); + write_string("\nheader_name"); write_word(proj_.header_file_name.c_str()); + write_string("\ncode_name"); write_word(proj_.code_file_name.c_str()); + Fluid.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); + if (proj_.write_mergeback_data) + write_string("\nmergeback %d", proj_.write_mergeback_data); } - for (Fl_Type *p = Fl_Type::first; p;) { + for (Node *p = proj_.tree.first; p;) { if (!selected_only || p->selected) { p->write(*this); write_string("\n"); @@ -161,7 +159,7 @@ int Project_Writer::write_project(const char *filename, int selected_only, bool } } int ret = close_write(); - undo_resume(); + proj_.undo.resume(); return ret; } diff --git a/fluid/io/Project_Writer.h b/fluid/io/Project_Writer.h index c6e50ca7c..67aebb46e 100644 --- a/fluid/io/Project_Writer.h +++ b/fluid/io/Project_Writer.h @@ -1,7 +1,7 @@ // -// Fluid file routines for the Fast Light Tool Kit (FLTK). +// Fluid Project File Writer header for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2023 by Bill Spitzak and others. +// Copyright 1998-2025 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -21,25 +21,31 @@ #include <stdio.h> -class Fl_Type; +class Node; namespace fld { + +class Project; + namespace io { -int write_file(const char *, int selected_only = 0, bool to_codeview = false); +int write_file(Project &proj, const char *, int selected_only = 0, bool to_codeview = false); class Project_Writer { protected: + /// Link Project_Writer class to the project. + Project &proj_; + // Project output file, always opened in "wb" mode - FILE *fout; + FILE *fout = nullptr; /// If set, one space is written before text unless the format starts with a newline character - int needspace; + int needspace = 0; /// Set if this file will be used in the codeview dialog - bool write_codeview_; + bool write_codeview_ = false; public: - Project_Writer(); + Project_Writer(Project &proj); ~Project_Writer(); int open_write(const char *s); int close_write(); diff --git a/fluid/io/String_Writer.cxx b/fluid/io/String_Writer.cxx new file mode 100644 index 000000000..6b70ec1f3 --- /dev/null +++ b/fluid/io/String_Writer.cxx @@ -0,0 +1,142 @@ +// +// String File Writer code for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#include "io/String_Writer.h" + +#include "Fluid.h" +#include "Project.h" +#include "nodes/Window_Node.h" +#include "nodes/Function_Node.h" + +using namespace fld; +using namespace fld::io; + +/** + Write a string to a file, replacing all non-ASCII characters with octal codes. + \param[in] out output file + \param[in] text write this NUL terminated utf-8 string + \return EOF if any of the file access calls failed, 0 if OK + */ +static int write_escaped_strings(FILE *out, const char *text) { + int ret = 0; + const unsigned char *utf8_text = (const unsigned char *)text; + for (const unsigned char *s = utf8_text; *s; ++s) { + unsigned char c = *s; + // escape control characters, delete, all utf-8, and the double quotes + // note: we should have an option in the project settings to allow utf-8 + // characters in the output text and not escape them + if (c < 32 || c > 126 || c == '\"') { + if (c == '\r') { + ret = fputs("\\r", out); + } else if (c == '\n') { + ret = fputs("\\n", out); + } else { + ret = fprintf(out, "\\%03o", c); + } + } else { + ret = putc((int)c, out); + } + } + return ret; +} + +/** + Write a file that contains all label and tooltip strings for internationalization. + The user is responsible to set the right file name extension. The file format + is determined by `proj_.i18n_type`. + \param[in] filename file path and name to a file that will hold the strings + \return 1 if the file could not be opened for writing, or the result of `fclose`. + */ +int fld::io::write_strings(Project &proj, const std::string &filename) { + Node *p; + Widget_Node *w; + int i; + + FILE *fp = fl_fopen(filename.c_str(), "wb"); + if (!fp) return 1; + + switch (proj.i18n_type) { + case fld::I18n_Type::NONE : /* None, just put static text out */ + fprintf(fp, "# generated by Fast Light User Interface Designer (fluid) version %.4f\n", + FL_VERSION); + for (auto w: proj.tree.all_widgets()) { + if (w->label()) { + write_escaped_strings(fp, w->label()); + putc('\n', fp); + } + if (w->tooltip()) { + write_escaped_strings(fp, w->tooltip()); + putc('\n', fp); + } + } + break; + case fld::I18n_Type::GNU : /* GNU gettext, put a .po file out */ + fprintf(fp, "# generated by Fast Light User Interface Designer (fluid) version %.4f\n", + FL_VERSION); + for (p = proj.tree.first; p; p = p->next) { + if (p->is_widget()) { + w = (Widget_Node *)p; + + if (w->label()) { + fputs("msgid \"", fp); + write_escaped_strings(fp, w->label()); + fputs("\"\n", fp); + + fputs("msgstr \"", fp); + write_escaped_strings(fp, w->label()); + fputs("\"\n", fp); + } + + if (w->tooltip()) { + fputs("msgid \"", fp); + write_escaped_strings(fp, w->tooltip()); + fputs("\"\n", fp); + + fputs("msgstr \"", fp); + write_escaped_strings(fp, w->tooltip()); + fputs("\"\n", fp); + } + } + } + break; + case fld::I18n_Type::POSIX : /* POSIX catgets, put a .msg file out */ + fprintf(fp, "$ generated by Fast Light User Interface Designer (fluid) version %.4f\n", + FL_VERSION); + fprintf(fp, "$set %s\n", proj.i18n_pos_set.c_str()); + fputs("$quote \"\n", fp); + + for (i = 1, p = proj.tree.first; p; p = p->next) { + if (p->is_widget()) { + w = (Widget_Node *)p; + + if (w->label()) { + fprintf(fp, "%d \"", i ++); + write_escaped_strings(fp, w->label()); + fputs("\"\n", fp); + } + + if (w->tooltip()) { + fprintf(fp, "%d \"", i ++); + write_escaped_strings(fp, w->tooltip()); + fputs("\"\n", fp); + } + } + } + break; + } + + return fclose(fp); +} diff --git a/fluid/io/String_Writer.h b/fluid/io/String_Writer.h new file mode 100644 index 000000000..0109a32a3 --- /dev/null +++ b/fluid/io/String_Writer.h @@ -0,0 +1,33 @@ +// +// String File Writer header for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#ifndef FLUID_IO_STRING_WRITER_H +#define FLUID_IO_STRING_WRITER_H + +#include <string> + +namespace fld { + +class Project; + +namespace io { + +int write_strings(Project &proj, const std::string &filename); + +} // namespace io +} // namespace fld + +#endif // FLUID_IO_STRING_WRITER_H |
