Include a stripped-down version of FLTK in tree and add a USE_INCLUDED_FLTK option to build against it.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4603 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/common/fltk/src/Fl.cxx b/common/fltk/src/Fl.cxx
new file mode 100644
index 0000000..900ff8f
--- /dev/null
+++ b/common/fltk/src/Fl.cxx
@@ -0,0 +1,2024 @@
+//
+// "$Id: Fl.cxx 8723 2011-05-23 16:49:02Z manolo $"
+//
+// Main event handling code for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2010 by Bill Spitzak and others.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+// USA.
+//
+// Please report all bugs and problems on the following page:
+//
+// http://www.fltk.org/str.php
+//
+
+
+// warning: the Apple Quartz version still uses some Quickdraw calls,
+// mostly to get around the single active context in QD and
+// to implement clipping. This should be changed into pure
+// Quartz calls in the near future.
+#include <config.h>
+
+/* We require Windows 2000 features (e.g. VK definitions) */
+#if defined(WIN32)
+# if !defined(WINVER) || (WINVER < 0x0500)
+# ifdef WINVER
+# undef WINVER
+# endif
+# define WINVER 0x0500
+# endif
+# if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+# ifdef _WIN32_WINNT
+# undef _WIN32_WINNT
+# endif
+# define _WIN32_WINNT 0x0500
+# endif
+#endif
+
+// recent versions of MinGW warn: "Please include winsock2.h before windows.h",
+// hence we must include winsock2.h before FL/Fl.H (A.S. Dec. 2010, IMM May 2011)
+#if defined(WIN32) && !defined(__CYGWIN__)
+# include <winsock2.h>
+#endif
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Tooltip.H>
+#include <FL/x.H>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "flstring.h"
+
+#if defined(DEBUG) || defined(DEBUG_WATCH)
+# include <stdio.h>
+#endif // DEBUG || DEBUG_WATCH
+
+#ifdef WIN32
+# include <ole2.h>
+void fl_free_fonts(void);
+HBRUSH fl_brush_action(int action);
+void fl_cleanup_pens(void);
+void fl_release_dc(HWND,HDC);
+void fl_cleanup_dc_list(void);
+#elif defined(__APPLE__)
+extern double fl_mac_flush_and_wait(double time_to_wait, char in_idle);
+#endif // WIN32
+
+//
+// Globals...
+//
+#if defined(__APPLE__) || defined(FL_DOXYGEN)
+const char *Fl_Mac_App_Menu::about = "About ";
+const char *Fl_Mac_App_Menu::print = "Print Front Window";
+const char *Fl_Mac_App_Menu::services = "Services";
+const char *Fl_Mac_App_Menu::hide = "Hide ";
+const char *Fl_Mac_App_Menu::hide_others = "Hide Others";
+const char *Fl_Mac_App_Menu::show = "Show All";
+const char *Fl_Mac_App_Menu::quit = "Quit ";
+#endif // __APPLE__
+#ifndef FL_DOXYGEN
+Fl_Widget *Fl::belowmouse_,
+ *Fl::pushed_,
+ *Fl::focus_,
+ *Fl::selection_owner_;
+int Fl::damage_,
+ Fl::e_number,
+ Fl::e_x,
+ Fl::e_y,
+ Fl::e_x_root,
+ Fl::e_y_root,
+ Fl::e_dx,
+ Fl::e_dy,
+ Fl::e_state,
+ Fl::e_clicks,
+ Fl::e_is_click,
+ Fl::e_keysym,
+ Fl::e_original_keysym,
+ Fl::scrollbar_size_ = 16;
+
+char *Fl::e_text = (char *)"";
+int Fl::e_length;
+
+Fl_Event_Dispatch Fl::e_dispatch = 0;
+
+unsigned char Fl::options_[] = { 0, 0 };
+unsigned char Fl::options_read_ = 0;
+
+
+Fl_Window *fl_xfocus; // which window X thinks has focus
+Fl_Window *fl_xmousewin;// which window X thinks has FL_ENTER
+Fl_Window *Fl::grab_; // most recent Fl::grab()
+Fl_Window *Fl::modal_; // topmost modal() window
+
+#endif // FL_DOXYGEN
+
+//
+// 'Fl::version()' - Return the API version number...
+//
+
+double
+/**
+ Returns the compiled-in value of the FL_VERSION constant. This
+ is useful for checking the version of a shared library.
+*/
+Fl::version() {
+ return FL_VERSION;
+}
+
+/**
+ Gets the default scrollbar size used by
+ Fl_Browser_,
+ Fl_Help_View,
+ Fl_Scroll, and
+ Fl_Text_Display widgets.
+ \returns The default size for widget scrollbars, in pixels.
+*/
+int Fl::scrollbar_size() {
+ return scrollbar_size_;
+}
+
+/**
+ Sets the default scrollbar size that is used by the
+ Fl_Browser_,
+ Fl_Help_View,
+ Fl_Scroll, and
+ Fl_Text_Display widgets.
+ \param[in] W The new default size for widget scrollbars, in pixels.
+*/
+void Fl::scrollbar_size(int W) {
+ scrollbar_size_ = W;
+}
+
+
+/** Returns whether or not the mouse event is inside the given rectangle.
+
+ Returns non-zero if the current Fl::event_x() and Fl::event_y()
+ put it inside the given arbitrary bounding box.
+
+ You should always call this rather than doing your own comparison
+ so you are consistent about edge effects.
+
+ To find out, whether the event is inside a child widget of the
+ current window, you can use Fl::event_inside(const Fl_Widget *).
+
+ \param[in] xx,yy,ww,hh bounding box
+ \return non-zero, if mouse event is inside
+*/
+int Fl::event_inside(int xx,int yy,int ww,int hh) /*const*/ {
+ int mx = e_x - xx;
+ int my = e_y - yy;
+ return (mx >= 0 && mx < ww && my >= 0 && my < hh);
+}
+
+/** Returns whether or not the mouse event is inside a given child widget.
+
+ Returns non-zero if the current Fl::event_x() and Fl::event_y()
+ put it inside the given child widget's bounding box.
+
+ This method can only be used to check whether the mouse event is
+ inside a \b child widget of the window that handles the event, and
+ there must not be an intermediate subwindow (i.e. the widget must
+ not be inside a subwindow of the current window). However, it is
+ valid if the widget is inside a nested Fl_Group.
+
+ You must not use it with the window itself as the \p o argument
+ in a window's handle() method.
+
+ \note The mentioned restrictions are necessary, because this method
+ does not transform coordinates of child widgets, and thus the given
+ widget \p o must be within the \e same window that is handling the
+ current event. Otherwise the results are undefined.
+
+ You should always call this rather than doing your own comparison
+ so you are consistent about edge effects.
+
+ \see Fl::event_inside(int, int, int, int)
+
+ \param[in] o child widget to be tested
+ \return non-zero, if mouse event is inside the widget
+*/
+int Fl::event_inside(const Fl_Widget *o) /*const*/ {
+ int mx = e_x - o->x();
+ int my = e_y - o->y();
+ return (mx >= 0 && mx < o->w() && my >= 0 && my < o->h());
+}
+
+//
+//
+// timer support
+//
+
+#ifdef WIN32
+
+// implementation in Fl_win32.cxx
+
+#elif defined(__APPLE__)
+
+// implementation in Fl_mac.cxx
+
+#else
+
+//
+// X11 timers
+//
+
+
+////////////////////////////////////////////////////////////////////////
+// Timeouts are stored in a sorted list (*first_timeout), so only the
+// first one needs to be checked to see if any should be called.
+// Allocated, but unused (free) Timeout structs are stored in another
+// linked list (*free_timeout).
+
+struct Timeout {
+ double time;
+ void (*cb)(void*);
+ void* arg;
+ Timeout* next;
+};
+static Timeout* first_timeout, *free_timeout;
+
+#include <sys/time.h>
+
+// I avoid the overhead of getting the current time when we have no
+// timeouts by setting this flag instead of getting the time.
+// In this case calling elapse_timeouts() does nothing, but records
+// the current time, and the next call will actually elapse time.
+static char reset_clock = 1;
+
+static void elapse_timeouts() {
+ static struct timeval prevclock;
+ struct timeval newclock;
+ gettimeofday(&newclock, NULL);
+ double elapsed = newclock.tv_sec - prevclock.tv_sec +
+ (newclock.tv_usec - prevclock.tv_usec)/1000000.0;
+ prevclock.tv_sec = newclock.tv_sec;
+ prevclock.tv_usec = newclock.tv_usec;
+ if (reset_clock) {
+ reset_clock = 0;
+ } else if (elapsed > 0) {
+ for (Timeout* t = first_timeout; t; t = t->next) t->time -= elapsed;
+ }
+}
+
+// Continuously-adjusted error value, this is a number <= 0 for how late
+// we were at calling the last timeout. This appears to make repeat_timeout
+// very accurate even when processing takes a significant portion of the
+// time interval:
+static double missed_timeout_by;
+
+void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void *argp) {
+ elapse_timeouts();
+ repeat_timeout(time, cb, argp);
+}
+
+void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void *argp) {
+ time += missed_timeout_by; if (time < -.05) time = 0;
+ Timeout* t = free_timeout;
+ if (t) {
+ free_timeout = t->next;
+ } else {
+ t = new Timeout;
+ }
+ t->time = time;
+ t->cb = cb;
+ t->arg = argp;
+ // insert-sort the new timeout:
+ Timeout** p = &first_timeout;
+ while (*p && (*p)->time <= time) p = &((*p)->next);
+ t->next = *p;
+ *p = t;
+}
+
+/**
+ Returns true if the timeout exists and has not been called yet.
+*/
+int Fl::has_timeout(Fl_Timeout_Handler cb, void *argp) {
+ for (Timeout* t = first_timeout; t; t = t->next)
+ if (t->cb == cb && t->arg == argp) return 1;
+ return 0;
+}
+
+/**
+ Removes a timeout callback. It is harmless to remove a timeout
+ callback that no longer exists.
+
+ \note This version removes all matching timeouts, not just the first one.
+ This may change in the future.
+*/
+void Fl::remove_timeout(Fl_Timeout_Handler cb, void *argp) {
+ for (Timeout** p = &first_timeout; *p;) {
+ Timeout* t = *p;
+ if (t->cb == cb && (t->arg == argp || !argp)) {
+ *p = t->next;
+ t->next = free_timeout;
+ free_timeout = t;
+ } else {
+ p = &(t->next);
+ }
+ }
+}
+
+#endif
+
+////////////////////////////////////////////////////////////////
+// Checks are just stored in a list. They are called in the reverse
+// order that they were added (this may change in the future).
+// This is a bit messy because I want to allow checks to be added,
+// removed, and have wait() called from inside them. To do this
+// next_check points at the next unprocessed one for the outermost
+// call to Fl::wait().
+
+struct Check {
+ void (*cb)(void*);
+ void* arg;
+ Check* next;
+};
+static Check *first_check, *next_check, *free_check;
+
+/**
+ FLTK will call this callback just before it flushes the display and
+ waits for events. This is different than an idle callback because it
+ is only called once, then FLTK calls the system and tells it not to
+ return until an event happens.
+
+ This can be used by code that wants to monitor the
+ application's state, such as to keep a display up to date. The
+ advantage of using a check callback is that it is called only when no
+ events are pending. If events are coming in quickly, whole blocks of
+ them will be processed before this is called once. This can save
+ significant time and avoid the application falling behind the events.
+
+ Sample code:
+
+ \code
+ bool state_changed; // anything that changes the display turns this on
+
+ void callback(void*) {
+ if (!state_changed) return;
+ state_changed = false;
+ do_expensive_calculation();
+ widget->redraw();
+ }
+
+ main() {
+ Fl::add_check(callback);
+ return Fl::run();
+ }
+ \endcode
+*/
+void Fl::add_check(Fl_Timeout_Handler cb, void *argp) {
+ Check* t = free_check;
+ if (t) free_check = t->next;
+ else t = new Check;
+ t->cb = cb;
+ t->arg = argp;
+ t->next = first_check;
+ if (next_check == first_check) next_check = t;
+ first_check = t;
+}
+
+/**
+ Removes a check callback. It is harmless to remove a check
+ callback that no longer exists.
+*/
+void Fl::remove_check(Fl_Timeout_Handler cb, void *argp) {
+ for (Check** p = &first_check; *p;) {
+ Check* t = *p;
+ if (t->cb == cb && t->arg == argp) {
+ if (next_check == t) next_check = t->next;
+ *p = t->next;
+ t->next = free_check;
+ free_check = t;
+ } else {
+ p = &(t->next);
+ }
+ }
+}
+
+/**
+ Returns 1 if the check exists and has not been called yet, 0 otherwise.
+*/
+int Fl::has_check(Fl_Timeout_Handler cb, void *argp) {
+ for (Check** p = &first_check; *p;) {
+ Check* t = *p;
+ if (t->cb == cb && t->arg == argp) {
+ return 1;
+ } else {
+ p = &(t->next);
+ }
+ }
+ return 0;
+}
+
+static void run_checks()
+{
+ // checks are a bit messy so that add/remove and wait may be called
+ // from inside them without causing an infinite loop:
+ if (next_check == first_check) {
+ while (next_check) {
+ Check* checkp = next_check;
+ next_check = checkp->next;
+ (checkp->cb)(checkp->arg);
+ }
+ next_check = first_check;
+ }
+}
+
+#ifndef WIN32
+static char in_idle;
+#endif
+
+////////////////////////////////////////////////////////////////
+// wait/run/check/ready:
+
+void (*Fl::idle)(); // see Fl::add_idle.cxx for the add/remove functions
+
+extern int fl_ready(); // in Fl_<platform>.cxx
+extern int fl_wait(double time); // in Fl_<platform>.cxx
+
+/**
+ See int Fl::wait()
+*/
+double Fl::wait(double time_to_wait) {
+ // delete all widgets that were listed during callbacks
+ do_widget_deletion();
+
+#ifdef WIN32
+
+ return fl_wait(time_to_wait);
+
+#elif defined(__APPLE__)
+
+ run_checks();
+ if (idle) {
+ if (!in_idle) {
+ in_idle = 1;
+ idle();
+ in_idle = 0;
+ }
+ // the idle function may turn off idle, we can then wait:
+ if (idle) time_to_wait = 0.0;
+ }
+ return fl_mac_flush_and_wait(time_to_wait, in_idle);
+
+#else
+
+ if (first_timeout) {
+ elapse_timeouts();
+ Timeout *t;
+ while ((t = first_timeout)) {
+ if (t->time > 0) break;
+ // The first timeout in the array has expired.
+ missed_timeout_by = t->time;
+ // We must remove timeout from array before doing the callback:
+ void (*cb)(void*) = t->cb;
+ void *argp = t->arg;
+ first_timeout = t->next;
+ t->next = free_timeout;
+ free_timeout = t;
+ // Now it is safe for the callback to do add_timeout:
+ cb(argp);
+ }
+ } else {
+ reset_clock = 1; // we are not going to check the clock
+ }
+ run_checks();
+// if (idle && !fl_ready()) {
+ if (idle) {
+ if (!in_idle) {
+ in_idle = 1;
+ idle();
+ in_idle = 0;
+ }
+ // the idle function may turn off idle, we can then wait:
+ if (idle) time_to_wait = 0.0;
+ }
+ if (first_timeout && first_timeout->time < time_to_wait)
+ time_to_wait = first_timeout->time;
+ if (time_to_wait <= 0.0) {
+ // do flush second so that the results of events are visible:
+ int ret = fl_wait(0.0);
+ flush();
+ return ret;
+ } else {
+ // do flush first so that user sees the display:
+ flush();
+ if (idle && !in_idle) // 'idle' may have been set within flush()
+ time_to_wait = 0.0;
+ return fl_wait(time_to_wait);
+ }
+#endif
+}
+
+#define FOREVER 1e20
+
+/**
+ As long as any windows are displayed this calls Fl::wait()
+ repeatedly. When all the windows are closed it returns zero
+ (supposedly it would return non-zero on any errors, but FLTK calls
+ exit directly for these). A normal program will end main()
+ with return Fl::run();.
+*/
+int Fl::run() {
+ while (Fl_X::first) wait(FOREVER);
+ return 0;
+}
+
+#ifdef WIN32
+
+// Function to initialize COM/OLE for usage. This must be done only once.
+// We define a flag to register whether we called it:
+static char oleInitialized = 0;
+
+// This calls the Windows function OleInitialize() exactly once.
+void fl_OleInitialize() {
+ if (!oleInitialized) {
+ OleInitialize(0L);
+ oleInitialized = 1;
+ }
+}
+
+// This calls the Windows function OleUninitialize() only, if
+// OleInitialize has been called before.
+void fl_OleUninitialize() {
+ if (oleInitialized) {
+ OleUninitialize();
+ oleInitialized = 0;
+ }
+}
+
+class Fl_Win32_At_Exit {
+public:
+ Fl_Win32_At_Exit() { }
+ ~Fl_Win32_At_Exit() {
+ fl_free_fonts(); // do some WIN32 cleanup
+ fl_cleanup_pens();
+ fl_OleUninitialize();
+ fl_brush_action(1);
+ fl_cleanup_dc_list();
+ }
+};
+static Fl_Win32_At_Exit win32_at_exit;
+#endif
+
+
+
+/**
+ Waits until "something happens" and then returns. Call this
+ repeatedly to "run" your program. You can also check what happened
+ each time after this returns, which is quite useful for managing
+ program state.
+
+ What this really does is call all idle callbacks, all elapsed
+ timeouts, call Fl::flush() to get the screen to update, and
+ then wait some time (zero if there are idle callbacks, the shortest of
+ all pending timeouts, or infinity), for any events from the user or
+ any Fl::add_fd() callbacks. It then handles the events and
+ calls the callbacks and then returns.
+
+ The return value of Fl::wait() is non-zero if there are any
+ visible windows - this may change in future versions of FLTK.
+
+ Fl::wait(time) waits a maximum of \e time seconds.
+ <i>It can return much sooner if something happens.</i>
+
+ The return value is positive if an event or fd happens before the
+ time elapsed. It is zero if nothing happens (on Win32 this will only
+ return zero if \e time is zero). It is negative if an error
+ occurs (this will happen on UNIX if a signal happens).
+*/
+int Fl::wait() {
+ if (!Fl_X::first) return 0;
+ wait(FOREVER);
+ return Fl_X::first != 0; // return true if there is a window
+}
+
+/**
+ Same as Fl::wait(0). Calling this during a big calculation
+ will keep the screen up to date and the interface responsive:
+
+ \code
+ while (!calculation_done()) {
+ calculate();
+ Fl::check();
+ if (user_hit_abort_button()) break;
+ }
+ \endcode
+
+ This returns non-zero if any windows are displayed, and 0 if no
+ windows are displayed (this is likely to change in future versions of
+ FLTK).
+*/
+int Fl::check() {
+ wait(0.0);
+ return Fl_X::first != 0; // return true if there is a window
+}
+
+/**
+ This is similar to Fl::check() except this does \e not
+ call Fl::flush() or any callbacks, which is useful if your
+ program is in a state where such callbacks are illegal. This returns
+ true if Fl::check() would do anything (it will continue to
+ return true until you call Fl::check() or Fl::wait()).
+
+ \code
+ while (!calculation_done()) {
+ calculate();
+ if (Fl::ready()) {
+ do_expensive_cleanup();
+ Fl::check();
+ if (user_hit_abort_button()) break;
+ }
+ }
+ \endcode
+*/
+int Fl::ready() {
+#if ! defined( WIN32 ) && ! defined(__APPLE__)
+ if (first_timeout) {
+ elapse_timeouts();
+ if (first_timeout->time <= 0) return 1;
+ } else {
+ reset_clock = 1;
+ }
+#endif
+ return fl_ready();
+}
+
+////////////////////////////////////////////////////////////////
+// Window list management:
+
+#ifndef FL_DOXYGEN
+Fl_X* Fl_X::first;
+#endif
+
+Fl_Window* fl_find(Window xid) {
+ Fl_X *window;
+ for (Fl_X **pp = &Fl_X::first; (window = *pp); pp = &window->next)
+#if defined(WIN32) || defined(USE_X11)
+ if (window->xid == xid)
+#elif defined(__APPLE_QUARTZ__)
+ if (window->xid == xid && !window->w->window())
+#else
+# error unsupported platform
+#endif // __APPLE__
+ {
+ if (window != Fl_X::first && !Fl::modal()) {
+ // make this window be first to speed up searches
+ // this is not done if modal is true to avoid messing up modal stack
+ *pp = window->next;
+ window->next = Fl_X::first;
+ Fl_X::first = window;
+ }
+ return window->w;
+ }
+ return 0;
+}
+
+/**
+ Returns the first top-level window in the list of shown() windows. If
+ a modal() window is shown this is the top-most modal window, otherwise
+ it is the most recent window to get an event.
+*/
+Fl_Window* Fl::first_window() {
+ Fl_X* i = Fl_X::first;
+ return i ? i->w : 0;
+}
+
+/**
+ Returns the next top-level window in the list of shown() windows.
+ You can use this call to iterate through all the windows that are shown().
+ \param[in] window must be shown and not NULL
+*/
+Fl_Window* Fl::next_window(const Fl_Window* window) {
+ Fl_X* i = Fl_X::i(window)->next;
+ return i ? i->w : 0;
+}
+
+/**
+ Sets the window that is returned by first_window().
+ The window is removed from wherever it is in the
+ list and inserted at the top. This is not done if Fl::modal()
+ is on or if the window is not shown(). Because the first window
+ is used to set the "parent" of modal windows, this is often
+ useful.
+ */
+void Fl::first_window(Fl_Window* window) {
+ if (!window || !window->shown()) return;
+ fl_find( Fl_X::i(window)->xid );
+}
+
+/**
+ Redraws all widgets.
+*/
+void Fl::redraw() {
+ for (Fl_X* i = Fl_X::first; i; i = i->next) i->w->redraw();
+}
+
+/**
+ Causes all the windows that need it to be redrawn and graphics forced
+ out through the pipes.
+
+ This is what wait() does before looking for events.
+
+ Note: in multi-threaded applications you should only call Fl::flush()
+ from the main thread. If a child thread needs to trigger a redraw event,
+ it should instead call Fl::awake() to get the main thread to process the
+ event queue.
+*/
+void Fl::flush() {
+ if (damage()) {
+ damage_ = 0;
+ for (Fl_X* i = Fl_X::first; i; i = i->next) {
+ if (i->wait_for_expose) {damage_ = 1; continue;}
+ Fl_Window* wi = i->w;
+ if (!wi->visible_r()) continue;
+ if (wi->damage()) {i->flush(); wi->clear_damage();}
+ // destroy damage regions for windows that don't use them:
+ if (i->region) {XDestroyRegion(i->region); i->region = 0;}
+ }
+ }
+#if defined(USE_X11)
+ if (fl_display) XFlush(fl_display);
+#elif defined(WIN32)
+ GdiFlush();
+#elif defined (__APPLE_QUARTZ__)
+ if (fl_gc)
+ CGContextFlush(fl_gc);
+#else
+# error unsupported platform
+#endif
+}
+
+
+////////////////////////////////////////////////////////////////
+// Event handlers:
+
+
+struct handler_link {
+ int (*handle)(int);
+ handler_link *next;
+};
+
+
+static handler_link *handlers = 0;
+
+
+/**
+ Install a function to parse unrecognized events. If FLTK cannot
+ figure out what to do with an event, it calls each of these functions
+ (most recent first) until one of them returns non-zero. If none of
+ them returns non-zero then the event is ignored. Events that cause
+ this to be called are:
+
+ - FL_SHORTCUT events that are not recognized by any widget.
+ This lets you provide global shortcut keys.
+ - System events that FLTK does not recognize. See fl_xevent.
+ - \e Some other events when the widget FLTK selected returns
+ zero from its handle() method. Exactly which ones may change
+ in future versions, however.
+
+ \see Fl::remove_handler(Fl_Event_Handler)
+ \see Fl::event_dispatch(Fl_Event_Dispatch d)
+ \see Fl::handle(int, Fl_Window*)
+*/
+void Fl::add_handler(Fl_Event_Handler ha) {
+ handler_link *l = new handler_link;
+ l->handle = ha;
+ l->next = handlers;
+ handlers = l;
+}
+
+
+/**
+ Removes a previously added event handler.
+ \see Fl::handle(int, Fl_Window*)
+*/
+void Fl::remove_handler(Fl_Event_Handler ha) {
+ handler_link *l, *p;
+
+ // Search for the handler in the list...
+ for (l = handlers, p = 0; l && l->handle != ha; p = l, l = l->next);
+
+ if (l) {
+ // Found it, so remove it from the list...
+ if (p) p->next = l->next;
+ else handlers = l->next;
+
+ // And free the record...
+ delete l;
+ }
+}
+
+int (*fl_local_grab)(int); // used by fl_dnd.cxx
+
+static int send_handlers(int e) {
+ for (const handler_link *hl = handlers; hl; hl = hl->next)
+ if (hl->handle(e)) return 1;
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////
+
+Fl_Widget* fl_oldfocus; // kludge for Fl_Group...
+
+/**
+ Sets the widget that will receive FL_KEYBOARD events.
+
+ If you change Fl::focus(), the previous widget and all
+ parents (that don't contain the new widget) are sent FL_UNFOCUS
+ events. Changing the focus does \e not send FL_FOCUS to
+ this or any widget, because sending FL_FOCUS is supposed to
+ \e test if the widget wants the focus (by it returning non-zero from
+ handle()).
+
+ \see Fl_Widget::take_focus()
+*/
+void Fl::focus(Fl_Widget *o) {
+ if (o && !o->visible_focus()) return;
+ if (grab()) return; // don't do anything while grab is on
+ Fl_Widget *p = focus_;
+ if (o != p) {
+ Fl::compose_reset();
+ focus_ = o;
+ // make sure that fl_xfocus is set to the top level window
+ // of this widget, or fl_fix_focus will clear our focus again
+ if (o) {
+ Fl_Window *win = 0, *w1 = o->as_window();
+ if (!w1) w1 = o->window();
+ while (w1) { win=w1; w1=win->window(); }
+ if (win) {
+#ifdef __APPLE__
+ if (fl_xfocus != win) {
+ Fl_X *x = Fl_X::i(win);
+ if (x) x->set_key_window();
+ }
+#endif
+ fl_xfocus = win;
+ }
+ }
+ // take focus from the old focused window
+ fl_oldfocus = 0;
+ int old_event = e_number;
+ e_number = FL_UNFOCUS;
+ for (; p; p = p->parent()) {
+ p->handle(FL_UNFOCUS);
+ fl_oldfocus = p;
+ }
+ e_number = old_event;
+ }
+}
+
+static char dnd_flag = 0; // make 'belowmouse' send DND_LEAVE instead of LEAVE
+
+/**
+ Sets the widget that is below the mouse. This is for
+ highlighting buttons. It is not used to send FL_PUSH or
+ FL_MOVE directly, for several obscure reasons, but those events
+ typically go to this widget. This is also the first widget tried for
+ FL_SHORTCUT events.
+
+ If you change the belowmouse widget, the previous one and all
+ parents (that don't contain the new widget) are sent FL_LEAVE
+ events. Changing this does \e not send FL_ENTER to this
+ or any widget, because sending FL_ENTER is supposed to \e test
+ if the widget wants the mouse (by it returning non-zero from
+ handle()).
+*/
+void Fl::belowmouse(Fl_Widget *o) {
+ if (grab()) return; // don't do anything while grab is on
+ Fl_Widget *p = belowmouse_;
+ if (o != p) {
+ belowmouse_ = o;
+ int old_event = e_number;
+ e_number = dnd_flag ? FL_DND_LEAVE : FL_LEAVE;
+ for (; p && !p->contains(o); p = p->parent()) {
+ p->handle(e_number);
+ }
+ e_number = old_event;
+ }
+}
+
+/**
+ Sets the widget that is being pushed. FL_DRAG or
+ FL_RELEASE (and any more FL_PUSH) events will be sent to
+ this widget.
+
+ If you change the pushed widget, the previous one and all parents
+ (that don't contain the new widget) are sent FL_RELEASE
+ events. Changing this does \e not send FL_PUSH to this
+ or any widget, because sending FL_PUSH is supposed to \e test
+ if the widget wants the mouse (by it returning non-zero from
+ handle()).
+*/
+ void Fl::pushed(Fl_Widget *o) {
+ pushed_ = o;
+}
+
+static void nothing(Fl_Widget *) {}
+void (*Fl_Tooltip::enter)(Fl_Widget *) = nothing;
+void (*Fl_Tooltip::exit)(Fl_Widget *) = nothing;
+
+// Update modal(), focus() and other state according to system state,
+// and send FL_ENTER, FL_LEAVE, FL_FOCUS, and/or FL_UNFOCUS events.
+// This is the only function that produces these events in response
+// to system activity.
+// This is called whenever a window is added or hidden, and whenever
+// X says the focus or mouse window have changed.
+
+void fl_fix_focus() {
+#ifdef DEBUG
+ puts("fl_fix_focus();");
+#endif // DEBUG
+
+ if (Fl::grab()) return; // don't do anything while grab is on.
+
+ // set focus based on Fl::modal() and fl_xfocus
+ Fl_Widget* w = fl_xfocus;
+ if (w) {
+ int saved = Fl::e_keysym;
+ if (Fl::e_keysym < (FL_Button + FL_LEFT_MOUSE) ||
+ Fl::e_keysym > (FL_Button + FL_RIGHT_MOUSE))
+ Fl::e_keysym = 0; // make sure widgets don't think a keystroke moved focus
+ while (w->parent()) w = w->parent();
+ if (Fl::modal()) w = Fl::modal();
+ if (!w->contains(Fl::focus()))
+ if (!w->take_focus()) Fl::focus(w);
+ Fl::e_keysym = saved;
+ } else
+ Fl::focus(0);
+
+// MRS: Originally we checked the button state, but a user reported that it
+// broke click-to-focus in FLWM?!?
+// if (!(Fl::event_state() & 0x7f00000 /*FL_BUTTONS*/)) {
+ if (!Fl::pushed()) {
+ // set belowmouse based on Fl::modal() and fl_xmousewin:
+ w = fl_xmousewin;
+ if (w) {
+ if (Fl::modal()) w = Fl::modal();
+ if (!w->contains(Fl::belowmouse())) {
+ int old_event = Fl::e_number;
+ w->handle(Fl::e_number = FL_ENTER);
+ Fl::e_number = old_event;
+ if (!w->contains(Fl::belowmouse())) Fl::belowmouse(w);
+ } else {
+ // send a FL_MOVE event so the enter/leave state is up to date
+ Fl::e_x = Fl::e_x_root-fl_xmousewin->x();
+ Fl::e_y = Fl::e_y_root-fl_xmousewin->y();
+ int old_event = Fl::e_number;
+ w->handle(Fl::e_number = FL_MOVE);
+ Fl::e_number = old_event;
+ }
+ } else {
+ Fl::belowmouse(0);
+ Fl_Tooltip::enter(0);
+ }
+ }
+}
+
+#if !(defined(WIN32) || defined(__APPLE__))
+extern Fl_Widget *fl_selection_requestor; // from Fl_x.cxx
+#endif
+
+// This function is called by ~Fl_Widget() and by Fl_Widget::deactivate()
+// and by Fl_Widget::hide(). It indicates that the widget does not want
+// to receive any more events, and also removes all global variables that
+// point at the widget.
+// I changed this from the 1.0.1 behavior, the older version could send
+// FL_LEAVE or FL_UNFOCUS events to the widget. This appears to not be
+// desirable behavior and caused flwm to crash.
+
+void fl_throw_focus(Fl_Widget *o) {
+#ifdef DEBUG
+ printf("fl_throw_focus(o=%p)\n", o);
+#endif // DEBUG
+
+ if (o->contains(Fl::pushed())) Fl::pushed_ = 0;
+#if !(defined(WIN32) || defined(__APPLE__))
+ if (o->contains(fl_selection_requestor)) fl_selection_requestor = 0;
+#endif
+ if (o->contains(Fl::belowmouse())) Fl::belowmouse_ = 0;
+ if (o->contains(Fl::focus())) Fl::focus_ = 0;
+ if (o == fl_xfocus) fl_xfocus = 0;
+ if (o == Fl_Tooltip::current()) Fl_Tooltip::current(0);
+ if (o == fl_xmousewin) fl_xmousewin = 0;
+ Fl_Tooltip::exit(o);
+ fl_fix_focus();
+}
+
+////////////////////////////////////////////////////////////////
+
+// Call to->handle(), but first replace the mouse x/y with the correct
+// values to account for nested windows. 'window' is the outermost
+// window the event was posted to by the system:
+static int send(int event, Fl_Widget* to, Fl_Window* window) {
+ int dx, dy;
+ int old_event = Fl::e_number;
+ if (window) {
+ dx = window->x();
+ dy = window->y();
+ } else {
+ dx = dy = 0;
+ }
+ for (const Fl_Widget* w = to; w; w = w->parent())
+ if (w->type()>=FL_WINDOW) {dx -= w->x(); dy -= w->y();}
+ int save_x = Fl::e_x; Fl::e_x += dx;
+ int save_y = Fl::e_y; Fl::e_y += dy;
+ int ret = to->handle(Fl::e_number = event);
+ Fl::e_number = old_event;
+ Fl::e_y = save_y;
+ Fl::e_x = save_x;
+ return ret;
+}
+
+
+/**
+ \brief Set a new event dispatch function.
+
+ The event dispatch function is called after native events are converted to
+ FLTK events, but before they are handled by FLTK. If the dispatch function
+ Fl_Event_Dispatch \p d is set, it is up to the dispatch function to call
+ Fl::handle_(int, Fl_Window*) or to ignore the event.
+
+ The dispatch function itself must return 0 if it ignored the event,
+ or non-zero if it used the event. If you call Fl::handle_(), then
+ this will return the correct value.
+
+ The event dispatch can be used to handle exceptions in FLTK events and
+ callbacks before they reach the native event handler:
+
+ \code
+ int myHandler(int e, Fl_Window *w) {
+ try {
+ return Fl::handle_(e, w);
+ } catch () {
+ ...
+ }
+ }
+
+ main() {
+ Fl::event_dispatch(myHandler);
+ ...
+ Fl::run();
+ }
+ \endcode
+
+ \param d new dispatch function, or NULL
+ \see Fl::add_handler(Fl_Event_Handler)
+ \see Fl::handle(int, Fl_Window*)
+ \see Fl::handle_(int, Fl_Window*)
+ */
+void Fl::event_dispatch(Fl_Event_Dispatch d)
+{
+ e_dispatch = d;
+}
+
+
+/**
+ \brief Return the current event dispatch function.
+ */
+Fl_Event_Dispatch Fl::event_dispatch()
+{
+ return e_dispatch;
+}
+
+
+/**
+ \brief Handle events from the window system.
+
+ This is called from the native event dispatch after native events have been
+ converted to FLTK notation. This function calls Fl::handle_(int, Fl_Window*)
+ unless the user sets a dispatch function. If a user dispatch function is set,
+ the user must make sure that Fl::handle_() is called, or the event will be
+ ignored.
+
+ \param e the event type (Fl::event_number() is not yet set)
+ \param window the window that caused this event
+ \return 0 if the event was not handled
+
+ \see Fl::add_handler(Fl_Event_Handler)
+ \see Fl::event_dispatch(Fl_Event_Dispatch)
+ */
+int Fl::handle(int e, Fl_Window* window)
+{
+ if (e_dispatch) {
+ return e_dispatch(e, window);
+ } else {
+ return handle_(e, window);
+ }
+}
+
+
+/**
+ \brief Handle events from the window system.
+
+ This function is called from the native event dispatch, unless the user sets
+ another dispatch function. In that case, the user dispatch function must
+ decide when to call Fl::handle_(int, Fl_Window*)
+
+ \param e the event type (Fl::event_number() is not yet set)
+ \param window the window that caused this event
+ \return 0 if the event was not handled
+
+ \see Fl::event_dispatch(Fl_Event_Dispatch)
+ */
+int Fl::handle_(int e, Fl_Window* window)
+{
+ e_number = e;
+ if (fl_local_grab) return fl_local_grab(e);
+
+ Fl_Widget* wi = window;
+
+ switch (e) {
+
+ case FL_CLOSE:
+ if ( grab() || (modal() && window != modal()) ) return 0;
+ wi->do_callback();
+ return 1;
+
+ case FL_SHOW:
+ wi->Fl_Widget::show(); // this calls Fl_Widget::show(), not Fl_Window::show()
+ return 1;
+
+ case FL_HIDE:
+ wi->Fl_Widget::hide(); // this calls Fl_Widget::hide(), not Fl_Window::hide()
+ return 1;
+
+ case FL_PUSH:
+#ifdef DEBUG
+ printf("Fl::handle(e=%d, window=%p);\n", e, window);
+#endif // DEBUG
+
+ if (grab()) wi = grab();
+ else if (modal() && wi != modal()) return 0;
+ pushed_ = wi;
+ Fl_Tooltip::current(wi);
+ if (send(e, wi, window)) return 1;
+ // raise windows that are clicked on:
+ window->show();
+ return 1;
+
+ case FL_DND_ENTER:
+ case FL_DND_DRAG:
+ dnd_flag = 1;
+ break;
+
+ case FL_DND_LEAVE:
+ dnd_flag = 1;
+ belowmouse(0);
+ dnd_flag = 0;
+ return 1;
+
+ case FL_DND_RELEASE:
+ wi = belowmouse();
+ break;
+
+ case FL_MOVE:
+ case FL_DRAG:
+ fl_xmousewin = window; // this should already be set, but just in case.
+ if (pushed()) {
+ wi = pushed();
+ if (grab()) wi = grab();
+ e_number = e = FL_DRAG;
+ break;
+ }
+ if (modal() && wi != modal()) wi = 0;
+ if (grab()) wi = grab();
+ { int ret;
+ Fl_Widget* pbm = belowmouse();
+#ifdef __APPLE__
+ if (fl_mac_os_version < 100500) {
+ // before 10.5, mouse moved events aren't sent to borderless windows such as tooltips
+ Fl_Window *tooltip = Fl_Tooltip::current_window();
+ int inside = 0;
+ if (tooltip && tooltip->shown() ) { // check if a tooltip window is currently opened
+ // check if mouse is inside the tooltip
+ inside = (Fl::event_x_root() >= tooltip->x() && Fl::event_x_root() < tooltip->x() + tooltip->w() &&
+ Fl::event_y_root() >= tooltip->y() && Fl::event_y_root() < tooltip->y() + tooltip->h() );
+ }
+ // if inside, send event to tooltip window instead of background window
+ if (inside) ret = send(e, tooltip, window);
+ else ret = (wi && send(e, wi, window));
+ } else
+#endif
+ ret = (wi && send(e, wi, window));
+ if (pbm != belowmouse()) {
+#ifdef DEBUG
+ printf("Fl::handle(e=%d, window=%p);\n", e, window);
+#endif // DEBUG
+ Fl_Tooltip::enter(belowmouse());
+ }
+ return ret;}
+
+ case FL_RELEASE: {
+// printf("FL_RELEASE: window=%p, pushed() = %p, grab() = %p, modal() = %p\n",
+// window, pushed(), grab(), modal());
+
+ if (grab()) {
+ wi = grab();
+ pushed_ = 0; // must be zero before callback is done!
+ } else if (pushed()) {
+ wi = pushed();
+ pushed_ = 0; // must be zero before callback is done!
+ } else if (modal() && wi != modal()) return 0;
+ int r = send(e, wi, window);
+ fl_fix_focus();
+ return r;}
+
+ case FL_UNFOCUS:
+ window = 0;
+ case FL_FOCUS:
+ fl_xfocus = window;
+ fl_fix_focus();
+ return 1;
+
+ case FL_KEYUP:
+ // Send the key-up to the current focus widget. This is not
+ // always the same widget that received the corresponding
+ // FL_KEYBOARD event because focus may have changed.
+ // Sending the KEYUP to the right KEYDOWN is possible, but
+ // would require that we track the KEYDOWN for every possible
+ // key stroke (users may hold down multiple keys!) and then
+ // make sure that the widget still exists before sending
+ // a KEYUP there. I believe that the current solution is
+ // "close enough".
+ for (wi = grab() ? grab() : focus(); wi; wi = wi->parent())
+ if (send(FL_KEYUP, wi, window)) return 1;
+ return 0;
+
+ case FL_KEYBOARD:
+#ifdef DEBUG
+ printf("Fl::handle(e=%d, window=%p);\n", e, window);
+#endif // DEBUG
+
+ Fl_Tooltip::enter((Fl_Widget*)0);
+
+ fl_xfocus = window; // this should not happen! But maybe it does:
+
+ // Try it as keystroke, sending it to focus and all parents:
+ for (wi = grab() ? grab() : focus(); wi; wi = wi->parent())
+ if (send(FL_KEYBOARD, wi, window)) return 1;
+
+ // recursive call to try shortcut:
+ if (handle(FL_SHORTCUT, window)) return 1;
+
+ // and then try a shortcut with the case of the text swapped, by
+ // changing the text and falling through to FL_SHORTCUT case:
+ {unsigned char* c = (unsigned char*)event_text(); // cast away const
+ if (!isalpha(*c)) return 0;
+ *c = isupper(*c) ? tolower(*c) : toupper(*c);}
+ e_number = e = FL_SHORTCUT;
+
+ case FL_SHORTCUT:
+ if (grab()) {wi = grab(); break;} // send it to grab window
+
+ // Try it as shortcut, sending to mouse widget and all parents:
+ wi = belowmouse();
+ if (!wi) {
+ wi = modal();
+ if (!wi) wi = window;
+ } else if (wi->window() != first_window()) {
+ if (send(FL_SHORTCUT, first_window(), first_window())) return 1;
+ }
+
+ for (; wi; wi = wi->parent()) {
+ if (send(FL_SHORTCUT, wi, wi->window())) return 1;
+ }
+
+ // try using add_handle() functions:
+ if (send_handlers(FL_SHORTCUT)) return 1;
+
+ // make Escape key close windows:
+ if (event_key()==FL_Escape) {
+ wi = modal(); if (!wi) wi = window;
+ wi->do_callback();
+ return 1;
+ }
+
+ return 0;
+
+ case FL_ENTER:
+#ifdef DEBUG
+ printf("Fl::handle(e=%d, window=%p);\n", e, window);
+#endif // DEBUG
+
+ fl_xmousewin = window;
+ fl_fix_focus();
+ Fl_Tooltip::enter(belowmouse());
+ return 1;
+
+ case FL_LEAVE:
+#ifdef DEBUG
+ printf("Fl::handle(e=%d, window=%p);\n", e, window);
+#endif // DEBUG
+
+ if (!pushed_) {
+ belowmouse(0);
+ Fl_Tooltip::enter(0);
+ }
+ if (window == fl_xmousewin) {fl_xmousewin = 0; fl_fix_focus();}
+ return 1;
+
+ case FL_MOUSEWHEEL:
+ fl_xfocus = window; // this should not happen! But maybe it does:
+
+ // Try sending it to the "grab" first
+ if (grab() && grab()!=modal() && grab()!=window) {
+ if (send(FL_MOUSEWHEEL, grab(), window)) return 1;
+ }
+ // Now try sending it to the "modal" window
+ if (modal()) {
+ send(FL_MOUSEWHEEL, modal(), window);
+ return 1;
+ }
+ // Finally try sending it to the window, the event occured in
+ if (send(FL_MOUSEWHEEL, window, window)) return 1;
+ default:
+ break;
+ }
+ if (wi && send(e, wi, window)) {
+ dnd_flag = 0;
+ return 1;
+ }
+ dnd_flag = 0;
+ return send_handlers(e);
+}
+
+////////////////////////////////////////////////////////////////
+// hide() destroys the X window, it does not do unmap!
+
+#if !defined(WIN32) && USE_XFT
+extern void fl_destroy_xft_draw(Window);
+#endif
+
+void Fl_Window::hide() {
+ clear_visible();
+
+ if (!shown()) return;
+
+ // remove from the list of windows:
+ Fl_X* ip = i;
+ Fl_X** pp = &Fl_X::first;
+ for (; *pp != ip; pp = &(*pp)->next) if (!*pp) return;
+ *pp = ip->next;
+#ifdef __APPLE__
+ ip->unlink();
+ // MacOS X manages a single pointer per application. Make sure that hiding
+ // a toplevel window will not leave us with some random pointer shape, or
+ // worst case, an invisible pointer
+ if (!parent()) cursor(FL_CURSOR_DEFAULT);
+#endif
+ i = 0;
+
+ // recursively remove any subwindows:
+ for (Fl_X *wi = Fl_X::first; wi;) {
+ Fl_Window* W = wi->w;
+ if (W->window() == this) {
+ W->hide();
+ W->set_visible();
+ wi = Fl_X::first;
+ } else wi = wi->next;
+ }
+
+ if (this == Fl::modal_) { // we are closing the modal window, find next one:
+ Fl_Window* W;
+ for (W = Fl::first_window(); W; W = Fl::next_window(W))
+ if (W->modal()) break;
+ Fl::modal_ = W;
+ }
+
+ // Make sure no events are sent to this window:
+ fl_throw_focus(this);
+ handle(FL_HIDE);
+
+#if defined(WIN32)
+ // this little trick keeps the current clipboard alive, even if we are about
+ // to destroy the window that owns the selection.
+ if (GetClipboardOwner()==ip->xid) {
+ Fl_Window *w1 = Fl::first_window();
+ if (w1 && OpenClipboard(fl_xid(w1))) {
+ EmptyClipboard();
+ SetClipboardData(CF_TEXT, NULL);
+ CloseClipboard();
+ }
+ }
+ // Send a message to myself so that I'll get out of the event loop...
+ PostMessage(ip->xid, WM_APP, 0, 0);
+ if (ip->private_dc) fl_release_dc(ip->xid, ip->private_dc);
+ if (ip->xid == fl_window && fl_gc) {
+ fl_release_dc(fl_window, fl_gc);
+ fl_window = (HWND)-1;
+ fl_gc = 0;
+# ifdef FLTK_USE_CAIRO
+ if (Fl::cairo_autolink_context()) Fl::cairo_make_current((Fl_Window*) 0);
+# endif
+ }
+#elif defined(__APPLE_QUARTZ__)
+ Fl_X::q_release_context(ip);
+ if ( ip->xid == fl_window && !parent() )
+ fl_window = 0;
+#endif
+
+ if (ip->region) XDestroyRegion(ip->region);
+
+#if defined(USE_X11)
+# if USE_XFT
+ fl_destroy_xft_draw(ip->xid);
+# endif
+ // this test makes sure ip->xid has not been destroyed already
+ if (ip->xid) XDestroyWindow(fl_display, ip->xid);
+#elif defined(WIN32)
+ // this little trickery seems to avoid the popup window stacking problem
+ HWND p = GetForegroundWindow();
+ if (p==GetParent(ip->xid)) {
+ ShowWindow(ip->xid, SW_HIDE);
+ ShowWindow(p, SW_SHOWNA);
+ }
+ XDestroyWindow(fl_display, ip->xid);
+#elif defined(__APPLE_QUARTZ__)
+ ip->destroy();
+#else
+# error unsupported platform
+#endif
+
+#ifdef WIN32
+ // Try to stop the annoying "raise another program" behavior
+ if (non_modal() && Fl::first_window() && Fl::first_window()->shown())
+ Fl::first_window()->show();
+#endif
+ delete ip;
+}
+
+Fl_Window::~Fl_Window() {
+ hide();
+ if (xclass_) {
+ free(xclass_);
+ }
+}
+
+// FL_SHOW and FL_HIDE are called whenever the visibility of this widget
+// or any parent changes. We must correctly map/unmap the system's window.
+
+// For top-level windows it is assumed the window has already been
+// mapped or unmapped!!! This is because this should only happen when
+// Fl_Window::show() or Fl_Window::hide() is called, or in response to
+// iconize/deiconize events from the system.
+
+int Fl_Window::handle(int ev)
+{
+ if (parent()) {
+ switch (ev) {
+ case FL_SHOW:
+ if (!shown()) show();
+ else {
+#if defined(USE_X11) || defined(WIN32)
+ XMapWindow(fl_display, fl_xid(this)); // extra map calls are harmless
+#elif defined(__APPLE_QUARTZ__)
+ i->map();
+#else
+# error unsupported platform
+#endif // __APPLE__
+ }
+ break;
+ case FL_HIDE:
+ if (shown()) {
+ // Find what really turned invisible, if it was a parent window
+ // we do nothing. We need to avoid unnecessary unmap calls
+ // because they cause the display to blink when the parent is
+ // remapped. However if this or any intermediate non-window
+ // widget has really had hide() called directly on it, we must
+ // unmap because when the parent window is remapped we don't
+ // want to reappear.
+ if (visible()) {
+ Fl_Widget* p = parent(); for (;p->visible();p = p->parent()) {}
+ if (p->type() >= FL_WINDOW) break; // don't do the unmap
+ }
+#if defined(USE_X11) || defined(WIN32)
+ XUnmapWindow(fl_display, fl_xid(this));
+#elif defined(__APPLE_QUARTZ__)
+ i->unmap();
+#else
+# error platform unsupported
+#endif
+ }
+ break;
+ }
+// } else if (ev == FL_FOCUS || ev == FL_UNFOCUS) {
+// Fl_Tooltip::exit(Fl_Tooltip::current());
+ }
+
+ return Fl_Group::handle(ev);
+}
+
+////////////////////////////////////////////////////////////////
+// Back compatibility cut & paste functions for fltk 1.1 only:
+
+/** Back-compatibility only: The single-argument call can be used to
+ move the selection to another widget or to set the owner to
+ NULL, without changing the actual text of the
+ selection. FL_SELECTIONCLEAR is sent to the previous
+ selection owner, if any.
+
+ <i>Copying the buffer every time the selection is changed is
+ obviously wasteful, especially for large selections. An interface will
+ probably be added in a future version to allow the selection to be made
+ by a callback function. The current interface will be emulated on top
+ of this.</i>
+*/
+void Fl::selection_owner(Fl_Widget *owner) {selection_owner_ = owner;}
+
+/**
+ Changes the current selection. The block of text is
+ copied to an internal buffer by FLTK (be careful if doing this in
+ response to an FL_PASTE as this \e may be the same buffer
+ returned by event_text()). The selection_owner()
+ widget is set to the passed owner.
+*/
+void Fl::selection(Fl_Widget &owner, const char* text, int len) {
+ selection_owner_ = &owner;
+ Fl::copy(text, len, 0);
+}
+
+/** Backward compatibility only.
+ This calls Fl::paste(receiver, 0);
+ \see Fl::paste(Fl_Widget &receiver, int clipboard)
+*/
+void Fl::paste(Fl_Widget &receiver) {
+ Fl::paste(receiver, 0);
+}
+
+////////////////////////////////////////////////////////////////
+
+#include <FL/fl_draw.H>
+
+void Fl_Widget::redraw() {
+ damage(FL_DAMAGE_ALL);
+}
+
+void Fl_Widget::redraw_label() {
+ if (window()) {
+ if (box() == FL_NO_BOX) {
+ // Widgets with the FL_NO_BOX boxtype need a parent to
+ // redraw, since it is responsible for redrawing the
+ // background...
+ int X = x() > 0 ? x() - 1 : 0;
+ int Y = y() > 0 ? y() - 1 : 0;
+ window()->damage(FL_DAMAGE_ALL, X, Y, w() + 2, h() + 2);
+ }
+
+ if (align() && !(align() & FL_ALIGN_INSIDE) && window()->shown()) {
+ // If the label is not inside the widget, compute the location of
+ // the label and redraw the window within that bounding box...
+ int W = 0, H = 0;
+ label_.measure(W, H);
+ W += 5; // Add a little to the size of the label to cover overflow
+ H += 5;
+
+ // FIXME:
+ // This assumes that measure() returns the correct outline, which it does
+ // not in all possible cases of alignment combinedwith image and symbols.
+ switch (align() & 0x0f) {
+ case FL_ALIGN_TOP_LEFT:
+ window()->damage(FL_DAMAGE_EXPOSE, x(), y()-H, W, H); break;
+ case FL_ALIGN_TOP:
+ window()->damage(FL_DAMAGE_EXPOSE, x()+(w()-W)/2, y()-H, W, H); break;
+ case FL_ALIGN_TOP_RIGHT:
+ window()->damage(FL_DAMAGE_EXPOSE, x()+w()-W, y()-H, W, H); break;
+ case FL_ALIGN_LEFT_TOP:
+ window()->damage(FL_DAMAGE_EXPOSE, x()-W, y(), W, H); break;
+ case FL_ALIGN_RIGHT_TOP:
+ window()->damage(FL_DAMAGE_EXPOSE, x()+w(), y(), W, H); break;
+ case FL_ALIGN_LEFT:
+ window()->damage(FL_DAMAGE_EXPOSE, x()-W, y()+(h()-H)/2, W, H); break;
+ case FL_ALIGN_RIGHT:
+ window()->damage(FL_DAMAGE_EXPOSE, x()+w(), y()+(h()-H)/2, W, H); break;
+ case FL_ALIGN_LEFT_BOTTOM:
+ window()->damage(FL_DAMAGE_EXPOSE, x()-W, y()+h()-H, W, H); break;
+ case FL_ALIGN_RIGHT_BOTTOM:
+ window()->damage(FL_DAMAGE_EXPOSE, x()+w(), y()+h()-H, W, H); break;
+ case FL_ALIGN_BOTTOM_LEFT:
+ window()->damage(FL_DAMAGE_EXPOSE, x(), y()+h(), W, H); break;
+ case FL_ALIGN_BOTTOM:
+ window()->damage(FL_DAMAGE_EXPOSE, x()+(w()-W)/2, y()+h(), W, H); break;
+ case FL_ALIGN_BOTTOM_RIGHT:
+ window()->damage(FL_DAMAGE_EXPOSE, x()+w()-W, y()+h(), W, H); break;
+ default:
+ window()->damage(FL_DAMAGE_ALL); break;
+ }
+ } else {
+ // The label is inside the widget, so just redraw the widget itself...
+ damage(FL_DAMAGE_ALL);
+ }
+ }
+}
+
+void Fl_Widget::damage(uchar fl) {
+ if (type() < FL_WINDOW) {
+ // damage only the rectangle covered by a child widget:
+ damage(fl, x(), y(), w(), h());
+ } else {
+ // damage entire window by deleting the region:
+ Fl_X* i = Fl_X::i((Fl_Window*)this);
+ if (!i) return; // window not mapped, so ignore it
+ if (i->region) {XDestroyRegion(i->region); i->region = 0;}
+ damage_ |= fl;
+ Fl::damage(FL_DAMAGE_CHILD);
+ }
+}
+
+void Fl_Widget::damage(uchar fl, int X, int Y, int W, int H) {
+ Fl_Widget* wi = this;
+ // mark all parent widgets between this and window with FL_DAMAGE_CHILD:
+ while (wi->type() < FL_WINDOW) {
+ wi->damage_ |= fl;
+ wi = wi->parent();
+ if (!wi) return;
+ fl = FL_DAMAGE_CHILD;
+ }
+ Fl_X* i = Fl_X::i((Fl_Window*)wi);
+ if (!i) return; // window not mapped, so ignore it
+
+ // clip the damage to the window and quit if none:
+ if (X < 0) {W += X; X = 0;}
+ if (Y < 0) {H += Y; Y = 0;}
+ if (W > wi->w()-X) W = wi->w()-X;
+ if (H > wi->h()-Y) H = wi->h()-Y;
+ if (W <= 0 || H <= 0) return;
+
+ if (!X && !Y && W==wi->w() && H==wi->h()) {
+ // if damage covers entire window delete region:
+ wi->damage(fl);
+ return;
+ }
+
+ if (wi->damage()) {
+ // if we already have damage we must merge with existing region:
+ if (i->region) {
+#if defined(USE_X11)
+ XRectangle R;
+ R.x = X; R.y = Y; R.width = W; R.height = H;
+ XUnionRectWithRegion(&R, i->region, i->region);
+#elif defined(WIN32)
+ Fl_Region R = XRectangleRegion(X, Y, W, H);
+ CombineRgn(i->region, i->region, R, RGN_OR);
+ XDestroyRegion(R);
+#elif defined(__APPLE_QUARTZ__)
+ CGRect arg = fl_cgrectmake_cocoa(X, Y, W, H);
+ int j; // don't add a rectangle totally inside the Fl_Region
+ for(j = 0; j < i->region->count; j++) {
+ if(CGRectContainsRect(i->region->rects[j], arg)) break;
+ }
+ if( j >= i->region->count) {
+ i->region->rects = (CGRect*)realloc(i->region->rects, (++(i->region->count)) * sizeof(CGRect));
+ i->region->rects[i->region->count - 1] = arg;
+ }
+#else
+# error unsupported platform
+#endif
+ }
+ wi->damage_ |= fl;
+ } else {
+ // create a new region:
+ if (i->region) XDestroyRegion(i->region);
+ i->region = XRectangleRegion(X,Y,W,H);
+ wi->damage_ = fl;
+ }
+ Fl::damage(FL_DAMAGE_CHILD);
+}
+void Fl_Window::flush() {
+ make_current();
+//if (damage() == FL_DAMAGE_EXPOSE && can_boxcheat(box())) fl_boxcheat = this;
+ fl_clip_region(i->region); i->region = 0;
+ draw();
+}
+
+#ifdef WIN32
+# include "Fl_win32.cxx"
+//#elif defined(__APPLE__)
+#endif
+
+//
+// The following methods allow callbacks to schedule the deletion of
+// widgets at "safe" times.
+//
+
+static int num_dwidgets = 0, alloc_dwidgets = 0;
+static Fl_Widget **dwidgets = 0;
+
+/**
+ Schedules a widget for deletion at the next call to the event loop.
+ Use this method to delete a widget inside a callback function.
+
+ To avoid early deletion of widgets, this function should be called
+ toward the end of a callback and only after any call to the event
+ loop (Fl::wait(), Fl::flush(), Fl::check(), fl_ask(), etc.).
+
+ When deleting groups or windows, you must only delete the group or
+ window widget and not the individual child widgets.
+
+ \since FLTK 1.3 it is not necessary to remove widgets from their parent
+ groups or windows before calling this, because it will be done in the
+ widget's destructor, but it is not a failure to do this nevertheless.
+
+ \note In FLTK 1.1 you \b must remove widgets from their parent group
+ (or window) before deleting them.
+
+ \see Fl_Widget::~Fl_Widget()
+*/
+void Fl::delete_widget(Fl_Widget *wi) {
+ if (!wi) return;
+
+ if (num_dwidgets >= alloc_dwidgets) {
+ Fl_Widget **temp;
+
+ temp = new Fl_Widget *[alloc_dwidgets + 10];
+ if (alloc_dwidgets) {
+ memcpy(temp, dwidgets, alloc_dwidgets * sizeof(Fl_Widget *));
+ delete[] dwidgets;
+ }
+
+ dwidgets = temp;
+ alloc_dwidgets += 10;
+ }
+
+ dwidgets[num_dwidgets] = wi;
+ num_dwidgets ++;
+}
+
+/**
+ Deletes widgets previously scheduled for deletion.
+
+ This is for internal use only. You should never call this directly.
+
+ Fl::do_widget_deletion() is called from the FLTK event loop or whenever
+ you call Fl::wait(). The previously scheduled widgets are deleted in the
+ same order they were scheduled by calling Fl::delete_widget().
+
+ \see Fl::delete_widget(Fl_Widget *wi)
+*/
+void Fl::do_widget_deletion() {
+ if (!num_dwidgets) return;
+
+ for (int i = 0; i < num_dwidgets; i ++)
+ delete dwidgets[i];
+
+ num_dwidgets = 0;
+}
+
+static Fl_Widget ***widget_watch = 0;
+static int num_widget_watch = 0;
+static int max_widget_watch = 0;
+
+/**
+ Adds a widget pointer to the widget watch list.
+
+ \note Internal use only, please use class Fl_Widget_Tracker instead.
+
+ This can be used, if it is possible that a widget might be deleted during
+ a callback or similar function. The widget pointer must be added to the
+ watch list before calling the callback. After the callback the widget
+ pointer can be queried, if it is NULL. \e If it is NULL, then the widget has been
+ deleted during the callback and must not be accessed anymore. If the widget
+ pointer is \e not NULL, then the widget has not been deleted and can be accessed
+ safely.
+
+ After accessing the widget, the widget pointer must be released from the
+ watch list by calling Fl::release_widget_pointer().
+
+ Example for a button that is clicked (from its handle() method):
+ \code
+ Fl_Widget *wp = this; // save 'this' in a pointer variable
+ Fl::watch_widget_pointer(wp); // add the pointer to the watch list
+ set_changed(); // set the changed flag
+ do_callback(); // call the callback
+ if (!wp) { // the widget has been deleted
+
+ // DO NOT ACCESS THE DELETED WIDGET !
+
+ } else { // the widget still exists
+ clear_changed(); // reset the changed flag
+ }
+
+ Fl::release_widget_pointer(wp); // remove the pointer from the watch list
+ \endcode
+
+ This works, because all widgets call Fl::clear_widget_pointer() in their
+ destructors.
+
+ \see Fl::release_widget_pointer()
+ \see Fl::clear_widget_pointer()
+
+ An easier and more convenient method to control widget deletion during
+ callbacks is to use the class Fl_Widget_Tracker with a local (automatic)
+ variable.
+
+ \see class Fl_Widget_Tracker
+*/
+void Fl::watch_widget_pointer(Fl_Widget *&w)
+{
+ Fl_Widget **wp = &w;
+ int i;
+ for (i=0; i<num_widget_watch; ++i) {
+ if (widget_watch[i]==wp) return;
+ }
+ if (num_widget_watch==max_widget_watch) {
+ max_widget_watch += 8;
+ widget_watch = (Fl_Widget***)realloc(widget_watch, sizeof(Fl_Widget**)*max_widget_watch);
+ }
+ widget_watch[num_widget_watch++] = wp;
+#ifdef DEBUG_WATCH
+ printf ("\nwatch_widget_pointer: (%d/%d) %8p => %8p\n",
+ num_widget_watch,num_widget_watch,wp,*wp);
+ fflush(stdout);
+#endif // DEBUG_WATCH
+}
+
+/**
+ Releases a widget pointer from the watch list.
+
+ This is used to remove a widget pointer that has been added to the watch list
+ with Fl::watch_widget_pointer(), when it is not needed anymore.
+
+ \note Internal use only, please use class Fl_Widget_Tracker instead.
+
+ \see Fl::watch_widget_pointer()
+*/
+void Fl::release_widget_pointer(Fl_Widget *&w)
+{
+ Fl_Widget **wp = &w;
+ int i,j=0;
+ for (i=0; i<num_widget_watch; ++i) {
+ if (widget_watch[i]!=wp) {
+ if (j<i) widget_watch[j] = widget_watch[i]; // fill gap
+ j++;
+ }
+#ifdef DEBUG_WATCH
+ else { // found widget pointer
+ printf ("release_widget_pointer: (%d/%d) %8p => %8p\n",
+ i+1,num_widget_watch,wp,*wp);
+ }
+#endif //DEBUG_WATCH
+ }
+ num_widget_watch = j;
+#ifdef DEBUG_WATCH
+ printf (" num_widget_watch = %d\n\n",num_widget_watch);
+ fflush(stdout);
+#endif // DEBUG_WATCH
+ return;
+}
+/**
+ Clears a widget pointer \e in the watch list.
+
+ This is called when a widget is destroyed (by its destructor). You should never
+ call this directly.
+
+ \note Internal use only !
+
+ This method searches the widget watch list for pointers to the widget and
+ clears each pointer that points to it. Widget pointers can be added to the
+ widget watch list by calling Fl::watch_widget_pointer() or by using the
+ helper class Fl_Widget_Tracker (recommended).
+
+ \see Fl::watch_widget_pointer()
+ \see class Fl_Widget_Tracker
+*/
+void Fl::clear_widget_pointer(Fl_Widget const *w)
+{
+ if (w==0L) return;
+ int i;
+ for (i=0; i<num_widget_watch; ++i) {
+ if (widget_watch[i] && *widget_watch[i]==w) {
+ *widget_watch[i] = 0L;
+ }
+ }
+}
+
+
+/**
+ \brief FLTK library options management.
+
+ This function needs to be documented in more detail. It can be used for more
+ optional settings, such as using a native file chooser instead of the FLTK one
+ wherever possible, disabling tooltips, disabling visible focus, disabling
+ FLTK file chooser preview, etc. .
+
+ There should be a command line option interface.
+
+ There should be an application that manages options system wide, per user, and
+ per application.
+
+ \note As of FLTK 1.3.0, options can be managed within fluid, using the menu
+ <i>Edit/Global FLTK Settings</i>.
+
+ \param opt which option
+ \return true or false
+ \see enum Fl::Fl_Option
+ \see Fl::option(Fl_Option, bool)
+
+ \since FLTK 1.3.0
+ */
+bool Fl::option(Fl_Option opt)
+{
+ if (!options_read_) {
+ int tmp;
+ { // first, read the system wide preferences
+ Fl_Preferences prefs(Fl_Preferences::SYSTEM, "fltk.org", "fltk");
+ Fl_Preferences opt_prefs(prefs, "options");
+ opt_prefs.get("ArrowFocus", tmp, 0); // default: off
+ options_[OPTION_ARROW_FOCUS] = tmp;
+ //opt_prefs.get("NativeFilechooser", tmp, 1); // default: on
+ //options_[OPTION_NATIVE_FILECHOOSER] = tmp;
+ //opt_prefs.get("FilechooserPreview", tmp, 1); // default: on
+ //options_[OPTION_FILECHOOSER_PREVIEW] = tmp;
+ opt_prefs.get("VisibleFocus", tmp, 1); // default: on
+ options_[OPTION_VISIBLE_FOCUS] = tmp;
+ opt_prefs.get("DNDText", tmp, 1); // default: on
+ options_[OPTION_DND_TEXT] = tmp;
+ opt_prefs.get("ShowTooltips", tmp, 1); // default: on
+ options_[OPTION_SHOW_TOOLTIPS] = tmp;
+ }
+ { // next, check the user preferences
+ // override system options only, if the option is set ( >= 0 )
+ Fl_Preferences prefs(Fl_Preferences::USER, "fltk.org", "fltk");
+ Fl_Preferences opt_prefs(prefs, "options");
+ opt_prefs.get("ArrowFocus", tmp, -1);
+ if (tmp >= 0) options_[OPTION_ARROW_FOCUS] = tmp;
+ //opt_prefs.get("NativeFilechooser", tmp, -1);
+ //if (tmp >= 0) options_[OPTION_NATIVE_FILECHOOSER] = tmp;
+ //opt_prefs.get("FilechooserPreview", tmp, -1);
+ //if (tmp >= 0) options_[OPTION_FILECHOOSER_PREVIEW] = tmp;
+ opt_prefs.get("VisibleFocus", tmp, -1);
+ if (tmp >= 0) options_[OPTION_VISIBLE_FOCUS] = tmp;
+ opt_prefs.get("DNDText", tmp, -1);
+ if (tmp >= 0) options_[OPTION_DND_TEXT] = tmp;
+ opt_prefs.get("ShowTooltips", tmp, -1);
+ if (tmp >= 0) options_[OPTION_SHOW_TOOLTIPS] = tmp;
+ }
+ { // now, if the developer has registered this app, we could as for per-application preferences
+ }
+ options_read_ = 1;
+ }
+ if (opt<0 || opt>=OPTION_LAST)
+ return false;
+ return (bool)(options_[opt]!=0);
+}
+
+/**
+ \brief Override an option while the application is running.
+
+ This function does not change any system or user settings.
+
+ \param opt which option
+ \param val set to true or false
+ \see enum Fl::Fl_Option
+ \see bool Fl::option(Fl_Option)
+ */
+void Fl::option(Fl_Option opt, bool val)
+{
+ if (opt<0 || opt>=OPTION_LAST)
+ return;
+ if (!options_read_) {
+ // first read this option, so we don't override our setting later
+ option(opt);
+ }
+ options_[opt] = val;
+}
+
+
+// Helper class Fl_Widget_Tracker
+
+/**
+ The constructor adds a widget to the watch list.
+*/
+Fl_Widget_Tracker::Fl_Widget_Tracker(Fl_Widget *wi)
+{
+ wp_ = wi;
+ Fl::watch_widget_pointer(wp_); // add pointer to watch list
+}
+
+/**
+ The destructor removes a widget from the watch list.
+*/
+Fl_Widget_Tracker::~Fl_Widget_Tracker()
+{
+ Fl::release_widget_pointer(wp_); // remove pointer from watch list
+}
+
+
+//
+// End of "$Id: Fl.cxx 8723 2011-05-23 16:49:02Z manolo $".
+//