From 51a55bc73660f64e8f4b32b8b4d3858f2a786f7b Mon Sep 17 00:00:00 2001 From: Matthias Melcher Date: Sun, 16 Mar 2025 17:16:12 -0400 Subject: 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`. --- fluid/CMakeLists.txt | 91 +- fluid/Fluid.cxx | 1392 +++++++++ fluid/Fluid.h | 219 ++ fluid/Project.cxx | 335 +++ fluid/Project.h | 145 + fluid/README_fl.txt | 22 +- fluid/app/Fd_Snap_Action.cxx | 1817 ------------ fluid/app/Fd_Snap_Action.h | 193 -- fluid/app/Fluid_Image.cxx | 413 --- fluid/app/Fluid_Image.h | 60 - fluid/app/Image_Asset.cxx | 532 ++++ fluid/app/Image_Asset.h | 63 + fluid/app/Menu.cxx | 190 ++ fluid/app/Menu.h | 29 + fluid/app/Snap_Action.cxx | 1835 ++++++++++++ fluid/app/Snap_Action.h | 207 ++ fluid/app/align_widget.cxx | 414 --- fluid/app/align_widget.h | 24 - fluid/app/args.cxx | 136 + fluid/app/args.h | 55 + fluid/app/fluid.cxx | 2169 -------------- fluid/app/fluid.h | 134 - fluid/app/history.cxx | 115 + fluid/app/history.h | 45 + fluid/app/mergeback.cxx | 493 ---- fluid/app/mergeback.h | 81 - fluid/app/project.cxx | 192 -- fluid/app/project.h | 103 - fluid/app/shell_command.cxx | 197 +- fluid/app/shell_command.h | 46 +- fluid/app/templates.cxx | 143 + fluid/app/templates.h | 29 + fluid/app/undo.cxx | 265 -- fluid/app/undo.h | 37 - fluid/io/Code_Writer.cxx | 430 +-- fluid/io/Code_Writer.h | 53 +- fluid/io/Project_Reader.cxx | 197 +- fluid/io/Project_Reader.h | 32 +- fluid/io/Project_Writer.cxx | 82 +- fluid/io/Project_Writer.h | 22 +- fluid/io/String_Writer.cxx | 142 + fluid/io/String_Writer.h | 33 + fluid/main.cxx | 4 +- fluid/nodes/Button_Node.cxx | 166 ++ fluid/nodes/Button_Node.h | 149 + fluid/nodes/Fl_Button_Type.cxx | 227 -- fluid/nodes/Fl_Button_Type.h | 46 - fluid/nodes/Fl_Function_Type.cxx | 2157 -------------- fluid/nodes/Fl_Function_Type.h | 259 -- fluid/nodes/Fl_Grid_Type.cxx | 994 ------- fluid/nodes/Fl_Grid_Type.h | 82 - fluid/nodes/Fl_Group_Type.cxx | 849 ------ fluid/nodes/Fl_Group_Type.h | 242 -- fluid/nodes/Fl_Menu_Type.cxx | 924 ------ fluid/nodes/Fl_Menu_Type.h | 287 -- fluid/nodes/Fl_Type.cxx | 1338 --------- fluid/nodes/Fl_Type.h | 323 --- fluid/nodes/Fl_Widget_Type.cxx | 3939 -------------------------- fluid/nodes/Fl_Widget_Type.h | 132 - fluid/nodes/Fl_Window_Type.cxx | 1562 ---------- fluid/nodes/Fl_Window_Type.h | 157 - fluid/nodes/Function_Node.cxx | 2158 ++++++++++++++ fluid/nodes/Function_Node.h | 277 ++ fluid/nodes/Grid_Node.cxx | 775 +++++ fluid/nodes/Grid_Node.h | 82 + fluid/nodes/Group_Node.cxx | 849 ++++++ fluid/nodes/Group_Node.h | 261 ++ fluid/nodes/Menu_Node.cxx | 876 ++++++ fluid/nodes/Menu_Node.h | 310 ++ fluid/nodes/Node.cxx | 1290 +++++++++ fluid/nodes/Node.h | 313 ++ fluid/nodes/Tree.cxx | 128 + fluid/nodes/Tree.h | 106 + fluid/nodes/Widget_Node.cxx | 2438 ++++++++++++++++ fluid/nodes/Widget_Node.h | 131 + fluid/nodes/Window_Node.cxx | 1497 ++++++++++ fluid/nodes/Window_Node.h | 163 ++ fluid/nodes/callbacks.cxx | 18 + fluid/nodes/callbacks.h | 23 + fluid/nodes/factory.cxx | 1286 +++++---- fluid/nodes/factory.h | 18 +- fluid/panels/about_panel.cxx | 5 +- fluid/panels/about_panel.fl | 6 +- fluid/panels/about_panel.h | 2 +- fluid/panels/codeview_panel.cxx | 115 +- fluid/panels/codeview_panel.fl | 117 +- fluid/panels/function_panel.cxx | 128 +- fluid/panels/function_panel.fl | 126 +- fluid/panels/print_panel.cxx | 586 ---- fluid/panels/print_panel.fl | 358 --- fluid/panels/print_panel.h | 56 - fluid/panels/settings_panel.cxx | 434 +-- fluid/panels/settings_panel.fl | 426 ++- fluid/panels/settings_panel.h | 9 +- fluid/panels/template_panel.cxx | 16 +- fluid/panels/template_panel.fl | 16 +- fluid/panels/widget_panel.cxx | 2840 ++++++++++++------- fluid/panels/widget_panel.fl | 2313 +++++++++------ fluid/panels/widget_panel.h | 115 +- fluid/panels/widget_panel/Grid_Child_Tab.cxx | 538 ++++ fluid/panels/widget_panel/Grid_Child_Tab.fl | 397 +++ fluid/panels/widget_panel/Grid_Child_Tab.h | 79 + fluid/panels/widget_panel/Grid_Tab.cxx | 778 +++++ fluid/panels/widget_panel/Grid_Tab.fl | 515 ++++ fluid/panels/widget_panel/Grid_Tab.h | 90 + fluid/proj/align_widget.cxx | 414 +++ fluid/proj/align_widget.h | 24 + fluid/proj/mergeback.cxx | 539 ++++ fluid/proj/mergeback.h | 81 + fluid/proj/undo.cxx | 271 ++ fluid/proj/undo.h | 93 + fluid/rsrcs/pixmaps.cxx | 144 +- fluid/rsrcs/pixmaps.h | 4 +- fluid/tools/ExternalCodeEditor_UNIX.cxx | 76 +- fluid/tools/ExternalCodeEditor_UNIX.h | 4 +- fluid/tools/ExternalCodeEditor_WIN32.cxx | 92 +- fluid/tools/ExternalCodeEditor_WIN32.h | 2 +- fluid/tools/autodoc.cxx | 64 +- fluid/tools/autodoc.h | 2 +- fluid/tools/filename.cxx | 32 +- fluid/tools/filename.h | 5 +- fluid/widgets/Bin_Button.cxx | 20 +- fluid/widgets/Code_Editor.cxx | 7 +- fluid/widgets/Code_Editor.h | 5 +- fluid/widgets/Code_Viewer.cxx | 5 +- fluid/widgets/Code_Viewer.h | 5 +- fluid/widgets/Formula_Input.cxx | 4 +- fluid/widgets/Formula_Input.h | 4 +- fluid/widgets/Node_Browser.cxx | 96 +- fluid/widgets/Node_Browser.h | 18 +- fluid/widgets/Text_Viewer.cxx | 5 +- fluid/widgets/Text_Viewer.h | 3 +- 132 files changed, 26935 insertions(+), 25197 deletions(-) create mode 100644 fluid/Fluid.cxx create mode 100644 fluid/Fluid.h create mode 100644 fluid/Project.cxx create mode 100644 fluid/Project.h delete mode 100644 fluid/app/Fd_Snap_Action.cxx delete mode 100644 fluid/app/Fd_Snap_Action.h delete mode 100644 fluid/app/Fluid_Image.cxx delete mode 100644 fluid/app/Fluid_Image.h create mode 100644 fluid/app/Image_Asset.cxx create mode 100644 fluid/app/Image_Asset.h create mode 100644 fluid/app/Menu.cxx create mode 100644 fluid/app/Menu.h create mode 100644 fluid/app/Snap_Action.cxx create mode 100644 fluid/app/Snap_Action.h delete mode 100644 fluid/app/align_widget.cxx delete mode 100644 fluid/app/align_widget.h create mode 100644 fluid/app/args.cxx create mode 100644 fluid/app/args.h delete mode 100644 fluid/app/fluid.cxx delete mode 100644 fluid/app/fluid.h create mode 100644 fluid/app/history.cxx create mode 100644 fluid/app/history.h delete mode 100644 fluid/app/mergeback.cxx delete mode 100644 fluid/app/mergeback.h delete mode 100644 fluid/app/project.cxx delete mode 100644 fluid/app/project.h create mode 100644 fluid/app/templates.cxx create mode 100644 fluid/app/templates.h delete mode 100644 fluid/app/undo.cxx delete mode 100644 fluid/app/undo.h create mode 100644 fluid/io/String_Writer.cxx create mode 100644 fluid/io/String_Writer.h create mode 100644 fluid/nodes/Button_Node.cxx create mode 100644 fluid/nodes/Button_Node.h delete mode 100644 fluid/nodes/Fl_Button_Type.cxx delete mode 100644 fluid/nodes/Fl_Button_Type.h delete mode 100644 fluid/nodes/Fl_Function_Type.cxx delete mode 100644 fluid/nodes/Fl_Function_Type.h delete mode 100644 fluid/nodes/Fl_Grid_Type.cxx delete mode 100644 fluid/nodes/Fl_Grid_Type.h delete mode 100644 fluid/nodes/Fl_Group_Type.cxx delete mode 100644 fluid/nodes/Fl_Group_Type.h delete mode 100644 fluid/nodes/Fl_Menu_Type.cxx delete mode 100644 fluid/nodes/Fl_Menu_Type.h delete mode 100644 fluid/nodes/Fl_Type.cxx delete mode 100644 fluid/nodes/Fl_Type.h delete mode 100644 fluid/nodes/Fl_Widget_Type.cxx delete mode 100644 fluid/nodes/Fl_Widget_Type.h delete mode 100644 fluid/nodes/Fl_Window_Type.cxx delete mode 100644 fluid/nodes/Fl_Window_Type.h create mode 100644 fluid/nodes/Function_Node.cxx create mode 100644 fluid/nodes/Function_Node.h create mode 100644 fluid/nodes/Grid_Node.cxx create mode 100644 fluid/nodes/Grid_Node.h create mode 100644 fluid/nodes/Group_Node.cxx create mode 100644 fluid/nodes/Group_Node.h create mode 100644 fluid/nodes/Menu_Node.cxx create mode 100644 fluid/nodes/Menu_Node.h create mode 100644 fluid/nodes/Node.cxx create mode 100644 fluid/nodes/Node.h create mode 100644 fluid/nodes/Tree.cxx create mode 100644 fluid/nodes/Tree.h create mode 100644 fluid/nodes/Widget_Node.cxx create mode 100644 fluid/nodes/Widget_Node.h create mode 100644 fluid/nodes/Window_Node.cxx create mode 100644 fluid/nodes/Window_Node.h create mode 100644 fluid/nodes/callbacks.cxx create mode 100644 fluid/nodes/callbacks.h delete mode 100644 fluid/panels/print_panel.cxx delete mode 100644 fluid/panels/print_panel.fl delete mode 100644 fluid/panels/print_panel.h create mode 100644 fluid/panels/widget_panel/Grid_Child_Tab.cxx create mode 100644 fluid/panels/widget_panel/Grid_Child_Tab.fl create mode 100644 fluid/panels/widget_panel/Grid_Child_Tab.h create mode 100644 fluid/panels/widget_panel/Grid_Tab.cxx create mode 100644 fluid/panels/widget_panel/Grid_Tab.fl create mode 100644 fluid/panels/widget_panel/Grid_Tab.h create mode 100644 fluid/proj/align_widget.cxx create mode 100644 fluid/proj/align_widget.h create mode 100644 fluid/proj/mergeback.cxx create mode 100644 fluid/proj/mergeback.h create mode 100644 fluid/proj/undo.cxx create mode 100644 fluid/proj/undo.h (limited to 'fluid') diff --git a/fluid/CMakeLists.txt b/fluid/CMakeLists.txt index 40f6e3589..bb28c2b9a 100644 --- a/fluid/CMakeLists.txt +++ b/fluid/CMakeLists.txt @@ -23,32 +23,41 @@ set(TARGETS fluid) # program fluid properly set(CPPFILES - app/align_widget.cxx - app/Fd_Snap_Action.cxx - app/fluid.cxx - app/Fluid_Image.cxx - app/mergeback.cxx - app/project.cxx + Fluid.cxx + Project.cxx + app/args.cxx + app/Snap_Action.cxx + app/Image_Asset.cxx + app/history.cxx + app/Menu.cxx app/shell_command.cxx - app/undo.cxx + app/templates.cxx io/Code_Writer.cxx io/Project_Writer.cxx io/Project_Reader.cxx + io/String_Writer.cxx + nodes/Tree.cxx + nodes/Button_Node.cxx + nodes/Function_Node.cxx + nodes/Grid_Node.cxx + nodes/Group_Node.cxx + nodes/Menu_Node.cxx + nodes/Node.cxx + nodes/Widget_Node.cxx + nodes/Window_Node.cxx + nodes/callbacks.cxx nodes/factory.cxx - nodes/Fl_Button_Type.cxx - nodes/Fl_Function_Type.cxx - nodes/Fl_Grid_Type.cxx - nodes/Fl_Group_Type.cxx - nodes/Fl_Menu_Type.cxx - nodes/Fl_Type.cxx - nodes/Fl_Widget_Type.cxx - nodes/Fl_Window_Type.cxx panels/about_panel.cxx panels/codeview_panel.cxx panels/function_panel.cxx panels/settings_panel.cxx panels/template_panel.cxx panels/widget_panel.cxx + panels/widget_panel/Grid_Tab.cxx + panels/widget_panel/Grid_Child_Tab.cxx + proj/align_widget.cxx + proj/mergeback.cxx + proj/undo.cxx rsrcs/pixmaps.cxx tools/autodoc.cxx tools/filename.cxx @@ -64,32 +73,41 @@ set(CPPFILES # List header files in Apple's Xcode IDE set(HEADERFILES - app/align_widget.h - app/Fd_Snap_Action.h - app/fluid.h - app/Fluid_Image.h - app/project.h - app/mergeback.h + Fluid.h + Project.h + app/args.h + app/Snap_Action.h + app/Image_Asset.h + app/history.h + app/Menu.h app/shell_command.h - app/undo.h + app/templates.h io/Code_Writer.h io/Project_Writer.h io/Project_Reader.h + io/String_Writer.h + nodes/Tree.h + nodes/Button_Node.h + nodes/Function_Node.h + nodes/Grid_Node.h + nodes/Group_Node.h + nodes/Menu_Node.h + nodes/Node.h + nodes/Widget_Node.h + nodes/Window_Node.h + nodes/callbacks.h nodes/factory.h - nodes/Fl_Button_Type.h - nodes/Fl_Function_Type.h - nodes/Fl_Grid_Type.h - nodes/Fl_Group_Type.h - nodes/Fl_Menu_Type.h - nodes/Fl_Type.h - nodes/Fl_Widget_Type.h - nodes/Fl_Window_Type.h panels/about_panel.h panels/codeview_panel.h panels/function_panel.h panels/settings_panel.h panels/template_panel.h panels/widget_panel.h + panels/widget_panel/Grid_Tab.h + panels/widget_panel/Grid_Child_Tab.h + proj/align_widget.h + proj/mergeback.h + proj/undo.h rsrcs/comments.h rsrcs/pixmaps.h tools/autodoc.h @@ -237,3 +255,16 @@ if(UNIX) ) endforeach() endif(UNIX) + +# Additional warnings during development + +if(APPLE) + if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + target_compile_options(fluid-lib PRIVATE + -Wall -Wextra -Wpedantic -Werror + -Wno-zero-as-null-pointer-constant + -Wno-missing-field-initializers + -Wno-unused-parameter + ) + endif() +endif(APPLE) diff --git a/fluid/Fluid.cxx b/fluid/Fluid.cxx new file mode 100644 index 000000000..48dd7dcfb --- /dev/null +++ b/fluid/Fluid.cxx @@ -0,0 +1,1392 @@ +// +// Fluid Application 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 "Fluid.h" + +#include "Project.h" +#include "proj/mergeback.h" +#include "app/Menu.h" +#include "app/shell_command.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "nodes/Node.h" +#include "nodes/Function_Node.h" +#include "nodes/Group_Node.h" +#include "nodes/Window_Node.h" +#include "nodes/factory.h" +#include "panels/settings_panel.h" +#include "panels/function_panel.h" +#include "panels/codeview_panel.h" +#include "panels/template_panel.h" +#include "panels/about_panel.h" +#include "rsrcs/pixmaps.h" +#include "tools/autodoc.h" +#include "widgets/Node_Browser.h" + +#include +#include +#ifdef __APPLE__ +#include // for fl_open_callback +#endif +#include +#include +#include +#include +#include +#include + +#include // setlocale().. +#include "../src/flstring.h" + + +fld::Application Fluid; + +using namespace fld; + + +/** + Timer to watch for external editor modifications. + + If one or more external editors open, check if their files were modified. + If so: reload to ram, update size/mtime records, and change fluid's + 'modified' state. + */ +static void external_editor_timer(void*) { + int editors_open = ExternalCodeEditor::editors_open(); + if ( Fluid.debug_external_editor ) printf("--- TIMER --- External editors open=%d\n", editors_open); + if ( editors_open > 0 ) { + // Walk tree looking for files modified by external editors. + int modified = 0; + for (Node *p: Fluid.proj.tree.all_nodes()) { + if ( p->is_a(Type::Code) ) { + Code_Node *code = static_cast(p); + // Code changed by external editor? + if ( code->handle_editor_changes() ) { // updates ram, file size/mtime + modified++; + } + if ( code->is_editing() ) { // editor open? + code->reap_editor(); // Try to reap; maybe it recently closed + } + } + } + if ( modified ) Fluid.proj.set_modflag(1); + } + // Repeat timeout if editors still open + // The ExternalCodeEditor class handles start/stopping timer, we just + // repeat_timeout() if it's already on. NOTE: above code may have reaped + // only open editor, which would disable further timeouts. So *recheck* + // if editors still open, to ensure we don't accidentally re-enable them. + // + if ( ExternalCodeEditor::editors_open() ) { + Fl::repeat_timeout(2.0, external_editor_timer); + } +} + + +/** + Create the Fluid application. + This creates the basic app with an empty project and reads the Fluid + preferences database. + */ +Application::Application() +: preferences( Fl_Preferences::USER_L, "fltk.org", "fluid" ) +{ } + + +/** + Start Fluid. + + Fluid can run in interactive mode with a full user interface to design new + user interfaces and write the C++ files to manage them, + + Fluid can run form the command line in batch mode to convert .fl design files + into C++ source and header files. In batch mode, no display is needed, + particularly no X11 connection will be attempted on Linux/Unix. + + \param[in] argc number of arguments in the list + \param[in] argv pointer to an array of arguments + \return in batch mode, an error code will be returned via \c exit() . This + function return 1, if there was an error in the parameters list. + \todo On Windows, Fluid can under certain conditions open a dialog box, even + in batch mode. Is that intentional? Does it circumvent issues with Windows' + stderr and stdout? + */ +int Application::run(int argc,char **argv) { + setlocale(LC_ALL, ""); // enable multi-language errors in file chooser + setlocale(LC_NUMERIC, "C"); // make sure numeric values are written correctly + launch_path_ = end_with_slash(fl_getcwd_str()); // store the current path at launch + + int i = 1; + if ((i = args.load(argc, argv)) == -1) + return 1; + + if (args.show_version) { + printf("fluid v%d.%d.%d\n", FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION); + ::exit(0); + } + + const char *c = nullptr; + if (args.autodoc_path.empty()) + c = argv[i]; + + fl_register_images(); + + make_main_window(); + + if (c) proj.set_filename(c); + if (!batch_mode) { +#ifdef __APPLE__ + fl_open_callback(apple_open_cb); +#endif // __APPLE__ + Fl::visual((Fl_Mode)(FL_DOUBLE|FL_INDEX)); + Fl_File_Icon::load_system_icons(); + main_window->callback(exit_cb); + position_window(main_window,"main_window_pos", 1, 10, 30, WINWIDTH, WINHEIGHT ); + if (g_shell_config) { + g_shell_config->read(preferences, fld::Tool_Store::USER); + g_shell_config->update_settings_dialog(); + g_shell_config->rebuild_shell_menu(); + } + 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]); + } + } + proj.undo.suspend(); + if (c && !fld::io::read_file(proj, c,0)) { + if (batch_mode) { + fprintf(stderr,"%s : %s\n", c, strerror(errno)); + exit(1); + } + fl_message("Can't read %s: %s", c, strerror(errno)); + } + proj.undo.resume(); + + // command line args override code and header filenames from the project file + // in batch mode only + if (batch_mode) { + if (!args.code_filename.empty()) { + proj.code_file_set = 1; + proj.code_file_name = args.code_filename; + } + if (!args.header_filename.empty()) { + proj.header_file_set = 1; + proj.header_file_name = args.header_filename; + } + } + + if (args.update_file) { // fluid -u + fld::io::write_file(proj, c, 0); + if (!args.compile_file) + exit(0); + } + + if (args.compile_file) { // fluid -c[s] + if (args.compile_strings) + proj.write_strings(); + write_code_files(); + exit(0); + } + + // don't lock up if inconsistent command line arguments were given + if (batch_mode) + exit(0); + + proj.set_modflag(0); + proj.undo.clear(); + + // Set (but do not start) timer callback for external editor updates + ExternalCodeEditor::set_update_timer_callback(external_editor_timer); + +#ifndef NDEBUG + // check if the user wants FLUID to generate image for the user documentation + if (!args.autodoc_path.empty()) { + run_autodoc(args.autodoc_path); + proj.set_modflag(0, 0); + quit(); + return 0; + } +#endif + + Fl::run(); + + proj.undo.clear(); + return 0; +} + + +/** + Exit Fluid; we hope you had a nice experience. + If the design was modified, a dialog will ask for confirmation. + */ +void Application::quit() { + if (shell_command_running()) { + int choice = fl_choice("Previous shell command still running!", + "Cancel", + "Exit", + nullptr); + if (choice == 0) { // user chose to cancel the exit operation + return; + } + } + + flush_text_widgets(); + + // verify user intention + if (confirm_project_clear() == false) + return; + + // Stop any external editor update timers + ExternalCodeEditor::stop_update_timer(); + + save_position(main_window,"main_window_pos"); + + if (widgetbin_panel) { + save_position(widgetbin_panel,"widgetbin_pos"); + delete widgetbin_panel; + } + if (codeview_panel) { + Fl_Preferences svp(preferences, "codeview"); + svp.set("autorefresh", cv_autorefresh->value()); + svp.set("autoposition", cv_autoposition->value()); + svp.set("tab", cv_tab->find(cv_tab->value())); + svp.set("code_choice", cv_code_choice); + save_position(codeview_panel,"codeview_pos"); + delete codeview_panel; + codeview_panel = nullptr; + } + if (shell_run_window) { + save_position(shell_run_window,"shell_run_Window_pos"); + } + + if (about_panel) + delete about_panel; + if (help_dialog) + delete help_dialog; + + if (g_shell_config) + g_shell_config->write(preferences, fld::Tool_Store::USER); + Fluid.layout_list.write(preferences, fld::Tool_Store::USER); + + proj.undo.clear(); + + // Destroy tree + // Doing so causes dtors to automatically close all external editors + // and cleans up editor tmp files. Then remove fluid tmpdir /last/. + proj.reset(); + ExternalCodeEditor::tmpdir_clear(); + delete_tmpdir(); + + exit(0); +} + + +/** + Return the working directory path at application launch. + \return a reference to the '/' terminated path. + */ +const std::string &Application::launch_path() const { + return launch_path_; +} + + +/** + Generate a path to a directory for temporary data storage. + \see delete_tmpdir(), get_tmpdir() + \todo remove duplicate API or reuse ExternalCodeEditor::create_tmpdir()! + */ +void Application::create_tmpdir() { + if (tmpdir_create_called) + return; + tmpdir_create_called = true; + + char buf[128]; +#if _WIN32 + // The usual temp file locations on Windows are + // %system%\Windows\Temp + // %userprofiles%\AppData\Local + // usually resolving into + // C:/Windows/Temp/ + // C:\Users\\AppData\Local\Temp + fl_snprintf(buf, sizeof(buf)-1, "fluid-%d/", (long)GetCurrentProcessId()); + std::string name = buf; + wchar_t tempdirW[FL_PATH_MAX+1]; + char tempdir[FL_PATH_MAX+1]; + unsigned len = GetTempPathW(FL_PATH_MAX, tempdirW); + if (len == 0) { + strcpy(tempdir, "c:/windows/temp/"); + } else { + unsigned wn = fl_utf8fromwc(tempdir, FL_PATH_MAX, tempdirW, len); + tempdir[wn] = 0; + } + std::string path = tempdir; + end_with_slash(path); + path += name; + fl_make_path(path.c_str()); + if (fl_access(path.c_str(), 6) == 0) tmpdir_path = path; +#else + fl_snprintf(buf, sizeof(buf)-1, "fluid-%d/", getpid()); + std::string name = buf; + auto path_temp = fl_getenv("TMPDIR"); + std::string path = path_temp ? path_temp : ""; + if (!path.empty()) { + end_with_slash(path); + path += name; + fl_make_path(path.c_str()); + if (fl_access(path.c_str(), 6) == 0) tmpdir_path = path; + } + if (tmpdir_path.empty()) { + path = std::string("/tmp/") + name; + fl_make_path(path.c_str()); + if (fl_access(path.c_str(), 6) == 0) tmpdir_path = path; + } +#endif + if (tmpdir_path.empty()) { + char pbuf[FL_PATH_MAX+1]; + preferences.get_userdata_path(pbuf, FL_PATH_MAX); + path = std::string(pbuf); + end_with_slash(path); + path += name; + fl_make_path(path.c_str()); + if (fl_access(path.c_str(), 6) == 0) tmpdir_path = path; + } + if (tmpdir_path.empty()) { + if (batch_mode) { + fprintf(stderr, "ERROR: Can't create directory for temporary data storage.\n"); + } else { + fl_alert("Can't create directory for temporary data storage."); + } + } +} + + +/** + Delete the temporary directory and all its contents. + \see create_tmpdir(), get_tmpdir() + */ +void Application::delete_tmpdir() { + // was a temporary directory created + if (!tmpdir_create_called) + return; + if (tmpdir_path.empty()) + return; + + // first delete all files that may still be left in the temp directory + struct dirent **de; + int n_de = fl_filename_list(tmpdir_path.c_str(), &de); + if (n_de >= 0) { + for (int i=0; id_name; + fl_unlink(path.c_str()); + } + fl_filename_free_list(&de, n_de); + } + + // then delete the directory itself + if (fl_rmdir(tmpdir_path.c_str()) < 0) { + if (batch_mode) { + fprintf(stderr, "WARNING: Can't delete tmpdir '%s': %s", tmpdir_path.c_str(), strerror(errno)); + } else { + fl_alert("WARNING: Can't delete tmpdir '%s': %s", tmpdir_path.c_str(), strerror(errno)); + } + } +} + + +/** + Return the path to a temporary directory for this instance of Fluid. + Fluid will do its best to clear and delete this directory when exiting. + \return the path to the temporary directory, ending in a '/', or and empty + string if no directory could be created. + */ +const std::string &Application::get_tmpdir() { + if (!tmpdir_create_called) + create_tmpdir(); + return tmpdir_path; +} + + +/** + Return the path and filename of a temporary file for cut or duplicated data. + \param[in] which 0 gets the cut/copy/paste buffer, 1 gets the duplication buffer + \return a pointer to a string in a static buffer + */ +const char *Application::cutfname(int which) { + static char name[2][FL_PATH_MAX]; + static char beenhere = 0; + + if (!beenhere) { + beenhere = 1; + preferences.getUserdataPath(name[0], sizeof(name[0])); + strlcat(name[0], "cut_buffer", sizeof(name[0])); + preferences.getUserdataPath(name[1], sizeof(name[1])); + strlcat(name[1], "dup_buffer", sizeof(name[1])); + } + + return name[which]; +} + + +/** + Clear the current project and create a new, empty one. + + If the current project was modified, FLUID will give the user the opportunity + to save the old project first. + + \param[in] user_must_confirm if set, a confimation dialog is presented to the + user before resetting the project. Default is `true`. + \return false if the operation was canceled + */ +bool Application::new_project(bool user_must_confirm) { + // verify user intention + if ((user_must_confirm) && (confirm_project_clear() == false)) + return false; + + // clear the current project + proj.reset(); + proj.set_filename(nullptr); + proj.set_modflag(0, 0); + widget_browser->rebuild(); + proj.update_settings_dialog(); + + // all is clear to continue + return true; +} + + +/** + Open a file chooser and load an exiting project file. + + If the current project was modified, FLUID will give the user the opportunity + to save the old project first. + + If no filename is given, FLUID will open a file chooser dialog. + + \param[in] filename_arg load from this file, or show file chooser if empty + \return false if the operation was canceled or failed otherwise + */ +bool Application::open_project_file(const std::string &filename_arg) { + // verify user intention + if (confirm_project_clear() == false) + return false; + + // ask for a filename if none was given + std::string new_filename = filename_arg; + if (new_filename.empty()) { + new_filename = open_project_filechooser("Open Project File"); + if (new_filename.empty()) { + return false; + } + } + + // clear the project and merge a file by the given name + new_project(false); + return merge_project_file(new_filename); +} + + +/** + Load a project from the give file name and path. + + The project file is inserted at the currently selected type. + + If no filename is given, FLUID will open a file chooser dialog. + + \param[in] filename_arg path and name of the new project file + \return false if the operation failed + */ +bool Application::merge_project_file(const std::string &filename_arg) { + bool is_a_merge = (!proj.tree.empty()); + std::string title = is_a_merge ? "Merge Project File" : "Open Project File"; + + // ask for a filename if none was given + std::string new_filename = filename_arg; + if (new_filename.empty()) { + new_filename = open_project_filechooser(title); + if (new_filename.empty()) { + return false; + } + } + + const char *c = new_filename.c_str(); + const char *oldfilename = proj.proj_filename; + proj.proj_filename = nullptr; + proj.set_filename(c); + if (is_a_merge) proj.undo.checkpoint(); + proj.undo.suspend(); + if (!fld::io::read_file(proj, c, is_a_merge)) { + proj.undo.resume(); + widget_browser->rebuild(); + proj.update_settings_dialog(); + fl_message("Can't read %s: %s", c, strerror(errno)); + free((void *)proj.proj_filename); + proj.proj_filename = oldfilename; + if (main_window) proj.set_modflag(proj.modflag); + return false; + } + proj.undo.resume(); + widget_browser->rebuild(); + if (is_a_merge) { + // Inserting a file; restore the original filename... + proj.set_filename(oldfilename); + proj.set_modflag(1); + } else { + // Loaded a file; free the old filename... + proj.set_modflag(0, 0); + proj.undo.clear(); + } + if (oldfilename) free((void *)oldfilename); + return true; +} + + +/** + Save the current design to the file given by \c filename. + If automatic, this overwrites an existing file. If interactive, if will + verify with the user. + \param[in] v if v is not nullptr, or no filename is set, open a filechooser. + */ +void Application::save_project_file(void *v) { + flush_text_widgets(); + Fl_Native_File_Chooser fnfc; + const char *c = proj.proj_filename; + if (v || !c || !*c) { + fnfc.title("Save To:"); + fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); + fnfc.filter("FLUID Files\t*.f[ld]"); + if (fnfc.show() != 0) return; + c = fnfc.filename(); + if (!fl_access(c, 0)) { + std::string basename = fl_filename_name_str(std::string(c)); + if (fl_choice("The file \"%s\" already exists.\n" + "Do you want to replace it?", "Cancel", + "Replace", nullptr, basename.c_str()) == 0) return; + } + + if (v != (void *)2) proj.set_filename(c); + } + if (!fld::io::write_file(proj, c)) { + fl_alert("Error writing %s: %s", c, strerror(errno)); + return; + } + + if (v != (void *)2) { + proj.set_modflag(0, 1); + proj.undo.save_ = proj.undo.current_; + } +} + + +/** + Reload the file set by \c filename, replacing the current design. + If the design was modified, a dialog will ask for confirmation. + */ +void Application::revert_project() { + if ( proj.modflag) { + if (!fl_choice("This user interface has been changed. Really revert?", + "Cancel", "Revert", nullptr)) return; + } + proj.undo.suspend(); + if (!fld::io::read_file(proj, proj.proj_filename, 0)) { + proj.undo.resume(); + widget_browser->rebuild(); + proj.update_settings_dialog(); + fl_message("Can't read %s: %s", proj.proj_filename, strerror(errno)); + return; + } + widget_browser->rebuild(); + proj.undo.resume(); + proj.set_modflag(0, 0); + proj.undo.clear(); + proj.update_settings_dialog(); +} + + +/** + Open the template browser and load a new file from templates. + + If the current project was modified, FLUID will give the user the opportunity + to save the old project first. + + \return false if the operation was canceled or failed otherwise + */ +bool Application::new_project_from_template() { + // clear the current project first + if (new_project() == false) + return false; + + // Setup the template panel... + if (!template_panel) make_template_panel(); + + template_clear(); + template_browser->add("Blank"); + template_load(); + + template_name->hide(); + template_name->value(""); + + template_instance->show(); + template_instance->deactivate(); + template_instance->value(""); + + template_delete->show(); + + template_submit->label("New"); + template_submit->deactivate(); + + template_panel->label("New"); + + //if ( template_browser->size() == 1 ) { // only one item? + template_browser->value(1); // select it + template_browser->do_callback(); + //} + + // Show the panel and wait for the user to do something... + template_panel->show(); + while (template_panel->shown()) Fl::wait(); + + // See if the user chose anything... + int item = template_browser->value(); + if (item < 1) return false; + + // Load the template, if any... + const char *tname = (const char *)template_browser->data(item); + + if (tname) { + // Grab the instance name... + const char *iname = template_instance->value(); + + if (iname && *iname) { + // Copy the template to a temp file, then read it in... + char line[1024], *ptr, *next; + FILE *infile, *outfile; + + if ((infile = fl_fopen(tname, "rb")) == nullptr) { + fl_alert("Error reading template file \"%s\":\n%s", tname, + strerror(errno)); + proj.set_modflag(0); + proj.undo.clear(); + return false; + } + + if ((outfile = fl_fopen(cutfname(1), "wb")) == nullptr) { + fl_alert("Error writing buffer file \"%s\":\n%s", cutfname(1), + strerror(errno)); + fclose(infile); + proj.set_modflag(0); + proj.undo.clear(); + return false; + } + + while (fgets(line, sizeof(line), infile)) { + // Replace @INSTANCE@ with the instance name... + for (ptr = line; (next = strstr(ptr, "@INSTANCE@")) != nullptr; ptr = next + 10) { + fwrite(ptr, next - ptr, 1, outfile); + fputs(iname, outfile); + } + + fputs(ptr, outfile); + } + + fclose(infile); + fclose(outfile); + + proj.undo.suspend(); + fld::io::read_file(proj, cutfname(1), 0); + fl_unlink(cutfname(1)); + proj.undo.resume(); + } else { + // No instance name, so read the template without replacements... + proj.undo.suspend(); + fld::io::read_file(proj, tname, 0); + proj.undo.resume(); + } + } + + widget_browser->rebuild(); + proj.update_settings_dialog(); + proj.set_modflag(0); + proj.undo.clear(); + + return true; +} + + +/** + Open the dialog to allow the user to print the current window. + */ +void Application::print_snapshots() { + int w, h, ww, hh; + int frompage, topage; + int num_windows = 0; // Number of windows + Window_Node *windows[1000]; // Windows to print + int winpage; // Current window page + Fl_Window *win; + + for (auto w: proj.tree.all_widgets()) { + if (w->is_a(Type::Window)) { + Window_Node *win_t = static_cast(w); + windows[num_windows] = win_t; + Fl_Window *win = static_cast(win_t->o); + if (!win->shown()) continue; + num_windows ++; + } + } + + Fl_Printer printjob; + if ( printjob.start_job(num_windows, &frompage, &topage) ) return; + int pagecount = 0; + for (winpage = 0; winpage < num_windows; winpage++) { + float scale = 1, scale_x = 1, scale_y = 1; + if (winpage+1 < frompage || winpage+1 > topage) continue; + printjob.start_page(); + printjob.printable_rect(&w, &h); + + // Get the time and date... + time_t curtime = time(nullptr); + struct tm *curdate = localtime(&curtime); + char date[1024]; + strftime(date, sizeof(date), "%c", curdate); + fl_font(FL_HELVETICA, 12); + fl_color(FL_BLACK); + fl_draw(date, (w - (int)fl_width(date))/2, fl_height()); + sprintf(date, "%d/%d", ++pagecount, topage-frompage+1); + fl_draw(date, w - (int)fl_width(date), fl_height()); + + // Get the base filename... + std::string basename = fl_filename_name_str(std::string(proj.proj_filename)); + fl_draw(basename.c_str(), 0, fl_height()); + + // print centered and scaled to fit in the page + win = (Fl_Window*)windows[winpage]->o; + ww = win->decorated_w(); + if(ww > w) scale_x = float(w)/ww; + hh = win->decorated_h(); + if(hh > h) scale_y = float(h)/hh; + if (scale_x < scale) scale = scale_x; + if (scale_y < scale) scale = scale_y; + if (scale < 1) { + printjob.scale(scale); + printjob.printable_rect(&w, &h); + } + printjob.origin(w/2, h/2); + printjob.print_window(win, -ww/2, -hh/2); + printjob.end_page(); + } + printjob.end_job(); +} + + +/** + Generate the C++ source and header filenames and write those files. + + This function creates the source filename by setting the file + extension to \c code_file_name and a header filename + with the extension \c header_file_name which are both + settable by the user. + + If the code filename has not been set yet, a "save file as" dialog will be + presented to the user. + + In batch_mode, the function will either be silent, or, if opening or writing + the files fails, write an error message to \c stderr and exit with exit code 1. + + In interactive mode, it will pop up an error message, or, if the user + hasn't disabled that, pop up a confirmation message. + + \param[in] dont_show_completion_dialog don't show the completion dialog + \return 1 if the operation failed, 0 if it succeeded + */ +int Application::write_code_files(bool dont_show_completion_dialog) +{ + // -- handle user interface issues + flush_text_widgets(); + if (!proj.proj_filename) { + save_project_file(nullptr); + if (!proj.proj_filename) return 1; + } + + // -- generate the file names with absolute paths + fld::io::Code_Writer f(proj); + std::string code_filename = proj.codefile_path() + proj.codefile_name(); + std::string header_filename = proj.headerfile_path() + proj.headerfile_name(); + + // -- write the code and header files + if (!batch_mode) proj.enter_project_dir(); + int x = f.write_code(code_filename.c_str(), header_filename.c_str()); + std::string code_filename_rel = fl_filename_relative_str(code_filename); + std::string header_filename_rel = fl_filename_relative_str(header_filename); + if (!batch_mode) proj.leave_project_dir(); + + // -- print error message in batch mode or pop up an error or confirmation dialog box + if (batch_mode) { + if (!x) { + fprintf(stderr, "%s and %s: %s\n", + code_filename_rel.c_str(), + header_filename_rel.c_str(), + strerror(errno)); + exit(1); + } + } else { + if (!x) { + fl_message("Can't write %s or %s: %s", + code_filename_rel.c_str(), + header_filename_rel.c_str(), + strerror(errno)); + } else { + proj.set_modflag(-1, 0); + if (dont_show_completion_dialog==false && completion_button->value()) { + fl_message("Wrote %s and %s", + code_filename_rel.c_str(), + header_filename_rel.c_str()); + } + } + } + return 0; +} + + +/** + User chose to cut the currently selected widgets. + */ +void Application::cut_selected() { + if (!proj.tree.current) { + fl_beep(); + return; + } + flush_text_widgets(); + if (!fld::io::write_file(proj, cutfname(),1)) { + fl_message("Can't write %s: %s", cutfname(), strerror(errno)); + return; + } + proj.undo.checkpoint(); + proj.set_modflag(1); + ipasteoffset = 0; + Node *p = proj.tree.current->parent; + while (p && p->selected) p = p->parent; + delete_all(1); + if (p) select_only(p); + widget_browser->rebuild(); +} + + +/** + User chose to copy the currently selected widgets. + */ +void Application::copy_selected() { + flush_text_widgets(); + if (!proj.tree.current) { + fl_beep(); + return; + } + flush_text_widgets(); + ipasteoffset = 10; + if (!fld::io::write_file(proj, cutfname(),1)) { + fl_message("Can't write %s: %s", cutfname(), strerror(errno)); + return; + } +} + + +/** + User chose to paste the widgets from the cut buffer. + + This function will paste the widgets in the cut buffer after the currently + selected widget. If the currently selected widget is a group widget and + it is not folded, the new widgets will be added inside the group. + */ +void Application::paste_from_clipboard() { + pasteoffset = ipasteoffset; + proj.undo.checkpoint(); + proj.undo.suspend(); + Strategy strategy = Strategy::FROM_FILE_AFTER_CURRENT; + if (proj.tree.current && proj.tree.current->can_have_children()) { + if (proj.tree.current->folded_ == 0) { + // If the current widget is a group widget and it is not folded, + // add the new widgets inside the group. + strategy = Strategy::FROM_FILE_AS_LAST_CHILD; + // The following alternative also works quite nicely + //strategy = Strategy::FROM_FILE_AS_FIRST_CHILD; + } + } + if (!fld::io::read_file(proj, cutfname(), 1, strategy)) { + widget_browser->rebuild(); + fl_message("Can't read %s: %s", cutfname(), strerror(errno)); + } + proj.undo.resume(); + widget_browser->display(proj.tree.current); + widget_browser->rebuild(); + pasteoffset = 0; + ipasteoffset += 10; +} + + +/** + Duplicate the selected widgets. + + This code is a bit complex because it needs to find the last selected + widget with the lowest level, so that the new widgets are inserted after + this one. + */ +void Application::duplicate_selected() { + if (!proj.tree.current) { + fl_beep(); + return; + } + + // flush the text widgets to make sure the user's changes are saved: + flush_text_widgets(); + + // find the last selected node with the lowest level: + int lowest_level = 9999; + Node *new_insert = nullptr; + if (proj.tree.current->selected) { + for (auto t: proj.tree.all_selected_nodes()) { + if (t->level <= lowest_level) { + lowest_level = t->level; + new_insert = t; + } + } + } + if (new_insert) + proj.tree.current = new_insert; + + // write the selected widgets to a file: + if (!fld::io::write_file(proj, cutfname(1),1)) { + fl_message("Can't write %s: %s", cutfname(1), strerror(errno)); + return; + } + + // read the file and add the widgets after the current one: + pasteoffset = 0; + proj.undo.checkpoint(); + proj.undo.suspend(); + if (!fld::io::read_file(proj, cutfname(1), 1, Strategy::FROM_FILE_AFTER_CURRENT)) { + fl_message("Can't read %s: %s", cutfname(1), strerror(errno)); + } + fl_unlink(cutfname(1)); + widget_browser->display(proj.tree.current); + widget_browser->rebuild(); + proj.undo.resume(); +} + + +/** + User chose to delete the currently selected widgets. + */ +void Application::delete_selected() { + if (!proj.tree.current) { + fl_beep(); + return; + } + proj.undo.checkpoint(); + proj.set_modflag(1); + ipasteoffset = 0; + Node *p = proj.tree.current->parent; + while (p && p->selected) p = p->parent; + delete_all(1); + if (p) select_only(p); + widget_browser->rebuild(); +} + + +/** + Show the editor for the \c current Node. + */ +void Application::edit_selected() { + if (!proj.tree.current) { + fl_message("Please select a widget"); + return; + } + proj.tree.current->open(); +} + + +/** + User wants to sort selected widgets by y coordinate. + */ +void Application::sort_selected() { + proj.undo.checkpoint(); + sort((Node*)nullptr); + widget_browser->rebuild(); + proj.set_modflag(1); +} + + +/** + Show or hide the widget bin. + The state is stored in the app preferences. + */ +void Application::toggle_widget_bin() { + if (!widgetbin_panel) { + make_widgetbin(); + if (!position_window(widgetbin_panel,"widgetbin_pos", 1, 320, 30)) return; + } + + if (widgetbin_panel->visible()) { + widgetbin_panel->hide(); + widgetbin_item->label("Show Widget &Bin..."); + } else { + widgetbin_panel->show(); + widgetbin_item->label("Hide Widget &Bin"); + } +} + + +/** + Open a dialog to show the HTML help page form the FLTK documentation folder. + \param[in] name name of the HTML help file. + */ +void Application::show_help(const char *name) { + const char *docdir; + char helpname[FL_PATH_MAX]; + + if (!help_dialog) help_dialog = new Fl_Help_Dialog(); + + if ((docdir = fl_getenv("FLTK_DOCDIR")) == nullptr) { + docdir = FLTK_DOCDIR; + } + snprintf(helpname, sizeof(helpname), "%s/%s", docdir, name); + + // make sure that we can read the file + FILE *f = fopen(helpname, "rb"); + if (f) { + fclose(f); + help_dialog->load(helpname); + } else { + // if we can not read the file, we display the canned version instead + // or ask the native browser to open the page on www.fltk.org + if (strcmp(name, "fluid.html")==0) { + if (!Fl_Shared_Image::find("embedded:/fluid_flow_chart_800.png")) + new Fl_PNG_Image("embedded:/fluid_flow_chart_800.png", fluid_flow_chart_800_png, sizeof(fluid_flow_chart_800_png)); + help_dialog->value + ( + "\n" + "FLTK: Programming with FLUID\n" + "

What is FLUID?

\n" + "The Fast Light User Interface Designer, or FLUID, is a graphical editor " + "that is used to produce FLTK source code. FLUID edits and saves its state " + "in .fl files. These files are text, and you can (with care) " + "edit them in a text editor, perhaps to get some special effects.

\n" + "FLUID can \"compile\" the .fl file into a .cxx " + "and a .h file. The .cxx file defines all the " + "objects from the .fl file and the .h file " + "declares all the global ones. FLUID also supports localization " + "(Internationalization) of label strings using message files and the GNU " + "gettext or POSIX catgets interfaces.

\n" + "A simple program can be made by putting all your code (including a " + "main() function) into the .fl file and thus making the " + ".cxx file a single source file to compile. Most programs are " + "more complex than this, so you write other .cxx files that " + "call the FLUID functions. These .cxx files must " + "#include the .h file or they can #include " + "the .cxx file so it still appears to be a single source file.

" + "

" + "

More information is available online at https://www.fltk.org/" + "" + ); + } else if (strcmp(name, "license.html")==0) { + fl_open_uri("https://www.fltk.org/doc-1.5/license.html"); + return; + } else if (strcmp(name, "index.html")==0) { + fl_open_uri("https://www.fltk.org/doc-1.5/index.html"); + return; + } else { + snprintf(helpname, sizeof(helpname), "https://www.fltk.org/%s", name); + fl_open_uri(helpname); + return; + } + } + help_dialog->show(); +} + + +/** + Open the "About" dialog. + */ +void Application::about() { +#if 1 + if (!about_panel) make_about_panel(); + about_panel->show(); +#else + for (auto &n: proj.tree.all_nodes()) { + puts(n.name()); + } +#endif +} + + +/** + Build the main app window and create a few other dialogs. + */ +void Application::make_main_window() { + if (!batch_mode) { + preferences.get("show_guides", show_guides, 1); + preferences.get("show_restricted", show_restricted, 1); + preferences.get("show_ghosted_outline", show_ghosted_outline, 0); + preferences.get("show_comments", show_comments, 1); + make_shell_window(); + } + + if (!main_window) { + Fl_Widget *o; + loadPixmaps(); + main_window = new Fl_Double_Window(WINWIDTH,WINHEIGHT,"fluid"); + main_window->box(FL_NO_BOX); + o = make_widget_browser(0,MENUHEIGHT,BROWSERWIDTH,BROWSERHEIGHT); + o->box(FL_FLAT_BOX); + o->tooltip("Double-click to view or change an item."); + main_window->resizable(o); + main_menubar = new Fl_Menu_Bar(0,0,BROWSERWIDTH,MENUHEIGHT); + main_menubar->menu(main_menu); + // quick access to all dynamic menu items + save_item = (Fl_Menu_Item*)main_menubar->find_item(menu_file_save_cb); + history_item = (Fl_Menu_Item*)main_menubar->find_item(menu_file_open_history_cb); + widgetbin_item = (Fl_Menu_Item*)main_menubar->find_item(toggle_widgetbin_cb); + codeview_item = (Fl_Menu_Item*)main_menubar->find_item((Fl_Callback*)toggle_codeview_cb); + overlay_item = (Fl_Menu_Item*)main_menubar->find_item((Fl_Callback*)toggle_overlays); + guides_item = (Fl_Menu_Item*)main_menubar->find_item((Fl_Callback*)toggle_guides); + restricted_item = (Fl_Menu_Item*)main_menubar->find_item((Fl_Callback*)toggle_restricted); + main_menubar->global(); + fill_in_New_Menu(); + main_window->end(); + } + + if (!batch_mode) { + history.load(); + g_shell_config = new Fd_Shell_Command_List; + widget_browser->load_prefs(); + make_settings_window(); + } +} + + +/** + Open a native file chooser to allow choosing a project file for reading. + + Path and filename are preset with the current project filename, if there + is one. + + \param title a text describing the action after selecting a file (load, merge, ...) + \return the file path and name, or an empty string if the operation was canceled + */ +std::string Application::open_project_filechooser(const std::string &title) { + Fl_Native_File_Chooser dialog; + dialog.title(title.c_str()); + dialog.type(Fl_Native_File_Chooser::BROWSE_FILE); + dialog.filter("FLUID Files\t*.f[ld]\n"); + if (proj.proj_filename) { + std::string current_project_file = proj.proj_filename; + dialog.directory(fl_filename_path_str(current_project_file).c_str()); + dialog.preset_file(fl_filename_name_str(current_project_file).c_str()); + } + if (dialog.show() != 0) + return std::string(); + return std::string(dialog.filename()); +} + + +/** + Give the user the opportunity to save a project before clearing it. + + If the project has unsaved changes, this function pops up a dialog, that + allows the user to save the project, continue without saving the project, + or to cancel the operation. + + If the user chooses to save, and no filename was set, a file dialog allows + the user to pick a name and location, or to cancel the operation. + + \return false if the user aborted the operation and the calling function + should abort as well + */ +bool Application::confirm_project_clear() { + if (proj.modflag == 0) return true; + switch (fl_choice("This project has unsaved changes. Do you want to save\n" + "the project file before proceeding?", + "Cancel", "Save", "Don't Save")) + { + case 0 : /* Cancel */ + return false; + case 1 : /* Save */ + save_project_file(nullptr); + if (proj.modflag) return false; // user canceled the "Save As" dialog + } + return true; +} + + +/** + Ensure that text widgets in the widget panel propagates apply current changes. + By temporarily clearing the text focus, all text widgets with changed text + will unfocus and call their respective callbacks, propagating those changes to + their data set. + */ +void Application::flush_text_widgets() { + if (Fl::focus() && (Fl::focus()->top_window() == the_panel)) { + Fl_Widget *old_focus = Fl::focus(); + Fl::focus(nullptr); // trigger callback of the widget that is losing focus + Fl::focus(old_focus); + } +} + + +/** + Position the given window window based on entries in the app preferences. + Customisable by user; feature can be switched off. + The window is not shown or hidden by this function, but a value is returned + to indicate the state to the caller. + \param[in] w position this window + \param[in] prefsName name of the preferences item that stores the window settings + \param[in] Visible default value if window is hidden or shown + \param[in] X, Y, W, H default size and position if nothing is specified in the preferences + \return 1 if the caller should make the window visible, 0 if hidden. + */ +char Application::position_window(Fl_Window *w, const char *prefsName, int Visible, int X, int Y, int W, int H) { + Fl_Preferences pos(preferences, prefsName); + if (prevpos_button->value()) { + pos.get("x", X, X); + pos.get("y", Y, Y); + if ( W!=0 ) { + pos.get("w", W, W); + pos.get("h", H, H); + w->resize( X, Y, W, H ); + } + else + w->position( X, Y ); + } + pos.get("visible", Visible, Visible); + return Visible; +} + + +/** + Save the position and visibility state of a window to the app preferences. + \param[in] w save this window data + \param[in] prefsName name of the preferences item that stores the window settings + */ +void Application::save_position(Fl_Window *w, const char *prefsName) { + Fl_Preferences pos(preferences, prefsName); + pos.set("x", w->x()); + pos.set("y", w->y()); + pos.set("w", w->w()); + pos.set("h", w->h()); + pos.set("visible", (int)(w->shown() && w->visible())); +} + + +/** + Change the app's and hence preview the design's scheme. + + The scheme setting is stored in the app preferences + - in key \p 'scheme_name' since 1.4.0 + - in key \p 'scheme' (index: 0 - 4) in 1.3.x + + This callback is triggered by changing the scheme in the + Fl_Scheme_Choice widget (\p Edit/GUI Settings). + + \param[in] choice the calling widget + + \see init_scheme() for choice values and backwards compatibility + */ +void Application::set_scheme(const char *new_scheme) { + if (batch_mode) + return; + + // set the new scheme only if the scheme was changed + if (Fl::is_scheme(new_scheme)) + return; + + Fl::scheme(new_scheme); + preferences.set("scheme_name", new_scheme); + + // Backwards compatibility: store 1.3 scheme index (1-4). + // We assume that index 0-3 (base, plastic, gtk+, gleam) are in the + // same order as in 1.3.x (index 1-4), higher values are ignored + + int scheme_index = scheme_choice->value(); + if (scheme_index <= 3) // max. index for 1.3.x (Gleam) + preferences.set("scheme", scheme_index + 1); // compensate for different indexing +} + + +/** + Read Fluid's scheme preferences and set the app's scheme. + + Since FLTK 1.4.0 the scheme \b name is stored as a character string + with key "scheme_name" in the preference database. + + In FLTK 1.3.x the scheme preference was stored as an integer index + with key "scheme" in the database. The known schemes were hardcoded in + Fluid's sources (here for reference): + + | Index | 1.3 Scheme Name | Choice | 1.4 Scheme Name | + |-------|-----------------|-------|-----------------| + | 0 | Default (same as None) | n/a | n/a | + | 1 | None (same as Default) | 0 | base | + | 2 | Plastic | 1 | plastic | + | 3 | GTK+ | 2 | gtk+ | + | 4 | Gleam | 3 | gleam | + | n/a | n/a | 4 | oxy | + + The new Fluid tries to keep backwards compatibility and reads both + keys (\p scheme and \p scheme_name). If the latter is defined, it is used. + If not the old \p scheme (index) is used - but we need to subtract one to + get the new Fl_Scheme_Choice index (column "Choice" above). + */ +void Application::init_scheme() { + int scheme_index = 0; // scheme index for backwards compatibility (1.3.x) + char *scheme_name = nullptr; // scheme name since 1.4.0 + preferences.get("scheme_name", scheme_name, "XXX"); // XXX means: not set => fallback 1.3.x + if (!strcmp(scheme_name, "XXX")) { + preferences.get("scheme", scheme_index, 0); + if (scheme_index > 0) { + scheme_index--; + scheme_choice->value(scheme_index); // set the choice value + } + if (scheme_index < 0) + scheme_index = 0; + else if (scheme_index > scheme_choice->size() - 1) + scheme_index = 0; + scheme_name = const_cast(scheme_choice->text(scheme_index)); + preferences.set("scheme_name", scheme_name); + } + // Set the new scheme only if it was not overridden by the -scheme + // command line option + if (Fl::scheme() == nullptr) { + Fl::scheme(scheme_name); + } + free(scheme_name); +} + + +#ifdef __APPLE__ +/** + Handle app launch with an associated filename (macOS only). + Should there be a modified design already, Fluid asks for user confirmation. + \param[in] c the filename of the new design + */ +void Application::apple_open_cb(const char *c) { + Fluid.open_project_file(std::string(c)); +} +#endif // __APPLE__ + diff --git a/fluid/Fluid.h b/fluid/Fluid.h new file mode 100644 index 000000000..1857518ee --- /dev/null +++ b/fluid/Fluid.h @@ -0,0 +1,219 @@ +// +// Fluid Application 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_FLUID_H +#define FLUID_FLUID_H + +#include "Project.h" +#include "app/args.h" +#include "app/history.h" +#include "app/Snap_Action.h" +#include "tools/filename.h" + +#include +#include +#include + +#include + +constexpr int BROWSERWIDTH = 300; +constexpr int BROWSERHEIGHT = 500; +constexpr int WINWIDTH = 300; +constexpr int MENUHEIGHT = 25; +constexpr int WINHEIGHT = (BROWSERHEIGHT+MENUHEIGHT); + +// ---- types + +class Fl_Double_Window; +class Fl_Window; +class Fl_Menu_Bar; +class Node; +class Fl_Choice; +class Fl_Button; +class Fl_Check_Button; +class Fl_Help_Dialog; + +namespace fld { +namespace app { +class Layout_List; +} + +/** + Indicate the storage location for tools like layout suites and shell macros. + \see class Fd_Shell_Command, class Layout_Suite + */ +enum class Tool_Store { + INTERNAL, ///< stored inside FLUID app + USER, ///< suite is stored in the user wide FLUID settings + PROJECT, ///< suite is stored within the current .fl project file + FILE ///< store suite in external file +}; + + +class Project; + +class Application { + /// Currently selected project. + Project *current_project_ = new Project(); + /// Working directory at application launch. + std::string launch_path_; + /// Path to store temporary files during app run. + std::string tmpdir_path; + /// True if the temporary file path was already created. + bool tmpdir_create_called = false; + // Generate a path to a directory for temporary data storage. + void create_tmpdir(); + // Delete the temporary directory and all its contents. + void delete_tmpdir(); + +public: // Member Variables + /// Application wide preferences + Fl_Preferences preferences; + /// Project history. + app::History history; + /// Command line arguments + app::Args args; + /// List of available layouts + app::Layout_List layout_list; + /// Set, if Fluid runs in batch mode, and no user interface is activated. + int batch_mode { 0 }; // fluid + any code generators (-u, -c, -cs) + + // TODO: make this into a class: app::Settings + /// Show guides in the design window when positioning widgets, saved in app preferences. + int show_guides { 1 }; + /// Show areas of restricted use in overlay plane. + /// Restricted areas are widget that overlap each other, widgets that are outside + /// of their parent's bounds (except children of Scroll groups), and areas + /// within an Fl_Tile that are not covered by children. + int show_restricted { 1 }; + /// Show a ghosted outline for groups that have very little contrast. + /// This makes groups with NO_BOX or FLAT_BOX better editable. + int show_ghosted_outline { 1 }; + /// Show widget comments in the browser, saved in app preferences. + int show_comments { 1 }; + + // TODO: make this into a class: app::External_Editor + /// Use external editor for editing Code_Node, saved in app preferences. + int use_external_editor { 0 }; + /// Debugging help for external Code_Node editor. + int debug_external_editor { 0 }; + /// Run this command to load an Code_Node into an external editor, save in app preferences. + // TODO: make this into a std::string + char external_editor_command[512] { }; + + // TODO: make this into a class: app::GUI + Fl_Window *main_window { nullptr }; + static Fl_Menu_Item main_menu[]; + Fl_Menu_Bar *main_menubar { nullptr }; + Fl_Menu_Item *save_item { nullptr }; + Fl_Menu_Item *history_item { nullptr }; + Fl_Menu_Item *widgetbin_item { nullptr }; + Fl_Menu_Item *codeview_item { nullptr }; + Fl_Menu_Item *overlay_item { nullptr }; + Fl_Button *overlay_button { nullptr }; + Fl_Menu_Item *guides_item { nullptr }; + Fl_Menu_Item *restricted_item { nullptr }; + /// Offset in pixels when adding widgets from an .fl file. + int pasteoffset { 0 }; + int ipasteoffset { 0 }; + /// FLUID-wide help dialog. + Fl_Help_Dialog *help_dialog { nullptr }; + +public: // Methods + // Create the Fluid application. + Application(); + /// Destructor. + ~Application() = default; + // Launch the application. + int run(int argc,char **argv); + // Quit the application and clean up. + void quit(); + /// Quick access to the current project. Make sure it stays synched to current_project_. + Project &proj { *current_project_ }; + // Return the working directory path at application launch. + const std::string &launch_path() const; + // Return the path to a temporary directory for this instance of Fluid. + const std::string &get_tmpdir(); + // Return the path and filename of a temporary file for cut or duplicated data. + const char *cutfname(int which = 0); + + // Clear the current project and create a new, empty one. + bool new_project(bool user_must_confirm = true); + // Open a file chooser and load an exiting project file. + bool open_project_file(const std::string &filename_arg); + // Load a project from the give file name and path. + bool merge_project_file(const std::string &filename_arg); + // Save the current design to the file given by \c filename. + void save_project_file(void *arg); + // Reload the file set by \c filename, replacing the current design. + void revert_project(); + // Open the template browser and load a new file from templates. + bool new_project_from_template(); + // Open the dialog to allow the user to print the current window. + void print_snapshots(); + // Generate the C++ source and header filenames and write those files. + int write_code_files(bool dont_show_completion_dialog=false); + + // User chose to cut the currently selected widgets. + void cut_selected(); + // User chose to copy the currently selected widgets. + void copy_selected(); + // User chose to paste the widgets from the cut buffer. + void paste_from_clipboard(); + // Duplicate the selected widgets. + void duplicate_selected(); + // User chose to delete the currently selected widgets. + void delete_selected(); + // Show the editor for the \c current Node. + void edit_selected(); + // User wants to sort selected widgets by y coordinate. + void sort_selected(); + // Show or hide the widget bin. + void toggle_widget_bin(); + // Open a dialog to show the HTML help page form the FLTK documentation folder. + void show_help(const char *name); + // Open the "About" dialog. + void about(); + + // Build the main app window and create a few other dialogs. + void make_main_window(); + // Open a native file chooser to allow choosing a project file for reading. + std::string open_project_filechooser(const std::string &title); + // Give the user the opportunity to save a project before clearing it. + bool confirm_project_clear(); + // Ensure that text widgets in the widget panel propagates apply current changes. + void flush_text_widgets(); + // Position the given window window based on entries in the app preferences. + char position_window(Fl_Window *w, const char *prefsName, int Visible, int X, int Y, int W=0, int H=0); + // Save the position and visibility state of a window to the app preferences. + void save_position(Fl_Window *w, const char *prefsName); + // Change the app's and hence preview the design's scheme. + void set_scheme(const char *new_scheme); + // Read Fluid's scheme preferences and set the app's scheme. + void init_scheme(); + +#ifdef __APPLE__ + static void apple_open_cb(const char *c); +#endif // __APPLE__ +}; + +} // namespace fld + +extern fld::Application Fluid; + + +#endif // FLUID_FLUID_H + 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 = ""; + i18n_gnu_conditional = ""; + i18n_gnu_function = "gettext"; + i18n_gnu_static_function = "gettext_noop"; + + i18n_pos_include = ""; + 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(); +} diff --git a/fluid/Project.h b/fluid/Project.h new file mode 100644 index 000000000..e60f7bc26 --- /dev/null +++ b/fluid/Project.h @@ -0,0 +1,145 @@ +// +// Fluid Project 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_PROJECT_H +#define FLUID_PROJECT_H + +#include "proj/undo.h" +#include "nodes/Tree.h" + +#include + +// ---- project class declaration + + +namespace fld { + +namespace app { + class Layout_Preset; + extern Layout_Preset *default_layout_preset; +} // namespace app + +/** + Enumeration of available internationalization types. + */ +enum class I18n_Type { + NONE = 0, ///< No i18n, all strings are litearals + GNU, ///< GNU gettext internationalization + POSIX ///< Posix catgets internationalization +}; + + +/** + Data and settings for a FLUID project file. + */ +class Project +{ +public: // Member Variables + // Undo actions for this Project. + proj::Undo undo { *this }; + + // Manage the node tree of the project. + node::Tree tree { *this }; + + /// One of the available internationalization types. + fld::I18n_Type i18n_type = I18n_Type::NONE; + /// Include file for GNU i18n, writes an #include statement into the source + /// file. This is usually `` or `"gettext.h"` for GNU gettext. + std::string i18n_gnu_include = ""; + // Optional name of a macro for conditional i18n compilation. + std::string i18n_gnu_conditional = ""; + /// For the gettext/intl.h options, this is the function that translates text + /// at runtime. This is usually "gettext" or "_". + std::string i18n_gnu_function = "gettext"; + /// For the gettext/intl.h options, this is the function that marks the translation + /// of text at initialisation time. This is usually "gettext_noop" or "N_". + std::string i18n_gnu_static_function = "gettext_noop"; + + /// Include file for Posix i18n, write a #include statement into the source + /// file. This is usually `` for Posix catgets. + std::string i18n_pos_include = ""; + // Optional name of a macro for conditional i18n compilation. + std::string i18n_pos_conditional = ""; + /// Name of the nl_catd database + std::string i18n_pos_file = ""; + /// Message set ID for the catalog. + std::string i18n_pos_set = "1"; + + /// If set, generate code to include the header file form the c++ file + int include_H_from_C = 1; + /// If set, handle keyboard shortcut Ctrl on macOS using Cmd instead + int use_FL_COMMAND = 0; + /// Clear if UTF-8 characters in statics texts are written as escape sequences + int utf8_in_src = 0; + /// If set, will not be included from the header code before anything else + int avoid_early_includes = 0; + /// If set, command line overrides header file name in .fl file. + int header_file_set = 0; + /// If set, command line overrides source code file name in .fl file. + int code_file_set = 0; + /// later + int write_mergeback_data = 0; + /// Filename of the current .fl project file + const char *proj_filename { nullptr }; + /// Hold the default extension for header files, or the entire filename if set via command line. + std::string header_file_name = ".h"; + /// Hold the default extension for source code files, or the entire filename if set via command line. + std::string code_file_name = ".cxx"; + + /// Used as a counter to set the .fl project dir as the current directory. + int in_project_dir { 0 }; + /// Application work directory, stored here when temporarily changing to the source code directory. + std::string app_work_dir = ""; + + /// Set if the current design has been modified compared to the associated .fl design file. + int modflag { 0 }; + /// Set if the code files are older than the current design. + int modflag_c { 0 }; + + /// Currently used layout preset. + app::Layout_Preset *layout { app::default_layout_preset }; + +public: // Methods + Project(); + ~Project(); + void reset(); + void update_settings_dialog(); + + std::string projectfile_path() const; + std::string projectfile_name() const; + std::string codefile_path() const; + std::string codefile_name() const; + std::string headerfile_path() const; + std::string headerfile_name() const; + std::string stringsfile_path() const; + std::string stringsfile_name() const; + std::string basename() const; + + void enter_project_dir(); + void leave_project_dir(); + + void set_filename(const char *c); + void write_strings(); + + void set_modflag(int mf, int mfc = -1); +}; + +} // namespace fld + +#endif // FLUID_PROJECT_H + + diff --git a/fluid/README_fl.txt b/fluid/README_fl.txt index 2e69d4cfa..21486d664 100644 --- a/fluid/README_fl.txt +++ b/fluid/README_fl.txt @@ -293,7 +293,7 @@ Note: the hierarchical dependency is implemented twice and somewhat conflicting The list of known Types and their inheritance is: - Fl_Type (note: can't be written) + Node (note: can't be written) +-- Function +-- code +-- codeblock @@ -315,7 +315,7 @@ The list of known Types and their inheritance is: | | +-- Fl_Tile | | +-- Fl_Wizard | | +-- Fl_Grid - | +-- Fl_Menu_Type (note: can't be written) + | +-- Menu_Node (note: can't be written) | | +-- Fl_Menu_Button | | +-- Fl_Choice | | +-- Fl_Input_Choice @@ -373,7 +373,7 @@ are stored as "parent_properties { ...list... }". If a node encounters this property tag, it must ask its parent to interpret the contents of that list. See Fl_Grid for an example. -Type Fl_Type +Type Node "uid" <4-digit-hex> : since Oct 2023, optional, a unique id for this node within the project file @@ -392,12 +392,12 @@ Type "Function" : function signature “C” : if set, function is extern “C” “return_type” : C or C++ type descriptor, can start with “virtual” and/or “static” to further define the function. - ... : inherits more from Fl_Type + ... : inherits more from Node Type codeblock : C++ code, for example "if (test())" "after" : C++ code or comment following the closing '}' - ... : inherits more from Fl_Type + ... : inherits more from Node Type "decl" : C++ code to declare a variable or class member @@ -405,7 +405,7 @@ Type "decl" : C++ code to declare a variable or class member defaults to "private" none or "local" or "global": for declaration in the code body defaults to "global" - ... : inherits more from Fl_Type + ... : inherits more from Node Type "data" : C++ variable name @@ -418,21 +418,21 @@ Type "declblock" : C++ code none or "public" or "protected" : defaults to private (obsolete) "map" : integer value, default is 2 (CODE_IN_SOURCE), - see Fl_DeclBlock_Type::write_map_ + see DeclBlock_Node::write_map_ "after" : C++ code or comment following the block - ... : inherits more from Fl_Type + ... : inherits more from Node Type "comment" : comment text "in_source" or "not_in_source": default to in_source "in_header" or "not_in_header": default to in_header - ... : inherits more from Fl_Type + ... : inherits more from Node Type "class" : prefix, class name none or "private" or "protected" : defaults to public ":" : name of super class - ... : inherits more from Fl_Type + ... : inherits more from Node Type "Fl_Widget" : C++ variable name @@ -487,7 +487,7 @@ Type "Fl_Widget" : C++ variable name "shortcut" : integer "code0" or "code1" or "code2" or "code3" : C++ extra code lines "extra_code" : C++ extra code lines - ... : inherits more from Fl_Type + ... : inherits more from Node Type "Fl_Button" : C++ variable name diff --git a/fluid/app/Fd_Snap_Action.cxx b/fluid/app/Fd_Snap_Action.cxx deleted file mode 100644 index 0a407ac8d..000000000 --- a/fluid/app/Fd_Snap_Action.cxx +++ /dev/null @@ -1,1817 +0,0 @@ -// -// Snap action code file for the Fast Light Tool Kit (FLTK). -// -// Copyright 2023 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 "app/Fd_Snap_Action.h" - -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "nodes/Fl_Group_Type.h" -#include "panels/settings_panel.h" - -#include -#include -#include -#include -#include -#include - -// TODO: warning if the user wants to change builtin layouts -// TODO: move panel to global settings panel (move load & save to main pulldown, or to toolbox?) -// INFO: how about a small tool box for quick preset selection and disabling of individual snaps? - -void select_layout_suite_cb(Fl_Widget *, void *user_data); - -int Fd_Snap_Action::eex = 0; -int Fd_Snap_Action::eey = 0; - -static Fd_Layout_Preset fltk_app = { - 15, 15, 15, 15, 0, 0, // window: l, r, t, b, gx, gy - 10, 10, 10, 10, 0, 0, // group: l, r, t, b, gx, gy - 25, 25, // tabs: t, b - 20, 10, 4, // widget_x: min, inc, gap - 20, 4, 8, // widget_y: min, inc, gap - 0, 14, -1, 14 // labelfont/size, textfont/size -}; -static Fd_Layout_Preset fltk_dlg = { - 10, 10, 10, 10, 0, 0, // window: l, r, t, b, gx, gy - 10, 10, 10, 10, 0, 0, // group: l, r, t, b, gx, gy - 20, 20, // tabs: t, b - 20, 10, 5, // widget_x: min, inc, gap - 20, 5, 5, // widget_y: min, inc, gap - 0, 11, -1, 11 // labelfont/size, textfont/size -}; -static Fd_Layout_Preset fltk_tool = { - 10, 10, 10, 10, 0, 0, // window: l, r, t, b, gx, gy - 10, 10, 10, 10, 0, 0, // group: l, r, t, b, gx, gy - 18, 18, // tabs: t, b - 16, 8, 2, // widget_x: min, inc, gap - 16, 4, 2, // widget_y: min, inc, gap - 0, 10, -1, 10 // labelfont/size, textfont/size -}; - -static Fd_Layout_Preset grid_app = { - 12, 12, 12, 12, 12, 12, // window: l, r, t, b, gx, gy - 12, 12, 12, 12, 12, 12, // group: l, r, t, b, gx, gy - 24, 24, // tabs: t, b - 12, 6, 6, // widget_x: min, inc, gap - 12, 6, 6, // widget_y: min, inc, gap - 0, 14, -1, 14 // labelfont/size, textfont/size -}; - -static Fd_Layout_Preset grid_dlg = { - 10, 10, 10, 10, 10, 10, // window: l, r, t, b, gx, gy - 10, 10, 10, 10, 10, 10, // group: l, r, t, b, gx, gy - 20, 20, // tabs: t, b - 10, 5, 5, // widget_x: min, inc, gap - 10, 5, 5, // widget_y: min, inc, gap - 0, 12, -1, 12 // labelfont/size, textfont/size -}; - -static Fd_Layout_Preset grid_tool = { - 8, 8, 8, 8, 8, 8, // window: l, r, t, b, gx, gy - 8, 8, 8, 8, 8, 8, // group: l, r, t, b, gx, gy - 16, 16, // tabs: t, b - 8, 4, 4, // widget_x: min, inc, gap - 8, 4, 4, // widget_y: min, inc, gap - 0, 10, -1, 10 // labelfont/size, textfont/size -}; - -static Fd_Layout_Suite static_suite_list[] = { - { (char*)"FLTK", (char*)"@fd_beaker FLTK", { &fltk_app, &fltk_dlg, &fltk_tool }, FD_STORE_INTERNAL }, - { (char*)"Grid", (char*)"@fd_beaker Grid", { &grid_app, &grid_dlg, &grid_tool }, FD_STORE_INTERNAL } -}; - -Fl_Menu_Item main_layout_submenu_[] = { - { static_suite_list[0].menu_label, 0, select_layout_suite_cb, (void*)0, FL_MENU_RADIO|FL_MENU_VALUE }, - { static_suite_list[1].menu_label, 0, select_layout_suite_cb, (void*)1, FL_MENU_RADIO }, - { NULL } -}; - -static Fl_Menu_Item static_choice_menu[] = { - { static_suite_list[0].menu_label }, - { static_suite_list[1].menu_label }, - { NULL } -}; - -Fd_Layout_Preset *layout = &fltk_app; -Fd_Layout_List g_layout_list; - -// ---- Callbacks ------------------------------------------------------ MARK: - - -void layout_suite_marker(Fl_Widget *, void *) { - // intentionally left empty -} - -void select_layout_suite_cb(Fl_Widget *, void *user_data) { - int index = (int)(fl_intptr_t)user_data; - assert(index >= 0); - assert(index < g_layout_list.list_size_); - g_layout_list.current_suite(index); - g_layout_list.update_dialogs(); -} - -void select_layout_preset_cb(Fl_Widget *, void *user_data) { - int index = (int)(fl_intptr_t)user_data; - assert(index >= 0); - assert(index < 3); - g_layout_list.current_preset(index); - g_layout_list.update_dialogs(); -} - -void edit_layout_preset_cb(Fl_Button *w, long user_data) { - int index = (int)w->argument(); - assert(index >= 0); - assert(index < 3); - if (user_data == (long)(fl_intptr_t)LOAD) { - w->value(g_layout_list.current_preset() == index); - } else { - g_layout_list.current_preset(index); - g_layout_list.update_dialogs(); - } -} - -// ---- Fd_Layout_Suite ------------------------------------------------ MARK: - - -/** - Write presets to a Preferences database. - */ -void Fd_Layout_Preset::write(Fl_Preferences &prefs) { - assert(this); - Fl_Preferences p_win(prefs, "Window"); - p_win.set("left_margin", left_window_margin); - p_win.set("right_margin", right_window_margin); - p_win.set("top_margin", top_window_margin); - p_win.set("bottom_margin", bottom_window_margin); - p_win.set("grid_x", window_grid_x); - p_win.set("grid_y", window_grid_y); - - Fl_Preferences p_grp(prefs, "Group"); - p_grp.set("left_margin", left_group_margin); - p_grp.set("right_margin", right_group_margin); - p_grp.set("top_margin", top_group_margin); - p_grp.set("bottom_margin", bottom_group_margin); - p_grp.set("grid_x", group_grid_x); - p_grp.set("grid_y", group_grid_y); - - Fl_Preferences p_tbs(prefs, "Tabs"); - p_tbs.set("top_margin", top_tabs_margin); - p_tbs.set("bottom_margin", bottom_tabs_margin); - - Fl_Preferences p_wgt(prefs, "Widget"); - p_wgt.set("min_w", widget_min_w); - p_wgt.set("inc_w", widget_inc_w); - p_wgt.set("gap_x", widget_gap_x); - p_wgt.set("min_h", widget_min_h); - p_wgt.set("inc_h", widget_inc_h); - p_wgt.set("gap_y", widget_gap_y); - - Fl_Preferences p_lyt(prefs, "Layout"); - p_lyt.set("labelfont", labelfont); - p_lyt.set("labelsize", labelsize); - p_lyt.set("textfont", textfont); - p_lyt.set("textsize", textsize); -} - -/** - Read presets from a Preferences database. - */ -void Fd_Layout_Preset::read(Fl_Preferences &prefs) { - assert(this); - Fl_Preferences p_win(prefs, "Window"); - p_win.get("left_margin", left_window_margin, 15); - p_win.get("right_margin", right_window_margin, 15); - p_win.get("top_margin", top_window_margin, 15); - p_win.get("bottom_margin", bottom_window_margin, 15); - p_win.get("grid_x", window_grid_x, 0); - p_win.get("grid_y", window_grid_y, 0); - - Fl_Preferences p_grp(prefs, "Group"); - p_grp.get("left_margin", left_group_margin, 10); - p_grp.get("right_margin", right_group_margin, 10); - p_grp.get("top_margin", top_group_margin, 10); - p_grp.get("bottom_margin", bottom_group_margin, 10); - p_grp.get("grid_x", group_grid_x, 0); - p_grp.get("grid_y", group_grid_y, 0); - - Fl_Preferences p_tbs(prefs, "Tabs"); - p_tbs.get("top_margin", top_tabs_margin, 25); - p_tbs.get("bottom_margin", bottom_tabs_margin, 25); - - Fl_Preferences p_wgt(prefs, "Widget"); - p_wgt.get("min_w", widget_min_w, 20); - p_wgt.get("inc_w", widget_inc_w, 10); - p_wgt.get("gap_x", widget_gap_x, 4); - p_wgt.get("min_h", widget_min_h, 20); - p_wgt.get("inc_h", widget_inc_h, 4); - p_wgt.get("gap_y", widget_gap_y, 8); - - Fl_Preferences p_lyt(prefs, "Layout"); - p_lyt.get("labelfont", labelfont, 0); - p_lyt.get("labelsize", labelsize, 14); - p_lyt.get("textfont", textfont, 0); - p_lyt.get("textsize", textsize, 14); -} - -/** - Write presets to an .fl project file. - */ -void Fd_Layout_Preset::write(fld::io::Project_Writer *out) { - out->write_string(" preset { 1\n"); // preset format version - out->write_string(" %d %d %d %d %d %d\n", - left_window_margin, right_window_margin, - top_window_margin, bottom_window_margin, - window_grid_x, window_grid_y); - out->write_string(" %d %d %d %d %d %d\n", - left_group_margin, right_group_margin, - top_group_margin, bottom_group_margin, - group_grid_x, group_grid_y); - out->write_string(" %d %d\n", top_tabs_margin, bottom_tabs_margin); - out->write_string(" %d %d %d %d %d %d\n", - widget_min_w, widget_inc_w, widget_gap_x, - widget_min_h, widget_inc_h, widget_gap_y); - out->write_string(" %d %d %d %d\n", - labelfont, labelsize, textfont, textsize); - out->write_string(" }\n"); // preset format version -} - -/** - Read presets from an .fl project file. - */ -void Fd_Layout_Preset::read(fld::io::Project_Reader *in) { - const char *key; - key = in->read_word(1); - if (key && !strcmp(key, "{")) { - for (;;) { - key = in->read_word(); - if (!key) return; - if (key[0] == '}') break; - int ver = atoi(key); - if (ver == 0) { - continue; - } else if (ver == 1) { - left_window_margin = in->read_int(); - right_window_margin = in->read_int(); - top_window_margin = in->read_int(); - bottom_window_margin = in->read_int(); - window_grid_x = in->read_int(); - window_grid_y = in->read_int(); - - left_group_margin = in->read_int(); - right_group_margin = in->read_int(); - top_group_margin = in->read_int(); - bottom_group_margin = in->read_int(); - group_grid_x = in->read_int(); - group_grid_y = in->read_int(); - - top_tabs_margin = in->read_int(); - bottom_tabs_margin = in->read_int(); - - widget_min_w = in->read_int(); - widget_inc_w = in->read_int(); - widget_gap_x = in->read_int(); - widget_min_h = in->read_int(); - widget_inc_h = in->read_int(); - widget_gap_y = in->read_int(); - - labelfont = in->read_int(); - labelsize = in->read_int(); - textfont = in->read_int(); - textsize = in->read_int(); - } else { // skip unknown chunks - for (;;) { - key = in->read_word(1); - if (key && (key[0] == '}')) - return; - } - } - } - } else { - // format error - } -} - -/** - Return the preferred text size, but make sure it's not 0. - */ -int Fd_Layout_Preset::textsize_not_null() { - // try the user selected text size - if (textsize > 0) return textsize; - // if the user did not set one, try the label size - if (labelsize > 0) return labelsize; - // if that doesn;t work, fall back to the default value - return 14; -} - - -// ---- Fd_Layout_Suite ------------------------------------------------ MARK: - - -/** - Write a presets suite to a Preferences database. - */ -void Fd_Layout_Suite::write(Fl_Preferences &prefs) { - assert(this); - assert(name_); - prefs.set("name", name_); - for (int i = 0; i < 3; ++i) { - Fl_Preferences prefs_preset(prefs, Fl_Preferences::Name(i)); - assert(layout[i]); - layout[i]->write(prefs_preset); - } -} - -/** - Read a presets suite from a Preferences database. - */ -void Fd_Layout_Suite::read(Fl_Preferences &prefs) { - assert(this); - for (int i = 0; i < 3; ++i) { - Fl_Preferences prefs_preset(prefs, Fl_Preferences::Name(i)); - assert(layout[i]); - layout[i]->read(prefs_preset); - } -} - -/** - Write a presets suite to an .fl project file. - */ -void Fd_Layout_Suite::write(fld::io::Project_Writer *out) { - out->write_string(" suite {\n"); - out->write_string(" name "); out->write_word(name_); out->write_string("\n"); - for (int i = 0; i < 3; ++i) { - layout[i]->write(out); - } - out->write_string(" }\n"); -} - -/** - Read a presets suite from an .fl project file. - */ -void Fd_Layout_Suite::read(fld::io::Project_Reader *in) { - const char *key; - key = in->read_word(1); - if (key && !strcmp(key, "{")) { - int ix = 0; - for (;;) { - key = in->read_word(); - if (!key) return; - if (!strcmp(key, "name")) { - name(in->read_word()); - } else if (!strcmp(key, "preset")) { - if (ix >= 3) return; // file format error - layout[ix++]->read(in); - } else if (!strcmp(key, "}")) { - break; - } else { - in->read_word(); // unknown key, ignore, hopefully a key-value pair - } - } - } else { - // file format error - } -} - -/** - \brief Update the menu_label to show a symbol representing the storage location. - Also updates the FLUID user interface. - */ -void Fd_Layout_Suite::update_label() { - std::string sym; - switch (storage_) { - case FD_STORE_INTERNAL: sym.assign("@fd_beaker "); break; - case FD_STORE_USER: sym.assign("@fd_user "); break; - case FD_STORE_PROJECT: sym.assign("@fd_project "); break; - case FD_STORE_FILE: sym.assign("@fd_file "); break; - } - sym.append(name_); - if (menu_label) - ::free(menu_label); - menu_label = fl_strdup(sym.c_str()); - g_layout_list.update_menu_labels(); -} - -/** - \brief Update the Suite name and the Suite menu_label. - Also updates the FLUID user interface. - */ -void Fd_Layout_Suite::name(const char *n) { - if (name_) - ::free(name_); - if (n) - name_ = fl_strdup(n); - else - name_ = NULL; - update_label(); -} - -/** - Initialize the class for first use. - */ -void Fd_Layout_Suite::init() { - name_ = NULL; - menu_label = NULL; - layout[0] = layout[1] = layout[2] = NULL; - storage_ = FD_STORE_INTERNAL; -} - -/** - Free all allocated resources. - */ -Fd_Layout_Suite::~Fd_Layout_Suite() { - if (storage_ == FD_STORE_INTERNAL) return; - if (name_) ::free(name_); - for (int i = 0; i < 3; ++i) { - delete layout[i]; - } -} - -// ---- Fd_Layout_List ------------------------------------------------- MARK: - - -/** - Draw a little FLUID beaker symbol. - */ -static void fd_beaker(Fl_Color c) { - fl_color(221); - fl_begin_polygon(); - fl_vertex(-0.6, 0.2); - fl_vertex(-0.9, 0.8); - fl_vertex(-0.8, 0.9); - fl_vertex( 0.8, 0.9); - fl_vertex( 0.9, 0.8); - fl_vertex( 0.6, 0.2); - fl_end_polygon(); - fl_color(c); - fl_begin_line(); - fl_vertex(-0.3, -0.9); - fl_vertex(-0.2, -0.8); - fl_vertex(-0.2, -0.2); - fl_vertex(-0.9, 0.8); - fl_vertex(-0.8, 0.9); - fl_vertex( 0.8, 0.9); - fl_vertex( 0.9, 0.8); - fl_vertex( 0.2, -0.2); - fl_vertex( 0.2, -0.8); - fl_vertex( 0.3, -0.9); - fl_end_line(); -} - -/** - Draw a user silhouette symbol - */ -static void fd_user(Fl_Color c) { - fl_color(245); - fl_begin_complex_polygon(); - fl_arc( 0.1, 0.9, 0.8, 0.0, 80.0); - fl_arc( 0.0, -0.5, 0.4, -65.0, 245.0); - fl_arc(-0.1, 0.9, 0.8, 100.0, 180.0); - fl_end_complex_polygon(); - fl_color(c); - fl_begin_line(); - fl_arc( 0.1, 0.9, 0.8, 0.0, 80.0); - fl_arc( 0.0, -0.5, 0.4, -65.0, 245.0); - fl_arc(-0.1, 0.9, 0.8, 100.0, 180.0); - fl_end_line(); -} - -/** - Draw a document symbol. - */ -static void fd_project(Fl_Color c) { - Fl_Color fc = FL_LIGHT2; - fl_color(fc); - fl_begin_complex_polygon(); - fl_vertex(-0.7, -1.0); - fl_vertex(0.1, -1.0); - fl_vertex(0.1, -0.4); - fl_vertex(0.7, -0.4); - fl_vertex(0.7, 1.0); - fl_vertex(-0.7, 1.0); - fl_end_complex_polygon(); - - fl_color(fl_lighter(fc)); - fl_begin_polygon(); - fl_vertex(0.1, -1.0); - fl_vertex(0.1, -0.4); - fl_vertex(0.7, -0.4); - fl_end_polygon(); - - fl_color(fl_darker(c)); - fl_begin_loop(); - fl_vertex(-0.7, -1.0); - fl_vertex(0.1, -1.0); - fl_vertex(0.1, -0.4); - fl_vertex(0.7, -0.4); - fl_vertex(0.7, 1.0); - fl_vertex(-0.7, 1.0); - fl_end_loop(); - - fl_begin_line(); - fl_vertex(0.1, -1.0); - fl_vertex(0.7, -0.4); - fl_end_line(); -} - -/** - Draw a 3 1/2" floppy symbol. - */ -void fd_file(Fl_Color c) { - Fl_Color fl = FL_LIGHT2; - Fl_Color fc = FL_DARK3; - fl_color(fc); - fl_begin_polygon(); // case - fl_vertex(-0.9, -1.0); - fl_vertex(0.9, -1.0); - fl_vertex(1.0, -0.9); - fl_vertex(1.0, 0.9); - fl_vertex(0.9, 1.0); - fl_vertex(-0.9, 1.0); - fl_vertex(-1.0, 0.9); - fl_vertex(-1.0, -0.9); - fl_end_polygon(); - - fl_color(fl_lighter(fl)); - fl_begin_polygon(); - fl_vertex(-0.7, -1.0); // slider - fl_vertex(0.7, -1.0); - fl_vertex(0.7, -0.4); - fl_vertex(-0.7, -0.4); - fl_end_polygon(); - - fl_begin_polygon(); // label - fl_vertex(-0.7, 0.0); - fl_vertex(0.7, 0.0); - fl_vertex(0.7, 1.0); - fl_vertex(-0.7, 1.0); - fl_end_polygon(); - - fl_color(fc); - fl_begin_polygon(); - fl_vertex(-0.5, -0.9); // slot - fl_vertex(-0.3, -0.9); - fl_vertex(-0.3, -0.5); - fl_vertex(-0.5, -0.5); - fl_end_polygon(); - - fl_color(fl_darker(c)); - fl_begin_loop(); - fl_vertex(-0.9, -1.0); - fl_vertex(0.9, -1.0); - fl_vertex(1.0, -0.9); - fl_vertex(1.0, 0.9); - fl_vertex(0.9, 1.0); - fl_vertex(-0.9, 1.0); - fl_vertex(-1.0, 0.9); - fl_vertex(-1.0, -0.9); - fl_end_loop(); -} - -/** - Instantiate the class that holds a list of all layouts and manages the UI. - */ -Fd_Layout_List::Fd_Layout_List() -: main_menu_(main_layout_submenu_), - choice_menu_(static_choice_menu), - list_(static_suite_list), - list_size_(2), - list_capacity_(2), - list_is_static_(true), - current_suite_(0), - current_preset_(0) -{ - fl_add_symbol("fd_beaker", fd_beaker, 1); - fl_add_symbol("fd_user", fd_user, 1); - fl_add_symbol("fd_project", fd_project, 1); - fl_add_symbol("fd_file", fd_file, 1); -} - -/** - Release allocated resources. - */ -Fd_Layout_List::~Fd_Layout_List() { - assert(this); - if (!list_is_static_) { - ::free(main_menu_); - ::free(choice_menu_); - for (int i = 0; i < list_size_; i++) { - Fd_Layout_Suite &suite = list_[i]; - if (suite.storage_ != FD_STORE_INTERNAL) - suite.~Fd_Layout_Suite(); - } - ::free(list_); - } -} - -/** - Update the Setting dialog and menus to reflect the current Layout selection state. - */ -void Fd_Layout_List::update_dialogs() { - static Fl_Menu_Item *preset_menu = NULL; - if (!preset_menu) { - preset_menu = (Fl_Menu_Item*)main_menubar->find_item(select_layout_preset_cb); - assert(preset_menu); - } - assert(this); - assert(current_suite_ >= 0 ); - assert(current_suite_ < list_size_); - assert(current_preset_ >= 0 ); - assert(current_preset_ < 3); - layout = list_[current_suite_].layout[current_preset_]; - assert(layout); - if (w_settings_layout_tab) { - w_settings_layout_tab->do_callback(w_settings_layout_tab, LOAD); - layout_choice->redraw(); - } - preset_menu[current_preset_].setonly(preset_menu); - main_menu_[current_suite_].setonly(main_menu_); -} - -/** - Refresh the label pointers for both pulldown menus. - */ -void Fd_Layout_List::update_menu_labels() { - for (int i=0; iwrite_string("\nsnap {\n ver 1\n"); - out->write_string(" current_suite "); out->write_word(list_[current_suite()].name_); out->write_string("\n"); - out->write_string(" current_preset %d\n", current_preset()); - for (int i=0; iwrite_string("}"); -} - -/** - Read Suite and Layout selection and project layout data from an .fl project file. - */ -void Fd_Layout_List::read(fld::io::Project_Reader *in) { - const char *key; - key = in->read_word(1); - if (key && !strcmp(key, "{")) { - std::string cs; - int cp = 0; - for (;;) { - key = in->read_word(); - if (!key) return; - if (!strcmp(key, "ver")) { - in->read_int(); - } else if (!strcmp(key, "current_suite")) { - cs = in->read_word(); - } else if (!strcmp(key, "current_preset")) { - cp = in->read_int(); - } else if (!strcmp(key, "suite")) { - int n = add(in->filename_name()); - list_[n].read(in); - list_[n].storage(FD_STORE_PROJECT); - } else if (!strcmp(key, "}")) { - break; - } else { - in->read_word(); // unknown key, ignore, hopefully a key-value pair - } - } - current_suite(cs); - current_preset(cp); - update_dialogs(); - } else { - // old style "snap" is followed by an integer. Ignore. - } -} - -/** - Set the current Suite. - \param[in] ix index into list of suites - */ -void Fd_Layout_List::current_suite(int ix) { - assert(ix >= 0); - assert(ix < list_size_); - current_suite_ = ix; - layout = list_[current_suite_].layout[current_preset_]; -} - -/** - Set the current Suite. - \param[in] arg_name name of the selected suite - \return if no name is given or the name is not found, keep the current suite selected - */ -void Fd_Layout_List::current_suite(std::string arg_name) { - if (arg_name.empty()) return; - for (int i = 0; i < list_size_; ++i) { - Fd_Layout_Suite &suite = list_[i]; - if (suite.name_ && (strcmp(suite.name_, arg_name.c_str()) == 0)) { - current_suite(i); - break; - } - } -} - -/** - Select a Preset within the current Suite. - \param[in] ix 0 = application, 1 = dialog, 2 = toolbox - */ -void Fd_Layout_List::current_preset(int ix) { - assert(ix >= 0); - assert(ix < 3); - current_preset_ = ix; - layout = list_[current_suite_].layout[current_preset_]; -} - -/** - Allocate enough space for n entries in the list. - */ -void Fd_Layout_List::capacity(int n) { - static Fl_Menu_Item *suite_menu = NULL; - if (!suite_menu) - suite_menu = (Fl_Menu_Item*)main_menubar->find_item(layout_suite_marker); - - int old_n = list_size_; - int i; - - Fd_Layout_Suite *new_list = (Fd_Layout_Suite*)::calloc(n, sizeof(Fd_Layout_Suite)); - for (i = 0; i < old_n; i++) - new_list[i] = list_[i]; - if (!list_is_static_) ::free(list_); - list_ = new_list; - - Fl_Menu_Item *new_main_menu = (Fl_Menu_Item*)::calloc(n+1, sizeof(Fl_Menu_Item)); - for (i = 0; i < old_n; i++) - new_main_menu[i] = main_menu_[i]; - if (!list_is_static_) ::free(main_menu_); - main_menu_ = new_main_menu; - suite_menu->user_data(main_menu_); - - Fl_Menu_Item *new_choice_menu = (Fl_Menu_Item*)::calloc(n+1, sizeof(Fl_Menu_Item)); - for (i = 0; i < old_n; i++) - new_choice_menu[i] = choice_menu_[i]; - if (!list_is_static_) ::free(choice_menu_); - choice_menu_ = new_choice_menu; - if (layout_choice) layout_choice->menu(choice_menu_); - - list_capacity_ = n; - list_is_static_ = false; -} - -/** - \brief Clone the currently selected suite and append it to the list. - Selects the new layout and updates the UI. - */ -int Fd_Layout_List::add(const char *name) { - if (list_size_ == list_capacity_) { - capacity(list_capacity_ * 2); - } - int n = list_size_; - Fd_Layout_Suite &old_suite = list_[current_suite_]; - Fd_Layout_Suite &new_suite = list_[n]; - new_suite.init(); - new_suite.name(name); - for (int i=0; i<3; ++i) { - new_suite.layout[i] = new Fd_Layout_Preset; - ::memcpy(new_suite.layout[i], old_suite.layout[i], sizeof(Fd_Layout_Preset)); - } - Fd_Tool_Store new_storage = old_suite.storage_; - if (new_storage == FD_STORE_INTERNAL) - new_storage = FD_STORE_USER; - new_suite.storage(new_storage); - main_menu_[n].label(new_suite.menu_label); - main_menu_[n].callback(main_menu_[0].callback()); - main_menu_[n].argument(n); - main_menu_[n].flags = main_menu_[0].flags; - choice_menu_[n].label(new_suite.menu_label); - list_size_++; - current_suite(n); - return n; -} - -/** - Rename the current Suite. - */ -void Fd_Layout_List::rename(const char *name) { - int n = current_suite(); - list_[n].name(name); - main_menu_[n].label(list_[n].menu_label); - choice_menu_[n].label(list_[n].menu_label); -} - -/** - Remove the given suite. - \param[in] ix index into list of suites - */ -void Fd_Layout_List::remove(int ix) { - int tail = list_size_-ix-1; - if (tail) { - for (int i = ix; i < list_size_-1; i++) - list_[i] = list_[i+1]; - } - ::memmove(main_menu_+ix, main_menu_+ix+1, (tail+1) * sizeof(Fl_Menu_Item)); - ::memmove(choice_menu_+ix, choice_menu_+ix+1, (tail+1) * sizeof(Fl_Menu_Item)); - list_size_--; - if (current_suite() >= list_size_) - current_suite(list_size_ - 1); -} - -/** - Remove all Suites that use the given storage attribute. - \param[in] storage storage attribute, see FD_STORE_INTERNAL, etc. - */ -void Fd_Layout_List::remove_all(Fd_Tool_Store storage) { - for (int i=list_size_-1; i>=0; --i) { - if (list_[i].storage_ == storage) - remove(i); - } -} - -// ---- Helper --------------------------------------------------------- MARK: - - -static void draw_h_arrow(int, int, int); -static void draw_v_arrow(int x, int y1, int y2); -static void draw_left_brace(const Fl_Widget *w); -static void draw_right_brace(const Fl_Widget *w); -static void draw_top_brace(const Fl_Widget *w); -static void draw_bottom_brace(const Fl_Widget *w); -static void draw_grid(int x, int y, int dx, int dy); -void draw_width(int x, int y, int r, Fl_Align a); -void draw_height(int x, int y, int b, Fl_Align a); - -static int nearest(int x, int left, int grid, int right=0x7fff) { - int grid_x = ((x-left+grid/2)/grid)*grid+left; - if (grid_x < left+grid/2) return left; // left+grid/2; - if (grid_x > right-grid/2) return right; // right-grid/2; - return grid_x; -} - -static bool in_window(Fd_Snap_Data &d) { - return (d.wgt && d.wgt->parent == d.win); -} - -static bool in_group(Fd_Snap_Data &d) { - return (d.wgt && d.wgt->parent && d.wgt->parent->is_a(ID_Group) && d.wgt->parent != d.win); -} - -static bool in_tabs(Fd_Snap_Data &d) { - return (d.wgt && d.wgt->parent && d.wgt->parent->is_a(ID_Tabs)); -} - -static Fl_Group *parent(Fd_Snap_Data &d) { - return (d.wgt->o->parent()); -} - -// ---- Fd_Snap_Action ------------------------------------------------- MARK: - - -/** \class Fd_Snap_Action - - When a user drags one or more widgets, snap actions can be defined that provide - hints if a preferred widget position or size is nearby. The user's motion is - then directed towards the nearest preferred position, and the widget selection - snaps into place. - - FLUID provides a list of various snap actions. Every snap action uses the data - from the motion event and combines it with the sizes and positions of all other - widgets in the layout. - - Common snap actions include gaps and margins, but also alignments and - simple grid positions. - */ - -/** - \brief Check if a snap action has reached a preferred x position. - \param[inout] d current event data - \param[in] x_ref position of moving point - \param[in] x_snap position of target point - \return 1 if the points are not within range and won;t be considered - \return 0 if the point is as close as another in a previous action - \return -1 if this point is closer than any previous check, and this is the - new distance to beat. - */ -int Fd_Snap_Action::check_x_(Fd_Snap_Data &d, int x_ref, int x_snap) { - int dd = x_ref + d.dx - x_snap; - int d2 = abs(dd); - if (d2 > d.x_dist) return 1; - dx = d.dx_out = d.dx - dd; - ex = d.ex_out = x_snap; - if (d2 == d.x_dist) return 0; - d.x_dist = d2; - return -1; -} - -/** - \brief Check if a snap action has reached a preferred y position. - \see Fd_Snap_Action::check_x_(Fd_Snap_Data &d, int x_ref, int x_snap) - */ -int Fd_Snap_Action::check_y_(Fd_Snap_Data &d, int y_ref, int y_snap) { - int dd = y_ref + d.dy - y_snap; - int d2 = abs(dd); - if (d2 > d.y_dist) return 1; - dy = d.dy_out = d.dy - dd; - ey = d.ey_out = y_snap; - if (d2 == d.y_dist) return 0; - d.y_dist = d2; - return -1; -} - -/** - \brief Check if a snap action has reached a preferred x and y position. - \see Fd_Snap_Action::check_x_(Fd_Snap_Data &d, int x_ref, int x_snap) - */ -void Fd_Snap_Action::check_x_y_(Fd_Snap_Data &d, int x_ref, int x_snap, int y_ref, int y_snap) { - int ddx = x_ref + d.dx - x_snap; - int d2x = abs(ddx); - int ddy = y_ref + d.dy - y_snap; - int d2y = abs(ddy); - if ((d2x <= d.x_dist) && (d2y <= d.y_dist)) { - dx = d.dx_out = d.dx - ddx; - ex = d.ex_out = x_snap; - d.x_dist = d2x; - dy = d.dy_out = d.dy - ddy; - ey = d.ey_out = y_snap; - d.y_dist = d2y; - } -} - -/** - \brief Check if a snap action was applied to the current event. - This method is used to determine if a visual indicator for this snap action - should be drawn. - \param[inout] d current event data - */ -bool Fd_Snap_Action::matches(Fd_Snap_Data &d) { - switch (type) { - case 1: return (d.drag & mask) && (eex == ex) && (d.dx == dx); - case 2: return (d.drag & mask) && (eey == ey) && (d.dy == dy); - case 3: return (d.drag & mask) && (eex == ex) && (d.dx == dx) && (eey == ey) && (d.dy == dy); - } - return false; -} - -/** - \brief Run through all possible snap actions and store the winning coordinates in eex and eey. - \param[inout] d current event data - */ -void Fd_Snap_Action::check_all(Fd_Snap_Data &data) { - for (int i=0; list[i]; i++) { - if (list[i]->mask & data.drag) - list[i]->check(data); - } - eex = data.ex_out; - eey = data.ey_out; -} - -/** - \brief Draw a visual indicator for all snap actions that were applied during the last check. - Only one snap coordinate can win. FLUID chooses the one that is closest to - the current user event. If two or more snap actions suggest the same - coordinate, all of them will be drawn. - \param[inout] d current event data - */ -void Fd_Snap_Action::draw_all(Fd_Snap_Data &data) { - for (int i=0; list[i]; i++) { - if (list[i]->matches(data)) - list[i]->draw(data); - } -} - -/** Return a sensible step size for resizing a widget. */ -void Fd_Snap_Action::get_resize_stepsize(int &x_step, int &y_step) { - if ((layout->widget_inc_w > 1) && (layout->widget_inc_h > 1)) { - x_step = layout->widget_inc_w; - y_step = layout->widget_inc_h; - } else if ((layout->group_grid_x > 1) && (layout->group_grid_y > 1)) { - x_step = layout->group_grid_x; - y_step = layout->group_grid_y; - } else { - x_step = layout->window_grid_x; - y_step = layout->window_grid_y; - } -} - -/** Return a sensible step size for moving a widget. */ -void Fd_Snap_Action::get_move_stepsize(int &x_step, int &y_step) { - if ((layout->group_grid_x > 1) && (layout->group_grid_y > 1)) { - x_step = layout->group_grid_x; - y_step = layout->group_grid_y; - } else if ((layout->window_grid_x > 1) && (layout->window_grid_y > 1)) { - x_step = layout->window_grid_x; - y_step = layout->window_grid_y; - } else { - x_step = layout->widget_gap_x; - y_step = layout->widget_gap_y; - } -} - -/** Fix the given size to the same or next bigger snap position. */ -void Fd_Snap_Action::better_size(int &w, int &h) { - int x_min = 1, y_min = 1, x_inc = 1, y_inc = 1; - get_resize_stepsize(x_inc, y_inc); - if (x_inc < 1) x_inc = 1; - if (y_inc < 1) y_inc = 1; - if ((layout->widget_min_w > 1) && (layout->widget_min_h > 1)) { - x_min = layout->widget_min_w; - y_min = layout->widget_min_h; - } else if ((layout->group_grid_x > 1) && (layout->group_grid_y > 1)) { - x_min = layout->group_grid_x; - y_min = layout->group_grid_y; - } else { - x_min = x_inc; - y_min = y_inc; - } - int ww = fd_max(w - x_min, 0); w = (w - ww + x_inc - 1) / x_inc; w = w * x_inc; w = w + ww; - int hh = fd_max(h - y_min, 0); h = (h - hh + y_inc - 1) / y_inc; h = h * y_inc; h = h + hh; -} - - -// ---- snapping prototypes -------------------------------------------- MARK: - - -/** - Base class for all actions that drag the left side or the entire widget. - */ -class Fd_Snap_Left : public Fd_Snap_Action { -public: - Fd_Snap_Left() { type = 1; mask = FD_LEFT|FD_DRAG; } -}; - -/** - Base class for all actions that drag the right side or the entire widget. - */ -class Fd_Snap_Right : public Fd_Snap_Action { -public: - Fd_Snap_Right() { type = 1; mask = FD_RIGHT|FD_DRAG; } -}; - -/** - Base class for all actions that drag the top side or the entire widget. - */ -class Fd_Snap_Top : public Fd_Snap_Action { -public: - Fd_Snap_Top() { type = 2; mask = FD_TOP|FD_DRAG; } -}; - -/** - Base class for all actions that drag the bottom side or the entire widget. - */ -class Fd_Snap_Bottom : public Fd_Snap_Action { -public: - Fd_Snap_Bottom() { type = 2; mask = FD_BOTTOM|FD_DRAG; } -}; - -// ---- window snapping ------------------------------------------------ MARK: - - -/** - Check if the widget hits the left window edge. - */ -class Fd_Snap_Left_Window_Edge : public Fd_Snap_Left { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { clr(); check_x_(d, d.bx, 0); } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { draw_left_brace(d.win->o); }; -}; -Fd_Snap_Left_Window_Edge snap_left_window_edge; - -/** - Check if the widget hits the right window edge. - */ -class Fd_Snap_Right_Window_Edge : public Fd_Snap_Right { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { clr(); check_x_(d, d.br, d.win->o->w()); } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { draw_right_brace(d.win->o); }; -}; -Fd_Snap_Right_Window_Edge snap_right_window_edge; - -/** - Check if the widget hits the top window edge. - */ -class Fd_Snap_Top_Window_Edge : public Fd_Snap_Top { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { clr(); check_y_(d, d.by, 0); } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { draw_top_brace(d.win->o); }; -}; -Fd_Snap_Top_Window_Edge snap_top_window_edge; - -/** - Check if the widget hits the bottom window edge. - */ -class Fd_Snap_Bottom_Window_Edge : public Fd_Snap_Bottom { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { clr(); check_y_(d, d.bt, d.win->o->h()); } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { draw_bottom_brace(d.win->o); }; -}; -Fd_Snap_Bottom_Window_Edge snap_bottom_window_edge; - -/** - Check if the widget hits the left window edge plus a user defined margin. - */ -class Fd_Snap_Left_Window_Margin : public Fd_Snap_Left { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_window(d)) check_x_(d, d.bx, layout->left_window_margin); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_h_arrow(d.bx, (d.by+d.bt)/2, 0); - }; -}; -Fd_Snap_Left_Window_Margin snap_left_window_margin; - -class Fd_Snap_Right_Window_Margin : public Fd_Snap_Right { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_window(d)) check_x_(d, d.br, d.win->o->w()-layout->right_window_margin); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_h_arrow(d.br, (d.by+d.bt)/2, d.win->o->w()-1); - }; -}; -Fd_Snap_Right_Window_Margin snap_right_window_margin; - -class Fd_Snap_Top_Window_Margin : public Fd_Snap_Top { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_window(d)) check_y_(d, d.by, layout->top_window_margin); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_v_arrow((d.bx+d.br)/2, d.by, 0); - }; -}; -Fd_Snap_Top_Window_Margin snap_top_window_margin; - -class Fd_Snap_Bottom_Window_Margin : public Fd_Snap_Bottom { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_window(d)) check_y_(d, d.bt, d.win->o->h()-layout->bottom_window_margin); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_v_arrow((d.bx+d.br)/2, d.bt, d.win->o->h()-1); - }; -}; -Fd_Snap_Bottom_Window_Margin snap_bottom_window_margin; - -// ---- group snapping ------------------------------------------------- MARK: - - -/** - Check if the widget hits the left group edge. - */ -class Fd_Snap_Left_Group_Edge : public Fd_Snap_Left { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_group(d)) check_x_(d, d.bx, parent(d)->x()); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_left_brace(parent(d)); - }; -}; -Fd_Snap_Left_Group_Edge snap_left_group_edge; - -class Fd_Snap_Right_Group_Edge : public Fd_Snap_Right { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_group(d)) check_x_(d, d.br, parent(d)->x() + parent(d)->w()); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_right_brace(parent(d)); - }; -}; -Fd_Snap_Right_Group_Edge snap_right_group_edge; - -class Fd_Snap_Top_Group_Edge : public Fd_Snap_Top { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_group(d)) check_y_(d, d.by, parent(d)->y()); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_top_brace(parent(d)); - }; -}; -Fd_Snap_Top_Group_Edge snap_top_group_edge; - -class Fd_Snap_Bottom_Group_Edge : public Fd_Snap_Bottom { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_group(d)) check_y_(d, d.bt, parent(d)->y() + parent(d)->h()); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_bottom_brace(parent(d)); - }; -}; -Fd_Snap_Bottom_Group_Edge snap_bottom_group_edge; - - -/** - Check if the widget hits the left group edge plus a user defined margin. - */ -class Fd_Snap_Left_Group_Margin : public Fd_Snap_Left { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_group(d)) check_x_(d, d.bx, parent(d)->x() + layout->left_group_margin); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_left_brace(parent(d)); - draw_h_arrow(d.bx, (d.by+d.bt)/2, parent(d)->x()); - }; -}; -Fd_Snap_Left_Group_Margin snap_left_group_margin; - -class Fd_Snap_Right_Group_Margin : public Fd_Snap_Right { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_group(d)) check_x_(d, d.br, parent(d)->x()+parent(d)->w()-layout->right_group_margin); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_right_brace(parent(d)); - draw_h_arrow(d.br, (d.by+d.bt)/2, parent(d)->x()+parent(d)->w()-1); - }; -}; -Fd_Snap_Right_Group_Margin snap_right_group_margin; - -class Fd_Snap_Top_Group_Margin : public Fd_Snap_Top { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_group(d) && !in_tabs(d)) check_y_(d, d.by, parent(d)->y()+layout->top_group_margin); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_top_brace(parent(d)); - draw_v_arrow((d.bx+d.br)/2, d.by, parent(d)->y()); - }; -}; -Fd_Snap_Top_Group_Margin snap_top_group_margin; - -class Fd_Snap_Bottom_Group_Margin : public Fd_Snap_Bottom { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_group(d) && !in_tabs(d)) check_y_(d, d.bt, parent(d)->y()+parent(d)->h()-layout->bottom_group_margin); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_bottom_brace(parent(d)); - draw_v_arrow((d.bx+d.br)/2, d.bt, parent(d)->y()+parent(d)->h()-1); - }; -}; -Fd_Snap_Bottom_Group_Margin snap_bottom_group_margin; - -// ----- tabs snapping ------------------------------------------------- MARK: - - -/** - Check if the widget top hits the Fl_Tabs group top edge plus a user defined margin. - */ -class Fd_Snap_Top_Tabs_Margin : public Fd_Snap_Top_Group_Margin { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_tabs(d)) check_y_(d, d.by, parent(d)->y()+layout->top_tabs_margin); - } -}; -Fd_Snap_Top_Tabs_Margin snap_top_tabs_margin; - -class Fd_Snap_Bottom_Tabs_Margin : public Fd_Snap_Bottom_Group_Margin { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_tabs(d)) check_y_(d, d.bt, parent(d)->y()+parent(d)->h()-layout->bottom_tabs_margin); - } -}; -Fd_Snap_Bottom_Tabs_Margin snap_bottom_tabs_margin; - -// ----- grid snapping ------------------------------------------------- MARK: - - -/** - Base class for grid based snapping. - */ -class Fd_Snap_Grid : public Fd_Snap_Action { -protected: - int nearest_x, nearest_y; -public: - Fd_Snap_Grid() { type = 3; mask = FD_LEFT|FD_TOP|FD_DRAG; } - void check_grid(Fd_Snap_Data &d, int left, int grid_x, int right, int top, int grid_y, int bottom) { - if ((grid_x <= 1) || (grid_y <= 1)) return; - int suggested_x = d.bx + d.dx; - nearest_x = nearest(suggested_x, left, grid_x, right); - int suggested_y = d.by + d.dy; - nearest_y = nearest(suggested_y, top, grid_y, bottom); - if (d.drag == FD_LEFT) - check_x_(d, d.bx, nearest_x); - else if (d.drag == FD_TOP) - check_y_(d, d.by, nearest_y); - else - check_x_y_(d, d.bx, nearest_x, d.by, nearest_y); - } - bool matches(Fd_Snap_Data &d) FL_OVERRIDE { - if (d.drag == FD_LEFT) return (eex == ex); - if (d.drag == FD_TOP) return (eey == ey) && (d.dx == dx); - return (d.drag & mask) && (eex == ex) && (d.dx == dx) && (eey == ey) && (d.dy == dy); - } -}; - -/** - Check if the widget hits window grid coordinates. - */ -class Fd_Snap_Window_Grid : public Fd_Snap_Grid { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (in_window(d)) check_grid(d, layout->left_window_margin, layout->window_grid_x, d.win->o->w()-layout->right_window_margin, - layout->top_window_margin, layout->window_grid_y, d.win->o->h()-layout->bottom_window_margin); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_grid(nearest_x, nearest_y, layout->window_grid_x, layout->window_grid_y); - }; -}; -Fd_Snap_Window_Grid snap_window_grid; - -/** - Check if the widget hits group grid coordinates. - */ -class Fd_Snap_Group_Grid : public Fd_Snap_Grid { -public: - void check(Fd_Snap_Data &d) FL_OVERRIDE { - if (in_group(d)) { - clr(); - Fl_Widget *g = parent(d); - check_grid(d, g->x()+layout->left_group_margin, layout->group_grid_x, g->x()+g->w()-layout->right_group_margin, - g->y()+layout->top_group_margin, layout->group_grid_y, g->y()+g->h()-layout->bottom_group_margin); - } - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_grid(nearest_x, nearest_y, layout->group_grid_x, layout->group_grid_y); - }; -}; -Fd_Snap_Group_Grid snap_group_grid; - -// ----- sibling snapping ---------------------------------------------- MARK: - - -/** - Base class the check distance to other widgets in the same group. - */ -class Fd_Snap_Sibling : public Fd_Snap_Action { -protected: - Fl_Widget *best_match; -public: - Fd_Snap_Sibling() : best_match(NULL) { } - virtual int sibling_check(Fd_Snap_Data &d, Fl_Widget *s) = 0; - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - best_match = NULL; - if (!d.wgt) return; - if (!d.wgt->parent->is_a(ID_Group)) return; - int dsib_min = 1024; - Fl_Group_Type *gt = (Fl_Group_Type*)d.wgt->parent; - Fl_Group *g = (Fl_Group*)gt->o; - Fl_Widget *w = d.wgt->o; - for (int i=0; ichildren(); i++) { - Fl_Widget *c = g->child(i); - if (c == w) continue; - int sret = sibling_check(d, c); - if (sret < 1) { - int dsib; - if (type==1) - dsib = abs( ((d.by+d.bt)/2+d.dy) - (c->y()+c->h()/2) ); - else - dsib = abs( ((d.bx+d.br)/2+d.dx) - (c->x()+c->w()/2) ); - if (sret == -1 || (dsib < dsib_min)) { - dsib_min = dsib; - best_match = c; - } - } - } - } -}; - -/** - Check if widgets have the same x coordinate, so they can be vertically aligned. - */ -class Fd_Snap_Siblings_Left_Same : public Fd_Snap_Sibling { -public: - Fd_Snap_Siblings_Left_Same() { type = 1; mask = FD_LEFT|FD_DRAG; } - int sibling_check(Fd_Snap_Data &d, Fl_Widget *s) FL_OVERRIDE { - return check_x_(d, d.bx, s->x()); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - if (best_match) draw_left_brace(best_match); - }; -}; -Fd_Snap_Siblings_Left_Same snap_siblings_left_same; - -/** - Check if widgets touch left to right, or have a user selected gap left to right. - */ -class Fd_Snap_Siblings_Left : public Fd_Snap_Sibling { -public: - Fd_Snap_Siblings_Left() { type = 1; mask = FD_LEFT|FD_DRAG; } - int sibling_check(Fd_Snap_Data &d, Fl_Widget *s) FL_OVERRIDE { - return fd_min(check_x_(d, d.bx, s->x()+s->w()), - check_x_(d, d.bx, s->x()+s->w()+layout->widget_gap_x) ); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - if (best_match) draw_right_brace(best_match); - }; -}; -Fd_Snap_Siblings_Left snap_siblings_left; - -class Fd_Snap_Siblings_Right_Same : public Fd_Snap_Sibling { -public: - Fd_Snap_Siblings_Right_Same() { type = 1; mask = FD_RIGHT|FD_DRAG; } - int sibling_check(Fd_Snap_Data &d, Fl_Widget *s) FL_OVERRIDE { - return check_x_(d, d.br, s->x()+s->w()); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - if (best_match) draw_right_brace(best_match); - }; -}; -Fd_Snap_Siblings_Right_Same snap_siblings_right_same; - -class Fd_Snap_Siblings_Right : public Fd_Snap_Sibling { -public: - Fd_Snap_Siblings_Right() { type = 1; mask = FD_RIGHT|FD_DRAG; } - int sibling_check(Fd_Snap_Data &d, Fl_Widget *s) FL_OVERRIDE { - return fd_min(check_x_(d, d.br, s->x()), - check_x_(d, d.br, s->x()-layout->widget_gap_x)); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - if (best_match) draw_left_brace(best_match); - }; -}; -Fd_Snap_Siblings_Right snap_siblings_right; - -class Fd_Snap_Siblings_Top_Same : public Fd_Snap_Sibling { -public: - Fd_Snap_Siblings_Top_Same() { type = 2; mask = FD_TOP|FD_DRAG; } - int sibling_check(Fd_Snap_Data &d, Fl_Widget *s) FL_OVERRIDE { - return check_y_(d, d.by, s->y()); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - if (best_match) draw_top_brace(best_match); - }; -}; -Fd_Snap_Siblings_Top_Same snap_siblings_top_same; - -class Fd_Snap_Siblings_Top : public Fd_Snap_Sibling { -public: - Fd_Snap_Siblings_Top() { type = 2; mask = FD_TOP|FD_DRAG; } - int sibling_check(Fd_Snap_Data &d, Fl_Widget *s) FL_OVERRIDE { - return fd_min(check_y_(d, d.by, s->y()+s->h()), - check_y_(d, d.by, s->y()+s->h()+layout->widget_gap_y)); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - if (best_match) draw_bottom_brace(best_match); - }; -}; -Fd_Snap_Siblings_Top snap_siblings_top; - -class Fd_Snap_Siblings_Bottom_Same : public Fd_Snap_Sibling { -public: - Fd_Snap_Siblings_Bottom_Same() { type = 2; mask = FD_BOTTOM|FD_DRAG; } - int sibling_check(Fd_Snap_Data &d, Fl_Widget *s) FL_OVERRIDE { - return check_y_(d, d.bt, s->y()+s->h()); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - if (best_match) draw_bottom_brace(best_match); - }; -}; -Fd_Snap_Siblings_Bottom_Same snap_siblings_bottom_same; - -class Fd_Snap_Siblings_Bottom : public Fd_Snap_Sibling { -public: - Fd_Snap_Siblings_Bottom() { type = 2; mask = FD_BOTTOM|FD_DRAG; } - int sibling_check(Fd_Snap_Data &d, Fl_Widget *s) FL_OVERRIDE { - return fd_min(check_y_(d, d.bt, s->y()), - check_y_(d, d.bt, s->y()-layout->widget_gap_y)); - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - if (best_match) draw_top_brace(best_match); - }; -}; -Fd_Snap_Siblings_Bottom snap_siblings_bottom; - - -// ------ widget snapping ---------------------------------------------- MARK: - - -/** - Snap horizontal resizing to min_w or min_w and a multiple of inc_w. - */ -class Fd_Snap_Widget_Ideal_Width : public Fd_Snap_Action { -public: - Fd_Snap_Widget_Ideal_Width() { type = 1; mask = FD_LEFT|FD_RIGHT; } - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (!d.wgt) return; - int iw = 15, ih = 15; - d.wgt->ideal_size(iw, ih); - if (d.drag == FD_RIGHT) { - check_x_(d, d.br, d.bx+iw); - iw = layout->widget_min_w; - if (iw > 0) iw = nearest(d.br-d.bx+d.dx, layout->widget_min_w, layout->widget_inc_w); - check_x_(d, d.br, d.bx+iw); - } else { - check_x_(d, d.bx, d.br-iw); - iw = layout->widget_min_w; - if (iw > 0) iw = nearest(d.br-d.bx-d.dx, layout->widget_min_w, layout->widget_inc_w); - check_x_(d, d.bx, d.br-iw); - } - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_width(d.bx, d.bt+7, d.br, 0); - }; -}; -Fd_Snap_Widget_Ideal_Width snap_widget_ideal_width; - -class Fd_Snap_Widget_Ideal_Height : public Fd_Snap_Action { -public: - Fd_Snap_Widget_Ideal_Height() { type = 2; mask = FD_TOP|FD_BOTTOM; } - void check(Fd_Snap_Data &d) FL_OVERRIDE { - clr(); - if (!d.wgt) return; - int iw, ih; - d.wgt->ideal_size(iw, ih); - if (d.drag == FD_BOTTOM) { - check_y_(d, d.bt, d.by+ih); - ih = layout->widget_min_h; - if (ih > 0) ih = nearest(d.bt-d.by+d.dy, layout->widget_min_h, layout->widget_inc_h); - check_y_(d, d.bt, d.by+ih); - } else { - check_y_(d, d.by, d.bt-ih); - ih = layout->widget_min_h; - if (ih > 0) ih = nearest(d.bt-d.by-d.dy, layout->widget_min_h, layout->widget_inc_h); - check_y_(d, d.by, d.bt-ih); - } - } - void draw(Fd_Snap_Data &d) FL_OVERRIDE { - draw_height(d.br+7, d.by, d.bt, 0); - }; -}; -Fd_Snap_Widget_Ideal_Height snap_widget_ideal_height; - -// ---- snap actions list ---------------------------------------------- MARK: - - -/** - /brief The list of all snap actions available to FLUID. - New snap actions can be appended to the list. If multiple snap actions - with different coordinates, but the same snap distance are found, the last - action in the list wins. All snap actions with the same distance and same - winning coordinates are drawn in the overlay plane. - */ -Fd_Snap_Action *Fd_Snap_Action::list[] = { - &snap_left_window_edge, - &snap_right_window_edge, - &snap_top_window_edge, - &snap_bottom_window_edge, - - &snap_left_window_margin, - &snap_right_window_margin, - &snap_top_window_margin, - &snap_bottom_window_margin, - - &snap_window_grid, - &snap_group_grid, - - &snap_left_group_edge, - &snap_right_group_edge, - &snap_top_group_edge, - &snap_bottom_group_edge, - - &snap_left_group_margin, - &snap_right_group_margin, - &snap_top_group_margin, - &snap_bottom_group_margin, - - &snap_top_tabs_margin, - &snap_bottom_tabs_margin, - - &snap_siblings_left_same, &snap_siblings_left, - &snap_siblings_right_same, &snap_siblings_right, - &snap_siblings_top_same, &snap_siblings_top, - &snap_siblings_bottom_same, &snap_siblings_bottom, - - &snap_widget_ideal_width, - &snap_widget_ideal_height, - - NULL -}; - -// ---- draw alignment marks ------------------------------------------- MARK: - - -static void draw_v_arrow(int x, int y1, int y2) { - int dy = (y1>y2) ? -1 : 1 ; - fl_yxline(x, y1, y2); - fl_xyline(x-4, y2, x+4); - fl_line(x-2, y2-dy*5, x, y2-dy); - fl_line(x+2, y2-dy*5, x, y2-dy); -} - -static void draw_h_arrow(int x1, int y, int x2) { - int dx = (x1>x2) ? -1 : 1 ; - fl_xyline(x1, y, x2); - fl_yxline(x2, y-4, y+4); - fl_line(x2-dx*5, y-2, x2-dx, y); - fl_line(x2-dx*5, y+2, x2-dx, y); -} - -static void draw_top_brace(const Fl_Widget *w) { - int x = w->as_window() ? 0 : w->x(); - int y = w->as_window() ? 0 : w->y(); - fl_yxline(x, y-2, y+6); - fl_yxline(x+w->w()-1, y-2, y+6); - fl_xyline(x-2, y, x+w->w()+1); -} - -static void draw_left_brace(const Fl_Widget *w) { - int x = w->as_window() ? 0 : w->x(); - int y = w->as_window() ? 0 : w->y(); - fl_xyline(x-2, y, x+6); - fl_xyline(x-2, y+w->h()-1, x+6); - fl_yxline(x, y-2, y+w->h()+1); -} - -static void draw_right_brace(const Fl_Widget *w) { - int x = w->as_window() ? w->w() - 1 : w->x() + w->w() - 1; - int y = w->as_window() ? 0 : w->y(); - fl_xyline(x-6, y, x+2); - fl_xyline(x-6, y+w->h()-1, x+2); - fl_yxline(x, y-2, y+w->h()+1); -} - -static void draw_bottom_brace(const Fl_Widget *w) { - int x = w->as_window() ? 0 : w->x(); - int y = w->as_window() ? w->h() - 1 : w->y() + w->h() - 1; - fl_yxline(x, y-6, y+2); - fl_yxline(x+w->w()-1, y-6, y+2); - fl_xyline(x-2, y, x+w->w()+1); -} - -void draw_height(int x, int y, int b, Fl_Align a) { - char buf[16]; - int h = b - y; - sprintf(buf, "%d", h); - fl_font(FL_HELVETICA, 9); - int lw = (int)fl_width(buf); - int lx; - - b --; - if (h < 30) { - // Move height to the side... - if (a == FL_ALIGN_LEFT) lx = x - lw - 2; - else lx = x + 2; - fl_yxline(x, y, b); - } else { - // Put height inside the arrows... - if (a == FL_ALIGN_LEFT) lx = x - lw + 2; - else lx = x - lw / 2; - fl_yxline(x, y, y + (h - 11) / 2); - fl_yxline(x, y + (h + 11) / 2, b); - } - - // Draw the height... - fl_draw(buf, lx, y + (h + 7) / 2); - - // Draw the arrowheads... - fl_line(x-2, y+5, x, y+1, x+2, y+5); - fl_line(x-2, b-5, x, b-1, x+2, b-5); - - // Draw the end lines... - fl_xyline(x - 4, y, x + 4); - fl_xyline(x - 4, b, x + 4); -} - -void draw_width(int x, int y, int r, Fl_Align a) { - char buf[16]; - int w = r-x; - sprintf(buf, "%d", w); - fl_font(FL_HELVETICA, 9); - int lw = (int)fl_width(buf); - int ly = y + 4; - - r--; - - if (lw > (w - 20)) { - // Move width above/below the arrows... - if (a == FL_ALIGN_TOP) ly -= 10; - else ly += 10; - - fl_xyline(x, y, r); - } else { - // Put width inside the arrows... - fl_xyline(x, y, x + (w - lw - 2) / 2); - fl_xyline(x + (w + lw + 2) / 2, y, r); - } - - // Draw the width... - fl_draw(buf, x + (w - lw) / 2, ly-2); - - // Draw the arrowheads... - fl_line(x+5, y-2, x+1, y, x+5, y+2); - fl_line(r-5, y-2, r-1, y, r-5, y+2); - - // Draw the end lines... - fl_yxline(x, y - 4, y + 4); - fl_yxline(r, y - 4, y + 4); -} - -static void draw_grid(int x, int y, int dx, int dy) { - int dx2 = 1, dy2 = 1; - const int n = 2; - for (int i=-n; i<=n; i++) { - for (int j=-n; j<=n; j++) { - if (abs(i)+abs(j) < 4) { - int xx = x + i*dx , yy = y + j*dy; - fl_xyline(xx-dx2, yy, xx+dx2); - fl_yxline(xx, yy-dy2, yy+dy2); - } - } - } -} diff --git a/fluid/app/Fd_Snap_Action.h b/fluid/app/Fd_Snap_Action.h deleted file mode 100644 index 0c98b56b8..000000000 --- a/fluid/app/Fd_Snap_Action.h +++ /dev/null @@ -1,193 +0,0 @@ -// -// Snap action header file for the Fast Light Tool Kit (FLTK). -// -// Copyright 2023 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_FD_SNAP_ACTION_H -#define _FLUID_FD_SNAP_ACTION_H - -#include "app/fluid.h" -#include "nodes/Fl_Window_Type.h" - -#include - -struct Fl_Menu_Item; - -extern Fl_Menu_Item main_layout_submenu_[]; - -/** - \brief Collection of layout settings. - - Presets contain default fonts and font sizes for labels and text. They - can be used to guide widget positions using margins, grids, and gap sizes. - There are three Presets available in one Suite, marked "application", - "dialog", and "toolbox". - */ -class Fd_Layout_Preset { -public: - int left_window_margin; ///< gap between the window border and the widget - int right_window_margin; - int top_window_margin; - int bottom_window_margin; - int window_grid_x; ///< a regular grid across the window with its origin in the top left window corner - int window_grid_y; - - int left_group_margin; ///< gap between the border of a widget and its parent group - int right_group_margin; - int top_group_margin; - int bottom_group_margin; - int group_grid_x; ///< a regular grid across the group with its origin in the top left group corner - int group_grid_y; - - int top_tabs_margin; ///< preferred top edge tab size inside Fl_Tabs - int bottom_tabs_margin; ///< preferred bottom edge tab size inside Fl_Tabs - - int widget_min_w; ///< minimum widget width - int widget_inc_w; ///< widget width increments starting from widget_min_w - int widget_gap_x; ///< preferred horizontal gap between widgets - int widget_min_h; - int widget_inc_h; - int widget_gap_y; - - int labelfont; ///< preferred font for labels - int labelsize; ///< preferred size for labels - int textfont; ///< preferred font for text elements - int textsize; ///< preferred size for text elements - - void write(Fl_Preferences &prefs); - void read(Fl_Preferences &prefs); - void write(fld::io::Project_Writer*); - void read(fld::io::Project_Reader*); - - int textsize_not_null(); -}; - -extern Fd_Layout_Preset *layout; - -/** - \brief A collection of layout presets. - - A suite of layout presets is designed to cover various use cases when - designing UI layouts for applications. - There are three Presets available in one Suite, marked "application", - "dialog", and "toolbox". - */ -class Fd_Layout_Suite { -public: - char *name_; ///< name of the suite - char *menu_label; ///< label text used in pulldown menu - Fd_Layout_Preset *layout[3]; ///< presets for application, dialog, and toolbox windows - Fd_Tool_Store storage_; ///< storage location (see FD_STORE_INTERNAL, etc.) - void write(Fl_Preferences &prefs); - void read(Fl_Preferences &prefs); - void write(fld::io::Project_Writer*); - void read(fld::io::Project_Reader*); - void update_label(); - void storage(Fd_Tool_Store s) { storage_ = s; update_label(); } - void name(const char *n); - void init(); - ~Fd_Layout_Suite(); -public: - -}; - -/** - \brief Manage all layout suites that are available to the user. - - FLUID has two built-in suites. More suites can be cloned or added and stored - as a user preference, as part of an .fl project file, or in a separate file - for import/export and sharing. - */ -class Fd_Layout_List { -public: - Fl_Menu_Item *main_menu_; - Fl_Menu_Item *choice_menu_; - Fd_Layout_Suite *list_; - int list_size_; - int list_capacity_; - bool list_is_static_; - int current_suite_; - int current_preset_; - std::string filename_; -public: - Fd_Layout_List(); - ~Fd_Layout_List(); - void update_dialogs(); - void update_menu_labels(); - int current_suite() const { return current_suite_; } - void current_suite(int ix); - void current_suite(std::string); - int current_preset() const { return current_preset_; } - void current_preset(int ix); - Fd_Layout_Suite &operator[](int ix) { return list_[ix]; } - int add(const char *name); - void rename(const char *name); - void capacity(int); - - int load(const std::string &filename); - int save(const std::string &filename); - void write(Fl_Preferences &prefs, Fd_Tool_Store storage); - void read(Fl_Preferences &prefs, Fd_Tool_Store storage); - void write(fld::io::Project_Writer*); - void read(fld::io::Project_Reader*); - int add(Fd_Layout_Suite*); - void remove(int index); - void remove_all(Fd_Tool_Store storage); - Fd_Layout_Preset *at(int); - int size(); -}; - -extern Fd_Layout_List g_layout_list; - -/** - \brief Structure holding all the data to perform interactive alignment operations. - */ -typedef struct Fd_Snap_Data { - int dx, dy; ///< distance of the mouse from its initial PUSH event - int bx, by, br, bt; ///< bounding box of the original push event or current bounding box when drawing - int drag; ///< drag event mask - int x_dist, y_dist; ///< current closest snapping distance in x and y - int dx_out, dy_out; ///< current closest snapping point as a delta - Fl_Widget_Type *wgt; ///< first selected widget - Fl_Window_Type *win; ///< window that handles the drag action - int ex_out, ey_out; ///< chosen snap position -} Fd_Snap_Data; - -/** - \brief Find points of interest when moving the bounding box of all selected widgets. - */ -class Fd_Snap_Action { -protected: - int check_x_(Fd_Snap_Data &d, int x_ref, int x_snap); - int check_y_(Fd_Snap_Data &d, int y_ref, int y_snap); - void check_x_y_(Fd_Snap_Data &d, int x_ref, int x_snap, int y_ref, int y_snap); - void clr() { ex = dx = 0x7fff; } -public: - int ex, ey, dx, dy, type, mask; - Fd_Snap_Action() : ex(0x7fff), ey(0x7fff), dx(128), dy(128), type(0), mask(0) { } - virtual ~Fd_Snap_Action() { } - virtual void check(Fd_Snap_Data &d) = 0; - virtual void draw(Fd_Snap_Data &d) { } - virtual bool matches(Fd_Snap_Data &d); -public: - static int eex, eey; - static Fd_Snap_Action *list[]; - static void check_all(Fd_Snap_Data &d); - static void draw_all(Fd_Snap_Data &d); - static void get_resize_stepsize(int &x_step, int &y_step); - static void get_move_stepsize(int &x_step, int &y_step); - static void better_size(int &w, int &h); -}; - -#endif // _FLUID_FD_SNAP_ACTION_H diff --git a/fluid/app/Fluid_Image.cxx b/fluid/app/Fluid_Image.cxx deleted file mode 100644 index a79ac6c35..000000000 --- a/fluid/app/Fluid_Image.cxx +++ /dev/null @@ -1,413 +0,0 @@ -// -// Pixmap (and other images) label support for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2022 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 "app/Fluid_Image.h" - -#include "app/fluid.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "nodes/Fl_Group_Type.h" -#include "nodes/Fl_Window_Type.h" -#include "tools/filename.h" - -#include -#include -#include -#include -#include // fl_fopen() -#include -#include -#include -#include "../src/flstring.h" - -#include -#include -#include -#include - -void Fluid_Image::image(Fl_Widget *o) { - if (o->window() != o) o->image(img); -} - -void Fluid_Image::deimage(Fl_Widget *o) { - if (o->window() != o) o->deimage(img); -} - -/** Write the contents of the name() file as binary source code. - \param fmt short name of file contents for error message - \return 0 if the file could not be opened or read */ -size_t Fluid_Image::write_static_binary(fld::io::Code_Writer& f, const char* fmt) { - size_t nData = 0; - enter_project_dir(); - FILE *in = fl_fopen(name(), "rb"); - leave_project_dir(); - if (!in) { - write_file_error(f, fmt); - return 0; - } else { - fseek(in, 0, SEEK_END); - nData = ftell(in); - fseek(in, 0, SEEK_SET); - if (nData) { - char *data = (char*)calloc(nData, 1); - if (fread(data, nData, 1, in)==0) { /* ignore */ } - f.write_cdata(data, (int)nData); - free(data); - } - fclose(in); - } - return nData; -} - -/** Write the contents of the name() file as textual source code. - \param fmt short name of file contents for error message - \return 0 if the file could not be opened or read */ -size_t Fluid_Image::write_static_text(fld::io::Code_Writer& f, const char* fmt) { - size_t nData = 0; - enter_project_dir(); - FILE *in = fl_fopen(name(), "rb"); - leave_project_dir(); - if (!in) { - write_file_error(f, fmt); - return 0; - } else { - fseek(in, 0, SEEK_END); - nData = ftell(in); - fseek(in, 0, SEEK_SET); - if (nData) { - char *data = (char*)calloc(nData+1, 1); - if (fread(data, nData, 1, in)==0) { /* ignore */ } - f.write_cstring(data, (int)nData); - free(data); - } - fclose(in); - } - return nData; -} - -void Fluid_Image::write_static_rgb(fld::io::Code_Writer& f, const char* idata_name) { - // Write image data... - f.write_c("\n"); - f.write_c_once("#include \n"); - f.write_c("static const unsigned char %s[] =\n", idata_name); - const int extra_data = img->ld() ? (img->ld()-img->w()*img->d()) : 0; - f.write_cdata(img->data()[0], (img->w() * img->d() + extra_data) * img->h()); - f.write_c(";\n"); - write_initializer(f, "Fl_RGB_Image", "%s, %d, %d, %d, %d", idata_name, img->w(), img->h(), img->d(), img->ld()); -} - -/** - Write the static image data into the soutrce file. - - If \p compressed is set, write the original image format, which requires - linking the matching image reader at runtime, or if we want to store the raw - uncompressed pixels, which makes images fast, needs no reader, but takes a - lot of memory (current default for PNG) - - \param compressed write data in the original compressed file format - */ -void Fluid_Image::write_static(fld::io::Code_Writer& f, int compressed) { - if (!img) return; - const char *idata_name = f.unique_id(this, "idata", fl_filename_name(name()), 0); - function_name_ = f.unique_id(this, "image", fl_filename_name(name()), 0); - - if (is_animated_gif_) { - // Write animated gif image data... - f.write_c("\n"); - f.write_c_once("#include \n"); - f.write_c("static const unsigned char %s[] =\n", idata_name); - size_t nData = write_static_binary(f, "AnimGIF"); - f.write_c(";\n"); - write_initializer(f, "Fl_Anim_GIF_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData); - } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(name()), ".gif")==0) { - // Write gif image data... - f.write_c("\n"); - f.write_c_once("#include \n"); - f.write_c("static const unsigned char %s[] =\n", idata_name); - size_t nData = write_static_binary(f, "GIF"); - f.write_c(";\n"); - write_initializer(f, "Fl_GIF_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData); - } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(name()), ".bmp")==0) { - // Write bmp image data... - f.write_c("\n"); - f.write_c_once("#include \n"); - f.write_c("static const unsigned char %s[] =\n", idata_name); - size_t nData = write_static_binary(f, "BMP"); - f.write_c(";\n"); - write_initializer(f, "Fl_BMP_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData); - } else if (img->count() > 1) { - // Write Pixmap data... - f.write_c("\n"); - f.write_c_once("#include \n"); - f.write_c("static const char *%s[] = {\n", idata_name); - f.write_cstring(img->data()[0], (int)strlen(img->data()[0])); - - int i; - int ncolors, chars_per_color; - sscanf(img->data()[0], "%*d%*d%d%d", &ncolors, &chars_per_color); - - if (ncolors < 0) { - f.write_c(",\n"); - f.write_cstring(img->data()[1], ncolors * -4); - i = 2; - } else { - for (i = 1; i <= ncolors; i ++) { - f.write_c(",\n"); - f.write_cstring(img->data()[i], (int)strlen(img->data()[i])); - } - } - for (; i < img->count(); i ++) { - f.write_c(",\n"); - f.write_cstring(img->data()[i], img->w() * chars_per_color); - } - f.write_c("\n};\n"); - write_initializer(f, "Fl_Pixmap", "%s", idata_name); - } else if (img->d() == 0) { - // Write Bitmap data... - f.write_c("\n"); - f.write_c_once("#include \n"); - f.write_c("static const unsigned char %s[] =\n", idata_name); - f.write_cdata(img->data()[0], ((img->w() + 7) / 8) * img->h()); - f.write_c(";\n"); - write_initializer(f, "Fl_Bitmap", "%s, %d, %d, %d", idata_name, ((img->w() + 7) / 8) * img->h(), img->w(), img->h()); - } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(name()), ".jpg")==0) { - // Write jpeg image data... - f.write_c("\n"); - f.write_c_once("#include \n"); - f.write_c("static const unsigned char %s[] =\n", idata_name); - size_t nData = write_static_binary(f, "JPEG"); - f.write_c(";\n"); - write_initializer(f, "Fl_JPEG_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData); - } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(name()), ".png")==0) { - // Write png image data... - f.write_c("\n"); - f.write_c_once("#include \n"); - f.write_c("static const unsigned char %s[] =\n", idata_name); - size_t nData = write_static_binary(f, "PNG"); - f.write_c(";\n"); - write_initializer(f, "Fl_PNG_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData); - } -#ifdef FLTK_USE_SVG - else if (fl_ascii_strcasecmp(fl_filename_ext(name()), ".svg")==0 || fl_ascii_strcasecmp(fl_filename_ext(name()), ".svgz")==0) { - bool gzipped = (strcmp(fl_filename_ext(name()), ".svgz") == 0); - // Write svg image data... - if (compressed) { - f.write_c("\n"); - f.write_c_once("#include \n"); - if (gzipped) { - f.write_c("static const unsigned char %s[] =\n", idata_name); - size_t nData = write_static_binary(f, "SVGZ"); - f.write_c(";\n"); - write_initializer(f, "Fl_SVG_Image", "\"%s\", %s, %ld", fl_filename_name(name()), idata_name, nData); - } else { - f.write_c("static const char %s[] =\n", idata_name); - write_static_text(f, "SVG"); - f.write_c(";\n"); - write_initializer(f, "Fl_SVG_Image", "\"%s\", %s", fl_filename_name(name()), idata_name); - } - } else { - // if FLUID runs from the command line, make sure that the image is not - // only loaded but also rasterized, so we can write the RGB image data - Fl_RGB_Image* rgb_image = NULL; - Fl_SVG_Image* svg_image = NULL; - if (img->d()>0) - rgb_image = (Fl_RGB_Image*)img->image(); - if (rgb_image) - svg_image = rgb_image->as_svg_image(); - if (svg_image) { - svg_image->resize(svg_image->w(), svg_image->h()); - write_static_rgb(f, idata_name); - } else { - write_file_error(f, "RGB_from_SVG"); - } - } - } -#endif // FLTK_USE_SVG - else { - write_static_rgb(f, idata_name); - } -} - -void Fluid_Image::write_file_error(fld::io::Code_Writer& f, const char *fmt) { - f.write_c("#warning Cannot read %s file \"%s\": %s\n", fmt, name(), strerror(errno)); - enter_project_dir(); - f.write_c("// Searching in path \"%s\"\n", fl_getcwd(0, FL_PATH_MAX)); - leave_project_dir(); -} - -void Fluid_Image::write_initializer(fld::io::Code_Writer& f, const char *type_name, const char *format, ...) { - /* Outputs code that returns (and initializes if needed) an Fl_Image as follows: - static Fl_Image *'function_name_'() { - static Fl_Image *image = NULL; - if (!image) - image = new 'type_name'('product of format and remaining args'); - return image; - } */ - va_list ap; - va_start(ap, format); - f.write_c("static Fl_Image *%s() {\n", function_name_); - if (is_animated_gif_) - f.write_c("%sFl_GIF_Image::animate = true;\n", f.indent(1)); - f.write_c("%sstatic Fl_Image *image = NULL;\n", f.indent(1)); - f.write_c("%sif (!image)\n", f.indent(1)); - f.write_c("%simage = new %s(", f.indent(2), type_name); - f.vwrite_c(format, ap); - f.write_c(");\n"); - f.write_c("%sreturn image;\n", f.indent(1)); - f.write_c("}\n"); - va_end(ap); -} - -void Fluid_Image::write_code(fld::io::Code_Writer& f, int bind, const char *var, int inactive) { - /* Outputs code that attaches an image to an Fl_Widget or Fl_Menu_Item. - This code calls a function output before by Fluid_Image::write_initializer() */ - if (img) { - f.write_c("%s%s->%s%s( %s() );\n", f.indent(), var, bind ? "bind_" : "", inactive ? "deimage" : "image", function_name_); - if (is_animated_gif_) - f.write_c("%s((Fl_Anim_GIF_Image*)(%s()))->canvas(%s, Fl_Anim_GIF_Image::DONT_RESIZE_CANVAS);\n", f.indent(), function_name_, var); - } -} - -void Fluid_Image::write_inline(fld::io::Code_Writer& f, int inactive) { - if (img) - f.write_c("%s()", function_name_); -} - - -//////////////////////////////////////////////////////////////// - -static Fluid_Image** images = 0; // sorted list -static int numimages = 0; -static int tablesize = 0; - -Fluid_Image* Fluid_Image::find(const char *iname) { - if (!iname || !*iname) return 0; - - // first search to see if it exists already: - int a = 0; - int b = numimages; - while (a < b) { - int c = (a+b)/2; - int i = strcmp(iname,images[c]->name_); - if (i < 0) b = c; - else if (i > 0) a = c+1; - else return images[c]; - } - - // no, so now see if the file exists: - - enter_project_dir(); - FILE *f = fl_fopen(iname,"rb"); - if (!f) { - if (batch_mode) - fprintf(stderr, "Can't open image file:\n%s\n%s",iname,strerror(errno)); - else - fl_message("Can't open image file:\n%s\n%s",iname,strerror(errno)); - leave_project_dir(); - return 0; - } - fclose(f); - - Fluid_Image *ret = new Fluid_Image(iname); - - if (!ret->img || !ret->img->w() || !ret->img->h()) { - delete ret; - ret = 0; - if (batch_mode) - fprintf(stderr, "Can't read image file:\n%s\nunrecognized image format",iname); - else - fl_message("Can't read image file:\n%s\nunrecognized image format",iname); - } - leave_project_dir(); - if (!ret) return 0; - - // make a new entry in the table: - numimages++; - if (numimages > tablesize) { - tablesize = tablesize ? 2*tablesize : 16; - if (images) images = (Fluid_Image**)realloc(images, tablesize*sizeof(Fluid_Image*)); - else images = (Fluid_Image**)malloc(tablesize*sizeof(Fluid_Image*)); - } - for (b = numimages-1; b > a; b--) images[b] = images[b-1]; - images[a] = ret; - - return ret; -} - -Fluid_Image::Fluid_Image(const char *iname) - : is_animated_gif_(false) -{ - name_ = fl_strdup(iname); - written = 0; - refcount = 0; - img = Fl_Shared_Image::get(iname); - if (img && iname) { - const char *ext = fl_filename_ext(iname); - if (fl_ascii_strcasecmp(ext, ".gif")==0) { - int fc = Fl_Anim_GIF_Image::frame_count(iname); - if (fc > 0) is_animated_gif_ = true; - } - } - function_name_ = NULL; -} - -void Fluid_Image::increment() { - ++refcount; -} - -void Fluid_Image::decrement() { - --refcount; - if (refcount > 0) return; - delete this; -} - -Fluid_Image::~Fluid_Image() { - int a; - if (images) { - for (a = 0; arelease(); - free((void*)name_); -} - -//////////////////////////////////////////////////////////////// - -const char *ui_find_image_name; -Fluid_Image *ui_find_image(const char *oldname) { - enter_project_dir(); - fl_file_chooser_ok_label("Use Image"); - const char *name = fl_file_chooser("Image?", - "Image Files (*.{bm,bmp,gif,jpg,pbm,pgm,png,ppm,xbm,xpm,svg" -#ifdef HAVE_LIBZ - ",svgz" -#endif - "})", - oldname,1); - fl_file_chooser_ok_label(NULL); - ui_find_image_name = name; - Fluid_Image *ret = (name && *name) ? Fluid_Image::find(name) : 0; - leave_project_dir(); - return ret; -} diff --git a/fluid/app/Fluid_Image.h b/fluid/app/Fluid_Image.h deleted file mode 100644 index f095e83ce..000000000 --- a/fluid/app/Fluid_Image.h +++ /dev/null @@ -1,60 +0,0 @@ -// -// Image header file for the Fast Light Tool Kit (FLTK). -// -// This class stores the image labels for widgets in fluid. This is -// not a class in FLTK itself, and will produce different types of -// code depending on what the image type is. -// -// Copyright 1998-2010 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_IMAGE_H -#define FLUID_IMAGE_H - -#include "io/Code_Writer.h" - -#include - -class Fluid_Image { - bool is_animated_gif_; - const char *name_; - int refcount; - Fl_Shared_Image *img; - const char *function_name_; -protected: - Fluid_Image(const char *name); // no public constructor - ~Fluid_Image(); // no public destructor - size_t write_static_binary(fld::io::Code_Writer& f, const char* fmt); - size_t write_static_text(fld::io::Code_Writer& f, const char* fmt); - void write_static_rgb(fld::io::Code_Writer& f, const char* idata_name); -public: - int written; - static Fluid_Image* find(const char *); - void decrement(); // reference counting & automatic free - void increment(); - void image(Fl_Widget *); // set the image of this widget - void deimage(Fl_Widget *); // set the deimage of this widget - void write_static(fld::io::Code_Writer& f, int compressed); - void write_initializer(fld::io::Code_Writer& f, const char *type_name, const char *format, ...); - void write_code(fld::io::Code_Writer& f, int bind, const char *var, int inactive = 0); - void write_inline(fld::io::Code_Writer& f, int inactive = 0); - void write_file_error(fld::io::Code_Writer& f, const char *fmt); - const char *name() const {return name_;} -}; - -// pop up file chooser and return a legal image selected by user, -// or zero for any errors: -Fluid_Image *ui_find_image(const char *); -extern const char *ui_find_image_name; - -#endif diff --git a/fluid/app/Image_Asset.cxx b/fluid/app/Image_Asset.cxx new file mode 100644 index 000000000..a001f2291 --- /dev/null +++ b/fluid/app/Image_Asset.cxx @@ -0,0 +1,532 @@ +// +// Image Helper 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 "app/Image_Asset.h" + +#include "Fluid.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "nodes/Group_Node.h" +#include "nodes/Window_Node.h" +#include "tools/filename.h" + +#include +#include +#include +#include +#include // fl_fopen() +#include +#include +#include +#include "../src/flstring.h" + +#include +#include +#include +#include +#include +#include + + +/** + \brief A map of all image assets. + \todo This is a global variable, but should be associated + with a project instead. + */ +static std::map image_asset_map; + + +/** + \brief Write the contents of the image file as binary source code. + + Write the contents of the image file as C++ code, so the image is available in + the target app in the original binary format, for example: + ``` + { 1, 2, 3, ...} + ``` + \param f Write code to this C++ source code file + \param fmt short name of file contents for error message + \return 0 if the file could not be opened or read + */ +size_t Image_Asset::write_static_binary(fld::io::Code_Writer& f, const char* fmt) { + size_t nData = 0; + Fluid.proj.enter_project_dir(); + FILE *in = fl_fopen(filename(), "rb"); + Fluid.proj.leave_project_dir(); + if (!in) { + write_file_error(f, fmt); + return 0; + } else { + fseek(in, 0, SEEK_END); + nData = ftell(in); + fseek(in, 0, SEEK_SET); + if (nData) { + char *data = (char*)calloc(nData, 1); + if (fread(data, nData, 1, in)==0) { /* ignore */ } + f.write_cdata(data, (int)nData); + free(data); + } + fclose(in); + } + return nData; +} + + +/** + \brief Write the contents of the image file as text with escaped special characters. + + This function is only useful for writing out image formats that are ASCII text + based, like svg and pixmaps. Other formats should use write_static_binary(). + + \param f Write code to this C++ source code file + \param fmt short name of file contents for error message + \return 0 if the file could not be opened or read + */ +size_t Image_Asset::write_static_text(fld::io::Code_Writer& f, const char* fmt) { + size_t nData = 0; + Fluid.proj.enter_project_dir(); + FILE *in = fl_fopen(filename(), "rb"); + Fluid.proj.leave_project_dir(); + if (!in) { + write_file_error(f, fmt); + return 0; + } else { + fseek(in, 0, SEEK_END); + nData = ftell(in); + fseek(in, 0, SEEK_SET); + if (nData) { + char *data = (char*)calloc(nData+1, 1); + if (fread(data, nData, 1, in)==0) { /* ignore */ } + f.write_cstring(data, (int)nData); + free(data); + } + fclose(in); + } + return nData; +} + + +/** + \brief Write the contents of the image file as uncompressed RGB. + + Write source code that generates the uncompressed RGB image data at compile + time, and an initializer that creates the image at run time. + + \todo If the ld() value is not 0 and not d()*w(), the image data is not + written correctly. There is no check if the data is actually in RGB format. + + \param f Write code to this C++ source code file + \param fmt short name of file contents for error message + \return 0 if the file could not be opened or read + */ +void Image_Asset::write_static_rgb(fld::io::Code_Writer& f, const char* idata_name) { + // Write image data... + f.write_c("\n"); + f.write_c_once("#include \n"); + f.write_c("static const unsigned char %s[] =\n", idata_name); + const int extra_data = image_->ld() ? (image_->ld()-image_->w()*image_->d()) : 0; + f.write_cdata(image_->data()[0], (image_->w() * image_->d() + extra_data) * image_->h()); + f.write_c(";\n"); + write_initializer(f, "Fl_RGB_Image", "%s, %d, %d, %d, %d", idata_name, image_->w(), image_->h(), image_->d(), image_->ld()); +} + + +/** + \brief Write the static image data into the source file. + + Write source code that generates the image data at compile time, and an + initializer that creates the image at run time. + + If \p compressed is set, write the original image format, which requires + linking the matching image reader at runtime, or if we want to store the raw + uncompressed pixels, which makes images fast, needs no reader, but takes a + lot of memory (current default for PNG) + + \param f Write code to this C++ source code file + \param compressed write data in the original compressed file format + */ +void Image_Asset::write_static(fld::io::Code_Writer& f, int compressed) { + if (!image_) return; + const char *idata_name = f.unique_id(this, "idata", fl_filename_name(filename()), nullptr); + initializer_function_ = f.unique_id(this, "image", fl_filename_name(filename()), nullptr); + + if (is_animated_gif_) { + // Write animated gif image data... + f.write_c("\n"); + f.write_c_once("#include \n"); + f.write_c("static const unsigned char %s[] =\n", idata_name); + size_t nData = write_static_binary(f, "AnimGIF"); + f.write_c(";\n"); + write_initializer(f, "Fl_Anim_GIF_Image", "\"%s\", %s, %d", fl_filename_name(filename()), idata_name, nData); + } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(filename()), ".gif")==0) { + // Write gif image data... + f.write_c("\n"); + f.write_c_once("#include \n"); + f.write_c("static const unsigned char %s[] =\n", idata_name); + size_t nData = write_static_binary(f, "GIF"); + f.write_c(";\n"); + write_initializer(f, "Fl_GIF_Image", "\"%s\", %s, %d", fl_filename_name(filename()), idata_name, nData); + } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(filename()), ".bmp")==0) { + // Write bmp image data... + f.write_c("\n"); + f.write_c_once("#include \n"); + f.write_c("static const unsigned char %s[] =\n", idata_name); + size_t nData = write_static_binary(f, "BMP"); + f.write_c(";\n"); + write_initializer(f, "Fl_BMP_Image", "\"%s\", %s, %d", fl_filename_name(filename()), idata_name, nData); + } else if (image_->count() > 1) { + // Write Pixmap data... + f.write_c("\n"); + f.write_c_once("#include \n"); + f.write_c("static const char *%s[] = {\n", idata_name); + f.write_cstring(image_->data()[0], (int)strlen(image_->data()[0])); + + int i; + int ncolors, chars_per_color; + sscanf(image_->data()[0], "%*d%*d%d%d", &ncolors, &chars_per_color); + + if (ncolors < 0) { + f.write_c(",\n"); + f.write_cstring(image_->data()[1], ncolors * -4); + i = 2; + } else { + for (i = 1; i <= ncolors; i ++) { + f.write_c(",\n"); + f.write_cstring(image_->data()[i], (int)strlen(image_->data()[i])); + } + } + for (; i < image_->count(); i ++) { + f.write_c(",\n"); + f.write_cstring(image_->data()[i], image_->w() * chars_per_color); + } + f.write_c("\n};\n"); + write_initializer(f, "Fl_Pixmap", "%s", idata_name); + } else if (image_->d() == 0) { + // Write Bitmap data... + f.write_c("\n"); + f.write_c_once("#include \n"); + f.write_c("static const unsigned char %s[] =\n", idata_name); + f.write_cdata(image_->data()[0], ((image_->w() + 7) / 8) * image_->h()); + f.write_c(";\n"); + write_initializer(f, "Fl_Bitmap", "%s, %d, %d, %d", idata_name, ((image_->w() + 7) / 8) * image_->h(), image_->w(), image_->h()); + } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(filename()), ".jpg")==0) { + // Write jpeg image data... + f.write_c("\n"); + f.write_c_once("#include \n"); + f.write_c("static const unsigned char %s[] =\n", idata_name); + size_t nData = write_static_binary(f, "JPEG"); + f.write_c(";\n"); + write_initializer(f, "Fl_JPEG_Image", "\"%s\", %s, %d", fl_filename_name(filename()), idata_name, nData); + } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(filename()), ".png")==0) { + // Write png image data... + f.write_c("\n"); + f.write_c_once("#include \n"); + f.write_c("static const unsigned char %s[] =\n", idata_name); + size_t nData = write_static_binary(f, "PNG"); + f.write_c(";\n"); + write_initializer(f, "Fl_PNG_Image", "\"%s\", %s, %d", fl_filename_name(filename()), idata_name, nData); + } +#ifdef FLTK_USE_SVG + else if (fl_ascii_strcasecmp(fl_filename_ext(filename()), ".svg")==0 || fl_ascii_strcasecmp(fl_filename_ext(filename()), ".svgz")==0) { + bool gzipped = (strcmp(fl_filename_ext(filename()), ".svgz") == 0); + // Write svg image data... + if (compressed) { + f.write_c("\n"); + f.write_c_once("#include \n"); + if (gzipped) { + f.write_c("static const unsigned char %s[] =\n", idata_name); + size_t nData = write_static_binary(f, "SVGZ"); + f.write_c(";\n"); + write_initializer(f, "Fl_SVG_Image", "\"%s\", %s, %ld", fl_filename_name(filename()), idata_name, nData); + } else { + f.write_c("static const char %s[] =\n", idata_name); + write_static_text(f, "SVG"); + f.write_c(";\n"); + write_initializer(f, "Fl_SVG_Image", "\"%s\", %s", fl_filename_name(filename()), idata_name); + } + } else { + // if FLUID runs from the command line, make sure that the image is not + // only loaded but also rasterized, so we can write the RGB image data + Fl_RGB_Image* rgb_image = nullptr; + Fl_SVG_Image* svg_image = nullptr; + if (image_->d()>0) + rgb_image = (Fl_RGB_Image*)image_->image(); + if (rgb_image) + svg_image = rgb_image->as_svg_image(); + if (svg_image) { + svg_image->resize(svg_image->w(), svg_image->h()); + write_static_rgb(f, idata_name); + } else { + write_file_error(f, "RGB_from_SVG"); + } + } + } +#endif // FLTK_USE_SVG + else { + write_static_rgb(f, idata_name); + } +} + + +/** + \brief Write a warning message to the generated code file that the image asset + can't be read. + + This writes a #warning directive to the generated code file which contains the + filename of the image asset and the error message as returned by strerror(errno). + The current working directory is also printed for debugging purposes. + \param f The C++ source code file to write the warning message to. + \param fmt The format string for the image file type. + */ +void Image_Asset::write_file_error(fld::io::Code_Writer& f, const char *fmt) { + f.write_c("#warning Cannot read %s file \"%s\": %s\n", fmt, filename(), strerror(errno)); + Fluid.proj.enter_project_dir(); + f.write_c("// Searching in path \"%s\"\n", fl_getcwd(nullptr, FL_PATH_MAX)); + Fluid.proj.leave_project_dir(); +} + + +/** + \brief Outputs code that loads and returns an Fl_Image. + + The generated code loads the image if it hasn't been loaded yet, and then + returns a pointer to the image. + + \code + static Fl_Image *'initializer_function_'() { + static Fl_Image *image = 0L; + if (!image) + image = new 'type_name'('product of format and remaining args'); + return image; + } + \endcode + + \param f Write the C++ code to this file. + \param image_class Name of the Fl_Image class, for example Fl_GIF_Image. + \param format Format string for additional parameters for the constructor. + */ +void Image_Asset::write_initializer(fld::io::Code_Writer& f, const char *image_class, const char *format, ...) { + va_list ap; + va_start(ap, format); + f.write_c("static Fl_Image *%s() {\n", initializer_function_.c_str()); + if (is_animated_gif_) + f.write_c("%sFl_GIF_Image::animate = true;\n", f.indent(1)); + f.write_c("%sstatic Fl_Image *image = 0L;\n", f.indent(1)); + f.write_c("%sif (!image)\n", f.indent(1)); + f.write_c("%simage = new %s(", f.indent(2), image_class); + f.vwrite_c(format, ap); + f.write_c(");\n"); + f.write_c("%sreturn image;\n", f.indent(1)); + f.write_c("}\n"); + va_end(ap); +} + + +/** + \brief Outputs code that attaches an image to an Fl_Widget or Fl_Menu_Item. + + The generated code will call the image initializer function and assign + the resulting image to the widget. + + \param f Write the C++ code to this file. + \param bind If true, use bind_image() instead of image(). + \param var Name of the Fl_Widget or Fl_Menu_Item to attach the image to. + \param inactive If true, use deimage() instead of image(). + */ +void Image_Asset::write_code(fld::io::Code_Writer& f, int bind, const char *var, int inactive) { + if (image_) { + f.write_c("%s%s->%s%s( %s() );\n", + f.indent(), + var, + bind ? "bind_" : "", + inactive ? "deimage" : "image", + initializer_function_.c_str()); + if (is_animated_gif_) + f.write_c("%s((Fl_Anim_GIF_Image*)(%s()))->canvas(%s, Fl_Anim_GIF_Image::DONT_RESIZE_CANVAS);\n", + f.indent(), + initializer_function_.c_str(), + var); + } +} + + +/** + \brief Outputs code that calls the image initializer function. + + The generated code calls the image initializer function, loading an image and + assigning it to a Fl_Menu_Item. + + \param f Write the C++ code to this file. + \param inactive Unused. + */ +void Image_Asset::write_inline(fld::io::Code_Writer& f, int inactive) { + (void)inactive; + if (image_) { + f.write_c("%s()", initializer_function_.c_str()); + } +} + + +/** + \brief Finds an image asset by filename. + + If the image asset has already been loaded, it is returned from the cache. + If the image asset has not been loaded, it is loaded from the file system. + If the image asset cannot be loaded, nullptr is returned. + + \param iname The filename of the image asset to find. + \returns The image asset, or nullptr if it cannot be loaded. + */ +Image_Asset* Image_Asset::find(const char *iname) { + if (!iname || !*iname) return nullptr; + + // First search to see if it exists already. If it does, return it. + auto result = image_asset_map.find(iname); + if (result != image_asset_map.end()) + return result->second; + + // Check if a file by that name exists. + Fluid.proj.enter_project_dir(); + FILE *f = fl_fopen(iname,"rb"); + if (!f) { + if (Fluid.batch_mode) + fprintf(stderr, "Can't open image file:\n%s\n%s",iname,strerror(errno)); + else + fl_message("Can't open image file:\n%s\n%s",iname,strerror(errno)); + Fluid.proj.leave_project_dir(); + return nullptr; + } + fclose(f); + + // We found the file. Create the asset. + Image_Asset *asset = new Image_Asset(iname); + if (!asset->image_ || !asset->image_->w() || !asset->image_->h()) { + delete asset; + if (Fluid.batch_mode) + fprintf(stderr, "Can't read image file:\n%s\nunrecognized image format",iname); + else + fl_message("Can't read image file:\n%s\nunrecognized image format",iname); + Fluid.proj.leave_project_dir(); + return nullptr; + } + + // Add the new asset to our image asset map and return it to the caller. + image_asset_map[iname] = asset; + return asset; +} + +/** + \brief Construct an image asset from a file in the project directory. + + This constructor creates an image asset from a file in the project + directory. The image file is loaded and stored in the map of image + assets. The image asset is given a reference count of 1, which means + that it will be destroyed when all references to it have been released. + The constructor also sets the flag to indicate if the image is an + animated GIF. This information is used when generating code for the + image asset. + + \param iname The name of the image file in the project directory. +*/ +Image_Asset::Image_Asset(const char *iname) +{ + filename_ = iname; + image_ = Fl_Shared_Image::get(iname); + if (image_ && iname) { + const char *ext = fl_filename_ext(iname); + if (fl_ascii_strcasecmp(ext, ".gif")==0) { + int fc = Fl_Anim_GIF_Image::frame_count(iname); + if (fc > 0) is_animated_gif_ = true; + } + } +} + +/** + \brief Increments the reference count of the image asset. + + This method increments the reference count of the image asset. The + reference count is used to keep track of how many times the image asset + is referenced in the user interface. When the reference count reaches zero, + the image asset is destroyed. +*/ +void Image_Asset::inc_ref() { + ++refcount_; +} + +/** + \brief Decrements the reference count of the image asset. + + This method decrements the reference count of the image asset. The + reference count is used to keep track of how many times the image asset + is referenced in the user interface. If the reference count reaches zero, + the image asset is destroyed. +*/ +void Image_Asset::dec_ref() { + --refcount_; + if (refcount_ > 0) return; + delete this; +} + +/** + \brief Destructor for the Image_Asset class. + + This destructor removes the image asset from the global image asset map + and releases the associated shared image if it exists. It ensures that + any resources associated with the Image_Asset are properly cleaned up + when the object is destroyed. +*/ +Image_Asset::~Image_Asset() { + image_asset_map.erase(filename_); + if (image_) image_->release(); +} + +//////////////////////////////////////////////////////////////// + +/** + \brief Displays a file chooser for the user to select an image file. + + This function displays a file chooser dialog with the title "Image?" and + the current working directory set to the project directory. The file + chooser displays files with the extensions .bm, .bmp, .gif, .jpg, .pbm, + .pgm, .png, .ppm, .xbm, .xpm, and .svg (and .svgz if zlib support is + enabled). The function returns a pointer to an Image_Asset object that + references the selected image. If the user cancels the file chooser or + selects a file that does not exist, the function returns nullptr. + + \param oldname The default filename to display in the file chooser. + + \return A pointer to an Image_Asset object that references the selected + image, or nullptr if the user cancels the file chooser or selects a file + that does not exist. The asset is automaticly added to the global image + asset map. +*/ +Image_Asset *ui_find_image(const char *oldname) { + Fluid.proj.enter_project_dir(); + fl_file_chooser_ok_label("Use Image"); + const char *name = fl_file_chooser("Image?", + "Image Files (*.{bm,bmp,gif,jpg,pbm,pgm,png,ppm,xbm,xpm,svg" +#ifdef HAVE_LIBZ + ",svgz" +#endif + "})", + oldname,1); + fl_file_chooser_ok_label(nullptr); + Image_Asset *ret = (name && *name) ? Image_Asset::find(name) : nullptr; + Fluid.proj.leave_project_dir(); + return ret; +} + diff --git a/fluid/app/Image_Asset.h b/fluid/app/Image_Asset.h new file mode 100644 index 000000000..f5376beaa --- /dev/null +++ b/fluid/app/Image_Asset.h @@ -0,0 +1,63 @@ +// +// Image Helper header file 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 +// + +// This class stores the image labels for widgets in fluid. This is +// not a class in FLTK itself, and will produce different types of +// code depending on what the image type is. + + +#ifndef APP_IMAGE_ASSET_H +#define APP_IMAGE_ASSET_H + +#include "io/Code_Writer.h" + +#include + +class Image_Asset { + +private: // member variables + bool is_animated_gif_ = false; ///< It's an animated gif. + std::string filename_ { }; ///< Relative path to the image file + int refcount_ = 0; ///< Reference count + Fl_Shared_Image *image_ = nullptr; ///< The actual image as managed by FLTK + std::string initializer_function_ { }; ///< The name of the initializer function + +private: // methods + Image_Asset(const char *name); // no public constructor + ~Image_Asset(); // no public destructor + size_t write_static_binary(fld::io::Code_Writer& f, const char* fmt); + size_t write_static_text(fld::io::Code_Writer& f, const char* fmt); + void write_static_rgb(fld::io::Code_Writer& f, const char* idata_name); + +public: // methods + static Image_Asset* find(const char *); + void dec_ref(); // reference counting & automatic free + void inc_ref(); + Fl_Shared_Image *image() const { return image_; } + void write_static(fld::io::Code_Writer& f, int compressed); + void write_initializer(fld::io::Code_Writer& f, const char *type_name, const char *format, ...); + void write_code(fld::io::Code_Writer& f, int bind, const char *var, int inactive = 0); + void write_inline(fld::io::Code_Writer& f, int inactive = 0); + void write_file_error(fld::io::Code_Writer& f, const char *fmt); + const char *filename() const { return filename_.c_str(); } +}; + +// pop up file chooser and return a legal image selected by user, +// or zero for any errors: +Image_Asset *ui_find_image(const char *); +extern const char *ui_find_image_name; + +#endif // APP_IMAGE_ASSET_H diff --git a/fluid/app/Menu.cxx b/fluid/app/Menu.cxx new file mode 100644 index 000000000..044cc29a7 --- /dev/null +++ b/fluid/app/Menu.cxx @@ -0,0 +1,190 @@ +// +// Application Main Menu 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 "app/Menu.h" + +#include "Fluid.h" + +#include "proj/undo.h" +#include "app/templates.h" +#include "nodes/Node.h" +#include "nodes/Group_Node.h" +#include "nodes/Window_Node.h" +#include "nodes/factory.h" +#include "panels/codeview_panel.h" +#include "app/shell_command.h" + +#include + +// In Snap_Action.h +extern void layout_suite_marker(Fl_Widget *, void *user_data); +extern void select_layout_preset_cb(Fl_Widget *, void *user_data); +extern Fl_Menu_Item main_layout_submenu_[]; + +using namespace fld; + + +void write_cb(Fl_Widget *, void *) { + Fluid.write_code_files(); +} +void openwidget_cb(Fl_Widget *, void *) { Fluid.edit_selected(); } +void copy_cb(Fl_Widget*, void*) { Fluid.copy_selected(); } +void cut_cb(Fl_Widget *, void *) { Fluid.cut_selected(); } +void delete_cb(Fl_Widget *, void *) { Fluid.delete_selected(); } +void paste_cb(Fl_Widget*, void*) { Fluid.paste_from_clipboard(); } +void duplicate_cb(Fl_Widget*, void*) { Fluid.duplicate_selected(); } +static void sort_cb(Fl_Widget *,void *) { Fluid.sort_selected(); } +void about_cb(Fl_Widget *, void *) { Fluid.about(); } +void help_cb(Fl_Widget *, void *) { + Fluid.show_help("fluid.html"); +} +static void save_template_cb(Fl_Widget *, void *) { fld::app::save_template(); } + +void manual_cb(Fl_Widget *, void *) { + Fluid.show_help("index.html"); +} + +static void menu_file_new_cb(Fl_Widget *, void *) { Fluid.new_project(); } +static void menu_file_new_from_template_cb(Fl_Widget *, void *) { Fluid.new_project_from_template(); } +static void menu_file_open_cb(Fl_Widget *, void *) { Fluid.open_project_file(""); } +static void menu_file_insert_cb(Fl_Widget *, void *) { Fluid.merge_project_file(""); } +void menu_file_save_cb(Fl_Widget *, void *arg) { Fluid.save_project_file(arg); } +static void menu_file_print_cb(Fl_Widget *, void *arg) { Fluid.print_snapshots(); } +void menu_file_open_history_cb(Fl_Widget *, void *v) { Fluid.open_project_file(std::string((const char*)v)); } +static void menu_layout_sync_resize_cb(Fl_Menu_ *m, void*) { + if (m->mvalue()->value()) Fluid.proj.tree.allow_layout = 1; else Fluid.proj.tree.allow_layout = 0; +} +static void menu_file_revert_cb(Fl_Widget *, void *) { Fluid.revert_project(); } +void exit_cb(Fl_Widget *,void *) { Fluid.quit(); } +static void write_strings_cb(Fl_Widget *, void *) { Fluid.proj.write_strings(); } +void toggle_widgetbin_cb(Fl_Widget *, void *) { Fluid.toggle_widget_bin(); } +/** + This is the main Fluid menu. + + Design history is manipulated right inside this menu structure. + Some menu items change or deactivate correctly, but most items just trigger + various callbacks. + + \c New_Menu creates new widgets and is explained in detail in another location. + + \see New_Menu + \todo This menu needs some major modernization. Menus are too long and their + sorting is not always obvious. + \todo Shortcuts are all over the place (Alt, Ctrl, Command, Shift-Ctrl, + function keys), and there should be a help page listing all shortcuts. + */ +Fl_Menu_Item Application::main_menu[] = { + {"&File",0,nullptr,nullptr,FL_SUBMENU}, + {"&New", FL_COMMAND+'n', menu_file_new_cb}, + {"&Open...", FL_COMMAND+'o', menu_file_open_cb}, + {"&Insert...", FL_COMMAND+'i', menu_file_insert_cb, nullptr, FL_MENU_DIVIDER}, + {"&Save", FL_COMMAND+'s', menu_file_save_cb, nullptr}, + {"Save &As...", FL_COMMAND+FL_SHIFT+'s', menu_file_save_cb, (void*)1}, + {"Sa&ve A Copy...", 0, menu_file_save_cb, (void*)2}, + {"&Revert...", 0, menu_file_revert_cb, nullptr, FL_MENU_DIVIDER}, + {"New &From Template...", FL_COMMAND+'N', menu_file_new_from_template_cb, nullptr}, + {"Save As &Template...", 0, save_template_cb, nullptr, FL_MENU_DIVIDER}, + {"&Print...", FL_COMMAND+'p', menu_file_print_cb}, + {"Write &Code", FL_COMMAND+FL_SHIFT+'c', write_cb, nullptr}, +// Matt: disabled {"MergeBack Code", FL_COMMAND+FL_SHIFT+'m', mergeback_cb, 0}, + {"&Write Strings", FL_COMMAND+FL_SHIFT+'w', write_strings_cb, nullptr, FL_MENU_DIVIDER}, + {Fluid.history.relpath[0], FL_COMMAND+'1', menu_file_open_history_cb, Fluid.history.abspath[0]}, + {Fluid.history.relpath[1], FL_COMMAND+'2', menu_file_open_history_cb, Fluid.history.abspath[1]}, + {Fluid.history.relpath[2], FL_COMMAND+'3', menu_file_open_history_cb, Fluid.history.abspath[2]}, + {Fluid.history.relpath[3], FL_COMMAND+'4', menu_file_open_history_cb, Fluid.history.abspath[3]}, + {Fluid.history.relpath[4], FL_COMMAND+'5', menu_file_open_history_cb, Fluid.history.abspath[4]}, + {Fluid.history.relpath[5], FL_COMMAND+'6', menu_file_open_history_cb, Fluid.history.abspath[5]}, + {Fluid.history.relpath[6], FL_COMMAND+'7', menu_file_open_history_cb, Fluid.history.abspath[6]}, + {Fluid.history.relpath[7], FL_COMMAND+'8', menu_file_open_history_cb, Fluid.history.abspath[7]}, + {Fluid.history.relpath[8], FL_COMMAND+'9', menu_file_open_history_cb, Fluid.history.abspath[8]}, + {Fluid.history.relpath[9], 0, menu_file_open_history_cb, Fluid.history.abspath[9], FL_MENU_DIVIDER}, + {"&Quit", FL_COMMAND+'q', exit_cb}, + {nullptr}, + {"&Edit",0,nullptr,nullptr,FL_SUBMENU}, + {"&Undo", FL_COMMAND+'z', fld::proj::Undo::undo_cb}, + {"&Redo", FL_COMMAND+FL_SHIFT+'z', fld::proj::Undo::redo_cb, nullptr, FL_MENU_DIVIDER}, + {"C&ut", FL_COMMAND+'x', cut_cb}, + {"&Copy", FL_COMMAND+'c', copy_cb}, + {"&Paste", FL_COMMAND+'v', paste_cb}, + {"Dup&licate", FL_COMMAND+'u', duplicate_cb}, + {"&Delete", FL_Delete, delete_cb, nullptr, FL_MENU_DIVIDER}, + {"Select &All", FL_COMMAND+'a', select_all_cb}, + {"Select &None", FL_COMMAND+FL_SHIFT+'a', select_none_cb, nullptr, FL_MENU_DIVIDER}, + {"Pr&operties...", FL_F+1, openwidget_cb}, + {"&Sort",0,sort_cb}, + {"&Earlier", FL_F+2, earlier_cb}, + {"&Later", FL_F+3, later_cb}, + {"&Group", FL_F+7, group_cb}, + {"Ung&roup", FL_F+8, ungroup_cb,nullptr, FL_MENU_DIVIDER}, + {"Hide O&verlays",FL_COMMAND+FL_SHIFT+'o',toggle_overlays}, + {"Hide Guides",FL_COMMAND+FL_SHIFT+'g',toggle_guides}, + {"Hide Restricted",FL_COMMAND+FL_SHIFT+'r',toggle_restricted}, + {"Show Widget &Bin...",FL_ALT+'b',toggle_widgetbin_cb}, + {"Show Code View",FL_ALT+'c', (Fl_Callback*)toggle_codeview_cb, nullptr, FL_MENU_DIVIDER}, + {"Settings...",FL_ALT+'p',show_settings_cb}, + {nullptr}, + {"&New", 0, nullptr, (void *)New_Menu, FL_SUBMENU_POINTER}, + {"&Layout",0,nullptr,nullptr,FL_SUBMENU}, + {"&Align",0,nullptr,nullptr,FL_SUBMENU}, + {"&Left",0,(Fl_Callback *)align_widget_cb,(void*)10}, + {"&Center",0,(Fl_Callback *)align_widget_cb,(void*)11}, + {"&Right",0,(Fl_Callback *)align_widget_cb,(void*)12}, + {"&Top",0,(Fl_Callback *)align_widget_cb,(void*)13}, + {"&Middle",0,(Fl_Callback *)align_widget_cb,(void*)14}, + {"&Bottom",0,(Fl_Callback *)align_widget_cb,(void*)15}, + {nullptr}, + {"&Space Evenly",0,nullptr,nullptr,FL_SUBMENU}, + {"&Across",0,(Fl_Callback *)align_widget_cb,(void*)20}, + {"&Down",0,(Fl_Callback *)align_widget_cb,(void*)21}, + {nullptr}, + {"&Make Same Size",0,nullptr,nullptr,FL_SUBMENU}, + {"&Width",0,(Fl_Callback *)align_widget_cb,(void*)30}, + {"&Height",0,(Fl_Callback *)align_widget_cb,(void*)31}, + {"&Both",0,(Fl_Callback *)align_widget_cb,(void*)32}, + {nullptr}, + {"&Center In Group",0,nullptr,nullptr,FL_SUBMENU}, + {"&Horizontal",0,(Fl_Callback *)align_widget_cb,(void*)40}, + {"&Vertical",0,(Fl_Callback *)align_widget_cb,(void*)41}, + {nullptr}, + {"Synchronized Resize", 0, (Fl_Callback*)menu_layout_sync_resize_cb, nullptr, FL_MENU_TOGGLE|FL_MENU_DIVIDER }, + {"&Grid and Size Settings...",FL_COMMAND+'g',show_grid_cb, nullptr, FL_MENU_DIVIDER}, + {"Presets", 0, layout_suite_marker, (void*)main_layout_submenu_, FL_SUBMENU_POINTER }, + {"Application", 0, select_layout_preset_cb, (void*)nullptr, FL_MENU_RADIO|FL_MENU_VALUE }, + {"Dialog", 0, select_layout_preset_cb, (void*)1, FL_MENU_RADIO }, + {"Toolbox", 0, select_layout_preset_cb, (void*)2, FL_MENU_RADIO }, + {nullptr}, +{"&Shell", 0, Fd_Shell_Command_List::menu_marker, (void*)Fd_Shell_Command_List::default_menu, FL_SUBMENU_POINTER}, + {"&Help",0,nullptr,nullptr,FL_SUBMENU}, + {"&Rapid development with FLUID...",0,help_cb}, + {"&FLTK Programmers Manual...",0,manual_cb, nullptr, FL_MENU_DIVIDER}, + {"&About FLUID...",0,about_cb}, + {nullptr}, +{nullptr}}; + +/** +Show or hide the code preview window. +*/ +void toggle_codeview_cb(Fl_Double_Window *, void *) { + codeview_toggle_visibility(); +} + +/** +Show or hide the code preview window, button callback. +*/ +void toggle_codeview_b_cb(Fl_Button*, void *) { + codeview_toggle_visibility(); +} + diff --git a/fluid/app/Menu.h b/fluid/app/Menu.h new file mode 100644 index 000000000..43f5d7e6f --- /dev/null +++ b/fluid/app/Menu.h @@ -0,0 +1,29 @@ +// +// Application Main Menu 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_APP_MENU_H +#define FLUID_APP_MENU_H + +#include + +extern void exit_cb(class Fl_Widget *,void *); // TODO: remove this +extern void toggle_widgetbin_cb(Fl_Widget *, void *); +extern void menu_file_save_cb(Fl_Widget *, void *arg); +extern void menu_file_open_history_cb(Fl_Widget *, void *v); +extern void align_widget_cb(Fl_Widget *, long); + +#endif // FLUID_APP_MENU_H + diff --git a/fluid/app/Snap_Action.cxx b/fluid/app/Snap_Action.cxx new file mode 100644 index 000000000..f33054eca --- /dev/null +++ b/fluid/app/Snap_Action.cxx @@ -0,0 +1,1835 @@ +// +// Snap action code file for the Fast Light Tool Kit (FLTK). +// +// Copyright 2023-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 "app/Snap_Action.h" + +#include "Fluid.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "nodes/Group_Node.h" +#include "nodes/Window_Node.h" +#include "panels/settings_panel.h" + +#include +#include +#include + +#include +#include +#include +#undef min +#undef max +#include + +using namespace fld; +using namespace fld::app; + +// TODO: warning if the user wants to change builtin layouts +// TODO: move panel to global settings panel (move load & save to main pulldown, or to toolbox?) +// INFO: how about a small tool box for quick preset selection and disabling of individual snaps? + +void select_layout_suite_cb(Fl_Widget *, void *user_data); + +int Snap_Action::eex = 0; +int Snap_Action::eey = 0; + +static Layout_Preset fltk_app = { + 15, 15, 15, 15, 0, 0, // window: l, r, t, b, gx, gy + 10, 10, 10, 10, 0, 0, // group: l, r, t, b, gx, gy + 25, 25, // tabs: t, b + 20, 10, 4, // widget_x: min, inc, gap + 20, 4, 8, // widget_y: min, inc, gap + 0, 14, -1, 14 // labelfont/size, textfont/size +}; +static Layout_Preset fltk_dlg = { + 10, 10, 10, 10, 0, 0, // window: l, r, t, b, gx, gy + 10, 10, 10, 10, 0, 0, // group: l, r, t, b, gx, gy + 20, 20, // tabs: t, b + 20, 10, 5, // widget_x: min, inc, gap + 20, 5, 5, // widget_y: min, inc, gap + 0, 11, -1, 11 // labelfont/size, textfont/size +}; +static Layout_Preset fltk_tool = { + 10, 10, 10, 10, 0, 0, // window: l, r, t, b, gx, gy + 10, 10, 10, 10, 0, 0, // group: l, r, t, b, gx, gy + 18, 18, // tabs: t, b + 16, 8, 2, // widget_x: min, inc, gap + 16, 4, 2, // widget_y: min, inc, gap + 0, 10, -1, 10 // labelfont/size, textfont/size +}; + +static Layout_Preset grid_app = { + 12, 12, 12, 12, 12, 12, // window: l, r, t, b, gx, gy + 12, 12, 12, 12, 12, 12, // group: l, r, t, b, gx, gy + 24, 24, // tabs: t, b + 12, 6, 6, // widget_x: min, inc, gap + 12, 6, 6, // widget_y: min, inc, gap + 0, 14, -1, 14 // labelfont/size, textfont/size +}; + +static Layout_Preset grid_dlg = { + 10, 10, 10, 10, 10, 10, // window: l, r, t, b, gx, gy + 10, 10, 10, 10, 10, 10, // group: l, r, t, b, gx, gy + 20, 20, // tabs: t, b + 10, 5, 5, // widget_x: min, inc, gap + 10, 5, 5, // widget_y: min, inc, gap + 0, 12, -1, 12 // labelfont/size, textfont/size +}; + +static Layout_Preset grid_tool = { + 8, 8, 8, 8, 8, 8, // window: l, r, t, b, gx, gy + 8, 8, 8, 8, 8, 8, // group: l, r, t, b, gx, gy + 16, 16, // tabs: t, b + 8, 4, 4, // widget_x: min, inc, gap + 8, 4, 4, // widget_y: min, inc, gap + 0, 10, -1, 10 // labelfont/size, textfont/size +}; + +Layout_Preset *fld::app::default_layout_preset = &fltk_app; + +static Layout_Suite static_suite_list[] = { + { (char*)"FLTK", (char*)"@fd_beaker FLTK", { &fltk_app, &fltk_dlg, &fltk_tool }, fld::Tool_Store::INTERNAL }, + { (char*)"Grid", (char*)"@fd_beaker Grid", { &grid_app, &grid_dlg, &grid_tool }, fld::Tool_Store::INTERNAL } +}; + +Fl_Menu_Item main_layout_submenu_[] = { + { static_suite_list[0].menu_label, 0, select_layout_suite_cb, (void*)0, FL_MENU_RADIO|FL_MENU_VALUE }, + { static_suite_list[1].menu_label, 0, select_layout_suite_cb, (void*)1, FL_MENU_RADIO }, + { nullptr } +}; + +static Fl_Menu_Item static_choice_menu[] = { + { static_suite_list[0].menu_label }, + { static_suite_list[1].menu_label }, + { nullptr } +}; + + +// ---- Callbacks ------------------------------------------------------ MARK: - + +void layout_suite_marker(Fl_Widget *, void *) { + // intentionally left empty +} + +void select_layout_suite_cb(Fl_Widget *, void *user_data) { + int index = (int)(fl_intptr_t)user_data; + assert(index >= 0); + assert(index < Fluid.layout_list.list_size_); + Fluid.layout_list.current_suite(index); + Fluid.layout_list.update_dialogs(); +} + +void select_layout_preset_cb(Fl_Widget *, void *user_data) { + int index = (int)(fl_intptr_t)user_data; + assert(index >= 0); + assert(index < 3); + Fluid.layout_list.current_preset(index); + Fluid.layout_list.update_dialogs(); +} + +void edit_layout_preset_cb(Fl_Button *w, long user_data) { + int index = (int)w->argument(); + assert(index >= 0); + assert(index < 3); + if (user_data == (long)(fl_intptr_t)LOAD) { + w->value(Fluid.layout_list.current_preset() == index); + } else { + Fluid.layout_list.current_preset(index); + Fluid.layout_list.update_dialogs(); + } +} + +// ---- Layout_Suite ------------------------------------------------ MARK: - + +/** + Write presets to a Preferences database. + */ +void Layout_Preset::write(Fl_Preferences &prefs) { + assert(this); + Fl_Preferences p_win(prefs, "Window"); + p_win.set("left_margin", left_window_margin); + p_win.set("right_margin", right_window_margin); + p_win.set("top_margin", top_window_margin); + p_win.set("bottom_margin", bottom_window_margin); + p_win.set("grid_x", window_grid_x); + p_win.set("grid_y", window_grid_y); + + Fl_Preferences p_grp(prefs, "Group"); + p_grp.set("left_margin", left_group_margin); + p_grp.set("right_margin", right_group_margin); + p_grp.set("top_margin", top_group_margin); + p_grp.set("bottom_margin", bottom_group_margin); + p_grp.set("grid_x", group_grid_x); + p_grp.set("grid_y", group_grid_y); + + Fl_Preferences p_tbs(prefs, "Tabs"); + p_tbs.set("top_margin", top_tabs_margin); + p_tbs.set("bottom_margin", bottom_tabs_margin); + + Fl_Preferences p_wgt(prefs, "Widget"); + p_wgt.set("min_w", widget_min_w); + p_wgt.set("inc_w", widget_inc_w); + p_wgt.set("gap_x", widget_gap_x); + p_wgt.set("min_h", widget_min_h); + p_wgt.set("inc_h", widget_inc_h); + p_wgt.set("gap_y", widget_gap_y); + + Fl_Preferences p_lyt(prefs, "Layout"); + p_lyt.set("labelfont", labelfont); + p_lyt.set("labelsize", labelsize); + p_lyt.set("textfont", textfont); + p_lyt.set("textsize", textsize); +} + +/** + Read presets from a Preferences database. + */ +void Layout_Preset::read(Fl_Preferences &prefs) { + assert(this); + Fl_Preferences p_win(prefs, "Window"); + p_win.get("left_margin", left_window_margin, 15); + p_win.get("right_margin", right_window_margin, 15); + p_win.get("top_margin", top_window_margin, 15); + p_win.get("bottom_margin", bottom_window_margin, 15); + p_win.get("grid_x", window_grid_x, 0); + p_win.get("grid_y", window_grid_y, 0); + + Fl_Preferences p_grp(prefs, "Group"); + p_grp.get("left_margin", left_group_margin, 10); + p_grp.get("right_margin", right_group_margin, 10); + p_grp.get("top_margin", top_group_margin, 10); + p_grp.get("bottom_margin", bottom_group_margin, 10); + p_grp.get("grid_x", group_grid_x, 0); + p_grp.get("grid_y", group_grid_y, 0); + + Fl_Preferences p_tbs(prefs, "Tabs"); + p_tbs.get("top_margin", top_tabs_margin, 25); + p_tbs.get("bottom_margin", bottom_tabs_margin, 25); + + Fl_Preferences p_wgt(prefs, "Widget"); + p_wgt.get("min_w", widget_min_w, 20); + p_wgt.get("inc_w", widget_inc_w, 10); + p_wgt.get("gap_x", widget_gap_x, 4); + p_wgt.get("min_h", widget_min_h, 20); + p_wgt.get("inc_h", widget_inc_h, 4); + p_wgt.get("gap_y", widget_gap_y, 8); + + Fl_Preferences p_lyt(prefs, "Layout"); + p_lyt.get("labelfont", labelfont, 0); + p_lyt.get("labelsize", labelsize, 14); + p_lyt.get("textfont", textfont, 0); + p_lyt.get("textsize", textsize, 14); +} + +/** + Write presets to an .fl project file. + */ +void Layout_Preset::write(fld::io::Project_Writer *out) { + out->write_string(" preset { 1\n"); // preset format version + out->write_string(" %d %d %d %d %d %d\n", + left_window_margin, right_window_margin, + top_window_margin, bottom_window_margin, + window_grid_x, window_grid_y); + out->write_string(" %d %d %d %d %d %d\n", + left_group_margin, right_group_margin, + top_group_margin, bottom_group_margin, + group_grid_x, group_grid_y); + out->write_string(" %d %d\n", top_tabs_margin, bottom_tabs_margin); + out->write_string(" %d %d %d %d %d %d\n", + widget_min_w, widget_inc_w, widget_gap_x, + widget_min_h, widget_inc_h, widget_gap_y); + out->write_string(" %d %d %d %d\n", + labelfont, labelsize, textfont, textsize); + out->write_string(" }\n"); // preset format version +} + +/** + Read presets from an .fl project file. + */ +void Layout_Preset::read(fld::io::Project_Reader *in) { + const char *key; + key = in->read_word(1); + if (key && !strcmp(key, "{")) { + for (;;) { + key = in->read_word(); + if (!key) return; + if (key[0] == '}') break; + int ver = atoi(key); + if (ver == 0) { + continue; + } else if (ver == 1) { + left_window_margin = in->read_int(); + right_window_margin = in->read_int(); + top_window_margin = in->read_int(); + bottom_window_margin = in->read_int(); + window_grid_x = in->read_int(); + window_grid_y = in->read_int(); + + left_group_margin = in->read_int(); + right_group_margin = in->read_int(); + top_group_margin = in->read_int(); + bottom_group_margin = in->read_int(); + group_grid_x = in->read_int(); + group_grid_y = in->read_int(); + + top_tabs_margin = in->read_int(); + bottom_tabs_margin = in->read_int(); + + widget_min_w = in->read_int(); + widget_inc_w = in->read_int(); + widget_gap_x = in->read_int(); + widget_min_h = in->read_int(); + widget_inc_h = in->read_int(); + widget_gap_y = in->read_int(); + + labelfont = in->read_int(); + labelsize = in->read_int(); + textfont = in->read_int(); + textsize = in->read_int(); + } else { // skip unknown chunks + for (;;) { + key = in->read_word(1); + if (key && (key[0] == '}')) + return; + } + } + } + } else { + // format error + } +} + +/** + Return the preferred text size, but make sure it's not 0. + */ +int Layout_Preset::textsize_not_null() { + // try the user selected text size + if (textsize > 0) return textsize; + // if the user did not set one, try the label size + if (labelsize > 0) return labelsize; + // if that doesn;t work, fall back to the default value + return 14; +} + + +// ---- Layout_Suite ------------------------------------------------ MARK: - + +/** + Write a presets suite to a Preferences database. + */ +void Layout_Suite::write(Fl_Preferences &prefs) { + assert(this); + assert(name_); + prefs.set("name", name_); + for (int i = 0; i < 3; ++i) { + Fl_Preferences prefs_preset(prefs, Fl_Preferences::Name(i)); + assert(layout[i]); + layout[i]->write(prefs_preset); + } +} + +/** + Read a presets suite from a Preferences database. + */ +void Layout_Suite::read(Fl_Preferences &prefs) { + assert(this); + for (int i = 0; i < 3; ++i) { + Fl_Preferences prefs_preset(prefs, Fl_Preferences::Name(i)); + assert(layout[i]); + layout[i]->read(prefs_preset); + } +} + +/** + Write a presets suite to an .fl project file. + */ +void Layout_Suite::write(fld::io::Project_Writer *out) { + out->write_string(" suite {\n"); + out->write_string(" name "); out->write_word(name_); out->write_string("\n"); + for (int i = 0; i < 3; ++i) { + layout[i]->write(out); + } + out->write_string(" }\n"); +} + +/** + Read a presets suite from an .fl project file. + */ +void Layout_Suite::read(fld::io::Project_Reader *in) { + const char *key; + key = in->read_word(1); + if (key && !strcmp(key, "{")) { + int ix = 0; + for (;;) { + key = in->read_word(); + if (!key) return; + if (!strcmp(key, "name")) { + name(in->read_word()); + } else if (!strcmp(key, "preset")) { + if (ix >= 3) return; // file format error + layout[ix++]->read(in); + } else if (!strcmp(key, "}")) { + break; + } else { + in->read_word(); // unknown key, ignore, hopefully a key-value pair + } + } + } else { + // file format error + } +} + +/** + \brief Update the menu_label to show a symbol representing the storage location. + Also updates the FLUID user interface. + */ +void Layout_Suite::update_label() { + std::string sym; + switch (storage_) { + case fld::Tool_Store::INTERNAL: sym.assign("@fd_beaker "); break; + case fld::Tool_Store::USER: sym.assign("@fd_user "); break; + case fld::Tool_Store::PROJECT: sym.assign("@fd_project "); break; + case fld::Tool_Store::FILE: sym.assign("@fd_file "); break; + } + sym.append(name_); + if (menu_label) + ::free(menu_label); + menu_label = fl_strdup(sym.c_str()); + Fluid.layout_list.update_menu_labels(); +} + +/** + \brief Update the Suite name and the Suite menu_label. + Also updates the FLUID user interface. + */ +void Layout_Suite::name(const char *n) { + if (name_) + ::free(name_); + if (n) + name_ = fl_strdup(n); + else + name_ = nullptr; + update_label(); +} + +/** + Initialize the class for first use. + */ +void Layout_Suite::init() { + name_ = nullptr; + menu_label = nullptr; + layout[0] = layout[1] = layout[2] = nullptr; + storage_ = fld::Tool_Store::INTERNAL; +} + +/** + Free all allocated resources. + */ +Layout_Suite::~Layout_Suite() { + if (storage_ == fld::Tool_Store::INTERNAL) return; + if (name_) ::free(name_); + for (int i = 0; i < 3; ++i) { + delete layout[i]; + } +} + +// ---- Layout_List ------------------------------------------------- MARK: - + +/** + Draw a little FLUID beaker symbol. + */ +static void fd_beaker(Fl_Color c) { + fl_color(221); + fl_begin_polygon(); + fl_vertex(-0.6, 0.2); + fl_vertex(-0.9, 0.8); + fl_vertex(-0.8, 0.9); + fl_vertex( 0.8, 0.9); + fl_vertex( 0.9, 0.8); + fl_vertex( 0.6, 0.2); + fl_end_polygon(); + fl_color(c); + fl_begin_line(); + fl_vertex(-0.3, -0.9); + fl_vertex(-0.2, -0.8); + fl_vertex(-0.2, -0.2); + fl_vertex(-0.9, 0.8); + fl_vertex(-0.8, 0.9); + fl_vertex( 0.8, 0.9); + fl_vertex( 0.9, 0.8); + fl_vertex( 0.2, -0.2); + fl_vertex( 0.2, -0.8); + fl_vertex( 0.3, -0.9); + fl_end_line(); +} + +/** + Draw a user silhouette symbol + */ +static void fd_user(Fl_Color c) { + fl_color(245); + fl_begin_complex_polygon(); + fl_arc( 0.1, 0.9, 0.8, 0.0, 80.0); + fl_arc( 0.0, -0.5, 0.4, -65.0, 245.0); + fl_arc(-0.1, 0.9, 0.8, 100.0, 180.0); + fl_end_complex_polygon(); + fl_color(c); + fl_begin_line(); + fl_arc( 0.1, 0.9, 0.8, 0.0, 80.0); + fl_arc( 0.0, -0.5, 0.4, -65.0, 245.0); + fl_arc(-0.1, 0.9, 0.8, 100.0, 180.0); + fl_end_line(); +} + +/** + Draw a document symbol. + */ +static void fd_project(Fl_Color c) { + Fl_Color fc = FL_LIGHT2; + fl_color(fc); + fl_begin_complex_polygon(); + fl_vertex(-0.7, -1.0); + fl_vertex(0.1, -1.0); + fl_vertex(0.1, -0.4); + fl_vertex(0.7, -0.4); + fl_vertex(0.7, 1.0); + fl_vertex(-0.7, 1.0); + fl_end_complex_polygon(); + + fl_color(fl_lighter(fc)); + fl_begin_polygon(); + fl_vertex(0.1, -1.0); + fl_vertex(0.1, -0.4); + fl_vertex(0.7, -0.4); + fl_end_polygon(); + + fl_color(fl_darker(c)); + fl_begin_loop(); + fl_vertex(-0.7, -1.0); + fl_vertex(0.1, -1.0); + fl_vertex(0.1, -0.4); + fl_vertex(0.7, -0.4); + fl_vertex(0.7, 1.0); + fl_vertex(-0.7, 1.0); + fl_end_loop(); + + fl_begin_line(); + fl_vertex(0.1, -1.0); + fl_vertex(0.7, -0.4); + fl_end_line(); +} + +/** + Draw a 3 1/2" floppy symbol. + */ +void fd_file(Fl_Color c) { + Fl_Color fl = FL_LIGHT2; + Fl_Color fc = FL_DARK3; + fl_color(fc); + fl_begin_polygon(); // case + fl_vertex(-0.9, -1.0); + fl_vertex(0.9, -1.0); + fl_vertex(1.0, -0.9); + fl_vertex(1.0, 0.9); + fl_vertex(0.9, 1.0); + fl_vertex(-0.9, 1.0); + fl_vertex(-1.0, 0.9); + fl_vertex(-1.0, -0.9); + fl_end_polygon(); + + fl_color(fl_lighter(fl)); + fl_begin_polygon(); + fl_vertex(-0.7, -1.0); // slider + fl_vertex(0.7, -1.0); + fl_vertex(0.7, -0.4); + fl_vertex(-0.7, -0.4); + fl_end_polygon(); + + fl_begin_polygon(); // label + fl_vertex(-0.7, 0.0); + fl_vertex(0.7, 0.0); + fl_vertex(0.7, 1.0); + fl_vertex(-0.7, 1.0); + fl_end_polygon(); + + fl_color(fc); + fl_begin_polygon(); + fl_vertex(-0.5, -0.9); // slot + fl_vertex(-0.3, -0.9); + fl_vertex(-0.3, -0.5); + fl_vertex(-0.5, -0.5); + fl_end_polygon(); + + fl_color(fl_darker(c)); + fl_begin_loop(); + fl_vertex(-0.9, -1.0); + fl_vertex(0.9, -1.0); + fl_vertex(1.0, -0.9); + fl_vertex(1.0, 0.9); + fl_vertex(0.9, 1.0); + fl_vertex(-0.9, 1.0); + fl_vertex(-1.0, 0.9); + fl_vertex(-1.0, -0.9); + fl_end_loop(); +} + +/** + Instantiate the class that holds a list of all layouts and manages the UI. + */ +Layout_List::Layout_List() +: main_menu_(main_layout_submenu_), + choice_menu_(static_choice_menu), + list_(static_suite_list), + list_size_(2), + list_capacity_(2), + list_is_static_(true), + current_suite_(0), + current_preset_(0) +{ + fl_add_symbol("fd_beaker", fd_beaker, 1); + fl_add_symbol("fd_user", fd_user, 1); + fl_add_symbol("fd_project", fd_project, 1); + fl_add_symbol("fd_file", fd_file, 1); +} + +/** + Release allocated resources. + */ +Layout_List::~Layout_List() { + assert(this); + if (!list_is_static_) { + ::free(main_menu_); + ::free(choice_menu_); + for (int i = 0; i < list_size_; i++) { + Layout_Suite &suite = list_[i]; + if (suite.storage_ != fld::Tool_Store::INTERNAL) + suite.~Layout_Suite(); + } + ::free(list_); + } +} + +/** + Update the Setting dialog and menus to reflect the current Layout selection state. + */ +void Layout_List::update_dialogs() { + static Fl_Menu_Item *preset_menu = nullptr; + if (!preset_menu) { + preset_menu = (Fl_Menu_Item*)Fluid.main_menubar->find_item(select_layout_preset_cb); + assert(preset_menu); + } + assert(this); + assert(current_suite_ >= 0 ); + assert(current_suite_ < list_size_); + assert(current_preset_ >= 0 ); + assert(current_preset_ < 3); + Fluid.proj.layout = list_[current_suite_].layout[current_preset_]; + assert(Fluid.proj.layout); + if (w_settings_layout_tab) { + w_settings_layout_tab->do_callback(w_settings_layout_tab, LOAD); + layout_choice->redraw(); + } + preset_menu[current_preset_].setonly(preset_menu); + main_menu_[current_suite_].setonly(main_menu_); +} + +/** + Refresh the label pointers for both pulldown menus. + */ +void Layout_List::update_menu_labels() { + for (int i=0; iwrite_string("\nsnap {\n ver 1\n"); + out->write_string(" current_suite "); out->write_word(list_[current_suite()].name_); out->write_string("\n"); + out->write_string(" current_preset %d\n", current_preset()); + for (int i=0; iwrite_string("}"); +} + +/** + Read Suite and Layout selection and project layout data from an .fl project file. + */ +void Layout_List::read(fld::io::Project_Reader *in) { + const char *key; + key = in->read_word(1); + if (key && !strcmp(key, "{")) { + std::string cs; + int cp = 0; + for (;;) { + key = in->read_word(); + if (!key) return; + if (!strcmp(key, "ver")) { + in->read_int(); + } else if (!strcmp(key, "current_suite")) { + cs = in->read_word(); + } else if (!strcmp(key, "current_preset")) { + cp = in->read_int(); + } else if (!strcmp(key, "suite")) { + int n = add(in->filename_name()); + list_[n].read(in); + list_[n].storage(fld::Tool_Store::PROJECT); + } else if (!strcmp(key, "}")) { + break; + } else { + in->read_word(); // unknown key, ignore, hopefully a key-value pair + } + } + current_suite(cs); + current_preset(cp); + update_dialogs(); + } else { + // old style "snap" is followed by an integer. Ignore. + } +} + +/** + Set the current Suite. + \param[in] ix index into list of suites + */ +void Layout_List::current_suite(int ix) { + assert(ix >= 0); + assert(ix < list_size_); + current_suite_ = ix; + Fluid.proj.layout = list_[current_suite_].layout[current_preset_]; +} + +/** + Set the current Suite. + \param[in] arg_name name of the selected suite + \return if no name is given or the name is not found, keep the current suite selected + */ +void Layout_List::current_suite(std::string arg_name) { + if (arg_name.empty()) return; + for (int i = 0; i < list_size_; ++i) { + Layout_Suite &suite = list_[i]; + if (suite.name_ && (strcmp(suite.name_, arg_name.c_str()) == 0)) { + current_suite(i); + break; + } + } +} + +/** + Select a Preset within the current Suite. + \param[in] ix 0 = application, 1 = dialog, 2 = toolbox + */ +void Layout_List::current_preset(int ix) { + assert(ix >= 0); + assert(ix < 3); + current_preset_ = ix; + Fluid.proj.layout = list_[current_suite_].layout[current_preset_]; +} + +/** + Allocate enough space for n entries in the list. + */ +void Layout_List::capacity(int n) { + static Fl_Menu_Item *suite_menu = nullptr; + if (!suite_menu) + suite_menu = (Fl_Menu_Item*)Fluid.main_menubar->find_item(layout_suite_marker); + + int old_n = list_size_; + int i; + + Layout_Suite *new_list = (Layout_Suite*)::calloc(n, sizeof(Layout_Suite)); + for (i = 0; i < old_n; i++) + new_list[i] = list_[i]; + if (!list_is_static_) ::free(list_); + list_ = new_list; + + Fl_Menu_Item *new_main_menu = (Fl_Menu_Item*)::calloc(n+1, sizeof(Fl_Menu_Item)); + for (i = 0; i < old_n; i++) + new_main_menu[i] = main_menu_[i]; + if (!list_is_static_) ::free(main_menu_); + main_menu_ = new_main_menu; + suite_menu->user_data(main_menu_); + + Fl_Menu_Item *new_choice_menu = (Fl_Menu_Item*)::calloc(n+1, sizeof(Fl_Menu_Item)); + for (i = 0; i < old_n; i++) + new_choice_menu[i] = choice_menu_[i]; + if (!list_is_static_) ::free(choice_menu_); + choice_menu_ = new_choice_menu; + if (layout_choice) layout_choice->menu(choice_menu_); + + list_capacity_ = n; + list_is_static_ = false; +} + +/** + \brief Clone the currently selected suite and append it to the list. + Selects the new layout and updates the UI. + */ +int Layout_List::add(const char *name) { + if (list_size_ == list_capacity_) { + capacity(list_capacity_ * 2); + } + int n = list_size_; + Layout_Suite &old_suite = list_[current_suite_]; + Layout_Suite &new_suite = list_[n]; + new_suite.init(); + new_suite.name(name); + for (int i=0; i<3; ++i) { + new_suite.layout[i] = new Layout_Preset; + ::memcpy(new_suite.layout[i], old_suite.layout[i], sizeof(Layout_Preset)); + } + fld::Tool_Store new_storage = old_suite.storage_; + if (new_storage == fld::Tool_Store::INTERNAL) + new_storage = fld::Tool_Store::USER; + new_suite.storage(new_storage); + main_menu_[n].label(new_suite.menu_label); + main_menu_[n].callback(main_menu_[0].callback()); + main_menu_[n].argument(n); + main_menu_[n].flags = main_menu_[0].flags; + choice_menu_[n].label(new_suite.menu_label); + list_size_++; + current_suite(n); + return n; +} + +/** + Rename the current Suite. + */ +void Layout_List::rename(const char *name) { + int n = current_suite(); + list_[n].name(name); + main_menu_[n].label(list_[n].menu_label); + choice_menu_[n].label(list_[n].menu_label); +} + +/** + Remove the given suite. + \param[in] ix index into list of suites + */ +void Layout_List::remove(int ix) { + int tail = list_size_-ix-1; + if (tail) { + for (int i = ix; i < list_size_-1; i++) + list_[i] = list_[i+1]; + } + ::memmove(main_menu_+ix, main_menu_+ix+1, (tail+1) * sizeof(Fl_Menu_Item)); + ::memmove(choice_menu_+ix, choice_menu_+ix+1, (tail+1) * sizeof(Fl_Menu_Item)); + list_size_--; + if (current_suite() >= list_size_) + current_suite(list_size_ - 1); +} + +/** + Remove all Suites that use the given storage attribute. + \param[in] storage storage attribute, see fld::Tool_Store::INTERNAL, etc. + */ +void Layout_List::remove_all(fld::Tool_Store storage) { + for (int i=list_size_-1; i>=0; --i) { + if (list_[i].storage_ == storage) + remove(i); + } +} + +// ---- Helper --------------------------------------------------------- MARK: - + +static void draw_h_arrow(int, int, int); +static void draw_v_arrow(int x, int y1, int y2); +static void draw_left_brace(const Fl_Widget *w); +static void draw_right_brace(const Fl_Widget *w); +static void draw_top_brace(const Fl_Widget *w); +static void draw_bottom_brace(const Fl_Widget *w); +static void draw_grid(int x, int y, int dx, int dy); +void draw_width(int x, int y, int r, Fl_Align a); +void draw_height(int x, int y, int b, Fl_Align a); + +static int nearest(int x, int left, int grid, int right=0x7fff) { + int grid_x = ((x-left+grid/2)/grid)*grid+left; + if (grid_x < left+grid/2) return left; // left+grid/2; + if (grid_x > right-grid/2) return right; // right-grid/2; + return grid_x; +} + +static bool in_window(Snap_Data &d) { + return (d.wgt && d.wgt->parent == d.win); +} + +static bool in_group(Snap_Data &d) { + return (d.wgt && d.wgt->parent && d.wgt->parent->is_a(Type::Group) && d.wgt->parent != d.win); +} + +static bool in_tabs(Snap_Data &d) { + return (d.wgt && d.wgt->parent && d.wgt->parent->is_a(Type::Tabs)); +} + +static Fl_Group *parent(Snap_Data &d) { + return (d.wgt->o->parent()); +} + +// ---- Snap_Action ------------------------------------------------- MARK: - + +/** \class Snap_Action + + When a user drags one or more widgets, snap actions can be defined that provide + hints if a preferred widget position or size is nearby. The user's motion is + then directed towards the nearest preferred position, and the widget selection + snaps into place. + + FLUID provides a list of various snap actions. Every snap action uses the data + from the motion event and combines it with the sizes and positions of all other + widgets in the layout. + + Common snap actions include gaps and margins, but also alignments and + simple grid positions. + */ + +/** + \brief Check if a snap action has reached a preferred x position. + \param[inout] d current event data + \param[in] x_ref position of moving point + \param[in] x_snap position of target point + \return 1 if the points are not within range and won;t be considered + \return 0 if the point is as close as another in a previous action + \return -1 if this point is closer than any previous check, and this is the + new distance to beat. + */ +int Snap_Action::check_x_(Snap_Data &d, int x_ref, int x_snap) { + int dd = x_ref + d.dx - x_snap; + int d2 = abs(dd); + if (d2 > d.x_dist) return 1; + dx = d.dx_out = d.dx - dd; + ex = d.ex_out = x_snap; + if (d2 == d.x_dist) return 0; + d.x_dist = d2; + return -1; +} + +/** + \brief Check if a snap action has reached a preferred y position. + \see Snap_Action::check_x_(Snap_Data &d, int x_ref, int x_snap) + */ +int Snap_Action::check_y_(Snap_Data &d, int y_ref, int y_snap) { + int dd = y_ref + d.dy - y_snap; + int d2 = abs(dd); + if (d2 > d.y_dist) return 1; + dy = d.dy_out = d.dy - dd; + ey = d.ey_out = y_snap; + if (d2 == d.y_dist) return 0; + d.y_dist = d2; + return -1; +} + +/** + \brief Check if a snap action has reached a preferred x and y position. + \see Snap_Action::check_x_(Snap_Data &d, int x_ref, int x_snap) + */ +void Snap_Action::check_x_y_(Snap_Data &d, int x_ref, int x_snap, int y_ref, int y_snap) { + int ddx = x_ref + d.dx - x_snap; + int d2x = abs(ddx); + int ddy = y_ref + d.dy - y_snap; + int d2y = abs(ddy); + if ((d2x <= d.x_dist) && (d2y <= d.y_dist)) { + dx = d.dx_out = d.dx - ddx; + ex = d.ex_out = x_snap; + d.x_dist = d2x; + dy = d.dy_out = d.dy - ddy; + ey = d.ey_out = y_snap; + d.y_dist = d2y; + } +} + +/** + \brief Check if a snap action was applied to the current event. + This method is used to determine if a visual indicator for this snap action + should be drawn. + \param[inout] d current event data + */ +bool Snap_Action::matches(Snap_Data &d) { + switch (type) { + case 1: return (d.drag & mask) && (eex == ex) && (d.dx == dx); + case 2: return (d.drag & mask) && (eey == ey) && (d.dy == dy); + case 3: return (d.drag & mask) && (eex == ex) && (d.dx == dx) && (eey == ey) && (d.dy == dy); + } + return false; +} + +/** + \brief Run through all possible snap actions and store the winning coordinates in eex and eey. + \param[inout] d current event data + */ +void Snap_Action::check_all(Snap_Data &data) { + for (int i=0; list[i]; i++) { + if (list[i]->mask & data.drag) + list[i]->check(data); + } + eex = data.ex_out; + eey = data.ey_out; +} + +/** + \brief Draw a visual indicator for all snap actions that were applied during the last check. + Only one snap coordinate can win. FLUID chooses the one that is closest to + the current user event. If two or more snap actions suggest the same + coordinate, all of them will be drawn. + \param[inout] d current event data + */ +void Snap_Action::draw_all(Snap_Data &data) { + for (int i=0; list[i]; i++) { + if (list[i]->matches(data)) + list[i]->draw(data); + } +} + +/** Return a sensible step size for resizing a widget. */ +void Snap_Action::get_resize_stepsize(int &x_step, int &y_step) { + auto layout = Fluid.proj.layout; + if ((layout->widget_inc_w > 1) && (layout->widget_inc_h > 1)) { + x_step = layout->widget_inc_w; + y_step = layout->widget_inc_h; + } else if ((layout->group_grid_x > 1) && (layout->group_grid_y > 1)) { + x_step = layout->group_grid_x; + y_step = layout->group_grid_y; + } else { + x_step = layout->window_grid_x; + y_step = layout->window_grid_y; + } +} + +/** Return a sensible step size for moving a widget. */ +void Snap_Action::get_move_stepsize(int &x_step, int &y_step) { + auto layout = Fluid.proj.layout; + if ((layout->group_grid_x > 1) && (layout->group_grid_y > 1)) { + x_step = layout->group_grid_x; + y_step = layout->group_grid_y; + } else if ((layout->window_grid_x > 1) && (layout->window_grid_y > 1)) { + x_step = layout->window_grid_x; + y_step = layout->window_grid_y; + } else { + x_step = layout->widget_gap_x; + y_step = layout->widget_gap_y; + } +} + +/** Fix the given size to the same or next bigger snap position. */ +void Snap_Action::better_size(int &w, int &h) { + auto layout = Fluid.proj.layout; + int x_min = 1, y_min = 1, x_inc = 1, y_inc = 1; + get_resize_stepsize(x_inc, y_inc); + if (x_inc < 1) x_inc = 1; + if (y_inc < 1) y_inc = 1; + if ((layout->widget_min_w > 1) && (layout->widget_min_h > 1)) { + x_min = layout->widget_min_w; + y_min = layout->widget_min_h; + } else if ((layout->group_grid_x > 1) && (layout->group_grid_y > 1)) { + x_min = layout->group_grid_x; + y_min = layout->group_grid_y; + } else { + x_min = x_inc; + y_min = y_inc; + } + int ww = std::max(w - x_min, 0); w = (w - ww + x_inc - 1) / x_inc; w = w * x_inc; w = w + ww; + int hh = std::max(h - y_min, 0); h = (h - hh + y_inc - 1) / y_inc; h = h * y_inc; h = h + hh; +} + + +// ---- snapping prototypes -------------------------------------------- MARK: - + +/** + Base class for all actions that drag the left side or the entire widget. + */ +class Fd_Snap_Left : public Snap_Action { +public: + Fd_Snap_Left() { type = 1; mask = FD_LEFT|FD_DRAG; } +}; + +/** + Base class for all actions that drag the right side or the entire widget. + */ +class Fd_Snap_Right : public Snap_Action { +public: + Fd_Snap_Right() { type = 1; mask = FD_RIGHT|FD_DRAG; } +}; + +/** + Base class for all actions that drag the top side or the entire widget. + */ +class Fd_Snap_Top : public Snap_Action { +public: + Fd_Snap_Top() { type = 2; mask = FD_TOP|FD_DRAG; } +}; + +/** + Base class for all actions that drag the bottom side or the entire widget. + */ +class Fd_Snap_Bottom : public Snap_Action { +public: + Fd_Snap_Bottom() { type = 2; mask = FD_BOTTOM|FD_DRAG; } +}; + +// ---- window snapping ------------------------------------------------ MARK: - + +/** + Check if the widget hits the left window edge. + */ +class Fd_Snap_Left_Window_Edge : public Fd_Snap_Left { +public: + void check(Snap_Data &d) override { clr(); check_x_(d, d.bx, 0); } + void draw(Snap_Data &d) override { draw_left_brace(d.win->o); }; +}; +Fd_Snap_Left_Window_Edge snap_left_window_edge; + +/** + Check if the widget hits the right window edge. + */ +class Fd_Snap_Right_Window_Edge : public Fd_Snap_Right { +public: + void check(Snap_Data &d) override { clr(); check_x_(d, d.br, d.win->o->w()); } + void draw(Snap_Data &d) override { draw_right_brace(d.win->o); }; +}; +Fd_Snap_Right_Window_Edge snap_right_window_edge; + +/** + Check if the widget hits the top window edge. + */ +class Fd_Snap_Top_Window_Edge : public Fd_Snap_Top { +public: + void check(Snap_Data &d) override { clr(); check_y_(d, d.by, 0); } + void draw(Snap_Data &d) override { draw_top_brace(d.win->o); }; +}; +Fd_Snap_Top_Window_Edge snap_top_window_edge; + +/** + Check if the widget hits the bottom window edge. + */ +class Fd_Snap_Bottom_Window_Edge : public Fd_Snap_Bottom { +public: + void check(Snap_Data &d) override { clr(); check_y_(d, d.bt, d.win->o->h()); } + void draw(Snap_Data &d) override { draw_bottom_brace(d.win->o); }; +}; +Fd_Snap_Bottom_Window_Edge snap_bottom_window_edge; + +/** + Check if the widget hits the left window edge plus a user defined margin. + */ +class Fd_Snap_Left_Window_Margin : public Fd_Snap_Left { +public: + void check(Snap_Data &d) override { + clr(); + if (in_window(d)) check_x_(d, d.bx, Fluid.proj.layout->left_window_margin); + } + void draw(Snap_Data &d) override { + draw_h_arrow(d.bx, (d.by+d.bt)/2, 0); + }; +}; +Fd_Snap_Left_Window_Margin snap_left_window_margin; + +class Fd_Snap_Right_Window_Margin : public Fd_Snap_Right { +public: + void check(Snap_Data &d) override { + clr(); + if (in_window(d)) check_x_(d, d.br, d.win->o->w()-Fluid.proj.layout->right_window_margin); + } + void draw(Snap_Data &d) override { + draw_h_arrow(d.br, (d.by+d.bt)/2, d.win->o->w()-1); + }; +}; +Fd_Snap_Right_Window_Margin snap_right_window_margin; + +class Fd_Snap_Top_Window_Margin : public Fd_Snap_Top { +public: + void check(Snap_Data &d) override { + clr(); + if (in_window(d)) check_y_(d, d.by, Fluid.proj.layout->top_window_margin); + } + void draw(Snap_Data &d) override { + draw_v_arrow((d.bx+d.br)/2, d.by, 0); + }; +}; +Fd_Snap_Top_Window_Margin snap_top_window_margin; + +class Fd_Snap_Bottom_Window_Margin : public Fd_Snap_Bottom { +public: + void check(Snap_Data &d) override { + clr(); + if (in_window(d)) check_y_(d, d.bt, d.win->o->h()-Fluid.proj.layout->bottom_window_margin); + } + void draw(Snap_Data &d) override { + draw_v_arrow((d.bx+d.br)/2, d.bt, d.win->o->h()-1); + }; +}; +Fd_Snap_Bottom_Window_Margin snap_bottom_window_margin; + +// ---- group snapping ------------------------------------------------- MARK: - + +/** + Check if the widget hits the left group edge. + */ +class Fd_Snap_Left_Group_Edge : public Fd_Snap_Left { +public: + void check(Snap_Data &d) override { + clr(); + if (in_group(d)) check_x_(d, d.bx, parent(d)->x()); + } + void draw(Snap_Data &d) override { + draw_left_brace(parent(d)); + }; +}; +Fd_Snap_Left_Group_Edge snap_left_group_edge; + +class Fd_Snap_Right_Group_Edge : public Fd_Snap_Right { +public: + void check(Snap_Data &d) override { + clr(); + if (in_group(d)) check_x_(d, d.br, parent(d)->x() + parent(d)->w()); + } + void draw(Snap_Data &d) override { + draw_right_brace(parent(d)); + }; +}; +Fd_Snap_Right_Group_Edge snap_right_group_edge; + +class Fd_Snap_Top_Group_Edge : public Fd_Snap_Top { +public: + void check(Snap_Data &d) override { + clr(); + if (in_group(d)) check_y_(d, d.by, parent(d)->y()); + } + void draw(Snap_Data &d) override { + draw_top_brace(parent(d)); + }; +}; +Fd_Snap_Top_Group_Edge snap_top_group_edge; + +class Fd_Snap_Bottom_Group_Edge : public Fd_Snap_Bottom { +public: + void check(Snap_Data &d) override { + clr(); + if (in_group(d)) check_y_(d, d.bt, parent(d)->y() + parent(d)->h()); + } + void draw(Snap_Data &d) override { + draw_bottom_brace(parent(d)); + }; +}; +Fd_Snap_Bottom_Group_Edge snap_bottom_group_edge; + + +/** + Check if the widget hits the left group edge plus a user defined margin. + */ +class Fd_Snap_Left_Group_Margin : public Fd_Snap_Left { +public: + void check(Snap_Data &d) override { + clr(); + if (in_group(d)) check_x_(d, d.bx, parent(d)->x() + Fluid.proj.layout->left_group_margin); + } + void draw(Snap_Data &d) override { + draw_left_brace(parent(d)); + draw_h_arrow(d.bx, (d.by+d.bt)/2, parent(d)->x()); + }; +}; +Fd_Snap_Left_Group_Margin snap_left_group_margin; + +class Fd_Snap_Right_Group_Margin : public Fd_Snap_Right { +public: + void check(Snap_Data &d) override { + clr(); + if (in_group(d)) check_x_(d, d.br, parent(d)->x()+parent(d)->w()-Fluid.proj.layout->right_group_margin); + } + void draw(Snap_Data &d) override { + draw_right_brace(parent(d)); + draw_h_arrow(d.br, (d.by+d.bt)/2, parent(d)->x()+parent(d)->w()-1); + }; +}; +Fd_Snap_Right_Group_Margin snap_right_group_margin; + +class Fd_Snap_Top_Group_Margin : public Fd_Snap_Top { +public: + void check(Snap_Data &d) override { + clr(); + if (in_group(d) && !in_tabs(d)) check_y_(d, d.by, parent(d)->y()+Fluid.proj.layout->top_group_margin); + } + void draw(Snap_Data &d) override { + draw_top_brace(parent(d)); + draw_v_arrow((d.bx+d.br)/2, d.by, parent(d)->y()); + }; +}; +Fd_Snap_Top_Group_Margin snap_top_group_margin; + +class Fd_Snap_Bottom_Group_Margin : public Fd_Snap_Bottom { +public: + void check(Snap_Data &d) override { + clr(); + if (in_group(d) && !in_tabs(d)) check_y_(d, d.bt, parent(d)->y()+parent(d)->h()-Fluid.proj.layout->bottom_group_margin); + } + void draw(Snap_Data &d) override { + draw_bottom_brace(parent(d)); + draw_v_arrow((d.bx+d.br)/2, d.bt, parent(d)->y()+parent(d)->h()-1); + }; +}; +Fd_Snap_Bottom_Group_Margin snap_bottom_group_margin; + +// ----- tabs snapping ------------------------------------------------- MARK: - + +/** + Check if the widget top hits the Fl_Tabs group top edge plus a user defined margin. + */ +class Fd_Snap_Top_Tabs_Margin : public Fd_Snap_Top_Group_Margin { +public: + void check(Snap_Data &d) override { + clr(); + if (in_tabs(d)) check_y_(d, d.by, parent(d)->y()+Fluid.proj.layout->top_tabs_margin); + } +}; +Fd_Snap_Top_Tabs_Margin snap_top_tabs_margin; + +class Fd_Snap_Bottom_Tabs_Margin : public Fd_Snap_Bottom_Group_Margin { +public: + void check(Snap_Data &d) override { + clr(); + if (in_tabs(d)) check_y_(d, d.bt, parent(d)->y()+parent(d)->h()-Fluid.proj.layout->bottom_tabs_margin); + } +}; +Fd_Snap_Bottom_Tabs_Margin snap_bottom_tabs_margin; + +// ----- grid snapping ------------------------------------------------- MARK: - + +/** + Base class for grid based snapping. + */ +class Fd_Snap_Grid : public Snap_Action { +protected: + int nearest_x, nearest_y; +public: + Fd_Snap_Grid() { type = 3; mask = FD_LEFT|FD_TOP|FD_DRAG; } + void check_grid(Snap_Data &d, int left, int grid_x, int right, int top, int grid_y, int bottom) { + if ((grid_x <= 1) || (grid_y <= 1)) return; + int suggested_x = d.bx + d.dx; + nearest_x = nearest(suggested_x, left, grid_x, right); + int suggested_y = d.by + d.dy; + nearest_y = nearest(suggested_y, top, grid_y, bottom); + if (d.drag == FD_LEFT) + check_x_(d, d.bx, nearest_x); + else if (d.drag == FD_TOP) + check_y_(d, d.by, nearest_y); + else + check_x_y_(d, d.bx, nearest_x, d.by, nearest_y); + } + bool matches(Snap_Data &d) override { + if (d.drag == FD_LEFT) return (eex == ex); + if (d.drag == FD_TOP) return (eey == ey) && (d.dx == dx); + return (d.drag & mask) && (eex == ex) && (d.dx == dx) && (eey == ey) && (d.dy == dy); + } +}; + +/** + Check if the widget hits window grid coordinates. + */ +class Fd_Snap_Window_Grid : public Fd_Snap_Grid { +public: + void check(Snap_Data &d) override { + auto layout = Fluid.proj.layout; + clr(); + if (in_window(d)) check_grid(d, layout->left_window_margin, layout->window_grid_x, d.win->o->w()-layout->right_window_margin, + layout->top_window_margin, layout->window_grid_y, d.win->o->h()-layout->bottom_window_margin); + } + void draw(Snap_Data &d) override { + auto layout = Fluid.proj.layout; + draw_grid(nearest_x, nearest_y, layout->window_grid_x, layout->window_grid_y); + }; +}; +Fd_Snap_Window_Grid snap_window_grid; + +/** + Check if the widget hits group grid coordinates. + */ +class Fd_Snap_Group_Grid : public Fd_Snap_Grid { +public: + void check(Snap_Data &d) override { + if (in_group(d)) { + auto layout = Fluid.proj.layout; + clr(); + Fl_Widget *g = parent(d); + check_grid(d, g->x()+layout->left_group_margin, layout->group_grid_x, g->x()+g->w()-layout->right_group_margin, + g->y()+layout->top_group_margin, layout->group_grid_y, g->y()+g->h()-layout->bottom_group_margin); + } + } + void draw(Snap_Data &d) override { + auto layout = Fluid.proj.layout; + draw_grid(nearest_x, nearest_y, layout->group_grid_x, layout->group_grid_y); + }; +}; +Fd_Snap_Group_Grid snap_group_grid; + +// ----- sibling snapping ---------------------------------------------- MARK: - + +/** + Base class the check distance to other widgets in the same group. + */ +class Fd_Snap_Sibling : public Snap_Action { +protected: + Fl_Widget *best_match; +public: + Fd_Snap_Sibling() : best_match(nullptr) { } + virtual int sibling_check(Snap_Data &d, Fl_Widget *s) = 0; + void check(Snap_Data &d) override { + clr(); + best_match = nullptr; + if (!d.wgt) return; + if (!d.wgt->parent->is_a(Type::Group)) return; + int dsib_min = 1024; + Group_Node *gt = (Group_Node*)d.wgt->parent; + Fl_Group *g = (Fl_Group*)gt->o; + Fl_Widget *w = d.wgt->o; + for (int i=0; ichildren(); i++) { + Fl_Widget *c = g->child(i); + if (c == w) continue; + int sret = sibling_check(d, c); + if (sret < 1) { + int dsib; + if (type==1) + dsib = abs( ((d.by+d.bt)/2+d.dy) - (c->y()+c->h()/2) ); + else + dsib = abs( ((d.bx+d.br)/2+d.dx) - (c->x()+c->w()/2) ); + if (sret == -1 || (dsib < dsib_min)) { + dsib_min = dsib; + best_match = c; + } + } + } + } +}; + +/** + Check if widgets have the same x coordinate, so they can be vertically aligned. + */ +class Fd_Snap_Siblings_Left_Same : public Fd_Snap_Sibling { +public: + Fd_Snap_Siblings_Left_Same() { type = 1; mask = FD_LEFT|FD_DRAG; } + int sibling_check(Snap_Data &d, Fl_Widget *s) override { + return check_x_(d, d.bx, s->x()); + } + void draw(Snap_Data &d) override { + if (best_match) draw_left_brace(best_match); + }; +}; +Fd_Snap_Siblings_Left_Same snap_siblings_left_same; + +/** + Check if widgets touch left to right, or have a user selected gap left to right. + */ +class Fd_Snap_Siblings_Left : public Fd_Snap_Sibling { +public: + Fd_Snap_Siblings_Left() { type = 1; mask = FD_LEFT|FD_DRAG; } + int sibling_check(Snap_Data &d, Fl_Widget *s) override { + return std::min(check_x_(d, d.bx, s->x()+s->w()), + check_x_(d, d.bx, s->x()+s->w()+Fluid.proj.layout->widget_gap_x) ); + } + void draw(Snap_Data &d) override { + if (best_match) draw_right_brace(best_match); + }; +}; +Fd_Snap_Siblings_Left snap_siblings_left; + +class Fd_Snap_Siblings_Right_Same : public Fd_Snap_Sibling { +public: + Fd_Snap_Siblings_Right_Same() { type = 1; mask = FD_RIGHT|FD_DRAG; } + int sibling_check(Snap_Data &d, Fl_Widget *s) override { + return check_x_(d, d.br, s->x()+s->w()); + } + void draw(Snap_Data &d) override { + if (best_match) draw_right_brace(best_match); + }; +}; +Fd_Snap_Siblings_Right_Same snap_siblings_right_same; + +class Fd_Snap_Siblings_Right : public Fd_Snap_Sibling { +public: + Fd_Snap_Siblings_Right() { type = 1; mask = FD_RIGHT|FD_DRAG; } + int sibling_check(Snap_Data &d, Fl_Widget *s) override { + return std::min(check_x_(d, d.br, s->x()), + check_x_(d, d.br, s->x()-Fluid.proj.layout->widget_gap_x)); + } + void draw(Snap_Data &d) override { + if (best_match) draw_left_brace(best_match); + }; +}; +Fd_Snap_Siblings_Right snap_siblings_right; + +class Fd_Snap_Siblings_Top_Same : public Fd_Snap_Sibling { +public: + Fd_Snap_Siblings_Top_Same() { type = 2; mask = FD_TOP|FD_DRAG; } + int sibling_check(Snap_Data &d, Fl_Widget *s) override { + return check_y_(d, d.by, s->y()); + } + void draw(Snap_Data &d) override { + if (best_match) draw_top_brace(best_match); + }; +}; +Fd_Snap_Siblings_Top_Same snap_siblings_top_same; + +class Fd_Snap_Siblings_Top : public Fd_Snap_Sibling { +public: + Fd_Snap_Siblings_Top() { type = 2; mask = FD_TOP|FD_DRAG; } + int sibling_check(Snap_Data &d, Fl_Widget *s) override { + return std::min(check_y_(d, d.by, s->y()+s->h()), + check_y_(d, d.by, s->y()+s->h()+Fluid.proj.layout->widget_gap_y)); + } + void draw(Snap_Data &d) override { + if (best_match) draw_bottom_brace(best_match); + }; +}; +Fd_Snap_Siblings_Top snap_siblings_top; + +class Fd_Snap_Siblings_Bottom_Same : public Fd_Snap_Sibling { +public: + Fd_Snap_Siblings_Bottom_Same() { type = 2; mask = FD_BOTTOM|FD_DRAG; } + int sibling_check(Snap_Data &d, Fl_Widget *s) override { + return check_y_(d, d.bt, s->y()+s->h()); + } + void draw(Snap_Data &d) override { + if (best_match) draw_bottom_brace(best_match); + }; +}; +Fd_Snap_Siblings_Bottom_Same snap_siblings_bottom_same; + +class Fd_Snap_Siblings_Bottom : public Fd_Snap_Sibling { +public: + Fd_Snap_Siblings_Bottom() { type = 2; mask = FD_BOTTOM|FD_DRAG; } + int sibling_check(Snap_Data &d, Fl_Widget *s) override { + return std::min(check_y_(d, d.bt, s->y()), + check_y_(d, d.bt, s->y()-Fluid.proj.layout->widget_gap_y)); + } + void draw(Snap_Data &d) override { + if (best_match) draw_top_brace(best_match); + }; +}; +Fd_Snap_Siblings_Bottom snap_siblings_bottom; + + +// ------ widget snapping ---------------------------------------------- MARK: - + +/** + Snap horizontal resizing to min_w or min_w and a multiple of inc_w. + */ +class Fd_Snap_Widget_Ideal_Width : public Snap_Action { +public: + Fd_Snap_Widget_Ideal_Width() { type = 1; mask = FD_LEFT|FD_RIGHT; } + void check(Snap_Data &d) override { + auto layout = Fluid.proj.layout; + clr(); + if (!d.wgt) return; + int iw = 15, ih = 15; + d.wgt->ideal_size(iw, ih); + if (d.drag == FD_RIGHT) { + check_x_(d, d.br, d.bx+iw); + iw = layout->widget_min_w; + if (iw > 0) iw = nearest(d.br-d.bx+d.dx, layout->widget_min_w, layout->widget_inc_w); + check_x_(d, d.br, d.bx+iw); + } else { + check_x_(d, d.bx, d.br-iw); + iw = layout->widget_min_w; + if (iw > 0) iw = nearest(d.br-d.bx-d.dx, layout->widget_min_w, layout->widget_inc_w); + check_x_(d, d.bx, d.br-iw); + } + } + void draw(Snap_Data &d) override { + draw_width(d.bx, d.bt+7, d.br, 0); + }; +}; +Fd_Snap_Widget_Ideal_Width snap_widget_ideal_width; + +class Fd_Snap_Widget_Ideal_Height : public Snap_Action { +public: + Fd_Snap_Widget_Ideal_Height() { type = 2; mask = FD_TOP|FD_BOTTOM; } + void check(Snap_Data &d) override { + auto layout = Fluid.proj.layout; + clr(); + if (!d.wgt) return; + int iw, ih; + d.wgt->ideal_size(iw, ih); + if (d.drag == FD_BOTTOM) { + check_y_(d, d.bt, d.by+ih); + ih = layout->widget_min_h; + if (ih > 0) ih = nearest(d.bt-d.by+d.dy, layout->widget_min_h, layout->widget_inc_h); + check_y_(d, d.bt, d.by+ih); + } else { + check_y_(d, d.by, d.bt-ih); + ih = layout->widget_min_h; + if (ih > 0) ih = nearest(d.bt-d.by-d.dy, layout->widget_min_h, layout->widget_inc_h); + check_y_(d, d.by, d.bt-ih); + } + } + void draw(Snap_Data &d) override { + draw_height(d.br+7, d.by, d.bt, 0); + }; +}; +Fd_Snap_Widget_Ideal_Height snap_widget_ideal_height; + +// ---- snap actions list ---------------------------------------------- MARK: - + +/** + /brief The list of all snap actions available to FLUID. + New snap actions can be appended to the list. If multiple snap actions + with different coordinates, but the same snap distance are found, the last + action in the list wins. All snap actions with the same distance and same + winning coordinates are drawn in the overlay plane. + */ +Snap_Action *Snap_Action::list[] = { + &snap_left_window_edge, + &snap_right_window_edge, + &snap_top_window_edge, + &snap_bottom_window_edge, + + &snap_left_window_margin, + &snap_right_window_margin, + &snap_top_window_margin, + &snap_bottom_window_margin, + + &snap_window_grid, + &snap_group_grid, + + &snap_left_group_edge, + &snap_right_group_edge, + &snap_top_group_edge, + &snap_bottom_group_edge, + + &snap_left_group_margin, + &snap_right_group_margin, + &snap_top_group_margin, + &snap_bottom_group_margin, + + &snap_top_tabs_margin, + &snap_bottom_tabs_margin, + + &snap_siblings_left_same, &snap_siblings_left, + &snap_siblings_right_same, &snap_siblings_right, + &snap_siblings_top_same, &snap_siblings_top, + &snap_siblings_bottom_same, &snap_siblings_bottom, + + &snap_widget_ideal_width, + &snap_widget_ideal_height, + + nullptr +}; + +// ---- draw alignment marks ------------------------------------------- MARK: - + +static void draw_v_arrow(int x, int y1, int y2) { + int dy = (y1>y2) ? -1 : 1 ; + fl_yxline(x, y1, y2); + fl_xyline(x-4, y2, x+4); + fl_line(x-2, y2-dy*5, x, y2-dy); + fl_line(x+2, y2-dy*5, x, y2-dy); +} + +static void draw_h_arrow(int x1, int y, int x2) { + int dx = (x1>x2) ? -1 : 1 ; + fl_xyline(x1, y, x2); + fl_yxline(x2, y-4, y+4); + fl_line(x2-dx*5, y-2, x2-dx, y); + fl_line(x2-dx*5, y+2, x2-dx, y); +} + +static void draw_top_brace(const Fl_Widget *w) { + int x = w->as_window() ? 0 : w->x(); + int y = w->as_window() ? 0 : w->y(); + fl_yxline(x, y-2, y+6); + fl_yxline(x+w->w()-1, y-2, y+6); + fl_xyline(x-2, y, x+w->w()+1); +} + +static void draw_left_brace(const Fl_Widget *w) { + int x = w->as_window() ? 0 : w->x(); + int y = w->as_window() ? 0 : w->y(); + fl_xyline(x-2, y, x+6); + fl_xyline(x-2, y+w->h()-1, x+6); + fl_yxline(x, y-2, y+w->h()+1); +} + +static void draw_right_brace(const Fl_Widget *w) { + int x = w->as_window() ? w->w() - 1 : w->x() + w->w() - 1; + int y = w->as_window() ? 0 : w->y(); + fl_xyline(x-6, y, x+2); + fl_xyline(x-6, y+w->h()-1, x+2); + fl_yxline(x, y-2, y+w->h()+1); +} + +static void draw_bottom_brace(const Fl_Widget *w) { + int x = w->as_window() ? 0 : w->x(); + int y = w->as_window() ? w->h() - 1 : w->y() + w->h() - 1; + fl_yxline(x, y-6, y+2); + fl_yxline(x+w->w()-1, y-6, y+2); + fl_xyline(x-2, y, x+w->w()+1); +} + +void draw_height(int x, int y, int b, Fl_Align a) { + char buf[16]; + int h = b - y; + sprintf(buf, "%d", h); + fl_font(FL_HELVETICA, 9); + int lw = (int)fl_width(buf); + int lx; + + b --; + if (h < 30) { + // Move height to the side... + if (a == FL_ALIGN_LEFT) lx = x - lw - 2; + else lx = x + 2; + fl_yxline(x, y, b); + } else { + // Put height inside the arrows... + if (a == FL_ALIGN_LEFT) lx = x - lw + 2; + else lx = x - lw / 2; + fl_yxline(x, y, y + (h - 11) / 2); + fl_yxline(x, y + (h + 11) / 2, b); + } + + // Draw the height... + fl_draw(buf, lx, y + (h + 7) / 2); + + // Draw the arrowheads... + fl_line(x-2, y+5, x, y+1, x+2, y+5); + fl_line(x-2, b-5, x, b-1, x+2, b-5); + + // Draw the end lines... + fl_xyline(x - 4, y, x + 4); + fl_xyline(x - 4, b, x + 4); +} + +void draw_width(int x, int y, int r, Fl_Align a) { + char buf[16]; + int w = r-x; + sprintf(buf, "%d", w); + fl_font(FL_HELVETICA, 9); + int lw = (int)fl_width(buf); + int ly = y + 4; + + r--; + + if (lw > (w - 20)) { + // Move width above/below the arrows... + if (a == FL_ALIGN_TOP) ly -= 10; + else ly += 10; + + fl_xyline(x, y, r); + } else { + // Put width inside the arrows... + fl_xyline(x, y, x + (w - lw - 2) / 2); + fl_xyline(x + (w + lw + 2) / 2, y, r); + } + + // Draw the width... + fl_draw(buf, x + (w - lw) / 2, ly-2); + + // Draw the arrowheads... + fl_line(x+5, y-2, x+1, y, x+5, y+2); + fl_line(r-5, y-2, r-1, y, r-5, y+2); + + // Draw the end lines... + fl_yxline(x, y - 4, y + 4); + fl_yxline(r, y - 4, y + 4); +} + +static void draw_grid(int x, int y, int dx, int dy) { + int dx2 = 1, dy2 = 1; + const int n = 2; + for (int i=-n; i<=n; i++) { + for (int j=-n; j<=n; j++) { + if (abs(i)+abs(j) < 4) { + int xx = x + i*dx , yy = y + j*dy; + fl_xyline(xx-dx2, yy, xx+dx2); + fl_yxline(xx, yy-dy2, yy+dy2); + } + } + } +} diff --git a/fluid/app/Snap_Action.h b/fluid/app/Snap_Action.h new file mode 100644 index 000000000..e555f9a44 --- /dev/null +++ b/fluid/app/Snap_Action.h @@ -0,0 +1,207 @@ +// +// Snap action header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 2023-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_FD_SNAP_ACTION_H +#define _FLUID_FD_SNAP_ACTION_H + +#include + +class Window_Node; +class Widget_Node; +struct Fl_Menu_Item; +class Fl_Preferences; + +extern Fl_Menu_Item main_layout_submenu_[]; + +namespace fld { +enum class Tool_Store; // fld:: +namespace io { +class Project_Reader; // fld::io:: +class Project_Writer; // fld::io:: +} // namespace io +} // namespace fld + +namespace fld { +namespace app { + +/** + \brief Collection of layout settings. + + Presets contain default fonts and font sizes for labels and text. They + can be used to guide widget positions using margins, grids, and gap sizes. + There are three Presets available in one Suite, marked "application", + "dialog", and "toolbox". + */ +class Layout_Preset { +public: + int left_window_margin; ///< gap between the window border and the widget + int right_window_margin; + int top_window_margin; + int bottom_window_margin; + int window_grid_x; ///< a regular grid across the window with its origin in the top left window corner + int window_grid_y; + + int left_group_margin; ///< gap between the border of a widget and its parent group + int right_group_margin; + int top_group_margin; + int bottom_group_margin; + int group_grid_x; ///< a regular grid across the group with its origin in the top left group corner + int group_grid_y; + + int top_tabs_margin; ///< preferred top edge tab size inside Fl_Tabs + int bottom_tabs_margin; ///< preferred bottom edge tab size inside Fl_Tabs + + int widget_min_w; ///< minimum widget width + int widget_inc_w; ///< widget width increments starting from widget_min_w + int widget_gap_x; ///< preferred horizontal gap between widgets + int widget_min_h; + int widget_inc_h; + int widget_gap_y; + + int labelfont; ///< preferred font for labels + int labelsize; ///< preferred size for labels + int textfont; ///< preferred font for text elements + int textsize; ///< preferred size for text elements + + void write(Fl_Preferences &prefs); + void read(Fl_Preferences &prefs); + void write(fld::io::Project_Writer*); + void read(fld::io::Project_Reader*); + + int textsize_not_null(); +}; + +extern Layout_Preset *default_layout_preset; + +/** + \brief A collection of layout presets. + + A suite of layout presets is designed to cover various use cases when + designing UI layouts for applications. + There are three Presets available in one Suite, marked "application", + "dialog", and "toolbox". + */ +class Layout_Suite { +public: + char *name_; ///< name of the suite + char *menu_label; ///< label text used in pulldown menu + Layout_Preset *layout[3]; ///< presets for application, dialog, and toolbox windows + fld::Tool_Store storage_; ///< storage location (see fld::Tool_Store::INTERNAL, etc.) + void write(Fl_Preferences &prefs); + void read(Fl_Preferences &prefs); + void write(fld::io::Project_Writer*); + void read(fld::io::Project_Reader*); + void update_label(); + void storage(fld::Tool_Store s) { storage_ = s; update_label(); } + void name(const char *n); + void init(); + ~Layout_Suite(); +public: + +}; + + +/** + \brief Manage all layout suites that are available to the user. + + FLUID has two built-in suites. More suites can be cloned or added and stored + as a user preference, as part of an .fl project file, or in a separate file + for import/export and sharing. + */ +class Layout_List { +public: + Fl_Menu_Item *main_menu_; + Fl_Menu_Item *choice_menu_; + Layout_Suite *list_; + int list_size_; + int list_capacity_; + bool list_is_static_; + int current_suite_; + int current_preset_; + std::string filename_; +public: + Layout_List(); + ~Layout_List(); + void update_dialogs(); + void update_menu_labels(); + int current_suite() const { return current_suite_; } + void current_suite(int ix); + void current_suite(std::string); + int current_preset() const { return current_preset_; } + void current_preset(int ix); + Layout_Suite &operator[](int ix) { return list_[ix]; } + int add(const char *name); + void rename(const char *name); + void capacity(int); + + int load(const std::string &filename); + int save(const std::string &filename); + void write(Fl_Preferences &prefs, fld::Tool_Store storage); + void read(Fl_Preferences &prefs, fld::Tool_Store storage); + void write(fld::io::Project_Writer*); + void read(fld::io::Project_Reader*); + int add(Layout_Suite*); + void remove(int index); + void remove_all(fld::Tool_Store storage); + Layout_Preset *at(int); + int size(); +}; + +/** + \brief Structure holding all the data to perform interactive alignment operations. + */ +typedef struct Snap_Data { + int dx, dy; ///< distance of the mouse from its initial PUSH event + int bx, by, br, bt; ///< bounding box of the original push event or current bounding box when drawing + int drag; ///< drag event mask + int x_dist, y_dist; ///< current closest snapping distance in x and y + int dx_out, dy_out; ///< current closest snapping point as a delta + Widget_Node *wgt; ///< first selected widget + Window_Node *win; ///< window that handles the drag action + int ex_out, ey_out; ///< chosen snap position +} Snap_Data; + +/** + \brief Find points of interest when moving the bounding box of all selected widgets. + */ +class Snap_Action { +protected: + int check_x_(Snap_Data &d, int x_ref, int x_snap); + int check_y_(Snap_Data &d, int y_ref, int y_snap); + void check_x_y_(Snap_Data &d, int x_ref, int x_snap, int y_ref, int y_snap); + void clr() { ex = dx = 0x7fff; } +public: + int ex, ey, dx, dy, type, mask; + Snap_Action() : ex(0x7fff), ey(0x7fff), dx(128), dy(128), type(0), mask(0) { } + virtual ~Snap_Action() { } + virtual void check(Snap_Data &d) = 0; + virtual void draw(Snap_Data &d) { } + virtual bool matches(Snap_Data &d); +public: + static int eex, eey; + static Snap_Action *list[]; + static void check_all(Snap_Data &d); + static void draw_all(Snap_Data &d); + static void get_resize_stepsize(int &x_step, int &y_step); + static void get_move_stepsize(int &x_step, int &y_step); + static void better_size(int &w, int &h); +}; + +} // namespace app +} // namespace fld + + +#endif // _FLUID_FD_SNAP_ACTION_H diff --git a/fluid/app/align_widget.cxx b/fluid/app/align_widget.cxx deleted file mode 100644 index a9badf9e4..000000000 --- a/fluid/app/align_widget.cxx +++ /dev/null @@ -1,414 +0,0 @@ -// -// Alignment code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 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 "app/align_widget.h" - -#include "app/fluid.h" -#include "app/undo.h" -#include "nodes/Fl_Group_Type.h" - -#include -#include - -/** - the first behavior always uses the first selected widget as a reference - the second behavior uses the largest widget (most extreme positions) as - a reference. - */ -#define BREAK_ON_FIRST break -//#define BREAK_ON_FIRST - -void align_widget_cb(Fl_Widget*, long how) -{ - const int max = 32768, min = -32768; - int left, right, top, bot, wdt, hgt, n; - Fl_Type *o; - int changed = 0; - switch ( how ) - { - //---- align - case 10: // align left - left = max; - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - if (w->x()x(); - BREAK_ON_FIRST; - } - if (left!=max) - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Type::allow_layout++; - w->resize(left, w->y(), w->w(), w->h()); - Fl_Type::allow_layout--; - w->redraw(); - if (w->window()) w->window()->redraw(); - } - break; - case 11: // align h.center - left = max; right = min; - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - if (w->x()x(); - if (w->x()+w->w()>right) - right = w->x()+w->w(); - BREAK_ON_FIRST; - } - if (left!=max) - { - int center2 = left+right; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Type::allow_layout++; - w->resize((center2-w->w())/2, w->y(), w->w(), w->h()); - Fl_Type::allow_layout--; - w->redraw(); - if (w->window()) w->window()->redraw(); - } - } - break; - case 12: // align right - right = min; - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - if (w->x()+w->w()>right) - right = w->x()+w->w(); - BREAK_ON_FIRST; - } - if (right!=min) - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Type::allow_layout++; - w->resize(right-w->w(), w->y(), w->w(), w->h()); - Fl_Type::allow_layout--; - w->redraw(); - if (w->window()) w->window()->redraw(); - } - break; - case 13: // align top - top = max; - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - if (w->y()y(); - BREAK_ON_FIRST; - } - if (top!=max) - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Type::allow_layout++; - w->resize(w->x(), top, w->w(), w->h()); - Fl_Type::allow_layout--; - w->redraw(); - if (w->window()) w->window()->redraw(); - } - break; - case 14: // align v.center - top = max; bot = min; - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - if (w->y()y(); - if (w->y()+w->h()>bot) - bot = w->y()+w->h(); - BREAK_ON_FIRST; - } - if (top!=max) - { - int center2 = top+bot; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Type::allow_layout++; - w->resize(w->x(), (center2-w->h())/2, w->w(), w->h()); - Fl_Type::allow_layout--; - w->redraw(); - if (w->window()) w->window()->redraw(); - } - } - break; - case 15: // align bottom - bot = min; - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - if (w->y()+w->h()>bot) - bot = w->y()+w->h(); - BREAK_ON_FIRST; - } - if (bot!=min) - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Type::allow_layout++; - w->resize( w->x(), bot-w->h(), w->w(), w->h()); - Fl_Type::allow_layout--; - w->redraw(); - if (w->window()) w->window()->redraw(); - } - break; - //---- space evenly - case 20: // space evenly across - left = max; right = min; wdt = 0; n = 0; - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - if (w->x()x(); - if (w->x()+w->w()>right) - right = w->x()+w->w(); - wdt += w->w(); - n++; - } - wdt = (right-left)-wdt; - n--; - if (n>0) - { - wdt = wdt/n*n; // make sure that all gaps are the same, possibly moving the rightmost widget - int cnt = 0, wsum = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Type::allow_layout++; - w->resize(left+wsum+wdt*cnt/n, w->y(), w->w(), w->h()); - Fl_Type::allow_layout--; - w->redraw(); - if (w->window()) w->window()->redraw(); - cnt++; - wsum += w->w(); - } - } - break; - case 21: // space evenly down - top = max; bot = min; hgt = 0, n = 0; - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - if (w->y()y(); - if (w->y()+w->h()>bot) - bot = w->y()+w->h(); - hgt += w->h(); - n++; - } - hgt = (bot-top)-hgt; - n--; - if (n>0) - { - hgt = hgt/n*n; // make sure that all gaps are the same, possibly moving the rightmost widget - int cnt = 0, hsum = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Type::allow_layout++; - w->resize(w->x(), top+hsum+hgt*cnt/n, w->w(), w->h()); - Fl_Type::allow_layout--; - w->redraw(); - if (w->window()) w->window()->redraw(); - cnt++; - hsum += w->h(); - } - } - break; - //---- make same size - case 30: // same width - wdt = min; - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - if (w->w()>wdt) - wdt = w->w(); - BREAK_ON_FIRST; - } - if (wdt!=min) - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Type::allow_layout++; - w->resize(w->x(), w->y(), wdt, w->h()); - Fl_Type::allow_layout--; - w->redraw(); - if (w->window()) w->window()->redraw(); - } - break; - case 31: // same height - hgt = min; - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - if (w->h()>hgt) - hgt = w->h(); - BREAK_ON_FIRST; - } - if (hgt!=min) - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Type::allow_layout++; - w->resize( w->x(), w->y(), w->w(), hgt); - Fl_Type::allow_layout--; - w->redraw(); - if (w->window()) w->window()->redraw(); - } - break; - case 32: // same size - hgt = min; wdt = min; - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - if (w->w()>wdt) - wdt = w->w(); - if (w->h()>hgt) - hgt = w->h(); - BREAK_ON_FIRST; - } - if (hgt!=min) - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget()) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Type::allow_layout++; - w->resize( w->x(), w->y(), wdt, hgt); - Fl_Type::allow_layout--; - w->redraw(); - if (w->window()) w->window()->redraw(); - } - break; - //---- center in group - case 40: // center hor - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget() && o->parent) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Widget *p = ((Fl_Widget_Type *)o->parent)->o; - int center2; - - if (w->window() == p) center2 = p->w(); - else center2 = 2*p->x()+p->w(); - - Fl_Type::allow_layout++; - w->resize((center2-w->w())/2, w->y(), w->w(), w->h()); - Fl_Type::allow_layout--; - w->redraw(); - if (w->window()) w->window()->redraw(); - } - break; - case 41: // center vert - for (o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_widget() && o->parent) - { - if (!changed) { - changed = 1; - undo_checkpoint(); - } - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - Fl_Widget *p = ((Fl_Widget_Type *)o->parent)->o; - int center2; - - if (w->window() == p) center2 = p->h(); - else center2 = 2*p->y()+p->h(); - - Fl_Type::allow_layout++; - w->resize(w->x(), (center2-w->h())/2, w->w(), w->h()); - Fl_Type::allow_layout--; - set_modflag(1); - w->redraw(); - if (w->window()) w->window()->redraw(); - } - break; - } - if (changed) - set_modflag(1); -} diff --git a/fluid/app/align_widget.h b/fluid/app/align_widget.h deleted file mode 100644 index f04372215..000000000 --- a/fluid/app/align_widget.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// FLUID main entry for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2021 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_ALIGN_WIDGET_H -#define _FLUID_ALIGN_WIDGET_H - -class Fl_Widget; - -void align_widget_cb(Fl_Widget *, long); - -#endif // _FLUID_ALIGN_WIDGET_H diff --git a/fluid/app/args.cxx b/fluid/app/args.cxx new file mode 100644 index 000000000..b9d958e8c --- /dev/null +++ b/fluid/app/args.cxx @@ -0,0 +1,136 @@ +// +// Command Line Arguments Handling 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 "app/args.h" + +#include "Fluid.h" + +#include +#include +#include + +using namespace fld; +using namespace fld::app; + +/** + Load args from command line into variables. + + \param[in] argc number of arguments in the list + \param[in] argv pointer to an array of arguments + \return 0 if the args were handled successfully, -1 if there was an error + and the usage message was shown. + */ +int Args::load(int argc,char **argv) { + int i = 1; + Fl::args_to_utf8(argc, argv); // for MSYS2/MinGW + if ( (Fl::args(argc,argv,i,arg_cb) == 0) // unsupported argument found + || (Fluid.batch_mode && (i != argc-1)) // .fl filename missing + || (!Fluid.batch_mode && (i < argc-1)) // more than one filename found + || (argv[i] && (argv[i][0] == '-'))) { // unknown option + static const char *msg = + "usage: %s name.fl\n" + " -u : update .fl file and exit (may be combined with '-c' or '-cs')\n" + " -c : write .cxx and .h and exit\n" + " -cs : write .cxx and .h and strings and exit\n" + " -o : .cxx output filename, or extension if starts with '.'\n" + " -h : .h output filename, or extension if starts with '.'\n" + " --help : brief usage information\n" + " --version, -v : print fluid version number\n" + " -d : enable internal debugging\n"; + const char *app_name = nullptr; + if ( (argc > 0) && argv[0] && argv[0][0] ) + app_name = fl_filename_name(argv[0]); + if ( !app_name || !app_name[0]) + app_name = "fluid"; +#ifdef _MSC_VER + // TODO: if this is fluid-cmd, use stderr and not fl_message + fl_message(msg, app_name); +#else + fprintf(stderr, msg, app_name); +#endif + return -1; + } + return i; +} + + +int Args::arg_cb(int argc, char** argv, int& i) { + return Fluid.args.arg(argc, argv, i); +} + + +/** + Handle command line arguments. + \param[in] argc number of arguments in the list + \param[in] argv pointer to an array of arguments + \param[inout] i current argument index + \return number of arguments used; if 0, the argument is not supported + */ +int Args::arg(int argc, char** argv, int& i) { + if (argv[i][0] != '-') + return 0; + if (argv[i][1] == 'd' && !argv[i][2]) { + Fluid.debug_external_editor=1; + i++; return 1; + } + if (argv[i][1] == 'u' && !argv[i][2]) { + update_file++; + Fluid.batch_mode++; + i++; return 1; + } + if (argv[i][1] == 'c' && !argv[i][2]) { + compile_file++; + Fluid.batch_mode++; + i++; return 1; + } + if ((strcmp(argv[i], "-v")==0) || (strcmp(argv[i], "--version")==0)) { + show_version = 1; + i++; return 1; + } + if (argv[i][1] == 'c' && argv[i][2] == 's' && !argv[i][3]) { + compile_file++; + compile_strings++; + Fluid.batch_mode++; + i++; return 1; + } + if (argv[i][1] == 'o' && !argv[i][2] && i+1 < argc) { + code_filename = argv[i+1]; + Fluid.batch_mode++; + i += 2; return 2; + } +#ifndef NDEBUG + if ((i+1 < argc) && (strcmp(argv[i], "--autodoc") == 0)) { + autodoc_path = argv[i+1]; + i += 2; return 2; + } +#endif + if (strcmp(argv[i], "--help")==0) { + return 0; + } + if (argv[i][1] == 'h' && !argv[i][2]) { + if ( (i+1 < argc) && (argv[i+1][0] != '-') ) { + header_filename = argv[i+1]; + Fluid.batch_mode++; + i += 2; + return 2; + } else { + // a lone "-h" without a filename will output the help string + return 0; + } + } + return 0; +} + diff --git a/fluid/app/args.h b/fluid/app/args.h new file mode 100644 index 000000000..80dfa2290 --- /dev/null +++ b/fluid/app/args.h @@ -0,0 +1,55 @@ +// +// Command Line Arguments Handling 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_APP_ARGS_H +#define FLUID_APP_ARGS_H + +#include + +namespace fld { +namespace app { + +class Args { + // Callback. + static int arg_cb(int argc, char** argv, int& i); + // Handle args individually. + int arg(int argc, char** argv, int& i); +public: + /// Set, if Fluid was started with the command line argument -u + int update_file { 0 }; // fluid -u + /// Set, if Fluid was started with the command line argument -c + int compile_file { 0 }; // fluid -c + /// Set, if Fluid was started with the command line argument -cs + int compile_strings { 0 }; // fluid -cs + /// command line arguments that overrides the generate code file extension or name + std::string code_filename { }; // fluid -o filename + /// command line arguments that overrides the generate header file extension or name + std::string header_filename { }; // fluid -h filename + /// if set, generate images for automatic documentation in this directory + std::string autodoc_path { }; // fluid --autodoc path + /// Set, if Fluid was started with the command line argument -v + int show_version { 0 }; // fluid -v + /// Constructor. + Args() = default; + // Load args from command line into variables. + int load(int argc, char **argv); +}; + +} // namespace app +} // namespace fld + +#endif // FLUID_APP_ARGS_H + diff --git a/fluid/app/fluid.cxx b/fluid/app/fluid.cxx deleted file mode 100644 index 91c28d0e6..000000000 --- a/fluid/app/fluid.cxx +++ /dev/null @@ -1,2169 +0,0 @@ -// -// FLUID main entry 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 "app/fluid.h" - -#include "app/project.h" -#include "app/mergeback.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "nodes/Fl_Type.h" -#include "nodes/Fl_Function_Type.h" -#include "nodes/Fl_Group_Type.h" -#include "nodes/Fl_Window_Type.h" -#include "nodes/factory.h" -#include "panels/settings_panel.h" -#include "panels/function_panel.h" -#include "panels/codeview_panel.h" -#include "panels/template_panel.h" -#include "panels/about_panel.h" -#include "rsrcs/pixmaps.h" -#include "app/shell_command.h" -#include "tools/autodoc.h" -#include "widgets/Node_Browser.h" - -#include -#ifdef __APPLE__ -#include // for fl_open_callback -#endif -#include -#include -#include -#include -#include -#include -#include // setlocale().. -#include "../src/flstring.h" - -extern "C" -{ -#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ) -# include -# ifdef HAVE_PNG_H -# include -# else -# include -# endif // HAVE_PNG_H -#endif // HAVE_LIBPNG && HAVE_LIBZ -} - -/// \defgroup globals Fluid Global Variables, Functions and Callbacks -/// \{ - -// -// Globals.. -// - -/// FLUID-wide help dialog. -static Fl_Help_Dialog *help_dialog = NULL; - -/// Main app window menu bar. -Fl_Menu_Bar *main_menubar = NULL; - -/// Main app window. -Fl_Window *main_window; - -/// Fluid application preferences, always accessible, will be flushed when app closes. -Fl_Preferences fluid_prefs(Fl_Preferences::USER_L, "fltk.org", "fluid"); - -/// Show guides in the design window when positioning widgets, saved in app preferences. -int show_guides = 1; - -/// Show areas of restricted use in overlay plane. -/// Restricted areas are widget that overlap each other, widgets that are outside -/// of their parent's bounds (except children of Scroll groups), and areas -/// within an Fl_Tile that are not covered by children. -int show_restricted = 1; - -/// Show a ghosted outline for groups that have very little contrast. -/// This makes groups with NO_BOX or FLAT_BOX better editable. -int show_ghosted_outline = 1; - -/// Show widget comments in the browser, saved in app preferences. -int show_comments = 1; - -/// Use external editor for editing Fl_Code_Type, saved in app preferences. -int G_use_external_editor = 0; - -/// Debugging help for external Fl_Code_Type editor. -int G_debug = 0; - -/// Run this command to load an Fl_Code_Type into an external editor, save in app preferences. -char G_external_editor_command[512]; - - -// File history info... - -/// Stores the absolute filename of the last 10 design files, saved in app preferences. -char absolute_history[10][FL_PATH_MAX]; - -/// This list of filenames is computed from \c absolute_history and displayed in the main menu. -char relative_history[10][FL_PATH_MAX]; - -/// Menuitem to save a .fl design file, will be deactivated if the design is unchanged. -Fl_Menu_Item *save_item = NULL; - -/// First Menuitem that shows the .fl design file history. -Fl_Menu_Item *history_item = NULL; - -/// Menuitem to show or hide the widget bin, label will change if bin is visible. -Fl_Menu_Item *widgetbin_item = NULL; - -/// Menuitem to show or hide the code view, label will change if view is visible. -Fl_Menu_Item *codeview_item = NULL; - -/// Menuitem to show or hide the editing overlay, label will change if overlay visibility changes. -Fl_Menu_Item *overlay_item = NULL; - -/// Menuitem to show or hide the editing guides, label will change if overlay visibility changes. -Fl_Menu_Item *guides_item = NULL; - -/// Menuitem to show or hide the restricted area overlys, label will change if overlay visibility changes. -Fl_Menu_Item *restricted_item = NULL; - -//////////////////////////////////////////////////////////////// - -/// Set if the current design has been modified compared to the associated .fl design file. -int modflag = 0; - -/// Set if the code files are older than the current design. -int modflag_c = 0; - -/// Application work directory, stored here when temporarily changing to the source code directory. -/// \see goto_source_dir() -static std::string app_work_dir; - -/// Used as a counter to set the .fl project dir as the current directory. -/// \see enter_project_dir(), leave_project_dir() -static char in_project_dir = 0; - -/// Set, if Fluid was started with the command line argument -u -int update_file = 0; // fluid -u - -/// Set, if Fluid was started with the command line argument -c -int compile_file = 0; // fluid -c - -/// Set, if Fluid was started with the command line argument -cs -int compile_strings = 0; // fluid -cs - -/// Set, if Fluid was started with the command line argument -v -int show_version = 0; // fluid -v - -/// Set, if Fluid runs in batch mode, and no user interface is activated. -int batch_mode = 0; // if set (-c, -u) don't open display - -/// command line arguments that overrides the generate code file extension or name -std::string g_code_filename_arg; - -/// command line arguments that overrides the generate header file extension or name -std::string g_header_filename_arg; - -/// current directory path at application launch -std::string g_launch_path; - -/// if set, generate images for automatic documentation in this directory -std::string g_autodoc_path; - -/// path to store temporary files during app run -/// \see tmpdir_create_called -std::string tmpdir_path; - -/// true if the temporary file path was already created -/// \see tmpdir_path -bool tmpdir_create_called = false; - - -/// Offset in pixels when adding widgets from an .fl file. -int pasteoffset = 0; - -/// Paste offset incrementing at every paste command. -static int ipasteoffset = 0; - - -/** - Make sure that a path name ends with a forward slash. - \param[in] str directory or path name - \return a new string, ending with a '/' - */ -std::string end_with_slash(const std::string &str) { - char last = str[str.size()-1]; - if (last !='/' && last != '\\') - return str + "/"; - else - return str; -} - -/** - Generate a path to a directory for temporary data storage. - The path is stored in g_tmpdir. - */ -static void create_tmpdir() { - if (tmpdir_create_called) - return; - tmpdir_create_called = true; - - char buf[128]; -#if _WIN32 - // The usual temp file locations on Windows are - // %system%\Windows\Temp - // %userprofiles%\AppData\Local - // usually resolving into - // C:/Windows/Temp/ - // C:\Users\\AppData\Local\Temp - fl_snprintf(buf, sizeof(buf)-1, "fluid-%d/", (long)GetCurrentProcessId()); - std::string name = buf; - wchar_t tempdirW[FL_PATH_MAX+1]; - char tempdir[FL_PATH_MAX+1]; - unsigned len = GetTempPathW(FL_PATH_MAX, tempdirW); - if (len == 0) { - strcpy(tempdir, "c:/windows/temp/"); - } else { - unsigned wn = fl_utf8fromwc(tempdir, FL_PATH_MAX, tempdirW, len); - tempdir[wn] = 0; - } - std::string path = tempdir; - end_with_slash(path); - path += name; - fl_make_path(path.c_str()); - if (fl_access(path.c_str(), 6) == 0) tmpdir_path = path; -#else - fl_snprintf(buf, sizeof(buf)-1, "fluid-%d/", getpid()); - std::string name = buf; - auto path_temp = fl_getenv("TMPDIR"); - std::string path = path_temp ? path_temp : ""; - if (!path.empty()) { - end_with_slash(path); - path += name; - fl_make_path(path.c_str()); - if (fl_access(path.c_str(), 6) == 0) tmpdir_path = path; - } - if (tmpdir_path.empty()) { - path = std::string("/tmp/") + name; - fl_make_path(path.c_str()); - if (fl_access(path.c_str(), 6) == 0) tmpdir_path = path; - } -#endif - if (tmpdir_path.empty()) { - char pbuf[FL_PATH_MAX+1]; - fluid_prefs.get_userdata_path(pbuf, FL_PATH_MAX); - path = std::string(pbuf); - end_with_slash(path); - path += name; - fl_make_path(path.c_str()); - if (fl_access(path.c_str(), 6) == 0) tmpdir_path = path; - } - if (tmpdir_path.empty()) { - if (batch_mode) { - fprintf(stderr, "ERROR: Can't create directory for temporary data storage.\n"); - } else { - fl_alert("Can't create directory for temporary data storage."); - } - } -} - -/** - Delete the temporary directory that was created in set_tmpdir. - */ -static void delete_tmpdir() { - // was a temporary directory created - if (!tmpdir_create_called) - return; - if (tmpdir_path.empty()) - return; - - // first delete all files that may still be left in the temp directory - struct dirent **de; - int n_de = fl_filename_list(tmpdir_path.c_str(), &de); - if (n_de >= 0) { - for (int i=0; id_name; - fl_unlink(path.c_str()); - } - fl_filename_free_list(&de, n_de); - } - - // then delete the directory itself - if (fl_rmdir(tmpdir_path.c_str()) < 0) { - if (batch_mode) { - fprintf(stderr, "WARNING: Can't delete tmpdir '%s': %s", tmpdir_path.c_str(), strerror(errno)); - } else { - fl_alert("WARNING: Can't delete tmpdir '%s': %s", tmpdir_path.c_str(), strerror(errno)); - } - } -} - -/** - Return the path to a temporary directory for this instance of FLUID. - Fluid will do its best to clear and delete this directory when exiting. - \return the path to the temporary directory, ending in a '/', or and empty - string if no directory could be created. - */ -const std::string &get_tmpdir() { - if (!tmpdir_create_called) - create_tmpdir(); - return tmpdir_path; -} - -/** - Give the user the opportunity to save a project before clearing it. - - If the project has unsaved changes, this function pops up a dialog, that - allows the user to save the project, continue without saving the project, - or to cancel the operation. - - If the user chooses to save, and no filename was set, a file dialog allows - the user to pick a name and location, or to cancel the operation. - - \return false if the user aborted the operation and the calling function - should abort as well - */ -bool confirm_project_clear() { - if (modflag == 0) return true; - switch (fl_choice("This project has unsaved changes. Do you want to save\n" - "the project file before proceeding?", - "Cancel", "Save", "Don't Save")) - { - case 0 : /* Cancel */ - return false; - case 1 : /* Save */ - save_cb(NULL, NULL); - if (modflag) return false; // user canceled the "Save As" dialog - } - return true; -} - -// ---- - -extern Fl_Window *the_panel; - -/** - Ensure that text widgets in the widget panel propagates apply current changes. - By temporarily clearing the text focus, all text widgets with changed text - will unfocus and call their respective callbacks, propagating those changes to - their data set. - */ -void flush_text_widgets() { - if (Fl::focus() && (Fl::focus()->top_window() == the_panel)) { - Fl_Widget *old_focus = Fl::focus(); - Fl::focus(NULL); // trigger callback of the widget that is losing focus - Fl::focus(old_focus); - } -} - -// ---- - -/** - 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 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 (!g_project.proj_filename || !*g_project.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(g_project.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 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)); - } -} - -/** - Position the given window window based on entries in the app preferences. - Customisable by user; feature can be switched off. - The window is not shown or hidden by this function, but a value is returned - to indicate the state to the caller. - \param[in] w position this window - \param[in] prefsName name of the preferences item that stores the window settings - \param[in] Visible default value if window is hidden or shown - \param[in] X, Y, W, H default size and position if nothing is specified in the preferences - \return 1 if the caller should make the window visible, 0 if hidden. - */ -char position_window(Fl_Window *w, const char *prefsName, int Visible, int X, int Y, int W, int H) { - Fl_Preferences pos(fluid_prefs, prefsName); - if (prevpos_button->value()) { - pos.get("x", X, X); - pos.get("y", Y, Y); - if ( W!=0 ) { - pos.get("w", W, W); - pos.get("h", H, H); - w->resize( X, Y, W, H ); - } - else - w->position( X, Y ); - } - pos.get("visible", Visible, Visible); - return Visible; -} - -/** - Save the position and visibility state of a window to the app preferences. - \param[in] w save this window data - \param[in] prefsName name of the preferences item that stores the window settings - */ -void save_position(Fl_Window *w, const char *prefsName) { - Fl_Preferences pos(fluid_prefs, prefsName); - pos.set("x", w->x()); - pos.set("y", w->y()); - pos.set("w", w->w()); - pos.set("h", w->h()); - pos.set("visible", (int)(w->shown() && w->visible())); -} - -/** - Return the path and filename of a temporary file for cut or duplicated data. - \param[in] which 0 gets the cut/copy/paste buffer, 1 gets the duplication buffer - \return a pointer to a string in a static buffer - */ -static char* cutfname(int which = 0) { - static char name[2][FL_PATH_MAX]; - static char beenhere = 0; - - if (!beenhere) { - beenhere = 1; - fluid_prefs.getUserdataPath(name[0], sizeof(name[0])); - strlcat(name[0], "cut_buffer", sizeof(name[0])); - fluid_prefs.getUserdataPath(name[1], sizeof(name[1])); - strlcat(name[1], "dup_buffer", sizeof(name[1])); - } - - return name[which]; -} - -/** - Timer to watch for external editor modifications. - - If one or more external editors open, check if their files were modified. - If so: reload to ram, update size/mtime records, and change fluid's - 'modified' state. - */ -static void external_editor_timer(void*) { - int editors_open = ExternalCodeEditor::editors_open(); - if ( G_debug ) printf("--- TIMER --- External editors open=%d\n", editors_open); - if ( editors_open > 0 ) { - // Walk tree looking for files modified by external editors. - int modified = 0; - for (Fl_Type *p = Fl_Type::first; p; p = p->next) { - if ( p->is_a(ID_Code) ) { - Fl_Code_Type *code = (Fl_Code_Type*)p; - // Code changed by external editor? - if ( code->handle_editor_changes() ) { // updates ram, file size/mtime - modified++; - } - if ( code->is_editing() ) { // editor open? - code->reap_editor(); // Try to reap; maybe it recently closed - } - } - } - if ( modified ) set_modflag(1); - } - // Repeat timeout if editors still open - // The ExternalCodeEditor class handles start/stopping timer, we just - // repeat_timeout() if it's already on. NOTE: above code may have reaped - // only open editor, which would disable further timeouts. So *recheck* - // if editors still open, to ensure we don't accidentally re-enable them. - // - if ( ExternalCodeEditor::editors_open() ) { - Fl::repeat_timeout(2.0, external_editor_timer); - } -} - -/** - Save the current design to the file given by \c filename. - If automatic, this overwrites an existing file. If interactive, if will - verify with the user. - \param[in] v if v is not NULL, or no filename is set, open a filechooser. - */ -void save_cb(Fl_Widget *, void *v) { - flush_text_widgets(); - Fl_Native_File_Chooser fnfc; - const char *c = g_project.proj_filename; - if (v || !c || !*c) { - fnfc.title("Save To:"); - fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); - fnfc.filter("FLUID Files\t*.f[ld]"); - if (fnfc.show() != 0) return; - c = fnfc.filename(); - if (!fl_access(c, 0)) { - std::string basename = fl_filename_name_str(std::string(c)); - if (fl_choice("The file \"%s\" already exists.\n" - "Do you want to replace it?", "Cancel", - "Replace", NULL, basename.c_str()) == 0) return; - } - - if (v != (void *)2) set_filename(c); - } - if (!fld::io::write_file(c)) { - fl_alert("Error writing %s: %s", c, strerror(errno)); - return; - } - - if (v != (void *)2) { - set_modflag(0, 1); - undo_save = undo_current; - } -} - -/** - Save a design template. - \todo We should document the concept of templates. - */ -void save_template_cb(Fl_Widget *, void *) { - // Setup the template panel... - if (!template_panel) make_template_panel(); - - template_clear(); - template_browser->add("New Template"); - template_load(); - - template_name->show(); - template_name->value(""); - - template_instance->hide(); - - template_delete->show(); - template_delete->deactivate(); - - template_submit->label("Save"); - template_submit->deactivate(); - - template_panel->label("Save Template"); - - // Show the panel and wait for the user to do something... - template_panel->show(); - while (template_panel->shown()) Fl::wait(); - - // Get the template name, return if it is empty... - const char *c = template_name->value(); - if (!c || !*c) return; - - // Convert template name to filename_with_underscores - char savename[FL_PATH_MAX], *saveptr; - strlcpy(savename, c, sizeof(savename)); - for (saveptr = savename; *saveptr; saveptr ++) { - if (isspace(*saveptr)) *saveptr = '_'; - } - - // Find the templates directory... - char filename[FL_PATH_MAX]; - fluid_prefs.getUserdataPath(filename, sizeof(filename)); - - strlcat(filename, "templates", sizeof(filename)); - if (fl_access(filename, 0)) fl_make_path(filename); - - strlcat(filename, "/", sizeof(filename)); - strlcat(filename, savename, sizeof(filename)); - - char *ext = filename + strlen(filename); - if (ext >= (filename + sizeof(filename) - 5)) { - fl_alert("The template name \"%s\" is too long!", c); - return; - } - - // Save the .fl file... - strcpy(ext, ".fl"); - - if (!fl_access(filename, 0)) { - if (fl_choice("The template \"%s\" already exists.\n" - "Do you want to replace it?", "Cancel", - "Replace", NULL, c) == 0) return; - } - - if (!fld::io::write_file(filename)) { - fl_alert("Error writing %s: %s", filename, strerror(errno)); - return; - } - -#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ) - // Get the screenshot, if any... - Fl_Type *t; - - for (t = Fl_Type::first; t; t = t->next) { - // Find the first window... - if (t->is_a(ID_Window)) break; - } - - if (!t) return; - - // Grab a screenshot... - Fl_Window_Type *wt = (Fl_Window_Type *)t; - uchar *pixels; - int w, h; - - if ((pixels = wt->read_image(w, h)) == NULL) return; - - // Save to a PNG file... - strcpy(ext, ".png"); - - errno = 0; - if (fl_write_png(filename, pixels, w, h, 3) != 0) { - delete[] pixels; - fl_alert("Error writing %s: %s", filename, strerror(errno)); - return; - } - -# if 0 // The original PPM output code... - strcpy(ext, ".ppm"); - fp = fl_fopen(filename, "wb"); - fprintf(fp, "P6\n%d %d 255\n", w, h); - fwrite(pixels, w * h, 3, fp); - fclose(fp); -# endif // 0 - - delete[] pixels; -#endif // HAVE_LIBPNG && HAVE_LIBZ -} - -/** - Reload the file set by \c filename, replacing the current design. - If the design was modified, a dialog will ask for confirmation. - */ -void revert_cb(Fl_Widget *,void *) { - if (modflag) { - if (!fl_choice("This user interface has been changed. Really revert?", - "Cancel", "Revert", NULL)) return; - } - undo_suspend(); - if (!fld::io::read_file(g_project.proj_filename, 0)) { - undo_resume(); - widget_browser->rebuild(); - g_project.update_settings_dialog(); - fl_message("Can't read %s: %s", g_project.proj_filename, strerror(errno)); - return; - } - widget_browser->rebuild(); - undo_resume(); - set_modflag(0, 0); - undo_clear(); - g_project.update_settings_dialog(); -} - -/** - Exit Fluid; we hope you had a nice experience. - If the design was modified, a dialog will ask for confirmation. - */ -void exit_cb(Fl_Widget *,void *) { - if (shell_command_running()) { - int choice = fl_choice("Previous shell command still running!", - "Cancel", - "Exit", - NULL); - if (choice == 0) { // user chose to cancel the exit operation - return; - } - } - - flush_text_widgets(); - - // verify user intention - if (confirm_project_clear() == false) - return; - - // Stop any external editor update timers - ExternalCodeEditor::stop_update_timer(); - - save_position(main_window,"main_window_pos"); - - if (widgetbin_panel) { - save_position(widgetbin_panel,"widgetbin_pos"); - delete widgetbin_panel; - } - if (codeview_panel) { - Fl_Preferences svp(fluid_prefs, "codeview"); - svp.set("autorefresh", cv_autorefresh->value()); - svp.set("autoposition", cv_autoposition->value()); - svp.set("tab", cv_tab->find(cv_tab->value())); - svp.set("code_choice", cv_code_choice); - save_position(codeview_panel,"codeview_pos"); - delete codeview_panel; - codeview_panel = 0; - } - if (shell_run_window) { - save_position(shell_run_window,"shell_run_Window_pos"); - } - - if (about_panel) - delete about_panel; - if (help_dialog) - delete help_dialog; - - if (g_shell_config) - g_shell_config->write(fluid_prefs, FD_STORE_USER); - g_layout_list.write(fluid_prefs, FD_STORE_USER); - - undo_clear(); - - // Destroy tree - // Doing so causes dtors to automatically close all external editors - // and cleans up editor tmp files. Then remove fluid tmpdir /last/. - g_project.reset(); - ExternalCodeEditor::tmpdir_clear(); - delete_tmpdir(); - - exit(0); -} - -/** - Clear the current project and create a new, empty one. - - If the current project was modified, FLUID will give the user the opportunity - to save the old project first. - - \param[in] user_must_confirm if set, a confimation dialog is presented to the - user before resetting the project. Default is `true`. - \return false if the operation was canceled - */ -bool new_project(bool user_must_confirm) { - // verify user intention - if ((user_must_confirm) && (confirm_project_clear() == false)) - return false; - - // clear the current project - g_project.reset(); - set_filename(NULL); - set_modflag(0, 0); - widget_browser->rebuild(); - g_project.update_settings_dialog(); - - // all is clear to continue - return true; -} - -/** - Open the template browser and load a new file from templates. - - If the current project was modified, FLUID will give the user the opportunity - to save the old project first. - - \return false if the operation was canceled or failed otherwise - */ -bool new_project_from_template() { - // clear the current project first - if (new_project() == false) - return false; - - // Setup the template panel... - if (!template_panel) make_template_panel(); - - template_clear(); - template_browser->add("Blank"); - template_load(); - - template_name->hide(); - template_name->value(""); - - template_instance->show(); - template_instance->deactivate(); - template_instance->value(""); - - template_delete->show(); - - template_submit->label("New"); - template_submit->deactivate(); - - template_panel->label("New"); - - //if ( template_browser->size() == 1 ) { // only one item? - template_browser->value(1); // select it - template_browser->do_callback(); - //} - - // Show the panel and wait for the user to do something... - template_panel->show(); - while (template_panel->shown()) Fl::wait(); - - // See if the user chose anything... - int item = template_browser->value(); - if (item < 1) return false; - - // Load the template, if any... - const char *tname = (const char *)template_browser->data(item); - - if (tname) { - // Grab the instance name... - const char *iname = template_instance->value(); - - if (iname && *iname) { - // Copy the template to a temp file, then read it in... - char line[1024], *ptr, *next; - FILE *infile, *outfile; - - if ((infile = fl_fopen(tname, "rb")) == NULL) { - fl_alert("Error reading template file \"%s\":\n%s", tname, - strerror(errno)); - set_modflag(0); - undo_clear(); - return false; - } - - if ((outfile = fl_fopen(cutfname(1), "wb")) == NULL) { - fl_alert("Error writing buffer file \"%s\":\n%s", cutfname(1), - strerror(errno)); - fclose(infile); - set_modflag(0); - undo_clear(); - return false; - } - - while (fgets(line, sizeof(line), infile)) { - // Replace @INSTANCE@ with the instance name... - for (ptr = line; (next = strstr(ptr, "@INSTANCE@")) != NULL; ptr = next + 10) { - fwrite(ptr, next - ptr, 1, outfile); - fputs(iname, outfile); - } - - fputs(ptr, outfile); - } - - fclose(infile); - fclose(outfile); - - undo_suspend(); - fld::io::read_file(cutfname(1), 0); - fl_unlink(cutfname(1)); - undo_resume(); - } else { - // No instance name, so read the template without replacements... - undo_suspend(); - fld::io::read_file(tname, 0); - undo_resume(); - } - } - - widget_browser->rebuild(); - g_project.update_settings_dialog(); - set_modflag(0); - undo_clear(); - - return true; -} - -/** - Open a native file chooser to allow choosing a project file for reading. - - Path and filename are preset with the current project filename, if there - is one. - - \param title a text describing the action after selecting a file (load, merge, ...) - \return the file path and name, or an empty string if the operation was canceled - */ -std::string open_project_filechooser(const std::string &title) { - Fl_Native_File_Chooser dialog; - dialog.title(title.c_str()); - dialog.type(Fl_Native_File_Chooser::BROWSE_FILE); - dialog.filter("FLUID Files\t*.f[ld]\n"); - if (g_project.proj_filename) { - std::string current_project_file = g_project.proj_filename; - dialog.directory(fl_filename_path_str(current_project_file).c_str()); - dialog.preset_file(fl_filename_name_str(current_project_file).c_str()); - } - if (dialog.show() != 0) - return std::string(); - return std::string(dialog.filename()); -} - -/** - Load a project from the give file name and path. - - The project file is inserted at the currently selected type. - - If no filename is given, FLUID will open a file chooser dialog. - - \param[in] filename_arg path and name of the new project file - \return false if the operation failed - */ -bool merge_project_file(const std::string &filename_arg) { - bool is_a_merge = (Fl_Type::first != NULL); - std::string title = is_a_merge ? "Merge Project File" : "Open Project File"; - - // ask for a filename if none was given - std::string new_filename = filename_arg; - if (new_filename.empty()) { - new_filename = open_project_filechooser(title); - if (new_filename.empty()) { - return false; - } - } - - const char *c = new_filename.c_str(); - const char *oldfilename = g_project.proj_filename; - g_project.proj_filename = NULL; - set_filename(c); - if (is_a_merge) undo_checkpoint(); - undo_suspend(); - if (!fld::io::read_file(c, is_a_merge)) { - undo_resume(); - widget_browser->rebuild(); - g_project.update_settings_dialog(); - fl_message("Can't read %s: %s", c, strerror(errno)); - free((void *)g_project.proj_filename); - g_project.proj_filename = oldfilename; - if (main_window) set_modflag(modflag); - return false; - } - undo_resume(); - widget_browser->rebuild(); - if (is_a_merge) { - // Inserting a file; restore the original filename... - set_filename(oldfilename); - set_modflag(1); - } else { - // Loaded a file; free the old filename... - set_modflag(0, 0); - undo_clear(); - } - if (oldfilename) free((void *)oldfilename); - return true; -} - -/** - Open a file chooser and load an exiting project file. - - If the current project was modified, FLUID will give the user the opportunity - to save the old project first. - - If no filename is given, FLUID will open a file chooser dialog. - - \param[in] filename_arg load from this file, or show file chooser if empty - \return false if the operation was canceled or failed otherwise - */ -bool open_project_file(const std::string &filename_arg) { - // verify user intention - if (confirm_project_clear() == false) - return false; - - // ask for a filename if none was given - std::string new_filename = filename_arg; - if (new_filename.empty()) { - new_filename = open_project_filechooser("Open Project File"); - if (new_filename.empty()) { - return false; - } - } - - // clear the project and merge a file by the given name - new_project(false); - return merge_project_file(new_filename); -} - -#ifdef __APPLE__ -/** - Handle app launch with an associated filename (macOS only). - Should there be a modified design already, Fluid asks for user confirmation. - \param[in] c the filename of the new design - */ -void apple_open_cb(const char *c) { - open_project_file(std::string(c)); -} -#endif // __APPLE__ - -/** - Generate the C++ source and header filenames and write those files. - - This function creates the source filename by setting the file - extension to \c code_file_name and a header filename - with the extension \c code_file_name which are both - settable by the user. - - If the code filename has not been set yet, a "save file as" dialog will be - presented to the user. - - In batch_mode, the function will either be silent, or, if opening or writing - the files fails, write an error message to \c stderr and exit with exit code 1. - - In interactive mode, it will pop up an error message, or, if the user - hasn't disabled that, pop up a confirmation message. - - \param[in] dont_show_completion_dialog don't show the completion dialog - \return 1 if the operation failed, 0 if it succeeded - */ -int write_code_files(bool dont_show_completion_dialog) -{ - // -- handle user interface issues - flush_text_widgets(); - if (!g_project.proj_filename) { - save_cb(0,0); - if (!g_project.proj_filename) return 1; - } - - // -- generate the file names with absolute paths - fld::io::Code_Writer f; - std::string code_filename = g_project.codefile_path() + g_project.codefile_name(); - std::string header_filename = g_project.headerfile_path() + g_project.headerfile_name(); - - // -- write the code and header files - if (!batch_mode) enter_project_dir(); - int x = f.write_code(code_filename.c_str(), header_filename.c_str()); - std::string code_filename_rel = fl_filename_relative_str(code_filename); - std::string header_filename_rel = fl_filename_relative_str(header_filename); - if (!batch_mode) leave_project_dir(); - - // -- print error message in batch mode or pop up an error or confirmation dialog box - if (batch_mode) { - if (!x) { - fprintf(stderr, "%s and %s: %s\n", - code_filename_rel.c_str(), - header_filename_rel.c_str(), - strerror(errno)); - exit(1); - } - } else { - if (!x) { - fl_message("Can't write %s or %s: %s", - code_filename_rel.c_str(), - header_filename_rel.c_str(), - strerror(errno)); - } else { - set_modflag(-1, 0); - if (dont_show_completion_dialog==false && completion_button->value()) { - fl_message("Wrote %s and %s", - code_filename_rel.c_str(), - header_filename_rel.c_str()); - } - } - } - return 0; -} - -/** - Callback to write C++ code and header files. - */ -void write_cb(Fl_Widget *, void *) { - write_code_files(); -} - -#if 0 -// Matt: disabled -/** - Merge the possibly modified content of code files back into the project. - */ -int mergeback_code_files() -{ - flush_text_widgets(); - if (!filename) return 1; - if (!g_project.write_mergeback_data) { - fl_message("MergeBack is not enabled for this project.\n" - "Please enable MergeBack in the project settings\n" - "dialog and re-save the project file and the code."); - return 0; - } - - std::string proj_filename = g_project.projectfile_path() + g_project.projectfile_name(); - std::string code_filename; -#if 1 - if (!batch_mode) { - Fl_Preferences build_records(Fl_Preferences::USER_L, "fltk.org", "fluid-build"); - Fl_Preferences path(build_records, proj_filename.c_str()); - int i, n = proj_filename.size(); - for (i=0; ivalue()) { - fl_message("Wrote %s", g_project.stringsfile_name().c_str()); - } - } -} - -/** - Show the editor for the \c current Fl_Type. - */ -void openwidget_cb(Fl_Widget *, void *) { - if (!Fl_Type::current) { - fl_message("Please select a widget"); - return; - } - Fl_Type::current->open(); -} - -/** - User chose to copy the currently selected widgets. - */ -void copy_cb(Fl_Widget*, void*) { - flush_text_widgets(); - if (!Fl_Type::current) { - fl_beep(); - return; - } - flush_text_widgets(); - ipasteoffset = 10; - if (!fld::io::write_file(cutfname(),1)) { - fl_message("Can't write %s: %s", cutfname(), strerror(errno)); - return; - } -} - -/** - User chose to cut the currently selected widgets. - */ -void cut_cb(Fl_Widget *, void *) { - if (!Fl_Type::current) { - fl_beep(); - return; - } - flush_text_widgets(); - if (!fld::io::write_file(cutfname(),1)) { - fl_message("Can't write %s: %s", cutfname(), strerror(errno)); - return; - } - undo_checkpoint(); - set_modflag(1); - ipasteoffset = 0; - Fl_Type *p = Fl_Type::current->parent; - while (p && p->selected) p = p->parent; - delete_all(1); - if (p) select_only(p); - widget_browser->rebuild(); -} - -/** - User chose to delete the currently selected widgets. - */ -void delete_cb(Fl_Widget *, void *) { - if (!Fl_Type::current) { - fl_beep(); - return; - } - undo_checkpoint(); - set_modflag(1); - ipasteoffset = 0; - Fl_Type *p = Fl_Type::current->parent; - while (p && p->selected) p = p->parent; - delete_all(1); - if (p) select_only(p); - widget_browser->rebuild(); -} - -/** - User chose to paste the widgets from the cut buffer. - - This function will paste the widgets in the cut buffer after the currently - selected widget. If the currently selected widget is a group widget and - it is not folded, the new widgets will be added inside the group. - */ -void paste_cb(Fl_Widget*, void*) { - pasteoffset = ipasteoffset; - undo_checkpoint(); - undo_suspend(); - Strategy strategy = Strategy::FROM_FILE_AFTER_CURRENT; - if (Fl_Type::current && Fl_Type::current->can_have_children()) { - if (Fl_Type::current->folded_ == 0) { - // If the current widget is a group widget and it is not folded, - // add the new widgets inside the group. - strategy = Strategy::FROM_FILE_AS_LAST_CHILD; - // The following alternative also works quite nicely - //strategy = Strategy::FROM_FILE_AS_FIRST_CHILD; - } - } - if (!fld::io::read_file(cutfname(), 1, strategy)) { - widget_browser->rebuild(); - fl_message("Can't read %s: %s", cutfname(), strerror(errno)); - } - undo_resume(); - widget_browser->display(Fl_Type::current); - widget_browser->rebuild(); - pasteoffset = 0; - ipasteoffset += 10; -} - -/** - Duplicate the selected widgets. - - This code is a bit complex because it needs to find the last selected - widget with the lowest level, so that the new widgets are inserted after - this one. - */ -void duplicate_cb(Fl_Widget*, void*) { - if (!Fl_Type::current) { - fl_beep(); - return; - } - - // flush the text widgets to make sure the user's changes are saved: - flush_text_widgets(); - - // find the last selected node with the lowest level: - int lowest_level = 9999; - Fl_Type *new_insert = NULL; - if (Fl_Type::current->selected) { - for (Fl_Type *t = Fl_Type::first; t; t = t->next) { - if (t->selected && (t->level <= lowest_level)) { - lowest_level = t->level; - new_insert = t; - } - } - } - if (new_insert) - Fl_Type::current = new_insert; - - // write the selected widgets to a file: - if (!fld::io::write_file(cutfname(1),1)) { - fl_message("Can't write %s: %s", cutfname(1), strerror(errno)); - return; - } - - // read the file and add the widgets after the current one: - pasteoffset = 0; - undo_checkpoint(); - undo_suspend(); - if (!fld::io::read_file(cutfname(1), 1, Strategy::FROM_FILE_AFTER_CURRENT)) { - fl_message("Can't read %s: %s", cutfname(1), strerror(errno)); - } - fl_unlink(cutfname(1)); - widget_browser->display(Fl_Type::current); - widget_browser->rebuild(); - undo_resume(); -} - -/** - User wants to sort selected widgets by y coordinate. - */ -static void sort_cb(Fl_Widget *,void *) { - undo_checkpoint(); - sort((Fl_Type*)NULL); - widget_browser->rebuild(); - set_modflag(1); -} - -/** - Open the "About" dialog. - */ -void about_cb(Fl_Widget *, void *) { - if (!about_panel) make_about_panel(); - about_panel->show(); -} - -/** - Open a dialog to show the HTML help page form the FLTK documentation folder. - \param[in] name name of the HTML help file. - */ -void show_help(const char *name) { - const char *docdir; - char helpname[FL_PATH_MAX]; - - if (!help_dialog) help_dialog = new Fl_Help_Dialog(); - - if ((docdir = fl_getenv("FLTK_DOCDIR")) == NULL) { - docdir = FLTK_DOCDIR; - } - snprintf(helpname, sizeof(helpname), "%s/%s", docdir, name); - - // make sure that we can read the file - FILE *f = fopen(helpname, "rb"); - if (f) { - fclose(f); - help_dialog->load(helpname); - } else { - // if we can not read the file, we display the canned version instead - // or ask the native browser to open the page on www.fltk.org - if (strcmp(name, "fluid.html")==0) { - if (!Fl_Shared_Image::find("embedded:/fluid_flow_chart_800.png")) - new Fl_PNG_Image("embedded:/fluid_flow_chart_800.png", fluid_flow_chart_800_png, sizeof(fluid_flow_chart_800_png)); - help_dialog->value - ( - "\n" - "FLTK: Programming with FLUID\n" - "

What is FLUID?

\n" - "The Fast Light User Interface Designer, or FLUID, is a graphical editor " - "that is used to produce FLTK source code. FLUID edits and saves its state " - "in .fl files. These files are text, and you can (with care) " - "edit them in a text editor, perhaps to get some special effects.

\n" - "FLUID can \"compile\" the .fl file into a .cxx " - "and a .h file. The .cxx file defines all the " - "objects from the .fl file and the .h file " - "declares all the global ones. FLUID also supports localization " - "(Internationalization) of label strings using message files and the GNU " - "gettext or POSIX catgets interfaces.

\n" - "A simple program can be made by putting all your code (including a " - "main() function) into the .fl file and thus making the " - ".cxx file a single source file to compile. Most programs are " - "more complex than this, so you write other .cxx files that " - "call the FLUID functions. These .cxx files must " - "#include the .h file or they can #include " - "the .cxx file so it still appears to be a single source file.

" - "

" - "

More information is available online at https://www.fltk.org/" - "" - ); - } else if (strcmp(name, "license.html")==0) { - fl_open_uri("https://www.fltk.org/doc-1.5/license.html"); - return; - } else if (strcmp(name, "index.html")==0) { - fl_open_uri("https://www.fltk.org/doc-1.5/index.html"); - return; - } else { - snprintf(helpname, sizeof(helpname), "https://www.fltk.org/%s", name); - fl_open_uri(helpname); - return; - } - } - help_dialog->show(); -} - -/** - User wants help on Fluid. - */ -void help_cb(Fl_Widget *, void *) { - show_help("fluid.html"); -} - -/** - User wants to see the Fluid manual. - */ -void manual_cb(Fl_Widget *, void *) { - show_help("index.html"); -} - -// ---- Printing - -/** - Open the dialog to allow the user to print the current window. - */ -void print_menu_cb(Fl_Widget *, void *) { - int w, h, ww, hh; - int frompage, topage; - Fl_Type *t; // Current widget - int num_windows; // Number of windows - Fl_Window_Type *windows[1000]; // Windows to print - int winpage; // Current window page - Fl_Window *win; - - for (t = Fl_Type::first, num_windows = 0; t; t = t->next) { - if (t->is_a(ID_Window)) { - windows[num_windows] = (Fl_Window_Type *)t; - if (!((Fl_Window*)(windows[num_windows]->o))->shown()) continue; - num_windows ++; - } - } - - Fl_Printer printjob; - if ( printjob.start_job(num_windows, &frompage, &topage) ) return; - int pagecount = 0; - for (winpage = 0; winpage < num_windows; winpage++) { - float scale = 1, scale_x = 1, scale_y = 1; - if (winpage+1 < frompage || winpage+1 > topage) continue; - printjob.start_page(); - printjob.printable_rect(&w, &h); - - // Get the time and date... - time_t curtime = time(NULL); - struct tm *curdate = localtime(&curtime); - char date[1024]; - strftime(date, sizeof(date), "%c", curdate); - fl_font(FL_HELVETICA, 12); - fl_color(FL_BLACK); - fl_draw(date, (w - (int)fl_width(date))/2, fl_height()); - sprintf(date, "%d/%d", ++pagecount, topage-frompage+1); - fl_draw(date, w - (int)fl_width(date), fl_height()); - - // Get the base filename... - std::string basename = fl_filename_name_str(std::string(g_project.proj_filename)); - fl_draw(basename.c_str(), 0, fl_height()); - - // print centered and scaled to fit in the page - win = (Fl_Window*)windows[winpage]->o; - ww = win->decorated_w(); - if(ww > w) scale_x = float(w)/ww; - hh = win->decorated_h(); - if(hh > h) scale_y = float(h)/hh; - if (scale_x < scale) scale = scale_x; - if (scale_y < scale) scale = scale_y; - if (scale < 1) { - printjob.scale(scale); - printjob.printable_rect(&w, &h); - } - printjob.origin(w/2, h/2); - printjob.print_window(win, -ww/2, -hh/2); - printjob.end_page(); - } - printjob.end_job(); -} - -// ---- Main menu bar - -extern void select_layout_preset_cb(Fl_Widget *, void *user_data); -extern void layout_suite_marker(Fl_Widget *, void *user_data); - -static void menu_file_new_cb(Fl_Widget *, void *) { new_project(); } -static void menu_file_new_from_template_cb(Fl_Widget *, void *) { new_project_from_template(); } -static void menu_file_open_cb(Fl_Widget *, void *) { open_project_file(""); } -static void menu_file_insert_cb(Fl_Widget *, void *) { merge_project_file(""); } -static void menu_file_open_history_cb(Fl_Widget *, void *v) { open_project_file(std::string((const char*)v)); } -static void menu_layout_sync_resize_cb(Fl_Menu_ *m, void*) { - if (m->mvalue()->value()) Fl_Type::allow_layout = 1; else Fl_Type::allow_layout = 0; } -/** - This is the main Fluid menu. - - Design history is manipulated right inside this menu structure. - Some menu items change or deactivate correctly, but most items just trigger - various callbacks. - - \c New_Menu creates new widgets and is explained in detail in another location. - - \see New_Menu - \todo This menu needs some major modernization. Menus are too long and their - sorting is not always obvious. - \todo Shortcuts are all over the place (Alt, Ctrl, Command, Shift-Ctrl, - function keys), and there should be a help page listing all shortcuts. - */ -Fl_Menu_Item Main_Menu[] = { -{"&File",0,0,0,FL_SUBMENU}, - {"&New", FL_COMMAND+'n', menu_file_new_cb}, - {"&Open...", FL_COMMAND+'o', menu_file_open_cb}, - {"&Insert...", FL_COMMAND+'i', menu_file_insert_cb, 0, FL_MENU_DIVIDER}, - {"&Save", FL_COMMAND+'s', save_cb, 0}, - {"Save &As...", FL_COMMAND+FL_SHIFT+'s', save_cb, (void*)1}, - {"Sa&ve A Copy...", 0, save_cb, (void*)2}, - {"&Revert...", 0, revert_cb, 0, FL_MENU_DIVIDER}, - {"New &From Template...", FL_COMMAND+'N', menu_file_new_from_template_cb, 0}, - {"Save As &Template...", 0, save_template_cb, 0, FL_MENU_DIVIDER}, - {"&Print...", FL_COMMAND+'p', print_menu_cb}, - {"Write &Code", FL_COMMAND+FL_SHIFT+'c', write_cb, 0}, -// Matt: disabled {"MergeBack Code", FL_COMMAND+FL_SHIFT+'m', mergeback_cb, 0}, - {"&Write Strings", FL_COMMAND+FL_SHIFT+'w', write_strings_cb, 0, FL_MENU_DIVIDER}, - {relative_history[0], FL_COMMAND+'1', menu_file_open_history_cb, absolute_history[0]}, - {relative_history[1], FL_COMMAND+'2', menu_file_open_history_cb, absolute_history[1]}, - {relative_history[2], FL_COMMAND+'3', menu_file_open_history_cb, absolute_history[2]}, - {relative_history[3], FL_COMMAND+'4', menu_file_open_history_cb, absolute_history[3]}, - {relative_history[4], FL_COMMAND+'5', menu_file_open_history_cb, absolute_history[4]}, - {relative_history[5], FL_COMMAND+'6', menu_file_open_history_cb, absolute_history[5]}, - {relative_history[6], FL_COMMAND+'7', menu_file_open_history_cb, absolute_history[6]}, - {relative_history[7], FL_COMMAND+'8', menu_file_open_history_cb, absolute_history[7]}, - {relative_history[8], FL_COMMAND+'9', menu_file_open_history_cb, absolute_history[8]}, - {relative_history[9], 0, menu_file_open_history_cb, absolute_history[9], FL_MENU_DIVIDER}, - {"&Quit", FL_COMMAND+'q', exit_cb}, - {0}, -{"&Edit",0,0,0,FL_SUBMENU}, - {"&Undo", FL_COMMAND+'z', undo_cb}, - {"&Redo", FL_COMMAND+FL_SHIFT+'z', redo_cb, 0, FL_MENU_DIVIDER}, - {"C&ut", FL_COMMAND+'x', cut_cb}, - {"&Copy", FL_COMMAND+'c', copy_cb}, - {"&Paste", FL_COMMAND+'v', paste_cb}, - {"Dup&licate", FL_COMMAND+'u', duplicate_cb}, - {"&Delete", FL_Delete, delete_cb, 0, FL_MENU_DIVIDER}, - {"Select &All", FL_COMMAND+'a', select_all_cb}, - {"Select &None", FL_COMMAND+FL_SHIFT+'a', select_none_cb, 0, FL_MENU_DIVIDER}, - {"Pr&operties...", FL_F+1, openwidget_cb}, - {"&Sort",0,sort_cb}, - {"&Earlier", FL_F+2, earlier_cb}, - {"&Later", FL_F+3, later_cb}, - {"&Group", FL_F+7, group_cb}, - {"Ung&roup", FL_F+8, ungroup_cb,0, FL_MENU_DIVIDER}, - {"Hide O&verlays",FL_COMMAND+FL_SHIFT+'o',toggle_overlays}, - {"Hide Guides",FL_COMMAND+FL_SHIFT+'g',toggle_guides}, - {"Hide Restricted",FL_COMMAND+FL_SHIFT+'r',toggle_restricted}, - {"Show Widget &Bin...",FL_ALT+'b',toggle_widgetbin_cb}, - {"Show Code View",FL_ALT+'c', (Fl_Callback*)toggle_codeview_cb, 0, FL_MENU_DIVIDER}, - {"Settings...",FL_ALT+'p',show_settings_cb}, - {0}, -{"&New", 0, 0, (void *)New_Menu, FL_SUBMENU_POINTER}, -{"&Layout",0,0,0,FL_SUBMENU}, - {"&Align",0,0,0,FL_SUBMENU}, - {"&Left",0,(Fl_Callback *)align_widget_cb,(void*)10}, - {"&Center",0,(Fl_Callback *)align_widget_cb,(void*)11}, - {"&Right",0,(Fl_Callback *)align_widget_cb,(void*)12}, - {"&Top",0,(Fl_Callback *)align_widget_cb,(void*)13}, - {"&Middle",0,(Fl_Callback *)align_widget_cb,(void*)14}, - {"&Bottom",0,(Fl_Callback *)align_widget_cb,(void*)15}, - {0}, - {"&Space Evenly",0,0,0,FL_SUBMENU}, - {"&Across",0,(Fl_Callback *)align_widget_cb,(void*)20}, - {"&Down",0,(Fl_Callback *)align_widget_cb,(void*)21}, - {0}, - {"&Make Same Size",0,0,0,FL_SUBMENU}, - {"&Width",0,(Fl_Callback *)align_widget_cb,(void*)30}, - {"&Height",0,(Fl_Callback *)align_widget_cb,(void*)31}, - {"&Both",0,(Fl_Callback *)align_widget_cb,(void*)32}, - {0}, - {"&Center In Group",0,0,0,FL_SUBMENU}, - {"&Horizontal",0,(Fl_Callback *)align_widget_cb,(void*)40}, - {"&Vertical",0,(Fl_Callback *)align_widget_cb,(void*)41}, - {0}, - {"Synchronized Resize", 0, (Fl_Callback*)menu_layout_sync_resize_cb, NULL, FL_MENU_TOGGLE|FL_MENU_DIVIDER }, - {"&Grid and Size Settings...",FL_COMMAND+'g',show_grid_cb, NULL, FL_MENU_DIVIDER}, - {"Presets", 0, layout_suite_marker, (void*)main_layout_submenu_, FL_SUBMENU_POINTER }, - {"Application", 0, select_layout_preset_cb, (void*)0, FL_MENU_RADIO|FL_MENU_VALUE }, - {"Dialog", 0, select_layout_preset_cb, (void*)1, FL_MENU_RADIO }, - {"Toolbox", 0, select_layout_preset_cb, (void*)2, FL_MENU_RADIO }, - {0}, -{"&Shell", 0, Fd_Shell_Command_List::menu_marker, (void*)Fd_Shell_Command_List::default_menu, FL_SUBMENU_POINTER}, -{"&Help",0,0,0,FL_SUBMENU}, - {"&Rapid development with FLUID...",0,help_cb}, - {"&FLTK Programmers Manual...",0,manual_cb, 0, FL_MENU_DIVIDER}, - {"&About FLUID...",0,about_cb}, - {0}, -{0}}; - -/** - Change the app's and hence preview the design's scheme. - - The scheme setting is stored in the app preferences - - in key \p 'scheme_name' since 1.4.0 - - in key \p 'scheme' (index: 0 - 4) in 1.3.x - - This callback is triggered by changing the scheme in the - Fl_Scheme_Choice widget (\p Edit/GUI Settings). - - \param[in] choice the calling widget - - \see init_scheme() for choice values and backwards compatibility - */ -void scheme_cb(Fl_Scheme_Choice *choice, void *) { - if (batch_mode) - return; - - // set the new scheme only if the scheme was changed - const char *new_scheme = choice->text(choice->value()); - - if (Fl::is_scheme(new_scheme)) - return; - - Fl::scheme(new_scheme); - fluid_prefs.set("scheme_name", new_scheme); - - // Backwards compatibility: store 1.3 scheme index (1-4). - // We assume that index 0-3 (base, plastic, gtk+, gleam) are in the - // same order as in 1.3.x (index 1-4), higher values are ignored - - int scheme_index = scheme_choice->value(); - if (scheme_index <= 3) // max. index for 1.3.x (Gleam) - fluid_prefs.set("scheme", scheme_index + 1); // compensate for different indexing -} - -/** - Read Fluid's scheme preferences and set the app's scheme. - - Since FLTK 1.4.0 the scheme \b name is stored as a character string - with key "scheme_name" in the preference database. - - In FLTK 1.3.x the scheme preference was stored as an integer index - with key "scheme" in the database. The known schemes were hardcoded in - Fluid's sources (here for reference): - - | Index | 1.3 Scheme Name | Choice | 1.4 Scheme Name | - |-------|-----------------|-------|-----------------| - | 0 | Default (same as None) | n/a | n/a | - | 1 | None (same as Default) | 0 | base | - | 2 | Plastic | 1 | plastic | - | 3 | GTK+ | 2 | gtk+ | - | 4 | Gleam | 3 | gleam | - | n/a | n/a | 4 | oxy | - - The new Fluid tries to keep backwards compatibility and reads both - keys (\p scheme and \p scheme_name). If the latter is defined, it is used. - If not the old \p scheme (index) is used - but we need to subtract one to - get the new Fl_Scheme_Choice index (column "Choice" above). -*/ -void init_scheme() { - int scheme_index = 0; // scheme index for backwards compatibility (1.3.x) - char *scheme_name = 0; // scheme name since 1.4.0 - fluid_prefs.get("scheme_name", scheme_name, "XXX"); // XXX means: not set => fallback 1.3.x - if (!strcmp(scheme_name, "XXX")) { - fluid_prefs.get("scheme", scheme_index, 0); - if (scheme_index > 0) { - scheme_index--; - scheme_choice->value(scheme_index); // set the choice value - } - if (scheme_index < 0) - scheme_index = 0; - else if (scheme_index > scheme_choice->size() - 1) - scheme_index = 0; - scheme_name = const_cast(scheme_choice->text(scheme_index)); - fluid_prefs.set("scheme_name", scheme_name); - } - // Set the new scheme only if it was not overridden by the -scheme - // command line option - if (Fl::scheme() == NULL) { - Fl::scheme(scheme_name); - } - free(scheme_name); -} - -/** - Show or hide the widget bin. - The state is stored in the app preferences. - */ -void toggle_widgetbin_cb(Fl_Widget *, void *) { - if (!widgetbin_panel) { - make_widgetbin(); - if (!position_window(widgetbin_panel,"widgetbin_pos", 1, 320, 30)) return; - } - - if (widgetbin_panel->visible()) { - widgetbin_panel->hide(); - widgetbin_item->label("Show Widget &Bin..."); - } else { - widgetbin_panel->show(); - widgetbin_item->label("Hide Widget &Bin"); - } -} - -/** - Show or hide the code preview window. - */ -void toggle_codeview_cb(Fl_Double_Window *, void *) { - codeview_toggle_visibility(); -} - -/** - Show or hide the code preview window, button callback. - */ -void toggle_codeview_b_cb(Fl_Button*, void *) { - codeview_toggle_visibility(); -} - -/** - Build the main app window and create a few other dialogs. - */ -void make_main_window() { - if (!batch_mode) { - fluid_prefs.get("show_guides", show_guides, 1); - fluid_prefs.get("show_restricted", show_restricted, 1); - fluid_prefs.get("show_ghosted_outline", show_ghosted_outline, 0); - fluid_prefs.get("show_comments", show_comments, 1); - make_shell_window(); - } - - if (!main_window) { - Fl_Widget *o; - loadPixmaps(); - main_window = new Fl_Double_Window(WINWIDTH,WINHEIGHT,"fluid"); - main_window->box(FL_NO_BOX); - o = make_widget_browser(0,MENUHEIGHT,BROWSERWIDTH,BROWSERHEIGHT); - o->box(FL_FLAT_BOX); - o->tooltip("Double-click to view or change an item."); - main_window->resizable(o); - main_menubar = new Fl_Menu_Bar(0,0,BROWSERWIDTH,MENUHEIGHT); - main_menubar->menu(Main_Menu); - // quick access to all dynamic menu items - save_item = (Fl_Menu_Item*)main_menubar->find_item(save_cb); - history_item = (Fl_Menu_Item*)main_menubar->find_item(menu_file_open_history_cb); - widgetbin_item = (Fl_Menu_Item*)main_menubar->find_item(toggle_widgetbin_cb); - codeview_item = (Fl_Menu_Item*)main_menubar->find_item((Fl_Callback*)toggle_codeview_cb); - overlay_item = (Fl_Menu_Item*)main_menubar->find_item((Fl_Callback*)toggle_overlays); - guides_item = (Fl_Menu_Item*)main_menubar->find_item((Fl_Callback*)toggle_guides); - restricted_item = (Fl_Menu_Item*)main_menubar->find_item((Fl_Callback*)toggle_restricted); - main_menubar->global(); - fill_in_New_Menu(); - main_window->end(); - } - - if (!batch_mode) { - load_history(); - g_shell_config = new Fd_Shell_Command_List; - widget_browser->load_prefs(); - make_settings_window(); - } -} - -/** - Load file history from preferences. - - This loads the absolute filepaths of the last 10 used design files. - It also computes and stores the relative filepaths for display in - the main menu. - */ -void load_history() { - int i; // Looping var - int max_files; - - fluid_prefs.get("recent_files", max_files, 5); - if (max_files > 10) max_files = 10; - - for (i = 0; i < max_files; i ++) { - fluid_prefs.get( Fl_Preferences::Name("file%d", i), absolute_history[i], "", sizeof(absolute_history[i])); - if (absolute_history[i][0]) { - // Make a shortened version of the filename for the menu... - std::string fn = fl_filename_shortened(absolute_history[i], 48); - strncpy(relative_history[i], fn.c_str(), sizeof(relative_history[i]) - 1); - if (i == 9) history_item[i].flags = FL_MENU_DIVIDER; - else history_item[i].flags = 0; - } else break; - } - - for (; i < 10; i ++) { - if (i) history_item[i-1].flags |= FL_MENU_DIVIDER; - history_item[i].hide(); - } -} - -/** - Update file history from preferences. - - Add this new filepath to the history and update the main menu. - Writes the new file history to the app preferences. - - \param[in] flname path or filename of .fl file, will be converted into an - absolute file path based on the current working directory. - */ -void update_history(const char *flname) { - int i; // Looping var - char absolute[FL_PATH_MAX]; - int max_files; - - - fluid_prefs.get("recent_files", max_files, 5); - if (max_files > 10) max_files = 10; - - fl_filename_absolute(absolute, sizeof(absolute), flname); -#ifdef _WIN32 - // Make path canonical. - for (char *s = absolute; *s; s++) { - if (*s == '\\') - *s = '/'; - } -#endif - - - for (i = 0; i < max_files; i ++) -#if defined(_WIN32) || defined(__APPLE__) - if (!strcasecmp(absolute, absolute_history[i])) break; -#else - if (!strcmp(absolute, absolute_history[i])) break; -#endif // _WIN32 || __APPLE__ - - if (i == 0) return; - - if (i >= max_files) i = max_files - 1; - - // Move the other flnames down in the list... - memmove(absolute_history + 1, absolute_history, - i * sizeof(absolute_history[0])); - memmove(relative_history + 1, relative_history, - i * sizeof(relative_history[0])); - - // Put the new file at the top... - strlcpy(absolute_history[0], absolute, sizeof(absolute_history[0])); - std::string fn = fl_filename_shortened(absolute_history[0], 48); - strncpy(relative_history[0], fn.c_str(), sizeof(relative_history[0]) - 1); - - // Update the menu items as needed... - for (i = 0; i < max_files; i ++) { - fluid_prefs.set( Fl_Preferences::Name("file%d", i), absolute_history[i]); - if (absolute_history[i][0]) { - if (i == 9) history_item[i].flags = FL_MENU_DIVIDER; - else history_item[i].flags = 0; - } else break; - } - - for (; i < 10; i ++) { - fluid_prefs.set( Fl_Preferences::Name("file%d", i), ""); - if (i) history_item[i-1].flags |= FL_MENU_DIVIDER; - history_item[i].hide(); - } - fluid_prefs.flush(); -} - -/** - Set the filename of the current .fl design. - \param[in] c the new absolute filename and path - */ -void set_filename(const char *c) { - if (g_project.proj_filename) free((void *)g_project.proj_filename); - g_project.proj_filename = c ? fl_strdup(c) : NULL; - - if (g_project.proj_filename && !batch_mode) - update_history(g_project.proj_filename); - - set_modflag(modflag); -} - - -/** - 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 set_modflag(int mf, int mfc) { - const char *code_ext = NULL; - 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 (main_window) { - std::string basename; - if (!g_project.proj_filename) basename = "Untitled.fl"; - else basename = fl_filename_name_str(std::string(g_project.proj_filename)); - code_ext = fl_filename_ext(g_project.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 = main_window->label(); - // only update the title if it actually changed - if (!old_title || strcmp(old_title, new_title)) - 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(); -} - -// ---- Main program entry point - -/** - Handle command line arguments. - \param[in] argc number of arguments in the list - \param[in] argv pointer to an array of arguments - \param[inout] i current argument index - \return number of arguments used; if 0, the argument is not supported - */ -static int arg(int argc, char** argv, int& i) { - if (argv[i][0] != '-') - return 0; - if (argv[i][1] == 'd' && !argv[i][2]) { - G_debug=1; - i++; return 1; - } - if (argv[i][1] == 'u' && !argv[i][2]) { - update_file++; - batch_mode++; - i++; return 1; - } - if (argv[i][1] == 'c' && !argv[i][2]) { - compile_file++; - batch_mode++; - i++; return 1; - } - if ((strcmp(argv[i], "-v")==0) || (strcmp(argv[i], "--version")==0)) { - show_version = 1; - i++; return 1; - } - if (argv[i][1] == 'c' && argv[i][2] == 's' && !argv[i][3]) { - compile_file++; - compile_strings++; - batch_mode++; - i++; return 1; - } - if (argv[i][1] == 'o' && !argv[i][2] && i+1 < argc) { - g_code_filename_arg = argv[i+1]; - batch_mode++; - i += 2; return 2; - } -#ifndef NDEBUG - if ((i+1 < argc) && (strcmp(argv[i], "--autodoc") == 0)) { - g_autodoc_path = argv[i+1]; - i += 2; return 2; - } -#endif - if (strcmp(argv[i], "--help")==0) { - return 0; - } - if (argv[i][1] == 'h' && !argv[i][2]) { - if ( (i+1 < argc) && (argv[i+1][0] != '-') ) { - g_header_filename_arg = argv[i+1]; - batch_mode++; - i += 2; - return 2; - } else { - // a lone "-h" without a filename will output the help string - return 0; - } - } - return 0; -} - -#if ! (defined(_WIN32) && !defined (__CYGWIN__)) - -int quit_flag = 0; -#include -#ifdef _sigargs -#define SIGARG _sigargs -#else -#ifdef __sigargs -#define SIGARG __sigargs -#else -#define SIGARG int // you may need to fix this for older systems -#endif -#endif - -extern "C" { -static void sigint(SIGARG) { - signal(SIGINT,sigint); - quit_flag = 1; -} -} - -#endif - -/** - Start Fluid. - - Fluid can run in interactive mode with a full user interface to design new - user interfaces and write the C++ files to manage them, - - Fluid can run form the command line in batch mode to convert .fl design files - into C++ source and header files. In batch mode, no display is needed, - particularly no X11 connection will be attempted on Linux/Unix. - - \param[in] argc number of arguments in the list - \param[in] argv pointer to an array of arguments - \return in batch mode, an error code will be returned via \c exit() . This - function return 1, if there was an error in the parameters list. - \todo On Windows, Fluid can under certain conditions open a dialog box, even - in batch mode. Is that intentional? Does it circumvent issues with Windows' - stderr and stdout? - */ -int fluid_main(int argc,char **argv) { - int i = 1; - - setlocale(LC_ALL, ""); // enable multi-language errors in file chooser - setlocale(LC_NUMERIC, "C"); // make sure numeric values are written correctly - g_launch_path = end_with_slash(fl_getcwd_str()); // store the current path at launch - - Fl::args_to_utf8(argc, argv); // for MSYS2/MinGW - if ( (Fl::args(argc,argv,i,arg) == 0) // unsupported argument found - || (batch_mode && (i != argc-1)) // .fl filename missing - || (!batch_mode && (i < argc-1)) // more than one filename found - || (argv[i] && (argv[i][0] == '-'))) { // unknown option - static const char *msg = - "usage: %s name.fl\n" - " -u : update .fl file and exit (may be combined with '-c' or '-cs')\n" - " -c : write .cxx and .h and exit\n" - " -cs : write .cxx and .h and strings and exit\n" - " -o : .cxx output filename, or extension if starts with '.'\n" - " -h : .h output filename, or extension if starts with '.'\n" - " --help : brief usage information\n" - " --version, -v : print fluid version number\n" - " -d : enable internal debugging\n"; - const char *app_name = NULL; - if ( (argc > 0) && argv[0] && argv[0][0] ) - app_name = fl_filename_name(argv[0]); - if ( !app_name || !app_name[0]) - app_name = "fluid"; -#ifdef _MSC_VER - // TODO: if this is fluid-cmd, use stderr and not fl_message - fl_message(msg, app_name); -#else - fprintf(stderr, msg, app_name); -#endif - return 1; - } - if (show_version) { - printf("fluid v%d.%d.%d\n", FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION); - ::exit(0); - } - - const char *c = NULL; - if (g_autodoc_path.empty()) - c = argv[i]; - - fl_register_images(); - - make_main_window(); - - if (c) set_filename(c); - if (!batch_mode) { -#ifdef __APPLE__ - fl_open_callback(apple_open_cb); -#endif // __APPLE__ - Fl::visual((Fl_Mode)(FL_DOUBLE|FL_INDEX)); - Fl_File_Icon::load_system_icons(); - main_window->callback(exit_cb); - position_window(main_window,"main_window_pos", 1, 10, 30, WINWIDTH, WINHEIGHT ); - if (g_shell_config) { - g_shell_config->read(fluid_prefs, FD_STORE_USER); - g_shell_config->update_settings_dialog(); - g_shell_config->rebuild_shell_menu(); - } - g_layout_list.read(fluid_prefs, FD_STORE_USER); - main_window->show(argc,argv); - toggle_widgetbin_cb(0,0); - toggle_codeview_cb(0,0); - if (!c && openlast_button->value() && absolute_history[0][0] && g_autodoc_path.empty()) { - // Open previous file when no file specified... - open_project_file(absolute_history[0]); - } - } - undo_suspend(); - if (c && !fld::io::read_file(c,0)) { - if (batch_mode) { - fprintf(stderr,"%s : %s\n", c, strerror(errno)); - exit(1); - } - fl_message("Can't read %s: %s", c, strerror(errno)); - } - undo_resume(); - - // command line args override code and header filenames from the project file - // in batch mode only - if (batch_mode) { - if (!g_code_filename_arg.empty()) { - g_project.code_file_set = 1; - g_project.code_file_name = g_code_filename_arg; - } - if (!g_header_filename_arg.empty()) { - g_project.header_file_set = 1; - g_project.header_file_name = g_header_filename_arg; - } - } - - if (update_file) { // fluid -u - fld::io::write_file(c,0); - if (!compile_file) - exit(0); - } - - if (compile_file) { // fluid -c[s] - if (compile_strings) - write_strings_cb(0,0); - write_cb(0,0); - exit(0); - } - - // don't lock up if inconsistent command line arguments were given - if (batch_mode) - exit(0); - - set_modflag(0); - undo_clear(); -#ifndef _WIN32 - signal(SIGINT,sigint); -#endif - - // Set (but do not start) timer callback for external editor updates - ExternalCodeEditor::set_update_timer_callback(external_editor_timer); - -#ifndef NDEBUG - // check if the user wants FLUID to generate image for the user documentation - if (!g_autodoc_path.empty()) { - run_autodoc(g_autodoc_path); - set_modflag(0, 0); - exit_cb(0,0); - return 0; - } -#endif - -#ifdef _WIN32 - Fl::run(); -#else - while (!quit_flag) Fl::wait(); - if (quit_flag) exit_cb(0,0); -#endif // _WIN32 - - undo_clear(); - return (0); -} - -/// \} - diff --git a/fluid/app/fluid.h b/fluid/app/fluid.h deleted file mode 100644 index 686047a72..000000000 --- a/fluid/app/fluid.h +++ /dev/null @@ -1,134 +0,0 @@ -// -// FLUID main entry 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_APP_FLUID_H -#define FLUID_APP_FLUID_H - -#include "tools/filename.h" - -#include -#include -#include - -#include - -#define BROWSERWIDTH 300 -#define BROWSERHEIGHT 500 -#define WINWIDTH 300 -#define MENUHEIGHT 25 -#define WINHEIGHT (BROWSERHEIGHT+MENUHEIGHT) - -// ---- types - -class Fl_Double_Window; -class Fl_Window; -class Fl_Menu_Bar; -class Fl_Type; -class Fl_Choice; -class Fl_Button; -class Fl_Check_Button; - -/** - Indicate the storage location for tools like layout suites and shell macros. - \see class Fd_Shell_Command, class Fd_Layout_Suite - */ -typedef enum { - FD_STORE_INTERNAL, ///< stored inside FLUID app - FD_STORE_USER, ///< suite is stored in the user wide FLUID settings - FD_STORE_PROJECT, ///< suite is stored within the current .fl project file - FD_STORE_FILE ///< store suite in external file -} Fd_Tool_Store; - -// ---- global variables - -extern Fl_Preferences fluid_prefs; -extern Fl_Menu_Item Main_Menu[]; -extern Fl_Menu_Bar *main_menubar; -extern Fl_Window *main_window; - -extern int show_guides; -extern int show_restricted; -extern int show_ghosted_outline; -extern int show_comments; - -extern int G_use_external_editor; -extern int G_debug; -extern char G_external_editor_command[512]; - -// File history info... -extern char absolute_history[10][FL_PATH_MAX]; -extern char relative_history[10][FL_PATH_MAX]; -extern void load_history(); -extern void update_history(const char *); - -extern Fl_Menu_Item *save_item; -extern Fl_Menu_Item *history_item; -extern Fl_Menu_Item *widgetbin_item; -extern Fl_Menu_Item *codeview_item; -extern Fl_Menu_Item *overlay_item; -extern Fl_Button *overlay_button; -extern Fl_Menu_Item *guides_item; -extern Fl_Menu_Item *restricted_item; -extern Fl_Check_Button *guides_button; - -extern int modflag; - -extern int update_file; // fluid -u -extern int compile_file; // fluid -c -extern int compile_strings; // fluic -cs -extern int batch_mode; - -extern int pasteoffset; - -extern std::string g_code_filename_arg; -extern std::string g_header_filename_arg; -extern std::string g_launch_path; - -extern std::string g_autodoc_path; - -// ---- public functions - -extern int fluid_main(int argc,char **argv); - -extern bool new_project(bool user_must_confirm = true); -extern void enter_project_dir(); -extern void leave_project_dir(); -extern void set_filename(const char *c); -extern void set_modflag(int mf, int mfc=-1); - -extern const std::string &get_tmpdir(); -extern std::string end_with_slash(const std::string &str); - -// ---- public callback functions - -extern void save_cb(Fl_Widget *, void *v); -extern void save_template_cb(Fl_Widget *, void *); -extern void revert_cb(Fl_Widget *,void *); -extern void exit_cb(Fl_Widget *,void *); - -extern int write_code_files(bool dont_show_completion_dialog=false); -extern void write_strings_cb(Fl_Widget *, void *); -extern void align_widget_cb(Fl_Widget *, long); -extern void toggle_widgetbin_cb(Fl_Widget *, void *); - -extern char position_window(Fl_Window *w, const char *prefsName, int Visible, int X, int Y, int W=0, int H=0); - -inline int fd_min(int a, int b) { return (a < b ? a : b); } -inline int fd_max(int a, int b) { return (a > b ? a : b); } -inline int fd_min(int a, int b, int c) { return fd_min(a, fd_min(b, c)); } - -#endif // FLUID_APP_FLUID_H - diff --git a/fluid/app/history.cxx b/fluid/app/history.cxx new file mode 100644 index 000000000..7eadb5469 --- /dev/null +++ b/fluid/app/history.cxx @@ -0,0 +1,115 @@ +// +// Fluid Project File History 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 "app/history.h" + +#include "Fluid.h" + +#include "../src/flstring.h" + +using namespace fld; +using namespace fld::app; + + +/** + Load file history from preferences. + + This loads the absolute filepaths of the last 10 used design files. + It also computes and stores the relative filepaths for display in + the main menu. + */ +void History::load() { + int i; // Looping var + int max_files; + + Fluid.preferences.get("recent_files", max_files, 5); + if (max_files > 10) max_files = 10; + + for (i = 0; i < max_files; i ++) { + Fluid.preferences.get( Fl_Preferences::Name("file%d", i), abspath[i], "", sizeof(abspath[i])); + if (abspath[i][0]) { + // Make a shortened version of the filename for the menu... + std::string fn = fl_filename_shortened(abspath[i], 48); + strncpy(relpath[i], fn.c_str(), sizeof(relpath[i]) - 1); + if (i == 9) Fluid.history_item[i].flags = FL_MENU_DIVIDER; + else Fluid.history_item[i].flags = 0; + } else break; + } + + for (; i < 10; i ++) { + if (i) Fluid.history_item[i-1].flags |= FL_MENU_DIVIDER; + Fluid.history_item[i].hide(); + } +} + +/** + Update file history from preferences. + + Add this new filepath to the history and update the main menu. + Writes the new file history to the app preferences. + + \param[in] project_file path and filename of .fl project file, will be + converted into an absolute file path based on the current working directory. + */ +void History::update(std::string project_file) { + int i; // Looping var + int max_files; + + Fluid.preferences.get("recent_files", max_files, 5); + if (max_files > 10) max_files = 10; + + std::string absolute = fld::fix_separators(fl_filename_absolute_str(project_file)); + for (i = 0; i < max_files; i ++) +#if defined(_WIN32) || defined(__APPLE__) + if (!strcasecmp(absolute.c_str(), abspath[i])) break; +#else + if (!strcmp(absolute.c_str(), abspath[i])) break; +#endif // _WIN32 || __APPLE__ + + // Does the first entry match? + if (i == 0) + return; + + // Was there no match? + if (i >= max_files) + i = max_files - 1; + + // Move the other filenames down in the list... + memmove(abspath + 1, abspath, i * sizeof(abspath[0])); + memmove(relpath + 1, relpath, i * sizeof(relpath[0])); + + // Put the new file at the top... + strlcpy(abspath[0], absolute.c_str(), sizeof(abspath[0])); + std::string fn = fl_filename_shortened(absolute, 48); + strncpy(relpath[0], fn.c_str(), sizeof(relpath[0]) - 1); + + // Update the menu items as needed... + for (i = 0; i < max_files; i ++) { + Fluid.preferences.set( Fl_Preferences::Name("file%d", i), abspath[i]); + if (abspath[i][0]) { + if (i == 9) Fluid.history_item[i].flags = FL_MENU_DIVIDER; + else Fluid.history_item[i].flags = 0; + } else break; + } + + for (; i < 10; i ++) { + Fluid.preferences.set( Fl_Preferences::Name("file%d", i), ""); + if (i) Fluid.history_item[i-1].flags |= FL_MENU_DIVIDER; + Fluid.history_item[i].hide(); + } + Fluid.preferences.flush(); +} + diff --git a/fluid/app/history.h b/fluid/app/history.h new file mode 100644 index 000000000..e32723b4b --- /dev/null +++ b/fluid/app/history.h @@ -0,0 +1,45 @@ +// +// Fluid Project File History 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_APP_HISTORY_H +#define FLUID_APP_HISTORY_H + +#include + +#include + +namespace fld { +namespace app { + +class History { +public: + /// Stores the absolute filename of the last 10 project files, saved in app preferences. + char abspath[10][FL_PATH_MAX] { }; + /// The list of filenames as displayed in the main menu. + char relpath[10][FL_PATH_MAX] { }; + // Create the project file history. + History() = default; + // Load the project history from the preferences database. + void load(); + // Add a new file to the project history using absolute paths. + void update(std::string project_file); +}; + +} // namespace app +} // namespace fld + +#endif // FLUID_APP_HISTORY_H + diff --git a/fluid/app/mergeback.cxx b/fluid/app/mergeback.cxx deleted file mode 100644 index e0eafe500..000000000 --- a/fluid/app/mergeback.cxx +++ /dev/null @@ -1,493 +0,0 @@ -// -// MergeBack routines for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 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 -// - -#if 0 -// Matt: disabled - -#include "app/mergeback.h" - -#include "app/fluid.h" -#include "app/undo.h" -#include "io/Code_Writer.h" -#include "nodes/Fl_Function_Type.h" -#include "nodes/Fl_Widget_Type.h" - -#include -#include - -#include -#include -#include -#include -#include - -extern void propagate_load(Fl_Group*, void*); -extern void load_panel(); -extern void redraw_browser(); - -// TODO: add application user setting to control mergeback -// [] new projects default to mergeback -// [] check mergeback when loading project -// [] check mergeback when app gets focus -// [] always apply if safe -// TODO: command line option for mergeback -// -mb or --merge-back -// -mbs or --merge-back-if-safe -// NOTE: automatic mergeback on timer when file changes if app focus doesn't work -// NOTE: allow the user to edit comment blocks - -/** - Merge external changes in a source code file back into the current project. - - This experimental function reads a source code file line by line. When it - encounters a special tag in a line, the crc32 stored in the tag is compared - to the crc32 that was calculated from the code lines since the previous tag. - - If the crc's differ, the user has modified the source file externally, and the - given block differs from the block as it was generated by FLUID. Depending on - the block type, the user has modified the widget code (FD_TAG_GENERIC), which - can not be transferred back into the project. - - Modifications to code blocks and callbacks (CODE, CALLBACK) can be merged back - into the project. Their corresponding Fl_Type is found using the unique - node id that is part of the tag. The block is only merged back if the crc's - from the project and from the edited block differ. - - The caller must make sure that this code file was generated by the currently - loaded project. - - The user is informed in detailed dialogs what the function discovered and - offered to merge or cancel if appropriate. Just in case this function is - destructive, "undo" restores the state before a MergeBack. - - Callers can set different task. FD_MERGEBACK_ANALYSE checks if there are any - modifications in the code file and returns -1 if there was an error, or a - bit field where bit 0 is set if internal structures were modified, bit 1 if - code was changed, and bit 2 if modified blocks were found, but no Type node. - Bit 3 is set, if code was changed in the code file *and* the project. - - FD_MERGEBACK_INTERACTIVE checks for changes and presents a status dialog box - to the user if there were conflicting changes or if a mergeback is possible, - presenting the user the option to merge or cancel. Returns 0 if the project - remains unchanged, and 1 if the user merged changes back. -1 is returned if an - invalid tag was found. - - FD_MERGEBACK_APPLY merges all changes back into the project without any - interaction. Returns 0 if nothing changed, and 1 if it merged any changes back. - - FD_MERGEBACK_APPLY_IF_SAFE merges changes back only if there are no conflicts. - Returns 0 if nothing changed, and 1 if it merged any changes back, and -1 if - there were conflicts. - - \note this function is currently part of fld::io::Code_Writer to get easy access - to our crc32 code that also wrote the code file originally. - - \param[in] s path and filename of the source code file - \param[in] task see above - \return -1 if an error was found in a tag - \return -2 if no code file was found - \return see above - */ -int merge_back(const std::string &s, const std::string &p, int task) { - if (g_project.write_mergeback_data) { - Fd_Mergeback mergeback; - return mergeback.merge_back(s, p, task); - } else { - // nothing to be done if the mergeback option is disabled in the project - return 0; - } -} - -/** Allocate and initialize MergeBack class. */ -Fd_Mergeback::Fd_Mergeback() : - code(NULL), - line_no(0), - tag_error(0), - num_changed_code(0), - num_changed_structure(0), - num_uid_not_found(0), - num_possible_override(0) -{ -} - -/** Release allocated resources. */ -Fd_Mergeback::~Fd_Mergeback() -{ - if (code) ::fclose(code); -} - -/** Remove the first two spaces at every line start. - \param[inout] s block of C code - */ -void Fd_Mergeback::unindent(char *s) { - char *d = s; - bool line_start = true; - while (*s) { - if (line_start) { - if (*s>0 && isspace(*s)) s++; - if (*s>0 && isspace(*s)) s++; - line_start = false; - } - if (*s=='\r') s++; - if (*s=='\n') line_start = true; - *d++ = *s++; - } - *d = 0; -} - -/** - Read a block of text from the source file and remove the leading two spaces in every line. - \param[in] start start of the block within the file - \param[in] end end of text within the file - \return a string holding the text that was found in the file - */ -std::string Fd_Mergeback::read_and_unindent_block(long start, long end) { - long bsize = end-start; - long here = ::ftell(code); - ::fseek(code, start, SEEK_SET); - char *block = (char*)::malloc(bsize+1); - size_t n = ::fread(block, bsize, 1, code); - if (n!=1) - block[0] = 0; // read error - else - block[bsize] = 0; - unindent(block); - std::string str = block; - ::free(block); - ::fseek(code, here, SEEK_SET); - return str; -} - -/** Tell user the results of our MergeBack analysis and pop up a dialog to give - the user a choice to merge or cancel. - \return 1 if the user wants to merge (choice dialog was shown) - \return 0 if there is nothing to merge (no dialog was shown) - \return -1 if the user wants to cancel or an error occurred or an issue was presented - (message or choice dialog was shown) - */ -int Fd_Mergeback::ask_user_to_merge(const std::string &code_filename, const std::string &proj_filename) { - if (tag_error) { - fl_message("Comparing\n \"%s\"\nto\n \"%s\"\n\n" - "MergeBack found an error in line %d while reading tags\n" - "from the source code. Merging code back is not possible.", - code_filename.c_str(), proj_filename.c_str(), line_no); - return -1; - } - if (!num_changed_code && !num_changed_structure) { - return 0; - } - if (num_changed_structure && !num_changed_code) { - fl_message("Comparing\n \"%1$s\"\nto\n \"%2$s\"\n\n" - "MergeBack found %3$d modifications in the project structure\n" - "of the source code. These kind of changes can no be\n" - "merged back and will be lost when the source code is\n" - "generated again from the open project.", - code_filename.c_str(), proj_filename.c_str(), num_changed_structure); - return -1; - } - std::string msg = "Comparing\n \"%1$s\"\nto\n \"%2$s\"\n\n" - "MergeBack found %3$d modifications in the source code."; - if (num_possible_override) - msg += "\n\nWARNING: %6$d of these modified blocks appear to also have\n" - "changed in the project. Merging will override changes in\n" - "the project with changes from the source code file."; - if (num_uid_not_found) - msg += "\n\nWARNING: for %4$d of these modifications no Type node\n" - "can be found and these modification can't be merged back."; - if (!num_possible_override && !num_uid_not_found) - msg += "\nMerging these changes back appears to be safe."; - - if (num_changed_structure) - msg += "\n\nWARNING: %5$d modifications were found in the project\n" - "structure. These kind of changes can no be merged back\n" - "and will be lost when the source code is generated again\n" - "from the open project."; - - if (num_changed_code==num_uid_not_found) { - fl_message(msg.c_str(), - code_filename.c_str(), proj_filename.c_str(), - num_changed_code, num_uid_not_found, - num_changed_structure, num_possible_override); - return -1; - } else { - msg += "\n\nClick Cancel to abort the MergeBack operation.\n" - "Click Merge to merge all code changes back into\n" - "the open project."; - int c = fl_choice(msg.c_str(), "Cancel", "Merge", NULL, - code_filename.c_str(), proj_filename.c_str(), - num_changed_code, num_uid_not_found, - num_changed_structure, num_possible_override); - if (c==0) return -1; - return 1; - } -} - -/** Analyse the block and its corresponding widget callback. - Return findings in num_changed_code, num_changed_code, and num_uid_not_found. - */ -void Fd_Mergeback::analyse_callback(unsigned long code_crc, unsigned long tag_crc, int uid) { - Fl_Type *tp = Fl_Type::find_by_uid(uid); - if (tp && tp->is_true_widget()) { - std::string cb = tp->callback(); cb += "\n"; - unsigned long project_crc = fld::io::Code_Writer::block_crc(cb.c_str()); - // check if the code and project crc are the same, so this modification was already applied - if (project_crc!=code_crc) { - num_changed_code++; - // check if the block change on the project side as well, so we may override changes - if (project_crc!=tag_crc) { - num_possible_override++; - } - } - } else { - num_uid_not_found++; - num_changed_code++; - } -} - -/** Analyse the block and its corresponding Code Type. - Return findings in num_changed_code, num_changed_code, and num_uid_not_found. - */ -void Fd_Mergeback::analyse_code(unsigned long code_crc, unsigned long tag_crc, int uid) { - Fl_Type *tp = Fl_Type::find_by_uid(uid); - if (tp && tp->is_a(ID_Code)) { - std::string code = tp->name(); code += "\n"; - unsigned long project_crc = fld::io::Code_Writer::block_crc(code.c_str()); - // check if the code and project crc are the same, so this modification was already applied - if (project_crc!=code_crc) { - num_changed_code++; - // check if the block change on the project side as well, so we may override changes - if (project_crc!=tag_crc) { - num_possible_override++; - } - } - } else { - num_changed_code++; - num_uid_not_found++; - } -} - - -/** Analyse the code file and return findings in class member variables. - - The code file must be open for reading already. - - * tag_error is set if a tag was found, but could not be read - * line_no returns the line where an error occurred - * num_changed_code is set to the number of changed code blocks in the file. - Code changes can be merged back to the project. - * num_changed_structure is set to the number of structural changes. - Structural changes outside of code blocks can not be read back. - * num_uid_not_found number of blocks that were modified, but the corresponding - type or widget can not be found in the project - * num_possible_override number of blocks that were changed in the code file, - but also were changed in the project. - - \return -1 if reading a tag failed, otherwise 0 - */ -int Fd_Mergeback::analyse() { - // initialize local variables - unsigned long code_crc = 0; - bool line_start = true; - char line[1024]; - // bail if the caller has not opened a file yet - if (!code) return 0; - // initialize member variables to return our findings - line_no = 0; - tag_error = 0; - num_changed_code = 0; - num_changed_structure = 0; - num_uid_not_found = 0; - num_possible_override = 0; - code_crc = 0; - // loop through all lines in the code file - ::fseek(code, 0, SEEK_SET); - for (;;) { - // get the next line until end of file - if (fgets(line, 1023, code)==0) break; - line_no++; - const char *tag = strstr(line, "//~fl~"); - if (!tag) { - // if this line has no tag, add the contents to the CRC and continue - code_crc = fld::io::Code_Writer::block_crc(line, -1, code_crc, &line_start); - } else { - // if this line has a tag, read all tag data - int tag_type = -1, uid = 0; - unsigned long tag_crc = 0; - int n = sscanf(tag, "//~fl~%d~%04x~%08lx~~", &tag_type, &uid, &tag_crc); - if (n!=3 || tag_type<0 || tag_type>FD_TAG_LAST ) { tag_error = 1; return -1; } - if (code_crc != tag_crc) { - switch (tag_type) { - case FD_TAG_GENERIC: - num_changed_structure++; - break; - case FD_TAG_MENU_CALLBACK: - case FD_TAG_WIDGET_CALLBACK: - analyse_callback(code_crc, tag_crc, uid); - break; - case FD_TAG_CODE: - analyse_code(code_crc, tag_crc, uid); - break; - } - } - // reset everything for the next block - code_crc = 0; - line_start = true; - } - } - return 0; -} - -/** Apply callback mergebacks from the code file to the project. - \return 1 if the project changed - */ -int Fd_Mergeback::apply_callback(long block_end, long block_start, unsigned long code_crc, int uid) { - Fl_Type *tp = Fl_Type::find_by_uid(uid); - if (tp && tp->is_true_widget()) { - std::string cb = tp->callback(); cb += "\n"; - unsigned long project_crc = fld::io::Code_Writer::block_crc(cb.c_str()); - if (project_crc!=code_crc) { - tp->callback(read_and_unindent_block(block_start, block_end).c_str()); - return 1; - } - } - return 0; -} - -/** Apply callback mergebacks from the code file to the project. - \return 1 if the project changed - */ -int Fd_Mergeback::apply_code(long block_end, long block_start, unsigned long code_crc, int uid) { - Fl_Type *tp = Fl_Type::find_by_uid(uid); - if (tp && tp->is_a(ID_Code)) { - std::string cb = tp->name(); cb += "\n"; - unsigned long project_crc = fld::io::Code_Writer::block_crc(cb.c_str()); - if (project_crc!=code_crc) { - tp->name(read_and_unindent_block(block_start, block_end).c_str()); - return 1; - } - } - return 0; -} - -/** Apply all possible mergebacks from the code file to the project. - The code file must be open for reading already. - \return -1 if reading a tag failed, 0 if nothing changed, 1 if the project changed - */ -int Fd_Mergeback::apply() { - // initialize local variables - unsigned long code_crc = 0; - bool line_start = true; - char line[1024]; - int changed = 0; - long block_start = 0; - long block_end = 0; - // bail if the caller has not opened a file yet - if (!code) return 0; - // initialize member variables to return our findings - line_no = 0; - tag_error = 0; - code_crc = 0; - // loop through all lines in the code file - ::fseek(code, 0, SEEK_SET); - for (;;) { - // get the next line until end of file - if (fgets(line, 1023, code)==0) break; - line_no++; - const char *tag = strstr(line, "//~fl~"); - if (!tag) { - // if this line has no tag, add the contents to the CRC and continue - code_crc = fld::io::Code_Writer::block_crc(line, -1, code_crc, &line_start); - block_end = ::ftell(code); - } else { - // if this line has a tag, read all tag data - int tag_type = -1, uid = 0; - unsigned long tag_crc = 0; - int n = sscanf(tag, "//~fl~%d~%04x~%08lx~~", &tag_type, &uid, &tag_crc); - if (n!=3 || tag_type<0 || tag_type>FD_TAG_LAST ) { tag_error = 1; return -1; } - if (code_crc != tag_crc) { - if (tag_type==FD_TAG_MENU_CALLBACK || tag_type==FD_TAG_WIDGET_CALLBACK) { - changed |= apply_callback(block_end, block_start, code_crc, uid); - } else if (tag_type==FD_TAG_CODE) { - changed |= apply_code(block_end, block_start, code_crc, uid); - } - } - // reset everything for the next block - code_crc = 0; - line_start = true; - block_start = ::ftell(code); - } - } - return changed; -} - -/** Dispatch the MergeBack into analysis, interactive, or apply directly. - \param[in] s source code filename and path - \param[in] task one of FD_MERGEBACK_ANALYSE, FD_MERGEBACK_INTERACTIVE, - FD_MERGEBACK_APPLY_IF_SAFE, or FD_MERGEBACK_APPLY - \return -1 if an error was found in a tag - \return -2 if no code file was found - \return See more at ::merge_back(const std::string &s, int task). - */ -int Fd_Mergeback::merge_back(const std::string &s, const std::string &p, int task) { - int ret = 0; - code = fl_fopen(s.c_str(), "rb"); - if (!code) return -2; - do { // no actual loop, just make sure we close the code file - if (task == FD_MERGEBACK_ANALYSE) { - analyse(); - if (tag_error) {ret = -1; break; } - if (num_changed_structure) ret |= 1; - if (num_changed_code) ret |= 2; - if (num_uid_not_found) ret |= 4; - if (num_possible_override) ret |= 8; - break; - } - if (task == FD_MERGEBACK_INTERACTIVE) { - analyse(); - ret = ask_user_to_merge(s, p); - if (ret != 1) - return ret; - task = FD_MERGEBACK_APPLY; // fall through - } - if (task == FD_MERGEBACK_APPLY_IF_SAFE) { - analyse(); - if (tag_error || num_changed_structure || num_possible_override) { - ret = -1; - break; - } - if (num_changed_code==0) { - ret = 0; - break; - } - task = FD_MERGEBACK_APPLY; // fall through - } - if (task == FD_MERGEBACK_APPLY) { - ret = apply(); - if (ret == 1) { - set_modflag(1); - redraw_browser(); - load_panel(); - } - ret = 1; // avoid message box in caller - } - } while (0); - fclose(code); - code = NULL; - return ret; -} - -#endif - diff --git a/fluid/app/mergeback.h b/fluid/app/mergeback.h deleted file mode 100644 index f828e9669..000000000 --- a/fluid/app/mergeback.h +++ /dev/null @@ -1,81 +0,0 @@ -// -// MergeBack routines for the Fast Light Tool Kit (FLTK). -// -// Copyright 2023 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 -// - -// Matt: disabled -#if 0 - -#ifndef _FLUID_MERGEBACK_H -#define _FLUID_MERGEBACK_H - -#include - -#include -#include - -const int FD_TAG_GENERIC = 0; -const int FD_TAG_CODE = 1; -const int FD_TAG_MENU_CALLBACK = 2; -const int FD_TAG_WIDGET_CALLBACK = 3; -const int FD_TAG_LAST = 3; - -const int FD_MERGEBACK_ANALYSE = 0; -const int FD_MERGEBACK_INTERACTIVE = 1; -const int FD_MERGEBACK_APPLY = 2; -const int FD_MERGEBACK_APPLY_IF_SAFE = 3; - -/** Class that implements the MergeBack functionality. - \see merge_back(const std::string &s, int task) - */ -class Fd_Mergeback -{ -protected: - /// Pointer to the C++ code file. - FILE *code; - /// Current line number in the C++ code file. - int line_no; - /// Set if there was an error reading a tag. - int tag_error; - /// Number of code blocks that were different than the CRC in their tag. - int num_changed_code; - /// Number of generic structure blocks that were different than the CRC in their tag. - int num_changed_structure; - /// Number of code block that were modified, but a type node by that uid was not found. - int num_uid_not_found; - /// Number of modified code block where the corresponding project block also changed. - int num_possible_override; - - void unindent(char *s); - std::string read_and_unindent_block(long start, long end); - void analyse_callback(unsigned long code_crc, unsigned long tag_crc, int uid); - void analyse_code(unsigned long code_crc, unsigned long tag_crc, int uid); - int apply_callback(long block_end, long block_start, unsigned long code_crc, int uid); - int apply_code(long block_end, long block_start, unsigned long code_crc, int uid); - -public: - Fd_Mergeback(); - ~Fd_Mergeback(); - int merge_back(const std::string &s, const std::string &p, int task); - int ask_user_to_merge(const std::string &s, const std::string &p); - int analyse(); - int apply(); -}; - -extern int merge_back(const std::string &s, const std::string &p, int task); - - -#endif // _FLUID_MERGEBACK_H - -#endif diff --git a/fluid/app/project.cxx b/fluid/app/project.cxx deleted file mode 100644 index f369680e5..000000000 --- a/fluid/app/project.cxx +++ /dev/null @@ -1,192 +0,0 @@ -// -// FLUID main entry 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 "app/project.h" - -#include "nodes/Fl_Type.h" -#include "panels/settings_panel.h" - -// ---- project settings - -/// The current project, possibly a new, empty roject -Fluid_Project g_project; - -/** - Initialize a new project. - */ -Fluid_Project::Fluid_Project() : -i18n_type(FD_I18N_NONE), -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), -write_mergeback_data(0), -header_file_name(".h"), -code_file_name(".cxx") -{ } - -/** - Clear all project resources. - Not implemented. - */ -Fluid_Project::~Fluid_Project() { -} - -/** - Reset all project setting to create a new empty project. - */ -void Fluid_Project::reset() { - ::delete_all(); - i18n_type = FD_I18N_NONE; - - i18n_gnu_include = ""; - i18n_gnu_conditional = ""; - i18n_gnu_function = "gettext"; - i18n_gnu_static_function = "gettext_noop"; - - i18n_pos_include = ""; - 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 Fluid_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 Fluid_Project::projectfile_path() const { - return end_with_slash(fl_filename_absolute_str(fl_filename_path_str(proj_filename), g_launch_path)); -} - -/** - Get the project file name including extension, for example `test.fl`. - \return the file name without path - */ -std::string Fluid_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 Fluid_Project::codefile_path() const { - std::string path = fl_filename_path_str(code_file_name); - if (batch_mode) - return end_with_slash(fl_filename_absolute_str(path, g_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 Fluid_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 Fluid_Project::headerfile_path() const { - std::string path = fl_filename_path_str(header_file_name); - if (batch_mode) - return end_with_slash(fl_filename_absolute_str(path, g_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 Fluid_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 Fluid_Project::stringsfile_path() const { - if (batch_mode) - return g_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 Fluid_Project::stringsfile_name() const { - switch (i18n_type) { - default: return fl_filename_setext_str(fl_filename_name(proj_filename), ".txt"); - case FD_I18N_GNU: return fl_filename_setext_str(fl_filename_name(proj_filename), ".po"); - case FD_I18N_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 Fluid_Project::basename() const { - return fl_filename_setext_str(fl_filename_name(proj_filename), ""); -} - diff --git a/fluid/app/project.h b/fluid/app/project.h deleted file mode 100644 index 058ce1764..000000000 --- a/fluid/app/project.h +++ /dev/null @@ -1,103 +0,0 @@ -// -// FLUID main entry 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_APP_PROJECT_H -#define FLUID_APP_PROJECT_H - -#include - -// ---- project class declaration - -/** - Enumeration of available internationalization types. - */ -typedef enum { - FD_I18N_NONE = 0, ///< No i18n, all strings are litearals - FD_I18N_GNU, ///< GNU gettext internationalization - FD_I18N_POSIX ///< Posix catgets internationalization -} Fd_I18n_Type; - -/** - Data and settings for a FLUID project file. - */ -class Fluid_Project { -public: - Fluid_Project(); - ~Fluid_Project(); - void reset(); - void update_settings_dialog(); - - std::string projectfile_path() const; - std::string projectfile_name() const; - std::string codefile_path() const; - std::string codefile_name() const; - std::string headerfile_path() const; - std::string headerfile_name() const; - std::string stringsfile_path() const; - std::string stringsfile_name() const; - std::string basename() const; - - /// One of the available internationalization types. - Fd_I18n_Type i18n_type; - /// Include file for GNU i18n, writes an #include statement into the source - /// file. This is usually `` or `"gettext.h"` for GNU gettext. - std::string i18n_gnu_include; - // Optional name of a macro for conditional i18n compilation. - std::string i18n_gnu_conditional; - /// For the gettext/intl.h options, this is the function that translates text - /// at runtime. This is usually "gettext" or "_". - std::string i18n_gnu_function; - /// For the gettext/intl.h options, this is the function that marks the translation - /// of text at initialisation time. This is usually "gettext_noop" or "N_". - std::string i18n_gnu_static_function; - - /// Include file for Posix i18n, write a #include statement into the source - /// file. This is usually `` for Posix catgets. - std::string i18n_pos_include; - // Optional name of a macro for conditional i18n compilation. - std::string i18n_pos_conditional; - /// Name of the nl_catd database - std::string i18n_pos_file; - /// Message set ID for the catalog. - std::string i18n_pos_set; - - /// If set, generate code to include the header file form the c++ file - int include_H_from_C; - /// If set, handle keyboard shortcut Ctrl on macOS using Cmd instead - int use_FL_COMMAND; - /// Clear if UTF-8 characters in statics texts are written as escape sequences - int utf8_in_src; - /// If set, will not be included from the header code before anything else - int avoid_early_includes; - /// If set, command line overrides header file name in .fl file. - int header_file_set; - /// If set, command line overrides source code file name in .fl file. - int code_file_set; - int write_mergeback_data; - /// Filename of the current .fl project file - const char *proj_filename { nullptr }; - /// Hold the default extension for header files, or the entire filename if set via command line. - std::string header_file_name; - /// Hold the default extension for source code files, or the entire filename if set via command line. - std::string code_file_name; -}; - -extern Fluid_Project g_project; - -#endif // FLUID_APP_PROJECT_H - - diff --git a/fluid/app/shell_command.cxx b/fluid/app/shell_command.cxx index 7012de8e6..3bb252cae 100644 --- a/fluid/app/shell_command.cxx +++ b/fluid/app/shell_command.cxx @@ -1,7 +1,7 @@ // -// FLUID main entry for the Fast Light Tool Kit (FLTK). +// Shell Command database coe 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 @@ -62,7 +62,7 @@ // TODO: make the settings dialog resizable // TODO: make g_shell_config static, not a pointer, but don't load anything in batch mode -// FEATURE: Fd_Tool_Store icons are currently redundant with @file and @save and could be improved +// FEATURE: fld::Tool_Store icons are currently redundant with @file and @save and could be improved // FEATURE: hostname, username, getenv support? // FEATURE: add the files ./fluid.prefs and ./fluid.user.prefs as tool locations // FEATURE: interpret compiler output, for example: clang, and highlight errors and warnings @@ -97,8 +97,8 @@ #include "app/shell_command.h" -#include "app/fluid.h" -#include "app/project.h" +#include "Fluid.h" +#include "Project.h" #include "io/Project_Reader.h" #include "io/Project_Writer.h" #include "panels/settings_panel.h" @@ -110,6 +110,8 @@ #include +using namespace fld; + static std::string fltk_config_cmd; static Fl_Process s_proc; @@ -120,40 +122,6 @@ bool shell_command_running() { return s_proc.desc() ? true : false; } -/** - Reads an entry from the group. A default value must be - supplied. The return value indicates if the value was available - (non-zero) or the default was used (0). - - \param[in] prefs preference group - \param[in] key name of entry - \param[out] value returned from preferences or default value if none was set - \param[in] defaultValue default value to be used if no preference was set - \return 0 if the default value was used - */ -char preferences_get(Fl_Preferences &prefs, const char *key, std::string &value, const std::string &defaultValue) { - char *v = NULL; - char ret = prefs.get(key, v, defaultValue.c_str()); - value = v; - ::free(v); - return ret; -} - -/** - Sets an entry (name/value pair). The return value indicates if there - was a problem storing the data in memory. However it does not - reflect if the value was actually stored in the preference file. - - \param[in] prefs preference group - \param[in] entry name of entry - \param[in] value set this entry to value (stops at the first nul character). - \return 0 if setting the value failed - */ -char preferences_set(Fl_Preferences &prefs, const char *key, const std::string &value) { - return prefs.set(key, value.c_str()); -} - - /** \class Fl_Process Launch an external shell command. */ @@ -162,7 +130,6 @@ char preferences_set(Fl_Preferences &prefs, const char *key, const std::string & Create a process manager */ Fl_Process::Fl_Process() { - _fpt= NULL; } /** @@ -183,13 +150,13 @@ Fl_Process::~Fl_Process() { FILE * Fl_Process::popen(const char *cmd, const char *mode) { #if defined(_WIN32) && !defined(__CYGWIN__) // PRECONDITIONS - if (!mode || !*mode || (*mode!='r' && *mode!='w') ) return NULL; + if (!mode || !*mode || (*mode!='r' && *mode!='w') ) return nullptr; if (_fpt) close(); // close first before reuse ptmode = *mode; pin[0] = pin[1] = pout[0] = pout[1] = perr[0] = perr[1] = INVALID_HANDLE_VALUE; // stderr to stdout wanted ? - int fusion = (strstr(cmd,"2>&1") !=NULL); + int fusion = (strstr(cmd,"2>&1") !=nullptr); // Create windows pipes if (!createPipe(pin) || !createPipe(pout) || (!fusion && !createPipe(perr) ) ) @@ -203,8 +170,8 @@ FILE * Fl_Process::popen(const char *cmd, const char *mode) { si.hStdOutput = pout[1]; si.hStdError = fusion ? pout[1] : perr [1]; - if ( CreateProcess(NULL, (LPTSTR) cmd,NULL,NULL,TRUE, - DETACHED_PROCESS,NULL,NULL, &si, &pi)) { + if ( CreateProcess(nullptr, (LPTSTR) cmd,nullptr,nullptr,TRUE, + DETACHED_PROCESS,nullptr,nullptr, &si, &pi)) { // don't need theses handles inherited by child process: clean_close(pin[0]); clean_close(pout[1]); clean_close(perr[1]); HANDLE & h = *mode == 'r' ? pout[0] : pin[1]; @@ -231,13 +198,13 @@ int Fl_Process::close() { clean_close(perr[0]); clean_close(pin[1]); clean_close(pout[0]); - _fpt = NULL; + _fpt = nullptr; return 0; } return -1; #else int ret = ::pclose(_fpt); - _fpt=NULL; + _fpt=nullptr; return ret; #endif } @@ -256,10 +223,10 @@ FILE *Fl_Process::desc() const { \param[out] line buffer to receive the line \param[in] s size of the provided buffer - \return NULL if an error occurred, otherwise a pointer to the string + \return nullptr if an error occurred, otherwise a pointer to the string */ char *Fl_Process::get_line(char * line, size_t s) const { - return _fpt ? fgets(line, (int)s, _fpt) : NULL; + return _fpt ? fgets(line, (int)s, _fpt) : nullptr; } // returns fileno(FILE*): @@ -279,7 +246,7 @@ int Fl_Process::get_fileno() const { bool Fl_Process::createPipe(HANDLE * h, BOOL bInheritHnd) { SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = NULL; + sa.lpSecurityDescriptor = nullptr; sa.bInheritHandle = bInheritHnd; return CreatePipe (&h[0],&h[1],&sa,0) ? true : false; } @@ -288,7 +255,7 @@ FILE *Fl_Process::freeHandles() { clean_close(pin[0]); clean_close(pin[1]); clean_close(pout[0]); clean_close(pout[1]); clean_close(perr[0]); clean_close(perr[1]); - return NULL; // convenient for error management + return nullptr; // convenient for error management } void Fl_Process::clean_close(HANDLE& h) { @@ -312,13 +279,13 @@ static bool prepare_shell_command(int flags) { return false; } if (flags & Fd_Shell_Command::SAVE_PROJECT) { - save_cb(0, 0); + Fluid.save_project_file(nullptr); } if (flags & Fd_Shell_Command::SAVE_SOURCECODE) { - write_code_files(true); + Fluid.write_code_files(true); } if (flags & Fd_Shell_Command::SAVE_STRINGS) { - write_strings_cb(0, 0); + Fluid.proj.write_strings(); } return true; } @@ -345,7 +312,7 @@ void shell_timer_cb(void*) { void shell_pipe_cb(FL_SOCKET, void*) { char line[1024]=""; // Line from command output... - if (s_proc.get_line(line, sizeof(line)) != NULL) { + if (s_proc.get_line(line, sizeof(line)) != nullptr) { // Add the line to the output list... shell_run_terminal->append(line); } else { @@ -373,29 +340,29 @@ static void expand_macro(std::string &cmd, const std::string ¯o, const std:: } static void expand_macros(std::string &cmd) { - expand_macro(cmd, "@BASENAME@", g_project.basename()); - expand_macro(cmd, "@PROJECTFILE_PATH@", g_project.projectfile_path()); - expand_macro(cmd, "@PROJECTFILE_NAME@", g_project.projectfile_name()); - expand_macro(cmd, "@CODEFILE_PATH@", g_project.codefile_path()); - expand_macro(cmd, "@CODEFILE_NAME@", g_project.codefile_name()); - expand_macro(cmd, "@HEADERFILE_PATH@", g_project.headerfile_path()); - expand_macro(cmd, "@HEADERFILE_NAME@", g_project.headerfile_name()); - expand_macro(cmd, "@TEXTFILE_PATH@", g_project.stringsfile_path()); - expand_macro(cmd, "@TEXTFILE_NAME@", g_project.stringsfile_name()); + expand_macro(cmd, "@BASENAME@", Fluid.proj.basename()); + expand_macro(cmd, "@PROJECTFILE_PATH@", Fluid.proj.projectfile_path()); + expand_macro(cmd, "@PROJECTFILE_NAME@", Fluid.proj.projectfile_name()); + expand_macro(cmd, "@CODEFILE_PATH@", Fluid.proj.codefile_path()); + expand_macro(cmd, "@CODEFILE_NAME@", Fluid.proj.codefile_name()); + expand_macro(cmd, "@HEADERFILE_PATH@", Fluid.proj.headerfile_path()); + expand_macro(cmd, "@HEADERFILE_NAME@", Fluid.proj.headerfile_name()); + expand_macro(cmd, "@TEXTFILE_PATH@", Fluid.proj.stringsfile_path()); + expand_macro(cmd, "@TEXTFILE_NAME@", Fluid.proj.stringsfile_name()); // TODO: implement finding the script `fltk-config` for all platforms // if (cmd.find("@FLTK_CONFIG@") != std::string::npos) { // find_fltk_config(); // expand_macro(cmd, "@FLTK_CONFIG@", fltk_config_cmd.c_str()); // } if (cmd.find("@TMPDIR@") != std::string::npos) - expand_macro(cmd, "@TMPDIR@", get_tmpdir()); + expand_macro(cmd, "@TMPDIR@", Fluid.get_tmpdir()); } /** Show the terminal window where it was last positioned. */ void show_terminal_window() { - Fl_Preferences pos(fluid_prefs, "shell_run_Window_pos"); + Fl_Preferences pos(Fluid.preferences, "shell_run_Window_pos"); int x, y, w, h; pos.get("x", x, -1); pos.get("y", y, 0); @@ -439,7 +406,7 @@ void run_shell_command(const std::string &cmd, int flags) { shell_run_terminal->printf("\033[0;32m%s\033[0m\n", expanded_cmd.c_str()); shell_run_window->label(expanded_cmd.c_str()); - if (s_proc.popen((char *)expanded_cmd.c_str()) == NULL) { + if (s_proc.popen((char *)expanded_cmd.c_str()) == nullptr) { shell_run_terminal->printf("\033[1;31mUnable to run shell command: %s\033[0m\n", strerror(errno)); shell_run_window->label("FLUID Shell"); @@ -459,10 +426,10 @@ void run_shell_command(const std::string &cmd, int flags) { */ Fd_Shell_Command::Fd_Shell_Command() : shortcut(0), - storage(FD_STORE_USER), + storage(fld::Tool_Store::USER), condition(0), flags(0), - shell_menu_item_(NULL) + shell_menu_item_(nullptr) { } @@ -480,7 +447,7 @@ Fd_Shell_Command::Fd_Shell_Command(const Fd_Shell_Command *rhs) condition_data(rhs->condition_data), command(rhs->command), flags(rhs->flags), - shell_menu_item_(NULL) + shell_menu_item_(nullptr) { } @@ -493,11 +460,11 @@ Fd_Shell_Command::Fd_Shell_Command(const std::string &in_name) : name(in_name), label(in_name), shortcut(0), - storage(FD_STORE_USER), + storage(fld::Tool_Store::USER), condition(Fd_Shell_Command::ALWAYS), command("echo \"Hello, FLUID!\""), flags(Fd_Shell_Command::SAVE_PROJECT|Fd_Shell_Command::SAVE_SOURCECODE), - shell_menu_item_(NULL) + shell_menu_item_(nullptr) { } @@ -516,7 +483,7 @@ Fd_Shell_Command::Fd_Shell_Command(const std::string &in_name) Fd_Shell_Command::Fd_Shell_Command(const std::string &in_name, const std::string &in_label, Fl_Shortcut in_shortcut, - Fd_Tool_Store in_storage, + fld::Tool_Store in_storage, int in_condition, const std::string &in_condition_data, const std::string &in_command, @@ -529,7 +496,7 @@ Fd_Shell_Command::Fd_Shell_Command(const std::string &in_name, condition_data(in_condition_data), command(in_command), flags(in_flags), - shell_menu_item_(NULL) + shell_menu_item_(nullptr) { } @@ -549,8 +516,8 @@ void Fd_Shell_Command::run() { */ void Fd_Shell_Command::update_shell_menu() { if (shell_menu_item_) { - const char *old_label = shell_menu_item_->label(); // can be NULL - const char *new_label = label.c_str(); // never NULL + const char *old_label = shell_menu_item_->label(); // can be nullptr + const char *new_label = label.c_str(); // never nullptr if (!old_label || (old_label && strcmp(old_label, new_label))) { if (old_label) ::free((void*)old_label); shell_menu_item_->label(fl_strdup(new_label)); @@ -597,33 +564,33 @@ bool Fd_Shell_Command::is_active() { void Fd_Shell_Command::read(Fl_Preferences &prefs) { int tmp; - preferences_get(prefs, "name", name, ""); - preferences_get(prefs, "label", label, ""); + prefs.get("name", name, ""); + prefs.get("label", label, ""); prefs.get("shortcut", tmp, 0); shortcut = (Fl_Shortcut)tmp; prefs.get("storage", tmp, -1); - if (tmp != -1) storage = (Fd_Tool_Store)tmp; + if (tmp != -1) storage = (fld::Tool_Store)tmp; prefs.get("condition", condition, ALWAYS); - preferences_get(prefs, "condition_data", condition_data, ""); - preferences_get(prefs, "command", command, ""); + prefs.get("condition_data", condition_data, ""); + prefs.get("command", command, ""); prefs.get("flags", flags, 0); } void Fd_Shell_Command::write(Fl_Preferences &prefs, bool save_location) { - preferences_set(prefs, "name", name); - preferences_set(prefs, "label", label); + prefs.set("name", name); + prefs.set("label", label); if (shortcut != 0) prefs.set("shortcut", (int)shortcut); if (save_location) prefs.set("storage", (int)storage); if (condition != ALWAYS) prefs.set("condition", condition); - if (!condition_data.empty()) preferences_set(prefs, "condition_data", condition_data); - if (!command.empty()) preferences_set(prefs, "command", command); + if (!condition_data.empty()) prefs.set("condition_data", condition_data); + if (!command.empty()) prefs.set("command", command); if (flags != 0) prefs.set("flags", flags); } void Fd_Shell_Command::read(class fld::io::Project_Reader *in) { const char *c = in->read_word(1); if (strcmp(c, "{")!=0) return; // expecting start of group - storage = FD_STORE_PROJECT; + storage = fld::Tool_Store::PROJECT; for (;;) { c = in->read_word(1); if (strcmp(c, "}")==0) break; // end of command list @@ -667,10 +634,6 @@ void Fd_Shell_Command::write(class fld::io::Project_Writer *out) { Manage a list of shell commands and their parameters. */ Fd_Shell_Command_List::Fd_Shell_Command_List() -: list(NULL), - list_size(0), - list_capacity(0), - shell_menu_(NULL) { } @@ -702,14 +665,14 @@ void Fd_Shell_Command_List::clear() { ::free(list); list_size = 0; list_capacity = 0; - list = 0; + list = nullptr; } } /** remove all shell commands of the given storage location from the list. */ -void Fd_Shell_Command_List::clear(Fd_Tool_Store storage) { +void Fd_Shell_Command_List::clear(fld::Tool_Store storage) { for (int i=list_size-1; i>=0; i--) { if (list[i]->storage == storage) { remove(i); @@ -720,22 +683,22 @@ void Fd_Shell_Command_List::clear(Fd_Tool_Store storage) { /** Read shell configuration from a preferences group. */ -void Fd_Shell_Command_List::read(Fl_Preferences &prefs, Fd_Tool_Store storage) { +void Fd_Shell_Command_List::read(Fl_Preferences &prefs, fld::Tool_Store storage) { // import the old shell commands from previous user settings - if (&fluid_prefs == &prefs) { + if (&Fluid.preferences == &prefs) { int version; prefs.get("shell_commands_version", version, 0); if (version == 0) { int save_fl, save_code, save_strings; Fd_Shell_Command *cmd = new Fd_Shell_Command(); - cmd->storage = FD_STORE_USER; + cmd->storage = fld::Tool_Store::USER; cmd->name = "Sample Shell Command"; cmd->label = "Sample Shell Command"; cmd->shortcut = FL_ALT+'g'; - preferences_get(fluid_prefs, "shell_command", cmd->command, "echo \"Sample Shell Command\""); - fluid_prefs.get("shell_savefl", save_fl, 1); - fluid_prefs.get("shell_writecode", save_code, 1); - fluid_prefs.get("shell_writemsgs", save_strings, 0); + Fluid.preferences.get("shell_command", cmd->command, "echo \"Sample Shell Command\""); + Fluid.preferences.get("shell_savefl", save_fl, 1); + Fluid.preferences.get("shell_writecode", save_code, 1); + Fluid.preferences.get("shell_writemsgs", save_strings, 0); if (save_fl) cmd->flags |= Fd_Shell_Command::SAVE_PROJECT; if (save_code) cmd->flags |= Fd_Shell_Command::SAVE_SOURCECODE; if (save_strings) cmd->flags |= Fd_Shell_Command::SAVE_STRINGS; @@ -749,7 +712,7 @@ void Fd_Shell_Command_List::read(Fl_Preferences &prefs, Fd_Tool_Store storage) { for (int i=0; istorage = FD_STORE_USER; + cmd->storage = fld::Tool_Store::USER; cmd->read(cmd_prefs); add(cmd); } @@ -758,12 +721,12 @@ void Fd_Shell_Command_List::read(Fl_Preferences &prefs, Fd_Tool_Store storage) { /** Write shell configuration to a preferences group. */ -void Fd_Shell_Command_List::write(Fl_Preferences &prefs, Fd_Tool_Store storage) { +void Fd_Shell_Command_List::write(Fl_Preferences &prefs, fld::Tool_Store storage) { Fl_Preferences shell_commands(prefs, "shell_commands"); shell_commands.delete_all_groups(); int index = 0; for (int i=0; istorage == FD_STORE_USER) { + if (list[i]->storage == fld::Tool_Store::USER) { Fl_Preferences cmd(shell_commands, Fl_Preferences::Name(index++)); list[i]->write(cmd); } @@ -776,7 +739,7 @@ void Fd_Shell_Command_List::write(Fl_Preferences &prefs, Fd_Tool_Store storage) void Fd_Shell_Command_List::read(fld::io::Project_Reader *in) { const char *c = in->read_word(1); if (strcmp(c, "{")!=0) return; // expecting start of group - clear(FD_STORE_PROJECT); + clear(fld::Tool_Store::PROJECT); for (;;) { c = in->read_word(1); if (strcmp(c, "}")==0) break; // end of command list @@ -796,13 +759,13 @@ void Fd_Shell_Command_List::read(fld::io::Project_Reader *in) { void Fd_Shell_Command_List::write(fld::io::Project_Writer *out) { int n_in_project_file = 0; for (int i=0; istorage == FD_STORE_PROJECT) + if (list[i]->storage == fld::Tool_Store::PROJECT) n_in_project_file++; } if (n_in_project_file > 0) { out->write_string("\nshell_commands {"); for (int i=0; istorage == FD_STORE_PROJECT) + if (list[i]->storage == fld::Tool_Store::PROJECT) list[i]->write(out); } out->write_string("\n}"); @@ -872,9 +835,9 @@ void menu_shell_customize_cb(Fl_Widget*, void*) { Rebuild the entire shell submenu from scratch and replace the old menu. */ void Fd_Shell_Command_List::rebuild_shell_menu() { - static Fl_Menu_Item *shell_submenu = NULL; + static Fl_Menu_Item *shell_submenu = nullptr; if (!shell_submenu) - shell_submenu = (Fl_Menu_Item*)main_menubar->find_item(menu_marker); + shell_submenu = (Fl_Menu_Item*)Fluid.main_menubar->find_item(menu_marker); int i, j, num_active_items = 0; // count the active commands @@ -926,7 +889,7 @@ void Fd_Shell_Command_List::update_settings_dialog() { */ Fl_Menu_Item Fd_Shell_Command_List::default_menu[] = { { "Customize...", FL_ALT+'x', menu_shell_customize_cb }, - { NULL } + { nullptr } }; /** @@ -939,7 +902,7 @@ void Fd_Shell_Command_List::menu_marker(Fl_Widget*, void*) { /** Export all selected shell commands to an external file. - Verify that g_shell_config and w_settings_shell_list are not NULL. Open a + Verify that g_shell_config and w_settings_shell_list are not nullptr. Open a file chooser and export all items that are selected in w_settings_shell_list into an external file. */ @@ -951,11 +914,11 @@ void Fd_Shell_Command_List::export_selected() { dialog.title("Export selected shell commands:"); dialog.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); dialog.filter("FLUID Files\t*.flcmd\n"); - dialog.directory(g_project.projectfile_path().c_str()); - dialog.preset_file((g_project.basename() + ".flcmd").c_str()); + dialog.directory(Fluid.proj.projectfile_path().c_str()); + dialog.preset_file((Fluid.proj.basename() + ".flcmd").c_str()); if (dialog.show() != 0) return; - Fl_Preferences file(dialog.filename(), "flcmd.fluid.fltk.org", NULL, (Fl_Preferences::Root)(Fl_Preferences::C_LOCALE|Fl_Preferences::CLEAR)); + Fl_Preferences file(dialog.filename(), "flcmd.fluid.fltk.org", nullptr, (Fl_Preferences::Root)(Fl_Preferences::C_LOCALE|Fl_Preferences::CLEAR)); Fl_Preferences shell_commands(file, "shell_commands"); int i, index = 0, n = w_settings_shell_list->size(); for (i = 0; i < n; i++) { @@ -969,7 +932,7 @@ void Fd_Shell_Command_List::export_selected() { /** Import shell commands from an external file and add them to the list. - Verify that g_shell_config and w_settings_shell_list are not NULL. Open a + Verify that g_shell_config and w_settings_shell_list are not nullptr. Open a file chooser and import all items. */ void Fd_Shell_Command_List::import_from_file() { @@ -980,17 +943,17 @@ void Fd_Shell_Command_List::import_from_file() { dialog.title("Import shell commands:"); dialog.type(Fl_Native_File_Chooser::BROWSE_FILE); dialog.filter("FLUID Files\t*.flcmd\n"); - dialog.directory(g_project.projectfile_path().c_str()); - dialog.preset_file((g_project.basename() + ".flcmd").c_str()); + dialog.directory(Fluid.proj.projectfile_path().c_str()); + dialog.preset_file((Fluid.proj.basename() + ".flcmd").c_str()); if (dialog.show() != 0) return; - Fl_Preferences file(dialog.filename(), "flcmd.fluid.fltk.org", NULL, Fl_Preferences::C_LOCALE); + Fl_Preferences file(dialog.filename(), "flcmd.fluid.fltk.org", nullptr, Fl_Preferences::C_LOCALE); Fl_Preferences shell_commands(file, "shell_commands"); int i, n = shell_commands.groups(); for (i = 0; i < n; i++) { Fl_Preferences cmd_prefs(shell_commands, Fl_Preferences::Name(i)); Fd_Shell_Command *cmd = new Fd_Shell_Command(); - cmd->storage = FD_STORE_USER; + cmd->storage = fld::Tool_Store::USER; cmd->read(cmd_prefs); g_shell_config->add(cmd); } @@ -1003,5 +966,5 @@ void Fd_Shell_Command_List::import_from_file() { /** A pointer to the list of shell commands if we are not in batch mode. */ -Fd_Shell_Command_List *g_shell_config = NULL; +Fd_Shell_Command_List *g_shell_config = nullptr; diff --git a/fluid/app/shell_command.h b/fluid/app/shell_command.h index 3f5508493..bc7a18c3e 100644 --- a/fluid/app/shell_command.h +++ b/fluid/app/shell_command.h @@ -1,7 +1,7 @@ // -// FLUID main entry for the Fast Light Tool Kit (FLTK). +// Shell Command database 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,7 +17,7 @@ #ifndef _FLUID_SHELL_COMMAND_H #define _FLUID_SHELL_COMMAND_H -#include "app/fluid.h" +#include "Fluid.h" #include @@ -48,13 +48,11 @@ struct Fl_Menu_Item; class Fl_Widget; class Fl_Preferences; -char preferences_get(Fl_Preferences &prefs, const char *key, std::string &value, const std::string &defaultValue); -char preferences_set(Fl_Preferences &prefs, const char *key, const std::string &value); - void show_terminal_window(); void run_shell_command(const std::string &cmd, int flags); bool shell_command_running(void); + class Fl_Process { public: Fl_Process(); @@ -83,9 +81,10 @@ private: #endif protected: - FILE * _fpt; + FILE * _fpt = nullptr; }; + class Fd_Shell_Command { public: enum { ALWAYS, NEVER, MAC_ONLY, UX_ONLY, WIN_ONLY, MAC_AND_UX_ONLY, USER_ONLY, HOST_ONLY, ENV_ONLY }; // conditions @@ -97,19 +96,19 @@ public: Fd_Shell_Command(const std::string &in_name, const std::string &in_label, Fl_Shortcut in_shortcut, - Fd_Tool_Store in_storage, + fld::Tool_Store in_storage, int in_condition, const std::string &in_condition_data, const std::string &in_command, int in_flags); - std::string name; - std::string label; - Fl_Shortcut shortcut; - Fd_Tool_Store storage; - int condition; // always, hide, windows only, linux only, mac only, user, machine - std::string condition_data; // user name, machine name - std::string command; - int flags; // save_project, save_code, save_string, ... + std::string name { }; + std::string label { }; + Fl_Shortcut shortcut = 0; + fld::Tool_Store storage = fld::Tool_Store::USER; + int condition = ALWAYS; // always, hide, windows only, linux only, mac only, user, machine + std::string condition_data { }; // user name, machine name + std::string command { }; + int flags = 0; // save_project, save_code, save_string, ... Fl_Menu_Item *shell_menu_item_; void run(); void read(Fl_Preferences &prefs); @@ -120,12 +119,13 @@ public: bool is_active(); }; + class Fd_Shell_Command_List { public: - Fd_Shell_Command **list; - int list_size; - int list_capacity; - Fl_Menu_Item *shell_menu_; + Fd_Shell_Command **list = nullptr; + int list_size = 0; + int list_capacity = 0; + Fl_Menu_Item *shell_menu_ = nullptr; public: Fd_Shell_Command_List(); ~Fd_Shell_Command_List(); @@ -134,13 +134,13 @@ public: void insert(int index, Fd_Shell_Command *cmd); void remove(int index); void clear(); - void clear(Fd_Tool_Store store); + void clear(fld::Tool_Store store); // void move_up(); // void move_down(); // int load(const std::string &filename); // int save(const std::string &filename); - void read(Fl_Preferences &prefs, Fd_Tool_Store storage); - void write(Fl_Preferences &prefs, Fd_Tool_Store storage); + void read(Fl_Preferences &prefs, fld::Tool_Store storage); + void write(Fl_Preferences &prefs, fld::Tool_Store storage); void read(class fld::io::Project_Reader*); void write(class fld::io::Project_Writer*); void rebuild_shell_menu(); diff --git a/fluid/app/templates.cxx b/fluid/app/templates.cxx new file mode 100644 index 000000000..802357ed2 --- /dev/null +++ b/fluid/app/templates.cxx @@ -0,0 +1,143 @@ +// +// Fluid Project Templates 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 "app/templates.h" + +#include "Fluid.h" +#include "io/Project_Writer.h" +#include "nodes/factory.h" +#include "nodes/Tree.h" +#include "nodes/Window_Node.h" +#include "panels/template_panel.h" + +#include +#include +#include +#include "../src/flstring.h" + +using namespace fld; +using namespace fld::app; + +/** + Save a design template. + \todo We should document the concept of templates. + */ +void fld::app::save_template() { + // Setup the template panel... + if (!template_panel) make_template_panel(); + + template_clear(); + template_browser->add("New Template"); + template_load(); + + template_name->show(); + template_name->value(""); + + template_instance->hide(); + + template_delete->show(); + template_delete->deactivate(); + + template_submit->label("Save"); + template_submit->deactivate(); + + template_panel->label("Save Template"); + + // Show the panel and wait for the user to do something... + template_panel->show(); + while (template_panel->shown()) Fl::wait(); + + // Get the template name, return if it is empty... + const char *c = template_name->value(); + if (!c || !*c) return; + + // Convert template name to filename_with_underscores + char savename[FL_PATH_MAX], *saveptr; + strlcpy(savename, c, sizeof(savename)); + for (saveptr = savename; *saveptr; saveptr ++) { + if (isspace(*saveptr)) *saveptr = '_'; + } + + // Find the templates directory... + char filename[FL_PATH_MAX]; + Fluid.preferences.getUserdataPath(filename, sizeof(filename)); + + strlcat(filename, "templates", sizeof(filename)); + if (fl_access(filename, 0)) fl_make_path(filename); + + strlcat(filename, "/", sizeof(filename)); + strlcat(filename, savename, sizeof(filename)); + + char *ext = filename + strlen(filename); + if (ext >= (filename + sizeof(filename) - 5)) { + fl_alert("The template name \"%s\" is too long!", c); + return; + } + + // Save the .fl file... + strcpy(ext, ".fl"); + + if (!fl_access(filename, 0)) { + if (fl_choice("The template \"%s\" already exists.\n" + "Do you want to replace it?", "Cancel", + "Replace", nullptr, c) == 0) return; + } + + if (!fld::io::write_file(Fluid.proj, filename)) { + fl_alert("Error writing %s: %s", filename, strerror(errno)); + return; + } + +#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ) + // Get the screenshot, if any... + Node *t; + + for (t = Fluid.proj.tree.first; t; t = t->next) { + // Find the first window... + if (t->is_a(Type::Window)) break; + } + + if (!t) return; + + // Grab a screenshot... + Window_Node *wt = (Window_Node *)t; + uchar *pixels; + int w, h; + + if ((pixels = wt->read_image(w, h)) == nullptr) return; + + // Save to a PNG file... + strcpy(ext, ".png"); + + errno = 0; + if (fl_write_png(filename, pixels, w, h, 3) != 0) { + delete[] pixels; + fl_alert("Error writing %s: %s", filename, strerror(errno)); + return; + } + +# if 0 // The original PPM output code... + strcpy(ext, ".ppm"); + fp = fl_fopen(filename, "wb"); + fprintf(fp, "P6\n%d %d 255\n", w, h); + fwrite(pixels, w * h, 3, fp); + fclose(fp); +# endif // 0 + + delete[] pixels; +#endif // HAVE_LIBPNG && HAVE_LIBZ +} + diff --git a/fluid/app/templates.h b/fluid/app/templates.h new file mode 100644 index 000000000..cedec9f76 --- /dev/null +++ b/fluid/app/templates.h @@ -0,0 +1,29 @@ +// +// Fluid Project Templates 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_APP_TEMPLATES_H +#define FLUID_APP_TEMPLATES_H + +namespace fld { +namespace app { + +void save_template(); + +} // namespace app +} // namespace fld + +#endif // FLUID_APP_TEMPLATES_H + diff --git a/fluid/app/undo.cxx b/fluid/app/undo.cxx deleted file mode 100644 index baef9e717..000000000 --- a/fluid/app/undo.cxx +++ /dev/null @@ -1,265 +0,0 @@ -// -// FLUID undo support 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 "app/undo.h" - -#include "app/fluid.h" -#include "app/project.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "nodes/Fl_Type.h" -#include "nodes/Fl_Widget_Type.h" -#include "widgets/Node_Browser.h" - -#include -#include -#include -#include -#include -#include "tools/filename.h" -#include "../src/flstring.h" - -#if defined(_WIN32) && !defined(__CYGWIN__) -# include -# include -# define getpid (int)GetCurrentProcessId -#else -# include -#endif // _WIN32 && !__CYGWIN__ - - -// -// This file implements an undo system using temporary files; ideally -// we'd like to do this in memory, however the current data structures -// and design aren't well-suited... Instead, we save and restore -// checkpoint files. -// - -extern Fl_Window* the_panel; - -int undo_current = 0; // Current undo level in buffer -int undo_last = 0; // Last undo level in buffer -int undo_max = 0; // Maximum undo level used -int undo_save = -1; // Last undo level that was saved -static int undo_paused = 0; // Undo checkpointing paused? -int undo_once_type = 0; // Suspend further undos of the same type - - -// Return the undo filename. -// The filename is constructed in a static internal buffer and -// this buffer is overwritten by every call of this function. -// The return value is a pointer to this internal string. -static char *undo_filename(int level) { - static char undo_path[FL_PATH_MAX] = ""; // Undo path - static unsigned int undo_path_len = 0; // length w/o filename - - if (!undo_path_len) { - fluid_prefs.getUserdataPath(undo_path, sizeof(undo_path)); - undo_path_len = (unsigned int)strlen(undo_path); - } - - // append filename: "undo_PID_LEVEL.fl" - snprintf(undo_path + undo_path_len, - sizeof(undo_path) - undo_path_len - 1, - "undo_%d_%d.fl", getpid(), level); - return undo_path; -} - - -// Redo menu callback -void redo_cb(Fl_Widget *, void *) { - // int undo_item = main_menubar->find_index(undo_cb); - // int redo_item = main_menubar->find_index(redo_cb); - undo_once_type = 0; - - if (undo_current >= undo_last) { - fl_beep(); - return; - } - - undo_suspend(); - if (widget_browser) { - widget_browser->save_scroll_position(); - widget_browser->new_list(); - } - int reload_panel = (the_panel && the_panel->visible()); - if (!fld::io::read_file(undo_filename(undo_current + 1), 0)) { - // Unable to read checkpoint file, don't redo... - widget_browser->rebuild(); - g_project.update_settings_dialog(); - undo_resume(); - return; - } - if (reload_panel) { - for (Fl_Type *t = Fl_Type::first; t; t=t->next) { - if (t->is_widget() && t->selected) - t->open(); - } - } - if (widget_browser) widget_browser->restore_scroll_position(); - - undo_current ++; - - // Update modified flag... - set_modflag(undo_current != undo_save); - widget_browser->rebuild(); - g_project.update_settings_dialog(); - - // Update undo/redo menu items... - // if (undo_current >= undo_last) Main_Menu[redo_item].deactivate(); - // Main_Menu[undo_item].activate(); - undo_resume(); -} - -// Undo menu callback -void undo_cb(Fl_Widget *, void *) { - // int undo_item = main_menubar->find_index(undo_cb); - // int redo_item = main_menubar->find_index(redo_cb); - undo_once_type = 0; - - if (undo_current <= 0) { - fl_beep(); - return; - } - - if (undo_current == undo_last) { - fld::io::write_file(undo_filename(undo_current)); - } - - undo_suspend(); - // Undo first deletes all widgets which resets the widget_tree browser. - // Save the current scroll position, so we don't scroll back to 0 at undo. - // TODO: make the scroll position part of the .fl project file - if (widget_browser) { - widget_browser->save_scroll_position(); - widget_browser->new_list(); - } - int reload_panel = (the_panel && the_panel->visible()); - if (!fld::io::read_file(undo_filename(undo_current - 1), 0)) { - // Unable to read checkpoint file, don't undo... - widget_browser->rebuild(); - g_project.update_settings_dialog(); - set_modflag(0, 0); - undo_resume(); - return; - } - if (reload_panel) { - for (Fl_Type *t = Fl_Type::first; t; t=t->next) { - if (t->is_widget() && t->selected) { - t->open(); - break; - } - } - } - // Restore old browser position. - // Ideally, we would save the browser position inside the undo file. - if (widget_browser) widget_browser->restore_scroll_position(); - - undo_current --; - - // Update modified flag... - set_modflag(undo_current != undo_save); - - // Update undo/redo menu items... - // if (undo_current <= 0) Main_Menu[undo_item].deactivate(); - // Main_Menu[redo_item].activate(); - widget_browser->rebuild(); - g_project.update_settings_dialog(); - undo_resume(); -} - -/** - \param[in] type set a new type, or set to 0 to clear the once_type without setting a checkpoint - \return 1 if the checkpoint was set, 0 if this is a repeating event - */ -int undo_checkpoint_once(int type) { - if (type == 0) { - undo_once_type = 0; - return 0; - } - if (undo_paused) return 0; - if (undo_once_type != type) { - undo_checkpoint(); - undo_once_type = type; - return 1; - } else { - // do not add more checkpoints for the same undo type - return 0; - } -} - -// Save current file to undo buffer -void undo_checkpoint() { - // printf("undo_checkpoint(): undo_current=%d, undo_paused=%d, modflag=%d\n", - // undo_current, undo_paused, modflag); - - // Don't checkpoint if undo_suspend() has been called... - if (undo_paused) return; - - // int undo_item = main_menubar->find_index(undo_cb); - // int redo_item = main_menubar->find_index(redo_cb); - undo_once_type = 0; - - // Save the current UI to a checkpoint file... - const char *filename = undo_filename(undo_current); - if (!fld::io::write_file(filename)) { - // Don't attempt to do undo stuff if we can't write a checkpoint file... - perror(filename); - return; - } - - // Update the saved level... - if (modflag && undo_current <= undo_save) undo_save = -1; - else if (!modflag) undo_save = undo_current; - - // Update the current undo level... - undo_current ++; - undo_last = undo_current; - if (undo_current > undo_max) undo_max = undo_current; - - // Enable the Undo and disable the Redo menu items... - // Main_Menu[undo_item].activate(); - // Main_Menu[redo_item].deactivate(); -} - -// Clear undo buffer -void undo_clear() { - // int undo_item = main_menubar->find_index(undo_cb); - // int redo_item = main_menubar->find_index(redo_cb); - // Remove old checkpoint files... - for (int i = 0; i <= undo_max; i ++) { - fl_unlink(undo_filename(i)); - } - - // Reset current, last, and save indices... - undo_current = undo_last = undo_max = 0; - if (modflag) undo_save = -1; - else undo_save = 0; - - // Disable the Undo and Redo menu items... - // Main_Menu[undo_item].deactivate(); - // Main_Menu[redo_item].deactivate(); -} - -// Resume undo checkpoints -void undo_resume() { - undo_paused--; -} - -// Suspend undo checkpoints -void undo_suspend() { - undo_paused++; -} diff --git a/fluid/app/undo.h b/fluid/app/undo.h deleted file mode 100644 index e8ffab891..000000000 --- a/fluid/app/undo.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// FLUID undo definitions 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 undo_h -#define undo_h - -class Fl_Widget; - -#define kUndoWindowResize 1 - -extern int undo_current; // Current undo level in buffer -extern int undo_last; // Last undo level in buffer -extern int undo_save; // Last undo level that was saved -extern int undo_once_type; // Suspend further undos of the same type - -void redo_cb(Fl_Widget *, void *); // Redo menu callback -void undo_cb(Fl_Widget *, void *); // Undo menu callback -void undo_checkpoint(); // Save current file to undo buffer -int undo_checkpoint_once(int type); // Save undo buffer once until a different checkpoint type is called -void undo_clear(); // Clear undo buffer -void undo_resume(); // Resume undo checkpoints -void undo_suspend(); // Suspend undo checkpoints - -#endif // !undo_h 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 #include "../src/flstring.h" #include -/// \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; iis_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 "); } - 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 \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 #include #include +#include +#include -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 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 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 text_in_code { }; /// searchable tree for pointers that are only written once to the code file - Fd_Pointer_Tree *ptr_in_code; + std::set 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 @@ -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(atoi(read_word())); + proj_.i18n_type = static_cast(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()) { // - ((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 #include -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(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 -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 + +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 diff --git a/fluid/main.cxx b/fluid/main.cxx index d05732d2b..cc30114df 100644 --- a/fluid/main.cxx +++ b/fluid/main.cxx @@ -15,9 +15,9 @@ // #include "main.h" -#include "app/fluid.h" +#include "Fluid.h" int main(int argc,char **argv) { - return fluid_main(argc, argv); + return Fluid.run(argc, argv); } diff --git a/fluid/nodes/Button_Node.cxx b/fluid/nodes/Button_Node.cxx new file mode 100644 index 000000000..c1ac26e95 --- /dev/null +++ b/fluid/nodes/Button_Node.cxx @@ -0,0 +1,166 @@ +// +// Button Node 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 +// + +/** + \file Bottun_Node.cxx + + Node prototypes for Fl_Button based classes. Those are used by the Node + Factory to generate the scene from project files or user input. + */ + +#include "nodes/Button_Node.h" + +#include "Fluid.h" +#include "app/Snap_Action.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +// ---- Button Nodes --------------------------------------------------- MARK: - + + +// ---- Button ---- + +Button_Node Button_Node::prototype; + +static Fl_Menu_Item buttontype_menu[] = { + {"Normal", 0, nullptr, (void*)nullptr}, + {"Toggle", 0, nullptr, (void*)FL_TOGGLE_BUTTON}, + {"Radio", 0, nullptr, (void*)FL_RADIO_BUTTON}, + {nullptr} +}; + +Fl_Menu_Item *Button_Node::subtypes() { + return buttontype_menu; +} + +void Button_Node::ideal_size(int &w, int &h) { + auto layout = Fluid.proj.layout; + h = layout->labelsize + 8; + w = layout->labelsize * 4 + 8; + fld::app::Snap_Action::better_size(w, h); +} + +Fl_Widget *Button_Node::widget(int x, int y, int w, int h) { + return new Fl_Button(x, y, w, h, "Button"); +} + +void Button_Node::write_properties(fld::io::Project_Writer &f) { + Widget_Node::write_properties(f); + Fl_Button *btn = (Fl_Button*)o; + if (btn->compact()) { + f.write_string("compact"); + f.write_string("%d", btn->compact()); + } +} + +void Button_Node::read_property(fld::io::Project_Reader &f, const char *c) { + Fl_Button *btn = (Fl_Button*)o; + if (!strcmp(c, "compact")) { + btn->compact((uchar)atol(f.read_word())); + } else { + Widget_Node::read_property(f, c); + } +} + +void Button_Node::copy_properties() { + Widget_Node::copy_properties(); + Fl_Button *s = (Fl_Button*)o, *d = (Fl_Button*)live_widget; + d->compact(s->compact()); +} + + +// ---- Return Button ---- + +void Return_Button_Node::ideal_size(int &w, int &h) { + auto layout = Fluid.proj.layout; + h = layout->labelsize + 8; + w = layout->labelsize * 4 + 8 + h; // make room for the symbol + fld::app::Snap_Action::better_size(w, h); +} + +Fl_Widget *Return_Button_Node::widget(int x, int y, int w, int h) { + return new Fl_Return_Button(x, y, w, h, "Button"); +} + +Return_Button_Node Return_Button_Node::prototype; + + +// ---- Repeat Button ---- + +Fl_Widget *Repeat_Button_Node::widget(int x, int y, int w, int h) { + return new Fl_Repeat_Button(x, y, w, h, "Button"); +} + +Repeat_Button_Node Repeat_Button_Node::prototype; + + +// ---- Light Button ---- + +void Light_Button_Node::ideal_size(int &w, int &h) { + auto layout = Fluid.proj.layout; + h = layout->labelsize + 8; + w = layout->labelsize * 4 + 8 + layout->labelsize; // make room for the light + fld::app::Snap_Action::better_size(w, h); +} + +Fl_Widget *Light_Button_Node::widget(int x, int y, int w, int h) { + return new Fl_Light_Button(x, y, w, h, "Button"); +} + +Light_Button_Node Light_Button_Node::prototype; + + +// ---- Check Button ---- + +void Check_Button_Node::ideal_size(int &w, int &h) { + auto layout = Fluid.proj.layout; + h = layout->labelsize + 8; + w = layout->labelsize * 4 + 8 + layout->labelsize; // make room for the symbol + fld::app::Snap_Action::better_size(w, h); +} + +Fl_Widget *Check_Button_Node::widget(int x, int y, int w, int h) { + return new Fl_Check_Button(x, y, w, h, "Button"); +} + +Check_Button_Node Check_Button_Node::prototype; + + +// ---- Round Button ---- + +void Round_Button_Node::ideal_size(int &w, int &h) { + auto layout = Fluid.proj.layout; + h = layout->labelsize + 8; + w = layout->labelsize * 4 + 8 + layout->labelsize; // make room for the symbol + fld::app::Snap_Action::better_size(w, h); +} + +Fl_Widget *Round_Button_Node::widget(int x, int y, int w, int h) { + return new Fl_Round_Button(x, y, w, h, "Button"); +} + +Round_Button_Node Round_Button_Node::prototype; + diff --git a/fluid/nodes/Button_Node.h b/fluid/nodes/Button_Node.h new file mode 100644 index 000000000..ecdc8cc24 --- /dev/null +++ b/fluid/nodes/Button_Node.h @@ -0,0 +1,149 @@ +// +// Button Node header file 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_NODES_BUTTON_NODE_H +#define FLUID_NODES_BUTTON_NODE_H + +#include "nodes/Widget_Node.h" + +/** + \brief A handler for the simple push button and a base class for all other buttons. + */ +class Button_Node : public Widget_Node +{ +public: + typedef Widget_Node super; + static Button_Node prototype; +private: + Fl_Menu_Item *subtypes() override; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override { return "Fl_Button"; } + const char *alt_type_name() override { return "fltk::Button"; } + Fl_Widget *widget(int x, int y, int w, int h) override; + Widget_Node *_make() override { return new Button_Node(); } + int is_button() const override { return 1; } + Type type() const override { return Type::Button; } + bool is_a(Type inType) const override { return (inType==Type::Button) ? true : super::is_a(inType); } + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + void copy_properties() override; +}; + +// ---- Return Button ---- + +/** + \brief The Return Button is simply a Button with the return key as a hotkey. + */ +class Return_Button_Node : public Button_Node +{ +public: + typedef Button_Node super; + static Return_Button_Node prototype; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override { return "Fl_Return_Button"; } + const char *alt_type_name() override { return "fltk::ReturnButton"; } + Fl_Widget *widget(int x, int y, int w, int h) override; + Widget_Node *_make() override { return new Return_Button_Node(); } + Type type() const override { return Type::Return_Button; } + bool is_a(Type inType) const override { return (inType==Type::Return_Button) ? true : super::is_a(inType); } +}; + +// ---- Repeat Button ---- + +/** + \brief Handler for Fl_Repeat_Button. + \note Even though Fl_Repeat_Button is somewhat limited compared to Fl_Button, + and some settings may not make much sense, it is still derived from it, + so the wrapper should be as well. + */ +class Repeat_Button_Node : public Button_Node +{ +public: + typedef Button_Node super; + static Repeat_Button_Node prototype; +public: + const char *type_name() override { return "Fl_Repeat_Button"; } + const char *alt_type_name() override { return "fltk::RepeatButton"; } + Fl_Widget *widget(int x, int y, int w, int h) override; + Widget_Node *_make() override { return new Repeat_Button_Node(); } + Type type() const override { return Type::Repeat_Button; } + bool is_a(Type inType) const override { return (inType==Type::Repeat_Button) ? true : super::is_a(inType); } +}; + +// ---- Light Button ---- + +/** + \brief A handler for a toggle button with an indicator light. + */ +class Light_Button_Node : public Button_Node +{ +public: + typedef Button_Node super; + static Light_Button_Node prototype; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override { return "Fl_Light_Button"; } + const char *alt_type_name() override { return "fltk::LightButton"; } + Fl_Widget *widget(int x, int y, int w, int h) override; + Widget_Node *_make() override { return new Light_Button_Node(); } + Type type() const override { return Type::Light_Button; } + bool is_a(Type inType) const override { return (inType==Type::Light_Button) ? true : super::is_a(inType); } +}; + +// ---- Check Button ---- + +/** + \brief Manage buttons with a check mark on its left. + */ +class Check_Button_Node : public Button_Node +{ +public: + typedef Button_Node super; + static Check_Button_Node prototype; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override { return "Fl_Check_Button"; } + const char *alt_type_name() override { return "fltk::CheckButton"; } + Fl_Widget *widget(int x, int y, int w, int h) override; + Widget_Node *_make() override { return new Check_Button_Node(); } + Type type() const override { return Type::Check_Button; } + bool is_a(Type inType) const override { return (inType==Type::Check_Button) ? true : super::is_a(inType); } +}; + +// ---- Round Button ---- + +/** + \brief Manage buttons with a round indicator on its left. + */ +class Round_Button_Node : public Button_Node +{ +public: + typedef Button_Node super; + static Round_Button_Node prototype; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override { return "Fl_Round_Button"; } + const char *alt_type_name() override { return "fltk::RadioButton"; } + Fl_Widget *widget(int x, int y, int w, int h) override; + Widget_Node *_make() override { return new Round_Button_Node(); } + Type type() const override { return Type::Round_Button; } + bool is_a(Type inType) const override { return (inType==Type::Round_Button) ? true : super::is_a(inType); } +}; + + +#endif // FLUID_NODES_BUTTON_NODE_H diff --git a/fluid/nodes/Fl_Button_Type.cxx b/fluid/nodes/Fl_Button_Type.cxx deleted file mode 100644 index 2d3b7f31f..000000000 --- a/fluid/nodes/Fl_Button_Type.cxx +++ /dev/null @@ -1,227 +0,0 @@ -// -// Button type factory code for the Fast Light Tool Kit (FLTK). -// -// Type classes for most of the fltk widgets. Most of the work -// is done by code in Fl_Widget_Type.C. Also a factory instance -// of each of these type classes. -// -// This file also contains the "new" menu, which has a pointer -// to a factory instance for every class (both the ones defined -// here and ones in other files) -// -// Copyright 1998-2023 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 "nodes/Fl_Button_Type.h" - -#include "app/Fd_Snap_Action.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - - - -// ---- Button Types --------------------------------------------------- MARK: - - - -// ---- Button ---- - -static Fl_Menu_Item buttontype_menu[] = { - {"Normal", 0, 0, (void*)0}, - {"Toggle", 0, 0, (void*)FL_TOGGLE_BUTTON}, - {"Radio", 0, 0, (void*)FL_RADIO_BUTTON}, - {0} -}; - -Fl_Menu_Item *Fl_Button_Type::subtypes() { - return buttontype_menu; -} - -void Fl_Button_Type::ideal_size(int &w, int &h) { - h = layout->labelsize + 8; - w = layout->labelsize * 4 + 8; - Fd_Snap_Action::better_size(w, h); -} - -Fl_Widget *Fl_Button_Type::widget(int x, int y, int w, int h) { - return new Fl_Button(x, y, w, h, "Button"); -} - -void Fl_Button_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Widget_Type::write_properties(f); - Fl_Button *btn = (Fl_Button*)o; - if (btn->compact()) { - f.write_string("compact"); - f.write_string("%d", btn->compact()); - } -} - -void Fl_Button_Type::read_property(fld::io::Project_Reader &f, const char *c) { - Fl_Button *btn = (Fl_Button*)o; - if (!strcmp(c, "compact")) { - btn->compact((uchar)atol(f.read_word())); - } else { - Fl_Widget_Type::read_property(f, c); - } -} - -void Fl_Button_Type::copy_properties() { - Fl_Widget_Type::copy_properties(); - Fl_Button *s = (Fl_Button*)o, *d = (Fl_Button*)live_widget; - d->compact(s->compact()); -} - -Fl_Button_Type Fl_Button_type; - - -// ---- Return Button ---- - -/** - \brief The Return Button is simply a Button with the return key as a hotkey. - */ -class Fl_Return_Button_Type : public Fl_Button_Type -{ - typedef Fl_Button_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE { - h = layout->labelsize + 8; - w = layout->labelsize * 4 + 8 + h; // make room for the symbol - Fd_Snap_Action::better_size(w, h); - } - const char *type_name() FL_OVERRIDE { return "Fl_Return_Button"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::ReturnButton"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { - return new Fl_Return_Button(x, y, w, h, "Button"); - } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Return_Button_Type(); } - ID id() const FL_OVERRIDE { return ID_Return_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Return_Button) ? true : super::is_a(inID); } -}; - -Fl_Return_Button_Type Fl_Return_Button_type; - - -// ---- Repeat Button ---- - -/** - \brief Handler for Fl_Repeat_Button. - \note Even though Fl_Repeat_Button is somewhat limited compared to Fl_Button, - and some settings may not make much sense, it is still derived from it, - so the wrapper should be as well. - */ -class Fl_Repeat_Button_Type : public Fl_Button_Type -{ - typedef Fl_Button_Type super; -public: - const char *type_name() FL_OVERRIDE { return "Fl_Repeat_Button"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::RepeatButton"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { - return new Fl_Repeat_Button(x, y, w, h, "Button"); - } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Repeat_Button_Type(); } - ID id() const FL_OVERRIDE { return ID_Repeat_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Repeat_Button) ? true : super::is_a(inID); } -}; - -Fl_Repeat_Button_Type Fl_Repeat_Button_type; - - -// ---- Light Button ---- - -/** - \brief A handler for a toggle button with an indicator light. - */ -class Fl_Light_Button_Type : public Fl_Button_Type -{ - typedef Fl_Button_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE { - h = layout->labelsize + 8; - w = layout->labelsize * 4 + 8 + layout->labelsize; // make room for the light - Fd_Snap_Action::better_size(w, h); - } - const char *type_name() FL_OVERRIDE { return "Fl_Light_Button"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::LightButton"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { - return new Fl_Light_Button(x, y, w, h, "Button"); - } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Light_Button_Type(); } - ID id() const FL_OVERRIDE { return ID_Light_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Light_Button) ? true : super::is_a(inID); } -}; - -Fl_Light_Button_Type Fl_Light_Button_type; - - -// ---- Check Button ---- - -/** - \brief Manage buttons with a check mark on its left. - */ -class Fl_Check_Button_Type : public Fl_Button_Type -{ - typedef Fl_Button_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE { - h = layout->labelsize + 8; - w = layout->labelsize * 4 + 8 + layout->labelsize; // make room for the symbol - Fd_Snap_Action::better_size(w, h); - } - const char *type_name() FL_OVERRIDE { return "Fl_Check_Button"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::CheckButton"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { - return new Fl_Check_Button(x, y, w, h, "Button"); - } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Check_Button_Type(); } - ID id() const FL_OVERRIDE { return ID_Check_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Check_Button) ? true : super::is_a(inID); } -}; - -Fl_Check_Button_Type Fl_Check_Button_type; - - -// ---- Round Button ---- - -/** - \brief Manage buttons with a round indicator on its left. - */ -class Fl_Round_Button_Type : public Fl_Button_Type -{ - typedef Fl_Button_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE { - h = layout->labelsize + 8; - w = layout->labelsize * 4 + 8 + layout->labelsize; // make room for the symbol - Fd_Snap_Action::better_size(w, h); - } - const char *type_name() FL_OVERRIDE { return "Fl_Round_Button"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::RadioButton"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { - return new Fl_Round_Button(x, y, w, h, "Button"); - } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Round_Button_Type(); } - ID id() const FL_OVERRIDE { return ID_Round_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Round_Button) ? true : super::is_a(inID); } -}; - -Fl_Round_Button_Type Fl_Round_Button_type; - diff --git a/fluid/nodes/Fl_Button_Type.h b/fluid/nodes/Fl_Button_Type.h deleted file mode 100644 index 4229ac82d..000000000 --- a/fluid/nodes/Fl_Button_Type.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// Button type header file for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 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 _FL_BUTTON_TYPE_H -#define _FL_BUTTON_TYPE_H - -#include "nodes/Fl_Widget_Type.h" - -/** - \brief A handler for the simple push button and a base class for all other buttons. - */ -class Fl_Button_Type : public Fl_Widget_Type -{ - typedef Fl_Widget_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE; - const char *type_name() FL_OVERRIDE { return "Fl_Button"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Button"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE; - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Button_Type(); } - int is_button() const FL_OVERRIDE { return 1; } - ID id() const FL_OVERRIDE { return ID_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Button) ? true : super::is_a(inID); } - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; -}; - -extern Fl_Button_Type Fl_Button_type; - - -#endif // _FL_BUTTON_TYPE_H diff --git a/fluid/nodes/Fl_Function_Type.cxx b/fluid/nodes/Fl_Function_Type.cxx deleted file mode 100644 index 5778324bf..000000000 --- a/fluid/nodes/Fl_Function_Type.cxx +++ /dev/null @@ -1,2157 +0,0 @@ -// -// C function type code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 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 "nodes/Fl_Function_Type.h" - -#include "app/fluid.h" -#include "app/mergeback.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "nodes/Fl_Window_Type.h" -#include "nodes/Fl_Group_Type.h" -#include "panels/function_panel.h" -#include "rsrcs/comments.h" -#include "widgets/Node_Browser.h" - -#include -#include -#include -#include "../src/flstring.h" - -#include - - -/// Set a current class, so that the code of the children is generated correctly. -Fl_Class_Type *current_class = NULL; - -/** - \brief Return 1 if the list contains a function with the given signature at the top level. - Fl_Widget_Type uses this to check if a callback by a certain signature is - already defined by the user within this file. If not, Fl_Widget_Type will - generate an `extern $sig$;` statement. - \param[in] rtype return type, can be NULL to avoid checking (not used by Fl_Widget_Type) - \param[in] sig function signature - \return 1 if found. - */ -int has_toplevel_function(const char *rtype, const char *sig) { - Fl_Type *child; - for (child = Fl_Type::first; child; child = child->next) { - if (!child->is_in_class() && child->is_a(ID_Function)) { - const Fl_Function_Type *fn = (const Fl_Function_Type*)child; - if (fn->has_signature(rtype, sig)) - return 1; - } - } - return 0; -} - - -//////////////////////////////////////////////////////////////// -// quick check of any C code for legality, returns an error message - -static char buffer[128]; // for error messages - -/** - Check a quoted string contains a character. - This is used to find a matching " or ' in a string. - \param[inout] c start searching here, return where we found \c type - \param[in] type find this character - \return NULL if the character was found, else a pointer to a static string - with an error message - */ -const char *_q_check(const char * & c, int type) { - for (;;) switch (*c++) { - case '\0': - sprintf(buffer,"missing %c",type); - return buffer; - case '\\': - if (*c) c++; - break; - default: - if (*(c-1) == type) return 0; - } -} - -/** - Check normal code, match brackets and parenthesis. - Recursively run a line of code and make sure that - {, [, ", ', and ( are matched. - \param[inout] c start searching here, return the end of the search - \param[in] type find this character match - \return NULL if the character was found, else a pointer to a static string - with an error message - */ -const char *_c_check(const char * & c, int type) { - const char *d; - for (;;) switch (*c++) { - case 0: - if (!type) return 0; - sprintf(buffer, "missing '%c'", type); - return buffer; - case '/': - // Skip comments as needed... - if (*c == '/') { - while (*c != '\n' && *c) c++; - } else if (*c == '*') { - c++; - while ((*c != '*' || c[1] != '/') && *c) c++; - if (*c == '*') c+=2; - else { - return "missing '*/'"; - } - } - break; -// case '#': -// // treat cpp directives as a comment: -// // Matt: a '#' character can appear as a concatenation when defining macros -// // Matt: so instead we just silently ignore the '#' -// while (*c != '\n' && *c) c++; -// break; - case '{': - if (type==')') goto UNEXPECTED; - d = _c_check(c,'}'); - if (d) return d; - break; - case '(': - d = _c_check(c,')'); - if (d) return d; - break; - case '[': - d = _c_check(c,']'); - if (d) return d; - break; - case '\"': - d = _q_check(c,'\"'); - if (d) return d; - break; - case '\'': - d = _q_check(c,'\''); - if (d) return d; - break; - case '}': - case ')': - case ']': - UNEXPECTED: - if (type == *(c-1)) return 0; - sprintf(buffer, "unexpected '%c'", *(c-1)); - return buffer; - } -} - -/** - Check legality of c code (sort of) and return error: - Make sure that {, ", ', and ( are matched. - \param[in] c start searching here - \param[in] type find this character match (default is 0) - \return NULL if the character was found, else a pointer to a static string - with an error message - \note This function checks every conceivable line of code, which is not - always wanted. It can't differentiate characters in comments, and the - user may well intend to leave a curly bracket open - (i.e. namespace { ... } ). We should make this option user selectable. - */ -const char *c_check(const char *c, int type) { - return _c_check(c,type); -} - -// ---- Fl_Function_Type implementation - -/** \class Fl_Function_Type - Manage a C++ function node in the Fluid design. - - A function can have a signature (name followed by arguments), a return type - and a comment section. If can be local or global, and it can be declared a C - or C++ function. - */ - -/// Prototype for a function to be used by the factory. -Fl_Function_Type Fl_Function_type; - -/** - Create a new function. - */ -Fl_Function_Type::Fl_Function_Type() : - Fl_Type(), - return_type(0L), - public_(0), - cdecl_(0), - constructor(0), - havewidgets(0) -{ } - -/** - Destructor. - */ -Fl_Function_Type::~Fl_Function_Type() { - if (return_type) free((void*)return_type); -} - -/** - Create a new function for the widget tree. - \param[in] strategy add new function after current or as last child - \return the new node - */ -Fl_Type *Fl_Function_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_decl_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_Function_Type *o = new Fl_Function_Type(); - o->name("make_window()"); - o->return_type = 0; - o->add(anchor, strategy); - o->factory = this; - o->public_ = 1; - o->cdecl_ = 0; - return o; -} - -/** - Write function specific properties to an .fl file. - - "private"/"public" indicates the state of the function - - "C" is written if we want a C signature instead of C++ - - "return_type" is followed by the return type of the function - */ -void Fl_Function_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - switch (public_) { - case 0: f.write_string("private"); break; - case 2: f.write_string("protected"); break; - } - if (cdecl_) f.write_string("C"); - if (return_type) { - f.write_string("return_type"); - f.write_word(return_type); - } -} - -/** - Read function specific properties fron an .fl file. - \param[in] c read from this string - */ -void Fl_Function_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"private")) { - public_ = 0; - } else if (!strcmp(c,"protected")) { - public_ = 2; - } else if (!strcmp(c,"C")) { - cdecl_ = 1; - } else if (!strcmp(c,"return_type")) { - storestring(f.read_word(),return_type); - } else { - Fl_Type::read_property(f, c); - } -} - -/** - Open the function_panel dialog box to edit this function. - */ -void Fl_Function_Type::open() { - // fill dialog box - if (!function_panel) make_function_panel(); - f_return_type_input->value(return_type); - f_name_input->value(name()); - if (is_in_class()) { - f_public_member_choice->value(public_); - f_public_member_choice->show(); - f_public_choice->hide(); - f_c_button->hide(); - } else { - f_public_choice->value(public_); - f_public_choice->show(); - f_public_member_choice->hide(); - f_c_button->show(); - } - f_c_button->value(cdecl_); - const char *c = comment(); - f_comment_input->buffer()->text(c?c:""); - function_panel->show(); - const char* message = 0; - for (;;) { // repeat as long as there are errors - // - message loop until OK or cancel is pressed - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == f_panel_cancel) goto BREAK2; - else if (w == f_panel_ok) break; - else if (!w) Fl::wait(); - } - // - check syntax - const char *c = f_name_input->value(); - while (isspace(*c)) c++; - message = c_check(c); - if (!message) { - const char *d = c; - for (; *d != '('; d++) if (isspace(*d) || !*d) break; - if (*c && *d != '(') - message = "must be 'name(arguments)'"; - } - if (!message) { - c = f_return_type_input->value(); - message = c_check(c); - } - // - alert user - if (message) { - int v = fl_choice("Potential syntax error detected: %s", - "Continue Editing", "Ignore Error", NULL, message); - if (v==0) continue; // Continue Editing - //if (v==1) { } // Ignore Error and close dialog - } - // - copy dialog data to target variables - int mod = 0; - name(f_name_input->value()); - storestring(f_return_type_input->value(), return_type); - if (is_in_class()) { - if (public_ != f_public_member_choice->value()) { - mod = 1; - public_ = f_public_member_choice->value(); - redraw_browser(); - } - } else { - if (public_ != f_public_choice->value()) { - mod = 1; - public_ = f_public_choice->value(); - redraw_browser(); - } - } - if (cdecl_ != f_c_button->value()) { - mod = 1; - cdecl_ = f_c_button->value(); - } - c = f_comment_input->buffer()->text(); - if (c && *c) { - if (!comment() || strcmp(c, comment())) { set_modflag(1); redraw_browser(); } - comment(c); - } else { - if (comment()) { set_modflag(1); redraw_browser(); } - comment(0); - } - if (c) free((void*)c); - if (mod) set_modflag(1); - break; - } -BREAK2: - function_panel->hide(); -} - -/** - Return 1 if the function is global. - \return 1 if public, 0 if local. - */ -int Fl_Function_Type::is_public() const { - return public_; -} - -static bool fd_isspace(int c) { - return (c>0 && c<128 && isspace(c)); -} - -// code duplication: see int is_id(char c) in code.cxx -static bool fd_iskeyword(int c) { - return (c>0 && c<128 && (isalnum(c) || c=='_')); -} - -// remove all function default parameters and `override` keyword -static void clean_function_for_implementation(char *out, const char *function_name) { - char *sptr = out; - const char *nptr = function_name; - int skips=0,skipc=0; - int nc=0,plevel=0; - bool arglist_done = false; - for (;*nptr; nc++,nptr++) { - if (arglist_done && fd_isspace(nptr[0])) { - // skip `override` and `FL_OVERRIDE` keywords if they are following the list of arguments - if (strncmp(nptr+1, "override", 8)==0 && !fd_iskeyword(nptr[9])) { nptr += 8; continue; } - else if (strncmp(nptr+1, "FL_OVERRIDE", 11)==0 && !fd_iskeyword(nptr[12])) { nptr += 11; continue; } - } - if (!skips && *nptr=='(') plevel++; - else if (!skips && *nptr==')') { plevel--; if (plevel==0) arglist_done = true; } - if ( *nptr=='"' && !(nc && *(nptr-1)=='\\') ) - skips = skips ? 0 : 1; - else if(!skips && *nptr=='\'' && !(nc && *(nptr-1)=='\\')) - skipc = skipc ? 0 : 1; - if(!skips && !skipc && plevel==1 && *nptr =='=' && !(nc && *(nptr-1)=='\'') ) { // ignore '=' case - while(*++nptr && (skips || skipc || ( (*nptr!=',' && *nptr!=')') || plevel!=1) )) { - if ( *nptr=='"' && *(nptr-1)!='\\' ) - skips = skips ? 0 : 1; - else if(!skips && *nptr=='\'' && *(nptr-1)!='\\') - skipc = skipc ? 0 : 1; - if (!skips && !skipc && *nptr=='(') plevel++; - else if (!skips && *nptr==')') plevel--; - } - if (*nptr==')') if (--plevel==0) arglist_done = true; - } - if (sptr < (out + 1024 - 1)) *sptr++ = *nptr; - } - *sptr = '\0'; -} - - -/** - Write the code for the source and the header file. - This writes the code that goes \b before all children of this class. - \see write_code2(fld::io::Code_Writer& f) - */ -void Fl_Function_Type::write_code1(fld::io::Code_Writer& f) { - constructor=0; - havewidgets = 0; - Fl_Type *child; - // if the function has no children (hence no body), Fluid will not generate - // the function either. This is great if you decide to implement that function - // inside another module - char havechildren = 0; - for (child = next; child && child->level > level; child = child->next) { - havechildren = 1; - if (child->is_widget()) { - havewidgets = 1; - break; - } - } - if (havechildren) - f.write_c("\n"); - if (ismain()) { - if (havechildren) - f.write_c("int main(int argc, char **argv) {\n"); - } else { - const char* rtype = return_type; - const char* star = ""; - // from matt: let the user type "static " at the start of type - // in order to declare a static method; - int is_static = 0; - int is_virtual = 0; - if (rtype) { - if (!strcmp(rtype,"static")) {is_static = 1; rtype = 0;} - else if (!strncmp(rtype, "static ",7)) {is_static = 1; rtype += 7;} - } - if (rtype) { - if (!strcmp(rtype, "virtual")) {is_virtual = 1; rtype = 0;} - else if (!strncmp(rtype, "virtual ",8)) {is_virtual = 1; rtype += 8;} - } - if (!rtype) { - if (havewidgets) { - rtype = subclassname(child); - star = "*"; - } else rtype = "void"; - } - - const char* k = class_name(0); - if (k) { - f.write_public(public_); - if (havechildren) - write_comment_c(f); - if (name()[0] == '~') - constructor = 1; - else { - size_t n = strlen(k); - if (!strncmp(name(), k, n) && name()[n] == '(') constructor = 1; - } - f.write_h("%s", f.indent(1)); - if (is_static) f.write_h("static "); - if (is_virtual) f.write_h("virtual "); - if (!constructor) { - f.write_h("%s%s ", rtype, star); - if (havechildren) - f.write_c("%s%s ", rtype, star); - } - - // if this is a subclass, only f.write_h() the part before the ':' - char s[1024], *sptr = s; - char *nptr = (char *)name(); - - while (*nptr) { - if (*nptr == ':') { - if (nptr[1] != ':') break; - // Copy extra ":" for "class::member"... - *sptr++ = *nptr++; - } - *sptr++ = *nptr++; - } - *sptr = '\0'; - - if (s[strlen(s)-1] == '}') { // special case for inlined functions - f.write_h("%s\n", s); - } else { - f.write_h("%s;\n", s); - } - if (havechildren) { - clean_function_for_implementation(s, name()); - f.write_c("%s::%s {\n", k, s); - } - } else { - if (havechildren) - write_comment_c(f); - if (public_==1) { - if (cdecl_) - f.write_h("extern \"C\" { %s%s %s; }\n", rtype, star, name()); - else - f.write_h("%s%s %s;\n", rtype, star, name()); - } else if (public_==2) { - // write neither the prototype nor static, the function may be declared elsewhere - } else { - if (havechildren) - f.write_c("static "); - } - - // write everything but the default parameters (if any) - char s[1024]; - if (havechildren) { - clean_function_for_implementation(s, name()); - f.write_c("%s%s %s {\n", rtype, star, s); - } - } - } - - if (havewidgets && child && !child->name()) - f.write_c("%s%s* w;\n", f.indent(1), subclassname(child)); - f.indentation++; -} - -/** - Write the code for the source and the header file. - This writes the code that goes \b after all children of this class. - \see write_code1(fld::io::Code_Writer& f) - */ -void Fl_Function_Type::write_code2(fld::io::Code_Writer& f) { - Fl_Type *child; - const char *var = "w"; - char havechildren = 0; - for (child = next; child && child->level > level; child = child->next) { - havechildren = 1; - if (child->is_a(ID_Window) && child->name()) var = child->name(); - } - - if (ismain()) { - if (havewidgets) - f.write_c("%s%s->show(argc, argv);\n", f.indent(1), var); - if (havechildren) - f.write_c("%sreturn Fl::run();\n", f.indent(1)); - } else if (havewidgets && !constructor && !return_type) { - f.write_c("%sreturn %s;\n", f.indent(1), var); - } - if (havechildren) - f.write_c("}\n"); - f.indentation = 0; -} - -/** - Check if the return type and signature s match. - \param[in] rtype function return type - \param[in] sig function name followed by arguments - \return 1 if they match, 0 if not - */ -int Fl_Function_Type::has_signature(const char *rtype, const char *sig) const { - if (rtype && !return_type) return 0; - if (!name()) return 0; - if ( (rtype==0L || strcmp(return_type, rtype)==0) - && fl_filename_match(name(), sig)) { - return 1; - } - return 0; -} - -// ---- Fl_Code_Type declaration - -/** \class Fl_Code_Type - Manage a block of C++ code in the Fluid design. - - This node manages an arbitrary block of code inside a function that will - be written into the source code file. Fl_Code_Block has no comment field. - However, the first line of code will be shown in the widget browser. - */ - -/// Prototype for code to be used by the factory. -Fl_Code_Type Fl_Code_type; - -/** - Constructor. - */ -Fl_Code_Type::Fl_Code_Type() : - cursor_position_(0), - code_input_scroll_row(0), - code_input_scroll_col(0) -{} - -/** - Make a new code node. - If the parent node is not a function, a message box will pop up and - the request will be ignored. - \param[in] strategy add code after current or as last child - \return new Code node - */ -Fl_Type *Fl_Code_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_code_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - if (!p) { - fl_message("Please select a function"); - return 0; - } - Fl_Code_Type *o = new Fl_Code_Type(); - o->name("printf(\"Hello, World!\\n\");"); - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Open the code_panel or an external editor to edit this code section. - */ -void Fl_Code_Type::open() { - // Using an external code editor? Open it.. - if ( G_use_external_editor && G_external_editor_command[0] ) { - const char *cmd = G_external_editor_command; - const char *code = name(); - if (!code) code = ""; - if ( editor_.open_editor(cmd, code) == 0 ) - return; // return if editor opened ok, fall thru to built-in if not - } - // Use built-in code editor.. - if (!code_panel) make_code_panel(); - const char *text = name(); - code_input->buffer()->text( text ? text : "" ); - code_input->insert_position(cursor_position_); - code_input->scroll(code_input_scroll_row, code_input_scroll_col); - code_panel->show(); - const char* message = 0; - for (;;) { // repeat as long as there are errors - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == code_panel_cancel) goto BREAK2; - else if (w == code_panel_ok) break; - else if (!w) Fl::wait(); - } - char*c = code_input->buffer()->text(); - message = c_check(c); - if (message) { - int v = fl_choice("Potential syntax error detected: %s", - "Continue Editing", "Ignore Error", NULL, message); - if (v==0) continue; // Continue Editing - //if (v==1) { } // Ignore Error and close dialog - } - name(c); - free(c); - break; - } - cursor_position_ = code_input->insert_position(); - code_input_scroll_row = code_input->scroll_row(); - code_input_scroll_col = code_input->scroll_col(); -BREAK2: - code_panel->hide(); -} - -/** - Grab changes from an external editor and write this node. - */ -void Fl_Code_Type::write(fld::io::Project_Writer &f) { - // External editor changes? If so, load changes into ram, update mtime/size - if ( handle_editor_changes() == 1 ) { - main_window->redraw(); // tell fluid to redraw; edits may affect tree's contents - } - Fl_Type::write(f); -} - -/** - Write the code block with the correct indentation. - */ -void Fl_Code_Type::write_code1(fld::io::Code_Writer& f) { - // External editor changes? If so, load changes into ram, update mtime/size - if ( handle_editor_changes() == 1 ) { - main_window->redraw(); // tell fluid to redraw; edits may affect tree's contents - } - // Matt: disabled f.tag(FD_TAG_GENERIC, 0); - f.write_c_indented(name(), 0, '\n'); - // Matt: disabled f.tag(FD_TAG_CODE, get_uid()); -} - -/** - See if external editor is open. - */ -int Fl_Code_Type::is_editing() { - return editor_.is_editing(); -} - -/** - Reap the editor's pid - \return -2: editor not open - \return -1: wait failed - \return 0: process still running - \return \>0: process finished + reaped (returns pid) - */ -int Fl_Code_Type::reap_editor() { - return editor_.reap_editor(); -} - -/** - Handle external editor file modifications. - If changed, record keeping is updated and file's contents is loaded into ram - \return 0: file unchanged or not editing - \return 1: file changed, internal records updated, 'code' has new content - \return -1: error getting file info (get_ms_errmsg() has reason) - \todo Figure out how saving a fluid file can be intercepted to grab - current contents of editor file.. - */ -int Fl_Code_Type::handle_editor_changes() { - const char *newcode = 0; - switch ( editor_.handle_changes(&newcode) ) { - case 1: { // (1)=changed - name(newcode); // update value in ram - free((void*)newcode); - return 1; - } - case -1: return -1; // (-1)=error -- couldn't read file (dialog showed reason) - default: break; // (0)=no change - } - return 0; -} - -// ---- Fl_CodeBlock_Type implementation - -/** \class Fl_CodeBlock_Type - Manage two blocks of C++ code enclosing its children. - - This node manages two lines of code that enclose all children - of this node. This is usually an if..then clause. - - \todo this node could support multiple lines of code for each block. - */ - -/// Prototype for a block of code to be used by the factory. -Fl_CodeBlock_Type Fl_CodeBlock_type; - -/** - Constructor. - */ -Fl_CodeBlock_Type::Fl_CodeBlock_Type() : - Fl_Type(), - after(NULL) -{ } - -/** - Destructor. - */ -Fl_CodeBlock_Type::~Fl_CodeBlock_Type() { - if (after) - free((void*)after); -} - -/** - Make a new code block. - If the parent node is not a function or another codeblock, a message box will - pop up and the request will be ignored. - \param[in] strategy add after current or as last child - \return new CodeBlock - */ -Fl_Type *Fl_CodeBlock_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_code_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - if (!p) { - fl_message("Please select a function"); - return 0; - } - Fl_CodeBlock_Type *o = new Fl_CodeBlock_Type(); - o->name("if (test())"); - o->after = 0; - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Write the specific properties for this node. - - "after" is followed by the code that comes after the children - The "before" code is stored in the name() field. - */ -void Fl_CodeBlock_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - if (after) { - f.write_string("after"); - f.write_word(after); - } -} - -/** - Read the node specific properties. - */ -void Fl_CodeBlock_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"after")) { - storestring(f.read_word(),after); - } else { - Fl_Type::read_property(f, c); - } -} - -/** - Open the codeblock_panel. - */ -void Fl_CodeBlock_Type::open() { - if (!codeblock_panel) make_codeblock_panel(); - code_before_input->value(name()); - code_after_input->value(after); - codeblock_panel->show(); - const char* message = 0; - for (;;) { // repeat as long as there are errors - // event loop - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == codeblock_panel_cancel) goto BREAK2; - else if (w == codeblock_panel_ok) break; - else if (!w) Fl::wait(); - } - // check for syntax errors - message = c_check(code_before_input->value()); - if (!message) { - message = c_check(code_after_input->value()); - } - // alert user - if (message) { - int v = fl_choice("Potential syntax error detected: %s", - "Continue Editing", "Ignore Error", NULL, message); - if (v==0) continue; // Continue Editing - //if (v==1) { } // Ignore Error and close dialog - } - // write to variables - name(code_before_input->value()); - storestring(code_after_input->value(), after); - break; - } -BREAK2: - codeblock_panel->hide(); -} - -/** - Write the "before" code. - */ -void Fl_CodeBlock_Type::write_code1(fld::io::Code_Writer& f) { - const char* c = name(); - f.write_c("%s%s {\n", f.indent(), c ? c : ""); - f.indentation++; -} - -/** - Write the "after" code. - */ -void Fl_CodeBlock_Type::write_code2(fld::io::Code_Writer& f) { - f.indentation--; - if (after) f.write_c("%s} %s\n", f.indent(), after); - else f.write_c("%s}\n", f.indent()); -} - -// ---- Fl_Decl_Type declaration - -/** \class Fl_Decl_Type - Manage the C/C++ declaration of a variable. - - This node manages a single line of code that can be in the header or the source - code, and can be made static. - - \todo this node could support multiple lines. - */ - -/// Prototype for a declaration to be used by the factory. -Fl_Decl_Type Fl_Decl_type; - -/** - Constructor. - */ -Fl_Decl_Type::Fl_Decl_Type() : - public_(0), - static_(1) -{ } - -/** - Return 1 if this declaration and its parents are public. - */ -int Fl_Decl_Type::is_public() const -{ - Fl_Type *p = parent; - while (p && !p->is_decl_block()) p = p->parent; - if(p && p->is_public() && public_) - return public_; - else if(!p) - return public_; - return 0; -} - -/** - Make a new declaration. - \param[in] strategy add after current or as last child - \return new Declaration node - */ -Fl_Type *Fl_Decl_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_decl_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_Decl_Type *o = new Fl_Decl_Type(); - o->public_ = 0; - o->static_ = 1; - o->name("int x;"); - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Write the specific properties. - - "private"/"public"/"protected" - - "local"/"global" if this is static or not - */ -void Fl_Decl_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - switch (public_) { - case 0: f.write_string("private"); break; - case 1: f.write_string("public"); break; - case 2: f.write_string("protected"); break; - } - if (static_) - f.write_string("local"); - else - f.write_string("global"); -} - -/** - Read the specific properties. - */ -void Fl_Decl_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"public")) { - public_ = 1; - } else if (!strcmp(c,"private")) { - public_ = 0; - } else if (!strcmp(c,"protected")) { - public_ = 2; - } else if (!strcmp(c,"local")) { - static_ = 1; - } else if (!strcmp(c,"global")) { - static_ = 0; - } else { - Fl_Type::read_property(f, c); - } -} - -/** - Open the decl_panel to edit this node. - */ -void Fl_Decl_Type::open() { - if (!decl_panel) make_decl_panel(); - decl_input->buffer()->text(name()); - if (is_in_class()) { - decl_class_choice->value(public_); - decl_class_choice->show(); - decl_choice->hide(); - } else { - decl_choice->value((public_&1)|((static_&1)<<1)); - decl_choice->show(); - decl_class_choice->hide(); - } - const char *c = comment(); - decl_comment_input->buffer()->text(c?c:""); - decl_panel->show(); - const char* message = 0; - for (;;) { // repeat as long as there are errors - // event loop - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == decl_panel_cancel) goto BREAK2; - else if (w == decl_panel_ok) break; - else if (!w) Fl::wait(); - } - // check values - const char*c = decl_input->buffer()->text(); - while (isspace(*c)) c++; - message = c_check(c&&c[0]=='#' ? c+1 : c); - // alert user - if (message) { - int v = fl_choice("Potential syntax error detected: %s", - "Continue Editing", "Ignore Error", NULL, message); - if (v==0) continue; // Continue Editing - //if (v==1) { } // Ignore Error and close dialog - } - // copy vlaues - name(c); - if (is_in_class()) { - if (public_!=decl_class_choice->value()) { - set_modflag(1); - public_ = decl_class_choice->value(); - } - } else { - if (public_!=(decl_choice->value()&1)) { - set_modflag(1); - public_ = (decl_choice->value()&1); - } - if (static_!=((decl_choice->value()>>1)&1)) { - set_modflag(1); - static_ = ((decl_choice->value()>>1)&1); - } - } - c = decl_comment_input->buffer()->text(); - if (c && *c) { - if (!comment() || strcmp(c, comment())) { set_modflag(1); redraw_browser(); } - comment(c); - } else { - if (comment()) { set_modflag(1); redraw_browser(); } - comment(0); - } - if (c) free((void*)c); - break; - } -BREAK2: - decl_panel->hide(); -} - -/** - Write the code to the source and header files. - \todo There are a lot of side effect in this node depending on the given text - and the parent node. They need to be understood and documented. - */ -void Fl_Decl_Type::write_code1(fld::io::Code_Writer& f) { - const char* c = name(); - if (!c) return; - // handle a few keywords differently if inside a class - if (is_in_class() && ( (!strncmp(c,"class",5) && isspace(c[5])) - || (!strncmp(c,"typedef",7) && isspace(c[7])) - || (!strncmp(c,"FL_EXPORT",9) && isspace(c[9])) - || (!strncmp(c,"struct",6) && isspace(c[6])) - || (!strncmp(c,"enum",4) && isspace(c[4])) - ) ) { - f.write_public(public_); - write_comment_h(f, f.indent(1)); - f.write_h("%s%s\n", f.indent(1), c); - return; - } - // handle putting #include, extern, using or typedef into decl: - if ( (!isalpha(*c) && *c != '~') - || (!strncmp(c,"extern",6) && isspace(c[6])) - || (!strncmp(c,"class",5) && isspace(c[5])) - || (!strncmp(c,"typedef",7) && isspace(c[7])) - || (!strncmp(c,"using",5) && isspace(c[5])) - || (!strncmp(c,"FL_EXPORT",9) && isspace(c[9])) - // || !strncmp(c,"struct",6) && isspace(c[6]) - ) { - if (public_) { - write_comment_h(f); - f.write_h("%s\n", c); - } else { - write_comment_c(f); - f.write_c("%s\n", c); - } - return; - } - // find the first C++ style comment - const char* e = c+strlen(c), *csc = c; - while (cscc && e[-1]==' ') e--; - if (class_name(1)) { - f.write_public(public_); - write_comment_h(f, f.indent(1)); - f.write_hc(f.indent(1), int(e-c), c, csc); - } else { - if (public_) { - if (static_) - f.write_h("extern "); - else - write_comment_h(f); - f.write_hc("", int(e-c), c, csc); - - if (static_) { - write_comment_c(f); - f.write_cc("", int(e-c), c, csc); - } - } else { - write_comment_c(f); - if (static_) - f.write_c("static "); - f.write_cc("", int(e-c), c, csc); - } - } -} - -// ---- Fl_Data_Type declaration - -/** \class Fl_Data_Type - Manage data from an external arbitrary file. - - The content of the file will be stored in binary inside the generated - code. This can be used to store images inline in the source code, - */ - -/// Prototype for a data node to be used by the factory. -Fl_Data_Type Fl_Data_type; - -/** - Constructor. - */ -Fl_Data_Type::Fl_Data_Type() : - Fl_Decl_Type(), - filename_(NULL), - text_mode_(0) -{ } - -/** - Destructor. - */ -Fl_Data_Type::~Fl_Data_Type() { - if (filename_) - free((void*)filename_); -} - -/** - Create an empty inline data node. - \param[in] strategy add after current or as last child - \return new inline data node - */ -Fl_Type *Fl_Data_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_decl_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_Data_Type *o = new Fl_Data_Type(); - o->public_ = 1; - o->static_ = 1; - o->filename_ = 0; - o->text_mode_ = 0; - o->name("myInlineData"); - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Write additional properties. - - "filename" followed by the filename of the file to inline - - "textmode" if data is written in ASCII vs. binary - */ -void Fl_Data_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Decl_Type::write_properties(f); - if (filename_) { - f.write_string("filename"); - f.write_word(filename_); - } - if (text_mode_ == 1) { - f.write_string("textmode"); - } - if (text_mode_ == 2) { - f.write_string("compressed"); - } -} - -/** - Read specific properties. - */ -void Fl_Data_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"filename")) { - storestring(f.read_word(), filename_, 1); - } else if (!strcmp(c,"textmode")) { - text_mode_ = 1; - } else if (!strcmp(c,"compressed")) { - text_mode_ = 2; - } else { - Fl_Decl_Type::read_property(f, c); - } -} - -/** - Open the data_panel to edit this node. - */ -void Fl_Data_Type::open() { - if (!data_panel) make_data_panel(); - data_input->value(name()); - if (is_in_class()) { - data_class_choice->value(public_); - data_class_choice->show(); - data_choice->hide(); - } else { - data_choice->value((public_&1)|((static_&1)<<1)); - data_choice->show(); - data_class_choice->hide(); - } - data_mode->value(text_mode_); - data_filename->value(filename_?filename_:""); - const char *c = comment(); - data_comment_input->buffer()->text(c?c:""); - data_panel->show(); - for (;;) { // repeat as long as there are errors - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == data_panel_cancel) goto BREAK2; - else if (w == data_panel_ok) break; - else if (w == data_filebrowser) { - enter_project_dir(); - const char *fn = fl_file_chooser("Load Inline Data", 0L, data_filename->value(), 1); - leave_project_dir(); - if (fn) { - if (strcmp(fn, data_filename->value())) - set_modflag(1); - data_filename->value(fn); - } - } - else if (!w) Fl::wait(); - } - // store the variable name: - const char*c = data_input->value(); - char *s = fl_strdup(c), *p = s, *q, *n; - for (;;++p) { // remove leading spaces - if (!isspace((unsigned char)(*p))) break; - } - n = p; - if ( (!isalpha((unsigned char)(*p))) && ((*p)!='_') && ((*p)!=':') ) goto OOPS; - ++p; - for (;;++p) { - if ( (!isalnum((unsigned char)(*p))) && ((*p)!='_') && ((*p)!=':') ) break; - } - q = p; - for (;;++q) { - if (!*q) break; - if (!isspace((unsigned char)(*q))) goto OOPS; - } - *p = 0; // remove trailing spaces - if (n==q) { - OOPS: - int v = fl_choice("%s", - "Continue Editing", "Ignore Error", NULL, - "Variable name must be a C identifier"); - if (v==0) { free(s); continue; } // Continue Editing - //if (v==1) { } // Ignore Error and close dialog - } - undo_checkpoint(); - name(n); - free(s); - // store flags - if (is_in_class()) { - if (public_!=data_class_choice->value()) { - set_modflag(1); - public_ = data_class_choice->value(); - } - } else { - if (public_!=(data_choice->value()&1)) { - set_modflag(1); - public_ = (data_choice->value()&1); - } - if (static_!=((data_choice->value()>>1)&1)) { - set_modflag(1); - static_ = ((data_choice->value()>>1)&1); - } - } - text_mode_ = data_mode->value(); - if (text_mode_ < 0) text_mode_ = 0; - if (text_mode_ > 2) text_mode_ = 2; - // store the filename - c = data_filename->value(); - if (filename_ && strcmp(filename_, data_filename->value())) - set_modflag(1); - else if (!filename_ && *c) - set_modflag(1); - if (filename_) { free((void*)filename_); filename_ = 0L; } - if (c && *c) filename_ = fl_strdup(c); - // store the comment - c = data_comment_input->buffer()->text(); - if (c && *c) { - if (!comment() || strcmp(c, comment())) { set_modflag(1); redraw_browser(); } - comment(c); - } else { - if (comment()) { set_modflag(1); redraw_browser(); } - comment(0); - } - if (c) free((void*)c); - set_modflag(1); - break; - } -BREAK2: - data_panel->hide(); -} - -/** - Write the content of the external file inline into the source code. - */ -void Fl_Data_Type::write_code1(fld::io::Code_Writer& f) { - const char *message = 0; - const char *c = name(); - if (!c) return; - const char *fn = filename_; - char *data = 0; - int nData = -1; - int uncompressedDataSize = 0; - // path should be set correctly already - if (filename_ && !f.write_codeview) { - enter_project_dir(); - FILE *f = fl_fopen(filename_, "rb"); - leave_project_dir(); - if (!f) { - message = "Can't include data from file. Can't open"; - } else { - fseek(f, 0, SEEK_END); - nData = (int)ftell(f); - fseek(f, 0, SEEK_SET); - if (nData) { - data = (char*)calloc(nData, 1); - if (fread(data, nData, 1, f)==0) { /* use default */ } - if (text_mode_ == 2) { - uncompressedDataSize = nData; - uLong nzData = compressBound(nData); - Bytef *zdata = (Bytef*)::malloc(nzData); - if (compress(zdata, &nzData, (Bytef*)data, nData) != Z_OK) { /* error */ } - ::free(data); - data = (char*)zdata; - nData = (int)nzData; - } - } - fclose(f); - } - } else { - fn = filename_ ? filename_ : ""; - } - if (is_in_class()) { - f.write_public(public_); - if (text_mode_ == 1) { - f.write_h("%sstatic const char *%s;\n", f.indent(1), c); - f.write_c("\n"); - write_comment_c(f); - f.write_c("const char *%s::%s = /* text inlined from %s */\n", class_name(1), c, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cstring(data, nData); - } else if (text_mode_ == 2) { - f.write_h("%sstatic int %s_size;\n", f.indent(1), c); - f.write_h("%sstatic unsigned char %s[%d];\n", f.indent(1), c, nData); - f.write_c("\n"); - write_comment_c(f); - f.write_c("int %s::%s_size = %d;\n", class_name(1), c, uncompressedDataSize); - f.write_c("unsigned char %s::%s[%d] = /* data compressed and inlined from %s */\n", class_name(1), c, nData, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cdata(data, nData); - } else { - f.write_h("%sstatic unsigned char %s[%d];\n", f.indent(1), c, nData); - f.write_c("\n"); - write_comment_c(f); - f.write_c("unsigned char %s::%s[%d] = /* data inlined from %s */\n", class_name(1), c, nData, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cdata(data, nData); - } - f.write_c(";\n"); - } else { - // the "header only" option does not apply here! - if (public_) { - if (static_) { - if (text_mode_ == 1) { - f.write_h("extern const char *%s;\n", c); - f.write_c("\n"); - write_comment_c(f); - f.write_c("const char *%s = /* text inlined from %s */\n", c, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cstring(data, nData); - } else if (text_mode_ == 2) { - f.write_h("extern int %s_size;\n", c); - f.write_h("extern unsigned char %s[%d];\n", c, nData); - f.write_c("\n"); - write_comment_c(f); - f.write_c("int %s_size = %d;\n", c, uncompressedDataSize); - f.write_c("unsigned char %s[%d] = /* data compressed and inlined from %s */\n", c, nData, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cdata(data, nData); - } else { - f.write_h("extern unsigned char %s[%d];\n", c, nData); - f.write_c("\n"); - write_comment_c(f); - f.write_c("unsigned char %s[%d] = /* data inlined from %s */\n", c, nData, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cdata(data, nData); - } - f.write_c(";\n"); - } else { - write_comment_h(f); - f.write_h("#error Unsupported declaration loading inline data %s\n", fn); - if (text_mode_ == 1) - f.write_h("const char *%s = \"abc...\";\n", c); - else - f.write_h("unsigned char %s[3] = { 1, 2, 3 };\n", c); - } - } else { - f.write_c("\n"); - write_comment_c(f); - if (static_) - f.write_c("static "); - if (text_mode_ == 1) { - f.write_c("const char *%s = /* text inlined from %s */\n", c, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cstring(data, nData); - } else if (text_mode_ == 2) { - f.write_c("int %s_size = %d;\n", c, uncompressedDataSize); - if (static_) f.write_c("static "); - f.write_c("unsigned char %s[%d] = /* data compressed and inlined from %s */\n", c, nData, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cdata(data, nData); - } else { - f.write_c("unsigned char %s[%d] = /* data inlined from %s */\n", c, nData, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cdata(data, nData); - } - f.write_c(";\n"); - } - } - // if we are in interactive mode, we pop up a warning dialog - // giving the error: (batch_mode && !write_codeview) ??? - if (message && !f.write_codeview) { - if (batch_mode) - fprintf(stderr, "FLUID ERROR: %s %s\n", message, fn); - else - fl_alert("%s\n%s\n", message, fn); - } - if (data) free(data); -} - -// ---- Fl_DeclBlock_Type declaration - -/** \class Fl_DeclBlock_Type - Manage a declaration block. - - Declaration blocks have two text field that are written before and after - the children of this block. This block is located at the top level and - is written to the source file, and to the header file, if declared public. - */ - -/// Prototype for a declaration block to be used by the factory. -Fl_DeclBlock_Type Fl_DeclBlock_type; - -/** - Constructor. - */ -Fl_DeclBlock_Type::Fl_DeclBlock_Type() : - Fl_Type(), - after(NULL), - write_map_(CODE_IN_SOURCE) -{ } - -/** - Destructor. - */ -Fl_DeclBlock_Type::~Fl_DeclBlock_Type() { - if (after) - ::free((void*)after); -} - -/** - Return 1 if this block is public. - */ -int Fl_DeclBlock_Type::is_public() const { - return ((write_map_&CODE_IN_HEADER) != 0); -} - -/** - Create a new declaration block. - \param[in] strategy add after current or as last child - \return new Declaration Block node - */ -Fl_Type *Fl_DeclBlock_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) p = p->parent; - while (p && !p->is_decl_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_DeclBlock_Type *o = new Fl_DeclBlock_Type(); - o->name("#if 1"); - o->write_map_ = CODE_IN_SOURCE; - o->after = fl_strdup("#endif"); - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Write the specific properties. - - "public"/"protected" - - "after" followed by the second code block. - */ -void Fl_DeclBlock_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - // deprecated - if (is_public()) f.write_string("public"); - // new way to map declaration block to various parts of the generated code - if (write_map_ != CODE_IN_SOURCE) - f.write_string("map %d", write_map_); - f.write_string("after"); - f.write_word(after); -} - -/** - Read the specific properties. - */ -void Fl_DeclBlock_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if(!strcmp(c,"public")) { - write_map_ |= CODE_IN_HEADER; - } else if(!strcmp(c,"protected")) { - // - } else if(!strcmp(c,"map")) { - write_map_ = (int)atol(f.read_word()); - } else if (!strcmp(c,"after")) { - storestring(f.read_word(),after); - } else { - Fl_Type::read_property(f, c); - } -} - -/** - Open the declblock_panel to edit this node. - */ -void Fl_DeclBlock_Type::open() { - // build dialog box - if (!declblock_panel) make_declblock_panel(); - // preset all values - declblock_before_input->value(name()); - declblock_after_input->value(after); - declblock_static_header->value(write_map_ & STATIC_IN_HEADER); - declblock_static_source->value(write_map_ & STATIC_IN_SOURCE); - declblock_code_header->value(write_map_ & CODE_IN_HEADER); - declblock_code_source->value(write_map_ & CODE_IN_SOURCE); - const char *c = comment(); - declblock_comment_input->buffer()->text(c?c:""); - // show modal dialog and loop until satisfied - declblock_panel->show(); - const char* message = 0; - for (;;) { // repeat as long as there are errors - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == declblock_panel_cancel) goto BREAK2; - else if (w == declblock_panel_ok) break; - else if (!w) Fl::wait(); - } - // verify user input - const char* a = declblock_before_input->value(); - while (isspace(*a)) a++; - const char* b = declblock_after_input->value(); - while (isspace(*b)) b++; - message = c_check(a&&a[0]=='#' ? a+1 : a); - if (!message) - message = c_check(b&&b[0]=='#' ? b+1 : b); - if (message) { - int v = fl_choice("Potential syntax error detected: %s", - "Continue Editing", "Ignore Error", NULL, message); - if (v==0) continue; // Continue Editing - //if (v==1) { } // Ignore Error and close dialog - } - // store user choices in data structure - name(a); - storestring(b, after); - if (write_map_ & STATIC_IN_HEADER) { - if (declblock_static_header->value()==0) { - write_map_ &= ~STATIC_IN_HEADER; - set_modflag(1); - } - } else { - if (declblock_static_header->value()) { - write_map_ |= STATIC_IN_HEADER; - set_modflag(1); - } - } - if (write_map_ & STATIC_IN_SOURCE) { - if (declblock_static_source->value()==0) { - write_map_ &= ~STATIC_IN_SOURCE; - set_modflag(1); - } - } else { - if (declblock_static_source->value()) { - write_map_ |= STATIC_IN_SOURCE; - set_modflag(1); - } - } - if (write_map_ & CODE_IN_HEADER) { - if (declblock_code_header->value()==0) { - write_map_ &= ~CODE_IN_HEADER; - set_modflag(1); - } - } else { - if (declblock_code_header->value()) { - write_map_ |= CODE_IN_HEADER; - set_modflag(1); - } - } - if (write_map_ & CODE_IN_SOURCE) { - if (declblock_code_source->value()==0) { - write_map_ &= ~CODE_IN_SOURCE; - set_modflag(1); - } - } else { - if (declblock_code_source->value()) { - write_map_ |= CODE_IN_SOURCE; - set_modflag(1); - } - } - c = declblock_comment_input->buffer()->text(); - if (c && *c) { - if (!comment() || strcmp(c, comment())) { set_modflag(1); redraw_browser(); } - comment(c); - } else { - if (comment()) { set_modflag(1); redraw_browser(); } - comment(0); - } - if (c) free((void*)c); - break; - } -BREAK2: - declblock_panel->hide(); -} - -/** - Write the \b before static code to the source file, and to the header file if declared public. - The before code is stored in the name() field. - */ -void Fl_DeclBlock_Type::write_static(fld::io::Code_Writer& f) { - const char* c = name(); - if (c && *c) { - if (write_map_ & STATIC_IN_HEADER) - f.write_h("%s\n", c); - if (write_map_ & STATIC_IN_SOURCE) - f.write_c("%s\n", c); - } -} - -/** - Write the \b after static code to the source file, and to the header file if declared public. - */ -void Fl_DeclBlock_Type::write_static_after(fld::io::Code_Writer& f) { - const char* c = after; - if (c && *c) { - if (write_map_ & STATIC_IN_HEADER) - f.write_h("%s\n", c); - if (write_map_ & STATIC_IN_SOURCE) - f.write_c("%s\n", c); - } -} - -/** - Write the \b before code to the source file, and to the header file if declared public. - The before code is stored in the name() field. - */ -void Fl_DeclBlock_Type::write_code1(fld::io::Code_Writer& f) { - const char* c = name(); - if (c && *c) { - if (write_map_ & CODE_IN_HEADER) - f.write_h("%s\n", c); - if (write_map_ & CODE_IN_SOURCE) - f.write_c("%s\n", c); - } -} - -/** - Write the \b after code to the source file, and to the header file if declared public. - */ -void Fl_DeclBlock_Type::write_code2(fld::io::Code_Writer& f) { - const char* c = after; - if (c && *c) { - if (write_map_ & CODE_IN_HEADER) - f.write_h("%s\n", c); - if (write_map_ & CODE_IN_SOURCE) - f.write_c("%s\n", c); - } -} - -// ---- Fl_Comment_Type declaration - -/** \class Fl_Comment_Type - Manage a comment node. - - The comment field takes one or more lines of ASCII text. If the text starts - with a '/' and a '*', Fluid assumes that the text is already formatted. If not, - every line will be preceded with "// ". - */ - -/// Prototype for a comment node to be used by the factory. -Fl_Comment_Type Fl_Comment_type; - -/** - Constructor. - */ -Fl_Comment_Type::Fl_Comment_Type() : - in_c_(1), - in_h_(1), - style_(0) -{ } - -/** - Make a new comment node. - \param[in] strategy add after current or as last child - \return new Comment node - */ -Fl_Type *Fl_Comment_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_code_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_Comment_Type *o = new Fl_Comment_Type(); - o->in_c_ = 1; - o->in_h_ = 1; - o->style_ = 0; - o->name("my comment"); - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Write respective properties. - - "in_source"/"not_in_source" if the comment will be written to the source code - - "in_header"/"not_in_header" if the comment will be written to the header file - */ -void Fl_Comment_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - if (in_c_) f.write_string("in_source"); else f.write_string("not_in_source"); - if (in_h_) f.write_string("in_header"); else f.write_string("not_in_header"); -} - -/** - Read extra properties. - */ -void Fl_Comment_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"in_source")) { - in_c_ = 1; - } else if (!strcmp(c,"not_in_source")) { - in_c_ = 0; - } else if (!strcmp(c,"in_header")) { - in_h_ = 1; - } else if (!strcmp(c,"not_in_header")) { - in_h_ = 0; - } else { - Fl_Type::read_property(f, c); - } -} - -/** - Load available preset comments. - Fluid comes with GPL and LGPL preset for comments. Users can - add their own presets which are stored per user in a separate - preferences database. - */ -static void load_comments_preset(Fl_Preferences &menu) { - static const char * const predefined_comment[] = { - "GNU Public License v3/GPL Header", "GNU Public License v3/GPL Footer", - "GNU Public License v3/LGPL Header", "GNU Public License v3/LGPL Footer", - "FLTK/Header" }; - int i, n; - menu.get("n", n, -1); - if (n == -1) menu.set("n", 5); - menu.set("version", 10400); - Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); - for (i=0; i<5; i++) { - menu.set(Fl_Preferences::Name(i), predefined_comment[i]); - db.set(predefined_comment[i], comment_text[i]); - } -} - -/** - Open the comment_panel to edit this node. - */ -void Fl_Comment_Type::open() { - if (!comment_panel) make_comment_panel(); - const char *text = name(); - { - int i=0, n=0, version = 0; - Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu"); - comment_predefined->clear(); - comment_predefined->add("_Edit/Add current comment..."); - comment_predefined->add("_Edit/Remove last selection..."); - menu.get("version", version, -1); - if (version < 10400) load_comments_preset(menu); - menu.get("n", n, 0); - for (i=0;iadd(text); - free(text); - } - } - comment_input->buffer()->text( text ? text : "" ); - comment_in_source->value(in_c_); - comment_in_header->value(in_h_); - comment_panel->show(); - char itempath[FL_PATH_MAX]; itempath[0] = 0; - int last_selected_item = 0; - for (;;) { // repeat as long as there are errors - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == comment_panel_cancel) goto BREAK2; - else if (w == comment_panel_ok) break; - else if (w == comment_predefined) { - if (comment_predefined->value()==1) { - // add the current comment to the database - const char *xname = fl_input( - "Please enter a name to reference the current\ncomment in your database.\n\n" - "Use forward slashes '/' to create submenus.", - "My Comment"); - if (xname) { - char *name = fl_strdup(xname); - for (char*s=name;*s;s++) if (*s==':') *s = ';'; - int n; - Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); - db.set(name, comment_input->buffer()->text()); - Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu"); - menu.get("n", n, 0); - menu.set(Fl_Preferences::Name(n), name); - menu.set("n", ++n); - comment_predefined->add(name); - free(name); - } - } else if (comment_predefined->value()==2) { - // remove the last selected comment from the database - if (itempath[0]==0 || last_selected_item==0) { - fl_message("Please select an entry from this menu first."); - } else if (fl_choice("Are you sure that you want to delete the entry\n" - "\"%s\"\nfrom the database?", "Cancel", "Delete", - NULL, itempath)) { - Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); - db.deleteEntry(itempath); - comment_predefined->remove(last_selected_item); - Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu"); - int i, n; - for (i=4, n=0; isize(); i++) { - const Fl_Menu_Item *mi = comment_predefined->menu()+i; - if (comment_predefined->item_pathname(itempath, 255, mi)==0) { - if (itempath[0]=='/') memmove(itempath, itempath+1, 255); - if (itempath[0]) menu.set(Fl_Preferences::Name(n++), itempath); - } - } - menu.set("n", n); - } - } else { - // load the selected comment from the database - if (comment_predefined->item_pathname(itempath, 255)==0) { - if (itempath[0]=='/') memmove(itempath, itempath+1, 255); - Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); - char *text; - db.get(itempath, text, "(no text found in data base)"); - comment_input->buffer()->text(text); - free(text); - last_selected_item = comment_predefined->value(); - } - } - } - else if (w == comment_load) { - // load a comment from disk - fl_file_chooser_ok_label("Use File"); - const char *fname = fl_file_chooser("Pick a comment", 0L, 0L); - fl_file_chooser_ok_label(NULL); - if (fname) { - if (comment_input->buffer()->loadfile(fname)) { - fl_alert("Error loading file\n%s", fname); - } - } - } - else if (!w) Fl::wait(); - } - char*c = comment_input->buffer()->text(); - name(c); - free(c); - int mod = 0; - if (in_c_ != comment_in_source->value()) { - in_c_ = comment_in_source->value(); - mod = 1; - } - if (in_h_ != comment_in_header->value()) { - in_h_ = comment_in_header->value(); - mod = 1; - } - if (mod) set_modflag(1); - break; - } -BREAK2: - comment_panel->hide(); -} - -/** - Write the comment to the files. - */ -void Fl_Comment_Type::write_code1(fld::io::Code_Writer& f) { - const char* c = name(); - if (!c) return; - if (!in_c_ && !in_h_) return; - // find out if there is already a valid comment: - const char *s = c; - while (isspace(*s)) s++; - // if this seems to be a C style comment, copy the block as is - // (it's up to the user to correctly close the comment) - if (s[0]=='/' && s[1]=='*') { - if (in_h_) f.write_h("%s\n", c); - if (in_c_) f.write_c("%s\n", c); - return; - } - // copy the comment line by line, add the double slash if needed - char *txt = fl_strdup(c); - char *b = txt, *e = txt; - for (;;) { - // find the end of the line and set it to NUL - while (*e && *e!='\n') e++; - char eol = *e; - *e = 0; - // check if there is a C++ style comment at the beginning of the line - char *s = b; - while (isspace(*s)) s++; - if (s!=e && ( s[0]!='/' || s[1]!='/') ) { - // if no comment marker was found, we add one ourselves - if (in_h_) f.write_h("// "); - if (in_c_) f.write_c("// "); - } - // now copy the rest of the line - if (in_h_) f.write_h("%s\n", b); - if (in_c_) f.write_c("%s\n", b); - if (eol==0) break; - *e++ = eol; - b = e; - } - free(txt); -} - -// ---- Fl_Class_Type declaration - -/** \class Fl_Class_Type - Manage a class declaration and implementation. - */ - -/// Prototype for a class node to be used by the factory. -Fl_Class_Type Fl_Class_type; - -/** - Constructor. - */ -Fl_Class_Type::Fl_Class_Type() : - Fl_Type(), - subclass_of(NULL), - public_(1), - class_prefix(NULL) -{ } - -/** - Destructor. - */ -Fl_Class_Type::~Fl_Class_Type() { - if (subclass_of) - free((void*)subclass_of); - if (class_prefix) - free((void*)class_prefix); -} - -/** - Return 1 if this class is marked public. - */ -int Fl_Class_Type::is_public() const { - return public_; -} - -/** - Set the prefixx string. - */ -void Fl_Class_Type::prefix(const char*p) { - free((void*) class_prefix); - class_prefix=fl_strdup(p ? p : "" ); -} - -/** - Make a new class node. - \param[in] strategy add after current or as last child - \return new Class node - */ -Fl_Type *Fl_Class_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_decl_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_Class_Type *o = new Fl_Class_Type(); - o->name("UserInterface"); - o->class_prefix = NULL; - o->subclass_of = NULL; - o->public_ = 1; - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Write the respective properties. - - ":" followed by the super class - - "private"/"protected" - */ -void Fl_Class_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - if (subclass_of) { - f.write_string(":"); - f.write_word(subclass_of); - } - switch (public_) { - case 0: f.write_string("private"); break; - case 2: f.write_string("protected"); break; - } -} - -/** - Read additional properties. - */ -void Fl_Class_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"private")) { - public_ = 0; - } else if (!strcmp(c,"protected")) { - public_ = 2; - } else if (!strcmp(c,":")) { - storestring(f.read_word(), subclass_of); - } else { - Fl_Type::read_property(f, c); - } -} - -/** - Open the class_panel to edit the class name and superclass name. - */ -void Fl_Class_Type::open() { - if (!class_panel) make_class_panel(); - char fullname[FL_PATH_MAX]=""; - if (prefix() && strlen(prefix())) - sprintf(fullname,"%s %s",prefix(),name()); - else - strcpy(fullname, name()); - c_name_input->value(fullname); - c_subclass_input->value(subclass_of); - c_public_button->value(public_); - const char *c = comment(); - c_comment_input->buffer()->text(c?c:""); - class_panel->show(); - const char* message = 0; - - char *na=0,*pr=0,*p=0; // name and prefix substrings - - for (;;) { // repeat as long as there are errors - // we don;t give the option to ignore this error here because code depends - // on this being a C++ identifier - if (message) fl_alert("%s", message); - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == c_panel_cancel) goto BREAK2; - else if (w == c_panel_ok) break; - else if (!w) Fl::wait(); - } - const char*c = c_name_input->value(); - char *s = fl_strdup(c); - size_t len = strlen(s); - if (!*s) goto OOPS; - p = (char*) (s+len-1); - while (p>=s && isspace(*p)) *(p--)='\0'; - if (p=s && is_id(*p)) p--; - if ( (ps) *p--='\0'; - while (p>=s && isspace(*p)) *(p--)='\0'; - while (p>=s && is_id(*p)) p--; - if (pvalue(); - message = c_check(c); - if (message) { free((void*)s);continue;} - name(na); - prefix(pr); - free((void*)s); - storestring(c, subclass_of); - if (public_ != c_public_button->value()) { - public_ = c_public_button->value(); - set_modflag(1); - } - c = c_comment_input->buffer()->text(); - if (c && *c) { - if (!comment() || strcmp(c, comment())) { set_modflag(1); redraw_browser(); } - comment(c); - } else { - if (comment()) { set_modflag(1); redraw_browser(); } - comment(0); - } - if (c) free((void*)c); - break; - } -BREAK2: - class_panel->hide(); -} - -/** - Write the header code that declares this class. - */ -void Fl_Class_Type::write_code1(fld::io::Code_Writer& f) { - parent_class = current_class; - current_class = this; - write_public_state = 0; - f.write_h("\n"); - write_comment_h(f); - if (prefix() && strlen(prefix())) - f.write_h("class %s %s ", prefix(), name()); - else - f.write_h("class %s ", name()); - if (subclass_of) f.write_h(": %s ", subclass_of); - f.write_h("{\n"); -} - -/** - Write the header code that ends the declaration of this class. - */ -void Fl_Class_Type::write_code2(fld::io::Code_Writer& f) { - f.write_h("};\n"); - current_class = parent_class; -} - -/** - Return 1 if this class contains a function with the given signature. - */ -int Fl_Type::has_function(const char *rtype, const char *sig) const { - Fl_Type *child; - for (child = next; child && child->level > level; child = child->next) { - if (child->level == level+1 && child->is_a(ID_Function)) { - const Fl_Function_Type *fn = (const Fl_Function_Type*)child; - if (fn->has_signature(rtype, sig)) - return 1; - } - } - return 0; -} diff --git a/fluid/nodes/Fl_Function_Type.h b/fluid/nodes/Fl_Function_Type.h deleted file mode 100644 index 4c2319aee..000000000 --- a/fluid/nodes/Fl_Function_Type.h +++ /dev/null @@ -1,259 +0,0 @@ -// -// C function type header file for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2021 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_FL_FUNCTION_TYPE_H -#define _FLUID_FL_FUNCTION_TYPE_H - -#include "nodes/Fl_Type.h" - -#include "app/Fluid_Image.h" -#ifdef _WIN32 -#include "tools/ExternalCodeEditor_WIN32.h" -#else -#include "tools/ExternalCodeEditor_UNIX.h" -#endif - -#include -#include -#include -#include - -#include -#include - -extern class Fl_Class_Type *current_class; - -int has_toplevel_function(const char *rtype, const char *sig); - -const char *c_check(const char *c, int type = 0); - -// ---- Fl_Function_Type declaration - -class Fl_Function_Type : public Fl_Type -{ - typedef Fl_Type super; - const char* return_type; - char public_, cdecl_, constructor, havewidgets; - -public: - Fl_Function_Type(); - ~Fl_Function_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void open() FL_OVERRIDE; - int ismain() {return name_ == 0;} - const char *type_name() FL_OVERRIDE {return "Function";} - const char *title() FL_OVERRIDE { - return name() ? name() : "main()"; - } - int can_have_children() const FL_OVERRIDE {return 1;} - int is_code_block() const FL_OVERRIDE {return 1;} - int is_public() const FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Function; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Function) ? true : super::is_a(inID); } - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - int has_signature(const char *, const char*) const; -}; - -// ---- Fl_Code_Type declaration - -class Fl_Code_Type : public Fl_Type -{ - typedef Fl_Type super; - ExternalCodeEditor editor_; - int cursor_position_; - int code_input_scroll_row; - int code_input_scroll_col; - -public: - Fl_Code_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write(fld::io::Project_Writer &f) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE { } - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "code";} - int is_code_block() const FL_OVERRIDE {return 0;} - ID id() const FL_OVERRIDE { return ID_Code; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Code) ? true : super::is_a(inID); } - int is_public() const FL_OVERRIDE { return -1; } - int is_editing(); - int reap_editor(); - int handle_editor_changes(); -}; - -// ---- Fl_CodeBlock_Type declaration - -class Fl_CodeBlock_Type : public Fl_Type -{ - typedef Fl_Type super; - const char* after; - -public: - Fl_CodeBlock_Type(); - ~Fl_CodeBlock_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "codeblock";} - int is_code_block() const FL_OVERRIDE {return 1;} - int can_have_children() const FL_OVERRIDE {return 1;} - int is_public() const FL_OVERRIDE { return -1; } - ID id() const FL_OVERRIDE { return ID_CodeBlock; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_CodeBlock) ? true : super::is_a(inID); } - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; -}; - -// ---- Fl_Decl_Type declaration - -class Fl_Decl_Type : public Fl_Type -{ - typedef Fl_Type super; - -protected: - char public_; - char static_; - -public: - Fl_Decl_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE { } - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "decl";} - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - int is_public() const FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Decl; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Decl) ? true : super::is_a(inID); } -}; - -// ---- Fl_Data_Type declaration - -class Fl_Data_Type : public Fl_Decl_Type -{ - typedef Fl_Decl_Type super; - const char *filename_; - int text_mode_; - -public: - Fl_Data_Type(); - ~Fl_Data_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE {} - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "data";} - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Data; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Data) ? true : super::is_a(inID); } -}; - -// ---- Fl_DeclBlock_Type declaration - -class Fl_DeclBlock_Type : public Fl_Type -{ - typedef Fl_Type super; - enum { - CODE_IN_HEADER = 1, - CODE_IN_SOURCE = 2, - STATIC_IN_HEADER = 4, - STATIC_IN_SOURCE = 8 - }; - const char* after; ///< code after all children of this block - int write_map_; ///< see enum above - -public: - Fl_DeclBlock_Type(); - ~Fl_DeclBlock_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_static(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_static_after(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "declblock";} - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - int can_have_children() const FL_OVERRIDE {return 1;} - int is_decl_block() const FL_OVERRIDE {return 1;} - int is_public() const FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_DeclBlock; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_DeclBlock) ? true : super::is_a(inID); } -}; - -// ---- Fl_Comment_Type declaration - -class Fl_Comment_Type : public Fl_Type -{ - typedef Fl_Type super; - char in_c_, in_h_, style_; - -public: - Fl_Comment_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE { } - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "comment";} - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - int is_public() const FL_OVERRIDE { return 1; } - ID id() const FL_OVERRIDE { return ID_Comment; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Comment) ? true : super::is_a(inID); } -}; - -// ---- Fl_Class_Type declaration - -class Fl_Class_Type : public Fl_Type -{ - typedef Fl_Type super; - const char* subclass_of; - char public_; - const char* class_prefix; - -public: - Fl_Class_Type(); - ~Fl_Class_Type(); - // state variables for output: - char write_public_state; // true when public: has been printed - Fl_Class_Type* parent_class; // save class if nested -// - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "class";} - int can_have_children() const FL_OVERRIDE {return 1;} - int is_decl_block() const FL_OVERRIDE {return 1;} - int is_class() const FL_OVERRIDE {return 1;} - int is_public() const FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Class; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Class) ? true : super::is_a(inID); } - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - - // class prefix attribute access - void prefix(const char* p); - const char* prefix() const {return class_prefix;} -}; - -#endif // _FLUID_FL_FUNCTION_TYPE_H diff --git a/fluid/nodes/Fl_Grid_Type.cxx b/fluid/nodes/Fl_Grid_Type.cxx deleted file mode 100644 index 03d6620ea..000000000 --- a/fluid/nodes/Fl_Grid_Type.cxx +++ /dev/null @@ -1,994 +0,0 @@ -// -// Fl_Grid object code for the Fast Light Tool Kit (FLTK). -// -// Copyright 2023 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 "nodes/Fl_Grid_Type.h" - -#include "app/fluid.h" -#include "app/Fd_Snap_Action.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "widgets/Node_Browser.h" -#include "widgets/Formula_Input.h" - -#include -#include -#include -#include -#include "../src/flstring.h" - -#include -#include -#include - -// ---- Fl_Grid_Proxy --------------------------------------------------- MARK: - - -/** - An implementation of the Fl_Grid widget with additional functionality. - - Fl_Grid_Proxy add a list of transient children, i.e. children that are - temporarily assigned to a cell that is already taken by another child. - */ -Fl_Grid_Proxy::Fl_Grid_Proxy(int X,int Y,int W,int H) -: Fl_Grid(X,Y,W,H), - transient_(NULL), - num_transient_(0), - cap_transient_(0) -{ -} - -Fl_Grid_Proxy::~Fl_Grid_Proxy() { - int i; - if (transient_) { - for (i=0; i 0) { - Fl_Grid::resize(X, Y, W, H); - } else { - Fl_Widget::resize(X, Y, W, H); - } - redraw(); -} - -/** - Override draw() to make groups with no box or flat box background visible. - */ -void Fl_Grid_Proxy::draw() { - if (show_ghosted_outline && (box() == FL_NO_BOX)) { - fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); - } - Fl_Grid::draw(); -} - -/** - Draw additional markings in the overlay plane when a grid is selected. - */ -void Fl_Grid_Proxy::draw_overlay() { - fl_line_style(FL_DOT); - grid_color = fl_color(); - draw_grid(); - fl_color(grid_color); -} - -/** - Move a cell into the grid or within the grid. - - If the target cell is already taken, \p how will determine what to do: - - If \p how is 0, the existing cell at \p to_row, \p to_col will be deleted, - unlinking the occupant from the grid. \p in_child will the be inserted at the - given location. - - If \p how is 1, the old cell will remain intact, however \p in_child will be - unlinked from the grid. - - If \p how is 2, the old cell will remain intact, and \p in_child will be - removed from the grid, but it will be stored in the transient list and - resized to the target cell position and size. If \p in_child is later - moved to an unoccupied cell, it will be removed from the transient list and - relinked to the grid. Rowspan and colspan are ignored here. - - \param[in] in_child must already be a child of grid - \param[in] to_row, to_col move the child into this cell - \param[in] how 0: replace occupant, 1: don't replace, 2: make transient - if occupied - */ -void Fl_Grid_Proxy::move_cell(Fl_Widget *in_child, int to_row, int to_col, int how) { - // the child must already be a true child of grid - assert(find(in_child)row() == to_row && old_cell->col() == to_col) return; - rowspan = old_cell->rowspan(); - colspan = old_cell->colspan(); - align = old_cell->align(); - old_cell->minimum_size(&w, &h); - } - if ((to_row < 0) || (to_row+rowspan > rows())) return; - if ((to_col < 0) || (to_col+colspan > cols())) return; - Fl_Grid::Cell *new_cell = NULL; - if (how == 0) { // replace old occupant in cell, making that one homeless - new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align); - } else if (how == 1) { // don't replace an old occupant, making ourselves homeless - // todo: colspan, rowspan? - if (cell(to_row, to_col) == NULL) { - new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align); - } else { - if (old_cell) remove_cell(old_cell->row(), old_cell->col()); - } - } else if (how == 2) { - Cell *current = cell(to_row, to_col); - if (current == NULL) { - new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align); - } else { - if (old_cell) remove_cell(old_cell->row(), old_cell->col()); - new_cell = transient_widget(in_child, to_row, to_col, rowspan, colspan, align); - Fl_Widget *w = current->widget(); - Fl_Type::allow_layout++; - in_child->resize(w->x(), w->y(), w->w(), w->h()); - Fl_Type::allow_layout--; - } - } - if (new_cell) new_cell->minimum_size(w, h); -} - -/** - Generate or replace a transient widget entry. - - If the widget is in the cell list, it will be removed there. - If the widget is already transient, the cell will be replaced. - - \param[in] wi a child of this Fl_Grid_Proxy, that may be linked to a cell or transient cell - \param[in] row, col, row_span, col_span, align cell parameters - */ -Fl_Grid::Cell* Fl_Grid_Proxy::transient_widget(Fl_Widget *wi, int row, int col, int row_span, int col_span, Fl_Grid_Align align) { - int i = 0; - bool remove_old_cell = false; - Cell *old_cell = cell(wi); - if (old_cell) { - remove_old_cell = true; - } else { - for (i=0; irowspan(row_span); - new_cell->colspan(col_span); - new_cell->align(align); - if (old_cell) { - int mw, mh; - old_cell->minimum_size(&mw, &mh); - new_cell->minimum_size(mw, mh); - if (remove_old_cell) { - remove_cell(old_cell->row(), old_cell->col()); - } else { - delete old_cell; - } - } - if (i == num_transient_) { - transient_make_room_(num_transient_ + 1); - transient_[i].widget = wi; - num_transient_++; - } - transient_[i].cell = new_cell; - return new_cell; -} - -/** - Make room for at least n transient widgets in the array. - \param[in] n minimum number of entries - */ -void Fl_Grid_Proxy::transient_make_room_(int n) { - if (n > cap_transient_) { - cap_transient_ = n + 10; - transient_ = (Cell_Widget_Pair*)::realloc(transient_, cap_transient_ * sizeof(Cell_Widget_Pair)); - } -} - -/** - Remove a widget form the list and deallocate the transient cell. - \param[in] w remove the transient cell for this widget - */ -void Fl_Grid_Proxy::transient_remove_(Fl_Widget *w) { - for (int i=0; ilayout(3, 3); - Fl_Group::current(0); - return g; -} - -Fl_Widget *Fl_Grid_Type::enter_live_mode(int top) { - Fl_Grid *grid = new Fl_Grid(o->x(), o->y(), o->w(), o->h()); - return propagate_live_mode(grid); -} - -void Fl_Grid_Type::leave_live_mode() { -} - -void Fl_Grid_Type::copy_properties() -{ - super::copy_properties(); - Fl_Grid *d = (Fl_Grid*)live_widget, *s =(Fl_Grid*)o; - d->layout(s->rows(), s->cols()); - int lm, tm, rm, bm; - s->margin(&lm, &tm, &rm, &bm); - d->margin(lm, tm, rm, bm); - int rg, cg; - s->gap(&rg, &cg); - d->gap(rg, cg); - // copy col widths, heights, and gaps - for (int c=0; ccols(); c++) { - d->col_width(c, s->col_width(c)); - d->col_gap(c, s->col_gap(c)); - d->col_weight(c, s->col_weight(c)); - } - // copy row widths, heights, and gaps - for (int r=0; rrows(); r++) { - d->row_height(r, s->row_height(r)); - d->row_gap(r, s->row_gap(r)); - d->row_weight(r, s->row_weight(r)); - } -} - -void Fl_Grid_Type::copy_properties_for_children() { - Fl_Grid *d = (Fl_Grid*)live_widget, *s =(Fl_Grid*)o; - for (int i=0; ichildren(); i++) { - Fl_Grid::Cell *cell = s->cell(s->child(i)); - if (cell && ichildren()) { - d->widget(d->child(i), - cell->row(), cell->col(), - cell->rowspan(), cell->colspan(), - cell->align()); - } - } - d->layout(); -} - -void Fl_Grid_Type::write_properties(fld::io::Project_Writer &f) -{ - super::write_properties(f); - Fl_Grid* grid = (Fl_Grid*)o; - int i, rows = grid->rows(), cols = grid->cols(); - f.write_indent(level+1); - f.write_string("dimensions {%d %d}", rows, cols); - int lm, tm, rm, bm; - grid->margin(&lm, &tm, &rm, &bm); - if (lm!=0 || tm!=0 || rm!=0 || bm!=0) - f.write_string("margin {%d %d %d %d}", lm, tm, rm, bm); - int rg, cg; - grid->gap(&rg, &cg); - if (rg!=0 || cg!=0) - f.write_string("gap {%d %d}", rg, cg); - // -- write all row heights if one of them is not the default 0 - for (i=0; irow_height(i)!=0) break; - if (irow_height(i)); - f.write_string("}"); - } - // -- write all row weights if one of them is not the default 50 - for (i=0; irow_weight(i)!=50) break; - if (irow_weight(i)); - f.write_string("}"); - } - // -- write all row gaps if one of them is not the default -1 - for (i=0; irow_gap(i)!=-1) break; - if (irow_gap(i)); - f.write_string("}"); - } - // -- write all col widths if one of them is not the default 0 - for (i=0; icol_width(i)!=0) break; - if (icol_width(i)); - f.write_string("}"); - } - // -- write all col weights if one of them is not the default 50 - for (i=0; icol_weight(i)!=50) break; - if (icol_weight(i)); - f.write_string("}"); - } - // -- write all col gaps if one of them is not the default -1 - for (i=0; icol_gap(i)!=-1) break; - if (icol_gap(i)); - f.write_string("}"); - } -} - -void Fl_Grid_Type::read_property(fld::io::Project_Reader &f, const char *c) -{ - Fl_Grid* grid = (Fl_Grid*)o; - if (!strcmp(c,"dimensions")) { - int rows = 3, cols = 3; - if (sscanf(f.read_word(),"%d %d", &rows, &cols) == 2) - grid->layout(rows, cols); - } else if (!strcmp(c,"margin")) { - int lm, tm, rm, bm; - if (sscanf(f.read_word(),"%d %d %d %d", &lm, &tm, &rm, &bm) == 4) - grid->margin(lm, tm, rm, bm); - } else if (!strcmp(c,"gap")) { - int rg, cg; - if (sscanf(f.read_word(),"%d %d", &rg, &cg) == 2) - grid->gap(rg, cg); - } else if (!strcmp(c,"rowheights")) { - int rows = grid->rows(); - f.read_word(1); // "{" - for (int i=0; irow_height(i, f.read_int()); - f.read_word(1); // "}" - } else if (!strcmp(c,"rowweights")) { - int rows = grid->rows(); - f.read_word(1); // "{" - for (int i=0; irow_weight(i, f.read_int()); - f.read_word(1); // "}" - } else if (!strcmp(c,"rowgaps")) { - int rows = grid->rows(); - f.read_word(1); // "{" - for (int i=0; irow_gap(i, f.read_int()); - f.read_word(1); // "}" - } else if (!strcmp(c,"colwidths")) { - int cols = grid->cols(); - f.read_word(1); // "{" - for (int i=0; icol_width(i, f.read_int()); - f.read_word(1); // "}" - } else if (!strcmp(c,"colweights")) { - int cols = grid->cols(); - f.read_word(1); // "{" - for (int i=0; icol_weight(i, f.read_int()); - f.read_word(1); // "}" - } else if (!strcmp(c,"colgaps")) { - int cols = grid->cols(); - f.read_word(1); // "{" - for (int i=0; icol_gap(i, f.read_int()); - f.read_word(1); // "}" - } else { - super::read_property(f, c); - } -} - -void Fl_Grid_Type::write_parent_properties(fld::io::Project_Writer &f, Fl_Type *child, bool encapsulate) { - Fl_Grid *grid; - Fl_Widget *child_widget; - Fl_Grid::Cell *cell; - if (!child->is_true_widget()) return super::write_parent_properties(f, child, true); - grid = (Fl_Grid*)o; - child_widget = ((Fl_Widget_Type*)child)->o; - cell = grid->cell(child_widget); - if (!cell) return super::write_parent_properties(f, child, true); - if (encapsulate) { - f.write_indent(level+2); - f.write_string("parent_properties {"); - } - f.write_indent(level+3); - f.write_string("location {%d %d}", cell->row(), cell->col()); - int v = cell->colspan(); - if (v>1) { - f.write_indent(level+3); - f.write_string("colspan %d", v); - } - v = cell->rowspan(); - if (v>1) { - f.write_indent(level+3); - f.write_string("rowspan %d", v); - } - v = (int)cell->align(); - if (v!=FL_GRID_FILL) { - f.write_indent(level+3); - f.write_string("align %d", v); - } - int min_w = 0, min_h = 0; - cell->minimum_size(&min_w, &min_h); - if (min_w!=20 || min_h!=20) { - f.write_indent(level+3); - f.write_string("minsize {%d %d}", min_w, min_h); - } - super::write_parent_properties(f, child, false); - if (encapsulate) { - f.write_indent(level+2); - f.write_string("}"); - } - return; -} - -// NOTE: we have to do this in a loop just as ::read_property() in case a new -// property is added. In the current setup, all the remaining properties -// will be skipped -void Fl_Grid_Type::read_parent_property(fld::io::Project_Reader &f, Fl_Type *child, const char *property) { - if (!child->is_true_widget()) { - super::read_parent_property(f, child, property); - return; - } - Fl_Grid *grid = (Fl_Grid*)o; - Fl_Widget *child_widget = ((Fl_Widget_Type*)child)->o; - if (!strcmp(property, "location")) { - int row = -1, col = -1; - const char *value = f.read_word(); - sscanf(value, "%d %d", &row, &col); - Fl_Grid::Cell *cell = grid->widget(child_widget, row, col); - if (cell) { - int min_w = 20, min_h = 20; - cell->minimum_size(min_w, min_h); - } - } else if (!strcmp(property, "colspan")) { - int colspan = atoi(f.read_word()); - Fl_Grid::Cell *cell = grid->cell(child_widget); - if (cell) cell->colspan(colspan); - } else if (!strcmp(property, "rowspan")) { - int rowspan = atoi(f.read_word()); - Fl_Grid::Cell *cell = grid->cell(child_widget); - if (cell) cell->rowspan(rowspan); - } else if (!strcmp(property, "align")) { - int align = atoi(f.read_word()); - Fl_Grid::Cell *cell = grid->cell(child_widget); - if (cell) cell->align((Fl_Grid_Align)align); - } if (!strcmp(property, "minsize")) { - int min_w = 20, min_h = 20; - const char *value = f.read_word(); - sscanf(value, "%d %d", &min_w, &min_h); - Fl_Grid::Cell *cell = grid->cell(child_widget); - if (cell) cell->minimum_size(min_w, min_h); - } else { - super::read_parent_property(f, child, property); - } -} - -void Fl_Grid_Type::write_code1(fld::io::Code_Writer& f) { - const char *var = name() ? name() : "o"; - Fl_Grid* grid = (Fl_Grid*)o; - Fl_Widget_Type::write_code1(f); - int i, rows = grid->rows(), cols = grid->cols(); - f.write_c("%s%s->layout(%d, %d);\n", f.indent(), var, rows, cols); - int lm, tm, rm, bm; - grid->margin(&lm, &tm, &rm, &bm); - if (lm!=0 || tm!=0 || rm!=0 || bm!=0) - f.write_c("%s%s->margin(%d, %d, %d, %d);\n", f.indent(), var, lm, tm, rm, bm); - int rg, cg; - grid->gap(&rg, &cg); - if (rg!=0 || cg!=0) - f.write_c("%s%s->gap(%d, %d);\n", f.indent(), var, rg, cg); - // -- write all row heights if one of them is not the default 0 - for (i=0; irow_height(i)!=0) break; - if (irow_height(0)); - for (i=1; irow_height(i)); - f.write_c(" };\n"); - f.write_c("%s%s->row_height(rowheights, %d);\n", f.indent(), var, rows); - } - // -- write all row weights if one of them is not the default 50 - for (i=0; irow_weight(i)!=50) break; - if (irow_weight(0)); - for (i=1; irow_weight(i)); - f.write_c(" };\n"); - f.write_c("%s%s->row_weight(rowweights, %d);\n", f.indent(), var, rows); - } - // -- write all row gaps if one of them is not the default -1 - for (i=0; irow_gap(i)!=-1) break; - if (irow_gap(0)); - for (i=1; irow_gap(i)); - f.write_c(" };\n"); - f.write_c("%s%s->row_gap(rowgaps, %d);\n", f.indent(), var, rows); - } - // -- write all col widths if one of them is not the default 0 - for (i=0; icol_width(i)!=0) break; - if (icol_width(0)); - for (i=1; icol_width(i)); - f.write_c(" };\n"); - f.write_c("%s%s->col_width(colwidths, %d);\n", f.indent(), var, cols); - } - // -- write all col weights if one of them is not the default 50 - for (i=0; icol_weight(i)!=50) break; - if (icol_weight(0)); - for (i=1; icol_weight(i)); - f.write_c(" };\n"); - f.write_c("%s%s->col_weight(colweights, %d);\n", f.indent(), var, cols); - } - // -- write all col gaps if one of them is not the default -1 - for (i=0; icol_gap(i)!=-1) break; - if (icol_gap(0)); - for (i=1; icol_gap(i)); - f.write_c(" };\n"); - f.write_c("%s%s->col_gap(colgaps, %d);\n", f.indent(), var, cols); - } -} - -void Fl_Grid_Type::write_code2(fld::io::Code_Writer& f) { - const char *var = name() ? name() : "o"; - Fl_Grid* grid = (Fl_Grid*)o; - bool first_cell = true; - for (int i=0; ichildren(); i++) { - Fl_Widget *c = grid->child(i); - Fl_Grid::Cell *cell = grid->cell(c); - if (cell) { - if (first_cell) { - f.write_c("%sFl_Grid::Cell *cell = NULL;\n", f.indent()); - first_cell = false; - } - f.write_c("%scell = %s->widget(%s->child(%d), %d, %d, %d, %d, %d);\n", - f.indent(), var, var, i, cell->row(), cell->col(), - cell->rowspan(), cell->colspan(), cell->align()); - int min_w = 20, min_h = 20; - cell->minimum_size(&min_w, &min_h); - f.write_c("%sif (cell) cell->minimum_size(%d, %d);\n", f.indent(), min_w, min_h); - } - } - super::write_code2(f); -} - -void Fl_Grid_Type::add_child(Fl_Type* a, Fl_Type* b) { - super::add_child(a, b); - Fl_Grid* grid = (Fl_Grid*)o; - grid->need_layout(1); - grid->redraw(); -} - -void Fl_Grid_Type::move_child(Fl_Type* a, Fl_Type* b) { - super::move_child(a, b); - Fl_Grid* grid = (Fl_Grid*)o; - grid->need_layout(1); - grid->redraw(); -} - -void Fl_Grid_Type::remove_child(Fl_Type* a) { - super::remove_child(a); - Fl_Grid* grid = (Fl_Grid*)o; - grid->need_layout(1); - grid->redraw(); -} - -/** Update the initial size of a child widget. - Fl_Grid keeps track of the size of children when they are first added. In - FLUID, users will want to resize children. So we need to trick Fl_Grid into - taking the new size as the initial size. - */ -void Fl_Grid_Type::child_resized(Fl_Widget_Type *child_type) { - Fl_Grid *grid = (Fl_Grid*)o; - Fl_Widget *child = child_type->o; - Fl_Grid::Cell *cell = grid->cell(child); - if (cell && ((cell->align()&FL_GRID_VERTICAL)==0)) { - int min_w = 0, min_h = 0; - cell->minimum_size(&min_w, &min_h); - cell->minimum_size(min_w, child->h()); - } - if (cell && ((cell->align()&FL_GRID_HORIZONTAL)==0)) { - int min_w = 0, min_h = 0; - cell->minimum_size(&min_w, &min_h); - cell->minimum_size(child->w(), min_h); - } - // TODO: if the user resizes an FL_GRID_FILL widget, should we change the alignment? -} - -/** Return the currently selected Grid widget if is a Grid Type. */ -Fl_Grid *Fl_Grid_Type::selected() { - if (current_widget && current_widget->is_a(ID_Grid)) - return ((Fl_Grid*)((Fl_Grid_Type*)current_widget)->o); - return NULL; -} - -/** - Insert a child widget into the cell at the x, y position inside the window. - /param[in] child - /param[in] x, y pixels from the top left of the window - */ -void Fl_Grid_Type::insert_child_at(Fl_Widget *child, int x, int y) { - Fl_Grid_Proxy *grid = (Fl_Grid_Proxy*)o; - int row = -1, col = -1, ml, mt, grg, gcg; - grid->margin(&ml, &mt, NULL, NULL); - grid->gap(&grg, &gcg); - int x0 = grid->x() + Fl::box_dx(grid->box()) + ml; - int y0 = grid->y() + Fl::box_dy(grid->box()) + mt; - - for (int r = 0; r < grid->rows(); r++) { - if (y>y0) row = r; - int gap = grid->row_gap(r)>=0 ? grid->row_gap(r) : grg; - y0 += grid->computed_row_height(r); - y0 += gap; - } - - for (int c = 0; c < grid->cols(); c++) { - if (x>x0) col = c; - int gap = grid->col_gap(c)>=0 ? grid->col_gap(c) : gcg; - x0 += grid->computed_col_width(c); - x0 += gap; - } - - grid->move_cell(child, row, col, 2); -} - -/** - Insert a child widget into the first new cell we can find . - - There are many other possible strategies. How about inserting to the right - of the last added child. Also, what happens if the grid is full? Should - we add a new row at the bottom? - - /param[in] child - */ -void Fl_Grid_Type::insert_child_at_next_free_cell(Fl_Widget *child) { - Fl_Grid_Proxy *grid = (Fl_Grid_Proxy*)o; - if (grid->cell(child)) return; -// The code below would insert the new widget after the last selected one, but -// unfortunately the current_widget is already invalid. -// if (current_widget && (current_widget->parent == this)) { -// Fl_Grid::Cell *current_cell = grid->any_cell(current_widget->o); -// if (current_cell) { -// r = current_cell->row(); -// c = current_cell->col(); -// } -// } - for (int r = 0; r < grid->rows(); r++) { - for (int c = 0; c < grid->cols(); c++) { - if (!grid->cell(r, c)) { - grid->move_cell(child, r, c); - return; - } - } - } - grid->layout(grid->rows() + 1, grid->cols()); - grid->move_cell(child, grid->rows() - 1, 0); -} - -/** Move cells around using the keyboard. - \note this fails if we have two children selected side by side and press 'right', - which will move the left child first, removing the right child from the - cell system. When trying to move the second child, it has no longer an - assigned row or column. - \param[in] child pointer to the child type - \param[in] key code of the last keypress when handling a FL_KEYBOARD event. - */ -void Fl_Grid_Type::keyboard_move_child(Fl_Widget_Type *child, int key) { - Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)o); - Fl_Grid::Cell *cell = grid->any_cell(child->o); - if (!cell) return; - if (key == FL_Right) { - grid->move_cell(child->o, cell->row(), cell->col()+1, 2); - } else if (key == FL_Left) { - grid->move_cell(child->o, cell->row(), cell->col()-1, 2); - } else if (key == FL_Up) { - grid->move_cell(child->o, cell->row()-1, cell->col(), 2); - } else if (key == FL_Down) { - grid->move_cell(child->o, cell->row()+1, cell->col(), 2); - } -} - -void Fl_Grid_Type::layout_widget() { - allow_layout++; - ((Fl_Grid*)o)->layout(); - allow_layout--; -} - - -// ---- Widget Panel Callbacks ---------------------------------------- MARK: - - -// TODO: better grid overlay? -// TODO: grid_child_cb should move all selected cells, not just the current_selected. -// TODO: buttons to add and delete rows and columns in the widget dialog -// TODO: ways to resize rows and columns, add and delete them in the project window, pulldown menu? -// TODO: alignment can be FL_GRID_LEFT|FL_GRID_VERTICAL? - -extern fld::widget::Formula_Input *widget_grid_row_input, *widget_grid_col_input, -*widget_grid_rowspan_input, *widget_grid_colspan_input; -extern Fl_Group *widget_tab_grid_child; - -void grid_child_cb(fld::widget::Formula_Input* i, void* v, int what) { - if ( !current_widget - || !current_widget->parent - || !current_widget->parent->is_a(ID_Grid)) - { - return; - } - Fl_Widget *child = ((Fl_Widget_Type*)current_widget)->o; - Fl_Grid_Proxy *g = ((Fl_Grid_Proxy*)((Fl_Widget_Type*)current_widget->parent)->o); - Fl_Grid::Cell *cell = g->any_cell(child); - if (v == LOAD) { - int v = -1; - if (cell) { - switch (what & 0x00ff) { - case 8: v = cell->row(); break; - case 9: v = cell->col(); break; - case 10: v = cell->rowspan(); break; - case 11: v = cell->colspan(); break; - case 12: cell->minimum_size(&v, NULL); break; - case 13: cell->minimum_size(NULL, &v); break; - } - } - i->value(v); - } else { - undo_checkpoint(); - int v2 = -2, old_v = -2, v = i->value(); - if (i==widget_grid_row_input) v2 = widget_grid_col_input->value(); - if (i==widget_grid_col_input) v2 = widget_grid_row_input->value(); - Fl_Grid::Cell *new_cell = NULL; - if (cell) { - switch (what & 0x00ff) { - case 8: old_v = cell->row(); v2 = cell->col(); break; - case 9: old_v = cell->col(); v2 = cell->row(); break; - case 10: old_v = cell->rowspan(); break; - case 11: old_v = cell->colspan(); break; - case 12: cell->minimum_size(&old_v, &v2); break; - case 13: cell->minimum_size(&v2, &old_v); break; - } - } - switch (what & 0xff00) { - case 0x0100: v--; break; - case 0x0200: v++; break; - } - if (old_v != v) { - switch (what & 0x00ff) { - case 8: - if (v2 == -1 && v >= 0) v2 = 0; - g->move_cell(current_widget->o, v, v2, 2); i->value(v); - break; - case 9: - if (v2 == -1 && v >= 0) v2 = 0; - g->move_cell(current_widget->o, v2, v, 2); i->value(v); - break; - case 10: if (cell && cell->row()+v<=g->rows() && v>0) cell->rowspan(v); - break; - case 11: if (cell && cell->col()+v<=g->cols() && v>0) cell->colspan(v); - break; - case 12: if (cell && v>=0) cell->minimum_size(v, v2); - break; - case 13: if (cell && v>=0) cell->minimum_size(v2, v); - break; - } - if (!cell && new_cell) - new_cell->minimum_size(20, 20); - g->need_layout(true); - set_modflag(1); - } - } -} -void grid_set_row_cb(fld::widget::Formula_Input* i, void* v) { - grid_child_cb(i, v, 8); - if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); -} -void grid_dec_row_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_row_input, v, 0x0100 + 8); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_inc_row_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_row_input, v, 0x0200 + 8); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_set_col_cb(fld::widget::Formula_Input* i, void* v) { - grid_child_cb(i, v, 9); - if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); -} -void grid_dec_col_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_col_input, v, 0x0100 + 9); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_inc_col_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_col_input, v, 0x0200 + 9); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_set_rowspan_cb(fld::widget::Formula_Input* i, void* v) { - grid_child_cb(i, v, 10); - if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); -} -void grid_dec_rowspan_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_rowspan_input, v, 0x0100 + 10); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_inc_rowspan_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_rowspan_input, v, 0x0200 + 10); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_set_colspan_cb(fld::widget::Formula_Input* i, void* v) { - grid_child_cb(i, v, 11); - if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); -} -void grid_dec_colspan_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_colspan_input, v, 0x0100 + 11); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_inc_colspan_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_colspan_input, v, 0x0200 + 11); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_set_min_wdt_cb(fld::widget::Formula_Input* i, void* v) { - grid_child_cb(i, v, 12); -} -void grid_set_min_hgt_cb(fld::widget::Formula_Input* i, void* v) { - grid_child_cb(i, v, 13); -} - -void grid_align_horizontal_cb(Fl_Choice* i, void* v) { - if ( !current_widget - || !current_widget->parent - || !current_widget->parent->is_a(ID_Grid)) - { - return; - } - int mask = (FL_GRID_LEFT | FL_GRID_RIGHT | FL_GRID_HORIZONTAL); - Fl_Grid *g = ((Fl_Grid*)((Fl_Widget_Type*)current_widget->parent)->o); - if (v == LOAD) { - int a = FL_GRID_FILL & mask; - Fl_Grid::Cell *cell = g->cell(current_widget->o); - if (cell) { - a = cell->align() & mask; - } - const Fl_Menu_Item *mi = i->find_item_with_argument(a); - if (mi) i->value(mi); - } else { - undo_checkpoint(); - int v = FL_GRID_FILL & mask, old_v = FL_GRID_FILL & mask; - const Fl_Menu_Item *mi = i->mvalue(); - if (mi) v = (int)mi->argument(); - Fl_Grid::Cell *cell = g->cell(current_widget->o); - if (cell) { - old_v = cell->align() & mask; - if (old_v != v) { - cell->align((Fl_Grid_Align)(v | (cell->align() & ~mask))); - g->need_layout(true); - g->redraw(); - set_modflag(1); - } - } - } -} - -void grid_align_vertical_cb(Fl_Choice* i, void* v) { - if ( !current_widget - || !current_widget->parent - || !current_widget->parent->is_a(ID_Grid)) - { - return; - } - int mask = (FL_GRID_TOP | FL_GRID_BOTTOM | FL_GRID_VERTICAL); - Fl_Grid *g = ((Fl_Grid*)((Fl_Widget_Type*)current_widget->parent)->o); - if (v == LOAD) { - int a = FL_GRID_FILL & mask; - Fl_Grid::Cell *cell = g->cell(current_widget->o); - if (cell) { - a = cell->align() & mask; - } - const Fl_Menu_Item *mi = i->find_item_with_argument(a); - if (mi) i->value(mi); - } else { - undo_checkpoint(); - int v = FL_GRID_FILL & mask, old_v = FL_GRID_FILL & mask; - const Fl_Menu_Item *mi = i->mvalue(); - if (mi) v = (int)mi->argument(); - Fl_Grid::Cell *cell = g->cell(current_widget->o); - if (cell) { - old_v = cell->align() & mask; - if (old_v != v) { - cell->align((Fl_Grid_Align)(v | (cell->align() & ~mask))); - g->need_layout(true); - g->redraw(); - set_modflag(1); - } - } - } -} - diff --git a/fluid/nodes/Fl_Grid_Type.h b/fluid/nodes/Fl_Grid_Type.h deleted file mode 100644 index 1ce5321c8..000000000 --- a/fluid/nodes/Fl_Grid_Type.h +++ /dev/null @@ -1,82 +0,0 @@ -// -// Fl_Grid type header file for the Fast Light Tool Kit (FLTK). -// -// Copyright 2023 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_FL_GRID_TYPE_H -#define _FLUID_FL_GRID_TYPE_H - -#include "nodes/Fl_Group_Type.h" -#include - -// ---- Fl_Grid_Type --------------------------------------------------- MARK: - - -extern const char grid_type_name[]; - -class Fl_Grid_Proxy : public Fl_Grid { -protected: - typedef struct { Fl_Widget *widget; Cell *cell; } Cell_Widget_Pair; - Cell_Widget_Pair *transient_; - int num_transient_; - int cap_transient_; - void transient_make_room_(int n); - void transient_remove_(Fl_Widget *w); -public: - Fl_Grid_Proxy(int X,int Y,int W,int H); - ~Fl_Grid_Proxy(); - void resize(int,int,int,int) FL_OVERRIDE; - void draw() FL_OVERRIDE; - void draw_overlay(); - void move_cell(Fl_Widget *child, int to_row, int to_col, int how = 0); - Cell* any_cell(Fl_Widget *widget) const; - Cell* transient_cell(Fl_Widget *widget) const; - Cell* transient_widget(Fl_Widget *wi, int row, int col, int row_span, int col_span, Fl_Grid_Align align = FL_GRID_FILL); - Cell* widget(Fl_Widget *wi, int row, int col, Fl_Grid_Align align = FL_GRID_FILL); - Cell* widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align = FL_GRID_FILL); -}; - -class Fl_Grid_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; -public: - Fl_Grid_Type(); - const char *type_name() FL_OVERRIDE {return grid_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::GridGroup";} - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Grid_Type(); } - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Grid; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Grid) ? true : super::is_a(inID); } - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - void write_parent_properties(fld::io::Project_Writer &f, Fl_Type *child, bool encapsulate) FL_OVERRIDE; - void read_parent_property(fld::io::Project_Reader &f, Fl_Type *child, const char *property) FL_OVERRIDE; - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void leave_live_mode() FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; - void copy_properties_for_children() FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void remove_child(Fl_Type*) FL_OVERRIDE; - void layout_widget() FL_OVERRIDE; - void child_resized(Fl_Widget_Type *child); - void insert_child_at(Fl_Widget *child, int x, int y); - void insert_child_at_next_free_cell(Fl_Widget *child); - void keyboard_move_child(Fl_Widget_Type*, int key); - - static class Fl_Grid *selected(); -}; - -#endif // _FLUID_FL_GRID_TYPE_H diff --git a/fluid/nodes/Fl_Group_Type.cxx b/fluid/nodes/Fl_Group_Type.cxx deleted file mode 100644 index 5d3cba07c..000000000 --- a/fluid/nodes/Fl_Group_Type.cxx +++ /dev/null @@ -1,849 +0,0 @@ -// -// Fl_Group object code for the Fast Light Tool Kit (FLTK). -// -// Object describing an Fl_Group and links to Fl_Window_Type.C and -// the Fl_Tabs widget, with special stuff to select tab items and -// insure that only one is visible. -// -// Copyright 1998-2023 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 "nodes/Fl_Group_Type.h" - -#include "app/fluid.h" -#include "app/undo.h" -#include "app/Fd_Snap_Action.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "widgets/Node_Browser.h" - -#include -#include -#include -#include -#include -#include -#include "../src/flstring.h" - -#include -#include - - -// ---- Fl_Group_Type -------------------------------------------------- MARK: - - -Fl_Group_Type Fl_Group_type; // the "factory" - -/** - Override group's resize behavior to do nothing to children by default. - \param[in] X, Y, W, H new size - */ -void Fl_Group_Proxy::resize(int X, int Y, int W, int H) { - if (Fl_Type::allow_layout > 0) { - Fl_Group::resize(X, Y, W, H); - } else { - Fl_Widget::resize(X, Y, W, H); - } - redraw(); -} - -/** - Override draw() to make groups with no box or flat box background visible. - */ -void Fl_Group_Proxy::draw() { - if (show_ghosted_outline && (box() == FL_NO_BOX)) { - fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); - } - Fl_Group::draw(); -} - - -/** - \brief Enlarge the group size, so all children fit within. - */ -void fix_group_size(Fl_Type *tt) { - if (!tt || !tt->is_a(ID_Group)) return; - Fl_Group_Type* t = (Fl_Group_Type*)tt; - int X = t->o->x(); - int Y = t->o->y(); - int R = X+t->o->w(); - int B = Y+t->o->h(); - for (Fl_Type *nn = t->next; nn && nn->level > t->level; nn = nn->next) { - if (nn->is_true_widget()) { - Fl_Widget_Type* n = (Fl_Widget_Type*)nn; - int x = n->o->x(); if (x < X) X = x; - int y = n->o->y(); if (y < Y) Y = y; - int r = x+n->o->w();if (r > R) R = r; - int b = y+n->o->h();if (b > B) B = b; - } - } - t->o->resize(X,Y,R-X,B-Y); -} - -extern void group_selected_menuitems(); - -void group_cb(Fl_Widget *, void *) { - if (!Fl_Type::current) { - fl_message("No widgets selected."); - return; - } - if (!Fl_Type::current->is_widget()) { - fl_message("Only widgets and menu items can be grouped."); - return; - } - if (Fl_Type::current->is_a(ID_Menu_Item)) { - group_selected_menuitems(); - return; - } - // The group will be created in the parent group of the current widget - Fl_Type *qq = Fl_Type::current->parent; - Fl_Widget_Type *q = static_cast(Fl_Type::current); - while (qq && !qq->is_a(ID_Group)) { - qq = qq->parent; - } - if (!qq) { - fl_message("Can't create a new group here."); - return; - } - undo_checkpoint(); - undo_suspend(); - Fl_Type::current = qq; - Fl_Group_Type *n = (Fl_Group_Type*)(Fl_Group_type.make(Strategy::AS_LAST_CHILD)); - n->move_before(q); - n->o->resize(q->o->x(),q->o->y(),q->o->w(),q->o->h()); - for (Fl_Type *t = qq->next; t && (t->level > qq->level);) { - if (t->level != n->level || t == n || !t->selected) { - t = t->next; - continue; - } - Fl_Type *nxt = t->remove(); - t->add(n, Strategy::AS_LAST_CHILD); - t = nxt; - } - fix_group_size(n); - Fl_Type::current = q; - n->layout_widget(); - widget_browser->rebuild(); - undo_resume(); - set_modflag(1); -} - -extern void ungroup_selected_menuitems(); - -void ungroup_cb(Fl_Widget *, void *) { - if (!Fl_Type::current) { - fl_message("No widgets selected."); - return; - } - if (!Fl_Type::current->is_widget()) { - fl_message("Only widgets and menu items can be ungrouped."); - return; - } - if (Fl_Type::current->is_a(ID_Menu_Item)) { - ungroup_selected_menuitems(); - return; - } - - Fl_Widget_Type *q = static_cast(Fl_Type::current); - int q_level = q->level; - Fl_Type *qq = Fl_Type::current->parent; - while (qq && !qq->is_true_widget()) qq = qq->parent; - if (!qq || !qq->is_a(ID_Group)) { - fl_message("Only menu widgets inside a group can be ungrouped."); - return; - } - undo_checkpoint(); - undo_suspend(); - Fl_Type::current = qq; - for (Fl_Type *t = qq->next; t && (t->level > qq->level);) { - if (t->level != q_level || !t->selected) { - t = t->next; - continue; - } - Fl_Type *nxt = t->remove(); - t->insert(qq); - t = nxt; - } - if (!qq->next || (qq->next->level <= qq->level)) { - qq->remove(); - delete qq; // qq has no children that need to be delete - } - Fl_Type::current = q; - widget_browser->rebuild(); - undo_resume(); - set_modflag(1); -} - -void Fl_Group_Type::ideal_size(int &w, int &h) { - if (parent && parent->is_true_widget()) { - Fl_Widget *p = ((Fl_Widget_Type*)parent)->o; - w = p->w() / 2; - h = p->h() / 2; - } else { - w = 140; - h = 140; - } - Fd_Snap_Action::better_size(w, h); -} - -void Fl_Group_Type::write_code1(fld::io::Code_Writer& f) { - Fl_Widget_Type::write_code1(f); -} - -void Fl_Group_Type::write_code2(fld::io::Code_Writer& f) { - const char *var = name() ? name() : "o"; - write_extra_code(f); - f.write_c("%s%s->end();\n", f.indent(), var); - if (resizable()) { - f.write_c("%sFl_Group::current()->resizable(%s);\n", f.indent(), var); - } - write_block_close(f); -} - -// This is called when o is created. If it is in the tab group make -// sure it is visible: -void Fl_Group_Type::add_child(Fl_Type* cc, Fl_Type* before) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - Fl_Widget* b = before ? ((Fl_Widget_Type*)before)->o : 0; - ((Fl_Group*)o)->insert(*(c->o), b); - o->redraw(); -} - -// This is called when o is deleted. If it is in the tab group make -// sure it is not visible: -void Fl_Group_Type::remove_child(Fl_Type* cc) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - ((Fl_Group*)o)->remove(c->o); - o->redraw(); -} - -// move, don't change selected value: -void Fl_Group_Type::move_child(Fl_Type* cc, Fl_Type* before) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - Fl_Widget* b = before ? ((Fl_Widget_Type*)before)->o : 0; - ((Fl_Group*)o)->insert(*(c->o), b); - o->redraw(); -} - -// live mode support -Fl_Widget* Fl_Group_Type::enter_live_mode(int) { - Fl_Group *grp = new Fl_Group(o->x(), o->y(), o->w(), o->h()); - return propagate_live_mode(grp); -} - -void Fl_Group_Type::leave_live_mode() { -} - -/** - copy all properties from the edit widget to the live widget - */ -void Fl_Group_Type::copy_properties() { - Fl_Widget_Type::copy_properties(); -} - -// ---- Fl_Pack_Type --------------------------------------------------- MARK: - - -Fl_Pack_Type Fl_Pack_type; // the "factory" - -const char pack_type_name[] = "Fl_Pack"; - -Fl_Menu_Item pack_type_menu[] = { - {"HORIZONTAL", 0, 0, (void*)Fl_Pack::HORIZONTAL}, - {"VERTICAL", 0, 0, (void*)Fl_Pack::VERTICAL}, - {0} -}; - -Fl_Widget *Fl_Pack_Type::enter_live_mode(int) { - Fl_Group *grp = new Fl_Pack(o->x(), o->y(), o->w(), o->h()); - return propagate_live_mode(grp); -} - -void Fl_Pack_Type::copy_properties() -{ - Fl_Group_Type::copy_properties(); - Fl_Pack *d = (Fl_Pack*)live_widget, *s =(Fl_Pack*)o; - d->spacing(s->spacing()); -} - -// ---- Fl_Flex_Type --------------------------------------------------- MARK: - - -const char flex_type_name[] = "Fl_Flex"; - -Fl_Menu_Item flex_type_menu[] = { - {"HORIZONTAL", 0, 0, (void*)Fl_Flex::HORIZONTAL}, - {"VERTICAL", 0, 0, (void*)Fl_Flex::VERTICAL}, - {0}}; - -Fl_Flex_Type Fl_Flex_type; // the "factory" - -/** - Override flex's resize behavior to do nothing to children by default. - - \param[in] X, Y, W, H new size - */ -void Fl_Flex_Proxy::resize(int X, int Y, int W, int H) { - if (Fl_Type::allow_layout > 0) { - Fl_Flex::resize(X, Y, W, H); - } else { - Fl_Widget::resize(X, Y, W, H); - } - redraw(); -} - -/** - Override draw() to make groups with no box or flat box background visible. - */ -void Fl_Flex_Proxy::draw() { - if (show_ghosted_outline && (box() == FL_NO_BOX)) { - fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); - } - Fl_Flex::draw(); -} - -Fl_Widget *Fl_Flex_Type::enter_live_mode(int) { - Fl_Flex *grp = new Fl_Flex(o->x(), o->y(), o->w(), o->h()); - propagate_live_mode(grp); - Fl_Flex *d = grp, *s =(Fl_Flex*)o; - int nc = s->children(), nd = d->children(); - if (nc>nd) nc = nd; - for (int i=0; ifixed(s->child(i))) { - Fl_Widget *dc = d->child(i); - d->fixed(d->child(i), s->horizontal() ? dc->w() : dc->h()); - } - } - return grp; -} - -void Fl_Flex_Type::copy_properties() -{ - Fl_Group_Type::copy_properties(); - Fl_Flex *d = (Fl_Flex*)live_widget, *s =(Fl_Flex*)o; - int lm, tm, rm, bm; - s->margin(&lm, &tm, &rm, &bm); - d->margin(lm, tm, rm, bm); - d->gap( s->gap() ); -} - -void Fl_Flex_Type::copy_properties_for_children() { - Fl_Flex *d = (Fl_Flex*)live_widget, *s =(Fl_Flex*)o; - for (int i=0; ichildren(); i++) { - if (s->fixed(s->child(i)) && ichildren()) { - if (s->horizontal()) { - d->fixed(d->child(i), d->child(i)->w()); - } else { - d->fixed(d->child(i), d->child(i)->h()); - } - } - } - d->layout(); -} - -void Fl_Flex_Type::write_properties(fld::io::Project_Writer &f) -{ - Fl_Group_Type::write_properties(f); - Fl_Flex* flex = (Fl_Flex*)o; - int lm, tm, rm, bm; - flex->margin(&lm, &tm, &rm, &bm); - if (lm!=0 || tm!=0 || rm!=0 || bm!=0) - f.write_string("margin {%d %d %d %d}", lm, tm, rm, bm); - if (flex->gap()) - f.write_string("gap %d", flex->gap()); - int nSet = 0; - for (int i=0; ichildren(); i++) - if (flex->fixed(flex->child(i))) - nSet++; - if (nSet) { - f.write_string("fixed_size_tuples {%d", nSet); - for (int i=0; ichildren(); i++) { - Fl_Widget *ci = flex->child(i); - if (flex->fixed(ci)) - f.write_string(" %d %d", i, flex->horizontal() ? ci->w() : ci->h()); - } - f.write_string("}"); - } -} - -void Fl_Flex_Type::read_property(fld::io::Project_Reader &f, const char *c) -{ - Fl_Flex* flex = (Fl_Flex*)o; - suspend_auto_layout = 1; - if (!strcmp(c,"margin")) { - int lm, tm, rm, bm; - if (sscanf(f.read_word(),"%d %d %d %d",&lm,&tm,&rm,&bm) == 4) - flex->margin(lm, tm, rm, bm); - } else if (!strcmp(c,"gap")) { - int g; - if (sscanf(f.read_word(),"%d",&g)) - flex->gap(g); - } else if (!strcmp(c,"fixed_size_tuples")) { - f.read_word(1); // must be '{' - const char *nStr = f.read_word(1); // number of indices in table - fixedSizeTupleSize = atoi(nStr); - fixedSizeTuple = new int[fixedSizeTupleSize*2]; - for (int i=0; i0) { - for (int i=0; i=0 && ixchildren()) { - Fl_Widget *ci = flex->child(ix); - flex->fixed(ci, size); - } - } - fixedSizeTupleSize = 0; - delete[] fixedSizeTuple; - fixedSizeTuple = NULL; - } - suspend_auto_layout = 0; -} - -void Fl_Flex_Type::write_code2(fld::io::Code_Writer& f) { - const char *var = name() ? name() : "o"; - Fl_Flex* flex = (Fl_Flex*)o; - int lm, tm, rm, bm; - flex->margin(&lm, &tm, &rm, &bm); - if (lm!=0 || tm!=0 || rm!=0 || bm!=0) - f.write_c("%s%s->margin(%d, %d, %d, %d);\n", f.indent(), var, lm, tm, rm, bm); - if (flex->gap()) - f.write_c("%s%s->gap(%d);\n", f.indent(), var, flex->gap()); - for (int i=0; ichildren(); ++i) { - Fl_Widget *ci = flex->child(i); - if (flex->fixed(ci)) - f.write_c("%s%s->fixed(%s->child(%d), %d);\n", f.indent(), var, var, i, - flex->horizontal() ? ci->w() : ci->h()); - } - Fl_Group_Type::write_code2(f); -} - -//void Fl_Flex_Type::add_child(Fl_Type* a, Fl_Type* b) { -// Fl_Group_Type::add_child(a, b); -// if (!suspend_auto_layout) -// ((Fl_Flex*)o)->layout(); -//} -// -//void Fl_Flex_Type::move_child(Fl_Type* a, Fl_Type* b) { -// Fl_Group_Type::move_child(a, b); -// if (!suspend_auto_layout) -// ((Fl_Flex*)o)->layout(); -//} - -void Fl_Flex_Type::remove_child(Fl_Type* a) { - if (a->is_widget()) - ((Fl_Flex*)o)->fixed(((Fl_Widget_Type*)a)->o, 0); - Fl_Group_Type::remove_child(a); -// ((Fl_Flex*)o)->layout(); - layout_widget(); -} - -void Fl_Flex_Type::layout_widget() { - allow_layout++; - ((Fl_Flex*)o)->layout(); - allow_layout--; -} - -// Change from HORIZONTAL to VERTICAL or back. -// Children in a horizontal Flex have already the full vertical height. If we -// just change to vertical, the accumulated hight of all children is too big. -// We need to relayout existing children. -void Fl_Flex_Type::change_subtype_to(int n) { - Fl_Flex* f = (Fl_Flex*)o; - if (f->type()==n) return; - - int nc = f->children(); - if (nc > 0) { - int dw = Fl::box_dw(f->box()); - int dh = Fl::box_dh(f->box()); - int lm, tm, rm, bm; - f->margin(&lm, &tm, &rm, &bm); - int gap = f->gap(); - int fw = f->w()-dw-lm-rm-(nc*gap); - if (fw<=nc) fw = nc; // avoid division by zero - int fh = f->h()-dh-tm-bm-(nc*gap); - if (fh<=nc) fh = nc; // avoid division by zero - - if (f->type()==Fl_Flex::HORIZONTAL && n==Fl_Flex::VERTICAL) { - float scl = (float)fh/(float)fw; - for (int i=0; ichild(i); - c->size(f->w(), (int)(c->w()*scl)); - } - } else if (f->type()==Fl_Flex::VERTICAL && n==Fl_Flex::HORIZONTAL) { - float scl = (float)fw/(float)fh; - for (int i=0; ichild(i); - c->size((int)(c->h()*scl), f->h()); - } - } - } - f->type(n); - f->layout(); -} - -int Fl_Flex_Type::parent_is_flex(Fl_Type *t) { - return (t->is_widget() - && t->parent - && t->parent->is_a(ID_Flex)); -} - -/** - Insert a widget in the child list so that it moves as close as possible the position. - - \param[in] child any widget in the tree but this, may already be a child of - this and will be relocated if so - \param[in] x, y pixel coordinates relative to the top left of the window - */ -void Fl_Flex_Type::insert_child_at(Fl_Widget *child, int x, int y) { - Fl_Flex *flex = (Fl_Flex*)o; - // find the insertion point closest to x, y - int d = flex->w() + flex->h(), di = -1; - if (flex->horizontal()) { - int i, dx; - for (i=0; ichildren(); i++) { - dx = x - flex->child(i)->x(); - if (dx < 0) dx = -dx; - if (dx < d) { d = dx; di = i; } - } - dx = x - (flex->x()+flex->w()); - if (dx < 0) dx = -dx; - if (dx < d) { d = dx; di = i; } - } else { - int i, dy; - for (i=0; ichildren(); i++) { - dy = y - flex->child(i)->y(); - if (dy < 0) dy = -dy; - if (dy < d) { d = dy; di = i; } - } - dy = y - (flex->y()+flex->h()); - if (dy < 0) dy = -dy; - if (dy < d) { d = dy; di = i; } - } - if (di > -1) { - flex->insert(*child, di); - } -} - -/** Move children around using the keyboard. - \param[in] child pointer to the child type - \param[in] key code of the last keypress when handling a FL_KEYBOARD event. - */ -void Fl_Flex_Type::keyboard_move_child(Fl_Widget_Type *child, int key) { - Fl_Flex *flex = ((Fl_Flex*)o); - int ix = flex->find(child->o); - if (ix == flex->children()) return; - if (flex->horizontal()) { - if (key==FL_Right) { - flex->insert(*child->o, ix+2); - } else if (key==FL_Left) { - if (ix > 0) flex->insert(*child->o, ix-1); - } - } else { - if (key==FL_Down) { - flex->insert(*child->o, ix+2); - } else if (key==FL_Up) { - if (ix > 0) flex->insert(*child->o, ix-1); - } - } -} - -int Fl_Flex_Type::size(Fl_Type *t, char fixed_only) { - if (!t->is_widget()) return 0; - if (!t->parent) return 0; - if (!t->parent->is_a(ID_Flex)) return 0; - Fl_Flex_Type* ft = (Fl_Flex_Type*)t->parent; - Fl_Flex* f = (Fl_Flex*)ft->o; - Fl_Widget *w = ((Fl_Widget_Type*)t)->o; - if (fixed_only && !f->fixed(w)) return 0; - return f->horizontal() ? w->w() : w->h(); -} - -int Fl_Flex_Type::is_fixed(Fl_Type *t) { - if (!t->is_widget()) return 0; - if (!t->parent) return 0; - if (!t->parent->is_a(ID_Flex)) return 0; - Fl_Flex_Type* ft = (Fl_Flex_Type*)t->parent; - Fl_Flex* f = (Fl_Flex*)ft->o; - Fl_Widget *w = ((Fl_Widget_Type*)t)->o; - return f->fixed(w); -} - -// ---- Fl_Table_Type -------------------------------------------------- MARK: - - -Fl_Table_Type Fl_Table_type; // the "factory" - -static const int MAX_ROWS = 14; -static const int MAX_COLS = 7; - -// this is a minimal table widget used as an example when adding tables in Fluid -class Fluid_Table : public Fl_Table { - int data[MAX_ROWS][MAX_COLS]; // data array for cells - - // Draw the row/col headings - // Make this a dark thin upbox with the text inside. - // - void DrawHeader(const char *s, int X, int Y, int W, int H) { - fl_push_clip(X,Y,W,H); - fl_draw_box(FL_THIN_UP_BOX, X,Y,W,H, row_header_color()); - fl_color(FL_BLACK); - fl_draw(s, X,Y,W,H, FL_ALIGN_CENTER); - fl_pop_clip(); - } - // Draw the cell data - // Dark gray text on white background with subtle border - // - void DrawData(const char *s, int X, int Y, int W, int H) { - fl_push_clip(X,Y,W,H); - // Draw cell bg - fl_color(FL_WHITE); fl_rectf(X,Y,W,H); - // Draw cell data - fl_color(FL_GRAY0); fl_draw(s, X,Y,W,H, FL_ALIGN_CENTER); - // Draw box border - fl_color(color()); fl_rect(X,Y,W,H); - fl_pop_clip(); - } - // Handle drawing table's cells - // Fl_Table calls this function to draw each visible cell in the table. - // It's up to us to use FLTK's drawing functions to draw the cells the way we want. - // - void draw_cell(TableContext context, int ROW=0, int COL=0, int X=0, int Y=0, int W=0, int H=0) FL_OVERRIDE { - static char s[40]; - switch ( context ) { - case CONTEXT_STARTPAGE: // before page is drawn.. - fl_font(FL_HELVETICA, 16); // set the font for our drawing operations - return; - case CONTEXT_COL_HEADER: // Draw column headers - sprintf(s,"%c",'A'+COL); // "A", "B", "C", etc. - DrawHeader(s,X,Y,W,H); - return; - case CONTEXT_ROW_HEADER: // Draw row headers - sprintf(s,"%03d:",ROW); // "001:", "002:", etc - DrawHeader(s,X,Y,W,H); - return; - case CONTEXT_CELL: // Draw data in cells - sprintf(s,"%d",data[ROW][COL]); - DrawData(s,X,Y,W,H); - return; - default: - return; - } - } -public: - Fluid_Table(int x, int y, int w, int h, const char *l=0L) - : Fl_Table(x, y, w, h, l) { - end(); - for ( int r=0; ro : 0; - if (((Fl_Table*)o)->children()==1) { // the FLuid_Table has one extra child - fl_message("Inserting child widgets into an Fl_Table is not recommended.\n" - "Please refer to the documentation on Fl_Table."); - } - ((Fl_Table*)o)->insert(*(c->o), b); - o->redraw(); -} - -void Fl_Table_Type::remove_child(Fl_Type* cc) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - ((Fl_Table*)o)->remove(*(c->o)); - o->redraw(); -} - -void Fl_Table_Type::move_child(Fl_Type* cc, Fl_Type* before) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - Fl_Widget* b = before ? ((Fl_Widget_Type*)before)->o : 0; - ((Fl_Table*)o)->insert(*(c->o), b); - o->redraw(); -} - -Fl_Widget *Fl_Table_Type::enter_live_mode(int) { - Fl_Group *grp = new Fluid_Table(o->x(), o->y(), o->w(), o->h()); - live_widget = grp; - copy_properties(); - grp->end(); - return live_widget; -} - -void Fl_Table_Type::ideal_size(int &w, int &h) { - w = 160; - h = 120; - Fd_Snap_Action::better_size(w, h); -} - -// ---- Fl_Tabs_Type --------------------------------------------------- MARK: - - -Fl_Tabs_Type Fl_Tabs_type; // the "factory" - -const char tabs_type_name[] = "Fl_Tabs"; - -// Override group's resize behavior to do nothing to children: -void Fl_Tabs_Proxy::resize(int X, int Y, int W, int H) { - if (Fl_Type::allow_layout > 0) { - Fl_Tabs::resize(X, Y, W, H); - } else { - Fl_Widget::resize(X, Y, W, H); - } - redraw(); -} - -/** - Override draw() to make groups with no box or flat box background visible. - */ -void Fl_Tabs_Proxy::draw() { - if (show_ghosted_outline && (box() == FL_NO_BOX)) { - fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); - } - Fl_Tabs::draw(); -} - -// This is called when user clicks on a widget in the window. See -// if it is a tab title, and adjust visibility and return new selection: -// If none, return o unchanged: - -Fl_Type* Fl_Tabs_Type::click_test(int x, int y) { - Fl_Tabs *t = (Fl_Tabs*)o; - Fl_Widget *a = t->which(x,y); - if (!a) return 0; // didn't click on tab - // changing the visible tab has an impact on the generated - // source code, so mark this project as changed. - int changed = (a!=t->value()); - // okay, run the tabs ui until they let go of mouse: - t->handle(FL_PUSH); - Fl::pushed(t); - while (Fl::pushed()==t) Fl::wait(); - if (changed) set_modflag(1); - return (Fl_Type*)(t->value()->user_data()); -} - -void Fl_Tabs_Type::add_child(Fl_Type* c, Fl_Type* before) { - Fl_Group_Type::add_child(c, before); -} - -void Fl_Tabs_Type::remove_child(Fl_Type* cc) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - Fl_Tabs *t = (Fl_Tabs*)o; - if (t->value() == c->o) t->value(0); - Fl_Group_Type::remove_child(c); -} - -Fl_Widget *Fl_Tabs_Type::enter_live_mode(int) { - Fl_Tabs *original = static_cast(o); - Fl_Tabs *clone = new Fl_Tabs(o->x(), o->y(), o->w(), o->h()); - propagate_live_mode(clone); - int tab_index = original->find(original->value()); - if ((tab_index>=0) && (tab_indexchildren())) - clone->value(clone->child(tab_index)); - return clone; -} - -// ---- Fl_Scroll_Type ------------------------------------------------- MARK: - - -Fl_Scroll_Type Fl_Scroll_type; // the "factory" - -const char scroll_type_name[] = "Fl_Scroll"; - -Fl_Menu_Item scroll_type_menu[] = { - {"BOTH", 0, 0, 0/*(void*)Fl_Scroll::BOTH*/}, - {"HORIZONTAL", 0, 0, (void*)Fl_Scroll::HORIZONTAL}, - {"VERTICAL", 0, 0, (void*)Fl_Scroll::VERTICAL}, - {"HORIZONTAL_ALWAYS", 0, 0, (void*)Fl_Scroll::HORIZONTAL_ALWAYS}, - {"VERTICAL_ALWAYS", 0, 0, (void*)Fl_Scroll::VERTICAL_ALWAYS}, - {"BOTH_ALWAYS", 0, 0, (void*)Fl_Scroll::BOTH_ALWAYS}, - {0}}; - -Fl_Widget *Fl_Scroll_Type::enter_live_mode(int) { - Fl_Group *grp = new Fl_Scroll(o->x(), o->y(), o->w(), o->h()); - grp->show(); - return propagate_live_mode(grp); -} - -void Fl_Scroll_Type::copy_properties() { - Fl_Group_Type::copy_properties(); - Fl_Scroll *s = (Fl_Scroll*)o, *d = (Fl_Scroll*)live_widget; - d->scroll_to(s->xposition(), s->yposition()); - d->type(s->type()); - d->scrollbar.align(s->scrollbar.align()); - d->hscrollbar.align(s->hscrollbar.align()); -} - -// ---- Fl_Tile_Type --------------------------------------------------- MARK: - - -Fl_Tile_Type Fl_Tile_type; // the "factory" - -const char tile_type_name[] = "Fl_Tile"; - -void Fl_Tile_Type::copy_properties() { - Fl_Group_Type::copy_properties(); - // no additional properties -} - -// ---- Fl_Wizard_Type ------------------------------------------------ MARK: - - -Fl_Wizard_Type Fl_Wizard_type; // the "factory" - -const char wizard_type_name[] = "Fl_Wizard"; - -// Override group's resize behavior to do nothing to children: -void Fl_Wizard_Proxy::resize(int X, int Y, int W, int H) { - if (Fl_Type::allow_layout > 0) { - Fl_Wizard::resize(X, Y, W, H); - } else { - Fl_Widget::resize(X, Y, W, H); - } - redraw(); -} - -/** - Override draw() to make groups with no box or flat box background visible. - */ -void Fl_Wizard_Proxy::draw() { - if (show_ghosted_outline && (box() == FL_NO_BOX)) { - fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); - } - Fl_Wizard::draw(); -} - diff --git a/fluid/nodes/Fl_Group_Type.h b/fluid/nodes/Fl_Group_Type.h deleted file mode 100644 index 30fab1d20..000000000 --- a/fluid/nodes/Fl_Group_Type.h +++ /dev/null @@ -1,242 +0,0 @@ -// -// Group type header file for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 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_FL_GROUP_TYPE_H -#define _FLUID_FL_GROUP_TYPE_H - -#include "nodes/Fl_Widget_Type.h" - -#include -#include -#include -#include - -void group_cb(Fl_Widget *, void *); -void ungroup_cb(Fl_Widget *, void *); - -// ---- Fl_Group_Type -------------------------------------------------- MARK: - - -/** - Proxy group to use in place of Fl_Group in the interactive window. - - In an interactive environment, groups should not automatically resize their - children. This proxy disables the layout of children by default. Children - layout propagation may be enable temporarily by incrementing `allow_layout` - before resizing and decrementing it again afterwards. - */ -class Fl_Group_Proxy : public Fl_Group { -public: - Fl_Group_Proxy(int X,int Y,int W,int H) : Fl_Group(X, Y, W, H) { Fl_Group::current(0); } - void resize(int x, int y, int w, int h) FL_OVERRIDE; - void draw() FL_OVERRIDE; -}; - -class Fl_Group_Type : public Fl_Widget_Type -{ - typedef Fl_Widget_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "Fl_Group";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::Group";} - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - Fl_Group_Proxy *g = new Fl_Group_Proxy(X,Y,W,H); Fl_Group::current(0); return g;} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Group_Type();} - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void remove_child(Fl_Type*) FL_OVERRIDE; - int can_have_children() const FL_OVERRIDE {return 1;} - ID id() const FL_OVERRIDE { return ID_Group; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Group) ? true : super::is_a(inID); } - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void leave_live_mode() FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; -}; - -// ---- Fl_Pack_Type --------------------------------------------------- MARK: - - -extern const char pack_type_name[]; -extern Fl_Menu_Item pack_type_menu[]; - -class Fl_Pack_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE {return pack_type_menu;} -public: - const char *type_name() FL_OVERRIDE {return pack_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::PackedGroup";} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Pack_Type();} - ID id() const FL_OVERRIDE { return ID_Pack; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Pack) ? true : super::is_a(inID); } - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; -}; - -// ---- Fl_Flex_Type --------------------------------------------------- MARK: - - -extern const char flex_type_name[]; -extern Fl_Menu_Item flex_type_menu[]; - -class Fl_Flex_Proxy : public Fl_Flex { -public: - Fl_Flex_Proxy(int X,int Y,int W,int H) : Fl_Flex(X, Y, W, H) { Fl_Group::current(0); } - void resize(int x, int y, int w, int h) FL_OVERRIDE; - void draw() FL_OVERRIDE; -}; - -class Fl_Flex_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE {return flex_type_menu;} - int fixedSizeTupleSize; /* number of pairs in array */ - int *fixedSizeTuple; /* [ index, size, index2, size2, ... ] */ - int suspend_auto_layout; -public: - Fl_Flex_Type() : fixedSizeTupleSize(0), fixedSizeTuple(NULL), suspend_auto_layout(0) { } - const char *type_name() FL_OVERRIDE {return flex_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::FlexGroup";} - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Flex_Type(); } - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - Fl_Flex *g = new Fl_Flex_Proxy(X,Y,W,H); Fl_Group::current(0); return g;} - ID id() const FL_OVERRIDE { return ID_Flex; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Flex) ? true : super::is_a(inID); } - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; - void copy_properties_for_children() FL_OVERRIDE; - void postprocess_read() FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; -// void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; -// void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void remove_child(Fl_Type*) FL_OVERRIDE; - void layout_widget() FL_OVERRIDE; - void change_subtype_to(int n); - void insert_child_at(Fl_Widget *child, int x, int y); - void keyboard_move_child(Fl_Widget_Type*, int key); - static int parent_is_flex(Fl_Type*); - static int size(Fl_Type*, char fixed_only=0); - static int is_fixed(Fl_Type*); -}; - -// ---- Fl_Table_Type -------------------------------------------------- MARK: - - -class Fl_Table_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE; - const char *type_name() FL_OVERRIDE { return "Fl_Table"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::TableGroup"; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Table_Type(); } - Fl_Widget *widget(int X, int Y, int W, int H) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Table; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Table) ? true : super::is_a(inID); } - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void remove_child(Fl_Type*) FL_OVERRIDE; -}; - -// ---- Fl_Tabs_Type --------------------------------------------------- MARK: - - -extern const char tabs_type_name[]; - -class Fl_Tabs_Proxy : public Fl_Tabs { -public: - Fl_Tabs_Proxy(int X,int Y,int W,int H) : Fl_Tabs(X,Y,W,H) {} - void resize(int,int,int,int) FL_OVERRIDE; - void draw() FL_OVERRIDE; -}; - -class Fl_Tabs_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; -public: - const char *type_name() FL_OVERRIDE {return tabs_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::TabGroup";} - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - Fl_Tabs_Proxy *g = new Fl_Tabs_Proxy(X,Y,W,H); Fl_Group::current(0); return g;} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Tabs_Type();} - Fl_Type* click_test(int,int) FL_OVERRIDE; - void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void remove_child(Fl_Type*) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Tabs; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Tabs) ? true : super::is_a(inID); } - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; -}; - -// ---- Fl_Scroll_Type ------------------------------------------------- MARK: - - -extern const char scroll_type_name[]; -extern Fl_Menu_Item scroll_type_menu[]; - -class Fl_Scroll_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE {return scroll_type_menu;} -public: - const char *type_name() FL_OVERRIDE {return scroll_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::ScrollGroup";} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Scroll_Type();} - ID id() const FL_OVERRIDE { return ID_Scroll; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Scroll) ? true : super::is_a(inID); } - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; -}; - -// ---- Fl_Tile_Type --------------------------------------------------- MARK: - - -extern const char tile_type_name[]; - -class Fl_Tile_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; -public: - const char *type_name() FL_OVERRIDE {return tile_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::TileGroup";} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Tile_Type();} - ID id() const FL_OVERRIDE { return ID_Tile; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Tile) ? true : super::is_a(inID); } - void copy_properties() FL_OVERRIDE; -}; - -// ---- Fl_Wizard_Type ------------------------------------------------- MARK: - - -class Fl_Wizard_Proxy : public Fl_Wizard { -public: - Fl_Wizard_Proxy(int X,int Y,int W,int H) : Fl_Wizard(X,Y,W,H) {} - void resize(int,int,int,int) FL_OVERRIDE; - void draw() FL_OVERRIDE; -}; - -extern const char wizard_type_name[]; - -class Fl_Wizard_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; -public: - const char *type_name() FL_OVERRIDE {return wizard_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::WizardGroup";} - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - Fl_Wizard_Proxy *g = new Fl_Wizard_Proxy(X,Y,W,H); Fl_Group::current(0); return g;} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Wizard_Type();} - ID id() const FL_OVERRIDE { return ID_Wizard; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Wizard) ? true : super::is_a(inID); } -}; - -#endif // _FLUID_FL_GROUP_TYPE_H diff --git a/fluid/nodes/Fl_Menu_Type.cxx b/fluid/nodes/Fl_Menu_Type.cxx deleted file mode 100644 index 83435df50..000000000 --- a/fluid/nodes/Fl_Menu_Type.cxx +++ /dev/null @@ -1,924 +0,0 @@ -// -// Menu item code for the Fast Light Tool Kit (FLTK). -// -// Menu items are kludged by making a phony Fl_Box widget so the normal -// widget panel can be used to control them. -// -// This file also contains code to make Fl_Menu_Button, Fl_Menu_Bar, -// etc widgets. -// -// Copyright 1998-2023 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 "nodes/Fl_Menu_Type.h" - -#include "app/fluid.h" -#include "app/project.h" -#include "app/Fluid_Image.h" -#include "app/mergeback.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "nodes/Fl_Window_Type.h" -#include "widgets/Formula_Input.h" -#include "widgets/Node_Browser.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../src/flstring.h" - -#include -#include - -Fl_Menu_Item menu_item_type_menu[] = { - {"Normal",0,0,(void*)0}, - {"Toggle",0,0,(void*)FL_MENU_BOX}, - {"Radio",0,0,(void*)FL_MENU_RADIO}, - {0}}; - -static void delete_dependents(Fl_Menu_Item *m) { - if (!m) - return; - int level = 0; - for (;;m++) { - if (m->label()==NULL) { - if (level==0) { - break; - } else { - level--; - } - } - if (m->flags&FL_SUBMENU) - level++; - if (m->labeltype()==FL_MULTI_LABEL) - delete (Fl_Multi_Label*)m->label(); - } -} - -static void delete_menu(Fl_Menu_Item *m) { - if (!m) - return; - delete_dependents(m); - delete[] m; -} - -void Fl_Input_Choice_Type::build_menu() { - Fl_Input_Choice* w = (Fl_Input_Choice*)o; - // count how many Fl_Menu_Item structures needed: - int n = 0; - Fl_Type* q; - for (q = next; q && q->level > level; q = q->next) { - if (q->can_have_children()) n++; // space for null at end of submenu - n++; - } - if (!n) { - if (menusize) delete_menu((Fl_Menu_Item*)(w->menu())); - w->menu(0); - menusize = 0; - } else { - n++; // space for null at end of menu - if (menusizemenu())); - menusize = n+10; - w->menu(new Fl_Menu_Item[menusize]); - } else { - if (menusize) delete_dependents((Fl_Menu_Item*)(w->menu())); - } - // Menus are already built during the .fl file reading process, so if the - // end of a menu list is not read yet, the end markers (label==NULL) will - // not be set, and deleting dependents will randomly free memory. - // Clearing the array should avoid that. - memset( (void*)w->menu(), 0, menusize * sizeof(Fl_Menu_Item) ); - // fill them all in: - Fl_Menu_Item* m = (Fl_Menu_Item*)(w->menu()); - int lvl = level+1; - for (q = next; q && q->level > level; q = q->next) { - Fl_Menu_Item_Type* i = (Fl_Menu_Item_Type*)q; - if (i->o->image()) { - if (i->o->label() && i->o->label()[0]) { - Fl_Multi_Label *ml = new Fl_Multi_Label; - ml->labela = (char*)i->o->image(); - ml->labelb = i->o->label(); - ml->typea = FL_IMAGE_LABEL; - ml->typeb = FL_NORMAL_LABEL; - ml->label(m); - } else { - i->o->image()->label(m); - } - } else { - m->label(i->o->label() ? i->o->label() : "(nolabel)"); - m->labeltype(i->o->labeltype()); - } - m->shortcut(((Fl_Button*)(i->o))->shortcut()); - m->callback(0,(void*)i); - m->flags = i->flags(); - m->labelfont(i->o->labelfont()); - m->labelsize(i->o->labelsize()); - m->labelcolor(i->o->labelcolor()); - if (q->can_have_children()) {lvl++; m->flags |= FL_SUBMENU;} - m++; - int l1 = - (q->next && q->next->is_a(ID_Menu_Item)) ? q->next->level : level; - while (lvl > l1) {m->label(0); m++; lvl--;} - lvl = l1; - } - } - o->redraw(); -} - -/** - Create and add a new Menu Item node. - \param[in] strategy add after current or as last child - \return new Menu Item node - */ -Fl_Type *Fl_Menu_Item_Type::make(Strategy strategy) { - return Fl_Menu_Item_Type::make(0, strategy); -} - -/** - Create an add a specific Menu Item node. - \param[in] flags set to 0, FL_MENU_RADIO, FL_MENU_TOGGLE, or FL_SUBMENU - \param[in] strategy add after current or as last child - \return new Menu Item node - */ -Fl_Type* Fl_Menu_Item_Type::make(int flags, Strategy strategy) { - // Find a good insert position based on the current marked node - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !(p->is_a(ID_Menu_Manager_) || p->is_a(ID_Submenu))) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - if (!p) { - fl_message("Please select a menu widget or a menu item"); - return 0; - } - if (!o) { - o = new Fl_Button(0,0,100,20); // create template widget - } - - Fl_Menu_Item_Type* t = NULL; - if (flags==FL_SUBMENU) { - t = new Fl_Submenu_Type(); - } else { - t = new Fl_Menu_Item_Type(); - } - t->o = new Fl_Button(0,0,100,20); - t->o->type(flags); - t->factory = this; - t->add(anchor, strategy); - if (strategy.source() == Strategy::FROM_USER) { - if (flags==FL_SUBMENU) { - t->label("submenu"); - } else { - t->label("item"); - } - } - return t; -} - -void group_selected_menuitems() { - // The group will be created in the parent group of the current menuitem - if (!Fl_Type::current->is_a(ID_Menu_Item)) { - return; - } - Fl_Menu_Item_Type *q = static_cast(Fl_Type::current); - Fl_Type *qq = Fl_Type::current->parent; - if (!qq || !(qq->is_a(ID_Menu_Manager_) || qq->is_a(ID_Submenu))) { - fl_message("Can't create a new submenu here."); - return; - } - undo_checkpoint(); - undo_suspend(); - Fl_Widget_Type *n = (Fl_Widget_Type*)(q->make(FL_SUBMENU, Strategy::AFTER_CURRENT)); - for (Fl_Type *t = qq->next; t && (t->level > qq->level);) { - if (t->level != n->level || t == n || !t->selected) { - t = t->next; - continue; - } - Fl_Type *nxt = t->remove(); - t->add(n, Strategy::AS_LAST_CHILD); - t = nxt; - } - widget_browser->rebuild(); - undo_resume(); - set_modflag(1); -} - -void ungroup_selected_menuitems() { - // Find the submenu - Fl_Type *qq = Fl_Type::current->parent; - Fl_Widget_Type *q = static_cast(Fl_Type::current); - int q_level = q->level; - if (!qq || !qq->is_a(ID_Submenu)) { - fl_message("Only menu items inside a submenu can be ungrouped."); - return; - } - undo_checkpoint(); - undo_suspend(); - Fl_Type::current = qq; - for (Fl_Type *t = qq->next; t && (t->level > qq->level);) { - if (t->level != q_level || !t->selected) { - t = t->next; - continue; - } - Fl_Type *nxt = t->remove(); - t->insert(qq); - t = nxt; - } - if (!qq->next || (qq->next->level <= qq->level)) { - qq->remove(); - delete qq; // qq has no children that need to be delete - } - Fl_Type::current = q; - widget_browser->rebuild(); - undo_resume(); - set_modflag(1); -} - - -/** - Create and add a new Checkbox Menu Item node. - \param[in] strategy add after current or as last child - \return new node - */ -Fl_Type *Fl_Checkbox_Menu_Item_Type::make(Strategy strategy) { - return Fl_Menu_Item_Type::make(FL_MENU_TOGGLE, strategy); -} - -/** - Create and add a new Radio ButtonMenu Item node. - \param[in] strategy add after current or as last child - \return new node - */ -Fl_Type *Fl_Radio_Menu_Item_Type::make(Strategy strategy) { - return Fl_Menu_Item_Type::make(FL_MENU_RADIO, strategy); -} - -/** - Create and add a new Submenu Item node. - \param[in] strategy add after current or as last child - \return new node - */ -Fl_Type *Fl_Submenu_Type::make(Strategy strategy) { - return Fl_Menu_Item_Type::make(FL_SUBMENU, strategy); -} - -Fl_Menu_Item_Type Fl_Menu_Item_type; -Fl_Checkbox_Menu_Item_Type Fl_Checkbox_Menu_Item_type; -Fl_Radio_Menu_Item_Type Fl_Radio_Menu_Item_type; -Fl_Submenu_Type Fl_Submenu_type; - -//////////////////////////////////////////////////////////////// -// Writing the C code: - -// test functions in Fl_Widget_Type.C: -int is_name(const char *c); -const char *array_name(Fl_Widget_Type *o); -int isdeclare(const char *c); - -// Search backwards to find the parent menu button and return it's name. -// Also put in i the index into the button's menu item array belonging -// to this menu item. -const char* Fl_Menu_Item_Type::menu_name(fld::io::Code_Writer& f, int& i) { - i = 0; - Fl_Type* t = prev; - while (t && t->is_a(ID_Menu_Item)) { - // be sure to count the {0} that ends a submenu: - if (t->level > t->next->level) i += (t->level - t->next->level); - // detect empty submenu: - else if (t->level == t->next->level && t->can_have_children()) i++; - t = t->prev; - i++; - } - if (!t) return "\n#error Fl_Menu_Item_Type::menu_name, invalid f\n"; - return f.unique_id(t, "menu", t->name(), t->label()); -} - -void Fl_Menu_Item_Type::write_static(fld::io::Code_Writer& f) { - if (image && label() && label()[0]) { - f.write_h_once("#include "); - f.write_h_once("#include "); - } - if (callback() && is_name(callback()) && !user_defined(callback())) - f.write_h_once("extern void %s(Fl_Menu_*, %s);", callback(), - user_data_type() ? user_data_type() : "void*"); - for (int n=0; n < NUM_EXTRA_CODE; n++) { - if (extra_code(n) && isdeclare(extra_code(n))) - f.write_h_once("%s", extra_code(n)); - } - if (callback() && !is_name(callback())) { - // see if 'o' or 'v' used, to prevent unused argument warnings: - int use_o = 0; - int use_v = 0; - const char *d; - for (d = callback(); *d;) { - if (*d == 'o' && !is_id(d[1])) use_o = 1; - if (*d == 'v' && !is_id(d[1])) use_v = 1; - do d++; while (is_id(*d)); - while (*d && !is_id(*d)) d++; - } - const char* cn = callback_name(f); - const char* k = class_name(1); - if (k) { - f.write_c("\nvoid %s::%s_i(Fl_Menu_*", k, cn); - } else { - f.write_c("\nstatic void %s(Fl_Menu_*", cn); - } - if (use_o) f.write_c(" o"); - const char* ut = user_data_type() ? user_data_type() : "void*"; - f.write_c(", %s", ut); - if (use_v) f.write_c(" v"); - f.write_c(") {\n"); - // Matt: disabled f.tag(FD_TAG_GENERIC, 0); - f.write_c_indented(callback(), 1, 0); - if (*(d-1) != ';' && *(d-1) != '}') { - const char *p = strrchr(callback(), '\n'); - if (p) p ++; - else p = callback(); - // Only add trailing semicolon if the last line is not a preprocessor - // statement... - if (*p != '#' && *p) f.write_c(";"); - } - f.write_c("\n"); - // Matt: disabled f.tag(FD_TAG_MENU_CALLBACK, get_uid()); - f.write_c("}\n"); - - // If the menu item is part of a Class or Widget Class, FLUID generates - // a dummy static callback which retrieves a pointer to the class and then - // calls the original callback from within the class context. - // k is the name of the enclosing class (or classes) - if (k) { - // Implement the callback as a static member function - f.write_c("void %s::%s(Fl_Menu_* o, %s v) {\n", k, cn, ut); - // Find the Fl_Menu_ container for this menu item - Fl_Type* t = parent; while (t->is_a(ID_Menu_Item)) t = t->parent; - if (t) { - Fl_Widget_Type *tw = (t->is_widget()) ? static_cast(t) : NULL; - Fl_Type *q = NULL; - // Generate code to call the callback - if (tw->is_a(ID_Menu_Bar) && ((Fl_Menu_Bar_Type*)tw)->is_sys_menu_bar()) { - // Fl_Sys_Menu_Bar removes itself from any parent on macOS, so we - // wrapped it in a class and remeber the parent class in a new - // class memeber variable. - Fl_Menu_Bar_Type *tmb = (Fl_Menu_Bar_Type*)tw; - f.write_c("%s%s* sys_menu_bar = ((%s*)o);\n", f.indent(1), - tmb->sys_menubar_proxy_name(), tmb->sys_menubar_proxy_name()); - f.write_c("%s%s* parent_class = ((%s*)sys_menu_bar->_parent_class);\n", - f.indent(1), k, k); - f.write_c("%sparent_class->%s_i(o,v);\n}\n", - f.indent(1), cn); - } else { - f.write_c("%s((%s*)(o", f.indent(1), k); - // The class pointer is in the user_data field of the top widget - if (t && t->is_a(ID_Input_Choice)) { - // Go up one more level for Fl_Input_Choice, as these are groups themselves - f.write_c("->parent()"); - } - // Now generate code to find the topmost widget in this class - for (t = t->parent; t && t->is_widget() && !is_class(); q = t, t = t->parent) - f.write_c("->parent()"); - // user_data is cast into a pointer to the - if (!q || !q->is_a(ID_Widget_Class)) - f.write_c("->user_data()"); - f.write_c("))->%s_i(o,v);\n}\n", cn); - } - } else { - f.write_c("#error Enclosing Fl_Menu_* not found\n"); - } - } - } - if (image) { - if (!f.c_contains(image)) - image->write_static(f, compress_image_); - } - if (next && next->is_a(ID_Menu_Item)) return; - // okay, when we hit last item in the menu we have to write the - // entire array out: - const char* k = class_name(1); - if (k) { - int i; - f.write_c("\nFl_Menu_Item %s::%s[] = {\n", k, menu_name(f, i)); - } else { - int i; - f.write_c("\nFl_Menu_Item %s[] = {\n", menu_name(f, i)); - } - Fl_Type* t = prev; while (t && t->is_a(ID_Menu_Item)) t = t->prev; - for (Fl_Type* q = t->next; q && q->is_a(ID_Menu_Item); q = q->next) { - ((Fl_Menu_Item_Type*)q)->write_item(f); - int thislevel = q->level; if (q->can_have_children()) thislevel++; - int nextlevel = - (q->next && q->next->is_a(ID_Menu_Item)) ? q->next->level : t->level+1; - while (thislevel > nextlevel) {f.write_c(" {0,0,0,0,0,0,0,0,0},\n"); thislevel--;} - } - f.write_c(" {0,0,0,0,0,0,0,0,0}\n};\n"); - - if (k) { - // Write menu item variables... - t = prev; while (t && t->is_a(ID_Menu_Item)) t = t->prev; - for (Fl_Type* q = t->next; q && q->is_a(ID_Menu_Item); q = q->next) { - Fl_Menu_Item_Type *m = (Fl_Menu_Item_Type*)q; - const char *c = array_name(m); - if (c) { - if (c==m->name()) { - // assign a menu item address directly to a variable - int i; - const char* n = ((Fl_Menu_Item_Type *)q)->menu_name(f, i); - f.write_c("Fl_Menu_Item* %s::%s = %s::%s + %d;\n", k, c, k, n, i); - } else { - // if the name is an array, only define the array. - // The actual assignment is in write_code1(fld::io::Code_Writer& f) - f.write_c("Fl_Menu_Item* %s::%s;\n", k, c); - } - } - } - } -} - -int Fl_Menu_Item_Type::flags() { - int i = o->type(); - if (((Fl_Button*)o)->value()) i |= FL_MENU_VALUE; - if (!o->active()) i |= FL_MENU_INACTIVE; - if (!o->visible()) i |= FL_MENU_INVISIBLE; - if (can_have_children()) { - if (user_data() == NULL) i |= FL_SUBMENU; - else i |= FL_SUBMENU_POINTER; - } - if (hotspot()) i |= FL_MENU_DIVIDER; - return i; -} - -void Fl_Menu_Item_Type::write_item(fld::io::Code_Writer& f) { - static const char * const labeltypes[] = { - "FL_NORMAL_LABEL", - "FL_NO_LABEL", - "FL_SHADOW_LABEL", - "FL_ENGRAVED_LABEL", - "FL_EMBOSSED_LABEL", - "FL_MULTI_LABEL", - "FL_ICON_LABEL", - "FL_IMAGE_LABEL" - }; - - write_comment_inline_c(f, " "); - f.write_c(" {"); - if (label() && label()[0]) - switch (g_project.i18n_type) { - case FD_I18N_GNU: - // we will call i18n when the menu is instantiated for the first time - f.write_c("%s(", g_project.i18n_gnu_static_function.c_str()); - f.write_cstring(label()); - f.write_c(")"); - break; - case FD_I18N_POSIX: - // fall through: strings can't be translated before a catalog is chosen - default: - f.write_cstring(label()); - } - else - f.write_c("\"\""); - if (((Fl_Button*)o)->shortcut()) { - int s = ((Fl_Button*)o)->shortcut(); - f.write_c(", "); - if (g_project.use_FL_COMMAND) { - if (s & FL_CTRL) { f.write_c("FL_CONTROL|"); s &= ~FL_CTRL; } - if (s & FL_META) { f.write_c("FL_COMMAND|"); s &= ~FL_META; } - } else { - if (s & FL_CTRL) { f.write_c("FL_CTRL|"); s &= ~FL_CTRL; } - if (s & FL_META) { f.write_c("FL_META|"); s &= ~FL_META; } - } - if (s & FL_SHIFT) { f.write_c("FL_SHIFT|"); s &= ~FL_SHIFT; } - if (s & FL_ALT) { f.write_c("FL_ALT|"); s &= ~FL_ALT; } - if ((s < 127) && isprint(s)) - f.write_c("'%c', ", s); - else - f.write_c("0x%x, ", s); - } else { - f.write_c(", 0, "); - } - if (callback()) { - const char* k = is_name(callback()) ? 0 : class_name(1); - if (k) { - f.write_c(" (Fl_Callback*)%s::%s,", k, callback_name(f)); - } else { - f.write_c(" (Fl_Callback*)%s,", callback_name(f)); - } - } else - f.write_c(" 0,"); - if (user_data()) - f.write_c(" (void*)(%s),", user_data()); - else - f.write_c(" 0,"); - f.write_c(" %d, (uchar)%s, %d, %d, %d", flags(), - labeltypes[o->labeltype()], o->labelfont(), o->labelsize(), o->labelcolor()); - f.write_c("},\n"); -} - -void start_menu_initialiser(fld::io::Code_Writer& f, int &initialized, const char *name, int index) { - if (!initialized) { - initialized = 1; - f.write_c("%s{ Fl_Menu_Item* o = &%s[%d];\n", f.indent(), name, index); - f.indentation++; - } -} - -void Fl_Menu_Item_Type::write_code1(fld::io::Code_Writer& f) { - int i; const char* mname = menu_name(f, i); - - if (!prev->is_a(ID_Menu_Item)) { - // for first menu item, declare the array - if (class_name(1)) { - f.write_h("%sstatic Fl_Menu_Item %s[];\n", f.indent(1), mname); - } else { - f.write_h("extern Fl_Menu_Item %s[];\n", mname); - } - } - - const char *c = array_name(this); - if (c) { - if (class_name(1)) { - f.write_public(public_); - f.write_h("%sstatic Fl_Menu_Item *%s;\n", f.indent(1), c); - } else { - if (c==name()) - f.write_h("#define %s (%s+%d)\n", c, mname, i); - else - f.write_h("extern Fl_Menu_Item *%s;\n", c); - } - } - - if (callback()) { - if (!is_name(callback()) && class_name(1)) { - const char* cn = callback_name(f); - const char* ut = user_data_type() ? user_data_type() : "void*"; - f.write_public(0); - f.write_h("%sinline void %s_i(Fl_Menu_*, %s);\n", f.indent(1), cn, ut); - f.write_h("%sstatic void %s(Fl_Menu_*, %s);\n", f.indent(1), cn, ut); - } - } - - int menuItemInitialized = 0; - // if the name is an array variable, assign the value here - if (name() && strchr(name(), '[')) { - f.write_c("%s%s = &%s[%d];\n", f.indent_plus(1), name(), mname, i); - } - if (image) { - start_menu_initialiser(f, menuItemInitialized, mname, i); - if (label() && label()[0]) { - f.write_c("%sFl_Multi_Label *ml = new Fl_Multi_Label;\n", f.indent()); - f.write_c("%sml->labela = (char*)", f.indent()); - image->write_inline(f); - f.write_c(";\n"); - if (g_project.i18n_type==FD_I18N_NONE) { - f.write_c("%sml->labelb = o->label();\n", f.indent()); - } else if (g_project.i18n_type==FD_I18N_GNU) { - f.write_c("%sml->labelb = %s(o->label());\n", - f.indent(), g_project.i18n_gnu_function.c_str()); - } else if (g_project.i18n_type==FD_I18N_POSIX) { - f.write_c("%sml->labelb = catgets(%s,%s,i+%d,o->label());\n", - f.indent(), - g_project.i18n_pos_file.empty() ? "_catalog" : g_project.i18n_pos_file.c_str(), - g_project.i18n_pos_set.c_str(), msgnum()); - } - f.write_c("%sml->typea = FL_IMAGE_LABEL;\n", f.indent()); - f.write_c("%sml->typeb = FL_NORMAL_LABEL;\n", f.indent()); - f.write_c("%sml->label(o);\n", f.indent()); - } else { - image->write_code(f, 0, "o"); - } - } - if (g_project.i18n_type && label() && label()[0]) { - Fl_Labeltype t = o->labeltype(); - if (image) { - // label was already copied a few lines up - } else if ( t==FL_NORMAL_LABEL || t==FL_SHADOW_LABEL - || t==FL_ENGRAVED_LABEL || t==FL_EMBOSSED_LABEL) { - start_menu_initialiser(f, menuItemInitialized, mname, i); - if (g_project.i18n_type==FD_I18N_GNU) { - f.write_c("%so->label(%s(o->label()));\n", - f.indent(), g_project.i18n_gnu_function.c_str()); - } else if (g_project.i18n_type==FD_I18N_POSIX) { - f.write_c("%so->label(catgets(%s,%s,i+%d,o->label()));\n", - f.indent(), - g_project.i18n_pos_file.empty() ? "_catalog" : g_project.i18n_pos_file.c_str(), - g_project.i18n_pos_set.c_str(), msgnum()); - } - } - } - for (int n=0; n < NUM_EXTRA_CODE; n++) { - if (extra_code(n) && !isdeclare(extra_code(n))) { - start_menu_initialiser(f, menuItemInitialized, mname, i); - f.write_c("%s%s\n", f.indent(), extra_code(n)); - } - } - if (menuItemInitialized) { - f.indentation--; - f.write_c("%s}\n",f.indent()); - } -} - -void Fl_Menu_Item_Type::write_code2(fld::io::Code_Writer&) {} - -//////////////////////////////////////////////////////////////// -// This is the base class for widgets that contain a menu (ie -// subclasses of Fl_Menu_. -// This is a parent widget and menu items can be added as -// children. An actual array of Fl_Menu_Items is kept parallel -// with the child objects and updated as they change. - -void Fl_Menu_Base_Type::build_menu() { - Fl_Menu_* w = (Fl_Menu_*)o; - // count how many Fl_Menu_Item structures needed: - int n = 0; - Fl_Type* q; - for (q = next; q && q->level > level; q = q->next) { - if (q->can_have_children()) n++; // space for null at end of submenu - n++; - } - if (!n) { - if (menusize) delete_menu((Fl_Menu_Item*)(w->menu())); - w->menu(0); - menusize = 0; - } else { - n++; // space for null at end of menu - if (menusizemenu())); - menusize = n+10; - w->menu(new Fl_Menu_Item[menusize]); - } else { - if (menusize) delete_dependents((Fl_Menu_Item*)(w->menu())); - } - // Menus are already built during the .fl file reading process, so if the - // end of a menu list is not read yet, the end markers (label==NULL) will - // not be set, and deleting dependents will randomly free memory. - // Clearing the array should avoid that. - memset( (void*)w->menu(), 0, menusize * sizeof(Fl_Menu_Item) ); - // fill them all in: - Fl_Menu_Item* m = (Fl_Menu_Item*)(w->menu()); - int lvl = level+1; - for (q = next; q && q->level > level; q = q->next) { - Fl_Menu_Item_Type* i = (Fl_Menu_Item_Type*)q; - if (i->o->image()) { - if (i->o->label() && i->o->label()[0]) { - Fl_Multi_Label *ml = new Fl_Multi_Label; - ml->labela = (char*)i->o->image(); - ml->labelb = i->o->label(); - ml->typea = FL_IMAGE_LABEL; - ml->typeb = FL_NORMAL_LABEL; - ml->label(m); - } else { - i->o->image()->label(m); - } - } else { - m->label(i->o->label() ? i->o->label() : "(nolabel)"); - m->labeltype(i->o->labeltype()); - } - m->shortcut(((Fl_Button*)(i->o))->shortcut()); - m->callback(0,(void*)i); - m->flags = i->flags() | i->o->type(); - m->labelfont(i->o->labelfont()); - m->labelsize(i->o->labelsize()); - m->labelcolor(i->o->labelcolor()); - if (q->can_have_children()) {lvl++; m->flags |= FL_SUBMENU;} - m++; - int l1 = - (q->next && q->next->is_a(ID_Menu_Item)) ? q->next->level : level; - while (lvl > l1) {m->label(0); m++; lvl--;} - lvl = l1; - } - } - o->redraw(); -} - -Fl_Type* Fl_Menu_Base_Type::click_test(int, int) { - if (selected) return 0; // let user move the widget - Fl_Menu_* w = (Fl_Menu_*)o; - if (!menusize) return 0; - const Fl_Menu_Item* save = w->mvalue(); - w->value((Fl_Menu_Item*)0); - Fl::pushed(w); - w->handle(FL_PUSH); - Fl::focus(NULL); - const Fl_Menu_Item* m = w->mvalue(); - if (m) { - // restore the settings of toggles & radio items: - if (m->flags & (FL_MENU_RADIO | FL_MENU_TOGGLE)) build_menu(); - return (Fl_Type*)(m->user_data()); - } - w->value(save); - return this; -} - -void Fl_Menu_Manager_Type::write_code2(fld::io::Code_Writer& f) { - if (next && next->is_a(ID_Menu_Item)) { - f.write_c("%s%s->menu(%s);\n", f.indent(), name() ? name() : "o", - f.unique_id(this, "menu", name(), label())); - } - Fl_Widget_Type::write_code2(f); -} - -void Fl_Menu_Base_Type::copy_properties() { - Fl_Widget_Type::copy_properties(); - Fl_Menu_ *s = (Fl_Menu_*)o, *d = (Fl_Menu_*)live_widget; - d->menu(s->menu()); - d->down_box(s->down_box()); - d->textcolor(s->textcolor()); - d->textfont(s->textfont()); - d->textsize(s->textsize()); -} - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Item button_type_menu[] = { - {"normal",0,0,(void*)0}, - {"popup1",0,0,(void*)Fl_Menu_Button::POPUP1}, - {"popup2",0,0,(void*)Fl_Menu_Button::POPUP2}, - {"popup3",0,0,(void*)Fl_Menu_Button::POPUP3}, - {"popup12",0,0,(void*)Fl_Menu_Button::POPUP12}, - {"popup23",0,0,(void*)Fl_Menu_Button::POPUP23}, - {"popup13",0,0,(void*)Fl_Menu_Button::POPUP13}, - {"popup123",0,0,(void*)Fl_Menu_Button::POPUP123}, - {0}}; - -Fl_Menu_Button_Type Fl_Menu_Button_type; - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Item dummymenu[] = {{"CHOICE"},{0}}; - -Fl_Choice_Type Fl_Choice_type; - -Fl_Input_Choice_Type Fl_Input_Choice_type; - -void Fl_Input_Choice_Type::copy_properties() { - Fl_Widget_Type::copy_properties(); - Fl_Input_Choice *s = (Fl_Input_Choice*)o, *d = (Fl_Input_Choice*)live_widget; - d->menu(s->menu()); - d->down_box(s->down_box()); - d->textcolor(s->textcolor()); - d->textfont(s->textfont()); - d->textsize(s->textsize()); -} - -Fl_Type* Fl_Input_Choice_Type::click_test(int, int) { - if (selected) return 0; // let user move the widget - Fl_Menu_* w = ((Fl_Input_Choice*)o)->menubutton(); - if (!menusize) return 0; - const Fl_Menu_Item* save = w->mvalue(); - w->value((Fl_Menu_Item*)0); - Fl::pushed(w); - w->handle(FL_PUSH); - Fl::focus(NULL); - const Fl_Menu_Item* m = w->mvalue(); - if (m) { - // restore the settings of toggles & radio items: - if (m->flags & (FL_MENU_RADIO | FL_MENU_TOGGLE)) build_menu(); - return (Fl_Type*)(m->user_data()); - } - w->value(save); - return this; -} - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Bar_Type Fl_Menu_Bar_type; - -Fl_Menu_Item menu_bar_type_menu[] = { - {"Fl_Menu_Bar",0,0,(void*)0}, - {"Fl_Sys_Menu_Bar",0,0,(void*)1}, - {0}}; - -Fl_Menu_Bar_Type::Fl_Menu_Bar_Type() -: _proxy_name(NULL) -{ -} - -Fl_Menu_Bar_Type::~Fl_Menu_Bar_Type() { - if (_proxy_name) - ::free(_proxy_name); -} - -/** - \brief Return true if this is an Fl_Sys_Menu_Bar. - This test fails if subclass() is the name of a class that the user may have - derived from Fl_Sys_Menu_Bar. - */ -bool Fl_Menu_Bar_Type::is_sys_menu_bar() { - if (o->type()==1) return true; - return ( subclass() && (strcmp(subclass(), "Fl_Sys_Menu_Bar")==0) ); -} - -const char *Fl_Menu_Bar_Type::sys_menubar_name() { - if (subclass()) - return subclass(); - else - return "Fl_Sys_Menu_Bar"; -} - -const char *Fl_Menu_Bar_Type::sys_menubar_proxy_name() { - if (!_proxy_name) - _proxy_name = (char*)::malloc(128); - ::snprintf(_proxy_name, 63, "%s_Proxy", sys_menubar_name()); - return _proxy_name; -} - - -void Fl_Menu_Bar_Type::write_static(fld::io::Code_Writer& f) { - super::write_static(f); - if (is_sys_menu_bar()) { - f.write_h_once("#include "); - if (is_in_class()) { - // Make room for a pointer to the enclosing class. - f.write_c_once( // must be less than 1024 bytes! - "\nclass %s: public %s {\n" - "public:\n" - " %s(int x, int y, int w, int h, const char *l=NULL)\n" - " : %s(x, y, w, h, l) { }\n" - " void *_parent_class;\n" - "};\n", - sys_menubar_proxy_name(), sys_menubar_name(), - sys_menubar_proxy_name(), sys_menubar_name() - ); - } - } -} - -void Fl_Menu_Bar_Type::write_code1(fld::io::Code_Writer& f) { - super::write_code1(f); - if (is_sys_menu_bar() && is_in_class()) { - f.write_c("%s((%s*)%s)->_parent_class = (void*)this;\n", - f.indent(), sys_menubar_proxy_name(), name() ? name() : "o"); - } -} - -//void Fl_Menu_Bar_Type::write_code2(fld::io::Code_Writer& f) { -// super::write_code2(f); -//} - -//////////////////////////////////////////////////////////////// -// Shortcut entry item in panel: -void shortcut_in_cb(Fl_Shortcut_Button* i, void* v) { - if (v == LOAD) { - if (current_widget->is_button()) - i->value( ((Fl_Button*)(current_widget->o))->shortcut() ); - else if (current_widget->is_a(ID_Input)) - i->value( ((Fl_Input_*)(current_widget->o))->shortcut() ); - else if (current_widget->is_a(ID_Value_Input)) - i->value( ((Fl_Value_Input*)(current_widget->o))->shortcut() ); - else if (current_widget->is_a(ID_Text_Display)) - i->value( ((Fl_Text_Display*)(current_widget->o))->shortcut() ); - else { - i->hide(); - i->parent()->hide(); - return; - } - //i->default_value( i->value() ); // enable the "undo" capability of the shortcut button - i->show(); - i->parent()->show(); - i->redraw(); - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_button()) { - Fl_Button* b = (Fl_Button*)(((Fl_Widget_Type*)o)->o); - if (b->shortcut() != (int)i->value()) mod = 1; - b->shortcut(i->value()); - if (o->is_a(ID_Menu_Item)) ((Fl_Widget_Type*)o)->redraw(); - } else if (o->selected && o->is_a(ID_Input)) { - Fl_Input_* b = (Fl_Input_*)(((Fl_Widget_Type*)o)->o); - if (b->shortcut() != (int)i->value()) mod = 1; - b->shortcut(i->value()); - } else if (o->selected && o->is_a(ID_Value_Input)) { - Fl_Value_Input* b = (Fl_Value_Input*)(((Fl_Widget_Type*)o)->o); - if (b->shortcut() != (int)i->value()) mod = 1; - b->shortcut(i->value()); - } else if (o->selected && o->is_a(ID_Text_Display)) { - Fl_Text_Display* b = (Fl_Text_Display*)(((Fl_Widget_Type*)o)->o); - if (b->shortcut() != (int)i->value()) mod = 1; - b->shortcut(i->value()); - } - if (mod) set_modflag(1); - } -} diff --git a/fluid/nodes/Fl_Menu_Type.h b/fluid/nodes/Fl_Menu_Type.h deleted file mode 100644 index 136a1c289..000000000 --- a/fluid/nodes/Fl_Menu_Type.h +++ /dev/null @@ -1,287 +0,0 @@ -// -// Menu type header file for the Fast Light Tool Kit (FLTK). -// -// Type for creating all subclasses of Fl_Widget -// This should have the widget pointer in it, but it is still in the -// Fl_Type base class. -// -// Copyright 1998-2023 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_FL_MENU_TYPE_H -#define _FLUID_FL_MENU_TYPE_H - -#include "nodes/Fl_Button_Type.h" - -#include "app/Fd_Snap_Action.h" - -#include -#include -#include -#include -#include -#include - -extern Fl_Menu_Item dummymenu[]; -extern Fl_Menu_Item button_type_menu[]; -extern Fl_Menu_Item menu_item_type_menu[]; -extern Fl_Menu_Item menu_bar_type_menu[]; - -/** - \brief Manage all types on menu items. - Deriving Fl_Menu_Item_Type from Fl_Button_Type is intentional. For the purpose - of editing, a Menu Item is implemented with `o` pointing to an Fl_Button for - holding all properties. - */ -class Fl_Menu_Item_Type : public Fl_Button_Type -{ - typedef Fl_Button_Type super; -public: - Fl_Menu_Item* subtypes() FL_OVERRIDE {return menu_item_type_menu;} - const char* type_name() FL_OVERRIDE {return "MenuItem";} - const char* alt_type_name() FL_OVERRIDE {return "fltk::Item";} - Fl_Type* make(Strategy strategy) FL_OVERRIDE; - Fl_Type* make(int flags, Strategy strategy); - int is_button() const FL_OVERRIDE {return 1;} // this gets shortcut to work - Fl_Widget* widget(int,int,int,int) FL_OVERRIDE {return 0;} - Fl_Widget_Type* _make() FL_OVERRIDE {return 0;} - virtual const char* menu_name(fld::io::Code_Writer& f, int& i); - int flags(); - void write_static(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_item(fld::io::Code_Writer& f); - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - int is_true_widget() const FL_OVERRIDE { return 0; } - ID id() const FL_OVERRIDE { return ID_Menu_Item; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Menu_Item) ? true : super::is_a(inID); } -}; - -/** - \brief Manage Radio style Menu Items. - */ -class Fl_Radio_Menu_Item_Type : public Fl_Menu_Item_Type -{ - typedef Fl_Menu_Item_Type super; -public: - const char* type_name() FL_OVERRIDE {return "RadioMenuItem";} - Fl_Type* make(Strategy strategy) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Radio_Menu_Item; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Radio_Menu_Item) ? true : super::is_a(inID); } -}; - -/** - \brief Manage Checkbox style Menu Items. - */ -class Fl_Checkbox_Menu_Item_Type : public Fl_Menu_Item_Type -{ - typedef Fl_Menu_Item_Type super; -public: - const char* type_name() FL_OVERRIDE {return "CheckMenuItem";} - Fl_Type* make(Strategy strategy) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Checkbox_Menu_Item; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Checkbox_Menu_Item) ? true : super::is_a(inID); } -}; - -/** - \brief Manage Submenu style Menu Items. - Submenu Items are simply buttons just like all other menu items, but they - can also hold a pointer to a list of submenus, or have a flag set that - allows submenus to follow in the current array. As buttons, they can - be clicked by the user, and they will call their callback, if one is set. - */ -class Fl_Submenu_Type : public Fl_Menu_Item_Type -{ - typedef Fl_Menu_Item_Type super; -public: - Fl_Menu_Item* subtypes() FL_OVERRIDE {return 0;} - const char* type_name() FL_OVERRIDE {return "Submenu";} - const char* alt_type_name() FL_OVERRIDE {return "fltk::ItemGroup";} - int can_have_children() const FL_OVERRIDE {return 1;} - int is_button() const FL_OVERRIDE {return 0;} // disable shortcut - Fl_Type* make(Strategy strategy) FL_OVERRIDE; - // changes to submenu must propagate up so build_menu is called - // on the parent Fl_Menu_Type: - void add_child(Fl_Type*a, Fl_Type*b) FL_OVERRIDE {parent->add_child(a,b);} - void move_child(Fl_Type*a, Fl_Type*b) FL_OVERRIDE {parent->move_child(a,b);} - void remove_child(Fl_Type*a) FL_OVERRIDE {parent->remove_child(a);} - ID id() const FL_OVERRIDE { return ID_Submenu; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Submenu) ? true : super::is_a(inID); } -}; - -// ----------------------------------------------------------------------------- - -/** - \brief Base class for all widgets that can have a pulldown menu attached. - Widgets with this type can be derived from Fl_Menu_ or from - Fl_Group (Fl_Input_Choice). - */ -class Fl_Menu_Manager_Type : public Fl_Widget_Type -{ - typedef Fl_Widget_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE { - h = layout->textsize_not_null() + 8; - w = layout->textsize_not_null() * 6 + 8; - Fd_Snap_Action::better_size(w, h); - } - int can_have_children() const FL_OVERRIDE {return 1;} - int menusize; - virtual void build_menu() = 0; - Fl_Menu_Manager_Type() : Fl_Widget_Type() {menusize = 0;} - void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE { build_menu(); } - void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE { build_menu(); } - void remove_child(Fl_Type*) FL_OVERRIDE { build_menu();} - Fl_Type* click_test(int x, int y) FL_OVERRIDE = 0; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void copy_properties() FL_OVERRIDE = 0; - ID id() const FL_OVERRIDE { return ID_Menu_Manager_; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Menu_Manager_) ? true : super::is_a(inID); } -}; - -/** - \brief Manage the composite widget Input Choice. - \note Input Choice is a composite window, so `o` will be pointing to a widget - derived from Fl_Group. All menu related methods from Fl_Menu_Trait_Type must - be virtual and must be reimplemented here (click_test, build_menu, textstuff). - */ -class Fl_Input_Choice_Type : public Fl_Menu_Manager_Type -{ - typedef Fl_Menu_Manager_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Input_Choice *myo = (Fl_Input_Choice*)(w==4 ? ((Fl_Widget_Type*)this->factory)->o : this->o); - switch (w) { - case 4: - case 0: f = (Fl_Font)myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; - case 1: myo->textfont(f); break; - case 2: myo->textsize(s); break; - case 3: myo->textcolor(c); break; - } - return 1; - } -public: - ~Fl_Input_Choice_Type() { - if (menusize) delete[] (Fl_Menu_Item*)(((Fl_Input_Choice*)o)->menu()); - } - const char *type_name() FL_OVERRIDE {return "Fl_Input_Choice";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::ComboBox";} - Fl_Type* click_test(int,int) FL_OVERRIDE; - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - Fl_Input_Choice *myo = new Fl_Input_Choice(X,Y,W,H,"input choice:"); - myo->menu(dummymenu); - myo->value("input"); - return myo; - } - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Input_Choice_Type();} - void build_menu() FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Input_Choice; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Input_Choice) ? true : super::is_a(inID); } - void copy_properties() FL_OVERRIDE; -}; - -/** - \brief Base class to handle widgets that are derived from Fl_Menu_. - */ -class Fl_Menu_Base_Type : public Fl_Menu_Manager_Type -{ - typedef Fl_Menu_Manager_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Menu_ *myo = (Fl_Menu_*)(w==4 ? ((Fl_Widget_Type*)this->factory)->o : this->o); - switch (w) { - case 4: - case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; - case 1: myo->textfont(f); break; - case 2: myo->textsize(s); break; - case 3: myo->textcolor(c); break; - } - return 1; - } -public: - int can_have_children() const FL_OVERRIDE {return 1;} - void build_menu() FL_OVERRIDE; - ~Fl_Menu_Base_Type() { - if (menusize) delete[] (Fl_Menu_Item*)(((Fl_Menu_*)o)->menu()); - } - Fl_Type* click_test(int x, int y) FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Menu_; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Menu_) ? true : super::is_a(inID); } -}; - -extern Fl_Menu_Item button_type_menu[]; - -/** - \brief Make Menu Button widgets. - */ -class Fl_Menu_Button_Type : public Fl_Menu_Base_Type -{ - typedef Fl_Menu_Base_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE {return button_type_menu;} -public: - const char *type_name() FL_OVERRIDE {return "Fl_Menu_Button";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::MenuButton";} - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - return new Fl_Menu_Button(X,Y,W,H,"menu");} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Menu_Button_Type();} - ID id() const FL_OVERRIDE { return ID_Menu_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Menu_Button) ? true : super::is_a(inID); } -}; - - -/** - \brief Manage Choice type menu widgets. - */ -class Fl_Choice_Type : public Fl_Menu_Base_Type -{ - typedef Fl_Menu_Base_Type super; -public: - const char *type_name() FL_OVERRIDE {return "Fl_Choice";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::Choice";} - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - Fl_Choice *myo = new Fl_Choice(X,Y,W,H,"choice:"); - myo->menu(dummymenu); - return myo; - } - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Choice_Type();} - ID id() const FL_OVERRIDE { return ID_Choice; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Choice) ? true : super::is_a(inID); } -}; - - -/** - \brief Manage Menubar widgets. - */ -class Fl_Menu_Bar_Type : public Fl_Menu_Base_Type -{ - typedef Fl_Menu_Base_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE {return menu_bar_type_menu;} -public: - Fl_Menu_Bar_Type(); - ~Fl_Menu_Bar_Type() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "Fl_Menu_Bar";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::MenuBar";} - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE {return new Fl_Menu_Bar(X,Y,W,H);} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Menu_Bar_Type();} - void write_static(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; -// void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Menu_Bar; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Menu_Bar) ? true : super::is_a(inID); } - bool is_sys_menu_bar(); - const char *sys_menubar_name(); - const char *sys_menubar_proxy_name(); -protected: - char *_proxy_name; -}; - - -#endif // _FLUID_FL_MENU_TYPE_H diff --git a/fluid/nodes/Fl_Type.cxx b/fluid/nodes/Fl_Type.cxx deleted file mode 100644 index 8f078e06e..000000000 --- a/fluid/nodes/Fl_Type.cxx +++ /dev/null @@ -1,1338 +0,0 @@ -// -// Widget type code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 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 -// - -/// \defgroup fl_type Basic Node for all Widgets and Functions -/// \{ - -/** \class Fl_Type - Each object described by Fluid is one of these objects. They - are all stored in a double-linked list. - - The "type" of the object is covered by the virtual functions. - There will probably be a lot of these virtual functions. - - The type browser is also a list of these objects, but they - are "factory" instances, not "real" ones. These objects exist - only so the "make" method can be called on them. They are - not in the linked list and are not written to files or - copied or otherwise examined. - - The Fl_Type inheritance is currently: - --+-- Fl_Type - +-- Fl_Function_Type - +-- Fl_Code_Type - +-- Fl_CodeBlock_Type - +-+ Fl_Decl_Type - | +-- Fl_Data - +-- Fl_DeclBlock_Type - +-- Fl_Comment_Type - +-- Fl_Class_Type - +-+ Fl_Widget_Type, 'o' points to a class derived from Fl_Widget - +-+ Fl_Browser_Base_Type, 'o' is Fl_Browser - | +-+ Fl_Browser - | | +-- Fl_File_Browser - | +-- Fl_Check_Browser - +-- Fl_Tree_Type - +-- Fl_Help_View_Type - +-+ Fl_Valuator_Type, 'o' is Fl_Valuator_ - | +-- Fl_Counter_Type - | +-- Fl_Adjuster_Type - | +-- Fl_Dial_Type - | +-- Fl_Roller_Type - | +-- Fl_Slider_Type - | +-- Fl_Value_Input_Type - | +-- Fl_Value_Output_Type - +-+ Fl_Input_Type - | +-- Fl_Output_Type - +-+ Fl_Text_Display_Type - | +-- Fl_Text_Editor_Type - +-- Fl_Terminal_Type - +-- Fl_Box_Type - +-- Fl_Clock_Type - +-- Fl_Progress_Type - +-- Fl_Spinner_Type - +-+ Fl_Group_Type - | +-- Fl_Pack_Type - | +-- Fl_Flex_Type - | +-- Fl_Grid_Type - | +-- Fl_Table_Type - | +-- Fl_Tabs_Type - | +-- Fl_Scroll_Type - | +-- Fl_Tile_Type - | +-- Fl_Wizard_Type - | +-+ Fl_Window_Type - | +-- Fl_Widget_Class_Type - +-+ Fl_Menu_Manager_Type, 'o' is based on Fl_Widget - | +-+ Fl_Menu_Base_Type, 'o' is based on Fl_Menu_ - | | +-- Fl_Menu_Button_Type - | | +-- Fl_Choice_Type - | | +-- Fl_Menu_Bar_Type - | +-- Fl_Input_Choice_Type, 'o' is based on Fl_Input_Choice which is Fl_Group - +-+ Fl_Button_Type - +-- Fl_Return_Button_Type - +-- Fl_Repeat_Button_Type - +-- Fl_Light_Button_Type - +-- Fl_Check_Button_Type - +-- Fl_Round_Button_Type - +-+ Fl_Menu_Item_Type, 'o' is derived from Fl_Button in FLUID - +-- Fl_Radio_Menu_Item_Type - +-- Fl_Checkbox_Menu_Item_Type - +-- Fl_Submenu_Item_Type - -*/ - -#include "nodes/Fl_Type.h" - -#include "app/fluid.h" -#include "app/project.h" -#include "app/Fd_Snap_Action.h" -#include "app/shell_command.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "nodes/Fl_Function_Type.h" -#include "nodes/Fl_Widget_Type.h" -#include "nodes/Fl_Window_Type.h" -#include "nodes/Fl_Group_Type.h" -#include "rsrcs/pixmaps.h" -#include "widgets/Node_Browser.h" - -#include -#include -#include -#include "../src/flstring.h" - -#include -#include - -// ---- global variables - -Fl_Type *Fl_Type::first = NULL; -Fl_Type *Fl_Type::last = NULL; -Fl_Type *Fl_Type::current = NULL; -Fl_Type *Fl_Type::current_dnd = NULL; -int Fl_Type::allow_layout = 0; - -Fl_Type *in_this_only; // set if menu popped-up in window - - -// ---- various functions - -#if 0 -#ifndef NDEBUG -/** - Print the current project tree to stderr. - */ -void print_project_tree() { - fprintf(stderr, "---- %s --->\n", g_project.projectfile_name().c_str()); - for (Fl_Type *t = Fl_Type::first; t; t = t->next) { - for (int i = t->level; i > 0; i--) - fprintf(stderr, ". "); - fprintf(stderr, "%s\n", subclassname(t)); - } -} -#endif - -#ifndef NDEBUG -/** - Check the validity of the project tree. - - Write problems with the project tree to stderr. - - \return true if the project tree is valid - */ -bool validate_project_tree() { - // Validate `first` and `last` - if (Fl_Type::first == NULL) { - if (Fl_Type::last == NULL) { - return true; - } else { - fprintf(stderr, "ERROR: `first` is NULL, but `last` is not!\n"); - return false; - } - } - if (Fl_Type::last == NULL) { - fprintf(stderr, "ERROR: `last` is NULL, but `first` is not!\n"); - return false; - } - // Validate the branch linkage, parent links, etc. - return validate_branch(Fl_Type::first); -} -#endif - -#ifndef NDEBUG -/** - Check the validity of a Type branch that is not connected to the project. - - Write problems with the branch to stderr. - - \param[in] root the first node in a branch - \return true if the branch is correctly separated and valid - */ -bool validate_independent_branch(class Fl_Type *root) { - // Make sure that `first` and `last` do not point at any node in this branch - if (Fl_Type::first) { - for (Fl_Type *t = root; t; t = t->next) { - if (Fl_Type::first == t) { - fprintf(stderr, "ERROR: Branch is not independent, `first` is pointing to branch member!\n"); - return false; - } - } - } - if (Fl_Type::last) { - for (Fl_Type *t = root; t; t = t->next) { - if (Fl_Type::last == t) { - fprintf(stderr, "ERROR: Branch is not independent, `last` is pointing to branch member!\n"); - return false; - } - } - } - // Validate the branch linkage, parent links, etc. - return validate_branch(root); -} -#endif - -#ifndef NDEBUG -/** - Check the validity of a Type branch. - - Write problems with the branch to stderr. - - \param[in] root the first node in a branch - \return true if the branch is valid - */ -bool validate_branch(class Fl_Type *root) { - // Only check real branches - if (!root) { - fprintf(stderr, "WARNING: Branch is empty!\n"); - return false; - } - // Check relation between this and next node - for (Fl_Type *t = root; t; t = t->next) { - if (t->level < root->level) { - fprintf(stderr, "ERROR: Node in tree is above root level!\n"); - return false; - } - if (t->next) { - // Make sure that all `next` types have the `prev` member link back - if (t->next->prev != t) { - fprintf(stderr, "ERROR: Doubly linked list broken!\n"); - return false; - } - if (t->next->level > t->level) { - // Validate `level` changes - if (t->next->level - t->level > 1) { - fprintf(stderr, "ERROR: Child level increment greater than one!\n"); - return false; - } - // Ensure that this node can actually have children - if (!t->can_have_children()) { - fprintf(stderr, "ERROR: This parent must not have children!\n"); - return false; - } - } - } - // Validate the `parent` entry - for (Fl_Type *p = t->prev; ; p = p->prev) { - if (p == NULL) { - if (t->parent != NULL) { - fprintf(stderr, "ERROR: `parent` pointer should be NULL!\n"); - return false; - } - break; - } - if (p->level < t->level) { - if (t->parent != p) { - fprintf(stderr, "ERROR: `parent` points to wrong parent!\n"); - return false; - } - break; - } - } - } - return true; -} -#endif -#endif - -void select_all_cb(Fl_Widget *,void *) { - Fl_Type *p = Fl_Type::current ? Fl_Type::current->parent : 0; - if (in_this_only) { - Fl_Type *t = p; - for (; t && t != in_this_only; t = t->parent) {/*empty*/} - if (t != in_this_only) p = in_this_only; - } - for (;;) { - if (p) { - int foundany = 0; - for (Fl_Type *t = p->next; t && t->level>p->level; t = t->next) { - if (!t->new_selected) {widget_browser->select(t,1,0); foundany = 1;} - } - if (foundany) break; - p = p->parent; - } else { - for (Fl_Type *t = Fl_Type::first; t; t = t->next) - widget_browser->select(t,1,0); - break; - } - } - selection_changed(p); -} - -void select_none_cb(Fl_Widget *,void *) { - Fl_Type *p = Fl_Type::current ? Fl_Type::current->parent : 0; - if (in_this_only) { - Fl_Type *t = p; - for (; t && t != in_this_only; t = t->parent) {/*empty*/} - if (t != in_this_only) p = in_this_only; - } - for (;;) { - if (p) { - int foundany = 0; - for (Fl_Type *t = p->next; t && t->level>p->level; t = t->next) { - if (t->new_selected) {widget_browser->select(t,0,0); foundany = 1;} - } - if (foundany) break; - p = p->parent; - } else { - for (Fl_Type *t = Fl_Type::first; t; t = t->next) - widget_browser->select(t,0,0); - break; - } - } - selection_changed(p); -} - -/** - Callback to move all selected items before their previous unselected sibling. - */ -void earlier_cb(Fl_Widget*,void*) { - Fl_Type *f; - int mod = 0; - for (f = Fl_Type::first; f; ) { - Fl_Type* nxt = f->next; - if (f->selected) { - Fl_Type* g; - for (g = f->prev; g && g->level > f->level; g = g->prev) {/*empty*/} - if (g && g->level == f->level && !g->selected) { - if (!mod) undo_checkpoint(); - f->move_before(g); - if (f->parent) f->parent->layout_widget(); - mod = 1; - } - } - f = nxt; - } - if (mod) set_modflag(1); - widget_browser->display(Fl_Type::current); - widget_browser->rebuild(); -} - -/** - Callback to move all selected items after their next unselected sibling. - */ -void later_cb(Fl_Widget*,void*) { - Fl_Type *f; - int mod = 0; - for (f = Fl_Type::last; f; ) { - Fl_Type* prv = f->prev; - if (f->selected) { - Fl_Type* g; - for (g = f->next; g && g->level > f->level; g = g->next) {/*empty*/} - if (g && g->level == f->level && !g->selected) { - if (!mod) undo_checkpoint(); - g->move_before(f); - if (f->parent) f->parent->layout_widget(); - mod = 1; - } - } - f = prv; - } - if (mod) set_modflag(1); - widget_browser->display(Fl_Type::current); - widget_browser->rebuild(); -} - -/** \brief Delete all children of a Type. - */ -static void delete_children(Fl_Type *p) { - Fl_Type *f; - // find all types following p that are higher in level, effectively finding - // the last child of the last child - for (f = p; f && f->next && f->next->level > p->level; f = f->next) {/*empty*/} - // now loop back up to p, deleting all children on the way - for (; f != p; ) { - Fl_Type *g = f->prev; - delete f; - f = g; - } -} - -/** Delete all nodes in the Types tree and reset project settings, or delete selected nodes. - Also calls the browser to refresh. - \note Please refactor this into two separate methods of Fluid_Project. - \param[in] selected_only if set, delete only the selected widgets and - don't reset the project. - */ -void delete_all(int selected_only) { - if (widget_browser) { - if (selected_only) - widget_browser->save_scroll_position(); - widget_browser->new_list(); - } - for (Fl_Type *f = Fl_Type::first; f;) { - if (f->selected || !selected_only) { - delete_children(f); - Fl_Type *g = f->next; - delete f; - f = g; - } else { - f = f->next; - } - } - if(!selected_only) { - // reset the setting for the external shell command - if (g_shell_config) { - g_shell_config->clear(FD_STORE_PROJECT); - g_shell_config->rebuild_shell_menu(); - g_shell_config->update_settings_dialog(); - } - if (widget_browser) { - widget_browser->hposition(0); - widget_browser->vposition(0); - } - g_layout_list.remove_all(FD_STORE_PROJECT); - g_layout_list.current_suite(0); - g_layout_list.current_preset(0); - g_layout_list.update_dialogs(); - } - selection_changed(0); - if (widget_browser) { - if (selected_only) - widget_browser->restore_scroll_position(); - widget_browser->rebuild(); - } -} - -/** Update a string. - Replace a string pointer with new value, strips leading/trailing blanks. - As a side effect, this call also sets the mod flags. - \param[in] n new string, can be NULL - \param[out] p update this pointer, possibly reallocate memory - \param[in] nostrip if set, do not strip leading and trailing spaces and tabs - \return 1 if the string in p changed - */ -int storestring(const char *n, const char * & p, int nostrip) { - if (n == p) return 0; - undo_checkpoint(); - int length = 0; - if (n) { // see if blank, strip leading & trailing blanks - if (!nostrip) while (isspace((int)(unsigned char)*n)) n++; - const char *e = n + strlen(n); - if (!nostrip) while (e > n && isspace((int)(unsigned char)*(e-1))) e--; - length = int(e-n); - if (!length) n = 0; - } - if (n == p) return 0; - if (n && p && !strncmp(n,p,length) && !p[length]) return 0; - if (p) free((void *)p); - if (!n || !*n) { - p = 0; - } else { - char *q = (char *)malloc(length+1); - strlcpy(q,n,length+1); - p = q; - } - set_modflag(1); - return 1; -} - -/** Update the `visible` flag for `p` and all its descendants. - \param[in] p start here and update all descendants - */ -void update_visibility_flag(Fl_Type *p) { - Fl_Type *t = p; - for (;;) { - if (t->parent) t->visible = t->parent->visible && !t->parent->folded_; - else t->visible = 1; - t = t->next; - if (!t || t->level <= p->level) break; - } -} - -// ---- implementation of Fl_Type - -/** \var Fl_Type *Fl_Type::parent - Link to the parent node in the tree structure. - Used for simulating a tree structure via a doubly linked list. - */ -/** \var Fl_Type *Fl_Type::level - Zero based depth of the node within the tree structure. - Level is used to emulate a tree structure: the first node with a lower - level in the prev list would be the parent of this node. If the next member - has a higher level value, it is this nodes first child. At the same level, - it would be the first sibling. - */ -/** \var Fl_Type *Fl_Type::next - Points to the next node in the doubly linked list. - If this is NULL, we are at the end of the list. - Used for simulating a tree structure via a doubly linked list. - */ -/** \var Fl_Type *Fl_Type::prev - Link to the next node in the tree structure. - If this is NULL, we are at the beginning of the list. - Used for simulating a tree structure via a doubly linked list. - */ - -/** - Constructor and base for any node in the widget tree. - */ -Fl_Type::Fl_Type() : - name_(NULL), - label_(NULL), - callback_(NULL), - user_data_(NULL), - user_data_type_(NULL), - comment_(NULL), - uid_(0), - parent(NULL), - new_selected(0), - selected(0), - folded_(0), - visible(0), - level(0), - next(NULL), prev(NULL), - factory(NULL), - code_static_start(-1), code_static_end(-1), - code1_start(-1), code1_end(-1), - code2_start(-1), code2_end(-1), - header1_start(-1), header1_end(-1), - header2_start(-1), header2_end(-1), - header_static_start(-1), header_static_end(-1), - proj1_start(-1), proj1_end(-1), - proj2_start(-1), proj2_end(-1) -{ -} - - -/** - Destructor for any node in the tree. - - The destructor removes itself from the doubly linked list. This is dangerous, - because the node does not know if it is part of the widget tree, or if it is - in a separate tree. We try to take care of that as well as possible. - */ -Fl_Type::~Fl_Type() { - // warning: destructor only works for widgets that have been add()ed. - if (prev) prev->next = next; // else first = next; // don't do that! The Type may not be part of the main list - if (next) next->prev = prev; // else last = prev; - if (Fl_Type::last == this) Fl_Type::last = prev; - if (Fl_Type::first == this) Fl_Type::first = next; - if (current == this) current = NULL; - if (parent) parent->remove_child(this); - if (name_) free((void*)name_); - if (label_) free((void*)label_); - if (callback_) free((void*)callback_); - if (user_data_) free((void*)user_data_); - if (user_data_type_) free((void*)user_data_type_); - if (comment_) free((void*)comment_); -} - -// Return the previous sibling in the tree structure or NULL. -Fl_Type *Fl_Type::prev_sibling() { - Fl_Type *n; - for (n = prev; n && n->level > level; n = n->prev) ; - if (n && (n->level == level)) - return n; - return 0; -} - -// Return the next sibling in the tree structure or NULL. -Fl_Type *Fl_Type::next_sibling() { - Fl_Type *n; - for (n = next; n && n->level > level; n = n->next) ; - if (n && (n->level == level)) - return n; - return 0; -} - -// Return the first child or NULL -Fl_Type *Fl_Type::first_child() { - Fl_Type *n = next; - if (n->level > level) - return n; - return NULL; -} - -// Generate a descriptive text for this item, to put in browser & window titles -const char* Fl_Type::title() { - const char* c = name(); - if (c) - return c; - return type_name(); -} - -/** - Return the window that contains this widget. - \return NULL if this is not a widget. - */ -Fl_Window_Type *Fl_Type::window() { - if (!is_widget()) - return NULL; - for (Fl_Type *t = this; t; t=t->parent) - if (t->is_a(ID_Window)) - return (Fl_Window_Type*)t; - return NULL; -} - -/** - Return the group that contains this widget. - \return NULL if this is not a widget. - */ -Fl_Group_Type *Fl_Type::group() { - if (!is_widget()) - return NULL; - for (Fl_Type *t = this; t; t=t->parent) - if (t->is_a(ID_Group)) - return (Fl_Group_Type*)t; - return NULL; -} - -/** - Add this list/tree of widgets as a new last child of p. - - \c this must not be part of the widget browser. \c p however must be in the - widget_browser, so \c Fl_Type::first and \c Fl_Type::last are valid for \c p. - - This methods updates the widget_browser. - - \param[in] p insert \c this tree as a child of \c p - \param[in] strategy is Strategy::AS_LAST_CHILD or Strategy::AFTER_CURRENT - */ -void Fl_Type::add(Fl_Type *anchor, Strategy strategy) { -#if 0 -#ifndef NDEBUG - // print_project_tree(); - // fprintf(stderr, "Validating project\n"); - validate_project_tree(); - // fprintf(stderr, "Validating branch\n"); - validate_independent_branch(this); -#endif -#endif - - Fl_Type *target = NULL; // insert self before target node, if NULL, insert last - Fl_Type *target_parent = NULL; // this will be the new parent for branch - int target_level = 0; // adjust self to this new level - - // Find the node after our insertion position - switch (strategy.placement()) { - case Strategy::AS_FIRST_CHILD: - default: - if (anchor == NULL) { - target = Fl_Type::first; - } else { - target = anchor->next; - target_level = anchor->level + 1; - target_parent = anchor; - } - break; - case Strategy::AS_LAST_CHILD: - if (anchor == NULL) { - /* empty */ - } else { - for (target = anchor->next; target && target->level > anchor->level; target = target->next) {/*empty*/} - target_level = anchor->level + 1; - target_parent = anchor; - } - break; - case Strategy::AFTER_CURRENT: - if (anchor == NULL) { - target = Fl_Type::first; - } else { - for (target = anchor->next; target && target->level > anchor->level; target = target->next) {/*empty*/} - target_level = anchor->level; - target_parent = anchor->parent; - } - break; - } - - - // Find the last node of our tree - Fl_Type *end = this; - while (end->next) end = end->next; - - // Everything is prepared, now insert ourself in front of the target node - undo_checkpoint(); - - // Walk the tree to update parent pointers and levels - int source_level = level; - for (Fl_Type *t = this; t; t = t->next) { - t->level += (target_level-source_level); - if (t->level == target_level) - t->parent = target_parent; - } - - // Now link ourselves and our children before 'target', or last, if 'target' is NULL - if (target) { - prev = target->prev; - target->prev = end; - end->next = target; - } else { - prev = Fl_Type::last; - end->next = NULL; - Fl_Type::last = end; - } - if (prev) { - prev->next = this; - } else { - Fl_Type::first = this; - } - -#if 0 - { // make sure that we have no duplicate uid's - Fl_Type *tp = this; - do { - tp->set_uid(tp->uid_); - tp = tp->next; - } while (tp!=end && tp!=NULL); - } -#endif - - // Give the widgets in our tree a chance to update themselves - for (Fl_Type *t = this; t && t!=end->next; t = t->next) { - if (target_parent && (t->level == target_level)) - target_parent->add_child(t, 0); - update_visibility_flag(t); - } - - set_modflag(1); - widget_browser->redraw(); - -#if 0 -#ifndef NDEBUG - // fprintf(stderr, "Validating project after adding branch\n"); - validate_project_tree(); -#endif -#endif -} - -/** - Add `this` list/tree of widgets as a new sibling before `g`. - - `This` is not part of the widget browser. `g` must be in the - widget_browser, so `Fl_Type::first` and `Fl_Type::last` are valid for `g . - - This methods updates the widget_browser. - - \param[in] g pointer to a node within the tree - */ -void Fl_Type::insert(Fl_Type *g) { - // 'this' is not in the Node_Browser, so we must run the linked list to find the last entry - Fl_Type *end = this; - while (end->next) end = end->next; - // 'this' will get the same parent as 'g' - parent = g->parent; - // run the list again to set the future node levels - int newlevel = g->level; - visible = g->visible; - for (Fl_Type *t = this->next; t; t = t->next) t->level += newlevel-level; - level = newlevel; - // insert this in the list before g - prev = g->prev; - if (prev) prev->next = this; else first = this; - end->next = g; - g->prev = end; - update_visibility_flag(this); - { // make sure that we have no duplicate uid's - Fl_Type *tp = this; - do { - tp->set_uid(tp->uid_); - tp = tp->next; - } while (tp!=end && tp!=NULL); - } - // tell parent that it has a new child, so it can update itself - if (parent) parent->add_child(this, g); - widget_browser->redraw(); -} - -// Return message number for I18N... -int Fl_Type::msgnum() { - int count; - Fl_Type *p; - - for (count = 0, p = this; p;) { - if (p->label()) count ++; - if (p != this && p->is_widget() && ((Fl_Widget_Type *)p)->tooltip()) count ++; - - if (p->prev) p = p->prev; - else p = p->parent; - } - - return count; -} - -/** - Remove this node and all its children from the parent node. - - This does not delete anything. The resulting list//tree will no longer be in - the widget_browser, so \c Fl_Type::first and \c Fl_Type::last do not apply - to it. - - \return the node that follows this node after the operation; can be NULL - */ -Fl_Type *Fl_Type::remove() { - // find the last child of this node - Fl_Type *end = this; - for (;;) { - if (!end->next || end->next->level <= level) - break; - end = end->next; - } - // unlink this node from the previous one - if (prev) - prev->next = end->next; - else - first = end->next; - // unlink the last child from their next node - if (end->next) - end->next->prev = prev; - else - last = prev; - Fl_Type *r = end->next; - prev = end->next = 0; - // allow the parent to update changes in the UI - if (parent) parent->remove_child(this); - parent = 0; - // tell the widget_browser that we removed some nodes - widget_browser->redraw(); - selection_changed(0); - return r; -} - -void Fl_Type::name(const char *n) { - int nostrip = is_a(ID_Comment); - if (storestring(n,name_,nostrip)) { - if (visible) widget_browser->redraw(); - } -} - -void Fl_Type::label(const char *n) { - if (storestring(n,label_,1)) { - setlabel(label_); - if (visible && !name_) widget_browser->redraw(); - } -} - -void Fl_Type::callback(const char *n) { - storestring(n,callback_); -} - -void Fl_Type::user_data(const char *n) { - storestring(n,user_data_); -} - -void Fl_Type::user_data_type(const char *n) { - storestring(n,user_data_type_); -} - -void Fl_Type::comment(const char *n) { - if (storestring(n,comment_,1)) { - if (visible) widget_browser->redraw(); - } -} - -void Fl_Type::open() { - printf("Open of '%s' is not yet implemented\n",type_name()); -} - -// returns pointer to whatever is after f & children - -/** - Move this node (and its children) into list before g. - Both `this` and `g` must be in the widget browser. - The caller must make sure that the widget browser is rebuilt correctly. - \param[in] g move \c this tree before \c g - */ -void Fl_Type::move_before(Fl_Type* g) { - if (level != g->level) printf("move_before levels don't match! %d %d\n", - level, g->level); - // Find the last child in the list - Fl_Type *n; - for (n = next; n && n->level > level; n = n->next) ; - if (n == g) return; - // now link this tree before g - Fl_Type *l = n ? n->prev : Fl_Type::last; - prev->next = n; - if (n) n->prev = prev; else Fl_Type::last = prev; - prev = g->prev; - l->next = g; - if (prev) prev->next = this; else Fl_Type::first = this; - g->prev = l; - // tell parent that it has a new child, so it can update itself - if (parent && is_widget()) parent->move_child(this,g); -} - - -// write a widget and all its children: -void Fl_Type::write(fld::io::Project_Writer &f) { - if (f.write_codeview()) proj1_start = (int)ftell(f.file()) + 1; - if (f.write_codeview()) proj2_start = (int)ftell(f.file()) + 1; - f.write_indent(level); - f.write_word(type_name()); - - if (is_class()) { - const char * p = ((Fl_Class_Type*)this)->prefix(); - if (p && strlen(p)) - f.write_word(p); - } - - f.write_word(name()); - f.write_open(); - write_properties(f); - if (parent) parent->write_parent_properties(f, this, true); - f.write_close(level); - if (f.write_codeview()) proj1_end = (int)ftell(f.file()); - if (!can_have_children()) { - if (f.write_codeview()) proj2_end = (int)ftell(f.file()); - return; - } - // now do children: - f.write_open(); - Fl_Type *child; - for (child = next; child && child->level > level; child = child->next) - if (child->level == level+1) child->write(f); - if (f.write_codeview()) proj2_start = (int)ftell(f.file()) + 1; - f.write_close(level); - if (f.write_codeview()) proj2_end = (int)ftell(f.file()); -} - -void Fl_Type::write_properties(fld::io::Project_Writer &f) { - // repeat this for each attribute: - if (g_project.write_mergeback_data && uid_) { - f.write_word("uid"); - f.write_string("%04x", uid_); - } - if (label()) { - f.write_indent(level+1); - f.write_word("label"); - f.write_word(label()); - } - if (user_data()) { - f.write_indent(level+1); - f.write_word("user_data"); - f.write_word(user_data()); - } - if (user_data_type()) { - f.write_word("user_data_type"); - f.write_word(user_data_type()); - } - if (callback()) { - f.write_indent(level+1); - f.write_word("callback"); - f.write_word(callback()); - } - if (comment()) { - f.write_indent(level+1); - f.write_word("comment"); - f.write_word(comment()); - } - if (can_have_children() && !folded_) f.write_word("open"); - if (selected) f.write_word("selected"); -} - -void Fl_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"uid")) { - const char *hex = f.read_word(); - int x = 0; - if (hex) - x = sscanf(hex, "%04x", &x); - set_uid(x); - } else if (!strcmp(c,"label")) - label(f.read_word()); - else if (!strcmp(c,"user_data")) - user_data(f.read_word()); - else if (!strcmp(c,"user_data_type")) - user_data_type(f.read_word()); - else if (!strcmp(c,"callback")) - callback(f.read_word()); - else if (!strcmp(c,"comment")) - comment(f.read_word()); - else if (!strcmp(c,"open")) - folded_ = 0; - else if (!strcmp(c,"selected")) - select(this,1); - else if (!strcmp(c,"parent_properties")) - if (parent) { - const char *cc = f.read_word(1); - if (strcmp(cc, "{")==0) { - for (;;) { - cc = f.read_word(); - if (!cc || cc[0]==0 || strcmp(cc, "}")==0) break; - parent->read_parent_property(f, this, cc); - } - } else { - f.read_error("'parent_properties' must be followed by '{'"); - } - } else { - f.read_error("Types using 'parent_properties' must have a parent"); - f.read_word(); // skip the entire block (this should generate a warning) - } - else - f.read_error("Unknown property \"%s\"", c); -} - -/** Write parent properties into the child property list. - - Some widgets store information for every child they manage. For example, - Fl_Grid stores the row and column position of every child. This method stores - this information with the child, but it is read and written by the parent. - - Parent properties solve several issues. A child will keep parent properties - if copied from on grid into another. The parent does not have to keep lists - of properties that may diverge from the actual order or number of children. - And lastly, properties are read when they are actually needed and don't have - to be stored in some temporary array. - - Parent properties are written as their own block at the end of the child's - property list. The block starts with the `parent_properties` keyword, followed - by a list of property/value pairs. The order of properties is significant, - however individual properties can be left out. - - To avoid writing the `parent_properties` block unnecessarily, this method - should only generate it if `encapsulate` is set *and* the contained - properties are not at their default. - - Lastly, this method should call the super class to give it a chance to append - its own properties. - - \see Fl_Grid_Type::write_parent_properties(fld::io::Project_Writer &f, Fl_Type *child, bool encapsulate) - - \param[in] f the project file writer - \param[in] child write properties for this child, make sure it has the correct type - \param[in] encapsulate write the `parent_properties {}` block if true before writing any properties - */ -void Fl_Type::write_parent_properties(fld::io::Project_Writer &f, Fl_Type *child, bool encapsulate) { - (void)f; (void)child; (void)encapsulate; - // nothing to do here - // put the following code into your implementation of write_parent_properties - // if there are actual non-default properties to write - // if (encapsulate) { - // f.write_indent(level+2); - // f.write_string("parent_properties {"); - // } - // now write your properties as name/value pairs - // f.write_indent(level+3); - // f.write_string("location {%d %d}", cell->row(), cell->col()); - // give the super class a chance to write its properties as well - // super::write_parent_properties(f, child, false); - // close the encapsulation - // if (encapsulate) { - // f.write_indent(level+2); - // f.write_string("}"); - // } -} - -/** Read one parent per-child property. - - A parent widget can store properties for every child that it manages. This - method reads back those properties. This function is virtual, so if a Type - does not support a property, it will propagate to its super class. - - \see Fl_Type::write_parent_properties(fld::io::Project_Writer &f, Fl_Type *child, bool encapsulate) - \see Fl_Grid_Type::read_parent_property(fld::io::Project_Reader &f, Fl_Type *child, const char *property) - - \param[in] f the project file writer - \param[in] child read properties for this child - \param[in] property the name of a property, or "}" when we reach the end of the list - */ -void Fl_Type::read_parent_property(fld::io::Project_Reader &f, Fl_Type *child, const char *property) { - (void)child; - f.read_error("Unknown parent property \"%s\"", property); -} - - -int Fl_Type::read_fdesign(const char*, const char*) {return 0;} - -/** - Write a comment into the header file. - \param[in] pre indent the comment by this string -*/ -void Fl_Type::write_comment_h(fld::io::Code_Writer& f, const char *pre) -{ - if (comment() && *comment()) { - f.write_h("%s/**\n", pre); - const char *s = comment(); - f.write_h("%s ", pre); - while(*s) { - if (*s=='\n') { - if (s[1]) { - f.write_h("\n%s ", pre); - } - } else { - f.write_h("%c", *s); // FIXME this is much too slow! - } - s++; - } - f.write_h("\n%s*/\n", pre); - } -} - -/** - Write a comment into the source file. -*/ -void Fl_Type::write_comment_c(fld::io::Code_Writer& f, const char *pre) -{ - if (comment() && *comment()) { - f.write_c("%s/**\n", pre); - const char *s = comment(); - if (*s && *s!='\n') - f.write_c("%s ", pre); - while(*s) { - if (*s=='\n') { - f.write_c("\n"); - if (s[1] && s[1]!='\n') { - f.write_c("%s ", pre); - } - } else { - f.write_c("%c", *s); // FIXME this is much too slow! - } - s++; - } - f.write_c("\n%s*/\n", pre); - } -} - -/** - Write a comment into the source file. -*/ -void Fl_Type::write_comment_inline_c(fld::io::Code_Writer& f, const char *pre) -{ - if (comment() && *comment()) { - const char *s = comment(); - if (strchr(s, '\n')==0L) { - // single line comment - if (pre) f.write_c("%s", pre); - f.write_c("// %s\n", s); - if (!pre) f.write_c("%s", f.indent_plus(1)); - } else { - f.write_c("%s/*\n", pre?pre:""); - if (*s && *s!='\n') { - if (pre) - f.write_c("%s ", pre); - else - f.write_c("%s ", f.indent_plus(1)); - } - while(*s) { - if (*s=='\n') { - f.write_c("\n"); - if (s[1] && s[1]!='\n') { - if (pre) - f.write_c("%s ", pre); - else - f.write_c("%s ", f.indent_plus(1)); - } - } else { - f.write_c("%c", *s); // FIXME this is much too slow! - } - s++; - } - if (pre) - f.write_c("\n%s */\n", pre); - else - f.write_c("\n%s */\n", f.indent_plus(1)); - if (!pre) - f.write_c("%s", f.indent_plus(1)); - } - } -} - -/** - Build widgets and dataset needed in live mode. - \return a widget pointer that the live mode initiator can 'show()' - \see leave_live_mode() -*/ -Fl_Widget *Fl_Type::enter_live_mode(int) { - return 0L; -} - -/** - Release all resources created when entering live mode. - \see enter_live_mode() -*/ -void Fl_Type::leave_live_mode() { -} - -/** - Copy all needed properties for this type into the live object. -*/ -void Fl_Type::copy_properties() { -} - -/** - Check whether callback \p cbname is declared anywhere else by the user. - - \b Warning: this just checks that the name is declared somewhere, - but it should probably also check that the name corresponds to a - plain function or a member function within the same class and that - the parameter types match. - */ -int Fl_Type::user_defined(const char* cbname) const { - for (Fl_Type* p = Fl_Type::first; p ; p = p->next) - if (p->is_a(ID_Function) && p->name() != 0) - if (strncmp(p->name(), cbname, strlen(cbname)) == 0) - if (p->name()[strlen(cbname)] == '(') - return 1; - return 0; -} - -const char *Fl_Type::callback_name(fld::io::Code_Writer& f) { - if (is_name(callback())) return callback(); - return f.unique_id(this, "cb", name(), label()); -} - -/** - \brief Return the class name if this type is inside a Class or Widget Class. - - This methods traverses up the hirarchy to find out if this Type is located - inside a Class or Widget Class. It then return the name of that class. If - need_nest is set, class_name searches all the way up the tree and concatenates - the names of classes within classes, separated by a "::". - - \param need_nest if clear, search up one level to the first enclosing class. - If set, recurse all the way up to the top node. - \return the name of the enclosing class, or names of the enclosing classes - in a static buffe (don't call free), or NULL if this Type is not inside a class - */ -const char* Fl_Type::class_name(const int need_nest) const { - Fl_Type* p = parent; - while (p) { - if (p->is_class()) { - // see if we are nested in another class, we must fully-qualify name: - // this is lame but works... - const char* q = 0; - if(need_nest) q=p->class_name(need_nest); - if (q) { - static char s[256]; - if (q != s) strlcpy(s, q, sizeof(s)); - strlcat(s, "::", sizeof(s)); - strlcat(s, p->name(), sizeof(s)); - return s; - } - return p->name(); - } - p = p->parent; - } - return 0; -} - -/** - Check if this is inside a Fl_Class_Type or Fl_Widget_Class_Type. - \return true if any of the parents is Fl_Class_Type or Fl_Widget_Class_Type - */ -bool Fl_Type::is_in_class() const { - Fl_Type* p = parent; - while (p) { - if (p->is_class()) return true; - p = p->parent; - } - return false; -} - -void Fl_Type::write_static(fld::io::Code_Writer&) { -} - -void Fl_Type::write_static_after(fld::io::Code_Writer&) { -} - -void Fl_Type::write_code1(fld::io::Code_Writer& f) { - f.write_h("// Header for %s\n", title()); - f.write_c("// Code for %s\n", title()); -} - -void Fl_Type::write_code2(fld::io::Code_Writer&) { -} - -/** Set a uid that is unique within the project. - - Try to set the given id as the unique id for this node. If the suggested id - is 0, or it is already taken inside this project, we try another random id - until we find one that is unique. - - \param[in] suggested_uid the preferred uid for this node - \return the actualt uid that was given to the node - */ -unsigned short Fl_Type::set_uid(unsigned short suggested_uid) { - if (suggested_uid==0) - suggested_uid = (unsigned short)rand(); - for (;;) { - Fl_Type *tp = Fl_Type::first; - for ( ; tp; tp = tp->next) - if (tp!=this && tp->uid_==suggested_uid) - break; - if (tp==NULL) - break; - suggested_uid = (unsigned short)rand(); - } - uid_ = suggested_uid; - return suggested_uid; -} - -/** Find a node by its unique id. - - Every node in a type tree has an id that is unique for the current project. - Walk the tree and return the node with this uid. - - \param[in] uid any number between 0 and 65535 - \return the node with this uid, or NULL if not found - */ -Fl_Type *Fl_Type::find_by_uid(unsigned short uid) { - for (Fl_Type *tp = Fl_Type::first; tp; tp = tp->next) { - if (tp->uid_ == uid) return tp; - } - return NULL; -} - -/** Find a type node by using the codeview text positions. - - \param[in] text_type 0=source file, 1=header, 2=.fl project file - \param[in] crsr cursor position in text - \return the node we found or NULL - */ -Fl_Type *Fl_Type::find_in_text(int text_type, int crsr) { - for (Fl_Type *node = first; node; node = node->next) { - switch (text_type) { - case 0: - if (crsr >= node->code1_start && crsr < node->code1_end) return node; - if (crsr >= node->code2_start && crsr < node->code2_end) return node; - if (crsr >= node->code_static_start && crsr < node->code_static_end) return node; - break; - case 1: - if (crsr >= node->header1_start && crsr < node->header1_end) return node; - if (crsr >= node->header2_start && crsr < node->header2_end) return node; - if (crsr >= node->header_static_start && crsr < node->header_static_end) return node; - break; - case 2: - if (crsr >= node->proj1_start && crsr < node->proj1_end) return node; - if (crsr >= node->proj2_start && crsr < node->proj2_end) return node; - break; - } - } - return 0; -} - -/// \} - diff --git a/fluid/nodes/Fl_Type.h b/fluid/nodes/Fl_Type.h deleted file mode 100644 index e7dde3b39..000000000 --- a/fluid/nodes/Fl_Type.h +++ /dev/null @@ -1,323 +0,0 @@ -// -// Widget type header file 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_FL_TYPE_H -#define _FLUID_FL_TYPE_H - -#include "io/Code_Writer.h" - -#include -#include - -class Fl_Type; -class Fl_Group_Type; -class Fl_Window_Type; - -namespace fld { -namespace io { - -class Project_Reader; -class Project_Writer; - -} // namespace io -} // namespace fld - -/** - Declare where a new type is placed and how to create it. - - Placement can be as the first or last child of the anchor, or right after the - anchor. In most cases, the anchor is the last selected type node. - - If the source is FROM_USER, widgets may be created with default titles and - labels. Type created FROM_FILE will start with no label, so the label is set - correctly later. - - \see Fl_Type *Fl_..._Type::make(Strategy strategy) calls `add()` - Add single Type: - Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy, bool and_open) - Fl_Type *add_new_widget_from_user(const char *inName, Strategy strategy, bool and_open) - Fl_Type *add_new_widget_from_file(const char *inName, Strategy strategy) - Add a hierarchy of Types - void Fl_Type::add(Fl_Type *p, Strategy strategy) - int read_file(const char *filename, int merge, Strategy strategy) - Fl_Type *fld::io::Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, char skip_options) - int fld::io::Project_Reader::read_project(const char *filename, int merge, Strategy strategy) - */ -typedef struct Strategy { - enum Flags { - AS_FIRST_CHILD = 0x0000, - AS_LAST_CHILD = 0x0001, - AFTER_CURRENT = 0x0002, - PLACEMENT_MASK = 0x000f, - FROM_USER = 0x0000, - FROM_FILE = 0x0010, - SOURCE_MASK = 0x00f0, - FROM_FILE_AS_FIRST_CHILD = 0x0010, - FROM_FILE_AS_LAST_CHILD = 0x0011, - FROM_FILE_AFTER_CURRENT = 0x0012, - }; - Flags flags; - Strategy(Flags f) { flags = f; } - void placement(Flags f) { flags = (Flags)((flags & ~PLACEMENT_MASK) | (f & PLACEMENT_MASK)); } - Flags placement() { return (Flags)(flags & PLACEMENT_MASK); } - void source(Flags f) { flags = (Flags)((flags & ~SOURCE_MASK) | (f & SOURCE_MASK)); } - Flags source() { return (Flags)(flags & SOURCE_MASK); } -} Strategy; - -enum ID { - // administrative - ID_Base_, ID_Widget_, ID_Menu_Manager_, ID_Menu_, ID_Browser_, ID_Valuator_, - // non-widget - ID_Function, ID_Code, ID_CodeBlock, - ID_Decl, ID_DeclBlock, ID_Class, - ID_Widget_Class, ID_Comment, ID_Data, - // groups - ID_Window, ID_Group, ID_Pack, - ID_Flex, ID_Tabs, ID_Scroll, - ID_Tile, ID_Wizard, ID_Grid, - // buttons - ID_Button, ID_Return_Button, ID_Light_Button, - ID_Check_Button, ID_Repeat_Button, ID_Round_Button, - // valuators - ID_Slider, ID_Scrollbar, ID_Value_Slider, - ID_Adjuster, ID_Counter, ID_Spinner, - ID_Dial, ID_Roller, ID_Value_Input, ID_Value_Output, - // text - ID_Input, ID_Output, ID_Text_Editor, - ID_Text_Display, ID_File_Input, ID_Terminal, - // menus - ID_Menu_Bar, ID_Menu_Button, ID_Choice, - ID_Input_Choice, ID_Submenu, ID_Menu_Item, - ID_Checkbox_Menu_Item, ID_Radio_Menu_Item, - // browsers - ID_Browser, ID_Check_Browser, ID_File_Browser, - ID_Tree, ID_Help_View, ID_Table, - // misc - ID_Box, ID_Clock, ID_Progress, - ID_Max_ -}; - -void update_visibility_flag(Fl_Type *p); -void delete_all(int selected_only=0); -int storestring(const char *n, const char * & p, int nostrip=0); - -void select_all_cb(Fl_Widget *,void *); -void select_none_cb(Fl_Widget *,void *); -void earlier_cb(Fl_Widget*,void*); -void later_cb(Fl_Widget*,void*); - -#ifndef NDEBUG -void print_project_tree(); -bool validate_project_tree(); -bool validate_independent_branch(class Fl_Type *root); -bool validate_branch(class Fl_Type *root); -#endif - -/** - \brief This is the base class for all elements in the project tree. - - All widgets and other types in the project are derived from Fl_Types. They - are organized in a doubly linked list. Every Type also has depth information - to create a pseudo tree structure. To make walking up the tree faster, Type - also holds a pointer to the `parent` Type. - - Types can be identified using the builtin ID system that works like RTTI. The - method `id()` returns the exact type, and the method `is_a(ID)` returns true - if this is the exact type or derived from the type, and a dynamic cast will - work reliably. - - \todo it would be nice if we can handle multiple independent trees. To do that - we must remove static members like `first` and `last`. - - \todo add virtual methods to handle events, draw widgets, and draw overlays. - It may also make sense to have a virtual method that returns a boolean if - a specific type can be added as a child. - - \todo it may make sense to have a readable iterator class instead of relying - on pointer manipulation. Or use std in future releases. - */ -class Fl_Type { - /** Copy the label text to Widgets and Windows, does nothing in Type. */ - virtual void setlabel(const char *) { } // virtual part of label(char*) - -protected: - - Fl_Type(); - - /** Name of a widget, or code some non-widget Types. */ - const char *name_; - /** Label text of a widget. */ - const char *label_; - /** If it is just a word, it's the name of the callback function. Otherwise - it is the full callback C++ code. Can be NULL. */ - const char *callback_; - /** Widget user data field as C++ text. */ - const char *user_data_; - /** Widget user data type as C++ text, usually `void*` or `long`. */ - const char *user_data_type_; - /** Optional comment for every node in the graph. Visible in browser and - panels, and will also be copied to the source code. */ - const char *comment_; - /** a unique ID within the project */ - unsigned short uid_; - -public: // things that should not be public: - - /** Quick link to the parent Type instead of walking up the linked list. */ - Fl_Type *parent; - /** This type is rendered "selected" in the tree browser. */ - char new_selected; // browser highlight - /** Backup storage for selection if an error occurred during some operation - (see `haderror`). It seems that this is often confused with new_selected - which seems to hold the true and visible selection state. */ - char selected; // copied here by selection_changed() - char folded_; // if set, children are not shown in browser - char visible; // true if all parents are open - int level; // number of parents over this - static Fl_Type *first, *last; - Fl_Type *next, *prev; - Fl_Type *prev_sibling(); - Fl_Type *next_sibling(); - Fl_Type *first_child(); - - Fl_Type *factory; - const char *callback_name(fld::io::Code_Writer& f); - - // text positions of this type in code, header, and project file (see codeview) - int code_static_start, code_static_end; - int code1_start, code1_end; - int code2_start, code2_end; - int header1_start, header1_end; - int header2_start, header2_end; - int header_static_start, header_static_end; - int proj1_start, proj1_end; - int proj2_start, proj2_end; - -protected: - int user_defined(const char* cbname) const; - -public: - - virtual ~Fl_Type(); - virtual Fl_Type *make(Strategy strategy) = 0; - - Fl_Window_Type *window(); - Fl_Group_Type *group(); - - void add(Fl_Type *parent, Strategy strategy); - void insert(Fl_Type *n); // insert into list before n - Fl_Type* remove(); // remove from list - void move_before(Fl_Type*); // move before a sibling - - virtual const char *title(); // string for browser - virtual const char *type_name() = 0; // type for code output - virtual const char *alt_type_name() { return type_name(); } // alternate type for FLTK2 code output - - const char *name() const {return name_;} - void name(const char *); - const char *label() const {return label_;} - void label(const char *); - const char *callback() const {return callback_;} - void callback(const char *); - const char *user_data() const {return user_data_;} - void user_data(const char *); - const char *user_data_type() const {return user_data_type_;} - void user_data_type(const char *); - const char *comment() { return comment_; } - void comment(const char *); - - virtual Fl_Type* click_test(int,int) { return NULL; } - - virtual void add_child(Fl_Type*, Fl_Type* beforethis) { } - virtual void move_child(Fl_Type*, Fl_Type* beforethis) { } - virtual void remove_child(Fl_Type*) { } - - /** Give widgets a chance to arrange their children after all children were added. - If adding individual children, this is called immediately, but if children - are read via a project file, we wait until all children are read and then - lay out the group. - */ - virtual void layout_widget() { } - - static Fl_Type *current; // most recently picked object - static Fl_Type *current_dnd; - - virtual void open(); // what happens when you double-click - - // read and write data to a saved file: - virtual void write(fld::io::Project_Writer &f); - virtual void write_properties(fld::io::Project_Writer &f); - virtual void read_property(fld::io::Project_Reader &f, const char *); - virtual void write_parent_properties(fld::io::Project_Writer &f, Fl_Type *child, bool encapsulate); - virtual void read_parent_property(fld::io::Project_Reader &f, Fl_Type *child, const char *property); - virtual int read_fdesign(const char*, const char*); - virtual void postprocess_read() { } - - // write code, these are called in order: - virtual void write_static(fld::io::Code_Writer& f); // write static stuff to .c file - virtual void write_static_after(fld::io::Code_Writer& f); // write static stuff after children - virtual void write_code1(fld::io::Code_Writer& f); // code and .h before children - virtual void write_code2(fld::io::Code_Writer& f); // code and .h after children - void write_comment_h(fld::io::Code_Writer& f, const char *ind=""); // write the commentary text into the header file - void write_comment_c(fld::io::Code_Writer& f, const char *ind=""); // write the commentary text into the source file - void write_comment_inline_c(fld::io::Code_Writer& f, const char *ind=0L); // write the commentary text - - // live mode - virtual Fl_Widget *enter_live_mode(int top=0); // build widgets needed for live mode - virtual void leave_live_mode(); // free allocated resources - virtual void copy_properties(); // copy properties from this type into a potential live object - virtual void copy_properties_for_children() { } // copy remaining properties after children were added - - // get message number for I18N - int msgnum(); - - /** Return 1 if the Type can have children. */ - virtual int can_have_children() const {return 0;} - /** Return 1 if the type is a widget or menu item. */ - virtual int is_widget() const {return 0;} - /** Return 1 if the type is a widget but not a menu item. */ - virtual int is_true_widget() const {return 0;} - /** Return 1 if a type behaves like a button (Fl_Button and Fl_Menu_Item and derived, but not Fl_Submenu_Type. */ - virtual int is_button() const {return 0;} - /** Return 1 if this is a Fl_Widget_Class_Type, Fl_CodeBlock_Type, or Fl_Function_Type */ - virtual int is_code_block() const {return 0;} - /** Return 1 if this is a Fl_Widget_Class_Type, Fl_Class_Type, or Fl_DeclBlock_Type */ - virtual int is_decl_block() const {return 0;} - /** Return 1 if this is a Fl_Class_Type or Fl_Widget_Class_Type. */ - virtual int is_class() const {return 0;} - /** Return 1 if the type browser shall draw a padlock over the icon. */ - virtual int is_public() const {return 1;} - /** Return the type ID for this Type. */ - virtual ID id() const { return ID_Base_; } - /** Check if this Type is of the give type ID or derived from that type ID. */ - virtual bool is_a(ID inID) const { return (inID==ID_Base_); } - - const char* class_name(const int need_nest) const; - bool is_in_class() const; - - int has_function(const char*, const char*) const; - - unsigned short set_uid(unsigned short suggested_uid=0); - unsigned short get_uid() { return uid_; } - static Fl_Type *find_by_uid(unsigned short uid); - - static Fl_Type *find_in_text(int text_type, int crsr); - - /// If this is greater zero, widgets will be allowed to lay out their children. - static int allow_layout; -}; - -#endif // _FLUID_FL_TYPE_H diff --git a/fluid/nodes/Fl_Widget_Type.cxx b/fluid/nodes/Fl_Widget_Type.cxx deleted file mode 100644 index 2cde4d531..000000000 --- a/fluid/nodes/Fl_Widget_Type.cxx +++ /dev/null @@ -1,3939 +0,0 @@ -// -// Widget type 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 "nodes/Fl_Widget_Type.h" - -#include "app/fluid.h" -#include "app/project.h" -#include "app/Fluid_Image.h" -#include "app/mergeback.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "nodes/Fl_Window_Type.h" -#include "nodes/Fl_Group_Type.h" -#include "nodes/Fl_Menu_Type.h" -#include "nodes/Fl_Function_Type.h" -#include "panels/settings_panel.h" -#include "panels/widget_panel.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../src/flstring.h" - -#include -#include - -// Make an Fl_Widget_Type subclass instance. -// It figures out the automatic size and parent of the new widget, -// creates the Fl_Widget (by calling the virtual function _make), -// adds it to the Fl_Widget hierarchy, creates a new Fl_Type -// instance, sets the widget pointers, and makes all the display -// update correctly... - -int Fl_Widget_Type::is_widget() const {return 1;} -int Fl_Widget_Type::is_public() const {return public_;} - -const char* subclassname(Fl_Type* l) { - if (l->is_a(ID_Menu_Bar)) { - Fl_Menu_Bar_Type *mb = static_cast(l); - if (mb->is_sys_menu_bar()) - return mb->sys_menubar_name(); - } - if (l->is_widget()) { - Fl_Widget_Type* p = (Fl_Widget_Type*)l; - const char* c = p->subclass(); - if (c) return c; - if (l->is_class()) return "Fl_Group"; - if (p->o->type() == FL_DOUBLE_WINDOW) return "Fl_Double_Window"; - if (p->id() == ID_Input) { - if (p->o->type() == FL_FLOAT_INPUT) return "Fl_Float_Input"; - if (p->o->type() == FL_INT_INPUT) return "Fl_Int_Input"; - } - } - return l->type_name(); -} - -// Return the ideal widget size... -void -Fl_Widget_Type::ideal_size(int &w, int &h) { - w = 120; - h = 100; - Fd_Snap_Action::better_size(w, h); -} - -/** - Make a new Widget node. - \param[in] strategy is Strategy::AS_LAST_CHILD or Strategy::AFTER_CURRENT - \return new node - */ -Fl_Type *Fl_Widget_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *pp = anchor; - if (pp && (strategy.placement() == Strategy::AFTER_CURRENT)) - pp = pp->parent; - while (pp && !pp->is_a(ID_Group)) { - anchor = pp; - strategy.placement(Strategy::AFTER_CURRENT); - pp = pp->parent; - } - if (!pp || !pp->is_true_widget() || !anchor->is_true_widget()) { - fl_message("Please select a group widget or window"); - return 0; - } - - Fl_Widget_Type* p = (Fl_Widget_Type*)pp; - Fl_Widget_Type* q = (Fl_Widget_Type*)anchor; - - // Figure out a border between widget and window: - int B = p->o->w()/2; if (p->o->h()/2 < B) B = p->o->h()/2; if (B>25) B = 25; - - int ULX,ULY; // parent's origin in window - if (!p->is_a(ID_Window)) { // if it is a group, add corner - ULX = p->o->x(); ULY = p->o->y(); - } else { - ULX = ULY = 0; - } - - // Figure out a position and size for the widget - int X,Y,W,H; - if (is_a(ID_Group)) { // fill the parent with the widget - X = ULX+B; - W = p->o->w()-B; - Y = ULY+B; - H = p->o->h()-B; - } else if (q != p) { // copy position and size of current widget - W = q->o->w(); - H = q->o->h(); - X = q->o->x()+W; - Y = q->o->y(); - if (X+W > ULX+p->o->w()) { - X = q->o->x(); - Y = q->o->y()+H; - if (Y+H > ULY+p->o->h()) Y = ULY+B; - } - } else { // just make it small and square... - X = ULX+B; - Y = ULY+B; - W = H = B; - } - - // Construct the Fl_Type: - Fl_Widget_Type *t = _make(); - if (!o) o = widget(0,0,100,100); // create template widget - t->factory = this; - // Construct the Fl_Widget: - t->o = widget(X,Y,W,H); - if (strategy.source() == Strategy::FROM_FILE) - t->o->label(0); - else if (t->o->label()) t->label(t->o->label()); // allow editing - t->o->user_data((void*)t); - // Put it in the parent: - // ((Fl_Group *)(p->o))->add(t->o); (done by Fl_Type::add()) - // add to browser: - t->add(anchor, strategy); - t->redraw(); - return t; -} - -void Fl_Widget_Type::setimage(Fluid_Image *i) { - if (i == image || is_a(ID_Window)) return; - if (image) image->decrement(); - if (i) i->increment(); - image = i; - if (i) { - i->image(o); - if (o->image() && (scale_image_w_ || scale_image_h_)) { - int iw = scale_image_w_>0 ? scale_image_w_ : o->image()->data_w(); - int ih = scale_image_h_>0 ? scale_image_h_ : o->image()->data_h(); - o->image()->scale(iw, ih, 0, 1); - } - } else { - o->image(0); - //scale_image_w_ = scale_image_h_ = 0; - } - redraw(); -} - -void Fl_Widget_Type::setinactive(Fluid_Image *i) { - if (i == inactive || is_a(ID_Window)) return; - if (inactive) inactive->decrement(); - if (i) i->increment(); - inactive = i; - if (i) { - i->deimage(o); - if (o->deimage()) { - int iw = scale_deimage_w_>0 ? scale_deimage_w_ : o->deimage()->data_w(); - int ih = scale_deimage_h_>0 ? scale_deimage_h_ : o->deimage()->data_h(); - o->deimage()->scale(iw, ih, 0, 1); - } - } else { - o->deimage(0); - //scale_deimage_w_ = scale_deimage_h_ = 0; - } - redraw(); -} - -void Fl_Widget_Type::setlabel(const char *n) { - o->label(n); - redraw(); -} - -Fl_Widget_Type::Fl_Widget_Type() -: override_visible_(0) -{ - for (int n=0; nwindow(); - delete o; - if (win) - win->redraw(); - } - if (subclass_) free((void*)subclass_); - if (tooltip_) free((void*)tooltip_); - if (image_name_) { - free((void*)image_name_); - if (image) image->decrement(); - } - if (inactive_name_) { - free((void*)inactive_name_); - if (inactive) inactive->decrement(); - } - for (int n=0; ntooltip(n); -} - -void Fl_Widget_Type::image_name(const char *n) { - setimage(Fluid_Image::find(n)); - storestring(n,image_name_); -} - -void Fl_Widget_Type::inactive_name(const char *n) { - setinactive(Fluid_Image::find(n)); - storestring(n,inactive_name_); -} - -void Fl_Widget_Type::redraw() { - Fl_Type *t = this; - if (is_a(ID_Menu_Item)) { - // find the menu button that parents this menu: - do t = t->parent; while (t && t->is_a(ID_Menu_Item)); - // kludge to cause build_menu to be called again: - if (t) - t->add_child(0, 0); - } else { - while (t->parent && t->parent->is_widget()) t = t->parent; - ((Fl_Widget_Type*)t)->o->redraw(); - } -} - -// the recursive part sorts all children, returns pointer to next: -Fl_Type *sort(Fl_Type *parent) { - Fl_Type *f,*n=0; - for (f = parent ? parent->next : Fl_Type::first; ; f = n) { - if (!f || (parent && f->level <= parent->level)) break; - n = sort(f); - if (!f->selected || !f->is_true_widget()) continue; - Fl_Widget* fw = ((Fl_Widget_Type*)f)->o; - Fl_Type *g; // we will insert before this - for (g = parent ? parent->next : Fl_Type::first; g != f; g = g->next) { - if (!g->selected || g->level > f->level) continue; - Fl_Widget* gw = ((Fl_Widget_Type*)g)->o; - if (gw->y() > fw->y()) break; - if (gw->y() == fw->y() && gw->x() > fw->x()) break; - } - if (g != f) f->move_before(g); - } - if (parent) - parent->layout_widget(); - return f; -} - -//////////////////////////////////////////////////////////////// -// The control panels! - -Fl_Window *the_panel; - -// All the callbacks use the argument to indicate whether to load or store. -// This avoids the need for pointers to all the widgets, and keeps the -// code localized in the callbacks. -// A value of LOAD means to load. The hope is that this will not collide -// with any actual useful values for the argument. I also use this to -// initialized parts of the widget that are nyi by fluid. - -Fl_Widget_Type *current_widget; // one of the selected ones -void* const LOAD = (void *)"LOAD"; // "magic" pointer to indicate that we need to load values into the dialog -static int numselected; // number selected -static int haderror; - -void name_cb(Fl_Input* o, void *v) { - if (v == LOAD) { - static char buf[1024]; - if (numselected != 1) { - snprintf(buf, sizeof(buf), "Widget Properties (%d widgets)", numselected); - o->hide(); - } else { - o->value(current_widget->name()); - o->show(); - snprintf(buf, sizeof(buf), "%s Properties", current_widget->title()); - } - - the_panel->label(buf); - } else { - if (numselected == 1) { - current_widget->name(o->value()); - // I don't update window title, as it probably is being closed - // and wm2 (a window manager) barfs if you retitle and then - // hide a window: - // ((Fl_Window*)(o->parent()->parent()->parent()))->label(current_widget->title()); - } - } -} - -void name_public_member_cb(Fl_Choice* i, void* v) { - if (v == LOAD) { - i->value(current_widget->public_); - if (current_widget->is_in_class()) i->show(); else i->hide(); - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type *w = ((Fl_Widget_Type*)o); - if (w->is_in_class()) { - w->public_ = i->value(); - } else { - // if this is not in a class, it can be only private or public - w->public_ = (i->value()>0); - } - mod = 1; - } - } - if (mod) { - set_modflag(1); - redraw_browser(); - } - } -} - -void name_public_cb(Fl_Choice* i, void* v) { - if (v == LOAD) { - i->value(current_widget->public_>0); - if (current_widget->is_in_class()) i->hide(); else i->show(); - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->public_ = i->value(); - mod = 1; - } - } - if (mod) { - set_modflag(1); - redraw_browser(); - } - } -} - -/* Treating UNDO for text widget. - - Goal: we want to continuously update the UI while the user is typing text - (changing the label, in this case). Code View does deferred updates, and - the widget browser and widget panel update on every keystroke. At the same - time, we want to limit undo actions to few and logical units. - - Caveats: - - 1: the text widget has its own undo handling for the text field, but we may want to do a global undo - 2: every o->label() call will create an undo entry, but we want only one single event for all selected widgets - 3: we want a single undo for the entire editing phase, but still propagate changes as they happen - - The edit process has these main states: - - 1: starting to edit [first_change==1 && !unfocus]; we must create a single undo checkpoint before anything changes - 2: continue editing [first_change==0 && !unfocus]; we must suspend any undo checkpoints - 3: done editing, unfocus [first_change==0 && unfocus]; we must make sure that undo checkpoints are enabled again - 4: losing focus without editing [first_change==1 && unfocus]; don't create and checkpoints - - We must also check: - 1: changing focus without changing text (works) - 2: copy and paste, drag and drop operations (works) - 3: save operation without unfocus event (works) - */ -void label_cb(Fl_Input* i, void *v) { - static int first_change = 1; - if (v == LOAD) { - i->value(current_widget->label()); - first_change = 1; - } else { - if (i->changed()) { - undo_suspend(); - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - if (!mod) { - if (first_change) { - undo_resume(); - undo_checkpoint(); - undo_suspend(); - first_change = 0; - } - mod = 1; - } - o->label(i->value()); - } - } - undo_resume(); - if (mod) set_modflag(1); - } - int r = (int)Fl::callback_reason(); - if ( (r == FL_REASON_LOST_FOCUS) || (r == FL_REASON_ENTER_KEY) ) - first_change = 1; - } -} - -static Fl_Input *image_input; - -void image_cb(Fl_Input* i, void *v) { - if (v == LOAD) { - image_input = i; - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { - i->activate(); - i->value(((Fl_Widget_Type*)current_widget)->image_name()); - } else i->deactivate(); - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->image_name(i->value()); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void image_browse_cb(Fl_Button* b, void *v) { - if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) - b->activate(); - else - b->deactivate(); - } else { - int mod = 0; - if (ui_find_image(image_input->value())) { - image_input->value(ui_find_image_name); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->image_name(ui_find_image_name); - mod = 1; - } - } - if (mod) set_modflag(1); - } - } -} - -void bind_image_cb(Fl_Check_Button* b, void *v) { - if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { - b->activate(); - b->value(current_widget->bind_image_); - } else { - b->deactivate(); - } - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->bind_image_ = b->value(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void compress_image_cb(Fl_Check_Button* b, void *v) { - if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { - b->activate(); - b->value(!current_widget->compress_image_); - } else { - b->deactivate(); - } - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->compress_image_ = !b->value(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -static Fl_Input *inactive_input; - -void inactive_cb(Fl_Input* i, void *v) { - if (v == LOAD) { - inactive_input = i; - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { - i->activate(); - i->value(((Fl_Widget_Type*)current_widget)->inactive_name()); - } else i->deactivate(); - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->inactive_name(i->value()); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void inactive_browse_cb(Fl_Button* b, void *v) { - if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) - b->activate(); - else - b->deactivate(); - } else { - int mod = 0; - if (ui_find_image(inactive_input->value())) { - inactive_input->value(ui_find_image_name); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->inactive_name(ui_find_image_name); - mod = 1; - } - } - if (mod) set_modflag(1); - } - } -} - -void bind_deimage_cb(Fl_Check_Button* b, void *v) { - if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { - b->activate(); - b->value(current_widget->bind_deimage_); - } else { - b->deactivate(); - } - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->bind_deimage_ = b->value(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void compress_deimage_cb(Fl_Check_Button* b, void *v) { - if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { - b->activate(); - b->value(!current_widget->compress_deimage_); - } else { - b->deactivate(); - } - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->compress_deimage_ = !b->value(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void tooltip_cb(Fl_Input* i, void *v) { - if (v == LOAD) { - if (current_widget->is_widget()) { - i->activate(); - i->value(((Fl_Widget_Type*)current_widget)->tooltip()); - } else i->deactivate(); - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->tooltip(i->value()); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -fld::widget::Formula_Input *x_input, *y_input, *w_input, *h_input; - -static int widget_i = 0; - -static int vars_i_cb(const fld::widget::Formula_Input*, void *v) { - return widget_i; -} - -static int vars_x_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = (Fl_Type*)v; - if (t->is_widget()) - return ((Fl_Widget_Type*)t)->o->x(); - return 0; -} - -static int vars_y_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = (Fl_Type*)v; - if (t->is_widget()) - return ((Fl_Widget_Type*)t)->o->y(); - return 0; -} - -static int vars_w_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = (Fl_Type*)v; - if (t->is_widget()) - return ((Fl_Widget_Type*)t)->o->w(); - return 0; -} - -static int vars_h_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = (Fl_Type*)v; - if (t->is_widget()) - return ((Fl_Widget_Type*)t)->o->h(); - return 0; -} - -static int vars_px_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->parent; - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->x(); - return 0; -} - -static int vars_py_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->parent; - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->y(); - return 0; -} - -static int vars_pw_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->parent; - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->w(); - return 0; -} - -static int vars_ph_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->parent; - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->h(); - return 0; -} - -static int vars_sx_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->prev_sibling(); - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->x(); - return 0; -} - -static int vars_sy_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->prev_sibling(); - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->y(); - return 0; -} - -static int vars_sw_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->prev_sibling(); - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->w(); - return 0; -} - -static int vars_sh_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->prev_sibling(); - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->h(); - return 0; -} - -static int bbox_x, bbox_y, bbox_r, bbox_b; -static int bbox_min(int a, int b) { return (ab) ? a : b; } - -static void calculate_bbox(Fl_Type *p) { - char first = 1; - bbox_x = bbox_y = bbox_r = bbox_b = 0; - for (p=p->first_child(); p; p=p->next_sibling()) { - if (p->is_widget()) { - Fl_Widget *o = ((Fl_Widget_Type*)p)->o; - if (first) { - bbox_x = o->x(); bbox_y = o->y(); - bbox_r = o->x() + o->w(); bbox_b = o->y() + o->h(); - first = 0; - } else { - bbox_x = bbox_min(bbox_x, o->x()); - bbox_y = bbox_min(bbox_y, o->y()); - bbox_r = bbox_max(bbox_r, o->x() + o->w()); - bbox_b = bbox_max(bbox_b, o->y() + o->h()); - } - } - } -} - -static int vars_cx_cb(const fld::widget::Formula_Input*, void *v) { - calculate_bbox((Fl_Type*)v); - return bbox_x; -} - -static int vars_cy_cb(const fld::widget::Formula_Input*, void *v) { - calculate_bbox((Fl_Type*)v); - return bbox_y; -} - -static int vars_cw_cb(const fld::widget::Formula_Input*, void *v) { - calculate_bbox((Fl_Type*)v); - return bbox_r - bbox_x; -} - -static int vars_ch_cb(const fld::widget::Formula_Input*, void *v) { - calculate_bbox((Fl_Type*)v); - return bbox_b - bbox_y; -} - -fld::widget::Formula_Input_Vars widget_vars[] = { - { "i", vars_i_cb }, // zero based counter of selected widgets - { "x", vars_x_cb }, // position and size of current widget - { "y", vars_y_cb }, - { "w", vars_w_cb }, - { "h", vars_h_cb }, - { "px", vars_px_cb }, // position and size of parent widget - { "py", vars_py_cb }, - { "pw", vars_pw_cb }, - { "ph", vars_ph_cb }, - { "sx", vars_sx_cb }, // position and size of previous sibling - { "sy", vars_sy_cb }, - { "sw", vars_sw_cb }, - { "sh", vars_sh_cb }, - { "cx", vars_cx_cb }, // position and size of bounding box of all children - { "cy", vars_cy_cb }, - { "cw", vars_cw_cb }, - { "ch", vars_ch_cb }, - { 0 } -}; - -void x_cb(fld::widget::Formula_Input *i, void *v) { - if (v == LOAD) { - x_input = i; - if (current_widget->is_true_widget()) { - i->value(((Fl_Widget_Type *)current_widget)->o->x()); - x_input->activate(); - } else x_input->deactivate(); - } else { - undo_checkpoint(); - widget_i = 0; - int mod = 0; - int v = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - i->variables(widget_vars, o); - v = i->value(); - w->resize(v, w->y(), w->w(), w->h()); - if (w->window()) w->window()->redraw(); - widget_i++; - mod = 1; - } - } - if (mod) { - set_modflag(1); - i->value(v); // change the displayed value to the result of the last - // calculation. Keep the formula if it was not used. - } - } -} - -void y_cb(fld::widget::Formula_Input *i, void *v) { - if (v == LOAD) { - y_input = i; - if (current_widget->is_true_widget()) { - i->value(((Fl_Widget_Type *)current_widget)->o->y()); - y_input->activate(); - } else y_input->deactivate(); - } else { - undo_checkpoint(); - widget_i = 0; - int mod = 0; - int v = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - i->variables(widget_vars, o); - v = i->value(); - w->resize(w->x(), v, w->w(), w->h()); - if (w->window()) w->window()->redraw(); - widget_i++; - mod = 1; - } - } - if (mod) { - set_modflag(1); - i->value(v); - } - } -} - -void w_cb(fld::widget::Formula_Input *i, void *v) { - if (v == LOAD) { - w_input = i; - if (current_widget->is_true_widget()) { - i->value(((Fl_Widget_Type *)current_widget)->o->w()); - w_input->activate(); - } else w_input->deactivate(); - } else { - undo_checkpoint(); - widget_i = 0; - int mod = 0; - int v = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - i->variables(widget_vars, o); - v = i->value(); - w->resize(w->x(), w->y(), v, w->h()); - if (w->window()) w->window()->redraw(); - widget_i++; - mod = 1; - } - } - if (mod) { - set_modflag(1); - i->value(v); - } - } -} - -void h_cb(fld::widget::Formula_Input *i, void *v) { - if (v == LOAD) { - h_input = i; - if (current_widget->is_true_widget()) { - i->value(((Fl_Widget_Type *)current_widget)->o->h()); - h_input->activate(); - } else h_input->deactivate(); - } else { - undo_checkpoint(); - widget_i = 0; - int mod = 0; - int v = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - i->variables(widget_vars, o); - v = i->value(); - w->resize(w->x(), w->y(), w->w(), v); - if (w->window()) w->window()->redraw(); - widget_i++; - mod = 1; - } - } - if (mod) { - set_modflag(1); - i->value(v); - } - } -} - -void wc_relative_cb(Fl_Choice *i, void *v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Widget_Class)) { - i->show(); - i->value(((Fl_Widget_Class_Type *)current_widget)->wc_relative); - } else { - i->hide(); - } - } else { - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && current_widget->is_a(ID_Widget_Class)) { - Fl_Widget_Class_Type *t = (Fl_Widget_Class_Type *)o; - t->wc_relative = i->value(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -// turn number to string or string to number for saving to file: -// does not work for hierarchical menus! - -const char *item_name(Fl_Menu_Item* m, int i) { - if (m) { - while (m->label()) { - if (m->argument() == i) return m->label(); - m++; - } - } - static char buffer[20]; - sprintf(buffer, "%d", i); - return buffer; -} -int item_number(Fl_Menu_Item* m, const char* i) { - if (!i) - return 0; - if (m && i) { - if (i[0]=='F' && i[1]=='L' && i[2]=='_') i += 3; - while (m->label()) { - if (!strcmp(m->label(), i)) return int(m->argument()); - m++; - } - } - return atoi(i); -} - -#define ZERO_ENTRY 1000 - -Fl_Menu_Item boxmenu[] = { -{"NO_BOX",0,0,(void *)ZERO_ENTRY}, -{"boxes",0,0,0,FL_SUBMENU}, -{"UP_BOX",0,0,(void *)FL_UP_BOX}, -{"DOWN_BOX",0,0,(void *)FL_DOWN_BOX}, -{"FLAT_BOX",0,0,(void *)FL_FLAT_BOX}, -{"BORDER_BOX",0,0,(void *)FL_BORDER_BOX}, -{"THIN_UP_BOX",0,0,(void *)FL_THIN_UP_BOX}, -{"THIN_DOWN_BOX",0,0,(void *)FL_THIN_DOWN_BOX}, -{"ENGRAVED_BOX",0,0,(void *)FL_ENGRAVED_BOX}, -{"EMBOSSED_BOX",0,0,(void *)FL_EMBOSSED_BOX}, -{"ROUND_UP_BOX",0,0,(void *)FL_ROUND_UP_BOX}, -{"ROUND_DOWN_BOX",0,0,(void *)FL_ROUND_DOWN_BOX}, -{"DIAMOND_UP_BOX",0,0,(void *)FL_DIAMOND_UP_BOX}, -{"DIAMOND_DOWN_BOX",0,0,(void *)FL_DIAMOND_DOWN_BOX}, -{"SHADOW_BOX",0,0,(void *)FL_SHADOW_BOX}, -{"ROUNDED_BOX",0,0,(void *)FL_ROUNDED_BOX}, -{"RSHADOW_BOX",0,0,(void *)FL_RSHADOW_BOX}, -{"RFLAT_BOX",0,0,(void *)FL_RFLAT_BOX}, -{"OVAL_BOX",0,0,(void *)FL_OVAL_BOX}, -{"OSHADOW_BOX",0,0,(void *)FL_OSHADOW_BOX}, -{"OFLAT_BOX",0,0,(void *)FL_OFLAT_BOX}, -{"PLASTIC_UP_BOX",0,0,(void *)FL_PLASTIC_UP_BOX}, -{"PLASTIC_DOWN_BOX",0,0,(void *)FL_PLASTIC_DOWN_BOX}, -{"PLASTIC_THIN_UP_BOX",0,0,(void *)FL_PLASTIC_THIN_UP_BOX}, -{"PLASTIC_THIN_DOWN_BOX",0,0,(void *)FL_PLASTIC_THIN_DOWN_BOX}, -{"PLASTIC_ROUND_UP_BOX",0,0,(void *)FL_PLASTIC_ROUND_UP_BOX}, -{"PLASTIC_ROUND_DOWN_BOX",0,0,(void *)FL_PLASTIC_ROUND_DOWN_BOX}, -{"GTK_UP_BOX",0,0,(void *)FL_GTK_UP_BOX}, -{"GTK_DOWN_BOX",0,0,(void *)FL_GTK_DOWN_BOX}, -{"GTK_THIN_UP_BOX",0,0,(void *)FL_GTK_THIN_UP_BOX}, -{"GTK_THIN_DOWN_BOX",0,0,(void *)FL_GTK_THIN_DOWN_BOX}, -{"GTK_ROUND_UP_BOX",0,0,(void *)FL_GTK_ROUND_UP_BOX}, -{"GTK_ROUND_DOWN_BOX",0,0,(void *)FL_GTK_ROUND_DOWN_BOX}, -{"GLEAM_UP_BOX",0,0,(void *)FL_GLEAM_UP_BOX}, -{"GLEAM_DOWN_BOX",0,0,(void *)FL_GLEAM_DOWN_BOX}, -{"GLEAM_THIN_UP_BOX",0,0,(void *)FL_GLEAM_THIN_UP_BOX}, -{"GLEAM_THIN_DOWN_BOX",0,0,(void *)FL_GLEAM_THIN_DOWN_BOX}, -{"GLEAM_ROUND_UP_BOX",0,0,(void *)FL_GLEAM_ROUND_UP_BOX}, -{"GLEAM_ROUND_DOWN_BOX",0,0,(void *)FL_GLEAM_ROUND_DOWN_BOX}, -{"OXY_UP_BOX",0,0,(void *)FL_OXY_UP_BOX}, -{"OXY_DOWN_BOX",0,0,(void *)FL_OXY_DOWN_BOX}, -{"OXY_THIN_UP_BOX",0,0,(void *)FL_OXY_THIN_UP_BOX}, -{"OXY_THIN_DOWN_BOX",0,0,(void *)FL_OXY_THIN_DOWN_BOX}, -{"OXY_ROUND_UP_BOX",0,0,(void *)FL_OXY_ROUND_UP_BOX}, -{"OXY_ROUND_DOWN_BOX",0,0,(void *)FL_OXY_ROUND_DOWN_BOX}, -{"OXY_BUTTON_UP_BOX",0,0,(void *)FL_OXY_BUTTON_UP_BOX}, -{"OXY_BUTTON_DOWN_BOX",0,0,(void *)FL_OXY_BUTTON_DOWN_BOX}, -{0}, -{"frames",0,0,0,FL_SUBMENU}, -{"UP_FRAME",0,0,(void *)FL_UP_FRAME}, -{"DOWN_FRAME",0,0,(void *)FL_DOWN_FRAME}, -{"THIN_UP_FRAME",0,0,(void *)FL_THIN_UP_FRAME}, -{"THIN_DOWN_FRAME",0,0,(void *)FL_THIN_DOWN_FRAME}, -{"ENGRAVED_FRAME",0,0,(void *)FL_ENGRAVED_FRAME}, -{"EMBOSSED_FRAME",0,0,(void *)FL_EMBOSSED_FRAME}, -{"BORDER_FRAME",0,0,(void *)FL_BORDER_FRAME}, -{"SHADOW_FRAME",0,0,(void *)FL_SHADOW_FRAME}, -{"ROUNDED_FRAME",0,0,(void *)FL_ROUNDED_FRAME}, -{"OVAL_FRAME",0,0,(void *)FL_OVAL_FRAME}, -{"PLASTIC_UP_FRAME",0,0,(void *)FL_PLASTIC_UP_FRAME}, -{"PLASTIC_DOWN_FRAME",0,0,(void *)FL_PLASTIC_DOWN_FRAME}, -{"GTK_UP_FRAME",0,0,(void *)FL_GTK_UP_FRAME}, -{"GTK_DOWN_FRAME",0,0,(void *)FL_GTK_DOWN_FRAME}, -{"GTK_THIN_UP_FRAME",0,0,(void *)FL_GTK_THIN_UP_FRAME}, -{"GTK_THIN_DOWN_FRAME",0,0,(void *)FL_GTK_THIN_DOWN_FRAME}, -{"GLEAM_UP_FRAME",0,0,(void *)FL_GLEAM_UP_FRAME}, -{"GLEAM_DOWN_FRAME",0,0,(void *)FL_GLEAM_DOWN_FRAME}, -{"OXY_UP_FRAME",0,0,(void *)FL_OXY_UP_FRAME}, -{"OXY_DOWN_FRAME",0,0,(void *)FL_OXY_DOWN_FRAME}, -{"OXY_THIN_UP_FRAME",0,0,(void *)FL_OXY_THIN_UP_FRAME}, -{"OXY_THIN_DOWN_FRAME",0,0,(void *)FL_OXY_THIN_DOWN_FRAME}, -{0}, -{0}}; - -const char *boxname(int i) { - if (!i) i = ZERO_ENTRY; - for (int j = 0; j < int(sizeof(boxmenu)/sizeof(*boxmenu)); j++) - if (boxmenu[j].argument() == i) return boxmenu[j].label(); - return 0; -} - -int boxnumber(const char *i) { - if (i[0]=='F' && i[1]=='L' && i[2]=='_') i += 3; - for (int j = 0; j < int(sizeof(boxmenu)/sizeof(*boxmenu)); j++) - if (boxmenu[j].label() && !strcmp(boxmenu[j].label(), i)) { - return int(boxmenu[j].argument()); - } - return 0; -} - -void box_cb(Fl_Choice* i, void *v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - int n = current_widget->o->box(); if (!n) n = ZERO_ENTRY; - for (int j = 0; j < int(sizeof(boxmenu)/sizeof(*boxmenu)); j++) - if (boxmenu[j].argument() == n) {i->value(j); break;} - } else { - int mod = 0; - int m = i->value(); - int n = int(boxmenu[m].argument()); - if (!n) return; // should not happen - if (n == ZERO_ENTRY) n = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->box((Fl_Boxtype)n); - q->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void down_box_cb(Fl_Choice* i, void *v) { - if (v == LOAD) { - int n; - if (current_widget->is_a(ID_Button)) - n = ((Fl_Button*)(current_widget->o))->down_box(); - else if (current_widget->is_a(ID_Input_Choice)) - n = ((Fl_Input_Choice*)(current_widget->o))->down_box(); - else if (current_widget->is_a(ID_Menu_Manager_)) - n = ((Fl_Menu_*)(current_widget->o))->down_box(); - else { - i->deactivate(); return; - } - i->activate(); - if (!n) n = ZERO_ENTRY; - for (int j = 0; j < int(sizeof(boxmenu)/sizeof(*boxmenu)); j++) - if (boxmenu[j].argument() == n) {i->value(j); break;} - } else { - int mod = 0; - int m = i->value(); - int n = int(boxmenu[m].argument()); - if (!n) return; // should not happen - if (n == ZERO_ENTRY) n = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected) { - if (o->is_a(ID_Button)) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - ((Fl_Button*)(q->o))->down_box((Fl_Boxtype)n); - if (((Fl_Button*)(q->o))->value()) q->redraw(); - } else if (o->is_a(ID_Input_Choice)) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - ((Fl_Input_Choice*)(q->o))->down_box((Fl_Boxtype)n); - } else if (o->is_a(ID_Menu_Manager_)) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - ((Fl_Menu_*)(q->o))->down_box((Fl_Boxtype)n); - } - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void compact_cb(Fl_Light_Button* i, void* v) { - if (v == LOAD) { - uchar n; - if (current_widget->is_a(ID_Button) && !current_widget->is_a(ID_Menu_Item)) { - n = ((Fl_Button*)(current_widget->o))->compact(); - i->value(n); - i->show(); - } else { - i->hide(); - } - } else { - int mod = 0; - uchar n = (uchar)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Button) && !o->is_a(ID_Menu_Item)) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - uchar v = ((Fl_Button*)(q->o))->compact(); - if (n != v) { - if (!mod) { - mod = 1; - undo_checkpoint(); - } - ((Fl_Button*)(q->o))->compact(n); - q->redraw(); - } - } - } - if (mod) set_modflag(1); - } -} - - - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Item whenmenu[] = { - // set individual bits - {"FL_WHEN_CHANGED",0,0,(void*)FL_WHEN_CHANGED, FL_MENU_TOGGLE}, - {"FL_WHEN_NOT_CHANGED",0,0,(void*)FL_WHEN_NOT_CHANGED, FL_MENU_TOGGLE}, - {"FL_WHEN_RELEASE",0,0,(void*)FL_WHEN_RELEASE, FL_MENU_TOGGLE}, - {"FL_WHEN_ENTER_KEY",0,0,(void*)FL_WHEN_ENTER_KEY, FL_MENU_TOGGLE}, - {"FL_WHEN_CLOSED",0,0,(void*)FL_WHEN_CLOSED, FL_MENU_TOGGLE|FL_MENU_DIVIDER}, - // set bit combinations - {"FL_WHEN_NEVER",0,0,(void*)FL_WHEN_NEVER}, - {"FL_WHEN_RELEASE_ALWAYS",0,0,(void*)FL_WHEN_RELEASE_ALWAYS}, - {"FL_WHEN_ENTER_KEY_ALWAYS",0,0,(void*)FL_WHEN_ENTER_KEY_ALWAYS}, - {"FL_WHEN_ENTER_KEY_CHANGED",0,0,(void*)FL_WHEN_ENTER_KEY_CHANGED}, - {0}}; - - -static Fl_Menu_Item whensymbolmenu[] = { - /* 0 */ {"FL_WHEN_NEVER",0,0,(void*)FL_WHEN_NEVER}, - /* 1 */ {"FL_WHEN_CHANGED",0,0,(void*)FL_WHEN_CHANGED}, - /* 2 */ {"FL_WHEN_NOT_CHANGED",0,0,(void*)FL_WHEN_NOT_CHANGED}, - /* 3 */ {"FL_WHEN_CHANGED | FL_WHEN_NOT_CHANGED",0,0,(void*)(FL_WHEN_CHANGED|FL_WHEN_NOT_CHANGED)}, - /* 4 */ {"FL_WHEN_RELEASE",0,0,(void*)FL_WHEN_RELEASE}, - /* 5 */ {"FL_WHEN_CHANGED | FL_WHEN_RELEASE",0,0,(void*)(FL_WHEN_CHANGED|FL_WHEN_RELEASE)}, - /* 6 */ {"FL_WHEN_RELEASE_ALWAYS",0,0,(void*)FL_WHEN_RELEASE_ALWAYS}, - /* 7 */ {"FL_WHEN_CHANGED | FL_WHEN_RELEASE_ALWAYS",0,0,(void*)(FL_WHEN_CHANGED|FL_WHEN_RELEASE_ALWAYS)}, - /* 8 */ {"FL_WHEN_ENTER_KEY",0,0,(void*)FL_WHEN_ENTER_KEY}, - /* 9 */ {"FL_WHEN_CHANGED | FL_WHEN_ENTER_KEY",0,0,(void*)(FL_WHEN_CHANGED|FL_WHEN_ENTER_KEY)}, - /* 10 */ {"FL_WHEN_ENTER_KEY_ALWAYS",0,0,(void*)FL_WHEN_ENTER_KEY_ALWAYS}, - /* 11 */ {"FL_WHEN_ENTER_KEY_CHANGED",0,0,(void*)FL_WHEN_ENTER_KEY_CHANGED}, - /* 12 */ {"FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY",0,0,(void*)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY)}, - /* 13 */ {"FL_WHEN_RELEASE | FL_WHEN_CHANGED | FL_WHEN_ENTER_KEY",0,0,(void*)(FL_WHEN_RELEASE|FL_WHEN_CHANGED|FL_WHEN_ENTER_KEY)}, - /* 14 */ {"FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY_ALWAYS",0,0,(void*)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY_ALWAYS)}, - /* 15 */ {"FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY_CHANGED",0,0,(void*)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY_CHANGED)}, - {0} -}; - -// Return a text string representing the Fl_When value n -const char* when_symbol_name(int n) { - static char sym[128]; - if (n == FL_WHEN_CLOSED) { - strcpy(sym, "FL_WHEN_CLOSED"); - } else { - strcpy(sym, whensymbolmenu[n&15].label()); - if (n & FL_WHEN_CLOSED) - strcat(sym, " | FL_WHEN_CLOSED"); - } - return sym; -} - -// Set the check marks in the "when()" menu according to the Fl_When value n -void set_whenmenu(int n) { - if (n&FL_WHEN_CHANGED) whenmenu[0].set(); else whenmenu[0].clear(); - if (n&FL_WHEN_NOT_CHANGED) whenmenu[1].set(); else whenmenu[1].clear(); - if (n&FL_WHEN_RELEASE) whenmenu[2].set(); else whenmenu[2].clear(); - if (n&FL_WHEN_ENTER_KEY) whenmenu[3].set(); else whenmenu[3].clear(); - if (n&FL_WHEN_CLOSED) whenmenu[4].set(); else whenmenu[4].clear(); -} - -void when_cb(Fl_Menu_Button* i, void *v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - int n = current_widget->o->when(); - set_whenmenu(n); - w_when_box->copy_label(when_symbol_name(n)); - } else { - int mod = 0; - int n = 0; - if (i->mvalue() && ((i->mvalue()->flags & FL_MENU_TOGGLE) == 0) ) { - n = (int)i->mvalue()->argument(); - set_whenmenu(n); - } else { - if (whenmenu[0].value()) n |= FL_WHEN_CHANGED; - if (whenmenu[1].value()) n |= FL_WHEN_NOT_CHANGED; - if (whenmenu[2].value()) n |= FL_WHEN_RELEASE; - if (whenmenu[3].value()) n |= FL_WHEN_ENTER_KEY; - if (whenmenu[4].value()) n |= FL_WHEN_CLOSED; - } - w_when_box->copy_label(when_symbol_name(n)); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->when(n); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -uchar Fl_Widget_Type::resizable() const { - if (is_a(ID_Window)) return ((Fl_Window*)o)->resizable() != 0; - Fl_Group* p = (Fl_Group*)o->parent(); - if (p) return p->resizable() == o; - else return 0; -} - -void Fl_Widget_Type::resizable(uchar v) { - if (v) { - if (resizable()) return; - if (is_a(ID_Window)) ((Fl_Window*)o)->resizable(o); - else { - Fl_Group* p = (Fl_Group*)o->parent(); - if (p) p->resizable(o); - } - } else { - if (!resizable()) return; - if (is_a(ID_Window)) { - ((Fl_Window*)o)->resizable(0); - } else { - Fl_Group* p = (Fl_Group*)o->parent(); - if (p) p->resizable(0); - } - } -} - -void resizable_cb(Fl_Light_Button* i,void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} - if (numselected > 1) {i->deactivate(); return;} - i->activate(); - i->value(current_widget->resizable()); - } else { - undo_checkpoint(); - current_widget->resizable(i->value()); - set_modflag(1); - } -} - -void hotspot_cb(Fl_Light_Button* i,void* v) { - if (v == LOAD) { - if (numselected > 1) {i->deactivate(); return;} - if (current_widget->is_a(ID_Menu_Item)) i->label("divider"); - else i->label("hotspot"); - i->activate(); - i->value(current_widget->hotspot()); - } else { - undo_checkpoint(); - current_widget->hotspot(i->value()); - if (current_widget->is_a(ID_Menu_Item)) { - current_widget->redraw(); - return; - } - if (i->value()) { - Fl_Type *p = current_widget->parent; - if (!p || !p->is_widget()) return; - while (!p->is_a(ID_Window)) p = p->parent; - for (Fl_Type *o = p->next; o && o->level > p->level; o = o->next) { - if (o->is_widget() && o != current_widget) - ((Fl_Widget_Type*)o)->hotspot(0); - } - } - set_modflag(1); - } -} - -void visible_cb(Fl_Light_Button* i, void* v) { - if (v == LOAD) { - i->value(current_widget->o->visible()); - if (current_widget->is_a(ID_Window)) i->deactivate(); - else i->activate(); - } else { - int mod = 0; - int n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - if (!mod) { - mod = 1; - undo_checkpoint(); - } - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - n ? q->o->show() : q->o->hide(); - q->redraw(); - if (n && q->parent && q->parent->type_name()) { - if (q->parent->is_a(ID_Tabs)) { - ((Fl_Tabs *)q->o->parent())->value(q->o); - } else if (q->parent->is_a(ID_Wizard)) { - ((Fl_Wizard *)q->o->parent())->value(q->o); - } - } - } - } - if (mod) { - set_modflag(1); - redraw_browser(); - } - } -} - -void active_cb(Fl_Light_Button* i, void* v) { - if (v == LOAD) { - i->value(current_widget->o->active()); - if (current_widget->is_a(ID_Window)) i->deactivate(); - else i->activate(); - } else { - int mod = 0; - int n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - if (!mod) { - mod = 1; - undo_checkpoint(); - } - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - n ? q->o->activate() : q->o->deactivate(); - q->redraw(); - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Item fontmenu[] = { - {"Helvetica"}, - {"Helvetica bold"}, - {"Helvetica italic"}, - {"Helvetica bold italic"}, - {"Courier"}, - {"Courier bold"}, - {"Courier italic"}, - {"Courier bold italic"}, - {"Times"}, - {"Times bold"}, - {"Times italic"}, - {"Times bold italic"}, - {"Symbol"}, - {"Terminal"}, - {"Terminal Bold"}, - {"Zapf Dingbats"}, - {NULL} -}; - -Fl_Menu_Item fontmenu_w_default[] = { - {"", 0, NULL, NULL, FL_MENU_DIVIDER}, - {"Helvetica"}, - {"Helvetica bold"}, - {"Helvetica italic"}, - {"Helvetica bold italic"}, - {"Courier"}, - {"Courier bold"}, - {"Courier italic"}, - {"Courier bold italic"}, - {"Times"}, - {"Times bold"}, - {"Times italic"}, - {"Times bold italic"}, - {"Symbol"}, - {"Terminal"}, - {"Terminal Bold"}, - {"Zapf Dingbats"}, - {NULL} -}; - -void labelfont_cb(Fl_Choice* i, void *v) { - if (v == LOAD) { - int n = current_widget->o->labelfont(); - if (n > 15) n = 0; - i->value(n); - } else { - int mod = 0; - int n = i->value(); - if (n <= 0) n = layout->labelfont; - if (n <= 0) n = FL_HELVETICA; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->labelfont(n); - q->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void labelsize_cb(Fl_Value_Input* i, void *v) { - int n; - if (v == LOAD) { - n = current_widget->o->labelsize(); - } else { - int mod = 0; - n = int(i->value()); - if (n <= 0) n = layout->labelsize; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->labelsize(n); - q->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); - } - i->value(n); -} - -extern const char *ui_find_image_name; - -Fl_Menu_Item labeltypemenu[] = { - {"NORMAL_LABEL",0,0,(void*)0}, - {"SHADOW_LABEL",0,0,(void*)FL_SHADOW_LABEL}, - {"ENGRAVED_LABEL",0,0,(void*)FL_ENGRAVED_LABEL}, - {"EMBOSSED_LABEL",0,0,(void*)FL_EMBOSSED_LABEL}, - {"NO_LABEL",0,0,(void*)(FL_NO_LABEL)}, -{0}}; - -void labeltype_cb(Fl_Choice* i, void *v) { - if (v == LOAD) { - int n; - n = current_widget->o->labeltype(); - i->when(FL_WHEN_RELEASE); - for (int j = 0; j < int(sizeof(labeltypemenu)/sizeof(*labeltypemenu)); j++) - if (labeltypemenu[j].argument() == n) {i->value(j); break;} - } else { - int mod = 0; - int m = i->value(); - int n = int(labeltypemenu[m].argument()); - if (n<0) return; // should not happen - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* p = (Fl_Widget_Type*)o; - p->o->labeltype((Fl_Labeltype)n); - p->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Item colormenu[] = { - { "Foreground Color", 0, 0, (void*)(fl_intptr_t)FL_FOREGROUND_COLOR, 0, 0, FL_HELVETICA, 11}, - { "Background Color", 0, 0, (void*)(fl_intptr_t)FL_BACKGROUND_COLOR, 0, 0, FL_HELVETICA, 11}, - { "Background Color 2", 0, 0, (void*)(fl_intptr_t)FL_BACKGROUND2_COLOR, 0, 0, FL_HELVETICA, 11}, - { "Selection Color", 0, 0, (void*)(fl_intptr_t)FL_SELECTION_COLOR, 0, 0, FL_HELVETICA, 11}, - { "Inactive Color", 0, 0, (void*)(fl_intptr_t)FL_INACTIVE_COLOR, FL_MENU_DIVIDER, 0, FL_HELVETICA, 11}, - { "Black", 0, 0, (void*)(fl_intptr_t)FL_BLACK, 0, 0, FL_HELVETICA, 11}, - { "White", 0, 0, (void*)(fl_intptr_t)FL_WHITE, FL_MENU_DIVIDER, 0, FL_HELVETICA, 11}, - { "Gray 0", 0, 0, (void*)(fl_intptr_t)FL_GRAY0, 0, 0, FL_HELVETICA, 11}, - { "Dark 3", 0, 0, (void*)(fl_intptr_t)FL_DARK3, 0, 0, FL_HELVETICA, 11}, - { "Dark 2", 0, 0, (void*)(fl_intptr_t)FL_DARK2, 0, 0, FL_HELVETICA, 11}, - { "Dark 1", 0, 0, (void*)(fl_intptr_t)FL_DARK1, 0, 0, FL_HELVETICA, 11}, - { "Light 1", 0, 0, (void*)(fl_intptr_t)FL_LIGHT1, 0, 0, FL_HELVETICA, 11}, - { "Light 2", 0, 0, (void*)(fl_intptr_t)FL_LIGHT2, 0, 0, FL_HELVETICA, 11}, - { "Light 3", 0, 0, (void*)(fl_intptr_t)FL_LIGHT3, 0, 0, FL_HELVETICA, 11}, - { 0 } -}; - -void color_common(Fl_Color c) { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->color(c); q->o->redraw(); - if (q->parent && q->parent->is_a(ID_Tabs)) { - if (q->o->parent()) q->o->parent()->redraw(); - } - mod = 1; - } - } - if (mod) set_modflag(1); -} - -void color_cb(Fl_Button* i, void *v) { - Fl_Color c = current_widget->o->color(); - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - } else { - Fl_Color d = fl_show_colormap(c); - if (d == c) return; - c = d; - color_common(c); - } - i->color(c); i->labelcolor(fl_contrast(FL_BLACK,c)); i->redraw(); -} - -void color_menu_cb(Fl_Menu_Button* i, void *v) { - Fl_Color c = current_widget->o->color(); - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - } else { - Fl_Color d = (Fl_Color)(i->mvalue()->argument()); - if (d == c) return; - c = d; - color_common(c); - w_color->color(c); w_color->labelcolor(fl_contrast(FL_BLACK,c)); w_color->redraw(); - } -} - -void color2_common(Fl_Color c) { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->selection_color(c); q->o->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); -} - -void color2_cb(Fl_Button* i, void *v) { - Fl_Color c = current_widget->o->selection_color(); - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - } else { - Fl_Color d = fl_show_colormap(c); - if (d == c) return; - c = d; - color2_common(c); - } - i->color(c); i->labelcolor(fl_contrast(FL_BLACK,c)); i->redraw(); -} - -void color2_menu_cb(Fl_Menu_Button* i, void *v) { - Fl_Color c = current_widget->o->selection_color(); - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - } else { - Fl_Color d = (Fl_Color)(i->mvalue()->argument()); - if (d == c) return; - c = d; - color2_common(c); - w_selectcolor->color(c); w_selectcolor->labelcolor(fl_contrast(FL_BLACK,c)); w_selectcolor->redraw(); - } -} - -void labelcolor_common(Fl_Color c) { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->labelcolor(c); q->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); -} - -void labelcolor_cb(Fl_Button* i, void *v) { - Fl_Color c = current_widget->o->labelcolor(); - if (v != LOAD) { - Fl_Color d = fl_show_colormap(c); - if (d == c) return; - c = d; - labelcolor_common(c); - } - i->color(c); i->labelcolor(fl_contrast(FL_BLACK,c)); i->redraw(); -} - -void labelcolor_menu_cb(Fl_Menu_Button* i, void *v) { - Fl_Color c = current_widget->o->labelcolor(); - if (v != LOAD) { - Fl_Color d = (Fl_Color)(i->mvalue()->argument()); - if (d == c) return; - c = d; - labelcolor_common(c); - w_labelcolor->color(c); w_labelcolor->labelcolor(fl_contrast(FL_BLACK,c)); w_labelcolor->redraw(); - } -} - -static Fl_Button* relative(Fl_Widget* o, int i) { - Fl_Group* g = (Fl_Group*)(o->parent()); - return (Fl_Button*)(g->child(g->find(*o)+i)); -} - -static Fl_Menu_Item alignmenu[] = { - {"FL_ALIGN_CENTER",0,0,(void*)(fl_intptr_t)(FL_ALIGN_CENTER)}, - {"FL_ALIGN_TOP",0,0,(void*)(fl_intptr_t)(FL_ALIGN_TOP)}, - {"FL_ALIGN_BOTTOM",0,0,(void*)(fl_intptr_t)(FL_ALIGN_BOTTOM)}, - {"FL_ALIGN_LEFT",0,0,(void*)(fl_intptr_t)(FL_ALIGN_LEFT)}, - {"FL_ALIGN_RIGHT",0,0,(void*)(fl_intptr_t)(FL_ALIGN_RIGHT)}, - {"FL_ALIGN_INSIDE",0,0,(void*)(fl_intptr_t)(FL_ALIGN_INSIDE)}, - {"FL_ALIGN_CLIP",0,0,(void*)(fl_intptr_t)(FL_ALIGN_CLIP)}, - {"FL_ALIGN_WRAP",0,0,(void*)(fl_intptr_t)(FL_ALIGN_WRAP)}, - {"FL_ALIGN_TEXT_OVER_IMAGE",0,0,(void*)(fl_intptr_t)(FL_ALIGN_TEXT_OVER_IMAGE)}, - {"FL_ALIGN_TOP_LEFT",0,0,(void*)(fl_intptr_t)(FL_ALIGN_TOP_LEFT)}, - {"FL_ALIGN_TOP_RIGHT",0,0,(void*)(fl_intptr_t)(FL_ALIGN_TOP_RIGHT)}, - {"FL_ALIGN_BOTTOM_LEFT",0,0,(void*)(fl_intptr_t)(FL_ALIGN_BOTTOM_LEFT)}, - {"FL_ALIGN_BOTTOM_RIGHT",0,0,(void*)(fl_intptr_t)(FL_ALIGN_BOTTOM_RIGHT)}, - {"FL_ALIGN_LEFT_TOP",0,0,(void*)(fl_intptr_t)(FL_ALIGN_LEFT_TOP)}, - {"FL_ALIGN_RIGHT_TOP",0,0,(void*)(fl_intptr_t)(FL_ALIGN_RIGHT_TOP)}, - {"FL_ALIGN_LEFT_BOTTOM",0,0,(void*)(fl_intptr_t)(FL_ALIGN_LEFT_BOTTOM)}, - {"FL_ALIGN_RIGHT_BOTTOM",0,0,(void*)(fl_intptr_t)(FL_ALIGN_RIGHT_BOTTOM)}, -{0}}; - -void align_cb(Fl_Button* i, void *v) { - Fl_Align b = Fl_Align(fl_uintptr_t(i->user_data())); - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - i->value(current_widget->o->align() & b); - } else { - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - Fl_Align x = q->o->align(); - Fl_Align y; - if (i->value()) { - y = x | b; - if (b == FL_ALIGN_LEFT || b == FL_ALIGN_TOP) { - Fl_Button *b1 = relative(i,+1); - b1->clear(); - y = y & ~(b1->argument()); - } - if (b == FL_ALIGN_RIGHT || b == FL_ALIGN_BOTTOM) { - Fl_Button *b1 = relative(i,-1); - b1->clear(); - y = y & ~(b1->argument()); - } - } else { - y = x & ~b; - } - if (x != y) { - q->o->align(y); - q->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void align_position_cb(Fl_Choice *i, void *v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - Fl_Menu_Item *mi = (Fl_Menu_Item*)i->menu(); - Fl_Align b = current_widget->o->align() & FL_ALIGN_POSITION_MASK; - for (;mi->text;mi++) { - if ((Fl_Align)(mi->argument())==b) - i->value(mi); - } - } else { - const Fl_Menu_Item *mi = i->menu() + i->value(); - Fl_Align b = Fl_Align(fl_uintptr_t(mi->user_data())); - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - Fl_Align x = q->o->align(); - Fl_Align y = (x & ~FL_ALIGN_POSITION_MASK) | b; - if (x != y) { - q->o->align(y); - q->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void align_text_image_cb(Fl_Choice *i, void *v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - Fl_Menu_Item *mi = (Fl_Menu_Item*)i->menu(); - Fl_Align b = current_widget->o->align() & FL_ALIGN_IMAGE_MASK; - for (;mi->text;mi++) { - if ((Fl_Align)(mi->argument())==b) - i->value(mi); - } - } else { - const Fl_Menu_Item *mi = i->menu() + i->value(); - Fl_Align b = Fl_Align(fl_uintptr_t(mi->user_data())); - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - Fl_Align x = q->o->align(); - Fl_Align y = (x & ~FL_ALIGN_IMAGE_MASK) | b; - if (x != y) { - q->o->align(y); - q->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -void callback_cb(fld::widget::Code_Editor* i, void *v) { - if (v == LOAD) { - const char *cbtext = current_widget->callback(); - i->buffer()->text( cbtext ? cbtext : "" ); - } else { - int mod = 0; - char *c = i->buffer()->text(); - const char *d = c_check(c); - if (d) { - fl_message("Error in callback: %s",d); - if (i->window()) i->window()->make_current(); - haderror = 1; - } - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected) { - o->callback(c); - mod = 1; - } - } - if (mod) set_modflag(1); - free(c); - } -} - -void comment_cb(Fl_Text_Editor* i, void *v) { - if (v == LOAD) { - const char *cmttext = current_widget->comment(); - i->buffer()->text( cmttext ? cmttext : "" ); - } else { - int mod = 0; - char *c = i->buffer()->text(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected) { - o->comment(c); - mod = 1; - } - } - if (mod) set_modflag(1); - free(c); - } -} - -void user_data_cb(Fl_Input *i, void *v) { - if (v == LOAD) { - i->value(current_widget->user_data()); - } else { - int mod = 0; - const char *c = i->value(); - const char *d = c_check(c); - if (d) {fl_message("Error in user_data: %s",d); haderror = 1; return;} - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected) { - o->user_data(c); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void user_data_type_cb(Fl_Input_Choice *i, void *v) { - static const char *dflt = "void*"; - if (v == LOAD) { - const char *c = current_widget->user_data_type(); - if (!c) c = dflt; - i->value(c); - } else { - int mod = 0; - const char *c = i->value(); - const char *d = c_check(c); - if (!*c) i->value(dflt); - else if (!strcmp(c,dflt)) c = 0; - if (!d) { - if (c && *c && c[strlen(c)-1] != '*' && strcmp(c,"long")) - d = "must be pointer or long"; - } - if (d) {fl_message("Error in type: %s",d); haderror = 1; return;} - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected) { - o->user_data_type(c); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -// "v_attributes" let user type in random code for attribute settings: - -void v_input_cb(Fl_Input* i, void* v) { - int n = fl_int(i->user_data()); - if (v == LOAD) { - i->value(current_widget->extra_code(n)); - } else { - int mod = 0; - const char *c = i->value(); - const char *d = c_check(c&&c[0]=='#' ? c+1 : c); - if (d) {fl_message("Error in %s: %s",i->label(),d); haderror = 1; return;} - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type *t = (Fl_Widget_Type*)o; - t->extra_code(n,c); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void subclass_cb(Fl_Input* i, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - i->value(current_widget->subclass()); - } else { - int mod = 0; - const char *c = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type *t = (Fl_Widget_Type*)o; - t->subclass(c); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -// textstuff: set textfont, textsize, textcolor attributes: - -// default widget returns 0 to indicate not-implemented: -// The first parameter specifies the operation: -// 0: get all values -// 1: set the text font -// 2: set the text size -// 3: set the text color -// 4: get all default values for this type -int Fl_Widget_Type::textstuff(int, Fl_Font&, int&, Fl_Color&) { - return 0; -} - -void textfont_cb(Fl_Choice* i, void* v) { - Fl_Font n; int s; Fl_Color c; - if (v == LOAD) { - if (!current_widget->textstuff(0,n,s,c)) {i->deactivate(); return;} - i->activate(); - if (n > 15) n = FL_HELVETICA; - i->value(n); - } else { - int mod = 0; - n = (Fl_Font)i->value(); - if (n <= 0) n = layout->textfont; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->textstuff(1,n,s,c); - q->o->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void textsize_cb(Fl_Value_Input* i, void* v) { - Fl_Font n; int s; Fl_Color c; - if (v == LOAD) { - if (!current_widget->textstuff(0,n,s,c)) {i->deactivate(); return;} - i->activate(); - } else { - int mod = 0; - s = int(i->value()); - if (s <= 0) s = layout->textsize; - if (s <= 0) s = layout->labelsize; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->textstuff(2,n,s,c); - q->o->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); - } - i->value(s); -} - -void textcolor_common(Fl_Color c) { - Fl_Font n; int s; - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->textstuff(3,n,s,c); q->o->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); -} - -void textcolor_cb(Fl_Button* i, void* v) { - Fl_Font n; int s; Fl_Color c; - if (v == LOAD) { - if (!current_widget->textstuff(0,n,s,c)) {i->deactivate(); return;} - i->activate(); - } else { - c = i->color(); - Fl_Color d = fl_show_colormap(c); - if (d == c) return; - c = d; - textcolor_common(c); - } - i->color(c); i->labelcolor(fl_contrast(FL_BLACK,c)); i->redraw(); -} - -void textcolor_menu_cb(Fl_Menu_Button* i, void* v) { - Fl_Font n; int s; Fl_Color c; - if (v == LOAD) { - if (!current_widget->textstuff(0,n,s,c)) {i->deactivate(); return;} - i->activate(); - } else { - c = i->color(); - Fl_Color d = (Fl_Color)(i->mvalue()->argument()); - if (d == c) return; - c = d; - textcolor_common(c); - w_textcolor->color(c); w_textcolor->labelcolor(fl_contrast(FL_BLACK,c)); w_textcolor->redraw(); - } -} - -void image_spacing_cb(Fl_Value_Input* i, void* v) { - int s; - if (v == LOAD) { - if (!current_widget->is_true_widget()) { - i->deactivate(); - i->value(0); - } else { - i->activate(); - i->value(((Fl_Widget_Type*)current_widget)->o->label_image_spacing()); - } - } else { - int mod = 0; - s = int(i->value()); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->o->label_image_spacing() != s) { - q->o->label_image_spacing(s); - if (!(q->o->align() & FL_ALIGN_INSIDE) && q->o->window()) - q->o->window()->damage(FL_DAMAGE_EXPOSE); // outside labels - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void h_label_margin_cb(Fl_Value_Input* i, void* v) { - int s; - if (v == LOAD) { - if (!current_widget->is_true_widget()) { - i->deactivate(); - i->value(0); - } else { - i->activate(); - i->value(((Fl_Widget_Type*)current_widget)->o->horizontal_label_margin()); - } - } else { - int mod = 0; - s = int(i->value()); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->o->horizontal_label_margin() != s) { - q->o->horizontal_label_margin(s); - if (!(q->o->align() & FL_ALIGN_INSIDE) && q->o->window()) - q->o->window()->damage(FL_DAMAGE_EXPOSE); // outside labels - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void v_label_margin_cb(Fl_Value_Input* i, void* v) { - int s; - if (v == LOAD) { - if (!current_widget->is_true_widget()) { - i->deactivate(); - i->value(0); - } else { - i->activate(); - i->value(((Fl_Widget_Type*)current_widget)->o->vertical_label_margin()); - } - } else { - int mod = 0; - s = int(i->value()); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->o->vertical_label_margin() != s) { - q->o->vertical_label_margin(s); - if (!(q->o->align() & FL_ALIGN_INSIDE) && q->o->window()) - q->o->window()->damage(FL_DAMAGE_EXPOSE); // outside labels - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// -// Kludges to the panel for subclasses: - -void min_w_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) return; - i->value(((Fl_Window_Type*)current_widget)->sr_min_w); - } else { - int mod = 0; - undo_checkpoint(); - int n = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - ((Fl_Window_Type*)current_widget)->sr_min_w = n; - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void min_h_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) return; - i->value(((Fl_Window_Type*)current_widget)->sr_min_h); - } else { - int mod = 0; - undo_checkpoint(); - int n = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - ((Fl_Window_Type*)current_widget)->sr_min_h = n; - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void max_w_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) return; - i->value(((Fl_Window_Type*)current_widget)->sr_max_w); - } else { - int mod = 0; - undo_checkpoint(); - int n = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - ((Fl_Window_Type*)current_widget)->sr_max_w = n; - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void max_h_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) return; - i->value(((Fl_Window_Type*)current_widget)->sr_max_h); - } else { - int mod = 0; - undo_checkpoint(); - int n = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - ((Fl_Window_Type*)current_widget)->sr_max_h = n; - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void set_min_size_cb(Fl_Button*, void* v) { - if (v == LOAD) { - } else { - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - Fl_Window_Type *win = (Fl_Window_Type*)current_widget; - win->sr_min_w = win->o->w(); - win->sr_min_h = win->o->h(); - mod = 1; - } - } - propagate_load(the_panel, LOAD); - if (mod) set_modflag(1); - } -} - -void set_max_size_cb(Fl_Button*, void* v) { - if (v == LOAD) { - } else { - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - Fl_Window_Type *win = (Fl_Window_Type*)current_widget; - win->sr_max_w = win->o->w(); - win->sr_max_h = win->o->h(); - mod = 1; - } - } - propagate_load(the_panel, LOAD); - if (mod) set_modflag(1); - } -} - -void slider_size_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Slider)) {i->deactivate(); return;} - i->activate(); - i->value(((Fl_Slider*)(current_widget->o))->slider_size()); - } else { - int mod = 0; - undo_checkpoint(); - double n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->is_a(ID_Slider)) { - ((Fl_Slider*)(q->o))->slider_size(n); - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void min_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Valuator_)) { - i->activate(); - i->value(((Fl_Valuator*)(current_widget->o))->minimum()); - } else if (current_widget->is_a(ID_Spinner)) { - i->activate(); - i->value(((Fl_Spinner*)(current_widget->o))->minimum()); - } else { - i->deactivate(); - return; - } - } else { - int mod = 0; - undo_checkpoint(); - double n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->is_a(ID_Valuator_)) { - ((Fl_Valuator*)(q->o))->minimum(n); - q->o->redraw(); - mod = 1; - } else if (q->is_a(ID_Spinner)) { - ((Fl_Spinner*)(q->o))->minimum(n); - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void max_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Valuator_)) { - i->activate(); - i->value(((Fl_Valuator*)(current_widget->o))->maximum()); - } else if (current_widget->is_a(ID_Spinner)) { - i->activate(); - i->value(((Fl_Spinner*)(current_widget->o))->maximum()); - } else { - i->deactivate(); - return; - } - } else { - int mod = 0; - undo_checkpoint(); - double n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->is_a(ID_Valuator_)) { - ((Fl_Valuator*)(q->o))->maximum(n); - q->o->redraw(); - mod = 1; - } else if (q->is_a(ID_Spinner)) { - ((Fl_Spinner*)(q->o))->maximum(n); - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void step_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Valuator_)) { - i->activate(); - i->value(((Fl_Valuator*)(current_widget->o))->step()); - } else if (current_widget->is_a(ID_Spinner)) { - i->activate(); - i->value(((Fl_Spinner*)(current_widget->o))->step()); - } else { - i->deactivate(); - return; - } - } else { - int mod = 0; - undo_checkpoint(); - double n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->is_a(ID_Valuator_)) { - ((Fl_Valuator*)(q->o))->step(n); - q->o->redraw(); - mod = 1; - } else if (q->is_a(ID_Spinner)) { - ((Fl_Spinner*)(q->o))->step(n); - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void value_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Valuator_)) { - i->activate(); - i->value(((Fl_Valuator*)(current_widget->o))->value()); - } else if (current_widget->is_button()) { - i->activate(); - i->value(((Fl_Button*)(current_widget->o))->value()); - } else if (current_widget->is_a(ID_Spinner)) { - i->activate(); - i->value(((Fl_Spinner*)(current_widget->o))->value()); - } else - i->deactivate(); - } else { - int mod = 0; - undo_checkpoint(); - double n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->is_a(ID_Valuator_)) { - ((Fl_Valuator*)(q->o))->value(n); - mod = 1; - } else if (q->is_button()) { - ((Fl_Button*)(q->o))->value(n != 0); - if (q->is_a(ID_Menu_Item)) q->redraw(); - mod = 1; - } else if (q->is_a(ID_Spinner)) { - ((Fl_Spinner*)(q->o))->value(n); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -// The following three callbacks cooperate, showing only one of the groups of -// widgets that use the same space in the dialog. - -void values_group_cb(Fl_Group* g, void* v) { - if (v == LOAD) { - if ( current_widget->is_a(ID_Flex) - || current_widget->is_a(ID_Grid) - || current_widget->is_a(ID_Window)) - { - g->hide(); - } else { - g->show(); - } - propagate_load(g, v); - } -} - -void flex_margin_group_cb(Fl_Group* g, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Flex)) { - g->show(); - } else { - g->hide(); - } - propagate_load(g, v); - } -} - -void size_range_group_cb(Fl_Group* g, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Window)) { - g->show(); - } else { - g->hide(); - } - propagate_load(g, v); - } -} - - -static void flex_margin_cb(Fl_Value_Input* i, void* v, - void (*load_margin)(Fl_Flex*,Fl_Value_Input*), - int (*update_margin)(Fl_Flex*,int)) { - if (v == LOAD) { - if (current_widget->is_a(ID_Flex)) { - load_margin((Fl_Flex*)current_widget->o, i); - } - } else { - int mod = 0; - int new_value = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Flex)) { - Fl_Flex_Type* q = (Fl_Flex_Type*)o; - Fl_Flex* w = (Fl_Flex*)q->o; - if (update_margin(w, new_value)) { - w->layout(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -static void load_left_margin(Fl_Flex *w, Fl_Value_Input* i) -{ - int v; - w->margin(&v, NULL, NULL, NULL); - i->value((double)v); -} - -static int update_left_margin(Fl_Flex *w, int new_value) -{ - int l, t, r, b; - w->margin(&l, &t, &r, &b); - if (new_value!=l) { - w->margin(new_value, t, r, b); - return 1; - } else { - return 0; - } -} - -void flex_margin_left_cb(Fl_Value_Input* i, void* v) { - flex_margin_cb(i, v, load_left_margin, update_left_margin); -} - -static void load_top_margin(Fl_Flex *w, Fl_Value_Input* i) -{ - int v; - w->margin(NULL, &v, NULL, NULL); - i->value((double)v); -} - -static int update_top_margin(Fl_Flex *w, int new_value) -{ - int l, t, r, b; - w->margin(&l, &t, &r, &b); - if (new_value!=t) { - w->margin(l, new_value, r, b); - return 1; - } else { - return 0; - } -} - -void flex_margin_top_cb(Fl_Value_Input* i, void* v) { - flex_margin_cb(i, v, load_top_margin, update_top_margin); -} - -static void load_right_margin(Fl_Flex *w, Fl_Value_Input* i) -{ - int v; - w->margin(NULL, NULL, &v, NULL); - i->value((double)v); -} - -static int update_right_margin(Fl_Flex *w, int new_value) -{ - int l, t, r, b; - w->margin(&l, &t, &r, &b); - if (new_value!=r) { - w->margin(l, t, new_value, b); - return 1; - } else { - return 0; - } -} - -void flex_margin_right_cb(Fl_Value_Input* i, void* v) { - flex_margin_cb(i, v, load_right_margin, update_right_margin); -} - -static void load_bottom_margin(Fl_Flex *w, Fl_Value_Input* i) -{ - int v; - w->margin(NULL, NULL, NULL, &v); - i->value((double)v); -} - -static int update_bottom_margin(Fl_Flex *w, int new_value) -{ - int l, t, r, b; - w->margin(&l, &t, &r, &b); - if (new_value!=b) { - w->margin(l, t, r, new_value); - return 1; - } else { - return 0; - } -} - -void flex_margin_bottom_cb(Fl_Value_Input* i, void* v) { - flex_margin_cb(i, v, load_bottom_margin, update_bottom_margin); -} - -static void load_gap(Fl_Flex *w, Fl_Value_Input* i) -{ - int v = w->gap(); - i->value((double)v); -} - -static int update_gap(Fl_Flex *w, int new_value) -{ - int g = w->gap(); - if (new_value!=g) { - w->gap(new_value); - return 1; - } else { - return 0; - } -} - -void flex_margin_gap_cb(Fl_Value_Input* i, void* v) { - flex_margin_cb(i, v, load_gap, update_gap); -} - -void position_group_cb(Fl_Group* g, void* v) { - if (v == LOAD) { - if (Fl_Flex_Type::parent_is_flex(current_widget)) { - g->hide(); - } else { - g->show(); - } - } - propagate_load(g, v); -} - -void flex_size_group_cb(Fl_Group* g, void* v) { - if (v == LOAD) { - if (Fl_Flex_Type::parent_is_flex(current_widget)) { - g->show(); - } else { - g->hide(); - } - } - propagate_load(g, v); -} - -void flex_size_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (Fl_Flex_Type::parent_is_flex(current_widget)) { - i->value(Fl_Flex_Type::size(current_widget)); - } - } else { - int mod = 0; - int new_size = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget() && Fl_Flex_Type::parent_is_flex(o)) { - Fl_Widget* w = (Fl_Widget*)((Fl_Widget_Type*)o)->o; - Fl_Flex* f = (Fl_Flex*)((Fl_Flex_Type*)o->parent)->o; - int was_fixed = f->fixed(w); - if (new_size==0) { - if (was_fixed) { - f->fixed(w, 0); - f->layout(); - widget_flex_fixed->value(0); - mod = 1; - } - } else { - int old_size = Fl_Flex_Type::size(o); - if (old_size!=new_size || !was_fixed) { - f->fixed(w, new_size); - f->layout(); - widget_flex_fixed->value(1); - mod = 1; - } - } - } - } - if (mod) set_modflag(1); - } -} - -void flex_fixed_cb(Fl_Check_Button* i, void* v) { - if (v == LOAD) { - if (Fl_Flex_Type::parent_is_flex(current_widget)) { - i->value(Fl_Flex_Type::is_fixed(current_widget)); - } - } else { - int mod = 0; - int new_fixed = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget() && Fl_Flex_Type::parent_is_flex(o)) { - Fl_Widget* w = (Fl_Widget*)((Fl_Widget_Type*)o)->o; - Fl_Flex* f = (Fl_Flex*)((Fl_Flex_Type*)o->parent)->o; - int was_fixed = f->fixed(w); - if (new_fixed==0) { - if (was_fixed) { - f->fixed(w, 0); - f->layout(); - mod = 1; - } - } else { - if (!was_fixed) { - f->fixed(w, Fl_Flex_Type::size(o)); - f->layout(); - mod = 1; - } - } - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -// subtypes: - -Fl_Menu_Item *Fl_Widget_Type::subtypes() {return 0;} - -void subtype_cb(Fl_Choice* i, void* v) { - static Fl_Menu_Item empty_type_menu[] = { - {"Normal",0,0,(void*)0}, - {0}}; - - if (v == LOAD) { - Fl_Menu_Item* m = current_widget->subtypes(); - if (!m) { - i->menu(empty_type_menu); - i->value(0); - i->deactivate(); - } else { - i->menu(m); - int j; - for (j = 0;; j++) { - if (!m[j].text) {j = 0; break;} - if (current_widget->is_a(ID_Spinner)) { - if (m[j].argument() == ((Fl_Spinner*)current_widget->o)->type()) break; - } else { - if (m[j].argument() == current_widget->o->type()) break; - } - } - i->value(j); - i->activate(); - } - i->redraw(); - } else { - int mod = 0; - int n = int(i->mvalue()->argument()); - Fl_Menu_Item* m = current_widget->subtypes(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->subtypes()==m) { - if (q->is_a(ID_Spinner)) - ((Fl_Spinner*)q->o)->type(n); - else if (q->is_a(ID_Flex)) - ((Fl_Flex_Type*)q)->change_subtype_to(n); - else - q->o->type(n); - q->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -void propagate_load(Fl_Group* g, void* v) { - if (v == LOAD) { - Fl_Widget*const* a = g->array(); - for (int i=g->children(); i--;) { - Fl_Widget* o = *a++; - o->do_callback(o, LOAD, FL_REASON_USER); - } - } -} - -void set_cb(Fl_Button*, void*) { - haderror = 0; - Fl_Widget*const* a = the_panel->array(); - for (int i=the_panel->children(); i--;) { - Fl_Widget* o = *a++; - if (o->changed()) { - o->do_callback(); - if (haderror) return; - o->clear_changed(); - } - } -} - -void ok_cb(Fl_Return_Button* o, void* v) { - set_cb(o,v); - if (!haderror) the_panel->hide(); -} - -void toggle_overlays(Fl_Widget *,void *); // in Fl_Window_Type.cxx -void overlay_cb(Fl_Button*o,void *v) { - toggle_overlays(o,v); -} - -void leave_live_mode_cb(Fl_Widget*, void*); - -void live_mode_cb(Fl_Button*o,void *) { - /// \todo live mode should end gracefully when the application quits - /// or when the user closes the live widget - static Fl_Type *live_type = 0L; - static Fl_Widget *live_widget = 0L; - static Fl_Window *live_window = 0L; - // if 'o' is 0, we must quit live mode - if (!o) { - o = wLiveMode; - o->value(0); - } - if (o->value()) { - if (numselected == 1) { - Fl_Group::current(0L); - live_widget = current_widget->enter_live_mode(1); - if (live_widget) { - live_type = current_widget; - Fl_Group::current(0); - int w = live_widget->w(); - int h = live_widget->h(); - live_window = new Fl_Double_Window(w+20, h+55, "Fluid Live Resize"); - live_window->box(FL_FLAT_BOX); - live_window->color(FL_GREEN); - Fl_Group *rsz = new Fl_Group(0, h+20, 130, 35); - rsz->box(FL_NO_BOX); - Fl_Box *rsz_dummy = new Fl_Box(110, h+20, 1, 25); - rsz_dummy->box(FL_NO_BOX); - rsz->resizable(rsz_dummy); - Fl_Button *btn = new Fl_Button(10, h+20, 100, 25, "Exit Live Resize"); - btn->labelsize(12); - btn->callback(leave_live_mode_cb); - rsz->end(); - live_window->add(live_widget); - live_widget->position(10, 10); - live_window->resizable(live_widget); - live_window->set_modal(); // block all other UI - live_window->callback(leave_live_mode_cb); - if (current_widget->is_a(ID_Window)) { - Fl_Window_Type *w = (Fl_Window_Type*)current_widget; - int mw = w->sr_min_w; if (mw>0) mw += 20; - int mh = w->sr_min_h; if (mh>0) mh += 55; - int MW = w->sr_max_w; if (MW>0) MW += 20; - int MH = w->sr_max_h; if (MH>2) MH += 55; - if (mw || mh || MW || MH) - live_window->size_range(mw, mh, MW, MH); - } - live_window->show(); - live_widget->show(); - } else o->value(0); - } else o->value(0); - } else { - if (live_type) - live_type->leave_live_mode(); - if (live_window) { - live_window->hide(); - Fl::delete_widget(live_window); - } - live_type = 0L; - live_widget = 0L; - live_window = 0L; - } -} - -// update the panel according to current widget set: -void load_panel() { - if (!the_panel) return; - - // find all the Fl_Widget subclasses currently selected: - numselected = 0; - current_widget = 0; - if (Fl_Type::current) { - if (Fl_Type::current->is_widget()) - current_widget=(Fl_Widget_Type*)Fl_Type::current; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->is_widget() && o->selected) { - numselected++; - if (!current_widget) current_widget = (Fl_Widget_Type*)o; - } - } - } - if (current_widget && current_widget->is_a(ID_Grid)) { - if (widget_tab_grid->parent()!=widget_tabs) - widget_tabs->add(widget_tab_grid); - } else { - if (widget_tab_grid->parent()==widget_tabs) { - widget_tabs_repo->add(widget_tab_grid); - } - } - if (current_widget && current_widget->parent && current_widget->parent->is_a(ID_Grid)) { - if (widget_tab_grid_child->parent()!=widget_tabs) - widget_tabs->add(widget_tab_grid_child); - } else { - if (widget_tab_grid_child->parent()==widget_tabs) { - widget_tabs_repo->add(widget_tab_grid_child); - } - } - if (numselected) - propagate_load(the_panel, LOAD); - else - the_panel->hide(); -} - -extern Fl_Window *widgetbin_panel; - -// This is called when user double-clicks an item, open or update the panel: -void Fl_Widget_Type::open() { - bool adjust_position = false; - if (!the_panel) { - the_panel = make_widget_panel(); - adjust_position = true; - } - load_panel(); - if (numselected) { - the_panel->show(); - if (adjust_position) { - if (widgetbin_panel && widgetbin_panel->visible()) { - if ( (the_panel->x()+the_panel->w() > widgetbin_panel->x()) - && (the_panel->x() < widgetbin_panel->x()+widgetbin_panel->w()) - && (the_panel->y()+the_panel->h() > widgetbin_panel->y()) - && (the_panel->y() < widgetbin_panel->y()+widgetbin_panel->h()) ) - { - if (widgetbin_panel->y()+widgetbin_panel->h()+the_panel->h() > Fl::h()) - the_panel->position(the_panel->x(), widgetbin_panel->y()-the_panel->h()-30); - else - the_panel->position(the_panel->x(), widgetbin_panel->y()+widgetbin_panel->h()+30); - } - } - } - } -} - -extern void redraw_overlays(); -extern void check_redraw_corresponding_parent(Fl_Type*); -extern void redraw_browser(); -extern void update_codeview_position(); - -// Called when ui changes what objects are selected: -// p is selected object, null for all deletions (we must throw away -// old panel in that case, as the object may no longer exist) -void selection_changed(Fl_Type *p) { - // store all changes to the current selected objects: - if (p && the_panel && the_panel->visible()) { - set_cb(0,0); - // if there was an error, we try to leave the selected set unchanged: - if (haderror) { - Fl_Type *q = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - o->new_selected = o->selected; - if (!q && o->selected) q = o; - } - if (!p || !p->selected) p = q; - Fl_Type::current = p; - redraw_browser(); - return; - } - } - // update the selected flags to new set: - Fl_Type *q = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - o->selected = o->new_selected; - if (!q && o->selected) q = o; - } - if (!p || !p->selected) p = q; - Fl_Type::current = p; - check_redraw_corresponding_parent(p); - redraw_overlays(); - // load the panel with the new settings: - load_panel(); - // update the code viewer to show the code for the selected object - update_codeview_position(); -} - -//////////////////////////////////////////////////////////////// -// Writing the C code: - -// test to see if user named a function, or typed in code: -int is_name(const char *c) { - for (; *c; c++) - if ((ispunct(*c)||*c=='\n') && *c!='_' && *c!=':') return 0; - return 1; -} - -// Test to see if name() is an array entry. If so, and this is the -// highest number, return name[num+1]. Return null if not the highest -// number or a field or function. Return name() if not an array entry. -const char *array_name(Fl_Widget_Type *o) { - const char *c = o->name(); - if (!c) return 0; - const char *d; - for (d = c; *d != '['; d++) { - if (!*d) return c; - if (ispunct(*d) && *d!='_') return 0; - } - int num = atoi(d+1); - int sawthis = 0; - Fl_Type *t = o->prev; - Fl_Type *tp = o; - const char *cn = o->class_name(1); - for (; t && t->class_name(1) == cn; tp = t, t = t->prev) {/*empty*/} - for (t = tp; t && t->class_name(1) == cn; t = t->next) { - if (t == o) {sawthis=1; continue;} - const char *e = t->name(); - if (!e) continue; - if (strncmp(c,e,d-c)) continue; - int n1 = atoi(e+(d-c)+1); - if (n1 > num || (n1==num && sawthis)) return 0; - } - static char buffer[128]; - // MRS: we want strncpy() here... - strncpy(buffer,c,d-c+1); - snprintf(buffer+(d-c+1),sizeof(buffer) - (d-c+1), "%d]",num+1); - return buffer; -} - -// Test to see if extra code is a declaration: -int isdeclare(const char *c) { - while (isspace(*c)) c++; - if (*c == '#') return 1; - if (!strncmp(c,"extern",6)) return 1; - if (!strncmp(c,"typedef",7)) return 1; - if (!strncmp(c,"using",5)) return 1; - return 0; -} - -void Fl_Widget_Type::write_static(fld::io::Code_Writer& f) { - const char* t = subclassname(this); - if (!subclass() || (is_class() && !strncmp(t, "Fl_", 3))) { - f.write_h_once("#include "); - f.write_h_once("#include ", t); - } - for (int n=0; n < NUM_EXTRA_CODE; n++) { - if (extra_code(n) && isdeclare(extra_code(n))) - f.write_h_once("%s", extra_code(n)); - } - if (callback() && is_name(callback())) { - int write_extern_declaration = 1; - char buf[1024]; snprintf(buf, 1023, "%s(*)", callback()); - if (is_in_class()) { - if (has_function("static void", buf)) - write_extern_declaration = 0; - } else { - if (has_toplevel_function(0L, buf)) - write_extern_declaration = 0; - } - if (write_extern_declaration) - f.write_h_once("extern void %s(%s*, %s);", callback(), t, - user_data_type() ? user_data_type() : "void*"); - } - const char* k = class_name(1); - const char* c = array_name(this); - if (c && !k && !is_class()) { - f.write_c("\n"); - if (!public_) f.write_c("static "); - else f.write_h("extern %s *%s;\n", t, c); - if (strchr(c, '[') == NULL) f.write_c("%s *%s=(%s *)0;\n", t, c, t); - else f.write_c("%s *%s={(%s *)0};\n", t, c, t); - } - if (callback() && !is_name(callback())) { - // see if 'o' or 'v' used, to prevent unused argument warnings: - int use_o = 0; - int use_v = 0; - const char *d; - for (d = callback(); *d;) { - if (*d == 'o' && !is_id(d[1])) use_o = 1; - if (*d == 'v' && !is_id(d[1])) use_v = 1; - do d++; while (is_id(*d)); - while (*d && !is_id(*d)) d++; - } - const char* cn = callback_name(f); - if (k) { - f.write_c("\nvoid %s::%s_i(%s*", k, cn, t); - } else { - f.write_c("\nstatic void %s(%s*", cn, t); - } - if (use_o) f.write_c(" o"); - const char* ut = user_data_type() ? user_data_type() : "void*"; - f.write_c(", %s", ut); - if (use_v) f.write_c(" v"); - f.write_c(") {\n"); - // Matt: disabled f.tag(FD_TAG_GENERIC, 0); - f.write_c_indented(callback(), 1, 0); - if (*(d-1) != ';' && *(d-1) != '}') { - const char *p = strrchr(callback(), '\n'); - if (p) p ++; - else p = callback(); - // Only add trailing semicolon if the last line is not a preprocessor - // statement... - if (*p != '#' && *p) f.write_c(";"); - } - f.write_c("\n"); - // Matt: disabled f.tag(FD_TAG_WIDGET_CALLBACK, get_uid()); - f.write_c("}\n"); - if (k) { - f.write_c("void %s::%s(%s* o, %s v) {\n", k, cn, t, ut); - f.write_c("%s((%s*)(o", f.indent(1), k); - Fl_Type *q = 0; - for (Fl_Type* p = parent; p && p->is_widget(); q = p, p = p->parent) - f.write_c("->parent()"); - if (!q || !q->is_a(ID_Widget_Class)) - f.write_c("->user_data()"); - f.write_c("))->%s_i(o,v);\n}\n", cn); - } - } - if (image) { - if (!f.c_contains(image)) - image->write_static(f, compress_image_); - } - if (inactive) { - if (!f.c_contains(inactive)) - inactive->write_static(f, compress_deimage_); - } -} - -void Fl_Widget_Type::write_code1(fld::io::Code_Writer& f) { - const char* t = subclassname(this); - const char *c = array_name(this); - if (c) { - if (class_name(1)) { - f.write_public(public_); - f.write_h("%s%s *%s;\n", f.indent(1), t, c); - } - } - if (class_name(1) && callback() && !is_name(callback())) { - const char* cn = callback_name(f); - const char* ut = user_data_type() ? user_data_type() : "void*"; - f.write_public(0); - f.write_h("%sinline void %s_i(%s*, %s);\n", f.indent(1), cn, t, ut); - f.write_h("%sstatic void %s(%s*, %s);\n", f.indent(1), cn, t, ut); - } - // figure out if local variable will be used (prevent compiler warnings): - int wused = !name() && is_a(ID_Window); - const char *ptr; - - f.varused = wused; - - if (!name() && !f.varused) { - f.varused |= can_have_children(); - - if (!f.varused) { - f.varused_test = 1; - write_widget_code(f); - f.varused_test = 0; - } - } - - if (!f.varused) { - for (int n=0; n < NUM_EXTRA_CODE; n++) - if (extra_code(n) && !isdeclare(extra_code(n))) - { - int instring = 0; - int inname = 0; - int incomment = 0; - int incppcomment = 0; - for (ptr = extra_code(n); *ptr; ptr ++) { - if (instring) { - if (*ptr == '\\') ptr++; - else if (*ptr == '\"') instring = 0; - } else if (inname && !isalnum(*ptr & 255)) { - inname = 0; - } else if (*ptr == '/' && ptr[1]=='*') { - incomment = 1; ptr++; - } else if (incomment) { - if (*ptr == '*' && ptr[1]=='/') { - incomment = 0; ptr++; - } - } else if (*ptr == '/' && ptr[1]=='/') { - incppcomment = 1; ptr++; - } else if (incppcomment) { - if (*ptr == '\n') - incppcomment = 0; - } else if (*ptr == '\"') { - instring = 1; - } else if (isalnum(*ptr & 255) || *ptr == '_') { - size_t len = strspn(ptr, "0123456789_" - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - if (!strncmp(ptr, "o", len)) { - f.varused = 1; - break; - } else { - ptr += len - 1; - } - } - } - } - } - - f.write_c("%s{ ", f.indent()); - write_comment_inline_c(f); - if (f.varused) f.write_c("%s* o = ", t); - if (name()) f.write_c("%s = ", name()); - if (is_a(ID_Window)) { - // Handle special case where user is faking a Fl_Group type as a window, - // there is no 2-argument constructor in that case: - if (!strstr(t, "Window")) - f.write_c("new %s(0, 0, %d, %d", t, o->w(), o->h()); - else - f.write_c("new %s(%d, %d", t, o->w(), o->h()); - } else if (is_a(ID_Menu_Bar) - && ((Fl_Menu_Bar_Type*)this)->is_sys_menu_bar() - && is_in_class()) { - f.write_c("(%s*)new %s(%d, %d, %d, %d", - t, ((Fl_Menu_Bar_Type*)this)->sys_menubar_proxy_name(), - o->x(), o->y(), o->w(), o->h()); - } else { - f.write_c("new %s(%d, %d, %d, %d", t, o->x(), o->y(), o->w(), o->h()); - } - if (label() && *label()) { - f.write_c(", "); - switch (g_project.i18n_type) { - case FD_I18N_NONE : /* None */ - f.write_cstring(label()); - break; - case FD_I18N_GNU : /* GNU gettext */ - f.write_c("%s(", g_project.i18n_gnu_function.c_str()); - f.write_cstring(label()); - f.write_c(")"); - break; - case FD_I18N_POSIX : /* POSIX catgets */ - f.write_c("catgets(%s,%s,%d,", - g_project.i18n_pos_file.empty() ? "_catalog" : g_project.i18n_pos_file.c_str(), - g_project.i18n_pos_set.c_str(), msgnum()); - f.write_cstring(label()); - f.write_c(")"); - break; - } - } - f.write_c(");\n"); - - f.indentation++; - - // Avoid compiler warning for unused variable. - // Also avoid quality control warnings about incorrect allocation error handling. - if (wused) f.write_c("%sw = o; (void)w;\n", f.indent()); - - write_widget_code(f); -} - -void Fl_Widget_Type::write_color(fld::io::Code_Writer& f, const char* field, Fl_Color color) { - const char* color_name = 0; - switch (color) { - case FL_FOREGROUND_COLOR: color_name = "FL_FOREGROUND_COLOR"; break; - case FL_BACKGROUND2_COLOR: color_name = "FL_BACKGROUND2_COLOR"; break; - case FL_INACTIVE_COLOR: color_name = "FL_INACTIVE_COLOR"; break; - case FL_SELECTION_COLOR: color_name = "FL_SELECTION_COLOR"; break; - case FL_GRAY0: color_name = "FL_GRAY0"; break; - case FL_DARK3: color_name = "FL_DARK3"; break; - case FL_DARK2: color_name = "FL_DARK2"; break; - case FL_DARK1: color_name = "FL_DARK1"; break; - case FL_BACKGROUND_COLOR: color_name = "FL_BACKGROUND_COLOR"; break; - case FL_LIGHT1: color_name = "FL_LIGHT1"; break; - case FL_LIGHT2: color_name = "FL_LIGHT2"; break; - case FL_LIGHT3: color_name = "FL_LIGHT3"; break; - case FL_BLACK: color_name = "FL_BLACK"; break; - case FL_RED: color_name = "FL_RED"; break; - case FL_GREEN: color_name = "FL_GREEN"; break; - case FL_YELLOW: color_name = "FL_YELLOW"; break; - case FL_BLUE: color_name = "FL_BLUE"; break; - case FL_MAGENTA: color_name = "FL_MAGENTA"; break; - case FL_CYAN: color_name = "FL_CYAN"; break; - case FL_DARK_RED: color_name = "FL_DARK_RED"; break; - case FL_DARK_GREEN: color_name = "FL_DARK_GREEN"; break; - case FL_DARK_YELLOW: color_name = "FL_DARK_YELLOW"; break; - case FL_DARK_BLUE: color_name = "FL_DARK_BLUE"; break; - case FL_DARK_MAGENTA: color_name = "FL_DARK_MAGENTA"; break; - case FL_DARK_CYAN: color_name = "FL_DARK_CYAN"; break; - case FL_WHITE: color_name = "FL_WHITE"; break; - } - const char *var = is_class() ? "this" : name() ? name() : "o"; - if (color_name) { - f.write_c("%s%s->%s(%s);\n", f.indent(), var, field, color_name); - } else { - f.write_c("%s%s->%s((Fl_Color)%d);\n", f.indent(), var, field, color); - } -} - -// this is split from write_code1(fld::io::Code_Writer& f) for Fl_Window_Type: -void Fl_Widget_Type::write_widget_code(fld::io::Code_Writer& f) { - Fl_Widget* tplate = ((Fl_Widget_Type*)factory)->o; - const char *var = is_class() ? "this" : name() ? name() : "o"; - - if (tooltip() && *tooltip()) { - f.write_c("%s%s->tooltip(",f.indent(), var); - switch (g_project.i18n_type) { - case FD_I18N_NONE : /* None */ - f.write_cstring(tooltip()); - break; - case FD_I18N_GNU : /* GNU gettext */ - f.write_c("%s(", g_project.i18n_gnu_function.c_str()); - f.write_cstring(tooltip()); - f.write_c(")"); - break; - case FD_I18N_POSIX : /* POSIX catgets */ - f.write_c("catgets(%s,%s,%d,", - g_project.i18n_pos_file.empty() ? "_catalog" : g_project.i18n_pos_file.c_str(), - g_project.i18n_pos_set.c_str(), - msgnum() + 1); - f.write_cstring(tooltip()); - f.write_c(")"); - break; - } - f.write_c(");\n"); - } - - if (is_a(ID_Spinner) && ((Fl_Spinner*)o)->type() != ((Fl_Spinner*)tplate)->type()) - f.write_c("%s%s->type(%d);\n", f.indent(), var, ((Fl_Spinner*)o)->type()); - else if (o->type() != tplate->type() && !is_a(ID_Window)) - f.write_c("%s%s->type(%d);\n", f.indent(), var, o->type()); - if (o->box() != tplate->box() || subclass()) - f.write_c("%s%s->box(FL_%s);\n", f.indent(), var, boxname(o->box())); - - // write shortcut command if needed - int shortcut = 0; - if (is_button()) shortcut = ((Fl_Button*)o)->shortcut(); - else if (is_a(ID_Input)) shortcut = ((Fl_Input_*)o)->shortcut(); - else if (is_a(ID_Value_Input)) shortcut = ((Fl_Value_Input*)o)->shortcut(); - else if (is_a(ID_Text_Display)) shortcut = ((Fl_Text_Display*)o)->shortcut(); - if (shortcut) { - int s = shortcut; - f.write_c("%s%s->shortcut(", f.indent(), var); - if (g_project.use_FL_COMMAND) { - if (s & FL_CTRL) { f.write_c("FL_CONTROL|"); s &= ~FL_CTRL; } - if (s & FL_META) { f.write_c("FL_COMMAND|"); s &= ~FL_META; } - } else { - if (s & FL_CTRL) { f.write_c("FL_CTRL|"); s &= ~FL_CTRL; } - if (s & FL_META) { f.write_c("FL_META|"); s &= ~FL_META; } - } - if (s & FL_SHIFT) { f.write_c("FL_SHIFT|"); s &= ~FL_SHIFT; } - if (s & FL_ALT) { f.write_c("FL_ALT|"); s &= ~FL_ALT; } - if ((s < 127) && isprint(s)) - f.write_c("'%c');\n", s); - else - f.write_c("0x%x);\n", s); - } - - if (is_a(ID_Button)) { - Fl_Button* b = (Fl_Button*)o; - if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var, - boxname(b->down_box())); - if (b->value()) f.write_c("%s%s->value(1);\n", f.indent(), var); - if (b->compact()) f.write_c("%s%s->compact(%d);\n", f.indent(), var, b->compact()); - } else if (is_a(ID_Input_Choice)) { - Fl_Input_Choice* b = (Fl_Input_Choice*)o; - if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var, - boxname(b->down_box())); - } else if (is_a(ID_Menu_Manager_)) { - Fl_Menu_* b = (Fl_Menu_*)o; - if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var, - boxname(b->down_box())); - } - if (o->color() != tplate->color() || subclass()) - write_color(f, "color", o->color()); - if (o->selection_color() != tplate->selection_color() || subclass()) - write_color(f, "selection_color", o->selection_color()); - if (image) { - image->write_code(f, bind_image_, var); - if (scale_image_w_ || scale_image_h_) { - f.write_c("%s%s->image()->scale(", f.indent(), var); - if (scale_image_w_>0) - f.write_c("%d, ", scale_image_w_); - else - f.write_c("%s->image()->data_w(), ", var); - if (scale_image_h_>0) - f.write_c("%d, 0, 1);\n", scale_image_h_); - else - f.write_c("%s->image()->data_h(), 0, 1);\n", var); - } - } - if (inactive) { - inactive->write_code(f, bind_deimage_, var, 1); - if (scale_deimage_w_ || scale_deimage_h_) { - f.write_c("%s%s->deimage()->scale(", f.indent(), var); - if (scale_deimage_w_>0) - f.write_c("%d, ", scale_deimage_w_); - else - f.write_c("%s->deimage()->data_w(), ", var); - if (scale_deimage_h_>0) - f.write_c("%d, 0, 1);\n", scale_deimage_h_); - else - f.write_c("%s->deimage()->data_h(), 0, 1);\n", var); - } - } - if (o->labeltype() != tplate->labeltype() || subclass()) - f.write_c("%s%s->labeltype(FL_%s);\n", f.indent(), var, - item_name(labeltypemenu, o->labeltype())); - if (o->labelfont() != tplate->labelfont() || subclass()) - f.write_c("%s%s->labelfont(%d);\n", f.indent(), var, o->labelfont()); - if (o->labelsize() != tplate->labelsize() || subclass()) - f.write_c("%s%s->labelsize(%d);\n", f.indent(), var, o->labelsize()); - if (o->labelcolor() != tplate->labelcolor() || subclass()) - write_color(f, "labelcolor", o->labelcolor()); - if (o->horizontal_label_margin() != tplate->horizontal_label_margin()) - f.write_c("%s%s->horizontal_label_margin(%d);\n", f.indent(), var, o->horizontal_label_margin()); - if (o->vertical_label_margin() != tplate->vertical_label_margin()) - f.write_c("%s%s->vertical_label_margin(%d);\n", f.indent(), var, o->vertical_label_margin()); - if (o->label_image_spacing() != tplate->label_image_spacing()) - f.write_c("%s%s->label_image_spacing(%d);\n", f.indent(), var, o->label_image_spacing()); - if (is_a(ID_Valuator_)) { - Fl_Valuator* v = (Fl_Valuator*)o; - Fl_Valuator* t = (Fl_Valuator*)(tplate); - if (v->minimum()!=t->minimum()) - f.write_c("%s%s->minimum(%g);\n", f.indent(), var, v->minimum()); - if (v->maximum()!=t->maximum()) - f.write_c("%s%s->maximum(%g);\n", f.indent(), var, v->maximum()); - if (v->step()!=t->step()) - f.write_c("%s%s->step(%g);\n", f.indent(), var, v->step()); - if (v->value()) { - if (is_a(ID_Scrollbar)) { // Fl_Scrollbar::value(double) is not available - f.write_c("%s%s->Fl_Slider::value(%g);\n", f.indent(), var, v->value()); - } else { - f.write_c("%s%s->value(%g);\n", f.indent(), var, v->value()); - } - } - if (is_a(ID_Slider)) { - double x = ((Fl_Slider*)v)->slider_size(); - double y = ((Fl_Slider*)t)->slider_size(); - if (x != y) f.write_c("%s%s->slider_size(%g);\n", f.indent(), var, x); - } - } - if (is_a(ID_Spinner)) { - Fl_Spinner* v = (Fl_Spinner*)o; - Fl_Spinner* t = (Fl_Spinner*)(tplate); - if (v->minimum()!=t->minimum()) - f.write_c("%s%s->minimum(%g);\n", f.indent(), var, v->minimum()); - if (v->maximum()!=t->maximum()) - f.write_c("%s%s->maximum(%g);\n", f.indent(), var, v->maximum()); - if (v->step()!=t->step()) - f.write_c("%s%s->step(%g);\n", f.indent(), var, v->step()); - if (v->value()!=1.0f) - f.write_c("%s%s->value(%g);\n", f.indent(), var, v->value()); - } - - {Fl_Font ff; int fs; Fl_Color fc; if (textstuff(4,ff,fs,fc)) { - Fl_Font g; int s; Fl_Color c; textstuff(0,g,s,c); - if (g != ff) f.write_c("%s%s->textfont(%d);\n", f.indent(), var, g); - if (s != fs) f.write_c("%s%s->textsize(%d);\n", f.indent(), var, s); - if (c != fc) write_color(f, "textcolor", c); - }} - const char* ud = user_data(); - if (class_name(1) && !parent->is_widget()) ud = "this"; - if (callback()) { - f.write_c("%s%s->callback((Fl_Callback*)%s", f.indent(), var, callback_name(f)); - if (ud) - f.write_c(", (void*)(%s));\n", ud); - else - f.write_c(");\n"); - } else if (ud) { - f.write_c("%s%s->user_data((void*)(%s));\n", f.indent(), var, ud); - } - if (o->align() != tplate->align() || subclass()) { - int i = o->align(); - f.write_c("%s%s->align(Fl_Align(%s", f.indent(), var, - item_name(alignmenu, i & ~FL_ALIGN_INSIDE)); - if (i & FL_ALIGN_INSIDE) f.write_c("|FL_ALIGN_INSIDE"); - f.write_c("));\n"); - } - Fl_When ww = o->when(); - if (ww != tplate->when() || subclass()) - f.write_c("%s%s->when(%s);\n", f.indent(), var, when_symbol_name(ww)); - if (!o->visible() && o->parent()) - f.write_c("%s%s->hide();\n", f.indent(), var); - if (!o->active()) - f.write_c("%s%s->deactivate();\n", f.indent(), var); - if (!is_a(ID_Group) && resizable()) - f.write_c("%sFl_Group::current()->resizable(%s);\n", f.indent(), var); - if (hotspot()) { - if (is_class()) - f.write_c("%shotspot(%s);\n", f.indent(), var); - else if (is_a(ID_Window)) - f.write_c("%s%s->hotspot(%s);\n", f.indent(), var, var); - else - f.write_c("%s%s->window()->hotspot(%s);\n", f.indent(), var, var); - } -} - -void Fl_Widget_Type::write_extra_code(fld::io::Code_Writer& f) { - for (int n=0; n < NUM_EXTRA_CODE; n++) - if (extra_code(n) && !isdeclare(extra_code(n))) - f.write_c("%s%s\n", f.indent(), extra_code(n)); -} - -void Fl_Widget_Type::write_block_close(fld::io::Code_Writer& f) { - f.indentation--; - f.write_c("%s} // %s* %s\n", f.indent(), subclassname(this), - name() ? name() : "o"); -} - -void Fl_Widget_Type::write_code2(fld::io::Code_Writer& f) { - write_extra_code(f); - write_block_close(f); -} - -//////////////////////////////////////////////////////////////// - -void Fl_Widget_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - f.write_indent(level+1); - switch (public_) { - case 0: f.write_string("private"); break; - case 1: break; - case 2: f.write_string("protected"); break; - } - if (tooltip() && *tooltip()) { - f.write_string("tooltip"); - f.write_word(tooltip()); - } - if (image_name() && *image_name()) { - if (scale_image_w_ || scale_image_h_) - f.write_string("scale_image {%d %d}", scale_image_w_, scale_image_h_); - f.write_string("image"); - f.write_word(image_name()); - f.write_string("compress_image %d", compress_image_); - } - if (bind_image_) f.write_string("bind_image 1"); - if (inactive_name() && *inactive_name()) { - if (scale_deimage_w_ || scale_deimage_h_) - f.write_string("scale_deimage {%d %d}", scale_deimage_w_, scale_deimage_h_); - f.write_string("deimage"); - f.write_word(inactive_name()); - f.write_string("compress_deimage %d", compress_deimage_); - } - if (bind_deimage_) f.write_string("bind_deimage 1"); - f.write_string("xywh {%d %d %d %d}", o->x(), o->y(), o->w(), o->h()); - Fl_Widget* tplate = ((Fl_Widget_Type*)factory)->o; - if (is_a(ID_Spinner) && ((Fl_Spinner*)o)->type() != ((Fl_Spinner*)tplate)->type()) { - f.write_string("type"); - f.write_word(item_name(subtypes(), ((Fl_Spinner*)o)->type())); - } else if (subtypes() && (o->type() != tplate->type() || is_a(ID_Window))) { - f.write_string("type"); - f.write_word(item_name(subtypes(), o->type())); - } - if (o->box() != tplate->box()) { - f.write_string("box"); f.write_word(boxname(o->box()));} - if (is_a(ID_Input)) { - Fl_Input_* b = (Fl_Input_*)o; - if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); - } - if (is_a(ID_Value_Input)) { - Fl_Value_Input* b = (Fl_Value_Input*)o; - if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); - } - if (is_a(ID_Text_Display)) { - Fl_Text_Display* b = (Fl_Text_Display*)o; - if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); - } - if (is_a(ID_Button)) { - Fl_Button* b = (Fl_Button*)o; - if (b->down_box()) { - f.write_string("down_box"); f.write_word(boxname(b->down_box()));} - if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); - if (b->value()) f.write_string("value 1"); - } else if (is_a(ID_Input_Choice)) { - Fl_Input_Choice* b = (Fl_Input_Choice*)o; - if (b->down_box()) { - f.write_string("down_box"); f.write_word(boxname(b->down_box()));} - } else if (is_a(ID_Menu_)) { - Fl_Menu_* b = (Fl_Menu_*)o; - if (b->down_box()) { - f.write_string("down_box"); f.write_word(boxname(b->down_box()));} - } - if (o->color()!=tplate->color()) - f.write_string("color %d", o->color()); - if (o->selection_color()!=tplate->selection_color()) - f.write_string("selection_color %d", o->selection_color()); - if (o->labeltype()!=tplate->labeltype()) { - f.write_string("labeltype"); - f.write_word(item_name(labeltypemenu, o->labeltype())); - } - if (o->labelfont()!=tplate->labelfont()) - f.write_string("labelfont %d", o->labelfont()); - if (o->labelsize()!=tplate->labelsize()) - f.write_string("labelsize %d", o->labelsize()); - if (o->labelcolor()!=tplate->labelcolor()) - f.write_string("labelcolor %d", o->labelcolor()); - if (o->align()!=tplate->align()) - f.write_string("align %d", o->align()); - if (o->horizontal_label_margin()!=tplate->horizontal_label_margin()) - f.write_string("h_label_margin %d", o->horizontal_label_margin()); - if (o->vertical_label_margin()!=tplate->vertical_label_margin()) - f.write_string("v_label_margin %d", o->vertical_label_margin()); - if (o->label_image_spacing()!=tplate->label_image_spacing()) - f.write_string("image_spacing %d", o->label_image_spacing()); - if (o->when() != tplate->when()) - f.write_string("when %d", o->when()); - if (is_a(ID_Valuator_)) { - Fl_Valuator* v = (Fl_Valuator*)o; - Fl_Valuator* t = (Fl_Valuator*)(tplate); - if (v->minimum()!=t->minimum()) f.write_string("minimum %g",v->minimum()); - if (v->maximum()!=t->maximum()) f.write_string("maximum %g",v->maximum()); - if (v->step()!=t->step()) f.write_string("step %g",v->step()); - if (v->value()!=0.0) f.write_string("value %g",v->value()); - if (is_a(ID_Slider)) { - double x = ((Fl_Slider*)v)->slider_size(); - double y = ((Fl_Slider*)t)->slider_size(); - if (x != y) f.write_string("slider_size %g", x); - } - } - if (is_a(ID_Spinner)) { - Fl_Spinner* v = (Fl_Spinner*)o; - Fl_Spinner* t = (Fl_Spinner*)(tplate); - if (v->minimum()!=t->minimum()) f.write_string("minimum %g",v->minimum()); - if (v->maximum()!=t->maximum()) f.write_string("maximum %g",v->maximum()); - if (v->step()!=t->step()) f.write_string("step %g",v->step()); - if (v->value()!=1.0) f.write_string("value %g",v->value()); - } - {Fl_Font ff; int fs; Fl_Color fc; if (textstuff(4,ff,fs,fc)) { - Fl_Font ft; int s; Fl_Color c; textstuff(0,ft,s,c); - if (ft != ff) f.write_string("textfont %d", ft); - if (s != fs) f.write_string("textsize %d", s); - if (c != fc) f.write_string("textcolor %d", c); - }} - if (!o->visible() && !override_visible_) f.write_string("hide"); - if (!o->active()) f.write_string("deactivate"); - if (resizable()) f.write_string("resizable"); - if (hotspot()) f.write_string(is_a(ID_Menu_Item) ? "divider" : "hotspot"); - for (int n=0; n < NUM_EXTRA_CODE; n++) if (extra_code(n)) { - f.write_indent(level+1); - f.write_string("code%d",n); - f.write_word(extra_code(n)); - } - if (subclass()) { - f.write_indent(level+1); - f.write_string("class"); - f.write_word(subclass()); - } -} - -void Fl_Widget_Type::read_property(fld::io::Project_Reader &f, const char *c) { - int x,y,w,h; Fl_Font ft; int s; Fl_Color cc; - if (!strcmp(c,"private")) { - public_ = 0; - } else if (!strcmp(c,"protected")) { - public_ = 2; - } else if (!strcmp(c,"xywh")) { - if (sscanf(f.read_word(),"%d %d %d %d",&x,&y,&w,&h) == 4) { - x += pasteoffset; - y += pasteoffset; - // FIXME temporary change! - if (f.read_version>=2.0 && o->parent() && o->parent()!=o->window()) { - x += o->parent()->x(); - y += o->parent()->y(); - } - o->resize(x,y,w,h); - } - } else if (!strcmp(c,"tooltip")) { - tooltip(f.read_word()); - } else if (!strcmp(c,"scale_image")) { - if (sscanf(f.read_word(),"%d %d",&w,&h) == 2) { - scale_image_w_ = w; - scale_image_h_ = h; - } - } else if (!strcmp(c,"image")) { - image_name(f.read_word()); - // starting in 2023, `image` is always followed by `compress_image` - // the code below is for compatibility with older .fl files - const char *ext = fl_filename_ext(image_name_); - if ( strcmp(ext, ".jpg") - && strcmp(ext, ".png") - && strcmp(ext, ".svg") - && strcmp(ext, ".svgz")) - compress_image_ = 0; // if it is neither of those, default to uncompressed - } else if (!strcmp(c,"bind_image")) { - bind_image_ = (int)atol(f.read_word()); - } else if (!strcmp(c,"compress_image")) { - compress_image_ = (int)atol(f.read_word()); - } else if (!strcmp(c,"scale_deimage")) { - if (sscanf(f.read_word(),"%d %d",&w,&h) == 2) { - scale_deimage_w_ = w; - scale_deimage_h_ = h; - } - } else if (!strcmp(c,"deimage")) { - inactive_name(f.read_word()); - // starting in 2023, `deimage` is always followed by `compress_deimage` - // the code below is for compatibility with older .fl files - const char *ext = fl_filename_ext(inactive_name_); - if ( strcmp(ext, ".jpg") - && strcmp(ext, ".png") - && strcmp(ext, ".svg") - && strcmp(ext, ".svgz")) - compress_deimage_ = 0; // if it is neither of those, default to uncompressed - } else if (!strcmp(c,"bind_deimage")) { - bind_deimage_ = (int)atol(f.read_word()); - } else if (!strcmp(c,"compress_deimage")) { - compress_deimage_ = (int)atol(f.read_word()); - } else if (!strcmp(c,"type")) { - if (is_a(ID_Spinner)) - ((Fl_Spinner*)o)->type(item_number(subtypes(), f.read_word())); - else - o->type(item_number(subtypes(), f.read_word())); - } else if (!strcmp(c,"box")) { - const char* value = f.read_word(); - if ((x = boxnumber(value))) { - if (x == ZERO_ENTRY) x = 0; - o->box((Fl_Boxtype)x); - } else if (sscanf(value,"%d",&x) == 1) o->box((Fl_Boxtype)x); - } else if (is_a(ID_Button) && !strcmp(c,"down_box")) { - const char* value = f.read_word(); - if ((x = boxnumber(value))) { - if (x == ZERO_ENTRY) x = 0; - ((Fl_Button*)o)->down_box((Fl_Boxtype)x); - } - } else if (is_a(ID_Input_Choice) && !strcmp(c,"down_box")) { - const char* value = f.read_word(); - if ((x = boxnumber(value))) { - if (x == ZERO_ENTRY) x = 0; - ((Fl_Input_Choice*)o)->down_box((Fl_Boxtype)x); - } - } else if (is_a(ID_Menu_) && !strcmp(c,"down_box")) { - const char* value = f.read_word(); - if ((x = boxnumber(value))) { - if (x == ZERO_ENTRY) x = 0; - ((Fl_Menu_*)o)->down_box((Fl_Boxtype)x); - } - } else if (is_button() && !strcmp(c,"value")) { - const char* value = f.read_word(); - ((Fl_Button*)o)->value(atoi(value)); - } else if (!strcmp(c,"color")) { - const char *cw = f.read_word(); - if (cw[0]=='0' && cw[1]=='x') { - sscanf(cw,"0x%x",&x); - o->color(x); - } else { - int n = sscanf(cw,"%d %d",&x,&y); - if (n == 2) { // back compatibility... - if (x != 47) o->color(x); - o->selection_color(y); - } else { - o->color(x); - } - } - } else if (!strcmp(c,"selection_color")) { - if (sscanf(f.read_word(),"%d",&x)) o->selection_color(x); - } else if (!strcmp(c,"labeltype")) { - c = f.read_word(); - if (!strcmp(c,"image")) { - Fluid_Image *i = Fluid_Image::find(label()); - if (!i) f.read_error("Image file '%s' not found", label()); - else setimage(i); - image_name(label()); - label(""); - } else { - o->labeltype((Fl_Labeltype)item_number(labeltypemenu,c)); - } - } else if (!strcmp(c,"labelfont")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->labelfont(x); - } else if (!strcmp(c,"labelsize")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->labelsize(x); - } else if (!strcmp(c,"labelcolor")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->labelcolor(x); - } else if (!strcmp(c,"align")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->align(x); - } else if (!strcmp(c,"h_label_margin")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->horizontal_label_margin(x); - } else if (!strcmp(c,"v_label_margin")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->vertical_label_margin(x); - } else if (!strcmp(c,"image_spacing")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->label_image_spacing(x); - } else if (!strcmp(c,"when")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->when(x); - } else if (!strcmp(c,"minimum")) { - if (is_a(ID_Valuator_)) ((Fl_Valuator*)o)->minimum(strtod(f.read_word(),0)); - if (is_a(ID_Spinner)) ((Fl_Spinner*)o)->minimum(strtod(f.read_word(),0)); - } else if (!strcmp(c,"maximum")) { - if (is_a(ID_Valuator_)) ((Fl_Valuator*)o)->maximum(strtod(f.read_word(),0)); - if (is_a(ID_Spinner)) ((Fl_Spinner*)o)->maximum(strtod(f.read_word(),0)); - } else if (!strcmp(c,"step")) { - if (is_a(ID_Valuator_)) ((Fl_Valuator*)o)->step(strtod(f.read_word(),0)); - if (is_a(ID_Spinner)) ((Fl_Spinner*)o)->step(strtod(f.read_word(),0)); - } else if (!strcmp(c,"value")) { - if (is_a(ID_Valuator_)) ((Fl_Valuator*)o)->value(strtod(f.read_word(),0)); - if (is_a(ID_Spinner)) ((Fl_Spinner*)o)->value(strtod(f.read_word(),0)); - } else if ( (!strcmp(c,"slider_size") || !strcmp(c,"size")) && is_a(ID_Slider)) { - ((Fl_Slider*)o)->slider_size(strtod(f.read_word(),0)); - } else if (!strcmp(c,"textfont")) { - if (sscanf(f.read_word(),"%d",&x) == 1) {ft=(Fl_Font)x; textstuff(1,ft,s,cc);} - } else if (!strcmp(c,"textsize")) { - if (sscanf(f.read_word(),"%d",&x) == 1) {s=x; textstuff(2,ft,s,cc);} - } else if (!strcmp(c,"textcolor")) { - if (sscanf(f.read_word(),"%d",&x) == 1) {cc=(Fl_Color)x;textstuff(3,ft,s,cc);} - } else if (!strcmp(c,"hide")) { - o->hide(); - } else if (!strcmp(c,"deactivate")) { - o->deactivate(); - } else if (!strcmp(c,"resizable")) { - resizable(1); - } else if (!strcmp(c,"hotspot") || !strcmp(c, "divider")) { - hotspot(1); - } else if (!strcmp(c,"class")) { - subclass(f.read_word()); - } else if (!strcmp(c,"shortcut")) { - int shortcut = (int)strtol(f.read_word(),0,0); - if (is_button()) ((Fl_Button*)o)->shortcut(shortcut); - else if (is_a(ID_Input)) ((Fl_Input_*)o)->shortcut(shortcut); - else if (is_a(ID_Value_Input)) ((Fl_Value_Input*)o)->shortcut(shortcut); - else if (is_a(ID_Text_Display)) ((Fl_Text_Display*)o)->shortcut(shortcut); - } else { - if (!strncmp(c,"code",4)) { - int n = atoi(c+4); - if (n >= 0 && n <= NUM_EXTRA_CODE) { - extra_code(n,f.read_word()); - return; - } - } else if (!strcmp(c,"extra_code")) { - extra_code(0,f.read_word()); - return; - } - Fl_Type::read_property(f, c); - } -} - -Fl_Menu_Item boxmenu1[] = { - // these extra ones are for looking up fdesign saved strings: - {"NO_FRAME", 0,0,(void *)FL_NO_BOX}, - {"ROUNDED3D_UPBOX", 0,0,(void *)_FL_ROUND_UP_BOX}, - {"ROUNDED3D_DOWNBOX", 0,0,(void *)_FL_ROUND_DOWN_BOX}, - {"OVAL3D_UPBOX", 0,0,(void *)_FL_ROUND_UP_BOX}, - {"OVAL3D_DOWNBOX", 0,0,(void *)_FL_ROUND_DOWN_BOX}, - {"0", 0,0,(void *)ZERO_ENTRY}, - {"1", 0,0,(void *)FL_UP_BOX}, - {"2", 0,0,(void *)FL_DOWN_BOX}, - {"3", 0,0,(void *)FL_FLAT_BOX}, - {"4", 0,0,(void *)FL_BORDER_BOX}, - {"5", 0,0,(void *)FL_SHADOW_BOX}, - {"6", 0,0,(void *)FL_FRAME_BOX}, - {"7", 0,0,(void *)FL_ROUNDED_BOX}, - {"8", 0,0,(void *)FL_RFLAT_BOX}, - {"9", 0,0,(void *)FL_RSHADOW_BOX}, - {"10", 0,0,(void *)FL_UP_FRAME}, - {"11", 0,0,(void *)FL_DOWN_FRAME}, -{0}}; - -int lookup_symbol(const char *, int &, int numberok = 0); - -int Fl_Widget_Type::read_fdesign(const char* propname, const char* value) { - int v; - if (!strcmp(propname,"box")) { - float x,y,w,h; - if (sscanf(value,"%f %f %f %f",&x,&y,&w,&h) == 4) { - if (fld::io::fdesign_flip) { - Fl_Type *p; - for (p = parent; p && !p->is_a(ID_Window); p = p->parent) {/*empty*/} - if (p && p->is_widget()) y = ((Fl_Widget_Type*)p)->o->h()-(y+h); - } - x += pasteoffset; - y += pasteoffset; - o->resize(int(x),int(y),int(w),int(h)); - } - } else if (!strcmp(propname,"label")) { - label(value); - } else if (!strcmp(propname,"name")) { - this->name(value); - } else if (!strcmp(propname,"callback")) { - callback(value); user_data_type("long"); - } else if (!strcmp(propname,"argument")) { - user_data(value); - } else if (!strcmp(propname,"shortcut")) { - if (value[0]) { - char buf[128]; sprintf(buf,"o->shortcut(\"%s\");",value); - extra_code(0,buf); - } - } else if (!strcmp(propname,"style")) { - if (!strncmp(value,"FL_NORMAL",9)) return 1; - if (!lookup_symbol(value,v,1)) return 0; - o->labelfont(v); o->labeltype((Fl_Labeltype)(v>>8)); - } else if (!strcmp(propname,"size")) { - if (!lookup_symbol(value,v,1)) return 0; - o->labelsize(v); - } else if (!strcmp(propname,"type")) { - if (!strncmp(value,"NORMAL",6)) return 1; - if (lookup_symbol(value,v,1)) {o->type(v); return 1;} - if (!strcmp(value+strlen(value)-5,"FRAME")) goto TRY_BOXTYPE; - if (!strcmp(value+strlen(value)-3,"BOX")) goto TRY_BOXTYPE; - return 0; - } else if (!strcmp(propname,"lcol")) { - if (!lookup_symbol(value,v,1)) return 0; - o->labelcolor(v); - } else if (!strcmp(propname,"return")) { - if (!lookup_symbol(value,v,0)) return 0; - o->when(v|FL_WHEN_RELEASE); - } else if (!strcmp(propname,"alignment")) { - if (!lookup_symbol(value,v)) { - // convert old numeric values: - int v1 = atoi(value); if (v1 <= 0 && strcmp(value,"0")) return 0; - v = 0; - if (v1 >= 5) {v = FL_ALIGN_INSIDE; v1 -= 5;} - switch (v1) { - case 0: v += FL_ALIGN_TOP; break; - case 1: v += FL_ALIGN_BOTTOM; break; - case 2: v += FL_ALIGN_LEFT; break; - case 3: v += FL_ALIGN_RIGHT; break; - case 4: v += FL_ALIGN_CENTER; break; - default: return 0; - } - } - o->align(v); - } else if (!strcmp(propname,"resizebox")) { - resizable(1); - } else if (!strcmp(propname,"colors")) { - char* p = (char*)value; - while (*p != ' ') {if (!*p) return 0; p++;} - *p = 0; - int v1; - if (!lookup_symbol(value,v,1) || !lookup_symbol(p+1,v1,1)) { - *p=' '; return 0;} - o->color(v,v1); - } else if (!strcmp(propname,"resize")) { - return !strcmp(value,"FL_RESIZE_ALL"); - } else if (!strcmp(propname,"gravity")) { - return !strcmp(value,"FL_NoGravity FL_NoGravity"); - } else if (!strcmp(propname,"boxtype")) { - TRY_BOXTYPE: - int x = boxnumber(value); - if (!x) {x = item_number(boxmenu1, value); if (x < 0) return 0;} - if (x == ZERO_ENTRY) { - x = 0; - if (o->box() != ((Fl_Widget_Type*)factory)->o->box()) return 1; // kludge for frame - } - o->box((Fl_Boxtype)x); - } else { - return 0; - } - return 1; -} - -void leave_live_mode_cb(Fl_Widget*, void*) { - live_mode_cb(0, 0); -} - -Fl_Widget *Fl_Widget_Type::enter_live_mode(int) { - live_widget = widget(o->x(), o->y(), o->w(), o->h()); - if (live_widget) - copy_properties(); - return live_widget; -} - -Fl_Widget* Fl_Widget_Type::propagate_live_mode(Fl_Group* grp) { - live_widget = grp; - copy_properties(); - Fl_Type *n; - for (n = next; n && n->level > level; n = n->next) { - if (n->level == level+1) { - Fl_Widget* proxy_child = n->enter_live_mode(); - if (proxy_child && n->is_widget() && ((Fl_Widget_Type*)n)->resizable()) { - grp->resizable(proxy_child); - } - } - } - grp->end(); - live_widget = grp; - copy_properties_for_children(); - return live_widget; -} - - -void Fl_Widget_Type::leave_live_mode() { -} - -/** - copy all properties from the edit widget to the live widget - */ -void Fl_Widget_Type::copy_properties() { - if (!live_widget) - return; - - Fl_Font ff = 0; - int fs = 0; - Fl_Color fc = 0; - textstuff(0, ff, fs, fc); - - // copy all attributes common to all widget types - Fl_Widget *w = live_widget; - w->label(o->label()); - w->tooltip(tooltip()); - w->type(o->type()); - w->box(o->box()); - w->color(o->color()); - w->selection_color(o->selection_color()); - w->labeltype(o->labeltype()); - w->labelfont(o->labelfont()); - w->labelsize(o->labelsize()); - w->labelcolor(o->labelcolor()); - w->align(o->align()); - w->when(o->when()); - - // copy all attributes specific to widgets derived from Fl_Button - if (is_button()) { - Fl_Button* d = (Fl_Button*)live_widget, *s = (Fl_Button*)o; - d->down_box(s->down_box()); - d->shortcut(s->shortcut()); - d->value(s->value()); - } - - // copy all attributes specific to widgets derived from Fl_Input_ - if (is_a(ID_Input)) { - Fl_Input_* d = (Fl_Input_*)live_widget, *s = (Fl_Input_*)o; - d->shortcut(s->shortcut()); - d->textfont(ff); - d->textsize(fs); - d->textcolor(fc); - } - - // copy all attributes specific to widgets derived from Fl_Value_Input - if (is_a(ID_Value_Input)) { - Fl_Value_Input* d = (Fl_Value_Input*)live_widget, *s = (Fl_Value_Input*)o; - d->shortcut(s->shortcut()); - d->textfont(ff); - d->textsize(fs); - d->textcolor(fc); - } - - // copy all attributes specific to widgets derived from Fl_Text_Display - if (is_a(ID_Text_Display)) { - Fl_Text_Display* d = (Fl_Text_Display*)live_widget, *s = (Fl_Text_Display*)o; - d->shortcut(s->shortcut()); - d->textfont(ff); - d->textsize(fs); - d->textcolor(fc); - } - - // copy all attributes specific to Fl_Valuator and derived classes - if (is_a(ID_Valuator_)) { - Fl_Valuator* d = (Fl_Valuator*)live_widget, *s = (Fl_Valuator*)o; - d->minimum(s->minimum()); - d->maximum(s->maximum()); - d->step(s->step()); - d->value(s->value()); - if (is_a(ID_Slider)) { - Fl_Slider *d = (Fl_Slider*)live_widget, *s = (Fl_Slider*)o; - d->slider_size(s->slider_size()); - } - } - - // copy all attributes specific to Fl_Spinner and derived classes - if (is_a(ID_Spinner)) { - Fl_Spinner* d = (Fl_Spinner*)live_widget, *s = (Fl_Spinner*)o; - d->minimum(s->minimum()); - d->maximum(s->maximum()); - d->step(s->step()); - d->value(s->value()); - } - - if (!o->visible()) - w->hide(); - if (!o->active()) - w->deactivate(); -} - diff --git a/fluid/nodes/Fl_Widget_Type.h b/fluid/nodes/Fl_Widget_Type.h deleted file mode 100644 index 6a26921cc..000000000 --- a/fluid/nodes/Fl_Widget_Type.h +++ /dev/null @@ -1,132 +0,0 @@ -// -// Widget type header file for the Fast Light Tool Kit (FLTK). -// -// Type for creating all subclasses of Fl_Widget -// This should have the widget pointer in it, but it is still in the -// Fl_Type base class. -// -// Copyright 1998-2023 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_FL_WIDGET_TYPE_H -#define _FLUID_FL_WIDGET_TYPE_H - -#include "nodes/Fl_Type.h" - -#define NUM_EXTRA_CODE 4 - -class Fl_Widget_Type; -class Fluid_Image; - -extern void* const LOAD; -extern Fl_Widget_Type *current_widget; // one of the selected ones - -extern const char* subclassname(Fl_Type* l); -extern int is_name(const char *c); -void selection_changed(Fl_Type* new_current); -Fl_Type *sort(Fl_Type *parent); -void comment_cb(class Fl_Text_Editor* i, void *v); - -class Fl_Widget_Type : public Fl_Type -{ - typedef Fl_Type super; - - virtual Fl_Widget *widget(int,int,int,int) = 0; - virtual Fl_Widget_Type *_make() = 0; // virtual constructor - void setlabel(const char *) FL_OVERRIDE; - - const char *extra_code_[NUM_EXTRA_CODE]; - const char *subclass_; - const char *tooltip_; - const char *image_name_; - const char *inactive_name_; - uchar hotspot_; - -protected: - - /// This variable is set for visible windows in batch mode. - /// We can't open a window in batch mode, even if we want the "visible" flags - /// set, so we need a second place to store this information while also - /// disabling the output of the "hide" property by the Widget Type. - uchar override_visible_; - - void write_static(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_widget_code(fld::io::Code_Writer& f); - void write_extra_code(fld::io::Code_Writer& f); - void write_block_close(fld::io::Code_Writer& f); - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_color(fld::io::Code_Writer& f, const char*, Fl_Color); - Fl_Widget *live_widget; - -public: - Fl_Widget *o; - int public_; - int bind_image_; - int compress_image_; - int bind_deimage_; - int compress_deimage_; - int scale_image_w_, scale_image_h_; - int scale_deimage_w_, scale_deimage_h_; - - Fluid_Image *image; - void setimage(Fluid_Image *); - Fluid_Image *inactive; - void setinactive(Fluid_Image *); - - Fl_Widget_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void open() FL_OVERRIDE; - - const char *extra_code(int n) const {return extra_code_[n];} - void extra_code(int n,const char *); - const char *subclass() const {return subclass_;} - void subclass(const char *); - const char *tooltip() const {return tooltip_;} - void tooltip(const char *); - const char *image_name() const {return image_name_;} - void image_name(const char *); - const char *inactive_name() const {return inactive_name_;} - void inactive_name(const char *); - uchar hotspot() const {return hotspot_;} - void hotspot(uchar v) {hotspot_ = v;} - uchar resizable() const; - void resizable(uchar v); - - virtual int textstuff(int what, Fl_Font &, int &, Fl_Color &); - virtual Fl_Menu_Item *subtypes(); - - ID id() const FL_OVERRIDE { return ID_Widget_; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Widget_) ? true : super::is_a(inID); } - int is_widget() const FL_OVERRIDE; - int is_true_widget() const FL_OVERRIDE { return 1; } - int is_public() const FL_OVERRIDE; - - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - int read_fdesign(const char*, const char*) FL_OVERRIDE; - - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - Fl_Widget *propagate_live_mode(Fl_Group* grp); - void leave_live_mode() FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; - - virtual void ideal_size(int &w, int &h); - - ~Fl_Widget_Type(); - void redraw(); -}; - -extern Fl_Window *the_panel; - -#endif // _FLUID_FL_WIDGET_TYPE_H diff --git a/fluid/nodes/Fl_Window_Type.cxx b/fluid/nodes/Fl_Window_Type.cxx deleted file mode 100644 index 3f68f7980..000000000 --- a/fluid/nodes/Fl_Window_Type.cxx +++ /dev/null @@ -1,1562 +0,0 @@ -// -// Window type code file for the Fast Light Tool Kit (FLTK). -// -// The widget describing an Fl_Window. This is also all the code -// for interacting with the overlay, which allows the user to -// select, move, and resize the children widgets. -// -// 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 "nodes/Fl_Window_Type.h" - -#include "app/Fd_Snap_Action.h" -#include "app/fluid.h" -#include "app/project.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "nodes/factory.h" -#include "nodes/Fl_Group_Type.h" -#include "nodes/Fl_Grid_Type.h" -#include "panels/settings_panel.h" -#include "panels/widget_panel.h" -#include "widgets/Node_Browser.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../src/flstring.h" - -#include -#include -#include - -extern Fl_Window *the_panel; -extern void draw_width(int x, int y, int r, Fl_Align a); -extern void draw_height(int x, int y, int b, Fl_Align a); - -extern Fl_Preferences fluid_prefs; - -// Update the XYWH values in the widget panel... -static void update_xywh() { - if (current_widget && current_widget->is_widget()) { - Fl_Widget *o = ((Fl_Widget_Type *)current_widget)->o; - widget_x_input->value(o->x()); - widget_y_input->value(o->y()); - widget_w_input->value(o->w()); - widget_h_input->value(o->h()); - if (Fl_Flex_Type::parent_is_flex(current_widget)) { - widget_flex_size->value(Fl_Flex_Type::size(current_widget)); - widget_flex_fixed->value(Fl_Flex_Type::is_fixed(current_widget)); - } - } -} - -void i18n_type_cb(Fl_Choice *c, void *v) { - if (v == LOAD) { - c->value(g_project.i18n_type); - } else { - undo_checkpoint(); - g_project.i18n_type = static_cast(c->value()); - set_modflag(1); - } - switch (g_project.i18n_type) { - case FD_I18N_NONE : /* None */ - i18n_gnu_group->hide(); - i18n_posix_group->hide(); - break; - case FD_I18N_GNU : /* GNU gettext */ - i18n_gnu_group->show(); - i18n_posix_group->hide(); - break; - case FD_I18N_POSIX : /* POSIX cat */ - i18n_gnu_group->hide(); - i18n_posix_group->show(); - break; - } - // make sure that the outside labels are redrawn too. - w_settings_i18n_tab->redraw(); -} - -void show_grid_cb(Fl_Widget *, void *) { - settings_window->show(); - w_settings_tabs->value(w_settings_layout_tab); -} - -void show_settings_cb(Fl_Widget *, void *) { - settings_window->hotspot(settings_window); - settings_window->show(); -} - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Item window_type_menu[] = { - {"Single",0,0,(void*)FL_WINDOW}, - {"Double",0,0,(void*)(FL_DOUBLE_WINDOW)}, - {0}}; - -static int overlays_invisible; - -// The following Fl_Widget is used to simulate the windows. It has -// an overlay for the fluid ui, and special-cases the FL_NO_BOX. - -class Overlay_Window : public Fl_Overlay_Window { - void draw() FL_OVERRIDE; - void draw_overlay() FL_OVERRIDE; - static void close_cb(Overlay_Window *self, void*); -public: - Fl_Window_Type *window; - int handle(int) FL_OVERRIDE; - Overlay_Window(int W,int H) : Fl_Overlay_Window(W,H) { - Fl_Group::current(0); - callback((Fl_Callback*)close_cb); - } - void resize(int,int,int,int) FL_OVERRIDE; - uchar *read_image(int &ww, int &hh); -}; - -/** - \brief User closes the window, so we mark the .fl file as changed. - Mark the .fl file a changed, but don;t mark the source files as changed. - \param self pointer to this window - */ -void Overlay_Window::close_cb(Overlay_Window *self, void*) { - if (self->visible()) - set_modflag(1, -2); - self->hide(); -} - -// Use this when drawing flat boxes while editing, so users can see the outline, -// even if the group and its parent have the same color. -static void fd_flat_box_ghosted(int x, int y, int w, int h, Fl_Color c) { - fl_rectf(x, y, w, h, Fl::box_color(c)); - fl_rect(x, y, w, h, Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, c, .1f))); -} - -void Overlay_Window::draw() { - const int CHECKSIZE = 8; - // see if box is clear or a frame or rounded: - if ((damage()&FL_DAMAGE_ALL) && - (!box() || (box()>=4&&!(box()&2)) || box()>=_FL_ROUNDED_BOX)) { - // if so, draw checkerboard so user can see what areas are clear: - for (int Y = 0; Y < h(); Y += CHECKSIZE) - for (int X = 0; X < w(); X += CHECKSIZE) { - fl_color(((Y/(2*CHECKSIZE))&1) != ((X/(2*CHECKSIZE))&1) ? - FL_WHITE : FL_BLACK); - fl_rectf(X,Y,CHECKSIZE,CHECKSIZE); - } - } - if (show_ghosted_outline) { - Fl_Box_Draw_F *old_flat_box = Fl::get_boxtype(FL_FLAT_BOX); - Fl::set_boxtype(FL_FLAT_BOX, fd_flat_box_ghosted, 0, 0, 0, 0); - Fl_Overlay_Window::draw(); - Fl::set_boxtype(FL_FLAT_BOX, old_flat_box, 0, 0, 0, 0); - } else { - Fl_Overlay_Window::draw(); - } -} - -extern Fl_Window *main_window; - -// Read an image of the overlay window -uchar *Overlay_Window::read_image(int &ww, int &hh) { - // Create an off-screen buffer for the window... - //main_window->make_current(); - make_current(); - - ww = w(); - hh = h(); - - Fl_Offscreen offscreen = fl_create_offscreen(ww, hh); - uchar *pixels; - - // Redraw the window into the offscreen buffer... - fl_begin_offscreen(offscreen); - - if (!shown()) image(Fl::scheme_bg_); - - redraw(); - draw(); - - // Read the screen image... - pixels = fl_read_image(0, 0, 0, ww, hh); - - fl_end_offscreen(); - - // Cleanup and return... - fl_delete_offscreen(offscreen); - main_window->make_current(); - return pixels; -} - -void Overlay_Window::draw_overlay() { - window->draw_overlay(); -} - -int Overlay_Window::handle(int e) { - int ret = window->handle(e); - if (ret==0) { - switch (e) { - case FL_SHOW: - case FL_HIDE: - ret = Fl_Overlay_Window::handle(e); - } - } - return ret; -} - -/** - Make and add a new Window node. - \param[in] strategy is Strategy::AS_LAST_CHILD or Strategy::AFTER_CURRENT - \return new node - */ -Fl_Type *Fl_Window_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) p = p->parent; - while (p && (!p->is_code_block() || p->is_a(ID_Widget_Class))) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - if (!p) { - fl_message("Please select a function"); - return 0; - } - Fl_Window_Type *myo = new Fl_Window_Type(); - if (!this->o) {// template widget - this->o = new Fl_Window(100,100); - Fl_Group::current(0); - } - myo->factory = this; - myo->drag = 0; - myo->numselected = 0; - Overlay_Window *w = new Overlay_Window(100, 100); - w->size_range(10, 10); - w->window = myo; - myo->o = w; - myo->add(anchor, strategy); - myo->modal = 0; - myo->non_modal = 0; - return myo; -} - -void Fl_Window_Type::add_child(Fl_Type* cc, Fl_Type* before) { - if (!cc->is_widget()) return; - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - Fl_Widget* b = before ? ((Fl_Widget_Type*)before)->o : 0; - ((Fl_Window*)o)->insert(*(c->o), b); - o->redraw(); -} - -void Fl_Window_Type::remove_child(Fl_Type* cc) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - ((Fl_Window*)o)->remove(c->o); - o->redraw(); -} - -void Fl_Window_Type::move_child(Fl_Type* cc, Fl_Type* before) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - ((Fl_Window*)o)->remove(c->o); - Fl_Widget* b = before ? ((Fl_Widget_Type*)before)->o : 0; - ((Fl_Window*)o)->insert(*(c->o), b); - o->redraw(); -} - -//////////////////////////////////////////////////////////////// - -/** - \brief Show the Window Type editor window without setting the modified flag. - \see Fl_Window_Type::open() - */ -void Fl_Window_Type::open_() { - Overlay_Window *w = (Overlay_Window *)o; - if (w->shown()) { - w->show(); - Fl_Widget_Type::open(); - } else { - Fl_Widget *p = w->resizable(); - if (!p) w->resizable(w); - w->show(); - w->resizable(p); - } - w->image(Fl::scheme_bg_); -} - -/** - \brief Show the Window Type editor window and set the modified flag if needed. - Double-click on window widget shows the window, or if already shown, it shows - the control panel. - \see Fl_Window_Type::open_() - */ -void Fl_Window_Type::open() { - Overlay_Window *w = (Overlay_Window *)o; - if (!w->visible()) { - set_modflag(1, -2); - } - open_(); -} - -// Read an image of the window -uchar *Fl_Window_Type::read_image(int &ww, int &hh) { - Overlay_Window *w = (Overlay_Window *)o; - - int hidden = !w->shown(); - w->show(); // make it the front window - - // Read the screen image... - uchar *idata = w->read_image(ww, hh); - if (hidden) - w->hide(); - return idata; -} - -void Fl_Window_Type::ideal_size(int &w, int &h) { - w = 480; h = 320; - if (main_window) { - int sx, sy, sw, sh; - Fl_Window *win = main_window; - int screen = Fl::screen_num(win->x(), win->y()); - Fl::screen_work_area(sx, sy, sw, sh, screen); - w = fd_min(w, sw*3/4); h = fd_min(h, sh*3/4); - } - Fd_Snap_Action::better_size(w, h); -} - - -// control panel items: - -void modal_cb(Fl_Light_Button* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) {i->hide(); return;} - i->show(); - i->value(((Fl_Window_Type *)current_widget)->modal); - } else { - undo_checkpoint(); - ((Fl_Window_Type *)current_widget)->modal = i->value(); - set_modflag(1); - } -} - -void non_modal_cb(Fl_Light_Button* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) {i->hide(); return;} - i->show(); - i->value(((Fl_Window_Type *)current_widget)->non_modal); - } else { - undo_checkpoint(); - ((Fl_Window_Type *)current_widget)->non_modal = i->value(); - set_modflag(1); - } -} - -void border_cb(Fl_Light_Button* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) {i->hide(); return;} - i->show(); - i->value(((Fl_Window*)(current_widget->o))->border()); - } else { - undo_checkpoint(); - ((Fl_Window*)(current_widget->o))->border(i->value()); - set_modflag(1); - } -} - -void xclass_cb(Fl_Input* i, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Window)) { - i->show(); - i->parent()->show(); - i->value(((Fl_Window_Type *)current_widget)->xclass); - } else { - i->hide(); - i->parent()->hide(); // hides the "X Class:" label as well - } - } else { - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - mod = 1; - Fl_Window_Type *wt = (Fl_Window_Type *)o; - storestring(i->value(), wt->xclass); - ((Fl_Window*)(wt->o))->xclass(wt->xclass); - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -void Fl_Window_Type::setlabel(const char *n) { - if (o) ((Fl_Window *)o)->label(n); -} - -// make() is called on this widget when user picks window off New menu: -Fl_Window_Type Fl_Window_type; - -// Resize from window manager... -void Overlay_Window::resize(int X,int Y,int W,int H) { - // Make sure we don't create undo checkpoints if the window does not actually change. - // Some WMs seem to send spurious resize events. - if (X!=x() || Y!=y() || W!=w() || H!=h()) { - // Set a checkpoint on the first resize event, ignore further resizes until - // a different type of checkpoint is triggered. - if (undo_checkpoint_once(kUndoWindowResize)) - set_modflag(1); - } - - Fl_Widget* t = resizable(); - if (Fl_Type::allow_layout == 0) { - resizable(0); - } - - // do not set the mod flag if the window was not resized. In FLUID, all - // windows are opened without a given x/y position, so modifying x/y - // should not mark the project as dirty - if (W!=w() || H!=h()) - set_modflag(1); - - Fl_Overlay_Window::resize(X,Y,W,H); - resizable(t); - update_xywh(); -} - -// calculate actual move by moving mouse position (mx,my) to -// nearest multiple of gridsize, and snap to original position -void Fl_Window_Type::newdx() { - int mydx, mydy; - mydx = mx-x1; - mydy = my-y1; - - if (!(drag & (FD_DRAG | FD_BOX | FD_LEFT | FD_RIGHT))) { - mydx = 0; - dx = 0; - } - - if (!(drag & (FD_DRAG | FD_BOX | FD_TOP | FD_BOTTOM))) { - mydy = 0; - dy = 0; - } - - if (show_guides && (drag & (FD_DRAG|FD_TOP|FD_LEFT|FD_BOTTOM|FD_RIGHT))) { - Fl_Type *selection = 0L; // special power for the first selected widget - for (Fl_Type *q=next; q && q->level>level; q = q->next) { - if (q->selected && q->is_true_widget()) { - selection = q; - break; - } - } - Fd_Snap_Data data = { mydx, mydy, bx, by, br, bt, drag, 4, 4, mydx, mydy, (Fl_Widget_Type*)selection, this }; - Fd_Snap_Action::check_all(data); - if (data.x_dist < 4) mydx = data.dx_out; - if (data.y_dist < 4) mydy = data.dy_out; - } - - if (dx != mydx || dy != mydy) { - dx = mydx; dy = mydy; - ((Overlay_Window *)o)->redraw_overlay(); - } -} - -// Move a widget according to dx and dy calculated above -void Fl_Window_Type::newposition(Fl_Widget_Type *myo,int &X,int &Y,int &R,int &T) { - X = myo->o->x(); - Y = myo->o->y(); - R = X+myo->o->w(); - T = Y+myo->o->h(); - if (!drag) return; - if (drag&FD_DRAG) { - X += dx; - Y += dy; - R += dx; - T += dy; - } else { - if (drag&FD_LEFT) { - if (X==bx) { - X += dx; - } else { - if (Xbr+dx) R = br+dx; - } - } - if (drag&FD_BOTTOM) { - if (T==bt) { - T += dy; - } else { - if (T>bt+dx) T = bt+dx; - } - } - } - if (R h) { - for (; yp < h; yp+=size) - fl_line(x, y+yp, x+yp, y); - for (; yp < w; yp+=size) - fl_line(x+yp-h, y+h, x+yp, y); - for (; yp < w+h; yp+=size) - fl_line(x+yp-h, y+h, x+w, y+yp-w); - } else { - for (; yp < w; yp+=size) - fl_line(x, y+yp, x+yp, y); - for (; yp < h; yp+=size) - fl_line(x, y+yp, x+w, y+yp-w); - for (; yp < h+w; yp+=size) - fl_line(x+yp-h, y+h, x+w, y+yp-w); - } -} - -/** - \brief Draw a hatch pattern over all children that overlap the bounds of this box. - \param[in] group check all children of this group - \param[in] x, y, w, h bounding box of this group - */ -void Fl_Window_Type::draw_out_of_bounds(Fl_Widget_Type *group, int x, int y, int w, int h) { - for (Fl_Type *p = group->next; p && p->level>group->level; p = p->next) { - if (p->level == group->level+1 && p->is_true_widget()) { - Fl_Widget *o = ((Fl_Widget_Type*)p)->o; - if (o->x() < x) fd_hatch(o->x(), o->y(), x-o->x(), o->h()); - if (o->y() < y) fd_hatch(o->x(), o->y(), o->w(), y-o->y()); - if (o->x()+o->w() > x+w) fd_hatch(x+w, o->y(), (o->x()+o->w())-(x+w), o->h()); - if (o->y()+o->h() > y+h) fd_hatch(o->x(), y+h, o->w(), (o->y()+o->h())-(y+h)); - } - } -} - -/** - \brief Draw a hatch pattern for all groups that have out of bounds children. - */ -void Fl_Window_Type::draw_out_of_bounds() { - // get every group in the hierarchy, then draw any overlap of a direct child with that group - fl_color(FL_DARK_RED); - draw_out_of_bounds(this, 0, 0, o->w(), o->h()); - for (Fl_Type *q=next; q && q->level>level; q = q->next) { - // don't do this for Fl_Scroll (which we currently can't handle in FLUID anyway) - if (q->is_a(ID_Group) && !q->is_a(ID_Scroll)) { - Fl_Widget_Type *w = (Fl_Widget_Type*)q; - draw_out_of_bounds(w, w->o->x(), w->o->y(), w->o->w(), w->o->h()); - } - } - fl_color(FL_RED); -} - -/** - \brief Compare all children in the same level and hatch overlapping areas. - */ -void Fl_Window_Type::draw_overlaps() { - fl_color(FL_DARK_YELLOW); - // loop through all widgets in this window - for (Fl_Type *q=next; q && q->level>level; q = q->next) { - // is it a valid widget - if (q->is_true_widget()) { - Fl_Widget_Type *w = (Fl_Widget_Type*)q; - // is the widget visible - if (w->o->visible()) { - int x = w->o->x(), y = w->o->y(); - int r = x + w->o->w(), b = y + w->o->h(); - for (Fl_Type *p=q->next; p && p->level>=q->level; p = p->next) { - if (p->level==q->level && p->is_true_widget()) { - Fl_Widget_Type *wp = (Fl_Widget_Type*)p; - if (wp->o->visible()) { - int px = fd_max(x, wp->o->x()); - int py = fd_max(y, wp->o->y()); - int pr = fd_min(r, wp->o->x() + wp->o->w()); - int pb = fd_min(b, wp->o->y() + wp->o->h()); - if (pr > px && pb > py) - fd_hatch(px, py, pr-px, pb-py); - } - } - } - } else { - int l = q->level; - for (; q && q->next && q->next->level>l; q = q->next) { } - } - } - } - fl_color(FL_RED); -} - -void Fl_Window_Type::draw_overlay() { - if (recalc) { - bx = o->w(); by = o->h(); br = 0; bt = 0; - numselected = 0; - for (Fl_Type *q=next; q && q->level>level; q=q->next) - if (q->selected && q->is_true_widget()) { - numselected++; - Fl_Widget_Type* myo = (Fl_Widget_Type*)q; - if (myo->o->x() < bx) bx = myo->o->x(); - if (myo->o->y() < by) by = myo->o->y(); - if (myo->o->x()+myo->o->w() > br) br = myo->o->x()+myo->o->w(); - if (myo->o->y()+myo->o->h() > bt) bt = myo->o->y()+myo->o->h(); - } - recalc = 0; - sx = bx; sy = by; sr = br; st = bt; - } - fl_color(FL_RED); - if (drag==FD_BOX && (x1 != mx || y1 != my)) { - int x = x1; int r = mx; if (x > r) {x = mx; r = x1;} - int y = y1; int b = my; if (y > b) {y = my; b = y1;} - fl_rect(x,y,r-x,b-y); - } - if (overlays_invisible && !drag) return; - - if (show_restricted) { - draw_out_of_bounds(); - draw_overlaps(); - // TODO: for Fl_Tile, find all areas that are not covered by visible children - } - - if (selected) fl_rect(0,0,o->w(),o->h()); - if (!numselected) return; - int mybx,myby,mybr,mybt; - int mysx,mysy,mysr,myst; - mybx = mysx = o->w(); myby = mysy = o->h(); mybr = mysr = 0; mybt = myst = 0; - Fl_Type *selection = 0L; // special power for the first selected widget - for (Fl_Type *q=next; q && q->level>level; q = q->next) - if (q->selected && q->is_true_widget()) { - if (!selection) selection = q; - Fl_Widget_Type* myo = (Fl_Widget_Type*)q; - int x,y,r,t; - newposition(myo,x,y,r,t); - if (show_guides) { - // If we are in a drag operation, and the parent is a grid, show the grid overlay - if (drag && q->parent && q->parent->is_a(ID_Grid)) { - Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)((Fl_Grid_Type*)q->parent)->o); - grid->draw_overlay(); - } - } - if (!show_guides || !drag || numselected != 1) { - if (Fl_Flex_Type::parent_is_flex(q) && Fl_Flex_Type::is_fixed(q)) { - Fl_Flex *flex = ((Fl_Flex*)((Fl_Flex_Type*)q->parent)->o); - Fl_Widget *wgt = myo->o; - if (flex->horizontal()) { - draw_width(wgt->x(), wgt->y()+15, wgt->x()+wgt->w(), FL_ALIGN_CENTER); - } else { - draw_height(wgt->x()+15, wgt->y(), wgt->y()+wgt->h(), FL_ALIGN_CENTER); - } - } else if (q->is_a(ID_Grid)) { - Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)((Fl_Grid_Type*)q)->o); - grid->draw_overlay(); - } - fl_rect(x,y,r-x,t-y); - } - if (x < mysx) mysx = x; - if (y < mysy) mysy = y; - if (r > mysr) mysr = r; - if (t > myst) myst = t; - if (!(myo->o->align() & FL_ALIGN_INSIDE)) { - // Adjust left/right/top/bottom for top/bottom labels... - int ww, hh; - ww = (myo->o->align() & FL_ALIGN_WRAP) ? myo->o->w() : 0; - hh = myo->o->labelsize(); - myo->o->measure_label(ww, hh); - if (myo->o->align() & FL_ALIGN_TOP) y -= hh; - else if (myo->o->align() & FL_ALIGN_BOTTOM) t += hh; - else if (myo->o->align() & FL_ALIGN_LEFT) x -= ww + 4; - else if (myo->o->align() & FL_ALIGN_RIGHT) r += ww + 4; - } - if (x < mybx) mybx = x; - if (y < myby) myby = y; - if (r > mybr) mybr = r; - if (t > mybt) mybt = t; - } - if (selected) return; - - // align the snapping selection box with the box we draw. - sx = mysx; sy = mysy; sr = mysr; st = myst; - - // Draw selection box + resize handles... - // draw box including all labels - fl_focus_rect(mybx,myby,mybr-mybx,mybt-myby); // issue #816 - // draw box excluding labels - fl_rect(mysx,mysy,mysr-mysx,myst-mysy); - fl_rectf(mysx,mysy,5,5); - fl_rectf(mysr-5,mysy,5,5); - fl_rectf(mysr-5,myst-5,5,5); - fl_rectf(mysx,myst-5,5,5); - - if (show_guides && (drag & (FD_DRAG|FD_TOP|FD_LEFT|FD_BOTTOM|FD_RIGHT))) { - Fd_Snap_Data data = { dx, dy, sx, sy, sr, st, drag, 4, 4, dx, dy, (Fl_Widget_Type*)selection, this}; - Fd_Snap_Action::draw_all(data); - } -} - -extern Fl_Menu_Item Main_Menu[]; - -// Calculate new bounding box of selected widgets: -void Fl_Window_Type::fix_overlay() { - overlay_item->label("Hide O&verlays"); - if (overlay_button) overlay_button->label("Hide &Overlays"); - overlays_invisible = 0; - recalc = 1; - ((Overlay_Window *)(this->o))->redraw_overlay(); -} - -// check if we must redraw any parent of tabs/wizard type -void check_redraw_corresponding_parent(Fl_Type *s) { - Fl_Widget_Type * prev_parent = 0; - if( !s || !s->selected || !s->is_widget()) return; - for (Fl_Type *i=s; i && i->parent; i=i->parent) { - if (i->is_a(ID_Group) && prev_parent) { - if (i->is_a(ID_Tabs)) { - ((Fl_Tabs*)((Fl_Widget_Type*)i)->o)->value(prev_parent->o); - return; - } - if (i->is_a(ID_Wizard)) { - ((Fl_Wizard*)((Fl_Widget_Type*)i)->o)->value(prev_parent->o); - return; - } - } - if (i->is_a(ID_Group) && s->is_widget()) - prev_parent = (Fl_Widget_Type*)i; - } -} - -// do that for every window (when selected set changes): -void redraw_overlays() { - for (Fl_Type *o=Fl_Type::first; o; o=o->next) - if (o->is_a(ID_Window)) ((Fl_Window_Type*)o)->fix_overlay(); -} - -void toggle_overlays(Fl_Widget *,void *) { - overlays_invisible = !overlays_invisible; - - if (overlays_invisible) { - overlay_item->label("Show O&verlays"); - if (overlay_button) overlay_button->label("Show &Overlays"); - } else { - overlay_item->label("Hide O&verlays"); - if (overlay_button) overlay_button->label("Hide &Overlays"); - } - - for (Fl_Type *o=Fl_Type::first; o; o=o->next) - if (o->is_a(ID_Window)) { - Fl_Widget_Type* w = (Fl_Widget_Type*)o; - ((Overlay_Window*)(w->o))->redraw_overlay(); - } -} - -/** - \brief User changes settings to show positioning guides in layout editor overlay. - This is called from the main menu and from the check button in the Settings - dialog. - */ -void toggle_guides(Fl_Widget *,void *) { - show_guides = !show_guides; - fluid_prefs.set("show_guides", show_guides); - - if (show_guides) - guides_item->label("Hide Guides"); - else - guides_item->label("Show Guides"); - if (guides_button) - guides_button->value(show_guides); - - for (Fl_Type *o=Fl_Type::first; o; o=o->next) { - if (o->is_a(ID_Window)) { - Fl_Widget_Type* w = (Fl_Widget_Type*)o; - ((Overlay_Window*)(w->o))->redraw_overlay(); - } - } -} - -/** - \brief User changes settings to show positioning guides in layout editor overlay. - This is called from the check button in the Settings dialog. - */ -void toggle_guides_cb(Fl_Check_Button *o, void *v) { - toggle_guides(NULL, NULL); -} - -/** - \brief User changes settings to show overlapping and out of bounds widgets. - This is called from the main menu and from the check button in the Settings - dialog. - */ -void toggle_restricted(Fl_Widget *,void *) { - show_restricted = !show_restricted; - fluid_prefs.set("show_restricted", show_restricted); - - if (show_restricted) - restricted_item->label("Hide Restricted"); - else - restricted_item->label("Show Restricted"); - if (restricted_button) - restricted_button->value(show_restricted); - - for (Fl_Type *o=Fl_Type::first; o; o=o->next) { - if (o->is_a(ID_Window)) { - Fl_Widget_Type* w = (Fl_Widget_Type*)o; - ((Overlay_Window*)(w->o))->redraw_overlay(); - } - } -} - -/** - \brief User changes settings to show low contrast groups with a ghosted outline. - */ -void toggle_ghosted_outline_cb(Fl_Check_Button *,void *) { - show_ghosted_outline = !show_ghosted_outline; - fluid_prefs.set("show_ghosted_outline", show_ghosted_outline); - for (Fl_Type *o=Fl_Type::first; o; o=o->next) { - if (o->is_a(ID_Window)) { - Fl_Widget_Type* w = (Fl_Widget_Type*)o; - ((Overlay_Window*)(w->o))->redraw(); - } - } -} - -/** - \brief User changes settings to show overlapping and out of bounds widgets. - This is called from the check button in the Settings dialog. - */ -void toggle_restricted_cb(Fl_Check_Button *o, void *v) { - toggle_restricted(NULL, NULL); -} - -extern void select(Fl_Type *,int); -extern void select_only(Fl_Type *); -extern void deselect(); -extern Fl_Type* in_this_only; -extern void fix_group_size(Fl_Type *t); - -extern Fl_Menu_Item Main_Menu[]; -extern Fl_Menu_Item New_Menu[]; - -/** - Move the selected children according to current dx, dy, drag state. - - This is somewhat of a do-all function that received many additions when new - widget types were added. In the default case, moving a group will simply move - all descendants with it. When resizing, children are resized to fit within - the group. - - This is not ideal for widgets that are moved or resized within a group that - manages the layout of its children. We must create a more universal way to - modify move events per widget type. - - \param[in] key if key is not 0, it contains the code of the keypress that - caused this call. This must only be set when handle FL_KEYBOARD events. - */ -void Fl_Window_Type::moveallchildren(int key) -{ - bool update_widget_panel = false; - undo_checkpoint(); - Fl_Type *i; - for (i=next; i && i->level>level;) { - if (i->selected && i->is_true_widget()) { - Fl_Widget_Type* myo = (Fl_Widget_Type*)i; - int x,y,r,t,ow=myo->o->w(),oh=myo->o->h(); - newposition(myo,x,y,r,t); - if (myo->is_a(ID_Flex) || myo->is_a(ID_Grid)) { - // Flex and Grid need to be able to layout their children. - allow_layout++; - myo->o->resize(x,y,r-x,t-y); - allow_layout--; - } else { - // Other groups are resized without affecting their children, however - // they move their children if the entire widget is moved. - myo->o->resize(x,y,r-x,t-y); - } - if (Fl_Flex_Type::parent_is_flex(myo)) { - // If the border of a Flex child is move, give that child a fixed size - // so that the user request is reflected. - Fl_Flex_Type* ft = (Fl_Flex_Type*)myo->parent; - Fl_Flex* f = (Fl_Flex*)ft->o; - if (key) { - ft->keyboard_move_child(myo, key); - } else if (drag & FD_DRAG) { - ft->insert_child_at(myo->o, Fl::event_x(), Fl::event_y()); - } else { - if (f->horizontal()) { - if (myo->o->w()!=ow) { - f->fixed(myo->o, myo->o->w()); - f->layout(); - } - } else { - if (myo->o->h()!=oh) { - f->fixed(myo->o, myo->o->h()); - f->layout(); - } - } - } - // relayout the Flex parent - allow_layout++; - f->layout(); - allow_layout--; - } else if (myo->parent && myo->parent->is_a(ID_Grid)) { - Fl_Grid_Type* gt = (Fl_Grid_Type*)myo->parent; - Fl_Grid* g = (Fl_Grid*)gt->o; - if (key) { - gt->keyboard_move_child(myo, key); - } else { - if (drag & FD_DRAG) { - gt->insert_child_at(myo->o, Fl::event_x(), Fl::event_y()); - } else { - gt->child_resized(myo); - } - } - allow_layout++; - g->layout(); - allow_layout--; - update_widget_panel = true; - } else if (myo->parent && myo->parent->is_a(ID_Group)) { - Fl_Group_Type* gt = (Fl_Group_Type*)myo->parent; - ((Fl_Group*)gt->o)->init_sizes(); - } - // move all the children, whether selected or not: - Fl_Type* p; - for (p = myo->next; p && p->level>myo->level; p = p->next) - if (p->is_true_widget() && !myo->is_a(ID_Flex) && !myo->is_a(ID_Grid)) { - Fl_Widget_Type* myo2 = (Fl_Widget_Type*)p; - int X,Y,R,T; - newposition(myo2,X,Y,R,T); - myo2->o->resize(X,Y,R-X,T-Y); - } - i = p; - } else { - i = i->next; - } - } - for (i=next; i && i->level>level; i=i->next) - fix_group_size(i); - o->redraw(); - recalc = 1; - ((Overlay_Window *)(this->o))->redraw_overlay(); - set_modflag(1); - dx = dy = 0; - - update_xywh(); - if (update_widget_panel && the_panel && the_panel->visible()) { - propagate_load(the_panel, LOAD); - } -} - -int Fl_Window_Type::popupx = 0x7FFFFFFF; // mark as invalid (MAXINT) -int Fl_Window_Type::popupy = 0x7FFFFFFF; - -int Fl_Window_Type::handle(int event) { - static Fl_Type* selection = NULL; - switch (event) { - case FL_DND_ENTER: - // printf("DND enter\n"); - case FL_DND_DRAG: - // printf("DND drag\n"); - { - // find the innermost item clicked on: - selection = this; - for (Fl_Type* i=next; i && i->level>level; i=i->next) - if (i->is_a(ID_Group)) { - Fl_Widget_Type* myo = (Fl_Widget_Type*)i; - if (Fl::event_inside(myo->o) && myo->o->visible_r()) { - selection = myo; - if (Fl::event_clicks()==1) - reveal_in_browser(myo); - } - } - if (selection && !selection->selected) { - select_only(selection); - ((Overlay_Window *)o)->redraw_overlay(); - } - } - Fl::belowmouse(o); - return 1; - case FL_DND_RELEASE: - // printf("DND release\n"); - Fl::belowmouse(o); - return 1; - case FL_PASTE: - // printf("DND paste\n"); - { Fl_Type *prototype = typename_to_prototype(Fl::event_text()); - if (prototype==NULL) { - // it's not a FLUID type, so it could be the filename of an image - const char *cfn = Fl::event_text(); - // printf("DND is filename %s?\n", cfn); - if ((cfn == NULL) || (*cfn == 0)) return 0; - if (strlen(cfn) >= FL_PATH_MAX) return 0; - char fn[FL_PATH_MAX+1]; - // some platform prepend "file://" or "computer://" or similar text - const char *sep = strstr(cfn, "://"); - if (sep) - strcpy(fn, sep+3); - else - strcpy(fn, cfn); - // remove possibly trailing \r\n - int n = (int)strlen(fn)-1; - if (fn[n] == '\n') fn[n--] = 0; - if (fn[n] == '\r') fn[n--] = 0; - // on X11 and Wayland (?), filenames need to be decoded -#if (defined(FLTK_USE_X11) || defined(FLTK_USE_WAYLAND)) - fl_decode_uri(fn); -#endif - // does a file by that name actually exist? - if (fl_access(fn, 4)==-1) return 0; - // but is this an image file? - Fl_Image *img = Fl_Shared_Image::get(fn); - if (!img || (img->ld() < 0)) return 0; - // ok, so it is an image - now add it as image() or deimage() to the widget - // printf("DND check for target %s\n", fn); - Fl_Widget_Type *tgt = NULL; - for (Fl_Type* i=next; i && i->level>level; i=i->next) { - if (i->is_widget()) { - Fl_Widget_Type* myo = (Fl_Widget_Type*)i; - if (Fl::event_inside(myo->o) && myo->o->visible_r()) - tgt = myo; - } - } - if (tgt) { - char rel[FL_PATH_MAX+1]; - enter_project_dir(); - fl_filename_relative(rel, FL_PATH_MAX, fn); - leave_project_dir(); - // printf("DND image = %s\n", fn); - if (Fl::get_key(FL_Alt_L) || Fl::get_key(FL_Alt_R)) { - //if (Fl::event_alt()) { // TODO: X11/Wayland does not set the e_state on DND events - tgt->inactive_name(rel); - tgt->compress_deimage_ = 1; - tgt->bind_deimage_ = 0; - } else { - tgt->image_name(rel); - tgt->compress_image_ = 1; - tgt->bind_image_ = 0; - } - select_only(tgt); - tgt->open(); - } - return 1; - } - - in_this_only = this; - popupx = Fl::event_x(); - popupy = Fl::event_y(); - // If the selected widget at dnd start and the drop target are the same, - // or in the same group, add after selection. Otherwise, just add - // at the end of the selected group. - if ( Fl_Type::current_dnd->group() - && selection && selection->group() - && Fl_Type::current_dnd->group()==selection->group()) - { - Fl_Type *cc = Fl_Type::current; - Fl_Type::current = Fl_Type::current_dnd; - add_new_widget_from_user(prototype, Strategy::AS_LAST_CHILD); - Fl_Type::current = cc; - } else { - add_new_widget_from_user(prototype, Strategy::AS_LAST_CHILD); - } - popupx = 0x7FFFFFFF; - popupy = 0x7FFFFFFF; // mark as invalid (MAXINT) - in_this_only = NULL; - widget_browser->display(Fl_Type::current); - widget_browser->rebuild(); - return 1; - } - case FL_PUSH: - x1 = mx = Fl::event_x(); - y1 = my = Fl::event_y(); - drag = dx = dy = 0; - // test for popup menu: - if (Fl::event_button() >= 3) { - in_this_only = this; // modifies how some menu items work. - static const Fl_Menu_Item* myprev; - popupx = mx; popupy = my; - const Fl_Menu_Item* m = New_Menu->popup(mx,my,"New",myprev); - if (m && m->callback()) {myprev = m; m->do_callback(this->o);} - popupx = 0x7FFFFFFF; popupy = 0x7FFFFFFF; // mark as invalid (MAXINT) - in_this_only = 0; - return 1; - } - // find the innermost item clicked on: - selection = this; - {for (Fl_Type* i=next; i && i->level>level; i=i->next) - if (i->is_true_widget()) { - Fl_Widget_Type* myo = (Fl_Widget_Type*)i; - for (Fl_Widget *o1 = myo->o; o1; o1 = o1->parent()) - if (!o1->visible()) goto CONTINUE2; - if (Fl::event_inside(myo->o)) { - selection = myo; - if (Fl::event_clicks()==1) - reveal_in_browser(myo); - } - CONTINUE2:; - }} - // see if user grabs edges of selected region: - if (numselected && !(Fl::event_state(FL_SHIFT)) && - mx<=br+2 && mx>=bx-2 && my<=bt+2 && my>=by-2) { - if (mx >= br-5) drag |= FD_RIGHT; - else if (mx <= bx+5) drag |= FD_LEFT; - if (my >= bt-5) drag |= FD_BOTTOM; - else if (my <= by+5) drag |= FD_TOP; - if (!drag) drag = FD_DRAG; - } - // do object-specific selection of other objects: - {Fl_Type* t = selection->click_test(mx, my); - if (t) { - //if (t == selection) return 1; // indicates mouse eaten w/o change - if (Fl::event_state(FL_SHIFT)) { - Fl::event_is_click(0); - select(t, !t->selected); - } else { - deselect(); - select(t, 1); - if (t->is_a(ID_Menu_Item)) t->open(); - } - selection = t; - drag = 0; - } else { - if (!drag) drag = FD_BOX; // if all else fails, start a new selection region - }} - return 1; - - case FL_DRAG: - if (!drag) return 0; - mx = Fl::event_x(); - my = Fl::event_y(); - newdx(); - return 1; - - case FL_RELEASE: - if (!drag) return 0; - mx = Fl::event_x(); - my = Fl::event_y(); - if (drag != FD_BOX && (dx || dy || !Fl::event_is_click())) { - if (dx || dy) moveallchildren(); - } else if ((Fl::event_clicks() || Fl::event_state(FL_CTRL))) { - Fl_Widget_Type::open(); - } else { - if (mxlevel>level; i=i->next) - if (i->is_true_widget()) { - Fl_Widget_Type* myo = (Fl_Widget_Type*)i; - for (Fl_Widget *o1 = myo->o; o1; o1 = o1->parent()) - if (!o1->visible()) goto CONTINUE; - if (Fl::event_inside(myo->o)) selection = myo; - if (myo && myo->o && myo->o->x()>=x1 && myo->o->y()>y1 && - myo->o->x()+myo->o->w()o->y()+myo->o->h()selected : 1); - } - CONTINUE:; - } - // if nothing in box, select what was clicked on: - if (selection && !n) { - select(selection, toggle ? !selection->selected : 1); - } - } - drag = 0; - ((Overlay_Window *)o)->redraw_overlay(); - return 1; - - case FL_KEYBOARD: { - - int backtab = 0; - switch (Fl::event_key()) { - - case FL_Escape: - ((Fl_Window*)o)->hide(); - return 1; - - case FL_Tab: { - if (Fl::event_state(FL_SHIFT)) backtab = 1; - // find current child: - Fl_Type *i = Fl_Type::current; - while (i && !i->is_true_widget()) i = i->parent; - if (!i) return 0; - Fl_Type *p = i->parent; - while (p && p != this) p = p->parent; - if (!p || !p->is_widget()) { - i = next; if (!i || i->level <= level) return 0; - } - p = i; - for (;;) { - i = backtab ? i->prev : i->next; - if (!i || i->level <= level) {i = p; break;} - if (i->is_true_widget()) break; - } - deselect(); select(i,1); - return 1;} - - case FL_Left: dx = -1; dy = 0; goto ARROW; - case FL_Right: dx = +1; dy = 0; goto ARROW; - case FL_Up: dx = 0; dy = -1; goto ARROW; - case FL_Down: dx = 0; dy = +1; goto ARROW; - ARROW: - drag = (Fl::event_state(FL_SHIFT)) ? (FD_RIGHT|FD_BOTTOM) : FD_DRAG; - if (Fl::event_state(FL_COMMAND)) { - int x_step, y_step; - if (drag & (FD_RIGHT|FD_BOTTOM)) - Fd_Snap_Action::get_resize_stepsize(x_step, y_step); - else - Fd_Snap_Action::get_move_stepsize(x_step, y_step); - dx *= x_step; - dy *= y_step; - } - moveallchildren(Fl::event_key()); - drag = 0; - return 1; - - case 'o': - toggle_overlays(0, 0); - break; - - default: - return 0; - }} - - case FL_SHORTCUT: { - in_this_only = this; // modifies how some menu items work. - const Fl_Menu_Item* m = Main_Menu->test_shortcut(); - if (m && m->callback()) m->do_callback(this->o); - in_this_only = 0; - return (m != 0);} - - default: - return 0; - } -} - -//////////////////////////////////////////////////////////////// - - -/** - Write the C++ code that comes before the children of the window are written. - \param f the source code output stream - */ -void Fl_Window_Type::write_code1(fld::io::Code_Writer& f) { - Fl_Widget_Type::write_code1(f); -} - - -/** - Write the C++ code that comes after the children of the window are written. - \param f the source code output stream - */ -void Fl_Window_Type::write_code2(fld::io::Code_Writer& f) { - const char *var = is_class() ? "this" : name() ? name() : "o"; - // make the window modal or non-modal - if (modal) { - f.write_c("%s%s->set_modal();\n", f.indent(), var); - } else if (non_modal) { - f.write_c("%s%s->set_non_modal();\n", f.indent(), var); - } - // clear the window border - if (!((Fl_Window*)o)->border()) { - f.write_c("%s%s->clear_border();\n", f.indent(), var); - } - // set the xclass of the window - if (xclass) { - f.write_c("%s%s->xclass(", f.indent(), var); - f.write_cstring(xclass); - f.write_c(");\n"); - } - // make the window resizable - if (((Fl_Window*)o)->resizable() == o) - f.write_c("%s%s->resizable(%s);\n", f.indent(), var, var); - // set the size range last - if (sr_max_w || sr_max_h) { - f.write_c("%s%s->size_range(%d, %d, %d, %d);\n", f.indent(), var, - sr_min_w, sr_min_h, sr_max_w, sr_max_h); - } else if (sr_min_w || sr_min_h) { - f.write_c("%s%s->size_range(%d, %d);\n", f.indent(), var, sr_min_w, sr_min_h); - } - // insert extra code from user, may call `show()` - write_extra_code(f); - // stop adding widgets to this window - f.write_c("%s%s->end();\n", f.indent(), var); - write_block_close(f); -} - -void Fl_Window_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Widget_Type::write_properties(f); - if (modal) f.write_string("modal"); - else if (non_modal) f.write_string("non_modal"); - if (!((Fl_Window*)o)->border()) f.write_string("noborder"); - if (xclass) {f.write_string("xclass"); f.write_word(xclass);} - if (sr_min_w || sr_min_h || sr_max_w || sr_max_h) - f.write_string("size_range {%d %d %d %d}", sr_min_w, sr_min_h, sr_max_w, sr_max_h); - if (o->visible() || override_visible_) f.write_string("visible"); -} - -void Fl_Window_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"modal")) { - modal = 1; - } else if (!strcmp(c,"non_modal")) { - non_modal = 1; - } else if (!strcmp(c, "visible")) { - if (batch_mode) // don't actually open any windows in batch mode - override_visible_ = 1; - else // in interactive mode, we simply show the window - open_(); - } else if (!strcmp(c,"noborder")) { - ((Fl_Window*)o)->border(0); - } else if (!strcmp(c,"xclass")) { - storestring(f.read_word(),xclass); - ((Fl_Window*)o)->xclass(xclass); - } else if (!strcmp(c,"size_range")) { - int mw, mh, MW, MH; - if (sscanf(f.read_word(),"%d %d %d %d",&mw,&mh,&MW,&MH) == 4) { - sr_min_w = mw; sr_min_h = mh; sr_max_w = MW; sr_max_h = MH; - } - } else if (!strcmp(c,"xywh")) { - Fl_Widget_Type::read_property(f, c); - pasteoffset = 0; // make it not apply to contents - } else { - Fl_Widget_Type::read_property(f, c); - } -} - -int Fl_Window_Type::read_fdesign(const char* propname, const char* value) { - int x; - o->box(FL_NO_BOX); // because fdesign always puts an Fl_Box next - if (!strcmp(propname,"Width")) { - if (sscanf(value,"%d",&x) == 1) o->size(x,o->h()); - } else if (!strcmp(propname,"Height")) { - if (sscanf(value,"%d",&x) == 1) o->size(o->w(),x); - } else if (!strcmp(propname,"NumberofWidgets")) { - return 1; // we can figure out count from file - } else if (!strcmp(propname,"border")) { - if (sscanf(value,"%d",&x) == 1) ((Fl_Window*)o)->border(x); - } else if (!strcmp(propname,"title")) { - label(value); - } else { - return Fl_Widget_Type::read_fdesign(propname,value); - } - return 1; -} - -/////////////////////////////////////////////////////////////////////// - -Fl_Widget_Class_Type Fl_Widget_Class_type; -Fl_Widget_Class_Type *current_widget_class = 0; - -/** - Create and add a new Widget Class node. - \param[in] strategy add after current or as last child - \return new node - */ -Fl_Type *Fl_Widget_Class_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) p = p->parent; - while (p && (!p->is_decl_block() || (p->is_widget() && p->is_class()))) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_Widget_Class_Type *myo = new Fl_Widget_Class_Type(); - myo->name("UserInterface"); - - if (!this->o) {// template widget - this->o = new Fl_Window(100,100); - Fl_Group::current(0); - } - myo->factory = this; - myo->drag = 0; - myo->numselected = 0; - Overlay_Window *w = new Overlay_Window(100, 100); - w->size_range(10, 10); - w->window = myo; - myo->o = w; - myo->add(anchor, strategy); - myo->modal = 0; - myo->non_modal = 0; - myo->wc_relative = 0; - - return myo; -} - -void Fl_Widget_Class_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Window_Type::write_properties(f); - if (wc_relative==1) - f.write_string("position_relative"); - else if (wc_relative==2) - f.write_string("position_relative_rescale"); -} - -void Fl_Widget_Class_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"position_relative")) { - wc_relative = 1; - } else if (!strcmp(c,"position_relative_rescale")) { - wc_relative = 2; - } else { - Fl_Window_Type::read_property(f, c); - } -} - -// Convert A::B::C::D to D (i.e. keep only innermost name) -// This is useful for classes that contain a namespace component -static const char *trimclassname(const char *n) { - if (!n) - return NULL; - const char *nn; - while((nn = strstr(n, "::"))) { - n = nn + 2; - } - return(n); -} - - -void Fl_Widget_Class_Type::write_code1(fld::io::Code_Writer& f) { -#if 0 - Fl_Widget_Type::write_code1(fld::io::Code_Writer& f); -#endif // 0 - - current_widget_class = this; - write_public_state = 1; - - const char *c = subclass(); - if (!c) c = "Fl_Group"; - - f.write_c("\n"); - write_comment_h(f); - f.write_h("\nclass %s : public %s {\n", name(), c); - if (strstr(c, "Window")) { - f.write_h("%svoid _%s();\n", f.indent(1), trimclassname(name())); - f.write_h("public:\n"); - f.write_h("%s%s(int X, int Y, int W, int H, const char *L = 0);\n", f.indent(1), trimclassname(name())); - f.write_h("%s%s(int W, int H, const char *L = 0);\n", f.indent(1), trimclassname(name())); - f.write_h("%s%s();\n", f.indent(1), trimclassname(name())); - - // a constructor with all four dimensions plus label - f.write_c("%s::%s(int X, int Y, int W, int H, const char *L) :\n", name(), trimclassname(name())); - f.write_c("%s%s(X, Y, W, H, L)\n{\n", f.indent(1), c); - f.write_c("%s_%s();\n", f.indent(1), trimclassname(name())); - f.write_c("}\n\n"); - - // a constructor with just the size and label. The window manager will position the window - f.write_c("%s::%s(int W, int H, const char *L) :\n", name(), trimclassname(name())); - f.write_c("%s%s(0, 0, W, H, L)\n{\n", f.indent(1), c); - f.write_c("%sclear_flag(16);\n", f.indent(1)); - f.write_c("%s_%s();\n", f.indent(1), trimclassname(name())); - f.write_c("}\n\n"); - - // a constructor that takes size and label from the Fluid database - f.write_c("%s::%s() :\n", name(), trimclassname(name())); - f.write_c("%s%s(0, 0, %d, %d, ", f.indent(1), c, o->w(), o->h()); - const char *cstr = label(); - if (cstr) f.write_cstring(cstr); - else f.write_c("0"); - f.write_c(")\n{\n"); - f.write_c("%sclear_flag(16);\n", f.indent(1)); - f.write_c("%s_%s();\n", f.indent(1), trimclassname(name())); - f.write_c("}\n\n"); - - f.write_c("void %s::_%s() {\n", name(), trimclassname(name())); -// f.write_c("%s%s *w = this;\n", f.indent(1), name()); - } else { - f.write_h("public:\n"); - f.write_h("%s%s(int X, int Y, int W, int H, const char *L = 0);\n", - f.indent(1), trimclassname(name())); - f.write_c("%s::%s(int X, int Y, int W, int H, const char *L) :\n", name(), trimclassname(name())); - if (wc_relative==1) - f.write_c("%s%s(0, 0, W, H, L)\n{\n", f.indent(1), c); - else if (wc_relative==2) - f.write_c("%s%s(0, 0, %d, %d, L)\n{\n", f.indent(1), c, o->w(), o->h()); - else - f.write_c("%s%s(X, Y, W, H, L)\n{\n", f.indent(1), c); - } - -// f.write_c("%s%s *o = this;\n", f.indent(1), name()); - - f.indentation++; - write_widget_code(f); -} - -/** - Write the C++ code that comes after the children of the window are written. - \param f the source code output stream - */ -void Fl_Widget_Class_Type::write_code2(fld::io::Code_Writer& f) { - // make the window modal or non-modal - if (modal) { - f.write_c("%sset_modal();\n", f.indent()); - } else if (non_modal) { - f.write_c("%sset_non_modal();\n", f.indent()); - } - // clear the window border - if (!((Fl_Window*)o)->border()) f.write_c("%sclear_border();\n", f.indent()); - // set the xclass of the window - if (xclass) { - f.write_c("%sxclass(", f.indent()); - f.write_cstring(xclass); - f.write_c(");\n"); - } - // make the window resizable - if (((Fl_Window*)o)->resizable() == o) - f.write_c("%sresizable(this);\n", f.indent()); - // insert extra code from user - write_extra_code(f); - // stop adding widgets to this window - f.write_c("%send();\n", f.indent()); - // reposition or resize the Widget Class to fit into the target - if (wc_relative==1) - f.write_c("%sposition(X, Y);\n", f.indent()); - else if (wc_relative==2) - f.write_c("%sresize(X, Y, W, H);\n", f.indent()); - f.indentation--; - f.write_c("}\n"); -} - - -//////////////////////////////////////////////////////////////// -// live mode support - -Fl_Widget *Fl_Window_Type::enter_live_mode(int) { - Fl_Window *win = new Fl_Window(10, 10, o->w(), o->h()); - return propagate_live_mode(win); -} - -void Fl_Window_Type::leave_live_mode() { -} - -/** - copy all properties from the edit widget to the live widget - */ -void Fl_Window_Type::copy_properties() { - Fl_Window *self = static_cast(o); - Fl_Window *live = static_cast(live_widget); - if (self->resizable() == self) - live->resizable(live); - Fl_Widget_Type::copy_properties(); -} diff --git a/fluid/nodes/Fl_Window_Type.h b/fluid/nodes/Fl_Window_Type.h deleted file mode 100644 index 0dcb9e96e..000000000 --- a/fluid/nodes/Fl_Window_Type.h +++ /dev/null @@ -1,157 +0,0 @@ -// -// Window type header file for the Fast Light Tool Kit (FLTK). -// -// Type for creating all subclasses of Fl_Widget -// This should have the widget pointer in it, but it is still in the -// Fl_Type base class. -// -// Copyright 1998-2023 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_FL_WINDOW_TYPE_H -#define _FLUID_FL_WINDOW_TYPE_H - -#include "nodes/Fl_Group_Type.h" - -class Fl_Widget_Class_Type; - -extern Fl_Menu_Item window_type_menu[]; -extern Fl_Widget_Class_Type *current_widget_class; - -void toggle_overlays(Fl_Widget *,void *); -void toggle_guides(Fl_Widget *,void *); -void toggle_restricted(Fl_Widget *,void *); -void show_project_cb(Fl_Widget *, void *); -void show_grid_cb(Fl_Widget *, void *); -void show_settings_cb(Fl_Widget *, void *); - -enum { - FD_LEFT = 1, // user drags the left side of the selection box - FD_RIGHT = 2, - FD_BOTTOM = 4, - FD_TOP = 8, - FD_DRAG = 16, // user drags the entire selection - FD_BOX = 32 // user creates a new selection box -}; - -class Fl_Window_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; -protected: - - Fl_Menu_Item* subtypes() FL_OVERRIDE {return window_type_menu;} - - friend class Overlay_Window; - int mx,my; // mouse position during dragging - int x1,y1; // initial position of selection box - int bx,by,br,bt; // bounding box of selection before snapping - int sx,sy,sr,st; // bounding box of selection after snapping to guides - int dx,dy; - int drag; // which parts of bbox are being moved - int numselected; // number of children selected - void draw_out_of_bounds(Fl_Widget_Type *group, int x, int y, int w, int h); - void draw_out_of_bounds(); - void draw_overlaps(); - void draw_overlay(); - void newdx(); - void newposition(Fl_Widget_Type *,int &x,int &y,int &w,int &h); - int handle(int); - void setlabel(const char *) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - Fl_Widget_Type *_make() FL_OVERRIDE {return 0;} // we don't call this - Fl_Widget *widget(int,int,int,int) FL_OVERRIDE {return 0;} - int recalc; // set by fix_overlay() - void moveallchildren(int key=0); - ID id() const FL_OVERRIDE { return ID_Window; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Window) ? true : super::is_a(inID); } - void open_(); - -public: - - Fl_Window_Type() : - mx(0), my(0), - x1(0), y1(0), - bx(0), by(0), br(0), bt(0), - sx(0), sy(0), sr(0), st(0), - dx(0), dy(0), - drag(0), - numselected(0), - recalc(0), - modal(0), non_modal(0), - xclass(NULL), - sr_min_w(0), sr_min_h(0), sr_max_w(0), sr_max_h(0) - { } - uchar modal, non_modal; - const char *xclass; // junk string, used for shortcut - - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "Fl_Window";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::Window";} - - void open() FL_OVERRIDE; - void ideal_size(int &w, int &h) FL_OVERRIDE; - - void fix_overlay(); // Update the bounding box, etc - uchar *read_image(int &ww, int &hh); // Read an image of the window - - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - int read_fdesign(const char*, const char*) FL_OVERRIDE; - - void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void remove_child(Fl_Type*) FL_OVERRIDE; - - int can_have_children() const FL_OVERRIDE {return 1;} - - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void leave_live_mode() FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; - - int sr_min_w, sr_min_h, sr_max_w, sr_max_h; - - static int popupx, popupy; -}; - -class Fl_Widget_Class_Type : private Fl_Window_Type -{ - typedef Fl_Window_Type super; -protected: - Fl_Menu_Item* subtypes() FL_OVERRIDE {return 0;} - -public: - Fl_Widget_Class_Type() { - write_public_state = 0; - wc_relative = 0; - } - // state variables for output: - char write_public_state; // true when public: has been printed - char wc_relative; // if 1, reposition all children, if 2, reposition and resize - - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "widget_class";} - ID id() const FL_OVERRIDE { return ID_Widget_Class; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Widget_Class) ? true : super::is_a(inID); } - int can_have_children() const FL_OVERRIDE {return 1;} - int is_code_block() const FL_OVERRIDE {return 1;} - int is_decl_block() const FL_OVERRIDE {return 1;} - int is_class() const FL_OVERRIDE {return 1;} -}; - -#endif // _FLUID_FL_WINDOW_TYPE_H diff --git a/fluid/nodes/Function_Node.cxx b/fluid/nodes/Function_Node.cxx new file mode 100644 index 000000000..8ffdbef01 --- /dev/null +++ b/fluid/nodes/Function_Node.cxx @@ -0,0 +1,2158 @@ +// +// C function Node 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 "nodes/Function_Node.h" + +#include "Fluid.h" +#include "proj/mergeback.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "nodes/Window_Node.h" +#include "nodes/Group_Node.h" +#include "panels/function_panel.h" +#include "rsrcs/comments.h" +#include "widgets/Node_Browser.h" + +#include +#include +#include +#include "../src/flstring.h" + +#include + + +/// Set a current class, so that the code of the children is generated correctly. +Class_Node *current_class = nullptr; + +/** + \brief Return 1 if the list contains a function with the given signature at the top level. + Widget_Node uses this to check if a callback by a certain signature is + already defined by the user within this file. If not, Widget_Node will + generate an `extern $sig$;` statement. + \param[in] rtype return type, can be nullptr to avoid checking (not used by Widget_Node) + \param[in] sig function signature + \return 1 if found. + */ +int has_toplevel_function(const char *rtype, const char *sig) { + Node *child; + for (child = Fluid.proj.tree.first; child; child = child->next) { + if (!child->is_in_class() && child->is_a(Type::Function)) { + const Function_Node *fn = (const Function_Node*)child; + if (fn->has_signature(rtype, sig)) + return 1; + } + } + return 0; +} + + +//////////////////////////////////////////////////////////////// +// quick check of any C code for legality, returns an error message + +static char buffer[128]; // for error messages + +/** + Check a quoted string contains a character. + This is used to find a matching " or ' in a string. + \param[inout] c start searching here, return where we found \c type + \param[in] type find this character + \return nullptr if the character was found, else a pointer to a static string + with an error message + */ +const char *_q_check(const char * & c, int type) { + for (;;) switch (*c++) { + case '\0': + sprintf(buffer,"missing %c",type); + return buffer; + case '\\': + if (*c) c++; + break; + default: + if (*(c-1) == type) return nullptr; + } +} + +/** + Check normal code, match brackets and parenthesis. + Recursively run a line of code and make sure that + {, [, ", ', and ( are matched. + \param[inout] c start searching here, return the end of the search + \param[in] type find this character match + \return nullptr if the character was found, else a pointer to a static string + with an error message + */ +const char *_c_check(const char * & c, int type) { + const char *d; + for (;;) switch (*c++) { + case 0: + if (!type) return nullptr; + sprintf(buffer, "missing '%c'", type); + return buffer; + case '/': + // Skip comments as needed... + if (*c == '/') { + while (*c != '\n' && *c) c++; + } else if (*c == '*') { + c++; + while ((*c != '*' || c[1] != '/') && *c) c++; + if (*c == '*') c+=2; + else { + return "missing '*/'"; + } + } + break; +// case '#': +// // treat cpp directives as a comment: +// // Matt: a '#' character can appear as a concatenation when defining macros +// // Matt: so instead we just silently ignore the '#' +// while (*c != '\n' && *c) c++; +// break; + case '{': +// // Matt: C++ does allow {} inside () now +// if (type==')') goto UNEXPECTED; + d = _c_check(c,'}'); + if (d) return d; + break; + case '(': + d = _c_check(c,')'); + if (d) return d; + break; + case '[': + d = _c_check(c,']'); + if (d) return d; + break; + case '\"': + d = _q_check(c,'\"'); + if (d) return d; + break; + case '\'': + d = _q_check(c,'\''); + if (d) return d; + break; + case '}': + case ')': + case ']': +// UNEXPECTED: + if (type == *(c-1)) return nullptr; + sprintf(buffer, "unexpected '%c'", *(c-1)); + return buffer; + } +} + +/** + Check legality of c code (sort of) and return error: + Make sure that {, ", ', and ( are matched. + \param[in] c start searching here + \param[in] type find this character match (default is 0) + \return nullptr if the character was found, else a pointer to a static string + with an error message + \note This function checks every conceivable line of code, which is not + always wanted. It can't differentiate characters in comments, and the + user may well intend to leave a curly bracket open + (i.e. namespace { ... } ). We should make this option user selectable. + */ +const char *c_check(const char *c, int type) { + return _c_check(c,type); +} + +// ---- Function_Node implementation + +/** \class Function_Node + Manage a C++ function node in the Fluid design. + + A function can have a signature (name followed by arguments), a return type + and a comment section. If can be local or global, and it can be declared a C + or C++ function. + */ + +/// Prototype for a function to be used by the factory. +Function_Node Function_Node::prototype; + +/** + Create a new function. + */ +Function_Node::Function_Node() : + Node(), + return_type(nullptr), + public_(0), + cdecl_(0), + constructor(0), + havewidgets(0) +{ } + +/** + Destructor. + */ +Function_Node::~Function_Node() { + if (return_type) free((void*)return_type); +} + +/** + Create a new function for the widget tree. + \param[in] strategy add new function after current or as last child + \return the new node + */ +Node *Function_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_decl_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + Function_Node *o = new Function_Node(); + o->name("make_window()"); + o->return_type = nullptr; + o->add(anchor, strategy); + o->factory = this; + o->public_ = 1; + o->cdecl_ = 0; + return o; +} + +/** + Write function specific properties to an .fl file. + - "private"/"public" indicates the state of the function + - "C" is written if we want a C signature instead of C++ + - "return_type" is followed by the return type of the function + */ +void Function_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + switch (public_) { + case 0: f.write_string("private"); break; + case 2: f.write_string("protected"); break; + } + if (cdecl_) f.write_string("C"); + if (return_type) { + f.write_string("return_type"); + f.write_word(return_type); + } +} + +/** + Read function specific properties fron an .fl file. + \param[in] c read from this string + */ +void Function_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"private")) { + public_ = 0; + } else if (!strcmp(c,"protected")) { + public_ = 2; + } else if (!strcmp(c,"C")) { + cdecl_ = 1; + } else if (!strcmp(c,"return_type")) { + storestring(f.read_word(),return_type); + } else { + Node::read_property(f, c); + } +} + +/** + Open the function_panel dialog box to edit this function. + */ +void Function_Node::open() { + // fill dialog box + if (!function_panel) make_function_panel(); + f_return_type_input->value(return_type); + f_name_input->value(name()); + if (is_in_class()) { + f_public_member_choice->value(public_); + f_public_member_choice->show(); + f_public_choice->hide(); + f_c_button->hide(); + } else { + f_public_choice->value(public_); + f_public_choice->show(); + f_public_member_choice->hide(); + f_c_button->show(); + } + f_c_button->value(cdecl_); + const char *c = comment(); + f_comment_input->buffer()->text(c?c:""); + function_panel->show(); + const char* message = nullptr; + for (;;) { // repeat as long as there are errors + // - message loop until OK or cancel is pressed + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == f_panel_cancel) goto BREAK2; + else if (w == f_panel_ok) break; + else if (!w) Fl::wait(); + } + // - check syntax + const char *c = f_name_input->value(); + while (isspace(*c)) c++; + message = c_check(c); + if (!message) { + const char *d = c; + for (; *d != '('; d++) if (isspace(*d) || !*d) break; + if (*c && *d != '(') + message = "must be 'name(arguments)'"; + } + if (!message) { + c = f_return_type_input->value(); + message = c_check(c); + } + // - alert user + if (message) { + int v = fl_choice("Potential syntax error detected: %s", + "Continue Editing", "Ignore Error", nullptr, message); + if (v==0) continue; // Continue Editing + //if (v==1) { } // Ignore Error and close dialog + } + // - copy dialog data to target variables + int mod = 0; + name(f_name_input->value()); + storestring(f_return_type_input->value(), return_type); + if (is_in_class()) { + if (public_ != f_public_member_choice->value()) { + mod = 1; + public_ = f_public_member_choice->value(); + redraw_browser(); + } + } else { + if (public_ != f_public_choice->value()) { + mod = 1; + public_ = f_public_choice->value(); + redraw_browser(); + } + } + if (cdecl_ != f_c_button->value()) { + mod = 1; + cdecl_ = f_c_button->value(); + } + c = f_comment_input->buffer()->text(); + if (c && *c) { + if (!comment() || strcmp(c, comment())) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(c); + } else { + if (comment()) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(nullptr); + } + if (c) free((void*)c); + if (mod) Fluid.proj.set_modflag(1); + break; + } +BREAK2: + function_panel->hide(); +} + +/** + Return 1 if the function is global. + \return 1 if public, 0 if local. + */ +int Function_Node::is_public() const { + return public_; +} + +static bool fd_isspace(int c) { + return (c>0 && c<128 && isspace(c)); +} + +// code duplication: see int is_id(char c) in code.cxx +static bool fd_iskeyword(int c) { + return (c>0 && c<128 && (isalnum(c) || c=='_')); +} + +// remove all function default parameters and `override` keyword +static void clean_function_for_implementation(char *out, const char *function_name) { + char *sptr = out; + const char *nptr = function_name; + int skips=0,skipc=0; + int nc=0,plevel=0; + bool arglist_done = false; + for (;*nptr; nc++,nptr++) { + if (arglist_done && fd_isspace(nptr[0])) { + // skip `override` and `FL_OVERRIDE` keywords if they are following the list of arguments + if (strncmp(nptr+1, "override", 8)==0 && !fd_iskeyword(nptr[9])) { nptr += 8; continue; } + else if (strncmp(nptr+1, "FL_OVERRIDE", 11)==0 && !fd_iskeyword(nptr[12])) { nptr += 11; continue; } + } + if (!skips && *nptr=='(') plevel++; + else if (!skips && *nptr==')') { plevel--; if (plevel==0) arglist_done = true; } + if ( *nptr=='"' && !(nc && *(nptr-1)=='\\') ) + skips = skips ? 0 : 1; + else if(!skips && *nptr=='\'' && !(nc && *(nptr-1)=='\\')) + skipc = skipc ? 0 : 1; + if(!skips && !skipc && plevel==1 && *nptr =='=' && !(nc && *(nptr-1)=='\'') ) { // ignore '=' case + while(*++nptr && (skips || skipc || ( (*nptr!=',' && *nptr!=')') || plevel!=1) )) { + if ( *nptr=='"' && *(nptr-1)!='\\' ) + skips = skips ? 0 : 1; + else if(!skips && *nptr=='\'' && *(nptr-1)!='\\') + skipc = skipc ? 0 : 1; + if (!skips && !skipc && *nptr=='(') plevel++; + else if (!skips && *nptr==')') plevel--; + } + if (*nptr==')') if (--plevel==0) arglist_done = true; + } + if (sptr < (out + 1024 - 1)) *sptr++ = *nptr; + } + *sptr = '\0'; +} + + +/** + Write the code for the source and the header file. + This writes the code that goes \b before all children of this class. + \see write_code2(fld::io::Code_Writer& f) + */ +void Function_Node::write_code1(fld::io::Code_Writer& f) { + constructor=0; + havewidgets = 0; + Node *child; + // if the function has no children (hence no body), Fluid will not generate + // the function either. This is great if you decide to implement that function + // inside another module + char havechildren = 0; + for (child = next; child && child->level > level; child = child->next) { + havechildren = 1; + if (child->is_widget()) { + havewidgets = 1; + break; + } + } + if (havechildren) + f.write_c("\n"); + if (ismain()) { + if (havechildren) + f.write_c("int main(int argc, char **argv) {\n"); + } else { + const char* rtype = return_type; + const char* star = ""; + // from matt: let the user type "static " at the start of type + // in order to declare a static method; + int is_static = 0; + int is_virtual = 0; + if (rtype) { + if (!strcmp(rtype,"static")) {is_static = 1; rtype = nullptr;} + else if (!strncmp(rtype, "static ",7)) {is_static = 1; rtype += 7;} + } + if (rtype) { + if (!strcmp(rtype, "virtual")) {is_virtual = 1; rtype = nullptr;} + else if (!strncmp(rtype, "virtual ",8)) {is_virtual = 1; rtype += 8;} + } + if (!rtype) { + if (havewidgets) { + rtype = subclassname(child); + star = "*"; + } else rtype = "void"; + } + + const char* k = class_name(0); + if (k) { + f.write_public(public_); + if (havechildren) + write_comment_c(f); + if (name()[0] == '~') + constructor = 1; + else { + size_t n = strlen(k); + if (!strncmp(name(), k, n) && name()[n] == '(') constructor = 1; + } + f.write_h("%s", f.indent(1)); + if (is_static) f.write_h("static "); + if (is_virtual) f.write_h("virtual "); + if (!constructor) { + f.write_h("%s%s ", rtype, star); + if (havechildren) + f.write_c("%s%s ", rtype, star); + } + + // if this is a subclass, only f.write_h() the part before the ':' + char s[1024], *sptr = s; + char *nptr = (char *)name(); + + while (*nptr) { + if (*nptr == ':') { + if (nptr[1] != ':') break; + // Copy extra ":" for "class::member"... + *sptr++ = *nptr++; + } + *sptr++ = *nptr++; + } + *sptr = '\0'; + + if (s[strlen(s)-1] == '}') { // special case for inlined functions + f.write_h("%s\n", s); + } else { + f.write_h("%s;\n", s); + } + if (havechildren) { + clean_function_for_implementation(s, name()); + f.write_c("%s::%s {\n", k, s); + } + } else { + if (havechildren) + write_comment_c(f); + if (public_==1) { + if (cdecl_) + f.write_h("extern \"C\" { %s%s %s; }\n", rtype, star, name()); + else + f.write_h("%s%s %s;\n", rtype, star, name()); + } else if (public_==2) { + // write neither the prototype nor static, the function may be declared elsewhere + } else { + if (havechildren) + f.write_c("static "); + } + + // write everything but the default parameters (if any) + char s[1024]; + if (havechildren) { + clean_function_for_implementation(s, name()); + f.write_c("%s%s %s {\n", rtype, star, s); + } + } + } + + if (havewidgets && child && !child->name()) + f.write_c("%s%s* w;\n", f.indent(1), subclassname(child)); + f.indentation++; +} + +/** + Write the code for the source and the header file. + This writes the code that goes \b after all children of this class. + \see write_code1(fld::io::Code_Writer& f) + */ +void Function_Node::write_code2(fld::io::Code_Writer& f) { + Node *child; + const char *var = "w"; + char havechildren = 0; + for (child = next; child && child->level > level; child = child->next) { + havechildren = 1; + if (child->is_a(Type::Window) && child->name()) var = child->name(); + } + + if (ismain()) { + if (havewidgets) + f.write_c("%s%s->show(argc, argv);\n", f.indent(1), var); + if (havechildren) + f.write_c("%sreturn Fl::run();\n", f.indent(1)); + } else if (havewidgets && !constructor && !return_type) { + f.write_c("%sreturn %s;\n", f.indent(1), var); + } + if (havechildren) + f.write_c("}\n"); + f.indentation = 0; +} + +/** + Check if the return type and signatures match. + \param[in] rtype function return type + \param[in] sig function name followed by arguments + \return 1 if they match, 0 if not + */ +int Function_Node::has_signature(const char *rtype, const char *sig) const { + if (rtype && !return_type) return 0; + if (!name()) return 0; + if ( (rtype==nullptr || strcmp(return_type, rtype)==0) + && fl_filename_match(name(), sig)) { + return 1; + } + return 0; +} + +// ---- Code_Node declaration + +/** \class Code_Node + Manage a block of C++ code in the Fluid design. + + This node manages an arbitrary block of code inside a function that will + be written into the source code file. Fl_Code_Block has no comment field. + However, the first line of code will be shown in the widget browser. + */ + +/// Prototype for code to be used by the factory. +Code_Node Code_Node::prototype; + +/** + Constructor. + */ +Code_Node::Code_Node() : + cursor_position_(0), + code_input_scroll_row(0), + code_input_scroll_col(0) +{} + +/** + Make a new code node. + If the parent node is not a function, a message box will pop up and + the request will be ignored. + \param[in] strategy add code after current or as last child + \return new Code node + */ +Node *Code_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_code_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + if (!p) { + fl_message("Please select a function"); + return nullptr; + } + Code_Node *o = new Code_Node(); + o->name("printf(\"Hello, World!\\n\");"); + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Open the code_panel or an external editor to edit this code section. + */ +void Code_Node::open() { + // Using an external code editor? Open it.. + if ( Fluid.use_external_editor && Fluid.external_editor_command[0] ) { + const char *cmd = Fluid.external_editor_command; + const char *code = name(); + if (!code) code = ""; + if ( editor_.open_editor(cmd, code) == 0 ) + return; // return if editor opened ok, fall thru to built-in if not + } + // Use built-in code editor.. + if (!code_panel) make_code_panel(); + const char *text = name(); + code_input->buffer()->text( text ? text : "" ); + code_input->insert_position(cursor_position_); + code_input->scroll(code_input_scroll_row, code_input_scroll_col); + code_panel->show(); + const char* message = nullptr; + for (;;) { // repeat as long as there are errors + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == code_panel_cancel) goto BREAK2; + else if (w == code_panel_ok) break; + else if (!w) Fl::wait(); + } + char*c = code_input->buffer()->text(); + message = c_check(c); + if (message) { + int v = fl_choice("Potential syntax error detected: %s", + "Continue Editing", "Ignore Error", nullptr, message); + if (v==0) continue; // Continue Editing + //if (v==1) { } // Ignore Error and close dialog + } + name(c); + free(c); + break; + } + cursor_position_ = code_input->insert_position(); + code_input_scroll_row = code_input->scroll_row(); + code_input_scroll_col = code_input->scroll_col(); +BREAK2: + code_panel->hide(); +} + +/** + Grab changes from an external editor and write this node. + */ +void Code_Node::write(fld::io::Project_Writer &f) { + // External editor changes? If so, load changes into ram, update mtime/size + if ( handle_editor_changes() == 1 ) { + Fluid.main_window->redraw(); // tell fluid to redraw; edits may affect tree's contents + } + Node::write(f); +} + +/** + Write the code block with the correct indentation. + */ +void Code_Node::write_code1(fld::io::Code_Writer& f) { + // External editor changes? If so, load changes into ram, update mtime/size + if ( handle_editor_changes() == 1 ) { + Fluid.main_window->redraw(); // tell fluid to redraw; edits may affect tree's contents + } + // Matt: disabled f.tag(FD_TAG_GENERIC, 0); + f.write_c_indented(name(), 0, '\n'); + // Matt: disabled f.tag(FD_TAG_CODE, get_uid()); +} + +/** + See if external editor is open. + */ +int Code_Node::is_editing() { + return editor_.is_editing(); +} + +/** + Reap the editor's pid + \return -2: editor not open + \return -1: wait failed + \return 0: process still running + \return \>0: process finished + reaped (returns pid) + */ +int Code_Node::reap_editor() { + return editor_.reap_editor(); +} + +/** + Handle external editor file modifications. + If changed, record keeping is updated and file's contents is loaded into ram + \return 0: file unchanged or not editing + \return 1: file changed, internal records updated, 'code' has new content + \return -1: error getting file info (get_ms_errmsg() has reason) + \todo Figure out how saving a fluid file can be intercepted to grab + current contents of editor file.. + */ +int Code_Node::handle_editor_changes() { + const char *newcode = nullptr; + switch ( editor_.handle_changes(&newcode) ) { + case 1: { // (1)=changed + name(newcode); // update value in ram + free((void*)newcode); + return 1; + } + case -1: return -1; // (-1)=error -- couldn't read file (dialog showed reason) + default: break; // (0)=no change + } + return 0; +} + +// ---- CodeBlock_Node implementation + +/** \class CodeBlock_Node + Manage two blocks of C++ code enclosing its children. + + This node manages two lines of code that enclose all children + of this node. This is usually an if..then clause. + + \todo this node could support multiple lines of code for each block. + */ + +/// Prototype for a block of code to be used by the factory. +CodeBlock_Node CodeBlock_Node::prototype; + +/** + Constructor. + */ +CodeBlock_Node::CodeBlock_Node() : + Node(), + after(nullptr) +{ } + +/** + Destructor. + */ +CodeBlock_Node::~CodeBlock_Node() { + if (after) + free((void*)after); +} + +/** + Make a new code block. + If the parent node is not a function or another codeblock, a message box will + pop up and the request will be ignored. + \param[in] strategy add after current or as last child + \return new CodeBlock + */ +Node *CodeBlock_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_code_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + if (!p) { + fl_message("Please select a function"); + return nullptr; + } + CodeBlock_Node *o = new CodeBlock_Node(); + o->name("if (test())"); + o->after = nullptr; + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Write the specific properties for this node. + - "after" is followed by the code that comes after the children + The "before" code is stored in the name() field. + */ +void CodeBlock_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + if (after) { + f.write_string("after"); + f.write_word(after); + } +} + +/** + Read the node specific properties. + */ +void CodeBlock_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"after")) { + storestring(f.read_word(),after); + } else { + Node::read_property(f, c); + } +} + +/** + Open the codeblock_panel. + */ +void CodeBlock_Node::open() { + if (!codeblock_panel) make_codeblock_panel(); + code_before_input->value(name()); + code_after_input->value(after); + codeblock_panel->show(); + const char* message = nullptr; + for (;;) { // repeat as long as there are errors + // event loop + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == codeblock_panel_cancel) goto BREAK2; + else if (w == codeblock_panel_ok) break; + else if (!w) Fl::wait(); + } + // check for syntax errors + message = c_check(code_before_input->value()); + if (!message) { + message = c_check(code_after_input->value()); + } + // alert user + if (message) { + int v = fl_choice("Potential syntax error detected: %s", + "Continue Editing", "Ignore Error", nullptr, message); + if (v==0) continue; // Continue Editing + //if (v==1) { } // Ignore Error and close dialog + } + // write to variables + name(code_before_input->value()); + storestring(code_after_input->value(), after); + break; + } +BREAK2: + codeblock_panel->hide(); +} + +/** + Write the "before" code. + */ +void CodeBlock_Node::write_code1(fld::io::Code_Writer& f) { + const char* c = name(); + f.write_c("%s%s {\n", f.indent(), c ? c : ""); + f.indentation++; +} + +/** + Write the "after" code. + */ +void CodeBlock_Node::write_code2(fld::io::Code_Writer& f) { + f.indentation--; + if (after) f.write_c("%s} %s\n", f.indent(), after); + else f.write_c("%s}\n", f.indent()); +} + +// ---- Decl_Node declaration + +/** \class Decl_Node + Manage the C/C++ declaration of a variable. + + This node manages a single line of code that can be in the header or the source + code, and can be made static. + + \todo this node could support multiple lines. + */ + +/// Prototype for a declaration to be used by the factory. +Decl_Node Decl_Node::prototype; + +/** + Constructor. + */ +Decl_Node::Decl_Node() : + public_(0), + static_(1) +{ } + +/** + Return 1 if this declaration and its parents are public. + */ +int Decl_Node::is_public() const +{ + Node *p = parent; + while (p && !p->is_decl_block()) p = p->parent; + if(p && p->is_public() && public_) + return public_; + else if(!p) + return public_; + return 0; +} + +/** + Make a new declaration. + \param[in] strategy add after current or as last child + \return new Declaration node + */ +Node *Decl_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_decl_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + Decl_Node *o = new Decl_Node(); + o->public_ = 0; + o->static_ = 1; + o->name("int x;"); + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Write the specific properties. + - "private"/"public"/"protected" + - "local"/"global" if this is static or not + */ +void Decl_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + switch (public_) { + case 0: f.write_string("private"); break; + case 1: f.write_string("public"); break; + case 2: f.write_string("protected"); break; + } + if (static_) + f.write_string("local"); + else + f.write_string("global"); +} + +/** + Read the specific properties. + */ +void Decl_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"public")) { + public_ = 1; + } else if (!strcmp(c,"private")) { + public_ = 0; + } else if (!strcmp(c,"protected")) { + public_ = 2; + } else if (!strcmp(c,"local")) { + static_ = 1; + } else if (!strcmp(c,"global")) { + static_ = 0; + } else { + Node::read_property(f, c); + } +} + +/** + Open the decl_panel to edit this node. + */ +void Decl_Node::open() { + if (!decl_panel) make_decl_panel(); + decl_input->buffer()->text(name()); + if (is_in_class()) { + decl_class_choice->value(public_); + decl_class_choice->show(); + decl_choice->hide(); + } else { + decl_choice->value((public_&1)|((static_&1)<<1)); + decl_choice->show(); + decl_class_choice->hide(); + } + const char *c = comment(); + decl_comment_input->buffer()->text(c?c:""); + decl_panel->show(); + const char* message = nullptr; + for (;;) { // repeat as long as there are errors + // event loop + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == decl_panel_cancel) goto BREAK2; + else if (w == decl_panel_ok) break; + else if (!w) Fl::wait(); + } + // check values + const char*c = decl_input->buffer()->text(); + while (isspace(*c)) c++; + message = c_check(c&&c[0]=='#' ? c+1 : c); + // alert user + if (message) { + int v = fl_choice("Potential syntax error detected: %s", + "Continue Editing", "Ignore Error", nullptr, message); + if (v==0) continue; // Continue Editing + //if (v==1) { } // Ignore Error and close dialog + } + // copy vlaues + name(c); + if (is_in_class()) { + if (public_!=decl_class_choice->value()) { + Fluid.proj.set_modflag(1); + public_ = decl_class_choice->value(); + } + } else { + if (public_!=(decl_choice->value()&1)) { + Fluid.proj.set_modflag(1); + public_ = (decl_choice->value()&1); + } + if (static_!=((decl_choice->value()>>1)&1)) { + Fluid.proj.set_modflag(1); + static_ = ((decl_choice->value()>>1)&1); + } + } + c = decl_comment_input->buffer()->text(); + if (c && *c) { + if (!comment() || strcmp(c, comment())) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(c); + } else { + if (comment()) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(nullptr); + } + if (c) free((void*)c); + break; + } +BREAK2: + decl_panel->hide(); +} + +/** + Write the code to the source and header files. + \todo There are a lot of side effect in this node depending on the given text + and the parent node. They need to be understood and documented. + */ +void Decl_Node::write_code1(fld::io::Code_Writer& f) { + const char* c = name(); + if (!c) return; + // handle a few keywords differently if inside a class + if (is_in_class() && ( (!strncmp(c,"class",5) && isspace(c[5])) + || (!strncmp(c,"typedef",7) && isspace(c[7])) + || (!strncmp(c,"FL_EXPORT",9) && isspace(c[9])) + || (!strncmp(c,"struct",6) && isspace(c[6])) + || (!strncmp(c,"enum",4) && isspace(c[4])) + ) ) { + f.write_public(public_); + write_comment_h(f, f.indent(1)); + f.write_h("%s%s\n", f.indent(1), c); + return; + } + // handle putting #include, extern, using or typedef into decl: + if ( (!isalpha(*c) && *c != '~') + || (!strncmp(c,"extern",6) && isspace(c[6])) + || (!strncmp(c,"class",5) && isspace(c[5])) + || (!strncmp(c,"typedef",7) && isspace(c[7])) + || (!strncmp(c,"using",5) && isspace(c[5])) + || (!strncmp(c,"FL_EXPORT",9) && isspace(c[9])) + // || !strncmp(c,"struct",6) && isspace(c[6]) + ) { + if (public_) { + write_comment_h(f); + f.write_h("%s\n", c); + } else { + write_comment_c(f); + f.write_c("%s\n", c); + } + return; + } + // find the first C++ style comment + const char* e = c+strlen(c), *csc = c; + while (cscc && e[-1]==' ') e--; + if (class_name(1)) { + f.write_public(public_); + write_comment_h(f, f.indent(1)); + f.write_hc(f.indent(1), int(e-c), c, csc); + } else { + if (public_) { + if (static_) + f.write_h("extern "); + else + write_comment_h(f); + f.write_hc("", int(e-c), c, csc); + + if (static_) { + write_comment_c(f); + f.write_cc("", int(e-c), c, csc); + } + } else { + write_comment_c(f); + if (static_) + f.write_c("static "); + f.write_cc("", int(e-c), c, csc); + } + } +} + +// ---- Data_Node declaration + +/** \class Data_Node + Manage data from an external arbitrary file. + + The content of the file will be stored in binary inside the generated + code. This can be used to store images inline in the source code, + */ + +/// Prototype for a data node to be used by the factory. +Data_Node Data_Node::prototype; + +/** + Constructor. + */ +Data_Node::Data_Node() : + Decl_Node(), + filename_(nullptr), + text_mode_(0) +{ } + +/** + Destructor. + */ +Data_Node::~Data_Node() { + if (filename_) + free((void*)filename_); +} + +/** + Create an empty inline data node. + \param[in] strategy add after current or as last child + \return new inline data node + */ +Node *Data_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_decl_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + Data_Node *o = new Data_Node(); + o->public_ = 1; + o->static_ = 1; + o->filename_ = nullptr; + o->text_mode_ = 0; + o->name("myInlineData"); + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Write additional properties. + - "filename" followed by the filename of the file to inline + - "textmode" if data is written in ASCII vs. binary + */ +void Data_Node::write_properties(fld::io::Project_Writer &f) { + Decl_Node::write_properties(f); + if (filename_) { + f.write_string("filename"); + f.write_word(filename_); + } + if (text_mode_ == 1) { + f.write_string("textmode"); + } + if (text_mode_ == 2) { + f.write_string("compressed"); + } +} + +/** + Read specific properties. + */ +void Data_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"filename")) { + storestring(f.read_word(), filename_, 1); + } else if (!strcmp(c,"textmode")) { + text_mode_ = 1; + } else if (!strcmp(c,"compressed")) { + text_mode_ = 2; + } else { + Decl_Node::read_property(f, c); + } +} + +/** + Open the data_panel to edit this node. + */ +void Data_Node::open() { + if (!data_panel) make_data_panel(); + data_input->value(name()); + if (is_in_class()) { + data_class_choice->value(public_); + data_class_choice->show(); + data_choice->hide(); + } else { + data_choice->value((public_&1)|((static_&1)<<1)); + data_choice->show(); + data_class_choice->hide(); + } + data_mode->value(text_mode_); + data_filename->value(filename_?filename_:""); + const char *c = comment(); + data_comment_input->buffer()->text(c?c:""); + data_panel->show(); + for (;;) { // repeat as long as there are errors + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == data_panel_cancel) goto BREAK2; + else if (w == data_panel_ok) break; + else if (w == data_filebrowser) { + Fluid.proj.enter_project_dir(); + const char *fn = fl_file_chooser("Load Inline Data", nullptr, data_filename->value(), 1); + Fluid.proj.leave_project_dir(); + if (fn) { + if (strcmp(fn, data_filename->value())) + Fluid.proj.set_modflag(1); + data_filename->value(fn); + } + } + else if (!w) Fl::wait(); + } + // store the variable name: + const char*c = data_input->value(); + char *s = fl_strdup(c), *p = s, *q, *n; + for (;;++p) { // remove leading spaces + if (!isspace((unsigned char)(*p))) break; + } + n = p; + if ( (!isalpha((unsigned char)(*p))) && ((*p)!='_') && ((*p)!=':') ) goto OOPS; + ++p; + for (;;++p) { + if ( (!isalnum((unsigned char)(*p))) && ((*p)!='_') && ((*p)!=':') ) break; + } + q = p; + for (;;++q) { + if (!*q) break; + if (!isspace((unsigned char)(*q))) goto OOPS; + } + *p = 0; // remove trailing spaces + if (n==q) { + OOPS: + int v = fl_choice("%s", + "Continue Editing", "Ignore Error", nullptr, + "Variable name must be a C identifier"); + if (v==0) { free(s); continue; } // Continue Editing + //if (v==1) { } // Ignore Error and close dialog + } + Fluid.proj.undo.checkpoint(); + name(n); + free(s); + // store flags + if (is_in_class()) { + if (public_!=data_class_choice->value()) { + Fluid.proj.set_modflag(1); + public_ = data_class_choice->value(); + } + } else { + if (public_!=(data_choice->value()&1)) { + Fluid.proj.set_modflag(1); + public_ = (data_choice->value()&1); + } + if (static_!=((data_choice->value()>>1)&1)) { + Fluid.proj.set_modflag(1); + static_ = ((data_choice->value()>>1)&1); + } + } + text_mode_ = data_mode->value(); + if (text_mode_ < 0) text_mode_ = 0; + if (text_mode_ > 2) text_mode_ = 2; + // store the filename + c = data_filename->value(); + if (filename_ && strcmp(filename_, data_filename->value())) + Fluid.proj.set_modflag(1); + else if (!filename_ && *c) + Fluid.proj.set_modflag(1); + if (filename_) { free((void*)filename_); filename_ = nullptr; } + if (c && *c) filename_ = fl_strdup(c); + // store the comment + c = data_comment_input->buffer()->text(); + if (c && *c) { + if (!comment() || strcmp(c, comment())) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(c); + } else { + if (comment()) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(nullptr); + } + if (c) free((void*)c); + Fluid.proj.set_modflag(1); + break; + } +BREAK2: + data_panel->hide(); +} + +/** + Write the content of the external file inline into the source code. + */ +void Data_Node::write_code1(fld::io::Code_Writer& f) { + const char *message = nullptr; + const char *c = name(); + if (!c) return; + const char *fn = filename_; + char *data = nullptr; + int nData = -1; + int uncompressedDataSize = 0; + // path should be set correctly already + if (filename_ && !f.write_codeview) { + Fluid.proj.enter_project_dir(); + FILE *f = fl_fopen(filename_, "rb"); + Fluid.proj.leave_project_dir(); + if (!f) { + message = "Can't include data from file. Can't open"; + } else { + fseek(f, 0, SEEK_END); + nData = (int)ftell(f); + fseek(f, 0, SEEK_SET); + if (nData) { + data = (char*)calloc(nData, 1); + if (fread(data, nData, 1, f)==0) { /* use default */ } + if (text_mode_ == 2) { + uncompressedDataSize = nData; + uLong nzData = compressBound(nData); + Bytef *zdata = (Bytef*)::malloc(nzData); + if (compress(zdata, &nzData, (Bytef*)data, nData) != Z_OK) { /* error */ } + ::free(data); + data = (char*)zdata; + nData = (int)nzData; + } + } + fclose(f); + } + } else { + fn = filename_ ? filename_ : ""; + } + if (is_in_class()) { + f.write_public(public_); + if (text_mode_ == 1) { + f.write_h("%sstatic const char *%s;\n", f.indent(1), c); + f.write_c("\n"); + write_comment_c(f); + f.write_c("const char *%s::%s = /* text inlined from %s */\n", class_name(1), c, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cstring(data, nData); + } else if (text_mode_ == 2) { + f.write_h("%sstatic int %s_size;\n", f.indent(1), c); + f.write_h("%sstatic unsigned char %s[%d];\n", f.indent(1), c, nData); + f.write_c("\n"); + write_comment_c(f); + f.write_c("int %s::%s_size = %d;\n", class_name(1), c, uncompressedDataSize); + f.write_c("unsigned char %s::%s[%d] = /* data compressed and inlined from %s */\n", class_name(1), c, nData, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cdata(data, nData); + } else { + f.write_h("%sstatic unsigned char %s[%d];\n", f.indent(1), c, nData); + f.write_c("\n"); + write_comment_c(f); + f.write_c("unsigned char %s::%s[%d] = /* data inlined from %s */\n", class_name(1), c, nData, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cdata(data, nData); + } + f.write_c(";\n"); + } else { + // the "header only" option does not apply here! + if (public_) { + if (static_) { + if (text_mode_ == 1) { + f.write_h("extern const char *%s;\n", c); + f.write_c("\n"); + write_comment_c(f); + f.write_c("const char *%s = /* text inlined from %s */\n", c, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cstring(data, nData); + } else if (text_mode_ == 2) { + f.write_h("extern int %s_size;\n", c); + f.write_h("extern unsigned char %s[%d];\n", c, nData); + f.write_c("\n"); + write_comment_c(f); + f.write_c("int %s_size = %d;\n", c, uncompressedDataSize); + f.write_c("unsigned char %s[%d] = /* data compressed and inlined from %s */\n", c, nData, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cdata(data, nData); + } else { + f.write_h("extern unsigned char %s[%d];\n", c, nData); + f.write_c("\n"); + write_comment_c(f); + f.write_c("unsigned char %s[%d] = /* data inlined from %s */\n", c, nData, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cdata(data, nData); + } + f.write_c(";\n"); + } else { + write_comment_h(f); + f.write_h("#error Unsupported declaration loading inline data %s\n", fn); + if (text_mode_ == 1) + f.write_h("const char *%s = \"abc...\";\n", c); + else + f.write_h("unsigned char %s[3] = { 1, 2, 3 };\n", c); + } + } else { + f.write_c("\n"); + write_comment_c(f); + if (static_) + f.write_c("static "); + if (text_mode_ == 1) { + f.write_c("const char *%s = /* text inlined from %s */\n", c, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cstring(data, nData); + } else if (text_mode_ == 2) { + f.write_c("int %s_size = %d;\n", c, uncompressedDataSize); + if (static_) f.write_c("static "); + f.write_c("unsigned char %s[%d] = /* data compressed and inlined from %s */\n", c, nData, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cdata(data, nData); + } else { + f.write_c("unsigned char %s[%d] = /* data inlined from %s */\n", c, nData, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cdata(data, nData); + } + f.write_c(";\n"); + } + } + // if we are in interactive mode, we pop up a warning dialog + // giving the error: (Fluid.batch_mode && !write_codeview) ??? + if (message && !f.write_codeview) { + if (Fluid.batch_mode) + fprintf(stderr, "FLUID ERROR: %s %s\n", message, fn); + else + fl_alert("%s\n%s\n", message, fn); + } + if (data) free(data); +} + +// ---- DeclBlock_Node declaration + +/** \class DeclBlock_Node + Manage a declaration block. + + Declaration blocks have two text field that are written before and after + the children of this block. This block is located at the top level and + is written to the source file, and to the header file, if declared public. + */ + +/// Prototype for a declaration block to be used by the factory. +DeclBlock_Node DeclBlock_Node::prototype; + +/** + Constructor. + */ +DeclBlock_Node::DeclBlock_Node() : + Node(), + after(nullptr), + write_map_(CODE_IN_SOURCE) +{ } + +/** + Destructor. + */ +DeclBlock_Node::~DeclBlock_Node() { + if (after) + ::free((void*)after); +} + +/** + Return 1 if this block is public. + */ +int DeclBlock_Node::is_public() const { + return ((write_map_&CODE_IN_HEADER) != 0); +} + +/** + Create a new declaration block. + \param[in] strategy add after current or as last child + \return new Declaration Block node + */ +Node *DeclBlock_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) p = p->parent; + while (p && !p->is_decl_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + DeclBlock_Node *o = new DeclBlock_Node(); + o->name("#if 1"); + o->write_map_ = CODE_IN_SOURCE; + o->after = fl_strdup("#endif"); + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Write the specific properties. + - "public"/"protected" + - "after" followed by the second code block. + */ +void DeclBlock_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + // deprecated + if (is_public()) f.write_string("public"); + // new way to map declaration block to various parts of the generated code + if (write_map_ != CODE_IN_SOURCE) + f.write_string("map %d", write_map_); + f.write_string("after"); + f.write_word(after); +} + +/** + Read the specific properties. + */ +void DeclBlock_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if(!strcmp(c,"public")) { + write_map_ |= CODE_IN_HEADER; + } else if(!strcmp(c,"protected")) { + // + } else if(!strcmp(c,"map")) { + write_map_ = (int)atol(f.read_word()); + } else if (!strcmp(c,"after")) { + storestring(f.read_word(),after); + } else { + Node::read_property(f, c); + } +} + +/** + Open the declblock_panel to edit this node. + */ +void DeclBlock_Node::open() { + // build dialog box + if (!declblock_panel) make_declblock_panel(); + // preset all values + declblock_before_input->value(name()); + declblock_after_input->value(after); + declblock_static_header->value(write_map_ & STATIC_IN_HEADER); + declblock_static_source->value(write_map_ & STATIC_IN_SOURCE); + declblock_code_header->value(write_map_ & CODE_IN_HEADER); + declblock_code_source->value(write_map_ & CODE_IN_SOURCE); + const char *c = comment(); + declblock_comment_input->buffer()->text(c?c:""); + // show modal dialog and loop until satisfied + declblock_panel->show(); + const char* message = nullptr; + for (;;) { // repeat as long as there are errors + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == declblock_panel_cancel) goto BREAK2; + else if (w == declblock_panel_ok) break; + else if (!w) Fl::wait(); + } + // verify user input + const char* a = declblock_before_input->value(); + while (isspace(*a)) a++; + const char* b = declblock_after_input->value(); + while (isspace(*b)) b++; + message = c_check(a&&a[0]=='#' ? a+1 : a); + if (!message) + message = c_check(b&&b[0]=='#' ? b+1 : b); + if (message) { + int v = fl_choice("Potential syntax error detected: %s", + "Continue Editing", "Ignore Error", nullptr, message); + if (v==0) continue; // Continue Editing + //if (v==1) { } // Ignore Error and close dialog + } + // store user choices in data structure + name(a); + storestring(b, after); + if (write_map_ & STATIC_IN_HEADER) { + if (declblock_static_header->value()==0) { + write_map_ &= ~STATIC_IN_HEADER; + Fluid.proj.set_modflag(1); + } + } else { + if (declblock_static_header->value()) { + write_map_ |= STATIC_IN_HEADER; + Fluid.proj.set_modflag(1); + } + } + if (write_map_ & STATIC_IN_SOURCE) { + if (declblock_static_source->value()==0) { + write_map_ &= ~STATIC_IN_SOURCE; + Fluid.proj.set_modflag(1); + } + } else { + if (declblock_static_source->value()) { + write_map_ |= STATIC_IN_SOURCE; + Fluid.proj.set_modflag(1); + } + } + if (write_map_ & CODE_IN_HEADER) { + if (declblock_code_header->value()==0) { + write_map_ &= ~CODE_IN_HEADER; + Fluid.proj.set_modflag(1); + } + } else { + if (declblock_code_header->value()) { + write_map_ |= CODE_IN_HEADER; + Fluid.proj.set_modflag(1); + } + } + if (write_map_ & CODE_IN_SOURCE) { + if (declblock_code_source->value()==0) { + write_map_ &= ~CODE_IN_SOURCE; + Fluid.proj.set_modflag(1); + } + } else { + if (declblock_code_source->value()) { + write_map_ |= CODE_IN_SOURCE; + Fluid.proj.set_modflag(1); + } + } + c = declblock_comment_input->buffer()->text(); + if (c && *c) { + if (!comment() || strcmp(c, comment())) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(c); + } else { + if (comment()) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(nullptr); + } + if (c) free((void*)c); + break; + } +BREAK2: + declblock_panel->hide(); +} + +/** + Write the \b before static code to the source file, and to the header file if declared public. + The before code is stored in the name() field. + */ +void DeclBlock_Node::write_static(fld::io::Code_Writer& f) { + const char* c = name(); + if (c && *c) { + if (write_map_ & STATIC_IN_HEADER) + f.write_h("%s\n", c); + if (write_map_ & STATIC_IN_SOURCE) + f.write_c("%s\n", c); + } +} + +/** + Write the \b after static code to the source file, and to the header file if declared public. + */ +void DeclBlock_Node::write_static_after(fld::io::Code_Writer& f) { + const char* c = after; + if (c && *c) { + if (write_map_ & STATIC_IN_HEADER) + f.write_h("%s\n", c); + if (write_map_ & STATIC_IN_SOURCE) + f.write_c("%s\n", c); + } +} + +/** + Write the \b before code to the source file, and to the header file if declared public. + The before code is stored in the name() field. + */ +void DeclBlock_Node::write_code1(fld::io::Code_Writer& f) { + const char* c = name(); + if (c && *c) { + if (write_map_ & CODE_IN_HEADER) + f.write_h("%s\n", c); + if (write_map_ & CODE_IN_SOURCE) + f.write_c("%s\n", c); + } +} + +/** + Write the \b after code to the source file, and to the header file if declared public. + */ +void DeclBlock_Node::write_code2(fld::io::Code_Writer& f) { + const char* c = after; + if (c && *c) { + if (write_map_ & CODE_IN_HEADER) + f.write_h("%s\n", c); + if (write_map_ & CODE_IN_SOURCE) + f.write_c("%s\n", c); + } +} + +// ---- Comment_Node declaration + +/** \class Comment_Node + Manage a comment node. + + The comment field takes one or more lines of ASCII text. If the text starts + with a '/' and a '*', Fluid assumes that the text is already formatted. If not, + every line will be preceded with "// ". + */ + +/// Prototype for a comment node to be used by the factory. +Comment_Node Comment_Node::prototype; + +/** + Constructor. + */ +Comment_Node::Comment_Node() : + in_c_(1), + in_h_(1), + style_(0) +{ } + +/** + Make a new comment node. + \param[in] strategy add after current or as last child + \return new Comment node + */ +Node *Comment_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_code_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + Comment_Node *o = new Comment_Node(); + o->in_c_ = 1; + o->in_h_ = 1; + o->style_ = 0; + o->name("my comment"); + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Write respective properties. + - "in_source"/"not_in_source" if the comment will be written to the source code + - "in_header"/"not_in_header" if the comment will be written to the header file + */ +void Comment_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + if (in_c_) f.write_string("in_source"); else f.write_string("not_in_source"); + if (in_h_) f.write_string("in_header"); else f.write_string("not_in_header"); +} + +/** + Read extra properties. + */ +void Comment_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"in_source")) { + in_c_ = 1; + } else if (!strcmp(c,"not_in_source")) { + in_c_ = 0; + } else if (!strcmp(c,"in_header")) { + in_h_ = 1; + } else if (!strcmp(c,"not_in_header")) { + in_h_ = 0; + } else { + Node::read_property(f, c); + } +} + +/** + Load available preset comments. + Fluid comes with GPL and LGPL preset for comments. Users can + add their own presets which are stored per user in a separate + preferences database. + */ +static void load_comments_preset(Fl_Preferences &menu) { + static const char * const predefined_comment[] = { + "GNU Public License v3/GPL Header", "GNU Public License v3/GPL Footer", + "GNU Public License v3/LGPL Header", "GNU Public License v3/LGPL Footer", + "FLTK/Header" }; + int i, n; + menu.get("n", n, -1); + if (n == -1) menu.set("n", 5); + menu.set("version", 10400); + Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); + for (i=0; i<5; i++) { + menu.set(Fl_Preferences::Name(i), predefined_comment[i]); + db.set(predefined_comment[i], comment_text[i]); + } +} + +/** + Open the comment_panel to edit this node. + */ +void Comment_Node::open() { + if (!comment_panel) make_comment_panel(); + const char *text = name(); + { + int i=0, n=0, version = 0; + Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu"); + comment_predefined->clear(); + comment_predefined->add("_Edit/Add current comment..."); + comment_predefined->add("_Edit/Remove last selection..."); + menu.get("version", version, -1); + if (version < 10400) load_comments_preset(menu); + menu.get("n", n, 0); + for (i=0;iadd(text); + free(text); + } + } + comment_input->buffer()->text( text ? text : "" ); + comment_in_source->value(in_c_); + comment_in_header->value(in_h_); + comment_panel->show(); + char itempath[FL_PATH_MAX]; itempath[0] = 0; + int last_selected_item = 0; + for (;;) { // repeat as long as there are errors + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == comment_panel_cancel) goto BREAK2; + else if (w == comment_panel_ok) break; + else if (w == comment_predefined) { + if (comment_predefined->value()==1) { + // add the current comment to the database + const char *xname = fl_input( + "Please enter a name to reference the current\ncomment in your database.\n\n" + "Use forward slashes '/' to create submenus.", + "My Comment"); + if (xname) { + char *name = fl_strdup(xname); + for (char*s=name;*s;s++) if (*s==':') *s = ';'; + int n; + Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); + db.set(name, comment_input->buffer()->text()); + Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu"); + menu.get("n", n, 0); + menu.set(Fl_Preferences::Name(n), name); + menu.set("n", ++n); + comment_predefined->add(name); + free(name); + } + } else if (comment_predefined->value()==2) { + // remove the last selected comment from the database + if (itempath[0]==0 || last_selected_item==0) { + fl_message("Please select an entry from this menu first."); + } else if (fl_choice("Are you sure that you want to delete the entry\n" + "\"%s\"\nfrom the database?", "Cancel", "Delete", + nullptr, itempath)) { + Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); + db.deleteEntry(itempath); + comment_predefined->remove(last_selected_item); + Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu"); + int i, n; + for (i=4, n=0; isize(); i++) { + const Fl_Menu_Item *mi = comment_predefined->menu()+i; + if (comment_predefined->item_pathname(itempath, 255, mi)==0) { + if (itempath[0]=='/') memmove(itempath, itempath+1, 255); + if (itempath[0]) menu.set(Fl_Preferences::Name(n++), itempath); + } + } + menu.set("n", n); + } + } else { + // load the selected comment from the database + if (comment_predefined->item_pathname(itempath, 255)==0) { + if (itempath[0]=='/') memmove(itempath, itempath+1, 255); + Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); + char *text; + db.get(itempath, text, "(no text found in data base)"); + comment_input->buffer()->text(text); + free(text); + last_selected_item = comment_predefined->value(); + } + } + } + else if (w == comment_load) { + // load a comment from disk + fl_file_chooser_ok_label("Use File"); + const char *fname = fl_file_chooser("Pick a comment", nullptr, nullptr); + fl_file_chooser_ok_label(nullptr); + if (fname) { + if (comment_input->buffer()->loadfile(fname)) { + fl_alert("Error loading file\n%s", fname); + } + } + } + else if (!w) Fl::wait(); + } + char*c = comment_input->buffer()->text(); + name(c); + free(c); + int mod = 0; + if (in_c_ != comment_in_source->value()) { + in_c_ = comment_in_source->value(); + mod = 1; + } + if (in_h_ != comment_in_header->value()) { + in_h_ = comment_in_header->value(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + break; + } +BREAK2: + comment_panel->hide(); +} + +/** + Write the comment to the files. + */ +void Comment_Node::write_code1(fld::io::Code_Writer& f) { + const char* c = name(); + if (!c) return; + if (!in_c_ && !in_h_) return; + // find out if there is already a valid comment: + const char *s = c; + while (isspace(*s)) s++; + // if this seems to be a C style comment, copy the block as is + // (it's up to the user to correctly close the comment) + if (s[0]=='/' && s[1]=='*') { + if (in_h_) f.write_h("%s\n", c); + if (in_c_) f.write_c("%s\n", c); + return; + } + // copy the comment line by line, add the double slash if needed + char *txt = fl_strdup(c); + char *b = txt, *e = txt; + for (;;) { + // find the end of the line and set it to NUL + while (*e && *e!='\n') e++; + char eol = *e; + *e = 0; + // check if there is a C++ style comment at the beginning of the line + char *s = b; + while (isspace(*s)) s++; + if (s!=e && ( s[0]!='/' || s[1]!='/') ) { + // if no comment marker was found, we add one ourselves + if (in_h_) f.write_h("// "); + if (in_c_) f.write_c("// "); + } + // now copy the rest of the line + if (in_h_) f.write_h("%s\n", b); + if (in_c_) f.write_c("%s\n", b); + if (eol==0) break; + *e++ = eol; + b = e; + } + free(txt); +} + +// ---- Class_Node declaration + +/** \class Class_Node + Manage a class declaration and implementation. + */ + +/// Prototype for a class node to be used by the factory. +Class_Node Class_Node::prototype; + +/** + Constructor. + */ +Class_Node::Class_Node() : + Node(), + subclass_of(nullptr), + public_(1), + class_prefix(nullptr) +{ } + +/** + Destructor. + */ +Class_Node::~Class_Node() { + if (subclass_of) + free((void*)subclass_of); + if (class_prefix) + free((void*)class_prefix); +} + +/** + Return 1 if this class is marked public. + */ +int Class_Node::is_public() const { + return public_; +} + +/** + Set the prefixx string. + */ +void Class_Node::prefix(const char*p) { + free((void*) class_prefix); + class_prefix=fl_strdup(p ? p : "" ); +} + +/** + Make a new class node. + \param[in] strategy add after current or as last child + \return new Class node + */ +Node *Class_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_decl_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + Class_Node *o = new Class_Node(); + o->name("UserInterface"); + o->class_prefix = nullptr; + o->subclass_of = nullptr; + o->public_ = 1; + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Write the respective properties. + - ":" followed by the super class + - "private"/"protected" + */ +void Class_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + if (subclass_of) { + f.write_string(":"); + f.write_word(subclass_of); + } + switch (public_) { + case 0: f.write_string("private"); break; + case 2: f.write_string("protected"); break; + } +} + +/** + Read additional properties. + */ +void Class_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"private")) { + public_ = 0; + } else if (!strcmp(c,"protected")) { + public_ = 2; + } else if (!strcmp(c,":")) { + storestring(f.read_word(), subclass_of); + } else { + Node::read_property(f, c); + } +} + +/** + Open the class_panel to edit the class name and superclass name. + */ +void Class_Node::open() { + if (!class_panel) make_class_panel(); + char fullname[FL_PATH_MAX]=""; + if (prefix() && strlen(prefix())) + sprintf(fullname,"%s %s",prefix(),name()); + else + strcpy(fullname, name()); + c_name_input->value(fullname); + c_subclass_input->value(subclass_of); + c_public_button->value(public_); + const char *c = comment(); + c_comment_input->buffer()->text(c?c:""); + class_panel->show(); + const char* message = nullptr; + + char *na=nullptr,*pr=nullptr,*p=nullptr; // name and prefix substrings + + for (;;) { // repeat as long as there are errors + // we don;t give the option to ignore this error here because code depends + // on this being a C++ identifier + if (message) fl_alert("%s", message); + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == c_panel_cancel) goto BREAK2; + else if (w == c_panel_ok) break; + else if (!w) Fl::wait(); + } + const char*c = c_name_input->value(); + char *s = fl_strdup(c); + size_t len = strlen(s); + if (!*s) goto OOPS; + p = (char*) (s+len-1); + while (p>=s && isspace(*p)) *(p--)='\0'; + if (p=s && is_id(*p)) p--; + if ( (ps) *p--='\0'; + while (p>=s && isspace(*p)) *(p--)='\0'; + while (p>=s && is_id(*p)) p--; + if (pvalue(); + message = c_check(c); + if (message) { free((void*)s);continue;} + name(na); + prefix(pr); + free((void*)s); + storestring(c, subclass_of); + if (public_ != c_public_button->value()) { + public_ = c_public_button->value(); + Fluid.proj.set_modflag(1); + } + c = c_comment_input->buffer()->text(); + if (c && *c) { + if (!comment() || strcmp(c, comment())) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(c); + } else { + if (comment()) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(nullptr); + } + if (c) free((void*)c); + break; + } +BREAK2: + class_panel->hide(); +} + +/** + Write the header code that declares this class. + */ +void Class_Node::write_code1(fld::io::Code_Writer& f) { + parent_class = current_class; + current_class = this; + write_public_state = 0; + f.write_h("\n"); + write_comment_h(f); + if (prefix() && strlen(prefix())) + f.write_h("class %s %s ", prefix(), name()); + else + f.write_h("class %s ", name()); + if (subclass_of) f.write_h(": %s ", subclass_of); + f.write_h("{\n"); +} + +/** + Write the header code that ends the declaration of this class. + */ +void Class_Node::write_code2(fld::io::Code_Writer& f) { + f.write_h("};\n"); + current_class = parent_class; +} + +/** + Return 1 if this class contains a function with the given signature. + */ +int Node::has_function(const char *rtype, const char *sig) const { + Node *child; + for (child = next; child && child->level > level; child = child->next) { + if (child->level == level+1 && child->is_a(Type::Function)) { + const Function_Node *fn = (const Function_Node*)child; + if (fn->has_signature(rtype, sig)) + return 1; + } + } + return 0; +} diff --git a/fluid/nodes/Function_Node.h b/fluid/nodes/Function_Node.h new file mode 100644 index 000000000..ac8bcdcc2 --- /dev/null +++ b/fluid/nodes/Function_Node.h @@ -0,0 +1,277 @@ +// +// C function Node header file 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_NODES_FUNCTION_NODE_H +#define FLUID_NODES_FUNCTION_NODE_H + +#include "nodes/Node.h" + +#include "app/Image_Asset.h" +#ifdef _WIN32 +#include "tools/ExternalCodeEditor_WIN32.h" +#else +#include "tools/ExternalCodeEditor_UNIX.h" +#endif + +#include +#include +#include +#include + +#include +#include + +extern class Class_Node *current_class; + +int has_toplevel_function(const char *rtype, const char *sig); + +const char *c_check(const char *c, int type = 0); + +// ---- Function_Node declaration + +class Function_Node : public Node +{ +public: + typedef Node super; + static Function_Node prototype; +private: + const char* return_type; + char public_, cdecl_, constructor, havewidgets; +public: + Function_Node(); + ~Function_Node(); + Node *make(Strategy strategy) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + void open() override; + int ismain() {return name_ == nullptr;} + const char *type_name() override {return "Function";} + const char *title() override { + return name() ? name() : "main()"; + } + int can_have_children() const override {return 1;} + int is_code_block() const override {return 1;} + int is_public() const override; + Type type() const override { return Type::Function; } + bool is_a(Type inType) const override { return (inType==Type::Function) ? true : super::is_a(inType); } + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + int has_signature(const char *, const char*) const; +}; + +// ---- Code_Node declaration + +class Code_Node : public Node +{ +public: + typedef Node super; + static Code_Node prototype; +private: + ExternalCodeEditor editor_; + int cursor_position_; + int code_input_scroll_row; + int code_input_scroll_col; +public: + Code_Node(); + Node *make(Strategy strategy) override; + void write(fld::io::Project_Writer &f) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override { } + void open() override; + const char *type_name() override {return "code";} + int is_code_block() const override {return 0;} + Type type() const override { return Type::Code; } + bool is_a(Type inType) const override { return (inType==Type::Code) ? true : super::is_a(inType); } + int is_public() const override { return -1; } + int is_editing(); + int reap_editor(); + int handle_editor_changes(); +}; + +// ---- CodeBlock_Node declaration + +class CodeBlock_Node : public Node +{ +public: + typedef Node super; + static CodeBlock_Node prototype; +private: + const char* after; +public: + CodeBlock_Node(); + ~CodeBlock_Node(); + Node *make(Strategy strategy) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + void open() override; + const char *type_name() override {return "codeblock";} + int is_code_block() const override {return 1;} + int can_have_children() const override {return 1;} + int is_public() const override { return -1; } + Type type() const override { return Type::CodeBlock; } + bool is_a(Type inType) const override { return (inType==Type::CodeBlock) ? true : super::is_a(inType); } + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; +}; + +// ---- Decl_Node declaration + +class Decl_Node : public Node +{ +public: + typedef Node super; + static Decl_Node prototype; +protected: + char public_; + char static_; + +public: + Decl_Node(); + Node *make(Strategy strategy) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override { } + void open() override; + const char *type_name() override {return "decl";} + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + int is_public() const override; + Type type() const override { return Type::Decl; } + bool is_a(Type inType) const override { return (inType==Type::Decl) ? true : super::is_a(inType); } +}; + +// ---- Data_Node declaration + +class Data_Node : public Decl_Node +{ +public: + typedef Decl_Node super; + static Data_Node prototype; +private: + const char *filename_; + int text_mode_; + +public: + Data_Node(); + ~Data_Node(); + Node *make(Strategy strategy) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override {} + void open() override; + const char *type_name() override {return "data";} + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + Type type() const override { return Type::Data; } + bool is_a(Type inType) const override { return (inType==Type::Data) ? true : super::is_a(inType); } +}; + +// ---- DeclBlock_Node declaration + +class DeclBlock_Node : public Node +{ +public: + typedef Node super; + static DeclBlock_Node prototype; +private: + enum { + CODE_IN_HEADER = 1, + CODE_IN_SOURCE = 2, + STATIC_IN_HEADER = 4, + STATIC_IN_SOURCE = 8 + }; + const char* after; ///< code after all children of this block + int write_map_; ///< see enum above + +public: + DeclBlock_Node(); + ~DeclBlock_Node(); + Node *make(Strategy strategy) override; + void write_static(fld::io::Code_Writer& f) override; + void write_static_after(fld::io::Code_Writer& f) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + void open() override; + const char *type_name() override {return "declblock";} + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + int can_have_children() const override {return 1;} + int is_decl_block() const override {return 1;} + int is_public() const override; + Type type() const override { return Type::DeclBlock; } + bool is_a(Type inType) const override { return (inType==Type::DeclBlock) ? true : super::is_a(inType); } +}; + +// ---- Comment_Node declaration + +class Comment_Node : public Node +{ +public: + typedef Node super; + static Comment_Node prototype; +private: + char in_c_, in_h_, style_; + +public: + Comment_Node(); + Node *make(Strategy strategy) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override { } + void open() override; + const char *type_name() override {return "comment";} + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + int is_public() const override { return 1; } + Type type() const override { return Type::Comment; } + bool is_a(Type inType) const override { return (inType==Type::Comment) ? true : super::is_a(inType); } +}; + +// ---- Class_Node declaration + +class Class_Node : public Node +{ +public: + typedef Node super; + static Class_Node prototype; +private: + const char* subclass_of; + char public_; + const char* class_prefix; +public: + Class_Node(); + ~Class_Node(); + // state variables for output: + char write_public_state; // true when public: has been printed + Class_Node* parent_class; // save class if nested +// + Node *make(Strategy strategy) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + void open() override; + const char *type_name() override {return "class";} + int can_have_children() const override {return 1;} + int is_decl_block() const override {return 1;} + int is_class() const override {return 1;} + int is_public() const override; + Type type() const override { return Type::Class; } + bool is_a(Type inType) const override { return (inType==Type::Class) ? true : super::is_a(inType); } + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + + // class prefix attribute access + void prefix(const char* p); + const char* prefix() const {return class_prefix;} +}; + +#endif // FLUID_NODES_FUNCTION_NODE_H diff --git a/fluid/nodes/Grid_Node.cxx b/fluid/nodes/Grid_Node.cxx new file mode 100644 index 000000000..06f6d4166 --- /dev/null +++ b/fluid/nodes/Grid_Node.cxx @@ -0,0 +1,775 @@ +// +// Grid Node code for the Fast Light Tool Kit (FLTK). +// +// Copyright 2023-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 "nodes/Grid_Node.h" + +#include "Fluid.h" +#include "app/Snap_Action.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "widgets/Node_Browser.h" +#include "widgets/Formula_Input.h" + +#include +#include +#include +#include +#include "../src/flstring.h" + +#include +#include +#include + +// TODO: better grid overlay? +// TODO: grid_child_cb should move all selected cells, not just the current_selected. +// TODO: buttons to add and delete rows and columns in the widget dialog +// TODO: ways to resize rows and columns, add and delete them in the project window, pulldown menu? +// TODO: alignment can be FL_GRID_LEFT|FL_GRID_VERTICAL? + +// ---- Fl_Grid_Proxy --------------------------------------------------- MARK: - + +/** + An implementation of the Fl_Grid widget with additional functionality. + + Fl_Grid_Proxy add a list of transient children, i.e. children that are + temporarily assigned to a cell that is already taken by another child. + */ +Fl_Grid_Proxy::Fl_Grid_Proxy(int X,int Y,int W,int H) +: Fl_Grid(X,Y,W,H), + transient_(nullptr), + num_transient_(0), + cap_transient_(0) +{ +} + +Fl_Grid_Proxy::~Fl_Grid_Proxy() { + int i; + if (transient_) { + for (i=0; i 0) { + Fl_Grid::resize(X, Y, W, H); + } else { + Fl_Widget::resize(X, Y, W, H); + } + redraw(); +} + +/** + Override draw() to make groups with no box or flat box background visible. + */ +void Fl_Grid_Proxy::draw() { + if (Fluid.show_ghosted_outline && (box() == FL_NO_BOX)) { + fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); + } + Fl_Grid::draw(); +} + +/** + Draw additional markings in the overlay plane when a grid is selected. + */ +void Fl_Grid_Proxy::draw_overlay() { + fl_line_style(FL_DOT); + grid_color = fl_color(); + draw_grid(); + fl_color(grid_color); +} + +/** + Move a cell into the grid or within the grid. + + If the target cell is already taken, \p how will determine what to do: + + If \p how is 0, the existing cell at \p to_row, \p to_col will be deleted, + unlinking the occupant from the grid. \p in_child will the be inserted at the + given location. + + If \p how is 1, the old cell will remain intact, however \p in_child will be + unlinked from the grid. + + If \p how is 2, the old cell will remain intact, and \p in_child will be + removed from the grid, but it will be stored in the transient list and + resized to the target cell position and size. If \p in_child is later + moved to an unoccupied cell, it will be removed from the transient list and + relinked to the grid. Rowspan and colspan are ignored here. + + \param[in] in_child must already be a child of grid + \param[in] to_row, to_col move the child into this cell + \param[in] how 0: replace occupant, 1: don't replace, 2: make transient + if occupied + */ +void Fl_Grid_Proxy::move_cell(Fl_Widget *in_child, int to_row, int to_col, int how) { + // the child must already be a true child of grid + assert(find(in_child)row() == to_row && old_cell->col() == to_col) return; + rowspan = old_cell->rowspan(); + colspan = old_cell->colspan(); + align = old_cell->align(); + old_cell->minimum_size(&w, &h); + } + if ((to_row < 0) || (to_row+rowspan > rows())) return; + if ((to_col < 0) || (to_col+colspan > cols())) return; + Fl_Grid::Cell *new_cell = nullptr; + if (how == 0) { // replace old occupant in cell, making that one homeless + new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align); + } else if (how == 1) { // don't replace an old occupant, making ourselves homeless + // todo: colspan, rowspan? + if (cell(to_row, to_col) == nullptr) { + new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align); + } else { + if (old_cell) remove_cell(old_cell->row(), old_cell->col()); + } + } else if (how == 2) { + Cell *current = cell(to_row, to_col); + if (current == nullptr) { + new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align); + } else { + if (old_cell) remove_cell(old_cell->row(), old_cell->col()); + new_cell = transient_widget(in_child, to_row, to_col, rowspan, colspan, align); + Fl_Widget *w = current->widget(); + Fluid.proj.tree.allow_layout++; + in_child->resize(w->x(), w->y(), w->w(), w->h()); + Fluid.proj.tree.allow_layout--; + } + } + if (new_cell) new_cell->minimum_size(w, h); +} + +/** + Generate or replace a transient widget entry. + + If the widget is in the cell list, it will be removed there. + If the widget is already transient, the cell will be replaced. + + \param[in] wi a child of this Fl_Grid_Proxy, that may be linked to a cell or transient cell + \param[in] row, col, row_span, col_span, align cell parameters + */ +Fl_Grid::Cell* Fl_Grid_Proxy::transient_widget(Fl_Widget *wi, int row, int col, int row_span, int col_span, Fl_Grid_Align align) { + int i = 0; + bool remove_old_cell = false; + Cell *old_cell = cell(wi); + if (old_cell) { + remove_old_cell = true; + } else { + for (i=0; irowspan(row_span); + new_cell->colspan(col_span); + new_cell->align(align); + if (old_cell) { + int mw, mh; + old_cell->minimum_size(&mw, &mh); + new_cell->minimum_size(mw, mh); + if (remove_old_cell) { + remove_cell(old_cell->row(), old_cell->col()); + } else { + delete old_cell; + } + } + if (i == num_transient_) { + transient_make_room_(num_transient_ + 1); + transient_[i].widget = wi; + num_transient_++; + } + transient_[i].cell = new_cell; + return new_cell; +} + +/** + Make room for at least n transient widgets in the array. + \param[in] n minimum number of entries + */ +void Fl_Grid_Proxy::transient_make_room_(int n) { + if (n > cap_transient_) { + cap_transient_ = n + 10; + transient_ = (Cell_Widget_Pair*)::realloc(transient_, cap_transient_ * sizeof(Cell_Widget_Pair)); + } +} + +/** + Remove a widget form the list and deallocate the transient cell. + \param[in] w remove the transient cell for this widget + */ +void Fl_Grid_Proxy::transient_remove_(Fl_Widget *w) { + for (int i=0; ilayout(3, 3); + Fl_Group::current(nullptr); + return g; +} + +Fl_Widget *Grid_Node::enter_live_mode(int top) { + Fl_Grid *grid = new Fl_Grid(o->x(), o->y(), o->w(), o->h()); + return propagate_live_mode(grid); +} + +void Grid_Node::leave_live_mode() { +} + +void Grid_Node::copy_properties() +{ + super::copy_properties(); + Fl_Grid *d = (Fl_Grid*)live_widget, *s =(Fl_Grid*)o; + d->layout(s->rows(), s->cols()); + int lm, tm, rm, bm; + s->margin(&lm, &tm, &rm, &bm); + d->margin(lm, tm, rm, bm); + int rg, cg; + s->gap(&rg, &cg); + d->gap(rg, cg); + // copy col widths, heights, and gaps + for (int c=0; ccols(); c++) { + d->col_width(c, s->col_width(c)); + d->col_gap(c, s->col_gap(c)); + d->col_weight(c, s->col_weight(c)); + } + // copy row widths, heights, and gaps + for (int r=0; rrows(); r++) { + d->row_height(r, s->row_height(r)); + d->row_gap(r, s->row_gap(r)); + d->row_weight(r, s->row_weight(r)); + } +} + +void Grid_Node::copy_properties_for_children() { + Fl_Grid *d = (Fl_Grid*)live_widget, *s =(Fl_Grid*)o; + for (int i=0; ichildren(); i++) { + Fl_Grid::Cell *cell = s->cell(s->child(i)); + if (cell && ichildren()) { + d->widget(d->child(i), + cell->row(), cell->col(), + cell->rowspan(), cell->colspan(), + cell->align()); + } + } + d->layout(); +} + +void Grid_Node::write_properties(fld::io::Project_Writer &f) +{ + super::write_properties(f); + Fl_Grid* grid = (Fl_Grid*)o; + int i, rows = grid->rows(), cols = grid->cols(); + f.write_indent(level+1); + f.write_string("dimensions {%d %d}", rows, cols); + int lm, tm, rm, bm; + grid->margin(&lm, &tm, &rm, &bm); + if (lm!=0 || tm!=0 || rm!=0 || bm!=0) + f.write_string("margin {%d %d %d %d}", lm, tm, rm, bm); + int rg, cg; + grid->gap(&rg, &cg); + if (rg!=0 || cg!=0) + f.write_string("gap {%d %d}", rg, cg); + // -- write all row heights if one of them is not the default 0 + for (i=0; irow_height(i)!=0) break; + if (irow_height(i)); + f.write_string("}"); + } + // -- write all row weights if one of them is not the default 50 + for (i=0; irow_weight(i)!=50) break; + if (irow_weight(i)); + f.write_string("}"); + } + // -- write all row gaps if one of them is not the default -1 + for (i=0; irow_gap(i)!=-1) break; + if (irow_gap(i)); + f.write_string("}"); + } + // -- write all col widths if one of them is not the default 0 + for (i=0; icol_width(i)!=0) break; + if (icol_width(i)); + f.write_string("}"); + } + // -- write all col weights if one of them is not the default 50 + for (i=0; icol_weight(i)!=50) break; + if (icol_weight(i)); + f.write_string("}"); + } + // -- write all col gaps if one of them is not the default -1 + for (i=0; icol_gap(i)!=-1) break; + if (icol_gap(i)); + f.write_string("}"); + } +} + +void Grid_Node::read_property(fld::io::Project_Reader &f, const char *c) +{ + Fl_Grid* grid = (Fl_Grid*)o; + if (!strcmp(c,"dimensions")) { + int rows = 3, cols = 3; + if (sscanf(f.read_word(),"%d %d", &rows, &cols) == 2) + grid->layout(rows, cols); + } else if (!strcmp(c,"margin")) { + int lm, tm, rm, bm; + if (sscanf(f.read_word(),"%d %d %d %d", &lm, &tm, &rm, &bm) == 4) + grid->margin(lm, tm, rm, bm); + } else if (!strcmp(c,"gap")) { + int rg, cg; + if (sscanf(f.read_word(),"%d %d", &rg, &cg) == 2) + grid->gap(rg, cg); + } else if (!strcmp(c,"rowheights")) { + int rows = grid->rows(); + f.read_word(1); // "{" + for (int i=0; irow_height(i, f.read_int()); + f.read_word(1); // "}" + } else if (!strcmp(c,"rowweights")) { + int rows = grid->rows(); + f.read_word(1); // "{" + for (int i=0; irow_weight(i, f.read_int()); + f.read_word(1); // "}" + } else if (!strcmp(c,"rowgaps")) { + int rows = grid->rows(); + f.read_word(1); // "{" + for (int i=0; irow_gap(i, f.read_int()); + f.read_word(1); // "}" + } else if (!strcmp(c,"colwidths")) { + int cols = grid->cols(); + f.read_word(1); // "{" + for (int i=0; icol_width(i, f.read_int()); + f.read_word(1); // "}" + } else if (!strcmp(c,"colweights")) { + int cols = grid->cols(); + f.read_word(1); // "{" + for (int i=0; icol_weight(i, f.read_int()); + f.read_word(1); // "}" + } else if (!strcmp(c,"colgaps")) { + int cols = grid->cols(); + f.read_word(1); // "{" + for (int i=0; icol_gap(i, f.read_int()); + f.read_word(1); // "}" + } else { + super::read_property(f, c); + } +} + +void Grid_Node::write_parent_properties(fld::io::Project_Writer &f, Node *child, bool encapsulate) { + Fl_Grid *grid; + Fl_Widget *child_widget; + Fl_Grid::Cell *cell; + if (!child->is_true_widget()) return super::write_parent_properties(f, child, true); + grid = (Fl_Grid*)o; + child_widget = ((Widget_Node*)child)->o; + cell = grid->cell(child_widget); + if (!cell) return super::write_parent_properties(f, child, true); + if (encapsulate) { + f.write_indent(level+2); + f.write_string("parent_properties {"); + } + f.write_indent(level+3); + f.write_string("location {%d %d}", cell->row(), cell->col()); + int v = cell->colspan(); + if (v>1) { + f.write_indent(level+3); + f.write_string("colspan %d", v); + } + v = cell->rowspan(); + if (v>1) { + f.write_indent(level+3); + f.write_string("rowspan %d", v); + } + v = (int)cell->align(); + if (v!=FL_GRID_FILL) { + f.write_indent(level+3); + f.write_string("align %d", v); + } + int min_w = 0, min_h = 0; + cell->minimum_size(&min_w, &min_h); + if (min_w!=20 || min_h!=20) { + f.write_indent(level+3); + f.write_string("minsize {%d %d}", min_w, min_h); + } + super::write_parent_properties(f, child, false); + if (encapsulate) { + f.write_indent(level+2); + f.write_string("}"); + } + return; +} + +// NOTE: we have to do this in a loop just as ::read_property() in case a new +// property is added. In the current setup, all the remaining properties +// will be skipped +void Grid_Node::read_parent_property(fld::io::Project_Reader &f, Node *child, const char *property) { + if (!child->is_true_widget()) { + super::read_parent_property(f, child, property); + return; + } + Fl_Grid *grid = (Fl_Grid*)o; + Fl_Widget *child_widget = ((Widget_Node*)child)->o; + if (!strcmp(property, "location")) { + int row = -1, col = -1; + const char *value = f.read_word(); + sscanf(value, "%d %d", &row, &col); + Fl_Grid::Cell *cell = grid->widget(child_widget, row, col); + if (cell) { + int min_w = 20, min_h = 20; + cell->minimum_size(min_w, min_h); + } + } else if (!strcmp(property, "colspan")) { + int colspan = atoi(f.read_word()); + Fl_Grid::Cell *cell = grid->cell(child_widget); + if (cell) cell->colspan(colspan); + } else if (!strcmp(property, "rowspan")) { + int rowspan = atoi(f.read_word()); + Fl_Grid::Cell *cell = grid->cell(child_widget); + if (cell) cell->rowspan(rowspan); + } else if (!strcmp(property, "align")) { + int align = atoi(f.read_word()); + Fl_Grid::Cell *cell = grid->cell(child_widget); + if (cell) cell->align((Fl_Grid_Align)align); + } if (!strcmp(property, "minsize")) { + int min_w = 20, min_h = 20; + const char *value = f.read_word(); + sscanf(value, "%d %d", &min_w, &min_h); + Fl_Grid::Cell *cell = grid->cell(child_widget); + if (cell) cell->minimum_size(min_w, min_h); + } else { + super::read_parent_property(f, child, property); + } +} + +void Grid_Node::write_code1(fld::io::Code_Writer& f) { + const char *var = name() ? name() : "o"; + Fl_Grid* grid = (Fl_Grid*)o; + Widget_Node::write_code1(f); + int i, rows = grid->rows(), cols = grid->cols(); + f.write_c("%s%s->layout(%d, %d);\n", f.indent(), var, rows, cols); + int lm, tm, rm, bm; + grid->margin(&lm, &tm, &rm, &bm); + if (lm!=0 || tm!=0 || rm!=0 || bm!=0) + f.write_c("%s%s->margin(%d, %d, %d, %d);\n", f.indent(), var, lm, tm, rm, bm); + int rg, cg; + grid->gap(&rg, &cg); + if (rg!=0 || cg!=0) + f.write_c("%s%s->gap(%d, %d);\n", f.indent(), var, rg, cg); + // -- write all row heights if one of them is not the default 0 + for (i=0; irow_height(i)!=0) break; + if (irow_height(0)); + for (i=1; irow_height(i)); + f.write_c(" };\n"); + f.write_c("%s%s->row_height(rowheights, %d);\n", f.indent(), var, rows); + } + // -- write all row weights if one of them is not the default 50 + for (i=0; irow_weight(i)!=50) break; + if (irow_weight(0)); + for (i=1; irow_weight(i)); + f.write_c(" };\n"); + f.write_c("%s%s->row_weight(rowweights, %d);\n", f.indent(), var, rows); + } + // -- write all row gaps if one of them is not the default -1 + for (i=0; irow_gap(i)!=-1) break; + if (irow_gap(0)); + for (i=1; irow_gap(i)); + f.write_c(" };\n"); + f.write_c("%s%s->row_gap(rowgaps, %d);\n", f.indent(), var, rows); + } + // -- write all col widths if one of them is not the default 0 + for (i=0; icol_width(i)!=0) break; + if (icol_width(0)); + for (i=1; icol_width(i)); + f.write_c(" };\n"); + f.write_c("%s%s->col_width(colwidths, %d);\n", f.indent(), var, cols); + } + // -- write all col weights if one of them is not the default 50 + for (i=0; icol_weight(i)!=50) break; + if (icol_weight(0)); + for (i=1; icol_weight(i)); + f.write_c(" };\n"); + f.write_c("%s%s->col_weight(colweights, %d);\n", f.indent(), var, cols); + } + // -- write all col gaps if one of them is not the default -1 + for (i=0; icol_gap(i)!=-1) break; + if (icol_gap(0)); + for (i=1; icol_gap(i)); + f.write_c(" };\n"); + f.write_c("%s%s->col_gap(colgaps, %d);\n", f.indent(), var, cols); + } +} + +void Grid_Node::write_code2(fld::io::Code_Writer& f) { + const char *var = name() ? name() : "o"; + Fl_Grid* grid = (Fl_Grid*)o; + bool first_cell = true; + for (int i=0; ichildren(); i++) { + Fl_Widget *c = grid->child(i); + Fl_Grid::Cell *cell = grid->cell(c); + if (cell) { + if (first_cell) { + f.write_c("%sFl_Grid::Cell *cell = 0L;\n", f.indent()); + first_cell = false; + } + f.write_c("%scell = %s->widget(%s->child(%d), %d, %d, %d, %d, %d);\n", + f.indent(), var, var, i, cell->row(), cell->col(), + cell->rowspan(), cell->colspan(), cell->align()); + int min_w = 20, min_h = 20; + cell->minimum_size(&min_w, &min_h); + f.write_c("%sif (cell) cell->minimum_size(%d, %d);\n", f.indent(), min_w, min_h); + } + } + super::write_code2(f); +} + +void Grid_Node::add_child(Node* a, Node* b) { + super::add_child(a, b); + Fl_Grid* grid = (Fl_Grid*)o; + grid->need_layout(1); + grid->redraw(); +} + +void Grid_Node::move_child(Node* a, Node* b) { + super::move_child(a, b); + Fl_Grid* grid = (Fl_Grid*)o; + grid->need_layout(1); + grid->redraw(); +} + +void Grid_Node::remove_child(Node* a) { + super::remove_child(a); + Fl_Grid* grid = (Fl_Grid*)o; + grid->need_layout(1); + grid->redraw(); +} + +/** Update the initial size of a child widget. + Fl_Grid keeps track of the size of children when they are first added. In + FLUID, users will want to resize children. So we need to trick Fl_Grid into + taking the new size as the initial size. + */ +void Grid_Node::child_resized(Widget_Node *child_type) { + Fl_Grid *grid = (Fl_Grid*)o; + Fl_Widget *child = child_type->o; + Fl_Grid::Cell *cell = grid->cell(child); + if (cell && ((cell->align()&FL_GRID_VERTICAL)==0)) { + int min_w = 0, min_h = 0; + cell->minimum_size(&min_w, &min_h); + cell->minimum_size(min_w, child->h()); + } + if (cell && ((cell->align()&FL_GRID_HORIZONTAL)==0)) { + int min_w = 0, min_h = 0; + cell->minimum_size(&min_w, &min_h); + cell->minimum_size(child->w(), min_h); + } + // TODO: if the user resizes an FL_GRID_FILL widget, should we change the alignment? +} + +/** Return the currently selected Grid widget if is a Grid Type. */ +Fl_Grid *Grid_Node::selected() { + if (current_widget && current_widget->is_a(Type::Grid)) + return ((Fl_Grid*)((Grid_Node*)current_widget)->o); + return nullptr; +} + +/** + Insert a child widget into the cell at the x, y position inside the window. + /param[in] child + /param[in] x, y pixels from the top left of the window + */ +void Grid_Node::insert_child_at(Fl_Widget *child, int x, int y) { + Fl_Grid_Proxy *grid = (Fl_Grid_Proxy*)o; + int row = -1, col = -1, ml, mt, grg, gcg; + grid->margin(&ml, &mt, nullptr, nullptr); + grid->gap(&grg, &gcg); + int x0 = grid->x() + Fl::box_dx(grid->box()) + ml; + int y0 = grid->y() + Fl::box_dy(grid->box()) + mt; + + for (int r = 0; r < grid->rows(); r++) { + if (y>y0) row = r; + int gap = grid->row_gap(r)>=0 ? grid->row_gap(r) : grg; + y0 += grid->computed_row_height(r); + y0 += gap; + } + + for (int c = 0; c < grid->cols(); c++) { + if (x>x0) col = c; + int gap = grid->col_gap(c)>=0 ? grid->col_gap(c) : gcg; + x0 += grid->computed_col_width(c); + x0 += gap; + } + + grid->move_cell(child, row, col, 2); +} + +/** + Insert a child widget into the first new cell we can find . + + There are many other possible strategies. How about inserting to the right + of the last added child. Also, what happens if the grid is full? Should + we add a new row at the bottom? + + /param[in] child + */ +void Grid_Node::insert_child_at_next_free_cell(Fl_Widget *child) { + Fl_Grid_Proxy *grid = (Fl_Grid_Proxy*)o; + if (grid->cell(child)) return; +// The code below would insert the new widget after the last selected one, but +// unfortunately the current_widget is already invalid. +// if (current_widget && (current_widget->parent == this)) { +// Fl_Grid::Cell *current_cell = grid->any_cell(current_widget->o); +// if (current_cell) { +// r = current_cell->row(); +// c = current_cell->col(); +// } +// } + for (int r = 0; r < grid->rows(); r++) { + for (int c = 0; c < grid->cols(); c++) { + if (!grid->cell(r, c)) { + grid->move_cell(child, r, c); + return; + } + } + } + grid->layout(grid->rows() + 1, grid->cols()); + grid->move_cell(child, grid->rows() - 1, 0); +} + +/** Move cells around using the keyboard. + \note this fails if we have two children selected side by side and press 'right', + which will move the left child first, removing the right child from the + cell system. When trying to move the second child, it has no longer an + assigned row or column. + \param[in] child pointer to the child type + \param[in] key code of the last keypress when handling a FL_KEYBOARD event. + */ +void Grid_Node::keyboard_move_child(Widget_Node *child, int key) { + Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)o); + Fl_Grid::Cell *cell = grid->any_cell(child->o); + if (!cell) return; + if (key == FL_Right) { + grid->move_cell(child->o, cell->row(), cell->col()+1, 2); + } else if (key == FL_Left) { + grid->move_cell(child->o, cell->row(), cell->col()-1, 2); + } else if (key == FL_Up) { + grid->move_cell(child->o, cell->row()-1, cell->col(), 2); + } else if (key == FL_Down) { + grid->move_cell(child->o, cell->row()+1, cell->col(), 2); + } +} + +void Grid_Node::layout_widget() { + Fluid.proj.tree.allow_layout++; + ((Fl_Grid*)o)->layout(); + Fluid.proj.tree.allow_layout--; +} + diff --git a/fluid/nodes/Grid_Node.h b/fluid/nodes/Grid_Node.h new file mode 100644 index 000000000..4363164ee --- /dev/null +++ b/fluid/nodes/Grid_Node.h @@ -0,0 +1,82 @@ +// +// Grid Node header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 2023-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_NODES_GRID_NODE_H +#define FLUID_NODES_GRID_NODE_H + +#include "nodes/Group_Node.h" +#include + +// ---- Grid_Node --------------------------------------------------- MARK: - + +class Fl_Grid_Proxy : public Fl_Grid { +protected: + typedef struct { Fl_Widget *widget; Cell *cell; } Cell_Widget_Pair; + Cell_Widget_Pair *transient_; + int num_transient_; + int cap_transient_; + void transient_make_room_(int n); + void transient_remove_(Fl_Widget *w); +public: + Fl_Grid_Proxy(int X,int Y,int W,int H); + ~Fl_Grid_Proxy(); + void resize(int,int,int,int) override; + void draw() override; + void draw_overlay(); + void move_cell(Fl_Widget *child, int to_row, int to_col, int how = 0); + Cell* any_cell(Fl_Widget *widget) const; + Cell* transient_cell(Fl_Widget *widget) const; + Cell* transient_widget(Fl_Widget *wi, int row, int col, int row_span, int col_span, Fl_Grid_Align align = FL_GRID_FILL); + Cell* widget(Fl_Widget *wi, int row, int col, Fl_Grid_Align align = FL_GRID_FILL); + Cell* widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align = FL_GRID_FILL); +}; + +class Grid_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Grid_Node prototype; +public: + Grid_Node(); + const char *type_name() override {return "Fl_Grid";} + const char *alt_type_name() override {return "fltk::GridGroup";} + Widget_Node *_make() override { return new Grid_Node(); } + Fl_Widget *widget(int X,int Y,int W,int H) override; + Type type() const override { return Type::Grid; } + bool is_a(Type inType) const override { return (inType==Type::Grid) ? true : super::is_a(inType); } + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + void write_parent_properties(fld::io::Project_Writer &f, Node *child, bool encapsulate) override; + void read_parent_property(fld::io::Project_Reader &f, Node *child, const char *property) override; + Fl_Widget *enter_live_mode(int top=0) override; + void leave_live_mode() override; + void copy_properties() override; + void copy_properties_for_children() override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + void add_child(Node*, Node*) override; + void move_child(Node*, Node*) override; + void remove_child(Node*) override; + void layout_widget() override; + void child_resized(Widget_Node *child); + void insert_child_at(Fl_Widget *child, int x, int y); + void insert_child_at_next_free_cell(Fl_Widget *child); + void keyboard_move_child(Widget_Node*, int key); + + static class Fl_Grid *selected(); +}; + +#endif // FLUID_NODES_GRID_NODE_H diff --git a/fluid/nodes/Group_Node.cxx b/fluid/nodes/Group_Node.cxx new file mode 100644 index 000000000..d269b62ab --- /dev/null +++ b/fluid/nodes/Group_Node.cxx @@ -0,0 +1,849 @@ +// +// Group Node code for the Fast Light Tool Kit (FLTK). +// +// Object describing an Fl_Group and links to Window_Node.C and +// the Fl_Tabs widget, with special stuff to select tab items and +// insure that only one is visible. +// +// Copyright 1998-2023 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 "nodes/Group_Node.h" + +#include "Fluid.h" +#include "proj/undo.h" +#include "app/Snap_Action.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "widgets/Node_Browser.h" + +#include +#include +#include +#include +#include +#include +#include "../src/flstring.h" + +#include +#include + + +// ---- Group_Node -------------------------------------------------- MARK: - + +Group_Node Group_Node::prototype; + +/** + Override group's resize behavior to do nothing to children by default. + \param[in] X, Y, W, H new size + */ +void Fl_Group_Proxy::resize(int X, int Y, int W, int H) { + if (Fluid.proj.tree.allow_layout > 0) { + Fl_Group::resize(X, Y, W, H); + } else { + Fl_Widget::resize(X, Y, W, H); + } + redraw(); +} + +/** + Override draw() to make groups with no box or flat box background visible. + */ +void Fl_Group_Proxy::draw() { + if (Fluid.show_ghosted_outline && (box() == FL_NO_BOX)) { + fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); + } + Fl_Group::draw(); +} + + +/** + \brief Enlarge the group size, so all children fit within. + */ +void fix_group_size(Node *tt) { + if (!tt || !tt->is_a(Type::Group)) return; + Group_Node* t = (Group_Node*)tt; + int X = t->o->x(); + int Y = t->o->y(); + int R = X+t->o->w(); + int B = Y+t->o->h(); + for (Node *nn = t->next; nn && nn->level > t->level; nn = nn->next) { + if (nn->is_true_widget()) { + Widget_Node* n = (Widget_Node*)nn; + int x = n->o->x(); if (x < X) X = x; + int y = n->o->y(); if (y < Y) Y = y; + int r = x+n->o->w();if (r > R) R = r; + int b = y+n->o->h();if (b > B) B = b; + } + } + t->o->resize(X,Y,R-X,B-Y); +} + +extern void group_selected_menuitems(); + +void group_cb(Fl_Widget *, void *) { + if (!Fluid.proj.tree.current) { + fl_message("No widgets selected."); + return; + } + if (!Fluid.proj.tree.current->is_widget()) { + fl_message("Only widgets and menu items can be grouped."); + return; + } + if (Fluid.proj.tree.current->is_a(Type::Menu_Item)) { + group_selected_menuitems(); + return; + } + // The group will be created in the parent group of the current widget + Node *qq = Fluid.proj.tree.current->parent; + Widget_Node *q = static_cast(Fluid.proj.tree.current); + while (qq && !qq->is_a(Type::Group)) { + qq = qq->parent; + } + if (!qq) { + fl_message("Can't create a new group here."); + return; + } + Fluid.proj.undo.checkpoint(); + Fluid.proj.undo.suspend(); + Fluid.proj.tree.current = qq; + Group_Node *n = (Group_Node*)(Group_Node::prototype.make(Strategy::AS_LAST_CHILD)); + n->move_before(q); + n->o->resize(q->o->x(),q->o->y(),q->o->w(),q->o->h()); + for (Node *t = qq->next; t && (t->level > qq->level);) { + if (t->level != n->level || t == n || !t->selected) { + t = t->next; + continue; + } + Node *nxt = t->remove(); + t->add(n, Strategy::AS_LAST_CHILD); + t = nxt; + } + fix_group_size(n); + Fluid.proj.tree.current = q; + n->layout_widget(); + widget_browser->rebuild(); + Fluid.proj.undo.resume(); + Fluid.proj.set_modflag(1); +} + +extern void ungroup_selected_menuitems(); + +void ungroup_cb(Fl_Widget *, void *) { + if (!Fluid.proj.tree.current) { + fl_message("No widgets selected."); + return; + } + if (!Fluid.proj.tree.current->is_widget()) { + fl_message("Only widgets and menu items can be ungrouped."); + return; + } + if (Fluid.proj.tree.current->is_a(Type::Menu_Item)) { + ungroup_selected_menuitems(); + return; + } + + Widget_Node *q = static_cast(Fluid.proj.tree.current); + int q_level = q->level; + Node *qq = Fluid.proj.tree.current->parent; + while (qq && !qq->is_true_widget()) qq = qq->parent; + if (!qq || !qq->is_a(Type::Group)) { + fl_message("Only menu widgets inside a group can be ungrouped."); + return; + } + Fluid.proj.undo.checkpoint(); + Fluid.proj.undo.suspend(); + Fluid.proj.tree.current = qq; + for (Node *t = qq->next; t && (t->level > qq->level);) { + if (t->level != q_level || !t->selected) { + t = t->next; + continue; + } + Node *nxt = t->remove(); + t->insert(qq); + t = nxt; + } + if (!qq->next || (qq->next->level <= qq->level)) { + qq->remove(); + delete qq; // qq has no children that need to be delete + } + Fluid.proj.tree.current = q; + widget_browser->rebuild(); + Fluid.proj.undo.resume(); + Fluid.proj.set_modflag(1); +} + +void Group_Node::ideal_size(int &w, int &h) { + if (parent && parent->is_true_widget()) { + Fl_Widget *p = ((Widget_Node*)parent)->o; + w = p->w() / 2; + h = p->h() / 2; + } else { + w = 140; + h = 140; + } + fld::app::Snap_Action::better_size(w, h); +} + +void Group_Node::write_code1(fld::io::Code_Writer& f) { + Widget_Node::write_code1(f); +} + +void Group_Node::write_code2(fld::io::Code_Writer& f) { + const char *var = name() ? name() : "o"; + write_extra_code(f); + f.write_c("%s%s->end();\n", f.indent(), var); + if (resizable()) { + f.write_c("%sFl_Group::current()->resizable(%s);\n", f.indent(), var); + } + write_block_close(f); +} + +// This is called when o is created. If it is in the tab group make +// sure it is visible: +void Group_Node::add_child(Node* cc, Node* before) { + Widget_Node* c = (Widget_Node*)cc; + Fl_Widget* b = before ? ((Widget_Node*)before)->o : nullptr; + ((Fl_Group*)o)->insert(*(c->o), b); + o->redraw(); +} + +// This is called when o is deleted. If it is in the tab group make +// sure it is not visible: +void Group_Node::remove_child(Node* cc) { + Widget_Node* c = (Widget_Node*)cc; + ((Fl_Group*)o)->remove(c->o); + o->redraw(); +} + +// move, don't change selected value: +void Group_Node::move_child(Node* cc, Node* before) { + Widget_Node* c = (Widget_Node*)cc; + Fl_Widget* b = before ? ((Widget_Node*)before)->o : nullptr; + ((Fl_Group*)o)->insert(*(c->o), b); + o->redraw(); +} + +// live mode support +Fl_Widget* Group_Node::enter_live_mode(int) { + Fl_Group *grp = new Fl_Group(o->x(), o->y(), o->w(), o->h()); + return propagate_live_mode(grp); +} + +void Group_Node::leave_live_mode() { +} + +/** + copy all properties from the edit widget to the live widget + */ +void Group_Node::copy_properties() { + Widget_Node::copy_properties(); +} + +// ---- Pack_Node --------------------------------------------------- MARK: - + +Pack_Node Pack_Node::prototype; // the "factory" + +const char pack_type_name[] = "Fl_Pack"; + +Fl_Menu_Item pack_type_menu[] = { + {"HORIZONTAL", 0, nullptr, (void*)Fl_Pack::HORIZONTAL}, + {"VERTICAL", 0, nullptr, (void*)Fl_Pack::VERTICAL}, + {nullptr} +}; + +Fl_Widget *Pack_Node::enter_live_mode(int) { + Fl_Group *grp = new Fl_Pack(o->x(), o->y(), o->w(), o->h()); + return propagate_live_mode(grp); +} + +void Pack_Node::copy_properties() +{ + Group_Node::copy_properties(); + Fl_Pack *d = (Fl_Pack*)live_widget, *s =(Fl_Pack*)o; + d->spacing(s->spacing()); +} + +// ---- Flex_Node --------------------------------------------------- MARK: - + +const char flex_type_name[] = "Fl_Flex"; + +Fl_Menu_Item flex_type_menu[] = { + {"HORIZONTAL", 0, nullptr, (void*)Fl_Flex::HORIZONTAL}, + {"VERTICAL", 0, nullptr, (void*)Fl_Flex::VERTICAL}, + {nullptr}}; + +Flex_Node Flex_Node::prototype; // the "factory" + +/** + Override flex's resize behavior to do nothing to children by default. + + \param[in] X, Y, W, H new size + */ +void Fl_Flex_Proxy::resize(int X, int Y, int W, int H) { + if (Fluid.proj.tree.allow_layout > 0) { + Fl_Flex::resize(X, Y, W, H); + } else { + Fl_Widget::resize(X, Y, W, H); + } + redraw(); +} + +/** + Override draw() to make groups with no box or flat box background visible. + */ +void Fl_Flex_Proxy::draw() { + if (Fluid.show_ghosted_outline && (box() == FL_NO_BOX)) { + fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); + } + Fl_Flex::draw(); +} + +Fl_Widget *Flex_Node::enter_live_mode(int) { + Fl_Flex *grp = new Fl_Flex(o->x(), o->y(), o->w(), o->h()); + propagate_live_mode(grp); + Fl_Flex *d = grp, *s =(Fl_Flex*)o; + int nc = s->children(), nd = d->children(); + if (nc>nd) nc = nd; + for (int i=0; ifixed(s->child(i))) { + Fl_Widget *dc = d->child(i); + d->fixed(d->child(i), s->horizontal() ? dc->w() : dc->h()); + } + } + return grp; +} + +void Flex_Node::copy_properties() +{ + Group_Node::copy_properties(); + Fl_Flex *d = (Fl_Flex*)live_widget, *s =(Fl_Flex*)o; + int lm, tm, rm, bm; + s->margin(&lm, &tm, &rm, &bm); + d->margin(lm, tm, rm, bm); + d->gap( s->gap() ); +} + +void Flex_Node::copy_properties_for_children() { + Fl_Flex *d = (Fl_Flex*)live_widget, *s =(Fl_Flex*)o; + for (int i=0; ichildren(); i++) { + if (s->fixed(s->child(i)) && ichildren()) { + if (s->horizontal()) { + d->fixed(d->child(i), d->child(i)->w()); + } else { + d->fixed(d->child(i), d->child(i)->h()); + } + } + } + d->layout(); +} + +void Flex_Node::write_properties(fld::io::Project_Writer &f) +{ + Group_Node::write_properties(f); + Fl_Flex* flex = (Fl_Flex*)o; + int lm, tm, rm, bm; + flex->margin(&lm, &tm, &rm, &bm); + if (lm!=0 || tm!=0 || rm!=0 || bm!=0) + f.write_string("margin {%d %d %d %d}", lm, tm, rm, bm); + if (flex->gap()) + f.write_string("gap %d", flex->gap()); + int nSet = 0; + for (int i=0; ichildren(); i++) + if (flex->fixed(flex->child(i))) + nSet++; + if (nSet) { + f.write_string("fixed_size_tuples {%d", nSet); + for (int i=0; ichildren(); i++) { + Fl_Widget *ci = flex->child(i); + if (flex->fixed(ci)) + f.write_string(" %d %d", i, flex->horizontal() ? ci->w() : ci->h()); + } + f.write_string("}"); + } +} + +void Flex_Node::read_property(fld::io::Project_Reader &f, const char *c) +{ + Fl_Flex* flex = (Fl_Flex*)o; + suspend_auto_layout = 1; + if (!strcmp(c,"margin")) { + int lm, tm, rm, bm; + if (sscanf(f.read_word(),"%d %d %d %d",&lm,&tm,&rm,&bm) == 4) + flex->margin(lm, tm, rm, bm); + } else if (!strcmp(c,"gap")) { + int g; + if (sscanf(f.read_word(),"%d",&g)) + flex->gap(g); + } else if (!strcmp(c,"fixed_size_tuples")) { + f.read_word(1); // must be '{' + const char *nStr = f.read_word(1); // number of indices in table + fixedSizeTupleSize = atoi(nStr); + fixedSizeTuple = new int[fixedSizeTupleSize*2]; + for (int i=0; i0) { + for (int i=0; i=0 && ixchildren()) { + Fl_Widget *ci = flex->child(ix); + flex->fixed(ci, size); + } + } + fixedSizeTupleSize = 0; + delete[] fixedSizeTuple; + fixedSizeTuple = nullptr; + } + suspend_auto_layout = 0; +} + +void Flex_Node::write_code2(fld::io::Code_Writer& f) { + const char *var = name() ? name() : "o"; + Fl_Flex* flex = (Fl_Flex*)o; + int lm, tm, rm, bm; + flex->margin(&lm, &tm, &rm, &bm); + if (lm!=0 || tm!=0 || rm!=0 || bm!=0) + f.write_c("%s%s->margin(%d, %d, %d, %d);\n", f.indent(), var, lm, tm, rm, bm); + if (flex->gap()) + f.write_c("%s%s->gap(%d);\n", f.indent(), var, flex->gap()); + for (int i=0; ichildren(); ++i) { + Fl_Widget *ci = flex->child(i); + if (flex->fixed(ci)) + f.write_c("%s%s->fixed(%s->child(%d), %d);\n", f.indent(), var, var, i, + flex->horizontal() ? ci->w() : ci->h()); + } + Group_Node::write_code2(f); +} + +//void Flex_Node::add_child(Node* a, Node* b) { +// Group_Node::add_child(a, b); +// if (!suspend_auto_layout) +// ((Fl_Flex*)o)->layout(); +//} +// +//void Flex_Node::move_child(Node* a, Node* b) { +// Group_Node::move_child(a, b); +// if (!suspend_auto_layout) +// ((Fl_Flex*)o)->layout(); +//} + +void Flex_Node::remove_child(Node* a) { + if (a->is_widget()) + ((Fl_Flex*)o)->fixed(((Widget_Node*)a)->o, 0); + Group_Node::remove_child(a); +// ((Fl_Flex*)o)->layout(); + layout_widget(); +} + +void Flex_Node::layout_widget() { + Fluid.proj.tree.allow_layout++; + ((Fl_Flex*)o)->layout(); + Fluid.proj.tree.allow_layout--; +} + +// Change from HORIZONTAL to VERTICAL or back. +// Children in a horizontal Flex have already the full vertical height. If we +// just change to vertical, the accumulated hight of all children is too big. +// We need to relayout existing children. +void Flex_Node::change_subtype_to(int n) { + Fl_Flex* f = (Fl_Flex*)o; + if (f->type()==n) return; + + int nc = f->children(); + if (nc > 0) { + int dw = Fl::box_dw(f->box()); + int dh = Fl::box_dh(f->box()); + int lm, tm, rm, bm; + f->margin(&lm, &tm, &rm, &bm); + int gap = f->gap(); + int fw = f->w()-dw-lm-rm-(nc*gap); + if (fw<=nc) fw = nc; // avoid division by zero + int fh = f->h()-dh-tm-bm-(nc*gap); + if (fh<=nc) fh = nc; // avoid division by zero + + if (f->type()==Fl_Flex::HORIZONTAL && n==Fl_Flex::VERTICAL) { + float scl = (float)fh/(float)fw; + for (int i=0; ichild(i); + c->size(f->w(), (int)(c->w()*scl)); + } + } else if (f->type()==Fl_Flex::VERTICAL && n==Fl_Flex::HORIZONTAL) { + float scl = (float)fw/(float)fh; + for (int i=0; ichild(i); + c->size((int)(c->h()*scl), f->h()); + } + } + } + f->type(n); + f->layout(); +} + +int Flex_Node::parent_is_flex(Node *t) { + return (t->is_widget() + && t->parent + && t->parent->is_a(Type::Flex)); +} + +/** + Insert a widget in the child list so that it moves as close as possible the position. + + \param[in] child any widget in the tree but this, may already be a child of + this and will be relocated if so + \param[in] x, y pixel coordinates relative to the top left of the window + */ +void Flex_Node::insert_child_at(Fl_Widget *child, int x, int y) { + Fl_Flex *flex = (Fl_Flex*)o; + // find the insertion point closest to x, y + int d = flex->w() + flex->h(), di = -1; + if (flex->horizontal()) { + int i, dx; + for (i=0; ichildren(); i++) { + dx = x - flex->child(i)->x(); + if (dx < 0) dx = -dx; + if (dx < d) { d = dx; di = i; } + } + dx = x - (flex->x()+flex->w()); + if (dx < 0) dx = -dx; + if (dx < d) { d = dx; di = i; } + } else { + int i, dy; + for (i=0; ichildren(); i++) { + dy = y - flex->child(i)->y(); + if (dy < 0) dy = -dy; + if (dy < d) { d = dy; di = i; } + } + dy = y - (flex->y()+flex->h()); + if (dy < 0) dy = -dy; + if (dy < d) { d = dy; di = i; } + } + if (di > -1) { + flex->insert(*child, di); + } +} + +/** Move children around using the keyboard. + \param[in] child pointer to the child type + \param[in] key code of the last keypress when handling a FL_KEYBOARD event. + */ +void Flex_Node::keyboard_move_child(Widget_Node *child, int key) { + Fl_Flex *flex = ((Fl_Flex*)o); + int ix = flex->find(child->o); + if (ix == flex->children()) return; + if (flex->horizontal()) { + if (key==FL_Right) { + flex->insert(*child->o, ix+2); + } else if (key==FL_Left) { + if (ix > 0) flex->insert(*child->o, ix-1); + } + } else { + if (key==FL_Down) { + flex->insert(*child->o, ix+2); + } else if (key==FL_Up) { + if (ix > 0) flex->insert(*child->o, ix-1); + } + } +} + +int Flex_Node::size(Node *t, char fixed_only) { + if (!t->is_widget()) return 0; + if (!t->parent) return 0; + if (!t->parent->is_a(Type::Flex)) return 0; + Flex_Node* ft = (Flex_Node*)t->parent; + Fl_Flex* f = (Fl_Flex*)ft->o; + Fl_Widget *w = ((Widget_Node*)t)->o; + if (fixed_only && !f->fixed(w)) return 0; + return f->horizontal() ? w->w() : w->h(); +} + +int Flex_Node::is_fixed(Node *t) { + if (!t->is_widget()) return 0; + if (!t->parent) return 0; + if (!t->parent->is_a(Type::Flex)) return 0; + Flex_Node* ft = (Flex_Node*)t->parent; + Fl_Flex* f = (Fl_Flex*)ft->o; + Fl_Widget *w = ((Widget_Node*)t)->o; + return f->fixed(w); +} + +// ---- Table_Node -------------------------------------------------- MARK: - + +Table_Node Table_Node::prototype; // the "factory" + +static const int MAX_ROWS = 14; +static const int MAX_COLS = 7; + +// this is a minimal table widget used as an example when adding tables in Fluid +class Fl_Table_Proxy : public Fl_Table { + int data[MAX_ROWS][MAX_COLS]; // data array for cells + + // Draw the row/col headings + // Make this a dark thin upbox with the text inside. + // + void DrawHeader(const char *s, int X, int Y, int W, int H) { + fl_push_clip(X,Y,W,H); + fl_draw_box(FL_THIN_UP_BOX, X,Y,W,H, row_header_color()); + fl_color(FL_BLACK); + fl_draw(s, X,Y,W,H, FL_ALIGN_CENTER); + fl_pop_clip(); + } + // Draw the cell data + // Dark gray text on white background with subtle border + // + void DrawData(const char *s, int X, int Y, int W, int H) { + fl_push_clip(X,Y,W,H); + // Draw cell bg + fl_color(FL_WHITE); fl_rectf(X,Y,W,H); + // Draw cell data + fl_color(FL_GRAY0); fl_draw(s, X,Y,W,H, FL_ALIGN_CENTER); + // Draw box border + fl_color(color()); fl_rect(X,Y,W,H); + fl_pop_clip(); + } + // Handle drawing table's cells + // Fl_Table calls this function to draw each visible cell in the table. + // It's up to us to use FLTK's drawing functions to draw the cells the way we want. + // + void draw_cell(TableContext context, int ROW=0, int COL=0, int X=0, int Y=0, int W=0, int H=0) override { + static char s[40]; + switch ( context ) { + case CONTEXT_STARTPAGE: // before page is drawn.. + fl_font(FL_HELVETICA, 16); // set the font for our drawing operations + return; + case CONTEXT_COL_HEADER: // Draw column headers + sprintf(s,"%c",'A'+COL); // "A", "B", "C", etc. + DrawHeader(s,X,Y,W,H); + return; + case CONTEXT_ROW_HEADER: // Draw row headers + sprintf(s,"%03d:",ROW); // "001:", "002:", etc + DrawHeader(s,X,Y,W,H); + return; + case CONTEXT_CELL: // Draw data in cells + sprintf(s,"%d",data[ROW][COL]); + DrawData(s,X,Y,W,H); + return; + default: + return; + } + } +public: + Fl_Table_Proxy(int x, int y, int w, int h, const char *l=nullptr) + : Fl_Table(x, y, w, h, l) { + end(); + for ( int r=0; ro : nullptr; + if (((Fl_Table*)o)->children()==1) { // the FLuid_Table has one extra child + fl_message("Inserting child widgets into an Fl_Table is not recommended.\n" + "Please refer to the documentation on Fl_Table."); + } + ((Fl_Table*)o)->insert(*(c->o), b); + o->redraw(); +} + +void Table_Node::remove_child(Node* cc) { + Widget_Node* c = (Widget_Node*)cc; + ((Fl_Table*)o)->remove(*(c->o)); + o->redraw(); +} + +void Table_Node::move_child(Node* cc, Node* before) { + Widget_Node* c = (Widget_Node*)cc; + Fl_Widget* b = before ? ((Widget_Node*)before)->o : nullptr; + ((Fl_Table*)o)->insert(*(c->o), b); + o->redraw(); +} + +Fl_Widget *Table_Node::enter_live_mode(int) { + Fl_Group *grp = new Fl_Table_Proxy(o->x(), o->y(), o->w(), o->h()); + live_widget = grp; + copy_properties(); + grp->end(); + return live_widget; +} + +void Table_Node::ideal_size(int &w, int &h) { + w = 160; + h = 120; + fld::app::Snap_Action::better_size(w, h); +} + +// ---- Tabs_Node --------------------------------------------------- MARK: - + +Tabs_Node Tabs_Node::prototype; + +const char tabs_type_name[] = "Fl_Tabs"; + +// Override group's resize behavior to do nothing to children: +void Fl_Tabs_Proxy::resize(int X, int Y, int W, int H) { + if (Fluid.proj.tree.allow_layout > 0) { + Fl_Tabs::resize(X, Y, W, H); + } else { + Fl_Widget::resize(X, Y, W, H); + } + redraw(); +} + +/** + Override draw() to make groups with no box or flat box background visible. + */ +void Fl_Tabs_Proxy::draw() { + if (Fluid.show_ghosted_outline && (box() == FL_NO_BOX)) { + fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); + } + Fl_Tabs::draw(); +} + +// This is called when user clicks on a widget in the window. See +// if it is a tab title, and adjust visibility and return new selection: +// If none, return o unchanged: + +Node* Tabs_Node::click_test(int x, int y) { + Fl_Tabs *t = (Fl_Tabs*)o; + Fl_Widget *a = t->which(x,y); + if (!a) return nullptr; // didn't click on tab + // changing the visible tab has an impact on the generated + // source code, so mark this project as changed. + int changed = (a!=t->value()); + // okay, run the tabs ui until they let go of mouse: + t->handle(FL_PUSH); + Fl::pushed(t); + while (Fl::pushed()==t) Fl::wait(); + if (changed) Fluid.proj.set_modflag(1); + return (Node*)(t->value()->user_data()); +} + +void Tabs_Node::add_child(Node* c, Node* before) { + Group_Node::add_child(c, before); +} + +void Tabs_Node::remove_child(Node* cc) { + Widget_Node* c = (Widget_Node*)cc; + Fl_Tabs *t = (Fl_Tabs*)o; + if (t->value() == c->o) t->value(nullptr); + Group_Node::remove_child(c); +} + +Fl_Widget *Tabs_Node::enter_live_mode(int) { + Fl_Tabs *original = static_cast(o); + Fl_Tabs *clone = new Fl_Tabs(o->x(), o->y(), o->w(), o->h()); + propagate_live_mode(clone); + int tab_index = original->find(original->value()); + if ((tab_index>=0) && (tab_indexchildren())) + clone->value(clone->child(tab_index)); + return clone; +} + +// ---- Scroll_Node ------------------------------------------------- MARK: - + +Scroll_Node Scroll_Node::prototype; // the "factory" + +const char scroll_type_name[] = "Fl_Scroll"; + +Fl_Menu_Item scroll_type_menu[] = { + {"BOTH", 0, nullptr, nullptr/*(void*)Fl_Scroll::BOTH*/}, + {"HORIZONTAL", 0, nullptr, (void*)Fl_Scroll::HORIZONTAL}, + {"VERTICAL", 0, nullptr, (void*)Fl_Scroll::VERTICAL}, + {"HORIZONTAL_ALWAYS", 0, nullptr, (void*)Fl_Scroll::HORIZONTAL_ALWAYS}, + {"VERTICAL_ALWAYS", 0, nullptr, (void*)Fl_Scroll::VERTICAL_ALWAYS}, + {"BOTH_ALWAYS", 0, nullptr, (void*)Fl_Scroll::BOTH_ALWAYS}, + {nullptr}}; + +Fl_Widget *Scroll_Node::enter_live_mode(int) { + Fl_Group *grp = new Fl_Scroll(o->x(), o->y(), o->w(), o->h()); + grp->show(); + return propagate_live_mode(grp); +} + +void Scroll_Node::copy_properties() { + Group_Node::copy_properties(); + Fl_Scroll *s = (Fl_Scroll*)o, *d = (Fl_Scroll*)live_widget; + d->scroll_to(s->xposition(), s->yposition()); + d->type(s->type()); + d->scrollbar.align(s->scrollbar.align()); + d->hscrollbar.align(s->hscrollbar.align()); +} + +// ---- Tile_Node --------------------------------------------------- MARK: - + +Tile_Node Tile_Node::prototype; // the "factory" + +const char tile_type_name[] = "Fl_Tile"; + +void Tile_Node::copy_properties() { + Group_Node::copy_properties(); + // no additional properties +} + +// ---- Wizard_Node ------------------------------------------------ MARK: - + +Wizard_Node Wizard_Node::prototype; // the "factory" + +const char wizard_type_name[] = "Fl_Wizard"; + +// Override group's resize behavior to do nothing to children: +void Fl_Wizard_Proxy::resize(int X, int Y, int W, int H) { + if (Fluid.proj.tree.allow_layout > 0) { + Fl_Wizard::resize(X, Y, W, H); + } else { + Fl_Widget::resize(X, Y, W, H); + } + redraw(); +} + +/** + Override draw() to make groups with no box or flat box background visible. + */ +void Fl_Wizard_Proxy::draw() { + if (Fluid.show_ghosted_outline && (box() == FL_NO_BOX)) { + fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); + } + Fl_Wizard::draw(); +} + diff --git a/fluid/nodes/Group_Node.h b/fluid/nodes/Group_Node.h new file mode 100644 index 000000000..6b6fe9fcd --- /dev/null +++ b/fluid/nodes/Group_Node.h @@ -0,0 +1,261 @@ +// +// Group Node header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2023 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_NODES_GROUP_NODE_H +#define FLUID_NODES_GROUP_NODE_H + +#include "nodes/Widget_Node.h" + +#include +#include +#include +#include + +void group_cb(Fl_Widget *, void *); +void ungroup_cb(Fl_Widget *, void *); + +// ---- Group_Node -------------------------------------------------- MARK: - + +/** + Proxy group to use in place of Fl_Group in the interactive window. + + In an interactive environment, groups should not automatically resize their + children. This proxy disables the layout of children by default. Children + layout propagation may be enable temporarily by incrementing `allow_layout` + before resizing and decrementing it again afterwards. + */ +class Fl_Group_Proxy : public Fl_Group { +public: + Fl_Group_Proxy(int X,int Y,int W,int H) : Fl_Group(X, Y, W, H) { Fl_Group::current(nullptr); } + void resize(int x, int y, int w, int h) override; + void draw() override; +}; + +class Group_Node : public Widget_Node +{ +public: + typedef Widget_Node super; + static Group_Node prototype; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override {return "Fl_Group";} + const char *alt_type_name() override {return "fltk::Group";} + Fl_Widget *widget(int X,int Y,int W,int H) override { + Fl_Group_Proxy *g = new Fl_Group_Proxy(X,Y,W,H); Fl_Group::current(nullptr); return g;} + Widget_Node *_make() override {return new Group_Node();} + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + void add_child(Node*, Node*) override; + void move_child(Node*, Node*) override; + void remove_child(Node*) override; + int can_have_children() const override {return 1;} + Type type() const override { return Type::Group; } + bool is_a(Type inType) const override { return (inType==Type::Group) ? true : super::is_a(inType); } + Fl_Widget *enter_live_mode(int top=0) override; + void leave_live_mode() override; + void copy_properties() override; +}; + +// ---- Pack_Node --------------------------------------------------- MARK: - + +extern const char pack_type_name[]; +extern Fl_Menu_Item pack_type_menu[]; + +class Pack_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Pack_Node prototype; +private: + Fl_Menu_Item *subtypes() override {return pack_type_menu;} +public: + const char *type_name() override {return pack_type_name;} + const char *alt_type_name() override {return "fltk::PackedGroup";} + Widget_Node *_make() override {return new Pack_Node();} + Type type() const override { return Type::Pack; } + bool is_a(Type inType) const override { return (inType==Type::Pack) ? true : super::is_a(inType); } + Fl_Widget *enter_live_mode(int top=0) override; + void copy_properties() override; +}; + +// ---- Flex_Node --------------------------------------------------- MARK: - + +extern const char flex_type_name[]; +extern Fl_Menu_Item flex_type_menu[]; + +class Fl_Flex_Proxy : public Fl_Flex { +public: + Fl_Flex_Proxy(int X,int Y,int W,int H) : Fl_Flex(X, Y, W, H) { Fl_Group::current(nullptr); } + void resize(int x, int y, int w, int h) override; + void draw() override; +}; + +class Flex_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Flex_Node prototype; +private: + Fl_Menu_Item *subtypes() override {return flex_type_menu;} + int fixedSizeTupleSize; /* number of pairs in array */ + int *fixedSizeTuple; /* [ index, size, index2, size2, ... ] */ + int suspend_auto_layout; +public: + Flex_Node() : fixedSizeTupleSize(0), fixedSizeTuple(nullptr), suspend_auto_layout(0) { } + const char *type_name() override {return flex_type_name;} + const char *alt_type_name() override {return "fltk::FlexGroup";} + Widget_Node *_make() override { return new Flex_Node(); } + Fl_Widget *widget(int X,int Y,int W,int H) override { + Fl_Flex *g = new Fl_Flex_Proxy(X,Y,W,H); Fl_Group::current(nullptr); return g;} + Type type() const override { return Type::Flex; } + bool is_a(Type inType) const override { return (inType==Type::Flex) ? true : super::is_a(inType); } + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + Fl_Widget *enter_live_mode(int top=0) override; + void copy_properties() override; + void copy_properties_for_children() override; + void postprocess_read() override; + void write_code2(fld::io::Code_Writer& f) override; +// void add_child(Node*, Node*) override; +// void move_child(Node*, Node*) override; + void remove_child(Node*) override; + void layout_widget() override; + void change_subtype_to(int n); + void insert_child_at(Fl_Widget *child, int x, int y); + void keyboard_move_child(Widget_Node*, int key); + static int parent_is_flex(Node*); + static int size(Node*, char fixed_only=0); + static int is_fixed(Node*); +}; + +// ---- Table_Node -------------------------------------------------- MARK: - + +class Table_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Table_Node prototype; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override { return "Fl_Table"; } + const char *alt_type_name() override { return "fltk::TableGroup"; } + Widget_Node *_make() override { return new Table_Node(); } + Fl_Widget *widget(int X, int Y, int W, int H) override; + Type type() const override { return Type::Table; } + bool is_a(Type inType) const override { return (inType==Type::Table) ? true : super::is_a(inType); } + Fl_Widget *enter_live_mode(int top=0) override; + void add_child(Node*, Node*) override; + void move_child(Node*, Node*) override; + void remove_child(Node*) override; +}; + +// ---- Tabs_Node --------------------------------------------------- MARK: - + +extern const char tabs_type_name[]; + +class Fl_Tabs_Proxy : public Fl_Tabs { +public: + Fl_Tabs_Proxy(int X,int Y,int W,int H) : Fl_Tabs(X,Y,W,H) {} + void resize(int,int,int,int) override; + void draw() override; +}; + +class Tabs_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Tabs_Node prototype; +public: + const char *type_name() override {return tabs_type_name;} + const char *alt_type_name() override {return "fltk::TabGroup";} + Fl_Widget *widget(int X,int Y,int W,int H) override { + Fl_Tabs_Proxy *g = new Fl_Tabs_Proxy(X,Y,W,H); Fl_Group::current(nullptr); return g;} + Widget_Node *_make() override {return new Tabs_Node();} + Node* click_test(int,int) override; + void add_child(Node*, Node*) override; + void remove_child(Node*) override; + Type type() const override { return Type::Tabs; } + bool is_a(Type inType) const override { return (inType==Type::Tabs) ? true : super::is_a(inType); } + Fl_Widget *enter_live_mode(int top=0) override; +}; + +// ---- Scroll_Node ------------------------------------------------- MARK: - + +extern const char scroll_type_name[]; +extern Fl_Menu_Item scroll_type_menu[]; + +class Scroll_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Scroll_Node prototype; +private: + Fl_Menu_Item *subtypes() override {return scroll_type_menu;} +public: + const char *type_name() override {return scroll_type_name;} + const char *alt_type_name() override {return "fltk::ScrollGroup";} + Widget_Node *_make() override {return new Scroll_Node();} + Type type() const override { return Type::Scroll; } + bool is_a(Type inType) const override { return (inType==Type::Scroll) ? true : super::is_a(inType); } + Fl_Widget *enter_live_mode(int top=0) override; + void copy_properties() override; +}; + +// ---- Tile_Node --------------------------------------------------- MARK: - + +extern const char tile_type_name[]; + +class Tile_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Tile_Node prototype; +public: + const char *type_name() override {return tile_type_name;} + const char *alt_type_name() override {return "fltk::TileGroup";} + Widget_Node *_make() override {return new Tile_Node();} + Type type() const override { return Type::Tile; } + bool is_a(Type inType) const override { return (inType==Type::Tile) ? true : super::is_a(inType); } + void copy_properties() override; +}; + +// ---- Wizard_Node ------------------------------------------------- MARK: - + +class Fl_Wizard_Proxy : public Fl_Wizard { +public: + Fl_Wizard_Proxy(int X,int Y,int W,int H) : Fl_Wizard(X,Y,W,H) {} + void resize(int,int,int,int) override; + void draw() override; +}; + +extern const char wizard_type_name[]; + +class Wizard_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Wizard_Node prototype; +public: + const char *type_name() override {return wizard_type_name;} + const char *alt_type_name() override {return "fltk::WizardGroup";} + Fl_Widget *widget(int X,int Y,int W,int H) override { + Fl_Wizard_Proxy *g = new Fl_Wizard_Proxy(X,Y,W,H); Fl_Group::current(nullptr); return g;} + Widget_Node *_make() override {return new Wizard_Node();} + Type type() const override { return Type::Wizard; } + bool is_a(Type inType) const override { return (inType==Type::Wizard) ? true : super::is_a(inType); } +}; + +#endif // FLUID_NODES_GROUP_NODE_H diff --git a/fluid/nodes/Menu_Node.cxx b/fluid/nodes/Menu_Node.cxx new file mode 100644 index 000000000..2a76b833a --- /dev/null +++ b/fluid/nodes/Menu_Node.cxx @@ -0,0 +1,876 @@ +// +// Menu Node 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 "nodes/Menu_Node.h" + +#include "Fluid.h" +#include "Project.h" +#include "app/Image_Asset.h" +#include "proj/mergeback.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "nodes/Window_Node.h" +#include "widgets/Formula_Input.h" +#include "widgets/Node_Browser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../src/flstring.h" + +#include +#include + +Fl_Menu_Item menu_item_type_menu[] = { + {"Normal",0,nullptr,(void*)nullptr}, + {"Toggle",0,nullptr,(void*)FL_MENU_BOX}, + {"Radio",0,nullptr,(void*)FL_MENU_RADIO}, + {nullptr}}; + +static void delete_dependents(Fl_Menu_Item *m) { + if (!m) + return; + int level = 0; + for (;;m++) { + if (m->label()==nullptr) { + if (level==0) { + break; + } else { + level--; + } + } + if (m->flags&FL_SUBMENU) + level++; + if (m->labeltype()==FL_MULTI_LABEL) + delete (Fl_Multi_Label*)m->label(); + } +} + +static void delete_menu(Fl_Menu_Item *m) { + if (!m) + return; + delete_dependents(m); + delete[] m; +} + +void Input_Choice_Node::build_menu() { + Fl_Input_Choice* w = (Fl_Input_Choice*)o; + // count how many Fl_Menu_Item structures needed: + int n = 0; + Node* q; + for (q = next; q && q->level > level; q = q->next) { + if (q->can_have_children()) n++; // space for null at end of submenu + n++; + } + if (!n) { + if (menusize) delete_menu((Fl_Menu_Item*)(w->menu())); + w->menu(nullptr); + menusize = 0; + } else { + n++; // space for null at end of menu + if (menusizemenu())); + menusize = n+10; + w->menu(new Fl_Menu_Item[menusize]); + } else { + if (menusize) delete_dependents((Fl_Menu_Item*)(w->menu())); + } + // Menus are already built during the .fl file reading process, so if the + // end of a menu list is not read yet, the end markers (label==nullptr) will + // not be set, and deleting dependents will randomly free memory. + // Clearing the array should avoid that. + memset( (void*)w->menu(), 0, menusize * sizeof(Fl_Menu_Item) ); + // fill them all in: + Fl_Menu_Item* m = (Fl_Menu_Item*)(w->menu()); + int lvl = level+1; + for (q = next; q && q->level > level; q = q->next) { + Menu_Item_Node* i = (Menu_Item_Node*)q; + if (i->o->image()) { + if (i->o->label() && i->o->label()[0]) { + Fl_Multi_Label *ml = new Fl_Multi_Label; + ml->labela = (char*)i->o->image(); + ml->labelb = i->o->label(); + ml->typea = FL_IMAGE_LABEL; + ml->typeb = FL_NORMAL_LABEL; + ml->label(m); + } else { + i->o->image()->label(m); + } + } else { + m->label(i->o->label() ? i->o->label() : "(nolabel)"); + m->labeltype(i->o->labeltype()); + } + m->shortcut(((Fl_Button*)(i->o))->shortcut()); + m->callback(nullptr,(void*)i); + m->flags = i->flags(); + m->labelfont(i->o->labelfont()); + m->labelsize(i->o->labelsize()); + m->labelcolor(i->o->labelcolor()); + if (q->can_have_children()) {lvl++; m->flags |= FL_SUBMENU;} + m++; + int l1 = + (q->next && q->next->is_a(Type::Menu_Item)) ? q->next->level : level; + while (lvl > l1) {m->label(nullptr); m++; lvl--;} + lvl = l1; + } + } + o->redraw(); +} + +/** + Create and add a new Menu Item node. + \param[in] strategy add after current or as last child + \return new Menu Item node + */ +Node *Menu_Item_Node::make(Strategy strategy) { + return Menu_Item_Node::make(0, strategy); +} + +/** + Create an add a specific Menu Item node. + \param[in] flags set to 0, FL_MENU_RADIO, FL_MENU_TOGGLE, or FL_SUBMENU + \param[in] strategy add after current or as last child + \return new Menu Item node + */ +Node* Menu_Item_Node::make(int flags, Strategy strategy) { + // Find a good insert position based on the current marked node + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !(p->is_a(Type::Menu_Manager_) || p->is_a(Type::Submenu))) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + if (!p) { + fl_message("Please select a menu widget or a menu item"); + return nullptr; + } + if (!o) { + o = new Fl_Button(0,0,100,20); // create template widget + } + + Menu_Item_Node* t = nullptr; + if (flags==FL_SUBMENU) { + t = new Submenu_Node(); + } else { + t = new Menu_Item_Node(); + } + t->o = new Fl_Button(0,0,100,20); + t->o->type(flags); + t->factory = this; + t->add(anchor, strategy); + if (strategy.source() == Strategy::FROM_USER) { + if (flags==FL_SUBMENU) { + t->label("submenu"); + } else { + t->label("item"); + } + } + return t; +} + +void group_selected_menuitems() { + // The group will be created in the parent group of the current menuitem + if (!Fluid.proj.tree.current->is_a(Type::Menu_Item)) { + return; + } + Menu_Item_Node *q = static_cast(Fluid.proj.tree.current); + Node *qq = Fluid.proj.tree.current->parent; + if (!qq || !(qq->is_a(Type::Menu_Manager_) || qq->is_a(Type::Submenu))) { + fl_message("Can't create a new submenu here."); + return; + } + Fluid.proj.undo.checkpoint(); + Fluid.proj.undo.suspend(); + Widget_Node *n = (Widget_Node*)(q->make(FL_SUBMENU, Strategy::AFTER_CURRENT)); + for (Node *t = qq->next; t && (t->level > qq->level);) { + if (t->level != n->level || t == n || !t->selected) { + t = t->next; + continue; + } + Node *nxt = t->remove(); + t->add(n, Strategy::AS_LAST_CHILD); + t = nxt; + } + widget_browser->rebuild(); + Fluid.proj.undo.resume(); + Fluid.proj.set_modflag(1); +} + +void ungroup_selected_menuitems() { + // Find the submenu + Node *qq = Fluid.proj.tree.current->parent; + Widget_Node *q = static_cast(Fluid.proj.tree.current); + int q_level = q->level; + if (!qq || !qq->is_a(Type::Submenu)) { + fl_message("Only menu items inside a submenu can be ungrouped."); + return; + } + Fluid.proj.undo.checkpoint(); + Fluid.proj.undo.suspend(); + Fluid.proj.tree.current = qq; + for (Node *t = qq->next; t && (t->level > qq->level);) { + if (t->level != q_level || !t->selected) { + t = t->next; + continue; + } + Node *nxt = t->remove(); + t->insert(qq); + t = nxt; + } + if (!qq->next || (qq->next->level <= qq->level)) { + qq->remove(); + delete qq; // qq has no children that need to be delete + } + Fluid.proj.tree.current = q; + widget_browser->rebuild(); + Fluid.proj.undo.resume(); + Fluid.proj.set_modflag(1); +} + + +/** + Create and add a new Checkbox Menu Item node. + \param[in] strategy add after current or as last child + \return new node + */ +Node *Checkbox_Menu_Item_Node::make(Strategy strategy) { + return Menu_Item_Node::make(FL_MENU_TOGGLE, strategy); +} + +/** + Create and add a new Radio ButtonMenu Item node. + \param[in] strategy add after current or as last child + \return new node + */ +Node *Radio_Menu_Item_Node::make(Strategy strategy) { + return Menu_Item_Node::make(FL_MENU_RADIO, strategy); +} + +/** + Create and add a new Submenu Item node. + \param[in] strategy add after current or as last child + \return new node + */ +Node *Submenu_Node::make(Strategy strategy) { + return Menu_Item_Node::make(FL_SUBMENU, strategy); +} + +Menu_Item_Node Menu_Item_Node::prototype; + +Checkbox_Menu_Item_Node Checkbox_Menu_Item_Node::prototype; + +Radio_Menu_Item_Node Radio_Menu_Item_Node::prototype; + +Submenu_Node Submenu_Node::prototype; + +//////////////////////////////////////////////////////////////// +// Writing the C code: + +// test functions in Widget_Node.C: +int is_name(const char *c); +const char *array_name(Widget_Node *o); +int isdeclare(const char *c); + +// Search backwards to find the parent menu button and return it's name. +// Also put in i the index into the button's menu item array belonging +// to this menu item. +const char* Menu_Item_Node::menu_name(fld::io::Code_Writer& f, int& i) { + i = 0; + Node* t = prev; + while (t && t->is_a(Type::Menu_Item)) { + // be sure to count the {0} that ends a submenu: + if (t->level > t->next->level) i += (t->level - t->next->level); + // detect empty submenu: + else if (t->level == t->next->level && t->can_have_children()) i++; + t = t->prev; + i++; + } + if (!t) return "\n#error Menu_Item_Node::menu_name, invalid f\n"; + return f.unique_id(t, "menu", t->name(), t->label()); +} + +void Menu_Item_Node::write_static(fld::io::Code_Writer& f) { + if (image && label() && label()[0]) { + f.write_h_once("#include "); + f.write_h_once("#include "); + } + if (callback() && is_name(callback()) && !user_defined(callback())) + f.write_h_once("extern void %s(Fl_Menu_*, %s);", callback(), + user_data_type() ? user_data_type() : "void*"); + for (int n=0; n < NUM_EXTRA_CODE; n++) { + if (extra_code(n) && isdeclare(extra_code(n))) + f.write_h_once("%s", extra_code(n)); + } + if (callback() && !is_name(callback())) { + // see if 'o' or 'v' used, to prevent unused argument warnings: + int use_o = 0; + int use_v = 0; + const char *d; + for (d = callback(); *d;) { + if (*d == 'o' && !is_id(d[1])) use_o = 1; + if (*d == 'v' && !is_id(d[1])) use_v = 1; + do d++; while (is_id(*d)); + while (*d && !is_id(*d)) d++; + } + const char* cn = callback_name(f); + const char* k = class_name(1); + if (k) { + f.write_c("\nvoid %s::%s_i(Fl_Menu_*", k, cn); + } else { + f.write_c("\nstatic void %s(Fl_Menu_*", cn); + } + if (use_o) f.write_c(" o"); + const char* ut = user_data_type() ? user_data_type() : "void*"; + f.write_c(", %s", ut); + if (use_v) f.write_c(" v"); + f.write_c(") {\n"); + // Matt: disabled f.tag(FD_TAG_GENERIC, 0); + f.write_c_indented(callback(), 1, 0); + if (*(d-1) != ';' && *(d-1) != '}') { + const char *p = strrchr(callback(), '\n'); + if (p) p ++; + else p = callback(); + // Only add trailing semicolon if the last line is not a preprocessor + // statement... + if (*p != '#' && *p) f.write_c(";"); + } + f.write_c("\n"); + // Matt: disabled f.tag(FD_TAG_MENU_CALLBACK, get_uid()); + f.write_c("}\n"); + + // If the menu item is part of a Class or Widget Class, FLUID generates + // a dummy static callback which retrieves a pointer to the class and then + // calls the original callback from within the class context. + // k is the name of the enclosing class (or classes) + if (k) { + // Implement the callback as a static member function + f.write_c("void %s::%s(Fl_Menu_* o, %s v) {\n", k, cn, ut); + // Find the Fl_Menu_ container for this menu item + Node* t = parent; while (t->is_a(Type::Menu_Item)) t = t->parent; + if (t) { + Widget_Node *tw = (t->is_widget()) ? static_cast(t) : nullptr; + Node *q = nullptr; + // Generate code to call the callback + if (tw->is_a(Type::Menu_Bar) && ((Menu_Bar_Node*)tw)->is_sys_menu_bar()) { + // Fl_Sys_Menu_Bar removes itself from any parent on macOS, so we + // wrapped it in a class and remeber the parent class in a new + // class memeber variable. + Menu_Bar_Node *tmb = (Menu_Bar_Node*)tw; + f.write_c("%s%s* sys_menu_bar = ((%s*)o);\n", f.indent(1), + tmb->sys_menubar_proxy_name(), tmb->sys_menubar_proxy_name()); + f.write_c("%s%s* parent_class = ((%s*)sys_menu_bar->_parent_class);\n", + f.indent(1), k, k); + f.write_c("%sparent_class->%s_i(o,v);\n}\n", + f.indent(1), cn); + } else { + f.write_c("%s((%s*)(o", f.indent(1), k); + // The class pointer is in the user_data field of the top widget + if (t && t->is_a(Type::Input_Choice)) { + // Go up one more level for Fl_Input_Choice, as these are groups themselves + f.write_c("->parent()"); + } + // Now generate code to find the topmost widget in this class + for (t = t->parent; t && t->is_widget() && !is_class(); q = t, t = t->parent) + f.write_c("->parent()"); + // user_data is cast into a pointer to the + if (!q || !q->is_a(Type::Widget_Class)) + f.write_c("->user_data()"); + f.write_c("))->%s_i(o,v);\n}\n", cn); + } + } else { + f.write_c("#error Enclosing Fl_Menu_* not found\n"); + } + } + } + if (image) { + if (!f.c_contains(image)) + image->write_static(f, compress_image_); + } + if (next && next->is_a(Type::Menu_Item)) return; + // okay, when we hit last item in the menu we have to write the + // entire array out: + const char* k = class_name(1); + if (k) { + int i; + f.write_c("\nFl_Menu_Item %s::%s[] = {\n", k, menu_name(f, i)); + } else { + int i; + f.write_c("\nFl_Menu_Item %s[] = {\n", menu_name(f, i)); + } + Node* t = prev; while (t && t->is_a(Type::Menu_Item)) t = t->prev; + for (Node* q = t->next; q && q->is_a(Type::Menu_Item); q = q->next) { + ((Menu_Item_Node*)q)->write_item(f); + int thislevel = q->level; if (q->can_have_children()) thislevel++; + int nextlevel = + (q->next && q->next->is_a(Type::Menu_Item)) ? q->next->level : t->level+1; + while (thislevel > nextlevel) {f.write_c(" {0,0,0,0,0,0,0,0,0},\n"); thislevel--;} + } + f.write_c(" {0,0,0,0,0,0,0,0,0}\n};\n"); + + if (k) { + // Write menu item variables... + t = prev; while (t && t->is_a(Type::Menu_Item)) t = t->prev; + for (Node* q = t->next; q && q->is_a(Type::Menu_Item); q = q->next) { + Menu_Item_Node *m = (Menu_Item_Node*)q; + const char *c = array_name(m); + if (c) { + if (c==m->name()) { + // assign a menu item address directly to a variable + int i; + const char* n = ((Menu_Item_Node *)q)->menu_name(f, i); + f.write_c("Fl_Menu_Item* %s::%s = %s::%s + %d;\n", k, c, k, n, i); + } else { + // if the name is an array, only define the array. + // The actual assignment is in write_code1(fld::io::Code_Writer& f) + f.write_c("Fl_Menu_Item* %s::%s;\n", k, c); + } + } + } + } +} + +int Menu_Item_Node::flags() { + int i = o->type(); + if (((Fl_Button*)o)->value()) i |= FL_MENU_VALUE; + if (!o->active()) i |= FL_MENU_INACTIVE; + if (!o->visible()) i |= FL_MENU_INVISIBLE; + if (can_have_children()) { + if (user_data() == nullptr) i |= FL_SUBMENU; + else i |= FL_SUBMENU_POINTER; + } + if (hotspot()) i |= FL_MENU_DIVIDER; + return i; +} + +void Menu_Item_Node::write_item(fld::io::Code_Writer& f) { + static const char * const labeltypes[] = { + "FL_NORMAL_LABEL", + "FL_NO_LABEL", + "FL_SHADOW_LABEL", + "FL_ENGRAVED_LABEL", + "FL_EMBOSSED_LABEL", + "FL_MULTI_LABEL", + "FL_ICON_LABEL", + "FL_IMAGE_LABEL" + }; + + write_comment_inline_c(f, " "); + f.write_c(" {"); + if (label() && label()[0]) + switch (Fluid.proj.i18n_type) { + case fld::I18n_Type::GNU: + // we will call i18n when the menu is instantiated for the first time + f.write_c("%s(", Fluid.proj.i18n_gnu_static_function.c_str()); + f.write_cstring(label()); + f.write_c(")"); + break; + case fld::I18n_Type::POSIX: + // fall through: strings can't be translated before a catalog is chosen + default: + f.write_cstring(label()); + } + else + f.write_c("\"\""); + if (((Fl_Button*)o)->shortcut()) { + int s = ((Fl_Button*)o)->shortcut(); + f.write_c(", "); + if (Fluid.proj.use_FL_COMMAND) { + if (s & FL_CTRL) { f.write_c("FL_CONTROL|"); s &= ~FL_CTRL; } + if (s & FL_META) { f.write_c("FL_COMMAND|"); s &= ~FL_META; } + } else { + if (s & FL_CTRL) { f.write_c("FL_CTRL|"); s &= ~FL_CTRL; } + if (s & FL_META) { f.write_c("FL_META|"); s &= ~FL_META; } + } + if (s & FL_SHIFT) { f.write_c("FL_SHIFT|"); s &= ~FL_SHIFT; } + if (s & FL_ALT) { f.write_c("FL_ALT|"); s &= ~FL_ALT; } + if ((s < 127) && isprint(s)) + f.write_c("'%c', ", s); + else + f.write_c("0x%x, ", s); + } else { + f.write_c(", 0, "); + } + if (callback()) { + const char* k = is_name(callback()) ? nullptr : class_name(1); + if (k) { + f.write_c(" (Fl_Callback*)%s::%s,", k, callback_name(f)); + } else { + f.write_c(" (Fl_Callback*)%s,", callback_name(f)); + } + } else + f.write_c(" 0,"); + if (user_data()) + f.write_c(" (void*)(%s),", user_data()); + else + f.write_c(" 0,"); + f.write_c(" %d, (uchar)%s, %d, %d, %d", flags(), + labeltypes[o->labeltype()], o->labelfont(), o->labelsize(), o->labelcolor()); + f.write_c("},\n"); +} + +void start_menu_initialiser(fld::io::Code_Writer& f, int &initialized, const char *name, int index) { + if (!initialized) { + initialized = 1; + f.write_c("%s{ Fl_Menu_Item* o = &%s[%d];\n", f.indent(), name, index); + f.indentation++; + } +} + +void Menu_Item_Node::write_code1(fld::io::Code_Writer& f) { + int i; const char* mname = menu_name(f, i); + + if (!prev->is_a(Type::Menu_Item)) { + // for first menu item, declare the array + if (class_name(1)) { + f.write_h("%sstatic Fl_Menu_Item %s[];\n", f.indent(1), mname); + } else { + f.write_h("extern Fl_Menu_Item %s[];\n", mname); + } + } + + const char *c = array_name(this); + if (c) { + if (class_name(1)) { + f.write_public(public_); + f.write_h("%sstatic Fl_Menu_Item *%s;\n", f.indent(1), c); + } else { + if (c==name()) + f.write_h("#define %s (%s+%d)\n", c, mname, i); + else + f.write_h("extern Fl_Menu_Item *%s;\n", c); + } + } + + if (callback()) { + if (!is_name(callback()) && class_name(1)) { + const char* cn = callback_name(f); + const char* ut = user_data_type() ? user_data_type() : "void*"; + f.write_public(0); + f.write_h("%sinline void %s_i(Fl_Menu_*, %s);\n", f.indent(1), cn, ut); + f.write_h("%sstatic void %s(Fl_Menu_*, %s);\n", f.indent(1), cn, ut); + } + } + + int menuItemInitialized = 0; + // if the name is an array variable, assign the value here + if (name() && strchr(name(), '[')) { + f.write_c("%s%s = &%s[%d];\n", f.indent_plus(1), name(), mname, i); + } + if (image) { + start_menu_initialiser(f, menuItemInitialized, mname, i); + if (label() && label()[0]) { + f.write_c("%sFl_Multi_Label *ml = new Fl_Multi_Label;\n", f.indent()); + f.write_c("%sml->labela = (char*)", f.indent()); + image->write_inline(f); + f.write_c(";\n"); + if (Fluid.proj.i18n_type==fld::I18n_Type::NONE) { + f.write_c("%sml->labelb = o->label();\n", f.indent()); + } else if (Fluid.proj.i18n_type==fld::I18n_Type::GNU) { + f.write_c("%sml->labelb = %s(o->label());\n", + f.indent(), Fluid.proj.i18n_gnu_function.c_str()); + } else if (Fluid.proj.i18n_type==fld::I18n_Type::POSIX) { + f.write_c("%sml->labelb = catgets(%s,%s,i+%d,o->label());\n", + f.indent(), + Fluid.proj.i18n_pos_file.empty() ? "_catalog" : Fluid.proj.i18n_pos_file.c_str(), + Fluid.proj.i18n_pos_set.c_str(), msgnum()); + } + f.write_c("%sml->typea = FL_IMAGE_LABEL;\n", f.indent()); + f.write_c("%sml->typeb = FL_NORMAL_LABEL;\n", f.indent()); + f.write_c("%sml->label(o);\n", f.indent()); + } else { + image->write_code(f, 0, "o"); + } + } + if ((Fluid.proj.i18n_type != fld::I18n_Type::NONE) && label() && label()[0]) { + Fl_Labeltype t = o->labeltype(); + if (image) { + // label was already copied a few lines up + } else if ( t==FL_NORMAL_LABEL || t==FL_SHADOW_LABEL + || t==FL_ENGRAVED_LABEL || t==FL_EMBOSSED_LABEL) { + start_menu_initialiser(f, menuItemInitialized, mname, i); + if (Fluid.proj.i18n_type==fld::I18n_Type::GNU) { + f.write_c("%so->label(%s(o->label()));\n", + f.indent(), Fluid.proj.i18n_gnu_function.c_str()); + } else if (Fluid.proj.i18n_type==fld::I18n_Type::POSIX) { + f.write_c("%so->label(catgets(%s,%s,i+%d,o->label()));\n", + f.indent(), + Fluid.proj.i18n_pos_file.empty() ? "_catalog" : Fluid.proj.i18n_pos_file.c_str(), + Fluid.proj.i18n_pos_set.c_str(), msgnum()); + } + } + } + for (int n=0; n < NUM_EXTRA_CODE; n++) { + if (extra_code(n) && !isdeclare(extra_code(n))) { + start_menu_initialiser(f, menuItemInitialized, mname, i); + f.write_c("%s%s\n", f.indent(), extra_code(n)); + } + } + if (menuItemInitialized) { + f.indentation--; + f.write_c("%s}\n",f.indent()); + } +} + +void Menu_Item_Node::write_code2(fld::io::Code_Writer&) {} + +//////////////////////////////////////////////////////////////// +// This is the base class for widgets that contain a menu (ie +// subclasses of Fl_Menu_. +// This is a parent widget and menu items can be added as +// children. An actual array of Fl_Menu_Items is kept parallel +// with the child objects and updated as they change. + +void Menu_Base_Node::build_menu() { + Fl_Menu_* w = (Fl_Menu_*)o; + // count how many Fl_Menu_Item structures needed: + int n = 0; + Node* q; + for (q = next; q && q->level > level; q = q->next) { + if (q->can_have_children()) n++; // space for null at end of submenu + n++; + } + if (!n) { + if (menusize) delete_menu((Fl_Menu_Item*)(w->menu())); + w->menu(nullptr); + menusize = 0; + } else { + n++; // space for null at end of menu + if (menusizemenu())); + menusize = n+10; + w->menu(new Fl_Menu_Item[menusize]); + } else { + if (menusize) delete_dependents((Fl_Menu_Item*)(w->menu())); + } + // Menus are already built during the .fl file reading process, so if the + // end of a menu list is not read yet, the end markers (label==nullptr) will + // not be set, and deleting dependents will randomly free memory. + // Clearing the array should avoid that. + memset( (void*)w->menu(), 0, menusize * sizeof(Fl_Menu_Item) ); + // fill them all in: + Fl_Menu_Item* m = (Fl_Menu_Item*)(w->menu()); + int lvl = level+1; + for (q = next; q && q->level > level; q = q->next) { + Menu_Item_Node* i = (Menu_Item_Node*)q; + if (i->o->image()) { + if (i->o->label() && i->o->label()[0]) { + Fl_Multi_Label *ml = new Fl_Multi_Label; + ml->labela = (char*)i->o->image(); + ml->labelb = i->o->label(); + ml->typea = FL_IMAGE_LABEL; + ml->typeb = FL_NORMAL_LABEL; + ml->label(m); + } else { + i->o->image()->label(m); + } + } else { + m->label(i->o->label() ? i->o->label() : "(nolabel)"); + m->labeltype(i->o->labeltype()); + } + m->shortcut(((Fl_Button*)(i->o))->shortcut()); + m->callback(nullptr,(void*)i); + m->flags = i->flags() | i->o->type(); + m->labelfont(i->o->labelfont()); + m->labelsize(i->o->labelsize()); + m->labelcolor(i->o->labelcolor()); + if (q->can_have_children()) {lvl++; m->flags |= FL_SUBMENU;} + m++; + int l1 = + (q->next && q->next->is_a(Type::Menu_Item)) ? q->next->level : level; + while (lvl > l1) {m->label(nullptr); m++; lvl--;} + lvl = l1; + } + } + o->redraw(); +} + +Node* Menu_Base_Node::click_test(int, int) { + if (selected) return nullptr; // let user move the widget + Fl_Menu_* w = (Fl_Menu_*)o; + if (!menusize) return nullptr; + const Fl_Menu_Item* save = w->mvalue(); + w->value((Fl_Menu_Item*)nullptr); + Fl::pushed(w); + w->handle(FL_PUSH); + Fl::focus(nullptr); + const Fl_Menu_Item* m = w->mvalue(); + if (m) { + // restore the settings of toggles & radio items: + if (m->flags & (FL_MENU_RADIO | FL_MENU_TOGGLE)) build_menu(); + return (Node*)(m->user_data()); + } + w->value(save); + return this; +} + +void Menu_Manager_Node::write_code2(fld::io::Code_Writer& f) { + if (next && next->is_a(Type::Menu_Item)) { + f.write_c("%s%s->menu(%s);\n", f.indent(), name() ? name() : "o", + f.unique_id(this, "menu", name(), label())); + } + Widget_Node::write_code2(f); +} + +void Menu_Base_Node::copy_properties() { + Widget_Node::copy_properties(); + Fl_Menu_ *s = (Fl_Menu_*)o, *d = (Fl_Menu_*)live_widget; + d->menu(s->menu()); + d->down_box(s->down_box()); + d->textcolor(s->textcolor()); + d->textfont(s->textfont()); + d->textsize(s->textsize()); +} + +//////////////////////////////////////////////////////////////// + +Fl_Menu_Item button_type_menu[] = { + {"normal",0,nullptr,(void*)nullptr}, + {"popup1",0,nullptr,(void*)Fl_Menu_Button::POPUP1}, + {"popup2",0,nullptr,(void*)Fl_Menu_Button::POPUP2}, + {"popup3",0,nullptr,(void*)Fl_Menu_Button::POPUP3}, + {"popup12",0,nullptr,(void*)Fl_Menu_Button::POPUP12}, + {"popup23",0,nullptr,(void*)Fl_Menu_Button::POPUP23}, + {"popup13",0,nullptr,(void*)Fl_Menu_Button::POPUP13}, + {"popup123",0,nullptr,(void*)Fl_Menu_Button::POPUP123}, + {nullptr}}; + +Menu_Button_Node Menu_Button_Node::prototype; + +//////////////////////////////////////////////////////////////// + +Fl_Menu_Item dummymenu[] = {{"CHOICE"},{nullptr}}; + +Choice_Node Choice_Node::prototype; + +Input_Choice_Node Input_Choice_Node::prototype; + +void Input_Choice_Node::copy_properties() { + Widget_Node::copy_properties(); + Fl_Input_Choice *s = (Fl_Input_Choice*)o, *d = (Fl_Input_Choice*)live_widget; + d->menu(s->menu()); + d->down_box(s->down_box()); + d->textcolor(s->textcolor()); + d->textfont(s->textfont()); + d->textsize(s->textsize()); +} + +Node* Input_Choice_Node::click_test(int, int) { + if (selected) return nullptr; // let user move the widget + Fl_Menu_* w = ((Fl_Input_Choice*)o)->menubutton(); + if (!menusize) return nullptr; + const Fl_Menu_Item* save = w->mvalue(); + w->value((Fl_Menu_Item*)nullptr); + Fl::pushed(w); + w->handle(FL_PUSH); + Fl::focus(nullptr); + const Fl_Menu_Item* m = w->mvalue(); + if (m) { + // restore the settings of toggles & radio items: + if (m->flags & (FL_MENU_RADIO | FL_MENU_TOGGLE)) build_menu(); + return (Node*)(m->user_data()); + } + w->value(save); + return this; +} + +//////////////////////////////////////////////////////////////// + +Menu_Bar_Node Menu_Bar_Node::prototype; + +Fl_Menu_Item menu_bar_type_menu[] = { + {"Fl_Menu_Bar",0,nullptr,(void*)nullptr}, + {"Fl_Sys_Menu_Bar",0,nullptr,(void*)1}, + {nullptr}}; + +Menu_Bar_Node::Menu_Bar_Node() +: _proxy_name(nullptr) +{ +} + +Menu_Bar_Node::~Menu_Bar_Node() { + if (_proxy_name) + ::free(_proxy_name); +} + +/** + \brief Return true if this is an Fl_Sys_Menu_Bar. + This test fails if subclass() is the name of a class that the user may have + derived from Fl_Sys_Menu_Bar. + */ +bool Menu_Bar_Node::is_sys_menu_bar() { + if (o->type()==1) return true; + return ( subclass() && (strcmp(subclass(), "Fl_Sys_Menu_Bar")==0) ); +} + +const char *Menu_Bar_Node::sys_menubar_name() { + if (subclass()) + return subclass(); + else + return "Fl_Sys_Menu_Bar"; +} + +const char *Menu_Bar_Node::sys_menubar_proxy_name() { + if (!_proxy_name) + _proxy_name = (char*)::malloc(128); + ::snprintf(_proxy_name, 63, "%s_Proxy", sys_menubar_name()); + return _proxy_name; +} + + +void Menu_Bar_Node::write_static(fld::io::Code_Writer& f) { + super::write_static(f); + if (is_sys_menu_bar()) { + f.write_h_once("#include "); + if (is_in_class()) { + // Make room for a pointer to the enclosing class. + f.write_c_once( // must be less than 1024 bytes! + "\nclass %s: public %s {\n" + "public:\n" + " %s(int x, int y, int w, int h, const char *l=0L)\n" + " : %s(x, y, w, h, l) { }\n" + " void *_parent_class;\n" + "};\n", + sys_menubar_proxy_name(), sys_menubar_name(), + sys_menubar_proxy_name(), sys_menubar_name() + ); + } + } +} + +void Menu_Bar_Node::write_code1(fld::io::Code_Writer& f) { + super::write_code1(f); + if (is_sys_menu_bar() && is_in_class()) { + f.write_c("%s((%s*)%s)->_parent_class = (void*)this;\n", + f.indent(), sys_menubar_proxy_name(), name() ? name() : "o"); + } +} + +//void Menu_Bar_Node::write_code2(fld::io::Code_Writer& f) { +// super::write_code2(f); +//} + diff --git a/fluid/nodes/Menu_Node.h b/fluid/nodes/Menu_Node.h new file mode 100644 index 000000000..59d3b06bb --- /dev/null +++ b/fluid/nodes/Menu_Node.h @@ -0,0 +1,310 @@ +// +// Menu Node header file 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 +// + +// Type for creating all subclasses of Fl_Widget +// This should have the widget pointer in it, but it is still in the +// Node base class. +// + + +#ifndef FLUID_NODES_MENU_NODE_H +#define FLUID_NODES_MENU_NODE_H + +#include "nodes/Button_Node.h" + +#include "Fluid.h" +#include "app/Snap_Action.h" + +#include +#include +#include +#include +#include +#include + +extern Fl_Menu_Item dummymenu[]; +extern Fl_Menu_Item button_type_menu[]; +extern Fl_Menu_Item menu_item_type_menu[]; +extern Fl_Menu_Item menu_bar_type_menu[]; + +/** + \brief Manage all types on menu items. + Deriving Menu_Item_Node from Button_Node is intentional. For the purpose + of editing, a Menu Item is implemented with `o` pointing to an Fl_Button for + holding all properties. + */ +class Menu_Item_Node : public Button_Node +{ +public: + typedef Button_Node super; + static Menu_Item_Node prototype; +public: + Fl_Menu_Item* subtypes() override {return menu_item_type_menu;} + const char* type_name() override {return "MenuItem";} + const char* alt_type_name() override {return "fltk::Item";} + Node* make(Strategy strategy) override; + Node* make(int flags, Strategy strategy); + int is_button() const override {return 1;} // this gets shortcut to work + Fl_Widget* widget(int,int,int,int) override {return nullptr;} + Widget_Node* _make() override {return nullptr;} + virtual const char* menu_name(fld::io::Code_Writer& f, int& i); + int flags(); + void write_static(fld::io::Code_Writer& f) override; + void write_item(fld::io::Code_Writer& f); + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + int is_true_widget() const override { return 0; } + Type type() const override { return Type::Menu_Item; } + bool is_a(Type inType) const override { return (inType==Type::Menu_Item) ? true : super::is_a(inType); } +}; + +/** + \brief Manage Radio style Menu Items. + */ +class Radio_Menu_Item_Node : public Menu_Item_Node +{ +public: + typedef Menu_Item_Node super; + static Radio_Menu_Item_Node prototype; +public: + const char* type_name() override {return "RadioMenuItem";} + Node* make(Strategy strategy) override; + Type type() const override { return Type::Radio_Menu_Item; } + bool is_a(Type inType) const override { return (inType==Type::Radio_Menu_Item) ? true : super::is_a(inType); } +}; + +/** + \brief Manage Checkbox style Menu Items. + */ +class Checkbox_Menu_Item_Node : public Menu_Item_Node +{ +public: + typedef Menu_Item_Node super; + static Checkbox_Menu_Item_Node prototype; +public: + const char* type_name() override {return "CheckMenuItem";} + Node* make(Strategy strategy) override; + Type type() const override { return Type::Checkbox_Menu_Item; } + bool is_a(Type inType) const override { return (inType==Type::Checkbox_Menu_Item) ? true : super::is_a(inType); } +}; + +/** + \brief Manage Submenu style Menu Items. + Submenu Items are simply buttons just like all other menu items, but they + can also hold a pointer to a list of submenus, or have a flag set that + allows submenus to follow in the current array. As buttons, they can + be clicked by the user, and they will call their callback, if one is set. + */ +class Submenu_Node : public Menu_Item_Node +{ +public: + typedef Menu_Item_Node super; + static Submenu_Node prototype; +public: + Fl_Menu_Item* subtypes() override {return nullptr;} + const char* type_name() override {return "Submenu";} + const char* alt_type_name() override {return "fltk::ItemGroup";} + int can_have_children() const override {return 1;} + int is_button() const override {return 0;} // disable shortcut + Node* make(Strategy strategy) override; + // changes to submenu must propagate up so build_menu is called + // on the parent Menu_Node: + void add_child(Node*a, Node*b) override {parent->add_child(a,b);} + void move_child(Node*a, Node*b) override {parent->move_child(a,b);} + void remove_child(Node*a) override {parent->remove_child(a);} + Type type() const override { return Type::Submenu; } + bool is_a(Type inType) const override { return (inType==Type::Submenu) ? true : super::is_a(inType); } +}; + +// ----------------------------------------------------------------------------- + +/** + \brief Base class for all widgets that can have a pulldown menu attached. + Widgets with this type can be derived from Fl_Menu_ or from + Fl_Group (Fl_Input_Choice). + */ +class Menu_Manager_Node : public Widget_Node +{ + typedef Widget_Node super; +public: + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; + h = layout->textsize_not_null() + 8; + w = layout->textsize_not_null() * 6 + 8; + fld::app::Snap_Action::better_size(w, h); + } + int can_have_children() const override {return 1;} + int menusize; + virtual void build_menu() = 0; + Menu_Manager_Node() : Widget_Node() {menusize = 0;} + void add_child(Node*, Node*) override { build_menu(); } + void move_child(Node*, Node*) override { build_menu(); } + void remove_child(Node*) override { build_menu();} + Node* click_test(int x, int y) override = 0; + void write_code2(fld::io::Code_Writer& f) override; + void copy_properties() override = 0; + Type type() const override { return Type::Menu_Manager_; } + bool is_a(Type inType) const override { return (inType==Type::Menu_Manager_) ? true : super::is_a(inType); } +}; + +/** + \brief Manage the composite widget Input Choice. + \note Input Choice is a composite window, so `o` will be pointing to a widget + derived from Fl_Group. All menu related methods from Fl_Menu_Trait_Type must + be virtual and must be reimplemented here (click_test, build_menu, textstuff). + */ +class Input_Choice_Node : public Menu_Manager_Node +{ +public: + typedef Menu_Manager_Node super; + static Input_Choice_Node prototype; +private: + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Input_Choice *myo = (Fl_Input_Choice*)(w==4 ? ((Widget_Node*)this->factory)->o : this->o); + switch (w) { + case 4: + case 0: f = (Fl_Font)myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; + case 1: myo->textfont(f); break; + case 2: myo->textsize(s); break; + case 3: myo->textcolor(c); break; + } + return 1; + } +public: + ~Input_Choice_Node() { + if (menusize) delete[] (Fl_Menu_Item*)(((Fl_Input_Choice*)o)->menu()); + } + const char *type_name() override {return "Fl_Input_Choice";} + const char *alt_type_name() override {return "fltk::ComboBox";} + Node* click_test(int,int) override; + Fl_Widget *widget(int X,int Y,int W,int H) override { + Fl_Input_Choice *myo = new Fl_Input_Choice(X,Y,W,H,"input choice:"); + myo->menu(dummymenu); + myo->value("input"); + return myo; + } + Widget_Node *_make() override {return new Input_Choice_Node();} + void build_menu() override; + Type type() const override { return Type::Input_Choice; } + bool is_a(Type inType) const override { return (inType==Type::Input_Choice) ? true : super::is_a(inType); } + void copy_properties() override; +}; + +/** + \brief Base class to handle widgets that are derived from Fl_Menu_. + */ +class Menu_Base_Node : public Menu_Manager_Node +{ + typedef Menu_Manager_Node super; + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Menu_ *myo = (Fl_Menu_*)(w==4 ? ((Widget_Node*)this->factory)->o : this->o); + switch (w) { + case 4: + case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; + case 1: myo->textfont(f); break; + case 2: myo->textsize(s); break; + case 3: myo->textcolor(c); break; + } + return 1; + } +public: + int can_have_children() const override {return 1;} + void build_menu() override; + ~Menu_Base_Node() { + if (menusize) delete[] (Fl_Menu_Item*)(((Fl_Menu_*)o)->menu()); + } + Node* click_test(int x, int y) override; + void copy_properties() override; + Type type() const override { return Type::Menu_; } + bool is_a(Type inType) const override { return (inType==Type::Menu_) ? true : super::is_a(inType); } +}; + +extern Fl_Menu_Item button_type_menu[]; + +/** + \brief Make Menu Button widgets. + */ +class Menu_Button_Node : public Menu_Base_Node +{ +public: + typedef Menu_Base_Node super; + static Menu_Button_Node prototype; +private: + Fl_Menu_Item *subtypes() override {return button_type_menu;} +public: + const char *type_name() override {return "Fl_Menu_Button";} + const char *alt_type_name() override {return "fltk::MenuButton";} + Fl_Widget *widget(int X,int Y,int W,int H) override { + return new Fl_Menu_Button(X,Y,W,H,"menu");} + Widget_Node *_make() override {return new Menu_Button_Node();} + Type type() const override { return Type::Menu_Button; } + bool is_a(Type inType) const override { return (inType==Type::Menu_Button) ? true : super::is_a(inType); } +}; + + +/** + \brief Manage Choice type menu widgets. + */ +class Choice_Node : public Menu_Base_Node +{ +public: + typedef Menu_Base_Node super; + static Choice_Node prototype; +public: + const char *type_name() override {return "Fl_Choice";} + const char *alt_type_name() override {return "fltk::Choice";} + Fl_Widget *widget(int X,int Y,int W,int H) override { + Fl_Choice *myo = new Fl_Choice(X,Y,W,H,"choice:"); + myo->menu(dummymenu); + return myo; + } + Widget_Node *_make() override {return new Choice_Node();} + Type type() const override { return Type::Choice; } + bool is_a(Type inType) const override { return (inType==Type::Choice) ? true : super::is_a(inType); } +}; + + +/** + \brief Manage Menubar widgets. + */ +class Menu_Bar_Node : public Menu_Base_Node +{ +public: + typedef Menu_Base_Node super; + static Menu_Bar_Node prototype; +private: + Fl_Menu_Item *subtypes() override {return menu_bar_type_menu;} +public: + Menu_Bar_Node(); + ~Menu_Bar_Node() override; + const char *type_name() override {return "Fl_Menu_Bar";} + const char *alt_type_name() override {return "fltk::MenuBar";} + Fl_Widget *widget(int X,int Y,int W,int H) override {return new Fl_Menu_Bar(X,Y,W,H);} + Widget_Node *_make() override {return new Menu_Bar_Node();} + void write_static(fld::io::Code_Writer& f) override; + void write_code1(fld::io::Code_Writer& f) override; +// void write_code2(fld::io::Code_Writer& f) override; + Type type() const override { return Type::Menu_Bar; } + bool is_a(Type inType) const override { return (inType==Type::Menu_Bar) ? true : super::is_a(inType); } + bool is_sys_menu_bar(); + const char *sys_menubar_name(); + const char *sys_menubar_proxy_name(); +protected: + char *_proxy_name; +}; + + +#endif // FLUID_NODES_MENU_NODE_H diff --git a/fluid/nodes/Node.cxx b/fluid/nodes/Node.cxx new file mode 100644 index 000000000..194484a93 --- /dev/null +++ b/fluid/nodes/Node.cxx @@ -0,0 +1,1290 @@ +// +// Node base class 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 +// + +/// \defgroup fl_type Basic Node for all Widgets and Functions +/// \{ + +/** \class Node + Each object described by Fluid is one of these objects. They + are all stored in a double-linked list. + + The "type" of the object is covered by the virtual functions. + There will probably be a lot of these virtual functions. + + The type browser is also a list of these objects, but they + are "factory" instances, not "real" ones. These objects exist + only so the "make" method can be called on them. They are + not in the linked list and are not written to files or + copied or otherwise examined. + + The Node inheritance is currently: + --+-- Node + +-- Function_Node + +-- Code_Node + +-- CodeBlock_Node + +-+ Decl_Node + | +-- Fl_Data + +-- DeclBlock_Node + +-- Comment_Node + +-- Class_Node + +-+ Widget_Node, 'o' points to a class derived from Fl_Widget + +-+ Browser_Base_Node, 'o' is Fl_Browser + | +-+ Fl_Browser + | | +-- Fl_File_Browser + | +-- Fl_Check_Browser + +-- Tree_Node + +-- Help_View_Node + +-+ Valuator_Node, 'o' is Fl_Valuator_ + | +-- Counter_Node + | +-- Adjuster_Node + | +-- Dial_Node + | +-- Roller_Node + | +-- Slider_Node + | +-- Value_Input_Node + | +-- Value_Output_Node + +-+ Input_Node + | +-- Output_Node + +-+ Text_Display_Node + | +-- Text_Editor_Node + +-- Terminal_Node + +-- Box_Node + +-- Clock_Node + +-- Progress_Node + +-- Spinner_Node + +-+ Group_Node + | +-- Pack_Node + | +-- Flex_Node + | +-- Grid_Node + | +-- Table_Node + | +-- Tabs_Node + | +-- Scroll_Node + | +-- Tile_Node + | +-- Wizard_Node + | +-+ Window_Node + | +-- Widget_Class_Node + +-+ Menu_Manager_Node, 'o' is based on Fl_Widget + | +-+ Menu_Base_Node, 'o' is based on Fl_Menu_ + | | +-- Menu_Button_Node + | | +-- Choice_Node + | | +-- Menu_Bar_Node + | +-- Input_Choice_Node, 'o' is based on Fl_Input_Choice which is Fl_Group + +-+ Button_Node + +-- Return_Button_Node + +-- Repeat_Button_Node + +-- Light_Button_Node + +-- Check_Button_Node + +-- Round_Button_Node + +-+ Menu_Item_Node, 'o' is derived from Fl_Button in FLUID + +-- Radio_Menu_Item_Node + +-- Checkbox_Menu_Item_Node + +-- Fl_Submenu_Item_Type + +*/ + +#include "nodes/Node.h" + +#include "Fluid.h" +#include "Project.h" +#include "app/Snap_Action.h" +#include "app/shell_command.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "nodes/Function_Node.h" +#include "nodes/Widget_Node.h" +#include "nodes/Window_Node.h" +#include "nodes/Group_Node.h" +#include "rsrcs/pixmaps.h" +#include "widgets/Node_Browser.h" + +#include +#include +#include +#include "../src/flstring.h" + +#include +#include + +// ---- global variables + +Node *in_this_only; // set if menu popped-up in window + + +// ---- various functions + +#if 0 +#ifndef NDEBUG +/** + Print the current project tree to stderr. + */ +void print_project_tree() { + fprintf(stderr, "---- %s --->\n", Fluid.proj.projectfile_name().c_str()); + for (Node *t = Fluid.proj.tree.first; t; t = t->next) { + for (int i = t->level; i > 0; i--) + fprintf(stderr, ". "); + fprintf(stderr, "%s\n", subclassname(t)); + } +} +#endif + +#ifndef NDEBUG +/** + Check the validity of the project tree. + + Write problems with the project tree to stderr. + + \return true if the project tree is valid + */ +bool validate_project_tree() { + // Validate `first` and `last` + if (Fluid.proj.tree.first == nullptr) { + if (Fluid.proj.tree.last == nullptr) { + return true; + } else { + fprintf(stderr, "ERROR: `first` is nullptr, but `last` is not!\n"); + return false; + } + } + if (Fluid.proj.tree.last == nullptr) { + fprintf(stderr, "ERROR: `last` is nullptr, but `first` is not!\n"); + return false; + } + // Validate the branch linkage, parent links, etc. + return validate_branch(Fluid.proj.tree.first); +} +#endif + +#ifndef NDEBUG +/** + Check the validity of a Type branch that is not connected to the project. + + Write problems with the branch to stderr. + + \param[in] root the first node in a branch + \return true if the branch is correctly separated and valid + */ +bool validate_independent_branch(class Node *root) { + // Make sure that `first` and `last` do not point at any node in this branch + if (Fluid.proj.tree.first) { + for (Node *t = root; t; t = t->next) { + if (Fluid.proj.tree.first == t) { + fprintf(stderr, "ERROR: Branch is not independent, `first` is pointing to branch member!\n"); + return false; + } + } + } + if (Fluid.proj.tree.last) { + for (Node *t = root; t; t = t->next) { + if (Fluid.proj.tree.last == t) { + fprintf(stderr, "ERROR: Branch is not independent, `last` is pointing to branch member!\n"); + return false; + } + } + } + // Validate the branch linkage, parent links, etc. + return validate_branch(root); +} +#endif + +#ifndef NDEBUG +/** + Check the validity of a Type branch. + + Write problems with the branch to stderr. + + \param[in] root the first node in a branch + \return true if the branch is valid + */ +bool validate_branch(class Node *root) { + // Only check real branches + if (!root) { + fprintf(stderr, "WARNING: Branch is empty!\n"); + return false; + } + // Check relation between this and next node + for (Node *t = root; t; t = t->next) { + if (t->level < root->level) { + fprintf(stderr, "ERROR: Node in tree is above root level!\n"); + return false; + } + if (t->next) { + // Make sure that all `next` types have the `prev` member link back + if (t->next->prev != t) { + fprintf(stderr, "ERROR: Doubly linked list broken!\n"); + return false; + } + if (t->next->level > t->level) { + // Validate `level` changes + if (t->next->level - t->level > 1) { + fprintf(stderr, "ERROR: Child level increment greater than one!\n"); + return false; + } + // Ensure that this node can actually have children + if (!t->can_have_children()) { + fprintf(stderr, "ERROR: This parent must not have children!\n"); + return false; + } + } + } + // Validate the `parent` entry + for (Node *p = t->prev; ; p = p->prev) { + if (p == nullptr) { + if (t->parent != nullptr) { + fprintf(stderr, "ERROR: `parent` pointer should be nullptr!\n"); + return false; + } + break; + } + if (p->level < t->level) { + if (t->parent != p) { + fprintf(stderr, "ERROR: `parent` points to wrong parent!\n"); + return false; + } + break; + } + } + } + return true; +} +#endif +#endif + +void select_all_cb(Fl_Widget *,void *) { + Node *p = Fluid.proj.tree.current ? Fluid.proj.tree.current->parent : nullptr; + if (in_this_only) { + Node *t = p; + for (; t && t != in_this_only; t = t->parent) {/*empty*/} + if (t != in_this_only) p = in_this_only; + } + for (;;) { + if (p) { + int foundany = 0; + for (Node *t = p->next; t && t->level>p->level; t = t->next) { + if (!t->new_selected) {widget_browser->select(t,1,0); foundany = 1;} + } + if (foundany) break; + p = p->parent; + } else { + for (Node *t = Fluid.proj.tree.first; t; t = t->next) + widget_browser->select(t,1,0); + break; + } + } + selection_changed(p); +} + +void select_none_cb(Fl_Widget *,void *) { + Node *p = Fluid.proj.tree.current ? Fluid.proj.tree.current->parent : nullptr; + if (in_this_only) { + Node *t = p; + for (; t && t != in_this_only; t = t->parent) {/*empty*/} + if (t != in_this_only) p = in_this_only; + } + for (;;) { + if (p) { + int foundany = 0; + for (Node *t = p->next; t && t->level>p->level; t = t->next) { + if (t->new_selected) {widget_browser->select(t,0,0); foundany = 1;} + } + if (foundany) break; + p = p->parent; + } else { + for (Node *t = Fluid.proj.tree.first; t; t = t->next) + widget_browser->select(t,0,0); + break; + } + } + selection_changed(p); +} + +/** + Callback to move all selected items before their previous unselected sibling. + */ +void earlier_cb(Fl_Widget*,void*) { + Node *f; + int mod = 0; + for (f = Fluid.proj.tree.first; f; ) { + Node* nxt = f->next; + if (f->selected) { + Node* g; + for (g = f->prev; g && g->level > f->level; g = g->prev) {/*empty*/} + if (g && g->level == f->level && !g->selected) { + if (!mod) Fluid.proj.undo.checkpoint(); + f->move_before(g); + if (f->parent) f->parent->layout_widget(); + mod = 1; + } + } + f = nxt; + } + if (mod) Fluid.proj.set_modflag(1); + widget_browser->display(Fluid.proj.tree.current); + widget_browser->rebuild(); +} + +/** + Callback to move all selected items after their next unselected sibling. + */ +void later_cb(Fl_Widget*,void*) { + Node *f; + int mod = 0; + for (f = Fluid.proj.tree.last; f; ) { + Node* prv = f->prev; + if (f->selected) { + Node* g; + for (g = f->next; g && g->level > f->level; g = g->next) {/*empty*/} + if (g && g->level == f->level && !g->selected) { + if (!mod) Fluid.proj.undo.checkpoint(); + g->move_before(f); + if (f->parent) f->parent->layout_widget(); + mod = 1; + } + } + f = prv; + } + if (mod) Fluid.proj.set_modflag(1); + widget_browser->display(Fluid.proj.tree.current); + widget_browser->rebuild(); +} + +/** \brief Delete all children of a Type. + */ +static void delete_children(Node *p) { + Node *f; + // find all types following p that are higher in level, effectively finding + // the last child of the last child + for (f = p; f && f->next && f->next->level > p->level; f = f->next) {/*empty*/} + // now loop back up to p, deleting all children on the way + for (; f != p; ) { + Node *g = f->prev; + delete f; + f = g; + } +} + +/** Delete all nodes in the Types tree and reset project settings, or delete selected nodes. + Also calls the browser to refresh. + \note Please refactor this into two separate methods of Project. + \param[in] selected_only if set, delete only the selected widgets and + don't reset the project. + */ +void delete_all(int selected_only) { + if (widget_browser) { + if (selected_only) + widget_browser->save_scroll_position(); + widget_browser->new_list(); + } + for (Node *f = Fluid.proj.tree.first; f;) { + if (f->selected || !selected_only) { + delete_children(f); + Node *g = f->next; + delete f; + f = g; + } else { + f = f->next; + } + } + if(!selected_only) { + // reset the setting for the external shell command + if (g_shell_config) { + g_shell_config->clear(fld::Tool_Store::PROJECT); + g_shell_config->rebuild_shell_menu(); + g_shell_config->update_settings_dialog(); + } + if (widget_browser) { + widget_browser->hposition(0); + widget_browser->vposition(0); + } + Fluid.layout_list.remove_all(fld::Tool_Store::PROJECT); + Fluid.layout_list.current_suite(0); + Fluid.layout_list.current_preset(0); + Fluid.layout_list.update_dialogs(); + } + selection_changed(nullptr); + if (widget_browser) { + if (selected_only) + widget_browser->restore_scroll_position(); + widget_browser->rebuild(); + } +} + +/** Update a string. + Replace a string pointer with new value, strips leading/trailing blanks. + As a side effect, this call also sets the mod flags. + \param[in] n new string, can be nullptr + \param[out] p update this pointer, possibly reallocate memory + \param[in] nostrip if set, do not strip leading and trailing spaces and tabs + \return 1 if the string in p changed + */ +int storestring(const char *n, const char * & p, int nostrip) { + if (n == p) return 0; + Fluid.proj.undo.checkpoint(); + int length = 0; + if (n) { // see if blank, strip leading & trailing blanks + if (!nostrip) while (isspace((int)(unsigned char)*n)) n++; + const char *e = n + strlen(n); + if (!nostrip) while (e > n && isspace((int)(unsigned char)*(e-1))) e--; + length = int(e-n); + if (!length) n = nullptr; + } + if (n == p) return 0; + if (n && p && !strncmp(n,p,length) && !p[length]) return 0; + if (p) free((void *)p); + if (!n || !*n) { + p = nullptr; + } else { + char *q = (char *)malloc(length+1); + strlcpy(q,n,length+1); + p = q; + } + Fluid.proj.set_modflag(1); + return 1; +} + +/** Update the `visible` flag for `p` and all its descendants. + \param[in] p start here and update all descendants + */ +void update_visibility_flag(Node *p) { + Node *t = p; + for (;;) { + if (t->parent) t->visible = t->parent->visible && !t->parent->folded_; + else t->visible = 1; + t = t->next; + if (!t || t->level <= p->level) break; + } +} + +// ---- implementation of Node + +/** \var Node *Node::parent + Link to the parent node in the tree structure. + Used for simulating a tree structure via a doubly linked list. + */ +/** \var Node *Node::level + Zero based depth of the node within the tree structure. + Level is used to emulate a tree structure: the first node with a lower + level in the prev list would be the parent of this node. If the next member + has a higher level value, it is this nodes first child. At the same level, + it would be the first sibling. + */ +/** \var Node *Node::next + Points to the next node in the doubly linked list. + If this is nullptr, we are at the end of the list. + Used for simulating a tree structure via a doubly linked list. + */ +/** \var Node *Node::prev + Link to the next node in the tree structure. + If this is nullptr, we are at the beginning of the list. + Used for simulating a tree structure via a doubly linked list. + */ + +/** + Constructor and base for any node in the widget tree. + */ +Node::Node() : + name_(nullptr), + label_(nullptr), + callback_(nullptr), + user_data_(nullptr), + user_data_type_(nullptr), + comment_(nullptr), + uid_(0), + parent(nullptr), + new_selected(0), + selected(0), + folded_(0), + visible(0), + level(0), + next(nullptr), prev(nullptr), + factory(nullptr), + code_static_start(-1), code_static_end(-1), + code1_start(-1), code1_end(-1), + code2_start(-1), code2_end(-1), + header1_start(-1), header1_end(-1), + header2_start(-1), header2_end(-1), + header_static_start(-1), header_static_end(-1), + proj1_start(-1), proj1_end(-1), + proj2_start(-1), proj2_end(-1) +{ +} + + +/** + Destructor for any node in the tree. + + The destructor removes itself from the doubly linked list. This is dangerous, + because the node does not know if it is part of the widget tree, or if it is + in a separate tree. We try to take care of that as well as possible. + */ +Node::~Node() { + // warning: destructor only works for widgets that have been add()ed. + if (prev) prev->next = next; // else first = next; // don't do that! The Type may not be part of the main list + if (next) next->prev = prev; // else last = prev; + if (Fluid.proj.tree.last == this) Fluid.proj.tree.last = prev; + if (Fluid.proj.tree.first == this) Fluid.proj.tree.first = next; + if (Fluid.proj.tree.current == this) Fluid.proj.tree.current = nullptr; + if (parent) parent->remove_child(this); + if (name_) free((void*)name_); + if (label_) free((void*)label_); + if (callback_) free((void*)callback_); + if (user_data_) free((void*)user_data_); + if (user_data_type_) free((void*)user_data_type_); + if (comment_) free((void*)comment_); +} + +// Return the previous sibling in the tree structure or nullptr. +Node *Node::prev_sibling() { + Node *n; + for (n = prev; n && n->level > level; n = n->prev) ; + if (n && (n->level == level)) + return n; + return nullptr; +} + +// Return the next sibling in the tree structure or nullptr. +Node *Node::next_sibling() { + Node *n; + for (n = next; n && n->level > level; n = n->next) ; + if (n && (n->level == level)) + return n; + return nullptr; +} + +// Return the first child or nullptr +Node *Node::first_child() { + Node *n = next; + if (n->level > level) + return n; + return nullptr; +} + +// Generate a descriptive text for this item, to put in browser & window titles +const char* Node::title() { + const char* c = name(); + if (c) + return c; + return type_name(); +} + +/** + Return the window that contains this widget. + \return nullptr if this is not a widget. + */ +Window_Node *Node::window() { + if (!is_widget()) + return nullptr; + for (Node *t = this; t; t=t->parent) + if (t->is_a(Type::Window)) + return (Window_Node*)t; + return nullptr; +} + +/** + Return the group that contains this widget. + \return nullptr if this is not a widget. + */ +Group_Node *Node::group() { + if (!is_widget()) + return nullptr; + for (Node *t = this; t; t=t->parent) + if (t->is_a(Type::Group)) + return (Group_Node*)t; + return nullptr; +} + +/** + Add this list/tree of widgets as a new last child of p. + + \c this must not be part of the widget browser. \c p however must be in the + widget_browser, so \c Fluid.proj.tree.first and \c Fluid.proj.tree.last are valid for \c p. + + This methods updates the widget_browser. + + \param[in] p insert \c this tree as a child of \c p + \param[in] strategy is Strategy::AS_LAST_CHILD or Strategy::AFTER_CURRENT + */ +void Node::add(Node *anchor, Strategy strategy) { +#if 0 +#ifndef NDEBUG + // print_project_tree(); + // fprintf(stderr, "Validating project\n"); + validate_project_tree(); + // fprintf(stderr, "Validating branch\n"); + validate_independent_branch(this); +#endif +#endif + + Node *target = nullptr; // insert self before target node, if nullptr, insert last + Node *target_parent = nullptr; // this will be the new parent for branch + int target_level = 0; // adjust self to this new level + + // Find the node after our insertion position + switch (strategy.placement()) { + case Strategy::AS_FIRST_CHILD: + default: + if (anchor == nullptr) { + target = Fluid.proj.tree.first; + } else { + target = anchor->next; + target_level = anchor->level + 1; + target_parent = anchor; + } + break; + case Strategy::AS_LAST_CHILD: + if (anchor == nullptr) { + /* empty */ + } else { + for (target = anchor->next; target && target->level > anchor->level; target = target->next) {/*empty*/} + target_level = anchor->level + 1; + target_parent = anchor; + } + break; + case Strategy::AFTER_CURRENT: + if (anchor == nullptr) { + target = Fluid.proj.tree.first; + } else { + for (target = anchor->next; target && target->level > anchor->level; target = target->next) {/*empty*/} + target_level = anchor->level; + target_parent = anchor->parent; + } + break; + } + + + // Find the last node of our tree + Node *end = this; + while (end->next) end = end->next; + + // Everything is prepared, now insert ourself in front of the target node + Fluid.proj.undo.checkpoint(); + + // Walk the tree to update parent pointers and levels + int source_level = level; + for (Node *t = this; t; t = t->next) { + t->level += (target_level-source_level); + if (t->level == target_level) + t->parent = target_parent; + } + + // Now link ourselves and our children before 'target', or last, if 'target' is nullptr + if (target) { + prev = target->prev; + target->prev = end; + end->next = target; + } else { + prev = Fluid.proj.tree.last; + end->next = nullptr; + Fluid.proj.tree.last = end; + } + if (prev) { + prev->next = this; + } else { + Fluid.proj.tree.first = this; + } + +#if 0 + { // make sure that we have no duplicate uid's + Node *tp = this; + do { + tp->set_uid(tp->uid_); + tp = tp->next; + } while (tp!=end && tp!=nullptr); + } +#endif + + // Give the widgets in our tree a chance to update themselves + for (Node *t = this; t && t!=end->next; t = t->next) { + if (target_parent && (t->level == target_level)) + target_parent->add_child(t, nullptr); + update_visibility_flag(t); + } + + Fluid.proj.set_modflag(1); + widget_browser->redraw(); + +#if 0 +#ifndef NDEBUG + // fprintf(stderr, "Validating project after adding branch\n"); + validate_project_tree(); +#endif +#endif +} + +/** + Add `this` list/tree of widgets as a new sibling before `g`. + + `This` is not part of the widget browser. `g` must be in the + widget_browser, so `Fluid.proj.tree.first` and `Fluid.proj.tree.last` are valid for `g . + + This methods updates the widget_browser. + + \param[in] g pointer to a node within the tree + */ +void Node::insert(Node *g) { + // 'this' is not in the Node_Browser, so we must run the linked list to find the last entry + Node *end = this; + while (end->next) end = end->next; + // 'this' will get the same parent as 'g' + parent = g->parent; + // run the list again to set the future node levels + int newlevel = g->level; + visible = g->visible; + for (Node *t = this->next; t; t = t->next) t->level += newlevel-level; + level = newlevel; + // insert this in the list before g + prev = g->prev; + if (prev) prev->next = this; else Fluid.proj.tree.first = this; + end->next = g; + g->prev = end; + update_visibility_flag(this); + { // make sure that we have no duplicate uid's + Node *tp = this; + do { + tp->set_uid(tp->uid_); + tp = tp->next; + } while (tp!=end && tp!=nullptr); + } + // tell parent that it has a new child, so it can update itself + if (parent) parent->add_child(this, g); + widget_browser->redraw(); +} + +// Return message number for I18N... +int Node::msgnum() { + int count; + Node *p; + + for (count = 0, p = this; p;) { + if (p->label()) count ++; + if (p != this && p->is_widget() && ((Widget_Node *)p)->tooltip()) count ++; + + if (p->prev) p = p->prev; + else p = p->parent; + } + + return count; +} + +/** + Remove this node and all its children from the parent node. + + This does not delete anything. The resulting list//tree will no longer be in + the widget_browser, so \c Fluid.proj.tree.first and \c Fluid.proj.tree.last do not apply + to it. + + \return the node that follows this node after the operation; can be nullptr + */ +Node *Node::remove() { + // find the last child of this node + Node *end = this; + for (;;) { + if (!end->next || end->next->level <= level) + break; + end = end->next; + } + // unlink this node from the previous one + if (prev) + prev->next = end->next; + else + Fluid.proj.tree.first = end->next; + // unlink the last child from their next node + if (end->next) + end->next->prev = prev; + else + Fluid.proj.tree.last = prev; + Node *r = end->next; + prev = end->next = nullptr; + // allow the parent to update changes in the UI + if (parent) parent->remove_child(this); + parent = nullptr; + // tell the widget_browser that we removed some nodes + widget_browser->redraw(); + selection_changed(nullptr); + return r; +} + +void Node::name(const char *n) { + int nostrip = is_a(Type::Comment); + if (storestring(n,name_,nostrip)) { + if (visible) widget_browser->redraw(); + } +} + +void Node::label(const char *n) { + if (storestring(n,label_,1)) { + setlabel(label_); + if (visible && !name_) widget_browser->redraw(); + } +} + +void Node::callback(const char *n) { + storestring(n,callback_); +} + +void Node::user_data(const char *n) { + storestring(n,user_data_); +} + +void Node::user_data_type(const char *n) { + storestring(n,user_data_type_); +} + +void Node::comment(const char *n) { + if (storestring(n,comment_,1)) { + if (visible) widget_browser->redraw(); + } +} + +void Node::open() { + printf("Open of '%s' is not yet implemented\n",type_name()); +} + +// returns pointer to whatever is after f & children + +/** + Move this node (and its children) into list before g. + Both `this` and `g` must be in the widget browser. + The caller must make sure that the widget browser is rebuilt correctly. + \param[in] g move \c this tree before \c g + */ +void Node::move_before(Node* g) { + if (level != g->level) printf("move_before levels don't match! %d %d\n", + level, g->level); + // Find the last child in the list + Node *n; + for (n = next; n && n->level > level; n = n->next) ; + if (n == g) return; + // now link this tree before g + Node *l = n ? n->prev : Fluid.proj.tree.last; + prev->next = n; + if (n) n->prev = prev; else Fluid.proj.tree.last = prev; + prev = g->prev; + l->next = g; + if (prev) prev->next = this; else Fluid.proj.tree.first = this; + g->prev = l; + // tell parent that it has a new child, so it can update itself + if (parent && is_widget()) parent->move_child(this,g); +} + + +// write a widget and all its children: +void Node::write(fld::io::Project_Writer &f) { + if (f.write_codeview()) proj1_start = (int)ftell(f.file()) + 1; + if (f.write_codeview()) proj2_start = (int)ftell(f.file()) + 1; + f.write_indent(level); + f.write_word(type_name()); + + if (is_class()) { + const char * p = ((Class_Node*)this)->prefix(); + if (p && strlen(p)) + f.write_word(p); + } + + f.write_word(name()); + f.write_open(); + write_properties(f); + if (parent) parent->write_parent_properties(f, this, true); + f.write_close(level); + if (f.write_codeview()) proj1_end = (int)ftell(f.file()); + if (!can_have_children()) { + if (f.write_codeview()) proj2_end = (int)ftell(f.file()); + return; + } + // now do children: + f.write_open(); + Node *child; + for (child = next; child && child->level > level; child = child->next) + if (child->level == level+1) child->write(f); + if (f.write_codeview()) proj2_start = (int)ftell(f.file()) + 1; + f.write_close(level); + if (f.write_codeview()) proj2_end = (int)ftell(f.file()); +} + +void Node::write_properties(fld::io::Project_Writer &f) { + // repeat this for each attribute: + if (Fluid.proj.write_mergeback_data && uid_) { + f.write_word("uid"); + f.write_string("%04x", uid_); + } + if (label()) { + f.write_indent(level+1); + f.write_word("label"); + f.write_word(label()); + } + if (user_data()) { + f.write_indent(level+1); + f.write_word("user_data"); + f.write_word(user_data()); + } + if (user_data_type()) { + f.write_word("user_data_type"); + f.write_word(user_data_type()); + } + if (callback()) { + f.write_indent(level+1); + f.write_word("callback"); + f.write_word(callback()); + } + if (comment()) { + f.write_indent(level+1); + f.write_word("comment"); + f.write_word(comment()); + } + if (can_have_children() && !folded_) f.write_word("open"); + if (selected) f.write_word("selected"); +} + +void Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"uid")) { + const char *hex = f.read_word(); + int x = 0; + if (hex) + x = sscanf(hex, "%04x", &x); + set_uid(x); + } else if (!strcmp(c,"label")) + label(f.read_word()); + else if (!strcmp(c,"user_data")) + user_data(f.read_word()); + else if (!strcmp(c,"user_data_type")) + user_data_type(f.read_word()); + else if (!strcmp(c,"callback")) + callback(f.read_word()); + else if (!strcmp(c,"comment")) + comment(f.read_word()); + else if (!strcmp(c,"open")) + folded_ = 0; + else if (!strcmp(c,"selected")) + select(this,1); + else if (!strcmp(c,"parent_properties")) + if (parent) { + const char *cc = f.read_word(1); + if (strcmp(cc, "{")==0) { + for (;;) { + cc = f.read_word(); + if (!cc || cc[0]==0 || strcmp(cc, "}")==0) break; + parent->read_parent_property(f, this, cc); + } + } else { + f.read_error("'parent_properties' must be followed by '{'"); + } + } else { + f.read_error("Types using 'parent_properties' must have a parent"); + f.read_word(); // skip the entire block (this should generate a warning) + } + else + f.read_error("Unknown property \"%s\"", c); +} + +/** Write parent properties into the child property list. + + Some widgets store information for every child they manage. For example, + Fl_Grid stores the row and column position of every child. This method stores + this information with the child, but it is read and written by the parent. + + Parent properties solve several issues. A child will keep parent properties + if copied from on grid into another. The parent does not have to keep lists + of properties that may diverge from the actual order or number of children. + And lastly, properties are read when they are actually needed and don't have + to be stored in some temporary array. + + Parent properties are written as their own block at the end of the child's + property list. The block starts with the `parent_properties` keyword, followed + by a list of property/value pairs. The order of properties is significant, + however individual properties can be left out. + + To avoid writing the `parent_properties` block unnecessarily, this method + should only generate it if `encapsulate` is set *and* the contained + properties are not at their default. + + Lastly, this method should call the super class to give it a chance to append + its own properties. + + \see Grid_Node::write_parent_properties(fld::io::Project_Writer &f, Node *child, bool encapsulate) + + \param[in] f the project file writer + \param[in] child write properties for this child, make sure it has the correct type + \param[in] encapsulate write the `parent_properties {}` block if true before writing any properties + */ +void Node::write_parent_properties(fld::io::Project_Writer &f, Node *child, bool encapsulate) { + (void)f; (void)child; (void)encapsulate; + // nothing to do here + // put the following code into your implementation of write_parent_properties + // if there are actual non-default properties to write + // if (encapsulate) { + // f.write_indent(level+2); + // f.write_string("parent_properties {"); + // } + // now write your properties as name/value pairs + // f.write_indent(level+3); + // f.write_string("location {%d %d}", cell->row(), cell->col()); + // give the super class a chance to write its properties as well + // super::write_parent_properties(f, child, false); + // close the encapsulation + // if (encapsulate) { + // f.write_indent(level+2); + // f.write_string("}"); + // } +} + +/** Read one parent per-child property. + + A parent widget can store properties for every child that it manages. This + method reads back those properties. This function is virtual, so if a Type + does not support a property, it will propagate to its super class. + + \see Node::write_parent_properties(fld::io::Project_Writer &f, Node *child, bool encapsulate) + \see Grid_Node::read_parent_property(fld::io::Project_Reader &f, Node *child, const char *property) + + \param[in] f the project file writer + \param[in] child read properties for this child + \param[in] property the name of a property, or "}" when we reach the end of the list + */ +void Node::read_parent_property(fld::io::Project_Reader &f, Node *child, const char *property) { + (void)child; + f.read_error("Unknown parent property \"%s\"", property); +} + + +int Node::read_fdesign(const char*, const char*) {return 0;} + +/** + Write a comment into the header file. + \param[in] pre indent the comment by this string +*/ +void Node::write_comment_h(fld::io::Code_Writer& f, const char *pre) +{ + if (comment() && *comment()) { + f.write_h("%s/**\n", pre); + const char *s = comment(); + f.write_h("%s ", pre); + while(*s) { + if (*s=='\n') { + if (s[1]) { + f.write_h("\n%s ", pre); + } + } else { + f.write_h("%c", *s); // FIXME this is much too slow! + } + s++; + } + f.write_h("\n%s*/\n", pre); + } +} + +/** + Write a comment into the source file. +*/ +void Node::write_comment_c(fld::io::Code_Writer& f, const char *pre) +{ + if (comment() && *comment()) { + f.write_c("%s/**\n", pre); + const char *s = comment(); + if (*s && *s!='\n') + f.write_c("%s ", pre); + while(*s) { + if (*s=='\n') { + f.write_c("\n"); + if (s[1] && s[1]!='\n') { + f.write_c("%s ", pre); + } + } else { + f.write_c("%c", *s); // FIXME this is much too slow! + } + s++; + } + f.write_c("\n%s*/\n", pre); + } +} + +/** + Write a comment into the source file. +*/ +void Node::write_comment_inline_c(fld::io::Code_Writer& f, const char *pre) +{ + if (comment() && *comment()) { + const char *s = comment(); + if (strchr(s, '\n')==nullptr) { + // single line comment + if (pre) f.write_c("%s", pre); + f.write_c("// %s\n", s); + if (!pre) f.write_c("%s", f.indent_plus(1)); + } else { + f.write_c("%s/*\n", pre?pre:""); + if (*s && *s!='\n') { + if (pre) + f.write_c("%s ", pre); + else + f.write_c("%s ", f.indent_plus(1)); + } + while(*s) { + if (*s=='\n') { + f.write_c("\n"); + if (s[1] && s[1]!='\n') { + if (pre) + f.write_c("%s ", pre); + else + f.write_c("%s ", f.indent_plus(1)); + } + } else { + f.write_c("%c", *s); // FIXME this is much too slow! + } + s++; + } + if (pre) + f.write_c("\n%s */\n", pre); + else + f.write_c("\n%s */\n", f.indent_plus(1)); + if (!pre) + f.write_c("%s", f.indent_plus(1)); + } + } +} + +/** + Build widgets and dataset needed in live mode. + \return a widget pointer that the live mode initiator can 'show()' + \see leave_live_mode() +*/ +Fl_Widget *Node::enter_live_mode(int) { + return nullptr; +} + +/** + Release all resources created when entering live mode. + \see enter_live_mode() +*/ +void Node::leave_live_mode() { +} + +/** + Copy all needed properties for this type into the live object. +*/ +void Node::copy_properties() { +} + +/** + Check whether callback \p cbname is declared anywhere else by the user. + + \b Warning: this just checks that the name is declared somewhere, + but it should probably also check that the name corresponds to a + plain function or a member function within the same class and that + the parameter types match. + */ +int Node::user_defined(const char* cbname) const { + for (Node* p = Fluid.proj.tree.first; p ; p = p->next) + if (p->is_a(Type::Function) && p->name() != nullptr) + if (strncmp(p->name(), cbname, strlen(cbname)) == 0) + if (p->name()[strlen(cbname)] == '(') + return 1; + return 0; +} + +const char *Node::callback_name(fld::io::Code_Writer& f) { + if (is_name(callback())) return callback(); + return f.unique_id(this, "cb", name(), label()); +} + +/** + \brief Return the class name if this type is inside a Class or Widget Class. + + This methods traverses up the hirarchy to find out if this Type is located + inside a Class or Widget Class. It then return the name of that class. If + need_nest is set, class_name searches all the way up the tree and concatenates + the names of classes within classes, separated by a "::". + + \param need_nest if clear, search up one level to the first enclosing class. + If set, recurse all the way up to the top node. + \return the name of the enclosing class, or names of the enclosing classes + in a static buffe (don't call free), or nullptr if this Type is not inside a class + */ +const char* Node::class_name(const int need_nest) const { + Node* p = parent; + while (p) { + if (p->is_class()) { + // see if we are nested in another class, we must fully-qualify name: + // this is lame but works... + const char* q = nullptr; + if(need_nest) q=p->class_name(need_nest); + if (q) { + static char s[256]; + if (q != s) strlcpy(s, q, sizeof(s)); + strlcat(s, "::", sizeof(s)); + strlcat(s, p->name(), sizeof(s)); + return s; + } + return p->name(); + } + p = p->parent; + } + return nullptr; +} + +/** + Check if this is inside a Class_Node or Widget_Class_Node. + \return true if any of the parents is Class_Node or Widget_Class_Node + */ +bool Node::is_in_class() const { + Node* p = parent; + while (p) { + if (p->is_class()) return true; + p = p->parent; + } + return false; +} + +void Node::write_static(fld::io::Code_Writer&) { +} + +void Node::write_static_after(fld::io::Code_Writer&) { +} + +void Node::write_code1(fld::io::Code_Writer& f) { + f.write_h("// Header for %s\n", title()); + f.write_c("// Code for %s\n", title()); +} + +void Node::write_code2(fld::io::Code_Writer&) { +} + +/** Set a uid that is unique within the project. + + Try to set the given id as the unique id for this node. If the suggested id + is 0, or it is already taken inside this project, we try another random id + until we find one that is unique. + + \param[in] suggested_uid the preferred uid for this node + \return the actualt uid that was given to the node + */ +unsigned short Node::set_uid(unsigned short suggested_uid) { + if (suggested_uid==0) + suggested_uid = (unsigned short)rand(); + for (;;) { + Node *tp = Fluid.proj.tree.first; + for ( ; tp; tp = tp->next) + if (tp!=this && tp->uid_==suggested_uid) + break; + if (tp==nullptr) + break; + suggested_uid = (unsigned short)rand(); + } + uid_ = suggested_uid; + return suggested_uid; +} + + +/// \} + diff --git a/fluid/nodes/Node.h b/fluid/nodes/Node.h new file mode 100644 index 000000000..5c473a936 --- /dev/null +++ b/fluid/nodes/Node.h @@ -0,0 +1,313 @@ +// +// Node base class header file 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_NODES_NODE_H +#define FLUID_NODES_NODE_H + +#include "io/Code_Writer.h" + +#include +#include + +class Node; +class Group_Node; +class Window_Node; + +namespace fld { +namespace io { + +class Project_Reader; +class Project_Writer; + +} // namespace io +} // namespace fld + +/** + Declare where a new type is placed and how to create it. + + Placement can be as the first or last child of the anchor, or right after the + anchor. In most cases, the anchor is the last selected type node. + + If the source is FROM_USER, widgets may be created with default titles and + labels. Type created FROM_FILE will start with no label, so the label is set + correctly later. + + \see Node *Fl_..._Type::make(Strategy strategy) calls `add()` + Add single Type: + Node *add_new_widget_from_user(Node *inPrototype, Strategy strategy, bool and_open) + Node *add_new_widget_from_user(const char *inName, Strategy strategy, bool and_open) + Node *add_new_widget_from_file(const char *inName, Strategy strategy) + Add a hierarchy of Types + void Node::add(Node *p, Strategy strategy) + int read_file(const char *filename, int merge, Strategy strategy) + Node *fld::io::Project_Reader::read_children(Node *p, int merge, Strategy strategy, char skip_options) + int fld::io::Project_Reader::read_project(const char *filename, int merge, Strategy strategy) + */ +typedef struct Strategy { + enum Flags { + AS_FIRST_CHILD = 0x0000, + AS_LAST_CHILD = 0x0001, + AFTER_CURRENT = 0x0002, + PLACEMENT_MASK = 0x000f, + FROM_USER = 0x0000, + FROM_FILE = 0x0010, + SOURCE_MASK = 0x00f0, + FROM_FILE_AS_FIRST_CHILD = 0x0010, + FROM_FILE_AS_LAST_CHILD = 0x0011, + FROM_FILE_AFTER_CURRENT = 0x0012, + }; + Flags flags; + Strategy(Flags f) { flags = f; } + void placement(Flags f) { flags = (Flags)((flags & ~PLACEMENT_MASK) | (f & PLACEMENT_MASK)); } + Flags placement() { return (Flags)(flags & PLACEMENT_MASK); } + void source(Flags f) { flags = (Flags)((flags & ~SOURCE_MASK) | (f & SOURCE_MASK)); } + Flags source() { return (Flags)(flags & SOURCE_MASK); } +} Strategy; + +enum class Type { + // administrative + Base_, Widget_, Menu_Manager_, Menu_, Browser_, Valuator_, + // non-widget + Function, Code, CodeBlock, + Decl, DeclBlock, Class, + Widget_Class, Comment, Data, + // groups + Window, Group, Pack, + Flex, Tabs, Scroll, + Tile, Wizard, Grid, + // buttons + Button, Return_Button, Light_Button, + Check_Button, Repeat_Button, Round_Button, + // valuators + Slider, Scrollbar, Value_Slider, + Adjuster, Counter, Spinner, + Dial, Roller, Value_Input, Value_Output, + // text + Input, Output, Text_Editor, + Text_Display, File_Input, Terminal, + // menus + Menu_Bar, Menu_Button, Choice, + Input_Choice, Submenu, Menu_Item, + Checkbox_Menu_Item, Radio_Menu_Item, + // browsers + Browser, Check_Browser, File_Browser, + Tree, Help_View, Table, + // misc + Box, Clock, Progress, + Max_ +}; + +void update_visibility_flag(Node *p); +void delete_all(int selected_only=0); +int storestring(const char *n, const char * & p, int nostrip=0); + +void select_all_cb(Fl_Widget *,void *); +void select_none_cb(Fl_Widget *,void *); +void earlier_cb(Fl_Widget*,void*); +void later_cb(Fl_Widget*,void*); + +#ifndef NDEBUG +void print_project_tree(); +bool validate_project_tree(); +bool validate_independent_branch(class Node *root); +bool validate_branch(class Node *root); +#endif + +/** + \brief This is the base class for all elements in the project tree. + + All widgets and other types in the project are derived from Fl_Types. They + are organized in a doubly linked list. Every Type also has depth information + to create a pseudo tree structure. To make walking up the tree faster, Type + also holds a pointer to the `parent` Type. + + Types can be identified using the builtin Type system that works like RTTI. The + method `type()` returns the exact type, and the method `is_a(Type)` returns true + if this is the exact type or derived from the type, and a dynamic cast will + work reliably. + + \todo it would be nice if we can handle multiple independent trees. To do that + we must remove static members like `first` and `last`. + + \todo add virtual methods to handle events, draw widgets, and draw overlays. + It may also make sense to have a virtual method that returns a boolean if + a specific type can be added as a child. + + \todo it may make sense to have a readable iterator class instead of relying + on pointer manipulation. Or use std in future releases. + */ +class Node { + /** Copy the label text to Widgets and Windows, does nothing in Type. */ + virtual void setlabel(const char *) { } // virtual part of label(char*) + +protected: + + Node(); + + /** Name of a widget, or code some non-widget Types. */ + const char *name_; + /** Label text of a widget. */ + const char *label_; + /** If it is just a word, it's the name of the callback function. Otherwise + it is the full callback C++ code. Can be nullptr. */ + const char *callback_; + /** Widget user data field as C++ text. */ + const char *user_data_; + /** Widget user data type as C++ text, usually `void*` or `long`. */ + const char *user_data_type_; + /** Optional comment for every node in the graph. Visible in browser and + panels, and will also be copied to the source code. */ + const char *comment_; + /** a unique ID within the project */ + unsigned short uid_; + +public: // things that should not be public: + // TODO: reference back to the tree + /** Quick link to the parent Type instead of walking up the linked list. */ + Node *parent; + /** This type is rendered "selected" in the tree browser. */ + char new_selected; // browser highlight + /** Backup storage for selection if an error occurred during some operation + (see `haderror`). It seems that this is often confused with new_selected + which seems to hold the true and visible selection state. */ + char selected; // copied here by selection_changed() + char folded_; // if set, children are not shown in browser + char visible; // true if all parents are open + int level; // number of parents over this + Node *next, *prev; + Node *prev_sibling(); + Node *next_sibling(); + Node *first_child(); + + Node *factory; + const char *callback_name(fld::io::Code_Writer& f); + + // text positions of this type in code, header, and project file (see codeview) + int code_static_start, code_static_end; + int code1_start, code1_end; + int code2_start, code2_end; + int header1_start, header1_end; + int header2_start, header2_end; + int header_static_start, header_static_end; + int proj1_start, proj1_end; + int proj2_start, proj2_end; + +protected: + int user_defined(const char* cbname) const; + +public: + + virtual ~Node(); + virtual Node *make(Strategy strategy) = 0; + + Window_Node *window(); + Group_Node *group(); + + void add(Node *parent, Strategy strategy); + void insert(Node *n); // insert into list before n + Node* remove(); // remove from list + void move_before(Node*); // move before a sibling + + virtual const char *title(); // string for browser + virtual const char *type_name() = 0; // type for code output + virtual const char *alt_type_name() { return type_name(); } // alternate type for FLTK2 code output + + const char *name() const {return name_;} + void name(const char *); + const char *label() const {return label_;} + void label(const char *); + const char *callback() const {return callback_;} + void callback(const char *); + const char *user_data() const {return user_data_;} + void user_data(const char *); + const char *user_data_type() const {return user_data_type_;} + void user_data_type(const char *); + const char *comment() { return comment_; } + void comment(const char *); + + virtual Node* click_test(int,int) { return nullptr; } + + virtual void add_child(Node*, Node* beforethis) { } + virtual void move_child(Node*, Node* beforethis) { } + virtual void remove_child(Node*) { } + + /** Give widgets a chance to arrange their children after all children were added. + If adding individual children, this is called immediately, but if children + are read via a project file, we wait until all children are read and then + lay out the group. + */ + virtual void layout_widget() { } + + virtual void open(); // what happens when you double-click + + // read and write data to a saved file: + virtual void write(fld::io::Project_Writer &f); + virtual void write_properties(fld::io::Project_Writer &f); + virtual void read_property(fld::io::Project_Reader &f, const char *); + virtual void write_parent_properties(fld::io::Project_Writer &f, Node *child, bool encapsulate); + virtual void read_parent_property(fld::io::Project_Reader &f, Node *child, const char *property); + virtual int read_fdesign(const char*, const char*); + virtual void postprocess_read() { } + + // write code, these are called in order: + virtual void write_static(fld::io::Code_Writer& f); // write static stuff to .c file + virtual void write_static_after(fld::io::Code_Writer& f); // write static stuff after children + virtual void write_code1(fld::io::Code_Writer& f); // code and .h before children + virtual void write_code2(fld::io::Code_Writer& f); // code and .h after children + void write_comment_h(fld::io::Code_Writer& f, const char *ind=""); // write the commentary text into the header file + void write_comment_c(fld::io::Code_Writer& f, const char *ind=""); // write the commentary text into the source file + void write_comment_inline_c(fld::io::Code_Writer& f, const char *ind=nullptr); // write the commentary text + + // live mode + virtual Fl_Widget *enter_live_mode(int top=0); // build widgets needed for live mode + virtual void leave_live_mode(); // free allocated resources + virtual void copy_properties(); // copy properties from this type into a potential live object + virtual void copy_properties_for_children() { } // copy remaining properties after children were added + + // get message number for I18N + int msgnum(); + + /** Return 1 if the Type can have children. */ + virtual int can_have_children() const {return 0;} + /** Return 1 if the type is a widget or menu item. */ + virtual int is_widget() const {return 0;} + /** Return 1 if the type is a widget but not a menu item. */ + virtual int is_true_widget() const {return 0;} + /** Return 1 if a type behaves like a button (Fl_Button and Fl_Menu_Item and derived, but not Submenu_Node. */ + virtual int is_button() const {return 0;} + /** Return 1 if this is a Widget_Class_Node, CodeBlock_Node, or Function_Node */ + virtual int is_code_block() const {return 0;} + /** Return 1 if this is a Widget_Class_Node, Class_Node, or DeclBlock_Node */ + virtual int is_decl_block() const {return 0;} + /** Return 1 if this is a Class_Node or Widget_Class_Node. */ + virtual int is_class() const {return 0;} + /** Return 1 if the type browser shall draw a padlock over the icon. */ + virtual int is_public() const {return 1;} + /** Return the type Type for this Type. */ + virtual Type type() const { return Type::Base_; } + /** Check if this Type is of the give type Type or derived from that type Type. */ + virtual bool is_a(Type inType) const { return (inType==Type::Base_); } + + const char* class_name(const int need_nest) const; + bool is_in_class() const; + + int has_function(const char*, const char*) const; + + unsigned short set_uid(unsigned short suggested_uid=0); + unsigned short get_uid() { return uid_; } +}; + +#endif // FLUID_NODES_NODE_H diff --git a/fluid/nodes/Tree.cxx b/fluid/nodes/Tree.cxx new file mode 100644 index 000000000..ca9b3d761 --- /dev/null +++ b/fluid/nodes/Tree.cxx @@ -0,0 +1,128 @@ +// +// Node Tree 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 "nodes/Tree.h" + +#include "Project.h" + +using namespace fld; +using namespace fld::node; + + +Tree::Iterator::Iterator(Node *t, bool only_selected) +: type_(t) +, only_selected_(only_selected) +{ + if (t) { + if (only_selected_) { + if (!type_->selected) { + operator++(); + } + } + } +} + +Tree::Iterator &Tree::Iterator::operator++() { + if (only_selected_) { + do { + type_ = type_->next; + } while (type_ && !type_->selected); + } else { + type_ = type_->next; + } + return *this; +} + +Tree::WIterator::WIterator(Node *t, bool only_selected) +: type_(t) +, only_selected_(only_selected) +{ + if (t) { + if (only_selected_) { + if (!type_->selected || !type_->is_widget()) { + operator++(); + } + } else { + if (!type_->is_widget()) { + operator++(); + } + } + } +} + +Tree::WIterator& Tree::WIterator::operator++() { + if (only_selected_) { + do { + type_ = type_->next; + } while (type_ && (!type_->selected || !type_->is_widget())); + } else { + do { + type_ = type_->next; + } while (type_ && !type_->is_widget()); + } + return *this; +} + + +Tree::Tree(Project &proj) +: proj_(proj) +{ (void)proj_; } + + +/** Find a node by its unique id. + + Every node in a type tree has an id that is unique for the current project. + Walk the tree and return the node with this uid. + + \param[in] uid any number between 0 and 65535 + \return the node with this uid, or nullptr if not found + */ +Node *Tree::find_by_uid(unsigned short uid) { + for (auto tp: all_nodes()) { + if (tp->get_uid() == uid) return tp; + } + return nullptr; +} + + +/** Find a type node by using the codeview text positions. + + \param[in] text_type 0=source file, 1=header, 2=.fl project file + \param[in] crsr cursor position in text + \return the node we found or nullptr + */ +Node *Tree::find_in_text(int text_type, int crsr) { + for (auto node: all_nodes()) { + switch (text_type) { + case 0: + if (crsr >= node->code1_start && crsr < node->code1_end) return node; + if (crsr >= node->code2_start && crsr < node->code2_end) return node; + if (crsr >= node->code_static_start && crsr < node->code_static_end) return node; + break; + case 1: + if (crsr >= node->header1_start && crsr < node->header1_end) return node; + if (crsr >= node->header2_start && crsr < node->header2_end) return node; + if (crsr >= node->header_static_start && crsr < node->header_static_end) return node; + break; + case 2: + if (crsr >= node->proj1_start && crsr < node->proj1_end) return node; + if (crsr >= node->proj2_start && crsr < node->proj2_end) return node; + break; + } + } + return nullptr; +} diff --git a/fluid/nodes/Tree.h b/fluid/nodes/Tree.h new file mode 100644 index 000000000..f494af651 --- /dev/null +++ b/fluid/nodes/Tree.h @@ -0,0 +1,106 @@ +// +// Node Tree header file 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_NODES_TREE_H +#define FLUID_NODES_TREE_H + +#include "nodes/Widget_Node.h" + +class Node; + +namespace fld { + +class Project; + +namespace node { + +class Tree { + + // A class that can iterate over the entire scene graph. + class Iterator { + Node *type_ = nullptr; + bool only_selected_ = false; + public: + explicit Iterator(Node *t, bool only_selected); + Node* operator*() { return type_; } + Iterator& operator++(); + bool operator!=(const Iterator& other) const { return type_ != other.type_; } + }; + + // A container for a node iterator + class Container { + Tree &tree_; + bool only_selected_ = false; + public: + Container(Tree &tree, bool only_selected) : tree_(tree), only_selected_(only_selected) { } + Iterator begin() { return Iterator(tree_.first, only_selected_); } + Iterator end() { return Iterator(nullptr, only_selected_); } + }; + + // A class that iterate over the scene graph, but returns only nodes of type widget. + class WIterator { + Node *type_ = nullptr; + bool only_selected_ = false; + public: + explicit WIterator(Node *t, bool only_selected); + Widget_Node* operator*() { return static_cast(type_); } + WIterator& operator++(); + bool operator!=(const WIterator& other) const { return type_ != other.type_; } + }; + + // A container for a widget node iterator + class WContainer { + Tree &tree_; + bool only_selected_ = false; + public: + WContainer(Tree &tree, bool only_selected) : tree_(tree), only_selected_(only_selected) { } + WIterator begin() { return WIterator(tree_.first, only_selected_); } + WIterator end() { return WIterator(nullptr, only_selected_); } + }; + + /// Link Tree class to the project. + Project &proj_; + +public: + + Node *first = nullptr; + Node *last = nullptr; + Node *current = nullptr; // most recently picked object + Node *current_dnd = nullptr; + /// If this is greater zero, widgets will be allowed to lay out their children. + int allow_layout = 0; + +public: + + Tree(Project &proj); + + bool empty() { return first == nullptr; } + + // Iterators: `for (auto &n: tree.all_nodes()) { n.print(); } + Container all_nodes() { return Container(*this, false); } + WContainer all_widgets() { return WContainer(*this, false); } + Container all_selected_nodes() { return Container(*this, true); } + WContainer all_selected_widgets() { return WContainer(*this, true); } + + Node *find_by_uid(unsigned short uid); + Node *find_in_text(int text_type, int crsr); +}; + +} // namespace node +} // namespace fld + + +#endif // FLUID_NODES_TREE_H diff --git a/fluid/nodes/Widget_Node.cxx b/fluid/nodes/Widget_Node.cxx new file mode 100644 index 000000000..cba8607f2 --- /dev/null +++ b/fluid/nodes/Widget_Node.cxx @@ -0,0 +1,2438 @@ +// +// Widget Node 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 "nodes/Widget_Node.h" + +#include "Fluid.h" +#include "Project.h" +#include "app/Image_Asset.h" +#include "proj/mergeback.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "nodes/Menu_Node.h" +#include "nodes/Function_Node.h" +#include "nodes/Window_Node.h" +#include "panels/widget_panel.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../src/flstring.h" + +#include +#include +#undef min +#undef max +#include + +// Make an Widget_Node subclass instance. +// It figures out the automatic size and parent of the new widget, +// creates the Fl_Widget (by calling the virtual function _make), +// adds it to the Fl_Widget hierarchy, creates a new Node +// instance, sets the widget pointers, and makes all the display +// update correctly... + +int Widget_Node::is_widget() const {return 1;} +int Widget_Node::is_public() const {return public_;} + +const char* subclassname(Node* l) { + if (l->is_a(Type::Menu_Bar)) { + Menu_Bar_Node *mb = static_cast(l); + if (mb->is_sys_menu_bar()) + return mb->sys_menubar_name(); + } + if (l->is_widget()) { + Widget_Node* p = (Widget_Node*)l; + const char* c = p->subclass(); + if (c) return c; + if (l->is_class()) return "Fl_Group"; + if (p->o->type() == FL_DOUBLE_WINDOW) return "Fl_Double_Window"; + if (p->type() == Type::Input) { + if (p->o->type() == FL_FLOAT_INPUT) return "Fl_Float_Input"; + if (p->o->type() == FL_INT_INPUT) return "Fl_Int_Input"; + } + } + return l->type_name(); +} + +// Return the ideal widget size... +void +Widget_Node::ideal_size(int &w, int &h) { + w = 120; + h = 100; + fld::app::Snap_Action::better_size(w, h); +} + +/** + Make a new Widget node. + \param[in] strategy is Strategy::AS_LAST_CHILD or Strategy::AFTER_CURRENT + \return new node + */ +Node *Widget_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *pp = anchor; + if (pp && (strategy.placement() == Strategy::AFTER_CURRENT)) + pp = pp->parent; + while (pp && !pp->is_a(Type::Group)) { + anchor = pp; + strategy.placement(Strategy::AFTER_CURRENT); + pp = pp->parent; + } + if (!pp || !pp->is_true_widget() || !anchor->is_true_widget()) { + fl_message("Please select a group widget or window"); + return nullptr; + } + + Widget_Node* p = (Widget_Node*)pp; + Widget_Node* q = (Widget_Node*)anchor; + + // Figure out a border between widget and window: + int B = p->o->w()/2; if (p->o->h()/2 < B) B = p->o->h()/2; if (B>25) B = 25; + + int ULX,ULY; // parent's origin in window + if (!p->is_a(Type::Window)) { // if it is a group, add corner + ULX = p->o->x(); ULY = p->o->y(); + } else { + ULX = ULY = 0; + } + + // Figure out a position and size for the widget + int X,Y,W,H; + if (is_a(Type::Group)) { // fill the parent with the widget + X = ULX+B; + W = p->o->w()-B; + Y = ULY+B; + H = p->o->h()-B; + } else if (q != p) { // copy position and size of current widget + W = q->o->w(); + H = q->o->h(); + X = q->o->x()+W; + Y = q->o->y(); + if (X+W > ULX+p->o->w()) { + X = q->o->x(); + Y = q->o->y()+H; + if (Y+H > ULY+p->o->h()) Y = ULY+B; + } + } else { // just make it small and square... + X = ULX+B; + Y = ULY+B; + W = H = B; + } + + // Construct the Node: + Widget_Node *t = _make(); + if (!o) o = widget(0,0,100,100); // create template widget + t->factory = this; + // Construct the Fl_Widget: + t->o = widget(X,Y,W,H); + if (strategy.source() == Strategy::FROM_FILE) + t->o->label(nullptr); + else if (t->o->label()) t->label(t->o->label()); // allow editing + t->o->user_data((void*)t); + // Put it in the parent: + // ((Fl_Group *)(p->o))->add(t->o); (done by Node::add()) + // add to browser: + t->add(anchor, strategy); + t->redraw(); + return t; +} + +void Widget_Node::setimage(Image_Asset *i) { + if (i == image || is_a(Type::Window)) return; + if (image) image->dec_ref(); + if (i) i->inc_ref(); + image = i; + if (i) { + o->image(i->image()); + if (o->image() && (scale_image_w_ || scale_image_h_)) { + int iw = scale_image_w_>0 ? scale_image_w_ : o->image()->data_w(); + int ih = scale_image_h_>0 ? scale_image_h_ : o->image()->data_h(); + o->image()->scale(iw, ih, 0, 1); + } + } else { + o->image(nullptr); + //scale_image_w_ = scale_image_h_ = 0; + } + redraw(); +} + +void Widget_Node::setinactive(Image_Asset *i) { + if (i == inactive || is_a(Type::Window)) return; + if (inactive) inactive->dec_ref(); + if (i) i->inc_ref(); + inactive = i; + if (i) { + o->deimage(i->image()); + if (o->deimage()) { + int iw = scale_deimage_w_>0 ? scale_deimage_w_ : o->deimage()->data_w(); + int ih = scale_deimage_h_>0 ? scale_deimage_h_ : o->deimage()->data_h(); + o->deimage()->scale(iw, ih, 0, 1); + } + } else { + o->deimage(nullptr); + //scale_deimage_w_ = scale_deimage_h_ = 0; + } + redraw(); +} + +void Widget_Node::setlabel(const char *n) { + o->label(n); + redraw(); +} + +Widget_Node::Widget_Node() +: override_visible_(0) +{ + for (int n=0; nwindow(); + delete o; + if (win) + win->redraw(); + } + if (subclass_) free((void*)subclass_); + if (tooltip_) free((void*)tooltip_); + if (image_name_) { + free((void*)image_name_); + if (image) image->dec_ref(); + } + if (inactive_name_) { + free((void*)inactive_name_); + if (inactive) inactive->dec_ref(); + } + for (int n=0; ntooltip(n); +} + +void Widget_Node::image_name(const char *n) { + setimage(Image_Asset::find(n)); + storestring(n,image_name_); +} + +void Widget_Node::inactive_name(const char *n) { + setinactive(Image_Asset::find(n)); + storestring(n,inactive_name_); +} + +void Widget_Node::redraw() { + Node *t = this; + if (is_a(Type::Menu_Item)) { + // find the menu button that parents this menu: + do t = t->parent; while (t && t->is_a(Type::Menu_Item)); + // kludge to cause build_menu to be called again: + if (t) + t->add_child(nullptr, nullptr); + } else { + while (t->parent && t->parent->is_widget()) t = t->parent; + ((Widget_Node*)t)->o->redraw(); + } +} + +// the recursive part sorts all children, returns pointer to next: +Node *sort(Node *parent) { + Node *f,*n=nullptr; + for (f = parent ? parent->next : Fluid.proj.tree.first; ; f = n) { + if (!f || (parent && f->level <= parent->level)) break; + n = sort(f); + if (!f->selected || !f->is_true_widget()) continue; + Fl_Widget* fw = ((Widget_Node*)f)->o; + Node *g; // we will insert before this + for (g = parent ? parent->next : Fluid.proj.tree.first; g != f; g = g->next) { + if (!g->selected || g->level > f->level) continue; + Fl_Widget* gw = ((Widget_Node*)g)->o; + if (gw->y() > fw->y()) break; + if (gw->y() == fw->y() && gw->x() > fw->x()) break; + } + if (g != f) f->move_before(g); + } + if (parent) + parent->layout_widget(); + return f; +} + +//////////////////////////////////////////////////////////////// +// The control panels! + +Fl_Window *the_panel; + +// All the callbacks use the argument to indicate whether to load or store. +// This avoids the need for pointers to all the widgets, and keeps the +// code localized in the callbacks. +// A value of LOAD means to load. The hope is that this will not collide +// with any actual useful values for the argument. I also use this to +// initialized parts of the widget that are nyi by fluid. + +Widget_Node *current_widget; // one of the selected ones +void* const LOAD = (void *)"LOAD"; // "magic" pointer to indicate that we need to load values into the dialog +int numselected; // number selected +int haderror; + +void name_public_cb(Fl_Choice* i, void* v) { + if (v == LOAD) { + i->value(current_widget->public_>0); + if (current_widget->is_in_class()) i->hide(); else i->show(); + } else { + int mod = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + ((Widget_Node*)o)->public_ = i->value(); + mod = 1; + } + } + if (mod) { + Fluid.proj.set_modflag(1); + redraw_browser(); + } + } +} + +/* Treating UNDO for text widget. + + Goal: we want to continuously update the UI while the user is typing text + (changing the label, in this case). Code View does deferred updates, and + the widget browser and widget panel update on every keystroke. At the same + time, we want to limit undo actions to few and logical units. + + Caveats: + + 1: the text widget has its own undo handling for the text field, but we may want to do a global undo + 2: every o->label() call will create an undo entry, but we want only one single event for all selected widgets + 3: we want a single undo for the entire editing phase, but still propagate changes as they happen + + The edit process has these main states: + + 1: starting to edit [first_change==1 && !unfocus]; we must create a single undo checkpoint before anything changes + 2: continue editing [first_change==0 && !unfocus]; we must suspend any undo checkpoints + 3: done editing, unfocus [first_change==0 && unfocus]; we must make sure that undo checkpoints are enabled again + 4: losing focus without editing [first_change==1 && unfocus]; don't create and checkpoints + + We must also check: + 1: changing focus without changing text (works) + 2: copy and paste, drag and drop operations (works) + 3: save operation without unfocus event (works) + */ +void label_cb(Fl_Input* i, void *v) { + static int first_change = 1; + if (v == LOAD) { + i->value(current_widget->label()); + first_change = 1; + } else { + if (i->changed()) { + Fluid.proj.undo.suspend(); + int mod = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + if (!mod) { + if (first_change) { + Fluid.proj.undo.resume(); + Fluid.proj.undo.checkpoint(); + Fluid.proj.undo.suspend(); + first_change = 0; + } + mod = 1; + } + o->label(i->value()); + } + } + Fluid.proj.undo.resume(); + if (mod) Fluid.proj.set_modflag(1); + } + int r = (int)Fl::callback_reason(); + if ( (r == FL_REASON_LOST_FOCUS) || (r == FL_REASON_ENTER_KEY) ) + first_change = 1; + } +} + + + + + + +int widget_i = 0; + +static int vars_i_cb(const fld::widget::Formula_Input*, void *v) { + return widget_i; +} + +static int vars_x_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = (Node*)v; + if (t->is_widget()) + return ((Widget_Node*)t)->o->x(); + return 0; +} + +static int vars_y_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = (Node*)v; + if (t->is_widget()) + return ((Widget_Node*)t)->o->y(); + return 0; +} + +static int vars_w_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = (Node*)v; + if (t->is_widget()) + return ((Widget_Node*)t)->o->w(); + return 0; +} + +static int vars_h_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = (Node*)v; + if (t->is_widget()) + return ((Widget_Node*)t)->o->h(); + return 0; +} + +static int vars_px_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->parent; + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->x(); + return 0; +} + +static int vars_py_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->parent; + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->y(); + return 0; +} + +static int vars_pw_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->parent; + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->w(); + return 0; +} + +static int vars_ph_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->parent; + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->h(); + return 0; +} + +static int vars_sx_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->prev_sibling(); + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->x(); + return 0; +} + +static int vars_sy_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->prev_sibling(); + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->y(); + return 0; +} + +static int vars_sw_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->prev_sibling(); + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->w(); + return 0; +} + +static int vars_sh_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->prev_sibling(); + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->h(); + return 0; +} + +static int bbox_x, bbox_y, bbox_r, bbox_b; + +static void calculate_bbox(Node *p) { + char first = 1; + bbox_x = bbox_y = bbox_r = bbox_b = 0; + for (p=p->first_child(); p; p=p->next_sibling()) { + if (p->is_widget()) { + Fl_Widget *o = ((Widget_Node*)p)->o; + if (first) { + bbox_x = o->x(); bbox_y = o->y(); + bbox_r = o->x() + o->w(); bbox_b = o->y() + o->h(); + first = 0; + } else { + bbox_x = std::min(bbox_x, o->x()); + bbox_y = std::min(bbox_y, o->y()); + bbox_r = std::max(bbox_r, o->x() + o->w()); + bbox_b = std::max(bbox_b, o->y() + o->h()); + } + } + } +} + +static int vars_cx_cb(const fld::widget::Formula_Input*, void *v) { + calculate_bbox((Node*)v); + return bbox_x; +} + +static int vars_cy_cb(const fld::widget::Formula_Input*, void *v) { + calculate_bbox((Node*)v); + return bbox_y; +} + +static int vars_cw_cb(const fld::widget::Formula_Input*, void *v) { + calculate_bbox((Node*)v); + return bbox_r - bbox_x; +} + +static int vars_ch_cb(const fld::widget::Formula_Input*, void *v) { + calculate_bbox((Node*)v); + return bbox_b - bbox_y; +} + +fld::widget::Formula_Input_Vars widget_vars[] = { + { "i", vars_i_cb }, // zero based counter of selected widgets + { "x", vars_x_cb }, // position and size of current widget + { "y", vars_y_cb }, + { "w", vars_w_cb }, + { "h", vars_h_cb }, + { "px", vars_px_cb }, // position and size of parent widget + { "py", vars_py_cb }, + { "pw", vars_pw_cb }, + { "ph", vars_ph_cb }, + { "sx", vars_sx_cb }, // position and size of previous sibling + { "sy", vars_sy_cb }, + { "sw", vars_sw_cb }, + { "sh", vars_sh_cb }, + { "cx", vars_cx_cb }, // position and size of bounding box of all children + { "cy", vars_cy_cb }, + { "cw", vars_cw_cb }, + { "ch", vars_ch_cb }, + { nullptr } +}; + + + + + +//////////////////////////////////////////////////////////////// + +// turn number to string or string to number for saving to file: +// does not work for hierarchical menus! + +const char *item_name(Fl_Menu_Item* m, int i) { + if (m) { + while (m->label()) { + if (m->argument() == i) return m->label(); + m++; + } + } + static char buffer[20]; + sprintf(buffer, "%d", i); + return buffer; +} +int item_number(Fl_Menu_Item* m, const char* i) { + if (!i) + return 0; + if (m && i) { + if (i[0]=='F' && i[1]=='L' && i[2]=='_') i += 3; + while (m->label()) { + if (!strcmp(m->label(), i)) return int(m->argument()); + m++; + } + } + return atoi(i); +} + +#define ZERO_ENTRY 1000 + +Fl_Menu_Item boxmenu[] = { + {"NO_BOX",0,nullptr,(void *)ZERO_ENTRY}, + {"boxes",0,nullptr,nullptr,FL_SUBMENU}, + {"UP_BOX",0,nullptr,(void *)FL_UP_BOX}, + {"DOWN_BOX",0,nullptr,(void *)FL_DOWN_BOX}, + {"FLAT_BOX",0,nullptr,(void *)FL_FLAT_BOX}, + {"BORDER_BOX",0,nullptr,(void *)FL_BORDER_BOX}, + {"THIN_UP_BOX",0,nullptr,(void *)FL_THIN_UP_BOX}, + {"THIN_DOWN_BOX",0,nullptr,(void *)FL_THIN_DOWN_BOX}, + {"ENGRAVED_BOX",0,nullptr,(void *)FL_ENGRAVED_BOX}, + {"EMBOSSED_BOX",0,nullptr,(void *)FL_EMBOSSED_BOX}, + {"ROUND_UP_BOX",0,nullptr,(void *)FL_ROUND_UP_BOX}, + {"ROUND_DOWN_BOX",0,nullptr,(void *)FL_ROUND_DOWN_BOX}, + {"DIAMOND_UP_BOX",0,nullptr,(void *)FL_DIAMOND_UP_BOX}, + {"DIAMOND_DOWN_BOX",0,nullptr,(void *)FL_DIAMOND_DOWN_BOX}, + {"SHADOW_BOX",0,nullptr,(void *)FL_SHADOW_BOX}, + {"ROUNDED_BOX",0,nullptr,(void *)FL_ROUNDED_BOX}, + {"RSHADOW_BOX",0,nullptr,(void *)FL_RSHADOW_BOX}, + {"RFLAT_BOX",0,nullptr,(void *)FL_RFLAT_BOX}, + {"OVAL_BOX",0,nullptr,(void *)FL_OVAL_BOX}, + {"OSHADOW_BOX",0,nullptr,(void *)FL_OSHADOW_BOX}, + {"OFLAT_BOX",0,nullptr,(void *)FL_OFLAT_BOX}, + {"PLASTIC_UP_BOX",0,nullptr,(void *)FL_PLASTIC_UP_BOX}, + {"PLASTIC_DOWN_BOX",0,nullptr,(void *)FL_PLASTIC_DOWN_BOX}, + {"PLASTIC_THIN_UP_BOX",0,nullptr,(void *)FL_PLASTIC_THIN_UP_BOX}, + {"PLASTIC_THIN_DOWN_BOX",0,nullptr,(void *)FL_PLASTIC_THIN_DOWN_BOX}, + {"PLASTIC_ROUND_UP_BOX",0,nullptr,(void *)FL_PLASTIC_ROUND_UP_BOX}, + {"PLASTIC_ROUND_DOWN_BOX",0,nullptr,(void *)FL_PLASTIC_ROUND_DOWN_BOX}, + {"GTK_UP_BOX",0,nullptr,(void *)FL_GTK_UP_BOX}, + {"GTK_DOWN_BOX",0,nullptr,(void *)FL_GTK_DOWN_BOX}, + {"GTK_THIN_UP_BOX",0,nullptr,(void *)FL_GTK_THIN_UP_BOX}, + {"GTK_THIN_DOWN_BOX",0,nullptr,(void *)FL_GTK_THIN_DOWN_BOX}, + {"GTK_ROUND_UP_BOX",0,nullptr,(void *)FL_GTK_ROUND_UP_BOX}, + {"GTK_ROUND_DOWN_BOX",0,nullptr,(void *)FL_GTK_ROUND_DOWN_BOX}, + {"GLEAM_UP_BOX",0,nullptr,(void *)FL_GLEAM_UP_BOX}, + {"GLEAM_DOWN_BOX",0,nullptr,(void *)FL_GLEAM_DOWN_BOX}, + {"GLEAM_THIN_UP_BOX",0,nullptr,(void *)FL_GLEAM_THIN_UP_BOX}, + {"GLEAM_THIN_DOWN_BOX",0,nullptr,(void *)FL_GLEAM_THIN_DOWN_BOX}, + {"GLEAM_ROUND_UP_BOX",0,nullptr,(void *)FL_GLEAM_ROUND_UP_BOX}, + {"GLEAM_ROUND_DOWN_BOX",0,nullptr,(void *)FL_GLEAM_ROUND_DOWN_BOX}, + {"OXY_UP_BOX",0,nullptr,(void *)FL_OXY_UP_BOX}, + {"OXY_DOWN_BOX",0,nullptr,(void *)FL_OXY_DOWN_BOX}, + {"OXY_THIN_UP_BOX",0,nullptr,(void *)FL_OXY_THIN_UP_BOX}, + {"OXY_THIN_DOWN_BOX",0,nullptr,(void *)FL_OXY_THIN_DOWN_BOX}, + {"OXY_ROUND_UP_BOX",0,nullptr,(void *)FL_OXY_ROUND_UP_BOX}, + {"OXY_ROUND_DOWN_BOX",0,nullptr,(void *)FL_OXY_ROUND_DOWN_BOX}, + {"OXY_BUTTON_UP_BOX",0,nullptr,(void *)FL_OXY_BUTTON_UP_BOX}, + {"OXY_BUTTON_DOWN_BOX",0,nullptr,(void *)FL_OXY_BUTTON_DOWN_BOX}, + {nullptr}, + {"frames",0,nullptr,nullptr,FL_SUBMENU}, + {"UP_FRAME",0,nullptr,(void *)FL_UP_FRAME}, + {"DOWN_FRAME",0,nullptr,(void *)FL_DOWN_FRAME}, + {"THIN_UP_FRAME",0,nullptr,(void *)FL_THIN_UP_FRAME}, + {"THIN_DOWN_FRAME",0,nullptr,(void *)FL_THIN_DOWN_FRAME}, + {"ENGRAVED_FRAME",0,nullptr,(void *)FL_ENGRAVED_FRAME}, + {"EMBOSSED_FRAME",0,nullptr,(void *)FL_EMBOSSED_FRAME}, + {"BORDER_FRAME",0,nullptr,(void *)FL_BORDER_FRAME}, + {"SHADOW_FRAME",0,nullptr,(void *)FL_SHADOW_FRAME}, + {"ROUNDED_FRAME",0,nullptr,(void *)FL_ROUNDED_FRAME}, + {"OVAL_FRAME",0,nullptr,(void *)FL_OVAL_FRAME}, + {"PLASTIC_UP_FRAME",0,nullptr,(void *)FL_PLASTIC_UP_FRAME}, + {"PLASTIC_DOWN_FRAME",0,nullptr,(void *)FL_PLASTIC_DOWN_FRAME}, + {"GTK_UP_FRAME",0,nullptr,(void *)FL_GTK_UP_FRAME}, + {"GTK_DOWN_FRAME",0,nullptr,(void *)FL_GTK_DOWN_FRAME}, + {"GTK_THIN_UP_FRAME",0,nullptr,(void *)FL_GTK_THIN_UP_FRAME}, + {"GTK_THIN_DOWN_FRAME",0,nullptr,(void *)FL_GTK_THIN_DOWN_FRAME}, + {"GLEAM_UP_FRAME",0,nullptr,(void *)FL_GLEAM_UP_FRAME}, + {"GLEAM_DOWN_FRAME",0,nullptr,(void *)FL_GLEAM_DOWN_FRAME}, + {"OXY_UP_FRAME",0,nullptr,(void *)FL_OXY_UP_FRAME}, + {"OXY_DOWN_FRAME",0,nullptr,(void *)FL_OXY_DOWN_FRAME}, + {"OXY_THIN_UP_FRAME",0,nullptr,(void *)FL_OXY_THIN_UP_FRAME}, + {"OXY_THIN_DOWN_FRAME",0,nullptr,(void *)FL_OXY_THIN_DOWN_FRAME}, + {nullptr}, + {nullptr}}; + +const char *boxname(int i) { + if (!i) i = ZERO_ENTRY; + for (int j = 0; j < int(sizeof(boxmenu)/sizeof(*boxmenu)); j++) + if (boxmenu[j].argument() == i) return boxmenu[j].label(); + return nullptr; +} + +int boxnumber(const char *i) { + if (i[0]=='F' && i[1]=='L' && i[2]=='_') i += 3; + for (int j = 0; j < int(sizeof(boxmenu)/sizeof(*boxmenu)); j++) + if (boxmenu[j].label() && !strcmp(boxmenu[j].label(), i)) { + return int(boxmenu[j].argument()); + } + return 0; +} + + + + +//////////////////////////////////////////////////////////////// + +Fl_Menu_Item whenmenu[] = { + // set individual bits + {"FL_WHEN_CHANGED",0,nullptr,(void*)FL_WHEN_CHANGED, FL_MENU_TOGGLE}, + {"FL_WHEN_NOT_CHANGED",0,nullptr,(void*)FL_WHEN_NOT_CHANGED, FL_MENU_TOGGLE}, + {"FL_WHEN_RELEASE",0,nullptr,(void*)FL_WHEN_RELEASE, FL_MENU_TOGGLE}, + {"FL_WHEN_ENTER_KEY",0,nullptr,(void*)FL_WHEN_ENTER_KEY, FL_MENU_TOGGLE}, + {"FL_WHEN_CLOSED",0,nullptr,(void*)FL_WHEN_CLOSED, FL_MENU_TOGGLE|FL_MENU_DIVIDER}, + // set bit combinations + {"FL_WHEN_NEVER",0,nullptr,(void*)FL_WHEN_NEVER}, + {"FL_WHEN_RELEASE_ALWAYS",0,nullptr,(void*)FL_WHEN_RELEASE_ALWAYS}, + {"FL_WHEN_ENTER_KEY_ALWAYS",0,nullptr,(void*)FL_WHEN_ENTER_KEY_ALWAYS}, + {"FL_WHEN_ENTER_KEY_CHANGED",0,nullptr,(void*)FL_WHEN_ENTER_KEY_CHANGED}, + {nullptr}}; + + +static Fl_Menu_Item whensymbolmenu[] = { + /* 0 */ {"FL_WHEN_NEVER",0,nullptr,(void*)FL_WHEN_NEVER}, + /* 1 */ {"FL_WHEN_CHANGED",0,nullptr,(void*)FL_WHEN_CHANGED}, + /* 2 */ {"FL_WHEN_NOT_CHANGED",0,nullptr,(void*)FL_WHEN_NOT_CHANGED}, + /* 3 */ {"FL_WHEN_CHANGED | FL_WHEN_NOT_CHANGED",0,nullptr,(void*)(FL_WHEN_CHANGED|FL_WHEN_NOT_CHANGED)}, + /* 4 */ {"FL_WHEN_RELEASE",0,nullptr,(void*)FL_WHEN_RELEASE}, + /* 5 */ {"FL_WHEN_CHANGED | FL_WHEN_RELEASE",0,nullptr,(void*)(FL_WHEN_CHANGED|FL_WHEN_RELEASE)}, + /* 6 */ {"FL_WHEN_RELEASE_ALWAYS",0,nullptr,(void*)FL_WHEN_RELEASE_ALWAYS}, + /* 7 */ {"FL_WHEN_CHANGED | FL_WHEN_RELEASE_ALWAYS",0,nullptr,(void*)(FL_WHEN_CHANGED|FL_WHEN_RELEASE_ALWAYS)}, + /* 8 */ {"FL_WHEN_ENTER_KEY",0,nullptr,(void*)FL_WHEN_ENTER_KEY}, + /* 9 */ {"FL_WHEN_CHANGED | FL_WHEN_ENTER_KEY",0,nullptr,(void*)(FL_WHEN_CHANGED|FL_WHEN_ENTER_KEY)}, + /* 10 */ {"FL_WHEN_ENTER_KEY_ALWAYS",0,nullptr,(void*)FL_WHEN_ENTER_KEY_ALWAYS}, + /* 11 */ {"FL_WHEN_ENTER_KEY_CHANGED",0,nullptr,(void*)FL_WHEN_ENTER_KEY_CHANGED}, + /* 12 */ {"FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY",0,nullptr,(void*)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY)}, + /* 13 */ {"FL_WHEN_RELEASE | FL_WHEN_CHANGED | FL_WHEN_ENTER_KEY",0,nullptr,(void*)(FL_WHEN_RELEASE|FL_WHEN_CHANGED|FL_WHEN_ENTER_KEY)}, + /* 14 */ {"FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY_ALWAYS",0,nullptr,(void*)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY_ALWAYS)}, + /* 15 */ {"FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY_CHANGED",0,nullptr,(void*)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY_CHANGED)}, + {nullptr} +}; + +// Return a text string representing the Fl_When value n +const char* when_symbol_name(int n) { + static char sym[128]; + if (n == FL_WHEN_CLOSED) { + strcpy(sym, "FL_WHEN_CLOSED"); + } else { + strcpy(sym, whensymbolmenu[n&15].label()); + if (n & FL_WHEN_CLOSED) + strcat(sym, " | FL_WHEN_CLOSED"); + } + return sym; +} + +// Set the check marks in the "when()" menu according to the Fl_When value n +void set_whenmenu(int n) { + if (n&FL_WHEN_CHANGED) whenmenu[0].set(); else whenmenu[0].clear(); + if (n&FL_WHEN_NOT_CHANGED) whenmenu[1].set(); else whenmenu[1].clear(); + if (n&FL_WHEN_RELEASE) whenmenu[2].set(); else whenmenu[2].clear(); + if (n&FL_WHEN_ENTER_KEY) whenmenu[3].set(); else whenmenu[3].clear(); + if (n&FL_WHEN_CLOSED) whenmenu[4].set(); else whenmenu[4].clear(); +} + + +uchar Widget_Node::resizable() const { + if (is_a(Type::Window)) return ((Fl_Window*)o)->resizable() != nullptr; + Fl_Group* p = (Fl_Group*)o->parent(); + if (p) return p->resizable() == o; + else return 0; +} + +void Widget_Node::resizable(uchar v) { + if (v) { + if (resizable()) return; + if (is_a(Type::Window)) ((Fl_Window*)o)->resizable(o); + else { + Fl_Group* p = (Fl_Group*)o->parent(); + if (p) p->resizable(o); + } + } else { + if (!resizable()) return; + if (is_a(Type::Window)) { + ((Fl_Window*)o)->resizable(nullptr); + } else { + Fl_Group* p = (Fl_Group*)o->parent(); + if (p) p->resizable(nullptr); + } + } +} + + + +//////////////////////////////////////////////////////////////// + +Fl_Menu_Item fontmenu[] = { + {"Helvetica"}, + {"Helvetica bold"}, + {"Helvetica italic"}, + {"Helvetica bold italic"}, + {"Courier"}, + {"Courier bold"}, + {"Courier italic"}, + {"Courier bold italic"}, + {"Times"}, + {"Times bold"}, + {"Times italic"}, + {"Times bold italic"}, + {"Symbol"}, + {"Terminal"}, + {"Terminal Bold"}, + {"Zapf Dingbats"}, + {nullptr} +}; + +Fl_Menu_Item fontmenu_w_default[] = { + {"", 0, nullptr, nullptr, FL_MENU_DIVIDER}, + {"Helvetica"}, + {"Helvetica bold"}, + {"Helvetica italic"}, + {"Helvetica bold italic"}, + {"Courier"}, + {"Courier bold"}, + {"Courier italic"}, + {"Courier bold italic"}, + {"Times"}, + {"Times bold"}, + {"Times italic"}, + {"Times bold italic"}, + {"Symbol"}, + {"Terminal"}, + {"Terminal Bold"}, + {"Zapf Dingbats"}, + {nullptr} +}; + + + +extern const char *ui_find_image_name; + +Fl_Menu_Item labeltypemenu[] = { + {"NORMAL_LABEL",0,nullptr,(void*)nullptr}, + {"SHADOW_LABEL",0,nullptr,(void*)FL_SHADOW_LABEL}, + {"ENGRAVED_LABEL",0,nullptr,(void*)FL_ENGRAVED_LABEL}, + {"EMBOSSED_LABEL",0,nullptr,(void*)FL_EMBOSSED_LABEL}, + {"NO_LABEL",0,nullptr,(void*)(FL_NO_LABEL)}, + {nullptr}}; + +void labeltype_cb(Fl_Choice* i, void *v) { + if (v == LOAD) { + int n; + n = current_widget->o->labeltype(); + i->when(FL_WHEN_RELEASE); + for (int j = 0; j < int(sizeof(labeltypemenu)/sizeof(*labeltypemenu)); j++) + if (labeltypemenu[j].argument() == n) {i->value(j); break;} + } else { + int mod = 0; + int m = i->value(); + int n = int(labeltypemenu[m].argument()); + if (n<0) return; // should not happen + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* p = (Widget_Node*)o; + p->o->labeltype((Fl_Labeltype)n); + p->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +//////////////////////////////////////////////////////////////// + +Fl_Menu_Item colormenu[] = { + { "Foreground Color", 0, nullptr, (void*)(fl_intptr_t)FL_FOREGROUND_COLOR, 0, 0, FL_HELVETICA, 11}, + { "Background Color", 0, nullptr, (void*)(fl_intptr_t)FL_BACKGROUND_COLOR, 0, 0, FL_HELVETICA, 11}, + { "Background Color 2", 0, nullptr, (void*)(fl_intptr_t)FL_BACKGROUND2_COLOR, 0, 0, FL_HELVETICA, 11}, + { "Selection Color", 0, nullptr, (void*)(fl_intptr_t)FL_SELECTION_COLOR, 0, 0, FL_HELVETICA, 11}, + { "Inactive Color", 0, nullptr, (void*)(fl_intptr_t)FL_INACTIVE_COLOR, FL_MENU_DIVIDER, 0, FL_HELVETICA, 11}, + { "Black", 0, nullptr, (void*)(fl_intptr_t)FL_BLACK, 0, 0, FL_HELVETICA, 11}, + { "White", 0, nullptr, (void*)(fl_intptr_t)FL_WHITE, FL_MENU_DIVIDER, 0, FL_HELVETICA, 11}, + { "Gray 0", 0, nullptr, (void*)(fl_intptr_t)FL_GRAY0, 0, 0, FL_HELVETICA, 11}, + { "Dark 3", 0, nullptr, (void*)(fl_intptr_t)FL_DARK3, 0, 0, FL_HELVETICA, 11}, + { "Dark 2", 0, nullptr, (void*)(fl_intptr_t)FL_DARK2, 0, 0, FL_HELVETICA, 11}, + { "Dark 1", 0, nullptr, (void*)(fl_intptr_t)FL_DARK1, 0, 0, FL_HELVETICA, 11}, + { "Light 1", 0, nullptr, (void*)(fl_intptr_t)FL_LIGHT1, 0, 0, FL_HELVETICA, 11}, + { "Light 2", 0, nullptr, (void*)(fl_intptr_t)FL_LIGHT2, 0, 0, FL_HELVETICA, 11}, + { "Light 3", 0, nullptr, (void*)(fl_intptr_t)FL_LIGHT3, 0, 0, FL_HELVETICA, 11}, + { nullptr } +}; + +void color_common(Fl_Color c) { + int mod = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + q->o->color(c); q->o->redraw(); + if (q->parent && q->parent->is_a(Type::Tabs)) { + if (q->o->parent()) q->o->parent()->redraw(); + } + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +} + + + +void color2_common(Fl_Color c) { + int mod = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + q->o->selection_color(c); q->o->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +} + + + +void labelcolor_common(Fl_Color c) { + int mod = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + q->o->labelcolor(c); q->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +} + + + +static Fl_Button* relative(Fl_Widget* o, int i) { + Fl_Group* g = (Fl_Group*)(o->parent()); + return (Fl_Button*)(g->child(g->find(*o)+i)); +} + +static Fl_Menu_Item alignmenu[] = { + {"FL_ALIGN_CENTER",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_CENTER)}, + {"FL_ALIGN_TOP",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_TOP)}, + {"FL_ALIGN_BOTTOM",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_BOTTOM)}, + {"FL_ALIGN_LEFT",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_LEFT)}, + {"FL_ALIGN_RIGHT",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_RIGHT)}, + {"FL_ALIGN_INSIDE",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_INSIDE)}, + {"FL_ALIGN_CLIP",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_CLIP)}, + {"FL_ALIGN_WRAP",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_WRAP)}, + {"FL_ALIGN_TEXT_OVER_IMAGE",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_TEXT_OVER_IMAGE)}, + {"FL_ALIGN_TOP_LEFT",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_TOP_LEFT)}, + {"FL_ALIGN_TOP_RIGHT",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_TOP_RIGHT)}, + {"FL_ALIGN_BOTTOM_LEFT",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_BOTTOM_LEFT)}, + {"FL_ALIGN_BOTTOM_RIGHT",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_BOTTOM_RIGHT)}, + {"FL_ALIGN_LEFT_TOP",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_LEFT_TOP)}, + {"FL_ALIGN_RIGHT_TOP",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_RIGHT_TOP)}, + {"FL_ALIGN_LEFT_BOTTOM",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_LEFT_BOTTOM)}, + {"FL_ALIGN_RIGHT_BOTTOM",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_RIGHT_BOTTOM)}, + {nullptr}}; + +void align_cb(Fl_Button* i, void *v) { + Fl_Align b = Fl_Align(fl_uintptr_t(i->user_data())); + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {i->deactivate(); return;} else i->activate(); + i->value(current_widget->o->align() & b); + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + Fl_Align x = q->o->align(); + Fl_Align y; + if (i->value()) { + y = x | b; + if (b == FL_ALIGN_LEFT || b == FL_ALIGN_TOP) { + Fl_Button *b1 = relative(i,+1); + b1->clear(); + y = y & ~(b1->argument()); + } + if (b == FL_ALIGN_RIGHT || b == FL_ALIGN_BOTTOM) { + Fl_Button *b1 = relative(i,-1); + b1->clear(); + y = y & ~(b1->argument()); + } + } else { + y = x & ~b; + } + if (x != y) { + q->o->align(y); + q->redraw(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +void align_position_cb(Fl_Choice *i, void *v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {i->deactivate(); return;} else i->activate(); + Fl_Menu_Item *mi = (Fl_Menu_Item*)i->menu(); + Fl_Align b = current_widget->o->align() & FL_ALIGN_POSITION_MASK; + for (;mi->text;mi++) { + if ((Fl_Align)(mi->argument())==b) + i->value(mi); + } + } else { + const Fl_Menu_Item *mi = i->menu() + i->value(); + Fl_Align b = Fl_Align(fl_uintptr_t(mi->user_data())); + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + Fl_Align x = q->o->align(); + Fl_Align y = (x & ~FL_ALIGN_POSITION_MASK) | b; + if (x != y) { + q->o->align(y); + q->redraw(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +void align_text_image_cb(Fl_Choice *i, void *v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {i->deactivate(); return;} else i->activate(); + Fl_Menu_Item *mi = (Fl_Menu_Item*)i->menu(); + Fl_Align b = current_widget->o->align() & FL_ALIGN_IMAGE_MASK; + for (;mi->text;mi++) { + if ((Fl_Align)(mi->argument())==b) + i->value(mi); + } + } else { + const Fl_Menu_Item *mi = i->menu() + i->value(); + Fl_Align b = Fl_Align(fl_uintptr_t(mi->user_data())); + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + Fl_Align x = q->o->align(); + Fl_Align y = (x & ~FL_ALIGN_IMAGE_MASK) | b; + if (x != y) { + q->o->align(y); + q->redraw(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +//////////////////////////////////////////////////////////////// + + + + + +//////////////////////////////////////////////////////////////// + +// textstuff: set textfont, textsize, textcolor attributes: + +// default widget returns 0 to indicate not-implemented: +// The first parameter specifies the operation: +// 0: get all values +// 1: set the text font +// 2: set the text size +// 3: set the text color +// 4: get all default values for this type +int Widget_Node::textstuff(int, Fl_Font&, int&, Fl_Color&) { + return 0; +} + + + +void textcolor_common(Fl_Color c) { + Fl_Font n; int s; + int mod = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + q->textstuff(3,n,s,c); q->o->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +} + +//////////////////////////////////////////////////////////////// +// Kludges to the panel for subclasses: + + + + +//static void flex_margin_cb(Fl_Value_Input* i, void* v, +// void (*load_margin)(Fl_Flex*,Fl_Value_Input*), +// int (*update_margin)(Fl_Flex*,int)) { +// if (v == LOAD) { +// if (current_widget->is_a(Type::Flex)) { +// load_margin((Fl_Flex*)current_widget->o, i); +// } +// } else { +// int mod = 0; +// int new_value = (int)i->value(); +// for (Node *o = Fluid.proj.tree.first; o; o = o->next) { +// if (o->selected && o->is_a(Type::Flex)) { +// Flex_Node* q = (Flex_Node*)o; +// Fl_Flex* w = (Fl_Flex*)q->o; +// if (update_margin(w, new_value)) { +// w->layout(); +// mod = 1; +// } +// } +// } +// if (mod) Fluid.proj.set_modflag(1); +// } +//} + + + + +static void load_gap(Fl_Flex *w, Fl_Value_Input* i) +{ + int v = w->gap(); + i->value((double)v); +} + +static int update_gap(Fl_Flex *w, int new_value) +{ + int g = w->gap(); + if (new_value!=g) { + w->gap(new_value); + return 1; + } else { + return 0; + } +} + +void flex_margin_gap_cb(Fl_Value_Input* i, void* v) { + flex_margin_cb(i, v, load_gap, update_gap); +} + +void position_group_cb(Fl_Group* g, void* v) { + if (v == LOAD) { + if (Flex_Node::parent_is_flex(current_widget)) { + g->hide(); + } else { + g->show(); + } + } + propagate_load(g, v); +} + + + +//////////////////////////////////////////////////////////////// + +// subtypes: + +Fl_Menu_Item *Widget_Node::subtypes() {return nullptr;} + + +//////////////////////////////////////////////////////////////// + +void propagate_load(Fl_Group* g, void* v) { + if (v == LOAD) { + Fl_Widget*const* a = g->array(); + for (int i=g->children(); i--;) { + Fl_Widget* o = *a++; + o->do_callback(o, LOAD, FL_REASON_USER); + } + } +} + +void set_cb(Fl_Button*, void*) { + haderror = 0; + Fl_Widget*const* a = the_panel->array(); + for (int i=the_panel->children(); i--;) { + Fl_Widget* o = *a++; + if (o->changed()) { + o->do_callback(); + if (haderror) return; + o->clear_changed(); + } + } +} + +void ok_cb(Fl_Return_Button* o, void* v) { + set_cb(o,v); + if (!haderror) the_panel->hide(); +} + +void toggle_overlays(Fl_Widget *,void *); // in Window_Node.cxx +void overlay_cb(Fl_Button*o,void *v) { + toggle_overlays(o,v); +} + +void leave_live_mode_cb(Fl_Widget*, void*); + +void live_mode_cb(Fl_Button*o,void *) { + /// \todo live mode should end gracefully when the application quits + /// or when the user closes the live widget + static Node *live_type = nullptr; + static Fl_Widget *live_widget = nullptr; + static Fl_Window *live_window = nullptr; + // if 'o' is 0, we must quit live mode + if (!o) { + o = wLiveMode; + o->value(0); + } + if (o->value()) { + if (numselected == 1) { + Fl_Group::current(nullptr); + live_widget = current_widget->enter_live_mode(1); + if (live_widget) { + live_type = current_widget; + Fl_Group::current(nullptr); + int w = live_widget->w(); + int h = live_widget->h(); + live_window = new Fl_Double_Window(w+20, h+55, "Fluid Live Resize"); + live_window->box(FL_FLAT_BOX); + live_window->color(FL_GREEN); + Fl_Group *rsz = new Fl_Group(0, h+20, 130, 35); + rsz->box(FL_NO_BOX); + Fl_Box *rsz_dummy = new Fl_Box(110, h+20, 1, 25); + rsz_dummy->box(FL_NO_BOX); + rsz->resizable(rsz_dummy); + Fl_Button *btn = new Fl_Button(10, h+20, 100, 25, "Exit Live Resize"); + btn->labelsize(12); + btn->callback(leave_live_mode_cb); + rsz->end(); + live_window->add(live_widget); + live_widget->position(10, 10); + live_window->resizable(live_widget); + live_window->set_modal(); // block all other UI + live_window->callback(leave_live_mode_cb); + if (current_widget->is_a(Type::Window)) { + Window_Node *w = (Window_Node*)current_widget; + int mw = w->sr_min_w; if (mw>0) mw += 20; + int mh = w->sr_min_h; if (mh>0) mh += 55; + int MW = w->sr_max_w; if (MW>0) MW += 20; + int MH = w->sr_max_h; if (MH>2) MH += 55; + if (mw || mh || MW || MH) + live_window->size_range(mw, mh, MW, MH); + } + live_window->show(); + live_widget->show(); + } else o->value(0); + } else o->value(0); + } else { + if (live_type) + live_type->leave_live_mode(); + if (live_window) { + live_window->hide(); + Fl::delete_widget(live_window); + } + live_type = nullptr; + live_widget = nullptr; + live_window = nullptr; + } +} + +// update the panel according to current widget set: +void load_panel() { + if (!the_panel) return; + + // find all the Fl_Widget subclasses currently selected: + numselected = 0; + current_widget = nullptr; + if (Fluid.proj.tree.current) { + if (Fluid.proj.tree.current->is_widget()) + current_widget=(Widget_Node*)Fluid.proj.tree.current; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->is_widget() && o->selected) { + numselected++; + if (!current_widget) current_widget = (Widget_Node*)o; + } + } + } + if (current_widget && current_widget->is_a(Type::Grid)) { + if (widget_tab_grid->parent()!=widget_tabs) + widget_tabs->add(widget_tab_grid); + } else { + if (widget_tab_grid->parent()==widget_tabs) { + widget_tabs_repo->add(widget_tab_grid); + } + } + if (current_widget && current_widget->parent && current_widget->parent->is_a(Type::Grid)) { + if (widget_tab_grid_child->parent()!=widget_tabs) + widget_tabs->add(widget_tab_grid_child); + } else { + if (widget_tab_grid_child->parent()==widget_tabs) { + widget_tabs_repo->add(widget_tab_grid_child); + } + } + if (numselected) + propagate_load(the_panel, LOAD); + else + the_panel->hide(); +} + +extern Fl_Window *widgetbin_panel; + +// This is called when user double-clicks an item, open or update the panel: +void Widget_Node::open() { + bool adjust_position = false; + if (!the_panel) { + the_panel = make_widget_panel(); + adjust_position = true; + } + load_panel(); + if (numselected) { + the_panel->show(); + if (adjust_position) { + if (widgetbin_panel && widgetbin_panel->visible()) { + if ( (the_panel->x()+the_panel->w() > widgetbin_panel->x()) + && (the_panel->x() < widgetbin_panel->x()+widgetbin_panel->w()) + && (the_panel->y()+the_panel->h() > widgetbin_panel->y()) + && (the_panel->y() < widgetbin_panel->y()+widgetbin_panel->h()) ) + { + if (widgetbin_panel->y()+widgetbin_panel->h()+the_panel->h() > Fl::h()) + the_panel->position(the_panel->x(), widgetbin_panel->y()-the_panel->h()-30); + else + the_panel->position(the_panel->x(), widgetbin_panel->y()+widgetbin_panel->h()+30); + } + } + } + } +} + +extern void redraw_overlays(); +extern void check_redraw_corresponding_parent(Node*); +extern void redraw_browser(); +extern void update_codeview_position(); + +// Called when ui changes what objects are selected: +// p is selected object, null for all deletions (we must throw away +// old panel in that case, as the object may no longer exist) +void selection_changed(Node *p) { + // store all changes to the current selected objects: + if (p && the_panel && the_panel->visible()) { + set_cb(nullptr,nullptr); + // if there was an error, we try to leave the selected set unchanged: + if (haderror) { + Node *q = nullptr; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + o->new_selected = o->selected; + if (!q && o->selected) q = o; + } + if (!p || !p->selected) p = q; + Fluid.proj.tree.current = p; + redraw_browser(); + return; + } + } + // update the selected flags to new set: + Node *q = nullptr; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + o->selected = o->new_selected; + if (!q && o->selected) q = o; + } + if (!p || !p->selected) p = q; + Fluid.proj.tree.current = p; + check_redraw_corresponding_parent(p); + redraw_overlays(); + // load the panel with the new settings: + load_panel(); + // update the code viewer to show the code for the selected object + update_codeview_position(); +} + +//////////////////////////////////////////////////////////////// +// Writing the C code: + +// test to see if user named a function, or typed in code: +int is_name(const char *c) { + for (; *c; c++) + if ((ispunct(*c)||*c=='\n') && *c!='_' && *c!=':') return 0; + return 1; +} + +// Test to see if name() is an array entry. If so, and this is the +// highest number, return name[num+1]. Return null if not the highest +// number or a field or function. Return name() if not an array entry. +const char *array_name(Widget_Node *o) { + const char *c = o->name(); + if (!c) return nullptr; + const char *d; + for (d = c; *d != '['; d++) { + if (!*d) return c; + if (ispunct(*d) && *d!='_') return nullptr; + } + int num = atoi(d+1); + int sawthis = 0; + Node *t = o->prev; + Node *tp = o; + const char *cn = o->class_name(1); + for (; t && t->class_name(1) == cn; tp = t, t = t->prev) {/*empty*/} + for (t = tp; t && t->class_name(1) == cn; t = t->next) { + if (t == o) {sawthis=1; continue;} + const char *e = t->name(); + if (!e) continue; + if (strncmp(c,e,d-c)) continue; + int n1 = atoi(e+(d-c)+1); + if (n1 > num || (n1==num && sawthis)) return nullptr; + } + static char buffer[128]; + // MRS: we want strncpy() here... + strncpy(buffer,c,d-c+1); + snprintf(buffer+(d-c+1),sizeof(buffer) - (d-c+1), "%d]",num+1); + return buffer; +} + +// Test to see if extra code is a declaration: +int isdeclare(const char *c) { + while (isspace(*c)) c++; + if (*c == '#') return 1; + if (!strncmp(c,"extern",6)) return 1; + if (!strncmp(c,"typedef",7)) return 1; + if (!strncmp(c,"using",5)) return 1; + return 0; +} + +void Widget_Node::write_static(fld::io::Code_Writer& f) { + const char* t = subclassname(this); + if (!subclass() || (is_class() && !strncmp(t, "Fl_", 3))) { + f.write_h_once("#include "); + f.write_h_once("#include ", t); + } + for (int n=0; n < NUM_EXTRA_CODE; n++) { + if (extra_code(n) && isdeclare(extra_code(n))) + f.write_h_once("%s", extra_code(n)); + } + if (callback() && is_name(callback())) { + int write_extern_declaration = 1; + char buf[1024]; snprintf(buf, 1023, "%s(*)", callback()); + if (is_in_class()) { + if (has_function("static void", buf)) + write_extern_declaration = 0; + } else { + if (has_toplevel_function(nullptr, buf)) + write_extern_declaration = 0; + } + if (write_extern_declaration) + f.write_h_once("extern void %s(%s*, %s);", callback(), t, + user_data_type() ? user_data_type() : "void*"); + } + const char* k = class_name(1); + const char* c = array_name(this); + if (c && !k && !is_class()) { + f.write_c("\n"); + if (!public_) f.write_c("static "); + else f.write_h("extern %s *%s;\n", t, c); + if (strchr(c, '[') == nullptr) f.write_c("%s *%s=(%s *)0;\n", t, c, t); + else f.write_c("%s *%s={(%s *)0};\n", t, c, t); + } + if (callback() && !is_name(callback())) { + // see if 'o' or 'v' used, to prevent unused argument warnings: + int use_o = 0; + int use_v = 0; + const char *d; + for (d = callback(); *d;) { + if (*d == 'o' && !is_id(d[1])) use_o = 1; + if (*d == 'v' && !is_id(d[1])) use_v = 1; + do d++; while (is_id(*d)); + while (*d && !is_id(*d)) d++; + } + const char* cn = callback_name(f); + if (k) { + f.write_c("\nvoid %s::%s_i(%s*", k, cn, t); + } else { + f.write_c("\nstatic void %s(%s*", cn, t); + } + if (use_o) f.write_c(" o"); + const char* ut = user_data_type() ? user_data_type() : "void*"; + f.write_c(", %s", ut); + if (use_v) f.write_c(" v"); + f.write_c(") {\n"); + // Matt: disabled f.tag(FD_TAG_GENERIC, 0); + f.write_c_indented(callback(), 1, 0); + if (*(d-1) != ';' && *(d-1) != '}') { + const char *p = strrchr(callback(), '\n'); + if (p) p ++; + else p = callback(); + // Only add trailing semicolon if the last line is not a preprocessor + // statement... + if (*p != '#' && *p) f.write_c(";"); + } + f.write_c("\n"); + // Matt: disabled f.tag(FD_TAG_WIDGET_CALLBACK, get_uid()); + f.write_c("}\n"); + if (k) { + f.write_c("void %s::%s(%s* o, %s v) {\n", k, cn, t, ut); + f.write_c("%s((%s*)(o", f.indent(1), k); + Node *q = nullptr; + for (Node* p = parent; p && p->is_widget(); q = p, p = p->parent) + f.write_c("->parent()"); + if (!q || !q->is_a(Type::Widget_Class)) + f.write_c("->user_data()"); + f.write_c("))->%s_i(o,v);\n}\n", cn); + } + } + if (image) { + if (!f.c_contains(image)) + image->write_static(f, compress_image_); + } + if (inactive) { + if (!f.c_contains(inactive)) + inactive->write_static(f, compress_deimage_); + } +} + +void Widget_Node::write_code1(fld::io::Code_Writer& f) { + const char* t = subclassname(this); + const char *c = array_name(this); + if (c) { + if (class_name(1)) { + f.write_public(public_); + f.write_h("%s%s *%s;\n", f.indent(1), t, c); + } + } + if (class_name(1) && callback() && !is_name(callback())) { + const char* cn = callback_name(f); + const char* ut = user_data_type() ? user_data_type() : "void*"; + f.write_public(0); + f.write_h("%sinline void %s_i(%s*, %s);\n", f.indent(1), cn, t, ut); + f.write_h("%sstatic void %s(%s*, %s);\n", f.indent(1), cn, t, ut); + } + // figure out if local variable will be used (prevent compiler warnings): + int wused = !name() && is_a(Type::Window); + const char *ptr; + + f.varused = wused; + + if (!name() && !f.varused) { + f.varused |= can_have_children(); + + if (!f.varused) { + f.varused_test = 1; + write_widget_code(f); + f.varused_test = 0; + } + } + + if (!f.varused) { + for (int n=0; n < NUM_EXTRA_CODE; n++) + if (extra_code(n) && !isdeclare(extra_code(n))) + { + int instring = 0; + int inname = 0; + int incomment = 0; + int incppcomment = 0; + for (ptr = extra_code(n); *ptr; ptr ++) { + if (instring) { + if (*ptr == '\\') ptr++; + else if (*ptr == '\"') instring = 0; + } else if (inname && !isalnum(*ptr & 255)) { + inname = 0; + } else if (*ptr == '/' && ptr[1]=='*') { + incomment = 1; ptr++; + } else if (incomment) { + if (*ptr == '*' && ptr[1]=='/') { + incomment = 0; ptr++; + } + } else if (*ptr == '/' && ptr[1]=='/') { + incppcomment = 1; ptr++; + } else if (incppcomment) { + if (*ptr == '\n') + incppcomment = 0; + } else if (*ptr == '\"') { + instring = 1; + } else if (isalnum(*ptr & 255) || *ptr == '_') { + size_t len = strspn(ptr, "0123456789_" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + if (!strncmp(ptr, "o", len)) { + f.varused = 1; + break; + } else { + ptr += len - 1; + } + } + } + } + } + + f.write_c("%s{ ", f.indent()); + write_comment_inline_c(f); + if (f.varused) f.write_c("%s* o = ", t); + if (name()) f.write_c("%s = ", name()); + if (is_a(Type::Window)) { + // Handle special case where user is faking a Fl_Group type as a window, + // there is no 2-argument constructor in that case: + if (!strstr(t, "Window")) + f.write_c("new %s(0, 0, %d, %d", t, o->w(), o->h()); + else + f.write_c("new %s(%d, %d", t, o->w(), o->h()); + } else if (is_a(Type::Menu_Bar) + && ((Menu_Bar_Node*)this)->is_sys_menu_bar() + && is_in_class()) { + f.write_c("(%s*)new %s(%d, %d, %d, %d", + t, ((Menu_Bar_Node*)this)->sys_menubar_proxy_name(), + o->x(), o->y(), o->w(), o->h()); + } else { + f.write_c("new %s(%d, %d, %d, %d", t, o->x(), o->y(), o->w(), o->h()); + } + if (label() && *label()) { + f.write_c(", "); + switch (Fluid.proj.i18n_type) { + case fld::I18n_Type::NONE : /* None */ + f.write_cstring(label()); + break; + case fld::I18n_Type::GNU : /* GNU gettext */ + f.write_c("%s(", Fluid.proj.i18n_gnu_function.c_str()); + f.write_cstring(label()); + f.write_c(")"); + break; + case fld::I18n_Type::POSIX : /* POSIX catgets */ + f.write_c("catgets(%s,%s,%d,", + Fluid.proj.i18n_pos_file.empty() ? "_catalog" : Fluid.proj.i18n_pos_file.c_str(), + Fluid.proj.i18n_pos_set.c_str(), msgnum()); + f.write_cstring(label()); + f.write_c(")"); + break; + } + } + f.write_c(");\n"); + + f.indentation++; + + // Avoid compiler warning for unused variable. + // Also avoid quality control warnings about incorrect allocation error handling. + if (wused) f.write_c("%sw = o; (void)w;\n", f.indent()); + + write_widget_code(f); +} + +void Widget_Node::write_color(fld::io::Code_Writer& f, const char* field, Fl_Color color) { + const char* color_name = nullptr; + switch (color) { + case FL_FOREGROUND_COLOR: color_name = "FL_FOREGROUND_COLOR"; break; + case FL_BACKGROUND2_COLOR: color_name = "FL_BACKGROUND2_COLOR"; break; + case FL_INACTIVE_COLOR: color_name = "FL_INACTIVE_COLOR"; break; + case FL_SELECTION_COLOR: color_name = "FL_SELECTION_COLOR"; break; + case FL_GRAY0: color_name = "FL_GRAY0"; break; + case FL_DARK3: color_name = "FL_DARK3"; break; + case FL_DARK2: color_name = "FL_DARK2"; break; + case FL_DARK1: color_name = "FL_DARK1"; break; + case FL_BACKGROUND_COLOR: color_name = "FL_BACKGROUND_COLOR"; break; + case FL_LIGHT1: color_name = "FL_LIGHT1"; break; + case FL_LIGHT2: color_name = "FL_LIGHT2"; break; + case FL_LIGHT3: color_name = "FL_LIGHT3"; break; + case FL_BLACK: color_name = "FL_BLACK"; break; + case FL_RED: color_name = "FL_RED"; break; + case FL_GREEN: color_name = "FL_GREEN"; break; + case FL_YELLOW: color_name = "FL_YELLOW"; break; + case FL_BLUE: color_name = "FL_BLUE"; break; + case FL_MAGENTA: color_name = "FL_MAGENTA"; break; + case FL_CYAN: color_name = "FL_CYAN"; break; + case FL_DARK_RED: color_name = "FL_DARK_RED"; break; + case FL_DARK_GREEN: color_name = "FL_DARK_GREEN"; break; + case FL_DARK_YELLOW: color_name = "FL_DARK_YELLOW"; break; + case FL_DARK_BLUE: color_name = "FL_DARK_BLUE"; break; + case FL_DARK_MAGENTA: color_name = "FL_DARK_MAGENTA"; break; + case FL_DARK_CYAN: color_name = "FL_DARK_CYAN"; break; + case FL_WHITE: color_name = "FL_WHITE"; break; + } + const char *var = is_class() ? "this" : name() ? name() : "o"; + if (color_name) { + f.write_c("%s%s->%s(%s);\n", f.indent(), var, field, color_name); + } else { + f.write_c("%s%s->%s((Fl_Color)%d);\n", f.indent(), var, field, color); + } +} + +// this is split from write_code1(fld::io::Code_Writer& f) for Window_Node: +void Widget_Node::write_widget_code(fld::io::Code_Writer& f) { + Fl_Widget* tplate = ((Widget_Node*)factory)->o; + const char *var = is_class() ? "this" : name() ? name() : "o"; + + if (tooltip() && *tooltip()) { + f.write_c("%s%s->tooltip(",f.indent(), var); + switch (Fluid.proj.i18n_type) { + case fld::I18n_Type::NONE : /* None */ + f.write_cstring(tooltip()); + break; + case fld::I18n_Type::GNU : /* GNU gettext */ + f.write_c("%s(", Fluid.proj.i18n_gnu_function.c_str()); + f.write_cstring(tooltip()); + f.write_c(")"); + break; + case fld::I18n_Type::POSIX : /* POSIX catgets */ + f.write_c("catgets(%s,%s,%d,", + Fluid.proj.i18n_pos_file.empty() ? "_catalog" : Fluid.proj.i18n_pos_file.c_str(), + Fluid.proj.i18n_pos_set.c_str(), + msgnum() + 1); + f.write_cstring(tooltip()); + f.write_c(")"); + break; + } + f.write_c(");\n"); + } + + if (is_a(Type::Spinner) && ((Fl_Spinner*)o)->type() != ((Fl_Spinner*)tplate)->type()) + f.write_c("%s%s->type(%d);\n", f.indent(), var, ((Fl_Spinner*)o)->type()); + else if (o->type() != tplate->type() && !is_a(Type::Window)) + f.write_c("%s%s->type(%d);\n", f.indent(), var, o->type()); + if (o->box() != tplate->box() || subclass()) + f.write_c("%s%s->box(FL_%s);\n", f.indent(), var, boxname(o->box())); + + // write shortcut command if needed + int shortcut = 0; + if (is_button()) shortcut = ((Fl_Button*)o)->shortcut(); + else if (is_a(Type::Input)) shortcut = ((Fl_Input_*)o)->shortcut(); + else if (is_a(Type::Value_Input)) shortcut = ((Fl_Value_Input*)o)->shortcut(); + else if (is_a(Type::Text_Display)) shortcut = ((Fl_Text_Display*)o)->shortcut(); + if (shortcut) { + int s = shortcut; + f.write_c("%s%s->shortcut(", f.indent(), var); + if (Fluid.proj.use_FL_COMMAND) { + if (s & FL_CTRL) { f.write_c("FL_CONTROL|"); s &= ~FL_CTRL; } + if (s & FL_META) { f.write_c("FL_COMMAND|"); s &= ~FL_META; } + } else { + if (s & FL_CTRL) { f.write_c("FL_CTRL|"); s &= ~FL_CTRL; } + if (s & FL_META) { f.write_c("FL_META|"); s &= ~FL_META; } + } + if (s & FL_SHIFT) { f.write_c("FL_SHIFT|"); s &= ~FL_SHIFT; } + if (s & FL_ALT) { f.write_c("FL_ALT|"); s &= ~FL_ALT; } + if ((s < 127) && isprint(s)) + f.write_c("'%c');\n", s); + else + f.write_c("0x%x);\n", s); + } + + if (is_a(Type::Button)) { + Fl_Button* b = (Fl_Button*)o; + if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var, + boxname(b->down_box())); + if (b->value()) f.write_c("%s%s->value(1);\n", f.indent(), var); + if (b->compact()) f.write_c("%s%s->compact(%d);\n", f.indent(), var, b->compact()); + } else if (is_a(Type::Input_Choice)) { + Fl_Input_Choice* b = (Fl_Input_Choice*)o; + if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var, + boxname(b->down_box())); + } else if (is_a(Type::Menu_Manager_)) { + Fl_Menu_* b = (Fl_Menu_*)o; + if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var, + boxname(b->down_box())); + } + if (o->color() != tplate->color() || subclass()) + write_color(f, "color", o->color()); + if (o->selection_color() != tplate->selection_color() || subclass()) + write_color(f, "selection_color", o->selection_color()); + if (image) { + image->write_code(f, bind_image_, var); + if (scale_image_w_ || scale_image_h_) { + f.write_c("%s%s->image()->scale(", f.indent(), var); + if (scale_image_w_>0) + f.write_c("%d, ", scale_image_w_); + else + f.write_c("%s->image()->data_w(), ", var); + if (scale_image_h_>0) + f.write_c("%d, 0, 1);\n", scale_image_h_); + else + f.write_c("%s->image()->data_h(), 0, 1);\n", var); + } + } + if (inactive) { + inactive->write_code(f, bind_deimage_, var, 1); + if (scale_deimage_w_ || scale_deimage_h_) { + f.write_c("%s%s->deimage()->scale(", f.indent(), var); + if (scale_deimage_w_>0) + f.write_c("%d, ", scale_deimage_w_); + else + f.write_c("%s->deimage()->data_w(), ", var); + if (scale_deimage_h_>0) + f.write_c("%d, 0, 1);\n", scale_deimage_h_); + else + f.write_c("%s->deimage()->data_h(), 0, 1);\n", var); + } + } + if (o->labeltype() != tplate->labeltype() || subclass()) + f.write_c("%s%s->labeltype(FL_%s);\n", f.indent(), var, + item_name(labeltypemenu, o->labeltype())); + if (o->labelfont() != tplate->labelfont() || subclass()) + f.write_c("%s%s->labelfont(%d);\n", f.indent(), var, o->labelfont()); + if (o->labelsize() != tplate->labelsize() || subclass()) + f.write_c("%s%s->labelsize(%d);\n", f.indent(), var, o->labelsize()); + if (o->labelcolor() != tplate->labelcolor() || subclass()) + write_color(f, "labelcolor", o->labelcolor()); + if (o->horizontal_label_margin() != tplate->horizontal_label_margin()) + f.write_c("%s%s->horizontal_label_margin(%d);\n", f.indent(), var, o->horizontal_label_margin()); + if (o->vertical_label_margin() != tplate->vertical_label_margin()) + f.write_c("%s%s->vertical_label_margin(%d);\n", f.indent(), var, o->vertical_label_margin()); + if (o->label_image_spacing() != tplate->label_image_spacing()) + f.write_c("%s%s->label_image_spacing(%d);\n", f.indent(), var, o->label_image_spacing()); + if (is_a(Type::Valuator_)) { + Fl_Valuator* v = (Fl_Valuator*)o; + Fl_Valuator* t = (Fl_Valuator*)(tplate); + if (v->minimum()!=t->minimum()) + f.write_c("%s%s->minimum(%g);\n", f.indent(), var, v->minimum()); + if (v->maximum()!=t->maximum()) + f.write_c("%s%s->maximum(%g);\n", f.indent(), var, v->maximum()); + if (v->step()!=t->step()) + f.write_c("%s%s->step(%g);\n", f.indent(), var, v->step()); + if (v->value()) { + if (is_a(Type::Scrollbar)) { // Fl_Scrollbar::value(double) is not available + f.write_c("%s%s->Fl_Slider::value(%g);\n", f.indent(), var, v->value()); + } else { + f.write_c("%s%s->value(%g);\n", f.indent(), var, v->value()); + } + } + if (is_a(Type::Slider)) { + double x = ((Fl_Slider*)v)->slider_size(); + double y = ((Fl_Slider*)t)->slider_size(); + if (x != y) f.write_c("%s%s->slider_size(%g);\n", f.indent(), var, x); + } + } + if (is_a(Type::Spinner)) { + Fl_Spinner* v = (Fl_Spinner*)o; + Fl_Spinner* t = (Fl_Spinner*)(tplate); + if (v->minimum()!=t->minimum()) + f.write_c("%s%s->minimum(%g);\n", f.indent(), var, v->minimum()); + if (v->maximum()!=t->maximum()) + f.write_c("%s%s->maximum(%g);\n", f.indent(), var, v->maximum()); + if (v->step()!=t->step()) + f.write_c("%s%s->step(%g);\n", f.indent(), var, v->step()); + if (v->value()!=1.0f) + f.write_c("%s%s->value(%g);\n", f.indent(), var, v->value()); + } + + {Fl_Font ff; int fs; Fl_Color fc; if (textstuff(4,ff,fs,fc)) { + Fl_Font g; int s; Fl_Color c; textstuff(0,g,s,c); + if (g != ff) f.write_c("%s%s->textfont(%d);\n", f.indent(), var, g); + if (s != fs) f.write_c("%s%s->textsize(%d);\n", f.indent(), var, s); + if (c != fc) write_color(f, "textcolor", c); + }} + const char* ud = user_data(); + if (class_name(1) && !parent->is_widget()) ud = "this"; + if (callback()) { + f.write_c("%s%s->callback((Fl_Callback*)%s", f.indent(), var, callback_name(f)); + if (ud) + f.write_c(", (void*)(%s));\n", ud); + else + f.write_c(");\n"); + } else if (ud) { + f.write_c("%s%s->user_data((void*)(%s));\n", f.indent(), var, ud); + } + if (o->align() != tplate->align() || subclass()) { + int i = o->align(); + f.write_c("%s%s->align(Fl_Align(%s", f.indent(), var, + item_name(alignmenu, i & ~FL_ALIGN_INSIDE)); + if (i & FL_ALIGN_INSIDE) f.write_c("|FL_ALIGN_INSIDE"); + f.write_c("));\n"); + } + Fl_When ww = o->when(); + if (ww != tplate->when() || subclass()) + f.write_c("%s%s->when(%s);\n", f.indent(), var, when_symbol_name(ww)); + if (!o->visible() && o->parent()) + f.write_c("%s%s->hide();\n", f.indent(), var); + if (!o->active()) + f.write_c("%s%s->deactivate();\n", f.indent(), var); + if (!is_a(Type::Group) && resizable()) + f.write_c("%sFl_Group::current()->resizable(%s);\n", f.indent(), var); + if (hotspot()) { + if (is_class()) + f.write_c("%shotspot(%s);\n", f.indent(), var); + else if (is_a(Type::Window)) + f.write_c("%s%s->hotspot(%s);\n", f.indent(), var, var); + else + f.write_c("%s%s->window()->hotspot(%s);\n", f.indent(), var, var); + } +} + +void Widget_Node::write_extra_code(fld::io::Code_Writer& f) { + for (int n=0; n < NUM_EXTRA_CODE; n++) + if (extra_code(n) && !isdeclare(extra_code(n))) + f.write_c("%s%s\n", f.indent(), extra_code(n)); +} + +void Widget_Node::write_block_close(fld::io::Code_Writer& f) { + f.indentation--; + f.write_c("%s} // %s* %s\n", f.indent(), subclassname(this), + name() ? name() : "o"); +} + +void Widget_Node::write_code2(fld::io::Code_Writer& f) { + write_extra_code(f); + write_block_close(f); +} + +//////////////////////////////////////////////////////////////// + +void Widget_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + f.write_indent(level+1); + switch (public_) { + case 0: f.write_string("private"); break; + case 1: break; + case 2: f.write_string("protected"); break; + } + if (tooltip() && *tooltip()) { + f.write_string("tooltip"); + f.write_word(tooltip()); + } + if (image_name() && *image_name()) { + if (scale_image_w_ || scale_image_h_) + f.write_string("scale_image {%d %d}", scale_image_w_, scale_image_h_); + f.write_string("image"); + f.write_word(image_name()); + f.write_string("compress_image %d", compress_image_); + } + if (bind_image_) f.write_string("bind_image 1"); + if (inactive_name() && *inactive_name()) { + if (scale_deimage_w_ || scale_deimage_h_) + f.write_string("scale_deimage {%d %d}", scale_deimage_w_, scale_deimage_h_); + f.write_string("deimage"); + f.write_word(inactive_name()); + f.write_string("compress_deimage %d", compress_deimage_); + } + if (bind_deimage_) f.write_string("bind_deimage 1"); + f.write_string("xywh {%d %d %d %d}", o->x(), o->y(), o->w(), o->h()); + Fl_Widget* tplate = ((Widget_Node*)factory)->o; + if (is_a(Type::Spinner) && ((Fl_Spinner*)o)->type() != ((Fl_Spinner*)tplate)->type()) { + f.write_string("type"); + f.write_word(item_name(subtypes(), ((Fl_Spinner*)o)->type())); + } else if (subtypes() && (o->type() != tplate->type() || is_a(Type::Window))) { + f.write_string("type"); + f.write_word(item_name(subtypes(), o->type())); + } + if (o->box() != tplate->box()) { + f.write_string("box"); f.write_word(boxname(o->box()));} + if (is_a(Type::Input)) { + Fl_Input_* b = (Fl_Input_*)o; + if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); + } + if (is_a(Type::Value_Input)) { + Fl_Value_Input* b = (Fl_Value_Input*)o; + if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); + } + if (is_a(Type::Text_Display)) { + Fl_Text_Display* b = (Fl_Text_Display*)o; + if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); + } + if (is_a(Type::Button)) { + Fl_Button* b = (Fl_Button*)o; + if (b->down_box()) { + f.write_string("down_box"); f.write_word(boxname(b->down_box()));} + if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); + if (b->value()) f.write_string("value 1"); + } else if (is_a(Type::Input_Choice)) { + Fl_Input_Choice* b = (Fl_Input_Choice*)o; + if (b->down_box()) { + f.write_string("down_box"); f.write_word(boxname(b->down_box()));} + } else if (is_a(Type::Menu_)) { + Fl_Menu_* b = (Fl_Menu_*)o; + if (b->down_box()) { + f.write_string("down_box"); f.write_word(boxname(b->down_box()));} + } + if (o->color()!=tplate->color()) + f.write_string("color %d", o->color()); + if (o->selection_color()!=tplate->selection_color()) + f.write_string("selection_color %d", o->selection_color()); + if (o->labeltype()!=tplate->labeltype()) { + f.write_string("labeltype"); + f.write_word(item_name(labeltypemenu, o->labeltype())); + } + if (o->labelfont()!=tplate->labelfont()) + f.write_string("labelfont %d", o->labelfont()); + if (o->labelsize()!=tplate->labelsize()) + f.write_string("labelsize %d", o->labelsize()); + if (o->labelcolor()!=tplate->labelcolor()) + f.write_string("labelcolor %d", o->labelcolor()); + if (o->align()!=tplate->align()) + f.write_string("align %d", o->align()); + if (o->horizontal_label_margin()!=tplate->horizontal_label_margin()) + f.write_string("h_label_margin %d", o->horizontal_label_margin()); + if (o->vertical_label_margin()!=tplate->vertical_label_margin()) + f.write_string("v_label_margin %d", o->vertical_label_margin()); + if (o->label_image_spacing()!=tplate->label_image_spacing()) + f.write_string("image_spacing %d", o->label_image_spacing()); + if (o->when() != tplate->when()) + f.write_string("when %d", o->when()); + if (is_a(Type::Valuator_)) { + Fl_Valuator* v = (Fl_Valuator*)o; + Fl_Valuator* t = (Fl_Valuator*)(tplate); + if (v->minimum()!=t->minimum()) f.write_string("minimum %g",v->minimum()); + if (v->maximum()!=t->maximum()) f.write_string("maximum %g",v->maximum()); + if (v->step()!=t->step()) f.write_string("step %g",v->step()); + if (v->value()!=0.0) f.write_string("value %g",v->value()); + if (is_a(Type::Slider)) { + double x = ((Fl_Slider*)v)->slider_size(); + double y = ((Fl_Slider*)t)->slider_size(); + if (x != y) f.write_string("slider_size %g", x); + } + } + if (is_a(Type::Spinner)) { + Fl_Spinner* v = (Fl_Spinner*)o; + Fl_Spinner* t = (Fl_Spinner*)(tplate); + if (v->minimum()!=t->minimum()) f.write_string("minimum %g",v->minimum()); + if (v->maximum()!=t->maximum()) f.write_string("maximum %g",v->maximum()); + if (v->step()!=t->step()) f.write_string("step %g",v->step()); + if (v->value()!=1.0) f.write_string("value %g",v->value()); + } + {Fl_Font ff; int fs; Fl_Color fc; if (textstuff(4,ff,fs,fc)) { + Fl_Font ft; int s; Fl_Color c; textstuff(0,ft,s,c); + if (ft != ff) f.write_string("textfont %d", ft); + if (s != fs) f.write_string("textsize %d", s); + if (c != fc) f.write_string("textcolor %d", c); + }} + if (!o->visible() && !override_visible_) f.write_string("hide"); + if (!o->active()) f.write_string("deactivate"); + if (resizable()) f.write_string("resizable"); + if (hotspot()) f.write_string(is_a(Type::Menu_Item) ? "divider" : "hotspot"); + for (int n=0; n < NUM_EXTRA_CODE; n++) if (extra_code(n)) { + f.write_indent(level+1); + f.write_string("code%d",n); + f.write_word(extra_code(n)); + } + if (subclass()) { + f.write_indent(level+1); + f.write_string("class"); + f.write_word(subclass()); + } +} + +void Widget_Node::read_property(fld::io::Project_Reader &f, const char *c) { + int x,y,w,h; Fl_Font ft; int s; Fl_Color cc; + if (!strcmp(c,"private")) { + public_ = 0; + } else if (!strcmp(c,"protected")) { + public_ = 2; + } else if (!strcmp(c,"xywh")) { + if (sscanf(f.read_word(),"%d %d %d %d",&x,&y,&w,&h) == 4) { + x += Fluid.pasteoffset; + y += Fluid.pasteoffset; + // FIXME temporary change! + if (f.read_version>=2.0 && o->parent() && o->parent()!=o->window()) { + x += o->parent()->x(); + y += o->parent()->y(); + } + o->resize(x,y,w,h); + } + } else if (!strcmp(c,"tooltip")) { + tooltip(f.read_word()); + } else if (!strcmp(c,"scale_image")) { + if (sscanf(f.read_word(),"%d %d",&w,&h) == 2) { + scale_image_w_ = w; + scale_image_h_ = h; + } + } else if (!strcmp(c,"image")) { + image_name(f.read_word()); + // starting in 2023, `image` is always followed by `compress_image` + // the code below is for compatibility with older .fl files + const char *ext = fl_filename_ext(image_name_); + if ( strcmp(ext, ".jpg") + && strcmp(ext, ".png") + && strcmp(ext, ".svg") + && strcmp(ext, ".svgz")) + compress_image_ = 0; // if it is neither of those, default to uncompressed + } else if (!strcmp(c,"bind_image")) { + bind_image_ = (int)atol(f.read_word()); + } else if (!strcmp(c,"compress_image")) { + compress_image_ = (int)atol(f.read_word()); + } else if (!strcmp(c,"scale_deimage")) { + if (sscanf(f.read_word(),"%d %d",&w,&h) == 2) { + scale_deimage_w_ = w; + scale_deimage_h_ = h; + } + } else if (!strcmp(c,"deimage")) { + inactive_name(f.read_word()); + // starting in 2023, `deimage` is always followed by `compress_deimage` + // the code below is for compatibility with older .fl files + const char *ext = fl_filename_ext(inactive_name_); + if ( strcmp(ext, ".jpg") + && strcmp(ext, ".png") + && strcmp(ext, ".svg") + && strcmp(ext, ".svgz")) + compress_deimage_ = 0; // if it is neither of those, default to uncompressed + } else if (!strcmp(c,"bind_deimage")) { + bind_deimage_ = (int)atol(f.read_word()); + } else if (!strcmp(c,"compress_deimage")) { + compress_deimage_ = (int)atol(f.read_word()); + } else if (!strcmp(c,"type")) { + if (is_a(Type::Spinner)) + ((Fl_Spinner*)o)->type(item_number(subtypes(), f.read_word())); + else + o->type(item_number(subtypes(), f.read_word())); + } else if (!strcmp(c,"box")) { + const char* value = f.read_word(); + if ((x = boxnumber(value))) { + if (x == ZERO_ENTRY) x = 0; + o->box((Fl_Boxtype)x); + } else if (sscanf(value,"%d",&x) == 1) o->box((Fl_Boxtype)x); + } else if (is_a(Type::Button) && !strcmp(c,"down_box")) { + const char* value = f.read_word(); + if ((x = boxnumber(value))) { + if (x == ZERO_ENTRY) x = 0; + ((Fl_Button*)o)->down_box((Fl_Boxtype)x); + } + } else if (is_a(Type::Input_Choice) && !strcmp(c,"down_box")) { + const char* value = f.read_word(); + if ((x = boxnumber(value))) { + if (x == ZERO_ENTRY) x = 0; + ((Fl_Input_Choice*)o)->down_box((Fl_Boxtype)x); + } + } else if (is_a(Type::Menu_) && !strcmp(c,"down_box")) { + const char* value = f.read_word(); + if ((x = boxnumber(value))) { + if (x == ZERO_ENTRY) x = 0; + ((Fl_Menu_*)o)->down_box((Fl_Boxtype)x); + } + } else if (is_button() && !strcmp(c,"value")) { + const char* value = f.read_word(); + ((Fl_Button*)o)->value(atoi(value)); + } else if (!strcmp(c,"color")) { + const char *cw = f.read_word(); + if (cw[0]=='0' && cw[1]=='x') { + sscanf(cw,"0x%x",&x); + o->color(x); + } else { + int n = sscanf(cw,"%d %d",&x,&y); + if (n == 2) { // back compatibility... + if (x != 47) o->color(x); + o->selection_color(y); + } else { + o->color(x); + } + } + } else if (!strcmp(c,"selection_color")) { + if (sscanf(f.read_word(),"%d",&x)) o->selection_color(x); + } else if (!strcmp(c,"labeltype")) { + c = f.read_word(); + if (!strcmp(c,"image")) { + Image_Asset *i = Image_Asset::find(label()); + if (!i) f.read_error("Image file '%s' not found", label()); + else setimage(i); + image_name(label()); + label(""); + } else { + o->labeltype((Fl_Labeltype)item_number(labeltypemenu,c)); + } + } else if (!strcmp(c,"labelfont")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->labelfont(x); + } else if (!strcmp(c,"labelsize")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->labelsize(x); + } else if (!strcmp(c,"labelcolor")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->labelcolor(x); + } else if (!strcmp(c,"align")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->align(x); + } else if (!strcmp(c,"h_label_margin")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->horizontal_label_margin(x); + } else if (!strcmp(c,"v_label_margin")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->vertical_label_margin(x); + } else if (!strcmp(c,"image_spacing")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->label_image_spacing(x); + } else if (!strcmp(c,"when")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->when(x); + } else if (!strcmp(c,"minimum")) { + if (is_a(Type::Valuator_)) ((Fl_Valuator*)o)->minimum(strtod(f.read_word(),nullptr)); + if (is_a(Type::Spinner)) ((Fl_Spinner*)o)->minimum(strtod(f.read_word(),nullptr)); + } else if (!strcmp(c,"maximum")) { + if (is_a(Type::Valuator_)) ((Fl_Valuator*)o)->maximum(strtod(f.read_word(),nullptr)); + if (is_a(Type::Spinner)) ((Fl_Spinner*)o)->maximum(strtod(f.read_word(),nullptr)); + } else if (!strcmp(c,"step")) { + if (is_a(Type::Valuator_)) ((Fl_Valuator*)o)->step(strtod(f.read_word(),nullptr)); + if (is_a(Type::Spinner)) ((Fl_Spinner*)o)->step(strtod(f.read_word(),nullptr)); + } else if (!strcmp(c,"value")) { + if (is_a(Type::Valuator_)) ((Fl_Valuator*)o)->value(strtod(f.read_word(),nullptr)); + if (is_a(Type::Spinner)) ((Fl_Spinner*)o)->value(strtod(f.read_word(),nullptr)); + } else if ( (!strcmp(c,"slider_size") || !strcmp(c,"size")) && is_a(Type::Slider)) { + ((Fl_Slider*)o)->slider_size(strtod(f.read_word(),nullptr)); + } else if (!strcmp(c,"textfont")) { + if (sscanf(f.read_word(),"%d",&x) == 1) {ft=(Fl_Font)x; textstuff(1,ft,s,cc);} + } else if (!strcmp(c,"textsize")) { + if (sscanf(f.read_word(),"%d",&x) == 1) {s=x; textstuff(2,ft,s,cc);} + } else if (!strcmp(c,"textcolor")) { + if (sscanf(f.read_word(),"%d",&x) == 1) {cc=(Fl_Color)x;textstuff(3,ft,s,cc);} + } else if (!strcmp(c,"hide")) { + o->hide(); + } else if (!strcmp(c,"deactivate")) { + o->deactivate(); + } else if (!strcmp(c,"resizable")) { + resizable(1); + } else if (!strcmp(c,"hotspot") || !strcmp(c, "divider")) { + hotspot(1); + } else if (!strcmp(c,"class")) { + subclass(f.read_word()); + } else if (!strcmp(c,"shortcut")) { + int shortcut = (int)strtol(f.read_word(),nullptr,0); + if (is_button()) ((Fl_Button*)o)->shortcut(shortcut); + else if (is_a(Type::Input)) ((Fl_Input_*)o)->shortcut(shortcut); + else if (is_a(Type::Value_Input)) ((Fl_Value_Input*)o)->shortcut(shortcut); + else if (is_a(Type::Text_Display)) ((Fl_Text_Display*)o)->shortcut(shortcut); + } else { + if (!strncmp(c,"code",4)) { + int n = atoi(c+4); + if (n >= 0 && n <= NUM_EXTRA_CODE) { + extra_code(n,f.read_word()); + return; + } + } else if (!strcmp(c,"extra_code")) { + extra_code(0,f.read_word()); + return; + } + Node::read_property(f, c); + } +} + +Fl_Menu_Item boxmenu1[] = { + // these extra ones are for looking up fdesign saved strings: + {"NO_FRAME", 0,nullptr,(void *)FL_NO_BOX}, + {"ROUNDED3D_UPBOX", 0,nullptr,(void *)_FL_ROUND_UP_BOX}, + {"ROUNDED3D_DOWNBOX", 0,nullptr,(void *)_FL_ROUND_DOWN_BOX}, + {"OVAL3D_UPBOX", 0,nullptr,(void *)_FL_ROUND_UP_BOX}, + {"OVAL3D_DOWNBOX", 0,nullptr,(void *)_FL_ROUND_DOWN_BOX}, + {"0", 0,nullptr,(void *)ZERO_ENTRY}, + {"1", 0,nullptr,(void *)FL_UP_BOX}, + {"2", 0,nullptr,(void *)FL_DOWN_BOX}, + {"3", 0,nullptr,(void *)FL_FLAT_BOX}, + {"4", 0,nullptr,(void *)FL_BORDER_BOX}, + {"5", 0,nullptr,(void *)FL_SHADOW_BOX}, + {"6", 0,nullptr,(void *)FL_FRAME_BOX}, + {"7", 0,nullptr,(void *)FL_ROUNDED_BOX}, + {"8", 0,nullptr,(void *)FL_RFLAT_BOX}, + {"9", 0,nullptr,(void *)FL_RSHADOW_BOX}, + {"10", 0,nullptr,(void *)FL_UP_FRAME}, + {"11", 0,nullptr,(void *)FL_DOWN_FRAME}, + {nullptr}}; + +int lookup_symbol(const char *, int &, int numberok = 0); + +int Widget_Node::read_fdesign(const char* propname, const char* value) { + int v; + if (!strcmp(propname,"box")) { + float x,y,w,h; + if (sscanf(value,"%f %f %f %f",&x,&y,&w,&h) == 4) { + if (fld::io::fdesign_flip) { + Node *p; + for (p = parent; p && !p->is_a(Type::Window); p = p->parent) {/*empty*/} + if (p && p->is_widget()) y = ((Widget_Node*)p)->o->h()-(y+h); + } + x += Fluid.pasteoffset; + y += Fluid.pasteoffset; + o->resize(int(x),int(y),int(w),int(h)); + } + } else if (!strcmp(propname,"label")) { + label(value); + } else if (!strcmp(propname,"name")) { + this->name(value); + } else if (!strcmp(propname,"callback")) { + callback(value); user_data_type("long"); + } else if (!strcmp(propname,"argument")) { + user_data(value); + } else if (!strcmp(propname,"shortcut")) { + if (value[0]) { + char buf[128]; sprintf(buf,"o->shortcut(\"%s\");",value); + extra_code(0,buf); + } + } else if (!strcmp(propname,"style")) { + if (!strncmp(value,"FL_NORMAL",9)) return 1; + if (!lookup_symbol(value,v,1)) return 0; + o->labelfont(v); o->labeltype((Fl_Labeltype)(v>>8)); + } else if (!strcmp(propname,"size")) { + if (!lookup_symbol(value,v,1)) return 0; + o->labelsize(v); + } else if (!strcmp(propname,"type")) { + if (!strncmp(value,"NORMAL",6)) return 1; + if (lookup_symbol(value,v,1)) {o->type(v); return 1;} + if (!strcmp(value+strlen(value)-5,"FRAME")) goto TRY_BOXTYPE; + if (!strcmp(value+strlen(value)-3,"BOX")) goto TRY_BOXTYPE; + return 0; + } else if (!strcmp(propname,"lcol")) { + if (!lookup_symbol(value,v,1)) return 0; + o->labelcolor(v); + } else if (!strcmp(propname,"return")) { + if (!lookup_symbol(value,v,0)) return 0; + o->when(v|FL_WHEN_RELEASE); + } else if (!strcmp(propname,"alignment")) { + if (!lookup_symbol(value,v)) { + // convert old numeric values: + int v1 = atoi(value); if (v1 <= 0 && strcmp(value,"0")) return 0; + v = 0; + if (v1 >= 5) {v = FL_ALIGN_INSIDE; v1 -= 5;} + switch (v1) { + case 0: v += FL_ALIGN_TOP; break; + case 1: v += FL_ALIGN_BOTTOM; break; + case 2: v += FL_ALIGN_LEFT; break; + case 3: v += FL_ALIGN_RIGHT; break; + case 4: v += FL_ALIGN_CENTER; break; + default: return 0; + } + } + o->align(v); + } else if (!strcmp(propname,"resizebox")) { + resizable(1); + } else if (!strcmp(propname,"colors")) { + char* p = (char*)value; + while (*p != ' ') {if (!*p) return 0; p++;} + *p = 0; + int v1; + if (!lookup_symbol(value,v,1) || !lookup_symbol(p+1,v1,1)) { + *p=' '; return 0;} + o->color(v,v1); + } else if (!strcmp(propname,"resize")) { + return !strcmp(value,"FL_RESIZE_ALL"); + } else if (!strcmp(propname,"gravity")) { + return !strcmp(value,"FL_NoGravity FL_NoGravity"); + } else if (!strcmp(propname,"boxtype")) { + TRY_BOXTYPE: + int x = boxnumber(value); + if (!x) {x = item_number(boxmenu1, value); if (x < 0) return 0;} + if (x == ZERO_ENTRY) { + x = 0; + if (o->box() != ((Widget_Node*)factory)->o->box()) return 1; // kludge for frame + } + o->box((Fl_Boxtype)x); + } else { + return 0; + } + return 1; +} + +void leave_live_mode_cb(Fl_Widget*, void*) { + live_mode_cb(nullptr, nullptr); +} + +Fl_Widget *Widget_Node::enter_live_mode(int) { + live_widget = widget(o->x(), o->y(), o->w(), o->h()); + if (live_widget) + copy_properties(); + return live_widget; +} + +Fl_Widget* Widget_Node::propagate_live_mode(Fl_Group* grp) { + live_widget = grp; + copy_properties(); + Node *n; + for (n = next; n && n->level > level; n = n->next) { + if (n->level == level+1) { + Fl_Widget* proxy_child = n->enter_live_mode(); + if (proxy_child && n->is_widget() && ((Widget_Node*)n)->resizable()) { + grp->resizable(proxy_child); + } + } + } + grp->end(); + live_widget = grp; + copy_properties_for_children(); + return live_widget; +} + + +void Widget_Node::leave_live_mode() { +} + +/** + copy all properties from the edit widget to the live widget + */ +void Widget_Node::copy_properties() { + if (!live_widget) + return; + + Fl_Font ff = 0; + int fs = 0; + Fl_Color fc = 0; + textstuff(0, ff, fs, fc); + + // copy all attributes common to all widget types + Fl_Widget *w = live_widget; + w->label(o->label()); + w->tooltip(tooltip()); + w->type(o->type()); + w->box(o->box()); + w->color(o->color()); + w->selection_color(o->selection_color()); + w->labeltype(o->labeltype()); + w->labelfont(o->labelfont()); + w->labelsize(o->labelsize()); + w->labelcolor(o->labelcolor()); + w->align(o->align()); + w->when(o->when()); + + // copy all attributes specific to widgets derived from Fl_Button + if (is_button()) { + Fl_Button* d = (Fl_Button*)live_widget, *s = (Fl_Button*)o; + d->down_box(s->down_box()); + d->shortcut(s->shortcut()); + d->value(s->value()); + } + + // copy all attributes specific to widgets derived from Fl_Input_ + if (is_a(Type::Input)) { + Fl_Input_* d = (Fl_Input_*)live_widget, *s = (Fl_Input_*)o; + d->shortcut(s->shortcut()); + d->textfont(ff); + d->textsize(fs); + d->textcolor(fc); + } + + // copy all attributes specific to widgets derived from Fl_Value_Input + if (is_a(Type::Value_Input)) { + Fl_Value_Input* d = (Fl_Value_Input*)live_widget, *s = (Fl_Value_Input*)o; + d->shortcut(s->shortcut()); + d->textfont(ff); + d->textsize(fs); + d->textcolor(fc); + } + + // copy all attributes specific to widgets derived from Fl_Text_Display + if (is_a(Type::Text_Display)) { + Fl_Text_Display* d = (Fl_Text_Display*)live_widget, *s = (Fl_Text_Display*)o; + d->shortcut(s->shortcut()); + d->textfont(ff); + d->textsize(fs); + d->textcolor(fc); + } + + // copy all attributes specific to Fl_Valuator and derived classes + if (is_a(Type::Valuator_)) { + Fl_Valuator* d = (Fl_Valuator*)live_widget, *s = (Fl_Valuator*)o; + d->minimum(s->minimum()); + d->maximum(s->maximum()); + d->step(s->step()); + d->value(s->value()); + if (is_a(Type::Slider)) { + Fl_Slider *d = (Fl_Slider*)live_widget, *s = (Fl_Slider*)o; + d->slider_size(s->slider_size()); + } + } + + // copy all attributes specific to Fl_Spinner and derived classes + if (is_a(Type::Spinner)) { + Fl_Spinner* d = (Fl_Spinner*)live_widget, *s = (Fl_Spinner*)o; + d->minimum(s->minimum()); + d->maximum(s->maximum()); + d->step(s->step()); + d->value(s->value()); + } + + if (!o->visible()) + w->hide(); + if (!o->active()) + w->deactivate(); +} + diff --git a/fluid/nodes/Widget_Node.h b/fluid/nodes/Widget_Node.h new file mode 100644 index 000000000..261005590 --- /dev/null +++ b/fluid/nodes/Widget_Node.h @@ -0,0 +1,131 @@ +// +// Widget Node header file 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 +// + +// Type for creating all subclasses of Fl_Widget +// This should have the widget pointer in it, but it is still in the +// Node base class. + +#ifndef FLUID_NODES_WIDGET_NODE_H +#define FLUID_NODES_WIDGET_NODE_H + +#include "nodes/Node.h" + +#define NUM_EXTRA_CODE 4 + +class Widget_Node; +class Image_Asset; + +extern void* const LOAD; +extern Widget_Node *current_widget; // one of the selected ones + +extern const char* subclassname(Node* l); +extern int is_name(const char *c); +void selection_changed(Node* new_current); +Node *sort(Node *parent); + +class Widget_Node : public Node +{ + typedef Node super; + + virtual Fl_Widget *widget(int,int,int,int) = 0; + virtual Widget_Node *_make() = 0; // virtual constructor + void setlabel(const char *) override; + + const char *extra_code_[NUM_EXTRA_CODE]; + const char *subclass_; + const char *tooltip_; + const char *image_name_; + const char *inactive_name_; + uchar hotspot_; + +protected: + + /// This variable is set for visible windows in batch mode. + /// We can't open a window in batch mode, even if we want the "visible" flags + /// set, so we need a second place to store this information while also + /// disabling the output of the "hide" property by the Widget Type. + uchar override_visible_; + + void write_static(fld::io::Code_Writer& f) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_widget_code(fld::io::Code_Writer& f); + void write_extra_code(fld::io::Code_Writer& f); + void write_block_close(fld::io::Code_Writer& f); + void write_code2(fld::io::Code_Writer& f) override; + void write_color(fld::io::Code_Writer& f, const char*, Fl_Color); + Fl_Widget *live_widget; + +public: + Fl_Widget *o; + int public_; + int bind_image_; + int compress_image_; + int bind_deimage_; + int compress_deimage_; + int scale_image_w_, scale_image_h_; + int scale_deimage_w_, scale_deimage_h_; + + Image_Asset *image; + void setimage(Image_Asset *); + Image_Asset *inactive; + void setinactive(Image_Asset *); + + Widget_Node(); + Node *make(Strategy strategy) override; + void open() override; + + const char *extra_code(int n) const {return extra_code_[n];} + void extra_code(int n,const char *); + const char *subclass() const {return subclass_;} + void subclass(const char *); + const char *tooltip() const {return tooltip_;} + void tooltip(const char *); + const char *image_name() const {return image_name_;} + void image_name(const char *); + const char *inactive_name() const {return inactive_name_;} + void inactive_name(const char *); + uchar hotspot() const {return hotspot_;} + void hotspot(uchar v) {hotspot_ = v;} + uchar resizable() const; + void resizable(uchar v); + + virtual int textstuff(int what, Fl_Font &, int &, Fl_Color &); + virtual Fl_Menu_Item *subtypes(); + + Type type() const override { return Type::Widget_; } + bool is_a(Type inType) const override { return (inType==Type::Widget_) ? true : super::is_a(inType); } + int is_widget() const override; + int is_true_widget() const override { return 1; } + int is_public() const override; + + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + int read_fdesign(const char*, const char*) override; + + Fl_Widget *enter_live_mode(int top=0) override; + Fl_Widget *propagate_live_mode(Fl_Group* grp); + void leave_live_mode() override; + void copy_properties() override; + + virtual void ideal_size(int &w, int &h); + + ~Widget_Node(); + void redraw(); +}; + +extern Fl_Window *the_panel; + +#endif // FLUID_NODES_WIDGET_NODE_H diff --git a/fluid/nodes/Window_Node.cxx b/fluid/nodes/Window_Node.cxx new file mode 100644 index 000000000..d6cbe80e6 --- /dev/null +++ b/fluid/nodes/Window_Node.cxx @@ -0,0 +1,1497 @@ +// +// Window Node code file 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 +// + +// +// The widget describing an Fl_Window. This is also all the code +// for interacting with the overlay, which allows the user to +// select, move, and resize the children widgets. + +#include "nodes/Window_Node.h" + +#include "app/Snap_Action.h" +#include "Fluid.h" +#include "Project.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "nodes/factory.h" +#include "nodes/Group_Node.h" +#include "nodes/Grid_Node.h" +#include "panels/settings_panel.h" +#include "panels/widget_panel.h" +#include "widgets/Node_Browser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../src/flstring.h" + +#include +#include +#include +#undef min +#undef max +#include + +extern Fl_Window *the_panel; +extern void draw_width(int x, int y, int r, Fl_Align a); +extern void draw_height(int x, int y, int b, Fl_Align a); + +// Update the XYWH values in the widget panel... +static void update_xywh() { + if (current_widget && current_widget->is_widget()) { + Fl_Widget *o = ((Widget_Node *)current_widget)->o; + widget_x_input->value(o->x()); + widget_y_input->value(o->y()); + widget_w_input->value(o->w()); + widget_h_input->value(o->h()); + if (Flex_Node::parent_is_flex(current_widget)) { + widget_flex_size->value(Flex_Node::size(current_widget)); + widget_flex_fixed->value(Flex_Node::is_fixed(current_widget)); + } + } +} + +void i18n_type_cb(Fl_Choice *c, void *v) { + if (v == LOAD) { + c->value(static_cast(Fluid.proj.i18n_type)); + } else { + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_type = static_cast(c->value()); + Fluid.proj.set_modflag(1); + } + switch (Fluid.proj.i18n_type) { + case fld::I18n_Type::NONE : /* None */ + i18n_gnu_group->hide(); + i18n_posix_group->hide(); + break; + case fld::I18n_Type::GNU : /* GNU gettext */ + i18n_gnu_group->show(); + i18n_posix_group->hide(); + break; + case fld::I18n_Type::POSIX : /* POSIX cat */ + i18n_gnu_group->hide(); + i18n_posix_group->show(); + break; + } + // make sure that the outside labels are redrawn too. + w_settings_i18n_tab->redraw(); +} + +void show_grid_cb(Fl_Widget *, void *) { + settings_window->show(); + w_settings_tabs->value(w_settings_layout_tab); +} + +void show_settings_cb(Fl_Widget *, void *) { + settings_window->hotspot(settings_window); + settings_window->show(); +} + +//////////////////////////////////////////////////////////////// + +Fl_Menu_Item window_type_menu[] = { + {"Single",0,nullptr,(void*)FL_WINDOW}, + {"Double",0,nullptr,(void*)(FL_DOUBLE_WINDOW)}, + {nullptr}}; + +static int overlays_invisible; + +// The following Fl_Widget is used to simulate the windows. It has +// an overlay for the fluid ui, and special-cases the FL_NO_BOX. + +class Overlay_Window : public Fl_Overlay_Window { + void draw() override; + void draw_overlay() override; + static void close_cb(Overlay_Window *self, void*); +public: + Window_Node *window; + int handle(int) override; + Overlay_Window(int W,int H) : Fl_Overlay_Window(W,H) { + Fl_Group::current(nullptr); + callback((Fl_Callback*)close_cb); + } + void resize(int,int,int,int) override; + uchar *read_image(int &ww, int &hh); +}; + +/** + \brief User closes the window, so we mark the .fl file as changed. + Mark the .fl file a changed, but don;t mark the source files as changed. + \param self pointer to this window + */ +void Overlay_Window::close_cb(Overlay_Window *self, void*) { + if (self->visible()) + Fluid.proj.set_modflag(1, -2); + self->hide(); +} + +// Use this when drawing flat boxes while editing, so users can see the outline, +// even if the group and its parent have the same color. +static void fd_flat_box_ghosted(int x, int y, int w, int h, Fl_Color c) { + fl_rectf(x, y, w, h, Fl::box_color(c)); + fl_rect(x, y, w, h, Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, c, .1f))); +} + +void Overlay_Window::draw() { + const int CHECKSIZE = 8; + // see if box is clear or a frame or rounded: + if ((damage()&FL_DAMAGE_ALL) && + (!box() || (box()>=4&&!(box()&2)) || box()>=_FL_ROUNDED_BOX)) { + // if so, draw checkerboard so user can see what areas are clear: + for (int Y = 0; Y < h(); Y += CHECKSIZE) + for (int X = 0; X < w(); X += CHECKSIZE) { + fl_color(((Y/(2*CHECKSIZE))&1) != ((X/(2*CHECKSIZE))&1) ? + FL_WHITE : FL_BLACK); + fl_rectf(X,Y,CHECKSIZE,CHECKSIZE); + } + } + if (Fluid.show_ghosted_outline) { + Fl_Box_Draw_F *old_flat_box = Fl::get_boxtype(FL_FLAT_BOX); + Fl::set_boxtype(FL_FLAT_BOX, fd_flat_box_ghosted, 0, 0, 0, 0); + Fl_Overlay_Window::draw(); + Fl::set_boxtype(FL_FLAT_BOX, old_flat_box, 0, 0, 0, 0); + } else { + Fl_Overlay_Window::draw(); + } +} + +// Read an image of the overlay window +uchar *Overlay_Window::read_image(int &ww, int &hh) { + // Create an off-screen buffer for the window... + //Fluid.main_window->make_current(); + make_current(); + + ww = w(); + hh = h(); + + Fl_Offscreen offscreen = fl_create_offscreen(ww, hh); + uchar *pixels; + + // Redraw the window into the offscreen buffer... + fl_begin_offscreen(offscreen); + + if (!shown()) image(Fl::scheme_bg_); + + redraw(); + draw(); + + // Read the screen image... + pixels = fl_read_image(nullptr, 0, 0, ww, hh); + + fl_end_offscreen(); + + // Cleanup and return... + fl_delete_offscreen(offscreen); + Fluid.main_window->make_current(); + return pixels; +} + +void Overlay_Window::draw_overlay() { + window->draw_overlay(); +} + +int Overlay_Window::handle(int e) { + int ret = window->handle(e); + if (ret==0) { + switch (e) { + case FL_SHOW: + case FL_HIDE: + ret = Fl_Overlay_Window::handle(e); + } + } + return ret; +} + +/** + Make and add a new Window node. + \param[in] strategy is Strategy::AS_LAST_CHILD or Strategy::AFTER_CURRENT + \return new node + */ +Node *Window_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) p = p->parent; + while (p && (!p->is_code_block() || p->is_a(Type::Widget_Class))) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + if (!p) { + fl_message("Please select a function"); + return nullptr; + } + Window_Node *myo = new Window_Node(); + if (!this->o) {// template widget + this->o = new Fl_Window(100,100); + Fl_Group::current(nullptr); + } + myo->factory = this; + myo->drag = 0; + myo->numselected = 0; + Overlay_Window *w = new Overlay_Window(100, 100); + w->size_range(10, 10); + w->window = myo; + myo->o = w; + myo->add(anchor, strategy); + myo->modal = 0; + myo->non_modal = 0; + return myo; +} + +void Window_Node::add_child(Node* cc, Node* before) { + if (!cc->is_widget()) return; + Widget_Node* c = (Widget_Node*)cc; + Fl_Widget* b = before ? ((Widget_Node*)before)->o : nullptr; + ((Fl_Window*)o)->insert(*(c->o), b); + o->redraw(); +} + +void Window_Node::remove_child(Node* cc) { + Widget_Node* c = (Widget_Node*)cc; + ((Fl_Window*)o)->remove(c->o); + o->redraw(); +} + +void Window_Node::move_child(Node* cc, Node* before) { + Widget_Node* c = (Widget_Node*)cc; + ((Fl_Window*)o)->remove(c->o); + Fl_Widget* b = before ? ((Widget_Node*)before)->o : nullptr; + ((Fl_Window*)o)->insert(*(c->o), b); + o->redraw(); +} + +//////////////////////////////////////////////////////////////// + +/** + \brief Show the Window Type editor window without setting the modified flag. + \see Window_Node::open() + */ +void Window_Node::open_() { + Overlay_Window *w = (Overlay_Window *)o; + if (w->shown()) { + w->show(); + Widget_Node::open(); + } else { + Fl_Widget *p = w->resizable(); + if (!p) w->resizable(w); + w->show(); + w->resizable(p); + } + w->image(Fl::scheme_bg_); +} + +/** + \brief Show the Window Type editor window and set the modified flag if needed. + Double-click on window widget shows the window, or if already shown, it shows + the control panel. + \see Window_Node::open_() + */ +void Window_Node::open() { + Overlay_Window *w = (Overlay_Window *)o; + if (!w->visible()) { + Fluid.proj.set_modflag(1, -2); + } + open_(); +} + +// Read an image of the window +uchar *Window_Node::read_image(int &ww, int &hh) { + Overlay_Window *w = (Overlay_Window *)o; + + int hidden = !w->shown(); + w->show(); // make it the front window + + // Read the screen image... + uchar *idata = w->read_image(ww, hh); + if (hidden) + w->hide(); + return idata; +} + +void Window_Node::ideal_size(int &w, int &h) { + w = 480; h = 320; + if (Fluid.main_window) { + int sx, sy, sw, sh; + Fl_Window *win = Fluid.main_window; + int screen = Fl::screen_num(win->x(), win->y()); + Fl::screen_work_area(sx, sy, sw, sh, screen); + w = std::min(w, sw*3/4); h = std::min(h, sh*3/4); + } + fld::app::Snap_Action::better_size(w, h); +} + + + +//////////////////////////////////////////////////////////////// + +Window_Node Window_Node::prototype; + +void Window_Node::setlabel(const char *n) { + if (o) ((Fl_Window *)o)->label(n); +} + +// Resize from window manager... +void Overlay_Window::resize(int X,int Y,int W,int H) { + // Make sure we don't create undo checkpoints if the window does not actually change. + // Some WMs seem to send spurious resize events. + if (X!=x() || Y!=y() || W!=w() || H!=h()) { + // Set a checkpoint on the first resize event, ignore further resizes until + // a different type of checkpoint is triggered. + if (Fluid.proj.undo.checkpoint(fld::proj::Undo::OnceType::WINDOW_RESIZE)) + Fluid.proj.set_modflag(1); + } + + Fl_Widget* t = resizable(); + if (Fluid.proj.tree.allow_layout == 0) { + resizable(nullptr); + } + + // do not set the mod flag if the window was not resized. In FLUID, all + // windows are opened without a given x/y position, so modifying x/y + // should not mark the project as dirty + if (W!=w() || H!=h()) + Fluid.proj.set_modflag(1); + + Fl_Overlay_Window::resize(X,Y,W,H); + resizable(t); + update_xywh(); +} + +// calculate actual move by moving mouse position (mx,my) to +// nearest multiple of gridsize, and snap to original position +void Window_Node::newdx() { + int mydx, mydy; + mydx = mx-x1; + mydy = my-y1; + + if (!(drag & (FD_DRAG | FD_BOX | FD_LEFT | FD_RIGHT))) { + mydx = 0; + dx = 0; + } + + if (!(drag & (FD_DRAG | FD_BOX | FD_TOP | FD_BOTTOM))) { + mydy = 0; + dy = 0; + } + + if (Fluid.show_guides && (drag & (FD_DRAG|FD_TOP|FD_LEFT|FD_BOTTOM|FD_RIGHT))) { + Node *selection = nullptr; // special power for the first selected widget + for (Node *q=next; q && q->level>level; q = q->next) { + if (q->selected && q->is_true_widget()) { + selection = q; + break; + } + } + fld::app::Snap_Data data = { mydx, mydy, bx, by, br, bt, drag, 4, 4, mydx, mydy, (Widget_Node*)selection, this }; + fld::app::Snap_Action::check_all(data); + if (data.x_dist < 4) mydx = data.dx_out; + if (data.y_dist < 4) mydy = data.dy_out; + } + + if (dx != mydx || dy != mydy) { + dx = mydx; dy = mydy; + ((Overlay_Window *)o)->redraw_overlay(); + } +} + +// Move a widget according to dx and dy calculated above +void Window_Node::newposition(Widget_Node *myo,int &X,int &Y,int &R,int &T) { + X = myo->o->x(); + Y = myo->o->y(); + R = X+myo->o->w(); + T = Y+myo->o->h(); + if (!drag) return; + if (drag&FD_DRAG) { + X += dx; + Y += dy; + R += dx; + T += dy; + } else { + if (drag&FD_LEFT) { + if (X==bx) { + X += dx; + } else { + if (Xbr+dx) R = br+dx; + } + } + if (drag&FD_BOTTOM) { + if (T==bt) { + T += dy; + } else { + if (T>bt+dx) T = bt+dx; + } + } + } + if (R h) { + for (; yp < h; yp+=size) + fl_line(x, y+yp, x+yp, y); + for (; yp < w; yp+=size) + fl_line(x+yp-h, y+h, x+yp, y); + for (; yp < w+h; yp+=size) + fl_line(x+yp-h, y+h, x+w, y+yp-w); + } else { + for (; yp < w; yp+=size) + fl_line(x, y+yp, x+yp, y); + for (; yp < h; yp+=size) + fl_line(x, y+yp, x+w, y+yp-w); + for (; yp < h+w; yp+=size) + fl_line(x+yp-h, y+h, x+w, y+yp-w); + } +} + +/** + \brief Draw a hatch pattern over all children that overlap the bounds of this box. + \param[in] group check all children of this group + \param[in] x, y, w, h bounding box of this group + */ +void Window_Node::draw_out_of_bounds(Widget_Node *group, int x, int y, int w, int h) { + for (Node *p = group->next; p && p->level>group->level; p = p->next) { + if (p->level == group->level+1 && p->is_true_widget()) { + Fl_Widget *o = ((Widget_Node*)p)->o; + if (o->x() < x) fd_hatch(o->x(), o->y(), x-o->x(), o->h()); + if (o->y() < y) fd_hatch(o->x(), o->y(), o->w(), y-o->y()); + if (o->x()+o->w() > x+w) fd_hatch(x+w, o->y(), (o->x()+o->w())-(x+w), o->h()); + if (o->y()+o->h() > y+h) fd_hatch(o->x(), y+h, o->w(), (o->y()+o->h())-(y+h)); + } + } +} + +/** + \brief Draw a hatch pattern for all groups that have out of bounds children. + */ +void Window_Node::draw_out_of_bounds() { + // get every group in the hierarchy, then draw any overlap of a direct child with that group + fl_color(FL_DARK_RED); + draw_out_of_bounds(this, 0, 0, o->w(), o->h()); + for (Node *q=next; q && q->level>level; q = q->next) { + // don't do this for Fl_Scroll (which we currently can't handle in FLUID anyway) + if (q->is_a(Type::Group) && !q->is_a(Type::Scroll)) { + Widget_Node *w = (Widget_Node*)q; + draw_out_of_bounds(w, w->o->x(), w->o->y(), w->o->w(), w->o->h()); + } + } + fl_color(FL_RED); +} + +/** + \brief Compare all children in the same level and hatch overlapping areas. + */ +void Window_Node::draw_overlaps() { + fl_color(FL_DARK_YELLOW); + // loop through all widgets in this window + for (Node *q=next; q && q->level>level; q = q->next) { + // is it a valid widget + if (q->is_true_widget()) { + Widget_Node *w = (Widget_Node*)q; + // is the widget visible + if (w->o->visible()) { + int x = w->o->x(), y = w->o->y(); + int r = x + w->o->w(), b = y + w->o->h(); + for (Node *p=q->next; p && p->level>=q->level; p = p->next) { + if (p->level==q->level && p->is_true_widget()) { + Widget_Node *wp = (Widget_Node*)p; + if (wp->o->visible()) { + int px = std::max(x, wp->o->x()); + int py = std::max(y, wp->o->y()); + int pr = std::min(r, wp->o->x() + wp->o->w()); + int pb = std::min(b, wp->o->y() + wp->o->h()); + if (pr > px && pb > py) + fd_hatch(px, py, pr-px, pb-py); + } + } + } + } else { + int l = q->level; + for (; q && q->next && q->next->level>l; q = q->next) { } + } + } + } + fl_color(FL_RED); +} + +void Window_Node::draw_overlay() { + if (recalc) { + bx = o->w(); by = o->h(); br = 0; bt = 0; + numselected = 0; + for (Node *q=next; q && q->level>level; q=q->next) + if (q->selected && q->is_true_widget()) { + numselected++; + Widget_Node* myo = (Widget_Node*)q; + if (myo->o->x() < bx) bx = myo->o->x(); + if (myo->o->y() < by) by = myo->o->y(); + if (myo->o->x()+myo->o->w() > br) br = myo->o->x()+myo->o->w(); + if (myo->o->y()+myo->o->h() > bt) bt = myo->o->y()+myo->o->h(); + } + recalc = 0; + sx = bx; sy = by; sr = br; st = bt; + } + fl_color(FL_RED); + if (drag==FD_BOX && (x1 != mx || y1 != my)) { + int x = x1; int r = mx; if (x > r) {x = mx; r = x1;} + int y = y1; int b = my; if (y > b) {y = my; b = y1;} + fl_rect(x,y,r-x,b-y); + } + if (overlays_invisible && !drag) return; + + if (Fluid.show_restricted) { + draw_out_of_bounds(); + draw_overlaps(); + // TODO: for Fl_Tile, find all areas that are not covered by visible children + } + + if (selected) fl_rect(0,0,o->w(),o->h()); + if (!numselected) return; + int mybx,myby,mybr,mybt; + int mysx,mysy,mysr,myst; + mybx = mysx = o->w(); myby = mysy = o->h(); mybr = mysr = 0; mybt = myst = 0; + Node *selection = nullptr; // special power for the first selected widget + for (Node *q=next; q && q->level>level; q = q->next) + if (q->selected && q->is_true_widget()) { + if (!selection) selection = q; + Widget_Node* myo = (Widget_Node*)q; + int x,y,r,t; + newposition(myo,x,y,r,t); + if (Fluid.show_guides) { + // If we are in a drag operation, and the parent is a grid, show the grid overlay + if (drag && q->parent && q->parent->is_a(Type::Grid)) { + Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)((Grid_Node*)q->parent)->o); + grid->draw_overlay(); + } + } + if (!Fluid.show_guides || !drag || numselected != 1) { + if (Flex_Node::parent_is_flex(q) && Flex_Node::is_fixed(q)) { + Fl_Flex *flex = ((Fl_Flex*)((Flex_Node*)q->parent)->o); + Fl_Widget *wgt = myo->o; + if (flex->horizontal()) { + draw_width(wgt->x(), wgt->y()+15, wgt->x()+wgt->w(), FL_ALIGN_CENTER); + } else { + draw_height(wgt->x()+15, wgt->y(), wgt->y()+wgt->h(), FL_ALIGN_CENTER); + } + } else if (q->is_a(Type::Grid)) { + Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)((Grid_Node*)q)->o); + grid->draw_overlay(); + } + fl_rect(x,y,r-x,t-y); + } + if (x < mysx) mysx = x; + if (y < mysy) mysy = y; + if (r > mysr) mysr = r; + if (t > myst) myst = t; + if (!(myo->o->align() & FL_ALIGN_INSIDE)) { + // Adjust left/right/top/bottom for top/bottom labels... + int ww, hh; + ww = (myo->o->align() & FL_ALIGN_WRAP) ? myo->o->w() : 0; + hh = myo->o->labelsize(); + myo->o->measure_label(ww, hh); + if (myo->o->align() & FL_ALIGN_TOP) y -= hh; + else if (myo->o->align() & FL_ALIGN_BOTTOM) t += hh; + else if (myo->o->align() & FL_ALIGN_LEFT) x -= ww + 4; + else if (myo->o->align() & FL_ALIGN_RIGHT) r += ww + 4; + } + if (x < mybx) mybx = x; + if (y < myby) myby = y; + if (r > mybr) mybr = r; + if (t > mybt) mybt = t; + } + if (selected) return; + + // align the snapping selection box with the box we draw. + sx = mysx; sy = mysy; sr = mysr; st = myst; + + // Draw selection box + resize handles... + // draw box including all labels + fl_focus_rect(mybx,myby,mybr-mybx,mybt-myby); // issue #816 + // draw box excluding labels + fl_rect(mysx,mysy,mysr-mysx,myst-mysy); + fl_rectf(mysx,mysy,5,5); + fl_rectf(mysr-5,mysy,5,5); + fl_rectf(mysr-5,myst-5,5,5); + fl_rectf(mysx,myst-5,5,5); + + if (Fluid.show_guides && (drag & (FD_DRAG|FD_TOP|FD_LEFT|FD_BOTTOM|FD_RIGHT))) { + fld::app::Snap_Data data = { dx, dy, sx, sy, sr, st, drag, 4, 4, dx, dy, (Widget_Node*)selection, this}; + fld::app::Snap_Action::draw_all(data); + } +} + +// Calculate new bounding box of selected widgets: +void Window_Node::fix_overlay() { + Fluid.overlay_item->label("Hide O&verlays"); + if (overlay_button) overlay_button->label("Hide &Overlays"); + overlays_invisible = 0; + recalc = 1; + ((Overlay_Window *)(this->o))->redraw_overlay(); +} + +// check if we must redraw any parent of tabs/wizard type +void check_redraw_corresponding_parent(Node *s) { + Widget_Node * prev_parent = nullptr; + if( !s || !s->selected || !s->is_widget()) return; + for (Node *i=s; i && i->parent; i=i->parent) { + if (i->is_a(Type::Group) && prev_parent) { + if (i->is_a(Type::Tabs)) { + ((Fl_Tabs*)((Widget_Node*)i)->o)->value(prev_parent->o); + return; + } + if (i->is_a(Type::Wizard)) { + ((Fl_Wizard*)((Widget_Node*)i)->o)->value(prev_parent->o); + return; + } + } + if (i->is_a(Type::Group) && s->is_widget()) + prev_parent = (Widget_Node*)i; + } +} + +// do that for every window (when selected set changes): +void redraw_overlays() { + for (Node *o=Fluid.proj.tree.first; o; o=o->next) + if (o->is_a(Type::Window)) ((Window_Node*)o)->fix_overlay(); +} + +void toggle_overlays(Fl_Widget *,void *) { + overlays_invisible = !overlays_invisible; + + if (overlays_invisible) { + Fluid.overlay_item->label("Show O&verlays"); + if (overlay_button) overlay_button->label("Show &Overlays"); + } else { + Fluid.overlay_item->label("Hide O&verlays"); + if (overlay_button) overlay_button->label("Hide &Overlays"); + } + + for (Node *o=Fluid.proj.tree.first; o; o=o->next) + if (o->is_a(Type::Window)) { + Widget_Node* w = (Widget_Node*)o; + ((Overlay_Window*)(w->o))->redraw_overlay(); + } +} + +/** + \brief User changes settings to show positioning guides in layout editor overlay. + This is called from the main menu and from the check button in the Settings + dialog. + */ +void toggle_guides(Fl_Widget *,void *) { + Fluid.show_guides = !Fluid.show_guides; + Fluid.preferences.set("Fluid.show_guides", Fluid.show_guides); + + if (Fluid.show_guides) + Fluid.guides_item->label("Hide Guides"); + else + Fluid.guides_item->label("Show Guides"); + if (guides_button) + guides_button->value(Fluid.show_guides); + + for (Node *o=Fluid.proj.tree.first; o; o=o->next) { + if (o->is_a(Type::Window)) { + Widget_Node* w = (Widget_Node*)o; + ((Overlay_Window*)(w->o))->redraw_overlay(); + } + } +} + +/** + \brief User changes settings to show positioning guides in layout editor overlay. + This is called from the check button in the Settings dialog. + */ +void toggle_guides_cb(Fl_Check_Button *o, void *v) { + toggle_guides(nullptr, nullptr); +} + +/** + \brief User changes settings to show overlapping and out of bounds widgets. + This is called from the main menu and from the check button in the Settings + dialog. + */ +void toggle_restricted(Fl_Widget *,void *) { + Fluid.show_restricted = !Fluid.show_restricted; + Fluid.preferences.set("Fluid.show_restricted", Fluid.show_restricted); + + if (Fluid.show_restricted) + Fluid.restricted_item->label("Hide Restricted"); + else + Fluid.restricted_item->label("Show Restricted"); + if (restricted_button) + restricted_button->value(Fluid.show_restricted); + + for (Node *o=Fluid.proj.tree.first; o; o=o->next) { + if (o->is_a(Type::Window)) { + Widget_Node* w = (Widget_Node*)o; + ((Overlay_Window*)(w->o))->redraw_overlay(); + } + } +} + +/** + \brief User changes settings to show low contrast groups with a ghosted outline. + */ +void toggle_ghosted_outline_cb(Fl_Check_Button *,void *) { + Fluid.show_ghosted_outline = !Fluid.show_ghosted_outline; + Fluid.preferences.set("Fluid.show_ghosted_outline", Fluid.show_ghosted_outline); + for (Node *o=Fluid.proj.tree.first; o; o=o->next) { + if (o->is_a(Type::Window)) { + Widget_Node* w = (Widget_Node*)o; + ((Overlay_Window*)(w->o))->redraw(); + } + } +} + +/** + \brief User changes settings to show overlapping and out of bounds widgets. + This is called from the check button in the Settings dialog. + */ +void toggle_restricted_cb(Fl_Check_Button *o, void *v) { + toggle_restricted(nullptr, nullptr); +} + +extern void select(Node *,int); +extern void select_only(Node *); +extern void deselect(); +extern Node* in_this_only; +extern void fix_group_size(Node *t); + +extern Fl_Menu_Item New_Menu[]; + +/** + Move the selected children according to current dx, dy, drag state. + + This is somewhat of a do-all function that received many additions when new + widget types were added. In the default case, moving a group will simply move + all descendants with it. When resizing, children are resized to fit within + the group. + + This is not ideal for widgets that are moved or resized within a group that + manages the layout of its children. We must create a more universal way to + modify move events per widget type. + + \param[in] key if key is not 0, it contains the code of the keypress that + caused this call. This must only be set when handle FL_KEYBOARD events. + */ +void Window_Node::moveallchildren(int key) +{ + bool update_widget_panel = false; + Fluid.proj.undo.checkpoint(); + Node *i; + for (i=next; i && i->level>level;) { + if (i->selected && i->is_true_widget()) { + Widget_Node* myo = (Widget_Node*)i; + int x,y,r,t,ow=myo->o->w(),oh=myo->o->h(); + newposition(myo,x,y,r,t); + if (myo->is_a(Type::Flex) || myo->is_a(Type::Grid)) { + // Flex and Grid need to be able to layout their children. + Fluid.proj.tree.allow_layout++; + myo->o->resize(x,y,r-x,t-y); + Fluid.proj.tree.allow_layout--; + } else { + // Other groups are resized without affecting their children, however + // they move their children if the entire widget is moved. + myo->o->resize(x,y,r-x,t-y); + } + if (Flex_Node::parent_is_flex(myo)) { + // If the border of a Flex child is move, give that child a fixed size + // so that the user request is reflected. + Flex_Node* ft = (Flex_Node*)myo->parent; + Fl_Flex* f = (Fl_Flex*)ft->o; + if (key) { + ft->keyboard_move_child(myo, key); + } else if (drag & FD_DRAG) { + ft->insert_child_at(myo->o, Fl::event_x(), Fl::event_y()); + } else { + if (f->horizontal()) { + if (myo->o->w()!=ow) { + f->fixed(myo->o, myo->o->w()); + f->layout(); + } + } else { + if (myo->o->h()!=oh) { + f->fixed(myo->o, myo->o->h()); + f->layout(); + } + } + } + // relayout the Flex parent + Fluid.proj.tree.allow_layout++; + f->layout(); + Fluid.proj.tree.allow_layout--; + } else if (myo->parent && myo->parent->is_a(Type::Grid)) { + Grid_Node* gt = (Grid_Node*)myo->parent; + Fl_Grid* g = (Fl_Grid*)gt->o; + if (key) { + gt->keyboard_move_child(myo, key); + } else { + if (drag & FD_DRAG) { + gt->insert_child_at(myo->o, Fl::event_x(), Fl::event_y()); + } else { + gt->child_resized(myo); + } + } + Fluid.proj.tree.allow_layout++; + g->layout(); + Fluid.proj.tree.allow_layout--; + update_widget_panel = true; + } else if (myo->parent && myo->parent->is_a(Type::Group)) { + Group_Node* gt = (Group_Node*)myo->parent; + ((Fl_Group*)gt->o)->init_sizes(); + } + // move all the children, whether selected or not: + Node* p; + for (p = myo->next; p && p->level>myo->level; p = p->next) + if (p->is_true_widget() && !myo->is_a(Type::Flex) && !myo->is_a(Type::Grid)) { + Widget_Node* myo2 = (Widget_Node*)p; + int X,Y,R,T; + newposition(myo2,X,Y,R,T); + myo2->o->resize(X,Y,R-X,T-Y); + } + i = p; + } else { + i = i->next; + } + } + for (i=next; i && i->level>level; i=i->next) + fix_group_size(i); + o->redraw(); + recalc = 1; + ((Overlay_Window *)(this->o))->redraw_overlay(); + Fluid.proj.set_modflag(1); + dx = dy = 0; + + update_xywh(); + if (update_widget_panel && the_panel && the_panel->visible()) { + propagate_load(the_panel, LOAD); + } +} + +int Window_Node::popupx = 0x7FFFFFFF; // mark as invalid (MAXINT) +int Window_Node::popupy = 0x7FFFFFFF; + +int Window_Node::handle(int event) { + static Node* selection = nullptr; + switch (event) { + case FL_DND_ENTER: + // printf("DND enter\n"); + case FL_DND_DRAG: + // printf("DND drag\n"); + { + // find the innermost item clicked on: + selection = this; + for (Node* i=next; i && i->level>level; i=i->next) + if (i->is_a(Type::Group)) { + Widget_Node* myo = (Widget_Node*)i; + if (Fl::event_inside(myo->o) && myo->o->visible_r()) { + selection = myo; + if (Fl::event_clicks()==1) + reveal_in_browser(myo); + } + } + if (selection && !selection->selected) { + select_only(selection); + ((Overlay_Window *)o)->redraw_overlay(); + } + } + Fl::belowmouse(o); + return 1; + case FL_DND_RELEASE: + // printf("DND release\n"); + Fl::belowmouse(o); + return 1; + case FL_PASTE: + // printf("DND paste\n"); + { Node *prototype = typename_to_prototype(Fl::event_text()); + if (prototype==nullptr) { + // it's not a FLUID type, so it could be the filename of an image + const char *cfn = Fl::event_text(); + // printf("DND is filename %s?\n", cfn); + if ((cfn == nullptr) || (*cfn == 0)) return 0; + if (strlen(cfn) >= FL_PATH_MAX) return 0; + char fn[FL_PATH_MAX+1]; + // some platform prepend "file://" or "computer://" or similar text + const char *sep = strstr(cfn, "://"); + if (sep) + strcpy(fn, sep+3); + else + strcpy(fn, cfn); + // remove possibly trailing \r\n + int n = (int)strlen(fn)-1; + if (fn[n] == '\n') fn[n--] = 0; + if (fn[n] == '\r') fn[n--] = 0; + // on X11 and Wayland (?), filenames need to be decoded +#if (defined(FLTK_USE_X11) || defined(FLTK_USE_WAYLAND)) + fl_decode_uri(fn); +#endif + // does a file by that name actually exist? + if (fl_access(fn, 4)==-1) return 0; + // but is this an image file? + Fl_Image *img = Fl_Shared_Image::get(fn); + if (!img || (img->ld() < 0)) return 0; + // ok, so it is an image - now add it as image() or deimage() to the widget + // printf("DND check for target %s\n", fn); + Widget_Node *tgt = nullptr; + for (Node* i=next; i && i->level>level; i=i->next) { + if (i->is_widget()) { + Widget_Node* myo = (Widget_Node*)i; + if (Fl::event_inside(myo->o) && myo->o->visible_r()) + tgt = myo; + } + } + if (tgt) { + char rel[FL_PATH_MAX+1]; + Fluid.proj.enter_project_dir(); + fl_filename_relative(rel, FL_PATH_MAX, fn); + Fluid.proj.leave_project_dir(); + // printf("DND image = %s\n", fn); + if (Fl::get_key(FL_Alt_L) || Fl::get_key(FL_Alt_R)) { + //if (Fl::event_alt()) { // TODO: X11/Wayland does not set the e_state on DND events + tgt->inactive_name(rel); + tgt->compress_deimage_ = 1; + tgt->bind_deimage_ = 0; + } else { + tgt->image_name(rel); + tgt->compress_image_ = 1; + tgt->bind_image_ = 0; + } + select_only(tgt); + tgt->open(); + } + return 1; + } + + in_this_only = this; + popupx = Fl::event_x(); + popupy = Fl::event_y(); + // If the selected widget at dnd start and the drop target are the same, + // or in the same group, add after selection. Otherwise, just add + // at the end of the selected group. + if ( Fluid.proj.tree.current_dnd->group() + && selection && selection->group() + && Fluid.proj.tree.current_dnd->group()==selection->group()) + { + Node *cc = Fluid.proj.tree.current; + Fluid.proj.tree.current = Fluid.proj.tree.current_dnd; + add_new_widget_from_user(prototype, Strategy::AS_LAST_CHILD); + Fluid.proj.tree.current = cc; + } else { + add_new_widget_from_user(prototype, Strategy::AS_LAST_CHILD); + } + popupx = 0x7FFFFFFF; + popupy = 0x7FFFFFFF; // mark as invalid (MAXINT) + in_this_only = nullptr; + widget_browser->display(Fluid.proj.tree.current); + widget_browser->rebuild(); + return 1; + } + case FL_PUSH: + x1 = mx = Fl::event_x(); + y1 = my = Fl::event_y(); + drag = dx = dy = 0; + // test for popup menu: + if (Fl::event_button() >= 3) { + in_this_only = this; // modifies how some menu items work. + static const Fl_Menu_Item* myprev; + popupx = mx; popupy = my; + const Fl_Menu_Item* m = New_Menu->popup(mx,my,"New",myprev); + if (m && m->callback()) {myprev = m; m->do_callback(this->o);} + popupx = 0x7FFFFFFF; popupy = 0x7FFFFFFF; // mark as invalid (MAXINT) + in_this_only = nullptr; + return 1; + } + // find the innermost item clicked on: + selection = this; + {for (Node* i=next; i && i->level>level; i=i->next) + if (i->is_true_widget()) { + Widget_Node* myo = (Widget_Node*)i; + for (Fl_Widget *o1 = myo->o; o1; o1 = o1->parent()) + if (!o1->visible()) goto CONTINUE2; + if (Fl::event_inside(myo->o)) { + selection = myo; + if (Fl::event_clicks()==1) + reveal_in_browser(myo); + } + CONTINUE2:; + }} + // see if user grabs edges of selected region: + if (numselected && !(Fl::event_state(FL_SHIFT)) && + mx<=br+2 && mx>=bx-2 && my<=bt+2 && my>=by-2) { + if (mx >= br-5) drag |= FD_RIGHT; + else if (mx <= bx+5) drag |= FD_LEFT; + if (my >= bt-5) drag |= FD_BOTTOM; + else if (my <= by+5) drag |= FD_TOP; + if (!drag) drag = FD_DRAG; + } + // do object-specific selection of other objects: + {Node* t = selection->click_test(mx, my); + if (t) { + //if (t == selection) return 1; // indicates mouse eaten w/o change + if (Fl::event_state(FL_SHIFT)) { + Fl::event_is_click(0); + select(t, !t->selected); + } else { + deselect(); + select(t, 1); + if (t->is_a(Type::Menu_Item)) t->open(); + } + selection = t; + drag = 0; + } else { + if (!drag) drag = FD_BOX; // if all else fails, start a new selection region + }} + return 1; + + case FL_DRAG: + if (!drag) return 0; + mx = Fl::event_x(); + my = Fl::event_y(); + newdx(); + return 1; + + case FL_RELEASE: + if (!drag) return 0; + mx = Fl::event_x(); + my = Fl::event_y(); + if (drag != FD_BOX && (dx || dy || !Fl::event_is_click())) { + if (dx || dy) moveallchildren(); + } else if ((Fl::event_clicks() || Fl::event_state(FL_CTRL))) { + Widget_Node::open(); + } else { + if (mxlevel>level; i=i->next) + if (i->is_true_widget()) { + Widget_Node* myo = (Widget_Node*)i; + for (Fl_Widget *o1 = myo->o; o1; o1 = o1->parent()) + if (!o1->visible()) goto CONTINUE; + if (Fl::event_inside(myo->o)) selection = myo; + if (myo && myo->o && myo->o->x()>=x1 && myo->o->y()>y1 && + myo->o->x()+myo->o->w()o->y()+myo->o->h()selected : 1); + } + CONTINUE:; + } + // if nothing in box, select what was clicked on: + if (selection && !n) { + select(selection, toggle ? !selection->selected : 1); + } + } + drag = 0; + ((Overlay_Window *)o)->redraw_overlay(); + return 1; + + case FL_KEYBOARD: { + + int backtab = 0; + switch (Fl::event_key()) { + + case FL_Escape: + ((Fl_Window*)o)->hide(); + return 1; + + case FL_Tab: { + if (Fl::event_state(FL_SHIFT)) backtab = 1; + // find current child: + Node *i = Fluid.proj.tree.current; + while (i && !i->is_true_widget()) i = i->parent; + if (!i) return 0; + Node *p = i->parent; + while (p && p != this) p = p->parent; + if (!p || !p->is_widget()) { + i = next; if (!i || i->level <= level) return 0; + } + p = i; + for (;;) { + i = backtab ? i->prev : i->next; + if (!i || i->level <= level) {i = p; break;} + if (i->is_true_widget()) break; + } + deselect(); select(i,1); + return 1;} + + case FL_Left: dx = -1; dy = 0; goto ARROW; + case FL_Right: dx = +1; dy = 0; goto ARROW; + case FL_Up: dx = 0; dy = -1; goto ARROW; + case FL_Down: dx = 0; dy = +1; goto ARROW; + ARROW: + drag = (Fl::event_state(FL_SHIFT)) ? (FD_RIGHT|FD_BOTTOM) : FD_DRAG; + if (Fl::event_state(FL_COMMAND)) { + int x_step, y_step; + if (drag & (FD_RIGHT|FD_BOTTOM)) + fld::app::Snap_Action::get_resize_stepsize(x_step, y_step); + else + fld::app::Snap_Action::get_move_stepsize(x_step, y_step); + dx *= x_step; + dy *= y_step; + } + moveallchildren(Fl::event_key()); + drag = 0; + return 1; + + case 'o': + toggle_overlays(nullptr, nullptr); + break; + + default: + return 0; + }} + + case FL_SHORTCUT: { + in_this_only = this; // modifies how some menu items work. + const Fl_Menu_Item* m = Fluid.main_menu->test_shortcut(); + if (m && m->callback()) m->do_callback(this->o); + in_this_only = nullptr; + return (m != nullptr);} + + default: + return 0; + } +} + +//////////////////////////////////////////////////////////////// + + +/** + Write the C++ code that comes before the children of the window are written. + \param f the source code output stream + */ +void Window_Node::write_code1(fld::io::Code_Writer& f) { + Widget_Node::write_code1(f); +} + + +/** + Write the C++ code that comes after the children of the window are written. + \param f the source code output stream + */ +void Window_Node::write_code2(fld::io::Code_Writer& f) { + const char *var = is_class() ? "this" : name() ? name() : "o"; + // make the window modal or non-modal + if (modal) { + f.write_c("%s%s->set_modal();\n", f.indent(), var); + } else if (non_modal) { + f.write_c("%s%s->set_non_modal();\n", f.indent(), var); + } + // clear the window border + if (!((Fl_Window*)o)->border()) { + f.write_c("%s%s->clear_border();\n", f.indent(), var); + } + // set the xclass of the window + if (xclass) { + f.write_c("%s%s->xclass(", f.indent(), var); + f.write_cstring(xclass); + f.write_c(");\n"); + } + // make the window resizable + if (((Fl_Window*)o)->resizable() == o) + f.write_c("%s%s->resizable(%s);\n", f.indent(), var, var); + // set the size range last + if (sr_max_w || sr_max_h) { + f.write_c("%s%s->size_range(%d, %d, %d, %d);\n", f.indent(), var, + sr_min_w, sr_min_h, sr_max_w, sr_max_h); + } else if (sr_min_w || sr_min_h) { + f.write_c("%s%s->size_range(%d, %d);\n", f.indent(), var, sr_min_w, sr_min_h); + } + // insert extra code from user, may call `show()` + write_extra_code(f); + // stop adding widgets to this window + f.write_c("%s%s->end();\n", f.indent(), var); + write_block_close(f); +} + +void Window_Node::write_properties(fld::io::Project_Writer &f) { + Widget_Node::write_properties(f); + if (modal) f.write_string("modal"); + else if (non_modal) f.write_string("non_modal"); + if (!((Fl_Window*)o)->border()) f.write_string("noborder"); + if (xclass) {f.write_string("xclass"); f.write_word(xclass);} + if (sr_min_w || sr_min_h || sr_max_w || sr_max_h) + f.write_string("size_range {%d %d %d %d}", sr_min_w, sr_min_h, sr_max_w, sr_max_h); + if (o->visible() || override_visible_) f.write_string("visible"); +} + +void Window_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"modal")) { + modal = 1; + } else if (!strcmp(c,"non_modal")) { + non_modal = 1; + } else if (!strcmp(c, "visible")) { + if (Fluid.batch_mode) // don't actually open any windows in batch mode + override_visible_ = 1; + else // in interactive mode, we simply show the window + open_(); + } else if (!strcmp(c,"noborder")) { + ((Fl_Window*)o)->border(0); + } else if (!strcmp(c,"xclass")) { + storestring(f.read_word(),xclass); + ((Fl_Window*)o)->xclass(xclass); + } else if (!strcmp(c,"size_range")) { + int mw, mh, MW, MH; + if (sscanf(f.read_word(),"%d %d %d %d",&mw,&mh,&MW,&MH) == 4) { + sr_min_w = mw; sr_min_h = mh; sr_max_w = MW; sr_max_h = MH; + } + } else if (!strcmp(c,"xywh")) { + Widget_Node::read_property(f, c); + Fluid.pasteoffset = 0; // make it not apply to contents + } else { + Widget_Node::read_property(f, c); + } +} + +int Window_Node::read_fdesign(const char* propname, const char* value) { + int x; + o->box(FL_NO_BOX); // because fdesign always puts an Fl_Box next + if (!strcmp(propname,"Width")) { + if (sscanf(value,"%d",&x) == 1) o->size(x,o->h()); + } else if (!strcmp(propname,"Height")) { + if (sscanf(value,"%d",&x) == 1) o->size(o->w(),x); + } else if (!strcmp(propname,"NumberofWidgets")) { + return 1; // we can figure out count from file + } else if (!strcmp(propname,"border")) { + if (sscanf(value,"%d",&x) == 1) ((Fl_Window*)o)->border(x); + } else if (!strcmp(propname,"title")) { + label(value); + } else { + return Widget_Node::read_fdesign(propname,value); + } + return 1; +} + +/////////////////////////////////////////////////////////////////////// + +Widget_Class_Node Widget_Class_Node::prototype; + +Widget_Class_Node *current_widget_class = nullptr; + +/** + Create and add a new Widget Class node. + \param[in] strategy add after current or as last child + \return new node + */ +Node *Widget_Class_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) p = p->parent; + while (p && (!p->is_decl_block() || (p->is_widget() && p->is_class()))) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + Widget_Class_Node *myo = new Widget_Class_Node(); + myo->name("UserInterface"); + + if (!this->o) {// template widget + this->o = new Fl_Window(100,100); + Fl_Group::current(nullptr); + } + myo->factory = this; + myo->drag = 0; + myo->numselected = 0; + Overlay_Window *w = new Overlay_Window(100, 100); + w->size_range(10, 10); + w->window = myo; + myo->o = w; + myo->add(anchor, strategy); + myo->modal = 0; + myo->non_modal = 0; + myo->wc_relative = 0; + + return myo; +} + +void Widget_Class_Node::write_properties(fld::io::Project_Writer &f) { + Window_Node::write_properties(f); + if (wc_relative==1) + f.write_string("position_relative"); + else if (wc_relative==2) + f.write_string("position_relative_rescale"); +} + +void Widget_Class_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"position_relative")) { + wc_relative = 1; + } else if (!strcmp(c,"position_relative_rescale")) { + wc_relative = 2; + } else { + Window_Node::read_property(f, c); + } +} + +// Convert A::B::C::D to D (i.e. keep only innermost name) +// This is useful for classes that contain a namespace component +static const char *trimclassname(const char *n) { + if (!n) + return nullptr; + const char *nn; + while((nn = strstr(n, "::"))) { + n = nn + 2; + } + return(n); +} + + +void Widget_Class_Node::write_code1(fld::io::Code_Writer& f) { +#if 0 + Widget_Node::write_code1(fld::io::Code_Writer& f); +#endif // 0 + + current_widget_class = this; + write_public_state = 1; + + const char *c = subclass(); + if (!c) c = "Fl_Group"; + + f.write_c("\n"); + write_comment_h(f); + f.write_h("\nclass %s : public %s {\n", name(), c); + if (strstr(c, "Window")) { + f.write_h("%svoid _%s();\n", f.indent(1), trimclassname(name())); + f.write_h("public:\n"); + f.write_h("%s%s(int X, int Y, int W, int H, const char *L = 0);\n", f.indent(1), trimclassname(name())); + f.write_h("%s%s(int W, int H, const char *L = 0);\n", f.indent(1), trimclassname(name())); + f.write_h("%s%s();\n", f.indent(1), trimclassname(name())); + + // a constructor with all four dimensions plus label + f.write_c("%s::%s(int X, int Y, int W, int H, const char *L) :\n", name(), trimclassname(name())); + f.write_c("%s%s(X, Y, W, H, L)\n{\n", f.indent(1), c); + f.write_c("%s_%s();\n", f.indent(1), trimclassname(name())); + f.write_c("}\n\n"); + + // a constructor with just the size and label. The window manager will position the window + f.write_c("%s::%s(int W, int H, const char *L) :\n", name(), trimclassname(name())); + f.write_c("%s%s(0, 0, W, H, L)\n{\n", f.indent(1), c); + f.write_c("%sclear_flag(16);\n", f.indent(1)); + f.write_c("%s_%s();\n", f.indent(1), trimclassname(name())); + f.write_c("}\n\n"); + + // a constructor that takes size and label from the Fluid database + f.write_c("%s::%s() :\n", name(), trimclassname(name())); + f.write_c("%s%s(0, 0, %d, %d, ", f.indent(1), c, o->w(), o->h()); + const char *cstr = label(); + if (cstr) f.write_cstring(cstr); + else f.write_c("0"); + f.write_c(")\n{\n"); + f.write_c("%sclear_flag(16);\n", f.indent(1)); + f.write_c("%s_%s();\n", f.indent(1), trimclassname(name())); + f.write_c("}\n\n"); + + f.write_c("void %s::_%s() {\n", name(), trimclassname(name())); +// f.write_c("%s%s *w = this;\n", f.indent(1), name()); + } else { + f.write_h("public:\n"); + f.write_h("%s%s(int X, int Y, int W, int H, const char *L = 0);\n", + f.indent(1), trimclassname(name())); + f.write_c("%s::%s(int X, int Y, int W, int H, const char *L) :\n", name(), trimclassname(name())); + if (wc_relative==1) + f.write_c("%s%s(0, 0, W, H, L)\n{\n", f.indent(1), c); + else if (wc_relative==2) + f.write_c("%s%s(0, 0, %d, %d, L)\n{\n", f.indent(1), c, o->w(), o->h()); + else + f.write_c("%s%s(X, Y, W, H, L)\n{\n", f.indent(1), c); + } + +// f.write_c("%s%s *o = this;\n", f.indent(1), name()); + + f.indentation++; + write_widget_code(f); +} + +/** + Write the C++ code that comes after the children of the window are written. + \param f the source code output stream + */ +void Widget_Class_Node::write_code2(fld::io::Code_Writer& f) { + // make the window modal or non-modal + if (modal) { + f.write_c("%sset_modal();\n", f.indent()); + } else if (non_modal) { + f.write_c("%sset_non_modal();\n", f.indent()); + } + // clear the window border + if (!((Fl_Window*)o)->border()) f.write_c("%sclear_border();\n", f.indent()); + // set the xclass of the window + if (xclass) { + f.write_c("%sxclass(", f.indent()); + f.write_cstring(xclass); + f.write_c(");\n"); + } + // make the window resizable + if (((Fl_Window*)o)->resizable() == o) + f.write_c("%sresizable(this);\n", f.indent()); + // insert extra code from user + write_extra_code(f); + // stop adding widgets to this window + f.write_c("%send();\n", f.indent()); + // reposition or resize the Widget Class to fit into the target + if (wc_relative==1) + f.write_c("%sposition(X, Y);\n", f.indent()); + else if (wc_relative==2) + f.write_c("%sresize(X, Y, W, H);\n", f.indent()); + f.indentation--; + f.write_c("}\n"); +} + + +//////////////////////////////////////////////////////////////// +// live mode support + +Fl_Widget *Window_Node::enter_live_mode(int) { + Fl_Window *win = new Fl_Window(10, 10, o->w(), o->h()); + return propagate_live_mode(win); +} + +void Window_Node::leave_live_mode() { +} + +/** + copy all properties from the edit widget to the live widget + */ +void Window_Node::copy_properties() { + Fl_Window *self = static_cast(o); + Fl_Window *live = static_cast(live_widget); + if (self->resizable() == self) + live->resizable(live); + Widget_Node::copy_properties(); +} diff --git a/fluid/nodes/Window_Node.h b/fluid/nodes/Window_Node.h new file mode 100644 index 000000000..a6846168b --- /dev/null +++ b/fluid/nodes/Window_Node.h @@ -0,0 +1,163 @@ +// +// Window type header file 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 +// + +// +// Type for creating all subclasses of Fl_Widget +// This should have the widget pointer in it, but it is still in the +// Node base class. + +#ifndef FLUID_NODES_WINDOW_NODE_H +#define FLUID_NODES_WINDOW_NODE_H + +#include "nodes/Group_Node.h" + +class Widget_Class_Node; + +extern Fl_Menu_Item window_type_menu[]; +extern Widget_Class_Node *current_widget_class; + +void toggle_overlays(Fl_Widget *,void *); +void toggle_guides(Fl_Widget *,void *); +void toggle_restricted(Fl_Widget *,void *); +void show_project_cb(Fl_Widget *, void *); +void show_grid_cb(Fl_Widget *, void *); +void show_settings_cb(Fl_Widget *, void *); + +enum { + FD_LEFT = 1, // user drags the left side of the selection box + FD_RIGHT = 2, + FD_BOTTOM = 4, + FD_TOP = 8, + FD_DRAG = 16, // user drags the entire selection + FD_BOX = 32 // user creates a new selection box +}; + +class Window_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Window_Node prototype; +protected: + + Fl_Menu_Item* subtypes() override {return window_type_menu;} + + friend class Overlay_Window; + int mx,my; // mouse position during dragging + int x1,y1; // initial position of selection box + int bx,by,br,bt; // bounding box of selection before snapping + int sx,sy,sr,st; // bounding box of selection after snapping to guides + int dx,dy; + int drag; // which parts of bbox are being moved + int numselected; // number of children selected + void draw_out_of_bounds(Widget_Node *group, int x, int y, int w, int h); + void draw_out_of_bounds(); + void draw_overlaps(); + void draw_overlay(); + void newdx(); + void newposition(Widget_Node *,int &x,int &y,int &w,int &h); + int handle(int); + void setlabel(const char *) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + Widget_Node *_make() override {return nullptr;} // we don't call this + Fl_Widget *widget(int,int,int,int) override {return nullptr;} + int recalc; // set by fix_overlay() + void moveallchildren(int key=0); + Type type() const override { return Type::Window; } + bool is_a(Type inType) const override { return (inType==Type::Window) ? true : super::is_a(inType); } + void open_(); + +public: + + Window_Node() : + mx(0), my(0), + x1(0), y1(0), + bx(0), by(0), br(0), bt(0), + sx(0), sy(0), sr(0), st(0), + dx(0), dy(0), + drag(0), + numselected(0), + recalc(0), + modal(0), non_modal(0), + xclass(nullptr), + sr_min_w(0), sr_min_h(0), sr_max_w(0), sr_max_h(0) + { } + uchar modal, non_modal; + const char *xclass; // junk string, used for shortcut + + Node *make(Strategy strategy) override; + const char *type_name() override {return "Fl_Window";} + const char *alt_type_name() override {return "fltk::Window";} + + void open() override; + void ideal_size(int &w, int &h) override; + + void fix_overlay(); // Update the bounding box, etc + uchar *read_image(int &ww, int &hh); // Read an image of the window + + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + int read_fdesign(const char*, const char*) override; + + void add_child(Node*, Node*) override; + void move_child(Node*, Node*) override; + void remove_child(Node*) override; + + int can_have_children() const override {return 1;} + + Fl_Widget *enter_live_mode(int top=0) override; + void leave_live_mode() override; + void copy_properties() override; + + int sr_min_w, sr_min_h, sr_max_w, sr_max_h; + + static int popupx, popupy; +}; + +class Widget_Class_Node : private Window_Node +{ +public: + typedef Window_Node super; + static Widget_Class_Node prototype; + +protected: + Fl_Menu_Item* subtypes() override {return nullptr;} + +public: + Widget_Class_Node() { + write_public_state = 0; + wc_relative = 0; + } + // state variables for output: + char write_public_state; // true when public: has been printed + char wc_relative; // if 1, reposition all children, if 2, reposition and resize + + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + Node *make(Strategy strategy) override; + const char *type_name() override {return "widget_class";} + Type type() const override { return Type::Widget_Class; } + bool is_a(Type inType) const override { return (inType==Type::Widget_Class) ? true : super::is_a(inType); } + int can_have_children() const override {return 1;} + int is_code_block() const override {return 1;} + int is_decl_block() const override {return 1;} + int is_class() const override {return 1;} +}; + +#endif // FLUID_NODES_WINDOW_NODE_H diff --git a/fluid/nodes/callbacks.cxx b/fluid/nodes/callbacks.cxx new file mode 100644 index 000000000..f53ca2d6c --- /dev/null +++ b/fluid/nodes/callbacks.cxx @@ -0,0 +1,18 @@ +// +// Fluid Node callbacks code for the Fast Light Tool Kit (FLTK). +// +// Copyright 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 "nodes/callbacks.h" + diff --git a/fluid/nodes/callbacks.h b/fluid/nodes/callbacks.h new file mode 100644 index 000000000..77a38754a --- /dev/null +++ b/fluid/nodes/callbacks.h @@ -0,0 +1,23 @@ +// +// Fluid Node callbacks header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 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_NODES_CALLBACKS_H +#define FLUID_NODES_CALLBACKS_H + +#include "nodes/Node.h" + + +#endif // FLUID_NODES_CALLBACKS_H diff --git a/fluid/nodes/factory.cxx b/fluid/nodes/factory.cxx index ad5d388e1..8388e0202 100644 --- a/fluid/nodes/factory.cxx +++ b/fluid/nodes/factory.cxx @@ -1,13 +1,5 @@ // -// Widget factory code for the Fast Light Tool Kit (FLTK). -// -// Type classes for most of the fltk widgets. Most of the work -// is done by code in Fl_Widget_Type.cxx. Also a factory instance -// of each of these type classes. -// -// This file also contains the "new" menu, which has a pointer -// to a factory instance for every class (both the ones defined -// here and ones in other files) +// Node Factory code for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2025 by Bill Spitzak and others. // @@ -22,14 +14,41 @@ // https://www.fltk.org/bugs.php // +/** + + + \todo Verify the text + + Type classes for most of the fltk widgets. Most of the work + is done by code in Widget_Node.cxx. Also a factory instance + of each of these type classes. + + This file also contains the "new" menu, which has a pointer + to a factory instance for every class (both the ones defined + here and ones in other files) + + + Type classes for most of the fltk widgets. Most of the work + is done by code in Widget_Node.C. Also a factory instance + of each of these type classes. + + This file also contains the "new" menu, which has a pointer + to a factory instance for every class (both the ones defined + here and ones in other files) + + */ #include "nodes/factory.h" -#include "app/Fd_Snap_Action.h" -#include "app/fluid.h" -#include "app/undo.h" -#include "nodes/Fl_Group_Type.h" -#include "nodes/Fl_Grid_Type.h" -#include "nodes/Fl_Menu_Type.h" +#include "app/Snap_Action.h" +#include "Fluid.h" +#include "proj/undo.h" +#include "nodes/Button_Node.h" +#include "nodes/Function_Node.h" +#include "nodes/Grid_Node.h" +#include "nodes/Group_Node.h" +#include "nodes/Menu_Node.h" +#include "nodes/Widget_Node.h" +#include "nodes/Window_Node.h" #include "rsrcs/pixmaps.h" #include @@ -70,23 +89,26 @@ // ---- Browser_Base ---- static Fl_Menu_Item browser_base_type_menu[] = { - {"No Select", 0, 0, (void*)FL_NORMAL_BROWSER}, - {"Select", 0, 0, (void*)FL_SELECT_BROWSER}, - {"Hold", 0, 0, (void*)FL_HOLD_BROWSER}, - {"Multi", 0, 0, (void*)FL_MULTI_BROWSER}, - {0} + {"No Select", 0, nullptr, (void*)nullptr}, + {"Select", 0, nullptr, (void*)FL_SELECT_BROWSER}, + {"Hold", 0, nullptr, (void*)FL_HOLD_BROWSER}, + {"Multi", 0, nullptr, (void*)FL_MULTI_BROWSER}, + {nullptr} }; /** \brief This is the base class for some browsers types. This class will not be instantiated. */ -class Fl_Browser_Base_Type : public Fl_Widget_Type +class Browser_Base_Node : public Widget_Node { - typedef Fl_Widget_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return browser_base_type_menu; } - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Browser_ *myo = (Fl_Browser_*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Widget_Node super; + static Browser_Base_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return browser_base_type_menu; } + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Browser_ *myo = (Fl_Browser_*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -97,42 +119,44 @@ class Fl_Browser_Base_Type : public Fl_Widget_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { w = 120; h = 160; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Browser_"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Browser_"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Browser_"; } + const char *alt_type_name() override { return "fltk::Browser_"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Browser* b = new Fl_Browser(x, y, w, h); return b; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Browser_Base_Type(); } - ID id() const FL_OVERRIDE { return ID_Browser_; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Browser_) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Browser_Base_Node(); } + Type type() const override { return Type::Browser_; } + bool is_a(Type inType) const override { return (inType==Type::Browser_) ? true : super::is_a(inType); } }; -static Fl_Browser_Base_Type Fl_Browser_Base_type; +Browser_Base_Node Browser_Base_Node::prototype; // ---- Browser ---- /** \brief Handle a plain browser widget. - Most of the work is already done in Fl_Browser_Base_Type. + Most of the work is already done in Browser_Base_Node. */ -class Fl_Browser_Type : public Fl_Browser_Base_Type +class Browser_Node : public Browser_Base_Node { - typedef Fl_Browser_Base_Type super; public: - const char *type_name() FL_OVERRIDE { return "Fl_Browser"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Browser"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Browser_Base_Node super; + static Browser_Node prototype; +public: + const char *type_name() override { return "Fl_Browser"; } + const char *alt_type_name() override { return "fltk::Browser"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Browser* b = new Fl_Browser(x, y, w, h); // Fl_Browser::add calls fl_height(), which requires the X display open. // Avoid this when compiling so it works w/o a display: - if (!batch_mode) { + if (!Fluid.batch_mode) { char buffer[20]; for (int i = 1; i <= 20; i++) { sprintf(buffer,"Browser Line %d",i); @@ -141,12 +165,12 @@ public: } return b; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Browser_Type(); } - ID id() const FL_OVERRIDE { return ID_Browser; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Browser) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Browser_Node(); } + Type type() const override { return Type::Browser; } + bool is_a(Type inType) const override { return (inType==Type::Browser) ? true : super::is_a(inType); } }; -static Fl_Browser_Type Fl_Browser_type; +Browser_Node Browser_Node::prototype; // ---- Check Browser ---- @@ -155,17 +179,19 @@ static Fl_Browser_Type Fl_Browser_type; \brief Manage the Check Browser. The Fl_Check_Browser is derived form Fl_Browser_ (underline!), not Fl_Browser. */ -class Fl_Check_Browser_Type : public Fl_Browser_Base_Type +class Check_Browser_Node : public Browser_Base_Node { - typedef Fl_Browser_Base_Type super; public: - const char *type_name() FL_OVERRIDE { return "Fl_Check_Browser"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::CheckBrowser"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Browser_Base_Node super; + static Check_Browser_Node prototype; +public: + const char *type_name() override { return "Fl_Check_Browser"; } + const char *alt_type_name() override { return "fltk::CheckBrowser"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Check_Browser* b = new Fl_Check_Browser(x, y, w, h); // Fl_Check_Browser::add calls fl_height(), which requires the X display open. // Avoid this when compiling so it works w/o a display: - if (!batch_mode) { + if (!Fluid.batch_mode) { char buffer[20]; for (int i = 1; i <= 20; i++) { sprintf(buffer,"Browser Line %d",i); @@ -174,12 +200,12 @@ public: } return b; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Check_Browser_Type(); } - ID id() const FL_OVERRIDE { return ID_Check_Browser; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Check_Browser) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Check_Browser_Node(); } + Type type() const override { return Type::Check_Browser; } + bool is_a(Type inType) const override { return (inType==Type::Check_Browser) ? true : super::is_a(inType); } }; -static Fl_Check_Browser_Type Fl_Check_Browser_type; +Check_Browser_Node Check_Browser_Node::prototype; // ---- File Browser ---- @@ -189,23 +215,25 @@ static Fl_Check_Browser_Type Fl_Check_Browser_type; As oppoesed to the Hold, Multi, and Select Browser, this is not a subclass, but its own implementation, based on Fl_Browser. */ -class Fl_File_Browser_Type : public Fl_Browser_Type +class File_Browser_Node : public Browser_Node { - typedef Fl_Browser_Type super; public: - const char *type_name() FL_OVERRIDE { return "Fl_File_Browser"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::FileBrowser"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Browser_Node super; + static File_Browser_Node prototype; +public: + const char *type_name() override { return "Fl_File_Browser"; } + const char *alt_type_name() override { return "fltk::FileBrowser"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_File_Browser* b = new Fl_File_Browser(x, y, w, h); - if (!batch_mode) b->load("."); + if (!Fluid.batch_mode) b->load("."); return b; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_File_Browser_Type(); } - ID id() const FL_OVERRIDE { return ID_File_Browser; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_File_Browser) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new File_Browser_Node(); } + Type type() const override { return Type::File_Browser; } + bool is_a(Type inType) const override { return (inType==Type::File_Browser) ? true : super::is_a(inType); } }; -static Fl_File_Browser_Type Fl_File_Browser_type; +File_Browser_Node File_Browser_Node::prototype; // ---- Tree Type ------------------------------------------------------ MARK: - @@ -213,25 +241,27 @@ static Fl_File_Browser_Type Fl_File_Browser_type; /** \brief Handle the Tree widget. Fl_Tree is derived from Fl_Group, but FLUID does not support extended Fl_Tree - functionality, so we derive the Type from Fl_Widget_Type. + functionality, so we derive the Type from Widget_Node. \note Updating item_labelfont etc. does not refresh any of the existing items in the tree, so I decided against implementig those via the labelfont UI. */ -class Fl_Tree_Type : public Fl_Widget_Type +class Tree_Node : public Widget_Node { - typedef Fl_Widget_Type super; public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Widget_Node super; + static Tree_Node prototype; +public: + void ideal_size(int &w, int &h) override { w = 120; h = 160; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Tree"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::TreeBrowser"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Tree"; } + const char *alt_type_name() override { return "fltk::TreeBrowser"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Tree* b = new Fl_Tree(x, y, w, h); - if (!batch_mode) { + if (!Fluid.batch_mode) { b->add("/A1/B1/C1"); b->add("/A1/B1/C2"); b->add("/A1/B2/C1"); @@ -243,12 +273,12 @@ public: } return b; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Tree_Type(); } - ID id() const FL_OVERRIDE { return ID_Tree; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Tree) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Tree_Node(); } + Type type() const override { return Type::Tree; } + bool is_a(Type inType) const override { return (inType==Type::Tree) ? true : super::is_a(inType); } }; -static Fl_Tree_Type Fl_Tree_type; +Tree_Node Tree_Node::prototype; @@ -257,13 +287,16 @@ static Fl_Tree_Type Fl_Tree_type; /** \brief Handle the Help View widget. Fl_Help_View is derived from Fl_Group, but supporting children is not useful, - so we derive from Fl_Widget_Type. + so we derive from Widget_Node. */ -class Fl_Help_View_Type : public Fl_Widget_Type +class Help_View_Node : public Widget_Node { - typedef Fl_Widget_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Help_View *myo = (Fl_Help_View*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Widget_Node super; + static Help_View_Node prototype; +private: + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Help_View *myo = (Fl_Help_View*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -274,27 +307,27 @@ class Fl_Help_View_Type : public Fl_Widget_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { w = 160; h = 120; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Help_View"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::HelpView"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Help_View"; } + const char *alt_type_name() override { return "fltk::HelpView"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Help_View *myo = new Fl_Help_View(x, y, w, h); - if (!batch_mode) { + if (!Fluid.batch_mode) { myo->value("

Fl_Help_View Widget

" "

This is a Fl_Help_View widget.

"); } return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Help_View_Type(); } - ID id() const FL_OVERRIDE { return ID_Help_View; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Help_View) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Help_View_Node(); } + Type type() const override { return Type::Help_View; } + bool is_a(Type inType) const override { return (inType==Type::Help_View) ? true : super::is_a(inType); } }; -static Fl_Help_View_Type Fl_Help_View_type; +Help_View_Node Help_View_Node::prototype; @@ -306,41 +339,46 @@ static Fl_Help_View_Type Fl_Help_View_type; /** \brief Just a base class for all valuators. */ -class Fl_Valuator_Type : public Fl_Widget_Type +class Valuator_Node : public Widget_Node { - typedef Fl_Widget_Type super; public: - const char *type_name() FL_OVERRIDE { return "Fl_Valuator"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Valuator"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Widget_Node super; + static Valuator_Node prototype; +public: + const char *type_name() override { return "Fl_Valuator"; } + const char *alt_type_name() override { return "fltk::Valuator"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Slider(x, y, w, h, "Valuator"); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Valuator_Type(); } - ID id() const FL_OVERRIDE { return ID_Valuator_; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Valuator_) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Valuator_Node(); } + Type type() const override { return Type::Valuator_; } + bool is_a(Type inType) const override { return (inType==Type::Valuator_) ? true : super::is_a(inType); } }; -static Fl_Valuator_Type Fl_Valuator_type; +Valuator_Node Valuator_Node::prototype; // ---- Counter ---- static Fl_Menu_Item counter_type_menu[] = { - { "Normal", 0, 0, (void*)FL_NORMAL_COUNTER }, - { "Simple", 0, 0, (void*)FL_SIMPLE_COUNTER }, - { 0 } + { "Normal", 0, nullptr, (void*)nullptr }, + { "Simple", 0, nullptr, (void*)FL_SIMPLE_COUNTER }, + { nullptr } }; /** \brief Manage the Counter widget. Strictly speaking, the ideal size should derive from the textsize not the labelsize. */ -class Fl_Counter_Type : public Fl_Valuator_Type +class Counter_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return counter_type_menu; } - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Counter *myo = (Fl_Counter*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Valuator_Node super; + static Counter_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return counter_type_menu; } + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Counter *myo = (Fl_Counter*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -351,22 +389,23 @@ class Fl_Counter_Type : public Fl_Valuator_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() + 8; w = layout->textsize_not_null() * 4 + 4 * h; // make room for the arrows - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Counter"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Counter"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Counter"; } + const char *alt_type_name() override { return "fltk::Counter"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Counter(x, y, w, h, "counter:"); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Counter_Type(); } - ID id() const FL_OVERRIDE { return ID_Counter; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Counter) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Counter_Node(); } + Type type() const override { return Type::Counter; } + bool is_a(Type inType) const override { return (inType==Type::Counter) ? true : super::is_a(inType); } }; -static Fl_Counter_Type Fl_Counter_type; +Counter_Node Counter_Node::prototype; // ---- Adjuster ---- @@ -374,105 +413,116 @@ static Fl_Counter_Type Fl_Counter_type; /** \brief Handle Adjuster widgets which are derived from valuators. */ -class Fl_Adjuster_Type : public Fl_Valuator_Type +class Adjuster_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Valuator_Node super; + static Adjuster_Node prototype; +public: + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->labelsize + 8; w = 3 * h; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Adjuster"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Adjuster"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Adjuster"; } + const char *alt_type_name() override { return "fltk::Adjuster"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Adjuster(x, y, w, h); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Adjuster_Type(); } - ID id() const FL_OVERRIDE { return ID_Adjuster; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Adjuster) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Adjuster_Node(); } + Type type() const override { return Type::Adjuster; } + bool is_a(Type inType) const override { return (inType==Type::Adjuster) ? true : super::is_a(inType); } }; -static Fl_Adjuster_Type Fl_Adjuster_type; +Adjuster_Node Adjuster_Node::prototype; // ---- Dial ---- static Fl_Menu_Item dial_type_menu[] = { - { "Dot", 0, 0, (void*)0 }, - { "Line", 0, 0, (void*)FL_LINE_DIAL }, - { "Fill", 0, 0, (void*)FL_FILL_DIAL }, - { 0 } + { "Dot", 0, nullptr, (void*)nullptr }, + { "Line", 0, nullptr, (void*)FL_LINE_DIAL }, + { "Fill", 0, nullptr, (void*)FL_FILL_DIAL }, + { nullptr } }; /** \brief Manage dials. */ -class Fl_Dial_Type : public Fl_Valuator_Type +class Dial_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return dial_type_menu; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Valuator_Node super; + static Dial_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return dial_type_menu; } +public: + void ideal_size(int &w, int &h) override { w = 60; h = 60; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Dial"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Dial"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Dial"; } + const char *alt_type_name() override { return "fltk::Dial"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Dial(x, y, w, h); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Dial_Type(); } - ID id() const FL_OVERRIDE { return ID_Dial; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Dial) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Dial_Node(); } + Type type() const override { return Type::Dial; } + bool is_a(Type inType) const override { return (inType==Type::Dial) ? true : super::is_a(inType); } }; -static Fl_Dial_Type Fl_Dial_type; + +Dial_Node Dial_Node::prototype; // ---- Roller ---- static Fl_Menu_Item roller_type_menu[] = { - { "Vertical", 0, 0, (void*)0 }, - { "Horizontal", 0, 0, (void*)FL_HORIZONTAL }, - { 0 } + { "Vertical", 0, nullptr, (void*)nullptr }, + { "Horizontal", 0, nullptr, (void*)FL_HORIZONTAL }, + { nullptr } }; /** \brief Manage Roller widgets. They are vertical by default. */ -class Fl_Roller_Type : public Fl_Valuator_Type +class Roller_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return roller_type_menu; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Valuator_Node super; + static Roller_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return roller_type_menu; } +public: + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; w = layout->labelsize + 8; h = 4 * w; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Roller"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Roller"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Roller"; } + const char *alt_type_name() override { return "fltk::Roller"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Roller(x, y, w, h); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Roller_Type(); } - ID id() const FL_OVERRIDE { return ID_Roller; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Roller) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Roller_Node(); } + Type type() const override { return Type::Roller; } + bool is_a(Type inType) const override { return (inType==Type::Roller) ? true : super::is_a(inType); } }; -static Fl_Roller_Type Fl_Roller_type; +Roller_Node Roller_Node::prototype; // ---- Slider ---- static Fl_Menu_Item slider_type_menu[] = { - { "Vertical", 0, 0, (void*)FL_VERT_SLIDER }, - { "Horizontal", 0, 0, (void*)FL_HOR_SLIDER }, - { "Vert Fill", 0, 0, (void*)FL_VERT_FILL_SLIDER }, - { "Horz Fill", 0, 0, (void*)FL_HOR_FILL_SLIDER }, - { "Vert Knob", 0, 0, (void*)FL_VERT_NICE_SLIDER }, - { "Horz Knob", 0, 0, (void*)FL_HOR_NICE_SLIDER }, - { 0 } + { "Vertical", 0, nullptr, (void*)nullptr }, + { "Horizontal", 0, nullptr, (void*)FL_HOR_SLIDER }, + { "Vert Fill", 0, nullptr, (void*)FL_VERT_FILL_SLIDER }, + { "Horz Fill", 0, nullptr, (void*)FL_HOR_FILL_SLIDER }, + { "Vert Knob", 0, nullptr, (void*)FL_VERT_NICE_SLIDER }, + { "Horz Knob", 0, nullptr, (void*)FL_HOR_NICE_SLIDER }, + { nullptr } }; /** @@ -480,55 +530,63 @@ static Fl_Menu_Item slider_type_menu[] = { They are vertical by default. Fl_Value_Slider has its own type. */ -class Fl_Slider_Type : public Fl_Valuator_Type +class Slider_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return slider_type_menu; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Valuator_Node super; + static Slider_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return slider_type_menu; } +public: + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; w = layout->labelsize + 8; h = 4 * w; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Slider"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Slider"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Slider"; } + const char *alt_type_name() override { return "fltk::Slider"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Slider(x, y, w, h, "slider:"); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Slider_Type(); } - ID id() const FL_OVERRIDE { return ID_Slider; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Slider) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Slider_Node(); } + Type type() const override { return Type::Slider; } + bool is_a(Type inType) const override { return (inType==Type::Slider) ? true : super::is_a(inType); } }; -static Fl_Slider_Type Fl_Slider_type; +Slider_Node Slider_Node::prototype; // ---- Scrollbar ---- static Fl_Menu_Item scrollbar_type_menu[] = { - { "Vertical", 0, 0, (void*)FL_VERT_SLIDER }, - { "Horizontal", 0, 0, (void*)FL_HOR_SLIDER }, - { 0 } + { "Vertical", 0, nullptr, (void*)nullptr }, + { "Horizontal", 0, nullptr, (void*)FL_HOR_SLIDER }, + { nullptr } }; /** \brief Manage Scrollbars which are derived from Sliders. */ -class Fl_Scrollbar_Type : public Fl_Slider_Type +class Scrollbar_Node : public Slider_Node { - typedef Fl_Slider_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return scrollbar_type_menu; } public: - const char *type_name() FL_OVERRIDE { return "Fl_Scrollbar"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Scrollbar"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Slider_Node super; + static Scrollbar_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return scrollbar_type_menu; } +public: + const char *type_name() override { return "Fl_Scrollbar"; } + const char *alt_type_name() override { return "fltk::Scrollbar"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Scrollbar(x, y, w, h); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Scrollbar_Type(); } - ID id() const FL_OVERRIDE { return ID_Scrollbar; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Scrollbar) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Scrollbar_Node(); } + Type type() const override { return Type::Scrollbar; } + bool is_a(Type inType) const override { return (inType==Type::Scrollbar) ? true : super::is_a(inType); } }; -static Fl_Scrollbar_Type Fl_Scrollbar_type; + +Scrollbar_Node Scrollbar_Node::prototype; // ---- Value Slider ---- @@ -536,11 +594,14 @@ static Fl_Scrollbar_Type Fl_Scrollbar_type; /** \brief Manage Value Sliders and their text settings. */ -class Fl_Value_Slider_Type : public Fl_Slider_Type +class Value_Slider_Node : public Slider_Node { - typedef Fl_Slider_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Value_Slider *myo = (Fl_Value_Slider*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Slider_Node super; + static Value_Slider_Node prototype; +private: + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Value_Slider *myo = (Fl_Value_Slider*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -551,17 +612,17 @@ class Fl_Value_Slider_Type : public Fl_Slider_Type return 1; } public: - const char *type_name() FL_OVERRIDE { return "Fl_Value_Slider"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::ValueSlider"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Value_Slider"; } + const char *alt_type_name() override { return "fltk::ValueSlider"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Value_Slider(x, y, w, h, "slider:"); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Value_Slider_Type(); } - ID id() const FL_OVERRIDE { return ID_Value_Slider; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Value_Slider) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Value_Slider_Node(); } + Type type() const override { return Type::Value_Slider; } + bool is_a(Type inType) const override { return (inType==Type::Value_Slider) ? true : super::is_a(inType); } }; -static Fl_Value_Slider_Type Fl_Value_Slider_type; +Value_Slider_Node Value_Slider_Node::prototype; // ---- Value Input ---- @@ -569,11 +630,14 @@ static Fl_Value_Slider_Type Fl_Value_Slider_type; /** \brief Manage Value Inputs and their text settings. */ -class Fl_Value_Input_Type : public Fl_Valuator_Type +class Value_Input_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Value_Input *myo = (Fl_Value_Input*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Valuator_Node super; + static Value_Input_Node prototype; +private: + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Value_Input *myo = (Fl_Value_Input*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -584,23 +648,24 @@ class Fl_Value_Input_Type : public Fl_Valuator_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() + 8; w = layout->textsize_not_null() * 4 + 8; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Value_Input"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::ValueInput"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Value_Input"; } + const char *alt_type_name() override { return "fltk::ValueInput"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Value_Input *myo = new Fl_Value_Input(x, y, w, h, "value:"); return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Value_Input_Type(); } - ID id() const FL_OVERRIDE { return ID_Value_Input; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Value_Input) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Value_Input_Node(); } + Type type() const override { return Type::Value_Input; } + bool is_a(Type inType) const override { return (inType==Type::Value_Input) ? true : super::is_a(inType); } }; -static Fl_Value_Input_Type Fl_Value_Input_type; +Value_Input_Node Value_Input_Node::prototype; // ---- Value Output ---- @@ -608,11 +673,14 @@ static Fl_Value_Input_Type Fl_Value_Input_type; /** \brief Handle Value Output widgets, no shortcut with Value Input unfortunately. */ -class Fl_Value_Output_Type : public Fl_Valuator_Type +class Value_Output_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Value_Output *myo = (Fl_Value_Output*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Valuator_Node super; + static Value_Output_Node prototype; +private: + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Value_Output *myo = (Fl_Value_Output*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -623,23 +691,24 @@ class Fl_Value_Output_Type : public Fl_Valuator_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() + 8; w = layout->textsize_not_null() * 4 + 8; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Value_Output"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::ValueOutput"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Value_Output"; } + const char *alt_type_name() override { return "fltk::ValueOutput"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Value_Output *myo = new Fl_Value_Output(x, y, w, h, "value:"); return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Value_Output_Type(); } - ID id() const FL_OVERRIDE { return ID_Value_Output; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Value_Output) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Value_Output_Node(); } + Type type() const override { return Type::Value_Output; } + bool is_a(Type inType) const override { return (inType==Type::Value_Output) ? true : super::is_a(inType); } }; -static Fl_Value_Output_Type Fl_Value_Output_type; +Value_Output_Node Value_Output_Node::prototype; @@ -649,25 +718,28 @@ static Fl_Value_Output_Type Fl_Value_Output_type; // ---- Input ---- static Fl_Menu_Item input_type_menu[] = { - { "Normal", 0, 0, (void*)FL_NORMAL_INPUT }, - { "Multiline", 0, 0, (void*)FL_MULTILINE_INPUT }, - { "Secret", 0, 0, (void*)FL_SECRET_INPUT }, - { "Int", 0, 0, (void*)FL_INT_INPUT }, - { "Float", 0, 0, (void*)FL_FLOAT_INPUT }, - {0} + { "Normal", 0, nullptr, (void*)nullptr }, + { "Multiline", 0, nullptr, (void*)FL_MULTILINE_INPUT }, + { "Secret", 0, nullptr, (void*)FL_SECRET_INPUT }, + { "Int", 0, nullptr, (void*)FL_INT_INPUT }, + { "Float", 0, nullptr, (void*)FL_FLOAT_INPUT }, + {nullptr} }; /** \brief Manage simple text input widgets. The managed class is derived from Fl_Input_, but for simplicity, deriving from - Fl_Widget_Type seems sufficient here. + Widget_Node seems sufficient here. */ -class Fl_Input_Type : public Fl_Widget_Type +class Input_Node : public Widget_Node { - typedef Fl_Widget_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return input_type_menu; } - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Input_ *myo = (Fl_Input_*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Widget_Node super; + static Input_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return input_type_menu; } + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Input_ *myo = (Fl_Input_*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -678,23 +750,24 @@ class Fl_Input_Type : public Fl_Widget_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() + 8; w = layout->textsize_not_null() * 6 + 8; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Input"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Input"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Input"; } + const char *alt_type_name() override { return "fltk::Input"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Input *myo = new Fl_Input(x, y, w, h, "input:"); myo->value("Text Input"); return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Input_Type(); } - ID id() const FL_OVERRIDE { return ID_Input; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Input) ? true : super::is_a(inID); } - void copy_properties() FL_OVERRIDE { - Fl_Widget_Type::copy_properties(); + Widget_Node *_make() override { return new Input_Node(); } + Type type() const override { return Type::Input; } + bool is_a(Type inType) const override { return (inType==Type::Input) ? true : super::is_a(inType); } + void copy_properties() override { + Widget_Node::copy_properties(); Fl_Input_ *d = (Fl_Input_*)live_widget, *s = (Fl_Input_*)o; d->textfont(s->textfont()); d->textsize(s->textsize()); @@ -702,7 +775,8 @@ public: d->shortcut(s->shortcut()); } }; -static Fl_Input_Type Fl_Input_type; + +Input_Node Input_Node::prototype; // ---- File Input ---- @@ -710,60 +784,67 @@ static Fl_Input_Type Fl_Input_type; /** \brief Manage file name input widgets. */ -class Fl_File_Input_Type : public Fl_Input_Type +class File_Input_Node : public Input_Node { - typedef Fl_Input_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return NULL; } // Don't inherit. public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Input_Node super; + static File_Input_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return nullptr; } // Don't inherit. +public: + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() + 8 + 10; // Directoy bar is additional 10 pixels high w = layout->textsize_not_null() * 10 + 8; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_File_Input"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::FileInput"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_File_Input"; } + const char *alt_type_name() override { return "fltk::FileInput"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_File_Input *myo = new Fl_File_Input(x, y, w, h, "file:"); myo->value("/usr/include/FL/Fl.H"); return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_File_Input_Type(); } - ID id() const FL_OVERRIDE { return ID_File_Input; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_File_Input) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new File_Input_Node(); } + Type type() const override { return Type::File_Input; } + bool is_a(Type inType) const override { return (inType==Type::File_Input) ? true : super::is_a(inType); } }; -static Fl_File_Input_Type Fl_File_Input_type; +File_Input_Node File_Input_Node::prototype; // ---- Output ---- static Fl_Menu_Item output_type_menu[] = { - { "Normal", 0, 0, (void*)FL_NORMAL_OUTPUT }, - { "Multiline", 0, 0, (void*)FL_MULTILINE_OUTPUT }, - { 0 } + { "Normal", 0, nullptr, (void*)FL_NORMAL_OUTPUT }, + { "Multiline", 0, nullptr, (void*)FL_MULTILINE_OUTPUT }, + { nullptr } }; /** \brief Manage Output widgets, derived from Input. */ -class Fl_Output_Type : public Fl_Input_Type +class Output_Node : public Input_Node { - typedef Fl_Input_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return output_type_menu; } public: - const char *type_name() FL_OVERRIDE { return "Fl_Output"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Output"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Input_Node super; + static Output_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return output_type_menu; } +public: + const char *type_name() override { return "Fl_Output"; } + const char *alt_type_name() override { return "fltk::Output"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Output *myo = new Fl_Output(x, y, w, h, "output:"); myo->value("Text Output"); return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Output_Type(); } - ID id() const FL_OVERRIDE { return ID_Output; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Output) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Output_Node(); } + Type type() const override { return Type::Output; } + bool is_a(Type inType) const override { return (inType==Type::Output) ? true : super::is_a(inType); } }; -static Fl_Output_Type Fl_Output_type; +Output_Node Output_Node::prototype; @@ -777,11 +858,14 @@ static Fl_Output_Type Fl_Output_type; Fl_Text_Display is actually derived from Fl_Group, but for FLUID, deriving the type from Widget is better. */ -class Fl_Text_Display_Type : public Fl_Widget_Type +class Text_Display_Node : public Widget_Node { - typedef Fl_Widget_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Text_Display *myo = (Fl_Text_Display*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Widget_Node super; + static Text_Display_Node prototype; +private: + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Text_Display *myo = (Fl_Text_Display*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -792,27 +876,29 @@ class Fl_Text_Display_Type : public Fl_Widget_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() * 4 + 8; w = layout->textsize_not_null() * 10 + 8; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Text_Display"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::TextDisplay"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Text_Display"; } + const char *alt_type_name() override { return "fltk::TextDisplay"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Text_Display *myo = new Fl_Text_Display(x, y, w, h); - if (!batch_mode) { + if (!Fluid.batch_mode) { Fl_Text_Buffer *b = new Fl_Text_Buffer(); b->text("Lorem ipsum dolor\nsit amet, consetetur\nsadipscing elitr"); myo->buffer(b); } return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Text_Display_Type(); } - ID id() const FL_OVERRIDE { return ID_Text_Display; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Text_Display) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Text_Display_Node(); } + Type type() const override { return Type::Text_Display; } + bool is_a(Type inType) const override { return (inType==Type::Text_Display) ? true : super::is_a(inType); } }; -static Fl_Text_Display_Type Fl_Text_Display_type; + +Text_Display_Node Text_Display_Node::prototype; // ---- Text Editor ---- @@ -820,27 +906,29 @@ static Fl_Text_Display_Type Fl_Text_Display_type; /** \brief Manage Text Editors based on Text Display. */ -class Fl_Text_Editor_Type : public Fl_Text_Display_Type +class Text_Editor_Node : public Text_Display_Node { - typedef Fl_Text_Display_Type super; public: - const char *type_name() FL_OVERRIDE {return "Fl_Text_Editor";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::TextEditor";} - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Text_Display_Node super; + static Text_Editor_Node prototype; +public: + const char *type_name() override {return "Fl_Text_Editor";} + const char *alt_type_name() override {return "fltk::TextEditor";} + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Text_Editor *myo = new Fl_Text_Editor(x, y, w, h); - if (!batch_mode) { + if (!Fluid.batch_mode) { Fl_Text_Buffer *b = new Fl_Text_Buffer(); b->text("Lorem ipsum dolor\nsit amet, consetetur\nsadipscing elitr"); myo->buffer(b); } return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Text_Editor_Type(); } - ID id() const FL_OVERRIDE { return ID_Text_Editor; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Text_Editor) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Text_Editor_Node(); } + Type type() const override { return Type::Text_Editor; } + bool is_a(Type inType) const override { return (inType==Type::Text_Editor) ? true : super::is_a(inType); } }; -static Fl_Text_Editor_Type Fl_Text_Editor_type; +Text_Editor_Node Text_Editor_Node::prototype; // ---- Terminal ---- @@ -848,13 +936,13 @@ static Fl_Text_Editor_Type Fl_Text_Editor_type; /** Use this terminal instead of Fl_Terminal to capture resize actions. */ class Fl_Terminal_Proxy : public Fl_Terminal { public: - Fl_Terminal_Proxy(int x, int y, int w, int h, const char *l=NULL) + Fl_Terminal_Proxy(int x, int y, int w, int h, const char *l=nullptr) : Fl_Terminal(x, y, w, h, l) { } void print_sample_text() { clear_screen_home(false); append("> ls -als"); } - void resize(int x, int y, int w, int h) FL_OVERRIDE { + void resize(int x, int y, int w, int h) override { Fl_Terminal::resize(x, y, w, h); // After a resize, the top text vanishes, so make sure we redraw it. print_sample_text(); @@ -867,7 +955,7 @@ public: Fl_Font tfont_; int tsize_; Fl_Color tcolor_; - Fl_Batchmode_Terminal(int x, int y, int w, int h, const char *l=NULL) + Fl_Batchmode_Terminal(int x, int y, int w, int h, const char *l=nullptr) : Fl_Group(x, y, w, h, l) { // set the defaults that Fl_Terminal would set box(FL_DOWN_BOX); @@ -889,16 +977,18 @@ public: /** \brief Manage a terminal widget. */ -class Fl_Terminal_Type : public Fl_Widget_Type +class Terminal_Node : public Widget_Node { - typedef Fl_Widget_Type super; public: - const char *type_name() FL_OVERRIDE { return "Fl_Terminal"; } + typedef Widget_Node super; + static Terminal_Node prototype; +public: + const char *type_name() override { return "Fl_Terminal"; } // Older .fl files with Fl_Simple_Terminal will create a Fl_Terminal instead. - const char *alt_type_name() FL_OVERRIDE { return "Fl_Simple_Terminal"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { - Fl_Widget *ret = NULL; - if (batch_mode) { + const char *alt_type_name() override { return "Fl_Simple_Terminal"; } + Fl_Widget *widget(int x, int y, int w, int h) override { + Fl_Widget *ret = nullptr; + if (Fluid.batch_mode) { ret = new Fl_Batchmode_Terminal(x, y, w, h); } else { Fl_Terminal_Proxy *term = new Fl_Terminal_Proxy(x, y, w+100, h); @@ -906,9 +996,9 @@ public: } return ret; } - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - if (batch_mode) { - Fl_Batchmode_Terminal *myo = (Fl_Batchmode_Terminal*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + if (Fluid.batch_mode) { + Fl_Batchmode_Terminal *myo = (Fl_Batchmode_Terminal*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = (Fl_Font)myo->tfont_; s = myo->tsize_; c = myo->tcolor_; break; @@ -917,7 +1007,7 @@ public: case 3: myo->tcolor_ = c; break; } } else { - Fl_Terminal_Proxy *myo = (Fl_Terminal_Proxy*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); + Fl_Terminal_Proxy *myo = (Fl_Terminal_Proxy*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = (Fl_Font)myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -928,12 +1018,12 @@ public: } return 1; } - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Terminal_Type();} - ID id() const FL_OVERRIDE { return ID_Terminal; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Terminal) ? true : super::is_a(inID); } + Widget_Node *_make() override {return new Terminal_Node();} + Type type() const override { return Type::Terminal; } + bool is_a(Type inType) const override { return (inType==Type::Terminal) ? true : super::is_a(inType); } }; -static Fl_Terminal_Type Fl_Terminal_type; +Terminal_Node Terminal_Node::prototype; // ---- Other ---------------------------------------------------------- MARK: - @@ -945,25 +1035,27 @@ static Fl_Terminal_Type Fl_Terminal_type; \brief Manage box widgets. Ideal size is set to 100x100, snapped to layout. */ -class Fl_Box_Type : public Fl_Widget_Type +class Box_Node : public Widget_Node { - typedef Fl_Widget_Type super; public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Widget_Node super; + static Box_Node prototype; +public: + void ideal_size(int &w, int &h) override { w = 100; h = 100; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Box"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Widget"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Box"; } + const char *alt_type_name() override { return "fltk::Widget"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Box(x, y, w, h, "label"); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Box_Type(); } - ID id() const FL_OVERRIDE { return ID_Box; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Box) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Box_Node(); } + Type type() const override { return Type::Box; } + bool is_a(Type inType) const override { return (inType==Type::Box) ? true : super::is_a(inType); } }; -static Fl_Box_Type Fl_Box_type; +Box_Node Box_Node::prototype; // ---- Clock ---- @@ -972,25 +1064,27 @@ static Fl_Box_Type Fl_Box_type; \brief Manage Clock widgets. Ideal size is set to 80x80 snapped to layout. */ -class Fl_Clock_Type : public Fl_Widget_Type +class Clock_Node : public Widget_Node { - typedef Fl_Widget_Type super; public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Widget_Node super; + static Clock_Node prototype; +public: + void ideal_size(int &w, int &h) override { w = 80; h = 80; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Clock"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Clock"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Clock"; } + const char *alt_type_name() override { return "fltk::Clock"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Clock(x, y, w, h); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Clock_Type(); } - ID id() const FL_OVERRIDE { return ID_Clock; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Clock) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Clock_Node(); } + Type type() const override { return Type::Clock; } + bool is_a(Type inType) const override { return (inType==Type::Clock) ? true : super::is_a(inType); } }; -static Fl_Clock_Type Fl_Clock_type; +Clock_Node Clock_Node::prototype; // ---- Progress ---- @@ -1000,35 +1094,38 @@ static Fl_Clock_Type Fl_Clock_type; Ideal size is set to match the label font and label text width times 3. \note minimum, maximum, and value must be set via extra code fields. */ -class Fl_Progress_Type : public Fl_Widget_Type +class Progress_Node : public Widget_Node { - typedef Fl_Widget_Type super; public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Widget_Node super; + static Progress_Node prototype; +public: + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->labelsize + 8; w = layout->labelsize * 12; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Progress"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::ProgressBar"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Progress"; } + const char *alt_type_name() override { return "fltk::ProgressBar"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Progress *myo = new Fl_Progress(x, y, w, h, "label"); myo->value(50); return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Progress_Type(); } - ID id() const FL_OVERRIDE { return ID_Progress; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Progress) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Progress_Node(); } + Type type() const override { return Type::Progress; } + bool is_a(Type inType) const override { return (inType==Type::Progress) ? true : super::is_a(inType); } }; -static Fl_Progress_Type Fl_Progress_type; +Progress_Node Progress_Node::prototype; // ---- Spinner ---- static Fl_Menu_Item spinner_type_menu[] = { - { "Integer", 0, 0, (void*)FL_INT_INPUT }, - { "Float", 0, 0, (void*)FL_FLOAT_INPUT }, - { 0 } + { "Integer", 0, nullptr, (void*)FL_INT_INPUT }, + { "Float", 0, nullptr, (void*)FL_FLOAT_INPUT }, + { nullptr } }; /** @@ -1036,12 +1133,15 @@ static Fl_Menu_Item spinner_type_menu[] = { \note Fl_Spinner is derived from Fl_Group, *not* Fl_Valuator as one may expect. For FLUID, this means some special handling and no Group support. */ -class Fl_Spinner_Type : public Fl_Widget_Type +class Spinner_Node : public Widget_Node { - typedef Fl_Widget_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return spinner_type_menu; } - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Spinner *myo = (Fl_Spinner*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Widget_Node super; + static Spinner_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return spinner_type_menu; } + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Spinner *myo = (Fl_Spinner*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = (Fl_Font)myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -1052,64 +1152,30 @@ class Fl_Spinner_Type : public Fl_Widget_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() + 8; w = layout->textsize_not_null() * 4 + 8; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Spinner"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Spinner"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Spinner"; } + const char *alt_type_name() override { return "fltk::Spinner"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Spinner(x, y, w, h, "spinner:"); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Spinner_Type(); } - ID id() const FL_OVERRIDE { return ID_Spinner; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Spinner) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Spinner_Node(); } + Type type() const override { return Type::Spinner; } + bool is_a(Type inType) const override { return (inType==Type::Spinner) ? true : super::is_a(inType); } }; -static Fl_Spinner_Type Fl_Spinner_type; +Spinner_Node Spinner_Node::prototype; // ---- Type Factory --------------------------------------------------- MARK: - -extern class Fl_Function_Type Fl_Function_type; -extern class Fl_Code_Type Fl_Code_type; -extern class Fl_CodeBlock_Type Fl_CodeBlock_type; -extern class Fl_Data_Type Fl_Data_type; -extern class Fl_Decl_Type Fl_Decl_type; -extern class Fl_DeclBlock_Type Fl_DeclBlock_type; -extern class Fl_Comment_Type Fl_Comment_type; -extern class Fl_Class_Type Fl_Class_type; -extern class Fl_Window_Type Fl_Window_type; -extern class Fl_Widget_Class_Type Fl_Widget_Class_type; -extern class Fl_Group_Type Fl_Group_type; -extern class Fl_Pack_Type Fl_Pack_type; -extern class Fl_Flex_Type Fl_Flex_type; -extern class Fl_Grid_Type Fl_Grid_type; -extern class Fl_Tabs_Type Fl_Tabs_type; -extern class Fl_Scroll_Type Fl_Scroll_type; -extern class Fl_Table_Type Fl_Table_type; -extern class Fl_Tile_Type Fl_Tile_type; -extern class Fl_Input_Choice_Type Fl_Input_Choice_type; -extern class Fl_Choice_Type Fl_Choice_type; -extern class Fl_Menu_Bar_Type Fl_Menu_Bar_type; -extern class Fl_Menu_Button_Type Fl_Menu_Button_type; -extern class Fl_Menu_Item_Type Fl_Menu_Item_type; -extern class Fl_Checkbox_Menu_Item_Type Fl_Checkbox_Menu_Item_type; -extern class Fl_Radio_Menu_Item_Type Fl_Radio_Menu_Item_type; -extern class Fl_Submenu_Type Fl_Submenu_type; -extern class Fl_Wizard_Type Fl_Wizard_type; - -extern class Fl_Button_Type Fl_Button_type; -extern class Fl_Return_Button_Type Fl_Return_Button_type; -extern class Fl_Light_Button_Type Fl_Light_Button_type; -extern class Fl_Check_Button_Type Fl_Check_Button_type; -extern class Fl_Repeat_Button_Type Fl_Repeat_Button_type; -extern class Fl_Round_Button_Type Fl_Round_Button_type; - -extern void select(Fl_Type *,int); -extern void select_only(Fl_Type *); +extern void select(Node *,int); +extern void select_only(Node *); /** List all known types. @@ -1120,72 +1186,72 @@ extern void select_only(Fl_Type *); \note Make sure that this array stays synchronized to `Fl_Menu_Item New_Menu[]` further down in this file. */ -static Fl_Type *known_types[] = { +static Node *known_types[] = { // functions - (Fl_Type*)&Fl_Function_type, - (Fl_Type*)&Fl_Code_type, - (Fl_Type*)&Fl_CodeBlock_type, - (Fl_Type*)&Fl_Decl_type, - (Fl_Type*)&Fl_DeclBlock_type, - (Fl_Type*)&Fl_Class_type, - (Fl_Type*)&Fl_Widget_Class_type, - (Fl_Type*)&Fl_Comment_type, - (Fl_Type*)&Fl_Data_type, + (Node*)&Function_Node::prototype, + (Node*)&Code_Node::prototype, + (Node*)&CodeBlock_Node::prototype, + (Node*)&Decl_Node::prototype, + (Node*)&DeclBlock_Node::prototype, + (Node*)&Class_Node::prototype, + (Node*)&Widget_Class_Node::prototype, + (Node*)&Comment_Node::prototype, + (Node*)&Data_Node::prototype, // groups - (Fl_Type*)&Fl_Window_type, - (Fl_Type*)&Fl_Group_type, - (Fl_Type*)&Fl_Pack_type, - (Fl_Type*)&Fl_Flex_type, - (Fl_Type*)&Fl_Tabs_type, - (Fl_Type*)&Fl_Scroll_type, - (Fl_Type*)&Fl_Tile_type, - (Fl_Type*)&Fl_Wizard_type, - (Fl_Type*)&Fl_Grid_type, + (Node*)&Window_Node::prototype, + (Node*)&Group_Node::prototype, + (Node*)&Pack_Node::prototype, + (Node*)&Flex_Node::prototype, + (Node*)&Tabs_Node::prototype, + (Node*)&Scroll_Node::prototype, + (Node*)&Tile_Node::prototype, + (Node*)&Wizard_Node::prototype, + (Node*)&Grid_Node::prototype, // buttons - (Fl_Type*)&Fl_Button_type, - (Fl_Type*)&Fl_Return_Button_type, - (Fl_Type*)&Fl_Light_Button_type, - (Fl_Type*)&Fl_Check_Button_type, - (Fl_Type*)&Fl_Repeat_Button_type, - (Fl_Type*)&Fl_Round_Button_type, + (Node*)&Button_Node::prototype, + (Node*)&Return_Button_Node::prototype, + (Node*)&Light_Button_Node::prototype, + (Node*)&Check_Button_Node::prototype, + (Node*)&Repeat_Button_Node::prototype, + (Node*)&Round_Button_Node::prototype, // valuators - (Fl_Type*)&Fl_Slider_type, - (Fl_Type*)&Fl_Scrollbar_type, - (Fl_Type*)&Fl_Value_Slider_type, - (Fl_Type*)&Fl_Adjuster_type, - (Fl_Type*)&Fl_Counter_type, - (Fl_Type*)&Fl_Spinner_type, - (Fl_Type*)&Fl_Dial_type, - (Fl_Type*)&Fl_Roller_type, - (Fl_Type*)&Fl_Value_Input_type, - (Fl_Type*)&Fl_Value_Output_type, + (Node*)&Slider_Node::prototype, + (Node*)&Scrollbar_Node::prototype, + (Node*)&Value_Slider_Node::prototype, + (Node*)&Adjuster_Node::prototype, + (Node*)&Counter_Node::prototype, + (Node*)&Spinner_Node::prototype, + (Node*)&Dial_Node::prototype, + (Node*)&Roller_Node::prototype, + (Node*)&Value_Input_Node::prototype, + (Node*)&Value_Output_Node::prototype, // text - (Fl_Type*)&Fl_Input_type, - (Fl_Type*)&Fl_Output_type, - (Fl_Type*)&Fl_Text_Editor_type, - (Fl_Type*)&Fl_Text_Display_type, - (Fl_Type*)&Fl_File_Input_type, - (Fl_Type*)&Fl_Terminal_type, + (Node*)&Input_Node::prototype, + (Node*)&Output_Node::prototype, + (Node*)&Text_Editor_Node::prototype, + (Node*)&Text_Display_Node::prototype, + (Node*)&File_Input_Node::prototype, + (Node*)&Terminal_Node::prototype, // menus - (Fl_Type*)&Fl_Menu_Bar_type, - (Fl_Type*)&Fl_Menu_Button_type, - (Fl_Type*)&Fl_Choice_type, - (Fl_Type*)&Fl_Input_Choice_type, - (Fl_Type*)&Fl_Submenu_type, - (Fl_Type*)&Fl_Menu_Item_type, - (Fl_Type*)&Fl_Checkbox_Menu_Item_type, - (Fl_Type*)&Fl_Radio_Menu_Item_type, + (Node*)&Menu_Bar_Node::prototype, + (Node*)&Menu_Button_Node::prototype, + (Node*)&Choice_Node::prototype, + (Node*)&Input_Choice_Node::prototype, + (Node*)&Submenu_Node::prototype, + (Node*)&Menu_Item_Node::prototype, + (Node*)&Checkbox_Menu_Item_Node::prototype, + (Node*)&Radio_Menu_Item_Node::prototype, // browsers - (Fl_Type*)&Fl_Browser_type, - (Fl_Type*)&Fl_Check_Browser_type, - (Fl_Type*)&Fl_File_Browser_type, - (Fl_Type*)&Fl_Tree_type, - (Fl_Type*)&Fl_Help_View_type, - (Fl_Type*)&Fl_Table_type, + (Node*)&Browser_Node::prototype, + (Node*)&Check_Browser_Node::prototype, + (Node*)&File_Browser_Node::prototype, + (Node*)&Tree_Node::prototype, + (Node*)&Help_View_Node::prototype, + (Node*)&Table_Node::prototype, // misc - (Fl_Type*)&Fl_Box_type, - (Fl_Type*)&Fl_Clock_type, - (Fl_Type*)&Fl_Progress_type, + (Node*)&Box_Node::prototype, + (Node*)&Clock_Node::prototype, + (Node*)&Progress_Node::prototype, }; /** @@ -1207,19 +1273,21 @@ static Fl_Type *known_types[] = { lower case 't' in type. \param[in] strategy add after current or as last child \param[in] and_open if set to true, call open() on the widget after creating it - \return the newly created type or NULL + \return the newly created type or nullptr \see add_new_widget_from_file(const char*, int) - add_new_widget_from_user(Fl_Type*, int) + add_new_widget_from_user(Node*, int) add_new_widget_from_user(const char*, int) */ -Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy, bool and_open) { - undo_checkpoint(); - undo_suspend(); - Fl_Type *t = ((Fl_Type*)inPrototype)->make(strategy); +Node *add_new_widget_from_user(Node *inPrototype, Strategy strategy, bool and_open) { + Fluid.proj.undo.checkpoint(); + Fluid.proj.undo.suspend(); + auto layout = Fluid.proj.layout; + Node *t = ((Node*)inPrototype)->make(strategy); if (t) { - if (t->is_widget() && !t->is_a(ID_Window)) { - Fl_Widget_Type *wt = (Fl_Widget_Type *)t; + if (t->is_widget() && !t->is_a(Type::Window)) { + auto layout = Fluid.proj.layout; + Widget_Node *wt = (Widget_Node *)t; bool changed = false; // Set font sizes... @@ -1244,71 +1312,71 @@ Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy, bool wt->textstuff(2, f, s, c); } - if (changed && t->is_a(ID_Menu_Item)) { - Fl_Type * tt = t->parent; - while (tt && !tt->is_a(ID_Menu_Manager_)) tt = tt->parent; + if (changed && t->is_a(Type::Menu_Item)) { + Node * tt = t->parent; + while (tt && !tt->is_a(Type::Menu_Manager_)) tt = tt->parent; if (tt) - ((Fl_Menu_Manager_Type*)tt)->build_menu(); + ((Menu_Manager_Node*)tt)->build_menu(); } } - if (t->is_true_widget() && !t->is_a(ID_Window)) { + if (t->is_true_widget() && !t->is_a(Type::Window)) { // Resize and/or reposition new widget... - Fl_Widget_Type *wt = (Fl_Widget_Type *)t; + Widget_Node *wt = (Widget_Node *)t; // The parent field is already set at this point, so we can use that // inside ideal_size(). int w = 0, h = 0; wt->ideal_size(w, h); - if ((t->parent && t->parent->is_a(ID_Flex))) { - if (Fl_Window_Type::popupx != 0x7FFFFFFF) - ((Fl_Flex_Type*)t->parent)->insert_child_at(((Fl_Widget_Type*)t)->o, Fl_Window_Type::popupx, Fl_Window_Type::popupy); + if ((t->parent && t->parent->is_a(Type::Flex))) { + if (Window_Node::popupx != 0x7FFFFFFF) + ((Flex_Node*)t->parent)->insert_child_at(((Widget_Node*)t)->o, Window_Node::popupx, Window_Node::popupy); t->parent->layout_widget(); - } else if ( wt->is_a(ID_Group) + } else if ( wt->is_a(Type::Group) && wt->parent - && wt->parent->is_a(ID_Tabs) - //&& (Fl_Window_Type::popupx == 0x7FFFFFFF) + && wt->parent->is_a(Type::Tabs) + //&& (Window_Node::popupx == 0x7FFFFFFF) && (layout->top_tabs_margin > 0)) { // If the widget is a group and the parent is tabs and the top tabs // margin is set (and the user is not requesting a specific position) // then prefit the group correctly to the Tabs container. - Fl_Widget *po = ((Fl_Tabs_Type*)wt->parent)->o; + Fl_Widget *po = ((Tabs_Node*)wt->parent)->o; wt->o->resize(po->x(), po->y() + layout->top_tabs_margin, po->w(), po->h() - layout->top_tabs_margin); - } else if ( wt->is_a(ID_Menu_Bar) + } else if ( wt->is_a(Type::Menu_Bar) && wt->parent - && wt->parent->is_a(ID_Window) + && wt->parent->is_a(Type::Window) && (wt->prev == wt->parent)) { // If this is the first child of a window, make the menu bar as wide as // the window and drop it at 0, 0. Otherwise just use the suggested size. w = wt->o->window()->w(); wt->o->resize(0, 0, w, h); } else { - if (Fl_Window_Type::popupx != 0x7FFFFFFF) { + if (Window_Node::popupx != 0x7FFFFFFF) { // If this callback was called from the RMB popup menu in a window, // popupx and popupy will contain the mouse coordinates at RMB event. - wt->o->resize(Fl_Window_Type::popupx, Fl_Window_Type::popupy, w, h); + wt->o->resize(Window_Node::popupx, Window_Node::popupy, w, h); } else { // If popupx is invalid, use the default position and find a good // size for the widget. wt->o->size(w, h); } } - if (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 (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); } } } - if (t->is_a(ID_Window)) { + if (t->is_a(Type::Window)) { int x = 0, y = 0, w = 480, h = 320; - Fl_Window_Type *wt = (Fl_Window_Type *)t; + Window_Node *wt = (Window_Node *)t; wt->ideal_size(w, h); - if (main_window) { + if (Fluid.main_window) { int sx, sy, sw, sh; - Fl_Window *win = main_window; + Fl_Window *win = Fluid.main_window; int screen = Fl::screen_num(win->x(), win->y()); Fl::screen_work_area(sx, sy, sw, sh, screen); x = sx + sw/2 - w/2; @@ -1318,14 +1386,14 @@ Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy, bool } // make the new widget visible select_only(t); - set_modflag(1); + Fluid.proj.set_modflag(1); if (and_open) t->open(); } else { - undo_current --; - undo_last --; + Fluid.proj.undo.current_ --; + Fluid.proj.undo.last_ --; } - undo_resume(); + Fluid.proj.undo.resume(); return t; } @@ -1335,126 +1403,126 @@ Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy, bool \param[in] inName find the right prototype by this name \param[in] strategy where to add the node \param[in] and_open if set to true, call open() on the widget after creating it - \return the newly created type or NULL + \return the newly created type or nullptr \see add_new_widget_from_file(const char*, int) - add_new_widget_from_user(Fl_Type*, int) + add_new_widget_from_user(Node*, int) add_new_widget_from_user(const char*, int) */ -Fl_Type *add_new_widget_from_user(const char *inName, Strategy strategy, bool and_open) { - Fl_Type *prototype = typename_to_prototype(inName); +Node *add_new_widget_from_user(const char *inName, Strategy strategy, bool and_open) { + Node *prototype = typename_to_prototype(inName); if (prototype) return add_new_widget_from_user(prototype, strategy, and_open); else - return NULL; + return nullptr; } /** Callback for all non-widget menu items. */ static void cbf(Fl_Widget *, void *v) { - Fl_Type *t = NULL; - if (Fl_Type::current && Fl_Type::current->can_have_children()) - t = ((Fl_Type*)v)->make(Strategy::AS_LAST_CHILD); + Node *t = nullptr; + if (Fluid.proj.tree.current && Fluid.proj.tree.current->can_have_children()) + t = ((Node*)v)->make(Strategy::AS_LAST_CHILD); else - t = ((Fl_Type*)v)->make(Strategy::AFTER_CURRENT); + t = ((Node*)v)->make(Strategy::AFTER_CURRENT); select_only(t); } /** Callback for all widget menu items. - \param[in] v cast to Fl_Type to get the prototype of the type that the user + \param[in] v cast to Node to get the prototype of the type that the user wants to create. */ static void cb(Fl_Widget *, void *v) { - Fl_Type *t = NULL; - if (Fl_Type::current && Fl_Type::current->can_have_children()) - t = add_new_widget_from_user((Fl_Type*)v, Strategy::AS_LAST_CHILD); + Node *t = nullptr; + if (Fluid.proj.tree.current && Fluid.proj.tree.current->can_have_children()) + t = add_new_widget_from_user((Node*)v, Strategy::AS_LAST_CHILD); else - t = add_new_widget_from_user((Fl_Type*)v, Strategy::AFTER_CURRENT); + t = add_new_widget_from_user((Node*)v, Strategy::AFTER_CURRENT); select_only(t); } /** - \note Make sure that this menu stays synchronized to `Fl_Type *known_types[]` + \note Make sure that this menu stays synchronized to `Node *known_types[]` defined further up in this file. */ Fl_Menu_Item New_Menu[] = { -{"Code",0,0,0,FL_SUBMENU}, - {"Function/Method",0,cbf,(void*)&Fl_Function_type}, - {"Code",0,cbf,(void*)&Fl_Code_type}, - {"Code Block",0,cbf,(void*)&Fl_CodeBlock_type}, - {"Declaration",0,cbf,(void*)&Fl_Decl_type}, - {"Declaration Block",0,cbf,(void*)&Fl_DeclBlock_type}, - {"Class",0,cbf,(void*)&Fl_Class_type}, - {"Widget Class",0,cb,(void*)&Fl_Widget_Class_type}, - {"Comment",0,cbf,(void*)&Fl_Comment_type}, - {"Inlined Data",0,cbf,(void*)&Fl_Data_type}, -{0}, -{"Group",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Window_type}, - {0,0,cb,(void*)&Fl_Group_type}, - {0,0,cb,(void*)&Fl_Pack_type}, - {0,0,cb,(void*)&Fl_Flex_type}, - {0,0,cb,(void*)&Fl_Tabs_type}, - {0,0,cb,(void*)&Fl_Scroll_type}, - {0,0,cb,(void*)&Fl_Tile_type}, - {0,0,cb,(void*)&Fl_Wizard_type}, - {0,0,cb,(void*)&Fl_Grid_type}, -{0}, -{"Buttons",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Button_type}, - {0,0,cb,(void*)&Fl_Return_Button_type}, - {0,0,cb,(void*)&Fl_Light_Button_type}, - {0,0,cb,(void*)&Fl_Check_Button_type}, - {0,0,cb,(void*)&Fl_Repeat_Button_type}, - {0,0,cb,(void*)&Fl_Round_Button_type}, -{0}, -{"Valuators",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Slider_type}, - {0,0,cb,(void*)&Fl_Scrollbar_type}, - {0,0,cb,(void*)&Fl_Value_Slider_type}, - {0,0,cb,(void*)&Fl_Adjuster_type}, - {0,0,cb,(void*)&Fl_Counter_type}, - {0,0,cb,(void*)&Fl_Spinner_type}, - {0,0,cb,(void*)&Fl_Dial_type}, - {0,0,cb,(void*)&Fl_Roller_type}, - {0,0,cb,(void*)&Fl_Value_Input_type}, - {0,0,cb,(void*)&Fl_Value_Output_type}, -{0}, -{"Text",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Input_type}, - {0,0,cb,(void*)&Fl_Output_type}, - {0,0,cb,(void*)&Fl_Text_Editor_type}, - {0,0,cb,(void*)&Fl_Text_Display_type}, - {0,0,cb,(void*)&Fl_File_Input_type}, - {0,0,cb,(void*)&Fl_Terminal_type}, -{0}, -{"Menus",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Menu_Bar_type}, - {0,0,cb,(void*)&Fl_Menu_Button_type}, - {0,0,cb,(void*)&Fl_Choice_type}, - {0,0,cb,(void*)&Fl_Input_Choice_type}, - {0,0,cb, (void*)&Fl_Submenu_type}, - {0,0,cb, (void*)&Fl_Menu_Item_type}, - {"Checkbox Menu Item",0,cb, (void*)&Fl_Checkbox_Menu_Item_type}, - {"Radio Menu Item",0,cb, (void*)&Fl_Radio_Menu_Item_type}, -{0}, -{"Browsers",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Browser_type}, - {0,0,cb,(void*)&Fl_Check_Browser_type}, - {0,0,cb,(void*)&Fl_File_Browser_type}, - {0,0,cb,(void*)&Fl_Tree_type}, - {0,0,cb,(void*)&Fl_Help_View_type}, - {0,0,cb,(void*)&Fl_Table_type}, -{0}, -{"Other",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Box_type}, - {0,0,cb,(void*)&Fl_Clock_type}, - {0,0,cb,(void*)&Fl_Progress_type}, -{0}, -{0}}; + {"Code",0,nullptr,nullptr,FL_SUBMENU}, + {"Function/Method",0,cbf,(void*)&Function_Node::prototype}, + {"Code",0,cbf,(void*)&Code_Node::prototype}, + {"Code Block",0,cbf,(void*)&CodeBlock_Node::prototype}, + {"Declaration",0,cbf,(void*)&Decl_Node::prototype}, + {"Declaration Block",0,cbf,(void*)&DeclBlock_Node::prototype}, + {"Class",0,cbf,(void*)&Class_Node::prototype}, + {"Widget Class",0,cb,(void*)&Widget_Class_Node::prototype}, + {"Comment",0,cbf,(void*)&Comment_Node::prototype}, + {"Inlined Data",0,cbf,(void*)&Data_Node::prototype}, + {nullptr}, + {"Group",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Window_Node::prototype}, + {nullptr,0,cb,(void*)&Group_Node::prototype}, + {nullptr,0,cb,(void*)&Pack_Node::prototype}, + {nullptr,0,cb,(void*)&Flex_Node::prototype}, + {nullptr,0,cb,(void*)&Tabs_Node::prototype}, + {nullptr,0,cb,(void*)&Scroll_Node::prototype}, + {nullptr,0,cb,(void*)&Tile_Node::prototype}, + {nullptr,0,cb,(void*)&Wizard_Node::prototype}, + {nullptr,0,cb,(void*)&Grid_Node::prototype}, + {nullptr}, + {"Buttons",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Button_Node::prototype}, + {nullptr,0,cb,(void*)&Return_Button_Node::prototype}, + {nullptr,0,cb,(void*)&Light_Button_Node::prototype}, + {nullptr,0,cb,(void*)&Check_Button_Node::prototype}, + {nullptr,0,cb,(void*)&Repeat_Button_Node::prototype}, + {nullptr,0,cb,(void*)&Round_Button_Node::prototype}, + {nullptr}, + {"Valuators",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Slider_Node::prototype}, + {nullptr,0,cb,(void*)&Scrollbar_Node::prototype}, + {nullptr,0,cb,(void*)&Value_Slider_Node::prototype}, + {nullptr,0,cb,(void*)&Adjuster_Node::prototype}, + {nullptr,0,cb,(void*)&Counter_Node::prototype}, + {nullptr,0,cb,(void*)&Spinner_Node::prototype}, + {nullptr,0,cb,(void*)&Dial_Node::prototype}, + {nullptr,0,cb,(void*)&Roller_Node::prototype}, + {nullptr,0,cb,(void*)&Value_Input_Node::prototype}, + {nullptr,0,cb,(void*)&Value_Output_Node::prototype}, + {nullptr}, + {"Text",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Input_Node::prototype}, + {nullptr,0,cb,(void*)&Output_Node::prototype}, + {nullptr,0,cb,(void*)&Text_Editor_Node::prototype}, + {nullptr,0,cb,(void*)&Text_Display_Node::prototype}, + {nullptr,0,cb,(void*)&File_Input_Node::prototype}, + {nullptr,0,cb,(void*)&Terminal_Node::prototype}, + {nullptr}, + {"Menus",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Menu_Bar_Node::prototype}, + {nullptr,0,cb,(void*)&Menu_Button_Node::prototype}, + {nullptr,0,cb,(void*)&Choice_Node::prototype}, + {nullptr,0,cb,(void*)&Input_Choice_Node::prototype}, + {nullptr,0,cb, (void*)&Submenu_Node::prototype}, + {nullptr,0,cb, (void*)&Menu_Item_Node::prototype}, + {"Checkbox Menu Item",0,cb, (void*)&Checkbox_Menu_Item_Node::prototype}, + {"Radio Menu Item",0,cb, (void*)&Radio_Menu_Item_Node::prototype}, + {nullptr}, + {"Browsers",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Browser_Node::prototype}, + {nullptr,0,cb,(void*)&Check_Browser_Node::prototype}, + {nullptr,0,cb,(void*)&File_Browser_Node::prototype}, + {nullptr,0,cb,(void*)&Tree_Node::prototype}, + {nullptr,0,cb,(void*)&Help_View_Node::prototype}, + {nullptr,0,cb,(void*)&Table_Node::prototype}, + {nullptr}, + {"Other",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Box_Node::prototype}, + {nullptr,0,cb,(void*)&Clock_Node::prototype}, + {nullptr,0,cb,(void*)&Progress_Node::prototype}, + {nullptr}, + {nullptr}}; #include @@ -1464,8 +1532,8 @@ Fl_Menu_Item New_Menu[] = { The icon may be null. If ic is null only the text is assigned to the label and Fl_Multi_Label is not used. \param[in] mi pointer to tme menu item that will be modified - \param[in] ic icon for the menu, may be NULL - \param[in] txt new label text, may *not* be NULL, will not be copied + \param[in] ic icon for the menu, may be nullptr + \param[in] txt new label text, may *not* be nullptr, will not be copied */ static void make_iconlabel(Fl_Menu_Item *mi, Fl_Image *ic, const char *txt) { @@ -1495,14 +1563,14 @@ void fill_in_New_Menu() { for (unsigned i = 0; i < sizeof(New_Menu)/sizeof(*New_Menu); i++) { Fl_Menu_Item *m = New_Menu+i; if (m->user_data()) { - Fl_Type *t = (Fl_Type*)m->user_data(); + Node *t = (Node*)m->user_data(); if (m->text) { - make_iconlabel( m, pixmap[t->id()], m->label() ); + make_iconlabel( m, pixmap[(int)t->type()], m->label() ); } else { const char *n = t->type_name(); if (!strncmp(n,"Fl_",3)) n += 3; if (!strncmp(n,"fltk::",6)) n += 6; - make_iconlabel( m, pixmap[t->id()], n ); + make_iconlabel( m, pixmap[(int)t->type()], n ); } } } @@ -1511,21 +1579,21 @@ void fill_in_New_Menu() { /** Find the correct prototype for a given type name. \param[in] inName a C string that must match type_name() or alt_type_name() of - one of the known Fl_Type classes. - \return the matching prototype or NULL + one of the known Node classes. + \return the matching prototype or nullptr */ -Fl_Type *typename_to_prototype(const char *inName) +Node *typename_to_prototype(const char *inName) { - if (inName==NULL || *inName==0) - return NULL; + if (inName==nullptr || *inName==0) + return nullptr; for (unsigned i = 0; i < sizeof(known_types)/sizeof(*known_types); i++) { - Fl_Type *prototype = known_types[i]; + Node *prototype = known_types[i]; if (fl_ascii_strcasecmp(inName, prototype->type_name())==0) return prototype; if (fl_ascii_strcasecmp(inName, prototype->alt_type_name())==0) return prototype; } - return NULL; + return nullptr; } /** @@ -1537,16 +1605,16 @@ Fl_Type *typename_to_prototype(const char *inName) \param[in] inName a C string that described the type we want \param[in] strategy add after current or as last child - \return the type node that was created or NULL + \return the type node that was created or nullptr \see add_new_widget_from_file(const char*, int) - add_new_widget_from_user(Fl_Type*, int) + add_new_widget_from_user(Node*, int) add_new_widget_from_user(const char*, int) */ -Fl_Type *add_new_widget_from_file(const char *inName, Strategy strategy) { - Fl_Type *prototype = typename_to_prototype(inName); +Node *add_new_widget_from_file(const char *inName, Strategy strategy) { + Node *prototype = typename_to_prototype(inName); if (!prototype) - return NULL; - Fl_Type *new_node = prototype->make(strategy); + return nullptr; + Node *new_node = prototype->make(strategy); return new_node; } diff --git a/fluid/nodes/factory.h b/fluid/nodes/factory.h index f1968b167..172f7e5d4 100644 --- a/fluid/nodes/factory.h +++ b/fluid/nodes/factory.h @@ -1,5 +1,5 @@ // -// Widget type header file for the Fast Light Tool Kit (FLTK). +// Node Factory header file for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2021 by Bill Spitzak and others. // @@ -14,21 +14,21 @@ // https://www.fltk.org/bugs.php // -#ifndef _FLUID_FACTORY_H -#define _FLUID_FACTORY_H +#ifndef FLUID_NODES_FACTORY_H +#define FLUID_NODES_FACTORY_H -#include "nodes/Fl_Type.h" +#include "nodes/Node.h" struct Fl_Menu_Item; extern Fl_Menu_Item New_Menu[]; void fill_in_New_Menu(); -Fl_Type *typename_to_prototype(const char *inName); +Node *typename_to_prototype(const char *inName); -Fl_Type *add_new_widget_from_file(const char *inName, Strategy strategy); -Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy, bool and_open=true); -Fl_Type *add_new_widget_from_user(const char *inName, Strategy strategy, bool and_open=true); +Node *add_new_widget_from_file(const char *inName, Strategy strategy); +Node *add_new_widget_from_user(Node *inPrototype, Strategy strategy, bool and_open=true); +Node *add_new_widget_from_user(const char *inName, Strategy strategy, bool and_open=true); -#endif // _FLUID_FACTORY_H +#endif // FLUID_NODES_FACTORY_H diff --git a/fluid/panels/about_panel.cxx b/fluid/panels/about_panel.cxx index ab36ddc10..6c78314dd 100644 --- a/fluid/panels/about_panel.cxx +++ b/fluid/panels/about_panel.cxx @@ -17,7 +17,6 @@ // generated by Fast Light User Interface Designer (fluid) version 1.0500 #include "about_panel.h" -void show_help(const char *name); Fl_Double_Window *about_panel=(Fl_Double_Window *)0; @@ -140,14 +139,14 @@ static const unsigned char idata_fluid[] = 99,199,176,225,186,61,16,35,74,220,19,2,0,59}; static Fl_Image *image_fluid() { Fl_GIF_Image::animate = true; - static Fl_Image *image = NULL; + static Fl_Image *image = 0L; if (!image) image = new Fl_Anim_GIF_Image("fluid.animated.gif", idata_fluid, 2545); return image; } static void cb_View(Fl_Button*, void*) { - show_help("license.html"); + Fluid.show_help("license.html"); } static void cb_Close(Fl_Return_Button* o, void*) { diff --git a/fluid/panels/about_panel.fl b/fluid/panels/about_panel.fl index 3b5d54b72..7e7feba5f 100644 --- a/fluid/panels/about_panel.fl +++ b/fluid/panels/about_panel.fl @@ -20,10 +20,10 @@ comment {// } {in_source in_header } -decl {\#include } {public global +decl {\#include "Fluid.h"} {public global } -decl {void show_help(const char *name);} {public local +decl {\#include } {public global } Function {make_about_panel()} {open @@ -60,7 +60,7 @@ Version x.x.x} } Fl_Button {} { label {View License...} - callback {show_help("license.html");} + callback {Fluid.show_help("license.html");} xywh {115 145 123 25} labelcolor 136 } Fl_Return_Button {} { diff --git a/fluid/panels/about_panel.h b/fluid/panels/about_panel.h index 0130a1e44..421b1d40c 100644 --- a/fluid/panels/about_panel.h +++ b/fluid/panels/about_panel.h @@ -19,8 +19,8 @@ #ifndef about_panel_h #define about_panel_h #include +#include "Fluid.h" #include -extern void show_help(const char *name); #include #include "../src/flstring.h" extern Fl_Double_Window *about_panel; diff --git a/fluid/panels/codeview_panel.cxx b/fluid/panels/codeview_panel.cxx index c1c6a1d65..745ad0ad1 100644 --- a/fluid/panels/codeview_panel.cxx +++ b/fluid/panels/codeview_panel.cxx @@ -17,19 +17,20 @@ // generated by Fast Light User Interface Designer (fluid) version 1.0500 #include "codeview_panel.h" -#include "app/fluid.h" -#include "app/project.h" +#include "Fluid.h" +#include "Project.h" #include "io/Project_Reader.h" #include "io/Project_Writer.h" +#include "io/String_Writer.h" #include #include #include "../src/flstring.h" -static char *cv_source_filename = NULL; -static char *cv_header_filename = NULL; -static char *cv_design_filename = NULL; +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(Fl_Type *o); -extern void reveal_in_browser(Fl_Type *t); +extern void select_only(Node *o); +extern void reveal_in_browser(Node *t); /** Update the header and source code highlighting depending on the @@ -45,29 +46,29 @@ void update_codeview_position() { return; if (cv_autoposition->value()==0) return; - if (codeview_panel && codeview_panel->visible() && Fl_Type::current) { + if (codeview_panel && codeview_panel->visible() && Fluid.proj.tree.current) { int pos0 = 0, pos1 = 0; if (cv_source->visible_r()) { switch (cv_code_choice) { case 0: // prolog: not yet (include statements) - pos0 = Fl_Type::current->code1_start; - pos1 = Fl_Type::current->code2_end; + pos0 = Fluid.proj.tree.current->code1_start; + pos1 = Fluid.proj.tree.current->code2_end; break; case 1: // static: callbacks, menu declarations - pos0 = Fl_Type::current->code_static_start; - pos1 = Fl_Type::current->code_static_end; + pos0 = Fluid.proj.tree.current->code_static_start; + pos1 = Fluid.proj.tree.current->code_static_end; break; case 2: // code: entire implementation block including children - pos0 = Fl_Type::current->code1_start; - pos1 = Fl_Type::current->code2_end; + pos0 = Fluid.proj.tree.current->code1_start; + pos1 = Fluid.proj.tree.current->code2_end; break; case 3: // code1: all implementation code before the children - pos0 = Fl_Type::current->code1_start; - pos1 = Fl_Type::current->code1_end; + pos0 = Fluid.proj.tree.current->code1_start; + pos1 = Fluid.proj.tree.current->code1_end; break; case 4: // code1: all implementation code before the children - pos0 = Fl_Type::current->code2_start; - pos1 = Fl_Type::current->code2_end; + pos0 = Fluid.proj.tree.current->code2_start; + pos1 = Fluid.proj.tree.current->code2_end; break; } if (pos0>=0) { @@ -82,20 +83,20 @@ void update_codeview_position() { switch (cv_code_choice) { case 0: // prolog: not yet (include statements) case 1: // static: callbacks, menu declarations - pos0 = Fl_Type::current->header_static_start; - pos1 = Fl_Type::current->header_static_end; + pos0 = Fluid.proj.tree.current->header_static_start; + pos1 = Fluid.proj.tree.current->header_static_end; break; case 2: // code: entire implementation block including children - pos0 = Fl_Type::current->header1_start; - pos1 = Fl_Type::current->header2_end; + pos0 = Fluid.proj.tree.current->header1_start; + pos1 = Fluid.proj.tree.current->header2_end; break; case 3: // code1: all implementation code before the children - pos0 = Fl_Type::current->header1_start; - pos1 = Fl_Type::current->header1_end; + pos0 = Fluid.proj.tree.current->header1_start; + pos1 = Fluid.proj.tree.current->header1_end; break; case 4: // code1: all implementation code before the children - pos0 = Fl_Type::current->header2_start; - pos1 = Fl_Type::current->header2_end; + pos0 = Fluid.proj.tree.current->header2_start; + pos1 = Fluid.proj.tree.current->header2_end; break; } if (pos0>=0) { @@ -111,16 +112,16 @@ void update_codeview_position() { case 0: // prolog: not yet (include statements) case 1: // static: callbacks, menu declarations case 2: // code: entire implementation block including children - pos0 = Fl_Type::current->proj1_start; - pos1 = Fl_Type::current->proj2_end; + pos0 = Fluid.proj.tree.current->proj1_start; + pos1 = Fluid.proj.tree.current->proj2_end; break; case 3: // code1: all implementation code before the children - pos0 = Fl_Type::current->proj1_start; - pos1 = Fl_Type::current->proj1_end; + pos0 = Fluid.proj.tree.current->proj1_start; + pos1 = Fluid.proj.tree.current->proj1_end; break; case 4: // code1: all implementation code before the children - pos0 = Fl_Type::current->proj2_start; - pos1 = Fl_Type::current->proj2_end; + pos0 = Fluid.proj.tree.current->proj2_start; + pos1 = Fluid.proj.tree.current->proj2_end; break; } if (pos0>=0) { @@ -154,43 +155,43 @@ void update_codeview_cb(class Fl_Button*, void*) { if (!cv_source_filename) { cv_source_filename = (char*)malloc(FL_PATH_MAX); - fl_strlcpy(cv_source_filename, get_tmpdir().c_str(), 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, get_tmpdir().c_str(), 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, get_tmpdir().c_str(), 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(cv_design_filename, false, true); + 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, get_tmpdir().c_str(), FL_PATH_MAX); + 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[g_project.i18n_type]); - write_strings(fn); + 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 = g_project.code_file_name; - g_project.code_file_name = cv_source_filename; - std::string header_file_name_bak = g_project.header_file_name; - g_project.header_file_name = cv_header_filename; + 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; + fld::io::Code_Writer f(Fluid.proj); // generate files if (f.write_code(cv_source_filename, cv_header_filename, true)) { @@ -206,8 +207,8 @@ void update_codeview_cb(class Fl_Button*, void*) { update_codeview_position(); } - g_project.code_file_name = code_file_name_bak; - g_project.header_file_name = header_file_name_bak; + Fluid.proj.code_file_name = code_file_name_bak; + Fluid.proj.header_file_name = header_file_name_bak; } } @@ -236,7 +237,7 @@ void codeview_toggle_visibility() { if (!codeview_panel) { make_codeview(); codeview_panel->callback((Fl_Callback*)toggle_codeview_cb); - Fl_Preferences svp(fluid_prefs, "codeview"); + Fl_Preferences svp(Fluid.preferences, "codeview"); int autorefresh; svp.get("autorefresh", autorefresh, 1); cv_autorefresh->value(autorefresh); @@ -248,15 +249,15 @@ void codeview_toggle_visibility() { if (tab>=0 && tabchildren()) cv_tab->value(cv_tab->child(tab)); svp.get("code_choice", cv_code_choice, 2); cv_code_choice_w->value(cv_code_choice_w->find_item_with_argument(cv_code_choice)); - if (!position_window(codeview_panel,"codeview_pos", 0, 320, 120, 550, 500)) return; + if (!Fluid.position_window(codeview_panel,"codeview_pos", 0, 320, 120, 550, 500)) return; } if (codeview_panel->visible()) { codeview_panel->hide(); - codeview_item->label("Show Code View"); + Fluid.codeview_item->label("Show Code View"); } else { codeview_panel->show(); - codeview_item->label("Hide Code View"); + Fluid.codeview_item->label("Hide Code View"); update_codeview_cb(0,0); } } @@ -282,7 +283,7 @@ Fl_Button *cv_find_text_case=(Fl_Button *)0; Fl_Input *cv_find_text=(Fl_Input *)0; static void cb_cv_find_text(Fl_Input* o, void*) { - Fl_Text_Display *e = NULL; + Fl_Text_Display *e = nullptr; if (cv_source->visible_r()) { e = cv_source; } else if (cv_header->visible_r()) { @@ -303,7 +304,7 @@ static void cb_cv_find_text(Fl_Input* o, void*) { } static void cb_(Fl_Button*, void*) { - Fl_Text_Display *e = NULL; + Fl_Text_Display *e = nullptr; if (cv_source->visible_r()) { e = cv_source; } else if (cv_header->visible_r()) { @@ -328,7 +329,7 @@ static void cb_(Fl_Button*, void*) { } static void cb_1(Fl_Button*, void*) { - Fl_Text_Display *e = NULL; + Fl_Text_Display *e = nullptr; if (cv_source->visible_r()) { e = cv_source; } else if (cv_header->visible_r()) { @@ -354,13 +355,13 @@ static void cb_1(Fl_Button*, void*) { static void cb_Reveal(Fl_Button*, void*) { if (codeview_panel && codeview_panel->visible()) { - Fl_Type *node = NULL; + Node *node = nullptr; if (cv_source->visible_r()) - node = Fl_Type::find_in_text(0, cv_source->insert_position()); + node = Fluid.proj.tree.find_in_text(0, cv_source->insert_position()); else if (cv_header->visible_r()) - node = Fl_Type::find_in_text(1, cv_header->insert_position()); + node = Fluid.proj.tree.find_in_text(1, cv_header->insert_position()); else if (cv_project->visible_r()) - node = Fl_Type::find_in_text(2, cv_project->insert_position()); + node = Fluid.proj.tree.find_in_text(2, cv_project->insert_position()); if (node) { select_only(node); reveal_in_browser(node); diff --git a/fluid/panels/codeview_panel.fl b/fluid/panels/codeview_panel.fl index ce40ffd8a..fc62fee51 100644 --- a/fluid/panels/codeview_panel.fl +++ b/fluid/panels/codeview_panel.fl @@ -20,10 +20,10 @@ comment {// } {in_source in_header } -decl {\#include "app/fluid.h"} {private local +decl {\#include "Fluid.h"} {private local } -decl {\#include "app/project.h"} {private local +decl {\#include "Project.h"} {private local } decl {\#include "io/Project_Reader.h"} {private local @@ -32,6 +32,9 @@ decl {\#include "io/Project_Reader.h"} {private local decl {\#include "io/Project_Writer.h"} {private local } +decl {\#include "io/String_Writer.h"} {private local +} + decl {\#include } {private local } @@ -41,22 +44,22 @@ decl {\#include } {private local decl {\#include "../src/flstring.h"} {selected private local } -decl {char *cv_source_filename = NULL;} {private local +decl {char *cv_source_filename = nullptr;} {private local } -decl {char *cv_header_filename = NULL;} {private local +decl {char *cv_header_filename = nullptr;} {private local } -decl {char *cv_design_filename = NULL;} {private local +decl {char *cv_design_filename = nullptr;} {private local } decl {int cv_code_choice;} {public local } -decl {extern void select_only(Fl_Type *o);} {private global +decl {extern void select_only(Node *o);} {private global } -decl {extern void reveal_in_browser(Fl_Type *t);} {private global +decl {extern void reveal_in_browser(Node *t);} {private global } Function {update_codeview_position()} { @@ -72,29 +75,29 @@ file.} open return_type void return; if (cv_autoposition->value()==0) return; - if (codeview_panel && codeview_panel->visible() && Fl_Type::current) { + if (codeview_panel && codeview_panel->visible() && Fluid.proj.tree.current) { int pos0 = 0, pos1 = 0; if (cv_source->visible_r()) { switch (cv_code_choice) { case 0: // prolog: not yet (include statements) - pos0 = Fl_Type::current->code1_start; - pos1 = Fl_Type::current->code2_end; + pos0 = Fluid.proj.tree.current->code1_start; + pos1 = Fluid.proj.tree.current->code2_end; break; case 1: // static: callbacks, menu declarations - pos0 = Fl_Type::current->code_static_start; - pos1 = Fl_Type::current->code_static_end; + pos0 = Fluid.proj.tree.current->code_static_start; + pos1 = Fluid.proj.tree.current->code_static_end; break; case 2: // code: entire implementation block including children - pos0 = Fl_Type::current->code1_start; - pos1 = Fl_Type::current->code2_end; + pos0 = Fluid.proj.tree.current->code1_start; + pos1 = Fluid.proj.tree.current->code2_end; break; case 3: // code1: all implementation code before the children - pos0 = Fl_Type::current->code1_start; - pos1 = Fl_Type::current->code1_end; + pos0 = Fluid.proj.tree.current->code1_start; + pos1 = Fluid.proj.tree.current->code1_end; break; case 4: // code1: all implementation code before the children - pos0 = Fl_Type::current->code2_start; - pos1 = Fl_Type::current->code2_end; + pos0 = Fluid.proj.tree.current->code2_start; + pos1 = Fluid.proj.tree.current->code2_end; break; } if (pos0>=0) { @@ -109,20 +112,20 @@ file.} open return_type void switch (cv_code_choice) { case 0: // prolog: not yet (include statements) case 1: // static: callbacks, menu declarations - pos0 = Fl_Type::current->header_static_start; - pos1 = Fl_Type::current->header_static_end; + pos0 = Fluid.proj.tree.current->header_static_start; + pos1 = Fluid.proj.tree.current->header_static_end; break; case 2: // code: entire implementation block including children - pos0 = Fl_Type::current->header1_start; - pos1 = Fl_Type::current->header2_end; + pos0 = Fluid.proj.tree.current->header1_start; + pos1 = Fluid.proj.tree.current->header2_end; break; case 3: // code1: all implementation code before the children - pos0 = Fl_Type::current->header1_start; - pos1 = Fl_Type::current->header1_end; + pos0 = Fluid.proj.tree.current->header1_start; + pos1 = Fluid.proj.tree.current->header1_end; break; case 4: // code1: all implementation code before the children - pos0 = Fl_Type::current->header2_start; - pos1 = Fl_Type::current->header2_end; + pos0 = Fluid.proj.tree.current->header2_start; + pos1 = Fluid.proj.tree.current->header2_end; break; } if (pos0>=0) { @@ -138,16 +141,16 @@ file.} open return_type void case 0: // prolog: not yet (include statements) case 1: // static: callbacks, menu declarations case 2: // code: entire implementation block including children - pos0 = Fl_Type::current->proj1_start; - pos1 = Fl_Type::current->proj2_end; + pos0 = Fluid.proj.tree.current->proj1_start; + pos1 = Fluid.proj.tree.current->proj2_end; break; case 3: // code1: all implementation code before the children - pos0 = Fl_Type::current->proj1_start; - pos1 = Fl_Type::current->proj1_end; + pos0 = Fluid.proj.tree.current->proj1_start; + pos1 = Fluid.proj.tree.current->proj1_end; break; case 4: // code1: all implementation code before the children - pos0 = Fl_Type::current->proj2_start; - pos1 = Fl_Type::current->proj2_end; + pos0 = Fluid.proj.tree.current->proj2_start; + pos1 = Fluid.proj.tree.current->proj2_end; break; } if (pos0>=0) { @@ -179,43 +182,43 @@ and load those into the Code Viewer widgets.} open return_type void if (!cv_source_filename) { cv_source_filename = (char*)malloc(FL_PATH_MAX); - fl_strlcpy(cv_source_filename, get_tmpdir().c_str(), 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, get_tmpdir().c_str(), 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, get_tmpdir().c_str(), 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(cv_design_filename, false, true); + 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, get_tmpdir().c_str(), FL_PATH_MAX); + 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[g_project.i18n_type]); - write_strings(fn); + 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 = g_project.code_file_name; - g_project.code_file_name = cv_source_filename; - std::string header_file_name_bak = g_project.header_file_name; - g_project.header_file_name = cv_header_filename; + 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; + fld::io::Code_Writer f(Fluid.proj); // generate files if (f.write_code(cv_source_filename, cv_header_filename, true)) { @@ -231,8 +234,8 @@ and load those into the Code Viewer widgets.} open return_type void update_codeview_position(); } - g_project.code_file_name = code_file_name_bak; - g_project.header_file_name = header_file_name_bak; + Fluid.proj.code_file_name = code_file_name_bak; + Fluid.proj.header_file_name = header_file_name_bak; }} {} } @@ -260,7 +263,7 @@ The state is stored in the app preferences. code {if (!codeview_panel) { make_codeview(); codeview_panel->callback((Fl_Callback*)toggle_codeview_cb); - Fl_Preferences svp(fluid_prefs, "codeview"); + Fl_Preferences svp(Fluid.preferences, "codeview"); int autorefresh; svp.get("autorefresh", autorefresh, 1); cv_autorefresh->value(autorefresh); @@ -272,15 +275,15 @@ The state is stored in the app preferences. if (tab>=0 && tabchildren()) cv_tab->value(cv_tab->child(tab)); svp.get("code_choice", cv_code_choice, 2); cv_code_choice_w->value(cv_code_choice_w->find_item_with_argument(cv_code_choice)); - if (!position_window(codeview_panel,"codeview_pos", 0, 320, 120, 550, 500)) return; + if (!Fluid.position_window(codeview_panel,"codeview_pos", 0, 320, 120, 550, 500)) return; } if (codeview_panel->visible()) { codeview_panel->hide(); - codeview_item->label("Show Code View"); + Fluid.codeview_item->label("Show Code View"); } else { codeview_panel->show(); - codeview_item->label("Hide Code View"); + Fluid.codeview_item->label("Hide Code View"); update_codeview_cb(0,0); }} {} } @@ -354,7 +357,7 @@ Function {make_codeview()} {open } Fl_Input cv_find_text { label {Find:} - callback {Fl_Text_Display *e = NULL; + callback {Fl_Text_Display *e = nullptr; if (cv_source->visible_r()) { e = cv_source; } else if (cv_header->visible_r()) { @@ -376,7 +379,7 @@ if (e) { } Fl_Button {} { label {<<} - callback {Fl_Text_Display *e = NULL; + callback {Fl_Text_Display *e = nullptr; if (cv_source->visible_r()) { e = cv_source; } else if (cv_header->visible_r()) { @@ -402,7 +405,7 @@ if (e) { } Fl_Button {} { label {>>} - callback {Fl_Text_Display *e = NULL; + callback {Fl_Text_Display *e = nullptr; if (cv_source->visible_r()) { e = cv_source; } else if (cv_header->visible_r()) { @@ -429,13 +432,13 @@ if (e) { Fl_Button {} { label Reveal callback {if (codeview_panel && codeview_panel->visible()) { - Fl_Type *node = NULL; + Node *node = nullptr; if (cv_source->visible_r()) - node = Fl_Type::find_in_text(0, cv_source->insert_position()); + node = Fluid.proj.tree.find_in_text(0, cv_source->insert_position()); else if (cv_header->visible_r()) - node = Fl_Type::find_in_text(1, cv_header->insert_position()); + node = Fluid.proj.tree.find_in_text(1, cv_header->insert_position()); else if (cv_project->visible_r()) - node = Fl_Type::find_in_text(2, cv_project->insert_position()); + node = Fluid.proj.tree.find_in_text(2, cv_project->insert_position()); if (node) { select_only(node); reveal_in_browser(node); diff --git a/fluid/panels/function_panel.cxx b/fluid/panels/function_panel.cxx index 4ef8fac64..3703cb157 100644 --- a/fluid/panels/function_panel.cxx +++ b/fluid/panels/function_panel.cxx @@ -17,9 +17,9 @@ // generated by Fast Light User Interface Designer (fluid) version 1.0500 #include "function_panel.h" -#include "app/fluid.h" -#include "app/undo.h" -#include "nodes/Fl_Type.h" +#include "Fluid.h" +#include "proj/undo.h" +#include "nodes/Node.h" #include "nodes/factory.h" #include "rsrcs/pixmaps.h" #include "widgets/Bin_Button.h" @@ -765,7 +765,7 @@ Fl_Double_Window* make_comment_panel() { void type_make_cb(Fl_Widget*,void*d) { const char *type_name = (const char*)d; - if (Fl_Type::current && Fl_Type::current->can_have_children()) + if (Fluid.proj.tree.current && Fluid.proj.tree.current->can_have_children()) add_new_widget_from_user(type_name, Strategy::AS_LAST_CHILD); else add_new_widget_from_user(type_name, Strategy::AFTER_CURRENT); @@ -773,11 +773,11 @@ void type_make_cb(Fl_Widget*,void*d) { Fl_Window *widgetbin_panel=(Fl_Window *)0; -static void cb_widgetbin_panel(Fl_Window* o, void* v) { +static void cb_widgetbin_panel(Fl_Window*, void*) { if (Fl::event()==FL_SHORTCUT && Fl::event_key()==FL_Escape) - exit_cb((Fl_Widget*)o, v); + Fluid.quit(); else - toggle_widgetbin_cb((Fl_Widget*)o, v); + Fluid.toggle_widget_bin(); } Fl_Window* make_widgetbin() { @@ -790,31 +790,31 @@ Fl_Window* make_widgetbin() { o->tooltip("Function"); o->box(FL_THIN_UP_BOX); o->callback((Fl_Callback*)type_make_cb, (void*)("Function")); - o->image(pixmap[ID_Function]); + o->image(pixmap[(int)Type::Function]); } // Fl_Button* o { Fl_Button* o = new Fl_Button(30, 21, 24, 24); o->tooltip("Class"); o->box(FL_THIN_UP_BOX); o->callback((Fl_Callback*)type_make_cb, (void*)("Class")); - o->image(pixmap[ID_Class]); + o->image(pixmap[(int)Type::Class]); } // Fl_Button* o { Fl_Button* o = new Fl_Button(55, 21, 24, 24); o->tooltip("Comment"); o->box(FL_THIN_UP_BOX); o->callback((Fl_Callback*)type_make_cb, (void*)("comment")); - o->image(pixmap[ID_Comment]); + o->image(pixmap[(int)Type::Comment]); } // Fl_Button* o { Fl_Button* o = new Fl_Button(5, 46, 24, 24); o->tooltip("Code"); o->box(FL_THIN_UP_BOX); o->callback((Fl_Callback*)type_make_cb, (void*)("Code")); - o->image(pixmap[ID_Code]); + o->image(pixmap[(int)Type::Code]); } // Fl_Button* o { Fl_Button* o = new Fl_Button(30, 46, 24, 24); o->tooltip("Code Block"); o->box(FL_THIN_UP_BOX); o->callback((Fl_Callback*)type_make_cb, (void*)("CodeBlock")); - o->image(pixmap[ID_CodeBlock]); + o->image(pixmap[(int)Type::CodeBlock]); } // Fl_Button* o { fld::widget::Bin_Window_Button* o = new fld::widget::Bin_Window_Button(55, 46, 24, 24); o->tooltip("Widget Class"); @@ -828,25 +828,25 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("widget_class")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Widget_Class]); + o->image(pixmap[(int)Type::Widget_Class]); } // fld::widget::Bin_Window_Button* o { Fl_Button* o = new Fl_Button(5, 71, 24, 24); o->tooltip("Declaration"); o->box(FL_THIN_UP_BOX); o->callback((Fl_Callback*)type_make_cb, (void*)("decl")); - o->image(pixmap[ID_Decl]); + o->image(pixmap[(int)Type::Decl]); } // Fl_Button* o { Fl_Button* o = new Fl_Button(30, 71, 24, 24); o->tooltip("Declaration Block"); o->box(FL_THIN_UP_BOX); o->callback((Fl_Callback*)type_make_cb, (void*)("declblock")); - o->image(pixmap[ID_DeclBlock]); + o->image(pixmap[(int)Type::DeclBlock]); } // Fl_Button* o { Fl_Button* o = new Fl_Button(55, 71, 24, 24); o->tooltip("Inline Data"); o->box(FL_THIN_UP_BOX); o->callback((Fl_Callback*)type_make_cb, (void*)("data")); - o->image(pixmap[ID_Data]); + o->image(pixmap[(int)Type::Data]); } // Fl_Button* o o->end(); } // Fl_Group* o @@ -864,7 +864,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Window")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Window]); + o->image(pixmap[(int)Type::Window]); } // fld::widget::Bin_Window_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(114, 21, 24, 24); o->tooltip("Group"); @@ -878,7 +878,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Group")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Group]); + o->image(pixmap[(int)Type::Group]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(139, 21, 24, 24); o->tooltip("Pack"); @@ -892,7 +892,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Pack")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Pack]); + o->image(pixmap[(int)Type::Pack]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(89, 46, 24, 24); o->tooltip("Tabs"); @@ -906,7 +906,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Tabs")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Tabs]); + o->image(pixmap[(int)Type::Tabs]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(114, 46, 24, 24); o->tooltip("Scroll"); @@ -920,7 +920,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Scroll")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Scroll]); + o->image(pixmap[(int)Type::Scroll]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(139, 46, 24, 24); o->tooltip("Flex"); @@ -934,7 +934,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Flex")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Flex]); + o->image(pixmap[(int)Type::Flex]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(89, 71, 24, 24); o->tooltip("Tile"); @@ -948,7 +948,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Tile")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Tile]); + o->image(pixmap[(int)Type::Tile]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(114, 71, 24, 24); o->tooltip("Wizard"); @@ -962,7 +962,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Wizard")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Wizard]); + o->image(pixmap[(int)Type::Wizard]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(139, 71, 24, 24); o->tooltip("Grid"); @@ -976,7 +976,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Grid")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Grid]); + o->image(pixmap[(int)Type::Grid]); } // fld::widget::Bin_Button* o o->end(); } // Fl_Group* o @@ -994,7 +994,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Button")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Button]); + o->image(pixmap[(int)Type::Button]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(198, 21, 24, 24); o->tooltip("Return Button"); @@ -1008,7 +1008,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Return_Button")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Return_Button]); + o->image(pixmap[(int)Type::Return_Button]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(173, 46, 24, 24); o->tooltip("Light Button"); @@ -1022,7 +1022,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Light_Button")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Light_Button]); + o->image(pixmap[(int)Type::Light_Button]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(198, 46, 24, 24); o->tooltip("Repeat Button"); @@ -1036,7 +1036,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Repeat_Button")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Repeat_Button]); + o->image(pixmap[(int)Type::Repeat_Button]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(173, 71, 24, 24); o->tooltip("Check Button"); @@ -1050,7 +1050,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Check_Button")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Check_Button]); + o->image(pixmap[(int)Type::Check_Button]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(198, 71, 24, 24); o->tooltip("Round Button"); @@ -1064,7 +1064,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Round_Button")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Round_Button]); + o->image(pixmap[(int)Type::Round_Button]); } // fld::widget::Bin_Button* o o->end(); } // Fl_Group* o @@ -1082,7 +1082,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Slider")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Slider]); + o->image(pixmap[(int)Type::Slider]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(257, 21, 24, 24); o->tooltip("Scroll Bar"); @@ -1096,7 +1096,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Scrollbar")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Scrollbar]); + o->image(pixmap[(int)Type::Scrollbar]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(282, 21, 24, 24); o->tooltip("Value Slider"); @@ -1110,7 +1110,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Value_Slider")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Value_Slider]); + o->image(pixmap[(int)Type::Value_Slider]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(307, 21, 24, 24); o->tooltip("Value Output"); @@ -1124,7 +1124,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Value_Output")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Value_Output]); + o->image(pixmap[(int)Type::Value_Output]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(232, 46, 24, 24); o->tooltip("Adjuster"); @@ -1138,7 +1138,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Adjuster")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Adjuster]); + o->image(pixmap[(int)Type::Adjuster]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(257, 46, 24, 24); o->tooltip("Counter"); @@ -1152,7 +1152,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Counter")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Counter]); + o->image(pixmap[(int)Type::Counter]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(282, 46, 24, 24); o->tooltip("Dial"); @@ -1166,7 +1166,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Dial")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Dial]); + o->image(pixmap[(int)Type::Dial]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(232, 71, 24, 24); o->tooltip("Roller"); @@ -1180,7 +1180,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Roller")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Roller]); + o->image(pixmap[(int)Type::Roller]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(257, 71, 24, 24); o->tooltip("Spinner"); @@ -1194,7 +1194,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Spinner")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Spinner]); + o->image(pixmap[(int)Type::Spinner]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(282, 71, 24, 24); o->tooltip("Value Input"); @@ -1208,7 +1208,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Value_Input")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Value_Input]); + o->image(pixmap[(int)Type::Value_Input]); } // fld::widget::Bin_Button* o o->end(); } // Fl_Group* o @@ -1226,7 +1226,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Input")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Input]); + o->image(pixmap[(int)Type::Input]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(366, 21, 24, 24); o->tooltip("Output"); @@ -1240,7 +1240,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Output")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Output]); + o->image(pixmap[(int)Type::Output]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(341, 46, 24, 24); o->tooltip("Text Edit"); @@ -1254,7 +1254,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Text_Editor")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Text_Editor]); + o->image(pixmap[(int)Type::Text_Editor]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(366, 46, 24, 24); o->tooltip("Text Display"); @@ -1268,7 +1268,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Text_Display")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Text_Display]); + o->image(pixmap[(int)Type::Text_Display]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(341, 71, 24, 24); o->tooltip("File Input"); @@ -1282,7 +1282,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_File_Input")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_File_Input]); + o->image(pixmap[(int)Type::File_Input]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(366, 71, 24, 24); o->tooltip("Terminal"); @@ -1296,7 +1296,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Terminal")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Terminal]); + o->image(pixmap[(int)Type::Terminal]); } // fld::widget::Bin_Button* o o->end(); } // Fl_Group* o @@ -1314,7 +1314,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Input_Choice")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Input_Choice]); + o->image(pixmap[(int)Type::Input_Choice]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(425, 21, 24, 24); o->tooltip("Menu Item"); @@ -1328,7 +1328,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("menuitem")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Menu_Item]); + o->image(pixmap[(int)Type::Menu_Item]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(450, 21, 24, 24); o->tooltip("Menu Bar"); @@ -1342,7 +1342,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Menu_Bar")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Menu_Bar]); + o->image(pixmap[(int)Type::Menu_Bar]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(400, 46, 24, 24); o->tooltip("Menu Button"); @@ -1356,7 +1356,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Menu_Button")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Menu_Button]); + o->image(pixmap[(int)Type::Menu_Button]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(425, 46, 24, 24); o->tooltip("Checkbox Menu Item"); @@ -1370,7 +1370,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("checkmenuitem")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Checkbox_Menu_Item]); + o->image(pixmap[(int)Type::Checkbox_Menu_Item]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(450, 46, 24, 24); o->tooltip("Sub Menu"); @@ -1384,7 +1384,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("submenu")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Submenu]); + o->image(pixmap[(int)Type::Submenu]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(400, 71, 24, 24); o->tooltip("Choice"); @@ -1398,7 +1398,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Choice")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Choice]); + o->image(pixmap[(int)Type::Choice]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(425, 71, 24, 24); o->tooltip("Radio Menu Item"); @@ -1412,7 +1412,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("radiomenuitem")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Radio_Menu_Item]); + o->image(pixmap[(int)Type::Radio_Menu_Item]); } // fld::widget::Bin_Button* o o->end(); } // Fl_Group* o @@ -1430,7 +1430,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Browser")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Browser]); + o->image(pixmap[(int)Type::Browser]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(509, 21, 24, 24); o->tooltip("Tree"); @@ -1444,7 +1444,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Tree")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Tree]); + o->image(pixmap[(int)Type::Tree]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(484, 46, 24, 24); o->tooltip("Check Browser"); @@ -1458,7 +1458,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Check_Browser")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Check_Browser]); + o->image(pixmap[(int)Type::Check_Browser]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(509, 46, 24, 24); o->tooltip("Help Browser"); @@ -1472,7 +1472,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Help_View")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Help_View]); + o->image(pixmap[(int)Type::Help_View]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(484, 71, 24, 24); o->tooltip("File Browser"); @@ -1486,7 +1486,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_File_Browser")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_File_Browser]); + o->image(pixmap[(int)Type::File_Browser]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(509, 71, 24, 24); o->tooltip("Table"); @@ -1500,7 +1500,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Table")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Table]); + o->image(pixmap[(int)Type::Table]); } // fld::widget::Bin_Button* o o->end(); } // Fl_Group* o @@ -1518,7 +1518,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Box")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Box]); + o->image(pixmap[(int)Type::Box]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(567, 21, 24, 24); o->tooltip("Clock"); @@ -1532,7 +1532,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Clock")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Clock]); + o->image(pixmap[(int)Type::Clock]); } // fld::widget::Bin_Button* o { fld::widget::Bin_Button* o = new fld::widget::Bin_Button(542, 46, 24, 24); o->tooltip("Progress"); @@ -1546,7 +1546,7 @@ Fl_Window* make_widgetbin() { o->callback((Fl_Callback*)type_make_cb, (void*)("Fl_Progress")); o->align(Fl_Align(FL_ALIGN_CENTER)); o->when(FL_WHEN_RELEASE); - o->image(pixmap[ID_Progress]); + o->image(pixmap[(int)Type::Progress]); } // fld::widget::Bin_Button* o o->end(); } // Fl_Group* o diff --git a/fluid/panels/function_panel.fl b/fluid/panels/function_panel.fl index 7adb2ecea..d07af5c8c 100644 --- a/fluid/panels/function_panel.fl +++ b/fluid/panels/function_panel.fl @@ -25,13 +25,13 @@ comment {// } {in_source in_header } -decl {\#include "app/fluid.h"} {private local +decl {\#include "Fluid.h"} {private local } -decl {\#include "app/undo.h"} {private local +decl {\#include "proj/undo.h"} {private local } -decl {\#include "nodes/Fl_Type.h"} {private local +decl {\#include "nodes/Node.h"} {private local } decl {\#include "nodes/factory.h"} {private local @@ -588,7 +588,7 @@ Function {make_comment_panel()} {open Function {type_make_cb(Fl_Widget*,void*d)} {open return_type void } { code {const char *type_name = (const char*)d; -if (Fl_Type::current && Fl_Type::current->can_have_children()) +if (Fluid.proj.tree.current && Fluid.proj.tree.current->can_have_children()) add_new_widget_from_user(type_name, Strategy::AS_LAST_CHILD); else add_new_widget_from_user(type_name, Strategy::AFTER_CURRENT);} {} @@ -599,9 +599,9 @@ Function {make_widgetbin()} {open Fl_Window widgetbin_panel { label {Widget Bin} callback {if (Fl::event()==FL_SHORTCUT && Fl::event_key()==FL_Escape) - exit_cb((Fl_Widget*)o, v); + Fluid.quit(); else - toggle_widgetbin_cb((Fl_Widget*)o, v);} open + Fluid.toggle_widget_bin();} open xywh {395 227 600 102} type Single align 80 non_modal visible } { Fl_Group {} { @@ -612,56 +612,56 @@ else user_data {"Function"} callback type_make_cb tooltip Function xywh {5 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Function]);} + code0 {o->image(pixmap[(int)Type::Function]);} } Fl_Button {} { user_data {"Class"} callback type_make_cb tooltip Class xywh {30 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Class]);} + code0 {o->image(pixmap[(int)Type::Class]);} } Fl_Button {} { user_data {"comment"} callback type_make_cb tooltip Comment xywh {55 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Comment]);} + code0 {o->image(pixmap[(int)Type::Comment]);} } Fl_Button {} { user_data {"Code"} callback type_make_cb tooltip Code xywh {5 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Code]);} + code0 {o->image(pixmap[(int)Type::Code]);} } Fl_Button {} { user_data {"CodeBlock"} callback type_make_cb tooltip {Code Block} xywh {30 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_CodeBlock]);} + code0 {o->image(pixmap[(int)Type::CodeBlock]);} } Fl_Button {} { user_data {"widget_class"} callback type_make_cb tooltip {Widget Class} xywh {55 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Widget_Class]);} + code0 {o->image(pixmap[(int)Type::Widget_Class]);} class {fld::widget::Bin_Window_Button} } Fl_Button {} { user_data {"decl"} callback type_make_cb tooltip Declaration xywh {5 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Decl]);} + code0 {o->image(pixmap[(int)Type::Decl]);} } Fl_Button {} { user_data {"declblock"} callback type_make_cb tooltip {Declaration Block} xywh {30 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_DeclBlock]);} + code0 {o->image(pixmap[(int)Type::DeclBlock]);} } Fl_Button {} { user_data {"data"} callback type_make_cb tooltip {Inline Data} xywh {55 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Data]);} + code0 {o->image(pixmap[(int)Type::Data]);} } } Fl_Group {} { @@ -672,63 +672,63 @@ else user_data {"Fl_Window"} callback type_make_cb tooltip Window xywh {89 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Window]);} + code0 {o->image(pixmap[(int)Type::Window]);} class {fld::widget::Bin_Window_Button} } Fl_Button {} { user_data {"Fl_Group"} callback type_make_cb selected tooltip Group xywh {114 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Group]);} + code0 {o->image(pixmap[(int)Type::Group]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Pack"} callback type_make_cb tooltip Pack xywh {139 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Pack]);} + code0 {o->image(pixmap[(int)Type::Pack]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Tabs"} callback type_make_cb tooltip Tabs xywh {89 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Tabs]);} + code0 {o->image(pixmap[(int)Type::Tabs]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Scroll"} callback type_make_cb tooltip Scroll xywh {114 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Scroll]);} + code0 {o->image(pixmap[(int)Type::Scroll]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Flex"} callback type_make_cb tooltip Flex xywh {139 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Flex]);} + code0 {o->image(pixmap[(int)Type::Flex]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Tile"} callback type_make_cb tooltip Tile xywh {89 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Tile]);} + code0 {o->image(pixmap[(int)Type::Tile]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Wizard"} callback type_make_cb tooltip Wizard xywh {114 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Wizard]);} + code0 {o->image(pixmap[(int)Type::Wizard]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Grid"} callback type_make_cb tooltip Grid xywh {139 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Grid]);} + code0 {o->image(pixmap[(int)Type::Grid]);} class {fld::widget::Bin_Button} } } @@ -740,42 +740,42 @@ else user_data {"Fl_Button"} callback type_make_cb tooltip Button xywh {173 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Button]);} + code0 {o->image(pixmap[(int)Type::Button]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Return_Button"} callback type_make_cb tooltip {Return Button} xywh {198 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Return_Button]);} + code0 {o->image(pixmap[(int)Type::Return_Button]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Light_Button"} callback type_make_cb tooltip {Light Button} xywh {173 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Light_Button]);} + code0 {o->image(pixmap[(int)Type::Light_Button]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Repeat_Button"} callback type_make_cb tooltip {Repeat Button} xywh {198 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Repeat_Button]);} + code0 {o->image(pixmap[(int)Type::Repeat_Button]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Check_Button"} callback type_make_cb tooltip {Check Button} xywh {173 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Check_Button]);} + code0 {o->image(pixmap[(int)Type::Check_Button]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Round_Button"} callback type_make_cb tooltip {Round Button} xywh {198 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Round_Button]);} + code0 {o->image(pixmap[(int)Type::Round_Button]);} class {fld::widget::Bin_Button} } } @@ -787,70 +787,70 @@ else user_data {"Fl_Slider"} callback type_make_cb tooltip Slider xywh {232 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Slider]);} + code0 {o->image(pixmap[(int)Type::Slider]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Scrollbar"} callback type_make_cb tooltip {Scroll Bar} xywh {257 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Scrollbar]);} + code0 {o->image(pixmap[(int)Type::Scrollbar]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Value_Slider"} callback type_make_cb tooltip {Value Slider} xywh {282 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Value_Slider]);} + code0 {o->image(pixmap[(int)Type::Value_Slider]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Value_Output"} callback type_make_cb tooltip {Value Output} xywh {307 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Value_Output]);} + code0 {o->image(pixmap[(int)Type::Value_Output]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Adjuster"} callback type_make_cb tooltip Adjuster xywh {232 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Adjuster]);} + code0 {o->image(pixmap[(int)Type::Adjuster]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Counter"} callback type_make_cb tooltip Counter xywh {257 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Counter]);} + code0 {o->image(pixmap[(int)Type::Counter]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Dial"} callback type_make_cb tooltip Dial xywh {282 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Dial]);} + code0 {o->image(pixmap[(int)Type::Dial]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Roller"} callback type_make_cb tooltip Roller xywh {232 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Roller]);} + code0 {o->image(pixmap[(int)Type::Roller]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Spinner"} callback type_make_cb tooltip Spinner xywh {257 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Spinner]);} + code0 {o->image(pixmap[(int)Type::Spinner]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Value_Input"} callback type_make_cb tooltip {Value Input} xywh {282 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Value_Input]);} + code0 {o->image(pixmap[(int)Type::Value_Input]);} class {fld::widget::Bin_Button} } } @@ -862,42 +862,42 @@ else user_data {"Fl_Input"} callback type_make_cb tooltip Input xywh {341 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Input]);} + code0 {o->image(pixmap[(int)Type::Input]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Output"} callback type_make_cb tooltip Output xywh {366 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Output]);} + code0 {o->image(pixmap[(int)Type::Output]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Text_Editor"} callback type_make_cb tooltip {Text Edit} xywh {341 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Text_Editor]);} + code0 {o->image(pixmap[(int)Type::Text_Editor]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Text_Display"} callback type_make_cb tooltip {Text Display} xywh {366 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Text_Display]);} + code0 {o->image(pixmap[(int)Type::Text_Display]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_File_Input"} callback type_make_cb tooltip {File Input} xywh {341 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_File_Input]);} + code0 {o->image(pixmap[(int)Type::File_Input]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Terminal"} callback type_make_cb tooltip Terminal xywh {366 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Terminal]);} + code0 {o->image(pixmap[(int)Type::Terminal]);} class {fld::widget::Bin_Button} } } @@ -909,56 +909,56 @@ else user_data {"Fl_Input_Choice"} callback type_make_cb tooltip {Input Choice} xywh {400 22 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Input_Choice]);} + code0 {o->image(pixmap[(int)Type::Input_Choice]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"menuitem"} callback type_make_cb tooltip {Menu Item} xywh {425 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Menu_Item]);} + code0 {o->image(pixmap[(int)Type::Menu_Item]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Menu_Bar"} callback type_make_cb tooltip {Menu Bar} xywh {450 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Menu_Bar]);} + code0 {o->image(pixmap[(int)Type::Menu_Bar]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Menu_Button"} callback type_make_cb tooltip {Menu Button} xywh {400 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Menu_Button]);} + code0 {o->image(pixmap[(int)Type::Menu_Button]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"checkmenuitem"} callback type_make_cb tooltip {Checkbox Menu Item} xywh {425 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Checkbox_Menu_Item]);} + code0 {o->image(pixmap[(int)Type::Checkbox_Menu_Item]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"submenu"} callback type_make_cb tooltip {Sub Menu} xywh {450 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Submenu]);} + code0 {o->image(pixmap[(int)Type::Submenu]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Choice"} callback type_make_cb tooltip Choice xywh {400 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Choice]);} + code0 {o->image(pixmap[(int)Type::Choice]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"radiomenuitem"} callback type_make_cb tooltip {Radio Menu Item} xywh {425 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Radio_Menu_Item]);} + code0 {o->image(pixmap[(int)Type::Radio_Menu_Item]);} class {fld::widget::Bin_Button} } } @@ -970,42 +970,42 @@ else user_data {"Fl_Browser"} callback type_make_cb tooltip Browser xywh {484 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Browser]);} + code0 {o->image(pixmap[(int)Type::Browser]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Tree"} callback type_make_cb tooltip Tree xywh {509 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Tree]);} + code0 {o->image(pixmap[(int)Type::Tree]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Check_Browser"} callback type_make_cb tooltip {Check Browser} xywh {484 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Check_Browser]);} + code0 {o->image(pixmap[(int)Type::Check_Browser]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Help_View"} callback type_make_cb tooltip {Help Browser} xywh {509 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Help_View]);} + code0 {o->image(pixmap[(int)Type::Help_View]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_File_Browser"} callback type_make_cb tooltip {File Browser} xywh {484 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_File_Browser]);} + code0 {o->image(pixmap[(int)Type::File_Browser]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Table"} callback type_make_cb tooltip Table xywh {509 71 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Table]);} + code0 {o->image(pixmap[(int)Type::Table]);} class {fld::widget::Bin_Button} } } @@ -1017,21 +1017,21 @@ else user_data {"Fl_Box"} callback type_make_cb tooltip Box xywh {542 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Box]);} + code0 {o->image(pixmap[(int)Type::Box]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Clock"} callback type_make_cb tooltip Clock xywh {567 21 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Clock]);} + code0 {o->image(pixmap[(int)Type::Clock]);} class {fld::widget::Bin_Button} } Fl_Button {} { user_data {"Fl_Progress"} callback type_make_cb tooltip Progress xywh {542 46 24 24} box THIN_UP_BOX - code0 {o->image(pixmap[ID_Progress]);} + code0 {o->image(pixmap[(int)Type::Progress]);} class {fld::widget::Bin_Button} } } diff --git a/fluid/panels/print_panel.cxx b/fluid/panels/print_panel.cxx deleted file mode 100644 index f585efee5..000000000 --- a/fluid/panels/print_panel.cxx +++ /dev/null @@ -1,586 +0,0 @@ -// -// FLUID print panel for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2020 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 -// - -// generated by Fast Light User Interface Designer (fluid) version 1.0500 - -#include "print_panel.h" -#include "app/fluid.h" -#include -#include "../src/flstring.h" -#include -#include - -Fl_Double_Window *print_panel=(Fl_Double_Window *)0; - -Fl_Group *print_panel_controls=(Fl_Group *)0; - -Fl_Choice *print_choice=(Fl_Choice *)0; - -static void cb_print_choice(Fl_Choice*, void*) { - print_update_status(); -} - -Fl_Button *print_properties=(Fl_Button *)0; - -static void cb_print_properties(Fl_Button*, void*) { - print_properties_panel->show(); -} - -Fl_Box *print_status=(Fl_Box *)0; - -Fl_Round_Button *print_all=(Fl_Round_Button *)0; - -static void cb_print_all(Fl_Round_Button*, void*) { - print_from->deactivate(); - print_to->deactivate(); -} - -Fl_Round_Button *print_pages=(Fl_Round_Button *)0; - -static void cb_print_pages(Fl_Round_Button*, void*) { - print_from->activate(); - print_to->activate(); -} - -Fl_Round_Button *print_selection=(Fl_Round_Button *)0; - -static void cb_print_selection(Fl_Round_Button*, void*) { - print_from->deactivate(); - print_to->deactivate(); -} - -Fl_Int_Input *print_from=(Fl_Int_Input *)0; - -Fl_Int_Input *print_to=(Fl_Int_Input *)0; - -Fl_Spinner *print_copies=(Fl_Spinner *)0; - -static void cb_print_copies(Fl_Spinner*, void*) { - if (print_copies->value() == 1) { - print_collate_button->deactivate(); - print_collate_group[0]->deactivate(); - print_collate_group[1]->deactivate(); - } else { - print_collate_button->activate(); - print_collate_group[0]->activate(); - print_collate_group[1]->activate(); - } -} - -Fl_Check_Button *print_collate_button=(Fl_Check_Button *)0; - -static void cb_print_collate_button(Fl_Check_Button*, void*) { - int i = print_collate_button->value() != 0; - print_collate_group[i]->show(); - print_collate_group[1 - i]->hide(); -} - -Fl_Group *print_collate_group[2]={(Fl_Group *)0}; - -static void cb_Cancel(Fl_Button*, void*) { - print_panel->hide(); -} - -Fl_Progress *print_progress=(Fl_Progress *)0; - -Fl_Double_Window *print_properties_panel=(Fl_Double_Window *)0; - -static void cb_print_properties_panel(Fl_Double_Window*, void*) { - print_properties_panel->hide(); - print_update_status(); -} - -Fl_Choice *print_page_size=(Fl_Choice *)0; - -Fl_Menu_Item menu_print_page_size[] = { - {"Letter", 0, 0, 0, 0, (uchar)FL_NORMAL_LABEL, 0, 14, 0}, - {"A4", 0, 0, 0, 0, (uchar)FL_NORMAL_LABEL, 0, 14, 0}, - {0,0,0,0,0,0,0,0,0} -}; - -#include - -static const char *idata_print_color[] = { -"24 24 17 1", -" \tc None", -".\tc #FFFF00", -"+\tc #C8FF00", -"@\tc #00FF00", -"#\tc #FFC800", -"$\tc #FF0000", -"%\tc #00FFFF", -"&\tc #000000", -"*\tc #FF00FF", -"=\tc #00FFC8", -"-\tc #FF00C8", -";\tc #00C800", -">\tc #C80000", -",\tc #0000C8", -"\'\tc #0000FF", -")\tc #00C8FF", -"!\tc #C800FF", -" ...... ", -" .......... ", -" ............ ", -" .............. ", -" .............. ", -" ................ ", -" ................ ", -" ................ ", -" +@@@@@@+#$$$$$$# ", -" %@@@@@@@&&$$$$$$$* ", -" %%@@@@@@&&&&$$$$$$** ", -" %%%=@@@@&&&&&&$$$$-*** ", -" %%%%@@@;&&&&&&>$$$**** ", -"%%%%%%@@&&&&&&&&$$******", -"%%%%%%%@&&&&&&&&$*******", -"%%%%%%%%,&&&&&&,********", -"%%%%%%%%\'\'\'\'\'\'\'\'********", -"%%%%%%%%\'\'\'\'\'\'\'\'********", -"%%%%%%%%\'\'\'\'\'\'\'\'********", -" %%%%%%%)\'\'\'\'\'\'!******* ", -" %%%%%%%%\'\'\'\'\'\'******** ", -" %%%%%%%%\'\'\'\'******** ", -" %%%%%%%%\'\'******** ", -" %%%%%% ****** " -}; -static Fl_Image *image_print_color() { - static Fl_Image *image = NULL; - if (!image) - image = new Fl_Pixmap(idata_print_color); - return image; -} - -static const char *idata_print_gray[] = { -"24 24 17 1", -" \tc None", -".\tc #E3E3E3", -"+\tc #D2D2D2", -"@\tc #969696", -"#\tc #C2C2C2", -"$\tc #4C4C4C", -"%\tc #B2B2B2", -"&\tc #000000", -"*\tc #696969", -"=\tc #ACACAC", -"-\tc #626262", -";\tc #767676", -">\tc #3C3C3C", -",\tc #161616", -"\'\tc #1C1C1C", -")\tc #929292", -"!\tc #585858", -" ...... ", -" .......... ", -" ............ ", -" .............. ", -" .............. ", -" ................ ", -" ................ ", -" ................ ", -" +@@@@@@+#$$$$$$# ", -" %@@@@@@@&&$$$$$$$* ", -" %%@@@@@@&&&&$$$$$$** ", -" %%%=@@@@&&&&&&$$$$-*** ", -" %%%%@@@;&&&&&&>$$$**** ", -"%%%%%%@@&&&&&&&&$$******", -"%%%%%%%@&&&&&&&&$*******", -"%%%%%%%%,&&&&&&,********", -"%%%%%%%%\'\'\'\'\'\'\'\'********", -"%%%%%%%%\'\'\'\'\'\'\'\'********", -"%%%%%%%%\'\'\'\'\'\'\'\'********", -" %%%%%%%)\'\'\'\'\'\'!******* ", -" %%%%%%%%\'\'\'\'\'\'******** ", -" %%%%%%%%\'\'\'\'******** ", -" %%%%%%%%\'\'******** ", -" %%%%%% ****** " -}; -static Fl_Image *image_print_gray() { - static Fl_Image *image = NULL; - if (!image) - image = new Fl_Pixmap(idata_print_gray); - return image; -} - -Fl_Button *print_output_mode[4]={(Fl_Button *)0}; - -static void cb_Save(Fl_Return_Button*, void*) { - print_properties_panel->hide(); - - char name[1024]; - int val; - const char *printer = (const char *)print_choice->menu()[print_choice->value()].user_data(); - - snprintf(name, sizeof(name), "%s/page_size", printer); - fluid_prefs.set(name, print_page_size->value()); - - snprintf(name, sizeof(name), "%s/output_mode", printer); - for (val = 0; val < 4; val ++) { - if (print_output_mode[val]->value()) break; - } - fluid_prefs.set(name, val); -} - -static void cb_Cancel1(Fl_Button*, void*) { - print_properties_panel->hide(); - print_update_status(); -} - -static void cb_Use(Fl_Button*, void*) { - print_properties_panel->hide(); -} - -Fl_Double_Window* make_print_panel() { - { print_panel = new Fl_Double_Window(465, 235, "Print"); - { print_panel_controls = new Fl_Group(10, 10, 447, 216); - { print_choice = new Fl_Choice(113, 10, 181, 25, "Printer:"); - print_choice->down_box(FL_BORDER_BOX); - print_choice->labelfont(1); - print_choice->callback((Fl_Callback*)cb_print_choice); - print_choice->when(FL_WHEN_CHANGED); - } // Fl_Choice* print_choice - { print_properties = new Fl_Button(294, 10, 105, 25, "Properties..."); - print_properties->callback((Fl_Callback*)cb_print_properties); - } // Fl_Button* print_properties - { print_status = new Fl_Box(111, 41, 288, 17, "printer/job status"); - print_status->align(Fl_Align(68|FL_ALIGN_INSIDE)); - } // Fl_Box* print_status - { Fl_Group* o = new Fl_Group(10, 86, 227, 105, "Print Range"); - o->box(FL_THIN_DOWN_BOX); - o->labelfont(1); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - { print_all = new Fl_Round_Button(20, 96, 38, 25, "All"); - print_all->type(102); - print_all->down_box(FL_ROUND_DOWN_BOX); - print_all->value(1); - print_all->callback((Fl_Callback*)cb_print_all); - } // Fl_Round_Button* print_all - { print_pages = new Fl_Round_Button(20, 126, 64, 25, "Pages"); - print_pages->type(102); - print_pages->down_box(FL_ROUND_DOWN_BOX); - print_pages->callback((Fl_Callback*)cb_print_pages); - } // Fl_Round_Button* print_pages - { print_selection = new Fl_Round_Button(20, 156, 82, 25, "Selection"); - print_selection->type(102); - print_selection->down_box(FL_ROUND_DOWN_BOX); - print_selection->callback((Fl_Callback*)cb_print_selection); - } // Fl_Round_Button* print_selection - { print_from = new Fl_Int_Input(136, 126, 28, 25, "From:"); - print_from->type(2); - print_from->textfont(4); - print_from->deactivate(); - } // Fl_Int_Input* print_from - { print_to = new Fl_Int_Input(199, 126, 28, 25, "To:"); - print_to->type(2); - print_to->textfont(4); - print_to->deactivate(); - } // Fl_Int_Input* print_to - o->end(); - } // Fl_Group* o - { Fl_Group* o = new Fl_Group(247, 86, 210, 105, "Copies"); - o->box(FL_THIN_DOWN_BOX); - o->labelfont(1); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - { print_copies = new Fl_Spinner(321, 96, 45, 25, "# Copies:"); - print_copies->callback((Fl_Callback*)cb_print_copies); - print_copies->when(FL_WHEN_CHANGED); - } // Fl_Spinner* print_copies - { print_collate_button = new Fl_Check_Button(376, 96, 64, 25, "Collate"); - print_collate_button->down_box(FL_DOWN_BOX); - print_collate_button->callback((Fl_Callback*)cb_print_collate_button); - print_collate_button->when(FL_WHEN_CHANGED); - print_collate_button->deactivate(); - } // Fl_Check_Button* print_collate_button - { print_collate_group[0] = new Fl_Group(257, 131, 191, 50); - print_collate_group[0]->deactivate(); - { Fl_Box* o = new Fl_Box(287, 141, 30, 40, "1"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - o->deactivate(); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(272, 136, 30, 40, "1"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - o->deactivate(); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(257, 131, 30, 40, "1"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - o->deactivate(); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(352, 141, 30, 40, "2"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - o->deactivate(); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(337, 136, 30, 40, "2"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - o->deactivate(); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(322, 131, 30, 40, "2"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - o->deactivate(); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(417, 141, 30, 40, "3"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - o->deactivate(); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(402, 136, 30, 40, "3"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - o->deactivate(); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(387, 131, 30, 40, "3"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - o->deactivate(); - } // Fl_Box* o - print_collate_group[0]->end(); - } // Fl_Group* print_collate_group[0] - { print_collate_group[1] = new Fl_Group(257, 131, 191, 50); - print_collate_group[1]->hide(); - print_collate_group[1]->deactivate(); - { Fl_Box* o = new Fl_Box(287, 141, 30, 40, "3"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(272, 136, 30, 40, "2"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(257, 131, 30, 40, "1"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(352, 141, 30, 40, "3"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(337, 136, 30, 40, "2"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(322, 131, 30, 40, "1"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(417, 141, 30, 40, "3"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(402, 136, 30, 40, "2"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - } // Fl_Box* o - { Fl_Box* o = new Fl_Box(387, 131, 30, 40, "1"); - o->box(FL_BORDER_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->labelsize(11); - o->align(Fl_Align(FL_ALIGN_BOTTOM_RIGHT|FL_ALIGN_INSIDE)); - } // Fl_Box* o - print_collate_group[1]->end(); - } // Fl_Group* print_collate_group[1] - o->end(); - } // Fl_Group* o - { Fl_Return_Button* o = new Fl_Return_Button(309, 201, 70, 25, "Print"); - o->callback((Fl_Callback*)print_cb); - } // Fl_Return_Button* o - { Fl_Button* o = new Fl_Button(389, 201, 68, 25, "Cancel"); - o->callback((Fl_Callback*)cb_Cancel); - } // Fl_Button* o - print_panel_controls->end(); - } // Fl_Group* print_panel_controls - { print_progress = new Fl_Progress(10, 203, 289, 21); - print_progress->selection_color((Fl_Color)4); - print_progress->hide(); - } // Fl_Progress* print_progress - print_panel->set_modal(); - print_panel->end(); - } // Fl_Double_Window* print_panel - { print_properties_panel = new Fl_Double_Window(290, 130, "Printer Properties"); - print_properties_panel->callback((Fl_Callback*)cb_print_properties_panel); - { print_page_size = new Fl_Choice(110, 10, 80, 25, "Page Size:"); - print_page_size->down_box(FL_BORDER_BOX); - print_page_size->labelfont(1); - print_page_size->labelsize(12); - print_page_size->menu(menu_print_page_size); - } // Fl_Choice* print_page_size - { Fl_Group* o = new Fl_Group(110, 45, 170, 40, "Output Mode:"); - o->labelfont(1); - o->labelsize(12); - o->align(Fl_Align(FL_ALIGN_LEFT)); - { print_output_mode[0] = new Fl_Button(110, 45, 30, 40); - print_output_mode[0]->type(102); - print_output_mode[0]->box(FL_BORDER_BOX); - print_output_mode[0]->down_box(FL_BORDER_BOX); - print_output_mode[0]->value(1); - print_output_mode[0]->color(FL_BACKGROUND2_COLOR); - print_output_mode[0]->selection_color(FL_FOREGROUND_COLOR); - print_output_mode[0]->image( image_print_color() ); - } // Fl_Button* print_output_mode[0] - { print_output_mode[1] = new Fl_Button(150, 50, 40, 30); - print_output_mode[1]->type(102); - print_output_mode[1]->box(FL_BORDER_BOX); - print_output_mode[1]->down_box(FL_BORDER_BOX); - print_output_mode[1]->color(FL_BACKGROUND2_COLOR); - print_output_mode[1]->selection_color(FL_FOREGROUND_COLOR); - print_output_mode[1]->image( image_print_color() ); - } // Fl_Button* print_output_mode[1] - { print_output_mode[2] = new Fl_Button(200, 45, 30, 40); - print_output_mode[2]->type(102); - print_output_mode[2]->box(FL_BORDER_BOX); - print_output_mode[2]->down_box(FL_BORDER_BOX); - print_output_mode[2]->color(FL_BACKGROUND2_COLOR); - print_output_mode[2]->selection_color(FL_FOREGROUND_COLOR); - print_output_mode[2]->image( image_print_gray() ); - } // Fl_Button* print_output_mode[2] - { print_output_mode[3] = new Fl_Button(240, 50, 40, 30); - print_output_mode[3]->type(102); - print_output_mode[3]->box(FL_BORDER_BOX); - print_output_mode[3]->down_box(FL_BORDER_BOX); - print_output_mode[3]->color(FL_BACKGROUND2_COLOR); - print_output_mode[3]->selection_color(FL_FOREGROUND_COLOR); - print_output_mode[3]->image( image_print_gray() ); - } // Fl_Button* print_output_mode[3] - o->end(); - } // Fl_Group* o - { Fl_Return_Button* o = new Fl_Return_Button(123, 95, 79, 25, "Save"); - o->callback((Fl_Callback*)cb_Save); - } // Fl_Return_Button* o - { Fl_Button* o = new Fl_Button(212, 95, 68, 25, "Cancel"); - o->callback((Fl_Callback*)cb_Cancel1); - } // Fl_Button* o - { Fl_Button* o = new Fl_Button(60, 95, 53, 25, "Use"); - o->callback((Fl_Callback*)cb_Use); - } // Fl_Button* o - print_properties_panel->set_modal(); - print_properties_panel->end(); - } // Fl_Double_Window* print_properties_panel - return print_properties_panel; -} -void print_cb(Fl_Return_Button *, void *); - -void print_load() { - FILE *lpstat; - char line[1024], name[1024], *nptr, qname[2048], *qptr, defname[1024]; - int i; - - if (print_choice->size() > 1) { - for (i = 1; print_choice->text(i); i ++) { - free(print_choice->menu()[i].user_data()); - } - } - - print_choice->clear(); - print_choice->add("Print To File", 0, 0, 0, FL_MENU_DIVIDER); - print_choice->value(0); - - defname[0] = '\0'; - - if ((lpstat = popen("LC_MESSAGES=C LANG=C lpstat -p -d", "r")) != NULL) { - while (fgets(line, sizeof(line), lpstat)) { - if (!strncmp(line, "printer ", 8) && - sscanf(line + 8, "%s", name) == 1) { - for (nptr = name, qptr = qname; *nptr; *qptr++ = *nptr++) { - if (*nptr == '/') *qptr++ = '\\'; - } - *qptr = '\0'; - - print_choice->add(qname, 0, 0, (void *)fl_strdup(name), 0); - } else if (!strncmp(line, "system default destination: ", 28)) { - if (sscanf(line + 28, "%s", defname) != 1) defname[0] = '\0'; - } - } - pclose(lpstat); - } - - if (defname[0]) { - for (i = 1; print_choice->text(i); i ++) { - if (!strcmp((char *)print_choice->menu()[i].user_data(), defname)) { - print_choice->value(i); - break; - } - } - } else if (print_choice->size() > 2) print_choice->value(1); - - - print_update_status(); -} - -void print_update_status() { - FILE *lpstat; - char command[1024]; - static char status[1024]; - const char *printer = (const char *)print_choice->menu()[print_choice->value()].user_data(); - - if (print_choice->value()) { - snprintf(command, sizeof(command), "lpstat -p '%s'", printer); - if ((lpstat = popen(command, "r")) != NULL) { - if (fgets(status, sizeof(status), lpstat)==0) { /* ignore */ } - pclose(lpstat); - } else strcpy(status, "printer status unavailable"); - } else status[0] = '\0'; - - print_status->label(status); - - char name[1024]; - int val; - - snprintf(name, sizeof(name), "%s/page_size", printer); - fluid_prefs.get(name, val, 0); - print_page_size->value(val); - - snprintf(name, sizeof(name), "%s/output_mode", printer); - fluid_prefs.get(name, val, 0); - print_output_mode[val]->setonly(); -} diff --git a/fluid/panels/print_panel.fl b/fluid/panels/print_panel.fl deleted file mode 100644 index ae1899c3e..000000000 --- a/fluid/panels/print_panel.fl +++ /dev/null @@ -1,358 +0,0 @@ -# data file for the Fltk User Interface Designer (fluid) -version 1.0500 -header_name {.h} -code_name {.cxx} -comment {// -// FLUID print panel for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2020 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 -// -} {in_source in_header -} - -decl {\#include "app/fluid.h"} {private local -} - -decl {\#include } {private local -} - -decl {\#include "../src/flstring.h"} {private local -} - -decl {\#include } {private local -} - -decl {\#include } {private local -} - -Function {make_print_panel()} {open -} { - Fl_Window print_panel { - label Print selected - xywh {465 222 465 235} type Double modal visible - } { - Fl_Group print_panel_controls {open - xywh {10 10 447 216} - } { - Fl_Choice print_choice { - label {Printer:} - callback {print_update_status();} open - xywh {113 10 181 25} down_box BORDER_BOX labelfont 1 when 1 - } {} - Fl_Button print_properties { - label {Properties...} - callback {print_properties_panel->show();} - xywh {294 10 105 25} - } - Fl_Box print_status { - label {printer/job status} - xywh {111 41 288 17} align 84 - } - Fl_Group {} { - label {Print Range} open - xywh {10 86 227 105} box THIN_DOWN_BOX labelfont 1 align 5 - } { - Fl_Round_Button print_all { - label All - callback {print_from->deactivate(); -print_to->deactivate();} - xywh {20 96 38 25} type Radio down_box ROUND_DOWN_BOX value 1 - } - Fl_Round_Button print_pages { - label Pages - callback {print_from->activate(); -print_to->activate();} - xywh {20 126 64 25} type Radio down_box ROUND_DOWN_BOX - } - Fl_Round_Button print_selection { - label Selection - callback {print_from->deactivate(); -print_to->deactivate();} - xywh {20 156 82 25} type Radio down_box ROUND_DOWN_BOX - } - Fl_Input print_from { - label {From:} - xywh {136 126 28 25} type Int textfont 4 deactivate - } - Fl_Input print_to { - label {To:} - xywh {199 126 28 25} type Int textfont 4 deactivate - } - } - Fl_Group {} { - label Copies open - xywh {247 86 210 105} box THIN_DOWN_BOX labelfont 1 align 5 - } { - Fl_Spinner print_copies { - label {\# Copies:} - callback {if (print_copies->value() == 1) { - print_collate_button->deactivate(); - print_collate_group[0]->deactivate(); - print_collate_group[1]->deactivate(); -} else { - print_collate_button->activate(); - print_collate_group[0]->activate(); - print_collate_group[1]->activate(); -}} - xywh {321 96 45 25} when 1 - } - Fl_Check_Button print_collate_button { - label Collate - callback {int i = print_collate_button->value() != 0; -print_collate_group[i]->show(); -print_collate_group[1 - i]->hide();} - xywh {376 96 64 25} down_box DOWN_BOX when 1 deactivate - } - Fl_Group {print_collate_group[0]} { - xywh {257 131 191 50} deactivate - } { - Fl_Box {} { - label 1 - xywh {287 141 30 40} box BORDER_BOX color 7 labelsize 11 align 26 deactivate - } - Fl_Box {} { - label 1 - xywh {272 136 30 40} box BORDER_BOX color 7 labelsize 11 align 26 deactivate - } - Fl_Box {} { - label 1 - xywh {257 131 30 40} box BORDER_BOX color 7 labelsize 11 align 26 deactivate - } - Fl_Box {} { - label 2 - xywh {352 141 30 40} box BORDER_BOX color 7 labelsize 11 align 26 deactivate - } - Fl_Box {} { - label 2 - xywh {337 136 30 40} box BORDER_BOX color 7 labelsize 11 align 26 deactivate - } - Fl_Box {} { - label 2 - xywh {322 131 30 40} box BORDER_BOX color 7 labelsize 11 align 26 deactivate - } - Fl_Box {} { - label 3 - xywh {417 141 30 40} box BORDER_BOX color 7 labelsize 11 align 26 deactivate - } - Fl_Box {} { - label 3 - xywh {402 136 30 40} box BORDER_BOX color 7 labelsize 11 align 26 deactivate - } - Fl_Box {} { - label 3 - xywh {387 131 30 40} box BORDER_BOX color 7 labelsize 11 align 26 deactivate - } - } - Fl_Group {print_collate_group[1]} { - xywh {257 131 191 50} hide deactivate - } { - Fl_Box {} { - label 3 - xywh {287 141 30 40} box BORDER_BOX color 7 labelsize 11 align 26 - } - Fl_Box {} { - label 2 - xywh {272 136 30 40} box BORDER_BOX color 7 labelsize 11 align 26 - } - Fl_Box {} { - label 1 - xywh {257 131 30 40} box BORDER_BOX color 7 labelsize 11 align 26 - } - Fl_Box {} { - label 3 - xywh {352 141 30 40} box BORDER_BOX color 7 labelsize 11 align 26 - } - Fl_Box {} { - label 2 - xywh {337 136 30 40} box BORDER_BOX color 7 labelsize 11 align 26 - } - Fl_Box {} { - label 1 - xywh {322 131 30 40} box BORDER_BOX color 7 labelsize 11 align 26 - } - Fl_Box {} { - label 3 - xywh {417 141 30 40} box BORDER_BOX color 7 labelsize 11 align 26 - } - Fl_Box {} { - label 2 - xywh {402 136 30 40} box BORDER_BOX color 7 labelsize 11 align 26 - } - Fl_Box {} { - label 1 - xywh {387 131 30 40} box BORDER_BOX color 7 labelsize 11 align 26 - } - } - } - Fl_Return_Button {} { - label Print - callback print_cb - xywh {309 201 70 25} - } - Fl_Button {} { - label Cancel - callback {print_panel->hide();} - xywh {389 201 68 25} - } - } - Fl_Progress print_progress { - xywh {10 203 289 21} selection_color 4 hide - } - } - Fl_Window print_properties_panel { - label {Printer Properties} - callback {print_properties_panel->hide(); -print_update_status();} - xywh {462 486 290 130} type Double modal visible - } { - Fl_Choice print_page_size { - label {Page Size:} - xywh {110 10 80 25} down_box BORDER_BOX labelfont 1 labelsize 12 - } { - MenuItem {} { - label Letter - xywh {0 0 35 25} - } - MenuItem {} { - label A4 - xywh {0 0 35 25} - } - } - Fl_Group {} { - label {Output Mode:} open - xywh {110 45 170 40} labelfont 1 labelsize 12 align 4 - } { - Fl_Button {print_output_mode[0]} { - image {../pixmaps/print_color.xpm} compress_image 0 xywh {110 45 30 40} type Radio box BORDER_BOX down_box BORDER_BOX value 1 color 7 selection_color 0 - } - Fl_Button {print_output_mode[1]} { - image {../pixmaps/print_color.xpm} compress_image 0 xywh {150 50 40 30} type Radio box BORDER_BOX down_box BORDER_BOX color 7 selection_color 0 - } - Fl_Button {print_output_mode[2]} { - image {../pixmaps/print_gray.xpm} compress_image 0 xywh {200 45 30 40} type Radio box BORDER_BOX down_box BORDER_BOX color 7 selection_color 0 - } - Fl_Button {print_output_mode[3]} { - image {../pixmaps/print_gray.xpm} compress_image 0 xywh {240 50 40 30} type Radio box BORDER_BOX down_box BORDER_BOX color 7 selection_color 0 - } - } - Fl_Return_Button {} { - label Save - callback {print_properties_panel->hide(); - -char name[1024]; -int val; -const char *printer = (const char *)print_choice->menu()[print_choice->value()].user_data(); - -snprintf(name, sizeof(name), "%s/page_size", printer); -fluid_prefs.set(name, print_page_size->value()); - -snprintf(name, sizeof(name), "%s/output_mode", printer); -for (val = 0; val < 4; val ++) { - if (print_output_mode[val]->value()) break; -} -fluid_prefs.set(name, val);} - xywh {123 95 79 25} - } - Fl_Button {} { - label Cancel - callback {print_properties_panel->hide(); -print_update_status();} - xywh {212 95 68 25} - } - Fl_Button {} { - label Use - callback {print_properties_panel->hide();} - xywh {60 95 53 25} - } - } -} - -decl {void print_cb(Fl_Return_Button *, void *);} {public local -} - -Function {print_load()} {open return_type void -} { - code {FILE *lpstat; -char line[1024], name[1024], *nptr, qname[2048], *qptr, defname[1024]; -int i; - -if (print_choice->size() > 1) { - for (i = 1; print_choice->text(i); i ++) { - free(print_choice->menu()[i].user_data()); - } -} - -print_choice->clear(); -print_choice->add("Print To File", 0, 0, 0, FL_MENU_DIVIDER); -print_choice->value(0); - -defname[0] = '\\0'; - -if ((lpstat = popen("LC_MESSAGES=C LANG=C lpstat -p -d", "r")) != NULL) { - while (fgets(line, sizeof(line), lpstat)) { - if (!strncmp(line, "printer ", 8) && - sscanf(line + 8, "%s", name) == 1) { - for (nptr = name, qptr = qname; *nptr; *qptr++ = *nptr++) { - if (*nptr == '/') *qptr++ = '\\\\'; - } - *qptr = '\\0'; - - print_choice->add(qname, 0, 0, (void *)fl_strdup(name), 0); - } else if (!strncmp(line, "system default destination: ", 28)) { - if (sscanf(line + 28, "%s", defname) != 1) defname[0] = '\\0'; - } - } - pclose(lpstat); -} - -if (defname[0]) { - for (i = 1; print_choice->text(i); i ++) { - if (!strcmp((char *)print_choice->menu()[i].user_data(), defname)) { - print_choice->value(i); - break; - } - } -} else if (print_choice->size() > 2) print_choice->value(1); - - -print_update_status();} {} -} - -Function {print_update_status()} {open return_type void -} { - code {FILE *lpstat; -char command[1024]; -static char status[1024]; -const char *printer = (const char *)print_choice->menu()[print_choice->value()].user_data(); - -if (print_choice->value()) { - snprintf(command, sizeof(command), "lpstat -p '%s'", printer); - if ((lpstat = popen(command, "r")) != NULL) { - if (fgets(status, sizeof(status), lpstat)==0) { /* ignore */ } - pclose(lpstat); - } else strcpy(status, "printer status unavailable"); -} else status[0] = '\\0'; - -print_status->label(status); - -char name[1024]; -int val; - -snprintf(name, sizeof(name), "%s/page_size", printer); -fluid_prefs.get(name, val, 0); -print_page_size->value(val); - -snprintf(name, sizeof(name), "%s/output_mode", printer); -fluid_prefs.get(name, val, 0); -print_output_mode[val]->setonly();} {} -} diff --git a/fluid/panels/print_panel.h b/fluid/panels/print_panel.h deleted file mode 100644 index fed8fd3c0..000000000 --- a/fluid/panels/print_panel.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// FLUID print panel for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2020 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 -// - -// generated by Fast Light User Interface Designer (fluid) version 1.0500 - -#ifndef print_panel_h -#define print_panel_h -#include -#include -extern Fl_Double_Window *print_panel; -#include -extern Fl_Group *print_panel_controls; -#include -extern Fl_Choice *print_choice; -#include -extern Fl_Button *print_properties; -#include -extern Fl_Box *print_status; -#include -extern Fl_Round_Button *print_all; -extern Fl_Round_Button *print_pages; -extern Fl_Round_Button *print_selection; -#include -extern Fl_Int_Input *print_from; -extern Fl_Int_Input *print_to; -#include -extern Fl_Spinner *print_copies; -#include -extern Fl_Check_Button *print_collate_button; -extern Fl_Group *print_collate_group[2]; -#include -extern void print_cb(Fl_Return_Button*, void*); -#include -extern Fl_Progress *print_progress; -extern Fl_Double_Window *print_properties_panel; -extern Fl_Choice *print_page_size; -extern Fl_Button *print_output_mode[4]; -Fl_Double_Window* make_print_panel(); -extern Fl_Menu_Item menu_print_page_size[]; -extern void print_cb(Fl_Return_Button *, void *); -void print_load(); -void print_update_status(); -#endif diff --git a/fluid/panels/settings_panel.cxx b/fluid/panels/settings_panel.cxx index 358f90c8d..432613623 100644 --- a/fluid/panels/settings_panel.cxx +++ b/fluid/panels/settings_panel.cxx @@ -17,8 +17,8 @@ // generated by Fast Light User Interface Designer (fluid) version 1.0500 #include "settings_panel.h" -#include "app/project.h" -#include "app/undo.h" +#include "Project.h" +#include "proj/undo.h" #include #include #include @@ -230,7 +230,7 @@ static const unsigned char idata_general_64[] = 150,102,123,63,248,127,27,97,180,206,27,14,172,151,0,0,0,0,73,69,78,68,174,66, 96,130}; static Fl_Image *image_general_64() { - static Fl_Image *image = NULL; + static Fl_Image *image = 0L; if (!image) image = new Fl_PNG_Image("general_64.png", idata_general_64, 2162); return image; @@ -242,36 +242,40 @@ static void cb_(Fl_Group* o, void* v) { Fl_Scheme_Choice *scheme_choice=(Fl_Scheme_Choice *)0; +static void cb_scheme_choice(Fl_Scheme_Choice* o, void*) { + Fluid.set_scheme(o->text(o->value())); +} + Fl_Check_Button *tooltips_button=(Fl_Check_Button *)0; static void cb_tooltips_button(Fl_Check_Button*, void*) { Fl_Tooltip::enable(tooltips_button->value()); - fluid_prefs.set("show_tooltips", tooltips_button->value()); + Fluid.preferences.set("show_tooltips", tooltips_button->value()); } Fl_Check_Button *completion_button=(Fl_Check_Button *)0; static void cb_completion_button(Fl_Check_Button*, void*) { - fluid_prefs.set("show_completion_dialogs", completion_button->value()); + Fluid.preferences.set("show_completion_dialogs", completion_button->value()); } Fl_Check_Button *openlast_button=(Fl_Check_Button *)0; static void cb_openlast_button(Fl_Check_Button*, void*) { - fluid_prefs.set("open_previous_file", openlast_button->value()); + Fluid.preferences.set("open_previous_file", openlast_button->value()); } Fl_Check_Button *prevpos_button=(Fl_Check_Button *)0; static void cb_prevpos_button(Fl_Check_Button*, void*) { - fluid_prefs.set("prev_window_pos", prevpos_button->value()); + Fluid.preferences.set("prev_window_pos", prevpos_button->value()); } Fl_Check_Button *show_comments_button=(Fl_Check_Button *)0; static void cb_show_comments_button(Fl_Check_Button*, void*) { - show_comments = show_comments_button->value(); - fluid_prefs.set("show_comments", show_comments); + Fluid.show_comments = show_comments_button->value(); + Fluid.preferences.set("Fluid.show_comments", Fluid.show_comments); redraw_browser(); } @@ -282,24 +286,24 @@ static void cb_1(Fl_Group* o, void* v) { Fl_Spinner *recent_spinner=(Fl_Spinner *)0; static void cb_recent_spinner(Fl_Spinner*, void*) { - fluid_prefs.set("recent_files", recent_spinner->value()); - load_history(); + Fluid.preferences.set("recent_files", recent_spinner->value()); + Fluid.history.load(); } Fl_Check_Button *use_external_editor_button=(Fl_Check_Button *)0; static void cb_use_external_editor_button(Fl_Check_Button*, void*) { - G_use_external_editor = use_external_editor_button->value(); - fluid_prefs.set("use_external_editor", G_use_external_editor); + Fluid.use_external_editor = use_external_editor_button->value(); + Fluid.preferences.set("use_external_editor", Fluid.use_external_editor); redraw_browser(); } Fl_Input *editor_command_input=(Fl_Input *)0; static void cb_editor_command_input(Fl_Input*, void*) { - strncpy(G_external_editor_command, editor_command_input->value(), sizeof(G_external_editor_command)-1); - G_external_editor_command[sizeof(G_external_editor_command)-1] = 0; - fluid_prefs.set("external_editor_command", G_external_editor_command); + strncpy(Fluid.external_editor_command, editor_command_input->value(), sizeof(Fluid.external_editor_command)-1); + Fluid.external_editor_command[sizeof(Fluid.external_editor_command)-1] = 0; + Fluid.preferences.set("external_editor_command", Fluid.external_editor_command); redraw_browser(); } @@ -359,7 +363,7 @@ static const unsigned char idata_document_64[] = 147,239,104,255,202,153,244,20,250,15,100,60,232,29,230,9,101,148,0,0,0,0,73,69, 78,68,174,66,96,130}; static Fl_Image *image_document_64() { - static Fl_Image *image = NULL; + static Fl_Image *image = 0L; if (!image) image = new Fl_PNG_Image("document_64.png", idata_document_64, 927); return image; @@ -369,11 +373,11 @@ Fl_Input *header_file_input=(Fl_Input *)0; static void cb_header_file_input(Fl_Input* o, void* v) { if (v == LOAD) { - o->value(g_project.header_file_name.c_str()); + o->value(Fluid.proj.header_file_name.c_str()); } else { - if (strcmp(g_project.header_file_name.c_str(), o->value())) { - g_project.header_file_name = o->value(); - set_modflag(1); + if (strcmp(Fluid.proj.header_file_name.c_str(), o->value())) { + Fluid.proj.header_file_name = o->value(); + Fluid.proj.set_modflag(1); } } } @@ -382,11 +386,11 @@ Fl_Input *code_file_input=(Fl_Input *)0; static void cb_code_file_input(Fl_Input* o, void* v) { if (v == LOAD) { - o->value(g_project.code_file_name.c_str()); + o->value(Fluid.proj.code_file_name.c_str()); } else { - if (strcmp(g_project.code_file_name.c_str(), o->value())) { - g_project.code_file_name = o->value(); - set_modflag(1); + if (strcmp(Fluid.proj.code_file_name.c_str(), o->value())) { + Fluid.proj.code_file_name = o->value(); + Fluid.proj.set_modflag(1); } } } @@ -395,11 +399,11 @@ Fl_Check_Button *include_H_from_C_button=(Fl_Check_Button *)0; static void cb_include_H_from_C_button(Fl_Check_Button* o, void* v) { if (v == LOAD) { - o->value(g_project.include_H_from_C); + o->value(Fluid.proj.include_H_from_C); } else { - if (g_project.include_H_from_C != o->value()) { - set_modflag(1); - g_project.include_H_from_C = o->value(); + if (Fluid.proj.include_H_from_C != o->value()) { + Fluid.proj.set_modflag(1); + Fluid.proj.include_H_from_C = o->value(); } } } @@ -408,11 +412,11 @@ Fl_Check_Button *use_FL_COMMAND_button=(Fl_Check_Button *)0; static void cb_use_FL_COMMAND_button(Fl_Check_Button* o, void* v) { if (v == LOAD) { - o->value(g_project.use_FL_COMMAND); + o->value(Fluid.proj.use_FL_COMMAND); } else { - if (g_project.use_FL_COMMAND != o->value()) { - set_modflag(1); - g_project.use_FL_COMMAND = o->value(); + if (Fluid.proj.use_FL_COMMAND != o->value()) { + Fluid.proj.set_modflag(1); + Fluid.proj.use_FL_COMMAND = o->value(); } } } @@ -421,11 +425,11 @@ Fl_Check_Button *utf8_in_src_button=(Fl_Check_Button *)0; static void cb_utf8_in_src_button(Fl_Check_Button* o, void* v) { if (v == LOAD) { - o->value(g_project.utf8_in_src); + o->value(Fluid.proj.utf8_in_src); } else { - if (g_project.utf8_in_src != o->value()) { - set_modflag(1); - g_project.utf8_in_src = o->value(); + if (Fluid.proj.utf8_in_src != o->value()) { + Fluid.proj.set_modflag(1); + Fluid.proj.utf8_in_src = o->value(); } } } @@ -434,11 +438,11 @@ Fl_Check_Button *avoid_early_includes_button=(Fl_Check_Button *)0; static void cb_avoid_early_includes_button(Fl_Check_Button* o, void* v) { if (v == LOAD) { - o->value(g_project.avoid_early_includes); + o->value(Fluid.proj.avoid_early_includes); } else { - if (g_project.avoid_early_includes != o->value()) { - set_modflag(1); - g_project.avoid_early_includes = o->value(); + if (Fluid.proj.avoid_early_includes != o->value()) { + Fluid.proj.set_modflag(1); + Fluid.proj.avoid_early_includes = o->value(); } } } @@ -447,11 +451,11 @@ Fl_Check_Button *w_proj_mergeback=(Fl_Check_Button *)0; static void cb_w_proj_mergeback(Fl_Check_Button* o, void* v) { if (v == LOAD) { - o->value(g_project.write_mergeback_data); + o->value(Fluid.proj.write_mergeback_data); } else { - if (g_project.write_mergeback_data != o->value()) { - set_modflag(1); - g_project.write_mergeback_data = o->value(); + if (Fluid.proj.write_mergeback_data != o->value()) { + Fluid.proj.set_modflag(1); + Fluid.proj.write_mergeback_data = o->value(); } } } @@ -485,7 +489,7 @@ static const unsigned char idata_layout_64[] = 219,234,153,20,127,159,185,12,250,99,90,14,203,239,127,120,165,154,78,208,47, 215,15,118,242,56,45,94,1,0,0,0,0,73,69,78,68,174,66,96,130}; static Fl_Image *image_layout_64() { - static Fl_Image *image = NULL; + static Fl_Image *image = 0L; if (!image) image = new Fl_PNG_Image("layout_64.png", idata_layout_64, 481); return image; @@ -495,11 +499,11 @@ Fl_Choice *layout_choice=(Fl_Choice *)0; static void cb_layout_choice(Fl_Choice* o, void* v) { if (v == LOAD) { - o->value(g_layout_list.current_suite()); + o->value(Fluid.layout_list.current_suite()); } else { int index = o->value(); - g_layout_list.current_suite(index); - g_layout_list.update_dialogs(); + Fluid.layout_list.current_suite(index); + Fluid.layout_list.update_dialogs(); } } @@ -515,21 +519,21 @@ static void cb_2(Fl_Button*, void* v) { if (v == LOAD) return; std::string old_name = "Copy of "; - old_name.append(g_layout_list[g_layout_list.current_suite()].name_); + old_name.append(Fluid.layout_list[Fluid.layout_list.current_suite()].name_); const char *new_name = fl_input("Enter a name for the new layout:", old_name.c_str()); - if (new_name == NULL) + if (new_name == nullptr) return; - g_layout_list.add(new_name); - g_layout_list.update_dialogs(); + Fluid.layout_list.add(new_name); + Fluid.layout_list.update_dialogs(); } Fl_Menu_Button *w_layout_menu=(Fl_Menu_Button *)0; static void cb_w_layout_menu(Fl_Menu_Button*, void* v) { if (v == LOAD) { - Fd_Layout_Suite &suite = g_layout_list[g_layout_list.current_suite()]; - if (suite.storage_ == FD_STORE_INTERNAL) { + fld::app::Layout_Suite &suite = Fluid.layout_list[Fluid.layout_list.current_suite()]; + if (suite.storage_ == fld::Tool_Store::INTERNAL) { w_layout_menu_rename->deactivate(); for (int i=1; i<4; i++) w_layout_menu_storage[i]->deactivate(); w_layout_menu_delete->deactivate(); @@ -538,44 +542,44 @@ static void cb_w_layout_menu(Fl_Menu_Button*, void* v) { for (int i=1; i<4; i++) w_layout_menu_storage[i]->activate(); w_layout_menu_delete->activate(); } - w_layout_menu_storage[suite.storage_]->setonly(); + w_layout_menu_storage[static_cast(suite.storage_)]->setonly(); } } static void cb_w_layout_menu_rename(Fl_Menu_*, void*) { // Rename the current layout suite - std::string old_name = g_layout_list[g_layout_list.current_suite()].name_; + std::string old_name = Fluid.layout_list[Fluid.layout_list.current_suite()].name_; const char *new_name = fl_input("Enter a new name for the layout:", old_name.c_str()); - if (new_name == NULL) + if (new_name == nullptr) return; - g_layout_list.rename(new_name); - g_layout_list.update_dialogs(); + Fluid.layout_list.rename(new_name); + Fluid.layout_list.update_dialogs(); } static void cb_w_layout_menu_storage(Fl_Menu_*, void*) { - Fd_Layout_Suite &suite = g_layout_list[g_layout_list.current_suite()]; - suite.storage(FD_STORE_INTERNAL); - g_layout_list.update_dialogs(); + fld::app::Layout_Suite &suite = Fluid.layout_list[Fluid.layout_list.current_suite()]; + suite.storage(fld::Tool_Store::INTERNAL); + Fluid.layout_list.update_dialogs(); } static void cb_w_layout_menu_storage1(Fl_Menu_*, void*) { - Fd_Layout_Suite &suite = g_layout_list[g_layout_list.current_suite()]; - suite.storage(FD_STORE_USER); - g_layout_list.update_dialogs(); + fld::app::Layout_Suite &suite = Fluid.layout_list[Fluid.layout_list.current_suite()]; + suite.storage(fld::Tool_Store::USER); + Fluid.layout_list.update_dialogs(); } static void cb_w_layout_menu_storage2(Fl_Menu_*, void*) { - Fd_Layout_Suite &suite = g_layout_list[g_layout_list.current_suite()]; - suite.storage(FD_STORE_PROJECT); - g_layout_list.update_dialogs(); + fld::app::Layout_Suite &suite = Fluid.layout_list[Fluid.layout_list.current_suite()]; + suite.storage(fld::Tool_Store::PROJECT); + Fluid.layout_list.update_dialogs(); } static void cb_w_layout_menu_storage3(Fl_Menu_*, void*) { - Fd_Layout_Suite &suite = g_layout_list[g_layout_list.current_suite()]; - suite.storage(FD_STORE_FILE); - g_layout_list.update_dialogs(); + fld::app::Layout_Suite &suite = Fluid.layout_list[Fluid.layout_list.current_suite()]; + suite.storage(fld::Tool_Store::FILE); + Fluid.layout_list.update_dialogs(); } static void cb_w_layout_menu_load(Fl_Menu_*, void*) { @@ -588,9 +592,9 @@ static void cb_w_layout_menu_load(Fl_Menu_*, void*) { if (fnfc.show() != 0) return; const char *new_filename = fnfc.filename(); if (!new_filename) return; - g_layout_list.load(new_filename); - //g_layout_list.current_suite(n); - g_layout_list.update_dialogs(); + Fluid.layout_list.load(new_filename); + //Fluid.layout_list.current_suite(n); + Fluid.layout_list.update_dialogs(); } static void cb_w_layout_menu_save(Fl_Menu_*, void*) { @@ -600,21 +604,21 @@ static void cb_w_layout_menu_save(Fl_Menu_*, void*) { fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); fnfc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT); fnfc.filter("FLUID Layouts\t*.fll\n"); - std::string filename = g_layout_list.filename_; + std::string filename = Fluid.layout_list.filename_; fnfc.directory(fl_filename_path_str(filename).c_str()); fnfc.preset_file(fl_filename_name_str(filename).c_str()); if (fnfc.show() != 0) return; const char *new_filename = fnfc.filename(); if (!new_filename) return; - g_layout_list.filename_ = new_filename; - g_layout_list.save(new_filename); + Fluid.layout_list.filename_ = new_filename; + Fluid.layout_list.save(new_filename); } static void cb_w_layout_menu_delete(Fl_Menu_*, void*) { // remove the current suite - g_layout_list.remove(g_layout_list.current_suite()); - g_layout_list.update_dialogs(); + Fluid.layout_list.remove(Fluid.layout_list.current_suite()); + Fluid.layout_list.update_dialogs(); } Fl_Menu_Item menu_w_layout_menu[] = { @@ -633,193 +637,193 @@ Fl_Button *preset_choice[3]={(Fl_Button *)0}; static void cb_Left(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->left_window_margin); + o->value((double)Fluid.proj.layout->left_window_margin); } else { - layout->left_window_margin = (int)o->value(); + Fluid.proj.layout->left_window_margin = (int)o->value(); } } static void cb_Top(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->top_window_margin); + o->value((double)Fluid.proj.layout->top_window_margin); } else { - layout->top_window_margin = (int)o->value(); + Fluid.proj.layout->top_window_margin = (int)o->value(); } } static void cb_Right(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->right_window_margin); + o->value((double)Fluid.proj.layout->right_window_margin); } else { - layout->right_window_margin = (int)o->value(); + Fluid.proj.layout->right_window_margin = (int)o->value(); } } static void cb_Bottom(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->bottom_window_margin); + o->value((double)Fluid.proj.layout->bottom_window_margin); } else { - layout->bottom_window_margin = (int)o->value(); + Fluid.proj.layout->bottom_window_margin = (int)o->value(); } } static void cb_Horizontal(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->window_grid_x); + o->value((double)Fluid.proj.layout->window_grid_x); } else { - layout->window_grid_x = (int)o->value(); + Fluid.proj.layout->window_grid_x = (int)o->value(); } } static void cb_Vertical(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->window_grid_y); + o->value((double)Fluid.proj.layout->window_grid_y); } else { - layout->window_grid_y = (int)o->value(); + Fluid.proj.layout->window_grid_y = (int)o->value(); } } static void cb_Left1(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->left_group_margin); + o->value((double)Fluid.proj.layout->left_group_margin); } else { - layout->left_group_margin = (int)o->value(); + Fluid.proj.layout->left_group_margin = (int)o->value(); } } static void cb_Top1(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->top_group_margin); + o->value((double)Fluid.proj.layout->top_group_margin); } else { - layout->top_group_margin = (int)o->value(); + Fluid.proj.layout->top_group_margin = (int)o->value(); } } static void cb_Right1(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->right_group_margin); + o->value((double)Fluid.proj.layout->right_group_margin); } else { - layout->right_group_margin = (int)o->value(); + Fluid.proj.layout->right_group_margin = (int)o->value(); } } static void cb_Bottom1(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->bottom_group_margin); + o->value((double)Fluid.proj.layout->bottom_group_margin); } else { - layout->bottom_group_margin = (int)o->value(); + Fluid.proj.layout->bottom_group_margin = (int)o->value(); } } static void cb_Horizontal1(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->group_grid_x); + o->value((double)Fluid.proj.layout->group_grid_x); } else { - layout->group_grid_x = (int)o->value(); + Fluid.proj.layout->group_grid_x = (int)o->value(); } } static void cb_Vertical1(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->group_grid_y); + o->value((double)Fluid.proj.layout->group_grid_y); } else { - layout->group_grid_y = (int)o->value(); + Fluid.proj.layout->group_grid_y = (int)o->value(); } } static void cb_Top2(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->top_tabs_margin); + o->value((double)Fluid.proj.layout->top_tabs_margin); } else { - layout->top_tabs_margin = (int)o->value(); + Fluid.proj.layout->top_tabs_margin = (int)o->value(); } } static void cb_Bottom2(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->bottom_tabs_margin); + o->value((double)Fluid.proj.layout->bottom_tabs_margin); } else { - layout->bottom_tabs_margin = (int)o->value(); + Fluid.proj.layout->bottom_tabs_margin = (int)o->value(); } } static void cb_Minimum(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->widget_min_w); + o->value((double)Fluid.proj.layout->widget_min_w); } else { - layout->widget_min_w = (int)o->value(); + Fluid.proj.layout->widget_min_w = (int)o->value(); } } static void cb_Increment(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->widget_inc_w); + o->value((double)Fluid.proj.layout->widget_inc_w); } else { - layout->widget_inc_w = (int)o->value(); + Fluid.proj.layout->widget_inc_w = (int)o->value(); } } static void cb_Gap(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->widget_gap_x); + o->value((double)Fluid.proj.layout->widget_gap_x); } else { - layout->widget_gap_x = (int)o->value(); + Fluid.proj.layout->widget_gap_x = (int)o->value(); } } static void cb_3(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->widget_min_h); + o->value((double)Fluid.proj.layout->widget_min_h); } else { - layout->widget_min_h = (int)o->value(); + Fluid.proj.layout->widget_min_h = (int)o->value(); } } static void cb_4(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->widget_inc_h); + o->value((double)Fluid.proj.layout->widget_inc_h); } else { - layout->widget_inc_h = (int)o->value(); + Fluid.proj.layout->widget_inc_h = (int)o->value(); } } static void cb_5(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value((double)layout->widget_gap_y); + o->value((double)Fluid.proj.layout->widget_gap_y); } else { - layout->widget_gap_y = (int)o->value(); + Fluid.proj.layout->widget_gap_y = (int)o->value(); } } static void cb_6(Fl_Choice* o, void* v) { if (v == LOAD) { - o->value(layout->labelfont+1); + o->value(Fluid.proj.layout->labelfont+1); } else { - layout->labelfont = (int)o->value()-1; + Fluid.proj.layout->labelfont = (int)o->value()-1; } } static void cb_7(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value(layout->labelsize); + o->value(Fluid.proj.layout->labelsize); } else { - layout->labelsize = (int)o->value(); + Fluid.proj.layout->labelsize = (int)o->value(); } } static void cb_8(Fl_Choice* o, void* v) { if (v == LOAD) { - o->value(layout->textfont+1); + o->value(Fluid.proj.layout->textfont+1); } else { - layout->textfont = (int)o->value()-1; + Fluid.proj.layout->textfont = (int)o->value()-1; } } static void cb_9(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value(layout->textsize); + o->value(Fluid.proj.layout->textsize); } else { - layout->textsize = (int)o->value(); + Fluid.proj.layout->textsize = (int)o->value(); } } @@ -863,7 +867,7 @@ static const unsigned char idata_shell_64[] = 82,219,229,239,135,114,138,14,109,164,201,83,85,44,126,133,81,55,103,232,191,0, 145,21,211,195,226,88,204,195,0,0,0,0,73,69,78,68,174,66,96,130}; static Fl_Image *image_shell_64() { - static Fl_Image *image = NULL; + static Fl_Image *image = 0L; if (!image) image = new Fl_PNG_Image("shell_64.png", idata_shell_64, 802); return image; @@ -880,9 +884,9 @@ static void cb_w_settings_shell_list(Fl_Browser* o, void* v) { for (int i=0; ilist_size; i++) { Fd_Shell_Command *cmd = g_shell_config->list[i]; o->add(cmd->name.c_str()); - if (cmd->storage == FD_STORE_USER) + if (cmd->storage == fld::Tool_Store::USER) o->icon(i+1, w_settings_shell_fd_user->image()); - else if (cmd->storage == FD_STORE_PROJECT) + else if (cmd->storage == fld::Tool_Store::PROJECT) o->icon(i+1, w_settings_shell_fd_project->image()); } } @@ -915,11 +919,11 @@ static void cb_a(Fl_Button*, void* v) { w_settings_shell_list->insert(selected+1, cmd->name.c_str()); w_settings_shell_list->deselect(); w_settings_shell_list->value(selected+1); - if (cmd->storage == FD_STORE_USER) { + if (cmd->storage == fld::Tool_Store::USER) { w_settings_shell_list->icon(selected+1, w_settings_shell_fd_user->image()); - } else if (cmd->storage == FD_STORE_PROJECT) { + } else if (cmd->storage == fld::Tool_Store::PROJECT) { w_settings_shell_list->icon(selected+1, w_settings_shell_fd_project->image()); - set_modflag(1); + Fluid.proj.set_modflag(1); } w_settings_shell_list->do_callback(); w_settings_shell_cmd->do_callback(w_settings_shell_cmd, LOAD); @@ -945,11 +949,11 @@ static void cb_w_settings_shell_dup(Fl_Button* o, void* v) { w_settings_shell_list->insert(selected+1, cmd->name.c_str()); w_settings_shell_list->deselect(); w_settings_shell_list->value(selected+1); - if (cmd->storage == FD_STORE_USER) { + if (cmd->storage == fld::Tool_Store::USER) { w_settings_shell_list->icon(selected+1, w_settings_shell_fd_user->image()); - } else if (cmd->storage == FD_STORE_PROJECT) { + } else if (cmd->storage == fld::Tool_Store::PROJECT) { w_settings_shell_list->icon(selected+1, w_settings_shell_fd_project->image()); - set_modflag(1); + Fluid.proj.set_modflag(1); } w_settings_shell_list->do_callback(); w_settings_shell_cmd->do_callback(w_settings_shell_cmd, LOAD); @@ -971,9 +975,9 @@ static void cb_w_settings_shell_remove(Fl_Button* o, void* v) { } else { if (!selected) return; int ret = fl_choice("Delete the shell command\n\"%s\"?\n\nThis can not be undone.", - "Delete", "Cancel", NULL, g_shell_config->list[selected-1]->name.c_str()); + "Delete", "Cancel", nullptr, g_shell_config->list[selected-1]->name.c_str()); if (ret==1) return; - if (g_shell_config->at(selected-1)->storage == FD_STORE_PROJECT) set_modflag(1); + if (g_shell_config->at(selected-1)->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); g_shell_config->remove(selected-1); w_settings_shell_list->remove(selected); if (selected <= w_settings_shell_list->size()) @@ -1058,7 +1062,7 @@ static void cb_Name(Fl_Input* o, void* v) { Fd_Shell_Command *cmd = g_shell_config->list[selected-1]; cmd->name = o->value(); w_settings_shell_list->text(selected, o->value()); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } } } @@ -1076,7 +1080,7 @@ static void cb_Menu(Fl_Input* o, void* v) { Fd_Shell_Command *cmd = g_shell_config->list[selected-1]; cmd->label = o->value(); cmd->update_shell_menu(); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } } } @@ -1099,7 +1103,7 @@ static void cb_Shortcut(Fl_Shortcut_Button* o, void* v) { Fd_Shell_Command *cmd = g_shell_config->list[selected-1]; cmd->shortcut = o->value(); cmd->update_shell_menu(); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } } } @@ -1108,30 +1112,30 @@ static void cb_Store(Fl_Choice* o, void* v) { int selected = w_settings_shell_list_selected; if (v == LOAD) { if (selected) { - Fd_Tool_Store ts = g_shell_config->list[selected-1]->storage; + fld::Tool_Store ts = g_shell_config->list[selected-1]->storage; o->value(o->find_item_with_argument((long)ts)); } else { - o->value(o->find_item_with_argument((long)FD_STORE_USER)); + o->value(o->find_item_with_argument((long)fld::Tool_Store::USER)); } } else { if (selected) { Fd_Shell_Command *cmd = g_shell_config->list[selected-1]; - Fd_Tool_Store ts = (Fd_Tool_Store)(o->mvalue()->argument()); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + fld::Tool_Store ts = (fld::Tool_Store)(o->mvalue()->argument()); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); cmd->storage = ts; //w_settings_shell_list->text(selected, cmd->name.c_str()); - if (cmd->storage == FD_STORE_USER) + if (cmd->storage == fld::Tool_Store::USER) w_settings_shell_list->icon(selected, w_settings_shell_fd_user->image()); - else if (cmd->storage == FD_STORE_PROJECT) + else if (cmd->storage == fld::Tool_Store::PROJECT) w_settings_shell_list->icon(selected, w_settings_shell_fd_project->image()); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } } } Fl_Menu_Item menu_Store[] = { - {"@fd_user User Setting", 0, 0, (void*)(FD_STORE_USER), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {"@fd_project Project File", 0, 0, (void*)(FD_STORE_PROJECT), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {"@fd_user User Setting", 0, 0, (void*)(fld::Tool_Store::USER), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {"@fd_project Project File", 0, 0, (void*)(fld::Tool_Store::PROJECT), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, {0,0,0,0,0,0,0,0,0} }; @@ -1150,7 +1154,7 @@ static void cb_Condition(Fl_Choice* o, void* v) { int cond = (int)(o->mvalue()->argument()); cmd->condition = cond; g_shell_config->rebuild_shell_menu(); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } } } @@ -1187,7 +1191,7 @@ static void cb_w_settings_shell_command(Fl_Text_Editor* o, void* v) { if (selected) { Fd_Shell_Command *cmd = g_shell_config->list[selected-1]; cmd->command = o->buffer()->text(); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } } } @@ -1211,7 +1215,7 @@ static void cb_w_settings_shell_text_macros(Fl_Menu_Button* o, void*) { int pos = w_settings_shell_command->insert_position(); w_settings_shell_command->buffer()->insert(pos, word); } - w_settings_shell_command->do_callback(w_settings_shell_command, (void*)NULL); + w_settings_shell_command->do_callback(w_settings_shell_command, (void*)nullptr); } } @@ -1266,7 +1270,7 @@ static void cb_save(Fl_Check_Button* o, void* v) { } else { cmd->flags &= ~Fd_Shell_Command::SAVE_PROJECT; } - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } } } @@ -1288,7 +1292,7 @@ static void cb_save1(Fl_Check_Button* o, void* v) { } else { cmd->flags &= ~Fd_Shell_Command::SAVE_SOURCECODE; } - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } } } @@ -1310,7 +1314,7 @@ static void cb_save2(Fl_Check_Button* o, void* v) { } else { cmd->flags &= ~Fd_Shell_Command::SAVE_STRINGS; } - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } } } @@ -1332,7 +1336,7 @@ static void cb_show(Fl_Check_Button* o, void* v) { } else { cmd->flags &= ~Fd_Shell_Command::DONT_SHOW_TERMINAL; } - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } } } @@ -1354,7 +1358,7 @@ static void cb_clear(Fl_Check_Button* o, void* v) { } else { cmd->flags &= ~Fd_Shell_Command::CLEAR_TERMINAL; } - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } } } @@ -1376,7 +1380,7 @@ static void cb_clear1(Fl_Check_Button* o, void* v) { } else { cmd->flags &= ~Fd_Shell_Command::CLEAR_HISTORY; } - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } } } @@ -1664,7 +1668,7 @@ static const unsigned char idata_fd_project[] = 213,122,16,176,223,94,207,73,175,70,249,59,81,177,117,128,158,31,49,127,246,30, 207,181,170,20,0,0,0,0,73,69,78,68,174,66,96,130}; static Fl_Image *image_fd_project() { - static Fl_Image *image = NULL; + static Fl_Image *image = 0L; if (!image) image = new Fl_PNG_Image("fd_project.png", idata_fd_project, 6950); return image; @@ -2029,7 +2033,7 @@ static const unsigned char idata_fd_user[] = 116,89,108,93,41,107,255,5,119,155,194,247,64,241,254,70,0,0,0,0,73,69,78,68, 174,66,96,130}; static Fl_Image *image_fd_user() { - static Fl_Image *image = NULL; + static Fl_Image *image = 0L; if (!image) image = new Fl_PNG_Image("fd_user.png", idata_fd_user, 8612); return image; @@ -2108,7 +2112,7 @@ static const unsigned char idata_language_64[] = 157,33,48,255,61,163,226,212,114,146,206,17,166,130,48,126,136,23,88,161,222, 205,191,56,75,123,84,202,251,159,166,0,0,0,0,73,69,78,68,174,66,96,130}; static Fl_Image *image_language_64() { - static Fl_Image *image = NULL; + static Fl_Image *image = 0L; if (!image) image = new Fl_PNG_Image("language_64.png", idata_language_64, 1450); return image; @@ -2133,11 +2137,11 @@ Fl_Input *i18n_gnu_include_input=(Fl_Input *)0; static void cb_i18n_gnu_include_input(Fl_Input* o, void* v) { if (v == LOAD) { - o->value(g_project.i18n_gnu_include.c_str()); + o->value(Fluid.proj.i18n_gnu_include.c_str()); } else { - undo_checkpoint(); - g_project.i18n_gnu_include = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_gnu_include = o->value(); + Fluid.proj.set_modflag(1); } } @@ -2145,11 +2149,11 @@ Fl_Input *i18n_gnu_conditional_input=(Fl_Input *)0; static void cb_i18n_gnu_conditional_input(Fl_Input* o, void* v) { if (v == LOAD) { - o->value(g_project.i18n_gnu_conditional.c_str()); + o->value(Fluid.proj.i18n_gnu_conditional.c_str()); } else { - undo_checkpoint(); - g_project.i18n_gnu_conditional = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_gnu_conditional = o->value(); + Fluid.proj.set_modflag(1); } } @@ -2157,11 +2161,11 @@ Fl_Input *i18n_gnu_function_input=(Fl_Input *)0; static void cb_i18n_gnu_function_input(Fl_Input* o, void* v) { if (v == LOAD) { - o->value(g_project.i18n_gnu_function.c_str()); + o->value(Fluid.proj.i18n_gnu_function.c_str()); } else { - undo_checkpoint(); - g_project.i18n_gnu_function = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_gnu_function = o->value(); + Fluid.proj.set_modflag(1); } } @@ -2169,11 +2173,11 @@ Fl_Input *i18n_gnu_static_function_input=(Fl_Input *)0; static void cb_i18n_gnu_static_function_input(Fl_Input* o, void* v) { if (v == LOAD) { - o->value(g_project.i18n_gnu_static_function.c_str()); + o->value(Fluid.proj.i18n_gnu_static_function.c_str()); } else { - undo_checkpoint(); - g_project.i18n_gnu_static_function = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_gnu_static_function = o->value(); + Fluid.proj.set_modflag(1); } } @@ -2187,11 +2191,11 @@ Fl_Input *i18n_pos_include_input=(Fl_Input *)0; static void cb_i18n_pos_include_input(Fl_Input* o, void* v) { if (v == LOAD) { - o->value(g_project.i18n_pos_include.c_str()); + o->value(Fluid.proj.i18n_pos_include.c_str()); } else { - undo_checkpoint(); - g_project.i18n_pos_include = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_pos_include = o->value(); + Fluid.proj.set_modflag(1); } } @@ -2199,11 +2203,11 @@ Fl_Input *i18n_pos_conditional_input=(Fl_Input *)0; static void cb_i18n_pos_conditional_input(Fl_Input* o, void* v) { if (v == LOAD) { - o->value(g_project.i18n_pos_conditional.c_str()); + o->value(Fluid.proj.i18n_pos_conditional.c_str()); } else { - undo_checkpoint(); - g_project.i18n_pos_conditional = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_pos_conditional = o->value(); + Fluid.proj.set_modflag(1); } } @@ -2211,11 +2215,11 @@ Fl_Input *i18n_pos_file_input=(Fl_Input *)0; static void cb_i18n_pos_file_input(Fl_Input* o, void* v) { if (v == LOAD) { - o->value(g_project.i18n_pos_file.c_str()); + o->value(Fluid.proj.i18n_pos_file.c_str()); } else { - undo_checkpoint(); - g_project.i18n_pos_file = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_pos_file = o->value(); + Fluid.proj.set_modflag(1); } } @@ -2227,11 +2231,11 @@ Fl_Int_Input *i18n_pos_set_input=(Fl_Int_Input *)0; static void cb_i18n_pos_set_input(Fl_Int_Input* o, void* v) { if (v == LOAD) { - o->value(g_project.i18n_pos_set.c_str()); + o->value(Fluid.proj.i18n_pos_set.c_str()); } else { - undo_checkpoint(); - g_project.i18n_pos_set = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_pos_set = o->value(); + Fluid.proj.set_modflag(1); } } @@ -2421,7 +2425,7 @@ static const unsigned char idata_user_circle_64[] = 252,106,89,247,128,255,3,60,207,245,248,165,38,113,147,0,0,0,0,73,69,78,68,174, 66,96,130}; static Fl_Image *image_user_circle_64() { - static Fl_Image *image = NULL; + static Fl_Image *image = 0L; if (!image) image = new Fl_PNG_Image("user_circle_64.png", idata_user_circle_64, 3909); return image; @@ -2431,8 +2435,8 @@ Fl_Choice *w_settings_user_commenttext=(Fl_Choice *)0; static void cb_Close(Fl_Button*, void*) { if (g_shell_config) - g_shell_config->write(fluid_prefs, FD_STORE_USER); - g_layout_list.write(fluid_prefs, FD_STORE_USER); + g_shell_config->write(Fluid.preferences, fld::Tool_Store::USER); + Fluid.layout_list.write(Fluid.preferences, fld::Tool_Store::USER); settings_window->hide(); } @@ -2459,10 +2463,10 @@ Fl_Double_Window* make_settings_window() { scheme_choice->labelfont(1); scheme_choice->labelsize(11); scheme_choice->labelcolor(FL_FOREGROUND_COLOR); - scheme_choice->callback((Fl_Callback*)scheme_cb); + scheme_choice->callback((Fl_Callback*)cb_scheme_choice); scheme_choice->align(Fl_Align(FL_ALIGN_LEFT)); scheme_choice->when(FL_WHEN_RELEASE); - init_scheme(); + Fluid.init_scheme(); } // Fl_Scheme_Choice* scheme_choice { Fl_Box* o = new Fl_Box(240, 78, 10, 25); o->hide(); @@ -2480,7 +2484,7 @@ Fl_Double_Window* make_settings_window() { tooltips_button->labelsize(11); tooltips_button->callback((Fl_Callback*)cb_tooltips_button); int b; - fluid_prefs.get("show_tooltips", b, 1); + Fluid.preferences.get("show_tooltips", b, 1); tooltips_button->value(b); Fl_Tooltip::enable(b); } // Fl_Check_Button* tooltips_button @@ -2489,7 +2493,7 @@ Fl_Double_Window* make_settings_window() { completion_button->labelsize(11); completion_button->callback((Fl_Callback*)cb_completion_button); int b; - fluid_prefs.get("show_completion_dialogs", b, 1); + Fluid.preferences.get("show_completion_dialogs", b, 1); completion_button->value(b); } // Fl_Check_Button* completion_button { openlast_button = new Fl_Check_Button(120, 155, 200, 20, "Open Previous File on Startup"); @@ -2497,7 +2501,7 @@ Fl_Double_Window* make_settings_window() { openlast_button->labelsize(11); openlast_button->callback((Fl_Callback*)cb_openlast_button); int b; - fluid_prefs.get("open_previous_file", b, 0); + Fluid.preferences.get("open_previous_file", b, 0); openlast_button->value(b); } // Fl_Check_Button* openlast_button { prevpos_button = new Fl_Check_Button(120, 175, 200, 20, "Remember Window Positions"); @@ -2505,15 +2509,15 @@ Fl_Double_Window* make_settings_window() { prevpos_button->labelsize(11); prevpos_button->callback((Fl_Callback*)cb_prevpos_button); int b; - fluid_prefs.get("prev_window_pos", b, 1); + Fluid.preferences.get("prev_window_pos", b, 1); prevpos_button->value(b); } // Fl_Check_Button* prevpos_button { show_comments_button = new Fl_Check_Button(120, 195, 200, 20, "Show Comments in Browser"); show_comments_button->down_box(FL_DOWN_BOX); show_comments_button->labelsize(11); show_comments_button->callback((Fl_Callback*)cb_show_comments_button); - fluid_prefs.get("show_comments", show_comments, 1); - show_comments_button->value(show_comments); + Fluid.preferences.get("Fluid.show_comments", Fluid.show_comments, 1); + show_comments_button->value(Fluid.show_comments); } // Fl_Check_Button* show_comments_button { Fl_Group* o = new Fl_Group(120, 225, 50, 20); o->callback((Fl_Callback*)cb_1); @@ -2525,7 +2529,7 @@ Fl_Double_Window* make_settings_window() { recent_spinner->callback((Fl_Callback*)cb_recent_spinner); recent_spinner->when(FL_WHEN_CHANGED); int c; - fluid_prefs.get("recent_files", c, 5); + Fluid.preferences.get("recent_files", c, 5); recent_spinner->maximum(10); recent_spinner->value(c); } // Fl_Spinner* recent_spinner @@ -2539,8 +2543,8 @@ Fl_Double_Window* make_settings_window() { use_external_editor_button->down_box(FL_DOWN_BOX); use_external_editor_button->labelsize(11); use_external_editor_button->callback((Fl_Callback*)cb_use_external_editor_button); - fluid_prefs.get("use_external_editor", G_use_external_editor, 0); - use_external_editor_button->value(G_use_external_editor); + Fluid.preferences.get("use_external_editor", Fluid.use_external_editor, 0); + use_external_editor_button->value(Fluid.use_external_editor); } // Fl_Check_Button* use_external_editor_button { editor_command_input = new Fl_Input(120, 255, 200, 20, "External Editor:"); editor_command_input->tooltip("The editor command to open your external text editor.\nInclude any necessary " @@ -2552,8 +2556,8 @@ Fl_Double_Window* make_settings_window() { editor_command_input->textsize(11); editor_command_input->callback((Fl_Callback*)cb_editor_command_input); editor_command_input->when(FL_WHEN_CHANGED); - fluid_prefs.get("external_editor_command", G_external_editor_command, "", sizeof(G_external_editor_command)-1); - editor_command_input->value(G_external_editor_command); + Fluid.preferences.get("external_editor_command", Fluid.external_editor_command, "", sizeof(Fluid.external_editor_command)-1); + editor_command_input->value(Fluid.external_editor_command); } // Fl_Input* editor_command_input { Fl_Box* o = new Fl_Box(120, 300, 0, 20, "Overlays: "); o->labelfont(1); @@ -2565,7 +2569,7 @@ Fl_Double_Window* make_settings_window() { guides_button->down_box(FL_DOWN_BOX); guides_button->labelsize(11); guides_button->callback((Fl_Callback*)toggle_guides_cb); - o->value(show_guides); + o->value(Fluid.show_guides); } // Fl_Check_Button* guides_button { Fl_Check_Button* o = restricted_button = new Fl_Check_Button(120, 320, 200, 20, "Show Restricted Areas"); restricted_button->tooltip("show overlapping and out of bounds areas, show unfilled areas in Fl_Pack grou" @@ -2573,7 +2577,7 @@ Fl_Double_Window* make_settings_window() { restricted_button->down_box(FL_DOWN_BOX); restricted_button->labelsize(11); restricted_button->callback((Fl_Callback*)toggle_restricted_cb); - o->value(show_restricted); + o->value(Fluid.show_restricted); } // Fl_Check_Button* restricted_button { Fl_Check_Button* o = ghosted_outline_button = new Fl_Check_Button(120, 340, 200, 20, "Show Ghosted Group Outlines"); ghosted_outline_button->tooltip("groups with no box type or flat boxtypes without contrast will be rendered wi" @@ -2581,7 +2585,7 @@ Fl_Double_Window* make_settings_window() { ghosted_outline_button->down_box(FL_DOWN_BOX); ghosted_outline_button->labelsize(11); ghosted_outline_button->callback((Fl_Callback*)toggle_ghosted_outline_cb); - o->value(show_ghosted_outline); + o->value(Fluid.show_ghosted_outline); } // Fl_Check_Button* ghosted_outline_button { Fl_Box* o = new Fl_Box(120, 530, 200, 10); o->hide(); @@ -3517,7 +3521,7 @@ static void cb_Clear(Fl_Button*, void*) { Fl_Return_Button *shell_run_button=(Fl_Return_Button *)0; static void cb_shell_run_button(Fl_Return_Button*, void*) { - Fl_Preferences pos(fluid_prefs, "shell_run_Window_pos"); + Fl_Preferences pos(Fluid.preferences, "shell_run_Window_pos"); pos.set("x", shell_run_window->x()); pos.set("y", shell_run_window->y()); pos.set("w", shell_run_window->w()); diff --git a/fluid/panels/settings_panel.fl b/fluid/panels/settings_panel.fl index 7460ae8af..c66f48bd2 100644 --- a/fluid/panels/settings_panel.fl +++ b/fluid/panels/settings_panel.fl @@ -4,7 +4,7 @@ header_name {.h} code_name {.cxx} snap { ver 1 - current_suite {FLUID (based on FLTK)} + current_suite FLTK current_preset 0 suite { name {FLUID (based on FLTK)} @@ -49,16 +49,16 @@ comment {// } {in_source in_header } -decl {\#include "app/fluid.h"} {public global +decl {\#include "Fluid.h"} {public global } -decl {\#include "app/project.h"} {private local +decl {\#include "Project.h"} {private local } -decl {\#include "app/undo.h"} {private global +decl {\#include "proj/undo.h"} {private global } -decl {\#include "app/Fd_Snap_Action.h"} {public global +decl {\#include "app/Snap_Action.h"} {public global } decl {\#include "app/shell_command.h"} {public global @@ -97,11 +97,7 @@ decl {\#include "../src/flstring.h"} {private global decl {\#include } {private global } -decl {using namespace fld::widget;} {selected private global -} - -decl {void init_scheme(void);} { - comment {// initialize the scheme from preferences} public global +decl {using namespace fld::widget;} {private global } decl {extern struct Fl_Menu_Item *dbmanager_item;} {public local @@ -211,9 +207,9 @@ Function {make_settings_window()} {open } { Fl_Choice scheme_choice { label {Scheme: } - callback scheme_cb open + callback {Fluid.set_scheme(o->text(o->value()));} open selected xywh {120 78 120 25} down_box BORDER_BOX labelfont 1 labelsize 11 - code0 {init_scheme();} + code0 {Fluid.init_scheme();} class Fl_Scheme_Choice } {} Fl_Box {} { @@ -227,45 +223,45 @@ Function {make_settings_window()} {open Fl_Check_Button tooltips_button { label {Show Tooltips} callback {Fl_Tooltip::enable(tooltips_button->value()); -fluid_prefs.set("show_tooltips", tooltips_button->value());} +Fluid.preferences.set("show_tooltips", tooltips_button->value());} xywh {120 115 200 20} down_box DOWN_BOX labelsize 11 code0 {int b;} - code1 {fluid_prefs.get("show_tooltips", b, 1);} + code1 {Fluid.preferences.get("show_tooltips", b, 1);} code2 {tooltips_button->value(b);} code3 {Fl_Tooltip::enable(b);} } Fl_Check_Button completion_button { label {Show Completion Dialogs} - callback {fluid_prefs.set("show_completion_dialogs", completion_button->value());} + callback {Fluid.preferences.set("show_completion_dialogs", completion_button->value());} xywh {120 135 200 20} down_box DOWN_BOX labelsize 11 code0 {int b;} - code1 {fluid_prefs.get("show_completion_dialogs", b, 1);} + code1 {Fluid.preferences.get("show_completion_dialogs", b, 1);} code2 {completion_button->value(b);} } Fl_Check_Button openlast_button { label {Open Previous File on Startup} - callback {fluid_prefs.set("open_previous_file", openlast_button->value());} + callback {Fluid.preferences.set("open_previous_file", openlast_button->value());} xywh {120 155 200 20} down_box DOWN_BOX labelsize 11 code0 {int b;} - code1 {fluid_prefs.get("open_previous_file", b, 0);} + code1 {Fluid.preferences.get("open_previous_file", b, 0);} code2 {openlast_button->value(b);} } Fl_Check_Button prevpos_button { label {Remember Window Positions} - callback {fluid_prefs.set("prev_window_pos", prevpos_button->value());} + callback {Fluid.preferences.set("prev_window_pos", prevpos_button->value());} xywh {120 175 200 20} down_box DOWN_BOX labelsize 11 code0 {int b;} - code1 {fluid_prefs.get("prev_window_pos", b, 1);} + code1 {Fluid.preferences.get("prev_window_pos", b, 1);} code2 {prevpos_button->value(b);} } Fl_Check_Button show_comments_button { label {Show Comments in Browser} - callback {show_comments = show_comments_button->value(); -fluid_prefs.set("show_comments", show_comments); + callback {Fluid.show_comments = show_comments_button->value(); +Fluid.preferences.set("Fluid.show_comments", Fluid.show_comments); redraw_browser();} xywh {120 195 200 20} down_box DOWN_BOX labelsize 11 - code1 {fluid_prefs.get("show_comments", show_comments, 1);} - code2 {show_comments_button->value(show_comments);} + code1 {Fluid.preferences.get("Fluid.show_comments", Fluid.show_comments, 1);} + code2 {show_comments_button->value(Fluid.show_comments);} } Fl_Group {} { callback {propagate_load(o, v);} open @@ -273,11 +269,11 @@ redraw_browser();} } { Fl_Spinner recent_spinner { label {\# Recent Files:} - callback {fluid_prefs.set("recent_files", recent_spinner->value()); -load_history();} + callback {Fluid.preferences.set("recent_files", recent_spinner->value()); +Fluid.history.load();} xywh {120 225 40 20} labelfont 1 labelsize 11 when 1 maximum 10 textsize 11 code0 {int c;} - code1 {fluid_prefs.get("recent_files", c, 5);} + code1 {Fluid.preferences.get("recent_files", c, 5);} code2 {recent_spinner->maximum(10);} code3 {recent_spinner->value(c);} } @@ -287,18 +283,18 @@ load_history();} } Fl_Check_Button use_external_editor_button { label {Use for Code Nodes} - callback {G_use_external_editor = use_external_editor_button->value(); -fluid_prefs.set("use_external_editor", G_use_external_editor); + callback {Fluid.use_external_editor = use_external_editor_button->value(); +Fluid.preferences.set("use_external_editor", Fluid.use_external_editor); redraw_browser();} xywh {120 275 200 20} down_box DOWN_BOX labelsize 11 - code1 {fluid_prefs.get("use_external_editor", G_use_external_editor, 0);} - code2 {use_external_editor_button->value(G_use_external_editor);} + code1 {Fluid.preferences.get("use_external_editor", Fluid.use_external_editor, 0);} + code2 {use_external_editor_button->value(Fluid.use_external_editor);} } Fl_Input editor_command_input { label {External Editor:} - callback {strncpy(G_external_editor_command, editor_command_input->value(), sizeof(G_external_editor_command)-1); -G_external_editor_command[sizeof(G_external_editor_command)-1] = 0; -fluid_prefs.set("external_editor_command", G_external_editor_command); + callback {strncpy(Fluid.external_editor_command, editor_command_input->value(), sizeof(Fluid.external_editor_command)-1); +Fluid.external_editor_command[sizeof(Fluid.external_editor_command)-1] = 0; +Fluid.preferences.set("external_editor_command", Fluid.external_editor_command); redraw_browser();} tooltip {The editor command to open your external text editor. Include any necessary flags to ensure your editor does not background itself. @@ -306,8 +302,8 @@ Examples: gvim -f gedit emacs} xywh {120 255 200 20} labelfont 1 labelsize 11 when 1 textfont 4 textsize 11 - code1 {fluid_prefs.get("external_editor_command", G_external_editor_command, "", sizeof(G_external_editor_command)-1);} - code2 {editor_command_input->value(G_external_editor_command);} + code1 {Fluid.preferences.get("external_editor_command", Fluid.external_editor_command, "", sizeof(Fluid.external_editor_command)-1);} + code2 {editor_command_input->value(Fluid.external_editor_command);} } Fl_Box {} { label {Overlays: } @@ -317,19 +313,19 @@ Examples: label {Show Positioning Guides} callback toggle_guides_cb tooltip {show guides that help to position and resize widgets and enable snapping} xywh {120 300 200 20} down_box DOWN_BOX labelsize 11 - code0 {o->value(show_guides);} + code0 {o->value(Fluid.show_guides);} } Fl_Check_Button restricted_button { label {Show Restricted Areas} callback toggle_restricted_cb tooltip {show overlapping and out of bounds areas, show unfilled areas in Fl_Pack groups} xywh {120 320 200 20} down_box DOWN_BOX labelsize 11 - code0 {o->value(show_restricted);} + code0 {o->value(Fluid.show_restricted);} } Fl_Check_Button ghosted_outline_button { label {Show Ghosted Group Outlines} callback toggle_ghosted_outline_cb tooltip {groups with no box type or flat boxtypes without contrast will be rendered with a dim outline in the editing window only} xywh {120 340 200 20} down_box DOWN_BOX labelsize 11 - code0 {o->value(show_ghosted_outline);} + code0 {o->value(Fluid.show_ghosted_outline);} } Fl_Box {} { xywh {120 530 200 10} hide resizable @@ -356,11 +352,11 @@ or just ".ext" to set extension.} label {Header File:} user_data 1 user_data_type {void*} callback {if (v == LOAD) { - o->value(g_project.header_file_name.c_str()); + o->value(Fluid.proj.header_file_name.c_str()); } else { - if (strcmp(g_project.header_file_name.c_str(), o->value())) { - g_project.header_file_name = o->value(); - set_modflag(1); + if (strcmp(Fluid.proj.header_file_name.c_str(), o->value())) { + Fluid.proj.header_file_name = o->value(); + Fluid.proj.set_modflag(1); } }} tooltip {The name of the generated header file.} xywh {100 112 220 20} box THIN_DOWN_BOX labelfont 1 labelsize 11 when 1 textfont 4 textsize 11 @@ -369,11 +365,11 @@ or just ".ext" to set extension.} label {Code File:} user_data 1 user_data_type {void*} callback {if (v == LOAD) { - o->value(g_project.code_file_name.c_str()); + o->value(Fluid.proj.code_file_name.c_str()); } else { - if (strcmp(g_project.code_file_name.c_str(), o->value())) { - g_project.code_file_name = o->value(); - set_modflag(1); + if (strcmp(Fluid.proj.code_file_name.c_str(), o->value())) { + Fluid.proj.code_file_name = o->value(); + Fluid.proj.set_modflag(1); } }} tooltip {The name of the generated code file.} xywh {100 137 220 20} box THIN_DOWN_BOX labelfont 1 labelsize 11 when 1 textfont 4 textsize 11 @@ -381,11 +377,11 @@ or just ".ext" to set extension.} Fl_Check_Button include_H_from_C_button { label {Include Header from Code} callback {if (v == LOAD) { - o->value(g_project.include_H_from_C); + o->value(Fluid.proj.include_H_from_C); } else { - if (g_project.include_H_from_C != o->value()) { - set_modflag(1); - g_project.include_H_from_C = o->value(); + if (Fluid.proj.include_H_from_C != o->value()) { + Fluid.proj.set_modflag(1); + Fluid.proj.include_H_from_C = o->value(); } }} tooltip {Include the header file from the code file.} xywh {100 162 220 20} down_box DOWN_BOX labelsize 11 @@ -397,11 +393,11 @@ or just ".ext" to set extension.} Fl_Check_Button use_FL_COMMAND_button { label {Menu shortcuts use FL_COMMAND} callback {if (v == LOAD) { - o->value(g_project.use_FL_COMMAND); + o->value(Fluid.proj.use_FL_COMMAND); } else { - if (g_project.use_FL_COMMAND != o->value()) { - set_modflag(1); - g_project.use_FL_COMMAND = o->value(); + if (Fluid.proj.use_FL_COMMAND != o->value()) { + Fluid.proj.set_modflag(1); + Fluid.proj.use_FL_COMMAND = o->value(); } }} tooltip {Replace FL_CTRL and FL_META with FL_COMMAND when generating menu shortcuts} xywh {100 205 220 20} down_box DOWN_BOX labelsize 11 @@ -409,11 +405,11 @@ or just ".ext" to set extension.} Fl_Check_Button utf8_in_src_button { label {allow Unicode UTF-8 in source code} callback {if (v == LOAD) { - o->value(g_project.utf8_in_src); + o->value(Fluid.proj.utf8_in_src); } else { - if (g_project.utf8_in_src != o->value()) { - set_modflag(1); - g_project.utf8_in_src = o->value(); + if (Fluid.proj.utf8_in_src != o->value()) { + Fluid.proj.set_modflag(1); + Fluid.proj.utf8_in_src = o->value(); } }} tooltip {For older compilers, characters outside of the printable ASCII range are escaped using octal notation `\\0123`. If this option is checked, Fluid will write UTF-8 characters unchanged.} xywh {100 230 220 20} down_box DOWN_BOX labelsize 11 @@ -421,11 +417,11 @@ or just ".ext" to set extension.} Fl_Check_Button avoid_early_includes_button { label {avoid early include of Fl.H} callback {if (v == LOAD) { - o->value(g_project.avoid_early_includes); + o->value(Fluid.proj.avoid_early_includes); } else { - if (g_project.avoid_early_includes != o->value()) { - set_modflag(1); - g_project.avoid_early_includes = o->value(); + if (Fluid.proj.avoid_early_includes != o->value()) { + Fluid.proj.set_modflag(1); + Fluid.proj.avoid_early_includes = o->value(); } }} tooltip {Do not emit \#include until it is needed by another include file.} xywh {100 255 220 20} down_box DOWN_BOX labelsize 11 @@ -437,11 +433,11 @@ or just ".ext" to set extension.} Fl_Check_Button w_proj_mergeback { label {generate MergeBack data} callback {if (v == LOAD) { - o->value(g_project.write_mergeback_data); + o->value(Fluid.proj.write_mergeback_data); } else { - if (g_project.write_mergeback_data != o->value()) { - set_modflag(1); - g_project.write_mergeback_data = o->value(); + if (Fluid.proj.write_mergeback_data != o->value()) { + Fluid.proj.set_modflag(1); + Fluid.proj.write_mergeback_data = o->value(); } }} comment {// Matt: disabled} @@ -462,11 +458,11 @@ or just ".ext" to set extension.} } Fl_Choice layout_choice { callback {if (v == LOAD) { - o->value(g_layout_list.current_suite()); + o->value(Fluid.layout_list.current_suite()); } else { int index = o->value(); - g_layout_list.current_suite(index); - g_layout_list.update_dialogs(); + Fluid.layout_list.current_suite(index); + Fluid.layout_list.update_dialogs(); }} xywh {85 78 187 24} down_box BORDER_BOX } { @@ -486,19 +482,19 @@ or just ".ext" to set extension.} if (v == LOAD) return; std::string old_name = "Copy of "; -old_name.append(g_layout_list[g_layout_list.current_suite()].name_); +old_name.append(Fluid.layout_list[Fluid.layout_list.current_suite()].name_); const char *new_name = fl_input("Enter a name for the new layout:", old_name.c_str()); -if (new_name == NULL) +if (new_name == nullptr) return; -g_layout_list.add(new_name); -g_layout_list.update_dialogs();} +Fluid.layout_list.add(new_name); +Fluid.layout_list.update_dialogs();} xywh {272 78 24 24} } Fl_Menu_Button w_layout_menu { callback {if (v == LOAD) { - Fd_Layout_Suite &suite = g_layout_list[g_layout_list.current_suite()]; - if (suite.storage_ == FD_STORE_INTERNAL) { + fld::app::Layout_Suite &suite = Fluid.layout_list[Fluid.layout_list.current_suite()]; + if (suite.storage_ == fld::Tool_Store::INTERNAL) { w_layout_menu_rename->deactivate(); for (int i=1; i<4; i++) w_layout_menu_storage[i]->deactivate(); w_layout_menu_delete->deactivate(); @@ -507,7 +503,7 @@ g_layout_list.update_dialogs();} for (int i=1; i<4; i++) w_layout_menu_storage[i]->activate(); w_layout_menu_delete->activate(); } - w_layout_menu_storage[suite.storage_]->setonly(); + w_layout_menu_storage[static_cast(suite.storage_)]->setonly(); }} open xywh {296 78 24 24} } { @@ -515,41 +511,41 @@ g_layout_list.update_dialogs();} label {Rename...} callback {// Rename the current layout suite -std::string old_name = g_layout_list[g_layout_list.current_suite()].name_; +std::string old_name = Fluid.layout_list[Fluid.layout_list.current_suite()].name_; const char *new_name = fl_input("Enter a new name for the layout:", old_name.c_str()); -if (new_name == NULL) +if (new_name == nullptr) return; -g_layout_list.rename(new_name); -g_layout_list.update_dialogs();} +Fluid.layout_list.rename(new_name); +Fluid.layout_list.update_dialogs();} xywh {0 0 31 20} divider } MenuItem {w_layout_menu_storage[0]} { label {@fd_beaker FLUID Built-In} - callback {Fd_Layout_Suite &suite = g_layout_list[g_layout_list.current_suite()]; -suite.storage(FD_STORE_INTERNAL); -g_layout_list.update_dialogs();} + callback {fld::app::Layout_Suite &suite = Fluid.layout_list[Fluid.layout_list.current_suite()]; +suite.storage(fld::Tool_Store::INTERNAL); +Fluid.layout_list.update_dialogs();} xywh {0 0 31 20} type Radio deactivate } MenuItem {w_layout_menu_storage[1]} { label {@fd_user User Preference} - callback {Fd_Layout_Suite &suite = g_layout_list[g_layout_list.current_suite()]; -suite.storage(FD_STORE_USER); -g_layout_list.update_dialogs();} + callback {fld::app::Layout_Suite &suite = Fluid.layout_list[Fluid.layout_list.current_suite()]; +suite.storage(fld::Tool_Store::USER); +Fluid.layout_list.update_dialogs();} xywh {0 0 31 20} type Radio } MenuItem {w_layout_menu_storage[2]} { label {@fd_project Store in .fl Project File} - callback {Fd_Layout_Suite &suite = g_layout_list[g_layout_list.current_suite()]; -suite.storage(FD_STORE_PROJECT); -g_layout_list.update_dialogs();} + callback {fld::app::Layout_Suite &suite = Fluid.layout_list[Fluid.layout_list.current_suite()]; +suite.storage(fld::Tool_Store::PROJECT); +Fluid.layout_list.update_dialogs();} xywh {0 0 31 20} type Radio } MenuItem {w_layout_menu_storage[3]} { label {@fd_file Store in External File} - callback {Fd_Layout_Suite &suite = g_layout_list[g_layout_list.current_suite()]; -suite.storage(FD_STORE_FILE); -g_layout_list.update_dialogs();} + callback {fld::app::Layout_Suite &suite = Fluid.layout_list[Fluid.layout_list.current_suite()]; +suite.storage(fld::Tool_Store::FILE); +Fluid.layout_list.update_dialogs();} xywh {0 0 31 20} type Radio divider } MenuItem w_layout_menu_load { @@ -563,9 +559,9 @@ fnfc.filter("FLUID Layouts\\t*.fll\\n"); if (fnfc.show() != 0) return; const char *new_filename = fnfc.filename(); if (!new_filename) return; -g_layout_list.load(new_filename); -//g_layout_list.current_suite(n); -g_layout_list.update_dialogs();} +Fluid.layout_list.load(new_filename); +//Fluid.layout_list.current_suite(n); +Fluid.layout_list.update_dialogs();} xywh {0 0 31 20} } MenuItem w_layout_menu_save { @@ -576,14 +572,14 @@ g_layout_list.update_dialogs();} fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); fnfc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT); fnfc.filter("FLUID Layouts\\t*.fll\\n"); - std::string filename = g_layout_list.filename_; + std::string filename = Fluid.layout_list.filename_; fnfc.directory(fl_filename_path_str(filename).c_str()); fnfc.preset_file(fl_filename_name_str(filename).c_str()); if (fnfc.show() != 0) return; const char *new_filename = fnfc.filename(); if (!new_filename) return; - g_layout_list.filename_ = new_filename; - g_layout_list.save(new_filename);} + Fluid.layout_list.filename_ = new_filename; + Fluid.layout_list.save(new_filename);} xywh {0 0 31 20} divider code0 {\#include } } @@ -591,8 +587,8 @@ g_layout_list.update_dialogs();} label Delete callback {// remove the current suite -g_layout_list.remove(g_layout_list.current_suite()); -g_layout_list.update_dialogs();} +Fluid.layout_list.remove(Fluid.layout_list.current_suite()); +Fluid.layout_list.update_dialogs();} xywh {0 0 31 20} } } @@ -634,36 +630,36 @@ g_layout_list.update_dialogs();} Fl_Value_Input {} { label {Left:} callback {if (v == LOAD) { - o->value((double)layout->left_window_margin); + o->value((double)Fluid.proj.layout->left_window_margin); } else { - layout->left_window_margin = (int)o->value(); + Fluid.proj.layout->left_window_margin = (int)o->value(); }} xywh {85 167 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { label {Top:} callback {if (v == LOAD) { - o->value((double)layout->top_window_margin); + o->value((double)Fluid.proj.layout->top_window_margin); } else { - layout->top_window_margin = (int)o->value(); + Fluid.proj.layout->top_window_margin = (int)o->value(); }} xywh {145 167 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { label {Right:} callback {if (v == LOAD) { - o->value((double)layout->right_window_margin); + o->value((double)Fluid.proj.layout->right_window_margin); } else { - layout->right_window_margin = (int)o->value(); + Fluid.proj.layout->right_window_margin = (int)o->value(); }} xywh {205 167 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { label {Bottom:} callback {if (v == LOAD) { - o->value((double)layout->bottom_window_margin); + o->value((double)Fluid.proj.layout->bottom_window_margin); } else { - layout->bottom_window_margin = (int)o->value(); + Fluid.proj.layout->bottom_window_margin = (int)o->value(); }} xywh {265 167 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } @@ -674,18 +670,18 @@ g_layout_list.update_dialogs();} Fl_Value_Input {} { label {Horizontal:} callback {if (v == LOAD) { - o->value((double)layout->window_grid_x); + o->value((double)Fluid.proj.layout->window_grid_x); } else { - layout->window_grid_x = (int)o->value(); + Fluid.proj.layout->window_grid_x = (int)o->value(); }} xywh {85 201 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { label {Vertical:} callback {if (v == LOAD) { - o->value((double)layout->window_grid_y); + o->value((double)Fluid.proj.layout->window_grid_y); } else { - layout->window_grid_y = (int)o->value(); + Fluid.proj.layout->window_grid_y = (int)o->value(); }} xywh {145 201 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } @@ -700,36 +696,36 @@ g_layout_list.update_dialogs();} Fl_Value_Input {} { label {Left:} callback {if (v == LOAD) { - o->value((double)layout->left_group_margin); + o->value((double)Fluid.proj.layout->left_group_margin); } else { - layout->left_group_margin = (int)o->value(); + Fluid.proj.layout->left_group_margin = (int)o->value(); }} xywh {85 261 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { label {Top:} callback {if (v == LOAD) { - o->value((double)layout->top_group_margin); + o->value((double)Fluid.proj.layout->top_group_margin); } else { - layout->top_group_margin = (int)o->value(); + Fluid.proj.layout->top_group_margin = (int)o->value(); }} xywh {145 261 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { label {Right:} callback {if (v == LOAD) { - o->value((double)layout->right_group_margin); + o->value((double)Fluid.proj.layout->right_group_margin); } else { - layout->right_group_margin = (int)o->value(); + Fluid.proj.layout->right_group_margin = (int)o->value(); }} xywh {205 261 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { label {Bottom:} callback {if (v == LOAD) { - o->value((double)layout->bottom_group_margin); + o->value((double)Fluid.proj.layout->bottom_group_margin); } else { - layout->bottom_group_margin = (int)o->value(); + Fluid.proj.layout->bottom_group_margin = (int)o->value(); }} xywh {265 261 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } @@ -740,18 +736,18 @@ g_layout_list.update_dialogs();} Fl_Value_Input {} { label {Horizontal:} callback {if (v == LOAD) { - o->value((double)layout->group_grid_x); + o->value((double)Fluid.proj.layout->group_grid_x); } else { - layout->group_grid_x = (int)o->value(); + Fluid.proj.layout->group_grid_x = (int)o->value(); }} xywh {85 295 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { label {Vertical:} callback {if (v == LOAD) { - o->value((double)layout->group_grid_y); + o->value((double)Fluid.proj.layout->group_grid_y); } else { - layout->group_grid_y = (int)o->value(); + Fluid.proj.layout->group_grid_y = (int)o->value(); }} xywh {145 295 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } @@ -766,18 +762,18 @@ g_layout_list.update_dialogs();} Fl_Value_Input {} { label {Top:} callback {if (v == LOAD) { - o->value((double)layout->top_tabs_margin); + o->value((double)Fluid.proj.layout->top_tabs_margin); } else { - layout->top_tabs_margin = (int)o->value(); + Fluid.proj.layout->top_tabs_margin = (int)o->value(); }} xywh {85 355 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { label {Bottom:} callback {if (v == LOAD) { - o->value((double)layout->bottom_tabs_margin); + o->value((double)Fluid.proj.layout->bottom_tabs_margin); } else { - layout->bottom_tabs_margin = (int)o->value(); + Fluid.proj.layout->bottom_tabs_margin = (int)o->value(); }} xywh {145 355 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } @@ -792,27 +788,27 @@ g_layout_list.update_dialogs();} Fl_Value_Input {} { label {Minimum:} callback {if (v == LOAD) { - o->value((double)layout->widget_min_w); + o->value((double)Fluid.proj.layout->widget_min_w); } else { - layout->widget_min_w = (int)o->value(); + Fluid.proj.layout->widget_min_w = (int)o->value(); }} xywh {85 414 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { label {Increment:} callback {if (v == LOAD) { - o->value((double)layout->widget_inc_w); + o->value((double)Fluid.proj.layout->widget_inc_w); } else { - layout->widget_inc_w = (int)o->value(); + Fluid.proj.layout->widget_inc_w = (int)o->value(); }} xywh {145 414 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { label {Gap:} callback {if (v == LOAD) { - o->value((double)layout->widget_gap_x); + o->value((double)Fluid.proj.layout->widget_gap_x); } else { - layout->widget_gap_x = (int)o->value(); + Fluid.proj.layout->widget_gap_x = (int)o->value(); }} xywh {205 414 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } @@ -822,25 +818,25 @@ g_layout_list.update_dialogs();} } Fl_Value_Input {} { callback {if (v == LOAD) { - o->value((double)layout->widget_min_h); + o->value((double)Fluid.proj.layout->widget_min_h); } else { - layout->widget_min_h = (int)o->value(); + Fluid.proj.layout->widget_min_h = (int)o->value(); }} xywh {85 440 55 20} labelsize 11 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { callback {if (v == LOAD) { - o->value((double)layout->widget_inc_h); + o->value((double)Fluid.proj.layout->widget_inc_h); } else { - layout->widget_inc_h = (int)o->value(); + Fluid.proj.layout->widget_inc_h = (int)o->value(); }} xywh {145 440 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } Fl_Value_Input {} { callback {if (v == LOAD) { - o->value((double)layout->widget_gap_y); + o->value((double)Fluid.proj.layout->widget_gap_y); } else { - layout->widget_gap_y = (int)o->value(); + Fluid.proj.layout->widget_gap_y = (int)o->value(); }} xywh {205 440 55 20} labelsize 11 align 5 maximum 32767 step 1 textsize 11 } @@ -851,9 +847,9 @@ g_layout_list.update_dialogs();} } { Fl_Choice {} { callback {if (v == LOAD) { - o->value(layout->labelfont+1); + o->value(Fluid.proj.layout->labelfont+1); } else { - layout->labelfont = (int)o->value()-1; + Fluid.proj.layout->labelfont = (int)o->value()-1; }} open tooltip {The style of the label text.} xywh {85 465 150 20} box THIN_UP_BOX down_box BORDER_BOX labelfont 1 labelsize 11 textsize 11 resizable code0 {extern Fl_Menu_Item fontmenu_w_default[];} @@ -861,9 +857,9 @@ g_layout_list.update_dialogs();} } {} Fl_Value_Input {} { callback {if (v == LOAD) { - o->value(layout->labelsize); + o->value(Fluid.proj.layout->labelsize); } else { - layout->labelsize = (int)o->value(); + Fluid.proj.layout->labelsize = (int)o->value(); }} tooltip {The size of the label text.} xywh {235 465 50 20} labelsize 11 minimum 1 maximum 1000 step 1 value 14 textsize 11 } @@ -875,9 +871,9 @@ g_layout_list.update_dialogs();} } { Fl_Choice {} { callback {if (v == LOAD) { - o->value(layout->textfont+1); + o->value(Fluid.proj.layout->textfont+1); } else { - layout->textfont = (int)o->value()-1; + Fluid.proj.layout->textfont = (int)o->value()-1; }} open tooltip {The value text style.} xywh {85 490 150 20} box DOWN_BOX down_box BORDER_BOX labelfont 1 labelsize 11 textsize 11 code0 {extern Fl_Menu_Item fontmenu_w_default[];} @@ -885,9 +881,9 @@ g_layout_list.update_dialogs();} } {} Fl_Value_Input {} { callback {if (v == LOAD) { - o->value(layout->textsize); + o->value(Fluid.proj.layout->textsize); } else { - layout->textsize = (int)o->value(); + Fluid.proj.layout->textsize = (int)o->value(); }} tooltip {The value text size.} xywh {235 490 50 20} labelsize 11 maximum 1000 step 1 value 14 textsize 11 } @@ -917,9 +913,9 @@ list:} for (int i=0; ilist_size; i++) { Fd_Shell_Command *cmd = g_shell_config->list[i]; o->add(cmd->name.c_str()); - if (cmd->storage == FD_STORE_USER) + if (cmd->storage == fld::Tool_Store::USER) o->icon(i+1, w_settings_shell_fd_user->image()); - else if (cmd->storage == FD_STORE_PROJECT) + else if (cmd->storage == fld::Tool_Store::PROJECT) o->icon(i+1, w_settings_shell_fd_project->image()); } } @@ -951,11 +947,11 @@ list:} w_settings_shell_list->insert(selected+1, cmd->name.c_str()); w_settings_shell_list->deselect(); w_settings_shell_list->value(selected+1); - if (cmd->storage == FD_STORE_USER) { + if (cmd->storage == fld::Tool_Store::USER) { w_settings_shell_list->icon(selected+1, w_settings_shell_fd_user->image()); - } else if (cmd->storage == FD_STORE_PROJECT) { + } else if (cmd->storage == fld::Tool_Store::PROJECT) { w_settings_shell_list->icon(selected+1, w_settings_shell_fd_project->image()); - set_modflag(1); + Fluid.proj.set_modflag(1); } w_settings_shell_list->do_callback(); w_settings_shell_cmd->do_callback(w_settings_shell_cmd, LOAD); @@ -980,11 +976,11 @@ if (v==LOAD) { w_settings_shell_list->insert(selected+1, cmd->name.c_str()); w_settings_shell_list->deselect(); w_settings_shell_list->value(selected+1); - if (cmd->storage == FD_STORE_USER) { + if (cmd->storage == fld::Tool_Store::USER) { w_settings_shell_list->icon(selected+1, w_settings_shell_fd_user->image()); - } else if (cmd->storage == FD_STORE_PROJECT) { + } else if (cmd->storage == fld::Tool_Store::PROJECT) { w_settings_shell_list->icon(selected+1, w_settings_shell_fd_project->image()); - set_modflag(1); + Fluid.proj.set_modflag(1); } w_settings_shell_list->do_callback(); w_settings_shell_cmd->do_callback(w_settings_shell_cmd, LOAD); @@ -1005,9 +1001,9 @@ if (v==LOAD) { } else { if (!selected) return; int ret = fl_choice("Delete the shell command\\n\\"%s\\"?\\n\\nThis can not be undone.", - "Delete", "Cancel", NULL, g_shell_config->list[selected-1]->name.c_str()); + "Delete", "Cancel", nullptr, g_shell_config->list[selected-1]->name.c_str()); if (ret==1) return; - if (g_shell_config->at(selected-1)->storage == FD_STORE_PROJECT) set_modflag(1); + if (g_shell_config->at(selected-1)->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); g_shell_config->remove(selected-1); w_settings_shell_list->remove(selected); if (selected <= w_settings_shell_list->size()) @@ -1109,7 +1105,7 @@ if (v == LOAD) { Fd_Shell_Command *cmd = g_shell_config->list[selected-1]; cmd->name = o->value(); w_settings_shell_list->text(selected, o->value()); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } }} tooltip {file the shell command under this name in the shell command list} xywh {100 246 220 20} labelfont 1 labelsize 11 when 13 textfont 4 textsize 11 @@ -1128,7 +1124,7 @@ if (v == LOAD) { Fd_Shell_Command *cmd = g_shell_config->list[selected-1]; cmd->label = o->value(); cmd->update_shell_menu(); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } }} tooltip {label text for the Shell menu in the main menu bar} xywh {100 272 220 20} labelfont 1 labelsize 11 textfont 4 textsize 11 @@ -1152,7 +1148,7 @@ if (v == LOAD) { Fd_Shell_Command *cmd = g_shell_config->list[selected-1]; cmd->shortcut = o->value(); cmd->update_shell_menu(); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } }} tooltip {an optional keyboard shortcut to run this shell command} xywh {100 297 130 20} labelsize 11 align 16 @@ -1164,35 +1160,35 @@ if (v == LOAD) { callback {int selected = w_settings_shell_list_selected; if (v == LOAD) { if (selected) { - Fd_Tool_Store ts = g_shell_config->list[selected-1]->storage; + fld::Tool_Store ts = g_shell_config->list[selected-1]->storage; o->value(o->find_item_with_argument((long)ts)); } else { - o->value(o->find_item_with_argument((long)FD_STORE_USER)); + o->value(o->find_item_with_argument((long)fld::Tool_Store::USER)); } } else { if (selected) { Fd_Shell_Command *cmd = g_shell_config->list[selected-1]; - Fd_Tool_Store ts = (Fd_Tool_Store)(o->mvalue()->argument()); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + fld::Tool_Store ts = (fld::Tool_Store)(o->mvalue()->argument()); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); cmd->storage = ts; //w_settings_shell_list->text(selected, cmd->name.c_str()); - if (cmd->storage == FD_STORE_USER) + if (cmd->storage == fld::Tool_Store::USER) w_settings_shell_list->icon(selected, w_settings_shell_fd_user->image()); - else if (cmd->storage == FD_STORE_PROJECT) + else if (cmd->storage == fld::Tool_Store::PROJECT) w_settings_shell_list->icon(selected, w_settings_shell_fd_project->image()); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } }} open tooltip {store this shell command as a user setting or save it with the .fl project file} xywh {100 322 130 20} down_box BORDER_BOX labelfont 1 labelsize 11 textsize 11 } { MenuItem {} { label {@fd_user User Setting} - user_data FD_STORE_USER user_data_type long + user_data {fld::Tool_Store::USER} user_data_type long xywh {0 0 100 20} labelsize 11 } MenuItem {} { label {@fd_project Project File} - user_data FD_STORE_PROJECT user_data_type long + user_data {fld::Tool_Store::PROJECT} user_data_type long xywh {0 0 100 20} labelsize 11 } } @@ -1212,7 +1208,7 @@ if (v == LOAD) { int cond = (int)(o->mvalue()->argument()); cmd->condition = cond; g_shell_config->rebuild_shell_menu(); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } }} open tooltip {add this command to the main menu bar only if this condition is true} xywh {100 348 130 20} down_box BORDER_BOX labelfont 1 labelsize 11 textsize 11 @@ -1278,7 +1274,7 @@ if (v == LOAD) { if (selected) { Fd_Shell_Command *cmd = g_shell_config->list[selected-1]; cmd->command = o->buffer()->text(); - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } }} xywh {100 373 196 80} labelfont 1 labelsize 11 align 4 textfont 4 textsize 12 resizable @@ -1304,7 +1300,7 @@ if (mi) { int pos = w_settings_shell_command->insert_position(); w_settings_shell_command->buffer()->insert(pos, word); } - w_settings_shell_command->do_callback(w_settings_shell_command, (void*)NULL); + w_settings_shell_command->do_callback(w_settings_shell_command, (void*)nullptr); }} open tooltip {a list of text replacements available for the shell script} xywh {296 373 24 22} labelsize 11 textsize 11 } { @@ -1396,7 +1392,7 @@ if (v == LOAD) { } else { cmd->flags &= ~Fd_Shell_Command::SAVE_PROJECT; } - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } }} tooltip {save the project to the .fl file before running the command} xywh {100 458 110 20} down_box DOWN_BOX labelsize 11 @@ -1419,7 +1415,7 @@ if (v == LOAD) { } else { cmd->flags &= ~Fd_Shell_Command::SAVE_SOURCECODE; } - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } }} tooltip {generate the source code and header file before running the command} xywh {100 478 110 19} down_box DOWN_BOX labelsize 11 @@ -1442,7 +1438,7 @@ if (v == LOAD) { } else { cmd->flags &= ~Fd_Shell_Command::SAVE_STRINGS; } - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } }} tooltip {save the internationalisation strings before running the command} xywh {100 498 110 20} down_box DOWN_BOX labelsize 11 @@ -1465,7 +1461,7 @@ if (v == LOAD) { } else { cmd->flags &= ~Fd_Shell_Command::DONT_SHOW_TERMINAL; } - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } }} tooltip {show the terminal window when launching this script} xywh {214 458 106 20} down_box DOWN_BOX labelsize 11 @@ -1488,7 +1484,7 @@ if (v == LOAD) { } else { cmd->flags &= ~Fd_Shell_Command::CLEAR_TERMINAL; } - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } }} tooltip {clear the teminal window before running this script} xywh {214 478 106 19} down_box DOWN_BOX labelsize 11 @@ -1511,7 +1507,7 @@ if (v == LOAD) { } else { cmd->flags &= ~Fd_Shell_Command::CLEAR_HISTORY; } - if (cmd->storage == FD_STORE_PROJECT) set_modflag(1); + if (cmd->storage == fld::Tool_Store::PROJECT) Fluid.proj.set_modflag(1); } }} tooltip {clear the teminal history in the terminal window} xywh {214 498 106 19} down_box DOWN_BOX labelsize 11 @@ -1564,44 +1560,44 @@ if (v == LOAD) { Fl_Input i18n_gnu_include_input { label {\#include:} callback {if (v == LOAD) { - o->value(g_project.i18n_gnu_include.c_str()); + o->value(Fluid.proj.i18n_gnu_include.c_str()); } else { - undo_checkpoint(); - g_project.i18n_gnu_include = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_gnu_include = o->value(); + Fluid.proj.set_modflag(1); }} tooltip {The include file for internationalization.} xywh {100 103 220 20} box THIN_DOWN_BOX labelsize 11 textfont 4 textsize 11 } Fl_Input i18n_gnu_conditional_input { label {Conditional:} callback {if (v == LOAD) { - o->value(g_project.i18n_gnu_conditional.c_str()); + o->value(Fluid.proj.i18n_gnu_conditional.c_str()); } else { - undo_checkpoint(); - g_project.i18n_gnu_conditional = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_gnu_conditional = o->value(); + Fluid.proj.set_modflag(1); }} tooltip {only include the header file if this preprocessor macro is defined, for example FLTK_GETTEXT_FOUND} xywh {100 128 220 20} box THIN_DOWN_BOX labelsize 11 textfont 4 textsize 11 } Fl_Input i18n_gnu_function_input { label {Function:} callback {if (v == LOAD) { - o->value(g_project.i18n_gnu_function.c_str()); + o->value(Fluid.proj.i18n_gnu_function.c_str()); } else { - undo_checkpoint(); - g_project.i18n_gnu_function = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_gnu_function = o->value(); + Fluid.proj.set_modflag(1); }} tooltip {The function to call to translate labels and tooltips, usually "gettext" or "_"} xywh {100 153 220 20} box THIN_DOWN_BOX labelsize 11 textfont 4 textsize 11 } Fl_Input i18n_gnu_static_function_input { label {Static Function:} callback {if (v == LOAD) { - o->value(g_project.i18n_gnu_static_function.c_str()); + o->value(Fluid.proj.i18n_gnu_static_function.c_str()); } else { - undo_checkpoint(); - g_project.i18n_gnu_static_function = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_gnu_static_function = o->value(); + Fluid.proj.set_modflag(1); }} tooltip {function to call to translate static text, The function to call to internationalize labels and tooltips, usually "gettext_noop" or "N_"} xywh {100 178 220 20} box THIN_DOWN_BOX labelsize 11 textfont 4 textsize 11 } @@ -1613,33 +1609,33 @@ if (v == LOAD) { Fl_Input i18n_pos_include_input { label {\#include:} callback {if (v == LOAD) { - o->value(g_project.i18n_pos_include.c_str()); + o->value(Fluid.proj.i18n_pos_include.c_str()); } else { - undo_checkpoint(); - g_project.i18n_pos_include = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_pos_include = o->value(); + Fluid.proj.set_modflag(1); }} tooltip {The include file for internationalization.} xywh {100 103 220 20} box THIN_DOWN_BOX labelsize 11 textfont 4 textsize 11 } Fl_Input i18n_pos_conditional_input { label {Conditional:} callback {if (v == LOAD) { - o->value(g_project.i18n_pos_conditional.c_str()); + o->value(Fluid.proj.i18n_pos_conditional.c_str()); } else { - undo_checkpoint(); - g_project.i18n_pos_conditional = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_pos_conditional = o->value(); + Fluid.proj.set_modflag(1); }} tooltip {only include the header file if this preprocessor macro is defined, for example FLTK_GETTEXT_FOUND} xywh {100 128 220 20} box THIN_DOWN_BOX labelsize 11 textfont 4 textsize 11 } Fl_Input i18n_pos_file_input { label {Catalog:} callback {if (v == LOAD) { - o->value(g_project.i18n_pos_file.c_str()); + o->value(Fluid.proj.i18n_pos_file.c_str()); } else { - undo_checkpoint(); - g_project.i18n_pos_file = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_pos_file = o->value(); + Fluid.proj.set_modflag(1); }} tooltip {The name of the message catalog.} xywh {100 153 220 20} box THIN_DOWN_BOX labelsize 11 textfont 4 textsize 11 } @@ -1650,11 +1646,11 @@ if (v == LOAD) { Fl_Input i18n_pos_set_input { label {Set:} callback {if (v == LOAD) { - o->value(g_project.i18n_pos_set.c_str()); + o->value(Fluid.proj.i18n_pos_set.c_str()); } else { - undo_checkpoint(); - g_project.i18n_pos_set = o->value(); - set_modflag(1); + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_pos_set = o->value(); + Fluid.proj.set_modflag(1); }} tooltip {The message set number.} xywh {100 178 80 20} type Int box THIN_DOWN_BOX labelsize 11 textfont 4 textsize 11 } @@ -1835,8 +1831,8 @@ if (v == LOAD) { Fl_Button {} { label Close callback {if (g_shell_config) - g_shell_config->write(fluid_prefs, FD_STORE_USER); -g_layout_list.write(fluid_prefs, FD_STORE_USER); + g_shell_config->write(Fluid.preferences, fld::Tool_Store::USER); +Fluid.layout_list.write(Fluid.preferences, fld::Tool_Store::USER); settings_window->hide();} tooltip {Close this dialog.} xywh {230 550 100 20} labelsize 11 } @@ -1873,7 +1869,7 @@ shell_run_terminal->append("\\033[2J\\033[3J\\033[H");} } Fl_Return_Button shell_run_button { label Close - callback {Fl_Preferences pos(fluid_prefs, "shell_run_Window_pos"); + callback {Fl_Preferences pos(Fluid.preferences, "shell_run_Window_pos"); pos.set("x", shell_run_window->x()); pos.set("y", shell_run_window->y()); pos.set("w", shell_run_window->w()); diff --git a/fluid/panels/settings_panel.h b/fluid/panels/settings_panel.h index 8aea3a221..c864d23b5 100644 --- a/fluid/panels/settings_panel.h +++ b/fluid/panels/settings_panel.h @@ -19,8 +19,8 @@ #ifndef settings_panel_h #define settings_panel_h #include -#include "app/fluid.h" -#include "app/Fd_Snap_Action.h" +#include "Fluid.h" +#include "app/Snap_Action.h" #include "app/shell_command.h" #include "tools/filename.h" #include "widgets/Node_Browser.h" @@ -28,10 +28,6 @@ #include #include #include -/** - // initialize the scheme from preferences -*/ -void init_scheme(void); extern struct Fl_Menu_Item *dbmanager_item; extern void i18n_cb(Fl_Choice *,void *); extern void scheme_cb(Fl_Scheme_Choice *, void *); @@ -52,7 +48,6 @@ extern Fl_Double_Window *settings_window; #include extern Fl_Tabs *w_settings_tabs; extern Fl_Group *w_settings_general_tab; -extern void scheme_cb(Fl_Scheme_Choice*, void*); extern Fl_Scheme_Choice *scheme_choice; #include extern Fl_Check_Button *tooltips_button; diff --git a/fluid/panels/template_panel.cxx b/fluid/panels/template_panel.cxx index 11227a549..7c2a9ac2b 100644 --- a/fluid/panels/template_panel.cxx +++ b/fluid/panels/template_panel.cxx @@ -17,7 +17,7 @@ // generated by Fast Light User Interface Designer (fluid) version 1.0500 #include "template_panel.h" -#include "app/fluid.h" +#include "Fluid.h" #include "tools/filename.h" #include #include @@ -84,7 +84,7 @@ static void cb_template_browser(Fl_Browser*, void*) { char pngfile[1024], *ext; strlcpy(pngfile, flfile, sizeof(pngfile)); - if ((ext = strrchr(pngfile, '.')) == NULL) return; + if ((ext = strrchr(pngfile, '.')) == nullptr) return; strcpy(ext, ".png"); img = Fl_Shared_Image::get(pngfile); @@ -183,7 +183,7 @@ void template_clear() { void *filename; for (i = 1; i <= template_browser->size(); i ++) { - if ((filename = template_browser->data(i)) != NULL) free(filename); + if ((filename = template_browser->data(i)) != nullptr) free(filename); } template_browser->deselect(); @@ -208,7 +208,7 @@ void template_delete_cb(Fl_Button *, void *) { char pngfile[1024], *ext; strlcpy(pngfile, flfile, sizeof(pngfile)); - if ((ext = strrchr(pngfile, '.')) != NULL) { + if ((ext = strrchr(pngfile, '.')) != nullptr) { strcpy(ext, ".png"); fl_unlink(pngfile); } @@ -284,12 +284,12 @@ void template_load() { struct dirent **files; int num_files; - fluid_prefs.getUserdataPath(path, sizeof(path)); + Fluid.preferences.getUserdataPath(path, sizeof(path)); strlcat(path, "templates", sizeof(path)); fl_make_path(path); int sample_templates_generated = 0; - fluid_prefs.get("sample_templates_generated", sample_templates_generated, 0); + Fluid.preferences.get("sample_templates_generated", sample_templates_generated, 0); if (sample_templates_generated < 2) { strcpy(filename, path); @@ -311,8 +311,8 @@ void template_load() { template_install(path, "/FLTK_License.fl", tmpl_FLTK_License_fl, sizeof(tmpl_FLTK_License_fl), tmpl_FLTK_License_fl_size); template_install(path, "/1of7GUIs.fl", tmpl_1of7GUIs_fl, sizeof(tmpl_1of7GUIs_fl), tmpl_1of7GUIs_fl_size); sample_templates_generated = 2; - fluid_prefs.set("sample_templates_generated", sample_templates_generated); - fluid_prefs.flush(); + Fluid.preferences.set("sample_templates_generated", sample_templates_generated); + Fluid.preferences.flush(); } num_files = fl_filename_list(path, &files); diff --git a/fluid/panels/template_panel.fl b/fluid/panels/template_panel.fl index b0ba20968..be0376c2c 100644 --- a/fluid/panels/template_panel.fl +++ b/fluid/panels/template_panel.fl @@ -20,7 +20,7 @@ comment {// } {in_source in_header } -decl {\#include "app/fluid.h"} {private local +decl {\#include "Fluid.h"} {private local } decl {\#include "tools/filename.h"} {selected private local @@ -111,7 +111,7 @@ template_delete->activate(); char pngfile[1024], *ext; strlcpy(pngfile, flfile, sizeof(pngfile)); -if ((ext = strrchr(pngfile, '.')) == NULL) return; +if ((ext = strrchr(pngfile, '.')) == nullptr) return; strcpy(ext, ".png"); img = Fl_Shared_Image::get(pngfile); @@ -179,7 +179,7 @@ Function {template_clear()} {return_type void void *filename; for (i = 1; i <= template_browser->size(); i ++) { - if ((filename = template_browser->data(i)) != NULL) free(filename); + if ((filename = template_browser->data(i)) != nullptr) free(filename); } template_browser->deselect(); @@ -205,7 +205,7 @@ if (fl_unlink(flfile)) { char pngfile[1024], *ext; strlcpy(pngfile, flfile, sizeof(pngfile)); -if ((ext = strrchr(pngfile, '.')) != NULL) { +if ((ext = strrchr(pngfile, '.')) != nullptr) { strcpy(ext, ".png"); fl_unlink(pngfile); } @@ -241,12 +241,12 @@ char name[1024], filename[1400], path[1024], *ptr; struct dirent **files; int num_files; -fluid_prefs.getUserdataPath(path, sizeof(path)); +Fluid.preferences.getUserdataPath(path, sizeof(path)); strlcat(path, "templates", sizeof(path)); fl_make_path(path); int sample_templates_generated = 0; -fluid_prefs.get("sample_templates_generated", sample_templates_generated, 0); +Fluid.preferences.get("sample_templates_generated", sample_templates_generated, 0); if (sample_templates_generated < 2) { strcpy(filename, path); @@ -268,8 +268,8 @@ if (sample_templates_generated < 2) { template_install(path, "/FLTK_License.fl", tmpl_FLTK_License_fl, sizeof(tmpl_FLTK_License_fl), tmpl_FLTK_License_fl_size); template_install(path, "/1of7GUIs.fl", tmpl_1of7GUIs_fl, sizeof(tmpl_1of7GUIs_fl), tmpl_1of7GUIs_fl_size); sample_templates_generated = 2; - fluid_prefs.set("sample_templates_generated", sample_templates_generated); - fluid_prefs.flush(); + Fluid.preferences.set("sample_templates_generated", sample_templates_generated); + Fluid.preferences.flush(); } num_files = fl_filename_list(path, &files); diff --git a/fluid/panels/widget_panel.cxx b/fluid/panels/widget_panel.cxx index df903d4a2..61b480f36 100644 --- a/fluid/panels/widget_panel.cxx +++ b/fluid/panels/widget_panel.cxx @@ -17,11 +17,32 @@ // generated by Fast Light User Interface Designer (fluid) version 1.0500 #include "widget_panel.h" -#include "app/undo.h" -#include "nodes/Fl_Widget_Type.h" -#include "nodes/Fl_Grid_Type.h" +#include "Fluid.h" +#include "app/Snap_Action.h" +#include "app/Image_Asset.h" +#include "proj/undo.h" +#include "nodes/Window_Node.h" +#include "nodes/Grid_Node.h" +#include #include -extern void set_modflag(int mf, int mfc=-1); +#include +#include +#include +#define ZERO_ENTRY 1000 +extern const char* when_symbol_name(int n); +extern void set_whenmenu(int n); +extern void redraw_browser(); +const char *c_check(const char *c, int type=0); +extern Fl_Color fl_show_colormap(Fl_Color oldcol); +extern void labelcolor_common(Fl_Color c); +extern void color_common(Fl_Color c); +extern void color2_common(Fl_Color c); +extern void textcolor_common(Fl_Color c); +extern int widget_i; +extern fld::widget::Formula_Input_Vars widget_vars[]; +extern int numselected; +extern Fl_Menu_Item boxmenu[]; +extern int haderror; Fl_Double_Window *image_panel_window=(Fl_Double_Window *)0; @@ -56,14 +77,14 @@ fld::widget::Formula_Input *image_panel_imagew=(fld::widget::Formula_Input *)0; static void cb_image_panel_imagew(fld::widget::Formula_Input* o, void* v) { if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { o->value(current_widget->scale_image_w_); } } else { int mod = 0; - for (Fl_Type *t = Fl_Type::first; t; t = t->next) { + for (Node *t = Fluid.proj.tree.first; t; t = t->next) { if (t->selected && t->is_widget()) { - Fl_Widget_Type* wt = ((Fl_Widget_Type*)t); + Widget_Node* wt = ((Widget_Node*)t); wt->scale_image_w_ = o->value(); Fl_Image *img = wt->o->image(); if (img) { @@ -78,7 +99,7 @@ static void cb_image_panel_imagew(fld::widget::Formula_Input* o, void* v) { mod = 1; } } - if (mod) set_modflag(1); + if (mod) Fluid.proj.set_modflag(1); } } @@ -86,14 +107,14 @@ fld::widget::Formula_Input *image_panel_imageh=(fld::widget::Formula_Input *)0; static void cb_image_panel_imageh(fld::widget::Formula_Input* o, void* v) { if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { o->value(current_widget->scale_image_h_); } } else { int mod = 0; - for (Fl_Type *t = Fl_Type::first; t; t = t->next) { + for (Node *t = Fluid.proj.tree.first; t; t = t->next) { if (t->selected && t->is_widget()) { - Fl_Widget_Type* wt = ((Fl_Widget_Type*)t); + Widget_Node* wt = ((Widget_Node*)t); wt->scale_image_h_ = o->value(); Fl_Image *img = wt->o->image(); if (img) { @@ -108,7 +129,7 @@ static void cb_image_panel_imageh(fld::widget::Formula_Input* o, void* v) { mod = 1; } } - if (mod) set_modflag(1); + if (mod) Fluid.proj.set_modflag(1); } } @@ -121,6 +142,42 @@ static void cb_Reset(Fl_Button*, void* v) { } } +static void cb_convert(Fl_Check_Button* o, void* v) { + if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { + o->activate(); + o->value(!current_widget->compress_image_); + } else { + o->deactivate(); + } + } else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->compress_image_ = !o->value(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_bind(Fl_Check_Button* o, void* v) { + if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { + o->activate(); + o->value(current_widget->bind_image_); + } else { + o->deactivate(); + } + } else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->bind_image_ = o->value(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +} + Fl_Group *image_panel_deimagegroup=(Fl_Group *)0; Fl_Box *image_panel_dedata=(Fl_Box *)0; @@ -148,14 +205,14 @@ fld::widget::Formula_Input *image_panel_deimagew=(fld::widget::Formula_Input *)0 static void cb_image_panel_deimagew(fld::widget::Formula_Input* o, void* v) { if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { o->value(current_widget->scale_deimage_w_); } } else { int mod = 0; - for (Fl_Type *t = Fl_Type::first; t; t = t->next) { + for (Node *t = Fluid.proj.tree.first; t; t = t->next) { if (t->selected && t->is_widget()) { - Fl_Widget_Type* wt = ((Fl_Widget_Type*)t); + Widget_Node* wt = ((Widget_Node*)t); wt->scale_deimage_w_ = o->value(); Fl_Image *img = wt->o->deimage(); if (img) { @@ -170,7 +227,7 @@ static void cb_image_panel_deimagew(fld::widget::Formula_Input* o, void* v) { mod = 1; } } - if (mod) set_modflag(1); + if (mod) Fluid.proj.set_modflag(1); } } @@ -178,14 +235,14 @@ fld::widget::Formula_Input *image_panel_deimageh=(fld::widget::Formula_Input *)0 static void cb_image_panel_deimageh(fld::widget::Formula_Input* o, void* v) { if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { o->value(current_widget->scale_deimage_h_); } } else { int mod = 0; - for (Fl_Type *t = Fl_Type::first; t; t = t->next) { + for (Node *t = Fluid.proj.tree.first; t; t = t->next) { if (t->selected && t->is_widget()) { - Fl_Widget_Type* wt = ((Fl_Widget_Type*)t); + Widget_Node* wt = ((Widget_Node*)t); wt->scale_deimage_h_ = o->value(); Fl_Image *img = wt->o->deimage(); if (img) { @@ -200,7 +257,7 @@ static void cb_image_panel_deimageh(fld::widget::Formula_Input* o, void* v) { mod = 1; } } - if (mod) set_modflag(1); + if (mod) Fluid.proj.set_modflag(1); } } @@ -213,6 +270,42 @@ static void cb_Reset1(Fl_Button*, void* v) { } } +static void cb_convert1(Fl_Check_Button* o, void* v) { + if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { + o->activate(); + o->value(!current_widget->compress_deimage_); + } else { + o->deactivate(); + } + } else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->compress_deimage_ = !o->value(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_bind1(Fl_Check_Button* o, void* v) { + if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { + o->activate(); + o->value(current_widget->bind_deimage_); + } else { + o->deactivate(); + } + } else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->bind_deimage_ = o->value(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +} + Fl_Button *image_panel_close=(Fl_Button *)0; static void cb_image_panel_close(Fl_Button*, void* v) { @@ -291,13 +384,13 @@ Fl_Double_Window* make_image_panel() { "ixel data"); o->down_box(FL_DOWN_BOX); o->labelsize(11); - o->callback((Fl_Callback*)compress_image_cb); + o->callback((Fl_Callback*)cb_convert); } // Fl_Check_Button* o { Fl_Check_Button* o = new Fl_Check_Button(75, 120, 170, 20, "bind to widget"); o->tooltip("bind the image to the widget, so it will be deleted automatically"); o->down_box(FL_DOWN_BOX); o->labelsize(11); - o->callback((Fl_Callback*)bind_image_cb); + o->callback((Fl_Callback*)cb_bind); o->window()->hotspot(o); } // Fl_Check_Button* o image_panel_imagegroup->end(); @@ -367,13 +460,13 @@ Fl_Double_Window* make_image_panel() { "ixel data"); o->down_box(FL_DOWN_BOX); o->labelsize(11); - o->callback((Fl_Callback*)compress_deimage_cb); + o->callback((Fl_Callback*)cb_convert1); } // Fl_Check_Button* o { Fl_Check_Button* o = new Fl_Check_Button(75, 260, 170, 20, "bind to widget"); o->tooltip("bind the image to the widget, so it will be deleted automatically"); o->down_box(FL_DOWN_BOX); o->labelsize(11); - o->callback((Fl_Callback*)bind_deimage_cb); + o->callback((Fl_Callback*)cb_bind1); } // Fl_Check_Button* o image_panel_deimagegroup->end(); } // Fl_Group* image_panel_deimagegroup @@ -405,7 +498,29 @@ void run_image_panel() { Fl_Shared_Image *img = (Fl_Shared_Image*)image_panel_data->user_data(); if (img) { img->release(); - image_panel_data->user_data(NULL); + image_panel_data->user_data(nullptr); + } +} + +void flex_margin_cb(Fl_Value_Input* i, void* v, void (*load_margin)(Fl_Flex*,Fl_Value_Input*), int (*update_margin)(Fl_Flex*,int)) { + if (v == LOAD) { + if (current_widget->is_a(Type::Flex)) { + load_margin((Fl_Flex*)current_widget->o, i); + } + } else { + int mod = 0; + int new_value = (int)i->value(); + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_a(Type::Flex)) { + Flex_Node* q = (Flex_Node*)o; + Fl_Flex* w = (Fl_Flex*)q->o; + if (update_margin(w, new_value)) { + w->layout(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); } } @@ -421,6 +536,42 @@ Fl_Input *wp_gui_label=(Fl_Input *)0; Fl_Input *widget_image_input=(Fl_Input *)0; +static void cb_widget_image_input(Fl_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { + o->activate(); + o->value(((Widget_Node*)current_widget)->image_name()); + } else o->deactivate(); + } else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->image_name(o->value()); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_Browse(Fl_Button* o, void* v) { + if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) + o->activate(); + else + o->deactivate(); + } else { + int mod = 0; + Image_Asset *image_asset = ui_find_image(widget_image_input->value()); + if (image_asset) { + widget_image_input->value(image_asset->filename()); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->image_name(image_asset->filename()); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } + } +} + static void cb_(Fl_Button*, void* v) { if (v != LOAD) { run_image_panel(); @@ -429,6 +580,42 @@ static void cb_(Fl_Button*, void* v) { Fl_Input *widget_deimage_input=(Fl_Input *)0; +static void cb_widget_deimage_input(Fl_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { + o->activate(); + o->value(((Widget_Node*)current_widget)->inactive_name()); + } else o->deactivate(); + } else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->inactive_name(o->value()); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_Browse1(Fl_Button* o, void* v) { + if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) + o->activate(); + else + o->deactivate(); + } else { + int mod = 0; + Image_Asset *image_asset = ui_find_image(widget_deimage_input->value()); + if (image_asset) { + widget_deimage_input->value(image_asset->filename()); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->inactive_name(image_asset->filename()); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } + } +} + Fl_Group *wp_gui_alignment=(Fl_Group *)0; Fl_Menu_Item menu_[] = { @@ -462,12 +649,151 @@ Fl_Menu_Item menu_1[] = { fld::widget::Formula_Input *widget_x_input=(fld::widget::Formula_Input *)0; +static void cb_widget_x_input(fld::widget::Formula_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_true_widget()) { + o->value(((Widget_Node *)current_widget)->o->x()); + o->activate(); + } else o->deactivate(); + } else { + Fluid.proj.undo.checkpoint(); + widget_i = 0; + int mod = 0; + int v = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + Fl_Widget *w = q->o; + o->variables(widget_vars, q); + v = o->value(); + w->resize(v, w->y(), w->w(), w->h()); + if (w->window()) w->window()->redraw(); + widget_i++; + mod = 1; + } + } + if (mod) { + Fluid.proj.set_modflag(1); + o->value(v); // change the displayed value to the result of the last + // calculation. Keep the formula if it was not used. + } + } +} + fld::widget::Formula_Input *widget_y_input=(fld::widget::Formula_Input *)0; +static void cb_widget_y_input(fld::widget::Formula_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_true_widget()) { + o->value(((Widget_Node *)current_widget)->o->y()); + o->activate(); + } else o->deactivate(); + } else { + Fluid.proj.undo.checkpoint(); + widget_i = 0; + int mod = 0; + int v = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + Fl_Widget *w = q->o; + o->variables(widget_vars, q); + v = o->value(); + w->resize(w->x(), v, w->w(), w->h()); + if (w->window()) w->window()->redraw(); + widget_i++; + mod = 1; + } + } + if (mod) { + Fluid.proj.set_modflag(1); + o->value(v); + } + } +} + fld::widget::Formula_Input *widget_w_input=(fld::widget::Formula_Input *)0; +static void cb_widget_w_input(fld::widget::Formula_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_true_widget()) { + o->value(((Widget_Node *)current_widget)->o->w()); + o->activate(); + } else o->deactivate(); + } else { + Fluid.proj.undo.checkpoint(); + widget_i = 0; + int mod = 0; + int v = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + Fl_Widget *w = q->o; + o->variables(widget_vars, q); + v = o->value(); + w->resize(w->x(), w->y(), v, w->h()); + if (w->window()) w->window()->redraw(); + widget_i++; + mod = 1; + } + } + if (mod) { + Fluid.proj.set_modflag(1); + o->value(v); + } + } +} + fld::widget::Formula_Input *widget_h_input=(fld::widget::Formula_Input *)0; +static void cb_widget_h_input(fld::widget::Formula_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_true_widget()) { + o->value(((Widget_Node *)current_widget)->o->h()); + o->activate(); + } else o->deactivate(); + } else { + Fluid.proj.undo.checkpoint(); + widget_i = 0; + int mod = 0; + int v = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + Fl_Widget *w = q->o; + o->variables(widget_vars, q); + v = o->value(); + w->resize(w->x(), w->y(), w->w(), v); + if (w->window()) w->window()->redraw(); + widget_i++; + mod = 1; + } + } + if (mod) { + Fluid.proj.set_modflag(1); + o->value(v); + } + } +} + +static void cb_Children(Fl_Choice* o, void* v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Widget_Class)) { + o->show(); + o->value(((Widget_Class_Node *)current_widget)->wc_relative); + } else { + o->hide(); + } + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Widget_Class)) { + Widget_Class_Node *t = (Widget_Class_Node *)q; + t->wc_relative = o->value(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + Fl_Menu_Item menu_Children[] = { {"Fixed", 0, 0, 0, 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, {"Reposition", 0, 0, 0, 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, @@ -477,517 +803,1419 @@ Fl_Menu_Item menu_Children[] = { Fl_Group *wp_gui_flexp=(Fl_Group *)0; -Fl_Value_Input *widget_flex_size=(Fl_Value_Input *)0; - -Fl_Check_Button *widget_flex_fixed=(Fl_Check_Button *)0; - -Fl_Group *wp_gui_values=(Fl_Group *)0; - -Fl_Group *wp_gui_margins=(Fl_Group *)0; - -Fl_Group *wp_gui_sizerange=(Fl_Group *)0; - -Fl_Shortcut_Button *wp_gui_shortcut=(Fl_Shortcut_Button *)0; - -Fl_Group *wp_gui_xclass=(Fl_Group *)0; - -Fl_Group *wp_gui_attributes=(Fl_Group *)0; - -Fl_Input *wp_gui_tooltip=(Fl_Input *)0; - -Fl_Group *wp_style_tab=(Fl_Group *)0; - -Fl_Group *wp_style_label=(Fl_Group *)0; - -Fl_Button *w_labelcolor=(Fl_Button *)0; - -Fl_Group *wp_style_box=(Fl_Group *)0; - -Fl_Button *w_color=(Fl_Button *)0; - -Fl_Group *wp_style_downbox=(Fl_Group *)0; - -Fl_Button *w_selectcolor=(Fl_Button *)0; - -Fl_Group *wp_style_text=(Fl_Group *)0; - -Fl_Button *w_textcolor=(Fl_Button *)0; - -Fl_Group *wp_cpp_tab=(Fl_Group *)0; - -Fl_Group *wp_cpp_class=(Fl_Group *)0; - -Fl_Group *wp_cpp_name=(Fl_Group *)0; - -Fl_Menu_Item menu_2[] = { - {"private", 0, 0, (void*)(0), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {"public", 0, 0, (void*)(1), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {"protected", 0, 0, (void*)(2), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {0,0,0,0,0,0,0,0,0} -}; - -Fl_Menu_Item menu_3[] = { - {"local", 0, 0, (void*)(0), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {"global", 0, 0, (void*)(1), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {0,0,0,0,0,0,0,0,0} -}; - -Fl_Input *v_input[4]={(Fl_Input *)0}; - -static void cb_1(Fl_Tile*, void* v) { - wComment->do_callback(wComment, v); - wCallback->do_callback(wCallback, v); +static void cb_wp_gui_flexp(Fl_Group* o, void* v) { + if (v == LOAD) { + if (Flex_Node::parent_is_flex(current_widget)) { + o->show(); + propagate_load(o, v); + } else { + o->hide(); + } + } } -Fl_Text_Editor *wComment=(Fl_Text_Editor *)0; - -fld::widget::Code_Editor *wCallback=(fld::widget::Code_Editor *)0; - -Fl_Group *wp_cpp_callback=(Fl_Group *)0; - -Fl_Menu_Item menu_4[] = { - {"void*", 0, 0, 0, 0, (uchar)FL_NORMAL_LABEL, 4, 11, 0}, - {"long", 0, 0, 0, 0, (uchar)FL_NORMAL_LABEL, 4, 11, 0}, - {0,0,0,0,0,0,0,0,0} -}; - -Fl_Box *w_when_box=(Fl_Box *)0; - -Fl_Group *widget_tab_grid_child=(Fl_Group *)0; - -fld::widget::Formula_Input *widget_grid_row_input=(fld::widget::Formula_Input *)0; - -fld::widget::Formula_Input *widget_grid_col_input=(fld::widget::Formula_Input *)0; - -Fl_Box *widget_grid_transient=(Fl_Box *)0; +Fl_Value_Input *widget_flex_size=(Fl_Value_Input *)0; -static void cb_widget_grid_transient(Fl_Box* o, void* v) { - if (v==LOAD) { - Fl_Widget *child = ((Fl_Widget_Type*)current_widget)->o; - Fl_Grid_Proxy *g = ((Fl_Grid_Proxy*)((Fl_Widget_Type*)current_widget->parent)->o); - // Fl_Grid::Cell *cell = g->cell(child); - // Fl_Grid::Cell *tcell = g->transient_cell(child); - widget_grid_transient->hide(); - widget_grid_unlinked->hide(); - if (g->transient_cell(child)) { - widget_grid_transient->show(); - } else if (!g->cell(child)) { - widget_grid_unlinked->show(); +static void cb_widget_flex_size(Fl_Value_Input* o, void* v) { + if (v == LOAD) { + if (Flex_Node::parent_is_flex(current_widget)) { + o->value(Flex_Node::size(current_widget)); + } + } else { + int mod = 0; + int new_size = (int)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (Flex_Node::parent_is_flex(q)) { + Fl_Widget* w = (Fl_Widget*)q->o; + Fl_Flex* f = (Fl_Flex*)((Flex_Node*)q->parent)->o; + int was_fixed = f->fixed(w); + if (new_size==0) { + if (was_fixed) { + f->fixed(w, 0); + f->layout(); + widget_flex_fixed->value(0); + mod = 1; + } + } else { + int old_size = Flex_Node::size(q); + if (old_size!=new_size || !was_fixed) { + f->fixed(w, new_size); + f->layout(); + widget_flex_fixed->value(1); + mod = 1; + } + } + } } + if (mod) Fluid.proj.set_modflag(1); } } -Fl_Box *widget_grid_unlinked=(Fl_Box *)0; - -Fl_Group *wp_gridc_align=(Fl_Group *)0; - -Fl_Menu_Item menu_Horizontal[] = { - {"GRID_LEFT", 0, 0, (void*)((fl_intptr_t)FL_GRID_LEFT), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {"GRID_CENTER", 0, 0, (void*)((fl_intptr_t)FL_GRID_CENTER), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {"GRID_RIGHT", 0, 0, (void*)((fl_intptr_t)FL_GRID_RIGHT), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {"GRID_FILL", 0, 0, (void*)((fl_intptr_t)FL_GRID_HORIZONTAL), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {0,0,0,0,0,0,0,0,0} -}; - -Fl_Menu_Item menu_Vertical[] = { - {"GRID_TOP", 0, 0, (void*)((fl_intptr_t)FL_GRID_TOP), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {"GRID_CENTER", 0, 0, (void*)((fl_intptr_t)FL_GRID_CENTER), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {"GRID_BOTTOM", 0, 0, (void*)((fl_intptr_t)FL_GRID_BOTTOM), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {"GRID_FILL", 0, 0, (void*)((fl_intptr_t)FL_GRID_VERTICAL), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, - {0,0,0,0,0,0,0,0,0} -}; - -Fl_Group *wp_gridc_size=(Fl_Group *)0; - -fld::widget::Formula_Input *widget_grid_rowspan_input=(fld::widget::Formula_Input *)0; - -fld::widget::Formula_Input *widget_grid_colspan_input=(fld::widget::Formula_Input *)0; - -Fl_Group *widget_tab_grid=(Fl_Group *)0; - -fld::widget::Formula_Input *widget_grid_rows=(fld::widget::Formula_Input *)0; +Fl_Check_Button *widget_flex_fixed=(Fl_Check_Button *)0; -static void cb_widget_grid_rows(fld::widget::Formula_Input* o, void* v) { - // grid_rows_cb - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; +static void cb_widget_flex_fixed(Fl_Check_Button* o, void* v) { if (v == LOAD) { - o->value(grid->rows()); - } else { - int m = o->value(), old_m = grid->rows(); - if (m < 1) { - m = 1; - o->value(m); - } - if (m < old_m) { - // TODO: verify that this will not unlink existings cells - // Offer a dialog with "delete children", "unlink cells", "cancel" + if (Flex_Node::parent_is_flex(current_widget)) { + o->value(Flex_Node::is_fixed(current_widget)); } - if (m != old_m) { - undo_checkpoint(); - grid->layout(m, grid->cols()); - grid->need_layout(true); - set_modflag(1); - widget_tab_grid->do_callback(widget_tab_grid, LOAD); + } else { + int mod = 0; + int new_fixed = (int)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (Flex_Node::parent_is_flex(q)) { + Fl_Widget* w = q->o; + Fl_Flex* f = (Fl_Flex*)((Flex_Node*)q->parent)->o; + int was_fixed = f->fixed(w); + if (new_fixed==0) { + if (was_fixed) { + f->fixed(w, 0); + f->layout(); + mod = 1; + } + } else { + if (!was_fixed) { + f->fixed(w, Flex_Node::size(q)); + f->layout(); + mod = 1; + } + } + } } + if (mod) Fluid.proj.set_modflag(1); } } -static void cb_2(Fl_Button*, void* v) { - if (v != LOAD) { - widget_grid_rows->value( widget_grid_rows->value()-1 ); - widget_grid_rows->do_callback(); - } -} +Fl_Group *wp_gui_values=(Fl_Group *)0; -static void cb_3(Fl_Button*, void* v) { - if (v != LOAD) { - widget_grid_rows->value( widget_grid_rows->value()+1 ); - widget_grid_rows->do_callback(); +static void cb_wp_gui_values(Fl_Group* o, void* v) { + if (v == LOAD) { + if ( current_widget->is_a(Type::Flex) + || current_widget->is_a(Type::Grid) + || current_widget->is_a(Type::Window)) + { + o->hide(); + } else { + o->show(); + propagate_load(o, v); + } } } -fld::widget::Formula_Input *widget_grid_cols=(fld::widget::Formula_Input *)0; - -static void cb_widget_grid_cols(fld::widget::Formula_Input* o, void* v) { - // grid_rows_cb - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; +static void cb_Size(Fl_Value_Input* o, void* v) { if (v == LOAD) { - o->value(grid->cols()); + if (!current_widget->is_a(Type::Slider)) {o->deactivate(); return;} + o->activate(); + o->value(((Fl_Slider*)(current_widget->o))->slider_size()); } else { - int m = o->value(), old_m = grid->cols(); - if (m < 1) { - m = 1; - o->value(m); - } - if (m < old_m) { - // TODO: verify that this will not unlink existings cells - // Offer a dialog with "delete children", "unlink cells", "cancel" - } - if (m != old_m) { - undo_checkpoint(); - grid->layout(grid->rows(), m); - grid->need_layout(true); - set_modflag(1); - widget_tab_grid->do_callback(widget_tab_grid, LOAD); + int mod = 0; + Fluid.proj.undo.checkpoint(); + double n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Slider)) { + ((Fl_Slider*)(q->o))->slider_size(n); + q->o->redraw(); + mod = 1; + } } + if (mod) Fluid.proj.set_modflag(1); } } -static void cb_4(Fl_Button*, void* v) { - if (v != LOAD) { - widget_grid_cols->value( widget_grid_cols->value()-1 ); - widget_grid_cols->do_callback(); +static void cb_Minimum(Fl_Value_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Valuator_)) { + o->activate(); + o->value(((Fl_Valuator*)(current_widget->o))->minimum()); + } else if (current_widget->is_a(Type::Spinner)) { + o->activate(); + o->value(((Fl_Spinner*)(current_widget->o))->minimum()); + } else { + o->deactivate(); + return; + } + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + double n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Valuator_)) { + ((Fl_Valuator*)(q->o))->minimum(n); + q->o->redraw(); + mod = 1; + } else if (q->is_a(Type::Spinner)) { + ((Fl_Spinner*)(q->o))->minimum(n); + q->o->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_Maximum(Fl_Value_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Valuator_)) { + o->activate(); + o->value(((Fl_Valuator*)(current_widget->o))->maximum()); + } else if (current_widget->is_a(Type::Spinner)) { + o->activate(); + o->value(((Fl_Spinner*)(current_widget->o))->maximum()); + } else { + o->deactivate(); + return; + } + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + double n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Valuator_)) { + ((Fl_Valuator*)(q->o))->maximum(n); + q->o->redraw(); + mod = 1; + } else if (q->is_a(Type::Spinner)) { + ((Fl_Spinner*)(q->o))->maximum(n); + q->o->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_Step(Fl_Value_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Valuator_)) { + o->activate(); + o->value(((Fl_Valuator*)(current_widget->o))->step()); + } else if (current_widget->is_a(Type::Spinner)) { + o->activate(); + o->value(((Fl_Spinner*)(current_widget->o))->step()); + } else { + o->deactivate(); + return; + } + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + double n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Valuator_)) { + ((Fl_Valuator*)(q->o))->step(n); + q->o->redraw(); + mod = 1; + } else if (q->is_a(Type::Spinner)) { + ((Fl_Spinner*)(q->o))->step(n); + q->o->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_Value(Fl_Value_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Valuator_)) { + o->activate(); + o->value(((Fl_Valuator*)(current_widget->o))->value()); + } else if (current_widget->is_button()) { + o->activate(); + o->value(((Fl_Button*)(current_widget->o))->value()); + } else if (current_widget->is_a(Type::Spinner)) { + o->activate(); + o->value(((Fl_Spinner*)(current_widget->o))->value()); + } else + o->deactivate(); + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + double n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Valuator_)) { + ((Fl_Valuator*)(q->o))->value(n); + mod = 1; + } else if (q->is_button()) { + ((Fl_Button*)(q->o))->value(n != 0); + if (q->is_a(Type::Menu_Item)) q->redraw(); + mod = 1; + } else if (q->is_a(Type::Spinner)) { + ((Fl_Spinner*)(q->o))->value(n); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +Fl_Group *wp_gui_margins=(Fl_Group *)0; + +static void cb_wp_gui_margins(Fl_Group* o, void* v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Flex)) { + o->show(); + propagate_load(o, v); + } else { + o->hide(); + } + } +} + +static void cb_Left(Fl_Value_Input* o, void* v) { + flex_margin_cb(o, v, + [](Fl_Flex *w, Fl_Value_Input* i) -> void + { + int v; + w->margin(&v, nullptr, nullptr, nullptr); + i->value((double)v); + }, + [](Fl_Flex *w, int new_value) -> int + { + int l, t, r, b; + w->margin(&l, &t, &r, &b); + if (new_value!=l) { + w->margin(new_value, t, r, b); + return 1; + } else { + return 0; + } + } + ); +} + +static void cb_Top(Fl_Value_Input* o, void* v) { + flex_margin_cb(o, v, + [](Fl_Flex *w, Fl_Value_Input* i) -> void + { + int v; + w->margin(nullptr, &v, nullptr, nullptr); + i->value((double)v); + }, + [](Fl_Flex *w, int new_value) + { + int l, t, r, b; + w->margin(&l, &t, &r, &b); + if (new_value!=t) { + w->margin(l, new_value, r, b); + return 1; + } else { + return 0; + } + } + ); +} + +static void cb_Right(Fl_Value_Input* o, void* v) { + flex_margin_cb(o, v, + [](Fl_Flex *w, Fl_Value_Input* i) -> void + { + int v; + w->margin(nullptr, nullptr, &v, nullptr); + i->value((double)v); + }, + [](Fl_Flex *w, int new_value) -> int + { + int l, t, r, b; + w->margin(&l, &t, &r, &b); + if (new_value!=r) { + w->margin(l, t, new_value, b); + return 1; + } else { + return 0; + } + } + ); +} + +static void cb_Bottom(Fl_Value_Input* o, void* v) { + flex_margin_cb(o, v, + [](Fl_Flex *w, Fl_Value_Input* i) -> void + { + int v; + w->margin(nullptr, nullptr, nullptr, &v); + i->value((double)v); + }, + [](Fl_Flex *w, int new_value) -> int + { + int l, t, r, b; + w->margin(&l, &t, &r, &b); + if (new_value!=b) { + w->margin(l, t, r, new_value); + return 1; + } else { + return 0; + } + } + ); +} + +static void cb_Gap(Fl_Value_Input* o, void* v) { + flex_margin_cb(o, v, + [](Fl_Flex *w, Fl_Value_Input* o) -> void + { + int v = w->gap(); + o->value((double)v); + }, + [](Fl_Flex *w, int new_value) -> int + { + int g = w->gap(); + if (new_value!=g) { + w->gap(new_value); + return 1; + } else { + return 0; + } + } + ); +} + +Fl_Group *wp_gui_sizerange=(Fl_Group *)0; + +static void cb_wp_gui_sizerange(Fl_Group* o, void* v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Window)) { + o->show(); + propagate_load(o, v); + } else { + o->hide(); + } + } +} + +static void cb_Minimum1(Fl_Value_Input* o, void* v) { + if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) return; + o->value(((Window_Node*)current_widget)->sr_min_w); + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + int n = (int)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + ((Window_Node*)q)->sr_min_w = n; + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_1(Fl_Value_Input* o, void* v) { + if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) return; + o->value(((Window_Node*)current_widget)->sr_min_h); + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + int n = (int)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + ((Window_Node*)q)->sr_min_h = n; + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_set(Fl_Button* o, void* v) { + if (v == LOAD) { + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + Window_Node *win = (Window_Node*)q; + win->sr_min_w = win->o->w(); + win->sr_min_h = win->o->h(); + mod = 1; + } + } + propagate_load(the_panel, LOAD); + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_Maximum1(Fl_Value_Input* o, void* v) { + if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) return; + o->value(((Window_Node*)current_widget)->sr_max_w); + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + int n = (int)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + ((Window_Node*)q)->sr_max_w = n; + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_2(Fl_Value_Input* o, void* v) { + if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) return; + o->value(((Window_Node*)current_widget)->sr_max_h); + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + int n = (int)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + ((Window_Node*)q)->sr_max_h = n; + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_set1(Fl_Button* o, void* v) { + if (v == LOAD) { + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + Window_Node *win = (Window_Node*)q; + win->sr_max_w = win->o->w(); + win->sr_max_h = win->o->h(); + mod = 1; + } + } + propagate_load(the_panel, LOAD); + if (mod) Fluid.proj.set_modflag(1); + } +} + +Fl_Shortcut_Button *wp_gui_shortcut=(Fl_Shortcut_Button *)0; + +static void cb_wp_gui_shortcut(Fl_Shortcut_Button* o, void* v) { + if (v == LOAD) { + if (current_widget->is_button()) + o->value( ((Fl_Button*)(current_widget->o))->shortcut() ); + else if (current_widget->is_a(Type::Input)) + o->value( ((Fl_Input_*)(current_widget->o))->shortcut() ); + else if (current_widget->is_a(Type::Value_Input)) + o->value( ((Fl_Value_Input*)(current_widget->o))->shortcut() ); + else if (current_widget->is_a(Type::Text_Display)) + o->value( ((Fl_Text_Display*)(current_widget->o))->shortcut() ); + else { + o->hide(); + o->parent()->hide(); + return; + } + //i->default_value( i->value() ); // enable the "undo" capability of the shortcut button + o->show(); + o->parent()->show(); + o->redraw(); + } else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) + if (q->is_button()) { + Fl_Button* b = (Fl_Button*)(q->o); + if (b->shortcut() != (int)o->value()) mod = 1; + b->shortcut(o->value()); + if (q->is_a(Type::Menu_Item)) q->redraw(); + } else if (q->is_a(Type::Input)) { + Fl_Input_* b = (Fl_Input_*)(q->o); + if (b->shortcut() != (int)o->value()) mod = 1; + b->shortcut(o->value()); + } else if (q->is_a(Type::Value_Input)) { + Fl_Value_Input* b = (Fl_Value_Input*)(q->o); + if (b->shortcut() != (int)o->value()) mod = 1; + b->shortcut(o->value()); + } else if (q->is_a(Type::Text_Display)) { + Fl_Text_Display* b = (Fl_Text_Display*)(q->o); + if (b->shortcut() != (int)o->value()) mod = 1; + b->shortcut(o->value()); + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +Fl_Group *wp_gui_xclass=(Fl_Group *)0; + +static void cb_3(Fl_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Window)) { + o->show(); + o->parent()->show(); + o->value(((Window_Node *)current_widget)->xclass); + } else { + o->hide(); + o->parent()->hide(); // hides the "X Class:" label as well + } + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + mod = 1; + Window_Node *wt = (Window_Node *)q; + storestring(o->value(), wt->xclass); + ((Fl_Window*)(wt->o))->xclass(wt->xclass); + } + } + if (mod) Fluid.proj.set_modflag(1); } } -static void cb_5(Fl_Button*, void* v) { +static void cb_Border(Fl_Light_Button* o, void* v) { + if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) {o->hide(); return;} + o->show(); + o->value(((Fl_Window*)(current_widget->o))->border()); + } else { + Fluid.proj.undo.checkpoint(); + ((Fl_Window*)(current_widget->o))->border(o->value()); + Fluid.proj.set_modflag(1); + } +} + +static void cb_Modal(Fl_Light_Button* o, void* v) { + if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) {o->hide(); return;} + o->show(); + o->value(((Window_Node *)current_widget)->modal); + } else { + Fluid.proj.undo.checkpoint(); + ((Window_Node *)current_widget)->modal = o->value(); + Fluid.proj.set_modflag(1); + } +} + +static void cb_Nonmodal(Fl_Light_Button* o, void* v) { + if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) {o->hide(); return;} + o->show(); + o->value(((Window_Node *)current_widget)->non_modal); + } else { + Fluid.proj.undo.checkpoint(); + ((Window_Node *)current_widget)->non_modal = o->value(); + Fluid.proj.set_modflag(1); + } +} + +Fl_Group *wp_gui_attributes=(Fl_Group *)0; + +static void cb_Visible(Fl_Light_Button* o, void* v) { + if (v == LOAD) { + o->value(current_widget->o->visible()); + if (current_widget->is_a(Type::Window)) o->deactivate(); + else o->activate(); + } else { + int mod = 0; + int n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (!mod) { + mod = 1; + Fluid.proj.undo.checkpoint(); + } + n ? q->o->show() : q->o->hide(); + q->redraw(); + if (n && q->parent && q->parent->type_name()) { + if (q->parent->is_a(Type::Tabs)) { + ((Fl_Tabs *)q->o->parent())->value(q->o); + } else if (q->parent->is_a(Type::Wizard)) { + ((Fl_Wizard *)q->o->parent())->value(q->o); + } + } + } + if (mod) { + Fluid.proj.set_modflag(1); + redraw_browser(); + } + } +} + +static void cb_Active(Fl_Light_Button* o, void* v) { + if (v == LOAD) { + o->value(current_widget->o->active()); + if (current_widget->is_a(Type::Window)) o->deactivate(); + else o->activate(); + } else { + int mod = 0; + int n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (!mod) { + mod = 1; + Fluid.proj.undo.checkpoint(); + } + n ? q->o->activate() : q->o->deactivate(); + q->redraw(); + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_Resizable(Fl_Light_Button* o, void* v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {o->deactivate(); return;} + if (numselected > 1) {o->deactivate(); return;} + o->activate(); + o->value(current_widget->resizable()); + } else { + Fluid.proj.undo.checkpoint(); + current_widget->resizable(o->value()); + Fluid.proj.set_modflag(1); + } +} + +static void cb_Hotspot(Fl_Light_Button* o, void* v) { + if (v == LOAD) { + if (numselected > 1) {o->deactivate(); return;} + if (current_widget->is_a(Type::Menu_Item)) o->label("divider"); + else o->label("hotspot"); + o->activate(); + o->value(current_widget->hotspot()); + } else { + Fluid.proj.undo.checkpoint(); + current_widget->hotspot(o->value()); + if (current_widget->is_a(Type::Menu_Item)) { + current_widget->redraw(); + return; + } + if (o->value()) { + Node *p = current_widget->parent; + if (!p || !p->is_widget()) return; + while (!p->is_a(Type::Window)) p = p->parent; + for (Node *q = p->next; q && q->level > p->level; q = q->next) { + if (q->is_widget() && q != current_widget) + ((Widget_Node*)q)->hotspot(0); + } + } + Fluid.proj.set_modflag(1); + } +} + +Fl_Input *wp_gui_tooltip=(Fl_Input *)0; + +static void cb_wp_gui_tooltip(Fl_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_widget()) { + o->activate(); + o->value(((Widget_Node*)current_widget)->tooltip()); + } else { + o->deactivate(); + } + } else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->tooltip(o->value()); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +Fl_Group *wp_style_tab=(Fl_Group *)0; + +Fl_Group *wp_style_label=(Fl_Group *)0; + +static void cb_4(Fl_Choice* o, void* v) { + if (v == LOAD) { + int n = current_widget->o->labelfont(); + if (n > 15) n = 0; + o->value(n); + } else { + int mod = 0; + int n = o->value(); + if (n <= 0) n = Fluid.proj.layout->labelfont; + if (n <= 0) n = FL_HELVETICA; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->o->labelfont(n); + q->redraw(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_5(Fl_Value_Input* o, void* v) { + int n; + if (v == LOAD) { + n = current_widget->o->labelsize(); + } else { + int mod = 0; + n = int(o->value()); + if (n <= 0) n = Fluid.proj.layout->labelsize; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->o->labelsize(n); + q->redraw(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } + o->value(n); +} + +Fl_Button *w_labelcolor=(Fl_Button *)0; + +static void cb_w_labelcolor(Fl_Button* o, void* v) { + Fl_Color c = current_widget->o->labelcolor(); + if (v != LOAD) { + Fl_Color d = fl_show_colormap(c); + if (d == c) return; + c = d; + labelcolor_common(c); + } + o->color(c); + o->labelcolor(fl_contrast(FL_BLACK,c)); + o->redraw(); +} + +static void cb_6(Fl_Menu_Button* o, void* v) { + Fl_Color c = current_widget->o->labelcolor(); if (v != LOAD) { - widget_grid_cols->value( widget_grid_cols->value()+1 ); - widget_grid_cols->do_callback(); + Fl_Color d = (Fl_Color)(o->mvalue()->argument()); + if (d == c) return; + c = d; + labelcolor_common(c); + w_labelcolor->color(c); + w_labelcolor->labelcolor(fl_contrast(FL_BLACK,c)); + w_labelcolor->redraw(); + } +} + +Fl_Group *wp_style_box=(Fl_Group *)0; + +static void cb_7(Fl_Choice* o, void* v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {o->deactivate(); return;} else o->activate(); + int n = current_widget->o->box(); + if (!n) n = ZERO_ENTRY; + for (int j = 0; j < 72 /*int(sizeof(boxmenu)/sizeof(*boxmenu))*/; j++) + if (boxmenu[j].argument() == n) {o->value(j); break;} + } else { + int mod = 0; + int m = o->value(); + int n = int(boxmenu[m].argument()); + if (!n) return; // should not happen + if (n == ZERO_ENTRY) n = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->o->box((Fl_Boxtype)n); + q->redraw(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +Fl_Button *w_color=(Fl_Button *)0; + +static void cb_w_color(Fl_Button* o, void* v) { + Fl_Color c = current_widget->o->color(); + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) { + o->deactivate(); + } else { + o->activate(); + } + } else { + Fl_Color d = fl_show_colormap(c); + if (d == c) return; + c = d; + color_common(c); + } + o->color(c); + o->labelcolor(fl_contrast(FL_BLACK,c)); + o->redraw(); +} + +static void cb_8(Fl_Menu_Button* o, void* v) { + Fl_Color c = current_widget->o->color(); + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {o->deactivate(); return;} else o->activate(); + } else { + Fl_Color d = (Fl_Color)(o->mvalue()->argument()); + if (d == c) return; + c = d; + color_common(c); + w_color->color(c); + w_color->labelcolor(fl_contrast(FL_BLACK,c)); + w_color->redraw(); } } -Fl_Group *wp_grid_margin=(Fl_Group *)0; +Fl_Group *wp_style_downbox=(Fl_Group *)0; + +static void cb_9(Fl_Choice* o, void* v) { + if (v == LOAD) { + int n; + if (current_widget->is_a(Type::Button)) + n = ((Fl_Button*)(current_widget->o))->down_box(); + else if (current_widget->is_a(Type::Input_Choice)) + n = ((Fl_Input_Choice*)(current_widget->o))->down_box(); + else if (current_widget->is_a(Type::Menu_Manager_)) + n = ((Fl_Menu_*)(current_widget->o))->down_box(); + else { + o->deactivate(); return; + } + o->activate(); + if (!n) n = ZERO_ENTRY; + for (int j = 0; j < 72 /*int(sizeof(boxmenu)/sizeof(*boxmenu))*/; j++) + if (boxmenu[j].argument() == n) {o->value(j); break;} + } else { + int mod = 0; + int m = o->value(); + int n = int(boxmenu[m].argument()); + if (!n) return; // should not happen + if (n == ZERO_ENTRY) n = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Button)) { + ((Fl_Button*)(q->o))->down_box((Fl_Boxtype)n); + if (((Fl_Button*)(q->o))->value()) q->redraw(); + } else if (q->is_a(Type::Input_Choice)) { + ((Fl_Input_Choice*)(q->o))->down_box((Fl_Boxtype)n); + } else if (q->is_a(Type::Menu_Manager_)) { + ((Fl_Menu_*)(q->o))->down_box((Fl_Boxtype)n); + } + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +Fl_Button *w_selectcolor=(Fl_Button *)0; + +static void cb_w_selectcolor(Fl_Button* o, void* v) { + Fl_Color c = current_widget->o->selection_color(); + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) { + o->deactivate(); + return; + } else { + o->activate(); + } + } else { + Fl_Color d = fl_show_colormap(c); + if (d == c) return; + c = d; + color2_common(c); + } + o->color(c); + o->labelcolor(fl_contrast(FL_BLACK,c)); + o->redraw(); +} + +static void cb_a(Fl_Menu_Button* o, void* v) { + Fl_Color c = current_widget->o->selection_color(); + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) { + o->deactivate(); + return; + } else { + o->activate(); + } + } else { + Fl_Color d = (Fl_Color)(o->mvalue()->argument()); + if (d == c) return; + c = d; + color2_common(c); + w_selectcolor->color(c); + w_selectcolor->labelcolor(fl_contrast(FL_BLACK,c)); + w_selectcolor->redraw(); + } +} + +Fl_Group *wp_style_text=(Fl_Group *)0; + +static void cb_b(Fl_Choice* o, void* v) { + Fl_Font n; int s; Fl_Color c; + if (v == LOAD) { + if (!current_widget->textstuff(0,n,s,c)) {o->deactivate(); return;} + o->activate(); + if (n > 15) n = FL_HELVETICA; + o->value(n); + } else { + int mod = 0; + n = (Fl_Font)o->value(); + if (n <= 0) n = Fluid.proj.layout->textfont; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->textstuff(1,n,s,c); + q->o->redraw(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_c(Fl_Value_Input* o, void* v) { + Fl_Font n; int s; Fl_Color c; + if (v == LOAD) { + if (!current_widget->textstuff(0,n,s,c)) {o->deactivate(); return;} + o->activate(); + } else { + int mod = 0; + s = int(o->value()); + if (s <= 0) s = Fluid.proj.layout->textsize; + if (s <= 0) s = Fluid.proj.layout->labelsize; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->textstuff(2,n,s,c); + q->o->redraw(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } + o->value(s); +} + +Fl_Button *w_textcolor=(Fl_Button *)0; + +static void cb_w_textcolor(Fl_Button* o, void* v) { + Fl_Font n; int s; Fl_Color c; + if (v == LOAD) { + if (!current_widget->textstuff(0,n,s,c)) { + o->deactivate(); + return; + } + o->activate(); + } else { + c = o->color(); + Fl_Color d = fl_show_colormap(c); + if (d == c) return; + c = d; + textcolor_common(c); + } + o->color(c); + o->labelcolor(fl_contrast(FL_BLACK,c)); + o->redraw(); +} + +static void cb_d(Fl_Menu_Button* o, void* v) { + Fl_Font n; int s; Fl_Color c; + if (v == LOAD) { + if (!current_widget->textstuff(0,n,s,c)) { + o->deactivate(); + return; + } + o->activate(); + } else { + c = o->color(); + Fl_Color d = (Fl_Color)(o->mvalue()->argument()); + if (d == c) return; + c = d; + textcolor_common(c); + w_textcolor->color(c); + w_textcolor->labelcolor(fl_contrast(FL_BLACK,c)); + w_textcolor->redraw(); + } +} + +static void cb_Horizontal(Fl_Value_Input* o, void* v) { + int s; + if (v == LOAD) { + if (!current_widget->is_true_widget()) { + o->deactivate(); + o->value(0); + } else { + o->activate(); + o->value(((Widget_Node*)current_widget)->o->horizontal_label_margin()); + } + } else { + int mod = 0; + s = int(o->value()); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + if (q->o->horizontal_label_margin() != s) { + q->o->horizontal_label_margin(s); + if (!(q->o->align() & FL_ALIGN_INSIDE) && q->o->window()) + q->o->window()->damage(FL_DAMAGE_EXPOSE); // outside labels + q->o->redraw(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_Vertical(Fl_Value_Input* o, void* v) { + int s; + if (v == LOAD) { + if (!current_widget->is_true_widget()) { + o->deactivate(); + o->value(0); + } else { + o->activate(); + o->value(((Widget_Node*)current_widget)->o->vertical_label_margin()); + } + } else { + int mod = 0; + s = int(o->value()); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + if (q->o->vertical_label_margin() != s) { + q->o->vertical_label_margin(s); + if (!(q->o->align() & FL_ALIGN_INSIDE) && q->o->window()) + q->o->window()->damage(FL_DAMAGE_EXPOSE); // outside labels + q->o->redraw(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_Image(Fl_Value_Input* o, void* v) { + int s; + if (v == LOAD) { + if (!current_widget->is_true_widget()) { + o->deactivate(); + o->value(0); + } else { + o->activate(); + o->value(((Widget_Node*)current_widget)->o->label_image_spacing()); + } + } else { + int mod = 0; + s = int(o->value()); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + if (q->o->label_image_spacing() != s) { + q->o->label_image_spacing(s); + if (!(q->o->align() & FL_ALIGN_INSIDE) && q->o->window()) + q->o->window()->damage(FL_DAMAGE_EXPOSE); // outside labels + q->o->redraw(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_Compact(Fl_Light_Button* o, void* v) { + if (v == LOAD) { + uchar n; + if (current_widget->is_a(Type::Button) && !current_widget->is_a(Type::Menu_Item)) { + n = ((Fl_Button*)(current_widget->o))->compact(); + o->value(n); + o->show(); + } else { + o->hide(); + } + } else { + int mod = 0; + uchar n = (uchar)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Button) && !q->is_a(Type::Menu_Item)) { + uchar v = ((Fl_Button*)(q->o))->compact(); + if (n != v) { + if (!mod) { + mod = 1; + Fluid.proj.undo.checkpoint(); + } + ((Fl_Button*)(q->o))->compact(n); + q->redraw(); + } + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +Fl_Group *wp_cpp_tab=(Fl_Group *)0; + +Fl_Group *wp_cpp_class=(Fl_Group *)0; + +static void cb_e(Fl_Input* o, void* v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) { + o->deactivate(); + } else { + o->activate(); + o->value(current_widget->subclass()); + } + } else { + int mod = 0; + const char *c = o->value(); + for (Widget_Node *t: Fluid.proj.tree.all_selected_widgets()) { + t->subclass(c); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +static void cb_f(Fl_Choice* o, void* v) { + static Fl_Menu_Item empty_type_menu[] = { + {"Normal",0,nullptr,(void*)nullptr}, + {nullptr}}; + + if (v == LOAD) { + Fl_Menu_Item* m = current_widget->subtypes(); + if (!m) { + o->menu(empty_type_menu); + o->value(0); + o->deactivate(); + } else { + o->menu(m); + int j; + for (j = 0;; j++) { + if (!m[j].text) {j = 0; break;} + if (current_widget->is_a(Type::Spinner)) { + if (m[j].argument() == ((Fl_Spinner*)current_widget->o)->type()) break; + } else { + if (m[j].argument() == current_widget->o->type()) break; + } + } + o->value(j); + o->activate(); + } + o->redraw(); + } else { + int mod = 0; + int n = int(o->mvalue()->argument()); + Fl_Menu_Item* m = current_widget->subtypes(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->subtypes()==m) { + if (q->is_a(Type::Spinner)) + ((Fl_Spinner*)q->o)->type(n); + else if (q->is_a(Type::Flex)) + ((Flex_Node*)q)->change_subtype_to(n); + else + q->o->type(n); + q->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +Fl_Group *wp_cpp_name=(Fl_Group *)0; -static void cb_Left(Fl_Value_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - int m = 0; +static void cb_10(Fl_Input* o, void* v) { if (v == LOAD) { - grid->margin(&m, NULL, NULL, NULL); - o->value(m); - } else { - int m = (int)o->value(), old_m; - grid->margin(&old_m, NULL, NULL, NULL); - if (m != old_m) { - undo_checkpoint(); - grid->margin(m, -1, -1, -1); - grid->need_layout(true); - set_modflag(1); - } - } -} + static char buf[1024]; + if (numselected != 1) { + snprintf(buf, sizeof(buf), "Widget Properties (%d widgets)", numselected); + o->hide(); + } else { + o->value(current_widget->name()); + o->show(); + snprintf(buf, sizeof(buf), "%s Properties", current_widget->title()); + } -static void cb_Top(Fl_Value_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - int m = 0; - if (v == LOAD) { - grid->margin(NULL, &m, NULL, NULL); - o->value(m); - } else { - int m = (int)o->value(), old_m; - grid->margin(NULL, &old_m, NULL, NULL); - if (m != old_m) { - undo_checkpoint(); - grid->margin(-1, m, -1, -1); - grid->need_layout(true); - set_modflag(1); + the_panel->label(buf); + } else { + if (numselected == 1) { + current_widget->name(o->value()); + // I don't update window title, as it probably is being closed + // and wm2 (a window manager) barfs if you retitle and then + // hide a window: + // ((Fl_Window*)(o->parent()->parent()->parent()))->label(current_widget->title()); + } } - } } -static void cb_Right(Fl_Value_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - int m = 0; +static void cb_11(Fl_Choice* o, void* v) { if (v == LOAD) { - grid->margin(NULL, NULL, &m, NULL); - o->value(m); + o->value(current_widget->public_); + if (current_widget->is_in_class()) o->show(); else o->hide(); } else { - int m = (int)o->value(), old_m; - grid->margin(NULL, NULL, &old_m, NULL); - if (m != old_m) { - undo_checkpoint(); - grid->margin(-1, -1, m, -1); - grid->need_layout(true); - set_modflag(1); + int mod = 0; + for (Widget_Node *w: Fluid.proj.tree.all_selected_widgets()) { + if (w->is_in_class()) { + w->public_ = o->value(); + } else { + // if this is not in a class, it can be only private or public + w->public_ = (o->value()>0); + } + mod = 1; } - } -} - -static void cb_Bottom(Fl_Value_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - int m = 0; - if (v == LOAD) { - grid->margin(NULL, NULL, NULL, &m); - o->value(m); - } else { - int m = (int)o->value(), old_m; - grid->margin(NULL, NULL, NULL, &old_m); - if (m != old_m) { - undo_checkpoint(); - grid->margin(-1, -1, -1, m); - grid->need_layout(true); - set_modflag(1); + if (mod) { + Fluid.proj.set_modflag(1); + redraw_browser(); } } } -Fl_Group *wp_grid_gaps=(Fl_Group *)0; +Fl_Menu_Item menu_2[] = { + {"private", 0, 0, (void*)(0), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {"public", 0, 0, (void*)(1), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {"protected", 0, 0, (void*)(2), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {0,0,0,0,0,0,0,0,0} +}; -static void cb_Row(Fl_Value_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - if (v == LOAD) { - int m = 0; - grid->gap(&m, NULL); - o->value(m); - } else { - int m = (int)o->value(), old_m, m2; - grid->gap(&old_m, &m2); - if (m != old_m) { - undo_checkpoint(); - grid->gap(m, m2); - grid->need_layout(true); - set_modflag(1); +Fl_Menu_Item menu_3[] = { + {"local", 0, 0, (void*)(0), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {"global", 0, 0, (void*)(1), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {0,0,0,0,0,0,0,0,0} +}; + +static void cb_v_input(Fl_Input* o, void* v) { + int n = fl_int(o->user_data()); + if (v == LOAD) { + o->value(current_widget->extra_code(n)); + } else { + int mod = 0; + const char *c = o->value(); + const char *d = c_check(c&&c[0]=='#' ? c+1 : c); + if (d) {fl_message("Error in %s: %s",o->label(),d); haderror = 1; return;} + for (Widget_Node *w: Fluid.proj.tree.all_selected_widgets()) { + w->extra_code(n,c); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); } - } } -static void cb_Col(Fl_Value_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - if (v == LOAD) { - int m = 0; - grid->gap(NULL, &m); - o->value(m); - } else { - int m = (int)o->value(), old_m, m2; - grid->gap(&m2, &old_m); - if (m != old_m) { - undo_checkpoint(); - grid->gap(m2, m); - grid->need_layout(true); - set_modflag(1); - } - } +static void cb_v_input1(Fl_Input* o, void* v) { + cb_v_input(o, v); } -static void cb_Row1(Fl_Group* o, void* v) { - if (v == LOAD) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (grid) - o->activate(); - else - o->deactivate(); - propagate_load(o, v); - } +static void cb_v_input2(Fl_Input* o, void* v) { + cb_v_input(o, v); } -fld::widget::Formula_Input *widget_grid_curr_row=(fld::widget::Formula_Input *)0; +Fl_Input *v_input[4]={(Fl_Input *)0}; -static void cb_widget_grid_curr_row(fld::widget::Formula_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - int r = o->value(), old_r = r; - if (r < 0) r = 0; - if (r >= grid->rows()) r = grid->rows()-1; - if (r != old_r) o->value(r); - if (v == LOAD) { - // will automatically propagate - } else { - widget_grid_curr_row_attributes->do_callback(widget_grid_curr_row_attributes, LOAD); - } +static void cb_v_input3(Fl_Input* o, void* v) { + cb_v_input(o, v); } -static void cb_6(Fl_Button*, void* v) { - if (v != LOAD) { - widget_grid_curr_row->value( widget_grid_curr_row->value()-1 ); - widget_grid_curr_row->do_callback(); - } +static void cb_12(Fl_Tile*, void* v) { + wComment->do_callback(wComment, v); + wCallback->do_callback(wCallback, v); } -static void cb_7(Fl_Button*, void* v) { - if (v != LOAD) { - widget_grid_curr_row->value( widget_grid_curr_row->value()+1 ); - widget_grid_curr_row->do_callback(); - } +Fl_Text_Editor *wComment=(Fl_Text_Editor *)0; + +static void cb_wComment(Fl_Text_Editor* o, void* v) { + if (v == LOAD) { + const char *cmttext = current_widget->comment(); + o->buffer()->text( cmttext ? cmttext : "" ); + } else { + int mod = 0; + char *c = o->buffer()->text(); + for (Node *n: Fluid.proj.tree.all_selected_nodes()) { + n->comment(c); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + free(c); + } } -Fl_Group *widget_grid_curr_row_attributes=(Fl_Group *)0; +fld::widget::Code_Editor *wCallback=(fld::widget::Code_Editor *)0; -static void cb_Height(fld::widget::Formula_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - int r = widget_grid_curr_row->value(); +static void cb_wCallback(fld::widget::Code_Editor* o, void* v) { if (v == LOAD) { - o->value(grid->row_height(r)); - } else { - int h = o->value(), old_h = grid->row_height(r); - if (h < 0) h = 0; - if (h != old_h) { - undo_checkpoint(); - grid->row_height(r, h); - grid->need_layout(true); - set_modflag(1); + const char *cbtext = current_widget->callback(); + o->buffer()->text( cbtext ? cbtext : "" ); + } else { + int mod = 0; + char *c = o->buffer()->text(); + const char *d = c_check(c); + if (d) { + fl_message("Error in callback: %s",d); + if (o->window()) o->window()->make_current(); + haderror = 1; + } + for (Node *n: Fluid.proj.tree.all_selected_nodes()) { + n->callback(c); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + free(c); } - } } -static void cb_Weight(fld::widget::Formula_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - int r = widget_grid_curr_row->value(); +Fl_Group *wp_cpp_callback=(Fl_Group *)0; + +static void cb_13(Fl_Input* o, void* v) { if (v == LOAD) { - o->value(grid->row_weight(r)); + o->value(current_widget->user_data()); } else { - int h = o->value(), old_h = grid->row_weight(r); - if (h < 0) h = 0; - if (h != old_h) { - undo_checkpoint(); - grid->row_weight(r, h); - grid->need_layout(true); - set_modflag(1); + int mod = 0; + const char *c = o->value(); + const char *d = c_check(c); + if (d) {fl_message("Error in user_data: %s",d); haderror = 1; return;} + for (Node *n: Fluid.proj.tree.all_selected_nodes()) { + n->user_data(c); + mod = 1; } + if (mod) Fluid.proj.set_modflag(1); } } -static void cb_Gap(fld::widget::Formula_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - int r = widget_grid_curr_row->value(); +static void cb_When(Fl_Menu_Button* o, void* v) { if (v == LOAD) { - o->value(grid->row_gap(r)); + if (current_widget->is_a(Type::Menu_Item)) {o->deactivate(); return;} else o->activate(); + int n = current_widget->o->when(); + set_whenmenu(n); + w_when_box->copy_label(when_symbol_name(n)); } else { - int h = o->value(), old_h = grid->row_gap(r); - if (h < -1) h = -1; - if (h != old_h) { - undo_checkpoint(); - grid->row_gap(r, h); - grid->need_layout(true); - set_modflag(1); + int mod = 0; + int n = 0; + if (o->mvalue() && ((o->mvalue()->flags & FL_MENU_TOGGLE) == 0) ) { + n = (int)o->mvalue()->argument(); + set_whenmenu(n); + } else { + if (whenmenu[0].value()) n |= FL_WHEN_CHANGED; + if (whenmenu[1].value()) n |= FL_WHEN_NOT_CHANGED; + if (whenmenu[2].value()) n |= FL_WHEN_RELEASE; + if (whenmenu[3].value()) n |= FL_WHEN_ENTER_KEY; + if (whenmenu[4].value()) n |= FL_WHEN_CLOSED; } + w_when_box->copy_label(when_symbol_name(n)); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->o->when(n); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); } } -fld::widget::Formula_Input *widget_grid_curr_col=(fld::widget::Formula_Input *)0; - -static void cb_widget_grid_curr_col(fld::widget::Formula_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - int c = o->value(), old_c = c; - if (c < 0) c = 0; - if (c >= grid->cols()) c = grid->cols()-1; - if (c != old_c) o->value(c); +static void cb_14(Fl_Input_Choice* o, void* v) { + static const char *dflt = "void*"; if (v == LOAD) { - // will automatically propagate + const char *c = current_widget->user_data_type(); + if (!c) c = dflt; + o->value(c); } else { - widget_grid_curr_col_attributes->do_callback(widget_grid_curr_col_attributes, LOAD); + int mod = 0; + const char *c = o->value(); + const char *d = c_check(c); + if (!*c) o->value(dflt); + else if (!strcmp(c,dflt)) c = nullptr; + if (!d) { + if (c && *c && c[strlen(c)-1] != '*' && strcmp(c,"long")) + d = "must be pointer or long"; + } + if (d) {fl_message("Error in type: %s",d); haderror = 1; return;} + for (Node *q: Fluid.proj.tree.all_selected_nodes()) { + q->user_data_type(c); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); } } -static void cb_8(Fl_Button*, void* v) { - if (v != LOAD) { - widget_grid_curr_col->value( widget_grid_curr_col->value()-1 ); - widget_grid_curr_col->do_callback(); - } -} +Fl_Menu_Item menu_4[] = { + {"void*", 0, 0, 0, 0, (uchar)FL_NORMAL_LABEL, 4, 11, 0}, + {"long", 0, 0, 0, 0, (uchar)FL_NORMAL_LABEL, 4, 11, 0}, + {0,0,0,0,0,0,0,0,0} +}; -static void cb_9(Fl_Button*, void* v) { - if (v != LOAD) { - widget_grid_curr_col->value( widget_grid_curr_col->value()+1 ); - widget_grid_curr_col->do_callback(); - } -} +Fl_Box *w_when_box=(Fl_Box *)0; -Fl_Group *widget_grid_curr_col_attributes=(Fl_Group *)0; +Grid_Tab *widget_tab_grid=(Grid_Tab *)0; -static void cb_Width(fld::widget::Formula_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - int c = widget_grid_curr_col->value(); - if (v == LOAD) { - o->value(grid->col_width(c)); - } else { - int h = o->value(), old_h = grid->col_width(c); - if (h < 0) h = 0; - if (h != old_h) { - undo_checkpoint(); - grid->col_width(c, h); - grid->need_layout(true); - set_modflag(1); - } - } +static void cb_widget_tab_grid(Grid_Tab* o, void*) { + o->callback((Fl_Callback*)propagate_load); } -static void cb_Weight1(fld::widget::Formula_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - int c = widget_grid_curr_col->value(); - if (v == LOAD) { - o->value(grid->col_weight(c)); - } else { - int h = o->value(), old_h = grid->col_weight(c); - if (h < 0) h = 0; - if (h != old_h) { - undo_checkpoint(); - grid->col_weight(c, h); - grid->need_layout(true); - set_modflag(1); - } - } -} +Grid_Child_Tab *widget_tab_grid_child=(Grid_Child_Tab *)0; -static void cb_Gap1(fld::widget::Formula_Input* o, void* v) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (!grid) return; - int c = widget_grid_curr_col->value(); - if (v == LOAD) { - o->value(grid->col_gap(c)); - } else { - int h = o->value(), old_h = grid->col_gap(c); - if (h < -1) h = -1; - if (h != old_h) { - undo_checkpoint(); - grid->col_gap(c, h); - grid->need_layout(true); - set_modflag(1); - } - } +static void cb_widget_tab_grid_child(Grid_Child_Tab* o, void*) { + o->callback((Fl_Callback*)propagate_load); } Fl_Tabs *widget_tabs_repo=(Fl_Tabs *)0; @@ -1053,13 +2281,13 @@ Fl_Double_Window* make_widget_panel() { widget_image_input->labelfont(1); widget_image_input->labelsize(11); widget_image_input->textsize(11); - widget_image_input->callback((Fl_Callback*)image_cb); + widget_image_input->callback((Fl_Callback*)cb_widget_image_input); Fl_Group::current()->resizable(widget_image_input); } // Fl_Input* widget_image_input { Fl_Button* o = new Fl_Button(295, 65, 89, 20, "Browse..."); o->tooltip("Click to choose the active image."); o->labelsize(11); - o->callback((Fl_Callback*)image_browse_cb); + o->callback((Fl_Callback*)cb_Browse); o->align(Fl_Align(256)); } // Fl_Button* o { Fl_Button* o = new Fl_Button(384, 65, 20, 20, "..."); @@ -1078,13 +2306,13 @@ Fl_Double_Window* make_widget_panel() { widget_deimage_input->labelfont(1); widget_deimage_input->labelsize(11); widget_deimage_input->textsize(11); - widget_deimage_input->callback((Fl_Callback*)inactive_cb); + widget_deimage_input->callback((Fl_Callback*)cb_widget_deimage_input); Fl_Group::current()->resizable(widget_deimage_input); } // Fl_Input* widget_deimage_input { Fl_Button* o = new Fl_Button(295, 90, 89, 20, "Browse..."); o->tooltip("Click to choose the inactive image."); o->labelsize(11); - o->callback((Fl_Callback*)inactive_browse_cb); + o->callback((Fl_Callback*)cb_Browse1); } // Fl_Button* o o->end(); } // Fl_Group* o @@ -1188,7 +2416,7 @@ Fl_Double_Window* make_widget_panel() { widget_x_input->labelsize(11); widget_x_input->labelcolor(FL_FOREGROUND_COLOR); widget_x_input->textsize(11); - widget_x_input->callback((Fl_Callback*)x_cb); + widget_x_input->callback((Fl_Callback*)cb_widget_x_input); widget_x_input->align(Fl_Align(FL_ALIGN_TOP_LEFT)); widget_x_input->when(FL_WHEN_RELEASE); } // fld::widget::Formula_Input* widget_x_input @@ -1203,7 +2431,7 @@ Fl_Double_Window* make_widget_panel() { widget_y_input->labelsize(11); widget_y_input->labelcolor(FL_FOREGROUND_COLOR); widget_y_input->textsize(11); - widget_y_input->callback((Fl_Callback*)y_cb); + widget_y_input->callback((Fl_Callback*)cb_widget_y_input); widget_y_input->align(Fl_Align(FL_ALIGN_TOP_LEFT)); widget_y_input->when(FL_WHEN_RELEASE); } // fld::widget::Formula_Input* widget_y_input @@ -1218,7 +2446,7 @@ Fl_Double_Window* make_widget_panel() { widget_w_input->labelsize(11); widget_w_input->labelcolor(FL_FOREGROUND_COLOR); widget_w_input->textsize(11); - widget_w_input->callback((Fl_Callback*)w_cb); + widget_w_input->callback((Fl_Callback*)cb_widget_w_input); widget_w_input->align(Fl_Align(FL_ALIGN_TOP_LEFT)); widget_w_input->when(FL_WHEN_RELEASE); } // fld::widget::Formula_Input* widget_w_input @@ -1233,7 +2461,7 @@ Fl_Double_Window* make_widget_panel() { widget_h_input->labelsize(11); widget_h_input->labelcolor(FL_FOREGROUND_COLOR); widget_h_input->textsize(11); - widget_h_input->callback((Fl_Callback*)h_cb); + widget_h_input->callback((Fl_Callback*)cb_widget_h_input); widget_h_input->align(Fl_Align(FL_ALIGN_TOP_LEFT)); widget_h_input->when(FL_WHEN_RELEASE); } // fld::widget::Formula_Input* widget_h_input @@ -1244,7 +2472,7 @@ Fl_Double_Window* make_widget_panel() { o->down_box(FL_BORDER_BOX); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)wc_relative_cb); + o->callback((Fl_Callback*)cb_Children); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); o->menu(menu_Children); } // Fl_Choice* o @@ -1258,21 +2486,21 @@ Fl_Double_Window* make_widget_panel() { wp_gui_flexp = new Fl_Group(95, 150, 314, 20, "Flex Parent:"); wp_gui_flexp->labelfont(1); wp_gui_flexp->labelsize(11); - wp_gui_flexp->callback((Fl_Callback*)flex_size_group_cb); + wp_gui_flexp->callback((Fl_Callback*)cb_wp_gui_flexp); wp_gui_flexp->align(Fl_Align(FL_ALIGN_LEFT)); wp_gui_flexp->hide(); { widget_flex_size = new Fl_Value_Input(95, 150, 55, 20, "Size:"); widget_flex_size->tooltip("Fixed Width or Height for a horizontal or vertical Fl_Flex Parent."); widget_flex_size->labelsize(11); widget_flex_size->textsize(11); - widget_flex_size->callback((Fl_Callback*)flex_size_cb); + widget_flex_size->callback((Fl_Callback*)cb_widget_flex_size); widget_flex_size->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* widget_flex_size { widget_flex_fixed = new Fl_Check_Button(155, 150, 55, 20, "fixed"); widget_flex_fixed->tooltip("If checked, the size of the widget stays fixed."); widget_flex_fixed->down_box(FL_DOWN_BOX); widget_flex_fixed->labelsize(11); - widget_flex_fixed->callback((Fl_Callback*)flex_fixed_cb); + widget_flex_fixed->callback((Fl_Callback*)cb_widget_flex_fixed); } // Fl_Check_Button* widget_flex_fixed { Fl_Box* o = new Fl_Box(398, 150, 1, 20); Fl_Group::current()->resizable(o); @@ -1282,20 +2510,20 @@ Fl_Double_Window* make_widget_panel() { { wp_gui_values = new Fl_Group(95, 185, 300, 20, "Values:"); wp_gui_values->labelfont(1); wp_gui_values->labelsize(11); - wp_gui_values->callback((Fl_Callback*)values_group_cb); + wp_gui_values->callback((Fl_Callback*)cb_wp_gui_values); wp_gui_values->align(Fl_Align(FL_ALIGN_LEFT)); { Fl_Value_Input* o = new Fl_Value_Input(95, 185, 55, 20, "Size:"); o->tooltip("The size of the slider."); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)slider_size_cb); + o->callback((Fl_Callback*)cb_Size); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Value_Input* o = new Fl_Value_Input(155, 185, 55, 20, "Minimum:"); o->tooltip("The minimum value of the widget."); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)min_cb); + o->callback((Fl_Callback*)cb_Minimum); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Value_Input* o = new Fl_Value_Input(215, 185, 55, 20, "Maximum:"); @@ -1303,21 +2531,21 @@ Fl_Double_Window* make_widget_panel() { o->labelsize(11); o->value(1); o->textsize(11); - o->callback((Fl_Callback*)max_cb); + o->callback((Fl_Callback*)cb_Maximum); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Value_Input* o = new Fl_Value_Input(275, 185, 55, 20, "Step:"); o->tooltip("The resolution of the widget value."); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)step_cb); + o->callback((Fl_Callback*)cb_Step); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Value_Input* o = new Fl_Value_Input(335, 185, 55, 20, "Value:"); o->tooltip("The current widget value."); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)value_cb); + o->callback((Fl_Callback*)cb_Value); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Box* o = new Fl_Box(395, 185, 0, 20); @@ -1329,42 +2557,42 @@ Fl_Double_Window* make_widget_panel() { wp_gui_margins = new Fl_Group(95, 185, 300, 20, "Margins:"); wp_gui_margins->labelfont(1); wp_gui_margins->labelsize(11); - wp_gui_margins->callback((Fl_Callback*)flex_margin_group_cb); + wp_gui_margins->callback((Fl_Callback*)cb_wp_gui_margins); wp_gui_margins->align(Fl_Align(FL_ALIGN_LEFT)); wp_gui_margins->hide(); { Fl_Value_Input* o = new Fl_Value_Input(95, 185, 55, 20, "Left:"); o->tooltip("Left margin in group."); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)flex_margin_left_cb); + o->callback((Fl_Callback*)cb_Left); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Value_Input* o = new Fl_Value_Input(155, 185, 55, 20, "Top:"); o->tooltip("Top margin in group."); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)flex_margin_top_cb); + o->callback((Fl_Callback*)cb_Top); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Value_Input* o = new Fl_Value_Input(215, 185, 55, 20, "Right:"); o->tooltip("Right margin in group."); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)flex_margin_right_cb); + o->callback((Fl_Callback*)cb_Right); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Value_Input* o = new Fl_Value_Input(275, 185, 55, 20, "Bottom:"); o->tooltip("Bottom margin in group."); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)flex_margin_bottom_cb); + o->callback((Fl_Callback*)cb_Bottom); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Value_Input* o = new Fl_Value_Input(335, 185, 55, 20, "Gap:"); o->tooltip("Gap between children."); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)flex_margin_gap_cb); + o->callback((Fl_Callback*)cb_Gap); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Box* o = new Fl_Box(395, 185, 0, 20); @@ -1375,7 +2603,7 @@ Fl_Double_Window* make_widget_panel() { { wp_gui_sizerange = new Fl_Group(95, 185, 300, 20, "Size Range:"); wp_gui_sizerange->labelfont(1); wp_gui_sizerange->labelsize(11); - wp_gui_sizerange->callback((Fl_Callback*)size_range_group_cb); + wp_gui_sizerange->callback((Fl_Callback*)cb_wp_gui_sizerange); wp_gui_sizerange->align(Fl_Align(FL_ALIGN_LEFT)); wp_gui_sizerange->hide(); { Fl_Value_Input* o = new Fl_Value_Input(95, 185, 55, 20, "Minimum Size:"); @@ -1384,7 +2612,7 @@ Fl_Double_Window* make_widget_panel() { o->maximum(2048); o->step(1); o->textsize(11); - o->callback((Fl_Callback*)min_w_cb); + o->callback((Fl_Callback*)cb_Minimum1); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Value_Input* o = new Fl_Value_Input(155, 185, 55, 20); @@ -1393,12 +2621,12 @@ Fl_Double_Window* make_widget_panel() { o->maximum(2048); o->step(1); o->textsize(11); - o->callback((Fl_Callback*)min_h_cb); + o->callback((Fl_Callback*)cb_1); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Button* o = new Fl_Button(215, 185, 25, 20, "set"); o->labelsize(11); - o->callback((Fl_Callback*)set_min_size_cb); + o->callback((Fl_Callback*)cb_set); } // Fl_Button* o { Fl_Value_Input* o = new Fl_Value_Input(245, 185, 55, 20, "Maximum Size:"); o->tooltip("The maximum value of the widget."); @@ -1406,7 +2634,7 @@ Fl_Double_Window* make_widget_panel() { o->maximum(2048); o->step(1); o->textsize(11); - o->callback((Fl_Callback*)max_w_cb); + o->callback((Fl_Callback*)cb_Maximum1); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Value_Input* o = new Fl_Value_Input(305, 185, 55, 20); @@ -1415,12 +2643,12 @@ Fl_Double_Window* make_widget_panel() { o->maximum(2048); o->step(1); o->textsize(11); - o->callback((Fl_Callback*)max_h_cb); + o->callback((Fl_Callback*)cb_2); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Button* o = new Fl_Button(365, 185, 25, 20, "set"); o->labelsize(11); - o->callback((Fl_Callback*)set_max_size_cb); + o->callback((Fl_Callback*)cb_set1); } // Fl_Button* o { Fl_Box* o = new Fl_Box(395, 185, 0, 20); Fl_Group::current()->resizable(o); @@ -1442,7 +2670,7 @@ Fl_Double_Window* make_widget_panel() { wp_gui_shortcut->labelfont(0); wp_gui_shortcut->labelsize(11); wp_gui_shortcut->labelcolor(FL_FOREGROUND_COLOR); - wp_gui_shortcut->callback((Fl_Callback*)shortcut_in_cb); + wp_gui_shortcut->callback((Fl_Callback*)cb_wp_gui_shortcut); wp_gui_shortcut->align(Fl_Align(FL_ALIGN_CENTER)); wp_gui_shortcut->when(FL_WHEN_CHANGED); } // Fl_Shortcut_Button* wp_gui_shortcut @@ -1458,26 +2686,26 @@ Fl_Double_Window* make_widget_panel() { o->labelfont(1); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)xclass_cb); + o->callback((Fl_Callback*)cb_3); Fl_Group::current()->resizable(o); } // Fl_Input* o { Fl_Light_Button* o = new Fl_Light_Button(195, 235, 60, 20, "Border"); o->tooltip("Add a border around the window."); o->selection_color((Fl_Color)1); o->labelsize(11); - o->callback((Fl_Callback*)border_cb); + o->callback((Fl_Callback*)cb_Border); } // Fl_Light_Button* o { Fl_Light_Button* o = new Fl_Light_Button(260, 235, 55, 20, "Modal"); o->tooltip("Make the window modal."); o->selection_color((Fl_Color)1); o->labelsize(11); - o->callback((Fl_Callback*)modal_cb); + o->callback((Fl_Callback*)cb_Modal); } // Fl_Light_Button* o { Fl_Light_Button* o = new Fl_Light_Button(320, 235, 75, 20, "Nonmodal"); o->tooltip("Make the window non-modal."); o->selection_color((Fl_Color)1); o->labelsize(11); - o->callback((Fl_Callback*)non_modal_cb); + o->callback((Fl_Callback*)cb_Nonmodal); o->align(Fl_Align(132|FL_ALIGN_INSIDE)); } // Fl_Light_Button* o wp_gui_xclass->end(); @@ -1491,26 +2719,26 @@ Fl_Double_Window* make_widget_panel() { o->tooltip("Show the widget."); o->selection_color((Fl_Color)1); o->labelsize(11); - o->callback((Fl_Callback*)visible_cb); + o->callback((Fl_Callback*)cb_Visible); } // Fl_Light_Button* o { Fl_Light_Button* o = new Fl_Light_Button(160, 260, 60, 20, "Active"); o->tooltip("Activate the widget."); o->selection_color((Fl_Color)1); o->labelsize(11); - o->callback((Fl_Callback*)active_cb); + o->callback((Fl_Callback*)cb_Active); } // Fl_Light_Button* o { Fl_Light_Button* o = new Fl_Light_Button(225, 260, 75, 20, "Resizable"); o->tooltip("Make the widget resizable."); o->selection_color((Fl_Color)1); o->labelsize(11); - o->callback((Fl_Callback*)resizable_cb); + o->callback((Fl_Callback*)cb_Resizable); o->when(FL_WHEN_CHANGED); } // Fl_Light_Button* o { Fl_Light_Button* o = new Fl_Light_Button(305, 260, 70, 20, "Hotspot"); o->tooltip("Center the window under this widget."); o->selection_color((Fl_Color)1); o->labelsize(11); - o->callback((Fl_Callback*)hotspot_cb); + o->callback((Fl_Callback*)cb_Hotspot); o->when(FL_WHEN_CHANGED); } // Fl_Light_Button* o { Fl_Box* o = new Fl_Box(395, 260, 0, 20); @@ -1524,7 +2752,7 @@ Fl_Double_Window* make_widget_panel() { wp_gui_tooltip->labelfont(1); wp_gui_tooltip->labelsize(11); wp_gui_tooltip->textsize(11); - wp_gui_tooltip->callback((Fl_Callback*)tooltip_cb); + wp_gui_tooltip->callback((Fl_Callback*)cb_wp_gui_tooltip); } // Fl_Input* wp_gui_tooltip { Fl_Box* o = new Fl_Box(95, 305, 300, 5); o->hide(); @@ -1550,7 +2778,7 @@ Fl_Double_Window* make_widget_panel() { o->labelfont(1); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)labelfont_cb); + o->callback((Fl_Callback*)cb_4); Fl_Group::current()->resizable(o); o->menu(fontmenu); } // Fl_Choice* o @@ -1561,15 +2789,15 @@ Fl_Double_Window* make_widget_panel() { o->step(1); o->value(14); o->textsize(11); - o->callback((Fl_Callback*)labelsize_cb); + o->callback((Fl_Callback*)cb_5); } // Fl_Value_Input* o { w_labelcolor = new Fl_Button(296, 40, 90, 20, "Label Color"); w_labelcolor->tooltip("The color of the label text."); w_labelcolor->labelsize(11); - w_labelcolor->callback((Fl_Callback*)labelcolor_cb); + w_labelcolor->callback((Fl_Callback*)cb_w_labelcolor); } // Fl_Button* w_labelcolor { Fl_Menu_Button* o = new Fl_Menu_Button(386, 40, 18, 20); - o->callback((Fl_Callback*)labelcolor_menu_cb); + o->callback((Fl_Callback*)cb_6); o->menu(colormenu); } // Fl_Menu_Button* o wp_style_label->end(); @@ -1586,17 +2814,17 @@ Fl_Double_Window* make_widget_panel() { o->labelfont(1); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)box_cb); + o->callback((Fl_Callback*)cb_7); Fl_Group::current()->resizable(o); o->menu(boxmenu); } // Fl_Choice* o { w_color = new Fl_Button(296, 65, 90, 20, "Color"); w_color->tooltip("The background color of the widget."); w_color->labelsize(11); - w_color->callback((Fl_Callback*)color_cb); + w_color->callback((Fl_Callback*)cb_w_color); } // Fl_Button* w_color { Fl_Menu_Button* o = new Fl_Menu_Button(386, 65, 18, 20); - o->callback((Fl_Callback*)color_menu_cb); + o->callback((Fl_Callback*)cb_8); o->menu(colormenu); } // Fl_Menu_Button* o wp_style_box->end(); @@ -1613,17 +2841,17 @@ Fl_Double_Window* make_widget_panel() { o->labelfont(1); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)down_box_cb); + o->callback((Fl_Callback*)cb_9); Fl_Group::current()->resizable(o); o->menu(boxmenu); } // Fl_Choice* o { w_selectcolor = new Fl_Button(296, 90, 90, 20, "Select Color"); w_selectcolor->tooltip("The selection color of the widget."); w_selectcolor->labelsize(11); - w_selectcolor->callback((Fl_Callback*)color2_cb); + w_selectcolor->callback((Fl_Callback*)cb_w_selectcolor); } // Fl_Button* w_selectcolor { Fl_Menu_Button* o = new Fl_Menu_Button(386, 90, 18, 20); - o->callback((Fl_Callback*)color2_menu_cb); + o->callback((Fl_Callback*)cb_a); o->menu(colormenu); } // Fl_Menu_Button* o wp_style_downbox->end(); @@ -1640,7 +2868,7 @@ Fl_Double_Window* make_widget_panel() { o->labelfont(1); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)textfont_cb); + o->callback((Fl_Callback*)cb_b); Fl_Group::current()->resizable(o); o->menu(fontmenu); } // Fl_Choice* o @@ -1651,15 +2879,15 @@ Fl_Double_Window* make_widget_panel() { o->step(1); o->value(14); o->textsize(11); - o->callback((Fl_Callback*)textsize_cb); + o->callback((Fl_Callback*)cb_c); } // Fl_Value_Input* o { w_textcolor = new Fl_Button(296, 115, 90, 20, "Text Color"); w_textcolor->tooltip("The value text color."); w_textcolor->labelsize(11); - w_textcolor->callback((Fl_Callback*)textcolor_cb); + w_textcolor->callback((Fl_Callback*)cb_w_textcolor); } // Fl_Button* w_textcolor { Fl_Menu_Button* o = new Fl_Menu_Button(386, 115, 18, 20); - o->callback((Fl_Callback*)textcolor_menu_cb); + o->callback((Fl_Callback*)cb_d); o->menu(colormenu); } // Fl_Menu_Button* o wp_style_text->end(); @@ -1676,7 +2904,7 @@ Fl_Double_Window* make_widget_panel() { o->maximum(128); o->step(1); o->textsize(11); - o->callback((Fl_Callback*)h_label_margin_cb); + o->callback((Fl_Callback*)cb_Horizontal); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Value_Input* o = new Fl_Value_Input(159, 150, 55, 20, "Vertical:"); @@ -1686,16 +2914,16 @@ Fl_Double_Window* make_widget_panel() { o->maximum(127); o->step(1); o->textsize(11); - o->callback((Fl_Callback*)v_label_margin_cb); + o->callback((Fl_Callback*)cb_Vertical); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o - { Fl_Value_Input* o = new Fl_Value_Input(219, 150, 55, 20, "Text to Image:"); + { Fl_Value_Input* o = new Fl_Value_Input(219, 150, 55, 20, "Image Gap:"); o->tooltip("Gap between label image and text in pixels"); o->labelsize(11); o->maximum(255); o->step(1); o->textsize(11); - o->callback((Fl_Callback*)image_spacing_cb); + o->callback((Fl_Callback*)cb_Image); o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); } // Fl_Value_Input* o { Fl_Box* o = new Fl_Box(281, 150, 60, 20); @@ -1709,7 +2937,7 @@ Fl_Double_Window* make_widget_panel() { o->tooltip("use compact box types for closely set buttons"); o->selection_color((Fl_Color)1); o->labelsize(11); - o->callback((Fl_Callback*)compact_cb); + o->callback((Fl_Callback*)cb_Compact); } // Fl_Light_Button* o { Fl_Box* o = new Fl_Box(195, 205, 40, 40); o->labelsize(11); @@ -1733,7 +2961,7 @@ Fl_Double_Window* make_widget_panel() { o->labelsize(11); o->textfont(4); o->textsize(11); - o->callback((Fl_Callback*)subclass_cb, (void*)(4)); + o->callback((Fl_Callback*)cb_e, (void*)(4)); Fl_Group::current()->resizable(o); } // Fl_Input* o { Fl_Choice* o = new Fl_Choice(267, 40, 138, 20); @@ -1742,7 +2970,7 @@ Fl_Double_Window* make_widget_panel() { o->down_box(FL_BORDER_BOX); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)subtype_cb); + o->callback((Fl_Callback*)cb_f); } // Fl_Choice* o wp_cpp_class->end(); } // Fl_Group* wp_cpp_class @@ -1756,7 +2984,7 @@ Fl_Double_Window* make_widget_panel() { o->labelfont(1); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)name_cb); + o->callback((Fl_Callback*)cb_10); Fl_Group::current()->resizable(o); } // Fl_Input* o { Fl_Choice* o = new Fl_Choice(330, 65, 75, 20); @@ -1764,7 +2992,7 @@ Fl_Double_Window* make_widget_panel() { o->down_box(FL_BORDER_BOX); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)name_public_member_cb); + o->callback((Fl_Callback*)cb_11); o->when(FL_WHEN_CHANGED); o->menu(menu_2); } // Fl_Choice* o @@ -1786,31 +3014,31 @@ Fl_Double_Window* make_widget_panel() { v_input[0]->labelsize(11); v_input[0]->textfont(4); v_input[0]->textsize(11); - v_input[0]->callback((Fl_Callback*)v_input_cb, (void*)(0)); + v_input[0]->callback((Fl_Callback*)cb_v_input, (void*)(0)); } // Fl_Input* v_input[0] { v_input[1] = new Fl_Input(95, 110, 310, 20); v_input[1]->tooltip("Extra initialization code for the widget."); v_input[1]->labelsize(11); v_input[1]->textfont(4); v_input[1]->textsize(11); - v_input[1]->callback((Fl_Callback*)v_input_cb, (void*)(1)); + v_input[1]->callback((Fl_Callback*)cb_v_input1, (void*)(1)); } // Fl_Input* v_input[1] { v_input[2] = new Fl_Input(95, 130, 310, 20); v_input[2]->tooltip("Extra initialization code for the widget."); v_input[2]->labelsize(11); v_input[2]->textfont(4); v_input[2]->textsize(11); - v_input[2]->callback((Fl_Callback*)v_input_cb, (void*)(2)); + v_input[2]->callback((Fl_Callback*)cb_v_input2, (void*)(2)); } // Fl_Input* v_input[2] { v_input[3] = new Fl_Input(95, 150, 310, 20); v_input[3]->tooltip("Extra initialization code for the widget."); v_input[3]->labelsize(11); v_input[3]->textfont(4); v_input[3]->textsize(11); - v_input[3]->callback((Fl_Callback*)v_input_cb, (void*)(3)); + v_input[3]->callback((Fl_Callback*)cb_v_input3, (void*)(3)); } // Fl_Input* v_input[3] { Fl_Tile* o = new Fl_Tile(95, 175, 310, 130); - o->callback((Fl_Callback*)cb_1); + o->callback((Fl_Callback*)cb_12); { Fl_Group* o = new Fl_Group(95, 175, 310, 48); o->box(FL_FLAT_BOX); { wComment = new Fl_Text_Editor(95, 175, 310, 45, "Comment:"); @@ -1822,11 +3050,11 @@ Fl_Double_Window* make_widget_panel() { wComment->textfont(6); wComment->textsize(11); wComment->textcolor((Fl_Color)59); + wComment->callback((Fl_Callback*)cb_wComment); wComment->align(Fl_Align(FL_ALIGN_LEFT)); wComment->when(FL_WHEN_CHANGED); Fl_Group::current()->resizable(wComment); wComment->buffer(new Fl_Text_Buffer()); - wComment->callback((Fl_Callback*)comment_cb); } // Fl_Text_Editor* wComment o->end(); } // Fl_Group* o @@ -1844,7 +3072,7 @@ Fl_Double_Window* make_widget_panel() { wCallback->labelcolor(FL_FOREGROUND_COLOR); wCallback->textfont(4); wCallback->textsize(11); - wCallback->callback((Fl_Callback*)callback_cb); + wCallback->callback((Fl_Callback*)cb_wCallback); wCallback->align(Fl_Align(FL_ALIGN_LEFT)); wCallback->when(FL_WHEN_RELEASE); Fl_Group::current()->resizable(wCallback); @@ -1865,7 +3093,7 @@ Fl_Double_Window* make_widget_panel() { o->labelsize(11); o->textfont(4); o->textsize(11); - o->callback((Fl_Callback*)user_data_cb); + o->callback((Fl_Callback*)cb_13); Fl_Group::current()->resizable(o); } // Fl_Input* o { Fl_Menu_Button* o = new Fl_Menu_Button(260, 310, 145, 20, "When"); @@ -1875,7 +3103,7 @@ Fl_Double_Window* make_widget_panel() { o->labelfont(1); o->labelsize(11); o->textsize(11); - o->callback((Fl_Callback*)when_cb); + o->callback((Fl_Callback*)cb_When); o->when(FL_WHEN_CHANGED); o->menu(whenmenu); } // Fl_Menu_Button* o @@ -1892,7 +3120,7 @@ Fl_Double_Window* make_widget_panel() { o->labelsize(11); o->textfont(4); o->textsize(11); - o->callback((Fl_Callback*)user_data_type_cb); + o->callback((Fl_Callback*)cb_14); Fl_Group::current()->resizable(o); o->menu(menu_4); } // Fl_Input_Choice* o @@ -1906,550 +3134,34 @@ Fl_Double_Window* make_widget_panel() { } // Fl_Group* o wp_cpp_tab->end(); } // Fl_Group* wp_cpp_tab - { widget_tab_grid_child = new Fl_Group(10, 30, 400, 330, "Grid Child"); - widget_tab_grid_child->labelsize(11); - widget_tab_grid_child->callback((Fl_Callback*)propagate_load); - widget_tab_grid_child->hide(); - { Fl_Group* o = new Fl_Group(95, 60, 315, 20, "Location:"); - o->box(FL_FLAT_BOX); - o->labelfont(1); - o->labelsize(11); - o->callback((Fl_Callback*)propagate_load); - o->align(Fl_Align(FL_ALIGN_LEFT)); - { widget_grid_row_input = new fld::widget::Formula_Input(95, 60, 40, 20, "Row:"); - widget_grid_row_input->box(FL_DOWN_BOX); - widget_grid_row_input->color(FL_BACKGROUND2_COLOR); - widget_grid_row_input->selection_color(FL_SELECTION_COLOR); - widget_grid_row_input->labeltype(FL_NORMAL_LABEL); - widget_grid_row_input->labelfont(0); - widget_grid_row_input->labelsize(11); - widget_grid_row_input->labelcolor(FL_FOREGROUND_COLOR); - widget_grid_row_input->textsize(11); - widget_grid_row_input->callback((Fl_Callback*)grid_set_row_cb); - widget_grid_row_input->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - widget_grid_row_input->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* widget_grid_row_input - { Fl_Group* o = new Fl_Group(135, 60, 30, 20); - { Fl_Button* o = new Fl_Button(135, 60, 15, 20, "-"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)grid_dec_row_cb); - o->clear_visible_focus(); - } // Fl_Button* o - { Fl_Button* o = new Fl_Button(150, 60, 15, 20, "+"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)grid_inc_row_cb); - o->clear_visible_focus(); - } // Fl_Button* o - o->end(); - } // Fl_Group* o - { widget_grid_col_input = new fld::widget::Formula_Input(175, 60, 40, 20, "Column:"); - widget_grid_col_input->box(FL_DOWN_BOX); - widget_grid_col_input->color(FL_BACKGROUND2_COLOR); - widget_grid_col_input->selection_color(FL_SELECTION_COLOR); - widget_grid_col_input->labeltype(FL_NORMAL_LABEL); - widget_grid_col_input->labelfont(0); - widget_grid_col_input->labelsize(11); - widget_grid_col_input->labelcolor(FL_FOREGROUND_COLOR); - widget_grid_col_input->textsize(11); - widget_grid_col_input->callback((Fl_Callback*)grid_set_col_cb); - widget_grid_col_input->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - widget_grid_col_input->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* widget_grid_col_input - { Fl_Group* o = new Fl_Group(215, 60, 30, 20); - { Fl_Button* o = new Fl_Button(215, 60, 15, 20, "-"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)grid_dec_col_cb); - o->clear_visible_focus(); - } // Fl_Button* o - { Fl_Button* o = new Fl_Button(230, 60, 15, 20, "+"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)grid_inc_col_cb); - o->clear_visible_focus(); - } // Fl_Button* o - o->end(); - } // Fl_Group* o - { Fl_Box* o = new Fl_Box(395, 60, 1, 20); - o->hide(); - Fl_Group::current()->resizable(o); - } // Fl_Box* o - { widget_grid_transient = new Fl_Box(250, 60, 80, 20, "TRANSIENT"); - widget_grid_transient->labelsize(11); - widget_grid_transient->labelcolor((Fl_Color)1); - widget_grid_transient->callback((Fl_Callback*)cb_widget_grid_transient); - } // Fl_Box* widget_grid_transient - { widget_grid_unlinked = new Fl_Box(250, 60, 80, 20, "UNLINKED"); - widget_grid_unlinked->labelsize(11); - widget_grid_unlinked->labelcolor((Fl_Color)1); - widget_grid_unlinked->hide(); - } // Fl_Box* widget_grid_unlinked - o->end(); - } // Fl_Group* o - { wp_gridc_align = new Fl_Group(95, 100, 315, 20, "Align:"); - wp_gridc_align->labelfont(1); - wp_gridc_align->labelsize(11); - wp_gridc_align->callback((Fl_Callback*)propagate_load); - wp_gridc_align->align(Fl_Align(FL_ALIGN_LEFT)); - { Fl_Choice* o = new Fl_Choice(95, 100, 115, 20, "Horizontal"); - o->down_box(FL_BORDER_BOX); - o->labelsize(11); - o->textsize(11); - o->callback((Fl_Callback*)grid_align_horizontal_cb); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - o->menu(menu_Horizontal); - } // Fl_Choice* o - { Fl_Choice* o = new Fl_Choice(215, 100, 115, 20, "Vertical"); - o->down_box(FL_BORDER_BOX); - o->labelsize(11); - o->textsize(11); - o->callback((Fl_Callback*)grid_align_vertical_cb); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - o->menu(menu_Vertical); - } // Fl_Choice* o - { Fl_Box* o = new Fl_Box(395, 100, 1, 20); - o->hide(); - Fl_Group::current()->resizable(o); - } // Fl_Box* o - wp_gridc_align->end(); - } // Fl_Group* wp_gridc_align - { wp_gridc_size = new Fl_Group(95, 135, 315, 20, "Min. Size:"); - wp_gridc_size->labelfont(1); - wp_gridc_size->labelsize(11); - wp_gridc_size->callback((Fl_Callback*)propagate_load); - wp_gridc_size->align(Fl_Align(FL_ALIGN_LEFT)); - { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(95, 135, 55, 20, "Width:"); - o->box(FL_DOWN_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->selection_color(FL_SELECTION_COLOR); - o->labeltype(FL_NORMAL_LABEL); - o->labelfont(0); - o->labelsize(11); - o->labelcolor(FL_FOREGROUND_COLOR); - o->textsize(11); - o->callback((Fl_Callback*)grid_set_min_wdt_cb); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - o->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* o - { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(155, 135, 55, 20, "Height:"); - o->box(FL_DOWN_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->selection_color(FL_SELECTION_COLOR); - o->labeltype(FL_NORMAL_LABEL); - o->labelfont(0); - o->labelsize(11); - o->labelcolor(FL_FOREGROUND_COLOR); - o->textsize(11); - o->callback((Fl_Callback*)grid_set_min_hgt_cb); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - o->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* o - { Fl_Box* o = new Fl_Box(395, 135, 1, 20); - o->hide(); - Fl_Group::current()->resizable(o); - } // Fl_Box* o - wp_gridc_size->end(); - } // Fl_Group* wp_gridc_size - { Fl_Group* o = new Fl_Group(95, 170, 315, 20, "Span:"); - o->labelfont(1); - o->labelsize(11); - o->callback((Fl_Callback*)propagate_load); - o->align(Fl_Align(FL_ALIGN_LEFT)); - { widget_grid_rowspan_input = new fld::widget::Formula_Input(95, 170, 40, 20, "Row Span:"); - widget_grid_rowspan_input->box(FL_DOWN_BOX); - widget_grid_rowspan_input->color(FL_BACKGROUND2_COLOR); - widget_grid_rowspan_input->selection_color(FL_SELECTION_COLOR); - widget_grid_rowspan_input->labeltype(FL_NORMAL_LABEL); - widget_grid_rowspan_input->labelfont(0); - widget_grid_rowspan_input->labelsize(11); - widget_grid_rowspan_input->labelcolor(FL_FOREGROUND_COLOR); - widget_grid_rowspan_input->textsize(11); - widget_grid_rowspan_input->callback((Fl_Callback*)grid_set_rowspan_cb); - widget_grid_rowspan_input->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - widget_grid_rowspan_input->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* widget_grid_rowspan_input - { Fl_Group* o = new Fl_Group(135, 170, 30, 20); - { Fl_Button* o = new Fl_Button(135, 170, 15, 20, "-"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)grid_dec_rowspan_cb); - o->clear_visible_focus(); - } // Fl_Button* o - { Fl_Button* o = new Fl_Button(150, 170, 15, 20, "+"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)grid_inc_rowspan_cb); - o->clear_visible_focus(); - } // Fl_Button* o - o->end(); - } // Fl_Group* o - { widget_grid_colspan_input = new fld::widget::Formula_Input(175, 170, 40, 20, "Col. Span:"); - widget_grid_colspan_input->box(FL_DOWN_BOX); - widget_grid_colspan_input->color(FL_BACKGROUND2_COLOR); - widget_grid_colspan_input->selection_color(FL_SELECTION_COLOR); - widget_grid_colspan_input->labeltype(FL_NORMAL_LABEL); - widget_grid_colspan_input->labelfont(0); - widget_grid_colspan_input->labelsize(11); - widget_grid_colspan_input->labelcolor(FL_FOREGROUND_COLOR); - widget_grid_colspan_input->textsize(11); - widget_grid_colspan_input->callback((Fl_Callback*)grid_set_colspan_cb); - widget_grid_colspan_input->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - widget_grid_colspan_input->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* widget_grid_colspan_input - { Fl_Group* o = new Fl_Group(215, 170, 30, 20); - { Fl_Button* o = new Fl_Button(215, 170, 15, 20, "-"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)grid_dec_colspan_cb); - o->clear_visible_focus(); - } // Fl_Button* o - { Fl_Button* o = new Fl_Button(230, 170, 15, 20, "+"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)grid_inc_colspan_cb); - o->clear_visible_focus(); - } // Fl_Button* o - o->end(); - } // Fl_Group* o - { Fl_Box* o = new Fl_Box(395, 170, 1, 20); - o->hide(); - Fl_Group::current()->resizable(o); - } // Fl_Box* o - o->end(); - } // Fl_Group* o - { Fl_Box* o = new Fl_Box(95, 350, 300, 5); - o->labelsize(11); - o->hide(); - Fl_Group::current()->resizable(o); - } // Fl_Box* o - widget_tab_grid_child->end(); - } // Fl_Group* widget_tab_grid_child - { widget_tab_grid = new Fl_Group(10, 30, 400, 330, "Grid"); + { widget_tab_grid = new Grid_Tab(10, 30, 400, 330, "Grid"); + widget_tab_grid->box(FL_NO_BOX); + widget_tab_grid->color(FL_BACKGROUND_COLOR); + widget_tab_grid->selection_color(FL_BACKGROUND_COLOR); + widget_tab_grid->labeltype(FL_NORMAL_LABEL); + widget_tab_grid->labelfont(0); widget_tab_grid->labelsize(11); - widget_tab_grid->callback((Fl_Callback*)propagate_load); + widget_tab_grid->labelcolor(FL_FOREGROUND_COLOR); + widget_tab_grid->callback((Fl_Callback*)cb_widget_tab_grid); + widget_tab_grid->align(Fl_Align(FL_ALIGN_TOP)); + widget_tab_grid->when(FL_WHEN_RELEASE); widget_tab_grid->hide(); - { Fl_Group* o = new Fl_Group(95, 60, 315, 20, "Grid Layout:"); - o->labelfont(1); - o->labelsize(11); - o->callback((Fl_Callback*)propagate_load); - o->align(Fl_Align(FL_ALIGN_LEFT)); - { widget_grid_rows = new fld::widget::Formula_Input(95, 60, 40, 20, "Rows:"); - widget_grid_rows->tooltip("Number of horizontal rows in the Grid group"); - widget_grid_rows->box(FL_DOWN_BOX); - widget_grid_rows->color(FL_BACKGROUND2_COLOR); - widget_grid_rows->selection_color(FL_SELECTION_COLOR); - widget_grid_rows->labeltype(FL_NORMAL_LABEL); - widget_grid_rows->labelfont(0); - widget_grid_rows->labelsize(11); - widget_grid_rows->labelcolor(FL_FOREGROUND_COLOR); - widget_grid_rows->textsize(11); - widget_grid_rows->callback((Fl_Callback*)cb_widget_grid_rows); - widget_grid_rows->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - widget_grid_rows->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* widget_grid_rows - { Fl_Group* o = new Fl_Group(135, 60, 30, 20); - { Fl_Button* o = new Fl_Button(135, 60, 15, 20, "-"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)cb_2); - o->clear_visible_focus(); - } // Fl_Button* o - { Fl_Button* o = new Fl_Button(150, 60, 15, 20, "+"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)cb_3); - o->clear_visible_focus(); - } // Fl_Button* o - o->end(); - } // Fl_Group* o - { widget_grid_cols = new fld::widget::Formula_Input(175, 60, 40, 20, "Columns:"); - widget_grid_cols->tooltip("Number of vertical columns in the Grid group"); - widget_grid_cols->box(FL_DOWN_BOX); - widget_grid_cols->color(FL_BACKGROUND2_COLOR); - widget_grid_cols->selection_color(FL_SELECTION_COLOR); - widget_grid_cols->labeltype(FL_NORMAL_LABEL); - widget_grid_cols->labelfont(0); - widget_grid_cols->labelsize(11); - widget_grid_cols->labelcolor(FL_FOREGROUND_COLOR); - widget_grid_cols->textsize(11); - widget_grid_cols->callback((Fl_Callback*)cb_widget_grid_cols); - widget_grid_cols->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - widget_grid_cols->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* widget_grid_cols - { Fl_Group* o = new Fl_Group(215, 60, 30, 20); - { Fl_Button* o = new Fl_Button(215, 60, 15, 20, "-"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)cb_4); - o->clear_visible_focus(); - } // Fl_Button* o - { Fl_Button* o = new Fl_Button(230, 60, 15, 20, "+"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)cb_5); - o->clear_visible_focus(); - } // Fl_Button* o - o->end(); - } // Fl_Group* o - { Fl_Box* o = new Fl_Box(396, 60, 0, 20); - Fl_Group::current()->resizable(o); - } // Fl_Box* o - o->end(); - } // Fl_Group* o - { wp_grid_margin = new Fl_Group(95, 100, 315, 20, "Margins:"); - wp_grid_margin->labelfont(1); - wp_grid_margin->labelsize(11); - wp_grid_margin->callback((Fl_Callback*)propagate_load); - wp_grid_margin->align(Fl_Align(FL_ALIGN_LEFT)); - { Fl_Value_Input* o = new Fl_Value_Input(95, 100, 55, 20, "Left:"); - o->tooltip("Left margin in group."); - o->labelsize(11); - o->maximum(1000); - o->step(1); - o->textsize(11); - o->callback((Fl_Callback*)cb_Left); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - } // Fl_Value_Input* o - { Fl_Value_Input* o = new Fl_Value_Input(155, 100, 55, 20, "Top:"); - o->tooltip("Top margin in group."); - o->labelsize(11); - o->maximum(1000); - o->step(1); - o->textsize(11); - o->callback((Fl_Callback*)cb_Top); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - } // Fl_Value_Input* o - { Fl_Value_Input* o = new Fl_Value_Input(215, 100, 55, 20, "Right:"); - o->tooltip("Right margin in group."); - o->labelsize(11); - o->maximum(1000); - o->step(1); - o->textsize(11); - o->callback((Fl_Callback*)cb_Right); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - } // Fl_Value_Input* o - { Fl_Value_Input* o = new Fl_Value_Input(275, 100, 55, 20, "Bottom:"); - o->tooltip("Bottom margin in group."); - o->labelsize(11); - o->maximum(1000); - o->step(1); - o->textsize(11); - o->callback((Fl_Callback*)cb_Bottom); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - } // Fl_Value_Input* o - { Fl_Box* o = new Fl_Box(396, 100, 0, 20); - Fl_Group::current()->resizable(o); - } // Fl_Box* o - wp_grid_margin->end(); - } // Fl_Group* wp_grid_margin - { wp_grid_gaps = new Fl_Group(95, 135, 315, 20, "Gaps:"); - wp_grid_gaps->labelfont(1); - wp_grid_gaps->labelsize(11); - wp_grid_gaps->callback((Fl_Callback*)propagate_load); - wp_grid_gaps->align(Fl_Align(FL_ALIGN_LEFT)); - { Fl_Value_Input* o = new Fl_Value_Input(95, 135, 55, 20, "Row:"); - o->tooltip("Gap between children."); - o->labelsize(11); - o->maximum(1000); - o->step(1); - o->textsize(11); - o->callback((Fl_Callback*)cb_Row); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - } // Fl_Value_Input* o - { Fl_Value_Input* o = new Fl_Value_Input(155, 135, 55, 20, "Col:"); - o->tooltip("Gap between children."); - o->labelsize(11); - o->maximum(1000); - o->step(1); - o->textsize(11); - o->callback((Fl_Callback*)cb_Col); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - } // Fl_Value_Input* o - { Fl_Box* o = new Fl_Box(396, 135, 0, 20); - Fl_Group::current()->resizable(o); - } // Fl_Box* o - wp_grid_gaps->end(); - } // Fl_Group* wp_grid_gaps - { Fl_Group* o = new Fl_Group(95, 175, 315, 20, "Row:"); - o->labelfont(1); - o->labelsize(11); - o->callback((Fl_Callback*)cb_Row1); - o->align(Fl_Align(FL_ALIGN_LEFT)); - { widget_grid_curr_row = new fld::widget::Formula_Input(95, 175, 40, 20, "Index"); - widget_grid_curr_row->box(FL_DOWN_BOX); - widget_grid_curr_row->color(FL_BACKGROUND2_COLOR); - widget_grid_curr_row->selection_color(FL_SELECTION_COLOR); - widget_grid_curr_row->labeltype(FL_NORMAL_LABEL); - widget_grid_curr_row->labelfont(0); - widget_grid_curr_row->labelsize(11); - widget_grid_curr_row->labelcolor(FL_FOREGROUND_COLOR); - widget_grid_curr_row->textsize(11); - widget_grid_curr_row->callback((Fl_Callback*)cb_widget_grid_curr_row); - widget_grid_curr_row->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - widget_grid_curr_row->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* widget_grid_curr_row - { Fl_Group* o = new Fl_Group(135, 175, 30, 20); - o->callback((Fl_Callback*)propagate_load); - { Fl_Button* o = new Fl_Button(135, 175, 15, 20, "-"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)cb_6); - o->clear_visible_focus(); - } // Fl_Button* o - { Fl_Button* o = new Fl_Button(150, 175, 15, 20, "+"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)cb_7); - o->clear_visible_focus(); - } // Fl_Button* o - o->end(); - } // Fl_Group* o - { Fl_Box* o = new Fl_Box(165, 175, 15, 20, ":"); - o->labelsize(11); - } // Fl_Box* o - { widget_grid_curr_row_attributes = new Fl_Group(180, 175, 175, 20); - widget_grid_curr_row_attributes->callback((Fl_Callback*)propagate_load); - { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(180, 175, 55, 20, "Height:"); - o->box(FL_DOWN_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->selection_color(FL_SELECTION_COLOR); - o->labeltype(FL_NORMAL_LABEL); - o->labelfont(0); - o->labelsize(11); - o->labelcolor(FL_FOREGROUND_COLOR); - o->textsize(11); - o->callback((Fl_Callback*)cb_Height); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - o->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* o - { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(240, 175, 55, 20, "Weight:"); - o->box(FL_DOWN_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->selection_color(FL_SELECTION_COLOR); - o->labeltype(FL_NORMAL_LABEL); - o->labelfont(0); - o->labelsize(11); - o->labelcolor(FL_FOREGROUND_COLOR); - o->textsize(11); - o->callback((Fl_Callback*)cb_Weight); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - o->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* o - { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(300, 175, 55, 20, "Gap:"); - o->box(FL_DOWN_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->selection_color(FL_SELECTION_COLOR); - o->labeltype(FL_NORMAL_LABEL); - o->labelfont(0); - o->labelsize(11); - o->labelcolor(FL_FOREGROUND_COLOR); - o->textsize(11); - o->callback((Fl_Callback*)cb_Gap); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - o->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* o - widget_grid_curr_row_attributes->end(); - } // Fl_Group* widget_grid_curr_row_attributes - { Fl_Box* o = new Fl_Box(400, 175, 1, 20); - o->hide(); - Fl_Group::current()->resizable(o); - } // Fl_Box* o - o->end(); - } // Fl_Group* o - { Fl_Group* o = new Fl_Group(95, 210, 315, 20, "Column:"); - o->labelfont(1); - o->labelsize(11); - o->callback((Fl_Callback*)propagate_load); - o->align(Fl_Align(FL_ALIGN_LEFT)); - { widget_grid_curr_col = new fld::widget::Formula_Input(95, 210, 40, 20, "Index"); - widget_grid_curr_col->box(FL_DOWN_BOX); - widget_grid_curr_col->color(FL_BACKGROUND2_COLOR); - widget_grid_curr_col->selection_color(FL_SELECTION_COLOR); - widget_grid_curr_col->labeltype(FL_NORMAL_LABEL); - widget_grid_curr_col->labelfont(0); - widget_grid_curr_col->labelsize(11); - widget_grid_curr_col->labelcolor(FL_FOREGROUND_COLOR); - widget_grid_curr_col->textsize(11); - widget_grid_curr_col->callback((Fl_Callback*)cb_widget_grid_curr_col); - widget_grid_curr_col->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - widget_grid_curr_col->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* widget_grid_curr_col - { Fl_Group* o = new Fl_Group(135, 210, 30, 20); - { Fl_Button* o = new Fl_Button(135, 210, 15, 20, "-"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)cb_8); - o->clear_visible_focus(); - } // Fl_Button* o - { Fl_Button* o = new Fl_Button(150, 210, 15, 20, "+"); - o->compact(1); - o->labelsize(11); - o->callback((Fl_Callback*)cb_9); - o->clear_visible_focus(); - } // Fl_Button* o - o->end(); - } // Fl_Group* o - { Fl_Box* o = new Fl_Box(165, 210, 15, 20, ":"); - o->labelsize(11); - } // Fl_Box* o - { widget_grid_curr_col_attributes = new Fl_Group(180, 210, 175, 20); - widget_grid_curr_col_attributes->callback((Fl_Callback*)propagate_load); - { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(180, 210, 55, 20, "Width:"); - o->box(FL_DOWN_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->selection_color(FL_SELECTION_COLOR); - o->labeltype(FL_NORMAL_LABEL); - o->labelfont(0); - o->labelsize(11); - o->labelcolor(FL_FOREGROUND_COLOR); - o->textsize(11); - o->callback((Fl_Callback*)cb_Width); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - o->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* o - { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(240, 210, 55, 20, "Weight:"); - o->box(FL_DOWN_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->selection_color(FL_SELECTION_COLOR); - o->labeltype(FL_NORMAL_LABEL); - o->labelfont(0); - o->labelsize(11); - o->labelcolor(FL_FOREGROUND_COLOR); - o->textsize(11); - o->callback((Fl_Callback*)cb_Weight1); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - o->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* o - { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(300, 210, 55, 20, "Gap:"); - o->box(FL_DOWN_BOX); - o->color(FL_BACKGROUND2_COLOR); - o->selection_color(FL_SELECTION_COLOR); - o->labeltype(FL_NORMAL_LABEL); - o->labelfont(0); - o->labelsize(11); - o->labelcolor(FL_FOREGROUND_COLOR); - o->textsize(11); - o->callback((Fl_Callback*)cb_Gap1); - o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); - o->when(FL_WHEN_RELEASE); - } // fld::widget::Formula_Input* o - widget_grid_curr_col_attributes->end(); - } // Fl_Group* widget_grid_curr_col_attributes - { Fl_Box* o = new Fl_Box(400, 210, 1, 20); - o->hide(); - Fl_Group::current()->resizable(o); - } // Fl_Box* o - o->end(); - } // Fl_Group* o - { Fl_Box* o = new Fl_Box(95, 350, 300, 5); - o->labelsize(11); - o->hide(); - Fl_Group::current()->resizable(o); - } // Fl_Box* o widget_tab_grid->end(); - } // Fl_Group* widget_tab_grid + } // Grid_Tab* widget_tab_grid + { widget_tab_grid_child = new Grid_Child_Tab(10, 30, 400, 330, "Grid Child"); + widget_tab_grid_child->box(FL_NO_BOX); + widget_tab_grid_child->color(FL_BACKGROUND_COLOR); + widget_tab_grid_child->selection_color(FL_BACKGROUND_COLOR); + widget_tab_grid_child->labeltype(FL_NORMAL_LABEL); + widget_tab_grid_child->labelfont(0); + widget_tab_grid_child->labelsize(11); + widget_tab_grid_child->labelcolor(FL_FOREGROUND_COLOR); + widget_tab_grid_child->callback((Fl_Callback*)cb_widget_tab_grid_child); + widget_tab_grid_child->align(Fl_Align(FL_ALIGN_TOP)); + widget_tab_grid_child->when(FL_WHEN_RELEASE); + widget_tab_grid_child->hide(); + widget_tab_grid_child->end(); + } // Grid_Child_Tab* widget_tab_grid_child o->show(); widget_tabs->end(); Fl_Group::current()->resizable(widget_tabs); diff --git a/fluid/panels/widget_panel.fl b/fluid/panels/widget_panel.fl index 517271e08..026616714 100644 --- a/fluid/panels/widget_panel.fl +++ b/fluid/panels/widget_panel.fl @@ -25,26 +25,76 @@ comment {// } {in_source in_header } -decl {\#include "app/undo.h"} {private global +decl {\#include "panels/widget_panel/Grid_Child_Tab.h"} {public global } -decl {\#include "nodes/Fl_Widget_Type.h"} {private global +decl {\#include "panels/widget_panel/Grid_Tab.h"} {public global } -decl {\#include "nodes/Fl_Grid_Type.h"} {private global +decl {\#include "widgets/Formula_Input.h"} {public global } -decl {\#include "widgets/Formula_Input.h"} {selected public global +decl {class Fl_Flex;} {public global +} + +decl {\#include "Fluid.h"} {private global +} + +decl {\#include "app/Snap_Action.h"} {private global +} + +decl {\#include "app/Image_Asset.h"} {private global +} + +decl {\#include "proj/undo.h"} {private global +} + +decl {\#include "nodes/Window_Node.h"} {private global +} + +decl {\#include "nodes/Grid_Node.h"} {private global +} + +decl {\#include } {private global } decl {\#include } {private global } -decl {extern void set_modflag(int mf, int mfc=-1);} {private local +decl {\#include } {private global +} + +decl {\#include } {private global +} + +decl {\#include } {private global +} + +decl {\#define ZERO_ENTRY 1000} {private global +} + +decl {extern const char* when_symbol_name(int n); +extern void set_whenmenu(int n); +extern void redraw_browser(); +const char *c_check(const char *c, int type=0); +extern Fl_Color fl_show_colormap(Fl_Color oldcol); +extern void labelcolor_common(Fl_Color c); +extern void color_common(Fl_Color c); +extern void color2_common(Fl_Color c); +extern void textcolor_common(Fl_Color c); +extern int widget_i; +extern fld::widget::Formula_Input_Vars widget_vars[];} {private global +} + +decl {extern int numselected; +extern Fl_Menu_Item boxmenu[];} {private global +} + +decl {extern int haderror;} {private global } Function {make_image_panel()} { - comment {Create a panel for editing widget image data} open + comment {Create a panel for editing widget image data} } { Fl_Window image_panel_window { label {Image Options} @@ -87,14 +137,14 @@ Function {make_image_panel()} { Fl_Input image_panel_imagew { label {Width:} callback {if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { o->value(current_widget->scale_image_w_); } } else { int mod = 0; - for (Fl_Type *t = Fl_Type::first; t; t = t->next) { + for (Node *t = Fluid.proj.tree.first; t; t = t->next) { if (t->selected && t->is_widget()) { - Fl_Widget_Type* wt = ((Fl_Widget_Type*)t); + Widget_Node* wt = ((Widget_Node*)t); wt->scale_image_w_ = o->value(); Fl_Image *img = wt->o->image(); if (img) { @@ -109,7 +159,7 @@ Function {make_image_panel()} { mod = 1; } } - if (mod) set_modflag(1); + if (mod) Fluid.proj.set_modflag(1); }} tooltip {Scale image to this width in pixel units} xywh {75 75 55 20} labelsize 11 align 5 textsize 11 class {fld::widget::Formula_Input} @@ -117,14 +167,14 @@ Function {make_image_panel()} { Fl_Input image_panel_imageh { label {Height:} callback {if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { o->value(current_widget->scale_image_h_); } } else { int mod = 0; - for (Fl_Type *t = Fl_Type::first; t; t = t->next) { + for (Node *t = Fluid.proj.tree.first; t; t = t->next) { if (t->selected && t->is_widget()) { - Fl_Widget_Type* wt = ((Fl_Widget_Type*)t); + Widget_Node* wt = ((Widget_Node*)t); wt->scale_image_h_ = o->value(); Fl_Image *img = wt->o->image(); if (img) { @@ -139,7 +189,7 @@ Function {make_image_panel()} { mod = 1; } } - if (mod) set_modflag(1); + if (mod) Fluid.proj.set_modflag(1); }} tooltip {Scale image to this height in pixel units} xywh {135 75 55 20} labelsize 11 align 5 textsize 11 class {fld::widget::Formula_Input} @@ -165,12 +215,40 @@ Function {make_image_panel()} { } Fl_Check_Button {} { label {convert to raw pixel data} - callback compress_image_cb + callback {if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { + o->activate(); + o->value(!current_widget->compress_image_); + } else { + o->deactivate(); + } +} else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->compress_image_ = !o->value(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {if unchecked, keep the image in its original format and store the data as is; if checked, convert the image and store it as uncompressed RGB or grayscale pixel data} xywh {75 100 170 20} down_box DOWN_BOX labelsize 11 } Fl_Check_Button {} { label {bind to widget} - callback bind_image_cb + callback {if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { + o->activate(); + o->value(current_widget->bind_image_); + } else { + o->deactivate(); + } +} else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->bind_image_ = o->value(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {bind the image to the widget, so it will be deleted automatically} xywh {75 120 170 20} down_box DOWN_BOX labelsize 11 hotspot } } @@ -209,14 +287,14 @@ Function {make_image_panel()} { Fl_Input image_panel_deimagew { label {Width:} callback {if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { o->value(current_widget->scale_deimage_w_); } } else { int mod = 0; - for (Fl_Type *t = Fl_Type::first; t; t = t->next) { + for (Node *t = Fluid.proj.tree.first; t; t = t->next) { if (t->selected && t->is_widget()) { - Fl_Widget_Type* wt = ((Fl_Widget_Type*)t); + Widget_Node* wt = ((Widget_Node*)t); wt->scale_deimage_w_ = o->value(); Fl_Image *img = wt->o->deimage(); if (img) { @@ -231,7 +309,7 @@ Function {make_image_panel()} { mod = 1; } } - if (mod) set_modflag(1); + if (mod) Fluid.proj.set_modflag(1); }} tooltip {Scale image to this width in pixel units} xywh {75 215 55 20} labelsize 11 align 5 textsize 11 class {fld::widget::Formula_Input} @@ -239,14 +317,14 @@ Function {make_image_panel()} { Fl_Input image_panel_deimageh { label {Height:} callback {if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { o->value(current_widget->scale_deimage_h_); } } else { int mod = 0; - for (Fl_Type *t = Fl_Type::first; t; t = t->next) { + for (Node *t = Fluid.proj.tree.first; t; t = t->next) { if (t->selected && t->is_widget()) { - Fl_Widget_Type* wt = ((Fl_Widget_Type*)t); + Widget_Node* wt = ((Widget_Node*)t); wt->scale_deimage_h_ = o->value(); Fl_Image *img = wt->o->deimage(); if (img) { @@ -261,7 +339,7 @@ Function {make_image_panel()} { mod = 1; } } - if (mod) set_modflag(1); + if (mod) Fluid.proj.set_modflag(1); }} tooltip {Scale image to this height in pixel units} xywh {135 215 55 20} labelsize 11 align 5 textsize 11 class {fld::widget::Formula_Input} @@ -287,12 +365,40 @@ Function {make_image_panel()} { } Fl_Check_Button {} { label {convert to raw pixel data} - callback compress_deimage_cb + callback {if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { + o->activate(); + o->value(!current_widget->compress_deimage_); + } else { + o->deactivate(); + } +} else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->compress_deimage_ = !o->value(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {if unchecked, keep the image in its original format and store the data as is; if checked, convert the image and store it as uncompressed RGB or grayscale pixel data} xywh {75 240 170 20} down_box DOWN_BOX labelsize 11 } Fl_Check_Button {} { label {bind to widget} - callback bind_deimage_cb + callback {if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { + o->activate(); + o->value(current_widget->bind_deimage_); + } else { + o->deactivate(); + } +} else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->bind_deimage_ = o->value(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {bind the image to the widget, so it will be deleted automatically} xywh {75 260 170 20} down_box DOWN_BOX labelsize 11 } } @@ -305,7 +411,7 @@ Function {make_image_panel()} { } } -Function {run_image_panel()} {open return_type void +Function {run_image_panel()} {return_type void } { code {if (!image_panel_window) make_image_panel(); @@ -324,7 +430,30 @@ if (g) Fl_Shared_Image *img = (Fl_Shared_Image*)image_panel_data->user_data(); if (img) { img->release(); - image_panel_data->user_data(NULL); + image_panel_data->user_data(nullptr); +}} {} +} + +Function {flex_margin_cb(Fl_Value_Input* i, void* v, void (*load_margin)(Fl_Flex*,Fl_Value_Input*), int (*update_margin)(Fl_Flex*,int))} {return_type void +} { + code {if (v == LOAD) { + if (current_widget->is_a(Type::Flex)) { + load_margin((Fl_Flex*)current_widget->o, i); + } +} else { + int mod = 0; + int new_value = (int)i->value(); + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_a(Type::Flex)) { + Flex_Node* q = (Flex_Node*)o; + Fl_Flex* w = (Fl_Flex*)q->o; + if (update_margin(w, new_value)) { + w->layout(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); }} {} } @@ -343,7 +472,7 @@ Function {make_widget_panel()} { } { Fl_Group wp_gui_tab { label GUI - callback propagate_load open + callback propagate_load open selected xywh {10 30 400 330} labelsize 11 when 0 resizable } { Fl_Group {} { @@ -369,12 +498,40 @@ Use Ctrl-J for newlines.} xywh {95 40 190 20} labelfont 1 labelsize 11 when 15 t xywh {95 65 309 20} labelfont 1 labelsize 11 align 4 } { Fl_Input widget_image_input { - callback image_cb + callback {if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { + o->activate(); + o->value(((Widget_Node*)current_widget)->image_name()); + } else o->deactivate(); +} else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->image_name(o->value()); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The active image for the widget.} xywh {95 65 200 20} labelfont 1 labelsize 11 textsize 11 resizable } Fl_Button {} { label {Browse...} - callback image_browse_cb + callback {if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) + o->activate(); + else + o->deactivate(); +} else { + int mod = 0; + Image_Asset *image_asset = ui_find_image(widget_image_input->value()); + if (image_asset) { + widget_image_input->value(image_asset->filename()); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->image_name(image_asset->filename()); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +}} tooltip {Click to choose the active image.} xywh {295 65 89 20} labelsize 11 align 256 } Fl_Button {} { @@ -391,12 +548,40 @@ Use Ctrl-J for newlines.} xywh {95 40 190 20} labelfont 1 labelsize 11 when 15 t xywh {95 90 309 20} labelfont 1 labelsize 11 align 4 } { Fl_Input widget_deimage_input { - callback inactive_cb + callback {if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) { + o->activate(); + o->value(((Widget_Node*)current_widget)->inactive_name()); + } else o->deactivate(); +} else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->inactive_name(o->value()); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The inactive image for the widget.} xywh {95 90 200 20} labelfont 1 labelsize 11 textsize 11 resizable } Fl_Button {} { label {Browse...} - callback inactive_browse_cb + callback {if (v == LOAD) { + if (current_widget->is_widget() && !current_widget->is_a(Type::Window)) + o->activate(); + else + o->deactivate(); +} else { + int mod = 0; + Image_Asset *image_asset = ui_find_image(widget_deimage_input->value()); + if (image_asset) { + widget_deimage_input->value(image_asset->filename()); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->inactive_name(image_asset->filename()); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + } +}} tooltip {Click to choose the inactive image.} xywh {295 90 89 20} labelsize 11 } } @@ -568,12 +753,38 @@ Use Ctrl-J for newlines.} xywh {95 40 190 20} labelfont 1 labelsize 11 when 15 t } Fl_Group {} { label {Position:} - callback position_group_cb open + callback position_group_cb xywh {95 150 314 20} labelfont 1 labelsize 11 align 4 } { Fl_Input widget_x_input { label {X:} - callback x_cb + callback {if (v == LOAD) { + if (current_widget->is_true_widget()) { + o->value(((Widget_Node *)current_widget)->o->x()); + o->activate(); + } else o->deactivate(); +} else { + Fluid.proj.undo.checkpoint(); + widget_i = 0; + int mod = 0; + int v = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + Fl_Widget *w = q->o; + o->variables(widget_vars, q); + v = o->value(); + w->resize(v, w->y(), w->w(), w->h()); + if (w->window()) w->window()->redraw(); + widget_i++; + mod = 1; + } + } + if (mod) { + Fluid.proj.set_modflag(1); + o->value(v); // change the displayed value to the result of the last + // calculation. Keep the formula if it was not used. + } +}} tooltip {The X position of the widget as a number or formula. Formulas can be simple math, including the variables x, px, sx, cx, and i} xywh {95 150 55 20} labelsize 11 align 5 textsize 11 @@ -581,7 +792,32 @@ x, px, sx, cx, and i} xywh {95 150 55 20} labelsize 11 align 5 textsize 11 } Fl_Input widget_y_input { label {Y:} - callback y_cb + callback {if (v == LOAD) { + if (current_widget->is_true_widget()) { + o->value(((Widget_Node *)current_widget)->o->y()); + o->activate(); + } else o->deactivate(); +} else { + Fluid.proj.undo.checkpoint(); + widget_i = 0; + int mod = 0; + int v = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + Fl_Widget *w = q->o; + o->variables(widget_vars, q); + v = o->value(); + w->resize(w->x(), v, w->w(), w->h()); + if (w->window()) w->window()->redraw(); + widget_i++; + mod = 1; + } + } + if (mod) { + Fluid.proj.set_modflag(1); + o->value(v); + } +}} tooltip {The Y position of the widget as a number or formula. Formulas can be simple math, including the variables y, py, sy, cy, and i} xywh {155 150 55 20} labelsize 11 align 5 textsize 11 @@ -589,7 +825,32 @@ y, py, sy, cy, and i} xywh {155 150 55 20} labelsize 11 align 5 textsize 11 } Fl_Input widget_w_input { label {Width:} - callback w_cb + callback {if (v == LOAD) { + if (current_widget->is_true_widget()) { + o->value(((Widget_Node *)current_widget)->o->w()); + o->activate(); + } else o->deactivate(); +} else { + Fluid.proj.undo.checkpoint(); + widget_i = 0; + int mod = 0; + int v = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + Fl_Widget *w = q->o; + o->variables(widget_vars, q); + v = o->value(); + w->resize(w->x(), w->y(), v, w->h()); + if (w->window()) w->window()->redraw(); + widget_i++; + mod = 1; + } + } + if (mod) { + Fluid.proj.set_modflag(1); + o->value(v); + } +}} tooltip {The width of the widget as a number or formula. Formulas can be simple math, including the variables w, pw, sw, cw, and i} xywh {215 150 55 20} labelsize 11 align 5 textsize 11 @@ -597,7 +858,32 @@ w, pw, sw, cw, and i} xywh {215 150 55 20} labelsize 11 align 5 textsize 11 } Fl_Input widget_h_input { label {Height:} - callback h_cb + callback {if (v == LOAD) { + if (current_widget->is_true_widget()) { + o->value(((Widget_Node *)current_widget)->o->h()); + o->activate(); + } else o->deactivate(); +} else { + Fluid.proj.undo.checkpoint(); + widget_i = 0; + int mod = 0; + int v = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + Fl_Widget *w = q->o; + o->variables(widget_vars, q); + v = o->value(); + w->resize(w->x(), w->y(), w->w(), v); + if (w->window()) w->window()->redraw(); + widget_i++; + mod = 1; + } + } + if (mod) { + Fluid.proj.set_modflag(1); + o->value(v); + } +}} tooltip {The height of the widget as a number or formula. Formulas can be simple math, including the variables h, ph, sh, ch, and i} xywh {275 150 55 20} labelsize 11 align 5 textsize 11 @@ -605,7 +891,25 @@ h, ph, sh, ch, and i} xywh {275 150 55 20} labelsize 11 align 5 textsize 11 } Fl_Choice {} { label {Children:} - callback wc_relative_cb open + callback {if (v == LOAD) { + if (current_widget->is_a(Type::Widget_Class)) { + o->show(); + o->value(((Widget_Class_Node *)current_widget)->wc_relative); + } else { + o->hide(); + } +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Widget_Class)) { + Widget_Class_Node *t = (Widget_Class_Node *)q; + t->wc_relative = o->value(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {When instantiating a widget class, the children can either be fixed in their original position, automatically be repositioned, or both repsositioned and resized to fit the container.} xywh {335 150 64 20} down_box BORDER_BOX labelsize 11 align 5 textsize 11 } { MenuItem {} { @@ -627,18 +931,84 @@ h, ph, sh, ch, and i} xywh {275 150 55 20} labelsize 11 align 5 textsize 11 } Fl_Group wp_gui_flexp { label {Flex Parent:} - callback flex_size_group_cb + callback {if (v == LOAD) { + if (Flex_Node::parent_is_flex(current_widget)) { + o->show(); + propagate_load(o, v); + } else { + o->hide(); + } +}} comment {This group is only visible if the parent is an Fl_Flex widget} xywh {95 150 314 20} labelfont 1 labelsize 11 align 4 hide } { Fl_Value_Input widget_flex_size { label {Size:} - callback flex_size_cb + callback {if (v == LOAD) { + if (Flex_Node::parent_is_flex(current_widget)) { + o->value(Flex_Node::size(current_widget)); + } +} else { + int mod = 0; + int new_size = (int)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (Flex_Node::parent_is_flex(q)) { + Fl_Widget* w = (Fl_Widget*)q->o; + Fl_Flex* f = (Fl_Flex*)((Flex_Node*)q->parent)->o; + int was_fixed = f->fixed(w); + if (new_size==0) { + if (was_fixed) { + f->fixed(w, 0); + f->layout(); + widget_flex_fixed->value(0); + mod = 1; + } + } else { + int old_size = Flex_Node::size(q); + if (old_size!=new_size || !was_fixed) { + f->fixed(w, new_size); + f->layout(); + widget_flex_fixed->value(1); + mod = 1; + } + } + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {Fixed Width or Height for a horizontal or vertical Fl_Flex Parent.} xywh {95 150 55 20} labelsize 11 align 5 textsize 11 } Fl_Check_Button widget_flex_fixed { label fixed - callback flex_fixed_cb + callback {if (v == LOAD) { + if (Flex_Node::parent_is_flex(current_widget)) { + o->value(Flex_Node::is_fixed(current_widget)); + } +} else { + int mod = 0; + int new_fixed = (int)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (Flex_Node::parent_is_flex(q)) { + Fl_Widget* w = q->o; + Fl_Flex* f = (Fl_Flex*)((Flex_Node*)q->parent)->o; + int was_fixed = f->fixed(w); + if (new_fixed==0) { + if (was_fixed) { + f->fixed(w, 0); + f->layout(); + mod = 1; + } + } else { + if (!was_fixed) { + f->fixed(w, Flex_Node::size(q)); + f->layout(); + mod = 1; + } + } + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {If checked, the size of the widget stays fixed.} xywh {155 150 55 20} down_box DOWN_BOX labelsize 11 } Fl_Box {} { @@ -647,32 +1017,169 @@ h, ph, sh, ch, and i} xywh {275 150 55 20} labelsize 11 align 5 textsize 11 } Fl_Group wp_gui_values { label {Values:} - callback values_group_cb open + callback {if (v == LOAD) { + if ( current_widget->is_a(Type::Flex) + || current_widget->is_a(Type::Grid) + || current_widget->is_a(Type::Window)) + { + o->hide(); + } else { + o->show(); + propagate_load(o, v); + } +}} open xywh {95 185 300 20} labelfont 1 labelsize 11 align 4 } { Fl_Value_Input {} { label {Size:} - callback slider_size_cb + callback {if (v == LOAD) { + if (!current_widget->is_a(Type::Slider)) {o->deactivate(); return;} + o->activate(); + o->value(((Fl_Slider*)(current_widget->o))->slider_size()); +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + double n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Slider)) { + ((Fl_Slider*)(q->o))->slider_size(n); + q->o->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The size of the slider.} xywh {95 185 55 20} labelsize 11 align 5 textsize 11 } Fl_Value_Input {} { label {Minimum:} - callback min_cb + callback {if (v == LOAD) { + if (current_widget->is_a(Type::Valuator_)) { + o->activate(); + o->value(((Fl_Valuator*)(current_widget->o))->minimum()); + } else if (current_widget->is_a(Type::Spinner)) { + o->activate(); + o->value(((Fl_Spinner*)(current_widget->o))->minimum()); + } else { + o->deactivate(); + return; + } +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + double n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Valuator_)) { + ((Fl_Valuator*)(q->o))->minimum(n); + q->o->redraw(); + mod = 1; + } else if (q->is_a(Type::Spinner)) { + ((Fl_Spinner*)(q->o))->minimum(n); + q->o->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The minimum value of the widget.} xywh {155 185 55 20} labelsize 11 align 5 textsize 11 } Fl_Value_Input {} { label {Maximum:} - callback max_cb + callback {if (v == LOAD) { + if (current_widget->is_a(Type::Valuator_)) { + o->activate(); + o->value(((Fl_Valuator*)(current_widget->o))->maximum()); + } else if (current_widget->is_a(Type::Spinner)) { + o->activate(); + o->value(((Fl_Spinner*)(current_widget->o))->maximum()); + } else { + o->deactivate(); + return; + } +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + double n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Valuator_)) { + ((Fl_Valuator*)(q->o))->maximum(n); + q->o->redraw(); + mod = 1; + } else if (q->is_a(Type::Spinner)) { + ((Fl_Spinner*)(q->o))->maximum(n); + q->o->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The maximum value of the widget.} xywh {215 185 55 20} labelsize 11 align 5 value 1 textsize 11 } Fl_Value_Input {} { label {Step:} - callback step_cb + callback {if (v == LOAD) { + if (current_widget->is_a(Type::Valuator_)) { + o->activate(); + o->value(((Fl_Valuator*)(current_widget->o))->step()); + } else if (current_widget->is_a(Type::Spinner)) { + o->activate(); + o->value(((Fl_Spinner*)(current_widget->o))->step()); + } else { + o->deactivate(); + return; + } +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + double n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Valuator_)) { + ((Fl_Valuator*)(q->o))->step(n); + q->o->redraw(); + mod = 1; + } else if (q->is_a(Type::Spinner)) { + ((Fl_Spinner*)(q->o))->step(n); + q->o->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The resolution of the widget value.} xywh {275 185 55 20} labelsize 11 align 5 textsize 11 } Fl_Value_Input {} { label {Value:} - callback value_cb + callback {if (v == LOAD) { + if (current_widget->is_a(Type::Valuator_)) { + o->activate(); + o->value(((Fl_Valuator*)(current_widget->o))->value()); + } else if (current_widget->is_button()) { + o->activate(); + o->value(((Fl_Button*)(current_widget->o))->value()); + } else if (current_widget->is_a(Type::Spinner)) { + o->activate(); + o->value(((Fl_Spinner*)(current_widget->o))->value()); + } else + o->deactivate(); +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + double n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Valuator_)) { + ((Fl_Valuator*)(q->o))->value(n); + mod = 1; + } else if (q->is_button()) { + ((Fl_Button*)(q->o))->value(n != 0); + if (q->is_a(Type::Menu_Item)) q->redraw(); + mod = 1; + } else if (q->is_a(Type::Spinner)) { + ((Fl_Spinner*)(q->o))->value(n); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The current widget value.} xywh {335 185 55 20} labelsize 11 align 5 textsize 11 } Fl_Box {} { @@ -681,33 +1188,128 @@ h, ph, sh, ch, and i} xywh {275 150 55 20} labelsize 11 align 5 textsize 11 } Fl_Group wp_gui_margins { label {Margins:} - callback flex_margin_group_cb + callback {if (v == LOAD) { + if (current_widget->is_a(Type::Flex)) { + o->show(); + propagate_load(o, v); + } else { + o->hide(); + } +}} comment {This group is only visible for Fl_Flex widgets} xywh {95 185 300 20} labelfont 1 labelsize 11 align 4 hide } { Fl_Value_Input {} { label {Left:} - callback flex_margin_left_cb + callback {flex_margin_cb(o, v, + [](Fl_Flex *w, Fl_Value_Input* i) -> void + { + int v; + w->margin(&v, nullptr, nullptr, nullptr); + i->value((double)v); + }, + [](Fl_Flex *w, int new_value) -> int + { + int l, t, r, b; + w->margin(&l, &t, &r, &b); + if (new_value!=l) { + w->margin(new_value, t, r, b); + return 1; + } else { + return 0; + } + } +);} tooltip {Left margin in group.} xywh {95 185 55 20} labelsize 11 align 5 textsize 11 } Fl_Value_Input {} { label {Top:} - callback flex_margin_top_cb + callback {flex_margin_cb(o, v, + [](Fl_Flex *w, Fl_Value_Input* i) -> void + { + int v; + w->margin(nullptr, &v, nullptr, nullptr); + i->value((double)v); + }, + [](Fl_Flex *w, int new_value) + { + int l, t, r, b; + w->margin(&l, &t, &r, &b); + if (new_value!=t) { + w->margin(l, new_value, r, b); + return 1; + } else { + return 0; + } + } +);} tooltip {Top margin in group.} xywh {155 185 55 20} labelsize 11 align 5 textsize 11 } Fl_Value_Input {} { label {Right:} - callback flex_margin_right_cb + callback {flex_margin_cb(o, v, + [](Fl_Flex *w, Fl_Value_Input* i) -> void + { + int v; + w->margin(nullptr, nullptr, &v, nullptr); + i->value((double)v); + }, + [](Fl_Flex *w, int new_value) -> int + { + int l, t, r, b; + w->margin(&l, &t, &r, &b); + if (new_value!=r) { + w->margin(l, t, new_value, b); + return 1; + } else { + return 0; + } + } +);} tooltip {Right margin in group.} xywh {215 185 55 20} labelsize 11 align 5 textsize 11 } Fl_Value_Input {} { label {Bottom:} - callback flex_margin_bottom_cb + callback {flex_margin_cb(o, v, + [](Fl_Flex *w, Fl_Value_Input* i) -> void + { + int v; + w->margin(nullptr, nullptr, nullptr, &v); + i->value((double)v); + }, + [](Fl_Flex *w, int new_value) -> int + { + int l, t, r, b; + w->margin(&l, &t, &r, &b); + if (new_value!=b) { + w->margin(l, t, r, new_value); + return 1; + } else { + return 0; + } + } +);} tooltip {Bottom margin in group.} xywh {275 185 55 20} labelsize 11 align 5 textsize 11 } Fl_Value_Input {} { label {Gap:} - callback flex_margin_gap_cb + callback {flex_margin_cb(o, v, + [](Fl_Flex *w, Fl_Value_Input* o) -> void + { + int v = w->gap(); + o->value((double)v); + }, + [](Fl_Flex *w, int new_value) -> int + { + int g = w->gap(); + if (new_value!=g) { + w->gap(new_value); + return 1; + } else { + return 0; + } + } +);} tooltip {Gap between children.} xywh {335 185 55 20} labelsize 11 align 5 textsize 11 } Fl_Box {} { @@ -716,35 +1318,126 @@ h, ph, sh, ch, and i} xywh {275 150 55 20} labelsize 11 align 5 textsize 11 } Fl_Group wp_gui_sizerange { label {Size Range:} - callback size_range_group_cb open + callback {if (v == LOAD) { + if (current_widget->is_a(Type::Window)) { + o->show(); + propagate_load(o, v); + } else { + o->hide(); + } +}} xywh {95 185 300 20} labelfont 1 labelsize 11 align 4 hide } { Fl_Value_Input {} { label {Minimum Size:} - callback min_w_cb + callback {if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) return; + o->value(((Window_Node*)current_widget)->sr_min_w); +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + int n = (int)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + ((Window_Node*)q)->sr_min_w = n; + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The size of the slider.} xywh {95 185 55 20} labelsize 11 align 5 maximum 2048 step 1 textsize 11 } Fl_Value_Input {} { - callback min_h_cb + callback {if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) return; + o->value(((Window_Node*)current_widget)->sr_min_h); +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + int n = (int)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + ((Window_Node*)q)->sr_min_h = n; + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The minimum value of the widget.} xywh {155 185 55 20} labelsize 11 align 5 maximum 2048 step 1 textsize 11 } Fl_Button {} { label set - callback set_min_size_cb + callback {if (v == LOAD) { +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + Window_Node *win = (Window_Node*)q; + win->sr_min_w = win->o->w(); + win->sr_min_h = win->o->h(); + mod = 1; + } + } + propagate_load(the_panel, LOAD); + if (mod) Fluid.proj.set_modflag(1); +}} xywh {215 185 25 20} labelsize 11 } Fl_Value_Input {} { label {Maximum Size:} - callback max_w_cb + callback {if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) return; + o->value(((Window_Node*)current_widget)->sr_max_w); +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + int n = (int)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + ((Window_Node*)q)->sr_max_w = n; + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The maximum value of the widget.} xywh {245 185 55 20} labelsize 11 align 5 maximum 2048 step 1 textsize 11 } Fl_Value_Input {} { - callback max_h_cb + callback {if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) return; + o->value(((Window_Node*)current_widget)->sr_max_h); +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + int n = (int)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + ((Window_Node*)q)->sr_max_h = n; + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The resolution of the widget value.} xywh {305 185 55 20} labelsize 11 align 5 maximum 2048 step 1 textsize 11 } Fl_Button {} { label set - callback set_max_size_cb + callback {if (v == LOAD) { +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + Window_Node *win = (Window_Node*)q; + win->sr_max_w = win->o->w(); + win->sr_max_h = win->o->h(); + mod = 1; + } + } + propagate_load(the_panel, LOAD); + if (mod) Fluid.proj.set_modflag(1); +}} xywh {365 185 25 20} labelsize 11 } Fl_Box {} { @@ -753,11 +1446,51 @@ h, ph, sh, ch, and i} xywh {275 150 55 20} labelsize 11 align 5 textsize 11 } Fl_Group {} { label {Shortcut:} - callback propagate_load open + callback propagate_load xywh {95 210 310 20} labelfont 1 labelsize 11 align 4 } { Fl_Button wp_gui_shortcut { - callback shortcut_in_cb + callback {if (v == LOAD) { + if (current_widget->is_button()) + o->value( ((Fl_Button*)(current_widget->o))->shortcut() ); + else if (current_widget->is_a(Type::Input)) + o->value( ((Fl_Input_*)(current_widget->o))->shortcut() ); + else if (current_widget->is_a(Type::Value_Input)) + o->value( ((Fl_Value_Input*)(current_widget->o))->shortcut() ); + else if (current_widget->is_a(Type::Text_Display)) + o->value( ((Fl_Text_Display*)(current_widget->o))->shortcut() ); + else { + o->hide(); + o->parent()->hide(); + return; + } + //i->default_value( i->value() ); // enable the "undo" capability of the shortcut button + o->show(); + o->parent()->show(); + o->redraw(); +} else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) + if (q->is_button()) { + Fl_Button* b = (Fl_Button*)(q->o); + if (b->shortcut() != (int)o->value()) mod = 1; + b->shortcut(o->value()); + if (q->is_a(Type::Menu_Item)) q->redraw(); + } else if (q->is_a(Type::Input)) { + Fl_Input_* b = (Fl_Input_*)(q->o); + if (b->shortcut() != (int)o->value()) mod = 1; + b->shortcut(o->value()); + } else if (q->is_a(Type::Value_Input)) { + Fl_Value_Input* b = (Fl_Value_Input*)(q->o); + if (b->shortcut() != (int)o->value()) mod = 1; + b->shortcut(o->value()); + } else if (q->is_a(Type::Text_Display)) { + Fl_Text_Display* b = (Fl_Text_Display*)(q->o); + if (b->shortcut() != (int)o->value()) mod = 1; + b->shortcut(o->value()); + } + if (mod) Fluid.proj.set_modflag(1); +}} comment {This is a special button that grabs keystrokes directly} tooltip {The shortcut key for the widget. Use 'Backspace' key to clear.} xywh {95 210 310 20} box DOWN_BOX color 7 selection_color 12 labelsize 11 when 1 @@ -772,22 +1505,67 @@ Use 'Backspace' key to clear.} xywh {95 210 310 20} box DOWN_BOX color 7 selecti } { Fl_Input {} { label {:} - callback xclass_cb + callback {if (v == LOAD) { + if (current_widget->is_a(Type::Window)) { + o->show(); + o->parent()->show(); + o->value(((Window_Node *)current_widget)->xclass); + } else { + o->hide(); + o->parent()->hide(); // hides the "X Class:" label as well + } +} else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Window)) { + mod = 1; + Window_Node *wt = (Window_Node *)q; + storestring(o->value(), wt->xclass); + ((Fl_Window*)(wt->o))->xclass(wt->xclass); + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The X resource class.} xywh {95 235 95 20} labelfont 1 labelsize 11 textsize 11 resizable } Fl_Light_Button {} { label Border - callback border_cb + callback {if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) {o->hide(); return;} + o->show(); + o->value(((Fl_Window*)(current_widget->o))->border()); +} else { + Fluid.proj.undo.checkpoint(); + ((Fl_Window*)(current_widget->o))->border(o->value()); + Fluid.proj.set_modflag(1); +}} tooltip {Add a border around the window.} xywh {195 235 60 20} selection_color 1 labelsize 11 } Fl_Light_Button {} { label Modal - callback modal_cb + callback {if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) {o->hide(); return;} + o->show(); + o->value(((Window_Node *)current_widget)->modal); +} else { + Fluid.proj.undo.checkpoint(); + ((Window_Node *)current_widget)->modal = o->value(); + Fluid.proj.set_modflag(1); +}} tooltip {Make the window modal.} xywh {260 235 55 20} selection_color 1 labelsize 11 } Fl_Light_Button {} { label Nonmodal - callback non_modal_cb + callback {if (v == LOAD) { + if (!current_widget->is_a(Type::Window)) {o->hide(); return;} + o->show(); + o->value(((Window_Node *)current_widget)->non_modal); +} else { + Fluid.proj.undo.checkpoint(); + ((Window_Node *)current_widget)->non_modal = o->value(); + Fluid.proj.set_modflag(1); +}} tooltip {Make the window non-modal.} xywh {320 235 75 20} selection_color 1 labelsize 11 align 148 } } @@ -798,22 +1576,96 @@ Use 'Backspace' key to clear.} xywh {95 210 310 20} box DOWN_BOX color 7 selecti } { Fl_Light_Button {} { label Visible - callback visible_cb + callback {if (v == LOAD) { + o->value(current_widget->o->visible()); + if (current_widget->is_a(Type::Window)) o->deactivate(); + else o->activate(); +} else { + int mod = 0; + int n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (!mod) { + mod = 1; + Fluid.proj.undo.checkpoint(); + } + n ? q->o->show() : q->o->hide(); + q->redraw(); + if (n && q->parent && q->parent->type_name()) { + if (q->parent->is_a(Type::Tabs)) { + ((Fl_Tabs *)q->o->parent())->value(q->o); + } else if (q->parent->is_a(Type::Wizard)) { + ((Fl_Wizard *)q->o->parent())->value(q->o); + } + } + } + if (mod) { + Fluid.proj.set_modflag(1); + redraw_browser(); + } +}} tooltip {Show the widget.} xywh {95 260 60 20} selection_color 1 labelsize 11 } Fl_Light_Button {} { label Active - callback active_cb + callback {if (v == LOAD) { + o->value(current_widget->o->active()); + if (current_widget->is_a(Type::Window)) o->deactivate(); + else o->activate(); +} else { + int mod = 0; + int n = o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (!mod) { + mod = 1; + Fluid.proj.undo.checkpoint(); + } + n ? q->o->activate() : q->o->deactivate(); + q->redraw(); + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {Activate the widget.} xywh {160 260 60 20} selection_color 1 labelsize 11 } Fl_Light_Button {} { label Resizable - callback resizable_cb - tooltip {Make the widget resizable.} xywh {225 260 75 20} selection_color 1 labelsize 11 when 1 + callback {if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {o->deactivate(); return;} + if (numselected > 1) {o->deactivate(); return;} + o->activate(); + o->value(current_widget->resizable()); +} else { + Fluid.proj.undo.checkpoint(); + current_widget->resizable(o->value()); + Fluid.proj.set_modflag(1); +}} + tooltip {Make the widget resizable.} xywh {225 260 75 20} selection_color 1 labelsize 11 when 1 } Fl_Light_Button {} { label Hotspot - callback hotspot_cb + callback {if (v == LOAD) { + if (numselected > 1) {o->deactivate(); return;} + if (current_widget->is_a(Type::Menu_Item)) o->label("divider"); + else o->label("hotspot"); + o->activate(); + o->value(current_widget->hotspot()); +} else { + Fluid.proj.undo.checkpoint(); + current_widget->hotspot(o->value()); + if (current_widget->is_a(Type::Menu_Item)) { + current_widget->redraw(); + return; + } + if (o->value()) { + Node *p = current_widget->parent; + if (!p || !p->is_widget()) return; + while (!p->is_a(Type::Window)) p = p->parent; + for (Node *q = p->next; q && q->level > p->level; q = q->next) { + if (q->is_widget() && q != current_widget) + ((Widget_Node*)q)->hotspot(0); + } + } + Fluid.proj.set_modflag(1); +}} tooltip {Center the window under this widget.} xywh {305 260 70 20} selection_color 1 labelsize 11 when 1 } Fl_Box {} { @@ -822,7 +1674,21 @@ Use 'Backspace' key to clear.} xywh {95 210 310 20} box DOWN_BOX color 7 selecti } Fl_Input wp_gui_tooltip { label {Tooltip:} - callback tooltip_cb + callback {if (v == LOAD) { + if (current_widget->is_widget()) { + o->activate(); + o->value(((Widget_Node*)current_widget)->tooltip()); + } else { + o->deactivate(); + } +} else { + int mod = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->tooltip(o->value()); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The tooltip text for the widget. Use Ctrl-J for newlines.} xywh {95 285 310 20} labelfont 1 labelsize 11 textsize 11 } @@ -841,22 +1707,69 @@ Use Ctrl-J for newlines.} xywh {95 285 310 20} labelfont 1 labelsize 11 textsize xywh {99 40 305 20} labelfont 1 labelsize 11 align 4 } { Fl_Choice {} { - callback labelfont_cb open + callback {if (v == LOAD) { + int n = current_widget->o->labelfont(); + if (n > 15) n = 0; + o->value(n); +} else { + int mod = 0; + int n = o->value(); + if (n <= 0) n = Fluid.proj.layout->labelfont; + if (n <= 0) n = FL_HELVETICA; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->o->labelfont(n); + q->redraw(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} open tooltip {The style of the label text.} xywh {99 40 148 20} box THIN_UP_BOX down_box BORDER_BOX labelfont 1 labelsize 11 textsize 11 resizable code0 {extern Fl_Menu_Item fontmenu[];} code1 {o->menu(fontmenu);} } {} Fl_Value_Input {} { - callback labelsize_cb + callback {int n; +if (v == LOAD) { + n = current_widget->o->labelsize(); +} else { + int mod = 0; + n = int(o->value()); + if (n <= 0) n = Fluid.proj.layout->labelsize; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->o->labelsize(n); + q->redraw(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +} +o->value(n);} tooltip {The size of the label text.} xywh {247 40 49 20} labelsize 11 maximum 100 step 1 value 14 textsize 11 } Fl_Button w_labelcolor { label {Label Color} - callback labelcolor_cb + callback {Fl_Color c = current_widget->o->labelcolor(); +if (v != LOAD) { + Fl_Color d = fl_show_colormap(c); + if (d == c) return; + c = d; + labelcolor_common(c); +} +o->color(c); +o->labelcolor(fl_contrast(FL_BLACK,c)); +o->redraw();} tooltip {The color of the label text.} xywh {296 40 90 20} labelsize 11 } Fl_Menu_Button {} { - callback labelcolor_menu_cb open + callback {Fl_Color c = current_widget->o->labelcolor(); +if (v != LOAD) { + Fl_Color d = (Fl_Color)(o->mvalue()->argument()); + if (d == c) return; + c = d; + labelcolor_common(c); + w_labelcolor->color(c); + w_labelcolor->labelcolor(fl_contrast(FL_BLACK,c)); + w_labelcolor->redraw(); +}} open xywh {386 40 18 20} code0 {extern Fl_Menu_Item colormenu[];} code1 {o->menu(colormenu);} @@ -868,18 +1781,62 @@ Use Ctrl-J for newlines.} xywh {95 285 310 20} labelfont 1 labelsize 11 textsize xywh {99 65 305 20} labelfont 1 labelsize 11 align 4 } { Fl_Choice {} { - callback box_cb open + callback {if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {o->deactivate(); return;} else o->activate(); + int n = current_widget->o->box(); + if (!n) n = ZERO_ENTRY; + for (int j = 0; j < 72 /*int(sizeof(boxmenu)/sizeof(*boxmenu))*/; j++) + if (boxmenu[j].argument() == n) {o->value(j); break;} +} else { + int mod = 0; + int m = o->value(); + int n = int(boxmenu[m].argument()); + if (!n) return; // should not happen + if (n == ZERO_ENTRY) n = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->o->box((Fl_Boxtype)n); + q->redraw(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} open tooltip {The "up" box of the widget.} xywh {100 65 196 20} box THIN_UP_BOX down_box BORDER_BOX labelfont 1 labelsize 11 textsize 11 resizable code0 {extern Fl_Menu_Item boxmenu[];} code1 {o->menu(boxmenu);} } {} Fl_Button w_color { label Color - callback color_cb + callback {Fl_Color c = current_widget->o->color(); +if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) { + o->deactivate(); + } else { + o->activate(); + } +} else { + Fl_Color d = fl_show_colormap(c); + if (d == c) return; + c = d; + color_common(c); +} +o->color(c); +o->labelcolor(fl_contrast(FL_BLACK,c)); +o->redraw();} tooltip {The background color of the widget.} xywh {296 65 90 20} labelsize 11 } Fl_Menu_Button {} { - callback color_menu_cb open + callback {Fl_Color c = current_widget->o->color(); +if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {o->deactivate(); return;} else o->activate(); +} else { + Fl_Color d = (Fl_Color)(o->mvalue()->argument()); + if (d == c) return; + c = d; + color_common(c); + w_color->color(c); + w_color->labelcolor(fl_contrast(FL_BLACK,c)); + w_color->redraw(); +}} open xywh {386 65 18 20} code0 {extern Fl_Menu_Item colormenu[];} code1 {o->menu(colormenu);} @@ -891,18 +1848,83 @@ Use Ctrl-J for newlines.} xywh {95 285 310 20} labelfont 1 labelsize 11 textsize xywh {99 90 305 20} labelfont 1 labelsize 11 align 4 } { Fl_Choice {} { - callback down_box_cb open + callback {if (v == LOAD) { + int n; + if (current_widget->is_a(Type::Button)) + n = ((Fl_Button*)(current_widget->o))->down_box(); + else if (current_widget->is_a(Type::Input_Choice)) + n = ((Fl_Input_Choice*)(current_widget->o))->down_box(); + else if (current_widget->is_a(Type::Menu_Manager_)) + n = ((Fl_Menu_*)(current_widget->o))->down_box(); + else { + o->deactivate(); return; + } + o->activate(); + if (!n) n = ZERO_ENTRY; + for (int j = 0; j < 72 /*int(sizeof(boxmenu)/sizeof(*boxmenu))*/; j++) + if (boxmenu[j].argument() == n) {o->value(j); break;} +} else { + int mod = 0; + int m = o->value(); + int n = int(boxmenu[m].argument()); + if (!n) return; // should not happen + if (n == ZERO_ENTRY) n = 0; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Button)) { + ((Fl_Button*)(q->o))->down_box((Fl_Boxtype)n); + if (((Fl_Button*)(q->o))->value()) q->redraw(); + } else if (q->is_a(Type::Input_Choice)) { + ((Fl_Input_Choice*)(q->o))->down_box((Fl_Boxtype)n); + } else if (q->is_a(Type::Menu_Manager_)) { + ((Fl_Menu_*)(q->o))->down_box((Fl_Boxtype)n); + } + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} open tooltip {The "down" box of the widget.} xywh {99 90 197 20} box THIN_UP_BOX down_box BORDER_BOX labelfont 1 labelsize 11 textsize 11 resizable code0 {extern Fl_Menu_Item boxmenu[];} code1 {o->menu(boxmenu);} } {} Fl_Button w_selectcolor { label {Select Color} - callback color2_cb + callback {Fl_Color c = current_widget->o->selection_color(); +if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) { + o->deactivate(); + return; + } else { + o->activate(); + } +} else { + Fl_Color d = fl_show_colormap(c); + if (d == c) return; + c = d; + color2_common(c); +} +o->color(c); +o->labelcolor(fl_contrast(FL_BLACK,c)); +o->redraw();} tooltip {The selection color of the widget.} xywh {296 90 90 20} labelsize 11 } Fl_Menu_Button {} { - callback color2_menu_cb open + callback {Fl_Color c = current_widget->o->selection_color(); +if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) { + o->deactivate(); + return; + } else { + o->activate(); + } +} else { + Fl_Color d = (Fl_Color)(o->mvalue()->argument()); + if (d == c) return; + c = d; + color2_common(c); + w_selectcolor->color(c); + w_selectcolor->labelcolor(fl_contrast(FL_BLACK,c)); + w_selectcolor->redraw(); +}} open xywh {386 90 18 20} code0 {extern Fl_Menu_Item colormenu[];} code1 {o->menu(colormenu);} @@ -914,22 +1936,86 @@ Use Ctrl-J for newlines.} xywh {95 285 310 20} labelfont 1 labelsize 11 textsize xywh {99 115 305 20} labelfont 1 labelsize 11 align 4 } { Fl_Choice {} { - callback textfont_cb open + callback {Fl_Font n; int s; Fl_Color c; +if (v == LOAD) { + if (!current_widget->textstuff(0,n,s,c)) {o->deactivate(); return;} + o->activate(); + if (n > 15) n = FL_HELVETICA; + o->value(n); +} else { + int mod = 0; + n = (Fl_Font)o->value(); + if (n <= 0) n = Fluid.proj.layout->textfont; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->textstuff(1,n,s,c); + q->o->redraw(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} open tooltip {The value text style.} xywh {99 115 148 20} box DOWN_BOX down_box BORDER_BOX labelfont 1 labelsize 11 textsize 11 resizable code0 {extern Fl_Menu_Item fontmenu[];} code1 {o->menu(fontmenu);} } {} Fl_Value_Input {} { - callback textsize_cb + callback {Fl_Font n; int s; Fl_Color c; +if (v == LOAD) { + if (!current_widget->textstuff(0,n,s,c)) {o->deactivate(); return;} + o->activate(); +} else { + int mod = 0; + s = int(o->value()); + if (s <= 0) s = Fluid.proj.layout->textsize; + if (s <= 0) s = Fluid.proj.layout->labelsize; + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->textstuff(2,n,s,c); + q->o->redraw(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +} +o->value(s);} tooltip {The value text size.} xywh {247 115 49 20} labelsize 11 maximum 100 step 1 value 14 textsize 11 } Fl_Button w_textcolor { label {Text Color} - callback textcolor_cb + callback {Fl_Font n; int s; Fl_Color c; +if (v == LOAD) { + if (!current_widget->textstuff(0,n,s,c)) { + o->deactivate(); + return; + } + o->activate(); +} else { + c = o->color(); + Fl_Color d = fl_show_colormap(c); + if (d == c) return; + c = d; + textcolor_common(c); +} +o->color(c); +o->labelcolor(fl_contrast(FL_BLACK,c)); +o->redraw();} tooltip {The value text color.} xywh {296 115 90 20} labelsize 11 } Fl_Menu_Button {} { - callback textcolor_menu_cb open + callback {Fl_Font n; int s; Fl_Color c; +if (v == LOAD) { + if (!current_widget->textstuff(0,n,s,c)) { + o->deactivate(); + return; + } + o->activate(); +} else { + c = o->color(); + Fl_Color d = (Fl_Color)(o->mvalue()->argument()); + if (d == c) return; + c = d; + textcolor_common(c); + w_textcolor->color(c); + w_textcolor->labelcolor(fl_contrast(FL_BLACK,c)); + w_textcolor->redraw(); +}} open xywh {386 115 18 20} code0 {extern Fl_Menu_Item colormenu[];} code1 {o->menu(colormenu);} @@ -942,17 +2028,89 @@ Use Ctrl-J for newlines.} xywh {95 285 310 20} labelfont 1 labelsize 11 textsize } { Fl_Value_Input {} { label {Horizontal:} - callback h_label_margin_cb + callback {int s; +if (v == LOAD) { + if (!current_widget->is_true_widget()) { + o->deactivate(); + o->value(0); + } else { + o->activate(); + o->value(((Widget_Node*)current_widget)->o->horizontal_label_margin()); + } +} else { + int mod = 0; + s = int(o->value()); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + if (q->o->horizontal_label_margin() != s) { + q->o->horizontal_label_margin(s); + if (!(q->o->align() & FL_ALIGN_INSIDE) && q->o->window()) + q->o->window()->damage(FL_DAMAGE_EXPOSE); // outside labels + q->o->redraw(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {Spacing between label and the horizontally aligned side of the widget.} xywh {99 150 55 20} labelsize 11 align 5 minimum -127 maximum 128 step 1 textsize 11 } Fl_Value_Input {} { label {Vertical:} - callback v_label_margin_cb + callback {int s; +if (v == LOAD) { + if (!current_widget->is_true_widget()) { + o->deactivate(); + o->value(0); + } else { + o->activate(); + o->value(((Widget_Node*)current_widget)->o->vertical_label_margin()); + } +} else { + int mod = 0; + s = int(o->value()); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + if (q->o->vertical_label_margin() != s) { + q->o->vertical_label_margin(s); + if (!(q->o->align() & FL_ALIGN_INSIDE) && q->o->window()) + q->o->window()->damage(FL_DAMAGE_EXPOSE); // outside labels + q->o->redraw(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {Spacing between label and the vertically aligned side of the widget.} xywh {159 150 55 20} labelsize 11 align 5 minimum -127 maximum 127 step 1 textsize 11 } Fl_Value_Input {} { - label {Text to Image:} - callback image_spacing_cb + label {Image Gap:} + callback {int s; +if (v == LOAD) { + if (!current_widget->is_true_widget()) { + o->deactivate(); + o->value(0); + } else { + o->activate(); + o->value(((Widget_Node*)current_widget)->o->label_image_spacing()); + } +} else { + int mod = 0; + s = int(o->value()); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_true_widget()) { + if (q->o->label_image_spacing() != s) { + q->o->label_image_spacing(s); + if (!(q->o->align() & FL_ALIGN_INSIDE) && q->o->window()) + q->o->window()->damage(FL_DAMAGE_EXPOSE); // outside labels + q->o->redraw(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {Gap between label image and text in pixels} xywh {219 150 55 20} labelsize 11 align 5 maximum 255 step 1 textsize 11 } Fl_Box {} { @@ -961,7 +2119,33 @@ Use Ctrl-J for newlines.} xywh {95 285 310 20} labelfont 1 labelsize 11 textsize } Fl_Light_Button {} { label Compact - callback compact_cb + callback {if (v == LOAD) { + uchar n; + if (current_widget->is_a(Type::Button) && !current_widget->is_a(Type::Menu_Item)) { + n = ((Fl_Button*)(current_widget->o))->compact(); + o->value(n); + o->show(); + } else { + o->hide(); + } +} else { + int mod = 0; + uchar n = (uchar)o->value(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->is_a(Type::Button) && !q->is_a(Type::Menu_Item)) { + uchar v = ((Fl_Button*)(q->o))->compact(); + if (n != v) { + if (!mod) { + mod = 1; + Fluid.proj.undo.checkpoint(); + } + ((Fl_Button*)(q->o))->compact(n); + q->redraw(); + } + } + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {use compact box types for closely set buttons} xywh {99 175 90 20} selection_color 1 labelsize 11 } Fl_Box {} { @@ -970,7 +2154,7 @@ Use Ctrl-J for newlines.} xywh {95 285 310 20} labelfont 1 labelsize 11 textsize } Fl_Group wp_cpp_tab { label {C++} - callback propagate_load open + callback propagate_load xywh {10 30 400 330} labelsize 11 when 0 hide } { Fl_Group wp_cpp_class { @@ -980,25 +2164,120 @@ Use Ctrl-J for newlines.} xywh {95 285 310 20} labelfont 1 labelsize 11 textsize } { Fl_Input {} { user_data 4 - callback subclass_cb + callback {if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) { + o->deactivate(); + } else { + o->activate(); + o->value(current_widget->subclass()); + } + } else { + int mod = 0; + const char *c = o->value(); + for (Widget_Node *t: Fluid.proj.tree.all_selected_widgets()) { + t->subclass(c); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + }} tooltip {The widget subclass.} xywh {95 40 172 20} labelfont 1 labelsize 11 textfont 4 textsize 11 resizable } Fl_Choice {} { - callback subtype_cb open + callback {static Fl_Menu_Item empty_type_menu[] = { + {"Normal",0,nullptr,(void*)nullptr}, + {nullptr}}; + + if (v == LOAD) { + Fl_Menu_Item* m = current_widget->subtypes(); + if (!m) { + o->menu(empty_type_menu); + o->value(0); + o->deactivate(); + } else { + o->menu(m); + int j; + for (j = 0;; j++) { + if (!m[j].text) {j = 0; break;} + if (current_widget->is_a(Type::Spinner)) { + if (m[j].argument() == ((Fl_Spinner*)current_widget->o)->type()) break; + } else { + if (m[j].argument() == current_widget->o->type()) break; + } + } + o->value(j); + o->activate(); + } + o->redraw(); + } else { + int mod = 0; + int n = int(o->mvalue()->argument()); + Fl_Menu_Item* m = current_widget->subtypes(); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + if (q->subtypes()==m) { + if (q->is_a(Type::Spinner)) + ((Fl_Spinner*)q->o)->type(n); + else if (q->is_a(Type::Flex)) + ((Flex_Node*)q)->change_subtype_to(n); + else + q->o->type(n); + q->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + }} open tooltip {The widget subtype.} xywh {267 40 138 20} box THIN_UP_BOX down_box BORDER_BOX labelsize 11 textsize 11 } {} } Fl_Group wp_cpp_name { label {Name:} - callback propagate_load + callback propagate_load open xywh {95 65 310 20} labelfont 1 labelsize 11 align 4 } { Fl_Input {} { - callback name_cb + callback {if (v == LOAD) { + static char buf[1024]; + if (numselected != 1) { + snprintf(buf, sizeof(buf), "Widget Properties (%d widgets)", numselected); + o->hide(); + } else { + o->value(current_widget->name()); + o->show(); + snprintf(buf, sizeof(buf), "%s Properties", current_widget->title()); + } + + the_panel->label(buf); + } else { + if (numselected == 1) { + current_widget->name(o->value()); + // I don't update window title, as it probably is being closed + // and wm2 (a window manager) barfs if you retitle and then + // hide a window: + // ((Fl_Window*)(o->parent()->parent()->parent()))->label(current_widget->title()); + } + }} tooltip {The name of the widget.} xywh {95 65 235 20} labelfont 1 labelsize 11 textsize 11 resizable } Fl_Choice {} { - callback name_public_member_cb open + callback {if (v == LOAD) { + o->value(current_widget->public_); + if (current_widget->is_in_class()) o->show(); else o->hide(); +} else { + int mod = 0; + for (Widget_Node *w: Fluid.proj.tree.all_selected_widgets()) { + if (w->is_in_class()) { + w->public_ = o->value(); + } else { + // if this is not in a class, it can be only private or public + w->public_ = (o->value()>0); + } + mod = 1; + } + if (mod) { + Fluid.proj.set_modflag(1); + redraw_browser(); + } +}} open tooltip {Change member access attribute.} xywh {330 65 75 20} down_box BORDER_BOX labelsize 11 when 1 textsize 11 } { MenuItem {} { @@ -1036,22 +2315,35 @@ Use Ctrl-J for newlines.} xywh {95 285 310 20} labelfont 1 labelsize 11 textsize Fl_Input {v_input[0]} { label {Extra Code:} user_data 0 - callback v_input_cb + callback {int n = fl_int(o->user_data()); + if (v == LOAD) { + o->value(current_widget->extra_code(n)); + } else { + int mod = 0; + const char *c = o->value(); + const char *d = c_check(c&&c[0]=='\#' ? c+1 : c); + if (d) {fl_message("Error in %s: %s",o->label(),d); haderror = 1; return;} + for (Widget_Node *w: Fluid.proj.tree.all_selected_widgets()) { + w->extra_code(n,c); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + }} tooltip {Extra initialization code for the widget.} xywh {95 90 310 20} labelfont 1 labelsize 11 textfont 4 textsize 11 } Fl_Input {v_input[1]} { user_data 1 - callback v_input_cb + callback {cb_v_input(o, v);} tooltip {Extra initialization code for the widget.} xywh {95 110 310 20} labelsize 11 textfont 4 textsize 11 } Fl_Input {v_input[2]} { user_data 2 - callback v_input_cb + callback {cb_v_input(o, v);} tooltip {Extra initialization code for the widget.} xywh {95 130 310 20} labelsize 11 textfont 4 textsize 11 } Fl_Input {v_input[3]} { user_data 3 - callback v_input_cb + callback {cb_v_input(o, v);} tooltip {Extra initialization code for the widget.} xywh {95 150 310 20} labelsize 11 textfont 4 textsize 11 } Fl_Tile {} { @@ -1064,9 +2356,21 @@ wCallback->do_callback(wCallback, v);} open } { Fl_Text_Editor wComment { label {Comment:} + callback {if (v == LOAD) { + const char *cmttext = current_widget->comment(); + o->buffer()->text( cmttext ? cmttext : "" ); + } else { + int mod = 0; + char *c = o->buffer()->text(); + for (Node *n: Fluid.proj.tree.all_selected_nodes()) { + n->comment(c); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + free(c); + }} tooltip {Write a comment that will appear in the source code and in the widget tree overview.} xywh {95 175 310 45} box DOWN_BOX labelfont 1 labelsize 11 align 4 when 1 textfont 6 textsize 11 textcolor 59 resizable code0 {wComment->buffer(new Fl_Text_Buffer());} - code1 {wComment->callback((Fl_Callback*)comment_cb);} } } Fl_Group {} {open @@ -1074,7 +2378,25 @@ wCallback->do_callback(wCallback, v);} open } { Fl_Text_Editor wCallback { label {Callback:} - callback callback_cb + callback {if (v == LOAD) { + const char *cbtext = current_widget->callback(); + o->buffer()->text( cbtext ? cbtext : "" ); + } else { + int mod = 0; + char *c = o->buffer()->text(); + const char *d = c_check(c); + if (d) { + fl_message("Error in callback: %s",d); + if (o->window()) o->window()->make_current(); + haderror = 1; + } + for (Node *n: Fluid.proj.tree.all_selected_nodes()) { + n->callback(c); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + free(c); + }} tooltip {The callback function or code for the widget. Use the variable name 'o' to access the Widget pointer and 'v' to access the user value.} xywh {95 225 310 80} box DOWN_BOX labelfont 1 labelsize 11 align 4 textfont 4 textsize 11 resizable code0 {\#include "widgets/Code_Editor.h"} class {fld::widget::Code_Editor} @@ -1087,12 +2409,48 @@ wCallback->do_callback(wCallback, v);} open xywh {95 310 310 20} labelfont 1 labelsize 11 align 4 } { Fl_Input {} { - callback user_data_cb + callback {if (v == LOAD) { + o->value(current_widget->user_data()); +} else { + int mod = 0; + const char *c = o->value(); + const char *d = c_check(c); + if (d) {fl_message("Error in user_data: %s",d); haderror = 1; return;} + for (Node *n: Fluid.proj.tree.all_selected_nodes()) { + n->user_data(c); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} tooltip {The user data to pass into the callback code.} xywh {95 310 158 20} labelfont 1 labelsize 11 textfont 4 textsize 11 resizable } Fl_Menu_Button {} { label When - callback when_cb open + callback {if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {o->deactivate(); return;} else o->activate(); + int n = current_widget->o->when(); + set_whenmenu(n); + w_when_box->copy_label(when_symbol_name(n)); +} else { + int mod = 0; + int n = 0; + if (o->mvalue() && ((o->mvalue()->flags & FL_MENU_TOGGLE) == 0) ) { + n = (int)o->mvalue()->argument(); + set_whenmenu(n); + } else { + if (whenmenu[0].value()) n |= FL_WHEN_CHANGED; + if (whenmenu[1].value()) n |= FL_WHEN_NOT_CHANGED; + if (whenmenu[2].value()) n |= FL_WHEN_RELEASE; + if (whenmenu[3].value()) n |= FL_WHEN_ENTER_KEY; + if (whenmenu[4].value()) n |= FL_WHEN_CLOSED; + } + w_when_box->copy_label(when_symbol_name(n)); + for (Widget_Node *q: Fluid.proj.tree.all_selected_widgets()) { + q->o->when(n); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} open tooltip {When to call the callback function.} xywh {260 310 145 20} box THIN_UP_BOX down_box BORDER_BOX labelfont 1 labelsize 11 when 1 textsize 11 code0 {extern Fl_Menu_Item whenmenu[];} code1 {o->menu(whenmenu);} @@ -1104,7 +2462,28 @@ wCallback->do_callback(wCallback, v);} open xywh {95 332 310 26} labelfont 1 labelsize 11 align 4 } { Fl_Input_Choice {} { - callback user_data_type_cb open + callback {static const char *dflt = "void*"; +if (v == LOAD) { + const char *c = current_widget->user_data_type(); + if (!c) c = dflt; + o->value(c); +} else { + int mod = 0; + const char *c = o->value(); + const char *d = c_check(c); + if (!*c) o->value(dflt); + else if (!strcmp(c,dflt)) c = nullptr; + if (!d) { + if (c && *c && c[strlen(c)-1] != '*' && strcmp(c,"long")) + d = "must be pointer or long"; + } + if (d) {fl_message("Error in type: %s",d); haderror = 1; return;} + for (Node *q: Fluid.proj.tree.all_selected_nodes()) { + q->user_data_type(c); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); +}} open tooltip {The type of the user data.} xywh {95 335 158 20} labelfont 1 labelsize 11 textfont 4 textsize 11 resizable } { MenuItem {} { @@ -1122,720 +2501,18 @@ wCallback->do_callback(wCallback, v);} open } } } - Fl_Group widget_tab_grid_child { - label {Grid Child} - callback propagate_load open - xywh {10 30 400 330} labelsize 11 hide - } { - Fl_Group {} { - label {Location:} - callback propagate_load open - xywh {95 60 315 20} box FLAT_BOX labelfont 1 labelsize 11 align 4 - } { - Fl_Input widget_grid_row_input { - label {Row:} - callback grid_set_row_cb - xywh {95 60 40 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Group {} {open - xywh {135 60 30 20} - } { - Fl_Button {} { - label {-} - callback grid_dec_row_cb - xywh {135 60 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - Fl_Button {} { - label {+} - callback grid_inc_row_cb - xywh {150 60 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - } - Fl_Input widget_grid_col_input { - label {Column:} - callback grid_set_col_cb - xywh {175 60 40 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Group {} {open - xywh {215 60 30 20} - } { - Fl_Button {} { - label {-} - callback grid_dec_col_cb - xywh {215 60 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - Fl_Button {} { - label {+} - callback grid_inc_col_cb - xywh {230 60 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - } - Fl_Box {} { - xywh {395 60 1 20} hide resizable - } - Fl_Box widget_grid_transient { - label TRANSIENT - callback {if (v==LOAD) { - Fl_Widget *child = ((Fl_Widget_Type*)current_widget)->o; - Fl_Grid_Proxy *g = ((Fl_Grid_Proxy*)((Fl_Widget_Type*)current_widget->parent)->o); -// Fl_Grid::Cell *cell = g->cell(child); -// Fl_Grid::Cell *tcell = g->transient_cell(child); - widget_grid_transient->hide(); - widget_grid_unlinked->hide(); - if (g->transient_cell(child)) { - widget_grid_transient->show(); - } else if (!g->cell(child)) { - widget_grid_unlinked->show(); - } -}} - xywh {250 60 80 20} labelsize 11 labelcolor 1 - } - Fl_Box widget_grid_unlinked { - label UNLINKED - xywh {250 60 80 20} labelsize 11 labelcolor 1 hide - } - } - Fl_Group wp_gridc_align { - label {Align:} - callback propagate_load open - xywh {95 100 315 20} labelfont 1 labelsize 11 align 4 - } { - Fl_Choice {} { - label Horizontal - callback grid_align_horizontal_cb open - xywh {95 100 115 20} down_box BORDER_BOX labelsize 11 align 5 textsize 11 - } { - MenuItem GRID_LEFT { - label GRID_LEFT - user_data {(fl_intptr_t)FL_GRID_LEFT} user_data_type long - xywh {10 10 31 20} labelsize 11 - } - MenuItem {} { - label GRID_CENTER - user_data {(fl_intptr_t)FL_GRID_CENTER} user_data_type long - xywh {10 10 31 20} labelsize 11 - } - MenuItem {} { - label GRID_RIGHT - user_data {(fl_intptr_t)FL_GRID_RIGHT} user_data_type long - xywh {10 10 31 20} labelsize 11 - } - MenuItem {} { - label GRID_FILL - user_data {(fl_intptr_t)FL_GRID_HORIZONTAL} user_data_type long - xywh {10 10 31 20} labelsize 11 - } - } - Fl_Choice {} { - label Vertical - callback grid_align_vertical_cb open - xywh {215 100 115 20} down_box BORDER_BOX labelsize 11 align 5 textsize 11 - } { - MenuItem {} { - label GRID_TOP - user_data {(fl_intptr_t)FL_GRID_TOP} user_data_type long - xywh {10 10 31 20} labelsize 11 - } - MenuItem {} { - label GRID_CENTER - user_data {(fl_intptr_t)FL_GRID_CENTER} user_data_type long - xywh {10 10 31 20} labelsize 11 - } - MenuItem {} { - label GRID_BOTTOM - user_data {(fl_intptr_t)FL_GRID_BOTTOM} user_data_type long - xywh {10 10 31 20} labelsize 11 - } - MenuItem {} { - label GRID_FILL - user_data {(fl_intptr_t)FL_GRID_VERTICAL} user_data_type long - xywh {10 10 31 20} labelsize 11 - } - } - Fl_Box {} { - xywh {395 100 1 20} hide resizable - } - } - Fl_Group wp_gridc_size { - label {Min. Size:} - callback propagate_load open - xywh {95 135 315 20} labelfont 1 labelsize 11 align 4 - } { - Fl_Input {} { - label {Width:} - callback grid_set_min_wdt_cb - xywh {95 135 55 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Input {} { - label {Height:} - callback grid_set_min_hgt_cb - xywh {155 135 55 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Box {} { - xywh {395 135 1 20} hide resizable - } - } - Fl_Group {} { - label {Span:} - callback propagate_load open - xywh {95 170 315 20} labelfont 1 labelsize 11 align 4 - } { - Fl_Input widget_grid_rowspan_input { - label {Row Span:} - callback grid_set_rowspan_cb - xywh {95 170 40 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Group {} {open - xywh {135 170 30 20} - } { - Fl_Button {} { - label {-} - callback grid_dec_rowspan_cb - xywh {135 170 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - Fl_Button {} { - label {+} - callback grid_inc_rowspan_cb - xywh {150 170 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - } - Fl_Input widget_grid_colspan_input { - label {Col. Span:} - callback grid_set_colspan_cb - xywh {175 170 40 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Group {} {open - xywh {215 170 30 20} - } { - Fl_Button {} { - label {-} - callback grid_dec_colspan_cb - xywh {215 170 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - Fl_Button {} { - label {+} - callback grid_inc_colspan_cb - xywh {230 170 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - } - Fl_Box {} { - xywh {395 170 1 20} hide resizable - } - } - Fl_Box {} { - xywh {95 350 300 5} labelsize 11 hide resizable - } - } Fl_Group widget_tab_grid { label Grid - callback propagate_load open + callback {o->callback((Fl_Callback*)propagate_load);} open xywh {10 30 400 330} labelsize 11 hide - } { - Fl_Group {} { - label {Grid Layout:} - callback propagate_load open - xywh {95 60 315 20} labelfont 1 labelsize 11 align 4 - } { - Fl_Input widget_grid_rows { - label {Rows:} - callback {// grid_rows_cb -Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -if (v == LOAD) { - o->value(grid->rows()); -} else { - int m = o->value(), old_m = grid->rows(); - if (m < 1) { - m = 1; - o->value(m); - } - if (m < old_m) { - // TODO: verify that this will not unlink existings cells - // Offer a dialog with "delete children", "unlink cells", "cancel" - } - if (m != old_m) { - undo_checkpoint(); - grid->layout(m, grid->cols()); - grid->need_layout(true); - set_modflag(1); - widget_tab_grid->do_callback(widget_tab_grid, LOAD); - } -}} - tooltip {Number of horizontal rows in the Grid group} xywh {95 60 40 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Group {} {open - xywh {135 60 30 20} - } { - Fl_Button {} { - label {-} - callback {if (v != LOAD) { - widget_grid_rows->value( widget_grid_rows->value()-1 ); - widget_grid_rows->do_callback(); -}} - xywh {135 60 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - Fl_Button {} { - label {+} - callback {if (v != LOAD) { - widget_grid_rows->value( widget_grid_rows->value()+1 ); - widget_grid_rows->do_callback(); -}} - xywh {150 60 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - } - Fl_Input widget_grid_cols { - label {Columns:} - callback {// grid_rows_cb -Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -if (v == LOAD) { - o->value(grid->cols()); -} else { - int m = o->value(), old_m = grid->cols(); - if (m < 1) { - m = 1; - o->value(m); - } - if (m < old_m) { - // TODO: verify that this will not unlink existings cells - // Offer a dialog with "delete children", "unlink cells", "cancel" - } - if (m != old_m) { - undo_checkpoint(); - grid->layout(grid->rows(), m); - grid->need_layout(true); - set_modflag(1); - widget_tab_grid->do_callback(widget_tab_grid, LOAD); - } -}} - tooltip {Number of vertical columns in the Grid group} xywh {175 60 40 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Group {} {open - xywh {215 60 30 20} - } { - Fl_Button {} { - label {-} - callback {if (v != LOAD) { - widget_grid_cols->value( widget_grid_cols->value()-1 ); - widget_grid_cols->do_callback(); -}} - xywh {215 60 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - Fl_Button {} { - label {+} - callback {if (v != LOAD) { - widget_grid_cols->value( widget_grid_cols->value()+1 ); - widget_grid_cols->do_callback(); -}} - xywh {230 60 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - } - Fl_Box {} { - xywh {396 60 0 20} resizable - } - } - Fl_Group wp_grid_margin { - label {Margins:} - callback propagate_load open - xywh {95 100 315 20} labelfont 1 labelsize 11 align 4 - } { - Fl_Value_Input {} { - label {Left:} - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -int m = 0; -if (v == LOAD) { - grid->margin(&m, NULL, NULL, NULL); - o->value(m); -} else { - int m = (int)o->value(), old_m; - grid->margin(&old_m, NULL, NULL, NULL); - if (m != old_m) { - undo_checkpoint(); - grid->margin(m, -1, -1, -1); - grid->need_layout(true); - set_modflag(1); - } -}} - tooltip {Left margin in group.} xywh {95 100 55 20} labelsize 11 align 5 maximum 1000 step 1 textsize 11 - } - Fl_Value_Input {} { - label {Top:} - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -int m = 0; -if (v == LOAD) { - grid->margin(NULL, &m, NULL, NULL); - o->value(m); -} else { - int m = (int)o->value(), old_m; - grid->margin(NULL, &old_m, NULL, NULL); - if (m != old_m) { - undo_checkpoint(); - grid->margin(-1, m, -1, -1); - grid->need_layout(true); - set_modflag(1); - } -}} - tooltip {Top margin in group.} xywh {155 100 55 20} labelsize 11 align 5 maximum 1000 step 1 textsize 11 - } - Fl_Value_Input {} { - label {Right:} - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -int m = 0; -if (v == LOAD) { - grid->margin(NULL, NULL, &m, NULL); - o->value(m); -} else { - int m = (int)o->value(), old_m; - grid->margin(NULL, NULL, &old_m, NULL); - if (m != old_m) { - undo_checkpoint(); - grid->margin(-1, -1, m, -1); - grid->need_layout(true); - set_modflag(1); - } -}} - tooltip {Right margin in group.} xywh {215 100 55 20} labelsize 11 align 5 maximum 1000 step 1 textsize 11 - } - Fl_Value_Input {} { - label {Bottom:} - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -int m = 0; -if (v == LOAD) { - grid->margin(NULL, NULL, NULL, &m); - o->value(m); -} else { - int m = (int)o->value(), old_m; - grid->margin(NULL, NULL, NULL, &old_m); - if (m != old_m) { - undo_checkpoint(); - grid->margin(-1, -1, -1, m); - grid->need_layout(true); - set_modflag(1); - } -}} - tooltip {Bottom margin in group.} xywh {275 100 55 20} labelsize 11 align 5 maximum 1000 step 1 textsize 11 - } - Fl_Box {} { - xywh {396 100 0 20} resizable - } - } - Fl_Group wp_grid_gaps { - label {Gaps:} - callback propagate_load open - xywh {95 135 315 20} labelfont 1 labelsize 11 align 4 - } { - Fl_Value_Input {} { - label {Row:} - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -if (v == LOAD) { - int m = 0; - grid->gap(&m, NULL); - o->value(m); -} else { - int m = (int)o->value(), old_m, m2; - grid->gap(&old_m, &m2); - if (m != old_m) { - undo_checkpoint(); - grid->gap(m, m2); - grid->need_layout(true); - set_modflag(1); - } -}} - tooltip {Gap between children.} xywh {95 135 55 20} labelsize 11 align 5 maximum 1000 step 1 textsize 11 - } - Fl_Value_Input {} { - label {Col:} - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -if (v == LOAD) { - int m = 0; - grid->gap(NULL, &m); - o->value(m); -} else { - int m = (int)o->value(), old_m, m2; - grid->gap(&m2, &old_m); - if (m != old_m) { - undo_checkpoint(); - grid->gap(m2, m); - grid->need_layout(true); - set_modflag(1); - } -}} - tooltip {Gap between children.} xywh {155 135 55 20} labelsize 11 align 5 maximum 1000 step 1 textsize 11 - } - Fl_Box {} { - xywh {396 135 0 20} resizable - } - } - Fl_Group {} { - label {Row:} - callback {if (v == LOAD) { - Fl_Grid *grid = Fl_Grid_Type::selected(); - if (grid) - o->activate(); - else - o->deactivate(); - propagate_load(o, v); -}} open - xywh {95 175 315 20} labelfont 1 labelsize 11 align 4 - } { - Fl_Input widget_grid_curr_row { - label Index - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -int r = o->value(), old_r = r; -if (r < 0) r = 0; -if (r >= grid->rows()) r = grid->rows()-1; -if (r != old_r) o->value(r); -if (v == LOAD) { - // will automatically propagate -} else { - widget_grid_curr_row_attributes->do_callback(widget_grid_curr_row_attributes, LOAD); -}} - xywh {95 175 40 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Group {} { - callback propagate_load open - xywh {135 175 30 20} - } { - Fl_Button {} { - label {-} - callback {if (v != LOAD) { - widget_grid_curr_row->value( widget_grid_curr_row->value()-1 ); - widget_grid_curr_row->do_callback(); -}} - xywh {135 175 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - Fl_Button {} { - label {+} - callback {if (v != LOAD) { - widget_grid_curr_row->value( widget_grid_curr_row->value()+1 ); - widget_grid_curr_row->do_callback(); -}} - xywh {150 175 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - } - Fl_Box {} { - label {:} - xywh {165 175 15 20} labelsize 11 - } - Fl_Group widget_grid_curr_row_attributes { - callback propagate_load open - xywh {180 175 175 20} - } { - Fl_Input {} { - label {Height:} - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -int r = widget_grid_curr_row->value(); -if (v == LOAD) { - o->value(grid->row_height(r)); -} else { - int h = o->value(), old_h = grid->row_height(r); - if (h < 0) h = 0; - if (h != old_h) { - undo_checkpoint(); - grid->row_height(r, h); - grid->need_layout(true); - set_modflag(1); - } -}} - xywh {180 175 55 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Input {} { - label {Weight:} - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -int r = widget_grid_curr_row->value(); -if (v == LOAD) { - o->value(grid->row_weight(r)); -} else { - int h = o->value(), old_h = grid->row_weight(r); - if (h < 0) h = 0; - if (h != old_h) { - undo_checkpoint(); - grid->row_weight(r, h); - grid->need_layout(true); - set_modflag(1); - } -}} - xywh {240 175 55 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Input {} { - label {Gap:} - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -int r = widget_grid_curr_row->value(); -if (v == LOAD) { - o->value(grid->row_gap(r)); -} else { - int h = o->value(), old_h = grid->row_gap(r); - if (h < -1) h = -1; - if (h != old_h) { - undo_checkpoint(); - grid->row_gap(r, h); - grid->need_layout(true); - set_modflag(1); - } -}} - xywh {300 175 55 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - } - Fl_Box {} { - xywh {400 175 1 20} hide resizable - } - } - Fl_Group {} { - label {Column:} - callback propagate_load open - xywh {95 210 315 20} labelfont 1 labelsize 11 align 4 - } { - Fl_Input widget_grid_curr_col { - label Index - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -int c = o->value(), old_c = c; -if (c < 0) c = 0; -if (c >= grid->cols()) c = grid->cols()-1; -if (c != old_c) o->value(c); -if (v == LOAD) { - // will automatically propagate -} else { - widget_grid_curr_col_attributes->do_callback(widget_grid_curr_col_attributes, LOAD); -}} - xywh {95 210 40 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Group {} {open - xywh {135 210 30 20} - } { - Fl_Button {} { - label {-} - callback {if (v != LOAD) { - widget_grid_curr_col->value( widget_grid_curr_col->value()-1 ); - widget_grid_curr_col->do_callback(); -}} - xywh {135 210 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - Fl_Button {} { - label {+} - callback {if (v != LOAD) { - widget_grid_curr_col->value( widget_grid_curr_col->value()+1 ); - widget_grid_curr_col->do_callback(); -}} - xywh {150 210 15 20} labelsize 11 - code0 {o->clear_visible_focus();} compact 1 - } - } - Fl_Box {} { - label {:} - xywh {165 210 15 20} labelsize 11 - } - Fl_Group widget_grid_curr_col_attributes { - callback propagate_load open - xywh {180 210 175 20} - } { - Fl_Input {} { - label {Width:} - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -int c = widget_grid_curr_col->value(); -if (v == LOAD) { - o->value(grid->col_width(c)); -} else { - int h = o->value(), old_h = grid->col_width(c); - if (h < 0) h = 0; - if (h != old_h) { - undo_checkpoint(); - grid->col_width(c, h); - grid->need_layout(true); - set_modflag(1); - } -}} - xywh {180 210 55 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Input {} { - label {Weight:} - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -int c = widget_grid_curr_col->value(); -if (v == LOAD) { - o->value(grid->col_weight(c)); -} else { - int h = o->value(), old_h = grid->col_weight(c); - if (h < 0) h = 0; - if (h != old_h) { - undo_checkpoint(); - grid->col_weight(c, h); - grid->need_layout(true); - set_modflag(1); - } -}} - xywh {240 210 55 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - Fl_Input {} { - label {Gap:} - callback {Fl_Grid *grid = Fl_Grid_Type::selected(); -if (!grid) return; -int c = widget_grid_curr_col->value(); -if (v == LOAD) { - o->value(grid->col_gap(c)); -} else { - int h = o->value(), old_h = grid->col_gap(c); - if (h < -1) h = -1; - if (h != old_h) { - undo_checkpoint(); - grid->col_gap(c, h); - grid->need_layout(true); - set_modflag(1); - } -}} - xywh {300 210 55 20} labelsize 11 align 5 textsize 11 - class {fld::widget::Formula_Input} - } - } - Fl_Box {} { - xywh {400 210 1 20} hide resizable - } - } - Fl_Box {} { - xywh {95 350 300 5} labelsize 11 hide resizable - } - } + class Grid_Tab + } {} + Fl_Group widget_tab_grid_child { + label {Grid Child} + callback {o->callback((Fl_Callback*)propagate_load);} open + xywh {10 30 400 330} labelsize 11 hide + class Grid_Child_Tab + } {} } Fl_Tabs widget_tabs_repo { xywh {10 10 400 350} hide diff --git a/fluid/panels/widget_panel.h b/fluid/panels/widget_panel.h index bac21a771..80ccd4171 100644 --- a/fluid/panels/widget_panel.h +++ b/fluid/panels/widget_panel.h @@ -19,7 +19,10 @@ #ifndef widget_panel_h #define widget_panel_h #include +#include "panels/widget_panel/Grid_Child_Tab.h" +#include "panels/widget_panel/Grid_Tab.h" #include "widgets/Formula_Input.h" +class Fl_Flex; #include extern Fl_Double_Window *image_panel_window; #include @@ -32,17 +35,14 @@ extern fld::widget::Formula_Input *image_panel_imagew; extern fld::widget::Formula_Input *image_panel_imageh; #include #include -extern void compress_image_cb(Fl_Check_Button*, void*); -extern void bind_image_cb(Fl_Check_Button*, void*); extern Fl_Group *image_panel_deimagegroup; extern Fl_Box *image_panel_dedata; extern fld::widget::Formula_Input *image_panel_deimagew; extern fld::widget::Formula_Input *image_panel_deimageh; -extern void compress_deimage_cb(Fl_Check_Button*, void*); -extern void bind_deimage_cb(Fl_Check_Button*, void*); extern Fl_Button *image_panel_close; Fl_Double_Window* make_image_panel(); void run_image_panel(); +void flex_margin_cb(Fl_Value_Input* i, void* v, void (*load_margin)(Fl_Flex*,Fl_Value_Input*), int (*update_margin)(Fl_Flex*,int)); #include extern Fl_Tabs *widget_tabs; extern Fl_Group *wp_gui_tab; @@ -52,159 +52,59 @@ extern Fl_Input *wp_gui_label; #include extern Fl_Menu_Item labeltypemenu[]; extern void labeltype_cb(Fl_Choice*, void*); -extern void image_cb(Fl_Input*, void*); extern Fl_Input *widget_image_input; -extern void image_browse_cb(Fl_Button*, void*); -extern void inactive_cb(Fl_Input*, void*); extern Fl_Input *widget_deimage_input; -extern void inactive_browse_cb(Fl_Button*, void*); extern Fl_Group *wp_gui_alignment; extern void align_cb(Fl_Button*, void*); extern void align_text_image_cb(Fl_Choice*, void*); extern void align_position_cb(Fl_Choice*, void*); extern void position_group_cb(Fl_Group*, void*); -extern void x_cb(fld::widget::Formula_Input*, void*); extern fld::widget::Formula_Input *widget_x_input; -extern void y_cb(fld::widget::Formula_Input*, void*); extern fld::widget::Formula_Input *widget_y_input; -extern void w_cb(fld::widget::Formula_Input*, void*); extern fld::widget::Formula_Input *widget_w_input; -extern void h_cb(fld::widget::Formula_Input*, void*); extern fld::widget::Formula_Input *widget_h_input; -extern void wc_relative_cb(Fl_Choice*, void*); -extern void flex_size_group_cb(Fl_Group*, void*); extern Fl_Group *wp_gui_flexp; #include -extern void flex_size_cb(Fl_Value_Input*, void*); extern Fl_Value_Input *widget_flex_size; -extern void flex_fixed_cb(Fl_Check_Button*, void*); extern Fl_Check_Button *widget_flex_fixed; -extern void values_group_cb(Fl_Group*, void*); extern Fl_Group *wp_gui_values; -extern void slider_size_cb(Fl_Value_Input*, void*); -extern void min_cb(Fl_Value_Input*, void*); -extern void max_cb(Fl_Value_Input*, void*); -extern void step_cb(Fl_Value_Input*, void*); -extern void value_cb(Fl_Value_Input*, void*); -extern void flex_margin_group_cb(Fl_Group*, void*); extern Fl_Group *wp_gui_margins; -extern void flex_margin_left_cb(Fl_Value_Input*, void*); -extern void flex_margin_top_cb(Fl_Value_Input*, void*); -extern void flex_margin_right_cb(Fl_Value_Input*, void*); -extern void flex_margin_bottom_cb(Fl_Value_Input*, void*); -extern void flex_margin_gap_cb(Fl_Value_Input*, void*); -extern void size_range_group_cb(Fl_Group*, void*); extern Fl_Group *wp_gui_sizerange; -extern void min_w_cb(Fl_Value_Input*, void*); -extern void min_h_cb(Fl_Value_Input*, void*); -extern void set_min_size_cb(Fl_Button*, void*); -extern void max_w_cb(Fl_Value_Input*, void*); -extern void max_h_cb(Fl_Value_Input*, void*); -extern void set_max_size_cb(Fl_Button*, void*); #include -extern void shortcut_in_cb(Fl_Shortcut_Button*, void*); extern Fl_Shortcut_Button *wp_gui_shortcut; extern Fl_Group *wp_gui_xclass; -extern void xclass_cb(Fl_Input*, void*); #include -extern void border_cb(Fl_Light_Button*, void*); -extern void modal_cb(Fl_Light_Button*, void*); -extern void non_modal_cb(Fl_Light_Button*, void*); extern Fl_Group *wp_gui_attributes; -extern void visible_cb(Fl_Light_Button*, void*); -extern void active_cb(Fl_Light_Button*, void*); -extern void resizable_cb(Fl_Light_Button*, void*); -extern void hotspot_cb(Fl_Light_Button*, void*); -extern void tooltip_cb(Fl_Input*, void*); extern Fl_Input *wp_gui_tooltip; extern Fl_Group *wp_style_tab; extern Fl_Group *wp_style_label; extern Fl_Menu_Item fontmenu[]; -extern void labelfont_cb(Fl_Choice*, void*); -extern void labelsize_cb(Fl_Value_Input*, void*); -extern void labelcolor_cb(Fl_Button*, void*); extern Fl_Button *w_labelcolor; #include extern Fl_Menu_Item colormenu[]; -extern void labelcolor_menu_cb(Fl_Menu_Button*, void*); extern Fl_Group *wp_style_box; extern Fl_Menu_Item boxmenu[]; -extern void box_cb(Fl_Choice*, void*); -extern void color_cb(Fl_Button*, void*); extern Fl_Button *w_color; -extern void color_menu_cb(Fl_Menu_Button*, void*); extern Fl_Group *wp_style_downbox; -extern void down_box_cb(Fl_Choice*, void*); -extern void color2_cb(Fl_Button*, void*); extern Fl_Button *w_selectcolor; -extern void color2_menu_cb(Fl_Menu_Button*, void*); extern Fl_Group *wp_style_text; -extern void textfont_cb(Fl_Choice*, void*); -extern void textsize_cb(Fl_Value_Input*, void*); -extern void textcolor_cb(Fl_Button*, void*); extern Fl_Button *w_textcolor; -extern void textcolor_menu_cb(Fl_Menu_Button*, void*); -extern void h_label_margin_cb(Fl_Value_Input*, void*); -extern void v_label_margin_cb(Fl_Value_Input*, void*); -extern void image_spacing_cb(Fl_Value_Input*, void*); -extern void compact_cb(Fl_Light_Button*, void*); extern Fl_Group *wp_cpp_tab; extern Fl_Group *wp_cpp_class; -extern void subclass_cb(Fl_Input*, void*); -extern void subtype_cb(Fl_Choice*, void*); extern Fl_Group *wp_cpp_name; -extern void name_cb(Fl_Input*, void*); -extern void name_public_member_cb(Fl_Choice*, void*); extern void name_public_cb(Fl_Choice*, void*); -extern void v_input_cb(Fl_Input*, void*); extern Fl_Input *v_input[4]; #include #include extern Fl_Text_Editor *wComment; #include "widgets/Code_Editor.h" -extern void callback_cb(fld::widget::Code_Editor*, void*); extern fld::widget::Code_Editor *wCallback; extern Fl_Group *wp_cpp_callback; -extern void user_data_cb(Fl_Input*, void*); extern Fl_Menu_Item whenmenu[]; -extern void when_cb(Fl_Menu_Button*, void*); #include -extern void user_data_type_cb(Fl_Input_Choice*, void*); extern Fl_Box *w_when_box; -extern Fl_Group *widget_tab_grid_child; -extern void grid_set_row_cb(fld::widget::Formula_Input*, void*); -extern fld::widget::Formula_Input *widget_grid_row_input; -extern void grid_dec_row_cb(Fl_Button*, void*); -extern void grid_inc_row_cb(Fl_Button*, void*); -extern void grid_set_col_cb(fld::widget::Formula_Input*, void*); -extern fld::widget::Formula_Input *widget_grid_col_input; -extern void grid_dec_col_cb(Fl_Button*, void*); -extern void grid_inc_col_cb(Fl_Button*, void*); -extern Fl_Box *widget_grid_transient; -extern Fl_Box *widget_grid_unlinked; -extern Fl_Group *wp_gridc_align; -extern void grid_align_horizontal_cb(Fl_Choice*, void*); -extern void grid_align_vertical_cb(Fl_Choice*, void*); -extern Fl_Group *wp_gridc_size; -extern void grid_set_min_wdt_cb(fld::widget::Formula_Input*, void*); -extern void grid_set_min_hgt_cb(fld::widget::Formula_Input*, void*); -extern void grid_set_rowspan_cb(fld::widget::Formula_Input*, void*); -extern fld::widget::Formula_Input *widget_grid_rowspan_input; -extern void grid_dec_rowspan_cb(Fl_Button*, void*); -extern void grid_inc_rowspan_cb(Fl_Button*, void*); -extern void grid_set_colspan_cb(fld::widget::Formula_Input*, void*); -extern fld::widget::Formula_Input *widget_grid_colspan_input; -extern void grid_dec_colspan_cb(Fl_Button*, void*); -extern void grid_inc_colspan_cb(Fl_Button*, void*); -extern Fl_Group *widget_tab_grid; -extern fld::widget::Formula_Input *widget_grid_rows; -extern fld::widget::Formula_Input *widget_grid_cols; -extern Fl_Group *wp_grid_margin; -extern Fl_Group *wp_grid_gaps; -extern fld::widget::Formula_Input *widget_grid_curr_row; -extern Fl_Group *widget_grid_curr_row_attributes; -extern fld::widget::Formula_Input *widget_grid_curr_col; -extern Fl_Group *widget_grid_curr_col_attributes; +extern Grid_Tab *widget_tab_grid; +extern Grid_Child_Tab *widget_tab_grid_child; extern Fl_Tabs *widget_tabs_repo; extern void live_mode_cb(Fl_Button*, void*); extern Fl_Button *wLiveMode; @@ -219,7 +119,4 @@ extern Fl_Menu_Item menu_Children[]; extern Fl_Menu_Item menu_2[]; extern Fl_Menu_Item menu_3[]; extern Fl_Menu_Item menu_4[]; -extern Fl_Menu_Item menu_Horizontal[]; -#define GRID_LEFT (menu_Horizontal+0) -extern Fl_Menu_Item menu_Vertical[]; #endif diff --git a/fluid/panels/widget_panel/Grid_Child_Tab.cxx b/fluid/panels/widget_panel/Grid_Child_Tab.cxx new file mode 100644 index 000000000..b0f943803 --- /dev/null +++ b/fluid/panels/widget_panel/Grid_Child_Tab.cxx @@ -0,0 +1,538 @@ +// generated by Fast Light User Interface Designer (fluid) version 1.0500 + +#include "Grid_Child_Tab.h" +#include "Fluid.h" +#include "proj/undo.h" +#include "nodes/Grid_Node.h" +extern Grid_Child_Tab *widget_tab_grid_child; + +void Grid_Child_Tab::cb_widget_grid_row_input_i(fld::widget::Formula_Input* o, void* v) { + grid_child_cb(o, v, 8); + if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); +} +void Grid_Child_Tab::cb_widget_grid_row_input(fld::widget::Formula_Input* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()))->cb_widget_grid_row_input_i(o,v); +} + +void Grid_Child_Tab::cb__i(Fl_Button*, void* v) { + if (v!=LOAD) { + grid_child_cb(widget_grid_row_input, v, 0x0100 + 8); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + } +} +void Grid_Child_Tab::cb_(Fl_Button* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()->parent()))->cb__i(o,v); +} + +void Grid_Child_Tab::cb_1_i(Fl_Button*, void* v) { + if (v!=LOAD) { + grid_child_cb(widget_grid_row_input, v, 0x0200 + 8); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + } +} +void Grid_Child_Tab::cb_1(Fl_Button* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()->parent()))->cb_1_i(o,v); +} + +void Grid_Child_Tab::cb_widget_grid_col_input_i(fld::widget::Formula_Input* o, void* v) { + grid_child_cb(o, v, 9); + if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); +} +void Grid_Child_Tab::cb_widget_grid_col_input(fld::widget::Formula_Input* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()))->cb_widget_grid_col_input_i(o,v); +} + +void Grid_Child_Tab::cb_2_i(Fl_Button*, void* v) { + if (v!=LOAD) { + grid_child_cb(widget_grid_col_input, v, 0x0100 + 9); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + } +} +void Grid_Child_Tab::cb_2(Fl_Button* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()->parent()))->cb_2_i(o,v); +} + +void Grid_Child_Tab::cb_3_i(Fl_Button*, void* v) { + if (v!=LOAD) { + grid_child_cb(widget_grid_col_input, v, 0x0200 + 9); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + } +} +void Grid_Child_Tab::cb_3(Fl_Button* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()->parent()))->cb_3_i(o,v); +} + +void Grid_Child_Tab::cb_widget_grid_transient_i(Fl_Box* o, void* v) { + if (v==LOAD) { + Fl_Widget *child = ((Widget_Node*)current_widget)->o; + Fl_Grid_Proxy *g = ((Fl_Grid_Proxy*)((Widget_Node*)current_widget->parent)->o); + // Fl_Grid::Cell *cell = g->cell(child); + // Fl_Grid::Cell *tcell = g->transient_cell(child); + widget_grid_transient->hide(); + widget_grid_unlinked->hide(); + if (g->transient_cell(child)) { + widget_grid_transient->show(); + } else if (!g->cell(child)) { + widget_grid_unlinked->show(); + } + } +} +void Grid_Child_Tab::cb_widget_grid_transient(Fl_Box* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()))->cb_widget_grid_transient_i(o,v); +} + +void Grid_Child_Tab::cb_Horizontal_i(Fl_Choice* o, void* v) { + if ( !current_widget + || !current_widget->parent + || !current_widget->parent->is_a(Type::Grid)) + { + return; + } + int mask = (FL_GRID_LEFT | FL_GRID_RIGHT | FL_GRID_HORIZONTAL); + Fl_Grid *g = ((Fl_Grid*)((Widget_Node*)current_widget->parent)->o); + if (v == LOAD) { + int a = FL_GRID_FILL & mask; + Fl_Grid::Cell *cell = g->cell(current_widget->o); + if (cell) { + a = cell->align() & mask; + } + const Fl_Menu_Item *mi = o->find_item_with_argument(a); + if (mi) o->value(mi); + } else { + Fluid.proj.undo.checkpoint(); + int v = FL_GRID_FILL & mask, old_v = FL_GRID_FILL & mask; + const Fl_Menu_Item *mi = o->mvalue(); + if (mi) v = (int)mi->argument(); + Fl_Grid::Cell *cell = g->cell(current_widget->o); + if (cell) { + old_v = cell->align() & mask; + if (old_v != v) { + cell->align((Fl_Grid_Align)(v | (cell->align() & ~mask))); + g->need_layout(true); + g->redraw(); + Fluid.proj.set_modflag(1); + } + } + } +} +void Grid_Child_Tab::cb_Horizontal(Fl_Choice* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()))->cb_Horizontal_i(o,v); +} + +Fl_Menu_Item Grid_Child_Tab::menu_Horizontal[] = { + {"GRID_LEFT", 0, 0, (void*)((fl_intptr_t)FL_GRID_LEFT), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {"GRID_CENTER", 0, 0, (void*)((fl_intptr_t)FL_GRID_CENTER), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {"GRID_RIGHT", 0, 0, (void*)((fl_intptr_t)FL_GRID_RIGHT), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {"GRID_FILL", 0, 0, (void*)((fl_intptr_t)FL_GRID_HORIZONTAL), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {0,0,0,0,0,0,0,0,0} +}; +Fl_Menu_Item* Grid_Child_Tab::GRID_LEFT = Grid_Child_Tab::menu_Horizontal + 0; + +void Grid_Child_Tab::cb_Vertical_i(Fl_Choice* o, void* v) { + if ( !current_widget + || !current_widget->parent + || !current_widget->parent->is_a(Type::Grid)) + { + return; + } + int mask = (FL_GRID_TOP | FL_GRID_BOTTOM | FL_GRID_VERTICAL); + Fl_Grid *g = ((Fl_Grid*)((Widget_Node*)current_widget->parent)->o); + if (v == LOAD) { + int a = FL_GRID_FILL & mask; + Fl_Grid::Cell *cell = g->cell(current_widget->o); + if (cell) { + a = cell->align() & mask; + } + const Fl_Menu_Item *mi = o->find_item_with_argument(a); + if (mi) o->value(mi); + } else { + Fluid.proj.undo.checkpoint(); + int v = FL_GRID_FILL & mask, old_v = FL_GRID_FILL & mask; + const Fl_Menu_Item *mi = o->mvalue(); + if (mi) v = (int)mi->argument(); + Fl_Grid::Cell *cell = g->cell(current_widget->o); + if (cell) { + old_v = cell->align() & mask; + if (old_v != v) { + cell->align((Fl_Grid_Align)(v | (cell->align() & ~mask))); + g->need_layout(true); + g->redraw(); + Fluid.proj.set_modflag(1); + } + } + } +} +void Grid_Child_Tab::cb_Vertical(Fl_Choice* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()))->cb_Vertical_i(o,v); +} + +Fl_Menu_Item Grid_Child_Tab::menu_Vertical[] = { + {"GRID_TOP", 0, 0, (void*)((fl_intptr_t)FL_GRID_TOP), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {"GRID_CENTER", 0, 0, (void*)((fl_intptr_t)FL_GRID_CENTER), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {"GRID_BOTTOM", 0, 0, (void*)((fl_intptr_t)FL_GRID_BOTTOM), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {"GRID_FILL", 0, 0, (void*)((fl_intptr_t)FL_GRID_VERTICAL), 0, (uchar)FL_NORMAL_LABEL, 0, 11, 0}, + {0,0,0,0,0,0,0,0,0} +}; + +void Grid_Child_Tab::cb_Width_i(fld::widget::Formula_Input* o, void* v) { + grid_child_cb(o, v, 12); +} +void Grid_Child_Tab::cb_Width(fld::widget::Formula_Input* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()))->cb_Width_i(o,v); +} + +void Grid_Child_Tab::cb_Height_i(fld::widget::Formula_Input* o, void* v) { + grid_child_cb(o, v, 13); +} +void Grid_Child_Tab::cb_Height(fld::widget::Formula_Input* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()))->cb_Height_i(o,v); +} + +void Grid_Child_Tab::cb_widget_grid_rowspan_input_i(fld::widget::Formula_Input* o, void* v) { + grid_child_cb(o, v, 10); + if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); +} +void Grid_Child_Tab::cb_widget_grid_rowspan_input(fld::widget::Formula_Input* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()))->cb_widget_grid_rowspan_input_i(o,v); +} + +void Grid_Child_Tab::cb_4_i(Fl_Button*, void* v) { + if (v!=LOAD) { + grid_child_cb(widget_grid_rowspan_input, v, 0x0100 + 10); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + } +} +void Grid_Child_Tab::cb_4(Fl_Button* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()->parent()))->cb_4_i(o,v); +} + +void Grid_Child_Tab::cb_5_i(Fl_Button*, void* v) { + if (v!=LOAD) { + grid_child_cb(widget_grid_rowspan_input, v, 0x0200 + 10); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + } +} +void Grid_Child_Tab::cb_5(Fl_Button* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()->parent()))->cb_5_i(o,v); +} + +void Grid_Child_Tab::cb_widget_grid_colspan_input_i(fld::widget::Formula_Input* o, void* v) { + grid_child_cb(o, v, 11); + if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); +} +void Grid_Child_Tab::cb_widget_grid_colspan_input(fld::widget::Formula_Input* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()))->cb_widget_grid_colspan_input_i(o,v); +} + +void Grid_Child_Tab::cb_6_i(Fl_Button*, void* v) { + if (v!=LOAD) { + grid_child_cb(widget_grid_colspan_input, v, 0x0100 + 11); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + } +} +void Grid_Child_Tab::cb_6(Fl_Button* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()->parent()))->cb_6_i(o,v); +} + +void Grid_Child_Tab::cb_7_i(Fl_Button*, void* v) { + if (v!=LOAD) { + grid_child_cb(widget_grid_colspan_input, v, 0x0200 + 11); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + } +} +void Grid_Child_Tab::cb_7(Fl_Button* o, void* v) { + ((Grid_Child_Tab*)(o->parent()->parent()->parent()))->cb_7_i(o,v); +} + +Grid_Child_Tab::Grid_Child_Tab(int X, int Y, int W, int H, const char *L) : + Fl_Group(0, 0, 400, 330, L) +{ + this->labelsize(11); + this->callback((Fl_Callback*)propagate_load); + { Fl_Group* o = new Fl_Group(85, 30, 315, 20, "Location:"); + o->box(FL_FLAT_BOX); + o->labelfont(1); + o->labelsize(11); + o->callback((Fl_Callback*)propagate_load); + o->align(Fl_Align(FL_ALIGN_LEFT)); + { widget_grid_row_input = new fld::widget::Formula_Input(85, 30, 40, 20, "Row:"); + widget_grid_row_input->box(FL_DOWN_BOX); + widget_grid_row_input->color(FL_BACKGROUND2_COLOR); + widget_grid_row_input->selection_color(FL_SELECTION_COLOR); + widget_grid_row_input->labeltype(FL_NORMAL_LABEL); + widget_grid_row_input->labelfont(0); + widget_grid_row_input->labelsize(11); + widget_grid_row_input->labelcolor(FL_FOREGROUND_COLOR); + widget_grid_row_input->textsize(11); + widget_grid_row_input->callback((Fl_Callback*)cb_widget_grid_row_input); + widget_grid_row_input->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + widget_grid_row_input->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* widget_grid_row_input + { Fl_Group* o = new Fl_Group(125, 30, 30, 20); + { Fl_Button* o = new Fl_Button(125, 30, 15, 20, "-"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_); + o->clear_visible_focus(); + } // Fl_Button* o + { Fl_Button* o = new Fl_Button(140, 30, 15, 20, "+"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_1); + o->clear_visible_focus(); + } // Fl_Button* o + o->end(); + } // Fl_Group* o + { widget_grid_col_input = new fld::widget::Formula_Input(165, 30, 40, 20, "Column:"); + widget_grid_col_input->box(FL_DOWN_BOX); + widget_grid_col_input->color(FL_BACKGROUND2_COLOR); + widget_grid_col_input->selection_color(FL_SELECTION_COLOR); + widget_grid_col_input->labeltype(FL_NORMAL_LABEL); + widget_grid_col_input->labelfont(0); + widget_grid_col_input->labelsize(11); + widget_grid_col_input->labelcolor(FL_FOREGROUND_COLOR); + widget_grid_col_input->textsize(11); + widget_grid_col_input->callback((Fl_Callback*)cb_widget_grid_col_input); + widget_grid_col_input->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + widget_grid_col_input->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* widget_grid_col_input + { Fl_Group* o = new Fl_Group(205, 30, 30, 20); + { Fl_Button* o = new Fl_Button(205, 30, 15, 20, "-"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_2); + o->clear_visible_focus(); + } // Fl_Button* o + { Fl_Button* o = new Fl_Button(220, 30, 15, 20, "+"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_3); + o->clear_visible_focus(); + } // Fl_Button* o + o->end(); + } // Fl_Group* o + { Fl_Box* o = new Fl_Box(385, 30, 1, 20); + o->hide(); + Fl_Group::current()->resizable(o); + } // Fl_Box* o + { widget_grid_transient = new Fl_Box(240, 30, 80, 20, "TRANSIENT"); + widget_grid_transient->labelsize(11); + widget_grid_transient->labelcolor((Fl_Color)1); + widget_grid_transient->callback((Fl_Callback*)cb_widget_grid_transient); + } // Fl_Box* widget_grid_transient + { widget_grid_unlinked = new Fl_Box(240, 30, 80, 20, "UNLINKED"); + widget_grid_unlinked->labelsize(11); + widget_grid_unlinked->labelcolor((Fl_Color)1); + widget_grid_unlinked->hide(); + } // Fl_Box* widget_grid_unlinked + o->end(); + } // Fl_Group* o + { wp_gridc_align = new Fl_Group(85, 70, 315, 20, "Align:"); + wp_gridc_align->labelfont(1); + wp_gridc_align->labelsize(11); + wp_gridc_align->callback((Fl_Callback*)propagate_load); + wp_gridc_align->align(Fl_Align(FL_ALIGN_LEFT)); + { Fl_Choice* o = new Fl_Choice(85, 70, 115, 20, "Horizontal"); + o->down_box(FL_BORDER_BOX); + o->labelsize(11); + o->textsize(11); + o->callback((Fl_Callback*)cb_Horizontal); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + o->menu(menu_Horizontal); + } // Fl_Choice* o + { Fl_Choice* o = new Fl_Choice(205, 70, 115, 20, "Vertical"); + o->down_box(FL_BORDER_BOX); + o->labelsize(11); + o->textsize(11); + o->callback((Fl_Callback*)cb_Vertical); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + o->menu(menu_Vertical); + } // Fl_Choice* o + { Fl_Box* o = new Fl_Box(385, 70, 1, 20); + o->hide(); + Fl_Group::current()->resizable(o); + } // Fl_Box* o + wp_gridc_align->end(); + } // Fl_Group* wp_gridc_align + { wp_gridc_size = new Fl_Group(85, 105, 315, 20, "Min. Size:"); + wp_gridc_size->labelfont(1); + wp_gridc_size->labelsize(11); + wp_gridc_size->callback((Fl_Callback*)propagate_load); + wp_gridc_size->align(Fl_Align(FL_ALIGN_LEFT)); + { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(85, 105, 55, 20, "Width:"); + o->box(FL_DOWN_BOX); + o->color(FL_BACKGROUND2_COLOR); + o->selection_color(FL_SELECTION_COLOR); + o->labeltype(FL_NORMAL_LABEL); + o->labelfont(0); + o->labelsize(11); + o->labelcolor(FL_FOREGROUND_COLOR); + o->textsize(11); + o->callback((Fl_Callback*)cb_Width); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + o->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* o + { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(145, 105, 55, 20, "Height:"); + o->box(FL_DOWN_BOX); + o->color(FL_BACKGROUND2_COLOR); + o->selection_color(FL_SELECTION_COLOR); + o->labeltype(FL_NORMAL_LABEL); + o->labelfont(0); + o->labelsize(11); + o->labelcolor(FL_FOREGROUND_COLOR); + o->textsize(11); + o->callback((Fl_Callback*)cb_Height); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + o->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* o + { Fl_Box* o = new Fl_Box(385, 105, 1, 20); + o->hide(); + Fl_Group::current()->resizable(o); + } // Fl_Box* o + wp_gridc_size->end(); + } // Fl_Group* wp_gridc_size + { Fl_Group* o = new Fl_Group(85, 140, 315, 20, "Span:"); + o->labelfont(1); + o->labelsize(11); + o->callback((Fl_Callback*)propagate_load); + o->align(Fl_Align(FL_ALIGN_LEFT)); + { widget_grid_rowspan_input = new fld::widget::Formula_Input(85, 140, 40, 20, "Row Span:"); + widget_grid_rowspan_input->box(FL_DOWN_BOX); + widget_grid_rowspan_input->color(FL_BACKGROUND2_COLOR); + widget_grid_rowspan_input->selection_color(FL_SELECTION_COLOR); + widget_grid_rowspan_input->labeltype(FL_NORMAL_LABEL); + widget_grid_rowspan_input->labelfont(0); + widget_grid_rowspan_input->labelsize(11); + widget_grid_rowspan_input->labelcolor(FL_FOREGROUND_COLOR); + widget_grid_rowspan_input->textsize(11); + widget_grid_rowspan_input->callback((Fl_Callback*)cb_widget_grid_rowspan_input); + widget_grid_rowspan_input->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + widget_grid_rowspan_input->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* widget_grid_rowspan_input + { Fl_Group* o = new Fl_Group(125, 140, 30, 20); + { Fl_Button* o = new Fl_Button(125, 140, 15, 20, "-"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_4); + o->clear_visible_focus(); + } // Fl_Button* o + { Fl_Button* o = new Fl_Button(140, 140, 15, 20, "+"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_5); + o->clear_visible_focus(); + } // Fl_Button* o + o->end(); + } // Fl_Group* o + { widget_grid_colspan_input = new fld::widget::Formula_Input(165, 140, 40, 20, "Col. Span:"); + widget_grid_colspan_input->box(FL_DOWN_BOX); + widget_grid_colspan_input->color(FL_BACKGROUND2_COLOR); + widget_grid_colspan_input->selection_color(FL_SELECTION_COLOR); + widget_grid_colspan_input->labeltype(FL_NORMAL_LABEL); + widget_grid_colspan_input->labelfont(0); + widget_grid_colspan_input->labelsize(11); + widget_grid_colspan_input->labelcolor(FL_FOREGROUND_COLOR); + widget_grid_colspan_input->textsize(11); + widget_grid_colspan_input->callback((Fl_Callback*)cb_widget_grid_colspan_input); + widget_grid_colspan_input->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + widget_grid_colspan_input->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* widget_grid_colspan_input + { Fl_Group* o = new Fl_Group(205, 140, 30, 20); + { Fl_Button* o = new Fl_Button(205, 140, 15, 20, "-"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_6); + o->clear_visible_focus(); + } // Fl_Button* o + { Fl_Button* o = new Fl_Button(220, 140, 15, 20, "+"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_7); + o->clear_visible_focus(); + } // Fl_Button* o + o->end(); + } // Fl_Group* o + { Fl_Box* o = new Fl_Box(385, 140, 1, 20); + o->hide(); + Fl_Group::current()->resizable(o); + } // Fl_Box* o + o->end(); + } // Fl_Group* o + { Fl_Box* o = new Fl_Box(85, 320, 300, 5); + o->labelsize(11); + o->hide(); + Fl_Group::current()->resizable(o); + } // Fl_Box* o + end(); + resize(X, Y, W, H); +} + +void Grid_Child_Tab::grid_child_cb(fld::widget::Formula_Input* i, void* v, int what) { + if ( !current_widget + || !current_widget->parent + || !current_widget->parent->is_a(Type::Grid)) + { + return; + } + Fl_Widget *child = ((Widget_Node*)current_widget)->o; + Fl_Grid_Proxy *g = ((Fl_Grid_Proxy*)((Widget_Node*)current_widget->parent)->o); + Fl_Grid::Cell *cell = g->any_cell(child); + if (v == LOAD) { + int v = -1; + if (cell) { + switch (what & 0x00ff) { + case 8: v = cell->row(); break; + case 9: v = cell->col(); break; + case 10: v = cell->rowspan(); break; + case 11: v = cell->colspan(); break; + case 12: cell->minimum_size(&v, nullptr); break; + case 13: cell->minimum_size(nullptr, &v); break; + } + } + i->value(v); + } else { + Fluid.proj.undo.checkpoint(); + int v2 = -2, old_v = -2, v = i->value(); + if (i==widget_grid_row_input) v2 = widget_grid_col_input->value(); + if (i==widget_grid_col_input) v2 = widget_grid_row_input->value(); + Fl_Grid::Cell *new_cell = nullptr; + if (cell) { + switch (what & 0x00ff) { + case 8: old_v = cell->row(); v2 = cell->col(); break; + case 9: old_v = cell->col(); v2 = cell->row(); break; + case 10: old_v = cell->rowspan(); break; + case 11: old_v = cell->colspan(); break; + case 12: cell->minimum_size(&old_v, &v2); break; + case 13: cell->minimum_size(&v2, &old_v); break; + } + } + switch (what & 0xff00) { + case 0x0100: v--; break; + case 0x0200: v++; break; + } + if (old_v != v) { + switch (what & 0x00ff) { + case 8: + if (v2 == -1 && v >= 0) v2 = 0; + g->move_cell(current_widget->o, v, v2, 2); i->value(v); + break; + case 9: + if (v2 == -1 && v >= 0) v2 = 0; + g->move_cell(current_widget->o, v2, v, 2); i->value(v); + break; + case 10: if (cell && cell->row()+v<=g->rows() && v>0) cell->rowspan(v); + break; + case 11: if (cell && cell->col()+v<=g->cols() && v>0) cell->colspan(v); + break; + case 12: if (cell && v>=0) cell->minimum_size(v, v2); + break; + case 13: if (cell && v>=0) cell->minimum_size(v2, v); + break; + } + if (!cell && new_cell) + new_cell->minimum_size(20, 20); + g->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} diff --git a/fluid/panels/widget_panel/Grid_Child_Tab.fl b/fluid/panels/widget_panel/Grid_Child_Tab.fl new file mode 100644 index 000000000..161eab696 --- /dev/null +++ b/fluid/panels/widget_panel/Grid_Child_Tab.fl @@ -0,0 +1,397 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0500 +header_name {.h} +code_name {.cxx} +decl {\#include "widgets/Formula_Input.h"} {public global +} + +decl {\#include "Fluid.h"} {private global +} + +decl {\#include "proj/undo.h"} {private global +} + +decl {\#include "nodes/Grid_Node.h"} {private global +} + +decl {extern Grid_Child_Tab *widget_tab_grid_child;} {private global +} + +widget_class Grid_Child_Tab { + label Grid + callback propagate_load open + xywh {480 287 400 330} labelsize 11 resizable visible position_relative_rescale +} { + Fl_Group {} { + label {Location:} + callback propagate_load open + xywh {85 30 315 20} box FLAT_BOX labelfont 1 labelsize 11 align 4 + } { + Fl_Input widget_grid_row_input { + label {Row:} + callback {grid_child_cb(o, v, 8); + if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD);} + xywh {85 30 40 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Group {} {open + xywh {125 30 30 20} + } { + Fl_Button {} { + label {-} + callback {if (v!=LOAD) { + grid_child_cb(widget_grid_row_input, v, 0x0100 + 8); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + }} + xywh {125 30 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + Fl_Button {} { + label {+} + callback {if (v!=LOAD) { + grid_child_cb(widget_grid_row_input, v, 0x0200 + 8); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + }} + xywh {140 30 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + } + Fl_Input widget_grid_col_input { + label {Column:} + callback {grid_child_cb(o, v, 9); + if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD);} + xywh {165 30 40 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Group {} {open + xywh {205 30 30 20} + } { + Fl_Button {} { + label {-} + callback {if (v!=LOAD) { + grid_child_cb(widget_grid_col_input, v, 0x0100 + 9); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + }} + xywh {205 30 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + Fl_Button {} { + label {+} + callback {if (v!=LOAD) { + grid_child_cb(widget_grid_col_input, v, 0x0200 + 9); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + }} + xywh {220 30 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + } + Fl_Box {} { + xywh {385 30 1 20} hide resizable + } + Fl_Box widget_grid_transient { + label TRANSIENT + callback {if (v==LOAD) { + Fl_Widget *child = ((Widget_Node*)current_widget)->o; + Fl_Grid_Proxy *g = ((Fl_Grid_Proxy*)((Widget_Node*)current_widget->parent)->o); +// Fl_Grid::Cell *cell = g->cell(child); +// Fl_Grid::Cell *tcell = g->transient_cell(child); + widget_grid_transient->hide(); + widget_grid_unlinked->hide(); + if (g->transient_cell(child)) { + widget_grid_transient->show(); + } else if (!g->cell(child)) { + widget_grid_unlinked->show(); + } +}} + xywh {240 30 80 20} labelsize 11 labelcolor 1 + } + Fl_Box widget_grid_unlinked { + label UNLINKED + xywh {240 30 80 20} labelsize 11 labelcolor 1 hide + } + } + Fl_Group wp_gridc_align { + label {Align:} + callback propagate_load + xywh {85 70 315 20} labelfont 1 labelsize 11 align 4 + } { + Fl_Choice {} { + label Horizontal + callback {if ( !current_widget + || !current_widget->parent + || !current_widget->parent->is_a(Type::Grid)) + { + return; + } + int mask = (FL_GRID_LEFT | FL_GRID_RIGHT | FL_GRID_HORIZONTAL); + Fl_Grid *g = ((Fl_Grid*)((Widget_Node*)current_widget->parent)->o); + if (v == LOAD) { + int a = FL_GRID_FILL & mask; + Fl_Grid::Cell *cell = g->cell(current_widget->o); + if (cell) { + a = cell->align() & mask; + } + const Fl_Menu_Item *mi = o->find_item_with_argument(a); + if (mi) o->value(mi); + } else { + Fluid.proj.undo.checkpoint(); + int v = FL_GRID_FILL & mask, old_v = FL_GRID_FILL & mask; + const Fl_Menu_Item *mi = o->mvalue(); + if (mi) v = (int)mi->argument(); + Fl_Grid::Cell *cell = g->cell(current_widget->o); + if (cell) { + old_v = cell->align() & mask; + if (old_v != v) { + cell->align((Fl_Grid_Align)(v | (cell->align() & ~mask))); + g->need_layout(true); + g->redraw(); + Fluid.proj.set_modflag(1); + } + } + }} open + xywh {85 70 115 20} down_box BORDER_BOX labelsize 11 align 5 textsize 11 + } { + MenuItem GRID_LEFT { + label GRID_LEFT + user_data {(fl_intptr_t)FL_GRID_LEFT} user_data_type long + xywh {20 20 31 20} labelsize 11 + } + MenuItem {} { + label GRID_CENTER + user_data {(fl_intptr_t)FL_GRID_CENTER} user_data_type long + xywh {20 20 31 20} labelsize 11 + } + MenuItem {} { + label GRID_RIGHT + user_data {(fl_intptr_t)FL_GRID_RIGHT} user_data_type long + xywh {20 20 31 20} labelsize 11 + } + MenuItem {} { + label GRID_FILL + user_data {(fl_intptr_t)FL_GRID_HORIZONTAL} user_data_type long + xywh {20 20 31 20} labelsize 11 + } + } + Fl_Choice {} { + label Vertical + callback {if ( !current_widget + || !current_widget->parent + || !current_widget->parent->is_a(Type::Grid)) + { + return; + } + int mask = (FL_GRID_TOP | FL_GRID_BOTTOM | FL_GRID_VERTICAL); + Fl_Grid *g = ((Fl_Grid*)((Widget_Node*)current_widget->parent)->o); + if (v == LOAD) { + int a = FL_GRID_FILL & mask; + Fl_Grid::Cell *cell = g->cell(current_widget->o); + if (cell) { + a = cell->align() & mask; + } + const Fl_Menu_Item *mi = o->find_item_with_argument(a); + if (mi) o->value(mi); + } else { + Fluid.proj.undo.checkpoint(); + int v = FL_GRID_FILL & mask, old_v = FL_GRID_FILL & mask; + const Fl_Menu_Item *mi = o->mvalue(); + if (mi) v = (int)mi->argument(); + Fl_Grid::Cell *cell = g->cell(current_widget->o); + if (cell) { + old_v = cell->align() & mask; + if (old_v != v) { + cell->align((Fl_Grid_Align)(v | (cell->align() & ~mask))); + g->need_layout(true); + g->redraw(); + Fluid.proj.set_modflag(1); + } + } + }} open selected + xywh {205 70 115 20} down_box BORDER_BOX labelsize 11 align 5 textsize 11 + } { + MenuItem {} { + label GRID_TOP + user_data {(fl_intptr_t)FL_GRID_TOP} user_data_type long + xywh {20 20 31 20} labelsize 11 + } + MenuItem {} { + label GRID_CENTER + user_data {(fl_intptr_t)FL_GRID_CENTER} user_data_type long + xywh {20 20 31 20} labelsize 11 + } + MenuItem {} { + label GRID_BOTTOM + user_data {(fl_intptr_t)FL_GRID_BOTTOM} user_data_type long + xywh {20 20 31 20} labelsize 11 + } + MenuItem {} { + label GRID_FILL + user_data {(fl_intptr_t)FL_GRID_VERTICAL} user_data_type long + xywh {20 20 31 20} labelsize 11 + } + } + Fl_Box {} { + xywh {385 70 1 20} hide resizable + } + } + Fl_Group wp_gridc_size { + label {Min. Size:} + callback propagate_load + xywh {85 105 315 20} labelfont 1 labelsize 11 align 4 + } { + Fl_Input {} { + label {Width:} + callback {grid_child_cb(o, v, 12);} + xywh {85 105 55 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Input {} { + label {Height:} + callback {grid_child_cb(o, v, 13);} + xywh {145 105 55 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Box {} { + xywh {385 105 1 20} hide resizable + } + } + Fl_Group {} { + label {Span:} + callback propagate_load + xywh {85 140 315 20} labelfont 1 labelsize 11 align 4 + } { + Fl_Input widget_grid_rowspan_input { + label {Row Span:} + callback {grid_child_cb(o, v, 10); + if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD);} + xywh {85 140 40 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Group {} {open + xywh {125 140 30 20} + } { + Fl_Button {} { + label {-} + callback {if (v!=LOAD) { + grid_child_cb(widget_grid_rowspan_input, v, 0x0100 + 10); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + }} + xywh {125 140 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + Fl_Button {} { + label {+} + callback {if (v!=LOAD) { + grid_child_cb(widget_grid_rowspan_input, v, 0x0200 + 10); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + }} + xywh {140 140 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + } + Fl_Input widget_grid_colspan_input { + label {Col. Span:} + callback {grid_child_cb(o, v, 11); + if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD);} + xywh {165 140 40 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Group {} {open + xywh {205 140 30 20} + } { + Fl_Button {} { + label {-} + callback {if (v!=LOAD) { + grid_child_cb(widget_grid_colspan_input, v, 0x0100 + 11); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + }} + xywh {205 140 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + Fl_Button {} { + label {+} + callback {if (v!=LOAD) { + grid_child_cb(widget_grid_colspan_input, v, 0x0200 + 11); + widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); + }} + xywh {220 140 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + } + Fl_Box {} { + xywh {385 140 1 20} hide resizable + } + } + Fl_Box {} { + xywh {85 320 300 5} labelsize 11 hide resizable + } + Function {grid_child_cb(fld::widget::Formula_Input* i, void* v, int what)} {open return_type void + } { + code {if ( !current_widget + || !current_widget->parent + || !current_widget->parent->is_a(Type::Grid)) + { + return; + } + Fl_Widget *child = ((Widget_Node*)current_widget)->o; + Fl_Grid_Proxy *g = ((Fl_Grid_Proxy*)((Widget_Node*)current_widget->parent)->o); + Fl_Grid::Cell *cell = g->any_cell(child); + if (v == LOAD) { + int v = -1; + if (cell) { + switch (what & 0x00ff) { + case 8: v = cell->row(); break; + case 9: v = cell->col(); break; + case 10: v = cell->rowspan(); break; + case 11: v = cell->colspan(); break; + case 12: cell->minimum_size(&v, nullptr); break; + case 13: cell->minimum_size(nullptr, &v); break; + } + } + i->value(v); + } else { + Fluid.proj.undo.checkpoint(); + int v2 = -2, old_v = -2, v = i->value(); + if (i==widget_grid_row_input) v2 = widget_grid_col_input->value(); + if (i==widget_grid_col_input) v2 = widget_grid_row_input->value(); + Fl_Grid::Cell *new_cell = nullptr; + if (cell) { + switch (what & 0x00ff) { + case 8: old_v = cell->row(); v2 = cell->col(); break; + case 9: old_v = cell->col(); v2 = cell->row(); break; + case 10: old_v = cell->rowspan(); break; + case 11: old_v = cell->colspan(); break; + case 12: cell->minimum_size(&old_v, &v2); break; + case 13: cell->minimum_size(&v2, &old_v); break; + } + } + switch (what & 0xff00) { + case 0x0100: v--; break; + case 0x0200: v++; break; + } + if (old_v != v) { + switch (what & 0x00ff) { + case 8: + if (v2 == -1 && v >= 0) v2 = 0; + g->move_cell(current_widget->o, v, v2, 2); i->value(v); + break; + case 9: + if (v2 == -1 && v >= 0) v2 = 0; + g->move_cell(current_widget->o, v2, v, 2); i->value(v); + break; + case 10: if (cell && cell->row()+v<=g->rows() && v>0) cell->rowspan(v); + break; + case 11: if (cell && cell->col()+v<=g->cols() && v>0) cell->colspan(v); + break; + case 12: if (cell && v>=0) cell->minimum_size(v, v2); + break; + case 13: if (cell && v>=0) cell->minimum_size(v2, v); + break; + } + if (!cell && new_cell) + new_cell->minimum_size(20, 20); + g->need_layout(true); + Fluid.proj.set_modflag(1); + } + }} {} + } +} diff --git a/fluid/panels/widget_panel/Grid_Child_Tab.h b/fluid/panels/widget_panel/Grid_Child_Tab.h new file mode 100644 index 000000000..93cfda78b --- /dev/null +++ b/fluid/panels/widget_panel/Grid_Child_Tab.h @@ -0,0 +1,79 @@ +// generated by Fast Light User Interface Designer (fluid) version 1.0500 + +#ifndef Grid_Child_Tab_h +#define Grid_Child_Tab_h +#include +#include "widgets/Formula_Input.h" +#include +extern void propagate_load(Fl_Group*, void*); +#include +#include +#include + +class Grid_Child_Tab : public Fl_Group { +public: + Grid_Child_Tab(int X, int Y, int W, int H, const char *L = 0); + fld::widget::Formula_Input *widget_grid_row_input; +private: + inline void cb_widget_grid_row_input_i(fld::widget::Formula_Input*, void*); + static void cb_widget_grid_row_input(fld::widget::Formula_Input*, void*); + inline void cb__i(Fl_Button*, void*); + static void cb_(Fl_Button*, void*); + inline void cb_1_i(Fl_Button*, void*); + static void cb_1(Fl_Button*, void*); +public: + fld::widget::Formula_Input *widget_grid_col_input; +private: + inline void cb_widget_grid_col_input_i(fld::widget::Formula_Input*, void*); + static void cb_widget_grid_col_input(fld::widget::Formula_Input*, void*); + inline void cb_2_i(Fl_Button*, void*); + static void cb_2(Fl_Button*, void*); + inline void cb_3_i(Fl_Button*, void*); + static void cb_3(Fl_Button*, void*); +public: + Fl_Box *widget_grid_transient; +private: + inline void cb_widget_grid_transient_i(Fl_Box*, void*); + static void cb_widget_grid_transient(Fl_Box*, void*); +public: + Fl_Box *widget_grid_unlinked; + Fl_Group *wp_gridc_align; +private: + inline void cb_Horizontal_i(Fl_Choice*, void*); + static void cb_Horizontal(Fl_Choice*, void*); + static Fl_Menu_Item menu_Horizontal[]; +public: + static Fl_Menu_Item *GRID_LEFT; +private: + inline void cb_Vertical_i(Fl_Choice*, void*); + static void cb_Vertical(Fl_Choice*, void*); + static Fl_Menu_Item menu_Vertical[]; +public: + Fl_Group *wp_gridc_size; +private: + inline void cb_Width_i(fld::widget::Formula_Input*, void*); + static void cb_Width(fld::widget::Formula_Input*, void*); + inline void cb_Height_i(fld::widget::Formula_Input*, void*); + static void cb_Height(fld::widget::Formula_Input*, void*); +public: + fld::widget::Formula_Input *widget_grid_rowspan_input; +private: + inline void cb_widget_grid_rowspan_input_i(fld::widget::Formula_Input*, void*); + static void cb_widget_grid_rowspan_input(fld::widget::Formula_Input*, void*); + inline void cb_4_i(Fl_Button*, void*); + static void cb_4(Fl_Button*, void*); + inline void cb_5_i(Fl_Button*, void*); + static void cb_5(Fl_Button*, void*); +public: + fld::widget::Formula_Input *widget_grid_colspan_input; +private: + inline void cb_widget_grid_colspan_input_i(fld::widget::Formula_Input*, void*); + static void cb_widget_grid_colspan_input(fld::widget::Formula_Input*, void*); + inline void cb_6_i(Fl_Button*, void*); + static void cb_6(Fl_Button*, void*); + inline void cb_7_i(Fl_Button*, void*); + static void cb_7(Fl_Button*, void*); +public: + void grid_child_cb(fld::widget::Formula_Input* i, void* v, int what); +}; +#endif diff --git a/fluid/panels/widget_panel/Grid_Tab.cxx b/fluid/panels/widget_panel/Grid_Tab.cxx new file mode 100644 index 000000000..756d957ac --- /dev/null +++ b/fluid/panels/widget_panel/Grid_Tab.cxx @@ -0,0 +1,778 @@ +// generated by Fast Light User Interface Designer (fluid) version 1.0500 + +#include "Grid_Tab.h" +#include "Fluid.h" +#include "proj/undo.h" +#include "nodes/Grid_Node.h" +extern Grid_Tab *widget_tab_grid; + +void Grid_Tab::cb_widget_grid_rows_i(fld::widget::Formula_Input* o, void* v) { + // grid_rows_cb + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + if (v == LOAD) { + o->value(grid->rows()); + } else { + int m = o->value(), old_m = grid->rows(); + if (m < 1) { + m = 1; + o->value(m); + } + if (m < old_m) { + // TODO: verify that this will not unlink existings cells + // Offer a dialog with "delete children", "unlink cells", "cancel" + } + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->layout(m, grid->cols()); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + widget_tab_grid->do_callback(widget_tab_grid, LOAD); + } + } +} +void Grid_Tab::cb_widget_grid_rows(fld::widget::Formula_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()))->cb_widget_grid_rows_i(o,v); +} + +void Grid_Tab::cb__i(Fl_Button*, void* v) { + if (v != LOAD) { + widget_grid_rows->value( widget_grid_rows->value()-1 ); + widget_grid_rows->do_callback(); + } +} +void Grid_Tab::cb_(Fl_Button* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb__i(o,v); +} + +void Grid_Tab::cb_1_i(Fl_Button*, void* v) { + if (v != LOAD) { + widget_grid_rows->value( widget_grid_rows->value()+1 ); + widget_grid_rows->do_callback(); + } +} +void Grid_Tab::cb_1(Fl_Button* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_1_i(o,v); +} + +void Grid_Tab::cb_widget_grid_cols_i(fld::widget::Formula_Input* o, void* v) { + // grid_rows_cb + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + if (v == LOAD) { + o->value(grid->cols()); + } else { + int m = o->value(), old_m = grid->cols(); + if (m < 1) { + m = 1; + o->value(m); + } + if (m < old_m) { + // TODO: verify that this will not unlink existings cells + // Offer a dialog with "delete children", "unlink cells", "cancel" + } + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->layout(grid->rows(), m); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + widget_tab_grid->do_callback(widget_tab_grid, LOAD); + } + } +} +void Grid_Tab::cb_widget_grid_cols(fld::widget::Formula_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()))->cb_widget_grid_cols_i(o,v); +} + +void Grid_Tab::cb_2_i(Fl_Button*, void* v) { + if (v != LOAD) { + widget_grid_cols->value( widget_grid_cols->value()-1 ); + widget_grid_cols->do_callback(); + } +} +void Grid_Tab::cb_2(Fl_Button* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_2_i(o,v); +} + +void Grid_Tab::cb_3_i(Fl_Button*, void* v) { + if (v != LOAD) { + widget_grid_cols->value( widget_grid_cols->value()+1 ); + widget_grid_cols->do_callback(); + } +} +void Grid_Tab::cb_3(Fl_Button* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_3_i(o,v); +} + +void Grid_Tab::cb_Left_i(Fl_Value_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + int m = 0; + if (v == LOAD) { + grid->margin(&m, nullptr, nullptr, nullptr); + o->value(m); + } else { + int m = (int)o->value(), old_m; + grid->margin(&old_m, nullptr, nullptr, nullptr); + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->margin(m, -1, -1, -1); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} +void Grid_Tab::cb_Left(Fl_Value_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()))->cb_Left_i(o,v); +} + +void Grid_Tab::cb_Top_i(Fl_Value_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + int m = 0; + if (v == LOAD) { + grid->margin(nullptr, &m, nullptr, nullptr); + o->value(m); + } else { + int m = (int)o->value(), old_m; + grid->margin(nullptr, &old_m, nullptr, nullptr); + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->margin(-1, m, -1, -1); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} +void Grid_Tab::cb_Top(Fl_Value_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()))->cb_Top_i(o,v); +} + +void Grid_Tab::cb_Right_i(Fl_Value_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + int m = 0; + if (v == LOAD) { + grid->margin(nullptr, nullptr, &m, nullptr); + o->value(m); + } else { + int m = (int)o->value(), old_m; + grid->margin(nullptr, nullptr, &old_m, nullptr); + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->margin(-1, -1, m, -1); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} +void Grid_Tab::cb_Right(Fl_Value_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()))->cb_Right_i(o,v); +} + +void Grid_Tab::cb_Bottom_i(Fl_Value_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + int m = 0; + if (v == LOAD) { + grid->margin(nullptr, nullptr, nullptr, &m); + o->value(m); + } else { + int m = (int)o->value(), old_m; + grid->margin(nullptr, nullptr, nullptr, &old_m); + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->margin(-1, -1, -1, m); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} +void Grid_Tab::cb_Bottom(Fl_Value_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()))->cb_Bottom_i(o,v); +} + +void Grid_Tab::cb_Row_i(Fl_Value_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + if (v == LOAD) { + int m = 0; + grid->gap(&m, nullptr); + o->value(m); + } else { + int m = (int)o->value(), old_m, m2; + grid->gap(&old_m, &m2); + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->gap(m, m2); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} +void Grid_Tab::cb_Row(Fl_Value_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()))->cb_Row_i(o,v); +} + +void Grid_Tab::cb_Col_i(Fl_Value_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + if (v == LOAD) { + int m = 0; + grid->gap(nullptr, &m); + o->value(m); + } else { + int m = (int)o->value(), old_m, m2; + grid->gap(&m2, &old_m); + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->gap(m2, m); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} +void Grid_Tab::cb_Col(Fl_Value_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()))->cb_Col_i(o,v); +} + +void Grid_Tab::cb_Row1_i(Fl_Group* o, void* v) { + if (v == LOAD) { + Fl_Grid *grid = Grid_Node::selected(); + if (grid) + o->activate(); + else + o->deactivate(); + propagate_load(o, v); + } +} +void Grid_Tab::cb_Row1(Fl_Group* o, void* v) { + ((Grid_Tab*)(o->parent()))->cb_Row1_i(o,v); +} + +void Grid_Tab::cb_widget_grid_curr_row_i(fld::widget::Formula_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + int r = o->value(), old_r = r; + if (r < 0) r = 0; + if (r >= grid->rows()) r = grid->rows()-1; + if (r != old_r) o->value(r); + if (v == LOAD) { + // will automatically propagate + } else { + widget_grid_curr_row_attributes->do_callback(widget_grid_curr_row_attributes, LOAD); + } +} +void Grid_Tab::cb_widget_grid_curr_row(fld::widget::Formula_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()))->cb_widget_grid_curr_row_i(o,v); +} + +void Grid_Tab::cb_4_i(Fl_Button*, void* v) { + if (v != LOAD) { + widget_grid_curr_row->value( widget_grid_curr_row->value()-1 ); + widget_grid_curr_row->do_callback(); + } +} +void Grid_Tab::cb_4(Fl_Button* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_4_i(o,v); +} + +void Grid_Tab::cb_5_i(Fl_Button*, void* v) { + if (v != LOAD) { + widget_grid_curr_row->value( widget_grid_curr_row->value()+1 ); + widget_grid_curr_row->do_callback(); + } +} +void Grid_Tab::cb_5(Fl_Button* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_5_i(o,v); +} + +void Grid_Tab::cb_Height_i(fld::widget::Formula_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + int r = widget_grid_curr_row->value(); + if (v == LOAD) { + o->value(grid->row_height(r)); + } else { + int h = o->value(), old_h = grid->row_height(r); + if (h < 0) h = 0; + if (h != old_h) { + Fluid.proj.undo.checkpoint(); + grid->row_height(r, h); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} +void Grid_Tab::cb_Height(fld::widget::Formula_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_Height_i(o,v); +} + +void Grid_Tab::cb_Weight_i(fld::widget::Formula_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + int r = widget_grid_curr_row->value(); + if (v == LOAD) { + o->value(grid->row_weight(r)); + } else { + int h = o->value(), old_h = grid->row_weight(r); + if (h < 0) h = 0; + if (h != old_h) { + Fluid.proj.undo.checkpoint(); + grid->row_weight(r, h); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} +void Grid_Tab::cb_Weight(fld::widget::Formula_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_Weight_i(o,v); +} + +void Grid_Tab::cb_Gap_i(fld::widget::Formula_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + int r = widget_grid_curr_row->value(); + if (v == LOAD) { + o->value(grid->row_gap(r)); + } else { + int h = o->value(), old_h = grid->row_gap(r); + if (h < -1) h = -1; + if (h != old_h) { + Fluid.proj.undo.checkpoint(); + grid->row_gap(r, h); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} +void Grid_Tab::cb_Gap(fld::widget::Formula_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_Gap_i(o,v); +} + +void Grid_Tab::cb_widget_grid_curr_col_i(fld::widget::Formula_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + int c = o->value(), old_c = c; + if (c < 0) c = 0; + if (c >= grid->cols()) c = grid->cols()-1; + if (c != old_c) o->value(c); + if (v == LOAD) { + // will automatically propagate + } else { + widget_grid_curr_col_attributes->do_callback(widget_grid_curr_col_attributes, LOAD); + } +} +void Grid_Tab::cb_widget_grid_curr_col(fld::widget::Formula_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()))->cb_widget_grid_curr_col_i(o,v); +} + +void Grid_Tab::cb_6_i(Fl_Button*, void* v) { + if (v != LOAD) { + widget_grid_curr_col->value( widget_grid_curr_col->value()-1 ); + widget_grid_curr_col->do_callback(); + } +} +void Grid_Tab::cb_6(Fl_Button* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_6_i(o,v); +} + +void Grid_Tab::cb_7_i(Fl_Button*, void* v) { + if (v != LOAD) { + widget_grid_curr_col->value( widget_grid_curr_col->value()+1 ); + widget_grid_curr_col->do_callback(); + } +} +void Grid_Tab::cb_7(Fl_Button* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_7_i(o,v); +} + +void Grid_Tab::cb_Width_i(fld::widget::Formula_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + int c = widget_grid_curr_col->value(); + if (v == LOAD) { + o->value(grid->col_width(c)); + } else { + int h = o->value(), old_h = grid->col_width(c); + if (h < 0) h = 0; + if (h != old_h) { + Fluid.proj.undo.checkpoint(); + grid->col_width(c, h); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} +void Grid_Tab::cb_Width(fld::widget::Formula_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_Width_i(o,v); +} + +void Grid_Tab::cb_Weight1_i(fld::widget::Formula_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + int c = widget_grid_curr_col->value(); + if (v == LOAD) { + o->value(grid->col_weight(c)); + } else { + int h = o->value(), old_h = grid->col_weight(c); + if (h < 0) h = 0; + if (h != old_h) { + Fluid.proj.undo.checkpoint(); + grid->col_weight(c, h); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} +void Grid_Tab::cb_Weight1(fld::widget::Formula_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_Weight1_i(o,v); +} + +void Grid_Tab::cb_Gap1_i(fld::widget::Formula_Input* o, void* v) { + Fl_Grid *grid = Grid_Node::selected(); + if (!grid) return; + int c = widget_grid_curr_col->value(); + if (v == LOAD) { + o->value(grid->col_gap(c)); + } else { + int h = o->value(), old_h = grid->col_gap(c); + if (h < -1) h = -1; + if (h != old_h) { + Fluid.proj.undo.checkpoint(); + grid->col_gap(c, h); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } + } +} +void Grid_Tab::cb_Gap1(fld::widget::Formula_Input* o, void* v) { + ((Grid_Tab*)(o->parent()->parent()->parent()))->cb_Gap1_i(o,v); +} + +Grid_Tab::Grid_Tab(int X, int Y, int W, int H, const char *L) : + Fl_Group(0, 0, 400, 330, L) +{ + this->labelsize(11); + this->callback((Fl_Callback*)propagate_load); + { Fl_Group* o = new Fl_Group(85, 30, 315, 20, "Grid Layout:"); + o->labelfont(1); + o->labelsize(11); + o->callback((Fl_Callback*)propagate_load); + o->align(Fl_Align(FL_ALIGN_LEFT)); + { widget_grid_rows = new fld::widget::Formula_Input(85, 30, 40, 20, "Rows:"); + widget_grid_rows->tooltip("Number of horizontal rows in the Grid group"); + widget_grid_rows->box(FL_DOWN_BOX); + widget_grid_rows->color(FL_BACKGROUND2_COLOR); + widget_grid_rows->selection_color(FL_SELECTION_COLOR); + widget_grid_rows->labeltype(FL_NORMAL_LABEL); + widget_grid_rows->labelfont(0); + widget_grid_rows->labelsize(11); + widget_grid_rows->labelcolor(FL_FOREGROUND_COLOR); + widget_grid_rows->textsize(11); + widget_grid_rows->callback((Fl_Callback*)cb_widget_grid_rows); + widget_grid_rows->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + widget_grid_rows->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* widget_grid_rows + { Fl_Group* o = new Fl_Group(125, 30, 30, 20); + { Fl_Button* o = new Fl_Button(125, 30, 15, 20, "-"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_); + o->clear_visible_focus(); + } // Fl_Button* o + { Fl_Button* o = new Fl_Button(140, 30, 15, 20, "+"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_1); + o->clear_visible_focus(); + } // Fl_Button* o + o->end(); + } // Fl_Group* o + { widget_grid_cols = new fld::widget::Formula_Input(165, 30, 40, 20, "Columns:"); + widget_grid_cols->tooltip("Number of vertical columns in the Grid group"); + widget_grid_cols->box(FL_DOWN_BOX); + widget_grid_cols->color(FL_BACKGROUND2_COLOR); + widget_grid_cols->selection_color(FL_SELECTION_COLOR); + widget_grid_cols->labeltype(FL_NORMAL_LABEL); + widget_grid_cols->labelfont(0); + widget_grid_cols->labelsize(11); + widget_grid_cols->labelcolor(FL_FOREGROUND_COLOR); + widget_grid_cols->textsize(11); + widget_grid_cols->callback((Fl_Callback*)cb_widget_grid_cols); + widget_grid_cols->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + widget_grid_cols->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* widget_grid_cols + { Fl_Group* o = new Fl_Group(205, 30, 30, 20); + { Fl_Button* o = new Fl_Button(205, 30, 15, 20, "-"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_2); + o->clear_visible_focus(); + } // Fl_Button* o + { Fl_Button* o = new Fl_Button(220, 30, 15, 20, "+"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_3); + o->clear_visible_focus(); + } // Fl_Button* o + o->end(); + } // Fl_Group* o + { Fl_Box* o = new Fl_Box(386, 30, 0, 20); + Fl_Group::current()->resizable(o); + } // Fl_Box* o + o->end(); + } // Fl_Group* o + { wp_grid_margin = new Fl_Group(85, 70, 315, 20, "Margins:"); + wp_grid_margin->labelfont(1); + wp_grid_margin->labelsize(11); + wp_grid_margin->callback((Fl_Callback*)propagate_load); + wp_grid_margin->align(Fl_Align(FL_ALIGN_LEFT)); + { Fl_Value_Input* o = new Fl_Value_Input(85, 70, 55, 20, "Left:"); + o->tooltip("Left margin in group."); + o->labelsize(11); + o->maximum(1000); + o->step(1); + o->textsize(11); + o->callback((Fl_Callback*)cb_Left); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + } // Fl_Value_Input* o + { Fl_Value_Input* o = new Fl_Value_Input(145, 70, 55, 20, "Top:"); + o->tooltip("Top margin in group."); + o->labelsize(11); + o->maximum(1000); + o->step(1); + o->textsize(11); + o->callback((Fl_Callback*)cb_Top); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + } // Fl_Value_Input* o + { Fl_Value_Input* o = new Fl_Value_Input(205, 70, 55, 20, "Right:"); + o->tooltip("Right margin in group."); + o->labelsize(11); + o->maximum(1000); + o->step(1); + o->textsize(11); + o->callback((Fl_Callback*)cb_Right); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + } // Fl_Value_Input* o + { Fl_Value_Input* o = new Fl_Value_Input(265, 70, 55, 20, "Bottom:"); + o->tooltip("Bottom margin in group."); + o->labelsize(11); + o->maximum(1000); + o->step(1); + o->textsize(11); + o->callback((Fl_Callback*)cb_Bottom); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + } // Fl_Value_Input* o + { Fl_Box* o = new Fl_Box(386, 70, 0, 20); + Fl_Group::current()->resizable(o); + } // Fl_Box* o + wp_grid_margin->end(); + } // Fl_Group* wp_grid_margin + { wp_grid_gaps = new Fl_Group(85, 105, 315, 20, "Gaps:"); + wp_grid_gaps->labelfont(1); + wp_grid_gaps->labelsize(11); + wp_grid_gaps->callback((Fl_Callback*)propagate_load); + wp_grid_gaps->align(Fl_Align(FL_ALIGN_LEFT)); + { Fl_Value_Input* o = new Fl_Value_Input(85, 105, 55, 20, "Row:"); + o->tooltip("Gap between children."); + o->labelsize(11); + o->maximum(1000); + o->step(1); + o->textsize(11); + o->callback((Fl_Callback*)cb_Row); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + } // Fl_Value_Input* o + { Fl_Value_Input* o = new Fl_Value_Input(145, 105, 55, 20, "Col:"); + o->tooltip("Gap between children."); + o->labelsize(11); + o->maximum(1000); + o->step(1); + o->textsize(11); + o->callback((Fl_Callback*)cb_Col); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + } // Fl_Value_Input* o + { Fl_Box* o = new Fl_Box(386, 105, 0, 20); + Fl_Group::current()->resizable(o); + } // Fl_Box* o + wp_grid_gaps->end(); + } // Fl_Group* wp_grid_gaps + { Fl_Group* o = new Fl_Group(85, 145, 315, 20, "Row:"); + o->labelfont(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_Row1); + o->align(Fl_Align(FL_ALIGN_LEFT)); + { widget_grid_curr_row = new fld::widget::Formula_Input(85, 145, 40, 20, "Index"); + widget_grid_curr_row->box(FL_DOWN_BOX); + widget_grid_curr_row->color(FL_BACKGROUND2_COLOR); + widget_grid_curr_row->selection_color(FL_SELECTION_COLOR); + widget_grid_curr_row->labeltype(FL_NORMAL_LABEL); + widget_grid_curr_row->labelfont(0); + widget_grid_curr_row->labelsize(11); + widget_grid_curr_row->labelcolor(FL_FOREGROUND_COLOR); + widget_grid_curr_row->textsize(11); + widget_grid_curr_row->callback((Fl_Callback*)cb_widget_grid_curr_row); + widget_grid_curr_row->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + widget_grid_curr_row->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* widget_grid_curr_row + { Fl_Group* o = new Fl_Group(125, 145, 30, 20); + o->callback((Fl_Callback*)propagate_load); + { Fl_Button* o = new Fl_Button(125, 145, 15, 20, "-"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_4); + o->clear_visible_focus(); + } // Fl_Button* o + { Fl_Button* o = new Fl_Button(140, 145, 15, 20, "+"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_5); + o->clear_visible_focus(); + } // Fl_Button* o + o->end(); + } // Fl_Group* o + { Fl_Box* o = new Fl_Box(155, 145, 15, 20, ":"); + o->labelsize(11); + } // Fl_Box* o + { widget_grid_curr_row_attributes = new Fl_Group(170, 145, 175, 20); + widget_grid_curr_row_attributes->callback((Fl_Callback*)propagate_load); + { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(170, 145, 55, 20, "Height:"); + o->box(FL_DOWN_BOX); + o->color(FL_BACKGROUND2_COLOR); + o->selection_color(FL_SELECTION_COLOR); + o->labeltype(FL_NORMAL_LABEL); + o->labelfont(0); + o->labelsize(11); + o->labelcolor(FL_FOREGROUND_COLOR); + o->textsize(11); + o->callback((Fl_Callback*)cb_Height); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + o->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* o + { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(230, 145, 55, 20, "Weight:"); + o->box(FL_DOWN_BOX); + o->color(FL_BACKGROUND2_COLOR); + o->selection_color(FL_SELECTION_COLOR); + o->labeltype(FL_NORMAL_LABEL); + o->labelfont(0); + o->labelsize(11); + o->labelcolor(FL_FOREGROUND_COLOR); + o->textsize(11); + o->callback((Fl_Callback*)cb_Weight); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + o->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* o + { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(290, 145, 55, 20, "Gap:"); + o->box(FL_DOWN_BOX); + o->color(FL_BACKGROUND2_COLOR); + o->selection_color(FL_SELECTION_COLOR); + o->labeltype(FL_NORMAL_LABEL); + o->labelfont(0); + o->labelsize(11); + o->labelcolor(FL_FOREGROUND_COLOR); + o->textsize(11); + o->callback((Fl_Callback*)cb_Gap); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + o->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* o + widget_grid_curr_row_attributes->end(); + } // Fl_Group* widget_grid_curr_row_attributes + { Fl_Box* o = new Fl_Box(390, 145, 1, 20); + o->hide(); + Fl_Group::current()->resizable(o); + } // Fl_Box* o + o->end(); + } // Fl_Group* o + { Fl_Group* o = new Fl_Group(85, 180, 315, 20, "Column:"); + o->labelfont(1); + o->labelsize(11); + o->callback((Fl_Callback*)propagate_load); + o->align(Fl_Align(FL_ALIGN_LEFT)); + { widget_grid_curr_col = new fld::widget::Formula_Input(85, 180, 40, 20, "Index"); + widget_grid_curr_col->box(FL_DOWN_BOX); + widget_grid_curr_col->color(FL_BACKGROUND2_COLOR); + widget_grid_curr_col->selection_color(FL_SELECTION_COLOR); + widget_grid_curr_col->labeltype(FL_NORMAL_LABEL); + widget_grid_curr_col->labelfont(0); + widget_grid_curr_col->labelsize(11); + widget_grid_curr_col->labelcolor(FL_FOREGROUND_COLOR); + widget_grid_curr_col->textsize(11); + widget_grid_curr_col->callback((Fl_Callback*)cb_widget_grid_curr_col); + widget_grid_curr_col->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + widget_grid_curr_col->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* widget_grid_curr_col + { Fl_Group* o = new Fl_Group(125, 180, 30, 20); + { Fl_Button* o = new Fl_Button(125, 180, 15, 20, "-"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_6); + o->clear_visible_focus(); + } // Fl_Button* o + { Fl_Button* o = new Fl_Button(140, 180, 15, 20, "+"); + o->compact(1); + o->labelsize(11); + o->callback((Fl_Callback*)cb_7); + o->clear_visible_focus(); + } // Fl_Button* o + o->end(); + } // Fl_Group* o + { Fl_Box* o = new Fl_Box(155, 180, 15, 20, ":"); + o->labelsize(11); + } // Fl_Box* o + { widget_grid_curr_col_attributes = new Fl_Group(170, 180, 175, 20); + widget_grid_curr_col_attributes->callback((Fl_Callback*)propagate_load); + { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(170, 180, 55, 20, "Width:"); + o->box(FL_DOWN_BOX); + o->color(FL_BACKGROUND2_COLOR); + o->selection_color(FL_SELECTION_COLOR); + o->labeltype(FL_NORMAL_LABEL); + o->labelfont(0); + o->labelsize(11); + o->labelcolor(FL_FOREGROUND_COLOR); + o->textsize(11); + o->callback((Fl_Callback*)cb_Width); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + o->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* o + { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(230, 180, 55, 20, "Weight:"); + o->box(FL_DOWN_BOX); + o->color(FL_BACKGROUND2_COLOR); + o->selection_color(FL_SELECTION_COLOR); + o->labeltype(FL_NORMAL_LABEL); + o->labelfont(0); + o->labelsize(11); + o->labelcolor(FL_FOREGROUND_COLOR); + o->textsize(11); + o->callback((Fl_Callback*)cb_Weight1); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + o->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* o + { fld::widget::Formula_Input* o = new fld::widget::Formula_Input(290, 180, 55, 20, "Gap:"); + o->box(FL_DOWN_BOX); + o->color(FL_BACKGROUND2_COLOR); + o->selection_color(FL_SELECTION_COLOR); + o->labeltype(FL_NORMAL_LABEL); + o->labelfont(0); + o->labelsize(11); + o->labelcolor(FL_FOREGROUND_COLOR); + o->textsize(11); + o->callback((Fl_Callback*)cb_Gap1); + o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); + o->when(FL_WHEN_RELEASE); + } // fld::widget::Formula_Input* o + widget_grid_curr_col_attributes->end(); + } // Fl_Group* widget_grid_curr_col_attributes + { Fl_Box* o = new Fl_Box(390, 180, 1, 20); + o->hide(); + Fl_Group::current()->resizable(o); + } // Fl_Box* o + o->end(); + } // Fl_Group* o + { Fl_Box* o = new Fl_Box(85, 320, 300, 5); + o->labelsize(11); + o->hide(); + Fl_Group::current()->resizable(o); + } // Fl_Box* o + end(); + resize(X, Y, W, H); +} diff --git a/fluid/panels/widget_panel/Grid_Tab.fl b/fluid/panels/widget_panel/Grid_Tab.fl new file mode 100644 index 000000000..e37610ca5 --- /dev/null +++ b/fluid/panels/widget_panel/Grid_Tab.fl @@ -0,0 +1,515 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0500 +header_name {.h} +code_name {.cxx} +decl {\#include "widgets/Formula_Input.h"} {public global +} + +decl {\#include "Fluid.h"} {private global +} + +decl {\#include "proj/undo.h"} {private global +} + +decl {\#include "nodes/Grid_Node.h"} {private global +} + +decl {extern Grid_Tab *widget_tab_grid;} {selected private global +} + +widget_class Grid_Tab { + label Grid + callback propagate_load open + xywh {480 287 400 330} labelsize 11 resizable visible position_relative_rescale +} { + Fl_Group {} { + label {Grid Layout:} + callback propagate_load open + xywh {85 30 315 20} labelfont 1 labelsize 11 align 4 + } { + Fl_Input widget_grid_rows { + label {Rows:} + callback {// grid_rows_cb +Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +if (v == LOAD) { + o->value(grid->rows()); +} else { + int m = o->value(), old_m = grid->rows(); + if (m < 1) { + m = 1; + o->value(m); + } + if (m < old_m) { + // TODO: verify that this will not unlink existings cells + // Offer a dialog with "delete children", "unlink cells", "cancel" + } + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->layout(m, grid->cols()); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + widget_tab_grid->do_callback(widget_tab_grid, LOAD); + } +}} + tooltip {Number of horizontal rows in the Grid group} xywh {85 30 40 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Group {} {open + xywh {125 30 30 20} + } { + Fl_Button {} { + label {-} + callback {if (v != LOAD) { + widget_grid_rows->value( widget_grid_rows->value()-1 ); + widget_grid_rows->do_callback(); +}} + xywh {125 30 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + Fl_Button {} { + label {+} + callback {if (v != LOAD) { + widget_grid_rows->value( widget_grid_rows->value()+1 ); + widget_grid_rows->do_callback(); +}} + xywh {140 30 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + } + Fl_Input widget_grid_cols { + label {Columns:} + callback {// grid_rows_cb +Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +if (v == LOAD) { + o->value(grid->cols()); +} else { + int m = o->value(), old_m = grid->cols(); + if (m < 1) { + m = 1; + o->value(m); + } + if (m < old_m) { + // TODO: verify that this will not unlink existings cells + // Offer a dialog with "delete children", "unlink cells", "cancel" + } + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->layout(grid->rows(), m); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + widget_tab_grid->do_callback(widget_tab_grid, LOAD); + } +}} + tooltip {Number of vertical columns in the Grid group} xywh {165 30 40 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Group {} {open + xywh {205 30 30 20} + } { + Fl_Button {} { + label {-} + callback {if (v != LOAD) { + widget_grid_cols->value( widget_grid_cols->value()-1 ); + widget_grid_cols->do_callback(); +}} + xywh {205 30 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + Fl_Button {} { + label {+} + callback {if (v != LOAD) { + widget_grid_cols->value( widget_grid_cols->value()+1 ); + widget_grid_cols->do_callback(); +}} + xywh {220 30 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + } + Fl_Box {} { + xywh {386 30 0 20} resizable + } + } + Fl_Group wp_grid_margin { + label {Margins:} + callback propagate_load open + xywh {85 70 315 20} labelfont 1 labelsize 11 align 4 + } { + Fl_Value_Input {} { + label {Left:} + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +int m = 0; +if (v == LOAD) { + grid->margin(&m, nullptr, nullptr, nullptr); + o->value(m); +} else { + int m = (int)o->value(), old_m; + grid->margin(&old_m, nullptr, nullptr, nullptr); + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->margin(m, -1, -1, -1); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } +}} + tooltip {Left margin in group.} xywh {85 70 55 20} labelsize 11 align 5 maximum 1000 step 1 textsize 11 + } + Fl_Value_Input {} { + label {Top:} + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +int m = 0; +if (v == LOAD) { + grid->margin(nullptr, &m, nullptr, nullptr); + o->value(m); +} else { + int m = (int)o->value(), old_m; + grid->margin(nullptr, &old_m, nullptr, nullptr); + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->margin(-1, m, -1, -1); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } +}} + tooltip {Top margin in group.} xywh {145 70 55 20} labelsize 11 align 5 maximum 1000 step 1 textsize 11 + } + Fl_Value_Input {} { + label {Right:} + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +int m = 0; +if (v == LOAD) { + grid->margin(nullptr, nullptr, &m, nullptr); + o->value(m); +} else { + int m = (int)o->value(), old_m; + grid->margin(nullptr, nullptr, &old_m, nullptr); + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->margin(-1, -1, m, -1); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } +}} + tooltip {Right margin in group.} xywh {205 70 55 20} labelsize 11 align 5 maximum 1000 step 1 textsize 11 + } + Fl_Value_Input {} { + label {Bottom:} + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +int m = 0; +if (v == LOAD) { + grid->margin(nullptr, nullptr, nullptr, &m); + o->value(m); +} else { + int m = (int)o->value(), old_m; + grid->margin(nullptr, nullptr, nullptr, &old_m); + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->margin(-1, -1, -1, m); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } +}} + tooltip {Bottom margin in group.} xywh {265 70 55 20} labelsize 11 align 5 maximum 1000 step 1 textsize 11 + } + Fl_Box {} { + xywh {386 70 0 20} resizable + } + } + Fl_Group wp_grid_gaps { + label {Gaps:} + callback propagate_load open + xywh {85 105 315 20} labelfont 1 labelsize 11 align 4 + } { + Fl_Value_Input {} { + label {Row:} + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +if (v == LOAD) { + int m = 0; + grid->gap(&m, nullptr); + o->value(m); +} else { + int m = (int)o->value(), old_m, m2; + grid->gap(&old_m, &m2); + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->gap(m, m2); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } +}} + tooltip {Gap between children.} xywh {85 105 55 20} labelsize 11 align 5 maximum 1000 step 1 textsize 11 + } + Fl_Value_Input {} { + label {Col:} + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +if (v == LOAD) { + int m = 0; + grid->gap(nullptr, &m); + o->value(m); +} else { + int m = (int)o->value(), old_m, m2; + grid->gap(&m2, &old_m); + if (m != old_m) { + Fluid.proj.undo.checkpoint(); + grid->gap(m2, m); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } +}} + tooltip {Gap between children.} xywh {145 105 55 20} labelsize 11 align 5 maximum 1000 step 1 textsize 11 + } + Fl_Box {} { + xywh {386 105 0 20} resizable + } + } + Fl_Group {} { + label {Row:} + callback {if (v == LOAD) { + Fl_Grid *grid = Grid_Node::selected(); + if (grid) + o->activate(); + else + o->deactivate(); + propagate_load(o, v); +}} open + xywh {85 145 315 20} labelfont 1 labelsize 11 align 4 + } { + Fl_Input widget_grid_curr_row { + label Index + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +int r = o->value(), old_r = r; +if (r < 0) r = 0; +if (r >= grid->rows()) r = grid->rows()-1; +if (r != old_r) o->value(r); +if (v == LOAD) { + // will automatically propagate +} else { + widget_grid_curr_row_attributes->do_callback(widget_grid_curr_row_attributes, LOAD); +}} + xywh {85 145 40 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Group {} { + callback propagate_load open + xywh {125 145 30 20} + } { + Fl_Button {} { + label {-} + callback {if (v != LOAD) { + widget_grid_curr_row->value( widget_grid_curr_row->value()-1 ); + widget_grid_curr_row->do_callback(); +}} + xywh {125 145 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + Fl_Button {} { + label {+} + callback {if (v != LOAD) { + widget_grid_curr_row->value( widget_grid_curr_row->value()+1 ); + widget_grid_curr_row->do_callback(); +}} + xywh {140 145 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + } + Fl_Box {} { + label {:} + xywh {155 145 15 20} labelsize 11 + } + Fl_Group widget_grid_curr_row_attributes { + callback propagate_load open + xywh {170 145 175 20} + } { + Fl_Input {} { + label {Height:} + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +int r = widget_grid_curr_row->value(); +if (v == LOAD) { + o->value(grid->row_height(r)); +} else { + int h = o->value(), old_h = grid->row_height(r); + if (h < 0) h = 0; + if (h != old_h) { + Fluid.proj.undo.checkpoint(); + grid->row_height(r, h); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } +}} + xywh {170 145 55 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Input {} { + label {Weight:} + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +int r = widget_grid_curr_row->value(); +if (v == LOAD) { + o->value(grid->row_weight(r)); +} else { + int h = o->value(), old_h = grid->row_weight(r); + if (h < 0) h = 0; + if (h != old_h) { + Fluid.proj.undo.checkpoint(); + grid->row_weight(r, h); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } +}} + xywh {230 145 55 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Input {} { + label {Gap:} + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +int r = widget_grid_curr_row->value(); +if (v == LOAD) { + o->value(grid->row_gap(r)); +} else { + int h = o->value(), old_h = grid->row_gap(r); + if (h < -1) h = -1; + if (h != old_h) { + Fluid.proj.undo.checkpoint(); + grid->row_gap(r, h); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } +}} + xywh {290 145 55 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + } + Fl_Box {} { + xywh {390 145 1 20} hide resizable + } + } + Fl_Group {} { + label {Column:} + callback propagate_load open + xywh {85 180 315 20} labelfont 1 labelsize 11 align 4 + } { + Fl_Input widget_grid_curr_col { + label Index + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +int c = o->value(), old_c = c; +if (c < 0) c = 0; +if (c >= grid->cols()) c = grid->cols()-1; +if (c != old_c) o->value(c); +if (v == LOAD) { + // will automatically propagate +} else { + widget_grid_curr_col_attributes->do_callback(widget_grid_curr_col_attributes, LOAD); +}} + xywh {85 180 40 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Group {} {open + xywh {125 180 30 20} + } { + Fl_Button {} { + label {-} + callback {if (v != LOAD) { + widget_grid_curr_col->value( widget_grid_curr_col->value()-1 ); + widget_grid_curr_col->do_callback(); +}} + xywh {125 180 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + Fl_Button {} { + label {+} + callback {if (v != LOAD) { + widget_grid_curr_col->value( widget_grid_curr_col->value()+1 ); + widget_grid_curr_col->do_callback(); +}} + xywh {140 180 15 20} labelsize 11 + code0 {o->clear_visible_focus();} compact 1 + } + } + Fl_Box {} { + label {:} + xywh {155 180 15 20} labelsize 11 + } + Fl_Group widget_grid_curr_col_attributes { + callback propagate_load open + xywh {170 180 175 20} + } { + Fl_Input {} { + label {Width:} + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +int c = widget_grid_curr_col->value(); +if (v == LOAD) { + o->value(grid->col_width(c)); +} else { + int h = o->value(), old_h = grid->col_width(c); + if (h < 0) h = 0; + if (h != old_h) { + Fluid.proj.undo.checkpoint(); + grid->col_width(c, h); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } +}} + xywh {170 180 55 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Input {} { + label {Weight:} + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +int c = widget_grid_curr_col->value(); +if (v == LOAD) { + o->value(grid->col_weight(c)); +} else { + int h = o->value(), old_h = grid->col_weight(c); + if (h < 0) h = 0; + if (h != old_h) { + Fluid.proj.undo.checkpoint(); + grid->col_weight(c, h); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } +}} + xywh {230 180 55 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + Fl_Input {} { + label {Gap:} + callback {Fl_Grid *grid = Grid_Node::selected(); +if (!grid) return; +int c = widget_grid_curr_col->value(); +if (v == LOAD) { + o->value(grid->col_gap(c)); +} else { + int h = o->value(), old_h = grid->col_gap(c); + if (h < -1) h = -1; + if (h != old_h) { + Fluid.proj.undo.checkpoint(); + grid->col_gap(c, h); + grid->need_layout(true); + Fluid.proj.set_modflag(1); + } +}} + xywh {290 180 55 20} labelsize 11 align 5 textsize 11 + class {fld::widget::Formula_Input} + } + } + Fl_Box {} { + xywh {390 180 1 20} hide resizable + } + } + Fl_Box {} { + xywh {85 320 300 5} labelsize 11 hide resizable + } +} diff --git a/fluid/panels/widget_panel/Grid_Tab.h b/fluid/panels/widget_panel/Grid_Tab.h new file mode 100644 index 000000000..4b06a9e43 --- /dev/null +++ b/fluid/panels/widget_panel/Grid_Tab.h @@ -0,0 +1,90 @@ +// generated by Fast Light User Interface Designer (fluid) version 1.0500 + +#ifndef Grid_Tab_h +#define Grid_Tab_h +#include +#include "widgets/Formula_Input.h" +#include +extern void propagate_load(Fl_Group*, void*); +#include +#include +#include + +class Grid_Tab : public Fl_Group { +public: + Grid_Tab(int X, int Y, int W, int H, const char *L = 0); + fld::widget::Formula_Input *widget_grid_rows; +private: + inline void cb_widget_grid_rows_i(fld::widget::Formula_Input*, void*); + static void cb_widget_grid_rows(fld::widget::Formula_Input*, void*); + inline void cb__i(Fl_Button*, void*); + static void cb_(Fl_Button*, void*); + inline void cb_1_i(Fl_Button*, void*); + static void cb_1(Fl_Button*, void*); +public: + fld::widget::Formula_Input *widget_grid_cols; +private: + inline void cb_widget_grid_cols_i(fld::widget::Formula_Input*, void*); + static void cb_widget_grid_cols(fld::widget::Formula_Input*, void*); + inline void cb_2_i(Fl_Button*, void*); + static void cb_2(Fl_Button*, void*); + inline void cb_3_i(Fl_Button*, void*); + static void cb_3(Fl_Button*, void*); +public: + Fl_Group *wp_grid_margin; +private: + inline void cb_Left_i(Fl_Value_Input*, void*); + static void cb_Left(Fl_Value_Input*, void*); + inline void cb_Top_i(Fl_Value_Input*, void*); + static void cb_Top(Fl_Value_Input*, void*); + inline void cb_Right_i(Fl_Value_Input*, void*); + static void cb_Right(Fl_Value_Input*, void*); + inline void cb_Bottom_i(Fl_Value_Input*, void*); + static void cb_Bottom(Fl_Value_Input*, void*); +public: + Fl_Group *wp_grid_gaps; +private: + inline void cb_Row_i(Fl_Value_Input*, void*); + static void cb_Row(Fl_Value_Input*, void*); + inline void cb_Col_i(Fl_Value_Input*, void*); + static void cb_Col(Fl_Value_Input*, void*); + inline void cb_Row1_i(Fl_Group*, void*); + static void cb_Row1(Fl_Group*, void*); +public: + fld::widget::Formula_Input *widget_grid_curr_row; +private: + inline void cb_widget_grid_curr_row_i(fld::widget::Formula_Input*, void*); + static void cb_widget_grid_curr_row(fld::widget::Formula_Input*, void*); + inline void cb_4_i(Fl_Button*, void*); + static void cb_4(Fl_Button*, void*); + inline void cb_5_i(Fl_Button*, void*); + static void cb_5(Fl_Button*, void*); +public: + Fl_Group *widget_grid_curr_row_attributes; +private: + inline void cb_Height_i(fld::widget::Formula_Input*, void*); + static void cb_Height(fld::widget::Formula_Input*, void*); + inline void cb_Weight_i(fld::widget::Formula_Input*, void*); + static void cb_Weight(fld::widget::Formula_Input*, void*); + inline void cb_Gap_i(fld::widget::Formula_Input*, void*); + static void cb_Gap(fld::widget::Formula_Input*, void*); +public: + fld::widget::Formula_Input *widget_grid_curr_col; +private: + inline void cb_widget_grid_curr_col_i(fld::widget::Formula_Input*, void*); + static void cb_widget_grid_curr_col(fld::widget::Formula_Input*, void*); + inline void cb_6_i(Fl_Button*, void*); + static void cb_6(Fl_Button*, void*); + inline void cb_7_i(Fl_Button*, void*); + static void cb_7(Fl_Button*, void*); +public: + Fl_Group *widget_grid_curr_col_attributes; +private: + inline void cb_Width_i(fld::widget::Formula_Input*, void*); + static void cb_Width(fld::widget::Formula_Input*, void*); + inline void cb_Weight1_i(fld::widget::Formula_Input*, void*); + static void cb_Weight1(fld::widget::Formula_Input*, void*); + inline void cb_Gap1_i(fld::widget::Formula_Input*, void*); + static void cb_Gap1(fld::widget::Formula_Input*, void*); +}; +#endif diff --git a/fluid/proj/align_widget.cxx b/fluid/proj/align_widget.cxx new file mode 100644 index 000000000..9ce9e2b62 --- /dev/null +++ b/fluid/proj/align_widget.cxx @@ -0,0 +1,414 @@ +// +// Alignment 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 "proj/align_widget.h" + +#include "Fluid.h" +#include "proj/undo.h" +#include "nodes/Group_Node.h" + +#include +#include + +/** + the first behavior always uses the first selected widget as a reference + the second behavior uses the largest widget (most extreme positions) as + a reference. + */ +#define BREAK_ON_FIRST break +//#define BREAK_ON_FIRST + +void align_widget_cb(Fl_Widget*, long how) +{ + const int max = 32768, min = -32768; + int left, right, top, bot, wdt, hgt, n; + Node *o; + int changed = 0; + switch ( how ) + { + //---- align + case 10: // align left + left = max; + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + Fl_Widget *w = ((Widget_Node *)o)->o; + if (w->x()x(); + BREAK_ON_FIRST; + } + if (left!=max) + for (Node *o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fluid.proj.tree.allow_layout++; + w->resize(left, w->y(), w->w(), w->h()); + Fluid.proj.tree.allow_layout--; + w->redraw(); + if (w->window()) w->window()->redraw(); + } + break; + case 11: // align h.center + left = max; right = min; + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + Fl_Widget *w = ((Widget_Node *)o)->o; + if (w->x()x(); + if (w->x()+w->w()>right) + right = w->x()+w->w(); + BREAK_ON_FIRST; + } + if (left!=max) + { + int center2 = left+right; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fluid.proj.tree.allow_layout++; + w->resize((center2-w->w())/2, w->y(), w->w(), w->h()); + Fluid.proj.tree.allow_layout--; + w->redraw(); + if (w->window()) w->window()->redraw(); + } + } + break; + case 12: // align right + right = min; + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + Fl_Widget *w = ((Widget_Node *)o)->o; + if (w->x()+w->w()>right) + right = w->x()+w->w(); + BREAK_ON_FIRST; + } + if (right!=min) + for (Node *o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fluid.proj.tree.allow_layout++; + w->resize(right-w->w(), w->y(), w->w(), w->h()); + Fluid.proj.tree.allow_layout--; + w->redraw(); + if (w->window()) w->window()->redraw(); + } + break; + case 13: // align top + top = max; + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + Fl_Widget *w = ((Widget_Node *)o)->o; + if (w->y()y(); + BREAK_ON_FIRST; + } + if (top!=max) + for (Node *o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fluid.proj.tree.allow_layout++; + w->resize(w->x(), top, w->w(), w->h()); + Fluid.proj.tree.allow_layout--; + w->redraw(); + if (w->window()) w->window()->redraw(); + } + break; + case 14: // align v.center + top = max; bot = min; + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + Fl_Widget *w = ((Widget_Node *)o)->o; + if (w->y()y(); + if (w->y()+w->h()>bot) + bot = w->y()+w->h(); + BREAK_ON_FIRST; + } + if (top!=max) + { + int center2 = top+bot; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fluid.proj.tree.allow_layout++; + w->resize(w->x(), (center2-w->h())/2, w->w(), w->h()); + Fluid.proj.tree.allow_layout--; + w->redraw(); + if (w->window()) w->window()->redraw(); + } + } + break; + case 15: // align bottom + bot = min; + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + Fl_Widget *w = ((Widget_Node *)o)->o; + if (w->y()+w->h()>bot) + bot = w->y()+w->h(); + BREAK_ON_FIRST; + } + if (bot!=min) + for (Node *o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fluid.proj.tree.allow_layout++; + w->resize( w->x(), bot-w->h(), w->w(), w->h()); + Fluid.proj.tree.allow_layout--; + w->redraw(); + if (w->window()) w->window()->redraw(); + } + break; + //---- space evenly + case 20: // space evenly across + left = max; right = min; wdt = 0; n = 0; + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + Fl_Widget *w = ((Widget_Node *)o)->o; + if (w->x()x(); + if (w->x()+w->w()>right) + right = w->x()+w->w(); + wdt += w->w(); + n++; + } + wdt = (right-left)-wdt; + n--; + if (n>0) + { + wdt = wdt/n*n; // make sure that all gaps are the same, possibly moving the rightmost widget + int cnt = 0, wsum = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fluid.proj.tree.allow_layout++; + w->resize(left+wsum+wdt*cnt/n, w->y(), w->w(), w->h()); + Fluid.proj.tree.allow_layout--; + w->redraw(); + if (w->window()) w->window()->redraw(); + cnt++; + wsum += w->w(); + } + } + break; + case 21: // space evenly down + top = max; bot = min; hgt = 0, n = 0; + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + Fl_Widget *w = ((Widget_Node *)o)->o; + if (w->y()y(); + if (w->y()+w->h()>bot) + bot = w->y()+w->h(); + hgt += w->h(); + n++; + } + hgt = (bot-top)-hgt; + n--; + if (n>0) + { + hgt = hgt/n*n; // make sure that all gaps are the same, possibly moving the rightmost widget + int cnt = 0, hsum = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fluid.proj.tree.allow_layout++; + w->resize(w->x(), top+hsum+hgt*cnt/n, w->w(), w->h()); + Fluid.proj.tree.allow_layout--; + w->redraw(); + if (w->window()) w->window()->redraw(); + cnt++; + hsum += w->h(); + } + } + break; + //---- make same size + case 30: // same width + wdt = min; + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + Fl_Widget *w = ((Widget_Node *)o)->o; + if (w->w()>wdt) + wdt = w->w(); + BREAK_ON_FIRST; + } + if (wdt!=min) + for (Node *o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fluid.proj.tree.allow_layout++; + w->resize(w->x(), w->y(), wdt, w->h()); + Fluid.proj.tree.allow_layout--; + w->redraw(); + if (w->window()) w->window()->redraw(); + } + break; + case 31: // same height + hgt = min; + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + Fl_Widget *w = ((Widget_Node *)o)->o; + if (w->h()>hgt) + hgt = w->h(); + BREAK_ON_FIRST; + } + if (hgt!=min) + for (Node *o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fluid.proj.tree.allow_layout++; + w->resize( w->x(), w->y(), w->w(), hgt); + Fluid.proj.tree.allow_layout--; + w->redraw(); + if (w->window()) w->window()->redraw(); + } + break; + case 32: // same size + hgt = min; wdt = min; + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + Fl_Widget *w = ((Widget_Node *)o)->o; + if (w->w()>wdt) + wdt = w->w(); + if (w->h()>hgt) + hgt = w->h(); + BREAK_ON_FIRST; + } + if (hgt!=min) + for (Node *o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget()) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fluid.proj.tree.allow_layout++; + w->resize( w->x(), w->y(), wdt, hgt); + Fluid.proj.tree.allow_layout--; + w->redraw(); + if (w->window()) w->window()->redraw(); + } + break; + //---- center in group + case 40: // center hor + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget() && o->parent) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fl_Widget *p = ((Widget_Node *)o->parent)->o; + int center2; + + if (w->window() == p) center2 = p->w(); + else center2 = 2*p->x()+p->w(); + + Fluid.proj.tree.allow_layout++; + w->resize((center2-w->w())/2, w->y(), w->w(), w->h()); + Fluid.proj.tree.allow_layout--; + w->redraw(); + if (w->window()) w->window()->redraw(); + } + break; + case 41: // center vert + for (o = Fluid.proj.tree.first; o; o = o->next) + if (o->selected && o->is_widget() && o->parent) + { + if (!changed) { + changed = 1; + Fluid.proj.undo.checkpoint(); + } + Fl_Widget *w = ((Widget_Node *)o)->o; + Fl_Widget *p = ((Widget_Node *)o->parent)->o; + int center2; + + if (w->window() == p) center2 = p->h(); + else center2 = 2*p->y()+p->h(); + + Fluid.proj.tree.allow_layout++; + w->resize(w->x(), (center2-w->h())/2, w->w(), w->h()); + Fluid.proj.tree.allow_layout--; + Fluid.proj.set_modflag(1); + w->redraw(); + if (w->window()) w->window()->redraw(); + } + break; + } + if (changed) + Fluid.proj.set_modflag(1); +} diff --git a/fluid/proj/align_widget.h b/fluid/proj/align_widget.h new file mode 100644 index 000000000..88860b7ce --- /dev/null +++ b/fluid/proj/align_widget.h @@ -0,0 +1,24 @@ +// +// FLUID main entry 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_ALIGN_WIDGET_H +#define _FLUID_ALIGN_WIDGET_H + +class Fl_Widget; + +void align_widget_cb(Fl_Widget *, long); + +#endif // _FLUID_ALIGN_WIDGET_H diff --git a/fluid/proj/mergeback.cxx b/fluid/proj/mergeback.cxx new file mode 100644 index 000000000..12f272638 --- /dev/null +++ b/fluid/proj/mergeback.cxx @@ -0,0 +1,539 @@ +// +// MergeBack code for the Fast Light Tool Kit (FLTK). +// +// Copyright 2023-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 +// + +#if 0 +// Matt: disabled + +#include "proj/mergeback.h" + +#include "Fluid.h" +#include "proj/undo.h" +#include "io/Code_Writer.h" +#include "nodes/Function_Node.h" +#include "nodes/Widget_Node.h" + +#include +#include + +#include +#include +#include +#include +#include + +extern void propagate_load(Fl_Group*, void*); +extern void load_panel(); +extern void redraw_browser(); + +// TODO: add application user setting to control mergeback +// [] new projects default to mergeback +// [] check mergeback when loading project +// [] check mergeback when app gets focus +// [] always apply if safe +// TODO: command line option for mergeback +// -mb or --merge-back +// -mbs or --merge-back-if-safe +// NOTE: automatic mergeback on timer when file changes if app focus doesn't work +// NOTE: allow the user to edit comment blocks + +/** + Merge external changes in a source code file back into the current project. + + This experimental function reads a source code file line by line. When it + encounters a special tag in a line, the crc32 stored in the tag is compared + to the crc32 that was calculated from the code lines since the previous tag. + + If the crc's differ, the user has modified the source file externally, and the + given block differs from the block as it was generated by FLUID. Depending on + the block type, the user has modified the widget code (FD_TAG_GENERIC), which + can not be transferred back into the project. + + Modifications to code blocks and callbacks (CODE, CALLBACK) can be merged back + into the project. Their corresponding Node is found using the unique + node id that is part of the tag. The block is only merged back if the crc's + from the project and from the edited block differ. + + The caller must make sure that this code file was generated by the currently + loaded project. + + The user is informed in detailed dialogs what the function discovered and + offered to merge or cancel if appropriate. Just in case this function is + destructive, "undo" restores the state before a MergeBack. + + Callers can set different task. FD_MERGEBACK_ANALYSE checks if there are any + modifications in the code file and returns -1 if there was an error, or a + bit field where bit 0 is set if internal structures were modified, bit 1 if + code was changed, and bit 2 if modified blocks were found, but no Type node. + Bit 3 is set, if code was changed in the code file *and* the project. + + FD_MERGEBACK_INTERACTIVE checks for changes and presents a status dialog box + to the user if there were conflicting changes or if a mergeback is possible, + presenting the user the option to merge or cancel. Returns 0 if the project + remains unchanged, and 1 if the user merged changes back. -1 is returned if an + invalid tag was found. + + FD_MERGEBACK_APPLY merges all changes back into the project without any + interaction. Returns 0 if nothing changed, and 1 if it merged any changes back. + + FD_MERGEBACK_APPLY_IF_SAFE merges changes back only if there are no conflicts. + Returns 0 if nothing changed, and 1 if it merged any changes back, and -1 if + there were conflicts. + + \note this function is currently part of fld::io::Code_Writer to get easy access + to our crc32 code that also wrote the code file originally. + + \param[in] s path and filename of the source code file + \param[in] task see above + \return -1 if an error was found in a tag + \return -2 if no code file was found + \return see above + */ +int merge_back(const std::string &s, const std::string &p, int task) { + if (Fluid.proj.write_mergeback_data) { + Fd_Mergeback mergeback; + return mergeback.merge_back(s, p, task); + } else { + // nothing to be done if the mergeback option is disabled in the project + return 0; + } +} + +/** Allocate and initialize MergeBack class. */ +Fd_Mergeback::Fd_Mergeback() : + code(nullptr), + line_no(0), + tag_error(0), + num_changed_code(0), + num_changed_structure(0), + num_uid_not_found(0), + num_possible_override(0) +{ +} + +/** Release allocated resources. */ +Fd_Mergeback::~Fd_Mergeback() +{ + if (code) ::fclose(code); +} + +/** Remove the first two spaces at every line start. + \param[inout] s block of C code + */ +void Fd_Mergeback::unindent(char *s) { + char *d = s; + bool line_start = true; + while (*s) { + if (line_start) { + if (*s>0 && isspace(*s)) s++; + if (*s>0 && isspace(*s)) s++; + line_start = false; + } + if (*s=='\r') s++; + if (*s=='\n') line_start = true; + *d++ = *s++; + } + *d = 0; +} + +/** + Read a block of text from the source file and remove the leading two spaces in every line. + \param[in] start start of the block within the file + \param[in] end end of text within the file + \return a string holding the text that was found in the file + */ +std::string Fd_Mergeback::read_and_unindent_block(long start, long end) { + long bsize = end-start; + long here = ::ftell(code); + ::fseek(code, start, SEEK_SET); + char *block = (char*)::malloc(bsize+1); + size_t n = ::fread(block, bsize, 1, code); + if (n!=1) + block[0] = 0; // read error + else + block[bsize] = 0; + unindent(block); + std::string str = block; + ::free(block); + ::fseek(code, here, SEEK_SET); + return str; +} + +/** Tell user the results of our MergeBack analysis and pop up a dialog to give + the user a choice to merge or cancel. + \return 1 if the user wants to merge (choice dialog was shown) + \return 0 if there is nothing to merge (no dialog was shown) + \return -1 if the user wants to cancel or an error occurred or an issue was presented + (message or choice dialog was shown) + */ +int Fd_Mergeback::ask_user_to_merge(const std::string &code_filename, const std::string &proj_filename) { + if (tag_error) { + fl_message("Comparing\n \"%s\"\nto\n \"%s\"\n\n" + "MergeBack found an error in line %d while reading tags\n" + "from the source code. Merging code back is not possible.", + code_filename.c_str(), proj_filename.c_str(), line_no); + return -1; + } + if (!num_changed_code && !num_changed_structure) { + return 0; + } + if (num_changed_structure && !num_changed_code) { + fl_message("Comparing\n \"%1$s\"\nto\n \"%2$s\"\n\n" + "MergeBack found %3$d modifications in the project structure\n" + "of the source code. These kind of changes can no be\n" + "merged back and will be lost when the source code is\n" + "generated again from the open project.", + code_filename.c_str(), proj_filename.c_str(), num_changed_structure); + return -1; + } + std::string msg = "Comparing\n \"%1$s\"\nto\n \"%2$s\"\n\n" + "MergeBack found %3$d modifications in the source code."; + if (num_possible_override) + msg += "\n\nWARNING: %6$d of these modified blocks appear to also have\n" + "changed in the project. Merging will override changes in\n" + "the project with changes from the source code file."; + if (num_uid_not_found) + msg += "\n\nWARNING: for %4$d of these modifications no Type node\n" + "can be found and these modification can't be merged back."; + if (!num_possible_override && !num_uid_not_found) + msg += "\nMerging these changes back appears to be safe."; + + if (num_changed_structure) + msg += "\n\nWARNING: %5$d modifications were found in the project\n" + "structure. These kind of changes can no be merged back\n" + "and will be lost when the source code is generated again\n" + "from the open project."; + + if (num_changed_code==num_uid_not_found) { + fl_message(msg.c_str(), + code_filename.c_str(), proj_filename.c_str(), + num_changed_code, num_uid_not_found, + num_changed_structure, num_possible_override); + return -1; + } else { + msg += "\n\nClick Cancel to abort the MergeBack operation.\n" + "Click Merge to merge all code changes back into\n" + "the open project."; + int c = fl_choice(msg.c_str(), "Cancel", "Merge", nullptr, + code_filename.c_str(), proj_filename.c_str(), + num_changed_code, num_uid_not_found, + num_changed_structure, num_possible_override); + if (c==0) return -1; + return 1; + } +} + +/** Analyse the block and its corresponding widget callback. + Return findings in num_changed_code, num_changed_code, and num_uid_not_found. + */ +void Fd_Mergeback::analyse_callback(unsigned long code_crc, unsigned long tag_crc, int uid) { + Node *tp = Node::find_by_uid(uid); + if (tp && tp->is_true_widget()) { + std::string cb = tp->callback(); cb += "\n"; + unsigned long project_crc = fld::io::Code_Writer::block_crc(cb.c_str()); + // check if the code and project crc are the same, so this modification was already applied + if (project_crc!=code_crc) { + num_changed_code++; + // check if the block change on the project side as well, so we may override changes + if (project_crc!=tag_crc) { + num_possible_override++; + } + } + } else { + num_uid_not_found++; + num_changed_code++; + } +} + +/** Analyse the block and its corresponding Code Type. + Return findings in num_changed_code, num_changed_code, and num_uid_not_found. + */ +void Fd_Mergeback::analyse_code(unsigned long code_crc, unsigned long tag_crc, int uid) { + Node *tp = Node::find_by_uid(uid); + if (tp && tp->is_a(ID_Code)) { + std::string code = tp->name(); code += "\n"; + unsigned long project_crc = fld::io::Code_Writer::block_crc(code.c_str()); + // check if the code and project crc are the same, so this modification was already applied + if (project_crc!=code_crc) { + num_changed_code++; + // check if the block change on the project side as well, so we may override changes + if (project_crc!=tag_crc) { + num_possible_override++; + } + } + } else { + num_changed_code++; + num_uid_not_found++; + } +} + + +/** Analyse the code file and return findings in class member variables. + + The code file must be open for reading already. + + * tag_error is set if a tag was found, but could not be read + * line_no returns the line where an error occurred + * num_changed_code is set to the number of changed code blocks in the file. + Code changes can be merged back to the project. + * num_changed_structure is set to the number of structural changes. + Structural changes outside of code blocks can not be read back. + * num_uid_not_found number of blocks that were modified, but the corresponding + type or widget can not be found in the project + * num_possible_override number of blocks that were changed in the code file, + but also were changed in the project. + + \return -1 if reading a tag failed, otherwise 0 + */ +int Fd_Mergeback::analyse() { + // initialize local variables + unsigned long code_crc = 0; + bool line_start = true; + char line[1024]; + // bail if the caller has not opened a file yet + if (!code) return 0; + // initialize member variables to return our findings + line_no = 0; + tag_error = 0; + num_changed_code = 0; + num_changed_structure = 0; + num_uid_not_found = 0; + num_possible_override = 0; + code_crc = 0; + // loop through all lines in the code file + ::fseek(code, 0, SEEK_SET); + for (;;) { + // get the next line until end of file + if (fgets(line, 1023, code)==0) break; + line_no++; + const char *tag = strstr(line, "//~fl~"); + if (!tag) { + // if this line has no tag, add the contents to the CRC and continue + code_crc = fld::io::Code_Writer::block_crc(line, -1, code_crc, &line_start); + } else { + // if this line has a tag, read all tag data + int tag_type = -1, uid = 0; + unsigned long tag_crc = 0; + int n = sscanf(tag, "//~fl~%d~%04x~%08lx~~", &tag_type, &uid, &tag_crc); + if (n!=3 || tag_type<0 || tag_type>FD_TAG_LAST ) { tag_error = 1; return -1; } + if (code_crc != tag_crc) { + switch (tag_type) { + case FD_TAG_GENERIC: + num_changed_structure++; + break; + case FD_TAG_MENU_CALLBACK: + case FD_TAG_WIDGET_CALLBACK: + analyse_callback(code_crc, tag_crc, uid); + break; + case FD_TAG_CODE: + analyse_code(code_crc, tag_crc, uid); + break; + } + } + // reset everything for the next block + code_crc = 0; + line_start = true; + } + } + return 0; +} + +/** Apply callback mergebacks from the code file to the project. + \return 1 if the project changed + */ +int Fd_Mergeback::apply_callback(long block_end, long block_start, unsigned long code_crc, int uid) { + Node *tp = Node::find_by_uid(uid); + if (tp && tp->is_true_widget()) { + std::string cb = tp->callback(); cb += "\n"; + unsigned long project_crc = fld::io::Code_Writer::block_crc(cb.c_str()); + if (project_crc!=code_crc) { + tp->callback(read_and_unindent_block(block_start, block_end).c_str()); + return 1; + } + } + return 0; +} + +/** Apply callback mergebacks from the code file to the project. + \return 1 if the project changed + */ +int Fd_Mergeback::apply_code(long block_end, long block_start, unsigned long code_crc, int uid) { + Node *tp = Node::find_by_uid(uid); + if (tp && tp->is_a(ID_Code)) { + std::string cb = tp->name(); cb += "\n"; + unsigned long project_crc = fld::io::Code_Writer::block_crc(cb.c_str()); + if (project_crc!=code_crc) { + tp->name(read_and_unindent_block(block_start, block_end).c_str()); + return 1; + } + } + return 0; +} + +/** Apply all possible mergebacks from the code file to the project. + The code file must be open for reading already. + \return -1 if reading a tag failed, 0 if nothing changed, 1 if the project changed + */ +int Fd_Mergeback::apply() { + // initialize local variables + unsigned long code_crc = 0; + bool line_start = true; + char line[1024]; + int changed = 0; + long block_start = 0; + long block_end = 0; + // bail if the caller has not opened a file yet + if (!code) return 0; + // initialize member variables to return our findings + line_no = 0; + tag_error = 0; + code_crc = 0; + // loop through all lines in the code file + ::fseek(code, 0, SEEK_SET); + for (;;) { + // get the next line until end of file + if (fgets(line, 1023, code)==0) break; + line_no++; + const char *tag = strstr(line, "//~fl~"); + if (!tag) { + // if this line has no tag, add the contents to the CRC and continue + code_crc = fld::io::Code_Writer::block_crc(line, -1, code_crc, &line_start); + block_end = ::ftell(code); + } else { + // if this line has a tag, read all tag data + int tag_type = -1, uid = 0; + unsigned long tag_crc = 0; + int n = sscanf(tag, "//~fl~%d~%04x~%08lx~~", &tag_type, &uid, &tag_crc); + if (n!=3 || tag_type<0 || tag_type>FD_TAG_LAST ) { tag_error = 1; return -1; } + if (code_crc != tag_crc) { + if (tag_type==FD_TAG_MENU_CALLBACK || tag_type==FD_TAG_WIDGET_CALLBACK) { + changed |= apply_callback(block_end, block_start, code_crc, uid); + } else if (tag_type==FD_TAG_CODE) { + changed |= apply_code(block_end, block_start, code_crc, uid); + } + } + // reset everything for the next block + code_crc = 0; + line_start = true; + block_start = ::ftell(code); + } + } + return changed; +} + +/** Dispatch the MergeBack into analysis, interactive, or apply directly. + \param[in] s source code filename and path + \param[in] task one of FD_MERGEBACK_ANALYSE, FD_MERGEBACK_INTERACTIVE, + FD_MERGEBACK_APPLY_IF_SAFE, or FD_MERGEBACK_APPLY + \return -1 if an error was found in a tag + \return -2 if no code file was found + \return See more at ::merge_back(const std::string &s, int task). + */ +int Fd_Mergeback::merge_back(const std::string &s, const std::string &p, int task) { + int ret = 0; + code = fl_fopen(s.c_str(), "rb"); + if (!code) return -2; + do { // no actual loop, just make sure we close the code file + if (task == FD_MERGEBACK_ANALYSE) { + analyse(); + if (tag_error) {ret = -1; break; } + if (num_changed_structure) ret |= 1; + if (num_changed_code) ret |= 2; + if (num_uid_not_found) ret |= 4; + if (num_possible_override) ret |= 8; + break; + } + if (task == FD_MERGEBACK_INTERACTIVE) { + analyse(); + ret = ask_user_to_merge(s, p); + if (ret != 1) + return ret; + task = FD_MERGEBACK_APPLY; // fall through + } + if (task == FD_MERGEBACK_APPLY_IF_SAFE) { + analyse(); + if (tag_error || num_changed_structure || num_possible_override) { + ret = -1; + break; + } + if (num_changed_code==0) { + ret = 0; + break; + } + task = FD_MERGEBACK_APPLY; // fall through + } + if (task == FD_MERGEBACK_APPLY) { + ret = apply(); + if (ret == 1) { + Fluid.proj.set_modflag(1); + redraw_browser(); + load_panel(); + } + ret = 1; // avoid message box in caller + } + } while (0); + fclose(code); + code = nullptr; + return ret; +} + +#if 0 +// Matt: disabled +/** + Merge the possibly modified content of code files back into the project. + */ +int mergeback_code_files() +{ + flush_text_widgets(); + if (!filename) return 1; + if (!Fluid.proj.write_mergeback_data) { + fl_message("MergeBack is not enabled for this project.\n" + "Please enable MergeBack in the project settings\n" + "dialog and re-save the project file and the code."); + return 0; + } + + std::string proj_filename = Fluid.proj.projectfile_path() + Fluid.proj.projectfile_name(); + std::string code_filename; +#if 1 + if (!Fluid.batch_mode) { + Fl_Preferences build_records(Fl_Preferences::USER_L, "fltk.org", "fluid-build"); + Fl_Preferences path(build_records, proj_filename.c_str()); + int i, n = proj_filename.size(); + for (i=0; i + +#include +#include + +const int FD_TAG_GENERIC = 0; +const int FD_TAG_CODE = 1; +const int FD_TAG_MENU_CALLBACK = 2; +const int FD_TAG_WIDGET_CALLBACK = 3; +const int FD_TAG_LAST = 3; + +const int FD_MERGEBACK_ANALYSE = 0; +const int FD_MERGEBACK_INTERACTIVE = 1; +const int FD_MERGEBACK_APPLY = 2; +const int FD_MERGEBACK_APPLY_IF_SAFE = 3; + +/** Class that implements the MergeBack functionality. + \see merge_back(const std::string &s, int task) + */ +class Fd_Mergeback +{ +protected: + /// Pointer to the C++ code file. + FILE *code; + /// Current line number in the C++ code file. + int line_no; + /// Set if there was an error reading a tag. + int tag_error; + /// Number of code blocks that were different than the CRC in their tag. + int num_changed_code; + /// Number of generic structure blocks that were different than the CRC in their tag. + int num_changed_structure; + /// Number of code block that were modified, but a type node by that uid was not found. + int num_uid_not_found; + /// Number of modified code block where the corresponding project block also changed. + int num_possible_override; + + void unindent(char *s); + std::string read_and_unindent_block(long start, long end); + void analyse_callback(unsigned long code_crc, unsigned long tag_crc, int uid); + void analyse_code(unsigned long code_crc, unsigned long tag_crc, int uid); + int apply_callback(long block_end, long block_start, unsigned long code_crc, int uid); + int apply_code(long block_end, long block_start, unsigned long code_crc, int uid); + +public: + Fd_Mergeback(); + ~Fd_Mergeback(); + int merge_back(const std::string &s, const std::string &p, int task); + int ask_user_to_merge(const std::string &s, const std::string &p); + int analyse(); + int apply(); +}; + +extern int merge_back(const std::string &s, const std::string &p, int task); + + +#endif // _FLUID_MERGEBACK_H + +#endif diff --git a/fluid/proj/undo.cxx b/fluid/proj/undo.cxx new file mode 100644 index 000000000..0f1a478fb --- /dev/null +++ b/fluid/proj/undo.cxx @@ -0,0 +1,271 @@ +// +// Fluid Undo 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 "proj/undo.h" + +#include "Fluid.h" +#include "Project.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "nodes/Node.h" +#include "nodes/Widget_Node.h" +#include "widgets/Node_Browser.h" + +#include +#include +#include +#include +#include +#include "tools/filename.h" +#include "../src/flstring.h" + +#if defined(_WIN32) && !defined(__CYGWIN__) +# include +# include +# define getpid (int)GetCurrentProcessId +#else +# include +#endif // _WIN32 && !__CYGWIN__ + +// This file implements an undo system using temporary files; ideally +// we'd like to do this in memory, however the current data structures +// and design aren't well-suited... Instead, we save and restore +// checkpoint files. + +extern Fl_Window* the_panel; + +using namespace fld; +using namespace fld::proj; + + +Undo::Undo(Project &p) +: proj_( p ) +{ } + +Undo::~Undo() { + // TODO: delete old undo files when calling the destructor. +} + + +// Return the undo filename. +// The filename is constructed in a static internal buffer and +// this buffer is overwritten by every call of this function. +// The return value is a pointer to this internal string. +char *Undo::filename(int level) { + if (!path_len_) { + Fluid.preferences.getUserdataPath(path_, sizeof(path_)); + path_len_ = (unsigned int)strlen(path_); + } + + // append filename: "undo_PID_LEVEL.fl" + snprintf(path_ + path_len_, + sizeof(path_) - path_len_ - 1, + "undo_%d_%d.fl", getpid(), level); + return path_; +} + + +// Redo menu callback +void Undo::redo() { + // int undo_item = main_menubar->find_index(undo_cb); + // int redo_item = main_menubar->find_index(redo_cb); + once_type_ = OnceType::ALWAYS; + + if (current_ >= last_) { + fl_beep(); + return; + } + + suspend(); + if (widget_browser) { + widget_browser->save_scroll_position(); + widget_browser->new_list(); + } + int reload_panel = (the_panel && the_panel->visible()); + if (!fld::io::read_file(proj_, filename(current_ + 1), 0)) { + // Unable to read checkpoint file, don't redo... + widget_browser->rebuild(); + proj_.update_settings_dialog(); + resume(); + return; + } + if (reload_panel) { + for (auto w: Fluid.proj.tree.all_selected_widgets()) { + w->open(); + } + } + if (widget_browser) widget_browser->restore_scroll_position(); + + current_ ++; + + // Update modified flag... + proj_.set_modflag(current_ != save_); + widget_browser->rebuild(); + proj_.update_settings_dialog(); + + // Update undo/redo menu items... + // if (current_ >= last_) main_menu[redo_item].deactivate(); + // main_menu[undo_item].activate(); + resume(); +} + +// Undo menu callback +void Undo::undo() { + // int undo_item = main_menubar->find_index(undo_cb); + // int redo_item = main_menubar->find_index(redo_cb); + once_type_ = OnceType::ALWAYS; + + if (current_ <= 0) { + fl_beep(); + return; + } + + if (current_ == last_) { + fld::io::write_file(proj_, filename(current_)); + } + + suspend(); + // Undo first deletes all widgets which resets the widget_tree browser. + // Save the current scroll position, so we don't scroll back to 0 at undo. + // TODO: make the scroll position part of the .fl project file + if (widget_browser) { + widget_browser->save_scroll_position(); + widget_browser->new_list(); + } + int reload_panel = (the_panel && the_panel->visible()); + if (!fld::io::read_file(proj_, filename(current_ - 1), 0)) { + // Unable to read checkpoint file, don't undo... + widget_browser->rebuild(); + proj_.update_settings_dialog(); + proj_.set_modflag(0, 0); + resume(); + return; + } + if (reload_panel) { + for (Node *t = Fluid.proj.tree.first; t; t=t->next) { + if (t->is_widget() && t->selected) { + t->open(); + break; + } + } + } + // Restore old browser position. + // Ideally, we would save the browser position inside the undo file. + if (widget_browser) widget_browser->restore_scroll_position(); + + current_ --; + + // Update modified flag... + proj_.set_modflag(current_ != save_); + + // Update undo/redo menu items... + // if (current_ <= 0) main_menu[undo_item].deactivate(); + // main_menu[redo_item].activate(); + widget_browser->rebuild(); + proj_.update_settings_dialog(); + resume(); +} + +/** + \param[in] type set a new type, or set to 0 to clear the once_type without setting a checkpoint + \return 1 if the checkpoint was set, 0 if this is a repeating event + */ +int Undo::checkpoint(OnceType type) { + if (type == OnceType::ALWAYS) { + once_type_ = OnceType::ALWAYS; + return 0; + } + if (paused_) return 0; + if (once_type_ != type) { + checkpoint(); + once_type_ = type; + return 1; + } else { + // do not add more checkpoints for the same undo type + return 0; + } +} + +// Save current file to undo buffer +void Undo::checkpoint() { + // printf("checkpoint(): current_=%d, paused_=%d, modflag=%d\n", + // current_, paused_, modflag); + + // Don't checkpoint if suspend() has been called... + if (paused_) return; + + // int undo_item = main_menubar->find_index(undo_cb); + // int redo_item = main_menubar->find_index(redo_cb); + once_type_ = OnceType::ALWAYS; + + // Save the current UI to a checkpoint file... + const char *file = filename(current_); + if (!fld::io::write_file(proj_, file)) { + // Don't attempt to do undo stuff if we can't write a checkpoint file... + perror(file); + return; + } + + // Update the saved level... + if (proj_.modflag && current_ <= save_) save_ = -1; + else if (!proj_.modflag) save_ = current_; + + // Update the current undo level... + current_ ++; + last_ = current_; + if (current_ > max_) max_ = current_; + + // Enable the Undo and disable the Redo menu items... + // main_menu[undo_item].activate(); + // main_menu[redo_item].deactivate(); +} + +// Clear undo buffer +void Undo::clear() { + // int undo_item = main_menubar->find_index(undo_cb); + // int redo_item = main_menubar->find_index(redo_cb); + // Remove old checkpoint files... + for (int i = 0; i <= max_; i ++) { + fl_unlink(filename(i)); + } + + // Reset current, last, and save indices... + current_ = last_ = max_ = 0; + if (proj_.modflag) save_ = -1; + else save_ = 0; + + // Disable the Undo and Redo menu items... + // main_menu[undo_item].deactivate(); + // main_menu[redo_item].deactivate(); +} + +// Resume undo checkpoints +void Undo::resume() { + paused_--; +} + +// Suspend undo checkpoints +void Undo::suspend() { + paused_++; +} + +void Undo::undo_cb(Fl_Widget *, void *) { + Fluid.proj.undo.undo(); +} + +void Undo::redo_cb(Fl_Widget *, void *) { + Fluid.proj.undo.redo(); +} diff --git a/fluid/proj/undo.h b/fluid/proj/undo.h new file mode 100644 index 000000000..f87c747bd --- /dev/null +++ b/fluid/proj/undo.h @@ -0,0 +1,93 @@ +// +// Fluid Undo 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 undo_h +#define undo_h + +#include + +class Fl_Widget; + +namespace fld { + +class Project; + +namespace proj { + +class Undo +{ +public: + + enum class OnceType { + ALWAYS = 0, + WINDOW_RESIZE + }; + + /// Link Undo class to this project. + Project &proj_; + /// Current undo level in buffer + int current_ = 0; + /// Last undo level in buffer + int last_ = 0; + // Maximum undo level used + int max_ = 0; + /// Last undo level that was saved + int save_ = -1; + // Undo checkpointing paused? + int paused_ = 0; + // Undo file path + char path_[FL_PATH_MAX] { }; + // length w/o filename + unsigned int path_len_ = 0; + /// Suspend further undos of the same type + OnceType once_type_ = OnceType::ALWAYS; + +public: + + // Constructor. + Undo(Project &p); + // Destructor. + ~Undo(); + + // Save current file to undo buffer + void checkpoint(); + // Save undo buffer once until a different checkpoint type is called + int checkpoint(OnceType type); + // Clear undo buffer + void clear(); + // Resume undo checkpoints + void resume(); + // Suspend undo checkpoints + void suspend(); + // Return the undo filename. + char *filename(int level); + + // Redo menu callback + void redo(); + // Undo menu callback + void undo(); + + // Redo menu callback + static void redo_cb(Fl_Widget *, void *); + // Undo menu callback + static void undo_cb(Fl_Widget *, void *); +}; + +} // namespace fld +} // namespace proj + + +#endif // !undo_h diff --git a/fluid/rsrcs/pixmaps.cxx b/fluid/rsrcs/pixmaps.cxx index dbcebe043..640cf79d9 100644 --- a/fluid/rsrcs/pixmaps.cxx +++ b/fluid/rsrcs/pixmaps.cxx @@ -1,7 +1,7 @@ // -// Fluid Image management for the Fast Light Tool Kit (FLTK). +// Fluid GUI Image code 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 @@ -16,7 +16,7 @@ #include "rsrcs/pixmaps.h" -#include "nodes/Fl_Type.h" +#include "nodes/Node.h" #include @@ -90,7 +90,7 @@ Fl_Pixmap *protected_pixmap; Fl_Pixmap *invisible_pixmap; Fl_Pixmap *compressed_pixmap; -Fl_Pixmap *pixmap[ID_Max_] = { NULL }; +Fl_Pixmap *pixmap[(int)Type::Max_] = { nullptr }; /** Draw a zoom cross pointing in all four diagonal directions @@ -152,74 +152,74 @@ void loadPixmaps() invisible_pixmap = new Fl_Pixmap(invisible_xpm); invisible_pixmap->scale(16, 16); compressed_pixmap = new Fl_Pixmap(compressed_xpm); compressed_pixmap->scale(16, 16); - pixmap[ID_Window] = tmp = new Fl_Pixmap(flWindow_xpm); tmp->scale(16, 16); - pixmap[ID_Button] = tmp = new Fl_Pixmap(flButton_xpm); tmp->scale(16, 16); - pixmap[ID_Check_Button] = tmp = new Fl_Pixmap(flCheckButton_xpm); tmp->scale(16, 16); - pixmap[ID_Round_Button] = tmp = new Fl_Pixmap(flRoundButton_xpm); tmp->scale(16, 16); - - pixmap[ID_Box] = tmp = new Fl_Pixmap(flBox_xpm); tmp->scale(16, 16); - pixmap[ID_Group] = tmp = new Fl_Pixmap(flGroup_xpm); tmp->scale(16, 16); - pixmap[ID_Function] = tmp = new Fl_Pixmap(flFunction_xpm); tmp->scale(16, 16); - pixmap[ID_Code] = tmp = new Fl_Pixmap(flCode_xpm); tmp->scale(16, 16); - pixmap[ID_CodeBlock] = tmp = new Fl_Pixmap(flCodeBlock_xpm); tmp->scale(16, 16); - pixmap[ID_Decl] = tmp = new Fl_Pixmap(flDeclaration_xpm); tmp->scale(16, 16); - - pixmap[ID_DeclBlock] = tmp = new Fl_Pixmap(flDeclarationBlock_xpm); tmp->scale(16, 16); - pixmap[ID_Class] = tmp = new Fl_Pixmap(flClass_xpm); tmp->scale(16, 16); - pixmap[ID_Tabs] = tmp = new Fl_Pixmap(flTabs_xpm); tmp->scale(16, 16); - pixmap[ID_Input] = tmp = new Fl_Pixmap(flInput_xpm); tmp->scale(16, 16); - pixmap[ID_Choice] = tmp = new Fl_Pixmap(flChoice_xpm); tmp->scale(16, 16); - - pixmap[ID_Menu_Item] = tmp = new Fl_Pixmap(flMenuitem_xpm); tmp->scale(16, 16); - pixmap[ID_Menu_Bar] = tmp = new Fl_Pixmap(flMenubar_xpm); tmp->scale(16, 16); - pixmap[ID_Submenu] = tmp = new Fl_Pixmap(flSubmenu_xpm); tmp->scale(16, 16); - pixmap[ID_Scroll] = tmp = new Fl_Pixmap(flScroll_xpm); tmp->scale(16, 16); - pixmap[ID_Tile] = tmp = new Fl_Pixmap(flTile_xpm); tmp->scale(16, 16); - pixmap[ID_Wizard] = tmp = new Fl_Pixmap(flWizard_xpm); tmp->scale(16, 16); - - pixmap[ID_Pack] = tmp = new Fl_Pixmap(flPack_xpm); tmp->scale(16, 16); - pixmap[ID_Return_Button] = tmp = new Fl_Pixmap(flReturnButton_xpm); tmp->scale(16, 16); - pixmap[ID_Light_Button] = tmp = new Fl_Pixmap(flLightButton_xpm); tmp->scale(16, 16); - pixmap[ID_Repeat_Button] = tmp = new Fl_Pixmap(flRepeatButton_xpm); tmp->scale(16, 16); - pixmap[ID_Menu_Button] = tmp = new Fl_Pixmap(flMenuButton_xpm); tmp->scale(16, 16); - - pixmap[ID_Output] = tmp = new Fl_Pixmap(flOutput_xpm); tmp->scale(16, 16); - pixmap[ID_Text_Display] = tmp = new Fl_Pixmap(flTextDisplay_xpm); tmp->scale(16, 16); - pixmap[ID_Text_Editor] = tmp = new Fl_Pixmap(flTextEdit_xpm); tmp->scale(16, 16); - pixmap[ID_File_Input] = tmp = new Fl_Pixmap(flFileInput_xpm); tmp->scale(16, 16); - pixmap[ID_Browser] = tmp = new Fl_Pixmap(flBrowser_xpm); tmp->scale(16, 16); - - pixmap[ID_Check_Browser] = tmp = new Fl_Pixmap(flCheckBrowser_xpm); tmp->scale(16, 16); - pixmap[ID_File_Browser] = tmp = new Fl_Pixmap(flFileBrowser_xpm); tmp->scale(16, 16); - pixmap[ID_Clock] = tmp = new Fl_Pixmap(flClock_xpm); tmp->scale(16, 16); - pixmap[ID_Help_View] = tmp = new Fl_Pixmap(flHelp_xpm); tmp->scale(16, 16); - pixmap[ID_Progress] = tmp = new Fl_Pixmap(flProgress_xpm); tmp->scale(16, 16); - - pixmap[ID_Slider] = tmp = new Fl_Pixmap(flSlider_xpm); tmp->scale(16, 16); - pixmap[ID_Scrollbar] = tmp = new Fl_Pixmap(flScrollBar_xpm); tmp->scale(16, 16); - pixmap[ID_Value_Slider] = tmp = new Fl_Pixmap(flValueSlider_xpm); tmp->scale(16, 16); - pixmap[ID_Adjuster] = tmp = new Fl_Pixmap(flAdjuster_xpm); tmp->scale(16, 16); - pixmap[ID_Counter] = tmp = new Fl_Pixmap(flCounter_xpm); tmp->scale(16, 16); - - pixmap[ID_Dial] = tmp = new Fl_Pixmap(flDial_xpm); tmp->scale(16, 16); - pixmap[ID_Roller] = tmp = new Fl_Pixmap(flRoller_xpm); tmp->scale(16, 16); - pixmap[ID_Value_Input] = tmp = new Fl_Pixmap(flValueInput_xpm); tmp->scale(16, 16); - pixmap[ID_Value_Output] = tmp = new Fl_Pixmap(flValueOutput_xpm); tmp->scale(16, 16); - pixmap[ID_Comment] = tmp = new Fl_Pixmap(flComment_xpm); tmp->scale(16, 16); - - pixmap[ID_Spinner] = tmp = new Fl_Pixmap(flSpinner_xpm); tmp->scale(16, 16); - pixmap[ID_Widget_Class] = tmp = new Fl_Pixmap(flWidgetClass_xpm); tmp->scale(16, 16); - pixmap[ID_Data] = tmp = new Fl_Pixmap(flData_xpm); tmp->scale(16, 16); - pixmap[ID_Tree] = tmp = new Fl_Pixmap(flTree_xpm); tmp->scale(16, 16); - pixmap[ID_Table] = tmp = new Fl_Pixmap(flTable_xpm); tmp->scale(16, 16); - - pixmap[ID_Terminal] = tmp = new Fl_Pixmap(flSimpleTerminal_xpm); tmp->scale(16, 16); - pixmap[ID_Input_Choice] = tmp = new Fl_Pixmap(flInputChoice_xpm); tmp->scale(16, 16); - pixmap[ID_Checkbox_Menu_Item] = tmp = new Fl_Pixmap(flCheckMenuitem_xpm); tmp->scale(16, 16); - pixmap[ID_Radio_Menu_Item] = tmp = new Fl_Pixmap(flRadioMenuitem_xpm); tmp->scale(16, 16); - - pixmap[ID_Flex] = tmp = new Fl_Pixmap(flFlex_xpm); tmp->scale(16, 16); - pixmap[ID_Grid] = tmp = new Fl_Pixmap(flGrid_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Window] = tmp = new Fl_Pixmap(flWindow_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Button] = tmp = new Fl_Pixmap(flButton_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Check_Button] = tmp = new Fl_Pixmap(flCheckButton_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Round_Button] = tmp = new Fl_Pixmap(flRoundButton_xpm); tmp->scale(16, 16); + + pixmap[(int)Type::Box] = tmp = new Fl_Pixmap(flBox_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Group] = tmp = new Fl_Pixmap(flGroup_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Function] = tmp = new Fl_Pixmap(flFunction_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Code] = tmp = new Fl_Pixmap(flCode_xpm); tmp->scale(16, 16); + pixmap[(int)Type::CodeBlock] = tmp = new Fl_Pixmap(flCodeBlock_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Decl] = tmp = new Fl_Pixmap(flDeclaration_xpm); tmp->scale(16, 16); + + pixmap[(int)Type::DeclBlock] = tmp = new Fl_Pixmap(flDeclarationBlock_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Class] = tmp = new Fl_Pixmap(flClass_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Tabs] = tmp = new Fl_Pixmap(flTabs_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Input] = tmp = new Fl_Pixmap(flInput_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Choice] = tmp = new Fl_Pixmap(flChoice_xpm); tmp->scale(16, 16); + + pixmap[(int)Type::Menu_Item] = tmp = new Fl_Pixmap(flMenuitem_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Menu_Bar] = tmp = new Fl_Pixmap(flMenubar_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Submenu] = tmp = new Fl_Pixmap(flSubmenu_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Scroll] = tmp = new Fl_Pixmap(flScroll_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Tile] = tmp = new Fl_Pixmap(flTile_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Wizard] = tmp = new Fl_Pixmap(flWizard_xpm); tmp->scale(16, 16); + + pixmap[(int)Type::Pack] = tmp = new Fl_Pixmap(flPack_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Return_Button] = tmp = new Fl_Pixmap(flReturnButton_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Light_Button] = tmp = new Fl_Pixmap(flLightButton_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Repeat_Button] = tmp = new Fl_Pixmap(flRepeatButton_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Menu_Button] = tmp = new Fl_Pixmap(flMenuButton_xpm); tmp->scale(16, 16); + + pixmap[(int)Type::Output] = tmp = new Fl_Pixmap(flOutput_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Text_Display] = tmp = new Fl_Pixmap(flTextDisplay_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Text_Editor] = tmp = new Fl_Pixmap(flTextEdit_xpm); tmp->scale(16, 16); + pixmap[(int)Type::File_Input] = tmp = new Fl_Pixmap(flFileInput_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Browser] = tmp = new Fl_Pixmap(flBrowser_xpm); tmp->scale(16, 16); + + pixmap[(int)Type::Check_Browser] = tmp = new Fl_Pixmap(flCheckBrowser_xpm); tmp->scale(16, 16); + pixmap[(int)Type::File_Browser] = tmp = new Fl_Pixmap(flFileBrowser_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Clock] = tmp = new Fl_Pixmap(flClock_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Help_View] = tmp = new Fl_Pixmap(flHelp_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Progress] = tmp = new Fl_Pixmap(flProgress_xpm); tmp->scale(16, 16); + + pixmap[(int)Type::Slider] = tmp = new Fl_Pixmap(flSlider_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Scrollbar] = tmp = new Fl_Pixmap(flScrollBar_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Value_Slider] = tmp = new Fl_Pixmap(flValueSlider_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Adjuster] = tmp = new Fl_Pixmap(flAdjuster_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Counter] = tmp = new Fl_Pixmap(flCounter_xpm); tmp->scale(16, 16); + + pixmap[(int)Type::Dial] = tmp = new Fl_Pixmap(flDial_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Roller] = tmp = new Fl_Pixmap(flRoller_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Value_Input] = tmp = new Fl_Pixmap(flValueInput_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Value_Output] = tmp = new Fl_Pixmap(flValueOutput_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Comment] = tmp = new Fl_Pixmap(flComment_xpm); tmp->scale(16, 16); + + pixmap[(int)Type::Spinner] = tmp = new Fl_Pixmap(flSpinner_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Widget_Class] = tmp = new Fl_Pixmap(flWidgetClass_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Data] = tmp = new Fl_Pixmap(flData_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Tree] = tmp = new Fl_Pixmap(flTree_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Table] = tmp = new Fl_Pixmap(flTable_xpm); tmp->scale(16, 16); + + pixmap[(int)Type::Terminal] = tmp = new Fl_Pixmap(flSimpleTerminal_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Input_Choice] = tmp = new Fl_Pixmap(flInputChoice_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Checkbox_Menu_Item] = tmp = new Fl_Pixmap(flCheckMenuitem_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Radio_Menu_Item] = tmp = new Fl_Pixmap(flRadioMenuitem_xpm); tmp->scale(16, 16); + + pixmap[(int)Type::Flex] = tmp = new Fl_Pixmap(flFlex_xpm); tmp->scale(16, 16); + pixmap[(int)Type::Grid] = tmp = new Fl_Pixmap(flGrid_xpm); tmp->scale(16, 16); fl_add_symbol("fd_zoom", fd_zoom, 1); } diff --git a/fluid/rsrcs/pixmaps.h b/fluid/rsrcs/pixmaps.h index 628bcd199..a09362f3d 100644 --- a/fluid/rsrcs/pixmaps.h +++ b/fluid/rsrcs/pixmaps.h @@ -1,7 +1,7 @@ // -// Fluid Image management for the Fast Light Tool Kit (FLTK). +// Fluid GUI Image 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 diff --git a/fluid/tools/ExternalCodeEditor_UNIX.cxx b/fluid/tools/ExternalCodeEditor_UNIX.cxx index bf472d896..9e9ffd00e 100644 --- a/fluid/tools/ExternalCodeEditor_UNIX.cxx +++ b/fluid/tools/ExternalCodeEditor_UNIX.cxx @@ -5,8 +5,8 @@ #include "ExternalCodeEditor_UNIX.h" -#include "app/fluid.h" -#include "app/project.h" +#include "Fluid.h" +#include "Project.h" #include /* Fl_Timeout_Handler.. */ #include /* fl_alert() */ @@ -23,9 +23,11 @@ #include /* free().. */ #include /* snprintf().. */ +using namespace fld; + // Static local data static int L_editors_open = 0; // keep track of #editors open -static Fl_Timeout_Handler L_update_timer_cb = 0; // app's update timer callback +static Fl_Timeout_Handler L_update_timer_cb = nullptr; // app's update timer callback // [Static/Local] See if file exists static int is_file(const char *filename) { @@ -57,7 +59,7 @@ static int is_dir(const char *dirname) { */ ExternalCodeEditor::ExternalCodeEditor() { pid_ = -1; - filename_ = 0; + filename_ = nullptr; file_mtime_ = 0; file_size_ = 0; alert_pipe_[0] = alert_pipe_[1] = -1; @@ -69,11 +71,11 @@ ExternalCodeEditor::ExternalCodeEditor() { This also closes the external editor. */ ExternalCodeEditor::~ExternalCodeEditor() { - if ( G_debug ) + if ( Fluid.debug_external_editor ) printf("ExternalCodeEditor() DTOR CALLED (this=%p, pid=%ld)\n", (void*)this, (long)pid_); close_editor(); // close editor, delete tmp file - set_filename(0); // free()s filename + set_filename(nullptr); // free()s filename if (alert_pipe_open_) { Fl::remove_fd(alert_pipe_[0]); @@ -85,12 +87,12 @@ ExternalCodeEditor::~ExternalCodeEditor() { /** Set the filename for the file we wish to edit. Handles memory allocation/free. - If set to NULL, frees memory. + If set to nullptr, frees memory. \param[in] val new filename */ void ExternalCodeEditor::set_filename(const char *val) { if ( filename_ ) free((void*)filename_); - filename_ = val ? fl_strdup(val) : 0; + filename_ = val ? fl_strdup(val) : nullptr; } /** @@ -105,7 +107,7 @@ int ExternalCodeEditor::is_editing() { Wait for editor to close */ void ExternalCodeEditor::close_editor() { - if ( G_debug ) printf("close_editor() called: pid=%ld\n", long(pid_)); + if ( Fluid.debug_external_editor ) printf("close_editor() called: pid=%ld\n", long(pid_)); // Wait until editor is closed + reaped while ( is_editing() ) { switch ( reap_editor() ) { @@ -119,7 +121,7 @@ void ExternalCodeEditor::close_editor() { switch ( fl_choice("Please close external editor\npid=%ld file=%s", "Force Close", // button 0 "Closed", // button 1 - 0, // button 2 + nullptr, // button 2 long(pid_), filename() ) ) { case 0: // Force Close kill_editor(); @@ -141,7 +143,7 @@ void ExternalCodeEditor::close_editor() { The dtor calls this to ensure no editors remain running when fluid exits. */ void ExternalCodeEditor::kill_editor() { - if ( G_debug ) printf("kill_editor() called: pid=%ld\n", (long)pid_); + if ( Fluid.debug_external_editor ) printf("kill_editor() called: pid=%ld\n", (long)pid_); if ( !is_editing() ) return; // editor not running? return.. kill(pid_, SIGTERM); // kill editor int wcount = 0; @@ -164,7 +166,7 @@ void ExternalCodeEditor::kill_editor() { } continue; case 1: // process reaped (reap_editor() sets pid_ to -1) - if ( G_debug ) + if ( Fluid.debug_external_editor ) printf("*** REAPED KILLED EXTERNAL EDITOR: PID %ld\n", (long)pid_reaped); break; } @@ -185,7 +187,7 @@ void ExternalCodeEditor::kill_editor() { \return -1 error getting file info (strerror() has reason) */ int ExternalCodeEditor::handle_changes(const char **code, int force) { - code[0] = 0; + code[0] = nullptr; if ( !is_editing() ) return 0; // Get current time/size info, see if file changed int changed = 0; @@ -239,13 +241,13 @@ int ExternalCodeEditor::remove_tmpfile() { if ( !tmpfile ) return 0; // Filename set? remove (if exists) and zero filename/mtime/size if ( is_file(tmpfile) ) { - if ( G_debug ) printf("Removing tmpfile '%s'\n", tmpfile); + if ( Fluid.debug_external_editor ) printf("Removing tmpfile '%s'\n", tmpfile); if ( remove(tmpfile) < 0 ) { fl_alert("WARNING: Can't remove() '%s': %s", tmpfile, strerror(errno)); return -1; } } - set_filename(0); + set_filename(nullptr); file_mtime_ = 0; file_size_ = 0; return 1; @@ -268,7 +270,7 @@ const char* ExternalCodeEditor::tmpdir_name() { void ExternalCodeEditor::tmpdir_clear() { const char *tmpdir = tmpdir_name(); if ( is_dir(tmpdir) ) { - if ( G_debug ) printf("Removing tmpdir '%s'\n", tmpdir); + if ( Fluid.debug_external_editor ) printf("Removing tmpdir '%s'\n", tmpdir); if ( rmdir(tmpdir) < 0 ) { fl_alert("WARNING: Can't rmdir() '%s': %s", tmpdir, strerror(errno)); } @@ -278,7 +280,7 @@ void ExternalCodeEditor::tmpdir_clear() { /** Creates temp dir (if doesn't exist) and returns the dirname as a static string. - \return NULL on error, dialog shows reason. + \return nullptr on error, dialog shows reason. */ const char* ExternalCodeEditor::create_tmpdir() { const char *dirname = tmpdir_name(); @@ -286,7 +288,7 @@ const char* ExternalCodeEditor::create_tmpdir() { if ( mkdir(dirname, 0777) < 0 ) { fl_alert("can't create directory '%s': %s", dirname, strerror(errno)); - return NULL; + return nullptr; } } return dirname; @@ -294,13 +296,13 @@ const char* ExternalCodeEditor::create_tmpdir() { /** Returns temp filename in static buffer. - \return NULL if can't, posts dialog explaining why. + \return nullptr if can't, posts dialog explaining why. */ const char* ExternalCodeEditor::tmp_filename() { static char path[FL_PATH_MAX+1]; const char *tmpdir = create_tmpdir(); - if ( !tmpdir ) return 0; - const char *ext = g_project.code_file_name.c_str(); // e.g. ".cxx" + if ( !tmpdir ) return nullptr; + const char *ext = Fluid.proj.code_file_name.c_str(); // e.g. ".cxx" snprintf(path, FL_PATH_MAX, "%s/%p%s", tmpdir, (void*)this, ext); path[FL_PATH_MAX] = 0; return path; @@ -308,12 +310,12 @@ const char* ExternalCodeEditor::tmp_filename() { /** Save string 'code' to 'filename', returning file's mtime/size. - 'code' can be NULL -- writes an empty file if so. + 'code' can be nullptr -- writes an empty file if so. \return 0 on success \return -1 on error (posts dialog with reason) */ static int save_file(const char *filename, const char *code) { - if ( code == 0 ) code = ""; // NULL? write an empty file + if ( code == nullptr ) code = ""; // nullptr? write an empty file int fd = open(filename, O_WRONLY|O_CREAT, 0666); if ( fd == -1 ) { fl_alert("ERROR: open() '%s': %s", filename, strerror(errno)); @@ -336,7 +338,7 @@ static int save_file(const char *filename, const char *code) { /** Convert string 's' to array of argv[], useful for execve(). - - 's' will be modified (words will be NULL separated) + - 's' will be modified (words will be nullptr separated) - argv[] will end up pointing to the words of 's' - Caller must free argv with: free(argv); \return -1 in case of memory allocation error @@ -346,14 +348,14 @@ static int make_args(char *s, // string containing words (gets trashed!) int *aargc, // pointer to argc char ***aargv) { // pointer to argv char *ss, **argv; - if ((argv=(char**)malloc(sizeof(char*) * (strlen(s)/2)))==NULL) { + if ((argv=(char**)malloc(sizeof(char*) * (strlen(s)/2)))==nullptr) { return -1; } int t; - for(t=0; (t==0)?(ss=strtok(s," \t")):(ss=strtok(0," \t")); t++) { + for(t=0; (t==0)?(ss=strtok(s," \t")):(ss=strtok(nullptr," \t")); t++) { argv[t] = ss; } - argv[t] = 0; + argv[t] = nullptr; aargv[0] = argv; aargc[0] = t; return(t); @@ -383,7 +385,7 @@ void ExternalCodeEditor::open_alert_pipe() { */ int ExternalCodeEditor::start_editor(const char *editor_cmd, const char *filename) { - if ( G_debug ) printf("start_editor() cmd='%s', filename='%s'\n", + if ( Fluid.debug_external_editor ) printf("start_editor() cmd='%s', filename='%s'\n", editor_cmd, filename); char cmd[1024]; snprintf(cmd, sizeof(cmd), "%s %s", editor_cmd, filename); @@ -399,7 +401,7 @@ int ExternalCodeEditor::start_editor(const char *editor_cmd, // NOTE: no FLTK calls after a fork. Use a pipe to tell the app if the // command can't launch int nargs; - char **args = 0; + char **args = nullptr; if (make_args(cmd, &nargs, &args) > 0) { execvp(args[0], args); // run command - doesn't return if succeeds if (alert_pipe_open_) { @@ -416,7 +418,7 @@ int ExternalCodeEditor::start_editor(const char *editor_cmd, default: // parent if ( L_editors_open++ == 0 ) // first editor? start timers { start_update_timer(); } - if ( G_debug ) + if ( Fluid.debug_external_editor ) printf("--- EDITOR STARTED: pid_=%ld #open=%d\n", (long)pid_, L_editors_open); break; } @@ -426,7 +428,7 @@ int ExternalCodeEditor::start_editor(const char *editor_cmd, /** Try to reap external editor process. - If 'pid_reaped' not NULL, returns PID of reaped editor. + If 'pid_reaped' not nullptr, returns PID of reaped editor. \return -2: editor not open \return -1: waitpid() failed (errno has reason) @@ -453,7 +455,7 @@ int ExternalCodeEditor::reap_editor(pid_t *pid_reaped) { { stop_update_timer(); } break; } - if ( G_debug ) + if ( Fluid.debug_external_editor ) printf("*** EDITOR REAPED: pid=%ld #open=%d\n", long(wpid), L_editors_open); return 1; } @@ -462,7 +464,7 @@ int ExternalCodeEditor::reap_editor(pid_t *pid_reaped) { Open external editor using 'editor_cmd' to edit 'code'. 'code' contains multiline code to be edited as a temp file. - 'code' can be NULL -- edits an empty file if so. + 'code' can be nullptr -- edits an empty file if so. \return 0 if succeeds \return -1 if can't open editor (already open, etc), @@ -492,7 +494,7 @@ int ExternalCodeEditor::open_editor(const char *editor_cmd, filename(), (long)pid_); return 0; case 1: // process reaped, wpid is pid reaped - if ( G_debug ) + if ( Fluid.debug_external_editor ) printf("*** REAPED EXTERNAL EDITOR: PID %ld\n", (long)wpid); break; // fall thru to open new editor instance } @@ -512,7 +514,7 @@ int ExternalCodeEditor::open_editor(const char *editor_cmd, file_mtime_ = sbuf.st_mtime; file_size_ = sbuf.st_size; if ( start_editor(editor_cmd, filename()) < 0 ) { // open file in external editor - if ( G_debug ) printf("Editor failed to start\n"); + if ( Fluid.debug_external_editor ) printf("Editor failed to start\n"); return -1; // errors were shown in dialog } return 0; @@ -523,7 +525,7 @@ int ExternalCodeEditor::open_editor(const char *editor_cmd, */ void ExternalCodeEditor::start_update_timer() { if ( !L_update_timer_cb ) return; - if ( G_debug ) printf("--- TIMER: STARTING UPDATES\n"); + if ( Fluid.debug_external_editor ) printf("--- TIMER: STARTING UPDATES\n"); Fl::add_timeout(2.0, L_update_timer_cb); } @@ -532,7 +534,7 @@ void ExternalCodeEditor::start_update_timer() { */ void ExternalCodeEditor::stop_update_timer() { if ( !L_update_timer_cb ) return; - if ( G_debug ) printf("--- TIMER: STOPPING UPDATES\n"); + if ( Fluid.debug_external_editor ) printf("--- TIMER: STOPPING UPDATES\n"); Fl::remove_timeout(L_update_timer_cb); } diff --git a/fluid/tools/ExternalCodeEditor_UNIX.h b/fluid/tools/ExternalCodeEditor_UNIX.h index 644f22afa..2142d5a10 100644 --- a/fluid/tools/ExternalCodeEditor_UNIX.h +++ b/fluid/tools/ExternalCodeEditor_UNIX.h @@ -7,7 +7,7 @@ #ifndef _EXTCODEEDITOR_H #define _EXTCODEEDITOR_H -#include "app/fluid.h" +#include "Fluid.h" #include @@ -42,7 +42,7 @@ public: ExternalCodeEditor(); ~ExternalCodeEditor(); int is_editing(); - int reap_editor(pid_t *pid_reaped=NULL); + int reap_editor(pid_t *pid_reaped=nullptr); void close_editor(); const char *filename() { return filename_; } int open_editor(const char *editor_cmd, const char *code); diff --git a/fluid/tools/ExternalCodeEditor_WIN32.cxx b/fluid/tools/ExternalCodeEditor_WIN32.cxx index 09688d16a..64d791400 100644 --- a/fluid/tools/ExternalCodeEditor_WIN32.cxx +++ b/fluid/tools/ExternalCodeEditor_WIN32.cxx @@ -17,8 +17,8 @@ // Note: This entire file Windows only. #include "tools/ExternalCodeEditor_WIN32.h" -#include "app/fluid.h" -#include "app/project.h" +#include "Fluid.h" +#include "Project.h" #include // Fl_Timeout_Handler.. #include // fl_alert() @@ -28,17 +28,17 @@ #include // snprintf() #include -extern int G_debug; // defined in fluid.cxx +using namespace fld; // Static local data static int L_editors_open = 0; // keep track of #editors open static Fl_Timeout_Handler L_update_timer_cb = 0; // app's update timer callback -static wchar_t *wbuf = NULL; -static char *abuf = NULL; +static wchar_t *wbuf = nullptr; +static char *abuf = nullptr; static wchar_t *utf8_to_wchar(const char *utf8, wchar_t *&wbuf, int lg = -1) { unsigned len = (lg >= 0) ? (unsigned)lg : (unsigned)strlen(utf8); - unsigned wn = fl_utf8toUtf16(utf8, len, NULL, 0) + 1; // Query length + unsigned wn = fl_utf8toUtf16(utf8, len, nullptr, 0) + 1; // Query length wbuf = (wchar_t *)realloc(wbuf, sizeof(wchar_t) * wn); wn = fl_utf8toUtf16(utf8, len, (unsigned short *)wbuf, wn); // Convert string wbuf[wn] = 0; @@ -47,7 +47,7 @@ static wchar_t *utf8_to_wchar(const char *utf8, wchar_t *&wbuf, int lg = -1) { static char *wchar_to_utf8(const wchar_t *wstr, char *&utf8) { unsigned len = (unsigned)wcslen(wstr); - unsigned wn = fl_utf8fromwc(NULL, 0, wstr, len) + 1; // query length + unsigned wn = fl_utf8fromwc(nullptr, 0, wstr, len) + 1; // query length utf8 = (char *)realloc(utf8, wn); wn = fl_utf8fromwc(utf8, wn, wstr, len); // convert string utf8[wn] = 0; @@ -68,7 +68,7 @@ static const char *get_ms_errmsg() { DWORD msize = 0; // Get error message from Windows - msize = FormatMessageW(flags, 0, lastErr, langid, (LPWSTR)&mbuf, 0, NULL); + msize = FormatMessageW(flags, 0, lastErr, langid, (LPWSTR)&mbuf, 0, nullptr); if ( msize == 0 ) { _snprintf(emsg, sizeof(emsg), "Error #%ld", (unsigned long)lastErr); } else { @@ -119,7 +119,7 @@ ExternalCodeEditor::~ExternalCodeEditor() { } // [Protected] Set the filename. Handles memory allocation/free -// If set to NULL, frees memory. +// If set to nullptr, frees memory. // void ExternalCodeEditor::set_filename(const char *val) { if ( filename_ ) free((void*)filename_); @@ -137,7 +137,7 @@ static BOOL CALLBACK terminate_app_enum(HWND hwnd, LPARAM lParam) { GetWindowThreadProcessId(hwnd, &dwID); if (dwID == (DWORD)lParam) { PostMessage(hwnd, WM_CLOSE, 0, 0); - if ( G_debug ) + if ( Fluid.debug_external_editor ) printf("terminate_app_enum() sends WIN_CLOSE to hwnd=%p\n", (void*)hwnd); } return TRUE; @@ -154,12 +154,12 @@ static int terminate_app(DWORD pid, DWORD msecTimeout) { // Wait on handle. If it closes, great. If it times out, use TerminateProcess() int ret = 0; if ( WaitForSingleObject(hProc, msecTimeout) != WAIT_OBJECT_0 ) { - if ( G_debug ) { + if ( Fluid.debug_external_editor ) { printf("WARNING: sent WIN_CLOSE, but timeout after %ld msecs.." "trying TerminateProcess\n", msecTimeout); } if ( TerminateProcess(hProc, 0) == 0 ) { - if ( G_debug ) { + if ( Fluid.debug_external_editor ) { printf("ERROR: TerminateProcess() for pid=%ld failed: %s\n", long(pid), get_ms_errmsg()); } @@ -176,7 +176,7 @@ static int terminate_app(DWORD pid, DWORD msecTimeout) { // [Protected] Wait for editor to close void ExternalCodeEditor::close_editor() { - if ( G_debug ) printf("close_editor() called: pid=%ld\n", long(pinfo_.dwProcessId)); + if ( Fluid.debug_external_editor ) printf("close_editor() called: pid=%ld\n", long(pinfo_.dwProcessId)); // Wait until editor is closed + reaped while ( is_editing() ) { switch ( reap_editor() ) { @@ -210,7 +210,7 @@ void ExternalCodeEditor::close_editor() { // The dtor calls this to ensure no editors remain running when fluid exits. // void ExternalCodeEditor::kill_editor() { - if ( G_debug ) + if ( Fluid.debug_external_editor ) printf("kill_editor() called: pid=%ld\n", (long)pinfo_.dwProcessId); if ( !is_editing() ) return; switch ( terminate_app(pinfo_.dwProcessId, 500) ) { // kill editor, wait up to 1/2 sec to die @@ -222,7 +222,7 @@ void ExternalCodeEditor::kill_editor() { case 0: { // success -- process reaped DWORD pid = pinfo_.dwProcessId; // save pid reap_cleanup(); // clears pinfo_ - if ( G_debug ) + if ( Fluid.debug_external_editor ) printf("*** kill_editor() REAP pid=%ld #open=%ld\n", long(pid), long(L_editors_open)); break; @@ -252,10 +252,10 @@ int ExternalCodeEditor::handle_changes(const char **code, int force) { HANDLE fh = CreateFileW(wbuf, // file to read GENERIC_READ, // reading only FILE_SHARE_READ, // sharing -- allow read share; just getting file size - NULL, // security + nullptr, // security OPEN_EXISTING, // create flags -- must exist 0, // misc flags - NULL); // templates + nullptr); // templates if ( fh == INVALID_HANDLE_VALUE ) return -1; LARGE_INTEGER fsize; // Get file size @@ -315,18 +315,18 @@ int ExternalCodeEditor::handle_changes(const char **code, int force) { // int ExternalCodeEditor::remove_tmpfile() { const char *tmpfile = filename(); - if ( G_debug ) printf("remove_tmpfile() '%s'\n", tmpfile ? tmpfile : "(empty)"); + if ( Fluid.debug_external_editor ) printf("remove_tmpfile() '%s'\n", tmpfile ? tmpfile : "(empty)"); if ( !tmpfile ) return 0; // Filename set? remove (if exists) and zero filename/mtime/size if ( is_file(tmpfile) ) { - if ( G_debug ) printf("Removing tmpfile '%s'\n", tmpfile); + if ( Fluid.debug_external_editor ) printf("Removing tmpfile '%s'\n", tmpfile); utf8_to_wchar(tmpfile, wbuf); if (DeleteFileW(wbuf) == 0) { fl_alert("WARNING: Can't DeleteFile() '%s': %s", tmpfile, get_ms_errmsg()); return -1; } } else { - if ( G_debug ) printf("remove_tmpfile(): is_file(%s) failed\n", tmpfile); + if ( Fluid.debug_external_editor ) printf("remove_tmpfile(): is_file(%s) failed\n", tmpfile); } set_filename(0); memset(&file_mtime_, 0, sizeof(file_mtime_)); @@ -348,7 +348,7 @@ const char* ExternalCodeEditor::tmpdir_name() { static char dirname[100]; _snprintf(dirname, sizeof(dirname), "%s.fluid-%ld", tempdir, (long)GetCurrentProcessId()); - if ( G_debug ) printf("tmpdir_name(): '%s'\n", dirname); + if ( Fluid.debug_external_editor ) printf("tmpdir_name(): '%s'\n", dirname); return dirname; } @@ -358,7 +358,7 @@ const char* ExternalCodeEditor::tmpdir_name() { void ExternalCodeEditor::tmpdir_clear() { const char *tmpdir = tmpdir_name(); if ( is_dir(tmpdir) ) { - if ( G_debug ) printf("Removing tmpdir '%s'\n", tmpdir); + if ( Fluid.debug_external_editor ) printf("Removing tmpdir '%s'\n", tmpdir); utf8_to_wchar(tmpdir, wbuf); if ( RemoveDirectoryW(wbuf) == 0 ) { fl_alert("WARNING: Can't RemoveDirectory() '%s': %s", @@ -368,7 +368,7 @@ void ExternalCodeEditor::tmpdir_clear() { } // [Protected] Creates temp dir (if doesn't exist) and returns the dirname -// as a static string. Returns NULL on error, dialog shows reason. +// as a static string. Returns nullptr on error, dialog shows reason. // const char* ExternalCodeEditor::create_tmpdir() { const char *dirname = tmpdir_name(); @@ -377,27 +377,27 @@ const char* ExternalCodeEditor::create_tmpdir() { if (CreateDirectoryW(wbuf, 0) == 0) { fl_alert("can't create directory '%s': %s", dirname, get_ms_errmsg()); - return NULL; + return nullptr; } } return dirname; } // [Protected] Returns temp filename in static buffer. -// Returns NULL if can't, posts dialog explaining why. +// Returns nullptr if can't, posts dialog explaining why. // const char* ExternalCodeEditor::tmp_filename() { static char path[512]; const char *tmpdir = create_tmpdir(); if ( !tmpdir ) return 0; - const char *ext = g_project.code_file_name.c_str(); // e.g. ".cxx" + const char *ext = Fluid.proj.code_file_name.c_str(); // e.g. ".cxx" _snprintf(path, sizeof(path), "%s\\%p%s", tmpdir, (void*)this, ext); path[sizeof(path)-1] = 0; return path; } // [Static/Local] Save string 'code' to 'filename', returning file's mtime/size -// 'code' can be NULL -- writes an empty file if so. +// 'code' can be nullptr -- writes an empty file if so. // Returns: // 0 on success // -1 on error (posts dialog with reason) @@ -406,17 +406,17 @@ static int save_file(const char *filename, const char *code, FILETIME &file_mtime, // return these since in win32 it's.. LARGE_INTEGER &file_size) { // ..efficient to get while file open - if ( code == 0 ) code = ""; // NULL? write an empty file + if ( code == 0 ) code = ""; // nullptr? write an empty file memset(&file_mtime, 0, sizeof(file_mtime)); memset(&file_size, 0, sizeof(file_size)); utf8_to_wchar(filename, wbuf); HANDLE fh = CreateFileW(wbuf, // filename GENERIC_WRITE, // write only 0, // sharing -- no share during write - NULL, // security + nullptr, // security CREATE_ALWAYS, // create flags -- recreate FILE_ATTRIBUTE_NORMAL, // misc flags - NULL); // templates + nullptr); // templates if ( fh == INVALID_HANDLE_VALUE ) { fl_alert("ERROR: couldn't create file '%s': %s", filename, get_ms_errmsg()); @@ -426,7 +426,7 @@ static int save_file(const char *filename, DWORD clen = (DWORD)strlen(code); DWORD count = 0; int ret = 0; - if ( WriteFile(fh, code, clen, &count, NULL) == 0 ) { + if ( WriteFile(fh, code, clen, &count, nullptr) == 0 ) { fl_alert("ERROR: WriteFile() '%s': %s", filename, get_ms_errmsg()); ret = -1; // fallthru to CloseHandle() } else if ( count != clen ) { @@ -459,7 +459,7 @@ static int save_file(const char *filename, // int ExternalCodeEditor::start_editor(const char *editor_cmd, const char *filename) { - if ( G_debug ) printf("start_editor() cmd='%s', filename='%s'\n", + if ( Fluid.debug_external_editor ) printf("start_editor() cmd='%s', filename='%s'\n", editor_cmd, filename); // Startup info STARTUPINFOW sinfo; @@ -474,14 +474,14 @@ int ExternalCodeEditor::start_editor(const char *editor_cmd, _snprintf(cmd, sizeof(cmd), "%s %s", editor_cmd, filename); utf8_to_wchar(cmd, wbuf); // Start editor process - if (CreateProcessW(NULL, // app name + if (CreateProcessW(nullptr, // app name wbuf, // command to exec - NULL, // secure attribs - NULL, // thread secure attribs + nullptr, // secure attribs + nullptr, // thread secure attribs FALSE, // handle inheritance 0, // creation flags - NULL, // environ block - NULL, // current dir + nullptr, // environ block + nullptr, // current dir &sinfo, // startup info &pinfo_) == 0 ) { // process info fl_alert("CreateProcess() failed to start '%s': %s", @@ -490,7 +490,7 @@ int ExternalCodeEditor::start_editor(const char *editor_cmd, } if ( L_editors_open++ == 0 ) // first editor? start timers { start_update_timer(); } - if ( G_debug ) + if ( Fluid.debug_external_editor ) printf("--- EDITOR STARTED: pid_=%ld #open=%d\n", (long)pinfo_.dwProcessId, L_editors_open); return 0; @@ -512,7 +512,7 @@ void ExternalCodeEditor::reap_cleanup() { } // [Public] Try to reap external editor process -// If 'pid_reaped' not NULL, returns PID of reaped editor. +// If 'pid_reaped' not nullptr, returns PID of reaped editor. // Returns: // -2 -- editor not open // -1 -- WaitForSingleObject() failed (get_ms_errmsg() has reason) @@ -534,7 +534,7 @@ int ExternalCodeEditor::reap_editor(DWORD *pid_reaped) { DWORD wpid = pinfo_.dwProcessId; // save pid reap_cleanup(); // clears pinfo_ if ( pid_reaped ) *pid_reaped = wpid; // return pid to caller - if ( G_debug ) printf("*** EDITOR REAPED: pid=%ld #open=%d\n", + if ( Fluid.debug_external_editor ) printf("*** EDITOR REAPED: pid=%ld #open=%d\n", long(wpid), L_editors_open); return 1; } @@ -548,7 +548,7 @@ int ExternalCodeEditor::reap_editor(DWORD *pid_reaped) { // [Public] Open external editor using 'editor_cmd' to edit 'code'. // // 'code' contains multiline code to be edited as a temp file. -// 'code' can be NULL -- edits an empty file if so. +// 'code' can be nullptr -- edits an empty file if so. // // Returns: // 0 if succeeds @@ -579,7 +579,7 @@ int ExternalCodeEditor::open_editor(const char *editor_cmd, filename(), long(pinfo_.dwProcessId)); return 0; case 1: // process reaped, wpid is pid reaped - if ( G_debug ) + if ( Fluid.debug_external_editor ) printf("*** REAPED EXTERNAL EDITOR: PID %ld\n", long(wpid)); break; // fall thru to open new editor instance } @@ -592,12 +592,12 @@ int ExternalCodeEditor::open_editor(const char *editor_cmd, return -1; // errors were shown in dialog } if ( start_editor(editor_cmd, filename()) < 0 ) { // open file in external editor - if ( G_debug ) printf("Editor failed to start\n"); + if ( Fluid.debug_external_editor ) printf("Editor failed to start\n"); return -1; // errors were shown in dialog } // New editor opened -- start update timer (if not already) if ( L_update_timer_cb && !Fl::has_timeout(L_update_timer_cb) ) { - if ( G_debug ) printf("--- Editor opened: STARTING UPDATE TIMER\n"); + if ( Fluid.debug_external_editor ) printf("--- Editor opened: STARTING UPDATE TIMER\n"); Fl::add_timeout(2.0, L_update_timer_cb); } return 0; @@ -606,14 +606,14 @@ int ExternalCodeEditor::open_editor(const char *editor_cmd, // [Public/Static] Start update timer void ExternalCodeEditor::start_update_timer() { if ( !L_update_timer_cb ) return; - if ( G_debug ) printf("--- TIMER: STARTING UPDATES\n"); + if ( Fluid.debug_external_editor ) printf("--- TIMER: STARTING UPDATES\n"); Fl::add_timeout(2.0, L_update_timer_cb); } // [Public/Static] Stop update timer void ExternalCodeEditor::stop_update_timer() { if ( !L_update_timer_cb ) return; - if ( G_debug ) printf("--- TIMER: STOPPING UPDATES\n"); + if ( Fluid.debug_external_editor ) printf("--- TIMER: STOPPING UPDATES\n"); Fl::remove_timeout(L_update_timer_cb); } diff --git a/fluid/tools/ExternalCodeEditor_WIN32.h b/fluid/tools/ExternalCodeEditor_WIN32.h index 97d93e495..83c50a742 100644 --- a/fluid/tools/ExternalCodeEditor_WIN32.h +++ b/fluid/tools/ExternalCodeEditor_WIN32.h @@ -43,7 +43,7 @@ public: ExternalCodeEditor(); ~ExternalCodeEditor(); int is_editing(); - int reap_editor(DWORD *pid_reaped=NULL); + int reap_editor(DWORD *pid_reaped=nullptr); void close_editor(); const char *filename() { return filename_; } int open_editor(const char *editor_cmd, const char *code); diff --git a/fluid/tools/autodoc.cxx b/fluid/tools/autodoc.cxx index 295ab8d2f..c87b59bc2 100644 --- a/fluid/tools/autodoc.cxx +++ b/fluid/tools/autodoc.cxx @@ -18,11 +18,11 @@ #include "tools/autodoc.h" -#include "app/fluid.h" -#include "app/project.h" +#include "Fluid.h" +#include "Project.h" #include "nodes/factory.h" -#include "nodes/Fl_Widget_Type.h" -#include "nodes/Fl_Window_Type.h" +#include "nodes/Widget_Node.h" +#include "nodes/Window_Node.h" #include "panels/widget_panel.h" #include "panels/function_panel.h" #include "panels/settings_panel.h" @@ -232,7 +232,7 @@ void blend_alpha_bottom(const Fl_RGB_Image *img, int dy) { created in FLTK resolution, even if the screen uses a higher resolution. \param[in] filename the snapshot will be written to this file in png format - \param[in] w draw a bounding box around all widgets in the NULL terminated list + \param[in] w draw a bounding box around all widgets in the nullptr terminated list \param[in] frame add a margin around the bounding box \param[in] blend add another margin around the bounding box that fades to full transparency \param[in] scale scale everything by this factor before saving it @@ -342,7 +342,7 @@ int fl_snapshot(const char *filename, Fl_Widget *w1, Fl_Widget *w2, const Fl_Rect &blend, double scale) { - Fl_Widget *ww[3] = { w1, w2, NULL }; + Fl_Widget *ww[3] = { w1, w2, nullptr }; return fl_snapshot(filename, ww, frame, blend, scale); } @@ -363,7 +363,7 @@ int fl_snapshot(const char *filename, Fl_Widget *w, const Fl_Rect &blend, double scale) { - Fl_Widget *ww[2] = { w, NULL }; + Fl_Widget *ww[2] = { w, nullptr }; return fl_snapshot(filename, ww, frame, blend, scale); } @@ -383,27 +383,27 @@ void run_autodoc(const std::string &target_dir) { // Fl::scheme("gtk+"); // Create a silly project that contains all widgets that we want to document - new_project(false); + Fluid.new_project(false); - /*Fl_Type *t_func = */ add_new_widget_from_user("Function", Strategy::AS_LAST_CHILD, false); - Fl_Window_Type *t_win = (Fl_Window_Type*)add_new_widget_from_user("Fl_Window", Strategy::AS_LAST_CHILD, false); + /*Node *t_func = */ add_new_widget_from_user("Function", Strategy::AS_LAST_CHILD, false); + Window_Node *t_win = (Window_Node*)add_new_widget_from_user("Fl_Window", Strategy::AS_LAST_CHILD, false); t_win->label("My Main Window"); - Fl_Widget_Type *t_grp = (Fl_Widget_Type*)add_new_widget_from_user("Fl_Group", Strategy::AS_LAST_CHILD, false); + Widget_Node *t_grp = (Widget_Node*)add_new_widget_from_user("Fl_Group", Strategy::AS_LAST_CHILD, false); t_grp->public_ = 0; - Fl_Widget_Type *t_btn = (Fl_Widget_Type*)add_new_widget_from_user("Fl_Button", Strategy::AS_LAST_CHILD, false); + Widget_Node *t_btn = (Widget_Node*)add_new_widget_from_user("Fl_Button", Strategy::AS_LAST_CHILD, false); t_btn->comment("Don't press this button!"); t_btn->name("emergency_btn"); ((Fl_Button*)t_btn->o)->shortcut(FL_COMMAND|'g'); - Fl_Type *t_sldr = add_new_widget_from_user("Fl_Slider", Strategy::AS_LAST_CHILD, false); - Fl_Type *t_inp = add_new_widget_from_user("Fl_Input", Strategy::AS_LAST_CHILD, false); - Fl_Type *t_flx = add_new_widget_from_user("Fl_Flex", Strategy::AS_LAST_CHILD, false); - Fl_Type *t_flxc = add_new_widget_from_user("Fl_Button", Strategy::AS_LAST_CHILD, false); + Node *t_sldr = add_new_widget_from_user("Fl_Slider", Strategy::AS_LAST_CHILD, false); + Node *t_inp = add_new_widget_from_user("Fl_Input", Strategy::AS_LAST_CHILD, false); + Node *t_flx = add_new_widget_from_user("Fl_Flex", Strategy::AS_LAST_CHILD, false); + Node *t_flxc = add_new_widget_from_user("Fl_Button", Strategy::AS_LAST_CHILD, false); select_only(t_grp); - Fl_Type *t_grd = add_new_widget_from_user("Fl_Grid", Strategy::AS_LAST_CHILD, false); - Fl_Type *t_grdc = add_new_widget_from_user("Fl_Button", Strategy::AS_LAST_CHILD, false); + Node *t_grd = add_new_widget_from_user("Fl_Grid", Strategy::AS_LAST_CHILD, false); + Node *t_grdc = add_new_widget_from_user("Fl_Button", Strategy::AS_LAST_CHILD, false); widget_browser->rebuild(); - g_project.update_settings_dialog(); + Fluid.proj.update_settings_dialog(); // TODO: FLUID overview @@ -417,9 +417,9 @@ void run_autodoc(const std::string &target_dir) { // explain menubar? // explain widget browser // explain widget browser entry - main_window->size(350, 320); - fl_snapshot((target_dir + "main_window.png").c_str(), main_window, win_margin, win_blend); - fl_snapshot((target_dir + "main_menubar.png").c_str(), main_menubar, row_margin, row_blend); + Fluid.main_window->size(350, 320); + fl_snapshot((target_dir + "Fluid.main_window.png").c_str(), Fluid.main_window, win_margin, win_blend); + fl_snapshot((target_dir + "main_menubar.png").c_str(), Fluid.main_menubar, row_margin, row_blend); fl_snapshot((target_dir + "main_browser.png").c_str(), widget_browser, FL_SNAP_AREA_CLEAR, Fl_Rect(0, 30, FL_SNAP_TO_WINDOW, 100), row_blend, 2.0); @@ -453,7 +453,7 @@ void run_autodoc(const std::string &target_dir) { codeview_panel->show(); Fl::wait(0.2); Fl::flush(); - update_codeview_cb(NULL, NULL); // must be visible on screen for this to work + update_codeview_cb(nullptr, nullptr); // must be visible on screen for this to work cv_tab->value(cv_source_tab); codeview_panel->redraw(); Fl::flush(); @@ -487,41 +487,41 @@ void run_autodoc(const std::string &target_dir) { // ---- dialog types // list and show all non-widget types and their respective dialog boxes - // -- ID_Function + // -- Type::Function Fl_Window *adoc_function_panel = make_function_panel(); f_name_input->value("count_trees(const char *forest_name)"); f_return_type_input->value("unsigned int"); fl_snapshot((target_dir + "function_panel.png").c_str(), adoc_function_panel, win_margin, win_blend); adoc_function_panel->hide(); - // -- ID_Code + // -- Type::Code Fl_Window *adoc_code_panel = make_code_panel(); code_input->buffer()->text("// increment user count\nif (new_user) {\n user_count++;\n}\n"); fl_snapshot((target_dir + "code_panel.png").c_str(), adoc_code_panel, win_margin, win_blend); adoc_code_panel->hide(); - // -- ID_CodeBlock + // -- Type::CodeBlock Fl_Window *adoc_codeblock_panel = make_codeblock_panel(); code_before_input->value("if (test())"); code_after_input->value("// test widgets added..."); fl_snapshot((target_dir + "codeblock_panel.png").c_str(), adoc_codeblock_panel, win_margin, win_blend); adoc_codeblock_panel->hide(); - // -- ID_Decl + // -- Type::Decl Fl_Window *adoc_decl_panel = make_decl_panel(); decl_class_choice->hide(); decl_input->buffer()->text("const char *damage = \"'tis but a scratch\";"); fl_snapshot((target_dir + "decl_panel.png").c_str(), adoc_decl_panel, win_margin, win_blend); adoc_decl_panel->hide(); - // -- ID_DeclBlock + // -- Type::DeclBlock Fl_Window *adoc_declblock_panel = make_declblock_panel(); declblock_before_input->value("#ifdef NDEBUG"); declblock_after_input->value("#endif // NDEBUG"); fl_snapshot((target_dir + "declblock_panel.png").c_str(), adoc_declblock_panel, win_margin, win_blend); adoc_declblock_panel->hide(); - // -- ID_Class + // -- Type::Class Fl_Window *adoc_class_panel = make_class_panel(); decl_class_choice->hide(); c_name_input->value("Zoo_Giraffe"); @@ -529,15 +529,15 @@ void run_autodoc(const std::string &target_dir) { fl_snapshot((target_dir + "class_panel.png").c_str(), adoc_class_panel, win_margin, win_blend); adoc_class_panel->hide(); - // -- ID_Widget_Class is handled like Fl_Window_Type + // -- Type::Widget_Class is handled like Window_Node - // -- ID_Comment + // -- Type::Comment Fl_Window *adoc_comment_panel = make_comment_panel(); comment_input->buffer()->text("Make sure that the giraffe gets enough hay,\nbut the monkey can't reach it."); fl_snapshot((target_dir + "comment_panel.png").c_str(), adoc_comment_panel, win_margin, win_blend); adoc_comment_panel->hide(); - // -- ID_Data + // -- Type::Data Fl_Window *adoc_data_panel = make_data_panel(); data_class_choice->hide(); data_input->value("emulated_ROM"); diff --git a/fluid/tools/autodoc.h b/fluid/tools/autodoc.h index 33b0bd6fc..136a7e119 100644 --- a/fluid/tools/autodoc.h +++ b/fluid/tools/autodoc.h @@ -1,5 +1,5 @@ // -// Widget snapshot header-only file for the Fast Light Tool Kit (FLTK). +// Self-generate snapshots of user interface for FLUID documentation. // // Copyright 2023-2025 by Bill Spitzak and others. // diff --git a/fluid/tools/filename.cxx b/fluid/tools/filename.cxx index bbd749ce1..a5d6e22b3 100644 --- a/fluid/tools/filename.cxx +++ b/fluid/tools/filename.cxx @@ -1,7 +1,7 @@ // -// Filename expansion routines for the Fast Light Tool Kit (FLTK). +// Filename handling code 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 @@ -95,3 +95,31 @@ std::string fl_filename_shortened(const std::string &filename, int max_chars) { return homed_filename; } } + +/** + Make sure that a path name ends with a forward slash. + \param[in] str directory or path name + \return a new string, ending with a '/' + */ +std::string fld::end_with_slash(const std::string &str) { + char last = str[str.size()-1]; + if (last !='/' && last != '\\') + return str + "/"; + else + return str; +} + +/** + Replace Windows '\\' directory separator with UNix '/' separators. + \param[in] fn a file path in Unix or Windows format + \return a copy of the file path in Unix format. + */ +std::string fld::fix_separators(const std::string &fn) { + std::string ret = fn; + for (size_t i=0; i @@ -57,7 +57,7 @@ int fld::widget::Bin_Button::handle(int inEvent) Fl_Button::handle(FL_RELEASE); // make it into a dnd event const char *type_name = (const char*)user_data(); - Fl_Type::current_dnd = Fl_Type::current; + Fluid.proj.tree.current_dnd = Fluid.proj.tree.current; Fl::copy(type_name, (int)strlen(type_name)+1, 0); Fl::dnd(); return 1; @@ -81,7 +81,7 @@ int fld::widget::Bin_Button::handle(int inEvent) */ int fld::widget::Bin_Window_Button::handle(int inEvent) { - static Fl_Window *drag_win = NULL; + static Fl_Window *drag_win = nullptr; int ret = 0; switch (inEvent) { case FL_PUSH: @@ -107,18 +107,18 @@ int fld::widget::Bin_Window_Button::handle(int inEvent) case FL_RELEASE: if (drag_win) { Fl::delete_widget(drag_win); - drag_win = NULL; + drag_win = nullptr; // create a new window here - Fl_Type *prototype = typename_to_prototype((char*)user_data()); + Node *prototype = typename_to_prototype((char*)user_data()); if (prototype) { - Fl_Type *new_type = add_new_widget_from_user(prototype, Strategy::AFTER_CURRENT); - if (new_type && new_type->is_a(ID_Window)) { - Fl_Window_Type *new_window = (Fl_Window_Type*)new_type; + Node *new_type = add_new_widget_from_user(prototype, Strategy::AFTER_CURRENT); + if (new_type && new_type->is_a(Type::Window)) { + Window_Node *new_window = (Window_Node*)new_type; Fl_Window *w = (Fl_Window *)new_window->o; w->position(Fl::event_x_root(), Fl::event_y_root()); } } - widget_browser->display(Fl_Type::current); + widget_browser->display(Fluid.proj.tree.current); widget_browser->rebuild(); } return Fl_Button::handle(inEvent); diff --git a/fluid/widgets/Code_Editor.cxx b/fluid/widgets/Code_Editor.cxx index a145b5055..b4379c845 100644 --- a/fluid/widgets/Code_Editor.cxx +++ b/fluid/widgets/Code_Editor.cxx @@ -1,6 +1,5 @@ // // Code editor widget for the Fast Light Tool Kit (FLTK). -// Syntax highlighting rewritten by erco@seriss.com 09/15/20. // // Copyright 1998-2025 by Bill Spitzak and others. // @@ -15,6 +14,8 @@ // https://www.fltk.org/bugs.php // +// Syntax highlighting rewritten by erco@seriss.com 09/15/20. + // // Include necessary headers... // @@ -230,11 +231,11 @@ Code_Editor::Code_Editor(int X, int Y, int W, int H, const char *L) : */ Code_Editor::~Code_Editor() { Fl_Text_Buffer *buf = mStyleBuffer; - mStyleBuffer = 0; + mStyleBuffer = nullptr; delete buf; buf = mBuffer; - buffer(0); + buffer(nullptr); delete buf; } diff --git a/fluid/widgets/Code_Editor.h b/fluid/widgets/Code_Editor.h index ef8df0e0d..49afe7e25 100644 --- a/fluid/widgets/Code_Editor.h +++ b/fluid/widgets/Code_Editor.h @@ -1,6 +1,5 @@ // // Code editor widget for the Fast Light Tool Kit (FLTK). -// Syntax highlighting rewritten by erco@seriss.com 09/15/20. // // Copyright 1998-2025 by Bill Spitzak and others. // @@ -18,6 +17,8 @@ #ifndef FLUID_WIDGETS_CODE_EDITOR_H #define FLUID_WIDGETS_CODE_EDITOR_H +// Syntax highlighting rewritten by erco@seriss.com 09/15/20. + // // Include necessary headers... // @@ -49,7 +50,7 @@ class Code_Editor : public Fl_Text_Editor { static int auto_indent(int, Code_Editor* e); public: - Code_Editor(int X, int Y, int W, int H, const char *L=0); + Code_Editor(int X, int Y, int W, int H, const char *L=nullptr); ~Code_Editor(); void textsize(Fl_Fontsize s); diff --git a/fluid/widgets/Code_Viewer.cxx b/fluid/widgets/Code_Viewer.cxx index 82a71f4f6..fdbfdc626 100644 --- a/fluid/widgets/Code_Viewer.cxx +++ b/fluid/widgets/Code_Viewer.cxx @@ -1,6 +1,5 @@ // -// Code editor widget for the Fast Light Tool Kit (FLTK). -// Syntax highlighting rewritten by erco@seriss.com 09/15/20. +// Code viewer widget for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2025 by Bill Spitzak and others. // @@ -15,6 +14,8 @@ // https://www.fltk.org/bugs.php // +// Syntax highlighting rewritten by erco@seriss.com 09/15/20. + // // Include necessary headers... // diff --git a/fluid/widgets/Code_Viewer.h b/fluid/widgets/Code_Viewer.h index 5bf8b870a..4198fd433 100644 --- a/fluid/widgets/Code_Viewer.h +++ b/fluid/widgets/Code_Viewer.h @@ -1,6 +1,5 @@ // -// Code editor widget for the Fast Light Tool Kit (FLTK). -// Syntax highlighting rewritten by erco@seriss.com 09/15/20. +// Code viewer widget for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2025 by Bill Spitzak and others. // @@ -18,6 +17,8 @@ #ifndef FLUID_WIDGETS_CODE_VIEWER_H #define FLUID_WIDGETS_CODE_VIEWER_H +// Syntax highlighting rewritten by erco@seriss.com 09/15/20. + // // Include necessary headers... // diff --git a/fluid/widgets/Formula_Input.cxx b/fluid/widgets/Formula_Input.cxx index b26bd83e3..dadebe674 100644 --- a/fluid/widgets/Formula_Input.cxx +++ b/fluid/widgets/Formula_Input.cxx @@ -1,5 +1,5 @@ // -// Widget type code for the Fast Light Tool Kit (FLTK). +// Formula Int Input widget code for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2025 by Bill Spitzak and others. // @@ -209,7 +209,7 @@ int Formula_Input::handle(int event) { } /** Set the list of the available variables - \param vars array of variables, last entry `has name_` set to `NULL` + \param vars array of variables, last entry `has name_` set to `nullptr` \param user_data is forwarded to the Variable callback */ void Formula_Input::variables(Formula_Input_Vars *vars, void *user_data) { vars_ = vars; diff --git a/fluid/widgets/Formula_Input.h b/fluid/widgets/Formula_Input.h index 5307cdc88..060c1e157 100644 --- a/fluid/widgets/Formula_Input.h +++ b/fluid/widgets/Formula_Input.h @@ -1,5 +1,5 @@ // -// Formula_Input widget header file for the Fast Light Tool Kit (FLTK). +// Formula Int Input widget header file for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2025 by Bill Spitzak and others. // @@ -29,7 +29,7 @@ typedef int (Fluid_Coord_Callback)(Formula_Input const *, void*); // Entry for a list of variables available to an input field. // Formula_Input::variables() expects an array of -// Formula_Input_Vars with the last entry's name_ set to NULL. +// Formula_Input_Vars with the last entry's name_ set to nullptr. typedef struct Formula_Input_Vars { const char *name_; Fluid_Coord_Callback *callback_; diff --git a/fluid/widgets/Node_Browser.cxx b/fluid/widgets/Node_Browser.cxx index fafe236cf..745282e23 100644 --- a/fluid/widgets/Node_Browser.cxx +++ b/fluid/widgets/Node_Browser.cxx @@ -1,5 +1,5 @@ // -// Widget Browser code for the Fast Light Tool Kit (FLTK). +// Widget Tree Browser code for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2025 by Bill Spitzak and others. // @@ -16,8 +16,8 @@ #include "widgets/Node_Browser.h" -#include "app/fluid.h" -#include "nodes/Fl_Widget_Type.h" +#include "Fluid.h" +#include "nodes/Widget_Node.h" #include "rsrcs/pixmaps.h" #include @@ -41,9 +41,9 @@ using namespace fld::widget; The Widget Browser is derived from the FLTK basic browser, extending tree browsing functionality by using the \c depth component of the double - linked list of \c Fl_Type items. + linked list of \c Node items. - \see Fl_Type + \see Node */ @@ -81,9 +81,9 @@ Fl_Widget *make_widget_browser(int x,int y,int w,int h) { /** Make sure that the caller is visible in the widget browser. \param[in] caller scroll the browser in y so that caller - is visible (may be NULL) + is visible (may be nullptr) */ -void redraw_widget_browser(Fl_Type *caller) +void redraw_widget_browser(Node *caller) { if (caller) widget_browser->display(caller); @@ -95,7 +95,7 @@ void redraw_widget_browser(Fl_Type *caller) \param[in] o (de)select this node \param[in] v the new selection state (1=select, 0=de-select) */ -void select(Fl_Type *o, int v) { +void select(Node *o, int v) { widget_browser->select(o,v,1); } @@ -103,7 +103,7 @@ void select(Fl_Type *o, int v) { Select a single node in the widget browser, deselect all others. \param[in] o select this node */ -void select_only(Fl_Type *o) { +void select_only(Node *o) { widget_browser->select_only(o,1); } @@ -122,8 +122,8 @@ void deselect() { \param[in] t show this item */ -void reveal_in_browser(Fl_Type *t) { - Fl_Type *p = t->parent; +void reveal_in_browser(Node *t) { + Node *p = t->parent; if (p) { for (;;) { if (p->folded_) @@ -165,7 +165,7 @@ static char *copy_trunc(char *p, const char *str, int maxl, int quote, int trunc { int size = 0; // truncated string size in characters int bs; // size of UTF-8 character in bytes - if (!p) return NULL; // bad buffer + if (!p) return nullptr; // bad buffer if (!str) { // no input string if (quote) { *p++='"'; *p++='"'; } *p = 0; @@ -206,7 +206,7 @@ static char *copy_trunc(char *p, const char *str, int maxl, int quote, int trunc Fluid currently generates only one instance of this browser. If we want to use multiple browser at some point, we need to refactor a few global - variables, i.e. Fl_Type::first and Fl_Type::last . + variables, i.e. Fluid.proj.tree.first and Fluid.proj.tree.last . \param[in] X, Y, W, H position and size of widget \param[in] l optional label @@ -226,25 +226,25 @@ Node_Browser::Node_Browser(int X,int Y,int W,int H,const char*l) : \return the first item */ void *Node_Browser::item_first() const { - return Fl_Type::first; + return Fluid.proj.tree.first; } /** Override the method to find the next item in the list of elements. \param l this item - \return the next item, irregardless of tree depth, or NULL at the end + \return the next item, irregardless of tree depth, or nullptr at the end */ void *Node_Browser::item_next(void *l) const { - return ((Fl_Type*)l)->next; + return ((Node*)l)->next; } /** Override the method to find the previous item in the list of elements. \param l this item - \return the previous item, irregardless of tree depth, or NULL at the start + \return the previous item, irregardless of tree depth, or nullptr at the start */ void *Node_Browser::item_prev(void *l) const { - return ((Fl_Type*)l)->prev; + return ((Node*)l)->prev; } /** @@ -254,7 +254,7 @@ void *Node_Browser::item_prev(void *l) const { \todo what is the difference between selected and new_selected, and why do we do this? */ int Node_Browser::item_selected(void *l) const { - return ((Fl_Type*)l)->new_selected; + return ((Node*)l)->new_selected; } /** @@ -263,7 +263,7 @@ int Node_Browser::item_selected(void *l) const { \param[in] v 1 if selecting, 0 if not */ void Node_Browser::item_select(void *l,int v) { - ((Fl_Type*)l)->new_selected = v; + ((Node*)l)->new_selected = v; } /** @@ -272,9 +272,9 @@ void Node_Browser::item_select(void *l,int v) { \return height in FLTK units (used to be pixels before high res screens) */ int Node_Browser::item_height(void *l) const { - Fl_Type *t = (Fl_Type*)l; + Node *t = (Node*)l; if (t->visible) { - if (show_comments && t->comment()) + if (Fluid.show_comments && t->comment()) return textsize()*2+4; else return textsize()+5; @@ -307,13 +307,13 @@ int Node_Browser::incr_height() const { text, possibly abbreviated with an ellipsis. \param v v is a pointer to the actual widget type and can be cast safely - to Fl_Type + to Node \param X,Y these give the position in window coordinates of the top left corner of this line */ void Node_Browser::item_draw(void *v, int X, int Y, int, int) const { // cast to a more general type - Fl_Type *l = (Fl_Type *)v; + Node *l = (Node *)v; char buf[500]; // edit buffer: large enough to hold 80 UTF-8 chars + nul @@ -331,7 +331,7 @@ void Node_Browser::item_draw(void *v, int X, int Y, int, int) const { // items can contain a comment. If they do, the comment gets a second text // line inside this browser line int comment_incr = 0; - if (show_comments && l->comment()) { + if (Fluid.show_comments && l->comment()) { // -- comment copy_trunc(buf, l->comment(), 80, 0, 1); comment_incr = textsize()-1; @@ -370,7 +370,7 @@ void Node_Browser::item_draw(void *v, int X, int Y, int, int) const { } // Width=18: Draw the icon associated with the type. - Fl_Pixmap *pm = pixmap[l->id()]; + Fl_Pixmap *pm = pixmap[(int)l->type()]; if (pm) pm->draw(X-18, Y); // Add tags on top of the icon for locked and protected types. @@ -380,11 +380,11 @@ void Node_Browser::item_draw(void *v, int X, int Y, int, int) const { } if ( l->is_widget() - && !l->is_a(ID_Window) - && ((Fl_Widget_Type*)l)->o - && !((Fl_Widget_Type*)l)->o->visible() - && (!l->parent || ( !l->parent->is_a(ID_Tabs) - && !l->parent->is_a(ID_Wizard) ) ) + && !l->is_a(Type::Window) + && ((Widget_Node*)l)->o + && !((Widget_Node*)l)->o->visible() + && (!l->parent || ( !l->parent->is_a(Type::Tabs) + && !l->parent->is_a(Type::Wizard) ) ) ) { invisible_pixmap->draw(X - 17, Y); @@ -424,7 +424,7 @@ void Node_Browser::item_draw(void *v, int X, int Y, int, int) const { else fl_color(func_color); copy_trunc(buf, l->title(), 55, 0, 0); } else { - if (l->is_a(ID_Comment)) { + if (l->is_a(Type::Comment)) { // -- comment (in main line, not above entry) fl_font(comment_font, textsize()); if (l->new_selected) fl_color(fl_contrast(comment_color, FL_SELECTION_COLOR)); @@ -458,7 +458,7 @@ int Node_Browser::item_width(void *v) const { char buf[500]; // edit buffer: large enough to hold 80 UTF-8 chars + nul - Fl_Type *l = (Fl_Type *)v; + Node *l = (Node *)v; if (!l->visible) return 0; @@ -490,7 +490,7 @@ int Node_Browser::item_width(void *v) const { Callback to tell the Fluid UI when the list of selected items changed. */ void Node_Browser::callback() { - selection_changed((Fl_Type*)selection()); + selection_changed((Node*)selection()); } /** @@ -508,13 +508,13 @@ void Node_Browser::callback() { \return 0 if the event is not supported, and 1 if the event was "used up" */ int Node_Browser::handle(int e) { - static Fl_Type *title; - Fl_Type *l; + static Node *title; + Node *l; int X,Y,W,H; bbox(X,Y,W,H); switch (e) { case FL_PUSH: if (!Fl::event_inside(X,Y,W,H)) break; - l = (Fl_Type*)find_item(Fl::event_y()); + l = (Node*)find_item(Fl::event_y()); if (l) { X += 3 + 12*l->level - hposition(); if (l->can_have_children() && Fl::event_x()>X && Fl::event_x()level - hposition(); if (l->can_have_children() && Fl::event_x()>X && Fl::event_x()new_selected && (Fl::event_clicks() || Fl::event_state(FL_CTRL))) l->open(); break; @@ -550,14 +550,14 @@ int Node_Browser::handle(int e) { if (l) { if (!l->folded_) { l->folded_ = 1; - for (Fl_Type*k = l->next; k&&k->level>l->level; k = k->next) + for (Node*k = l->next; k&&k->level>l->level; k = k->next) k->visible = 0; } else { l->folded_ = 0; - for (Fl_Type*k=l->next; k&&k->level>l->level;) { + for (Node*k=l->next; k&&k->level>l->level;) { k->visible = 1; if (k->can_have_children() && k->folded_) { - Fl_Type *j; + Node *j; for (j = k->next; j && j->level>k->level; j = j->next) {/*empty*/} k = j; } else @@ -602,9 +602,9 @@ void Node_Browser::rebuild() { /** Rebuild the browser layout and make sure that the given item is visible. - \param[in] inNode pointer to a widget node derived from Fl_Type. + \param[in] inNode pointer to a widget node derived from Node. */ -void Node_Browser::display(Fl_Type *inNode) { +void Node_Browser::display(Node *inNode) { if (!inNode) { // Alternative: find the first (last?) visible selected item. return; @@ -613,7 +613,7 @@ void Node_Browser::display(Fl_Type *inNode) { int currentV = vposition(), newV = currentV; int nodeV = 0; // find the inNode in the tree and check, if it is already visible - Fl_Type *p=Fl_Type::first; + Node *p=Fluid.proj.tree.first; for ( ; p && p!=inNode; p=p->next) { if (p->visible) nodeV += item_height(p) + linespacing(); @@ -640,7 +640,7 @@ void Node_Browser::display(Fl_Type *inNode) { void Node_Browser::load_prefs() { int c; - Fl_Preferences p(fluid_prefs, "widget_browser"); + Fl_Preferences p(Fluid.preferences, "widget_browser"); p.get("label_color", c, 72); label_color = c; p.get("label_font", c, FL_HELVETICA); label_font = c; p.get("class_color", c, FL_FOREGROUND_COLOR); class_color = c; @@ -656,7 +656,7 @@ void Node_Browser::load_prefs() { } void Node_Browser::save_prefs() { - Fl_Preferences p(fluid_prefs, "widget_browser"); + Fl_Preferences p(Fluid.preferences, "widget_browser"); p.set("label_color", (int)label_color); p.set("label_font", (int)label_font); p.set("class_color", (int)class_color); diff --git a/fluid/widgets/Node_Browser.h b/fluid/widgets/Node_Browser.h index 5ac74b50a..eec1e7960 100644 --- a/fluid/widgets/Node_Browser.h +++ b/fluid/widgets/Node_Browser.h @@ -1,5 +1,5 @@ // -// Widget Browser code for the Fast Light Tool Kit (FLTK). +// Widget Tree Browser code for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2025 by Bill Spitzak and others. // @@ -19,20 +19,20 @@ #include -class Fl_Type; +class Node; namespace fld { namespace widget { class Node_Browser : public Fl_Browser_ { - friend class Fl_Type; + friend class Node; static void callback_stub(Fl_Widget *o, void *) { ((Node_Browser *)o)->callback(); } - Fl_Type* pushedtitle { nullptr }; + Node* pushedtitle { nullptr }; int saved_h_scroll_ { 0 }; int saved_v_scroll_ { 0 }; @@ -55,7 +55,7 @@ public: void restore_scroll_position(); void rebuild(); void new_list() { Fl_Browser_::new_list(); } - void display(Fl_Type *); + void display(Node *); void load_prefs(); void save_prefs(); @@ -78,11 +78,11 @@ public: extern void redraw_browser(); extern Fl_Widget *make_widget_browser(int x,int y,int w,int h); -extern void redraw_widget_browser(Fl_Type *caller); -extern void select(Fl_Type *o, int v); -extern void select_only(Fl_Type *o); +extern void redraw_widget_browser(Node *caller); +extern void select(Node *o, int v); +extern void select_only(Node *o); extern void deselect(); -extern void reveal_in_browser(Fl_Type *t); +extern void reveal_in_browser(Node *t); extern fld::widget::Node_Browser *widget_browser; diff --git a/fluid/widgets/Text_Viewer.cxx b/fluid/widgets/Text_Viewer.cxx index ba2d145fe..5ae821d6b 100644 --- a/fluid/widgets/Text_Viewer.cxx +++ b/fluid/widgets/Text_Viewer.cxx @@ -1,6 +1,5 @@ // -// Code editor widget for the Fast Light Tool Kit (FLTK). -// Syntax highlighting rewritten by erco@seriss.com 09/15/20. +// Text Viewer widget for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2025 by Bill Spitzak and others. // @@ -40,7 +39,7 @@ Text_Viewer::Text_Viewer(int X, int Y, int W, int H, const char *L) */ Text_Viewer::~Text_Viewer() { Fl_Text_Buffer *buf = mBuffer; - buffer(0); + buffer(nullptr); delete buf; } diff --git a/fluid/widgets/Text_Viewer.h b/fluid/widgets/Text_Viewer.h index 1e5810d50..98fa9200f 100644 --- a/fluid/widgets/Text_Viewer.h +++ b/fluid/widgets/Text_Viewer.h @@ -1,6 +1,5 @@ // -// Code editor widget for the Fast Light Tool Kit (FLTK). -// Syntax highlighting rewritten by erco@seriss.com 09/15/20. +// Text Viewer widget for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2025 by Bill Spitzak and others. // -- cgit v1.2.3