diff options
Diffstat (limited to 'src/Fl.cxx')
| -rw-r--r-- | src/Fl.cxx | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/src/Fl.cxx b/src/Fl.cxx new file mode 100644 index 000000000..06a0c62b1 --- /dev/null +++ b/src/Fl.cxx @@ -0,0 +1,566 @@ +// Fl.C + +// fltk (Fast Light Tool Kit) version 0.99 +// Copyright (C) 1998 Bill Spitzak + +// 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. + +// Written by Bill Spitzak spitzak@d2.com + +#include <FL/Fl.H> +#include <FL/Fl_Window.H> +#include <FL/x.H> +#include <ctype.h> + +int Fl::damage_; +Fl_Widget *Fl::belowmouse_; +Fl_Widget *Fl::pushed_; +Fl_Widget *Fl::focus_; +Fl_Widget *Fl::selection_owner_; +int Fl::e_x, Fl::e_y, Fl::e_x_root, Fl::e_y_root; +int Fl::e_state; +int Fl::e_clicks; +int Fl::e_is_click; +int Fl::e_keysym; +char *Fl::e_text; +int Fl::e_length; + +int Fl::event_inside(int x,int y,int w,int h) /*const*/ { + int mx = event_x(); + int my = event_y(); + return (mx >= x && mx < x+w && my >= y && my < y+h); +} + +int Fl::event_inside(const Fl_Widget *o) /*const*/ { + return event_inside(o->x(),o->y(),o->w(),o->h()); +} + +// Timeouts are insert-sorted into order. This works good if there +// are only a small number: + +#define MAXTIMEOUT 8 + +static struct { + double time; + void (*cb)(void*); + void* arg; +} timeout[MAXTIMEOUT+1]; +static int numtimeouts; + +void Fl::add_timeout(double t, void (*cb)(void *), void *v) { + int i; + if (numtimeouts<MAXTIMEOUT) numtimeouts++; + for (i=0; i<numtimeouts-1; i++) { + if (timeout[i].time > t) { + for (int j=numtimeouts-1; j>i; j--) timeout[j] = timeout[j-1]; + break; + } + } + timeout[i].time = t; + timeout[i].cb = cb; + timeout[i].arg = v; +} + +void Fl::remove_timeout(void (*cb)(void *), void *v) { + int i,j; + for (i=j=0; i<numtimeouts; i++) { + if (timeout[i].cb == cb && timeout[i].arg==v) ; + else {if (j<i) timeout[j]=timeout[i]; j++;} + } + numtimeouts = j; +} + +static void call_timeouts() { + if (timeout[0].time > 0) return; + struct { + void (*cb)(void *); + void *arg; + } temp[MAXTIMEOUT]; + int i,j,k; + // copy all expired timeouts to temp array: + for (i=j=0; j<numtimeouts && timeout[j].time <= 0; i++,j++) { + temp[i].cb = timeout[j].cb; + temp[i].arg= timeout[j].arg; + } + // remove them from source array: + for (k=0; j<numtimeouts;) timeout[k++] = timeout[j++]; + numtimeouts = k; + // and then call them: + for (k=0; k<i; k++) temp[k].cb(temp[k].arg); +} + +void Fl::flush() { + if (damage()) { + damage_ = 0; + for (Fl_X* x = Fl_X::first; x; x = x->next) { + if (x->w->damage() && x->w->visible()) { + x->flush(); + x->w->clear_damage(); + } + } + } +#ifndef WIN32 + if (fl_display) XFlush(fl_display); +#endif +} + +extern double fl_wait(int timeout_flag, double timeout); +extern int fl_ready(); + +static int initclock; // if false we didn't call fl_elapsed() last time + +#ifndef WIN32 +#include <sys/time.h> +#endif + +// fl_elapsed must return the amount of time since the last time it was +// called. To reduce the number of system calls the to get the +// current time, the "initclock" symbol is turned on by an indefinate +// wait. This should then reset the measured-from time and return zero +static double fl_elapsed() { + +#ifdef WIN32 + + unsigned long newclock = fl_msg.time; // NOT YET IMPLEMENTED! + const int TICKS_PER_SECOND = 1000; // divisor of the value to get seconds + static unsigned long prevclock; + if (!initclock) {prevclock = newclock; initclock = 1; return 0.0;} + double t = double(newclock-prevclock)/TICKS_PER_SECOND; + prevclock = newclock; + +#else + + static struct timeval prevclock; + struct timeval newclock; + gettimeofday(&newclock, 0); + if (!initclock) { + prevclock.tv_sec = newclock.tv_sec; + prevclock.tv_usec = newclock.tv_usec; + initclock = 1; + return 0.0; + } + double t = 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 + + // expire any timeouts: + if (t > 0.0) for (int i=0; i<numtimeouts; i++) timeout[i].time -= t; + return t; +} + +void (*Fl::idle)(); +static char in_idle; +static void callidle() { + if (!Fl::idle || in_idle) return; + in_idle = 1; + Fl::idle(); + in_idle = 0; +} + +int Fl::wait() { + callidle(); + if (numtimeouts) {fl_elapsed(); call_timeouts();} + flush(); + if (!Fl_X::first) return 0; // no windows + if (idle && !in_idle) + fl_wait(1,0.0); + else if (numtimeouts) + fl_wait(1, timeout[0].time); + else { + initclock = 0; + fl_wait(0,0); + } + return 1; +} + +double Fl::wait(double time) { + callidle(); + if (numtimeouts) {time -= fl_elapsed(); call_timeouts();} + flush(); + double wait_time = idle && !in_idle ? 0.0 : time; + if (numtimeouts && timeout[0].time < wait_time) wait_time = timeout[0].time; + fl_wait(1, wait_time); + return time - fl_elapsed(); +} + +int Fl::check() { + callidle(); + if (numtimeouts) {fl_elapsed(); call_timeouts();} + flush(); + if (!Fl_X::first) return 0; // no windows + fl_wait(1, 0.0); + return 1; +} + +int Fl::ready() { + // if (idle && !in_idle) return 1; // should it do this? + if (numtimeouts) {fl_elapsed(); if (timeout[0].time <= 0) return 1;} + return fl_ready(); +} + +int Fl::run() { + while (wait()); + return 0; +} + +//////////////////////////////////////////////////////////////// +// 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) + if (window->xid == xid) { + 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; +} + +void Fl::redraw() { + for (Fl_X* x = Fl_X::first; x; x = x->next) x->w->redraw(); +} + +Fl_Window* Fl::first_window() {Fl_X* x = Fl_X::first; return x ? x->w : 0;} + +Fl_Window* Fl::next_window(const Fl_Window* w) { + Fl_X* x = Fl_X::i(w)->next; return x ? x->w : 0;} + +//////////////////////////////////////////////////////////////// +// Event handlers: + +struct handler_link { + int (*handle)(int); + const handler_link *next; +}; + +static const handler_link *handlers = 0; + +void Fl::add_handler(int (*h)(int)) { + handler_link *l = new handler_link; + l->handle = h; + l->next = handlers; + handlers = l; +} + +static int send_handlers(int event) { + for (const handler_link *h = handlers; h; h = h->next) + if (h->handle(event)) return 1; + return 0; +} + +//////////////////////////////////////////////////////////////// + +Fl_Widget* fl_oldfocus; // kludge for Fl_Group... + +void Fl::focus(Fl_Widget *o) { + Fl_Widget *p = focus_; + if (o != p) { + focus_ = o; + fl_oldfocus = 0; + for (; p && !p->contains(o); p = p->parent()) { + p->handle(FL_UNFOCUS); + fl_oldfocus = p; + } + } +} + +void Fl::belowmouse(Fl_Widget *o) { + Fl_Widget *p = belowmouse_; + if (o != p) { + event_is_click(0); + belowmouse_ = o; + for (; p && !p->contains(o); p = p->parent()) p->handle(FL_LEAVE); + } +} + +// Because mouse events are posted to the outermost window we need to +// adjust them for child windows if they are pushed(). This should also +// be done for the focus() but that is nyi. +static int mouse_dx; +static int mouse_dy; + +void Fl::pushed(Fl_Widget *o) { + pushed_ = o; + mouse_dx = 0; + mouse_dy = 0; + if (o) for (Fl_Widget* w = o; w->parent(); w = w->parent()) { + if (w->type()>=FL_WINDOW) {mouse_dx -= w->x(); mouse_dy -= w->y();} + } +} + +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_; + +// Update modal(), focus() and other state according to system state. +// This is called whenever a window is added or hidden, and whenever +// X says the focus or mouse window have changed, and when grab_ is +// changed. +void fl_fix_focus() { + + // set Fl::modal() based on grab or any modal windows displayed: + if (Fl::grab_) + Fl::modal_ = Fl::grab_; + else { + Fl_Window* w = Fl::first_window(); + while (w && w->parent()) w = Fl::next_window(w); + Fl::modal_ = w && w->modal() ? w : 0; + } + + // set focus based on Fl::modal() and fl_xfocus + Fl_Window *w = fl_xfocus; + while (w && w->parent()) w = w->window(); + if (w) { + if (Fl::modal()) w = Fl::modal(); + if (!w->contains(Fl::focus())) + if (!w->take_focus()) Fl::focus(w); + } else + Fl::focus(0); + + if (Fl::pushed()) { + + // move pushed() to modal window (necessary for menus): + if (Fl::modal() && !Fl::modal()->contains(Fl::pushed())) + Fl::pushed_ = Fl::modal(); + + } else { // set belowmouse only when pushed() is false + + // 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())) { + Fl::belowmouse(w); w->handle(FL_ENTER);} + } else + Fl::belowmouse(0); + } +} + +//////////////////////////////////////////////////////////////// + +int Fl::handle(int event, Fl_Window* window) +{ + Fl_Widget* w = window; + + switch (event) { + + case FL_CLOSE: + if (modal() && window != modal()) return 0; + w->do_callback(); + return 1; + + case FL_SHOW: + ((Fl_Widget*)w)->show(); + return 1; + + case FL_HIDE: + ((Fl_Widget*)w)->hide(); + return 1; + + case FL_PUSH: + if (Fl::grab()) w = Fl::grab(); + else if (Fl::modal() && w != Fl::modal()) return 0; + Fl::pushed_ = w; mouse_dx = mouse_dy = 0; + if (w->handle(event)) return 1; + // raise windows that are clicked on: + window->show(); + return 1; + + case FL_MOVE: + case FL_DRAG: + if (window != fl_xmousewin) { + // this should not happen if enter/leave events were reported + // correctly by the system, but just in case... + fl_xmousewin = window; fl_fix_focus(); + } + if (Fl::pushed()) { + Fl::e_x += mouse_dx; + Fl::e_y += mouse_dy; + event = FL_DRAG; + w = Fl::pushed(); + } else if (Fl::grab()) + w = Fl::grab(); + else if (Fl::modal() && w != Fl::modal()) + w = 0; + break; + + case FL_RELEASE: { + if (Fl::pushed_) w = Fl::pushed_; Fl::pushed_ = 0; + int r = w->handle(event); + fl_fix_focus(); + if (fl_xmousewin) fl_xmousewin->handle(FL_MOVE); + return r;} + + case FL_UNFOCUS: + window = 0; + case FL_FOCUS: + fl_xfocus = window; + Fl::e_keysym = 0; // make sure it is not confused with navigation key + fl_fix_focus(); + return 1; + + case FL_KEYBOARD: + if (window != fl_xfocus) { + // this should not happen if enter/leave events were reported + // correctly by the system, but just in case... + fl_xfocus = window; fl_fix_focus(); + } + // Try it as keystroke, sending it to focus and all parents: + for (w = Fl::focus(); w; w = w->parent()) + if (w->handle(FL_KEYBOARD)) return 1; + + // Try it as shortcut, sending to mouse widget and all parents: + w = Fl::belowmouse(); if (!w) {w = Fl::modal(); if (!w) w = window;} + for (; w; w = w->parent()) if (w->handle(FL_SHORTCUT)) return 1; + + // try using add_handle() functions: + if (send_handlers(FL_SHORTCUT)) return 1; + + // Try swapping the case of the text in the shortcut: + if (isalpha(Fl::event_text()[0])) { + *(char*)(Fl::event_text()) ^= ('A'^'a'); + w = Fl::belowmouse(); if (!w) {w = Fl::modal(); if (!w) w = window;} + for (; w; w = w->parent()) if (w->handle(FL_SHORTCUT)) return 1; + if (send_handlers(FL_SHORTCUT)) return 1; + } + + // make Escape key close windows: + if (Fl::event_key()==FL_Escape) { + window->do_callback(); + return 1; + } + + return 0; + + case FL_ENTER: + fl_xmousewin = window; fl_fix_focus(); + return 1; + + case FL_LEAVE: + if (window == fl_xmousewin) {fl_xmousewin = 0; fl_fix_focus();} + return 1; + + default: + break; + } + if (w && w->handle(event)) return 1; + return send_handlers(event); +} + +//////////////////////////////////////////////////////////////// +// hide() destroys the X window, it does not do unmap! + +void fl_throw_focus(Fl_Widget*); // in Fl_x.C + +void Fl_Window::hide() { + if (!shown()) return; + + // remove from the list of windows: + Fl_X* x = i; + Fl_X** pp = &Fl_X::first; + for (; *pp != x; pp = &(*pp)->next) if (!*pp) return; + *pp = x->next; + i = 0; + + // recursively remove any subwindows: + for (Fl_X *w = Fl_X::first; w;) { + Fl_Window* W = w->w; + if (W->window() == this) { + W->hide(); + W->set_visible(); + w = Fl_X::first; + } else w = w->next; + } + + // Make sure no events are sent to this window: + if (this == fl_xmousewin) fl_xmousewin = 0; + if (this == fl_xfocus) fl_xfocus = 0; + fl_throw_focus(this); + handle(FL_HIDE); + +#ifdef WIN32 + if (x->private_dc) ReleaseDC(x->xid,x->private_dc); + if (x->xid == fl_window) fl_GetDC(0); // releases dc belonging to window +#else + if (x->region) XDestroyRegion(x->region); +#endif + XDestroyWindow(fl_display, x->xid); + + delete x; +} + +Fl_Window::~Fl_Window() { + hide(); +} + +// Child windows must respond to FL_SHOW and FL_HIDE by actually +// doing unmap operations. Outer windows assumme FL_SHOW & FL_HIDE +// are messages from X: + +int Fl_Window::handle(int event) { + if (parent()) switch (event) { + case FL_SHOW: + if (!shown()) show(); + else XMapWindow(fl_display, fl_xid(this)); + break; + case FL_HIDE: + if (shown()) XUnmapWindow(fl_display, fl_xid(this)); + break; + } + return Fl_Group::handle(event); +} + +//////////////////////////////////////////////////////////////// +// ~Fl_Widget() calls this: this function must get rid of any +// global pointers to the widget. This is also called by hide() +// and deactivate(). + +// call this to free a selection (or change the owner): +void Fl::selection_owner(Fl_Widget *owner) { + if (selection_owner_ && owner != selection_owner_) + selection_owner_->handle(FL_SELECTIONCLEAR); + selection_owner_ = owner; +} + +#ifndef WIN32 +Fl_Widget *fl_selection_requestor; // from Fl_cutpaste.C +#endif + +void fl_throw_focus(Fl_Widget *o) { + if (o->contains(Fl::pushed())) Fl::pushed(0); + if (o->contains(Fl::selection_owner())) Fl::selection_owner(0); +#ifndef WIN32 + if (o->contains(fl_selection_requestor)) fl_selection_requestor = 0; +#endif + int fix = 0; + if (o->contains(Fl::belowmouse())) {Fl::belowmouse(0); fix = 1;} + if (o->contains(Fl::focus())) {Fl::focus(0); fix = 1;} + if (fix) fl_fix_focus(); +} + +// End of Fl.C // |
