blob: b0cb020f3cdf09d8f588d0d4c22ef05594c3a808 [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
2// "$Id: Fl.cxx 8723 2011-05-23 16:49:02Z manolo $"
3//
4// Main event handling code for the Fast Light Tool Kit (FLTK).
5//
6// Copyright 1998-2010 by Bill Spitzak and others.
7//
8// This library is free software; you can redistribute it and/or
9// modify it under the terms of the GNU Library General Public
10// License as published by the Free Software Foundation; either
11// version 2 of the License, or (at your option) any later version.
12//
13// This library is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// Library General Public License for more details.
17//
18// You should have received a copy of the GNU Library General Public
19// License along with this library; if not, write to the Free Software
20// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21// USA.
22//
23// Please report all bugs and problems on the following page:
24//
25// http://www.fltk.org/str.php
26//
27
28
29// warning: the Apple Quartz version still uses some Quickdraw calls,
30// mostly to get around the single active context in QD and
31// to implement clipping. This should be changed into pure
32// Quartz calls in the near future.
33#include <config.h>
34
35/* We require Windows 2000 features (e.g. VK definitions) */
36#if defined(WIN32)
37# if !defined(WINVER) || (WINVER < 0x0500)
38# ifdef WINVER
39# undef WINVER
40# endif
41# define WINVER 0x0500
42# endif
43# if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
44# ifdef _WIN32_WINNT
45# undef _WIN32_WINNT
46# endif
47# define _WIN32_WINNT 0x0500
48# endif
49#endif
50
51// recent versions of MinGW warn: "Please include winsock2.h before windows.h",
52// hence we must include winsock2.h before FL/Fl.H (A.S. Dec. 2010, IMM May 2011)
53#if defined(WIN32) && !defined(__CYGWIN__)
54# include <winsock2.h>
55#endif
56
57#include <FL/Fl.H>
58#include <FL/Fl_Window.H>
59#include <FL/Fl_Tooltip.H>
60#include <FL/x.H>
61
62#include <ctype.h>
63#include <stdio.h>
64#include <stdlib.h>
65#include "flstring.h"
66
67#if defined(DEBUG) || defined(DEBUG_WATCH)
68# include <stdio.h>
69#endif // DEBUG || DEBUG_WATCH
70
71#ifdef WIN32
72# include <ole2.h>
73void fl_free_fonts(void);
74HBRUSH fl_brush_action(int action);
75void fl_cleanup_pens(void);
76void fl_release_dc(HWND,HDC);
77void fl_cleanup_dc_list(void);
78#elif defined(__APPLE__)
79extern double fl_mac_flush_and_wait(double time_to_wait, char in_idle);
80#endif // WIN32
81
DRC685f17e2011-07-28 09:23:00 +000082extern void fl_update_focus(void);
83
DRC2ff39b82011-07-28 08:38:59 +000084//
85// Globals...
86//
87#if defined(__APPLE__) || defined(FL_DOXYGEN)
88const char *Fl_Mac_App_Menu::about = "About ";
89const char *Fl_Mac_App_Menu::print = "Print Front Window";
90const char *Fl_Mac_App_Menu::services = "Services";
91const char *Fl_Mac_App_Menu::hide = "Hide ";
92const char *Fl_Mac_App_Menu::hide_others = "Hide Others";
93const char *Fl_Mac_App_Menu::show = "Show All";
94const char *Fl_Mac_App_Menu::quit = "Quit ";
95#endif // __APPLE__
96#ifndef FL_DOXYGEN
97Fl_Widget *Fl::belowmouse_,
98 *Fl::pushed_,
99 *Fl::focus_,
100 *Fl::selection_owner_;
101int Fl::damage_,
102 Fl::e_number,
103 Fl::e_x,
104 Fl::e_y,
105 Fl::e_x_root,
106 Fl::e_y_root,
107 Fl::e_dx,
108 Fl::e_dy,
109 Fl::e_state,
110 Fl::e_clicks,
111 Fl::e_is_click,
112 Fl::e_keysym,
113 Fl::e_original_keysym,
114 Fl::scrollbar_size_ = 16;
115
116char *Fl::e_text = (char *)"";
117int Fl::e_length;
118
119Fl_Event_Dispatch Fl::e_dispatch = 0;
120
121unsigned char Fl::options_[] = { 0, 0 };
122unsigned char Fl::options_read_ = 0;
123
124
125Fl_Window *fl_xfocus; // which window X thinks has focus
126Fl_Window *fl_xmousewin;// which window X thinks has FL_ENTER
127Fl_Window *Fl::grab_; // most recent Fl::grab()
128Fl_Window *Fl::modal_; // topmost modal() window
129
130#endif // FL_DOXYGEN
131
132//
133// 'Fl::version()' - Return the API version number...
134//
135
136double
137/**
138 Returns the compiled-in value of the FL_VERSION constant. This
139 is useful for checking the version of a shared library.
140*/
141Fl::version() {
142 return FL_VERSION;
143}
144
145/**
146 Gets the default scrollbar size used by
147 Fl_Browser_,
148 Fl_Help_View,
149 Fl_Scroll, and
150 Fl_Text_Display widgets.
151 \returns The default size for widget scrollbars, in pixels.
152*/
153int Fl::scrollbar_size() {
154 return scrollbar_size_;
155}
156
157/**
158 Sets the default scrollbar size that is used by the
159 Fl_Browser_,
160 Fl_Help_View,
161 Fl_Scroll, and
162 Fl_Text_Display widgets.
163 \param[in] W The new default size for widget scrollbars, in pixels.
164*/
165void Fl::scrollbar_size(int W) {
166 scrollbar_size_ = W;
167}
168
169
170/** Returns whether or not the mouse event is inside the given rectangle.
171
172 Returns non-zero if the current Fl::event_x() and Fl::event_y()
173 put it inside the given arbitrary bounding box.
174
175 You should always call this rather than doing your own comparison
176 so you are consistent about edge effects.
177
178 To find out, whether the event is inside a child widget of the
179 current window, you can use Fl::event_inside(const Fl_Widget *).
180
181 \param[in] xx,yy,ww,hh bounding box
182 \return non-zero, if mouse event is inside
183*/
184int Fl::event_inside(int xx,int yy,int ww,int hh) /*const*/ {
185 int mx = e_x - xx;
186 int my = e_y - yy;
187 return (mx >= 0 && mx < ww && my >= 0 && my < hh);
188}
189
190/** Returns whether or not the mouse event is inside a given child widget.
191
192 Returns non-zero if the current Fl::event_x() and Fl::event_y()
193 put it inside the given child widget's bounding box.
194
195 This method can only be used to check whether the mouse event is
196 inside a \b child widget of the window that handles the event, and
197 there must not be an intermediate subwindow (i.e. the widget must
198 not be inside a subwindow of the current window). However, it is
199 valid if the widget is inside a nested Fl_Group.
200
201 You must not use it with the window itself as the \p o argument
202 in a window's handle() method.
203
204 \note The mentioned restrictions are necessary, because this method
205 does not transform coordinates of child widgets, and thus the given
206 widget \p o must be within the \e same window that is handling the
207 current event. Otherwise the results are undefined.
208
209 You should always call this rather than doing your own comparison
210 so you are consistent about edge effects.
211
212 \see Fl::event_inside(int, int, int, int)
213
214 \param[in] o child widget to be tested
215 \return non-zero, if mouse event is inside the widget
216*/
217int Fl::event_inside(const Fl_Widget *o) /*const*/ {
218 int mx = e_x - o->x();
219 int my = e_y - o->y();
220 return (mx >= 0 && mx < o->w() && my >= 0 && my < o->h());
221}
222
223//
224//
225// timer support
226//
227
228#ifdef WIN32
229
230// implementation in Fl_win32.cxx
231
232#elif defined(__APPLE__)
233
234// implementation in Fl_mac.cxx
235
236#else
237
238//
239// X11 timers
240//
241
242
243////////////////////////////////////////////////////////////////////////
244// Timeouts are stored in a sorted list (*first_timeout), so only the
245// first one needs to be checked to see if any should be called.
246// Allocated, but unused (free) Timeout structs are stored in another
247// linked list (*free_timeout).
248
249struct Timeout {
250 double time;
251 void (*cb)(void*);
252 void* arg;
253 Timeout* next;
254};
255static Timeout* first_timeout, *free_timeout;
256
257#include <sys/time.h>
258
259// I avoid the overhead of getting the current time when we have no
260// timeouts by setting this flag instead of getting the time.
261// In this case calling elapse_timeouts() does nothing, but records
262// the current time, and the next call will actually elapse time.
263static char reset_clock = 1;
264
265static void elapse_timeouts() {
266 static struct timeval prevclock;
267 struct timeval newclock;
268 gettimeofday(&newclock, NULL);
269 double elapsed = newclock.tv_sec - prevclock.tv_sec +
270 (newclock.tv_usec - prevclock.tv_usec)/1000000.0;
271 prevclock.tv_sec = newclock.tv_sec;
272 prevclock.tv_usec = newclock.tv_usec;
273 if (reset_clock) {
274 reset_clock = 0;
275 } else if (elapsed > 0) {
276 for (Timeout* t = first_timeout; t; t = t->next) t->time -= elapsed;
277 }
278}
279
280// Continuously-adjusted error value, this is a number <= 0 for how late
281// we were at calling the last timeout. This appears to make repeat_timeout
282// very accurate even when processing takes a significant portion of the
283// time interval:
284static double missed_timeout_by;
285
286void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void *argp) {
287 elapse_timeouts();
288 repeat_timeout(time, cb, argp);
289}
290
291void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void *argp) {
292 time += missed_timeout_by; if (time < -.05) time = 0;
293 Timeout* t = free_timeout;
294 if (t) {
295 free_timeout = t->next;
296 } else {
297 t = new Timeout;
298 }
299 t->time = time;
300 t->cb = cb;
301 t->arg = argp;
302 // insert-sort the new timeout:
303 Timeout** p = &first_timeout;
304 while (*p && (*p)->time <= time) p = &((*p)->next);
305 t->next = *p;
306 *p = t;
307}
308
309/**
310 Returns true if the timeout exists and has not been called yet.
311*/
312int Fl::has_timeout(Fl_Timeout_Handler cb, void *argp) {
313 for (Timeout* t = first_timeout; t; t = t->next)
314 if (t->cb == cb && t->arg == argp) return 1;
315 return 0;
316}
317
318/**
319 Removes a timeout callback. It is harmless to remove a timeout
320 callback that no longer exists.
321
322 \note This version removes all matching timeouts, not just the first one.
323 This may change in the future.
324*/
325void Fl::remove_timeout(Fl_Timeout_Handler cb, void *argp) {
326 for (Timeout** p = &first_timeout; *p;) {
327 Timeout* t = *p;
328 if (t->cb == cb && (t->arg == argp || !argp)) {
329 *p = t->next;
330 t->next = free_timeout;
331 free_timeout = t;
332 } else {
333 p = &(t->next);
334 }
335 }
336}
337
338#endif
339
340////////////////////////////////////////////////////////////////
341// Checks are just stored in a list. They are called in the reverse
342// order that they were added (this may change in the future).
343// This is a bit messy because I want to allow checks to be added,
344// removed, and have wait() called from inside them. To do this
345// next_check points at the next unprocessed one for the outermost
346// call to Fl::wait().
347
348struct Check {
349 void (*cb)(void*);
350 void* arg;
351 Check* next;
352};
353static Check *first_check, *next_check, *free_check;
354
355/**
356 FLTK will call this callback just before it flushes the display and
357 waits for events. This is different than an idle callback because it
358 is only called once, then FLTK calls the system and tells it not to
359 return until an event happens.
360
361 This can be used by code that wants to monitor the
362 application's state, such as to keep a display up to date. The
363 advantage of using a check callback is that it is called only when no
364 events are pending. If events are coming in quickly, whole blocks of
365 them will be processed before this is called once. This can save
366 significant time and avoid the application falling behind the events.
367
368 Sample code:
369
370 \code
371 bool state_changed; // anything that changes the display turns this on
372
373 void callback(void*) {
374 if (!state_changed) return;
375 state_changed = false;
376 do_expensive_calculation();
377 widget-&gt;redraw();
378 }
379
380 main() {
381 Fl::add_check(callback);
382 return Fl::run();
383 }
384 \endcode
385*/
386void Fl::add_check(Fl_Timeout_Handler cb, void *argp) {
387 Check* t = free_check;
388 if (t) free_check = t->next;
389 else t = new Check;
390 t->cb = cb;
391 t->arg = argp;
392 t->next = first_check;
393 if (next_check == first_check) next_check = t;
394 first_check = t;
395}
396
397/**
398 Removes a check callback. It is harmless to remove a check
399 callback that no longer exists.
400*/
401void Fl::remove_check(Fl_Timeout_Handler cb, void *argp) {
402 for (Check** p = &first_check; *p;) {
403 Check* t = *p;
404 if (t->cb == cb && t->arg == argp) {
405 if (next_check == t) next_check = t->next;
406 *p = t->next;
407 t->next = free_check;
408 free_check = t;
409 } else {
410 p = &(t->next);
411 }
412 }
413}
414
415/**
416 Returns 1 if the check exists and has not been called yet, 0 otherwise.
417*/
418int Fl::has_check(Fl_Timeout_Handler cb, void *argp) {
419 for (Check** p = &first_check; *p;) {
420 Check* t = *p;
421 if (t->cb == cb && t->arg == argp) {
422 return 1;
423 } else {
424 p = &(t->next);
425 }
426 }
427 return 0;
428}
429
430static void run_checks()
431{
432 // checks are a bit messy so that add/remove and wait may be called
433 // from inside them without causing an infinite loop:
434 if (next_check == first_check) {
435 while (next_check) {
436 Check* checkp = next_check;
437 next_check = checkp->next;
438 (checkp->cb)(checkp->arg);
439 }
440 next_check = first_check;
441 }
442}
443
444#ifndef WIN32
445static char in_idle;
446#endif
447
448////////////////////////////////////////////////////////////////
DRC685f17e2011-07-28 09:23:00 +0000449// Clipboard notifications
450
451struct Clipboard_Notify {
452 Fl_Clipboard_Notify_Handler handler;
453 void *data;
454 struct Clipboard_Notify *next;
455};
456
457static struct Clipboard_Notify *clip_notify_list = NULL;
458
459extern void fl_clipboard_notify_change(); // in Fl_<platform>.cxx
460
461void Fl::add_clipboard_notify(Fl_Clipboard_Notify_Handler h, void *data) {
462 struct Clipboard_Notify *node;
463
464 remove_clipboard_notify(h);
465
466 node = new Clipboard_Notify;
467
468 node->handler = h;
469 node->data = data;
470 node->next = clip_notify_list;
471
472 clip_notify_list = node;
473
474 fl_clipboard_notify_change();
475}
476
477void Fl::remove_clipboard_notify(Fl_Clipboard_Notify_Handler h) {
478 struct Clipboard_Notify *node, **prev;
479
480 node = clip_notify_list;
481 prev = &clip_notify_list;
482 while (node != NULL) {
483 if (node->handler == h) {
484 *prev = node->next;
485 delete node;
486
487 fl_clipboard_notify_change();
488
489 return;
490 }
491
492 prev = &node->next;
493 node = node->next;
494 }
495}
496
497bool fl_clipboard_notify_empty(void) {
498 return clip_notify_list == NULL;
499}
500
501void fl_trigger_clipboard_notify(int source) {
502 struct Clipboard_Notify *node;
503
504 node = clip_notify_list;
505 while (node != NULL) {
506 node->handler(source, node->data);
507 node = node->next;
508 }
509}
510
511////////////////////////////////////////////////////////////////
DRC2ff39b82011-07-28 08:38:59 +0000512// wait/run/check/ready:
513
514void (*Fl::idle)(); // see Fl::add_idle.cxx for the add/remove functions
515
516extern int fl_ready(); // in Fl_<platform>.cxx
517extern int fl_wait(double time); // in Fl_<platform>.cxx
518
519/**
520 See int Fl::wait()
521*/
522double Fl::wait(double time_to_wait) {
523 // delete all widgets that were listed during callbacks
524 do_widget_deletion();
525
526#ifdef WIN32
527
528 return fl_wait(time_to_wait);
529
530#elif defined(__APPLE__)
531
532 run_checks();
533 if (idle) {
534 if (!in_idle) {
535 in_idle = 1;
536 idle();
537 in_idle = 0;
538 }
539 // the idle function may turn off idle, we can then wait:
540 if (idle) time_to_wait = 0.0;
541 }
542 return fl_mac_flush_and_wait(time_to_wait, in_idle);
543
544#else
545
546 if (first_timeout) {
547 elapse_timeouts();
548 Timeout *t;
549 while ((t = first_timeout)) {
550 if (t->time > 0) break;
551 // The first timeout in the array has expired.
552 missed_timeout_by = t->time;
553 // We must remove timeout from array before doing the callback:
554 void (*cb)(void*) = t->cb;
555 void *argp = t->arg;
556 first_timeout = t->next;
557 t->next = free_timeout;
558 free_timeout = t;
559 // Now it is safe for the callback to do add_timeout:
560 cb(argp);
561 }
562 } else {
563 reset_clock = 1; // we are not going to check the clock
564 }
565 run_checks();
566// if (idle && !fl_ready()) {
567 if (idle) {
568 if (!in_idle) {
569 in_idle = 1;
570 idle();
571 in_idle = 0;
572 }
573 // the idle function may turn off idle, we can then wait:
574 if (idle) time_to_wait = 0.0;
575 }
576 if (first_timeout && first_timeout->time < time_to_wait)
577 time_to_wait = first_timeout->time;
578 if (time_to_wait <= 0.0) {
579 // do flush second so that the results of events are visible:
580 int ret = fl_wait(0.0);
581 flush();
582 return ret;
583 } else {
584 // do flush first so that user sees the display:
585 flush();
586 if (idle && !in_idle) // 'idle' may have been set within flush()
587 time_to_wait = 0.0;
588 return fl_wait(time_to_wait);
589 }
590#endif
591}
592
593#define FOREVER 1e20
594
595/**
596 As long as any windows are displayed this calls Fl::wait()
597 repeatedly. When all the windows are closed it returns zero
598 (supposedly it would return non-zero on any errors, but FLTK calls
599 exit directly for these). A normal program will end main()
600 with return Fl::run();.
601*/
602int Fl::run() {
603 while (Fl_X::first) wait(FOREVER);
604 return 0;
605}
606
607#ifdef WIN32
608
609// Function to initialize COM/OLE for usage. This must be done only once.
610// We define a flag to register whether we called it:
611static char oleInitialized = 0;
612
613// This calls the Windows function OleInitialize() exactly once.
614void fl_OleInitialize() {
615 if (!oleInitialized) {
616 OleInitialize(0L);
617 oleInitialized = 1;
618 }
619}
620
621// This calls the Windows function OleUninitialize() only, if
622// OleInitialize has been called before.
623void fl_OleUninitialize() {
624 if (oleInitialized) {
625 OleUninitialize();
626 oleInitialized = 0;
627 }
628}
629
630class Fl_Win32_At_Exit {
631public:
632 Fl_Win32_At_Exit() { }
633 ~Fl_Win32_At_Exit() {
634 fl_free_fonts(); // do some WIN32 cleanup
635 fl_cleanup_pens();
636 fl_OleUninitialize();
637 fl_brush_action(1);
638 fl_cleanup_dc_list();
639 }
640};
641static Fl_Win32_At_Exit win32_at_exit;
642#endif
643
644
645
646/**
647 Waits until "something happens" and then returns. Call this
648 repeatedly to "run" your program. You can also check what happened
649 each time after this returns, which is quite useful for managing
650 program state.
651
652 What this really does is call all idle callbacks, all elapsed
653 timeouts, call Fl::flush() to get the screen to update, and
654 then wait some time (zero if there are idle callbacks, the shortest of
655 all pending timeouts, or infinity), for any events from the user or
656 any Fl::add_fd() callbacks. It then handles the events and
657 calls the callbacks and then returns.
658
659 The return value of Fl::wait() is non-zero if there are any
660 visible windows - this may change in future versions of FLTK.
661
662 Fl::wait(time) waits a maximum of \e time seconds.
663 <i>It can return much sooner if something happens.</i>
664
665 The return value is positive if an event or fd happens before the
666 time elapsed. It is zero if nothing happens (on Win32 this will only
667 return zero if \e time is zero). It is negative if an error
668 occurs (this will happen on UNIX if a signal happens).
669*/
670int Fl::wait() {
671 if (!Fl_X::first) return 0;
672 wait(FOREVER);
673 return Fl_X::first != 0; // return true if there is a window
674}
675
676/**
677 Same as Fl::wait(0). Calling this during a big calculation
678 will keep the screen up to date and the interface responsive:
679
680 \code
681 while (!calculation_done()) {
682 calculate();
683 Fl::check();
684 if (user_hit_abort_button()) break;
685 }
686 \endcode
687
688 This returns non-zero if any windows are displayed, and 0 if no
689 windows are displayed (this is likely to change in future versions of
690 FLTK).
691*/
692int Fl::check() {
693 wait(0.0);
694 return Fl_X::first != 0; // return true if there is a window
695}
696
697/**
698 This is similar to Fl::check() except this does \e not
699 call Fl::flush() or any callbacks, which is useful if your
700 program is in a state where such callbacks are illegal. This returns
701 true if Fl::check() would do anything (it will continue to
702 return true until you call Fl::check() or Fl::wait()).
703
704 \code
705 while (!calculation_done()) {
706 calculate();
707 if (Fl::ready()) {
708 do_expensive_cleanup();
709 Fl::check();
710 if (user_hit_abort_button()) break;
711 }
712 }
713 \endcode
714*/
715int Fl::ready() {
716#if ! defined( WIN32 ) && ! defined(__APPLE__)
717 if (first_timeout) {
718 elapse_timeouts();
719 if (first_timeout->time <= 0) return 1;
720 } else {
721 reset_clock = 1;
722 }
723#endif
724 return fl_ready();
725}
726
727////////////////////////////////////////////////////////////////
728// Window list management:
729
730#ifndef FL_DOXYGEN
731Fl_X* Fl_X::first;
732#endif
733
734Fl_Window* fl_find(Window xid) {
735 Fl_X *window;
736 for (Fl_X **pp = &Fl_X::first; (window = *pp); pp = &window->next)
737#if defined(WIN32) || defined(USE_X11)
738 if (window->xid == xid)
739#elif defined(__APPLE_QUARTZ__)
740 if (window->xid == xid && !window->w->window())
741#else
742# error unsupported platform
743#endif // __APPLE__
744 {
745 if (window != Fl_X::first && !Fl::modal()) {
746 // make this window be first to speed up searches
747 // this is not done if modal is true to avoid messing up modal stack
748 *pp = window->next;
749 window->next = Fl_X::first;
750 Fl_X::first = window;
751 }
752 return window->w;
753 }
754 return 0;
755}
756
757/**
758 Returns the first top-level window in the list of shown() windows. If
759 a modal() window is shown this is the top-most modal window, otherwise
760 it is the most recent window to get an event.
761*/
762Fl_Window* Fl::first_window() {
763 Fl_X* i = Fl_X::first;
764 return i ? i->w : 0;
765}
766
767/**
768 Returns the next top-level window in the list of shown() windows.
769 You can use this call to iterate through all the windows that are shown().
770 \param[in] window must be shown and not NULL
771*/
772Fl_Window* Fl::next_window(const Fl_Window* window) {
773 Fl_X* i = Fl_X::i(window)->next;
774 return i ? i->w : 0;
775}
776
777/**
778 Sets the window that is returned by first_window().
779 The window is removed from wherever it is in the
780 list and inserted at the top. This is not done if Fl::modal()
781 is on or if the window is not shown(). Because the first window
782 is used to set the "parent" of modal windows, this is often
783 useful.
784 */
785void Fl::first_window(Fl_Window* window) {
786 if (!window || !window->shown()) return;
787 fl_find( Fl_X::i(window)->xid );
788}
789
790/**
791 Redraws all widgets.
792*/
793void Fl::redraw() {
794 for (Fl_X* i = Fl_X::first; i; i = i->next) i->w->redraw();
795}
796
797/**
798 Causes all the windows that need it to be redrawn and graphics forced
799 out through the pipes.
800
801 This is what wait() does before looking for events.
802
803 Note: in multi-threaded applications you should only call Fl::flush()
804 from the main thread. If a child thread needs to trigger a redraw event,
805 it should instead call Fl::awake() to get the main thread to process the
806 event queue.
807*/
808void Fl::flush() {
809 if (damage()) {
810 damage_ = 0;
811 for (Fl_X* i = Fl_X::first; i; i = i->next) {
812 if (i->wait_for_expose) {damage_ = 1; continue;}
813 Fl_Window* wi = i->w;
814 if (!wi->visible_r()) continue;
815 if (wi->damage()) {i->flush(); wi->clear_damage();}
816 // destroy damage regions for windows that don't use them:
817 if (i->region) {XDestroyRegion(i->region); i->region = 0;}
818 }
819 }
820#if defined(USE_X11)
821 if (fl_display) XFlush(fl_display);
822#elif defined(WIN32)
823 GdiFlush();
824#elif defined (__APPLE_QUARTZ__)
825 if (fl_gc)
826 CGContextFlush(fl_gc);
827#else
828# error unsupported platform
829#endif
830}
831
832
833////////////////////////////////////////////////////////////////
834// Event handlers:
835
836
837struct handler_link {
838 int (*handle)(int);
839 handler_link *next;
840};
841
842
843static handler_link *handlers = 0;
844
845
846/**
847 Install a function to parse unrecognized events. If FLTK cannot
848 figure out what to do with an event, it calls each of these functions
849 (most recent first) until one of them returns non-zero. If none of
850 them returns non-zero then the event is ignored. Events that cause
851 this to be called are:
852
853 - FL_SHORTCUT events that are not recognized by any widget.
854 This lets you provide global shortcut keys.
855 - System events that FLTK does not recognize. See fl_xevent.
856 - \e Some other events when the widget FLTK selected returns
857 zero from its handle() method. Exactly which ones may change
858 in future versions, however.
859
860 \see Fl::remove_handler(Fl_Event_Handler)
861 \see Fl::event_dispatch(Fl_Event_Dispatch d)
862 \see Fl::handle(int, Fl_Window*)
863*/
864void Fl::add_handler(Fl_Event_Handler ha) {
865 handler_link *l = new handler_link;
866 l->handle = ha;
867 l->next = handlers;
868 handlers = l;
869}
870
871
872/**
873 Removes a previously added event handler.
874 \see Fl::handle(int, Fl_Window*)
875*/
876void Fl::remove_handler(Fl_Event_Handler ha) {
877 handler_link *l, *p;
878
879 // Search for the handler in the list...
880 for (l = handlers, p = 0; l && l->handle != ha; p = l, l = l->next);
881
882 if (l) {
883 // Found it, so remove it from the list...
884 if (p) p->next = l->next;
885 else handlers = l->next;
886
887 // And free the record...
888 delete l;
889 }
890}
891
892int (*fl_local_grab)(int); // used by fl_dnd.cxx
893
894static int send_handlers(int e) {
895 for (const handler_link *hl = handlers; hl; hl = hl->next)
896 if (hl->handle(e)) return 1;
897 return 0;
898}
899
900////////////////////////////////////////////////////////////////
901
902Fl_Widget* fl_oldfocus; // kludge for Fl_Group...
903
904/**
905 Sets the widget that will receive FL_KEYBOARD events.
906
907 If you change Fl::focus(), the previous widget and all
908 parents (that don't contain the new widget) are sent FL_UNFOCUS
909 events. Changing the focus does \e not send FL_FOCUS to
910 this or any widget, because sending FL_FOCUS is supposed to
911 \e test if the widget wants the focus (by it returning non-zero from
912 handle()).
913
914 \see Fl_Widget::take_focus()
915*/
916void Fl::focus(Fl_Widget *o) {
917 if (o && !o->visible_focus()) return;
918 if (grab()) return; // don't do anything while grab is on
919 Fl_Widget *p = focus_;
920 if (o != p) {
921 Fl::compose_reset();
922 focus_ = o;
923 // make sure that fl_xfocus is set to the top level window
924 // of this widget, or fl_fix_focus will clear our focus again
925 if (o) {
926 Fl_Window *win = 0, *w1 = o->as_window();
927 if (!w1) w1 = o->window();
928 while (w1) { win=w1; w1=win->window(); }
929 if (win) {
930#ifdef __APPLE__
931 if (fl_xfocus != win) {
932 Fl_X *x = Fl_X::i(win);
933 if (x) x->set_key_window();
934 }
935#endif
936 fl_xfocus = win;
937 }
938 }
939 // take focus from the old focused window
940 fl_oldfocus = 0;
941 int old_event = e_number;
942 e_number = FL_UNFOCUS;
943 for (; p; p = p->parent()) {
944 p->handle(FL_UNFOCUS);
945 fl_oldfocus = p;
946 }
947 e_number = old_event;
DRC685f17e2011-07-28 09:23:00 +0000948 // let the platform code do what it needs
949 fl_update_focus();
DRC2ff39b82011-07-28 08:38:59 +0000950 }
951}
952
953static char dnd_flag = 0; // make 'belowmouse' send DND_LEAVE instead of LEAVE
954
955/**
956 Sets the widget that is below the mouse. This is for
957 highlighting buttons. It is not used to send FL_PUSH or
958 FL_MOVE directly, for several obscure reasons, but those events
959 typically go to this widget. This is also the first widget tried for
960 FL_SHORTCUT events.
961
962 If you change the belowmouse widget, the previous one and all
963 parents (that don't contain the new widget) are sent FL_LEAVE
964 events. Changing this does \e not send FL_ENTER to this
965 or any widget, because sending FL_ENTER is supposed to \e test
966 if the widget wants the mouse (by it returning non-zero from
967 handle()).
968*/
969void Fl::belowmouse(Fl_Widget *o) {
970 if (grab()) return; // don't do anything while grab is on
971 Fl_Widget *p = belowmouse_;
972 if (o != p) {
973 belowmouse_ = o;
974 int old_event = e_number;
975 e_number = dnd_flag ? FL_DND_LEAVE : FL_LEAVE;
976 for (; p && !p->contains(o); p = p->parent()) {
977 p->handle(e_number);
978 }
979 e_number = old_event;
980 }
981}
982
983/**
984 Sets the widget that is being pushed. FL_DRAG or
985 FL_RELEASE (and any more FL_PUSH) events will be sent to
986 this widget.
987
988 If you change the pushed widget, the previous one and all parents
989 (that don't contain the new widget) are sent FL_RELEASE
990 events. Changing this does \e not send FL_PUSH to this
991 or any widget, because sending FL_PUSH is supposed to \e test
992 if the widget wants the mouse (by it returning non-zero from
993 handle()).
994*/
995 void Fl::pushed(Fl_Widget *o) {
996 pushed_ = o;
997}
998
999static void nothing(Fl_Widget *) {}
1000void (*Fl_Tooltip::enter)(Fl_Widget *) = nothing;
1001void (*Fl_Tooltip::exit)(Fl_Widget *) = nothing;
1002
1003// Update modal(), focus() and other state according to system state,
1004// and send FL_ENTER, FL_LEAVE, FL_FOCUS, and/or FL_UNFOCUS events.
1005// This is the only function that produces these events in response
1006// to system activity.
1007// This is called whenever a window is added or hidden, and whenever
1008// X says the focus or mouse window have changed.
1009
1010void fl_fix_focus() {
1011#ifdef DEBUG
1012 puts("fl_fix_focus();");
1013#endif // DEBUG
1014
1015 if (Fl::grab()) return; // don't do anything while grab is on.
1016
1017 // set focus based on Fl::modal() and fl_xfocus
1018 Fl_Widget* w = fl_xfocus;
1019 if (w) {
1020 int saved = Fl::e_keysym;
1021 if (Fl::e_keysym < (FL_Button + FL_LEFT_MOUSE) ||
1022 Fl::e_keysym > (FL_Button + FL_RIGHT_MOUSE))
1023 Fl::e_keysym = 0; // make sure widgets don't think a keystroke moved focus
1024 while (w->parent()) w = w->parent();
1025 if (Fl::modal()) w = Fl::modal();
1026 if (!w->contains(Fl::focus()))
1027 if (!w->take_focus()) Fl::focus(w);
1028 Fl::e_keysym = saved;
1029 } else
1030 Fl::focus(0);
1031
1032// MRS: Originally we checked the button state, but a user reported that it
1033// broke click-to-focus in FLWM?!?
1034// if (!(Fl::event_state() & 0x7f00000 /*FL_BUTTONS*/)) {
1035 if (!Fl::pushed()) {
1036 // set belowmouse based on Fl::modal() and fl_xmousewin:
1037 w = fl_xmousewin;
1038 if (w) {
1039 if (Fl::modal()) w = Fl::modal();
1040 if (!w->contains(Fl::belowmouse())) {
1041 int old_event = Fl::e_number;
1042 w->handle(Fl::e_number = FL_ENTER);
1043 Fl::e_number = old_event;
1044 if (!w->contains(Fl::belowmouse())) Fl::belowmouse(w);
1045 } else {
1046 // send a FL_MOVE event so the enter/leave state is up to date
1047 Fl::e_x = Fl::e_x_root-fl_xmousewin->x();
1048 Fl::e_y = Fl::e_y_root-fl_xmousewin->y();
1049 int old_event = Fl::e_number;
1050 w->handle(Fl::e_number = FL_MOVE);
1051 Fl::e_number = old_event;
1052 }
1053 } else {
1054 Fl::belowmouse(0);
1055 Fl_Tooltip::enter(0);
1056 }
1057 }
1058}
1059
1060#if !(defined(WIN32) || defined(__APPLE__))
1061extern Fl_Widget *fl_selection_requestor; // from Fl_x.cxx
1062#endif
1063
1064// This function is called by ~Fl_Widget() and by Fl_Widget::deactivate()
1065// and by Fl_Widget::hide(). It indicates that the widget does not want
1066// to receive any more events, and also removes all global variables that
1067// point at the widget.
1068// I changed this from the 1.0.1 behavior, the older version could send
1069// FL_LEAVE or FL_UNFOCUS events to the widget. This appears to not be
1070// desirable behavior and caused flwm to crash.
1071
1072void fl_throw_focus(Fl_Widget *o) {
1073#ifdef DEBUG
1074 printf("fl_throw_focus(o=%p)\n", o);
1075#endif // DEBUG
1076
1077 if (o->contains(Fl::pushed())) Fl::pushed_ = 0;
1078#if !(defined(WIN32) || defined(__APPLE__))
1079 if (o->contains(fl_selection_requestor)) fl_selection_requestor = 0;
1080#endif
1081 if (o->contains(Fl::belowmouse())) Fl::belowmouse_ = 0;
1082 if (o->contains(Fl::focus())) Fl::focus_ = 0;
1083 if (o == fl_xfocus) fl_xfocus = 0;
1084 if (o == Fl_Tooltip::current()) Fl_Tooltip::current(0);
1085 if (o == fl_xmousewin) fl_xmousewin = 0;
1086 Fl_Tooltip::exit(o);
1087 fl_fix_focus();
1088}
1089
1090////////////////////////////////////////////////////////////////
1091
1092// Call to->handle(), but first replace the mouse x/y with the correct
1093// values to account for nested windows. 'window' is the outermost
1094// window the event was posted to by the system:
1095static int send(int event, Fl_Widget* to, Fl_Window* window) {
1096 int dx, dy;
1097 int old_event = Fl::e_number;
1098 if (window) {
1099 dx = window->x();
1100 dy = window->y();
1101 } else {
1102 dx = dy = 0;
1103 }
1104 for (const Fl_Widget* w = to; w; w = w->parent())
1105 if (w->type()>=FL_WINDOW) {dx -= w->x(); dy -= w->y();}
1106 int save_x = Fl::e_x; Fl::e_x += dx;
1107 int save_y = Fl::e_y; Fl::e_y += dy;
1108 int ret = to->handle(Fl::e_number = event);
1109 Fl::e_number = old_event;
1110 Fl::e_y = save_y;
1111 Fl::e_x = save_x;
1112 return ret;
1113}
1114
1115
1116/**
1117 \brief Set a new event dispatch function.
1118
1119 The event dispatch function is called after native events are converted to
1120 FLTK events, but before they are handled by FLTK. If the dispatch function
1121 Fl_Event_Dispatch \p d is set, it is up to the dispatch function to call
1122 Fl::handle_(int, Fl_Window*) or to ignore the event.
1123
1124 The dispatch function itself must return 0 if it ignored the event,
1125 or non-zero if it used the event. If you call Fl::handle_(), then
1126 this will return the correct value.
1127
1128 The event dispatch can be used to handle exceptions in FLTK events and
1129 callbacks before they reach the native event handler:
1130
1131 \code
1132 int myHandler(int e, Fl_Window *w) {
1133 try {
1134 return Fl::handle_(e, w);
1135 } catch () {
1136 ...
1137 }
1138 }
1139
1140 main() {
1141 Fl::event_dispatch(myHandler);
1142 ...
1143 Fl::run();
1144 }
1145 \endcode
1146
1147 \param d new dispatch function, or NULL
1148 \see Fl::add_handler(Fl_Event_Handler)
1149 \see Fl::handle(int, Fl_Window*)
1150 \see Fl::handle_(int, Fl_Window*)
1151 */
1152void Fl::event_dispatch(Fl_Event_Dispatch d)
1153{
1154 e_dispatch = d;
1155}
1156
1157
1158/**
1159 \brief Return the current event dispatch function.
1160 */
1161Fl_Event_Dispatch Fl::event_dispatch()
1162{
1163 return e_dispatch;
1164}
1165
1166
1167/**
1168 \brief Handle events from the window system.
1169
1170 This is called from the native event dispatch after native events have been
1171 converted to FLTK notation. This function calls Fl::handle_(int, Fl_Window*)
1172 unless the user sets a dispatch function. If a user dispatch function is set,
1173 the user must make sure that Fl::handle_() is called, or the event will be
1174 ignored.
1175
1176 \param e the event type (Fl::event_number() is not yet set)
1177 \param window the window that caused this event
1178 \return 0 if the event was not handled
1179
1180 \see Fl::add_handler(Fl_Event_Handler)
1181 \see Fl::event_dispatch(Fl_Event_Dispatch)
1182 */
1183int Fl::handle(int e, Fl_Window* window)
1184{
1185 if (e_dispatch) {
1186 return e_dispatch(e, window);
1187 } else {
1188 return handle_(e, window);
1189 }
1190}
1191
1192
1193/**
1194 \brief Handle events from the window system.
1195
1196 This function is called from the native event dispatch, unless the user sets
1197 another dispatch function. In that case, the user dispatch function must
1198 decide when to call Fl::handle_(int, Fl_Window*)
1199
1200 \param e the event type (Fl::event_number() is not yet set)
1201 \param window the window that caused this event
1202 \return 0 if the event was not handled
1203
1204 \see Fl::event_dispatch(Fl_Event_Dispatch)
1205 */
1206int Fl::handle_(int e, Fl_Window* window)
1207{
1208 e_number = e;
1209 if (fl_local_grab) return fl_local_grab(e);
1210
1211 Fl_Widget* wi = window;
1212
1213 switch (e) {
1214
1215 case FL_CLOSE:
1216 if ( grab() || (modal() && window != modal()) ) return 0;
1217 wi->do_callback();
1218 return 1;
1219
1220 case FL_SHOW:
1221 wi->Fl_Widget::show(); // this calls Fl_Widget::show(), not Fl_Window::show()
1222 return 1;
1223
1224 case FL_HIDE:
1225 wi->Fl_Widget::hide(); // this calls Fl_Widget::hide(), not Fl_Window::hide()
1226 return 1;
1227
1228 case FL_PUSH:
1229#ifdef DEBUG
1230 printf("Fl::handle(e=%d, window=%p);\n", e, window);
1231#endif // DEBUG
1232
1233 if (grab()) wi = grab();
1234 else if (modal() && wi != modal()) return 0;
1235 pushed_ = wi;
1236 Fl_Tooltip::current(wi);
1237 if (send(e, wi, window)) return 1;
1238 // raise windows that are clicked on:
1239 window->show();
1240 return 1;
1241
1242 case FL_DND_ENTER:
1243 case FL_DND_DRAG:
1244 dnd_flag = 1;
1245 break;
1246
1247 case FL_DND_LEAVE:
1248 dnd_flag = 1;
1249 belowmouse(0);
1250 dnd_flag = 0;
1251 return 1;
1252
1253 case FL_DND_RELEASE:
1254 wi = belowmouse();
1255 break;
1256
1257 case FL_MOVE:
1258 case FL_DRAG:
1259 fl_xmousewin = window; // this should already be set, but just in case.
1260 if (pushed()) {
1261 wi = pushed();
1262 if (grab()) wi = grab();
1263 e_number = e = FL_DRAG;
1264 break;
1265 }
1266 if (modal() && wi != modal()) wi = 0;
1267 if (grab()) wi = grab();
1268 { int ret;
1269 Fl_Widget* pbm = belowmouse();
1270#ifdef __APPLE__
1271 if (fl_mac_os_version < 100500) {
1272 // before 10.5, mouse moved events aren't sent to borderless windows such as tooltips
1273 Fl_Window *tooltip = Fl_Tooltip::current_window();
1274 int inside = 0;
1275 if (tooltip && tooltip->shown() ) { // check if a tooltip window is currently opened
1276 // check if mouse is inside the tooltip
1277 inside = (Fl::event_x_root() >= tooltip->x() && Fl::event_x_root() < tooltip->x() + tooltip->w() &&
1278 Fl::event_y_root() >= tooltip->y() && Fl::event_y_root() < tooltip->y() + tooltip->h() );
1279 }
1280 // if inside, send event to tooltip window instead of background window
1281 if (inside) ret = send(e, tooltip, window);
1282 else ret = (wi && send(e, wi, window));
1283 } else
1284#endif
1285 ret = (wi && send(e, wi, window));
1286 if (pbm != belowmouse()) {
1287#ifdef DEBUG
1288 printf("Fl::handle(e=%d, window=%p);\n", e, window);
1289#endif // DEBUG
1290 Fl_Tooltip::enter(belowmouse());
1291 }
1292 return ret;}
1293
1294 case FL_RELEASE: {
1295// printf("FL_RELEASE: window=%p, pushed() = %p, grab() = %p, modal() = %p\n",
1296// window, pushed(), grab(), modal());
1297
1298 if (grab()) {
1299 wi = grab();
1300 pushed_ = 0; // must be zero before callback is done!
1301 } else if (pushed()) {
1302 wi = pushed();
1303 pushed_ = 0; // must be zero before callback is done!
1304 } else if (modal() && wi != modal()) return 0;
1305 int r = send(e, wi, window);
1306 fl_fix_focus();
1307 return r;}
1308
1309 case FL_UNFOCUS:
1310 window = 0;
1311 case FL_FOCUS:
1312 fl_xfocus = window;
1313 fl_fix_focus();
1314 return 1;
1315
1316 case FL_KEYUP:
1317 // Send the key-up to the current focus widget. This is not
1318 // always the same widget that received the corresponding
1319 // FL_KEYBOARD event because focus may have changed.
1320 // Sending the KEYUP to the right KEYDOWN is possible, but
1321 // would require that we track the KEYDOWN for every possible
1322 // key stroke (users may hold down multiple keys!) and then
1323 // make sure that the widget still exists before sending
1324 // a KEYUP there. I believe that the current solution is
1325 // "close enough".
1326 for (wi = grab() ? grab() : focus(); wi; wi = wi->parent())
1327 if (send(FL_KEYUP, wi, window)) return 1;
1328 return 0;
1329
1330 case FL_KEYBOARD:
1331#ifdef DEBUG
1332 printf("Fl::handle(e=%d, window=%p);\n", e, window);
1333#endif // DEBUG
1334
1335 Fl_Tooltip::enter((Fl_Widget*)0);
1336
1337 fl_xfocus = window; // this should not happen! But maybe it does:
1338
1339 // Try it as keystroke, sending it to focus and all parents:
1340 for (wi = grab() ? grab() : focus(); wi; wi = wi->parent())
1341 if (send(FL_KEYBOARD, wi, window)) return 1;
1342
1343 // recursive call to try shortcut:
1344 if (handle(FL_SHORTCUT, window)) return 1;
1345
1346 // and then try a shortcut with the case of the text swapped, by
1347 // changing the text and falling through to FL_SHORTCUT case:
1348 {unsigned char* c = (unsigned char*)event_text(); // cast away const
1349 if (!isalpha(*c)) return 0;
1350 *c = isupper(*c) ? tolower(*c) : toupper(*c);}
1351 e_number = e = FL_SHORTCUT;
1352
1353 case FL_SHORTCUT:
1354 if (grab()) {wi = grab(); break;} // send it to grab window
1355
1356 // Try it as shortcut, sending to mouse widget and all parents:
1357 wi = belowmouse();
1358 if (!wi) {
1359 wi = modal();
1360 if (!wi) wi = window;
1361 } else if (wi->window() != first_window()) {
1362 if (send(FL_SHORTCUT, first_window(), first_window())) return 1;
1363 }
1364
1365 for (; wi; wi = wi->parent()) {
1366 if (send(FL_SHORTCUT, wi, wi->window())) return 1;
1367 }
1368
1369 // try using add_handle() functions:
1370 if (send_handlers(FL_SHORTCUT)) return 1;
1371
1372 // make Escape key close windows:
1373 if (event_key()==FL_Escape) {
1374 wi = modal(); if (!wi) wi = window;
1375 wi->do_callback();
1376 return 1;
1377 }
1378
1379 return 0;
1380
1381 case FL_ENTER:
1382#ifdef DEBUG
1383 printf("Fl::handle(e=%d, window=%p);\n", e, window);
1384#endif // DEBUG
1385
1386 fl_xmousewin = window;
1387 fl_fix_focus();
1388 Fl_Tooltip::enter(belowmouse());
1389 return 1;
1390
1391 case FL_LEAVE:
1392#ifdef DEBUG
1393 printf("Fl::handle(e=%d, window=%p);\n", e, window);
1394#endif // DEBUG
1395
1396 if (!pushed_) {
1397 belowmouse(0);
1398 Fl_Tooltip::enter(0);
1399 }
1400 if (window == fl_xmousewin) {fl_xmousewin = 0; fl_fix_focus();}
1401 return 1;
1402
1403 case FL_MOUSEWHEEL:
1404 fl_xfocus = window; // this should not happen! But maybe it does:
1405
1406 // Try sending it to the "grab" first
1407 if (grab() && grab()!=modal() && grab()!=window) {
1408 if (send(FL_MOUSEWHEEL, grab(), window)) return 1;
1409 }
1410 // Now try sending it to the "modal" window
1411 if (modal()) {
1412 send(FL_MOUSEWHEEL, modal(), window);
1413 return 1;
1414 }
1415 // Finally try sending it to the window, the event occured in
1416 if (send(FL_MOUSEWHEEL, window, window)) return 1;
1417 default:
1418 break;
1419 }
1420 if (wi && send(e, wi, window)) {
1421 dnd_flag = 0;
1422 return 1;
1423 }
1424 dnd_flag = 0;
1425 return send_handlers(e);
1426}
1427
1428////////////////////////////////////////////////////////////////
1429// hide() destroys the X window, it does not do unmap!
1430
DRC685f17e2011-07-28 09:23:00 +00001431#if defined(WIN32)
1432extern void fl_clipboard_notify_untarget(HWND wnd);
1433extern void fl_update_clipboard(void);
1434#elif USE_XFT
DRC2ff39b82011-07-28 08:38:59 +00001435extern void fl_destroy_xft_draw(Window);
1436#endif
1437
1438void Fl_Window::hide() {
1439 clear_visible();
1440
1441 if (!shown()) return;
1442
1443 // remove from the list of windows:
1444 Fl_X* ip = i;
1445 Fl_X** pp = &Fl_X::first;
1446 for (; *pp != ip; pp = &(*pp)->next) if (!*pp) return;
1447 *pp = ip->next;
1448#ifdef __APPLE__
1449 ip->unlink();
1450 // MacOS X manages a single pointer per application. Make sure that hiding
1451 // a toplevel window will not leave us with some random pointer shape, or
1452 // worst case, an invisible pointer
1453 if (!parent()) cursor(FL_CURSOR_DEFAULT);
1454#endif
1455 i = 0;
1456
1457 // recursively remove any subwindows:
1458 for (Fl_X *wi = Fl_X::first; wi;) {
1459 Fl_Window* W = wi->w;
1460 if (W->window() == this) {
1461 W->hide();
1462 W->set_visible();
1463 wi = Fl_X::first;
1464 } else wi = wi->next;
1465 }
1466
1467 if (this == Fl::modal_) { // we are closing the modal window, find next one:
1468 Fl_Window* W;
1469 for (W = Fl::first_window(); W; W = Fl::next_window(W))
1470 if (W->modal()) break;
1471 Fl::modal_ = W;
1472 }
1473
1474 // Make sure no events are sent to this window:
1475 fl_throw_focus(this);
1476 handle(FL_HIDE);
1477
1478#if defined(WIN32)
1479 // this little trick keeps the current clipboard alive, even if we are about
1480 // to destroy the window that owns the selection.
DRC685f17e2011-07-28 09:23:00 +00001481 if (GetClipboardOwner()==ip->xid)
1482 fl_update_clipboard();
1483 // Make sure we unlink this window from the clipboard chain
1484 fl_clipboard_notify_untarget(ip->xid);
DRC2ff39b82011-07-28 08:38:59 +00001485 // Send a message to myself so that I'll get out of the event loop...
1486 PostMessage(ip->xid, WM_APP, 0, 0);
1487 if (ip->private_dc) fl_release_dc(ip->xid, ip->private_dc);
1488 if (ip->xid == fl_window && fl_gc) {
1489 fl_release_dc(fl_window, fl_gc);
1490 fl_window = (HWND)-1;
1491 fl_gc = 0;
1492# ifdef FLTK_USE_CAIRO
1493 if (Fl::cairo_autolink_context()) Fl::cairo_make_current((Fl_Window*) 0);
1494# endif
1495 }
1496#elif defined(__APPLE_QUARTZ__)
1497 Fl_X::q_release_context(ip);
1498 if ( ip->xid == fl_window && !parent() )
1499 fl_window = 0;
1500#endif
1501
1502 if (ip->region) XDestroyRegion(ip->region);
1503
1504#if defined(USE_X11)
1505# if USE_XFT
1506 fl_destroy_xft_draw(ip->xid);
1507# endif
1508 // this test makes sure ip->xid has not been destroyed already
1509 if (ip->xid) XDestroyWindow(fl_display, ip->xid);
1510#elif defined(WIN32)
1511 // this little trickery seems to avoid the popup window stacking problem
1512 HWND p = GetForegroundWindow();
1513 if (p==GetParent(ip->xid)) {
1514 ShowWindow(ip->xid, SW_HIDE);
1515 ShowWindow(p, SW_SHOWNA);
1516 }
1517 XDestroyWindow(fl_display, ip->xid);
1518#elif defined(__APPLE_QUARTZ__)
1519 ip->destroy();
1520#else
1521# error unsupported platform
1522#endif
1523
1524#ifdef WIN32
1525 // Try to stop the annoying "raise another program" behavior
1526 if (non_modal() && Fl::first_window() && Fl::first_window()->shown())
1527 Fl::first_window()->show();
1528#endif
1529 delete ip;
1530}
1531
1532Fl_Window::~Fl_Window() {
1533 hide();
1534 if (xclass_) {
1535 free(xclass_);
1536 }
1537}
1538
1539// FL_SHOW and FL_HIDE are called whenever the visibility of this widget
1540// or any parent changes. We must correctly map/unmap the system's window.
1541
1542// For top-level windows it is assumed the window has already been
1543// mapped or unmapped!!! This is because this should only happen when
1544// Fl_Window::show() or Fl_Window::hide() is called, or in response to
1545// iconize/deiconize events from the system.
1546
1547int Fl_Window::handle(int ev)
1548{
1549 if (parent()) {
1550 switch (ev) {
1551 case FL_SHOW:
1552 if (!shown()) show();
1553 else {
1554#if defined(USE_X11) || defined(WIN32)
1555 XMapWindow(fl_display, fl_xid(this)); // extra map calls are harmless
1556#elif defined(__APPLE_QUARTZ__)
1557 i->map();
1558#else
1559# error unsupported platform
1560#endif // __APPLE__
1561 }
1562 break;
1563 case FL_HIDE:
1564 if (shown()) {
1565 // Find what really turned invisible, if it was a parent window
1566 // we do nothing. We need to avoid unnecessary unmap calls
1567 // because they cause the display to blink when the parent is
1568 // remapped. However if this or any intermediate non-window
1569 // widget has really had hide() called directly on it, we must
1570 // unmap because when the parent window is remapped we don't
1571 // want to reappear.
1572 if (visible()) {
1573 Fl_Widget* p = parent(); for (;p->visible();p = p->parent()) {}
1574 if (p->type() >= FL_WINDOW) break; // don't do the unmap
1575 }
1576#if defined(USE_X11) || defined(WIN32)
1577 XUnmapWindow(fl_display, fl_xid(this));
1578#elif defined(__APPLE_QUARTZ__)
1579 i->unmap();
1580#else
1581# error platform unsupported
1582#endif
1583 }
1584 break;
1585 }
1586// } else if (ev == FL_FOCUS || ev == FL_UNFOCUS) {
1587// Fl_Tooltip::exit(Fl_Tooltip::current());
1588 }
1589
1590 return Fl_Group::handle(ev);
1591}
1592
1593////////////////////////////////////////////////////////////////
1594// Back compatibility cut & paste functions for fltk 1.1 only:
1595
1596/** Back-compatibility only: The single-argument call can be used to
1597 move the selection to another widget or to set the owner to
1598 NULL, without changing the actual text of the
1599 selection. FL_SELECTIONCLEAR is sent to the previous
1600 selection owner, if any.
1601
1602 <i>Copying the buffer every time the selection is changed is
1603 obviously wasteful, especially for large selections. An interface will
1604 probably be added in a future version to allow the selection to be made
1605 by a callback function. The current interface will be emulated on top
1606 of this.</i>
1607*/
1608void Fl::selection_owner(Fl_Widget *owner) {selection_owner_ = owner;}
1609
1610/**
1611 Changes the current selection. The block of text is
1612 copied to an internal buffer by FLTK (be careful if doing this in
1613 response to an FL_PASTE as this \e may be the same buffer
1614 returned by event_text()). The selection_owner()
1615 widget is set to the passed owner.
1616*/
1617void Fl::selection(Fl_Widget &owner, const char* text, int len) {
1618 selection_owner_ = &owner;
1619 Fl::copy(text, len, 0);
1620}
1621
1622/** Backward compatibility only.
1623 This calls Fl::paste(receiver, 0);
1624 \see Fl::paste(Fl_Widget &receiver, int clipboard)
1625*/
1626void Fl::paste(Fl_Widget &receiver) {
1627 Fl::paste(receiver, 0);
1628}
1629
1630////////////////////////////////////////////////////////////////
1631
1632#include <FL/fl_draw.H>
1633
1634void Fl_Widget::redraw() {
1635 damage(FL_DAMAGE_ALL);
1636}
1637
1638void Fl_Widget::redraw_label() {
1639 if (window()) {
1640 if (box() == FL_NO_BOX) {
1641 // Widgets with the FL_NO_BOX boxtype need a parent to
1642 // redraw, since it is responsible for redrawing the
1643 // background...
1644 int X = x() > 0 ? x() - 1 : 0;
1645 int Y = y() > 0 ? y() - 1 : 0;
1646 window()->damage(FL_DAMAGE_ALL, X, Y, w() + 2, h() + 2);
1647 }
1648
1649 if (align() && !(align() & FL_ALIGN_INSIDE) && window()->shown()) {
1650 // If the label is not inside the widget, compute the location of
1651 // the label and redraw the window within that bounding box...
1652 int W = 0, H = 0;
1653 label_.measure(W, H);
1654 W += 5; // Add a little to the size of the label to cover overflow
1655 H += 5;
1656
1657 // FIXME:
1658 // This assumes that measure() returns the correct outline, which it does
1659 // not in all possible cases of alignment combinedwith image and symbols.
1660 switch (align() & 0x0f) {
1661 case FL_ALIGN_TOP_LEFT:
1662 window()->damage(FL_DAMAGE_EXPOSE, x(), y()-H, W, H); break;
1663 case FL_ALIGN_TOP:
1664 window()->damage(FL_DAMAGE_EXPOSE, x()+(w()-W)/2, y()-H, W, H); break;
1665 case FL_ALIGN_TOP_RIGHT:
1666 window()->damage(FL_DAMAGE_EXPOSE, x()+w()-W, y()-H, W, H); break;
1667 case FL_ALIGN_LEFT_TOP:
1668 window()->damage(FL_DAMAGE_EXPOSE, x()-W, y(), W, H); break;
1669 case FL_ALIGN_RIGHT_TOP:
1670 window()->damage(FL_DAMAGE_EXPOSE, x()+w(), y(), W, H); break;
1671 case FL_ALIGN_LEFT:
1672 window()->damage(FL_DAMAGE_EXPOSE, x()-W, y()+(h()-H)/2, W, H); break;
1673 case FL_ALIGN_RIGHT:
1674 window()->damage(FL_DAMAGE_EXPOSE, x()+w(), y()+(h()-H)/2, W, H); break;
1675 case FL_ALIGN_LEFT_BOTTOM:
1676 window()->damage(FL_DAMAGE_EXPOSE, x()-W, y()+h()-H, W, H); break;
1677 case FL_ALIGN_RIGHT_BOTTOM:
1678 window()->damage(FL_DAMAGE_EXPOSE, x()+w(), y()+h()-H, W, H); break;
1679 case FL_ALIGN_BOTTOM_LEFT:
1680 window()->damage(FL_DAMAGE_EXPOSE, x(), y()+h(), W, H); break;
1681 case FL_ALIGN_BOTTOM:
1682 window()->damage(FL_DAMAGE_EXPOSE, x()+(w()-W)/2, y()+h(), W, H); break;
1683 case FL_ALIGN_BOTTOM_RIGHT:
1684 window()->damage(FL_DAMAGE_EXPOSE, x()+w()-W, y()+h(), W, H); break;
1685 default:
1686 window()->damage(FL_DAMAGE_ALL); break;
1687 }
1688 } else {
1689 // The label is inside the widget, so just redraw the widget itself...
1690 damage(FL_DAMAGE_ALL);
1691 }
1692 }
1693}
1694
1695void Fl_Widget::damage(uchar fl) {
1696 if (type() < FL_WINDOW) {
1697 // damage only the rectangle covered by a child widget:
1698 damage(fl, x(), y(), w(), h());
1699 } else {
1700 // damage entire window by deleting the region:
1701 Fl_X* i = Fl_X::i((Fl_Window*)this);
1702 if (!i) return; // window not mapped, so ignore it
1703 if (i->region) {XDestroyRegion(i->region); i->region = 0;}
1704 damage_ |= fl;
1705 Fl::damage(FL_DAMAGE_CHILD);
1706 }
1707}
1708
1709void Fl_Widget::damage(uchar fl, int X, int Y, int W, int H) {
1710 Fl_Widget* wi = this;
1711 // mark all parent widgets between this and window with FL_DAMAGE_CHILD:
1712 while (wi->type() < FL_WINDOW) {
1713 wi->damage_ |= fl;
1714 wi = wi->parent();
1715 if (!wi) return;
1716 fl = FL_DAMAGE_CHILD;
1717 }
1718 Fl_X* i = Fl_X::i((Fl_Window*)wi);
1719 if (!i) return; // window not mapped, so ignore it
1720
1721 // clip the damage to the window and quit if none:
1722 if (X < 0) {W += X; X = 0;}
1723 if (Y < 0) {H += Y; Y = 0;}
1724 if (W > wi->w()-X) W = wi->w()-X;
1725 if (H > wi->h()-Y) H = wi->h()-Y;
1726 if (W <= 0 || H <= 0) return;
1727
1728 if (!X && !Y && W==wi->w() && H==wi->h()) {
1729 // if damage covers entire window delete region:
1730 wi->damage(fl);
1731 return;
1732 }
1733
1734 if (wi->damage()) {
1735 // if we already have damage we must merge with existing region:
1736 if (i->region) {
1737#if defined(USE_X11)
1738 XRectangle R;
1739 R.x = X; R.y = Y; R.width = W; R.height = H;
1740 XUnionRectWithRegion(&R, i->region, i->region);
1741#elif defined(WIN32)
1742 Fl_Region R = XRectangleRegion(X, Y, W, H);
1743 CombineRgn(i->region, i->region, R, RGN_OR);
1744 XDestroyRegion(R);
1745#elif defined(__APPLE_QUARTZ__)
1746 CGRect arg = fl_cgrectmake_cocoa(X, Y, W, H);
1747 int j; // don't add a rectangle totally inside the Fl_Region
1748 for(j = 0; j < i->region->count; j++) {
1749 if(CGRectContainsRect(i->region->rects[j], arg)) break;
1750 }
1751 if( j >= i->region->count) {
1752 i->region->rects = (CGRect*)realloc(i->region->rects, (++(i->region->count)) * sizeof(CGRect));
1753 i->region->rects[i->region->count - 1] = arg;
1754 }
1755#else
1756# error unsupported platform
1757#endif
1758 }
1759 wi->damage_ |= fl;
1760 } else {
1761 // create a new region:
1762 if (i->region) XDestroyRegion(i->region);
1763 i->region = XRectangleRegion(X,Y,W,H);
1764 wi->damage_ = fl;
1765 }
1766 Fl::damage(FL_DAMAGE_CHILD);
1767}
1768void Fl_Window::flush() {
1769 make_current();
1770//if (damage() == FL_DAMAGE_EXPOSE && can_boxcheat(box())) fl_boxcheat = this;
1771 fl_clip_region(i->region); i->region = 0;
1772 draw();
1773}
1774
1775#ifdef WIN32
1776# include "Fl_win32.cxx"
1777//#elif defined(__APPLE__)
1778#endif
1779
1780//
1781// The following methods allow callbacks to schedule the deletion of
1782// widgets at "safe" times.
1783//
1784
1785static int num_dwidgets = 0, alloc_dwidgets = 0;
1786static Fl_Widget **dwidgets = 0;
1787
1788/**
1789 Schedules a widget for deletion at the next call to the event loop.
1790 Use this method to delete a widget inside a callback function.
1791
1792 To avoid early deletion of widgets, this function should be called
1793 toward the end of a callback and only after any call to the event
1794 loop (Fl::wait(), Fl::flush(), Fl::check(), fl_ask(), etc.).
1795
1796 When deleting groups or windows, you must only delete the group or
1797 window widget and not the individual child widgets.
1798
1799 \since FLTK 1.3 it is not necessary to remove widgets from their parent
1800 groups or windows before calling this, because it will be done in the
1801 widget's destructor, but it is not a failure to do this nevertheless.
1802
1803 \note In FLTK 1.1 you \b must remove widgets from their parent group
1804 (or window) before deleting them.
1805
1806 \see Fl_Widget::~Fl_Widget()
1807*/
1808void Fl::delete_widget(Fl_Widget *wi) {
1809 if (!wi) return;
1810
1811 if (num_dwidgets >= alloc_dwidgets) {
1812 Fl_Widget **temp;
1813
1814 temp = new Fl_Widget *[alloc_dwidgets + 10];
1815 if (alloc_dwidgets) {
1816 memcpy(temp, dwidgets, alloc_dwidgets * sizeof(Fl_Widget *));
1817 delete[] dwidgets;
1818 }
1819
1820 dwidgets = temp;
1821 alloc_dwidgets += 10;
1822 }
1823
1824 dwidgets[num_dwidgets] = wi;
1825 num_dwidgets ++;
1826}
1827
1828/**
1829 Deletes widgets previously scheduled for deletion.
1830
1831 This is for internal use only. You should never call this directly.
1832
1833 Fl::do_widget_deletion() is called from the FLTK event loop or whenever
1834 you call Fl::wait(). The previously scheduled widgets are deleted in the
1835 same order they were scheduled by calling Fl::delete_widget().
1836
1837 \see Fl::delete_widget(Fl_Widget *wi)
1838*/
1839void Fl::do_widget_deletion() {
1840 if (!num_dwidgets) return;
1841
1842 for (int i = 0; i < num_dwidgets; i ++)
1843 delete dwidgets[i];
1844
1845 num_dwidgets = 0;
1846}
1847
1848static Fl_Widget ***widget_watch = 0;
1849static int num_widget_watch = 0;
1850static int max_widget_watch = 0;
1851
1852/**
1853 Adds a widget pointer to the widget watch list.
1854
1855 \note Internal use only, please use class Fl_Widget_Tracker instead.
1856
1857 This can be used, if it is possible that a widget might be deleted during
1858 a callback or similar function. The widget pointer must be added to the
1859 watch list before calling the callback. After the callback the widget
1860 pointer can be queried, if it is NULL. \e If it is NULL, then the widget has been
1861 deleted during the callback and must not be accessed anymore. If the widget
1862 pointer is \e not NULL, then the widget has not been deleted and can be accessed
1863 safely.
1864
1865 After accessing the widget, the widget pointer must be released from the
1866 watch list by calling Fl::release_widget_pointer().
1867
1868 Example for a button that is clicked (from its handle() method):
1869 \code
1870 Fl_Widget *wp = this; // save 'this' in a pointer variable
1871 Fl::watch_widget_pointer(wp); // add the pointer to the watch list
1872 set_changed(); // set the changed flag
1873 do_callback(); // call the callback
1874 if (!wp) { // the widget has been deleted
1875
1876 // DO NOT ACCESS THE DELETED WIDGET !
1877
1878 } else { // the widget still exists
1879 clear_changed(); // reset the changed flag
1880 }
1881
1882 Fl::release_widget_pointer(wp); // remove the pointer from the watch list
1883 \endcode
1884
1885 This works, because all widgets call Fl::clear_widget_pointer() in their
1886 destructors.
1887
1888 \see Fl::release_widget_pointer()
1889 \see Fl::clear_widget_pointer()
1890
1891 An easier and more convenient method to control widget deletion during
1892 callbacks is to use the class Fl_Widget_Tracker with a local (automatic)
1893 variable.
1894
1895 \see class Fl_Widget_Tracker
1896*/
1897void Fl::watch_widget_pointer(Fl_Widget *&w)
1898{
1899 Fl_Widget **wp = &w;
1900 int i;
1901 for (i=0; i<num_widget_watch; ++i) {
1902 if (widget_watch[i]==wp) return;
1903 }
1904 if (num_widget_watch==max_widget_watch) {
1905 max_widget_watch += 8;
1906 widget_watch = (Fl_Widget***)realloc(widget_watch, sizeof(Fl_Widget**)*max_widget_watch);
1907 }
1908 widget_watch[num_widget_watch++] = wp;
1909#ifdef DEBUG_WATCH
1910 printf ("\nwatch_widget_pointer: (%d/%d) %8p => %8p\n",
1911 num_widget_watch,num_widget_watch,wp,*wp);
1912 fflush(stdout);
1913#endif // DEBUG_WATCH
1914}
1915
1916/**
1917 Releases a widget pointer from the watch list.
1918
1919 This is used to remove a widget pointer that has been added to the watch list
1920 with Fl::watch_widget_pointer(), when it is not needed anymore.
1921
1922 \note Internal use only, please use class Fl_Widget_Tracker instead.
1923
1924 \see Fl::watch_widget_pointer()
1925*/
1926void Fl::release_widget_pointer(Fl_Widget *&w)
1927{
1928 Fl_Widget **wp = &w;
1929 int i,j=0;
1930 for (i=0; i<num_widget_watch; ++i) {
1931 if (widget_watch[i]!=wp) {
1932 if (j<i) widget_watch[j] = widget_watch[i]; // fill gap
1933 j++;
1934 }
1935#ifdef DEBUG_WATCH
1936 else { // found widget pointer
1937 printf ("release_widget_pointer: (%d/%d) %8p => %8p\n",
1938 i+1,num_widget_watch,wp,*wp);
1939 }
1940#endif //DEBUG_WATCH
1941 }
1942 num_widget_watch = j;
1943#ifdef DEBUG_WATCH
1944 printf (" num_widget_watch = %d\n\n",num_widget_watch);
1945 fflush(stdout);
1946#endif // DEBUG_WATCH
1947 return;
1948}
1949/**
1950 Clears a widget pointer \e in the watch list.
1951
1952 This is called when a widget is destroyed (by its destructor). You should never
1953 call this directly.
1954
1955 \note Internal use only !
1956
1957 This method searches the widget watch list for pointers to the widget and
1958 clears each pointer that points to it. Widget pointers can be added to the
1959 widget watch list by calling Fl::watch_widget_pointer() or by using the
1960 helper class Fl_Widget_Tracker (recommended).
1961
1962 \see Fl::watch_widget_pointer()
1963 \see class Fl_Widget_Tracker
1964*/
1965void Fl::clear_widget_pointer(Fl_Widget const *w)
1966{
1967 if (w==0L) return;
1968 int i;
1969 for (i=0; i<num_widget_watch; ++i) {
1970 if (widget_watch[i] && *widget_watch[i]==w) {
1971 *widget_watch[i] = 0L;
1972 }
1973 }
1974}
1975
1976
1977/**
1978 \brief FLTK library options management.
1979
1980 This function needs to be documented in more detail. It can be used for more
1981 optional settings, such as using a native file chooser instead of the FLTK one
1982 wherever possible, disabling tooltips, disabling visible focus, disabling
1983 FLTK file chooser preview, etc. .
1984
1985 There should be a command line option interface.
1986
1987 There should be an application that manages options system wide, per user, and
1988 per application.
1989
1990 \note As of FLTK 1.3.0, options can be managed within fluid, using the menu
1991 <i>Edit/Global FLTK Settings</i>.
1992
1993 \param opt which option
1994 \return true or false
1995 \see enum Fl::Fl_Option
1996 \see Fl::option(Fl_Option, bool)
1997
1998 \since FLTK 1.3.0
1999 */
2000bool Fl::option(Fl_Option opt)
2001{
2002 if (!options_read_) {
2003 int tmp;
2004 { // first, read the system wide preferences
2005 Fl_Preferences prefs(Fl_Preferences::SYSTEM, "fltk.org", "fltk");
2006 Fl_Preferences opt_prefs(prefs, "options");
2007 opt_prefs.get("ArrowFocus", tmp, 0); // default: off
2008 options_[OPTION_ARROW_FOCUS] = tmp;
2009 //opt_prefs.get("NativeFilechooser", tmp, 1); // default: on
2010 //options_[OPTION_NATIVE_FILECHOOSER] = tmp;
2011 //opt_prefs.get("FilechooserPreview", tmp, 1); // default: on
2012 //options_[OPTION_FILECHOOSER_PREVIEW] = tmp;
2013 opt_prefs.get("VisibleFocus", tmp, 1); // default: on
2014 options_[OPTION_VISIBLE_FOCUS] = tmp;
2015 opt_prefs.get("DNDText", tmp, 1); // default: on
2016 options_[OPTION_DND_TEXT] = tmp;
2017 opt_prefs.get("ShowTooltips", tmp, 1); // default: on
2018 options_[OPTION_SHOW_TOOLTIPS] = tmp;
2019 }
2020 { // next, check the user preferences
2021 // override system options only, if the option is set ( >= 0 )
2022 Fl_Preferences prefs(Fl_Preferences::USER, "fltk.org", "fltk");
2023 Fl_Preferences opt_prefs(prefs, "options");
2024 opt_prefs.get("ArrowFocus", tmp, -1);
2025 if (tmp >= 0) options_[OPTION_ARROW_FOCUS] = tmp;
2026 //opt_prefs.get("NativeFilechooser", tmp, -1);
2027 //if (tmp >= 0) options_[OPTION_NATIVE_FILECHOOSER] = tmp;
2028 //opt_prefs.get("FilechooserPreview", tmp, -1);
2029 //if (tmp >= 0) options_[OPTION_FILECHOOSER_PREVIEW] = tmp;
2030 opt_prefs.get("VisibleFocus", tmp, -1);
2031 if (tmp >= 0) options_[OPTION_VISIBLE_FOCUS] = tmp;
2032 opt_prefs.get("DNDText", tmp, -1);
2033 if (tmp >= 0) options_[OPTION_DND_TEXT] = tmp;
2034 opt_prefs.get("ShowTooltips", tmp, -1);
2035 if (tmp >= 0) options_[OPTION_SHOW_TOOLTIPS] = tmp;
2036 }
2037 { // now, if the developer has registered this app, we could as for per-application preferences
2038 }
2039 options_read_ = 1;
2040 }
2041 if (opt<0 || opt>=OPTION_LAST)
2042 return false;
2043 return (bool)(options_[opt]!=0);
2044}
2045
2046/**
2047 \brief Override an option while the application is running.
2048
2049 This function does not change any system or user settings.
2050
2051 \param opt which option
2052 \param val set to true or false
2053 \see enum Fl::Fl_Option
2054 \see bool Fl::option(Fl_Option)
2055 */
2056void Fl::option(Fl_Option opt, bool val)
2057{
2058 if (opt<0 || opt>=OPTION_LAST)
2059 return;
2060 if (!options_read_) {
2061 // first read this option, so we don't override our setting later
2062 option(opt);
2063 }
2064 options_[opt] = val;
2065}
2066
2067
2068// Helper class Fl_Widget_Tracker
2069
2070/**
2071 The constructor adds a widget to the watch list.
2072*/
2073Fl_Widget_Tracker::Fl_Widget_Tracker(Fl_Widget *wi)
2074{
2075 wp_ = wi;
2076 Fl::watch_widget_pointer(wp_); // add pointer to watch list
2077}
2078
2079/**
2080 The destructor removes a widget from the watch list.
2081*/
2082Fl_Widget_Tracker::~Fl_Widget_Tracker()
2083{
2084 Fl::release_widget_pointer(wp_); // remove pointer from watch list
2085}
2086
2087
2088//
2089// End of "$Id: Fl.cxx 8723 2011-05-23 16:49:02Z manolo $".
2090//