// // 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/App_Menu_Bar.h" #include "widgets/Node_Browser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../src/flstring.h" #ifdef __APPLE__ #include #endif #ifndef _WIN32 #include #endif Application Fluid; /** Timer to watch for external editor modifications. */ 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) { int modified = 0; for (Node *p: Fluid.proj.tree.all_nodes()) { if (p->is_a(FLD_NODE_TYPE_Code)) { Code_Node *code = static_cast(p); if (code->handle_editor_changes()) { modified++; } if (code->is_editing()) { code->reap_editor(); } } } if (modified) Fluid.proj.set_modflag(1); } if (ExternalCodeEditor::editors_open()) { Fl::repeat_timeout(2.0, external_editor_timer); } } /** Create the Fluid application. */ Application::Application() : current_project_(new Project()), tmpdir_create_called(0), preferences(Fl_Preferences::USER_L, "fltk.org", "fluid"), layout_list(0), batch_mode(0), show_guides(1), show_restricted(1), show_ghosted_outline(1), show_comments(1), use_external_editor(0), debug_external_editor(0), main_window(0), main_menubar(0), save_item(0), history_item(0), widgetbin_item(0), codeview_item(0), overlay_item(0), overlay_button(0), guides_item(0), restricted_item(0), pasteoffset(0), ipasteoffset(0), help_dialog(0), proj(*current_project_) { launch_path_[0] = '\0'; tmpdir_path_[0] = '\0'; external_editor_command[0] = '\0'; } /** Destructor. */ Application::~Application() { delete current_project_; } /** Start Fluid. */ int Application::run(int argc, char **argv) { setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, "C"); // store the current path at launch fl_getcwd(launch_path_, FL_PATH_MAX); fld_end_with_slash(launch_path_, FL_PATH_MAX, launch_path_); 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 = 0; if (args.autodoc_path[0] == '\0') c = argv[i]; fl_register_images(); make_main_window(); if (c) { if (batch_mode) { proj.set_filename(c); } else { char abs_path[FL_PATH_MAX]; fl_filename_absolute(abs_path, FL_PATH_MAX, c); proj.set_filename(abs_path); } } if (!batch_mode) { #ifdef __APPLE__ fl_open_callback(apple_open_cb); #endif 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, FLD_WINWIDTH, FLD_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(); } layout_list->read(preferences, FLD_TOOL_STORE_USER); main_window->show(argc, argv); toggle_widget_bin(); toggle_codeview_cb(0, 0); if (!c && openlast_button->value() && history.abspath[0][0] && args.autodoc_path[0] == '\0') { open_project_file(history.abspath[0]); } } proj.undo.suspend(); if (c && !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(); if (batch_mode) { if (args.code_filename[0]) { proj.code_file_set = 1; proj.set_code_file_name(args.code_filename); } if (args.header_filename[0]) { proj.header_file_set = 1; proj.set_header_file_name(args.header_filename); } } if (args.update_file) { write_file(proj, c, 0); if (!args.compile_file) exit(0); } if (args.compile_file) { if (args.compile_strings) proj.write_strings(); write_code_files(0); exit(0); } if (batch_mode) exit(0); proj.set_modflag(0); proj.undo.clear(); ExternalCodeEditor::set_update_timer_callback(external_editor_timer); #ifndef NDEBUG if (args.autodoc_path[0]) { run_autodoc(args.autodoc_path); proj.set_modflag(0, 0); quit(); return 0; } #endif start_auto_mergeback(); Fl::run(); proj.undo.clear(); return 0; } /** Exit Fluid. */ void Application::quit() { if (shell_command_running()) { int choice = fl_choice("Previous shell command still running!", "Cancel", "Exit", 0); if (choice == 0) return; } flush_text_widgets(); if (confirm_project_clear() == 0) return; 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 = 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(preferences, FLD_TOOL_STORE_USER); layout_list->write(preferences, FLD_TOOL_STORE_USER); proj.undo.clear(); proj.reset(); ExternalCodeEditor::tmpdir_clear(); delete_tmpdir(); exit(0); } /** Return the working directory path at application launch. */ const char *Application::launch_path() const { return launch_path_; } /** Generate a path to a directory for temporary data storage. */ void Application::create_tmpdir() { if (tmpdir_create_called) return; tmpdir_create_called = 1; char buf[128]; char path[FL_PATH_MAX]; #ifdef _WIN32 fl_snprintf(buf, sizeof(buf) - 1, "fluid-%ld/", (long)GetCurrentProcessId()); 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; } fld_end_with_slash(path, FL_PATH_MAX, tempdir); strlcat(path, buf, FL_PATH_MAX); fl_make_path(path); if (fl_access(path, 6) == 0) strlcpy(tmpdir_path_, path, FL_PATH_MAX); #else fl_snprintf(buf, sizeof(buf) - 1, "fluid-%d/", getpid()); const char *path_temp = fl_getenv("TMPDIR"); if (path_temp && *path_temp) { fld_end_with_slash(path, FL_PATH_MAX, path_temp); strlcat(path, buf, FL_PATH_MAX); fl_make_path(path); if (fl_access(path, 6) == 0) strlcpy(tmpdir_path_, path, FL_PATH_MAX); } if (tmpdir_path_[0] == '\0') { snprintf(path, FL_PATH_MAX, "/tmp/%s", buf); fl_make_path(path); if (fl_access(path, 6) == 0) strlcpy(tmpdir_path_, path, FL_PATH_MAX); } #endif if (tmpdir_path_[0] == '\0') { char pbuf[FL_PATH_MAX + 1]; preferences.get_userdata_path(pbuf, FL_PATH_MAX); fld_end_with_slash(path, FL_PATH_MAX, pbuf); strlcat(path, buf, FL_PATH_MAX); fl_make_path(path); if (fl_access(path, 6) == 0) strlcpy(tmpdir_path_, path, FL_PATH_MAX); } if (tmpdir_path_[0] == '\0') { 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. */ void Application::delete_tmpdir() { if (!tmpdir_create_called) return; if (tmpdir_path_[0] == '\0') return; struct dirent **de; int n_de = fl_filename_list(tmpdir_path_, &de); if (n_de >= 0) { int i; for (i = 0; i < n_de; i++) { char filepath[FL_PATH_MAX]; snprintf(filepath, FL_PATH_MAX, "%s%s", tmpdir_path_, de[i]->d_name); fl_unlink(filepath); } fl_filename_free_list(&de, n_de); } if (fl_rmdir(tmpdir_path_) < 0) { if (batch_mode) { fprintf(stderr, "WARNING: Can't delete tmpdir '%s': %s", tmpdir_path_, strerror(errno)); } else { fl_alert("WARNING: Can't delete tmpdir '%s': %s", tmpdir_path_, strerror(errno)); } } } /** Return the path to a temporary directory for this instance of Fluid. */ const char *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. */ 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. */ int Application::new_project(int user_must_confirm) { if ((user_must_confirm) && (confirm_project_clear() == 0)) return 0; proj.reset(); proj.clear_filename(); proj.set_modflag(0, 0); widget_browser->rebuild(); proj.update_settings_dialog(); return 1; } /** Open a file chooser and load an existing project file. */ int Application::open_project_file(const char *filename_arg) { if (confirm_project_clear() == 0) return 0; char new_filename[FL_PATH_MAX]; new_filename[0] = '\0'; if (!filename_arg || !*filename_arg) { open_project_filechooser(new_filename, FL_PATH_MAX, "Open Project File"); if (new_filename[0] == '\0') { return 0; } } else { strlcpy(new_filename, filename_arg, FL_PATH_MAX); } new_project(0); int success = merge_project_file(new_filename); if (success) mergeback_on_load(); return success; } /** Load a project from the given file name and path. */ int Application::merge_project_file(const char *filename_arg) { int is_a_merge = (!proj.tree.empty()); const char *title = is_a_merge ? "Merge Project File" : "Open Project File"; char new_filename[FL_PATH_MAX]; new_filename[0] = '\0'; if (!filename_arg || !*filename_arg) { open_project_filechooser(new_filename, FL_PATH_MAX, title); if (new_filename[0] == '\0') { return 0; } } else { strlcpy(new_filename, filename_arg, FL_PATH_MAX); } const char *c = new_filename; const char *oldfilename = proj.proj_filename; proj.proj_filename = 0; proj.set_filename(c); if (is_a_merge) proj.undo.checkpoint(); proj.undo.suspend(); if (!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 0; } proj.undo.resume(); widget_browser->rebuild(); if (is_a_merge) { proj.set_filename(oldfilename); proj.set_modflag(1); } else { proj.set_modflag(0, 0); proj.undo.clear(); } if (oldfilename) free((void *)oldfilename); return 1; } /** Save the current design to the file given by filename. */ 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 Project File As:"); fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); #ifndef __APPLE__ fnfc.options(Fl_Native_File_Chooser::NEW_FOLDER); #else fnfc.options(Fl_Native_File_Chooser::NEW_FOLDER | Fl_Native_File_Chooser::SAVEAS_CONFIRM); #endif fnfc.filter("FLUID Files\t*.f[ld]"); if (proj.proj_filename) { char proj_path[FL_PATH_MAX]; proj.projectfile_path(proj_path, FL_PATH_MAX); if (proj_path[0]) fnfc.directory(proj_path); const char *proj_name = proj.projectfile_name(); if (proj_name) fnfc.preset_file(proj_name); } fnfc.filter("Fluid Project\t*.fl\nAny\t*"); if (fnfc.show() != 0) return; c = fnfc.filename(); #ifndef __APPLE__ if (!fl_access(c, 0)) { const char *basename = fl_filename_name(c); if (fl_choice("The file \"%s\" already exists.\n" "Do you want to replace it?", "Cancel", "Replace", 0, basename) == 0) return; } #endif if (v != (void *)2) proj.set_filename(c); } if (!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 filename, replacing the current design. */ void Application::revert_project() { if (proj.modflag) { if (!fl_choice("This user interface has been changed. Really revert?", "Cancel", "Revert", 0)) return; } proj.undo.suspend(); if (!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. */ int Application::new_project_from_template() { if (new_project(1) == 0) return 0; 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"); template_browser->value(1); template_browser->do_callback(); template_panel->show(); while (template_panel->shown()) Fl::wait(); int item = template_browser->value(); if (item < 1) return 0; const char *tname = (const char *)template_browser->data(item); if (tname) { const char *iname = template_instance->value(); if (iname && *iname) { char line[1024], *ptr, *next; FILE *infile, *outfile; if ((infile = fl_fopen(tname, "rb")) == 0) { fl_alert("Error reading template file \"%s\":\n%s", tname, strerror(errno)); proj.set_modflag(0); proj.undo.clear(); return 0; } if ((outfile = fl_fopen(cutfname(1), "wb")) == 0) { fl_alert("Error writing buffer file \"%s\":\n%s", cutfname(1), strerror(errno)); fclose(infile); proj.set_modflag(0); proj.undo.clear(); return 0; } while (fgets(line, sizeof(line), infile)) { for (ptr = line; (next = strstr(ptr, "@INSTANCE@")) != 0; ptr = next + 10) { fwrite(ptr, next - ptr, 1, outfile); fputs(iname, outfile); } fputs(ptr, outfile); } fclose(infile); fclose(outfile); proj.undo.suspend(); read_file(proj, cutfname(1), 0); fl_unlink(cutfname(1)); proj.undo.resume(); } else { proj.undo.suspend(); read_file(proj, tname, 0); proj.undo.resume(); } } widget_browser->rebuild(); proj.update_settings_dialog(); proj.set_modflag(0); proj.undo.clear(); return 1; } /** 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; Window_Node *windows[1000]; int winpage; Fl_Window *win; for (Node *wn: proj.tree.all_nodes()) { if (wn->is_a(FLD_NODE_TYPE_Window)) { Window_Node *win_t = static_cast(wn); Fl_Window *win_w = static_cast(win_t->o); if (!win_w->shown()) continue; windows[num_windows] = win_t; 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); time_t curtime = time(0); 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()); const char *basename = fl_filename_name(proj.proj_filename); if (basename) fl_draw(basename, 0, fl_height()); 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. */ int Application::write_code_files(int dont_show_completion_dialog) { flush_text_widgets(); if (!proj.proj_filename) { save_project_file(0); if (!proj.proj_filename) return 1; } Code_Writer f(proj); char code_path[FL_PATH_MAX], code_name[FL_PATH_MAX]; char header_path[FL_PATH_MAX], header_name[FL_PATH_MAX]; proj.codefile_path(code_path, FL_PATH_MAX); proj.codefile_name(code_name, FL_PATH_MAX); proj.headerfile_path(header_path, FL_PATH_MAX); proj.headerfile_name(header_name, FL_PATH_MAX); char code_filename[FL_PATH_MAX], header_filename[FL_PATH_MAX]; snprintf(code_filename, FL_PATH_MAX, "%s%s", code_path, code_name); snprintf(header_filename, FL_PATH_MAX, "%s%s", header_path, header_name); if (!batch_mode) proj.enter_project_dir(); int x = f.write_code(code_filename, header_filename); char code_filename_rel[FL_PATH_MAX], header_filename_rel[FL_PATH_MAX]; fl_filename_relative(code_filename_rel, FL_PATH_MAX, code_filename); fl_filename_relative(header_filename_rel, FL_PATH_MAX, header_filename); if (!batch_mode) proj.leave_project_dir(); if (batch_mode) { if (!x) { fprintf(stderr, "%s and %s: %s\n", code_filename_rel, header_filename_rel, strerror(errno)); exit(1); } } else { if (!x) { fl_message("Can't write %s or %s: %s", code_filename_rel, header_filename_rel, strerror(errno)); } else { proj.set_modflag(-1, 0); if (dont_show_completion_dialog == 0 && completion_button->value()) { fl_message("Wrote %s and %s", code_filename_rel, header_filename_rel); } } } return 0; } void Application::cut_selected() { if (!proj.tree.current) { fl_beep(); return; } flush_text_widgets(); if (!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(); } void Application::copy_selected() { flush_text_widgets(); if (!proj.tree.current) { fl_beep(); return; } flush_text_widgets(); ipasteoffset = 10; if (!write_file(proj, cutfname(), 1)) { fl_message("Can't write %s: %s", cutfname(), strerror(errno)); return; } } 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) { strategy = Strategy::FROM_FILE_AS_LAST_CHILD; } } if (!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; } void Application::duplicate_selected() { if (!proj.tree.current) { fl_beep(); return; } flush_text_widgets(); int lowest_level = 9999; Node *new_insert = 0; if (proj.tree.current->selected) { for (Node *t: proj.tree.all_nodes()) { if (t->selected && t->level <= lowest_level) { lowest_level = t->level; new_insert = t; } } } if (new_insert) proj.tree.current = new_insert; if (!write_file(proj, cutfname(1), 1)) { fl_message("Can't write %s: %s", cutfname(1), strerror(errno)); return; } pasteoffset = 0; proj.undo.checkpoint(); proj.undo.suspend(); if (!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(); } 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(); } void Application::edit_selected() { if (!proj.tree.current) { fl_message("Please select a widget"); return; } proj.tree.current->open(); } void Application::sort_selected() { proj.undo.checkpoint(); sort((Node*)0); widget_browser->rebuild(); proj.set_modflag(1); } 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"); } } 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")) == 0) { docdir = FLTK_DOCDIR; } snprintf(helpname, sizeof(helpname), "%s/%s", docdir, name); FILE *f = fopen(helpname, "rb"); if (f) { fclose(f); help_dialog->load(helpname); } else { 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(); } void Application::about() { if (!about_panel) make_about_panel(); about_panel->show(); } 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(FLD_WINWIDTH, FLD_WINHEIGHT, "fluid"); main_window->box(FL_NO_BOX); o = make_widget_browser(0, FLD_MENUHEIGHT, FLD_BROWSERWIDTH, FLD_BROWSERHEIGHT); o->box(FL_FLAT_BOX); o->tooltip("Double-click to view or change an item."); main_window->resizable(o); main_menubar = new App_Menu_Bar(0, 0, FLD_BROWSERWIDTH, FLD_MENUHEIGHT); main_menubar->menu(main_menu); 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(); } } void Application::open_project_filechooser(char *result, int result_size, const char *title) { result[0] = '\0'; Fl_Native_File_Chooser dialog; dialog.title(title); dialog.type(Fl_Native_File_Chooser::BROWSE_FILE); dialog.filter("FLUID Files\t*.f[ld]\n"); if (proj.proj_filename) { char path[FL_PATH_MAX]; fl_filename_path(path, FL_PATH_MAX, proj.proj_filename); dialog.directory(path); dialog.preset_file(fl_filename_name(proj.proj_filename)); } if (dialog.show() != 0) return; strlcpy(result, dialog.filename(), result_size); } int Application::confirm_project_clear() { if (proj.modflag == 0) return 1; 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: return 0; case 1: save_project_file(0); if (proj.modflag) return 0; } return 1; } void Application::flush_text_widgets() { if (Fl::focus() && (Fl::focus()->top_window() == the_panel)) { Fl_Widget *old_focus = Fl::focus(); Fl::focus(0); Fl::focus(old_focus); } } 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; } 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())); } void Application::set_scheme(const char *new_scheme) { if (batch_mode) return; if (Fl::is_scheme(new_scheme)) return; Fl::scheme(new_scheme); preferences.set("scheme_name", new_scheme); int scheme_index = scheme_choice->value(); if (scheme_index <= 3) preferences.set("scheme", scheme_index + 1); } void Application::init_scheme() { int scheme_index = 0; char *scheme_name = 0; preferences.get("scheme_name", scheme_name, "XXX"); if (!strcmp(scheme_name, "XXX")) { preferences.get("scheme", scheme_index, 0); if (scheme_index > 0) { scheme_index--; scheme_choice->value(scheme_index); } 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); } if (Fl::scheme() == 0) { Fl::scheme(scheme_name); } free(scheme_name); } #ifdef __APPLE__ void Application::apple_open_cb(const char *c) { Fluid.open_project_file(c); } #endif