summaryrefslogtreecommitdiff
path: root/fluid/nodes/Widget_Node.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'fluid/nodes/Widget_Node.cxx')
-rw-r--r--fluid/nodes/Widget_Node.cxx2438
1 files changed, 2438 insertions, 0 deletions
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 <FL/Fl.H>
+#include <FL/Fl_Group.H>
+#include <FL/Fl_Table.H>
+#include <FL/Fl_Input.H>
+#include <FL/fl_message.H>
+#include <FL/Fl_Slider.H>
+#include <FL/Fl_Spinner.H>
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Flex.H>
+#include "../src/flstring.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#undef min
+#undef max
+#include <algorithm>
+
+// 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<Menu_Bar_Node*>(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; n<NUM_EXTRA_CODE; n++) {extra_code_[n] = nullptr; }
+ subclass_ = nullptr;
+ hotspot_ = 0;
+ tooltip_ = nullptr;
+ image_name_ = nullptr;
+ inactive_name_ = nullptr;
+ image = nullptr;
+ inactive = nullptr;
+ o = nullptr;
+ public_ = 1;
+ bind_image_ = 0;
+ compress_image_ = 1;
+ bind_deimage_ = 0;
+ compress_deimage_ = 1;
+ scale_image_w_ = 0;
+ scale_image_h_ = 0;
+ scale_deimage_w_ = 0;
+ scale_deimage_h_ = 0;
+}
+
+Widget_Node::~Widget_Node() {
+ if (o) {
+ Fl_Window *win = o->window();
+ 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; n<NUM_EXTRA_CODE; n++) {
+ if (extra_code_[n]) free((void*) extra_code_[n]);
+ }
+}
+
+void Widget_Node::extra_code(int m,const char *n) {
+ storestring(n,extra_code_[m]);
+}
+
+extern void redraw_browser();
+void Widget_Node::subclass(const char *n) {
+ if (storestring(n,subclass_) && visible)
+ redraw_browser();
+}
+
+void Widget_Node::tooltip(const char *n) {
+ storestring(n,tooltip_);
+ o->tooltip(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[] = {
+ {"<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 <FL/Fl.H>");
+ f.write_h_once("#include <FL/%s.H>", 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();
+}
+