summaryrefslogtreecommitdiff
path: root/src/Fl_Message.cxx
diff options
context:
space:
mode:
authorAlbrecht Schlosser <albrechts.fltk@online.de>2021-12-04 13:35:47 +0100
committerAlbrecht Schlosser <albrechts.fltk@online.de>2021-12-04 14:49:27 +0100
commitb6de09cff2465db3c0a6e6a013b825462bc9a0e7 (patch)
tree23725f27cc08e314f3c5d15d1c47c3f3b24b7588 /src/Fl_Message.cxx
parent240465626604ed9a7f7b2cb997ab4dffd419eabc (diff)
Re-enable nested (aka recursive) common dialogs (STR 3242, #282)
Apply Fl_Dialog_r10831.patch as given in STR 3242: https://www.fltk.org/strfiles/3242/Fl_Dialog_r10831.patch Reformat, add missing pieces, rename private members, cleanup... Improve documentation, add fl_choice_n() (issue #282) New methods fl_input_str() and fl_password_str() return Fl_String
Diffstat (limited to 'src/Fl_Message.cxx')
-rw-r--r--src/Fl_Message.cxx532
1 files changed, 532 insertions, 0 deletions
diff --git a/src/Fl_Message.cxx b/src/Fl_Message.cxx
new file mode 100644
index 000000000..1b93ecad3
--- /dev/null
+++ b/src/Fl_Message.cxx
@@ -0,0 +1,532 @@
+//
+// Common dialog implementation for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2021 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+// https://www.fltk.org/bugs.php
+//
+
+/**
+ \cond DriverDev
+ \addtogroup DriverDeveloper
+ \{
+*/
+
+/**
+ \file Fl_Message.cxx - Base class for common dialogs.
+
+ This is the base class for all common FLTK dialog windows used in
+ fl_message(), fl_ask(), fl_choice(), fl_input(), and fl_password().
+
+ \note <b>Internal use only. This class may be changed as required
+ without notice.</b>\n
+ This class is reserved for FLTK's internal usage in common dialogs
+ in FLTK 1.4.x and later. The header file is "hidden" in the src/
+ folder and not installed with the public header files.
+
+ \since 1.4.0
+
+ All common dialogs can be altered by changing the contents of some
+ static class variables. This is done by accessor methods like
+ fl_message_title() and others defined in FL/fl_ask.H. This is for
+ backwards compatibility with FLTK 1.3.x and earlier.
+
+ \note The documentation is only visible if the CMake option
+ \c 'OPTION_INCLUDE_DRIVER_DOCUMENTATION' is enabled.
+*/
+
+#include <FL/Fl.H>
+#include "flstring.h"
+#include <FL/fl_ask.H>
+#include "Fl_Message.h" // intentionally "hidden" in src/...
+
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Button.H>
+#include <FL/Fl_Return_Button.H>
+#include <FL/Fl_Input.H>
+#include <FL/Fl_Secret_Input.H>
+#include <FL/fl_draw.H>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+// Fl_Message static variables
+
+const char *Fl_Message::message_title_default_;
+const char *Fl_Message::message_title_;
+
+Fl_Box *Fl_Message::message_icon_;
+
+char *Fl_Message::input_buffer_;
+int Fl_Message::input_size_;
+
+int Fl_Message::enable_hotspot_ = 1;
+int Fl_Message::form_x_ = 0;
+int Fl_Message::form_y_ = 0;
+int Fl_Message::form_position_ = 0; // 0 = not set, 1 = absolute, 2 = centered
+
+/**
+ Fl_Message's internal button callback.
+
+ This callback function is used when the user pushes a button, presses the
+ \c Return key, or uses a valid shortcut for one of the buttons.
+
+ The internal return value \p retval_ is set according to the button that
+ was invoked (0, 1, 2) and \p window_closed_ is set to 0 (dialog closed
+ by dialog button). Then the dialog window is closed (hidden).
+*/
+
+void Fl_Message::button_cb_(Fl_Widget *w, void *d) {
+ Fl_Window *window = w->window();
+ Fl_Message *dialog = (Fl_Message *)window->user_data();
+ dialog->window_closed_ = 0;
+ dialog->retval_ = fl_int(d); // button
+ window->hide();
+} // button_cb_()
+
+/**
+ Fl_Message's internal window callback.
+
+ This callback function is used internally when the dialog window is closed
+ by the user with the window close button or by pressing \c Escape.
+
+ The internal return value \p retval_ is set to 0 (i.e. button 0) and
+ \p window_closed_ is set to -1 (Escape) or -2 (window close button).
+ Then the dialog window is closed (hidden).
+*/
+
+void Fl_Message::window_cb_(Fl_Widget *w, void *d) {
+ Fl_Window *window = (Fl_Window *)w;
+ Fl_Message *dialog = (Fl_Message *)window->user_data();
+ if ((Fl::event() == FL_KEYBOARD || Fl::event() == FL_SHORTCUT) &&
+ (Fl::event_key() == FL_Escape))
+ dialog->window_closed_ = -1;
+ else
+ dialog->window_closed_ = -2;
+ dialog->retval_ = 0; // either window or button 0
+ window->hide();
+} // window_cb_()
+
+/**
+ Fl_Message constructor.
+
+ The constructor creates a default message window and sets the icon type
+ to the given \p iconlabel which can be any character (or string).
+
+ Message text box (Fl_Box), icon (Fl_Box), and an input (Fl_Input) widgets
+ are created and initialized. Three buttons are created and arranged right to
+ left in the message window. The second (middle) button is an Fl_Return_Button.
+
+ The message window is set to modal()
+*/
+Fl_Message::Fl_Message(const char *iconlabel)
+ : window_(0)
+ , retval_(0)
+ , window_closed_(0) {
+
+ // Make sure that the dialog does not become the child of some current group.
+
+ Fl_Group *previous_group = Fl_Group::current();
+ if (previous_group)
+ Fl_Group::current(0);
+
+ // create widgets
+
+ window_ = new Fl_Window(400, 150, NULL);
+ message_ = new Fl_Box(60, 25, 340, 20);
+ message_->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_WRAP);
+
+ input_ = new Fl_Input(60, 37, 340, 23);
+ input_->hide();
+
+ Fl_Box *icon_template = message_icon(); // use template for icon box
+
+ icon_ = new Fl_Box(10, 10, 50, 50);
+ icon_->box(icon_template->box());
+ icon_->labelfont(icon_template->labelfont());
+ icon_->labelsize(icon_template->labelsize());
+ icon_->color(icon_template->color());
+ icon_->labelcolor(icon_template->labelcolor());
+ icon_->label(iconlabel);
+
+ window_->end(); // don't add the buttons automatically
+
+ // create the buttons (positions: right to left)
+ // button 1 is a return button
+
+ for (int b = 0, x = 310; b < 3; b++, x -= 100) {
+ if (b == 1) {
+ button_[b] = new Fl_Return_Button(x, 70, 90, 23);
+ } else {
+ button_[b] = new Fl_Button(x, 70, 90, 23);
+ }
+ button_[b]->align(FL_ALIGN_INSIDE | FL_ALIGN_WRAP);
+ button_[b]->callback(button_cb_, fl_voidptr(b));
+ }
+
+ // add the buttons left to right for tab navigation
+
+ for (int b = 2; b >= 0; b--) {
+ window_->add(button_[b]);
+ }
+
+ window_->begin();
+ window_->resizable(new Fl_Box(60, 10, 110 - 60, 27));
+ window_->end();
+ window_->callback(window_cb_, this);
+ window_->set_modal();
+
+ // restore previous group
+ Fl_Group::current(previous_group);
+}
+
+/**
+ Resizes the form and widgets so that they hold everything
+ that is asked of them.
+*/
+void Fl_Message::resizeform() {
+ int i;
+ int message_w, message_h;
+ int text_height;
+ int button_w[3], button_h[3];
+ int x, w, h, max_w, max_h;
+ const int icon_size = 50;
+
+ fl_font(message_->labelfont(), message_->labelsize());
+ message_w = message_h = 0;
+ fl_measure(message_->label(), message_w, message_h);
+
+ message_w += 10;
+ message_h += 10;
+ if (message_w < 340)
+ message_w = 340;
+ if (message_h < 30)
+ message_h = 30;
+
+ fl_font(button_[0]->labelfont(), button_[0]->labelsize());
+
+ memset(button_w, 0, sizeof(button_w));
+ memset(button_h, 0, sizeof(button_h));
+
+ for (max_h = 25, i = 0; i < 3; i++)
+ if (button_[i]->visible()) {
+ fl_measure(button_[i]->label(), button_w[i], button_h[i]);
+
+ if (i == 1)
+ button_w[1] += 20; // account for return button arrow
+
+ button_w[i] += 30;
+ button_h[i] += 10;
+
+ if (button_h[i] > max_h)
+ max_h = button_h[i];
+ }
+
+ if (input_->visible())
+ text_height = message_h + 25;
+ else
+ text_height = message_h;
+
+ max_w = message_w + 10 + icon_size;
+ w = button_w[0] + button_w[1] + button_w[2] - 10;
+
+ if (w > max_w)
+ max_w = w;
+
+ message_w = max_w - 10 - icon_size;
+
+ w = max_w + 20;
+ h = max_h + 30 + text_height;
+
+ window_->size(w, h);
+ window_->size_range(w, h, w, h);
+
+ message_->resize(20 + icon_size, 10, message_w, message_h);
+ icon_->resize(10, 10, icon_size, icon_size);
+ icon_->labelsize(icon_size - 10);
+ input_->resize(20 + icon_size, 10 + message_h, message_w, 25);
+
+ for (x = w, i = 0; i < 3; i++) {
+ if (button_w[i]) {
+ x -= button_w[i];
+ button_[i]->resize(x, h - 10 - max_h, button_w[i] - 10, max_h);
+ }
+ }
+}
+
+/**
+ Does all Fl_Message window internals for messages with and w/o an input field.
+
+ This method finalizes the layout of the message window, arranges (shows or hides)
+ the buttons and the optional text input widget, sets the message window title,
+ pops up the window, and waits for user input.
+
+ The message window is positioned according to the positioning options set before
+ calling the message function.
+
+ The private variables \p retval_ and \p window_closed_ are set depending
+ on the user action (pushing a button or closing the window).
+
+ \note The above mentioned variables must be evaluated before the event
+ loop is called because they might be overwritten by another dialog.
+ It is safe to evaluate the variables before the message function returns.
+
+ \param[in] fmt printf style format used in the user function call
+ \param[in] ap argument list provided by the user function call
+ \param[in] b0 text of button 0 (right: usually the "cancel" button)
+ \param[in] b1 text of button 1 (middle)
+ \param[in] b2 text of button 2 (left)
+
+ \returns return code
+ \retval 0 for Escape, window close, or button 0
+ \retval 1 button 1 was pushed
+ \retval 2 button 2 was pushed
+*/
+
+int Fl_Message::innards(const char *fmt, va_list ap, const char *b0, const char *b1, const char *b2) {
+ Fl::pushed(0); // stop dragging (STR #2159)
+
+ char buffer[1024];
+ if (!strcmp(fmt, "%s")) {
+ message_->label(va_arg(ap, const char *));
+ } else {
+ ::vsnprintf(buffer, sizeof(buffer) - 1, fmt, ap);
+ message_->label(buffer);
+ }
+
+ message_->labelfont(fl_message_font_);
+ if (fl_message_size_ == -1)
+ message_->labelsize(FL_NORMAL_SIZE);
+ else
+ message_->labelsize(fl_message_size_);
+
+ if (b0) {
+ button_[0]->show();
+ button_[0]->label(b0);
+ button_[1]->position(210, 70);
+ } else {
+ button_[0]->hide();
+ button_[1]->position(310, 70);
+ }
+ if (b1) {
+ button_[1]->show();
+ button_[1]->label(b1);
+ } else
+ button_[1]->hide();
+ if (b2) {
+ button_[2]->show();
+ button_[2]->label(b2);
+ } else
+ button_[2]->hide();
+
+ resizeform();
+
+ if (button_[1]->visible() && !input_->visible())
+ button_[1]->take_focus();
+
+ if (form_position_) {
+ if (form_position_ == 2) { // centered
+ form_x_ -= window_->w() / 2;
+ form_y_ -= window_->h() / 2;
+ }
+ window_->position(form_x_, form_y_);
+ form_x_ = form_y_ = form_position_ = 0;
+ } else if (enable_hotspot_)
+ window_->hotspot(button_[0]);
+ else
+ window_->free_position();
+
+ if (b0 && Fl_Widget::label_shortcut(b0))
+ button_[0]->shortcut(0);
+
+ // set the one-time window title, if defined and a specific title is not set
+ if (!window_->label() && message_title_) {
+ window_->copy_label(message_title_);
+ message_title(0); // reset global message title (compat.)
+ }
+
+ // set default window title, if defined and a specific title is not set
+ if (!window_->label() && message_title_default_)
+ window_->copy_label(message_title_default_);
+
+ // deactivate Fl::grab() because it is incompatible with modal windows
+ Fl_Window *g = Fl::grab();
+ if (g)
+ Fl::grab(0);
+ Fl_Group *current_group = Fl_Group::current(); // make sure the dialog does not interfere with any active group
+ window_->show();
+ Fl_Group::current(current_group);
+ while (window_->shown())
+ Fl::wait();
+ if (g) // regrab the previous popup menu, if there was one
+ Fl::grab(g);
+
+ return retval_;
+}
+
+/**
+ Gets the default icon container (Fl_Box) used in common dialogs.
+
+ Many common dialogs like fl_message(), fl_alert(), fl_ask(),
+ fl_choice(), fl_input(), and fl_password() display an icon.
+
+ You can use this method to get the icon box (Fl_Box) and modify
+ the icon's box type, font, fontsize etc.
+
+ \since FLTK 1.4.0
+
+ \note This method replaces the deprecated method fl_message_icon().
+ Note that this new method correctly returns an Fl_Box * pointer,
+ whereas fl_message_icon() returns Fl_Widget *.
+
+ The current icon default values are:
+
+ - width and height: 50 (currently not changeable)
+ - box(FL_THIN_UP_BOX)
+ - labelfont(FL_TIMES_BOLD)
+ - labelsize(34)
+ - color(FL_WHITE)
+ - labelcolor(FL_BLUE)
+
+ These values may be changed in a future FLTK version (although this
+ is not very likely to happen).
+
+ If you change this then the changed values are used for subsequently
+ started common dialogs. This is intended to change the icon layout
+ for your application at startup time. It is not recommended to change
+ the default values for each message.
+*/
+Fl_Box *Fl_Message::message_icon() {
+
+ if (!Fl_Message::message_icon_) { // not yet initialized
+
+ Fl_Group *current_group = Fl_Group::current();
+ Fl_Group::current(0);
+
+ Fl_Box *o = Fl_Message::message_icon_ = new Fl_Box(10, 10, 50, 50);
+ o->box(FL_THIN_UP_BOX);
+ o->labelfont(FL_TIMES_BOLD);
+ o->labelsize(34);
+ o->color(FL_WHITE);
+ o->labelcolor(FL_BLUE);
+ Fl_Group::current(current_group);
+ }
+ return Fl_Message::message_icon_;
+}
+
+/**
+ Does all Fl_Message window internals for messages with a text input field.
+
+ \see innards()
+*/
+const char *Fl_Message::input_innards(const char *fmt, va_list ap, const char *defstr, uchar type, int maxchar) {
+ message_->position(60, 10);
+ input_->type(type);
+ input_->show();
+ input_->value(defstr);
+ input_->take_focus();
+ if (maxchar > 0)
+ input_->maximum_size(maxchar);
+
+ int r = innards(fmt, ap, fl_cancel, fl_ok, 0);
+
+ if (!r)
+ return 0;
+
+ if (input_->value()) {
+
+ int size = input_->size() + 1;
+
+ if (maxchar < 0) { // need to store the value in pre-allocated buffer
+
+ // The allocated input buffer starts with size 0 and is allocated
+ // in multiples of 128 bytes >= size. If both the size and the pointer
+ // are 0 (NULL) then realloc() allocates a /new/ buffer.
+
+ if (size > input_size_) {
+ size += 127;
+ size &= ~127;
+ input_buffer_ = (char *)realloc(input_buffer_, size);
+ input_size_ = size;
+ }
+
+ // Store the input. Note that value() can contain null bytes,
+ // so we use memcpy and add a terminating null byte as well.
+
+ memcpy(input_buffer_, input_->value(), input_->size());
+ input_buffer_[input_->size()] = '\0';
+ return (input_buffer_);
+
+ } else { // new version: allocate string buffer
+
+ char *buf = (char *)malloc(size);
+ memcpy(buf, input_->value(), input_->size());
+ buf[input_->size()] = '\0';
+ return (buf);
+ }
+
+ } else
+ return 0;
+}
+
+/** Sets the title of the dialog window used in many common dialogs.
+
+ This window \p title will be used in the next call of one of the
+ common dialogs like fl_message(), fl_alert(), fl_ask(), fl_choice(),
+ fl_input(), fl_password().
+
+ The \p title string is copied internally, so that you can use a
+ local variable or free the string immediately after this call. It
+ applies only to the \b next call of one of the common dialogs and
+ will be reset to an empty title (the default for all dialogs) after
+ that call.
+
+ \param[in] title window label, string copied internally
+*/
+void Fl_Message::message_title(const char *title) {
+ if (message_title_) {
+ free((void *)message_title_);
+ message_title_ = 0;
+ }
+ if (title)
+ message_title_ = strdup(title);
+}
+
+/** Sets the default title of the dialog window used in many common dialogs.
+
+ This window \p title will be used in all subsequent calls of one of the
+ common dialogs like fl_message(), fl_alert(), fl_ask(), fl_choice(),
+ fl_input(), fl_password(), unless a specific title has been set
+ with fl_message_title(const char *title).
+
+ The default is no title. You can override the default title for a
+ single dialog with fl_message_title(const char *title).
+
+ The \p title string is copied internally, so that you can use a
+ local variable or free the string immediately after this call.
+
+ \param[in] title default window label, string copied internally
+*/
+void Fl_Message::message_title_default(const char *title) {
+ if (message_title_default_) {
+ free((void *)message_title_default_);
+ message_title_default_ = 0;
+ }
+ if (title)
+ message_title_default_ = strdup(title);
+}
+
+/**
+ \}
+ \endcond
+*/