From c3571838cb10133aa913efd7523b9543a65459c1 Mon Sep 17 00:00:00 2001 From: Matthias Melcher Date: Fri, 7 Mar 2025 18:54:03 +0100 Subject: Fluid: Rebuilding most of the widget directory. One file per logical unit. Namespaces. Non-static data member initializers to never get an uninitialized field again. --- fluid/widgets/Bin_Button.cxx | 128 +++++++++++++++ fluid/widgets/Bin_Button.h | 44 ++++++ fluid/widgets/CodeEditor.cxx | 316 ------------------------------------- fluid/widgets/CodeEditor.h | 105 ------------- fluid/widgets/Code_Editor.cxx | 254 ++++++++++++++++++++++++++++++ fluid/widgets/Code_Editor.h | 70 +++++++++ fluid/widgets/Code_Viewer.cxx | 51 ++++++ fluid/widgets/Code_Viewer.h | 51 ++++++ fluid/widgets/Formula_Input.cxx | 217 +++++++++++++++++++++++++ fluid/widgets/Formula_Input.h | 73 +++++++++ fluid/widgets/StyleParse.cxx | 328 -------------------------------------- fluid/widgets/StyleParse.h | 61 -------- fluid/widgets/Style_Parser.cxx | 331 +++++++++++++++++++++++++++++++++++++++ fluid/widgets/Style_Parser.h | 59 +++++++ fluid/widgets/Text_Viewer.cxx | 58 +++++++ fluid/widgets/Text_Viewer.h | 46 ++++++ fluid/widgets/custom_widgets.cxx | 311 ------------------------------------ fluid/widgets/custom_widgets.h | 90 ----------- 18 files changed, 1382 insertions(+), 1211 deletions(-) create mode 100644 fluid/widgets/Bin_Button.cxx create mode 100644 fluid/widgets/Bin_Button.h delete mode 100644 fluid/widgets/CodeEditor.cxx delete mode 100644 fluid/widgets/CodeEditor.h create mode 100644 fluid/widgets/Code_Editor.cxx create mode 100644 fluid/widgets/Code_Editor.h create mode 100644 fluid/widgets/Code_Viewer.cxx create mode 100644 fluid/widgets/Code_Viewer.h create mode 100644 fluid/widgets/Formula_Input.cxx create mode 100644 fluid/widgets/Formula_Input.h delete mode 100644 fluid/widgets/StyleParse.cxx delete mode 100644 fluid/widgets/StyleParse.h create mode 100644 fluid/widgets/Style_Parser.cxx create mode 100644 fluid/widgets/Style_Parser.h create mode 100644 fluid/widgets/Text_Viewer.cxx create mode 100644 fluid/widgets/Text_Viewer.h delete mode 100644 fluid/widgets/custom_widgets.cxx delete mode 100644 fluid/widgets/custom_widgets.h (limited to 'fluid/widgets') diff --git a/fluid/widgets/Bin_Button.cxx b/fluid/widgets/Bin_Button.cxx new file mode 100644 index 000000000..cc351085d --- /dev/null +++ b/fluid/widgets/Bin_Button.cxx @@ -0,0 +1,128 @@ +// +// Widget Bin Button 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 "widgets/Bin_Button.h" + +#include "app/fluid.h" +#include "nodes/factory.h" +#include "nodes/Fl_Window_Type.h" +#include "widgets/widget_browser.h" + +#include +#include + +using namespace fld; +using namespace fld::widget; + + +/** \class fld::widget::Bin_Button + The Bin_Button button is a button that can be used in the widget bin to + allow the user to drag and drop widgets into a window or group. This feature + makes it easy for the user to position a widget at a specific location within + the window or group. + */ + +/** + Convert mouse dragging into a drag and drop event. + */ +int fld::widget::Bin_Button::handle(int inEvent) +{ + int ret = 0; + switch (inEvent) { + case FL_PUSH: + Fl_Button::handle(inEvent); + return 1; // make sure that we get drag events + case FL_DRAG: + ret = Fl_Button::handle(inEvent); + if (!user_data()) + return ret; + if (!Fl::event_is_click()) { // make it a dnd event + // fake a drag outside of the widget + Fl::e_x = x()-1; + Fl_Button::handle(inEvent); + // fake a button release + Fl_Button::handle(FL_RELEASE); + // make it into a dnd event + const char *type_name = (const char*)user_data(); + Fl_Type::current_dnd = Fl_Type::current; + Fl::copy(type_name, (int)strlen(type_name)+1, 0); + Fl::dnd(); + return 1; + } + return ret; + } + return Fl_Button::handle(inEvent); +} + +/** \class fld::widget::Bin_Window_Button + The Bin_Window_Button button is used in the widget bin to create new + windows by dragging and dropping. When the button is dragged and dropped onto + the desktop, a new window will be created at the drop location. + + This does not work in Wayland because Wayland does not allow client + applications to control window placement. + */ + +/** + Convert mouse dragging into a drag and drop event. + */ +int fld::widget::Bin_Window_Button::handle(int inEvent) +{ + static Fl_Window *drag_win = NULL; + int ret = 0; + switch (inEvent) { + case FL_PUSH: + Fl_Button::handle(inEvent); + return 1; // make sure that we get drag events + case FL_DRAG: + ret = Fl_Button::handle(inEvent); + if (!user_data()) + return ret; + if (!Fl::event_is_click()) { + if (!drag_win) { + drag_win = new Fl_Window(0, 0, 480, 320); + drag_win->border(0); + drag_win->set_non_modal(); + } + if (drag_win) { + drag_win->position(Fl::event_x_root()+1, Fl::event_y_root()+1); + drag_win->show(); + } + // Does not work outside window: fl_cursor(FL_CURSOR_HAND); + } + return ret; + case FL_RELEASE: + if (drag_win) { + Fl::delete_widget(drag_win); + drag_win = NULL; + // create a new window here + Fl_Type *prototype = typename_to_prototype((char*)user_data()); + if (prototype) { + Fl_Type *new_type = add_new_widget_from_user(prototype, Strategy::AFTER_CURRENT); + if (new_type && new_type->is_a(ID_Window)) { + Fl_Window_Type *new_window = (Fl_Window_Type*)new_type; + Fl_Window *w = (Fl_Window *)new_window->o; + w->position(Fl::event_x_root(), Fl::event_y_root()); + } + } + widget_browser->display(Fl_Type::current); + widget_browser->rebuild(); + } + return Fl_Button::handle(inEvent); + } + return Fl_Button::handle(inEvent); +} + diff --git a/fluid/widgets/Bin_Button.h b/fluid/widgets/Bin_Button.h new file mode 100644 index 000000000..342fe0eac --- /dev/null +++ b/fluid/widgets/Bin_Button.h @@ -0,0 +1,44 @@ +// +// Widget Bin Button 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_WIDGETS_BIN_BUTTON_H +#define FLUID_WIDGETS_BIN_BUTTON_H + +#include + +namespace fld { +namespace widget { + +// Adding drag and drop for dragging widgets into windows. +class Bin_Button : public Fl_Button { +public: + int handle(int) override; + Bin_Button(int X,int Y,int W,int H, const char* l = nullptr) : + Fl_Button(X,Y,W,H,l) { } +}; + +// Adding drag and drop functionality to drag window prototypes onto the desktop. +class Bin_Window_Button : public Fl_Button { +public: + int handle(int) override; + Bin_Window_Button(int X,int Y,int W,int H, const char* l = nullptr) : + Fl_Button(X,Y,W,H,l) { } +}; + +} // namespace widget +} // namespace fld + +#endif // FLUID_WIDGETS_BIN_BUTTON_H diff --git a/fluid/widgets/CodeEditor.cxx b/fluid/widgets/CodeEditor.cxx deleted file mode 100644 index ca114b577..000000000 --- a/fluid/widgets/CodeEditor.cxx +++ /dev/null @@ -1,316 +0,0 @@ -// -// Code editor widget for the Fast Light Tool Kit (FLTK). -// Syntax highlighting rewritten by erco@seriss.com 09/15/20. -// -// Copyright 1998-2025 by Bill Spitzak and others. -// -// 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 necessary headers... -// - -#include "widgets/CodeEditor.h" - -#include -#include -#include -#include - -// ---- CodeEditor implementation - -/** - Lookup table for all supported styles. - Every table entry describes a rendering style for the corresponding text. - */ -Fl_Text_Display::Style_Table_Entry CodeEditor::styletable[] = { // Style table - { FL_FOREGROUND_COLOR, FL_COURIER, 11 }, // A - Plain - { FL_DARK_GREEN, FL_COURIER_ITALIC, 11 }, // B - Line comments - { FL_DARK_GREEN, FL_COURIER_ITALIC, 11 }, // C - Block comments - { FL_BLUE, FL_COURIER, 11 }, // D - Strings - { FL_DARK_RED, FL_COURIER, 11 }, // E - Directives - { FL_DARK_RED, FL_COURIER_BOLD, 11 }, // F - Types - { FL_BLUE, FL_COURIER_BOLD, 11 }, // G - Keywords - { 220, /* med cyan */ FL_COURIER, 11 } // H - Single quote chars - }; - -/** - Parse text and produce style data. - \param[in] in_tbuff text buffer to parse - \param[inout] in_sbuff style buffer we modify - \param[in] in_len byte length to parse - \param[in] in_style starting style letter - */ -void CodeEditor::style_parse(const char *in_tbuff, // text buffer to parse - char *in_sbuff, // style buffer we modify - int in_len, // byte length to parse - char in_style) { // starting style letter - // Style letters: - // - // 'A' - Plain - // 'B' - Line comments // .. - // 'C' - Block comments /*..*/ - // 'D' - Strings "xxx" - // 'E' - Directives #define, #include.. - // 'F' - Types void, char.. - // 'G' - Keywords if, while.. - // 'H' - Chars 'x' - - StyleParse sp; - sp.tbuff = in_tbuff; - sp.sbuff = in_sbuff; - sp.len = in_len; - sp.style = in_style; - sp.lwhite = 1; // 1:while parsing over leading white and first char past, 0:past white - sp.col = 0; - sp.last = 0; - - // Loop through the code, updating style buffer - char c; - while ( sp.len > 0 ) { - c = sp.tbuff[0]; // current char - if ( sp.style == 'C' ) { // Started in middle of comment block? - if ( !sp.parse_block_comment() ) break; - } else if ( strncmp(sp.tbuff, "/*", 2)==0 ) { // C style comment block? - if ( !sp.parse_block_comment() ) break; - } else if ( c == '\\' ) { // Backslash escape char? - if ( !sp.parse_escape() ) break; - } else if ( strncmp(sp.tbuff, "//", 2)==0 ) { // Line comment? - if ( !sp.parse_line_comment() ) break; - } else if ( c == '"' ) { // Start of double quoted string? - if ( !sp.parse_quoted_string('"', 'D') ) break; - } else if ( c == '\'' ) { // Start of single quoted string? - if ( !sp.parse_quoted_string('\'', 'H') ) break; - } else if ( c == '#' && sp.lwhite ) { // Start of '#' directive? - if ( !sp.parse_directive() ) break; - } else if ( !sp.last && (islower(c) || c == '_') ) { // Possible C/C++ keyword? - if ( !sp.parse_keyword() ) break; - } else { // All other chars? - if ( !sp.parse_all_else() ) break; - } - } -} - -/** - Update unfinished styles. - */ -void CodeEditor::style_unfinished_cb(int, void*) { -} - -/** - Update the style buffer. - \param[in] pos insert position in text - \param[in] nInserted number of bytes inserted - \param[in] nDeleted number of bytes deleted - \param[in] cbArg pointer back to the code editor - */ -void CodeEditor::style_update(int pos, int nInserted, int nDeleted, - int /*nRestyled*/, const char * /*deletedText*/, - void *cbArg) { - CodeEditor *editor = (CodeEditor*)cbArg; - char *style, // Style data - *text; // Text data - - - // If this is just a selection change, just unselect the style buffer... - if (nInserted == 0 && nDeleted == 0) { - editor->mStyleBuffer->unselect(); - return; - } - - // Track changes in the text buffer... - if (nInserted > 0) { - // Insert characters into the style buffer... - style = new char[nInserted + 1]; - memset(style, 'A', nInserted); - style[nInserted] = '\0'; - - editor->mStyleBuffer->replace(pos, pos + nDeleted, style); - delete[] style; - } else { - // Just delete characters in the style buffer... - editor->mStyleBuffer->remove(pos, pos + nDeleted); - } - - // Select the area that was just updated to avoid unnecessary - // callbacks... - editor->mStyleBuffer->select(pos, pos + nInserted - nDeleted); - - // Reparse whole buffer, don't get cute. Maybe optimize range later - int len = editor->buffer()->length(); - text = editor->mBuffer->text_range(0, len); - style = editor->mStyleBuffer->text_range(0, len); - - style_parse(text, style, editor->mBuffer->length(), 'A'); - - editor->mStyleBuffer->replace(0, len, style); - editor->redisplay_range(0, len); - editor->redraw(); - - free(text); - free(style); -} - -/** - Find the right indentation depth after pressing the Enter key. - \param[in] e pointer back to the code editor - */ -int CodeEditor::auto_indent(int, CodeEditor* e) { - if (e->buffer()->selected()) { - e->insert_position(e->buffer()->primary_selection()->start()); - e->buffer()->remove_selection(); - } - - int pos = e->insert_position(); - int start = e->line_start(pos); - char *text = e->buffer()->text_range(start, pos); - char *ptr; - - for (ptr = text; isspace(*ptr); ptr ++) {/*empty*/} - *ptr = '\0'; - if (*text) { - // use only a single 'insert' call to avoid redraw issues - size_t n = strlen(text); - char *b = (char*)malloc(n+2); - *b = '\n'; - strcpy(b+1, text); - e->insert(b); - free(b); - } else { - e->insert("\n"); - } - e->show_insert_position(); - e->set_changed(); - if (e->when()&FL_WHEN_CHANGED) e->do_callback(FL_REASON_CHANGED); - - free(text); - - return 1; -} - -/** - Create a CodeEditor widget. - \param[in] X, Y, W, H position and size of the widget - \param[in] L optional label - */ -CodeEditor::CodeEditor(int X, int Y, int W, int H, const char *L) : - Fl_Text_Editor(X, Y, W, H, L) { - buffer(new Fl_Text_Buffer); - - char *style = new char[mBuffer->length() + 1]; - char *text = mBuffer->text(); - - memset(style, 'A', mBuffer->length()); - style[mBuffer->length()] = '\0'; - - highlight_data(new Fl_Text_Buffer(mBuffer->length()), styletable, - sizeof(styletable) / sizeof(styletable[0]), - 'A', style_unfinished_cb, this); - - style_parse(text, style, mBuffer->length(), 'A'); - - mStyleBuffer->text(style); - delete[] style; - free(text); - - mBuffer->add_modify_callback(style_update, this); - add_key_binding(FL_Enter, FL_TEXT_EDITOR_ANY_STATE, - (Fl_Text_Editor::Key_Func)auto_indent); -} - -/** - Destroy a CodeEditor widget. - */ -CodeEditor::~CodeEditor() { - Fl_Text_Buffer *buf = mStyleBuffer; - mStyleBuffer = 0; - delete buf; - - buf = mBuffer; - buffer(0); - delete buf; -} - -/** - Attempt to make the fluid code editor widget honor textsize setting. - This works by updating the fontsizes in the style table. - \param[in] s the new general height of the text font - */ -void CodeEditor::textsize(Fl_Fontsize s) { - Fl_Text_Editor::textsize(s); // call base class method - // now attempt to update our styletable to honor the new size... - int entries = sizeof(styletable) / sizeof(styletable[0]); - for(int iter = 0; iter < entries; iter++) { - styletable[iter].size = s; - } -} // textsize - -// ---- CodeViewer implementation - -/** - Create a CodeViewer widget. - \param[in] X, Y, W, H position and size of the widget - \param[in] L optional label - */ -CodeViewer::CodeViewer(int X, int Y, int W, int H, const char *L) -: CodeEditor(X, Y, W, H, L) -{ - default_key_function(kf_ignore); - remove_all_key_bindings(&key_bindings); - cursor_style(CARET_CURSOR); -} - -/** - Tricking Fl_Text_Display into using bearable colors for this specific task. - */ -void CodeViewer::draw() -{ - Fl_Color c = Fl::get_color(FL_SELECTION_COLOR); - Fl::set_color(FL_SELECTION_COLOR, fl_color_average(FL_BACKGROUND_COLOR, FL_FOREGROUND_COLOR, 0.9f)); - CodeEditor::draw(); - Fl::set_color(FL_SELECTION_COLOR, c); -} - -// ---- TextViewer implementation - -/** - Create a TextViewer widget. - \param[in] X, Y, W, H position and size of the widget - \param[in] L optional label - */ -TextViewer::TextViewer(int X, int Y, int W, int H, const char *L) -: Fl_Text_Display(X, Y, W, H, L) -{ - buffer(new Fl_Text_Buffer); -} - -/** - Avoid memory leaks. - */ -TextViewer::~TextViewer() { - Fl_Text_Buffer *buf = mBuffer; - buffer(0); - delete buf; -} - -/** - Tricking Fl_Text_Display into using bearable colors for this specific task. - */ -void TextViewer::draw() -{ - Fl_Color c = Fl::get_color(FL_SELECTION_COLOR); - Fl::set_color(FL_SELECTION_COLOR, fl_color_average(FL_BACKGROUND_COLOR, FL_FOREGROUND_COLOR, 0.9f)); - Fl_Text_Display::draw(); - Fl::set_color(FL_SELECTION_COLOR, c); -} - - diff --git a/fluid/widgets/CodeEditor.h b/fluid/widgets/CodeEditor.h deleted file mode 100644 index cc720d618..000000000 --- a/fluid/widgets/CodeEditor.h +++ /dev/null @@ -1,105 +0,0 @@ -// -// Code editor widget for the Fast Light Tool Kit (FLTK). -// Syntax highlighting rewritten by erco@seriss.com 09/15/20. -// -// Copyright 1998-2025 by Bill Spitzak and others. -// -// 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 CodeEditor_h -#define CodeEditor_h - -// -// Include necessary headers... -// - -#include "StyleParse.h" - -#include -#include -#include - -#include -#include -#include -#include - -// ---- CodeEditor declaration - -/** - A widget derived from Fl_Text_Editor that implements C++ code highlighting. - - CodeEditor is used in Fluid whenever the user can edit C++ source - code or header text. - */ -class CodeEditor : public Fl_Text_Editor { - friend class StyleParse; - - static Fl_Text_Display::Style_Table_Entry styletable[]; - static void style_parse(const char *tbuff, char *sbuff, int len, char style); - static void style_unfinished_cb(int, void*); - static void style_update(int pos, int nInserted, int nDeleted, - int /*nRestyled*/, const char * /*deletedText*/, - void *cbArg); - static int auto_indent(int, CodeEditor* e); - -public: - CodeEditor(int X, int Y, int W, int H, const char *L=0); - ~CodeEditor(); - void textsize(Fl_Fontsize s); - - /// access to protected member get_absolute_top_line_number() - int top_line() { return get_absolute_top_line_number(); } - - /// access to protected member mTopLineNum - int scroll_row() { return mTopLineNum; } - - /// access to protected member mHorizOffset - int scroll_col() { return mHorizOffset; } -}; - -// ---- CodeViewer declaration - -/** - A widget derived from CodeEditor with highlighting for code blocks. - - This widget is used by the codeview system to show the design's - source and header code. The secondary highlighting show the text - part that corresponds to the selected widget(s). - */ -class CodeViewer : public CodeEditor { -public: - CodeViewer(int X, int Y, int W, int H, const char *L=0); - -protected: - void draw() FL_OVERRIDE; - - /// Limit event handling to viewing, not editing - int handle(int ev) FL_OVERRIDE { return Fl_Text_Display::handle(ev); } -}; - -// ---- Project File Text Viewer declaration - -/** - A text viewer with an additional highlighting color scheme. - */ -class TextViewer : public Fl_Text_Display { -public: - TextViewer(int X, int Y, int W, int H, const char *L=0); - ~TextViewer(); - void draw() FL_OVERRIDE; - - /// access to protected member get_absolute_top_line_number() - int top_line() { return get_absolute_top_line_number(); } -}; - -#endif // !CodeEditor_h diff --git a/fluid/widgets/Code_Editor.cxx b/fluid/widgets/Code_Editor.cxx new file mode 100644 index 000000000..a145b5055 --- /dev/null +++ b/fluid/widgets/Code_Editor.cxx @@ -0,0 +1,254 @@ +// +// Code editor widget for the Fast Light Tool Kit (FLTK). +// Syntax highlighting rewritten by erco@seriss.com 09/15/20. +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// 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 necessary headers... +// + +#include "widgets/Code_Editor.h" + +using namespace fld; +using namespace fld::widget; + +// ---- Code_Editor implementation + +/** + Lookup table for all supported styles. + Every table entry describes a rendering style for the corresponding text. + */ +Fl_Text_Display::Style_Table_Entry Code_Editor::styletable[] = { // Style table + { FL_FOREGROUND_COLOR, FL_COURIER, 11 }, // A - Plain + { FL_DARK_GREEN, FL_COURIER_ITALIC, 11 }, // B - Line comments + { FL_DARK_GREEN, FL_COURIER_ITALIC, 11 }, // C - Block comments + { FL_BLUE, FL_COURIER, 11 }, // D - Strings + { FL_DARK_RED, FL_COURIER, 11 }, // E - Directives + { FL_DARK_RED, FL_COURIER_BOLD, 11 }, // F - Types + { FL_BLUE, FL_COURIER_BOLD, 11 }, // G - Keywords + { 220, /* med cyan */ FL_COURIER, 11 } // H - Single quote chars + }; + +/** + Parse text and produce style data. + \param[in] in_tbuff text buffer to parse + \param[inout] in_sbuff style buffer we modify + \param[in] in_len byte length to parse + \param[in] in_style starting style letter + */ +void Code_Editor::style_parse(const char *in_tbuff, // text buffer to parse + char *in_sbuff, // style buffer we modify + int in_len, // byte length to parse + char in_style) { // starting style letter + // Style letters: + // + // 'A' - Plain + // 'B' - Line comments // .. + // 'C' - Block comments /*..*/ + // 'D' - Strings "xxx" + // 'E' - Directives #define, #include.. + // 'F' - Types void, char.. + // 'G' - Keywords if, while.. + // 'H' - Chars 'x' + + Style_Parser sp; + sp.tbuff = in_tbuff; + sp.sbuff = in_sbuff; + sp.len = in_len; + sp.style = in_style; + sp.lwhite = 1; // 1:while parsing over leading white and first char past, 0:past white + sp.col = 0; + sp.last = 0; + + // Loop through the code, updating style buffer + char c; + while ( sp.len > 0 ) { + c = sp.tbuff[0]; // current char + if ( sp.style == 'C' ) { // Started in middle of comment block? + if ( !sp.parse_block_comment() ) break; + } else if ( strncmp(sp.tbuff, "/*", 2)==0 ) { // C style comment block? + if ( !sp.parse_block_comment() ) break; + } else if ( c == '\\' ) { // Backslash escape char? + if ( !sp.parse_escape() ) break; + } else if ( strncmp(sp.tbuff, "//", 2)==0 ) { // Line comment? + if ( !sp.parse_line_comment() ) break; + } else if ( c == '"' ) { // Start of double quoted string? + if ( !sp.parse_quoted_string('"', 'D') ) break; + } else if ( c == '\'' ) { // Start of single quoted string? + if ( !sp.parse_quoted_string('\'', 'H') ) break; + } else if ( c == '#' && sp.lwhite ) { // Start of '#' directive? + if ( !sp.parse_directive() ) break; + } else if ( !sp.last && (islower(c) || c == '_') ) { // Possible C/C++ keyword? + if ( !sp.parse_keyword() ) break; + } else { // All other chars? + if ( !sp.parse_all_else() ) break; + } + } +} + +/** + Update unfinished styles. + */ +void Code_Editor::style_unfinished_cb(int, void*) { +} + +/** + Update the style buffer. + \param[in] pos insert position in text + \param[in] nInserted number of bytes inserted + \param[in] nDeleted number of bytes deleted + \param[in] cbArg pointer back to the code editor + */ +void Code_Editor::style_update(int pos, int nInserted, int nDeleted, + int /*nRestyled*/, const char * /*deletedText*/, + void *cbArg) { + Code_Editor *editor = (Code_Editor*)cbArg; + char *style, // Style data + *text; // Text data + + + // If this is just a selection change, just unselect the style buffer... + if (nInserted == 0 && nDeleted == 0) { + editor->mStyleBuffer->unselect(); + return; + } + + // Track changes in the text buffer... + if (nInserted > 0) { + // Insert characters into the style buffer... + style = new char[nInserted + 1]; + memset(style, 'A', nInserted); + style[nInserted] = '\0'; + + editor->mStyleBuffer->replace(pos, pos + nDeleted, style); + delete[] style; + } else { + // Just delete characters in the style buffer... + editor->mStyleBuffer->remove(pos, pos + nDeleted); + } + + // Select the area that was just updated to avoid unnecessary + // callbacks... + editor->mStyleBuffer->select(pos, pos + nInserted - nDeleted); + + // Reparse whole buffer, don't get cute. Maybe optimize range later + int len = editor->buffer()->length(); + text = editor->mBuffer->text_range(0, len); + style = editor->mStyleBuffer->text_range(0, len); + + style_parse(text, style, editor->mBuffer->length(), 'A'); + + editor->mStyleBuffer->replace(0, len, style); + editor->redisplay_range(0, len); + editor->redraw(); + + free(text); + free(style); +} + +/** + Find the right indentation depth after pressing the Enter key. + \param[in] e pointer back to the code editor + */ +int Code_Editor::auto_indent(int, Code_Editor* e) { + if (e->buffer()->selected()) { + e->insert_position(e->buffer()->primary_selection()->start()); + e->buffer()->remove_selection(); + } + + int pos = e->insert_position(); + int start = e->line_start(pos); + char *text = e->buffer()->text_range(start, pos); + char *ptr; + + for (ptr = text; isspace(*ptr); ptr ++) {/*empty*/} + *ptr = '\0'; + if (*text) { + // use only a single 'insert' call to avoid redraw issues + size_t n = strlen(text); + char *b = (char*)malloc(n+2); + *b = '\n'; + strcpy(b+1, text); + e->insert(b); + free(b); + } else { + e->insert("\n"); + } + e->show_insert_position(); + e->set_changed(); + if (e->when()&FL_WHEN_CHANGED) e->do_callback(FL_REASON_CHANGED); + + free(text); + + return 1; +} + +/** + Create a Code_Editor widget. + \param[in] X, Y, W, H position and size of the widget + \param[in] L optional label + */ +Code_Editor::Code_Editor(int X, int Y, int W, int H, const char *L) : + Fl_Text_Editor(X, Y, W, H, L) { + buffer(new Fl_Text_Buffer); + + char *style = new char[mBuffer->length() + 1]; + char *text = mBuffer->text(); + + memset(style, 'A', mBuffer->length()); + style[mBuffer->length()] = '\0'; + + highlight_data(new Fl_Text_Buffer(mBuffer->length()), styletable, + sizeof(styletable) / sizeof(styletable[0]), + 'A', style_unfinished_cb, this); + + style_parse(text, style, mBuffer->length(), 'A'); + + mStyleBuffer->text(style); + delete[] style; + free(text); + + mBuffer->add_modify_callback(style_update, this); + add_key_binding(FL_Enter, FL_TEXT_EDITOR_ANY_STATE, + (Fl_Text_Editor::Key_Func)auto_indent); +} + +/** + Destroy a Code_Editor widget. + */ +Code_Editor::~Code_Editor() { + Fl_Text_Buffer *buf = mStyleBuffer; + mStyleBuffer = 0; + delete buf; + + buf = mBuffer; + buffer(0); + delete buf; +} + +/** + Attempt to make the fluid code editor widget honor textsize setting. + This works by updating the fontsizes in the style table. + \param[in] s the new general height of the text font + */ +void Code_Editor::textsize(Fl_Fontsize s) { + Fl_Text_Editor::textsize(s); // call base class method + // now attempt to update our styletable to honor the new size... + int entries = sizeof(styletable) / sizeof(styletable[0]); + for(int iter = 0; iter < entries; iter++) { + styletable[iter].size = s; + } +} // textsize + diff --git a/fluid/widgets/Code_Editor.h b/fluid/widgets/Code_Editor.h new file mode 100644 index 000000000..ef8df0e0d --- /dev/null +++ b/fluid/widgets/Code_Editor.h @@ -0,0 +1,70 @@ +// +// Code editor widget for the Fast Light Tool Kit (FLTK). +// Syntax highlighting rewritten by erco@seriss.com 09/15/20. +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// 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_WIDGETS_CODE_EDITOR_H +#define FLUID_WIDGETS_CODE_EDITOR_H + +// +// Include necessary headers... +// + +#include "Style_Parser.h" + +#include + +namespace fld { +namespace widget { + +// ---- Code_Editor declaration + +/** + A widget derived from Fl_Text_Editor that implements C++ code highlighting. + + Code_Editor is used in Fluid whenever the user can edit C++ source + code or header text. + */ +class Code_Editor : public Fl_Text_Editor { + friend class Style_Parser; + + static Fl_Text_Display::Style_Table_Entry styletable[]; + static void style_parse(const char *tbuff, char *sbuff, int len, char style); + static void style_unfinished_cb(int, void*); + static void style_update(int pos, int nInserted, int nDeleted, + int /*nRestyled*/, const char * /*deletedText*/, + void *cbArg); + static int auto_indent(int, Code_Editor* e); + +public: + Code_Editor(int X, int Y, int W, int H, const char *L=0); + ~Code_Editor(); + void textsize(Fl_Fontsize s); + + /// access to protected member get_absolute_top_line_number() + int top_line() { return get_absolute_top_line_number(); } + + /// access to protected member mTopLineNum + int scroll_row() { return mTopLineNum; } + + /// access to protected member mHorizOffset + int scroll_col() { return mHorizOffset; } +}; + +} // namespace widget +} // namespace fld + + +#endif // FLUID_WIDGETS_CODE_EDITOR_H diff --git a/fluid/widgets/Code_Viewer.cxx b/fluid/widgets/Code_Viewer.cxx new file mode 100644 index 000000000..82a71f4f6 --- /dev/null +++ b/fluid/widgets/Code_Viewer.cxx @@ -0,0 +1,51 @@ +// +// Code editor widget for the Fast Light Tool Kit (FLTK). +// Syntax highlighting rewritten by erco@seriss.com 09/15/20. +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// 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 necessary headers... +// + +#include "widgets/Code_Viewer.h" + +using namespace fld; +using namespace fld::widget; + +/** + Create a fld::widget::Code_Viewer widget. + \param[in] X, Y, W, H position and size of the widget + \param[in] L optional label + */ +Code_Viewer::Code_Viewer(int X, int Y, int W, int H, const char *L) +: Code_Editor(X, Y, W, H, L) +{ + default_key_function(kf_ignore); + remove_all_key_bindings(&key_bindings); + cursor_style(CARET_CURSOR); +} + +/** + Tricking Fl_Text_Display into using bearable colors for this specific task. + */ +void Code_Viewer::draw() +{ + Fl_Color c = Fl::get_color(FL_SELECTION_COLOR); + Fl::set_color(FL_SELECTION_COLOR, fl_color_average(FL_BACKGROUND_COLOR, FL_FOREGROUND_COLOR, 0.9f)); + Code_Editor::draw(); + Fl::set_color(FL_SELECTION_COLOR, c); +} + + diff --git a/fluid/widgets/Code_Viewer.h b/fluid/widgets/Code_Viewer.h new file mode 100644 index 000000000..5bf8b870a --- /dev/null +++ b/fluid/widgets/Code_Viewer.h @@ -0,0 +1,51 @@ +// +// Code editor widget for the Fast Light Tool Kit (FLTK). +// Syntax highlighting rewritten by erco@seriss.com 09/15/20. +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// 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_WIDGETS_CODE_VIEWER_H +#define FLUID_WIDGETS_CODE_VIEWER_H + +// +// Include necessary headers... +// + +#include "widgets/Code_Editor.h" + +namespace fld { +namespace widget { + +/** + A widget derived from Code_Editor with highlighting for code blocks. + + This widget is used by the codeview system to show the design's + source and header code. The secondary highlighting show the text + part that corresponds to the selected widget(s). + */ +class Code_Viewer : public Code_Editor { +public: + Code_Viewer(int X, int Y, int W, int H, const char *L = nullptr); + +protected: + void draw() override; + + /// Limit event handling to viewing, not editing + int handle(int ev) override { return Fl_Text_Display::handle(ev); } +}; + +} // namespace widget +} // namespace fld + +#endif // FLUID_WIDGETS_CODE_VIEWER_H diff --git a/fluid/widgets/Formula_Input.cxx b/fluid/widgets/Formula_Input.cxx new file mode 100644 index 000000000..b26bd83e3 --- /dev/null +++ b/fluid/widgets/Formula_Input.cxx @@ -0,0 +1,217 @@ +// +// 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 "widgets/Formula_Input.h" + +#include +#include "../src/flstring.h" + +#include +#include +#include + +using namespace fld; +using namespace fld::widget; + +/** \class fld::widget::Formula_Input + The Formula_Input widget is an input field for entering widget coordinates + and sizes. It includes basic math capabilities and allows the use of variables + in formulas. This widget is useful for specifying precise positions and + dimensions for widgets in a graphical user interface. + */ + +/** + Create an input field. + */ +Formula_Input::Formula_Input(int x, int y, int w, int h, const char *l) +: Fl_Input(x, y, w, h, l) +{ + Fl_Input::callback((Fl_Callback*)callback_handler_cb); + text("0"); +} + +void Formula_Input::callback_handler_cb(Formula_Input *This, void *v) { + This->callback_handler(v); +} + +void Formula_Input::callback_handler(void *v) { + if (user_callback_) + (*user_callback_)(this, v); + // do *not* update the value to show the evaluated formula here, because the + // values of the variables have already updated after the user callback. +} + +/** + \brief Get the value of a variable. + Collects all consecutive ASCII letters into a variable name, scans the + Variable list for that name, and then calls the corresponding callback from + the Variable array. + \param s points to the first character of the variable name, must point after + the last character of the variable name when returning. + \return the integer value that was found or calculated + */ +int Formula_Input::eval_var(uchar *&s) const { + if (!vars_) + return 0; + // find the end of the variable name + uchar *v = s; + while (isalpha(*s)) s++; + int n = (int)(s-v); + // find the variable in the list + for (Formula_Input_Vars *vars = vars_; vars->name_; vars++) { + if (strncmp((char*)v, vars->name_, n)==0 && vars->name_[n]==0) + return vars->callback_(this, vars_user_data_); + } + return 0; +} + +/** + Evaluate a formula into an integer, recursive part. + \param s remaining text in this formula, must return a pointer to the next + character that will be interpreted. + \param prio priority of current operation + \return the value so far + */ +int Formula_Input::eval(uchar *&s, int prio) const { + int v = 0, sgn = 1; + uchar c = *s++; + + // check for end of text + if (c==0) { s--; return sgn*v; } + + // check for unary operator + if (c=='-') { sgn = -1; c = *s++; } + else if (c=='+') { sgn = 1; c = *s++; } + + // read value, variable, or bracketed term + if (c==0) { + s--; return sgn*v; + } else if (c>='0' && c<='9') { + // numeric value + while (c>='0' && c<='9') { + v = v*10 + (c-'0'); + c = *s++; + } + } else if (isalpha(c)) { + v = eval_var(--s); + c = *s++; + } else if (c=='(') { + // opening bracket + v = eval(s, 5); + } else { + return sgn*v; // syntax error + } + if (sgn==-1) v = -v; + + // Now evaluate all following binary operators + for (;;) { + if (c==0) { + s--; + return v; + } else if (c=='+' || c=='-') { + if (prio<=4) { s--; return v; } + if (c=='+') { v += eval(s, 4); } + else if (c=='-') { v -= eval(s, 4); } + } else if (c=='*' || c=='/') { + if (prio<=3) { s--; return v; } + if (c=='*') { v *= eval(s, 3); } + else if (c=='/') { + int x = eval(s, 3); + if (x!=0) // if x is zero, don't divide + v /= x; + } + } else if (c==')') { + return v; + } else { + return v; // syntax error + } + c = *s++; + } + return v; +} + +/** + Evaluate a formula into an integer. + + The Formula_Input widget includes a formula interpreter that allows you + to evaluate a string containing a mathematical formula and obtain the result + as an integer. The interpreter supports unary plus and minus, basic integer + math operations (such as addition, subtraction, multiplication, and division), + and brackets. It also allows you to define a list of variables by name and use + them in the formula. The interpreter does not perform error checking, so it is + assumed that the formula is entered correctly. + + \param s formula as a C string + \return the calculated value + */ +int Formula_Input::eval(const char *s) const +{ + // duplicate the text, so we can modify it + uchar *buf = (uchar*)fl_strdup(s); + uchar *src = buf, *dst = buf; + // remove all whitespace to make the parser easier + for (;;) { + uchar c = *src++; + if (c==' ' || c=='\t') continue; + *dst++ = c; + if (c==0) break; + } + src = buf; + // now jump into the recursion + int ret = eval(src, 5); + ::free(buf); + return ret; +} + +/** + Evaluate the formula and return the result. + */ +int Formula_Input::value() const { + return eval(text()); +} + +/** + Set the field to an integer value, replacing previous texts. + */ +void Formula_Input::value(int v) { + char buf[32]; + fl_snprintf(buf, sizeof(buf), "%d", v); + text(buf); +} + +/** + Allow vertical mouse dragging and mouse wheel to interactively change the value. + */ +int Formula_Input::handle(int event) { + switch (event) { + case FL_MOUSEWHEEL: + if (Fl::event_dy()) { + value( value() - Fl::event_dy() ); + set_changed(); + do_callback(FL_REASON_CHANGED); + } + return 1; + } + return Fl_Input::handle(event); +} + +/** Set the list of the available variables + \param vars array of variables, last entry `has name_` set to `NULL` + \param user_data is forwarded to the Variable callback */ +void Formula_Input::variables(Formula_Input_Vars *vars, void *user_data) { + vars_ = vars; + vars_user_data_ = user_data; +} diff --git a/fluid/widgets/Formula_Input.h b/fluid/widgets/Formula_Input.h new file mode 100644 index 000000000..5307cdc88 --- /dev/null +++ b/fluid/widgets/Formula_Input.h @@ -0,0 +1,73 @@ +// +// Formula_Input widget 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_WIDGETS_FORMULA_INPUT_H +#define FLUID_WIDGETS_FORMULA_INPUT_H + +#include + +namespace fld { +namespace widget { + +class Formula_Input; + +// Callback signature for function returning the value of a variable. +typedef int (Fluid_Coord_Callback)(Formula_Input const *, void*); + +// Entry for a list of variables available to an input field. +// Formula_Input::variables() expects an array of +// Formula_Input_Vars with the last entry's name_ set to NULL. +typedef struct Formula_Input_Vars { + const char *name_; + Fluid_Coord_Callback *callback_; +} Formula_Input_Vars; + +// A text input widget that understands simple math. +class Formula_Input : public Fl_Input +{ + Fl_Callback *user_callback_ { nullptr }; + Formula_Input_Vars *vars_ { nullptr }; + void *vars_user_data_ { nullptr }; + + static void callback_handler_cb(Formula_Input *This, void *v); + void callback_handler(void *v); + int eval_var(uchar *&s) const; + int eval(uchar *&s, int prio) const; + int eval(const char *s) const; + +public: + Formula_Input(int x, int y, int w, int h, const char *l = nullptr); + + /** Return the text in the widget text field. */ + const char *text() const { return Fl_Input::value(); } + + /** Set the text in the text field */ + void text(const char *v) { Fl_Input::value(v); } + + int value() const; + void value(int v); + + /** Set the general callback for this widget. */ + void callback(Fl_Callback *cb) { user_callback_ = cb; } + + void variables(fld::widget::Formula_Input_Vars *vars, void *user_data); + int handle(int) override; +}; + +} // namespace widget +} // namespace fld + +#endif // FLUID_WIDGETS_FORMULA_INPUT_H diff --git a/fluid/widgets/StyleParse.cxx b/fluid/widgets/StyleParse.cxx deleted file mode 100644 index b8b8ff4f0..000000000 --- a/fluid/widgets/StyleParse.cxx +++ /dev/null @@ -1,328 +0,0 @@ -// -// Syntax highlighting for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2020 by Bill Spitzak and others. -// Copyright 2020 Greg Ercolano. -// -// 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 "StyleParse.h" - -#include -#include -#include -#include // bsearch() - -// Sorted list of C/C++ keywords... -static const char * const code_keywords[] = { - "and", - "and_eq", - "asm", - "bitand", - "bitor", - "break", - "case", - "catch", - "compl", - "continue", - "default", - "delete", - "do", - "else", - "false", - "for", - "goto", - "if", - "new", - "not", - "not_eq", - "operator", - "or", - "or_eq", - "return", - "switch", - "template", - "this", - "throw", - "true", - "try", - "while", - "xor", - "xor_eq" -}; - -// Sorted list of C/C++ types... -static const char * const code_types[] = { - "auto", - "bool", - "char", - "class", - "const", - "const_cast", - "double", - "dynamic_cast", - "enum", - "explicit", - "extern", - "float", - "friend", - "inline", - "int", - "long", - "mutable", - "namespace", - "private", - "protected", - "public", - "register", - "short", - "signed", - "sizeof", - "static", - "static_cast", - "struct", - "template", - "typedef", - "typename", - "union", - "unsigned", - "virtual", - "void", - "volatile" -}; - -// 'compare_keywords()' - Compare two keywords... -extern "C" { - static int compare_keywords(const void *a, const void *b) { - return strcmp(*((const char **)a), *((const char **)b)); - } -} - -// See if 'find' is a C/C++ keyword. -// Refer to bsearch(3) for return value. -// -static void* search_keywords(char *find) { - return bsearch(&find, code_keywords, - sizeof(code_keywords) / sizeof(code_keywords[0]), - sizeof(code_keywords[0]), compare_keywords); -} - -// See if 'find' is a C/C++ type. -// Refer to bsearch(3) for return value. -// -static void* search_types(char *find) { - return bsearch(&find, code_types, - sizeof(code_types) / sizeof(code_types[0]), - sizeof(code_types[0]), compare_keywords); -} - -// Handle style parsing over a character -// Handles updating col counter when \n encountered. -// Applies the current style, advances to next text + style char. -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_over_char(int handle_crlf) { - char c = *tbuff; - - // End of line? - if ( handle_crlf ) { - if ( c == '\n' ) { - lwhite = 1; // restart leading white flag - } else { - // End of leading white? (used by #directive) - if ( !strchr(" \t", c) ) lwhite = 0; - } - } - - // Adjust and advance - // If handling crlfs, zero col on crlf. If not handling, let col continue to count past crlf - // e.g. for multiline #define's that have lines ending in backslashes. - // - col = (c=='\n') ? (handle_crlf ? 0 : col) : col+1; // column counter - tbuff++; // advance text ptr - *sbuff++ = style; // apply style & advance its ptr - if ( --len <= 0 ) return 0; // keep track of length - return 1; -} - -// Parse over white space using current style -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_over_white() { - while ( len > 0 && strchr(" \t", *tbuff)) - { if ( !parse_over_char() ) return 0; } - return 1; -} - -// Parse over non-white alphabetic text -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_over_alpha() { - while ( len > 0 && isalpha(*tbuff) ) - { if ( !parse_over_char() ) return 0; } - return 1; -} - -// Parse to end of line in specified style. -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_to_eol(char s) { - char save = style; - style = s; - while ( *tbuff != '\n' ) - { if ( !parse_over_char() ) return 0; } - style = save; - return 1; -} - -// Parse a block comment until end of comment or buffer. -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_block_comment() { - char save = style; - style = 'C'; // block comment style - while ( len > 0 ) { - if ( strncmp(tbuff, "*/", 2) == 0 ) { - if ( !parse_over_char() ) return 0; // handle '*' - if ( !parse_over_char() ) return 0; // handle '/' - break; - } - if ( !parse_over_char() ) return 0; // handle comment text - } - style = save; // revert style - return 1; -} - -// Copy keyword from tbuff -> keyword[] buffer -void StyleParse::buffer_keyword() { - char *key = keyword; - char *kend = key + sizeof(keyword) - 1; // end of buffer - for ( const char *s=tbuff; - (islower(*s) || *s=='_') && (key < kend); - *key++ = *s++ ) { } - *key = 0; // terminate -} - -// Parse over specified 'key'word in specified style 's'. -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_over_key(const char *key, char s) { - char save = style; - style = s; - // Parse over the keyword while applying style to sbuff - while ( *key++ ) - { if ( !parse_over_char() ) return 0; } - last = 1; - style = save; - return 1; -} - -// Parse over angle brackets <..> in specified style. -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_over_angles(char s) { - if ( *tbuff != '<' ) return 1; // not <..>, early exit - char save = style; - style = s; - // Parse over angle brackets in specified style - while ( len > 0 && *tbuff != '>' ) - { if ( !parse_over_char() ) return 0; } // parse over '<' and angle content - if ( !parse_over_char() ) return 0; // parse over trailing '>' - style = save; - return 1; -} - -// Parse line for possible keyword -// spi.keyword[] will contain parsed word. -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_keyword() { - // Parse into 'keyword' buffer - buffer_keyword(); - char *key = keyword; - // C/C++ type? (void, char..) - if ( search_types(key) ) - return parse_over_key(key, 'F'); // 'type' style - // C/C++ Keyword? (switch, return..) - else if ( search_keywords(key) ) - return parse_over_key(key, 'G'); // 'keyword' style - // Not a type or keyword? Parse over it - return parse_over_key(key, style); -} - -// Style parse a quoted string, either "" or ''. -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_quoted_string(char quote_char, // e.g. '"' or '\'' - char in_style) { // style for quoted text - style = in_style; // start string style - if ( !parse_over_char() ) return 0; // parse over opening quote - - // Parse until closing quote reached - char c; - while ( len > 0 ) { - c = tbuff[0]; - if ( c == quote_char ) { // Closing quote? Parse and done - if ( !parse_over_char() ) return 0; // close quote - break; - } else if ( c == '\\' ) { // Escape sequence? Parse over, continue - if ( !parse_over_char() ) return 0; // escape - if ( !parse_over_char() ) return 0; // char being escaped - continue; - } - // Keep parsing until end of buffer or closing quote.. - if ( !parse_over_char() ) return 0; - } - style = 'A'; // revert normal style - return 1; -} - -// Style parse a directive (#include, #define..) -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_directive() { - style = 'E'; // start directive style - if ( !parse_over_char() ) return 0; // Parse over '#' - if ( !parse_over_white() ) return 0; // Parse over any whitespace after '#' - if ( !parse_over_alpha() ) return 0; // Parse over the directive - style = 'A'; // revert normal style - if ( !parse_over_white() ) return 0; // Parse over white after directive - if ( !parse_over_angles('D')) return 0; // #include <..> (if any) - return 1; -} - -// Style parse a line comment to end of line. -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_line_comment() { - return parse_to_eol('B'); -} - -// Parse a backslash escape character sequence. -// Purposefully don't 'handle' \n, since an escaped \n should be -// a continuation of a line, such as in a multiline #directive. -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_escape() { - const char no_crlf = 0; - if ( !parse_over_char(no_crlf) ) return 0; // backslash - if ( !parse_over_char(no_crlf) ) return 0; // char escaped - return 1; -} - -// Parse all other non-specific characters -// Returns 0 if hit end of buffer, 1 otherwise. -// -int StyleParse::parse_all_else() { - last = isalnum(*tbuff) || *tbuff == '_' || *tbuff == '.'; - return parse_over_char(); -} diff --git a/fluid/widgets/StyleParse.h b/fluid/widgets/StyleParse.h deleted file mode 100644 index 2fcc4f4db..000000000 --- a/fluid/widgets/StyleParse.h +++ /dev/null @@ -1,61 +0,0 @@ -// -// Syntax highlighting for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2020 by Bill Spitzak and others. -// Copyright 2020 Greg Ercolano. -// -// 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 StyleParse_h -#define StyleParse_h - -// Class to manage style parsing, friend of CodeEditor -class StyleParse { -public: - const char *tbuff; // text buffer - char *sbuff; // style buffer - int len; // running length - char style; // current style - char lwhite; // leading white space (1=white, 0=past white) - int col; // line's column counter - char keyword[40]; // keyword parsing buffer - char last; // flag for keyword parsing - - StyleParse() { - tbuff = 0; - sbuff = 0; - len = 0; - style = 0; - lwhite = 1; - col = 0; - last = 0; - } - - // Methods to aid in parsing - int parse_over_char(int handle_crlf=1); - int parse_over_white(); - int parse_over_alpha(); - int parse_to_eol(char s); - int parse_block_comment(); // "/* text.. */" - void buffer_keyword(); - int parse_over_key(const char *key, char s); - int parse_over_angles(char s); - int parse_keyword(); // "switch" - int parse_quoted_string(char quote_char, char in_style); - // "hello", 'x' - int parse_directive(); // "#define" - int parse_line_comment(); // "// text.." - int parse_escape(); // "\'" - int parse_all_else(); // all other code -}; - -#endif // StyleParse_h diff --git a/fluid/widgets/Style_Parser.cxx b/fluid/widgets/Style_Parser.cxx new file mode 100644 index 000000000..9929cc01a --- /dev/null +++ b/fluid/widgets/Style_Parser.cxx @@ -0,0 +1,331 @@ +// +// Syntax highlighting for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// Copyright 2020 Greg Ercolano. +// +// 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 "Style_Parser.h" + +#include +#include +#include +#include // bsearch() + +using namespace fld; +using namespace fld::widget; + +// Sorted list of C/C++ keywords... +static const char * const code_keywords[] = { + "and", + "and_eq", + "asm", + "bitand", + "bitor", + "break", + "case", + "catch", + "compl", + "continue", + "default", + "delete", + "do", + "else", + "false", + "for", + "goto", + "if", + "new", + "not", + "not_eq", + "operator", + "or", + "or_eq", + "return", + "switch", + "template", + "this", + "throw", + "true", + "try", + "while", + "xor", + "xor_eq" +}; + +// Sorted list of C/C++ types... +static const char * const code_types[] = { + "auto", + "bool", + "char", + "class", + "const", + "const_cast", + "double", + "dynamic_cast", + "enum", + "explicit", + "extern", + "float", + "friend", + "inline", + "int", + "long", + "mutable", + "namespace", + "private", + "protected", + "public", + "register", + "short", + "signed", + "sizeof", + "static", + "static_cast", + "struct", + "template", + "typedef", + "typename", + "union", + "unsigned", + "virtual", + "void", + "volatile" +}; + +// 'compare_keywords()' - Compare two keywords... +extern "C" { + static int compare_keywords(const void *a, const void *b) { + return strcmp(*((const char **)a), *((const char **)b)); + } +} + +// See if 'find' is a C/C++ keyword. +// Refer to bsearch(3) for return value. +// +static void* search_keywords(char *find) { + return bsearch(&find, code_keywords, + sizeof(code_keywords) / sizeof(code_keywords[0]), + sizeof(code_keywords[0]), compare_keywords); +} + +// See if 'find' is a C/C++ type. +// Refer to bsearch(3) for return value. +// +static void* search_types(char *find) { + return bsearch(&find, code_types, + sizeof(code_types) / sizeof(code_types[0]), + sizeof(code_types[0]), compare_keywords); +} + +// Handle style parsing over a character +// Handles updating col counter when \n encountered. +// Applies the current style, advances to next text + style char. +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_over_char(int handle_crlf) { + char c = *tbuff; + + // End of line? + if ( handle_crlf ) { + if ( c == '\n' ) { + lwhite = 1; // restart leading white flag + } else { + // End of leading white? (used by #directive) + if ( !strchr(" \t", c) ) lwhite = 0; + } + } + + // Adjust and advance + // If handling crlfs, zero col on crlf. If not handling, let col continue to count past crlf + // e.g. for multiline #define's that have lines ending in backslashes. + // + col = (c=='\n') ? (handle_crlf ? 0 : col) : col+1; // column counter + tbuff++; // advance text ptr + *sbuff++ = style; // apply style & advance its ptr + if ( --len <= 0 ) return 0; // keep track of length + return 1; +} + +// Parse over white space using current style +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_over_white() { + while ( len > 0 && strchr(" \t", *tbuff)) + { if ( !parse_over_char() ) return 0; } + return 1; +} + +// Parse over non-white alphabetic text +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_over_alpha() { + while ( len > 0 && isalpha(*tbuff) ) + { if ( !parse_over_char() ) return 0; } + return 1; +} + +// Parse to end of line in specified style. +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_to_eol(char s) { + char save = style; + style = s; + while ( *tbuff != '\n' ) + { if ( !parse_over_char() ) return 0; } + style = save; + return 1; +} + +// Parse a block comment until end of comment or buffer. +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_block_comment() { + char save = style; + style = 'C'; // block comment style + while ( len > 0 ) { + if ( strncmp(tbuff, "*/", 2) == 0 ) { + if ( !parse_over_char() ) return 0; // handle '*' + if ( !parse_over_char() ) return 0; // handle '/' + break; + } + if ( !parse_over_char() ) return 0; // handle comment text + } + style = save; // revert style + return 1; +} + +// Copy keyword from tbuff -> keyword[] buffer +void Style_Parser::buffer_keyword() { + char *key = keyword; + char *kend = key + sizeof(keyword) - 1; // end of buffer + for ( const char *s=tbuff; + (islower(*s) || *s=='_') && (key < kend); + *key++ = *s++ ) { } + *key = 0; // terminate +} + +// Parse over specified 'key'word in specified style 's'. +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_over_key(const char *key, char s) { + char save = style; + style = s; + // Parse over the keyword while applying style to sbuff + while ( *key++ ) + { if ( !parse_over_char() ) return 0; } + last = 1; + style = save; + return 1; +} + +// Parse over angle brackets <..> in specified style. +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_over_angles(char s) { + if ( *tbuff != '<' ) return 1; // not <..>, early exit + char save = style; + style = s; + // Parse over angle brackets in specified style + while ( len > 0 && *tbuff != '>' ) + { if ( !parse_over_char() ) return 0; } // parse over '<' and angle content + if ( !parse_over_char() ) return 0; // parse over trailing '>' + style = save; + return 1; +} + +// Parse line for possible keyword +// spi.keyword[] will contain parsed word. +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_keyword() { + // Parse into 'keyword' buffer + buffer_keyword(); + char *key = keyword; + // C/C++ type? (void, char..) + if ( search_types(key) ) + return parse_over_key(key, 'F'); // 'type' style + // C/C++ Keyword? (switch, return..) + else if ( search_keywords(key) ) + return parse_over_key(key, 'G'); // 'keyword' style + // Not a type or keyword? Parse over it + return parse_over_key(key, style); +} + +// Style parse a quoted string, either "" or ''. +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_quoted_string(char quote_char, // e.g. '"' or '\'' + char in_style) { // style for quoted text + style = in_style; // start string style + if ( !parse_over_char() ) return 0; // parse over opening quote + + // Parse until closing quote reached + char c; + while ( len > 0 ) { + c = tbuff[0]; + if ( c == quote_char ) { // Closing quote? Parse and done + if ( !parse_over_char() ) return 0; // close quote + break; + } else if ( c == '\\' ) { // Escape sequence? Parse over, continue + if ( !parse_over_char() ) return 0; // escape + if ( !parse_over_char() ) return 0; // char being escaped + continue; + } + // Keep parsing until end of buffer or closing quote.. + if ( !parse_over_char() ) return 0; + } + style = 'A'; // revert normal style + return 1; +} + +// Style parse a directive (#include, #define..) +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_directive() { + style = 'E'; // start directive style + if ( !parse_over_char() ) return 0; // Parse over '#' + if ( !parse_over_white() ) return 0; // Parse over any whitespace after '#' + if ( !parse_over_alpha() ) return 0; // Parse over the directive + style = 'A'; // revert normal style + if ( !parse_over_white() ) return 0; // Parse over white after directive + if ( !parse_over_angles('D')) return 0; // #include <..> (if any) + return 1; +} + +// Style parse a line comment to end of line. +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_line_comment() { + return parse_to_eol('B'); +} + +// Parse a backslash escape character sequence. +// Purposefully don't 'handle' \n, since an escaped \n should be +// a continuation of a line, such as in a multiline #directive. +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_escape() { + const char no_crlf = 0; + if ( !parse_over_char(no_crlf) ) return 0; // backslash + if ( !parse_over_char(no_crlf) ) return 0; // char escaped + return 1; +} + +// Parse all other non-specific characters +// Returns 0 if hit end of buffer, 1 otherwise. +// +int Style_Parser::parse_all_else() { + last = isalnum(*tbuff) || *tbuff == '_' || *tbuff == '.'; + return parse_over_char(); +} diff --git a/fluid/widgets/Style_Parser.h b/fluid/widgets/Style_Parser.h new file mode 100644 index 000000000..287ea6b21 --- /dev/null +++ b/fluid/widgets/Style_Parser.h @@ -0,0 +1,59 @@ +// +// Syntax highlighting for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2025 by Bill Spitzak and others. +// Copyright 2020 Greg Ercolano. +// +// 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_WIDGETS_STYLE_PARSER_H +#define FLUID_WIDGETS_STYLE_PARSER_H + +namespace fld { +namespace widget { + +// Class to manage style parsing, friend of Code_Editor +class Style_Parser { +public: + const char *tbuff { nullptr }; // text buffer + char *sbuff { nullptr }; // style buffer + int len { 0 }; // running length + char style { 0 }; // current style + char lwhite { 1 }; // leading white space (1=white, 0=past white) + int col { 0 }; // line's column counter + char keyword[40] { }; // keyword parsing buffer + char last { 0 }; // flag for keyword parsing + + Style_Parser() = default; + + // Methods to aid in parsing + int parse_over_char(int handle_crlf=1); + int parse_over_white(); + int parse_over_alpha(); + int parse_to_eol(char s); + int parse_block_comment(); // "/* text.. */" + void buffer_keyword(); + int parse_over_key(const char *key, char s); + int parse_over_angles(char s); + int parse_keyword(); // "switch" + int parse_quoted_string(char quote_char, char in_style); + // "hello", 'x' + int parse_directive(); // "#define" + int parse_line_comment(); // "// text.." + int parse_escape(); // "\'" + int parse_all_else(); // all other code +}; + +} // namespace widget +} // namespace fld + +#endif // FLUID_WIDGETS_STYLE_PARSER_H diff --git a/fluid/widgets/Text_Viewer.cxx b/fluid/widgets/Text_Viewer.cxx new file mode 100644 index 000000000..ba2d145fe --- /dev/null +++ b/fluid/widgets/Text_Viewer.cxx @@ -0,0 +1,58 @@ +// +// Code editor widget for the Fast Light Tool Kit (FLTK). +// Syntax highlighting rewritten by erco@seriss.com 09/15/20. +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// 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 necessary headers... +// + +#include "widgets/Text_Viewer.h" + +using namespace fld; +using namespace fld::widget; + +/** + Create a fld::widget::Text_Viewer widget. + \param[in] X, Y, W, H position and size of the widget + \param[in] L optional label + */ +Text_Viewer::Text_Viewer(int X, int Y, int W, int H, const char *L) +: Fl_Text_Display(X, Y, W, H, L) +{ + buffer(new Fl_Text_Buffer); +} + +/** + Avoid memory leaks. + */ +Text_Viewer::~Text_Viewer() { + Fl_Text_Buffer *buf = mBuffer; + buffer(0); + delete buf; +} + +/** + Tricking Fl_Text_Display into using bearable colors for this specific task. + */ +void Text_Viewer::draw() +{ + Fl_Color c = Fl::get_color(FL_SELECTION_COLOR); + Fl::set_color(FL_SELECTION_COLOR, fl_color_average(FL_BACKGROUND_COLOR, FL_FOREGROUND_COLOR, 0.9f)); + Fl_Text_Display::draw(); + Fl::set_color(FL_SELECTION_COLOR, c); +} + + diff --git a/fluid/widgets/Text_Viewer.h b/fluid/widgets/Text_Viewer.h new file mode 100644 index 000000000..1e5810d50 --- /dev/null +++ b/fluid/widgets/Text_Viewer.h @@ -0,0 +1,46 @@ +// +// Code editor widget for the Fast Light Tool Kit (FLTK). +// Syntax highlighting rewritten by erco@seriss.com 09/15/20. +// +// Copyright 1998-2025 by Bill Spitzak and others. +// +// 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_WIDGETS_TEXT_VIEWER_H +#define FLUID_WIDGETS_TEXT_VIEWER_H + +// +// Include necessary headers... +// + +#include + +namespace fld { +namespace widget { + +/** + A text viewer with an additional highlighting color scheme. + */ +class Text_Viewer : public Fl_Text_Display { +public: + Text_Viewer(int X, int Y, int W, int H, const char *L = nullptr); + ~Text_Viewer(); + void draw() override; + + /// access to protected member get_absolute_top_line_number() + int top_line() { return get_absolute_top_line_number(); } +}; + +} // namespace widget +} // namespace fld + +#endif // FLUID_WIDGETS_TEXT_VIEWER_H diff --git a/fluid/widgets/custom_widgets.cxx b/fluid/widgets/custom_widgets.cxx deleted file mode 100644 index 25a630783..000000000 --- a/fluid/widgets/custom_widgets.cxx +++ /dev/null @@ -1,311 +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 -// - -#include "widgets/custom_widgets.h" - -#include "app/fluid.h" -#include "nodes/Fl_Window_Type.h" -#include "nodes/factory.h" -#include "panels/widget_panel.h" -#include "widgets/widget_browser.h" - -#include -#include -#include -#include -#include -#include -#include "../src/flstring.h" - -/** \class Widget_Bin_Button - The Widget_Bin_Button button is a button that can be used in the widget bin to - allow the user to drag and drop widgets into a window or group. This feature - makes it easy for the user to position a widget at a specific location within - the window or group. - */ - -/** - Convert mouse dragging into a drag and drop event. - */ -int Widget_Bin_Button::handle(int inEvent) -{ - int ret = 0; - switch (inEvent) { - case FL_PUSH: - Fl_Button::handle(inEvent); - return 1; // make sure that we get drag events - case FL_DRAG: - ret = Fl_Button::handle(inEvent); - if (!user_data()) - return ret; - if (!Fl::event_is_click()) { // make it a dnd event - // fake a drag outside of the widget - Fl::e_x = x()-1; - Fl_Button::handle(inEvent); - // fake a button release - Fl_Button::handle(FL_RELEASE); - // make it into a dnd event - const char *type_name = (const char*)user_data(); - Fl_Type::current_dnd = Fl_Type::current; - Fl::copy(type_name, (int)strlen(type_name)+1, 0); - Fl::dnd(); - return 1; - } - return ret; - } - return Fl_Button::handle(inEvent); -} - -/** \class Widget_Bin_Window_Button - The Widget_Bin_Window_Button button is used in the widget bin to create new - windows by dragging and dropping. When the button is dragged and dropped onto - the desktop, a new window will be created at the drop location. - */ - -/** - Convert mouse dragging into a drag and drop event. - */ -int Widget_Bin_Window_Button::handle(int inEvent) -{ - static Fl_Window *drag_win = NULL; - int ret = 0; - switch (inEvent) { - case FL_PUSH: - Fl_Button::handle(inEvent); - return 1; // make sure that we get drag events - case FL_DRAG: - ret = Fl_Button::handle(inEvent); - if (!user_data()) - return ret; - if (!Fl::event_is_click()) { - if (!drag_win) { - drag_win = new Fl_Window(0, 0, 480, 320); - drag_win->border(0); - drag_win->set_non_modal(); - } - if (drag_win) { - drag_win->position(Fl::event_x_root()+1, Fl::event_y_root()+1); - drag_win->show(); - } - // Does not work outside window: fl_cursor(FL_CURSOR_HAND); - } - return ret; - case FL_RELEASE: - if (drag_win) { - Fl::delete_widget(drag_win); - drag_win = NULL; - // create a new window here - Fl_Type *prototype = typename_to_prototype((char*)user_data()); - if (prototype) { - Fl_Type *new_type = add_new_widget_from_user(prototype, Strategy::AFTER_CURRENT); - if (new_type && new_type->is_a(ID_Window)) { - Fl_Window_Type *new_window = (Fl_Window_Type*)new_type; - Fl_Window *w = (Fl_Window *)new_window->o; - w->position(Fl::event_x_root(), Fl::event_y_root()); - } - } - widget_browser->display(Fl_Type::current); - widget_browser->rebuild(); - } - return Fl_Button::handle(inEvent); - } - return Fl_Button::handle(inEvent); -} - -/** \class Fluid_Coord_Input - The Fluid_Coord_Input widget is an input field for entering widget coordinates - and sizes. It includes basic math capabilities and allows the use of variables - in formulas. This widget is useful for specifying precise positions and - dimensions for widgets in a graphical user interface. - */ - -/** - Create an input field. - */ -Fluid_Coord_Input::Fluid_Coord_Input(int x, int y, int w, int h, const char *l) : -Fl_Input(x, y, w, h, l), -user_callback_(0L), -vars_(0L), -vars_user_data_(0L) -{ - Fl_Input::callback((Fl_Callback*)callback_handler_cb); - text("0"); -} - -void Fluid_Coord_Input::callback_handler_cb(Fluid_Coord_Input *This, void *v) { - This->callback_handler(v); -} - -void Fluid_Coord_Input::callback_handler(void *v) { - if (user_callback_) - (*user_callback_)(this, v); - // do *not* update the value to show the evaluated formula here, because the - // values of the variables have already updated after the user callback. -} - -/** - \brief Get the value of a variable. - Collects all consecutive ASCII letters into a variable name, scans the - Variable list for that name, and then calls the corresponding callback from - the Variable array. - \param s points to the first character of the variable name, must point after - the last character of the variable name when returning. - \return the integer value that was found or calculated - */ -int Fluid_Coord_Input::eval_var(uchar *&s) const { - if (!vars_) - return 0; - // find the end of the variable name - uchar *v = s; - while (isalpha(*s)) s++; - int n = (int)(s-v); - // find the variable in the list - for (Fluid_Coord_Input_Vars *vars = vars_; vars->name_; vars++) { - if (strncmp((char*)v, vars->name_, n)==0 && vars->name_[n]==0) - return vars->callback_(this, vars_user_data_); - } - return 0; -} - -/** - Evaluate a formula into an integer, recursive part. - \param s remaining text in this formula, must return a pointer to the next - character that will be interpreted. - \param prio priority of current operation - \return the value so far - */ -int Fluid_Coord_Input::eval(uchar *&s, int prio) const { - int v = 0, sgn = 1; - uchar c = *s++; - - // check for end of text - if (c==0) { s--; return sgn*v; } - - // check for unary operator - if (c=='-') { sgn = -1; c = *s++; } - else if (c=='+') { sgn = 1; c = *s++; } - - // read value, variable, or bracketed term - if (c==0) { - s--; return sgn*v; - } else if (c>='0' && c<='9') { - // numeric value - while (c>='0' && c<='9') { - v = v*10 + (c-'0'); - c = *s++; - } - } else if (isalpha(c)) { - v = eval_var(--s); - c = *s++; - } else if (c=='(') { - // opening bracket - v = eval(s, 5); - } else { - return sgn*v; // syntax error - } - if (sgn==-1) v = -v; - - // Now evaluate all following binary operators - for (;;) { - if (c==0) { - s--; - return v; - } else if (c=='+' || c=='-') { - if (prio<=4) { s--; return v; } - if (c=='+') { v += eval(s, 4); } - else if (c=='-') { v -= eval(s, 4); } - } else if (c=='*' || c=='/') { - if (prio<=3) { s--; return v; } - if (c=='*') { v *= eval(s, 3); } - else if (c=='/') { - int x = eval(s, 3); - if (x!=0) // if x is zero, don't divide - v /= x; - } - } else if (c==')') { - return v; - } else { - return v; // syntax error - } - c = *s++; - } - return v; -} - -/** - Evaluate a formula into an integer. - - The Fluid_Coord_Input widget includes a formula interpreter that allows you - to evaluate a string containing a mathematical formula and obtain the result - as an integer. The interpreter supports unary plus and minus, basic integer - math operations (such as addition, subtraction, multiplication, and division), - and brackets. It also allows you to define a list of variables by name and use - them in the formula. The interpreter does not perform error checking, so it is - assumed that the formula is entered correctly. - - \param s formula as a C string - \return the calculated value - */ -int Fluid_Coord_Input::eval(const char *s) const -{ - // duplicate the text, so we can modify it - uchar *buf = (uchar*)fl_strdup(s); - uchar *src = buf, *dst = buf; - // remove all whitespace to make the parser easier - for (;;) { - uchar c = *src++; - if (c==' ' || c=='\t') continue; - *dst++ = c; - if (c==0) break; - } - src = buf; - // now jump into the recursion - int ret = eval(src, 5); - ::free(buf); - return ret; -} - -/** - Evaluate the formula and return the result. - */ -int Fluid_Coord_Input::value() const { - return eval(text()); -} - -/** - Set the field to an integer value, replacing previous texts. - */ -void Fluid_Coord_Input::value(int v) { - char buf[32]; - fl_snprintf(buf, sizeof(buf), "%d", v); - text(buf); -} - -/** - Allow vertical mouse dragging and mouse wheel to interactively change the value. - */ -int Fluid_Coord_Input::handle(int event) { - switch (event) { - case FL_MOUSEWHEEL: - if (Fl::event_dy()) { - value( value() - Fl::event_dy() ); - set_changed(); - do_callback(FL_REASON_CHANGED); - } - return 1; - } - return Fl_Input::handle(event); -} diff --git a/fluid/widgets/custom_widgets.h b/fluid/widgets/custom_widgets.h deleted file mode 100644 index 875496b8e..000000000 --- a/fluid/widgets/custom_widgets.h +++ /dev/null @@ -1,90 +0,0 @@ -// -// Shortcut 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_SHORTCUT_BUTTON_H -#define _FLUID_SHORTCUT_BUTTON_H - -#include -#include - -// Adding drag and drop for dragging widgets into windows. -class Widget_Bin_Button : public Fl_Button { -public: - int handle(int) FL_OVERRIDE; - Widget_Bin_Button(int X,int Y,int W,int H, const char* l = 0) : - Fl_Button(X,Y,W,H,l) { } -}; - -// Adding drag and drop functionality to drag window prototypes onto the desktop. -class Widget_Bin_Window_Button : public Fl_Button { -public: - int handle(int) FL_OVERRIDE; - Widget_Bin_Window_Button(int X,int Y,int W,int H, const char* l = 0) : - Fl_Button(X,Y,W,H,l) { } -}; - -// Callback signature for function returning the value of a variable. -typedef int (Fluid_Coord_Callback)(class Fluid_Coord_Input const *, void*); - -// Entry for a list of variables available to an input field. -// Fluid_Coord_Input::variables() expects an array of Fluid_Coord_Input_Vars -// with the last entry's name_ set to NULL. -typedef struct Fluid_Coord_Input_Vars { - const char *name_; - Fluid_Coord_Callback *callback_; -} Fluid_Coord_Input_Vars; - -// A text input widget that understands simple math. -class Fluid_Coord_Input : public Fl_Input -{ - Fl_Callback *user_callback_; - Fluid_Coord_Input_Vars *vars_; - void *vars_user_data_; - static void callback_handler_cb(Fluid_Coord_Input *This, void *v); - void callback_handler(void *v); - int eval_var(uchar *&s) const; - int eval(uchar *&s, int prio) const; - int eval(const char *s) const; - -public: - Fluid_Coord_Input(int x, int y, int w, int h, const char *l=0L); - - /** Return the text in the widget text field. */ - const char *text() const { return Fl_Input::value(); } - - /** Set the text in the text field */ - void text(const char *v) { Fl_Input::value(v); } - - int value() const; - void value(int v); - - /** Set the general callback for this widget. */ - void callback(Fl_Callback *cb) { - user_callback_ = cb; - } - - /** Set the list of the available variables - \param vars array of variables, last entry `has name_` set to `NULL` - \param user_data is forwarded to the Variable callback */ - void variables(Fluid_Coord_Input_Vars *vars, void *user_data) { - vars_ = vars; - vars_user_data_ = user_data; - } - - int handle(int) FL_OVERRIDE; -}; - -#endif -- cgit v1.2.3