diff options
| author | Matthias Melcher <github@matthiasm.com> | 2025-03-16 17:16:12 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-03-16 17:16:12 -0400 |
| commit | 51a55bc73660f64e8f4b32b8b4d3858f2a786f7b (patch) | |
| tree | 122ad9f838fcf8f61ed7cf5fa031e8ed69817e10 /fluid/Project.cxx | |
| parent | 13a7073a1e007ce5b71ef70bced1a9b15158820d (diff) | |
Fluid: restructuring and rejuvenation of the source code.
* Add classes for application and project
* Removed all globals from Fluid.h
* Extracting args and project history into their own classes
* Moving globals into Application class
* Initialize values inside headers for some classes.
* Undo functionality wrapped in a class inside Project.
* File reader and writer are now linked to a project.
* Avoid global project access
* Nodes (former Types) will be managed by a new Tree class.
* Removed static members (hidden globals) form Node/Fl_Type.
* Adding Tree iterator.
* Use nullptr instead of 0, NULL, or 0L
* Renamed Fl_..._Type to ..._Node, FL_OVERRIDE -> override
* Renaming ..._type to ...::prototype
* Splitting Widget Panel into multiple files.
* Moved callback code into widget panel file.
* Cleaning up Fluid_Image -> Image_asset
* Moving Fd_Snap_Action into new namespace fld::app::Snap_Action etc.
* Moved mergeback into proj folder.
* `enum ID` is now `enum class Type`.
Diffstat (limited to 'fluid/Project.cxx')
| -rw-r--r-- | fluid/Project.cxx | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/fluid/Project.cxx b/fluid/Project.cxx new file mode 100644 index 000000000..9eb555299 --- /dev/null +++ b/fluid/Project.cxx @@ -0,0 +1,335 @@ +// +// Fluid Project 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 "Project.h" + +#include "io/String_Writer.h" +#include "nodes/Node.h" +#include "panels/settings_panel.h" +#include "panels/codeview_panel.h" + +using namespace fld; + +// ---- project settings + +/** + Initialize a new project. + */ +Project::Project() { +} + +/** + Clear all project resources. + Not implemented. + */ +Project::~Project() { +} + +/** + Reset all project setting to create a new empty project. + */ +void Project::reset() { + ::delete_all(); + i18n_type = fld::I18n_Type::NONE; + + i18n_gnu_include = "<libintl.h>"; + i18n_gnu_conditional = ""; + i18n_gnu_function = "gettext"; + i18n_gnu_static_function = "gettext_noop"; + + i18n_pos_include = "<nl_types.h>"; + i18n_pos_conditional = ""; + i18n_pos_file = ""; + i18n_pos_set = "1"; + + include_H_from_C = 1; + use_FL_COMMAND = 0; + utf8_in_src = 0; + avoid_early_includes = 0; + header_file_set = 0; + code_file_set = 0; + header_file_name = ".h"; + code_file_name = ".cxx"; + write_mergeback_data = 0; +} + +/** + Tell the project and i18n tab of the settings dialog to refresh themselves. + */ +void Project::update_settings_dialog() { + if (settings_window) { + w_settings_project_tab->do_callback(w_settings_project_tab, LOAD); + w_settings_i18n_tab->do_callback(w_settings_i18n_tab, LOAD); + } +} + +/** + Get the absolute path of the project file, for example `/Users/matt/dev/`. + \return the path ending in '/' + */ +std::string Project::projectfile_path() const { + return end_with_slash(fl_filename_absolute_str(fl_filename_path_str(proj_filename), Fluid.launch_path())); +} + +/** + Get the project file name including extension, for example `test.fl`. + \return the file name without path + */ +std::string Project::projectfile_name() const { + return fl_filename_name(proj_filename); +} + +/** + Get the absolute path of the generated C++ code file, for example `/Users/matt/dev/src/`. + \return the path ending in '/' + */ +std::string Project::codefile_path() const { + std::string path = fl_filename_path_str(code_file_name); + if (Fluid.batch_mode) + return end_with_slash(fl_filename_absolute_str(path, Fluid.launch_path())); + else + return end_with_slash(fl_filename_absolute_str(path, projectfile_path())); +} + +/** + Get the generated C++ code file name including extension, for example `test.cxx`. + \return the file name without path + */ +std::string Project::codefile_name() const { + std::string name = fl_filename_name_str(code_file_name); + if (name.empty()) { + return fl_filename_setext_str(fl_filename_name(proj_filename), ".cxx"); + } else if (name[0] == '.') { + return fl_filename_setext_str(fl_filename_name(proj_filename), code_file_name); + } else { + return name; + } +} + +/** + Get the absolute path of the generated C++ header file, for example `/Users/matt/dev/src/`. + \return the path ending in '/' + */ +std::string Project::headerfile_path() const { + std::string path = fl_filename_path_str(header_file_name); + if (Fluid.batch_mode) + return end_with_slash(fl_filename_absolute_str(path, Fluid.launch_path())); + else + return end_with_slash(fl_filename_absolute_str(path, projectfile_path())); +} + +/** + Get the generated C++ header file name including extension, for example `test.cxx`. + \return the file name without path + */ +std::string Project::headerfile_name() const { + std::string name = fl_filename_name_str(header_file_name); + if (name.empty()) { + return fl_filename_setext_str(fl_filename_name_str(proj_filename), ".h"); + } else if (name[0] == '.') { + return fl_filename_setext_str(fl_filename_name_str(proj_filename), header_file_name); + } else { + return name; + } +} + +/** + Get the absolute path of the generated i18n strings file, for example `/Users/matt/dev/`. + Although it may be more useful to put the text file into the same directory + with the source and header file, historically, the text is always saved with + the project file in interactive mode, and in the FLUID launch directory in + batch mode. + \return the path ending in '/' + */ +std::string Project::stringsfile_path() const { + if (Fluid.batch_mode) + return Fluid.launch_path(); + else + return projectfile_path(); +} + +/** + Get the generated i18n text file name including extension, for example `test.po`. + \return the file name without path + */ +std::string Project::stringsfile_name() const { + switch (i18n_type) { + default: return fl_filename_setext_str(fl_filename_name(proj_filename), ".txt"); + case fld::I18n_Type::GNU: return fl_filename_setext_str(fl_filename_name(proj_filename), ".po"); + case fld::I18n_Type::POSIX: return fl_filename_setext_str(fl_filename_name(proj_filename), ".msg"); + } +} + +/** + Get the name of the project file without the filename extension. + \return the file name without path or extension + */ +std::string Project::basename() const { + return fl_filename_setext_str(fl_filename_name(proj_filename), ""); +} + + +/** + Change the current working directory to the .fl project directory. + + Every call to enter_project_dir() must have a corresponding leave_project_dir() + call. Enter and leave calls can be nested. + + The first call to enter_project_dir() remembers the original directory, usually + the launch directory of the application. Nested calls will increment a nesting + counter. When the nesting counter is back to 0, leave_project_dir() will return + to the original directory. + + The global variable 'filename' must be set to the current project file with + absolute or relative path information. + + \see leave_project_dir(), pwd, in_project_dir + */ +void Project::enter_project_dir() { + if (in_project_dir<0) { + fprintf(stderr, "** Fluid internal error: enter_project_dir() calls unmatched\n"); + return; + } + in_project_dir++; + // check if we are already in the project dir and do nothing if so + if (in_project_dir>1) return; + // check if there is an active project, and do nothing if there is none + if (!proj_filename || !*proj_filename) { + fprintf(stderr, "** Fluid internal error: enter_project_dir() no filename set\n"); + return; + } + // store the current working directory for later + app_work_dir = fl_getcwd_str(); + // set the current directory to the path of our .fl file + std::string project_path = fl_filename_path_str(fl_filename_absolute_str(proj_filename)); + if (fl_chdir(project_path.c_str()) == -1) { + fprintf(stderr, "** Fluid internal error: enter_project_dir() can't chdir to %s: %s\n", + project_path.c_str(), strerror(errno)); + return; + } + //fprintf(stderr, "chdir from %s to %s\n", app_work_dir.c_str(), fl_getcwd().c_str()); +} + +/** + Change the current working directory to the previous directory. + \see enter_project_dir(), pwd, in_project_dir + */ +void Project::leave_project_dir() { + if (in_project_dir == 0) { + fprintf(stderr, "** Fluid internal error: leave_project_dir() calls unmatched\n"); + return; + } + in_project_dir--; + // still nested, stay in the project directory + if (in_project_dir > 0) return; + // no longer nested, return to the original, usually the application working directory + if (fl_chdir(app_work_dir.c_str()) < 0) { + fprintf(stderr, "** Fluid internal error: leave_project_dir() can't chdir back to %s : %s\n", + app_work_dir.c_str(), strerror(errno)); + } +} + +/** + Set the filename of the current .fl design. + \param[in] c the new absolute filename and path + */ +void Project::set_filename(const char *c) { + if (proj_filename) free((void *)proj_filename); + proj_filename = c ? fl_strdup(c) : nullptr; + + if (proj_filename && !Fluid.batch_mode) + Fluid.history.update(proj_filename); + + set_modflag(modflag); +} + +/** + Write the strings that are used in i18n. + */ +void Project::write_strings() { + Fluid.flush_text_widgets(); + if (!proj_filename) { + Fluid.save_project_file(nullptr); + if (!proj_filename) return; + } + std::string filename = stringsfile_path() + stringsfile_name(); + int x = fld::io::write_strings(*this, filename); + if (Fluid.batch_mode) { + if (x) { + fprintf(stderr, "%s : %s\n", filename.c_str(), strerror(errno)); + exit(1); + } + } else { + if (x) { + fl_message("Can't write %s: %s", filename.c_str(), strerror(errno)); + } else if (completion_button->value()) { + fl_message("Wrote %s", stringsfile_name().c_str()); + } + } +} + + +/** + Set the "modified" flag and update the title of the main window. + + The first argument sets the modification state of the current design against + the corresponding .fl design file. Any change to the widget tree will mark + the design 'modified'. Saving the design will mark it clean. + + The second argument is optional and set the modification state of the current + design against the source code and header file. Any change to the tree, + including saving the tree, will mark the code 'outdated'. Generating source + code and header files will clear this flag until the next modification. + + \param[in] mf 0 to clear the modflag, 1 to mark the design "modified", -1 to + ignore this parameter + \param[in] mfc default -1 to let \c mf control \c modflag_c, 0 to mark the + code files current, 1 to mark it out of date. -2 to ignore changes to mf. + */ +void Project::set_modflag(int mf, int mfc) { + const char *code_ext = nullptr; + char new_title[FL_PATH_MAX]; + + // Update the modflag_c to the worst possible condition. We could be a bit + // more graceful and compare modification times of the files, but C++ has + // no API for that until C++17. + if (mf!=-1) { + modflag = mf; + if (mfc==-1 && mf==1) + mfc = mf; + } + if (mfc>=0) { + modflag_c = mfc; + } + + if (Fluid.main_window) { + std::string basename; + if (!proj_filename) basename = "Untitled.fl"; + else basename = fl_filename_name_str(std::string(proj_filename)); + code_ext = fl_filename_ext(code_file_name.c_str()); + char mod_star = modflag ? '*' : ' '; + char mod_c_star = modflag_c ? '*' : ' '; + snprintf(new_title, sizeof(new_title), "%s%c %s%c", + basename.c_str(), mod_star, code_ext, mod_c_star); + const char *old_title = Fluid.main_window->label(); + // only update the title if it actually changed + if (!old_title || strcmp(old_title, new_title)) + Fluid.main_window->copy_label(new_title); + } + // if the UI was modified in any way, update the Code View panel + if (codeview_panel && codeview_panel->visible() && cv_autorefresh->value()) + codeview_defer_update(); +} |
