From 51a55bc73660f64e8f4b32b8b4d3858f2a786f7b Mon Sep 17 00:00:00 2001 From: Matthias Melcher Date: Sun, 16 Mar 2025 17:16:12 -0400 Subject: Fluid: restructuring and rejuvenation of the source code. * Add classes for application and project * Removed all globals from Fluid.h * Extracting args and project history into their own classes * Moving globals into Application class * Initialize values inside headers for some classes. * Undo functionality wrapped in a class inside Project. * File reader and writer are now linked to a project. * Avoid global project access * Nodes (former Types) will be managed by a new Tree class. * Removed static members (hidden globals) form Node/Fl_Type. * Adding Tree iterator. * Use nullptr instead of 0, NULL, or 0L * Renamed Fl_..._Type to ..._Node, FL_OVERRIDE -> override * Renaming ..._type to ...::prototype * Splitting Widget Panel into multiple files. * Moved callback code into widget panel file. * Cleaning up Fluid_Image -> Image_asset * Moving Fd_Snap_Action into new namespace fld::app::Snap_Action etc. * Moved mergeback into proj folder. * `enum ID` is now `enum class Type`. --- fluid/nodes/Button_Node.cxx | 166 ++ fluid/nodes/Button_Node.h | 149 ++ fluid/nodes/Fl_Button_Type.cxx | 227 --- fluid/nodes/Fl_Button_Type.h | 46 - fluid/nodes/Fl_Function_Type.cxx | 2157 --------------------- fluid/nodes/Fl_Function_Type.h | 259 --- fluid/nodes/Fl_Grid_Type.cxx | 994 ---------- fluid/nodes/Fl_Grid_Type.h | 82 - fluid/nodes/Fl_Group_Type.cxx | 849 -------- fluid/nodes/Fl_Group_Type.h | 242 --- fluid/nodes/Fl_Menu_Type.cxx | 924 --------- fluid/nodes/Fl_Menu_Type.h | 287 --- fluid/nodes/Fl_Type.cxx | 1338 ------------- fluid/nodes/Fl_Type.h | 323 ---- fluid/nodes/Fl_Widget_Type.cxx | 3939 -------------------------------------- fluid/nodes/Fl_Widget_Type.h | 132 -- fluid/nodes/Fl_Window_Type.cxx | 1562 --------------- fluid/nodes/Fl_Window_Type.h | 157 -- fluid/nodes/Function_Node.cxx | 2158 +++++++++++++++++++++ fluid/nodes/Function_Node.h | 277 +++ fluid/nodes/Grid_Node.cxx | 775 ++++++++ fluid/nodes/Grid_Node.h | 82 + fluid/nodes/Group_Node.cxx | 849 ++++++++ fluid/nodes/Group_Node.h | 261 +++ fluid/nodes/Menu_Node.cxx | 876 +++++++++ fluid/nodes/Menu_Node.h | 310 +++ fluid/nodes/Node.cxx | 1290 +++++++++++++ fluid/nodes/Node.h | 313 +++ fluid/nodes/Tree.cxx | 128 ++ fluid/nodes/Tree.h | 106 + fluid/nodes/Widget_Node.cxx | 2438 +++++++++++++++++++++++ fluid/nodes/Widget_Node.h | 131 ++ fluid/nodes/Window_Node.cxx | 1497 +++++++++++++++ fluid/nodes/Window_Node.h | 163 ++ fluid/nodes/callbacks.cxx | 18 + fluid/nodes/callbacks.h | 23 + fluid/nodes/factory.cxx | 1286 +++++++------ fluid/nodes/factory.h | 18 +- 38 files changed, 12696 insertions(+), 14136 deletions(-) create mode 100644 fluid/nodes/Button_Node.cxx create mode 100644 fluid/nodes/Button_Node.h delete mode 100644 fluid/nodes/Fl_Button_Type.cxx delete mode 100644 fluid/nodes/Fl_Button_Type.h delete mode 100644 fluid/nodes/Fl_Function_Type.cxx delete mode 100644 fluid/nodes/Fl_Function_Type.h delete mode 100644 fluid/nodes/Fl_Grid_Type.cxx delete mode 100644 fluid/nodes/Fl_Grid_Type.h delete mode 100644 fluid/nodes/Fl_Group_Type.cxx delete mode 100644 fluid/nodes/Fl_Group_Type.h delete mode 100644 fluid/nodes/Fl_Menu_Type.cxx delete mode 100644 fluid/nodes/Fl_Menu_Type.h delete mode 100644 fluid/nodes/Fl_Type.cxx delete mode 100644 fluid/nodes/Fl_Type.h delete mode 100644 fluid/nodes/Fl_Widget_Type.cxx delete mode 100644 fluid/nodes/Fl_Widget_Type.h delete mode 100644 fluid/nodes/Fl_Window_Type.cxx delete mode 100644 fluid/nodes/Fl_Window_Type.h create mode 100644 fluid/nodes/Function_Node.cxx create mode 100644 fluid/nodes/Function_Node.h create mode 100644 fluid/nodes/Grid_Node.cxx create mode 100644 fluid/nodes/Grid_Node.h create mode 100644 fluid/nodes/Group_Node.cxx create mode 100644 fluid/nodes/Group_Node.h create mode 100644 fluid/nodes/Menu_Node.cxx create mode 100644 fluid/nodes/Menu_Node.h create mode 100644 fluid/nodes/Node.cxx create mode 100644 fluid/nodes/Node.h create mode 100644 fluid/nodes/Tree.cxx create mode 100644 fluid/nodes/Tree.h create mode 100644 fluid/nodes/Widget_Node.cxx create mode 100644 fluid/nodes/Widget_Node.h create mode 100644 fluid/nodes/Window_Node.cxx create mode 100644 fluid/nodes/Window_Node.h create mode 100644 fluid/nodes/callbacks.cxx create mode 100644 fluid/nodes/callbacks.h (limited to 'fluid/nodes') diff --git a/fluid/nodes/Button_Node.cxx b/fluid/nodes/Button_Node.cxx new file mode 100644 index 000000000..c1ac26e95 --- /dev/null +++ b/fluid/nodes/Button_Node.cxx @@ -0,0 +1,166 @@ +// +// Button Node code for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +/** + \file Bottun_Node.cxx + + Node prototypes for Fl_Button based classes. Those are used by the Node + Factory to generate the scene from project files or user input. + */ + +#include "nodes/Button_Node.h" + +#include "Fluid.h" +#include "app/Snap_Action.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +// ---- Button Nodes --------------------------------------------------- MARK: - + + +// ---- Button ---- + +Button_Node Button_Node::prototype; + +static Fl_Menu_Item buttontype_menu[] = { + {"Normal", 0, nullptr, (void*)nullptr}, + {"Toggle", 0, nullptr, (void*)FL_TOGGLE_BUTTON}, + {"Radio", 0, nullptr, (void*)FL_RADIO_BUTTON}, + {nullptr} +}; + +Fl_Menu_Item *Button_Node::subtypes() { + return buttontype_menu; +} + +void Button_Node::ideal_size(int &w, int &h) { + auto layout = Fluid.proj.layout; + h = layout->labelsize + 8; + w = layout->labelsize * 4 + 8; + fld::app::Snap_Action::better_size(w, h); +} + +Fl_Widget *Button_Node::widget(int x, int y, int w, int h) { + return new Fl_Button(x, y, w, h, "Button"); +} + +void Button_Node::write_properties(fld::io::Project_Writer &f) { + Widget_Node::write_properties(f); + Fl_Button *btn = (Fl_Button*)o; + if (btn->compact()) { + f.write_string("compact"); + f.write_string("%d", btn->compact()); + } +} + +void Button_Node::read_property(fld::io::Project_Reader &f, const char *c) { + Fl_Button *btn = (Fl_Button*)o; + if (!strcmp(c, "compact")) { + btn->compact((uchar)atol(f.read_word())); + } else { + Widget_Node::read_property(f, c); + } +} + +void Button_Node::copy_properties() { + Widget_Node::copy_properties(); + Fl_Button *s = (Fl_Button*)o, *d = (Fl_Button*)live_widget; + d->compact(s->compact()); +} + + +// ---- Return Button ---- + +void Return_Button_Node::ideal_size(int &w, int &h) { + auto layout = Fluid.proj.layout; + h = layout->labelsize + 8; + w = layout->labelsize * 4 + 8 + h; // make room for the symbol + fld::app::Snap_Action::better_size(w, h); +} + +Fl_Widget *Return_Button_Node::widget(int x, int y, int w, int h) { + return new Fl_Return_Button(x, y, w, h, "Button"); +} + +Return_Button_Node Return_Button_Node::prototype; + + +// ---- Repeat Button ---- + +Fl_Widget *Repeat_Button_Node::widget(int x, int y, int w, int h) { + return new Fl_Repeat_Button(x, y, w, h, "Button"); +} + +Repeat_Button_Node Repeat_Button_Node::prototype; + + +// ---- Light Button ---- + +void Light_Button_Node::ideal_size(int &w, int &h) { + auto layout = Fluid.proj.layout; + h = layout->labelsize + 8; + w = layout->labelsize * 4 + 8 + layout->labelsize; // make room for the light + fld::app::Snap_Action::better_size(w, h); +} + +Fl_Widget *Light_Button_Node::widget(int x, int y, int w, int h) { + return new Fl_Light_Button(x, y, w, h, "Button"); +} + +Light_Button_Node Light_Button_Node::prototype; + + +// ---- Check Button ---- + +void Check_Button_Node::ideal_size(int &w, int &h) { + auto layout = Fluid.proj.layout; + h = layout->labelsize + 8; + w = layout->labelsize * 4 + 8 + layout->labelsize; // make room for the symbol + fld::app::Snap_Action::better_size(w, h); +} + +Fl_Widget *Check_Button_Node::widget(int x, int y, int w, int h) { + return new Fl_Check_Button(x, y, w, h, "Button"); +} + +Check_Button_Node Check_Button_Node::prototype; + + +// ---- Round Button ---- + +void Round_Button_Node::ideal_size(int &w, int &h) { + auto layout = Fluid.proj.layout; + h = layout->labelsize + 8; + w = layout->labelsize * 4 + 8 + layout->labelsize; // make room for the symbol + fld::app::Snap_Action::better_size(w, h); +} + +Fl_Widget *Round_Button_Node::widget(int x, int y, int w, int h) { + return new Fl_Round_Button(x, y, w, h, "Button"); +} + +Round_Button_Node Round_Button_Node::prototype; + diff --git a/fluid/nodes/Button_Node.h b/fluid/nodes/Button_Node.h new file mode 100644 index 000000000..ecdc8cc24 --- /dev/null +++ b/fluid/nodes/Button_Node.h @@ -0,0 +1,149 @@ +// +// Button Node header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#ifndef FLUID_NODES_BUTTON_NODE_H +#define FLUID_NODES_BUTTON_NODE_H + +#include "nodes/Widget_Node.h" + +/** + \brief A handler for the simple push button and a base class for all other buttons. + */ +class Button_Node : public Widget_Node +{ +public: + typedef Widget_Node super; + static Button_Node prototype; +private: + Fl_Menu_Item *subtypes() override; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override { return "Fl_Button"; } + const char *alt_type_name() override { return "fltk::Button"; } + Fl_Widget *widget(int x, int y, int w, int h) override; + Widget_Node *_make() override { return new Button_Node(); } + int is_button() const override { return 1; } + Type type() const override { return Type::Button; } + bool is_a(Type inType) const override { return (inType==Type::Button) ? true : super::is_a(inType); } + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + void copy_properties() override; +}; + +// ---- Return Button ---- + +/** + \brief The Return Button is simply a Button with the return key as a hotkey. + */ +class Return_Button_Node : public Button_Node +{ +public: + typedef Button_Node super; + static Return_Button_Node prototype; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override { return "Fl_Return_Button"; } + const char *alt_type_name() override { return "fltk::ReturnButton"; } + Fl_Widget *widget(int x, int y, int w, int h) override; + Widget_Node *_make() override { return new Return_Button_Node(); } + Type type() const override { return Type::Return_Button; } + bool is_a(Type inType) const override { return (inType==Type::Return_Button) ? true : super::is_a(inType); } +}; + +// ---- Repeat Button ---- + +/** + \brief Handler for Fl_Repeat_Button. + \note Even though Fl_Repeat_Button is somewhat limited compared to Fl_Button, + and some settings may not make much sense, it is still derived from it, + so the wrapper should be as well. + */ +class Repeat_Button_Node : public Button_Node +{ +public: + typedef Button_Node super; + static Repeat_Button_Node prototype; +public: + const char *type_name() override { return "Fl_Repeat_Button"; } + const char *alt_type_name() override { return "fltk::RepeatButton"; } + Fl_Widget *widget(int x, int y, int w, int h) override; + Widget_Node *_make() override { return new Repeat_Button_Node(); } + Type type() const override { return Type::Repeat_Button; } + bool is_a(Type inType) const override { return (inType==Type::Repeat_Button) ? true : super::is_a(inType); } +}; + +// ---- Light Button ---- + +/** + \brief A handler for a toggle button with an indicator light. + */ +class Light_Button_Node : public Button_Node +{ +public: + typedef Button_Node super; + static Light_Button_Node prototype; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override { return "Fl_Light_Button"; } + const char *alt_type_name() override { return "fltk::LightButton"; } + Fl_Widget *widget(int x, int y, int w, int h) override; + Widget_Node *_make() override { return new Light_Button_Node(); } + Type type() const override { return Type::Light_Button; } + bool is_a(Type inType) const override { return (inType==Type::Light_Button) ? true : super::is_a(inType); } +}; + +// ---- Check Button ---- + +/** + \brief Manage buttons with a check mark on its left. + */ +class Check_Button_Node : public Button_Node +{ +public: + typedef Button_Node super; + static Check_Button_Node prototype; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override { return "Fl_Check_Button"; } + const char *alt_type_name() override { return "fltk::CheckButton"; } + Fl_Widget *widget(int x, int y, int w, int h) override; + Widget_Node *_make() override { return new Check_Button_Node(); } + Type type() const override { return Type::Check_Button; } + bool is_a(Type inType) const override { return (inType==Type::Check_Button) ? true : super::is_a(inType); } +}; + +// ---- Round Button ---- + +/** + \brief Manage buttons with a round indicator on its left. + */ +class Round_Button_Node : public Button_Node +{ +public: + typedef Button_Node super; + static Round_Button_Node prototype; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override { return "Fl_Round_Button"; } + const char *alt_type_name() override { return "fltk::RadioButton"; } + Fl_Widget *widget(int x, int y, int w, int h) override; + Widget_Node *_make() override { return new Round_Button_Node(); } + Type type() const override { return Type::Round_Button; } + bool is_a(Type inType) const override { return (inType==Type::Round_Button) ? true : super::is_a(inType); } +}; + + +#endif // FLUID_NODES_BUTTON_NODE_H diff --git a/fluid/nodes/Fl_Button_Type.cxx b/fluid/nodes/Fl_Button_Type.cxx deleted file mode 100644 index 2d3b7f31f..000000000 --- a/fluid/nodes/Fl_Button_Type.cxx +++ /dev/null @@ -1,227 +0,0 @@ -// -// Button type factory code for the Fast Light Tool Kit (FLTK). -// -// Type classes for most of the fltk widgets. Most of the work -// is done by code in Fl_Widget_Type.C. Also a factory instance -// of each of these type classes. -// -// This file also contains the "new" menu, which has a pointer -// to a factory instance for every class (both the ones defined -// here and ones in other files) -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "nodes/Fl_Button_Type.h" - -#include "app/Fd_Snap_Action.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - - - -// ---- Button Types --------------------------------------------------- MARK: - - - -// ---- Button ---- - -static Fl_Menu_Item buttontype_menu[] = { - {"Normal", 0, 0, (void*)0}, - {"Toggle", 0, 0, (void*)FL_TOGGLE_BUTTON}, - {"Radio", 0, 0, (void*)FL_RADIO_BUTTON}, - {0} -}; - -Fl_Menu_Item *Fl_Button_Type::subtypes() { - return buttontype_menu; -} - -void Fl_Button_Type::ideal_size(int &w, int &h) { - h = layout->labelsize + 8; - w = layout->labelsize * 4 + 8; - Fd_Snap_Action::better_size(w, h); -} - -Fl_Widget *Fl_Button_Type::widget(int x, int y, int w, int h) { - return new Fl_Button(x, y, w, h, "Button"); -} - -void Fl_Button_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Widget_Type::write_properties(f); - Fl_Button *btn = (Fl_Button*)o; - if (btn->compact()) { - f.write_string("compact"); - f.write_string("%d", btn->compact()); - } -} - -void Fl_Button_Type::read_property(fld::io::Project_Reader &f, const char *c) { - Fl_Button *btn = (Fl_Button*)o; - if (!strcmp(c, "compact")) { - btn->compact((uchar)atol(f.read_word())); - } else { - Fl_Widget_Type::read_property(f, c); - } -} - -void Fl_Button_Type::copy_properties() { - Fl_Widget_Type::copy_properties(); - Fl_Button *s = (Fl_Button*)o, *d = (Fl_Button*)live_widget; - d->compact(s->compact()); -} - -Fl_Button_Type Fl_Button_type; - - -// ---- Return Button ---- - -/** - \brief The Return Button is simply a Button with the return key as a hotkey. - */ -class Fl_Return_Button_Type : public Fl_Button_Type -{ - typedef Fl_Button_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE { - h = layout->labelsize + 8; - w = layout->labelsize * 4 + 8 + h; // make room for the symbol - Fd_Snap_Action::better_size(w, h); - } - const char *type_name() FL_OVERRIDE { return "Fl_Return_Button"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::ReturnButton"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { - return new Fl_Return_Button(x, y, w, h, "Button"); - } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Return_Button_Type(); } - ID id() const FL_OVERRIDE { return ID_Return_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Return_Button) ? true : super::is_a(inID); } -}; - -Fl_Return_Button_Type Fl_Return_Button_type; - - -// ---- Repeat Button ---- - -/** - \brief Handler for Fl_Repeat_Button. - \note Even though Fl_Repeat_Button is somewhat limited compared to Fl_Button, - and some settings may not make much sense, it is still derived from it, - so the wrapper should be as well. - */ -class Fl_Repeat_Button_Type : public Fl_Button_Type -{ - typedef Fl_Button_Type super; -public: - const char *type_name() FL_OVERRIDE { return "Fl_Repeat_Button"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::RepeatButton"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { - return new Fl_Repeat_Button(x, y, w, h, "Button"); - } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Repeat_Button_Type(); } - ID id() const FL_OVERRIDE { return ID_Repeat_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Repeat_Button) ? true : super::is_a(inID); } -}; - -Fl_Repeat_Button_Type Fl_Repeat_Button_type; - - -// ---- Light Button ---- - -/** - \brief A handler for a toggle button with an indicator light. - */ -class Fl_Light_Button_Type : public Fl_Button_Type -{ - typedef Fl_Button_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE { - h = layout->labelsize + 8; - w = layout->labelsize * 4 + 8 + layout->labelsize; // make room for the light - Fd_Snap_Action::better_size(w, h); - } - const char *type_name() FL_OVERRIDE { return "Fl_Light_Button"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::LightButton"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { - return new Fl_Light_Button(x, y, w, h, "Button"); - } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Light_Button_Type(); } - ID id() const FL_OVERRIDE { return ID_Light_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Light_Button) ? true : super::is_a(inID); } -}; - -Fl_Light_Button_Type Fl_Light_Button_type; - - -// ---- Check Button ---- - -/** - \brief Manage buttons with a check mark on its left. - */ -class Fl_Check_Button_Type : public Fl_Button_Type -{ - typedef Fl_Button_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE { - h = layout->labelsize + 8; - w = layout->labelsize * 4 + 8 + layout->labelsize; // make room for the symbol - Fd_Snap_Action::better_size(w, h); - } - const char *type_name() FL_OVERRIDE { return "Fl_Check_Button"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::CheckButton"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { - return new Fl_Check_Button(x, y, w, h, "Button"); - } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Check_Button_Type(); } - ID id() const FL_OVERRIDE { return ID_Check_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Check_Button) ? true : super::is_a(inID); } -}; - -Fl_Check_Button_Type Fl_Check_Button_type; - - -// ---- Round Button ---- - -/** - \brief Manage buttons with a round indicator on its left. - */ -class Fl_Round_Button_Type : public Fl_Button_Type -{ - typedef Fl_Button_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE { - h = layout->labelsize + 8; - w = layout->labelsize * 4 + 8 + layout->labelsize; // make room for the symbol - Fd_Snap_Action::better_size(w, h); - } - const char *type_name() FL_OVERRIDE { return "Fl_Round_Button"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::RadioButton"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { - return new Fl_Round_Button(x, y, w, h, "Button"); - } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Round_Button_Type(); } - ID id() const FL_OVERRIDE { return ID_Round_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Round_Button) ? true : super::is_a(inID); } -}; - -Fl_Round_Button_Type Fl_Round_Button_type; - diff --git a/fluid/nodes/Fl_Button_Type.h b/fluid/nodes/Fl_Button_Type.h deleted file mode 100644 index 4229ac82d..000000000 --- a/fluid/nodes/Fl_Button_Type.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// Button type header file for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef _FL_BUTTON_TYPE_H -#define _FL_BUTTON_TYPE_H - -#include "nodes/Fl_Widget_Type.h" - -/** - \brief A handler for the simple push button and a base class for all other buttons. - */ -class Fl_Button_Type : public Fl_Widget_Type -{ - typedef Fl_Widget_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE; - const char *type_name() FL_OVERRIDE { return "Fl_Button"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Button"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE; - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Button_Type(); } - int is_button() const FL_OVERRIDE { return 1; } - ID id() const FL_OVERRIDE { return ID_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Button) ? true : super::is_a(inID); } - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; -}; - -extern Fl_Button_Type Fl_Button_type; - - -#endif // _FL_BUTTON_TYPE_H diff --git a/fluid/nodes/Fl_Function_Type.cxx b/fluid/nodes/Fl_Function_Type.cxx deleted file mode 100644 index 5778324bf..000000000 --- a/fluid/nodes/Fl_Function_Type.cxx +++ /dev/null @@ -1,2157 +0,0 @@ -// -// C function type code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "nodes/Fl_Function_Type.h" - -#include "app/fluid.h" -#include "app/mergeback.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "nodes/Fl_Window_Type.h" -#include "nodes/Fl_Group_Type.h" -#include "panels/function_panel.h" -#include "rsrcs/comments.h" -#include "widgets/Node_Browser.h" - -#include -#include -#include -#include "../src/flstring.h" - -#include - - -/// Set a current class, so that the code of the children is generated correctly. -Fl_Class_Type *current_class = NULL; - -/** - \brief Return 1 if the list contains a function with the given signature at the top level. - Fl_Widget_Type uses this to check if a callback by a certain signature is - already defined by the user within this file. If not, Fl_Widget_Type will - generate an `extern $sig$;` statement. - \param[in] rtype return type, can be NULL to avoid checking (not used by Fl_Widget_Type) - \param[in] sig function signature - \return 1 if found. - */ -int has_toplevel_function(const char *rtype, const char *sig) { - Fl_Type *child; - for (child = Fl_Type::first; child; child = child->next) { - if (!child->is_in_class() && child->is_a(ID_Function)) { - const Fl_Function_Type *fn = (const Fl_Function_Type*)child; - if (fn->has_signature(rtype, sig)) - return 1; - } - } - return 0; -} - - -//////////////////////////////////////////////////////////////// -// quick check of any C code for legality, returns an error message - -static char buffer[128]; // for error messages - -/** - Check a quoted string contains a character. - This is used to find a matching " or ' in a string. - \param[inout] c start searching here, return where we found \c type - \param[in] type find this character - \return NULL if the character was found, else a pointer to a static string - with an error message - */ -const char *_q_check(const char * & c, int type) { - for (;;) switch (*c++) { - case '\0': - sprintf(buffer,"missing %c",type); - return buffer; - case '\\': - if (*c) c++; - break; - default: - if (*(c-1) == type) return 0; - } -} - -/** - Check normal code, match brackets and parenthesis. - Recursively run a line of code and make sure that - {, [, ", ', and ( are matched. - \param[inout] c start searching here, return the end of the search - \param[in] type find this character match - \return NULL if the character was found, else a pointer to a static string - with an error message - */ -const char *_c_check(const char * & c, int type) { - const char *d; - for (;;) switch (*c++) { - case 0: - if (!type) return 0; - sprintf(buffer, "missing '%c'", type); - return buffer; - case '/': - // Skip comments as needed... - if (*c == '/') { - while (*c != '\n' && *c) c++; - } else if (*c == '*') { - c++; - while ((*c != '*' || c[1] != '/') && *c) c++; - if (*c == '*') c+=2; - else { - return "missing '*/'"; - } - } - break; -// case '#': -// // treat cpp directives as a comment: -// // Matt: a '#' character can appear as a concatenation when defining macros -// // Matt: so instead we just silently ignore the '#' -// while (*c != '\n' && *c) c++; -// break; - case '{': - if (type==')') goto UNEXPECTED; - d = _c_check(c,'}'); - if (d) return d; - break; - case '(': - d = _c_check(c,')'); - if (d) return d; - break; - case '[': - d = _c_check(c,']'); - if (d) return d; - break; - case '\"': - d = _q_check(c,'\"'); - if (d) return d; - break; - case '\'': - d = _q_check(c,'\''); - if (d) return d; - break; - case '}': - case ')': - case ']': - UNEXPECTED: - if (type == *(c-1)) return 0; - sprintf(buffer, "unexpected '%c'", *(c-1)); - return buffer; - } -} - -/** - Check legality of c code (sort of) and return error: - Make sure that {, ", ', and ( are matched. - \param[in] c start searching here - \param[in] type find this character match (default is 0) - \return NULL if the character was found, else a pointer to a static string - with an error message - \note This function checks every conceivable line of code, which is not - always wanted. It can't differentiate characters in comments, and the - user may well intend to leave a curly bracket open - (i.e. namespace { ... } ). We should make this option user selectable. - */ -const char *c_check(const char *c, int type) { - return _c_check(c,type); -} - -// ---- Fl_Function_Type implementation - -/** \class Fl_Function_Type - Manage a C++ function node in the Fluid design. - - A function can have a signature (name followed by arguments), a return type - and a comment section. If can be local or global, and it can be declared a C - or C++ function. - */ - -/// Prototype for a function to be used by the factory. -Fl_Function_Type Fl_Function_type; - -/** - Create a new function. - */ -Fl_Function_Type::Fl_Function_Type() : - Fl_Type(), - return_type(0L), - public_(0), - cdecl_(0), - constructor(0), - havewidgets(0) -{ } - -/** - Destructor. - */ -Fl_Function_Type::~Fl_Function_Type() { - if (return_type) free((void*)return_type); -} - -/** - Create a new function for the widget tree. - \param[in] strategy add new function after current or as last child - \return the new node - */ -Fl_Type *Fl_Function_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_decl_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_Function_Type *o = new Fl_Function_Type(); - o->name("make_window()"); - o->return_type = 0; - o->add(anchor, strategy); - o->factory = this; - o->public_ = 1; - o->cdecl_ = 0; - return o; -} - -/** - Write function specific properties to an .fl file. - - "private"/"public" indicates the state of the function - - "C" is written if we want a C signature instead of C++ - - "return_type" is followed by the return type of the function - */ -void Fl_Function_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - switch (public_) { - case 0: f.write_string("private"); break; - case 2: f.write_string("protected"); break; - } - if (cdecl_) f.write_string("C"); - if (return_type) { - f.write_string("return_type"); - f.write_word(return_type); - } -} - -/** - Read function specific properties fron an .fl file. - \param[in] c read from this string - */ -void Fl_Function_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"private")) { - public_ = 0; - } else if (!strcmp(c,"protected")) { - public_ = 2; - } else if (!strcmp(c,"C")) { - cdecl_ = 1; - } else if (!strcmp(c,"return_type")) { - storestring(f.read_word(),return_type); - } else { - Fl_Type::read_property(f, c); - } -} - -/** - Open the function_panel dialog box to edit this function. - */ -void Fl_Function_Type::open() { - // fill dialog box - if (!function_panel) make_function_panel(); - f_return_type_input->value(return_type); - f_name_input->value(name()); - if (is_in_class()) { - f_public_member_choice->value(public_); - f_public_member_choice->show(); - f_public_choice->hide(); - f_c_button->hide(); - } else { - f_public_choice->value(public_); - f_public_choice->show(); - f_public_member_choice->hide(); - f_c_button->show(); - } - f_c_button->value(cdecl_); - const char *c = comment(); - f_comment_input->buffer()->text(c?c:""); - function_panel->show(); - const char* message = 0; - for (;;) { // repeat as long as there are errors - // - message loop until OK or cancel is pressed - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == f_panel_cancel) goto BREAK2; - else if (w == f_panel_ok) break; - else if (!w) Fl::wait(); - } - // - check syntax - const char *c = f_name_input->value(); - while (isspace(*c)) c++; - message = c_check(c); - if (!message) { - const char *d = c; - for (; *d != '('; d++) if (isspace(*d) || !*d) break; - if (*c && *d != '(') - message = "must be 'name(arguments)'"; - } - if (!message) { - c = f_return_type_input->value(); - message = c_check(c); - } - // - alert user - if (message) { - int v = fl_choice("Potential syntax error detected: %s", - "Continue Editing", "Ignore Error", NULL, message); - if (v==0) continue; // Continue Editing - //if (v==1) { } // Ignore Error and close dialog - } - // - copy dialog data to target variables - int mod = 0; - name(f_name_input->value()); - storestring(f_return_type_input->value(), return_type); - if (is_in_class()) { - if (public_ != f_public_member_choice->value()) { - mod = 1; - public_ = f_public_member_choice->value(); - redraw_browser(); - } - } else { - if (public_ != f_public_choice->value()) { - mod = 1; - public_ = f_public_choice->value(); - redraw_browser(); - } - } - if (cdecl_ != f_c_button->value()) { - mod = 1; - cdecl_ = f_c_button->value(); - } - c = f_comment_input->buffer()->text(); - if (c && *c) { - if (!comment() || strcmp(c, comment())) { set_modflag(1); redraw_browser(); } - comment(c); - } else { - if (comment()) { set_modflag(1); redraw_browser(); } - comment(0); - } - if (c) free((void*)c); - if (mod) set_modflag(1); - break; - } -BREAK2: - function_panel->hide(); -} - -/** - Return 1 if the function is global. - \return 1 if public, 0 if local. - */ -int Fl_Function_Type::is_public() const { - return public_; -} - -static bool fd_isspace(int c) { - return (c>0 && c<128 && isspace(c)); -} - -// code duplication: see int is_id(char c) in code.cxx -static bool fd_iskeyword(int c) { - return (c>0 && c<128 && (isalnum(c) || c=='_')); -} - -// remove all function default parameters and `override` keyword -static void clean_function_for_implementation(char *out, const char *function_name) { - char *sptr = out; - const char *nptr = function_name; - int skips=0,skipc=0; - int nc=0,plevel=0; - bool arglist_done = false; - for (;*nptr; nc++,nptr++) { - if (arglist_done && fd_isspace(nptr[0])) { - // skip `override` and `FL_OVERRIDE` keywords if they are following the list of arguments - if (strncmp(nptr+1, "override", 8)==0 && !fd_iskeyword(nptr[9])) { nptr += 8; continue; } - else if (strncmp(nptr+1, "FL_OVERRIDE", 11)==0 && !fd_iskeyword(nptr[12])) { nptr += 11; continue; } - } - if (!skips && *nptr=='(') plevel++; - else if (!skips && *nptr==')') { plevel--; if (plevel==0) arglist_done = true; } - if ( *nptr=='"' && !(nc && *(nptr-1)=='\\') ) - skips = skips ? 0 : 1; - else if(!skips && *nptr=='\'' && !(nc && *(nptr-1)=='\\')) - skipc = skipc ? 0 : 1; - if(!skips && !skipc && plevel==1 && *nptr =='=' && !(nc && *(nptr-1)=='\'') ) { // ignore '=' case - while(*++nptr && (skips || skipc || ( (*nptr!=',' && *nptr!=')') || plevel!=1) )) { - if ( *nptr=='"' && *(nptr-1)!='\\' ) - skips = skips ? 0 : 1; - else if(!skips && *nptr=='\'' && *(nptr-1)!='\\') - skipc = skipc ? 0 : 1; - if (!skips && !skipc && *nptr=='(') plevel++; - else if (!skips && *nptr==')') plevel--; - } - if (*nptr==')') if (--plevel==0) arglist_done = true; - } - if (sptr < (out + 1024 - 1)) *sptr++ = *nptr; - } - *sptr = '\0'; -} - - -/** - Write the code for the source and the header file. - This writes the code that goes \b before all children of this class. - \see write_code2(fld::io::Code_Writer& f) - */ -void Fl_Function_Type::write_code1(fld::io::Code_Writer& f) { - constructor=0; - havewidgets = 0; - Fl_Type *child; - // if the function has no children (hence no body), Fluid will not generate - // the function either. This is great if you decide to implement that function - // inside another module - char havechildren = 0; - for (child = next; child && child->level > level; child = child->next) { - havechildren = 1; - if (child->is_widget()) { - havewidgets = 1; - break; - } - } - if (havechildren) - f.write_c("\n"); - if (ismain()) { - if (havechildren) - f.write_c("int main(int argc, char **argv) {\n"); - } else { - const char* rtype = return_type; - const char* star = ""; - // from matt: let the user type "static " at the start of type - // in order to declare a static method; - int is_static = 0; - int is_virtual = 0; - if (rtype) { - if (!strcmp(rtype,"static")) {is_static = 1; rtype = 0;} - else if (!strncmp(rtype, "static ",7)) {is_static = 1; rtype += 7;} - } - if (rtype) { - if (!strcmp(rtype, "virtual")) {is_virtual = 1; rtype = 0;} - else if (!strncmp(rtype, "virtual ",8)) {is_virtual = 1; rtype += 8;} - } - if (!rtype) { - if (havewidgets) { - rtype = subclassname(child); - star = "*"; - } else rtype = "void"; - } - - const char* k = class_name(0); - if (k) { - f.write_public(public_); - if (havechildren) - write_comment_c(f); - if (name()[0] == '~') - constructor = 1; - else { - size_t n = strlen(k); - if (!strncmp(name(), k, n) && name()[n] == '(') constructor = 1; - } - f.write_h("%s", f.indent(1)); - if (is_static) f.write_h("static "); - if (is_virtual) f.write_h("virtual "); - if (!constructor) { - f.write_h("%s%s ", rtype, star); - if (havechildren) - f.write_c("%s%s ", rtype, star); - } - - // if this is a subclass, only f.write_h() the part before the ':' - char s[1024], *sptr = s; - char *nptr = (char *)name(); - - while (*nptr) { - if (*nptr == ':') { - if (nptr[1] != ':') break; - // Copy extra ":" for "class::member"... - *sptr++ = *nptr++; - } - *sptr++ = *nptr++; - } - *sptr = '\0'; - - if (s[strlen(s)-1] == '}') { // special case for inlined functions - f.write_h("%s\n", s); - } else { - f.write_h("%s;\n", s); - } - if (havechildren) { - clean_function_for_implementation(s, name()); - f.write_c("%s::%s {\n", k, s); - } - } else { - if (havechildren) - write_comment_c(f); - if (public_==1) { - if (cdecl_) - f.write_h("extern \"C\" { %s%s %s; }\n", rtype, star, name()); - else - f.write_h("%s%s %s;\n", rtype, star, name()); - } else if (public_==2) { - // write neither the prototype nor static, the function may be declared elsewhere - } else { - if (havechildren) - f.write_c("static "); - } - - // write everything but the default parameters (if any) - char s[1024]; - if (havechildren) { - clean_function_for_implementation(s, name()); - f.write_c("%s%s %s {\n", rtype, star, s); - } - } - } - - if (havewidgets && child && !child->name()) - f.write_c("%s%s* w;\n", f.indent(1), subclassname(child)); - f.indentation++; -} - -/** - Write the code for the source and the header file. - This writes the code that goes \b after all children of this class. - \see write_code1(fld::io::Code_Writer& f) - */ -void Fl_Function_Type::write_code2(fld::io::Code_Writer& f) { - Fl_Type *child; - const char *var = "w"; - char havechildren = 0; - for (child = next; child && child->level > level; child = child->next) { - havechildren = 1; - if (child->is_a(ID_Window) && child->name()) var = child->name(); - } - - if (ismain()) { - if (havewidgets) - f.write_c("%s%s->show(argc, argv);\n", f.indent(1), var); - if (havechildren) - f.write_c("%sreturn Fl::run();\n", f.indent(1)); - } else if (havewidgets && !constructor && !return_type) { - f.write_c("%sreturn %s;\n", f.indent(1), var); - } - if (havechildren) - f.write_c("}\n"); - f.indentation = 0; -} - -/** - Check if the return type and signature s match. - \param[in] rtype function return type - \param[in] sig function name followed by arguments - \return 1 if they match, 0 if not - */ -int Fl_Function_Type::has_signature(const char *rtype, const char *sig) const { - if (rtype && !return_type) return 0; - if (!name()) return 0; - if ( (rtype==0L || strcmp(return_type, rtype)==0) - && fl_filename_match(name(), sig)) { - return 1; - } - return 0; -} - -// ---- Fl_Code_Type declaration - -/** \class Fl_Code_Type - Manage a block of C++ code in the Fluid design. - - This node manages an arbitrary block of code inside a function that will - be written into the source code file. Fl_Code_Block has no comment field. - However, the first line of code will be shown in the widget browser. - */ - -/// Prototype for code to be used by the factory. -Fl_Code_Type Fl_Code_type; - -/** - Constructor. - */ -Fl_Code_Type::Fl_Code_Type() : - cursor_position_(0), - code_input_scroll_row(0), - code_input_scroll_col(0) -{} - -/** - Make a new code node. - If the parent node is not a function, a message box will pop up and - the request will be ignored. - \param[in] strategy add code after current or as last child - \return new Code node - */ -Fl_Type *Fl_Code_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_code_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - if (!p) { - fl_message("Please select a function"); - return 0; - } - Fl_Code_Type *o = new Fl_Code_Type(); - o->name("printf(\"Hello, World!\\n\");"); - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Open the code_panel or an external editor to edit this code section. - */ -void Fl_Code_Type::open() { - // Using an external code editor? Open it.. - if ( G_use_external_editor && G_external_editor_command[0] ) { - const char *cmd = G_external_editor_command; - const char *code = name(); - if (!code) code = ""; - if ( editor_.open_editor(cmd, code) == 0 ) - return; // return if editor opened ok, fall thru to built-in if not - } - // Use built-in code editor.. - if (!code_panel) make_code_panel(); - const char *text = name(); - code_input->buffer()->text( text ? text : "" ); - code_input->insert_position(cursor_position_); - code_input->scroll(code_input_scroll_row, code_input_scroll_col); - code_panel->show(); - const char* message = 0; - for (;;) { // repeat as long as there are errors - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == code_panel_cancel) goto BREAK2; - else if (w == code_panel_ok) break; - else if (!w) Fl::wait(); - } - char*c = code_input->buffer()->text(); - message = c_check(c); - if (message) { - int v = fl_choice("Potential syntax error detected: %s", - "Continue Editing", "Ignore Error", NULL, message); - if (v==0) continue; // Continue Editing - //if (v==1) { } // Ignore Error and close dialog - } - name(c); - free(c); - break; - } - cursor_position_ = code_input->insert_position(); - code_input_scroll_row = code_input->scroll_row(); - code_input_scroll_col = code_input->scroll_col(); -BREAK2: - code_panel->hide(); -} - -/** - Grab changes from an external editor and write this node. - */ -void Fl_Code_Type::write(fld::io::Project_Writer &f) { - // External editor changes? If so, load changes into ram, update mtime/size - if ( handle_editor_changes() == 1 ) { - main_window->redraw(); // tell fluid to redraw; edits may affect tree's contents - } - Fl_Type::write(f); -} - -/** - Write the code block with the correct indentation. - */ -void Fl_Code_Type::write_code1(fld::io::Code_Writer& f) { - // External editor changes? If so, load changes into ram, update mtime/size - if ( handle_editor_changes() == 1 ) { - main_window->redraw(); // tell fluid to redraw; edits may affect tree's contents - } - // Matt: disabled f.tag(FD_TAG_GENERIC, 0); - f.write_c_indented(name(), 0, '\n'); - // Matt: disabled f.tag(FD_TAG_CODE, get_uid()); -} - -/** - See if external editor is open. - */ -int Fl_Code_Type::is_editing() { - return editor_.is_editing(); -} - -/** - Reap the editor's pid - \return -2: editor not open - \return -1: wait failed - \return 0: process still running - \return \>0: process finished + reaped (returns pid) - */ -int Fl_Code_Type::reap_editor() { - return editor_.reap_editor(); -} - -/** - Handle external editor file modifications. - If changed, record keeping is updated and file's contents is loaded into ram - \return 0: file unchanged or not editing - \return 1: file changed, internal records updated, 'code' has new content - \return -1: error getting file info (get_ms_errmsg() has reason) - \todo Figure out how saving a fluid file can be intercepted to grab - current contents of editor file.. - */ -int Fl_Code_Type::handle_editor_changes() { - const char *newcode = 0; - switch ( editor_.handle_changes(&newcode) ) { - case 1: { // (1)=changed - name(newcode); // update value in ram - free((void*)newcode); - return 1; - } - case -1: return -1; // (-1)=error -- couldn't read file (dialog showed reason) - default: break; // (0)=no change - } - return 0; -} - -// ---- Fl_CodeBlock_Type implementation - -/** \class Fl_CodeBlock_Type - Manage two blocks of C++ code enclosing its children. - - This node manages two lines of code that enclose all children - of this node. This is usually an if..then clause. - - \todo this node could support multiple lines of code for each block. - */ - -/// Prototype for a block of code to be used by the factory. -Fl_CodeBlock_Type Fl_CodeBlock_type; - -/** - Constructor. - */ -Fl_CodeBlock_Type::Fl_CodeBlock_Type() : - Fl_Type(), - after(NULL) -{ } - -/** - Destructor. - */ -Fl_CodeBlock_Type::~Fl_CodeBlock_Type() { - if (after) - free((void*)after); -} - -/** - Make a new code block. - If the parent node is not a function or another codeblock, a message box will - pop up and the request will be ignored. - \param[in] strategy add after current or as last child - \return new CodeBlock - */ -Fl_Type *Fl_CodeBlock_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_code_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - if (!p) { - fl_message("Please select a function"); - return 0; - } - Fl_CodeBlock_Type *o = new Fl_CodeBlock_Type(); - o->name("if (test())"); - o->after = 0; - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Write the specific properties for this node. - - "after" is followed by the code that comes after the children - The "before" code is stored in the name() field. - */ -void Fl_CodeBlock_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - if (after) { - f.write_string("after"); - f.write_word(after); - } -} - -/** - Read the node specific properties. - */ -void Fl_CodeBlock_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"after")) { - storestring(f.read_word(),after); - } else { - Fl_Type::read_property(f, c); - } -} - -/** - Open the codeblock_panel. - */ -void Fl_CodeBlock_Type::open() { - if (!codeblock_panel) make_codeblock_panel(); - code_before_input->value(name()); - code_after_input->value(after); - codeblock_panel->show(); - const char* message = 0; - for (;;) { // repeat as long as there are errors - // event loop - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == codeblock_panel_cancel) goto BREAK2; - else if (w == codeblock_panel_ok) break; - else if (!w) Fl::wait(); - } - // check for syntax errors - message = c_check(code_before_input->value()); - if (!message) { - message = c_check(code_after_input->value()); - } - // alert user - if (message) { - int v = fl_choice("Potential syntax error detected: %s", - "Continue Editing", "Ignore Error", NULL, message); - if (v==0) continue; // Continue Editing - //if (v==1) { } // Ignore Error and close dialog - } - // write to variables - name(code_before_input->value()); - storestring(code_after_input->value(), after); - break; - } -BREAK2: - codeblock_panel->hide(); -} - -/** - Write the "before" code. - */ -void Fl_CodeBlock_Type::write_code1(fld::io::Code_Writer& f) { - const char* c = name(); - f.write_c("%s%s {\n", f.indent(), c ? c : ""); - f.indentation++; -} - -/** - Write the "after" code. - */ -void Fl_CodeBlock_Type::write_code2(fld::io::Code_Writer& f) { - f.indentation--; - if (after) f.write_c("%s} %s\n", f.indent(), after); - else f.write_c("%s}\n", f.indent()); -} - -// ---- Fl_Decl_Type declaration - -/** \class Fl_Decl_Type - Manage the C/C++ declaration of a variable. - - This node manages a single line of code that can be in the header or the source - code, and can be made static. - - \todo this node could support multiple lines. - */ - -/// Prototype for a declaration to be used by the factory. -Fl_Decl_Type Fl_Decl_type; - -/** - Constructor. - */ -Fl_Decl_Type::Fl_Decl_Type() : - public_(0), - static_(1) -{ } - -/** - Return 1 if this declaration and its parents are public. - */ -int Fl_Decl_Type::is_public() const -{ - Fl_Type *p = parent; - while (p && !p->is_decl_block()) p = p->parent; - if(p && p->is_public() && public_) - return public_; - else if(!p) - return public_; - return 0; -} - -/** - Make a new declaration. - \param[in] strategy add after current or as last child - \return new Declaration node - */ -Fl_Type *Fl_Decl_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_decl_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_Decl_Type *o = new Fl_Decl_Type(); - o->public_ = 0; - o->static_ = 1; - o->name("int x;"); - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Write the specific properties. - - "private"/"public"/"protected" - - "local"/"global" if this is static or not - */ -void Fl_Decl_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - switch (public_) { - case 0: f.write_string("private"); break; - case 1: f.write_string("public"); break; - case 2: f.write_string("protected"); break; - } - if (static_) - f.write_string("local"); - else - f.write_string("global"); -} - -/** - Read the specific properties. - */ -void Fl_Decl_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"public")) { - public_ = 1; - } else if (!strcmp(c,"private")) { - public_ = 0; - } else if (!strcmp(c,"protected")) { - public_ = 2; - } else if (!strcmp(c,"local")) { - static_ = 1; - } else if (!strcmp(c,"global")) { - static_ = 0; - } else { - Fl_Type::read_property(f, c); - } -} - -/** - Open the decl_panel to edit this node. - */ -void Fl_Decl_Type::open() { - if (!decl_panel) make_decl_panel(); - decl_input->buffer()->text(name()); - if (is_in_class()) { - decl_class_choice->value(public_); - decl_class_choice->show(); - decl_choice->hide(); - } else { - decl_choice->value((public_&1)|((static_&1)<<1)); - decl_choice->show(); - decl_class_choice->hide(); - } - const char *c = comment(); - decl_comment_input->buffer()->text(c?c:""); - decl_panel->show(); - const char* message = 0; - for (;;) { // repeat as long as there are errors - // event loop - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == decl_panel_cancel) goto BREAK2; - else if (w == decl_panel_ok) break; - else if (!w) Fl::wait(); - } - // check values - const char*c = decl_input->buffer()->text(); - while (isspace(*c)) c++; - message = c_check(c&&c[0]=='#' ? c+1 : c); - // alert user - if (message) { - int v = fl_choice("Potential syntax error detected: %s", - "Continue Editing", "Ignore Error", NULL, message); - if (v==0) continue; // Continue Editing - //if (v==1) { } // Ignore Error and close dialog - } - // copy vlaues - name(c); - if (is_in_class()) { - if (public_!=decl_class_choice->value()) { - set_modflag(1); - public_ = decl_class_choice->value(); - } - } else { - if (public_!=(decl_choice->value()&1)) { - set_modflag(1); - public_ = (decl_choice->value()&1); - } - if (static_!=((decl_choice->value()>>1)&1)) { - set_modflag(1); - static_ = ((decl_choice->value()>>1)&1); - } - } - c = decl_comment_input->buffer()->text(); - if (c && *c) { - if (!comment() || strcmp(c, comment())) { set_modflag(1); redraw_browser(); } - comment(c); - } else { - if (comment()) { set_modflag(1); redraw_browser(); } - comment(0); - } - if (c) free((void*)c); - break; - } -BREAK2: - decl_panel->hide(); -} - -/** - Write the code to the source and header files. - \todo There are a lot of side effect in this node depending on the given text - and the parent node. They need to be understood and documented. - */ -void Fl_Decl_Type::write_code1(fld::io::Code_Writer& f) { - const char* c = name(); - if (!c) return; - // handle a few keywords differently if inside a class - if (is_in_class() && ( (!strncmp(c,"class",5) && isspace(c[5])) - || (!strncmp(c,"typedef",7) && isspace(c[7])) - || (!strncmp(c,"FL_EXPORT",9) && isspace(c[9])) - || (!strncmp(c,"struct",6) && isspace(c[6])) - || (!strncmp(c,"enum",4) && isspace(c[4])) - ) ) { - f.write_public(public_); - write_comment_h(f, f.indent(1)); - f.write_h("%s%s\n", f.indent(1), c); - return; - } - // handle putting #include, extern, using or typedef into decl: - if ( (!isalpha(*c) && *c != '~') - || (!strncmp(c,"extern",6) && isspace(c[6])) - || (!strncmp(c,"class",5) && isspace(c[5])) - || (!strncmp(c,"typedef",7) && isspace(c[7])) - || (!strncmp(c,"using",5) && isspace(c[5])) - || (!strncmp(c,"FL_EXPORT",9) && isspace(c[9])) - // || !strncmp(c,"struct",6) && isspace(c[6]) - ) { - if (public_) { - write_comment_h(f); - f.write_h("%s\n", c); - } else { - write_comment_c(f); - f.write_c("%s\n", c); - } - return; - } - // find the first C++ style comment - const char* e = c+strlen(c), *csc = c; - while (cscc && e[-1]==' ') e--; - if (class_name(1)) { - f.write_public(public_); - write_comment_h(f, f.indent(1)); - f.write_hc(f.indent(1), int(e-c), c, csc); - } else { - if (public_) { - if (static_) - f.write_h("extern "); - else - write_comment_h(f); - f.write_hc("", int(e-c), c, csc); - - if (static_) { - write_comment_c(f); - f.write_cc("", int(e-c), c, csc); - } - } else { - write_comment_c(f); - if (static_) - f.write_c("static "); - f.write_cc("", int(e-c), c, csc); - } - } -} - -// ---- Fl_Data_Type declaration - -/** \class Fl_Data_Type - Manage data from an external arbitrary file. - - The content of the file will be stored in binary inside the generated - code. This can be used to store images inline in the source code, - */ - -/// Prototype for a data node to be used by the factory. -Fl_Data_Type Fl_Data_type; - -/** - Constructor. - */ -Fl_Data_Type::Fl_Data_Type() : - Fl_Decl_Type(), - filename_(NULL), - text_mode_(0) -{ } - -/** - Destructor. - */ -Fl_Data_Type::~Fl_Data_Type() { - if (filename_) - free((void*)filename_); -} - -/** - Create an empty inline data node. - \param[in] strategy add after current or as last child - \return new inline data node - */ -Fl_Type *Fl_Data_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_decl_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_Data_Type *o = new Fl_Data_Type(); - o->public_ = 1; - o->static_ = 1; - o->filename_ = 0; - o->text_mode_ = 0; - o->name("myInlineData"); - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Write additional properties. - - "filename" followed by the filename of the file to inline - - "textmode" if data is written in ASCII vs. binary - */ -void Fl_Data_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Decl_Type::write_properties(f); - if (filename_) { - f.write_string("filename"); - f.write_word(filename_); - } - if (text_mode_ == 1) { - f.write_string("textmode"); - } - if (text_mode_ == 2) { - f.write_string("compressed"); - } -} - -/** - Read specific properties. - */ -void Fl_Data_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"filename")) { - storestring(f.read_word(), filename_, 1); - } else if (!strcmp(c,"textmode")) { - text_mode_ = 1; - } else if (!strcmp(c,"compressed")) { - text_mode_ = 2; - } else { - Fl_Decl_Type::read_property(f, c); - } -} - -/** - Open the data_panel to edit this node. - */ -void Fl_Data_Type::open() { - if (!data_panel) make_data_panel(); - data_input->value(name()); - if (is_in_class()) { - data_class_choice->value(public_); - data_class_choice->show(); - data_choice->hide(); - } else { - data_choice->value((public_&1)|((static_&1)<<1)); - data_choice->show(); - data_class_choice->hide(); - } - data_mode->value(text_mode_); - data_filename->value(filename_?filename_:""); - const char *c = comment(); - data_comment_input->buffer()->text(c?c:""); - data_panel->show(); - for (;;) { // repeat as long as there are errors - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == data_panel_cancel) goto BREAK2; - else if (w == data_panel_ok) break; - else if (w == data_filebrowser) { - enter_project_dir(); - const char *fn = fl_file_chooser("Load Inline Data", 0L, data_filename->value(), 1); - leave_project_dir(); - if (fn) { - if (strcmp(fn, data_filename->value())) - set_modflag(1); - data_filename->value(fn); - } - } - else if (!w) Fl::wait(); - } - // store the variable name: - const char*c = data_input->value(); - char *s = fl_strdup(c), *p = s, *q, *n; - for (;;++p) { // remove leading spaces - if (!isspace((unsigned char)(*p))) break; - } - n = p; - if ( (!isalpha((unsigned char)(*p))) && ((*p)!='_') && ((*p)!=':') ) goto OOPS; - ++p; - for (;;++p) { - if ( (!isalnum((unsigned char)(*p))) && ((*p)!='_') && ((*p)!=':') ) break; - } - q = p; - for (;;++q) { - if (!*q) break; - if (!isspace((unsigned char)(*q))) goto OOPS; - } - *p = 0; // remove trailing spaces - if (n==q) { - OOPS: - int v = fl_choice("%s", - "Continue Editing", "Ignore Error", NULL, - "Variable name must be a C identifier"); - if (v==0) { free(s); continue; } // Continue Editing - //if (v==1) { } // Ignore Error and close dialog - } - undo_checkpoint(); - name(n); - free(s); - // store flags - if (is_in_class()) { - if (public_!=data_class_choice->value()) { - set_modflag(1); - public_ = data_class_choice->value(); - } - } else { - if (public_!=(data_choice->value()&1)) { - set_modflag(1); - public_ = (data_choice->value()&1); - } - if (static_!=((data_choice->value()>>1)&1)) { - set_modflag(1); - static_ = ((data_choice->value()>>1)&1); - } - } - text_mode_ = data_mode->value(); - if (text_mode_ < 0) text_mode_ = 0; - if (text_mode_ > 2) text_mode_ = 2; - // store the filename - c = data_filename->value(); - if (filename_ && strcmp(filename_, data_filename->value())) - set_modflag(1); - else if (!filename_ && *c) - set_modflag(1); - if (filename_) { free((void*)filename_); filename_ = 0L; } - if (c && *c) filename_ = fl_strdup(c); - // store the comment - c = data_comment_input->buffer()->text(); - if (c && *c) { - if (!comment() || strcmp(c, comment())) { set_modflag(1); redraw_browser(); } - comment(c); - } else { - if (comment()) { set_modflag(1); redraw_browser(); } - comment(0); - } - if (c) free((void*)c); - set_modflag(1); - break; - } -BREAK2: - data_panel->hide(); -} - -/** - Write the content of the external file inline into the source code. - */ -void Fl_Data_Type::write_code1(fld::io::Code_Writer& f) { - const char *message = 0; - const char *c = name(); - if (!c) return; - const char *fn = filename_; - char *data = 0; - int nData = -1; - int uncompressedDataSize = 0; - // path should be set correctly already - if (filename_ && !f.write_codeview) { - enter_project_dir(); - FILE *f = fl_fopen(filename_, "rb"); - leave_project_dir(); - if (!f) { - message = "Can't include data from file. Can't open"; - } else { - fseek(f, 0, SEEK_END); - nData = (int)ftell(f); - fseek(f, 0, SEEK_SET); - if (nData) { - data = (char*)calloc(nData, 1); - if (fread(data, nData, 1, f)==0) { /* use default */ } - if (text_mode_ == 2) { - uncompressedDataSize = nData; - uLong nzData = compressBound(nData); - Bytef *zdata = (Bytef*)::malloc(nzData); - if (compress(zdata, &nzData, (Bytef*)data, nData) != Z_OK) { /* error */ } - ::free(data); - data = (char*)zdata; - nData = (int)nzData; - } - } - fclose(f); - } - } else { - fn = filename_ ? filename_ : ""; - } - if (is_in_class()) { - f.write_public(public_); - if (text_mode_ == 1) { - f.write_h("%sstatic const char *%s;\n", f.indent(1), c); - f.write_c("\n"); - write_comment_c(f); - f.write_c("const char *%s::%s = /* text inlined from %s */\n", class_name(1), c, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cstring(data, nData); - } else if (text_mode_ == 2) { - f.write_h("%sstatic int %s_size;\n", f.indent(1), c); - f.write_h("%sstatic unsigned char %s[%d];\n", f.indent(1), c, nData); - f.write_c("\n"); - write_comment_c(f); - f.write_c("int %s::%s_size = %d;\n", class_name(1), c, uncompressedDataSize); - f.write_c("unsigned char %s::%s[%d] = /* data compressed and inlined from %s */\n", class_name(1), c, nData, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cdata(data, nData); - } else { - f.write_h("%sstatic unsigned char %s[%d];\n", f.indent(1), c, nData); - f.write_c("\n"); - write_comment_c(f); - f.write_c("unsigned char %s::%s[%d] = /* data inlined from %s */\n", class_name(1), c, nData, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cdata(data, nData); - } - f.write_c(";\n"); - } else { - // the "header only" option does not apply here! - if (public_) { - if (static_) { - if (text_mode_ == 1) { - f.write_h("extern const char *%s;\n", c); - f.write_c("\n"); - write_comment_c(f); - f.write_c("const char *%s = /* text inlined from %s */\n", c, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cstring(data, nData); - } else if (text_mode_ == 2) { - f.write_h("extern int %s_size;\n", c); - f.write_h("extern unsigned char %s[%d];\n", c, nData); - f.write_c("\n"); - write_comment_c(f); - f.write_c("int %s_size = %d;\n", c, uncompressedDataSize); - f.write_c("unsigned char %s[%d] = /* data compressed and inlined from %s */\n", c, nData, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cdata(data, nData); - } else { - f.write_h("extern unsigned char %s[%d];\n", c, nData); - f.write_c("\n"); - write_comment_c(f); - f.write_c("unsigned char %s[%d] = /* data inlined from %s */\n", c, nData, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cdata(data, nData); - } - f.write_c(";\n"); - } else { - write_comment_h(f); - f.write_h("#error Unsupported declaration loading inline data %s\n", fn); - if (text_mode_ == 1) - f.write_h("const char *%s = \"abc...\";\n", c); - else - f.write_h("unsigned char %s[3] = { 1, 2, 3 };\n", c); - } - } else { - f.write_c("\n"); - write_comment_c(f); - if (static_) - f.write_c("static "); - if (text_mode_ == 1) { - f.write_c("const char *%s = /* text inlined from %s */\n", c, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cstring(data, nData); - } else if (text_mode_ == 2) { - f.write_c("int %s_size = %d;\n", c, uncompressedDataSize); - if (static_) f.write_c("static "); - f.write_c("unsigned char %s[%d] = /* data compressed and inlined from %s */\n", c, nData, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cdata(data, nData); - } else { - f.write_c("unsigned char %s[%d] = /* data inlined from %s */\n", c, nData, fn); - if (message) f.write_c("#error %s %s\n", message, fn); - f.write_cdata(data, nData); - } - f.write_c(";\n"); - } - } - // if we are in interactive mode, we pop up a warning dialog - // giving the error: (batch_mode && !write_codeview) ??? - if (message && !f.write_codeview) { - if (batch_mode) - fprintf(stderr, "FLUID ERROR: %s %s\n", message, fn); - else - fl_alert("%s\n%s\n", message, fn); - } - if (data) free(data); -} - -// ---- Fl_DeclBlock_Type declaration - -/** \class Fl_DeclBlock_Type - Manage a declaration block. - - Declaration blocks have two text field that are written before and after - the children of this block. This block is located at the top level and - is written to the source file, and to the header file, if declared public. - */ - -/// Prototype for a declaration block to be used by the factory. -Fl_DeclBlock_Type Fl_DeclBlock_type; - -/** - Constructor. - */ -Fl_DeclBlock_Type::Fl_DeclBlock_Type() : - Fl_Type(), - after(NULL), - write_map_(CODE_IN_SOURCE) -{ } - -/** - Destructor. - */ -Fl_DeclBlock_Type::~Fl_DeclBlock_Type() { - if (after) - ::free((void*)after); -} - -/** - Return 1 if this block is public. - */ -int Fl_DeclBlock_Type::is_public() const { - return ((write_map_&CODE_IN_HEADER) != 0); -} - -/** - Create a new declaration block. - \param[in] strategy add after current or as last child - \return new Declaration Block node - */ -Fl_Type *Fl_DeclBlock_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) p = p->parent; - while (p && !p->is_decl_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_DeclBlock_Type *o = new Fl_DeclBlock_Type(); - o->name("#if 1"); - o->write_map_ = CODE_IN_SOURCE; - o->after = fl_strdup("#endif"); - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Write the specific properties. - - "public"/"protected" - - "after" followed by the second code block. - */ -void Fl_DeclBlock_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - // deprecated - if (is_public()) f.write_string("public"); - // new way to map declaration block to various parts of the generated code - if (write_map_ != CODE_IN_SOURCE) - f.write_string("map %d", write_map_); - f.write_string("after"); - f.write_word(after); -} - -/** - Read the specific properties. - */ -void Fl_DeclBlock_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if(!strcmp(c,"public")) { - write_map_ |= CODE_IN_HEADER; - } else if(!strcmp(c,"protected")) { - // - } else if(!strcmp(c,"map")) { - write_map_ = (int)atol(f.read_word()); - } else if (!strcmp(c,"after")) { - storestring(f.read_word(),after); - } else { - Fl_Type::read_property(f, c); - } -} - -/** - Open the declblock_panel to edit this node. - */ -void Fl_DeclBlock_Type::open() { - // build dialog box - if (!declblock_panel) make_declblock_panel(); - // preset all values - declblock_before_input->value(name()); - declblock_after_input->value(after); - declblock_static_header->value(write_map_ & STATIC_IN_HEADER); - declblock_static_source->value(write_map_ & STATIC_IN_SOURCE); - declblock_code_header->value(write_map_ & CODE_IN_HEADER); - declblock_code_source->value(write_map_ & CODE_IN_SOURCE); - const char *c = comment(); - declblock_comment_input->buffer()->text(c?c:""); - // show modal dialog and loop until satisfied - declblock_panel->show(); - const char* message = 0; - for (;;) { // repeat as long as there are errors - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == declblock_panel_cancel) goto BREAK2; - else if (w == declblock_panel_ok) break; - else if (!w) Fl::wait(); - } - // verify user input - const char* a = declblock_before_input->value(); - while (isspace(*a)) a++; - const char* b = declblock_after_input->value(); - while (isspace(*b)) b++; - message = c_check(a&&a[0]=='#' ? a+1 : a); - if (!message) - message = c_check(b&&b[0]=='#' ? b+1 : b); - if (message) { - int v = fl_choice("Potential syntax error detected: %s", - "Continue Editing", "Ignore Error", NULL, message); - if (v==0) continue; // Continue Editing - //if (v==1) { } // Ignore Error and close dialog - } - // store user choices in data structure - name(a); - storestring(b, after); - if (write_map_ & STATIC_IN_HEADER) { - if (declblock_static_header->value()==0) { - write_map_ &= ~STATIC_IN_HEADER; - set_modflag(1); - } - } else { - if (declblock_static_header->value()) { - write_map_ |= STATIC_IN_HEADER; - set_modflag(1); - } - } - if (write_map_ & STATIC_IN_SOURCE) { - if (declblock_static_source->value()==0) { - write_map_ &= ~STATIC_IN_SOURCE; - set_modflag(1); - } - } else { - if (declblock_static_source->value()) { - write_map_ |= STATIC_IN_SOURCE; - set_modflag(1); - } - } - if (write_map_ & CODE_IN_HEADER) { - if (declblock_code_header->value()==0) { - write_map_ &= ~CODE_IN_HEADER; - set_modflag(1); - } - } else { - if (declblock_code_header->value()) { - write_map_ |= CODE_IN_HEADER; - set_modflag(1); - } - } - if (write_map_ & CODE_IN_SOURCE) { - if (declblock_code_source->value()==0) { - write_map_ &= ~CODE_IN_SOURCE; - set_modflag(1); - } - } else { - if (declblock_code_source->value()) { - write_map_ |= CODE_IN_SOURCE; - set_modflag(1); - } - } - c = declblock_comment_input->buffer()->text(); - if (c && *c) { - if (!comment() || strcmp(c, comment())) { set_modflag(1); redraw_browser(); } - comment(c); - } else { - if (comment()) { set_modflag(1); redraw_browser(); } - comment(0); - } - if (c) free((void*)c); - break; - } -BREAK2: - declblock_panel->hide(); -} - -/** - Write the \b before static code to the source file, and to the header file if declared public. - The before code is stored in the name() field. - */ -void Fl_DeclBlock_Type::write_static(fld::io::Code_Writer& f) { - const char* c = name(); - if (c && *c) { - if (write_map_ & STATIC_IN_HEADER) - f.write_h("%s\n", c); - if (write_map_ & STATIC_IN_SOURCE) - f.write_c("%s\n", c); - } -} - -/** - Write the \b after static code to the source file, and to the header file if declared public. - */ -void Fl_DeclBlock_Type::write_static_after(fld::io::Code_Writer& f) { - const char* c = after; - if (c && *c) { - if (write_map_ & STATIC_IN_HEADER) - f.write_h("%s\n", c); - if (write_map_ & STATIC_IN_SOURCE) - f.write_c("%s\n", c); - } -} - -/** - Write the \b before code to the source file, and to the header file if declared public. - The before code is stored in the name() field. - */ -void Fl_DeclBlock_Type::write_code1(fld::io::Code_Writer& f) { - const char* c = name(); - if (c && *c) { - if (write_map_ & CODE_IN_HEADER) - f.write_h("%s\n", c); - if (write_map_ & CODE_IN_SOURCE) - f.write_c("%s\n", c); - } -} - -/** - Write the \b after code to the source file, and to the header file if declared public. - */ -void Fl_DeclBlock_Type::write_code2(fld::io::Code_Writer& f) { - const char* c = after; - if (c && *c) { - if (write_map_ & CODE_IN_HEADER) - f.write_h("%s\n", c); - if (write_map_ & CODE_IN_SOURCE) - f.write_c("%s\n", c); - } -} - -// ---- Fl_Comment_Type declaration - -/** \class Fl_Comment_Type - Manage a comment node. - - The comment field takes one or more lines of ASCII text. If the text starts - with a '/' and a '*', Fluid assumes that the text is already formatted. If not, - every line will be preceded with "// ". - */ - -/// Prototype for a comment node to be used by the factory. -Fl_Comment_Type Fl_Comment_type; - -/** - Constructor. - */ -Fl_Comment_Type::Fl_Comment_Type() : - in_c_(1), - in_h_(1), - style_(0) -{ } - -/** - Make a new comment node. - \param[in] strategy add after current or as last child - \return new Comment node - */ -Fl_Type *Fl_Comment_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_code_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_Comment_Type *o = new Fl_Comment_Type(); - o->in_c_ = 1; - o->in_h_ = 1; - o->style_ = 0; - o->name("my comment"); - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Write respective properties. - - "in_source"/"not_in_source" if the comment will be written to the source code - - "in_header"/"not_in_header" if the comment will be written to the header file - */ -void Fl_Comment_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - if (in_c_) f.write_string("in_source"); else f.write_string("not_in_source"); - if (in_h_) f.write_string("in_header"); else f.write_string("not_in_header"); -} - -/** - Read extra properties. - */ -void Fl_Comment_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"in_source")) { - in_c_ = 1; - } else if (!strcmp(c,"not_in_source")) { - in_c_ = 0; - } else if (!strcmp(c,"in_header")) { - in_h_ = 1; - } else if (!strcmp(c,"not_in_header")) { - in_h_ = 0; - } else { - Fl_Type::read_property(f, c); - } -} - -/** - Load available preset comments. - Fluid comes with GPL and LGPL preset for comments. Users can - add their own presets which are stored per user in a separate - preferences database. - */ -static void load_comments_preset(Fl_Preferences &menu) { - static const char * const predefined_comment[] = { - "GNU Public License v3/GPL Header", "GNU Public License v3/GPL Footer", - "GNU Public License v3/LGPL Header", "GNU Public License v3/LGPL Footer", - "FLTK/Header" }; - int i, n; - menu.get("n", n, -1); - if (n == -1) menu.set("n", 5); - menu.set("version", 10400); - Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); - for (i=0; i<5; i++) { - menu.set(Fl_Preferences::Name(i), predefined_comment[i]); - db.set(predefined_comment[i], comment_text[i]); - } -} - -/** - Open the comment_panel to edit this node. - */ -void Fl_Comment_Type::open() { - if (!comment_panel) make_comment_panel(); - const char *text = name(); - { - int i=0, n=0, version = 0; - Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu"); - comment_predefined->clear(); - comment_predefined->add("_Edit/Add current comment..."); - comment_predefined->add("_Edit/Remove last selection..."); - menu.get("version", version, -1); - if (version < 10400) load_comments_preset(menu); - menu.get("n", n, 0); - for (i=0;iadd(text); - free(text); - } - } - comment_input->buffer()->text( text ? text : "" ); - comment_in_source->value(in_c_); - comment_in_header->value(in_h_); - comment_panel->show(); - char itempath[FL_PATH_MAX]; itempath[0] = 0; - int last_selected_item = 0; - for (;;) { // repeat as long as there are errors - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == comment_panel_cancel) goto BREAK2; - else if (w == comment_panel_ok) break; - else if (w == comment_predefined) { - if (comment_predefined->value()==1) { - // add the current comment to the database - const char *xname = fl_input( - "Please enter a name to reference the current\ncomment in your database.\n\n" - "Use forward slashes '/' to create submenus.", - "My Comment"); - if (xname) { - char *name = fl_strdup(xname); - for (char*s=name;*s;s++) if (*s==':') *s = ';'; - int n; - Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); - db.set(name, comment_input->buffer()->text()); - Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu"); - menu.get("n", n, 0); - menu.set(Fl_Preferences::Name(n), name); - menu.set("n", ++n); - comment_predefined->add(name); - free(name); - } - } else if (comment_predefined->value()==2) { - // remove the last selected comment from the database - if (itempath[0]==0 || last_selected_item==0) { - fl_message("Please select an entry from this menu first."); - } else if (fl_choice("Are you sure that you want to delete the entry\n" - "\"%s\"\nfrom the database?", "Cancel", "Delete", - NULL, itempath)) { - Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); - db.deleteEntry(itempath); - comment_predefined->remove(last_selected_item); - Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu"); - int i, n; - for (i=4, n=0; isize(); i++) { - const Fl_Menu_Item *mi = comment_predefined->menu()+i; - if (comment_predefined->item_pathname(itempath, 255, mi)==0) { - if (itempath[0]=='/') memmove(itempath, itempath+1, 255); - if (itempath[0]) menu.set(Fl_Preferences::Name(n++), itempath); - } - } - menu.set("n", n); - } - } else { - // load the selected comment from the database - if (comment_predefined->item_pathname(itempath, 255)==0) { - if (itempath[0]=='/') memmove(itempath, itempath+1, 255); - Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); - char *text; - db.get(itempath, text, "(no text found in data base)"); - comment_input->buffer()->text(text); - free(text); - last_selected_item = comment_predefined->value(); - } - } - } - else if (w == comment_load) { - // load a comment from disk - fl_file_chooser_ok_label("Use File"); - const char *fname = fl_file_chooser("Pick a comment", 0L, 0L); - fl_file_chooser_ok_label(NULL); - if (fname) { - if (comment_input->buffer()->loadfile(fname)) { - fl_alert("Error loading file\n%s", fname); - } - } - } - else if (!w) Fl::wait(); - } - char*c = comment_input->buffer()->text(); - name(c); - free(c); - int mod = 0; - if (in_c_ != comment_in_source->value()) { - in_c_ = comment_in_source->value(); - mod = 1; - } - if (in_h_ != comment_in_header->value()) { - in_h_ = comment_in_header->value(); - mod = 1; - } - if (mod) set_modflag(1); - break; - } -BREAK2: - comment_panel->hide(); -} - -/** - Write the comment to the files. - */ -void Fl_Comment_Type::write_code1(fld::io::Code_Writer& f) { - const char* c = name(); - if (!c) return; - if (!in_c_ && !in_h_) return; - // find out if there is already a valid comment: - const char *s = c; - while (isspace(*s)) s++; - // if this seems to be a C style comment, copy the block as is - // (it's up to the user to correctly close the comment) - if (s[0]=='/' && s[1]=='*') { - if (in_h_) f.write_h("%s\n", c); - if (in_c_) f.write_c("%s\n", c); - return; - } - // copy the comment line by line, add the double slash if needed - char *txt = fl_strdup(c); - char *b = txt, *e = txt; - for (;;) { - // find the end of the line and set it to NUL - while (*e && *e!='\n') e++; - char eol = *e; - *e = 0; - // check if there is a C++ style comment at the beginning of the line - char *s = b; - while (isspace(*s)) s++; - if (s!=e && ( s[0]!='/' || s[1]!='/') ) { - // if no comment marker was found, we add one ourselves - if (in_h_) f.write_h("// "); - if (in_c_) f.write_c("// "); - } - // now copy the rest of the line - if (in_h_) f.write_h("%s\n", b); - if (in_c_) f.write_c("%s\n", b); - if (eol==0) break; - *e++ = eol; - b = e; - } - free(txt); -} - -// ---- Fl_Class_Type declaration - -/** \class Fl_Class_Type - Manage a class declaration and implementation. - */ - -/// Prototype for a class node to be used by the factory. -Fl_Class_Type Fl_Class_type; - -/** - Constructor. - */ -Fl_Class_Type::Fl_Class_Type() : - Fl_Type(), - subclass_of(NULL), - public_(1), - class_prefix(NULL) -{ } - -/** - Destructor. - */ -Fl_Class_Type::~Fl_Class_Type() { - if (subclass_of) - free((void*)subclass_of); - if (class_prefix) - free((void*)class_prefix); -} - -/** - Return 1 if this class is marked public. - */ -int Fl_Class_Type::is_public() const { - return public_; -} - -/** - Set the prefixx string. - */ -void Fl_Class_Type::prefix(const char*p) { - free((void*) class_prefix); - class_prefix=fl_strdup(p ? p : "" ); -} - -/** - Make a new class node. - \param[in] strategy add after current or as last child - \return new Class node - */ -Fl_Type *Fl_Class_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !p->is_decl_block()) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_Class_Type *o = new Fl_Class_Type(); - o->name("UserInterface"); - o->class_prefix = NULL; - o->subclass_of = NULL; - o->public_ = 1; - o->add(anchor, strategy); - o->factory = this; - return o; -} - -/** - Write the respective properties. - - ":" followed by the super class - - "private"/"protected" - */ -void Fl_Class_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - if (subclass_of) { - f.write_string(":"); - f.write_word(subclass_of); - } - switch (public_) { - case 0: f.write_string("private"); break; - case 2: f.write_string("protected"); break; - } -} - -/** - Read additional properties. - */ -void Fl_Class_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"private")) { - public_ = 0; - } else if (!strcmp(c,"protected")) { - public_ = 2; - } else if (!strcmp(c,":")) { - storestring(f.read_word(), subclass_of); - } else { - Fl_Type::read_property(f, c); - } -} - -/** - Open the class_panel to edit the class name and superclass name. - */ -void Fl_Class_Type::open() { - if (!class_panel) make_class_panel(); - char fullname[FL_PATH_MAX]=""; - if (prefix() && strlen(prefix())) - sprintf(fullname,"%s %s",prefix(),name()); - else - strcpy(fullname, name()); - c_name_input->value(fullname); - c_subclass_input->value(subclass_of); - c_public_button->value(public_); - const char *c = comment(); - c_comment_input->buffer()->text(c?c:""); - class_panel->show(); - const char* message = 0; - - char *na=0,*pr=0,*p=0; // name and prefix substrings - - for (;;) { // repeat as long as there are errors - // we don;t give the option to ignore this error here because code depends - // on this being a C++ identifier - if (message) fl_alert("%s", message); - for (;;) { - Fl_Widget* w = Fl::readqueue(); - if (w == c_panel_cancel) goto BREAK2; - else if (w == c_panel_ok) break; - else if (!w) Fl::wait(); - } - const char*c = c_name_input->value(); - char *s = fl_strdup(c); - size_t len = strlen(s); - if (!*s) goto OOPS; - p = (char*) (s+len-1); - while (p>=s && isspace(*p)) *(p--)='\0'; - if (p=s && is_id(*p)) p--; - if ( (ps) *p--='\0'; - while (p>=s && isspace(*p)) *(p--)='\0'; - while (p>=s && is_id(*p)) p--; - if (pvalue(); - message = c_check(c); - if (message) { free((void*)s);continue;} - name(na); - prefix(pr); - free((void*)s); - storestring(c, subclass_of); - if (public_ != c_public_button->value()) { - public_ = c_public_button->value(); - set_modflag(1); - } - c = c_comment_input->buffer()->text(); - if (c && *c) { - if (!comment() || strcmp(c, comment())) { set_modflag(1); redraw_browser(); } - comment(c); - } else { - if (comment()) { set_modflag(1); redraw_browser(); } - comment(0); - } - if (c) free((void*)c); - break; - } -BREAK2: - class_panel->hide(); -} - -/** - Write the header code that declares this class. - */ -void Fl_Class_Type::write_code1(fld::io::Code_Writer& f) { - parent_class = current_class; - current_class = this; - write_public_state = 0; - f.write_h("\n"); - write_comment_h(f); - if (prefix() && strlen(prefix())) - f.write_h("class %s %s ", prefix(), name()); - else - f.write_h("class %s ", name()); - if (subclass_of) f.write_h(": %s ", subclass_of); - f.write_h("{\n"); -} - -/** - Write the header code that ends the declaration of this class. - */ -void Fl_Class_Type::write_code2(fld::io::Code_Writer& f) { - f.write_h("};\n"); - current_class = parent_class; -} - -/** - Return 1 if this class contains a function with the given signature. - */ -int Fl_Type::has_function(const char *rtype, const char *sig) const { - Fl_Type *child; - for (child = next; child && child->level > level; child = child->next) { - if (child->level == level+1 && child->is_a(ID_Function)) { - const Fl_Function_Type *fn = (const Fl_Function_Type*)child; - if (fn->has_signature(rtype, sig)) - return 1; - } - } - return 0; -} diff --git a/fluid/nodes/Fl_Function_Type.h b/fluid/nodes/Fl_Function_Type.h deleted file mode 100644 index 4c2319aee..000000000 --- a/fluid/nodes/Fl_Function_Type.h +++ /dev/null @@ -1,259 +0,0 @@ -// -// C function type header file for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2021 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef _FLUID_FL_FUNCTION_TYPE_H -#define _FLUID_FL_FUNCTION_TYPE_H - -#include "nodes/Fl_Type.h" - -#include "app/Fluid_Image.h" -#ifdef _WIN32 -#include "tools/ExternalCodeEditor_WIN32.h" -#else -#include "tools/ExternalCodeEditor_UNIX.h" -#endif - -#include -#include -#include -#include - -#include -#include - -extern class Fl_Class_Type *current_class; - -int has_toplevel_function(const char *rtype, const char *sig); - -const char *c_check(const char *c, int type = 0); - -// ---- Fl_Function_Type declaration - -class Fl_Function_Type : public Fl_Type -{ - typedef Fl_Type super; - const char* return_type; - char public_, cdecl_, constructor, havewidgets; - -public: - Fl_Function_Type(); - ~Fl_Function_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void open() FL_OVERRIDE; - int ismain() {return name_ == 0;} - const char *type_name() FL_OVERRIDE {return "Function";} - const char *title() FL_OVERRIDE { - return name() ? name() : "main()"; - } - int can_have_children() const FL_OVERRIDE {return 1;} - int is_code_block() const FL_OVERRIDE {return 1;} - int is_public() const FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Function; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Function) ? true : super::is_a(inID); } - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - int has_signature(const char *, const char*) const; -}; - -// ---- Fl_Code_Type declaration - -class Fl_Code_Type : public Fl_Type -{ - typedef Fl_Type super; - ExternalCodeEditor editor_; - int cursor_position_; - int code_input_scroll_row; - int code_input_scroll_col; - -public: - Fl_Code_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write(fld::io::Project_Writer &f) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE { } - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "code";} - int is_code_block() const FL_OVERRIDE {return 0;} - ID id() const FL_OVERRIDE { return ID_Code; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Code) ? true : super::is_a(inID); } - int is_public() const FL_OVERRIDE { return -1; } - int is_editing(); - int reap_editor(); - int handle_editor_changes(); -}; - -// ---- Fl_CodeBlock_Type declaration - -class Fl_CodeBlock_Type : public Fl_Type -{ - typedef Fl_Type super; - const char* after; - -public: - Fl_CodeBlock_Type(); - ~Fl_CodeBlock_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "codeblock";} - int is_code_block() const FL_OVERRIDE {return 1;} - int can_have_children() const FL_OVERRIDE {return 1;} - int is_public() const FL_OVERRIDE { return -1; } - ID id() const FL_OVERRIDE { return ID_CodeBlock; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_CodeBlock) ? true : super::is_a(inID); } - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; -}; - -// ---- Fl_Decl_Type declaration - -class Fl_Decl_Type : public Fl_Type -{ - typedef Fl_Type super; - -protected: - char public_; - char static_; - -public: - Fl_Decl_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE { } - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "decl";} - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - int is_public() const FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Decl; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Decl) ? true : super::is_a(inID); } -}; - -// ---- Fl_Data_Type declaration - -class Fl_Data_Type : public Fl_Decl_Type -{ - typedef Fl_Decl_Type super; - const char *filename_; - int text_mode_; - -public: - Fl_Data_Type(); - ~Fl_Data_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE {} - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "data";} - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Data; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Data) ? true : super::is_a(inID); } -}; - -// ---- Fl_DeclBlock_Type declaration - -class Fl_DeclBlock_Type : public Fl_Type -{ - typedef Fl_Type super; - enum { - CODE_IN_HEADER = 1, - CODE_IN_SOURCE = 2, - STATIC_IN_HEADER = 4, - STATIC_IN_SOURCE = 8 - }; - const char* after; ///< code after all children of this block - int write_map_; ///< see enum above - -public: - Fl_DeclBlock_Type(); - ~Fl_DeclBlock_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_static(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_static_after(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "declblock";} - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - int can_have_children() const FL_OVERRIDE {return 1;} - int is_decl_block() const FL_OVERRIDE {return 1;} - int is_public() const FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_DeclBlock; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_DeclBlock) ? true : super::is_a(inID); } -}; - -// ---- Fl_Comment_Type declaration - -class Fl_Comment_Type : public Fl_Type -{ - typedef Fl_Type super; - char in_c_, in_h_, style_; - -public: - Fl_Comment_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE { } - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "comment";} - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - int is_public() const FL_OVERRIDE { return 1; } - ID id() const FL_OVERRIDE { return ID_Comment; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Comment) ? true : super::is_a(inID); } -}; - -// ---- Fl_Class_Type declaration - -class Fl_Class_Type : public Fl_Type -{ - typedef Fl_Type super; - const char* subclass_of; - char public_; - const char* class_prefix; - -public: - Fl_Class_Type(); - ~Fl_Class_Type(); - // state variables for output: - char write_public_state; // true when public: has been printed - Fl_Class_Type* parent_class; // save class if nested -// - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void open() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "class";} - int can_have_children() const FL_OVERRIDE {return 1;} - int is_decl_block() const FL_OVERRIDE {return 1;} - int is_class() const FL_OVERRIDE {return 1;} - int is_public() const FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Class; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Class) ? true : super::is_a(inID); } - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - - // class prefix attribute access - void prefix(const char* p); - const char* prefix() const {return class_prefix;} -}; - -#endif // _FLUID_FL_FUNCTION_TYPE_H diff --git a/fluid/nodes/Fl_Grid_Type.cxx b/fluid/nodes/Fl_Grid_Type.cxx deleted file mode 100644 index 03d6620ea..000000000 --- a/fluid/nodes/Fl_Grid_Type.cxx +++ /dev/null @@ -1,994 +0,0 @@ -// -// Fl_Grid object code for the Fast Light Tool Kit (FLTK). -// -// Copyright 2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "nodes/Fl_Grid_Type.h" - -#include "app/fluid.h" -#include "app/Fd_Snap_Action.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "widgets/Node_Browser.h" -#include "widgets/Formula_Input.h" - -#include -#include -#include -#include -#include "../src/flstring.h" - -#include -#include -#include - -// ---- Fl_Grid_Proxy --------------------------------------------------- MARK: - - -/** - An implementation of the Fl_Grid widget with additional functionality. - - Fl_Grid_Proxy add a list of transient children, i.e. children that are - temporarily assigned to a cell that is already taken by another child. - */ -Fl_Grid_Proxy::Fl_Grid_Proxy(int X,int Y,int W,int H) -: Fl_Grid(X,Y,W,H), - transient_(NULL), - num_transient_(0), - cap_transient_(0) -{ -} - -Fl_Grid_Proxy::~Fl_Grid_Proxy() { - int i; - if (transient_) { - for (i=0; i 0) { - Fl_Grid::resize(X, Y, W, H); - } else { - Fl_Widget::resize(X, Y, W, H); - } - redraw(); -} - -/** - Override draw() to make groups with no box or flat box background visible. - */ -void Fl_Grid_Proxy::draw() { - if (show_ghosted_outline && (box() == FL_NO_BOX)) { - fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); - } - Fl_Grid::draw(); -} - -/** - Draw additional markings in the overlay plane when a grid is selected. - */ -void Fl_Grid_Proxy::draw_overlay() { - fl_line_style(FL_DOT); - grid_color = fl_color(); - draw_grid(); - fl_color(grid_color); -} - -/** - Move a cell into the grid or within the grid. - - If the target cell is already taken, \p how will determine what to do: - - If \p how is 0, the existing cell at \p to_row, \p to_col will be deleted, - unlinking the occupant from the grid. \p in_child will the be inserted at the - given location. - - If \p how is 1, the old cell will remain intact, however \p in_child will be - unlinked from the grid. - - If \p how is 2, the old cell will remain intact, and \p in_child will be - removed from the grid, but it will be stored in the transient list and - resized to the target cell position and size. If \p in_child is later - moved to an unoccupied cell, it will be removed from the transient list and - relinked to the grid. Rowspan and colspan are ignored here. - - \param[in] in_child must already be a child of grid - \param[in] to_row, to_col move the child into this cell - \param[in] how 0: replace occupant, 1: don't replace, 2: make transient - if occupied - */ -void Fl_Grid_Proxy::move_cell(Fl_Widget *in_child, int to_row, int to_col, int how) { - // the child must already be a true child of grid - assert(find(in_child)row() == to_row && old_cell->col() == to_col) return; - rowspan = old_cell->rowspan(); - colspan = old_cell->colspan(); - align = old_cell->align(); - old_cell->minimum_size(&w, &h); - } - if ((to_row < 0) || (to_row+rowspan > rows())) return; - if ((to_col < 0) || (to_col+colspan > cols())) return; - Fl_Grid::Cell *new_cell = NULL; - if (how == 0) { // replace old occupant in cell, making that one homeless - new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align); - } else if (how == 1) { // don't replace an old occupant, making ourselves homeless - // todo: colspan, rowspan? - if (cell(to_row, to_col) == NULL) { - new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align); - } else { - if (old_cell) remove_cell(old_cell->row(), old_cell->col()); - } - } else if (how == 2) { - Cell *current = cell(to_row, to_col); - if (current == NULL) { - new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align); - } else { - if (old_cell) remove_cell(old_cell->row(), old_cell->col()); - new_cell = transient_widget(in_child, to_row, to_col, rowspan, colspan, align); - Fl_Widget *w = current->widget(); - Fl_Type::allow_layout++; - in_child->resize(w->x(), w->y(), w->w(), w->h()); - Fl_Type::allow_layout--; - } - } - if (new_cell) new_cell->minimum_size(w, h); -} - -/** - Generate or replace a transient widget entry. - - If the widget is in the cell list, it will be removed there. - If the widget is already transient, the cell will be replaced. - - \param[in] wi a child of this Fl_Grid_Proxy, that may be linked to a cell or transient cell - \param[in] row, col, row_span, col_span, align cell parameters - */ -Fl_Grid::Cell* Fl_Grid_Proxy::transient_widget(Fl_Widget *wi, int row, int col, int row_span, int col_span, Fl_Grid_Align align) { - int i = 0; - bool remove_old_cell = false; - Cell *old_cell = cell(wi); - if (old_cell) { - remove_old_cell = true; - } else { - for (i=0; irowspan(row_span); - new_cell->colspan(col_span); - new_cell->align(align); - if (old_cell) { - int mw, mh; - old_cell->minimum_size(&mw, &mh); - new_cell->minimum_size(mw, mh); - if (remove_old_cell) { - remove_cell(old_cell->row(), old_cell->col()); - } else { - delete old_cell; - } - } - if (i == num_transient_) { - transient_make_room_(num_transient_ + 1); - transient_[i].widget = wi; - num_transient_++; - } - transient_[i].cell = new_cell; - return new_cell; -} - -/** - Make room for at least n transient widgets in the array. - \param[in] n minimum number of entries - */ -void Fl_Grid_Proxy::transient_make_room_(int n) { - if (n > cap_transient_) { - cap_transient_ = n + 10; - transient_ = (Cell_Widget_Pair*)::realloc(transient_, cap_transient_ * sizeof(Cell_Widget_Pair)); - } -} - -/** - Remove a widget form the list and deallocate the transient cell. - \param[in] w remove the transient cell for this widget - */ -void Fl_Grid_Proxy::transient_remove_(Fl_Widget *w) { - for (int i=0; ilayout(3, 3); - Fl_Group::current(0); - return g; -} - -Fl_Widget *Fl_Grid_Type::enter_live_mode(int top) { - Fl_Grid *grid = new Fl_Grid(o->x(), o->y(), o->w(), o->h()); - return propagate_live_mode(grid); -} - -void Fl_Grid_Type::leave_live_mode() { -} - -void Fl_Grid_Type::copy_properties() -{ - super::copy_properties(); - Fl_Grid *d = (Fl_Grid*)live_widget, *s =(Fl_Grid*)o; - d->layout(s->rows(), s->cols()); - int lm, tm, rm, bm; - s->margin(&lm, &tm, &rm, &bm); - d->margin(lm, tm, rm, bm); - int rg, cg; - s->gap(&rg, &cg); - d->gap(rg, cg); - // copy col widths, heights, and gaps - for (int c=0; ccols(); c++) { - d->col_width(c, s->col_width(c)); - d->col_gap(c, s->col_gap(c)); - d->col_weight(c, s->col_weight(c)); - } - // copy row widths, heights, and gaps - for (int r=0; rrows(); r++) { - d->row_height(r, s->row_height(r)); - d->row_gap(r, s->row_gap(r)); - d->row_weight(r, s->row_weight(r)); - } -} - -void Fl_Grid_Type::copy_properties_for_children() { - Fl_Grid *d = (Fl_Grid*)live_widget, *s =(Fl_Grid*)o; - for (int i=0; ichildren(); i++) { - Fl_Grid::Cell *cell = s->cell(s->child(i)); - if (cell && ichildren()) { - d->widget(d->child(i), - cell->row(), cell->col(), - cell->rowspan(), cell->colspan(), - cell->align()); - } - } - d->layout(); -} - -void Fl_Grid_Type::write_properties(fld::io::Project_Writer &f) -{ - super::write_properties(f); - Fl_Grid* grid = (Fl_Grid*)o; - int i, rows = grid->rows(), cols = grid->cols(); - f.write_indent(level+1); - f.write_string("dimensions {%d %d}", rows, cols); - int lm, tm, rm, bm; - grid->margin(&lm, &tm, &rm, &bm); - if (lm!=0 || tm!=0 || rm!=0 || bm!=0) - f.write_string("margin {%d %d %d %d}", lm, tm, rm, bm); - int rg, cg; - grid->gap(&rg, &cg); - if (rg!=0 || cg!=0) - f.write_string("gap {%d %d}", rg, cg); - // -- write all row heights if one of them is not the default 0 - for (i=0; irow_height(i)!=0) break; - if (irow_height(i)); - f.write_string("}"); - } - // -- write all row weights if one of them is not the default 50 - for (i=0; irow_weight(i)!=50) break; - if (irow_weight(i)); - f.write_string("}"); - } - // -- write all row gaps if one of them is not the default -1 - for (i=0; irow_gap(i)!=-1) break; - if (irow_gap(i)); - f.write_string("}"); - } - // -- write all col widths if one of them is not the default 0 - for (i=0; icol_width(i)!=0) break; - if (icol_width(i)); - f.write_string("}"); - } - // -- write all col weights if one of them is not the default 50 - for (i=0; icol_weight(i)!=50) break; - if (icol_weight(i)); - f.write_string("}"); - } - // -- write all col gaps if one of them is not the default -1 - for (i=0; icol_gap(i)!=-1) break; - if (icol_gap(i)); - f.write_string("}"); - } -} - -void Fl_Grid_Type::read_property(fld::io::Project_Reader &f, const char *c) -{ - Fl_Grid* grid = (Fl_Grid*)o; - if (!strcmp(c,"dimensions")) { - int rows = 3, cols = 3; - if (sscanf(f.read_word(),"%d %d", &rows, &cols) == 2) - grid->layout(rows, cols); - } else if (!strcmp(c,"margin")) { - int lm, tm, rm, bm; - if (sscanf(f.read_word(),"%d %d %d %d", &lm, &tm, &rm, &bm) == 4) - grid->margin(lm, tm, rm, bm); - } else if (!strcmp(c,"gap")) { - int rg, cg; - if (sscanf(f.read_word(),"%d %d", &rg, &cg) == 2) - grid->gap(rg, cg); - } else if (!strcmp(c,"rowheights")) { - int rows = grid->rows(); - f.read_word(1); // "{" - for (int i=0; irow_height(i, f.read_int()); - f.read_word(1); // "}" - } else if (!strcmp(c,"rowweights")) { - int rows = grid->rows(); - f.read_word(1); // "{" - for (int i=0; irow_weight(i, f.read_int()); - f.read_word(1); // "}" - } else if (!strcmp(c,"rowgaps")) { - int rows = grid->rows(); - f.read_word(1); // "{" - for (int i=0; irow_gap(i, f.read_int()); - f.read_word(1); // "}" - } else if (!strcmp(c,"colwidths")) { - int cols = grid->cols(); - f.read_word(1); // "{" - for (int i=0; icol_width(i, f.read_int()); - f.read_word(1); // "}" - } else if (!strcmp(c,"colweights")) { - int cols = grid->cols(); - f.read_word(1); // "{" - for (int i=0; icol_weight(i, f.read_int()); - f.read_word(1); // "}" - } else if (!strcmp(c,"colgaps")) { - int cols = grid->cols(); - f.read_word(1); // "{" - for (int i=0; icol_gap(i, f.read_int()); - f.read_word(1); // "}" - } else { - super::read_property(f, c); - } -} - -void Fl_Grid_Type::write_parent_properties(fld::io::Project_Writer &f, Fl_Type *child, bool encapsulate) { - Fl_Grid *grid; - Fl_Widget *child_widget; - Fl_Grid::Cell *cell; - if (!child->is_true_widget()) return super::write_parent_properties(f, child, true); - grid = (Fl_Grid*)o; - child_widget = ((Fl_Widget_Type*)child)->o; - cell = grid->cell(child_widget); - if (!cell) return super::write_parent_properties(f, child, true); - if (encapsulate) { - f.write_indent(level+2); - f.write_string("parent_properties {"); - } - f.write_indent(level+3); - f.write_string("location {%d %d}", cell->row(), cell->col()); - int v = cell->colspan(); - if (v>1) { - f.write_indent(level+3); - f.write_string("colspan %d", v); - } - v = cell->rowspan(); - if (v>1) { - f.write_indent(level+3); - f.write_string("rowspan %d", v); - } - v = (int)cell->align(); - if (v!=FL_GRID_FILL) { - f.write_indent(level+3); - f.write_string("align %d", v); - } - int min_w = 0, min_h = 0; - cell->minimum_size(&min_w, &min_h); - if (min_w!=20 || min_h!=20) { - f.write_indent(level+3); - f.write_string("minsize {%d %d}", min_w, min_h); - } - super::write_parent_properties(f, child, false); - if (encapsulate) { - f.write_indent(level+2); - f.write_string("}"); - } - return; -} - -// NOTE: we have to do this in a loop just as ::read_property() in case a new -// property is added. In the current setup, all the remaining properties -// will be skipped -void Fl_Grid_Type::read_parent_property(fld::io::Project_Reader &f, Fl_Type *child, const char *property) { - if (!child->is_true_widget()) { - super::read_parent_property(f, child, property); - return; - } - Fl_Grid *grid = (Fl_Grid*)o; - Fl_Widget *child_widget = ((Fl_Widget_Type*)child)->o; - if (!strcmp(property, "location")) { - int row = -1, col = -1; - const char *value = f.read_word(); - sscanf(value, "%d %d", &row, &col); - Fl_Grid::Cell *cell = grid->widget(child_widget, row, col); - if (cell) { - int min_w = 20, min_h = 20; - cell->minimum_size(min_w, min_h); - } - } else if (!strcmp(property, "colspan")) { - int colspan = atoi(f.read_word()); - Fl_Grid::Cell *cell = grid->cell(child_widget); - if (cell) cell->colspan(colspan); - } else if (!strcmp(property, "rowspan")) { - int rowspan = atoi(f.read_word()); - Fl_Grid::Cell *cell = grid->cell(child_widget); - if (cell) cell->rowspan(rowspan); - } else if (!strcmp(property, "align")) { - int align = atoi(f.read_word()); - Fl_Grid::Cell *cell = grid->cell(child_widget); - if (cell) cell->align((Fl_Grid_Align)align); - } if (!strcmp(property, "minsize")) { - int min_w = 20, min_h = 20; - const char *value = f.read_word(); - sscanf(value, "%d %d", &min_w, &min_h); - Fl_Grid::Cell *cell = grid->cell(child_widget); - if (cell) cell->minimum_size(min_w, min_h); - } else { - super::read_parent_property(f, child, property); - } -} - -void Fl_Grid_Type::write_code1(fld::io::Code_Writer& f) { - const char *var = name() ? name() : "o"; - Fl_Grid* grid = (Fl_Grid*)o; - Fl_Widget_Type::write_code1(f); - int i, rows = grid->rows(), cols = grid->cols(); - f.write_c("%s%s->layout(%d, %d);\n", f.indent(), var, rows, cols); - int lm, tm, rm, bm; - grid->margin(&lm, &tm, &rm, &bm); - if (lm!=0 || tm!=0 || rm!=0 || bm!=0) - f.write_c("%s%s->margin(%d, %d, %d, %d);\n", f.indent(), var, lm, tm, rm, bm); - int rg, cg; - grid->gap(&rg, &cg); - if (rg!=0 || cg!=0) - f.write_c("%s%s->gap(%d, %d);\n", f.indent(), var, rg, cg); - // -- write all row heights if one of them is not the default 0 - for (i=0; irow_height(i)!=0) break; - if (irow_height(0)); - for (i=1; irow_height(i)); - f.write_c(" };\n"); - f.write_c("%s%s->row_height(rowheights, %d);\n", f.indent(), var, rows); - } - // -- write all row weights if one of them is not the default 50 - for (i=0; irow_weight(i)!=50) break; - if (irow_weight(0)); - for (i=1; irow_weight(i)); - f.write_c(" };\n"); - f.write_c("%s%s->row_weight(rowweights, %d);\n", f.indent(), var, rows); - } - // -- write all row gaps if one of them is not the default -1 - for (i=0; irow_gap(i)!=-1) break; - if (irow_gap(0)); - for (i=1; irow_gap(i)); - f.write_c(" };\n"); - f.write_c("%s%s->row_gap(rowgaps, %d);\n", f.indent(), var, rows); - } - // -- write all col widths if one of them is not the default 0 - for (i=0; icol_width(i)!=0) break; - if (icol_width(0)); - for (i=1; icol_width(i)); - f.write_c(" };\n"); - f.write_c("%s%s->col_width(colwidths, %d);\n", f.indent(), var, cols); - } - // -- write all col weights if one of them is not the default 50 - for (i=0; icol_weight(i)!=50) break; - if (icol_weight(0)); - for (i=1; icol_weight(i)); - f.write_c(" };\n"); - f.write_c("%s%s->col_weight(colweights, %d);\n", f.indent(), var, cols); - } - // -- write all col gaps if one of them is not the default -1 - for (i=0; icol_gap(i)!=-1) break; - if (icol_gap(0)); - for (i=1; icol_gap(i)); - f.write_c(" };\n"); - f.write_c("%s%s->col_gap(colgaps, %d);\n", f.indent(), var, cols); - } -} - -void Fl_Grid_Type::write_code2(fld::io::Code_Writer& f) { - const char *var = name() ? name() : "o"; - Fl_Grid* grid = (Fl_Grid*)o; - bool first_cell = true; - for (int i=0; ichildren(); i++) { - Fl_Widget *c = grid->child(i); - Fl_Grid::Cell *cell = grid->cell(c); - if (cell) { - if (first_cell) { - f.write_c("%sFl_Grid::Cell *cell = NULL;\n", f.indent()); - first_cell = false; - } - f.write_c("%scell = %s->widget(%s->child(%d), %d, %d, %d, %d, %d);\n", - f.indent(), var, var, i, cell->row(), cell->col(), - cell->rowspan(), cell->colspan(), cell->align()); - int min_w = 20, min_h = 20; - cell->minimum_size(&min_w, &min_h); - f.write_c("%sif (cell) cell->minimum_size(%d, %d);\n", f.indent(), min_w, min_h); - } - } - super::write_code2(f); -} - -void Fl_Grid_Type::add_child(Fl_Type* a, Fl_Type* b) { - super::add_child(a, b); - Fl_Grid* grid = (Fl_Grid*)o; - grid->need_layout(1); - grid->redraw(); -} - -void Fl_Grid_Type::move_child(Fl_Type* a, Fl_Type* b) { - super::move_child(a, b); - Fl_Grid* grid = (Fl_Grid*)o; - grid->need_layout(1); - grid->redraw(); -} - -void Fl_Grid_Type::remove_child(Fl_Type* a) { - super::remove_child(a); - Fl_Grid* grid = (Fl_Grid*)o; - grid->need_layout(1); - grid->redraw(); -} - -/** Update the initial size of a child widget. - Fl_Grid keeps track of the size of children when they are first added. In - FLUID, users will want to resize children. So we need to trick Fl_Grid into - taking the new size as the initial size. - */ -void Fl_Grid_Type::child_resized(Fl_Widget_Type *child_type) { - Fl_Grid *grid = (Fl_Grid*)o; - Fl_Widget *child = child_type->o; - Fl_Grid::Cell *cell = grid->cell(child); - if (cell && ((cell->align()&FL_GRID_VERTICAL)==0)) { - int min_w = 0, min_h = 0; - cell->minimum_size(&min_w, &min_h); - cell->minimum_size(min_w, child->h()); - } - if (cell && ((cell->align()&FL_GRID_HORIZONTAL)==0)) { - int min_w = 0, min_h = 0; - cell->minimum_size(&min_w, &min_h); - cell->minimum_size(child->w(), min_h); - } - // TODO: if the user resizes an FL_GRID_FILL widget, should we change the alignment? -} - -/** Return the currently selected Grid widget if is a Grid Type. */ -Fl_Grid *Fl_Grid_Type::selected() { - if (current_widget && current_widget->is_a(ID_Grid)) - return ((Fl_Grid*)((Fl_Grid_Type*)current_widget)->o); - return NULL; -} - -/** - Insert a child widget into the cell at the x, y position inside the window. - /param[in] child - /param[in] x, y pixels from the top left of the window - */ -void Fl_Grid_Type::insert_child_at(Fl_Widget *child, int x, int y) { - Fl_Grid_Proxy *grid = (Fl_Grid_Proxy*)o; - int row = -1, col = -1, ml, mt, grg, gcg; - grid->margin(&ml, &mt, NULL, NULL); - grid->gap(&grg, &gcg); - int x0 = grid->x() + Fl::box_dx(grid->box()) + ml; - int y0 = grid->y() + Fl::box_dy(grid->box()) + mt; - - for (int r = 0; r < grid->rows(); r++) { - if (y>y0) row = r; - int gap = grid->row_gap(r)>=0 ? grid->row_gap(r) : grg; - y0 += grid->computed_row_height(r); - y0 += gap; - } - - for (int c = 0; c < grid->cols(); c++) { - if (x>x0) col = c; - int gap = grid->col_gap(c)>=0 ? grid->col_gap(c) : gcg; - x0 += grid->computed_col_width(c); - x0 += gap; - } - - grid->move_cell(child, row, col, 2); -} - -/** - Insert a child widget into the first new cell we can find . - - There are many other possible strategies. How about inserting to the right - of the last added child. Also, what happens if the grid is full? Should - we add a new row at the bottom? - - /param[in] child - */ -void Fl_Grid_Type::insert_child_at_next_free_cell(Fl_Widget *child) { - Fl_Grid_Proxy *grid = (Fl_Grid_Proxy*)o; - if (grid->cell(child)) return; -// The code below would insert the new widget after the last selected one, but -// unfortunately the current_widget is already invalid. -// if (current_widget && (current_widget->parent == this)) { -// Fl_Grid::Cell *current_cell = grid->any_cell(current_widget->o); -// if (current_cell) { -// r = current_cell->row(); -// c = current_cell->col(); -// } -// } - for (int r = 0; r < grid->rows(); r++) { - for (int c = 0; c < grid->cols(); c++) { - if (!grid->cell(r, c)) { - grid->move_cell(child, r, c); - return; - } - } - } - grid->layout(grid->rows() + 1, grid->cols()); - grid->move_cell(child, grid->rows() - 1, 0); -} - -/** Move cells around using the keyboard. - \note this fails if we have two children selected side by side and press 'right', - which will move the left child first, removing the right child from the - cell system. When trying to move the second child, it has no longer an - assigned row or column. - \param[in] child pointer to the child type - \param[in] key code of the last keypress when handling a FL_KEYBOARD event. - */ -void Fl_Grid_Type::keyboard_move_child(Fl_Widget_Type *child, int key) { - Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)o); - Fl_Grid::Cell *cell = grid->any_cell(child->o); - if (!cell) return; - if (key == FL_Right) { - grid->move_cell(child->o, cell->row(), cell->col()+1, 2); - } else if (key == FL_Left) { - grid->move_cell(child->o, cell->row(), cell->col()-1, 2); - } else if (key == FL_Up) { - grid->move_cell(child->o, cell->row()-1, cell->col(), 2); - } else if (key == FL_Down) { - grid->move_cell(child->o, cell->row()+1, cell->col(), 2); - } -} - -void Fl_Grid_Type::layout_widget() { - allow_layout++; - ((Fl_Grid*)o)->layout(); - allow_layout--; -} - - -// ---- Widget Panel Callbacks ---------------------------------------- MARK: - - -// TODO: better grid overlay? -// TODO: grid_child_cb should move all selected cells, not just the current_selected. -// TODO: buttons to add and delete rows and columns in the widget dialog -// TODO: ways to resize rows and columns, add and delete them in the project window, pulldown menu? -// TODO: alignment can be FL_GRID_LEFT|FL_GRID_VERTICAL? - -extern fld::widget::Formula_Input *widget_grid_row_input, *widget_grid_col_input, -*widget_grid_rowspan_input, *widget_grid_colspan_input; -extern Fl_Group *widget_tab_grid_child; - -void grid_child_cb(fld::widget::Formula_Input* i, void* v, int what) { - if ( !current_widget - || !current_widget->parent - || !current_widget->parent->is_a(ID_Grid)) - { - return; - } - Fl_Widget *child = ((Fl_Widget_Type*)current_widget)->o; - Fl_Grid_Proxy *g = ((Fl_Grid_Proxy*)((Fl_Widget_Type*)current_widget->parent)->o); - Fl_Grid::Cell *cell = g->any_cell(child); - if (v == LOAD) { - int v = -1; - if (cell) { - switch (what & 0x00ff) { - case 8: v = cell->row(); break; - case 9: v = cell->col(); break; - case 10: v = cell->rowspan(); break; - case 11: v = cell->colspan(); break; - case 12: cell->minimum_size(&v, NULL); break; - case 13: cell->minimum_size(NULL, &v); break; - } - } - i->value(v); - } else { - undo_checkpoint(); - int v2 = -2, old_v = -2, v = i->value(); - if (i==widget_grid_row_input) v2 = widget_grid_col_input->value(); - if (i==widget_grid_col_input) v2 = widget_grid_row_input->value(); - Fl_Grid::Cell *new_cell = NULL; - if (cell) { - switch (what & 0x00ff) { - case 8: old_v = cell->row(); v2 = cell->col(); break; - case 9: old_v = cell->col(); v2 = cell->row(); break; - case 10: old_v = cell->rowspan(); break; - case 11: old_v = cell->colspan(); break; - case 12: cell->minimum_size(&old_v, &v2); break; - case 13: cell->minimum_size(&v2, &old_v); break; - } - } - switch (what & 0xff00) { - case 0x0100: v--; break; - case 0x0200: v++; break; - } - if (old_v != v) { - switch (what & 0x00ff) { - case 8: - if (v2 == -1 && v >= 0) v2 = 0; - g->move_cell(current_widget->o, v, v2, 2); i->value(v); - break; - case 9: - if (v2 == -1 && v >= 0) v2 = 0; - g->move_cell(current_widget->o, v2, v, 2); i->value(v); - break; - case 10: if (cell && cell->row()+v<=g->rows() && v>0) cell->rowspan(v); - break; - case 11: if (cell && cell->col()+v<=g->cols() && v>0) cell->colspan(v); - break; - case 12: if (cell && v>=0) cell->minimum_size(v, v2); - break; - case 13: if (cell && v>=0) cell->minimum_size(v2, v); - break; - } - if (!cell && new_cell) - new_cell->minimum_size(20, 20); - g->need_layout(true); - set_modflag(1); - } - } -} -void grid_set_row_cb(fld::widget::Formula_Input* i, void* v) { - grid_child_cb(i, v, 8); - if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); -} -void grid_dec_row_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_row_input, v, 0x0100 + 8); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_inc_row_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_row_input, v, 0x0200 + 8); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_set_col_cb(fld::widget::Formula_Input* i, void* v) { - grid_child_cb(i, v, 9); - if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); -} -void grid_dec_col_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_col_input, v, 0x0100 + 9); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_inc_col_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_col_input, v, 0x0200 + 9); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_set_rowspan_cb(fld::widget::Formula_Input* i, void* v) { - grid_child_cb(i, v, 10); - if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); -} -void grid_dec_rowspan_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_rowspan_input, v, 0x0100 + 10); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_inc_rowspan_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_rowspan_input, v, 0x0200 + 10); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_set_colspan_cb(fld::widget::Formula_Input* i, void* v) { - grid_child_cb(i, v, 11); - if (v!=LOAD) widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); -} -void grid_dec_colspan_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_colspan_input, v, 0x0100 + 11); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_inc_colspan_cb(Fl_Button* i, void* v) { - if (v!=LOAD) { - grid_child_cb(widget_grid_colspan_input, v, 0x0200 + 11); - widget_tab_grid_child->do_callback(widget_tab_grid_child, LOAD); - } -} -void grid_set_min_wdt_cb(fld::widget::Formula_Input* i, void* v) { - grid_child_cb(i, v, 12); -} -void grid_set_min_hgt_cb(fld::widget::Formula_Input* i, void* v) { - grid_child_cb(i, v, 13); -} - -void grid_align_horizontal_cb(Fl_Choice* i, void* v) { - if ( !current_widget - || !current_widget->parent - || !current_widget->parent->is_a(ID_Grid)) - { - return; - } - int mask = (FL_GRID_LEFT | FL_GRID_RIGHT | FL_GRID_HORIZONTAL); - Fl_Grid *g = ((Fl_Grid*)((Fl_Widget_Type*)current_widget->parent)->o); - if (v == LOAD) { - int a = FL_GRID_FILL & mask; - Fl_Grid::Cell *cell = g->cell(current_widget->o); - if (cell) { - a = cell->align() & mask; - } - const Fl_Menu_Item *mi = i->find_item_with_argument(a); - if (mi) i->value(mi); - } else { - undo_checkpoint(); - int v = FL_GRID_FILL & mask, old_v = FL_GRID_FILL & mask; - const Fl_Menu_Item *mi = i->mvalue(); - if (mi) v = (int)mi->argument(); - Fl_Grid::Cell *cell = g->cell(current_widget->o); - if (cell) { - old_v = cell->align() & mask; - if (old_v != v) { - cell->align((Fl_Grid_Align)(v | (cell->align() & ~mask))); - g->need_layout(true); - g->redraw(); - set_modflag(1); - } - } - } -} - -void grid_align_vertical_cb(Fl_Choice* i, void* v) { - if ( !current_widget - || !current_widget->parent - || !current_widget->parent->is_a(ID_Grid)) - { - return; - } - int mask = (FL_GRID_TOP | FL_GRID_BOTTOM | FL_GRID_VERTICAL); - Fl_Grid *g = ((Fl_Grid*)((Fl_Widget_Type*)current_widget->parent)->o); - if (v == LOAD) { - int a = FL_GRID_FILL & mask; - Fl_Grid::Cell *cell = g->cell(current_widget->o); - if (cell) { - a = cell->align() & mask; - } - const Fl_Menu_Item *mi = i->find_item_with_argument(a); - if (mi) i->value(mi); - } else { - undo_checkpoint(); - int v = FL_GRID_FILL & mask, old_v = FL_GRID_FILL & mask; - const Fl_Menu_Item *mi = i->mvalue(); - if (mi) v = (int)mi->argument(); - Fl_Grid::Cell *cell = g->cell(current_widget->o); - if (cell) { - old_v = cell->align() & mask; - if (old_v != v) { - cell->align((Fl_Grid_Align)(v | (cell->align() & ~mask))); - g->need_layout(true); - g->redraw(); - set_modflag(1); - } - } - } -} - diff --git a/fluid/nodes/Fl_Grid_Type.h b/fluid/nodes/Fl_Grid_Type.h deleted file mode 100644 index 1ce5321c8..000000000 --- a/fluid/nodes/Fl_Grid_Type.h +++ /dev/null @@ -1,82 +0,0 @@ -// -// Fl_Grid type header file for the Fast Light Tool Kit (FLTK). -// -// Copyright 2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef _FLUID_FL_GRID_TYPE_H -#define _FLUID_FL_GRID_TYPE_H - -#include "nodes/Fl_Group_Type.h" -#include - -// ---- Fl_Grid_Type --------------------------------------------------- MARK: - - -extern const char grid_type_name[]; - -class Fl_Grid_Proxy : public Fl_Grid { -protected: - typedef struct { Fl_Widget *widget; Cell *cell; } Cell_Widget_Pair; - Cell_Widget_Pair *transient_; - int num_transient_; - int cap_transient_; - void transient_make_room_(int n); - void transient_remove_(Fl_Widget *w); -public: - Fl_Grid_Proxy(int X,int Y,int W,int H); - ~Fl_Grid_Proxy(); - void resize(int,int,int,int) FL_OVERRIDE; - void draw() FL_OVERRIDE; - void draw_overlay(); - void move_cell(Fl_Widget *child, int to_row, int to_col, int how = 0); - Cell* any_cell(Fl_Widget *widget) const; - Cell* transient_cell(Fl_Widget *widget) const; - Cell* transient_widget(Fl_Widget *wi, int row, int col, int row_span, int col_span, Fl_Grid_Align align = FL_GRID_FILL); - Cell* widget(Fl_Widget *wi, int row, int col, Fl_Grid_Align align = FL_GRID_FILL); - Cell* widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align = FL_GRID_FILL); -}; - -class Fl_Grid_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; -public: - Fl_Grid_Type(); - const char *type_name() FL_OVERRIDE {return grid_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::GridGroup";} - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Grid_Type(); } - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Grid; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Grid) ? true : super::is_a(inID); } - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - void write_parent_properties(fld::io::Project_Writer &f, Fl_Type *child, bool encapsulate) FL_OVERRIDE; - void read_parent_property(fld::io::Project_Reader &f, Fl_Type *child, const char *property) FL_OVERRIDE; - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void leave_live_mode() FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; - void copy_properties_for_children() FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void remove_child(Fl_Type*) FL_OVERRIDE; - void layout_widget() FL_OVERRIDE; - void child_resized(Fl_Widget_Type *child); - void insert_child_at(Fl_Widget *child, int x, int y); - void insert_child_at_next_free_cell(Fl_Widget *child); - void keyboard_move_child(Fl_Widget_Type*, int key); - - static class Fl_Grid *selected(); -}; - -#endif // _FLUID_FL_GRID_TYPE_H diff --git a/fluid/nodes/Fl_Group_Type.cxx b/fluid/nodes/Fl_Group_Type.cxx deleted file mode 100644 index 5d3cba07c..000000000 --- a/fluid/nodes/Fl_Group_Type.cxx +++ /dev/null @@ -1,849 +0,0 @@ -// -// Fl_Group object code for the Fast Light Tool Kit (FLTK). -// -// Object describing an Fl_Group and links to Fl_Window_Type.C and -// the Fl_Tabs widget, with special stuff to select tab items and -// insure that only one is visible. -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "nodes/Fl_Group_Type.h" - -#include "app/fluid.h" -#include "app/undo.h" -#include "app/Fd_Snap_Action.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "widgets/Node_Browser.h" - -#include -#include -#include -#include -#include -#include -#include "../src/flstring.h" - -#include -#include - - -// ---- Fl_Group_Type -------------------------------------------------- MARK: - - -Fl_Group_Type Fl_Group_type; // the "factory" - -/** - Override group's resize behavior to do nothing to children by default. - \param[in] X, Y, W, H new size - */ -void Fl_Group_Proxy::resize(int X, int Y, int W, int H) { - if (Fl_Type::allow_layout > 0) { - Fl_Group::resize(X, Y, W, H); - } else { - Fl_Widget::resize(X, Y, W, H); - } - redraw(); -} - -/** - Override draw() to make groups with no box or flat box background visible. - */ -void Fl_Group_Proxy::draw() { - if (show_ghosted_outline && (box() == FL_NO_BOX)) { - fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); - } - Fl_Group::draw(); -} - - -/** - \brief Enlarge the group size, so all children fit within. - */ -void fix_group_size(Fl_Type *tt) { - if (!tt || !tt->is_a(ID_Group)) return; - Fl_Group_Type* t = (Fl_Group_Type*)tt; - int X = t->o->x(); - int Y = t->o->y(); - int R = X+t->o->w(); - int B = Y+t->o->h(); - for (Fl_Type *nn = t->next; nn && nn->level > t->level; nn = nn->next) { - if (nn->is_true_widget()) { - Fl_Widget_Type* n = (Fl_Widget_Type*)nn; - int x = n->o->x(); if (x < X) X = x; - int y = n->o->y(); if (y < Y) Y = y; - int r = x+n->o->w();if (r > R) R = r; - int b = y+n->o->h();if (b > B) B = b; - } - } - t->o->resize(X,Y,R-X,B-Y); -} - -extern void group_selected_menuitems(); - -void group_cb(Fl_Widget *, void *) { - if (!Fl_Type::current) { - fl_message("No widgets selected."); - return; - } - if (!Fl_Type::current->is_widget()) { - fl_message("Only widgets and menu items can be grouped."); - return; - } - if (Fl_Type::current->is_a(ID_Menu_Item)) { - group_selected_menuitems(); - return; - } - // The group will be created in the parent group of the current widget - Fl_Type *qq = Fl_Type::current->parent; - Fl_Widget_Type *q = static_cast(Fl_Type::current); - while (qq && !qq->is_a(ID_Group)) { - qq = qq->parent; - } - if (!qq) { - fl_message("Can't create a new group here."); - return; - } - undo_checkpoint(); - undo_suspend(); - Fl_Type::current = qq; - Fl_Group_Type *n = (Fl_Group_Type*)(Fl_Group_type.make(Strategy::AS_LAST_CHILD)); - n->move_before(q); - n->o->resize(q->o->x(),q->o->y(),q->o->w(),q->o->h()); - for (Fl_Type *t = qq->next; t && (t->level > qq->level);) { - if (t->level != n->level || t == n || !t->selected) { - t = t->next; - continue; - } - Fl_Type *nxt = t->remove(); - t->add(n, Strategy::AS_LAST_CHILD); - t = nxt; - } - fix_group_size(n); - Fl_Type::current = q; - n->layout_widget(); - widget_browser->rebuild(); - undo_resume(); - set_modflag(1); -} - -extern void ungroup_selected_menuitems(); - -void ungroup_cb(Fl_Widget *, void *) { - if (!Fl_Type::current) { - fl_message("No widgets selected."); - return; - } - if (!Fl_Type::current->is_widget()) { - fl_message("Only widgets and menu items can be ungrouped."); - return; - } - if (Fl_Type::current->is_a(ID_Menu_Item)) { - ungroup_selected_menuitems(); - return; - } - - Fl_Widget_Type *q = static_cast(Fl_Type::current); - int q_level = q->level; - Fl_Type *qq = Fl_Type::current->parent; - while (qq && !qq->is_true_widget()) qq = qq->parent; - if (!qq || !qq->is_a(ID_Group)) { - fl_message("Only menu widgets inside a group can be ungrouped."); - return; - } - undo_checkpoint(); - undo_suspend(); - Fl_Type::current = qq; - for (Fl_Type *t = qq->next; t && (t->level > qq->level);) { - if (t->level != q_level || !t->selected) { - t = t->next; - continue; - } - Fl_Type *nxt = t->remove(); - t->insert(qq); - t = nxt; - } - if (!qq->next || (qq->next->level <= qq->level)) { - qq->remove(); - delete qq; // qq has no children that need to be delete - } - Fl_Type::current = q; - widget_browser->rebuild(); - undo_resume(); - set_modflag(1); -} - -void Fl_Group_Type::ideal_size(int &w, int &h) { - if (parent && parent->is_true_widget()) { - Fl_Widget *p = ((Fl_Widget_Type*)parent)->o; - w = p->w() / 2; - h = p->h() / 2; - } else { - w = 140; - h = 140; - } - Fd_Snap_Action::better_size(w, h); -} - -void Fl_Group_Type::write_code1(fld::io::Code_Writer& f) { - Fl_Widget_Type::write_code1(f); -} - -void Fl_Group_Type::write_code2(fld::io::Code_Writer& f) { - const char *var = name() ? name() : "o"; - write_extra_code(f); - f.write_c("%s%s->end();\n", f.indent(), var); - if (resizable()) { - f.write_c("%sFl_Group::current()->resizable(%s);\n", f.indent(), var); - } - write_block_close(f); -} - -// This is called when o is created. If it is in the tab group make -// sure it is visible: -void Fl_Group_Type::add_child(Fl_Type* cc, Fl_Type* before) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - Fl_Widget* b = before ? ((Fl_Widget_Type*)before)->o : 0; - ((Fl_Group*)o)->insert(*(c->o), b); - o->redraw(); -} - -// This is called when o is deleted. If it is in the tab group make -// sure it is not visible: -void Fl_Group_Type::remove_child(Fl_Type* cc) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - ((Fl_Group*)o)->remove(c->o); - o->redraw(); -} - -// move, don't change selected value: -void Fl_Group_Type::move_child(Fl_Type* cc, Fl_Type* before) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - Fl_Widget* b = before ? ((Fl_Widget_Type*)before)->o : 0; - ((Fl_Group*)o)->insert(*(c->o), b); - o->redraw(); -} - -// live mode support -Fl_Widget* Fl_Group_Type::enter_live_mode(int) { - Fl_Group *grp = new Fl_Group(o->x(), o->y(), o->w(), o->h()); - return propagate_live_mode(grp); -} - -void Fl_Group_Type::leave_live_mode() { -} - -/** - copy all properties from the edit widget to the live widget - */ -void Fl_Group_Type::copy_properties() { - Fl_Widget_Type::copy_properties(); -} - -// ---- Fl_Pack_Type --------------------------------------------------- MARK: - - -Fl_Pack_Type Fl_Pack_type; // the "factory" - -const char pack_type_name[] = "Fl_Pack"; - -Fl_Menu_Item pack_type_menu[] = { - {"HORIZONTAL", 0, 0, (void*)Fl_Pack::HORIZONTAL}, - {"VERTICAL", 0, 0, (void*)Fl_Pack::VERTICAL}, - {0} -}; - -Fl_Widget *Fl_Pack_Type::enter_live_mode(int) { - Fl_Group *grp = new Fl_Pack(o->x(), o->y(), o->w(), o->h()); - return propagate_live_mode(grp); -} - -void Fl_Pack_Type::copy_properties() -{ - Fl_Group_Type::copy_properties(); - Fl_Pack *d = (Fl_Pack*)live_widget, *s =(Fl_Pack*)o; - d->spacing(s->spacing()); -} - -// ---- Fl_Flex_Type --------------------------------------------------- MARK: - - -const char flex_type_name[] = "Fl_Flex"; - -Fl_Menu_Item flex_type_menu[] = { - {"HORIZONTAL", 0, 0, (void*)Fl_Flex::HORIZONTAL}, - {"VERTICAL", 0, 0, (void*)Fl_Flex::VERTICAL}, - {0}}; - -Fl_Flex_Type Fl_Flex_type; // the "factory" - -/** - Override flex's resize behavior to do nothing to children by default. - - \param[in] X, Y, W, H new size - */ -void Fl_Flex_Proxy::resize(int X, int Y, int W, int H) { - if (Fl_Type::allow_layout > 0) { - Fl_Flex::resize(X, Y, W, H); - } else { - Fl_Widget::resize(X, Y, W, H); - } - redraw(); -} - -/** - Override draw() to make groups with no box or flat box background visible. - */ -void Fl_Flex_Proxy::draw() { - if (show_ghosted_outline && (box() == FL_NO_BOX)) { - fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); - } - Fl_Flex::draw(); -} - -Fl_Widget *Fl_Flex_Type::enter_live_mode(int) { - Fl_Flex *grp = new Fl_Flex(o->x(), o->y(), o->w(), o->h()); - propagate_live_mode(grp); - Fl_Flex *d = grp, *s =(Fl_Flex*)o; - int nc = s->children(), nd = d->children(); - if (nc>nd) nc = nd; - for (int i=0; ifixed(s->child(i))) { - Fl_Widget *dc = d->child(i); - d->fixed(d->child(i), s->horizontal() ? dc->w() : dc->h()); - } - } - return grp; -} - -void Fl_Flex_Type::copy_properties() -{ - Fl_Group_Type::copy_properties(); - Fl_Flex *d = (Fl_Flex*)live_widget, *s =(Fl_Flex*)o; - int lm, tm, rm, bm; - s->margin(&lm, &tm, &rm, &bm); - d->margin(lm, tm, rm, bm); - d->gap( s->gap() ); -} - -void Fl_Flex_Type::copy_properties_for_children() { - Fl_Flex *d = (Fl_Flex*)live_widget, *s =(Fl_Flex*)o; - for (int i=0; ichildren(); i++) { - if (s->fixed(s->child(i)) && ichildren()) { - if (s->horizontal()) { - d->fixed(d->child(i), d->child(i)->w()); - } else { - d->fixed(d->child(i), d->child(i)->h()); - } - } - } - d->layout(); -} - -void Fl_Flex_Type::write_properties(fld::io::Project_Writer &f) -{ - Fl_Group_Type::write_properties(f); - Fl_Flex* flex = (Fl_Flex*)o; - int lm, tm, rm, bm; - flex->margin(&lm, &tm, &rm, &bm); - if (lm!=0 || tm!=0 || rm!=0 || bm!=0) - f.write_string("margin {%d %d %d %d}", lm, tm, rm, bm); - if (flex->gap()) - f.write_string("gap %d", flex->gap()); - int nSet = 0; - for (int i=0; ichildren(); i++) - if (flex->fixed(flex->child(i))) - nSet++; - if (nSet) { - f.write_string("fixed_size_tuples {%d", nSet); - for (int i=0; ichildren(); i++) { - Fl_Widget *ci = flex->child(i); - if (flex->fixed(ci)) - f.write_string(" %d %d", i, flex->horizontal() ? ci->w() : ci->h()); - } - f.write_string("}"); - } -} - -void Fl_Flex_Type::read_property(fld::io::Project_Reader &f, const char *c) -{ - Fl_Flex* flex = (Fl_Flex*)o; - suspend_auto_layout = 1; - if (!strcmp(c,"margin")) { - int lm, tm, rm, bm; - if (sscanf(f.read_word(),"%d %d %d %d",&lm,&tm,&rm,&bm) == 4) - flex->margin(lm, tm, rm, bm); - } else if (!strcmp(c,"gap")) { - int g; - if (sscanf(f.read_word(),"%d",&g)) - flex->gap(g); - } else if (!strcmp(c,"fixed_size_tuples")) { - f.read_word(1); // must be '{' - const char *nStr = f.read_word(1); // number of indices in table - fixedSizeTupleSize = atoi(nStr); - fixedSizeTuple = new int[fixedSizeTupleSize*2]; - for (int i=0; i0) { - for (int i=0; i=0 && ixchildren()) { - Fl_Widget *ci = flex->child(ix); - flex->fixed(ci, size); - } - } - fixedSizeTupleSize = 0; - delete[] fixedSizeTuple; - fixedSizeTuple = NULL; - } - suspend_auto_layout = 0; -} - -void Fl_Flex_Type::write_code2(fld::io::Code_Writer& f) { - const char *var = name() ? name() : "o"; - Fl_Flex* flex = (Fl_Flex*)o; - int lm, tm, rm, bm; - flex->margin(&lm, &tm, &rm, &bm); - if (lm!=0 || tm!=0 || rm!=0 || bm!=0) - f.write_c("%s%s->margin(%d, %d, %d, %d);\n", f.indent(), var, lm, tm, rm, bm); - if (flex->gap()) - f.write_c("%s%s->gap(%d);\n", f.indent(), var, flex->gap()); - for (int i=0; ichildren(); ++i) { - Fl_Widget *ci = flex->child(i); - if (flex->fixed(ci)) - f.write_c("%s%s->fixed(%s->child(%d), %d);\n", f.indent(), var, var, i, - flex->horizontal() ? ci->w() : ci->h()); - } - Fl_Group_Type::write_code2(f); -} - -//void Fl_Flex_Type::add_child(Fl_Type* a, Fl_Type* b) { -// Fl_Group_Type::add_child(a, b); -// if (!suspend_auto_layout) -// ((Fl_Flex*)o)->layout(); -//} -// -//void Fl_Flex_Type::move_child(Fl_Type* a, Fl_Type* b) { -// Fl_Group_Type::move_child(a, b); -// if (!suspend_auto_layout) -// ((Fl_Flex*)o)->layout(); -//} - -void Fl_Flex_Type::remove_child(Fl_Type* a) { - if (a->is_widget()) - ((Fl_Flex*)o)->fixed(((Fl_Widget_Type*)a)->o, 0); - Fl_Group_Type::remove_child(a); -// ((Fl_Flex*)o)->layout(); - layout_widget(); -} - -void Fl_Flex_Type::layout_widget() { - allow_layout++; - ((Fl_Flex*)o)->layout(); - allow_layout--; -} - -// Change from HORIZONTAL to VERTICAL or back. -// Children in a horizontal Flex have already the full vertical height. If we -// just change to vertical, the accumulated hight of all children is too big. -// We need to relayout existing children. -void Fl_Flex_Type::change_subtype_to(int n) { - Fl_Flex* f = (Fl_Flex*)o; - if (f->type()==n) return; - - int nc = f->children(); - if (nc > 0) { - int dw = Fl::box_dw(f->box()); - int dh = Fl::box_dh(f->box()); - int lm, tm, rm, bm; - f->margin(&lm, &tm, &rm, &bm); - int gap = f->gap(); - int fw = f->w()-dw-lm-rm-(nc*gap); - if (fw<=nc) fw = nc; // avoid division by zero - int fh = f->h()-dh-tm-bm-(nc*gap); - if (fh<=nc) fh = nc; // avoid division by zero - - if (f->type()==Fl_Flex::HORIZONTAL && n==Fl_Flex::VERTICAL) { - float scl = (float)fh/(float)fw; - for (int i=0; ichild(i); - c->size(f->w(), (int)(c->w()*scl)); - } - } else if (f->type()==Fl_Flex::VERTICAL && n==Fl_Flex::HORIZONTAL) { - float scl = (float)fw/(float)fh; - for (int i=0; ichild(i); - c->size((int)(c->h()*scl), f->h()); - } - } - } - f->type(n); - f->layout(); -} - -int Fl_Flex_Type::parent_is_flex(Fl_Type *t) { - return (t->is_widget() - && t->parent - && t->parent->is_a(ID_Flex)); -} - -/** - Insert a widget in the child list so that it moves as close as possible the position. - - \param[in] child any widget in the tree but this, may already be a child of - this and will be relocated if so - \param[in] x, y pixel coordinates relative to the top left of the window - */ -void Fl_Flex_Type::insert_child_at(Fl_Widget *child, int x, int y) { - Fl_Flex *flex = (Fl_Flex*)o; - // find the insertion point closest to x, y - int d = flex->w() + flex->h(), di = -1; - if (flex->horizontal()) { - int i, dx; - for (i=0; ichildren(); i++) { - dx = x - flex->child(i)->x(); - if (dx < 0) dx = -dx; - if (dx < d) { d = dx; di = i; } - } - dx = x - (flex->x()+flex->w()); - if (dx < 0) dx = -dx; - if (dx < d) { d = dx; di = i; } - } else { - int i, dy; - for (i=0; ichildren(); i++) { - dy = y - flex->child(i)->y(); - if (dy < 0) dy = -dy; - if (dy < d) { d = dy; di = i; } - } - dy = y - (flex->y()+flex->h()); - if (dy < 0) dy = -dy; - if (dy < d) { d = dy; di = i; } - } - if (di > -1) { - flex->insert(*child, di); - } -} - -/** Move children around using the keyboard. - \param[in] child pointer to the child type - \param[in] key code of the last keypress when handling a FL_KEYBOARD event. - */ -void Fl_Flex_Type::keyboard_move_child(Fl_Widget_Type *child, int key) { - Fl_Flex *flex = ((Fl_Flex*)o); - int ix = flex->find(child->o); - if (ix == flex->children()) return; - if (flex->horizontal()) { - if (key==FL_Right) { - flex->insert(*child->o, ix+2); - } else if (key==FL_Left) { - if (ix > 0) flex->insert(*child->o, ix-1); - } - } else { - if (key==FL_Down) { - flex->insert(*child->o, ix+2); - } else if (key==FL_Up) { - if (ix > 0) flex->insert(*child->o, ix-1); - } - } -} - -int Fl_Flex_Type::size(Fl_Type *t, char fixed_only) { - if (!t->is_widget()) return 0; - if (!t->parent) return 0; - if (!t->parent->is_a(ID_Flex)) return 0; - Fl_Flex_Type* ft = (Fl_Flex_Type*)t->parent; - Fl_Flex* f = (Fl_Flex*)ft->o; - Fl_Widget *w = ((Fl_Widget_Type*)t)->o; - if (fixed_only && !f->fixed(w)) return 0; - return f->horizontal() ? w->w() : w->h(); -} - -int Fl_Flex_Type::is_fixed(Fl_Type *t) { - if (!t->is_widget()) return 0; - if (!t->parent) return 0; - if (!t->parent->is_a(ID_Flex)) return 0; - Fl_Flex_Type* ft = (Fl_Flex_Type*)t->parent; - Fl_Flex* f = (Fl_Flex*)ft->o; - Fl_Widget *w = ((Fl_Widget_Type*)t)->o; - return f->fixed(w); -} - -// ---- Fl_Table_Type -------------------------------------------------- MARK: - - -Fl_Table_Type Fl_Table_type; // the "factory" - -static const int MAX_ROWS = 14; -static const int MAX_COLS = 7; - -// this is a minimal table widget used as an example when adding tables in Fluid -class Fluid_Table : public Fl_Table { - int data[MAX_ROWS][MAX_COLS]; // data array for cells - - // Draw the row/col headings - // Make this a dark thin upbox with the text inside. - // - void DrawHeader(const char *s, int X, int Y, int W, int H) { - fl_push_clip(X,Y,W,H); - fl_draw_box(FL_THIN_UP_BOX, X,Y,W,H, row_header_color()); - fl_color(FL_BLACK); - fl_draw(s, X,Y,W,H, FL_ALIGN_CENTER); - fl_pop_clip(); - } - // Draw the cell data - // Dark gray text on white background with subtle border - // - void DrawData(const char *s, int X, int Y, int W, int H) { - fl_push_clip(X,Y,W,H); - // Draw cell bg - fl_color(FL_WHITE); fl_rectf(X,Y,W,H); - // Draw cell data - fl_color(FL_GRAY0); fl_draw(s, X,Y,W,H, FL_ALIGN_CENTER); - // Draw box border - fl_color(color()); fl_rect(X,Y,W,H); - fl_pop_clip(); - } - // Handle drawing table's cells - // Fl_Table calls this function to draw each visible cell in the table. - // It's up to us to use FLTK's drawing functions to draw the cells the way we want. - // - void draw_cell(TableContext context, int ROW=0, int COL=0, int X=0, int Y=0, int W=0, int H=0) FL_OVERRIDE { - static char s[40]; - switch ( context ) { - case CONTEXT_STARTPAGE: // before page is drawn.. - fl_font(FL_HELVETICA, 16); // set the font for our drawing operations - return; - case CONTEXT_COL_HEADER: // Draw column headers - sprintf(s,"%c",'A'+COL); // "A", "B", "C", etc. - DrawHeader(s,X,Y,W,H); - return; - case CONTEXT_ROW_HEADER: // Draw row headers - sprintf(s,"%03d:",ROW); // "001:", "002:", etc - DrawHeader(s,X,Y,W,H); - return; - case CONTEXT_CELL: // Draw data in cells - sprintf(s,"%d",data[ROW][COL]); - DrawData(s,X,Y,W,H); - return; - default: - return; - } - } -public: - Fluid_Table(int x, int y, int w, int h, const char *l=0L) - : Fl_Table(x, y, w, h, l) { - end(); - for ( int r=0; ro : 0; - if (((Fl_Table*)o)->children()==1) { // the FLuid_Table has one extra child - fl_message("Inserting child widgets into an Fl_Table is not recommended.\n" - "Please refer to the documentation on Fl_Table."); - } - ((Fl_Table*)o)->insert(*(c->o), b); - o->redraw(); -} - -void Fl_Table_Type::remove_child(Fl_Type* cc) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - ((Fl_Table*)o)->remove(*(c->o)); - o->redraw(); -} - -void Fl_Table_Type::move_child(Fl_Type* cc, Fl_Type* before) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - Fl_Widget* b = before ? ((Fl_Widget_Type*)before)->o : 0; - ((Fl_Table*)o)->insert(*(c->o), b); - o->redraw(); -} - -Fl_Widget *Fl_Table_Type::enter_live_mode(int) { - Fl_Group *grp = new Fluid_Table(o->x(), o->y(), o->w(), o->h()); - live_widget = grp; - copy_properties(); - grp->end(); - return live_widget; -} - -void Fl_Table_Type::ideal_size(int &w, int &h) { - w = 160; - h = 120; - Fd_Snap_Action::better_size(w, h); -} - -// ---- Fl_Tabs_Type --------------------------------------------------- MARK: - - -Fl_Tabs_Type Fl_Tabs_type; // the "factory" - -const char tabs_type_name[] = "Fl_Tabs"; - -// Override group's resize behavior to do nothing to children: -void Fl_Tabs_Proxy::resize(int X, int Y, int W, int H) { - if (Fl_Type::allow_layout > 0) { - Fl_Tabs::resize(X, Y, W, H); - } else { - Fl_Widget::resize(X, Y, W, H); - } - redraw(); -} - -/** - Override draw() to make groups with no box or flat box background visible. - */ -void Fl_Tabs_Proxy::draw() { - if (show_ghosted_outline && (box() == FL_NO_BOX)) { - fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); - } - Fl_Tabs::draw(); -} - -// This is called when user clicks on a widget in the window. See -// if it is a tab title, and adjust visibility and return new selection: -// If none, return o unchanged: - -Fl_Type* Fl_Tabs_Type::click_test(int x, int y) { - Fl_Tabs *t = (Fl_Tabs*)o; - Fl_Widget *a = t->which(x,y); - if (!a) return 0; // didn't click on tab - // changing the visible tab has an impact on the generated - // source code, so mark this project as changed. - int changed = (a!=t->value()); - // okay, run the tabs ui until they let go of mouse: - t->handle(FL_PUSH); - Fl::pushed(t); - while (Fl::pushed()==t) Fl::wait(); - if (changed) set_modflag(1); - return (Fl_Type*)(t->value()->user_data()); -} - -void Fl_Tabs_Type::add_child(Fl_Type* c, Fl_Type* before) { - Fl_Group_Type::add_child(c, before); -} - -void Fl_Tabs_Type::remove_child(Fl_Type* cc) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - Fl_Tabs *t = (Fl_Tabs*)o; - if (t->value() == c->o) t->value(0); - Fl_Group_Type::remove_child(c); -} - -Fl_Widget *Fl_Tabs_Type::enter_live_mode(int) { - Fl_Tabs *original = static_cast(o); - Fl_Tabs *clone = new Fl_Tabs(o->x(), o->y(), o->w(), o->h()); - propagate_live_mode(clone); - int tab_index = original->find(original->value()); - if ((tab_index>=0) && (tab_indexchildren())) - clone->value(clone->child(tab_index)); - return clone; -} - -// ---- Fl_Scroll_Type ------------------------------------------------- MARK: - - -Fl_Scroll_Type Fl_Scroll_type; // the "factory" - -const char scroll_type_name[] = "Fl_Scroll"; - -Fl_Menu_Item scroll_type_menu[] = { - {"BOTH", 0, 0, 0/*(void*)Fl_Scroll::BOTH*/}, - {"HORIZONTAL", 0, 0, (void*)Fl_Scroll::HORIZONTAL}, - {"VERTICAL", 0, 0, (void*)Fl_Scroll::VERTICAL}, - {"HORIZONTAL_ALWAYS", 0, 0, (void*)Fl_Scroll::HORIZONTAL_ALWAYS}, - {"VERTICAL_ALWAYS", 0, 0, (void*)Fl_Scroll::VERTICAL_ALWAYS}, - {"BOTH_ALWAYS", 0, 0, (void*)Fl_Scroll::BOTH_ALWAYS}, - {0}}; - -Fl_Widget *Fl_Scroll_Type::enter_live_mode(int) { - Fl_Group *grp = new Fl_Scroll(o->x(), o->y(), o->w(), o->h()); - grp->show(); - return propagate_live_mode(grp); -} - -void Fl_Scroll_Type::copy_properties() { - Fl_Group_Type::copy_properties(); - Fl_Scroll *s = (Fl_Scroll*)o, *d = (Fl_Scroll*)live_widget; - d->scroll_to(s->xposition(), s->yposition()); - d->type(s->type()); - d->scrollbar.align(s->scrollbar.align()); - d->hscrollbar.align(s->hscrollbar.align()); -} - -// ---- Fl_Tile_Type --------------------------------------------------- MARK: - - -Fl_Tile_Type Fl_Tile_type; // the "factory" - -const char tile_type_name[] = "Fl_Tile"; - -void Fl_Tile_Type::copy_properties() { - Fl_Group_Type::copy_properties(); - // no additional properties -} - -// ---- Fl_Wizard_Type ------------------------------------------------ MARK: - - -Fl_Wizard_Type Fl_Wizard_type; // the "factory" - -const char wizard_type_name[] = "Fl_Wizard"; - -// Override group's resize behavior to do nothing to children: -void Fl_Wizard_Proxy::resize(int X, int Y, int W, int H) { - if (Fl_Type::allow_layout > 0) { - Fl_Wizard::resize(X, Y, W, H); - } else { - Fl_Widget::resize(X, Y, W, H); - } - redraw(); -} - -/** - Override draw() to make groups with no box or flat box background visible. - */ -void Fl_Wizard_Proxy::draw() { - if (show_ghosted_outline && (box() == FL_NO_BOX)) { - fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); - } - Fl_Wizard::draw(); -} - diff --git a/fluid/nodes/Fl_Group_Type.h b/fluid/nodes/Fl_Group_Type.h deleted file mode 100644 index 30fab1d20..000000000 --- a/fluid/nodes/Fl_Group_Type.h +++ /dev/null @@ -1,242 +0,0 @@ -// -// Group type header file for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef _FLUID_FL_GROUP_TYPE_H -#define _FLUID_FL_GROUP_TYPE_H - -#include "nodes/Fl_Widget_Type.h" - -#include -#include -#include -#include - -void group_cb(Fl_Widget *, void *); -void ungroup_cb(Fl_Widget *, void *); - -// ---- Fl_Group_Type -------------------------------------------------- MARK: - - -/** - Proxy group to use in place of Fl_Group in the interactive window. - - In an interactive environment, groups should not automatically resize their - children. This proxy disables the layout of children by default. Children - layout propagation may be enable temporarily by incrementing `allow_layout` - before resizing and decrementing it again afterwards. - */ -class Fl_Group_Proxy : public Fl_Group { -public: - Fl_Group_Proxy(int X,int Y,int W,int H) : Fl_Group(X, Y, W, H) { Fl_Group::current(0); } - void resize(int x, int y, int w, int h) FL_OVERRIDE; - void draw() FL_OVERRIDE; -}; - -class Fl_Group_Type : public Fl_Widget_Type -{ - typedef Fl_Widget_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "Fl_Group";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::Group";} - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - Fl_Group_Proxy *g = new Fl_Group_Proxy(X,Y,W,H); Fl_Group::current(0); return g;} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Group_Type();} - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void remove_child(Fl_Type*) FL_OVERRIDE; - int can_have_children() const FL_OVERRIDE {return 1;} - ID id() const FL_OVERRIDE { return ID_Group; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Group) ? true : super::is_a(inID); } - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void leave_live_mode() FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; -}; - -// ---- Fl_Pack_Type --------------------------------------------------- MARK: - - -extern const char pack_type_name[]; -extern Fl_Menu_Item pack_type_menu[]; - -class Fl_Pack_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE {return pack_type_menu;} -public: - const char *type_name() FL_OVERRIDE {return pack_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::PackedGroup";} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Pack_Type();} - ID id() const FL_OVERRIDE { return ID_Pack; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Pack) ? true : super::is_a(inID); } - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; -}; - -// ---- Fl_Flex_Type --------------------------------------------------- MARK: - - -extern const char flex_type_name[]; -extern Fl_Menu_Item flex_type_menu[]; - -class Fl_Flex_Proxy : public Fl_Flex { -public: - Fl_Flex_Proxy(int X,int Y,int W,int H) : Fl_Flex(X, Y, W, H) { Fl_Group::current(0); } - void resize(int x, int y, int w, int h) FL_OVERRIDE; - void draw() FL_OVERRIDE; -}; - -class Fl_Flex_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE {return flex_type_menu;} - int fixedSizeTupleSize; /* number of pairs in array */ - int *fixedSizeTuple; /* [ index, size, index2, size2, ... ] */ - int suspend_auto_layout; -public: - Fl_Flex_Type() : fixedSizeTupleSize(0), fixedSizeTuple(NULL), suspend_auto_layout(0) { } - const char *type_name() FL_OVERRIDE {return flex_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::FlexGroup";} - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Flex_Type(); } - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - Fl_Flex *g = new Fl_Flex_Proxy(X,Y,W,H); Fl_Group::current(0); return g;} - ID id() const FL_OVERRIDE { return ID_Flex; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Flex) ? true : super::is_a(inID); } - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; - void copy_properties_for_children() FL_OVERRIDE; - void postprocess_read() FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; -// void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; -// void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void remove_child(Fl_Type*) FL_OVERRIDE; - void layout_widget() FL_OVERRIDE; - void change_subtype_to(int n); - void insert_child_at(Fl_Widget *child, int x, int y); - void keyboard_move_child(Fl_Widget_Type*, int key); - static int parent_is_flex(Fl_Type*); - static int size(Fl_Type*, char fixed_only=0); - static int is_fixed(Fl_Type*); -}; - -// ---- Fl_Table_Type -------------------------------------------------- MARK: - - -class Fl_Table_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE; - const char *type_name() FL_OVERRIDE { return "Fl_Table"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::TableGroup"; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Table_Type(); } - Fl_Widget *widget(int X, int Y, int W, int H) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Table; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Table) ? true : super::is_a(inID); } - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void remove_child(Fl_Type*) FL_OVERRIDE; -}; - -// ---- Fl_Tabs_Type --------------------------------------------------- MARK: - - -extern const char tabs_type_name[]; - -class Fl_Tabs_Proxy : public Fl_Tabs { -public: - Fl_Tabs_Proxy(int X,int Y,int W,int H) : Fl_Tabs(X,Y,W,H) {} - void resize(int,int,int,int) FL_OVERRIDE; - void draw() FL_OVERRIDE; -}; - -class Fl_Tabs_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; -public: - const char *type_name() FL_OVERRIDE {return tabs_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::TabGroup";} - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - Fl_Tabs_Proxy *g = new Fl_Tabs_Proxy(X,Y,W,H); Fl_Group::current(0); return g;} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Tabs_Type();} - Fl_Type* click_test(int,int) FL_OVERRIDE; - void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void remove_child(Fl_Type*) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Tabs; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Tabs) ? true : super::is_a(inID); } - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; -}; - -// ---- Fl_Scroll_Type ------------------------------------------------- MARK: - - -extern const char scroll_type_name[]; -extern Fl_Menu_Item scroll_type_menu[]; - -class Fl_Scroll_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE {return scroll_type_menu;} -public: - const char *type_name() FL_OVERRIDE {return scroll_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::ScrollGroup";} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Scroll_Type();} - ID id() const FL_OVERRIDE { return ID_Scroll; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Scroll) ? true : super::is_a(inID); } - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; -}; - -// ---- Fl_Tile_Type --------------------------------------------------- MARK: - - -extern const char tile_type_name[]; - -class Fl_Tile_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; -public: - const char *type_name() FL_OVERRIDE {return tile_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::TileGroup";} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Tile_Type();} - ID id() const FL_OVERRIDE { return ID_Tile; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Tile) ? true : super::is_a(inID); } - void copy_properties() FL_OVERRIDE; -}; - -// ---- Fl_Wizard_Type ------------------------------------------------- MARK: - - -class Fl_Wizard_Proxy : public Fl_Wizard { -public: - Fl_Wizard_Proxy(int X,int Y,int W,int H) : Fl_Wizard(X,Y,W,H) {} - void resize(int,int,int,int) FL_OVERRIDE; - void draw() FL_OVERRIDE; -}; - -extern const char wizard_type_name[]; - -class Fl_Wizard_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; -public: - const char *type_name() FL_OVERRIDE {return wizard_type_name;} - const char *alt_type_name() FL_OVERRIDE {return "fltk::WizardGroup";} - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - Fl_Wizard_Proxy *g = new Fl_Wizard_Proxy(X,Y,W,H); Fl_Group::current(0); return g;} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Wizard_Type();} - ID id() const FL_OVERRIDE { return ID_Wizard; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Wizard) ? true : super::is_a(inID); } -}; - -#endif // _FLUID_FL_GROUP_TYPE_H diff --git a/fluid/nodes/Fl_Menu_Type.cxx b/fluid/nodes/Fl_Menu_Type.cxx deleted file mode 100644 index 83435df50..000000000 --- a/fluid/nodes/Fl_Menu_Type.cxx +++ /dev/null @@ -1,924 +0,0 @@ -// -// Menu item code for the Fast Light Tool Kit (FLTK). -// -// Menu items are kludged by making a phony Fl_Box widget so the normal -// widget panel can be used to control them. -// -// This file also contains code to make Fl_Menu_Button, Fl_Menu_Bar, -// etc widgets. -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "nodes/Fl_Menu_Type.h" - -#include "app/fluid.h" -#include "app/project.h" -#include "app/Fluid_Image.h" -#include "app/mergeback.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "nodes/Fl_Window_Type.h" -#include "widgets/Formula_Input.h" -#include "widgets/Node_Browser.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../src/flstring.h" - -#include -#include - -Fl_Menu_Item menu_item_type_menu[] = { - {"Normal",0,0,(void*)0}, - {"Toggle",0,0,(void*)FL_MENU_BOX}, - {"Radio",0,0,(void*)FL_MENU_RADIO}, - {0}}; - -static void delete_dependents(Fl_Menu_Item *m) { - if (!m) - return; - int level = 0; - for (;;m++) { - if (m->label()==NULL) { - if (level==0) { - break; - } else { - level--; - } - } - if (m->flags&FL_SUBMENU) - level++; - if (m->labeltype()==FL_MULTI_LABEL) - delete (Fl_Multi_Label*)m->label(); - } -} - -static void delete_menu(Fl_Menu_Item *m) { - if (!m) - return; - delete_dependents(m); - delete[] m; -} - -void Fl_Input_Choice_Type::build_menu() { - Fl_Input_Choice* w = (Fl_Input_Choice*)o; - // count how many Fl_Menu_Item structures needed: - int n = 0; - Fl_Type* q; - for (q = next; q && q->level > level; q = q->next) { - if (q->can_have_children()) n++; // space for null at end of submenu - n++; - } - if (!n) { - if (menusize) delete_menu((Fl_Menu_Item*)(w->menu())); - w->menu(0); - menusize = 0; - } else { - n++; // space for null at end of menu - if (menusizemenu())); - menusize = n+10; - w->menu(new Fl_Menu_Item[menusize]); - } else { - if (menusize) delete_dependents((Fl_Menu_Item*)(w->menu())); - } - // Menus are already built during the .fl file reading process, so if the - // end of a menu list is not read yet, the end markers (label==NULL) will - // not be set, and deleting dependents will randomly free memory. - // Clearing the array should avoid that. - memset( (void*)w->menu(), 0, menusize * sizeof(Fl_Menu_Item) ); - // fill them all in: - Fl_Menu_Item* m = (Fl_Menu_Item*)(w->menu()); - int lvl = level+1; - for (q = next; q && q->level > level; q = q->next) { - Fl_Menu_Item_Type* i = (Fl_Menu_Item_Type*)q; - if (i->o->image()) { - if (i->o->label() && i->o->label()[0]) { - Fl_Multi_Label *ml = new Fl_Multi_Label; - ml->labela = (char*)i->o->image(); - ml->labelb = i->o->label(); - ml->typea = FL_IMAGE_LABEL; - ml->typeb = FL_NORMAL_LABEL; - ml->label(m); - } else { - i->o->image()->label(m); - } - } else { - m->label(i->o->label() ? i->o->label() : "(nolabel)"); - m->labeltype(i->o->labeltype()); - } - m->shortcut(((Fl_Button*)(i->o))->shortcut()); - m->callback(0,(void*)i); - m->flags = i->flags(); - m->labelfont(i->o->labelfont()); - m->labelsize(i->o->labelsize()); - m->labelcolor(i->o->labelcolor()); - if (q->can_have_children()) {lvl++; m->flags |= FL_SUBMENU;} - m++; - int l1 = - (q->next && q->next->is_a(ID_Menu_Item)) ? q->next->level : level; - while (lvl > l1) {m->label(0); m++; lvl--;} - lvl = l1; - } - } - o->redraw(); -} - -/** - Create and add a new Menu Item node. - \param[in] strategy add after current or as last child - \return new Menu Item node - */ -Fl_Type *Fl_Menu_Item_Type::make(Strategy strategy) { - return Fl_Menu_Item_Type::make(0, strategy); -} - -/** - Create an add a specific Menu Item node. - \param[in] flags set to 0, FL_MENU_RADIO, FL_MENU_TOGGLE, or FL_SUBMENU - \param[in] strategy add after current or as last child - \return new Menu Item node - */ -Fl_Type* Fl_Menu_Item_Type::make(int flags, Strategy strategy) { - // Find a good insert position based on the current marked node - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) - p = p->parent; - while (p && !(p->is_a(ID_Menu_Manager_) || p->is_a(ID_Submenu))) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - if (!p) { - fl_message("Please select a menu widget or a menu item"); - return 0; - } - if (!o) { - o = new Fl_Button(0,0,100,20); // create template widget - } - - Fl_Menu_Item_Type* t = NULL; - if (flags==FL_SUBMENU) { - t = new Fl_Submenu_Type(); - } else { - t = new Fl_Menu_Item_Type(); - } - t->o = new Fl_Button(0,0,100,20); - t->o->type(flags); - t->factory = this; - t->add(anchor, strategy); - if (strategy.source() == Strategy::FROM_USER) { - if (flags==FL_SUBMENU) { - t->label("submenu"); - } else { - t->label("item"); - } - } - return t; -} - -void group_selected_menuitems() { - // The group will be created in the parent group of the current menuitem - if (!Fl_Type::current->is_a(ID_Menu_Item)) { - return; - } - Fl_Menu_Item_Type *q = static_cast(Fl_Type::current); - Fl_Type *qq = Fl_Type::current->parent; - if (!qq || !(qq->is_a(ID_Menu_Manager_) || qq->is_a(ID_Submenu))) { - fl_message("Can't create a new submenu here."); - return; - } - undo_checkpoint(); - undo_suspend(); - Fl_Widget_Type *n = (Fl_Widget_Type*)(q->make(FL_SUBMENU, Strategy::AFTER_CURRENT)); - for (Fl_Type *t = qq->next; t && (t->level > qq->level);) { - if (t->level != n->level || t == n || !t->selected) { - t = t->next; - continue; - } - Fl_Type *nxt = t->remove(); - t->add(n, Strategy::AS_LAST_CHILD); - t = nxt; - } - widget_browser->rebuild(); - undo_resume(); - set_modflag(1); -} - -void ungroup_selected_menuitems() { - // Find the submenu - Fl_Type *qq = Fl_Type::current->parent; - Fl_Widget_Type *q = static_cast(Fl_Type::current); - int q_level = q->level; - if (!qq || !qq->is_a(ID_Submenu)) { - fl_message("Only menu items inside a submenu can be ungrouped."); - return; - } - undo_checkpoint(); - undo_suspend(); - Fl_Type::current = qq; - for (Fl_Type *t = qq->next; t && (t->level > qq->level);) { - if (t->level != q_level || !t->selected) { - t = t->next; - continue; - } - Fl_Type *nxt = t->remove(); - t->insert(qq); - t = nxt; - } - if (!qq->next || (qq->next->level <= qq->level)) { - qq->remove(); - delete qq; // qq has no children that need to be delete - } - Fl_Type::current = q; - widget_browser->rebuild(); - undo_resume(); - set_modflag(1); -} - - -/** - Create and add a new Checkbox Menu Item node. - \param[in] strategy add after current or as last child - \return new node - */ -Fl_Type *Fl_Checkbox_Menu_Item_Type::make(Strategy strategy) { - return Fl_Menu_Item_Type::make(FL_MENU_TOGGLE, strategy); -} - -/** - Create and add a new Radio ButtonMenu Item node. - \param[in] strategy add after current or as last child - \return new node - */ -Fl_Type *Fl_Radio_Menu_Item_Type::make(Strategy strategy) { - return Fl_Menu_Item_Type::make(FL_MENU_RADIO, strategy); -} - -/** - Create and add a new Submenu Item node. - \param[in] strategy add after current or as last child - \return new node - */ -Fl_Type *Fl_Submenu_Type::make(Strategy strategy) { - return Fl_Menu_Item_Type::make(FL_SUBMENU, strategy); -} - -Fl_Menu_Item_Type Fl_Menu_Item_type; -Fl_Checkbox_Menu_Item_Type Fl_Checkbox_Menu_Item_type; -Fl_Radio_Menu_Item_Type Fl_Radio_Menu_Item_type; -Fl_Submenu_Type Fl_Submenu_type; - -//////////////////////////////////////////////////////////////// -// Writing the C code: - -// test functions in Fl_Widget_Type.C: -int is_name(const char *c); -const char *array_name(Fl_Widget_Type *o); -int isdeclare(const char *c); - -// Search backwards to find the parent menu button and return it's name. -// Also put in i the index into the button's menu item array belonging -// to this menu item. -const char* Fl_Menu_Item_Type::menu_name(fld::io::Code_Writer& f, int& i) { - i = 0; - Fl_Type* t = prev; - while (t && t->is_a(ID_Menu_Item)) { - // be sure to count the {0} that ends a submenu: - if (t->level > t->next->level) i += (t->level - t->next->level); - // detect empty submenu: - else if (t->level == t->next->level && t->can_have_children()) i++; - t = t->prev; - i++; - } - if (!t) return "\n#error Fl_Menu_Item_Type::menu_name, invalid f\n"; - return f.unique_id(t, "menu", t->name(), t->label()); -} - -void Fl_Menu_Item_Type::write_static(fld::io::Code_Writer& f) { - if (image && label() && label()[0]) { - f.write_h_once("#include "); - f.write_h_once("#include "); - } - if (callback() && is_name(callback()) && !user_defined(callback())) - f.write_h_once("extern void %s(Fl_Menu_*, %s);", callback(), - user_data_type() ? user_data_type() : "void*"); - for (int n=0; n < NUM_EXTRA_CODE; n++) { - if (extra_code(n) && isdeclare(extra_code(n))) - f.write_h_once("%s", extra_code(n)); - } - if (callback() && !is_name(callback())) { - // see if 'o' or 'v' used, to prevent unused argument warnings: - int use_o = 0; - int use_v = 0; - const char *d; - for (d = callback(); *d;) { - if (*d == 'o' && !is_id(d[1])) use_o = 1; - if (*d == 'v' && !is_id(d[1])) use_v = 1; - do d++; while (is_id(*d)); - while (*d && !is_id(*d)) d++; - } - const char* cn = callback_name(f); - const char* k = class_name(1); - if (k) { - f.write_c("\nvoid %s::%s_i(Fl_Menu_*", k, cn); - } else { - f.write_c("\nstatic void %s(Fl_Menu_*", cn); - } - if (use_o) f.write_c(" o"); - const char* ut = user_data_type() ? user_data_type() : "void*"; - f.write_c(", %s", ut); - if (use_v) f.write_c(" v"); - f.write_c(") {\n"); - // Matt: disabled f.tag(FD_TAG_GENERIC, 0); - f.write_c_indented(callback(), 1, 0); - if (*(d-1) != ';' && *(d-1) != '}') { - const char *p = strrchr(callback(), '\n'); - if (p) p ++; - else p = callback(); - // Only add trailing semicolon if the last line is not a preprocessor - // statement... - if (*p != '#' && *p) f.write_c(";"); - } - f.write_c("\n"); - // Matt: disabled f.tag(FD_TAG_MENU_CALLBACK, get_uid()); - f.write_c("}\n"); - - // If the menu item is part of a Class or Widget Class, FLUID generates - // a dummy static callback which retrieves a pointer to the class and then - // calls the original callback from within the class context. - // k is the name of the enclosing class (or classes) - if (k) { - // Implement the callback as a static member function - f.write_c("void %s::%s(Fl_Menu_* o, %s v) {\n", k, cn, ut); - // Find the Fl_Menu_ container for this menu item - Fl_Type* t = parent; while (t->is_a(ID_Menu_Item)) t = t->parent; - if (t) { - Fl_Widget_Type *tw = (t->is_widget()) ? static_cast(t) : NULL; - Fl_Type *q = NULL; - // Generate code to call the callback - if (tw->is_a(ID_Menu_Bar) && ((Fl_Menu_Bar_Type*)tw)->is_sys_menu_bar()) { - // Fl_Sys_Menu_Bar removes itself from any parent on macOS, so we - // wrapped it in a class and remeber the parent class in a new - // class memeber variable. - Fl_Menu_Bar_Type *tmb = (Fl_Menu_Bar_Type*)tw; - f.write_c("%s%s* sys_menu_bar = ((%s*)o);\n", f.indent(1), - tmb->sys_menubar_proxy_name(), tmb->sys_menubar_proxy_name()); - f.write_c("%s%s* parent_class = ((%s*)sys_menu_bar->_parent_class);\n", - f.indent(1), k, k); - f.write_c("%sparent_class->%s_i(o,v);\n}\n", - f.indent(1), cn); - } else { - f.write_c("%s((%s*)(o", f.indent(1), k); - // The class pointer is in the user_data field of the top widget - if (t && t->is_a(ID_Input_Choice)) { - // Go up one more level for Fl_Input_Choice, as these are groups themselves - f.write_c("->parent()"); - } - // Now generate code to find the topmost widget in this class - for (t = t->parent; t && t->is_widget() && !is_class(); q = t, t = t->parent) - f.write_c("->parent()"); - // user_data is cast into a pointer to the - if (!q || !q->is_a(ID_Widget_Class)) - f.write_c("->user_data()"); - f.write_c("))->%s_i(o,v);\n}\n", cn); - } - } else { - f.write_c("#error Enclosing Fl_Menu_* not found\n"); - } - } - } - if (image) { - if (!f.c_contains(image)) - image->write_static(f, compress_image_); - } - if (next && next->is_a(ID_Menu_Item)) return; - // okay, when we hit last item in the menu we have to write the - // entire array out: - const char* k = class_name(1); - if (k) { - int i; - f.write_c("\nFl_Menu_Item %s::%s[] = {\n", k, menu_name(f, i)); - } else { - int i; - f.write_c("\nFl_Menu_Item %s[] = {\n", menu_name(f, i)); - } - Fl_Type* t = prev; while (t && t->is_a(ID_Menu_Item)) t = t->prev; - for (Fl_Type* q = t->next; q && q->is_a(ID_Menu_Item); q = q->next) { - ((Fl_Menu_Item_Type*)q)->write_item(f); - int thislevel = q->level; if (q->can_have_children()) thislevel++; - int nextlevel = - (q->next && q->next->is_a(ID_Menu_Item)) ? q->next->level : t->level+1; - while (thislevel > nextlevel) {f.write_c(" {0,0,0,0,0,0,0,0,0},\n"); thislevel--;} - } - f.write_c(" {0,0,0,0,0,0,0,0,0}\n};\n"); - - if (k) { - // Write menu item variables... - t = prev; while (t && t->is_a(ID_Menu_Item)) t = t->prev; - for (Fl_Type* q = t->next; q && q->is_a(ID_Menu_Item); q = q->next) { - Fl_Menu_Item_Type *m = (Fl_Menu_Item_Type*)q; - const char *c = array_name(m); - if (c) { - if (c==m->name()) { - // assign a menu item address directly to a variable - int i; - const char* n = ((Fl_Menu_Item_Type *)q)->menu_name(f, i); - f.write_c("Fl_Menu_Item* %s::%s = %s::%s + %d;\n", k, c, k, n, i); - } else { - // if the name is an array, only define the array. - // The actual assignment is in write_code1(fld::io::Code_Writer& f) - f.write_c("Fl_Menu_Item* %s::%s;\n", k, c); - } - } - } - } -} - -int Fl_Menu_Item_Type::flags() { - int i = o->type(); - if (((Fl_Button*)o)->value()) i |= FL_MENU_VALUE; - if (!o->active()) i |= FL_MENU_INACTIVE; - if (!o->visible()) i |= FL_MENU_INVISIBLE; - if (can_have_children()) { - if (user_data() == NULL) i |= FL_SUBMENU; - else i |= FL_SUBMENU_POINTER; - } - if (hotspot()) i |= FL_MENU_DIVIDER; - return i; -} - -void Fl_Menu_Item_Type::write_item(fld::io::Code_Writer& f) { - static const char * const labeltypes[] = { - "FL_NORMAL_LABEL", - "FL_NO_LABEL", - "FL_SHADOW_LABEL", - "FL_ENGRAVED_LABEL", - "FL_EMBOSSED_LABEL", - "FL_MULTI_LABEL", - "FL_ICON_LABEL", - "FL_IMAGE_LABEL" - }; - - write_comment_inline_c(f, " "); - f.write_c(" {"); - if (label() && label()[0]) - switch (g_project.i18n_type) { - case FD_I18N_GNU: - // we will call i18n when the menu is instantiated for the first time - f.write_c("%s(", g_project.i18n_gnu_static_function.c_str()); - f.write_cstring(label()); - f.write_c(")"); - break; - case FD_I18N_POSIX: - // fall through: strings can't be translated before a catalog is chosen - default: - f.write_cstring(label()); - } - else - f.write_c("\"\""); - if (((Fl_Button*)o)->shortcut()) { - int s = ((Fl_Button*)o)->shortcut(); - f.write_c(", "); - if (g_project.use_FL_COMMAND) { - if (s & FL_CTRL) { f.write_c("FL_CONTROL|"); s &= ~FL_CTRL; } - if (s & FL_META) { f.write_c("FL_COMMAND|"); s &= ~FL_META; } - } else { - if (s & FL_CTRL) { f.write_c("FL_CTRL|"); s &= ~FL_CTRL; } - if (s & FL_META) { f.write_c("FL_META|"); s &= ~FL_META; } - } - if (s & FL_SHIFT) { f.write_c("FL_SHIFT|"); s &= ~FL_SHIFT; } - if (s & FL_ALT) { f.write_c("FL_ALT|"); s &= ~FL_ALT; } - if ((s < 127) && isprint(s)) - f.write_c("'%c', ", s); - else - f.write_c("0x%x, ", s); - } else { - f.write_c(", 0, "); - } - if (callback()) { - const char* k = is_name(callback()) ? 0 : class_name(1); - if (k) { - f.write_c(" (Fl_Callback*)%s::%s,", k, callback_name(f)); - } else { - f.write_c(" (Fl_Callback*)%s,", callback_name(f)); - } - } else - f.write_c(" 0,"); - if (user_data()) - f.write_c(" (void*)(%s),", user_data()); - else - f.write_c(" 0,"); - f.write_c(" %d, (uchar)%s, %d, %d, %d", flags(), - labeltypes[o->labeltype()], o->labelfont(), o->labelsize(), o->labelcolor()); - f.write_c("},\n"); -} - -void start_menu_initialiser(fld::io::Code_Writer& f, int &initialized, const char *name, int index) { - if (!initialized) { - initialized = 1; - f.write_c("%s{ Fl_Menu_Item* o = &%s[%d];\n", f.indent(), name, index); - f.indentation++; - } -} - -void Fl_Menu_Item_Type::write_code1(fld::io::Code_Writer& f) { - int i; const char* mname = menu_name(f, i); - - if (!prev->is_a(ID_Menu_Item)) { - // for first menu item, declare the array - if (class_name(1)) { - f.write_h("%sstatic Fl_Menu_Item %s[];\n", f.indent(1), mname); - } else { - f.write_h("extern Fl_Menu_Item %s[];\n", mname); - } - } - - const char *c = array_name(this); - if (c) { - if (class_name(1)) { - f.write_public(public_); - f.write_h("%sstatic Fl_Menu_Item *%s;\n", f.indent(1), c); - } else { - if (c==name()) - f.write_h("#define %s (%s+%d)\n", c, mname, i); - else - f.write_h("extern Fl_Menu_Item *%s;\n", c); - } - } - - if (callback()) { - if (!is_name(callback()) && class_name(1)) { - const char* cn = callback_name(f); - const char* ut = user_data_type() ? user_data_type() : "void*"; - f.write_public(0); - f.write_h("%sinline void %s_i(Fl_Menu_*, %s);\n", f.indent(1), cn, ut); - f.write_h("%sstatic void %s(Fl_Menu_*, %s);\n", f.indent(1), cn, ut); - } - } - - int menuItemInitialized = 0; - // if the name is an array variable, assign the value here - if (name() && strchr(name(), '[')) { - f.write_c("%s%s = &%s[%d];\n", f.indent_plus(1), name(), mname, i); - } - if (image) { - start_menu_initialiser(f, menuItemInitialized, mname, i); - if (label() && label()[0]) { - f.write_c("%sFl_Multi_Label *ml = new Fl_Multi_Label;\n", f.indent()); - f.write_c("%sml->labela = (char*)", f.indent()); - image->write_inline(f); - f.write_c(";\n"); - if (g_project.i18n_type==FD_I18N_NONE) { - f.write_c("%sml->labelb = o->label();\n", f.indent()); - } else if (g_project.i18n_type==FD_I18N_GNU) { - f.write_c("%sml->labelb = %s(o->label());\n", - f.indent(), g_project.i18n_gnu_function.c_str()); - } else if (g_project.i18n_type==FD_I18N_POSIX) { - f.write_c("%sml->labelb = catgets(%s,%s,i+%d,o->label());\n", - f.indent(), - g_project.i18n_pos_file.empty() ? "_catalog" : g_project.i18n_pos_file.c_str(), - g_project.i18n_pos_set.c_str(), msgnum()); - } - f.write_c("%sml->typea = FL_IMAGE_LABEL;\n", f.indent()); - f.write_c("%sml->typeb = FL_NORMAL_LABEL;\n", f.indent()); - f.write_c("%sml->label(o);\n", f.indent()); - } else { - image->write_code(f, 0, "o"); - } - } - if (g_project.i18n_type && label() && label()[0]) { - Fl_Labeltype t = o->labeltype(); - if (image) { - // label was already copied a few lines up - } else if ( t==FL_NORMAL_LABEL || t==FL_SHADOW_LABEL - || t==FL_ENGRAVED_LABEL || t==FL_EMBOSSED_LABEL) { - start_menu_initialiser(f, menuItemInitialized, mname, i); - if (g_project.i18n_type==FD_I18N_GNU) { - f.write_c("%so->label(%s(o->label()));\n", - f.indent(), g_project.i18n_gnu_function.c_str()); - } else if (g_project.i18n_type==FD_I18N_POSIX) { - f.write_c("%so->label(catgets(%s,%s,i+%d,o->label()));\n", - f.indent(), - g_project.i18n_pos_file.empty() ? "_catalog" : g_project.i18n_pos_file.c_str(), - g_project.i18n_pos_set.c_str(), msgnum()); - } - } - } - for (int n=0; n < NUM_EXTRA_CODE; n++) { - if (extra_code(n) && !isdeclare(extra_code(n))) { - start_menu_initialiser(f, menuItemInitialized, mname, i); - f.write_c("%s%s\n", f.indent(), extra_code(n)); - } - } - if (menuItemInitialized) { - f.indentation--; - f.write_c("%s}\n",f.indent()); - } -} - -void Fl_Menu_Item_Type::write_code2(fld::io::Code_Writer&) {} - -//////////////////////////////////////////////////////////////// -// This is the base class for widgets that contain a menu (ie -// subclasses of Fl_Menu_. -// This is a parent widget and menu items can be added as -// children. An actual array of Fl_Menu_Items is kept parallel -// with the child objects and updated as they change. - -void Fl_Menu_Base_Type::build_menu() { - Fl_Menu_* w = (Fl_Menu_*)o; - // count how many Fl_Menu_Item structures needed: - int n = 0; - Fl_Type* q; - for (q = next; q && q->level > level; q = q->next) { - if (q->can_have_children()) n++; // space for null at end of submenu - n++; - } - if (!n) { - if (menusize) delete_menu((Fl_Menu_Item*)(w->menu())); - w->menu(0); - menusize = 0; - } else { - n++; // space for null at end of menu - if (menusizemenu())); - menusize = n+10; - w->menu(new Fl_Menu_Item[menusize]); - } else { - if (menusize) delete_dependents((Fl_Menu_Item*)(w->menu())); - } - // Menus are already built during the .fl file reading process, so if the - // end of a menu list is not read yet, the end markers (label==NULL) will - // not be set, and deleting dependents will randomly free memory. - // Clearing the array should avoid that. - memset( (void*)w->menu(), 0, menusize * sizeof(Fl_Menu_Item) ); - // fill them all in: - Fl_Menu_Item* m = (Fl_Menu_Item*)(w->menu()); - int lvl = level+1; - for (q = next; q && q->level > level; q = q->next) { - Fl_Menu_Item_Type* i = (Fl_Menu_Item_Type*)q; - if (i->o->image()) { - if (i->o->label() && i->o->label()[0]) { - Fl_Multi_Label *ml = new Fl_Multi_Label; - ml->labela = (char*)i->o->image(); - ml->labelb = i->o->label(); - ml->typea = FL_IMAGE_LABEL; - ml->typeb = FL_NORMAL_LABEL; - ml->label(m); - } else { - i->o->image()->label(m); - } - } else { - m->label(i->o->label() ? i->o->label() : "(nolabel)"); - m->labeltype(i->o->labeltype()); - } - m->shortcut(((Fl_Button*)(i->o))->shortcut()); - m->callback(0,(void*)i); - m->flags = i->flags() | i->o->type(); - m->labelfont(i->o->labelfont()); - m->labelsize(i->o->labelsize()); - m->labelcolor(i->o->labelcolor()); - if (q->can_have_children()) {lvl++; m->flags |= FL_SUBMENU;} - m++; - int l1 = - (q->next && q->next->is_a(ID_Menu_Item)) ? q->next->level : level; - while (lvl > l1) {m->label(0); m++; lvl--;} - lvl = l1; - } - } - o->redraw(); -} - -Fl_Type* Fl_Menu_Base_Type::click_test(int, int) { - if (selected) return 0; // let user move the widget - Fl_Menu_* w = (Fl_Menu_*)o; - if (!menusize) return 0; - const Fl_Menu_Item* save = w->mvalue(); - w->value((Fl_Menu_Item*)0); - Fl::pushed(w); - w->handle(FL_PUSH); - Fl::focus(NULL); - const Fl_Menu_Item* m = w->mvalue(); - if (m) { - // restore the settings of toggles & radio items: - if (m->flags & (FL_MENU_RADIO | FL_MENU_TOGGLE)) build_menu(); - return (Fl_Type*)(m->user_data()); - } - w->value(save); - return this; -} - -void Fl_Menu_Manager_Type::write_code2(fld::io::Code_Writer& f) { - if (next && next->is_a(ID_Menu_Item)) { - f.write_c("%s%s->menu(%s);\n", f.indent(), name() ? name() : "o", - f.unique_id(this, "menu", name(), label())); - } - Fl_Widget_Type::write_code2(f); -} - -void Fl_Menu_Base_Type::copy_properties() { - Fl_Widget_Type::copy_properties(); - Fl_Menu_ *s = (Fl_Menu_*)o, *d = (Fl_Menu_*)live_widget; - d->menu(s->menu()); - d->down_box(s->down_box()); - d->textcolor(s->textcolor()); - d->textfont(s->textfont()); - d->textsize(s->textsize()); -} - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Item button_type_menu[] = { - {"normal",0,0,(void*)0}, - {"popup1",0,0,(void*)Fl_Menu_Button::POPUP1}, - {"popup2",0,0,(void*)Fl_Menu_Button::POPUP2}, - {"popup3",0,0,(void*)Fl_Menu_Button::POPUP3}, - {"popup12",0,0,(void*)Fl_Menu_Button::POPUP12}, - {"popup23",0,0,(void*)Fl_Menu_Button::POPUP23}, - {"popup13",0,0,(void*)Fl_Menu_Button::POPUP13}, - {"popup123",0,0,(void*)Fl_Menu_Button::POPUP123}, - {0}}; - -Fl_Menu_Button_Type Fl_Menu_Button_type; - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Item dummymenu[] = {{"CHOICE"},{0}}; - -Fl_Choice_Type Fl_Choice_type; - -Fl_Input_Choice_Type Fl_Input_Choice_type; - -void Fl_Input_Choice_Type::copy_properties() { - Fl_Widget_Type::copy_properties(); - Fl_Input_Choice *s = (Fl_Input_Choice*)o, *d = (Fl_Input_Choice*)live_widget; - d->menu(s->menu()); - d->down_box(s->down_box()); - d->textcolor(s->textcolor()); - d->textfont(s->textfont()); - d->textsize(s->textsize()); -} - -Fl_Type* Fl_Input_Choice_Type::click_test(int, int) { - if (selected) return 0; // let user move the widget - Fl_Menu_* w = ((Fl_Input_Choice*)o)->menubutton(); - if (!menusize) return 0; - const Fl_Menu_Item* save = w->mvalue(); - w->value((Fl_Menu_Item*)0); - Fl::pushed(w); - w->handle(FL_PUSH); - Fl::focus(NULL); - const Fl_Menu_Item* m = w->mvalue(); - if (m) { - // restore the settings of toggles & radio items: - if (m->flags & (FL_MENU_RADIO | FL_MENU_TOGGLE)) build_menu(); - return (Fl_Type*)(m->user_data()); - } - w->value(save); - return this; -} - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Bar_Type Fl_Menu_Bar_type; - -Fl_Menu_Item menu_bar_type_menu[] = { - {"Fl_Menu_Bar",0,0,(void*)0}, - {"Fl_Sys_Menu_Bar",0,0,(void*)1}, - {0}}; - -Fl_Menu_Bar_Type::Fl_Menu_Bar_Type() -: _proxy_name(NULL) -{ -} - -Fl_Menu_Bar_Type::~Fl_Menu_Bar_Type() { - if (_proxy_name) - ::free(_proxy_name); -} - -/** - \brief Return true if this is an Fl_Sys_Menu_Bar. - This test fails if subclass() is the name of a class that the user may have - derived from Fl_Sys_Menu_Bar. - */ -bool Fl_Menu_Bar_Type::is_sys_menu_bar() { - if (o->type()==1) return true; - return ( subclass() && (strcmp(subclass(), "Fl_Sys_Menu_Bar")==0) ); -} - -const char *Fl_Menu_Bar_Type::sys_menubar_name() { - if (subclass()) - return subclass(); - else - return "Fl_Sys_Menu_Bar"; -} - -const char *Fl_Menu_Bar_Type::sys_menubar_proxy_name() { - if (!_proxy_name) - _proxy_name = (char*)::malloc(128); - ::snprintf(_proxy_name, 63, "%s_Proxy", sys_menubar_name()); - return _proxy_name; -} - - -void Fl_Menu_Bar_Type::write_static(fld::io::Code_Writer& f) { - super::write_static(f); - if (is_sys_menu_bar()) { - f.write_h_once("#include "); - if (is_in_class()) { - // Make room for a pointer to the enclosing class. - f.write_c_once( // must be less than 1024 bytes! - "\nclass %s: public %s {\n" - "public:\n" - " %s(int x, int y, int w, int h, const char *l=NULL)\n" - " : %s(x, y, w, h, l) { }\n" - " void *_parent_class;\n" - "};\n", - sys_menubar_proxy_name(), sys_menubar_name(), - sys_menubar_proxy_name(), sys_menubar_name() - ); - } - } -} - -void Fl_Menu_Bar_Type::write_code1(fld::io::Code_Writer& f) { - super::write_code1(f); - if (is_sys_menu_bar() && is_in_class()) { - f.write_c("%s((%s*)%s)->_parent_class = (void*)this;\n", - f.indent(), sys_menubar_proxy_name(), name() ? name() : "o"); - } -} - -//void Fl_Menu_Bar_Type::write_code2(fld::io::Code_Writer& f) { -// super::write_code2(f); -//} - -//////////////////////////////////////////////////////////////// -// Shortcut entry item in panel: -void shortcut_in_cb(Fl_Shortcut_Button* i, void* v) { - if (v == LOAD) { - if (current_widget->is_button()) - i->value( ((Fl_Button*)(current_widget->o))->shortcut() ); - else if (current_widget->is_a(ID_Input)) - i->value( ((Fl_Input_*)(current_widget->o))->shortcut() ); - else if (current_widget->is_a(ID_Value_Input)) - i->value( ((Fl_Value_Input*)(current_widget->o))->shortcut() ); - else if (current_widget->is_a(ID_Text_Display)) - i->value( ((Fl_Text_Display*)(current_widget->o))->shortcut() ); - else { - i->hide(); - i->parent()->hide(); - return; - } - //i->default_value( i->value() ); // enable the "undo" capability of the shortcut button - i->show(); - i->parent()->show(); - i->redraw(); - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) - if (o->selected && o->is_button()) { - Fl_Button* b = (Fl_Button*)(((Fl_Widget_Type*)o)->o); - if (b->shortcut() != (int)i->value()) mod = 1; - b->shortcut(i->value()); - if (o->is_a(ID_Menu_Item)) ((Fl_Widget_Type*)o)->redraw(); - } else if (o->selected && o->is_a(ID_Input)) { - Fl_Input_* b = (Fl_Input_*)(((Fl_Widget_Type*)o)->o); - if (b->shortcut() != (int)i->value()) mod = 1; - b->shortcut(i->value()); - } else if (o->selected && o->is_a(ID_Value_Input)) { - Fl_Value_Input* b = (Fl_Value_Input*)(((Fl_Widget_Type*)o)->o); - if (b->shortcut() != (int)i->value()) mod = 1; - b->shortcut(i->value()); - } else if (o->selected && o->is_a(ID_Text_Display)) { - Fl_Text_Display* b = (Fl_Text_Display*)(((Fl_Widget_Type*)o)->o); - if (b->shortcut() != (int)i->value()) mod = 1; - b->shortcut(i->value()); - } - if (mod) set_modflag(1); - } -} diff --git a/fluid/nodes/Fl_Menu_Type.h b/fluid/nodes/Fl_Menu_Type.h deleted file mode 100644 index 136a1c289..000000000 --- a/fluid/nodes/Fl_Menu_Type.h +++ /dev/null @@ -1,287 +0,0 @@ -// -// Menu type header file for the Fast Light Tool Kit (FLTK). -// -// Type for creating all subclasses of Fl_Widget -// This should have the widget pointer in it, but it is still in the -// Fl_Type base class. -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef _FLUID_FL_MENU_TYPE_H -#define _FLUID_FL_MENU_TYPE_H - -#include "nodes/Fl_Button_Type.h" - -#include "app/Fd_Snap_Action.h" - -#include -#include -#include -#include -#include -#include - -extern Fl_Menu_Item dummymenu[]; -extern Fl_Menu_Item button_type_menu[]; -extern Fl_Menu_Item menu_item_type_menu[]; -extern Fl_Menu_Item menu_bar_type_menu[]; - -/** - \brief Manage all types on menu items. - Deriving Fl_Menu_Item_Type from Fl_Button_Type is intentional. For the purpose - of editing, a Menu Item is implemented with `o` pointing to an Fl_Button for - holding all properties. - */ -class Fl_Menu_Item_Type : public Fl_Button_Type -{ - typedef Fl_Button_Type super; -public: - Fl_Menu_Item* subtypes() FL_OVERRIDE {return menu_item_type_menu;} - const char* type_name() FL_OVERRIDE {return "MenuItem";} - const char* alt_type_name() FL_OVERRIDE {return "fltk::Item";} - Fl_Type* make(Strategy strategy) FL_OVERRIDE; - Fl_Type* make(int flags, Strategy strategy); - int is_button() const FL_OVERRIDE {return 1;} // this gets shortcut to work - Fl_Widget* widget(int,int,int,int) FL_OVERRIDE {return 0;} - Fl_Widget_Type* _make() FL_OVERRIDE {return 0;} - virtual const char* menu_name(fld::io::Code_Writer& f, int& i); - int flags(); - void write_static(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_item(fld::io::Code_Writer& f); - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - int is_true_widget() const FL_OVERRIDE { return 0; } - ID id() const FL_OVERRIDE { return ID_Menu_Item; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Menu_Item) ? true : super::is_a(inID); } -}; - -/** - \brief Manage Radio style Menu Items. - */ -class Fl_Radio_Menu_Item_Type : public Fl_Menu_Item_Type -{ - typedef Fl_Menu_Item_Type super; -public: - const char* type_name() FL_OVERRIDE {return "RadioMenuItem";} - Fl_Type* make(Strategy strategy) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Radio_Menu_Item; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Radio_Menu_Item) ? true : super::is_a(inID); } -}; - -/** - \brief Manage Checkbox style Menu Items. - */ -class Fl_Checkbox_Menu_Item_Type : public Fl_Menu_Item_Type -{ - typedef Fl_Menu_Item_Type super; -public: - const char* type_name() FL_OVERRIDE {return "CheckMenuItem";} - Fl_Type* make(Strategy strategy) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Checkbox_Menu_Item; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Checkbox_Menu_Item) ? true : super::is_a(inID); } -}; - -/** - \brief Manage Submenu style Menu Items. - Submenu Items are simply buttons just like all other menu items, but they - can also hold a pointer to a list of submenus, or have a flag set that - allows submenus to follow in the current array. As buttons, they can - be clicked by the user, and they will call their callback, if one is set. - */ -class Fl_Submenu_Type : public Fl_Menu_Item_Type -{ - typedef Fl_Menu_Item_Type super; -public: - Fl_Menu_Item* subtypes() FL_OVERRIDE {return 0;} - const char* type_name() FL_OVERRIDE {return "Submenu";} - const char* alt_type_name() FL_OVERRIDE {return "fltk::ItemGroup";} - int can_have_children() const FL_OVERRIDE {return 1;} - int is_button() const FL_OVERRIDE {return 0;} // disable shortcut - Fl_Type* make(Strategy strategy) FL_OVERRIDE; - // changes to submenu must propagate up so build_menu is called - // on the parent Fl_Menu_Type: - void add_child(Fl_Type*a, Fl_Type*b) FL_OVERRIDE {parent->add_child(a,b);} - void move_child(Fl_Type*a, Fl_Type*b) FL_OVERRIDE {parent->move_child(a,b);} - void remove_child(Fl_Type*a) FL_OVERRIDE {parent->remove_child(a);} - ID id() const FL_OVERRIDE { return ID_Submenu; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Submenu) ? true : super::is_a(inID); } -}; - -// ----------------------------------------------------------------------------- - -/** - \brief Base class for all widgets that can have a pulldown menu attached. - Widgets with this type can be derived from Fl_Menu_ or from - Fl_Group (Fl_Input_Choice). - */ -class Fl_Menu_Manager_Type : public Fl_Widget_Type -{ - typedef Fl_Widget_Type super; -public: - void ideal_size(int &w, int &h) FL_OVERRIDE { - h = layout->textsize_not_null() + 8; - w = layout->textsize_not_null() * 6 + 8; - Fd_Snap_Action::better_size(w, h); - } - int can_have_children() const FL_OVERRIDE {return 1;} - int menusize; - virtual void build_menu() = 0; - Fl_Menu_Manager_Type() : Fl_Widget_Type() {menusize = 0;} - void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE { build_menu(); } - void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE { build_menu(); } - void remove_child(Fl_Type*) FL_OVERRIDE { build_menu();} - Fl_Type* click_test(int x, int y) FL_OVERRIDE = 0; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void copy_properties() FL_OVERRIDE = 0; - ID id() const FL_OVERRIDE { return ID_Menu_Manager_; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Menu_Manager_) ? true : super::is_a(inID); } -}; - -/** - \brief Manage the composite widget Input Choice. - \note Input Choice is a composite window, so `o` will be pointing to a widget - derived from Fl_Group. All menu related methods from Fl_Menu_Trait_Type must - be virtual and must be reimplemented here (click_test, build_menu, textstuff). - */ -class Fl_Input_Choice_Type : public Fl_Menu_Manager_Type -{ - typedef Fl_Menu_Manager_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Input_Choice *myo = (Fl_Input_Choice*)(w==4 ? ((Fl_Widget_Type*)this->factory)->o : this->o); - switch (w) { - case 4: - case 0: f = (Fl_Font)myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; - case 1: myo->textfont(f); break; - case 2: myo->textsize(s); break; - case 3: myo->textcolor(c); break; - } - return 1; - } -public: - ~Fl_Input_Choice_Type() { - if (menusize) delete[] (Fl_Menu_Item*)(((Fl_Input_Choice*)o)->menu()); - } - const char *type_name() FL_OVERRIDE {return "Fl_Input_Choice";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::ComboBox";} - Fl_Type* click_test(int,int) FL_OVERRIDE; - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - Fl_Input_Choice *myo = new Fl_Input_Choice(X,Y,W,H,"input choice:"); - myo->menu(dummymenu); - myo->value("input"); - return myo; - } - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Input_Choice_Type();} - void build_menu() FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Input_Choice; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Input_Choice) ? true : super::is_a(inID); } - void copy_properties() FL_OVERRIDE; -}; - -/** - \brief Base class to handle widgets that are derived from Fl_Menu_. - */ -class Fl_Menu_Base_Type : public Fl_Menu_Manager_Type -{ - typedef Fl_Menu_Manager_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Menu_ *myo = (Fl_Menu_*)(w==4 ? ((Fl_Widget_Type*)this->factory)->o : this->o); - switch (w) { - case 4: - case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; - case 1: myo->textfont(f); break; - case 2: myo->textsize(s); break; - case 3: myo->textcolor(c); break; - } - return 1; - } -public: - int can_have_children() const FL_OVERRIDE {return 1;} - void build_menu() FL_OVERRIDE; - ~Fl_Menu_Base_Type() { - if (menusize) delete[] (Fl_Menu_Item*)(((Fl_Menu_*)o)->menu()); - } - Fl_Type* click_test(int x, int y) FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Menu_; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Menu_) ? true : super::is_a(inID); } -}; - -extern Fl_Menu_Item button_type_menu[]; - -/** - \brief Make Menu Button widgets. - */ -class Fl_Menu_Button_Type : public Fl_Menu_Base_Type -{ - typedef Fl_Menu_Base_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE {return button_type_menu;} -public: - const char *type_name() FL_OVERRIDE {return "Fl_Menu_Button";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::MenuButton";} - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - return new Fl_Menu_Button(X,Y,W,H,"menu");} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Menu_Button_Type();} - ID id() const FL_OVERRIDE { return ID_Menu_Button; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Menu_Button) ? true : super::is_a(inID); } -}; - - -/** - \brief Manage Choice type menu widgets. - */ -class Fl_Choice_Type : public Fl_Menu_Base_Type -{ - typedef Fl_Menu_Base_Type super; -public: - const char *type_name() FL_OVERRIDE {return "Fl_Choice";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::Choice";} - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE { - Fl_Choice *myo = new Fl_Choice(X,Y,W,H,"choice:"); - myo->menu(dummymenu); - return myo; - } - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Choice_Type();} - ID id() const FL_OVERRIDE { return ID_Choice; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Choice) ? true : super::is_a(inID); } -}; - - -/** - \brief Manage Menubar widgets. - */ -class Fl_Menu_Bar_Type : public Fl_Menu_Base_Type -{ - typedef Fl_Menu_Base_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE {return menu_bar_type_menu;} -public: - Fl_Menu_Bar_Type(); - ~Fl_Menu_Bar_Type() FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "Fl_Menu_Bar";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::MenuBar";} - Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE {return new Fl_Menu_Bar(X,Y,W,H);} - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Menu_Bar_Type();} - void write_static(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; -// void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - ID id() const FL_OVERRIDE { return ID_Menu_Bar; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Menu_Bar) ? true : super::is_a(inID); } - bool is_sys_menu_bar(); - const char *sys_menubar_name(); - const char *sys_menubar_proxy_name(); -protected: - char *_proxy_name; -}; - - -#endif // _FLUID_FL_MENU_TYPE_H diff --git a/fluid/nodes/Fl_Type.cxx b/fluid/nodes/Fl_Type.cxx deleted file mode 100644 index 8f078e06e..000000000 --- a/fluid/nodes/Fl_Type.cxx +++ /dev/null @@ -1,1338 +0,0 @@ -// -// Widget type code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/// \defgroup fl_type Basic Node for all Widgets and Functions -/// \{ - -/** \class Fl_Type - Each object described by Fluid is one of these objects. They - are all stored in a double-linked list. - - The "type" of the object is covered by the virtual functions. - There will probably be a lot of these virtual functions. - - The type browser is also a list of these objects, but they - are "factory" instances, not "real" ones. These objects exist - only so the "make" method can be called on them. They are - not in the linked list and are not written to files or - copied or otherwise examined. - - The Fl_Type inheritance is currently: - --+-- Fl_Type - +-- Fl_Function_Type - +-- Fl_Code_Type - +-- Fl_CodeBlock_Type - +-+ Fl_Decl_Type - | +-- Fl_Data - +-- Fl_DeclBlock_Type - +-- Fl_Comment_Type - +-- Fl_Class_Type - +-+ Fl_Widget_Type, 'o' points to a class derived from Fl_Widget - +-+ Fl_Browser_Base_Type, 'o' is Fl_Browser - | +-+ Fl_Browser - | | +-- Fl_File_Browser - | +-- Fl_Check_Browser - +-- Fl_Tree_Type - +-- Fl_Help_View_Type - +-+ Fl_Valuator_Type, 'o' is Fl_Valuator_ - | +-- Fl_Counter_Type - | +-- Fl_Adjuster_Type - | +-- Fl_Dial_Type - | +-- Fl_Roller_Type - | +-- Fl_Slider_Type - | +-- Fl_Value_Input_Type - | +-- Fl_Value_Output_Type - +-+ Fl_Input_Type - | +-- Fl_Output_Type - +-+ Fl_Text_Display_Type - | +-- Fl_Text_Editor_Type - +-- Fl_Terminal_Type - +-- Fl_Box_Type - +-- Fl_Clock_Type - +-- Fl_Progress_Type - +-- Fl_Spinner_Type - +-+ Fl_Group_Type - | +-- Fl_Pack_Type - | +-- Fl_Flex_Type - | +-- Fl_Grid_Type - | +-- Fl_Table_Type - | +-- Fl_Tabs_Type - | +-- Fl_Scroll_Type - | +-- Fl_Tile_Type - | +-- Fl_Wizard_Type - | +-+ Fl_Window_Type - | +-- Fl_Widget_Class_Type - +-+ Fl_Menu_Manager_Type, 'o' is based on Fl_Widget - | +-+ Fl_Menu_Base_Type, 'o' is based on Fl_Menu_ - | | +-- Fl_Menu_Button_Type - | | +-- Fl_Choice_Type - | | +-- Fl_Menu_Bar_Type - | +-- Fl_Input_Choice_Type, 'o' is based on Fl_Input_Choice which is Fl_Group - +-+ Fl_Button_Type - +-- Fl_Return_Button_Type - +-- Fl_Repeat_Button_Type - +-- Fl_Light_Button_Type - +-- Fl_Check_Button_Type - +-- Fl_Round_Button_Type - +-+ Fl_Menu_Item_Type, 'o' is derived from Fl_Button in FLUID - +-- Fl_Radio_Menu_Item_Type - +-- Fl_Checkbox_Menu_Item_Type - +-- Fl_Submenu_Item_Type - -*/ - -#include "nodes/Fl_Type.h" - -#include "app/fluid.h" -#include "app/project.h" -#include "app/Fd_Snap_Action.h" -#include "app/shell_command.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "nodes/Fl_Function_Type.h" -#include "nodes/Fl_Widget_Type.h" -#include "nodes/Fl_Window_Type.h" -#include "nodes/Fl_Group_Type.h" -#include "rsrcs/pixmaps.h" -#include "widgets/Node_Browser.h" - -#include -#include -#include -#include "../src/flstring.h" - -#include -#include - -// ---- global variables - -Fl_Type *Fl_Type::first = NULL; -Fl_Type *Fl_Type::last = NULL; -Fl_Type *Fl_Type::current = NULL; -Fl_Type *Fl_Type::current_dnd = NULL; -int Fl_Type::allow_layout = 0; - -Fl_Type *in_this_only; // set if menu popped-up in window - - -// ---- various functions - -#if 0 -#ifndef NDEBUG -/** - Print the current project tree to stderr. - */ -void print_project_tree() { - fprintf(stderr, "---- %s --->\n", g_project.projectfile_name().c_str()); - for (Fl_Type *t = Fl_Type::first; t; t = t->next) { - for (int i = t->level; i > 0; i--) - fprintf(stderr, ". "); - fprintf(stderr, "%s\n", subclassname(t)); - } -} -#endif - -#ifndef NDEBUG -/** - Check the validity of the project tree. - - Write problems with the project tree to stderr. - - \return true if the project tree is valid - */ -bool validate_project_tree() { - // Validate `first` and `last` - if (Fl_Type::first == NULL) { - if (Fl_Type::last == NULL) { - return true; - } else { - fprintf(stderr, "ERROR: `first` is NULL, but `last` is not!\n"); - return false; - } - } - if (Fl_Type::last == NULL) { - fprintf(stderr, "ERROR: `last` is NULL, but `first` is not!\n"); - return false; - } - // Validate the branch linkage, parent links, etc. - return validate_branch(Fl_Type::first); -} -#endif - -#ifndef NDEBUG -/** - Check the validity of a Type branch that is not connected to the project. - - Write problems with the branch to stderr. - - \param[in] root the first node in a branch - \return true if the branch is correctly separated and valid - */ -bool validate_independent_branch(class Fl_Type *root) { - // Make sure that `first` and `last` do not point at any node in this branch - if (Fl_Type::first) { - for (Fl_Type *t = root; t; t = t->next) { - if (Fl_Type::first == t) { - fprintf(stderr, "ERROR: Branch is not independent, `first` is pointing to branch member!\n"); - return false; - } - } - } - if (Fl_Type::last) { - for (Fl_Type *t = root; t; t = t->next) { - if (Fl_Type::last == t) { - fprintf(stderr, "ERROR: Branch is not independent, `last` is pointing to branch member!\n"); - return false; - } - } - } - // Validate the branch linkage, parent links, etc. - return validate_branch(root); -} -#endif - -#ifndef NDEBUG -/** - Check the validity of a Type branch. - - Write problems with the branch to stderr. - - \param[in] root the first node in a branch - \return true if the branch is valid - */ -bool validate_branch(class Fl_Type *root) { - // Only check real branches - if (!root) { - fprintf(stderr, "WARNING: Branch is empty!\n"); - return false; - } - // Check relation between this and next node - for (Fl_Type *t = root; t; t = t->next) { - if (t->level < root->level) { - fprintf(stderr, "ERROR: Node in tree is above root level!\n"); - return false; - } - if (t->next) { - // Make sure that all `next` types have the `prev` member link back - if (t->next->prev != t) { - fprintf(stderr, "ERROR: Doubly linked list broken!\n"); - return false; - } - if (t->next->level > t->level) { - // Validate `level` changes - if (t->next->level - t->level > 1) { - fprintf(stderr, "ERROR: Child level increment greater than one!\n"); - return false; - } - // Ensure that this node can actually have children - if (!t->can_have_children()) { - fprintf(stderr, "ERROR: This parent must not have children!\n"); - return false; - } - } - } - // Validate the `parent` entry - for (Fl_Type *p = t->prev; ; p = p->prev) { - if (p == NULL) { - if (t->parent != NULL) { - fprintf(stderr, "ERROR: `parent` pointer should be NULL!\n"); - return false; - } - break; - } - if (p->level < t->level) { - if (t->parent != p) { - fprintf(stderr, "ERROR: `parent` points to wrong parent!\n"); - return false; - } - break; - } - } - } - return true; -} -#endif -#endif - -void select_all_cb(Fl_Widget *,void *) { - Fl_Type *p = Fl_Type::current ? Fl_Type::current->parent : 0; - if (in_this_only) { - Fl_Type *t = p; - for (; t && t != in_this_only; t = t->parent) {/*empty*/} - if (t != in_this_only) p = in_this_only; - } - for (;;) { - if (p) { - int foundany = 0; - for (Fl_Type *t = p->next; t && t->level>p->level; t = t->next) { - if (!t->new_selected) {widget_browser->select(t,1,0); foundany = 1;} - } - if (foundany) break; - p = p->parent; - } else { - for (Fl_Type *t = Fl_Type::first; t; t = t->next) - widget_browser->select(t,1,0); - break; - } - } - selection_changed(p); -} - -void select_none_cb(Fl_Widget *,void *) { - Fl_Type *p = Fl_Type::current ? Fl_Type::current->parent : 0; - if (in_this_only) { - Fl_Type *t = p; - for (; t && t != in_this_only; t = t->parent) {/*empty*/} - if (t != in_this_only) p = in_this_only; - } - for (;;) { - if (p) { - int foundany = 0; - for (Fl_Type *t = p->next; t && t->level>p->level; t = t->next) { - if (t->new_selected) {widget_browser->select(t,0,0); foundany = 1;} - } - if (foundany) break; - p = p->parent; - } else { - for (Fl_Type *t = Fl_Type::first; t; t = t->next) - widget_browser->select(t,0,0); - break; - } - } - selection_changed(p); -} - -/** - Callback to move all selected items before their previous unselected sibling. - */ -void earlier_cb(Fl_Widget*,void*) { - Fl_Type *f; - int mod = 0; - for (f = Fl_Type::first; f; ) { - Fl_Type* nxt = f->next; - if (f->selected) { - Fl_Type* g; - for (g = f->prev; g && g->level > f->level; g = g->prev) {/*empty*/} - if (g && g->level == f->level && !g->selected) { - if (!mod) undo_checkpoint(); - f->move_before(g); - if (f->parent) f->parent->layout_widget(); - mod = 1; - } - } - f = nxt; - } - if (mod) set_modflag(1); - widget_browser->display(Fl_Type::current); - widget_browser->rebuild(); -} - -/** - Callback to move all selected items after their next unselected sibling. - */ -void later_cb(Fl_Widget*,void*) { - Fl_Type *f; - int mod = 0; - for (f = Fl_Type::last; f; ) { - Fl_Type* prv = f->prev; - if (f->selected) { - Fl_Type* g; - for (g = f->next; g && g->level > f->level; g = g->next) {/*empty*/} - if (g && g->level == f->level && !g->selected) { - if (!mod) undo_checkpoint(); - g->move_before(f); - if (f->parent) f->parent->layout_widget(); - mod = 1; - } - } - f = prv; - } - if (mod) set_modflag(1); - widget_browser->display(Fl_Type::current); - widget_browser->rebuild(); -} - -/** \brief Delete all children of a Type. - */ -static void delete_children(Fl_Type *p) { - Fl_Type *f; - // find all types following p that are higher in level, effectively finding - // the last child of the last child - for (f = p; f && f->next && f->next->level > p->level; f = f->next) {/*empty*/} - // now loop back up to p, deleting all children on the way - for (; f != p; ) { - Fl_Type *g = f->prev; - delete f; - f = g; - } -} - -/** Delete all nodes in the Types tree and reset project settings, or delete selected nodes. - Also calls the browser to refresh. - \note Please refactor this into two separate methods of Fluid_Project. - \param[in] selected_only if set, delete only the selected widgets and - don't reset the project. - */ -void delete_all(int selected_only) { - if (widget_browser) { - if (selected_only) - widget_browser->save_scroll_position(); - widget_browser->new_list(); - } - for (Fl_Type *f = Fl_Type::first; f;) { - if (f->selected || !selected_only) { - delete_children(f); - Fl_Type *g = f->next; - delete f; - f = g; - } else { - f = f->next; - } - } - if(!selected_only) { - // reset the setting for the external shell command - if (g_shell_config) { - g_shell_config->clear(FD_STORE_PROJECT); - g_shell_config->rebuild_shell_menu(); - g_shell_config->update_settings_dialog(); - } - if (widget_browser) { - widget_browser->hposition(0); - widget_browser->vposition(0); - } - g_layout_list.remove_all(FD_STORE_PROJECT); - g_layout_list.current_suite(0); - g_layout_list.current_preset(0); - g_layout_list.update_dialogs(); - } - selection_changed(0); - if (widget_browser) { - if (selected_only) - widget_browser->restore_scroll_position(); - widget_browser->rebuild(); - } -} - -/** Update a string. - Replace a string pointer with new value, strips leading/trailing blanks. - As a side effect, this call also sets the mod flags. - \param[in] n new string, can be NULL - \param[out] p update this pointer, possibly reallocate memory - \param[in] nostrip if set, do not strip leading and trailing spaces and tabs - \return 1 if the string in p changed - */ -int storestring(const char *n, const char * & p, int nostrip) { - if (n == p) return 0; - undo_checkpoint(); - int length = 0; - if (n) { // see if blank, strip leading & trailing blanks - if (!nostrip) while (isspace((int)(unsigned char)*n)) n++; - const char *e = n + strlen(n); - if (!nostrip) while (e > n && isspace((int)(unsigned char)*(e-1))) e--; - length = int(e-n); - if (!length) n = 0; - } - if (n == p) return 0; - if (n && p && !strncmp(n,p,length) && !p[length]) return 0; - if (p) free((void *)p); - if (!n || !*n) { - p = 0; - } else { - char *q = (char *)malloc(length+1); - strlcpy(q,n,length+1); - p = q; - } - set_modflag(1); - return 1; -} - -/** Update the `visible` flag for `p` and all its descendants. - \param[in] p start here and update all descendants - */ -void update_visibility_flag(Fl_Type *p) { - Fl_Type *t = p; - for (;;) { - if (t->parent) t->visible = t->parent->visible && !t->parent->folded_; - else t->visible = 1; - t = t->next; - if (!t || t->level <= p->level) break; - } -} - -// ---- implementation of Fl_Type - -/** \var Fl_Type *Fl_Type::parent - Link to the parent node in the tree structure. - Used for simulating a tree structure via a doubly linked list. - */ -/** \var Fl_Type *Fl_Type::level - Zero based depth of the node within the tree structure. - Level is used to emulate a tree structure: the first node with a lower - level in the prev list would be the parent of this node. If the next member - has a higher level value, it is this nodes first child. At the same level, - it would be the first sibling. - */ -/** \var Fl_Type *Fl_Type::next - Points to the next node in the doubly linked list. - If this is NULL, we are at the end of the list. - Used for simulating a tree structure via a doubly linked list. - */ -/** \var Fl_Type *Fl_Type::prev - Link to the next node in the tree structure. - If this is NULL, we are at the beginning of the list. - Used for simulating a tree structure via a doubly linked list. - */ - -/** - Constructor and base for any node in the widget tree. - */ -Fl_Type::Fl_Type() : - name_(NULL), - label_(NULL), - callback_(NULL), - user_data_(NULL), - user_data_type_(NULL), - comment_(NULL), - uid_(0), - parent(NULL), - new_selected(0), - selected(0), - folded_(0), - visible(0), - level(0), - next(NULL), prev(NULL), - factory(NULL), - code_static_start(-1), code_static_end(-1), - code1_start(-1), code1_end(-1), - code2_start(-1), code2_end(-1), - header1_start(-1), header1_end(-1), - header2_start(-1), header2_end(-1), - header_static_start(-1), header_static_end(-1), - proj1_start(-1), proj1_end(-1), - proj2_start(-1), proj2_end(-1) -{ -} - - -/** - Destructor for any node in the tree. - - The destructor removes itself from the doubly linked list. This is dangerous, - because the node does not know if it is part of the widget tree, or if it is - in a separate tree. We try to take care of that as well as possible. - */ -Fl_Type::~Fl_Type() { - // warning: destructor only works for widgets that have been add()ed. - if (prev) prev->next = next; // else first = next; // don't do that! The Type may not be part of the main list - if (next) next->prev = prev; // else last = prev; - if (Fl_Type::last == this) Fl_Type::last = prev; - if (Fl_Type::first == this) Fl_Type::first = next; - if (current == this) current = NULL; - if (parent) parent->remove_child(this); - if (name_) free((void*)name_); - if (label_) free((void*)label_); - if (callback_) free((void*)callback_); - if (user_data_) free((void*)user_data_); - if (user_data_type_) free((void*)user_data_type_); - if (comment_) free((void*)comment_); -} - -// Return the previous sibling in the tree structure or NULL. -Fl_Type *Fl_Type::prev_sibling() { - Fl_Type *n; - for (n = prev; n && n->level > level; n = n->prev) ; - if (n && (n->level == level)) - return n; - return 0; -} - -// Return the next sibling in the tree structure or NULL. -Fl_Type *Fl_Type::next_sibling() { - Fl_Type *n; - for (n = next; n && n->level > level; n = n->next) ; - if (n && (n->level == level)) - return n; - return 0; -} - -// Return the first child or NULL -Fl_Type *Fl_Type::first_child() { - Fl_Type *n = next; - if (n->level > level) - return n; - return NULL; -} - -// Generate a descriptive text for this item, to put in browser & window titles -const char* Fl_Type::title() { - const char* c = name(); - if (c) - return c; - return type_name(); -} - -/** - Return the window that contains this widget. - \return NULL if this is not a widget. - */ -Fl_Window_Type *Fl_Type::window() { - if (!is_widget()) - return NULL; - for (Fl_Type *t = this; t; t=t->parent) - if (t->is_a(ID_Window)) - return (Fl_Window_Type*)t; - return NULL; -} - -/** - Return the group that contains this widget. - \return NULL if this is not a widget. - */ -Fl_Group_Type *Fl_Type::group() { - if (!is_widget()) - return NULL; - for (Fl_Type *t = this; t; t=t->parent) - if (t->is_a(ID_Group)) - return (Fl_Group_Type*)t; - return NULL; -} - -/** - Add this list/tree of widgets as a new last child of p. - - \c this must not be part of the widget browser. \c p however must be in the - widget_browser, so \c Fl_Type::first and \c Fl_Type::last are valid for \c p. - - This methods updates the widget_browser. - - \param[in] p insert \c this tree as a child of \c p - \param[in] strategy is Strategy::AS_LAST_CHILD or Strategy::AFTER_CURRENT - */ -void Fl_Type::add(Fl_Type *anchor, Strategy strategy) { -#if 0 -#ifndef NDEBUG - // print_project_tree(); - // fprintf(stderr, "Validating project\n"); - validate_project_tree(); - // fprintf(stderr, "Validating branch\n"); - validate_independent_branch(this); -#endif -#endif - - Fl_Type *target = NULL; // insert self before target node, if NULL, insert last - Fl_Type *target_parent = NULL; // this will be the new parent for branch - int target_level = 0; // adjust self to this new level - - // Find the node after our insertion position - switch (strategy.placement()) { - case Strategy::AS_FIRST_CHILD: - default: - if (anchor == NULL) { - target = Fl_Type::first; - } else { - target = anchor->next; - target_level = anchor->level + 1; - target_parent = anchor; - } - break; - case Strategy::AS_LAST_CHILD: - if (anchor == NULL) { - /* empty */ - } else { - for (target = anchor->next; target && target->level > anchor->level; target = target->next) {/*empty*/} - target_level = anchor->level + 1; - target_parent = anchor; - } - break; - case Strategy::AFTER_CURRENT: - if (anchor == NULL) { - target = Fl_Type::first; - } else { - for (target = anchor->next; target && target->level > anchor->level; target = target->next) {/*empty*/} - target_level = anchor->level; - target_parent = anchor->parent; - } - break; - } - - - // Find the last node of our tree - Fl_Type *end = this; - while (end->next) end = end->next; - - // Everything is prepared, now insert ourself in front of the target node - undo_checkpoint(); - - // Walk the tree to update parent pointers and levels - int source_level = level; - for (Fl_Type *t = this; t; t = t->next) { - t->level += (target_level-source_level); - if (t->level == target_level) - t->parent = target_parent; - } - - // Now link ourselves and our children before 'target', or last, if 'target' is NULL - if (target) { - prev = target->prev; - target->prev = end; - end->next = target; - } else { - prev = Fl_Type::last; - end->next = NULL; - Fl_Type::last = end; - } - if (prev) { - prev->next = this; - } else { - Fl_Type::first = this; - } - -#if 0 - { // make sure that we have no duplicate uid's - Fl_Type *tp = this; - do { - tp->set_uid(tp->uid_); - tp = tp->next; - } while (tp!=end && tp!=NULL); - } -#endif - - // Give the widgets in our tree a chance to update themselves - for (Fl_Type *t = this; t && t!=end->next; t = t->next) { - if (target_parent && (t->level == target_level)) - target_parent->add_child(t, 0); - update_visibility_flag(t); - } - - set_modflag(1); - widget_browser->redraw(); - -#if 0 -#ifndef NDEBUG - // fprintf(stderr, "Validating project after adding branch\n"); - validate_project_tree(); -#endif -#endif -} - -/** - Add `this` list/tree of widgets as a new sibling before `g`. - - `This` is not part of the widget browser. `g` must be in the - widget_browser, so `Fl_Type::first` and `Fl_Type::last` are valid for `g . - - This methods updates the widget_browser. - - \param[in] g pointer to a node within the tree - */ -void Fl_Type::insert(Fl_Type *g) { - // 'this' is not in the Node_Browser, so we must run the linked list to find the last entry - Fl_Type *end = this; - while (end->next) end = end->next; - // 'this' will get the same parent as 'g' - parent = g->parent; - // run the list again to set the future node levels - int newlevel = g->level; - visible = g->visible; - for (Fl_Type *t = this->next; t; t = t->next) t->level += newlevel-level; - level = newlevel; - // insert this in the list before g - prev = g->prev; - if (prev) prev->next = this; else first = this; - end->next = g; - g->prev = end; - update_visibility_flag(this); - { // make sure that we have no duplicate uid's - Fl_Type *tp = this; - do { - tp->set_uid(tp->uid_); - tp = tp->next; - } while (tp!=end && tp!=NULL); - } - // tell parent that it has a new child, so it can update itself - if (parent) parent->add_child(this, g); - widget_browser->redraw(); -} - -// Return message number for I18N... -int Fl_Type::msgnum() { - int count; - Fl_Type *p; - - for (count = 0, p = this; p;) { - if (p->label()) count ++; - if (p != this && p->is_widget() && ((Fl_Widget_Type *)p)->tooltip()) count ++; - - if (p->prev) p = p->prev; - else p = p->parent; - } - - return count; -} - -/** - Remove this node and all its children from the parent node. - - This does not delete anything. The resulting list//tree will no longer be in - the widget_browser, so \c Fl_Type::first and \c Fl_Type::last do not apply - to it. - - \return the node that follows this node after the operation; can be NULL - */ -Fl_Type *Fl_Type::remove() { - // find the last child of this node - Fl_Type *end = this; - for (;;) { - if (!end->next || end->next->level <= level) - break; - end = end->next; - } - // unlink this node from the previous one - if (prev) - prev->next = end->next; - else - first = end->next; - // unlink the last child from their next node - if (end->next) - end->next->prev = prev; - else - last = prev; - Fl_Type *r = end->next; - prev = end->next = 0; - // allow the parent to update changes in the UI - if (parent) parent->remove_child(this); - parent = 0; - // tell the widget_browser that we removed some nodes - widget_browser->redraw(); - selection_changed(0); - return r; -} - -void Fl_Type::name(const char *n) { - int nostrip = is_a(ID_Comment); - if (storestring(n,name_,nostrip)) { - if (visible) widget_browser->redraw(); - } -} - -void Fl_Type::label(const char *n) { - if (storestring(n,label_,1)) { - setlabel(label_); - if (visible && !name_) widget_browser->redraw(); - } -} - -void Fl_Type::callback(const char *n) { - storestring(n,callback_); -} - -void Fl_Type::user_data(const char *n) { - storestring(n,user_data_); -} - -void Fl_Type::user_data_type(const char *n) { - storestring(n,user_data_type_); -} - -void Fl_Type::comment(const char *n) { - if (storestring(n,comment_,1)) { - if (visible) widget_browser->redraw(); - } -} - -void Fl_Type::open() { - printf("Open of '%s' is not yet implemented\n",type_name()); -} - -// returns pointer to whatever is after f & children - -/** - Move this node (and its children) into list before g. - Both `this` and `g` must be in the widget browser. - The caller must make sure that the widget browser is rebuilt correctly. - \param[in] g move \c this tree before \c g - */ -void Fl_Type::move_before(Fl_Type* g) { - if (level != g->level) printf("move_before levels don't match! %d %d\n", - level, g->level); - // Find the last child in the list - Fl_Type *n; - for (n = next; n && n->level > level; n = n->next) ; - if (n == g) return; - // now link this tree before g - Fl_Type *l = n ? n->prev : Fl_Type::last; - prev->next = n; - if (n) n->prev = prev; else Fl_Type::last = prev; - prev = g->prev; - l->next = g; - if (prev) prev->next = this; else Fl_Type::first = this; - g->prev = l; - // tell parent that it has a new child, so it can update itself - if (parent && is_widget()) parent->move_child(this,g); -} - - -// write a widget and all its children: -void Fl_Type::write(fld::io::Project_Writer &f) { - if (f.write_codeview()) proj1_start = (int)ftell(f.file()) + 1; - if (f.write_codeview()) proj2_start = (int)ftell(f.file()) + 1; - f.write_indent(level); - f.write_word(type_name()); - - if (is_class()) { - const char * p = ((Fl_Class_Type*)this)->prefix(); - if (p && strlen(p)) - f.write_word(p); - } - - f.write_word(name()); - f.write_open(); - write_properties(f); - if (parent) parent->write_parent_properties(f, this, true); - f.write_close(level); - if (f.write_codeview()) proj1_end = (int)ftell(f.file()); - if (!can_have_children()) { - if (f.write_codeview()) proj2_end = (int)ftell(f.file()); - return; - } - // now do children: - f.write_open(); - Fl_Type *child; - for (child = next; child && child->level > level; child = child->next) - if (child->level == level+1) child->write(f); - if (f.write_codeview()) proj2_start = (int)ftell(f.file()) + 1; - f.write_close(level); - if (f.write_codeview()) proj2_end = (int)ftell(f.file()); -} - -void Fl_Type::write_properties(fld::io::Project_Writer &f) { - // repeat this for each attribute: - if (g_project.write_mergeback_data && uid_) { - f.write_word("uid"); - f.write_string("%04x", uid_); - } - if (label()) { - f.write_indent(level+1); - f.write_word("label"); - f.write_word(label()); - } - if (user_data()) { - f.write_indent(level+1); - f.write_word("user_data"); - f.write_word(user_data()); - } - if (user_data_type()) { - f.write_word("user_data_type"); - f.write_word(user_data_type()); - } - if (callback()) { - f.write_indent(level+1); - f.write_word("callback"); - f.write_word(callback()); - } - if (comment()) { - f.write_indent(level+1); - f.write_word("comment"); - f.write_word(comment()); - } - if (can_have_children() && !folded_) f.write_word("open"); - if (selected) f.write_word("selected"); -} - -void Fl_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"uid")) { - const char *hex = f.read_word(); - int x = 0; - if (hex) - x = sscanf(hex, "%04x", &x); - set_uid(x); - } else if (!strcmp(c,"label")) - label(f.read_word()); - else if (!strcmp(c,"user_data")) - user_data(f.read_word()); - else if (!strcmp(c,"user_data_type")) - user_data_type(f.read_word()); - else if (!strcmp(c,"callback")) - callback(f.read_word()); - else if (!strcmp(c,"comment")) - comment(f.read_word()); - else if (!strcmp(c,"open")) - folded_ = 0; - else if (!strcmp(c,"selected")) - select(this,1); - else if (!strcmp(c,"parent_properties")) - if (parent) { - const char *cc = f.read_word(1); - if (strcmp(cc, "{")==0) { - for (;;) { - cc = f.read_word(); - if (!cc || cc[0]==0 || strcmp(cc, "}")==0) break; - parent->read_parent_property(f, this, cc); - } - } else { - f.read_error("'parent_properties' must be followed by '{'"); - } - } else { - f.read_error("Types using 'parent_properties' must have a parent"); - f.read_word(); // skip the entire block (this should generate a warning) - } - else - f.read_error("Unknown property \"%s\"", c); -} - -/** Write parent properties into the child property list. - - Some widgets store information for every child they manage. For example, - Fl_Grid stores the row and column position of every child. This method stores - this information with the child, but it is read and written by the parent. - - Parent properties solve several issues. A child will keep parent properties - if copied from on grid into another. The parent does not have to keep lists - of properties that may diverge from the actual order or number of children. - And lastly, properties are read when they are actually needed and don't have - to be stored in some temporary array. - - Parent properties are written as their own block at the end of the child's - property list. The block starts with the `parent_properties` keyword, followed - by a list of property/value pairs. The order of properties is significant, - however individual properties can be left out. - - To avoid writing the `parent_properties` block unnecessarily, this method - should only generate it if `encapsulate` is set *and* the contained - properties are not at their default. - - Lastly, this method should call the super class to give it a chance to append - its own properties. - - \see Fl_Grid_Type::write_parent_properties(fld::io::Project_Writer &f, Fl_Type *child, bool encapsulate) - - \param[in] f the project file writer - \param[in] child write properties for this child, make sure it has the correct type - \param[in] encapsulate write the `parent_properties {}` block if true before writing any properties - */ -void Fl_Type::write_parent_properties(fld::io::Project_Writer &f, Fl_Type *child, bool encapsulate) { - (void)f; (void)child; (void)encapsulate; - // nothing to do here - // put the following code into your implementation of write_parent_properties - // if there are actual non-default properties to write - // if (encapsulate) { - // f.write_indent(level+2); - // f.write_string("parent_properties {"); - // } - // now write your properties as name/value pairs - // f.write_indent(level+3); - // f.write_string("location {%d %d}", cell->row(), cell->col()); - // give the super class a chance to write its properties as well - // super::write_parent_properties(f, child, false); - // close the encapsulation - // if (encapsulate) { - // f.write_indent(level+2); - // f.write_string("}"); - // } -} - -/** Read one parent per-child property. - - A parent widget can store properties for every child that it manages. This - method reads back those properties. This function is virtual, so if a Type - does not support a property, it will propagate to its super class. - - \see Fl_Type::write_parent_properties(fld::io::Project_Writer &f, Fl_Type *child, bool encapsulate) - \see Fl_Grid_Type::read_parent_property(fld::io::Project_Reader &f, Fl_Type *child, const char *property) - - \param[in] f the project file writer - \param[in] child read properties for this child - \param[in] property the name of a property, or "}" when we reach the end of the list - */ -void Fl_Type::read_parent_property(fld::io::Project_Reader &f, Fl_Type *child, const char *property) { - (void)child; - f.read_error("Unknown parent property \"%s\"", property); -} - - -int Fl_Type::read_fdesign(const char*, const char*) {return 0;} - -/** - Write a comment into the header file. - \param[in] pre indent the comment by this string -*/ -void Fl_Type::write_comment_h(fld::io::Code_Writer& f, const char *pre) -{ - if (comment() && *comment()) { - f.write_h("%s/**\n", pre); - const char *s = comment(); - f.write_h("%s ", pre); - while(*s) { - if (*s=='\n') { - if (s[1]) { - f.write_h("\n%s ", pre); - } - } else { - f.write_h("%c", *s); // FIXME this is much too slow! - } - s++; - } - f.write_h("\n%s*/\n", pre); - } -} - -/** - Write a comment into the source file. -*/ -void Fl_Type::write_comment_c(fld::io::Code_Writer& f, const char *pre) -{ - if (comment() && *comment()) { - f.write_c("%s/**\n", pre); - const char *s = comment(); - if (*s && *s!='\n') - f.write_c("%s ", pre); - while(*s) { - if (*s=='\n') { - f.write_c("\n"); - if (s[1] && s[1]!='\n') { - f.write_c("%s ", pre); - } - } else { - f.write_c("%c", *s); // FIXME this is much too slow! - } - s++; - } - f.write_c("\n%s*/\n", pre); - } -} - -/** - Write a comment into the source file. -*/ -void Fl_Type::write_comment_inline_c(fld::io::Code_Writer& f, const char *pre) -{ - if (comment() && *comment()) { - const char *s = comment(); - if (strchr(s, '\n')==0L) { - // single line comment - if (pre) f.write_c("%s", pre); - f.write_c("// %s\n", s); - if (!pre) f.write_c("%s", f.indent_plus(1)); - } else { - f.write_c("%s/*\n", pre?pre:""); - if (*s && *s!='\n') { - if (pre) - f.write_c("%s ", pre); - else - f.write_c("%s ", f.indent_plus(1)); - } - while(*s) { - if (*s=='\n') { - f.write_c("\n"); - if (s[1] && s[1]!='\n') { - if (pre) - f.write_c("%s ", pre); - else - f.write_c("%s ", f.indent_plus(1)); - } - } else { - f.write_c("%c", *s); // FIXME this is much too slow! - } - s++; - } - if (pre) - f.write_c("\n%s */\n", pre); - else - f.write_c("\n%s */\n", f.indent_plus(1)); - if (!pre) - f.write_c("%s", f.indent_plus(1)); - } - } -} - -/** - Build widgets and dataset needed in live mode. - \return a widget pointer that the live mode initiator can 'show()' - \see leave_live_mode() -*/ -Fl_Widget *Fl_Type::enter_live_mode(int) { - return 0L; -} - -/** - Release all resources created when entering live mode. - \see enter_live_mode() -*/ -void Fl_Type::leave_live_mode() { -} - -/** - Copy all needed properties for this type into the live object. -*/ -void Fl_Type::copy_properties() { -} - -/** - Check whether callback \p cbname is declared anywhere else by the user. - - \b Warning: this just checks that the name is declared somewhere, - but it should probably also check that the name corresponds to a - plain function or a member function within the same class and that - the parameter types match. - */ -int Fl_Type::user_defined(const char* cbname) const { - for (Fl_Type* p = Fl_Type::first; p ; p = p->next) - if (p->is_a(ID_Function) && p->name() != 0) - if (strncmp(p->name(), cbname, strlen(cbname)) == 0) - if (p->name()[strlen(cbname)] == '(') - return 1; - return 0; -} - -const char *Fl_Type::callback_name(fld::io::Code_Writer& f) { - if (is_name(callback())) return callback(); - return f.unique_id(this, "cb", name(), label()); -} - -/** - \brief Return the class name if this type is inside a Class or Widget Class. - - This methods traverses up the hirarchy to find out if this Type is located - inside a Class or Widget Class. It then return the name of that class. If - need_nest is set, class_name searches all the way up the tree and concatenates - the names of classes within classes, separated by a "::". - - \param need_nest if clear, search up one level to the first enclosing class. - If set, recurse all the way up to the top node. - \return the name of the enclosing class, or names of the enclosing classes - in a static buffe (don't call free), or NULL if this Type is not inside a class - */ -const char* Fl_Type::class_name(const int need_nest) const { - Fl_Type* p = parent; - while (p) { - if (p->is_class()) { - // see if we are nested in another class, we must fully-qualify name: - // this is lame but works... - const char* q = 0; - if(need_nest) q=p->class_name(need_nest); - if (q) { - static char s[256]; - if (q != s) strlcpy(s, q, sizeof(s)); - strlcat(s, "::", sizeof(s)); - strlcat(s, p->name(), sizeof(s)); - return s; - } - return p->name(); - } - p = p->parent; - } - return 0; -} - -/** - Check if this is inside a Fl_Class_Type or Fl_Widget_Class_Type. - \return true if any of the parents is Fl_Class_Type or Fl_Widget_Class_Type - */ -bool Fl_Type::is_in_class() const { - Fl_Type* p = parent; - while (p) { - if (p->is_class()) return true; - p = p->parent; - } - return false; -} - -void Fl_Type::write_static(fld::io::Code_Writer&) { -} - -void Fl_Type::write_static_after(fld::io::Code_Writer&) { -} - -void Fl_Type::write_code1(fld::io::Code_Writer& f) { - f.write_h("// Header for %s\n", title()); - f.write_c("// Code for %s\n", title()); -} - -void Fl_Type::write_code2(fld::io::Code_Writer&) { -} - -/** Set a uid that is unique within the project. - - Try to set the given id as the unique id for this node. If the suggested id - is 0, or it is already taken inside this project, we try another random id - until we find one that is unique. - - \param[in] suggested_uid the preferred uid for this node - \return the actualt uid that was given to the node - */ -unsigned short Fl_Type::set_uid(unsigned short suggested_uid) { - if (suggested_uid==0) - suggested_uid = (unsigned short)rand(); - for (;;) { - Fl_Type *tp = Fl_Type::first; - for ( ; tp; tp = tp->next) - if (tp!=this && tp->uid_==suggested_uid) - break; - if (tp==NULL) - break; - suggested_uid = (unsigned short)rand(); - } - uid_ = suggested_uid; - return suggested_uid; -} - -/** Find a node by its unique id. - - Every node in a type tree has an id that is unique for the current project. - Walk the tree and return the node with this uid. - - \param[in] uid any number between 0 and 65535 - \return the node with this uid, or NULL if not found - */ -Fl_Type *Fl_Type::find_by_uid(unsigned short uid) { - for (Fl_Type *tp = Fl_Type::first; tp; tp = tp->next) { - if (tp->uid_ == uid) return tp; - } - return NULL; -} - -/** Find a type node by using the codeview text positions. - - \param[in] text_type 0=source file, 1=header, 2=.fl project file - \param[in] crsr cursor position in text - \return the node we found or NULL - */ -Fl_Type *Fl_Type::find_in_text(int text_type, int crsr) { - for (Fl_Type *node = first; node; node = node->next) { - switch (text_type) { - case 0: - if (crsr >= node->code1_start && crsr < node->code1_end) return node; - if (crsr >= node->code2_start && crsr < node->code2_end) return node; - if (crsr >= node->code_static_start && crsr < node->code_static_end) return node; - break; - case 1: - if (crsr >= node->header1_start && crsr < node->header1_end) return node; - if (crsr >= node->header2_start && crsr < node->header2_end) return node; - if (crsr >= node->header_static_start && crsr < node->header_static_end) return node; - break; - case 2: - if (crsr >= node->proj1_start && crsr < node->proj1_end) return node; - if (crsr >= node->proj2_start && crsr < node->proj2_end) return node; - break; - } - } - return 0; -} - -/// \} - diff --git a/fluid/nodes/Fl_Type.h b/fluid/nodes/Fl_Type.h deleted file mode 100644 index e7dde3b39..000000000 --- a/fluid/nodes/Fl_Type.h +++ /dev/null @@ -1,323 +0,0 @@ -// -// Widget type header file for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2025 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef _FLUID_FL_TYPE_H -#define _FLUID_FL_TYPE_H - -#include "io/Code_Writer.h" - -#include -#include - -class Fl_Type; -class Fl_Group_Type; -class Fl_Window_Type; - -namespace fld { -namespace io { - -class Project_Reader; -class Project_Writer; - -} // namespace io -} // namespace fld - -/** - Declare where a new type is placed and how to create it. - - Placement can be as the first or last child of the anchor, or right after the - anchor. In most cases, the anchor is the last selected type node. - - If the source is FROM_USER, widgets may be created with default titles and - labels. Type created FROM_FILE will start with no label, so the label is set - correctly later. - - \see Fl_Type *Fl_..._Type::make(Strategy strategy) calls `add()` - Add single Type: - Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy, bool and_open) - Fl_Type *add_new_widget_from_user(const char *inName, Strategy strategy, bool and_open) - Fl_Type *add_new_widget_from_file(const char *inName, Strategy strategy) - Add a hierarchy of Types - void Fl_Type::add(Fl_Type *p, Strategy strategy) - int read_file(const char *filename, int merge, Strategy strategy) - Fl_Type *fld::io::Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, char skip_options) - int fld::io::Project_Reader::read_project(const char *filename, int merge, Strategy strategy) - */ -typedef struct Strategy { - enum Flags { - AS_FIRST_CHILD = 0x0000, - AS_LAST_CHILD = 0x0001, - AFTER_CURRENT = 0x0002, - PLACEMENT_MASK = 0x000f, - FROM_USER = 0x0000, - FROM_FILE = 0x0010, - SOURCE_MASK = 0x00f0, - FROM_FILE_AS_FIRST_CHILD = 0x0010, - FROM_FILE_AS_LAST_CHILD = 0x0011, - FROM_FILE_AFTER_CURRENT = 0x0012, - }; - Flags flags; - Strategy(Flags f) { flags = f; } - void placement(Flags f) { flags = (Flags)((flags & ~PLACEMENT_MASK) | (f & PLACEMENT_MASK)); } - Flags placement() { return (Flags)(flags & PLACEMENT_MASK); } - void source(Flags f) { flags = (Flags)((flags & ~SOURCE_MASK) | (f & SOURCE_MASK)); } - Flags source() { return (Flags)(flags & SOURCE_MASK); } -} Strategy; - -enum ID { - // administrative - ID_Base_, ID_Widget_, ID_Menu_Manager_, ID_Menu_, ID_Browser_, ID_Valuator_, - // non-widget - ID_Function, ID_Code, ID_CodeBlock, - ID_Decl, ID_DeclBlock, ID_Class, - ID_Widget_Class, ID_Comment, ID_Data, - // groups - ID_Window, ID_Group, ID_Pack, - ID_Flex, ID_Tabs, ID_Scroll, - ID_Tile, ID_Wizard, ID_Grid, - // buttons - ID_Button, ID_Return_Button, ID_Light_Button, - ID_Check_Button, ID_Repeat_Button, ID_Round_Button, - // valuators - ID_Slider, ID_Scrollbar, ID_Value_Slider, - ID_Adjuster, ID_Counter, ID_Spinner, - ID_Dial, ID_Roller, ID_Value_Input, ID_Value_Output, - // text - ID_Input, ID_Output, ID_Text_Editor, - ID_Text_Display, ID_File_Input, ID_Terminal, - // menus - ID_Menu_Bar, ID_Menu_Button, ID_Choice, - ID_Input_Choice, ID_Submenu, ID_Menu_Item, - ID_Checkbox_Menu_Item, ID_Radio_Menu_Item, - // browsers - ID_Browser, ID_Check_Browser, ID_File_Browser, - ID_Tree, ID_Help_View, ID_Table, - // misc - ID_Box, ID_Clock, ID_Progress, - ID_Max_ -}; - -void update_visibility_flag(Fl_Type *p); -void delete_all(int selected_only=0); -int storestring(const char *n, const char * & p, int nostrip=0); - -void select_all_cb(Fl_Widget *,void *); -void select_none_cb(Fl_Widget *,void *); -void earlier_cb(Fl_Widget*,void*); -void later_cb(Fl_Widget*,void*); - -#ifndef NDEBUG -void print_project_tree(); -bool validate_project_tree(); -bool validate_independent_branch(class Fl_Type *root); -bool validate_branch(class Fl_Type *root); -#endif - -/** - \brief This is the base class for all elements in the project tree. - - All widgets and other types in the project are derived from Fl_Types. They - are organized in a doubly linked list. Every Type also has depth information - to create a pseudo tree structure. To make walking up the tree faster, Type - also holds a pointer to the `parent` Type. - - Types can be identified using the builtin ID system that works like RTTI. The - method `id()` returns the exact type, and the method `is_a(ID)` returns true - if this is the exact type or derived from the type, and a dynamic cast will - work reliably. - - \todo it would be nice if we can handle multiple independent trees. To do that - we must remove static members like `first` and `last`. - - \todo add virtual methods to handle events, draw widgets, and draw overlays. - It may also make sense to have a virtual method that returns a boolean if - a specific type can be added as a child. - - \todo it may make sense to have a readable iterator class instead of relying - on pointer manipulation. Or use std in future releases. - */ -class Fl_Type { - /** Copy the label text to Widgets and Windows, does nothing in Type. */ - virtual void setlabel(const char *) { } // virtual part of label(char*) - -protected: - - Fl_Type(); - - /** Name of a widget, or code some non-widget Types. */ - const char *name_; - /** Label text of a widget. */ - const char *label_; - /** If it is just a word, it's the name of the callback function. Otherwise - it is the full callback C++ code. Can be NULL. */ - const char *callback_; - /** Widget user data field as C++ text. */ - const char *user_data_; - /** Widget user data type as C++ text, usually `void*` or `long`. */ - const char *user_data_type_; - /** Optional comment for every node in the graph. Visible in browser and - panels, and will also be copied to the source code. */ - const char *comment_; - /** a unique ID within the project */ - unsigned short uid_; - -public: // things that should not be public: - - /** Quick link to the parent Type instead of walking up the linked list. */ - Fl_Type *parent; - /** This type is rendered "selected" in the tree browser. */ - char new_selected; // browser highlight - /** Backup storage for selection if an error occurred during some operation - (see `haderror`). It seems that this is often confused with new_selected - which seems to hold the true and visible selection state. */ - char selected; // copied here by selection_changed() - char folded_; // if set, children are not shown in browser - char visible; // true if all parents are open - int level; // number of parents over this - static Fl_Type *first, *last; - Fl_Type *next, *prev; - Fl_Type *prev_sibling(); - Fl_Type *next_sibling(); - Fl_Type *first_child(); - - Fl_Type *factory; - const char *callback_name(fld::io::Code_Writer& f); - - // text positions of this type in code, header, and project file (see codeview) - int code_static_start, code_static_end; - int code1_start, code1_end; - int code2_start, code2_end; - int header1_start, header1_end; - int header2_start, header2_end; - int header_static_start, header_static_end; - int proj1_start, proj1_end; - int proj2_start, proj2_end; - -protected: - int user_defined(const char* cbname) const; - -public: - - virtual ~Fl_Type(); - virtual Fl_Type *make(Strategy strategy) = 0; - - Fl_Window_Type *window(); - Fl_Group_Type *group(); - - void add(Fl_Type *parent, Strategy strategy); - void insert(Fl_Type *n); // insert into list before n - Fl_Type* remove(); // remove from list - void move_before(Fl_Type*); // move before a sibling - - virtual const char *title(); // string for browser - virtual const char *type_name() = 0; // type for code output - virtual const char *alt_type_name() { return type_name(); } // alternate type for FLTK2 code output - - const char *name() const {return name_;} - void name(const char *); - const char *label() const {return label_;} - void label(const char *); - const char *callback() const {return callback_;} - void callback(const char *); - const char *user_data() const {return user_data_;} - void user_data(const char *); - const char *user_data_type() const {return user_data_type_;} - void user_data_type(const char *); - const char *comment() { return comment_; } - void comment(const char *); - - virtual Fl_Type* click_test(int,int) { return NULL; } - - virtual void add_child(Fl_Type*, Fl_Type* beforethis) { } - virtual void move_child(Fl_Type*, Fl_Type* beforethis) { } - virtual void remove_child(Fl_Type*) { } - - /** Give widgets a chance to arrange their children after all children were added. - If adding individual children, this is called immediately, but if children - are read via a project file, we wait until all children are read and then - lay out the group. - */ - virtual void layout_widget() { } - - static Fl_Type *current; // most recently picked object - static Fl_Type *current_dnd; - - virtual void open(); // what happens when you double-click - - // read and write data to a saved file: - virtual void write(fld::io::Project_Writer &f); - virtual void write_properties(fld::io::Project_Writer &f); - virtual void read_property(fld::io::Project_Reader &f, const char *); - virtual void write_parent_properties(fld::io::Project_Writer &f, Fl_Type *child, bool encapsulate); - virtual void read_parent_property(fld::io::Project_Reader &f, Fl_Type *child, const char *property); - virtual int read_fdesign(const char*, const char*); - virtual void postprocess_read() { } - - // write code, these are called in order: - virtual void write_static(fld::io::Code_Writer& f); // write static stuff to .c file - virtual void write_static_after(fld::io::Code_Writer& f); // write static stuff after children - virtual void write_code1(fld::io::Code_Writer& f); // code and .h before children - virtual void write_code2(fld::io::Code_Writer& f); // code and .h after children - void write_comment_h(fld::io::Code_Writer& f, const char *ind=""); // write the commentary text into the header file - void write_comment_c(fld::io::Code_Writer& f, const char *ind=""); // write the commentary text into the source file - void write_comment_inline_c(fld::io::Code_Writer& f, const char *ind=0L); // write the commentary text - - // live mode - virtual Fl_Widget *enter_live_mode(int top=0); // build widgets needed for live mode - virtual void leave_live_mode(); // free allocated resources - virtual void copy_properties(); // copy properties from this type into a potential live object - virtual void copy_properties_for_children() { } // copy remaining properties after children were added - - // get message number for I18N - int msgnum(); - - /** Return 1 if the Type can have children. */ - virtual int can_have_children() const {return 0;} - /** Return 1 if the type is a widget or menu item. */ - virtual int is_widget() const {return 0;} - /** Return 1 if the type is a widget but not a menu item. */ - virtual int is_true_widget() const {return 0;} - /** Return 1 if a type behaves like a button (Fl_Button and Fl_Menu_Item and derived, but not Fl_Submenu_Type. */ - virtual int is_button() const {return 0;} - /** Return 1 if this is a Fl_Widget_Class_Type, Fl_CodeBlock_Type, or Fl_Function_Type */ - virtual int is_code_block() const {return 0;} - /** Return 1 if this is a Fl_Widget_Class_Type, Fl_Class_Type, or Fl_DeclBlock_Type */ - virtual int is_decl_block() const {return 0;} - /** Return 1 if this is a Fl_Class_Type or Fl_Widget_Class_Type. */ - virtual int is_class() const {return 0;} - /** Return 1 if the type browser shall draw a padlock over the icon. */ - virtual int is_public() const {return 1;} - /** Return the type ID for this Type. */ - virtual ID id() const { return ID_Base_; } - /** Check if this Type is of the give type ID or derived from that type ID. */ - virtual bool is_a(ID inID) const { return (inID==ID_Base_); } - - const char* class_name(const int need_nest) const; - bool is_in_class() const; - - int has_function(const char*, const char*) const; - - unsigned short set_uid(unsigned short suggested_uid=0); - unsigned short get_uid() { return uid_; } - static Fl_Type *find_by_uid(unsigned short uid); - - static Fl_Type *find_in_text(int text_type, int crsr); - - /// If this is greater zero, widgets will be allowed to lay out their children. - static int allow_layout; -}; - -#endif // _FLUID_FL_TYPE_H diff --git a/fluid/nodes/Fl_Widget_Type.cxx b/fluid/nodes/Fl_Widget_Type.cxx deleted file mode 100644 index 2cde4d531..000000000 --- a/fluid/nodes/Fl_Widget_Type.cxx +++ /dev/null @@ -1,3939 +0,0 @@ -// -// Widget type code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2025 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "nodes/Fl_Widget_Type.h" - -#include "app/fluid.h" -#include "app/project.h" -#include "app/Fluid_Image.h" -#include "app/mergeback.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "nodes/Fl_Window_Type.h" -#include "nodes/Fl_Group_Type.h" -#include "nodes/Fl_Menu_Type.h" -#include "nodes/Fl_Function_Type.h" -#include "panels/settings_panel.h" -#include "panels/widget_panel.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../src/flstring.h" - -#include -#include - -// Make an Fl_Widget_Type subclass instance. -// It figures out the automatic size and parent of the new widget, -// creates the Fl_Widget (by calling the virtual function _make), -// adds it to the Fl_Widget hierarchy, creates a new Fl_Type -// instance, sets the widget pointers, and makes all the display -// update correctly... - -int Fl_Widget_Type::is_widget() const {return 1;} -int Fl_Widget_Type::is_public() const {return public_;} - -const char* subclassname(Fl_Type* l) { - if (l->is_a(ID_Menu_Bar)) { - Fl_Menu_Bar_Type *mb = static_cast(l); - if (mb->is_sys_menu_bar()) - return mb->sys_menubar_name(); - } - if (l->is_widget()) { - Fl_Widget_Type* p = (Fl_Widget_Type*)l; - const char* c = p->subclass(); - if (c) return c; - if (l->is_class()) return "Fl_Group"; - if (p->o->type() == FL_DOUBLE_WINDOW) return "Fl_Double_Window"; - if (p->id() == ID_Input) { - if (p->o->type() == FL_FLOAT_INPUT) return "Fl_Float_Input"; - if (p->o->type() == FL_INT_INPUT) return "Fl_Int_Input"; - } - } - return l->type_name(); -} - -// Return the ideal widget size... -void -Fl_Widget_Type::ideal_size(int &w, int &h) { - w = 120; - h = 100; - Fd_Snap_Action::better_size(w, h); -} - -/** - Make a new Widget node. - \param[in] strategy is Strategy::AS_LAST_CHILD or Strategy::AFTER_CURRENT - \return new node - */ -Fl_Type *Fl_Widget_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *pp = anchor; - if (pp && (strategy.placement() == Strategy::AFTER_CURRENT)) - pp = pp->parent; - while (pp && !pp->is_a(ID_Group)) { - anchor = pp; - strategy.placement(Strategy::AFTER_CURRENT); - pp = pp->parent; - } - if (!pp || !pp->is_true_widget() || !anchor->is_true_widget()) { - fl_message("Please select a group widget or window"); - return 0; - } - - Fl_Widget_Type* p = (Fl_Widget_Type*)pp; - Fl_Widget_Type* q = (Fl_Widget_Type*)anchor; - - // Figure out a border between widget and window: - int B = p->o->w()/2; if (p->o->h()/2 < B) B = p->o->h()/2; if (B>25) B = 25; - - int ULX,ULY; // parent's origin in window - if (!p->is_a(ID_Window)) { // if it is a group, add corner - ULX = p->o->x(); ULY = p->o->y(); - } else { - ULX = ULY = 0; - } - - // Figure out a position and size for the widget - int X,Y,W,H; - if (is_a(ID_Group)) { // fill the parent with the widget - X = ULX+B; - W = p->o->w()-B; - Y = ULY+B; - H = p->o->h()-B; - } else if (q != p) { // copy position and size of current widget - W = q->o->w(); - H = q->o->h(); - X = q->o->x()+W; - Y = q->o->y(); - if (X+W > ULX+p->o->w()) { - X = q->o->x(); - Y = q->o->y()+H; - if (Y+H > ULY+p->o->h()) Y = ULY+B; - } - } else { // just make it small and square... - X = ULX+B; - Y = ULY+B; - W = H = B; - } - - // Construct the Fl_Type: - Fl_Widget_Type *t = _make(); - if (!o) o = widget(0,0,100,100); // create template widget - t->factory = this; - // Construct the Fl_Widget: - t->o = widget(X,Y,W,H); - if (strategy.source() == Strategy::FROM_FILE) - t->o->label(0); - else if (t->o->label()) t->label(t->o->label()); // allow editing - t->o->user_data((void*)t); - // Put it in the parent: - // ((Fl_Group *)(p->o))->add(t->o); (done by Fl_Type::add()) - // add to browser: - t->add(anchor, strategy); - t->redraw(); - return t; -} - -void Fl_Widget_Type::setimage(Fluid_Image *i) { - if (i == image || is_a(ID_Window)) return; - if (image) image->decrement(); - if (i) i->increment(); - image = i; - if (i) { - i->image(o); - if (o->image() && (scale_image_w_ || scale_image_h_)) { - int iw = scale_image_w_>0 ? scale_image_w_ : o->image()->data_w(); - int ih = scale_image_h_>0 ? scale_image_h_ : o->image()->data_h(); - o->image()->scale(iw, ih, 0, 1); - } - } else { - o->image(0); - //scale_image_w_ = scale_image_h_ = 0; - } - redraw(); -} - -void Fl_Widget_Type::setinactive(Fluid_Image *i) { - if (i == inactive || is_a(ID_Window)) return; - if (inactive) inactive->decrement(); - if (i) i->increment(); - inactive = i; - if (i) { - i->deimage(o); - if (o->deimage()) { - int iw = scale_deimage_w_>0 ? scale_deimage_w_ : o->deimage()->data_w(); - int ih = scale_deimage_h_>0 ? scale_deimage_h_ : o->deimage()->data_h(); - o->deimage()->scale(iw, ih, 0, 1); - } - } else { - o->deimage(0); - //scale_deimage_w_ = scale_deimage_h_ = 0; - } - redraw(); -} - -void Fl_Widget_Type::setlabel(const char *n) { - o->label(n); - redraw(); -} - -Fl_Widget_Type::Fl_Widget_Type() -: override_visible_(0) -{ - for (int n=0; nwindow(); - delete o; - if (win) - win->redraw(); - } - if (subclass_) free((void*)subclass_); - if (tooltip_) free((void*)tooltip_); - if (image_name_) { - free((void*)image_name_); - if (image) image->decrement(); - } - if (inactive_name_) { - free((void*)inactive_name_); - if (inactive) inactive->decrement(); - } - for (int n=0; ntooltip(n); -} - -void Fl_Widget_Type::image_name(const char *n) { - setimage(Fluid_Image::find(n)); - storestring(n,image_name_); -} - -void Fl_Widget_Type::inactive_name(const char *n) { - setinactive(Fluid_Image::find(n)); - storestring(n,inactive_name_); -} - -void Fl_Widget_Type::redraw() { - Fl_Type *t = this; - if (is_a(ID_Menu_Item)) { - // find the menu button that parents this menu: - do t = t->parent; while (t && t->is_a(ID_Menu_Item)); - // kludge to cause build_menu to be called again: - if (t) - t->add_child(0, 0); - } else { - while (t->parent && t->parent->is_widget()) t = t->parent; - ((Fl_Widget_Type*)t)->o->redraw(); - } -} - -// the recursive part sorts all children, returns pointer to next: -Fl_Type *sort(Fl_Type *parent) { - Fl_Type *f,*n=0; - for (f = parent ? parent->next : Fl_Type::first; ; f = n) { - if (!f || (parent && f->level <= parent->level)) break; - n = sort(f); - if (!f->selected || !f->is_true_widget()) continue; - Fl_Widget* fw = ((Fl_Widget_Type*)f)->o; - Fl_Type *g; // we will insert before this - for (g = parent ? parent->next : Fl_Type::first; g != f; g = g->next) { - if (!g->selected || g->level > f->level) continue; - Fl_Widget* gw = ((Fl_Widget_Type*)g)->o; - if (gw->y() > fw->y()) break; - if (gw->y() == fw->y() && gw->x() > fw->x()) break; - } - if (g != f) f->move_before(g); - } - if (parent) - parent->layout_widget(); - return f; -} - -//////////////////////////////////////////////////////////////// -// The control panels! - -Fl_Window *the_panel; - -// All the callbacks use the argument to indicate whether to load or store. -// This avoids the need for pointers to all the widgets, and keeps the -// code localized in the callbacks. -// A value of LOAD means to load. The hope is that this will not collide -// with any actual useful values for the argument. I also use this to -// initialized parts of the widget that are nyi by fluid. - -Fl_Widget_Type *current_widget; // one of the selected ones -void* const LOAD = (void *)"LOAD"; // "magic" pointer to indicate that we need to load values into the dialog -static int numselected; // number selected -static int haderror; - -void name_cb(Fl_Input* o, void *v) { - if (v == LOAD) { - static char buf[1024]; - if (numselected != 1) { - snprintf(buf, sizeof(buf), "Widget Properties (%d widgets)", numselected); - o->hide(); - } else { - o->value(current_widget->name()); - o->show(); - snprintf(buf, sizeof(buf), "%s Properties", current_widget->title()); - } - - the_panel->label(buf); - } else { - if (numselected == 1) { - current_widget->name(o->value()); - // I don't update window title, as it probably is being closed - // and wm2 (a window manager) barfs if you retitle and then - // hide a window: - // ((Fl_Window*)(o->parent()->parent()->parent()))->label(current_widget->title()); - } - } -} - -void name_public_member_cb(Fl_Choice* i, void* v) { - if (v == LOAD) { - i->value(current_widget->public_); - if (current_widget->is_in_class()) i->show(); else i->hide(); - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type *w = ((Fl_Widget_Type*)o); - if (w->is_in_class()) { - w->public_ = i->value(); - } else { - // if this is not in a class, it can be only private or public - w->public_ = (i->value()>0); - } - mod = 1; - } - } - if (mod) { - set_modflag(1); - redraw_browser(); - } - } -} - -void name_public_cb(Fl_Choice* i, void* v) { - if (v == LOAD) { - i->value(current_widget->public_>0); - if (current_widget->is_in_class()) i->hide(); else i->show(); - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->public_ = i->value(); - mod = 1; - } - } - if (mod) { - set_modflag(1); - redraw_browser(); - } - } -} - -/* Treating UNDO for text widget. - - Goal: we want to continuously update the UI while the user is typing text - (changing the label, in this case). Code View does deferred updates, and - the widget browser and widget panel update on every keystroke. At the same - time, we want to limit undo actions to few and logical units. - - Caveats: - - 1: the text widget has its own undo handling for the text field, but we may want to do a global undo - 2: every o->label() call will create an undo entry, but we want only one single event for all selected widgets - 3: we want a single undo for the entire editing phase, but still propagate changes as they happen - - The edit process has these main states: - - 1: starting to edit [first_change==1 && !unfocus]; we must create a single undo checkpoint before anything changes - 2: continue editing [first_change==0 && !unfocus]; we must suspend any undo checkpoints - 3: done editing, unfocus [first_change==0 && unfocus]; we must make sure that undo checkpoints are enabled again - 4: losing focus without editing [first_change==1 && unfocus]; don't create and checkpoints - - We must also check: - 1: changing focus without changing text (works) - 2: copy and paste, drag and drop operations (works) - 3: save operation without unfocus event (works) - */ -void label_cb(Fl_Input* i, void *v) { - static int first_change = 1; - if (v == LOAD) { - i->value(current_widget->label()); - first_change = 1; - } else { - if (i->changed()) { - undo_suspend(); - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - if (!mod) { - if (first_change) { - undo_resume(); - undo_checkpoint(); - undo_suspend(); - first_change = 0; - } - mod = 1; - } - o->label(i->value()); - } - } - undo_resume(); - if (mod) set_modflag(1); - } - int r = (int)Fl::callback_reason(); - if ( (r == FL_REASON_LOST_FOCUS) || (r == FL_REASON_ENTER_KEY) ) - first_change = 1; - } -} - -static Fl_Input *image_input; - -void image_cb(Fl_Input* i, void *v) { - if (v == LOAD) { - image_input = i; - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { - i->activate(); - i->value(((Fl_Widget_Type*)current_widget)->image_name()); - } else i->deactivate(); - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->image_name(i->value()); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void image_browse_cb(Fl_Button* b, void *v) { - if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) - b->activate(); - else - b->deactivate(); - } else { - int mod = 0; - if (ui_find_image(image_input->value())) { - image_input->value(ui_find_image_name); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->image_name(ui_find_image_name); - mod = 1; - } - } - if (mod) set_modflag(1); - } - } -} - -void bind_image_cb(Fl_Check_Button* b, void *v) { - if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { - b->activate(); - b->value(current_widget->bind_image_); - } else { - b->deactivate(); - } - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->bind_image_ = b->value(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void compress_image_cb(Fl_Check_Button* b, void *v) { - if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { - b->activate(); - b->value(!current_widget->compress_image_); - } else { - b->deactivate(); - } - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->compress_image_ = !b->value(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -static Fl_Input *inactive_input; - -void inactive_cb(Fl_Input* i, void *v) { - if (v == LOAD) { - inactive_input = i; - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { - i->activate(); - i->value(((Fl_Widget_Type*)current_widget)->inactive_name()); - } else i->deactivate(); - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->inactive_name(i->value()); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void inactive_browse_cb(Fl_Button* b, void *v) { - if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) - b->activate(); - else - b->deactivate(); - } else { - int mod = 0; - if (ui_find_image(inactive_input->value())) { - inactive_input->value(ui_find_image_name); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->inactive_name(ui_find_image_name); - mod = 1; - } - } - if (mod) set_modflag(1); - } - } -} - -void bind_deimage_cb(Fl_Check_Button* b, void *v) { - if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { - b->activate(); - b->value(current_widget->bind_deimage_); - } else { - b->deactivate(); - } - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->bind_deimage_ = b->value(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void compress_deimage_cb(Fl_Check_Button* b, void *v) { - if (v == LOAD) { - if (current_widget->is_widget() && !current_widget->is_a(ID_Window)) { - b->activate(); - b->value(!current_widget->compress_deimage_); - } else { - b->deactivate(); - } - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->compress_deimage_ = !b->value(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void tooltip_cb(Fl_Input* i, void *v) { - if (v == LOAD) { - if (current_widget->is_widget()) { - i->activate(); - i->value(((Fl_Widget_Type*)current_widget)->tooltip()); - } else i->deactivate(); - } else { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - ((Fl_Widget_Type*)o)->tooltip(i->value()); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -fld::widget::Formula_Input *x_input, *y_input, *w_input, *h_input; - -static int widget_i = 0; - -static int vars_i_cb(const fld::widget::Formula_Input*, void *v) { - return widget_i; -} - -static int vars_x_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = (Fl_Type*)v; - if (t->is_widget()) - return ((Fl_Widget_Type*)t)->o->x(); - return 0; -} - -static int vars_y_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = (Fl_Type*)v; - if (t->is_widget()) - return ((Fl_Widget_Type*)t)->o->y(); - return 0; -} - -static int vars_w_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = (Fl_Type*)v; - if (t->is_widget()) - return ((Fl_Widget_Type*)t)->o->w(); - return 0; -} - -static int vars_h_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = (Fl_Type*)v; - if (t->is_widget()) - return ((Fl_Widget_Type*)t)->o->h(); - return 0; -} - -static int vars_px_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->parent; - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->x(); - return 0; -} - -static int vars_py_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->parent; - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->y(); - return 0; -} - -static int vars_pw_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->parent; - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->w(); - return 0; -} - -static int vars_ph_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->parent; - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->h(); - return 0; -} - -static int vars_sx_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->prev_sibling(); - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->x(); - return 0; -} - -static int vars_sy_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->prev_sibling(); - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->y(); - return 0; -} - -static int vars_sw_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->prev_sibling(); - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->w(); - return 0; -} - -static int vars_sh_cb(const fld::widget::Formula_Input*, void *v) { - Fl_Type *t = ((Fl_Type*)v)->prev_sibling(); - if (t && t->is_widget()) - return ((Fl_Widget_Type*)t)->o->h(); - return 0; -} - -static int bbox_x, bbox_y, bbox_r, bbox_b; -static int bbox_min(int a, int b) { return (ab) ? a : b; } - -static void calculate_bbox(Fl_Type *p) { - char first = 1; - bbox_x = bbox_y = bbox_r = bbox_b = 0; - for (p=p->first_child(); p; p=p->next_sibling()) { - if (p->is_widget()) { - Fl_Widget *o = ((Fl_Widget_Type*)p)->o; - if (first) { - bbox_x = o->x(); bbox_y = o->y(); - bbox_r = o->x() + o->w(); bbox_b = o->y() + o->h(); - first = 0; - } else { - bbox_x = bbox_min(bbox_x, o->x()); - bbox_y = bbox_min(bbox_y, o->y()); - bbox_r = bbox_max(bbox_r, o->x() + o->w()); - bbox_b = bbox_max(bbox_b, o->y() + o->h()); - } - } - } -} - -static int vars_cx_cb(const fld::widget::Formula_Input*, void *v) { - calculate_bbox((Fl_Type*)v); - return bbox_x; -} - -static int vars_cy_cb(const fld::widget::Formula_Input*, void *v) { - calculate_bbox((Fl_Type*)v); - return bbox_y; -} - -static int vars_cw_cb(const fld::widget::Formula_Input*, void *v) { - calculate_bbox((Fl_Type*)v); - return bbox_r - bbox_x; -} - -static int vars_ch_cb(const fld::widget::Formula_Input*, void *v) { - calculate_bbox((Fl_Type*)v); - return bbox_b - bbox_y; -} - -fld::widget::Formula_Input_Vars widget_vars[] = { - { "i", vars_i_cb }, // zero based counter of selected widgets - { "x", vars_x_cb }, // position and size of current widget - { "y", vars_y_cb }, - { "w", vars_w_cb }, - { "h", vars_h_cb }, - { "px", vars_px_cb }, // position and size of parent widget - { "py", vars_py_cb }, - { "pw", vars_pw_cb }, - { "ph", vars_ph_cb }, - { "sx", vars_sx_cb }, // position and size of previous sibling - { "sy", vars_sy_cb }, - { "sw", vars_sw_cb }, - { "sh", vars_sh_cb }, - { "cx", vars_cx_cb }, // position and size of bounding box of all children - { "cy", vars_cy_cb }, - { "cw", vars_cw_cb }, - { "ch", vars_ch_cb }, - { 0 } -}; - -void x_cb(fld::widget::Formula_Input *i, void *v) { - if (v == LOAD) { - x_input = i; - if (current_widget->is_true_widget()) { - i->value(((Fl_Widget_Type *)current_widget)->o->x()); - x_input->activate(); - } else x_input->deactivate(); - } else { - undo_checkpoint(); - widget_i = 0; - int mod = 0; - int v = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - i->variables(widget_vars, o); - v = i->value(); - w->resize(v, w->y(), w->w(), w->h()); - if (w->window()) w->window()->redraw(); - widget_i++; - mod = 1; - } - } - if (mod) { - set_modflag(1); - i->value(v); // change the displayed value to the result of the last - // calculation. Keep the formula if it was not used. - } - } -} - -void y_cb(fld::widget::Formula_Input *i, void *v) { - if (v == LOAD) { - y_input = i; - if (current_widget->is_true_widget()) { - i->value(((Fl_Widget_Type *)current_widget)->o->y()); - y_input->activate(); - } else y_input->deactivate(); - } else { - undo_checkpoint(); - widget_i = 0; - int mod = 0; - int v = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - i->variables(widget_vars, o); - v = i->value(); - w->resize(w->x(), v, w->w(), w->h()); - if (w->window()) w->window()->redraw(); - widget_i++; - mod = 1; - } - } - if (mod) { - set_modflag(1); - i->value(v); - } - } -} - -void w_cb(fld::widget::Formula_Input *i, void *v) { - if (v == LOAD) { - w_input = i; - if (current_widget->is_true_widget()) { - i->value(((Fl_Widget_Type *)current_widget)->o->w()); - w_input->activate(); - } else w_input->deactivate(); - } else { - undo_checkpoint(); - widget_i = 0; - int mod = 0; - int v = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - i->variables(widget_vars, o); - v = i->value(); - w->resize(w->x(), w->y(), v, w->h()); - if (w->window()) w->window()->redraw(); - widget_i++; - mod = 1; - } - } - if (mod) { - set_modflag(1); - i->value(v); - } - } -} - -void h_cb(fld::widget::Formula_Input *i, void *v) { - if (v == LOAD) { - h_input = i; - if (current_widget->is_true_widget()) { - i->value(((Fl_Widget_Type *)current_widget)->o->h()); - h_input->activate(); - } else h_input->deactivate(); - } else { - undo_checkpoint(); - widget_i = 0; - int mod = 0; - int v = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget *w = ((Fl_Widget_Type *)o)->o; - i->variables(widget_vars, o); - v = i->value(); - w->resize(w->x(), w->y(), w->w(), v); - if (w->window()) w->window()->redraw(); - widget_i++; - mod = 1; - } - } - if (mod) { - set_modflag(1); - i->value(v); - } - } -} - -void wc_relative_cb(Fl_Choice *i, void *v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Widget_Class)) { - i->show(); - i->value(((Fl_Widget_Class_Type *)current_widget)->wc_relative); - } else { - i->hide(); - } - } else { - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && current_widget->is_a(ID_Widget_Class)) { - Fl_Widget_Class_Type *t = (Fl_Widget_Class_Type *)o; - t->wc_relative = i->value(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -// turn number to string or string to number for saving to file: -// does not work for hierarchical menus! - -const char *item_name(Fl_Menu_Item* m, int i) { - if (m) { - while (m->label()) { - if (m->argument() == i) return m->label(); - m++; - } - } - static char buffer[20]; - sprintf(buffer, "%d", i); - return buffer; -} -int item_number(Fl_Menu_Item* m, const char* i) { - if (!i) - return 0; - if (m && i) { - if (i[0]=='F' && i[1]=='L' && i[2]=='_') i += 3; - while (m->label()) { - if (!strcmp(m->label(), i)) return int(m->argument()); - m++; - } - } - return atoi(i); -} - -#define ZERO_ENTRY 1000 - -Fl_Menu_Item boxmenu[] = { -{"NO_BOX",0,0,(void *)ZERO_ENTRY}, -{"boxes",0,0,0,FL_SUBMENU}, -{"UP_BOX",0,0,(void *)FL_UP_BOX}, -{"DOWN_BOX",0,0,(void *)FL_DOWN_BOX}, -{"FLAT_BOX",0,0,(void *)FL_FLAT_BOX}, -{"BORDER_BOX",0,0,(void *)FL_BORDER_BOX}, -{"THIN_UP_BOX",0,0,(void *)FL_THIN_UP_BOX}, -{"THIN_DOWN_BOX",0,0,(void *)FL_THIN_DOWN_BOX}, -{"ENGRAVED_BOX",0,0,(void *)FL_ENGRAVED_BOX}, -{"EMBOSSED_BOX",0,0,(void *)FL_EMBOSSED_BOX}, -{"ROUND_UP_BOX",0,0,(void *)FL_ROUND_UP_BOX}, -{"ROUND_DOWN_BOX",0,0,(void *)FL_ROUND_DOWN_BOX}, -{"DIAMOND_UP_BOX",0,0,(void *)FL_DIAMOND_UP_BOX}, -{"DIAMOND_DOWN_BOX",0,0,(void *)FL_DIAMOND_DOWN_BOX}, -{"SHADOW_BOX",0,0,(void *)FL_SHADOW_BOX}, -{"ROUNDED_BOX",0,0,(void *)FL_ROUNDED_BOX}, -{"RSHADOW_BOX",0,0,(void *)FL_RSHADOW_BOX}, -{"RFLAT_BOX",0,0,(void *)FL_RFLAT_BOX}, -{"OVAL_BOX",0,0,(void *)FL_OVAL_BOX}, -{"OSHADOW_BOX",0,0,(void *)FL_OSHADOW_BOX}, -{"OFLAT_BOX",0,0,(void *)FL_OFLAT_BOX}, -{"PLASTIC_UP_BOX",0,0,(void *)FL_PLASTIC_UP_BOX}, -{"PLASTIC_DOWN_BOX",0,0,(void *)FL_PLASTIC_DOWN_BOX}, -{"PLASTIC_THIN_UP_BOX",0,0,(void *)FL_PLASTIC_THIN_UP_BOX}, -{"PLASTIC_THIN_DOWN_BOX",0,0,(void *)FL_PLASTIC_THIN_DOWN_BOX}, -{"PLASTIC_ROUND_UP_BOX",0,0,(void *)FL_PLASTIC_ROUND_UP_BOX}, -{"PLASTIC_ROUND_DOWN_BOX",0,0,(void *)FL_PLASTIC_ROUND_DOWN_BOX}, -{"GTK_UP_BOX",0,0,(void *)FL_GTK_UP_BOX}, -{"GTK_DOWN_BOX",0,0,(void *)FL_GTK_DOWN_BOX}, -{"GTK_THIN_UP_BOX",0,0,(void *)FL_GTK_THIN_UP_BOX}, -{"GTK_THIN_DOWN_BOX",0,0,(void *)FL_GTK_THIN_DOWN_BOX}, -{"GTK_ROUND_UP_BOX",0,0,(void *)FL_GTK_ROUND_UP_BOX}, -{"GTK_ROUND_DOWN_BOX",0,0,(void *)FL_GTK_ROUND_DOWN_BOX}, -{"GLEAM_UP_BOX",0,0,(void *)FL_GLEAM_UP_BOX}, -{"GLEAM_DOWN_BOX",0,0,(void *)FL_GLEAM_DOWN_BOX}, -{"GLEAM_THIN_UP_BOX",0,0,(void *)FL_GLEAM_THIN_UP_BOX}, -{"GLEAM_THIN_DOWN_BOX",0,0,(void *)FL_GLEAM_THIN_DOWN_BOX}, -{"GLEAM_ROUND_UP_BOX",0,0,(void *)FL_GLEAM_ROUND_UP_BOX}, -{"GLEAM_ROUND_DOWN_BOX",0,0,(void *)FL_GLEAM_ROUND_DOWN_BOX}, -{"OXY_UP_BOX",0,0,(void *)FL_OXY_UP_BOX}, -{"OXY_DOWN_BOX",0,0,(void *)FL_OXY_DOWN_BOX}, -{"OXY_THIN_UP_BOX",0,0,(void *)FL_OXY_THIN_UP_BOX}, -{"OXY_THIN_DOWN_BOX",0,0,(void *)FL_OXY_THIN_DOWN_BOX}, -{"OXY_ROUND_UP_BOX",0,0,(void *)FL_OXY_ROUND_UP_BOX}, -{"OXY_ROUND_DOWN_BOX",0,0,(void *)FL_OXY_ROUND_DOWN_BOX}, -{"OXY_BUTTON_UP_BOX",0,0,(void *)FL_OXY_BUTTON_UP_BOX}, -{"OXY_BUTTON_DOWN_BOX",0,0,(void *)FL_OXY_BUTTON_DOWN_BOX}, -{0}, -{"frames",0,0,0,FL_SUBMENU}, -{"UP_FRAME",0,0,(void *)FL_UP_FRAME}, -{"DOWN_FRAME",0,0,(void *)FL_DOWN_FRAME}, -{"THIN_UP_FRAME",0,0,(void *)FL_THIN_UP_FRAME}, -{"THIN_DOWN_FRAME",0,0,(void *)FL_THIN_DOWN_FRAME}, -{"ENGRAVED_FRAME",0,0,(void *)FL_ENGRAVED_FRAME}, -{"EMBOSSED_FRAME",0,0,(void *)FL_EMBOSSED_FRAME}, -{"BORDER_FRAME",0,0,(void *)FL_BORDER_FRAME}, -{"SHADOW_FRAME",0,0,(void *)FL_SHADOW_FRAME}, -{"ROUNDED_FRAME",0,0,(void *)FL_ROUNDED_FRAME}, -{"OVAL_FRAME",0,0,(void *)FL_OVAL_FRAME}, -{"PLASTIC_UP_FRAME",0,0,(void *)FL_PLASTIC_UP_FRAME}, -{"PLASTIC_DOWN_FRAME",0,0,(void *)FL_PLASTIC_DOWN_FRAME}, -{"GTK_UP_FRAME",0,0,(void *)FL_GTK_UP_FRAME}, -{"GTK_DOWN_FRAME",0,0,(void *)FL_GTK_DOWN_FRAME}, -{"GTK_THIN_UP_FRAME",0,0,(void *)FL_GTK_THIN_UP_FRAME}, -{"GTK_THIN_DOWN_FRAME",0,0,(void *)FL_GTK_THIN_DOWN_FRAME}, -{"GLEAM_UP_FRAME",0,0,(void *)FL_GLEAM_UP_FRAME}, -{"GLEAM_DOWN_FRAME",0,0,(void *)FL_GLEAM_DOWN_FRAME}, -{"OXY_UP_FRAME",0,0,(void *)FL_OXY_UP_FRAME}, -{"OXY_DOWN_FRAME",0,0,(void *)FL_OXY_DOWN_FRAME}, -{"OXY_THIN_UP_FRAME",0,0,(void *)FL_OXY_THIN_UP_FRAME}, -{"OXY_THIN_DOWN_FRAME",0,0,(void *)FL_OXY_THIN_DOWN_FRAME}, -{0}, -{0}}; - -const char *boxname(int i) { - if (!i) i = ZERO_ENTRY; - for (int j = 0; j < int(sizeof(boxmenu)/sizeof(*boxmenu)); j++) - if (boxmenu[j].argument() == i) return boxmenu[j].label(); - return 0; -} - -int boxnumber(const char *i) { - if (i[0]=='F' && i[1]=='L' && i[2]=='_') i += 3; - for (int j = 0; j < int(sizeof(boxmenu)/sizeof(*boxmenu)); j++) - if (boxmenu[j].label() && !strcmp(boxmenu[j].label(), i)) { - return int(boxmenu[j].argument()); - } - return 0; -} - -void box_cb(Fl_Choice* i, void *v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - int n = current_widget->o->box(); if (!n) n = ZERO_ENTRY; - for (int j = 0; j < int(sizeof(boxmenu)/sizeof(*boxmenu)); j++) - if (boxmenu[j].argument() == n) {i->value(j); break;} - } else { - int mod = 0; - int m = i->value(); - int n = int(boxmenu[m].argument()); - if (!n) return; // should not happen - if (n == ZERO_ENTRY) n = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->box((Fl_Boxtype)n); - q->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void down_box_cb(Fl_Choice* i, void *v) { - if (v == LOAD) { - int n; - if (current_widget->is_a(ID_Button)) - n = ((Fl_Button*)(current_widget->o))->down_box(); - else if (current_widget->is_a(ID_Input_Choice)) - n = ((Fl_Input_Choice*)(current_widget->o))->down_box(); - else if (current_widget->is_a(ID_Menu_Manager_)) - n = ((Fl_Menu_*)(current_widget->o))->down_box(); - else { - i->deactivate(); return; - } - i->activate(); - if (!n) n = ZERO_ENTRY; - for (int j = 0; j < int(sizeof(boxmenu)/sizeof(*boxmenu)); j++) - if (boxmenu[j].argument() == n) {i->value(j); break;} - } else { - int mod = 0; - int m = i->value(); - int n = int(boxmenu[m].argument()); - if (!n) return; // should not happen - if (n == ZERO_ENTRY) n = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected) { - if (o->is_a(ID_Button)) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - ((Fl_Button*)(q->o))->down_box((Fl_Boxtype)n); - if (((Fl_Button*)(q->o))->value()) q->redraw(); - } else if (o->is_a(ID_Input_Choice)) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - ((Fl_Input_Choice*)(q->o))->down_box((Fl_Boxtype)n); - } else if (o->is_a(ID_Menu_Manager_)) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - ((Fl_Menu_*)(q->o))->down_box((Fl_Boxtype)n); - } - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void compact_cb(Fl_Light_Button* i, void* v) { - if (v == LOAD) { - uchar n; - if (current_widget->is_a(ID_Button) && !current_widget->is_a(ID_Menu_Item)) { - n = ((Fl_Button*)(current_widget->o))->compact(); - i->value(n); - i->show(); - } else { - i->hide(); - } - } else { - int mod = 0; - uchar n = (uchar)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Button) && !o->is_a(ID_Menu_Item)) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - uchar v = ((Fl_Button*)(q->o))->compact(); - if (n != v) { - if (!mod) { - mod = 1; - undo_checkpoint(); - } - ((Fl_Button*)(q->o))->compact(n); - q->redraw(); - } - } - } - if (mod) set_modflag(1); - } -} - - - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Item whenmenu[] = { - // set individual bits - {"FL_WHEN_CHANGED",0,0,(void*)FL_WHEN_CHANGED, FL_MENU_TOGGLE}, - {"FL_WHEN_NOT_CHANGED",0,0,(void*)FL_WHEN_NOT_CHANGED, FL_MENU_TOGGLE}, - {"FL_WHEN_RELEASE",0,0,(void*)FL_WHEN_RELEASE, FL_MENU_TOGGLE}, - {"FL_WHEN_ENTER_KEY",0,0,(void*)FL_WHEN_ENTER_KEY, FL_MENU_TOGGLE}, - {"FL_WHEN_CLOSED",0,0,(void*)FL_WHEN_CLOSED, FL_MENU_TOGGLE|FL_MENU_DIVIDER}, - // set bit combinations - {"FL_WHEN_NEVER",0,0,(void*)FL_WHEN_NEVER}, - {"FL_WHEN_RELEASE_ALWAYS",0,0,(void*)FL_WHEN_RELEASE_ALWAYS}, - {"FL_WHEN_ENTER_KEY_ALWAYS",0,0,(void*)FL_WHEN_ENTER_KEY_ALWAYS}, - {"FL_WHEN_ENTER_KEY_CHANGED",0,0,(void*)FL_WHEN_ENTER_KEY_CHANGED}, - {0}}; - - -static Fl_Menu_Item whensymbolmenu[] = { - /* 0 */ {"FL_WHEN_NEVER",0,0,(void*)FL_WHEN_NEVER}, - /* 1 */ {"FL_WHEN_CHANGED",0,0,(void*)FL_WHEN_CHANGED}, - /* 2 */ {"FL_WHEN_NOT_CHANGED",0,0,(void*)FL_WHEN_NOT_CHANGED}, - /* 3 */ {"FL_WHEN_CHANGED | FL_WHEN_NOT_CHANGED",0,0,(void*)(FL_WHEN_CHANGED|FL_WHEN_NOT_CHANGED)}, - /* 4 */ {"FL_WHEN_RELEASE",0,0,(void*)FL_WHEN_RELEASE}, - /* 5 */ {"FL_WHEN_CHANGED | FL_WHEN_RELEASE",0,0,(void*)(FL_WHEN_CHANGED|FL_WHEN_RELEASE)}, - /* 6 */ {"FL_WHEN_RELEASE_ALWAYS",0,0,(void*)FL_WHEN_RELEASE_ALWAYS}, - /* 7 */ {"FL_WHEN_CHANGED | FL_WHEN_RELEASE_ALWAYS",0,0,(void*)(FL_WHEN_CHANGED|FL_WHEN_RELEASE_ALWAYS)}, - /* 8 */ {"FL_WHEN_ENTER_KEY",0,0,(void*)FL_WHEN_ENTER_KEY}, - /* 9 */ {"FL_WHEN_CHANGED | FL_WHEN_ENTER_KEY",0,0,(void*)(FL_WHEN_CHANGED|FL_WHEN_ENTER_KEY)}, - /* 10 */ {"FL_WHEN_ENTER_KEY_ALWAYS",0,0,(void*)FL_WHEN_ENTER_KEY_ALWAYS}, - /* 11 */ {"FL_WHEN_ENTER_KEY_CHANGED",0,0,(void*)FL_WHEN_ENTER_KEY_CHANGED}, - /* 12 */ {"FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY",0,0,(void*)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY)}, - /* 13 */ {"FL_WHEN_RELEASE | FL_WHEN_CHANGED | FL_WHEN_ENTER_KEY",0,0,(void*)(FL_WHEN_RELEASE|FL_WHEN_CHANGED|FL_WHEN_ENTER_KEY)}, - /* 14 */ {"FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY_ALWAYS",0,0,(void*)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY_ALWAYS)}, - /* 15 */ {"FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY_CHANGED",0,0,(void*)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY_CHANGED)}, - {0} -}; - -// Return a text string representing the Fl_When value n -const char* when_symbol_name(int n) { - static char sym[128]; - if (n == FL_WHEN_CLOSED) { - strcpy(sym, "FL_WHEN_CLOSED"); - } else { - strcpy(sym, whensymbolmenu[n&15].label()); - if (n & FL_WHEN_CLOSED) - strcat(sym, " | FL_WHEN_CLOSED"); - } - return sym; -} - -// Set the check marks in the "when()" menu according to the Fl_When value n -void set_whenmenu(int n) { - if (n&FL_WHEN_CHANGED) whenmenu[0].set(); else whenmenu[0].clear(); - if (n&FL_WHEN_NOT_CHANGED) whenmenu[1].set(); else whenmenu[1].clear(); - if (n&FL_WHEN_RELEASE) whenmenu[2].set(); else whenmenu[2].clear(); - if (n&FL_WHEN_ENTER_KEY) whenmenu[3].set(); else whenmenu[3].clear(); - if (n&FL_WHEN_CLOSED) whenmenu[4].set(); else whenmenu[4].clear(); -} - -void when_cb(Fl_Menu_Button* i, void *v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - int n = current_widget->o->when(); - set_whenmenu(n); - w_when_box->copy_label(when_symbol_name(n)); - } else { - int mod = 0; - int n = 0; - if (i->mvalue() && ((i->mvalue()->flags & FL_MENU_TOGGLE) == 0) ) { - n = (int)i->mvalue()->argument(); - set_whenmenu(n); - } else { - if (whenmenu[0].value()) n |= FL_WHEN_CHANGED; - if (whenmenu[1].value()) n |= FL_WHEN_NOT_CHANGED; - if (whenmenu[2].value()) n |= FL_WHEN_RELEASE; - if (whenmenu[3].value()) n |= FL_WHEN_ENTER_KEY; - if (whenmenu[4].value()) n |= FL_WHEN_CLOSED; - } - w_when_box->copy_label(when_symbol_name(n)); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->when(n); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -uchar Fl_Widget_Type::resizable() const { - if (is_a(ID_Window)) return ((Fl_Window*)o)->resizable() != 0; - Fl_Group* p = (Fl_Group*)o->parent(); - if (p) return p->resizable() == o; - else return 0; -} - -void Fl_Widget_Type::resizable(uchar v) { - if (v) { - if (resizable()) return; - if (is_a(ID_Window)) ((Fl_Window*)o)->resizable(o); - else { - Fl_Group* p = (Fl_Group*)o->parent(); - if (p) p->resizable(o); - } - } else { - if (!resizable()) return; - if (is_a(ID_Window)) { - ((Fl_Window*)o)->resizable(0); - } else { - Fl_Group* p = (Fl_Group*)o->parent(); - if (p) p->resizable(0); - } - } -} - -void resizable_cb(Fl_Light_Button* i,void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} - if (numselected > 1) {i->deactivate(); return;} - i->activate(); - i->value(current_widget->resizable()); - } else { - undo_checkpoint(); - current_widget->resizable(i->value()); - set_modflag(1); - } -} - -void hotspot_cb(Fl_Light_Button* i,void* v) { - if (v == LOAD) { - if (numselected > 1) {i->deactivate(); return;} - if (current_widget->is_a(ID_Menu_Item)) i->label("divider"); - else i->label("hotspot"); - i->activate(); - i->value(current_widget->hotspot()); - } else { - undo_checkpoint(); - current_widget->hotspot(i->value()); - if (current_widget->is_a(ID_Menu_Item)) { - current_widget->redraw(); - return; - } - if (i->value()) { - Fl_Type *p = current_widget->parent; - if (!p || !p->is_widget()) return; - while (!p->is_a(ID_Window)) p = p->parent; - for (Fl_Type *o = p->next; o && o->level > p->level; o = o->next) { - if (o->is_widget() && o != current_widget) - ((Fl_Widget_Type*)o)->hotspot(0); - } - } - set_modflag(1); - } -} - -void visible_cb(Fl_Light_Button* i, void* v) { - if (v == LOAD) { - i->value(current_widget->o->visible()); - if (current_widget->is_a(ID_Window)) i->deactivate(); - else i->activate(); - } else { - int mod = 0; - int n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - if (!mod) { - mod = 1; - undo_checkpoint(); - } - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - n ? q->o->show() : q->o->hide(); - q->redraw(); - if (n && q->parent && q->parent->type_name()) { - if (q->parent->is_a(ID_Tabs)) { - ((Fl_Tabs *)q->o->parent())->value(q->o); - } else if (q->parent->is_a(ID_Wizard)) { - ((Fl_Wizard *)q->o->parent())->value(q->o); - } - } - } - } - if (mod) { - set_modflag(1); - redraw_browser(); - } - } -} - -void active_cb(Fl_Light_Button* i, void* v) { - if (v == LOAD) { - i->value(current_widget->o->active()); - if (current_widget->is_a(ID_Window)) i->deactivate(); - else i->activate(); - } else { - int mod = 0; - int n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - if (!mod) { - mod = 1; - undo_checkpoint(); - } - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - n ? q->o->activate() : q->o->deactivate(); - q->redraw(); - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Item fontmenu[] = { - {"Helvetica"}, - {"Helvetica bold"}, - {"Helvetica italic"}, - {"Helvetica bold italic"}, - {"Courier"}, - {"Courier bold"}, - {"Courier italic"}, - {"Courier bold italic"}, - {"Times"}, - {"Times bold"}, - {"Times italic"}, - {"Times bold italic"}, - {"Symbol"}, - {"Terminal"}, - {"Terminal Bold"}, - {"Zapf Dingbats"}, - {NULL} -}; - -Fl_Menu_Item fontmenu_w_default[] = { - {"", 0, NULL, NULL, FL_MENU_DIVIDER}, - {"Helvetica"}, - {"Helvetica bold"}, - {"Helvetica italic"}, - {"Helvetica bold italic"}, - {"Courier"}, - {"Courier bold"}, - {"Courier italic"}, - {"Courier bold italic"}, - {"Times"}, - {"Times bold"}, - {"Times italic"}, - {"Times bold italic"}, - {"Symbol"}, - {"Terminal"}, - {"Terminal Bold"}, - {"Zapf Dingbats"}, - {NULL} -}; - -void labelfont_cb(Fl_Choice* i, void *v) { - if (v == LOAD) { - int n = current_widget->o->labelfont(); - if (n > 15) n = 0; - i->value(n); - } else { - int mod = 0; - int n = i->value(); - if (n <= 0) n = layout->labelfont; - if (n <= 0) n = FL_HELVETICA; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->labelfont(n); - q->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void labelsize_cb(Fl_Value_Input* i, void *v) { - int n; - if (v == LOAD) { - n = current_widget->o->labelsize(); - } else { - int mod = 0; - n = int(i->value()); - if (n <= 0) n = layout->labelsize; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->labelsize(n); - q->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); - } - i->value(n); -} - -extern const char *ui_find_image_name; - -Fl_Menu_Item labeltypemenu[] = { - {"NORMAL_LABEL",0,0,(void*)0}, - {"SHADOW_LABEL",0,0,(void*)FL_SHADOW_LABEL}, - {"ENGRAVED_LABEL",0,0,(void*)FL_ENGRAVED_LABEL}, - {"EMBOSSED_LABEL",0,0,(void*)FL_EMBOSSED_LABEL}, - {"NO_LABEL",0,0,(void*)(FL_NO_LABEL)}, -{0}}; - -void labeltype_cb(Fl_Choice* i, void *v) { - if (v == LOAD) { - int n; - n = current_widget->o->labeltype(); - i->when(FL_WHEN_RELEASE); - for (int j = 0; j < int(sizeof(labeltypemenu)/sizeof(*labeltypemenu)); j++) - if (labeltypemenu[j].argument() == n) {i->value(j); break;} - } else { - int mod = 0; - int m = i->value(); - int n = int(labeltypemenu[m].argument()); - if (n<0) return; // should not happen - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* p = (Fl_Widget_Type*)o; - p->o->labeltype((Fl_Labeltype)n); - p->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Item colormenu[] = { - { "Foreground Color", 0, 0, (void*)(fl_intptr_t)FL_FOREGROUND_COLOR, 0, 0, FL_HELVETICA, 11}, - { "Background Color", 0, 0, (void*)(fl_intptr_t)FL_BACKGROUND_COLOR, 0, 0, FL_HELVETICA, 11}, - { "Background Color 2", 0, 0, (void*)(fl_intptr_t)FL_BACKGROUND2_COLOR, 0, 0, FL_HELVETICA, 11}, - { "Selection Color", 0, 0, (void*)(fl_intptr_t)FL_SELECTION_COLOR, 0, 0, FL_HELVETICA, 11}, - { "Inactive Color", 0, 0, (void*)(fl_intptr_t)FL_INACTIVE_COLOR, FL_MENU_DIVIDER, 0, FL_HELVETICA, 11}, - { "Black", 0, 0, (void*)(fl_intptr_t)FL_BLACK, 0, 0, FL_HELVETICA, 11}, - { "White", 0, 0, (void*)(fl_intptr_t)FL_WHITE, FL_MENU_DIVIDER, 0, FL_HELVETICA, 11}, - { "Gray 0", 0, 0, (void*)(fl_intptr_t)FL_GRAY0, 0, 0, FL_HELVETICA, 11}, - { "Dark 3", 0, 0, (void*)(fl_intptr_t)FL_DARK3, 0, 0, FL_HELVETICA, 11}, - { "Dark 2", 0, 0, (void*)(fl_intptr_t)FL_DARK2, 0, 0, FL_HELVETICA, 11}, - { "Dark 1", 0, 0, (void*)(fl_intptr_t)FL_DARK1, 0, 0, FL_HELVETICA, 11}, - { "Light 1", 0, 0, (void*)(fl_intptr_t)FL_LIGHT1, 0, 0, FL_HELVETICA, 11}, - { "Light 2", 0, 0, (void*)(fl_intptr_t)FL_LIGHT2, 0, 0, FL_HELVETICA, 11}, - { "Light 3", 0, 0, (void*)(fl_intptr_t)FL_LIGHT3, 0, 0, FL_HELVETICA, 11}, - { 0 } -}; - -void color_common(Fl_Color c) { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->color(c); q->o->redraw(); - if (q->parent && q->parent->is_a(ID_Tabs)) { - if (q->o->parent()) q->o->parent()->redraw(); - } - mod = 1; - } - } - if (mod) set_modflag(1); -} - -void color_cb(Fl_Button* i, void *v) { - Fl_Color c = current_widget->o->color(); - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - } else { - Fl_Color d = fl_show_colormap(c); - if (d == c) return; - c = d; - color_common(c); - } - i->color(c); i->labelcolor(fl_contrast(FL_BLACK,c)); i->redraw(); -} - -void color_menu_cb(Fl_Menu_Button* i, void *v) { - Fl_Color c = current_widget->o->color(); - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - } else { - Fl_Color d = (Fl_Color)(i->mvalue()->argument()); - if (d == c) return; - c = d; - color_common(c); - w_color->color(c); w_color->labelcolor(fl_contrast(FL_BLACK,c)); w_color->redraw(); - } -} - -void color2_common(Fl_Color c) { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->selection_color(c); q->o->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); -} - -void color2_cb(Fl_Button* i, void *v) { - Fl_Color c = current_widget->o->selection_color(); - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - } else { - Fl_Color d = fl_show_colormap(c); - if (d == c) return; - c = d; - color2_common(c); - } - i->color(c); i->labelcolor(fl_contrast(FL_BLACK,c)); i->redraw(); -} - -void color2_menu_cb(Fl_Menu_Button* i, void *v) { - Fl_Color c = current_widget->o->selection_color(); - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - } else { - Fl_Color d = (Fl_Color)(i->mvalue()->argument()); - if (d == c) return; - c = d; - color2_common(c); - w_selectcolor->color(c); w_selectcolor->labelcolor(fl_contrast(FL_BLACK,c)); w_selectcolor->redraw(); - } -} - -void labelcolor_common(Fl_Color c) { - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->o->labelcolor(c); q->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); -} - -void labelcolor_cb(Fl_Button* i, void *v) { - Fl_Color c = current_widget->o->labelcolor(); - if (v != LOAD) { - Fl_Color d = fl_show_colormap(c); - if (d == c) return; - c = d; - labelcolor_common(c); - } - i->color(c); i->labelcolor(fl_contrast(FL_BLACK,c)); i->redraw(); -} - -void labelcolor_menu_cb(Fl_Menu_Button* i, void *v) { - Fl_Color c = current_widget->o->labelcolor(); - if (v != LOAD) { - Fl_Color d = (Fl_Color)(i->mvalue()->argument()); - if (d == c) return; - c = d; - labelcolor_common(c); - w_labelcolor->color(c); w_labelcolor->labelcolor(fl_contrast(FL_BLACK,c)); w_labelcolor->redraw(); - } -} - -static Fl_Button* relative(Fl_Widget* o, int i) { - Fl_Group* g = (Fl_Group*)(o->parent()); - return (Fl_Button*)(g->child(g->find(*o)+i)); -} - -static Fl_Menu_Item alignmenu[] = { - {"FL_ALIGN_CENTER",0,0,(void*)(fl_intptr_t)(FL_ALIGN_CENTER)}, - {"FL_ALIGN_TOP",0,0,(void*)(fl_intptr_t)(FL_ALIGN_TOP)}, - {"FL_ALIGN_BOTTOM",0,0,(void*)(fl_intptr_t)(FL_ALIGN_BOTTOM)}, - {"FL_ALIGN_LEFT",0,0,(void*)(fl_intptr_t)(FL_ALIGN_LEFT)}, - {"FL_ALIGN_RIGHT",0,0,(void*)(fl_intptr_t)(FL_ALIGN_RIGHT)}, - {"FL_ALIGN_INSIDE",0,0,(void*)(fl_intptr_t)(FL_ALIGN_INSIDE)}, - {"FL_ALIGN_CLIP",0,0,(void*)(fl_intptr_t)(FL_ALIGN_CLIP)}, - {"FL_ALIGN_WRAP",0,0,(void*)(fl_intptr_t)(FL_ALIGN_WRAP)}, - {"FL_ALIGN_TEXT_OVER_IMAGE",0,0,(void*)(fl_intptr_t)(FL_ALIGN_TEXT_OVER_IMAGE)}, - {"FL_ALIGN_TOP_LEFT",0,0,(void*)(fl_intptr_t)(FL_ALIGN_TOP_LEFT)}, - {"FL_ALIGN_TOP_RIGHT",0,0,(void*)(fl_intptr_t)(FL_ALIGN_TOP_RIGHT)}, - {"FL_ALIGN_BOTTOM_LEFT",0,0,(void*)(fl_intptr_t)(FL_ALIGN_BOTTOM_LEFT)}, - {"FL_ALIGN_BOTTOM_RIGHT",0,0,(void*)(fl_intptr_t)(FL_ALIGN_BOTTOM_RIGHT)}, - {"FL_ALIGN_LEFT_TOP",0,0,(void*)(fl_intptr_t)(FL_ALIGN_LEFT_TOP)}, - {"FL_ALIGN_RIGHT_TOP",0,0,(void*)(fl_intptr_t)(FL_ALIGN_RIGHT_TOP)}, - {"FL_ALIGN_LEFT_BOTTOM",0,0,(void*)(fl_intptr_t)(FL_ALIGN_LEFT_BOTTOM)}, - {"FL_ALIGN_RIGHT_BOTTOM",0,0,(void*)(fl_intptr_t)(FL_ALIGN_RIGHT_BOTTOM)}, -{0}}; - -void align_cb(Fl_Button* i, void *v) { - Fl_Align b = Fl_Align(fl_uintptr_t(i->user_data())); - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - i->value(current_widget->o->align() & b); - } else { - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - Fl_Align x = q->o->align(); - Fl_Align y; - if (i->value()) { - y = x | b; - if (b == FL_ALIGN_LEFT || b == FL_ALIGN_TOP) { - Fl_Button *b1 = relative(i,+1); - b1->clear(); - y = y & ~(b1->argument()); - } - if (b == FL_ALIGN_RIGHT || b == FL_ALIGN_BOTTOM) { - Fl_Button *b1 = relative(i,-1); - b1->clear(); - y = y & ~(b1->argument()); - } - } else { - y = x & ~b; - } - if (x != y) { - q->o->align(y); - q->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void align_position_cb(Fl_Choice *i, void *v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - Fl_Menu_Item *mi = (Fl_Menu_Item*)i->menu(); - Fl_Align b = current_widget->o->align() & FL_ALIGN_POSITION_MASK; - for (;mi->text;mi++) { - if ((Fl_Align)(mi->argument())==b) - i->value(mi); - } - } else { - const Fl_Menu_Item *mi = i->menu() + i->value(); - Fl_Align b = Fl_Align(fl_uintptr_t(mi->user_data())); - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - Fl_Align x = q->o->align(); - Fl_Align y = (x & ~FL_ALIGN_POSITION_MASK) | b; - if (x != y) { - q->o->align(y); - q->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void align_text_image_cb(Fl_Choice *i, void *v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - Fl_Menu_Item *mi = (Fl_Menu_Item*)i->menu(); - Fl_Align b = current_widget->o->align() & FL_ALIGN_IMAGE_MASK; - for (;mi->text;mi++) { - if ((Fl_Align)(mi->argument())==b) - i->value(mi); - } - } else { - const Fl_Menu_Item *mi = i->menu() + i->value(); - Fl_Align b = Fl_Align(fl_uintptr_t(mi->user_data())); - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - Fl_Align x = q->o->align(); - Fl_Align y = (x & ~FL_ALIGN_IMAGE_MASK) | b; - if (x != y) { - q->o->align(y); - q->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -void callback_cb(fld::widget::Code_Editor* i, void *v) { - if (v == LOAD) { - const char *cbtext = current_widget->callback(); - i->buffer()->text( cbtext ? cbtext : "" ); - } else { - int mod = 0; - char *c = i->buffer()->text(); - const char *d = c_check(c); - if (d) { - fl_message("Error in callback: %s",d); - if (i->window()) i->window()->make_current(); - haderror = 1; - } - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected) { - o->callback(c); - mod = 1; - } - } - if (mod) set_modflag(1); - free(c); - } -} - -void comment_cb(Fl_Text_Editor* i, void *v) { - if (v == LOAD) { - const char *cmttext = current_widget->comment(); - i->buffer()->text( cmttext ? cmttext : "" ); - } else { - int mod = 0; - char *c = i->buffer()->text(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected) { - o->comment(c); - mod = 1; - } - } - if (mod) set_modflag(1); - free(c); - } -} - -void user_data_cb(Fl_Input *i, void *v) { - if (v == LOAD) { - i->value(current_widget->user_data()); - } else { - int mod = 0; - const char *c = i->value(); - const char *d = c_check(c); - if (d) {fl_message("Error in user_data: %s",d); haderror = 1; return;} - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected) { - o->user_data(c); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void user_data_type_cb(Fl_Input_Choice *i, void *v) { - static const char *dflt = "void*"; - if (v == LOAD) { - const char *c = current_widget->user_data_type(); - if (!c) c = dflt; - i->value(c); - } else { - int mod = 0; - const char *c = i->value(); - const char *d = c_check(c); - if (!*c) i->value(dflt); - else if (!strcmp(c,dflt)) c = 0; - if (!d) { - if (c && *c && c[strlen(c)-1] != '*' && strcmp(c,"long")) - d = "must be pointer or long"; - } - if (d) {fl_message("Error in type: %s",d); haderror = 1; return;} - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected) { - o->user_data_type(c); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -// "v_attributes" let user type in random code for attribute settings: - -void v_input_cb(Fl_Input* i, void* v) { - int n = fl_int(i->user_data()); - if (v == LOAD) { - i->value(current_widget->extra_code(n)); - } else { - int mod = 0; - const char *c = i->value(); - const char *d = c_check(c&&c[0]=='#' ? c+1 : c); - if (d) {fl_message("Error in %s: %s",i->label(),d); haderror = 1; return;} - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type *t = (Fl_Widget_Type*)o; - t->extra_code(n,c); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void subclass_cb(Fl_Input* i, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Menu_Item)) {i->deactivate(); return;} else i->activate(); - i->value(current_widget->subclass()); - } else { - int mod = 0; - const char *c = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type *t = (Fl_Widget_Type*)o; - t->subclass(c); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -// textstuff: set textfont, textsize, textcolor attributes: - -// default widget returns 0 to indicate not-implemented: -// The first parameter specifies the operation: -// 0: get all values -// 1: set the text font -// 2: set the text size -// 3: set the text color -// 4: get all default values for this type -int Fl_Widget_Type::textstuff(int, Fl_Font&, int&, Fl_Color&) { - return 0; -} - -void textfont_cb(Fl_Choice* i, void* v) { - Fl_Font n; int s; Fl_Color c; - if (v == LOAD) { - if (!current_widget->textstuff(0,n,s,c)) {i->deactivate(); return;} - i->activate(); - if (n > 15) n = FL_HELVETICA; - i->value(n); - } else { - int mod = 0; - n = (Fl_Font)i->value(); - if (n <= 0) n = layout->textfont; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->textstuff(1,n,s,c); - q->o->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void textsize_cb(Fl_Value_Input* i, void* v) { - Fl_Font n; int s; Fl_Color c; - if (v == LOAD) { - if (!current_widget->textstuff(0,n,s,c)) {i->deactivate(); return;} - i->activate(); - } else { - int mod = 0; - s = int(i->value()); - if (s <= 0) s = layout->textsize; - if (s <= 0) s = layout->labelsize; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->textstuff(2,n,s,c); - q->o->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); - } - i->value(s); -} - -void textcolor_common(Fl_Color c) { - Fl_Font n; int s; - int mod = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - q->textstuff(3,n,s,c); q->o->redraw(); - mod = 1; - } - } - if (mod) set_modflag(1); -} - -void textcolor_cb(Fl_Button* i, void* v) { - Fl_Font n; int s; Fl_Color c; - if (v == LOAD) { - if (!current_widget->textstuff(0,n,s,c)) {i->deactivate(); return;} - i->activate(); - } else { - c = i->color(); - Fl_Color d = fl_show_colormap(c); - if (d == c) return; - c = d; - textcolor_common(c); - } - i->color(c); i->labelcolor(fl_contrast(FL_BLACK,c)); i->redraw(); -} - -void textcolor_menu_cb(Fl_Menu_Button* i, void* v) { - Fl_Font n; int s; Fl_Color c; - if (v == LOAD) { - if (!current_widget->textstuff(0,n,s,c)) {i->deactivate(); return;} - i->activate(); - } else { - c = i->color(); - Fl_Color d = (Fl_Color)(i->mvalue()->argument()); - if (d == c) return; - c = d; - textcolor_common(c); - w_textcolor->color(c); w_textcolor->labelcolor(fl_contrast(FL_BLACK,c)); w_textcolor->redraw(); - } -} - -void image_spacing_cb(Fl_Value_Input* i, void* v) { - int s; - if (v == LOAD) { - if (!current_widget->is_true_widget()) { - i->deactivate(); - i->value(0); - } else { - i->activate(); - i->value(((Fl_Widget_Type*)current_widget)->o->label_image_spacing()); - } - } else { - int mod = 0; - s = int(i->value()); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->o->label_image_spacing() != s) { - q->o->label_image_spacing(s); - if (!(q->o->align() & FL_ALIGN_INSIDE) && q->o->window()) - q->o->window()->damage(FL_DAMAGE_EXPOSE); // outside labels - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void h_label_margin_cb(Fl_Value_Input* i, void* v) { - int s; - if (v == LOAD) { - if (!current_widget->is_true_widget()) { - i->deactivate(); - i->value(0); - } else { - i->activate(); - i->value(((Fl_Widget_Type*)current_widget)->o->horizontal_label_margin()); - } - } else { - int mod = 0; - s = int(i->value()); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->o->horizontal_label_margin() != s) { - q->o->horizontal_label_margin(s); - if (!(q->o->align() & FL_ALIGN_INSIDE) && q->o->window()) - q->o->window()->damage(FL_DAMAGE_EXPOSE); // outside labels - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void v_label_margin_cb(Fl_Value_Input* i, void* v) { - int s; - if (v == LOAD) { - if (!current_widget->is_true_widget()) { - i->deactivate(); - i->value(0); - } else { - i->activate(); - i->value(((Fl_Widget_Type*)current_widget)->o->vertical_label_margin()); - } - } else { - int mod = 0; - s = int(i->value()); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_true_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->o->vertical_label_margin() != s) { - q->o->vertical_label_margin(s); - if (!(q->o->align() & FL_ALIGN_INSIDE) && q->o->window()) - q->o->window()->damage(FL_DAMAGE_EXPOSE); // outside labels - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// -// Kludges to the panel for subclasses: - -void min_w_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) return; - i->value(((Fl_Window_Type*)current_widget)->sr_min_w); - } else { - int mod = 0; - undo_checkpoint(); - int n = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - ((Fl_Window_Type*)current_widget)->sr_min_w = n; - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void min_h_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) return; - i->value(((Fl_Window_Type*)current_widget)->sr_min_h); - } else { - int mod = 0; - undo_checkpoint(); - int n = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - ((Fl_Window_Type*)current_widget)->sr_min_h = n; - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void max_w_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) return; - i->value(((Fl_Window_Type*)current_widget)->sr_max_w); - } else { - int mod = 0; - undo_checkpoint(); - int n = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - ((Fl_Window_Type*)current_widget)->sr_max_w = n; - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void max_h_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) return; - i->value(((Fl_Window_Type*)current_widget)->sr_max_h); - } else { - int mod = 0; - undo_checkpoint(); - int n = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - ((Fl_Window_Type*)current_widget)->sr_max_h = n; - mod = 1; - } - } - if (mod) set_modflag(1); - } -} - -void set_min_size_cb(Fl_Button*, void* v) { - if (v == LOAD) { - } else { - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - Fl_Window_Type *win = (Fl_Window_Type*)current_widget; - win->sr_min_w = win->o->w(); - win->sr_min_h = win->o->h(); - mod = 1; - } - } - propagate_load(the_panel, LOAD); - if (mod) set_modflag(1); - } -} - -void set_max_size_cb(Fl_Button*, void* v) { - if (v == LOAD) { - } else { - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - Fl_Window_Type *win = (Fl_Window_Type*)current_widget; - win->sr_max_w = win->o->w(); - win->sr_max_h = win->o->h(); - mod = 1; - } - } - propagate_load(the_panel, LOAD); - if (mod) set_modflag(1); - } -} - -void slider_size_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Slider)) {i->deactivate(); return;} - i->activate(); - i->value(((Fl_Slider*)(current_widget->o))->slider_size()); - } else { - int mod = 0; - undo_checkpoint(); - double n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->is_a(ID_Slider)) { - ((Fl_Slider*)(q->o))->slider_size(n); - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void min_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Valuator_)) { - i->activate(); - i->value(((Fl_Valuator*)(current_widget->o))->minimum()); - } else if (current_widget->is_a(ID_Spinner)) { - i->activate(); - i->value(((Fl_Spinner*)(current_widget->o))->minimum()); - } else { - i->deactivate(); - return; - } - } else { - int mod = 0; - undo_checkpoint(); - double n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->is_a(ID_Valuator_)) { - ((Fl_Valuator*)(q->o))->minimum(n); - q->o->redraw(); - mod = 1; - } else if (q->is_a(ID_Spinner)) { - ((Fl_Spinner*)(q->o))->minimum(n); - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void max_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Valuator_)) { - i->activate(); - i->value(((Fl_Valuator*)(current_widget->o))->maximum()); - } else if (current_widget->is_a(ID_Spinner)) { - i->activate(); - i->value(((Fl_Spinner*)(current_widget->o))->maximum()); - } else { - i->deactivate(); - return; - } - } else { - int mod = 0; - undo_checkpoint(); - double n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->is_a(ID_Valuator_)) { - ((Fl_Valuator*)(q->o))->maximum(n); - q->o->redraw(); - mod = 1; - } else if (q->is_a(ID_Spinner)) { - ((Fl_Spinner*)(q->o))->maximum(n); - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void step_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Valuator_)) { - i->activate(); - i->value(((Fl_Valuator*)(current_widget->o))->step()); - } else if (current_widget->is_a(ID_Spinner)) { - i->activate(); - i->value(((Fl_Spinner*)(current_widget->o))->step()); - } else { - i->deactivate(); - return; - } - } else { - int mod = 0; - undo_checkpoint(); - double n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->is_a(ID_Valuator_)) { - ((Fl_Valuator*)(q->o))->step(n); - q->o->redraw(); - mod = 1; - } else if (q->is_a(ID_Spinner)) { - ((Fl_Spinner*)(q->o))->step(n); - q->o->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -void value_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Valuator_)) { - i->activate(); - i->value(((Fl_Valuator*)(current_widget->o))->value()); - } else if (current_widget->is_button()) { - i->activate(); - i->value(((Fl_Button*)(current_widget->o))->value()); - } else if (current_widget->is_a(ID_Spinner)) { - i->activate(); - i->value(((Fl_Spinner*)(current_widget->o))->value()); - } else - i->deactivate(); - } else { - int mod = 0; - undo_checkpoint(); - double n = i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->is_a(ID_Valuator_)) { - ((Fl_Valuator*)(q->o))->value(n); - mod = 1; - } else if (q->is_button()) { - ((Fl_Button*)(q->o))->value(n != 0); - if (q->is_a(ID_Menu_Item)) q->redraw(); - mod = 1; - } else if (q->is_a(ID_Spinner)) { - ((Fl_Spinner*)(q->o))->value(n); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -// The following three callbacks cooperate, showing only one of the groups of -// widgets that use the same space in the dialog. - -void values_group_cb(Fl_Group* g, void* v) { - if (v == LOAD) { - if ( current_widget->is_a(ID_Flex) - || current_widget->is_a(ID_Grid) - || current_widget->is_a(ID_Window)) - { - g->hide(); - } else { - g->show(); - } - propagate_load(g, v); - } -} - -void flex_margin_group_cb(Fl_Group* g, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Flex)) { - g->show(); - } else { - g->hide(); - } - propagate_load(g, v); - } -} - -void size_range_group_cb(Fl_Group* g, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Window)) { - g->show(); - } else { - g->hide(); - } - propagate_load(g, v); - } -} - - -static void flex_margin_cb(Fl_Value_Input* i, void* v, - void (*load_margin)(Fl_Flex*,Fl_Value_Input*), - int (*update_margin)(Fl_Flex*,int)) { - if (v == LOAD) { - if (current_widget->is_a(ID_Flex)) { - load_margin((Fl_Flex*)current_widget->o, i); - } - } else { - int mod = 0; - int new_value = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Flex)) { - Fl_Flex_Type* q = (Fl_Flex_Type*)o; - Fl_Flex* w = (Fl_Flex*)q->o; - if (update_margin(w, new_value)) { - w->layout(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -static void load_left_margin(Fl_Flex *w, Fl_Value_Input* i) -{ - int v; - w->margin(&v, NULL, NULL, NULL); - i->value((double)v); -} - -static int update_left_margin(Fl_Flex *w, int new_value) -{ - int l, t, r, b; - w->margin(&l, &t, &r, &b); - if (new_value!=l) { - w->margin(new_value, t, r, b); - return 1; - } else { - return 0; - } -} - -void flex_margin_left_cb(Fl_Value_Input* i, void* v) { - flex_margin_cb(i, v, load_left_margin, update_left_margin); -} - -static void load_top_margin(Fl_Flex *w, Fl_Value_Input* i) -{ - int v; - w->margin(NULL, &v, NULL, NULL); - i->value((double)v); -} - -static int update_top_margin(Fl_Flex *w, int new_value) -{ - int l, t, r, b; - w->margin(&l, &t, &r, &b); - if (new_value!=t) { - w->margin(l, new_value, r, b); - return 1; - } else { - return 0; - } -} - -void flex_margin_top_cb(Fl_Value_Input* i, void* v) { - flex_margin_cb(i, v, load_top_margin, update_top_margin); -} - -static void load_right_margin(Fl_Flex *w, Fl_Value_Input* i) -{ - int v; - w->margin(NULL, NULL, &v, NULL); - i->value((double)v); -} - -static int update_right_margin(Fl_Flex *w, int new_value) -{ - int l, t, r, b; - w->margin(&l, &t, &r, &b); - if (new_value!=r) { - w->margin(l, t, new_value, b); - return 1; - } else { - return 0; - } -} - -void flex_margin_right_cb(Fl_Value_Input* i, void* v) { - flex_margin_cb(i, v, load_right_margin, update_right_margin); -} - -static void load_bottom_margin(Fl_Flex *w, Fl_Value_Input* i) -{ - int v; - w->margin(NULL, NULL, NULL, &v); - i->value((double)v); -} - -static int update_bottom_margin(Fl_Flex *w, int new_value) -{ - int l, t, r, b; - w->margin(&l, &t, &r, &b); - if (new_value!=b) { - w->margin(l, t, r, new_value); - return 1; - } else { - return 0; - } -} - -void flex_margin_bottom_cb(Fl_Value_Input* i, void* v) { - flex_margin_cb(i, v, load_bottom_margin, update_bottom_margin); -} - -static void load_gap(Fl_Flex *w, Fl_Value_Input* i) -{ - int v = w->gap(); - i->value((double)v); -} - -static int update_gap(Fl_Flex *w, int new_value) -{ - int g = w->gap(); - if (new_value!=g) { - w->gap(new_value); - return 1; - } else { - return 0; - } -} - -void flex_margin_gap_cb(Fl_Value_Input* i, void* v) { - flex_margin_cb(i, v, load_gap, update_gap); -} - -void position_group_cb(Fl_Group* g, void* v) { - if (v == LOAD) { - if (Fl_Flex_Type::parent_is_flex(current_widget)) { - g->hide(); - } else { - g->show(); - } - } - propagate_load(g, v); -} - -void flex_size_group_cb(Fl_Group* g, void* v) { - if (v == LOAD) { - if (Fl_Flex_Type::parent_is_flex(current_widget)) { - g->show(); - } else { - g->hide(); - } - } - propagate_load(g, v); -} - -void flex_size_cb(Fl_Value_Input* i, void* v) { - if (v == LOAD) { - if (Fl_Flex_Type::parent_is_flex(current_widget)) { - i->value(Fl_Flex_Type::size(current_widget)); - } - } else { - int mod = 0; - int new_size = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget() && Fl_Flex_Type::parent_is_flex(o)) { - Fl_Widget* w = (Fl_Widget*)((Fl_Widget_Type*)o)->o; - Fl_Flex* f = (Fl_Flex*)((Fl_Flex_Type*)o->parent)->o; - int was_fixed = f->fixed(w); - if (new_size==0) { - if (was_fixed) { - f->fixed(w, 0); - f->layout(); - widget_flex_fixed->value(0); - mod = 1; - } - } else { - int old_size = Fl_Flex_Type::size(o); - if (old_size!=new_size || !was_fixed) { - f->fixed(w, new_size); - f->layout(); - widget_flex_fixed->value(1); - mod = 1; - } - } - } - } - if (mod) set_modflag(1); - } -} - -void flex_fixed_cb(Fl_Check_Button* i, void* v) { - if (v == LOAD) { - if (Fl_Flex_Type::parent_is_flex(current_widget)) { - i->value(Fl_Flex_Type::is_fixed(current_widget)); - } - } else { - int mod = 0; - int new_fixed = (int)i->value(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget() && Fl_Flex_Type::parent_is_flex(o)) { - Fl_Widget* w = (Fl_Widget*)((Fl_Widget_Type*)o)->o; - Fl_Flex* f = (Fl_Flex*)((Fl_Flex_Type*)o->parent)->o; - int was_fixed = f->fixed(w); - if (new_fixed==0) { - if (was_fixed) { - f->fixed(w, 0); - f->layout(); - mod = 1; - } - } else { - if (!was_fixed) { - f->fixed(w, Fl_Flex_Type::size(o)); - f->layout(); - mod = 1; - } - } - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -// subtypes: - -Fl_Menu_Item *Fl_Widget_Type::subtypes() {return 0;} - -void subtype_cb(Fl_Choice* i, void* v) { - static Fl_Menu_Item empty_type_menu[] = { - {"Normal",0,0,(void*)0}, - {0}}; - - if (v == LOAD) { - Fl_Menu_Item* m = current_widget->subtypes(); - if (!m) { - i->menu(empty_type_menu); - i->value(0); - i->deactivate(); - } else { - i->menu(m); - int j; - for (j = 0;; j++) { - if (!m[j].text) {j = 0; break;} - if (current_widget->is_a(ID_Spinner)) { - if (m[j].argument() == ((Fl_Spinner*)current_widget->o)->type()) break; - } else { - if (m[j].argument() == current_widget->o->type()) break; - } - } - i->value(j); - i->activate(); - } - i->redraw(); - } else { - int mod = 0; - int n = int(i->mvalue()->argument()); - Fl_Menu_Item* m = current_widget->subtypes(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_widget()) { - Fl_Widget_Type* q = (Fl_Widget_Type*)o; - if (q->subtypes()==m) { - if (q->is_a(ID_Spinner)) - ((Fl_Spinner*)q->o)->type(n); - else if (q->is_a(ID_Flex)) - ((Fl_Flex_Type*)q)->change_subtype_to(n); - else - q->o->type(n); - q->redraw(); - mod = 1; - } - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -void propagate_load(Fl_Group* g, void* v) { - if (v == LOAD) { - Fl_Widget*const* a = g->array(); - for (int i=g->children(); i--;) { - Fl_Widget* o = *a++; - o->do_callback(o, LOAD, FL_REASON_USER); - } - } -} - -void set_cb(Fl_Button*, void*) { - haderror = 0; - Fl_Widget*const* a = the_panel->array(); - for (int i=the_panel->children(); i--;) { - Fl_Widget* o = *a++; - if (o->changed()) { - o->do_callback(); - if (haderror) return; - o->clear_changed(); - } - } -} - -void ok_cb(Fl_Return_Button* o, void* v) { - set_cb(o,v); - if (!haderror) the_panel->hide(); -} - -void toggle_overlays(Fl_Widget *,void *); // in Fl_Window_Type.cxx -void overlay_cb(Fl_Button*o,void *v) { - toggle_overlays(o,v); -} - -void leave_live_mode_cb(Fl_Widget*, void*); - -void live_mode_cb(Fl_Button*o,void *) { - /// \todo live mode should end gracefully when the application quits - /// or when the user closes the live widget - static Fl_Type *live_type = 0L; - static Fl_Widget *live_widget = 0L; - static Fl_Window *live_window = 0L; - // if 'o' is 0, we must quit live mode - if (!o) { - o = wLiveMode; - o->value(0); - } - if (o->value()) { - if (numselected == 1) { - Fl_Group::current(0L); - live_widget = current_widget->enter_live_mode(1); - if (live_widget) { - live_type = current_widget; - Fl_Group::current(0); - int w = live_widget->w(); - int h = live_widget->h(); - live_window = new Fl_Double_Window(w+20, h+55, "Fluid Live Resize"); - live_window->box(FL_FLAT_BOX); - live_window->color(FL_GREEN); - Fl_Group *rsz = new Fl_Group(0, h+20, 130, 35); - rsz->box(FL_NO_BOX); - Fl_Box *rsz_dummy = new Fl_Box(110, h+20, 1, 25); - rsz_dummy->box(FL_NO_BOX); - rsz->resizable(rsz_dummy); - Fl_Button *btn = new Fl_Button(10, h+20, 100, 25, "Exit Live Resize"); - btn->labelsize(12); - btn->callback(leave_live_mode_cb); - rsz->end(); - live_window->add(live_widget); - live_widget->position(10, 10); - live_window->resizable(live_widget); - live_window->set_modal(); // block all other UI - live_window->callback(leave_live_mode_cb); - if (current_widget->is_a(ID_Window)) { - Fl_Window_Type *w = (Fl_Window_Type*)current_widget; - int mw = w->sr_min_w; if (mw>0) mw += 20; - int mh = w->sr_min_h; if (mh>0) mh += 55; - int MW = w->sr_max_w; if (MW>0) MW += 20; - int MH = w->sr_max_h; if (MH>2) MH += 55; - if (mw || mh || MW || MH) - live_window->size_range(mw, mh, MW, MH); - } - live_window->show(); - live_widget->show(); - } else o->value(0); - } else o->value(0); - } else { - if (live_type) - live_type->leave_live_mode(); - if (live_window) { - live_window->hide(); - Fl::delete_widget(live_window); - } - live_type = 0L; - live_widget = 0L; - live_window = 0L; - } -} - -// update the panel according to current widget set: -void load_panel() { - if (!the_panel) return; - - // find all the Fl_Widget subclasses currently selected: - numselected = 0; - current_widget = 0; - if (Fl_Type::current) { - if (Fl_Type::current->is_widget()) - current_widget=(Fl_Widget_Type*)Fl_Type::current; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->is_widget() && o->selected) { - numselected++; - if (!current_widget) current_widget = (Fl_Widget_Type*)o; - } - } - } - if (current_widget && current_widget->is_a(ID_Grid)) { - if (widget_tab_grid->parent()!=widget_tabs) - widget_tabs->add(widget_tab_grid); - } else { - if (widget_tab_grid->parent()==widget_tabs) { - widget_tabs_repo->add(widget_tab_grid); - } - } - if (current_widget && current_widget->parent && current_widget->parent->is_a(ID_Grid)) { - if (widget_tab_grid_child->parent()!=widget_tabs) - widget_tabs->add(widget_tab_grid_child); - } else { - if (widget_tab_grid_child->parent()==widget_tabs) { - widget_tabs_repo->add(widget_tab_grid_child); - } - } - if (numselected) - propagate_load(the_panel, LOAD); - else - the_panel->hide(); -} - -extern Fl_Window *widgetbin_panel; - -// This is called when user double-clicks an item, open or update the panel: -void Fl_Widget_Type::open() { - bool adjust_position = false; - if (!the_panel) { - the_panel = make_widget_panel(); - adjust_position = true; - } - load_panel(); - if (numselected) { - the_panel->show(); - if (adjust_position) { - if (widgetbin_panel && widgetbin_panel->visible()) { - if ( (the_panel->x()+the_panel->w() > widgetbin_panel->x()) - && (the_panel->x() < widgetbin_panel->x()+widgetbin_panel->w()) - && (the_panel->y()+the_panel->h() > widgetbin_panel->y()) - && (the_panel->y() < widgetbin_panel->y()+widgetbin_panel->h()) ) - { - if (widgetbin_panel->y()+widgetbin_panel->h()+the_panel->h() > Fl::h()) - the_panel->position(the_panel->x(), widgetbin_panel->y()-the_panel->h()-30); - else - the_panel->position(the_panel->x(), widgetbin_panel->y()+widgetbin_panel->h()+30); - } - } - } - } -} - -extern void redraw_overlays(); -extern void check_redraw_corresponding_parent(Fl_Type*); -extern void redraw_browser(); -extern void update_codeview_position(); - -// Called when ui changes what objects are selected: -// p is selected object, null for all deletions (we must throw away -// old panel in that case, as the object may no longer exist) -void selection_changed(Fl_Type *p) { - // store all changes to the current selected objects: - if (p && the_panel && the_panel->visible()) { - set_cb(0,0); - // if there was an error, we try to leave the selected set unchanged: - if (haderror) { - Fl_Type *q = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - o->new_selected = o->selected; - if (!q && o->selected) q = o; - } - if (!p || !p->selected) p = q; - Fl_Type::current = p; - redraw_browser(); - return; - } - } - // update the selected flags to new set: - Fl_Type *q = 0; - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - o->selected = o->new_selected; - if (!q && o->selected) q = o; - } - if (!p || !p->selected) p = q; - Fl_Type::current = p; - check_redraw_corresponding_parent(p); - redraw_overlays(); - // load the panel with the new settings: - load_panel(); - // update the code viewer to show the code for the selected object - update_codeview_position(); -} - -//////////////////////////////////////////////////////////////// -// Writing the C code: - -// test to see if user named a function, or typed in code: -int is_name(const char *c) { - for (; *c; c++) - if ((ispunct(*c)||*c=='\n') && *c!='_' && *c!=':') return 0; - return 1; -} - -// Test to see if name() is an array entry. If so, and this is the -// highest number, return name[num+1]. Return null if not the highest -// number or a field or function. Return name() if not an array entry. -const char *array_name(Fl_Widget_Type *o) { - const char *c = o->name(); - if (!c) return 0; - const char *d; - for (d = c; *d != '['; d++) { - if (!*d) return c; - if (ispunct(*d) && *d!='_') return 0; - } - int num = atoi(d+1); - int sawthis = 0; - Fl_Type *t = o->prev; - Fl_Type *tp = o; - const char *cn = o->class_name(1); - for (; t && t->class_name(1) == cn; tp = t, t = t->prev) {/*empty*/} - for (t = tp; t && t->class_name(1) == cn; t = t->next) { - if (t == o) {sawthis=1; continue;} - const char *e = t->name(); - if (!e) continue; - if (strncmp(c,e,d-c)) continue; - int n1 = atoi(e+(d-c)+1); - if (n1 > num || (n1==num && sawthis)) return 0; - } - static char buffer[128]; - // MRS: we want strncpy() here... - strncpy(buffer,c,d-c+1); - snprintf(buffer+(d-c+1),sizeof(buffer) - (d-c+1), "%d]",num+1); - return buffer; -} - -// Test to see if extra code is a declaration: -int isdeclare(const char *c) { - while (isspace(*c)) c++; - if (*c == '#') return 1; - if (!strncmp(c,"extern",6)) return 1; - if (!strncmp(c,"typedef",7)) return 1; - if (!strncmp(c,"using",5)) return 1; - return 0; -} - -void Fl_Widget_Type::write_static(fld::io::Code_Writer& f) { - const char* t = subclassname(this); - if (!subclass() || (is_class() && !strncmp(t, "Fl_", 3))) { - f.write_h_once("#include "); - f.write_h_once("#include ", t); - } - for (int n=0; n < NUM_EXTRA_CODE; n++) { - if (extra_code(n) && isdeclare(extra_code(n))) - f.write_h_once("%s", extra_code(n)); - } - if (callback() && is_name(callback())) { - int write_extern_declaration = 1; - char buf[1024]; snprintf(buf, 1023, "%s(*)", callback()); - if (is_in_class()) { - if (has_function("static void", buf)) - write_extern_declaration = 0; - } else { - if (has_toplevel_function(0L, buf)) - write_extern_declaration = 0; - } - if (write_extern_declaration) - f.write_h_once("extern void %s(%s*, %s);", callback(), t, - user_data_type() ? user_data_type() : "void*"); - } - const char* k = class_name(1); - const char* c = array_name(this); - if (c && !k && !is_class()) { - f.write_c("\n"); - if (!public_) f.write_c("static "); - else f.write_h("extern %s *%s;\n", t, c); - if (strchr(c, '[') == NULL) f.write_c("%s *%s=(%s *)0;\n", t, c, t); - else f.write_c("%s *%s={(%s *)0};\n", t, c, t); - } - if (callback() && !is_name(callback())) { - // see if 'o' or 'v' used, to prevent unused argument warnings: - int use_o = 0; - int use_v = 0; - const char *d; - for (d = callback(); *d;) { - if (*d == 'o' && !is_id(d[1])) use_o = 1; - if (*d == 'v' && !is_id(d[1])) use_v = 1; - do d++; while (is_id(*d)); - while (*d && !is_id(*d)) d++; - } - const char* cn = callback_name(f); - if (k) { - f.write_c("\nvoid %s::%s_i(%s*", k, cn, t); - } else { - f.write_c("\nstatic void %s(%s*", cn, t); - } - if (use_o) f.write_c(" o"); - const char* ut = user_data_type() ? user_data_type() : "void*"; - f.write_c(", %s", ut); - if (use_v) f.write_c(" v"); - f.write_c(") {\n"); - // Matt: disabled f.tag(FD_TAG_GENERIC, 0); - f.write_c_indented(callback(), 1, 0); - if (*(d-1) != ';' && *(d-1) != '}') { - const char *p = strrchr(callback(), '\n'); - if (p) p ++; - else p = callback(); - // Only add trailing semicolon if the last line is not a preprocessor - // statement... - if (*p != '#' && *p) f.write_c(";"); - } - f.write_c("\n"); - // Matt: disabled f.tag(FD_TAG_WIDGET_CALLBACK, get_uid()); - f.write_c("}\n"); - if (k) { - f.write_c("void %s::%s(%s* o, %s v) {\n", k, cn, t, ut); - f.write_c("%s((%s*)(o", f.indent(1), k); - Fl_Type *q = 0; - for (Fl_Type* p = parent; p && p->is_widget(); q = p, p = p->parent) - f.write_c("->parent()"); - if (!q || !q->is_a(ID_Widget_Class)) - f.write_c("->user_data()"); - f.write_c("))->%s_i(o,v);\n}\n", cn); - } - } - if (image) { - if (!f.c_contains(image)) - image->write_static(f, compress_image_); - } - if (inactive) { - if (!f.c_contains(inactive)) - inactive->write_static(f, compress_deimage_); - } -} - -void Fl_Widget_Type::write_code1(fld::io::Code_Writer& f) { - const char* t = subclassname(this); - const char *c = array_name(this); - if (c) { - if (class_name(1)) { - f.write_public(public_); - f.write_h("%s%s *%s;\n", f.indent(1), t, c); - } - } - if (class_name(1) && callback() && !is_name(callback())) { - const char* cn = callback_name(f); - const char* ut = user_data_type() ? user_data_type() : "void*"; - f.write_public(0); - f.write_h("%sinline void %s_i(%s*, %s);\n", f.indent(1), cn, t, ut); - f.write_h("%sstatic void %s(%s*, %s);\n", f.indent(1), cn, t, ut); - } - // figure out if local variable will be used (prevent compiler warnings): - int wused = !name() && is_a(ID_Window); - const char *ptr; - - f.varused = wused; - - if (!name() && !f.varused) { - f.varused |= can_have_children(); - - if (!f.varused) { - f.varused_test = 1; - write_widget_code(f); - f.varused_test = 0; - } - } - - if (!f.varused) { - for (int n=0; n < NUM_EXTRA_CODE; n++) - if (extra_code(n) && !isdeclare(extra_code(n))) - { - int instring = 0; - int inname = 0; - int incomment = 0; - int incppcomment = 0; - for (ptr = extra_code(n); *ptr; ptr ++) { - if (instring) { - if (*ptr == '\\') ptr++; - else if (*ptr == '\"') instring = 0; - } else if (inname && !isalnum(*ptr & 255)) { - inname = 0; - } else if (*ptr == '/' && ptr[1]=='*') { - incomment = 1; ptr++; - } else if (incomment) { - if (*ptr == '*' && ptr[1]=='/') { - incomment = 0; ptr++; - } - } else if (*ptr == '/' && ptr[1]=='/') { - incppcomment = 1; ptr++; - } else if (incppcomment) { - if (*ptr == '\n') - incppcomment = 0; - } else if (*ptr == '\"') { - instring = 1; - } else if (isalnum(*ptr & 255) || *ptr == '_') { - size_t len = strspn(ptr, "0123456789_" - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - if (!strncmp(ptr, "o", len)) { - f.varused = 1; - break; - } else { - ptr += len - 1; - } - } - } - } - } - - f.write_c("%s{ ", f.indent()); - write_comment_inline_c(f); - if (f.varused) f.write_c("%s* o = ", t); - if (name()) f.write_c("%s = ", name()); - if (is_a(ID_Window)) { - // Handle special case where user is faking a Fl_Group type as a window, - // there is no 2-argument constructor in that case: - if (!strstr(t, "Window")) - f.write_c("new %s(0, 0, %d, %d", t, o->w(), o->h()); - else - f.write_c("new %s(%d, %d", t, o->w(), o->h()); - } else if (is_a(ID_Menu_Bar) - && ((Fl_Menu_Bar_Type*)this)->is_sys_menu_bar() - && is_in_class()) { - f.write_c("(%s*)new %s(%d, %d, %d, %d", - t, ((Fl_Menu_Bar_Type*)this)->sys_menubar_proxy_name(), - o->x(), o->y(), o->w(), o->h()); - } else { - f.write_c("new %s(%d, %d, %d, %d", t, o->x(), o->y(), o->w(), o->h()); - } - if (label() && *label()) { - f.write_c(", "); - switch (g_project.i18n_type) { - case FD_I18N_NONE : /* None */ - f.write_cstring(label()); - break; - case FD_I18N_GNU : /* GNU gettext */ - f.write_c("%s(", g_project.i18n_gnu_function.c_str()); - f.write_cstring(label()); - f.write_c(")"); - break; - case FD_I18N_POSIX : /* POSIX catgets */ - f.write_c("catgets(%s,%s,%d,", - g_project.i18n_pos_file.empty() ? "_catalog" : g_project.i18n_pos_file.c_str(), - g_project.i18n_pos_set.c_str(), msgnum()); - f.write_cstring(label()); - f.write_c(")"); - break; - } - } - f.write_c(");\n"); - - f.indentation++; - - // Avoid compiler warning for unused variable. - // Also avoid quality control warnings about incorrect allocation error handling. - if (wused) f.write_c("%sw = o; (void)w;\n", f.indent()); - - write_widget_code(f); -} - -void Fl_Widget_Type::write_color(fld::io::Code_Writer& f, const char* field, Fl_Color color) { - const char* color_name = 0; - switch (color) { - case FL_FOREGROUND_COLOR: color_name = "FL_FOREGROUND_COLOR"; break; - case FL_BACKGROUND2_COLOR: color_name = "FL_BACKGROUND2_COLOR"; break; - case FL_INACTIVE_COLOR: color_name = "FL_INACTIVE_COLOR"; break; - case FL_SELECTION_COLOR: color_name = "FL_SELECTION_COLOR"; break; - case FL_GRAY0: color_name = "FL_GRAY0"; break; - case FL_DARK3: color_name = "FL_DARK3"; break; - case FL_DARK2: color_name = "FL_DARK2"; break; - case FL_DARK1: color_name = "FL_DARK1"; break; - case FL_BACKGROUND_COLOR: color_name = "FL_BACKGROUND_COLOR"; break; - case FL_LIGHT1: color_name = "FL_LIGHT1"; break; - case FL_LIGHT2: color_name = "FL_LIGHT2"; break; - case FL_LIGHT3: color_name = "FL_LIGHT3"; break; - case FL_BLACK: color_name = "FL_BLACK"; break; - case FL_RED: color_name = "FL_RED"; break; - case FL_GREEN: color_name = "FL_GREEN"; break; - case FL_YELLOW: color_name = "FL_YELLOW"; break; - case FL_BLUE: color_name = "FL_BLUE"; break; - case FL_MAGENTA: color_name = "FL_MAGENTA"; break; - case FL_CYAN: color_name = "FL_CYAN"; break; - case FL_DARK_RED: color_name = "FL_DARK_RED"; break; - case FL_DARK_GREEN: color_name = "FL_DARK_GREEN"; break; - case FL_DARK_YELLOW: color_name = "FL_DARK_YELLOW"; break; - case FL_DARK_BLUE: color_name = "FL_DARK_BLUE"; break; - case FL_DARK_MAGENTA: color_name = "FL_DARK_MAGENTA"; break; - case FL_DARK_CYAN: color_name = "FL_DARK_CYAN"; break; - case FL_WHITE: color_name = "FL_WHITE"; break; - } - const char *var = is_class() ? "this" : name() ? name() : "o"; - if (color_name) { - f.write_c("%s%s->%s(%s);\n", f.indent(), var, field, color_name); - } else { - f.write_c("%s%s->%s((Fl_Color)%d);\n", f.indent(), var, field, color); - } -} - -// this is split from write_code1(fld::io::Code_Writer& f) for Fl_Window_Type: -void Fl_Widget_Type::write_widget_code(fld::io::Code_Writer& f) { - Fl_Widget* tplate = ((Fl_Widget_Type*)factory)->o; - const char *var = is_class() ? "this" : name() ? name() : "o"; - - if (tooltip() && *tooltip()) { - f.write_c("%s%s->tooltip(",f.indent(), var); - switch (g_project.i18n_type) { - case FD_I18N_NONE : /* None */ - f.write_cstring(tooltip()); - break; - case FD_I18N_GNU : /* GNU gettext */ - f.write_c("%s(", g_project.i18n_gnu_function.c_str()); - f.write_cstring(tooltip()); - f.write_c(")"); - break; - case FD_I18N_POSIX : /* POSIX catgets */ - f.write_c("catgets(%s,%s,%d,", - g_project.i18n_pos_file.empty() ? "_catalog" : g_project.i18n_pos_file.c_str(), - g_project.i18n_pos_set.c_str(), - msgnum() + 1); - f.write_cstring(tooltip()); - f.write_c(")"); - break; - } - f.write_c(");\n"); - } - - if (is_a(ID_Spinner) && ((Fl_Spinner*)o)->type() != ((Fl_Spinner*)tplate)->type()) - f.write_c("%s%s->type(%d);\n", f.indent(), var, ((Fl_Spinner*)o)->type()); - else if (o->type() != tplate->type() && !is_a(ID_Window)) - f.write_c("%s%s->type(%d);\n", f.indent(), var, o->type()); - if (o->box() != tplate->box() || subclass()) - f.write_c("%s%s->box(FL_%s);\n", f.indent(), var, boxname(o->box())); - - // write shortcut command if needed - int shortcut = 0; - if (is_button()) shortcut = ((Fl_Button*)o)->shortcut(); - else if (is_a(ID_Input)) shortcut = ((Fl_Input_*)o)->shortcut(); - else if (is_a(ID_Value_Input)) shortcut = ((Fl_Value_Input*)o)->shortcut(); - else if (is_a(ID_Text_Display)) shortcut = ((Fl_Text_Display*)o)->shortcut(); - if (shortcut) { - int s = shortcut; - f.write_c("%s%s->shortcut(", f.indent(), var); - if (g_project.use_FL_COMMAND) { - if (s & FL_CTRL) { f.write_c("FL_CONTROL|"); s &= ~FL_CTRL; } - if (s & FL_META) { f.write_c("FL_COMMAND|"); s &= ~FL_META; } - } else { - if (s & FL_CTRL) { f.write_c("FL_CTRL|"); s &= ~FL_CTRL; } - if (s & FL_META) { f.write_c("FL_META|"); s &= ~FL_META; } - } - if (s & FL_SHIFT) { f.write_c("FL_SHIFT|"); s &= ~FL_SHIFT; } - if (s & FL_ALT) { f.write_c("FL_ALT|"); s &= ~FL_ALT; } - if ((s < 127) && isprint(s)) - f.write_c("'%c');\n", s); - else - f.write_c("0x%x);\n", s); - } - - if (is_a(ID_Button)) { - Fl_Button* b = (Fl_Button*)o; - if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var, - boxname(b->down_box())); - if (b->value()) f.write_c("%s%s->value(1);\n", f.indent(), var); - if (b->compact()) f.write_c("%s%s->compact(%d);\n", f.indent(), var, b->compact()); - } else if (is_a(ID_Input_Choice)) { - Fl_Input_Choice* b = (Fl_Input_Choice*)o; - if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var, - boxname(b->down_box())); - } else if (is_a(ID_Menu_Manager_)) { - Fl_Menu_* b = (Fl_Menu_*)o; - if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var, - boxname(b->down_box())); - } - if (o->color() != tplate->color() || subclass()) - write_color(f, "color", o->color()); - if (o->selection_color() != tplate->selection_color() || subclass()) - write_color(f, "selection_color", o->selection_color()); - if (image) { - image->write_code(f, bind_image_, var); - if (scale_image_w_ || scale_image_h_) { - f.write_c("%s%s->image()->scale(", f.indent(), var); - if (scale_image_w_>0) - f.write_c("%d, ", scale_image_w_); - else - f.write_c("%s->image()->data_w(), ", var); - if (scale_image_h_>0) - f.write_c("%d, 0, 1);\n", scale_image_h_); - else - f.write_c("%s->image()->data_h(), 0, 1);\n", var); - } - } - if (inactive) { - inactive->write_code(f, bind_deimage_, var, 1); - if (scale_deimage_w_ || scale_deimage_h_) { - f.write_c("%s%s->deimage()->scale(", f.indent(), var); - if (scale_deimage_w_>0) - f.write_c("%d, ", scale_deimage_w_); - else - f.write_c("%s->deimage()->data_w(), ", var); - if (scale_deimage_h_>0) - f.write_c("%d, 0, 1);\n", scale_deimage_h_); - else - f.write_c("%s->deimage()->data_h(), 0, 1);\n", var); - } - } - if (o->labeltype() != tplate->labeltype() || subclass()) - f.write_c("%s%s->labeltype(FL_%s);\n", f.indent(), var, - item_name(labeltypemenu, o->labeltype())); - if (o->labelfont() != tplate->labelfont() || subclass()) - f.write_c("%s%s->labelfont(%d);\n", f.indent(), var, o->labelfont()); - if (o->labelsize() != tplate->labelsize() || subclass()) - f.write_c("%s%s->labelsize(%d);\n", f.indent(), var, o->labelsize()); - if (o->labelcolor() != tplate->labelcolor() || subclass()) - write_color(f, "labelcolor", o->labelcolor()); - if (o->horizontal_label_margin() != tplate->horizontal_label_margin()) - f.write_c("%s%s->horizontal_label_margin(%d);\n", f.indent(), var, o->horizontal_label_margin()); - if (o->vertical_label_margin() != tplate->vertical_label_margin()) - f.write_c("%s%s->vertical_label_margin(%d);\n", f.indent(), var, o->vertical_label_margin()); - if (o->label_image_spacing() != tplate->label_image_spacing()) - f.write_c("%s%s->label_image_spacing(%d);\n", f.indent(), var, o->label_image_spacing()); - if (is_a(ID_Valuator_)) { - Fl_Valuator* v = (Fl_Valuator*)o; - Fl_Valuator* t = (Fl_Valuator*)(tplate); - if (v->minimum()!=t->minimum()) - f.write_c("%s%s->minimum(%g);\n", f.indent(), var, v->minimum()); - if (v->maximum()!=t->maximum()) - f.write_c("%s%s->maximum(%g);\n", f.indent(), var, v->maximum()); - if (v->step()!=t->step()) - f.write_c("%s%s->step(%g);\n", f.indent(), var, v->step()); - if (v->value()) { - if (is_a(ID_Scrollbar)) { // Fl_Scrollbar::value(double) is not available - f.write_c("%s%s->Fl_Slider::value(%g);\n", f.indent(), var, v->value()); - } else { - f.write_c("%s%s->value(%g);\n", f.indent(), var, v->value()); - } - } - if (is_a(ID_Slider)) { - double x = ((Fl_Slider*)v)->slider_size(); - double y = ((Fl_Slider*)t)->slider_size(); - if (x != y) f.write_c("%s%s->slider_size(%g);\n", f.indent(), var, x); - } - } - if (is_a(ID_Spinner)) { - Fl_Spinner* v = (Fl_Spinner*)o; - Fl_Spinner* t = (Fl_Spinner*)(tplate); - if (v->minimum()!=t->minimum()) - f.write_c("%s%s->minimum(%g);\n", f.indent(), var, v->minimum()); - if (v->maximum()!=t->maximum()) - f.write_c("%s%s->maximum(%g);\n", f.indent(), var, v->maximum()); - if (v->step()!=t->step()) - f.write_c("%s%s->step(%g);\n", f.indent(), var, v->step()); - if (v->value()!=1.0f) - f.write_c("%s%s->value(%g);\n", f.indent(), var, v->value()); - } - - {Fl_Font ff; int fs; Fl_Color fc; if (textstuff(4,ff,fs,fc)) { - Fl_Font g; int s; Fl_Color c; textstuff(0,g,s,c); - if (g != ff) f.write_c("%s%s->textfont(%d);\n", f.indent(), var, g); - if (s != fs) f.write_c("%s%s->textsize(%d);\n", f.indent(), var, s); - if (c != fc) write_color(f, "textcolor", c); - }} - const char* ud = user_data(); - if (class_name(1) && !parent->is_widget()) ud = "this"; - if (callback()) { - f.write_c("%s%s->callback((Fl_Callback*)%s", f.indent(), var, callback_name(f)); - if (ud) - f.write_c(", (void*)(%s));\n", ud); - else - f.write_c(");\n"); - } else if (ud) { - f.write_c("%s%s->user_data((void*)(%s));\n", f.indent(), var, ud); - } - if (o->align() != tplate->align() || subclass()) { - int i = o->align(); - f.write_c("%s%s->align(Fl_Align(%s", f.indent(), var, - item_name(alignmenu, i & ~FL_ALIGN_INSIDE)); - if (i & FL_ALIGN_INSIDE) f.write_c("|FL_ALIGN_INSIDE"); - f.write_c("));\n"); - } - Fl_When ww = o->when(); - if (ww != tplate->when() || subclass()) - f.write_c("%s%s->when(%s);\n", f.indent(), var, when_symbol_name(ww)); - if (!o->visible() && o->parent()) - f.write_c("%s%s->hide();\n", f.indent(), var); - if (!o->active()) - f.write_c("%s%s->deactivate();\n", f.indent(), var); - if (!is_a(ID_Group) && resizable()) - f.write_c("%sFl_Group::current()->resizable(%s);\n", f.indent(), var); - if (hotspot()) { - if (is_class()) - f.write_c("%shotspot(%s);\n", f.indent(), var); - else if (is_a(ID_Window)) - f.write_c("%s%s->hotspot(%s);\n", f.indent(), var, var); - else - f.write_c("%s%s->window()->hotspot(%s);\n", f.indent(), var, var); - } -} - -void Fl_Widget_Type::write_extra_code(fld::io::Code_Writer& f) { - for (int n=0; n < NUM_EXTRA_CODE; n++) - if (extra_code(n) && !isdeclare(extra_code(n))) - f.write_c("%s%s\n", f.indent(), extra_code(n)); -} - -void Fl_Widget_Type::write_block_close(fld::io::Code_Writer& f) { - f.indentation--; - f.write_c("%s} // %s* %s\n", f.indent(), subclassname(this), - name() ? name() : "o"); -} - -void Fl_Widget_Type::write_code2(fld::io::Code_Writer& f) { - write_extra_code(f); - write_block_close(f); -} - -//////////////////////////////////////////////////////////////// - -void Fl_Widget_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Type::write_properties(f); - f.write_indent(level+1); - switch (public_) { - case 0: f.write_string("private"); break; - case 1: break; - case 2: f.write_string("protected"); break; - } - if (tooltip() && *tooltip()) { - f.write_string("tooltip"); - f.write_word(tooltip()); - } - if (image_name() && *image_name()) { - if (scale_image_w_ || scale_image_h_) - f.write_string("scale_image {%d %d}", scale_image_w_, scale_image_h_); - f.write_string("image"); - f.write_word(image_name()); - f.write_string("compress_image %d", compress_image_); - } - if (bind_image_) f.write_string("bind_image 1"); - if (inactive_name() && *inactive_name()) { - if (scale_deimage_w_ || scale_deimage_h_) - f.write_string("scale_deimage {%d %d}", scale_deimage_w_, scale_deimage_h_); - f.write_string("deimage"); - f.write_word(inactive_name()); - f.write_string("compress_deimage %d", compress_deimage_); - } - if (bind_deimage_) f.write_string("bind_deimage 1"); - f.write_string("xywh {%d %d %d %d}", o->x(), o->y(), o->w(), o->h()); - Fl_Widget* tplate = ((Fl_Widget_Type*)factory)->o; - if (is_a(ID_Spinner) && ((Fl_Spinner*)o)->type() != ((Fl_Spinner*)tplate)->type()) { - f.write_string("type"); - f.write_word(item_name(subtypes(), ((Fl_Spinner*)o)->type())); - } else if (subtypes() && (o->type() != tplate->type() || is_a(ID_Window))) { - f.write_string("type"); - f.write_word(item_name(subtypes(), o->type())); - } - if (o->box() != tplate->box()) { - f.write_string("box"); f.write_word(boxname(o->box()));} - if (is_a(ID_Input)) { - Fl_Input_* b = (Fl_Input_*)o; - if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); - } - if (is_a(ID_Value_Input)) { - Fl_Value_Input* b = (Fl_Value_Input*)o; - if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); - } - if (is_a(ID_Text_Display)) { - Fl_Text_Display* b = (Fl_Text_Display*)o; - if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); - } - if (is_a(ID_Button)) { - Fl_Button* b = (Fl_Button*)o; - if (b->down_box()) { - f.write_string("down_box"); f.write_word(boxname(b->down_box()));} - if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); - if (b->value()) f.write_string("value 1"); - } else if (is_a(ID_Input_Choice)) { - Fl_Input_Choice* b = (Fl_Input_Choice*)o; - if (b->down_box()) { - f.write_string("down_box"); f.write_word(boxname(b->down_box()));} - } else if (is_a(ID_Menu_)) { - Fl_Menu_* b = (Fl_Menu_*)o; - if (b->down_box()) { - f.write_string("down_box"); f.write_word(boxname(b->down_box()));} - } - if (o->color()!=tplate->color()) - f.write_string("color %d", o->color()); - if (o->selection_color()!=tplate->selection_color()) - f.write_string("selection_color %d", o->selection_color()); - if (o->labeltype()!=tplate->labeltype()) { - f.write_string("labeltype"); - f.write_word(item_name(labeltypemenu, o->labeltype())); - } - if (o->labelfont()!=tplate->labelfont()) - f.write_string("labelfont %d", o->labelfont()); - if (o->labelsize()!=tplate->labelsize()) - f.write_string("labelsize %d", o->labelsize()); - if (o->labelcolor()!=tplate->labelcolor()) - f.write_string("labelcolor %d", o->labelcolor()); - if (o->align()!=tplate->align()) - f.write_string("align %d", o->align()); - if (o->horizontal_label_margin()!=tplate->horizontal_label_margin()) - f.write_string("h_label_margin %d", o->horizontal_label_margin()); - if (o->vertical_label_margin()!=tplate->vertical_label_margin()) - f.write_string("v_label_margin %d", o->vertical_label_margin()); - if (o->label_image_spacing()!=tplate->label_image_spacing()) - f.write_string("image_spacing %d", o->label_image_spacing()); - if (o->when() != tplate->when()) - f.write_string("when %d", o->when()); - if (is_a(ID_Valuator_)) { - Fl_Valuator* v = (Fl_Valuator*)o; - Fl_Valuator* t = (Fl_Valuator*)(tplate); - if (v->minimum()!=t->minimum()) f.write_string("minimum %g",v->minimum()); - if (v->maximum()!=t->maximum()) f.write_string("maximum %g",v->maximum()); - if (v->step()!=t->step()) f.write_string("step %g",v->step()); - if (v->value()!=0.0) f.write_string("value %g",v->value()); - if (is_a(ID_Slider)) { - double x = ((Fl_Slider*)v)->slider_size(); - double y = ((Fl_Slider*)t)->slider_size(); - if (x != y) f.write_string("slider_size %g", x); - } - } - if (is_a(ID_Spinner)) { - Fl_Spinner* v = (Fl_Spinner*)o; - Fl_Spinner* t = (Fl_Spinner*)(tplate); - if (v->minimum()!=t->minimum()) f.write_string("minimum %g",v->minimum()); - if (v->maximum()!=t->maximum()) f.write_string("maximum %g",v->maximum()); - if (v->step()!=t->step()) f.write_string("step %g",v->step()); - if (v->value()!=1.0) f.write_string("value %g",v->value()); - } - {Fl_Font ff; int fs; Fl_Color fc; if (textstuff(4,ff,fs,fc)) { - Fl_Font ft; int s; Fl_Color c; textstuff(0,ft,s,c); - if (ft != ff) f.write_string("textfont %d", ft); - if (s != fs) f.write_string("textsize %d", s); - if (c != fc) f.write_string("textcolor %d", c); - }} - if (!o->visible() && !override_visible_) f.write_string("hide"); - if (!o->active()) f.write_string("deactivate"); - if (resizable()) f.write_string("resizable"); - if (hotspot()) f.write_string(is_a(ID_Menu_Item) ? "divider" : "hotspot"); - for (int n=0; n < NUM_EXTRA_CODE; n++) if (extra_code(n)) { - f.write_indent(level+1); - f.write_string("code%d",n); - f.write_word(extra_code(n)); - } - if (subclass()) { - f.write_indent(level+1); - f.write_string("class"); - f.write_word(subclass()); - } -} - -void Fl_Widget_Type::read_property(fld::io::Project_Reader &f, const char *c) { - int x,y,w,h; Fl_Font ft; int s; Fl_Color cc; - if (!strcmp(c,"private")) { - public_ = 0; - } else if (!strcmp(c,"protected")) { - public_ = 2; - } else if (!strcmp(c,"xywh")) { - if (sscanf(f.read_word(),"%d %d %d %d",&x,&y,&w,&h) == 4) { - x += pasteoffset; - y += pasteoffset; - // FIXME temporary change! - if (f.read_version>=2.0 && o->parent() && o->parent()!=o->window()) { - x += o->parent()->x(); - y += o->parent()->y(); - } - o->resize(x,y,w,h); - } - } else if (!strcmp(c,"tooltip")) { - tooltip(f.read_word()); - } else if (!strcmp(c,"scale_image")) { - if (sscanf(f.read_word(),"%d %d",&w,&h) == 2) { - scale_image_w_ = w; - scale_image_h_ = h; - } - } else if (!strcmp(c,"image")) { - image_name(f.read_word()); - // starting in 2023, `image` is always followed by `compress_image` - // the code below is for compatibility with older .fl files - const char *ext = fl_filename_ext(image_name_); - if ( strcmp(ext, ".jpg") - && strcmp(ext, ".png") - && strcmp(ext, ".svg") - && strcmp(ext, ".svgz")) - compress_image_ = 0; // if it is neither of those, default to uncompressed - } else if (!strcmp(c,"bind_image")) { - bind_image_ = (int)atol(f.read_word()); - } else if (!strcmp(c,"compress_image")) { - compress_image_ = (int)atol(f.read_word()); - } else if (!strcmp(c,"scale_deimage")) { - if (sscanf(f.read_word(),"%d %d",&w,&h) == 2) { - scale_deimage_w_ = w; - scale_deimage_h_ = h; - } - } else if (!strcmp(c,"deimage")) { - inactive_name(f.read_word()); - // starting in 2023, `deimage` is always followed by `compress_deimage` - // the code below is for compatibility with older .fl files - const char *ext = fl_filename_ext(inactive_name_); - if ( strcmp(ext, ".jpg") - && strcmp(ext, ".png") - && strcmp(ext, ".svg") - && strcmp(ext, ".svgz")) - compress_deimage_ = 0; // if it is neither of those, default to uncompressed - } else if (!strcmp(c,"bind_deimage")) { - bind_deimage_ = (int)atol(f.read_word()); - } else if (!strcmp(c,"compress_deimage")) { - compress_deimage_ = (int)atol(f.read_word()); - } else if (!strcmp(c,"type")) { - if (is_a(ID_Spinner)) - ((Fl_Spinner*)o)->type(item_number(subtypes(), f.read_word())); - else - o->type(item_number(subtypes(), f.read_word())); - } else if (!strcmp(c,"box")) { - const char* value = f.read_word(); - if ((x = boxnumber(value))) { - if (x == ZERO_ENTRY) x = 0; - o->box((Fl_Boxtype)x); - } else if (sscanf(value,"%d",&x) == 1) o->box((Fl_Boxtype)x); - } else if (is_a(ID_Button) && !strcmp(c,"down_box")) { - const char* value = f.read_word(); - if ((x = boxnumber(value))) { - if (x == ZERO_ENTRY) x = 0; - ((Fl_Button*)o)->down_box((Fl_Boxtype)x); - } - } else if (is_a(ID_Input_Choice) && !strcmp(c,"down_box")) { - const char* value = f.read_word(); - if ((x = boxnumber(value))) { - if (x == ZERO_ENTRY) x = 0; - ((Fl_Input_Choice*)o)->down_box((Fl_Boxtype)x); - } - } else if (is_a(ID_Menu_) && !strcmp(c,"down_box")) { - const char* value = f.read_word(); - if ((x = boxnumber(value))) { - if (x == ZERO_ENTRY) x = 0; - ((Fl_Menu_*)o)->down_box((Fl_Boxtype)x); - } - } else if (is_button() && !strcmp(c,"value")) { - const char* value = f.read_word(); - ((Fl_Button*)o)->value(atoi(value)); - } else if (!strcmp(c,"color")) { - const char *cw = f.read_word(); - if (cw[0]=='0' && cw[1]=='x') { - sscanf(cw,"0x%x",&x); - o->color(x); - } else { - int n = sscanf(cw,"%d %d",&x,&y); - if (n == 2) { // back compatibility... - if (x != 47) o->color(x); - o->selection_color(y); - } else { - o->color(x); - } - } - } else if (!strcmp(c,"selection_color")) { - if (sscanf(f.read_word(),"%d",&x)) o->selection_color(x); - } else if (!strcmp(c,"labeltype")) { - c = f.read_word(); - if (!strcmp(c,"image")) { - Fluid_Image *i = Fluid_Image::find(label()); - if (!i) f.read_error("Image file '%s' not found", label()); - else setimage(i); - image_name(label()); - label(""); - } else { - o->labeltype((Fl_Labeltype)item_number(labeltypemenu,c)); - } - } else if (!strcmp(c,"labelfont")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->labelfont(x); - } else if (!strcmp(c,"labelsize")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->labelsize(x); - } else if (!strcmp(c,"labelcolor")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->labelcolor(x); - } else if (!strcmp(c,"align")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->align(x); - } else if (!strcmp(c,"h_label_margin")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->horizontal_label_margin(x); - } else if (!strcmp(c,"v_label_margin")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->vertical_label_margin(x); - } else if (!strcmp(c,"image_spacing")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->label_image_spacing(x); - } else if (!strcmp(c,"when")) { - if (sscanf(f.read_word(),"%d",&x) == 1) o->when(x); - } else if (!strcmp(c,"minimum")) { - if (is_a(ID_Valuator_)) ((Fl_Valuator*)o)->minimum(strtod(f.read_word(),0)); - if (is_a(ID_Spinner)) ((Fl_Spinner*)o)->minimum(strtod(f.read_word(),0)); - } else if (!strcmp(c,"maximum")) { - if (is_a(ID_Valuator_)) ((Fl_Valuator*)o)->maximum(strtod(f.read_word(),0)); - if (is_a(ID_Spinner)) ((Fl_Spinner*)o)->maximum(strtod(f.read_word(),0)); - } else if (!strcmp(c,"step")) { - if (is_a(ID_Valuator_)) ((Fl_Valuator*)o)->step(strtod(f.read_word(),0)); - if (is_a(ID_Spinner)) ((Fl_Spinner*)o)->step(strtod(f.read_word(),0)); - } else if (!strcmp(c,"value")) { - if (is_a(ID_Valuator_)) ((Fl_Valuator*)o)->value(strtod(f.read_word(),0)); - if (is_a(ID_Spinner)) ((Fl_Spinner*)o)->value(strtod(f.read_word(),0)); - } else if ( (!strcmp(c,"slider_size") || !strcmp(c,"size")) && is_a(ID_Slider)) { - ((Fl_Slider*)o)->slider_size(strtod(f.read_word(),0)); - } else if (!strcmp(c,"textfont")) { - if (sscanf(f.read_word(),"%d",&x) == 1) {ft=(Fl_Font)x; textstuff(1,ft,s,cc);} - } else if (!strcmp(c,"textsize")) { - if (sscanf(f.read_word(),"%d",&x) == 1) {s=x; textstuff(2,ft,s,cc);} - } else if (!strcmp(c,"textcolor")) { - if (sscanf(f.read_word(),"%d",&x) == 1) {cc=(Fl_Color)x;textstuff(3,ft,s,cc);} - } else if (!strcmp(c,"hide")) { - o->hide(); - } else if (!strcmp(c,"deactivate")) { - o->deactivate(); - } else if (!strcmp(c,"resizable")) { - resizable(1); - } else if (!strcmp(c,"hotspot") || !strcmp(c, "divider")) { - hotspot(1); - } else if (!strcmp(c,"class")) { - subclass(f.read_word()); - } else if (!strcmp(c,"shortcut")) { - int shortcut = (int)strtol(f.read_word(),0,0); - if (is_button()) ((Fl_Button*)o)->shortcut(shortcut); - else if (is_a(ID_Input)) ((Fl_Input_*)o)->shortcut(shortcut); - else if (is_a(ID_Value_Input)) ((Fl_Value_Input*)o)->shortcut(shortcut); - else if (is_a(ID_Text_Display)) ((Fl_Text_Display*)o)->shortcut(shortcut); - } else { - if (!strncmp(c,"code",4)) { - int n = atoi(c+4); - if (n >= 0 && n <= NUM_EXTRA_CODE) { - extra_code(n,f.read_word()); - return; - } - } else if (!strcmp(c,"extra_code")) { - extra_code(0,f.read_word()); - return; - } - Fl_Type::read_property(f, c); - } -} - -Fl_Menu_Item boxmenu1[] = { - // these extra ones are for looking up fdesign saved strings: - {"NO_FRAME", 0,0,(void *)FL_NO_BOX}, - {"ROUNDED3D_UPBOX", 0,0,(void *)_FL_ROUND_UP_BOX}, - {"ROUNDED3D_DOWNBOX", 0,0,(void *)_FL_ROUND_DOWN_BOX}, - {"OVAL3D_UPBOX", 0,0,(void *)_FL_ROUND_UP_BOX}, - {"OVAL3D_DOWNBOX", 0,0,(void *)_FL_ROUND_DOWN_BOX}, - {"0", 0,0,(void *)ZERO_ENTRY}, - {"1", 0,0,(void *)FL_UP_BOX}, - {"2", 0,0,(void *)FL_DOWN_BOX}, - {"3", 0,0,(void *)FL_FLAT_BOX}, - {"4", 0,0,(void *)FL_BORDER_BOX}, - {"5", 0,0,(void *)FL_SHADOW_BOX}, - {"6", 0,0,(void *)FL_FRAME_BOX}, - {"7", 0,0,(void *)FL_ROUNDED_BOX}, - {"8", 0,0,(void *)FL_RFLAT_BOX}, - {"9", 0,0,(void *)FL_RSHADOW_BOX}, - {"10", 0,0,(void *)FL_UP_FRAME}, - {"11", 0,0,(void *)FL_DOWN_FRAME}, -{0}}; - -int lookup_symbol(const char *, int &, int numberok = 0); - -int Fl_Widget_Type::read_fdesign(const char* propname, const char* value) { - int v; - if (!strcmp(propname,"box")) { - float x,y,w,h; - if (sscanf(value,"%f %f %f %f",&x,&y,&w,&h) == 4) { - if (fld::io::fdesign_flip) { - Fl_Type *p; - for (p = parent; p && !p->is_a(ID_Window); p = p->parent) {/*empty*/} - if (p && p->is_widget()) y = ((Fl_Widget_Type*)p)->o->h()-(y+h); - } - x += pasteoffset; - y += pasteoffset; - o->resize(int(x),int(y),int(w),int(h)); - } - } else if (!strcmp(propname,"label")) { - label(value); - } else if (!strcmp(propname,"name")) { - this->name(value); - } else if (!strcmp(propname,"callback")) { - callback(value); user_data_type("long"); - } else if (!strcmp(propname,"argument")) { - user_data(value); - } else if (!strcmp(propname,"shortcut")) { - if (value[0]) { - char buf[128]; sprintf(buf,"o->shortcut(\"%s\");",value); - extra_code(0,buf); - } - } else if (!strcmp(propname,"style")) { - if (!strncmp(value,"FL_NORMAL",9)) return 1; - if (!lookup_symbol(value,v,1)) return 0; - o->labelfont(v); o->labeltype((Fl_Labeltype)(v>>8)); - } else if (!strcmp(propname,"size")) { - if (!lookup_symbol(value,v,1)) return 0; - o->labelsize(v); - } else if (!strcmp(propname,"type")) { - if (!strncmp(value,"NORMAL",6)) return 1; - if (lookup_symbol(value,v,1)) {o->type(v); return 1;} - if (!strcmp(value+strlen(value)-5,"FRAME")) goto TRY_BOXTYPE; - if (!strcmp(value+strlen(value)-3,"BOX")) goto TRY_BOXTYPE; - return 0; - } else if (!strcmp(propname,"lcol")) { - if (!lookup_symbol(value,v,1)) return 0; - o->labelcolor(v); - } else if (!strcmp(propname,"return")) { - if (!lookup_symbol(value,v,0)) return 0; - o->when(v|FL_WHEN_RELEASE); - } else if (!strcmp(propname,"alignment")) { - if (!lookup_symbol(value,v)) { - // convert old numeric values: - int v1 = atoi(value); if (v1 <= 0 && strcmp(value,"0")) return 0; - v = 0; - if (v1 >= 5) {v = FL_ALIGN_INSIDE; v1 -= 5;} - switch (v1) { - case 0: v += FL_ALIGN_TOP; break; - case 1: v += FL_ALIGN_BOTTOM; break; - case 2: v += FL_ALIGN_LEFT; break; - case 3: v += FL_ALIGN_RIGHT; break; - case 4: v += FL_ALIGN_CENTER; break; - default: return 0; - } - } - o->align(v); - } else if (!strcmp(propname,"resizebox")) { - resizable(1); - } else if (!strcmp(propname,"colors")) { - char* p = (char*)value; - while (*p != ' ') {if (!*p) return 0; p++;} - *p = 0; - int v1; - if (!lookup_symbol(value,v,1) || !lookup_symbol(p+1,v1,1)) { - *p=' '; return 0;} - o->color(v,v1); - } else if (!strcmp(propname,"resize")) { - return !strcmp(value,"FL_RESIZE_ALL"); - } else if (!strcmp(propname,"gravity")) { - return !strcmp(value,"FL_NoGravity FL_NoGravity"); - } else if (!strcmp(propname,"boxtype")) { - TRY_BOXTYPE: - int x = boxnumber(value); - if (!x) {x = item_number(boxmenu1, value); if (x < 0) return 0;} - if (x == ZERO_ENTRY) { - x = 0; - if (o->box() != ((Fl_Widget_Type*)factory)->o->box()) return 1; // kludge for frame - } - o->box((Fl_Boxtype)x); - } else { - return 0; - } - return 1; -} - -void leave_live_mode_cb(Fl_Widget*, void*) { - live_mode_cb(0, 0); -} - -Fl_Widget *Fl_Widget_Type::enter_live_mode(int) { - live_widget = widget(o->x(), o->y(), o->w(), o->h()); - if (live_widget) - copy_properties(); - return live_widget; -} - -Fl_Widget* Fl_Widget_Type::propagate_live_mode(Fl_Group* grp) { - live_widget = grp; - copy_properties(); - Fl_Type *n; - for (n = next; n && n->level > level; n = n->next) { - if (n->level == level+1) { - Fl_Widget* proxy_child = n->enter_live_mode(); - if (proxy_child && n->is_widget() && ((Fl_Widget_Type*)n)->resizable()) { - grp->resizable(proxy_child); - } - } - } - grp->end(); - live_widget = grp; - copy_properties_for_children(); - return live_widget; -} - - -void Fl_Widget_Type::leave_live_mode() { -} - -/** - copy all properties from the edit widget to the live widget - */ -void Fl_Widget_Type::copy_properties() { - if (!live_widget) - return; - - Fl_Font ff = 0; - int fs = 0; - Fl_Color fc = 0; - textstuff(0, ff, fs, fc); - - // copy all attributes common to all widget types - Fl_Widget *w = live_widget; - w->label(o->label()); - w->tooltip(tooltip()); - w->type(o->type()); - w->box(o->box()); - w->color(o->color()); - w->selection_color(o->selection_color()); - w->labeltype(o->labeltype()); - w->labelfont(o->labelfont()); - w->labelsize(o->labelsize()); - w->labelcolor(o->labelcolor()); - w->align(o->align()); - w->when(o->when()); - - // copy all attributes specific to widgets derived from Fl_Button - if (is_button()) { - Fl_Button* d = (Fl_Button*)live_widget, *s = (Fl_Button*)o; - d->down_box(s->down_box()); - d->shortcut(s->shortcut()); - d->value(s->value()); - } - - // copy all attributes specific to widgets derived from Fl_Input_ - if (is_a(ID_Input)) { - Fl_Input_* d = (Fl_Input_*)live_widget, *s = (Fl_Input_*)o; - d->shortcut(s->shortcut()); - d->textfont(ff); - d->textsize(fs); - d->textcolor(fc); - } - - // copy all attributes specific to widgets derived from Fl_Value_Input - if (is_a(ID_Value_Input)) { - Fl_Value_Input* d = (Fl_Value_Input*)live_widget, *s = (Fl_Value_Input*)o; - d->shortcut(s->shortcut()); - d->textfont(ff); - d->textsize(fs); - d->textcolor(fc); - } - - // copy all attributes specific to widgets derived from Fl_Text_Display - if (is_a(ID_Text_Display)) { - Fl_Text_Display* d = (Fl_Text_Display*)live_widget, *s = (Fl_Text_Display*)o; - d->shortcut(s->shortcut()); - d->textfont(ff); - d->textsize(fs); - d->textcolor(fc); - } - - // copy all attributes specific to Fl_Valuator and derived classes - if (is_a(ID_Valuator_)) { - Fl_Valuator* d = (Fl_Valuator*)live_widget, *s = (Fl_Valuator*)o; - d->minimum(s->minimum()); - d->maximum(s->maximum()); - d->step(s->step()); - d->value(s->value()); - if (is_a(ID_Slider)) { - Fl_Slider *d = (Fl_Slider*)live_widget, *s = (Fl_Slider*)o; - d->slider_size(s->slider_size()); - } - } - - // copy all attributes specific to Fl_Spinner and derived classes - if (is_a(ID_Spinner)) { - Fl_Spinner* d = (Fl_Spinner*)live_widget, *s = (Fl_Spinner*)o; - d->minimum(s->minimum()); - d->maximum(s->maximum()); - d->step(s->step()); - d->value(s->value()); - } - - if (!o->visible()) - w->hide(); - if (!o->active()) - w->deactivate(); -} - diff --git a/fluid/nodes/Fl_Widget_Type.h b/fluid/nodes/Fl_Widget_Type.h deleted file mode 100644 index 6a26921cc..000000000 --- a/fluid/nodes/Fl_Widget_Type.h +++ /dev/null @@ -1,132 +0,0 @@ -// -// Widget type header file for the Fast Light Tool Kit (FLTK). -// -// Type for creating all subclasses of Fl_Widget -// This should have the widget pointer in it, but it is still in the -// Fl_Type base class. -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef _FLUID_FL_WIDGET_TYPE_H -#define _FLUID_FL_WIDGET_TYPE_H - -#include "nodes/Fl_Type.h" - -#define NUM_EXTRA_CODE 4 - -class Fl_Widget_Type; -class Fluid_Image; - -extern void* const LOAD; -extern Fl_Widget_Type *current_widget; // one of the selected ones - -extern const char* subclassname(Fl_Type* l); -extern int is_name(const char *c); -void selection_changed(Fl_Type* new_current); -Fl_Type *sort(Fl_Type *parent); -void comment_cb(class Fl_Text_Editor* i, void *v); - -class Fl_Widget_Type : public Fl_Type -{ - typedef Fl_Type super; - - virtual Fl_Widget *widget(int,int,int,int) = 0; - virtual Fl_Widget_Type *_make() = 0; // virtual constructor - void setlabel(const char *) FL_OVERRIDE; - - const char *extra_code_[NUM_EXTRA_CODE]; - const char *subclass_; - const char *tooltip_; - const char *image_name_; - const char *inactive_name_; - uchar hotspot_; - -protected: - - /// This variable is set for visible windows in batch mode. - /// We can't open a window in batch mode, even if we want the "visible" flags - /// set, so we need a second place to store this information while also - /// disabling the output of the "hide" property by the Widget Type. - uchar override_visible_; - - void write_static(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_widget_code(fld::io::Code_Writer& f); - void write_extra_code(fld::io::Code_Writer& f); - void write_block_close(fld::io::Code_Writer& f); - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_color(fld::io::Code_Writer& f, const char*, Fl_Color); - Fl_Widget *live_widget; - -public: - Fl_Widget *o; - int public_; - int bind_image_; - int compress_image_; - int bind_deimage_; - int compress_deimage_; - int scale_image_w_, scale_image_h_; - int scale_deimage_w_, scale_deimage_h_; - - Fluid_Image *image; - void setimage(Fluid_Image *); - Fluid_Image *inactive; - void setinactive(Fluid_Image *); - - Fl_Widget_Type(); - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - void open() FL_OVERRIDE; - - const char *extra_code(int n) const {return extra_code_[n];} - void extra_code(int n,const char *); - const char *subclass() const {return subclass_;} - void subclass(const char *); - const char *tooltip() const {return tooltip_;} - void tooltip(const char *); - const char *image_name() const {return image_name_;} - void image_name(const char *); - const char *inactive_name() const {return inactive_name_;} - void inactive_name(const char *); - uchar hotspot() const {return hotspot_;} - void hotspot(uchar v) {hotspot_ = v;} - uchar resizable() const; - void resizable(uchar v); - - virtual int textstuff(int what, Fl_Font &, int &, Fl_Color &); - virtual Fl_Menu_Item *subtypes(); - - ID id() const FL_OVERRIDE { return ID_Widget_; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Widget_) ? true : super::is_a(inID); } - int is_widget() const FL_OVERRIDE; - int is_true_widget() const FL_OVERRIDE { return 1; } - int is_public() const FL_OVERRIDE; - - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - int read_fdesign(const char*, const char*) FL_OVERRIDE; - - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - Fl_Widget *propagate_live_mode(Fl_Group* grp); - void leave_live_mode() FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; - - virtual void ideal_size(int &w, int &h); - - ~Fl_Widget_Type(); - void redraw(); -}; - -extern Fl_Window *the_panel; - -#endif // _FLUID_FL_WIDGET_TYPE_H diff --git a/fluid/nodes/Fl_Window_Type.cxx b/fluid/nodes/Fl_Window_Type.cxx deleted file mode 100644 index 3f68f7980..000000000 --- a/fluid/nodes/Fl_Window_Type.cxx +++ /dev/null @@ -1,1562 +0,0 @@ -// -// Window type code file for the Fast Light Tool Kit (FLTK). -// -// The widget describing an Fl_Window. This is also all the code -// for interacting with the overlay, which allows the user to -// select, move, and resize the children widgets. -// -// Copyright 1998-2025 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "nodes/Fl_Window_Type.h" - -#include "app/Fd_Snap_Action.h" -#include "app/fluid.h" -#include "app/project.h" -#include "app/undo.h" -#include "io/Project_Reader.h" -#include "io/Project_Writer.h" -#include "io/Code_Writer.h" -#include "nodes/factory.h" -#include "nodes/Fl_Group_Type.h" -#include "nodes/Fl_Grid_Type.h" -#include "panels/settings_panel.h" -#include "panels/widget_panel.h" -#include "widgets/Node_Browser.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../src/flstring.h" - -#include -#include -#include - -extern Fl_Window *the_panel; -extern void draw_width(int x, int y, int r, Fl_Align a); -extern void draw_height(int x, int y, int b, Fl_Align a); - -extern Fl_Preferences fluid_prefs; - -// Update the XYWH values in the widget panel... -static void update_xywh() { - if (current_widget && current_widget->is_widget()) { - Fl_Widget *o = ((Fl_Widget_Type *)current_widget)->o; - widget_x_input->value(o->x()); - widget_y_input->value(o->y()); - widget_w_input->value(o->w()); - widget_h_input->value(o->h()); - if (Fl_Flex_Type::parent_is_flex(current_widget)) { - widget_flex_size->value(Fl_Flex_Type::size(current_widget)); - widget_flex_fixed->value(Fl_Flex_Type::is_fixed(current_widget)); - } - } -} - -void i18n_type_cb(Fl_Choice *c, void *v) { - if (v == LOAD) { - c->value(g_project.i18n_type); - } else { - undo_checkpoint(); - g_project.i18n_type = static_cast(c->value()); - set_modflag(1); - } - switch (g_project.i18n_type) { - case FD_I18N_NONE : /* None */ - i18n_gnu_group->hide(); - i18n_posix_group->hide(); - break; - case FD_I18N_GNU : /* GNU gettext */ - i18n_gnu_group->show(); - i18n_posix_group->hide(); - break; - case FD_I18N_POSIX : /* POSIX cat */ - i18n_gnu_group->hide(); - i18n_posix_group->show(); - break; - } - // make sure that the outside labels are redrawn too. - w_settings_i18n_tab->redraw(); -} - -void show_grid_cb(Fl_Widget *, void *) { - settings_window->show(); - w_settings_tabs->value(w_settings_layout_tab); -} - -void show_settings_cb(Fl_Widget *, void *) { - settings_window->hotspot(settings_window); - settings_window->show(); -} - -//////////////////////////////////////////////////////////////// - -Fl_Menu_Item window_type_menu[] = { - {"Single",0,0,(void*)FL_WINDOW}, - {"Double",0,0,(void*)(FL_DOUBLE_WINDOW)}, - {0}}; - -static int overlays_invisible; - -// The following Fl_Widget is used to simulate the windows. It has -// an overlay for the fluid ui, and special-cases the FL_NO_BOX. - -class Overlay_Window : public Fl_Overlay_Window { - void draw() FL_OVERRIDE; - void draw_overlay() FL_OVERRIDE; - static void close_cb(Overlay_Window *self, void*); -public: - Fl_Window_Type *window; - int handle(int) FL_OVERRIDE; - Overlay_Window(int W,int H) : Fl_Overlay_Window(W,H) { - Fl_Group::current(0); - callback((Fl_Callback*)close_cb); - } - void resize(int,int,int,int) FL_OVERRIDE; - uchar *read_image(int &ww, int &hh); -}; - -/** - \brief User closes the window, so we mark the .fl file as changed. - Mark the .fl file a changed, but don;t mark the source files as changed. - \param self pointer to this window - */ -void Overlay_Window::close_cb(Overlay_Window *self, void*) { - if (self->visible()) - set_modflag(1, -2); - self->hide(); -} - -// Use this when drawing flat boxes while editing, so users can see the outline, -// even if the group and its parent have the same color. -static void fd_flat_box_ghosted(int x, int y, int w, int h, Fl_Color c) { - fl_rectf(x, y, w, h, Fl::box_color(c)); - fl_rect(x, y, w, h, Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, c, .1f))); -} - -void Overlay_Window::draw() { - const int CHECKSIZE = 8; - // see if box is clear or a frame or rounded: - if ((damage()&FL_DAMAGE_ALL) && - (!box() || (box()>=4&&!(box()&2)) || box()>=_FL_ROUNDED_BOX)) { - // if so, draw checkerboard so user can see what areas are clear: - for (int Y = 0; Y < h(); Y += CHECKSIZE) - for (int X = 0; X < w(); X += CHECKSIZE) { - fl_color(((Y/(2*CHECKSIZE))&1) != ((X/(2*CHECKSIZE))&1) ? - FL_WHITE : FL_BLACK); - fl_rectf(X,Y,CHECKSIZE,CHECKSIZE); - } - } - if (show_ghosted_outline) { - Fl_Box_Draw_F *old_flat_box = Fl::get_boxtype(FL_FLAT_BOX); - Fl::set_boxtype(FL_FLAT_BOX, fd_flat_box_ghosted, 0, 0, 0, 0); - Fl_Overlay_Window::draw(); - Fl::set_boxtype(FL_FLAT_BOX, old_flat_box, 0, 0, 0, 0); - } else { - Fl_Overlay_Window::draw(); - } -} - -extern Fl_Window *main_window; - -// Read an image of the overlay window -uchar *Overlay_Window::read_image(int &ww, int &hh) { - // Create an off-screen buffer for the window... - //main_window->make_current(); - make_current(); - - ww = w(); - hh = h(); - - Fl_Offscreen offscreen = fl_create_offscreen(ww, hh); - uchar *pixels; - - // Redraw the window into the offscreen buffer... - fl_begin_offscreen(offscreen); - - if (!shown()) image(Fl::scheme_bg_); - - redraw(); - draw(); - - // Read the screen image... - pixels = fl_read_image(0, 0, 0, ww, hh); - - fl_end_offscreen(); - - // Cleanup and return... - fl_delete_offscreen(offscreen); - main_window->make_current(); - return pixels; -} - -void Overlay_Window::draw_overlay() { - window->draw_overlay(); -} - -int Overlay_Window::handle(int e) { - int ret = window->handle(e); - if (ret==0) { - switch (e) { - case FL_SHOW: - case FL_HIDE: - ret = Fl_Overlay_Window::handle(e); - } - } - return ret; -} - -/** - Make and add a new Window node. - \param[in] strategy is Strategy::AS_LAST_CHILD or Strategy::AFTER_CURRENT - \return new node - */ -Fl_Type *Fl_Window_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) p = p->parent; - while (p && (!p->is_code_block() || p->is_a(ID_Widget_Class))) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - if (!p) { - fl_message("Please select a function"); - return 0; - } - Fl_Window_Type *myo = new Fl_Window_Type(); - if (!this->o) {// template widget - this->o = new Fl_Window(100,100); - Fl_Group::current(0); - } - myo->factory = this; - myo->drag = 0; - myo->numselected = 0; - Overlay_Window *w = new Overlay_Window(100, 100); - w->size_range(10, 10); - w->window = myo; - myo->o = w; - myo->add(anchor, strategy); - myo->modal = 0; - myo->non_modal = 0; - return myo; -} - -void Fl_Window_Type::add_child(Fl_Type* cc, Fl_Type* before) { - if (!cc->is_widget()) return; - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - Fl_Widget* b = before ? ((Fl_Widget_Type*)before)->o : 0; - ((Fl_Window*)o)->insert(*(c->o), b); - o->redraw(); -} - -void Fl_Window_Type::remove_child(Fl_Type* cc) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - ((Fl_Window*)o)->remove(c->o); - o->redraw(); -} - -void Fl_Window_Type::move_child(Fl_Type* cc, Fl_Type* before) { - Fl_Widget_Type* c = (Fl_Widget_Type*)cc; - ((Fl_Window*)o)->remove(c->o); - Fl_Widget* b = before ? ((Fl_Widget_Type*)before)->o : 0; - ((Fl_Window*)o)->insert(*(c->o), b); - o->redraw(); -} - -//////////////////////////////////////////////////////////////// - -/** - \brief Show the Window Type editor window without setting the modified flag. - \see Fl_Window_Type::open() - */ -void Fl_Window_Type::open_() { - Overlay_Window *w = (Overlay_Window *)o; - if (w->shown()) { - w->show(); - Fl_Widget_Type::open(); - } else { - Fl_Widget *p = w->resizable(); - if (!p) w->resizable(w); - w->show(); - w->resizable(p); - } - w->image(Fl::scheme_bg_); -} - -/** - \brief Show the Window Type editor window and set the modified flag if needed. - Double-click on window widget shows the window, or if already shown, it shows - the control panel. - \see Fl_Window_Type::open_() - */ -void Fl_Window_Type::open() { - Overlay_Window *w = (Overlay_Window *)o; - if (!w->visible()) { - set_modflag(1, -2); - } - open_(); -} - -// Read an image of the window -uchar *Fl_Window_Type::read_image(int &ww, int &hh) { - Overlay_Window *w = (Overlay_Window *)o; - - int hidden = !w->shown(); - w->show(); // make it the front window - - // Read the screen image... - uchar *idata = w->read_image(ww, hh); - if (hidden) - w->hide(); - return idata; -} - -void Fl_Window_Type::ideal_size(int &w, int &h) { - w = 480; h = 320; - if (main_window) { - int sx, sy, sw, sh; - Fl_Window *win = main_window; - int screen = Fl::screen_num(win->x(), win->y()); - Fl::screen_work_area(sx, sy, sw, sh, screen); - w = fd_min(w, sw*3/4); h = fd_min(h, sh*3/4); - } - Fd_Snap_Action::better_size(w, h); -} - - -// control panel items: - -void modal_cb(Fl_Light_Button* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) {i->hide(); return;} - i->show(); - i->value(((Fl_Window_Type *)current_widget)->modal); - } else { - undo_checkpoint(); - ((Fl_Window_Type *)current_widget)->modal = i->value(); - set_modflag(1); - } -} - -void non_modal_cb(Fl_Light_Button* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) {i->hide(); return;} - i->show(); - i->value(((Fl_Window_Type *)current_widget)->non_modal); - } else { - undo_checkpoint(); - ((Fl_Window_Type *)current_widget)->non_modal = i->value(); - set_modflag(1); - } -} - -void border_cb(Fl_Light_Button* i, void* v) { - if (v == LOAD) { - if (!current_widget->is_a(ID_Window)) {i->hide(); return;} - i->show(); - i->value(((Fl_Window*)(current_widget->o))->border()); - } else { - undo_checkpoint(); - ((Fl_Window*)(current_widget->o))->border(i->value()); - set_modflag(1); - } -} - -void xclass_cb(Fl_Input* i, void* v) { - if (v == LOAD) { - if (current_widget->is_a(ID_Window)) { - i->show(); - i->parent()->show(); - i->value(((Fl_Window_Type *)current_widget)->xclass); - } else { - i->hide(); - i->parent()->hide(); // hides the "X Class:" label as well - } - } else { - int mod = 0; - undo_checkpoint(); - for (Fl_Type *o = Fl_Type::first; o; o = o->next) { - if (o->selected && o->is_a(ID_Window)) { - mod = 1; - Fl_Window_Type *wt = (Fl_Window_Type *)o; - storestring(i->value(), wt->xclass); - ((Fl_Window*)(wt->o))->xclass(wt->xclass); - } - } - if (mod) set_modflag(1); - } -} - -//////////////////////////////////////////////////////////////// - -void Fl_Window_Type::setlabel(const char *n) { - if (o) ((Fl_Window *)o)->label(n); -} - -// make() is called on this widget when user picks window off New menu: -Fl_Window_Type Fl_Window_type; - -// Resize from window manager... -void Overlay_Window::resize(int X,int Y,int W,int H) { - // Make sure we don't create undo checkpoints if the window does not actually change. - // Some WMs seem to send spurious resize events. - if (X!=x() || Y!=y() || W!=w() || H!=h()) { - // Set a checkpoint on the first resize event, ignore further resizes until - // a different type of checkpoint is triggered. - if (undo_checkpoint_once(kUndoWindowResize)) - set_modflag(1); - } - - Fl_Widget* t = resizable(); - if (Fl_Type::allow_layout == 0) { - resizable(0); - } - - // do not set the mod flag if the window was not resized. In FLUID, all - // windows are opened without a given x/y position, so modifying x/y - // should not mark the project as dirty - if (W!=w() || H!=h()) - set_modflag(1); - - Fl_Overlay_Window::resize(X,Y,W,H); - resizable(t); - update_xywh(); -} - -// calculate actual move by moving mouse position (mx,my) to -// nearest multiple of gridsize, and snap to original position -void Fl_Window_Type::newdx() { - int mydx, mydy; - mydx = mx-x1; - mydy = my-y1; - - if (!(drag & (FD_DRAG | FD_BOX | FD_LEFT | FD_RIGHT))) { - mydx = 0; - dx = 0; - } - - if (!(drag & (FD_DRAG | FD_BOX | FD_TOP | FD_BOTTOM))) { - mydy = 0; - dy = 0; - } - - if (show_guides && (drag & (FD_DRAG|FD_TOP|FD_LEFT|FD_BOTTOM|FD_RIGHT))) { - Fl_Type *selection = 0L; // special power for the first selected widget - for (Fl_Type *q=next; q && q->level>level; q = q->next) { - if (q->selected && q->is_true_widget()) { - selection = q; - break; - } - } - Fd_Snap_Data data = { mydx, mydy, bx, by, br, bt, drag, 4, 4, mydx, mydy, (Fl_Widget_Type*)selection, this }; - Fd_Snap_Action::check_all(data); - if (data.x_dist < 4) mydx = data.dx_out; - if (data.y_dist < 4) mydy = data.dy_out; - } - - if (dx != mydx || dy != mydy) { - dx = mydx; dy = mydy; - ((Overlay_Window *)o)->redraw_overlay(); - } -} - -// Move a widget according to dx and dy calculated above -void Fl_Window_Type::newposition(Fl_Widget_Type *myo,int &X,int &Y,int &R,int &T) { - X = myo->o->x(); - Y = myo->o->y(); - R = X+myo->o->w(); - T = Y+myo->o->h(); - if (!drag) return; - if (drag&FD_DRAG) { - X += dx; - Y += dy; - R += dx; - T += dy; - } else { - if (drag&FD_LEFT) { - if (X==bx) { - X += dx; - } else { - if (Xbr+dx) R = br+dx; - } - } - if (drag&FD_BOTTOM) { - if (T==bt) { - T += dy; - } else { - if (T>bt+dx) T = bt+dx; - } - } - } - if (R h) { - for (; yp < h; yp+=size) - fl_line(x, y+yp, x+yp, y); - for (; yp < w; yp+=size) - fl_line(x+yp-h, y+h, x+yp, y); - for (; yp < w+h; yp+=size) - fl_line(x+yp-h, y+h, x+w, y+yp-w); - } else { - for (; yp < w; yp+=size) - fl_line(x, y+yp, x+yp, y); - for (; yp < h; yp+=size) - fl_line(x, y+yp, x+w, y+yp-w); - for (; yp < h+w; yp+=size) - fl_line(x+yp-h, y+h, x+w, y+yp-w); - } -} - -/** - \brief Draw a hatch pattern over all children that overlap the bounds of this box. - \param[in] group check all children of this group - \param[in] x, y, w, h bounding box of this group - */ -void Fl_Window_Type::draw_out_of_bounds(Fl_Widget_Type *group, int x, int y, int w, int h) { - for (Fl_Type *p = group->next; p && p->level>group->level; p = p->next) { - if (p->level == group->level+1 && p->is_true_widget()) { - Fl_Widget *o = ((Fl_Widget_Type*)p)->o; - if (o->x() < x) fd_hatch(o->x(), o->y(), x-o->x(), o->h()); - if (o->y() < y) fd_hatch(o->x(), o->y(), o->w(), y-o->y()); - if (o->x()+o->w() > x+w) fd_hatch(x+w, o->y(), (o->x()+o->w())-(x+w), o->h()); - if (o->y()+o->h() > y+h) fd_hatch(o->x(), y+h, o->w(), (o->y()+o->h())-(y+h)); - } - } -} - -/** - \brief Draw a hatch pattern for all groups that have out of bounds children. - */ -void Fl_Window_Type::draw_out_of_bounds() { - // get every group in the hierarchy, then draw any overlap of a direct child with that group - fl_color(FL_DARK_RED); - draw_out_of_bounds(this, 0, 0, o->w(), o->h()); - for (Fl_Type *q=next; q && q->level>level; q = q->next) { - // don't do this for Fl_Scroll (which we currently can't handle in FLUID anyway) - if (q->is_a(ID_Group) && !q->is_a(ID_Scroll)) { - Fl_Widget_Type *w = (Fl_Widget_Type*)q; - draw_out_of_bounds(w, w->o->x(), w->o->y(), w->o->w(), w->o->h()); - } - } - fl_color(FL_RED); -} - -/** - \brief Compare all children in the same level and hatch overlapping areas. - */ -void Fl_Window_Type::draw_overlaps() { - fl_color(FL_DARK_YELLOW); - // loop through all widgets in this window - for (Fl_Type *q=next; q && q->level>level; q = q->next) { - // is it a valid widget - if (q->is_true_widget()) { - Fl_Widget_Type *w = (Fl_Widget_Type*)q; - // is the widget visible - if (w->o->visible()) { - int x = w->o->x(), y = w->o->y(); - int r = x + w->o->w(), b = y + w->o->h(); - for (Fl_Type *p=q->next; p && p->level>=q->level; p = p->next) { - if (p->level==q->level && p->is_true_widget()) { - Fl_Widget_Type *wp = (Fl_Widget_Type*)p; - if (wp->o->visible()) { - int px = fd_max(x, wp->o->x()); - int py = fd_max(y, wp->o->y()); - int pr = fd_min(r, wp->o->x() + wp->o->w()); - int pb = fd_min(b, wp->o->y() + wp->o->h()); - if (pr > px && pb > py) - fd_hatch(px, py, pr-px, pb-py); - } - } - } - } else { - int l = q->level; - for (; q && q->next && q->next->level>l; q = q->next) { } - } - } - } - fl_color(FL_RED); -} - -void Fl_Window_Type::draw_overlay() { - if (recalc) { - bx = o->w(); by = o->h(); br = 0; bt = 0; - numselected = 0; - for (Fl_Type *q=next; q && q->level>level; q=q->next) - if (q->selected && q->is_true_widget()) { - numselected++; - Fl_Widget_Type* myo = (Fl_Widget_Type*)q; - if (myo->o->x() < bx) bx = myo->o->x(); - if (myo->o->y() < by) by = myo->o->y(); - if (myo->o->x()+myo->o->w() > br) br = myo->o->x()+myo->o->w(); - if (myo->o->y()+myo->o->h() > bt) bt = myo->o->y()+myo->o->h(); - } - recalc = 0; - sx = bx; sy = by; sr = br; st = bt; - } - fl_color(FL_RED); - if (drag==FD_BOX && (x1 != mx || y1 != my)) { - int x = x1; int r = mx; if (x > r) {x = mx; r = x1;} - int y = y1; int b = my; if (y > b) {y = my; b = y1;} - fl_rect(x,y,r-x,b-y); - } - if (overlays_invisible && !drag) return; - - if (show_restricted) { - draw_out_of_bounds(); - draw_overlaps(); - // TODO: for Fl_Tile, find all areas that are not covered by visible children - } - - if (selected) fl_rect(0,0,o->w(),o->h()); - if (!numselected) return; - int mybx,myby,mybr,mybt; - int mysx,mysy,mysr,myst; - mybx = mysx = o->w(); myby = mysy = o->h(); mybr = mysr = 0; mybt = myst = 0; - Fl_Type *selection = 0L; // special power for the first selected widget - for (Fl_Type *q=next; q && q->level>level; q = q->next) - if (q->selected && q->is_true_widget()) { - if (!selection) selection = q; - Fl_Widget_Type* myo = (Fl_Widget_Type*)q; - int x,y,r,t; - newposition(myo,x,y,r,t); - if (show_guides) { - // If we are in a drag operation, and the parent is a grid, show the grid overlay - if (drag && q->parent && q->parent->is_a(ID_Grid)) { - Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)((Fl_Grid_Type*)q->parent)->o); - grid->draw_overlay(); - } - } - if (!show_guides || !drag || numselected != 1) { - if (Fl_Flex_Type::parent_is_flex(q) && Fl_Flex_Type::is_fixed(q)) { - Fl_Flex *flex = ((Fl_Flex*)((Fl_Flex_Type*)q->parent)->o); - Fl_Widget *wgt = myo->o; - if (flex->horizontal()) { - draw_width(wgt->x(), wgt->y()+15, wgt->x()+wgt->w(), FL_ALIGN_CENTER); - } else { - draw_height(wgt->x()+15, wgt->y(), wgt->y()+wgt->h(), FL_ALIGN_CENTER); - } - } else if (q->is_a(ID_Grid)) { - Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)((Fl_Grid_Type*)q)->o); - grid->draw_overlay(); - } - fl_rect(x,y,r-x,t-y); - } - if (x < mysx) mysx = x; - if (y < mysy) mysy = y; - if (r > mysr) mysr = r; - if (t > myst) myst = t; - if (!(myo->o->align() & FL_ALIGN_INSIDE)) { - // Adjust left/right/top/bottom for top/bottom labels... - int ww, hh; - ww = (myo->o->align() & FL_ALIGN_WRAP) ? myo->o->w() : 0; - hh = myo->o->labelsize(); - myo->o->measure_label(ww, hh); - if (myo->o->align() & FL_ALIGN_TOP) y -= hh; - else if (myo->o->align() & FL_ALIGN_BOTTOM) t += hh; - else if (myo->o->align() & FL_ALIGN_LEFT) x -= ww + 4; - else if (myo->o->align() & FL_ALIGN_RIGHT) r += ww + 4; - } - if (x < mybx) mybx = x; - if (y < myby) myby = y; - if (r > mybr) mybr = r; - if (t > mybt) mybt = t; - } - if (selected) return; - - // align the snapping selection box with the box we draw. - sx = mysx; sy = mysy; sr = mysr; st = myst; - - // Draw selection box + resize handles... - // draw box including all labels - fl_focus_rect(mybx,myby,mybr-mybx,mybt-myby); // issue #816 - // draw box excluding labels - fl_rect(mysx,mysy,mysr-mysx,myst-mysy); - fl_rectf(mysx,mysy,5,5); - fl_rectf(mysr-5,mysy,5,5); - fl_rectf(mysr-5,myst-5,5,5); - fl_rectf(mysx,myst-5,5,5); - - if (show_guides && (drag & (FD_DRAG|FD_TOP|FD_LEFT|FD_BOTTOM|FD_RIGHT))) { - Fd_Snap_Data data = { dx, dy, sx, sy, sr, st, drag, 4, 4, dx, dy, (Fl_Widget_Type*)selection, this}; - Fd_Snap_Action::draw_all(data); - } -} - -extern Fl_Menu_Item Main_Menu[]; - -// Calculate new bounding box of selected widgets: -void Fl_Window_Type::fix_overlay() { - overlay_item->label("Hide O&verlays"); - if (overlay_button) overlay_button->label("Hide &Overlays"); - overlays_invisible = 0; - recalc = 1; - ((Overlay_Window *)(this->o))->redraw_overlay(); -} - -// check if we must redraw any parent of tabs/wizard type -void check_redraw_corresponding_parent(Fl_Type *s) { - Fl_Widget_Type * prev_parent = 0; - if( !s || !s->selected || !s->is_widget()) return; - for (Fl_Type *i=s; i && i->parent; i=i->parent) { - if (i->is_a(ID_Group) && prev_parent) { - if (i->is_a(ID_Tabs)) { - ((Fl_Tabs*)((Fl_Widget_Type*)i)->o)->value(prev_parent->o); - return; - } - if (i->is_a(ID_Wizard)) { - ((Fl_Wizard*)((Fl_Widget_Type*)i)->o)->value(prev_parent->o); - return; - } - } - if (i->is_a(ID_Group) && s->is_widget()) - prev_parent = (Fl_Widget_Type*)i; - } -} - -// do that for every window (when selected set changes): -void redraw_overlays() { - for (Fl_Type *o=Fl_Type::first; o; o=o->next) - if (o->is_a(ID_Window)) ((Fl_Window_Type*)o)->fix_overlay(); -} - -void toggle_overlays(Fl_Widget *,void *) { - overlays_invisible = !overlays_invisible; - - if (overlays_invisible) { - overlay_item->label("Show O&verlays"); - if (overlay_button) overlay_button->label("Show &Overlays"); - } else { - overlay_item->label("Hide O&verlays"); - if (overlay_button) overlay_button->label("Hide &Overlays"); - } - - for (Fl_Type *o=Fl_Type::first; o; o=o->next) - if (o->is_a(ID_Window)) { - Fl_Widget_Type* w = (Fl_Widget_Type*)o; - ((Overlay_Window*)(w->o))->redraw_overlay(); - } -} - -/** - \brief User changes settings to show positioning guides in layout editor overlay. - This is called from the main menu and from the check button in the Settings - dialog. - */ -void toggle_guides(Fl_Widget *,void *) { - show_guides = !show_guides; - fluid_prefs.set("show_guides", show_guides); - - if (show_guides) - guides_item->label("Hide Guides"); - else - guides_item->label("Show Guides"); - if (guides_button) - guides_button->value(show_guides); - - for (Fl_Type *o=Fl_Type::first; o; o=o->next) { - if (o->is_a(ID_Window)) { - Fl_Widget_Type* w = (Fl_Widget_Type*)o; - ((Overlay_Window*)(w->o))->redraw_overlay(); - } - } -} - -/** - \brief User changes settings to show positioning guides in layout editor overlay. - This is called from the check button in the Settings dialog. - */ -void toggle_guides_cb(Fl_Check_Button *o, void *v) { - toggle_guides(NULL, NULL); -} - -/** - \brief User changes settings to show overlapping and out of bounds widgets. - This is called from the main menu and from the check button in the Settings - dialog. - */ -void toggle_restricted(Fl_Widget *,void *) { - show_restricted = !show_restricted; - fluid_prefs.set("show_restricted", show_restricted); - - if (show_restricted) - restricted_item->label("Hide Restricted"); - else - restricted_item->label("Show Restricted"); - if (restricted_button) - restricted_button->value(show_restricted); - - for (Fl_Type *o=Fl_Type::first; o; o=o->next) { - if (o->is_a(ID_Window)) { - Fl_Widget_Type* w = (Fl_Widget_Type*)o; - ((Overlay_Window*)(w->o))->redraw_overlay(); - } - } -} - -/** - \brief User changes settings to show low contrast groups with a ghosted outline. - */ -void toggle_ghosted_outline_cb(Fl_Check_Button *,void *) { - show_ghosted_outline = !show_ghosted_outline; - fluid_prefs.set("show_ghosted_outline", show_ghosted_outline); - for (Fl_Type *o=Fl_Type::first; o; o=o->next) { - if (o->is_a(ID_Window)) { - Fl_Widget_Type* w = (Fl_Widget_Type*)o; - ((Overlay_Window*)(w->o))->redraw(); - } - } -} - -/** - \brief User changes settings to show overlapping and out of bounds widgets. - This is called from the check button in the Settings dialog. - */ -void toggle_restricted_cb(Fl_Check_Button *o, void *v) { - toggle_restricted(NULL, NULL); -} - -extern void select(Fl_Type *,int); -extern void select_only(Fl_Type *); -extern void deselect(); -extern Fl_Type* in_this_only; -extern void fix_group_size(Fl_Type *t); - -extern Fl_Menu_Item Main_Menu[]; -extern Fl_Menu_Item New_Menu[]; - -/** - Move the selected children according to current dx, dy, drag state. - - This is somewhat of a do-all function that received many additions when new - widget types were added. In the default case, moving a group will simply move - all descendants with it. When resizing, children are resized to fit within - the group. - - This is not ideal for widgets that are moved or resized within a group that - manages the layout of its children. We must create a more universal way to - modify move events per widget type. - - \param[in] key if key is not 0, it contains the code of the keypress that - caused this call. This must only be set when handle FL_KEYBOARD events. - */ -void Fl_Window_Type::moveallchildren(int key) -{ - bool update_widget_panel = false; - undo_checkpoint(); - Fl_Type *i; - for (i=next; i && i->level>level;) { - if (i->selected && i->is_true_widget()) { - Fl_Widget_Type* myo = (Fl_Widget_Type*)i; - int x,y,r,t,ow=myo->o->w(),oh=myo->o->h(); - newposition(myo,x,y,r,t); - if (myo->is_a(ID_Flex) || myo->is_a(ID_Grid)) { - // Flex and Grid need to be able to layout their children. - allow_layout++; - myo->o->resize(x,y,r-x,t-y); - allow_layout--; - } else { - // Other groups are resized without affecting their children, however - // they move their children if the entire widget is moved. - myo->o->resize(x,y,r-x,t-y); - } - if (Fl_Flex_Type::parent_is_flex(myo)) { - // If the border of a Flex child is move, give that child a fixed size - // so that the user request is reflected. - Fl_Flex_Type* ft = (Fl_Flex_Type*)myo->parent; - Fl_Flex* f = (Fl_Flex*)ft->o; - if (key) { - ft->keyboard_move_child(myo, key); - } else if (drag & FD_DRAG) { - ft->insert_child_at(myo->o, Fl::event_x(), Fl::event_y()); - } else { - if (f->horizontal()) { - if (myo->o->w()!=ow) { - f->fixed(myo->o, myo->o->w()); - f->layout(); - } - } else { - if (myo->o->h()!=oh) { - f->fixed(myo->o, myo->o->h()); - f->layout(); - } - } - } - // relayout the Flex parent - allow_layout++; - f->layout(); - allow_layout--; - } else if (myo->parent && myo->parent->is_a(ID_Grid)) { - Fl_Grid_Type* gt = (Fl_Grid_Type*)myo->parent; - Fl_Grid* g = (Fl_Grid*)gt->o; - if (key) { - gt->keyboard_move_child(myo, key); - } else { - if (drag & FD_DRAG) { - gt->insert_child_at(myo->o, Fl::event_x(), Fl::event_y()); - } else { - gt->child_resized(myo); - } - } - allow_layout++; - g->layout(); - allow_layout--; - update_widget_panel = true; - } else if (myo->parent && myo->parent->is_a(ID_Group)) { - Fl_Group_Type* gt = (Fl_Group_Type*)myo->parent; - ((Fl_Group*)gt->o)->init_sizes(); - } - // move all the children, whether selected or not: - Fl_Type* p; - for (p = myo->next; p && p->level>myo->level; p = p->next) - if (p->is_true_widget() && !myo->is_a(ID_Flex) && !myo->is_a(ID_Grid)) { - Fl_Widget_Type* myo2 = (Fl_Widget_Type*)p; - int X,Y,R,T; - newposition(myo2,X,Y,R,T); - myo2->o->resize(X,Y,R-X,T-Y); - } - i = p; - } else { - i = i->next; - } - } - for (i=next; i && i->level>level; i=i->next) - fix_group_size(i); - o->redraw(); - recalc = 1; - ((Overlay_Window *)(this->o))->redraw_overlay(); - set_modflag(1); - dx = dy = 0; - - update_xywh(); - if (update_widget_panel && the_panel && the_panel->visible()) { - propagate_load(the_panel, LOAD); - } -} - -int Fl_Window_Type::popupx = 0x7FFFFFFF; // mark as invalid (MAXINT) -int Fl_Window_Type::popupy = 0x7FFFFFFF; - -int Fl_Window_Type::handle(int event) { - static Fl_Type* selection = NULL; - switch (event) { - case FL_DND_ENTER: - // printf("DND enter\n"); - case FL_DND_DRAG: - // printf("DND drag\n"); - { - // find the innermost item clicked on: - selection = this; - for (Fl_Type* i=next; i && i->level>level; i=i->next) - if (i->is_a(ID_Group)) { - Fl_Widget_Type* myo = (Fl_Widget_Type*)i; - if (Fl::event_inside(myo->o) && myo->o->visible_r()) { - selection = myo; - if (Fl::event_clicks()==1) - reveal_in_browser(myo); - } - } - if (selection && !selection->selected) { - select_only(selection); - ((Overlay_Window *)o)->redraw_overlay(); - } - } - Fl::belowmouse(o); - return 1; - case FL_DND_RELEASE: - // printf("DND release\n"); - Fl::belowmouse(o); - return 1; - case FL_PASTE: - // printf("DND paste\n"); - { Fl_Type *prototype = typename_to_prototype(Fl::event_text()); - if (prototype==NULL) { - // it's not a FLUID type, so it could be the filename of an image - const char *cfn = Fl::event_text(); - // printf("DND is filename %s?\n", cfn); - if ((cfn == NULL) || (*cfn == 0)) return 0; - if (strlen(cfn) >= FL_PATH_MAX) return 0; - char fn[FL_PATH_MAX+1]; - // some platform prepend "file://" or "computer://" or similar text - const char *sep = strstr(cfn, "://"); - if (sep) - strcpy(fn, sep+3); - else - strcpy(fn, cfn); - // remove possibly trailing \r\n - int n = (int)strlen(fn)-1; - if (fn[n] == '\n') fn[n--] = 0; - if (fn[n] == '\r') fn[n--] = 0; - // on X11 and Wayland (?), filenames need to be decoded -#if (defined(FLTK_USE_X11) || defined(FLTK_USE_WAYLAND)) - fl_decode_uri(fn); -#endif - // does a file by that name actually exist? - if (fl_access(fn, 4)==-1) return 0; - // but is this an image file? - Fl_Image *img = Fl_Shared_Image::get(fn); - if (!img || (img->ld() < 0)) return 0; - // ok, so it is an image - now add it as image() or deimage() to the widget - // printf("DND check for target %s\n", fn); - Fl_Widget_Type *tgt = NULL; - for (Fl_Type* i=next; i && i->level>level; i=i->next) { - if (i->is_widget()) { - Fl_Widget_Type* myo = (Fl_Widget_Type*)i; - if (Fl::event_inside(myo->o) && myo->o->visible_r()) - tgt = myo; - } - } - if (tgt) { - char rel[FL_PATH_MAX+1]; - enter_project_dir(); - fl_filename_relative(rel, FL_PATH_MAX, fn); - leave_project_dir(); - // printf("DND image = %s\n", fn); - if (Fl::get_key(FL_Alt_L) || Fl::get_key(FL_Alt_R)) { - //if (Fl::event_alt()) { // TODO: X11/Wayland does not set the e_state on DND events - tgt->inactive_name(rel); - tgt->compress_deimage_ = 1; - tgt->bind_deimage_ = 0; - } else { - tgt->image_name(rel); - tgt->compress_image_ = 1; - tgt->bind_image_ = 0; - } - select_only(tgt); - tgt->open(); - } - return 1; - } - - in_this_only = this; - popupx = Fl::event_x(); - popupy = Fl::event_y(); - // If the selected widget at dnd start and the drop target are the same, - // or in the same group, add after selection. Otherwise, just add - // at the end of the selected group. - if ( Fl_Type::current_dnd->group() - && selection && selection->group() - && Fl_Type::current_dnd->group()==selection->group()) - { - Fl_Type *cc = Fl_Type::current; - Fl_Type::current = Fl_Type::current_dnd; - add_new_widget_from_user(prototype, Strategy::AS_LAST_CHILD); - Fl_Type::current = cc; - } else { - add_new_widget_from_user(prototype, Strategy::AS_LAST_CHILD); - } - popupx = 0x7FFFFFFF; - popupy = 0x7FFFFFFF; // mark as invalid (MAXINT) - in_this_only = NULL; - widget_browser->display(Fl_Type::current); - widget_browser->rebuild(); - return 1; - } - case FL_PUSH: - x1 = mx = Fl::event_x(); - y1 = my = Fl::event_y(); - drag = dx = dy = 0; - // test for popup menu: - if (Fl::event_button() >= 3) { - in_this_only = this; // modifies how some menu items work. - static const Fl_Menu_Item* myprev; - popupx = mx; popupy = my; - const Fl_Menu_Item* m = New_Menu->popup(mx,my,"New",myprev); - if (m && m->callback()) {myprev = m; m->do_callback(this->o);} - popupx = 0x7FFFFFFF; popupy = 0x7FFFFFFF; // mark as invalid (MAXINT) - in_this_only = 0; - return 1; - } - // find the innermost item clicked on: - selection = this; - {for (Fl_Type* i=next; i && i->level>level; i=i->next) - if (i->is_true_widget()) { - Fl_Widget_Type* myo = (Fl_Widget_Type*)i; - for (Fl_Widget *o1 = myo->o; o1; o1 = o1->parent()) - if (!o1->visible()) goto CONTINUE2; - if (Fl::event_inside(myo->o)) { - selection = myo; - if (Fl::event_clicks()==1) - reveal_in_browser(myo); - } - CONTINUE2:; - }} - // see if user grabs edges of selected region: - if (numselected && !(Fl::event_state(FL_SHIFT)) && - mx<=br+2 && mx>=bx-2 && my<=bt+2 && my>=by-2) { - if (mx >= br-5) drag |= FD_RIGHT; - else if (mx <= bx+5) drag |= FD_LEFT; - if (my >= bt-5) drag |= FD_BOTTOM; - else if (my <= by+5) drag |= FD_TOP; - if (!drag) drag = FD_DRAG; - } - // do object-specific selection of other objects: - {Fl_Type* t = selection->click_test(mx, my); - if (t) { - //if (t == selection) return 1; // indicates mouse eaten w/o change - if (Fl::event_state(FL_SHIFT)) { - Fl::event_is_click(0); - select(t, !t->selected); - } else { - deselect(); - select(t, 1); - if (t->is_a(ID_Menu_Item)) t->open(); - } - selection = t; - drag = 0; - } else { - if (!drag) drag = FD_BOX; // if all else fails, start a new selection region - }} - return 1; - - case FL_DRAG: - if (!drag) return 0; - mx = Fl::event_x(); - my = Fl::event_y(); - newdx(); - return 1; - - case FL_RELEASE: - if (!drag) return 0; - mx = Fl::event_x(); - my = Fl::event_y(); - if (drag != FD_BOX && (dx || dy || !Fl::event_is_click())) { - if (dx || dy) moveallchildren(); - } else if ((Fl::event_clicks() || Fl::event_state(FL_CTRL))) { - Fl_Widget_Type::open(); - } else { - if (mxlevel>level; i=i->next) - if (i->is_true_widget()) { - Fl_Widget_Type* myo = (Fl_Widget_Type*)i; - for (Fl_Widget *o1 = myo->o; o1; o1 = o1->parent()) - if (!o1->visible()) goto CONTINUE; - if (Fl::event_inside(myo->o)) selection = myo; - if (myo && myo->o && myo->o->x()>=x1 && myo->o->y()>y1 && - myo->o->x()+myo->o->w()o->y()+myo->o->h()selected : 1); - } - CONTINUE:; - } - // if nothing in box, select what was clicked on: - if (selection && !n) { - select(selection, toggle ? !selection->selected : 1); - } - } - drag = 0; - ((Overlay_Window *)o)->redraw_overlay(); - return 1; - - case FL_KEYBOARD: { - - int backtab = 0; - switch (Fl::event_key()) { - - case FL_Escape: - ((Fl_Window*)o)->hide(); - return 1; - - case FL_Tab: { - if (Fl::event_state(FL_SHIFT)) backtab = 1; - // find current child: - Fl_Type *i = Fl_Type::current; - while (i && !i->is_true_widget()) i = i->parent; - if (!i) return 0; - Fl_Type *p = i->parent; - while (p && p != this) p = p->parent; - if (!p || !p->is_widget()) { - i = next; if (!i || i->level <= level) return 0; - } - p = i; - for (;;) { - i = backtab ? i->prev : i->next; - if (!i || i->level <= level) {i = p; break;} - if (i->is_true_widget()) break; - } - deselect(); select(i,1); - return 1;} - - case FL_Left: dx = -1; dy = 0; goto ARROW; - case FL_Right: dx = +1; dy = 0; goto ARROW; - case FL_Up: dx = 0; dy = -1; goto ARROW; - case FL_Down: dx = 0; dy = +1; goto ARROW; - ARROW: - drag = (Fl::event_state(FL_SHIFT)) ? (FD_RIGHT|FD_BOTTOM) : FD_DRAG; - if (Fl::event_state(FL_COMMAND)) { - int x_step, y_step; - if (drag & (FD_RIGHT|FD_BOTTOM)) - Fd_Snap_Action::get_resize_stepsize(x_step, y_step); - else - Fd_Snap_Action::get_move_stepsize(x_step, y_step); - dx *= x_step; - dy *= y_step; - } - moveallchildren(Fl::event_key()); - drag = 0; - return 1; - - case 'o': - toggle_overlays(0, 0); - break; - - default: - return 0; - }} - - case FL_SHORTCUT: { - in_this_only = this; // modifies how some menu items work. - const Fl_Menu_Item* m = Main_Menu->test_shortcut(); - if (m && m->callback()) m->do_callback(this->o); - in_this_only = 0; - return (m != 0);} - - default: - return 0; - } -} - -//////////////////////////////////////////////////////////////// - - -/** - Write the C++ code that comes before the children of the window are written. - \param f the source code output stream - */ -void Fl_Window_Type::write_code1(fld::io::Code_Writer& f) { - Fl_Widget_Type::write_code1(f); -} - - -/** - Write the C++ code that comes after the children of the window are written. - \param f the source code output stream - */ -void Fl_Window_Type::write_code2(fld::io::Code_Writer& f) { - const char *var = is_class() ? "this" : name() ? name() : "o"; - // make the window modal or non-modal - if (modal) { - f.write_c("%s%s->set_modal();\n", f.indent(), var); - } else if (non_modal) { - f.write_c("%s%s->set_non_modal();\n", f.indent(), var); - } - // clear the window border - if (!((Fl_Window*)o)->border()) { - f.write_c("%s%s->clear_border();\n", f.indent(), var); - } - // set the xclass of the window - if (xclass) { - f.write_c("%s%s->xclass(", f.indent(), var); - f.write_cstring(xclass); - f.write_c(");\n"); - } - // make the window resizable - if (((Fl_Window*)o)->resizable() == o) - f.write_c("%s%s->resizable(%s);\n", f.indent(), var, var); - // set the size range last - if (sr_max_w || sr_max_h) { - f.write_c("%s%s->size_range(%d, %d, %d, %d);\n", f.indent(), var, - sr_min_w, sr_min_h, sr_max_w, sr_max_h); - } else if (sr_min_w || sr_min_h) { - f.write_c("%s%s->size_range(%d, %d);\n", f.indent(), var, sr_min_w, sr_min_h); - } - // insert extra code from user, may call `show()` - write_extra_code(f); - // stop adding widgets to this window - f.write_c("%s%s->end();\n", f.indent(), var); - write_block_close(f); -} - -void Fl_Window_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Widget_Type::write_properties(f); - if (modal) f.write_string("modal"); - else if (non_modal) f.write_string("non_modal"); - if (!((Fl_Window*)o)->border()) f.write_string("noborder"); - if (xclass) {f.write_string("xclass"); f.write_word(xclass);} - if (sr_min_w || sr_min_h || sr_max_w || sr_max_h) - f.write_string("size_range {%d %d %d %d}", sr_min_w, sr_min_h, sr_max_w, sr_max_h); - if (o->visible() || override_visible_) f.write_string("visible"); -} - -void Fl_Window_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"modal")) { - modal = 1; - } else if (!strcmp(c,"non_modal")) { - non_modal = 1; - } else if (!strcmp(c, "visible")) { - if (batch_mode) // don't actually open any windows in batch mode - override_visible_ = 1; - else // in interactive mode, we simply show the window - open_(); - } else if (!strcmp(c,"noborder")) { - ((Fl_Window*)o)->border(0); - } else if (!strcmp(c,"xclass")) { - storestring(f.read_word(),xclass); - ((Fl_Window*)o)->xclass(xclass); - } else if (!strcmp(c,"size_range")) { - int mw, mh, MW, MH; - if (sscanf(f.read_word(),"%d %d %d %d",&mw,&mh,&MW,&MH) == 4) { - sr_min_w = mw; sr_min_h = mh; sr_max_w = MW; sr_max_h = MH; - } - } else if (!strcmp(c,"xywh")) { - Fl_Widget_Type::read_property(f, c); - pasteoffset = 0; // make it not apply to contents - } else { - Fl_Widget_Type::read_property(f, c); - } -} - -int Fl_Window_Type::read_fdesign(const char* propname, const char* value) { - int x; - o->box(FL_NO_BOX); // because fdesign always puts an Fl_Box next - if (!strcmp(propname,"Width")) { - if (sscanf(value,"%d",&x) == 1) o->size(x,o->h()); - } else if (!strcmp(propname,"Height")) { - if (sscanf(value,"%d",&x) == 1) o->size(o->w(),x); - } else if (!strcmp(propname,"NumberofWidgets")) { - return 1; // we can figure out count from file - } else if (!strcmp(propname,"border")) { - if (sscanf(value,"%d",&x) == 1) ((Fl_Window*)o)->border(x); - } else if (!strcmp(propname,"title")) { - label(value); - } else { - return Fl_Widget_Type::read_fdesign(propname,value); - } - return 1; -} - -/////////////////////////////////////////////////////////////////////// - -Fl_Widget_Class_Type Fl_Widget_Class_type; -Fl_Widget_Class_Type *current_widget_class = 0; - -/** - Create and add a new Widget Class node. - \param[in] strategy add after current or as last child - \return new node - */ -Fl_Type *Fl_Widget_Class_Type::make(Strategy strategy) { - Fl_Type *anchor = Fl_Type::current, *p = anchor; - if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) p = p->parent; - while (p && (!p->is_decl_block() || (p->is_widget() && p->is_class()))) { - anchor = p; - strategy.placement(Strategy::AFTER_CURRENT); - p = p->parent; - } - Fl_Widget_Class_Type *myo = new Fl_Widget_Class_Type(); - myo->name("UserInterface"); - - if (!this->o) {// template widget - this->o = new Fl_Window(100,100); - Fl_Group::current(0); - } - myo->factory = this; - myo->drag = 0; - myo->numselected = 0; - Overlay_Window *w = new Overlay_Window(100, 100); - w->size_range(10, 10); - w->window = myo; - myo->o = w; - myo->add(anchor, strategy); - myo->modal = 0; - myo->non_modal = 0; - myo->wc_relative = 0; - - return myo; -} - -void Fl_Widget_Class_Type::write_properties(fld::io::Project_Writer &f) { - Fl_Window_Type::write_properties(f); - if (wc_relative==1) - f.write_string("position_relative"); - else if (wc_relative==2) - f.write_string("position_relative_rescale"); -} - -void Fl_Widget_Class_Type::read_property(fld::io::Project_Reader &f, const char *c) { - if (!strcmp(c,"position_relative")) { - wc_relative = 1; - } else if (!strcmp(c,"position_relative_rescale")) { - wc_relative = 2; - } else { - Fl_Window_Type::read_property(f, c); - } -} - -// Convert A::B::C::D to D (i.e. keep only innermost name) -// This is useful for classes that contain a namespace component -static const char *trimclassname(const char *n) { - if (!n) - return NULL; - const char *nn; - while((nn = strstr(n, "::"))) { - n = nn + 2; - } - return(n); -} - - -void Fl_Widget_Class_Type::write_code1(fld::io::Code_Writer& f) { -#if 0 - Fl_Widget_Type::write_code1(fld::io::Code_Writer& f); -#endif // 0 - - current_widget_class = this; - write_public_state = 1; - - const char *c = subclass(); - if (!c) c = "Fl_Group"; - - f.write_c("\n"); - write_comment_h(f); - f.write_h("\nclass %s : public %s {\n", name(), c); - if (strstr(c, "Window")) { - f.write_h("%svoid _%s();\n", f.indent(1), trimclassname(name())); - f.write_h("public:\n"); - f.write_h("%s%s(int X, int Y, int W, int H, const char *L = 0);\n", f.indent(1), trimclassname(name())); - f.write_h("%s%s(int W, int H, const char *L = 0);\n", f.indent(1), trimclassname(name())); - f.write_h("%s%s();\n", f.indent(1), trimclassname(name())); - - // a constructor with all four dimensions plus label - f.write_c("%s::%s(int X, int Y, int W, int H, const char *L) :\n", name(), trimclassname(name())); - f.write_c("%s%s(X, Y, W, H, L)\n{\n", f.indent(1), c); - f.write_c("%s_%s();\n", f.indent(1), trimclassname(name())); - f.write_c("}\n\n"); - - // a constructor with just the size and label. The window manager will position the window - f.write_c("%s::%s(int W, int H, const char *L) :\n", name(), trimclassname(name())); - f.write_c("%s%s(0, 0, W, H, L)\n{\n", f.indent(1), c); - f.write_c("%sclear_flag(16);\n", f.indent(1)); - f.write_c("%s_%s();\n", f.indent(1), trimclassname(name())); - f.write_c("}\n\n"); - - // a constructor that takes size and label from the Fluid database - f.write_c("%s::%s() :\n", name(), trimclassname(name())); - f.write_c("%s%s(0, 0, %d, %d, ", f.indent(1), c, o->w(), o->h()); - const char *cstr = label(); - if (cstr) f.write_cstring(cstr); - else f.write_c("0"); - f.write_c(")\n{\n"); - f.write_c("%sclear_flag(16);\n", f.indent(1)); - f.write_c("%s_%s();\n", f.indent(1), trimclassname(name())); - f.write_c("}\n\n"); - - f.write_c("void %s::_%s() {\n", name(), trimclassname(name())); -// f.write_c("%s%s *w = this;\n", f.indent(1), name()); - } else { - f.write_h("public:\n"); - f.write_h("%s%s(int X, int Y, int W, int H, const char *L = 0);\n", - f.indent(1), trimclassname(name())); - f.write_c("%s::%s(int X, int Y, int W, int H, const char *L) :\n", name(), trimclassname(name())); - if (wc_relative==1) - f.write_c("%s%s(0, 0, W, H, L)\n{\n", f.indent(1), c); - else if (wc_relative==2) - f.write_c("%s%s(0, 0, %d, %d, L)\n{\n", f.indent(1), c, o->w(), o->h()); - else - f.write_c("%s%s(X, Y, W, H, L)\n{\n", f.indent(1), c); - } - -// f.write_c("%s%s *o = this;\n", f.indent(1), name()); - - f.indentation++; - write_widget_code(f); -} - -/** - Write the C++ code that comes after the children of the window are written. - \param f the source code output stream - */ -void Fl_Widget_Class_Type::write_code2(fld::io::Code_Writer& f) { - // make the window modal or non-modal - if (modal) { - f.write_c("%sset_modal();\n", f.indent()); - } else if (non_modal) { - f.write_c("%sset_non_modal();\n", f.indent()); - } - // clear the window border - if (!((Fl_Window*)o)->border()) f.write_c("%sclear_border();\n", f.indent()); - // set the xclass of the window - if (xclass) { - f.write_c("%sxclass(", f.indent()); - f.write_cstring(xclass); - f.write_c(");\n"); - } - // make the window resizable - if (((Fl_Window*)o)->resizable() == o) - f.write_c("%sresizable(this);\n", f.indent()); - // insert extra code from user - write_extra_code(f); - // stop adding widgets to this window - f.write_c("%send();\n", f.indent()); - // reposition or resize the Widget Class to fit into the target - if (wc_relative==1) - f.write_c("%sposition(X, Y);\n", f.indent()); - else if (wc_relative==2) - f.write_c("%sresize(X, Y, W, H);\n", f.indent()); - f.indentation--; - f.write_c("}\n"); -} - - -//////////////////////////////////////////////////////////////// -// live mode support - -Fl_Widget *Fl_Window_Type::enter_live_mode(int) { - Fl_Window *win = new Fl_Window(10, 10, o->w(), o->h()); - return propagate_live_mode(win); -} - -void Fl_Window_Type::leave_live_mode() { -} - -/** - copy all properties from the edit widget to the live widget - */ -void Fl_Window_Type::copy_properties() { - Fl_Window *self = static_cast(o); - Fl_Window *live = static_cast(live_widget); - if (self->resizable() == self) - live->resizable(live); - Fl_Widget_Type::copy_properties(); -} diff --git a/fluid/nodes/Fl_Window_Type.h b/fluid/nodes/Fl_Window_Type.h deleted file mode 100644 index 0dcb9e96e..000000000 --- a/fluid/nodes/Fl_Window_Type.h +++ /dev/null @@ -1,157 +0,0 @@ -// -// Window type header file for the Fast Light Tool Kit (FLTK). -// -// Type for creating all subclasses of Fl_Widget -// This should have the widget pointer in it, but it is still in the -// Fl_Type base class. -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef _FLUID_FL_WINDOW_TYPE_H -#define _FLUID_FL_WINDOW_TYPE_H - -#include "nodes/Fl_Group_Type.h" - -class Fl_Widget_Class_Type; - -extern Fl_Menu_Item window_type_menu[]; -extern Fl_Widget_Class_Type *current_widget_class; - -void toggle_overlays(Fl_Widget *,void *); -void toggle_guides(Fl_Widget *,void *); -void toggle_restricted(Fl_Widget *,void *); -void show_project_cb(Fl_Widget *, void *); -void show_grid_cb(Fl_Widget *, void *); -void show_settings_cb(Fl_Widget *, void *); - -enum { - FD_LEFT = 1, // user drags the left side of the selection box - FD_RIGHT = 2, - FD_BOTTOM = 4, - FD_TOP = 8, - FD_DRAG = 16, // user drags the entire selection - FD_BOX = 32 // user creates a new selection box -}; - -class Fl_Window_Type : public Fl_Group_Type -{ - typedef Fl_Group_Type super; -protected: - - Fl_Menu_Item* subtypes() FL_OVERRIDE {return window_type_menu;} - - friend class Overlay_Window; - int mx,my; // mouse position during dragging - int x1,y1; // initial position of selection box - int bx,by,br,bt; // bounding box of selection before snapping - int sx,sy,sr,st; // bounding box of selection after snapping to guides - int dx,dy; - int drag; // which parts of bbox are being moved - int numselected; // number of children selected - void draw_out_of_bounds(Fl_Widget_Type *group, int x, int y, int w, int h); - void draw_out_of_bounds(); - void draw_overlaps(); - void draw_overlay(); - void newdx(); - void newposition(Fl_Widget_Type *,int &x,int &y,int &w,int &h); - int handle(int); - void setlabel(const char *) FL_OVERRIDE; - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - Fl_Widget_Type *_make() FL_OVERRIDE {return 0;} // we don't call this - Fl_Widget *widget(int,int,int,int) FL_OVERRIDE {return 0;} - int recalc; // set by fix_overlay() - void moveallchildren(int key=0); - ID id() const FL_OVERRIDE { return ID_Window; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Window) ? true : super::is_a(inID); } - void open_(); - -public: - - Fl_Window_Type() : - mx(0), my(0), - x1(0), y1(0), - bx(0), by(0), br(0), bt(0), - sx(0), sy(0), sr(0), st(0), - dx(0), dy(0), - drag(0), - numselected(0), - recalc(0), - modal(0), non_modal(0), - xclass(NULL), - sr_min_w(0), sr_min_h(0), sr_max_w(0), sr_max_h(0) - { } - uchar modal, non_modal; - const char *xclass; // junk string, used for shortcut - - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "Fl_Window";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::Window";} - - void open() FL_OVERRIDE; - void ideal_size(int &w, int &h) FL_OVERRIDE; - - void fix_overlay(); // Update the bounding box, etc - uchar *read_image(int &ww, int &hh); // Read an image of the window - - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - int read_fdesign(const char*, const char*) FL_OVERRIDE; - - void add_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; - void remove_child(Fl_Type*) FL_OVERRIDE; - - int can_have_children() const FL_OVERRIDE {return 1;} - - Fl_Widget *enter_live_mode(int top=0) FL_OVERRIDE; - void leave_live_mode() FL_OVERRIDE; - void copy_properties() FL_OVERRIDE; - - int sr_min_w, sr_min_h, sr_max_w, sr_max_h; - - static int popupx, popupy; -}; - -class Fl_Widget_Class_Type : private Fl_Window_Type -{ - typedef Fl_Window_Type super; -protected: - Fl_Menu_Item* subtypes() FL_OVERRIDE {return 0;} - -public: - Fl_Widget_Class_Type() { - write_public_state = 0; - wc_relative = 0; - } - // state variables for output: - char write_public_state; // true when public: has been printed - char wc_relative; // if 1, reposition all children, if 2, reposition and resize - - void write_properties(fld::io::Project_Writer &f) FL_OVERRIDE; - void read_property(fld::io::Project_Reader &f, const char *) FL_OVERRIDE; - - void write_code1(fld::io::Code_Writer& f) FL_OVERRIDE; - void write_code2(fld::io::Code_Writer& f) FL_OVERRIDE; - Fl_Type *make(Strategy strategy) FL_OVERRIDE; - const char *type_name() FL_OVERRIDE {return "widget_class";} - ID id() const FL_OVERRIDE { return ID_Widget_Class; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Widget_Class) ? true : super::is_a(inID); } - int can_have_children() const FL_OVERRIDE {return 1;} - int is_code_block() const FL_OVERRIDE {return 1;} - int is_decl_block() const FL_OVERRIDE {return 1;} - int is_class() const FL_OVERRIDE {return 1;} -}; - -#endif // _FLUID_FL_WINDOW_TYPE_H diff --git a/fluid/nodes/Function_Node.cxx b/fluid/nodes/Function_Node.cxx new file mode 100644 index 000000000..8ffdbef01 --- /dev/null +++ b/fluid/nodes/Function_Node.cxx @@ -0,0 +1,2158 @@ +// +// C function Node code for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#include "nodes/Function_Node.h" + +#include "Fluid.h" +#include "proj/mergeback.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "nodes/Window_Node.h" +#include "nodes/Group_Node.h" +#include "panels/function_panel.h" +#include "rsrcs/comments.h" +#include "widgets/Node_Browser.h" + +#include +#include +#include +#include "../src/flstring.h" + +#include + + +/// Set a current class, so that the code of the children is generated correctly. +Class_Node *current_class = nullptr; + +/** + \brief Return 1 if the list contains a function with the given signature at the top level. + Widget_Node uses this to check if a callback by a certain signature is + already defined by the user within this file. If not, Widget_Node will + generate an `extern $sig$;` statement. + \param[in] rtype return type, can be nullptr to avoid checking (not used by Widget_Node) + \param[in] sig function signature + \return 1 if found. + */ +int has_toplevel_function(const char *rtype, const char *sig) { + Node *child; + for (child = Fluid.proj.tree.first; child; child = child->next) { + if (!child->is_in_class() && child->is_a(Type::Function)) { + const Function_Node *fn = (const Function_Node*)child; + if (fn->has_signature(rtype, sig)) + return 1; + } + } + return 0; +} + + +//////////////////////////////////////////////////////////////// +// quick check of any C code for legality, returns an error message + +static char buffer[128]; // for error messages + +/** + Check a quoted string contains a character. + This is used to find a matching " or ' in a string. + \param[inout] c start searching here, return where we found \c type + \param[in] type find this character + \return nullptr if the character was found, else a pointer to a static string + with an error message + */ +const char *_q_check(const char * & c, int type) { + for (;;) switch (*c++) { + case '\0': + sprintf(buffer,"missing %c",type); + return buffer; + case '\\': + if (*c) c++; + break; + default: + if (*(c-1) == type) return nullptr; + } +} + +/** + Check normal code, match brackets and parenthesis. + Recursively run a line of code and make sure that + {, [, ", ', and ( are matched. + \param[inout] c start searching here, return the end of the search + \param[in] type find this character match + \return nullptr if the character was found, else a pointer to a static string + with an error message + */ +const char *_c_check(const char * & c, int type) { + const char *d; + for (;;) switch (*c++) { + case 0: + if (!type) return nullptr; + sprintf(buffer, "missing '%c'", type); + return buffer; + case '/': + // Skip comments as needed... + if (*c == '/') { + while (*c != '\n' && *c) c++; + } else if (*c == '*') { + c++; + while ((*c != '*' || c[1] != '/') && *c) c++; + if (*c == '*') c+=2; + else { + return "missing '*/'"; + } + } + break; +// case '#': +// // treat cpp directives as a comment: +// // Matt: a '#' character can appear as a concatenation when defining macros +// // Matt: so instead we just silently ignore the '#' +// while (*c != '\n' && *c) c++; +// break; + case '{': +// // Matt: C++ does allow {} inside () now +// if (type==')') goto UNEXPECTED; + d = _c_check(c,'}'); + if (d) return d; + break; + case '(': + d = _c_check(c,')'); + if (d) return d; + break; + case '[': + d = _c_check(c,']'); + if (d) return d; + break; + case '\"': + d = _q_check(c,'\"'); + if (d) return d; + break; + case '\'': + d = _q_check(c,'\''); + if (d) return d; + break; + case '}': + case ')': + case ']': +// UNEXPECTED: + if (type == *(c-1)) return nullptr; + sprintf(buffer, "unexpected '%c'", *(c-1)); + return buffer; + } +} + +/** + Check legality of c code (sort of) and return error: + Make sure that {, ", ', and ( are matched. + \param[in] c start searching here + \param[in] type find this character match (default is 0) + \return nullptr if the character was found, else a pointer to a static string + with an error message + \note This function checks every conceivable line of code, which is not + always wanted. It can't differentiate characters in comments, and the + user may well intend to leave a curly bracket open + (i.e. namespace { ... } ). We should make this option user selectable. + */ +const char *c_check(const char *c, int type) { + return _c_check(c,type); +} + +// ---- Function_Node implementation + +/** \class Function_Node + Manage a C++ function node in the Fluid design. + + A function can have a signature (name followed by arguments), a return type + and a comment section. If can be local or global, and it can be declared a C + or C++ function. + */ + +/// Prototype for a function to be used by the factory. +Function_Node Function_Node::prototype; + +/** + Create a new function. + */ +Function_Node::Function_Node() : + Node(), + return_type(nullptr), + public_(0), + cdecl_(0), + constructor(0), + havewidgets(0) +{ } + +/** + Destructor. + */ +Function_Node::~Function_Node() { + if (return_type) free((void*)return_type); +} + +/** + Create a new function for the widget tree. + \param[in] strategy add new function after current or as last child + \return the new node + */ +Node *Function_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_decl_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + Function_Node *o = new Function_Node(); + o->name("make_window()"); + o->return_type = nullptr; + o->add(anchor, strategy); + o->factory = this; + o->public_ = 1; + o->cdecl_ = 0; + return o; +} + +/** + Write function specific properties to an .fl file. + - "private"/"public" indicates the state of the function + - "C" is written if we want a C signature instead of C++ + - "return_type" is followed by the return type of the function + */ +void Function_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + switch (public_) { + case 0: f.write_string("private"); break; + case 2: f.write_string("protected"); break; + } + if (cdecl_) f.write_string("C"); + if (return_type) { + f.write_string("return_type"); + f.write_word(return_type); + } +} + +/** + Read function specific properties fron an .fl file. + \param[in] c read from this string + */ +void Function_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"private")) { + public_ = 0; + } else if (!strcmp(c,"protected")) { + public_ = 2; + } else if (!strcmp(c,"C")) { + cdecl_ = 1; + } else if (!strcmp(c,"return_type")) { + storestring(f.read_word(),return_type); + } else { + Node::read_property(f, c); + } +} + +/** + Open the function_panel dialog box to edit this function. + */ +void Function_Node::open() { + // fill dialog box + if (!function_panel) make_function_panel(); + f_return_type_input->value(return_type); + f_name_input->value(name()); + if (is_in_class()) { + f_public_member_choice->value(public_); + f_public_member_choice->show(); + f_public_choice->hide(); + f_c_button->hide(); + } else { + f_public_choice->value(public_); + f_public_choice->show(); + f_public_member_choice->hide(); + f_c_button->show(); + } + f_c_button->value(cdecl_); + const char *c = comment(); + f_comment_input->buffer()->text(c?c:""); + function_panel->show(); + const char* message = nullptr; + for (;;) { // repeat as long as there are errors + // - message loop until OK or cancel is pressed + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == f_panel_cancel) goto BREAK2; + else if (w == f_panel_ok) break; + else if (!w) Fl::wait(); + } + // - check syntax + const char *c = f_name_input->value(); + while (isspace(*c)) c++; + message = c_check(c); + if (!message) { + const char *d = c; + for (; *d != '('; d++) if (isspace(*d) || !*d) break; + if (*c && *d != '(') + message = "must be 'name(arguments)'"; + } + if (!message) { + c = f_return_type_input->value(); + message = c_check(c); + } + // - alert user + if (message) { + int v = fl_choice("Potential syntax error detected: %s", + "Continue Editing", "Ignore Error", nullptr, message); + if (v==0) continue; // Continue Editing + //if (v==1) { } // Ignore Error and close dialog + } + // - copy dialog data to target variables + int mod = 0; + name(f_name_input->value()); + storestring(f_return_type_input->value(), return_type); + if (is_in_class()) { + if (public_ != f_public_member_choice->value()) { + mod = 1; + public_ = f_public_member_choice->value(); + redraw_browser(); + } + } else { + if (public_ != f_public_choice->value()) { + mod = 1; + public_ = f_public_choice->value(); + redraw_browser(); + } + } + if (cdecl_ != f_c_button->value()) { + mod = 1; + cdecl_ = f_c_button->value(); + } + c = f_comment_input->buffer()->text(); + if (c && *c) { + if (!comment() || strcmp(c, comment())) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(c); + } else { + if (comment()) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(nullptr); + } + if (c) free((void*)c); + if (mod) Fluid.proj.set_modflag(1); + break; + } +BREAK2: + function_panel->hide(); +} + +/** + Return 1 if the function is global. + \return 1 if public, 0 if local. + */ +int Function_Node::is_public() const { + return public_; +} + +static bool fd_isspace(int c) { + return (c>0 && c<128 && isspace(c)); +} + +// code duplication: see int is_id(char c) in code.cxx +static bool fd_iskeyword(int c) { + return (c>0 && c<128 && (isalnum(c) || c=='_')); +} + +// remove all function default parameters and `override` keyword +static void clean_function_for_implementation(char *out, const char *function_name) { + char *sptr = out; + const char *nptr = function_name; + int skips=0,skipc=0; + int nc=0,plevel=0; + bool arglist_done = false; + for (;*nptr; nc++,nptr++) { + if (arglist_done && fd_isspace(nptr[0])) { + // skip `override` and `FL_OVERRIDE` keywords if they are following the list of arguments + if (strncmp(nptr+1, "override", 8)==0 && !fd_iskeyword(nptr[9])) { nptr += 8; continue; } + else if (strncmp(nptr+1, "FL_OVERRIDE", 11)==0 && !fd_iskeyword(nptr[12])) { nptr += 11; continue; } + } + if (!skips && *nptr=='(') plevel++; + else if (!skips && *nptr==')') { plevel--; if (plevel==0) arglist_done = true; } + if ( *nptr=='"' && !(nc && *(nptr-1)=='\\') ) + skips = skips ? 0 : 1; + else if(!skips && *nptr=='\'' && !(nc && *(nptr-1)=='\\')) + skipc = skipc ? 0 : 1; + if(!skips && !skipc && plevel==1 && *nptr =='=' && !(nc && *(nptr-1)=='\'') ) { // ignore '=' case + while(*++nptr && (skips || skipc || ( (*nptr!=',' && *nptr!=')') || plevel!=1) )) { + if ( *nptr=='"' && *(nptr-1)!='\\' ) + skips = skips ? 0 : 1; + else if(!skips && *nptr=='\'' && *(nptr-1)!='\\') + skipc = skipc ? 0 : 1; + if (!skips && !skipc && *nptr=='(') plevel++; + else if (!skips && *nptr==')') plevel--; + } + if (*nptr==')') if (--plevel==0) arglist_done = true; + } + if (sptr < (out + 1024 - 1)) *sptr++ = *nptr; + } + *sptr = '\0'; +} + + +/** + Write the code for the source and the header file. + This writes the code that goes \b before all children of this class. + \see write_code2(fld::io::Code_Writer& f) + */ +void Function_Node::write_code1(fld::io::Code_Writer& f) { + constructor=0; + havewidgets = 0; + Node *child; + // if the function has no children (hence no body), Fluid will not generate + // the function either. This is great if you decide to implement that function + // inside another module + char havechildren = 0; + for (child = next; child && child->level > level; child = child->next) { + havechildren = 1; + if (child->is_widget()) { + havewidgets = 1; + break; + } + } + if (havechildren) + f.write_c("\n"); + if (ismain()) { + if (havechildren) + f.write_c("int main(int argc, char **argv) {\n"); + } else { + const char* rtype = return_type; + const char* star = ""; + // from matt: let the user type "static " at the start of type + // in order to declare a static method; + int is_static = 0; + int is_virtual = 0; + if (rtype) { + if (!strcmp(rtype,"static")) {is_static = 1; rtype = nullptr;} + else if (!strncmp(rtype, "static ",7)) {is_static = 1; rtype += 7;} + } + if (rtype) { + if (!strcmp(rtype, "virtual")) {is_virtual = 1; rtype = nullptr;} + else if (!strncmp(rtype, "virtual ",8)) {is_virtual = 1; rtype += 8;} + } + if (!rtype) { + if (havewidgets) { + rtype = subclassname(child); + star = "*"; + } else rtype = "void"; + } + + const char* k = class_name(0); + if (k) { + f.write_public(public_); + if (havechildren) + write_comment_c(f); + if (name()[0] == '~') + constructor = 1; + else { + size_t n = strlen(k); + if (!strncmp(name(), k, n) && name()[n] == '(') constructor = 1; + } + f.write_h("%s", f.indent(1)); + if (is_static) f.write_h("static "); + if (is_virtual) f.write_h("virtual "); + if (!constructor) { + f.write_h("%s%s ", rtype, star); + if (havechildren) + f.write_c("%s%s ", rtype, star); + } + + // if this is a subclass, only f.write_h() the part before the ':' + char s[1024], *sptr = s; + char *nptr = (char *)name(); + + while (*nptr) { + if (*nptr == ':') { + if (nptr[1] != ':') break; + // Copy extra ":" for "class::member"... + *sptr++ = *nptr++; + } + *sptr++ = *nptr++; + } + *sptr = '\0'; + + if (s[strlen(s)-1] == '}') { // special case for inlined functions + f.write_h("%s\n", s); + } else { + f.write_h("%s;\n", s); + } + if (havechildren) { + clean_function_for_implementation(s, name()); + f.write_c("%s::%s {\n", k, s); + } + } else { + if (havechildren) + write_comment_c(f); + if (public_==1) { + if (cdecl_) + f.write_h("extern \"C\" { %s%s %s; }\n", rtype, star, name()); + else + f.write_h("%s%s %s;\n", rtype, star, name()); + } else if (public_==2) { + // write neither the prototype nor static, the function may be declared elsewhere + } else { + if (havechildren) + f.write_c("static "); + } + + // write everything but the default parameters (if any) + char s[1024]; + if (havechildren) { + clean_function_for_implementation(s, name()); + f.write_c("%s%s %s {\n", rtype, star, s); + } + } + } + + if (havewidgets && child && !child->name()) + f.write_c("%s%s* w;\n", f.indent(1), subclassname(child)); + f.indentation++; +} + +/** + Write the code for the source and the header file. + This writes the code that goes \b after all children of this class. + \see write_code1(fld::io::Code_Writer& f) + */ +void Function_Node::write_code2(fld::io::Code_Writer& f) { + Node *child; + const char *var = "w"; + char havechildren = 0; + for (child = next; child && child->level > level; child = child->next) { + havechildren = 1; + if (child->is_a(Type::Window) && child->name()) var = child->name(); + } + + if (ismain()) { + if (havewidgets) + f.write_c("%s%s->show(argc, argv);\n", f.indent(1), var); + if (havechildren) + f.write_c("%sreturn Fl::run();\n", f.indent(1)); + } else if (havewidgets && !constructor && !return_type) { + f.write_c("%sreturn %s;\n", f.indent(1), var); + } + if (havechildren) + f.write_c("}\n"); + f.indentation = 0; +} + +/** + Check if the return type and signatures match. + \param[in] rtype function return type + \param[in] sig function name followed by arguments + \return 1 if they match, 0 if not + */ +int Function_Node::has_signature(const char *rtype, const char *sig) const { + if (rtype && !return_type) return 0; + if (!name()) return 0; + if ( (rtype==nullptr || strcmp(return_type, rtype)==0) + && fl_filename_match(name(), sig)) { + return 1; + } + return 0; +} + +// ---- Code_Node declaration + +/** \class Code_Node + Manage a block of C++ code in the Fluid design. + + This node manages an arbitrary block of code inside a function that will + be written into the source code file. Fl_Code_Block has no comment field. + However, the first line of code will be shown in the widget browser. + */ + +/// Prototype for code to be used by the factory. +Code_Node Code_Node::prototype; + +/** + Constructor. + */ +Code_Node::Code_Node() : + cursor_position_(0), + code_input_scroll_row(0), + code_input_scroll_col(0) +{} + +/** + Make a new code node. + If the parent node is not a function, a message box will pop up and + the request will be ignored. + \param[in] strategy add code after current or as last child + \return new Code node + */ +Node *Code_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_code_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + if (!p) { + fl_message("Please select a function"); + return nullptr; + } + Code_Node *o = new Code_Node(); + o->name("printf(\"Hello, World!\\n\");"); + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Open the code_panel or an external editor to edit this code section. + */ +void Code_Node::open() { + // Using an external code editor? Open it.. + if ( Fluid.use_external_editor && Fluid.external_editor_command[0] ) { + const char *cmd = Fluid.external_editor_command; + const char *code = name(); + if (!code) code = ""; + if ( editor_.open_editor(cmd, code) == 0 ) + return; // return if editor opened ok, fall thru to built-in if not + } + // Use built-in code editor.. + if (!code_panel) make_code_panel(); + const char *text = name(); + code_input->buffer()->text( text ? text : "" ); + code_input->insert_position(cursor_position_); + code_input->scroll(code_input_scroll_row, code_input_scroll_col); + code_panel->show(); + const char* message = nullptr; + for (;;) { // repeat as long as there are errors + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == code_panel_cancel) goto BREAK2; + else if (w == code_panel_ok) break; + else if (!w) Fl::wait(); + } + char*c = code_input->buffer()->text(); + message = c_check(c); + if (message) { + int v = fl_choice("Potential syntax error detected: %s", + "Continue Editing", "Ignore Error", nullptr, message); + if (v==0) continue; // Continue Editing + //if (v==1) { } // Ignore Error and close dialog + } + name(c); + free(c); + break; + } + cursor_position_ = code_input->insert_position(); + code_input_scroll_row = code_input->scroll_row(); + code_input_scroll_col = code_input->scroll_col(); +BREAK2: + code_panel->hide(); +} + +/** + Grab changes from an external editor and write this node. + */ +void Code_Node::write(fld::io::Project_Writer &f) { + // External editor changes? If so, load changes into ram, update mtime/size + if ( handle_editor_changes() == 1 ) { + Fluid.main_window->redraw(); // tell fluid to redraw; edits may affect tree's contents + } + Node::write(f); +} + +/** + Write the code block with the correct indentation. + */ +void Code_Node::write_code1(fld::io::Code_Writer& f) { + // External editor changes? If so, load changes into ram, update mtime/size + if ( handle_editor_changes() == 1 ) { + Fluid.main_window->redraw(); // tell fluid to redraw; edits may affect tree's contents + } + // Matt: disabled f.tag(FD_TAG_GENERIC, 0); + f.write_c_indented(name(), 0, '\n'); + // Matt: disabled f.tag(FD_TAG_CODE, get_uid()); +} + +/** + See if external editor is open. + */ +int Code_Node::is_editing() { + return editor_.is_editing(); +} + +/** + Reap the editor's pid + \return -2: editor not open + \return -1: wait failed + \return 0: process still running + \return \>0: process finished + reaped (returns pid) + */ +int Code_Node::reap_editor() { + return editor_.reap_editor(); +} + +/** + Handle external editor file modifications. + If changed, record keeping is updated and file's contents is loaded into ram + \return 0: file unchanged or not editing + \return 1: file changed, internal records updated, 'code' has new content + \return -1: error getting file info (get_ms_errmsg() has reason) + \todo Figure out how saving a fluid file can be intercepted to grab + current contents of editor file.. + */ +int Code_Node::handle_editor_changes() { + const char *newcode = nullptr; + switch ( editor_.handle_changes(&newcode) ) { + case 1: { // (1)=changed + name(newcode); // update value in ram + free((void*)newcode); + return 1; + } + case -1: return -1; // (-1)=error -- couldn't read file (dialog showed reason) + default: break; // (0)=no change + } + return 0; +} + +// ---- CodeBlock_Node implementation + +/** \class CodeBlock_Node + Manage two blocks of C++ code enclosing its children. + + This node manages two lines of code that enclose all children + of this node. This is usually an if..then clause. + + \todo this node could support multiple lines of code for each block. + */ + +/// Prototype for a block of code to be used by the factory. +CodeBlock_Node CodeBlock_Node::prototype; + +/** + Constructor. + */ +CodeBlock_Node::CodeBlock_Node() : + Node(), + after(nullptr) +{ } + +/** + Destructor. + */ +CodeBlock_Node::~CodeBlock_Node() { + if (after) + free((void*)after); +} + +/** + Make a new code block. + If the parent node is not a function or another codeblock, a message box will + pop up and the request will be ignored. + \param[in] strategy add after current or as last child + \return new CodeBlock + */ +Node *CodeBlock_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_code_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + if (!p) { + fl_message("Please select a function"); + return nullptr; + } + CodeBlock_Node *o = new CodeBlock_Node(); + o->name("if (test())"); + o->after = nullptr; + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Write the specific properties for this node. + - "after" is followed by the code that comes after the children + The "before" code is stored in the name() field. + */ +void CodeBlock_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + if (after) { + f.write_string("after"); + f.write_word(after); + } +} + +/** + Read the node specific properties. + */ +void CodeBlock_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"after")) { + storestring(f.read_word(),after); + } else { + Node::read_property(f, c); + } +} + +/** + Open the codeblock_panel. + */ +void CodeBlock_Node::open() { + if (!codeblock_panel) make_codeblock_panel(); + code_before_input->value(name()); + code_after_input->value(after); + codeblock_panel->show(); + const char* message = nullptr; + for (;;) { // repeat as long as there are errors + // event loop + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == codeblock_panel_cancel) goto BREAK2; + else if (w == codeblock_panel_ok) break; + else if (!w) Fl::wait(); + } + // check for syntax errors + message = c_check(code_before_input->value()); + if (!message) { + message = c_check(code_after_input->value()); + } + // alert user + if (message) { + int v = fl_choice("Potential syntax error detected: %s", + "Continue Editing", "Ignore Error", nullptr, message); + if (v==0) continue; // Continue Editing + //if (v==1) { } // Ignore Error and close dialog + } + // write to variables + name(code_before_input->value()); + storestring(code_after_input->value(), after); + break; + } +BREAK2: + codeblock_panel->hide(); +} + +/** + Write the "before" code. + */ +void CodeBlock_Node::write_code1(fld::io::Code_Writer& f) { + const char* c = name(); + f.write_c("%s%s {\n", f.indent(), c ? c : ""); + f.indentation++; +} + +/** + Write the "after" code. + */ +void CodeBlock_Node::write_code2(fld::io::Code_Writer& f) { + f.indentation--; + if (after) f.write_c("%s} %s\n", f.indent(), after); + else f.write_c("%s}\n", f.indent()); +} + +// ---- Decl_Node declaration + +/** \class Decl_Node + Manage the C/C++ declaration of a variable. + + This node manages a single line of code that can be in the header or the source + code, and can be made static. + + \todo this node could support multiple lines. + */ + +/// Prototype for a declaration to be used by the factory. +Decl_Node Decl_Node::prototype; + +/** + Constructor. + */ +Decl_Node::Decl_Node() : + public_(0), + static_(1) +{ } + +/** + Return 1 if this declaration and its parents are public. + */ +int Decl_Node::is_public() const +{ + Node *p = parent; + while (p && !p->is_decl_block()) p = p->parent; + if(p && p->is_public() && public_) + return public_; + else if(!p) + return public_; + return 0; +} + +/** + Make a new declaration. + \param[in] strategy add after current or as last child + \return new Declaration node + */ +Node *Decl_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_decl_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + Decl_Node *o = new Decl_Node(); + o->public_ = 0; + o->static_ = 1; + o->name("int x;"); + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Write the specific properties. + - "private"/"public"/"protected" + - "local"/"global" if this is static or not + */ +void Decl_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + switch (public_) { + case 0: f.write_string("private"); break; + case 1: f.write_string("public"); break; + case 2: f.write_string("protected"); break; + } + if (static_) + f.write_string("local"); + else + f.write_string("global"); +} + +/** + Read the specific properties. + */ +void Decl_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"public")) { + public_ = 1; + } else if (!strcmp(c,"private")) { + public_ = 0; + } else if (!strcmp(c,"protected")) { + public_ = 2; + } else if (!strcmp(c,"local")) { + static_ = 1; + } else if (!strcmp(c,"global")) { + static_ = 0; + } else { + Node::read_property(f, c); + } +} + +/** + Open the decl_panel to edit this node. + */ +void Decl_Node::open() { + if (!decl_panel) make_decl_panel(); + decl_input->buffer()->text(name()); + if (is_in_class()) { + decl_class_choice->value(public_); + decl_class_choice->show(); + decl_choice->hide(); + } else { + decl_choice->value((public_&1)|((static_&1)<<1)); + decl_choice->show(); + decl_class_choice->hide(); + } + const char *c = comment(); + decl_comment_input->buffer()->text(c?c:""); + decl_panel->show(); + const char* message = nullptr; + for (;;) { // repeat as long as there are errors + // event loop + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == decl_panel_cancel) goto BREAK2; + else if (w == decl_panel_ok) break; + else if (!w) Fl::wait(); + } + // check values + const char*c = decl_input->buffer()->text(); + while (isspace(*c)) c++; + message = c_check(c&&c[0]=='#' ? c+1 : c); + // alert user + if (message) { + int v = fl_choice("Potential syntax error detected: %s", + "Continue Editing", "Ignore Error", nullptr, message); + if (v==0) continue; // Continue Editing + //if (v==1) { } // Ignore Error and close dialog + } + // copy vlaues + name(c); + if (is_in_class()) { + if (public_!=decl_class_choice->value()) { + Fluid.proj.set_modflag(1); + public_ = decl_class_choice->value(); + } + } else { + if (public_!=(decl_choice->value()&1)) { + Fluid.proj.set_modflag(1); + public_ = (decl_choice->value()&1); + } + if (static_!=((decl_choice->value()>>1)&1)) { + Fluid.proj.set_modflag(1); + static_ = ((decl_choice->value()>>1)&1); + } + } + c = decl_comment_input->buffer()->text(); + if (c && *c) { + if (!comment() || strcmp(c, comment())) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(c); + } else { + if (comment()) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(nullptr); + } + if (c) free((void*)c); + break; + } +BREAK2: + decl_panel->hide(); +} + +/** + Write the code to the source and header files. + \todo There are a lot of side effect in this node depending on the given text + and the parent node. They need to be understood and documented. + */ +void Decl_Node::write_code1(fld::io::Code_Writer& f) { + const char* c = name(); + if (!c) return; + // handle a few keywords differently if inside a class + if (is_in_class() && ( (!strncmp(c,"class",5) && isspace(c[5])) + || (!strncmp(c,"typedef",7) && isspace(c[7])) + || (!strncmp(c,"FL_EXPORT",9) && isspace(c[9])) + || (!strncmp(c,"struct",6) && isspace(c[6])) + || (!strncmp(c,"enum",4) && isspace(c[4])) + ) ) { + f.write_public(public_); + write_comment_h(f, f.indent(1)); + f.write_h("%s%s\n", f.indent(1), c); + return; + } + // handle putting #include, extern, using or typedef into decl: + if ( (!isalpha(*c) && *c != '~') + || (!strncmp(c,"extern",6) && isspace(c[6])) + || (!strncmp(c,"class",5) && isspace(c[5])) + || (!strncmp(c,"typedef",7) && isspace(c[7])) + || (!strncmp(c,"using",5) && isspace(c[5])) + || (!strncmp(c,"FL_EXPORT",9) && isspace(c[9])) + // || !strncmp(c,"struct",6) && isspace(c[6]) + ) { + if (public_) { + write_comment_h(f); + f.write_h("%s\n", c); + } else { + write_comment_c(f); + f.write_c("%s\n", c); + } + return; + } + // find the first C++ style comment + const char* e = c+strlen(c), *csc = c; + while (cscc && e[-1]==' ') e--; + if (class_name(1)) { + f.write_public(public_); + write_comment_h(f, f.indent(1)); + f.write_hc(f.indent(1), int(e-c), c, csc); + } else { + if (public_) { + if (static_) + f.write_h("extern "); + else + write_comment_h(f); + f.write_hc("", int(e-c), c, csc); + + if (static_) { + write_comment_c(f); + f.write_cc("", int(e-c), c, csc); + } + } else { + write_comment_c(f); + if (static_) + f.write_c("static "); + f.write_cc("", int(e-c), c, csc); + } + } +} + +// ---- Data_Node declaration + +/** \class Data_Node + Manage data from an external arbitrary file. + + The content of the file will be stored in binary inside the generated + code. This can be used to store images inline in the source code, + */ + +/// Prototype for a data node to be used by the factory. +Data_Node Data_Node::prototype; + +/** + Constructor. + */ +Data_Node::Data_Node() : + Decl_Node(), + filename_(nullptr), + text_mode_(0) +{ } + +/** + Destructor. + */ +Data_Node::~Data_Node() { + if (filename_) + free((void*)filename_); +} + +/** + Create an empty inline data node. + \param[in] strategy add after current or as last child + \return new inline data node + */ +Node *Data_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_decl_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + Data_Node *o = new Data_Node(); + o->public_ = 1; + o->static_ = 1; + o->filename_ = nullptr; + o->text_mode_ = 0; + o->name("myInlineData"); + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Write additional properties. + - "filename" followed by the filename of the file to inline + - "textmode" if data is written in ASCII vs. binary + */ +void Data_Node::write_properties(fld::io::Project_Writer &f) { + Decl_Node::write_properties(f); + if (filename_) { + f.write_string("filename"); + f.write_word(filename_); + } + if (text_mode_ == 1) { + f.write_string("textmode"); + } + if (text_mode_ == 2) { + f.write_string("compressed"); + } +} + +/** + Read specific properties. + */ +void Data_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"filename")) { + storestring(f.read_word(), filename_, 1); + } else if (!strcmp(c,"textmode")) { + text_mode_ = 1; + } else if (!strcmp(c,"compressed")) { + text_mode_ = 2; + } else { + Decl_Node::read_property(f, c); + } +} + +/** + Open the data_panel to edit this node. + */ +void Data_Node::open() { + if (!data_panel) make_data_panel(); + data_input->value(name()); + if (is_in_class()) { + data_class_choice->value(public_); + data_class_choice->show(); + data_choice->hide(); + } else { + data_choice->value((public_&1)|((static_&1)<<1)); + data_choice->show(); + data_class_choice->hide(); + } + data_mode->value(text_mode_); + data_filename->value(filename_?filename_:""); + const char *c = comment(); + data_comment_input->buffer()->text(c?c:""); + data_panel->show(); + for (;;) { // repeat as long as there are errors + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == data_panel_cancel) goto BREAK2; + else if (w == data_panel_ok) break; + else if (w == data_filebrowser) { + Fluid.proj.enter_project_dir(); + const char *fn = fl_file_chooser("Load Inline Data", nullptr, data_filename->value(), 1); + Fluid.proj.leave_project_dir(); + if (fn) { + if (strcmp(fn, data_filename->value())) + Fluid.proj.set_modflag(1); + data_filename->value(fn); + } + } + else if (!w) Fl::wait(); + } + // store the variable name: + const char*c = data_input->value(); + char *s = fl_strdup(c), *p = s, *q, *n; + for (;;++p) { // remove leading spaces + if (!isspace((unsigned char)(*p))) break; + } + n = p; + if ( (!isalpha((unsigned char)(*p))) && ((*p)!='_') && ((*p)!=':') ) goto OOPS; + ++p; + for (;;++p) { + if ( (!isalnum((unsigned char)(*p))) && ((*p)!='_') && ((*p)!=':') ) break; + } + q = p; + for (;;++q) { + if (!*q) break; + if (!isspace((unsigned char)(*q))) goto OOPS; + } + *p = 0; // remove trailing spaces + if (n==q) { + OOPS: + int v = fl_choice("%s", + "Continue Editing", "Ignore Error", nullptr, + "Variable name must be a C identifier"); + if (v==0) { free(s); continue; } // Continue Editing + //if (v==1) { } // Ignore Error and close dialog + } + Fluid.proj.undo.checkpoint(); + name(n); + free(s); + // store flags + if (is_in_class()) { + if (public_!=data_class_choice->value()) { + Fluid.proj.set_modflag(1); + public_ = data_class_choice->value(); + } + } else { + if (public_!=(data_choice->value()&1)) { + Fluid.proj.set_modflag(1); + public_ = (data_choice->value()&1); + } + if (static_!=((data_choice->value()>>1)&1)) { + Fluid.proj.set_modflag(1); + static_ = ((data_choice->value()>>1)&1); + } + } + text_mode_ = data_mode->value(); + if (text_mode_ < 0) text_mode_ = 0; + if (text_mode_ > 2) text_mode_ = 2; + // store the filename + c = data_filename->value(); + if (filename_ && strcmp(filename_, data_filename->value())) + Fluid.proj.set_modflag(1); + else if (!filename_ && *c) + Fluid.proj.set_modflag(1); + if (filename_) { free((void*)filename_); filename_ = nullptr; } + if (c && *c) filename_ = fl_strdup(c); + // store the comment + c = data_comment_input->buffer()->text(); + if (c && *c) { + if (!comment() || strcmp(c, comment())) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(c); + } else { + if (comment()) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(nullptr); + } + if (c) free((void*)c); + Fluid.proj.set_modflag(1); + break; + } +BREAK2: + data_panel->hide(); +} + +/** + Write the content of the external file inline into the source code. + */ +void Data_Node::write_code1(fld::io::Code_Writer& f) { + const char *message = nullptr; + const char *c = name(); + if (!c) return; + const char *fn = filename_; + char *data = nullptr; + int nData = -1; + int uncompressedDataSize = 0; + // path should be set correctly already + if (filename_ && !f.write_codeview) { + Fluid.proj.enter_project_dir(); + FILE *f = fl_fopen(filename_, "rb"); + Fluid.proj.leave_project_dir(); + if (!f) { + message = "Can't include data from file. Can't open"; + } else { + fseek(f, 0, SEEK_END); + nData = (int)ftell(f); + fseek(f, 0, SEEK_SET); + if (nData) { + data = (char*)calloc(nData, 1); + if (fread(data, nData, 1, f)==0) { /* use default */ } + if (text_mode_ == 2) { + uncompressedDataSize = nData; + uLong nzData = compressBound(nData); + Bytef *zdata = (Bytef*)::malloc(nzData); + if (compress(zdata, &nzData, (Bytef*)data, nData) != Z_OK) { /* error */ } + ::free(data); + data = (char*)zdata; + nData = (int)nzData; + } + } + fclose(f); + } + } else { + fn = filename_ ? filename_ : ""; + } + if (is_in_class()) { + f.write_public(public_); + if (text_mode_ == 1) { + f.write_h("%sstatic const char *%s;\n", f.indent(1), c); + f.write_c("\n"); + write_comment_c(f); + f.write_c("const char *%s::%s = /* text inlined from %s */\n", class_name(1), c, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cstring(data, nData); + } else if (text_mode_ == 2) { + f.write_h("%sstatic int %s_size;\n", f.indent(1), c); + f.write_h("%sstatic unsigned char %s[%d];\n", f.indent(1), c, nData); + f.write_c("\n"); + write_comment_c(f); + f.write_c("int %s::%s_size = %d;\n", class_name(1), c, uncompressedDataSize); + f.write_c("unsigned char %s::%s[%d] = /* data compressed and inlined from %s */\n", class_name(1), c, nData, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cdata(data, nData); + } else { + f.write_h("%sstatic unsigned char %s[%d];\n", f.indent(1), c, nData); + f.write_c("\n"); + write_comment_c(f); + f.write_c("unsigned char %s::%s[%d] = /* data inlined from %s */\n", class_name(1), c, nData, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cdata(data, nData); + } + f.write_c(";\n"); + } else { + // the "header only" option does not apply here! + if (public_) { + if (static_) { + if (text_mode_ == 1) { + f.write_h("extern const char *%s;\n", c); + f.write_c("\n"); + write_comment_c(f); + f.write_c("const char *%s = /* text inlined from %s */\n", c, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cstring(data, nData); + } else if (text_mode_ == 2) { + f.write_h("extern int %s_size;\n", c); + f.write_h("extern unsigned char %s[%d];\n", c, nData); + f.write_c("\n"); + write_comment_c(f); + f.write_c("int %s_size = %d;\n", c, uncompressedDataSize); + f.write_c("unsigned char %s[%d] = /* data compressed and inlined from %s */\n", c, nData, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cdata(data, nData); + } else { + f.write_h("extern unsigned char %s[%d];\n", c, nData); + f.write_c("\n"); + write_comment_c(f); + f.write_c("unsigned char %s[%d] = /* data inlined from %s */\n", c, nData, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cdata(data, nData); + } + f.write_c(";\n"); + } else { + write_comment_h(f); + f.write_h("#error Unsupported declaration loading inline data %s\n", fn); + if (text_mode_ == 1) + f.write_h("const char *%s = \"abc...\";\n", c); + else + f.write_h("unsigned char %s[3] = { 1, 2, 3 };\n", c); + } + } else { + f.write_c("\n"); + write_comment_c(f); + if (static_) + f.write_c("static "); + if (text_mode_ == 1) { + f.write_c("const char *%s = /* text inlined from %s */\n", c, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cstring(data, nData); + } else if (text_mode_ == 2) { + f.write_c("int %s_size = %d;\n", c, uncompressedDataSize); + if (static_) f.write_c("static "); + f.write_c("unsigned char %s[%d] = /* data compressed and inlined from %s */\n", c, nData, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cdata(data, nData); + } else { + f.write_c("unsigned char %s[%d] = /* data inlined from %s */\n", c, nData, fn); + if (message) f.write_c("#error %s %s\n", message, fn); + f.write_cdata(data, nData); + } + f.write_c(";\n"); + } + } + // if we are in interactive mode, we pop up a warning dialog + // giving the error: (Fluid.batch_mode && !write_codeview) ??? + if (message && !f.write_codeview) { + if (Fluid.batch_mode) + fprintf(stderr, "FLUID ERROR: %s %s\n", message, fn); + else + fl_alert("%s\n%s\n", message, fn); + } + if (data) free(data); +} + +// ---- DeclBlock_Node declaration + +/** \class DeclBlock_Node + Manage a declaration block. + + Declaration blocks have two text field that are written before and after + the children of this block. This block is located at the top level and + is written to the source file, and to the header file, if declared public. + */ + +/// Prototype for a declaration block to be used by the factory. +DeclBlock_Node DeclBlock_Node::prototype; + +/** + Constructor. + */ +DeclBlock_Node::DeclBlock_Node() : + Node(), + after(nullptr), + write_map_(CODE_IN_SOURCE) +{ } + +/** + Destructor. + */ +DeclBlock_Node::~DeclBlock_Node() { + if (after) + ::free((void*)after); +} + +/** + Return 1 if this block is public. + */ +int DeclBlock_Node::is_public() const { + return ((write_map_&CODE_IN_HEADER) != 0); +} + +/** + Create a new declaration block. + \param[in] strategy add after current or as last child + \return new Declaration Block node + */ +Node *DeclBlock_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) p = p->parent; + while (p && !p->is_decl_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + DeclBlock_Node *o = new DeclBlock_Node(); + o->name("#if 1"); + o->write_map_ = CODE_IN_SOURCE; + o->after = fl_strdup("#endif"); + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Write the specific properties. + - "public"/"protected" + - "after" followed by the second code block. + */ +void DeclBlock_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + // deprecated + if (is_public()) f.write_string("public"); + // new way to map declaration block to various parts of the generated code + if (write_map_ != CODE_IN_SOURCE) + f.write_string("map %d", write_map_); + f.write_string("after"); + f.write_word(after); +} + +/** + Read the specific properties. + */ +void DeclBlock_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if(!strcmp(c,"public")) { + write_map_ |= CODE_IN_HEADER; + } else if(!strcmp(c,"protected")) { + // + } else if(!strcmp(c,"map")) { + write_map_ = (int)atol(f.read_word()); + } else if (!strcmp(c,"after")) { + storestring(f.read_word(),after); + } else { + Node::read_property(f, c); + } +} + +/** + Open the declblock_panel to edit this node. + */ +void DeclBlock_Node::open() { + // build dialog box + if (!declblock_panel) make_declblock_panel(); + // preset all values + declblock_before_input->value(name()); + declblock_after_input->value(after); + declblock_static_header->value(write_map_ & STATIC_IN_HEADER); + declblock_static_source->value(write_map_ & STATIC_IN_SOURCE); + declblock_code_header->value(write_map_ & CODE_IN_HEADER); + declblock_code_source->value(write_map_ & CODE_IN_SOURCE); + const char *c = comment(); + declblock_comment_input->buffer()->text(c?c:""); + // show modal dialog and loop until satisfied + declblock_panel->show(); + const char* message = nullptr; + for (;;) { // repeat as long as there are errors + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == declblock_panel_cancel) goto BREAK2; + else if (w == declblock_panel_ok) break; + else if (!w) Fl::wait(); + } + // verify user input + const char* a = declblock_before_input->value(); + while (isspace(*a)) a++; + const char* b = declblock_after_input->value(); + while (isspace(*b)) b++; + message = c_check(a&&a[0]=='#' ? a+1 : a); + if (!message) + message = c_check(b&&b[0]=='#' ? b+1 : b); + if (message) { + int v = fl_choice("Potential syntax error detected: %s", + "Continue Editing", "Ignore Error", nullptr, message); + if (v==0) continue; // Continue Editing + //if (v==1) { } // Ignore Error and close dialog + } + // store user choices in data structure + name(a); + storestring(b, after); + if (write_map_ & STATIC_IN_HEADER) { + if (declblock_static_header->value()==0) { + write_map_ &= ~STATIC_IN_HEADER; + Fluid.proj.set_modflag(1); + } + } else { + if (declblock_static_header->value()) { + write_map_ |= STATIC_IN_HEADER; + Fluid.proj.set_modflag(1); + } + } + if (write_map_ & STATIC_IN_SOURCE) { + if (declblock_static_source->value()==0) { + write_map_ &= ~STATIC_IN_SOURCE; + Fluid.proj.set_modflag(1); + } + } else { + if (declblock_static_source->value()) { + write_map_ |= STATIC_IN_SOURCE; + Fluid.proj.set_modflag(1); + } + } + if (write_map_ & CODE_IN_HEADER) { + if (declblock_code_header->value()==0) { + write_map_ &= ~CODE_IN_HEADER; + Fluid.proj.set_modflag(1); + } + } else { + if (declblock_code_header->value()) { + write_map_ |= CODE_IN_HEADER; + Fluid.proj.set_modflag(1); + } + } + if (write_map_ & CODE_IN_SOURCE) { + if (declblock_code_source->value()==0) { + write_map_ &= ~CODE_IN_SOURCE; + Fluid.proj.set_modflag(1); + } + } else { + if (declblock_code_source->value()) { + write_map_ |= CODE_IN_SOURCE; + Fluid.proj.set_modflag(1); + } + } + c = declblock_comment_input->buffer()->text(); + if (c && *c) { + if (!comment() || strcmp(c, comment())) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(c); + } else { + if (comment()) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(nullptr); + } + if (c) free((void*)c); + break; + } +BREAK2: + declblock_panel->hide(); +} + +/** + Write the \b before static code to the source file, and to the header file if declared public. + The before code is stored in the name() field. + */ +void DeclBlock_Node::write_static(fld::io::Code_Writer& f) { + const char* c = name(); + if (c && *c) { + if (write_map_ & STATIC_IN_HEADER) + f.write_h("%s\n", c); + if (write_map_ & STATIC_IN_SOURCE) + f.write_c("%s\n", c); + } +} + +/** + Write the \b after static code to the source file, and to the header file if declared public. + */ +void DeclBlock_Node::write_static_after(fld::io::Code_Writer& f) { + const char* c = after; + if (c && *c) { + if (write_map_ & STATIC_IN_HEADER) + f.write_h("%s\n", c); + if (write_map_ & STATIC_IN_SOURCE) + f.write_c("%s\n", c); + } +} + +/** + Write the \b before code to the source file, and to the header file if declared public. + The before code is stored in the name() field. + */ +void DeclBlock_Node::write_code1(fld::io::Code_Writer& f) { + const char* c = name(); + if (c && *c) { + if (write_map_ & CODE_IN_HEADER) + f.write_h("%s\n", c); + if (write_map_ & CODE_IN_SOURCE) + f.write_c("%s\n", c); + } +} + +/** + Write the \b after code to the source file, and to the header file if declared public. + */ +void DeclBlock_Node::write_code2(fld::io::Code_Writer& f) { + const char* c = after; + if (c && *c) { + if (write_map_ & CODE_IN_HEADER) + f.write_h("%s\n", c); + if (write_map_ & CODE_IN_SOURCE) + f.write_c("%s\n", c); + } +} + +// ---- Comment_Node declaration + +/** \class Comment_Node + Manage a comment node. + + The comment field takes one or more lines of ASCII text. If the text starts + with a '/' and a '*', Fluid assumes that the text is already formatted. If not, + every line will be preceded with "// ". + */ + +/// Prototype for a comment node to be used by the factory. +Comment_Node Comment_Node::prototype; + +/** + Constructor. + */ +Comment_Node::Comment_Node() : + in_c_(1), + in_h_(1), + style_(0) +{ } + +/** + Make a new comment node. + \param[in] strategy add after current or as last child + \return new Comment node + */ +Node *Comment_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_code_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + Comment_Node *o = new Comment_Node(); + o->in_c_ = 1; + o->in_h_ = 1; + o->style_ = 0; + o->name("my comment"); + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Write respective properties. + - "in_source"/"not_in_source" if the comment will be written to the source code + - "in_header"/"not_in_header" if the comment will be written to the header file + */ +void Comment_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + if (in_c_) f.write_string("in_source"); else f.write_string("not_in_source"); + if (in_h_) f.write_string("in_header"); else f.write_string("not_in_header"); +} + +/** + Read extra properties. + */ +void Comment_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"in_source")) { + in_c_ = 1; + } else if (!strcmp(c,"not_in_source")) { + in_c_ = 0; + } else if (!strcmp(c,"in_header")) { + in_h_ = 1; + } else if (!strcmp(c,"not_in_header")) { + in_h_ = 0; + } else { + Node::read_property(f, c); + } +} + +/** + Load available preset comments. + Fluid comes with GPL and LGPL preset for comments. Users can + add their own presets which are stored per user in a separate + preferences database. + */ +static void load_comments_preset(Fl_Preferences &menu) { + static const char * const predefined_comment[] = { + "GNU Public License v3/GPL Header", "GNU Public License v3/GPL Footer", + "GNU Public License v3/LGPL Header", "GNU Public License v3/LGPL Footer", + "FLTK/Header" }; + int i, n; + menu.get("n", n, -1); + if (n == -1) menu.set("n", 5); + menu.set("version", 10400); + Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); + for (i=0; i<5; i++) { + menu.set(Fl_Preferences::Name(i), predefined_comment[i]); + db.set(predefined_comment[i], comment_text[i]); + } +} + +/** + Open the comment_panel to edit this node. + */ +void Comment_Node::open() { + if (!comment_panel) make_comment_panel(); + const char *text = name(); + { + int i=0, n=0, version = 0; + Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu"); + comment_predefined->clear(); + comment_predefined->add("_Edit/Add current comment..."); + comment_predefined->add("_Edit/Remove last selection..."); + menu.get("version", version, -1); + if (version < 10400) load_comments_preset(menu); + menu.get("n", n, 0); + for (i=0;iadd(text); + free(text); + } + } + comment_input->buffer()->text( text ? text : "" ); + comment_in_source->value(in_c_); + comment_in_header->value(in_h_); + comment_panel->show(); + char itempath[FL_PATH_MAX]; itempath[0] = 0; + int last_selected_item = 0; + for (;;) { // repeat as long as there are errors + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == comment_panel_cancel) goto BREAK2; + else if (w == comment_panel_ok) break; + else if (w == comment_predefined) { + if (comment_predefined->value()==1) { + // add the current comment to the database + const char *xname = fl_input( + "Please enter a name to reference the current\ncomment in your database.\n\n" + "Use forward slashes '/' to create submenus.", + "My Comment"); + if (xname) { + char *name = fl_strdup(xname); + for (char*s=name;*s;s++) if (*s==':') *s = ';'; + int n; + Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); + db.set(name, comment_input->buffer()->text()); + Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu"); + menu.get("n", n, 0); + menu.set(Fl_Preferences::Name(n), name); + menu.set("n", ++n); + comment_predefined->add(name); + free(name); + } + } else if (comment_predefined->value()==2) { + // remove the last selected comment from the database + if (itempath[0]==0 || last_selected_item==0) { + fl_message("Please select an entry from this menu first."); + } else if (fl_choice("Are you sure that you want to delete the entry\n" + "\"%s\"\nfrom the database?", "Cancel", "Delete", + nullptr, itempath)) { + Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); + db.deleteEntry(itempath); + comment_predefined->remove(last_selected_item); + Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu"); + int i, n; + for (i=4, n=0; isize(); i++) { + const Fl_Menu_Item *mi = comment_predefined->menu()+i; + if (comment_predefined->item_pathname(itempath, 255, mi)==0) { + if (itempath[0]=='/') memmove(itempath, itempath+1, 255); + if (itempath[0]) menu.set(Fl_Preferences::Name(n++), itempath); + } + } + menu.set("n", n); + } + } else { + // load the selected comment from the database + if (comment_predefined->item_pathname(itempath, 255)==0) { + if (itempath[0]=='/') memmove(itempath, itempath+1, 255); + Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments"); + char *text; + db.get(itempath, text, "(no text found in data base)"); + comment_input->buffer()->text(text); + free(text); + last_selected_item = comment_predefined->value(); + } + } + } + else if (w == comment_load) { + // load a comment from disk + fl_file_chooser_ok_label("Use File"); + const char *fname = fl_file_chooser("Pick a comment", nullptr, nullptr); + fl_file_chooser_ok_label(nullptr); + if (fname) { + if (comment_input->buffer()->loadfile(fname)) { + fl_alert("Error loading file\n%s", fname); + } + } + } + else if (!w) Fl::wait(); + } + char*c = comment_input->buffer()->text(); + name(c); + free(c); + int mod = 0; + if (in_c_ != comment_in_source->value()) { + in_c_ = comment_in_source->value(); + mod = 1; + } + if (in_h_ != comment_in_header->value()) { + in_h_ = comment_in_header->value(); + mod = 1; + } + if (mod) Fluid.proj.set_modflag(1); + break; + } +BREAK2: + comment_panel->hide(); +} + +/** + Write the comment to the files. + */ +void Comment_Node::write_code1(fld::io::Code_Writer& f) { + const char* c = name(); + if (!c) return; + if (!in_c_ && !in_h_) return; + // find out if there is already a valid comment: + const char *s = c; + while (isspace(*s)) s++; + // if this seems to be a C style comment, copy the block as is + // (it's up to the user to correctly close the comment) + if (s[0]=='/' && s[1]=='*') { + if (in_h_) f.write_h("%s\n", c); + if (in_c_) f.write_c("%s\n", c); + return; + } + // copy the comment line by line, add the double slash if needed + char *txt = fl_strdup(c); + char *b = txt, *e = txt; + for (;;) { + // find the end of the line and set it to NUL + while (*e && *e!='\n') e++; + char eol = *e; + *e = 0; + // check if there is a C++ style comment at the beginning of the line + char *s = b; + while (isspace(*s)) s++; + if (s!=e && ( s[0]!='/' || s[1]!='/') ) { + // if no comment marker was found, we add one ourselves + if (in_h_) f.write_h("// "); + if (in_c_) f.write_c("// "); + } + // now copy the rest of the line + if (in_h_) f.write_h("%s\n", b); + if (in_c_) f.write_c("%s\n", b); + if (eol==0) break; + *e++ = eol; + b = e; + } + free(txt); +} + +// ---- Class_Node declaration + +/** \class Class_Node + Manage a class declaration and implementation. + */ + +/// Prototype for a class node to be used by the factory. +Class_Node Class_Node::prototype; + +/** + Constructor. + */ +Class_Node::Class_Node() : + Node(), + subclass_of(nullptr), + public_(1), + class_prefix(nullptr) +{ } + +/** + Destructor. + */ +Class_Node::~Class_Node() { + if (subclass_of) + free((void*)subclass_of); + if (class_prefix) + free((void*)class_prefix); +} + +/** + Return 1 if this class is marked public. + */ +int Class_Node::is_public() const { + return public_; +} + +/** + Set the prefixx string. + */ +void Class_Node::prefix(const char*p) { + free((void*) class_prefix); + class_prefix=fl_strdup(p ? p : "" ); +} + +/** + Make a new class node. + \param[in] strategy add after current or as last child + \return new Class node + */ +Node *Class_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !p->is_decl_block()) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + Class_Node *o = new Class_Node(); + o->name("UserInterface"); + o->class_prefix = nullptr; + o->subclass_of = nullptr; + o->public_ = 1; + o->add(anchor, strategy); + o->factory = this; + return o; +} + +/** + Write the respective properties. + - ":" followed by the super class + - "private"/"protected" + */ +void Class_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + if (subclass_of) { + f.write_string(":"); + f.write_word(subclass_of); + } + switch (public_) { + case 0: f.write_string("private"); break; + case 2: f.write_string("protected"); break; + } +} + +/** + Read additional properties. + */ +void Class_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"private")) { + public_ = 0; + } else if (!strcmp(c,"protected")) { + public_ = 2; + } else if (!strcmp(c,":")) { + storestring(f.read_word(), subclass_of); + } else { + Node::read_property(f, c); + } +} + +/** + Open the class_panel to edit the class name and superclass name. + */ +void Class_Node::open() { + if (!class_panel) make_class_panel(); + char fullname[FL_PATH_MAX]=""; + if (prefix() && strlen(prefix())) + sprintf(fullname,"%s %s",prefix(),name()); + else + strcpy(fullname, name()); + c_name_input->value(fullname); + c_subclass_input->value(subclass_of); + c_public_button->value(public_); + const char *c = comment(); + c_comment_input->buffer()->text(c?c:""); + class_panel->show(); + const char* message = nullptr; + + char *na=nullptr,*pr=nullptr,*p=nullptr; // name and prefix substrings + + for (;;) { // repeat as long as there are errors + // we don;t give the option to ignore this error here because code depends + // on this being a C++ identifier + if (message) fl_alert("%s", message); + for (;;) { + Fl_Widget* w = Fl::readqueue(); + if (w == c_panel_cancel) goto BREAK2; + else if (w == c_panel_ok) break; + else if (!w) Fl::wait(); + } + const char*c = c_name_input->value(); + char *s = fl_strdup(c); + size_t len = strlen(s); + if (!*s) goto OOPS; + p = (char*) (s+len-1); + while (p>=s && isspace(*p)) *(p--)='\0'; + if (p=s && is_id(*p)) p--; + if ( (ps) *p--='\0'; + while (p>=s && isspace(*p)) *(p--)='\0'; + while (p>=s && is_id(*p)) p--; + if (pvalue(); + message = c_check(c); + if (message) { free((void*)s);continue;} + name(na); + prefix(pr); + free((void*)s); + storestring(c, subclass_of); + if (public_ != c_public_button->value()) { + public_ = c_public_button->value(); + Fluid.proj.set_modflag(1); + } + c = c_comment_input->buffer()->text(); + if (c && *c) { + if (!comment() || strcmp(c, comment())) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(c); + } else { + if (comment()) { Fluid.proj.set_modflag(1); redraw_browser(); } + comment(nullptr); + } + if (c) free((void*)c); + break; + } +BREAK2: + class_panel->hide(); +} + +/** + Write the header code that declares this class. + */ +void Class_Node::write_code1(fld::io::Code_Writer& f) { + parent_class = current_class; + current_class = this; + write_public_state = 0; + f.write_h("\n"); + write_comment_h(f); + if (prefix() && strlen(prefix())) + f.write_h("class %s %s ", prefix(), name()); + else + f.write_h("class %s ", name()); + if (subclass_of) f.write_h(": %s ", subclass_of); + f.write_h("{\n"); +} + +/** + Write the header code that ends the declaration of this class. + */ +void Class_Node::write_code2(fld::io::Code_Writer& f) { + f.write_h("};\n"); + current_class = parent_class; +} + +/** + Return 1 if this class contains a function with the given signature. + */ +int Node::has_function(const char *rtype, const char *sig) const { + Node *child; + for (child = next; child && child->level > level; child = child->next) { + if (child->level == level+1 && child->is_a(Type::Function)) { + const Function_Node *fn = (const Function_Node*)child; + if (fn->has_signature(rtype, sig)) + return 1; + } + } + return 0; +} diff --git a/fluid/nodes/Function_Node.h b/fluid/nodes/Function_Node.h new file mode 100644 index 000000000..ac8bcdcc2 --- /dev/null +++ b/fluid/nodes/Function_Node.h @@ -0,0 +1,277 @@ +// +// C function Node header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#ifndef FLUID_NODES_FUNCTION_NODE_H +#define FLUID_NODES_FUNCTION_NODE_H + +#include "nodes/Node.h" + +#include "app/Image_Asset.h" +#ifdef _WIN32 +#include "tools/ExternalCodeEditor_WIN32.h" +#else +#include "tools/ExternalCodeEditor_UNIX.h" +#endif + +#include +#include +#include +#include + +#include +#include + +extern class Class_Node *current_class; + +int has_toplevel_function(const char *rtype, const char *sig); + +const char *c_check(const char *c, int type = 0); + +// ---- Function_Node declaration + +class Function_Node : public Node +{ +public: + typedef Node super; + static Function_Node prototype; +private: + const char* return_type; + char public_, cdecl_, constructor, havewidgets; +public: + Function_Node(); + ~Function_Node(); + Node *make(Strategy strategy) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + void open() override; + int ismain() {return name_ == nullptr;} + const char *type_name() override {return "Function";} + const char *title() override { + return name() ? name() : "main()"; + } + int can_have_children() const override {return 1;} + int is_code_block() const override {return 1;} + int is_public() const override; + Type type() const override { return Type::Function; } + bool is_a(Type inType) const override { return (inType==Type::Function) ? true : super::is_a(inType); } + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + int has_signature(const char *, const char*) const; +}; + +// ---- Code_Node declaration + +class Code_Node : public Node +{ +public: + typedef Node super; + static Code_Node prototype; +private: + ExternalCodeEditor editor_; + int cursor_position_; + int code_input_scroll_row; + int code_input_scroll_col; +public: + Code_Node(); + Node *make(Strategy strategy) override; + void write(fld::io::Project_Writer &f) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override { } + void open() override; + const char *type_name() override {return "code";} + int is_code_block() const override {return 0;} + Type type() const override { return Type::Code; } + bool is_a(Type inType) const override { return (inType==Type::Code) ? true : super::is_a(inType); } + int is_public() const override { return -1; } + int is_editing(); + int reap_editor(); + int handle_editor_changes(); +}; + +// ---- CodeBlock_Node declaration + +class CodeBlock_Node : public Node +{ +public: + typedef Node super; + static CodeBlock_Node prototype; +private: + const char* after; +public: + CodeBlock_Node(); + ~CodeBlock_Node(); + Node *make(Strategy strategy) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + void open() override; + const char *type_name() override {return "codeblock";} + int is_code_block() const override {return 1;} + int can_have_children() const override {return 1;} + int is_public() const override { return -1; } + Type type() const override { return Type::CodeBlock; } + bool is_a(Type inType) const override { return (inType==Type::CodeBlock) ? true : super::is_a(inType); } + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; +}; + +// ---- Decl_Node declaration + +class Decl_Node : public Node +{ +public: + typedef Node super; + static Decl_Node prototype; +protected: + char public_; + char static_; + +public: + Decl_Node(); + Node *make(Strategy strategy) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override { } + void open() override; + const char *type_name() override {return "decl";} + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + int is_public() const override; + Type type() const override { return Type::Decl; } + bool is_a(Type inType) const override { return (inType==Type::Decl) ? true : super::is_a(inType); } +}; + +// ---- Data_Node declaration + +class Data_Node : public Decl_Node +{ +public: + typedef Decl_Node super; + static Data_Node prototype; +private: + const char *filename_; + int text_mode_; + +public: + Data_Node(); + ~Data_Node(); + Node *make(Strategy strategy) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override {} + void open() override; + const char *type_name() override {return "data";} + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + Type type() const override { return Type::Data; } + bool is_a(Type inType) const override { return (inType==Type::Data) ? true : super::is_a(inType); } +}; + +// ---- DeclBlock_Node declaration + +class DeclBlock_Node : public Node +{ +public: + typedef Node super; + static DeclBlock_Node prototype; +private: + enum { + CODE_IN_HEADER = 1, + CODE_IN_SOURCE = 2, + STATIC_IN_HEADER = 4, + STATIC_IN_SOURCE = 8 + }; + const char* after; ///< code after all children of this block + int write_map_; ///< see enum above + +public: + DeclBlock_Node(); + ~DeclBlock_Node(); + Node *make(Strategy strategy) override; + void write_static(fld::io::Code_Writer& f) override; + void write_static_after(fld::io::Code_Writer& f) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + void open() override; + const char *type_name() override {return "declblock";} + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + int can_have_children() const override {return 1;} + int is_decl_block() const override {return 1;} + int is_public() const override; + Type type() const override { return Type::DeclBlock; } + bool is_a(Type inType) const override { return (inType==Type::DeclBlock) ? true : super::is_a(inType); } +}; + +// ---- Comment_Node declaration + +class Comment_Node : public Node +{ +public: + typedef Node super; + static Comment_Node prototype; +private: + char in_c_, in_h_, style_; + +public: + Comment_Node(); + Node *make(Strategy strategy) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override { } + void open() override; + const char *type_name() override {return "comment";} + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + int is_public() const override { return 1; } + Type type() const override { return Type::Comment; } + bool is_a(Type inType) const override { return (inType==Type::Comment) ? true : super::is_a(inType); } +}; + +// ---- Class_Node declaration + +class Class_Node : public Node +{ +public: + typedef Node super; + static Class_Node prototype; +private: + const char* subclass_of; + char public_; + const char* class_prefix; +public: + Class_Node(); + ~Class_Node(); + // state variables for output: + char write_public_state; // true when public: has been printed + Class_Node* parent_class; // save class if nested +// + Node *make(Strategy strategy) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + void open() override; + const char *type_name() override {return "class";} + int can_have_children() const override {return 1;} + int is_decl_block() const override {return 1;} + int is_class() const override {return 1;} + int is_public() const override; + Type type() const override { return Type::Class; } + bool is_a(Type inType) const override { return (inType==Type::Class) ? true : super::is_a(inType); } + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + + // class prefix attribute access + void prefix(const char* p); + const char* prefix() const {return class_prefix;} +}; + +#endif // FLUID_NODES_FUNCTION_NODE_H diff --git a/fluid/nodes/Grid_Node.cxx b/fluid/nodes/Grid_Node.cxx new file mode 100644 index 000000000..06f6d4166 --- /dev/null +++ b/fluid/nodes/Grid_Node.cxx @@ -0,0 +1,775 @@ +// +// Grid Node code for the Fast Light Tool Kit (FLTK). +// +// Copyright 2023-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#include "nodes/Grid_Node.h" + +#include "Fluid.h" +#include "app/Snap_Action.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "widgets/Node_Browser.h" +#include "widgets/Formula_Input.h" + +#include +#include +#include +#include +#include "../src/flstring.h" + +#include +#include +#include + +// TODO: better grid overlay? +// TODO: grid_child_cb should move all selected cells, not just the current_selected. +// TODO: buttons to add and delete rows and columns in the widget dialog +// TODO: ways to resize rows and columns, add and delete them in the project window, pulldown menu? +// TODO: alignment can be FL_GRID_LEFT|FL_GRID_VERTICAL? + +// ---- Fl_Grid_Proxy --------------------------------------------------- MARK: - + +/** + An implementation of the Fl_Grid widget with additional functionality. + + Fl_Grid_Proxy add a list of transient children, i.e. children that are + temporarily assigned to a cell that is already taken by another child. + */ +Fl_Grid_Proxy::Fl_Grid_Proxy(int X,int Y,int W,int H) +: Fl_Grid(X,Y,W,H), + transient_(nullptr), + num_transient_(0), + cap_transient_(0) +{ +} + +Fl_Grid_Proxy::~Fl_Grid_Proxy() { + int i; + if (transient_) { + for (i=0; i 0) { + Fl_Grid::resize(X, Y, W, H); + } else { + Fl_Widget::resize(X, Y, W, H); + } + redraw(); +} + +/** + Override draw() to make groups with no box or flat box background visible. + */ +void Fl_Grid_Proxy::draw() { + if (Fluid.show_ghosted_outline && (box() == FL_NO_BOX)) { + fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); + } + Fl_Grid::draw(); +} + +/** + Draw additional markings in the overlay plane when a grid is selected. + */ +void Fl_Grid_Proxy::draw_overlay() { + fl_line_style(FL_DOT); + grid_color = fl_color(); + draw_grid(); + fl_color(grid_color); +} + +/** + Move a cell into the grid or within the grid. + + If the target cell is already taken, \p how will determine what to do: + + If \p how is 0, the existing cell at \p to_row, \p to_col will be deleted, + unlinking the occupant from the grid. \p in_child will the be inserted at the + given location. + + If \p how is 1, the old cell will remain intact, however \p in_child will be + unlinked from the grid. + + If \p how is 2, the old cell will remain intact, and \p in_child will be + removed from the grid, but it will be stored in the transient list and + resized to the target cell position and size. If \p in_child is later + moved to an unoccupied cell, it will be removed from the transient list and + relinked to the grid. Rowspan and colspan are ignored here. + + \param[in] in_child must already be a child of grid + \param[in] to_row, to_col move the child into this cell + \param[in] how 0: replace occupant, 1: don't replace, 2: make transient + if occupied + */ +void Fl_Grid_Proxy::move_cell(Fl_Widget *in_child, int to_row, int to_col, int how) { + // the child must already be a true child of grid + assert(find(in_child)row() == to_row && old_cell->col() == to_col) return; + rowspan = old_cell->rowspan(); + colspan = old_cell->colspan(); + align = old_cell->align(); + old_cell->minimum_size(&w, &h); + } + if ((to_row < 0) || (to_row+rowspan > rows())) return; + if ((to_col < 0) || (to_col+colspan > cols())) return; + Fl_Grid::Cell *new_cell = nullptr; + if (how == 0) { // replace old occupant in cell, making that one homeless + new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align); + } else if (how == 1) { // don't replace an old occupant, making ourselves homeless + // todo: colspan, rowspan? + if (cell(to_row, to_col) == nullptr) { + new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align); + } else { + if (old_cell) remove_cell(old_cell->row(), old_cell->col()); + } + } else if (how == 2) { + Cell *current = cell(to_row, to_col); + if (current == nullptr) { + new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align); + } else { + if (old_cell) remove_cell(old_cell->row(), old_cell->col()); + new_cell = transient_widget(in_child, to_row, to_col, rowspan, colspan, align); + Fl_Widget *w = current->widget(); + Fluid.proj.tree.allow_layout++; + in_child->resize(w->x(), w->y(), w->w(), w->h()); + Fluid.proj.tree.allow_layout--; + } + } + if (new_cell) new_cell->minimum_size(w, h); +} + +/** + Generate or replace a transient widget entry. + + If the widget is in the cell list, it will be removed there. + If the widget is already transient, the cell will be replaced. + + \param[in] wi a child of this Fl_Grid_Proxy, that may be linked to a cell or transient cell + \param[in] row, col, row_span, col_span, align cell parameters + */ +Fl_Grid::Cell* Fl_Grid_Proxy::transient_widget(Fl_Widget *wi, int row, int col, int row_span, int col_span, Fl_Grid_Align align) { + int i = 0; + bool remove_old_cell = false; + Cell *old_cell = cell(wi); + if (old_cell) { + remove_old_cell = true; + } else { + for (i=0; irowspan(row_span); + new_cell->colspan(col_span); + new_cell->align(align); + if (old_cell) { + int mw, mh; + old_cell->minimum_size(&mw, &mh); + new_cell->minimum_size(mw, mh); + if (remove_old_cell) { + remove_cell(old_cell->row(), old_cell->col()); + } else { + delete old_cell; + } + } + if (i == num_transient_) { + transient_make_room_(num_transient_ + 1); + transient_[i].widget = wi; + num_transient_++; + } + transient_[i].cell = new_cell; + return new_cell; +} + +/** + Make room for at least n transient widgets in the array. + \param[in] n minimum number of entries + */ +void Fl_Grid_Proxy::transient_make_room_(int n) { + if (n > cap_transient_) { + cap_transient_ = n + 10; + transient_ = (Cell_Widget_Pair*)::realloc(transient_, cap_transient_ * sizeof(Cell_Widget_Pair)); + } +} + +/** + Remove a widget form the list and deallocate the transient cell. + \param[in] w remove the transient cell for this widget + */ +void Fl_Grid_Proxy::transient_remove_(Fl_Widget *w) { + for (int i=0; ilayout(3, 3); + Fl_Group::current(nullptr); + return g; +} + +Fl_Widget *Grid_Node::enter_live_mode(int top) { + Fl_Grid *grid = new Fl_Grid(o->x(), o->y(), o->w(), o->h()); + return propagate_live_mode(grid); +} + +void Grid_Node::leave_live_mode() { +} + +void Grid_Node::copy_properties() +{ + super::copy_properties(); + Fl_Grid *d = (Fl_Grid*)live_widget, *s =(Fl_Grid*)o; + d->layout(s->rows(), s->cols()); + int lm, tm, rm, bm; + s->margin(&lm, &tm, &rm, &bm); + d->margin(lm, tm, rm, bm); + int rg, cg; + s->gap(&rg, &cg); + d->gap(rg, cg); + // copy col widths, heights, and gaps + for (int c=0; ccols(); c++) { + d->col_width(c, s->col_width(c)); + d->col_gap(c, s->col_gap(c)); + d->col_weight(c, s->col_weight(c)); + } + // copy row widths, heights, and gaps + for (int r=0; rrows(); r++) { + d->row_height(r, s->row_height(r)); + d->row_gap(r, s->row_gap(r)); + d->row_weight(r, s->row_weight(r)); + } +} + +void Grid_Node::copy_properties_for_children() { + Fl_Grid *d = (Fl_Grid*)live_widget, *s =(Fl_Grid*)o; + for (int i=0; ichildren(); i++) { + Fl_Grid::Cell *cell = s->cell(s->child(i)); + if (cell && ichildren()) { + d->widget(d->child(i), + cell->row(), cell->col(), + cell->rowspan(), cell->colspan(), + cell->align()); + } + } + d->layout(); +} + +void Grid_Node::write_properties(fld::io::Project_Writer &f) +{ + super::write_properties(f); + Fl_Grid* grid = (Fl_Grid*)o; + int i, rows = grid->rows(), cols = grid->cols(); + f.write_indent(level+1); + f.write_string("dimensions {%d %d}", rows, cols); + int lm, tm, rm, bm; + grid->margin(&lm, &tm, &rm, &bm); + if (lm!=0 || tm!=0 || rm!=0 || bm!=0) + f.write_string("margin {%d %d %d %d}", lm, tm, rm, bm); + int rg, cg; + grid->gap(&rg, &cg); + if (rg!=0 || cg!=0) + f.write_string("gap {%d %d}", rg, cg); + // -- write all row heights if one of them is not the default 0 + for (i=0; irow_height(i)!=0) break; + if (irow_height(i)); + f.write_string("}"); + } + // -- write all row weights if one of them is not the default 50 + for (i=0; irow_weight(i)!=50) break; + if (irow_weight(i)); + f.write_string("}"); + } + // -- write all row gaps if one of them is not the default -1 + for (i=0; irow_gap(i)!=-1) break; + if (irow_gap(i)); + f.write_string("}"); + } + // -- write all col widths if one of them is not the default 0 + for (i=0; icol_width(i)!=0) break; + if (icol_width(i)); + f.write_string("}"); + } + // -- write all col weights if one of them is not the default 50 + for (i=0; icol_weight(i)!=50) break; + if (icol_weight(i)); + f.write_string("}"); + } + // -- write all col gaps if one of them is not the default -1 + for (i=0; icol_gap(i)!=-1) break; + if (icol_gap(i)); + f.write_string("}"); + } +} + +void Grid_Node::read_property(fld::io::Project_Reader &f, const char *c) +{ + Fl_Grid* grid = (Fl_Grid*)o; + if (!strcmp(c,"dimensions")) { + int rows = 3, cols = 3; + if (sscanf(f.read_word(),"%d %d", &rows, &cols) == 2) + grid->layout(rows, cols); + } else if (!strcmp(c,"margin")) { + int lm, tm, rm, bm; + if (sscanf(f.read_word(),"%d %d %d %d", &lm, &tm, &rm, &bm) == 4) + grid->margin(lm, tm, rm, bm); + } else if (!strcmp(c,"gap")) { + int rg, cg; + if (sscanf(f.read_word(),"%d %d", &rg, &cg) == 2) + grid->gap(rg, cg); + } else if (!strcmp(c,"rowheights")) { + int rows = grid->rows(); + f.read_word(1); // "{" + for (int i=0; irow_height(i, f.read_int()); + f.read_word(1); // "}" + } else if (!strcmp(c,"rowweights")) { + int rows = grid->rows(); + f.read_word(1); // "{" + for (int i=0; irow_weight(i, f.read_int()); + f.read_word(1); // "}" + } else if (!strcmp(c,"rowgaps")) { + int rows = grid->rows(); + f.read_word(1); // "{" + for (int i=0; irow_gap(i, f.read_int()); + f.read_word(1); // "}" + } else if (!strcmp(c,"colwidths")) { + int cols = grid->cols(); + f.read_word(1); // "{" + for (int i=0; icol_width(i, f.read_int()); + f.read_word(1); // "}" + } else if (!strcmp(c,"colweights")) { + int cols = grid->cols(); + f.read_word(1); // "{" + for (int i=0; icol_weight(i, f.read_int()); + f.read_word(1); // "}" + } else if (!strcmp(c,"colgaps")) { + int cols = grid->cols(); + f.read_word(1); // "{" + for (int i=0; icol_gap(i, f.read_int()); + f.read_word(1); // "}" + } else { + super::read_property(f, c); + } +} + +void Grid_Node::write_parent_properties(fld::io::Project_Writer &f, Node *child, bool encapsulate) { + Fl_Grid *grid; + Fl_Widget *child_widget; + Fl_Grid::Cell *cell; + if (!child->is_true_widget()) return super::write_parent_properties(f, child, true); + grid = (Fl_Grid*)o; + child_widget = ((Widget_Node*)child)->o; + cell = grid->cell(child_widget); + if (!cell) return super::write_parent_properties(f, child, true); + if (encapsulate) { + f.write_indent(level+2); + f.write_string("parent_properties {"); + } + f.write_indent(level+3); + f.write_string("location {%d %d}", cell->row(), cell->col()); + int v = cell->colspan(); + if (v>1) { + f.write_indent(level+3); + f.write_string("colspan %d", v); + } + v = cell->rowspan(); + if (v>1) { + f.write_indent(level+3); + f.write_string("rowspan %d", v); + } + v = (int)cell->align(); + if (v!=FL_GRID_FILL) { + f.write_indent(level+3); + f.write_string("align %d", v); + } + int min_w = 0, min_h = 0; + cell->minimum_size(&min_w, &min_h); + if (min_w!=20 || min_h!=20) { + f.write_indent(level+3); + f.write_string("minsize {%d %d}", min_w, min_h); + } + super::write_parent_properties(f, child, false); + if (encapsulate) { + f.write_indent(level+2); + f.write_string("}"); + } + return; +} + +// NOTE: we have to do this in a loop just as ::read_property() in case a new +// property is added. In the current setup, all the remaining properties +// will be skipped +void Grid_Node::read_parent_property(fld::io::Project_Reader &f, Node *child, const char *property) { + if (!child->is_true_widget()) { + super::read_parent_property(f, child, property); + return; + } + Fl_Grid *grid = (Fl_Grid*)o; + Fl_Widget *child_widget = ((Widget_Node*)child)->o; + if (!strcmp(property, "location")) { + int row = -1, col = -1; + const char *value = f.read_word(); + sscanf(value, "%d %d", &row, &col); + Fl_Grid::Cell *cell = grid->widget(child_widget, row, col); + if (cell) { + int min_w = 20, min_h = 20; + cell->minimum_size(min_w, min_h); + } + } else if (!strcmp(property, "colspan")) { + int colspan = atoi(f.read_word()); + Fl_Grid::Cell *cell = grid->cell(child_widget); + if (cell) cell->colspan(colspan); + } else if (!strcmp(property, "rowspan")) { + int rowspan = atoi(f.read_word()); + Fl_Grid::Cell *cell = grid->cell(child_widget); + if (cell) cell->rowspan(rowspan); + } else if (!strcmp(property, "align")) { + int align = atoi(f.read_word()); + Fl_Grid::Cell *cell = grid->cell(child_widget); + if (cell) cell->align((Fl_Grid_Align)align); + } if (!strcmp(property, "minsize")) { + int min_w = 20, min_h = 20; + const char *value = f.read_word(); + sscanf(value, "%d %d", &min_w, &min_h); + Fl_Grid::Cell *cell = grid->cell(child_widget); + if (cell) cell->minimum_size(min_w, min_h); + } else { + super::read_parent_property(f, child, property); + } +} + +void Grid_Node::write_code1(fld::io::Code_Writer& f) { + const char *var = name() ? name() : "o"; + Fl_Grid* grid = (Fl_Grid*)o; + Widget_Node::write_code1(f); + int i, rows = grid->rows(), cols = grid->cols(); + f.write_c("%s%s->layout(%d, %d);\n", f.indent(), var, rows, cols); + int lm, tm, rm, bm; + grid->margin(&lm, &tm, &rm, &bm); + if (lm!=0 || tm!=0 || rm!=0 || bm!=0) + f.write_c("%s%s->margin(%d, %d, %d, %d);\n", f.indent(), var, lm, tm, rm, bm); + int rg, cg; + grid->gap(&rg, &cg); + if (rg!=0 || cg!=0) + f.write_c("%s%s->gap(%d, %d);\n", f.indent(), var, rg, cg); + // -- write all row heights if one of them is not the default 0 + for (i=0; irow_height(i)!=0) break; + if (irow_height(0)); + for (i=1; irow_height(i)); + f.write_c(" };\n"); + f.write_c("%s%s->row_height(rowheights, %d);\n", f.indent(), var, rows); + } + // -- write all row weights if one of them is not the default 50 + for (i=0; irow_weight(i)!=50) break; + if (irow_weight(0)); + for (i=1; irow_weight(i)); + f.write_c(" };\n"); + f.write_c("%s%s->row_weight(rowweights, %d);\n", f.indent(), var, rows); + } + // -- write all row gaps if one of them is not the default -1 + for (i=0; irow_gap(i)!=-1) break; + if (irow_gap(0)); + for (i=1; irow_gap(i)); + f.write_c(" };\n"); + f.write_c("%s%s->row_gap(rowgaps, %d);\n", f.indent(), var, rows); + } + // -- write all col widths if one of them is not the default 0 + for (i=0; icol_width(i)!=0) break; + if (icol_width(0)); + for (i=1; icol_width(i)); + f.write_c(" };\n"); + f.write_c("%s%s->col_width(colwidths, %d);\n", f.indent(), var, cols); + } + // -- write all col weights if one of them is not the default 50 + for (i=0; icol_weight(i)!=50) break; + if (icol_weight(0)); + for (i=1; icol_weight(i)); + f.write_c(" };\n"); + f.write_c("%s%s->col_weight(colweights, %d);\n", f.indent(), var, cols); + } + // -- write all col gaps if one of them is not the default -1 + for (i=0; icol_gap(i)!=-1) break; + if (icol_gap(0)); + for (i=1; icol_gap(i)); + f.write_c(" };\n"); + f.write_c("%s%s->col_gap(colgaps, %d);\n", f.indent(), var, cols); + } +} + +void Grid_Node::write_code2(fld::io::Code_Writer& f) { + const char *var = name() ? name() : "o"; + Fl_Grid* grid = (Fl_Grid*)o; + bool first_cell = true; + for (int i=0; ichildren(); i++) { + Fl_Widget *c = grid->child(i); + Fl_Grid::Cell *cell = grid->cell(c); + if (cell) { + if (first_cell) { + f.write_c("%sFl_Grid::Cell *cell = 0L;\n", f.indent()); + first_cell = false; + } + f.write_c("%scell = %s->widget(%s->child(%d), %d, %d, %d, %d, %d);\n", + f.indent(), var, var, i, cell->row(), cell->col(), + cell->rowspan(), cell->colspan(), cell->align()); + int min_w = 20, min_h = 20; + cell->minimum_size(&min_w, &min_h); + f.write_c("%sif (cell) cell->minimum_size(%d, %d);\n", f.indent(), min_w, min_h); + } + } + super::write_code2(f); +} + +void Grid_Node::add_child(Node* a, Node* b) { + super::add_child(a, b); + Fl_Grid* grid = (Fl_Grid*)o; + grid->need_layout(1); + grid->redraw(); +} + +void Grid_Node::move_child(Node* a, Node* b) { + super::move_child(a, b); + Fl_Grid* grid = (Fl_Grid*)o; + grid->need_layout(1); + grid->redraw(); +} + +void Grid_Node::remove_child(Node* a) { + super::remove_child(a); + Fl_Grid* grid = (Fl_Grid*)o; + grid->need_layout(1); + grid->redraw(); +} + +/** Update the initial size of a child widget. + Fl_Grid keeps track of the size of children when they are first added. In + FLUID, users will want to resize children. So we need to trick Fl_Grid into + taking the new size as the initial size. + */ +void Grid_Node::child_resized(Widget_Node *child_type) { + Fl_Grid *grid = (Fl_Grid*)o; + Fl_Widget *child = child_type->o; + Fl_Grid::Cell *cell = grid->cell(child); + if (cell && ((cell->align()&FL_GRID_VERTICAL)==0)) { + int min_w = 0, min_h = 0; + cell->minimum_size(&min_w, &min_h); + cell->minimum_size(min_w, child->h()); + } + if (cell && ((cell->align()&FL_GRID_HORIZONTAL)==0)) { + int min_w = 0, min_h = 0; + cell->minimum_size(&min_w, &min_h); + cell->minimum_size(child->w(), min_h); + } + // TODO: if the user resizes an FL_GRID_FILL widget, should we change the alignment? +} + +/** Return the currently selected Grid widget if is a Grid Type. */ +Fl_Grid *Grid_Node::selected() { + if (current_widget && current_widget->is_a(Type::Grid)) + return ((Fl_Grid*)((Grid_Node*)current_widget)->o); + return nullptr; +} + +/** + Insert a child widget into the cell at the x, y position inside the window. + /param[in] child + /param[in] x, y pixels from the top left of the window + */ +void Grid_Node::insert_child_at(Fl_Widget *child, int x, int y) { + Fl_Grid_Proxy *grid = (Fl_Grid_Proxy*)o; + int row = -1, col = -1, ml, mt, grg, gcg; + grid->margin(&ml, &mt, nullptr, nullptr); + grid->gap(&grg, &gcg); + int x0 = grid->x() + Fl::box_dx(grid->box()) + ml; + int y0 = grid->y() + Fl::box_dy(grid->box()) + mt; + + for (int r = 0; r < grid->rows(); r++) { + if (y>y0) row = r; + int gap = grid->row_gap(r)>=0 ? grid->row_gap(r) : grg; + y0 += grid->computed_row_height(r); + y0 += gap; + } + + for (int c = 0; c < grid->cols(); c++) { + if (x>x0) col = c; + int gap = grid->col_gap(c)>=0 ? grid->col_gap(c) : gcg; + x0 += grid->computed_col_width(c); + x0 += gap; + } + + grid->move_cell(child, row, col, 2); +} + +/** + Insert a child widget into the first new cell we can find . + + There are many other possible strategies. How about inserting to the right + of the last added child. Also, what happens if the grid is full? Should + we add a new row at the bottom? + + /param[in] child + */ +void Grid_Node::insert_child_at_next_free_cell(Fl_Widget *child) { + Fl_Grid_Proxy *grid = (Fl_Grid_Proxy*)o; + if (grid->cell(child)) return; +// The code below would insert the new widget after the last selected one, but +// unfortunately the current_widget is already invalid. +// if (current_widget && (current_widget->parent == this)) { +// Fl_Grid::Cell *current_cell = grid->any_cell(current_widget->o); +// if (current_cell) { +// r = current_cell->row(); +// c = current_cell->col(); +// } +// } + for (int r = 0; r < grid->rows(); r++) { + for (int c = 0; c < grid->cols(); c++) { + if (!grid->cell(r, c)) { + grid->move_cell(child, r, c); + return; + } + } + } + grid->layout(grid->rows() + 1, grid->cols()); + grid->move_cell(child, grid->rows() - 1, 0); +} + +/** Move cells around using the keyboard. + \note this fails if we have two children selected side by side and press 'right', + which will move the left child first, removing the right child from the + cell system. When trying to move the second child, it has no longer an + assigned row or column. + \param[in] child pointer to the child type + \param[in] key code of the last keypress when handling a FL_KEYBOARD event. + */ +void Grid_Node::keyboard_move_child(Widget_Node *child, int key) { + Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)o); + Fl_Grid::Cell *cell = grid->any_cell(child->o); + if (!cell) return; + if (key == FL_Right) { + grid->move_cell(child->o, cell->row(), cell->col()+1, 2); + } else if (key == FL_Left) { + grid->move_cell(child->o, cell->row(), cell->col()-1, 2); + } else if (key == FL_Up) { + grid->move_cell(child->o, cell->row()-1, cell->col(), 2); + } else if (key == FL_Down) { + grid->move_cell(child->o, cell->row()+1, cell->col(), 2); + } +} + +void Grid_Node::layout_widget() { + Fluid.proj.tree.allow_layout++; + ((Fl_Grid*)o)->layout(); + Fluid.proj.tree.allow_layout--; +} + diff --git a/fluid/nodes/Grid_Node.h b/fluid/nodes/Grid_Node.h new file mode 100644 index 000000000..4363164ee --- /dev/null +++ b/fluid/nodes/Grid_Node.h @@ -0,0 +1,82 @@ +// +// Grid Node header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 2023-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#ifndef FLUID_NODES_GRID_NODE_H +#define FLUID_NODES_GRID_NODE_H + +#include "nodes/Group_Node.h" +#include + +// ---- Grid_Node --------------------------------------------------- MARK: - + +class Fl_Grid_Proxy : public Fl_Grid { +protected: + typedef struct { Fl_Widget *widget; Cell *cell; } Cell_Widget_Pair; + Cell_Widget_Pair *transient_; + int num_transient_; + int cap_transient_; + void transient_make_room_(int n); + void transient_remove_(Fl_Widget *w); +public: + Fl_Grid_Proxy(int X,int Y,int W,int H); + ~Fl_Grid_Proxy(); + void resize(int,int,int,int) override; + void draw() override; + void draw_overlay(); + void move_cell(Fl_Widget *child, int to_row, int to_col, int how = 0); + Cell* any_cell(Fl_Widget *widget) const; + Cell* transient_cell(Fl_Widget *widget) const; + Cell* transient_widget(Fl_Widget *wi, int row, int col, int row_span, int col_span, Fl_Grid_Align align = FL_GRID_FILL); + Cell* widget(Fl_Widget *wi, int row, int col, Fl_Grid_Align align = FL_GRID_FILL); + Cell* widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align = FL_GRID_FILL); +}; + +class Grid_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Grid_Node prototype; +public: + Grid_Node(); + const char *type_name() override {return "Fl_Grid";} + const char *alt_type_name() override {return "fltk::GridGroup";} + Widget_Node *_make() override { return new Grid_Node(); } + Fl_Widget *widget(int X,int Y,int W,int H) override; + Type type() const override { return Type::Grid; } + bool is_a(Type inType) const override { return (inType==Type::Grid) ? true : super::is_a(inType); } + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + void write_parent_properties(fld::io::Project_Writer &f, Node *child, bool encapsulate) override; + void read_parent_property(fld::io::Project_Reader &f, Node *child, const char *property) override; + Fl_Widget *enter_live_mode(int top=0) override; + void leave_live_mode() override; + void copy_properties() override; + void copy_properties_for_children() override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + void add_child(Node*, Node*) override; + void move_child(Node*, Node*) override; + void remove_child(Node*) override; + void layout_widget() override; + void child_resized(Widget_Node *child); + void insert_child_at(Fl_Widget *child, int x, int y); + void insert_child_at_next_free_cell(Fl_Widget *child); + void keyboard_move_child(Widget_Node*, int key); + + static class Fl_Grid *selected(); +}; + +#endif // FLUID_NODES_GRID_NODE_H diff --git a/fluid/nodes/Group_Node.cxx b/fluid/nodes/Group_Node.cxx new file mode 100644 index 000000000..d269b62ab --- /dev/null +++ b/fluid/nodes/Group_Node.cxx @@ -0,0 +1,849 @@ +// +// Group Node code for the Fast Light Tool Kit (FLTK). +// +// Object describing an Fl_Group and links to Window_Node.C and +// the Fl_Tabs widget, with special stuff to select tab items and +// insure that only one is visible. +// +// Copyright 1998-2023 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#include "nodes/Group_Node.h" + +#include "Fluid.h" +#include "proj/undo.h" +#include "app/Snap_Action.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "widgets/Node_Browser.h" + +#include +#include +#include +#include +#include +#include +#include "../src/flstring.h" + +#include +#include + + +// ---- Group_Node -------------------------------------------------- MARK: - + +Group_Node Group_Node::prototype; + +/** + Override group's resize behavior to do nothing to children by default. + \param[in] X, Y, W, H new size + */ +void Fl_Group_Proxy::resize(int X, int Y, int W, int H) { + if (Fluid.proj.tree.allow_layout > 0) { + Fl_Group::resize(X, Y, W, H); + } else { + Fl_Widget::resize(X, Y, W, H); + } + redraw(); +} + +/** + Override draw() to make groups with no box or flat box background visible. + */ +void Fl_Group_Proxy::draw() { + if (Fluid.show_ghosted_outline && (box() == FL_NO_BOX)) { + fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); + } + Fl_Group::draw(); +} + + +/** + \brief Enlarge the group size, so all children fit within. + */ +void fix_group_size(Node *tt) { + if (!tt || !tt->is_a(Type::Group)) return; + Group_Node* t = (Group_Node*)tt; + int X = t->o->x(); + int Y = t->o->y(); + int R = X+t->o->w(); + int B = Y+t->o->h(); + for (Node *nn = t->next; nn && nn->level > t->level; nn = nn->next) { + if (nn->is_true_widget()) { + Widget_Node* n = (Widget_Node*)nn; + int x = n->o->x(); if (x < X) X = x; + int y = n->o->y(); if (y < Y) Y = y; + int r = x+n->o->w();if (r > R) R = r; + int b = y+n->o->h();if (b > B) B = b; + } + } + t->o->resize(X,Y,R-X,B-Y); +} + +extern void group_selected_menuitems(); + +void group_cb(Fl_Widget *, void *) { + if (!Fluid.proj.tree.current) { + fl_message("No widgets selected."); + return; + } + if (!Fluid.proj.tree.current->is_widget()) { + fl_message("Only widgets and menu items can be grouped."); + return; + } + if (Fluid.proj.tree.current->is_a(Type::Menu_Item)) { + group_selected_menuitems(); + return; + } + // The group will be created in the parent group of the current widget + Node *qq = Fluid.proj.tree.current->parent; + Widget_Node *q = static_cast(Fluid.proj.tree.current); + while (qq && !qq->is_a(Type::Group)) { + qq = qq->parent; + } + if (!qq) { + fl_message("Can't create a new group here."); + return; + } + Fluid.proj.undo.checkpoint(); + Fluid.proj.undo.suspend(); + Fluid.proj.tree.current = qq; + Group_Node *n = (Group_Node*)(Group_Node::prototype.make(Strategy::AS_LAST_CHILD)); + n->move_before(q); + n->o->resize(q->o->x(),q->o->y(),q->o->w(),q->o->h()); + for (Node *t = qq->next; t && (t->level > qq->level);) { + if (t->level != n->level || t == n || !t->selected) { + t = t->next; + continue; + } + Node *nxt = t->remove(); + t->add(n, Strategy::AS_LAST_CHILD); + t = nxt; + } + fix_group_size(n); + Fluid.proj.tree.current = q; + n->layout_widget(); + widget_browser->rebuild(); + Fluid.proj.undo.resume(); + Fluid.proj.set_modflag(1); +} + +extern void ungroup_selected_menuitems(); + +void ungroup_cb(Fl_Widget *, void *) { + if (!Fluid.proj.tree.current) { + fl_message("No widgets selected."); + return; + } + if (!Fluid.proj.tree.current->is_widget()) { + fl_message("Only widgets and menu items can be ungrouped."); + return; + } + if (Fluid.proj.tree.current->is_a(Type::Menu_Item)) { + ungroup_selected_menuitems(); + return; + } + + Widget_Node *q = static_cast(Fluid.proj.tree.current); + int q_level = q->level; + Node *qq = Fluid.proj.tree.current->parent; + while (qq && !qq->is_true_widget()) qq = qq->parent; + if (!qq || !qq->is_a(Type::Group)) { + fl_message("Only menu widgets inside a group can be ungrouped."); + return; + } + Fluid.proj.undo.checkpoint(); + Fluid.proj.undo.suspend(); + Fluid.proj.tree.current = qq; + for (Node *t = qq->next; t && (t->level > qq->level);) { + if (t->level != q_level || !t->selected) { + t = t->next; + continue; + } + Node *nxt = t->remove(); + t->insert(qq); + t = nxt; + } + if (!qq->next || (qq->next->level <= qq->level)) { + qq->remove(); + delete qq; // qq has no children that need to be delete + } + Fluid.proj.tree.current = q; + widget_browser->rebuild(); + Fluid.proj.undo.resume(); + Fluid.proj.set_modflag(1); +} + +void Group_Node::ideal_size(int &w, int &h) { + if (parent && parent->is_true_widget()) { + Fl_Widget *p = ((Widget_Node*)parent)->o; + w = p->w() / 2; + h = p->h() / 2; + } else { + w = 140; + h = 140; + } + fld::app::Snap_Action::better_size(w, h); +} + +void Group_Node::write_code1(fld::io::Code_Writer& f) { + Widget_Node::write_code1(f); +} + +void Group_Node::write_code2(fld::io::Code_Writer& f) { + const char *var = name() ? name() : "o"; + write_extra_code(f); + f.write_c("%s%s->end();\n", f.indent(), var); + if (resizable()) { + f.write_c("%sFl_Group::current()->resizable(%s);\n", f.indent(), var); + } + write_block_close(f); +} + +// This is called when o is created. If it is in the tab group make +// sure it is visible: +void Group_Node::add_child(Node* cc, Node* before) { + Widget_Node* c = (Widget_Node*)cc; + Fl_Widget* b = before ? ((Widget_Node*)before)->o : nullptr; + ((Fl_Group*)o)->insert(*(c->o), b); + o->redraw(); +} + +// This is called when o is deleted. If it is in the tab group make +// sure it is not visible: +void Group_Node::remove_child(Node* cc) { + Widget_Node* c = (Widget_Node*)cc; + ((Fl_Group*)o)->remove(c->o); + o->redraw(); +} + +// move, don't change selected value: +void Group_Node::move_child(Node* cc, Node* before) { + Widget_Node* c = (Widget_Node*)cc; + Fl_Widget* b = before ? ((Widget_Node*)before)->o : nullptr; + ((Fl_Group*)o)->insert(*(c->o), b); + o->redraw(); +} + +// live mode support +Fl_Widget* Group_Node::enter_live_mode(int) { + Fl_Group *grp = new Fl_Group(o->x(), o->y(), o->w(), o->h()); + return propagate_live_mode(grp); +} + +void Group_Node::leave_live_mode() { +} + +/** + copy all properties from the edit widget to the live widget + */ +void Group_Node::copy_properties() { + Widget_Node::copy_properties(); +} + +// ---- Pack_Node --------------------------------------------------- MARK: - + +Pack_Node Pack_Node::prototype; // the "factory" + +const char pack_type_name[] = "Fl_Pack"; + +Fl_Menu_Item pack_type_menu[] = { + {"HORIZONTAL", 0, nullptr, (void*)Fl_Pack::HORIZONTAL}, + {"VERTICAL", 0, nullptr, (void*)Fl_Pack::VERTICAL}, + {nullptr} +}; + +Fl_Widget *Pack_Node::enter_live_mode(int) { + Fl_Group *grp = new Fl_Pack(o->x(), o->y(), o->w(), o->h()); + return propagate_live_mode(grp); +} + +void Pack_Node::copy_properties() +{ + Group_Node::copy_properties(); + Fl_Pack *d = (Fl_Pack*)live_widget, *s =(Fl_Pack*)o; + d->spacing(s->spacing()); +} + +// ---- Flex_Node --------------------------------------------------- MARK: - + +const char flex_type_name[] = "Fl_Flex"; + +Fl_Menu_Item flex_type_menu[] = { + {"HORIZONTAL", 0, nullptr, (void*)Fl_Flex::HORIZONTAL}, + {"VERTICAL", 0, nullptr, (void*)Fl_Flex::VERTICAL}, + {nullptr}}; + +Flex_Node Flex_Node::prototype; // the "factory" + +/** + Override flex's resize behavior to do nothing to children by default. + + \param[in] X, Y, W, H new size + */ +void Fl_Flex_Proxy::resize(int X, int Y, int W, int H) { + if (Fluid.proj.tree.allow_layout > 0) { + Fl_Flex::resize(X, Y, W, H); + } else { + Fl_Widget::resize(X, Y, W, H); + } + redraw(); +} + +/** + Override draw() to make groups with no box or flat box background visible. + */ +void Fl_Flex_Proxy::draw() { + if (Fluid.show_ghosted_outline && (box() == FL_NO_BOX)) { + fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); + } + Fl_Flex::draw(); +} + +Fl_Widget *Flex_Node::enter_live_mode(int) { + Fl_Flex *grp = new Fl_Flex(o->x(), o->y(), o->w(), o->h()); + propagate_live_mode(grp); + Fl_Flex *d = grp, *s =(Fl_Flex*)o; + int nc = s->children(), nd = d->children(); + if (nc>nd) nc = nd; + for (int i=0; ifixed(s->child(i))) { + Fl_Widget *dc = d->child(i); + d->fixed(d->child(i), s->horizontal() ? dc->w() : dc->h()); + } + } + return grp; +} + +void Flex_Node::copy_properties() +{ + Group_Node::copy_properties(); + Fl_Flex *d = (Fl_Flex*)live_widget, *s =(Fl_Flex*)o; + int lm, tm, rm, bm; + s->margin(&lm, &tm, &rm, &bm); + d->margin(lm, tm, rm, bm); + d->gap( s->gap() ); +} + +void Flex_Node::copy_properties_for_children() { + Fl_Flex *d = (Fl_Flex*)live_widget, *s =(Fl_Flex*)o; + for (int i=0; ichildren(); i++) { + if (s->fixed(s->child(i)) && ichildren()) { + if (s->horizontal()) { + d->fixed(d->child(i), d->child(i)->w()); + } else { + d->fixed(d->child(i), d->child(i)->h()); + } + } + } + d->layout(); +} + +void Flex_Node::write_properties(fld::io::Project_Writer &f) +{ + Group_Node::write_properties(f); + Fl_Flex* flex = (Fl_Flex*)o; + int lm, tm, rm, bm; + flex->margin(&lm, &tm, &rm, &bm); + if (lm!=0 || tm!=0 || rm!=0 || bm!=0) + f.write_string("margin {%d %d %d %d}", lm, tm, rm, bm); + if (flex->gap()) + f.write_string("gap %d", flex->gap()); + int nSet = 0; + for (int i=0; ichildren(); i++) + if (flex->fixed(flex->child(i))) + nSet++; + if (nSet) { + f.write_string("fixed_size_tuples {%d", nSet); + for (int i=0; ichildren(); i++) { + Fl_Widget *ci = flex->child(i); + if (flex->fixed(ci)) + f.write_string(" %d %d", i, flex->horizontal() ? ci->w() : ci->h()); + } + f.write_string("}"); + } +} + +void Flex_Node::read_property(fld::io::Project_Reader &f, const char *c) +{ + Fl_Flex* flex = (Fl_Flex*)o; + suspend_auto_layout = 1; + if (!strcmp(c,"margin")) { + int lm, tm, rm, bm; + if (sscanf(f.read_word(),"%d %d %d %d",&lm,&tm,&rm,&bm) == 4) + flex->margin(lm, tm, rm, bm); + } else if (!strcmp(c,"gap")) { + int g; + if (sscanf(f.read_word(),"%d",&g)) + flex->gap(g); + } else if (!strcmp(c,"fixed_size_tuples")) { + f.read_word(1); // must be '{' + const char *nStr = f.read_word(1); // number of indices in table + fixedSizeTupleSize = atoi(nStr); + fixedSizeTuple = new int[fixedSizeTupleSize*2]; + for (int i=0; i0) { + for (int i=0; i=0 && ixchildren()) { + Fl_Widget *ci = flex->child(ix); + flex->fixed(ci, size); + } + } + fixedSizeTupleSize = 0; + delete[] fixedSizeTuple; + fixedSizeTuple = nullptr; + } + suspend_auto_layout = 0; +} + +void Flex_Node::write_code2(fld::io::Code_Writer& f) { + const char *var = name() ? name() : "o"; + Fl_Flex* flex = (Fl_Flex*)o; + int lm, tm, rm, bm; + flex->margin(&lm, &tm, &rm, &bm); + if (lm!=0 || tm!=0 || rm!=0 || bm!=0) + f.write_c("%s%s->margin(%d, %d, %d, %d);\n", f.indent(), var, lm, tm, rm, bm); + if (flex->gap()) + f.write_c("%s%s->gap(%d);\n", f.indent(), var, flex->gap()); + for (int i=0; ichildren(); ++i) { + Fl_Widget *ci = flex->child(i); + if (flex->fixed(ci)) + f.write_c("%s%s->fixed(%s->child(%d), %d);\n", f.indent(), var, var, i, + flex->horizontal() ? ci->w() : ci->h()); + } + Group_Node::write_code2(f); +} + +//void Flex_Node::add_child(Node* a, Node* b) { +// Group_Node::add_child(a, b); +// if (!suspend_auto_layout) +// ((Fl_Flex*)o)->layout(); +//} +// +//void Flex_Node::move_child(Node* a, Node* b) { +// Group_Node::move_child(a, b); +// if (!suspend_auto_layout) +// ((Fl_Flex*)o)->layout(); +//} + +void Flex_Node::remove_child(Node* a) { + if (a->is_widget()) + ((Fl_Flex*)o)->fixed(((Widget_Node*)a)->o, 0); + Group_Node::remove_child(a); +// ((Fl_Flex*)o)->layout(); + layout_widget(); +} + +void Flex_Node::layout_widget() { + Fluid.proj.tree.allow_layout++; + ((Fl_Flex*)o)->layout(); + Fluid.proj.tree.allow_layout--; +} + +// Change from HORIZONTAL to VERTICAL or back. +// Children in a horizontal Flex have already the full vertical height. If we +// just change to vertical, the accumulated hight of all children is too big. +// We need to relayout existing children. +void Flex_Node::change_subtype_to(int n) { + Fl_Flex* f = (Fl_Flex*)o; + if (f->type()==n) return; + + int nc = f->children(); + if (nc > 0) { + int dw = Fl::box_dw(f->box()); + int dh = Fl::box_dh(f->box()); + int lm, tm, rm, bm; + f->margin(&lm, &tm, &rm, &bm); + int gap = f->gap(); + int fw = f->w()-dw-lm-rm-(nc*gap); + if (fw<=nc) fw = nc; // avoid division by zero + int fh = f->h()-dh-tm-bm-(nc*gap); + if (fh<=nc) fh = nc; // avoid division by zero + + if (f->type()==Fl_Flex::HORIZONTAL && n==Fl_Flex::VERTICAL) { + float scl = (float)fh/(float)fw; + for (int i=0; ichild(i); + c->size(f->w(), (int)(c->w()*scl)); + } + } else if (f->type()==Fl_Flex::VERTICAL && n==Fl_Flex::HORIZONTAL) { + float scl = (float)fw/(float)fh; + for (int i=0; ichild(i); + c->size((int)(c->h()*scl), f->h()); + } + } + } + f->type(n); + f->layout(); +} + +int Flex_Node::parent_is_flex(Node *t) { + return (t->is_widget() + && t->parent + && t->parent->is_a(Type::Flex)); +} + +/** + Insert a widget in the child list so that it moves as close as possible the position. + + \param[in] child any widget in the tree but this, may already be a child of + this and will be relocated if so + \param[in] x, y pixel coordinates relative to the top left of the window + */ +void Flex_Node::insert_child_at(Fl_Widget *child, int x, int y) { + Fl_Flex *flex = (Fl_Flex*)o; + // find the insertion point closest to x, y + int d = flex->w() + flex->h(), di = -1; + if (flex->horizontal()) { + int i, dx; + for (i=0; ichildren(); i++) { + dx = x - flex->child(i)->x(); + if (dx < 0) dx = -dx; + if (dx < d) { d = dx; di = i; } + } + dx = x - (flex->x()+flex->w()); + if (dx < 0) dx = -dx; + if (dx < d) { d = dx; di = i; } + } else { + int i, dy; + for (i=0; ichildren(); i++) { + dy = y - flex->child(i)->y(); + if (dy < 0) dy = -dy; + if (dy < d) { d = dy; di = i; } + } + dy = y - (flex->y()+flex->h()); + if (dy < 0) dy = -dy; + if (dy < d) { d = dy; di = i; } + } + if (di > -1) { + flex->insert(*child, di); + } +} + +/** Move children around using the keyboard. + \param[in] child pointer to the child type + \param[in] key code of the last keypress when handling a FL_KEYBOARD event. + */ +void Flex_Node::keyboard_move_child(Widget_Node *child, int key) { + Fl_Flex *flex = ((Fl_Flex*)o); + int ix = flex->find(child->o); + if (ix == flex->children()) return; + if (flex->horizontal()) { + if (key==FL_Right) { + flex->insert(*child->o, ix+2); + } else if (key==FL_Left) { + if (ix > 0) flex->insert(*child->o, ix-1); + } + } else { + if (key==FL_Down) { + flex->insert(*child->o, ix+2); + } else if (key==FL_Up) { + if (ix > 0) flex->insert(*child->o, ix-1); + } + } +} + +int Flex_Node::size(Node *t, char fixed_only) { + if (!t->is_widget()) return 0; + if (!t->parent) return 0; + if (!t->parent->is_a(Type::Flex)) return 0; + Flex_Node* ft = (Flex_Node*)t->parent; + Fl_Flex* f = (Fl_Flex*)ft->o; + Fl_Widget *w = ((Widget_Node*)t)->o; + if (fixed_only && !f->fixed(w)) return 0; + return f->horizontal() ? w->w() : w->h(); +} + +int Flex_Node::is_fixed(Node *t) { + if (!t->is_widget()) return 0; + if (!t->parent) return 0; + if (!t->parent->is_a(Type::Flex)) return 0; + Flex_Node* ft = (Flex_Node*)t->parent; + Fl_Flex* f = (Fl_Flex*)ft->o; + Fl_Widget *w = ((Widget_Node*)t)->o; + return f->fixed(w); +} + +// ---- Table_Node -------------------------------------------------- MARK: - + +Table_Node Table_Node::prototype; // the "factory" + +static const int MAX_ROWS = 14; +static const int MAX_COLS = 7; + +// this is a minimal table widget used as an example when adding tables in Fluid +class Fl_Table_Proxy : public Fl_Table { + int data[MAX_ROWS][MAX_COLS]; // data array for cells + + // Draw the row/col headings + // Make this a dark thin upbox with the text inside. + // + void DrawHeader(const char *s, int X, int Y, int W, int H) { + fl_push_clip(X,Y,W,H); + fl_draw_box(FL_THIN_UP_BOX, X,Y,W,H, row_header_color()); + fl_color(FL_BLACK); + fl_draw(s, X,Y,W,H, FL_ALIGN_CENTER); + fl_pop_clip(); + } + // Draw the cell data + // Dark gray text on white background with subtle border + // + void DrawData(const char *s, int X, int Y, int W, int H) { + fl_push_clip(X,Y,W,H); + // Draw cell bg + fl_color(FL_WHITE); fl_rectf(X,Y,W,H); + // Draw cell data + fl_color(FL_GRAY0); fl_draw(s, X,Y,W,H, FL_ALIGN_CENTER); + // Draw box border + fl_color(color()); fl_rect(X,Y,W,H); + fl_pop_clip(); + } + // Handle drawing table's cells + // Fl_Table calls this function to draw each visible cell in the table. + // It's up to us to use FLTK's drawing functions to draw the cells the way we want. + // + void draw_cell(TableContext context, int ROW=0, int COL=0, int X=0, int Y=0, int W=0, int H=0) override { + static char s[40]; + switch ( context ) { + case CONTEXT_STARTPAGE: // before page is drawn.. + fl_font(FL_HELVETICA, 16); // set the font for our drawing operations + return; + case CONTEXT_COL_HEADER: // Draw column headers + sprintf(s,"%c",'A'+COL); // "A", "B", "C", etc. + DrawHeader(s,X,Y,W,H); + return; + case CONTEXT_ROW_HEADER: // Draw row headers + sprintf(s,"%03d:",ROW); // "001:", "002:", etc + DrawHeader(s,X,Y,W,H); + return; + case CONTEXT_CELL: // Draw data in cells + sprintf(s,"%d",data[ROW][COL]); + DrawData(s,X,Y,W,H); + return; + default: + return; + } + } +public: + Fl_Table_Proxy(int x, int y, int w, int h, const char *l=nullptr) + : Fl_Table(x, y, w, h, l) { + end(); + for ( int r=0; ro : nullptr; + if (((Fl_Table*)o)->children()==1) { // the FLuid_Table has one extra child + fl_message("Inserting child widgets into an Fl_Table is not recommended.\n" + "Please refer to the documentation on Fl_Table."); + } + ((Fl_Table*)o)->insert(*(c->o), b); + o->redraw(); +} + +void Table_Node::remove_child(Node* cc) { + Widget_Node* c = (Widget_Node*)cc; + ((Fl_Table*)o)->remove(*(c->o)); + o->redraw(); +} + +void Table_Node::move_child(Node* cc, Node* before) { + Widget_Node* c = (Widget_Node*)cc; + Fl_Widget* b = before ? ((Widget_Node*)before)->o : nullptr; + ((Fl_Table*)o)->insert(*(c->o), b); + o->redraw(); +} + +Fl_Widget *Table_Node::enter_live_mode(int) { + Fl_Group *grp = new Fl_Table_Proxy(o->x(), o->y(), o->w(), o->h()); + live_widget = grp; + copy_properties(); + grp->end(); + return live_widget; +} + +void Table_Node::ideal_size(int &w, int &h) { + w = 160; + h = 120; + fld::app::Snap_Action::better_size(w, h); +} + +// ---- Tabs_Node --------------------------------------------------- MARK: - + +Tabs_Node Tabs_Node::prototype; + +const char tabs_type_name[] = "Fl_Tabs"; + +// Override group's resize behavior to do nothing to children: +void Fl_Tabs_Proxy::resize(int X, int Y, int W, int H) { + if (Fluid.proj.tree.allow_layout > 0) { + Fl_Tabs::resize(X, Y, W, H); + } else { + Fl_Widget::resize(X, Y, W, H); + } + redraw(); +} + +/** + Override draw() to make groups with no box or flat box background visible. + */ +void Fl_Tabs_Proxy::draw() { + if (Fluid.show_ghosted_outline && (box() == FL_NO_BOX)) { + fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); + } + Fl_Tabs::draw(); +} + +// This is called when user clicks on a widget in the window. See +// if it is a tab title, and adjust visibility and return new selection: +// If none, return o unchanged: + +Node* Tabs_Node::click_test(int x, int y) { + Fl_Tabs *t = (Fl_Tabs*)o; + Fl_Widget *a = t->which(x,y); + if (!a) return nullptr; // didn't click on tab + // changing the visible tab has an impact on the generated + // source code, so mark this project as changed. + int changed = (a!=t->value()); + // okay, run the tabs ui until they let go of mouse: + t->handle(FL_PUSH); + Fl::pushed(t); + while (Fl::pushed()==t) Fl::wait(); + if (changed) Fluid.proj.set_modflag(1); + return (Node*)(t->value()->user_data()); +} + +void Tabs_Node::add_child(Node* c, Node* before) { + Group_Node::add_child(c, before); +} + +void Tabs_Node::remove_child(Node* cc) { + Widget_Node* c = (Widget_Node*)cc; + Fl_Tabs *t = (Fl_Tabs*)o; + if (t->value() == c->o) t->value(nullptr); + Group_Node::remove_child(c); +} + +Fl_Widget *Tabs_Node::enter_live_mode(int) { + Fl_Tabs *original = static_cast(o); + Fl_Tabs *clone = new Fl_Tabs(o->x(), o->y(), o->w(), o->h()); + propagate_live_mode(clone); + int tab_index = original->find(original->value()); + if ((tab_index>=0) && (tab_indexchildren())) + clone->value(clone->child(tab_index)); + return clone; +} + +// ---- Scroll_Node ------------------------------------------------- MARK: - + +Scroll_Node Scroll_Node::prototype; // the "factory" + +const char scroll_type_name[] = "Fl_Scroll"; + +Fl_Menu_Item scroll_type_menu[] = { + {"BOTH", 0, nullptr, nullptr/*(void*)Fl_Scroll::BOTH*/}, + {"HORIZONTAL", 0, nullptr, (void*)Fl_Scroll::HORIZONTAL}, + {"VERTICAL", 0, nullptr, (void*)Fl_Scroll::VERTICAL}, + {"HORIZONTAL_ALWAYS", 0, nullptr, (void*)Fl_Scroll::HORIZONTAL_ALWAYS}, + {"VERTICAL_ALWAYS", 0, nullptr, (void*)Fl_Scroll::VERTICAL_ALWAYS}, + {"BOTH_ALWAYS", 0, nullptr, (void*)Fl_Scroll::BOTH_ALWAYS}, + {nullptr}}; + +Fl_Widget *Scroll_Node::enter_live_mode(int) { + Fl_Group *grp = new Fl_Scroll(o->x(), o->y(), o->w(), o->h()); + grp->show(); + return propagate_live_mode(grp); +} + +void Scroll_Node::copy_properties() { + Group_Node::copy_properties(); + Fl_Scroll *s = (Fl_Scroll*)o, *d = (Fl_Scroll*)live_widget; + d->scroll_to(s->xposition(), s->yposition()); + d->type(s->type()); + d->scrollbar.align(s->scrollbar.align()); + d->hscrollbar.align(s->hscrollbar.align()); +} + +// ---- Tile_Node --------------------------------------------------- MARK: - + +Tile_Node Tile_Node::prototype; // the "factory" + +const char tile_type_name[] = "Fl_Tile"; + +void Tile_Node::copy_properties() { + Group_Node::copy_properties(); + // no additional properties +} + +// ---- Wizard_Node ------------------------------------------------ MARK: - + +Wizard_Node Wizard_Node::prototype; // the "factory" + +const char wizard_type_name[] = "Fl_Wizard"; + +// Override group's resize behavior to do nothing to children: +void Fl_Wizard_Proxy::resize(int X, int Y, int W, int H) { + if (Fluid.proj.tree.allow_layout > 0) { + Fl_Wizard::resize(X, Y, W, H); + } else { + Fl_Widget::resize(X, Y, W, H); + } + redraw(); +} + +/** + Override draw() to make groups with no box or flat box background visible. + */ +void Fl_Wizard_Proxy::draw() { + if (Fluid.show_ghosted_outline && (box() == FL_NO_BOX)) { + fl_rect(x(), y(), w(), h(), Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, color(), .1f))); + } + Fl_Wizard::draw(); +} + diff --git a/fluid/nodes/Group_Node.h b/fluid/nodes/Group_Node.h new file mode 100644 index 000000000..6b6fe9fcd --- /dev/null +++ b/fluid/nodes/Group_Node.h @@ -0,0 +1,261 @@ +// +// Group Node header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2023 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#ifndef FLUID_NODES_GROUP_NODE_H +#define FLUID_NODES_GROUP_NODE_H + +#include "nodes/Widget_Node.h" + +#include +#include +#include +#include + +void group_cb(Fl_Widget *, void *); +void ungroup_cb(Fl_Widget *, void *); + +// ---- Group_Node -------------------------------------------------- MARK: - + +/** + Proxy group to use in place of Fl_Group in the interactive window. + + In an interactive environment, groups should not automatically resize their + children. This proxy disables the layout of children by default. Children + layout propagation may be enable temporarily by incrementing `allow_layout` + before resizing and decrementing it again afterwards. + */ +class Fl_Group_Proxy : public Fl_Group { +public: + Fl_Group_Proxy(int X,int Y,int W,int H) : Fl_Group(X, Y, W, H) { Fl_Group::current(nullptr); } + void resize(int x, int y, int w, int h) override; + void draw() override; +}; + +class Group_Node : public Widget_Node +{ +public: + typedef Widget_Node super; + static Group_Node prototype; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override {return "Fl_Group";} + const char *alt_type_name() override {return "fltk::Group";} + Fl_Widget *widget(int X,int Y,int W,int H) override { + Fl_Group_Proxy *g = new Fl_Group_Proxy(X,Y,W,H); Fl_Group::current(nullptr); return g;} + Widget_Node *_make() override {return new Group_Node();} + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + void add_child(Node*, Node*) override; + void move_child(Node*, Node*) override; + void remove_child(Node*) override; + int can_have_children() const override {return 1;} + Type type() const override { return Type::Group; } + bool is_a(Type inType) const override { return (inType==Type::Group) ? true : super::is_a(inType); } + Fl_Widget *enter_live_mode(int top=0) override; + void leave_live_mode() override; + void copy_properties() override; +}; + +// ---- Pack_Node --------------------------------------------------- MARK: - + +extern const char pack_type_name[]; +extern Fl_Menu_Item pack_type_menu[]; + +class Pack_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Pack_Node prototype; +private: + Fl_Menu_Item *subtypes() override {return pack_type_menu;} +public: + const char *type_name() override {return pack_type_name;} + const char *alt_type_name() override {return "fltk::PackedGroup";} + Widget_Node *_make() override {return new Pack_Node();} + Type type() const override { return Type::Pack; } + bool is_a(Type inType) const override { return (inType==Type::Pack) ? true : super::is_a(inType); } + Fl_Widget *enter_live_mode(int top=0) override; + void copy_properties() override; +}; + +// ---- Flex_Node --------------------------------------------------- MARK: - + +extern const char flex_type_name[]; +extern Fl_Menu_Item flex_type_menu[]; + +class Fl_Flex_Proxy : public Fl_Flex { +public: + Fl_Flex_Proxy(int X,int Y,int W,int H) : Fl_Flex(X, Y, W, H) { Fl_Group::current(nullptr); } + void resize(int x, int y, int w, int h) override; + void draw() override; +}; + +class Flex_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Flex_Node prototype; +private: + Fl_Menu_Item *subtypes() override {return flex_type_menu;} + int fixedSizeTupleSize; /* number of pairs in array */ + int *fixedSizeTuple; /* [ index, size, index2, size2, ... ] */ + int suspend_auto_layout; +public: + Flex_Node() : fixedSizeTupleSize(0), fixedSizeTuple(nullptr), suspend_auto_layout(0) { } + const char *type_name() override {return flex_type_name;} + const char *alt_type_name() override {return "fltk::FlexGroup";} + Widget_Node *_make() override { return new Flex_Node(); } + Fl_Widget *widget(int X,int Y,int W,int H) override { + Fl_Flex *g = new Fl_Flex_Proxy(X,Y,W,H); Fl_Group::current(nullptr); return g;} + Type type() const override { return Type::Flex; } + bool is_a(Type inType) const override { return (inType==Type::Flex) ? true : super::is_a(inType); } + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + Fl_Widget *enter_live_mode(int top=0) override; + void copy_properties() override; + void copy_properties_for_children() override; + void postprocess_read() override; + void write_code2(fld::io::Code_Writer& f) override; +// void add_child(Node*, Node*) override; +// void move_child(Node*, Node*) override; + void remove_child(Node*) override; + void layout_widget() override; + void change_subtype_to(int n); + void insert_child_at(Fl_Widget *child, int x, int y); + void keyboard_move_child(Widget_Node*, int key); + static int parent_is_flex(Node*); + static int size(Node*, char fixed_only=0); + static int is_fixed(Node*); +}; + +// ---- Table_Node -------------------------------------------------- MARK: - + +class Table_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Table_Node prototype; +public: + void ideal_size(int &w, int &h) override; + const char *type_name() override { return "Fl_Table"; } + const char *alt_type_name() override { return "fltk::TableGroup"; } + Widget_Node *_make() override { return new Table_Node(); } + Fl_Widget *widget(int X, int Y, int W, int H) override; + Type type() const override { return Type::Table; } + bool is_a(Type inType) const override { return (inType==Type::Table) ? true : super::is_a(inType); } + Fl_Widget *enter_live_mode(int top=0) override; + void add_child(Node*, Node*) override; + void move_child(Node*, Node*) override; + void remove_child(Node*) override; +}; + +// ---- Tabs_Node --------------------------------------------------- MARK: - + +extern const char tabs_type_name[]; + +class Fl_Tabs_Proxy : public Fl_Tabs { +public: + Fl_Tabs_Proxy(int X,int Y,int W,int H) : Fl_Tabs(X,Y,W,H) {} + void resize(int,int,int,int) override; + void draw() override; +}; + +class Tabs_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Tabs_Node prototype; +public: + const char *type_name() override {return tabs_type_name;} + const char *alt_type_name() override {return "fltk::TabGroup";} + Fl_Widget *widget(int X,int Y,int W,int H) override { + Fl_Tabs_Proxy *g = new Fl_Tabs_Proxy(X,Y,W,H); Fl_Group::current(nullptr); return g;} + Widget_Node *_make() override {return new Tabs_Node();} + Node* click_test(int,int) override; + void add_child(Node*, Node*) override; + void remove_child(Node*) override; + Type type() const override { return Type::Tabs; } + bool is_a(Type inType) const override { return (inType==Type::Tabs) ? true : super::is_a(inType); } + Fl_Widget *enter_live_mode(int top=0) override; +}; + +// ---- Scroll_Node ------------------------------------------------- MARK: - + +extern const char scroll_type_name[]; +extern Fl_Menu_Item scroll_type_menu[]; + +class Scroll_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Scroll_Node prototype; +private: + Fl_Menu_Item *subtypes() override {return scroll_type_menu;} +public: + const char *type_name() override {return scroll_type_name;} + const char *alt_type_name() override {return "fltk::ScrollGroup";} + Widget_Node *_make() override {return new Scroll_Node();} + Type type() const override { return Type::Scroll; } + bool is_a(Type inType) const override { return (inType==Type::Scroll) ? true : super::is_a(inType); } + Fl_Widget *enter_live_mode(int top=0) override; + void copy_properties() override; +}; + +// ---- Tile_Node --------------------------------------------------- MARK: - + +extern const char tile_type_name[]; + +class Tile_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Tile_Node prototype; +public: + const char *type_name() override {return tile_type_name;} + const char *alt_type_name() override {return "fltk::TileGroup";} + Widget_Node *_make() override {return new Tile_Node();} + Type type() const override { return Type::Tile; } + bool is_a(Type inType) const override { return (inType==Type::Tile) ? true : super::is_a(inType); } + void copy_properties() override; +}; + +// ---- Wizard_Node ------------------------------------------------- MARK: - + +class Fl_Wizard_Proxy : public Fl_Wizard { +public: + Fl_Wizard_Proxy(int X,int Y,int W,int H) : Fl_Wizard(X,Y,W,H) {} + void resize(int,int,int,int) override; + void draw() override; +}; + +extern const char wizard_type_name[]; + +class Wizard_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Wizard_Node prototype; +public: + const char *type_name() override {return wizard_type_name;} + const char *alt_type_name() override {return "fltk::WizardGroup";} + Fl_Widget *widget(int X,int Y,int W,int H) override { + Fl_Wizard_Proxy *g = new Fl_Wizard_Proxy(X,Y,W,H); Fl_Group::current(nullptr); return g;} + Widget_Node *_make() override {return new Wizard_Node();} + Type type() const override { return Type::Wizard; } + bool is_a(Type inType) const override { return (inType==Type::Wizard) ? true : super::is_a(inType); } +}; + +#endif // FLUID_NODES_GROUP_NODE_H diff --git a/fluid/nodes/Menu_Node.cxx b/fluid/nodes/Menu_Node.cxx new file mode 100644 index 000000000..2a76b833a --- /dev/null +++ b/fluid/nodes/Menu_Node.cxx @@ -0,0 +1,876 @@ +// +// Menu Node code for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#include "nodes/Menu_Node.h" + +#include "Fluid.h" +#include "Project.h" +#include "app/Image_Asset.h" +#include "proj/mergeback.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "nodes/Window_Node.h" +#include "widgets/Formula_Input.h" +#include "widgets/Node_Browser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../src/flstring.h" + +#include +#include + +Fl_Menu_Item menu_item_type_menu[] = { + {"Normal",0,nullptr,(void*)nullptr}, + {"Toggle",0,nullptr,(void*)FL_MENU_BOX}, + {"Radio",0,nullptr,(void*)FL_MENU_RADIO}, + {nullptr}}; + +static void delete_dependents(Fl_Menu_Item *m) { + if (!m) + return; + int level = 0; + for (;;m++) { + if (m->label()==nullptr) { + if (level==0) { + break; + } else { + level--; + } + } + if (m->flags&FL_SUBMENU) + level++; + if (m->labeltype()==FL_MULTI_LABEL) + delete (Fl_Multi_Label*)m->label(); + } +} + +static void delete_menu(Fl_Menu_Item *m) { + if (!m) + return; + delete_dependents(m); + delete[] m; +} + +void Input_Choice_Node::build_menu() { + Fl_Input_Choice* w = (Fl_Input_Choice*)o; + // count how many Fl_Menu_Item structures needed: + int n = 0; + Node* q; + for (q = next; q && q->level > level; q = q->next) { + if (q->can_have_children()) n++; // space for null at end of submenu + n++; + } + if (!n) { + if (menusize) delete_menu((Fl_Menu_Item*)(w->menu())); + w->menu(nullptr); + menusize = 0; + } else { + n++; // space for null at end of menu + if (menusizemenu())); + menusize = n+10; + w->menu(new Fl_Menu_Item[menusize]); + } else { + if (menusize) delete_dependents((Fl_Menu_Item*)(w->menu())); + } + // Menus are already built during the .fl file reading process, so if the + // end of a menu list is not read yet, the end markers (label==nullptr) will + // not be set, and deleting dependents will randomly free memory. + // Clearing the array should avoid that. + memset( (void*)w->menu(), 0, menusize * sizeof(Fl_Menu_Item) ); + // fill them all in: + Fl_Menu_Item* m = (Fl_Menu_Item*)(w->menu()); + int lvl = level+1; + for (q = next; q && q->level > level; q = q->next) { + Menu_Item_Node* i = (Menu_Item_Node*)q; + if (i->o->image()) { + if (i->o->label() && i->o->label()[0]) { + Fl_Multi_Label *ml = new Fl_Multi_Label; + ml->labela = (char*)i->o->image(); + ml->labelb = i->o->label(); + ml->typea = FL_IMAGE_LABEL; + ml->typeb = FL_NORMAL_LABEL; + ml->label(m); + } else { + i->o->image()->label(m); + } + } else { + m->label(i->o->label() ? i->o->label() : "(nolabel)"); + m->labeltype(i->o->labeltype()); + } + m->shortcut(((Fl_Button*)(i->o))->shortcut()); + m->callback(nullptr,(void*)i); + m->flags = i->flags(); + m->labelfont(i->o->labelfont()); + m->labelsize(i->o->labelsize()); + m->labelcolor(i->o->labelcolor()); + if (q->can_have_children()) {lvl++; m->flags |= FL_SUBMENU;} + m++; + int l1 = + (q->next && q->next->is_a(Type::Menu_Item)) ? q->next->level : level; + while (lvl > l1) {m->label(nullptr); m++; lvl--;} + lvl = l1; + } + } + o->redraw(); +} + +/** + Create and add a new Menu Item node. + \param[in] strategy add after current or as last child + \return new Menu Item node + */ +Node *Menu_Item_Node::make(Strategy strategy) { + return Menu_Item_Node::make(0, strategy); +} + +/** + Create an add a specific Menu Item node. + \param[in] flags set to 0, FL_MENU_RADIO, FL_MENU_TOGGLE, or FL_SUBMENU + \param[in] strategy add after current or as last child + \return new Menu Item node + */ +Node* Menu_Item_Node::make(int flags, Strategy strategy) { + // Find a good insert position based on the current marked node + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) + p = p->parent; + while (p && !(p->is_a(Type::Menu_Manager_) || p->is_a(Type::Submenu))) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + if (!p) { + fl_message("Please select a menu widget or a menu item"); + return nullptr; + } + if (!o) { + o = new Fl_Button(0,0,100,20); // create template widget + } + + Menu_Item_Node* t = nullptr; + if (flags==FL_SUBMENU) { + t = new Submenu_Node(); + } else { + t = new Menu_Item_Node(); + } + t->o = new Fl_Button(0,0,100,20); + t->o->type(flags); + t->factory = this; + t->add(anchor, strategy); + if (strategy.source() == Strategy::FROM_USER) { + if (flags==FL_SUBMENU) { + t->label("submenu"); + } else { + t->label("item"); + } + } + return t; +} + +void group_selected_menuitems() { + // The group will be created in the parent group of the current menuitem + if (!Fluid.proj.tree.current->is_a(Type::Menu_Item)) { + return; + } + Menu_Item_Node *q = static_cast(Fluid.proj.tree.current); + Node *qq = Fluid.proj.tree.current->parent; + if (!qq || !(qq->is_a(Type::Menu_Manager_) || qq->is_a(Type::Submenu))) { + fl_message("Can't create a new submenu here."); + return; + } + Fluid.proj.undo.checkpoint(); + Fluid.proj.undo.suspend(); + Widget_Node *n = (Widget_Node*)(q->make(FL_SUBMENU, Strategy::AFTER_CURRENT)); + for (Node *t = qq->next; t && (t->level > qq->level);) { + if (t->level != n->level || t == n || !t->selected) { + t = t->next; + continue; + } + Node *nxt = t->remove(); + t->add(n, Strategy::AS_LAST_CHILD); + t = nxt; + } + widget_browser->rebuild(); + Fluid.proj.undo.resume(); + Fluid.proj.set_modflag(1); +} + +void ungroup_selected_menuitems() { + // Find the submenu + Node *qq = Fluid.proj.tree.current->parent; + Widget_Node *q = static_cast(Fluid.proj.tree.current); + int q_level = q->level; + if (!qq || !qq->is_a(Type::Submenu)) { + fl_message("Only menu items inside a submenu can be ungrouped."); + return; + } + Fluid.proj.undo.checkpoint(); + Fluid.proj.undo.suspend(); + Fluid.proj.tree.current = qq; + for (Node *t = qq->next; t && (t->level > qq->level);) { + if (t->level != q_level || !t->selected) { + t = t->next; + continue; + } + Node *nxt = t->remove(); + t->insert(qq); + t = nxt; + } + if (!qq->next || (qq->next->level <= qq->level)) { + qq->remove(); + delete qq; // qq has no children that need to be delete + } + Fluid.proj.tree.current = q; + widget_browser->rebuild(); + Fluid.proj.undo.resume(); + Fluid.proj.set_modflag(1); +} + + +/** + Create and add a new Checkbox Menu Item node. + \param[in] strategy add after current or as last child + \return new node + */ +Node *Checkbox_Menu_Item_Node::make(Strategy strategy) { + return Menu_Item_Node::make(FL_MENU_TOGGLE, strategy); +} + +/** + Create and add a new Radio ButtonMenu Item node. + \param[in] strategy add after current or as last child + \return new node + */ +Node *Radio_Menu_Item_Node::make(Strategy strategy) { + return Menu_Item_Node::make(FL_MENU_RADIO, strategy); +} + +/** + Create and add a new Submenu Item node. + \param[in] strategy add after current or as last child + \return new node + */ +Node *Submenu_Node::make(Strategy strategy) { + return Menu_Item_Node::make(FL_SUBMENU, strategy); +} + +Menu_Item_Node Menu_Item_Node::prototype; + +Checkbox_Menu_Item_Node Checkbox_Menu_Item_Node::prototype; + +Radio_Menu_Item_Node Radio_Menu_Item_Node::prototype; + +Submenu_Node Submenu_Node::prototype; + +//////////////////////////////////////////////////////////////// +// Writing the C code: + +// test functions in Widget_Node.C: +int is_name(const char *c); +const char *array_name(Widget_Node *o); +int isdeclare(const char *c); + +// Search backwards to find the parent menu button and return it's name. +// Also put in i the index into the button's menu item array belonging +// to this menu item. +const char* Menu_Item_Node::menu_name(fld::io::Code_Writer& f, int& i) { + i = 0; + Node* t = prev; + while (t && t->is_a(Type::Menu_Item)) { + // be sure to count the {0} that ends a submenu: + if (t->level > t->next->level) i += (t->level - t->next->level); + // detect empty submenu: + else if (t->level == t->next->level && t->can_have_children()) i++; + t = t->prev; + i++; + } + if (!t) return "\n#error Menu_Item_Node::menu_name, invalid f\n"; + return f.unique_id(t, "menu", t->name(), t->label()); +} + +void Menu_Item_Node::write_static(fld::io::Code_Writer& f) { + if (image && label() && label()[0]) { + f.write_h_once("#include "); + f.write_h_once("#include "); + } + if (callback() && is_name(callback()) && !user_defined(callback())) + f.write_h_once("extern void %s(Fl_Menu_*, %s);", callback(), + user_data_type() ? user_data_type() : "void*"); + for (int n=0; n < NUM_EXTRA_CODE; n++) { + if (extra_code(n) && isdeclare(extra_code(n))) + f.write_h_once("%s", extra_code(n)); + } + if (callback() && !is_name(callback())) { + // see if 'o' or 'v' used, to prevent unused argument warnings: + int use_o = 0; + int use_v = 0; + const char *d; + for (d = callback(); *d;) { + if (*d == 'o' && !is_id(d[1])) use_o = 1; + if (*d == 'v' && !is_id(d[1])) use_v = 1; + do d++; while (is_id(*d)); + while (*d && !is_id(*d)) d++; + } + const char* cn = callback_name(f); + const char* k = class_name(1); + if (k) { + f.write_c("\nvoid %s::%s_i(Fl_Menu_*", k, cn); + } else { + f.write_c("\nstatic void %s(Fl_Menu_*", cn); + } + if (use_o) f.write_c(" o"); + const char* ut = user_data_type() ? user_data_type() : "void*"; + f.write_c(", %s", ut); + if (use_v) f.write_c(" v"); + f.write_c(") {\n"); + // Matt: disabled f.tag(FD_TAG_GENERIC, 0); + f.write_c_indented(callback(), 1, 0); + if (*(d-1) != ';' && *(d-1) != '}') { + const char *p = strrchr(callback(), '\n'); + if (p) p ++; + else p = callback(); + // Only add trailing semicolon if the last line is not a preprocessor + // statement... + if (*p != '#' && *p) f.write_c(";"); + } + f.write_c("\n"); + // Matt: disabled f.tag(FD_TAG_MENU_CALLBACK, get_uid()); + f.write_c("}\n"); + + // If the menu item is part of a Class or Widget Class, FLUID generates + // a dummy static callback which retrieves a pointer to the class and then + // calls the original callback from within the class context. + // k is the name of the enclosing class (or classes) + if (k) { + // Implement the callback as a static member function + f.write_c("void %s::%s(Fl_Menu_* o, %s v) {\n", k, cn, ut); + // Find the Fl_Menu_ container for this menu item + Node* t = parent; while (t->is_a(Type::Menu_Item)) t = t->parent; + if (t) { + Widget_Node *tw = (t->is_widget()) ? static_cast(t) : nullptr; + Node *q = nullptr; + // Generate code to call the callback + if (tw->is_a(Type::Menu_Bar) && ((Menu_Bar_Node*)tw)->is_sys_menu_bar()) { + // Fl_Sys_Menu_Bar removes itself from any parent on macOS, so we + // wrapped it in a class and remeber the parent class in a new + // class memeber variable. + Menu_Bar_Node *tmb = (Menu_Bar_Node*)tw; + f.write_c("%s%s* sys_menu_bar = ((%s*)o);\n", f.indent(1), + tmb->sys_menubar_proxy_name(), tmb->sys_menubar_proxy_name()); + f.write_c("%s%s* parent_class = ((%s*)sys_menu_bar->_parent_class);\n", + f.indent(1), k, k); + f.write_c("%sparent_class->%s_i(o,v);\n}\n", + f.indent(1), cn); + } else { + f.write_c("%s((%s*)(o", f.indent(1), k); + // The class pointer is in the user_data field of the top widget + if (t && t->is_a(Type::Input_Choice)) { + // Go up one more level for Fl_Input_Choice, as these are groups themselves + f.write_c("->parent()"); + } + // Now generate code to find the topmost widget in this class + for (t = t->parent; t && t->is_widget() && !is_class(); q = t, t = t->parent) + f.write_c("->parent()"); + // user_data is cast into a pointer to the + if (!q || !q->is_a(Type::Widget_Class)) + f.write_c("->user_data()"); + f.write_c("))->%s_i(o,v);\n}\n", cn); + } + } else { + f.write_c("#error Enclosing Fl_Menu_* not found\n"); + } + } + } + if (image) { + if (!f.c_contains(image)) + image->write_static(f, compress_image_); + } + if (next && next->is_a(Type::Menu_Item)) return; + // okay, when we hit last item in the menu we have to write the + // entire array out: + const char* k = class_name(1); + if (k) { + int i; + f.write_c("\nFl_Menu_Item %s::%s[] = {\n", k, menu_name(f, i)); + } else { + int i; + f.write_c("\nFl_Menu_Item %s[] = {\n", menu_name(f, i)); + } + Node* t = prev; while (t && t->is_a(Type::Menu_Item)) t = t->prev; + for (Node* q = t->next; q && q->is_a(Type::Menu_Item); q = q->next) { + ((Menu_Item_Node*)q)->write_item(f); + int thislevel = q->level; if (q->can_have_children()) thislevel++; + int nextlevel = + (q->next && q->next->is_a(Type::Menu_Item)) ? q->next->level : t->level+1; + while (thislevel > nextlevel) {f.write_c(" {0,0,0,0,0,0,0,0,0},\n"); thislevel--;} + } + f.write_c(" {0,0,0,0,0,0,0,0,0}\n};\n"); + + if (k) { + // Write menu item variables... + t = prev; while (t && t->is_a(Type::Menu_Item)) t = t->prev; + for (Node* q = t->next; q && q->is_a(Type::Menu_Item); q = q->next) { + Menu_Item_Node *m = (Menu_Item_Node*)q; + const char *c = array_name(m); + if (c) { + if (c==m->name()) { + // assign a menu item address directly to a variable + int i; + const char* n = ((Menu_Item_Node *)q)->menu_name(f, i); + f.write_c("Fl_Menu_Item* %s::%s = %s::%s + %d;\n", k, c, k, n, i); + } else { + // if the name is an array, only define the array. + // The actual assignment is in write_code1(fld::io::Code_Writer& f) + f.write_c("Fl_Menu_Item* %s::%s;\n", k, c); + } + } + } + } +} + +int Menu_Item_Node::flags() { + int i = o->type(); + if (((Fl_Button*)o)->value()) i |= FL_MENU_VALUE; + if (!o->active()) i |= FL_MENU_INACTIVE; + if (!o->visible()) i |= FL_MENU_INVISIBLE; + if (can_have_children()) { + if (user_data() == nullptr) i |= FL_SUBMENU; + else i |= FL_SUBMENU_POINTER; + } + if (hotspot()) i |= FL_MENU_DIVIDER; + return i; +} + +void Menu_Item_Node::write_item(fld::io::Code_Writer& f) { + static const char * const labeltypes[] = { + "FL_NORMAL_LABEL", + "FL_NO_LABEL", + "FL_SHADOW_LABEL", + "FL_ENGRAVED_LABEL", + "FL_EMBOSSED_LABEL", + "FL_MULTI_LABEL", + "FL_ICON_LABEL", + "FL_IMAGE_LABEL" + }; + + write_comment_inline_c(f, " "); + f.write_c(" {"); + if (label() && label()[0]) + switch (Fluid.proj.i18n_type) { + case fld::I18n_Type::GNU: + // we will call i18n when the menu is instantiated for the first time + f.write_c("%s(", Fluid.proj.i18n_gnu_static_function.c_str()); + f.write_cstring(label()); + f.write_c(")"); + break; + case fld::I18n_Type::POSIX: + // fall through: strings can't be translated before a catalog is chosen + default: + f.write_cstring(label()); + } + else + f.write_c("\"\""); + if (((Fl_Button*)o)->shortcut()) { + int s = ((Fl_Button*)o)->shortcut(); + f.write_c(", "); + if (Fluid.proj.use_FL_COMMAND) { + if (s & FL_CTRL) { f.write_c("FL_CONTROL|"); s &= ~FL_CTRL; } + if (s & FL_META) { f.write_c("FL_COMMAND|"); s &= ~FL_META; } + } else { + if (s & FL_CTRL) { f.write_c("FL_CTRL|"); s &= ~FL_CTRL; } + if (s & FL_META) { f.write_c("FL_META|"); s &= ~FL_META; } + } + if (s & FL_SHIFT) { f.write_c("FL_SHIFT|"); s &= ~FL_SHIFT; } + if (s & FL_ALT) { f.write_c("FL_ALT|"); s &= ~FL_ALT; } + if ((s < 127) && isprint(s)) + f.write_c("'%c', ", s); + else + f.write_c("0x%x, ", s); + } else { + f.write_c(", 0, "); + } + if (callback()) { + const char* k = is_name(callback()) ? nullptr : class_name(1); + if (k) { + f.write_c(" (Fl_Callback*)%s::%s,", k, callback_name(f)); + } else { + f.write_c(" (Fl_Callback*)%s,", callback_name(f)); + } + } else + f.write_c(" 0,"); + if (user_data()) + f.write_c(" (void*)(%s),", user_data()); + else + f.write_c(" 0,"); + f.write_c(" %d, (uchar)%s, %d, %d, %d", flags(), + labeltypes[o->labeltype()], o->labelfont(), o->labelsize(), o->labelcolor()); + f.write_c("},\n"); +} + +void start_menu_initialiser(fld::io::Code_Writer& f, int &initialized, const char *name, int index) { + if (!initialized) { + initialized = 1; + f.write_c("%s{ Fl_Menu_Item* o = &%s[%d];\n", f.indent(), name, index); + f.indentation++; + } +} + +void Menu_Item_Node::write_code1(fld::io::Code_Writer& f) { + int i; const char* mname = menu_name(f, i); + + if (!prev->is_a(Type::Menu_Item)) { + // for first menu item, declare the array + if (class_name(1)) { + f.write_h("%sstatic Fl_Menu_Item %s[];\n", f.indent(1), mname); + } else { + f.write_h("extern Fl_Menu_Item %s[];\n", mname); + } + } + + const char *c = array_name(this); + if (c) { + if (class_name(1)) { + f.write_public(public_); + f.write_h("%sstatic Fl_Menu_Item *%s;\n", f.indent(1), c); + } else { + if (c==name()) + f.write_h("#define %s (%s+%d)\n", c, mname, i); + else + f.write_h("extern Fl_Menu_Item *%s;\n", c); + } + } + + if (callback()) { + if (!is_name(callback()) && class_name(1)) { + const char* cn = callback_name(f); + const char* ut = user_data_type() ? user_data_type() : "void*"; + f.write_public(0); + f.write_h("%sinline void %s_i(Fl_Menu_*, %s);\n", f.indent(1), cn, ut); + f.write_h("%sstatic void %s(Fl_Menu_*, %s);\n", f.indent(1), cn, ut); + } + } + + int menuItemInitialized = 0; + // if the name is an array variable, assign the value here + if (name() && strchr(name(), '[')) { + f.write_c("%s%s = &%s[%d];\n", f.indent_plus(1), name(), mname, i); + } + if (image) { + start_menu_initialiser(f, menuItemInitialized, mname, i); + if (label() && label()[0]) { + f.write_c("%sFl_Multi_Label *ml = new Fl_Multi_Label;\n", f.indent()); + f.write_c("%sml->labela = (char*)", f.indent()); + image->write_inline(f); + f.write_c(";\n"); + if (Fluid.proj.i18n_type==fld::I18n_Type::NONE) { + f.write_c("%sml->labelb = o->label();\n", f.indent()); + } else if (Fluid.proj.i18n_type==fld::I18n_Type::GNU) { + f.write_c("%sml->labelb = %s(o->label());\n", + f.indent(), Fluid.proj.i18n_gnu_function.c_str()); + } else if (Fluid.proj.i18n_type==fld::I18n_Type::POSIX) { + f.write_c("%sml->labelb = catgets(%s,%s,i+%d,o->label());\n", + f.indent(), + Fluid.proj.i18n_pos_file.empty() ? "_catalog" : Fluid.proj.i18n_pos_file.c_str(), + Fluid.proj.i18n_pos_set.c_str(), msgnum()); + } + f.write_c("%sml->typea = FL_IMAGE_LABEL;\n", f.indent()); + f.write_c("%sml->typeb = FL_NORMAL_LABEL;\n", f.indent()); + f.write_c("%sml->label(o);\n", f.indent()); + } else { + image->write_code(f, 0, "o"); + } + } + if ((Fluid.proj.i18n_type != fld::I18n_Type::NONE) && label() && label()[0]) { + Fl_Labeltype t = o->labeltype(); + if (image) { + // label was already copied a few lines up + } else if ( t==FL_NORMAL_LABEL || t==FL_SHADOW_LABEL + || t==FL_ENGRAVED_LABEL || t==FL_EMBOSSED_LABEL) { + start_menu_initialiser(f, menuItemInitialized, mname, i); + if (Fluid.proj.i18n_type==fld::I18n_Type::GNU) { + f.write_c("%so->label(%s(o->label()));\n", + f.indent(), Fluid.proj.i18n_gnu_function.c_str()); + } else if (Fluid.proj.i18n_type==fld::I18n_Type::POSIX) { + f.write_c("%so->label(catgets(%s,%s,i+%d,o->label()));\n", + f.indent(), + Fluid.proj.i18n_pos_file.empty() ? "_catalog" : Fluid.proj.i18n_pos_file.c_str(), + Fluid.proj.i18n_pos_set.c_str(), msgnum()); + } + } + } + for (int n=0; n < NUM_EXTRA_CODE; n++) { + if (extra_code(n) && !isdeclare(extra_code(n))) { + start_menu_initialiser(f, menuItemInitialized, mname, i); + f.write_c("%s%s\n", f.indent(), extra_code(n)); + } + } + if (menuItemInitialized) { + f.indentation--; + f.write_c("%s}\n",f.indent()); + } +} + +void Menu_Item_Node::write_code2(fld::io::Code_Writer&) {} + +//////////////////////////////////////////////////////////////// +// This is the base class for widgets that contain a menu (ie +// subclasses of Fl_Menu_. +// This is a parent widget and menu items can be added as +// children. An actual array of Fl_Menu_Items is kept parallel +// with the child objects and updated as they change. + +void Menu_Base_Node::build_menu() { + Fl_Menu_* w = (Fl_Menu_*)o; + // count how many Fl_Menu_Item structures needed: + int n = 0; + Node* q; + for (q = next; q && q->level > level; q = q->next) { + if (q->can_have_children()) n++; // space for null at end of submenu + n++; + } + if (!n) { + if (menusize) delete_menu((Fl_Menu_Item*)(w->menu())); + w->menu(nullptr); + menusize = 0; + } else { + n++; // space for null at end of menu + if (menusizemenu())); + menusize = n+10; + w->menu(new Fl_Menu_Item[menusize]); + } else { + if (menusize) delete_dependents((Fl_Menu_Item*)(w->menu())); + } + // Menus are already built during the .fl file reading process, so if the + // end of a menu list is not read yet, the end markers (label==nullptr) will + // not be set, and deleting dependents will randomly free memory. + // Clearing the array should avoid that. + memset( (void*)w->menu(), 0, menusize * sizeof(Fl_Menu_Item) ); + // fill them all in: + Fl_Menu_Item* m = (Fl_Menu_Item*)(w->menu()); + int lvl = level+1; + for (q = next; q && q->level > level; q = q->next) { + Menu_Item_Node* i = (Menu_Item_Node*)q; + if (i->o->image()) { + if (i->o->label() && i->o->label()[0]) { + Fl_Multi_Label *ml = new Fl_Multi_Label; + ml->labela = (char*)i->o->image(); + ml->labelb = i->o->label(); + ml->typea = FL_IMAGE_LABEL; + ml->typeb = FL_NORMAL_LABEL; + ml->label(m); + } else { + i->o->image()->label(m); + } + } else { + m->label(i->o->label() ? i->o->label() : "(nolabel)"); + m->labeltype(i->o->labeltype()); + } + m->shortcut(((Fl_Button*)(i->o))->shortcut()); + m->callback(nullptr,(void*)i); + m->flags = i->flags() | i->o->type(); + m->labelfont(i->o->labelfont()); + m->labelsize(i->o->labelsize()); + m->labelcolor(i->o->labelcolor()); + if (q->can_have_children()) {lvl++; m->flags |= FL_SUBMENU;} + m++; + int l1 = + (q->next && q->next->is_a(Type::Menu_Item)) ? q->next->level : level; + while (lvl > l1) {m->label(nullptr); m++; lvl--;} + lvl = l1; + } + } + o->redraw(); +} + +Node* Menu_Base_Node::click_test(int, int) { + if (selected) return nullptr; // let user move the widget + Fl_Menu_* w = (Fl_Menu_*)o; + if (!menusize) return nullptr; + const Fl_Menu_Item* save = w->mvalue(); + w->value((Fl_Menu_Item*)nullptr); + Fl::pushed(w); + w->handle(FL_PUSH); + Fl::focus(nullptr); + const Fl_Menu_Item* m = w->mvalue(); + if (m) { + // restore the settings of toggles & radio items: + if (m->flags & (FL_MENU_RADIO | FL_MENU_TOGGLE)) build_menu(); + return (Node*)(m->user_data()); + } + w->value(save); + return this; +} + +void Menu_Manager_Node::write_code2(fld::io::Code_Writer& f) { + if (next && next->is_a(Type::Menu_Item)) { + f.write_c("%s%s->menu(%s);\n", f.indent(), name() ? name() : "o", + f.unique_id(this, "menu", name(), label())); + } + Widget_Node::write_code2(f); +} + +void Menu_Base_Node::copy_properties() { + Widget_Node::copy_properties(); + Fl_Menu_ *s = (Fl_Menu_*)o, *d = (Fl_Menu_*)live_widget; + d->menu(s->menu()); + d->down_box(s->down_box()); + d->textcolor(s->textcolor()); + d->textfont(s->textfont()); + d->textsize(s->textsize()); +} + +//////////////////////////////////////////////////////////////// + +Fl_Menu_Item button_type_menu[] = { + {"normal",0,nullptr,(void*)nullptr}, + {"popup1",0,nullptr,(void*)Fl_Menu_Button::POPUP1}, + {"popup2",0,nullptr,(void*)Fl_Menu_Button::POPUP2}, + {"popup3",0,nullptr,(void*)Fl_Menu_Button::POPUP3}, + {"popup12",0,nullptr,(void*)Fl_Menu_Button::POPUP12}, + {"popup23",0,nullptr,(void*)Fl_Menu_Button::POPUP23}, + {"popup13",0,nullptr,(void*)Fl_Menu_Button::POPUP13}, + {"popup123",0,nullptr,(void*)Fl_Menu_Button::POPUP123}, + {nullptr}}; + +Menu_Button_Node Menu_Button_Node::prototype; + +//////////////////////////////////////////////////////////////// + +Fl_Menu_Item dummymenu[] = {{"CHOICE"},{nullptr}}; + +Choice_Node Choice_Node::prototype; + +Input_Choice_Node Input_Choice_Node::prototype; + +void Input_Choice_Node::copy_properties() { + Widget_Node::copy_properties(); + Fl_Input_Choice *s = (Fl_Input_Choice*)o, *d = (Fl_Input_Choice*)live_widget; + d->menu(s->menu()); + d->down_box(s->down_box()); + d->textcolor(s->textcolor()); + d->textfont(s->textfont()); + d->textsize(s->textsize()); +} + +Node* Input_Choice_Node::click_test(int, int) { + if (selected) return nullptr; // let user move the widget + Fl_Menu_* w = ((Fl_Input_Choice*)o)->menubutton(); + if (!menusize) return nullptr; + const Fl_Menu_Item* save = w->mvalue(); + w->value((Fl_Menu_Item*)nullptr); + Fl::pushed(w); + w->handle(FL_PUSH); + Fl::focus(nullptr); + const Fl_Menu_Item* m = w->mvalue(); + if (m) { + // restore the settings of toggles & radio items: + if (m->flags & (FL_MENU_RADIO | FL_MENU_TOGGLE)) build_menu(); + return (Node*)(m->user_data()); + } + w->value(save); + return this; +} + +//////////////////////////////////////////////////////////////// + +Menu_Bar_Node Menu_Bar_Node::prototype; + +Fl_Menu_Item menu_bar_type_menu[] = { + {"Fl_Menu_Bar",0,nullptr,(void*)nullptr}, + {"Fl_Sys_Menu_Bar",0,nullptr,(void*)1}, + {nullptr}}; + +Menu_Bar_Node::Menu_Bar_Node() +: _proxy_name(nullptr) +{ +} + +Menu_Bar_Node::~Menu_Bar_Node() { + if (_proxy_name) + ::free(_proxy_name); +} + +/** + \brief Return true if this is an Fl_Sys_Menu_Bar. + This test fails if subclass() is the name of a class that the user may have + derived from Fl_Sys_Menu_Bar. + */ +bool Menu_Bar_Node::is_sys_menu_bar() { + if (o->type()==1) return true; + return ( subclass() && (strcmp(subclass(), "Fl_Sys_Menu_Bar")==0) ); +} + +const char *Menu_Bar_Node::sys_menubar_name() { + if (subclass()) + return subclass(); + else + return "Fl_Sys_Menu_Bar"; +} + +const char *Menu_Bar_Node::sys_menubar_proxy_name() { + if (!_proxy_name) + _proxy_name = (char*)::malloc(128); + ::snprintf(_proxy_name, 63, "%s_Proxy", sys_menubar_name()); + return _proxy_name; +} + + +void Menu_Bar_Node::write_static(fld::io::Code_Writer& f) { + super::write_static(f); + if (is_sys_menu_bar()) { + f.write_h_once("#include "); + if (is_in_class()) { + // Make room for a pointer to the enclosing class. + f.write_c_once( // must be less than 1024 bytes! + "\nclass %s: public %s {\n" + "public:\n" + " %s(int x, int y, int w, int h, const char *l=0L)\n" + " : %s(x, y, w, h, l) { }\n" + " void *_parent_class;\n" + "};\n", + sys_menubar_proxy_name(), sys_menubar_name(), + sys_menubar_proxy_name(), sys_menubar_name() + ); + } + } +} + +void Menu_Bar_Node::write_code1(fld::io::Code_Writer& f) { + super::write_code1(f); + if (is_sys_menu_bar() && is_in_class()) { + f.write_c("%s((%s*)%s)->_parent_class = (void*)this;\n", + f.indent(), sys_menubar_proxy_name(), name() ? name() : "o"); + } +} + +//void Menu_Bar_Node::write_code2(fld::io::Code_Writer& f) { +// super::write_code2(f); +//} + diff --git a/fluid/nodes/Menu_Node.h b/fluid/nodes/Menu_Node.h new file mode 100644 index 000000000..59d3b06bb --- /dev/null +++ b/fluid/nodes/Menu_Node.h @@ -0,0 +1,310 @@ +// +// Menu Node header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +// Type for creating all subclasses of Fl_Widget +// This should have the widget pointer in it, but it is still in the +// Node base class. +// + + +#ifndef FLUID_NODES_MENU_NODE_H +#define FLUID_NODES_MENU_NODE_H + +#include "nodes/Button_Node.h" + +#include "Fluid.h" +#include "app/Snap_Action.h" + +#include +#include +#include +#include +#include +#include + +extern Fl_Menu_Item dummymenu[]; +extern Fl_Menu_Item button_type_menu[]; +extern Fl_Menu_Item menu_item_type_menu[]; +extern Fl_Menu_Item menu_bar_type_menu[]; + +/** + \brief Manage all types on menu items. + Deriving Menu_Item_Node from Button_Node is intentional. For the purpose + of editing, a Menu Item is implemented with `o` pointing to an Fl_Button for + holding all properties. + */ +class Menu_Item_Node : public Button_Node +{ +public: + typedef Button_Node super; + static Menu_Item_Node prototype; +public: + Fl_Menu_Item* subtypes() override {return menu_item_type_menu;} + const char* type_name() override {return "MenuItem";} + const char* alt_type_name() override {return "fltk::Item";} + Node* make(Strategy strategy) override; + Node* make(int flags, Strategy strategy); + int is_button() const override {return 1;} // this gets shortcut to work + Fl_Widget* widget(int,int,int,int) override {return nullptr;} + Widget_Node* _make() override {return nullptr;} + virtual const char* menu_name(fld::io::Code_Writer& f, int& i); + int flags(); + void write_static(fld::io::Code_Writer& f) override; + void write_item(fld::io::Code_Writer& f); + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + int is_true_widget() const override { return 0; } + Type type() const override { return Type::Menu_Item; } + bool is_a(Type inType) const override { return (inType==Type::Menu_Item) ? true : super::is_a(inType); } +}; + +/** + \brief Manage Radio style Menu Items. + */ +class Radio_Menu_Item_Node : public Menu_Item_Node +{ +public: + typedef Menu_Item_Node super; + static Radio_Menu_Item_Node prototype; +public: + const char* type_name() override {return "RadioMenuItem";} + Node* make(Strategy strategy) override; + Type type() const override { return Type::Radio_Menu_Item; } + bool is_a(Type inType) const override { return (inType==Type::Radio_Menu_Item) ? true : super::is_a(inType); } +}; + +/** + \brief Manage Checkbox style Menu Items. + */ +class Checkbox_Menu_Item_Node : public Menu_Item_Node +{ +public: + typedef Menu_Item_Node super; + static Checkbox_Menu_Item_Node prototype; +public: + const char* type_name() override {return "CheckMenuItem";} + Node* make(Strategy strategy) override; + Type type() const override { return Type::Checkbox_Menu_Item; } + bool is_a(Type inType) const override { return (inType==Type::Checkbox_Menu_Item) ? true : super::is_a(inType); } +}; + +/** + \brief Manage Submenu style Menu Items. + Submenu Items are simply buttons just like all other menu items, but they + can also hold a pointer to a list of submenus, or have a flag set that + allows submenus to follow in the current array. As buttons, they can + be clicked by the user, and they will call their callback, if one is set. + */ +class Submenu_Node : public Menu_Item_Node +{ +public: + typedef Menu_Item_Node super; + static Submenu_Node prototype; +public: + Fl_Menu_Item* subtypes() override {return nullptr;} + const char* type_name() override {return "Submenu";} + const char* alt_type_name() override {return "fltk::ItemGroup";} + int can_have_children() const override {return 1;} + int is_button() const override {return 0;} // disable shortcut + Node* make(Strategy strategy) override; + // changes to submenu must propagate up so build_menu is called + // on the parent Menu_Node: + void add_child(Node*a, Node*b) override {parent->add_child(a,b);} + void move_child(Node*a, Node*b) override {parent->move_child(a,b);} + void remove_child(Node*a) override {parent->remove_child(a);} + Type type() const override { return Type::Submenu; } + bool is_a(Type inType) const override { return (inType==Type::Submenu) ? true : super::is_a(inType); } +}; + +// ----------------------------------------------------------------------------- + +/** + \brief Base class for all widgets that can have a pulldown menu attached. + Widgets with this type can be derived from Fl_Menu_ or from + Fl_Group (Fl_Input_Choice). + */ +class Menu_Manager_Node : public Widget_Node +{ + typedef Widget_Node super; +public: + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; + h = layout->textsize_not_null() + 8; + w = layout->textsize_not_null() * 6 + 8; + fld::app::Snap_Action::better_size(w, h); + } + int can_have_children() const override {return 1;} + int menusize; + virtual void build_menu() = 0; + Menu_Manager_Node() : Widget_Node() {menusize = 0;} + void add_child(Node*, Node*) override { build_menu(); } + void move_child(Node*, Node*) override { build_menu(); } + void remove_child(Node*) override { build_menu();} + Node* click_test(int x, int y) override = 0; + void write_code2(fld::io::Code_Writer& f) override; + void copy_properties() override = 0; + Type type() const override { return Type::Menu_Manager_; } + bool is_a(Type inType) const override { return (inType==Type::Menu_Manager_) ? true : super::is_a(inType); } +}; + +/** + \brief Manage the composite widget Input Choice. + \note Input Choice is a composite window, so `o` will be pointing to a widget + derived from Fl_Group. All menu related methods from Fl_Menu_Trait_Type must + be virtual and must be reimplemented here (click_test, build_menu, textstuff). + */ +class Input_Choice_Node : public Menu_Manager_Node +{ +public: + typedef Menu_Manager_Node super; + static Input_Choice_Node prototype; +private: + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Input_Choice *myo = (Fl_Input_Choice*)(w==4 ? ((Widget_Node*)this->factory)->o : this->o); + switch (w) { + case 4: + case 0: f = (Fl_Font)myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; + case 1: myo->textfont(f); break; + case 2: myo->textsize(s); break; + case 3: myo->textcolor(c); break; + } + return 1; + } +public: + ~Input_Choice_Node() { + if (menusize) delete[] (Fl_Menu_Item*)(((Fl_Input_Choice*)o)->menu()); + } + const char *type_name() override {return "Fl_Input_Choice";} + const char *alt_type_name() override {return "fltk::ComboBox";} + Node* click_test(int,int) override; + Fl_Widget *widget(int X,int Y,int W,int H) override { + Fl_Input_Choice *myo = new Fl_Input_Choice(X,Y,W,H,"input choice:"); + myo->menu(dummymenu); + myo->value("input"); + return myo; + } + Widget_Node *_make() override {return new Input_Choice_Node();} + void build_menu() override; + Type type() const override { return Type::Input_Choice; } + bool is_a(Type inType) const override { return (inType==Type::Input_Choice) ? true : super::is_a(inType); } + void copy_properties() override; +}; + +/** + \brief Base class to handle widgets that are derived from Fl_Menu_. + */ +class Menu_Base_Node : public Menu_Manager_Node +{ + typedef Menu_Manager_Node super; + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Menu_ *myo = (Fl_Menu_*)(w==4 ? ((Widget_Node*)this->factory)->o : this->o); + switch (w) { + case 4: + case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; + case 1: myo->textfont(f); break; + case 2: myo->textsize(s); break; + case 3: myo->textcolor(c); break; + } + return 1; + } +public: + int can_have_children() const override {return 1;} + void build_menu() override; + ~Menu_Base_Node() { + if (menusize) delete[] (Fl_Menu_Item*)(((Fl_Menu_*)o)->menu()); + } + Node* click_test(int x, int y) override; + void copy_properties() override; + Type type() const override { return Type::Menu_; } + bool is_a(Type inType) const override { return (inType==Type::Menu_) ? true : super::is_a(inType); } +}; + +extern Fl_Menu_Item button_type_menu[]; + +/** + \brief Make Menu Button widgets. + */ +class Menu_Button_Node : public Menu_Base_Node +{ +public: + typedef Menu_Base_Node super; + static Menu_Button_Node prototype; +private: + Fl_Menu_Item *subtypes() override {return button_type_menu;} +public: + const char *type_name() override {return "Fl_Menu_Button";} + const char *alt_type_name() override {return "fltk::MenuButton";} + Fl_Widget *widget(int X,int Y,int W,int H) override { + return new Fl_Menu_Button(X,Y,W,H,"menu");} + Widget_Node *_make() override {return new Menu_Button_Node();} + Type type() const override { return Type::Menu_Button; } + bool is_a(Type inType) const override { return (inType==Type::Menu_Button) ? true : super::is_a(inType); } +}; + + +/** + \brief Manage Choice type menu widgets. + */ +class Choice_Node : public Menu_Base_Node +{ +public: + typedef Menu_Base_Node super; + static Choice_Node prototype; +public: + const char *type_name() override {return "Fl_Choice";} + const char *alt_type_name() override {return "fltk::Choice";} + Fl_Widget *widget(int X,int Y,int W,int H) override { + Fl_Choice *myo = new Fl_Choice(X,Y,W,H,"choice:"); + myo->menu(dummymenu); + return myo; + } + Widget_Node *_make() override {return new Choice_Node();} + Type type() const override { return Type::Choice; } + bool is_a(Type inType) const override { return (inType==Type::Choice) ? true : super::is_a(inType); } +}; + + +/** + \brief Manage Menubar widgets. + */ +class Menu_Bar_Node : public Menu_Base_Node +{ +public: + typedef Menu_Base_Node super; + static Menu_Bar_Node prototype; +private: + Fl_Menu_Item *subtypes() override {return menu_bar_type_menu;} +public: + Menu_Bar_Node(); + ~Menu_Bar_Node() override; + const char *type_name() override {return "Fl_Menu_Bar";} + const char *alt_type_name() override {return "fltk::MenuBar";} + Fl_Widget *widget(int X,int Y,int W,int H) override {return new Fl_Menu_Bar(X,Y,W,H);} + Widget_Node *_make() override {return new Menu_Bar_Node();} + void write_static(fld::io::Code_Writer& f) override; + void write_code1(fld::io::Code_Writer& f) override; +// void write_code2(fld::io::Code_Writer& f) override; + Type type() const override { return Type::Menu_Bar; } + bool is_a(Type inType) const override { return (inType==Type::Menu_Bar) ? true : super::is_a(inType); } + bool is_sys_menu_bar(); + const char *sys_menubar_name(); + const char *sys_menubar_proxy_name(); +protected: + char *_proxy_name; +}; + + +#endif // FLUID_NODES_MENU_NODE_H diff --git a/fluid/nodes/Node.cxx b/fluid/nodes/Node.cxx new file mode 100644 index 000000000..194484a93 --- /dev/null +++ b/fluid/nodes/Node.cxx @@ -0,0 +1,1290 @@ +// +// Node base class code for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +/// \defgroup fl_type Basic Node for all Widgets and Functions +/// \{ + +/** \class Node + Each object described by Fluid is one of these objects. They + are all stored in a double-linked list. + + The "type" of the object is covered by the virtual functions. + There will probably be a lot of these virtual functions. + + The type browser is also a list of these objects, but they + are "factory" instances, not "real" ones. These objects exist + only so the "make" method can be called on them. They are + not in the linked list and are not written to files or + copied or otherwise examined. + + The Node inheritance is currently: + --+-- Node + +-- Function_Node + +-- Code_Node + +-- CodeBlock_Node + +-+ Decl_Node + | +-- Fl_Data + +-- DeclBlock_Node + +-- Comment_Node + +-- Class_Node + +-+ Widget_Node, 'o' points to a class derived from Fl_Widget + +-+ Browser_Base_Node, 'o' is Fl_Browser + | +-+ Fl_Browser + | | +-- Fl_File_Browser + | +-- Fl_Check_Browser + +-- Tree_Node + +-- Help_View_Node + +-+ Valuator_Node, 'o' is Fl_Valuator_ + | +-- Counter_Node + | +-- Adjuster_Node + | +-- Dial_Node + | +-- Roller_Node + | +-- Slider_Node + | +-- Value_Input_Node + | +-- Value_Output_Node + +-+ Input_Node + | +-- Output_Node + +-+ Text_Display_Node + | +-- Text_Editor_Node + +-- Terminal_Node + +-- Box_Node + +-- Clock_Node + +-- Progress_Node + +-- Spinner_Node + +-+ Group_Node + | +-- Pack_Node + | +-- Flex_Node + | +-- Grid_Node + | +-- Table_Node + | +-- Tabs_Node + | +-- Scroll_Node + | +-- Tile_Node + | +-- Wizard_Node + | +-+ Window_Node + | +-- Widget_Class_Node + +-+ Menu_Manager_Node, 'o' is based on Fl_Widget + | +-+ Menu_Base_Node, 'o' is based on Fl_Menu_ + | | +-- Menu_Button_Node + | | +-- Choice_Node + | | +-- Menu_Bar_Node + | +-- Input_Choice_Node, 'o' is based on Fl_Input_Choice which is Fl_Group + +-+ Button_Node + +-- Return_Button_Node + +-- Repeat_Button_Node + +-- Light_Button_Node + +-- Check_Button_Node + +-- Round_Button_Node + +-+ Menu_Item_Node, 'o' is derived from Fl_Button in FLUID + +-- Radio_Menu_Item_Node + +-- Checkbox_Menu_Item_Node + +-- Fl_Submenu_Item_Type + +*/ + +#include "nodes/Node.h" + +#include "Fluid.h" +#include "Project.h" +#include "app/Snap_Action.h" +#include "app/shell_command.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "nodes/Function_Node.h" +#include "nodes/Widget_Node.h" +#include "nodes/Window_Node.h" +#include "nodes/Group_Node.h" +#include "rsrcs/pixmaps.h" +#include "widgets/Node_Browser.h" + +#include +#include +#include +#include "../src/flstring.h" + +#include +#include + +// ---- global variables + +Node *in_this_only; // set if menu popped-up in window + + +// ---- various functions + +#if 0 +#ifndef NDEBUG +/** + Print the current project tree to stderr. + */ +void print_project_tree() { + fprintf(stderr, "---- %s --->\n", Fluid.proj.projectfile_name().c_str()); + for (Node *t = Fluid.proj.tree.first; t; t = t->next) { + for (int i = t->level; i > 0; i--) + fprintf(stderr, ". "); + fprintf(stderr, "%s\n", subclassname(t)); + } +} +#endif + +#ifndef NDEBUG +/** + Check the validity of the project tree. + + Write problems with the project tree to stderr. + + \return true if the project tree is valid + */ +bool validate_project_tree() { + // Validate `first` and `last` + if (Fluid.proj.tree.first == nullptr) { + if (Fluid.proj.tree.last == nullptr) { + return true; + } else { + fprintf(stderr, "ERROR: `first` is nullptr, but `last` is not!\n"); + return false; + } + } + if (Fluid.proj.tree.last == nullptr) { + fprintf(stderr, "ERROR: `last` is nullptr, but `first` is not!\n"); + return false; + } + // Validate the branch linkage, parent links, etc. + return validate_branch(Fluid.proj.tree.first); +} +#endif + +#ifndef NDEBUG +/** + Check the validity of a Type branch that is not connected to the project. + + Write problems with the branch to stderr. + + \param[in] root the first node in a branch + \return true if the branch is correctly separated and valid + */ +bool validate_independent_branch(class Node *root) { + // Make sure that `first` and `last` do not point at any node in this branch + if (Fluid.proj.tree.first) { + for (Node *t = root; t; t = t->next) { + if (Fluid.proj.tree.first == t) { + fprintf(stderr, "ERROR: Branch is not independent, `first` is pointing to branch member!\n"); + return false; + } + } + } + if (Fluid.proj.tree.last) { + for (Node *t = root; t; t = t->next) { + if (Fluid.proj.tree.last == t) { + fprintf(stderr, "ERROR: Branch is not independent, `last` is pointing to branch member!\n"); + return false; + } + } + } + // Validate the branch linkage, parent links, etc. + return validate_branch(root); +} +#endif + +#ifndef NDEBUG +/** + Check the validity of a Type branch. + + Write problems with the branch to stderr. + + \param[in] root the first node in a branch + \return true if the branch is valid + */ +bool validate_branch(class Node *root) { + // Only check real branches + if (!root) { + fprintf(stderr, "WARNING: Branch is empty!\n"); + return false; + } + // Check relation between this and next node + for (Node *t = root; t; t = t->next) { + if (t->level < root->level) { + fprintf(stderr, "ERROR: Node in tree is above root level!\n"); + return false; + } + if (t->next) { + // Make sure that all `next` types have the `prev` member link back + if (t->next->prev != t) { + fprintf(stderr, "ERROR: Doubly linked list broken!\n"); + return false; + } + if (t->next->level > t->level) { + // Validate `level` changes + if (t->next->level - t->level > 1) { + fprintf(stderr, "ERROR: Child level increment greater than one!\n"); + return false; + } + // Ensure that this node can actually have children + if (!t->can_have_children()) { + fprintf(stderr, "ERROR: This parent must not have children!\n"); + return false; + } + } + } + // Validate the `parent` entry + for (Node *p = t->prev; ; p = p->prev) { + if (p == nullptr) { + if (t->parent != nullptr) { + fprintf(stderr, "ERROR: `parent` pointer should be nullptr!\n"); + return false; + } + break; + } + if (p->level < t->level) { + if (t->parent != p) { + fprintf(stderr, "ERROR: `parent` points to wrong parent!\n"); + return false; + } + break; + } + } + } + return true; +} +#endif +#endif + +void select_all_cb(Fl_Widget *,void *) { + Node *p = Fluid.proj.tree.current ? Fluid.proj.tree.current->parent : nullptr; + if (in_this_only) { + Node *t = p; + for (; t && t != in_this_only; t = t->parent) {/*empty*/} + if (t != in_this_only) p = in_this_only; + } + for (;;) { + if (p) { + int foundany = 0; + for (Node *t = p->next; t && t->level>p->level; t = t->next) { + if (!t->new_selected) {widget_browser->select(t,1,0); foundany = 1;} + } + if (foundany) break; + p = p->parent; + } else { + for (Node *t = Fluid.proj.tree.first; t; t = t->next) + widget_browser->select(t,1,0); + break; + } + } + selection_changed(p); +} + +void select_none_cb(Fl_Widget *,void *) { + Node *p = Fluid.proj.tree.current ? Fluid.proj.tree.current->parent : nullptr; + if (in_this_only) { + Node *t = p; + for (; t && t != in_this_only; t = t->parent) {/*empty*/} + if (t != in_this_only) p = in_this_only; + } + for (;;) { + if (p) { + int foundany = 0; + for (Node *t = p->next; t && t->level>p->level; t = t->next) { + if (t->new_selected) {widget_browser->select(t,0,0); foundany = 1;} + } + if (foundany) break; + p = p->parent; + } else { + for (Node *t = Fluid.proj.tree.first; t; t = t->next) + widget_browser->select(t,0,0); + break; + } + } + selection_changed(p); +} + +/** + Callback to move all selected items before their previous unselected sibling. + */ +void earlier_cb(Fl_Widget*,void*) { + Node *f; + int mod = 0; + for (f = Fluid.proj.tree.first; f; ) { + Node* nxt = f->next; + if (f->selected) { + Node* g; + for (g = f->prev; g && g->level > f->level; g = g->prev) {/*empty*/} + if (g && g->level == f->level && !g->selected) { + if (!mod) Fluid.proj.undo.checkpoint(); + f->move_before(g); + if (f->parent) f->parent->layout_widget(); + mod = 1; + } + } + f = nxt; + } + if (mod) Fluid.proj.set_modflag(1); + widget_browser->display(Fluid.proj.tree.current); + widget_browser->rebuild(); +} + +/** + Callback to move all selected items after their next unselected sibling. + */ +void later_cb(Fl_Widget*,void*) { + Node *f; + int mod = 0; + for (f = Fluid.proj.tree.last; f; ) { + Node* prv = f->prev; + if (f->selected) { + Node* g; + for (g = f->next; g && g->level > f->level; g = g->next) {/*empty*/} + if (g && g->level == f->level && !g->selected) { + if (!mod) Fluid.proj.undo.checkpoint(); + g->move_before(f); + if (f->parent) f->parent->layout_widget(); + mod = 1; + } + } + f = prv; + } + if (mod) Fluid.proj.set_modflag(1); + widget_browser->display(Fluid.proj.tree.current); + widget_browser->rebuild(); +} + +/** \brief Delete all children of a Type. + */ +static void delete_children(Node *p) { + Node *f; + // find all types following p that are higher in level, effectively finding + // the last child of the last child + for (f = p; f && f->next && f->next->level > p->level; f = f->next) {/*empty*/} + // now loop back up to p, deleting all children on the way + for (; f != p; ) { + Node *g = f->prev; + delete f; + f = g; + } +} + +/** Delete all nodes in the Types tree and reset project settings, or delete selected nodes. + Also calls the browser to refresh. + \note Please refactor this into two separate methods of Project. + \param[in] selected_only if set, delete only the selected widgets and + don't reset the project. + */ +void delete_all(int selected_only) { + if (widget_browser) { + if (selected_only) + widget_browser->save_scroll_position(); + widget_browser->new_list(); + } + for (Node *f = Fluid.proj.tree.first; f;) { + if (f->selected || !selected_only) { + delete_children(f); + Node *g = f->next; + delete f; + f = g; + } else { + f = f->next; + } + } + if(!selected_only) { + // reset the setting for the external shell command + if (g_shell_config) { + g_shell_config->clear(fld::Tool_Store::PROJECT); + g_shell_config->rebuild_shell_menu(); + g_shell_config->update_settings_dialog(); + } + if (widget_browser) { + widget_browser->hposition(0); + widget_browser->vposition(0); + } + Fluid.layout_list.remove_all(fld::Tool_Store::PROJECT); + Fluid.layout_list.current_suite(0); + Fluid.layout_list.current_preset(0); + Fluid.layout_list.update_dialogs(); + } + selection_changed(nullptr); + if (widget_browser) { + if (selected_only) + widget_browser->restore_scroll_position(); + widget_browser->rebuild(); + } +} + +/** Update a string. + Replace a string pointer with new value, strips leading/trailing blanks. + As a side effect, this call also sets the mod flags. + \param[in] n new string, can be nullptr + \param[out] p update this pointer, possibly reallocate memory + \param[in] nostrip if set, do not strip leading and trailing spaces and tabs + \return 1 if the string in p changed + */ +int storestring(const char *n, const char * & p, int nostrip) { + if (n == p) return 0; + Fluid.proj.undo.checkpoint(); + int length = 0; + if (n) { // see if blank, strip leading & trailing blanks + if (!nostrip) while (isspace((int)(unsigned char)*n)) n++; + const char *e = n + strlen(n); + if (!nostrip) while (e > n && isspace((int)(unsigned char)*(e-1))) e--; + length = int(e-n); + if (!length) n = nullptr; + } + if (n == p) return 0; + if (n && p && !strncmp(n,p,length) && !p[length]) return 0; + if (p) free((void *)p); + if (!n || !*n) { + p = nullptr; + } else { + char *q = (char *)malloc(length+1); + strlcpy(q,n,length+1); + p = q; + } + Fluid.proj.set_modflag(1); + return 1; +} + +/** Update the `visible` flag for `p` and all its descendants. + \param[in] p start here and update all descendants + */ +void update_visibility_flag(Node *p) { + Node *t = p; + for (;;) { + if (t->parent) t->visible = t->parent->visible && !t->parent->folded_; + else t->visible = 1; + t = t->next; + if (!t || t->level <= p->level) break; + } +} + +// ---- implementation of Node + +/** \var Node *Node::parent + Link to the parent node in the tree structure. + Used for simulating a tree structure via a doubly linked list. + */ +/** \var Node *Node::level + Zero based depth of the node within the tree structure. + Level is used to emulate a tree structure: the first node with a lower + level in the prev list would be the parent of this node. If the next member + has a higher level value, it is this nodes first child. At the same level, + it would be the first sibling. + */ +/** \var Node *Node::next + Points to the next node in the doubly linked list. + If this is nullptr, we are at the end of the list. + Used for simulating a tree structure via a doubly linked list. + */ +/** \var Node *Node::prev + Link to the next node in the tree structure. + If this is nullptr, we are at the beginning of the list. + Used for simulating a tree structure via a doubly linked list. + */ + +/** + Constructor and base for any node in the widget tree. + */ +Node::Node() : + name_(nullptr), + label_(nullptr), + callback_(nullptr), + user_data_(nullptr), + user_data_type_(nullptr), + comment_(nullptr), + uid_(0), + parent(nullptr), + new_selected(0), + selected(0), + folded_(0), + visible(0), + level(0), + next(nullptr), prev(nullptr), + factory(nullptr), + code_static_start(-1), code_static_end(-1), + code1_start(-1), code1_end(-1), + code2_start(-1), code2_end(-1), + header1_start(-1), header1_end(-1), + header2_start(-1), header2_end(-1), + header_static_start(-1), header_static_end(-1), + proj1_start(-1), proj1_end(-1), + proj2_start(-1), proj2_end(-1) +{ +} + + +/** + Destructor for any node in the tree. + + The destructor removes itself from the doubly linked list. This is dangerous, + because the node does not know if it is part of the widget tree, or if it is + in a separate tree. We try to take care of that as well as possible. + */ +Node::~Node() { + // warning: destructor only works for widgets that have been add()ed. + if (prev) prev->next = next; // else first = next; // don't do that! The Type may not be part of the main list + if (next) next->prev = prev; // else last = prev; + if (Fluid.proj.tree.last == this) Fluid.proj.tree.last = prev; + if (Fluid.proj.tree.first == this) Fluid.proj.tree.first = next; + if (Fluid.proj.tree.current == this) Fluid.proj.tree.current = nullptr; + if (parent) parent->remove_child(this); + if (name_) free((void*)name_); + if (label_) free((void*)label_); + if (callback_) free((void*)callback_); + if (user_data_) free((void*)user_data_); + if (user_data_type_) free((void*)user_data_type_); + if (comment_) free((void*)comment_); +} + +// Return the previous sibling in the tree structure or nullptr. +Node *Node::prev_sibling() { + Node *n; + for (n = prev; n && n->level > level; n = n->prev) ; + if (n && (n->level == level)) + return n; + return nullptr; +} + +// Return the next sibling in the tree structure or nullptr. +Node *Node::next_sibling() { + Node *n; + for (n = next; n && n->level > level; n = n->next) ; + if (n && (n->level == level)) + return n; + return nullptr; +} + +// Return the first child or nullptr +Node *Node::first_child() { + Node *n = next; + if (n->level > level) + return n; + return nullptr; +} + +// Generate a descriptive text for this item, to put in browser & window titles +const char* Node::title() { + const char* c = name(); + if (c) + return c; + return type_name(); +} + +/** + Return the window that contains this widget. + \return nullptr if this is not a widget. + */ +Window_Node *Node::window() { + if (!is_widget()) + return nullptr; + for (Node *t = this; t; t=t->parent) + if (t->is_a(Type::Window)) + return (Window_Node*)t; + return nullptr; +} + +/** + Return the group that contains this widget. + \return nullptr if this is not a widget. + */ +Group_Node *Node::group() { + if (!is_widget()) + return nullptr; + for (Node *t = this; t; t=t->parent) + if (t->is_a(Type::Group)) + return (Group_Node*)t; + return nullptr; +} + +/** + Add this list/tree of widgets as a new last child of p. + + \c this must not be part of the widget browser. \c p however must be in the + widget_browser, so \c Fluid.proj.tree.first and \c Fluid.proj.tree.last are valid for \c p. + + This methods updates the widget_browser. + + \param[in] p insert \c this tree as a child of \c p + \param[in] strategy is Strategy::AS_LAST_CHILD or Strategy::AFTER_CURRENT + */ +void Node::add(Node *anchor, Strategy strategy) { +#if 0 +#ifndef NDEBUG + // print_project_tree(); + // fprintf(stderr, "Validating project\n"); + validate_project_tree(); + // fprintf(stderr, "Validating branch\n"); + validate_independent_branch(this); +#endif +#endif + + Node *target = nullptr; // insert self before target node, if nullptr, insert last + Node *target_parent = nullptr; // this will be the new parent for branch + int target_level = 0; // adjust self to this new level + + // Find the node after our insertion position + switch (strategy.placement()) { + case Strategy::AS_FIRST_CHILD: + default: + if (anchor == nullptr) { + target = Fluid.proj.tree.first; + } else { + target = anchor->next; + target_level = anchor->level + 1; + target_parent = anchor; + } + break; + case Strategy::AS_LAST_CHILD: + if (anchor == nullptr) { + /* empty */ + } else { + for (target = anchor->next; target && target->level > anchor->level; target = target->next) {/*empty*/} + target_level = anchor->level + 1; + target_parent = anchor; + } + break; + case Strategy::AFTER_CURRENT: + if (anchor == nullptr) { + target = Fluid.proj.tree.first; + } else { + for (target = anchor->next; target && target->level > anchor->level; target = target->next) {/*empty*/} + target_level = anchor->level; + target_parent = anchor->parent; + } + break; + } + + + // Find the last node of our tree + Node *end = this; + while (end->next) end = end->next; + + // Everything is prepared, now insert ourself in front of the target node + Fluid.proj.undo.checkpoint(); + + // Walk the tree to update parent pointers and levels + int source_level = level; + for (Node *t = this; t; t = t->next) { + t->level += (target_level-source_level); + if (t->level == target_level) + t->parent = target_parent; + } + + // Now link ourselves and our children before 'target', or last, if 'target' is nullptr + if (target) { + prev = target->prev; + target->prev = end; + end->next = target; + } else { + prev = Fluid.proj.tree.last; + end->next = nullptr; + Fluid.proj.tree.last = end; + } + if (prev) { + prev->next = this; + } else { + Fluid.proj.tree.first = this; + } + +#if 0 + { // make sure that we have no duplicate uid's + Node *tp = this; + do { + tp->set_uid(tp->uid_); + tp = tp->next; + } while (tp!=end && tp!=nullptr); + } +#endif + + // Give the widgets in our tree a chance to update themselves + for (Node *t = this; t && t!=end->next; t = t->next) { + if (target_parent && (t->level == target_level)) + target_parent->add_child(t, nullptr); + update_visibility_flag(t); + } + + Fluid.proj.set_modflag(1); + widget_browser->redraw(); + +#if 0 +#ifndef NDEBUG + // fprintf(stderr, "Validating project after adding branch\n"); + validate_project_tree(); +#endif +#endif +} + +/** + Add `this` list/tree of widgets as a new sibling before `g`. + + `This` is not part of the widget browser. `g` must be in the + widget_browser, so `Fluid.proj.tree.first` and `Fluid.proj.tree.last` are valid for `g . + + This methods updates the widget_browser. + + \param[in] g pointer to a node within the tree + */ +void Node::insert(Node *g) { + // 'this' is not in the Node_Browser, so we must run the linked list to find the last entry + Node *end = this; + while (end->next) end = end->next; + // 'this' will get the same parent as 'g' + parent = g->parent; + // run the list again to set the future node levels + int newlevel = g->level; + visible = g->visible; + for (Node *t = this->next; t; t = t->next) t->level += newlevel-level; + level = newlevel; + // insert this in the list before g + prev = g->prev; + if (prev) prev->next = this; else Fluid.proj.tree.first = this; + end->next = g; + g->prev = end; + update_visibility_flag(this); + { // make sure that we have no duplicate uid's + Node *tp = this; + do { + tp->set_uid(tp->uid_); + tp = tp->next; + } while (tp!=end && tp!=nullptr); + } + // tell parent that it has a new child, so it can update itself + if (parent) parent->add_child(this, g); + widget_browser->redraw(); +} + +// Return message number for I18N... +int Node::msgnum() { + int count; + Node *p; + + for (count = 0, p = this; p;) { + if (p->label()) count ++; + if (p != this && p->is_widget() && ((Widget_Node *)p)->tooltip()) count ++; + + if (p->prev) p = p->prev; + else p = p->parent; + } + + return count; +} + +/** + Remove this node and all its children from the parent node. + + This does not delete anything. The resulting list//tree will no longer be in + the widget_browser, so \c Fluid.proj.tree.first and \c Fluid.proj.tree.last do not apply + to it. + + \return the node that follows this node after the operation; can be nullptr + */ +Node *Node::remove() { + // find the last child of this node + Node *end = this; + for (;;) { + if (!end->next || end->next->level <= level) + break; + end = end->next; + } + // unlink this node from the previous one + if (prev) + prev->next = end->next; + else + Fluid.proj.tree.first = end->next; + // unlink the last child from their next node + if (end->next) + end->next->prev = prev; + else + Fluid.proj.tree.last = prev; + Node *r = end->next; + prev = end->next = nullptr; + // allow the parent to update changes in the UI + if (parent) parent->remove_child(this); + parent = nullptr; + // tell the widget_browser that we removed some nodes + widget_browser->redraw(); + selection_changed(nullptr); + return r; +} + +void Node::name(const char *n) { + int nostrip = is_a(Type::Comment); + if (storestring(n,name_,nostrip)) { + if (visible) widget_browser->redraw(); + } +} + +void Node::label(const char *n) { + if (storestring(n,label_,1)) { + setlabel(label_); + if (visible && !name_) widget_browser->redraw(); + } +} + +void Node::callback(const char *n) { + storestring(n,callback_); +} + +void Node::user_data(const char *n) { + storestring(n,user_data_); +} + +void Node::user_data_type(const char *n) { + storestring(n,user_data_type_); +} + +void Node::comment(const char *n) { + if (storestring(n,comment_,1)) { + if (visible) widget_browser->redraw(); + } +} + +void Node::open() { + printf("Open of '%s' is not yet implemented\n",type_name()); +} + +// returns pointer to whatever is after f & children + +/** + Move this node (and its children) into list before g. + Both `this` and `g` must be in the widget browser. + The caller must make sure that the widget browser is rebuilt correctly. + \param[in] g move \c this tree before \c g + */ +void Node::move_before(Node* g) { + if (level != g->level) printf("move_before levels don't match! %d %d\n", + level, g->level); + // Find the last child in the list + Node *n; + for (n = next; n && n->level > level; n = n->next) ; + if (n == g) return; + // now link this tree before g + Node *l = n ? n->prev : Fluid.proj.tree.last; + prev->next = n; + if (n) n->prev = prev; else Fluid.proj.tree.last = prev; + prev = g->prev; + l->next = g; + if (prev) prev->next = this; else Fluid.proj.tree.first = this; + g->prev = l; + // tell parent that it has a new child, so it can update itself + if (parent && is_widget()) parent->move_child(this,g); +} + + +// write a widget and all its children: +void Node::write(fld::io::Project_Writer &f) { + if (f.write_codeview()) proj1_start = (int)ftell(f.file()) + 1; + if (f.write_codeview()) proj2_start = (int)ftell(f.file()) + 1; + f.write_indent(level); + f.write_word(type_name()); + + if (is_class()) { + const char * p = ((Class_Node*)this)->prefix(); + if (p && strlen(p)) + f.write_word(p); + } + + f.write_word(name()); + f.write_open(); + write_properties(f); + if (parent) parent->write_parent_properties(f, this, true); + f.write_close(level); + if (f.write_codeview()) proj1_end = (int)ftell(f.file()); + if (!can_have_children()) { + if (f.write_codeview()) proj2_end = (int)ftell(f.file()); + return; + } + // now do children: + f.write_open(); + Node *child; + for (child = next; child && child->level > level; child = child->next) + if (child->level == level+1) child->write(f); + if (f.write_codeview()) proj2_start = (int)ftell(f.file()) + 1; + f.write_close(level); + if (f.write_codeview()) proj2_end = (int)ftell(f.file()); +} + +void Node::write_properties(fld::io::Project_Writer &f) { + // repeat this for each attribute: + if (Fluid.proj.write_mergeback_data && uid_) { + f.write_word("uid"); + f.write_string("%04x", uid_); + } + if (label()) { + f.write_indent(level+1); + f.write_word("label"); + f.write_word(label()); + } + if (user_data()) { + f.write_indent(level+1); + f.write_word("user_data"); + f.write_word(user_data()); + } + if (user_data_type()) { + f.write_word("user_data_type"); + f.write_word(user_data_type()); + } + if (callback()) { + f.write_indent(level+1); + f.write_word("callback"); + f.write_word(callback()); + } + if (comment()) { + f.write_indent(level+1); + f.write_word("comment"); + f.write_word(comment()); + } + if (can_have_children() && !folded_) f.write_word("open"); + if (selected) f.write_word("selected"); +} + +void Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"uid")) { + const char *hex = f.read_word(); + int x = 0; + if (hex) + x = sscanf(hex, "%04x", &x); + set_uid(x); + } else if (!strcmp(c,"label")) + label(f.read_word()); + else if (!strcmp(c,"user_data")) + user_data(f.read_word()); + else if (!strcmp(c,"user_data_type")) + user_data_type(f.read_word()); + else if (!strcmp(c,"callback")) + callback(f.read_word()); + else if (!strcmp(c,"comment")) + comment(f.read_word()); + else if (!strcmp(c,"open")) + folded_ = 0; + else if (!strcmp(c,"selected")) + select(this,1); + else if (!strcmp(c,"parent_properties")) + if (parent) { + const char *cc = f.read_word(1); + if (strcmp(cc, "{")==0) { + for (;;) { + cc = f.read_word(); + if (!cc || cc[0]==0 || strcmp(cc, "}")==0) break; + parent->read_parent_property(f, this, cc); + } + } else { + f.read_error("'parent_properties' must be followed by '{'"); + } + } else { + f.read_error("Types using 'parent_properties' must have a parent"); + f.read_word(); // skip the entire block (this should generate a warning) + } + else + f.read_error("Unknown property \"%s\"", c); +} + +/** Write parent properties into the child property list. + + Some widgets store information for every child they manage. For example, + Fl_Grid stores the row and column position of every child. This method stores + this information with the child, but it is read and written by the parent. + + Parent properties solve several issues. A child will keep parent properties + if copied from on grid into another. The parent does not have to keep lists + of properties that may diverge from the actual order or number of children. + And lastly, properties are read when they are actually needed and don't have + to be stored in some temporary array. + + Parent properties are written as their own block at the end of the child's + property list. The block starts with the `parent_properties` keyword, followed + by a list of property/value pairs. The order of properties is significant, + however individual properties can be left out. + + To avoid writing the `parent_properties` block unnecessarily, this method + should only generate it if `encapsulate` is set *and* the contained + properties are not at their default. + + Lastly, this method should call the super class to give it a chance to append + its own properties. + + \see Grid_Node::write_parent_properties(fld::io::Project_Writer &f, Node *child, bool encapsulate) + + \param[in] f the project file writer + \param[in] child write properties for this child, make sure it has the correct type + \param[in] encapsulate write the `parent_properties {}` block if true before writing any properties + */ +void Node::write_parent_properties(fld::io::Project_Writer &f, Node *child, bool encapsulate) { + (void)f; (void)child; (void)encapsulate; + // nothing to do here + // put the following code into your implementation of write_parent_properties + // if there are actual non-default properties to write + // if (encapsulate) { + // f.write_indent(level+2); + // f.write_string("parent_properties {"); + // } + // now write your properties as name/value pairs + // f.write_indent(level+3); + // f.write_string("location {%d %d}", cell->row(), cell->col()); + // give the super class a chance to write its properties as well + // super::write_parent_properties(f, child, false); + // close the encapsulation + // if (encapsulate) { + // f.write_indent(level+2); + // f.write_string("}"); + // } +} + +/** Read one parent per-child property. + + A parent widget can store properties for every child that it manages. This + method reads back those properties. This function is virtual, so if a Type + does not support a property, it will propagate to its super class. + + \see Node::write_parent_properties(fld::io::Project_Writer &f, Node *child, bool encapsulate) + \see Grid_Node::read_parent_property(fld::io::Project_Reader &f, Node *child, const char *property) + + \param[in] f the project file writer + \param[in] child read properties for this child + \param[in] property the name of a property, or "}" when we reach the end of the list + */ +void Node::read_parent_property(fld::io::Project_Reader &f, Node *child, const char *property) { + (void)child; + f.read_error("Unknown parent property \"%s\"", property); +} + + +int Node::read_fdesign(const char*, const char*) {return 0;} + +/** + Write a comment into the header file. + \param[in] pre indent the comment by this string +*/ +void Node::write_comment_h(fld::io::Code_Writer& f, const char *pre) +{ + if (comment() && *comment()) { + f.write_h("%s/**\n", pre); + const char *s = comment(); + f.write_h("%s ", pre); + while(*s) { + if (*s=='\n') { + if (s[1]) { + f.write_h("\n%s ", pre); + } + } else { + f.write_h("%c", *s); // FIXME this is much too slow! + } + s++; + } + f.write_h("\n%s*/\n", pre); + } +} + +/** + Write a comment into the source file. +*/ +void Node::write_comment_c(fld::io::Code_Writer& f, const char *pre) +{ + if (comment() && *comment()) { + f.write_c("%s/**\n", pre); + const char *s = comment(); + if (*s && *s!='\n') + f.write_c("%s ", pre); + while(*s) { + if (*s=='\n') { + f.write_c("\n"); + if (s[1] && s[1]!='\n') { + f.write_c("%s ", pre); + } + } else { + f.write_c("%c", *s); // FIXME this is much too slow! + } + s++; + } + f.write_c("\n%s*/\n", pre); + } +} + +/** + Write a comment into the source file. +*/ +void Node::write_comment_inline_c(fld::io::Code_Writer& f, const char *pre) +{ + if (comment() && *comment()) { + const char *s = comment(); + if (strchr(s, '\n')==nullptr) { + // single line comment + if (pre) f.write_c("%s", pre); + f.write_c("// %s\n", s); + if (!pre) f.write_c("%s", f.indent_plus(1)); + } else { + f.write_c("%s/*\n", pre?pre:""); + if (*s && *s!='\n') { + if (pre) + f.write_c("%s ", pre); + else + f.write_c("%s ", f.indent_plus(1)); + } + while(*s) { + if (*s=='\n') { + f.write_c("\n"); + if (s[1] && s[1]!='\n') { + if (pre) + f.write_c("%s ", pre); + else + f.write_c("%s ", f.indent_plus(1)); + } + } else { + f.write_c("%c", *s); // FIXME this is much too slow! + } + s++; + } + if (pre) + f.write_c("\n%s */\n", pre); + else + f.write_c("\n%s */\n", f.indent_plus(1)); + if (!pre) + f.write_c("%s", f.indent_plus(1)); + } + } +} + +/** + Build widgets and dataset needed in live mode. + \return a widget pointer that the live mode initiator can 'show()' + \see leave_live_mode() +*/ +Fl_Widget *Node::enter_live_mode(int) { + return nullptr; +} + +/** + Release all resources created when entering live mode. + \see enter_live_mode() +*/ +void Node::leave_live_mode() { +} + +/** + Copy all needed properties for this type into the live object. +*/ +void Node::copy_properties() { +} + +/** + Check whether callback \p cbname is declared anywhere else by the user. + + \b Warning: this just checks that the name is declared somewhere, + but it should probably also check that the name corresponds to a + plain function or a member function within the same class and that + the parameter types match. + */ +int Node::user_defined(const char* cbname) const { + for (Node* p = Fluid.proj.tree.first; p ; p = p->next) + if (p->is_a(Type::Function) && p->name() != nullptr) + if (strncmp(p->name(), cbname, strlen(cbname)) == 0) + if (p->name()[strlen(cbname)] == '(') + return 1; + return 0; +} + +const char *Node::callback_name(fld::io::Code_Writer& f) { + if (is_name(callback())) return callback(); + return f.unique_id(this, "cb", name(), label()); +} + +/** + \brief Return the class name if this type is inside a Class or Widget Class. + + This methods traverses up the hirarchy to find out if this Type is located + inside a Class or Widget Class. It then return the name of that class. If + need_nest is set, class_name searches all the way up the tree and concatenates + the names of classes within classes, separated by a "::". + + \param need_nest if clear, search up one level to the first enclosing class. + If set, recurse all the way up to the top node. + \return the name of the enclosing class, or names of the enclosing classes + in a static buffe (don't call free), or nullptr if this Type is not inside a class + */ +const char* Node::class_name(const int need_nest) const { + Node* p = parent; + while (p) { + if (p->is_class()) { + // see if we are nested in another class, we must fully-qualify name: + // this is lame but works... + const char* q = nullptr; + if(need_nest) q=p->class_name(need_nest); + if (q) { + static char s[256]; + if (q != s) strlcpy(s, q, sizeof(s)); + strlcat(s, "::", sizeof(s)); + strlcat(s, p->name(), sizeof(s)); + return s; + } + return p->name(); + } + p = p->parent; + } + return nullptr; +} + +/** + Check if this is inside a Class_Node or Widget_Class_Node. + \return true if any of the parents is Class_Node or Widget_Class_Node + */ +bool Node::is_in_class() const { + Node* p = parent; + while (p) { + if (p->is_class()) return true; + p = p->parent; + } + return false; +} + +void Node::write_static(fld::io::Code_Writer&) { +} + +void Node::write_static_after(fld::io::Code_Writer&) { +} + +void Node::write_code1(fld::io::Code_Writer& f) { + f.write_h("// Header for %s\n", title()); + f.write_c("// Code for %s\n", title()); +} + +void Node::write_code2(fld::io::Code_Writer&) { +} + +/** Set a uid that is unique within the project. + + Try to set the given id as the unique id for this node. If the suggested id + is 0, or it is already taken inside this project, we try another random id + until we find one that is unique. + + \param[in] suggested_uid the preferred uid for this node + \return the actualt uid that was given to the node + */ +unsigned short Node::set_uid(unsigned short suggested_uid) { + if (suggested_uid==0) + suggested_uid = (unsigned short)rand(); + for (;;) { + Node *tp = Fluid.proj.tree.first; + for ( ; tp; tp = tp->next) + if (tp!=this && tp->uid_==suggested_uid) + break; + if (tp==nullptr) + break; + suggested_uid = (unsigned short)rand(); + } + uid_ = suggested_uid; + return suggested_uid; +} + + +/// \} + diff --git a/fluid/nodes/Node.h b/fluid/nodes/Node.h new file mode 100644 index 000000000..5c473a936 --- /dev/null +++ b/fluid/nodes/Node.h @@ -0,0 +1,313 @@ +// +// Node base class header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#ifndef FLUID_NODES_NODE_H +#define FLUID_NODES_NODE_H + +#include "io/Code_Writer.h" + +#include +#include + +class Node; +class Group_Node; +class Window_Node; + +namespace fld { +namespace io { + +class Project_Reader; +class Project_Writer; + +} // namespace io +} // namespace fld + +/** + Declare where a new type is placed and how to create it. + + Placement can be as the first or last child of the anchor, or right after the + anchor. In most cases, the anchor is the last selected type node. + + If the source is FROM_USER, widgets may be created with default titles and + labels. Type created FROM_FILE will start with no label, so the label is set + correctly later. + + \see Node *Fl_..._Type::make(Strategy strategy) calls `add()` + Add single Type: + Node *add_new_widget_from_user(Node *inPrototype, Strategy strategy, bool and_open) + Node *add_new_widget_from_user(const char *inName, Strategy strategy, bool and_open) + Node *add_new_widget_from_file(const char *inName, Strategy strategy) + Add a hierarchy of Types + void Node::add(Node *p, Strategy strategy) + int read_file(const char *filename, int merge, Strategy strategy) + Node *fld::io::Project_Reader::read_children(Node *p, int merge, Strategy strategy, char skip_options) + int fld::io::Project_Reader::read_project(const char *filename, int merge, Strategy strategy) + */ +typedef struct Strategy { + enum Flags { + AS_FIRST_CHILD = 0x0000, + AS_LAST_CHILD = 0x0001, + AFTER_CURRENT = 0x0002, + PLACEMENT_MASK = 0x000f, + FROM_USER = 0x0000, + FROM_FILE = 0x0010, + SOURCE_MASK = 0x00f0, + FROM_FILE_AS_FIRST_CHILD = 0x0010, + FROM_FILE_AS_LAST_CHILD = 0x0011, + FROM_FILE_AFTER_CURRENT = 0x0012, + }; + Flags flags; + Strategy(Flags f) { flags = f; } + void placement(Flags f) { flags = (Flags)((flags & ~PLACEMENT_MASK) | (f & PLACEMENT_MASK)); } + Flags placement() { return (Flags)(flags & PLACEMENT_MASK); } + void source(Flags f) { flags = (Flags)((flags & ~SOURCE_MASK) | (f & SOURCE_MASK)); } + Flags source() { return (Flags)(flags & SOURCE_MASK); } +} Strategy; + +enum class Type { + // administrative + Base_, Widget_, Menu_Manager_, Menu_, Browser_, Valuator_, + // non-widget + Function, Code, CodeBlock, + Decl, DeclBlock, Class, + Widget_Class, Comment, Data, + // groups + Window, Group, Pack, + Flex, Tabs, Scroll, + Tile, Wizard, Grid, + // buttons + Button, Return_Button, Light_Button, + Check_Button, Repeat_Button, Round_Button, + // valuators + Slider, Scrollbar, Value_Slider, + Adjuster, Counter, Spinner, + Dial, Roller, Value_Input, Value_Output, + // text + Input, Output, Text_Editor, + Text_Display, File_Input, Terminal, + // menus + Menu_Bar, Menu_Button, Choice, + Input_Choice, Submenu, Menu_Item, + Checkbox_Menu_Item, Radio_Menu_Item, + // browsers + Browser, Check_Browser, File_Browser, + Tree, Help_View, Table, + // misc + Box, Clock, Progress, + Max_ +}; + +void update_visibility_flag(Node *p); +void delete_all(int selected_only=0); +int storestring(const char *n, const char * & p, int nostrip=0); + +void select_all_cb(Fl_Widget *,void *); +void select_none_cb(Fl_Widget *,void *); +void earlier_cb(Fl_Widget*,void*); +void later_cb(Fl_Widget*,void*); + +#ifndef NDEBUG +void print_project_tree(); +bool validate_project_tree(); +bool validate_independent_branch(class Node *root); +bool validate_branch(class Node *root); +#endif + +/** + \brief This is the base class for all elements in the project tree. + + All widgets and other types in the project are derived from Fl_Types. They + are organized in a doubly linked list. Every Type also has depth information + to create a pseudo tree structure. To make walking up the tree faster, Type + also holds a pointer to the `parent` Type. + + Types can be identified using the builtin Type system that works like RTTI. The + method `type()` returns the exact type, and the method `is_a(Type)` returns true + if this is the exact type or derived from the type, and a dynamic cast will + work reliably. + + \todo it would be nice if we can handle multiple independent trees. To do that + we must remove static members like `first` and `last`. + + \todo add virtual methods to handle events, draw widgets, and draw overlays. + It may also make sense to have a virtual method that returns a boolean if + a specific type can be added as a child. + + \todo it may make sense to have a readable iterator class instead of relying + on pointer manipulation. Or use std in future releases. + */ +class Node { + /** Copy the label text to Widgets and Windows, does nothing in Type. */ + virtual void setlabel(const char *) { } // virtual part of label(char*) + +protected: + + Node(); + + /** Name of a widget, or code some non-widget Types. */ + const char *name_; + /** Label text of a widget. */ + const char *label_; + /** If it is just a word, it's the name of the callback function. Otherwise + it is the full callback C++ code. Can be nullptr. */ + const char *callback_; + /** Widget user data field as C++ text. */ + const char *user_data_; + /** Widget user data type as C++ text, usually `void*` or `long`. */ + const char *user_data_type_; + /** Optional comment for every node in the graph. Visible in browser and + panels, and will also be copied to the source code. */ + const char *comment_; + /** a unique ID within the project */ + unsigned short uid_; + +public: // things that should not be public: + // TODO: reference back to the tree + /** Quick link to the parent Type instead of walking up the linked list. */ + Node *parent; + /** This type is rendered "selected" in the tree browser. */ + char new_selected; // browser highlight + /** Backup storage for selection if an error occurred during some operation + (see `haderror`). It seems that this is often confused with new_selected + which seems to hold the true and visible selection state. */ + char selected; // copied here by selection_changed() + char folded_; // if set, children are not shown in browser + char visible; // true if all parents are open + int level; // number of parents over this + Node *next, *prev; + Node *prev_sibling(); + Node *next_sibling(); + Node *first_child(); + + Node *factory; + const char *callback_name(fld::io::Code_Writer& f); + + // text positions of this type in code, header, and project file (see codeview) + int code_static_start, code_static_end; + int code1_start, code1_end; + int code2_start, code2_end; + int header1_start, header1_end; + int header2_start, header2_end; + int header_static_start, header_static_end; + int proj1_start, proj1_end; + int proj2_start, proj2_end; + +protected: + int user_defined(const char* cbname) const; + +public: + + virtual ~Node(); + virtual Node *make(Strategy strategy) = 0; + + Window_Node *window(); + Group_Node *group(); + + void add(Node *parent, Strategy strategy); + void insert(Node *n); // insert into list before n + Node* remove(); // remove from list + void move_before(Node*); // move before a sibling + + virtual const char *title(); // string for browser + virtual const char *type_name() = 0; // type for code output + virtual const char *alt_type_name() { return type_name(); } // alternate type for FLTK2 code output + + const char *name() const {return name_;} + void name(const char *); + const char *label() const {return label_;} + void label(const char *); + const char *callback() const {return callback_;} + void callback(const char *); + const char *user_data() const {return user_data_;} + void user_data(const char *); + const char *user_data_type() const {return user_data_type_;} + void user_data_type(const char *); + const char *comment() { return comment_; } + void comment(const char *); + + virtual Node* click_test(int,int) { return nullptr; } + + virtual void add_child(Node*, Node* beforethis) { } + virtual void move_child(Node*, Node* beforethis) { } + virtual void remove_child(Node*) { } + + /** Give widgets a chance to arrange their children after all children were added. + If adding individual children, this is called immediately, but if children + are read via a project file, we wait until all children are read and then + lay out the group. + */ + virtual void layout_widget() { } + + virtual void open(); // what happens when you double-click + + // read and write data to a saved file: + virtual void write(fld::io::Project_Writer &f); + virtual void write_properties(fld::io::Project_Writer &f); + virtual void read_property(fld::io::Project_Reader &f, const char *); + virtual void write_parent_properties(fld::io::Project_Writer &f, Node *child, bool encapsulate); + virtual void read_parent_property(fld::io::Project_Reader &f, Node *child, const char *property); + virtual int read_fdesign(const char*, const char*); + virtual void postprocess_read() { } + + // write code, these are called in order: + virtual void write_static(fld::io::Code_Writer& f); // write static stuff to .c file + virtual void write_static_after(fld::io::Code_Writer& f); // write static stuff after children + virtual void write_code1(fld::io::Code_Writer& f); // code and .h before children + virtual void write_code2(fld::io::Code_Writer& f); // code and .h after children + void write_comment_h(fld::io::Code_Writer& f, const char *ind=""); // write the commentary text into the header file + void write_comment_c(fld::io::Code_Writer& f, const char *ind=""); // write the commentary text into the source file + void write_comment_inline_c(fld::io::Code_Writer& f, const char *ind=nullptr); // write the commentary text + + // live mode + virtual Fl_Widget *enter_live_mode(int top=0); // build widgets needed for live mode + virtual void leave_live_mode(); // free allocated resources + virtual void copy_properties(); // copy properties from this type into a potential live object + virtual void copy_properties_for_children() { } // copy remaining properties after children were added + + // get message number for I18N + int msgnum(); + + /** Return 1 if the Type can have children. */ + virtual int can_have_children() const {return 0;} + /** Return 1 if the type is a widget or menu item. */ + virtual int is_widget() const {return 0;} + /** Return 1 if the type is a widget but not a menu item. */ + virtual int is_true_widget() const {return 0;} + /** Return 1 if a type behaves like a button (Fl_Button and Fl_Menu_Item and derived, but not Submenu_Node. */ + virtual int is_button() const {return 0;} + /** Return 1 if this is a Widget_Class_Node, CodeBlock_Node, or Function_Node */ + virtual int is_code_block() const {return 0;} + /** Return 1 if this is a Widget_Class_Node, Class_Node, or DeclBlock_Node */ + virtual int is_decl_block() const {return 0;} + /** Return 1 if this is a Class_Node or Widget_Class_Node. */ + virtual int is_class() const {return 0;} + /** Return 1 if the type browser shall draw a padlock over the icon. */ + virtual int is_public() const {return 1;} + /** Return the type Type for this Type. */ + virtual Type type() const { return Type::Base_; } + /** Check if this Type is of the give type Type or derived from that type Type. */ + virtual bool is_a(Type inType) const { return (inType==Type::Base_); } + + const char* class_name(const int need_nest) const; + bool is_in_class() const; + + int has_function(const char*, const char*) const; + + unsigned short set_uid(unsigned short suggested_uid=0); + unsigned short get_uid() { return uid_; } +}; + +#endif // FLUID_NODES_NODE_H diff --git a/fluid/nodes/Tree.cxx b/fluid/nodes/Tree.cxx new file mode 100644 index 000000000..ca9b3d761 --- /dev/null +++ b/fluid/nodes/Tree.cxx @@ -0,0 +1,128 @@ +// +// Node Tree code for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + + +#include "nodes/Tree.h" + +#include "Project.h" + +using namespace fld; +using namespace fld::node; + + +Tree::Iterator::Iterator(Node *t, bool only_selected) +: type_(t) +, only_selected_(only_selected) +{ + if (t) { + if (only_selected_) { + if (!type_->selected) { + operator++(); + } + } + } +} + +Tree::Iterator &Tree::Iterator::operator++() { + if (only_selected_) { + do { + type_ = type_->next; + } while (type_ && !type_->selected); + } else { + type_ = type_->next; + } + return *this; +} + +Tree::WIterator::WIterator(Node *t, bool only_selected) +: type_(t) +, only_selected_(only_selected) +{ + if (t) { + if (only_selected_) { + if (!type_->selected || !type_->is_widget()) { + operator++(); + } + } else { + if (!type_->is_widget()) { + operator++(); + } + } + } +} + +Tree::WIterator& Tree::WIterator::operator++() { + if (only_selected_) { + do { + type_ = type_->next; + } while (type_ && (!type_->selected || !type_->is_widget())); + } else { + do { + type_ = type_->next; + } while (type_ && !type_->is_widget()); + } + return *this; +} + + +Tree::Tree(Project &proj) +: proj_(proj) +{ (void)proj_; } + + +/** Find a node by its unique id. + + Every node in a type tree has an id that is unique for the current project. + Walk the tree and return the node with this uid. + + \param[in] uid any number between 0 and 65535 + \return the node with this uid, or nullptr if not found + */ +Node *Tree::find_by_uid(unsigned short uid) { + for (auto tp: all_nodes()) { + if (tp->get_uid() == uid) return tp; + } + return nullptr; +} + + +/** Find a type node by using the codeview text positions. + + \param[in] text_type 0=source file, 1=header, 2=.fl project file + \param[in] crsr cursor position in text + \return the node we found or nullptr + */ +Node *Tree::find_in_text(int text_type, int crsr) { + for (auto node: all_nodes()) { + switch (text_type) { + case 0: + if (crsr >= node->code1_start && crsr < node->code1_end) return node; + if (crsr >= node->code2_start && crsr < node->code2_end) return node; + if (crsr >= node->code_static_start && crsr < node->code_static_end) return node; + break; + case 1: + if (crsr >= node->header1_start && crsr < node->header1_end) return node; + if (crsr >= node->header2_start && crsr < node->header2_end) return node; + if (crsr >= node->header_static_start && crsr < node->header_static_end) return node; + break; + case 2: + if (crsr >= node->proj1_start && crsr < node->proj1_end) return node; + if (crsr >= node->proj2_start && crsr < node->proj2_end) return node; + break; + } + } + return nullptr; +} diff --git a/fluid/nodes/Tree.h b/fluid/nodes/Tree.h new file mode 100644 index 000000000..f494af651 --- /dev/null +++ b/fluid/nodes/Tree.h @@ -0,0 +1,106 @@ +// +// Node Tree header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#ifndef FLUID_NODES_TREE_H +#define FLUID_NODES_TREE_H + +#include "nodes/Widget_Node.h" + +class Node; + +namespace fld { + +class Project; + +namespace node { + +class Tree { + + // A class that can iterate over the entire scene graph. + class Iterator { + Node *type_ = nullptr; + bool only_selected_ = false; + public: + explicit Iterator(Node *t, bool only_selected); + Node* operator*() { return type_; } + Iterator& operator++(); + bool operator!=(const Iterator& other) const { return type_ != other.type_; } + }; + + // A container for a node iterator + class Container { + Tree &tree_; + bool only_selected_ = false; + public: + Container(Tree &tree, bool only_selected) : tree_(tree), only_selected_(only_selected) { } + Iterator begin() { return Iterator(tree_.first, only_selected_); } + Iterator end() { return Iterator(nullptr, only_selected_); } + }; + + // A class that iterate over the scene graph, but returns only nodes of type widget. + class WIterator { + Node *type_ = nullptr; + bool only_selected_ = false; + public: + explicit WIterator(Node *t, bool only_selected); + Widget_Node* operator*() { return static_cast(type_); } + WIterator& operator++(); + bool operator!=(const WIterator& other) const { return type_ != other.type_; } + }; + + // A container for a widget node iterator + class WContainer { + Tree &tree_; + bool only_selected_ = false; + public: + WContainer(Tree &tree, bool only_selected) : tree_(tree), only_selected_(only_selected) { } + WIterator begin() { return WIterator(tree_.first, only_selected_); } + WIterator end() { return WIterator(nullptr, only_selected_); } + }; + + /// Link Tree class to the project. + Project &proj_; + +public: + + Node *first = nullptr; + Node *last = nullptr; + Node *current = nullptr; // most recently picked object + Node *current_dnd = nullptr; + /// If this is greater zero, widgets will be allowed to lay out their children. + int allow_layout = 0; + +public: + + Tree(Project &proj); + + bool empty() { return first == nullptr; } + + // Iterators: `for (auto &n: tree.all_nodes()) { n.print(); } + Container all_nodes() { return Container(*this, false); } + WContainer all_widgets() { return WContainer(*this, false); } + Container all_selected_nodes() { return Container(*this, true); } + WContainer all_selected_widgets() { return WContainer(*this, true); } + + Node *find_by_uid(unsigned short uid); + Node *find_in_text(int text_type, int crsr); +}; + +} // namespace node +} // namespace fld + + +#endif // FLUID_NODES_TREE_H diff --git a/fluid/nodes/Widget_Node.cxx b/fluid/nodes/Widget_Node.cxx new file mode 100644 index 000000000..cba8607f2 --- /dev/null +++ b/fluid/nodes/Widget_Node.cxx @@ -0,0 +1,2438 @@ +// +// Widget Node code for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#include "nodes/Widget_Node.h" + +#include "Fluid.h" +#include "Project.h" +#include "app/Image_Asset.h" +#include "proj/mergeback.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "nodes/Menu_Node.h" +#include "nodes/Function_Node.h" +#include "nodes/Window_Node.h" +#include "panels/widget_panel.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../src/flstring.h" + +#include +#include +#undef min +#undef max +#include + +// Make an Widget_Node subclass instance. +// It figures out the automatic size and parent of the new widget, +// creates the Fl_Widget (by calling the virtual function _make), +// adds it to the Fl_Widget hierarchy, creates a new Node +// instance, sets the widget pointers, and makes all the display +// update correctly... + +int Widget_Node::is_widget() const {return 1;} +int Widget_Node::is_public() const {return public_;} + +const char* subclassname(Node* l) { + if (l->is_a(Type::Menu_Bar)) { + Menu_Bar_Node *mb = static_cast(l); + if (mb->is_sys_menu_bar()) + return mb->sys_menubar_name(); + } + if (l->is_widget()) { + Widget_Node* p = (Widget_Node*)l; + const char* c = p->subclass(); + if (c) return c; + if (l->is_class()) return "Fl_Group"; + if (p->o->type() == FL_DOUBLE_WINDOW) return "Fl_Double_Window"; + if (p->type() == Type::Input) { + if (p->o->type() == FL_FLOAT_INPUT) return "Fl_Float_Input"; + if (p->o->type() == FL_INT_INPUT) return "Fl_Int_Input"; + } + } + return l->type_name(); +} + +// Return the ideal widget size... +void +Widget_Node::ideal_size(int &w, int &h) { + w = 120; + h = 100; + fld::app::Snap_Action::better_size(w, h); +} + +/** + Make a new Widget node. + \param[in] strategy is Strategy::AS_LAST_CHILD or Strategy::AFTER_CURRENT + \return new node + */ +Node *Widget_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *pp = anchor; + if (pp && (strategy.placement() == Strategy::AFTER_CURRENT)) + pp = pp->parent; + while (pp && !pp->is_a(Type::Group)) { + anchor = pp; + strategy.placement(Strategy::AFTER_CURRENT); + pp = pp->parent; + } + if (!pp || !pp->is_true_widget() || !anchor->is_true_widget()) { + fl_message("Please select a group widget or window"); + return nullptr; + } + + Widget_Node* p = (Widget_Node*)pp; + Widget_Node* q = (Widget_Node*)anchor; + + // Figure out a border between widget and window: + int B = p->o->w()/2; if (p->o->h()/2 < B) B = p->o->h()/2; if (B>25) B = 25; + + int ULX,ULY; // parent's origin in window + if (!p->is_a(Type::Window)) { // if it is a group, add corner + ULX = p->o->x(); ULY = p->o->y(); + } else { + ULX = ULY = 0; + } + + // Figure out a position and size for the widget + int X,Y,W,H; + if (is_a(Type::Group)) { // fill the parent with the widget + X = ULX+B; + W = p->o->w()-B; + Y = ULY+B; + H = p->o->h()-B; + } else if (q != p) { // copy position and size of current widget + W = q->o->w(); + H = q->o->h(); + X = q->o->x()+W; + Y = q->o->y(); + if (X+W > ULX+p->o->w()) { + X = q->o->x(); + Y = q->o->y()+H; + if (Y+H > ULY+p->o->h()) Y = ULY+B; + } + } else { // just make it small and square... + X = ULX+B; + Y = ULY+B; + W = H = B; + } + + // Construct the Node: + Widget_Node *t = _make(); + if (!o) o = widget(0,0,100,100); // create template widget + t->factory = this; + // Construct the Fl_Widget: + t->o = widget(X,Y,W,H); + if (strategy.source() == Strategy::FROM_FILE) + t->o->label(nullptr); + else if (t->o->label()) t->label(t->o->label()); // allow editing + t->o->user_data((void*)t); + // Put it in the parent: + // ((Fl_Group *)(p->o))->add(t->o); (done by Node::add()) + // add to browser: + t->add(anchor, strategy); + t->redraw(); + return t; +} + +void Widget_Node::setimage(Image_Asset *i) { + if (i == image || is_a(Type::Window)) return; + if (image) image->dec_ref(); + if (i) i->inc_ref(); + image = i; + if (i) { + o->image(i->image()); + if (o->image() && (scale_image_w_ || scale_image_h_)) { + int iw = scale_image_w_>0 ? scale_image_w_ : o->image()->data_w(); + int ih = scale_image_h_>0 ? scale_image_h_ : o->image()->data_h(); + o->image()->scale(iw, ih, 0, 1); + } + } else { + o->image(nullptr); + //scale_image_w_ = scale_image_h_ = 0; + } + redraw(); +} + +void Widget_Node::setinactive(Image_Asset *i) { + if (i == inactive || is_a(Type::Window)) return; + if (inactive) inactive->dec_ref(); + if (i) i->inc_ref(); + inactive = i; + if (i) { + o->deimage(i->image()); + if (o->deimage()) { + int iw = scale_deimage_w_>0 ? scale_deimage_w_ : o->deimage()->data_w(); + int ih = scale_deimage_h_>0 ? scale_deimage_h_ : o->deimage()->data_h(); + o->deimage()->scale(iw, ih, 0, 1); + } + } else { + o->deimage(nullptr); + //scale_deimage_w_ = scale_deimage_h_ = 0; + } + redraw(); +} + +void Widget_Node::setlabel(const char *n) { + o->label(n); + redraw(); +} + +Widget_Node::Widget_Node() +: override_visible_(0) +{ + for (int n=0; nwindow(); + delete o; + if (win) + win->redraw(); + } + if (subclass_) free((void*)subclass_); + if (tooltip_) free((void*)tooltip_); + if (image_name_) { + free((void*)image_name_); + if (image) image->dec_ref(); + } + if (inactive_name_) { + free((void*)inactive_name_); + if (inactive) inactive->dec_ref(); + } + for (int n=0; ntooltip(n); +} + +void Widget_Node::image_name(const char *n) { + setimage(Image_Asset::find(n)); + storestring(n,image_name_); +} + +void Widget_Node::inactive_name(const char *n) { + setinactive(Image_Asset::find(n)); + storestring(n,inactive_name_); +} + +void Widget_Node::redraw() { + Node *t = this; + if (is_a(Type::Menu_Item)) { + // find the menu button that parents this menu: + do t = t->parent; while (t && t->is_a(Type::Menu_Item)); + // kludge to cause build_menu to be called again: + if (t) + t->add_child(nullptr, nullptr); + } else { + while (t->parent && t->parent->is_widget()) t = t->parent; + ((Widget_Node*)t)->o->redraw(); + } +} + +// the recursive part sorts all children, returns pointer to next: +Node *sort(Node *parent) { + Node *f,*n=nullptr; + for (f = parent ? parent->next : Fluid.proj.tree.first; ; f = n) { + if (!f || (parent && f->level <= parent->level)) break; + n = sort(f); + if (!f->selected || !f->is_true_widget()) continue; + Fl_Widget* fw = ((Widget_Node*)f)->o; + Node *g; // we will insert before this + for (g = parent ? parent->next : Fluid.proj.tree.first; g != f; g = g->next) { + if (!g->selected || g->level > f->level) continue; + Fl_Widget* gw = ((Widget_Node*)g)->o; + if (gw->y() > fw->y()) break; + if (gw->y() == fw->y() && gw->x() > fw->x()) break; + } + if (g != f) f->move_before(g); + } + if (parent) + parent->layout_widget(); + return f; +} + +//////////////////////////////////////////////////////////////// +// The control panels! + +Fl_Window *the_panel; + +// All the callbacks use the argument to indicate whether to load or store. +// This avoids the need for pointers to all the widgets, and keeps the +// code localized in the callbacks. +// A value of LOAD means to load. The hope is that this will not collide +// with any actual useful values for the argument. I also use this to +// initialized parts of the widget that are nyi by fluid. + +Widget_Node *current_widget; // one of the selected ones +void* const LOAD = (void *)"LOAD"; // "magic" pointer to indicate that we need to load values into the dialog +int numselected; // number selected +int haderror; + +void name_public_cb(Fl_Choice* i, void* v) { + if (v == LOAD) { + i->value(current_widget->public_>0); + if (current_widget->is_in_class()) i->hide(); else i->show(); + } else { + int mod = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + ((Widget_Node*)o)->public_ = i->value(); + mod = 1; + } + } + if (mod) { + Fluid.proj.set_modflag(1); + redraw_browser(); + } + } +} + +/* Treating UNDO for text widget. + + Goal: we want to continuously update the UI while the user is typing text + (changing the label, in this case). Code View does deferred updates, and + the widget browser and widget panel update on every keystroke. At the same + time, we want to limit undo actions to few and logical units. + + Caveats: + + 1: the text widget has its own undo handling for the text field, but we may want to do a global undo + 2: every o->label() call will create an undo entry, but we want only one single event for all selected widgets + 3: we want a single undo for the entire editing phase, but still propagate changes as they happen + + The edit process has these main states: + + 1: starting to edit [first_change==1 && !unfocus]; we must create a single undo checkpoint before anything changes + 2: continue editing [first_change==0 && !unfocus]; we must suspend any undo checkpoints + 3: done editing, unfocus [first_change==0 && unfocus]; we must make sure that undo checkpoints are enabled again + 4: losing focus without editing [first_change==1 && unfocus]; don't create and checkpoints + + We must also check: + 1: changing focus without changing text (works) + 2: copy and paste, drag and drop operations (works) + 3: save operation without unfocus event (works) + */ +void label_cb(Fl_Input* i, void *v) { + static int first_change = 1; + if (v == LOAD) { + i->value(current_widget->label()); + first_change = 1; + } else { + if (i->changed()) { + Fluid.proj.undo.suspend(); + int mod = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + if (!mod) { + if (first_change) { + Fluid.proj.undo.resume(); + Fluid.proj.undo.checkpoint(); + Fluid.proj.undo.suspend(); + first_change = 0; + } + mod = 1; + } + o->label(i->value()); + } + } + Fluid.proj.undo.resume(); + if (mod) Fluid.proj.set_modflag(1); + } + int r = (int)Fl::callback_reason(); + if ( (r == FL_REASON_LOST_FOCUS) || (r == FL_REASON_ENTER_KEY) ) + first_change = 1; + } +} + + + + + + +int widget_i = 0; + +static int vars_i_cb(const fld::widget::Formula_Input*, void *v) { + return widget_i; +} + +static int vars_x_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = (Node*)v; + if (t->is_widget()) + return ((Widget_Node*)t)->o->x(); + return 0; +} + +static int vars_y_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = (Node*)v; + if (t->is_widget()) + return ((Widget_Node*)t)->o->y(); + return 0; +} + +static int vars_w_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = (Node*)v; + if (t->is_widget()) + return ((Widget_Node*)t)->o->w(); + return 0; +} + +static int vars_h_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = (Node*)v; + if (t->is_widget()) + return ((Widget_Node*)t)->o->h(); + return 0; +} + +static int vars_px_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->parent; + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->x(); + return 0; +} + +static int vars_py_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->parent; + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->y(); + return 0; +} + +static int vars_pw_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->parent; + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->w(); + return 0; +} + +static int vars_ph_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->parent; + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->h(); + return 0; +} + +static int vars_sx_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->prev_sibling(); + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->x(); + return 0; +} + +static int vars_sy_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->prev_sibling(); + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->y(); + return 0; +} + +static int vars_sw_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->prev_sibling(); + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->w(); + return 0; +} + +static int vars_sh_cb(const fld::widget::Formula_Input*, void *v) { + Node *t = ((Node*)v)->prev_sibling(); + if (t && t->is_widget()) + return ((Widget_Node*)t)->o->h(); + return 0; +} + +static int bbox_x, bbox_y, bbox_r, bbox_b; + +static void calculate_bbox(Node *p) { + char first = 1; + bbox_x = bbox_y = bbox_r = bbox_b = 0; + for (p=p->first_child(); p; p=p->next_sibling()) { + if (p->is_widget()) { + Fl_Widget *o = ((Widget_Node*)p)->o; + if (first) { + bbox_x = o->x(); bbox_y = o->y(); + bbox_r = o->x() + o->w(); bbox_b = o->y() + o->h(); + first = 0; + } else { + bbox_x = std::min(bbox_x, o->x()); + bbox_y = std::min(bbox_y, o->y()); + bbox_r = std::max(bbox_r, o->x() + o->w()); + bbox_b = std::max(bbox_b, o->y() + o->h()); + } + } + } +} + +static int vars_cx_cb(const fld::widget::Formula_Input*, void *v) { + calculate_bbox((Node*)v); + return bbox_x; +} + +static int vars_cy_cb(const fld::widget::Formula_Input*, void *v) { + calculate_bbox((Node*)v); + return bbox_y; +} + +static int vars_cw_cb(const fld::widget::Formula_Input*, void *v) { + calculate_bbox((Node*)v); + return bbox_r - bbox_x; +} + +static int vars_ch_cb(const fld::widget::Formula_Input*, void *v) { + calculate_bbox((Node*)v); + return bbox_b - bbox_y; +} + +fld::widget::Formula_Input_Vars widget_vars[] = { + { "i", vars_i_cb }, // zero based counter of selected widgets + { "x", vars_x_cb }, // position and size of current widget + { "y", vars_y_cb }, + { "w", vars_w_cb }, + { "h", vars_h_cb }, + { "px", vars_px_cb }, // position and size of parent widget + { "py", vars_py_cb }, + { "pw", vars_pw_cb }, + { "ph", vars_ph_cb }, + { "sx", vars_sx_cb }, // position and size of previous sibling + { "sy", vars_sy_cb }, + { "sw", vars_sw_cb }, + { "sh", vars_sh_cb }, + { "cx", vars_cx_cb }, // position and size of bounding box of all children + { "cy", vars_cy_cb }, + { "cw", vars_cw_cb }, + { "ch", vars_ch_cb }, + { nullptr } +}; + + + + + +//////////////////////////////////////////////////////////////// + +// turn number to string or string to number for saving to file: +// does not work for hierarchical menus! + +const char *item_name(Fl_Menu_Item* m, int i) { + if (m) { + while (m->label()) { + if (m->argument() == i) return m->label(); + m++; + } + } + static char buffer[20]; + sprintf(buffer, "%d", i); + return buffer; +} +int item_number(Fl_Menu_Item* m, const char* i) { + if (!i) + return 0; + if (m && i) { + if (i[0]=='F' && i[1]=='L' && i[2]=='_') i += 3; + while (m->label()) { + if (!strcmp(m->label(), i)) return int(m->argument()); + m++; + } + } + return atoi(i); +} + +#define ZERO_ENTRY 1000 + +Fl_Menu_Item boxmenu[] = { + {"NO_BOX",0,nullptr,(void *)ZERO_ENTRY}, + {"boxes",0,nullptr,nullptr,FL_SUBMENU}, + {"UP_BOX",0,nullptr,(void *)FL_UP_BOX}, + {"DOWN_BOX",0,nullptr,(void *)FL_DOWN_BOX}, + {"FLAT_BOX",0,nullptr,(void *)FL_FLAT_BOX}, + {"BORDER_BOX",0,nullptr,(void *)FL_BORDER_BOX}, + {"THIN_UP_BOX",0,nullptr,(void *)FL_THIN_UP_BOX}, + {"THIN_DOWN_BOX",0,nullptr,(void *)FL_THIN_DOWN_BOX}, + {"ENGRAVED_BOX",0,nullptr,(void *)FL_ENGRAVED_BOX}, + {"EMBOSSED_BOX",0,nullptr,(void *)FL_EMBOSSED_BOX}, + {"ROUND_UP_BOX",0,nullptr,(void *)FL_ROUND_UP_BOX}, + {"ROUND_DOWN_BOX",0,nullptr,(void *)FL_ROUND_DOWN_BOX}, + {"DIAMOND_UP_BOX",0,nullptr,(void *)FL_DIAMOND_UP_BOX}, + {"DIAMOND_DOWN_BOX",0,nullptr,(void *)FL_DIAMOND_DOWN_BOX}, + {"SHADOW_BOX",0,nullptr,(void *)FL_SHADOW_BOX}, + {"ROUNDED_BOX",0,nullptr,(void *)FL_ROUNDED_BOX}, + {"RSHADOW_BOX",0,nullptr,(void *)FL_RSHADOW_BOX}, + {"RFLAT_BOX",0,nullptr,(void *)FL_RFLAT_BOX}, + {"OVAL_BOX",0,nullptr,(void *)FL_OVAL_BOX}, + {"OSHADOW_BOX",0,nullptr,(void *)FL_OSHADOW_BOX}, + {"OFLAT_BOX",0,nullptr,(void *)FL_OFLAT_BOX}, + {"PLASTIC_UP_BOX",0,nullptr,(void *)FL_PLASTIC_UP_BOX}, + {"PLASTIC_DOWN_BOX",0,nullptr,(void *)FL_PLASTIC_DOWN_BOX}, + {"PLASTIC_THIN_UP_BOX",0,nullptr,(void *)FL_PLASTIC_THIN_UP_BOX}, + {"PLASTIC_THIN_DOWN_BOX",0,nullptr,(void *)FL_PLASTIC_THIN_DOWN_BOX}, + {"PLASTIC_ROUND_UP_BOX",0,nullptr,(void *)FL_PLASTIC_ROUND_UP_BOX}, + {"PLASTIC_ROUND_DOWN_BOX",0,nullptr,(void *)FL_PLASTIC_ROUND_DOWN_BOX}, + {"GTK_UP_BOX",0,nullptr,(void *)FL_GTK_UP_BOX}, + {"GTK_DOWN_BOX",0,nullptr,(void *)FL_GTK_DOWN_BOX}, + {"GTK_THIN_UP_BOX",0,nullptr,(void *)FL_GTK_THIN_UP_BOX}, + {"GTK_THIN_DOWN_BOX",0,nullptr,(void *)FL_GTK_THIN_DOWN_BOX}, + {"GTK_ROUND_UP_BOX",0,nullptr,(void *)FL_GTK_ROUND_UP_BOX}, + {"GTK_ROUND_DOWN_BOX",0,nullptr,(void *)FL_GTK_ROUND_DOWN_BOX}, + {"GLEAM_UP_BOX",0,nullptr,(void *)FL_GLEAM_UP_BOX}, + {"GLEAM_DOWN_BOX",0,nullptr,(void *)FL_GLEAM_DOWN_BOX}, + {"GLEAM_THIN_UP_BOX",0,nullptr,(void *)FL_GLEAM_THIN_UP_BOX}, + {"GLEAM_THIN_DOWN_BOX",0,nullptr,(void *)FL_GLEAM_THIN_DOWN_BOX}, + {"GLEAM_ROUND_UP_BOX",0,nullptr,(void *)FL_GLEAM_ROUND_UP_BOX}, + {"GLEAM_ROUND_DOWN_BOX",0,nullptr,(void *)FL_GLEAM_ROUND_DOWN_BOX}, + {"OXY_UP_BOX",0,nullptr,(void *)FL_OXY_UP_BOX}, + {"OXY_DOWN_BOX",0,nullptr,(void *)FL_OXY_DOWN_BOX}, + {"OXY_THIN_UP_BOX",0,nullptr,(void *)FL_OXY_THIN_UP_BOX}, + {"OXY_THIN_DOWN_BOX",0,nullptr,(void *)FL_OXY_THIN_DOWN_BOX}, + {"OXY_ROUND_UP_BOX",0,nullptr,(void *)FL_OXY_ROUND_UP_BOX}, + {"OXY_ROUND_DOWN_BOX",0,nullptr,(void *)FL_OXY_ROUND_DOWN_BOX}, + {"OXY_BUTTON_UP_BOX",0,nullptr,(void *)FL_OXY_BUTTON_UP_BOX}, + {"OXY_BUTTON_DOWN_BOX",0,nullptr,(void *)FL_OXY_BUTTON_DOWN_BOX}, + {nullptr}, + {"frames",0,nullptr,nullptr,FL_SUBMENU}, + {"UP_FRAME",0,nullptr,(void *)FL_UP_FRAME}, + {"DOWN_FRAME",0,nullptr,(void *)FL_DOWN_FRAME}, + {"THIN_UP_FRAME",0,nullptr,(void *)FL_THIN_UP_FRAME}, + {"THIN_DOWN_FRAME",0,nullptr,(void *)FL_THIN_DOWN_FRAME}, + {"ENGRAVED_FRAME",0,nullptr,(void *)FL_ENGRAVED_FRAME}, + {"EMBOSSED_FRAME",0,nullptr,(void *)FL_EMBOSSED_FRAME}, + {"BORDER_FRAME",0,nullptr,(void *)FL_BORDER_FRAME}, + {"SHADOW_FRAME",0,nullptr,(void *)FL_SHADOW_FRAME}, + {"ROUNDED_FRAME",0,nullptr,(void *)FL_ROUNDED_FRAME}, + {"OVAL_FRAME",0,nullptr,(void *)FL_OVAL_FRAME}, + {"PLASTIC_UP_FRAME",0,nullptr,(void *)FL_PLASTIC_UP_FRAME}, + {"PLASTIC_DOWN_FRAME",0,nullptr,(void *)FL_PLASTIC_DOWN_FRAME}, + {"GTK_UP_FRAME",0,nullptr,(void *)FL_GTK_UP_FRAME}, + {"GTK_DOWN_FRAME",0,nullptr,(void *)FL_GTK_DOWN_FRAME}, + {"GTK_THIN_UP_FRAME",0,nullptr,(void *)FL_GTK_THIN_UP_FRAME}, + {"GTK_THIN_DOWN_FRAME",0,nullptr,(void *)FL_GTK_THIN_DOWN_FRAME}, + {"GLEAM_UP_FRAME",0,nullptr,(void *)FL_GLEAM_UP_FRAME}, + {"GLEAM_DOWN_FRAME",0,nullptr,(void *)FL_GLEAM_DOWN_FRAME}, + {"OXY_UP_FRAME",0,nullptr,(void *)FL_OXY_UP_FRAME}, + {"OXY_DOWN_FRAME",0,nullptr,(void *)FL_OXY_DOWN_FRAME}, + {"OXY_THIN_UP_FRAME",0,nullptr,(void *)FL_OXY_THIN_UP_FRAME}, + {"OXY_THIN_DOWN_FRAME",0,nullptr,(void *)FL_OXY_THIN_DOWN_FRAME}, + {nullptr}, + {nullptr}}; + +const char *boxname(int i) { + if (!i) i = ZERO_ENTRY; + for (int j = 0; j < int(sizeof(boxmenu)/sizeof(*boxmenu)); j++) + if (boxmenu[j].argument() == i) return boxmenu[j].label(); + return nullptr; +} + +int boxnumber(const char *i) { + if (i[0]=='F' && i[1]=='L' && i[2]=='_') i += 3; + for (int j = 0; j < int(sizeof(boxmenu)/sizeof(*boxmenu)); j++) + if (boxmenu[j].label() && !strcmp(boxmenu[j].label(), i)) { + return int(boxmenu[j].argument()); + } + return 0; +} + + + + +//////////////////////////////////////////////////////////////// + +Fl_Menu_Item whenmenu[] = { + // set individual bits + {"FL_WHEN_CHANGED",0,nullptr,(void*)FL_WHEN_CHANGED, FL_MENU_TOGGLE}, + {"FL_WHEN_NOT_CHANGED",0,nullptr,(void*)FL_WHEN_NOT_CHANGED, FL_MENU_TOGGLE}, + {"FL_WHEN_RELEASE",0,nullptr,(void*)FL_WHEN_RELEASE, FL_MENU_TOGGLE}, + {"FL_WHEN_ENTER_KEY",0,nullptr,(void*)FL_WHEN_ENTER_KEY, FL_MENU_TOGGLE}, + {"FL_WHEN_CLOSED",0,nullptr,(void*)FL_WHEN_CLOSED, FL_MENU_TOGGLE|FL_MENU_DIVIDER}, + // set bit combinations + {"FL_WHEN_NEVER",0,nullptr,(void*)FL_WHEN_NEVER}, + {"FL_WHEN_RELEASE_ALWAYS",0,nullptr,(void*)FL_WHEN_RELEASE_ALWAYS}, + {"FL_WHEN_ENTER_KEY_ALWAYS",0,nullptr,(void*)FL_WHEN_ENTER_KEY_ALWAYS}, + {"FL_WHEN_ENTER_KEY_CHANGED",0,nullptr,(void*)FL_WHEN_ENTER_KEY_CHANGED}, + {nullptr}}; + + +static Fl_Menu_Item whensymbolmenu[] = { + /* 0 */ {"FL_WHEN_NEVER",0,nullptr,(void*)FL_WHEN_NEVER}, + /* 1 */ {"FL_WHEN_CHANGED",0,nullptr,(void*)FL_WHEN_CHANGED}, + /* 2 */ {"FL_WHEN_NOT_CHANGED",0,nullptr,(void*)FL_WHEN_NOT_CHANGED}, + /* 3 */ {"FL_WHEN_CHANGED | FL_WHEN_NOT_CHANGED",0,nullptr,(void*)(FL_WHEN_CHANGED|FL_WHEN_NOT_CHANGED)}, + /* 4 */ {"FL_WHEN_RELEASE",0,nullptr,(void*)FL_WHEN_RELEASE}, + /* 5 */ {"FL_WHEN_CHANGED | FL_WHEN_RELEASE",0,nullptr,(void*)(FL_WHEN_CHANGED|FL_WHEN_RELEASE)}, + /* 6 */ {"FL_WHEN_RELEASE_ALWAYS",0,nullptr,(void*)FL_WHEN_RELEASE_ALWAYS}, + /* 7 */ {"FL_WHEN_CHANGED | FL_WHEN_RELEASE_ALWAYS",0,nullptr,(void*)(FL_WHEN_CHANGED|FL_WHEN_RELEASE_ALWAYS)}, + /* 8 */ {"FL_WHEN_ENTER_KEY",0,nullptr,(void*)FL_WHEN_ENTER_KEY}, + /* 9 */ {"FL_WHEN_CHANGED | FL_WHEN_ENTER_KEY",0,nullptr,(void*)(FL_WHEN_CHANGED|FL_WHEN_ENTER_KEY)}, + /* 10 */ {"FL_WHEN_ENTER_KEY_ALWAYS",0,nullptr,(void*)FL_WHEN_ENTER_KEY_ALWAYS}, + /* 11 */ {"FL_WHEN_ENTER_KEY_CHANGED",0,nullptr,(void*)FL_WHEN_ENTER_KEY_CHANGED}, + /* 12 */ {"FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY",0,nullptr,(void*)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY)}, + /* 13 */ {"FL_WHEN_RELEASE | FL_WHEN_CHANGED | FL_WHEN_ENTER_KEY",0,nullptr,(void*)(FL_WHEN_RELEASE|FL_WHEN_CHANGED|FL_WHEN_ENTER_KEY)}, + /* 14 */ {"FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY_ALWAYS",0,nullptr,(void*)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY_ALWAYS)}, + /* 15 */ {"FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY_CHANGED",0,nullptr,(void*)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY_CHANGED)}, + {nullptr} +}; + +// Return a text string representing the Fl_When value n +const char* when_symbol_name(int n) { + static char sym[128]; + if (n == FL_WHEN_CLOSED) { + strcpy(sym, "FL_WHEN_CLOSED"); + } else { + strcpy(sym, whensymbolmenu[n&15].label()); + if (n & FL_WHEN_CLOSED) + strcat(sym, " | FL_WHEN_CLOSED"); + } + return sym; +} + +// Set the check marks in the "when()" menu according to the Fl_When value n +void set_whenmenu(int n) { + if (n&FL_WHEN_CHANGED) whenmenu[0].set(); else whenmenu[0].clear(); + if (n&FL_WHEN_NOT_CHANGED) whenmenu[1].set(); else whenmenu[1].clear(); + if (n&FL_WHEN_RELEASE) whenmenu[2].set(); else whenmenu[2].clear(); + if (n&FL_WHEN_ENTER_KEY) whenmenu[3].set(); else whenmenu[3].clear(); + if (n&FL_WHEN_CLOSED) whenmenu[4].set(); else whenmenu[4].clear(); +} + + +uchar Widget_Node::resizable() const { + if (is_a(Type::Window)) return ((Fl_Window*)o)->resizable() != nullptr; + Fl_Group* p = (Fl_Group*)o->parent(); + if (p) return p->resizable() == o; + else return 0; +} + +void Widget_Node::resizable(uchar v) { + if (v) { + if (resizable()) return; + if (is_a(Type::Window)) ((Fl_Window*)o)->resizable(o); + else { + Fl_Group* p = (Fl_Group*)o->parent(); + if (p) p->resizable(o); + } + } else { + if (!resizable()) return; + if (is_a(Type::Window)) { + ((Fl_Window*)o)->resizable(nullptr); + } else { + Fl_Group* p = (Fl_Group*)o->parent(); + if (p) p->resizable(nullptr); + } + } +} + + + +//////////////////////////////////////////////////////////////// + +Fl_Menu_Item fontmenu[] = { + {"Helvetica"}, + {"Helvetica bold"}, + {"Helvetica italic"}, + {"Helvetica bold italic"}, + {"Courier"}, + {"Courier bold"}, + {"Courier italic"}, + {"Courier bold italic"}, + {"Times"}, + {"Times bold"}, + {"Times italic"}, + {"Times bold italic"}, + {"Symbol"}, + {"Terminal"}, + {"Terminal Bold"}, + {"Zapf Dingbats"}, + {nullptr} +}; + +Fl_Menu_Item fontmenu_w_default[] = { + {"", 0, nullptr, nullptr, FL_MENU_DIVIDER}, + {"Helvetica"}, + {"Helvetica bold"}, + {"Helvetica italic"}, + {"Helvetica bold italic"}, + {"Courier"}, + {"Courier bold"}, + {"Courier italic"}, + {"Courier bold italic"}, + {"Times"}, + {"Times bold"}, + {"Times italic"}, + {"Times bold italic"}, + {"Symbol"}, + {"Terminal"}, + {"Terminal Bold"}, + {"Zapf Dingbats"}, + {nullptr} +}; + + + +extern const char *ui_find_image_name; + +Fl_Menu_Item labeltypemenu[] = { + {"NORMAL_LABEL",0,nullptr,(void*)nullptr}, + {"SHADOW_LABEL",0,nullptr,(void*)FL_SHADOW_LABEL}, + {"ENGRAVED_LABEL",0,nullptr,(void*)FL_ENGRAVED_LABEL}, + {"EMBOSSED_LABEL",0,nullptr,(void*)FL_EMBOSSED_LABEL}, + {"NO_LABEL",0,nullptr,(void*)(FL_NO_LABEL)}, + {nullptr}}; + +void labeltype_cb(Fl_Choice* i, void *v) { + if (v == LOAD) { + int n; + n = current_widget->o->labeltype(); + i->when(FL_WHEN_RELEASE); + for (int j = 0; j < int(sizeof(labeltypemenu)/sizeof(*labeltypemenu)); j++) + if (labeltypemenu[j].argument() == n) {i->value(j); break;} + } else { + int mod = 0; + int m = i->value(); + int n = int(labeltypemenu[m].argument()); + if (n<0) return; // should not happen + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* p = (Widget_Node*)o; + p->o->labeltype((Fl_Labeltype)n); + p->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +//////////////////////////////////////////////////////////////// + +Fl_Menu_Item colormenu[] = { + { "Foreground Color", 0, nullptr, (void*)(fl_intptr_t)FL_FOREGROUND_COLOR, 0, 0, FL_HELVETICA, 11}, + { "Background Color", 0, nullptr, (void*)(fl_intptr_t)FL_BACKGROUND_COLOR, 0, 0, FL_HELVETICA, 11}, + { "Background Color 2", 0, nullptr, (void*)(fl_intptr_t)FL_BACKGROUND2_COLOR, 0, 0, FL_HELVETICA, 11}, + { "Selection Color", 0, nullptr, (void*)(fl_intptr_t)FL_SELECTION_COLOR, 0, 0, FL_HELVETICA, 11}, + { "Inactive Color", 0, nullptr, (void*)(fl_intptr_t)FL_INACTIVE_COLOR, FL_MENU_DIVIDER, 0, FL_HELVETICA, 11}, + { "Black", 0, nullptr, (void*)(fl_intptr_t)FL_BLACK, 0, 0, FL_HELVETICA, 11}, + { "White", 0, nullptr, (void*)(fl_intptr_t)FL_WHITE, FL_MENU_DIVIDER, 0, FL_HELVETICA, 11}, + { "Gray 0", 0, nullptr, (void*)(fl_intptr_t)FL_GRAY0, 0, 0, FL_HELVETICA, 11}, + { "Dark 3", 0, nullptr, (void*)(fl_intptr_t)FL_DARK3, 0, 0, FL_HELVETICA, 11}, + { "Dark 2", 0, nullptr, (void*)(fl_intptr_t)FL_DARK2, 0, 0, FL_HELVETICA, 11}, + { "Dark 1", 0, nullptr, (void*)(fl_intptr_t)FL_DARK1, 0, 0, FL_HELVETICA, 11}, + { "Light 1", 0, nullptr, (void*)(fl_intptr_t)FL_LIGHT1, 0, 0, FL_HELVETICA, 11}, + { "Light 2", 0, nullptr, (void*)(fl_intptr_t)FL_LIGHT2, 0, 0, FL_HELVETICA, 11}, + { "Light 3", 0, nullptr, (void*)(fl_intptr_t)FL_LIGHT3, 0, 0, FL_HELVETICA, 11}, + { nullptr } +}; + +void color_common(Fl_Color c) { + int mod = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + q->o->color(c); q->o->redraw(); + if (q->parent && q->parent->is_a(Type::Tabs)) { + if (q->o->parent()) q->o->parent()->redraw(); + } + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +} + + + +void color2_common(Fl_Color c) { + int mod = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + q->o->selection_color(c); q->o->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +} + + + +void labelcolor_common(Fl_Color c) { + int mod = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + q->o->labelcolor(c); q->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +} + + + +static Fl_Button* relative(Fl_Widget* o, int i) { + Fl_Group* g = (Fl_Group*)(o->parent()); + return (Fl_Button*)(g->child(g->find(*o)+i)); +} + +static Fl_Menu_Item alignmenu[] = { + {"FL_ALIGN_CENTER",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_CENTER)}, + {"FL_ALIGN_TOP",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_TOP)}, + {"FL_ALIGN_BOTTOM",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_BOTTOM)}, + {"FL_ALIGN_LEFT",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_LEFT)}, + {"FL_ALIGN_RIGHT",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_RIGHT)}, + {"FL_ALIGN_INSIDE",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_INSIDE)}, + {"FL_ALIGN_CLIP",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_CLIP)}, + {"FL_ALIGN_WRAP",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_WRAP)}, + {"FL_ALIGN_TEXT_OVER_IMAGE",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_TEXT_OVER_IMAGE)}, + {"FL_ALIGN_TOP_LEFT",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_TOP_LEFT)}, + {"FL_ALIGN_TOP_RIGHT",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_TOP_RIGHT)}, + {"FL_ALIGN_BOTTOM_LEFT",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_BOTTOM_LEFT)}, + {"FL_ALIGN_BOTTOM_RIGHT",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_BOTTOM_RIGHT)}, + {"FL_ALIGN_LEFT_TOP",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_LEFT_TOP)}, + {"FL_ALIGN_RIGHT_TOP",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_RIGHT_TOP)}, + {"FL_ALIGN_LEFT_BOTTOM",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_LEFT_BOTTOM)}, + {"FL_ALIGN_RIGHT_BOTTOM",0,nullptr,(void*)(fl_intptr_t)(FL_ALIGN_RIGHT_BOTTOM)}, + {nullptr}}; + +void align_cb(Fl_Button* i, void *v) { + Fl_Align b = Fl_Align(fl_uintptr_t(i->user_data())); + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {i->deactivate(); return;} else i->activate(); + i->value(current_widget->o->align() & b); + } else { + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + Fl_Align x = q->o->align(); + Fl_Align y; + if (i->value()) { + y = x | b; + if (b == FL_ALIGN_LEFT || b == FL_ALIGN_TOP) { + Fl_Button *b1 = relative(i,+1); + b1->clear(); + y = y & ~(b1->argument()); + } + if (b == FL_ALIGN_RIGHT || b == FL_ALIGN_BOTTOM) { + Fl_Button *b1 = relative(i,-1); + b1->clear(); + y = y & ~(b1->argument()); + } + } else { + y = x & ~b; + } + if (x != y) { + q->o->align(y); + q->redraw(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +void align_position_cb(Fl_Choice *i, void *v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {i->deactivate(); return;} else i->activate(); + Fl_Menu_Item *mi = (Fl_Menu_Item*)i->menu(); + Fl_Align b = current_widget->o->align() & FL_ALIGN_POSITION_MASK; + for (;mi->text;mi++) { + if ((Fl_Align)(mi->argument())==b) + i->value(mi); + } + } else { + const Fl_Menu_Item *mi = i->menu() + i->value(); + Fl_Align b = Fl_Align(fl_uintptr_t(mi->user_data())); + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + Fl_Align x = q->o->align(); + Fl_Align y = (x & ~FL_ALIGN_POSITION_MASK) | b; + if (x != y) { + q->o->align(y); + q->redraw(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +void align_text_image_cb(Fl_Choice *i, void *v) { + if (v == LOAD) { + if (current_widget->is_a(Type::Menu_Item)) {i->deactivate(); return;} else i->activate(); + Fl_Menu_Item *mi = (Fl_Menu_Item*)i->menu(); + Fl_Align b = current_widget->o->align() & FL_ALIGN_IMAGE_MASK; + for (;mi->text;mi++) { + if ((Fl_Align)(mi->argument())==b) + i->value(mi); + } + } else { + const Fl_Menu_Item *mi = i->menu() + i->value(); + Fl_Align b = Fl_Align(fl_uintptr_t(mi->user_data())); + int mod = 0; + Fluid.proj.undo.checkpoint(); + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + Fl_Align x = q->o->align(); + Fl_Align y = (x & ~FL_ALIGN_IMAGE_MASK) | b; + if (x != y) { + q->o->align(y); + q->redraw(); + mod = 1; + } + } + } + if (mod) Fluid.proj.set_modflag(1); + } +} + +//////////////////////////////////////////////////////////////// + + + + + +//////////////////////////////////////////////////////////////// + +// textstuff: set textfont, textsize, textcolor attributes: + +// default widget returns 0 to indicate not-implemented: +// The first parameter specifies the operation: +// 0: get all values +// 1: set the text font +// 2: set the text size +// 3: set the text color +// 4: get all default values for this type +int Widget_Node::textstuff(int, Fl_Font&, int&, Fl_Color&) { + return 0; +} + + + +void textcolor_common(Fl_Color c) { + Fl_Font n; int s; + int mod = 0; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->selected && o->is_widget()) { + Widget_Node* q = (Widget_Node*)o; + q->textstuff(3,n,s,c); q->o->redraw(); + mod = 1; + } + } + if (mod) Fluid.proj.set_modflag(1); +} + +//////////////////////////////////////////////////////////////// +// Kludges to the panel for subclasses: + + + + +//static void flex_margin_cb(Fl_Value_Input* i, void* v, +// void (*load_margin)(Fl_Flex*,Fl_Value_Input*), +// int (*update_margin)(Fl_Flex*,int)) { +// if (v == LOAD) { +// if (current_widget->is_a(Type::Flex)) { +// load_margin((Fl_Flex*)current_widget->o, i); +// } +// } else { +// int mod = 0; +// int new_value = (int)i->value(); +// for (Node *o = Fluid.proj.tree.first; o; o = o->next) { +// if (o->selected && o->is_a(Type::Flex)) { +// Flex_Node* q = (Flex_Node*)o; +// Fl_Flex* w = (Fl_Flex*)q->o; +// if (update_margin(w, new_value)) { +// w->layout(); +// mod = 1; +// } +// } +// } +// if (mod) Fluid.proj.set_modflag(1); +// } +//} + + + + +static void load_gap(Fl_Flex *w, Fl_Value_Input* i) +{ + int v = w->gap(); + i->value((double)v); +} + +static int update_gap(Fl_Flex *w, int new_value) +{ + int g = w->gap(); + if (new_value!=g) { + w->gap(new_value); + return 1; + } else { + return 0; + } +} + +void flex_margin_gap_cb(Fl_Value_Input* i, void* v) { + flex_margin_cb(i, v, load_gap, update_gap); +} + +void position_group_cb(Fl_Group* g, void* v) { + if (v == LOAD) { + if (Flex_Node::parent_is_flex(current_widget)) { + g->hide(); + } else { + g->show(); + } + } + propagate_load(g, v); +} + + + +//////////////////////////////////////////////////////////////// + +// subtypes: + +Fl_Menu_Item *Widget_Node::subtypes() {return nullptr;} + + +//////////////////////////////////////////////////////////////// + +void propagate_load(Fl_Group* g, void* v) { + if (v == LOAD) { + Fl_Widget*const* a = g->array(); + for (int i=g->children(); i--;) { + Fl_Widget* o = *a++; + o->do_callback(o, LOAD, FL_REASON_USER); + } + } +} + +void set_cb(Fl_Button*, void*) { + haderror = 0; + Fl_Widget*const* a = the_panel->array(); + for (int i=the_panel->children(); i--;) { + Fl_Widget* o = *a++; + if (o->changed()) { + o->do_callback(); + if (haderror) return; + o->clear_changed(); + } + } +} + +void ok_cb(Fl_Return_Button* o, void* v) { + set_cb(o,v); + if (!haderror) the_panel->hide(); +} + +void toggle_overlays(Fl_Widget *,void *); // in Window_Node.cxx +void overlay_cb(Fl_Button*o,void *v) { + toggle_overlays(o,v); +} + +void leave_live_mode_cb(Fl_Widget*, void*); + +void live_mode_cb(Fl_Button*o,void *) { + /// \todo live mode should end gracefully when the application quits + /// or when the user closes the live widget + static Node *live_type = nullptr; + static Fl_Widget *live_widget = nullptr; + static Fl_Window *live_window = nullptr; + // if 'o' is 0, we must quit live mode + if (!o) { + o = wLiveMode; + o->value(0); + } + if (o->value()) { + if (numselected == 1) { + Fl_Group::current(nullptr); + live_widget = current_widget->enter_live_mode(1); + if (live_widget) { + live_type = current_widget; + Fl_Group::current(nullptr); + int w = live_widget->w(); + int h = live_widget->h(); + live_window = new Fl_Double_Window(w+20, h+55, "Fluid Live Resize"); + live_window->box(FL_FLAT_BOX); + live_window->color(FL_GREEN); + Fl_Group *rsz = new Fl_Group(0, h+20, 130, 35); + rsz->box(FL_NO_BOX); + Fl_Box *rsz_dummy = new Fl_Box(110, h+20, 1, 25); + rsz_dummy->box(FL_NO_BOX); + rsz->resizable(rsz_dummy); + Fl_Button *btn = new Fl_Button(10, h+20, 100, 25, "Exit Live Resize"); + btn->labelsize(12); + btn->callback(leave_live_mode_cb); + rsz->end(); + live_window->add(live_widget); + live_widget->position(10, 10); + live_window->resizable(live_widget); + live_window->set_modal(); // block all other UI + live_window->callback(leave_live_mode_cb); + if (current_widget->is_a(Type::Window)) { + Window_Node *w = (Window_Node*)current_widget; + int mw = w->sr_min_w; if (mw>0) mw += 20; + int mh = w->sr_min_h; if (mh>0) mh += 55; + int MW = w->sr_max_w; if (MW>0) MW += 20; + int MH = w->sr_max_h; if (MH>2) MH += 55; + if (mw || mh || MW || MH) + live_window->size_range(mw, mh, MW, MH); + } + live_window->show(); + live_widget->show(); + } else o->value(0); + } else o->value(0); + } else { + if (live_type) + live_type->leave_live_mode(); + if (live_window) { + live_window->hide(); + Fl::delete_widget(live_window); + } + live_type = nullptr; + live_widget = nullptr; + live_window = nullptr; + } +} + +// update the panel according to current widget set: +void load_panel() { + if (!the_panel) return; + + // find all the Fl_Widget subclasses currently selected: + numselected = 0; + current_widget = nullptr; + if (Fluid.proj.tree.current) { + if (Fluid.proj.tree.current->is_widget()) + current_widget=(Widget_Node*)Fluid.proj.tree.current; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + if (o->is_widget() && o->selected) { + numselected++; + if (!current_widget) current_widget = (Widget_Node*)o; + } + } + } + if (current_widget && current_widget->is_a(Type::Grid)) { + if (widget_tab_grid->parent()!=widget_tabs) + widget_tabs->add(widget_tab_grid); + } else { + if (widget_tab_grid->parent()==widget_tabs) { + widget_tabs_repo->add(widget_tab_grid); + } + } + if (current_widget && current_widget->parent && current_widget->parent->is_a(Type::Grid)) { + if (widget_tab_grid_child->parent()!=widget_tabs) + widget_tabs->add(widget_tab_grid_child); + } else { + if (widget_tab_grid_child->parent()==widget_tabs) { + widget_tabs_repo->add(widget_tab_grid_child); + } + } + if (numselected) + propagate_load(the_panel, LOAD); + else + the_panel->hide(); +} + +extern Fl_Window *widgetbin_panel; + +// This is called when user double-clicks an item, open or update the panel: +void Widget_Node::open() { + bool adjust_position = false; + if (!the_panel) { + the_panel = make_widget_panel(); + adjust_position = true; + } + load_panel(); + if (numselected) { + the_panel->show(); + if (adjust_position) { + if (widgetbin_panel && widgetbin_panel->visible()) { + if ( (the_panel->x()+the_panel->w() > widgetbin_panel->x()) + && (the_panel->x() < widgetbin_panel->x()+widgetbin_panel->w()) + && (the_panel->y()+the_panel->h() > widgetbin_panel->y()) + && (the_panel->y() < widgetbin_panel->y()+widgetbin_panel->h()) ) + { + if (widgetbin_panel->y()+widgetbin_panel->h()+the_panel->h() > Fl::h()) + the_panel->position(the_panel->x(), widgetbin_panel->y()-the_panel->h()-30); + else + the_panel->position(the_panel->x(), widgetbin_panel->y()+widgetbin_panel->h()+30); + } + } + } + } +} + +extern void redraw_overlays(); +extern void check_redraw_corresponding_parent(Node*); +extern void redraw_browser(); +extern void update_codeview_position(); + +// Called when ui changes what objects are selected: +// p is selected object, null for all deletions (we must throw away +// old panel in that case, as the object may no longer exist) +void selection_changed(Node *p) { + // store all changes to the current selected objects: + if (p && the_panel && the_panel->visible()) { + set_cb(nullptr,nullptr); + // if there was an error, we try to leave the selected set unchanged: + if (haderror) { + Node *q = nullptr; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + o->new_selected = o->selected; + if (!q && o->selected) q = o; + } + if (!p || !p->selected) p = q; + Fluid.proj.tree.current = p; + redraw_browser(); + return; + } + } + // update the selected flags to new set: + Node *q = nullptr; + for (Node *o = Fluid.proj.tree.first; o; o = o->next) { + o->selected = o->new_selected; + if (!q && o->selected) q = o; + } + if (!p || !p->selected) p = q; + Fluid.proj.tree.current = p; + check_redraw_corresponding_parent(p); + redraw_overlays(); + // load the panel with the new settings: + load_panel(); + // update the code viewer to show the code for the selected object + update_codeview_position(); +} + +//////////////////////////////////////////////////////////////// +// Writing the C code: + +// test to see if user named a function, or typed in code: +int is_name(const char *c) { + for (; *c; c++) + if ((ispunct(*c)||*c=='\n') && *c!='_' && *c!=':') return 0; + return 1; +} + +// Test to see if name() is an array entry. If so, and this is the +// highest number, return name[num+1]. Return null if not the highest +// number or a field or function. Return name() if not an array entry. +const char *array_name(Widget_Node *o) { + const char *c = o->name(); + if (!c) return nullptr; + const char *d; + for (d = c; *d != '['; d++) { + if (!*d) return c; + if (ispunct(*d) && *d!='_') return nullptr; + } + int num = atoi(d+1); + int sawthis = 0; + Node *t = o->prev; + Node *tp = o; + const char *cn = o->class_name(1); + for (; t && t->class_name(1) == cn; tp = t, t = t->prev) {/*empty*/} + for (t = tp; t && t->class_name(1) == cn; t = t->next) { + if (t == o) {sawthis=1; continue;} + const char *e = t->name(); + if (!e) continue; + if (strncmp(c,e,d-c)) continue; + int n1 = atoi(e+(d-c)+1); + if (n1 > num || (n1==num && sawthis)) return nullptr; + } + static char buffer[128]; + // MRS: we want strncpy() here... + strncpy(buffer,c,d-c+1); + snprintf(buffer+(d-c+1),sizeof(buffer) - (d-c+1), "%d]",num+1); + return buffer; +} + +// Test to see if extra code is a declaration: +int isdeclare(const char *c) { + while (isspace(*c)) c++; + if (*c == '#') return 1; + if (!strncmp(c,"extern",6)) return 1; + if (!strncmp(c,"typedef",7)) return 1; + if (!strncmp(c,"using",5)) return 1; + return 0; +} + +void Widget_Node::write_static(fld::io::Code_Writer& f) { + const char* t = subclassname(this); + if (!subclass() || (is_class() && !strncmp(t, "Fl_", 3))) { + f.write_h_once("#include "); + f.write_h_once("#include ", t); + } + for (int n=0; n < NUM_EXTRA_CODE; n++) { + if (extra_code(n) && isdeclare(extra_code(n))) + f.write_h_once("%s", extra_code(n)); + } + if (callback() && is_name(callback())) { + int write_extern_declaration = 1; + char buf[1024]; snprintf(buf, 1023, "%s(*)", callback()); + if (is_in_class()) { + if (has_function("static void", buf)) + write_extern_declaration = 0; + } else { + if (has_toplevel_function(nullptr, buf)) + write_extern_declaration = 0; + } + if (write_extern_declaration) + f.write_h_once("extern void %s(%s*, %s);", callback(), t, + user_data_type() ? user_data_type() : "void*"); + } + const char* k = class_name(1); + const char* c = array_name(this); + if (c && !k && !is_class()) { + f.write_c("\n"); + if (!public_) f.write_c("static "); + else f.write_h("extern %s *%s;\n", t, c); + if (strchr(c, '[') == nullptr) f.write_c("%s *%s=(%s *)0;\n", t, c, t); + else f.write_c("%s *%s={(%s *)0};\n", t, c, t); + } + if (callback() && !is_name(callback())) { + // see if 'o' or 'v' used, to prevent unused argument warnings: + int use_o = 0; + int use_v = 0; + const char *d; + for (d = callback(); *d;) { + if (*d == 'o' && !is_id(d[1])) use_o = 1; + if (*d == 'v' && !is_id(d[1])) use_v = 1; + do d++; while (is_id(*d)); + while (*d && !is_id(*d)) d++; + } + const char* cn = callback_name(f); + if (k) { + f.write_c("\nvoid %s::%s_i(%s*", k, cn, t); + } else { + f.write_c("\nstatic void %s(%s*", cn, t); + } + if (use_o) f.write_c(" o"); + const char* ut = user_data_type() ? user_data_type() : "void*"; + f.write_c(", %s", ut); + if (use_v) f.write_c(" v"); + f.write_c(") {\n"); + // Matt: disabled f.tag(FD_TAG_GENERIC, 0); + f.write_c_indented(callback(), 1, 0); + if (*(d-1) != ';' && *(d-1) != '}') { + const char *p = strrchr(callback(), '\n'); + if (p) p ++; + else p = callback(); + // Only add trailing semicolon if the last line is not a preprocessor + // statement... + if (*p != '#' && *p) f.write_c(";"); + } + f.write_c("\n"); + // Matt: disabled f.tag(FD_TAG_WIDGET_CALLBACK, get_uid()); + f.write_c("}\n"); + if (k) { + f.write_c("void %s::%s(%s* o, %s v) {\n", k, cn, t, ut); + f.write_c("%s((%s*)(o", f.indent(1), k); + Node *q = nullptr; + for (Node* p = parent; p && p->is_widget(); q = p, p = p->parent) + f.write_c("->parent()"); + if (!q || !q->is_a(Type::Widget_Class)) + f.write_c("->user_data()"); + f.write_c("))->%s_i(o,v);\n}\n", cn); + } + } + if (image) { + if (!f.c_contains(image)) + image->write_static(f, compress_image_); + } + if (inactive) { + if (!f.c_contains(inactive)) + inactive->write_static(f, compress_deimage_); + } +} + +void Widget_Node::write_code1(fld::io::Code_Writer& f) { + const char* t = subclassname(this); + const char *c = array_name(this); + if (c) { + if (class_name(1)) { + f.write_public(public_); + f.write_h("%s%s *%s;\n", f.indent(1), t, c); + } + } + if (class_name(1) && callback() && !is_name(callback())) { + const char* cn = callback_name(f); + const char* ut = user_data_type() ? user_data_type() : "void*"; + f.write_public(0); + f.write_h("%sinline void %s_i(%s*, %s);\n", f.indent(1), cn, t, ut); + f.write_h("%sstatic void %s(%s*, %s);\n", f.indent(1), cn, t, ut); + } + // figure out if local variable will be used (prevent compiler warnings): + int wused = !name() && is_a(Type::Window); + const char *ptr; + + f.varused = wused; + + if (!name() && !f.varused) { + f.varused |= can_have_children(); + + if (!f.varused) { + f.varused_test = 1; + write_widget_code(f); + f.varused_test = 0; + } + } + + if (!f.varused) { + for (int n=0; n < NUM_EXTRA_CODE; n++) + if (extra_code(n) && !isdeclare(extra_code(n))) + { + int instring = 0; + int inname = 0; + int incomment = 0; + int incppcomment = 0; + for (ptr = extra_code(n); *ptr; ptr ++) { + if (instring) { + if (*ptr == '\\') ptr++; + else if (*ptr == '\"') instring = 0; + } else if (inname && !isalnum(*ptr & 255)) { + inname = 0; + } else if (*ptr == '/' && ptr[1]=='*') { + incomment = 1; ptr++; + } else if (incomment) { + if (*ptr == '*' && ptr[1]=='/') { + incomment = 0; ptr++; + } + } else if (*ptr == '/' && ptr[1]=='/') { + incppcomment = 1; ptr++; + } else if (incppcomment) { + if (*ptr == '\n') + incppcomment = 0; + } else if (*ptr == '\"') { + instring = 1; + } else if (isalnum(*ptr & 255) || *ptr == '_') { + size_t len = strspn(ptr, "0123456789_" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + if (!strncmp(ptr, "o", len)) { + f.varused = 1; + break; + } else { + ptr += len - 1; + } + } + } + } + } + + f.write_c("%s{ ", f.indent()); + write_comment_inline_c(f); + if (f.varused) f.write_c("%s* o = ", t); + if (name()) f.write_c("%s = ", name()); + if (is_a(Type::Window)) { + // Handle special case where user is faking a Fl_Group type as a window, + // there is no 2-argument constructor in that case: + if (!strstr(t, "Window")) + f.write_c("new %s(0, 0, %d, %d", t, o->w(), o->h()); + else + f.write_c("new %s(%d, %d", t, o->w(), o->h()); + } else if (is_a(Type::Menu_Bar) + && ((Menu_Bar_Node*)this)->is_sys_menu_bar() + && is_in_class()) { + f.write_c("(%s*)new %s(%d, %d, %d, %d", + t, ((Menu_Bar_Node*)this)->sys_menubar_proxy_name(), + o->x(), o->y(), o->w(), o->h()); + } else { + f.write_c("new %s(%d, %d, %d, %d", t, o->x(), o->y(), o->w(), o->h()); + } + if (label() && *label()) { + f.write_c(", "); + switch (Fluid.proj.i18n_type) { + case fld::I18n_Type::NONE : /* None */ + f.write_cstring(label()); + break; + case fld::I18n_Type::GNU : /* GNU gettext */ + f.write_c("%s(", Fluid.proj.i18n_gnu_function.c_str()); + f.write_cstring(label()); + f.write_c(")"); + break; + case fld::I18n_Type::POSIX : /* POSIX catgets */ + f.write_c("catgets(%s,%s,%d,", + Fluid.proj.i18n_pos_file.empty() ? "_catalog" : Fluid.proj.i18n_pos_file.c_str(), + Fluid.proj.i18n_pos_set.c_str(), msgnum()); + f.write_cstring(label()); + f.write_c(")"); + break; + } + } + f.write_c(");\n"); + + f.indentation++; + + // Avoid compiler warning for unused variable. + // Also avoid quality control warnings about incorrect allocation error handling. + if (wused) f.write_c("%sw = o; (void)w;\n", f.indent()); + + write_widget_code(f); +} + +void Widget_Node::write_color(fld::io::Code_Writer& f, const char* field, Fl_Color color) { + const char* color_name = nullptr; + switch (color) { + case FL_FOREGROUND_COLOR: color_name = "FL_FOREGROUND_COLOR"; break; + case FL_BACKGROUND2_COLOR: color_name = "FL_BACKGROUND2_COLOR"; break; + case FL_INACTIVE_COLOR: color_name = "FL_INACTIVE_COLOR"; break; + case FL_SELECTION_COLOR: color_name = "FL_SELECTION_COLOR"; break; + case FL_GRAY0: color_name = "FL_GRAY0"; break; + case FL_DARK3: color_name = "FL_DARK3"; break; + case FL_DARK2: color_name = "FL_DARK2"; break; + case FL_DARK1: color_name = "FL_DARK1"; break; + case FL_BACKGROUND_COLOR: color_name = "FL_BACKGROUND_COLOR"; break; + case FL_LIGHT1: color_name = "FL_LIGHT1"; break; + case FL_LIGHT2: color_name = "FL_LIGHT2"; break; + case FL_LIGHT3: color_name = "FL_LIGHT3"; break; + case FL_BLACK: color_name = "FL_BLACK"; break; + case FL_RED: color_name = "FL_RED"; break; + case FL_GREEN: color_name = "FL_GREEN"; break; + case FL_YELLOW: color_name = "FL_YELLOW"; break; + case FL_BLUE: color_name = "FL_BLUE"; break; + case FL_MAGENTA: color_name = "FL_MAGENTA"; break; + case FL_CYAN: color_name = "FL_CYAN"; break; + case FL_DARK_RED: color_name = "FL_DARK_RED"; break; + case FL_DARK_GREEN: color_name = "FL_DARK_GREEN"; break; + case FL_DARK_YELLOW: color_name = "FL_DARK_YELLOW"; break; + case FL_DARK_BLUE: color_name = "FL_DARK_BLUE"; break; + case FL_DARK_MAGENTA: color_name = "FL_DARK_MAGENTA"; break; + case FL_DARK_CYAN: color_name = "FL_DARK_CYAN"; break; + case FL_WHITE: color_name = "FL_WHITE"; break; + } + const char *var = is_class() ? "this" : name() ? name() : "o"; + if (color_name) { + f.write_c("%s%s->%s(%s);\n", f.indent(), var, field, color_name); + } else { + f.write_c("%s%s->%s((Fl_Color)%d);\n", f.indent(), var, field, color); + } +} + +// this is split from write_code1(fld::io::Code_Writer& f) for Window_Node: +void Widget_Node::write_widget_code(fld::io::Code_Writer& f) { + Fl_Widget* tplate = ((Widget_Node*)factory)->o; + const char *var = is_class() ? "this" : name() ? name() : "o"; + + if (tooltip() && *tooltip()) { + f.write_c("%s%s->tooltip(",f.indent(), var); + switch (Fluid.proj.i18n_type) { + case fld::I18n_Type::NONE : /* None */ + f.write_cstring(tooltip()); + break; + case fld::I18n_Type::GNU : /* GNU gettext */ + f.write_c("%s(", Fluid.proj.i18n_gnu_function.c_str()); + f.write_cstring(tooltip()); + f.write_c(")"); + break; + case fld::I18n_Type::POSIX : /* POSIX catgets */ + f.write_c("catgets(%s,%s,%d,", + Fluid.proj.i18n_pos_file.empty() ? "_catalog" : Fluid.proj.i18n_pos_file.c_str(), + Fluid.proj.i18n_pos_set.c_str(), + msgnum() + 1); + f.write_cstring(tooltip()); + f.write_c(")"); + break; + } + f.write_c(");\n"); + } + + if (is_a(Type::Spinner) && ((Fl_Spinner*)o)->type() != ((Fl_Spinner*)tplate)->type()) + f.write_c("%s%s->type(%d);\n", f.indent(), var, ((Fl_Spinner*)o)->type()); + else if (o->type() != tplate->type() && !is_a(Type::Window)) + f.write_c("%s%s->type(%d);\n", f.indent(), var, o->type()); + if (o->box() != tplate->box() || subclass()) + f.write_c("%s%s->box(FL_%s);\n", f.indent(), var, boxname(o->box())); + + // write shortcut command if needed + int shortcut = 0; + if (is_button()) shortcut = ((Fl_Button*)o)->shortcut(); + else if (is_a(Type::Input)) shortcut = ((Fl_Input_*)o)->shortcut(); + else if (is_a(Type::Value_Input)) shortcut = ((Fl_Value_Input*)o)->shortcut(); + else if (is_a(Type::Text_Display)) shortcut = ((Fl_Text_Display*)o)->shortcut(); + if (shortcut) { + int s = shortcut; + f.write_c("%s%s->shortcut(", f.indent(), var); + if (Fluid.proj.use_FL_COMMAND) { + if (s & FL_CTRL) { f.write_c("FL_CONTROL|"); s &= ~FL_CTRL; } + if (s & FL_META) { f.write_c("FL_COMMAND|"); s &= ~FL_META; } + } else { + if (s & FL_CTRL) { f.write_c("FL_CTRL|"); s &= ~FL_CTRL; } + if (s & FL_META) { f.write_c("FL_META|"); s &= ~FL_META; } + } + if (s & FL_SHIFT) { f.write_c("FL_SHIFT|"); s &= ~FL_SHIFT; } + if (s & FL_ALT) { f.write_c("FL_ALT|"); s &= ~FL_ALT; } + if ((s < 127) && isprint(s)) + f.write_c("'%c');\n", s); + else + f.write_c("0x%x);\n", s); + } + + if (is_a(Type::Button)) { + Fl_Button* b = (Fl_Button*)o; + if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var, + boxname(b->down_box())); + if (b->value()) f.write_c("%s%s->value(1);\n", f.indent(), var); + if (b->compact()) f.write_c("%s%s->compact(%d);\n", f.indent(), var, b->compact()); + } else if (is_a(Type::Input_Choice)) { + Fl_Input_Choice* b = (Fl_Input_Choice*)o; + if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var, + boxname(b->down_box())); + } else if (is_a(Type::Menu_Manager_)) { + Fl_Menu_* b = (Fl_Menu_*)o; + if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var, + boxname(b->down_box())); + } + if (o->color() != tplate->color() || subclass()) + write_color(f, "color", o->color()); + if (o->selection_color() != tplate->selection_color() || subclass()) + write_color(f, "selection_color", o->selection_color()); + if (image) { + image->write_code(f, bind_image_, var); + if (scale_image_w_ || scale_image_h_) { + f.write_c("%s%s->image()->scale(", f.indent(), var); + if (scale_image_w_>0) + f.write_c("%d, ", scale_image_w_); + else + f.write_c("%s->image()->data_w(), ", var); + if (scale_image_h_>0) + f.write_c("%d, 0, 1);\n", scale_image_h_); + else + f.write_c("%s->image()->data_h(), 0, 1);\n", var); + } + } + if (inactive) { + inactive->write_code(f, bind_deimage_, var, 1); + if (scale_deimage_w_ || scale_deimage_h_) { + f.write_c("%s%s->deimage()->scale(", f.indent(), var); + if (scale_deimage_w_>0) + f.write_c("%d, ", scale_deimage_w_); + else + f.write_c("%s->deimage()->data_w(), ", var); + if (scale_deimage_h_>0) + f.write_c("%d, 0, 1);\n", scale_deimage_h_); + else + f.write_c("%s->deimage()->data_h(), 0, 1);\n", var); + } + } + if (o->labeltype() != tplate->labeltype() || subclass()) + f.write_c("%s%s->labeltype(FL_%s);\n", f.indent(), var, + item_name(labeltypemenu, o->labeltype())); + if (o->labelfont() != tplate->labelfont() || subclass()) + f.write_c("%s%s->labelfont(%d);\n", f.indent(), var, o->labelfont()); + if (o->labelsize() != tplate->labelsize() || subclass()) + f.write_c("%s%s->labelsize(%d);\n", f.indent(), var, o->labelsize()); + if (o->labelcolor() != tplate->labelcolor() || subclass()) + write_color(f, "labelcolor", o->labelcolor()); + if (o->horizontal_label_margin() != tplate->horizontal_label_margin()) + f.write_c("%s%s->horizontal_label_margin(%d);\n", f.indent(), var, o->horizontal_label_margin()); + if (o->vertical_label_margin() != tplate->vertical_label_margin()) + f.write_c("%s%s->vertical_label_margin(%d);\n", f.indent(), var, o->vertical_label_margin()); + if (o->label_image_spacing() != tplate->label_image_spacing()) + f.write_c("%s%s->label_image_spacing(%d);\n", f.indent(), var, o->label_image_spacing()); + if (is_a(Type::Valuator_)) { + Fl_Valuator* v = (Fl_Valuator*)o; + Fl_Valuator* t = (Fl_Valuator*)(tplate); + if (v->minimum()!=t->minimum()) + f.write_c("%s%s->minimum(%g);\n", f.indent(), var, v->minimum()); + if (v->maximum()!=t->maximum()) + f.write_c("%s%s->maximum(%g);\n", f.indent(), var, v->maximum()); + if (v->step()!=t->step()) + f.write_c("%s%s->step(%g);\n", f.indent(), var, v->step()); + if (v->value()) { + if (is_a(Type::Scrollbar)) { // Fl_Scrollbar::value(double) is not available + f.write_c("%s%s->Fl_Slider::value(%g);\n", f.indent(), var, v->value()); + } else { + f.write_c("%s%s->value(%g);\n", f.indent(), var, v->value()); + } + } + if (is_a(Type::Slider)) { + double x = ((Fl_Slider*)v)->slider_size(); + double y = ((Fl_Slider*)t)->slider_size(); + if (x != y) f.write_c("%s%s->slider_size(%g);\n", f.indent(), var, x); + } + } + if (is_a(Type::Spinner)) { + Fl_Spinner* v = (Fl_Spinner*)o; + Fl_Spinner* t = (Fl_Spinner*)(tplate); + if (v->minimum()!=t->minimum()) + f.write_c("%s%s->minimum(%g);\n", f.indent(), var, v->minimum()); + if (v->maximum()!=t->maximum()) + f.write_c("%s%s->maximum(%g);\n", f.indent(), var, v->maximum()); + if (v->step()!=t->step()) + f.write_c("%s%s->step(%g);\n", f.indent(), var, v->step()); + if (v->value()!=1.0f) + f.write_c("%s%s->value(%g);\n", f.indent(), var, v->value()); + } + + {Fl_Font ff; int fs; Fl_Color fc; if (textstuff(4,ff,fs,fc)) { + Fl_Font g; int s; Fl_Color c; textstuff(0,g,s,c); + if (g != ff) f.write_c("%s%s->textfont(%d);\n", f.indent(), var, g); + if (s != fs) f.write_c("%s%s->textsize(%d);\n", f.indent(), var, s); + if (c != fc) write_color(f, "textcolor", c); + }} + const char* ud = user_data(); + if (class_name(1) && !parent->is_widget()) ud = "this"; + if (callback()) { + f.write_c("%s%s->callback((Fl_Callback*)%s", f.indent(), var, callback_name(f)); + if (ud) + f.write_c(", (void*)(%s));\n", ud); + else + f.write_c(");\n"); + } else if (ud) { + f.write_c("%s%s->user_data((void*)(%s));\n", f.indent(), var, ud); + } + if (o->align() != tplate->align() || subclass()) { + int i = o->align(); + f.write_c("%s%s->align(Fl_Align(%s", f.indent(), var, + item_name(alignmenu, i & ~FL_ALIGN_INSIDE)); + if (i & FL_ALIGN_INSIDE) f.write_c("|FL_ALIGN_INSIDE"); + f.write_c("));\n"); + } + Fl_When ww = o->when(); + if (ww != tplate->when() || subclass()) + f.write_c("%s%s->when(%s);\n", f.indent(), var, when_symbol_name(ww)); + if (!o->visible() && o->parent()) + f.write_c("%s%s->hide();\n", f.indent(), var); + if (!o->active()) + f.write_c("%s%s->deactivate();\n", f.indent(), var); + if (!is_a(Type::Group) && resizable()) + f.write_c("%sFl_Group::current()->resizable(%s);\n", f.indent(), var); + if (hotspot()) { + if (is_class()) + f.write_c("%shotspot(%s);\n", f.indent(), var); + else if (is_a(Type::Window)) + f.write_c("%s%s->hotspot(%s);\n", f.indent(), var, var); + else + f.write_c("%s%s->window()->hotspot(%s);\n", f.indent(), var, var); + } +} + +void Widget_Node::write_extra_code(fld::io::Code_Writer& f) { + for (int n=0; n < NUM_EXTRA_CODE; n++) + if (extra_code(n) && !isdeclare(extra_code(n))) + f.write_c("%s%s\n", f.indent(), extra_code(n)); +} + +void Widget_Node::write_block_close(fld::io::Code_Writer& f) { + f.indentation--; + f.write_c("%s} // %s* %s\n", f.indent(), subclassname(this), + name() ? name() : "o"); +} + +void Widget_Node::write_code2(fld::io::Code_Writer& f) { + write_extra_code(f); + write_block_close(f); +} + +//////////////////////////////////////////////////////////////// + +void Widget_Node::write_properties(fld::io::Project_Writer &f) { + Node::write_properties(f); + f.write_indent(level+1); + switch (public_) { + case 0: f.write_string("private"); break; + case 1: break; + case 2: f.write_string("protected"); break; + } + if (tooltip() && *tooltip()) { + f.write_string("tooltip"); + f.write_word(tooltip()); + } + if (image_name() && *image_name()) { + if (scale_image_w_ || scale_image_h_) + f.write_string("scale_image {%d %d}", scale_image_w_, scale_image_h_); + f.write_string("image"); + f.write_word(image_name()); + f.write_string("compress_image %d", compress_image_); + } + if (bind_image_) f.write_string("bind_image 1"); + if (inactive_name() && *inactive_name()) { + if (scale_deimage_w_ || scale_deimage_h_) + f.write_string("scale_deimage {%d %d}", scale_deimage_w_, scale_deimage_h_); + f.write_string("deimage"); + f.write_word(inactive_name()); + f.write_string("compress_deimage %d", compress_deimage_); + } + if (bind_deimage_) f.write_string("bind_deimage 1"); + f.write_string("xywh {%d %d %d %d}", o->x(), o->y(), o->w(), o->h()); + Fl_Widget* tplate = ((Widget_Node*)factory)->o; + if (is_a(Type::Spinner) && ((Fl_Spinner*)o)->type() != ((Fl_Spinner*)tplate)->type()) { + f.write_string("type"); + f.write_word(item_name(subtypes(), ((Fl_Spinner*)o)->type())); + } else if (subtypes() && (o->type() != tplate->type() || is_a(Type::Window))) { + f.write_string("type"); + f.write_word(item_name(subtypes(), o->type())); + } + if (o->box() != tplate->box()) { + f.write_string("box"); f.write_word(boxname(o->box()));} + if (is_a(Type::Input)) { + Fl_Input_* b = (Fl_Input_*)o; + if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); + } + if (is_a(Type::Value_Input)) { + Fl_Value_Input* b = (Fl_Value_Input*)o; + if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); + } + if (is_a(Type::Text_Display)) { + Fl_Text_Display* b = (Fl_Text_Display*)o; + if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); + } + if (is_a(Type::Button)) { + Fl_Button* b = (Fl_Button*)o; + if (b->down_box()) { + f.write_string("down_box"); f.write_word(boxname(b->down_box()));} + if (b->shortcut()) f.write_string("shortcut 0x%x", b->shortcut()); + if (b->value()) f.write_string("value 1"); + } else if (is_a(Type::Input_Choice)) { + Fl_Input_Choice* b = (Fl_Input_Choice*)o; + if (b->down_box()) { + f.write_string("down_box"); f.write_word(boxname(b->down_box()));} + } else if (is_a(Type::Menu_)) { + Fl_Menu_* b = (Fl_Menu_*)o; + if (b->down_box()) { + f.write_string("down_box"); f.write_word(boxname(b->down_box()));} + } + if (o->color()!=tplate->color()) + f.write_string("color %d", o->color()); + if (o->selection_color()!=tplate->selection_color()) + f.write_string("selection_color %d", o->selection_color()); + if (o->labeltype()!=tplate->labeltype()) { + f.write_string("labeltype"); + f.write_word(item_name(labeltypemenu, o->labeltype())); + } + if (o->labelfont()!=tplate->labelfont()) + f.write_string("labelfont %d", o->labelfont()); + if (o->labelsize()!=tplate->labelsize()) + f.write_string("labelsize %d", o->labelsize()); + if (o->labelcolor()!=tplate->labelcolor()) + f.write_string("labelcolor %d", o->labelcolor()); + if (o->align()!=tplate->align()) + f.write_string("align %d", o->align()); + if (o->horizontal_label_margin()!=tplate->horizontal_label_margin()) + f.write_string("h_label_margin %d", o->horizontal_label_margin()); + if (o->vertical_label_margin()!=tplate->vertical_label_margin()) + f.write_string("v_label_margin %d", o->vertical_label_margin()); + if (o->label_image_spacing()!=tplate->label_image_spacing()) + f.write_string("image_spacing %d", o->label_image_spacing()); + if (o->when() != tplate->when()) + f.write_string("when %d", o->when()); + if (is_a(Type::Valuator_)) { + Fl_Valuator* v = (Fl_Valuator*)o; + Fl_Valuator* t = (Fl_Valuator*)(tplate); + if (v->minimum()!=t->minimum()) f.write_string("minimum %g",v->minimum()); + if (v->maximum()!=t->maximum()) f.write_string("maximum %g",v->maximum()); + if (v->step()!=t->step()) f.write_string("step %g",v->step()); + if (v->value()!=0.0) f.write_string("value %g",v->value()); + if (is_a(Type::Slider)) { + double x = ((Fl_Slider*)v)->slider_size(); + double y = ((Fl_Slider*)t)->slider_size(); + if (x != y) f.write_string("slider_size %g", x); + } + } + if (is_a(Type::Spinner)) { + Fl_Spinner* v = (Fl_Spinner*)o; + Fl_Spinner* t = (Fl_Spinner*)(tplate); + if (v->minimum()!=t->minimum()) f.write_string("minimum %g",v->minimum()); + if (v->maximum()!=t->maximum()) f.write_string("maximum %g",v->maximum()); + if (v->step()!=t->step()) f.write_string("step %g",v->step()); + if (v->value()!=1.0) f.write_string("value %g",v->value()); + } + {Fl_Font ff; int fs; Fl_Color fc; if (textstuff(4,ff,fs,fc)) { + Fl_Font ft; int s; Fl_Color c; textstuff(0,ft,s,c); + if (ft != ff) f.write_string("textfont %d", ft); + if (s != fs) f.write_string("textsize %d", s); + if (c != fc) f.write_string("textcolor %d", c); + }} + if (!o->visible() && !override_visible_) f.write_string("hide"); + if (!o->active()) f.write_string("deactivate"); + if (resizable()) f.write_string("resizable"); + if (hotspot()) f.write_string(is_a(Type::Menu_Item) ? "divider" : "hotspot"); + for (int n=0; n < NUM_EXTRA_CODE; n++) if (extra_code(n)) { + f.write_indent(level+1); + f.write_string("code%d",n); + f.write_word(extra_code(n)); + } + if (subclass()) { + f.write_indent(level+1); + f.write_string("class"); + f.write_word(subclass()); + } +} + +void Widget_Node::read_property(fld::io::Project_Reader &f, const char *c) { + int x,y,w,h; Fl_Font ft; int s; Fl_Color cc; + if (!strcmp(c,"private")) { + public_ = 0; + } else if (!strcmp(c,"protected")) { + public_ = 2; + } else if (!strcmp(c,"xywh")) { + if (sscanf(f.read_word(),"%d %d %d %d",&x,&y,&w,&h) == 4) { + x += Fluid.pasteoffset; + y += Fluid.pasteoffset; + // FIXME temporary change! + if (f.read_version>=2.0 && o->parent() && o->parent()!=o->window()) { + x += o->parent()->x(); + y += o->parent()->y(); + } + o->resize(x,y,w,h); + } + } else if (!strcmp(c,"tooltip")) { + tooltip(f.read_word()); + } else if (!strcmp(c,"scale_image")) { + if (sscanf(f.read_word(),"%d %d",&w,&h) == 2) { + scale_image_w_ = w; + scale_image_h_ = h; + } + } else if (!strcmp(c,"image")) { + image_name(f.read_word()); + // starting in 2023, `image` is always followed by `compress_image` + // the code below is for compatibility with older .fl files + const char *ext = fl_filename_ext(image_name_); + if ( strcmp(ext, ".jpg") + && strcmp(ext, ".png") + && strcmp(ext, ".svg") + && strcmp(ext, ".svgz")) + compress_image_ = 0; // if it is neither of those, default to uncompressed + } else if (!strcmp(c,"bind_image")) { + bind_image_ = (int)atol(f.read_word()); + } else if (!strcmp(c,"compress_image")) { + compress_image_ = (int)atol(f.read_word()); + } else if (!strcmp(c,"scale_deimage")) { + if (sscanf(f.read_word(),"%d %d",&w,&h) == 2) { + scale_deimage_w_ = w; + scale_deimage_h_ = h; + } + } else if (!strcmp(c,"deimage")) { + inactive_name(f.read_word()); + // starting in 2023, `deimage` is always followed by `compress_deimage` + // the code below is for compatibility with older .fl files + const char *ext = fl_filename_ext(inactive_name_); + if ( strcmp(ext, ".jpg") + && strcmp(ext, ".png") + && strcmp(ext, ".svg") + && strcmp(ext, ".svgz")) + compress_deimage_ = 0; // if it is neither of those, default to uncompressed + } else if (!strcmp(c,"bind_deimage")) { + bind_deimage_ = (int)atol(f.read_word()); + } else if (!strcmp(c,"compress_deimage")) { + compress_deimage_ = (int)atol(f.read_word()); + } else if (!strcmp(c,"type")) { + if (is_a(Type::Spinner)) + ((Fl_Spinner*)o)->type(item_number(subtypes(), f.read_word())); + else + o->type(item_number(subtypes(), f.read_word())); + } else if (!strcmp(c,"box")) { + const char* value = f.read_word(); + if ((x = boxnumber(value))) { + if (x == ZERO_ENTRY) x = 0; + o->box((Fl_Boxtype)x); + } else if (sscanf(value,"%d",&x) == 1) o->box((Fl_Boxtype)x); + } else if (is_a(Type::Button) && !strcmp(c,"down_box")) { + const char* value = f.read_word(); + if ((x = boxnumber(value))) { + if (x == ZERO_ENTRY) x = 0; + ((Fl_Button*)o)->down_box((Fl_Boxtype)x); + } + } else if (is_a(Type::Input_Choice) && !strcmp(c,"down_box")) { + const char* value = f.read_word(); + if ((x = boxnumber(value))) { + if (x == ZERO_ENTRY) x = 0; + ((Fl_Input_Choice*)o)->down_box((Fl_Boxtype)x); + } + } else if (is_a(Type::Menu_) && !strcmp(c,"down_box")) { + const char* value = f.read_word(); + if ((x = boxnumber(value))) { + if (x == ZERO_ENTRY) x = 0; + ((Fl_Menu_*)o)->down_box((Fl_Boxtype)x); + } + } else if (is_button() && !strcmp(c,"value")) { + const char* value = f.read_word(); + ((Fl_Button*)o)->value(atoi(value)); + } else if (!strcmp(c,"color")) { + const char *cw = f.read_word(); + if (cw[0]=='0' && cw[1]=='x') { + sscanf(cw,"0x%x",&x); + o->color(x); + } else { + int n = sscanf(cw,"%d %d",&x,&y); + if (n == 2) { // back compatibility... + if (x != 47) o->color(x); + o->selection_color(y); + } else { + o->color(x); + } + } + } else if (!strcmp(c,"selection_color")) { + if (sscanf(f.read_word(),"%d",&x)) o->selection_color(x); + } else if (!strcmp(c,"labeltype")) { + c = f.read_word(); + if (!strcmp(c,"image")) { + Image_Asset *i = Image_Asset::find(label()); + if (!i) f.read_error("Image file '%s' not found", label()); + else setimage(i); + image_name(label()); + label(""); + } else { + o->labeltype((Fl_Labeltype)item_number(labeltypemenu,c)); + } + } else if (!strcmp(c,"labelfont")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->labelfont(x); + } else if (!strcmp(c,"labelsize")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->labelsize(x); + } else if (!strcmp(c,"labelcolor")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->labelcolor(x); + } else if (!strcmp(c,"align")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->align(x); + } else if (!strcmp(c,"h_label_margin")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->horizontal_label_margin(x); + } else if (!strcmp(c,"v_label_margin")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->vertical_label_margin(x); + } else if (!strcmp(c,"image_spacing")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->label_image_spacing(x); + } else if (!strcmp(c,"when")) { + if (sscanf(f.read_word(),"%d",&x) == 1) o->when(x); + } else if (!strcmp(c,"minimum")) { + if (is_a(Type::Valuator_)) ((Fl_Valuator*)o)->minimum(strtod(f.read_word(),nullptr)); + if (is_a(Type::Spinner)) ((Fl_Spinner*)o)->minimum(strtod(f.read_word(),nullptr)); + } else if (!strcmp(c,"maximum")) { + if (is_a(Type::Valuator_)) ((Fl_Valuator*)o)->maximum(strtod(f.read_word(),nullptr)); + if (is_a(Type::Spinner)) ((Fl_Spinner*)o)->maximum(strtod(f.read_word(),nullptr)); + } else if (!strcmp(c,"step")) { + if (is_a(Type::Valuator_)) ((Fl_Valuator*)o)->step(strtod(f.read_word(),nullptr)); + if (is_a(Type::Spinner)) ((Fl_Spinner*)o)->step(strtod(f.read_word(),nullptr)); + } else if (!strcmp(c,"value")) { + if (is_a(Type::Valuator_)) ((Fl_Valuator*)o)->value(strtod(f.read_word(),nullptr)); + if (is_a(Type::Spinner)) ((Fl_Spinner*)o)->value(strtod(f.read_word(),nullptr)); + } else if ( (!strcmp(c,"slider_size") || !strcmp(c,"size")) && is_a(Type::Slider)) { + ((Fl_Slider*)o)->slider_size(strtod(f.read_word(),nullptr)); + } else if (!strcmp(c,"textfont")) { + if (sscanf(f.read_word(),"%d",&x) == 1) {ft=(Fl_Font)x; textstuff(1,ft,s,cc);} + } else if (!strcmp(c,"textsize")) { + if (sscanf(f.read_word(),"%d",&x) == 1) {s=x; textstuff(2,ft,s,cc);} + } else if (!strcmp(c,"textcolor")) { + if (sscanf(f.read_word(),"%d",&x) == 1) {cc=(Fl_Color)x;textstuff(3,ft,s,cc);} + } else if (!strcmp(c,"hide")) { + o->hide(); + } else if (!strcmp(c,"deactivate")) { + o->deactivate(); + } else if (!strcmp(c,"resizable")) { + resizable(1); + } else if (!strcmp(c,"hotspot") || !strcmp(c, "divider")) { + hotspot(1); + } else if (!strcmp(c,"class")) { + subclass(f.read_word()); + } else if (!strcmp(c,"shortcut")) { + int shortcut = (int)strtol(f.read_word(),nullptr,0); + if (is_button()) ((Fl_Button*)o)->shortcut(shortcut); + else if (is_a(Type::Input)) ((Fl_Input_*)o)->shortcut(shortcut); + else if (is_a(Type::Value_Input)) ((Fl_Value_Input*)o)->shortcut(shortcut); + else if (is_a(Type::Text_Display)) ((Fl_Text_Display*)o)->shortcut(shortcut); + } else { + if (!strncmp(c,"code",4)) { + int n = atoi(c+4); + if (n >= 0 && n <= NUM_EXTRA_CODE) { + extra_code(n,f.read_word()); + return; + } + } else if (!strcmp(c,"extra_code")) { + extra_code(0,f.read_word()); + return; + } + Node::read_property(f, c); + } +} + +Fl_Menu_Item boxmenu1[] = { + // these extra ones are for looking up fdesign saved strings: + {"NO_FRAME", 0,nullptr,(void *)FL_NO_BOX}, + {"ROUNDED3D_UPBOX", 0,nullptr,(void *)_FL_ROUND_UP_BOX}, + {"ROUNDED3D_DOWNBOX", 0,nullptr,(void *)_FL_ROUND_DOWN_BOX}, + {"OVAL3D_UPBOX", 0,nullptr,(void *)_FL_ROUND_UP_BOX}, + {"OVAL3D_DOWNBOX", 0,nullptr,(void *)_FL_ROUND_DOWN_BOX}, + {"0", 0,nullptr,(void *)ZERO_ENTRY}, + {"1", 0,nullptr,(void *)FL_UP_BOX}, + {"2", 0,nullptr,(void *)FL_DOWN_BOX}, + {"3", 0,nullptr,(void *)FL_FLAT_BOX}, + {"4", 0,nullptr,(void *)FL_BORDER_BOX}, + {"5", 0,nullptr,(void *)FL_SHADOW_BOX}, + {"6", 0,nullptr,(void *)FL_FRAME_BOX}, + {"7", 0,nullptr,(void *)FL_ROUNDED_BOX}, + {"8", 0,nullptr,(void *)FL_RFLAT_BOX}, + {"9", 0,nullptr,(void *)FL_RSHADOW_BOX}, + {"10", 0,nullptr,(void *)FL_UP_FRAME}, + {"11", 0,nullptr,(void *)FL_DOWN_FRAME}, + {nullptr}}; + +int lookup_symbol(const char *, int &, int numberok = 0); + +int Widget_Node::read_fdesign(const char* propname, const char* value) { + int v; + if (!strcmp(propname,"box")) { + float x,y,w,h; + if (sscanf(value,"%f %f %f %f",&x,&y,&w,&h) == 4) { + if (fld::io::fdesign_flip) { + Node *p; + for (p = parent; p && !p->is_a(Type::Window); p = p->parent) {/*empty*/} + if (p && p->is_widget()) y = ((Widget_Node*)p)->o->h()-(y+h); + } + x += Fluid.pasteoffset; + y += Fluid.pasteoffset; + o->resize(int(x),int(y),int(w),int(h)); + } + } else if (!strcmp(propname,"label")) { + label(value); + } else if (!strcmp(propname,"name")) { + this->name(value); + } else if (!strcmp(propname,"callback")) { + callback(value); user_data_type("long"); + } else if (!strcmp(propname,"argument")) { + user_data(value); + } else if (!strcmp(propname,"shortcut")) { + if (value[0]) { + char buf[128]; sprintf(buf,"o->shortcut(\"%s\");",value); + extra_code(0,buf); + } + } else if (!strcmp(propname,"style")) { + if (!strncmp(value,"FL_NORMAL",9)) return 1; + if (!lookup_symbol(value,v,1)) return 0; + o->labelfont(v); o->labeltype((Fl_Labeltype)(v>>8)); + } else if (!strcmp(propname,"size")) { + if (!lookup_symbol(value,v,1)) return 0; + o->labelsize(v); + } else if (!strcmp(propname,"type")) { + if (!strncmp(value,"NORMAL",6)) return 1; + if (lookup_symbol(value,v,1)) {o->type(v); return 1;} + if (!strcmp(value+strlen(value)-5,"FRAME")) goto TRY_BOXTYPE; + if (!strcmp(value+strlen(value)-3,"BOX")) goto TRY_BOXTYPE; + return 0; + } else if (!strcmp(propname,"lcol")) { + if (!lookup_symbol(value,v,1)) return 0; + o->labelcolor(v); + } else if (!strcmp(propname,"return")) { + if (!lookup_symbol(value,v,0)) return 0; + o->when(v|FL_WHEN_RELEASE); + } else if (!strcmp(propname,"alignment")) { + if (!lookup_symbol(value,v)) { + // convert old numeric values: + int v1 = atoi(value); if (v1 <= 0 && strcmp(value,"0")) return 0; + v = 0; + if (v1 >= 5) {v = FL_ALIGN_INSIDE; v1 -= 5;} + switch (v1) { + case 0: v += FL_ALIGN_TOP; break; + case 1: v += FL_ALIGN_BOTTOM; break; + case 2: v += FL_ALIGN_LEFT; break; + case 3: v += FL_ALIGN_RIGHT; break; + case 4: v += FL_ALIGN_CENTER; break; + default: return 0; + } + } + o->align(v); + } else if (!strcmp(propname,"resizebox")) { + resizable(1); + } else if (!strcmp(propname,"colors")) { + char* p = (char*)value; + while (*p != ' ') {if (!*p) return 0; p++;} + *p = 0; + int v1; + if (!lookup_symbol(value,v,1) || !lookup_symbol(p+1,v1,1)) { + *p=' '; return 0;} + o->color(v,v1); + } else if (!strcmp(propname,"resize")) { + return !strcmp(value,"FL_RESIZE_ALL"); + } else if (!strcmp(propname,"gravity")) { + return !strcmp(value,"FL_NoGravity FL_NoGravity"); + } else if (!strcmp(propname,"boxtype")) { + TRY_BOXTYPE: + int x = boxnumber(value); + if (!x) {x = item_number(boxmenu1, value); if (x < 0) return 0;} + if (x == ZERO_ENTRY) { + x = 0; + if (o->box() != ((Widget_Node*)factory)->o->box()) return 1; // kludge for frame + } + o->box((Fl_Boxtype)x); + } else { + return 0; + } + return 1; +} + +void leave_live_mode_cb(Fl_Widget*, void*) { + live_mode_cb(nullptr, nullptr); +} + +Fl_Widget *Widget_Node::enter_live_mode(int) { + live_widget = widget(o->x(), o->y(), o->w(), o->h()); + if (live_widget) + copy_properties(); + return live_widget; +} + +Fl_Widget* Widget_Node::propagate_live_mode(Fl_Group* grp) { + live_widget = grp; + copy_properties(); + Node *n; + for (n = next; n && n->level > level; n = n->next) { + if (n->level == level+1) { + Fl_Widget* proxy_child = n->enter_live_mode(); + if (proxy_child && n->is_widget() && ((Widget_Node*)n)->resizable()) { + grp->resizable(proxy_child); + } + } + } + grp->end(); + live_widget = grp; + copy_properties_for_children(); + return live_widget; +} + + +void Widget_Node::leave_live_mode() { +} + +/** + copy all properties from the edit widget to the live widget + */ +void Widget_Node::copy_properties() { + if (!live_widget) + return; + + Fl_Font ff = 0; + int fs = 0; + Fl_Color fc = 0; + textstuff(0, ff, fs, fc); + + // copy all attributes common to all widget types + Fl_Widget *w = live_widget; + w->label(o->label()); + w->tooltip(tooltip()); + w->type(o->type()); + w->box(o->box()); + w->color(o->color()); + w->selection_color(o->selection_color()); + w->labeltype(o->labeltype()); + w->labelfont(o->labelfont()); + w->labelsize(o->labelsize()); + w->labelcolor(o->labelcolor()); + w->align(o->align()); + w->when(o->when()); + + // copy all attributes specific to widgets derived from Fl_Button + if (is_button()) { + Fl_Button* d = (Fl_Button*)live_widget, *s = (Fl_Button*)o; + d->down_box(s->down_box()); + d->shortcut(s->shortcut()); + d->value(s->value()); + } + + // copy all attributes specific to widgets derived from Fl_Input_ + if (is_a(Type::Input)) { + Fl_Input_* d = (Fl_Input_*)live_widget, *s = (Fl_Input_*)o; + d->shortcut(s->shortcut()); + d->textfont(ff); + d->textsize(fs); + d->textcolor(fc); + } + + // copy all attributes specific to widgets derived from Fl_Value_Input + if (is_a(Type::Value_Input)) { + Fl_Value_Input* d = (Fl_Value_Input*)live_widget, *s = (Fl_Value_Input*)o; + d->shortcut(s->shortcut()); + d->textfont(ff); + d->textsize(fs); + d->textcolor(fc); + } + + // copy all attributes specific to widgets derived from Fl_Text_Display + if (is_a(Type::Text_Display)) { + Fl_Text_Display* d = (Fl_Text_Display*)live_widget, *s = (Fl_Text_Display*)o; + d->shortcut(s->shortcut()); + d->textfont(ff); + d->textsize(fs); + d->textcolor(fc); + } + + // copy all attributes specific to Fl_Valuator and derived classes + if (is_a(Type::Valuator_)) { + Fl_Valuator* d = (Fl_Valuator*)live_widget, *s = (Fl_Valuator*)o; + d->minimum(s->minimum()); + d->maximum(s->maximum()); + d->step(s->step()); + d->value(s->value()); + if (is_a(Type::Slider)) { + Fl_Slider *d = (Fl_Slider*)live_widget, *s = (Fl_Slider*)o; + d->slider_size(s->slider_size()); + } + } + + // copy all attributes specific to Fl_Spinner and derived classes + if (is_a(Type::Spinner)) { + Fl_Spinner* d = (Fl_Spinner*)live_widget, *s = (Fl_Spinner*)o; + d->minimum(s->minimum()); + d->maximum(s->maximum()); + d->step(s->step()); + d->value(s->value()); + } + + if (!o->visible()) + w->hide(); + if (!o->active()) + w->deactivate(); +} + diff --git a/fluid/nodes/Widget_Node.h b/fluid/nodes/Widget_Node.h new file mode 100644 index 000000000..261005590 --- /dev/null +++ b/fluid/nodes/Widget_Node.h @@ -0,0 +1,131 @@ +// +// Widget Node header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +// Type for creating all subclasses of Fl_Widget +// This should have the widget pointer in it, but it is still in the +// Node base class. + +#ifndef FLUID_NODES_WIDGET_NODE_H +#define FLUID_NODES_WIDGET_NODE_H + +#include "nodes/Node.h" + +#define NUM_EXTRA_CODE 4 + +class Widget_Node; +class Image_Asset; + +extern void* const LOAD; +extern Widget_Node *current_widget; // one of the selected ones + +extern const char* subclassname(Node* l); +extern int is_name(const char *c); +void selection_changed(Node* new_current); +Node *sort(Node *parent); + +class Widget_Node : public Node +{ + typedef Node super; + + virtual Fl_Widget *widget(int,int,int,int) = 0; + virtual Widget_Node *_make() = 0; // virtual constructor + void setlabel(const char *) override; + + const char *extra_code_[NUM_EXTRA_CODE]; + const char *subclass_; + const char *tooltip_; + const char *image_name_; + const char *inactive_name_; + uchar hotspot_; + +protected: + + /// This variable is set for visible windows in batch mode. + /// We can't open a window in batch mode, even if we want the "visible" flags + /// set, so we need a second place to store this information while also + /// disabling the output of the "hide" property by the Widget Type. + uchar override_visible_; + + void write_static(fld::io::Code_Writer& f) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_widget_code(fld::io::Code_Writer& f); + void write_extra_code(fld::io::Code_Writer& f); + void write_block_close(fld::io::Code_Writer& f); + void write_code2(fld::io::Code_Writer& f) override; + void write_color(fld::io::Code_Writer& f, const char*, Fl_Color); + Fl_Widget *live_widget; + +public: + Fl_Widget *o; + int public_; + int bind_image_; + int compress_image_; + int bind_deimage_; + int compress_deimage_; + int scale_image_w_, scale_image_h_; + int scale_deimage_w_, scale_deimage_h_; + + Image_Asset *image; + void setimage(Image_Asset *); + Image_Asset *inactive; + void setinactive(Image_Asset *); + + Widget_Node(); + Node *make(Strategy strategy) override; + void open() override; + + const char *extra_code(int n) const {return extra_code_[n];} + void extra_code(int n,const char *); + const char *subclass() const {return subclass_;} + void subclass(const char *); + const char *tooltip() const {return tooltip_;} + void tooltip(const char *); + const char *image_name() const {return image_name_;} + void image_name(const char *); + const char *inactive_name() const {return inactive_name_;} + void inactive_name(const char *); + uchar hotspot() const {return hotspot_;} + void hotspot(uchar v) {hotspot_ = v;} + uchar resizable() const; + void resizable(uchar v); + + virtual int textstuff(int what, Fl_Font &, int &, Fl_Color &); + virtual Fl_Menu_Item *subtypes(); + + Type type() const override { return Type::Widget_; } + bool is_a(Type inType) const override { return (inType==Type::Widget_) ? true : super::is_a(inType); } + int is_widget() const override; + int is_true_widget() const override { return 1; } + int is_public() const override; + + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + int read_fdesign(const char*, const char*) override; + + Fl_Widget *enter_live_mode(int top=0) override; + Fl_Widget *propagate_live_mode(Fl_Group* grp); + void leave_live_mode() override; + void copy_properties() override; + + virtual void ideal_size(int &w, int &h); + + ~Widget_Node(); + void redraw(); +}; + +extern Fl_Window *the_panel; + +#endif // FLUID_NODES_WIDGET_NODE_H diff --git a/fluid/nodes/Window_Node.cxx b/fluid/nodes/Window_Node.cxx new file mode 100644 index 000000000..d6cbe80e6 --- /dev/null +++ b/fluid/nodes/Window_Node.cxx @@ -0,0 +1,1497 @@ +// +// Window Node code file for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +// +// The widget describing an Fl_Window. This is also all the code +// for interacting with the overlay, which allows the user to +// select, move, and resize the children widgets. + +#include "nodes/Window_Node.h" + +#include "app/Snap_Action.h" +#include "Fluid.h" +#include "Project.h" +#include "proj/undo.h" +#include "io/Project_Reader.h" +#include "io/Project_Writer.h" +#include "io/Code_Writer.h" +#include "nodes/factory.h" +#include "nodes/Group_Node.h" +#include "nodes/Grid_Node.h" +#include "panels/settings_panel.h" +#include "panels/widget_panel.h" +#include "widgets/Node_Browser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../src/flstring.h" + +#include +#include +#include +#undef min +#undef max +#include + +extern Fl_Window *the_panel; +extern void draw_width(int x, int y, int r, Fl_Align a); +extern void draw_height(int x, int y, int b, Fl_Align a); + +// Update the XYWH values in the widget panel... +static void update_xywh() { + if (current_widget && current_widget->is_widget()) { + Fl_Widget *o = ((Widget_Node *)current_widget)->o; + widget_x_input->value(o->x()); + widget_y_input->value(o->y()); + widget_w_input->value(o->w()); + widget_h_input->value(o->h()); + if (Flex_Node::parent_is_flex(current_widget)) { + widget_flex_size->value(Flex_Node::size(current_widget)); + widget_flex_fixed->value(Flex_Node::is_fixed(current_widget)); + } + } +} + +void i18n_type_cb(Fl_Choice *c, void *v) { + if (v == LOAD) { + c->value(static_cast(Fluid.proj.i18n_type)); + } else { + Fluid.proj.undo.checkpoint(); + Fluid.proj.i18n_type = static_cast(c->value()); + Fluid.proj.set_modflag(1); + } + switch (Fluid.proj.i18n_type) { + case fld::I18n_Type::NONE : /* None */ + i18n_gnu_group->hide(); + i18n_posix_group->hide(); + break; + case fld::I18n_Type::GNU : /* GNU gettext */ + i18n_gnu_group->show(); + i18n_posix_group->hide(); + break; + case fld::I18n_Type::POSIX : /* POSIX cat */ + i18n_gnu_group->hide(); + i18n_posix_group->show(); + break; + } + // make sure that the outside labels are redrawn too. + w_settings_i18n_tab->redraw(); +} + +void show_grid_cb(Fl_Widget *, void *) { + settings_window->show(); + w_settings_tabs->value(w_settings_layout_tab); +} + +void show_settings_cb(Fl_Widget *, void *) { + settings_window->hotspot(settings_window); + settings_window->show(); +} + +//////////////////////////////////////////////////////////////// + +Fl_Menu_Item window_type_menu[] = { + {"Single",0,nullptr,(void*)FL_WINDOW}, + {"Double",0,nullptr,(void*)(FL_DOUBLE_WINDOW)}, + {nullptr}}; + +static int overlays_invisible; + +// The following Fl_Widget is used to simulate the windows. It has +// an overlay for the fluid ui, and special-cases the FL_NO_BOX. + +class Overlay_Window : public Fl_Overlay_Window { + void draw() override; + void draw_overlay() override; + static void close_cb(Overlay_Window *self, void*); +public: + Window_Node *window; + int handle(int) override; + Overlay_Window(int W,int H) : Fl_Overlay_Window(W,H) { + Fl_Group::current(nullptr); + callback((Fl_Callback*)close_cb); + } + void resize(int,int,int,int) override; + uchar *read_image(int &ww, int &hh); +}; + +/** + \brief User closes the window, so we mark the .fl file as changed. + Mark the .fl file a changed, but don;t mark the source files as changed. + \param self pointer to this window + */ +void Overlay_Window::close_cb(Overlay_Window *self, void*) { + if (self->visible()) + Fluid.proj.set_modflag(1, -2); + self->hide(); +} + +// Use this when drawing flat boxes while editing, so users can see the outline, +// even if the group and its parent have the same color. +static void fd_flat_box_ghosted(int x, int y, int w, int h, Fl_Color c) { + fl_rectf(x, y, w, h, Fl::box_color(c)); + fl_rect(x, y, w, h, Fl::box_color(fl_color_average(FL_FOREGROUND_COLOR, c, .1f))); +} + +void Overlay_Window::draw() { + const int CHECKSIZE = 8; + // see if box is clear or a frame or rounded: + if ((damage()&FL_DAMAGE_ALL) && + (!box() || (box()>=4&&!(box()&2)) || box()>=_FL_ROUNDED_BOX)) { + // if so, draw checkerboard so user can see what areas are clear: + for (int Y = 0; Y < h(); Y += CHECKSIZE) + for (int X = 0; X < w(); X += CHECKSIZE) { + fl_color(((Y/(2*CHECKSIZE))&1) != ((X/(2*CHECKSIZE))&1) ? + FL_WHITE : FL_BLACK); + fl_rectf(X,Y,CHECKSIZE,CHECKSIZE); + } + } + if (Fluid.show_ghosted_outline) { + Fl_Box_Draw_F *old_flat_box = Fl::get_boxtype(FL_FLAT_BOX); + Fl::set_boxtype(FL_FLAT_BOX, fd_flat_box_ghosted, 0, 0, 0, 0); + Fl_Overlay_Window::draw(); + Fl::set_boxtype(FL_FLAT_BOX, old_flat_box, 0, 0, 0, 0); + } else { + Fl_Overlay_Window::draw(); + } +} + +// Read an image of the overlay window +uchar *Overlay_Window::read_image(int &ww, int &hh) { + // Create an off-screen buffer for the window... + //Fluid.main_window->make_current(); + make_current(); + + ww = w(); + hh = h(); + + Fl_Offscreen offscreen = fl_create_offscreen(ww, hh); + uchar *pixels; + + // Redraw the window into the offscreen buffer... + fl_begin_offscreen(offscreen); + + if (!shown()) image(Fl::scheme_bg_); + + redraw(); + draw(); + + // Read the screen image... + pixels = fl_read_image(nullptr, 0, 0, ww, hh); + + fl_end_offscreen(); + + // Cleanup and return... + fl_delete_offscreen(offscreen); + Fluid.main_window->make_current(); + return pixels; +} + +void Overlay_Window::draw_overlay() { + window->draw_overlay(); +} + +int Overlay_Window::handle(int e) { + int ret = window->handle(e); + if (ret==0) { + switch (e) { + case FL_SHOW: + case FL_HIDE: + ret = Fl_Overlay_Window::handle(e); + } + } + return ret; +} + +/** + Make and add a new Window node. + \param[in] strategy is Strategy::AS_LAST_CHILD or Strategy::AFTER_CURRENT + \return new node + */ +Node *Window_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) p = p->parent; + while (p && (!p->is_code_block() || p->is_a(Type::Widget_Class))) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + if (!p) { + fl_message("Please select a function"); + return nullptr; + } + Window_Node *myo = new Window_Node(); + if (!this->o) {// template widget + this->o = new Fl_Window(100,100); + Fl_Group::current(nullptr); + } + myo->factory = this; + myo->drag = 0; + myo->numselected = 0; + Overlay_Window *w = new Overlay_Window(100, 100); + w->size_range(10, 10); + w->window = myo; + myo->o = w; + myo->add(anchor, strategy); + myo->modal = 0; + myo->non_modal = 0; + return myo; +} + +void Window_Node::add_child(Node* cc, Node* before) { + if (!cc->is_widget()) return; + Widget_Node* c = (Widget_Node*)cc; + Fl_Widget* b = before ? ((Widget_Node*)before)->o : nullptr; + ((Fl_Window*)o)->insert(*(c->o), b); + o->redraw(); +} + +void Window_Node::remove_child(Node* cc) { + Widget_Node* c = (Widget_Node*)cc; + ((Fl_Window*)o)->remove(c->o); + o->redraw(); +} + +void Window_Node::move_child(Node* cc, Node* before) { + Widget_Node* c = (Widget_Node*)cc; + ((Fl_Window*)o)->remove(c->o); + Fl_Widget* b = before ? ((Widget_Node*)before)->o : nullptr; + ((Fl_Window*)o)->insert(*(c->o), b); + o->redraw(); +} + +//////////////////////////////////////////////////////////////// + +/** + \brief Show the Window Type editor window without setting the modified flag. + \see Window_Node::open() + */ +void Window_Node::open_() { + Overlay_Window *w = (Overlay_Window *)o; + if (w->shown()) { + w->show(); + Widget_Node::open(); + } else { + Fl_Widget *p = w->resizable(); + if (!p) w->resizable(w); + w->show(); + w->resizable(p); + } + w->image(Fl::scheme_bg_); +} + +/** + \brief Show the Window Type editor window and set the modified flag if needed. + Double-click on window widget shows the window, or if already shown, it shows + the control panel. + \see Window_Node::open_() + */ +void Window_Node::open() { + Overlay_Window *w = (Overlay_Window *)o; + if (!w->visible()) { + Fluid.proj.set_modflag(1, -2); + } + open_(); +} + +// Read an image of the window +uchar *Window_Node::read_image(int &ww, int &hh) { + Overlay_Window *w = (Overlay_Window *)o; + + int hidden = !w->shown(); + w->show(); // make it the front window + + // Read the screen image... + uchar *idata = w->read_image(ww, hh); + if (hidden) + w->hide(); + return idata; +} + +void Window_Node::ideal_size(int &w, int &h) { + w = 480; h = 320; + if (Fluid.main_window) { + int sx, sy, sw, sh; + Fl_Window *win = Fluid.main_window; + int screen = Fl::screen_num(win->x(), win->y()); + Fl::screen_work_area(sx, sy, sw, sh, screen); + w = std::min(w, sw*3/4); h = std::min(h, sh*3/4); + } + fld::app::Snap_Action::better_size(w, h); +} + + + +//////////////////////////////////////////////////////////////// + +Window_Node Window_Node::prototype; + +void Window_Node::setlabel(const char *n) { + if (o) ((Fl_Window *)o)->label(n); +} + +// Resize from window manager... +void Overlay_Window::resize(int X,int Y,int W,int H) { + // Make sure we don't create undo checkpoints if the window does not actually change. + // Some WMs seem to send spurious resize events. + if (X!=x() || Y!=y() || W!=w() || H!=h()) { + // Set a checkpoint on the first resize event, ignore further resizes until + // a different type of checkpoint is triggered. + if (Fluid.proj.undo.checkpoint(fld::proj::Undo::OnceType::WINDOW_RESIZE)) + Fluid.proj.set_modflag(1); + } + + Fl_Widget* t = resizable(); + if (Fluid.proj.tree.allow_layout == 0) { + resizable(nullptr); + } + + // do not set the mod flag if the window was not resized. In FLUID, all + // windows are opened without a given x/y position, so modifying x/y + // should not mark the project as dirty + if (W!=w() || H!=h()) + Fluid.proj.set_modflag(1); + + Fl_Overlay_Window::resize(X,Y,W,H); + resizable(t); + update_xywh(); +} + +// calculate actual move by moving mouse position (mx,my) to +// nearest multiple of gridsize, and snap to original position +void Window_Node::newdx() { + int mydx, mydy; + mydx = mx-x1; + mydy = my-y1; + + if (!(drag & (FD_DRAG | FD_BOX | FD_LEFT | FD_RIGHT))) { + mydx = 0; + dx = 0; + } + + if (!(drag & (FD_DRAG | FD_BOX | FD_TOP | FD_BOTTOM))) { + mydy = 0; + dy = 0; + } + + if (Fluid.show_guides && (drag & (FD_DRAG|FD_TOP|FD_LEFT|FD_BOTTOM|FD_RIGHT))) { + Node *selection = nullptr; // special power for the first selected widget + for (Node *q=next; q && q->level>level; q = q->next) { + if (q->selected && q->is_true_widget()) { + selection = q; + break; + } + } + fld::app::Snap_Data data = { mydx, mydy, bx, by, br, bt, drag, 4, 4, mydx, mydy, (Widget_Node*)selection, this }; + fld::app::Snap_Action::check_all(data); + if (data.x_dist < 4) mydx = data.dx_out; + if (data.y_dist < 4) mydy = data.dy_out; + } + + if (dx != mydx || dy != mydy) { + dx = mydx; dy = mydy; + ((Overlay_Window *)o)->redraw_overlay(); + } +} + +// Move a widget according to dx and dy calculated above +void Window_Node::newposition(Widget_Node *myo,int &X,int &Y,int &R,int &T) { + X = myo->o->x(); + Y = myo->o->y(); + R = X+myo->o->w(); + T = Y+myo->o->h(); + if (!drag) return; + if (drag&FD_DRAG) { + X += dx; + Y += dy; + R += dx; + T += dy; + } else { + if (drag&FD_LEFT) { + if (X==bx) { + X += dx; + } else { + if (Xbr+dx) R = br+dx; + } + } + if (drag&FD_BOTTOM) { + if (T==bt) { + T += dy; + } else { + if (T>bt+dx) T = bt+dx; + } + } + } + if (R h) { + for (; yp < h; yp+=size) + fl_line(x, y+yp, x+yp, y); + for (; yp < w; yp+=size) + fl_line(x+yp-h, y+h, x+yp, y); + for (; yp < w+h; yp+=size) + fl_line(x+yp-h, y+h, x+w, y+yp-w); + } else { + for (; yp < w; yp+=size) + fl_line(x, y+yp, x+yp, y); + for (; yp < h; yp+=size) + fl_line(x, y+yp, x+w, y+yp-w); + for (; yp < h+w; yp+=size) + fl_line(x+yp-h, y+h, x+w, y+yp-w); + } +} + +/** + \brief Draw a hatch pattern over all children that overlap the bounds of this box. + \param[in] group check all children of this group + \param[in] x, y, w, h bounding box of this group + */ +void Window_Node::draw_out_of_bounds(Widget_Node *group, int x, int y, int w, int h) { + for (Node *p = group->next; p && p->level>group->level; p = p->next) { + if (p->level == group->level+1 && p->is_true_widget()) { + Fl_Widget *o = ((Widget_Node*)p)->o; + if (o->x() < x) fd_hatch(o->x(), o->y(), x-o->x(), o->h()); + if (o->y() < y) fd_hatch(o->x(), o->y(), o->w(), y-o->y()); + if (o->x()+o->w() > x+w) fd_hatch(x+w, o->y(), (o->x()+o->w())-(x+w), o->h()); + if (o->y()+o->h() > y+h) fd_hatch(o->x(), y+h, o->w(), (o->y()+o->h())-(y+h)); + } + } +} + +/** + \brief Draw a hatch pattern for all groups that have out of bounds children. + */ +void Window_Node::draw_out_of_bounds() { + // get every group in the hierarchy, then draw any overlap of a direct child with that group + fl_color(FL_DARK_RED); + draw_out_of_bounds(this, 0, 0, o->w(), o->h()); + for (Node *q=next; q && q->level>level; q = q->next) { + // don't do this for Fl_Scroll (which we currently can't handle in FLUID anyway) + if (q->is_a(Type::Group) && !q->is_a(Type::Scroll)) { + Widget_Node *w = (Widget_Node*)q; + draw_out_of_bounds(w, w->o->x(), w->o->y(), w->o->w(), w->o->h()); + } + } + fl_color(FL_RED); +} + +/** + \brief Compare all children in the same level and hatch overlapping areas. + */ +void Window_Node::draw_overlaps() { + fl_color(FL_DARK_YELLOW); + // loop through all widgets in this window + for (Node *q=next; q && q->level>level; q = q->next) { + // is it a valid widget + if (q->is_true_widget()) { + Widget_Node *w = (Widget_Node*)q; + // is the widget visible + if (w->o->visible()) { + int x = w->o->x(), y = w->o->y(); + int r = x + w->o->w(), b = y + w->o->h(); + for (Node *p=q->next; p && p->level>=q->level; p = p->next) { + if (p->level==q->level && p->is_true_widget()) { + Widget_Node *wp = (Widget_Node*)p; + if (wp->o->visible()) { + int px = std::max(x, wp->o->x()); + int py = std::max(y, wp->o->y()); + int pr = std::min(r, wp->o->x() + wp->o->w()); + int pb = std::min(b, wp->o->y() + wp->o->h()); + if (pr > px && pb > py) + fd_hatch(px, py, pr-px, pb-py); + } + } + } + } else { + int l = q->level; + for (; q && q->next && q->next->level>l; q = q->next) { } + } + } + } + fl_color(FL_RED); +} + +void Window_Node::draw_overlay() { + if (recalc) { + bx = o->w(); by = o->h(); br = 0; bt = 0; + numselected = 0; + for (Node *q=next; q && q->level>level; q=q->next) + if (q->selected && q->is_true_widget()) { + numselected++; + Widget_Node* myo = (Widget_Node*)q; + if (myo->o->x() < bx) bx = myo->o->x(); + if (myo->o->y() < by) by = myo->o->y(); + if (myo->o->x()+myo->o->w() > br) br = myo->o->x()+myo->o->w(); + if (myo->o->y()+myo->o->h() > bt) bt = myo->o->y()+myo->o->h(); + } + recalc = 0; + sx = bx; sy = by; sr = br; st = bt; + } + fl_color(FL_RED); + if (drag==FD_BOX && (x1 != mx || y1 != my)) { + int x = x1; int r = mx; if (x > r) {x = mx; r = x1;} + int y = y1; int b = my; if (y > b) {y = my; b = y1;} + fl_rect(x,y,r-x,b-y); + } + if (overlays_invisible && !drag) return; + + if (Fluid.show_restricted) { + draw_out_of_bounds(); + draw_overlaps(); + // TODO: for Fl_Tile, find all areas that are not covered by visible children + } + + if (selected) fl_rect(0,0,o->w(),o->h()); + if (!numselected) return; + int mybx,myby,mybr,mybt; + int mysx,mysy,mysr,myst; + mybx = mysx = o->w(); myby = mysy = o->h(); mybr = mysr = 0; mybt = myst = 0; + Node *selection = nullptr; // special power for the first selected widget + for (Node *q=next; q && q->level>level; q = q->next) + if (q->selected && q->is_true_widget()) { + if (!selection) selection = q; + Widget_Node* myo = (Widget_Node*)q; + int x,y,r,t; + newposition(myo,x,y,r,t); + if (Fluid.show_guides) { + // If we are in a drag operation, and the parent is a grid, show the grid overlay + if (drag && q->parent && q->parent->is_a(Type::Grid)) { + Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)((Grid_Node*)q->parent)->o); + grid->draw_overlay(); + } + } + if (!Fluid.show_guides || !drag || numselected != 1) { + if (Flex_Node::parent_is_flex(q) && Flex_Node::is_fixed(q)) { + Fl_Flex *flex = ((Fl_Flex*)((Flex_Node*)q->parent)->o); + Fl_Widget *wgt = myo->o; + if (flex->horizontal()) { + draw_width(wgt->x(), wgt->y()+15, wgt->x()+wgt->w(), FL_ALIGN_CENTER); + } else { + draw_height(wgt->x()+15, wgt->y(), wgt->y()+wgt->h(), FL_ALIGN_CENTER); + } + } else if (q->is_a(Type::Grid)) { + Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)((Grid_Node*)q)->o); + grid->draw_overlay(); + } + fl_rect(x,y,r-x,t-y); + } + if (x < mysx) mysx = x; + if (y < mysy) mysy = y; + if (r > mysr) mysr = r; + if (t > myst) myst = t; + if (!(myo->o->align() & FL_ALIGN_INSIDE)) { + // Adjust left/right/top/bottom for top/bottom labels... + int ww, hh; + ww = (myo->o->align() & FL_ALIGN_WRAP) ? myo->o->w() : 0; + hh = myo->o->labelsize(); + myo->o->measure_label(ww, hh); + if (myo->o->align() & FL_ALIGN_TOP) y -= hh; + else if (myo->o->align() & FL_ALIGN_BOTTOM) t += hh; + else if (myo->o->align() & FL_ALIGN_LEFT) x -= ww + 4; + else if (myo->o->align() & FL_ALIGN_RIGHT) r += ww + 4; + } + if (x < mybx) mybx = x; + if (y < myby) myby = y; + if (r > mybr) mybr = r; + if (t > mybt) mybt = t; + } + if (selected) return; + + // align the snapping selection box with the box we draw. + sx = mysx; sy = mysy; sr = mysr; st = myst; + + // Draw selection box + resize handles... + // draw box including all labels + fl_focus_rect(mybx,myby,mybr-mybx,mybt-myby); // issue #816 + // draw box excluding labels + fl_rect(mysx,mysy,mysr-mysx,myst-mysy); + fl_rectf(mysx,mysy,5,5); + fl_rectf(mysr-5,mysy,5,5); + fl_rectf(mysr-5,myst-5,5,5); + fl_rectf(mysx,myst-5,5,5); + + if (Fluid.show_guides && (drag & (FD_DRAG|FD_TOP|FD_LEFT|FD_BOTTOM|FD_RIGHT))) { + fld::app::Snap_Data data = { dx, dy, sx, sy, sr, st, drag, 4, 4, dx, dy, (Widget_Node*)selection, this}; + fld::app::Snap_Action::draw_all(data); + } +} + +// Calculate new bounding box of selected widgets: +void Window_Node::fix_overlay() { + Fluid.overlay_item->label("Hide O&verlays"); + if (overlay_button) overlay_button->label("Hide &Overlays"); + overlays_invisible = 0; + recalc = 1; + ((Overlay_Window *)(this->o))->redraw_overlay(); +} + +// check if we must redraw any parent of tabs/wizard type +void check_redraw_corresponding_parent(Node *s) { + Widget_Node * prev_parent = nullptr; + if( !s || !s->selected || !s->is_widget()) return; + for (Node *i=s; i && i->parent; i=i->parent) { + if (i->is_a(Type::Group) && prev_parent) { + if (i->is_a(Type::Tabs)) { + ((Fl_Tabs*)((Widget_Node*)i)->o)->value(prev_parent->o); + return; + } + if (i->is_a(Type::Wizard)) { + ((Fl_Wizard*)((Widget_Node*)i)->o)->value(prev_parent->o); + return; + } + } + if (i->is_a(Type::Group) && s->is_widget()) + prev_parent = (Widget_Node*)i; + } +} + +// do that for every window (when selected set changes): +void redraw_overlays() { + for (Node *o=Fluid.proj.tree.first; o; o=o->next) + if (o->is_a(Type::Window)) ((Window_Node*)o)->fix_overlay(); +} + +void toggle_overlays(Fl_Widget *,void *) { + overlays_invisible = !overlays_invisible; + + if (overlays_invisible) { + Fluid.overlay_item->label("Show O&verlays"); + if (overlay_button) overlay_button->label("Show &Overlays"); + } else { + Fluid.overlay_item->label("Hide O&verlays"); + if (overlay_button) overlay_button->label("Hide &Overlays"); + } + + for (Node *o=Fluid.proj.tree.first; o; o=o->next) + if (o->is_a(Type::Window)) { + Widget_Node* w = (Widget_Node*)o; + ((Overlay_Window*)(w->o))->redraw_overlay(); + } +} + +/** + \brief User changes settings to show positioning guides in layout editor overlay. + This is called from the main menu and from the check button in the Settings + dialog. + */ +void toggle_guides(Fl_Widget *,void *) { + Fluid.show_guides = !Fluid.show_guides; + Fluid.preferences.set("Fluid.show_guides", Fluid.show_guides); + + if (Fluid.show_guides) + Fluid.guides_item->label("Hide Guides"); + else + Fluid.guides_item->label("Show Guides"); + if (guides_button) + guides_button->value(Fluid.show_guides); + + for (Node *o=Fluid.proj.tree.first; o; o=o->next) { + if (o->is_a(Type::Window)) { + Widget_Node* w = (Widget_Node*)o; + ((Overlay_Window*)(w->o))->redraw_overlay(); + } + } +} + +/** + \brief User changes settings to show positioning guides in layout editor overlay. + This is called from the check button in the Settings dialog. + */ +void toggle_guides_cb(Fl_Check_Button *o, void *v) { + toggle_guides(nullptr, nullptr); +} + +/** + \brief User changes settings to show overlapping and out of bounds widgets. + This is called from the main menu and from the check button in the Settings + dialog. + */ +void toggle_restricted(Fl_Widget *,void *) { + Fluid.show_restricted = !Fluid.show_restricted; + Fluid.preferences.set("Fluid.show_restricted", Fluid.show_restricted); + + if (Fluid.show_restricted) + Fluid.restricted_item->label("Hide Restricted"); + else + Fluid.restricted_item->label("Show Restricted"); + if (restricted_button) + restricted_button->value(Fluid.show_restricted); + + for (Node *o=Fluid.proj.tree.first; o; o=o->next) { + if (o->is_a(Type::Window)) { + Widget_Node* w = (Widget_Node*)o; + ((Overlay_Window*)(w->o))->redraw_overlay(); + } + } +} + +/** + \brief User changes settings to show low contrast groups with a ghosted outline. + */ +void toggle_ghosted_outline_cb(Fl_Check_Button *,void *) { + Fluid.show_ghosted_outline = !Fluid.show_ghosted_outline; + Fluid.preferences.set("Fluid.show_ghosted_outline", Fluid.show_ghosted_outline); + for (Node *o=Fluid.proj.tree.first; o; o=o->next) { + if (o->is_a(Type::Window)) { + Widget_Node* w = (Widget_Node*)o; + ((Overlay_Window*)(w->o))->redraw(); + } + } +} + +/** + \brief User changes settings to show overlapping and out of bounds widgets. + This is called from the check button in the Settings dialog. + */ +void toggle_restricted_cb(Fl_Check_Button *o, void *v) { + toggle_restricted(nullptr, nullptr); +} + +extern void select(Node *,int); +extern void select_only(Node *); +extern void deselect(); +extern Node* in_this_only; +extern void fix_group_size(Node *t); + +extern Fl_Menu_Item New_Menu[]; + +/** + Move the selected children according to current dx, dy, drag state. + + This is somewhat of a do-all function that received many additions when new + widget types were added. In the default case, moving a group will simply move + all descendants with it. When resizing, children are resized to fit within + the group. + + This is not ideal for widgets that are moved or resized within a group that + manages the layout of its children. We must create a more universal way to + modify move events per widget type. + + \param[in] key if key is not 0, it contains the code of the keypress that + caused this call. This must only be set when handle FL_KEYBOARD events. + */ +void Window_Node::moveallchildren(int key) +{ + bool update_widget_panel = false; + Fluid.proj.undo.checkpoint(); + Node *i; + for (i=next; i && i->level>level;) { + if (i->selected && i->is_true_widget()) { + Widget_Node* myo = (Widget_Node*)i; + int x,y,r,t,ow=myo->o->w(),oh=myo->o->h(); + newposition(myo,x,y,r,t); + if (myo->is_a(Type::Flex) || myo->is_a(Type::Grid)) { + // Flex and Grid need to be able to layout their children. + Fluid.proj.tree.allow_layout++; + myo->o->resize(x,y,r-x,t-y); + Fluid.proj.tree.allow_layout--; + } else { + // Other groups are resized without affecting their children, however + // they move their children if the entire widget is moved. + myo->o->resize(x,y,r-x,t-y); + } + if (Flex_Node::parent_is_flex(myo)) { + // If the border of a Flex child is move, give that child a fixed size + // so that the user request is reflected. + Flex_Node* ft = (Flex_Node*)myo->parent; + Fl_Flex* f = (Fl_Flex*)ft->o; + if (key) { + ft->keyboard_move_child(myo, key); + } else if (drag & FD_DRAG) { + ft->insert_child_at(myo->o, Fl::event_x(), Fl::event_y()); + } else { + if (f->horizontal()) { + if (myo->o->w()!=ow) { + f->fixed(myo->o, myo->o->w()); + f->layout(); + } + } else { + if (myo->o->h()!=oh) { + f->fixed(myo->o, myo->o->h()); + f->layout(); + } + } + } + // relayout the Flex parent + Fluid.proj.tree.allow_layout++; + f->layout(); + Fluid.proj.tree.allow_layout--; + } else if (myo->parent && myo->parent->is_a(Type::Grid)) { + Grid_Node* gt = (Grid_Node*)myo->parent; + Fl_Grid* g = (Fl_Grid*)gt->o; + if (key) { + gt->keyboard_move_child(myo, key); + } else { + if (drag & FD_DRAG) { + gt->insert_child_at(myo->o, Fl::event_x(), Fl::event_y()); + } else { + gt->child_resized(myo); + } + } + Fluid.proj.tree.allow_layout++; + g->layout(); + Fluid.proj.tree.allow_layout--; + update_widget_panel = true; + } else if (myo->parent && myo->parent->is_a(Type::Group)) { + Group_Node* gt = (Group_Node*)myo->parent; + ((Fl_Group*)gt->o)->init_sizes(); + } + // move all the children, whether selected or not: + Node* p; + for (p = myo->next; p && p->level>myo->level; p = p->next) + if (p->is_true_widget() && !myo->is_a(Type::Flex) && !myo->is_a(Type::Grid)) { + Widget_Node* myo2 = (Widget_Node*)p; + int X,Y,R,T; + newposition(myo2,X,Y,R,T); + myo2->o->resize(X,Y,R-X,T-Y); + } + i = p; + } else { + i = i->next; + } + } + for (i=next; i && i->level>level; i=i->next) + fix_group_size(i); + o->redraw(); + recalc = 1; + ((Overlay_Window *)(this->o))->redraw_overlay(); + Fluid.proj.set_modflag(1); + dx = dy = 0; + + update_xywh(); + if (update_widget_panel && the_panel && the_panel->visible()) { + propagate_load(the_panel, LOAD); + } +} + +int Window_Node::popupx = 0x7FFFFFFF; // mark as invalid (MAXINT) +int Window_Node::popupy = 0x7FFFFFFF; + +int Window_Node::handle(int event) { + static Node* selection = nullptr; + switch (event) { + case FL_DND_ENTER: + // printf("DND enter\n"); + case FL_DND_DRAG: + // printf("DND drag\n"); + { + // find the innermost item clicked on: + selection = this; + for (Node* i=next; i && i->level>level; i=i->next) + if (i->is_a(Type::Group)) { + Widget_Node* myo = (Widget_Node*)i; + if (Fl::event_inside(myo->o) && myo->o->visible_r()) { + selection = myo; + if (Fl::event_clicks()==1) + reveal_in_browser(myo); + } + } + if (selection && !selection->selected) { + select_only(selection); + ((Overlay_Window *)o)->redraw_overlay(); + } + } + Fl::belowmouse(o); + return 1; + case FL_DND_RELEASE: + // printf("DND release\n"); + Fl::belowmouse(o); + return 1; + case FL_PASTE: + // printf("DND paste\n"); + { Node *prototype = typename_to_prototype(Fl::event_text()); + if (prototype==nullptr) { + // it's not a FLUID type, so it could be the filename of an image + const char *cfn = Fl::event_text(); + // printf("DND is filename %s?\n", cfn); + if ((cfn == nullptr) || (*cfn == 0)) return 0; + if (strlen(cfn) >= FL_PATH_MAX) return 0; + char fn[FL_PATH_MAX+1]; + // some platform prepend "file://" or "computer://" or similar text + const char *sep = strstr(cfn, "://"); + if (sep) + strcpy(fn, sep+3); + else + strcpy(fn, cfn); + // remove possibly trailing \r\n + int n = (int)strlen(fn)-1; + if (fn[n] == '\n') fn[n--] = 0; + if (fn[n] == '\r') fn[n--] = 0; + // on X11 and Wayland (?), filenames need to be decoded +#if (defined(FLTK_USE_X11) || defined(FLTK_USE_WAYLAND)) + fl_decode_uri(fn); +#endif + // does a file by that name actually exist? + if (fl_access(fn, 4)==-1) return 0; + // but is this an image file? + Fl_Image *img = Fl_Shared_Image::get(fn); + if (!img || (img->ld() < 0)) return 0; + // ok, so it is an image - now add it as image() or deimage() to the widget + // printf("DND check for target %s\n", fn); + Widget_Node *tgt = nullptr; + for (Node* i=next; i && i->level>level; i=i->next) { + if (i->is_widget()) { + Widget_Node* myo = (Widget_Node*)i; + if (Fl::event_inside(myo->o) && myo->o->visible_r()) + tgt = myo; + } + } + if (tgt) { + char rel[FL_PATH_MAX+1]; + Fluid.proj.enter_project_dir(); + fl_filename_relative(rel, FL_PATH_MAX, fn); + Fluid.proj.leave_project_dir(); + // printf("DND image = %s\n", fn); + if (Fl::get_key(FL_Alt_L) || Fl::get_key(FL_Alt_R)) { + //if (Fl::event_alt()) { // TODO: X11/Wayland does not set the e_state on DND events + tgt->inactive_name(rel); + tgt->compress_deimage_ = 1; + tgt->bind_deimage_ = 0; + } else { + tgt->image_name(rel); + tgt->compress_image_ = 1; + tgt->bind_image_ = 0; + } + select_only(tgt); + tgt->open(); + } + return 1; + } + + in_this_only = this; + popupx = Fl::event_x(); + popupy = Fl::event_y(); + // If the selected widget at dnd start and the drop target are the same, + // or in the same group, add after selection. Otherwise, just add + // at the end of the selected group. + if ( Fluid.proj.tree.current_dnd->group() + && selection && selection->group() + && Fluid.proj.tree.current_dnd->group()==selection->group()) + { + Node *cc = Fluid.proj.tree.current; + Fluid.proj.tree.current = Fluid.proj.tree.current_dnd; + add_new_widget_from_user(prototype, Strategy::AS_LAST_CHILD); + Fluid.proj.tree.current = cc; + } else { + add_new_widget_from_user(prototype, Strategy::AS_LAST_CHILD); + } + popupx = 0x7FFFFFFF; + popupy = 0x7FFFFFFF; // mark as invalid (MAXINT) + in_this_only = nullptr; + widget_browser->display(Fluid.proj.tree.current); + widget_browser->rebuild(); + return 1; + } + case FL_PUSH: + x1 = mx = Fl::event_x(); + y1 = my = Fl::event_y(); + drag = dx = dy = 0; + // test for popup menu: + if (Fl::event_button() >= 3) { + in_this_only = this; // modifies how some menu items work. + static const Fl_Menu_Item* myprev; + popupx = mx; popupy = my; + const Fl_Menu_Item* m = New_Menu->popup(mx,my,"New",myprev); + if (m && m->callback()) {myprev = m; m->do_callback(this->o);} + popupx = 0x7FFFFFFF; popupy = 0x7FFFFFFF; // mark as invalid (MAXINT) + in_this_only = nullptr; + return 1; + } + // find the innermost item clicked on: + selection = this; + {for (Node* i=next; i && i->level>level; i=i->next) + if (i->is_true_widget()) { + Widget_Node* myo = (Widget_Node*)i; + for (Fl_Widget *o1 = myo->o; o1; o1 = o1->parent()) + if (!o1->visible()) goto CONTINUE2; + if (Fl::event_inside(myo->o)) { + selection = myo; + if (Fl::event_clicks()==1) + reveal_in_browser(myo); + } + CONTINUE2:; + }} + // see if user grabs edges of selected region: + if (numselected && !(Fl::event_state(FL_SHIFT)) && + mx<=br+2 && mx>=bx-2 && my<=bt+2 && my>=by-2) { + if (mx >= br-5) drag |= FD_RIGHT; + else if (mx <= bx+5) drag |= FD_LEFT; + if (my >= bt-5) drag |= FD_BOTTOM; + else if (my <= by+5) drag |= FD_TOP; + if (!drag) drag = FD_DRAG; + } + // do object-specific selection of other objects: + {Node* t = selection->click_test(mx, my); + if (t) { + //if (t == selection) return 1; // indicates mouse eaten w/o change + if (Fl::event_state(FL_SHIFT)) { + Fl::event_is_click(0); + select(t, !t->selected); + } else { + deselect(); + select(t, 1); + if (t->is_a(Type::Menu_Item)) t->open(); + } + selection = t; + drag = 0; + } else { + if (!drag) drag = FD_BOX; // if all else fails, start a new selection region + }} + return 1; + + case FL_DRAG: + if (!drag) return 0; + mx = Fl::event_x(); + my = Fl::event_y(); + newdx(); + return 1; + + case FL_RELEASE: + if (!drag) return 0; + mx = Fl::event_x(); + my = Fl::event_y(); + if (drag != FD_BOX && (dx || dy || !Fl::event_is_click())) { + if (dx || dy) moveallchildren(); + } else if ((Fl::event_clicks() || Fl::event_state(FL_CTRL))) { + Widget_Node::open(); + } else { + if (mxlevel>level; i=i->next) + if (i->is_true_widget()) { + Widget_Node* myo = (Widget_Node*)i; + for (Fl_Widget *o1 = myo->o; o1; o1 = o1->parent()) + if (!o1->visible()) goto CONTINUE; + if (Fl::event_inside(myo->o)) selection = myo; + if (myo && myo->o && myo->o->x()>=x1 && myo->o->y()>y1 && + myo->o->x()+myo->o->w()o->y()+myo->o->h()selected : 1); + } + CONTINUE:; + } + // if nothing in box, select what was clicked on: + if (selection && !n) { + select(selection, toggle ? !selection->selected : 1); + } + } + drag = 0; + ((Overlay_Window *)o)->redraw_overlay(); + return 1; + + case FL_KEYBOARD: { + + int backtab = 0; + switch (Fl::event_key()) { + + case FL_Escape: + ((Fl_Window*)o)->hide(); + return 1; + + case FL_Tab: { + if (Fl::event_state(FL_SHIFT)) backtab = 1; + // find current child: + Node *i = Fluid.proj.tree.current; + while (i && !i->is_true_widget()) i = i->parent; + if (!i) return 0; + Node *p = i->parent; + while (p && p != this) p = p->parent; + if (!p || !p->is_widget()) { + i = next; if (!i || i->level <= level) return 0; + } + p = i; + for (;;) { + i = backtab ? i->prev : i->next; + if (!i || i->level <= level) {i = p; break;} + if (i->is_true_widget()) break; + } + deselect(); select(i,1); + return 1;} + + case FL_Left: dx = -1; dy = 0; goto ARROW; + case FL_Right: dx = +1; dy = 0; goto ARROW; + case FL_Up: dx = 0; dy = -1; goto ARROW; + case FL_Down: dx = 0; dy = +1; goto ARROW; + ARROW: + drag = (Fl::event_state(FL_SHIFT)) ? (FD_RIGHT|FD_BOTTOM) : FD_DRAG; + if (Fl::event_state(FL_COMMAND)) { + int x_step, y_step; + if (drag & (FD_RIGHT|FD_BOTTOM)) + fld::app::Snap_Action::get_resize_stepsize(x_step, y_step); + else + fld::app::Snap_Action::get_move_stepsize(x_step, y_step); + dx *= x_step; + dy *= y_step; + } + moveallchildren(Fl::event_key()); + drag = 0; + return 1; + + case 'o': + toggle_overlays(nullptr, nullptr); + break; + + default: + return 0; + }} + + case FL_SHORTCUT: { + in_this_only = this; // modifies how some menu items work. + const Fl_Menu_Item* m = Fluid.main_menu->test_shortcut(); + if (m && m->callback()) m->do_callback(this->o); + in_this_only = nullptr; + return (m != nullptr);} + + default: + return 0; + } +} + +//////////////////////////////////////////////////////////////// + + +/** + Write the C++ code that comes before the children of the window are written. + \param f the source code output stream + */ +void Window_Node::write_code1(fld::io::Code_Writer& f) { + Widget_Node::write_code1(f); +} + + +/** + Write the C++ code that comes after the children of the window are written. + \param f the source code output stream + */ +void Window_Node::write_code2(fld::io::Code_Writer& f) { + const char *var = is_class() ? "this" : name() ? name() : "o"; + // make the window modal or non-modal + if (modal) { + f.write_c("%s%s->set_modal();\n", f.indent(), var); + } else if (non_modal) { + f.write_c("%s%s->set_non_modal();\n", f.indent(), var); + } + // clear the window border + if (!((Fl_Window*)o)->border()) { + f.write_c("%s%s->clear_border();\n", f.indent(), var); + } + // set the xclass of the window + if (xclass) { + f.write_c("%s%s->xclass(", f.indent(), var); + f.write_cstring(xclass); + f.write_c(");\n"); + } + // make the window resizable + if (((Fl_Window*)o)->resizable() == o) + f.write_c("%s%s->resizable(%s);\n", f.indent(), var, var); + // set the size range last + if (sr_max_w || sr_max_h) { + f.write_c("%s%s->size_range(%d, %d, %d, %d);\n", f.indent(), var, + sr_min_w, sr_min_h, sr_max_w, sr_max_h); + } else if (sr_min_w || sr_min_h) { + f.write_c("%s%s->size_range(%d, %d);\n", f.indent(), var, sr_min_w, sr_min_h); + } + // insert extra code from user, may call `show()` + write_extra_code(f); + // stop adding widgets to this window + f.write_c("%s%s->end();\n", f.indent(), var); + write_block_close(f); +} + +void Window_Node::write_properties(fld::io::Project_Writer &f) { + Widget_Node::write_properties(f); + if (modal) f.write_string("modal"); + else if (non_modal) f.write_string("non_modal"); + if (!((Fl_Window*)o)->border()) f.write_string("noborder"); + if (xclass) {f.write_string("xclass"); f.write_word(xclass);} + if (sr_min_w || sr_min_h || sr_max_w || sr_max_h) + f.write_string("size_range {%d %d %d %d}", sr_min_w, sr_min_h, sr_max_w, sr_max_h); + if (o->visible() || override_visible_) f.write_string("visible"); +} + +void Window_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"modal")) { + modal = 1; + } else if (!strcmp(c,"non_modal")) { + non_modal = 1; + } else if (!strcmp(c, "visible")) { + if (Fluid.batch_mode) // don't actually open any windows in batch mode + override_visible_ = 1; + else // in interactive mode, we simply show the window + open_(); + } else if (!strcmp(c,"noborder")) { + ((Fl_Window*)o)->border(0); + } else if (!strcmp(c,"xclass")) { + storestring(f.read_word(),xclass); + ((Fl_Window*)o)->xclass(xclass); + } else if (!strcmp(c,"size_range")) { + int mw, mh, MW, MH; + if (sscanf(f.read_word(),"%d %d %d %d",&mw,&mh,&MW,&MH) == 4) { + sr_min_w = mw; sr_min_h = mh; sr_max_w = MW; sr_max_h = MH; + } + } else if (!strcmp(c,"xywh")) { + Widget_Node::read_property(f, c); + Fluid.pasteoffset = 0; // make it not apply to contents + } else { + Widget_Node::read_property(f, c); + } +} + +int Window_Node::read_fdesign(const char* propname, const char* value) { + int x; + o->box(FL_NO_BOX); // because fdesign always puts an Fl_Box next + if (!strcmp(propname,"Width")) { + if (sscanf(value,"%d",&x) == 1) o->size(x,o->h()); + } else if (!strcmp(propname,"Height")) { + if (sscanf(value,"%d",&x) == 1) o->size(o->w(),x); + } else if (!strcmp(propname,"NumberofWidgets")) { + return 1; // we can figure out count from file + } else if (!strcmp(propname,"border")) { + if (sscanf(value,"%d",&x) == 1) ((Fl_Window*)o)->border(x); + } else if (!strcmp(propname,"title")) { + label(value); + } else { + return Widget_Node::read_fdesign(propname,value); + } + return 1; +} + +/////////////////////////////////////////////////////////////////////// + +Widget_Class_Node Widget_Class_Node::prototype; + +Widget_Class_Node *current_widget_class = nullptr; + +/** + Create and add a new Widget Class node. + \param[in] strategy add after current or as last child + \return new node + */ +Node *Widget_Class_Node::make(Strategy strategy) { + Node *anchor = Fluid.proj.tree.current, *p = anchor; + if (p && (strategy.placement() == Strategy::AFTER_CURRENT)) p = p->parent; + while (p && (!p->is_decl_block() || (p->is_widget() && p->is_class()))) { + anchor = p; + strategy.placement(Strategy::AFTER_CURRENT); + p = p->parent; + } + Widget_Class_Node *myo = new Widget_Class_Node(); + myo->name("UserInterface"); + + if (!this->o) {// template widget + this->o = new Fl_Window(100,100); + Fl_Group::current(nullptr); + } + myo->factory = this; + myo->drag = 0; + myo->numselected = 0; + Overlay_Window *w = new Overlay_Window(100, 100); + w->size_range(10, 10); + w->window = myo; + myo->o = w; + myo->add(anchor, strategy); + myo->modal = 0; + myo->non_modal = 0; + myo->wc_relative = 0; + + return myo; +} + +void Widget_Class_Node::write_properties(fld::io::Project_Writer &f) { + Window_Node::write_properties(f); + if (wc_relative==1) + f.write_string("position_relative"); + else if (wc_relative==2) + f.write_string("position_relative_rescale"); +} + +void Widget_Class_Node::read_property(fld::io::Project_Reader &f, const char *c) { + if (!strcmp(c,"position_relative")) { + wc_relative = 1; + } else if (!strcmp(c,"position_relative_rescale")) { + wc_relative = 2; + } else { + Window_Node::read_property(f, c); + } +} + +// Convert A::B::C::D to D (i.e. keep only innermost name) +// This is useful for classes that contain a namespace component +static const char *trimclassname(const char *n) { + if (!n) + return nullptr; + const char *nn; + while((nn = strstr(n, "::"))) { + n = nn + 2; + } + return(n); +} + + +void Widget_Class_Node::write_code1(fld::io::Code_Writer& f) { +#if 0 + Widget_Node::write_code1(fld::io::Code_Writer& f); +#endif // 0 + + current_widget_class = this; + write_public_state = 1; + + const char *c = subclass(); + if (!c) c = "Fl_Group"; + + f.write_c("\n"); + write_comment_h(f); + f.write_h("\nclass %s : public %s {\n", name(), c); + if (strstr(c, "Window")) { + f.write_h("%svoid _%s();\n", f.indent(1), trimclassname(name())); + f.write_h("public:\n"); + f.write_h("%s%s(int X, int Y, int W, int H, const char *L = 0);\n", f.indent(1), trimclassname(name())); + f.write_h("%s%s(int W, int H, const char *L = 0);\n", f.indent(1), trimclassname(name())); + f.write_h("%s%s();\n", f.indent(1), trimclassname(name())); + + // a constructor with all four dimensions plus label + f.write_c("%s::%s(int X, int Y, int W, int H, const char *L) :\n", name(), trimclassname(name())); + f.write_c("%s%s(X, Y, W, H, L)\n{\n", f.indent(1), c); + f.write_c("%s_%s();\n", f.indent(1), trimclassname(name())); + f.write_c("}\n\n"); + + // a constructor with just the size and label. The window manager will position the window + f.write_c("%s::%s(int W, int H, const char *L) :\n", name(), trimclassname(name())); + f.write_c("%s%s(0, 0, W, H, L)\n{\n", f.indent(1), c); + f.write_c("%sclear_flag(16);\n", f.indent(1)); + f.write_c("%s_%s();\n", f.indent(1), trimclassname(name())); + f.write_c("}\n\n"); + + // a constructor that takes size and label from the Fluid database + f.write_c("%s::%s() :\n", name(), trimclassname(name())); + f.write_c("%s%s(0, 0, %d, %d, ", f.indent(1), c, o->w(), o->h()); + const char *cstr = label(); + if (cstr) f.write_cstring(cstr); + else f.write_c("0"); + f.write_c(")\n{\n"); + f.write_c("%sclear_flag(16);\n", f.indent(1)); + f.write_c("%s_%s();\n", f.indent(1), trimclassname(name())); + f.write_c("}\n\n"); + + f.write_c("void %s::_%s() {\n", name(), trimclassname(name())); +// f.write_c("%s%s *w = this;\n", f.indent(1), name()); + } else { + f.write_h("public:\n"); + f.write_h("%s%s(int X, int Y, int W, int H, const char *L = 0);\n", + f.indent(1), trimclassname(name())); + f.write_c("%s::%s(int X, int Y, int W, int H, const char *L) :\n", name(), trimclassname(name())); + if (wc_relative==1) + f.write_c("%s%s(0, 0, W, H, L)\n{\n", f.indent(1), c); + else if (wc_relative==2) + f.write_c("%s%s(0, 0, %d, %d, L)\n{\n", f.indent(1), c, o->w(), o->h()); + else + f.write_c("%s%s(X, Y, W, H, L)\n{\n", f.indent(1), c); + } + +// f.write_c("%s%s *o = this;\n", f.indent(1), name()); + + f.indentation++; + write_widget_code(f); +} + +/** + Write the C++ code that comes after the children of the window are written. + \param f the source code output stream + */ +void Widget_Class_Node::write_code2(fld::io::Code_Writer& f) { + // make the window modal or non-modal + if (modal) { + f.write_c("%sset_modal();\n", f.indent()); + } else if (non_modal) { + f.write_c("%sset_non_modal();\n", f.indent()); + } + // clear the window border + if (!((Fl_Window*)o)->border()) f.write_c("%sclear_border();\n", f.indent()); + // set the xclass of the window + if (xclass) { + f.write_c("%sxclass(", f.indent()); + f.write_cstring(xclass); + f.write_c(");\n"); + } + // make the window resizable + if (((Fl_Window*)o)->resizable() == o) + f.write_c("%sresizable(this);\n", f.indent()); + // insert extra code from user + write_extra_code(f); + // stop adding widgets to this window + f.write_c("%send();\n", f.indent()); + // reposition or resize the Widget Class to fit into the target + if (wc_relative==1) + f.write_c("%sposition(X, Y);\n", f.indent()); + else if (wc_relative==2) + f.write_c("%sresize(X, Y, W, H);\n", f.indent()); + f.indentation--; + f.write_c("}\n"); +} + + +//////////////////////////////////////////////////////////////// +// live mode support + +Fl_Widget *Window_Node::enter_live_mode(int) { + Fl_Window *win = new Fl_Window(10, 10, o->w(), o->h()); + return propagate_live_mode(win); +} + +void Window_Node::leave_live_mode() { +} + +/** + copy all properties from the edit widget to the live widget + */ +void Window_Node::copy_properties() { + Fl_Window *self = static_cast(o); + Fl_Window *live = static_cast(live_widget); + if (self->resizable() == self) + live->resizable(live); + Widget_Node::copy_properties(); +} diff --git a/fluid/nodes/Window_Node.h b/fluid/nodes/Window_Node.h new file mode 100644 index 000000000..a6846168b --- /dev/null +++ b/fluid/nodes/Window_Node.h @@ -0,0 +1,163 @@ +// +// Window type header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +// +// Type for creating all subclasses of Fl_Widget +// This should have the widget pointer in it, but it is still in the +// Node base class. + +#ifndef FLUID_NODES_WINDOW_NODE_H +#define FLUID_NODES_WINDOW_NODE_H + +#include "nodes/Group_Node.h" + +class Widget_Class_Node; + +extern Fl_Menu_Item window_type_menu[]; +extern Widget_Class_Node *current_widget_class; + +void toggle_overlays(Fl_Widget *,void *); +void toggle_guides(Fl_Widget *,void *); +void toggle_restricted(Fl_Widget *,void *); +void show_project_cb(Fl_Widget *, void *); +void show_grid_cb(Fl_Widget *, void *); +void show_settings_cb(Fl_Widget *, void *); + +enum { + FD_LEFT = 1, // user drags the left side of the selection box + FD_RIGHT = 2, + FD_BOTTOM = 4, + FD_TOP = 8, + FD_DRAG = 16, // user drags the entire selection + FD_BOX = 32 // user creates a new selection box +}; + +class Window_Node : public Group_Node +{ +public: + typedef Group_Node super; + static Window_Node prototype; +protected: + + Fl_Menu_Item* subtypes() override {return window_type_menu;} + + friend class Overlay_Window; + int mx,my; // mouse position during dragging + int x1,y1; // initial position of selection box + int bx,by,br,bt; // bounding box of selection before snapping + int sx,sy,sr,st; // bounding box of selection after snapping to guides + int dx,dy; + int drag; // which parts of bbox are being moved + int numselected; // number of children selected + void draw_out_of_bounds(Widget_Node *group, int x, int y, int w, int h); + void draw_out_of_bounds(); + void draw_overlaps(); + void draw_overlay(); + void newdx(); + void newposition(Widget_Node *,int &x,int &y,int &w,int &h); + int handle(int); + void setlabel(const char *) override; + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + Widget_Node *_make() override {return nullptr;} // we don't call this + Fl_Widget *widget(int,int,int,int) override {return nullptr;} + int recalc; // set by fix_overlay() + void moveallchildren(int key=0); + Type type() const override { return Type::Window; } + bool is_a(Type inType) const override { return (inType==Type::Window) ? true : super::is_a(inType); } + void open_(); + +public: + + Window_Node() : + mx(0), my(0), + x1(0), y1(0), + bx(0), by(0), br(0), bt(0), + sx(0), sy(0), sr(0), st(0), + dx(0), dy(0), + drag(0), + numselected(0), + recalc(0), + modal(0), non_modal(0), + xclass(nullptr), + sr_min_w(0), sr_min_h(0), sr_max_w(0), sr_max_h(0) + { } + uchar modal, non_modal; + const char *xclass; // junk string, used for shortcut + + Node *make(Strategy strategy) override; + const char *type_name() override {return "Fl_Window";} + const char *alt_type_name() override {return "fltk::Window";} + + void open() override; + void ideal_size(int &w, int &h) override; + + void fix_overlay(); // Update the bounding box, etc + uchar *read_image(int &ww, int &hh); // Read an image of the window + + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + int read_fdesign(const char*, const char*) override; + + void add_child(Node*, Node*) override; + void move_child(Node*, Node*) override; + void remove_child(Node*) override; + + int can_have_children() const override {return 1;} + + Fl_Widget *enter_live_mode(int top=0) override; + void leave_live_mode() override; + void copy_properties() override; + + int sr_min_w, sr_min_h, sr_max_w, sr_max_h; + + static int popupx, popupy; +}; + +class Widget_Class_Node : private Window_Node +{ +public: + typedef Window_Node super; + static Widget_Class_Node prototype; + +protected: + Fl_Menu_Item* subtypes() override {return nullptr;} + +public: + Widget_Class_Node() { + write_public_state = 0; + wc_relative = 0; + } + // state variables for output: + char write_public_state; // true when public: has been printed + char wc_relative; // if 1, reposition all children, if 2, reposition and resize + + void write_properties(fld::io::Project_Writer &f) override; + void read_property(fld::io::Project_Reader &f, const char *) override; + + void write_code1(fld::io::Code_Writer& f) override; + void write_code2(fld::io::Code_Writer& f) override; + Node *make(Strategy strategy) override; + const char *type_name() override {return "widget_class";} + Type type() const override { return Type::Widget_Class; } + bool is_a(Type inType) const override { return (inType==Type::Widget_Class) ? true : super::is_a(inType); } + int can_have_children() const override {return 1;} + int is_code_block() const override {return 1;} + int is_decl_block() const override {return 1;} + int is_class() const override {return 1;} +}; + +#endif // FLUID_NODES_WINDOW_NODE_H diff --git a/fluid/nodes/callbacks.cxx b/fluid/nodes/callbacks.cxx new file mode 100644 index 000000000..f53ca2d6c --- /dev/null +++ b/fluid/nodes/callbacks.cxx @@ -0,0 +1,18 @@ +// +// Fluid Node callbacks code for the Fast Light Tool Kit (FLTK). +// +// Copyright 2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#include "nodes/callbacks.h" + diff --git a/fluid/nodes/callbacks.h b/fluid/nodes/callbacks.h new file mode 100644 index 000000000..77a38754a --- /dev/null +++ b/fluid/nodes/callbacks.h @@ -0,0 +1,23 @@ +// +// Fluid Node callbacks header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 2025 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#ifndef FLUID_NODES_CALLBACKS_H +#define FLUID_NODES_CALLBACKS_H + +#include "nodes/Node.h" + + +#endif // FLUID_NODES_CALLBACKS_H diff --git a/fluid/nodes/factory.cxx b/fluid/nodes/factory.cxx index ad5d388e1..8388e0202 100644 --- a/fluid/nodes/factory.cxx +++ b/fluid/nodes/factory.cxx @@ -1,13 +1,5 @@ // -// Widget factory code for the Fast Light Tool Kit (FLTK). -// -// Type classes for most of the fltk widgets. Most of the work -// is done by code in Fl_Widget_Type.cxx. Also a factory instance -// of each of these type classes. -// -// This file also contains the "new" menu, which has a pointer -// to a factory instance for every class (both the ones defined -// here and ones in other files) +// Node Factory code for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2025 by Bill Spitzak and others. // @@ -22,14 +14,41 @@ // https://www.fltk.org/bugs.php // +/** + + + \todo Verify the text + + Type classes for most of the fltk widgets. Most of the work + is done by code in Widget_Node.cxx. Also a factory instance + of each of these type classes. + + This file also contains the "new" menu, which has a pointer + to a factory instance for every class (both the ones defined + here and ones in other files) + + + Type classes for most of the fltk widgets. Most of the work + is done by code in Widget_Node.C. Also a factory instance + of each of these type classes. + + This file also contains the "new" menu, which has a pointer + to a factory instance for every class (both the ones defined + here and ones in other files) + + */ #include "nodes/factory.h" -#include "app/Fd_Snap_Action.h" -#include "app/fluid.h" -#include "app/undo.h" -#include "nodes/Fl_Group_Type.h" -#include "nodes/Fl_Grid_Type.h" -#include "nodes/Fl_Menu_Type.h" +#include "app/Snap_Action.h" +#include "Fluid.h" +#include "proj/undo.h" +#include "nodes/Button_Node.h" +#include "nodes/Function_Node.h" +#include "nodes/Grid_Node.h" +#include "nodes/Group_Node.h" +#include "nodes/Menu_Node.h" +#include "nodes/Widget_Node.h" +#include "nodes/Window_Node.h" #include "rsrcs/pixmaps.h" #include @@ -70,23 +89,26 @@ // ---- Browser_Base ---- static Fl_Menu_Item browser_base_type_menu[] = { - {"No Select", 0, 0, (void*)FL_NORMAL_BROWSER}, - {"Select", 0, 0, (void*)FL_SELECT_BROWSER}, - {"Hold", 0, 0, (void*)FL_HOLD_BROWSER}, - {"Multi", 0, 0, (void*)FL_MULTI_BROWSER}, - {0} + {"No Select", 0, nullptr, (void*)nullptr}, + {"Select", 0, nullptr, (void*)FL_SELECT_BROWSER}, + {"Hold", 0, nullptr, (void*)FL_HOLD_BROWSER}, + {"Multi", 0, nullptr, (void*)FL_MULTI_BROWSER}, + {nullptr} }; /** \brief This is the base class for some browsers types. This class will not be instantiated. */ -class Fl_Browser_Base_Type : public Fl_Widget_Type +class Browser_Base_Node : public Widget_Node { - typedef Fl_Widget_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return browser_base_type_menu; } - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Browser_ *myo = (Fl_Browser_*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Widget_Node super; + static Browser_Base_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return browser_base_type_menu; } + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Browser_ *myo = (Fl_Browser_*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -97,42 +119,44 @@ class Fl_Browser_Base_Type : public Fl_Widget_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { w = 120; h = 160; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Browser_"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Browser_"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Browser_"; } + const char *alt_type_name() override { return "fltk::Browser_"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Browser* b = new Fl_Browser(x, y, w, h); return b; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Browser_Base_Type(); } - ID id() const FL_OVERRIDE { return ID_Browser_; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Browser_) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Browser_Base_Node(); } + Type type() const override { return Type::Browser_; } + bool is_a(Type inType) const override { return (inType==Type::Browser_) ? true : super::is_a(inType); } }; -static Fl_Browser_Base_Type Fl_Browser_Base_type; +Browser_Base_Node Browser_Base_Node::prototype; // ---- Browser ---- /** \brief Handle a plain browser widget. - Most of the work is already done in Fl_Browser_Base_Type. + Most of the work is already done in Browser_Base_Node. */ -class Fl_Browser_Type : public Fl_Browser_Base_Type +class Browser_Node : public Browser_Base_Node { - typedef Fl_Browser_Base_Type super; public: - const char *type_name() FL_OVERRIDE { return "Fl_Browser"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Browser"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Browser_Base_Node super; + static Browser_Node prototype; +public: + const char *type_name() override { return "Fl_Browser"; } + const char *alt_type_name() override { return "fltk::Browser"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Browser* b = new Fl_Browser(x, y, w, h); // Fl_Browser::add calls fl_height(), which requires the X display open. // Avoid this when compiling so it works w/o a display: - if (!batch_mode) { + if (!Fluid.batch_mode) { char buffer[20]; for (int i = 1; i <= 20; i++) { sprintf(buffer,"Browser Line %d",i); @@ -141,12 +165,12 @@ public: } return b; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Browser_Type(); } - ID id() const FL_OVERRIDE { return ID_Browser; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Browser) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Browser_Node(); } + Type type() const override { return Type::Browser; } + bool is_a(Type inType) const override { return (inType==Type::Browser) ? true : super::is_a(inType); } }; -static Fl_Browser_Type Fl_Browser_type; +Browser_Node Browser_Node::prototype; // ---- Check Browser ---- @@ -155,17 +179,19 @@ static Fl_Browser_Type Fl_Browser_type; \brief Manage the Check Browser. The Fl_Check_Browser is derived form Fl_Browser_ (underline!), not Fl_Browser. */ -class Fl_Check_Browser_Type : public Fl_Browser_Base_Type +class Check_Browser_Node : public Browser_Base_Node { - typedef Fl_Browser_Base_Type super; public: - const char *type_name() FL_OVERRIDE { return "Fl_Check_Browser"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::CheckBrowser"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Browser_Base_Node super; + static Check_Browser_Node prototype; +public: + const char *type_name() override { return "Fl_Check_Browser"; } + const char *alt_type_name() override { return "fltk::CheckBrowser"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Check_Browser* b = new Fl_Check_Browser(x, y, w, h); // Fl_Check_Browser::add calls fl_height(), which requires the X display open. // Avoid this when compiling so it works w/o a display: - if (!batch_mode) { + if (!Fluid.batch_mode) { char buffer[20]; for (int i = 1; i <= 20; i++) { sprintf(buffer,"Browser Line %d",i); @@ -174,12 +200,12 @@ public: } return b; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Check_Browser_Type(); } - ID id() const FL_OVERRIDE { return ID_Check_Browser; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Check_Browser) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Check_Browser_Node(); } + Type type() const override { return Type::Check_Browser; } + bool is_a(Type inType) const override { return (inType==Type::Check_Browser) ? true : super::is_a(inType); } }; -static Fl_Check_Browser_Type Fl_Check_Browser_type; +Check_Browser_Node Check_Browser_Node::prototype; // ---- File Browser ---- @@ -189,23 +215,25 @@ static Fl_Check_Browser_Type Fl_Check_Browser_type; As oppoesed to the Hold, Multi, and Select Browser, this is not a subclass, but its own implementation, based on Fl_Browser. */ -class Fl_File_Browser_Type : public Fl_Browser_Type +class File_Browser_Node : public Browser_Node { - typedef Fl_Browser_Type super; public: - const char *type_name() FL_OVERRIDE { return "Fl_File_Browser"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::FileBrowser"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Browser_Node super; + static File_Browser_Node prototype; +public: + const char *type_name() override { return "Fl_File_Browser"; } + const char *alt_type_name() override { return "fltk::FileBrowser"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_File_Browser* b = new Fl_File_Browser(x, y, w, h); - if (!batch_mode) b->load("."); + if (!Fluid.batch_mode) b->load("."); return b; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_File_Browser_Type(); } - ID id() const FL_OVERRIDE { return ID_File_Browser; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_File_Browser) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new File_Browser_Node(); } + Type type() const override { return Type::File_Browser; } + bool is_a(Type inType) const override { return (inType==Type::File_Browser) ? true : super::is_a(inType); } }; -static Fl_File_Browser_Type Fl_File_Browser_type; +File_Browser_Node File_Browser_Node::prototype; // ---- Tree Type ------------------------------------------------------ MARK: - @@ -213,25 +241,27 @@ static Fl_File_Browser_Type Fl_File_Browser_type; /** \brief Handle the Tree widget. Fl_Tree is derived from Fl_Group, but FLUID does not support extended Fl_Tree - functionality, so we derive the Type from Fl_Widget_Type. + functionality, so we derive the Type from Widget_Node. \note Updating item_labelfont etc. does not refresh any of the existing items in the tree, so I decided against implementig those via the labelfont UI. */ -class Fl_Tree_Type : public Fl_Widget_Type +class Tree_Node : public Widget_Node { - typedef Fl_Widget_Type super; public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Widget_Node super; + static Tree_Node prototype; +public: + void ideal_size(int &w, int &h) override { w = 120; h = 160; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Tree"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::TreeBrowser"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Tree"; } + const char *alt_type_name() override { return "fltk::TreeBrowser"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Tree* b = new Fl_Tree(x, y, w, h); - if (!batch_mode) { + if (!Fluid.batch_mode) { b->add("/A1/B1/C1"); b->add("/A1/B1/C2"); b->add("/A1/B2/C1"); @@ -243,12 +273,12 @@ public: } return b; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Tree_Type(); } - ID id() const FL_OVERRIDE { return ID_Tree; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Tree) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Tree_Node(); } + Type type() const override { return Type::Tree; } + bool is_a(Type inType) const override { return (inType==Type::Tree) ? true : super::is_a(inType); } }; -static Fl_Tree_Type Fl_Tree_type; +Tree_Node Tree_Node::prototype; @@ -257,13 +287,16 @@ static Fl_Tree_Type Fl_Tree_type; /** \brief Handle the Help View widget. Fl_Help_View is derived from Fl_Group, but supporting children is not useful, - so we derive from Fl_Widget_Type. + so we derive from Widget_Node. */ -class Fl_Help_View_Type : public Fl_Widget_Type +class Help_View_Node : public Widget_Node { - typedef Fl_Widget_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Help_View *myo = (Fl_Help_View*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Widget_Node super; + static Help_View_Node prototype; +private: + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Help_View *myo = (Fl_Help_View*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -274,27 +307,27 @@ class Fl_Help_View_Type : public Fl_Widget_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { w = 160; h = 120; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Help_View"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::HelpView"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Help_View"; } + const char *alt_type_name() override { return "fltk::HelpView"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Help_View *myo = new Fl_Help_View(x, y, w, h); - if (!batch_mode) { + if (!Fluid.batch_mode) { myo->value("

Fl_Help_View Widget

" "

This is a Fl_Help_View widget.

"); } return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Help_View_Type(); } - ID id() const FL_OVERRIDE { return ID_Help_View; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Help_View) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Help_View_Node(); } + Type type() const override { return Type::Help_View; } + bool is_a(Type inType) const override { return (inType==Type::Help_View) ? true : super::is_a(inType); } }; -static Fl_Help_View_Type Fl_Help_View_type; +Help_View_Node Help_View_Node::prototype; @@ -306,41 +339,46 @@ static Fl_Help_View_Type Fl_Help_View_type; /** \brief Just a base class for all valuators. */ -class Fl_Valuator_Type : public Fl_Widget_Type +class Valuator_Node : public Widget_Node { - typedef Fl_Widget_Type super; public: - const char *type_name() FL_OVERRIDE { return "Fl_Valuator"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Valuator"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Widget_Node super; + static Valuator_Node prototype; +public: + const char *type_name() override { return "Fl_Valuator"; } + const char *alt_type_name() override { return "fltk::Valuator"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Slider(x, y, w, h, "Valuator"); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Valuator_Type(); } - ID id() const FL_OVERRIDE { return ID_Valuator_; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Valuator_) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Valuator_Node(); } + Type type() const override { return Type::Valuator_; } + bool is_a(Type inType) const override { return (inType==Type::Valuator_) ? true : super::is_a(inType); } }; -static Fl_Valuator_Type Fl_Valuator_type; +Valuator_Node Valuator_Node::prototype; // ---- Counter ---- static Fl_Menu_Item counter_type_menu[] = { - { "Normal", 0, 0, (void*)FL_NORMAL_COUNTER }, - { "Simple", 0, 0, (void*)FL_SIMPLE_COUNTER }, - { 0 } + { "Normal", 0, nullptr, (void*)nullptr }, + { "Simple", 0, nullptr, (void*)FL_SIMPLE_COUNTER }, + { nullptr } }; /** \brief Manage the Counter widget. Strictly speaking, the ideal size should derive from the textsize not the labelsize. */ -class Fl_Counter_Type : public Fl_Valuator_Type +class Counter_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return counter_type_menu; } - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Counter *myo = (Fl_Counter*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Valuator_Node super; + static Counter_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return counter_type_menu; } + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Counter *myo = (Fl_Counter*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -351,22 +389,23 @@ class Fl_Counter_Type : public Fl_Valuator_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() + 8; w = layout->textsize_not_null() * 4 + 4 * h; // make room for the arrows - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Counter"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Counter"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Counter"; } + const char *alt_type_name() override { return "fltk::Counter"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Counter(x, y, w, h, "counter:"); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Counter_Type(); } - ID id() const FL_OVERRIDE { return ID_Counter; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Counter) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Counter_Node(); } + Type type() const override { return Type::Counter; } + bool is_a(Type inType) const override { return (inType==Type::Counter) ? true : super::is_a(inType); } }; -static Fl_Counter_Type Fl_Counter_type; +Counter_Node Counter_Node::prototype; // ---- Adjuster ---- @@ -374,105 +413,116 @@ static Fl_Counter_Type Fl_Counter_type; /** \brief Handle Adjuster widgets which are derived from valuators. */ -class Fl_Adjuster_Type : public Fl_Valuator_Type +class Adjuster_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Valuator_Node super; + static Adjuster_Node prototype; +public: + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->labelsize + 8; w = 3 * h; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Adjuster"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Adjuster"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Adjuster"; } + const char *alt_type_name() override { return "fltk::Adjuster"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Adjuster(x, y, w, h); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Adjuster_Type(); } - ID id() const FL_OVERRIDE { return ID_Adjuster; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Adjuster) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Adjuster_Node(); } + Type type() const override { return Type::Adjuster; } + bool is_a(Type inType) const override { return (inType==Type::Adjuster) ? true : super::is_a(inType); } }; -static Fl_Adjuster_Type Fl_Adjuster_type; +Adjuster_Node Adjuster_Node::prototype; // ---- Dial ---- static Fl_Menu_Item dial_type_menu[] = { - { "Dot", 0, 0, (void*)0 }, - { "Line", 0, 0, (void*)FL_LINE_DIAL }, - { "Fill", 0, 0, (void*)FL_FILL_DIAL }, - { 0 } + { "Dot", 0, nullptr, (void*)nullptr }, + { "Line", 0, nullptr, (void*)FL_LINE_DIAL }, + { "Fill", 0, nullptr, (void*)FL_FILL_DIAL }, + { nullptr } }; /** \brief Manage dials. */ -class Fl_Dial_Type : public Fl_Valuator_Type +class Dial_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return dial_type_menu; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Valuator_Node super; + static Dial_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return dial_type_menu; } +public: + void ideal_size(int &w, int &h) override { w = 60; h = 60; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Dial"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Dial"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Dial"; } + const char *alt_type_name() override { return "fltk::Dial"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Dial(x, y, w, h); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Dial_Type(); } - ID id() const FL_OVERRIDE { return ID_Dial; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Dial) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Dial_Node(); } + Type type() const override { return Type::Dial; } + bool is_a(Type inType) const override { return (inType==Type::Dial) ? true : super::is_a(inType); } }; -static Fl_Dial_Type Fl_Dial_type; + +Dial_Node Dial_Node::prototype; // ---- Roller ---- static Fl_Menu_Item roller_type_menu[] = { - { "Vertical", 0, 0, (void*)0 }, - { "Horizontal", 0, 0, (void*)FL_HORIZONTAL }, - { 0 } + { "Vertical", 0, nullptr, (void*)nullptr }, + { "Horizontal", 0, nullptr, (void*)FL_HORIZONTAL }, + { nullptr } }; /** \brief Manage Roller widgets. They are vertical by default. */ -class Fl_Roller_Type : public Fl_Valuator_Type +class Roller_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return roller_type_menu; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Valuator_Node super; + static Roller_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return roller_type_menu; } +public: + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; w = layout->labelsize + 8; h = 4 * w; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Roller"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Roller"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Roller"; } + const char *alt_type_name() override { return "fltk::Roller"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Roller(x, y, w, h); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Roller_Type(); } - ID id() const FL_OVERRIDE { return ID_Roller; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Roller) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Roller_Node(); } + Type type() const override { return Type::Roller; } + bool is_a(Type inType) const override { return (inType==Type::Roller) ? true : super::is_a(inType); } }; -static Fl_Roller_Type Fl_Roller_type; +Roller_Node Roller_Node::prototype; // ---- Slider ---- static Fl_Menu_Item slider_type_menu[] = { - { "Vertical", 0, 0, (void*)FL_VERT_SLIDER }, - { "Horizontal", 0, 0, (void*)FL_HOR_SLIDER }, - { "Vert Fill", 0, 0, (void*)FL_VERT_FILL_SLIDER }, - { "Horz Fill", 0, 0, (void*)FL_HOR_FILL_SLIDER }, - { "Vert Knob", 0, 0, (void*)FL_VERT_NICE_SLIDER }, - { "Horz Knob", 0, 0, (void*)FL_HOR_NICE_SLIDER }, - { 0 } + { "Vertical", 0, nullptr, (void*)nullptr }, + { "Horizontal", 0, nullptr, (void*)FL_HOR_SLIDER }, + { "Vert Fill", 0, nullptr, (void*)FL_VERT_FILL_SLIDER }, + { "Horz Fill", 0, nullptr, (void*)FL_HOR_FILL_SLIDER }, + { "Vert Knob", 0, nullptr, (void*)FL_VERT_NICE_SLIDER }, + { "Horz Knob", 0, nullptr, (void*)FL_HOR_NICE_SLIDER }, + { nullptr } }; /** @@ -480,55 +530,63 @@ static Fl_Menu_Item slider_type_menu[] = { They are vertical by default. Fl_Value_Slider has its own type. */ -class Fl_Slider_Type : public Fl_Valuator_Type +class Slider_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return slider_type_menu; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Valuator_Node super; + static Slider_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return slider_type_menu; } +public: + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; w = layout->labelsize + 8; h = 4 * w; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Slider"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Slider"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Slider"; } + const char *alt_type_name() override { return "fltk::Slider"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Slider(x, y, w, h, "slider:"); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Slider_Type(); } - ID id() const FL_OVERRIDE { return ID_Slider; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Slider) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Slider_Node(); } + Type type() const override { return Type::Slider; } + bool is_a(Type inType) const override { return (inType==Type::Slider) ? true : super::is_a(inType); } }; -static Fl_Slider_Type Fl_Slider_type; +Slider_Node Slider_Node::prototype; // ---- Scrollbar ---- static Fl_Menu_Item scrollbar_type_menu[] = { - { "Vertical", 0, 0, (void*)FL_VERT_SLIDER }, - { "Horizontal", 0, 0, (void*)FL_HOR_SLIDER }, - { 0 } + { "Vertical", 0, nullptr, (void*)nullptr }, + { "Horizontal", 0, nullptr, (void*)FL_HOR_SLIDER }, + { nullptr } }; /** \brief Manage Scrollbars which are derived from Sliders. */ -class Fl_Scrollbar_Type : public Fl_Slider_Type +class Scrollbar_Node : public Slider_Node { - typedef Fl_Slider_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return scrollbar_type_menu; } public: - const char *type_name() FL_OVERRIDE { return "Fl_Scrollbar"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Scrollbar"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Slider_Node super; + static Scrollbar_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return scrollbar_type_menu; } +public: + const char *type_name() override { return "Fl_Scrollbar"; } + const char *alt_type_name() override { return "fltk::Scrollbar"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Scrollbar(x, y, w, h); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Scrollbar_Type(); } - ID id() const FL_OVERRIDE { return ID_Scrollbar; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Scrollbar) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Scrollbar_Node(); } + Type type() const override { return Type::Scrollbar; } + bool is_a(Type inType) const override { return (inType==Type::Scrollbar) ? true : super::is_a(inType); } }; -static Fl_Scrollbar_Type Fl_Scrollbar_type; + +Scrollbar_Node Scrollbar_Node::prototype; // ---- Value Slider ---- @@ -536,11 +594,14 @@ static Fl_Scrollbar_Type Fl_Scrollbar_type; /** \brief Manage Value Sliders and their text settings. */ -class Fl_Value_Slider_Type : public Fl_Slider_Type +class Value_Slider_Node : public Slider_Node { - typedef Fl_Slider_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Value_Slider *myo = (Fl_Value_Slider*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Slider_Node super; + static Value_Slider_Node prototype; +private: + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Value_Slider *myo = (Fl_Value_Slider*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -551,17 +612,17 @@ class Fl_Value_Slider_Type : public Fl_Slider_Type return 1; } public: - const char *type_name() FL_OVERRIDE { return "Fl_Value_Slider"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::ValueSlider"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Value_Slider"; } + const char *alt_type_name() override { return "fltk::ValueSlider"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Value_Slider(x, y, w, h, "slider:"); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Value_Slider_Type(); } - ID id() const FL_OVERRIDE { return ID_Value_Slider; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Value_Slider) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Value_Slider_Node(); } + Type type() const override { return Type::Value_Slider; } + bool is_a(Type inType) const override { return (inType==Type::Value_Slider) ? true : super::is_a(inType); } }; -static Fl_Value_Slider_Type Fl_Value_Slider_type; +Value_Slider_Node Value_Slider_Node::prototype; // ---- Value Input ---- @@ -569,11 +630,14 @@ static Fl_Value_Slider_Type Fl_Value_Slider_type; /** \brief Manage Value Inputs and their text settings. */ -class Fl_Value_Input_Type : public Fl_Valuator_Type +class Value_Input_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Value_Input *myo = (Fl_Value_Input*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Valuator_Node super; + static Value_Input_Node prototype; +private: + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Value_Input *myo = (Fl_Value_Input*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -584,23 +648,24 @@ class Fl_Value_Input_Type : public Fl_Valuator_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() + 8; w = layout->textsize_not_null() * 4 + 8; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Value_Input"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::ValueInput"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Value_Input"; } + const char *alt_type_name() override { return "fltk::ValueInput"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Value_Input *myo = new Fl_Value_Input(x, y, w, h, "value:"); return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Value_Input_Type(); } - ID id() const FL_OVERRIDE { return ID_Value_Input; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Value_Input) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Value_Input_Node(); } + Type type() const override { return Type::Value_Input; } + bool is_a(Type inType) const override { return (inType==Type::Value_Input) ? true : super::is_a(inType); } }; -static Fl_Value_Input_Type Fl_Value_Input_type; +Value_Input_Node Value_Input_Node::prototype; // ---- Value Output ---- @@ -608,11 +673,14 @@ static Fl_Value_Input_Type Fl_Value_Input_type; /** \brief Handle Value Output widgets, no shortcut with Value Input unfortunately. */ -class Fl_Value_Output_Type : public Fl_Valuator_Type +class Value_Output_Node : public Valuator_Node { - typedef Fl_Valuator_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Value_Output *myo = (Fl_Value_Output*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Valuator_Node super; + static Value_Output_Node prototype; +private: + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Value_Output *myo = (Fl_Value_Output*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -623,23 +691,24 @@ class Fl_Value_Output_Type : public Fl_Valuator_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() + 8; w = layout->textsize_not_null() * 4 + 8; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Value_Output"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::ValueOutput"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Value_Output"; } + const char *alt_type_name() override { return "fltk::ValueOutput"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Value_Output *myo = new Fl_Value_Output(x, y, w, h, "value:"); return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Value_Output_Type(); } - ID id() const FL_OVERRIDE { return ID_Value_Output; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Value_Output) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Value_Output_Node(); } + Type type() const override { return Type::Value_Output; } + bool is_a(Type inType) const override { return (inType==Type::Value_Output) ? true : super::is_a(inType); } }; -static Fl_Value_Output_Type Fl_Value_Output_type; +Value_Output_Node Value_Output_Node::prototype; @@ -649,25 +718,28 @@ static Fl_Value_Output_Type Fl_Value_Output_type; // ---- Input ---- static Fl_Menu_Item input_type_menu[] = { - { "Normal", 0, 0, (void*)FL_NORMAL_INPUT }, - { "Multiline", 0, 0, (void*)FL_MULTILINE_INPUT }, - { "Secret", 0, 0, (void*)FL_SECRET_INPUT }, - { "Int", 0, 0, (void*)FL_INT_INPUT }, - { "Float", 0, 0, (void*)FL_FLOAT_INPUT }, - {0} + { "Normal", 0, nullptr, (void*)nullptr }, + { "Multiline", 0, nullptr, (void*)FL_MULTILINE_INPUT }, + { "Secret", 0, nullptr, (void*)FL_SECRET_INPUT }, + { "Int", 0, nullptr, (void*)FL_INT_INPUT }, + { "Float", 0, nullptr, (void*)FL_FLOAT_INPUT }, + {nullptr} }; /** \brief Manage simple text input widgets. The managed class is derived from Fl_Input_, but for simplicity, deriving from - Fl_Widget_Type seems sufficient here. + Widget_Node seems sufficient here. */ -class Fl_Input_Type : public Fl_Widget_Type +class Input_Node : public Widget_Node { - typedef Fl_Widget_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return input_type_menu; } - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Input_ *myo = (Fl_Input_*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Widget_Node super; + static Input_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return input_type_menu; } + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Input_ *myo = (Fl_Input_*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -678,23 +750,24 @@ class Fl_Input_Type : public Fl_Widget_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() + 8; w = layout->textsize_not_null() * 6 + 8; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Input"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Input"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Input"; } + const char *alt_type_name() override { return "fltk::Input"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Input *myo = new Fl_Input(x, y, w, h, "input:"); myo->value("Text Input"); return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Input_Type(); } - ID id() const FL_OVERRIDE { return ID_Input; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Input) ? true : super::is_a(inID); } - void copy_properties() FL_OVERRIDE { - Fl_Widget_Type::copy_properties(); + Widget_Node *_make() override { return new Input_Node(); } + Type type() const override { return Type::Input; } + bool is_a(Type inType) const override { return (inType==Type::Input) ? true : super::is_a(inType); } + void copy_properties() override { + Widget_Node::copy_properties(); Fl_Input_ *d = (Fl_Input_*)live_widget, *s = (Fl_Input_*)o; d->textfont(s->textfont()); d->textsize(s->textsize()); @@ -702,7 +775,8 @@ public: d->shortcut(s->shortcut()); } }; -static Fl_Input_Type Fl_Input_type; + +Input_Node Input_Node::prototype; // ---- File Input ---- @@ -710,60 +784,67 @@ static Fl_Input_Type Fl_Input_type; /** \brief Manage file name input widgets. */ -class Fl_File_Input_Type : public Fl_Input_Type +class File_Input_Node : public Input_Node { - typedef Fl_Input_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return NULL; } // Don't inherit. public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Input_Node super; + static File_Input_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return nullptr; } // Don't inherit. +public: + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() + 8 + 10; // Directoy bar is additional 10 pixels high w = layout->textsize_not_null() * 10 + 8; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_File_Input"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::FileInput"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_File_Input"; } + const char *alt_type_name() override { return "fltk::FileInput"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_File_Input *myo = new Fl_File_Input(x, y, w, h, "file:"); myo->value("/usr/include/FL/Fl.H"); return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_File_Input_Type(); } - ID id() const FL_OVERRIDE { return ID_File_Input; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_File_Input) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new File_Input_Node(); } + Type type() const override { return Type::File_Input; } + bool is_a(Type inType) const override { return (inType==Type::File_Input) ? true : super::is_a(inType); } }; -static Fl_File_Input_Type Fl_File_Input_type; +File_Input_Node File_Input_Node::prototype; // ---- Output ---- static Fl_Menu_Item output_type_menu[] = { - { "Normal", 0, 0, (void*)FL_NORMAL_OUTPUT }, - { "Multiline", 0, 0, (void*)FL_MULTILINE_OUTPUT }, - { 0 } + { "Normal", 0, nullptr, (void*)FL_NORMAL_OUTPUT }, + { "Multiline", 0, nullptr, (void*)FL_MULTILINE_OUTPUT }, + { nullptr } }; /** \brief Manage Output widgets, derived from Input. */ -class Fl_Output_Type : public Fl_Input_Type +class Output_Node : public Input_Node { - typedef Fl_Input_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return output_type_menu; } public: - const char *type_name() FL_OVERRIDE { return "Fl_Output"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Output"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Input_Node super; + static Output_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return output_type_menu; } +public: + const char *type_name() override { return "Fl_Output"; } + const char *alt_type_name() override { return "fltk::Output"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Output *myo = new Fl_Output(x, y, w, h, "output:"); myo->value("Text Output"); return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Output_Type(); } - ID id() const FL_OVERRIDE { return ID_Output; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Output) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Output_Node(); } + Type type() const override { return Type::Output; } + bool is_a(Type inType) const override { return (inType==Type::Output) ? true : super::is_a(inType); } }; -static Fl_Output_Type Fl_Output_type; +Output_Node Output_Node::prototype; @@ -777,11 +858,14 @@ static Fl_Output_Type Fl_Output_type; Fl_Text_Display is actually derived from Fl_Group, but for FLUID, deriving the type from Widget is better. */ -class Fl_Text_Display_Type : public Fl_Widget_Type +class Text_Display_Node : public Widget_Node { - typedef Fl_Widget_Type super; - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Text_Display *myo = (Fl_Text_Display*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Widget_Node super; + static Text_Display_Node prototype; +private: + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Text_Display *myo = (Fl_Text_Display*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -792,27 +876,29 @@ class Fl_Text_Display_Type : public Fl_Widget_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() * 4 + 8; w = layout->textsize_not_null() * 10 + 8; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Text_Display"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::TextDisplay"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Text_Display"; } + const char *alt_type_name() override { return "fltk::TextDisplay"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Text_Display *myo = new Fl_Text_Display(x, y, w, h); - if (!batch_mode) { + if (!Fluid.batch_mode) { Fl_Text_Buffer *b = new Fl_Text_Buffer(); b->text("Lorem ipsum dolor\nsit amet, consetetur\nsadipscing elitr"); myo->buffer(b); } return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Text_Display_Type(); } - ID id() const FL_OVERRIDE { return ID_Text_Display; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Text_Display) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Text_Display_Node(); } + Type type() const override { return Type::Text_Display; } + bool is_a(Type inType) const override { return (inType==Type::Text_Display) ? true : super::is_a(inType); } }; -static Fl_Text_Display_Type Fl_Text_Display_type; + +Text_Display_Node Text_Display_Node::prototype; // ---- Text Editor ---- @@ -820,27 +906,29 @@ static Fl_Text_Display_Type Fl_Text_Display_type; /** \brief Manage Text Editors based on Text Display. */ -class Fl_Text_Editor_Type : public Fl_Text_Display_Type +class Text_Editor_Node : public Text_Display_Node { - typedef Fl_Text_Display_Type super; public: - const char *type_name() FL_OVERRIDE {return "Fl_Text_Editor";} - const char *alt_type_name() FL_OVERRIDE {return "fltk::TextEditor";} - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + typedef Text_Display_Node super; + static Text_Editor_Node prototype; +public: + const char *type_name() override {return "Fl_Text_Editor";} + const char *alt_type_name() override {return "fltk::TextEditor";} + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Text_Editor *myo = new Fl_Text_Editor(x, y, w, h); - if (!batch_mode) { + if (!Fluid.batch_mode) { Fl_Text_Buffer *b = new Fl_Text_Buffer(); b->text("Lorem ipsum dolor\nsit amet, consetetur\nsadipscing elitr"); myo->buffer(b); } return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Text_Editor_Type(); } - ID id() const FL_OVERRIDE { return ID_Text_Editor; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Text_Editor) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Text_Editor_Node(); } + Type type() const override { return Type::Text_Editor; } + bool is_a(Type inType) const override { return (inType==Type::Text_Editor) ? true : super::is_a(inType); } }; -static Fl_Text_Editor_Type Fl_Text_Editor_type; +Text_Editor_Node Text_Editor_Node::prototype; // ---- Terminal ---- @@ -848,13 +936,13 @@ static Fl_Text_Editor_Type Fl_Text_Editor_type; /** Use this terminal instead of Fl_Terminal to capture resize actions. */ class Fl_Terminal_Proxy : public Fl_Terminal { public: - Fl_Terminal_Proxy(int x, int y, int w, int h, const char *l=NULL) + Fl_Terminal_Proxy(int x, int y, int w, int h, const char *l=nullptr) : Fl_Terminal(x, y, w, h, l) { } void print_sample_text() { clear_screen_home(false); append("> ls -als"); } - void resize(int x, int y, int w, int h) FL_OVERRIDE { + void resize(int x, int y, int w, int h) override { Fl_Terminal::resize(x, y, w, h); // After a resize, the top text vanishes, so make sure we redraw it. print_sample_text(); @@ -867,7 +955,7 @@ public: Fl_Font tfont_; int tsize_; Fl_Color tcolor_; - Fl_Batchmode_Terminal(int x, int y, int w, int h, const char *l=NULL) + Fl_Batchmode_Terminal(int x, int y, int w, int h, const char *l=nullptr) : Fl_Group(x, y, w, h, l) { // set the defaults that Fl_Terminal would set box(FL_DOWN_BOX); @@ -889,16 +977,18 @@ public: /** \brief Manage a terminal widget. */ -class Fl_Terminal_Type : public Fl_Widget_Type +class Terminal_Node : public Widget_Node { - typedef Fl_Widget_Type super; public: - const char *type_name() FL_OVERRIDE { return "Fl_Terminal"; } + typedef Widget_Node super; + static Terminal_Node prototype; +public: + const char *type_name() override { return "Fl_Terminal"; } // Older .fl files with Fl_Simple_Terminal will create a Fl_Terminal instead. - const char *alt_type_name() FL_OVERRIDE { return "Fl_Simple_Terminal"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { - Fl_Widget *ret = NULL; - if (batch_mode) { + const char *alt_type_name() override { return "Fl_Simple_Terminal"; } + Fl_Widget *widget(int x, int y, int w, int h) override { + Fl_Widget *ret = nullptr; + if (Fluid.batch_mode) { ret = new Fl_Batchmode_Terminal(x, y, w, h); } else { Fl_Terminal_Proxy *term = new Fl_Terminal_Proxy(x, y, w+100, h); @@ -906,9 +996,9 @@ public: } return ret; } - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - if (batch_mode) { - Fl_Batchmode_Terminal *myo = (Fl_Batchmode_Terminal*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + if (Fluid.batch_mode) { + Fl_Batchmode_Terminal *myo = (Fl_Batchmode_Terminal*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = (Fl_Font)myo->tfont_; s = myo->tsize_; c = myo->tcolor_; break; @@ -917,7 +1007,7 @@ public: case 3: myo->tcolor_ = c; break; } } else { - Fl_Terminal_Proxy *myo = (Fl_Terminal_Proxy*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); + Fl_Terminal_Proxy *myo = (Fl_Terminal_Proxy*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = (Fl_Font)myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -928,12 +1018,12 @@ public: } return 1; } - Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Terminal_Type();} - ID id() const FL_OVERRIDE { return ID_Terminal; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Terminal) ? true : super::is_a(inID); } + Widget_Node *_make() override {return new Terminal_Node();} + Type type() const override { return Type::Terminal; } + bool is_a(Type inType) const override { return (inType==Type::Terminal) ? true : super::is_a(inType); } }; -static Fl_Terminal_Type Fl_Terminal_type; +Terminal_Node Terminal_Node::prototype; // ---- Other ---------------------------------------------------------- MARK: - @@ -945,25 +1035,27 @@ static Fl_Terminal_Type Fl_Terminal_type; \brief Manage box widgets. Ideal size is set to 100x100, snapped to layout. */ -class Fl_Box_Type : public Fl_Widget_Type +class Box_Node : public Widget_Node { - typedef Fl_Widget_Type super; public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Widget_Node super; + static Box_Node prototype; +public: + void ideal_size(int &w, int &h) override { w = 100; h = 100; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Box"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Widget"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Box"; } + const char *alt_type_name() override { return "fltk::Widget"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Box(x, y, w, h, "label"); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Box_Type(); } - ID id() const FL_OVERRIDE { return ID_Box; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Box) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Box_Node(); } + Type type() const override { return Type::Box; } + bool is_a(Type inType) const override { return (inType==Type::Box) ? true : super::is_a(inType); } }; -static Fl_Box_Type Fl_Box_type; +Box_Node Box_Node::prototype; // ---- Clock ---- @@ -972,25 +1064,27 @@ static Fl_Box_Type Fl_Box_type; \brief Manage Clock widgets. Ideal size is set to 80x80 snapped to layout. */ -class Fl_Clock_Type : public Fl_Widget_Type +class Clock_Node : public Widget_Node { - typedef Fl_Widget_Type super; public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Widget_Node super; + static Clock_Node prototype; +public: + void ideal_size(int &w, int &h) override { w = 80; h = 80; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Clock"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Clock"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Clock"; } + const char *alt_type_name() override { return "fltk::Clock"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Clock(x, y, w, h); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Clock_Type(); } - ID id() const FL_OVERRIDE { return ID_Clock; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Clock) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Clock_Node(); } + Type type() const override { return Type::Clock; } + bool is_a(Type inType) const override { return (inType==Type::Clock) ? true : super::is_a(inType); } }; -static Fl_Clock_Type Fl_Clock_type; +Clock_Node Clock_Node::prototype; // ---- Progress ---- @@ -1000,35 +1094,38 @@ static Fl_Clock_Type Fl_Clock_type; Ideal size is set to match the label font and label text width times 3. \note minimum, maximum, and value must be set via extra code fields. */ -class Fl_Progress_Type : public Fl_Widget_Type +class Progress_Node : public Widget_Node { - typedef Fl_Widget_Type super; public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + typedef Widget_Node super; + static Progress_Node prototype; +public: + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->labelsize + 8; w = layout->labelsize * 12; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Progress"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::ProgressBar"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Progress"; } + const char *alt_type_name() override { return "fltk::ProgressBar"; } + Fl_Widget *widget(int x, int y, int w, int h) override { Fl_Progress *myo = new Fl_Progress(x, y, w, h, "label"); myo->value(50); return myo; } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Progress_Type(); } - ID id() const FL_OVERRIDE { return ID_Progress; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Progress) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Progress_Node(); } + Type type() const override { return Type::Progress; } + bool is_a(Type inType) const override { return (inType==Type::Progress) ? true : super::is_a(inType); } }; -static Fl_Progress_Type Fl_Progress_type; +Progress_Node Progress_Node::prototype; // ---- Spinner ---- static Fl_Menu_Item spinner_type_menu[] = { - { "Integer", 0, 0, (void*)FL_INT_INPUT }, - { "Float", 0, 0, (void*)FL_FLOAT_INPUT }, - { 0 } + { "Integer", 0, nullptr, (void*)FL_INT_INPUT }, + { "Float", 0, nullptr, (void*)FL_FLOAT_INPUT }, + { nullptr } }; /** @@ -1036,12 +1133,15 @@ static Fl_Menu_Item spinner_type_menu[] = { \note Fl_Spinner is derived from Fl_Group, *not* Fl_Valuator as one may expect. For FLUID, this means some special handling and no Group support. */ -class Fl_Spinner_Type : public Fl_Widget_Type +class Spinner_Node : public Widget_Node { - typedef Fl_Widget_Type super; - Fl_Menu_Item *subtypes() FL_OVERRIDE { return spinner_type_menu; } - int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) FL_OVERRIDE { - Fl_Spinner *myo = (Fl_Spinner*)(w==4 ? ((Fl_Widget_Type*)factory)->o : o); +public: + typedef Widget_Node super; + static Spinner_Node prototype; +private: + Fl_Menu_Item *subtypes() override { return spinner_type_menu; } + int textstuff(int w, Fl_Font& f, int& s, Fl_Color& c) override { + Fl_Spinner *myo = (Fl_Spinner*)(w==4 ? ((Widget_Node*)factory)->o : o); switch (w) { case 4: case 0: f = (Fl_Font)myo->textfont(); s = myo->textsize(); c = myo->textcolor(); break; @@ -1052,64 +1152,30 @@ class Fl_Spinner_Type : public Fl_Widget_Type return 1; } public: - void ideal_size(int &w, int &h) FL_OVERRIDE { + void ideal_size(int &w, int &h) override { + auto layout = Fluid.proj.layout; h = layout->textsize_not_null() + 8; w = layout->textsize_not_null() * 4 + 8; - Fd_Snap_Action::better_size(w, h); + fld::app::Snap_Action::better_size(w, h); } - const char *type_name() FL_OVERRIDE { return "Fl_Spinner"; } - const char *alt_type_name() FL_OVERRIDE { return "fltk::Spinner"; } - Fl_Widget *widget(int x, int y, int w, int h) FL_OVERRIDE { + const char *type_name() override { return "Fl_Spinner"; } + const char *alt_type_name() override { return "fltk::Spinner"; } + Fl_Widget *widget(int x, int y, int w, int h) override { return new Fl_Spinner(x, y, w, h, "spinner:"); } - Fl_Widget_Type *_make() FL_OVERRIDE { return new Fl_Spinner_Type(); } - ID id() const FL_OVERRIDE { return ID_Spinner; } - bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Spinner) ? true : super::is_a(inID); } + Widget_Node *_make() override { return new Spinner_Node(); } + Type type() const override { return Type::Spinner; } + bool is_a(Type inType) const override { return (inType==Type::Spinner) ? true : super::is_a(inType); } }; -static Fl_Spinner_Type Fl_Spinner_type; +Spinner_Node Spinner_Node::prototype; // ---- Type Factory --------------------------------------------------- MARK: - -extern class Fl_Function_Type Fl_Function_type; -extern class Fl_Code_Type Fl_Code_type; -extern class Fl_CodeBlock_Type Fl_CodeBlock_type; -extern class Fl_Data_Type Fl_Data_type; -extern class Fl_Decl_Type Fl_Decl_type; -extern class Fl_DeclBlock_Type Fl_DeclBlock_type; -extern class Fl_Comment_Type Fl_Comment_type; -extern class Fl_Class_Type Fl_Class_type; -extern class Fl_Window_Type Fl_Window_type; -extern class Fl_Widget_Class_Type Fl_Widget_Class_type; -extern class Fl_Group_Type Fl_Group_type; -extern class Fl_Pack_Type Fl_Pack_type; -extern class Fl_Flex_Type Fl_Flex_type; -extern class Fl_Grid_Type Fl_Grid_type; -extern class Fl_Tabs_Type Fl_Tabs_type; -extern class Fl_Scroll_Type Fl_Scroll_type; -extern class Fl_Table_Type Fl_Table_type; -extern class Fl_Tile_Type Fl_Tile_type; -extern class Fl_Input_Choice_Type Fl_Input_Choice_type; -extern class Fl_Choice_Type Fl_Choice_type; -extern class Fl_Menu_Bar_Type Fl_Menu_Bar_type; -extern class Fl_Menu_Button_Type Fl_Menu_Button_type; -extern class Fl_Menu_Item_Type Fl_Menu_Item_type; -extern class Fl_Checkbox_Menu_Item_Type Fl_Checkbox_Menu_Item_type; -extern class Fl_Radio_Menu_Item_Type Fl_Radio_Menu_Item_type; -extern class Fl_Submenu_Type Fl_Submenu_type; -extern class Fl_Wizard_Type Fl_Wizard_type; - -extern class Fl_Button_Type Fl_Button_type; -extern class Fl_Return_Button_Type Fl_Return_Button_type; -extern class Fl_Light_Button_Type Fl_Light_Button_type; -extern class Fl_Check_Button_Type Fl_Check_Button_type; -extern class Fl_Repeat_Button_Type Fl_Repeat_Button_type; -extern class Fl_Round_Button_Type Fl_Round_Button_type; - -extern void select(Fl_Type *,int); -extern void select_only(Fl_Type *); +extern void select(Node *,int); +extern void select_only(Node *); /** List all known types. @@ -1120,72 +1186,72 @@ extern void select_only(Fl_Type *); \note Make sure that this array stays synchronized to `Fl_Menu_Item New_Menu[]` further down in this file. */ -static Fl_Type *known_types[] = { +static Node *known_types[] = { // functions - (Fl_Type*)&Fl_Function_type, - (Fl_Type*)&Fl_Code_type, - (Fl_Type*)&Fl_CodeBlock_type, - (Fl_Type*)&Fl_Decl_type, - (Fl_Type*)&Fl_DeclBlock_type, - (Fl_Type*)&Fl_Class_type, - (Fl_Type*)&Fl_Widget_Class_type, - (Fl_Type*)&Fl_Comment_type, - (Fl_Type*)&Fl_Data_type, + (Node*)&Function_Node::prototype, + (Node*)&Code_Node::prototype, + (Node*)&CodeBlock_Node::prototype, + (Node*)&Decl_Node::prototype, + (Node*)&DeclBlock_Node::prototype, + (Node*)&Class_Node::prototype, + (Node*)&Widget_Class_Node::prototype, + (Node*)&Comment_Node::prototype, + (Node*)&Data_Node::prototype, // groups - (Fl_Type*)&Fl_Window_type, - (Fl_Type*)&Fl_Group_type, - (Fl_Type*)&Fl_Pack_type, - (Fl_Type*)&Fl_Flex_type, - (Fl_Type*)&Fl_Tabs_type, - (Fl_Type*)&Fl_Scroll_type, - (Fl_Type*)&Fl_Tile_type, - (Fl_Type*)&Fl_Wizard_type, - (Fl_Type*)&Fl_Grid_type, + (Node*)&Window_Node::prototype, + (Node*)&Group_Node::prototype, + (Node*)&Pack_Node::prototype, + (Node*)&Flex_Node::prototype, + (Node*)&Tabs_Node::prototype, + (Node*)&Scroll_Node::prototype, + (Node*)&Tile_Node::prototype, + (Node*)&Wizard_Node::prototype, + (Node*)&Grid_Node::prototype, // buttons - (Fl_Type*)&Fl_Button_type, - (Fl_Type*)&Fl_Return_Button_type, - (Fl_Type*)&Fl_Light_Button_type, - (Fl_Type*)&Fl_Check_Button_type, - (Fl_Type*)&Fl_Repeat_Button_type, - (Fl_Type*)&Fl_Round_Button_type, + (Node*)&Button_Node::prototype, + (Node*)&Return_Button_Node::prototype, + (Node*)&Light_Button_Node::prototype, + (Node*)&Check_Button_Node::prototype, + (Node*)&Repeat_Button_Node::prototype, + (Node*)&Round_Button_Node::prototype, // valuators - (Fl_Type*)&Fl_Slider_type, - (Fl_Type*)&Fl_Scrollbar_type, - (Fl_Type*)&Fl_Value_Slider_type, - (Fl_Type*)&Fl_Adjuster_type, - (Fl_Type*)&Fl_Counter_type, - (Fl_Type*)&Fl_Spinner_type, - (Fl_Type*)&Fl_Dial_type, - (Fl_Type*)&Fl_Roller_type, - (Fl_Type*)&Fl_Value_Input_type, - (Fl_Type*)&Fl_Value_Output_type, + (Node*)&Slider_Node::prototype, + (Node*)&Scrollbar_Node::prototype, + (Node*)&Value_Slider_Node::prototype, + (Node*)&Adjuster_Node::prototype, + (Node*)&Counter_Node::prototype, + (Node*)&Spinner_Node::prototype, + (Node*)&Dial_Node::prototype, + (Node*)&Roller_Node::prototype, + (Node*)&Value_Input_Node::prototype, + (Node*)&Value_Output_Node::prototype, // text - (Fl_Type*)&Fl_Input_type, - (Fl_Type*)&Fl_Output_type, - (Fl_Type*)&Fl_Text_Editor_type, - (Fl_Type*)&Fl_Text_Display_type, - (Fl_Type*)&Fl_File_Input_type, - (Fl_Type*)&Fl_Terminal_type, + (Node*)&Input_Node::prototype, + (Node*)&Output_Node::prototype, + (Node*)&Text_Editor_Node::prototype, + (Node*)&Text_Display_Node::prototype, + (Node*)&File_Input_Node::prototype, + (Node*)&Terminal_Node::prototype, // menus - (Fl_Type*)&Fl_Menu_Bar_type, - (Fl_Type*)&Fl_Menu_Button_type, - (Fl_Type*)&Fl_Choice_type, - (Fl_Type*)&Fl_Input_Choice_type, - (Fl_Type*)&Fl_Submenu_type, - (Fl_Type*)&Fl_Menu_Item_type, - (Fl_Type*)&Fl_Checkbox_Menu_Item_type, - (Fl_Type*)&Fl_Radio_Menu_Item_type, + (Node*)&Menu_Bar_Node::prototype, + (Node*)&Menu_Button_Node::prototype, + (Node*)&Choice_Node::prototype, + (Node*)&Input_Choice_Node::prototype, + (Node*)&Submenu_Node::prototype, + (Node*)&Menu_Item_Node::prototype, + (Node*)&Checkbox_Menu_Item_Node::prototype, + (Node*)&Radio_Menu_Item_Node::prototype, // browsers - (Fl_Type*)&Fl_Browser_type, - (Fl_Type*)&Fl_Check_Browser_type, - (Fl_Type*)&Fl_File_Browser_type, - (Fl_Type*)&Fl_Tree_type, - (Fl_Type*)&Fl_Help_View_type, - (Fl_Type*)&Fl_Table_type, + (Node*)&Browser_Node::prototype, + (Node*)&Check_Browser_Node::prototype, + (Node*)&File_Browser_Node::prototype, + (Node*)&Tree_Node::prototype, + (Node*)&Help_View_Node::prototype, + (Node*)&Table_Node::prototype, // misc - (Fl_Type*)&Fl_Box_type, - (Fl_Type*)&Fl_Clock_type, - (Fl_Type*)&Fl_Progress_type, + (Node*)&Box_Node::prototype, + (Node*)&Clock_Node::prototype, + (Node*)&Progress_Node::prototype, }; /** @@ -1207,19 +1273,21 @@ static Fl_Type *known_types[] = { lower case 't' in type. \param[in] strategy add after current or as last child \param[in] and_open if set to true, call open() on the widget after creating it - \return the newly created type or NULL + \return the newly created type or nullptr \see add_new_widget_from_file(const char*, int) - add_new_widget_from_user(Fl_Type*, int) + add_new_widget_from_user(Node*, int) add_new_widget_from_user(const char*, int) */ -Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy, bool and_open) { - undo_checkpoint(); - undo_suspend(); - Fl_Type *t = ((Fl_Type*)inPrototype)->make(strategy); +Node *add_new_widget_from_user(Node *inPrototype, Strategy strategy, bool and_open) { + Fluid.proj.undo.checkpoint(); + Fluid.proj.undo.suspend(); + auto layout = Fluid.proj.layout; + Node *t = ((Node*)inPrototype)->make(strategy); if (t) { - if (t->is_widget() && !t->is_a(ID_Window)) { - Fl_Widget_Type *wt = (Fl_Widget_Type *)t; + if (t->is_widget() && !t->is_a(Type::Window)) { + auto layout = Fluid.proj.layout; + Widget_Node *wt = (Widget_Node *)t; bool changed = false; // Set font sizes... @@ -1244,71 +1312,71 @@ Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy, bool wt->textstuff(2, f, s, c); } - if (changed && t->is_a(ID_Menu_Item)) { - Fl_Type * tt = t->parent; - while (tt && !tt->is_a(ID_Menu_Manager_)) tt = tt->parent; + if (changed && t->is_a(Type::Menu_Item)) { + Node * tt = t->parent; + while (tt && !tt->is_a(Type::Menu_Manager_)) tt = tt->parent; if (tt) - ((Fl_Menu_Manager_Type*)tt)->build_menu(); + ((Menu_Manager_Node*)tt)->build_menu(); } } - if (t->is_true_widget() && !t->is_a(ID_Window)) { + if (t->is_true_widget() && !t->is_a(Type::Window)) { // Resize and/or reposition new widget... - Fl_Widget_Type *wt = (Fl_Widget_Type *)t; + Widget_Node *wt = (Widget_Node *)t; // The parent field is already set at this point, so we can use that // inside ideal_size(). int w = 0, h = 0; wt->ideal_size(w, h); - if ((t->parent && t->parent->is_a(ID_Flex))) { - if (Fl_Window_Type::popupx != 0x7FFFFFFF) - ((Fl_Flex_Type*)t->parent)->insert_child_at(((Fl_Widget_Type*)t)->o, Fl_Window_Type::popupx, Fl_Window_Type::popupy); + if ((t->parent && t->parent->is_a(Type::Flex))) { + if (Window_Node::popupx != 0x7FFFFFFF) + ((Flex_Node*)t->parent)->insert_child_at(((Widget_Node*)t)->o, Window_Node::popupx, Window_Node::popupy); t->parent->layout_widget(); - } else if ( wt->is_a(ID_Group) + } else if ( wt->is_a(Type::Group) && wt->parent - && wt->parent->is_a(ID_Tabs) - //&& (Fl_Window_Type::popupx == 0x7FFFFFFF) + && wt->parent->is_a(Type::Tabs) + //&& (Window_Node::popupx == 0x7FFFFFFF) && (layout->top_tabs_margin > 0)) { // If the widget is a group and the parent is tabs and the top tabs // margin is set (and the user is not requesting a specific position) // then prefit the group correctly to the Tabs container. - Fl_Widget *po = ((Fl_Tabs_Type*)wt->parent)->o; + Fl_Widget *po = ((Tabs_Node*)wt->parent)->o; wt->o->resize(po->x(), po->y() + layout->top_tabs_margin, po->w(), po->h() - layout->top_tabs_margin); - } else if ( wt->is_a(ID_Menu_Bar) + } else if ( wt->is_a(Type::Menu_Bar) && wt->parent - && wt->parent->is_a(ID_Window) + && wt->parent->is_a(Type::Window) && (wt->prev == wt->parent)) { // If this is the first child of a window, make the menu bar as wide as // the window and drop it at 0, 0. Otherwise just use the suggested size. w = wt->o->window()->w(); wt->o->resize(0, 0, w, h); } else { - if (Fl_Window_Type::popupx != 0x7FFFFFFF) { + if (Window_Node::popupx != 0x7FFFFFFF) { // If this callback was called from the RMB popup menu in a window, // popupx and popupy will contain the mouse coordinates at RMB event. - wt->o->resize(Fl_Window_Type::popupx, Fl_Window_Type::popupy, w, h); + wt->o->resize(Window_Node::popupx, Window_Node::popupy, w, h); } else { // If popupx is invalid, use the default position and find a good // size for the widget. wt->o->size(w, h); } } - if (t->parent && t->parent->is_a(ID_Grid)) { - if (Fl_Window_Type::popupx != 0x7FFFFFFF) { - ((Fl_Grid_Type*)t->parent)->insert_child_at(((Fl_Widget_Type*)t)->o, Fl_Window_Type::popupx, Fl_Window_Type::popupy); + if (t->parent && t->parent->is_a(Type::Grid)) { + if (Window_Node::popupx != 0x7FFFFFFF) { + ((Grid_Node*)t->parent)->insert_child_at(((Widget_Node*)t)->o, Window_Node::popupx, Window_Node::popupy); } else { - ((Fl_Grid_Type*)t->parent)->insert_child_at_next_free_cell(((Fl_Widget_Type*)t)->o); + ((Grid_Node*)t->parent)->insert_child_at_next_free_cell(((Widget_Node*)t)->o); } } } - if (t->is_a(ID_Window)) { + if (t->is_a(Type::Window)) { int x = 0, y = 0, w = 480, h = 320; - Fl_Window_Type *wt = (Fl_Window_Type *)t; + Window_Node *wt = (Window_Node *)t; wt->ideal_size(w, h); - if (main_window) { + if (Fluid.main_window) { int sx, sy, sw, sh; - Fl_Window *win = main_window; + Fl_Window *win = Fluid.main_window; int screen = Fl::screen_num(win->x(), win->y()); Fl::screen_work_area(sx, sy, sw, sh, screen); x = sx + sw/2 - w/2; @@ -1318,14 +1386,14 @@ Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy, bool } // make the new widget visible select_only(t); - set_modflag(1); + Fluid.proj.set_modflag(1); if (and_open) t->open(); } else { - undo_current --; - undo_last --; + Fluid.proj.undo.current_ --; + Fluid.proj.undo.last_ --; } - undo_resume(); + Fluid.proj.undo.resume(); return t; } @@ -1335,126 +1403,126 @@ Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy, bool \param[in] inName find the right prototype by this name \param[in] strategy where to add the node \param[in] and_open if set to true, call open() on the widget after creating it - \return the newly created type or NULL + \return the newly created type or nullptr \see add_new_widget_from_file(const char*, int) - add_new_widget_from_user(Fl_Type*, int) + add_new_widget_from_user(Node*, int) add_new_widget_from_user(const char*, int) */ -Fl_Type *add_new_widget_from_user(const char *inName, Strategy strategy, bool and_open) { - Fl_Type *prototype = typename_to_prototype(inName); +Node *add_new_widget_from_user(const char *inName, Strategy strategy, bool and_open) { + Node *prototype = typename_to_prototype(inName); if (prototype) return add_new_widget_from_user(prototype, strategy, and_open); else - return NULL; + return nullptr; } /** Callback for all non-widget menu items. */ static void cbf(Fl_Widget *, void *v) { - Fl_Type *t = NULL; - if (Fl_Type::current && Fl_Type::current->can_have_children()) - t = ((Fl_Type*)v)->make(Strategy::AS_LAST_CHILD); + Node *t = nullptr; + if (Fluid.proj.tree.current && Fluid.proj.tree.current->can_have_children()) + t = ((Node*)v)->make(Strategy::AS_LAST_CHILD); else - t = ((Fl_Type*)v)->make(Strategy::AFTER_CURRENT); + t = ((Node*)v)->make(Strategy::AFTER_CURRENT); select_only(t); } /** Callback for all widget menu items. - \param[in] v cast to Fl_Type to get the prototype of the type that the user + \param[in] v cast to Node to get the prototype of the type that the user wants to create. */ static void cb(Fl_Widget *, void *v) { - Fl_Type *t = NULL; - if (Fl_Type::current && Fl_Type::current->can_have_children()) - t = add_new_widget_from_user((Fl_Type*)v, Strategy::AS_LAST_CHILD); + Node *t = nullptr; + if (Fluid.proj.tree.current && Fluid.proj.tree.current->can_have_children()) + t = add_new_widget_from_user((Node*)v, Strategy::AS_LAST_CHILD); else - t = add_new_widget_from_user((Fl_Type*)v, Strategy::AFTER_CURRENT); + t = add_new_widget_from_user((Node*)v, Strategy::AFTER_CURRENT); select_only(t); } /** - \note Make sure that this menu stays synchronized to `Fl_Type *known_types[]` + \note Make sure that this menu stays synchronized to `Node *known_types[]` defined further up in this file. */ Fl_Menu_Item New_Menu[] = { -{"Code",0,0,0,FL_SUBMENU}, - {"Function/Method",0,cbf,(void*)&Fl_Function_type}, - {"Code",0,cbf,(void*)&Fl_Code_type}, - {"Code Block",0,cbf,(void*)&Fl_CodeBlock_type}, - {"Declaration",0,cbf,(void*)&Fl_Decl_type}, - {"Declaration Block",0,cbf,(void*)&Fl_DeclBlock_type}, - {"Class",0,cbf,(void*)&Fl_Class_type}, - {"Widget Class",0,cb,(void*)&Fl_Widget_Class_type}, - {"Comment",0,cbf,(void*)&Fl_Comment_type}, - {"Inlined Data",0,cbf,(void*)&Fl_Data_type}, -{0}, -{"Group",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Window_type}, - {0,0,cb,(void*)&Fl_Group_type}, - {0,0,cb,(void*)&Fl_Pack_type}, - {0,0,cb,(void*)&Fl_Flex_type}, - {0,0,cb,(void*)&Fl_Tabs_type}, - {0,0,cb,(void*)&Fl_Scroll_type}, - {0,0,cb,(void*)&Fl_Tile_type}, - {0,0,cb,(void*)&Fl_Wizard_type}, - {0,0,cb,(void*)&Fl_Grid_type}, -{0}, -{"Buttons",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Button_type}, - {0,0,cb,(void*)&Fl_Return_Button_type}, - {0,0,cb,(void*)&Fl_Light_Button_type}, - {0,0,cb,(void*)&Fl_Check_Button_type}, - {0,0,cb,(void*)&Fl_Repeat_Button_type}, - {0,0,cb,(void*)&Fl_Round_Button_type}, -{0}, -{"Valuators",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Slider_type}, - {0,0,cb,(void*)&Fl_Scrollbar_type}, - {0,0,cb,(void*)&Fl_Value_Slider_type}, - {0,0,cb,(void*)&Fl_Adjuster_type}, - {0,0,cb,(void*)&Fl_Counter_type}, - {0,0,cb,(void*)&Fl_Spinner_type}, - {0,0,cb,(void*)&Fl_Dial_type}, - {0,0,cb,(void*)&Fl_Roller_type}, - {0,0,cb,(void*)&Fl_Value_Input_type}, - {0,0,cb,(void*)&Fl_Value_Output_type}, -{0}, -{"Text",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Input_type}, - {0,0,cb,(void*)&Fl_Output_type}, - {0,0,cb,(void*)&Fl_Text_Editor_type}, - {0,0,cb,(void*)&Fl_Text_Display_type}, - {0,0,cb,(void*)&Fl_File_Input_type}, - {0,0,cb,(void*)&Fl_Terminal_type}, -{0}, -{"Menus",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Menu_Bar_type}, - {0,0,cb,(void*)&Fl_Menu_Button_type}, - {0,0,cb,(void*)&Fl_Choice_type}, - {0,0,cb,(void*)&Fl_Input_Choice_type}, - {0,0,cb, (void*)&Fl_Submenu_type}, - {0,0,cb, (void*)&Fl_Menu_Item_type}, - {"Checkbox Menu Item",0,cb, (void*)&Fl_Checkbox_Menu_Item_type}, - {"Radio Menu Item",0,cb, (void*)&Fl_Radio_Menu_Item_type}, -{0}, -{"Browsers",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Browser_type}, - {0,0,cb,(void*)&Fl_Check_Browser_type}, - {0,0,cb,(void*)&Fl_File_Browser_type}, - {0,0,cb,(void*)&Fl_Tree_type}, - {0,0,cb,(void*)&Fl_Help_View_type}, - {0,0,cb,(void*)&Fl_Table_type}, -{0}, -{"Other",0,0,0,FL_SUBMENU}, - {0,0,cb,(void*)&Fl_Box_type}, - {0,0,cb,(void*)&Fl_Clock_type}, - {0,0,cb,(void*)&Fl_Progress_type}, -{0}, -{0}}; + {"Code",0,nullptr,nullptr,FL_SUBMENU}, + {"Function/Method",0,cbf,(void*)&Function_Node::prototype}, + {"Code",0,cbf,(void*)&Code_Node::prototype}, + {"Code Block",0,cbf,(void*)&CodeBlock_Node::prototype}, + {"Declaration",0,cbf,(void*)&Decl_Node::prototype}, + {"Declaration Block",0,cbf,(void*)&DeclBlock_Node::prototype}, + {"Class",0,cbf,(void*)&Class_Node::prototype}, + {"Widget Class",0,cb,(void*)&Widget_Class_Node::prototype}, + {"Comment",0,cbf,(void*)&Comment_Node::prototype}, + {"Inlined Data",0,cbf,(void*)&Data_Node::prototype}, + {nullptr}, + {"Group",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Window_Node::prototype}, + {nullptr,0,cb,(void*)&Group_Node::prototype}, + {nullptr,0,cb,(void*)&Pack_Node::prototype}, + {nullptr,0,cb,(void*)&Flex_Node::prototype}, + {nullptr,0,cb,(void*)&Tabs_Node::prototype}, + {nullptr,0,cb,(void*)&Scroll_Node::prototype}, + {nullptr,0,cb,(void*)&Tile_Node::prototype}, + {nullptr,0,cb,(void*)&Wizard_Node::prototype}, + {nullptr,0,cb,(void*)&Grid_Node::prototype}, + {nullptr}, + {"Buttons",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Button_Node::prototype}, + {nullptr,0,cb,(void*)&Return_Button_Node::prototype}, + {nullptr,0,cb,(void*)&Light_Button_Node::prototype}, + {nullptr,0,cb,(void*)&Check_Button_Node::prototype}, + {nullptr,0,cb,(void*)&Repeat_Button_Node::prototype}, + {nullptr,0,cb,(void*)&Round_Button_Node::prototype}, + {nullptr}, + {"Valuators",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Slider_Node::prototype}, + {nullptr,0,cb,(void*)&Scrollbar_Node::prototype}, + {nullptr,0,cb,(void*)&Value_Slider_Node::prototype}, + {nullptr,0,cb,(void*)&Adjuster_Node::prototype}, + {nullptr,0,cb,(void*)&Counter_Node::prototype}, + {nullptr,0,cb,(void*)&Spinner_Node::prototype}, + {nullptr,0,cb,(void*)&Dial_Node::prototype}, + {nullptr,0,cb,(void*)&Roller_Node::prototype}, + {nullptr,0,cb,(void*)&Value_Input_Node::prototype}, + {nullptr,0,cb,(void*)&Value_Output_Node::prototype}, + {nullptr}, + {"Text",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Input_Node::prototype}, + {nullptr,0,cb,(void*)&Output_Node::prototype}, + {nullptr,0,cb,(void*)&Text_Editor_Node::prototype}, + {nullptr,0,cb,(void*)&Text_Display_Node::prototype}, + {nullptr,0,cb,(void*)&File_Input_Node::prototype}, + {nullptr,0,cb,(void*)&Terminal_Node::prototype}, + {nullptr}, + {"Menus",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Menu_Bar_Node::prototype}, + {nullptr,0,cb,(void*)&Menu_Button_Node::prototype}, + {nullptr,0,cb,(void*)&Choice_Node::prototype}, + {nullptr,0,cb,(void*)&Input_Choice_Node::prototype}, + {nullptr,0,cb, (void*)&Submenu_Node::prototype}, + {nullptr,0,cb, (void*)&Menu_Item_Node::prototype}, + {"Checkbox Menu Item",0,cb, (void*)&Checkbox_Menu_Item_Node::prototype}, + {"Radio Menu Item",0,cb, (void*)&Radio_Menu_Item_Node::prototype}, + {nullptr}, + {"Browsers",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Browser_Node::prototype}, + {nullptr,0,cb,(void*)&Check_Browser_Node::prototype}, + {nullptr,0,cb,(void*)&File_Browser_Node::prototype}, + {nullptr,0,cb,(void*)&Tree_Node::prototype}, + {nullptr,0,cb,(void*)&Help_View_Node::prototype}, + {nullptr,0,cb,(void*)&Table_Node::prototype}, + {nullptr}, + {"Other",0,nullptr,nullptr,FL_SUBMENU}, + {nullptr,0,cb,(void*)&Box_Node::prototype}, + {nullptr,0,cb,(void*)&Clock_Node::prototype}, + {nullptr,0,cb,(void*)&Progress_Node::prototype}, + {nullptr}, + {nullptr}}; #include @@ -1464,8 +1532,8 @@ Fl_Menu_Item New_Menu[] = { The icon may be null. If ic is null only the text is assigned to the label and Fl_Multi_Label is not used. \param[in] mi pointer to tme menu item that will be modified - \param[in] ic icon for the menu, may be NULL - \param[in] txt new label text, may *not* be NULL, will not be copied + \param[in] ic icon for the menu, may be nullptr + \param[in] txt new label text, may *not* be nullptr, will not be copied */ static void make_iconlabel(Fl_Menu_Item *mi, Fl_Image *ic, const char *txt) { @@ -1495,14 +1563,14 @@ void fill_in_New_Menu() { for (unsigned i = 0; i < sizeof(New_Menu)/sizeof(*New_Menu); i++) { Fl_Menu_Item *m = New_Menu+i; if (m->user_data()) { - Fl_Type *t = (Fl_Type*)m->user_data(); + Node *t = (Node*)m->user_data(); if (m->text) { - make_iconlabel( m, pixmap[t->id()], m->label() ); + make_iconlabel( m, pixmap[(int)t->type()], m->label() ); } else { const char *n = t->type_name(); if (!strncmp(n,"Fl_",3)) n += 3; if (!strncmp(n,"fltk::",6)) n += 6; - make_iconlabel( m, pixmap[t->id()], n ); + make_iconlabel( m, pixmap[(int)t->type()], n ); } } } @@ -1511,21 +1579,21 @@ void fill_in_New_Menu() { /** Find the correct prototype for a given type name. \param[in] inName a C string that must match type_name() or alt_type_name() of - one of the known Fl_Type classes. - \return the matching prototype or NULL + one of the known Node classes. + \return the matching prototype or nullptr */ -Fl_Type *typename_to_prototype(const char *inName) +Node *typename_to_prototype(const char *inName) { - if (inName==NULL || *inName==0) - return NULL; + if (inName==nullptr || *inName==0) + return nullptr; for (unsigned i = 0; i < sizeof(known_types)/sizeof(*known_types); i++) { - Fl_Type *prototype = known_types[i]; + Node *prototype = known_types[i]; if (fl_ascii_strcasecmp(inName, prototype->type_name())==0) return prototype; if (fl_ascii_strcasecmp(inName, prototype->alt_type_name())==0) return prototype; } - return NULL; + return nullptr; } /** @@ -1537,16 +1605,16 @@ Fl_Type *typename_to_prototype(const char *inName) \param[in] inName a C string that described the type we want \param[in] strategy add after current or as last child - \return the type node that was created or NULL + \return the type node that was created or nullptr \see add_new_widget_from_file(const char*, int) - add_new_widget_from_user(Fl_Type*, int) + add_new_widget_from_user(Node*, int) add_new_widget_from_user(const char*, int) */ -Fl_Type *add_new_widget_from_file(const char *inName, Strategy strategy) { - Fl_Type *prototype = typename_to_prototype(inName); +Node *add_new_widget_from_file(const char *inName, Strategy strategy) { + Node *prototype = typename_to_prototype(inName); if (!prototype) - return NULL; - Fl_Type *new_node = prototype->make(strategy); + return nullptr; + Node *new_node = prototype->make(strategy); return new_node; } diff --git a/fluid/nodes/factory.h b/fluid/nodes/factory.h index f1968b167..172f7e5d4 100644 --- a/fluid/nodes/factory.h +++ b/fluid/nodes/factory.h @@ -1,5 +1,5 @@ // -// Widget type header file for the Fast Light Tool Kit (FLTK). +// Node Factory header file for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2021 by Bill Spitzak and others. // @@ -14,21 +14,21 @@ // https://www.fltk.org/bugs.php // -#ifndef _FLUID_FACTORY_H -#define _FLUID_FACTORY_H +#ifndef FLUID_NODES_FACTORY_H +#define FLUID_NODES_FACTORY_H -#include "nodes/Fl_Type.h" +#include "nodes/Node.h" struct Fl_Menu_Item; extern Fl_Menu_Item New_Menu[]; void fill_in_New_Menu(); -Fl_Type *typename_to_prototype(const char *inName); +Node *typename_to_prototype(const char *inName); -Fl_Type *add_new_widget_from_file(const char *inName, Strategy strategy); -Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy, bool and_open=true); -Fl_Type *add_new_widget_from_user(const char *inName, Strategy strategy, bool and_open=true); +Node *add_new_widget_from_file(const char *inName, Strategy strategy); +Node *add_new_widget_from_user(Node *inPrototype, Strategy strategy, bool and_open=true); +Node *add_new_widget_from_user(const char *inName, Strategy strategy, bool and_open=true); -#endif // _FLUID_FACTORY_H +#endif // FLUID_NODES_FACTORY_H -- cgit v1.2.3