summaryrefslogtreecommitdiff
path: root/fluid/app
diff options
context:
space:
mode:
authorMatthias Melcher <github@matthiasm.com>2025-03-16 17:16:12 -0400
committerGitHub <noreply@github.com>2025-03-16 17:16:12 -0400
commit51a55bc73660f64e8f4b32b8b4d3858f2a786f7b (patch)
tree122ad9f838fcf8f61ed7cf5fa031e8ed69817e10 /fluid/app
parent13a7073a1e007ce5b71ef70bced1a9b15158820d (diff)
Fluid: restructuring and rejuvenation of the source code.
* Add classes for application and project * Removed all globals from Fluid.h * Extracting args and project history into their own classes * Moving globals into Application class * Initialize values inside headers for some classes. * Undo functionality wrapped in a class inside Project. * File reader and writer are now linked to a project. * Avoid global project access * Nodes (former Types) will be managed by a new Tree class. * Removed static members (hidden globals) form Node/Fl_Type. * Adding Tree iterator. * Use nullptr instead of 0, NULL, or 0L * Renamed Fl_..._Type to ..._Node, FL_OVERRIDE -> override * Renaming ..._type to ...::prototype * Splitting Widget Panel into multiple files. * Moved callback code into widget panel file. * Cleaning up Fluid_Image -> Image_asset * Moving Fd_Snap_Action into new namespace fld::app::Snap_Action etc. * Moved mergeback into proj folder. * `enum ID` is now `enum class Type`.
Diffstat (limited to 'fluid/app')
-rw-r--r--fluid/app/Fluid_Image.cxx413
-rw-r--r--fluid/app/Image_Asset.cxx532
-rw-r--r--fluid/app/Image_Asset.h (renamed from fluid/app/Fluid_Image.h)57
-rw-r--r--fluid/app/Menu.cxx190
-rw-r--r--fluid/app/Menu.h29
-rw-r--r--fluid/app/Snap_Action.cxx (renamed from fluid/app/Fd_Snap_Action.cxx)458
-rw-r--r--fluid/app/Snap_Action.h (renamed from fluid/app/Fd_Snap_Action.h)92
-rw-r--r--fluid/app/align_widget.cxx414
-rw-r--r--fluid/app/args.cxx136
-rw-r--r--fluid/app/args.h55
-rw-r--r--fluid/app/fluid.cxx2169
-rw-r--r--fluid/app/fluid.h134
-rw-r--r--fluid/app/history.cxx115
-rw-r--r--fluid/app/history.h45
-rw-r--r--fluid/app/mergeback.cxx493
-rw-r--r--fluid/app/mergeback.h81
-rw-r--r--fluid/app/project.cxx192
-rw-r--r--fluid/app/project.h103
-rw-r--r--fluid/app/shell_command.cxx197
-rw-r--r--fluid/app/shell_command.h46
-rw-r--r--fluid/app/templates.cxx143
-rw-r--r--fluid/app/templates.h (renamed from fluid/app/align_widget.h)19
-rw-r--r--fluid/app/undo.cxx265
-rw-r--r--fluid/app/undo.h37
24 files changed, 1681 insertions, 4734 deletions
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 <FL/Fl.H>
-#include <FL/Fl_Widget.H>
-#include <FL/Fl_Window.H>
-#include <FL/fl_string_functions.h>
-#include <FL/fl_utf8.h> // fl_fopen()
-#include <FL/Fl_File_Chooser.H>
-#include <FL/Fl_SVG_Image.H>
-#include <FL/Fl_Anim_GIF_Image.H>
-#include "../src/flstring.h"
-
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-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 <FL/Fl_Image.H>\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 <FL/Fl_Anim_GIF_Image.H>\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 <FL/Fl_GIF_Image.H>\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 <FL/Fl_BMP_Image.H>\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 <FL/Fl_Pixmap.H>\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 <FL/Fl_Bitmap.H>\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 <FL/Fl_JPEG_Image.H>\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 <FL/Fl_PNG_Image.H>\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 <FL/Fl_SVG_Image.H>\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; a<numimages; a++) {
- if (images[a] == this) {
- numimages--;
- for (; a < numimages; a++) {
- images[a] = images[a+1];
- }
- break;
- }
- }
- }
- if (img) img->release();
- 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/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 <FL/Fl.H>
+#include <FL/Fl_Widget.H>
+#include <FL/Fl_Window.H>
+#include <FL/fl_string_functions.h>
+#include <FL/fl_utf8.h> // fl_fopen()
+#include <FL/Fl_File_Chooser.H>
+#include <FL/Fl_SVG_Image.H>
+#include <FL/Fl_Anim_GIF_Image.H>
+#include "../src/flstring.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <map>
+#include <string>
+
+
+/**
+ \brief A map of all image assets.
+ \todo This is a global variable, but should be associated
+ with a project instead.
+ */
+static std::map<std::string, Image_Asset*> 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 <FL/Fl_Image.H>\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 <FL/Fl_Anim_GIF_Image.H>\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 <FL/Fl_GIF_Image.H>\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 <FL/Fl_BMP_Image.H>\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 <FL/Fl_Pixmap.H>\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 <FL/Fl_Bitmap.H>\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 <FL/Fl_JPEG_Image.H>\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 <FL/Fl_PNG_Image.H>\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 <FL/Fl_SVG_Image.H>\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/Fluid_Image.h b/fluid/app/Image_Asset.h
index f095e83ce..f5376beaa 100644
--- a/fluid/app/Fluid_Image.h
+++ b/fluid/app/Image_Asset.h
@@ -1,11 +1,7 @@
//
-// Image header file for the Fast Light Tool Kit (FLTK).
+// Image Helper 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.
+// 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
@@ -18,43 +14,50 @@
// https://www.fltk.org/bugs.php
//
-#ifndef FLUID_IMAGE_H
-#define FLUID_IMAGE_H
+// 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 <FL/Fl_Shared_Image.H>
-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
+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:
- 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
+
+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 *name() const {return name_;}
+ 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:
-Fluid_Image *ui_find_image(const char *);
+Image_Asset *ui_find_image(const char *);
extern const char *ui_find_image_name;
-#endif
+#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 <FL/Fl_Menu_Bar.H>
+
+// 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 <FL/Fl_Menu_Item.H>
+
+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/Fd_Snap_Action.cxx b/fluid/app/Snap_Action.cxx
index 0a407ac8d..f33054eca 100644
--- a/fluid/app/Fd_Snap_Action.cxx
+++ b/fluid/app/Snap_Action.cxx
@@ -1,7 +1,7 @@
//
// Snap action code file for the Fast Light Tool Kit (FLTK).
//
-// Copyright 2023 by Bill Spitzak and others.
+// 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
@@ -14,19 +14,28 @@
// https://www.fltk.org/bugs.php
//
-#include "app/Fd_Snap_Action.h"
+#include "app/Snap_Action.h"
+#include "Fluid.h"
#include "io/Project_Reader.h"
#include "io/Project_Writer.h"
-#include "nodes/Fl_Group_Type.h"
+#include "nodes/Group_Node.h"
+#include "nodes/Window_Node.h"
#include "panels/settings_panel.h"
#include <FL/fl_draw.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/fl_string_functions.h>
+
#include <math.h>
#include <string.h>
#include <assert.h>
+#undef min
+#undef max
+#include <algorithm>
+
+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?)
@@ -34,10 +43,10 @@
void select_layout_suite_cb(Fl_Widget *, void *user_data);
-int Fd_Snap_Action::eex = 0;
-int Fd_Snap_Action::eey = 0;
+int Snap_Action::eex = 0;
+int Snap_Action::eey = 0;
-static Fd_Layout_Preset fltk_app = {
+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
@@ -45,7 +54,7 @@ static Fd_Layout_Preset fltk_app = {
20, 4, 8, // widget_y: min, inc, gap
0, 14, -1, 14 // labelfont/size, textfont/size
};
-static Fd_Layout_Preset fltk_dlg = {
+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
@@ -53,7 +62,7 @@ static Fd_Layout_Preset fltk_dlg = {
20, 5, 5, // widget_y: min, inc, gap
0, 11, -1, 11 // labelfont/size, textfont/size
};
-static Fd_Layout_Preset fltk_tool = {
+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
@@ -62,7 +71,7 @@ static Fd_Layout_Preset fltk_tool = {
0, 10, -1, 10 // labelfont/size, textfont/size
};
-static Fd_Layout_Preset grid_app = {
+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
@@ -71,7 +80,7 @@ static Fd_Layout_Preset grid_app = {
0, 14, -1, 14 // labelfont/size, textfont/size
};
-static Fd_Layout_Preset grid_dlg = {
+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
@@ -80,7 +89,7 @@ static Fd_Layout_Preset grid_dlg = {
0, 12, -1, 12 // labelfont/size, textfont/size
};
-static Fd_Layout_Preset grid_tool = {
+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
@@ -89,25 +98,25 @@ static Fd_Layout_Preset grid_tool = {
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 }
+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 },
- { NULL }
+ { nullptr }
};
static Fl_Menu_Item static_choice_menu[] = {
{ static_suite_list[0].menu_label },
{ static_suite_list[1].menu_label },
- { NULL }
+ { nullptr }
};
-Fd_Layout_Preset *layout = &fltk_app;
-Fd_Layout_List g_layout_list;
// ---- Callbacks ------------------------------------------------------ MARK: -
@@ -118,17 +127,17 @@ void layout_suite_marker(Fl_Widget *, void *) {
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();
+ 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);
- g_layout_list.current_preset(index);
- g_layout_list.update_dialogs();
+ Fluid.layout_list.current_preset(index);
+ Fluid.layout_list.update_dialogs();
}
void edit_layout_preset_cb(Fl_Button *w, long user_data) {
@@ -136,19 +145,19 @@ void edit_layout_preset_cb(Fl_Button *w, long user_data) {
assert(index >= 0);
assert(index < 3);
if (user_data == (long)(fl_intptr_t)LOAD) {
- w->value(g_layout_list.current_preset() == index);
+ w->value(Fluid.layout_list.current_preset() == index);
} else {
- g_layout_list.current_preset(index);
- g_layout_list.update_dialogs();
+ Fluid.layout_list.current_preset(index);
+ Fluid.layout_list.update_dialogs();
}
}
-// ---- Fd_Layout_Suite ------------------------------------------------ MARK: -
+// ---- Layout_Suite ------------------------------------------------ MARK: -
/**
Write presets to a Preferences database.
*/
-void Fd_Layout_Preset::write(Fl_Preferences &prefs) {
+void Layout_Preset::write(Fl_Preferences &prefs) {
assert(this);
Fl_Preferences p_win(prefs, "Window");
p_win.set("left_margin", left_window_margin);
@@ -188,7 +197,7 @@ void Fd_Layout_Preset::write(Fl_Preferences &prefs) {
/**
Read presets from a Preferences database.
*/
-void Fd_Layout_Preset::read(Fl_Preferences &prefs) {
+void Layout_Preset::read(Fl_Preferences &prefs) {
assert(this);
Fl_Preferences p_win(prefs, "Window");
p_win.get("left_margin", left_window_margin, 15);
@@ -228,7 +237,7 @@ void Fd_Layout_Preset::read(Fl_Preferences &prefs) {
/**
Write presets to an .fl project file.
*/
-void Fd_Layout_Preset::write(fld::io::Project_Writer *out) {
+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,
@@ -250,7 +259,7 @@ void Fd_Layout_Preset::write(fld::io::Project_Writer *out) {
/**
Read presets from an .fl project file.
*/
-void Fd_Layout_Preset::read(fld::io::Project_Reader *in) {
+void Layout_Preset::read(fld::io::Project_Reader *in) {
const char *key;
key = in->read_word(1);
if (key && !strcmp(key, "{")) {
@@ -306,7 +315,7 @@ void Fd_Layout_Preset::read(fld::io::Project_Reader *in) {
/**
Return the preferred text size, but make sure it's not 0.
*/
-int Fd_Layout_Preset::textsize_not_null() {
+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
@@ -316,12 +325,12 @@ int Fd_Layout_Preset::textsize_not_null() {
}
-// ---- Fd_Layout_Suite ------------------------------------------------ MARK: -
+// ---- Layout_Suite ------------------------------------------------ MARK: -
/**
Write a presets suite to a Preferences database.
*/
-void Fd_Layout_Suite::write(Fl_Preferences &prefs) {
+void Layout_Suite::write(Fl_Preferences &prefs) {
assert(this);
assert(name_);
prefs.set("name", name_);
@@ -335,7 +344,7 @@ void Fd_Layout_Suite::write(Fl_Preferences &prefs) {
/**
Read a presets suite from a Preferences database.
*/
-void Fd_Layout_Suite::read(Fl_Preferences &prefs) {
+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));
@@ -347,7 +356,7 @@ void Fd_Layout_Suite::read(Fl_Preferences &prefs) {
/**
Write a presets suite to an .fl project file.
*/
-void Fd_Layout_Suite::write(fld::io::Project_Writer *out) {
+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) {
@@ -359,7 +368,7 @@ void Fd_Layout_Suite::write(fld::io::Project_Writer *out) {
/**
Read a presets suite from an .fl project file.
*/
-void Fd_Layout_Suite::read(fld::io::Project_Reader *in) {
+void Layout_Suite::read(fld::io::Project_Reader *in) {
const char *key;
key = in->read_word(1);
if (key && !strcmp(key, "{")) {
@@ -387,57 +396,57 @@ void Fd_Layout_Suite::read(fld::io::Project_Reader *in) {
\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() {
+void 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;
+ 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());
- g_layout_list.update_menu_labels();
+ Fluid.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) {
+void Layout_Suite::name(const char *n) {
if (name_)
::free(name_);
if (n)
name_ = fl_strdup(n);
else
- name_ = NULL;
+ name_ = nullptr;
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;
+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.
*/
-Fd_Layout_Suite::~Fd_Layout_Suite() {
- if (storage_ == FD_STORE_INTERNAL) return;
+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];
}
}
-// ---- Fd_Layout_List ------------------------------------------------- MARK: -
+// ---- Layout_List ------------------------------------------------- MARK: -
/**
Draw a little FLUID beaker symbol.
@@ -580,7 +589,7 @@ void fd_file(Fl_Color c) {
/**
Instantiate the class that holds a list of all layouts and manages the UI.
*/
-Fd_Layout_List::Fd_Layout_List()
+Layout_List::Layout_List()
: main_menu_(main_layout_submenu_),
choice_menu_(static_choice_menu),
list_(static_suite_list),
@@ -599,15 +608,15 @@ Fd_Layout_List::Fd_Layout_List()
/**
Release allocated resources.
*/
-Fd_Layout_List::~Fd_Layout_List() {
+Layout_List::~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();
+ Layout_Suite &suite = list_[i];
+ if (suite.storage_ != fld::Tool_Store::INTERNAL)
+ suite.~Layout_Suite();
}
::free(list_);
}
@@ -616,10 +625,10 @@ Fd_Layout_List::~Fd_Layout_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;
+void Layout_List::update_dialogs() {
+ static Fl_Menu_Item *preset_menu = nullptr;
if (!preset_menu) {
- preset_menu = (Fl_Menu_Item*)main_menubar->find_item(select_layout_preset_cb);
+ preset_menu = (Fl_Menu_Item*)Fluid.main_menubar->find_item(select_layout_preset_cb);
assert(preset_menu);
}
assert(this);
@@ -627,8 +636,8 @@ void Fd_Layout_List::update_dialogs() {
assert(current_suite_ < list_size_);
assert(current_preset_ >= 0 );
assert(current_preset_ < 3);
- layout = list_[current_suite_].layout[current_preset_];
- assert(layout);
+ 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();
@@ -640,7 +649,7 @@ void Fd_Layout_List::update_dialogs() {
/**
Refresh the label pointers for both pulldown menus.
*/
-void Fd_Layout_List::update_menu_labels() {
+void Layout_List::update_menu_labels() {
for (int i=0; i<list_size_; i++) {
main_menu_[i].label(list_[i].menu_label);
choice_menu_[i].label(list_[i].menu_label);
@@ -650,35 +659,35 @@ void Fd_Layout_List::update_menu_labels() {
/**
Load all user layouts from the FLUID user preferences.
*/
-int Fd_Layout_List::load(const std::string &filename) {
- remove_all(FD_STORE_FILE);
- Fl_Preferences prefs(filename.c_str(), "layout.fluid.fltk.org", NULL, Fl_Preferences::C_LOCALE);
- read(prefs, FD_STORE_FILE);
+int Layout_List::load(const std::string &filename) {
+ remove_all(fld::Tool_Store::FILE);
+ Fl_Preferences prefs(filename.c_str(), "layout.fluid.fltk.org", nullptr, Fl_Preferences::C_LOCALE);
+ read(prefs, fld::Tool_Store::FILE);
return 0;
}
/**
Save all user layouts to the FLUID user preferences.
*/
-int Fd_Layout_List::save(const std::string &filename) {
+int Layout_List::save(const std::string &filename) {
assert(this);
- Fl_Preferences prefs(filename.c_str(), "layout.fluid.fltk.org", NULL, (Fl_Preferences::Root)(Fl_Preferences::C_LOCALE|Fl_Preferences::CLEAR));
+ Fl_Preferences prefs(filename.c_str(), "layout.fluid.fltk.org", nullptr, (Fl_Preferences::Root)(Fl_Preferences::C_LOCALE|Fl_Preferences::CLEAR));
prefs.clear();
- write(prefs, FD_STORE_FILE);
+ write(prefs, fld::Tool_Store::FILE);
return 0;
}
/**
Write Suite and Layout selection and selected layout data to Preferences database.
*/
-void Fd_Layout_List::write(Fl_Preferences &prefs, Fd_Tool_Store storage) {
+void Layout_List::write(Fl_Preferences &prefs, fld::Tool_Store storage) {
Fl_Preferences prefs_list(prefs, "Layouts");
prefs_list.clear();
prefs_list.set("current_suite", list_[current_suite()].name_);
prefs_list.set("current_preset", current_preset());
int n = 0;
for (int i = 0; i < list_size_; ++i) {
- Fd_Layout_Suite &suite = list_[i];
+ Layout_Suite &suite = list_[i];
if (suite.storage_ == storage) {
Fl_Preferences prefs_suite(prefs_list, Fl_Preferences::Name(n++));
suite.write(prefs_suite);
@@ -689,16 +698,16 @@ void Fd_Layout_List::write(Fl_Preferences &prefs, Fd_Tool_Store storage) {
/**
Read Suite and Layout selection and selected layout data to Preferences database.
*/
-void Fd_Layout_List::read(Fl_Preferences &prefs, Fd_Tool_Store storage) {
+void Layout_List::read(Fl_Preferences &prefs, fld::Tool_Store storage) {
Fl_Preferences prefs_list(prefs, "Layouts");
std::string cs;
int cp = 0;
- preferences_get(prefs_list, "current_suite", cs, "");
+ prefs_list.get("current_suite", cs, "");
prefs_list.get("current_preset", cp, 0);
for (int i = 0; i < prefs_list.groups(); ++i) {
Fl_Preferences prefs_suite(prefs_list, Fl_Preferences::Name(i));
- char *new_name = NULL;
- prefs_suite.get("name", new_name, NULL);
+ char *new_name = nullptr;
+ prefs_suite.get("name", new_name, nullptr);
if (new_name) {
int n = add(new_name);
list_[n].read(prefs_suite);
@@ -714,12 +723,12 @@ void Fd_Layout_List::read(Fl_Preferences &prefs, Fd_Tool_Store storage) {
/**
Write Suite and Layout selection and project layout data to an .fl project file.
*/
-void Fd_Layout_List::write(fld::io::Project_Writer *out) {
+void Layout_List::write(fld::io::Project_Writer *out) {
// Don't write the Snap field if no custom layout was used
if ((current_suite()==0) && (current_preset()==0)) {
int nSuite = 0;
for (int i=0; i<list_size_; i++) {
- if (list_[i].storage_ == FD_STORE_PROJECT) nSuite++;
+ if (list_[i].storage_ == fld::Tool_Store::PROJECT) nSuite++;
}
if (nSuite == 0) return;
}
@@ -727,8 +736,8 @@ void Fd_Layout_List::write(fld::io::Project_Writer *out) {
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; i<list_size_; i++) {
- Fd_Layout_Suite &suite = list_[i];
- if (suite.storage_ == FD_STORE_PROJECT)
+ Layout_Suite &suite = list_[i];
+ if (suite.storage_ == fld::Tool_Store::PROJECT)
suite.write(out);
}
out->write_string("}");
@@ -737,7 +746,7 @@ void Fd_Layout_List::write(fld::io::Project_Writer *out) {
/**
Read Suite and Layout selection and project layout data from an .fl project file.
*/
-void Fd_Layout_List::read(fld::io::Project_Reader *in) {
+void Layout_List::read(fld::io::Project_Reader *in) {
const char *key;
key = in->read_word(1);
if (key && !strcmp(key, "{")) {
@@ -755,7 +764,7 @@ void Fd_Layout_List::read(fld::io::Project_Reader *in) {
} else if (!strcmp(key, "suite")) {
int n = add(in->filename_name());
list_[n].read(in);
- list_[n].storage(FD_STORE_PROJECT);
+ list_[n].storage(fld::Tool_Store::PROJECT);
} else if (!strcmp(key, "}")) {
break;
} else {
@@ -774,11 +783,11 @@ void Fd_Layout_List::read(fld::io::Project_Reader *in) {
Set the current Suite.
\param[in] ix index into list of suites
*/
-void Fd_Layout_List::current_suite(int ix) {
+void Layout_List::current_suite(int ix) {
assert(ix >= 0);
assert(ix < list_size_);
current_suite_ = ix;
- layout = list_[current_suite_].layout[current_preset_];
+ Fluid.proj.layout = list_[current_suite_].layout[current_preset_];
}
/**
@@ -786,10 +795,10 @@ void Fd_Layout_List::current_suite(int ix) {
\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) {
+void 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];
+ Layout_Suite &suite = list_[i];
if (suite.name_ && (strcmp(suite.name_, arg_name.c_str()) == 0)) {
current_suite(i);
break;
@@ -801,25 +810,25 @@ void Fd_Layout_List::current_suite(std::string arg_name) {
Select a Preset within the current Suite.
\param[in] ix 0 = application, 1 = dialog, 2 = toolbox
*/
-void Fd_Layout_List::current_preset(int ix) {
+void Layout_List::current_preset(int ix) {
assert(ix >= 0);
assert(ix < 3);
current_preset_ = ix;
- layout = list_[current_suite_].layout[current_preset_];
+ Fluid.proj.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;
+void Layout_List::capacity(int n) {
+ static Fl_Menu_Item *suite_menu = nullptr;
if (!suite_menu)
- suite_menu = (Fl_Menu_Item*)main_menubar->find_item(layout_suite_marker);
+ suite_menu = (Fl_Menu_Item*)Fluid.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));
+ 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_);
@@ -847,22 +856,22 @@ void Fd_Layout_List::capacity(int n) {
\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) {
+int 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];
+ 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 Fd_Layout_Preset;
- ::memcpy(new_suite.layout[i], old_suite.layout[i], sizeof(Fd_Layout_Preset));
+ new_suite.layout[i] = new Layout_Preset;
+ ::memcpy(new_suite.layout[i], old_suite.layout[i], sizeof(Layout_Preset));
}
- Fd_Tool_Store new_storage = old_suite.storage_;
- if (new_storage == FD_STORE_INTERNAL)
- new_storage = FD_STORE_USER;
+ 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());
@@ -877,7 +886,7 @@ int Fd_Layout_List::add(const char *name) {
/**
Rename the current Suite.
*/
-void Fd_Layout_List::rename(const char *name) {
+void Layout_List::rename(const char *name) {
int n = current_suite();
list_[n].name(name);
main_menu_[n].label(list_[n].menu_label);
@@ -888,7 +897,7 @@ void Fd_Layout_List::rename(const char *name) {
Remove the given suite.
\param[in] ix index into list of suites
*/
-void Fd_Layout_List::remove(int ix) {
+void Layout_List::remove(int ix) {
int tail = list_size_-ix-1;
if (tail) {
for (int i = ix; i < list_size_-1; i++)
@@ -903,9 +912,9 @@ void Fd_Layout_List::remove(int ix) {
/**
Remove all Suites that use the given storage attribute.
- \param[in] storage storage attribute, see FD_STORE_INTERNAL, etc.
+ \param[in] storage storage attribute, see fld::Tool_Store::INTERNAL, etc.
*/
-void Fd_Layout_List::remove_all(Fd_Tool_Store storage) {
+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);
@@ -931,25 +940,25 @@ static int nearest(int x, int left, int grid, int right=0x7fff) {
return grid_x;
}
-static bool in_window(Fd_Snap_Data &d) {
+static bool in_window(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_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(Fd_Snap_Data &d) {
- return (d.wgt && d.wgt->parent && d.wgt->parent->is_a(ID_Tabs));
+static bool in_tabs(Snap_Data &d) {
+ return (d.wgt && d.wgt->parent && d.wgt->parent->is_a(Type::Tabs));
}
-static Fl_Group *parent(Fd_Snap_Data &d) {
+static Fl_Group *parent(Snap_Data &d) {
return (d.wgt->o->parent());
}
-// ---- Fd_Snap_Action ------------------------------------------------- MARK: -
+// ---- Snap_Action ------------------------------------------------- MARK: -
-/** \class Fd_Snap_Action
+/** \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
@@ -974,7 +983,7 @@ static Fl_Group *parent(Fd_Snap_Data &d) {
\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 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;
@@ -987,9 +996,9 @@ int Fd_Snap_Action::check_x_(Fd_Snap_Data &d, int x_ref, int x_snap) {
/**
\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)
+ \see Snap_Action::check_x_(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 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;
@@ -1002,9 +1011,9 @@ int Fd_Snap_Action::check_y_(Fd_Snap_Data &d, int y_ref, int y_snap) {
/**
\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)
+ \see Snap_Action::check_x_(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) {
+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;
@@ -1025,7 +1034,7 @@ void Fd_Snap_Action::check_x_y_(Fd_Snap_Data &d, int x_ref, int x_snap, int y_re
should be drawn.
\param[inout] d current event data
*/
-bool Fd_Snap_Action::matches(Fd_Snap_Data &d) {
+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);
@@ -1038,7 +1047,7 @@ bool Fd_Snap_Action::matches(Fd_Snap_Data &d) {
\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) {
+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);
@@ -1054,7 +1063,7 @@ void Fd_Snap_Action::check_all(Fd_Snap_Data &data) {
coordinate, all of them will be drawn.
\param[inout] d current event data
*/
-void Fd_Snap_Action::draw_all(Fd_Snap_Data &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);
@@ -1062,7 +1071,8 @@ void Fd_Snap_Action::draw_all(Fd_Snap_Data &data) {
}
/** Return a sensible step size for resizing a widget. */
-void Fd_Snap_Action::get_resize_stepsize(int &x_step, int &y_step) {
+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;
@@ -1076,7 +1086,8 @@ void Fd_Snap_Action::get_resize_stepsize(int &x_step, int &y_step) {
}
/** Return a sensible step size for moving a widget. */
-void Fd_Snap_Action::get_move_stepsize(int &x_step, int &y_step) {
+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;
@@ -1090,7 +1101,8 @@ void Fd_Snap_Action::get_move_stepsize(int &x_step, int &y_step) {
}
/** Fix the given size to the same or next bigger snap position. */
-void Fd_Snap_Action::better_size(int &w, int &h) {
+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;
@@ -1105,8 +1117,8 @@ void Fd_Snap_Action::better_size(int &w, int &h) {
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;
+ 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;
}
@@ -1115,7 +1127,7 @@ void Fd_Snap_Action::better_size(int &w, int &h) {
/**
Base class for all actions that drag the left side or the entire widget.
*/
-class Fd_Snap_Left : public Fd_Snap_Action {
+class Fd_Snap_Left : public Snap_Action {
public:
Fd_Snap_Left() { type = 1; mask = FD_LEFT|FD_DRAG; }
};
@@ -1123,7 +1135,7 @@ public:
/**
Base class for all actions that drag the right side or the entire widget.
*/
-class Fd_Snap_Right : public Fd_Snap_Action {
+class Fd_Snap_Right : public Snap_Action {
public:
Fd_Snap_Right() { type = 1; mask = FD_RIGHT|FD_DRAG; }
};
@@ -1131,7 +1143,7 @@ public:
/**
Base class for all actions that drag the top side or the entire widget.
*/
-class Fd_Snap_Top : public Fd_Snap_Action {
+class Fd_Snap_Top : public Snap_Action {
public:
Fd_Snap_Top() { type = 2; mask = FD_TOP|FD_DRAG; }
};
@@ -1139,7 +1151,7 @@ public:
/**
Base class for all actions that drag the bottom side or the entire widget.
*/
-class Fd_Snap_Bottom : public Fd_Snap_Action {
+class Fd_Snap_Bottom : public Snap_Action {
public:
Fd_Snap_Bottom() { type = 2; mask = FD_BOTTOM|FD_DRAG; }
};
@@ -1151,8 +1163,8 @@ public:
*/
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); };
+ 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;
@@ -1161,8 +1173,8 @@ Fd_Snap_Left_Window_Edge snap_left_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); };
+ 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;
@@ -1171,8 +1183,8 @@ Fd_Snap_Right_Window_Edge snap_right_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); };
+ 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;
@@ -1181,8 +1193,8 @@ Fd_Snap_Top_Window_Edge snap_top_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); };
+ 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;
@@ -1191,11 +1203,11 @@ Fd_Snap_Bottom_Window_Edge snap_bottom_window_edge;
*/
class Fd_Snap_Left_Window_Margin : public Fd_Snap_Left {
public:
- void check(Fd_Snap_Data &d) FL_OVERRIDE {
+ void check(Snap_Data &d) override {
clr();
- if (in_window(d)) check_x_(d, d.bx, layout->left_window_margin);
+ if (in_window(d)) check_x_(d, d.bx, Fluid.proj.layout->left_window_margin);
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
draw_h_arrow(d.bx, (d.by+d.bt)/2, 0);
};
};
@@ -1203,11 +1215,11 @@ 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 {
+ void check(Snap_Data &d) override {
clr();
- if (in_window(d)) check_x_(d, d.br, d.win->o->w()-layout->right_window_margin);
+ if (in_window(d)) check_x_(d, d.br, d.win->o->w()-Fluid.proj.layout->right_window_margin);
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
draw_h_arrow(d.br, (d.by+d.bt)/2, d.win->o->w()-1);
};
};
@@ -1215,11 +1227,11 @@ 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 {
+ void check(Snap_Data &d) override {
clr();
- if (in_window(d)) check_y_(d, d.by, layout->top_window_margin);
+ if (in_window(d)) check_y_(d, d.by, Fluid.proj.layout->top_window_margin);
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
draw_v_arrow((d.bx+d.br)/2, d.by, 0);
};
};
@@ -1227,11 +1239,11 @@ 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 {
+ void check(Snap_Data &d) override {
clr();
- if (in_window(d)) check_y_(d, d.bt, d.win->o->h()-layout->bottom_window_margin);
+ if (in_window(d)) check_y_(d, d.bt, d.win->o->h()-Fluid.proj.layout->bottom_window_margin);
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
draw_v_arrow((d.bx+d.br)/2, d.bt, d.win->o->h()-1);
};
};
@@ -1244,11 +1256,11 @@ Fd_Snap_Bottom_Window_Margin snap_bottom_window_margin;
*/
class Fd_Snap_Left_Group_Edge : public Fd_Snap_Left {
public:
- void check(Fd_Snap_Data &d) FL_OVERRIDE {
+ void check(Snap_Data &d) override {
clr();
if (in_group(d)) check_x_(d, d.bx, parent(d)->x());
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
draw_left_brace(parent(d));
};
};
@@ -1256,11 +1268,11 @@ 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 {
+ void check(Snap_Data &d) 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 {
+ void draw(Snap_Data &d) override {
draw_right_brace(parent(d));
};
};
@@ -1268,11 +1280,11 @@ 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 {
+ void check(Snap_Data &d) override {
clr();
if (in_group(d)) check_y_(d, d.by, parent(d)->y());
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
draw_top_brace(parent(d));
};
};
@@ -1280,11 +1292,11 @@ 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 {
+ void check(Snap_Data &d) 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 {
+ void draw(Snap_Data &d) override {
draw_bottom_brace(parent(d));
};
};
@@ -1296,11 +1308,11 @@ Fd_Snap_Bottom_Group_Edge snap_bottom_group_edge;
*/
class Fd_Snap_Left_Group_Margin : public Fd_Snap_Left {
public:
- void check(Fd_Snap_Data &d) FL_OVERRIDE {
+ void check(Snap_Data &d) override {
clr();
- if (in_group(d)) check_x_(d, d.bx, parent(d)->x() + layout->left_group_margin);
+ if (in_group(d)) check_x_(d, d.bx, parent(d)->x() + Fluid.proj.layout->left_group_margin);
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
draw_left_brace(parent(d));
draw_h_arrow(d.bx, (d.by+d.bt)/2, parent(d)->x());
};
@@ -1309,11 +1321,11 @@ 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 {
+ void check(Snap_Data &d) override {
clr();
- if (in_group(d)) check_x_(d, d.br, parent(d)->x()+parent(d)->w()-layout->right_group_margin);
+ if (in_group(d)) check_x_(d, d.br, parent(d)->x()+parent(d)->w()-Fluid.proj.layout->right_group_margin);
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ 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);
};
@@ -1322,11 +1334,11 @@ 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 {
+ void check(Snap_Data &d) override {
clr();
- if (in_group(d) && !in_tabs(d)) check_y_(d, d.by, parent(d)->y()+layout->top_group_margin);
+ if (in_group(d) && !in_tabs(d)) check_y_(d, d.by, parent(d)->y()+Fluid.proj.layout->top_group_margin);
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
draw_top_brace(parent(d));
draw_v_arrow((d.bx+d.br)/2, d.by, parent(d)->y());
};
@@ -1335,11 +1347,11 @@ 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 {
+ 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()-layout->bottom_group_margin);
+ 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(Fd_Snap_Data &d) FL_OVERRIDE {
+ 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);
};
@@ -1353,18 +1365,18 @@ Fd_Snap_Bottom_Group_Margin snap_bottom_group_margin;
*/
class Fd_Snap_Top_Tabs_Margin : public Fd_Snap_Top_Group_Margin {
public:
- void check(Fd_Snap_Data &d) FL_OVERRIDE {
+ void check(Snap_Data &d) override {
clr();
- if (in_tabs(d)) check_y_(d, d.by, parent(d)->y()+layout->top_tabs_margin);
+ 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(Fd_Snap_Data &d) FL_OVERRIDE {
+ void check(Snap_Data &d) override {
clr();
- if (in_tabs(d)) check_y_(d, d.bt, parent(d)->y()+parent(d)->h()-layout->bottom_tabs_margin);
+ 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;
@@ -1374,12 +1386,12 @@ Fd_Snap_Bottom_Tabs_Margin snap_bottom_tabs_margin;
/**
Base class for grid based snapping.
*/
-class Fd_Snap_Grid : public Fd_Snap_Action {
+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(Fd_Snap_Data &d, int left, int grid_x, int right, int top, int grid_y, int bottom) {
+ 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);
@@ -1392,7 +1404,7 @@ public:
else
check_x_y_(d, d.bx, nearest_x, d.by, nearest_y);
}
- bool matches(Fd_Snap_Data &d) FL_OVERRIDE {
+ 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);
@@ -1404,12 +1416,14 @@ public:
*/
class Fd_Snap_Window_Grid : public Fd_Snap_Grid {
public:
- void check(Fd_Snap_Data &d) FL_OVERRIDE {
+ 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(Fd_Snap_Data &d) FL_OVERRIDE {
+ 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);
};
};
@@ -1420,15 +1434,17 @@ Fd_Snap_Window_Grid snap_window_grid;
*/
class Fd_Snap_Group_Grid : public Fd_Snap_Grid {
public:
- void check(Fd_Snap_Data &d) FL_OVERRIDE {
+ 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(Fd_Snap_Data &d) FL_OVERRIDE {
+ 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);
};
};
@@ -1439,19 +1455,19 @@ Fd_Snap_Group_Grid snap_group_grid;
/**
Base class the check distance to other widgets in the same group.
*/
-class Fd_Snap_Sibling : public Fd_Snap_Action {
+class Fd_Snap_Sibling : public 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 {
+ 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 = NULL;
+ best_match = nullptr;
if (!d.wgt) return;
- if (!d.wgt->parent->is_a(ID_Group)) return;
+ if (!d.wgt->parent->is_a(Type::Group)) return;
int dsib_min = 1024;
- Fl_Group_Type *gt = (Fl_Group_Type*)d.wgt->parent;
+ 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; i<g->children(); i++) {
@@ -1479,10 +1495,10 @@ public:
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 {
+ int sibling_check(Snap_Data &d, Fl_Widget *s) override {
return check_x_(d, d.bx, s->x());
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
if (best_match) draw_left_brace(best_match);
};
};
@@ -1494,11 +1510,11 @@ Fd_Snap_Siblings_Left_Same snap_siblings_left_same;
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) );
+ 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(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
if (best_match) draw_right_brace(best_match);
};
};
@@ -1507,10 +1523,10 @@ 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 {
+ int sibling_check(Snap_Data &d, Fl_Widget *s) override {
return check_x_(d, d.br, s->x()+s->w());
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
if (best_match) draw_right_brace(best_match);
};
};
@@ -1519,11 +1535,11 @@ 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));
+ 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(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
if (best_match) draw_left_brace(best_match);
};
};
@@ -1532,10 +1548,10 @@ 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 {
+ int sibling_check(Snap_Data &d, Fl_Widget *s) override {
return check_y_(d, d.by, s->y());
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
if (best_match) draw_top_brace(best_match);
};
};
@@ -1544,11 +1560,11 @@ 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));
+ 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(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
if (best_match) draw_bottom_brace(best_match);
};
};
@@ -1557,10 +1573,10 @@ 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 {
+ int sibling_check(Snap_Data &d, Fl_Widget *s) override {
return check_y_(d, d.bt, s->y()+s->h());
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
if (best_match) draw_bottom_brace(best_match);
};
};
@@ -1569,11 +1585,11 @@ 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));
+ 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(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
if (best_match) draw_top_brace(best_match);
};
};
@@ -1585,10 +1601,11 @@ Fd_Snap_Siblings_Bottom snap_siblings_bottom;
/**
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 {
+class Fd_Snap_Widget_Ideal_Width : public Snap_Action {
public:
Fd_Snap_Widget_Ideal_Width() { type = 1; mask = FD_LEFT|FD_RIGHT; }
- void check(Fd_Snap_Data &d) FL_OVERRIDE {
+ void check(Snap_Data &d) override {
+ auto layout = Fluid.proj.layout;
clr();
if (!d.wgt) return;
int iw = 15, ih = 15;
@@ -1605,16 +1622,17 @@ public:
check_x_(d, d.bx, d.br-iw);
}
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ 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 Fd_Snap_Action {
+class Fd_Snap_Widget_Ideal_Height : public Snap_Action {
public:
Fd_Snap_Widget_Ideal_Height() { type = 2; mask = FD_TOP|FD_BOTTOM; }
- void check(Fd_Snap_Data &d) FL_OVERRIDE {
+ void check(Snap_Data &d) override {
+ auto layout = Fluid.proj.layout;
clr();
if (!d.wgt) return;
int iw, ih;
@@ -1631,7 +1649,7 @@ public:
check_y_(d, d.by, d.bt-ih);
}
}
- void draw(Fd_Snap_Data &d) FL_OVERRIDE {
+ void draw(Snap_Data &d) override {
draw_height(d.br+7, d.by, d.bt, 0);
};
};
@@ -1646,7 +1664,7 @@ Fd_Snap_Widget_Ideal_Height snap_widget_ideal_height;
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_Action *Snap_Action::list[] = {
&snap_left_window_edge,
&snap_right_window_edge,
&snap_top_window_edge,
@@ -1681,7 +1699,7 @@ Fd_Snap_Action *Fd_Snap_Action::list[] = {
&snap_widget_ideal_width,
&snap_widget_ideal_height,
- NULL
+ nullptr
};
// ---- draw alignment marks ------------------------------------------- MARK: -
diff --git a/fluid/app/Fd_Snap_Action.h b/fluid/app/Snap_Action.h
index 0c98b56b8..e555f9a44 100644
--- a/fluid/app/Fd_Snap_Action.h
+++ b/fluid/app/Snap_Action.h
@@ -1,7 +1,7 @@
//
// Snap action header file for the Fast Light Tool Kit (FLTK).
//
-// Copyright 2023 by Bill Spitzak and others.
+// 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
@@ -17,15 +17,26 @@
#ifndef _FLUID_FD_SNAP_ACTION_H
#define _FLUID_FD_SNAP_ACTION_H
-#include "app/fluid.h"
-#include "nodes/Fl_Window_Type.h"
-
#include <string>
+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.
@@ -34,7 +45,7 @@ extern Fl_Menu_Item main_layout_submenu_[];
There are three Presets available in one Suite, marked "application",
"dialog", and "toolbox".
*/
-class Fd_Layout_Preset {
+class Layout_Preset {
public:
int left_window_margin; ///< gap between the window border and the widget
int right_window_margin;
@@ -73,7 +84,7 @@ public:
int textsize_not_null();
};
-extern Fd_Layout_Preset *layout;
+extern Layout_Preset *default_layout_preset;
/**
\brief A collection of layout presets.
@@ -83,25 +94,26 @@ extern Fd_Layout_Preset *layout;
There are three Presets available in one Suite, marked "application",
"dialog", and "toolbox".
*/
-class Fd_Layout_Suite {
+class 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.)
+ 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(Fd_Tool_Store s) { storage_ = s; update_label(); }
+ void storage(fld::Tool_Store s) { storage_ = s; update_label(); }
void name(const char *n);
void init();
- ~Fd_Layout_Suite();
+ ~Layout_Suite();
public:
};
+
/**
\brief Manage all layout suites that are available to the user.
@@ -109,11 +121,11 @@ public:
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 {
+class Layout_List {
public:
Fl_Menu_Item *main_menu_;
Fl_Menu_Item *choice_menu_;
- Fd_Layout_Suite *list_;
+ Layout_Suite *list_;
int list_size_;
int list_capacity_;
bool list_is_static_;
@@ -121,8 +133,8 @@ public:
int current_preset_;
std::string filename_;
public:
- Fd_Layout_List();
- ~Fd_Layout_List();
+ Layout_List();
+ ~Layout_List();
void update_dialogs();
void update_menu_labels();
int current_suite() const { return current_suite_; }
@@ -130,64 +142,66 @@ public:
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]; }
+ 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(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(Fd_Layout_Suite*);
+ int add(Layout_Suite*);
void remove(int index);
- void remove_all(Fd_Tool_Store storage);
- Fd_Layout_Preset *at(int);
+ void remove_all(fld::Tool_Store storage);
+ 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 {
+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
- Fl_Widget_Type *wgt; ///< first selected widget
- Fl_Window_Type *win; ///< window that handles the drag action
+ Widget_Node *wgt; ///< first selected widget
+ Window_Node *win; ///< window that handles the drag action
int ex_out, ey_out; ///< chosen snap position
-} Fd_Snap_Data;
+} Snap_Data;
/**
\brief Find points of interest when moving the bounding box of all selected widgets.
*/
-class Fd_Snap_Action {
+class 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);
+ 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;
- 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);
+ 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 Fd_Snap_Action *list[];
- static void check_all(Fd_Snap_Data &d);
- static void draw_all(Fd_Snap_Data &d);
+ 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 <FL/Fl.H>
-#include <FL/Fl_Window.H>
-
-/**
- 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()<left)
- left = w->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()<left)
- left = w->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()<top)
- top = w->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()<top)
- top = w->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()<left)
- left = w->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()<top)
- top = w->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/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 <FL/Fl.H>
+#include <FL/filename.H>
+#include <FL/fl_ask.H>
+
+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 <switches> 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 <name> : .cxx output filename, or extension if <name> starts with '.'\n"
+ " -h <name> : .h output filename, or extension if <name> 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 <string>
+
+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 <FL/Fl.H>
-#ifdef __APPLE__
-#include <FL/platform.H> // for fl_open_callback
-#endif
-#include <FL/Fl_Help_Dialog.H>
-#include <FL/Fl_Menu_Bar.H>
-#include <FL/Fl_PNG_Image.H>
-#include <FL/Fl_Native_File_Chooser.H>
-#include <FL/Fl_Printer.H>
-#include <FL/fl_string_functions.h>
-#include <locale.h> // setlocale()..
-#include "../src/flstring.h"
-
-extern "C"
-{
-#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ)
-# include <zlib.h>
-# ifdef HAVE_PNG_H
-# include <png.h>
-# else
-# include <libpng/png.h>
-# 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\<username>\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; i<n_de; i++) {
- std::string path = tmpdir_path + de[i]->d_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; i<n; i++) if (proj_filename[i]=='\\') proj_filename[i] = '/';
- preferences_get(path, "code", code_filename, "");
- }
-#endif
- if (code_filename.empty())
- code_filename = g_project.codefile_path() + g_project.codefile_name();
- if (!batch_mode) enter_project_dir();
- int c = merge_back(code_filename, proj_filename, FD_MERGEBACK_INTERACTIVE);
- if (!batch_mode) leave_project_dir();
-
- if (c==0) fl_message("Comparing\n \"%s\"\nto\n \"%s\"\n\n"
- "MergeBack found no external modifications\n"
- "in the source code.",
- code_filename.c_str(), proj_filename.c_str());
- if (c==-2) fl_message("No corresponding source code file found.");
- return c;
-}
-
-void mergeback_cb(Fl_Widget *, void *) {
- mergeback_code_files();
-}
-#endif
-
-/**
- Write the strings that are used in i18n.
- */
-void write_strings_cb(Fl_Widget *, void *) {
- flush_text_widgets();
- if (!g_project.proj_filename) {
- save_cb(0,0);
- if (!g_project.proj_filename) return;
- }
- std::string filename = g_project.stringsfile_path() + g_project.stringsfile_name();
- int x = write_strings(filename);
- if (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", 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
- (
- "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
- "<html><head><title>FLTK: Programming with FLUID</title></head><body>\n"
- "<h2>What is FLUID?</h2>\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 <code>.fl</code> files. These files are text, and you can (with care) "
- "edit them in a text editor, perhaps to get some special effects.<p>\n"
- "FLUID can \"compile\" the <code>.fl</code> file into a <code>.cxx</code> "
- "and a <code>.h</code> file. The <code>.cxx</code> file defines all the "
- "objects from the <code>.fl</code> file and the <code>.h</code> 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.<p>\n"
- "A simple program can be made by putting all your code (including a <code>"
- "main()</code> function) into the <code>.fl</code> file and thus making the "
- "<code>.cxx</code> file a single source file to compile. Most programs are "
- "more complex than this, so you write other <code>.cxx</code> files that "
- "call the FLUID functions. These <code>.cxx</code> files must <code>"
- "#include</code> the <code>.h</code> file or they can <code>#include</code> "
- "the <code>.cxx</code> file so it still appears to be a single source file.<p>"
- "<img src=\"embedded:/fluid_flow_chart_800.png\"></p>"
- "<p>More information is available online at <a href="
- "\"https://www.fltk.org/doc-1.5/fluid.html\">https://www.fltk.org/</a>"
- "</body></html>"
- );
- } 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<char *>(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 <signal.h>
-#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 <switches> 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 <name> : .cxx output filename, or extension if <name> starts with '.'\n"
- " -h <name> : .h output filename, or extension if <name> 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 <FL/Fl_Preferences.H>
-#include <FL/Fl_Menu_Item.H>
-#include <FL/filename.H>
-
-#include <string>
-
-#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 <tools/filename.h>
+
+#include <FL/filename.H>
+
+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 <FL/Fl_Window.H>
-#include <FL/fl_ask.H>
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <zlib.h>
-
-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 <FL/fl_attr.h>
-
-#include <stdio.h>
-#include <string>
-
-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 = "<libintl.h>";
- i18n_gnu_conditional = "";
- i18n_gnu_function = "gettext";
- i18n_gnu_static_function = "gettext_noop";
-
- i18n_pos_include = "<nl_types.h>";
- i18n_pos_conditional = "";
- i18n_pos_file = "";
- i18n_pos_set = "1";
-
- include_H_from_C = 1;
- use_FL_COMMAND = 0;
- utf8_in_src = 0;
- avoid_early_includes = 0;
- header_file_set = 0;
- code_file_set = 0;
- header_file_name = ".h";
- code_file_name = ".cxx";
- write_mergeback_data = 0;
-}
-
-/**
- Tell the project and i18n tab of the settings dialog to refresh themselves.
- */
-void 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 <string>
-
-// ---- 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 `<libintl.h>` 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 `<nl_types.h>` 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, <FL/Fl.H> 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 <errno.h>
+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 &macro, 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, "<unnamed>");
- preferences_get(prefs, "label", label, "<no label>");
+ prefs.get("name", name, "<unnamed>");
+ prefs.get("label", label, "<no 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; 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);
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; i<list_size; i++) {
- if (list[i]->storage == 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; i<list_size; i++) {
- if (list[i]->storage == 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; i<list_size; i++) {
- if (list[i]->storage == 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 <FL/Enumerations.H>
@@ -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 <FL/filename.H>
+#include <FL/fl_ask.H>
+#include <FL/Fl_PNG_Image.H>
+#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/align_widget.h b/fluid/app/templates.h
index f04372215..cedec9f76 100644
--- a/fluid/app/align_widget.h
+++ b/fluid/app/templates.h
@@ -1,7 +1,7 @@
//
-// FLUID main entry for the Fast Light Tool Kit (FLTK).
+// Fluid Project Templates 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
@@ -14,11 +14,16 @@
// https://www.fltk.org/bugs.php
//
-#ifndef _FLUID_ALIGN_WIDGET_H
-#define _FLUID_ALIGN_WIDGET_H
+#ifndef FLUID_APP_TEMPLATES_H
+#define FLUID_APP_TEMPLATES_H
-class Fl_Widget;
+namespace fld {
+namespace app {
-void align_widget_cb(Fl_Widget *, long);
+void save_template();
+
+} // namespace app
+} // namespace fld
+
+#endif // FLUID_APP_TEMPLATES_H
-#endif // _FLUID_ALIGN_WIDGET_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 <FL/Fl.H>
-#include <FL/Fl_Window.H>
-#include <FL/Fl_Preferences.H>
-#include <FL/Fl_Menu_Bar.H>
-#include <FL/fl_ask.H>
-#include "tools/filename.h"
-#include "../src/flstring.h"
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-# include <io.h>
-# include <windows.h>
-# define getpid (int)GetCurrentProcessId
-#else
-# include <unistd.h>
-#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