diff options
author | Aaron M. Ucko <ucko@debian.org> | 2005-04-05 18:24:06 +0000 |
---|---|---|
committer | Aaron M. Ucko <ucko@debian.org> | 2005-04-05 18:24:06 +0000 |
commit | 555037577b9953b61df882da1e0c2e640a90cbbf (patch) | |
tree | d6517512c6dab145e81033603c480505db8b8cc9 /src/Fl.cxx | |
parent | 3a30e2c54fc02778dae48d4da42a89ef58097179 (diff) |
Load fltk-1.1.3 into branches/upstream/current.
Diffstat (limited to 'src/Fl.cxx')
-rw-r--r-- | src/Fl.cxx | 979 |
1 files changed, 979 insertions, 0 deletions
diff --git a/src/Fl.cxx b/src/Fl.cxx new file mode 100644 index 0000000..36ec180 --- /dev/null +++ b/src/Fl.cxx @@ -0,0 +1,979 @@ +// +// "$Id: Fl.cxx,v 1.24.2.41.2.59 2003/01/30 21:41:14 easysw Exp $" +// +// Main event handling code for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2003 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 to "fltk-bugs@fltk.org". +// + +#include <FL/Fl.H> +#include <FL/Fl_Window.H> +#include <FL/x.H> +#include <FL/Fl_Tooltip.H> +#include <ctype.h> +#include <stdlib.h> +#include "flstring.h" + + +// +// Globals... +// + +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; +char *Fl::e_text = (char *)""; +int Fl::e_length; +int Fl::visible_focus_ = 1, + Fl::dnd_text_ops_ = 1; + + +// +// 'Fl::version()' - Return the API version number... +// + +double +Fl::version() { + return FL_VERSION; +} + + +// +// 'Fl:event_inside()' - Return whether or not the mouse event is inside +// the given rectangle. +// + +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); +} + +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()); +} + +//////////////////////////////////////////////////////////////// +// Timeouts are stored in a sorted list, so only the first one needs +// to be checked to see if any should be called. + +struct Timeout { + double time; + void (*cb)(void*); + void* arg; + Timeout* next; +}; +static Timeout* first_timeout, *free_timeout; + +#ifndef WIN32 +# include <sys/time.h> +#endif + +// 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 actualy elapse time. +static char reset_clock = 1; + +static void elapse_timeouts() { +#ifdef WIN32 + unsigned long newclock = GetTickCount(); + static unsigned long prevclock; + double elapsed = (newclock-prevclock)/1000.0; + prevclock = newclock; +#else + 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; +#endif + 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; +} + +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; +} + +void Fl::remove_timeout(Fl_Timeout_Handler cb, void *argp) { + // This version removes all matching timeouts, not just the first one. + // This may change in the future. + 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); + } + } +} + +//////////////////////////////////////////////////////////////// +// 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; + +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; +} + +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); + } + } +} + +//////////////////////////////////////////////////////////////// +// 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 + +static char in_idle; + +double Fl::wait(double time_to_wait) { + 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 + } + // 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; + } +// 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(); + return fl_wait(time_to_wait); + } +} + +#define FOREVER 1e20 + +int Fl::run() { + while (Fl_X::first) wait(FOREVER); + return 0; +} + +int Fl::wait() { + wait(FOREVER); + return Fl_X::first != 0; // return true if there is a window +} + +int Fl::check() { + wait(0.0); + return Fl_X::first != 0; // return true if there is a window +} + +extern int fl_ready(); + +int Fl::ready() { + if (first_timeout) { + elapse_timeouts(); + if (first_timeout->time <= 0) return 1; + } else { + reset_clock = 1; + } + return fl_ready(); +} + +//////////////////////////////////////////////////////////////// +// Window list management: + +Fl_X* Fl_X::first; + +Fl_Window* fl_find(Window xid) { + Fl_X *window; + for (Fl_X **pp = &Fl_X::first; (window = *pp); pp = &window->next) +#ifdef __APPLE__ + if (window->xid == xid && !window->w->window()) { +#else + if (window->xid == xid) { +#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; +} + +Fl_Window* Fl::first_window() { + Fl_X* i = Fl_X::first; + return i ? i->w : 0; +} + +Fl_Window* Fl::next_window(const Fl_Window* window) { + Fl_X* i = Fl_X::i(window)->next; + return i ? i->w : 0; +} + +void Fl::first_window(Fl_Window* window) { + if (!window || !window->shown()) return; + fl_find(fl_xid(window)); +} + +void Fl::redraw() { + for (Fl_X* i = Fl_X::first; i; i = i->next) i->w->redraw(); +} + +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;} + } + } + +#ifdef WIN32 + GdiFlush(); +#elif defined (__APPLE__) + GrafPtr port; GetPort( &port ); + if ( port ) + { + QDFlushPortBuffer( port, 0 ); + } +#else + if (fl_display) XFlush(fl_display); +#endif +} + +//////////////////////////////////////////////////////////////// +// Event handlers: + +struct handler_link { + int (*handle)(int); + handler_link *next; +}; + +static handler_link *handlers = 0; + +void Fl::add_handler(int (*ha)(int)) { + handler_link *l = new handler_link; + l->handle = ha; + l->next = handlers; + handlers = l; +} + +void Fl::remove_handler(int (*ha)(int)) { + 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... + +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; + fl_oldfocus = 0; + for (; p; p = p->parent()) { + p->handle(FL_UNFOCUS); + fl_oldfocus = p; + } + } +} + +static char dnd_flag = 0; // make 'belowmouse' send DND_LEAVE instead of LEAVE + +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; + for (; p && !p->contains(o); p = p->parent()) { + p->handle(dnd_flag ? FL_DND_LEAVE : FL_LEAVE); + } + } +} + +void Fl::pushed(Fl_Widget *o) { + pushed_ = o; +} + +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 + +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() { + + 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) { + 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); + } 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())) { + w->handle(FL_ENTER); + 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(); + w->handle(FL_MOVE); + } + } else { + Fl::belowmouse(0); + Fl_Tooltip::enter(0); + } + } +} + +#ifndef WIN32 +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) { + if (o->contains(Fl::pushed())) Fl::pushed_ = 0; +#ifndef WIN32 + 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 X windows. 'window' is the outermost +// window the event was posted to by X: +static int send(int event, Fl_Widget* to, Fl_Window* window) { + int dx = window->x(); + int dy = window->y(); + 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(event); + Fl::e_y = save_y; + Fl::e_x = save_x; + return ret; +} + +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->show(); // this calls Fl_Widget::show(), not Fl_Window::show() + return 1; + + case FL_HIDE: + wi->hide(); // this calls Fl_Widget::hide(), not Fl_Window::hide() + return 1; + + case FL_PUSH: + 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(); + {Fl_Widget* pbm = belowmouse(); + int ret = (wi && send(e, wi, window)); + if (pbm != belowmouse()) 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_KEYBOARD: + 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: + {char* c = (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;} + for (; wi; wi = wi->parent()) if (send(FL_SHORTCUT, 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: + fl_xmousewin = window; + fl_fix_focus(); + Fl_Tooltip::enter(belowmouse()); + return 1; + + case FL_LEAVE: + 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 it as keystroke, sending it to focus and all parents: + for (wi = grab() ? grab() : focus(); wi; wi = wi->parent()) + if (send(FL_MOUSEWHEEL, wi, 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__ + // remove all childwindow links + for ( Fl_X *pc = Fl_X::first; pc; pc = pc->next ) + { + if ( pc->xidNext == ip ) pc->xidNext = ip->xidNext; + if ( pc->xidChildren == ip ) pc->xidChildren = ip->xidNext; + } +#endif // __APPLE__ + + 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); + +#ifdef WIN32 + if (ip->private_dc) ReleaseDC(ip->xid,ip->private_dc); + if (ip->xid == fl_window && fl_gc) { + ReleaseDC(fl_window, fl_gc); + fl_window = (HWND)-1; + fl_gc = 0; + } +#elif defined(__APPLE__) + if ( ip->xid == fl_window ) + fl_window = 0; +#else + if (ip->region) XDestroyRegion(ip->region); +#endif + +#ifdef __APPLE__ + if ( !parent() ) // don't destroy shared windows! + { + //+ RemoveTrackingHandler( dndTrackingHandler, ip->xid ); + //+ RemoveReceiveHandler( dndReceiveHandler, ip->xid ); + XDestroyWindow(fl_display, ip->xid); + } +#else +# if USE_XFT + fl_destroy_xft_draw(ip->xid); +# endif + XDestroyWindow(fl_display, ip->xid); +#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(); +} + +// 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 assummed 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 XMapWindow(fl_display, fl_xid(this)); // extra map calls are harmless + break; + case FL_HIDE: + if (shown()) { + // Find what really turned invisible, if is 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 + } + XUnmapWindow(fl_display, fl_xid(this)); + } + break; + } +// } else if (ev == FL_FOCUS || ev == FL_UNFOCUS) { +// Fl_Tooltip::exit(Fl_Tooltip::current()); + } + + return Fl_Group::handle(ev); +} + +//////////////////////////////////////////////////////////////// +// Back compatability cut & paste functions for fltk 1.1 only: + +void Fl::selection_owner(Fl_Widget *owner) {selection_owner_ = owner;} + +void Fl::selection(Fl_Widget &owner, const char* text, int len) { + selection_owner_ = &owner; + Fl::copy(text, len, 0); +} + +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; + + if (align() & FL_ALIGN_BOTTOM) { + window()->damage(FL_DAMAGE_EXPOSE, x(), y() + h(), w(), H); + } else if (align() & FL_ALIGN_TOP) { + window()->damage(FL_DAMAGE_EXPOSE, x(), y() - H, w(), H); + } else if (align() & FL_ALIGN_LEFT) { + window()->damage(FL_DAMAGE_EXPOSE, x() - W, y(), W, h()); + } else if (align() & FL_ALIGN_RIGHT) { + window()->damage(FL_DAMAGE_EXPOSE, x() + w(), y(), W, h()); + } else { + window()->damage(FL_DAMAGE_ALL); + } + } 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 + + if (X<=0 && Y<=0 && W>=wi->w() && H>=wi->h()) { + // if damage covers entire window delete region: + wi->damage(fl); + return; + } + + // 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 (wi->damage()) { + // if we already have damage we must merge with existing region: + if (i->region) { +#ifdef WIN32 + Fl_Region R = XRectangleRegion(X, Y, W, H); + CombineRgn(i->region, i->region, R, RGN_OR); + XDestroyRegion(R); +#elif defined(__APPLE__) + Fl_Region R = NewRgn(); + SetRectRgn(R, X, Y, X+W, Y+H); + UnionRgn(R, i->region, i->region); + DisposeRgn(R); +#else + XRectangle R; + R.x = X; R.y = Y; R.width = W; R.height = H; + XUnionRectWithRegion(&R, i->region, i->region); +#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(); +} + +// +// End of "$Id: Fl.cxx,v 1.24.2.41.2.59 2003/01/30 21:41:14 easysw Exp $". +// |