diff options
| author | Michael R Sweet <michael.r.sweet@gmail.com> | 1998-10-06 18:21:25 +0000 |
|---|---|---|
| committer | Michael R Sweet <michael.r.sweet@gmail.com> | 1998-10-06 18:21:25 +0000 |
| commit | f9039b2ae21988783feae9b362818e7923e82d14 (patch) | |
| tree | 6d6fe3679d73448758f9794e7d4d4f6b22a4adad /src | |
| parent | 67e89232f9ba067825a158734a09e0fa21aacbe3 (diff) | |
Initial revision
git-svn-id: file:///fltk/svn/fltk/trunk@2 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src')
139 files changed, 19765 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 // diff --git a/src/Fl_Adjuster.cxx b/src/Fl_Adjuster.cxx new file mode 100644 index 000000000..a9b39d7bf --- /dev/null +++ b/src/Fl_Adjuster.cxx @@ -0,0 +1,105 @@ +// Fl_Adjuster.C + +// Fltk widget for drag-adjusting a floating point value. + +#include <FL/Fl.H> +#include <FL/Fl_Adjuster.H> +#include <FL/Fl_Bitmap.H> +#include <FL/fl_draw.H> + +#include "fastarrow.h" +static Fl_Bitmap fastarrow(fastarrow_bits, fastarrow_width, fastarrow_height); +#include "mediumarrow.h" +static Fl_Bitmap mediumarrow(mediumarrow_bits, mediumarrow_width, mediumarrow_height); +#include "slowarrow.h" +static Fl_Bitmap slowarrow(slowarrow_bits, slowarrow_width, slowarrow_height); + +// changing the value does not change the appearance: +void Fl_Adjuster::value_damage() {} + +void Fl_Adjuster::draw() { + int dx, dy, W, H; + if (w()>=h()) { + dx = W = w()/3; + dy = 0; H = h(); + } else { + dx = 0; W = w(); + dy = H = h()/3; + } + draw_box(drag==1?FL_DOWN_BOX:box(), x(), y()+2*dy, W, H, color()); + draw_box(drag==2?FL_DOWN_BOX:box(), x()+dx, y()+dy, W, H, color()); + draw_box(drag==3?FL_DOWN_BOX:box(), x()+2*dx, y(), W, H, color()); + fl_color(selection_color()); + fastarrow.draw(x()+(W-fastarrow_width)/2, + y()+2*dy+(H-fastarrow_height)/2, W, H); + mediumarrow.draw(x()+dx+(W-mediumarrow_width)/2, + y()+dy+(H-mediumarrow_height)/2, W, H); + slowarrow.draw(x()+2*dx+(W-slowarrow_width)/2, + y()+(H-slowarrow_width)/2, W, H); +} + +int Fl_Adjuster::handle(int event) { + double v; + int delta; + int mx = Fl::event_x(); + switch (event) { + case FL_PUSH: + ix = mx; + if (w()>=h()) + drag = 3*(mx-x())/w() + 1; + else + drag = 3-3*(Fl::event_y()-y()-1)/h(); + handle_push(); + redraw(); + return 1; + case FL_DRAG: + if (w() >= h()) { + delta = x()+(drag-1)*w()/3; // left edge of button + if (mx < delta) + delta = mx-delta; + else if (mx > delta+w()/3) // right edge of button + delta = mx-delta-w()/3; + else + delta = 0; + } else { + if (mx < x()) + delta = mx-x(); + else if (mx > x()+w()) + delta = mx-x()-w(); + else + delta = 0; + } + switch (drag) { + case 3: v = increment(previous_value(), delta); break; + case 2: v = increment(previous_value(), delta*10); break; + default:v = increment(previous_value(), delta*100); break; + } + handle_drag(soft() ? softclamp(v) : clamp(v)); + return 1; + case FL_RELEASE: + if (Fl::event_is_click()) { // detect click but no drag + if (Fl::event_state()&0xF0000) delta = -10; + else delta = 10; + switch (drag) { + case 3: v = increment(previous_value(), delta); break; + case 2: v = increment(previous_value(), delta*10); break; + default:v = increment(previous_value(), delta*100); break; + } + handle_drag(soft() ? softclamp(v) : clamp(v)); + } + drag = 0; + redraw(); + handle_release(); + return 1; + } + return 0; +} + +Fl_Adjuster::Fl_Adjuster(int x, int y, int w, int h, const char* l) + : Fl_Valuator(x, y, w, h, l) { + box(FL_UP_BOX); + step(1, 10000); + selection_color(FL_BLACK); + drag = 0; + soft_ = 1; +} diff --git a/src/Fl_Bitmap.cxx b/src/Fl_Bitmap.cxx new file mode 100644 index 000000000..b0bb53ab8 --- /dev/null +++ b/src/Fl_Bitmap.cxx @@ -0,0 +1,109 @@ +/* Fl_Bitmap.C + + Draw a bitmap in a box. + +*/ + +#include <FL/Fl.H> +#include <FL/x.H> +#include <FL/fl_draw.H> +#include <FL/Fl_Widget.H> +#include <FL/Fl_Menu_Item.H> +#include <FL/Fl_Bitmap.H> + +void Fl_Bitmap::draw(int X, int Y, int W, int H, int cx,int cy) { + // clip the box down to the size of image, quit if empty: + if (cx < 0) {W += cx; X -= cx; cx = 0;} + if (cx+W > w) W = w-cx; + if (W <= 0) return; + if (cy < 0) {H += cy; Y -= cy; cy = 0;} + if (cy+H > h) H = h-cy; + if (H <= 0) return; +#ifdef WIN32 + if (!id) { + // we need to pad the lines out to words & swap the bits + // in each byte. + int w1 = (w+7)/8; + int w2 = ((w+15)/16)*2; + uchar* newarray = new uchar[w2*h]; + const uchar* src = array; + uchar* dest = newarray; + for (int y=0; y < h; y++) { + for (int n = 0; n < w1; n++) { + *dest++ = + ((*src&0x01) << 7) + + ((*src&0x02) << 5) + + ((*src&0x04) << 3) + + ((*src&0x08) << 1) + + ((*src&0x10) >> 1) + + ((*src&0x20) >> 3) + + ((*src&0x40) >> 5) + + ((*src&0x80) >> 7); + src++; + } + dest += w2-w1; + } + id = (ulong)CreateBitmap(w, h, 1, 1, newarray); + array = newarray; // keep the pointer so I can free it later + } + HDC tempdc = CreateCompatibleDC(fl_gc); + SelectObject(tempdc, (HGDIOBJ)id); + SelectObject(fl_gc, fl_brush()); + // secret bitblt code found in old MSWindows reference manual: + BitBlt(fl_gc, X, Y, W, H, tempdc, cx, cy, 0xE20746L); + DeleteDC(tempdc); +#else + if (!id) id = XCreateBitmapFromData(fl_display, fl_window, + (const char*)array, (w+7)&-8, h); + XSetStipple(fl_display, fl_gc, id); + int ox = X-cx; if (ox < 0) ox += w; + int oy = Y-cy; if (oy < 0) oy += h; + XSetTSOrigin(fl_display, fl_gc, ox, oy); + XSetFillStyle(fl_display, fl_gc, FillStippled); + XFillRectangle(fl_display, fl_window, fl_gc, X, Y, W, H); + XSetFillStyle(fl_display, fl_gc, FillSolid); +#endif +} + +Fl_Bitmap::~Fl_Bitmap() { +#ifdef WIN32 + if (id) { + DeleteObject((HGDIOBJ)id); + delete[] (uchar*)array; + } +#else + if (id) fl_delete_offscreen((Fl_Offscreen)id); +#endif +} + +static void bitmap_labeltype( + const Fl_Label* o, int x, int y, int w, int h, Fl_Align a) +{ + Fl_Bitmap* b = (Fl_Bitmap*)(o->value); + int cx; + if (a & FL_ALIGN_LEFT) cx = 0; + else if (a & FL_ALIGN_RIGHT) cx = b->w-w; + else cx = (b->w-w)/2; + int cy; + if (a & FL_ALIGN_TOP) cy = 0; + else if (a & FL_ALIGN_BOTTOM) cy = b->h-h; + else cy = (b->h-h)/2; + fl_color((Fl_Color)o->color); + b->draw(x,y,w,h,cx,cy); +} + +static void bitmap_measure(const Fl_Label* o, int& w, int& h) { + Fl_Bitmap* b = (Fl_Bitmap*)(o->value); + w = b->w; + h = b->h; +} + +void Fl_Bitmap::label(Fl_Widget* o) { + Fl::set_labeltype(_FL_BITMAP_LABEL, bitmap_labeltype, bitmap_measure); + o->label(_FL_BITMAP_LABEL, (const char*)this); +} + +void Fl_Bitmap::label(Fl_Menu_Item* o) { + Fl::set_labeltype(_FL_BITMAP_LABEL, bitmap_labeltype, bitmap_measure); + o->label(_FL_BITMAP_LABEL, (const char*)this); +} diff --git a/src/Fl_Box.cxx b/src/Fl_Box.cxx new file mode 100644 index 000000000..48b968b39 --- /dev/null +++ b/src/Fl_Box.cxx @@ -0,0 +1,11 @@ +// Fl_Box.C + +// The box widget. An almost non-functional subclass of Fl_Widget. + +#include <FL/Fl_Widget.H> +#include <FL/Fl_Box.H> + +void Fl_Box::draw() { + draw_box(); + draw_label(); +} diff --git a/src/Fl_Browser.cxx b/src/Fl_Browser.cxx new file mode 100644 index 000000000..f58447386 --- /dev/null +++ b/src/Fl_Browser.cxx @@ -0,0 +1,421 @@ +// Fl_Browser.C + +// Forms-compatable browser. Probably useful for other lists of +// textual data. + +// I modified this from the original Forms data to use a linked list +// so that the number of items in the browser and size of those items +// is unlimited. The only problem is that the old browser used an +// index number to identify a line, and it is slow to convert from/to +// a pointer. I use a cache of the last match to try to speed this +// up. + +// Also added the ability to "hide" a line. This set's it's height to +// zero, so the Fl_Browser_ cannot pick it. + +#include <FL/Fl.H> +#include <FL/Fl_Browser.H> +#include <FL/fl_draw.H> +#include <string.h> +#include <stdlib.h> +#include <math.h> +#include <FL/Fl_Input_.H> // for default_font + +#define SELECTED 1 +#define NOTDISPLAYED 2 + +struct FL_BLINE { // data is in a linked list of these + FL_BLINE* prev; + FL_BLINE* next; + void* data; + short length; // sizeof(txt)-1, may be longer than string + char flags; // selected, displayed + char txt[1]; // start of allocated array +}; + +void* Fl_Browser::item_first() const {return first;} + +void* Fl_Browser::item_next(void* l) const {return ((FL_BLINE*)l)->next;} + +void* Fl_Browser::item_prev(void* l) const {return ((FL_BLINE*)l)->prev;} + +int Fl_Browser::item_selected(void* l) const { + return ((FL_BLINE*)l)->flags&SELECTED;} + +void Fl_Browser::item_select(void* l, int v) { + if (v) ((FL_BLINE*)l)->flags |= SELECTED; + else ((FL_BLINE*)l)->flags &= ~SELECTED; +} + +FL_BLINE* Fl_Browser::find_line(int line) const { + int n; FL_BLINE* l; + if (line == cacheline) return cache; + if (cacheline && line > cacheline/2 && line < (cacheline+lines)/2) { + n = cacheline; l = cache; + } else if (line <= lines/2) { + n = 1; l = first; + } else { + n = lines; l = last; + } + for (; n < line && l; n++) l = l->next; + for (; n > line && l; n--) l = l->prev; + ((Fl_Browser*)this)->cacheline = line; + ((Fl_Browser*)this)->cache = l; + return l; +} + +int Fl_Browser::lineno(void* v) const { + FL_BLINE* l = (FL_BLINE*)v; + if (!l) return 0; + if (l == cache) return cacheline; + if (l == first) return 1; + if (l == last) return lines; + if (!cache) { + ((Fl_Browser*)this)->cache = first; + ((Fl_Browser*)this)->cacheline = 1; + } + // assumme it is near cache, search both directions: + FL_BLINE* b = cache->prev; + int bnum = cacheline-1; + FL_BLINE* f = cache->next; + int fnum = cacheline+1; + int n = 0; + for (;;) { + if (b == l) {n = bnum; break;} + if (f == l) {n = fnum; break;} + if (b) {b = b->prev; bnum--;} + if (f) {f = f->next; fnum++;} + } + ((Fl_Browser*)this)->cache = l; + ((Fl_Browser*)this)->cacheline = n; + return n; +} + +FL_BLINE* Fl_Browser::_remove(int line) { + FL_BLINE* ttt = find_line(line); + deleting(ttt); + + cacheline = line-1; + cache = ttt->prev; + if (ttt->prev) ttt->prev->next = ttt->next; + else first = ttt->next; + if (ttt->next) ttt->next->prev = ttt->prev; + else last = ttt->prev; + + lines--; + full_height_ -= item_height(ttt); + return(ttt); +} + +void Fl_Browser::remove(int line) { + if (line < 1 || line > lines) return; + free(_remove(line)); +} + +void Fl_Browser::insert(int line, FL_BLINE* t) { + if (!first) { + t->prev = t->next = 0; + first = last = t; + } else if (line <= 1) { + inserting(first, t); + t->prev = 0; + t->next = first; + t->next->prev = t; + first = t; + } else if (line > lines) { + t->prev = last; + t->prev->next = t; + t->next = 0; + last = t; + } else { + FL_BLINE* n = find_line(line); + inserting(n, t); + t->next = n; + t->prev = n->prev; + t->prev->next = t; + n->prev = t; + } + cacheline = line; + cache = t; + lines++; + full_height_ += item_height(t); + redraw_line(t); +} + +void Fl_Browser::insert(int line, const char* newtext, void* data) { + int l = strlen(newtext); + FL_BLINE* t = (FL_BLINE*)malloc(sizeof(FL_BLINE)+l); + t->length = l; + t->flags = 0; + strcpy(t->txt, newtext); + t->data = data; + insert(line, t); +} + +void Fl_Browser::move(int to, int from) { + if (from < 1 || from > lines) return; + insert(to, _remove(from)); +} + +void Fl_Browser::text(int line, const char* newtext) { + if (line < 1 || line > lines) return; + FL_BLINE* t = find_line(line); + int l = strlen(newtext); + if (l > t->length) { + FL_BLINE* n = (FL_BLINE*)malloc(sizeof(FL_BLINE)+l); + replacing(t, n); + cache = n; + n->length = l; + n->flags = t->flags; + n->prev = t->prev; + if (n->prev) n->prev->next = n; else first = n; + n->next = t->next; + if (n->next) n->next->prev = n; else last = n; + free(t); + t = n; + } + strcpy(t->txt, newtext); + redraw_line(t); +} + +void Fl_Browser::data(int line, void* data) { + if (line < 1 || line > lines) return; + find_line(line)->data = data; +} + +int Fl_Browser::item_height(void* lv) const { + FL_BLINE* l = (FL_BLINE*)lv; + if (l->flags & NOTDISPLAYED) return 0; + char* str = l->txt; + int t = textsize()+2; + if (*str == format_char()) switch (*(str+1)) { + case 'l': case 'L': t = 26; break; + case 'm': case 'M': t = 20; break; + case 's': case 'S': t = 13; break; + } + return t + Fl_Input_::default_size(); +} + +int Fl_Browser::item_width(void* v) const { + char* str = ((FL_BLINE*)v)->txt; + const int* i = column_widths(); + int w = 0; + + while (*i) { // add up all tab-seperated fields + w += *i++; + char* e; + for (e = str; *e && *e != column_char(); e++); + if (!*e) return 0; // last one occupied by text + str = e+1; + } + + // OK, we gotta parse the string and find the string width... + int size = textsize(); + Fl_Font font = textfont(); + int done = 0; + + while (*str == format_char_ && *++str && *str != format_char_) { + switch (*str++) { + case 'l': case 'L': size = 24; break; + case 'm': case 'M': size = 18; break; + case 's': size = 11; break; + case 'b': font = (Fl_Font)(font|FL_BOLD); break; + case 'i': font = (Fl_Font)(font|FL_ITALIC); break; + case 'f': case 't': font = FL_COURIER; break; + case 'S': + size = strtol(str, &str, 10); + break; + case '.': + done = 1; + case '@': + str--; + done = 1; + } + + if (done) + break; + } + + fl_font(font, size, Fl_Input_::default_font(), Fl_Input_::default_size()); + return w + int(fl_width(str)) + 6; +} + +int Fl_Browser::full_height() const { + return full_height_; +} + +int Fl_Browser::incr_height() const { + return textsize()+2; +} + +void Fl_Browser::item_draw(void* v, int x, int y, int w, int h) const { + char* str = ((FL_BLINE*)v)->txt; + const int* i = column_widths(); + + while (w > 6) { // do each tab-seperated field + int w1 = w; // width for this field + char* e = 0; // pointer to end of field or null if none + if (*i) { // find end of field and temporarily replace with 0 + for (e = str; *e && *e != column_char(); e++); + if (*e) {*e = 0; w1 = *i++;} else e = 0; + } + int size = textsize(); + Fl_Font font = textfont(); + Fl_Color lcol = textcolor(); + Fl_Align align = FL_ALIGN_LEFT; + // check for all the @-lines recognized by XForms: + while (*str == format_char() && *++str && *str != format_char()) { + switch (*str++) { + case 'l': case 'L': size = 24; break; + case 'm': case 'M': size = 18; break; + case 's': size = 11; break; + case 'b': font = (Fl_Font)(font|FL_BOLD); break; + case 'i': font = (Fl_Font)(font|FL_ITALIC); break; + case 'f': case 't': font = FL_COURIER; break; + case 'c': align = FL_ALIGN_CENTER; break; + case 'r': align = FL_ALIGN_RIGHT; break; + case 'B': + fl_color((Fl_Color)strtol(str, &str, 10)); + fl_rectf(x, y, w1, h); + break; + case 'C': + lcol = (Fl_Color)strtol(str, &str, 10); + break; + case 'F': + font = (Fl_Font)strtol(str, &str, 10); + break; + case 'N': + lcol = FL_INACTIVE_COLOR; + break; + case 'S': + size = strtol(str, &str, 10); + break; + case '-': + fl_color(FL_DARK3); + fl_line(x+3, y+h/2, x+w1-3, y+h/2); + fl_color(FL_LIGHT3); + fl_line(x+3, y+h/2+1, x+w1-3, y+h/2+1); + break; + case 'u': + case '_': + fl_color(lcol); + fl_line(x+3, y+h-1, x+w1-3, y+h-1); + break; + case '.': + goto BREAK; + case '@': + str--; goto BREAK; + } + } + BREAK: + fl_font(font, size, Fl_Input_::default_font(), Fl_Input_::default_size()); + if (!active_r()) lcol = inactive(lcol); + if (((FL_BLINE*)v)->flags & SELECTED) + lcol = contrast(lcol, selection_color()); + fl_color(lcol); + fl_draw(str, x+3, y, w1-6, h, align); + if (!e) break; // no more fields... + *e = column_char(); // put the seperator back + x += w1; + w -= w1; + str = e+1; + } +} + +static const int no_columns[1] = {0}; + +Fl_Browser::Fl_Browser(int x, int y, int w, int h, const char*l) + : Fl_Browser_(x, y, w, h, l) { + column_widths_ = no_columns; + lines = 0; + full_height_ = 0; + cacheline = 0; + format_char_ = '@'; + column_char_ = '\t'; + first = last = cache = 0; +} + +void Fl_Browser::topline(int line) { + if (line<1) line = 1; + if (line>lines) line = lines; + int p = 0; + for (FL_BLINE* l=first; l&& line>1; l = l->next) { + line--; p += item_height(l); + } + position(p); +} + +int Fl_Browser::topline() const { + return lineno(top()); +} + +void Fl_Browser::clear() { + for (FL_BLINE* l = first; l;) { + FL_BLINE* h = l->next; + free(l); + l = h; + } + full_height_ = 0; + first = 0; + lines = 0; + new_list(); +} + +void Fl_Browser::add(const char* newtext, void* data) { + insert(lines+1, newtext, data); + Fl_Browser_::display(last); +} + +const char* Fl_Browser::text(int line) const { + if (line < 1 || line > lines) return 0; + return find_line(line)->txt; +} + +void* Fl_Browser::data(int line) const { + if (line < 1 || line > lines) return 0; + return find_line(line)->data; +} + +int Fl_Browser::select(int line, int value) { + if (line < 1 || line > lines) return 0; + return Fl_Browser_::select(find_line(line), value); +} + +int Fl_Browser::selected(int line) const { + if (line < 1 || line > lines) return 0; + return find_line(line)->flags & SELECTED; +} + +void Fl_Browser::show(int line) { + FL_BLINE* t = find_line(line); + if (t->flags & NOTDISPLAYED) { + t->flags &= ~NOTDISPLAYED; + full_height_ += item_height(t); + if (Fl_Browser_::displayed(t)) redraw_lines(); + } +} + +void Fl_Browser::hide(int line) { + FL_BLINE* t = find_line(line); + if (!(t->flags & NOTDISPLAYED)) { + full_height_ -= item_height(t); + t->flags |= NOTDISPLAYED; + if (Fl_Browser_::displayed(t)) redraw_lines(); + } +} + +void Fl_Browser::display(int line, int value) { + if (line < 1 || line > lines) return; + if (value) show(line); else hide(line); +} + +int Fl_Browser::visible(int line) const { + if (line < 1 || line > lines) return 0; + return !(find_line(line)->flags&NOTDISPLAYED); +} + +int Fl_Browser::value() const { + return lineno(selection()); +} + +// end of Fl_Browser.C diff --git a/src/Fl_Browser_.cxx b/src/Fl_Browser_.cxx new file mode 100644 index 000000000..ae1908808 --- /dev/null +++ b/src/Fl_Browser_.cxx @@ -0,0 +1,606 @@ +// Fl_Browser_.C + +// This is the base class for browsers. To be useful it must be +// subclassed and several virtual functions defined. The +// Forms-compatable browser and the file chooser's browser are +// subclassed off of this. + +// Yes, I know this should be a template... + +// This has been designed so that the subclass has complete control +// over the storage of the data, although because next() and prev() +// functions are used to index, it works best as a linked list or as a +// large block of characters in which the line breaks must be searched +// for. + +// A great deal of work has been done so that the "height" of a data +// object does not need to be determined until it is drawn. This was +// done for the file chooser, because the height requires doing stat() +// to see if the file is a directory, which can be annoyingly slow +// over the network. + +#include <FL/Fl.H> +#include <FL/Fl_Widget.H> +#include <FL/Fl_Browser_.H> +#include <FL/fl_draw.H> +#include <FL/Fl_Input_.H> // for default_font + +/* redraw bits: + 1 = redraw children (the scrollbar) + 2 = redraw one or two items + 4 = redraw all items +*/ + +static void scrollbar_callback(Fl_Widget* s, void*) { + ((Fl_Browser_*)(s->parent()))->position(int(((Fl_Scrollbar*)s)->value())); +} + +static void hscrollbar_callback(Fl_Widget* s, void*) { + ((Fl_Browser_*)(s->parent()))->hposition(int(((Fl_Scrollbar*)s)->value())); +} + +int Fl_Browser_::scrollbar_width_ = 17; + +// return where to draw the actual box: +void Fl_Browser_::bbox(int& X, int& Y, int& W, int& H) const { + Fl_Boxtype b = box() ? box() : Fl_Input_::default_box(); + X = x()+Fl::box_dx(b); + Y = y()+Fl::box_dy(b); + W = w()-Fl::box_dw(b); + H = h()-Fl::box_dh(b); + if (scrollbar.visible()) { + W -= scrollbar_width_; + if (scrollbar.align() & FL_ALIGN_LEFT) X += scrollbar_width_; + } + if (hscrollbar.visible()) { + H -= scrollbar_width_; + if (scrollbar.align() & FL_ALIGN_TOP) Y += scrollbar_width_; + } +} + +int Fl_Browser_::leftedge() const { + int X, Y, W, H; bbox(X, Y, W, H); + return X; +} + +// the scrollbars are resized & placed by draw(), since each one's size +// depends on whether the other is visible or not. This skips over +// Fl_Group::resize since it moves the scrollbars uselessly. +void Fl_Browser_::resize(int X, int Y, int W, int H) { + Fl_Widget::resize(X, Y, W, H); +} + +// Cause minimal update to redraw the given item: +void Fl_Browser_::redraw_line(void* l) { + if (!redraw1 || redraw1 == l) {redraw1 = l; damage(2);} + else if (!redraw2 || redraw2 == l) {redraw2 = l; damage(2);} + else damage(4); +} + +// Figure out top() based on position(): +void Fl_Browser_::update_top() { + if (!top_) top_ = item_first(); + if (position_ != real_position_) { + void* l; + int ly; + int y = position_; + // start from either head or current position, whichever is closer: + if (!top_ || y <= real_position_/2) { + l = item_first(); + ly = 0; + } else { + l = top_; + ly = real_position_-offset_; + } + if (!l) { + top_ = 0; + offset_ = 0; + real_position_ = 0; + } else { + int h = item_quick_height(l); + // step through list until we find line containing this point: + while (ly > y) { + void* l1 = item_prev(l); + if (!l1) {ly = 0; break;} // hit the top + l = l1; + h = item_quick_height(l); + ly -= h; + } + while (ly+h <= y) { + void* l1 = item_next(l); + if (!l1) {y = ly+h-1; break;} + l = l1; + ly += h; + h = item_quick_height(l); + } + // top item must *really* be visible, use slow height: + for (;;) { + h = item_height(l); + if (ly+h > y) break; // it is big enough to see + // go up to top of previous item: + void* l1 = item_prev(l); + if (!l1) {ly = y = 0; break;} // hit the top + l = l1; y = position_ = ly = ly-item_quick_height(l); + } + // use it: + top_ = l; + offset_ = y-ly; + real_position_ = y; + } + damage(4); + } +} + +// Change position(), top() will update when update_top() is called +// (probably by draw() or handle()): +void Fl_Browser_::position(int y) { + if (y < 0) y = 0; + if (y == position_) return; + position_ = y; + if (y != real_position_) redraw_lines(); +} + +void Fl_Browser_::hposition(int x) { + if (x < 0) x = 0; + if (x == hposition_) return; + hposition_ = x; + if (x != real_hposition_) redraw_lines(); +} + +// Tell whether item is currently displayed: +int Fl_Browser_::displayed(void* x) const { + int X, Y, W, H; bbox(X, Y, W, H); + int yy = H+offset_; + for (void* l = top_; l && yy > 0; l = item_next(l)) { + if (l == x) return 1; + yy -= item_height(l); + } + return 0; +} + +// Insure this item is displayed: +// Messy because we have no idea if it is before top or after bottom: +void Fl_Browser_::display(void* x) { + if (!top_) top_ = item_first(); + if (x == item_first()) {position(0); return;} + int X, Y, W, H; bbox(X, Y, W, H); + void* l = top_; + Y = -offset_; + // see if it is at the top or just above it: + if (l == x) {position(real_position_+Y); return;} // scroll up a bit + void* lp = item_prev(l); + if (lp == x) {position(real_position_+Y-item_quick_height(lp)); return;} + // search forward for it: + for (; l; l = item_next(l)) { + int h1 = item_quick_height(l); + if (l == x) { + if (Y <= H) { // it is visible or right at bottom + Y = Y+h1-H; // find where bottom edge is + if (Y > 0) position(real_position_+Y); // scroll down a bit + } else { + position(real_position_+Y-(H-h1)/2); // center it + } + return; + } + Y += h1; + } + // search backward for it, if found center it: + l = lp; + Y = -offset_; + for (; l; l = item_prev(l)) { + int h1 = item_quick_height(l); + Y -= h1; + if (l == x) { + if (Y + h1 >= 0) position(real_position_+Y); + else position(real_position_+Y-(H-h1)/2); + return; + } + } +} + +// redraw, has side effect of updating top and setting scrollbar: +void Fl_Browser_::draw() { + int drawsquare = 0; + if (damage() & 128) { // redraw the box if full redraw + Fl_Boxtype b = box() ? box() : Fl_Input_::default_box(); + draw_box(b, x(), y(), w(), h(), color()); + drawsquare = 1; + } + + update_top(); + int full_width_ = full_width(); + int full_height_ = full_height(); + int X, Y, W, H; bbox(X, Y, W, H); +J1: + // see if scrollbar needs to be switched on/off: + if ((has_scrollbar_ & VERTICAL) && ( + (has_scrollbar_ & ALWAYS_ON) || position_ || full_height_ > H)) { + if (!scrollbar.visible()) {scrollbar.show(); drawsquare = 1;} + } else { + top_ = item_first(); real_position_ = offset_ = 0; + scrollbar.hide(); + } + + if ((has_scrollbar_ & HORIZONTAL) && ( + (has_scrollbar_ & ALWAYS_ON) || hposition_ || full_width_ > W)) { + if (!hscrollbar.visible()) {hscrollbar.show(); drawsquare = 1;} + } else { + real_hposition_ = 0; + hscrollbar.hide(); + } + + bbox(X, Y, W, H); + + fl_clip(X, Y, W, H); + // for each line, draw it if full redraw or scrolled. Erase background + // if not a full redraw or if it is selected: + void* l = top(); + int yy = -offset_; + for (; l && yy < H; l = item_next(l)) { + int hh = item_height(l); + if (hh <= 0) continue; + if ((damage()&4) || l == redraw1 || l == redraw2) { + if (item_selected(l)) { + fl_color(selection_color()); + fl_rectf(X, yy+Y, W, hh); + } else if (!(damage()&128)) { + fl_color(color()); + fl_rectf(X, yy+Y, W, hh); + } + if (type() == FL_MULTI_BROWSER && l == selection_) { + fl_color(textcolor()); + fl_rect(X+1, yy+Y, W-2, hh); + } + item_draw(l, X-hposition_, yy+Y, W, hh); + int w = item_width(l); + if (w > max_width) {max_width = w; max_width_item = l;} + } + yy += hh; + } + // erase the area below last line: + if (!(damage()&128) && yy < H) { + fl_color(color()); + fl_rectf(X, yy+Y, W, H-yy); + } + fl_pop_clip(); + redraw1 = redraw2 = 0; + + // see if changes to full_height caused by calls to slow_height + // caused scrollbar state to change, in which case we have to redraw: + full_height_ = full_height(); + full_width_ = full_width(); + if ((has_scrollbar_ & VERTICAL) && ( + (has_scrollbar_ & ALWAYS_ON) || position_ || full_height_>H)) { + if (!scrollbar.visible()) goto J1; + } else { + if (scrollbar.visible()) goto J1; + } + if ((has_scrollbar_ & HORIZONTAL) && ( + (has_scrollbar_ & ALWAYS_ON) || hposition_ || full_width_>W)) { + if (!hscrollbar.visible()) goto J1; + } else { + if (hscrollbar.visible()) goto J1; + } + + // update the scrollbars and redraw them: + int dy = top_ ? item_quick_height(top_) : 0; if (dy < 10) dy = 10; + if (scrollbar.visible()) { + scrollbar.damage_resize( + scrollbar.align()&FL_ALIGN_LEFT ? X-scrollbar_width_ : X+W, + Y, scrollbar_width_, H); + scrollbar.value(position_, H, 0, full_height_); + scrollbar.linesize(dy); + if (drawsquare) draw_child(scrollbar); + else update_child(scrollbar); + } + if (hscrollbar.visible()) { + hscrollbar.damage_resize( + X, scrollbar.align()&FL_ALIGN_TOP ? Y-scrollbar_width_ : Y+H, + W, scrollbar_width_); + hscrollbar.value(hposition_, W, 0, full_width_); + hscrollbar.linesize(dy); + if (drawsquare) draw_child(hscrollbar); + else update_child(hscrollbar); + } + + // draw that little square between the scrolbars: + if (drawsquare && scrollbar.visible() && hscrollbar.visible()) { + fl_color(parent()->color()); + fl_rectf(scrollbar.x(), hscrollbar.y(), scrollbar_width_,scrollbar_width_); + } + + real_hposition_ = hposition_; +} + +// Quick way to delete and reset everything: +void Fl_Browser_::new_list() { + top_ = 0; + position_ = real_position_ = 0; + hposition_ = real_hposition_ = 0; + selection_ = 0; + offset_ = 0; + max_width = 0; + max_width_item = 0; + redraw_lines(); +} + +// Tell it that this item is going away, and that this must remove +// all pointers to it: +void Fl_Browser_::deleting(void* l) { + if (displayed(l)) redraw_lines(); + if (l == selection_) selection_ = 0; + if (l == top_) { + real_position_ -= offset_; + offset_ = 0; + top_ = item_next(l); + if (!top_) top_ = item_prev(l); + } + if (l == max_width_item) {max_width_item = 0; max_width = 0;} +} + +void Fl_Browser_::replacing(void* a, void* b) { + redraw_line(a); + if (a == selection_) selection_ = b; + if (a == top_) top_ = b; + if (a == max_width_item) {max_width_item = 0; max_width = 0;} +} + +void Fl_Browser_::inserting(void* a, void* b) { + if (displayed(a)) redraw_lines(); + if (a == top_) top_ = b; +} + +void* Fl_Browser_::find_item(int my) { + update_top(); + int X, Y, W, H; bbox(X, Y, W, H); + void* l; + int yy = Y-offset_; + for (l = top_; l; l = item_next(l)) { + int hh = item_height(l); if (hh <= 0) continue; + yy += hh; + if (my <= yy || yy>=Y+H) return l; + } + return 0; +} + +int Fl_Browser_::select(void* l, int i, int docallbacks) { + if (type() == FL_MULTI_BROWSER) { + if (selection_ != l) { + if (selection_) redraw_line(selection_); + selection_ = l; + redraw_line(l); + } + if ((!i)==(!item_selected(l))) return 0; + item_select(l, i); + redraw_line(l); + } else { + if (i && selection_ == l) return 0; + if (!i && selection_ != l) return 0; + if (selection_) { + item_select(selection_, 0); + redraw_line(selection_); + selection_ = 0; + } + if (i) { + item_select(l, 1); + selection_ = l; + redraw_line(l); + display(l); + } + } + Fl::event_clicks(0); + if (docallbacks) do_callback(); + return 1; +} + +int Fl_Browser_::deselect(int docallbacks) { + if (type() == FL_MULTI_BROWSER) { + int change = 0; + for (void* p = item_first(); p; p = item_next(p)) + change |= select(p, 0, docallbacks); + return change; + } else { + if (!selection_) return 0; + item_select(selection_, 0); + redraw_line(selection_); + selection_ = 0; + return 1; + } +} + +int Fl_Browser_::select_only(void* l, int docallbacks) { + if (!l) return deselect(docallbacks); + int change = 0; + if (type() == FL_MULTI_BROWSER) { + for (void* p = item_first(); p; p = item_next(p)) + if (p != l) change |= select(p, 0, docallbacks); + } + change |= select(l, 1, docallbacks); + display(l); + return change; +} + +int Fl_Browser_::handle(int event) { + + // must do shortcuts first or the scrollbar will get them... + if (event == FL_SHORTCUT && type() >= FL_HOLD_BROWSER) { + void* l1 = selection_; + void* l = l1; if (!l) l = top_; if (!l) l = item_first(); + if (l) { + if (type()==FL_HOLD_BROWSER) switch (Fl::event_key()) { + case FL_Down: + while ((l = item_next(l))) + if (item_height(l)>0) {select_only(l, 1); break;} + return 1; + case FL_Up: + while ((l = item_prev(l))) if (item_height(l)>0) { + select_only(l, 1); break;} + return 1; + } else switch (Fl::event_key()) { + case FL_Enter: + select_only(l, 1); + return 1; + case ' ': + selection_ = l; + select(l, !item_selected(l), 1); + return 1; + case FL_Down: + while ((l = item_next(l))) { + if (Fl::event_state(FL_SHIFT|FL_CTRL)) + select(l, l1 ? item_selected(l1) : 1, 1); + if (item_height(l)>0) goto J1; + } + return 1; + case FL_Up: + while ((l = item_prev(l))) { + if (Fl::event_state(FL_SHIFT|FL_CTRL)) + select(l, l1 ? item_selected(l1) : 1, 1); + if (item_height(l)>0) goto J1; + } + return 1; + J1: + if (selection_) redraw_line(selection_); + selection_ = l; redraw_line(l); + display(l); + return 1; + } + } + } + + if (Fl_Group::handle(event)) return 1; + int X, Y, W, H; bbox(X, Y, W, H); + int my; + static char change; + static char whichway; + static int py; + switch (event) { + case FL_PUSH: + if (!Fl::event_inside(X, Y, W, H)) return 0; + if (type() == FL_SELECT_BROWSER) deselect(); + my = py = Fl::event_y(); + change = 0; + if (type() == FL_NORMAL_BROWSER || !top_) + ; + else if (type() == FL_MULTI_BROWSER) { + void* l = find_item(my); + whichway = 1; + if (Fl::event_state(FL_SHIFT|FL_CTRL)) { // toggle selection: + if (l) { + whichway = !item_selected(l); + change = select(l, whichway, when() & FL_WHEN_CHANGED); + } + } else { + change = select_only(l, when() & FL_WHEN_CHANGED); + } + } else { + change = select_only(find_item(my), when() & FL_WHEN_CHANGED); + } + return 1; + case FL_DRAG: + // do the scrolling first: + my = Fl::event_y(); + if (my < Y && my < py) { + int p = real_position_+my-Y; + if (p<0) p = 0; + position(p); + } else if (my > Y+H && my > py) { + int p = real_position_+my-(Y+H); + int h = full_height()-H; if (p > h) p = h; + if (p<0) p = 0; + position(p); + } + if (type() == FL_NORMAL_BROWSER || !top_) + ; + else if (type() == FL_MULTI_BROWSER) { + void* l = find_item(my); + void* t; void* b; // this will be the range to change + if (my > py) { // go down + t = selection_ ? item_next(selection_) : 0; + b = l ? item_next(l) : 0; + } else { // go up + t = l; + b = selection_; + } + for (; t && t != b; t = item_next(t)) + change |= select(t, whichway, when() & FL_WHEN_CHANGED); + if (l) selection_ = l; + } else { + void* l1 = selection_; + void* l = + (Fl::event_x()<x() || Fl::event_x()>x()+w()) ? selection_ : + find_item(my); + select_only(l, when() & FL_WHEN_CHANGED); + change = (l != l1); + } + py = my; + return 1; + case FL_RELEASE: + if (type() == FL_SELECT_BROWSER) { + void* t = selection_; deselect(); selection_ = t; + } + if (change) { + if (when() & FL_WHEN_RELEASE) do_callback(); + else if (!(when()&FL_WHEN_CHANGED)) set_changed(); + } else { + if (when() & FL_WHEN_NOT_CHANGED) do_callback(); + } + return 1; + } + + return 0; +} + +Fl_Browser_::Fl_Browser_(int x, int y, int w, int h, const char* l) + : Fl_Group(x, y, w, h, l), + scrollbar(0, 0, 0, 0, 0), // they will be resized by draw() + hscrollbar(0, 0, 0, 0, 0) +{ + box(FL_NO_BOX); + align(FL_ALIGN_BOTTOM); + position_ = real_position_ = 0; + hposition_ = real_hposition_ = 0; + offset_ = 0; + top_ = 0; + when(FL_WHEN_RELEASE_ALWAYS); + selection_ = 0; + color(FL_WHITE); + selection_color(FL_SELECTION_COLOR); + scrollbar.callback(scrollbar_callback); +//scrollbar.align(FL_ALIGN_LEFT|FL_ALIGN_BOTTOM); // back compatability? + hscrollbar.callback(hscrollbar_callback); + hscrollbar.type(FL_HORIZONTAL); + textfont_ = FL_HELVETICA; + textsize_ = FL_NORMAL_SIZE; + textcolor_ = FL_BLACK; + has_scrollbar_ = BOTH; + max_width = 0; + max_width_item = 0; + end(); +} + +// Default versions of some of the virtual functions: + +int Fl_Browser_::item_quick_height(void* l) const { + return item_height(l); +} + +int Fl_Browser_::incr_height() const { + return item_quick_height(item_first()); +} + +int Fl_Browser_::full_height() const { + int t = 0; + for (void* p = item_first(); p; p = item_next(p)) + t += item_quick_height(p); + return t; +} + +int Fl_Browser_::full_width() const { + return max_width; +} + +void Fl_Browser_::item_select(void*, int) {} + +int Fl_Browser_::item_selected(void* l) const {return l==selection_;} + +// end of Fl_Browser_.C diff --git a/src/Fl_Browser_load.cxx b/src/Fl_Browser_load.cxx new file mode 100644 index 000000000..8e1da435a --- /dev/null +++ b/src/Fl_Browser_load.cxx @@ -0,0 +1,29 @@ +// Fl_Browser_load.C +// this should be moved to another source file, since it links stdio? + +#include <FL/Fl.H> +#include <FL/Fl_Browser.H> +#include <stdio.h> + +int Fl_Browser::load(const char *filename) { +#define MAXFL_BLINE 1024 + char newtext[MAXFL_BLINE]; + int c; + int i; + clear(); + if (!filename || !(filename[0])) return 1; + FILE *fl = fopen(filename,"r"); + if (!fl) return 0; + i = 0; + do { + c = getc(fl); + if (c == '\n' || c <= 0 || i>=MAXFL_BLINE-1) { + newtext[i] = 0; + add(newtext); + i = 0; + } else + newtext[i++] = c; + } while (c >= 0); + fclose(fl); + return 1; +} diff --git a/src/Fl_Button.cxx b/src/Fl_Button.cxx new file mode 100644 index 000000000..86b563e78 --- /dev/null +++ b/src/Fl_Button.cxx @@ -0,0 +1,92 @@ +// Fl_Button.C + +// There are a lot of subclasses, named Fl_*_Button. Some of +// them are implemented by setting the type() value and testing it +// here. This includes Fl_Radio_Button and Fl_Toggle_Button + +#include <FL/Fl.H> +#include <FL/Fl_Button.H> +#include <FL/Fl_Group.H> + +int Fl_Button::value(int v) { + v = v ? 1 : 0; + oldval = v; + clear_changed(); + if (value_ != v) {value_ = v; redraw(); return 1;} + else return 0; +} + +void Fl_Button::setonly() { // set this radio button on, turn others off + value(1); + Fl_Group* g = (Fl_Group*)parent(); + Fl_Widget*const* a = g->array(); + for (int i = g->children(); i--;) { + Fl_Widget* o = *a++; + if (o != this && o->type()==FL_RADIO_BUTTON) ((Fl_Button*)o)->value(0); + } +} + +void Fl_Button::draw() { + if (type() == FL_HIDDEN_BUTTON) return; + Fl_Color col = value() ? selection_color() : color(); +//if (col == FL_GRAY && Fl::belowmouse()==this) col = FL_LIGHT1; + draw_box(value() ? (down_box()?down_box():down(box())) : box(), col); + draw_label(); +} +int Fl_Button::handle(int event) { + int newval; + switch (event) { + case FL_ENTER: + case FL_LEAVE: +// if ((value_?selection_color():color())==FL_GRAY) redraw(); + return 1; + case FL_PUSH: + case FL_DRAG: + if (Fl::event_inside(this)) { + if (type() == FL_RADIO_BUTTON) newval = 1; + else newval = !oldval; + } else + newval = oldval; + if (newval != value_) { + value_ = newval; + redraw(); + if (when() & FL_WHEN_CHANGED) do_callback(); + } + return 1; + case FL_RELEASE: + if (value_ == oldval) return 1; + if (type() == FL_RADIO_BUTTON) + setonly(); + else if (type() == FL_TOGGLE_BUTTON) + oldval = value_; + else { + value(oldval); + if (when() & FL_WHEN_CHANGED) do_callback(); + } + if (when() & FL_WHEN_RELEASE) do_callback(); else set_changed(); + return 1; + case FL_SHORTCUT: + if (!(shortcut() ? + Fl::test_shortcut(shortcut()) : test_shortcut())) return 0; + if (type() == FL_RADIO_BUTTON && !value_) { + setonly(); + if (when() & FL_WHEN_CHANGED) do_callback(); + } else if (type() == FL_TOGGLE_BUTTON) { + value(!value()); + if (when() & FL_WHEN_CHANGED) do_callback(); + } + if (when() & FL_WHEN_RELEASE) do_callback(); else set_changed(); + return 1; + default: + return 0; + } +} + +Fl_Button::Fl_Button(int x,int y,int w,int h, const char *l) +: Fl_Widget(x,y,w,h,l) { + box(FL_UP_BOX); + down_box(FL_NO_BOX); + value_ = oldval = 0; + shortcut_ = 0; + set_flag(SHORTCUT_LABEL); +} diff --git a/src/Fl_Chart.cxx b/src/Fl_Chart.cxx new file mode 100644 index 000000000..54c6913f9 --- /dev/null +++ b/src/Fl_Chart.cxx @@ -0,0 +1,344 @@ +// Fl_Chart.C + +// Emulation of the Forms Chart widget. +// I did not try to improve this much, as I doubt it is used. + +// display code Written by: Mark Overmars + +#include <FL/math.h> +#include <FL/Fl.H> +#include <FL/Fl_Chart.H> +#include <FL/fl_draw.H> +#include <string.h> + +#define ARCINC (2.0*M_PI/360.0) + +// this function is in fl_boxtype.C: +void fl_rectbound(int x,int y,int w,int h, Fl_Color color); + +/* Widget specific information */ + +static void draw_barchart(int x,int y,int w,int h, + int numb, FL_CHART_ENTRY entries[], + double min, double max, int autosize, int maxnumb, + Fl_Color textcolor) +/* Draws a bar chart. x,y,w,h is the bounding box, entries the array of + numb entries and min and max the boundaries. */ +{ + double incr = h/(max-min); + int zeroh; + double lh = fl_height(); + if ( -min*incr < lh) { + incr = (h - lh + min*incr)/(max-min); + zeroh = int(y+h-lh); + } else { + zeroh = int(y+h+min * incr + .5); + } + int bwidth = int(w/double(autosize?numb:maxnumb)+.5); + /* Draw base line */ + fl_color(textcolor); + fl_line(x, zeroh, x+w, zeroh); + if (min == 0.0 && max == 0.0) return; /* Nothing else to draw */ + int i; + /* Draw the bars */ + for (i=0; i<numb; i++) { + int h = int(entries[i].val*incr+.5); + if (h < 0) + fl_rectbound(x+i*bwidth,zeroh,bwidth+1,-h+1, (Fl_Color)entries[i].col); + else if (h > 0) + fl_rectbound(x+i*bwidth,zeroh-h,bwidth+1,h+1,(Fl_Color)entries[i].col); + } + /* Draw the labels */ + fl_color(textcolor); + for (i=0; i<numb; i++) + fl_draw(entries[i].str, + x+i*bwidth+bwidth/2,zeroh,0,0, + FL_ALIGN_TOP); +} + +static void draw_horbarchart(int x,int y,int w,int h, + int numb, FL_CHART_ENTRY entries[], + double min, double max, int autosize, int maxnumb, + Fl_Color textcolor) +/* Draws a horizontal bar chart. x,y,w,h is the bounding box, entries the + array of numb entries and min and max the boundaries. */ +{ + int i; + double lw = 0.0; /* Maximal label width */ + /* Compute maximal label width */ + for (i=0; i<numb; i++) { + double w1 = fl_width(entries[i].str); + if (w1 > lw) lw = w1; + } + if (lw > 0.0) lw += 4.0; + double incr = w/(max-min); + int zeroh; + if ( -min*incr < lw) { + incr = (w - lw + min*incr)/(max-min); + zeroh = x+int(lw+.5); + } else { + zeroh = int(x-min * incr + .5); + } + int bwidth = int(h/double(autosize?numb:maxnumb)+.5); + /* Draw base line */ + fl_color(textcolor); + fl_line(zeroh, y, zeroh, y+h); + if (min == 0.0 && max == 0.0) return; /* Nothing else to draw */ + /* Draw the bars */ + for (i=0; i<numb; i++) { + int w = int(entries[i].val*incr+.5); + if (w > 0) + fl_rectbound(zeroh,y+i*bwidth,w+1,bwidth+1, (Fl_Color)entries[i].col); + else if (w < 0) + fl_rectbound(zeroh+w,y+i*bwidth,-w+1,bwidth+1,(Fl_Color)entries[i].col); + } + /* Draw the labels */ + for (i=0; i<numb; i++) + fl_draw(entries[i].str, + zeroh-2,y+i*bwidth+bwidth/2,0,0, + FL_ALIGN_RIGHT); +} + +static void draw_linechart(int type, int x,int y,int w,int h, + int numb, FL_CHART_ENTRY entries[], + double min, double max, int autosize, int maxnumb, + Fl_Color textcolor) +/* Draws a line chart. x,y,w,h is the bounding box, entries the array of + numb entries and min and max the boundaries. */ +{ + int i; + double lh = fl_height(); + double incr = (h-2.0*lh)/ (max-min); + int zeroh = int(y+h-lh+min * incr + .5); + double bwidth = w/double(autosize?numb:maxnumb); + /* Draw the values */ + for (i=0; i<numb; i++) { + int x0 = x + int((i-.5)*bwidth+.5); + int x1 = x + int((i+.5)*bwidth+.5); + int y0 = i ? zeroh - int(entries[i-1].val*incr+.5) : 0; + int y1 = zeroh - int(entries[i].val*incr+.5); + if (type == FL_SPIKE_CHART) { + fl_color((Fl_Color)entries[i].col); + fl_line(x1, zeroh, x1, y1); + } else if (type == FL_LINE_CHART && i != 0) { + fl_color((Fl_Color)entries[i-1].col); + fl_line(x0,y0,x1,y1); + } else if (type == FL_FILLED_CHART && i != 0) { + fl_color((Fl_Color)entries[i-1].col); + if ((entries[i-1].val>0.0)!=(entries[i].val>0.0)) { + double ttt = entries[i-1].val/(entries[i-1].val-entries[i].val); + int xt = x + int((i-.5+ttt)*bwidth+.5); + fl_polygon(x0,zeroh, x0,y0, xt,zeroh); + fl_polygon(xt,zeroh, x1,y1, x1,zeroh); + } else { + fl_polygon(x0,zeroh, x0,y0, x1,y1, x1,zeroh); + } + fl_color(textcolor); + fl_line(x0,y0,x1,y1); + } + } + /* Draw base line */ + fl_color(textcolor); + fl_line(x,zeroh,x+w,zeroh); + /* Draw the labels */ + for (i=0; i<numb; i++) + fl_draw(entries[i].str, + x+int((i+.5)*bwidth+.5), zeroh - int(entries[i].val*incr+.5),0,0, + entries[i].val>=0 ? FL_ALIGN_BOTTOM : FL_ALIGN_TOP); +} + +static void draw_piechart(int x,int y,int w,int h, + int numb, FL_CHART_ENTRY entries[], int special, + Fl_Color textcolor) +/* Draws a pie chart. x,y,w,h is the bounding box, entries the array of + numb entries */ +{ + int i; + double xc,yc,rad; /* center and radius */ + double tot; /* sum of values */ + double incr; /* increment in angle */ + double curang; /* current angle we are drawing */ + double txc,tyc; /* temporary center */ + double lh = fl_height(); + /* compute center and radius */ + xc = x+w/2.0; yc = y+h/2.0; + rad = h/2.0 - lh; + if (special) { yc += 0.1*rad; rad = 0.9*rad;} + /* compute sum of values */ + tot = 0.0; + for (i=0; i<numb; i++) + if (entries[i].val > 0.0) tot += entries[i].val; + if (tot == 0.0) return; + incr = 360.0/tot; + /* Draw the pie */ + curang = 0.0; + for (i=0; i<numb; i++) + if (entries[i].val > 0.0) + { + txc = xc; tyc = yc; + /* Correct for special pies */ + if (special && i==0) + { + txc += 0.3*rad*cos(ARCINC*(curang+0.5*incr*entries[i].val)); + tyc -= 0.3*rad*sin(ARCINC*(curang+0.5*incr*entries[i].val)); + } + fl_color((Fl_Color)entries[i].col); + fl_begin_polygon(); fl_vertex(txc,tyc); + fl_arc(txc,tyc,rad,curang, curang+incr*entries[i].val); + fl_end_polygon(); + fl_color(textcolor); + fl_begin_loop(); fl_vertex(txc,tyc); + fl_arc(txc,tyc,rad,curang, curang+incr*entries[i].val); + fl_end_loop(); + curang += 0.5 * incr * entries[i].val; + /* draw the label */ + double xl = txc + 1.1*rad*cos(ARCINC*curang); + fl_draw(entries[i].str, + int(xl+.5), + int(tyc - 1.1*rad*sin(ARCINC*curang)+.5), + 0, 0, + xl<txc ? FL_ALIGN_RIGHT : FL_ALIGN_LEFT); + curang += 0.5 * incr * entries[i].val; + } +} + +void Fl_Chart::draw() { + int xx,yy,ww,hh; + int i; + + xx = x()+9; + yy = y()+9; + ww = w()-2*9; + hh = h()-2*9; + + if (min >= max) { + min = max = 0.0; + for (i=0; i<numb; i++) { + if (entries[i].val < min) min = entries[i].val; + if (entries[i].val > max) max = entries[i].val; + } + } + + draw_box(); + fl_font(textfont(),textsize()); + + switch (type()) { + case FL_BAR_CHART: + draw_barchart(xx,yy,ww,hh, numb, entries, min, max, + autosize(), maxnumb, textcolor()); + break; + case FL_HORBAR_CHART: + draw_horbarchart(xx,yy,ww,hh, numb, entries, min, max, + autosize(), maxnumb, textcolor()); + break; + case FL_PIE_CHART: + draw_piechart(xx,yy,ww,hh,numb,entries,0, textcolor()); + break; + case FL_SPECIALPIE_CHART: + draw_piechart(xx,yy,ww,hh,numb,entries,1,textcolor()); + break; + default: + draw_linechart(type(),xx,yy,ww,hh, numb, entries, min, max, + autosize(), maxnumb, textcolor()); + break; + } + draw_label(); +} + +/*------------------------------*/ + +#define FL_CHART_BOXTYPE FL_BORDER_BOX +#define FL_CHART_COL1 FL_COL1 +#define FL_CHART_LCOL FL_LCOL +#define FL_CHART_ALIGN FL_ALIGN_BOTTOM + +Fl_Chart::Fl_Chart(int x,int y,int w,int h,const char *l) : +Fl_Widget(x,y,w,h,l) { + box(FL_BORDER_BOX); + align(FL_ALIGN_BOTTOM); + numb = 0; + maxnumb = FL_CHART_MAX; + autosize_ = 1; + min = max = 0; + textfont_ = FL_HELVETICA; + textsize_ = 10; + textcolor_ = FL_BLACK; +} + +void Fl_Chart::clear() { + numb = 0; + redraw(); +} + +void Fl_Chart::add(double val, const char *str, uchar col) { + int i; + /* Shift entries if required */ + if (numb >= maxnumb) { + for (i=0; i<numb-1; i++) entries[i] = entries[i+1]; + numb--; + } + entries[numb].val = float(val); + entries[numb].col = col; + if (str) { + strncpy(entries[numb].str,str,FL_CHART_LABEL_MAX+1); + entries[numb].str[FL_CHART_LABEL_MAX] = 0; + } else { + entries[numb].str[0] = 0; + } + numb++; + redraw(); +} + +void Fl_Chart::insert(int index, double val, const char *str, uchar col) { + int i; + if (index < 1 || index > numb+1) return; + /* Shift entries */ + for (i=numb; i >= index; i--) entries[i] = entries[i-1]; + if (numb < maxnumb) numb++; + /* Fill in the new entry */ + entries[index-1].val = float(val); + entries[index-1].col = col; + if (str) { + strncpy(entries[index-1].str,str,FL_CHART_LABEL_MAX+1); + entries[index-1].str[FL_CHART_LABEL_MAX] = 0; + } else { + entries[index-1].str[0] = 0; + } + redraw(); +} + +void Fl_Chart::replace(int index,double val, const char *str, uchar col) { + if (index < 1 || index > numb) return; + entries[index-1].val = float(val); + entries[index-1].col = col; + if (str) { + strncpy(entries[index-1].str,str,FL_CHART_LABEL_MAX+1); + entries[index-1].str[FL_CHART_LABEL_MAX] = 0; + } else { + entries[index-1].str[0] = 0; + } + redraw(); +} + +void Fl_Chart::bounds(double min, double max) { + this->min = min; + this->max = max; + redraw(); +} + +void Fl_Chart::maxsize(int m) { + int i; + /* Fill in the new number */ + if (m < 0) return; + if (m > FL_CHART_MAX) + maxnumb = FL_CHART_MAX; + else + maxnumb = m; + /* Shift entries if required */ + if (numb > maxnumb) { + for (i = 0; i<maxnumb; i++) + entries[i] = entries[i+numb-maxnumb]; + numb = maxnumb; + redraw(); + } +} diff --git a/src/Fl_Check_Button.cxx b/src/Fl_Check_Button.cxx new file mode 100644 index 000000000..6c123861b --- /dev/null +++ b/src/Fl_Check_Button.cxx @@ -0,0 +1,15 @@ +// Fl_Check_Button.C + +// A subclass of Fl_Button that always draws as a diamond box. This +// diamond is smaller than the widget size and can be surchecked by +// another box type, for compatability with Forms. + +#include <FL/Fl.H> +#include <FL/Fl_Check_Button.H> + +Fl_Check_Button::Fl_Check_Button(int x, int y, int w, int h, const char *l) +: Fl_Light_Button(x, y, w, h, l) { + box(FL_NO_BOX); + down_box(FL_DIAMOND_DOWN_BOX); + selection_color(FL_RED); +} diff --git a/src/Fl_Choice.cxx b/src/Fl_Choice.cxx new file mode 100644 index 000000000..de41d4ffc --- /dev/null +++ b/src/Fl_Choice.cxx @@ -0,0 +1,63 @@ +// Fl_Choice.C + +// Emulates the Forms choice widget. This is almost exactly the same +// as an Fl_Menu_Button. The only difference is the appearance of the +// button: it draws the text of the current pick and a down-arrow. + +#include <FL/Fl.H> +#include <FL/Fl_Choice.H> +#include <FL/fl_draw.H> + +extern char fl_draw_shortcut; + +void Fl_Choice::draw() { + draw_box(); + if (box() == FL_FLAT_BOX) return; // for XForms compatability + int H = labelsize()/2+1; + draw_box(FL_THIN_UP_BOX,x()+w()-3*H,y()+(h()-H)/2,2*H,H,color()); + fl_font(textfont(),textsize(),default_font(),default_size()); + fl_color(active_r() ? textcolor() : inactive(textcolor())); + fl_draw_shortcut = 2; // hack value to make '&' disappear + fl_draw(text(),x()+6,y(),w()-6,h(),FL_ALIGN_LEFT); + fl_draw_shortcut = 0; + draw_label(); +} + +Fl_Choice::Fl_Choice(int x,int y,int w,int h, const char *l) +: Fl_Menu_(x,y,w,h,l) { + align(FL_ALIGN_LEFT); + when(FL_WHEN_RELEASE); + textfont(FL_HELVETICA); + down_box(FL_NO_BOX); +} + +int Fl_Choice::value(int v) { + if (!Fl_Menu_::value(v)) return 0; + redraw(); + return 1; +} + +int Fl_Choice::handle(int e) { + if (!menu() || !menu()->text) return 0; + const Fl_Menu_Item* v; + switch (e) { + case FL_PUSH: + J1: + v = menu()->pulldown(x(), y(), w(), h(), mvalue(), this); + if (!v || v->submenu()) return 1; + if (v != mvalue()) redraw(); + picked(v); + return 1; + case FL_SHORTCUT: + if (Fl_Widget::test_shortcut()) goto J1; + v = menu()->test_shortcut(); + if (!v) return 0; + if (v != mvalue()) redraw(); + picked(v); + return 1; + default: + return 0; + } +} + +// end of Fl_Choice.C diff --git a/src/Fl_Clock.cxx b/src/Fl_Clock.cxx new file mode 100644 index 000000000..852dcb476 --- /dev/null +++ b/src/Fl_Clock.cxx @@ -0,0 +1,136 @@ +// Fl_Clock.C + +// There really should be a way to make this display something other +// than the current time... + +// Original clock display written by Paul Haeberli at SGI. +// Modifications by Mark Overmars for Forms +// Further changes by Bill Spitzak for fltk + +#include <FL/Fl.H> +#include <FL/Fl_Clock.H> +#include <FL/fl_draw.H> +#include <math.h> +#include <time.h> + +const float hourhand[4][2] = {{-0.5f, 0}, {0, 1.5f}, {0.5f, 0}, {0, -7.0f}}; +const float minhand[4][2] = {{-0.5f, 0}, {0, 1.5f}, {0.5f, 0}, {0, -11.5f}}; +const float sechand[4][2] = {{-0.1f, 0}, {0, 2.0f}, {0.1f, 0}, {0, -11.5f}}; + +static void drawhand(double ang,const float v[][2],Fl_Color fill,Fl_Color line) +{ + fl_push_matrix(); + fl_rotate(ang); + fl_color(fill); fl_begin_polygon(); + int i; for (i=0; i<4; i++) fl_vertex(v[i][0],v[i][1]); fl_end_polygon(); + fl_color(line); fl_begin_loop(); + for (i=0; i<4; i++) fl_vertex(v[i][0],v[i][1]); fl_end_loop(); + fl_pop_matrix(); +} + +void Fl_Clock::drawhands(Fl_Color fill, Fl_Color line) { + drawhand(-360*(hour()+minute()/60.0)/12, hourhand, fill, line); + drawhand(-360*(minute()+second()/60.0)/60, minhand, fill, line); + drawhand(-360*(second()/60.0), sechand, fill, line); +} + +static void rect(double x, double y, double w, double h) { + double r = x+w; + double t = y+h; + fl_begin_polygon(); + fl_vertex(x, y); + fl_vertex(r, y); + fl_vertex(r, t); + fl_vertex(x, t); + fl_end_polygon(); +} + +void Fl_Clock::draw(int x, int y, int w, int h) { + draw_box(box(), x, y, w, h, type()==FL_ROUND_CLOCK ? FL_GRAY : color()); + fl_push_matrix(); + fl_translate(x+w/2.0-.5, y+h/2.0-.5); + fl_scale((w-1)/28.0, (h-1)/28.0); + if (type() == FL_ROUND_CLOCK) { + fl_color(color()); + fl_begin_polygon(); fl_circle(0,0,14); fl_end_polygon(); + fl_color(FL_BLACK); + fl_begin_loop(); fl_circle(0,0,14); fl_end_loop(); + } + // draw the shadows: + fl_push_matrix(); + fl_translate(0.60, 0.60); + drawhands(FL_DARK3, FL_DARK3); + fl_pop_matrix(); + // draw the tick marks: + fl_push_matrix(); + fl_color(FL_BLACK); // color was 52 + for (int i=0; i<12; i++) { + if (i==6) rect(-0.5, 9, 1, 2); + else if (i==3 || i==0 || i== 9) rect(-0.5, 9.5, 1, 1); + else rect(-0.25, 9.5, .5, 1); + fl_rotate(-30); + } + fl_pop_matrix(); + // draw the hands: + drawhands(selection_color(), FL_GRAY0); // color was 54 + fl_pop_matrix(); +} + +void Fl_Clock::draw() { + draw(x(), y(), w(), h()); + draw_label(); +} + +void Fl_Clock::value(int h, int m, int s) { + if (h!=hour_ || m!=minute_ || s!=second_) { + hour_ = h; minute_ = m; second_ = s; + redraw(); + } +} + +void Fl_Clock::value(ulong v) { + struct tm *timeofday; + timeofday = localtime((const time_t *)&v); + value(timeofday->tm_hour, timeofday->tm_min, timeofday->tm_sec); +} + +static void tick(void *v) { + ((Fl_Clock*)v)->value(time(0)); + Fl::add_timeout(1, tick, v); +} + +void Fl_Clock::_Fl_Clock() { + selection_color(fl_gray_ramp(5)); + align(FL_ALIGN_BOTTOM); + value(time(0)); + //Fl::add_timeout(1, tick, this); +} + +Fl_Clock::Fl_Clock(int x, int y, int w, int h, const char *l) +: Fl_Widget(x, y, w, h, l) { + box(FL_UP_BOX); + _Fl_Clock(); +} + +Fl_Clock::Fl_Clock(uchar t, int x, int y, int w, int h, const char *l) +: Fl_Widget(x, y, w, h, l) { + type(t); + box(t==FL_ROUND_CLOCK ? FL_NO_BOX : FL_UP_BOX); + _Fl_Clock(); +} + +Fl_Clock::~Fl_Clock() { + Fl::remove_timeout(tick, this); +} + +int Fl_Clock::handle(int event) { + switch (event) { + case FL_HIDE: + Fl::remove_timeout(tick, this); + break; + case FL_SHOW: + Fl::remove_timeout(tick, this); + tick(this); + } + return 0; +} diff --git a/src/Fl_Color_Chooser.cxx b/src/Fl_Color_Chooser.cxx new file mode 100644 index 000000000..e0e2d926a --- /dev/null +++ b/src/Fl_Color_Chooser.cxx @@ -0,0 +1,397 @@ +// Fl_Color_Chooser.C + +// Besides being a useful object on it's own, the Fl_Color_Chooser was +// an attempt to make a complex composite object that could be easily +// imbedded into a user interface. If you wish to make complex objects +// of your own, be sure to read this code. + +// The function fl_color_chooser() creates a window containing a color +// chooser and a few buttons and current-color indicators. It is an +// easier interface for simple programs that just need a color. + +#include <FL/Fl.H> +#include <FL/Fl_Color_Chooser.H> +#include <FL/fl_draw.H> +#include <FL/math.h> +#include <stdio.h> + +// The "hue box" can be a circle or rectilinear. +// You get a circle by defining this: +// #define CIRCLE 1 +// And the "hue box" can auto-update when the value changes +// you get this by defining this: +#define UPDATE_HUE_BOX 1 + +void Fl_Color_Chooser::hsv2rgb( + double H, double S, double V, double& r, double& g, double& b) { + if (S < 5.0e-6) { + r = g = b = V; + } else { + int i = (int)H; + double f = H - (float)i; + double p1 = V*(1.0-S); + double p2 = V*(1.0-S*f); + double p3 = V*(1.0-S*(1.0-f)); + switch (i) { + case 0: r = V; g = p3; b = p1; break; + case 1: r = p2; g = V; b = p1; break; + case 2: r = p1; g = V; b = p3; break; + case 3: r = p1; g = p2; b = V; break; + case 4: r = p3; g = p1; b = V; break; + case 5: r = V; g = p1; b = p2; break; + } + } +} + +void Fl_Color_Chooser::rgb2hsv( + double r, double g, double b, double& H, double& S, double& V) { + double maxv = r > g ? r : g; if (b > maxv) maxv = b; + V = maxv; + if (maxv>0) { + double minv = r < g ? r : g; if (b < minv) minv = b; + S = 1.0 - double(minv)/maxv; + if (maxv > minv) { + if (maxv == r) {H = (g-b)/double(maxv-minv); if (H<0) H += 6.0;} + else if (maxv == g) H = 2.0+(b-r)/double(maxv-minv); + else H = 4.0+(r-g)/double(maxv-minv); + } + } +} + +enum {M_RGB, M_BYTE, M_HEX, M_HSV}; // modes +static Fl_Menu_Item mode_menu[] = { + {"rgb"}, + {"byte"}, + {"hex"}, + {"hsv"}, + {0} +}; + +int Flcc_Value_Input::format(char* buf) { + Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent(); + if (c->mode() == M_HEX) return sprintf(buf,"0x%02X", int(value())); + else return Fl_Valuator::format(buf); +} + +void Fl_Color_Chooser::set_valuators() { + switch (mode()) { + case M_RGB: + rvalue.range(0,1); rvalue.step(1,1000); rvalue.value(r_); + gvalue.range(0,1); gvalue.step(1,1000); gvalue.value(g_); + bvalue.range(0,1); bvalue.step(1,1000); bvalue.value(b_); + break; + case M_BYTE: + case M_HEX: + rvalue.range(0,255); rvalue.step(1); rvalue.value(int(255*r_+.5)); + gvalue.range(0,255); gvalue.step(1); gvalue.value(int(255*g_+.5)); + bvalue.range(0,255); bvalue.step(1); bvalue.value(int(255*b_+.5)); + break; + case M_HSV: + rvalue.range(0,6); rvalue.step(1,1000); rvalue.value(hue_); + gvalue.range(0,1); gvalue.step(1,1000); gvalue.value(saturation_); + bvalue.range(0,1); bvalue.step(1,1000); bvalue.value(value_); + break; + } +} + +int Fl_Color_Chooser::rgb(double r, double g, double b) { + if (r == r_ && g == g_ && b == b_) return 0; + r_ = r; g_ = g; b_ = b; + double ph = hue_; + double ps = saturation_; + double pv = value_; + rgb2hsv(r,g,b,hue_,saturation_,value_); + set_valuators(); + if (value_ != pv) { +#ifdef UPDATE_HUE_BOX + huebox.damage(6); +#endif + valuebox.damage(2);} + if (hue_ != ph || saturation_ != ps) {huebox.damage(2); valuebox.damage(6);} + return 1; +} + +int Fl_Color_Chooser::hsv(double h, double s, double v) { + h = fmod(h,6.0); if (h < 0.0) h += 6.0; + if (s < 0.0) s = 0.0; else if (s > 1.0) s = 1.0; + if (v < 0.0) v = 0.0; else if (v > 1.0) v = 1.0; + if (h == hue_ && s == saturation_ && v == value_) return 0; + double ph = hue_; + double ps = saturation_; + double pv = value_; + hue_ = h; saturation_ = s; value_ = v; + if (value_ != pv) { +#ifdef UPDATE_HUE_BOX + huebox.damage(6); +#endif + valuebox.damage(2);} + if (hue_ != ph || saturation_ != ps) {huebox.damage(2); valuebox.damage(6);} + hsv2rgb(h,s,v,r_,g_,b_); + set_valuators(); + return 1; +} + +//////////////////////////////////////////////////////////////// + +static void tohs(double x, double y, double& h, double& s) { +#ifdef CIRCLE + x = 2*x-1; + y = 1-2*y; + s = sqrt(x*x+y*y); if (s > 1.0) s = 1.0; + h = (3.0/M_PI)*atan2(y,x); + if (h<0) h += 6.0; +#else + h = fmod(6.0*x,6.0); if (h < 0.0) h += 6.0; + s = 1.0-y; if (s < 0.0) s = 0.0; else if (s > 1.0) s = 1.0; +#endif +} + +int Flcc_HueBox::handle(int e) { + static double ih, is; + Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent(); + switch (e) { + case FL_PUSH: + ih = c->hue(); + is = c->saturation(); + case FL_DRAG: { + double Xf, Yf, H, S; + Xf = (Fl::event_x()-x()-Fl::box_dx(box()))/double(w()-Fl::box_dw(box())); + Yf = (Fl::event_y()-y()-Fl::box_dy(box()))/double(h()-Fl::box_dh(box())); + tohs(Xf, Yf, H, S); + if (fabs(H-ih) < 3*6.0/w()) H = ih; + if (fabs(S-is) < 3*1.0/h()) S = is; + if (Fl::event_state(FL_CTRL)) H = ih; + if (c->hsv(H, S, c->value())) c->do_callback(); + } return 1; + default: + return 0; + } +} + +static void generate_image(void* vv, int X, int Y, int W, uchar* buf) { + Flcc_HueBox* v = (Flcc_HueBox*)vv; + int iw = v->w()-Fl::box_dw(v->box()); + double Yf = double(Y)/(v->h()-Fl::box_dh(v->box())); +#ifdef UPDATE_HUE_BOX + const double V = ((Fl_Color_Chooser*)(v->parent()))->value(); +#else + const double V = 1.0; +#endif + for (int x = X; x < X+W; x++) { + double Xf = double(x)/iw; + double H,S; tohs(Xf,Yf,H,S); + double r,g,b; + Fl_Color_Chooser::hsv2rgb(H,S,V,r,g,b); + *buf++ = uchar(255*r+.5); + *buf++ = uchar(255*g+.5); + *buf++ = uchar(255*b+.5); + } +} + +void Flcc_HueBox::draw() { + if (damage()&128) draw_box(); + int x1 = x()+Fl::box_dx(box()); + int y1 = y()+Fl::box_dy(box()); + int w1 = w()-Fl::box_dw(box()); + int h1 = h()-Fl::box_dh(box()); + if (damage() == 2) fl_clip(x1+px,y1+py,6,6); + fl_draw_image(generate_image, this, x1, y1, w1, h1); + if (damage() == 2) fl_pop_clip(); + Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent(); +#ifdef CIRCLE + int X = int(.5*(cos(c->hue()*(M_PI/3.0))*c->saturation()+1) * (w1-6)); + int Y = int(.5*(1-sin(c->hue()*(M_PI/3.0))*c->saturation()) * (h1-6)); +#else + int X = int(c->hue()/6.0*(w1-6)); + int Y = int((1-c->saturation())*(h1-6)); +#endif + if (X < 0) X = 0; else if (X > w1-6) X = w1-6; + if (Y < 0) Y = 0; else if (Y > h1-6) Y = h1-6; + // fl_color(c->value()>.75 ? FL_BLACK : FL_WHITE); + draw_box(FL_UP_BOX,x1+X,y1+Y,6,6,FL_GRAY); + px = X; py = Y; +} + +//////////////////////////////////////////////////////////////// + +int Flcc_ValueBox::handle(int e) { + static double iv; + Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent(); + switch (e) { + case FL_PUSH: + iv = c->value(); + case FL_DRAG: { + double Yf; + Yf = 1-(Fl::event_y()-y()-Fl::box_dy(box()))/double(h()-Fl::box_dh(box())); + if (fabs(Yf-iv)<3*1.0/h()) Yf = iv; + if (c->hsv(c->hue(),c->saturation(),Yf)) c->do_callback(); + } return 1; + default: + return 0; + } +} + +static double tr, tg, tb; +static void generate_vimage(void* vv, int X, int Y, int W, uchar* buf) { + Flcc_ValueBox* v = (Flcc_ValueBox*)vv; + double Yf = 255*(1.0-double(Y)/(v->h()-Fl::box_dh(v->box()))); + uchar r = uchar(tr*Yf+.5); + uchar g = uchar(tg*Yf+.5); + uchar b = uchar(tb*Yf+.5); + for (int x = X; x < X+W; x++) { + *buf++ = r; *buf++ = g; *buf++ = b; + } +} + +void Flcc_ValueBox::draw() { + if (damage()&128) draw_box(); + Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent(); + c->hsv2rgb(c->hue(),c->saturation(),1.0,tr,tg,tb); + int x1 = x()+Fl::box_dx(box()); + int y1 = y()+Fl::box_dy(box()); + int w1 = w()-Fl::box_dw(box()); + int h1 = h()-Fl::box_dh(box()); + if (damage() == 2) fl_clip(x1,y1+py,w1,6); + fl_draw_image(generate_vimage, this, x1, y1, w1, h1); + if (damage() == 2) fl_pop_clip(); + int Y = int((1-c->value()) * (h1-6)); + if (Y < 0) Y = 0; else if (Y > h1-6) Y = h1-6; + draw_box(FL_UP_BOX,x1,y1+Y,w1,6,FL_GRAY); + py = Y; +} + +//////////////////////////////////////////////////////////////// + +void Fl_Color_Chooser::rgb_cb(Fl_Widget* o, void*) { + Fl_Color_Chooser* c = (Fl_Color_Chooser*)(o->parent()); + double r = c->rvalue.value(); + double g = c->gvalue.value(); + double b = c->bvalue.value(); + if (c->mode() == M_HSV) { + if (c->hsv(r,g,b)) c->do_callback(); + return; + } + if (c->mode() != M_RGB) { + r = r/255; + g = g/255; + b = b/255; + } + if (c->rgb(r,g,b)) c->do_callback(); +} + +void Fl_Color_Chooser::mode_cb(Fl_Widget* o, void*) { + Fl_Color_Chooser* c = (Fl_Color_Chooser*)(o->parent()); + c->set_valuators(); +} + +//////////////////////////////////////////////////////////////// + +Fl_Color_Chooser::Fl_Color_Chooser(int X, int Y, int W, int H, const char* L) + : Fl_Group(0,0,180,100,L), + huebox(0,0,100,100), + valuebox(100,0,20,100), + choice(120,0,60,20), + rvalue(120,20,60,25), + gvalue(120,45,60,25), + bvalue(120,70,60,25), + resize_box(0,95,100,5) +{ + end(); + resizable(resize_box); + resize(X,Y,W,H); + r_ = g_ = b_ = 0; + hue_ = 0.0; + saturation_ = 0.0; + value_ = 0.0; + huebox.box(FL_DOWN_FRAME); + valuebox.box(FL_DOWN_FRAME); + choice.menu(mode_menu); + set_valuators(); + rvalue.callback(rgb_cb); + gvalue.callback(rgb_cb); + bvalue.callback(rgb_cb); + choice.callback(mode_cb); + choice.box(FL_THIN_UP_BOX); + choice.textfont(FL_HELVETICA_BOLD_ITALIC); +} + +//////////////////////////////////////////////////////////////// +// fl_color_chooser(): + +#include <FL/Fl_Window.H> +#include <FL/Fl_Box.H> +#include <FL/Fl_Return_Button.H> + +class ColorChip : public Fl_Widget { + void draw(); +public: + uchar r,g,b; + ColorChip(int X, int Y, int W, int H) : Fl_Widget(X,Y,W,H) { + box(FL_ENGRAVED_FRAME);} +}; + +void ColorChip::draw() { + if (damage()&128) draw_box(); + fl_rectf(x()+Fl::box_dx(box()), + y()+Fl::box_dy(box()), + w()-Fl::box_dw(box()), + h()-Fl::box_dh(box()),r,g,b); +} + +static void chooser_cb(Fl_Object* o, void* vv) { + Fl_Color_Chooser* c = (Fl_Color_Chooser*)o; + ColorChip* v = (ColorChip*)vv; + v->r = uchar(255*c->r()+.5); + v->g = uchar(255*c->g()+.5); + v->b = uchar(255*c->b()+.5); + v->damage(2); +} + +extern const char* fl_ok; +extern const char* fl_cancel; + +int fl_color_chooser(const char* name, double& r, double& g, double& b) { + Fl_Window window(210,165,name); + Fl_Color_Chooser chooser(5, 5, 200, 95); + ColorChip ok_color(5, 105, 95, 30); + Fl_Return_Button ok_button(5, 135, 95, 25, fl_ok); + ColorChip cancel_color(110, 105, 95, 30); + cancel_color.r = uchar(255*r+.5); ok_color.r = cancel_color.r; + ok_color.g = cancel_color.g = uchar(255*g+.5); + ok_color.b = cancel_color.b = uchar(255*b+.5); + Fl_Button cancel_button(110, 135, 95, 25, fl_cancel); + window.resizable(chooser); + chooser.rgb(r,g,b); + chooser.callback(chooser_cb, &ok_color); + window.end(); + window.set_modal(); + window.hotspot(window); + window.show(); + while (window.shown()) { + Fl::wait(); + Fl_Widget* o; + while ((o = Fl::readqueue())) { + if (o == &ok_button) { + r = chooser.r(); + g = chooser.g(); + b = chooser.b(); + return 1; + } + if (o == &window || o == &cancel_button) return 0; + } + } + return 0; +} + +int fl_color_chooser(const char* name, uchar& r, uchar& g, uchar& b) { + double dr = r/255.0; + double dg = g/255.0; + double db = b/255.0; + if (fl_color_chooser(name,dr,dg,db)) { + r = uchar(255*dr+.5); + g = uchar(255*dg+.5); + b = uchar(255*db+.5); + return 1; + } + return 0; +} diff --git a/src/Fl_Counter.cxx b/src/Fl_Counter.cxx new file mode 100644 index 000000000..6064f99b6 --- /dev/null +++ b/src/Fl_Counter.cxx @@ -0,0 +1,140 @@ +// Fl_Counter.H + +// A numerical value with up/down step buttons. From Forms. + +#include <FL/Fl.H> +#include <FL/Fl_Counter.H> +#include <FL/fl_draw.H> +#include <FL/Fl_Input.H> // for default_font + +void Fl_Counter::draw() { + int i; Fl_Boxtype boxtype[5]; + + boxtype[0] = box(); + if (boxtype[0] == FL_UP_BOX) boxtype[0] = Fl_Input::default_box(); + for (i=1; i<5; i++) + if (mouseobj == i) + boxtype[i] = down(box()); + else + boxtype[i] = box(); + + int xx[5], ww[5]; + if (type() == FL_NORMAL_COUNTER) { + int W = w()*15/100; + xx[1] = x(); ww[1] = W; + xx[2] = x()+1*W; ww[2] = W; + xx[0] = x()+2*W; ww[0] = w()-4*W; + xx[3] = x()+w()-2*W; ww[3] = W; + xx[4] = x()+w()-1*W; ww[4] = W; + } else { + int W = w()*20/100; + xx[2] = x(); ww[2] = W; + xx[0] = x()+W; ww[0] = w()-2*W; + xx[3] = x()+w()-1*W; ww[3] = W; + } + + draw_box(boxtype[0], xx[0], y(), ww[0], h(), FL_WHITE); + fl_font(textfont(), textsize(), + Fl_Input::default_font(), Fl_Input::default_size()); + fl_color(active_r() ? textcolor() : inactive(textcolor())); + char str[128]; format(str); + fl_draw(str, xx[0], y(), ww[0], h(), FL_ALIGN_CENTER); + if (!(damage()&128)) return; // only need to redraw text + + if (type() == FL_NORMAL_COUNTER) { + draw_box(boxtype[1], xx[1], y(), ww[1], h(), color()); + fl_draw_symbol("@-4<<", xx[1], y(), ww[1], h(), selection_color()); + } + draw_box(boxtype[2], xx[2], y(), ww[2], h(), color()); + fl_draw_symbol("@-4<", xx[2], y(), ww[2], h(), selection_color()); + draw_box(boxtype[3], xx[3], y(), ww[3], h(), color()); + fl_draw_symbol("@-4>", xx[3], y(), ww[3], h(), selection_color()); + if (type() == FL_NORMAL_COUNTER) { + draw_box(boxtype[4], xx[4], y(), ww[4], h(), color()); + fl_draw_symbol("@-4>>", xx[4], y(), ww[4], h(), selection_color()); + } +} + +void Fl_Counter::increment_cb() { + if (!mouseobj) return; + double v = value(); + switch (mouseobj) { + case 1: v -= lstep_; break; + case 2: v = increment(v, -1); break; + case 3: v = increment(v, 1); break; + case 4: v += lstep_; break; + } + handle_drag(clamp(round(v))); +} + +#define INITIALREPEAT .5 +#define REPEAT .1 + +void Fl_Counter::repeat_callback(void* v) { + Fl_Counter* b = (Fl_Counter*)v; + if (b->mouseobj) { + Fl::add_timeout(REPEAT, repeat_callback, b); + b->increment_cb(); + } +} + +int Fl_Counter::calc_mouseobj() { + if (type() == FL_NORMAL_COUNTER) { + int W = w()*15/100; + if (Fl::event_inside(x(), y(), W, h())) return 1; + if (Fl::event_inside(x()+W, y(), W, h())) return 2; + if (Fl::event_inside(x()+w()-2*W, y(), W, h())) return 3; + if (Fl::event_inside(x()+w()-W, y(), W, h())) return 4; + } else { + int W = w()*20/100; + if (Fl::event_inside(x(), y(), W, h())) return 2; + if (Fl::event_inside(x()+w()-W, y(), W, h())) return 3; + } + return -1; +} + +int Fl_Counter::handle(int event) { + int i; + switch (event) { + case FL_RELEASE: + if (mouseobj) { + Fl::remove_timeout(repeat_callback, this); + mouseobj = 0; + redraw(); + } + handle_release(); + return 1; + case FL_PUSH: + handle_push(); + case FL_DRAG: + i = calc_mouseobj(); + if (i != mouseobj) { + Fl::remove_timeout(repeat_callback, this); + mouseobj = i; + if (i) Fl::add_timeout(INITIALREPEAT, repeat_callback, this); + increment_cb(); + redraw(); + } + return 1; + default: + return 0; + } +} + +Fl_Counter::~Fl_Counter() { + Fl::remove_timeout(repeat_callback, this); +} + +Fl_Counter::Fl_Counter(int x, int y, int w, int h, const char* l) + : Fl_Valuator(x, y, w, h, l) { + box(FL_UP_BOX); + selection_color(FL_INACTIVE_COLOR); // was FL_BLUE + align(FL_ALIGN_BOTTOM); + bounds(-1000000.0, 1000000.0); + Fl_Valuator::step(1, 10); + lstep_ = 1.0; + mouseobj = 0; + textfont_ = FL_HELVETICA; + textsize_ = FL_NORMAL_SIZE; + textcolor_ = FL_BLACK; +} diff --git a/src/Fl_Dial.cxx b/src/Fl_Dial.cxx new file mode 100644 index 000000000..66b207180 --- /dev/null +++ b/src/Fl_Dial.cxx @@ -0,0 +1,113 @@ +// Fl_Dial.C + +// A circular dial control, like xv uses. From Forms. + +#include <FL/Fl.H> +#include <FL/Fl_Dial.H> +#include <FL/fl_draw.H> +#include <stdlib.h> +#include <FL/math.h> + +void Fl_Dial::draw(int x, int y, int w, int h) { + if (damage()&128) draw_box(box(), x, y, w, h, color()); + x += Fl::box_dx(box()); + y += Fl::box_dy(box()); + w -= Fl::box_dw(box()); + h -= Fl::box_dh(box()); + double angle = 270.0*(value()-minimum())/(maximum()-minimum()); + if (type() == FL_FILL_DIAL) { + double a = angle; if (a < 0) a = 0; + // foo: draw this nicely in certain round box types + int foo = (box() > _FL_ROUND_UP_BOX && Fl::box_dx(box())); + if (foo) {x--; y--; w+=2; h+=2;} + fl_color(color()); + fl_pie(x, y, w-1, h-1, 225, 225+360-a); + fl_color(selection_color()); + fl_pie(x, y, w-1, h-1, 225-a, 225); + if (foo) { + fl_color(FL_BLACK); + fl_arc(x, y, w, h, 0, 360); + } + return; + } + if (!(damage()&128)) { + fl_color(color()); + fl_pie(x+1, y+1, w-2, h-2, 0, 360); + } + fl_push_matrix(); + fl_translate(x+w/2-.5, y+h/2-.5); + fl_scale(w-1, h-1); + if (type() == FL_FILL_DIAL) { + fl_rotate(225); + fl_begin_line(); fl_vertex(0, 0); fl_vertex(.5, 0); fl_end_line(); + } + fl_rotate(-angle); + fl_color(selection_color()); + if (type() == FL_LINE_DIAL) { + fl_begin_polygon(); + fl_vertex(0.0, 0.0); + fl_vertex(-0.04, 0.0); + fl_vertex(-0.25, 0.25); + fl_vertex(0.0, 0.04); + fl_end_polygon(); + fl_color(FL_BLACK); + fl_begin_loop(); + fl_vertex(0.0, 0.0); + fl_vertex(-0.04, 0.0); + fl_vertex(-0.25, 0.25); + fl_vertex(0.0, 0.04); + fl_end_loop(); + } else { + fl_begin_polygon(); fl_circle(-0.20, 0.20, 0.07); fl_end_polygon(); + fl_color(FL_BLACK); + fl_begin_loop(); fl_circle(-0.20, 0.20, 0.07); fl_end_loop(); + } + fl_pop_matrix(); +} + +void Fl_Dial::draw() { + draw(x(), y(), w(), h()); + draw_label(); +} + +int Fl_Dial::handle(int event, int x, int y, int w, int h) { + switch (event) { + case FL_PUSH: + handle_push(); + case FL_DRAG: { + double angle; + double val = value(); + int mx = Fl::event_x()-x-w/2; + int my = Fl::event_y()-y-h/2; + if (!mx && !my) return 1; + if (abs(mx) > abs(my)) { + angle = atan(-(double)my/mx); + if (mx>0) angle = 1.25*M_PI - angle; + else angle = 0.25*M_PI - angle; + } else { + angle = atan(-(double)mx/my); + if (my<0) angle = 0.75*M_PI + angle; + else angle = -0.25*M_PI + angle; + } + if (angle<-0.25*M_PI) angle += 2.0*M_PI; + val = minimum() + (maximum()-minimum())*angle/(1.5*M_PI); + if (fabs(val-value()) < (maximum()-minimum())/2.0) + handle_drag(clamp(round(val))); + } return 1; + case FL_RELEASE: + handle_release(); + return 1; + default: + return 0; + } +} + +int Fl_Dial::handle(int e) { + return handle(e, x(), y(), w(), h()); +} + +Fl_Dial::Fl_Dial(int x, int y, int w, int h, const char* l) + : Fl_Valuator(x, y, w, h, l) { + box(FL_OVAL_BOX); + selection_color(FL_INACTIVE_COLOR); // was 37 +} diff --git a/src/Fl_Double_Window.cxx b/src/Fl_Double_Window.cxx new file mode 100644 index 000000000..aa285a5d2 --- /dev/null +++ b/src/Fl_Double_Window.cxx @@ -0,0 +1,157 @@ +// Fl_Double_Window.C + +// A double-buffered window. This is achieved by using the Xdbe extension, +// or a pixmap if that is not available. + +// On systems that support double buffering "naturally" the base +// Fl_Window class will probably do double-buffer and this subclass +// does nothing. + +#include <config.h> +#include <FL/Fl.H> +#include <FL/Fl_Double_Window.H> +#include <FL/x.H> +#include <FL/fl_draw.H> + +#if USE_XDBE + +#include <X11/extensions/Xdbe.h> + +static int use_xdbe; + +static int can_xdbe() { + static int tried; + if (!tried) { + tried = 1; + int event_base, error_base; + if (!XdbeQueryExtension(fl_display, &event_base, &error_base)) return 0; + Drawable root = RootWindow(fl_display,fl_screen); + int numscreens = 1; + XdbeScreenVisualInfo *a = XdbeGetVisualInfo(fl_display,&root,&numscreens); + if (!a) return 0; + for (int j = 0; j < a->count; j++) + if (a->visinfo[j].visual == fl_visual->visualid + /*&& a->visinfo[j].perflevel > 0*/) {use_xdbe = 1; break;} + XdbeFreeVisualInfo(a); + } + return use_xdbe; +} +#endif + +void Fl_Double_Window::show() { +#ifndef WIN32 + if (!shown()) { // don't set the background pixel + fl_open_display(); + Fl_X::make_xid(this); + return; + } +#endif + Fl_Window::show(); +} + +#ifdef WIN32 + +// Code to switch output to an off-screen window: +// this is lame, I allow two to exist... + +static HDC blt_gc[2]; + +void fl_switch_offscreen(HBITMAP bitmap) { + if (!blt_gc[0]) for (int i = 0; i < 2; i++) { + blt_gc[i] = CreateCompatibleDC(fl_gc); + SetTextAlign(blt_gc[i], TA_BASELINE|TA_LEFT); + SetBkMode(blt_gc[i], TRANSPARENT); +#if USE_COLORMAP + if (fl_palette) SelectPalette(blt_gc[i], fl_palette, FALSE); +#endif + } + int which = 0; if (fl_gc == blt_gc[0]) which = 1; + SelectObject(blt_gc[which], bitmap); + fl_gc = blt_gc[which]; +} + +void fl_copy_offscreen(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy) { + int which = 0; if (fl_gc == blt_gc[0]) which = 1; + SelectObject(blt_gc[which], bitmap); + BitBlt(fl_gc, x, y, w, h, blt_gc[which], srcx, srcy, SRCCOPY); +} + +#endif + +// protected method used by Fl_Overlay_Window to fake overlay: +void Fl_Double_Window::_flush(int eraseoverlay) { + make_current(); // make sure fl_gc is non-zero + Fl_X *i = Fl_X::i(this); + if (!i->other_xid) { +#if USE_XDBE + if (can_xdbe()) i->other_xid = + XdbeAllocateBackBufferName(fl_display, fl_xid(this), XdbeCopied); + else +#endif + i->other_xid = fl_create_offscreen(w(), h()); + clear_damage(~0); + } + XRectangle rect = {0,0,w(),h()}; + if (damage()) { + if (i->region && !eraseoverlay) XClipBox(i->region, &rect); + if ( // don't draw if back buffer is ok +#if USE_XDBE + use_xdbe || +#endif + damage() != 2) { +#ifdef WIN32 + fl_begin_offscreen(i->other_xid); + fl_clip_region(i->region); i->region = 0; + draw(); + fl_end_offscreen(); +#else + fl_window = i->other_xid; + fl_clip_region(i->region); i->region = 0; + draw(); + fl_window = i->xid; +#endif + } + } + fl_clip_region(0); +#if USE_XDBE + if (use_xdbe) { + XdbeSwapInfo s; + s.swap_window = fl_xid(this); + s.swap_action = XdbeCopied; + XdbeSwapBuffers(fl_display,&s,1); + } else +#endif + fl_copy_offscreen(rect.x, rect.y, rect.width, rect.height, + i->other_xid, rect.x, rect.y); +} + +void Fl_Double_Window::flush() {_flush(0);} + +void Fl_Double_Window::resize(int X,int Y,int W,int H) { + int ow = w(); + int oh = h(); + Fl_Window::resize(X,Y,W,H); +#if USE_XDBE + if (use_xdbe) return; +#endif + Fl_X* i = Fl_X::i(this); + if (i && i->other_xid && (ow != w() || oh != h())) { + fl_delete_offscreen(i->other_xid); + i->other_xid = 0; + } +} + +void Fl_Double_Window::hide() { + Fl_X* i = Fl_X::i(this); + if (i && i->other_xid) { +#if USE_XDBE + if (!use_xdbe) +#endif + fl_delete_offscreen(i->other_xid); + } + Fl_Window::hide(); +} + +Fl_Double_Window::~Fl_Double_Window() { + hide(); +} diff --git a/src/Fl_Font.H b/src/Fl_Font.H new file mode 100644 index 000000000..5296b9e2e --- /dev/null +++ b/src/Fl_Font.H @@ -0,0 +1,63 @@ +// Fl_Font.H + +// Two internal fltk data structures: + +// Fl_Fontdesc: an entry into the fl_font() table. This entry may contain +// several "fonts" according to the system, for instance under X all the +// sizes are different X fonts, but only one fl_font. + +// Fl_XFont: a structure for an actual system font, with junk to help +// choose it and info on character sizes. Each Fl_Font has a linked +// list of these. These are created the first time each system font +// is used. + +#ifndef FL_FONT_ +#define FL_FONT_ + +class Fl_XFont { +public: + Fl_XFont *next; // linked list of sizes of this style +#ifndef WIN32 + XFontStruct* font; // X font information, 0 for display list + XCharStruct* per_char;// modified from XFontStruct to have 0x20-0xff in it + XCharStruct* free_this; // pointer saved for delete[] + Fl_XFont(const char*, int); +#else + HFONT fid; + int width[256]; + TEXTMETRIC metr; + Fl_XFont(const char*, int, int); +#endif + int number; // which slot in FL's font table + int minsize; // smallest point size that should use this + int maxsize; // largest point size that should use this +#if HAVE_GL + unsigned int listbase;// base of display list, 0 = none +#endif + ~Fl_XFont(); +}; + +extern Fl_XFont *fl_current_xfont; +extern Fl_XFont *fl_fixed_xfont; + +struct Fl_Fontdesc { + const char *name; + Fl_XFont *first; // linked list of sizes of this style +#ifndef WIN32 + char **xlist; // matched X font names + int n; // size of xlist, negative = don't free xlist! +#endif +}; + +extern Fl_Fontdesc *fl_fonts; // the table + +#ifndef WIN32 +// functions for parsing X font names: +const char* fl_font_word(const char *p, int n); +char *fl_find_fontsize(char *name); +#endif + +void fl_draw(const char *, int x, int y, int w, int h, Fl_Align, + void (*callthis)(const char *, int n, int x, int y)); + +#endif diff --git a/src/Fl_Gl_Choice.H b/src/Fl_Gl_Choice.H new file mode 100644 index 000000000..c112559a9 --- /dev/null +++ b/src/Fl_Gl_Choice.H @@ -0,0 +1,57 @@ +// Internal interface to set up OpenGL. + +// A "Fl_Gl_Choice" is used to cache results of calling the +// OpenGL code for system-specific information needed to +// implement a "mode". +// For X this is a visual, and this must be called *before* +// the X window is created. +// For win32 this can be delayed to a more convienent time, +// as it only returns information for modifying a device +// context. +// This is used by Fl_Gl_Window, gl_start(), and gl_visual() + +#ifndef Fl_Gl_Choice_H +#define Fl_Gl_Choice_H + +#ifdef WIN32 +# include <windows.h> +# include <FL/gl.h> +# define GLXContext HGLRC +# define GLX_BUFFER_SIZE 1 +# define GLX_RGBA 2 +# define GLX_GREEN_SIZE 3 +# define GLX_ALPHA_SIZE 4 +# define GLX_ACCUM_GREEN_SIZE 5 +# define GLX_ACCUM_ALPHA_SIZE 6 +# define GLX_DOUBLEBUFFER 7 +# define GLX_DEPTH_SIZE 8 +# define GLX_STENCIL_SIZE 9 +#else +# include <GL/glx.h> +#endif + +// one of these structures is returned: +class Fl_Gl_Choice { + int mode; + const int *alist; + Fl_Gl_Choice *next; +public: +#ifdef WIN32 + PIXELFORMATDESCRIPTOR pfd; +#else + XVisualInfo *vis; // the visual to use + Colormap colormap; // a colormap to use +#endif + uchar r,d,o; // rgb mode, double buffered, overlay flags + // either use mode flags from gl_draw.H or a literal glX int list. + // one of the two arguments must be zero! + static Fl_Gl_Choice *find(int mode, const int *); +}; + +#ifdef WIN32 +// function to create and init the dc needed to draw OpenGL: +class Fl_Window; +HDC fl_private_dc(Fl_Window*, int, Fl_Gl_Choice **gp); +#endif + +#endif diff --git a/src/Fl_Gl_Choice.cxx b/src/Fl_Gl_Choice.cxx new file mode 100644 index 000000000..cd5c497d7 --- /dev/null +++ b/src/Fl_Gl_Choice.cxx @@ -0,0 +1,157 @@ +// Internal interface to select glX visuals +// Called by Fl_Gl_Window.C and by gl_visual() (in gl_start.C) + +#include <config.h> +#if HAVE_GL + +#include <FL/Fl.H> +#include <FL/x.H> +#include <stdlib.h> + +#include "Fl_Gl_Choice.H" + +static Fl_Gl_Choice *first; +GLXContext fl_first_context; + +// this assummes one of the two arguments is zero: +// We keep the list system in Win32 to stay compatible and interpret +// the list later... +Fl_Gl_Choice *Fl_Gl_Choice::find(int mode, const int *alist) { + Fl_Gl_Choice *g; + + for (g = first; g; g = g->next) + if (g->mode == mode && g->alist == alist) + return g; + +#ifndef WIN32 + const int *blist; + int list[32]; + + if (alist) + blist = alist; + else { + int n = 0; + if (mode & FL_INDEX) { + list[n++] = GLX_BUFFER_SIZE; + list[n++] = 8; // glut tries many sizes, but this should work... + } else { + list[n++] = GLX_RGBA; + list[n++] = GLX_GREEN_SIZE; + list[n++] = (mode & FL_RGB8) ? 8 : 1; + if (mode & FL_ALPHA) { + list[n++] = GLX_ALPHA_SIZE; + list[n++] = 1; + } + if (mode & FL_ACCUM) { + list[n++] = GLX_ACCUM_GREEN_SIZE; + list[n++] = 1; + if (mode & FL_ALPHA) { + list[n++] = GLX_ACCUM_ALPHA_SIZE; + list[n++] = 1; + } + } + } + if (mode & FL_DOUBLE) { + list[n++] = GLX_DOUBLEBUFFER; + } + if (mode & FL_DEPTH) { + list[n++] = GLX_DEPTH_SIZE; list[n++] = 1; + } + if (mode & FL_STENCIL) { + list[n++] = GLX_STENCIL_SIZE; list[n++] = 1; + } +#if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample) + if (mode & FL_MULTISAMPLE) { + list[n++] = GLX_SAMPLES_SGIS; + list[n++] = 4; // value Glut uses + } +#endif + list[n] = 0; + blist = list; + } + + fl_open_display(); + XVisualInfo *vis = glXChooseVisual(fl_display, fl_screen, (int *)blist); + if (!vis) { +# if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample) + if (mode&FL_MULTISAMPLE) return find(mode&~FL_MULTISAMPLE,0); +# endif + return 0; + } + +#else + + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL /*| PFD_DEPTH_DONTCARE*/, + PFD_TYPE_RGBA, 24 }; + + if (mode & FL_INDEX) { + pfd.iPixelType = PFD_TYPE_COLORINDEX; + pfd.cColorBits = 8; + } else { + if (mode & FL_ALPHA) pfd.cAlphaBits = 8; + if (mode & FL_ACCUM) { + pfd.cAccumBits = 6; // Wonko: I didn't find any documentation on those bits + pfd.cAccumGreenBits = 1;// Wonko: They don't seem to get anny support yet (4/98) + if (mode & FL_ALPHA) pfd.cAccumAlphaBits = 1; + } + } + if (mode & FL_DOUBLE) pfd.dwFlags |= PFD_DOUBLEBUFFER; + // if (!(mode & FL_DEPTH)) pfd.dwFlags = PFD_DEPTH_DONTCARE; + if (mode & FL_STENCIL) pfd.cStencilBits = 1; + pfd.bReserved = 1; // always ask for overlay + +#endif + + g = new Fl_Gl_Choice; + g->mode = mode; + g->alist = alist; + g->next = first; + first = g; + +#ifdef WIN32 + memcpy(&g->pfd, &pfd, sizeof(PIXELFORMATDESCRIPTOR)); + g->d = ((mode&FL_DOUBLE) != 0); + g->r = (mode & FL_INDEX); + g->o = 0; // not an overlay +#else + g->vis = vis; + g->colormap = 0; + int i; + glXGetConfig(fl_display, vis, GLX_DOUBLEBUFFER, &i); g->d = i; + glXGetConfig(fl_display, vis, GLX_RGBA, &i); g->r = i; + glXGetConfig(fl_display, vis, GLX_LEVEL, &i); g->o = i; + + if (/*MaxCmapsOfScreen(ScreenOfDisplay(fl_display,fl_screen))==1 && */ + vis->visualid == fl_visual->visualid && + !getenv("MESA_PRIVATE_CMAP")) + g->colormap = fl_colormap; + else + g->colormap = XCreateColormap(fl_display, RootWindow(fl_display,fl_screen), + vis->visual, AllocNone); +#endif + + return g; +} + +#ifdef WIN32 + +HDC fl_private_dc(Fl_Window* w, int mode, Fl_Gl_Choice **gp) { + Fl_X* i = Fl_X::i(w); + if (!i->private_dc) { + i->private_dc = GetDCEx(i->xid, 0, DCX_CACHE); + Fl_Gl_Choice *g = Fl_Gl_Choice::find(mode, 0); + if (gp) *gp = g; + int pixelFormat = ChoosePixelFormat(i->private_dc, &g->pfd); + if (!pixelFormat) {Fl::error("Insufficient GL support"); return NULL;} + SetPixelFormat(i->private_dc, pixelFormat, &g->pfd); +#if USE_COLORMAP + if (fl_palette) SelectPalette(i->private_dc, fl_palette, FALSE); +#endif + } + return i->private_dc; +} +#endif + +#endif diff --git a/src/Fl_Gl_Overlay.cxx b/src/Fl_Gl_Overlay.cxx new file mode 100644 index 000000000..cdc9583bc --- /dev/null +++ b/src/Fl_Gl_Overlay.cxx @@ -0,0 +1,175 @@ +// Fl_Gl_Overlay.C + +// Methods on Fl_Gl_Window that create an overlay window. Because +// many programs don't need the overlay, this is seperated into this +// source file so it is not linked in if not used. + +// Under X this is done by creating another window, of class _Fl_Gl_Overlay +// which is a subclass of Fl_Gl_Window except it uses the overlay planes. +// A pointer to this is stored in the "overlay" pointer of the Fl_Gl_Window. + +// Under win32 another GLX context is created to draw into the overlay +// and it is stored in into the "overlay" pointer. + +// In both cases if overlay hardware is unavailable, the overlay is +// "faked" by drawing into the main layers. This is indicated by +// setting overlay == this. + +#include <config.h> +#if HAVE_GL + +#include <FL/Fl.H> +#include <FL/Fl_Gl_Window.H> +#include <FL/x.H> +#include "Fl_Gl_Choice.H" +#include <stdlib.h> + +#if HAVE_GL_OVERLAY + +#ifndef WIN32 + +extern XVisualInfo *fl_find_overlay_visual(); +extern XVisualInfo *fl_overlay_visual; +extern Colormap fl_overlay_colormap; +extern unsigned long fl_transparent_pixel; +static Fl_Gl_Choice overlay_choice; +extern uchar fl_overlay; + +class _Fl_Gl_Overlay : public Fl_Gl_Window { + void draw(); +public: + void show(); + _Fl_Gl_Overlay(int x, int y, int w, int h) : + Fl_Gl_Window(x,y,w,h) { + overlay_choice.vis = fl_overlay_visual; + overlay_choice.colormap = fl_overlay_colormap; + overlay_choice.r = 0; + overlay_choice.d = 0; + overlay_choice.o = 1; + g = &overlay_choice; + deactivate(); + } +}; + +void _Fl_Gl_Overlay::draw() { + if (damage() != 2) glClear(GL_COLOR_BUFFER_BIT); + Fl_Gl_Window *w = (Fl_Gl_Window *)parent(); + uchar save_valid = w->valid_; + w->valid_ = valid_; + fl_overlay = 1; + w->draw_overlay(); + fl_overlay = 0; + valid_ = w->valid_; + w->valid_ = save_valid; +} + +void _Fl_Gl_Overlay::show() { + if (shown()) {Fl_Gl_Window::show(); return;} + fl_background_pixel = int(fl_transparent_pixel); + Fl_Gl_Window::show(); + fl_background_pixel = -1; + // find the outermost window to tell wm about the colormap: + Fl_Window *w = window(); + for (;;) {Fl_Window *w1 = w->window(); if (!w1) break; w = w1;} + XSetWMColormapWindows(fl_display, fl_xid(w), &(Fl_X::i(this)->xid), 1); +} + +int Fl_Gl_Window::can_do_overlay() { + return fl_find_overlay_visual() != 0; +} + +#else // WIN32: + +static int no_overlay_hardware; +int Fl_Gl_Window::can_do_overlay() { + if (no_overlay_hardware) return 0; + // need to write a test here... + return 1; +} + +extern GLXContext fl_first_context; + +#endif + +#else + +int Fl_Gl_Window::can_do_overlay() {return 0;} + +#endif + +void Fl_Gl_Window::make_overlay() { + if (!overlay) { +#if HAVE_GL_OVERLAY +#ifdef WIN32 + if (!no_overlay_hardware) { + HDC hdc = fl_private_dc(this, mode_,&g); + GLXContext context = wglCreateLayerContext(hdc, 1); + if (!context) { // no overlay hardware + no_overlay_hardware = 1; + } else { + // copy all colors except #0 into the overlay palette: + COLORREF pcr[256]; + for (int i = 0; i < 256; i++) { + uchar r,g,b; Fl::get_color((Fl_Color)i,r,g,b); + pcr[i] = RGB(r,g,b); + } + wglSetLayerPaletteEntries(hdc, 1, 1, 255, pcr+1); + wglRealizeLayerPalette(hdc, 1, TRUE); + if (fl_first_context) wglShareLists(fl_first_context, context); + else fl_first_context = context; + overlay = context; + valid(0); + return; + } + } +#else + if (can_do_overlay()) { + _Fl_Gl_Overlay* o = new _Fl_Gl_Overlay(0,0,w(),h()); + overlay = o; + add_resizable(*o); + o->show(); + return; + } +#endif +#endif + overlay = this; // fake the overlay + } +} + +void Fl_Gl_Window::redraw_overlay() { + if (!shown()) return; + make_overlay(); +#ifndef WIN32 + if (overlay != this) + ((Fl_Gl_Window*)overlay)->redraw(); + else +#endif + damage(8); +} + +void Fl_Gl_Window::make_overlay_current() { + make_overlay(); +#if HAVE_GL_OVERLAY + if (overlay != this) { +#ifdef WIN32 + wglMakeCurrent(Fl_X::i(this)->private_dc, (GLXContext)overlay); +#else + Fl_Gl_Window* w = (Fl_Gl_Window*)overlay; + w->make_current(); +#endif + } else +#endif + glDrawBuffer(GL_FRONT); +} + +void Fl_Gl_Window::hide_overlay() { +#if HAVE_GL_OVERLAY +#ifdef WIN32 + // nothing needs to be done? Or should it be erased? +#else + if (overlay && overlay!=this) ((Fl_Gl_Window*)overlay)->hide(); +#endif +#endif +} + +#endif diff --git a/src/Fl_Gl_Window.cxx b/src/Fl_Gl_Window.cxx new file mode 100644 index 000000000..acd08e6f3 --- /dev/null +++ b/src/Fl_Gl_Window.cxx @@ -0,0 +1,287 @@ +// Fl_Gl_Window.C + +#include <config.h> +#if HAVE_GL + +#include <FL/Fl.H> +#include <FL/x.H> +#include <FL/Fl_Gl_Window.H> +#include "Fl_Gl_Choice.H" + +//////////////////////////////////////////////////////////////// + +// The symbol SWAP_TYPE defines what is in the back buffer after doing +// a glXSwapBuffers(). + +// The OpenGl documentation says that the contents of the backbuffer +// are "undefined" after glXSwapBuffers(). However, if we know what +// is in the backbuffers then we can save a good deal of time. For +// this reason you can define some symbols to describe what is left in +// the back buffer. + +// The default of SWAP_SWAP works on an SGI, and will also work (but +// is sub-optimal) on machines that should be SWAP_COPY or SWAP_NODAMAGE. +// The win32 emulation of OpenGL can use COPY, but some (all?) OpenGL +// cards use SWAP. + +// contents of back buffer after glXSwapBuffers(): +#define UNDEFINED 0 // unknown +#define SWAP 1 // former front buffer +#define COPY 2 // unchanged +#define NODAMAGE 3 // unchanged even by X expose() events + +#ifdef MESA +#define SWAP_TYPE NODAMAGE +#else +#define SWAP_TYPE SWAP +#endif + +//////////////////////////////////////////////////////////////// + +int Fl_Gl_Window::can_do(int a, const int *b) { +#ifdef WIN32 + Fl_Gl_Choice *g = Fl_Gl_Choice::find(a,b); + HWND w = GetDesktopWindow(); + HDC dc = GetDC(w); + int r = ChoosePixelFormat(dc, &g->pfd); + ReleaseDC(w,dc); + return r != 0; +#else + return Fl_Gl_Choice::find(a,b) != 0; +#endif +} + +void Fl_Gl_Window::show() { +#ifndef WIN32 + if (!shown()) { + if (!g) { + g = Fl_Gl_Choice::find(mode_,alist); + if (!g) {Fl::error("Insufficient GL support"); return;} + } + Fl_X::make_xid(this, g->vis, g->colormap); + if (overlay && overlay != this) ((Fl_Gl_Window*)overlay)->show(); + } +#endif + Fl_Window::show(); +} + +void Fl_Gl_Window::invalidate() { + valid(0); +#ifndef WIN32 + if (overlay) ((Fl_Gl_Window*)overlay)->valid(0); +#endif +} + +extern GLXContext fl_first_context; // in Fl_Gl_Choice.C + +int Fl_Gl_Window::mode(int m, const int *a) { + if (m == mode_ && a == alist) return 0; + mode_ = m; alist = a; +#ifdef WIN32 + // destroy context and g: + if (shown()) {hide(); show();} +#else + // under X, if the visual changes we must make a new X window (!): + if (shown()) { + Fl_Gl_Choice *g1 = g; + g = Fl_Gl_Choice::find(mode_,alist); + if (!g || g->vis->visualid != g1->vis->visualid || g->d != g1->d) { + hide(); show(); + } + } +#endif + return 1; +} + +#ifdef WIN32 +extern char fl_direct_paint; // true when responding to WM_PAINT +#endif + +void Fl_Gl_Window::make_current() { +#ifdef WIN32 + HDC hdc = fl_private_dc(this, mode_,&g); + if (!context) { + context = wglCreateContext(hdc); + if (fl_first_context) wglShareLists(fl_first_context, (GLXContext)context); + else fl_first_context = (GLXContext)context; + valid(0); + } + wglMakeCurrent(hdc, (GLXContext)context); +#else + if (!context) { + context = glXCreateContext(fl_display, g->vis, fl_first_context, 1); + if (!fl_first_context) fl_first_context = (GLXContext)context; + valid(0); + } + glXMakeCurrent(fl_display, fl_xid(this), (GLXContext)context); +#endif + glDrawBuffer(GL_BACK); +} + +void Fl_Gl_Window::ortho() { + glLoadIdentity(); + glViewport(0, 0, w(), h()); + glOrtho(0, w(), 0, h(), -1, 1); +} + +void Fl_Gl_Window::swap_buffers() { +#ifdef WIN32 + SwapBuffers(Fl_X::i(this)->private_dc); +#else + glXSwapBuffers(fl_display, fl_xid(this)); +#endif +} + +#if HAVE_GL_OVERLAY +#if WIN32 +uchar fl_overlay; // changes how fl_color() works +#endif +#endif + +void Fl_Gl_Window::flush() { + make_current(); + +#if HAVE_GL_OVERLAY +#ifdef WIN32 + uchar save_valid = valid_; + if (overlay && overlay!= this && damage() == 8) goto DRAW_OVERLAY_ONLY; +#endif +#endif + + if (g->d) { + +#if SWAP_TYPE == NODAMAGE + + // don't draw if only overlay damage or expose events: + if ((damage()&~0xA0) || !valid()) draw(); + swap_buffers(); + +#elif SWAP_TYPE == COPY + + // don't draw if only the overlay is damaged: + if (damage() != 8 || !valid()) draw(); + swap_buffers(); + +#else // SWAP_TYPE == SWAP || SWAP_TYPE == UNDEFINED + + if (overlay == this) { // Use CopyPixels to act like SWAP_TYPE == COPY + + // don't draw if only the overlay is damaged: + if (damage1_ || damage() != 8 || !valid()) draw(); + // we use a seperate context for the copy because rasterpos must be 0 + // and depth test needs to be off: + static GLXContext ortho_context; + int init = !ortho_context; +#ifdef WIN32 + if (init) ortho_context = wglCreateContext(Fl_X::i(this)->private_dc); + wglMakeCurrent(Fl_X::i(this)->private_dc, ortho_context); +#else + if (init) + ortho_context = glXCreateContext(fl_display,g->vis,fl_first_context,1); + glXMakeCurrent(fl_display, fl_xid(this), ortho_context); +#endif + if (init) { + glDisable(GL_DEPTH_TEST); + glReadBuffer(GL_BACK); + glDrawBuffer(GL_FRONT); + } + glCopyPixels(0,0,w(),h(),GL_COLOR); + make_current(); // set current context back to draw overlay + damage1_ = 0; + + } else { + +#if SWAP_TYPE == SWAP + uchar old_damage = damage(); + clear_damage(damage1_|old_damage); draw(); + swap_buffers(); + damage1_ = old_damage; +#else // SWAP_TYPE == UNDEFINED + clear_damage(~0); draw(); + swap_buffers(); + damage1_ = ~0; +#endif + + } +#endif + + if (overlay==this) { // fake overlay in front buffer + glDrawBuffer(GL_FRONT); + draw_overlay(); + glDrawBuffer(GL_BACK); + glFlush(); + } + + } else { // single-buffered context is simpler: + + // this faking of the overlay is incorrect but worked good for + // one in-house program: + if (overlay != this || damage()!=8 || !Fl::pushed()) draw(); + if (overlay == this) draw_overlay(); + glFlush(); + + } + +#if HAVE_GL_OVERLAY +#ifdef WIN32 + if (overlay && overlay != this) { + DRAW_OVERLAY_ONLY: + valid_ = save_valid; + wglMakeCurrent(Fl_X::i(this)->private_dc, (GLXContext)overlay); + glDisable(GL_SCISSOR_TEST); + fl_overlay = 1; + glClear(GL_COLOR_BUFFER_BIT); + draw_overlay(); + wglSwapLayerBuffers(Fl_X::i(this)->private_dc,WGL_SWAP_OVERLAY1); + fl_overlay = 0; + } +#endif +#endif + + valid(1); +} + +void Fl_Gl_Window::resize(int X,int Y,int W,int H) { + if (W != w() || H != h()) valid(0); + Fl_Window::resize(X,Y,W,H); +} + +void Fl_Gl_Window::hide() { + if (context) { +#ifdef WIN32 + wglMakeCurrent(0, 0); + if (context && context != fl_first_context) + wglDeleteContext((GLXContext)context); + g = 0; +#else + glXMakeCurrent(fl_display, 0, 0); + if (context != fl_first_context) + glXDestroyContext(fl_display, (GLXContext)context); +#ifdef GLX_MESA_release_buffers + glXReleaseBuffersMESA(fl_display, fl_xid(this)); +#endif +#endif + context = 0; + } + Fl_Window::hide(); +} + +Fl_Gl_Window::~Fl_Gl_Window() { + hide(); +// delete overlay; this is done by ~Fl_Group +} + +void Fl_Gl_Window::init() { + end(); // we probably don't want any children + box(FL_NO_BOX); + mode_ = FL_RGB | FL_DEPTH | FL_DOUBLE; + alist = 0; + context = 0; + g = 0; + overlay = 0; + damage1_ = 0; +} + +void Fl_Gl_Window::draw_overlay() {} + +#endif diff --git a/src/Fl_Group.cxx b/src/Fl_Group.cxx new file mode 100644 index 000000000..e1a0b43ad --- /dev/null +++ b/src/Fl_Group.cxx @@ -0,0 +1,443 @@ +// Fl_Group.C + +// The Fl_Group is the only defined container type in fltk. + +// Fl_Window itself is a subclass of this, and most of the event +// handling is designed so windows themselves work correctly. + +#include <FL/Fl.H> +#include <FL/Fl_Group.H> +#include <FL/Fl_Window.H> +#include <FL/fl_draw.H> +#include <stdlib.h> + +Fl_Group* Fl_Group::current_; + +// Hack: A single child is stored in the pointer to the array, while +// multiple children are stored in an allocated array: +Fl_Widget*const* Fl_Group::array() const { + return children_ <= 1 ? (Fl_Widget**)(&array_) : array_; +} + +int Fl_Group::find(const Fl_Widget* o) const { + Fl_Widget*const* a = array(); + int i; for (i=0; i < children_; i++) if (*a++ == o) break; + return i; +} + +extern Fl_Widget* fl_oldfocus; // set by Fl::focus + +// For back-compatability, we must adjust all events sent to child +// windows so they are relative to that window. + +static int send(Fl_Widget* o, int event) { + if (o->type() < FL_WINDOW) return o->handle(event); + int save_x = Fl::e_x; Fl::e_x -= o->x(); + int save_y = Fl::e_y; Fl::e_y -= o->y(); + int ret = o->handle(event); + Fl::e_y = save_y; + Fl::e_x = save_x; + return ret; +} + +int Fl_Group::handle(int event) { + + Fl_Widget*const* a = array(); + int i; + Fl_Widget* o; + + switch (event) { + + case FL_FOCUS: + if (savedfocus_ && savedfocus_->take_focus()) return 1; + for (i = children(); i--;) if ((*a++)->take_focus()) return 1; + return 0; + + case FL_UNFOCUS: + savedfocus_ = fl_oldfocus; + return 0; + + case FL_KEYBOARD: + return navigation(); + + case FL_SHORTCUT: + for (i = children(); i--;) { + o = a[i]; + if (o->takesevents() && Fl::event_inside(o) && send(o,FL_SHORTCUT)) + return 1; + } + for (i = children(); i--;) { + o = a[i]; + if (o->takesevents() && !Fl::event_inside(o) && send(o,FL_SHORTCUT)) + return 1; + } + if (Fl::event_key() == FL_Enter) return navigation(FL_Down); + return 0; + + case FL_ENTER: + case FL_MOVE: + for (i = children(); i--;) { + o = a[i]; + if (o->takesevents() && Fl::event_inside(o)) { + if (o->contains(Fl::belowmouse())) { + return send(o,FL_MOVE); + } else if (send(o,FL_ENTER)) { + if (!o->contains(Fl::belowmouse())) Fl::belowmouse(o); + return 1; + } + } + } + Fl::belowmouse(this); + return 1; + + case FL_PUSH: + for (i = children(); i--;) { + o = a[i]; + if (o->takesevents() && Fl::event_inside(o)) { + if (send(o,FL_PUSH)) { + if (Fl::pushed() && !o->contains(Fl::pushed())) Fl::pushed(o); + return 1; + } + } + } + return 0; + + case FL_DEACTIVATE: + case FL_ACTIVATE: + for (i = children(); i--;) { + o = *a++; + if (o->active()) o->handle(event); + } + return 1; + + case FL_SHOW: + case FL_HIDE: + for (i = children(); i--;) { + o = *a++; + if (o->visible()) o->handle(event); + } + return 1; + + default: + return 0; + + } +} + +// translate the current keystroke into up/down/left/right for navigation: +#define ctrl(x) (x^0x40) +int navkey() { + switch (Fl::event_key()) { + case FL_Tab: + return (Fl::event_state(FL_SHIFT) ? FL_Left : FL_Right); + case FL_Right: + return FL_Right; + case FL_Left: + return FL_Left; + case FL_Up: + return FL_Up; + case FL_Down: + return FL_Down; + default: + switch (Fl::event_text()[0]) { + case ctrl('N') : return FL_Down; + case ctrl('P') : return FL_Up; + case ctrl('F') : return FL_Right; + case ctrl('B') : return FL_Left; + } + } + return 0; +} + +//void Fl_Group::focus(Fl_Widget *o) {Fl::focus(o); o->handle(FL_FOCUS);} + +#if 0 +const char *nameof(Fl_Widget *o) { + if (!o) return "NULL"; + if (!o->label()) return "<no label>"; + return o->label(); +} +#endif + +// try to move the focus in response to a keystroke: +int Fl_Group::navigation(int key) { + if (children() <= 1) return 0; + if (!key) {key = navkey(); if (!key) return 0;} + Fl_Widget *focus_ = Fl::focus(); + int old_i; + for (old_i=0;;old_i++) { + if (old_i >= children_) return 0; + if (array_[old_i]->contains(focus_)) break; + } + int i = old_i; + + for (;;) { + switch (key) { + case FL_Right: + case FL_Down: + i++; if (i >= children_) i = 0; + break; + case FL_Left: + case FL_Up: + if (i) i--; else i = children_-1; + break; + default: + return 0; + } + if (i == old_i) return 0; + Fl_Widget* o = array_[i]; + switch (key) { + case FL_Down: + case FL_Up: + if (o->x() >= focus_->x()+focus_->w() || + o->x()+o->w() <= focus_->x()) continue; + } + if (o->take_focus()) return 1; + } +} + +//////////////////////////////////////////////////////////////// + +Fl_Group::Fl_Group(int X,int Y,int W,int H,const char *l) +: Fl_Widget(X,Y,W,H,l) { + align(FL_ALIGN_TOP); + children_ = 0; + array_ = 0; + savedfocus_ = 0; + resizable_ = this; + sizes_ = 0; // this is allocated when first resize() is done + // Subclasses may want to construct child objects as part of their + // constructor, so make sure they are add()'d to this object. + // But you must end() the object! + begin(); +} + +void Fl_Group::clear() { + Fl_Widget*const* a = array(); + for (int i=children(); i--;) { + Fl_Widget* o = *a++; + // test the parent to see if child already destructed: + if (o->parent() == this) delete o; + } + if (children() > 1) free((void*)array_); + children_ = 0; + array_ = 0; + savedfocus_ = 0; + resizable_ = this; + init_sizes(); +} + +Fl_Group::~Fl_Group() {clear();} + +void Fl_Group::insert(Fl_Widget &o, int i) { + if (o.parent()) ((Fl_Group*)(o.parent()))->remove(o); + o.parent_ = this; + if (children_ == 0) { // use array pointer to point at single child + array_ = (Fl_Widget**)&o; + } else if (children_ == 1) { // go from 1 to 2 children + Fl_Widget* t = (Fl_Widget*)array_; + array_ = (Fl_Widget**)malloc(2*sizeof(Fl_Widget*)); + array_[!i] = t; array_[i] = &o; + } else { + if (!(children_ & (children_-1))) // double number of children + array_ = (Fl_Widget**)realloc((void*)array_, + 2*children_*sizeof(Fl_Widget*)); + for (int j = children_; j > i; j--) array_[j] = array_[j-1]; + array_[i] = &o; + } + children_++; + init_sizes(); +} + +void Fl_Group::add(Fl_Widget &o) {insert(o, children_);} + +void Fl_Group::remove(Fl_Widget &o) { + int i = find(o); + if (i >= children_) return; + if (&o == savedfocus_) savedfocus_ = 0; + o.parent_ = 0; + children_--; + if (children_ == 1) { // go from 2 to 1 child + Fl_Widget *t = array_[!i]; + free((void*)array_); + array_ = (Fl_Widget**)t; + } else if (children_ > 1) { // delete from array + for (; i < children_; i++) array_[i] = array_[i+1]; + } + init_sizes(); +} + +//////////////////////////////////////////////////////////////// + +// Rather lame kludge here, I need to detect windows and ignore the +// changes to X,Y, since all children are relative to X,Y. That +// is why I check type(): + +// sizes array stores the initial positions of widgets as +// left,right,top,bottom quads. The first quad is the group, the +// second is the resizable (clipped to the group), and the +// rest are the children. This is a convienent order for the +// algorithim. If you change this be sure to fix Fl_Tile which +// also uses this array! + +void Fl_Group::init_sizes() { + delete[] sizes_; sizes_ = 0; +} + +short* Fl_Group::sizes() { + if (!sizes_) { + short* p = sizes_ = new short[4*(children_+2)]; + // first thing in sizes array is the group's size: + if (type() < FL_WINDOW) {p[0] = x(); p[2] = y();} else {p[0] = p[2] = 0;} + p[1] = p[0]+w(); p[3] = p[2]+h(); + // next is the resizable's size: + p[4] = p[0]; // init to the group's size + p[5] = p[1]; + p[6] = p[2]; + p[7] = p[3]; + Fl_Widget* r = resizable(); + if (r && r != this) { // then clip the resizable to it + int t; + t = r->x(); if (t > p[0]) p[4] = t; + t +=r->w(); if (t < p[1]) p[5] = t; + t = r->y(); if (t > p[2]) p[6] = t; + t +=r->h(); if (t < p[3]) p[7] = t; + } + // next is all the children's sizes: + p += 8; + Fl_Widget*const* a = array(); + for (int i=children_; i--;) { + Fl_Widget* o = *a++; + *p++ = o->x(); + *p++ = o->x()+o->w(); + *p++ = o->y(); + *p++ = o->y()+o->h(); + } + } + return sizes_; +} + +void Fl_Group::resize(int X, int Y, int W, int H) { + + if (!resizable() || W==w() && H==h()) { + + if (type() < FL_WINDOW) { + int dx = X-x(); + int dy = Y-y(); + Fl_Widget*const* a = array(); + for (int i=children_; i--;) { + Fl_Widget* o = *a++; + o->resize(o->x()+dx, o->y()+dy, o->w(), o->h()); + } + } + + } else if (children_) { + + short* p = sizes(); + + // get changes in size/position from the initial size: + int dx = X - p[0]; + int dw = W - (p[1]-p[0]); + int dy = Y - p[2]; + int dh = H - (p[3]-p[2]); + if (type() >= FL_WINDOW) dx = dy = 0; + p += 4; + + // get initial size of resizable(): + int IX = *p++; + int IR = *p++; + int IY = *p++; + int IB = *p++; + + Fl_Widget*const* a = array(); + for (int i=children_; i--;) { + Fl_Widget* o = *a++; + + int X = *p++; + if (X >= IR) X += dw; + else if (X > IX) X = IX+((X-IX)*(IR+dw-IX)+(IR-IX)/2)/(IR-IX); + int R = *p++; + if (R >= IR) R += dw; + else if (R > IX) R = IX+((R-IX)*(IR+dw-IX)+(IR-IX)/2)/(IR-IX); + + int Y = *p++; + if (Y >= IB) Y += dh; + else if (Y > IY) Y = IY+((Y-IY)*(IB+dh-IY)+(IB-IY)/2)/(IB-IY); + int B = *p++; + if (B >= IB) B += dh; + else if (B > IY) B = IY+((B-IY)*(IB+dh-IY)+(IB-IY)/2)/(IB-IY); + + o->resize(X+dx, Y+dy, R-X, B-Y); + } + } + + Fl_Widget::resize(X,Y,W,H); +} + +void Fl_Group::draw() { + Fl_Widget*const* a = array(); + if (damage() & ~1) { // redraw the entire thing: + draw_box(); + draw_label(); + for (int i=children_; i--;) { + Fl_Widget& o = **a++; + draw_child(o); + draw_outside_label(o); + } + } else { // only redraw the children that need it: + for (int i=children_; i--;) update_child(**a++); + } +} + +// Draw a child only if it needs it: +void Fl_Group::update_child(Fl_Widget& w) const { + if (w.damage() && w.visible() && w.type() < FL_WINDOW && + fl_not_clipped(w.x(), w.y(), w.w(), w.h())) { + w.draw(); + w.clear_damage(); + } +} + +// Force a child to redraw: +void Fl_Group::draw_child(Fl_Widget& w) const { + if (w.visible() && w.type() < FL_WINDOW && + fl_not_clipped(w.x(), w.y(), w.w(), w.h())) { + w.clear_damage(~0); + w.draw(); + w.clear_damage(); + } +} + +extern char fl_draw_shortcut; + +// Parents normally call this to draw outside labels: +void Fl_Group::draw_outside_label(const Fl_Widget& w) const { + if (!w.visible()) return; + // skip any labels that are inside the widget: + if (!(w.align()&15) || (w.align() & FL_ALIGN_INSIDE)) return; + // invent a box that is outside the widget: + int align = w.align(); + int X = w.x(); + int Y = w.y(); + int W = w.w(); + int H = w.h(); + if (align & FL_ALIGN_TOP) { + align ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP); + Y = y(); + H = w.y()-Y; + } else if (align & FL_ALIGN_BOTTOM) { + align ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP); + Y = Y+H; + H = y()+h()-Y; + } else if (align & FL_ALIGN_LEFT) { + align ^= (FL_ALIGN_LEFT|FL_ALIGN_RIGHT); + X = x(); + W = w.x()-X-3; + } else if (align & FL_ALIGN_RIGHT) { + align ^= (FL_ALIGN_LEFT|FL_ALIGN_RIGHT); + X = X+W+3; + W = x()+this->w()-X; + } + w.draw_label(X,Y,W,H,(Fl_Align)align); +} + diff --git a/src/Fl_Image.cxx b/src/Fl_Image.cxx new file mode 100644 index 000000000..1aac860d4 --- /dev/null +++ b/src/Fl_Image.cxx @@ -0,0 +1,62 @@ +// Fl_Image.C + +// Draw a image in a box. + +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <FL/x.H> +#include <FL/Fl_Widget.H> +#include <FL/Fl_Menu_Item.H> +#include <FL/Fl_Image.H> + +void Fl_Image::draw(int X, int Y, int W, int H, int cx,int cy) { + // clip the box down to the size of image, quit if empty: + if (cx < 0) {W += cx; X -= cx; cx = 0;} + if (cx+W > w) W = w-cx; + if (W <= 0) return; + if (cy < 0) {H += cy; Y -= cy; cy = 0;} + if (cy+H > h) H = h-cy; + if (H <= 0) return; + if (!id) { + id = (ulong)fl_create_offscreen(w, h); + fl_begin_offscreen((Fl_Offscreen)id); + fl_draw_image(array, 0, 0, w, h, d, ld); + fl_end_offscreen(); + } + fl_copy_offscreen(X, Y, W, H, (Fl_Offscreen)id, cx, cy); +} + +Fl_Image::~Fl_Image() { + if (id) fl_delete_offscreen((Fl_Offscreen)id); +} + +static void image_labeltype( + const Fl_Label* o, int x, int y, int w, int h, Fl_Align a) +{ + Fl_Image* b = (Fl_Image*)(o->value); + int cx; + if (a & FL_ALIGN_LEFT) cx = 0; + else if (a & FL_ALIGN_RIGHT) cx = b->w-w; + else cx = (b->w-w)/2; + int cy; + if (a & FL_ALIGN_TOP) cy = 0; + else if (a & FL_ALIGN_BOTTOM) cy = b->h-h; + else cy = (b->h-h)/2; + b->draw(x,y,w,h,cx,cy); +} + +static void image_measure(const Fl_Label* o, int& w, int& h) { + Fl_Image* b = (Fl_Image*)(o->value); + w = b->w; + h = b->h; +} + +void Fl_Image::label(Fl_Widget* o) { + Fl::set_labeltype(_FL_IMAGE_LABEL, image_labeltype, image_measure); + o->label(_FL_IMAGE_LABEL, (const char*)this); +} + +void Fl_Image::label(Fl_Menu_Item* o) { + Fl::set_labeltype(_FL_IMAGE_LABEL, image_labeltype, image_measure); + o->label(_FL_IMAGE_LABEL, (const char*)this); +} diff --git a/src/Fl_Input.cxx b/src/Fl_Input.cxx new file mode 100644 index 000000000..34dfce1b9 --- /dev/null +++ b/src/Fl_Input.cxx @@ -0,0 +1,299 @@ +// Fl_Input.C + +// This is the "user interface", it decodes user actions into what to +// do to the text. See also Fl_Input_.C, where the text is actually +// manipulated (and some ui, in particular the mouse, is done...). +// In theory you can replace this code with another subclass to change +// the keybindings. + +#include <FL/Fl.H> +#include <FL/Fl_Input.H> +#include <FL/fl_draw.H> +#include <math.h> +#include <string.h> +#include <ctype.h> + +void Fl_Input::draw() { + if (type() == FL_HIDDEN_INPUT) return; + Fl_Boxtype b = box() ? box() : default_box(); + if (damage() & 128) draw_box(b, color()); + Fl_Input_::drawtext(x()+Fl::box_dx(b)+3, y()+Fl::box_dy(b), + w()-Fl::box_dw(b)-6, h()-Fl::box_dh(b)); +} + +// kludge so shift causes selection to extend: +int Fl_Input::shift_position(int p) { + return position(p, Fl::event_state(FL_SHIFT) ? mark() : p); +} +int Fl_Input::shift_up_down_position(int p) { + return up_down_position(p, Fl::event_state(FL_SHIFT)); +} + +//////////////////////////////////////////////////////////////// +// Fltk "compose" +// I tried to do compose characters "correctly" with much more user +// feedback. They can see the character they will get, rather than +// the "dead key" effect. Notice that I completely ignore that horrid +// XIM extension! +// Although the current scheme only works for Latin-NR1 character sets +// the intention is to expand this to UTF-8 someday, to allow you to +// compose all characters in all languages with no stupid "locale" +// setting. +// To use, you call "fl_compose()" for each keystroke. You pass it +// the characters it displayed last time plus the new character. It +// returns a new set of characters to replace the old one with. If +// it returns zero length you should leave the old set unchanged and +// treat the new key normally. +// Pressing any function keys or moving the cursor should set the +// compose state back to zero. + +// This string lists a pair for each possible foreign letter in Latin-NR1 +// starting at code 0xa0 (nbsp). If the second character is a space then +// only the first character needs to by typed: +static const char* const compose_pairs = +" ! % # $ y=| & : c a <<~ - r _ * +-2 3 ' u p . , 1 o >>141234? " +"A`A'A^A~A:A*AEC,E`E'E^E:I`I'I^I:D-N~O`O'O^O~O:x O/U`U'U^U:Y'DDss" +"a`a'a^a~a:a*aec,e`e'e^e:i`i'i^i:d-n~o`o'o^o~o:-:o/u`u'u^u:y'ddy:"; + +int fl_compose(int state, char c, int& del, char* buffer, int& ins) { + del = 0; ins = 1; buffer[0] = c; + + if (c == '"') c = ':'; + + if (!state) { // first character + if (c == ' ') {buffer[0]=char(0xA0);return 0x100;} // space turns into nbsp + // see if it is either character of any pair: + state = 0; + for (const char *p = compose_pairs; *p; p += 2) + if (p[0] == c || p[1] == c) { + if (p[1] == ' ') buffer[0] = (p-compose_pairs)/2+0xA0; + state = c; + } + return state; + + } else if (state == 0x100) { // third character + return 0; + + } else { // second character + char c1 = char(state); // first character + // now search for the pair in either order: + for (const char *p = compose_pairs; *p; p += 2) { + if (p[0] == c && p[1] == c1 || p[1] == c && p[0] == c1) { + buffer[0] = (p-compose_pairs)/2+0xA0; + ins = del = 1; + return 0x100; + } + } + return 0; + } +} + +//////////////////////////////////////////////////////////////// + +static int compose; // compose state (# of characters so far + 1) + +// If you define this symbol as zero you will get the peculiar fltk +// behavior where moving off the end of an input field will move the +// cursor into the next field: +// define it as 1 to prevent cursor movement from going to next field: +#define NORMAL_INPUT_MOVE 0 + +#define ctrl(x) (x^0x40) + +int Fl_Input::handle_key() { + int i; + + int pcompose = compose; compose = 0; + char key = Fl::event_text()[0]; + + if (pcompose && Fl::event_length()) { + char buf[20]; int ins; int del; + compose = fl_compose(pcompose-1, key, del, buf, ins); + if (compose) { + replace(position(), del ? position()-del : mark(), buf, ins); + compose++; // store value+1 so 1 can initialize compose state + return 1; + } else { + if (pcompose==1) // compose also acts as quote-next: + return replace(position(),mark(),Fl::event_text(),Fl::event_length()); + } + } + + if (Fl::event_state(FL_ALT|FL_META)) { // reserved for shortcuts + compose = pcompose; + return 0; + } + + switch (Fl::event_key()) { + case FL_Left: + key = ctrl('B'); break; + case FL_Right: + key = ctrl('F'); break; + case FL_Up: + key = ctrl('P'); break; + case FL_Down: + key = ctrl('N'); break; + case FL_Delete: + key = ctrl('D'); break; + case FL_Home: + key = ctrl('A'); break; + case FL_End: + key = ctrl('E'); break; + case FL_BackSpace: + if (mark() != position()) cut(); + else cut(-1); + return 1; + case FL_Enter: + case FL_KP_Enter: + if (when() & FL_WHEN_ENTER_KEY) { + position(size(), 0); + maybe_do_callback(); + return 1; + } else if (type() == FL_MULTILINE_INPUT) + return replace(position(), mark(), "\n", 1); + else + return 0; // reserved for shortcuts + case FL_Tab: + if (Fl::event_state(FL_CTRL) || type()!=FL_MULTILINE_INPUT) return 0; + break; + case FL_Escape: + return 0; // reserved for shortcuts (Forms cleared field) + case FL_Control_R: + case 0xff20: // Multi-Key + compose = 1; + return 1; + } + + switch(key) { + case 0: // key did not translate to any text + compose = pcompose; // allow user to hit shift keys after ^Q + return 0; + case ctrl('A'): + if (type() == FL_MULTILINE_INPUT) + for (i=position(); i && index(i-1)!='\n'; i--) ; + else + i = 0; + return shift_position(i) + NORMAL_INPUT_MOVE; + case ctrl('B'): + return shift_position(position()-1) + NORMAL_INPUT_MOVE; + case ctrl('C'): // copy + return copy(); + case ctrl('D'): + if (mark() != position()) return cut(); + else return cut(1); + case ctrl('E'): + if (type() == FL_MULTILINE_INPUT) + for (i=position(); index(i) && index(i)!='\n'; i++) ; + else + i = size(); + return shift_position(i) + NORMAL_INPUT_MOVE; + case ctrl('F'): + return shift_position(position()+1) + NORMAL_INPUT_MOVE; + case ctrl('K'): + if (position()>=size()) return 0; + if (type() == FL_MULTILINE_INPUT) { + if (index(position()) == '\n') + i = position() + 1; + else + for (i=position()+1; index(i) && index(i) != '\n'; i++); + } else + i = size(); + cut(position(), i); + return copy_cuts(); + case ctrl('N'): + if (type()!=FL_MULTILINE_INPUT) return 0; + for (i=position(); index(i)!='\n'; i++) + if (!index(i)) return NORMAL_INPUT_MOVE; + shift_up_down_position(i+1); + return 1; + case ctrl('P'): + if (type()!=FL_MULTILINE_INPUT) return 0; + for (i = position(); i > 0 && index(i-1) != '\n'; i--) ; + if (!i) return NORMAL_INPUT_MOVE; + shift_up_down_position(i-1); + return 1; + case ctrl('Q'): + compose = 1; + return 1; + case ctrl('U'): + return cut(0, size()); + case ctrl('V'): + case ctrl('Y'): + Fl::paste(*this); + return 1; + case ctrl('X'): + case ctrl('W'): + copy(); + return cut(); + case ctrl('Z'): + case ctrl('_'): + return undo(); + } + + // skip all illegal characters + // this could be improved to make sure characters are inserted at + // legal positions... + if (type() == FL_FLOAT_INPUT) { + if (!strchr("0123456789.eE+-", key)) return 0; + } else if (type() == FL_INT_INPUT) { + if (!strchr("0123456789+-", key)) return 0; + } + + return replace(position(), mark(), Fl::event_text(), Fl::event_length()); +} + +int Fl_Input::handle(int event) { + switch (event) { + + case FL_FOCUS: + switch (Fl::event_key()) { + case FL_Right: + position(0); + break; + case FL_Left: + position(size()); + break; + case FL_Down: + up_down_position(0); + break; + case FL_Up: + up_down_position(size()); + break; + case FL_Tab: + position(size(),0); + break; + } + break; + + case FL_UNFOCUS: + compose = 0; + break; + + case FL_KEYBOARD: + return handle_key(); + + case FL_PUSH: + compose = 0; + if (Fl::event_button() == 2) { + Fl::paste(*this); + if (Fl::focus()==this) return 1; // remove line for Motif behavior + } + if (Fl::focus() != this) { + Fl::focus(this); + handle(FL_FOCUS); // cause minimal update + } + break; + + case FL_DRAG: + case FL_RELEASE: + if (Fl::event_button() == 2) return 0; + break; + } + Fl_Boxtype b = box() ? box() : default_box(); + return Fl_Input_::handletext(event, + x()+Fl::box_dx(b)+3, y()+Fl::box_dy(b), + w()-Fl::box_dw(b)-6, h()-Fl::box_dh(b)); +} + +Fl_Input::Fl_Input(int x, int y, int w, int h, const char *l) +: Fl_Input_(x, y, w, h, l) {} diff --git a/src/Fl_Input_.cxx b/src/Fl_Input_.cxx new file mode 100644 index 000000000..a04d3cef2 --- /dev/null +++ b/src/Fl_Input_.cxx @@ -0,0 +1,704 @@ +// Fl_Input_.C + +// This is the base class for Fl_Input. You can use it directly +// if you are one of those people who like to define their own +// set of editing keys. It may also be useful for adding scrollbars +// to the input field. + +#include <FL/Fl.H> +#include <FL/Fl_Input_.H> +#include <FL/fl_draw.H> +#include <math.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#define MAXBUF 1024 + +//////////////////////////////////////////////////////////////// + +// Copy string p..e to the buffer, replacing characters with ^X and \nnn +// as necessary. Truncate if necessary so the resulting string and +// null terminator fits in a buffer of size n. Return new end pointer. +const char* Fl_Input_::expand(const char* p, char* buf) const { + char* o = buf; + char* e = buf+(MAXBUF-4); + if (type()==FL_SECRET_INPUT) { + while (o<e && p < value_+size_) {*o++ = '*'; p++;} + } else while (o<e) { + if (p >= value_+size_) break; + int c = *p++ & 255; + if (c < ' ' || c == 127) { + if (c=='\n' && type()==FL_MULTILINE_INPUT) {p--; break;} + if (c == '\t' && type()==FL_MULTILINE_INPUT) { + for (c = (o-buf)%8; c<8 && o<e; c++) *o++ = ' '; + } else { + *o++ = '^'; + *o++ = c ^ 0x40; + } + } else if (c >= 128 && c < 0xA0) { + *o++ = '\\'; + *o++ = (c>>6)+'0'; + *o++ = ((c>>3)&7)+'0'; + *o++ = (c&7)+'0'; + } else if (c == 0xA0) { // nbsp + *o++ = ' '; + } else { + *o++ = c; + } + } + *o = 0; + return p; +} + +// After filling in such a buffer, find the width to e +double Fl_Input_::expandpos( + const char* p, // real string + const char* e, // pointer into real string + const char* buf, // conversion of real string by expand() + int* returnn // return offset into buf here +) const { + int n = 0; + if (type()==FL_SECRET_INPUT) n = e-p; + else while (p<e) { + int c = *p++ & 255; + if (c < ' ' || c == 127) { + if (c == '\t' && type()==FL_MULTILINE_INPUT) n += 8-(n%8); + else n += 2; + } else if (c >= 128 && c < 0xA0) { + n += 4; + } else { + n++; + } + } + if (returnn) *returnn = n; + return fl_width(buf, n); +} + +//////////////////////////////////////////////////////////////// + +// minimal update: +// Characters from mu_p to end of widget are redrawn. +// If erase_cursor_only, small part at mu_p is redrawn. +// Right now minimal update just keeps unchanged characters from +// being erased, so they don't blink. + +void Fl_Input_::minimal_update(int p) { + if (damage() & 128) return; // don't waste time if it won't be done + if (damage() & 2) { + if (p < mu_p) mu_p = p; + } else { + mu_p = p; + } + damage(2); + erase_cursor_only = 0; +} + +void Fl_Input_::minimal_update(int p, int q) { + if (q < p) p = q; + minimal_update(p); +} + +//////////////////////////////////////////////////////////////// + +static double up_down_pos; +static int was_up_down; + +void Fl_Input_::setfont() const { + fl_font(textfont(), textsize(), default_font(), default_size()); +} + +void Fl_Input_::drawtext(int X, int Y, int W, int H) { + + int do_mu = !(damage()&128); + if (Fl::focus()!=this && !size()) { + if (do_mu) { // we have to erase it if cursor was there + fl_color(color()); + fl_rectf(X, Y, W, H); + } + return; + } + + int selstart, selend; + if (Fl::focus()!=this && Fl::selection_owner()!=this && Fl::pushed()!=this) + selstart = selend = 0; + else if (position() <= mark()) { + selstart = position(); selend = mark(); + } else { + selend = position(); selstart = mark(); + } + + setfont(); + +#if 0 // patch to do auto-wrap written by Ian West + if ((type()==FL_MULTILINE_INPUT) && (value_==buffer) && (bufsize>=size_)) { + int wwidth = W-10; + int strtofln=0,lastsp=0,idx=0,lastbr=0; + while(idx <= size_){ + if((buffer[idx] <= ' ') || (idx == size_)) { + if(buffer[idx] == '\n') lastbr=idx; + buffer[idx]=' '; + int twidth=(int)fl_width(&buffer[strtofln],idx-strtofln); + if ((twidth >= wwidth) && (lastsp > strtofln)) { +// printf(stderr,"Line break, lastsp=%d, idx=%d, strtofln=%d, lastbr=%d\n",lastsp,idx,strtofln,lastbr); + buffer[lastsp]='\n'; + if (lastsp != lastbr) { + if (lastsp < mu_p){ + mu_p=lastsp; + erase_cursor_only = 0; + } + } + strtofln=lastsp+1; + } else { + lastsp=idx; + } + } + idx++; + } +// fprintf(stderr,"Line length %d %d %d\n",(int)fl_width(buffer),size_, mu_p); +// if(xscroll_ > 0) {xscroll_=0; mu_p=0;} + buffer[size_] = 0; + } +#endif + + const char *p, *e; + char buf[MAXBUF]; + + // count how many lines and put the last one into the buffer: + // And figure out where the cursor is: + int height = fl_height(); + int lines; + int curx, cury; + for (p=value(), curx=cury=lines=0; ;) { + e = expand(p, buf); + if (position() >= p-value() && position() <= e-value()) { + curx = int(expandpos(p, value()+position(), buf, 0)+.5); + if (Fl::focus()==this && !was_up_down) up_down_pos = curx; + cury = lines*height; + if (Fl::focus()==this) { + int fullw = int(expandpos(p, e, buf, 0)); + if (curx > xscroll_+W-20) { + xscroll_ = curx+20-W; + if (xscroll_ > fullw-W+2) xscroll_ = fullw-W+2; + mu_p = 0; erase_cursor_only = 0; + } + if (curx < xscroll_+20 && xscroll_) { + if (fullw > W-2) xscroll_ = curx-20; + else xscroll_ = 0; + mu_p = 0; erase_cursor_only = 0; + } + if (xscroll_ < 0) xscroll_ = 0; + } + } + lines++; + if (e >= value_+size_) break; + if (*e == '\n') e++; + p = e; + } + + // adjust the scrolling: + if (type()==FL_MULTILINE_INPUT) { + int newy = yscroll_; + if (cury < newy) newy = cury; + if (cury > newy+H-height) newy = cury-H+height; + if (newy < -1) newy = -1; + if (newy != yscroll_) {yscroll_ = newy; mu_p = 0; erase_cursor_only = 0;} + } else { + yscroll_ = -(H-height)/2; + } + + fl_clip(X, Y, W, H); + Fl_Color color = active_r() ? textcolor() : inactive(textcolor()); + + p = value(); + // visit each line and draw it: + int desc = height-fl_descent(); + int ypos = -yscroll_; + for (; ypos < H;) { + + // re-expand line unless it is the last one calculated above: + if (lines>1) e = expand(p, buf); + + if (ypos <= -height) goto CONTINUE; // clipped off top + + if (do_mu) { // for minimal update: + const char* pp = value()+mu_p; // pointer to where minimal update starts + if (e >= pp && (!erase_cursor_only || p <= pp)) { // we must erase this + // calculate area to erase: + int x1 = -xscroll_; + if (p < pp) x1 += int(expandpos(p, pp, buf, 0)); + // erase it: + fl_color(this->color()); + fl_rectf(X+x1, Y+ypos, erase_cursor_only?2:W-x1, height); + // it now draws entire line over it + // this should not draw letters to left of erased area, but + // that is nyi. + } + } + + // Draw selection area if required: + if (selstart < selend && selstart <= e-value() && selend > p-value()) { + const char* pp = value()+selstart; + int x1 = -xscroll_; + int offset1 = 0; + if (pp > p) { + fl_color(color); + x1 += int(expandpos(p, pp, buf, &offset1)); + fl_draw(buf, offset1, X-xscroll_, Y+ypos+desc); + } + pp = value()+selend; + int x2 = W; + int offset2; + if (pp <= e) x2 = int(expandpos(p, pp, buf, &offset2))-xscroll_; + else offset2 = strlen(buf); + fl_color(selection_color()); + fl_rectf(X+int(x1+.5), Y+ypos, int(x2-x1), height); + fl_color(contrast(textcolor(), selection_color())); + fl_draw(buf+offset1, offset2-offset1, X+x1, Y+ypos+desc); + if (pp < e) { + fl_color(color); + fl_draw(buf+offset2, X+x2, Y+ypos+desc); + } + } else { + // draw the cursor: + if (Fl::focus() == this && selstart == selend && + position() >= p-value() && position() <= e-value()) { + fl_color(cursor_color()); + fl_rectf(X+curx-xscroll_, Y+ypos, 2, height); + } + fl_color(color); + fl_draw(buf, X-xscroll_, Y+ypos+desc); + } + CONTINUE: + ypos += height; + if (e >= value_+size_) break; + if (*e == '\n') e++; + p = e; + } + + // for minimal update, erase all lines below last one if necessary: + if (type()==FL_MULTILINE_INPUT && do_mu && ypos<H + && (!erase_cursor_only || p <= value()+mu_p)) { + if (ypos < 0) ypos = 0; + fl_color(this->color()); + fl_rectf(X, Y+ypos, W, H-ypos); + } + + fl_pop_clip(); +} + +static int isword(char c) { + return (c&128 || isalnum(c) || strchr("#%&-/@\\_~", c)); +} + +int Fl_Input_::wordboundary(int i) const { + if (i<=0 || i>=size()) return 1; + return isword(index(i-1)) != isword(index(i)); +} + +int Fl_Input_::lineboundary(int i) const { + if (i<=0 || i>=size()) return 1; + if (type() != FL_MULTILINE_INPUT) return 0; + return index(i-1) == '\n' || index(i) == '\n'; +} + +void Fl_Input_::handle_mouse(int X, int Y, int /*W*/, int /*H*/, int drag) { + was_up_down = 0; + if (!size()) return; + setfont(); + + const char *p, *e; + char buf[MAXBUF]; + + int theline = (type()==FL_MULTILINE_INPUT) ? + (Fl::event_y()-Y+yscroll_)/fl_height() : 0; + + int newpos = 0; + for (p=value();; ) { + e = expand(p, buf); + theline--; if (theline < 0) break; + if (*e == '\n') e++; + p = e; + if (e >= value_+size_) break; + } + const char *l, *r, *t; + for (l = p, r = e; l<r; ) { + double f; + t = l+(r-l+1)/2; + f = X-xscroll_+expandpos(p, t, buf, 0); + if (f <= Fl::event_x()) l = t; + else r = t-1; + } + newpos = l-value(); + + int newmark = drag ? mark() : newpos; + if (Fl::event_clicks()) { + if (newpos >= newmark) { + if (newpos == newmark) { + if (newpos < size()) newpos++; + else newmark--; + } + if (Fl::event_clicks()>1) { + while (!lineboundary(newpos)) newpos++; + while (!lineboundary(newmark)) newmark--; + } else { + while (!wordboundary(newpos)) newpos++; + while (!wordboundary(newmark)) newmark--; + } + } else { + if (Fl::event_clicks()>1) { + while (!lineboundary(newpos)) newpos--; + } else { + while (!wordboundary(newpos)) newpos--; + } + } + } + position(newpos, newmark); +} + +int Fl_Input_::position(int p, int m) { + was_up_down = 0; + if (p<0) p = 0; + if (p>size()) p = size(); + if (m<0) m = 0; + if (m>size()) m = size(); + if (p == position_ && m == mark_) return 0; + if (Fl::selection_owner() == this) Fl::selection_owner(0); + if (p != m) { + // new position is a selection + if (Fl::focus()==this || Fl::pushed()==this) { + if (p != position_) minimal_update(position_, p); + if (m != mark_) minimal_update(mark_, m); + } + } else if (Fl::focus() == this) { + // new position is a cursor + if (position_ == mark_) { + // old position was just a cursor + if (!(damage()&2)) { + minimal_update(position_); erase_cursor_only = 1; + } + } else { // old position was a selection + minimal_update(position_, mark_); + } + } + position_ = p; + mark_ = m; + return 1; +} + +int Fl_Input_::up_down_position(int i, int keepmark) { + while (i > 0 && index(i-1) != '\n') i--; // go to start of line + double oldwid = 0.0; + setfont(); + while (index(i) && index(i)!='\n') { + double tt = oldwid + fl_width(index(i)); + if ((oldwid+tt)/2 >= up_down_pos) break; + oldwid = tt; + i++; + } + int j = position(i, keepmark ? mark_ : i); + was_up_down = 1; + return j; +} + +int Fl_Input_::copy() { + if (mark() != position()) { + int b, e; if (position() < mark()) { + b = position(); e = mark(); + } else { + e = position(); b = mark(); + } + if (type()!=FL_SECRET_INPUT) Fl::selection(*this, value()+b, e-b); + return 1; + } + return 0; +} + +#define MAXFLOATSIZE 40 + +static char* undobuffer; +static int undobufferlength; +static Fl_Input_* undowidget; +static int undoat; // points after insertion +static int undocut; // number of characters deleted there +static int undoinsert; // number of characters inserted +static int yankcut; // length of valid contents of buffer, even if undocut=0 + +static void undobuffersize(int n) { + if (n > undobufferlength) { + if (undobuffer) { + do {undobufferlength *= 2;} while (undobufferlength < n); + undobuffer = (char*)realloc(undobuffer, undobufferlength); + } else { + undobufferlength = n+9; + undobuffer = (char*)malloc(undobufferlength); + } + } +} + +// all changes go through here, delete characters b-e and insert text: +int Fl_Input_::replace(int b, int e, const char* text, int ilen) { + + was_up_down = 0; + + if (b<0) b = 0; + if (e<0) e = 0; + if (b>size_) b = size_; + if (e>size_) e = size_; + if (e<b) {int t=b; b=e; e=t;} + if (text && !ilen) ilen = strlen(text); + if (e<=b && !ilen) return 0; // don't clobber undo for a null operation + if (size_+ilen-(e-b) > maximum_size_) { + ilen = maximum_size_-size_+(e-b); + if (ilen < 0) ilen = 0; + } + + put_in_buffer(size_+ilen); + + if (e>b) { + if (undowidget == this && b == undoat) { + undobuffersize(undocut+(e-b)); + memcpy(undobuffer+undocut, value_+b, e-b); + undocut += e-b; + } else if (undowidget == this && e == undoat && !undoinsert) { + undobuffersize(undocut+(e-b)); + memmove(undobuffer+(e-b), undobuffer, undocut); + memcpy(undobuffer, value_+b, e-b); + undocut += e-b; + } else if (undowidget == this && e == undoat && (e-b)<undoinsert) { + undoinsert -= e-b; + } else { + undobuffersize(e-b); + memmove(undobuffer, value_+b, e-b); + undocut = e-b; + undoinsert = 0; + } + memcpy(buffer+b, buffer+e, size_-b+1); + size_ -= e-b; + undowidget = this; + undoat = b; + if (type() == FL_SECRET_INPUT) yankcut = 0; else yankcut = undocut; + } + + if (ilen) { + size_ += ilen; + if (undowidget == this && b == undoat) + undoinsert += ilen; + else { + undocut = 0; + undoinsert = ilen; + } + int i; + for (i=size_; i>b; i--) buffer[i] = buffer[i-ilen]; + for (i=0; i<ilen; i++) buffer[b+i] = text[i]; + } + undowidget = this; + mark_ = position_ = undoat = b+ilen; + + minimal_update(b); + if (when()&FL_WHEN_CHANGED) do_callback(); else set_changed(); + return 1; +} + +int Fl_Input_::undo() { + was_up_down = 0; + if (undowidget != this || !undocut && !undoinsert) return 0; + + int ilen = undocut; + int xlen = undoinsert; + int b = undoat-xlen; + int b1 = b; + + put_in_buffer(size_+ilen); + + if (ilen) { + size_ += ilen; + int i; + for (i=size_; i>b; i--) buffer[i] = buffer[i-ilen]; + for (i=0; i<ilen; i++) buffer[b++] = undobuffer[i]; + } + + if (xlen) { + undobuffersize(xlen); + memcpy(undobuffer, buffer+b, xlen); + memmove(buffer+b, buffer+b+xlen, size_-xlen-b); + size_ -= xlen; + } + + undocut = xlen; + if (xlen) yankcut = xlen; + undoinsert = ilen; + undoat = b; + mark_ = b /* -ilen */; + position_ = b; + + minimal_update(b1); + if (when()&FL_WHEN_CHANGED) do_callback(); else set_changed(); + return 1; +} + +#if 0 +int Fl_Input_::yank() { + // fake yank by trying to get it out of undobuffer + if (!yankcut) return 0; + return change(position(), position(), undobuffer, yankcut); +} +#endif + +int Fl_Input_::copy_cuts() { + // put the yank buffer into the X clipboard + if (!yankcut) return 0; + Fl::selection(*this, undobuffer, yankcut); + return 1; +} + +void Fl_Input_::maybe_do_callback() { + if (changed() || (when()&FL_WHEN_NOT_CHANGED)) { + clear_changed(); do_callback();} +} + +int Fl_Input_::handletext(int event, int X, int Y, int W, int H) { + switch (event) { + + case FL_FOCUS: + if (mark_ == position_) { + minimal_update(size()+1); + } else if (Fl::selection_owner() != this) + minimal_update(mark_, position_); + return 1; + + case FL_UNFOCUS: + if (mark_ == position_) { + if (!(damage()&2)) {minimal_update(position_); erase_cursor_only = 1;} + } else if (Fl::selection_owner() != this) { + minimal_update(mark_, position_); + } + if (when() & FL_WHEN_RELEASE) maybe_do_callback(); + return 1; + + case FL_PUSH: + handle_mouse(X, Y, W, H, Fl::event_state(FL_SHIFT)); + return 1; + + case FL_DRAG: + handle_mouse(X, Y, W, H, 1); + return 1; + + case FL_RELEASE: +// handle_mouse(X, Y, W, H, 1); + copy(); + return 1; + + case FL_SELECTIONCLEAR: + minimal_update(mark_, position_); + return 1; + + case FL_PASTE: { + // strip trailing control characters and spaces before pasting: + const char* t = Fl::event_text(); + const char* e = t+Fl::event_length(); + if (type()!=FL_MULTILINE_INPUT) while (e > t && *(uchar*)(e-1) <= ' ') e--; + return replace(position(), mark(), t, e-t);} + + default: + return 0; + } +} + +/*------------------------------*/ + +Fl_Input_::Fl_Input_(int x, int y, int w, int h, const char* l) +: Fl_Widget(x, y, w, h, l) { + box(FL_NO_BOX); + color(FL_WHITE, FL_SELECTION_COLOR); + align(FL_ALIGN_LEFT); + textsize_ = FL_NORMAL_SIZE; + textfont_ = FL_HELVETICA; + textcolor_ = FL_BLACK; + cursor_color_ = FL_BLACK; // was FL_BLUE + mark_ = position_ = size_ = 0; + bufsize = 0; + value_ = ""; + xscroll_ = yscroll_ = 0; + maximum_size_ = 32767; +} + +void Fl_Input_::put_in_buffer(int len) { + if (value_ == buffer && bufsize > len) return; + if (!bufsize) { + if (len > size_) len += 9; // let a few characters insert before realloc + bufsize = len+1; + buffer = (char*)malloc(bufsize); + } else if (bufsize <= len) { + // we may need to move old value in case it points into buffer: + int moveit = (value_ >= buffer && value_ < buffer+bufsize); + // enlarge current buffer + if (len > size_) { + do {bufsize *= 2;} while (bufsize <= len); + } else { + bufsize = len+1; + } + char* nbuffer = (char*)realloc(buffer, bufsize); + if (moveit) value_ += (nbuffer-buffer); + buffer = nbuffer; + } + memmove(buffer, value_, size_); buffer[size_] = 0; + value_ = buffer; +} + +int Fl_Input_::static_value(const char* str, int len) { + clear_changed(); + if (undowidget == this) undowidget = 0; + if (str == value_ && len == size_) return 0; + if (len) { // non-empty new value: + if (xscroll_ || yscroll_) { + xscroll_ = yscroll_ = 0; + minimal_update(0); + } else { + int i = 0; + // find first different character: + if (value_) { + for (; i<size_ && i<len && str[i]==value_[i]; i++); + if (i==size_ && i==len) return 0; + } + minimal_update(i); + } + value_ = str; + size_ = len; + } else { // empty new value: + if (!size_) return 0; // both old and new are empty. + size_ = 0; + value_ = ""; + xscroll_ = yscroll_ = 0; + minimal_update(0); + } + position(size(), 0); + return 1; +} + +int Fl_Input_::static_value(const char* str) { + return static_value(str, str ? strlen(str) : 0); +} + +int Fl_Input_::value(const char* str, int len) { + int r = static_value(str, len); + if (len) put_in_buffer(len); + return r; +} + +int Fl_Input_::value(const char* str) { + return value(str, str ? strlen(str) : 0); +} + +void Fl_Input_::resize(int X, int Y, int W, int H) { + if (W != w()) xscroll_ = 0; + if (H != h()) yscroll_ = 0; + Fl_Widget::resize(X, Y, W, H); +} + +Fl_Input_::~Fl_Input_() { + if (undowidget == this) undowidget = 0; + if (bufsize) free((void*)buffer); +} + +// end of Fl_Input_.C diff --git a/src/Fl_Light_Button.cxx b/src/Fl_Light_Button.cxx new file mode 100644 index 000000000..dcf7400f5 --- /dev/null +++ b/src/Fl_Light_Button.cxx @@ -0,0 +1,47 @@ +// Fl_Light_Button.C + +// Subclass of Fl_Button where the "box" indicates whether it is +// pushed or not, and the "down box" is drawn small and square on +// the left to indicate the current state. + +// The default down_box of zero draws a rectangle designed to look +// just like Flame's buttons. + +#include <FL/Fl.H> +#include <FL/Fl_Light_Button.H> +#include <FL/fl_draw.H> + +void Fl_Light_Button::draw() { + if (box()) draw_box(this==Fl::pushed() ? down(box()) : box(), color()); + Fl_Color col = value() ? selection_color() : color(); + int d = h()/6; + int W = w()<h() ? w() : h(); + if (down_box()) { + // draw other down_box() styles: + draw_box(down_box(), x()+d, y()+d+1, W-2*d-2, W-2*d-2, col); + } else { + // if down_box() is zero, draw light button style: + int hh = h()-2*d; + int ww = hh/2+1; + int xx = d*2; + if (w()<ww+2*xx) xx = (w()-ww)/2; + draw_box(FL_THIN_DOWN_BOX, x()+xx, y()+d, ww, hh, col); + } + draw_label(x()+W-d, y(), w()-W+d, h()); +} + +int Fl_Light_Button::handle(int event) { + switch (event) { + case FL_RELEASE: + if (box()) redraw(); + default: + return Fl_Button::handle(event); + } +} + +Fl_Light_Button::Fl_Light_Button(int x, int y, int w, int h, const char* l) +: Fl_Button(x, y, w, h, l) { + type(FL_TOGGLE_BUTTON); + selection_color(FL_YELLOW); + align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); +} diff --git a/src/Fl_Menu.cxx b/src/Fl_Menu.cxx new file mode 100644 index 000000000..ddd80c80b --- /dev/null +++ b/src/Fl_Menu.cxx @@ -0,0 +1,694 @@ +// Fl_Menu.C + +// fltk (Fast Light Tool Kit) version 0.99 +// Copyright (C) 1998 Bill Spitzak + +// Warning: this menu code is quite a mess! + +// This file contains code for implementing Fl_Menu_Item, and for +// methods for bringing up popup menu hierarchies without using the +// Fl_Menu_ widget. + +#include <FL/Fl.H> +#include <FL/Fl_Menu_Window.H> +#include <FL/Fl_Menu_.H> +#include <FL/fl_draw.H> + +int Fl_Menu_Item::size() const { + const Fl_Menu_Item* m = this; + int nest = 0; + for (;;) { + if (!m->text) { + if (!nest) return (m-this+1); + nest--; + } else if (m->flags & FL_SUBMENU) { + nest++; + } + m++; + } +} + +const Fl_Menu_Item* Fl_Menu_Item::next(int n) const { + if (n < 0) return 0; // this is so selected==-1 returns NULL + const Fl_Menu_Item* m = this; + int nest = 0; + while (n>0) { + if (!m->text) { + if (!nest) return m; + nest--; + } else if (m->flags&FL_SUBMENU) { + nest++; + } + m++; + if (!nest && m->visible()) n--; + } + return m; +} + +// appearance of current menus are pulled from this parent widget: +static const Fl_Menu_* button; + +//////////////////////////////////////////////////////////////// + +// tiny window for title of menu: +class menutitle : public Fl_Menu_Window { + void draw(); +public: + const Fl_Menu_Item* menu; + menutitle(int X, int Y, int W, int H, const Fl_Menu_Item*); +}; + +// each vertical menu has one of these: +class menuwindow : public Fl_Menu_Window { + void draw(); + void drawentry(const Fl_Menu_Item*, int i, int erase); +public: + menutitle* title; + int handle(int); + int itemheight; // zero == menubar + int numitems; + int selected; + int drawn_selected; // last redraw has this selected + const Fl_Menu_Item* menu; + menuwindow(const Fl_Menu_Item* m, int X, int Y, int W, int H, + const Fl_Menu_Item* picked, const Fl_Menu_Item* title, + int menubar = 0); + ~menuwindow(); + void set_selected(int); + int find_selected(int mx, int my); + int titlex(int); + void autoscroll(int); + void position(int x, int y); +}; + +#define BW 3 // border thickness +#define LEFT 6 // between left edge of item and edge of box +#define RIGHT 8 // between right edge of item and edge of box +#define BOTTOM 4 // between bottom item and bottom of box +#define LEADING 4 // extra vertical leading +#define TOP 5 // between top item and top of box + +extern char fl_draw_shortcut; + +// width of label, including effect of & characters: +int Fl_Menu_Item::measure(int* hp, const Fl_Menu_* m) const { + Fl_Label l; + l.value = text; + l.type = labeltype_; + l.font = labelsize_ ? labelfont_ : uchar(m ? m->textfont() : FL_HELVETICA); + if (l.font < 4) l.font = (Fl_Font)(l.font | Fl_Menu_::default_font()); + l.size = labelsize_ ? labelsize_ : m ? m->textsize() : FL_NORMAL_SIZE; + l.size += Fl_Menu_::default_size(); + l.color = labelcolor_; + fl_draw_shortcut = 1; + int w = 0; int h = 0; l.measure(w, hp ? *hp : h); + fl_draw_shortcut = 0; + if (flags & (FL_MENU_TOGGLE|FL_MENU_RADIO)) w += 14; + return w; +} + +void Fl_Menu_Item::draw(int x, int y, int w, int h, const Fl_Menu_* m, + int selected) const { + Fl_Label l; + l.value = text; + l.type = labeltype_; + l.font = labelsize_ ? labelfont_ : uchar(m ? m->textfont() : FL_HELVETICA); + if (l.font < 4) l.font = (Fl_Font)(l.font | Fl_Menu_::default_font()); + l.size = labelsize_ ? labelsize_ : m ? m->textsize() : FL_NORMAL_SIZE; + l.size += Fl_Menu_::default_size(); + l.color = !active() ? (labelcolor_|8) : labelcolor_; + Fl_Color color = m ? m->color() : FL_GRAY; + if (selected) { + Fl_Color r = m ? m->selection_color() : FL_SELECTION_COLOR; + Fl_Boxtype b = m && m->down_box() ? m->down_box() : FL_FLAT_BOX; + if (contrast(r,color)!=r) { // back compatability boxtypes + if (selected == 2) { // menu title + r = color; + b = m ? m->box() : FL_UP_BOX; + } else { + r = (Fl_Color)(FL_COLOR_CUBE-1); // white + b = FL_THIN_UP_BOX; + } + } else { + l.color = contrast((Fl_Color)labelcolor_, r); + } + if (selected == 2) { + fl_draw_box(b, x, y, w, h, r); + x += LEFT; + w -= LEFT+RIGHT; + } else { + fl_draw_box(b, x-2, y-1, w+7, h+2, r); + } + } + + if (flags & (FL_MENU_TOGGLE|FL_MENU_RADIO)) { + int y1 = y+(h-14)/2; + fl_color(FL_DARK3); + if (flags & FL_MENU_RADIO) { + fl_line(x-1, y1+7, x+5, y1+1, x+11, y1+7); + if (selected) { + fl_color(color); + fl_polygon(x, y1+7, x+5, y1+2, x+10, y1+7, x+5, y1+12); + } + fl_color(FL_LIGHT3); fl_line(x+11, y1+7, x+5, y1+13, x-1, y1+7); + if (value()) { + fl_color(FL_BLACK); + fl_polygon(x+1, y1+7, x+5, y1+3, x+9, y1+7, x+5, y1+11); + } + } else { + fl_yxline(x, y1+11, y1+2, x+9); + if (selected) {fl_color(color); fl_rectf(x+1, y1+3, 9, 9);} + fl_color(FL_LIGHT3); fl_xyline(x+1, y1+12, x+10, y1+3); + if (value()) {fl_color(FL_BLACK); fl_rectf(x+2, y1+4, 7, 7);} + } + x += 14; w -= 14; + } + + fl_draw_shortcut = 1; + l.draw(x, y, w, h, FL_ALIGN_LEFT); + fl_draw_shortcut = 0; +} + +menutitle::menutitle(int X, int Y, int W, int H, const Fl_Menu_Item* L) : + Fl_Menu_Window(X, Y, W, H, 0) { + end(); + set_modal(); + clear_border(); + menu = L; + if (L->labelcolor_) clear_overlay(); + box(FL_NO_BOX); +} + +menuwindow::menuwindow(const Fl_Menu_Item* m, int X, int Y, int Wp, int Hp, + const Fl_Menu_Item* picked, const Fl_Menu_Item* t, + int menubar) + : Fl_Menu_Window(X, Y, Wp, Hp, 0) +{ + end(); + set_modal(); + clear_border(); + menu = m; + drawn_selected = -1; + box(FL_NO_BOX); + selected = -1; + {int i = 0; + if (m) for (const Fl_Menu_Item* m1=m; ; m1 = m1->next(), i++) { + if (picked) { + if (m1 == picked) {selected = i; picked = 0;} + else if (m1 > picked) {selected = i-1; picked = 0; Wp = Hp = 0;} + } + if (!m1->text) break; + } + numitems = i;} + + if (menubar) { + itemheight = 0; + title = 0; + return; + } + + itemheight = 1; + + int hotKeysw = 0; + int Wtitle = 0; + int Htitle = 0; + if (t) Wtitle = t->measure(&Htitle, button); + int W = Wtitle; + if (m) for (; m->text; m = m->next()) { + int h; int w1 = m->measure(&h, button); + if (h+LEADING>itemheight) itemheight = h+LEADING; + if (m->flags&(FL_SUBMENU|FL_SUBMENU_POINTER)) w1 += 14; + if (w1 > W) W = w1; + if (m->shortcut_) { + w1 = int(fl_width(fl_shortcut_label(m->shortcut_))) + 8; + if (w1 > hotKeysw) hotKeysw = w1; + } + if (m->labelcolor_) clear_overlay(); + } + if (selected >= 0 && !Wp) X -= W/2; + W += hotKeysw+LEFT+RIGHT; if (Wp > W) W = Wp; + + if (!Wp) {if (X < 0) X = 0; if (X > Fl::w()-W) X= Fl::w()-W;} + x(X); w(W); + h((numitems ? itemheight*numitems-LEADING : 0)+TOP+BOTTOM+1); + if (selected >= 0) + Y = Y+(Hp-itemheight)/2-selected*itemheight-2; + else + Y = Y+Hp; + if (m) y(Y-1); else {y(Y-3); w(1); h(1);} + + if (t) { + int ht = button && button->h() <= 50 ? button->h()-6 + : Htitle+TOP+BOTTOM-1; + title = new menutitle(X, Y-ht-3, Wtitle+LEFT+RIGHT, ht, t); + } else + title = 0; +} + +menuwindow::~menuwindow() { + delete title; +} + +void menuwindow::position(int X, int Y) { + if (title) {title->position(X, title->y()+Y-y());} + Fl_Menu_Window::position(X, Y); + x(X); y(Y); // don't wait for response from X +} + +// scroll so item i is visible on screen +void menuwindow::autoscroll(int i) { + int Y = y()+h()-(BOTTOM + (numitems-i)*itemheight - LEADING + 1); + if (Y <= 0) Y = -Y+10; + else { + Y = Y+itemheight-Fl::h(); + if (Y <= 0) return; + Y = -Y-10; + } + Fl_Menu_Window::position(x(), y()+Y); + y(y()+Y); // don't wait for response from X +} + +//////////////////////////////////////////////////////////////// + +void menuwindow::drawentry(const Fl_Menu_Item* m, int i, int erase) { + if (!m) return; // this happens if -1 is selected item and redrawn + + int x = LEFT-3; + int W = this->w(); + int w = W-(LEFT+RIGHT-6); + int y = h()-(BOTTOM + (numitems-i)*itemheight - LEADING + 1); + int h = itemheight - LEADING; + + if (erase && i != selected) { + fl_color(button ? button->color() : FL_GRAY); + fl_rectf(x+1, y-1, w+1, h+2); + } + + m->draw(x+3, y, w-6, h, button, i==selected); + + // the shortcuts and arrows assumme fl_color() was left set by draw(): + if (m->submenu()) { + int y1 = y+(h-14)/2; + fl_polygon(x+w-10, y1+2, x+w-10, y1+2+10, x+w, y1+2+5); + } else if (m->shortcut_) { + Fl_Font f = button ? button->textfont() : FL_HELVETICA; + fl_font(f, button ? button->textsize() : FL_NORMAL_SIZE, + Fl_Menu_::default_font(), Fl_Menu_::default_size()); + fl_draw(fl_shortcut_label(m->shortcut_), x, y, w-3, h, FL_ALIGN_RIGHT); + } + + if (m->flags & FL_MENU_DIVIDER) { + fl_color(FL_DARK3); + fl_xyline(BW-1, y+h+1, W-BW); + fl_color(FL_LIGHT3); + fl_xyline(BW, y+h+2, W-BW); + } + +} + +void menutitle::draw() { + menu->draw(0, 0, w(), h(), button, 2); +} + +void menuwindow::draw() { + + if (damage() != 1) { // complete redraw + if (menu) { + fl_draw_box(button&&button->box() ? button->box() : FL_UP_BOX, + 0, 0, w(), h(), + button ? button->color() : FL_GRAY); + const Fl_Menu_Item* m; int i; + for (m=menu, i=0; m->text; i++, m = m->next()) drawentry(m, i, 0); + } + } else { + if (damage() & 1 && selected!=drawn_selected) { // change selection + drawentry(menu->next(drawn_selected), drawn_selected, 1); + drawentry(menu->next(selected), selected, 1); + } + } + drawn_selected = selected; +} + +void menuwindow::set_selected(int i) { + if (i != selected) {selected = i; damage(1);} +} + +//////////////////////////////////////////////////////////////// + +int menuwindow::find_selected(int mx, int my) { + if (!menu || !menu->text) return -1; + mx -= x(); + my -= y(); + if (my <= 0 || my >= h()) return -1; + if (!itemheight) { // menubar + int x = BW; int i = 0; + const Fl_Menu_Item* m = menu; + for (; ; m = m->next(), i++) { + if (!m->text) return -1; + x += m->measure(0, button) + 16; + if (x > mx) break; + } + return i; + } + if (mx <= 0 || mx >= w()) return -1; + my -= h()-(BOTTOM + numitems*itemheight - LEADING + 2); + if (my <= 0) return -1; + int i = my/itemheight; + if (i>=numitems) i = numitems-1; + return i; +} + +// return horizontal position for item i in a menubar: +int menuwindow::titlex(int i) { + const Fl_Menu_Item* m; + int x = BW; + for (m=menu; i--; m = m->next()) x += m->measure(0, button) + 16; + return x; +} + +// match shortcuts & label shortcuts, don't search submenus: +// returns menu item and index +const Fl_Menu_Item* Fl_Menu_Item::find_shortcut(int* ip) const { + const Fl_Menu_Item* m1 = this; + for (int ii = 0; m1 && m1->text; m1 = m1->next(1), ii++) { + if (m1->activevisible() && + (Fl::test_shortcut(m1->shortcut_) + || Fl_Widget::test_shortcut(m1->text))) {if (ip) *ip=ii; return m1;} + } + return 0; +} + +//////////////////////////////////////////////////////////////// +// Fl_Menu_Item::popup(...) + +// Because Fl::grab() is done, all events go to one of the menu windows. +// But the handle method needs to look at all of them to find out +// what item the user is pointing at. And it needs a whole lot +// of other state variables to determine what is going on with +// the currently displayed menus. +// So the main loop (handlemenu()) puts all the state in a structure +// and puts a pointer to it in a static location, so the handle() +// on menus can refer to it and alter it. The handle() method +// changes variables in this state to indicate what item is +// picked, but does not actually alter the display, instead the +// main loop does that. This is because the X mapping and unmapping +// of windows is slow, and we don't want to fall behind the events. + +struct menustate { + const Fl_Menu_Item* current_item; // what mouse is pointing at + int menu_number; // which menu it is in + int item_number; // which item in that menu + menuwindow* p[20]; // pointers to menus + int nummenus; + int menubar; // if true p[0] is a menubar + int state; // 0 at first, 1 after push, 2 when done +}; +static menustate* p; + +static inline void setitem(const Fl_Menu_Item* i, int m, int n) { + p->current_item = i; + p->menu_number = m; + p->item_number = n; +} + +static void setitem(int m, int n) { + menustate &p = *(::p); + p.current_item = (m >= 0 && n >= 0) ? + p.current_item = p.p[m]->menu->next(n) : 0; + p.menu_number = m; + p.item_number = n; +} + +static int forward(int menu) { // go to next item in menu menu if possible + menustate &p = *(::p); + menuwindow &m = *(p.p[menu]); + int item = (menu == p.menu_number) ? p.item_number : m.selected; + while (++item < m.numitems) { + const Fl_Menu_Item* m1 = m.menu->next(item); + if (m1->activevisible()) {setitem(m1, menu, item); return 1;} + } + return 0; +} + +static int backward(int menu) { // previous item in menu menu if possible + menustate &p = *(::p); + menuwindow &m = *(p.p[menu]); + int item = (menu == p.menu_number) ? p.item_number : m.selected; + while (--item >= 0) { + const Fl_Menu_Item* m1 = m.menu->next(item); + if (m1->activevisible()) {setitem(m1, menu, item); return 1;} + } + return 0; +} + +int menuwindow::handle(int e) { + menustate &p = *(::p); + switch (e) { + case FL_KEYBOARD: + switch (Fl::event_key()) { + case FL_Up: + if (p.menu_number < 0) setitem(0, 0); + if (p.menubar && p.menu_number == 0) ; + else if (backward(p.menu_number)); + else if (p.menubar && p.menu_number==1) setitem(0, p.p[0]->selected); + return 1; + case FL_Down: + if (p.menu_number < 0) setitem(0, 0); + else if (p.menu_number || !p.menubar) forward(p.menu_number); + else if (p.menu_number < p.nummenus-1) forward(p.menu_number+1); + return 1; + case FL_Right: + if (p.menubar && (p.menu_number<=0 || p.menu_number==1 && p.nummenus==2)) + forward(0); + else if (p.menu_number < p.nummenus-1) forward(p.menu_number+1); + return 1; + case FL_Left: + if (p.menubar && p.menu_number<=1) backward(0); + else if (p.menu_number>0) + setitem(p.menu_number-1, p.p[p.menu_number-1]->selected); + return 1; + case FL_Enter: + p.state = 2; + return 1; + case FL_Escape: + setitem(0, -1, 0); + p.state = 2; + return 1; + } + break; + case FL_SHORTCUT: { + for (int menu = p.nummenus; menu--;) { + menuwindow &mw = *(p.p[menu]); + int item; const Fl_Menu_Item* m = mw.menu->find_shortcut(&item); + if (m) { + setitem(m, menu, item); + if (!m->submenu()) p.state = 2; + return 1; + } + }} break; + case FL_PUSH: + //case FL_MOVE: + case FL_DRAG: { + int mx = Fl::event_x_root(); + int my = Fl::event_y_root(); + int item=0; int menu; + for (menu = p.nummenus-1; menu >= 0; menu--) { + item = p.p[menu]->find_selected(mx, my); + if (item >= 0) break; + } + setitem(menu, item); + if (e == FL_PUSH) { + // detect second click on a menu title: + if (p.current_item && item == p.p[menu]->selected) p.state = 3; + else p.state = 1; + } + } return 1; + case FL_RELEASE: + if (!p.current_item) { + if (p.state || !Fl::event_is_click()) p.state = 2; + } else { + if (p.state == 3 && Fl::event_is_click()) p.state = 2; + else if (p.current_item->activevisible() && !p.current_item->submenu()) + p.state = 2; + } + return 1; + } + return Fl_Window::handle(e); +} + +const Fl_Menu_Item* Fl_Menu_Item::pulldown( + int X, int Y, int W, int H, + const Fl_Menu_Item* initial_item, + const Fl_Menu_* pbutton, + const Fl_Menu_Item* t, + int menubar) const +{ + Fl_Group::current(0); // fix possible user error... + + button = pbutton; + if (pbutton) { + for (Fl_Window* w = pbutton->window(); w; w = w->window()) { + X += w->x(); + Y += w->y(); + } + } else { + X += Fl::event_x_root()-Fl::event_x(); + Y += Fl::event_y_root()-Fl::event_y(); + } + menuwindow mw(this, X, Y, W, H, initial_item, t, menubar); + Fl::grab(mw); + menustate p; ::p = &p; + p.p[0] = &mw; + p.nummenus = 1; + p.menubar = menubar; + p.state = 0; + + menuwindow* fakemenu = 0; // kludge for buttons in menubar + + // preselected item, pop up submenus if necessary: + if (initial_item && mw.selected >= 0) { + setitem(0, mw.selected); + goto STARTUP; + } + + p.current_item = 0; p.menu_number = -1; p.item_number = -1; + if (menubar) mw.handle(FL_DRAG); // find the initial menu + initial_item = p.current_item; + if (initial_item) goto STARTUP; + + // the main loop, runs until p.state goes to 2: + for (;;) { + + // make sure all the menus are shown: + {for (int k = menubar; k < p.nummenus; k++) + if (!p.p[k]->shown()) { + if (p.p[k]->title) p.p[k]->title->show(); + p.p[k]->show(); + } + } + + // get events: + {const Fl_Menu_Item* oldi = p.current_item; + Fl::wait(); + if (p.state == 2) break; // done. + if (p.current_item == oldi) continue;} + // only do rest if item changes: + + delete fakemenu; fakemenu = 0; // turn off "menubar button" + + if (!p.current_item) { // pointing at nothing + // turn off selection in deepest menu, but don't erase other menus: + p.p[p.nummenus-1]->set_selected(-1); + continue; + } + + delete fakemenu; fakemenu = 0; + initial_item = 0; // stop the startup code + p.p[p.menu_number]->autoscroll(p.item_number); + + STARTUP: + menuwindow& cw = *p.p[p.menu_number]; + const Fl_Menu_Item* m = p.current_item; + if (!m->activevisible()) { // pointing at inactive item + cw.set_selected(-1); + initial_item = 0; // turn off startup code + continue; + } + cw.set_selected(p.item_number); + + if (m==initial_item) initial_item=0; // stop the startup code if item found + if (m->submenu()) { + const Fl_Menu_Item* title = m; + const Fl_Menu_Item* menutable; + if (m->flags&FL_SUBMENU) menutable = m+1; + else menutable = (Fl_Menu_Item*)(m)->user_data_; + // figure out where new menu goes: + int nX, nY; + if (!p.menu_number && p.menubar) { // menu off a menubar: + nX = cw.x() + cw.titlex(p.item_number); + nY = cw.y() + cw.h(); + initial_item = 0; + } else { + nX = cw.x() + cw.w(); + nY = cw.y() + 1 + p.item_number * cw.itemheight; + title = 0; + } + if (initial_item) { // bring up submenu containing initial item: + menuwindow* n = new menuwindow(menutable,X,Y,W,H,initial_item,title); + p.p[p.nummenus++] = n; + // move all earlier menus to line up with this new one: + if (n->selected>=0) { + int dy = n->y()-nY; + int dx = n->x()-nX; + for (int menu = 0; menu <= p.menu_number; menu++) { + menuwindow* t = p.p[menu]; + int nx = t->x()+dx; if (nx < 0) {nx = 0; dx = -t->x();} + int ny = t->y()+dy+1; if (ny < 0) {ny = 0; dy = -t->y()-1;} + t->position(nx, ny); + } + setitem(p.nummenus-1, n->selected); + goto STARTUP; + } + } else if (p.nummenus > p.menu_number+1 && + p.p[p.menu_number+1]->menu == menutable) { + // the menu is already up: + while (p.nummenus > p.menu_number+2) delete p.p[--p.nummenus]; + p.p[p.nummenus-1]->set_selected(-1); + } else { + // delete all the old menus and create new one: + while (p.nummenus > p.menu_number+1) delete p.p[--p.nummenus]; + p.p[p.nummenus++]= new menuwindow(menutable,nX,nY,title?1:0,0,0,title); + } + } else { // !m->submenu(): + while (p.nummenus > p.menu_number+1) delete p.p[--p.nummenus]; + if (!p.menu_number && p.menubar) { + // kludge so "menubar buttons" turn "on" by using menu title: + fakemenu = new menuwindow(0, + cw.x()+cw.titlex(p.item_number), + cw.y()+cw.h(), 0, 0, + 0, m); + fakemenu->title->show(); + } + } + } + const Fl_Menu_Item* m = p.current_item; + delete fakemenu; + while (p.nummenus>1) delete p.p[--p.nummenus]; + mw.hide(); + Fl::release(); + return m; +} + +const Fl_Menu_Item* +Fl_Menu_Item::popup( + int X, int Y, + const char* title, + const Fl_Menu_Item* picked, + const Fl_Menu_* button + ) const +{ + static Fl_Menu_Item dummy; // static so it is all zeros + dummy.text = title; + return pulldown(X, Y, 0, 0, picked, button, title ? &dummy : 0); +} + +const Fl_Menu_Item* Fl_Menu_Item::test_shortcut() const { + const Fl_Menu_Item* m = this; + const Fl_Menu_Item* ret = 0; + if (m) for (; m->text; m = m->next()) { + if (m->activevisible()) { + // return immediately any match of an item in top level menu: + if (Fl::test_shortcut(m->shortcut_)) return m; + // if (Fl_Widget::test_shortcut(m->text)) return m; + // only return matches from lower menu if nothing found in top menu: + if (!ret && m->submenu()) { + const Fl_Menu_Item* s = + (m->flags&FL_SUBMENU) ? m+1:(const Fl_Menu_Item*)m->user_data_; + ret = s->test_shortcut(); + } + } + } + return ret; +} + +// end of Fl_Menu.C diff --git a/src/Fl_Menu_.cxx b/src/Fl_Menu_.cxx new file mode 100644 index 000000000..a045b3489 --- /dev/null +++ b/src/Fl_Menu_.cxx @@ -0,0 +1,96 @@ +// Fl_Menu_.C + +// This is a base class for all items that have a menu: +// Fl_Menu_Bar, Fl_Menu_Button, Fl_Choice +// This provides storage for a menu item, functions to add/modify/delete +// items, and a call for when the user picks a menu item. + +// More code in Fl_Menu_add.C + +#include <FL/Fl.H> +#include <FL/Fl_Menu_.H> +#include <stdlib.h> + +Fl_Font Fl_Menu_::default_font_; +int Fl_Menu_::default_size_; + +int Fl_Menu_::value(const Fl_Menu_Item* m) { + clear_changed(); + if (value_ != m) {value_ = m; return 1;} + return 0; +} + +// When user picks a menu item, call this. It will do the callback. +// Unfortunatly this also casts away const for the checkboxes, but this +// was necessary so non-checkbox menus can really be declared const... +const Fl_Menu_Item* Fl_Menu_::picked(const Fl_Menu_Item* v) { + if (v) { + if (v->radio()) { + if (!v->value()) { // they are turning on a radio item + set_changed(); + ((Fl_Menu_Item*)v)->setonly(); + } + } else if (v->flags & FL_MENU_TOGGLE) { + set_changed(); + ((Fl_Menu_Item*)v)->flags ^= FL_MENU_VALUE; + } else if (v != value_) { // normal item + set_changed(); + } + value_ = v; + if (when()&(FL_WHEN_CHANGED|FL_WHEN_RELEASE)) { + if (changed() || when()&FL_WHEN_NOT_CHANGED) { + clear_changed(); + if (value_ && value_->callback_) value_->do_callback((Fl_Widget*)this); + else do_callback(); + } + } + } + return v; +} + +// turn on one of a set of radio buttons +void Fl_Menu_Item::setonly() { + flags |= FL_MENU_RADIO | FL_MENU_VALUE; + Fl_Menu_Item* j; + for (j = this; ; ) { // go down + if (j->flags & FL_MENU_DIVIDER) break; // stop on divider lines + j++; + if (!j->text || !j->radio()) break; // stop after group + j->clear(); + } + for (j = this-1; ; j--) { // go up + if (!j->text || (j->flags&FL_MENU_DIVIDER) || !j->radio()) break; + j->clear(); + } +} + +Fl_Menu_::Fl_Menu_(int X,int Y,int W,int H,const char* l) +: Fl_Widget(X,Y,W,H,l) { + set_flag(SHORTCUT_LABEL); + box(FL_UP_BOX); + when(FL_WHEN_RELEASE_ALWAYS); + value_ = menu_ = 0; + alloc = 0; + selection_color(FL_SELECTION_COLOR); + textfont(FL_HELVETICA); + textsize(FL_NORMAL_SIZE); + textcolor(FL_BLACK); + down_box(FL_NO_BOX); +} + +int Fl_Menu_::size() const { + if (!menu_) return 0; + return menu_->size(); +} + +void Fl_Menu_::menu(const Fl_Menu_Item* m) { + // if (alloc) clear(); + alloc = 0; + value_ = menu_ = (Fl_Menu_Item*)m; +} + +Fl_Menu_::~Fl_Menu_() { + // if (alloc) clear(); +} + +// end of Fl_Menu_.C diff --git a/src/Fl_Menu_Bar.cxx b/src/Fl_Menu_Bar.cxx new file mode 100644 index 000000000..763f1acb1 --- /dev/null +++ b/src/Fl_Menu_Bar.cxx @@ -0,0 +1,33 @@ +#include <FL/Fl.H> +#include <FL/Fl_Menu_Bar.H> + +void Fl_Menu_Bar::draw() { + draw_box(); + if (!menu() || !menu()->text) return; + const Fl_Menu_Item* m; + int X = x()+9; + for (m=menu(); m->text; m = m->next()) { + m->draw(X, y(), 0, h(), this); + X += m->measure(0,this) + 16; + } +} + +int Fl_Menu_Bar::handle(int event) { + const Fl_Menu_Item* v; + if (menu() && menu()->text) switch (event) { + case FL_PUSH: + v = 0; + J1: + v = menu()->pulldown(x(), y(), w(), h(), v, this, 0, 1); + picked(v); + return 1; + case FL_SHORTCUT: + v = menu()->test_shortcut(); + if (v) {picked(v); return 1;} + v = menu()->find_shortcut(); + if (v) goto J1; + return 0; + } + return 0; +} + diff --git a/src/Fl_Menu_Button.cxx b/src/Fl_Menu_Button.cxx new file mode 100644 index 000000000..96c250063 --- /dev/null +++ b/src/Fl_Menu_Button.cxx @@ -0,0 +1,57 @@ +// Fl_Menu_Button.C + +#include <FL/Fl.H> +#include <FL/Fl_Menu_Button.H> +#include <FL/fl_draw.H> + +void Fl_Menu_Button::draw() { + if (!box() || type()) return; + draw_box(box(), color()); + draw_label(); + if (box() == FL_FLAT_BOX) return; // for XForms compatability + int H = (labelsize()-3)&-2; + int X = x()+w()-H*2; + int Y = y()+(h()-H)/2; + fl_color(FL_DARK3); fl_line(X+H/2, Y+H, X, Y, X+H, Y); + fl_color(FL_LIGHT3); fl_line(X+H, Y, X+H/2, Y+H); +} + +const Fl_Menu_Item* Fl_Menu_Button::popup() { + const Fl_Menu_Item* m; + if (!box() || type()) { + m = menu()->popup(Fl::event_x(), Fl::event_y(), label(), mvalue(), this); + } else { + m = menu()->pulldown(x(), y(), w(), h(), 0, this); + } + picked(m); + return m; +} + +int Fl_Menu_Button::handle(int e) { + if (!menu() || !menu()->text) return 0; + switch (e) { + case FL_ENTER: + case FL_LEAVE: + return (box() && !type()) ? 1 : 0; + case FL_PUSH: + if (!box()) { + if (Fl::event_button() != 3) return 0; + } else if (type()) { + if (!(type() & (1 << (Fl::event_button()-1)))) return 0; + } + popup(); + return 1; + case FL_SHORTCUT: + if (Fl_Widget::test_shortcut()) {popup(); return 1;} + return test_shortcut() != 0; + default: + return 0; + } +} + +Fl_Menu_Button::Fl_Menu_Button(int X,int Y,int W,int H,const char *l) +: Fl_Menu_(X,Y,W,H,l) { + down_box(FL_NO_BOX); +} + +// end of Fl_Menu_Button.C diff --git a/src/Fl_Menu_Window.cxx b/src/Fl_Menu_Window.cxx new file mode 100644 index 000000000..f74db6ea2 --- /dev/null +++ b/src/Fl_Menu_Window.cxx @@ -0,0 +1,130 @@ +// Fl_Menu_Window.H + +// This is the window type used by Fl_Menu to make the pop-ups. +// It draws in the overlay planes if possible. + +// Also here is the implementation of the mouse & keyboard grab, +// which are used so that clicks outside the program's windows +// can be used to dismiss the menus. + +#include <config.h> +#include <FL/Fl.H> +#include <FL/x.H> +#include <FL/fl_draw.H> +#include <FL/Fl_Menu_Window.H> + +// WIN32 note: HAVE_OVERLAY is false +#if HAVE_OVERLAY +extern XVisualInfo *fl_find_overlay_visual(); +extern XVisualInfo *fl_overlay_visual; +extern Colormap fl_overlay_colormap; +extern unsigned long fl_transparent_pixel; +static GC gc; // the GC used by all X windows +extern uchar fl_overlay; // changes how fl_color(x) works +#endif + +#include <stdio.h> + +void Fl_Menu_Window::show() { +#if HAVE_OVERLAY + if (!shown() && overlay() && fl_find_overlay_visual()) { + XInstallColormap(fl_display, fl_overlay_colormap); + fl_background_pixel = int(fl_transparent_pixel); + Fl_X::make_xid(this, fl_overlay_visual, fl_overlay_colormap); + fl_background_pixel = -1; + } else +#endif + Fl_Single_Window::show(); +} + +void Fl_Menu_Window::flush() { +#if HAVE_OVERLAY + if (!fl_overlay_visual || !overlay()) {Fl_Single_Window::flush(); return;} + Fl_X *i = Fl_X::i(this); + fl_window = i->xid; + if (!gc) gc = XCreateGC(fl_display, i->xid, 0, 0); + fl_gc = gc; + fl_overlay = 1; + fl_clip_region(i->region); i->region = 0; + draw(); + fl_overlay = 0; +#else + Fl_Single_Window::flush(); +#endif +} + +void Fl_Menu_Window::erase() { +#if HAVE_OVERLAY + if (!gc || !shown()) return; +//XSetForeground(fl_display, gc, 0); +//XFillRectangle(fl_display, fl_xid(this), gc, 0, 0, w(), h()); + XClearWindow(fl_display, fl_xid(this)); +#endif +} + +// Fix the colormap flashing on Maximum Impact Graphics by erasing the +// menu before unmapping it: +void Fl_Menu_Window::hide() { + erase(); + Fl_Single_Window::hide(); +} + +Fl_Menu_Window::~Fl_Menu_Window() { + hide(); +} + +//////////////////////////////////////////////////////////////// +// "Grab" is done while menu systems are up. This has several effects: +// Events are all sent to the "grab window", which does not even +// have to be displayed (and in the case of Fl_Menu.C it isn't). +// Under X override_redirect and save_under is done to new windows. +// The system is also told to "grab" events and send them to this app. + +extern void fl_fix_focus(); +#ifdef WIN32 +HWND fl_capture; // for some reason we must keep forcing it back on! +#endif + +void Fl::grab(Fl_Window& w) { + grab_ = &w; + fl_fix_focus(); +#ifdef WIN32 + // this seems to have no effect... + SetCapture(fl_capture = fl_xid(first_window())); +#else + XGrabPointer(fl_display, + fl_xid(first_window()), + 1, + ButtonPressMask|ButtonReleaseMask| + ButtonMotionMask|PointerMotionMask, + GrabModeAsync, + GrabModeAsync, + None, + 0, + fl_event_time); + XGrabKeyboard(fl_display, + fl_xid(first_window()), + 1, + GrabModeAsync, + GrabModeAsync, + fl_event_time); +#endif +} + +void Fl::release() { + grab_ = 0; + fl_fix_focus(); +#ifdef WIN32 + fl_capture = 0; + ReleaseCapture(); +#else + XUngrabKeyboard(fl_display, fl_event_time); + XUngrabPointer(fl_display, fl_event_time); + // this flush is done in case the picked menu item goes into + // an infinite loop, so we don't leave the X server locked up: + XFlush(fl_display); +#endif + return; +} + +// end of Fl_Menu_Window.C diff --git a/src/Fl_Menu_add.cxx b/src/Fl_Menu_add.cxx new file mode 100644 index 000000000..1aee7517a --- /dev/null +++ b/src/Fl_Menu_add.cxx @@ -0,0 +1,151 @@ +// Fl_Menu_add.C + +// Methods to alter the menu in an Fl_Menu_ widget. +// This code is seperated so that it is not linked in if not used. + +// These functions are for emulation of Forms and for dynamically +// changing the menus. They are in this source file so they are +// not linked in if not used, which is what will happen if the +// the program only uses constant menu tables. + +// Not at all guaranteed to be Forms compatable, especially with any +// string with a % sign in it! + +#include <FL/Fl_Menu_.H> +#include <string.h> +#include <stdlib.h> + +int fl_old_shortcut(const char* s) { + if (!s || !*s) return 0; + int n = 0; + if (*s == '#') {n |= FL_ALT; s++;} + if (*s == '+') {n |= FL_SHIFT; s++;} + if (*s == '^') {n |= FL_CTRL; s++;} + return n | *s; +} + +int Fl_Menu_Item::add( + const char *text, + int shortcut, + Fl_Callback *cb, + void *data, + int flags) +{ + Fl_Menu_Item *m; + const char *p; + char *q; + char buf[1024]; + + int size = this->size(); + int flags1 = 0; + char* item; + + m = this; + + for (;;) { /* do all the supermenus: */ + + /* fill in the buf with name, changing \x to x: */ + q = buf; + for (p=text; *p && *p != '/'; *q++ = *p++) if (*p=='\\') p++; + *q = 0; + + item = buf; + if (*item == '_') {item++; flags1 = FL_MENU_DIVIDER;} + if (*p != '/') break; /* not a menu title */ + text = p+1; /* point at item title */ + + /* find a matching menu title: */ + for (; m->text; m = m->next()) + if (m->flags&FL_SUBMENU && !strcmp(item,m->text)) break; + + if (!m->text) { /* create a new menu */ + memmove(m+2,m,sizeof(Fl_Menu_Item)*(this+size-m)); + m->text = strdup(item); + m->shortcut_ = 0; + m->callback_ = 0; + m->user_data_ = 0; + m->flags = FL_SUBMENU|flags1; + m->labeltype_ = m->labelfont_ = m->labelsize_ = m->labelcolor_ = 0; + (m+1)->text = 0; + size += 2; + } + m++; /* go into the menu */ + flags1 = 0; + } + + /* find a matching menu item: */ + for (; m->text; m = m->next()) + if (!strcmp(m->text,item)) break; + + if (!m->text) { /* add a new menu item */ + memmove(m+1,m,sizeof(Fl_Menu_Item)*(this+size-m)); + size++; + m->text = strdup(item); + } + + /* fill it in */ + m->shortcut_ = shortcut; + m->callback_ = cb; + m->user_data_ = data; + m->flags = flags|flags1; + m->labeltype_ = m->labelfont_ = m->labelsize_ = m->labelcolor_ = 0; + + return m-this; +} + +// this is really lame, it will crash if this many items are added: +#define FL_MENU_MAXITEMS 128 + +int Fl_Menu_::add(const char *t, int s, Fl_Callback *c,void *v,int f) { + if (!menu_) { + value_ = menu_ = new Fl_Menu_Item[FL_MENU_MAXITEMS+1]; + alloc = 1; + menu_[0].text = 0; + } + return menu_->add(t,s,c,v,f); +} + +int Fl_Menu_::add(const char *str) { + char buf[128]; + int r = 0; + while (*str) { + int shortcut = 0; + char *c; + for (c = buf; *str && *str != '|'; str++) { + if (*str == '\t') {*c++ = 0; shortcut = fl_old_shortcut(str);} + else *c++ = *str; + } + *c = 0; + r = add(buf, shortcut, 0, 0, 0); + if (*str) str++; + } + return r; +} + +void Fl_Menu_::replace(int i, const char *str) { + if (i<0 || i>=size()) return; + if (alloc) free((void *)menu_[i].text); + menu_[i].text = strdup(str); +} + +void Fl_Menu_::remove(int i) { + int n = size(); + if (i<0 || i>=n) return; + if (alloc) free((void *)menu_[i].text); + memmove(&menu_[i],&menu_[i+1],(n-i)*sizeof(Fl_Menu_Item)); +} + +void Fl_Menu_::clear() { + for (int i = size(); i--;) + if (menu_[i].text) free((void*)menu_[i].text); + if (alloc) { + delete[] menu_; + menu_ = 0; + alloc = 0; + } else if (menu_) { + menu_[0].text = 0; + value_ = menu_; + } +} + +// end of Fl_Menu_.C diff --git a/src/Fl_Menu_global.cxx b/src/Fl_Menu_global.cxx new file mode 100644 index 000000000..ddbf8e1de --- /dev/null +++ b/src/Fl_Menu_global.cxx @@ -0,0 +1,20 @@ +// Fl_Menu_global.C + +// Make all the shortcuts in this menu global. +// Currently only one menu at a time and you cannot destruct the menu, +// is this sufficient? + +#include <FL/Fl.H> +#include <FL/Fl_Menu_.H> + +static Fl_Menu_* the_widget; + +static int handler(int e) { + if (e != FL_SHORTCUT || Fl::modal()) return 0; + return the_widget->test_shortcut() != 0; +} + +void Fl_Menu_::global() { + if (!the_widget) Fl::add_handler(handler); + the_widget = this; +} diff --git a/src/Fl_Multi_Label.cxx b/src/Fl_Multi_Label.cxx new file mode 100644 index 000000000..e2b59168b --- /dev/null +++ b/src/Fl_Multi_Label.cxx @@ -0,0 +1,51 @@ +// Fl_Multi_Label.C + +// Allows two labels to be used on a widget (by having one of them +// be one of these it allows an infinte number!) + +#include <FL/Fl.H> +#include <FL/Fl_Widget.H> +#include <FL/Fl_Menu_Item.H> +#include <FL/Fl_Multi_Label.H> + +static void multi_labeltype( + const Fl_Label* o, int x, int y, int w, int h, Fl_Align a) +{ + Fl_Multi_Label* b = (Fl_Multi_Label*)(o->value); + Fl_Label local = *o; + local.value = b->labela; + local.type = b->typea; + int W = w; int H = h; local.measure(W, H); + local.draw(x,y,w,h,a); + if (a & FL_ALIGN_BOTTOM) h -= H; + else if (a & FL_ALIGN_TOP) {y += H; h -= H;} + else if (a & FL_ALIGN_RIGHT) w -= W; + else if (a & FL_ALIGN_LEFT) {x += W; w -= W;} + else {int d = (h+H)/2; y += d; h -= d;} + local.value = b->labelb; + local.type = b->typeb; + local.draw(x,y,w,h,a); +} + +// measurement is only correct for left-to-right appending... +static void multi_measure(const Fl_Label* o, int& w, int& h) { + Fl_Multi_Label* b = (Fl_Multi_Label*)(o->value); + Fl_Label local = *o; + local.value = b->labela; + local.type = b->typea; + local.measure(w,h); + local.value = b->labelb; + local.type = b->typeb; + int W = 0; int H = 0; local.measure(W,H); + w += W; if (H>h) h = H; +} + +void Fl_Multi_Label::label(Fl_Widget* o) { + Fl::set_labeltype(_FL_MULTI_LABEL, multi_labeltype, multi_measure); + o->label(_FL_MULTI_LABEL, (const char*)this); +} + +void Fl_Multi_Label::label(Fl_Menu_Item* o) { + Fl::set_labeltype(_FL_MULTI_LABEL, multi_labeltype, multi_measure); + o->label(_FL_MULTI_LABEL, (const char*)this); +} diff --git a/src/Fl_Output.cxx b/src/Fl_Output.cxx new file mode 100644 index 000000000..a6dba007e --- /dev/null +++ b/src/Fl_Output.cxx @@ -0,0 +1,23 @@ +// Fl_Output.C + +// This subclass of Fl_Input_ does not allow user to edit the output. +// Used to display output. + +#include <FL/Fl.H> +#include <FL/Fl_Output.H> +#include <FL/fl_draw.H> + +void Fl_Output::draw() { + Fl_Boxtype b = box() ? box() : default_box(); + if (damage() & 128) draw_box(b, color()); + Fl_Input_::drawtext(x()+Fl::box_dx(b)+3, y()+Fl::box_dy(b), + w()-Fl::box_dw(b)-6, h()-Fl::box_dh(b)); +} + +int Fl_Output::handle(int event) { + if (event == FL_FOCUS) return 0; + Fl_Boxtype b = box() ? box() : default_box(); + return Fl_Input_::handletext(event, + x()+Fl::box_dx(b)+3, y()+Fl::box_dy(b), + w()-Fl::box_dw(b)-6, h()-Fl::box_dh(b)); +} diff --git a/src/Fl_Overlay_Window.cxx b/src/Fl_Overlay_Window.cxx new file mode 100644 index 000000000..7bbd85904 --- /dev/null +++ b/src/Fl_Overlay_Window.cxx @@ -0,0 +1,115 @@ +// Fl_Overlay_Window.C + +// A window using double-buffering and able to draw an overlay +// on top of that. Uses the hardware to draw the overlay if +// possible, otherwise it just draws in the front buffer. + +#include <config.h> +#include <FL/Fl.H> +#include <FL/Fl_Overlay_Window.H> +#include <FL/fl_draw.H> +#include <FL/x.H> + +void Fl_Overlay_Window::show() { + Fl_Double_Window::show(); + if (overlay_ && overlay_ != this) overlay_->show(); +} + +void Fl_Overlay_Window::hide() { + Fl_Double_Window::hide(); +} + +void Fl_Overlay_Window::flush() { + // a non-zero argument copies entire back buffer to window, erasing + // the overlay. We should only do this if fake overlay needs redraw: + uchar overlay_damage = damage()&8; clear_damage(damage()&~8); + _flush(overlay_damage); + if (overlay_ == this) draw_overlay(); +} + +void Fl_Overlay_Window::resize(int X, int Y, int W, int H) { + Fl_Double_Window::resize(X,Y,W,H); + if (overlay_ && overlay_!=this) overlay_->resize(0,0,w(),h()); +} + +Fl_Overlay_Window::~Fl_Overlay_Window() { + hide(); +// delete overlay; this is done by ~Fl_Group +} + +#if !HAVE_OVERLAY + +int Fl_Overlay_Window::can_do_overlay() {return 0;} + +void Fl_Overlay_Window::redraw_overlay() {overlay_ = this; damage(8);} + +#else + +extern XVisualInfo *fl_find_overlay_visual(); +extern XVisualInfo *fl_overlay_visual; +extern Colormap fl_overlay_colormap; +extern unsigned long fl_transparent_pixel; +static GC gc; // the GC used by all X windows +extern uchar fl_overlay; // changes how fl_color(x) works + +class _Fl_Overlay : public Fl_Window { + friend class Fl_Overlay_Window; + void flush(); + void show(); +public: + _Fl_Overlay(int x, int y, int w, int h) : + Fl_Window(x,y,w,h) {deactivate();} +}; + +int Fl_Overlay_Window::can_do_overlay() { + return fl_find_overlay_visual() != 0; +} + +void _Fl_Overlay::show() { + if (shown()) {Fl_Window::show(); return;} + fl_background_pixel = int(fl_transparent_pixel); + Fl_X::make_xid(this, fl_overlay_visual, fl_overlay_colormap); + fl_background_pixel = -1; + // find the outermost window to tell wm about the colormap: + Fl_Window *w = window(); + for (;;) {Fl_Window *w1 = w->window(); if (!w1) break; w = w1;} + XSetWMColormapWindows(fl_display, fl_xid(w), &(Fl_X::i(this)->xid), 1); +} + +void _Fl_Overlay::flush() { + fl_window = fl_xid(this); + if (!gc) gc = XCreateGC(fl_display, fl_xid(this), 0, 0); + fl_gc = gc; + fl_overlay = 1; + Fl_Overlay_Window *w = (Fl_Overlay_Window *)parent(); + Fl_X *i = Fl_X::i(this); + if (damage() != 2) XClearWindow(fl_display, fl_xid(this)); + fl_clip_region(i->region); i->region = 0; + w->draw_overlay(); + fl_overlay = 0; +} + +void Fl_Overlay_Window::redraw_overlay() { + if (!fl_display) return; // this prevents fluid -c from opening display + if (!overlay_) { + if (can_do_overlay()) { + Fl_Group::current(this); + overlay_ = new _Fl_Overlay(0,0,w(),h()); + Fl_Group::current(0); + } else { + overlay_ = this; // fake the overlay + } + } + if (shown()) { + if (overlay_ == this) + damage(8); + else if (!overlay_->shown()) + overlay_->show(); + else + overlay_->redraw(); + } +} + +#endif + +// End of Fl_Overlay_Window.C diff --git a/src/Fl_Pack.cxx b/src/Fl_Pack.cxx new file mode 100644 index 000000000..c236f29c6 --- /dev/null +++ b/src/Fl_Pack.cxx @@ -0,0 +1,78 @@ +// Fl_Pack.C + +// Based on code by Curtis Edwards +// Group that compresses all it's children together and resizes to surround +// them on each redraw (only if box() is zero) +// Bugs: ? + +#include <FL/Fl.H> +#include <FL/Fl_Pack.H> +#include <FL/fl_draw.H> + +Fl_Pack::Fl_Pack(int x,int y,int w ,int h,const char *l) +: Fl_Group(x, y, w, h, l) { + resizable(0); + spacing_ = 0; + // type(VERTICAL); // already set like this +} + +void Fl_Pack::draw() { + int tx = x()+Fl::box_dx(box()); + int ty = y()+Fl::box_dy(box()); + int tw = w()-Fl::box_dw(box()); + int th = h()-Fl::box_dh(box()); + int current_position = horizontal() ? tx : ty; + int maximum_position = current_position; + uchar d = damage(); + Fl_Widget*const* a = array(); + for (int i = children(); i--;) { + Fl_Widget* o = *a++; + int X,Y,W,H; + if (horizontal()) { + X = current_position; + W = o->w(); + Y = ty; + H = th; + } else { + X = tx; + W = tw; + Y = current_position; + H = o->h(); + } + if (spacing_ && current_position>maximum_position && + (X != o->x() || Y != o->y() || d&128)) { + fl_color(color()); + if (horizontal()) + fl_rectf(maximum_position, ty, spacing_, th); + else + fl_rectf(tx, maximum_position, tw, spacing_); + } + if (X != o->x() || Y != o->y() || W != o->w() || H != o->h()) { + o->resize(X,Y,W,H); + o->clear_damage(~0); + } + if (d&128) draw_child(*o); else update_child(*o); + // child's draw() can change it's size, so use new size: + current_position += (horizontal() ? o->w() : o->h()); + if (current_position > maximum_position) + maximum_position = current_position; + current_position += spacing_; + } + if (horizontal()) { + if (maximum_position < tx+tw) { + fl_color(color()); + fl_rectf(maximum_position, ty, tx+tw-maximum_position, th); + } + tw = maximum_position-tx; + } else { + if (maximum_position < ty+th) { + fl_color(color()); + fl_rectf(tx, maximum_position, tw, ty+th-maximum_position); + } + th = maximum_position-ty; + } + tw += Fl::box_dw(box()); if (tw <= 0) tw = 1; + th += Fl::box_dh(box()); if (th <= 0) th = 1; + if (tw != w() || th != h()) {Fl_Widget::resize(x(),y(),tw,th); d = 128;} + if (d&128) draw_box(); +} diff --git a/src/Fl_Pixmap.cxx b/src/Fl_Pixmap.cxx new file mode 100644 index 000000000..3e4e1af11 --- /dev/null +++ b/src/Fl_Pixmap.cxx @@ -0,0 +1,113 @@ +// Fl_Pixmap.C + +// Draws X pixmap data, keeping it stashed in a server pixmap so it +// redraws fast. + +// See fl_draw_pixmap.C for code used to get the actual data into pixmap. +// Implemented without using the xpm library (which I can't use because +// it interferes with the color cube used by fl_draw_image). + +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <FL/x.H> +#include <FL/Fl_Widget.H> +#include <FL/Fl_Menu_Item.H> +#include <FL/Fl_Pixmap.H> + +extern uchar **fl_mask_bitmap; // used by fl_draw_pixmap.C to store mask +void fl_restore_clip(); // in fl_rect.C + +void Fl_Pixmap::draw(int X, int Y, int W, int H, int cx,int cy) { + if (w<0) fl_measure_pixmap(data, w, h); + if (!w) return; + // clip the box down to the size of image, quit if empty: + if (cx < 0) {W += cx; X -= cx; cx = 0;} + if (cx+W > w) W = w-cx; + if (W <= 0) return; + if (cy < 0) {H += cy; Y -= cy; cy = 0;} + if (cy+H > h) H = h-cy; + if (H <= 0) return; + if (!id) { + id = (ulong)fl_create_offscreen(w, h); + fl_begin_offscreen((Fl_Offscreen)id); +#ifdef WIN32 // mask is nyi, instead use a constant color + fl_draw_pixmap(data, 0, 0, (Fl_Color)mask); +#else + uchar *bitmap = 0; + fl_mask_bitmap = &bitmap; + fl_draw_pixmap(data, 0, 0, FL_BLACK); + fl_mask_bitmap = 0; + if (bitmap) { + mask = XCreateBitmapFromData(fl_display, fl_window, + (const char*)bitmap, (w+7)&-8, h); + delete[] bitmap; + } +#endif + fl_end_offscreen(); + } +#ifndef WIN32 + if (mask) { + // I can't figure out how to combine a mask with existing region, + // so cut the image down to a clipped rectangle: + int nx, ny; fl_clip_box(X,Y,W,H,nx,ny,W,H); + cx += nx-X; X = nx; + cy += ny-Y; Y = ny; + // make X use the bitmap as a mask: + XSetClipMask(fl_display, fl_gc, mask); + int ox = X-cx; if (ox < 0) ox += w; + int oy = Y-cy; if (oy < 0) oy += h; + XSetClipOrigin(fl_display, fl_gc, X-cx, Y-cy); + } +#endif + fl_copy_offscreen(X, Y, W, H, (Fl_Offscreen)id, cx, cy); +#ifndef WIN32 + if (mask) { + // put the old clip region back + XSetClipOrigin(fl_display, fl_gc, 0, 0); + fl_restore_clip(); + } +#endif +} + +Fl_Pixmap::~Fl_Pixmap() { + if (id) fl_delete_offscreen((Fl_Offscreen)id); +#ifndef WIN32 + if (mask) fl_delete_offscreen((Fl_Offscreen)mask); +#endif +} + +static void pixmap_labeltype( + const Fl_Label* o, int x, int y, int w, int h, Fl_Align a) +{ + Fl_Pixmap* b = (Fl_Pixmap*)(o->value); + if (b->w<0) fl_measure_pixmap(b->data, b->w, b->h); + int cx; + if (a & FL_ALIGN_LEFT) cx = 0; + else if (a & FL_ALIGN_RIGHT) cx = b->w-w; + else cx = (b->w-w)/2; + int cy; + if (a & FL_ALIGN_TOP) cy = 0; + else if (a & FL_ALIGN_BOTTOM) cy = b->h-h; + else cy = (b->h-h)/2; + b->draw(x,y,w,h,cx,cy); +} + +static void pixmap_measure(const Fl_Label* o, int& w, int& h) { + Fl_Pixmap* b = (Fl_Pixmap*)(o->value); + if (b->w<0) fl_measure_pixmap(b->data, b->w, b->h); + w = b->w; + h = b->h; +} + +void Fl_Pixmap::label(Fl_Widget* o) { +#ifdef WIN32 + mask = o->color(); +#endif + Fl::set_labeltype(_FL_PIXMAP_LABEL, pixmap_labeltype, pixmap_measure); + o->label(_FL_PIXMAP_LABEL, (const char*)this); +} + +void Fl_Pixmap::label(Fl_Menu_Item* o) { + Fl::set_labeltype(_FL_PIXMAP_LABEL, pixmap_labeltype, pixmap_measure); + o->label(_FL_PIXMAP_LABEL, (const char*)this); +} diff --git a/src/Fl_Positioner.cxx b/src/Fl_Positioner.cxx new file mode 100644 index 000000000..017a4ef4d --- /dev/null +++ b/src/Fl_Positioner.cxx @@ -0,0 +1,106 @@ +// Fl_Positioner.C + +// The positioner widget from Forms, gives 2D input +// Written by: Mark Overmars + +#include <FL/Fl.H> +#include <FL/Fl_Positioner.H> +#include <FL/fl_draw.H> + +static double flinear(double val, double smin, double smax, double gmin, double gmax) +{ + if (smin == smax) return gmax; + else return gmin + (gmax - gmin) * (val - smin) / (smax - smin); +} + +void Fl_Positioner::draw(int x, int y, int w, int h) { + int x1 = x + 4; + int y1 = y + 4; + int w1 = w - 2 * 4; + int h1 = h - 2 * 4; + int xx = int(flinear(xvalue(), xmin, xmax, x1, x1+w1-1)+.5); + int yy = int(flinear(yvalue(), ymin, ymax, y1, y1+h1-1)+.5); + draw_box(box(), x, y, w, h, color()); + fl_color(selection_color()); + fl_xyline(x1, yy, x1+w1); + fl_yxline(xx, y1, y1+h1); +} + +void Fl_Positioner::draw() { + draw(x(), y(), w(), h()); + draw_label(); +} + +int Fl_Positioner::value(double X, double Y) { + clear_changed(); + if (X == xvalue_ && Y == yvalue_) return 0; + xvalue_ = X; yvalue_ = Y; + redraw(); + return 1; +} + +int Fl_Positioner::xvalue(double X) { + return(value(X, yvalue_)); +} + +int Fl_Positioner::yvalue(double Y) { + return(value(xvalue_, Y)); +} + +int Fl_Positioner::handle(int event, int x, int y, int w, int h) { + switch (event) { + case FL_PUSH: + case FL_DRAG: + case FL_RELEASE: { + double x1 = x + 4; + double y1 = y + 4; + double w1 = w - 2 * 4; + double h1 = h - 2 * 4; + double X = flinear(Fl::event_x(), x1, x1+w1-1.0, xmin, xmax); + if (xstep_) X = int(X/xstep_+0.5) * xstep_; + if (X < xmin) X = xmin; + if (X > xmax) X = xmax; + double Y = flinear(Fl::event_y(), y1, y1+h1-1.0, ymin, ymax); + if (ystep_) Y = int(Y/ystep_+0.5) * ystep_; + if (Y < ymin) Y = ymin; + if (Y > ymax) Y = ymax; + if (value(X, Y)) set_changed();} + if (!(when() & FL_WHEN_CHANGED || + when() & FL_WHEN_RELEASE && event == FL_RELEASE)) return 1; + if (changed() || when()&FL_WHEN_NOT_CHANGED) { + clear_changed(); do_callback();} + return 1; + default: + return 0; + } +} + +int Fl_Positioner::handle(int e) { + return handle(e, x(), y(), w(), h()); +} + +Fl_Positioner::Fl_Positioner(int x, int y, int w, int h, const char* l) +: Fl_Widget(x, y, w, h, l) { + box(FL_DOWN_BOX); + selection_color(FL_RED); + align(FL_ALIGN_BOTTOM); + when(FL_WHEN_CHANGED); + xmin = ymin = 0; + xmax = ymax = 1; + xvalue_ = yvalue_ = .5; + xstep_ = ystep_ = 0; +} + +void Fl_Positioner::xbounds(double a, double b) { + if (a != xmin || b != xmax) { + xmin = a; xmax = b; + redraw(); + } +} + +void Fl_Positioner::ybounds(double a, double b) { + if (a != ymin || b != ymax) { + ymin = a; ymax = b; + redraw(); + } +} diff --git a/src/Fl_Repeat_Button.cxx b/src/Fl_Repeat_Button.cxx new file mode 100644 index 000000000..e13e05c44 --- /dev/null +++ b/src/Fl_Repeat_Button.cxx @@ -0,0 +1,36 @@ +// Fl_Repeat_Button.C + +#include <FL/Fl.H> +#include <FL/Fl_Repeat_Button.H> + +#define INITIALREPEAT .5 +#define REPEAT .1 + +void Fl_Repeat_Button::repeat_callback(void *v) { + Fl_Button *b = (Fl_Button*)v; + Fl::add_timeout(REPEAT,repeat_callback,b); + b->do_callback(); +} + +int Fl_Repeat_Button::handle(int event) { + int newval; + switch (event) { + case FL_RELEASE: + newval = 0; goto J1; + case FL_PUSH: + case FL_DRAG: + newval = Fl::event_inside(this); + J1: + if (value(newval)) { + if (newval) { + Fl::add_timeout(INITIALREPEAT,repeat_callback,this); + do_callback(); + } else { + Fl::remove_timeout(repeat_callback,this); + } + } + return 1; + default: + return Fl_Button::handle(event); + } +} diff --git a/src/Fl_Return_Button.cxx b/src/Fl_Return_Button.cxx new file mode 100644 index 000000000..6d2e224ff --- /dev/null +++ b/src/Fl_Return_Button.cxx @@ -0,0 +1,42 @@ +// Fl_Return_Button.C + +#include <FL/Fl.H> +#include <FL/Fl_Return_Button.H> +#include <FL/fl_draw.H> + +int fl_return_arrow(int x, int y, int w, int h) { + int size = w; if (h<size) size = h; + int d = (size+2)/4; if (d<3) d = 3; + int t = (size+9)/12; if (t<1) t = 1; + int x0 = x+(w-2*d-2*t-1)/2; + int x1 = x0+d; + int y0 = y+h/2; + fl_color(FL_LIGHT3); + fl_line(x0, y0, x1, y0+d); + fl_yxline(x1, y0+d, y0+t, x1+d+2*t, y0-d); + fl_yxline(x1, y0-t, y0-d); + fl_color(fl_gray_ramp(0)); + fl_line(x0, y0, x1+1, y0-d-1); + fl_color(FL_DARK3); + fl_xyline(x1+1, y0-t, x1+d, y0-d, x1+d+2*t); + return 1; +} + +void Fl_Return_Button::draw() { + if (type() == FL_HIDDEN_BUTTON) return; + draw_box(value() ? (down_box()?down_box():down(box())) : box(), + value() ? selection_color() : color()); + int W = h(); + if (w()/3 < W) W = w()/3; + fl_return_arrow(x()+w()-W-4, y(), W, h()); + draw_label(x(), y(), w()-W+4, h()); +} + +int Fl_Return_Button::handle(int event) { + if (event == FL_SHORTCUT && + (Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter)) { + do_callback(); + return 1; + } else + return Fl_Button::handle(event); +} diff --git a/src/Fl_Roller.cxx b/src/Fl_Roller.cxx new file mode 100644 index 000000000..5e175385f --- /dev/null +++ b/src/Fl_Roller.cxx @@ -0,0 +1,111 @@ +// Fl_Roller.C + +// Rapid-App style knob + +#include <FL/Fl.H> +#include <FL/Fl_Roller.H> +#include <FL/fl_draw.H> +#include <math.h> + +int Fl_Roller::handle(int event) { + static int ipos; + int newpos = horizontal() ? Fl::event_x() : Fl::event_y(); + switch (event) { + case FL_PUSH: + handle_push(); + ipos = newpos; + return 1; + case FL_DRAG: + handle_drag(clamp(round(increment(previous_value(),newpos-ipos)))); + return 1; + case FL_RELEASE: + handle_release(); + return 1; + default: + return 0; + } +} + +void Fl_Roller::draw() { + if (damage()&128) draw_box(); + int X = x()+Fl::box_dx(box()); + int Y = y()+Fl::box_dy(box()); + int W = w()-Fl::box_dw(box())-1; + int H = h()-Fl::box_dh(box())-1; + int offset = step() ? int(value()/step()) : 0; + const double ARC = 1.5; // 1/2 the number of radians visible + const double delta = .2; // radians per knurl + if (horizontal()) { // horizontal one + // draw shaded ends of wheel: + int h1 = W/4+1; // distance from end that shading starts + fl_color(color()); fl_rectf(X+h1,Y,W-2*h1,H); + for (int i=0; h1; i++) { + fl_color((Fl_Color)(FL_GRAY-i-1)); + int h2 = FL_GRAY-i-1 > FL_DARK3 ? 2*h1/3+1 : 0; + fl_rectf(X+h2,Y,h1-h2,H); + fl_rectf(X+W-h1,Y,h1-h2,H); + h1 = h2; + } + // draw ridges: + double junk; + for (double y = -ARC+modf(offset*sin(ARC)/(W/2)/delta,&junk)*delta;; + y += delta) { + int y1 = int((sin(y)/sin(ARC)+1)*W/2); + if (y1 <= 0) continue; else if (y1 >= W-1) break; + fl_color(FL_DARK3); fl_yxline(X+y1,Y+1,Y+H-1); + if (y < 0) y1--; else y1++; + fl_color(FL_LIGHT1);fl_yxline(X+y1,Y+1,Y+H-1); + } + // draw edges: + h1 = W/8+1; // distance from end the color inverts + fl_color(FL_DARK2); + fl_xyline(X+h1,Y+H-1,X+W-h1); + fl_color(FL_DARK3); + fl_yxline(X,Y+H,Y,X+h1); + fl_xyline(X+W-h1,Y,X+W); + fl_color(FL_LIGHT2); + fl_xyline(X+h1,Y-1,X+W-h1); + fl_yxline(X+W,Y,Y+H,X+W-h1); + fl_xyline(X+h1,Y+H,X); + } else { // vertical one + // draw shaded ends of wheel: + int h1 = H/4+1; // distance from end that shading starts + fl_color(color()); fl_rectf(X,Y+h1,W,H-2*h1); + for (int i=0; h1; i++) { + fl_color((Fl_Color)(FL_GRAY-i-1)); + int h2 = FL_GRAY-i-1 > FL_DARK3 ? 2*h1/3+1 : 0; + fl_rectf(X,Y+h2,W,h1-h2); + fl_rectf(X,Y+H-h1,W,h1-h2); + h1 = h2; + } + // draw ridges: + double junk; + for (double y = -ARC+modf(offset*sin(ARC)/(H/2)/delta,&junk)*delta; + ; y += delta) { + int y1 = int((sin(y)/sin(ARC)+1)*H/2); + if (y1 <= 0) continue; else if (y1 >= H-1) break; + fl_color(FL_DARK3); fl_xyline(X+1,Y+y1,X+W-1); + if (y < 0) y1--; else y1++; + fl_color(FL_LIGHT1);fl_xyline(X+1,Y+y1,X+W-1); + } + // draw edges: + h1 = H/8+1; // distance from end the color inverts + fl_color(FL_DARK2); + fl_yxline(X+W-1,Y+h1,Y+H-h1); + fl_color(FL_DARK3); + fl_xyline(X+W,Y,X,Y+h1); + fl_yxline(X,Y+H-h1,Y+H); + fl_color(FL_LIGHT2); + fl_yxline(X,Y+h1,Y+H-h1); + fl_xyline(X,Y+H,X+W,Y+H-h1); + fl_yxline(X+W,Y+h1,Y); + } +} + +Fl_Roller::Fl_Roller(int X,int Y,int W,int H,const char* L) + : Fl_Valuator(X,Y,W,H,L) { + box(FL_UP_FRAME); + step(1,1000); +} + +// end of Fl_Roller.C diff --git a/src/Fl_Round_Button.cxx b/src/Fl_Round_Button.cxx new file mode 100644 index 000000000..34a3eab72 --- /dev/null +++ b/src/Fl_Round_Button.cxx @@ -0,0 +1,15 @@ +// Fl_Round_Button.C + +// A subclass of Fl_Button that always draws as a round circle. This +// circle is smaller than the widget size and can be surrounded by +// another box type, for compatability with Forms. + +#include <FL/Fl.H> +#include <FL/Fl_Round_Button.H> + +Fl_Round_Button::Fl_Round_Button(int x,int y,int w,int h, const char *l) +: Fl_Light_Button(x,y,w,h,l) { + box(FL_NO_BOX); + down_box(FL_ROUND_DOWN_BOX); + selection_color(FL_RED); +} diff --git a/src/Fl_Scroll.cxx b/src/Fl_Scroll.cxx new file mode 100644 index 000000000..b5cc49520 --- /dev/null +++ b/src/Fl_Scroll.cxx @@ -0,0 +1,210 @@ +// Fl_Scroll.C + +#include <FL/Fl.H> +#include <FL/Fl_Scroll.H> +#include <FL/fl_draw.H> + +// Insure the scrollbars are the last children: +void Fl_Scroll::fix_scrollbar_order() { + Fl_Widget*const* a = array(); + if (a[children()-1] != &scrollbar) { + Fl_Widget** a = (Fl_Widget**)array(); + int i,j; for (i = j = 0; j < children(); j++) + if (a[j] != &hscrollbar && a[j] != &scrollbar) a[i++] = a[j]; + a[i++] = &hscrollbar; + a[i++] = &scrollbar; + } +} + +void Fl_Scroll::draw_clip(void* v,int X, int Y, int W, int H) { + fl_clip(X,Y,W,H); + Fl_Scroll* s = (Fl_Scroll*)v; + // erase background if there is a boxtype: + if (s->box() && !(s->damage()&128)) { + fl_color(s->color()); + fl_rectf(X,Y,W,H); + } + Fl_Widget*const* a = s->array(); + int R = X; int B = Y; // track bottom & right edge of all children + for (int i=s->children()-2; i--;) { + Fl_Widget& o = **a++; + s->draw_child(o); + s->draw_outside_label(o); + if (o.x()+o.w() > R) R = o.x()+o.w(); + if (o.y()+o.h() > B) B = o.y()+o.h(); + } + // fill any area to right & bottom of widgets: + if (R < X+W && B > Y) { + fl_color(s->color()); + fl_rectf(R,Y,X+W-R,B-Y); + } + if (B < Y+H) { + fl_color(s->color()); + fl_rectf(X,B,W,Y+H-B); + } + fl_pop_clip(); +} + +void Fl_Scroll::bbox(int& X, int& Y, int& W, int& H) { + X = x()+Fl::box_dx(box()); + Y = y()+Fl::box_dy(box()); + W = w()-Fl::box_dw(box()); + H = h()-Fl::box_dh(box()); + if (scrollbar.visible()) { + W -= scrollbar.w(); + if (scrollbar.align() & FL_ALIGN_LEFT) X += scrollbar.w(); + } + if (hscrollbar.visible()) { + H -= hscrollbar.h(); + if (scrollbar.align() & FL_ALIGN_TOP) Y += hscrollbar.h(); + } +} + +void Fl_Scroll::draw() { + fix_scrollbar_order(); + int X,Y,W,H; bbox(X,Y,W,H); + + uchar d = damage(); + + if (d & 128) { // full redraw + draw_box(box(),x(),y(),w(),h(),color()); + draw_clip(this, X, Y, W, H); + } else { + if (d & 2) { // scroll the contents: + fl_scroll(X, Y, W, H, oldx-xposition_, oldy-yposition_, draw_clip, this); + } + if (d & 1) { // draw damaged children + fl_clip(X, Y, W, H); + Fl_Widget*const* a = array(); + for (int i=children()-2; i--;) update_child(**a++); + fl_pop_clip(); + } + } + + // accumulate bounding box of children: + int l = X; int r = X; int t = Y; int b = Y; + Fl_Widget*const* a = array(); + for (int i=children()-2; i--;) { + Fl_Object* o = *a++; + if (o->x() < l) l = o->x(); + if (o->y() < t) t = o->y(); + if (o->x()+o->w() > r) r = o->x()+o->w(); + if (o->y()+o->h() > b) b = o->y()+o->h(); + } + + // turn the scrollbars on and off as necessary: + for (int z = 0; z<2; z++) { + if ((type()&VERTICAL) && (type()&ALWAYS_ON || t < Y || b > Y+H)) { + if (!scrollbar.visible()) { + scrollbar.set_visible(); + W -= scrollbar.w(); + d = 128; + } + } else { + if (scrollbar.visible()) { + scrollbar.clear_visible(); + draw_clip(this, + scrollbar.align()&FL_ALIGN_LEFT ? X-scrollbar.w() : X+W, + Y, scrollbar.w(), H); + W += scrollbar.w(); + d = 128; + } + } + if ((type()&HORIZONTAL) && (type()&ALWAYS_ON || l < X || r > X+W)) { + if (!hscrollbar.visible()) { + hscrollbar.set_visible(); + H -= hscrollbar.h(); + d = 128; + } + } else { + if (hscrollbar.visible()) { + hscrollbar.clear_visible(); + draw_clip(this, X, + scrollbar.align()&FL_ALIGN_TOP ? Y-hscrollbar.h() : Y+H, + W, hscrollbar.h()); + H += hscrollbar.h(); + d = 128; + } + } + } + + scrollbar.resize(scrollbar.align()&FL_ALIGN_LEFT ? X-scrollbar.w() : X+W, + Y, scrollbar.w(), H); + scrollbar.value(oldy = yposition_ = Y, H, t, b-t); + + hscrollbar.resize(X, + scrollbar.align()&FL_ALIGN_TOP ? Y-hscrollbar.h() : Y+H, + W, hscrollbar.h()); + hscrollbar.value(oldx = xposition_ = X, W, l, r-l); + + // draw the scrollbars: + if (d & 128) { + draw_child(scrollbar); + draw_child(hscrollbar); + if (scrollbar.visible() && hscrollbar.visible()) { + // fill in the little box in the corner + fl_color(color()); + fl_rectf(scrollbar.x(), hscrollbar.y(), scrollbar.w(), hscrollbar.h()); + } + } else { + update_child(scrollbar); + update_child(hscrollbar); + } +} + +void Fl_Scroll::resize(int X, int Y, int W, int H) { + fix_scrollbar_order(); + // move all the children: + Fl_Widget*const* a = array(); + for (int i=children()-2; i--;) { + Fl_Object* o = *a++; + o->position(o->x()+X-x(), o->y()+Y-y()); + } + Fl_Widget::resize(X,Y,W,H); +} + +void Fl_Scroll::position(int X, int Y) { + int dx = xposition_-X; + int dy = yposition_-Y; + if (!dx && !dy) return; + xposition_ = X; + yposition_ = Y; + Fl_Widget*const* a = array(); + for (int i=children(); i--;) { + Fl_Widget* o = *a++; + if (o == &hscrollbar || o == &scrollbar) continue; + o->position(o->x()+dx, o->y()+dy); + } + damage(2); +} + +void Fl_Scroll::hscrollbar_cb(Fl_Widget* o, void*) { + Fl_Scroll* s = (Fl_Scroll*)(o->parent()); + s->position(int(((Fl_Scrollbar*)o)->value()), s->yposition()); +} + +void Fl_Scroll::scrollbar_cb(Fl_Widget* o, void*) { + Fl_Scroll* s = (Fl_Scroll*)(o->parent()); + s->position(s->xposition(), int(((Fl_Scrollbar*)o)->value())); +} + +#define SLIDER_WIDTH 17 + +Fl_Scroll::Fl_Scroll(int X,int Y,int W,int H,const char* L) + : Fl_Group(X,Y,W,H,L), + scrollbar(X+W-SLIDER_WIDTH,Y,SLIDER_WIDTH,H-SLIDER_WIDTH), + hscrollbar(X,Y+H-SLIDER_WIDTH,W-SLIDER_WIDTH,SLIDER_WIDTH) { + type(BOTH); + xposition_ = 0; + yposition_ = 0; + hscrollbar.type(FL_HORIZONTAL); + hscrollbar.callback(hscrollbar_cb); + scrollbar.callback(scrollbar_cb); +} + +int Fl_Scroll::handle(int event) { + fix_scrollbar_order(); + return Fl_Group::handle(event); +} + +// end of Fl_Scroll.C diff --git a/src/Fl_Scrollbar.cxx b/src/Fl_Scrollbar.cxx new file mode 100644 index 000000000..4863d9431 --- /dev/null +++ b/src/Fl_Scrollbar.cxx @@ -0,0 +1,158 @@ +// Fl_Scrollbar.C + +#include <FL/Fl.H> +#include <FL/Fl_Scrollbar.H> +#include <FL/fl_draw.H> +#include <math.h> + +#define INITIALREPEAT .5 +#define REPEAT .05 + +void Fl_Scrollbar::increment_cb() { + handle_drag(clamp(value() + ( + ((pushed_>1) == (maximum()>=minimum())) ? linesize_ : -linesize_))); +} + +void Fl_Scrollbar::timeout_cb(void* v) { + Fl_Scrollbar* s = (Fl_Scrollbar*)v; + s->increment_cb(); + Fl::add_timeout(REPEAT, timeout_cb, s); +} + +int Fl_Scrollbar::handle(int event) { + if (!pushed_) { + if (horizontal()) { + if (w() < 3*h()) return Fl_Slider::handle(event); + if (Fl_Slider::handle(event, x()+h(), y(), w()-2*h(), h())) return 1; + } else { + if (h() < 3*w()) return Fl_Slider::handle(event); + if (Fl_Slider::handle(event, x(), y()+w(), w(), h()-2*w())) return 1; + } + } + switch (event) { + case FL_RELEASE: + if (pushed_) { + Fl::remove_timeout(timeout_cb, this); + pushed_ = 0; + redraw(); + } + handle_release(); + return 1; + case FL_PUSH: + if (horizontal()) { + if (Fl::event_inside(x(), y(), h(), h())) pushed_ = 1; + if (Fl::event_inside(x()+w()-h(), y(), h(), h())) pushed_ = 2; + } else { + if (Fl::event_inside(x(), y(), w(), w())) pushed_ = 1; + if (Fl::event_inside(x(), y()+h()-w(), w(), w())) pushed_ = 2; + } + if (pushed_) { + handle_push(); + Fl::add_timeout(INITIALREPEAT, timeout_cb, this); + increment_cb(); + redraw(); + } + return 1; + case FL_DRAG: + return pushed_; + case FL_SHORTCUT: { + int v = value(); + int ls = maximum()>=minimum() ? linesize_ : -linesize_; + if (horizontal()) { + switch (Fl::event_key()) { + case FL_Left: + v -= ls; + break; + case FL_Right: + v += ls; + break; + default: + return 0; + } + } else { // vertical + switch (Fl::event_key()) { + case FL_Up: + v -= ls; + break; + case FL_Down: + v += ls; + break; + case FL_Page_Up: + if (slider_size() >= 1.0) return 0; + v -= int((maximum()-minimum())*slider_size()/(1.0-slider_size())); + v += ls; + break; + case FL_Page_Down: + if (slider_size() >= 1.0) return 0; + v += int((maximum()-minimum())*slider_size()/(1.0-slider_size())); + v -= ls; + break; + case FL_Home: + v = int(minimum()); + break; + case FL_End: + v = int(maximum()); + break; + default: + return 0; + } + } + v = int(clamp(v)); + if (v != value()) { + Fl_Slider::value(v); + value_damage(); + do_callback(); + } + return 1;} + } + return 0; +} + +void Fl_Scrollbar::draw() { + if (horizontal()) { + if (w() < 3*h()) {Fl_Slider::draw(); return;} + Fl_Slider::draw(x()+h(), y(), w()-2*h(), h()); + if (damage()&128) { + draw_box((pushed_&1) ? down(slider()) : slider(), + x(), y(), h(), h(), selection_color()); + draw_box((pushed_&2) ? down(slider()) : slider(), + x()+w()-h(), y(), h(), h(), selection_color()); + fl_color(labelcolor()); + int w1 = (h()-1)|1; // use odd sizes only + int Y = y()+w1/2; + int W = w1/3; + int X = x()+w1/2+W/2; + fl_polygon(X-W, Y, X, Y-W, X, Y+W); + X = x()+w()-(X-x())-1; + fl_polygon(X+W, Y, X, Y+W, X, Y-W); + } + } else { // vertical + if (h() < 3*w()) {Fl_Slider::draw(); return;} + Fl_Slider::draw(x(), y()+w(), w(), h()-2*w()); + if (damage()&128) { + draw_box((pushed_&1) ? down(slider()) : slider(), + x(), y(), w(), w(), selection_color()); + draw_box((pushed_&2) ? down(slider()) : slider(), + x(), y()+h()-w(), w(), w(), selection_color()); + fl_color(labelcolor()); + int w1 = (w()-1)|1; // use odd sizes only + int X = x()+w1/2; + int W = w1/3; + int Y = y()+w1/2+W/2; + fl_polygon(X, Y-W, X+W, Y, X-W, Y); + Y = y()+h()-(Y-y())-1; + fl_polygon(X, Y+W, X-W, Y, X+W, Y); + } + } +} + +Fl_Scrollbar::Fl_Scrollbar(int X, int Y, int W, int H, const char* L) + : Fl_Slider(X, Y, W, H, L) +{ + box(FL_FLAT_BOX); + color(FL_DARK2); + slider(FL_UP_BOX); + linesize_ = 16; + pushed_ = 0; + step(1); +} diff --git a/src/Fl_Single_Window.cxx b/src/Fl_Single_Window.cxx new file mode 100644 index 000000000..6bfe091a5 --- /dev/null +++ b/src/Fl_Single_Window.cxx @@ -0,0 +1,14 @@ +/* Fl_Single_Window.H + + A window with a single-buffered context + + This is provided for systems where the base class is double + buffered. You can turn it off using this subclass in case + your display looks better without it. + +*/ + +#include <FL/Fl_Single_Window.H> + +void Fl_Single_Window::show() {Fl_Window::show();} +void Fl_Single_Window::flush() {Fl_Window::flush();} diff --git a/src/Fl_Slider.cxx b/src/Fl_Slider.cxx new file mode 100644 index 000000000..6d062a608 --- /dev/null +++ b/src/Fl_Slider.cxx @@ -0,0 +1,200 @@ +// Fl_Slider.C + +#include <FL/Fl.H> +#include <FL/Fl_Slider.H> +#include <FL/fl_draw.H> +#include <math.h> + +void Fl_Slider::_Fl_Slider() { + slider_size_ = 0; + slider_ = 0; // FL_UP_BOX; +} + +Fl_Slider::Fl_Slider(int x, int y, int w, int h, const char* l) +: Fl_Valuator(x, y, w, h, l) { + box(FL_DOWN_BOX); + _Fl_Slider(); +} + +Fl_Slider::Fl_Slider(uchar t, int x, int y, int w, int h, const char* l) + : Fl_Valuator(x, y, w, h, l) { + type(t); + box(t==FL_HOR_NICE_SLIDER || t==FL_VERT_NICE_SLIDER ? + FL_FLAT_BOX : FL_DOWN_BOX); + _Fl_Slider(); +} + +void Fl_Slider::slider_size(double v) { + if (v < 0) v = 0; + if (v > 1) v = 1; + if (slider_size_ != float(v)) {slider_size_ = float(v); damage(2);} +} + +void Fl_Slider::bounds(double a, double b) { + if (minimum() != a || maximum() != b) {Fl_Valuator::bounds(a, b); damage(2);} +} + +int Fl_Slider::scrollvalue(int p, int w, int t, int l) { +// p = position, first line displayed +// w = window, number of lines displayed +// t = top, number of first line +// l = length, total number of lines + step(1, 1); + if (p+w > t+l) l = p+w-t; + slider_size(w >= l ? 1.0 : double(w)/double(l)); + bounds(t, l-w+t); + return value(p); +} + +// All slider interaction is done as though the slider ranges from +// zero to one, and the left (bottom) edge of the slider is at the +// given position. Since when the slider is all the way to the +// right (top) the left (bottom) edge is not all the way over, a +// position on the widget itself covers a wider range than 0-1, +// actually it ranges from 0 to 1/(1-size). + +void Fl_Slider::draw_bg(int x, int y, int w, int h) { + draw_box(box(), x, y, w, h, color()); + int BW = Fl::box_dx(box()); + if (type() == FL_VERT_NICE_SLIDER) { + draw_box(FL_THIN_DOWN_BOX, x+w/2-2, y+BW, 4, h-2*BW, FL_BLACK); + } else if (type() == FL_HOR_NICE_SLIDER) { + draw_box(FL_THIN_DOWN_BOX, x+BW, y+h/2-2, w-2*BW, 4, FL_BLACK); + } +} + +void Fl_Slider::draw(int x, int y, int w, int h) { + double val; + + if (minimum() == maximum()) + val = 0.5; + else { + val = (value()-minimum())/(maximum()-minimum()); + if (val > 1.0) val = 1.0; + else if (val < 0.0) val = 0.0; + } + + int BW = Fl::box_dx(box()); + int W = (horizontal() ? w : h) - 2*BW; + int X, S; + if (type()==FL_HOR_FILL_SLIDER || type() == FL_VERT_FILL_SLIDER) { + S = int(val*W+.5); + if (minimum()>maximum()) {S = W-S; X = h-BW-S;} + else X = BW; + } else { + S = int(slider_size_*W+.5); + int T = (horizontal() ? h : w)/2-BW+1; + if (type()==FL_VERT_NICE_SLIDER || type()==FL_HOR_NICE_SLIDER) T += 4; + if (S < T) S = T; + X = BW+int(val*(W-S)+.5); + } + int xsl, ysl, wsl, hsl; + if (horizontal()) { + xsl = x+X; + wsl = S; + ysl = y+BW; + hsl = h-2*BW; + } else { + ysl = y+X; + hsl = S; + xsl = x+BW; + wsl = w-2*BW; + } + + if (damage()&128) { // complete redraw + draw_bg(x, y, w, h); + } else { // partial redraw, clip off new position of slider + if (X > BW) { + if (horizontal()) fl_clip(x, ysl, X, hsl); + else fl_clip(xsl, y, wsl, X); + draw_bg(x, y, w, h); + fl_pop_clip(); + } + if (X+S < W+BW) { + if (horizontal()) fl_clip(xsl+wsl, ysl, x+w-BW-xsl-wsl, hsl); + else fl_clip(xsl, ysl+hsl, wsl, y+h-BW-ysl-hsl); + draw_bg(x, y, w, h); + fl_pop_clip(); + } + } + + Fl_Boxtype box1 = slider(); + if (!box1) {box1 = (Fl_Boxtype)(box()&-2); if (!box1) box1 = FL_UP_BOX;} + if (type() == FL_VERT_NICE_SLIDER) { + draw_box(box1, xsl, ysl, wsl, hsl, FL_GRAY); + int d = (hsl-4)/2; + draw_box(FL_THIN_DOWN_BOX, xsl+2, ysl+d, wsl-4, hsl-2*d,selection_color()); + } else if (type() == FL_HOR_NICE_SLIDER) { + draw_box(box1, xsl, ysl, wsl, hsl, FL_GRAY); + int d = (wsl-4)/2; + draw_box(FL_THIN_DOWN_BOX, xsl+d, ysl+2, wsl-2*d, hsl-4,selection_color()); + } else { + if (wsl>0 && hsl>0) draw_box(box1, xsl, ysl, wsl, hsl, selection_color()); + } + + draw_label(xsl, ysl, wsl, hsl); +} + +void Fl_Slider::draw() { + draw(x(), y(), w(), h()); +} + +int Fl_Slider::handle(int event, int x, int y, int w, int h) { + switch (event) { + case FL_PUSH: + if (!Fl::event_inside(x, y, w, h)) return 0; + handle_push(); + case FL_DRAG: { + if (slider_size() >= 1 || minimum()==maximum()) return 1; + int BW = Fl::box_dx(box()); + int W = (horizontal() ? w : h) - 2*BW; + int X = (horizontal() ? Fl::event_x()-x : Fl::event_y()-y) - BW; + int S = int(slider_size_*W+.5); + int T = (horizontal() ? h : w)/2-BW+1; + if (type()==FL_VERT_NICE_SLIDER || type()==FL_HOR_NICE_SLIDER) T += 4; + if (S < T) S = T; + double v = double(X)/(W-S); + double sliderwidth = double(S)/(W-S); + double val = (value()-minimum())/(maximum()-minimum()); + static double offcenter; + if (event == FL_PUSH) { + offcenter = v-val; + if (offcenter < 0) offcenter = 0; + else if (offcenter > sliderwidth) offcenter = sliderwidth; + else return 1; + } + TRY_AGAIN: + v -= offcenter; + if (v < 0) { + offcenter = v+offcenter; + if (offcenter<0) offcenter=0; + v = 0; + } else if (v > 1) { + offcenter = v+offcenter-1; + if (offcenter > sliderwidth) offcenter = sliderwidth; + v = 1; + } + // if (Fl::event_state(FL_SHIFT)) v = val+(v-val)*.05; + v = round(v*(maximum()-minimum())+minimum()); + // make sure a click outside the sliderbar moves it: + if (event == FL_PUSH && v == value()) { + offcenter = sliderwidth/2; + v = double(X)/(W-S); + event = FL_DRAG; + goto TRY_AGAIN; + } + handle_drag(clamp(v)); + } return 1; + case FL_RELEASE: + handle_release(); + return 1; + default: + return 0; + } +} + +int Fl_Slider::handle(int event) { + return handle(event, x(), y(), w(), h()); +} + +// End of Fl_Slider.C diff --git a/src/Fl_Tabs.cxx b/src/Fl_Tabs.cxx new file mode 100644 index 000000000..44e2d7ecf --- /dev/null +++ b/src/Fl_Tabs.cxx @@ -0,0 +1,234 @@ +// Fl_Tabs.C + +// This is the "file card tabs" interface to allow you to put lots and lots +// of buttons and switches in a panel, as popularized by many toolkits. + +// Each child widget is a card, and it's label() is printed on the card tab. +// Clicking the tab makes that card visible. + +#include <FL/Fl.H> +#include <FL/Fl_Tabs.H> +#include <FL/fl_draw.H> + +#define BORDER 10 +#define TABSLOPE 8 + +// return the left edges of each tab (plus a fake left edge for a tab +// past the right-hand one). These position are actually of the left +// edge of the slope. They are either seperated by the correct distance +// or by TABSLOPE or by zero. +// Return value is the index of the selected item. + +int Fl_Tabs::tab_positions(int* p, int* w) { + int selected = 0; + Fl_Widget*const* a = array(); + int i; + p[0] = 0; + for (i=0; i<children(); i++) { + Fl_Widget* o = *a++; + if (o == value_) selected = i; + if (o->label()) { + int wt = 0; int ht = 0; o->measure_label(wt,ht); + w[i] = wt+TABSLOPE; + } else + w[i] = 2*TABSLOPE; + p[i+1] = p[i]+w[i]; + } + int r = this->w()-TABSLOPE-1; + if (p[i] <= r) return selected; + // uh oh, they are too big: + // pack them against right edge: + p[i] = r; + for (i = children(); i--;) { + int l = r-w[i]; + if (p[i+1]-TABSLOPE < l) l = p[i+1]-TABSLOPE; + if (p[i] <= l) break; + p[i] = l; + r -= TABSLOPE; + } + // pack them against left edge and truncate width if they still don't fit: + for (i = 0; i<children(); i++) { + if (p[i] >= i*TABSLOPE) break; + p[i] = i*TABSLOPE; + int W = this->w()-1-TABSLOPE*(children()-i) - p[i]; + if (w[i] > W) w[i] = W; + } + // adjust edges according to visiblity: + for (i = children(); i > selected; i--) { + p[i] = p[i-1]+w[i-1]; + } + return selected; +} + +// return space needed for tabs. Negative to put them on the bottom: +int Fl_Tabs::tab_height() { + int H = h(); + int H2 = y(); + Fl_Widget*const* a = array(); + for (int i=children(); i--;) { + Fl_Widget* o = *a++; + if (o->y() < y()+H) H = o->y()-y(); + if (o->y()+o->h() > H2) H2 = o->y()+o->h(); + } + H2 = y()+h()-H2; + if (H2 > H) { + H = H2-Fl::box_dy(box()); + return (H <= 0) ? 0 : -H; + } else { + H = H-Fl::box_dy(box()); + return (H <= 0) ? 0 : H; + } +} + +// this is used by fluid to pick tabs: +Fl_Widget *Fl_Tabs::which(int event_x, int event_y) { + int H = tab_height(); + if (H < 0) { + if (event_y > y()+h() || event_y < y()+h()+H) return 0; + } else { + if (event_y > y()+H || event_y < y()) return 0; + } + if (event_x < x()) return 0; + int p[128], w[128]; + int selected = tab_positions(p, w); + int d = (event_y-(H>=0?y():y()+h()))*TABSLOPE/H; + for (int i=0; i<children(); i++) { + if (event_x < x()+p[i+1]+(i<selected ? TABSLOPE-d : d)) return child(i); + } + return 0; +} + +int Fl_Tabs::handle(int event) { + + Fl_Widget *o; + + switch (event) { + + case FL_PUSH: { + int H = tab_height(); + if (H >= 0) { + if (Fl::event_y() > y()+H) goto DEFAULT; + } else { + if (Fl::event_y() < y()+h()+H) goto DEFAULT; + }} + case FL_DRAG: + case FL_RELEASE: + o = which(Fl::event_x(), Fl::event_y()); + if (event == FL_RELEASE) {push(0); if (o) value(o);} + else push(o); + return 1; + + default: + DEFAULT: + value(); // initialize value & visibility if value_ == 0 + return Fl_Group::handle(event); + + } +} + +int Fl_Tabs::push(Fl_Widget *o) { + if (push_ == o) return 0; + if (push_ && push_ != value_ || o && o != value_) damage(2); + push_ = o; + return 1; +} + +Fl_Widget* Fl_Tabs::value() { + Fl_Widget *v = value_; + if (!v) { + // If value() has not been called, find first visible() child: + Fl_Widget*const* a = array(); + for (int i=children(); i--;) { + Fl_Widget* o = *a++; + if (v) o->hide(); + else if (o->visible()) v = o; + } + if (!v) return 0; // no children... + value_ = v; + } + return v; +} + +int Fl_Tabs::value(Fl_Widget *o) { + if (value_ == o) return 0; + if (o) o->show(); + if (value_) value_->hide(); + value_ = o; + redraw(); + do_callback(); + return 1; +} + +enum {LEFT, RIGHT, SELECTED}; + +void Fl_Tabs::draw() { + Fl_Widget *v = value(); + int H = tab_height(); + if (damage() & ~3) { // redraw the entire thing: + fl_clip(x(), y()+(H>=0?H:0), w(), h()-(H>=0?H:-H)); + draw_box(box(), x(), y(), w(), h(), v->color()); + fl_pop_clip(); + draw_child(*v); + } else { // redraw the child + update_child(*v); + } + if (damage() & 2) { + int p[128]; int w[128]; + int selected = tab_positions(p,w); + int i; + Fl_Widget*const* a = array(); + for (i=0; i<selected; i++) + draw_tab(x()+p[i], x()+p[i+1], w[i], H, a[i], LEFT); + for (i=children()-1; i > selected; i--) + draw_tab(x()+p[i], x()+p[i+1], w[i], H, a[i], RIGHT); + i = selected; + draw_tab(x()+p[i], x()+p[i+1], w[i], H, a[i], SELECTED); + } +} + +void Fl_Tabs::draw_tab(int x1, int x2, int W, int H, Fl_Widget* o, int what) { + if (x2 < x1+W) { + if (what == LEFT) { + if (x1+W < x2+TABSLOPE) x2 = x1+W; + else x2 += TABSLOPE; + } else { + if (x1+W < x2+TABSLOPE) x1 = x2-W; + else x1 -= TABSLOPE; + } + } + int sel = (what == SELECTED); + fl_color(o->color()); + if (H >= 0) { + fl_polygon(x1, y()+H+sel, x1+TABSLOPE, y(), x2, y(), + x2+TABSLOPE, y()+H+sel); + fl_color(!sel && o==push_ ? FL_DARK3 : FL_LIGHT3); + fl_line(x1, y()+H, x1+TABSLOPE, y(), x2, y()); + if (sel) { + if (x1>x()) fl_xyline(x(), y()+H, x1); + if (x2+TABSLOPE < x()+w()-1) fl_xyline(x2+TABSLOPE, y()+H, x()+w()-1); + } + fl_color(!sel && o==push_ ? FL_LIGHT3 : FL_DARK3); + fl_line(x2, y(), x2+TABSLOPE, y()+H); + } else { + fl_polygon(x1, y()+h()+H-sel, x1+TABSLOPE, y()+h(), x2, y()+h(), + x2+TABSLOPE, y()+h()+H-sel); + fl_color(!sel && o==push_ ? FL_LIGHT3 : FL_DARK3); + fl_line(x1+TABSLOPE, y()+h()-1, x2, y()+h()-1, x2+TABSLOPE, y()+h()+H); + if (sel) { + if (x1>x()) fl_xyline(x(), y()+h()+H, x1); + if (x2+TABSLOPE < x()+w()-1) fl_xyline(x2+TABSLOPE, y()+h()+H,x()+w()-1); + } + fl_color(!sel && o==push_ ? FL_DARK3 : FL_LIGHT3); + fl_line(x1, y()+h()+H, x1+TABSLOPE, y()+h()-1); + } + if (x2-x1 > 2*TABSLOPE) + o->draw_label(what==LEFT ? x1+TABSLOPE : x2-W+TABSLOPE, + y()+(H<0?h()+H-3:0), W-TABSLOPE, + (H<0?-H:H)+3, FL_ALIGN_CENTER); +} + +Fl_Tabs::Fl_Tabs(int X,int Y,int W, int H, const char *l) : + Fl_Group(X,Y,W,H,l) { + box(FL_THIN_UP_BOX); + value_ = push_ = 0; +} diff --git a/src/Fl_Tile.cxx b/src/Fl_Tile.cxx new file mode 100644 index 000000000..d422b844d --- /dev/null +++ b/src/Fl_Tile.cxx @@ -0,0 +1,173 @@ +// Fl_Tile.C - Group of 2 or 4 "tiles" that can be resized by dragging border + +// The size of the first child determines where the resize border is. +// The resizebox is used to limit where the border can be dragged to. + +#include <FL/Fl.H> +#include <FL/Fl_Tile.H> +#include <FL/Fl_Window.H> +#include <stdlib.h> + +// Drag the edges that were initially at oldx,oldy to newx,newy: +// pass zero as oldx or oldy to disable drag in that direction: + +void Fl_Tile::position(int oix, int oiy, int newx, int newy) { + Fl_Widget*const* a = array(); + short* p = sizes(); + p += 8; // skip group & resizable's saved size + for (int i=children(); i--;) { + Fl_Widget* o = *a++; + if (o == resizable()) continue; + int X = o->x(); + int R = X+o->w(); + if (oix) { + int t = p[0]; + if (t == oix || t>oix && X<newx || t<oix && X>newx) X = newx; + t = p[1]; + if (t == oix || t>oix && R<newx || t<oix && R>newx) R = newx; + } + int Y = o->y(); + int B = Y+o->h(); + if (oiy) { + int t = p[2]; + if (t == oiy || t>oiy && Y<newy || t<oiy && Y>newy) Y = newy; + t = p[3]; + if (t == oiy || t>oiy && B<newy || t<oiy && B>newy) B = newy; + } + o->damage_resize(X,Y,R-X,B-Y); + p += 4; + } +} + +// move the lower-right corner (sort of): +void Fl_Tile::resize(int X,int Y,int W,int H) { + // remember how much to move the child widgets: + int dx = X-x(); + int dy = Y-y(); + int dw = W-w(); + int dh = H-h(); + short* p = sizes(); + // resize this (skip the Fl_Group resize): + Fl_Widget::resize(X,Y,W,H); + // find bottom-right of resiable: + int OR = p[5]; + int NR = X+W-(p[1]-OR); + int OB = p[7]; + int NB = Y+H-(p[3]-OB); + // move everything to be on correct side of new resizable: + Fl_Widget*const* a = array(); + p += 8; + for (int i=children(); i--;) { + Fl_Widget* o = *a++; + int X = o->x()+dx; + int R = X+o->w(); + if (*p++ >= OR) X += dw; else if (X > NR) X = NR; + if (*p++ >= OR) R += dw; else if (R > NR) R = NR; + int Y = o->y()+dy; + int B = Y+o->h(); + if (*p++ >= OB) Y += dh; else if (Y > NB) Y = NB; + if (*p++ >= OB) B += dh; else if (B > NB) B = NB; + o->resize(X,Y,R-X,B-Y); o->redraw(); + } +} + +static void set_cursor(Fl_Tile*t, Fl_Cursor c) { + static Fl_Cursor cursor; + if (cursor == c) return; + cursor = c; +#ifdef __sgi + t->window()->cursor(c,FL_RED,FL_WHITE); +#else + t->window()->cursor(c); +#endif +} + +static Fl_Cursor cursors[4] = { + FL_CURSOR_DEFAULT, + FL_CURSOR_WE, + FL_CURSOR_NS, + FL_CURSOR_MOVE}; + +int Fl_Tile::handle(int event) { + static int sdrag; + static int sdx, sdy; + static int sx, sy; +#define DRAGH 1 +#define DRAGV 2 +#define GRABAREA 4 + + int mx = Fl::event_x(); + int my = Fl::event_y(); + + switch (event) { + + case FL_MOVE: + case FL_ENTER: + case FL_PUSH: { + int mindx = 100; + int mindy = 100; + int oldx = 0; + int oldy = 0; + Fl_Widget*const* a = array(); + short* q = sizes(); + short* p = q+8; + for (int i=children(); i--; p += 4) { + Fl_Widget* o = *a++; + if (o == resizable()) continue; + if (p[1]<q[1] && o->y()<=my+GRABAREA && o->y()+o->h()>=my-GRABAREA) { + int t = mx - (o->x()+o->w()); + if (abs(t) < mindx) { + sdx = t; + mindx = abs(t); + oldx = p[1]; + } + } + if (p[3]<q[3] && o->x()<=mx+GRABAREA && o->x()+o->w()>=mx-GRABAREA) { + int t = my - (o->y()+o->h()); + if (abs(t) < mindy) { + sdy = t; + mindy = abs(t); + oldy = p[3]; + } + } + } + sdrag = 0; sx = sy = 0; + if (mindx <= GRABAREA) {sdrag = DRAGH; sx = oldx;} + if (mindy <= GRABAREA) {sdrag |= DRAGV; sy = oldy;} + set_cursor(this, cursors[sdrag]); + if (sdrag) return 1; + return Fl_Group::handle(event); + } + + case FL_LEAVE: + set_cursor(this, FL_CURSOR_DEFAULT); + break; + + case FL_DRAG: + if (damage()) return 1; // don't fall behind + case FL_RELEASE: { + if (!sdrag) return 0; // should not happen + Fl_Widget* r = resizable(); if (!r) r = this; + int newx; + if (sdrag&DRAGH) { + newx = Fl::event_x()-sdx; + if (newx < r->x()) newx = r->x(); + else if (newx > r->x()+r->w()) newx = r->x()+r->w(); + } else + newx = sx; + int newy; + if (sdrag&DRAGV) { + newy = Fl::event_y()-sdy; + if (newy < r->y()) newy = r->y(); + else if (newy > r->y()+r->h()) newy = r->y()+r->h(); + } else + newy = sy; + position(sx,sy,newx,newy); + return 1;} + + } + + return Fl_Group::handle(event); +} + +// end of Fl_Tile.C diff --git a/src/Fl_Valuator.cxx b/src/Fl_Valuator.cxx new file mode 100644 index 000000000..a39513c37 --- /dev/null +++ b/src/Fl_Valuator.cxx @@ -0,0 +1,100 @@ +// Fl_Valuator.C + +// Base class for sliders and all other one-value "knobs" + +#include <FL/Fl.H> +#include <FL/Fl_Valuator.H> +#include <FL/math.h> +#include <stdio.h> + +Fl_Valuator::Fl_Valuator(int X, int Y, int W, int H, const char* L) + : Fl_Widget(X,Y,W,H,L) { + align(FL_ALIGN_BOTTOM); + when(FL_WHEN_CHANGED); + value_ = 0; + min = 0; + max = 1; + A = 0.0; + B = 1; +} + +const double epsilon = 1e-12 ; + +void Fl_Valuator::step(double s) { + if (s < 0) s = -s; + A = rint(s); + B = 1; + while (fabs(s-A/B) > epsilon) {B *= 10; A = rint(s*B);} +} + +void Fl_Valuator::precision(int p) { + A = 1.0; + for (B = 1; p--;) B *= 10; +} + +void Fl_Valuator::value_damage() {damage(2);} // by default do partial-redraw + +int Fl_Valuator::value(double v) { + clear_changed(); + if (v == value_) return 0; + value_ = v; + value_damage(); + return 1; +} + +double Fl_Valuator::softclamp(double v) { + int which = (min<=max); + double p = previous_value_; + if ((v<min)==which && p!=min && (p<min)!=which) return min; + else if ((v>max)==which && p!=max && (p>max)!=which) return max; + else return v; +} + +// inline void Fl_Valuator::handle_push() {previous_value_ = value_;} + +void Fl_Valuator::handle_drag(double v) { + if (v != value_) { + value_ = v; + value_damage(); + if (when() & FL_WHEN_CHANGED) do_callback(); + else set_changed(); + } +} + +void Fl_Valuator::handle_release() { + if (when()&FL_WHEN_RELEASE) { + // insure changed() is off even if no callback is done. It may have + // been turned on by the drag, and then the slider returned to it's + // initial position: + clear_changed(); + // now do the callback only if slider in new position or always is on: + if (value_ != previous_value_ || when() & FL_WHEN_NOT_CHANGED) + do_callback(); + } +} + +double Fl_Valuator::round(double v) { + if (A) return rint(v*B/A)*A/B; + else return v; +} + +double Fl_Valuator::clamp(double v) { + if ((v<min)==(min<=max)) return min; + else if ((v>max)==(min<=max)) return max; + else return v; +} + +double Fl_Valuator::increment(double v, int n) { + if (!A) return v+n*(max-min)/100; + if (min > max) n = -n; + return (rint(v*B/A)+n)*A/B; +} + +int Fl_Valuator::format(char* buffer) { + double v = value(); + if (!A || B==1) return sprintf(buffer, "%g", v); + int i, x; + for (x = 10, i = 2; x < B; x *= 10) i++; + if (x == B) i--; + return sprintf(buffer, "%.*f", i, v); +} diff --git a/src/Fl_Value_Input.cxx b/src/Fl_Value_Input.cxx new file mode 100644 index 000000000..c9cf28db1 --- /dev/null +++ b/src/Fl_Value_Input.cxx @@ -0,0 +1,100 @@ +// Fl_Value_Input.C + +// Fltk widget for drag-adjusting a floating point value. + +// Warning: this works by making a child Fl_Input object, even +// though this object is *not* an Fl_Group. May be a kludge? + +#include <FL/Fl.H> +#include <FL/Fl_Value_Input.H> +#include <FL/Fl_Group.H> +#include <stdlib.h> + +void Fl_Value_Input::input_cb(Fl_Widget*, void* v) { + Fl_Value_Input& t = *(Fl_Value_Input*)v; + double nv; + if (t.step()>=1.0) nv = strtol(t.input.value(), 0, 0); + else nv = strtod(t.input.value(), 0); + t.handle_push(); + t.handle_drag(nv); + t.handle_release(); +} + +void Fl_Value_Input::draw() { + if (damage()&~1) input.clear_damage(~0); + input.box(box()); + input.color(color(), selection_color()); + input.draw(); + input.clear_damage(); +} + +void Fl_Value_Input::resize(int X, int Y, int W, int H) { + Fl_Valuator::resize(X, Y, W, H); + input.resize(X, Y, W, H); +} + +void Fl_Value_Input::value_damage() { + char buf[128]; + format(buf); + input.value(buf); + input.mark(input.position()); // turn off selection highlight +} + +int Fl_Value_Input::handle(int event) { + double v; + int delta; + int mx = Fl::event_x(); + static int ix, drag; + switch (event) { + case FL_PUSH: + if (!step()) goto DEFAULT; + ix = mx; + drag = Fl::event_button(); + handle_push(); + return 1; + case FL_DRAG: + if (!step()) goto DEFAULT; + delta = Fl::event_x()-ix; + if (delta > 5) delta -= 5; + else if (delta < -5) delta += 5; + else delta = 0; + switch (drag) { + case 3: v = increment(previous_value(), delta*100); break; + case 2: v = increment(previous_value(), delta*10); break; + default:v = increment(previous_value(), delta); break; + } + v = round(v); + handle_drag(soft()?softclamp(v):clamp(v));; + return 1; + case FL_RELEASE: + if (!step()) goto DEFAULT; + if (value() != previous_value() || !Fl::event_is_click()) + handle_release(); + else { + input.handle(FL_PUSH); + input.handle(FL_RELEASE); + } + return 1; + case FL_FOCUS: + return input.take_focus(); + default: + DEFAULT: + input.type(step()>=1.0 ? FL_INT_INPUT : FL_FLOAT_INPUT); + return input.handle(event); + } +} + +Fl_Value_Input::Fl_Value_Input(int x, int y, int w, int h, const char* l) +: Fl_Valuator(x, y, w, h, l), input(x, y, w, h, 0) { + soft_ = 0; + if (input.parent()) // defeat automatic-add + ((Fl_Group*)input.parent())->remove(input); + input.parent(this); // kludge! + input.callback(input_cb, this); + input.when((Fl_When)(FL_WHEN_RELEASE|FL_WHEN_ENTER_KEY)); + box(input.box()); + color(input.color()); + selection_color(input.selection_color()); + align(FL_ALIGN_LEFT); + value_damage(); +} diff --git a/src/Fl_Value_Output.cxx b/src/Fl_Value_Output.cxx new file mode 100644 index 000000000..8c3f26bcd --- /dev/null +++ b/src/Fl_Value_Output.cxx @@ -0,0 +1,73 @@ +// Fl_Value_Output.C + +// Fltk widget for drag-adjusting a floating point value. +// This is much lighter than Fl_Value_Input because it has no text editor +// If step() is zero then it can be used to display a floating-point value + +#include <FL/Fl.H> +#include <FL/Fl_Value_Output.H> +#include <FL/fl_draw.H> +#include <FL/Fl_Input.H> // for default_font + +void Fl_Value_Output::draw() { + Fl_Boxtype b = box() ? box() : Fl_Input::default_box(); + int X = x()+Fl::box_dx(b); + int Y = y()+Fl::box_dy(b); + int W = w()-Fl::box_dw(b); + int H = h()-Fl::box_dh(b); + if (damage()&~1) + draw_box(b, color()); + else { + fl_color(color()); + fl_rectf(X, Y, W, H); + } + char buf[128]; + format(buf); + fl_color(active_r() ? textcolor() : inactive(textcolor())); + fl_font(textfont(), textsize(), + Fl_Input::default_font(), Fl_Input::default_size()); + fl_draw(buf,X+3,Y,W-6,H,FL_ALIGN_LEFT); +} + +int Fl_Value_Output::handle(int event) { + if (!step()) return 0; + double v; + int delta; + int mx = Fl::event_x(); + static int ix, drag; + switch (event) { + case FL_PUSH: + ix = mx; + drag = Fl::event_button(); + handle_push(); + return 1; + case FL_DRAG: + delta = Fl::event_x()-ix; + if (delta > 5) delta -= 5; + else if (delta < -5) delta += 5; + else delta = 0; + switch (drag) { + case 3: v = increment(previous_value(),delta*100); break; + case 2: v = increment(previous_value(),delta*10); break; + default:v = increment(previous_value(),delta); break; + } + v = round(v); + handle_drag(soft()?softclamp(v):clamp(v));; + return 1; + case FL_RELEASE: + handle_release(); + return 1; + default: + return 0; + } +} + +Fl_Value_Output::Fl_Value_Output(int x,int y,int w,int h,const char *l) +: Fl_Valuator(x,y,w,h,l) { + box(FL_NO_BOX); + align(FL_ALIGN_LEFT); + textfont_ = FL_HELVETICA; + textsize_ = FL_NORMAL_SIZE; + textcolor_ = FL_BLACK; + soft_ = 0; +} diff --git a/src/Fl_Value_Slider.cxx b/src/Fl_Value_Slider.cxx new file mode 100644 index 000000000..ade51106d --- /dev/null +++ b/src/Fl_Value_Slider.cxx @@ -0,0 +1,45 @@ +/* Fl_Value_Slider.C */ + +#include <FL/Fl.H> +#include <FL/Fl_Value_Slider.H> +#include <FL/fl_draw.H> +#include <math.h> +#include <FL/Fl_Input_.H> // for default_font + +Fl_Value_Slider::Fl_Value_Slider(int x,int y,int w,int h, const char*l) +: Fl_Slider(x,y,w,h,l) { + step(1,100); + textfont_ = FL_HELVETICA; + textsize_ = 10; + textcolor_ = FL_BLACK; +} + +void Fl_Value_Slider::draw() { + int sxx = x(), syy = y(), sww = w(), shh = h(); + int bxx = x(), byy = y(), bww = w(), bhh = h(); + if (horizontal()) { + bww = 35; sxx += 35; sww -= 35; + } else { + syy += 25; bhh = 25; shh -= 25; + } + Fl_Slider::draw(sxx,syy,sww,shh); + draw_box(box(),bxx,byy,bww,bhh,color()); + char buf[128]; + format(buf); + fl_font(textfont(), textsize(), + Fl_Input_::default_font(), Fl_Input_::default_size()); + fl_color(active_r() ? textcolor() : inactive(textcolor())); + fl_draw(buf, bxx, byy, bww, bhh, FL_ALIGN_CLIP); +} + +int Fl_Value_Slider::handle(int event) { + int sxx = x(), syy = y(), sww = w(), shh = h(); + if (horizontal()) { + sxx += 35; sww -= 35; + } else { + syy += 25; shh -= 25; + } + return Fl_Slider::handle(event,sxx,syy,sww,shh); +} + +// End of Fl_Value_Slider.C diff --git a/src/Fl_Widget.cxx b/src/Fl_Widget.cxx new file mode 100644 index 000000000..65fda468c --- /dev/null +++ b/src/Fl_Widget.cxx @@ -0,0 +1,164 @@ +// Fl_Widget.C + +// fltk (Fast Light Tool Kit) version 0.99 +// Copyright (C) 1998 Bill Spitzak + +#include <FL/Fl.H> +#include <FL/Fl_Widget.H> +#include <FL/Fl_Group.H> + +//////////////////////////////////////////////////////////////// +// for compatability with Forms, all widgets without callbacks are +// inserted into a "queue" when they are activated, and the forms +// compatability interaction functions (fl_do_events, etc) will +// read one widget at a time from this queue and return it: + +const int QUEUE_SIZE = 20; + +static Fl_Widget *obj_queue[QUEUE_SIZE]; +static int obj_head, obj_tail; + +void Fl_Widget::default_callback(Fl_Widget *o, void * /*v*/) { +#if 0 + // This is necessary for strict forms compatability but is confusing. + // Use the parent's callback if this widget does not have one. + for (Fl_Widget *p = o->parent(); p; p = p->parent()) + if (p->callback() != default_callback) { + p->do_callback(o,v); + return; + } +#endif + obj_queue[obj_head++] = o; + if (obj_head >= QUEUE_SIZE) obj_head = 0; + if (obj_head == obj_tail) { + obj_tail++; + if (obj_tail >= QUEUE_SIZE) obj_tail = 0; + } +} + +Fl_Widget *Fl::readqueue() { + if (obj_tail==obj_head) return 0; + Fl_Widget *o = obj_queue[obj_tail++]; + if (obj_tail >= QUEUE_SIZE) obj_tail = 0; + return o; +} + +//////////////////////////////////////////////////////////////// + +int Fl_Widget::handle(int) {return 0;} + +Fl_Widget::Fl_Widget(int X, int Y, int W, int H, const char* L) { + + x_ = X; y_ = Y; w_ = W; h_ = H; + + label_.value = L; + label_.type = FL_NORMAL_LABEL; + label_.font = FL_HELVETICA; + label_.size = FL_NORMAL_SIZE; + label_.color = FL_BLACK; + callback_ = default_callback; + user_data_ = 0; + type_ = 0; + flags_ = 0; + damage_ = 0; + box_ = FL_NO_BOX; + color_ = FL_GRAY; + color2_ = FL_GRAY; + align_ = FL_ALIGN_CENTER; + when_ = FL_WHEN_RELEASE; + + parent_ = 0; + if (Fl_Group::current()) Fl_Group::current()->add(this); +} + +void Fl_Widget::resize(int X, int Y, int W, int H) { + x_ = X; y_ = Y; w_ = W; h_ = H; +} + +// this is useful for parent widgets to call to resize children: +int Fl_Widget::damage_resize(int X, int Y, int W, int H) { + if (x() == X && y() == Y && w() == W && h() == H) return 0; + resize(X, Y, W, H); + redraw(); + return 1; +} + +int Fl_Widget::take_focus() { + if (!takesevents()) return 0; + if (!handle(FL_FOCUS)) return 0; // see if it wants it + if (contains(Fl::focus())) return 1; // it called Fl::focus for us + Fl::focus(this); + return 1; +} + +extern void fl_throw_focus(Fl_Widget*); // in Fl_x.C + +// Destruction does not remove from any parent group! And groups when +// destroyed destroy all their children. This is convienent and fast. +// However, it is only legal to destroy a "root" such as an Fl_Window, +// and automatic destructors may be called. +Fl_Widget::~Fl_Widget() { + parent_ = 0; // kludge to prevent ~Fl_Group from destroying again + fl_throw_focus(this); +} + +// redraw this, plus redraw opaque object if there is an outside label +static void redraw_label(Fl_Widget* w) { + w->redraw(); + if (w->label() && (w->align()&15) && !(w->align() & FL_ALIGN_INSIDE)) { + for (Fl_Widget *p = w->parent(); p; p = p->parent()) + if (p->box() || !p->parent()) {p->redraw(); break;} + } +} + +void Fl_Widget::activate() { + if (active()) return; + clear_flag(INACTIVE); + handle(FL_ACTIVATE); + if (inside(Fl::focus())) Fl::focus()->take_focus(); + redraw_label(this); +} + +void Fl_Widget::deactivate() { + if (!active()) return; + set_flag(INACTIVE); + handle(FL_DEACTIVATE); + fl_throw_focus(this); + redraw_label(this); +} + +int Fl_Widget::active_r() const { + for (const Fl_Widget* o = this; o; o = o->parent()) + if (!o->active()) return 0; + return 1; +} + +void Fl_Widget::show() { + if (visible()) return; + clear_flag(INVISIBLE); + handle(FL_SHOW); + if (inside(Fl::focus())) Fl::focus()->take_focus(); + redraw_label(this); +} + +void Fl_Widget::hide() { + if (!visible()) return; + set_flag(INVISIBLE); + handle(FL_HIDE); + fl_throw_focus(this); + for (Fl_Widget *p = parent(); p; p = p->parent()) + if (p->box() || !p->parent()) {p->redraw(); break;} +} + +int Fl_Widget::visible_r() const { + for (const Fl_Widget* o = this; o; o = o->parent()) + if (!o->visible()) return 0; + return 1; +} + +// return true if widget is inside (or equal to) this: +// Returns false for null widgets. +int Fl_Widget::contains(const Fl_Widget *o) const { + for (; o; o = o->parent_) if (o == this) return 1; + return 0; +} diff --git a/src/Fl_Window.cxx b/src/Fl_Window.cxx new file mode 100644 index 000000000..9c987df4e --- /dev/null +++ b/src/Fl_Window.cxx @@ -0,0 +1,83 @@ +// Fl_Window.C + +// fltk (Fast Light Tool Kit) version 0.99 +// Copyright (C) 1998 Bill Spitzak + +// The Fl_Window is a window in the fltk library. +// This is the system-independent portions. The huge amount of +// crap you need to do to communicate with X is in Fl_x.C, the +// equivalent (but totally different) crap for MSWindows is in Fl_win32.C + +#include <FL/Fl.H> +#include <FL/Fl_Window.H> + +void Fl_Window::_Fl_Window() { + type(FL_WINDOW); + box(FL_FLAT_BOX); + labeltype(FL_NO_LABEL); + i = 0; + xclass_ = 0; + iconlabel_ = 0; + resizable(0); + size_range_set = 0; + callback((Fl_Callback*)default_callback); +} + +Fl_Window::Fl_Window(int X,int Y,int W, int H, const char *l) +: Fl_Group(X, Y, W, H, l) { + _Fl_Window(); + set_flag(FL_FORCE_POSITION); +} + +Fl_Window::Fl_Window(int W, int H, const char *l) +// fix common user error of a missing end() with current(0): + : Fl_Group((Fl_Group::current(0),0), 0, W, H, l) { + _Fl_Window(); + clear_visible(); +} + +Fl_Window *Fl_Widget::window() const { + for (Fl_Widget *o = parent(); o; o = o->parent()) + if (o->type()>=FL_WINDOW) return (Fl_Window*)o; + return 0; +} + +int Fl_Window::x_root() const { + Fl_Window *p = window(); + if (p) return p->x_root() + x(); + return x(); +} + +int Fl_Window::y_root() const { + Fl_Window *p = window(); + if (p) return p->y_root() + y(); + return y(); +} + +void Fl_Window::draw() { + int savex = x(); x(0); + int savey = y(); y(0); + Fl_Group::draw(); + y(savey); + x(savex); +} + +void Fl_Window::label(const char *name) {label(name, iconlabel());} + +void Fl_Window::iconlabel(const char *iname) {label(label(), iname);} + +// the Fl::atclose pointer is provided for back compatability. You +// can now just change the callback for the window instead. + +void Fl::default_atclose(Fl_Window* window, void* v) { + window->hide(); + Fl_Widget::default_callback(window, v); // put on Fl::read_queue() +} + +void (*Fl::atclose)(Fl_Window*, void*) = default_atclose; + +void Fl_Window::default_callback(Fl_Window* window, void* v) { + Fl::atclose(window, v); +} + +// End of Fl_Window.C diff --git a/src/Fl_Window_fullscreen.cxx b/src/Fl_Window_fullscreen.cxx new file mode 100644 index 000000000..332a52cc4 --- /dev/null +++ b/src/Fl_Window_fullscreen.cxx @@ -0,0 +1,46 @@ +// Turning the border on/off by changing the motif_wm_hints property +// works on Irix 4DWM. Does not appear to work for any other window +// manager. Fullscreen still works on some window managers (fvwm is one) +// because they allow the border to be placed off-screen. + +// Unfortunatly most X window managers ignore changes to the border +// and refuse to position the border off-screen, so attempting to make +// the window full screen will lose the size of the border off the +// bottom and right. + +#include <FL/Fl.H> +#include <FL/x.H> + +void Fl_Window::border(int b) { + if (b) { + if (border()) return; + clear_flag(FL_NOBORDER); + } else { + if (!border()) return; + set_flag(FL_NOBORDER); + } +#ifdef WIN32 + // not yet implemented, but it's possible + // for full fullscreen we have to make the window topmost as well +#else + if (shown()) Fl_X::i(this)->sendxjunk(); +#endif +} + +void Fl_Window::fullscreen() { + border(0); + if (!x()) x(1); // force it to call XResizeWindow() + resize(0,0,Fl::w(),Fl::h()); +} + +void Fl_Window::fullscreen_off(int X,int Y,int W,int H) { +#ifdef WIN32 + border(1); + resize(X,Y,W,H); +#else + // this order produces less blinking on IRIX: + resize(X,Y,W,H); + border(1); +#endif +} + diff --git a/src/Fl_Window_hotspot.cxx b/src/Fl_Window_hotspot.cxx new file mode 100644 index 000000000..9a6f58a20 --- /dev/null +++ b/src/Fl_Window_hotspot.cxx @@ -0,0 +1,27 @@ +// Fl_Window_hotspot.C + +#include <FL/Fl.H> +#include <FL/Fl_Window.H> + +void Fl_Window::hotspot(int X, int Y, int offscreen) { + int mx,my; Fl::get_mouse(mx,my); + X = mx-X; Y = my-Y; + if (!offscreen) { + if (X < 0) X = 0; + if (X > Fl::w()-w()) X = Fl::w()-w(); + if (Y > Fl::h()-h()) Y = Fl::h()-h(); + if (Y < 0) Y = 0; + if (border() && Y < 20) Y = 20; + } + position(X,Y); +} + +void Fl_Window::hotspot(const Fl_Widget *o, int offscreen) { + int X = o->w()/2; + int Y = o->h()/2; + while (o != this) { + X += o->x(); Y += o->y(); + o = o->window(); + } + hotspot(X,Y,offscreen); +} diff --git a/src/Fl_Window_iconize.cxx b/src/Fl_Window_iconize.cxx new file mode 100644 index 000000000..a89080623 --- /dev/null +++ b/src/Fl_Window_iconize.cxx @@ -0,0 +1,19 @@ +// X-specific code that is not called by all programs, so it is put +// here in it's own source file to make programs smaller. + +#include <FL/x.H> + +extern char fl_show_iconic; // in Fl_x.C + +void Fl_Window::iconize() { + if (!shown()) { + fl_show_iconic = 1; + show(); + } else { +#ifdef WIN32 + ShowWindow(i->xid, SW_MINIMIZE); +#else + XIconifyWindow(fl_display, i->xid, fl_screen); +#endif + } +} diff --git a/src/Fl_XColor.H b/src/Fl_XColor.H new file mode 100644 index 000000000..0f2936d74 --- /dev/null +++ b/src/Fl_XColor.H @@ -0,0 +1,18 @@ +// X-specific structure for storing allocated colors. Used by fl_color.C, +// fl_draw_image.C, fl_set_color.C. + +#include <config.h> + +// one of these for each color in fltk's "colormap": +// if overlays are enabled, another one for the overlay +struct Fl_XColor { + unsigned char r,g,b; // actual color used by X + unsigned char mapped; // true when XAllocColor done + unsigned long pixel; // the X pixel to use +}; +extern Fl_XColor fl_xmap[/*overlay*/][256]; + +// mask & shifts to produce xcolor for truecolor visuals: +extern unsigned char fl_redmask, fl_greenmask, fl_bluemask; +extern int fl_redshift, fl_greenshift, fl_blueshift, fl_extrashift; + diff --git a/src/Fl_abort.cxx b/src/Fl_abort.cxx new file mode 100644 index 000000000..047af80a9 --- /dev/null +++ b/src/Fl_abort.cxx @@ -0,0 +1,51 @@ +// This method is in it's own source file so that stdlib and stdio +// do not need to be included in Fl.C: +// You can also override this by redefining all of these. + +#include <FL/Fl.H> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#ifndef WIN32 + +static void warning(const char *format, ...) { + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fputc('\n', stderr); + fflush(stderr); +} + +static void error(const char *format, ...) { + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fputc('\n', stderr); + fflush(stderr); +//abort(); // this produces a core dump, probably not desirable? + ::exit(1); +} + +#else + +// windows version is currently lame as vsprintf() is missing? + +#include <windows.h> + +static void warning(const char *format, ...) { + MessageBox(0,format,"Fltk warning",MB_ICONEXCLAMATION|MB_OK); +} + +static void error(const char *format, ...) { + MessageBox(0,format,"Fltk error",MB_ICONSTOP|MB_SYSTEMMODAL); + ::exit(1); +} + +#endif + +void (*Fl::warning)(const char* format, ...) = ::warning; +void (*Fl::error)(const char* format, ...) = ::error; +void (*Fl::fatal)(const char* format, ...) = ::error; diff --git a/src/Fl_add_idle.cxx b/src/Fl_add_idle.cxx new file mode 100644 index 000000000..3b0e127c2 --- /dev/null +++ b/src/Fl_add_idle.cxx @@ -0,0 +1,62 @@ +// Fl_add_idle.C + +// Allows you to manage an arbitrary set of idle() callbacks. +// Replaces the older set_idle() call (which is used to implement this) + +#include <FL/Fl.H> + +struct idle_cb { + void (*cb)(void*); + void* data; + idle_cb *next; +}; + +// the callbacks are stored linked in a ring. last points at the one +// just called, first at the next to call. last->next == first. + +static idle_cb* first; +static idle_cb* last; +static idle_cb* freelist; + +static void call_idle() { + idle_cb* p = first; + last = p; first = p->next; + p->cb(p->data); // this may call add_idle() or remove_idle()! +} + +void Fl::add_idle(void (*cb)(void*), void* data) { + idle_cb* p = freelist; + if (p) freelist = p->next; + else p = new idle_cb; + p->cb = cb; + p->data = data; + if (first) { + last->next = p; + p->next = first; + first = p; + } else { + first = last = p; + p->next = p; + set_idle(call_idle); + } +} + +void Fl::remove_idle(void (*cb)(void*), void* data) { + idle_cb* p = first; + if (!p) return; + idle_cb* l = last; + for (;; p = p->next) { + if (p->cb == cb && p->data == data) break; + if (p==last) return; // not found + l = p; + } + if (l == p) { // only one + first = last = 0; + set_idle(0); + } else { + last = l; + first = l->next = p->next; + } + p->next = freelist; + freelist = p; +} diff --git a/src/Fl_arg.cxx b/src/Fl_arg.cxx new file mode 100644 index 000000000..f15d4b5b7 --- /dev/null +++ b/src/Fl_arg.cxx @@ -0,0 +1,377 @@ +// Fl_arg.C + +// OPTIONAL initialization code for a program using fltk. +// You do not need to call this! Feel free to make up your own switches. + +#include <FL/Fl.H> +#include <FL/x.H> +#include <FL/Fl_Window.H> +#include <FL/filename.H> +#include <FL/fl_draw.H> +#include <ctype.h> +#include <string.h> + +#ifdef WIN32 +int XParseGeometry(const char*, int*, int*, unsigned int*, unsigned int*); +#define NoValue 0x0000 +#define XValue 0x0001 +#define YValue 0x0002 +#define WidthValue 0x0004 +#define HeightValue 0x0008 +#define AllValues 0x000F +#define XNegative 0x0010 +#define YNegative 0x0020 +#endif + +static int match(const char *a, const char *match, int atleast = 1) { + const char *b = match; + while (*a && (*a == *b || tolower(*a) == *b)) {a++; b++;} + return !*a && b >= match+atleast; +} + +// flags set by previously parsed arguments: +extern char fl_show_iconic; // in Fl_x.C +static char arg_called; +static char return_i; +static const char *name; +static const char *fg; +static const char *bg; +static const char *bg2; +static const char *geometry; +static const char *title; + +// consume a switch from argv. Returns number of words eaten, 0 on error: +int Fl::arg(int argc, char **argv, int &i) { + arg_called = 1; + const char *s = argv[i]; + + if (!s) {i++; return 1;} // something removed by calling program? + + // a word that does not start with '-', or a word after a '--', or + // the word '-' by itself all start the "non-switch arguments" to + // a program. Return 0 to indicate that we don't understand the + // word, but set a flag (return_i) so that args() will return at + // that point: + if (s[0] != '-' || !s[1]) {return_i = 1; return 0;} + if (s[1] == '-') { + if (!s[2]) {i++; return_i = 1; return 0;} + s++; // make "--word" work the same as "-word" for Gnu compatability + } + s++; // point after the dash + + if (match(s, "iconic")) { + fl_show_iconic = 1; + i++; + return 1; + } + + const char *v = argv[i+1]; + if (i >= argc-1 || !v) + return 0; // all the rest need an argument, so if missing it is an error + + if (match(s, "geometry")) { + + int flags, gx, gy; unsigned int gw, gh; + flags = XParseGeometry(v, &gx, &gy, &gw, &gh); + if (!flags) return 0; + geometry = v; + +#ifndef WIN32 + } else if (match(s, "display")) { + Fl::display(v); +#endif + + } else if (match(s, "title")) { + title = v; + + } else if (match(s, "name")) { + name = v; + + } else if (match(s, "bg2", 3) || match(s, "background2", 11)) { + bg2 = v; + + } else if (match(s, "bg") || match(s, "background")) { + bg = v; + + } else if (match(s, "fg") || match(s, "foreground")) { + fg = v; + + } else return 0; // unrecognized + + i += 2; + return 2; +} + +// consume all switches from argv. Returns number of words eaten. +// Returns zero on error. 'i' will either point at first word that +// does not start with '-', at the error word, or after a '--', or at +// argc. If your program does not take any word arguments you can +// report an error if i < argc. + +int Fl::args(int argc, char** argv, int& i, int (*cb)(int,char**,int&)) { + arg_called = 1; + i = 1; // skip argv[0] + while (i < argc) { + if (cb && cb(argc,argv,i)) continue; + if (!arg(argc,argv,i)) return return_i ? i : 0; + } + return i; +} + +#ifdef WIN32 +#include <stdio.h> +// simulation of XParseColor: +int fl_parse_color(const char* p, uchar& r, uchar& g, uchar& b) { + if (*p == '#') p++; + int n = strlen(p); + int m = n/3; + const char *pattern = 0; + switch(m) { + case 1: pattern = "%1x%1x%1x"; break; + case 2: pattern = "%2x%2x%2x"; break; + case 3: pattern = "%3x%3x%3x"; break; + case 4: pattern = "%4x%4x%4x"; break; + default: return 0; + } + int R,G,B; if (sscanf(p,pattern,&R,&G,&B) != 3) return 0; + r = R; g = G; b = B; + return 1; +} + +static void parsecolor(const char *name, void (*func)(uchar,uchar,uchar)) { + uchar r,g,b; + if (!name) return; + if (!fl_parse_color(name, r,g,b)) + Fl::error("Unknown color: %s", name); + else + func(r,g,b); +} + +#else + +static void parsecolor(const char *name, void (*func)(uchar,uchar,uchar)) { + XColor x; + if (!name) return; + if (!XParseColor(fl_display, fl_colormap, name, &x)) + Fl::error("Unknown color: %s", name); + else + func(x.red>>8, x.green>>8, x.blue>>8); +} + +#endif + +// show a main window, use any parsed arguments +void Fl_Window::show(int argc, char **argv) { + if (!arg_called) Fl::args(argc,argv); + + // set colors first, so background_pixel is correct: + static char beenhere; + if (!beenhere) { + beenhere = 1; + Fl::get_system_colors(); // opens display! May call Fl::fatal() + parsecolor(fg, Fl::foreground); + parsecolor(bg, Fl::background); + parsecolor(bg2,Fl::background2); + if (geometry) { + int flags = 0, gx = x(), gy = y(); unsigned int gw = w(), gh = h(); + flags = XParseGeometry(geometry, &gx, &gy, &gw, &gh); + if (flags & XNegative) gx = Fl::w()-w()+gx; + if (flags & YNegative) gy = Fl::h()-h()+gy; + // int mw,mh; minsize(mw,mh); + // if (mw > gw) gw = mw; + // if (mh > gh) gh = mh; + Fl_Widget *r = resizable(); + if (!r) resizable(this); + // for WIN32 we assumme window is not mapped yet: + if (flags & (XValue | YValue)) + x(-1), resize(gx,gy,gw,gh); + else + size(gw,gh); + resizable(r); + } + } + + if (name) {xclass(name); name = 0;} + else if (!xclass()) xclass(filename_name(argv[0])); + if (title) {label(title); title = 0;} + else if (!label()) label(xclass()); + show(); + +#ifndef WIN32 + // set the command string, used by state-saving window managers: + int i; + int n=0; for (i=0; i<argc; i++) n += strlen(argv[i])+1; +#ifdef __GNUC__ + char buffer[n]; +#else + char *buffer = new char[n]; +#endif + char *p = buffer; + for (i=0; i<argc; i++) for (const char *q = argv[i]; (*p++ = *q++);); + XChangeProperty(fl_display, fl_xid(this), XA_WM_COMMAND, XA_STRING, 8, 0, + (unsigned char *)buffer, p-buffer-1); +#ifndef __GNUC__ + delete[] buffer; +#endif +#endif + +} + +// Calls useful for simple demo programs, with automatic help message: + +static const char * const helpmsg = +"options are:\n" +" -display host:n.n\n" +" -geometry WxH+X+Y\n" +" -title windowtitle\n" +" -name classname\n" +" -iconic\n" +" -fg color\n" +" -bg color\n" +" -bg2 color"; + +const char * const Fl::help = helpmsg+13; + +void Fl::args(int argc, char **argv) { + int i; if (Fl::args(argc,argv,i) < argc) Fl::error(helpmsg); +} + +#ifdef WIN32 + +/* the following function was stolen from the X sources as indicated. */ + +/* Copyright Massachusetts Institute of Technology 1985, 1986, 1987 */ +/* $XConsortium: XParseGeom.c,v 11.18 91/02/21 17:23:05 rws Exp $ */ + +/* +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of M.I.T. not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. M.I.T. makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. +*/ + +/* + * XParseGeometry parses strings of the form + * "=<width>x<height>{+-}<xoffset>{+-}<yoffset>", where + * width, height, xoffset, and yoffset are unsigned integers. + * Example: "=80x24+300-49" + * The equal sign is optional. + * It returns a bitmask that indicates which of the four values + * were actually found in the string. For each value found, + * the corresponding argument is updated; for each value + * not found, the corresponding argument is left unchanged. + */ + +static int ReadInteger(char* string, char** NextString) +{ + register int Result = 0; + int Sign = 1; + + if (*string == '+') + string++; + else if (*string == '-') { + string++; + Sign = -1; + } + for (; (*string >= '0') && (*string <= '9'); string++) { + Result = (Result * 10) + (*string - '0'); + } + *NextString = string; + if (Sign >= 0) + return (Result); + else + return (-Result); +} + +int XParseGeometry(const char* string, int* x, int* y, + unsigned int* width, unsigned int* height) +{ + int mask = NoValue; + register char *strind; + unsigned int tempWidth, tempHeight; + int tempX, tempY; + char *nextCharacter; + + if ( (string == NULL) || (*string == '\0')) return(mask); + if (*string == '=') + string++; /* ignore possible '=' at beg of geometry spec */ + + strind = (char *)string; + if (*strind != '+' && *strind != '-' && *strind != 'x') { + tempWidth = ReadInteger(strind, &nextCharacter); + if (strind == nextCharacter) + return (0); + strind = nextCharacter; + mask |= WidthValue; + } + + if (*strind == 'x' || *strind == 'X') { + strind++; + tempHeight = ReadInteger(strind, &nextCharacter); + if (strind == nextCharacter) + return (0); + strind = nextCharacter; + mask |= HeightValue; + } + + if ((*strind == '+') || (*strind == '-')) { + if (*strind == '-') { + strind++; + tempX = -ReadInteger(strind, &nextCharacter); + if (strind == nextCharacter) + return (0); + strind = nextCharacter; + mask |= XNegative; + + } else { + strind++; + tempX = ReadInteger(strind, &nextCharacter); + if (strind == nextCharacter) + return(0); + strind = nextCharacter; + } + mask |= XValue; + if ((*strind == '+') || (*strind == '-')) { + if (*strind == '-') { + strind++; + tempY = -ReadInteger(strind, &nextCharacter); + if (strind == nextCharacter) + return(0); + strind = nextCharacter; + mask |= YNegative; + + } else { + strind++; + tempY = ReadInteger(strind, &nextCharacter); + if (strind == nextCharacter) + return(0); + strind = nextCharacter; + } + mask |= YValue; + } + } + + /* If strind isn't at the end of the string the it's an invalid + geometry specification. */ + + if (*strind != '\0') return (0); + + if (mask & XValue) + *x = tempX; + if (mask & YValue) + *y = tempY; + if (mask & WidthValue) + *width = tempWidth; + if (mask & HeightValue) + *height = tempHeight; + return (mask); +} + +#endif // ifdef WIN32 + +// end of Fl_Window_arg.C diff --git a/src/Fl_cutpaste.cxx b/src/Fl_cutpaste.cxx new file mode 100644 index 000000000..e7fcdd5e5 --- /dev/null +++ b/src/Fl_cutpaste.cxx @@ -0,0 +1,127 @@ +// Fl_cutpaste.C + +// Implementation of cut and paste. + +// This is seperated from Fl.C mostly to test Fl::add_handler(). +// But this will save a small amount of code size in a program that +// has no text editing fields or other things that call cut or paste. + +#ifdef WIN32 +#include "Fl_cutpaste_win32.C" +#else + +#include <FL/Fl.H> +#include <FL/x.H> +#include <FL/Fl_Window.H> +#include <string.h> + +static char *selection_buffer; +static int selection_length; +static int selection_buffer_length; +static char beenhere; + +extern Fl_Widget *fl_selection_requestor; // widget doing request_paste() + +static int selection_xevent_handler(int) { + + switch (fl_xevent->type) { + + case SelectionNotify: { + if (!fl_selection_requestor) return 0; + static char *pastebuffer; + if (pastebuffer) {XFree(pastebuffer); pastebuffer = 0;} + if (fl_xevent->xselection.property != 0) { + Atom a; int f; unsigned long n,b; + if (!XGetWindowProperty(fl_display, + fl_xevent->xselection.requestor, + fl_xevent->xselection.property, + 0,100000,1,0,&a,&f,&n,&b, + (unsigned char**)&pastebuffer)) { + Fl::e_text = pastebuffer; + Fl::e_length = int(n); + fl_selection_requestor->handle(FL_PASTE); + } + }} + return 1; + + case SelectionClear: + Fl::selection_owner(0); + return 1; + + case SelectionRequest: { + XSelectionEvent e; + e.type = SelectionNotify; + e.display = fl_display; + e.requestor = fl_xevent->xselectionrequest.requestor; + e.selection = fl_xevent->xselectionrequest.selection; + e.target = fl_xevent->xselectionrequest.target; + e.time = fl_xevent->xselectionrequest.time; + if (fl_xevent->xselectionrequest.target != XA_STRING || !selection_length) { + e.property = 0; + } else { + e.property = fl_xevent->xselectionrequest.property; + } + if (e.property) { + XChangeProperty(fl_display, e.requestor, e.property, + XA_STRING, 8, 0, (unsigned char *)selection_buffer, + selection_length); + } + XSendEvent(fl_display, e.requestor, 0, 0, (XEvent *)&e);} + return 1; + + default: + return 0; + } +} + +//////////////////////////////////////////////////////////////// + +// Call this when a "paste" operation happens: +void Fl::paste(Fl_Widget &receiver) { + if (selection_owner()) { + // We already have it, do it quickly without window server. + // Notice that the text is clobbered if set_selection is + // called in response to FL_PASTE! + Fl::e_text = selection_buffer; + Fl::e_length = selection_length; + receiver.handle(FL_PASTE); + return; + } + // otherwise get the window server to return it: + fl_selection_requestor = &receiver; + XConvertSelection(fl_display, XA_PRIMARY, XA_STRING, XA_PRIMARY, + fl_xid(Fl::first_window()), fl_event_time); + if (!beenhere) { + Fl::add_handler(selection_xevent_handler); + beenhere = 1; + } +} + +//////////////////////////////////////////////////////////////// + +// call this when you create a selection: +void Fl::selection(Fl_Widget &owner, const char *stuff, int len) { + if (!stuff || len<=0) return; + if (len+1 > selection_buffer_length) { + delete[] selection_buffer; + selection_buffer = new char[len+100]; + selection_buffer_length = len+100; + } + memcpy(selection_buffer, stuff, len); + selection_buffer[len] = 0; // needed for direct paste + selection_length = len; + selection_owner(&owner); + static Window selxid; // window X thinks selection belongs to + if (!selxid) selxid = + XCreateSimpleWindow(fl_display, + RootWindow(fl_display, fl_screen), + 0,0,1,1,0,0,0); + XSetSelectionOwner(fl_display, XA_PRIMARY, selxid, fl_event_time); + if (!beenhere) { + Fl::add_handler(selection_xevent_handler); + beenhere = 1; + } +} + +#endif +// end of Fl_cutpaste.C diff --git a/src/Fl_cutpaste_win32.cxx b/src/Fl_cutpaste_win32.cxx new file mode 100644 index 000000000..de60c8ed2 --- /dev/null +++ b/src/Fl_cutpaste_win32.cxx @@ -0,0 +1,106 @@ +// Fl_cutpaste_win32.C + +// Implementation of cut and paste. + +// This is seperated from Fl.C mostly to test Fl::add_handler(). +// But this will save a small amount of code size in a program that +// has no text editing fields or other things that call cut or paste. + +#include <FL/Fl.H> +#include <FL/x.H> +#include <FL/Fl_Widget.H> +#include <string.h> + +static char *selection_buffer; +static int selection_length; +static int selection_buffer_length; +static char beenhere; + +extern Fl_Widget *fl_selection_requestor; // widget doing request_paste() + +static int selection_xevent_handler(int) { + + switch (fl_msg.message) { + + case WM_DESTROYCLIPBOARD: + Fl::selection_owner(0); + Fl::flush(); // get the redraw to happen + return 1; + + case WM_RENDERALLFORMATS: + if (!OpenClipboard(fl_xid(Fl::first_window()))) return 0; + EmptyClipboard(); + // fall through... + case WM_RENDERFORMAT: { + HANDLE h = GlobalAlloc(GHND, selection_length+1); + if (h) { + LPSTR p = (LPSTR)GlobalLock(h); + memcpy(p, selection_buffer, selection_length); + p[selection_length] = 0; + GlobalUnlock(h); + SetClipboardData(CF_TEXT, h); + } + if (fl_msg.message == WM_RENDERALLFORMATS) + CloseClipboard(); + return 1;} + + default: + return 0; + } +} + +//////////////////////////////////////////////////////////////// + +// call this when you create a selection: +void Fl::selection(Fl_Widget &owner, const char *stuff, int len) { + if (!stuff || len<=0) return; + if (len+1 > selection_buffer_length) { + delete[] selection_buffer; + selection_buffer = new char[len+100]; + selection_buffer_length = len+100; + } + memcpy(selection_buffer, stuff, len); + selection_buffer[len] = 0; // needed for direct paste + selection_length = len; + if (OpenClipboard(fl_xid(Fl::first_window()))) { + EmptyClipboard(); + SetClipboardData(CF_TEXT, NULL); + CloseClipboard(); + if (!beenhere) { + Fl::add_handler(selection_xevent_handler); + beenhere = 1; + } + } + selection_owner(&owner); +} + +//////////////////////////////////////////////////////////////// + +// Call this when a "paste" operation happens: +void Fl::paste(Fl_Widget &receiver) { + if (selection_owner()) { + // We already have it, do it quickly without window server. + // Notice that the text is clobbered if set_selection is + // called in response to FL_PASTE! + Fl::e_text = selection_buffer; + Fl::e_length = selection_length; + receiver.handle(FL_PASTE); + } else { + if (!OpenClipboard(fl_xid(Fl::first_window()))) return; + HANDLE h = GetClipboardData(CF_TEXT); + if (h) { + Fl::e_text = (LPSTR)GlobalLock(h); + LPSTR a,b; + a = b = Fl::e_text; + while (*a) { // strip the CRLF pairs ($%$#@^) + if (*a == '\r' && a[1] == '\n') a++; + else *b++ = *a++; + } + *b = 0; + Fl::e_length = b - Fl::e_text; + receiver.handle(FL_PASTE); + GlobalUnlock(h); + } + CloseClipboard(); + } +} diff --git a/src/Fl_display.cxx b/src/Fl_display.cxx new file mode 100644 index 000000000..12985db54 --- /dev/null +++ b/src/Fl_display.cxx @@ -0,0 +1,16 @@ +// Fl_display.C + +// Startup method to set what display to use. +// Using setenv makes programs that are exec'd use the same display. + +#include <FL/Fl.H> +#include <stdlib.h> +#include <string.h> + +void Fl::display(const char *d) { + char *e = new char[strlen(d)+13]; + strcpy(e,"DISPLAY="); + strcpy(e+8,d); + for (char *c = e+8; *c!=':'; c++) if (!*c) {strcpy(c,":0.0"); break;} + putenv(e); +} diff --git a/src/Fl_get_key.cxx b/src/Fl_get_key.cxx new file mode 100644 index 000000000..72d9d070a --- /dev/null +++ b/src/Fl_get_key.cxx @@ -0,0 +1,37 @@ +// Fl_get_key.C + +#ifdef WIN32 +#include "Fl_get_key_win32.C" +#else + +// Return the current state of a key. This is the X version. I identify +// keys (mostly) by the X keysym. So this turns the keysym into a keycode +// and looks it up in the X key bit vector, which Fl_x.C keeps track of. + +#include <FL/Fl.H> +#include <FL/x.H> + +extern char fl_key_vector[32]; // in Fl_x.C + +int Fl::event_key(int k) { + if (k > FL_Button && k <= FL_Button+8) + return Fl::event_state(8<<(k-FL_Button)); + int i; +#ifdef __sgi + // get some missing PC keyboard keys: + if (k == FL_Meta_L) i = 147; + else if (k == FL_Meta_R) i = 148; + else if (k == FL_Menu) i = 149; + else +#endif + i = XKeysymToKeycode(fl_display, k); + return fl_key_vector[i/8] & (1 << (i%8)); +} + +int Fl::get_key(int k) { + fl_open_display(); + XQueryKeymap(fl_display, fl_key_vector); + return event_key(k); +} + +#endif diff --git a/src/Fl_get_key_win32.cxx b/src/Fl_get_key_win32.cxx new file mode 100755 index 000000000..f094e2982 --- /dev/null +++ b/src/Fl_get_key_win32.cxx @@ -0,0 +1,108 @@ +// Fl_get_key_win32.C + +// Return the current state of a key. Keys are named by fltk symbols, +// which are actually X keysyms. So this has to translate to MSWindows +// VK_x symbols. + +#include <FL/Fl.H> +#include <FL/win32.H> + +// convert an Fltk (X) keysym to a MSWindows VK symbol: +// See also the inverse converter in Fl_win32.C +// This table is in numeric order by Fltk symbol order for binary search: + +static const struct {unsigned short vk, fltk;} vktab[] = { + {VK_SPACE, ' '}, + {'1', '!'}, + {0xde, '\"'}, + {'3', '#'}, + {'4', '$'}, + {'5', '%'}, + {'7', '&'}, + {0xde, '\''}, + {'9', '('}, + {'0', ')'}, + {'8', '*'}, + {0xbb, '+'}, + {0xbc, ','}, + {0xbd, '-'}, + {0xbe, '.'}, + {0xbf, '/'}, + {0xba, ':'}, + {0xba, ';'}, + {0xbc, '<'}, + {0xbb, '='}, + {0xbe, '>'}, + {0xbf, '?'}, + {'2', '@'}, + {0xdb, '['}, + {0xdc, '\\'}, + {0xdd, ']'}, + {'6', '^'}, + {0xbd, '_'}, + {0xc0, '`'}, + {0xdb, '{'}, + {0xdc, '|'}, + {0xdd, '}'}, + {0xc0, '~'}, + {VK_BACK, FL_BackSpace}, + {VK_TAB, FL_Tab}, + {VK_RETURN, FL_Enter}, + {VK_PAUSE, FL_Pause}, + {VK_SCROLL, FL_Scroll_Lock}, + {VK_ESCAPE, FL_Escape}, + {VK_HOME, FL_Home}, + {VK_LEFT, FL_Left}, + {VK_UP, FL_Up}, + {VK_RIGHT, FL_Right}, + {VK_DOWN, FL_Down}, + {VK_PRIOR, FL_Page_Up}, + {VK_NEXT, FL_Page_Down}, + {VK_END, FL_End}, + {VK_SNAPSHOT, FL_Print}, + {VK_INSERT, FL_Insert}, + {VK_APPS, FL_Menu}, + {VK_NUMLOCK, FL_Num_Lock}, +//{VK_???, FL_KP_Enter}, + {VK_MULTIPLY, FL_KP+'*'}, + {VK_ADD, FL_KP+'+'}, + {VK_SUBTRACT, FL_KP+'-'}, + {VK_DECIMAL, FL_KP+'.'}, + {VK_DIVIDE, FL_KP+'/'}, + {VK_LSHIFT, FL_Shift_L}, + {VK_RSHIFT, FL_Shift_R}, + {VK_LCONTROL, FL_Control_L}, + {VK_RCONTROL, FL_Control_R}, + {VK_CAPITAL, FL_Caps_Lock}, + {VK_LWIN, FL_Meta_L}, + {VK_RWIN, FL_Meta_R}, + {VK_LMENU, FL_Alt_L}, + {VK_RMENU, FL_Alt_R}, + {VK_DELETE, FL_Delete} +}; + +static int fltk2ms(int fltk) { + if (fltk >= '0' && fltk <= '9') return fltk; + if (fltk >= 'A' && fltk <= 'Z') return fltk; + if (fltk >= 'a' && fltk <= 'z') return toupper(fltk); + if (fltk > FL_F && fltk <= FL_F+16) return fltk-(FL_F-(VK_F1-1)); + if (fltk >= FL_KP+'0' && fltk<=FL_KP+'9') return fltk-(FL_KP+'0'-VK_NUMPAD0); + int a = 0; + int b = sizeof(vktab)/sizeof(*vktab); + while (a < b) { + int c = (a+b)/2; + if (vktab[c].fltk == fltk) return vktab[c].vk; + if (vktab[c].fltk < fltk) a = c+1; else b = c; + } + return 0; +} + +int Fl::event_key(int k) { + return GetKeyState(fltk2ms(k))&~1; +} + +int Fl::get_key(int k) { + uchar foo[256]; + GetKeyboardState(foo); + return foo[fltk2ms(k)]&~1; +} diff --git a/src/Fl_get_system_colors.cxx b/src/Fl_get_system_colors.cxx new file mode 100644 index 000000000..ada3fdd3c --- /dev/null +++ b/src/Fl_get_system_colors.cxx @@ -0,0 +1,66 @@ +// Fl_get_system_colors.C + +#include <FL/Fl.H> +#include <FL/x.H> +#include <FL/math.h> + +void Fl::background(uchar r, uchar g, uchar b) { + // replace the gray ramp so that FL_GRAY is this color + if (!r) r = 1; else if (r==255) r = 254; + double powr = log(r/255.0)/log((FL_GRAY-FL_GRAY_RAMP)/(FL_NUM_GRAY-1.0)); + if (!g) g = 1; else if (g==255) g = 254; + double powg = log(g/255.0)/log((FL_GRAY-FL_GRAY_RAMP)/(FL_NUM_GRAY-1.0)); + if (!b) b = 1; else if (b==255) b = 254; + double powb = log(b/255.0)/log((FL_GRAY-FL_GRAY_RAMP)/(FL_NUM_GRAY-1.0)); + for (int i = 0; i < FL_NUM_GRAY; i++) { + double gray = i/(FL_NUM_GRAY-1.0); + Fl::set_color(fl_gray_ramp(i), + uchar(pow(gray,powr)*255+.5), + uchar(pow(gray,powg)*255+.5), + uchar(pow(gray,powb)*255+.5)); + } +} + +static void set_others() { + uchar r,g,b; Fl::get_color(FL_BLACK,r,g,b); + uchar r1,g1,b1; Fl::get_color(FL_WHITE,r1,g1,b1); + Fl::set_color(FL_INACTIVE_COLOR,(2*r+r1)/3, (2*g+g1)/3, (2*b+b1)/3); + Fl::set_color(FL_SELECTION_COLOR,(2*r1+r)/3, (2*g1+g)/3, (2*b1+b)/3); +} + +void Fl::foreground(uchar r, uchar g, uchar b) { + Fl::set_color(FL_BLACK,r,g,b); + set_others(); +} + +void Fl::background2(uchar r, uchar g, uchar b) { + Fl::set_color(FL_WHITE,r,g,b); + set_others(); +} + +#ifdef WIN32 + +static void getsyscolor(int what, void (*func)(uchar,uchar,uchar)) { + DWORD x = GetSysColor(what); + uchar r = uchar(x&255); + uchar g = uchar(x>>8); + uchar b = uchar(x>>16); + func(r,g,b); +} + +void Fl::get_system_colors() { + getsyscolor(COLOR_WINDOWTEXT, Fl::foreground); + getsyscolor(COLOR_BTNFACE, Fl::background); + getsyscolor(COLOR_WINDOW, Fl::background2); +} + +#else +// For X we should do something. KDE stores these colors in some standard +// place, where? + +void Fl::get_system_colors() +{ + fl_open_display(); +} + +#endif diff --git a/src/Fl_own_colormap.cxx b/src/Fl_own_colormap.cxx new file mode 100644 index 000000000..8f4ca3524 --- /dev/null +++ b/src/Fl_own_colormap.cxx @@ -0,0 +1,50 @@ +// Fl_own_colormap.C + +// Using the default system colormap can be a bad idea on PseudoColor +// visuals, since typically every application uses the default colormap and +// you can run out of colormap entries easily. +// +// The solution is to always create a new colormap on PseudoColor displays +// and copy the first 16 colors from the default colormap so that we won't +// get huge color changes when switching windows. + +#include <config.h> +#include <FL/Fl.H> +#include <FL/x.H> + +#ifdef WIN32 +// There is probably something relevant to do on MSWindows 8-bit displays +// but I don't know what it is + +void Fl::own_colormap() {} + +#else +// X version + +void Fl::own_colormap() { + fl_open_display(); +#if USE_COLORMAP + switch (fl_visual->c_class) { + case GrayScale : + case PseudoColor : + case DirectColor : + break; + default: + return; // don't do anything for non-colormapped visuals + } + int i; + XColor colors[16]; + // Get the first 16 colors from the default colormap... + for (i = 0; i < 16; i ++) colors[i].pixel = i; + XQueryColors(fl_display, fl_colormap, colors, 16); + // Create a new colormap... + fl_colormap = XCreateColormap(fl_display, + RootWindow(fl_display,fl_screen), + fl_visual->visual, AllocNone); + // Copy those first 16 colors to our own colormap: + for (i = 0; i < 16; i ++) + XAllocColor(fl_display, fl_colormap, colors + i); +#endif +} + +#endif diff --git a/src/Fl_visual.cxx b/src/Fl_visual.cxx new file mode 100644 index 000000000..d45c3ed49 --- /dev/null +++ b/src/Fl_visual.cxx @@ -0,0 +1,78 @@ +// Fl_visual.C +// +// Set the default visual according to passed switches: + +#include <config.h> +#include <FL/Fl.H> +#include <FL/x.H> + +#ifdef WIN32 +int Fl::visual(int flags) { + if (flags & FL_DOUBLE) return 0; + if (!(flags & FL_INDEX) && + GetDeviceCaps(fl_GetDC(0),BITSPIXEL) <= 8) return 0; + if ((flags & FL_RGB8) && GetDeviceCaps(fl_GetDC(0),BITSPIXEL)<24) return 0; + return 1; +} +#else + +#if USE_XDBE +#include <X11/extensions/Xdbe.h> +#endif + +static int test_visual(XVisualInfo& v, int flags) { + if (v.screen != fl_screen) return 0; + if (!(flags & FL_INDEX)) { + if (!v.red_mask) return 0; // detects static, true, and direct color + if (v.depth <= 8) return 0; // fltk will work better in colormap mode + } + if (flags & FL_RGB8) { + if (v.depth < 24) return 0; + } + // for now, fltk does not like colormaps of more than 8 bits: + if (!v.red_mask && v.depth > 8) return 0; +#if USE_XDBE + if (flags & FL_DOUBLE) { + static XdbeScreenVisualInfo *xdbejunk; + if (!xdbejunk) { + int event_base, error_base; + if (!XdbeQueryExtension(fl_display, &event_base, &error_base)) return 0; + Drawable root = RootWindow(fl_display,fl_screen); + int numscreens = 1; + xdbejunk = XdbeGetVisualInfo(fl_display,&root,&numscreens); + if (!xdbejunk) return 0; + } + for (int j = 0; ; j++) { + if (j >= xdbejunk->count) return 0; + if (xdbejunk->visinfo[j].visual == v.visualid) break; + } + } +#endif + return 1; +} + +int Fl::visual(int flags) { +#if USE_XDBE == 0 + if (flags & FL_DOUBLE) return 0; +#endif + fl_open_display(); + // always use default if possible: + if (test_visual(*fl_visual, flags)) return 1; + // get all the visuals: + XVisualInfo vTemplate; + int num; + XVisualInfo *visualList = XGetVisualInfo(fl_display, 0, &vTemplate, &num); + // find all matches, use the one with greatest depth: + XVisualInfo *found = 0; + for (int i=0; i<num; i++) if (test_visual(visualList[i], flags)) { + if (!found || found->depth < visualList[i].depth) + found = &visualList[i]; + } + if (!found) {XFree((void*)visualList); return 0;} + fl_visual = found; + fl_colormap = XCreateColormap(fl_display, RootWindow(fl_display,fl_screen), + fl_visual->visual, AllocNone); + return 1; +} + +#endif diff --git a/src/Fl_win32.cxx b/src/Fl_win32.cxx new file mode 100644 index 000000000..d00a22531 --- /dev/null +++ b/src/Fl_win32.cxx @@ -0,0 +1,696 @@ +// Fl_win32.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 + +// This file contains win32-specific code for fltk which is always linked +// in. Search other files for "WIN32" or filenames ending in _win32.C +// for other system-specific code. + +#include <config.h> +#include <FL/Fl.H> +#include <FL/win32.H> +#include <FL/Fl_Window.H> +#include <string.h> + +//////////////////////////////////////////////////////////////// +// interface to poll/select call: + +// fd's are not yet implemented. +// On NT these are probably only used for network stuff, so this may +// talk to a package that Wonko has proposed writing to make the network +// interface system independent. + +#define POLLIN 1 +#define POLLOUT 4 +#define POLLERR 8 + +void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) {} + +void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) { + Fl::add_fd(fd,POLLIN,cb,v); +} + +void Fl::remove_fd(int n) {} + +MSG fl_msg; + +int fl_ready() { + return PeekMessage(&fl_msg, NULL, 0, 0, PM_NOREMOVE); +} + +double fl_wait(int timeout_flag, double time) { + int have_message; + // get the first message by waiting the correct amount of time: + if (!timeout_flag) { + GetMessage(&fl_msg, NULL, 0, 0); + have_message = 1; + } else { + if (time >= 0.001) { + int timerid = SetTimer(NULL, 0, int(time*1000), NULL); + GetMessage(&fl_msg, NULL, 0, 0); + KillTimer(NULL, timerid); + have_message = 1; + } else { + have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE); + } + } + // execute it, them execute any other messages that become ready during it: + while (have_message) { + DispatchMessage(&fl_msg); + have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE); + } + return time; +} + +//////////////////////////////////////////////////////////////// + +int Fl::h() {return GetSystemMetrics(SM_CYSCREEN);} + +int Fl::w() {return GetSystemMetrics(SM_CXSCREEN);} + +void Fl::get_mouse(int &x, int &y) { + POINT p; + GetCursorPos(&p); + x = p.x; + y = p.y; +} + +//////////////////////////////////////////////////////////////// + +extern Fl_Window *fl_xfocus; // in Fl.C +extern Fl_Window *fl_xmousewin; // in Fl.C +void fl_fix_focus(); // in Fl.C + +//////////////////////////////////////////////////////////////// + +extern HWND fl_capture; + +static int mouse_event(Fl_Window *window, int what, int button, + WPARAM wParam, LPARAM lParam) +{ + static int px, py, pmx, pmy; + POINT pt; + Fl::e_x = pt.x = (signed short)LOWORD(lParam); + Fl::e_y = pt.y = (signed short)HIWORD(lParam); + ClientToScreen(fl_xid(window), &pt); + Fl::e_x_root = pt.x; + Fl::e_y_root = pt.y; + while (window->parent()) { + Fl::e_x += window->x(); + Fl::e_y += window->y(); + window = window->window(); + } + + ulong state = Fl::e_state & 0xff0000; // keep shift key states +#if 0 + // mouse event reports some shift flags, perhaps save them? + if (wParam & MK_SHIFT) state |= FL_SHIFT; + if (wParam & MK_CONTROL) state |= FL_CTRL; +#endif + if (wParam & MK_LBUTTON) state |= FL_BUTTON1; + if (wParam & MK_MBUTTON) state |= FL_BUTTON2; + if (wParam & MK_RBUTTON) state |= FL_BUTTON3; + Fl::e_state = state; + + switch (what) { + case 1: // double-click + if (Fl::e_is_click) {Fl::e_clicks++; goto J1;} + case 0: // single-click + Fl::e_clicks = 0; + J1: + if (!fl_capture) SetCapture(fl_xid(window)); + Fl::e_keysym = FL_Button + button; + Fl::e_is_click = 1; + px = pmx = Fl::e_x_root; py = pmy = Fl::e_y_root; + return Fl::handle(FL_PUSH,window); + + case 2: // release: + if (!fl_capture) ReleaseCapture(); + Fl::e_keysym = FL_Button + button; + return Fl::handle(FL_RELEASE,window); + + case 3: // move: + default: // avoid compiler warning + // MSWindows produces extra events even if mouse does not move, ignore em: + if (Fl::e_x_root == pmx && Fl::e_y_root == pmy) return 1; + pmx = Fl::e_x_root; pmy = Fl::e_y_root; + if (abs(Fl::e_x_root-px)>5 || abs(Fl::e_y_root-py)>5) Fl::e_is_click = 0; + return Fl::handle(FL_MOVE,window); + + } +} + +// convert a Micro$oft VK_x to an Fltk (X) Keysym: +// See also the inverse converter in Fl_get_key_win32.C +// This table is in numeric order by VK: +static const struct {unsigned short vk, fltk;} vktab[] = { + {VK_BACK, FL_BackSpace}, + {VK_TAB, FL_Tab}, + {VK_CLEAR, FL_KP+'5'}, + {VK_RETURN, FL_Enter}, + {VK_SHIFT, FL_Shift_L}, + {VK_CONTROL, FL_Control_L}, + {VK_MENU, FL_Alt_L}, + {VK_PAUSE, FL_Pause}, + {VK_CAPITAL, FL_Caps_Lock}, + {VK_ESCAPE, FL_Escape}, + {VK_SPACE, ' '}, + {VK_PRIOR, FL_Page_Up}, + {VK_NEXT, FL_Page_Down}, + {VK_END, FL_End}, + {VK_HOME, FL_Home}, + {VK_LEFT, FL_Left}, + {VK_UP, FL_Up}, + {VK_RIGHT, FL_Right}, + {VK_DOWN, FL_Down}, + {VK_SNAPSHOT, FL_Print}, // does not work on NT + {VK_INSERT, FL_Insert}, + {VK_DELETE, FL_Delete}, + {VK_LWIN, FL_Meta_L}, + {VK_RWIN, FL_Meta_R}, + {VK_APPS, FL_Menu}, + {VK_MULTIPLY, FL_KP+'*'}, + {VK_ADD, FL_KP+'+'}, + {VK_SUBTRACT, FL_KP+'-'}, + {VK_DECIMAL, FL_KP+'.'}, + {VK_DIVIDE, FL_KP+'/'}, + {VK_NUMLOCK, FL_Num_Lock}, + {VK_SCROLL, FL_Scroll_Lock}, + {0xba, ';'}, + {0xbb, '='}, + {0xbc, ','}, + {0xbd, '-'}, + {0xbe, '.'}, + {0xbf, '/'}, + {0xc0, '`'}, + {0xdb, '['}, + {0xdc, '\\'}, + {0xdd, ']'}, + {0xde, '\''} +}; +static int ms2fltk(int vk, int extended) { + static unsigned short vklut[256]; + if (!vklut[1]) { // init the table + int i; + for (i = 0; i < 256; i++) vklut[i] = tolower(i); + for (i=VK_F1; i<=VK_F16; i++) vklut[i] = i+(FL_F-(VK_F1-1)); + for (i=VK_NUMPAD0; i<=VK_NUMPAD9; i++) vklut[i] = i+(FL_KP+'0'-VK_NUMPAD0); + for (i = 0; i < sizeof(vktab)/sizeof(*vktab); i++) + vklut[vktab[i].vk] = vktab[i].fltk; + } + if (extended) switch (vk) { + case VK_CONTROL : return FL_Control_R; + case VK_MENU: return FL_Alt_R; + case VK_RETURN: return FL_KP_Enter; + } + return vklut[vk]; +} + +char fl_direct_paint; +static HDC direct_paint_dc; + +#if USE_COLORMAP +extern HPALETTE fl_select_palette(); // in fl_color_win32.C +#endif + +static Fl_Window* resize_bug_fix; + +static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static char buffer[2]; + + fl_msg.message = uMsg; + + Fl_Window *window = fl_find(hWnd); + + STUPID_MICROSOFT: + if (window) switch (uMsg) { + + case WM_QUIT: // this should not happen? + Fl::fatal("WM_QUIT message"); + + case WM_CLOSE: // user clicked close box + Fl::handle(FL_CLOSE, window); + return 0; + + case WM_PAINT: { + // MSWindows has already set the clip region! Fltk does not like this, + // since it wants to draw it's own damage at the same time, and + // this damage may be outside the clip region. I kludge around + // this, grep for fl_direct_paint to find the kludges... + if (!window->damage()) fl_direct_paint = 1; + PAINTSTRUCT ps; + direct_paint_dc = BeginPaint(hWnd, &ps); + window->expose(2, ps.rcPaint.left, ps.rcPaint.top, + ps.rcPaint.right-ps.rcPaint.left, + ps.rcPaint.bottom-ps.rcPaint.top); + if (!fl_direct_paint) {EndPaint(hWnd,&ps);ReleaseDC(hWnd,direct_paint_dc);} + Fl_X::i(window)->flush(); + window->clear_damage(); + Fl_X::i(window)->region = 0; + if (fl_direct_paint) {EndPaint(hWnd, &ps); fl_direct_paint = 0;} + } break; + + case WM_LBUTTONDOWN: mouse_event(window, 0, 1, wParam, lParam); return 0; + case WM_LBUTTONDBLCLK:mouse_event(window, 1, 1, wParam, lParam); return 0; + case WM_LBUTTONUP: mouse_event(window, 2, 1, wParam, lParam); return 0; + case WM_MBUTTONDOWN: mouse_event(window, 0, 2, wParam, lParam); return 0; + case WM_MBUTTONDBLCLK:mouse_event(window, 1, 2, wParam, lParam); return 0; + case WM_MBUTTONUP: mouse_event(window, 2, 2, wParam, lParam); return 0; + case WM_RBUTTONDOWN: mouse_event(window, 0, 3, wParam, lParam); return 0; + case WM_RBUTTONDBLCLK:mouse_event(window, 1, 3, wParam, lParam); return 0; + case WM_RBUTTONUP: mouse_event(window, 2, 3, wParam, lParam); return 0; + case WM_MOUSEMOVE: mouse_event(window, 3, 0, wParam, lParam); return 0; + + // kludges so the pop-up menus work. Title bar still blinks, sigh... + case WM_CAPTURECHANGED: + if (fl_capture && lParam != (LPARAM)fl_capture) { + SetCapture(fl_capture); + return 0; + } + break; + case WM_ACTIVATE: + if (fl_capture && wParam && hWnd!=fl_capture) { + SetActiveWindow(fl_capture); + return 0; + } + break; + + case WM_SETFOCUS: + Fl::handle(FL_FOCUS, window); + break; + + case WM_KILLFOCUS: + Fl::handle(FL_UNFOCUS, window); + Fl::flush(); // it never returns to main loop when deactivated... + break; + + case WM_SHOWWINDOW: + if (!window->parent()) + Fl::handle(wParam ? FL_SHOW : FL_HIDE, window); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + // save the keysym until we figure out the characters: + Fl::e_keysym = ms2fltk(wParam,lParam&(1<<24)); + case WM_KEYUP: + case WM_SYSKEYUP: + TranslateMessage(&fl_msg); // always returns 1!!! + // TranslateMessage is supposed to return true only if it turns + // into another message, but it seems to always return 1 on my + // NT machine. So I will instead peek to see if there is a + // character message in the queue, I hope this can only happen + // if the translation worked: + if (PeekMessage(&fl_msg, hWnd, WM_CHAR, WM_SYSDEADCHAR, 1)) { + uMsg = fl_msg.message; + wParam = fl_msg.wParam; + lParam = fl_msg.lParam; + goto STUPID_MICROSOFT; + } + // otherwise use it as a 0-character key... + case WM_DEADCHAR: + case WM_SYSDEADCHAR: + buffer[0] = 0; + Fl::e_text = buffer; + Fl::e_length = 0; + goto GETSTATE; + case WM_CHAR: + case WM_SYSCHAR: + buffer[0] = char(wParam); + Fl::e_text = buffer; + Fl::e_length = 1; + GETSTATE: + {ulong state = Fl::e_state & 0xff000000; // keep the mouse button state + // if GetKeyState is expensive we might want to comment some of these out: + if (GetKeyState(VK_SHIFT)&~1) state |= FL_SHIFT; + if (GetKeyState(VK_CAPITAL)) state |= FL_CAPS_LOCK; + if (GetKeyState(VK_CONTROL)&~1) state |= FL_CTRL; + // Alt gets reported for the Alt-GR switch on foreign keyboards. + // so we need to check the event as well to get it right: + if ((lParam&(1<<29)) //same as GetKeyState(VK_MENU) + && uMsg != WM_CHAR) state |= FL_ALT; + if (GetKeyState(VK_NUMLOCK)) state |= FL_NUM_LOCK; + if (GetKeyState(VK_LWIN)&~1 || GetKeyState(VK_RWIN)&~1) state |= FL_META; + if (GetKeyState(VK_SCROLL)) state |= FL_SCROLL_LOCK; + Fl::e_state = state;} + if (lParam & (1<<31)) goto DEFAULT; // ignore up events after fixing shift + // for (int i = lParam&0xff; i--;) + while (window->parent()) window = window->window(); + if (Fl::handle(FL_KEYBOARD,window)) return 0; + break; + + case WM_GETMINMAXINFO: + Fl_X::i(window)->set_minmax((LPMINMAXINFO)lParam); + break; + + case WM_SIZE: + if (!window->parent()) { + if (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXHIDE) { + Fl::handle(FL_HIDE, window); + } else { + Fl::handle(FL_SHOW, window); + resize_bug_fix = window; + window->size(LOWORD(lParam), HIWORD(lParam)); + } + } + break; + + case WM_MOVE: + resize_bug_fix = window; + window->position(LOWORD(lParam), HIWORD(lParam)); + break; + + case WM_SETCURSOR: + if (LOWORD(lParam) == HTCLIENT) { + while (window->parent()) window = window->window(); + SetCursor(Fl_X::i(window)->cursor); + return 0; + } + break; + +#if USE_COLORMAP + case WM_QUERYNEWPALETTE : + fl_GetDC(hWnd); + if (fl_select_palette()) InvalidateRect(hWnd, NULL, FALSE); + break; + + case WM_PALETTECHANGED: + fl_GetDC(hWnd); + if ((HWND)wParam != hWnd && fl_select_palette()) UpdateColors(fl_gc); + break; + + case WM_CREATE : + fl_GetDC(hWnd); + fl_select_palette(); + break; +#endif + + default: + DEFAULT: + if (Fl::handle(0,0)) return 0; + break; + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +//////////////////////////////////////////////////////////////// + +void Fl_Window::resize(int X,int Y,int W,int H) { + int resize_from_program = 1; + if (this == resize_bug_fix) { + resize_from_program = 0; + resize_bug_fix = 0; + } + if (X==x() && Y==y() && W==w() && H==h()) return; + if (X != x() || Y != y()) set_flag(FL_FORCE_POSITION); + if (W != w() || H != h()) Fl_Group::resize(X,Y,W,H); else {x(X); y(Y);} + if (resize_from_program && shown()) { + if (border() && !parent()) { + X -= GetSystemMetrics(SM_CXFRAME); + Y -= GetSystemMetrics(SM_CYFRAME)+GetSystemMetrics(SM_CYCAPTION); + W += 2*GetSystemMetrics(SM_CXFRAME); + H += 2*GetSystemMetrics(SM_CYFRAME)+GetSystemMetrics(SM_CYCAPTION); + } + MoveWindow(i->xid, X, Y, W, H, TRUE); + //if (!parent()) redraw(); + } +} + +//////////////////////////////////////////////////////////////// + +char fl_show_iconic; // hack for Fl_Window::iconic() +// int fl_background_pixel = -1; // color to use for background +HCURSOR fl_default_cursor; +int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR + +Fl_X* Fl_X::make(Fl_Window* w) { + Fl_Group::current(0); // get rid of very common user bug: forgot end() + w->clear_damage(); // wait for expose events + + static char* class_name; + if (!class_name) { // create a single WNDCLASS used for everything: + class_name = "FLTK"; + WNDCLASSEX wc; + wc.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC | CS_DBLCLKS; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.cbClsExtra = wc.cbWndExtra = 0; + wc.hInstance = fl_display; + wc.hIcon = wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = fl_default_cursor = LoadCursor(NULL, IDC_ARROW); + //uchar r,g,b; Fl::get_color(FL_GRAY,r,g,b); + //wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(r,g,b)); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = class_name; + wc.cbSize = sizeof(WNDCLASSEX); + RegisterClassEx(&wc); + } + + HWND parent; + DWORD style; + DWORD styleEx; + int xp = w->x(); + int yp = w->y(); + int wp = w->w(); + int hp = w->h(); + + if (w->parent()) { + style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + styleEx = WS_EX_LEFT | WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT; + parent = fl_xid(w->window()); + } else { + if (!w->size_range_set) { + if (w->resizable()) { + Fl_Widget *o = w->resizable(); + int minw = o->w(); if (minw > 100) minw = 100; + int minh = o->h(); if (minh > 100) minh = 100; + w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0); + } else { + w->size_range(w->w(), w->h(), w->w(), w->h()); + } + } + if (w->border()) { + style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU + | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + styleEx = WS_EX_LEFT | WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT; + if (w->maxw != w->minw || w->maxh != w->minh) + style |= WS_THICKFRAME | WS_MAXIMIZEBOX; + if (!w->modal()) style |= WS_MINIMIZEBOX; + xp -= GetSystemMetrics(SM_CXFRAME); + yp -= GetSystemMetrics(SM_CYFRAME)+GetSystemMetrics(SM_CYCAPTION); + wp += 2*GetSystemMetrics(SM_CXFRAME); + hp += 2*GetSystemMetrics(SM_CYFRAME)+GetSystemMetrics(SM_CYCAPTION); + } else { + style = WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPED; + styleEx = WS_EX_LEFT | WS_EX_TOPMOST | WS_EX_TOOLWINDOW; + } + if (!(w->flags() & Fl_Window::FL_FORCE_POSITION)) { + xp = yp = CW_USEDEFAULT; + } + parent = 0; + if (w->non_modal() && !fl_disable_transient_for) { + // find some other window to be "transient for": + for (Fl_X* y = Fl_X::first; y; y = y->next) { + Fl_Window* w = y->w; + while (w->parent()) w = w->window(); + if (!w->non_modal()) { + parent = fl_xid(w); + break; + } + } + } + } + + Fl_X* x = new Fl_X; + x->other_xid = 0; + x->setwindow(w); + x->region = 0; + x->private_dc = 0; + x->cursor = fl_default_cursor; + x->xid = CreateWindowEx( + styleEx, + class_name, w->label(), style, + xp, yp, wp, hp, + parent, + NULL, // menu + fl_display, + NULL // creation parameters + ); + x->next = Fl_X::first; + Fl_X::first = x; + + // use w->xclass() to set the icon... + + w->set_visible(); + w->handle(FL_SHOW); // get child windows to appear + ShowWindow(x->xid, fl_show_iconic ? SW_MINIMIZE : SW_SHOW); + fl_show_iconic = 0; + fl_fix_focus(); + return x; +} + +//////////////////////////////////////////////////////////////// + +HINSTANCE fl_display; + +int Fl_WinMain(HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow, + int (*mainp)(int, char**)) { + fl_display = hInstance; + + int argc; + char **argv; + // test version for now: + argc = 1; char* testargv[] = {"test", 0}; argv = testargv; + + return mainp(argc, argv); +} + +//////////////////////////////////////////////////////////////// + +void Fl_Window::size_range_() { + size_range_set = 1; +} + +void Fl_X::set_minmax(LPMINMAXINFO minmax) +{ + int wd, hd; + if (w->border()) { + wd = 2*GetSystemMetrics(SM_CXFRAME); + hd = 2*GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION); + } else { + wd = hd = 0; + } + minmax->ptMinTrackSize.x = w->minw + wd; + minmax->ptMinTrackSize.y = w->minh + hd; + if (w->maxw) { + minmax->ptMaxTrackSize.x = w->maxw + wd; + minmax->ptMaxSize.x = w->maxw + wd; + } + if (w->maxh) { + minmax->ptMaxTrackSize.y = w->maxh + hd; + minmax->ptMaxSize.y = w->maxh + hd; + } +} + +//////////////////////////////////////////////////////////////// + +// returns pointer to the filename, or null if name ends with '/' +const char *filename_name(const char *name) { + const char *p,*q; + q = name; + if (q[0] && q[1]==':') q += 2; // skip leading drive letter + for (p = q; *p; p++) if (*p == '/' || *p == '\\') q = p+1; + return q; +} + +void Fl_Window::label(const char *name,const char *iname) { + Fl_Widget::label(name); + iconlabel_ = iname; + if (shown() && !parent()) { + if (!name) name = ""; + SetWindowText(i->xid, name); + // if (!iname) iname = filename_name(name); + // should do something with iname here... + } +} + +//////////////////////////////////////////////////////////////// +// Implement the virtual functions for the base Fl_Window class: + +// If the box is a filled rectangle, we can make the redisplay *look* +// faster by using X's background pixel erasing. We can make it +// actually *be* faster by drawing the frame only, this is done by +// setting fl_boxcheat, which is seen by code in fl_drawbox.C: +// For WIN32 it looks like all windows share a background color, so +// I use FL_GRAY for this and only do this cheat for windows that are +// that color. +// Actually it is totally disabled. +// Fl_Widget *fl_boxcheat; +//static inline int can_boxcheat(uchar b) {return (b==1 || (b&2) && b<=15);} + +void Fl_Window::show() { + if (!shown()) { + // if (can_boxcheat(box())) fl_background_pixel = fl_xpixel(color()); + Fl_X::make(this); + } else { + ShowWindow(i->xid, SW_RESTORE); + SetActiveWindow(i->xid); + } +} + +Fl_Window *Fl_Window::current_; +HDC fl_gc; // the current context +HWND fl_window; // the current window + +// Make sure we always ReleaseDC every DC we allocate... +HDC fl_GetDC(HWND w) { + if (fl_gc) { + if (w == fl_window) return fl_gc; + ReleaseDC(fl_window, fl_gc); + } + fl_gc = fl_direct_paint ? direct_paint_dc : GetDC(w); + fl_window = w; + // calling GetDC seems to always reset these: (?) + SetTextAlign(fl_gc, TA_BASELINE|TA_LEFT); + SetBkMode(fl_gc, TRANSPARENT); + return fl_gc; +} + +// make X drawing go into this window (called by subclass flush() impl.) +void Fl_Window::make_current() { + fl_GetDC(fl_xid(this)); + current_ = this; +} + +// WM_PAINT events and cropped damage call this: +void Fl_Window::expose(uchar flags,int X,int Y,int W,int H) { + if (i) { + if (!i->region.r) { + i->region.x = X; + i->region.y = Y; + i->region.r = X+W; + i->region.b = Y+H; + } else { + if (X < i->region.x) i->region.x = X; + if (Y < i->region.y) i->region.y = Y; + if (X+W > i->region.r) i->region.r = X+W; + if (Y+H > i->region.b) i->region.b = Y+H; + } + } + damage(flags); +} + +#include <FL/fl_draw.H> + +void Fl_Window::flush() { + make_current(); + if (damage() & ~6) { + draw(); + } else { + fl_clip_region(i->region); + draw(); + fl_pop_clip(); + } +} + +// End of Fl_win32.C // diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx new file mode 100644 index 000000000..2d5a5ab46 --- /dev/null +++ b/src/Fl_x.cxx @@ -0,0 +1,807 @@ +// Fl_x.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 + +#ifdef WIN32 +#include "Fl_win32.C" +#else + +#include <config.h> +#include <FL/Fl.H> +#include <FL/x.H> +#include <FL/Fl_Window.H> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/time.h> + +//////////////////////////////////////////////////////////////// +// interface to poll/select call: + +#if HAVE_POLL +#include <poll.h> +#else +struct pollfd {int fd; short events; short revents;}; +#define POLLIN 1 +#define POLLOUT 4 +#define POLLERR 8 +#ifdef __sgi // fix bugs in Irix's select header: +//inline static void bzero(void *b, int l) {memset(b,0,l);} +extern "C" int select( int, fd_set *, fd_set *, fd_set *, struct timeval * ); +#endif +#ifdef hpux // fix from wm2: +#define select(a,b,c,d,e) select((a),(int *)(b),(int *)(c),(int *)(d),(e)) +#endif +#ifdef __EMX__ +#include <sys/select.h> +#endif +#endif + +#define MAXFD 8 +#if !HAVE_POLL +static fd_set fdsets[3]; +static int maxfd; +#endif +static int nfds; +static struct pollfd fds[MAXFD]; +static struct { + void (*cb)(int, void*); + void* arg; +} fd[MAXFD]; + +void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) { + int i; + if (nfds < MAXFD) {i = nfds; nfds++;} else {i = MAXFD-1;} + fds[i].fd = n; + fds[i].events = events; +#if !HAVE_POLL + if (events & POLLIN) FD_SET(n, &fdsets[0]); + if (events & POLLOUT) FD_SET(n, &fdsets[1]); + if (events & POLLERR) FD_SET(n, &fdsets[2]); + if (n > maxfd) maxfd = n; +#endif + fd[i].cb = cb; + fd[i].arg = v; +} + +void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) { + Fl::add_fd(fd,POLLIN,cb,v); +} + +void Fl::remove_fd(int n) { + int i,j; + for (i=j=0; i<nfds; i++) { + if (fds[i].fd == n); + else {if (j<i) {fd[j]=fd[i]; fds[j]=fds[i];} j++;} + } + nfds = j; +#if !HAVE_POLL + FD_CLR(n, &fdsets[0]); + FD_CLR(n, &fdsets[1]); + FD_CLR(n, &fdsets[2]); + if (n == maxfd) maxfd--; +#endif +} + +int fl_ready() { + if (XQLength(fl_display)) return 1; +#if HAVE_POLL + return ::poll(fds, nfds, 0); +#else + timeval t; + t.tv_sec = 0; + t.tv_usec = 0; + fd_set fdt[3]; + fdt[0] = fdsets[0]; + fdt[1] = fdsets[1]; + fdt[2] = fdsets[2]; + return ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t); +#endif +} + +static void do_queued_events() { + while (XEventsQueued(fl_display,QueuedAfterReading)) { + XEvent xevent; + XNextEvent(fl_display, &xevent); + fl_handle(xevent); + } +} + +double fl_wait(int timeout_flag, double time) { + + // OpenGL and other broken libraries call XEventsQueued + // unnecessarily and thus cause the file descriptor to not be ready, + // so we must check for already-read events: + if (XQLength(fl_display)) {do_queued_events(); return time;} + +#if !HAVE_POLL + fd_set fdt[3]; + fdt[0] = fdsets[0]; + fdt[1] = fdsets[1]; + fdt[2] = fdsets[2]; +#endif + int n; + + if (!timeout_flag) { +#if HAVE_POLL + n = ::poll(fds, nfds, -1); +#else + n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],0); +#endif + } else { +#if HAVE_POLL + int n = ::poll(fds, nfds, time > 0.0 ? int(time*1000) : 0); +#else + timeval t; + if (time <= 0.0) { + t.tv_sec = 0; + t.tv_usec = 0; + } else { + t.tv_sec = int(time); + t.tv_usec = int(1000000 * (time-t.tv_sec)); + } + n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t); +#endif + } + if (n > 0) { + for (int i=0; i<nfds; i++) { +#if HAVE_POLL + if (fds[i].revents) fd[i].cb(fds[i].fd, fd[i].arg); +#else + int f = fds[i].fd; + short revents = 0; + if (FD_ISSET(f,&fdt[0])) revents |= POLLIN; + if (FD_ISSET(f,&fdt[1])) revents |= POLLOUT; + if (FD_ISSET(f,&fdt[2])) revents |= POLLERR; + if (fds[i].events & revents) fd[i].cb(f, fd[i].arg); +#endif + } + } + return time; +} + +//////////////////////////////////////////////////////////////// + +Display *fl_display; +int fl_screen; +XVisualInfo *fl_visual; +Colormap fl_colormap; + +static Atom wm_delete_window; +static Atom wm_protocols; +static Atom _motif_wm_hints; + +static void fd_callback(int,void *) {do_queued_events();} + +static int io_error_handler(Display*) {Fl::fatal("X I/O error"); return 0;} + +static int xerror_handler(Display* d, XErrorEvent* e) { + char buf1[128], buf2[128]; + sprintf(buf1, "XRequest.%d", e->request_code); + XGetErrorDatabaseText(d,"",buf1,buf1,buf2,128); + XGetErrorText(d, e->error_code, buf1, 128); + Fl::warning("%s: %s 0x%lx", buf2, buf1, e->resourceid); + return 0; +} + +void fl_open_display() { + if (fl_display) return; + + XSetIOErrorHandler(io_error_handler); + XSetErrorHandler(xerror_handler); + + Display *d = XOpenDisplay(0); + if (!d) Fl::fatal("Can't open display: %s",XDisplayName(0)); + + fl_display = d; + + wm_delete_window = XInternAtom(d,"WM_DELETE_WINDOW",0); + wm_protocols = XInternAtom(d,"WM_PROTOCOLS",0); + _motif_wm_hints = XInternAtom(d,"_MOTIF_WM_HINTS",0); + Fl::add_fd(ConnectionNumber(d), POLLIN, fd_callback); + + fl_screen = DefaultScreen(fl_display); +// construct an XVisualInfo that matches the default Visual: + XVisualInfo templt; int num; + templt.visualid = XVisualIDFromVisual(DefaultVisual(fl_display,fl_screen)); + fl_visual = XGetVisualInfo(fl_display, VisualIDMask, &templt, &num); + fl_colormap = DefaultColormap(fl_display,fl_screen); +} + +void fl_close_display() { + Fl::remove_fd(ConnectionNumber(fl_display)); + XCloseDisplay(fl_display); +} + +int Fl::h() { + fl_open_display(); + return DisplayHeight(fl_display,fl_screen); +} + +int Fl::w() { + fl_open_display(); + return DisplayWidth(fl_display,fl_screen); +} + +void Fl::get_mouse(int &x, int &y) { + fl_open_display(); + Window root = RootWindow(fl_display, fl_screen); + Window c; int mx,my,cx,cy; unsigned int mask; + XQueryPointer(fl_display,root,&root,&c,&mx,&my,&cx,&cy,&mask); + x = mx; + y = my; +} + +//////////////////////////////////////////////////////////////// + +extern Fl_Window *fl_xfocus; // in Fl.C +extern Fl_Window *fl_xmousewin; // in Fl.C +void fl_fix_focus(); // in Fl.C + +//////////////////////////////////////////////////////////////// + +const XEvent* fl_xevent; // the current x event +ulong fl_event_time; // the last timestamp from an x event + +char fl_key_vector[32]; // used by Fl::get_key() + +// Record event mouse position and state from an XEvent: + +static int px, py; +static ulong ptime; + +static void set_event_xy() { + Fl::e_x_root = fl_xevent->xbutton.x_root; + Fl::e_x = fl_xevent->xbutton.x; + Fl::e_y_root = fl_xevent->xbutton.y_root; + Fl::e_y = fl_xevent->xbutton.y; + Fl::e_state = fl_xevent->xbutton.state << 16; + fl_event_time = fl_xevent->xbutton.time; +#ifdef __sgi + // get the meta key off PC keyboards: + if (fl_key_vector[18]&0x18) Fl::e_state |= FL_META; +#endif + // turn off is_click if enough time or mouse movement has passed: + if (abs(Fl::e_x_root-px)+abs(Fl::e_y_root-py) > 3 + || fl_event_time >= ptime+1000) + Fl::e_is_click = 0; +} + +// if this is same event as last && is_click, increment click count: +static inline void checkdouble() { + if (Fl::e_is_click == Fl::e_keysym) + Fl::e_clicks++; + else { + Fl::e_clicks = 0; + Fl::e_is_click = Fl::e_keysym; + } + px = Fl::e_x_root; + py = Fl::e_y_root; + ptime = fl_event_time; +} + +static Fl_Window* resize_bug_fix; + +//////////////////////////////////////////////////////////////// + +int fl_handle(const XEvent& xevent) +{ + fl_xevent = &xevent; + + switch (xevent.type) { // events where we don't care about window + + case KeymapNotify: + memcpy(fl_key_vector, xevent.xkeymap.key_vector, 32); + return 0; + + case MappingNotify: + XRefreshKeyboardMapping((XMappingEvent*)&xevent.xmapping); + return 0; + } + + int event = 0; + Fl_Window* window = fl_find(xevent.xany.window); + + if (window) switch (xevent.type) { + + case ClientMessage: + if ((Atom)(xevent.xclient.data.l[0]) == wm_delete_window) event = FL_CLOSE; + break; + + case MapNotify: + event = FL_SHOW; + break; + + case UnmapNotify: + event = FL_HIDE; + break; + + case Expose: + case GraphicsExpose: +#if 1 // try to keep windows on top even if WM_TRANSIENT_FOR does not work: + if (Fl::first_window()->non_modal() && window != Fl::first_window()) + Fl::first_window()->show(); +#endif + window->damage(2, xevent.xexpose.x, xevent.xexpose.y, + xevent.xexpose.width, xevent.xexpose.height); + return 1; + + case ButtonPress: + Fl::e_keysym = FL_Button + xevent.xbutton.button; + set_event_xy(); checkdouble(); + // fix buggy window managers that position window wrong: + Fl_X::x(window,Fl::e_x_root-Fl::e_x); + Fl_X::y(window,Fl::e_y_root-Fl::e_y); + Fl::e_state |= (FL_BUTTON1 << (xevent.xbutton.button-1)); + event = FL_PUSH; + break; + + case MotionNotify: + set_event_xy(); + event = FL_MOVE; + break; + + case ButtonRelease: + Fl::e_keysym = FL_Button + xevent.xbutton.button; + set_event_xy(); + Fl::e_state &= ~(FL_BUTTON1 << (xevent.xbutton.button-1)); + event = FL_RELEASE; + break; + + case FocusIn: + event = FL_FOCUS; + break; + + case FocusOut: + event = FL_UNFOCUS; + break; + + case KeyPress: { + static int got_backspace; + static char buffer[21]; + KeySym keysym; + int i = xevent.xkey.keycode; fl_key_vector[i/8] |= (1 << (i%8)); + int len = XLookupString((XKeyEvent*)&(xevent.xkey),buffer,20,&keysym,0); + if (!len && keysym < 0x400) { + // turn all latin-2,3,4 characters into 8-bit codes: + buffer[0] = char(keysym); + len = 1; + } + // ignore all effects of shift on the keysyms (makes it a lot + // easier to program shortcuts!) + keysym = XKeycodeToKeysym(fl_display, i, 0); +#ifdef __sgi + // get some missing PC keyboard keys: + if (!keysym) switch(i) { + case 147: keysym = FL_Meta_L; break; + case 148: keysym = FL_Meta_R; break; + case 149: keysym = FL_Menu; break; + } +#endif + if (!got_backspace) { + // Backspace kludge: until user hits the backspace key, assumme + // it is missing and use the Delete key for that purpose: + if (keysym == FL_Delete) keysym = FL_BackSpace; + else if (keysym == FL_BackSpace) got_backspace = 1; + } + if (keysym >= 0xff95 && keysym < 0xffa0) { + // Make NumLock irrelevant (always on): + // This lookup table turns the XK_KP_* functions back into the + // ascii characters. This won't work on non-PC layout keyboards, + // but are there any of those left?? + buffer[0] = "7486293150."[keysym-0xff95]; + len = 1; + keysym = FL_KP+buffer[0]; + } + buffer[len] = 0; + Fl::e_keysym = int(keysym); + Fl::e_text = buffer; + Fl::e_length = len; + set_event_xy(); Fl::e_is_click = 0; + if (Fl::event_state(FL_CTRL) && keysym == '-') buffer[0] = 0x1f; // ^_ + event = FL_KEYBOARD; + break;} + + case KeyRelease: { + int i = xevent.xkey.keycode; fl_key_vector[i/8] &= ~(1 << (i%8)); + set_event_xy();} + break; + + case EnterNotify: + if (xevent.xcrossing.detail == NotifyInferior) break; + // XInstallColormap(fl_display, Fl_X::i(window)->colormap); + set_event_xy(); + Fl::e_state = xevent.xcrossing.state << 16; + event = FL_ENTER; + break; + + case LeaveNotify: + if (xevent.xcrossing.detail == NotifyInferior) break; + set_event_xy(); + Fl::e_state = xevent.xcrossing.state << 16; + event = FL_LEAVE; + break; + + case ConfigureNotify: { + int x = xevent.xconfigure.x; + int y = xevent.xconfigure.y; + // avoid bug (?) in 4DWM, it reports position of 0,0 on resize: + if (!x && !y) { + Window r, c; int X, Y; unsigned int m; + XQueryPointer(fl_display, fl_xid(window), &r, &c, &x, &y, &X, &Y, &m); + x = x-X; y = y-Y; + } + resize_bug_fix = window; + window->resize(x, y, + xevent.xconfigure.width, xevent.xconfigure.height); + return 1;} + } + + return Fl::handle(event, window); +} + +//////////////////////////////////////////////////////////////// + +void Fl_Window::resize(int X,int Y,int W,int H) { + if (resize_bug_fix == this) + resize_bug_fix = 0; + else if (shown()) { + // tell X window manager to change window size: + if (!(flags()&FL_FORCE_POSITION) && X == x() && Y == y()) + XResizeWindow(fl_display, i->xid, W>0 ? W : 1, H>0 ? H : 1); + else if (W != w() || H != h()) + XMoveResizeWindow(fl_display, i->xid, X, Y, W>0 ? W : 1, H>0 ? H : 1); + else + XMoveWindow(fl_display, i->xid, X, Y); + } + if (X != x() || Y != y()) set_flag(FL_FORCE_POSITION); + if (W != w() || H != h()) Fl_Group::resize(X,Y,W,H); else {x(X); y(Y);} + // Notice that this does *not* set any redraw bits. I assumme + // I will receive damage for the whole window from X. I think + // that "ForgetGravity" forces the expose event for the entire + // window, but this may not be true on some implementations. +} + +//////////////////////////////////////////////////////////////// + +// A subclass of Fl_Window may call this to associate an X window it +// creates with the Fl_Window: + +Fl_X* Fl_X::set_xid(Fl_Window* w, Window xid) { + Fl_X* x = new Fl_X; + x->xid = xid; + x->other_xid = 0; + x->setwindow(w); + x->next = Fl_X::first; + x->region = 0; + Fl_X::first = x; + w->set_visible(); + w->handle(FL_SHOW); // get child windows to appear + fl_fix_focus(); // if this is modal we must fix focus now + return x; +} + +// More commonly a subclass calls this, because it hides the really +// ugly parts of X and sets all the stuff for a window that is set +// normally. The global variables like fl_show_iconic are so that +// subclasses of *that* class may change the behavior... + +char fl_show_iconic; // hack for iconize() +int fl_background_pixel = -1; // hack to speed up bg box drawing +int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR + +static const int childEventMask = ExposureMask; + +static const int XEventMask = +ExposureMask|StructureNotifyMask +|KeyPressMask|KeyReleaseMask|KeymapStateMask|FocusChangeMask +|ButtonPressMask|ButtonReleaseMask +|EnterWindowMask|LeaveWindowMask +|PointerMotionMask; + +void Fl_X::make_xid(Fl_Window* w, XVisualInfo *visual, Colormap colormap) +{ + Fl_Group::current(0); // get rid of very common user bug: forgot end() + + ulong root = w->parent() ? + fl_xid(w->window()) : RootWindow(fl_display, fl_screen); + + XSetWindowAttributes attr; + int mask = CWBorderPixel|CWColormap|CWEventMask|CWBitGravity; + attr.event_mask = w->parent() ? childEventMask : XEventMask; + attr.colormap = colormap; + attr.border_pixel = 0; + attr.bit_gravity = 0; // StaticGravity; + attr.override_redirect = 0; + if (Fl::grab()) { + attr.save_under = 1; mask |= CWSaveUnder; + if (!w->border()) {attr.override_redirect = 1; mask |= CWOverrideRedirect;} + } + if (fl_background_pixel >= 0) { + attr.background_pixel = fl_background_pixel; + fl_background_pixel = -1; + mask |= CWBackPixel; + } + Fl_X* x = + set_xid(w, XCreateWindow(fl_display, + root, + w->x(), w->y(), + w->w()>0 ? w->w() : 1, + w->h()>0 ? w->h() : 1, + 0, // borderwidth + visual->depth, + InputOutput, + visual->visual, + mask, &attr)); + //XInstallColormap(fl_display, colormap); + + if (!w->parent() && !attr.override_redirect) { + // Communicate all kinds 'o junk to the X Window Manager: + + w->label(w->label(), w->iconlabel()); + + XChangeProperty(fl_display, x->xid, wm_protocols, + XA_ATOM, 32, 0, (uchar*)&wm_delete_window, 1); + + // send size limits and border: + x->sendxjunk(); + + // set the class property, which controls the icon used: + if (w->xclass()) { + char buffer[1024]; + char *p; const char *q; + // truncate on any punctuation, because they break XResource lookup: + for (p = buffer, q = w->xclass(); isalnum(*q)||(*q&128);) *p++ = *q++; + *p++ = 0; + // create the capitalized version: + q = buffer; + *p = toupper(*q++); if (*p++ == 'X') *p++ = toupper(*q++); + while ((*p++ = *q++)); + XChangeProperty(fl_display, x->xid, XA_WM_CLASS, XA_STRING, 8, 0, + (unsigned char *)buffer, p-buffer-1); + } + + if (w->non_modal() && x->next && !fl_disable_transient_for) { + // find some other window to be "transient for": + Fl_Window* w = x->next->w; + while (w->parent()) w = w->window(); + XSetTransientForHint(fl_display, x->xid, fl_xid(w)); + } + + if (fl_show_iconic) { + XWMHints hints; + hints.flags = StateHint; + hints.initial_state = 3; + XSetWMHints(fl_display, x->xid, &hints); + fl_show_iconic = 0; + } + } + + XMapWindow(fl_display, x->xid); +} + +//////////////////////////////////////////////////////////////// +// Send X window stuff that can be changed over time: + +void Fl_X::sendxjunk() { + if (w->parent()) return; // it's not a window manager window! + + if (!w->size_range_set) { // default size_range based on resizable(): + if (w->resizable()) { + Fl_Widget *o = w->resizable(); + int minw = o->w(); if (minw > 100) minw = 100; + int minh = o->h(); if (minh > 100) minh = 100; + w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0); + } else { + w->size_range(w->w(), w->h(), w->w(), w->h()); + } + return; // because this recursively called here + } + + XSizeHints hints; + hints.min_width = w->minw; + hints.min_height = w->minh; + hints.max_width = w->maxw; + hints.max_height = w->maxh; + hints.width_inc = w->dw; + hints.height_inc = w->dh; + + // see the file /usr/include/X11/Xm/MwmUtil.h: + // fill all fields to avoid bugs in kwm and perhaps other window managers: + // 0, MWM_FUNC_ALL, MWM_DECOR_ALL + long prop[5] = {0, 1, 1, 0, 0}; + + if (hints.min_width != hints.max_width || + hints.min_height != hints.max_height) { // resizable + hints.flags = PMinSize; + if (hints.max_width >= hints.min_width || + hints.max_height >= hints.min_height) { + hints.flags = PMinSize|PMaxSize; + // unfortunately we can't set just one maximum size. Guess a + // value for the other one. Some window managers will make the + // window fit on screen when maximized, others will put it off screen: + if (hints.max_width < hints.min_width) hints.max_width = Fl::w(); + if (hints.max_height < hints.min_height) hints.max_height = Fl::h(); + } + if (hints.width_inc && hints.height_inc) hints.flags |= PResizeInc; + if (w->aspect) { + // stupid X! It could insist that the corner go on the + // straight line between min and max... + hints.min_aspect.x = hints.max_aspect.x = hints.min_width; + hints.min_aspect.y = hints.max_aspect.y = hints.min_height; + hints.flags |= PAspect; + } + } else { // not resizable: + hints.flags = PMinSize|PMaxSize; + prop[0] = 1; // MWM_HINTS_FUNCTIONS + prop[1] = 1|2|16; // MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE + } + if (w->non_modal()) { + prop[0] = 1; // MWM_HINTS_FUNCTIONS + prop[1] |= 8; // turn off MWM_FUNC_MINIMIZE in 4Dwm + } + + if (w->flags() & Fl_Window::FL_FORCE_POSITION) { + hints.flags |= USPosition; + hints.x = w->x(); + hints.y = w->y(); + } + + if (!w->border()) { + prop[0] |= 2; // MWM_HINTS_DECORATIONS + prop[2] = 0; // no decorations + } + + XSetWMNormalHints(fl_display, xid, &hints); + XChangeProperty(fl_display, xid, + _motif_wm_hints, _motif_wm_hints, + 32, 0, (unsigned char *)prop, 5); +} + +void Fl_Window::size_range_() { + size_range_set = 1; + if (shown()) i->sendxjunk(); +} + +//////////////////////////////////////////////////////////////// + +// returns pointer to the filename, or null if name ends with '/' +const char *filename_name(const char *name) { + const char *p,*q; + for (p=q=name; *p;) if (*p++ == '/') q = p; + return q; +} + +void Fl_Window::label(const char *name,const char *iname) { + Fl_Widget::label(name); + iconlabel_ = iname; + if (shown() && !parent()) { + if (!name) name = ""; + XChangeProperty(fl_display, i->xid, XA_WM_NAME, + XA_STRING, 8, 0, (uchar*)name, strlen(name)); + if (!iname) iname = filename_name(name); + XChangeProperty(fl_display, i->xid, XA_WM_ICON_NAME, + XA_STRING, 8, 0, (uchar*)iname, strlen(iname)); + } +} + +//////////////////////////////////////////////////////////////// +// Implement the virtual functions for the base Fl_Window class: + +// If the box is a filled rectangle, we can make the redisplay *look* +// faster by using X's background pixel erasing. We can make it +// actually *be* faster by drawing the frame only, this is done by +// setting fl_boxcheat, which is seen by code in fl_drawbox.C: +// +// On XFree86 (and prehaps all X's) this has a problem if the window +// is resized while a save-behind window is atop it. The previous +// contents are restored to the area, but this assummes the area +// is cleared to background color. So this is disabled in this version. +// Fl_Window *fl_boxcheat; +static inline int can_boxcheat(uchar b) {return (b==1 || (b&2) && b<=15);} + +void Fl_Window::show() { + if (!shown()) { + fl_open_display(); + if (can_boxcheat(box())) fl_background_pixel = int(fl_xpixel(color())); + Fl_X::make_xid(this); + } else { + XMapRaised(fl_display, i->xid); + } +} + +Window fl_window; +Fl_Window *Fl_Window::current_; +GC fl_gc; + +// make X drawing go into this window (called by subclass flush() impl.) +void Fl_Window::make_current() { + static GC gc; // the GC used by all X windows + if (!gc) gc = XCreateGC(fl_display, i->xid, 0, 0); + fl_window = i->xid; + fl_gc = gc; + current_ = this; + fl_clip_region(0); +} + +#include <FL/fl_draw.H> + +// Current meaning of damage() bits on a window: +// 1 = a child needs redrawing +// 2 = expose events +// 128 = redraw everything + +void Fl_Widget::damage(uchar flags) { + if (type() < FL_WINDOW) { + damage(flags, x(), y(), w(), h()); + } else { + Fl_X* i = Fl_X::i((Fl_Window*)this); + if (i) { + if (i->region) {XDestroyRegion(i->region); i->region = 0;} + damage_ |= flags; + Fl::damage(1); + } + } +} + +void Fl_Widget::redraw() {damage(~0);} + +Region XRectangleRegion(int x, int y, int w, int h); // in fl_rect.C + +void Fl_Widget::damage(uchar flags, int X, int Y, int W, int H) { + if (type() < FL_WINDOW) { + damage_ |= flags; + if (parent()) parent()->damage(1,X,Y,W,H); + } else { + // see if damage covers entire window: + if (X<=0 && Y<=0 && W>=w() && H>=h()) {damage(flags); return;} + Fl_X* i = Fl_X::i((Fl_Window*)this); + if (i) { + if (damage()) { + // if we already have damage we must merge with existing region: + if (i->region) { + XRectangle R; + R.x = X; R.y = Y; R.width = W; R.height = H; + XUnionRectWithRegion(&R, i->region, i->region); + } + damage_ |= flags; + } else { + // create a new region: + if (i->region) XDestroyRegion(i->region); + i->region = XRectangleRegion(X,Y,W,H); + damage_ = flags; + } + Fl::damage(1); + } + } +} + +void Fl_Window::flush() { + make_current(); +//if (damage() == 2 && can_boxcheat(box())) fl_boxcheat = this; + fl_clip_region(i->region); i->region = 0; + draw(); +} + +#endif +// End of Fl_x.C diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 000000000..1de491e1e --- /dev/null +++ b/src/Makefile @@ -0,0 +1,215 @@ +CPPFILES = \ + Fl.C \ + Fl_Adjuster.C \ + Fl_Bitmap.C \ + Fl_Browser.C \ + Fl_Browser_.C \ + Fl_Browser_load.C \ + Fl_Box.C \ + Fl_Button.C \ + Fl_Chart.C \ + Fl_Check_Button.C \ + Fl_Choice.C \ + Fl_Clock.C \ + Fl_Color_Chooser.C \ + Fl_Counter.C \ + Fl_Dial.C \ + Fl_Double_Window.C \ + Fl_Gl_Choice.C \ + Fl_Gl_Overlay.C \ + Fl_Gl_Window.C \ + Fl_Group.C \ + Fl_Image.C \ + Fl_Input.C \ + Fl_Input_.C \ + Fl_Light_Button.C \ + Fl_Menu.C \ + Fl_Menu_.C \ + Fl_Menu_Bar.C \ + Fl_Menu_Button.C \ + Fl_Menu_Window.C \ + Fl_Menu_add.C \ + Fl_Menu_global.C \ + Fl_Multi_Label.C \ + Fl_Output.C \ + Fl_Overlay_Window.C \ + Fl_Pack.C \ + Fl_Pixmap.C \ + Fl_Positioner.C \ + Fl_Repeat_Button.C \ + Fl_Return_Button.C \ + Fl_Roller.C \ + Fl_Round_Button.C \ + Fl_Scroll.C \ + Fl_Scrollbar.C \ + Fl_Single_Window.C \ + Fl_Slider.C \ + Fl_Tabs.C \ + Fl_Tile.C \ + Fl_Valuator.C \ + Fl_Value_Input.C \ + Fl_Value_Output.C \ + Fl_Value_Slider.C \ + Fl_Widget.C \ + Fl_Window.C \ + Fl_Window_fullscreen.C \ + Fl_Window_hotspot.C \ + Fl_Window_iconize.C \ + Fl_abort.C \ + Fl_add_idle.C \ + Fl_arg.C \ + Fl_cutpaste.C \ + Fl_display.C \ + Fl_get_key.C \ + Fl_get_system_colors.C \ + Fl_own_colormap.C \ + Fl_visual.C \ + Fl_x.C \ + filename_absolute.C \ + filename_expand.C \ + filename_ext.C \ + filename_isdir.C \ + filename_list.C \ + filename_match.C \ + filename_setext.C \ + fl_arc.C \ + fl_arci.C \ + fl_ask.C \ + fl_boxtype.C \ + fl_color.C \ + fl_cursor.C \ + fl_curve.C \ + fl_diamond_box.C \ + fl_draw.C \ + fl_draw_image.C \ + fl_draw_pixmap.C \ + fl_engraved_label.C \ + fl_file_chooser.C \ + fl_font.C \ + fl_labeltype.C \ + fl_oval_box.C \ + fl_overlay.C \ + fl_overlay_visual.C \ + fl_rect.C \ + fl_round_box.C \ + fl_rounded_box.C \ + fl_set_font.C \ + fl_set_fonts.C \ + fl_scroll_area.C \ + fl_shadow_box.C \ + fl_shortcut.C \ + fl_show_colormap.C \ + fl_symbols.C \ + fl_vertex.C \ + forms_compatability.C \ + forms_bitmap.C \ + forms_free.C \ + forms_fselect.C \ + forms_pixmap.C \ + forms_timer.C \ + gl_draw.C \ + gl_start.C \ + glut_compatability.C \ + glut_font.C + +CFILES = scandir.c numericsort.c + +CLEAN = + +################################################################ + +include ../makeinclude + +LIBRARY = ../lib/$(LIBNAME) + +OBJECTS = $(CPPFILES:.C=.o) $(CFILES:.c=.o) + +$(LIBRARY) : $(OBJECTS) + @echo Building $(LIBRARY) + @$(AR) $(LIBRARY) $(OBJECTS) + @$(RANLIB) $(LIBRARY) + +.C.o : + @echo $<: + @$(CXX) -I.. $(CXXFLAGS) -c $< +.c.o : + @echo $<: + @$(CC) -I.. $(CFLAGS) -c -o $@ $< + +clean : + -@ rm -f *.o *.do $(LIBRARY) $(CLEAN) core *~ makedepend + @touch makedepend + +depend: + @echo Making makedepend + @$(MAKEDEPEND) -I.. $(CXXFLAGS) $(CPPFILES) $(CFILES) > makedepend +include makedepend + +################################################################ + +LIBRARY_D = ../lib/$(LIBNAME_D) + +debug: $(LIBRARY_D) + +OBJECTS_D = $(CPPFILES:.C=.do) $(CFILES:.c=.do) + +.SUFFIXES : .C .c .o .do + +.C.do : + @echo $<: + @$(CXX) -I.. $(CXXFLAGS_D) -c -o $@ $< +.c.do : + @echo $<: + @$(CC) -I.. $(CFLAGS) -c -o $@ $< + +$(LIBRARY_D) : $(OBJECTS_D) + @echo building $(LIBRARY_D): + @$(AR) $(LIBRARY_D) $(OBJECTS_D) + @$(RANLIB) $(LIBRARY_D) + +################################################################ +# Linux-specific shared libraries: + +include ../version + +SHBASENAME = libfltk.so +SONAME = $(SHBASENAME).$(VERSION) +SHLIBNAME = $(SHBASENAME).$(VERSION).$(REVISION) +PICOBJECTS = $(CPPFILES:.C=.pic.o) $(CFILES:.c=.pic.o) + +shared: ../lib/$(SHLIBNAME) ../lib/$(SONAME) ../lib/$(SHBASENAME) + touch ../lib/$(LIBNAME) + +../lib/$(SHLIBNAME): $(PICOBJECTS) + @echo building $@: + @$(CXX) -shared -Wl,-soname,$(SONAME) $(GLDLIBS) -o $@ $(PICOBJECTS) + +../lib/$(SONAME): ../lib/$(SHLIBNAME) + ln -nsf $(SHLIBNAME) $@ + +../lib/$(SHBASENAME): ../lib/$(SHLIBNAME) + ln -nsf $(SHLIBNAME) $@ + +%.pic.o: %.C + @echo $<: + @$(CXX) -fPIC -I.. $(CXXFLAGS) -c -o $@ $< +%.pic.o: %.c + @echo $<: + @$(CC) -fPIC -I.. $(CFLAGS) -c -o $@ $< + +################################################################ + +install: ../lib/$(LIBNAME) + cp -f ../lib/$(LIBNAME) $(libdir) + -cp -f ../lib/$(SHLIBNAME) $(libdir) + @chmod a+r,u+w,g-w,o-w $(libdir)/$(LIBNAME)* + cp -rf ../FL $(includedir) + @chmod -R a+r,u+w,g-w,o-w $(includedir)/FL + +# it used to do this, but it was recommended that I take it out +# @chown root $(libdir)/$(LIBNAME)* +# @chown -R root $(includedir)/FL + +uninstall: + -@ rm -f $(libdir)/libfltk* + -@ rm -rf $(includedir)/FL diff --git a/src/cmap.cxx b/src/cmap.cxx new file mode 100644 index 000000000..e98b765b5 --- /dev/null +++ b/src/cmap.cxx @@ -0,0 +1,120 @@ +// This program produces the contents of "fl_cmap.h" as stdout + +// #include <gl/gl.h> +#include <stdio.h> + +// This table is initialized with color values I got by reading the +// colormap on an IRIX 4.3 machine: + +// "full intensity colors" have been turned down some to make white +// background less intense by default. The hope is that this will make +// fltk programs more friendly on color-adjusted screens. If you want +// pure colors you should get them out of the colormap. + +#define III 244 // maximum intensity of the basic colors + +static short cmap[256][3] = { +// 3-bit colormap: + { 0, 0, 0}, // black + {III, 0, 0}, // red + { 0,III, 0}, // green + {III,III, 0}, // yellow + { 0, 0,III}, // blue + {III, 0,III}, // magenta + { 0,III,III}, // cyan + {III,III,III}, // white +// pastel versions of those colors: + { 85, 85, 85}, // 1/3 gray + {198,113,113}, // salmon? pale red? + {113,198,113}, // pale green + {142,142, 56}, // khaki + {113,113,198}, // pale blue + {142, 56,142}, // purple, orchid, pale magenta + { 56,142,142}, // cadet blue, aquamarine, pale cyan + {170,170,170}, // 2/3 gray +// These next 16 are the FL_FREE_COLOR area. For compatability with +// some existing DD programs, I prefill them with the random colors +// you get on a 5.3 machine: + { 16, 16, 16}, + {128, 40,128}, + {198, 30, 30}, + { 66, 30, 30}, + {176,140,140}, + { 0, 20, 20}, + { 20, 10, 10}, + { 40, 20, 20}, + { 60, 30, 30}, + { 0, 80, 80}, + { 0, 40, 40}, + { 20, 20, 0}, + { 40, 40, 0}, + { 80, 80, 10}, + {150,150, 20}, + {160, 10, 10}, +// The rest of the colormap is a gray ramp and table, filled in below: +}; + +// This is Fl::background from Fl_get_system_colors.C, with modifications: + +#define FL_GRAY_RAMP 32 +#define FL_NUM_GRAY 24 +#define FL_GRAY 49 // old value is 47 +typedef unsigned char uchar; +#include <math.h> + +void background(uchar r, uchar g, uchar b) { + // replace the gray ramp so that color 47 (by default 2/3) is this color + if (!r) r = 1; else if (r==255) r = 254; + double powr = log(r/255.0)/log((FL_GRAY-FL_GRAY_RAMP)/(FL_NUM_GRAY-1.0)); + if (!g) g = 1; else if (g==255) g = 254; + double powg = log(g/255.0)/log((FL_GRAY-FL_GRAY_RAMP)/(FL_NUM_GRAY-1.0)); + if (!b) b = 1; else if (b==255) b = 254; + double powb = log(b/255.0)/log((FL_GRAY-FL_GRAY_RAMP)/(FL_NUM_GRAY-1.0)); + for (int i = 0; i < FL_NUM_GRAY; i++) { + double gray = i/(FL_NUM_GRAY-1.0); + cmap[i+FL_GRAY_RAMP][0] = uchar(pow(gray,powr)*255+.5); + cmap[i+FL_GRAY_RAMP][1] = uchar(pow(gray,powg)*255+.5); + cmap[i+FL_GRAY_RAMP][2] = uchar(pow(gray,powb)*255+.5); + } +} + +int main() { + int i,r,g,b; +#if 0 + /* Read colormap colors into internal table */ + long cmwin; + noport(); + cmwin = winopen("CM"); + for (i=0; i<256; i++) + getmcolor(i,&cmap[i][0],&cmap[i][1],&cmap[i][2]); + winclose(cmwin); +#endif +// overwrite the X allocation area with one color so people are +// discouraged from using it: + for (i=16; i<32; i++) {cmap[i][0]=cmap[i][1]=cmap[i][2] = 85;} + + // fill in the gray ramp: + background(0xc0, 0xc0, 0xc0); // microsoft colors + // background(cmap[15][0],cmap[15][1],cmap[15][2]); // old fltk colors + // copy the 1/3 and 2/3 gray to the closest locations in gray ramp: + cmap[39][0] = cmap[39][1] = cmap[39][2] = cmap[8][0]; + cmap[47][0] = cmap[47][1] = cmap[47][2] = cmap[15][0]; + + // fill in the color cube + i = 56; + for (b=0; b<5; b++) + for (r=0; r<5; r++) + for (g=0; g<8; g++) { + cmap[i][0] = r*255/4; + cmap[i][1] = g*255/7; + cmap[i][2] = b*255/4; + i++; + } + + for (i=0; i<256; i++) { + printf("\t0x%02x%02x%02x00",cmap[i][0],cmap[i][1],cmap[i][2]); + if (i < 255) printf(",\n"); + } + printf("\n"); + return 0; +} diff --git a/src/d1.xbm b/src/d1.xbm new file mode 100644 index 000000000..a0e67f029 --- /dev/null +++ b/src/d1.xbm @@ -0,0 +1,6 @@ +#define d1_width 16 +#define d1_height 16 +static unsigned char d1_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x38, 0x00, 0x78, 0x00, + 0xe8, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x1c, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/src/d1_mask.xbm b/src/d1_mask.xbm new file mode 100644 index 000000000..6b1e14dac --- /dev/null +++ b/src/d1_mask.xbm @@ -0,0 +1,6 @@ +#define d1_mask_width 16 +#define d1_mask_height 16 +static unsigned char d1_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x7c, 0x00, 0xfc, 0x00, + 0xfc, 0x01, 0xec, 0x03, 0xc0, 0x37, 0x80, 0x3f, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00}; diff --git a/src/dump_compose.c b/src/dump_compose.c new file mode 100644 index 000000000..fe41991cf --- /dev/null +++ b/src/dump_compose.c @@ -0,0 +1,26 @@ +/* write out the documentation for the compose key */ + +/* copy the string from Fl_Input.C */ +static const char* const compose_pairs = +" ! @ # $ y=| & : c a <<~ - r _ * +-2 3 ' u p . , 1 o >>141234? " +"A`A'A^A~A:A*AEC,E`E'E^E:I`I'I^I:D-N~O`O'O^O~O:x O/U`U'U^U:Y'DDss" +"a`a'a^a~a:a*aec,e`e'e^e:i`i'i^i:d-n~o`o'o^o~o:-:o/u`u'u^u:y'ddy:"; + +#include <stdio.h> + +int main() { + int x,y; + for (x = 0; x<16; x++) { + for (y = 0; y<6; y++) { + const char *p = compose_pairs + (16*y+x)*2; + if (p[1] == ' ') + printf("<td><code>%c </code>   %c\n", + p[0],(p-compose_pairs)/2+0xA0); + else + printf("<td><code>%c%c</code>   %c\n", + p[0],p[1],(p-compose_pairs)/2+0xA0); + } + printf("<tr>"); + } + return 0; +} diff --git a/src/ew.xbm b/src/ew.xbm new file mode 100644 index 000000000..1ff835cd5 --- /dev/null +++ b/src/ew.xbm @@ -0,0 +1,8 @@ +#define ew_width 16 +#define ew_height 16 +#define ew_x_hot 8 +#define ew_y_hot 8 +static unsigned char ew_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, + 0x0c, 0x30, 0xfe, 0x7f, 0xfe, 0x7f, 0x0c, 0x30, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/src/ew_mask.xbm b/src/ew_mask.xbm new file mode 100644 index 000000000..49dc1df88 --- /dev/null +++ b/src/ew_mask.xbm @@ -0,0 +1,8 @@ +#define ew_mask_width 16 +#define ew_mask_height 16 +#define ew_mask_x_hot 8 +#define ew_mask_y_hot 8 +static unsigned char ew_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x1c, 0x38, + 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0x1c, 0x38, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/src/fastarrow.h b/src/fastarrow.h new file mode 100644 index 000000000..e381acdce --- /dev/null +++ b/src/fastarrow.h @@ -0,0 +1,6 @@ +#define fastarrow_width 16 +#define fastarrow_height 16 +static unsigned char fastarrow_bits[] = { + 0x00, 0x00, 0x00, 0x07, 0xe0, 0x07, 0xfc, 0x03, 0xff, 0xff, 0xfc, 0x03, + 0xe0, 0x07, 0x00, 0x07, 0xe0, 0x00, 0xe0, 0x07, 0xc0, 0x3f, 0xff, 0xff, + 0xc0, 0x3f, 0xe0, 0x07, 0xe0, 0x00, 0x00, 0x00}; diff --git a/src/filename_absolute.cxx b/src/filename_absolute.cxx new file mode 100644 index 000000000..d5a937539 --- /dev/null +++ b/src/filename_absolute.cxx @@ -0,0 +1,67 @@ +/* expand a file name by prepending current directory, deleting . and + .. (not really correct for symbolic links) between the prepended + current directory. Use $PWD if it exists. + Returns true if any changes were made. +*/ + +#include <FL/filename.H> +#include <stdlib.h> +#include <string.h> +#if defined(WIN32) && !defined(CYGNUS) +# include <direct.h> +# define getcwd(a,b) _getdcwd(0,a,b) +#else +# include <unistd.h> +# ifdef __EMX__ +# define getcwd _getcwd2 +# endif +#endif + +#if defined(WIN32) || defined(__EMX__) +inline int isdirsep(char c) {return c=='/' || c=='\\';} +#else +#define isdirsep(c) ((c)=='/') +#endif + +int filename_absolute(char *to,const char *from) { + + if (isdirsep(*from) || *from == '|' +#if defined(WIN32) || defined(__EMX__) + || from[1]==':' +#endif + ) { + strcpy(to,from); + return 0; + } + + char *a,temp[FL_PATH_MAX]; + const char *start = from; + + a = getenv("PWD"); + if (a) strncpy(temp,a,FL_PATH_MAX); + else {a = getcwd(temp,FL_PATH_MAX); if (!a) return 0;} +#if defined(WIN32) || defined(__EMX__) + for (a = temp; *a; a++) if (*a=='\\') *a = '/'; // ha ha +#else + a = temp+strlen(temp); +#endif + if (isdirsep(*(a-1))) a--; + /* remove intermediate . and .. names: */ + while (*start == '.') { + if (start[1]=='.' && isdirsep(start[2])) { + char *b; + for (b = a-1; b >= temp && !isdirsep(*b); b--); + if (b < temp) break; + a = b; + start += 3; + } else if (isdirsep(start[1])) { + start += 2; + } else + break; + } + *a++ = '/'; + strcpy(a,start); + strcpy(to,temp); + return 1; + +} diff --git a/src/filename_expand.cxx b/src/filename_expand.cxx new file mode 100644 index 000000000..27e593389 --- /dev/null +++ b/src/filename_expand.cxx @@ -0,0 +1,72 @@ +/* expand a file name by substuting environment variables and + home directories. Returns true if any changes were made. + to & from may be the same buffer. +*/ + +#include <FL/filename.H> +#include <stdlib.h> +#include <string.h> +#ifdef WIN32 +#else +# include <unistd.h> +# include <pwd.h> +#endif + +#if defined(WIN32) || defined(__EMX__) +static inline int isdirsep(char c) {return c=='/' || c=='\\';} +#else +#define isdirsep(c) ((c)=='/') +#endif + +int filename_expand(char *to,const char *from) { + + char temp[FL_PATH_MAX]; + strcpy(temp,from); + const char *start = temp; + const char *end = temp+strlen(temp); + + int ret = 0; + + for (char *a=temp; a<end; ) { // for each slash component + char *e; for (e=a; e<end && !isdirsep(*e); e++); // find next slash + const char *value = 0; // this will point at substitute value + switch (*a) { + case '~': // a home directory name + if (e <= a+1) { // current user's directory + value = getenv("HOME"); +#ifndef WIN32 + } else { // another user's directory + struct passwd *pwd; + char t = *e; *(char *)e = 0; + pwd = getpwnam(a+1); + *(char *)e = t; + if (pwd) value = pwd->pw_dir; +#endif + } + break; + case '$': /* an environment variable */ + {char t = *e; *(char *)e = 0; value = getenv(a+1); *(char *)e = t;} + break; + } + if (value) { + // substitutions that start with slash delete everything before them: + if (isdirsep(value[0])) start = a; +#if defined(WIN32) || defined(__EMX__) + // also if it starts with "A:" + if (value[0] && value[1]==':') start = a; +#endif + int t = strlen(value); if (isdirsep(value[t-1])) t--; + memmove(a+t, e, end+1-e); + end = a+t+(end-e); + memcpy(a, value, t); + ret++; + } else { + a = e+1; +#if defined(WIN32) || defined(__EMX__) + if (*e == '\\') {*e = '/'; ret++;} // ha ha! +#endif + } + } + strcpy(to,start); + return ret; +} diff --git a/src/filename_ext.cxx b/src/filename_ext.cxx new file mode 100644 index 000000000..9fcd67096 --- /dev/null +++ b/src/filename_ext.cxx @@ -0,0 +1,17 @@ +// returns pointer to the last '.' or to the null if none: + +#include <FL/filename.H> + +const char *filename_ext(const char *buf) { + const char *q = 0; + const char *p = buf; + for (p=buf; *p; p++) { + if (*p == '/') q = 0; +#if defined(WIN32) || defined(__EMX__) + else if (*p == '\\') q = 0; +#endif + else if (*p == '.') q = p; + } + return q ? q : p; +} + diff --git a/src/filename_isdir.cxx b/src/filename_isdir.cxx new file mode 100644 index 000000000..ce02d2302 --- /dev/null +++ b/src/filename_isdir.cxx @@ -0,0 +1,12 @@ +// filename_isdir.C + +// Used by fl_file_chooser + +#include <config.h> +#include <FL/filename.H> +#include <sys/stat.h> + +int filename_isdir(const char* n) { + struct stat s; + return !stat(n, &s) && (s.st_mode&0170000)==0040000; +} diff --git a/src/filename_list.cxx b/src/filename_list.cxx new file mode 100644 index 000000000..31fd52eea --- /dev/null +++ b/src/filename_list.cxx @@ -0,0 +1,36 @@ +// filename_list.C + +// Wrapper for scandir with const-correct function prototypes. + +#include <config.h> +#include <FL/filename.H> + +#if !defined(WIN32) || defined(__GNUC__) +extern "C" { +#endif + int numericsort(const dirent **, const dirent **); +#if HAVE_SCANDIR +#else + int alphasort(const dirent **, const dirent **); + int scandir (const char *dir, dirent ***namelist, + int (*select)(const dirent *), + int (*compar)(const dirent **, const dirent **)); +#endif +#if !defined(WIN32) || defined(__GNUC__) +} +#endif + +int filename_list(const char *d, dirent ***list) { +#if defined(_AIX) || defined(CRAY) + // on some systems you may need to do this, due to a rather common + // error in the prototype for the sorting function, where a level + // of pointer indirection is missing: + return scandir(d, list, 0, (int(*)(const void*,const void*))numericsort); +#else +#if HAVE_SCANDIR + return scandir(d, list, 0, (int(*)(dirent**,dirent**))numericsort); +#else // built-in scandir is const-correct: + return scandir(d, list, 0, numericsort); +#endif +#endif +} diff --git a/src/filename_match.cxx b/src/filename_match.cxx new file mode 100644 index 000000000..ba8ad4aad --- /dev/null +++ b/src/filename_match.cxx @@ -0,0 +1,74 @@ +/*------------------- Pattern matching --------------------------*/ +/* Adapted from Rich Salz. */ +#include <FL/filename.H> + +int filename_match(const char *s, const char *p) { + int matched; + + for (;;) { + switch(*p++) { + + case '?' : // match any single character + if (!*s++) return 0; + break; + + case '*' : // match 0-n of any characters + if (!*p) return 1; // do trailing * quickly + while (!filename_match(s, p)) if (!*s++) return 0; + return 1; + + case '[': { // match one character in set of form [abc-d] or [^a-b] + if (!*s) return 0; + int reverse = (*p=='^' || *p=='!'); if (reverse) p++; + matched = 0; + char last = 0; + while (*p) { + if (*p=='-' && last) { + if (*s <= *++p && *s >= last ) matched = 1; + last = 0; + } else { + if (*s == *p) matched = 1; + } + last = *p++; + if (*p==']') break; + } + if (matched == reverse) return 0; + s++; p++;} + break; + + case '{' : // {pattern1|pattern2|pattern3} + NEXTCASE: + if (filename_match(s,p)) return 1; + for (matched = 0;;) { + switch (*p++) { + case '\\': if (*p) p++; break; + case '{': matched++; break; + case '}': if (!matched--) return 0; break; + case '|': case ',': if (matched==0) goto NEXTCASE; + case 0: return 0; + } + } + case '|': // skip rest of |pattern|pattern} when called recursively + case ',': + for (matched = 0; *p && matched >= 0;) { + switch (*p++) { + case '\\': if (*p) p++; break; + case '{': matched++; break; + case '}': matched--; break; + } + } + break; + case '}': + break; + + case 0: // end of pattern + return !*s; + + case '\\': // quote next character + if (*p) p++; + default : // other characters + if (*s++ != *(p-1)) return 0; + break; + } + } +} diff --git a/src/filename_setext.cxx b/src/filename_setext.cxx new file mode 100644 index 000000000..2e9ca128c --- /dev/null +++ b/src/filename_setext.cxx @@ -0,0 +1,12 @@ +// Replace .ext with new extension +// If no . in name, append new extension +// If new extension is null, act like it is "" + +#include <FL/filename.H> +#include <string.h> + +char *filename_setext(char *buf, const char *ext) { + char *q = (char *)filename_ext(buf); + if (ext) strcpy(q,ext); else *q = 0; + return(buf); +} diff --git a/src/fl_arc.cxx b/src/fl_arc.cxx new file mode 100644 index 000000000..756c2a4bb --- /dev/null +++ b/src/fl_arc.cxx @@ -0,0 +1,50 @@ +// fl_arc.C + +// Utility for drawing arcs and circles. They are added to +// the current fl_begin/fl_vertex/fl_end path. +// Incremental math implementation: + +#include <FL/fl_draw.H> +#include <FL/math.h> + +void fl_arc(double x, double y, double r, double start, double end) { + + // draw start point accurately: + double A = start*(M_PI/180); + double X = r*cos(A); + double Y = -r*sin(A); + fl_vertex(x+X,y+Y); + + // number of segments per radian: + int n; { + double x1 = fl_transform_dx(r,0); + double y1 = fl_transform_dy(r,0); + double r1 = x1*x1+y1*y1; + x1 = fl_transform_dx(0,r); + y1 = fl_transform_dy(0,r); + double r2 = x1*x1+y1*y1; + if (r2 < r1) r1 = r2; + n = int(sqrt(r1)*.841471); + if (n < 2) n = 2; + } + double epsilon = 1.0/n; + double E = end*(M_PI/180); + int i = int((E-A)*n); + if (i < 0) {i = -i; epsilon = -epsilon;} + double epsilon2 = epsilon/2; + for (; i>1; i--) { + X += epsilon*Y; + Y -= epsilon2*X; + fl_vertex(x+X,y+Y); + Y -= epsilon2*X; + } + + // draw the end point accurately: + fl_vertex(x+r*cos(E), y-r*sin(E)); +} + +#if 0 // portable version. X-specific one in fl_vertex.C +void fl_circle(double x,double y,double r) { + _fl_arc(x, y, r, r, 0, 360); +} +#endif diff --git a/src/fl_arci.cxx b/src/fl_arci.cxx new file mode 100644 index 000000000..1a96535c6 --- /dev/null +++ b/src/fl_arci.cxx @@ -0,0 +1,45 @@ +// fl_arci.C + +// "integer" circle drawing functions. These draw the limited +// circle types provided by X and NT graphics. The advantage of +// these is that small ones draw quite nicely (probably due to stored +// hand-drawn bitmaps of small circles!) and may be implemented by +// hardware and thus are fast. + +// Probably should add fl_chord. + +// 3/10/98: created + +#include <FL/fl_draw.H> +#include <FL/x.H> +#ifdef WIN32 +#include <FL/math.h> +#endif + +void fl_arc(int x,int y,int w,int h,double a1,double a2) { + if (w <= 0 || h <= 0) return; +#ifdef WIN32 + int xa = x+w/2+int(w*cos(a1/180.0*M_PI)); + int ya = y+h/2-int(h*sin(a1/180.0*M_PI)); + int xb = x+w/2+int(w*cos(a2/180.0*M_PI)); + int yb = y+h/2-int(h*sin(a2/180.0*M_PI)); + Arc(fl_gc, x, y, x+w, y+h, xa, ya, xb, yb); +#else + XDrawArc(fl_display, fl_window, fl_gc, x,y,w-1,h-1, int(a1*64),int((a2-a1)*64)); +#endif +} + +void fl_pie(int x,int y,int w,int h,double a1,double a2) { + if (w <= 0 || h <= 0) return; +#ifdef WIN32 + if (a1 == a2) return; + int xa = x+w/2+int(w*cos(a1/180.0*M_PI)); + int ya = y+h/2-int(h*sin(a1/180.0*M_PI)); + int xb = x+w/2+int(w*cos(a2/180.0*M_PI)); + int yb = y+h/2-int(h*sin(a2/180.0*M_PI)); + SelectObject(fl_gc, fl_brush()); + Pie(fl_gc, x, y, x+w, y+h, xa, ya, xb, yb); +#else + XFillArc(fl_display, fl_window, fl_gc, x,y,w,h, int(a1*64),int((a2-a1)*64)); +#endif +} diff --git a/src/fl_ask.cxx b/src/fl_ask.cxx new file mode 100644 index 000000000..8d92fe482 --- /dev/null +++ b/src/fl_ask.cxx @@ -0,0 +1,165 @@ +// fl_ask.C + +// Implementation of fl_message, fl_ask, fl_choice, fl_input + +// The three-message fl_show_x functions are for forms compatibility +// mostly. In most cases it is easier to get a multi-line message +// by putting newlines in the message. + +#include <FL/Fl.H> + +#include <FL/fl_ask.H> + +#include <FL/Fl_Box.H> +#include <FL/Fl_Button.H> +#include <FL/Fl_Return_Button.H> +#include <FL/Fl_Window.H> +#include <FL/Fl_Input.H> +#include <FL/Fl_Secret_Input.H> +static Fl_Window *message_form; +static Fl_Box *message[3]; +static Fl_Box *icon; +static Fl_Button *button[3]; +static Fl_Input *input; +static char *iconlabel; +uchar fl_message_font_ = 0; +uchar fl_message_size_ = FL_NORMAL_SIZE; + +static Fl_Window *makeform() { + if (message_form) return message_form; + Fl_Window *w = message_form = new Fl_Window(410,105); + // w->clear_border(); + // w->box(FL_UP_BOX); + (message[0] = new Fl_Box(60, 9, 340, 20)) + ->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_WRAP); + (message[1] = new Fl_Box(60, 25, 340, 20)) + ->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_WRAP); + (message[2] = new Fl_Box(60, 41, 340, 20)) + ->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_WRAP); + (input = new Fl_Input(60,32,340,30))->hide(); + {Fl_Box* o = icon = new Fl_Box(10, 10, 50, 50); + o->box(FL_THIN_UP_BOX); + o->labelfont(FL_TIMES_BOLD); + o->labelsize(34); + o->color(FL_WHITE); + o->labelcolor(FL_BLUE); + } + (button[0] = new Fl_Button(310, 70, 90, 25))->shortcut("^["); + button[1] = new Fl_Return_Button(210, 70, 90, 25); + button[2] = new Fl_Button(110, 70, 90, 25); + w->end(); + w->set_modal(); + return w; +} + +// back-compatable functions: + +int fl_show_choice( + const char *m0, + const char *m1, + const char *m2, + int, // number of buttons, ignored + const char *b0, + const char *b1, + const char *b2) +{ + makeform(); + message[0]->label(m0); + message[1]->label(m1); + message[2]->label(m2); + Fl_Font f = (Fl_Font)fl_message_font_; + if (!f) f = Fl_Input_::default_font(); + int s = fl_message_size_ + Fl_Input::default_size(); + for (int i=0; i<3; i++) { + message[i]->labelfont(f); + message[i]->labelsize(s); + } + if (b0) {button[0]->show();button[0]->label(b0);button[1]->position(210,70);} + else {button[0]->hide(); button[1]->position(310,70);} + if (b1) {button[1]->show(); button[1]->label(b1);} + else button[1]->hide(); + if (b2) {button[2]->show(); button[2]->label(b2);} + else button[2]->hide(); + const char* prev_icon_label = icon->label(); + if (!prev_icon_label) icon->label(iconlabel); + message_form->hotspot(button[0]); + message_form->show(); + int r; + for (;;) { + Fl_Widget *o = Fl::readqueue(); + if (!o) Fl::wait(); + else if (o == button[0]) {r = 0; break;} + else if (o == button[1]) {r = 1; break;} + else if (o == button[2]) {r = 2; break;} + else if (o == message_form) {r = 0; break;} + } + message_form->hide(); + icon->label(prev_icon_label); + return r+1; +} + +// pointers you can use to change fltk to a foreign language: +const char* fl_no = "No"; +const char* fl_yes= "Yes"; +const char* fl_ok = "OK"; +const char* fl_cancel= "Cancel"; + +// back-compatable XForms functions: + +void fl_show_message(const char *q1,const char *q2,const char *q3) { + iconlabel = "i"; + fl_show_choice(q1, q2, q3, 1, 0, fl_ok, 0); +} + +void fl_show_alert(const char *q1,const char *q2,const char *q3) { + iconlabel = "!"; + fl_show_choice(q1, q2, q3, 1, 0, fl_ok, 0); +} + +int fl_show_question(const char *q1,const char *q2,const char *q3) { + iconlabel = "?"; + return fl_show_choice(q1, q2, q3, 2, fl_no, fl_yes, 0) - 1; +} + +// fltk functions: + +void fl_message(const char *question) { + fl_show_message(0, question, 0); +} + +void fl_alert(const char *question) { + fl_show_alert(0, question, 0); +} + +int fl_ask(const char *question) { + return fl_show_question(0, question, 0); +} + +int fl_choice(const char *q,const char *b0,const char *b1,const char *b2) { + iconlabel = "?"; + return fl_show_choice(0,q,0,3,b0,b1,b2) - 1; +} + +Fl_Widget *fl_message_icon() {makeform(); return icon;} + +const char *fl_input(const char *str1, const char *defstr, uchar type) { + makeform(); + input->type(type); + input->show(); + input->value(defstr); + iconlabel = "?"; + int r = fl_show_choice(str1,0,0,2,fl_cancel,fl_ok,0); + input->hide(); + return r==2 ? input->value() : 0; +} + +const char *fl_input(const char *str1, const char *defstr) { + return fl_input(str1, defstr, FL_NORMAL_INPUT); +} + +char *fl_show_simple_input(const char *str1, const char *defstr) { + const char *r = fl_input(str1, defstr, FL_NORMAL_INPUT); + return (char *)(r ? r : defstr); +} + +// end of fl_ask.C diff --git a/src/fl_boxtype.cxx b/src/fl_boxtype.cxx new file mode 100644 index 000000000..8bfa7dd71 --- /dev/null +++ b/src/fl_boxtype.cxx @@ -0,0 +1,256 @@ +// fl_boxtype.c + +// Box drawing code for the common box types and the table of +// boxtypes. Other box types are in seperate files so they are not +// linked in if not used. + +#include <FL/Fl.H> +#include <FL/Fl_Widget.H> +#include <FL/fl_draw.H> +#include <config.h> + +//////////////////////////////////////////////////////////////// + +static uchar active_ramp[24] = { + FL_GRAY_RAMP+0, FL_GRAY_RAMP+1, FL_GRAY_RAMP+2, FL_GRAY_RAMP+3, + FL_GRAY_RAMP+4, FL_GRAY_RAMP+5, FL_GRAY_RAMP+6, FL_GRAY_RAMP+7, + FL_GRAY_RAMP+8, FL_GRAY_RAMP+9, FL_GRAY_RAMP+10,FL_GRAY_RAMP+11, + FL_GRAY_RAMP+12,FL_GRAY_RAMP+13,FL_GRAY_RAMP+14,FL_GRAY_RAMP+15, + FL_GRAY_RAMP+16,FL_GRAY_RAMP+17,FL_GRAY_RAMP+18,FL_GRAY_RAMP+19, + FL_GRAY_RAMP+20,FL_GRAY_RAMP+21,FL_GRAY_RAMP+22,FL_GRAY_RAMP+23}; +static uchar inactive_ramp[24] = { + 43, 43, 44, 44, + 44, 45, 45, 46, + 46, 46, 47, 47, + 48, 48, 48, 49, + 49, 49, 50, 50, + 51, 51, 52, 52}; +uchar* Fl_Gray_Ramp = (uchar*)active_ramp-'A'; + +void fl_frame(const char* s, int x, int y, int w, int h) { + if (h > 0 && w > 0) for (;*s;) { + // draw top line: + fl_color(Fl_Gray_Ramp[*s++]); + fl_xyline(x, y, x+w-1); + y++; if (--h <= 0) break; + // draw left line: + fl_color(Fl_Gray_Ramp[*s++]); + fl_yxline(x, y+h-1, y); + x++; if (--w <= 0) break; + // draw bottom line: + fl_color(Fl_Gray_Ramp[*s++]); + fl_xyline(x, y+h-1, x+w-1); + if (--h <= 0) break; + // draw right line: + fl_color(Fl_Gray_Ramp[*s++]); + fl_yxline(x+w-1, y+h-1, y); + if (--w <= 0) break; + } +} + +void fl_frame2(const char* s, int x, int y, int w, int h) { + if (h > 0 && w > 0) for (;*s;) { + // draw bottom line: + fl_color(Fl_Gray_Ramp[*s++]); + fl_xyline(x, y+h-1, x+w-1); + if (--h <= 0) break; + // draw right line: + fl_color(Fl_Gray_Ramp[*s++]); + fl_yxline(x+w-1, y+h-1, y); + if (--w <= 0) break; + // draw top line: + fl_color(Fl_Gray_Ramp[*s++]); + fl_xyline(x, y, x+w-1); + y++; if (--h <= 0) break; + // draw left line: + fl_color(Fl_Gray_Ramp[*s++]); + fl_yxline(x, y+h-1, y); + x++; if (--w <= 0) break; + } +} + +void fl_no_box(int, int, int, int, Fl_Color) {} + +void fl_thin_down_frame(int x, int y, int w, int h, Fl_Color) { + fl_frame2("WWHH",x,y,w,h); +} + +void fl_thin_down_box(int x, int y, int w, int h, Fl_Color c) { + fl_thin_down_frame(x,y,w,h,c); + fl_color(c); fl_rectf(x+1, y+1, w-2, h-2); +} + +void fl_thin_up_frame(int x, int y, int w, int h, Fl_Color) { + fl_frame2("HHWW",x,y,w,h); +} + +void fl_thin_up_box(int x, int y, int w, int h, Fl_Color c) { + fl_thin_up_frame(x,y,w,h,c); + fl_color(c); fl_rectf(x+1, y+1, w-2, h-2); +} + +void fl_up_frame(int x, int y, int w, int h, Fl_Color) { +#if BORDER_WIDTH == 1 + fl_frame2("HHWW",x,y,w,h); +#else +#if BORDER_WIDTH == 2 + fl_frame2("AAPPMMWU",x,y,w,h); +#else + fl_frame("AAAAWUJJUSNN",x,y,w,h); +#endif +#endif +} + +#define D1 BORDER_WIDTH +#define D2 (BORDER_WIDTH+BORDER_WIDTH) + +void fl_up_box(int x, int y, int w, int h, Fl_Color c) { + fl_up_frame(x,y,w,h,c); + fl_color(c); fl_rectf(x+D1, y+D1, w-D2, h-D2); +} + +void fl_down_frame(int x, int y, int w, int h, Fl_Color) { +#if BORDER_WIDTH == 1 + fl_frame2("WWHH",x,y,w,h); +#else +#if BORDER_WIDTH == 2 + fl_frame2("UWMMPPAA",x,y,w,h); +#else + fl_frame("NNSUJJUWAAAA",x,y,w,h); +#endif +#endif +} + +void fl_down_box(int x, int y, int w, int h, Fl_Color c) { + fl_down_frame(x,y,w,h,c); + fl_color(c); fl_rectf(x+D1, y+D1, w-D2, h-D2); +} + +void fl_engraved_frame(int x, int y, int w, int h, Fl_Color) { + fl_frame("HHWWWWHH",x,y,w,h); +} + +void fl_engraved_box(int x, int y, int w, int h, Fl_Color c) { + fl_engraved_frame(x,y,w,h,c); + fl_color(c); fl_rectf(x+2, y+2, w-4, h-4); +} + +void fl_embossed_frame(int x, int y, int w, int h, Fl_Color) { + fl_frame("WWHHHHWW",x,y,w,h); +} + +void fl_embossed_box(int x, int y, int w, int h, Fl_Color c) { + fl_embossed_frame(x,y,w,h,c); + fl_color(c); fl_rectf(x+2, y+2, w-4, h-4); +} + +void fl_rectbound(int x, int y, int w, int h, Fl_Color bgcolor) { + fl_color(FL_BLACK); fl_rect(x, y, w, h); + fl_color(bgcolor); fl_rectf(x+1, y+1, w-2, h-2); +} +#define fl_border_box fl_rectbound + +void fl_rectf(int x, int y, int w, int h, Fl_Color c) { + fl_color(c); + fl_rectf(x, y, w, h); +} + +void fl_border_frame(int x, int y, int w, int h, Fl_Color c) { + fl_color(c); + fl_rect(x, y, w, h); +} + +//////////////////////////////////////////////////////////////// + +static struct { + Fl_Box_Draw_F *f; + uchar dx, dy, dw, dh; +} fl_box_table[] = { +// must match list in Enumerations.H!!! + {fl_no_box, 0,0,0,0}, + {fl_rectf, 0,0,0,0}, // FL_FLAT_BOX + {fl_up_box, D1,D1,D2,D2}, + {fl_down_box, D1,D1,D2,D2}, + {fl_up_frame, D1,D1,D2,D2}, + {fl_down_frame, D1,D1,D2,D2}, + {fl_thin_up_box, 1,1,2,2}, + {fl_thin_down_box, 1,1,2,2}, + {fl_thin_up_frame, 1,1,2,2}, + {fl_thin_down_frame, 1,1,2,2}, + {fl_engraved_box, 2,2,4,4}, + {fl_embossed_box, 2,2,4,4}, + {fl_engraved_frame, 2,2,4,4}, + {fl_embossed_frame, 2,2,4,4}, + {fl_border_box, 1,1,2,2}, + {fl_border_box, 1,1,2,2}, // _FL_SHADOW_BOX, + {fl_border_frame, 1,1,2,2}, + {fl_border_frame, 1,1,2,2}, // _FL_SHADOW_FRAME, + {fl_border_box, 1,1,2,2}, // _FL_ROUNDED_BOX, + {fl_border_box, 1,1,2,2}, // _FL_RSHADOW_BOX, + {fl_border_frame, 1,1,2,2}, // _FL_ROUNDED_FRAME + {fl_rectf, 0,0,0,0}, // _FL_RFLAT_BOX, + {fl_up_box, 3,3,6,6}, // _FL_ROUND_UP_BOX + {fl_down_box, 3,3,6,6}, // _FL_ROUND_DOWN_BOX, + {fl_up_box, 0,0,0,0}, // _FL_DIAMOND_UP_BOX + {fl_down_box, 0,0,0,0}, // _FL_DIAMOND_DOWN_BOX + {fl_border_box, 1,1,2,2}, // _FL_OVAL_BOX, + {fl_border_box, 1,1,2,2}, // _FL_OVAL_SHADOW_BOX, + {fl_border_frame, 1,1,2,2}, // _FL_OVAL_FRAME + {fl_rectf, 0,0,0,0}, // _FL_OVAL_FLAT_BOX, + {fl_up_box, 3,3,6,6}, // FL_FREE_BOX+0 + {fl_down_box, 3,3,6,6}, // FL_FREE_BOX+1 + {fl_up_box, 3,3,6,6}, // FL_FREE_BOX+2 + {fl_down_box, 3,3,6,6}, // FL_FREE_BOX+3 + {fl_up_box, 3,3,6,6}, // FL_FREE_BOX+4 + {fl_down_box, 3,3,6,6}, // FL_FREE_BOX+5 + {fl_up_box, 3,3,6,6}, // FL_FREE_BOX+6 + {fl_down_box, 3,3,6,6}, // FL_FREE_BOX+7 +}; + +int Fl::box_dx(Fl_Boxtype t) {return fl_box_table[t].dx;} +int Fl::box_dy(Fl_Boxtype t) {return fl_box_table[t].dy;} +int Fl::box_dw(Fl_Boxtype t) {return fl_box_table[t].dw;} +int Fl::box_dh(Fl_Boxtype t) {return fl_box_table[t].dh;} + +void fl_internal_boxtype(Fl_Boxtype t, Fl_Box_Draw_F* f) {fl_box_table[t].f=f;} + +void Fl::set_boxtype(Fl_Boxtype t, Fl_Box_Draw_F* f, + uchar a, uchar b, uchar c, uchar d) { + fl_box_table[t].f=f; + fl_box_table[t].dx = a; + fl_box_table[t].dy = b; + fl_box_table[t].dw = c; + fl_box_table[t].dh = d; +} + +void Fl::set_boxtype(Fl_Boxtype t, Fl_Boxtype f) { + fl_box_table[t] = fl_box_table[f]; +} + +void fl_draw_box(Fl_Boxtype t, int x, int y, int w, int h, Fl_Color c) { + if (t) fl_box_table[t].f(x,y,w,h,c); +} + +//extern Fl_Widget *fl_boxcheat; // hack set by Fl_Window.C + +void Fl_Widget::draw_box() const { + int t = box_; + if (!t) return; +// if (this == fl_boxcheat) { +// fl_boxcheat = 0; +// if (t == FL_FLAT_BOX) return; +// t += 2; // convert box to frame +// } + draw_box((Fl_Boxtype)t, x_, y_, w_, h_, (Fl_Color)color_); +} + +void Fl_Widget::draw_box(Fl_Boxtype b, Fl_Color c) const { + draw_box(b, x_, y_, w_, h_, c); +} + +void Fl_Widget::draw_box(Fl_Boxtype b, int x, int y, int w, int h, Fl_Color c) +const { + if (!active_r()) Fl_Gray_Ramp = inactive_ramp-'A'; + fl_box_table[b].f(x, y, w, h, c); + Fl_Gray_Ramp = active_ramp-'A'; +} diff --git a/src/fl_cmap.h b/src/fl_cmap.h new file mode 100644 index 000000000..934172440 --- /dev/null +++ b/src/fl_cmap.h @@ -0,0 +1,256 @@ + 0x00000000, + 0xf4000000, + 0x00f40000, + 0xf4f40000, + 0x0000f400, + 0xf400f400, + 0x00f4f400, + 0xf4f4f400, + 0x55555500, + 0xc6717100, + 0x71c67100, + 0x8e8e3800, + 0x7171c600, + 0x8e388e00, + 0x388e8e00, + 0xaaaaaa00, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x00000000, + 0x0d0d0d00, + 0x1a1a1a00, + 0x26262600, + 0x31313100, + 0x3d3d3d00, + 0x48484800, + 0x55555500, + 0x5f5f5f00, + 0x6a6a6a00, + 0x75757500, + 0x80808000, + 0x8a8a8a00, + 0x95959500, + 0xa0a0a000, + 0xaaaaaa00, + 0xb5b5b500, + 0xc0c0c000, + 0xcbcbcb00, + 0xd5d5d500, + 0xe0e0e000, + 0xeaeaea00, + 0xf5f5f500, + 0xffffff00, + 0x00000000, + 0x00240000, + 0x00480000, + 0x006d0000, + 0x00910000, + 0x00b60000, + 0x00da0000, + 0x00ff0000, + 0x3f000000, + 0x3f240000, + 0x3f480000, + 0x3f6d0000, + 0x3f910000, + 0x3fb60000, + 0x3fda0000, + 0x3fff0000, + 0x7f000000, + 0x7f240000, + 0x7f480000, + 0x7f6d0000, + 0x7f910000, + 0x7fb60000, + 0x7fda0000, + 0x7fff0000, + 0xbf000000, + 0xbf240000, + 0xbf480000, + 0xbf6d0000, + 0xbf910000, + 0xbfb60000, + 0xbfda0000, + 0xbfff0000, + 0xff000000, + 0xff240000, + 0xff480000, + 0xff6d0000, + 0xff910000, + 0xffb60000, + 0xffda0000, + 0xffff0000, + 0x00003f00, + 0x00243f00, + 0x00483f00, + 0x006d3f00, + 0x00913f00, + 0x00b63f00, + 0x00da3f00, + 0x00ff3f00, + 0x3f003f00, + 0x3f243f00, + 0x3f483f00, + 0x3f6d3f00, + 0x3f913f00, + 0x3fb63f00, + 0x3fda3f00, + 0x3fff3f00, + 0x7f003f00, + 0x7f243f00, + 0x7f483f00, + 0x7f6d3f00, + 0x7f913f00, + 0x7fb63f00, + 0x7fda3f00, + 0x7fff3f00, + 0xbf003f00, + 0xbf243f00, + 0xbf483f00, + 0xbf6d3f00, + 0xbf913f00, + 0xbfb63f00, + 0xbfda3f00, + 0xbfff3f00, + 0xff003f00, + 0xff243f00, + 0xff483f00, + 0xff6d3f00, + 0xff913f00, + 0xffb63f00, + 0xffda3f00, + 0xffff3f00, + 0x00007f00, + 0x00247f00, + 0x00487f00, + 0x006d7f00, + 0x00917f00, + 0x00b67f00, + 0x00da7f00, + 0x00ff7f00, + 0x3f007f00, + 0x3f247f00, + 0x3f487f00, + 0x3f6d7f00, + 0x3f917f00, + 0x3fb67f00, + 0x3fda7f00, + 0x3fff7f00, + 0x7f007f00, + 0x7f247f00, + 0x7f487f00, + 0x7f6d7f00, + 0x7f917f00, + 0x7fb67f00, + 0x7fda7f00, + 0x7fff7f00, + 0xbf007f00, + 0xbf247f00, + 0xbf487f00, + 0xbf6d7f00, + 0xbf917f00, + 0xbfb67f00, + 0xbfda7f00, + 0xbfff7f00, + 0xff007f00, + 0xff247f00, + 0xff487f00, + 0xff6d7f00, + 0xff917f00, + 0xffb67f00, + 0xffda7f00, + 0xffff7f00, + 0x0000bf00, + 0x0024bf00, + 0x0048bf00, + 0x006dbf00, + 0x0091bf00, + 0x00b6bf00, + 0x00dabf00, + 0x00ffbf00, + 0x3f00bf00, + 0x3f24bf00, + 0x3f48bf00, + 0x3f6dbf00, + 0x3f91bf00, + 0x3fb6bf00, + 0x3fdabf00, + 0x3fffbf00, + 0x7f00bf00, + 0x7f24bf00, + 0x7f48bf00, + 0x7f6dbf00, + 0x7f91bf00, + 0x7fb6bf00, + 0x7fdabf00, + 0x7fffbf00, + 0xbf00bf00, + 0xbf24bf00, + 0xbf48bf00, + 0xbf6dbf00, + 0xbf91bf00, + 0xbfb6bf00, + 0xbfdabf00, + 0xbfffbf00, + 0xff00bf00, + 0xff24bf00, + 0xff48bf00, + 0xff6dbf00, + 0xff91bf00, + 0xffb6bf00, + 0xffdabf00, + 0xffffbf00, + 0x0000ff00, + 0x0024ff00, + 0x0048ff00, + 0x006dff00, + 0x0091ff00, + 0x00b6ff00, + 0x00daff00, + 0x00ffff00, + 0x3f00ff00, + 0x3f24ff00, + 0x3f48ff00, + 0x3f6dff00, + 0x3f91ff00, + 0x3fb6ff00, + 0x3fdaff00, + 0x3fffff00, + 0x7f00ff00, + 0x7f24ff00, + 0x7f48ff00, + 0x7f6dff00, + 0x7f91ff00, + 0x7fb6ff00, + 0x7fdaff00, + 0x7fffff00, + 0xbf00ff00, + 0xbf24ff00, + 0xbf48ff00, + 0xbf6dff00, + 0xbf91ff00, + 0xbfb6ff00, + 0xbfdaff00, + 0xbfffff00, + 0xff00ff00, + 0xff24ff00, + 0xff48ff00, + 0xff6dff00, + 0xff91ff00, + 0xffb6ff00, + 0xffdaff00, + 0xffffff00 diff --git a/src/fl_color.cxx b/src/fl_color.cxx new file mode 100644 index 000000000..d17cfafc9 --- /dev/null +++ b/src/fl_color.cxx @@ -0,0 +1,309 @@ +// fl_color.C + +// Implementation of fl_color(i), fl_color(r,g,b). + +#ifdef WIN32 +#include "fl_color_win32.C" +#else + +// Also code to look at the X visual and figure out the best way to turn +// a color into a pixel value. + +// SGI compiler seems to have problems with unsigned char arguments +// being used to index arrays. So I always copy them to an integer +// before use. + +#include "Fl_XColor.H" +#include <FL/Fl.H> +#include <FL/x.H> +#include <FL/fl_draw.H> + +//////////////////////////////////////////////////////////////// +// figure_out_visual() calculates masks & shifts for generating +// pixels in true-color visuals: + +uchar fl_redmask, fl_greenmask, fl_bluemask; +int fl_redshift, fl_greenshift, fl_blueshift, fl_extrashift; +static uchar beenhere; + +static void figure_out_visual() { + beenhere = 1; + if (!fl_visual->red_mask || !fl_visual->green_mask || !fl_visual->blue_mask){ +#if USE_COLORMAP + fl_redmask = 0; + return; +#else + Fl::fatal("Requires true color visual"); +#endif + } + + // get the bit masks into a more useful form: + int i,j,m; + + for (i = 0, m = 1; m; i++, m<<=1) if (fl_visual->red_mask & m) break; + for (j = i; m; j++, m<<=1) if (!(fl_visual->red_mask & m)) break; + fl_redshift = j-8; + fl_redmask = (j-i >= 8) ? 0xFF : 0xFF-(255>>(j-i)); + + for (i = 0, m = 1; m; i++, m<<=1) if (fl_visual->green_mask & m) break; + for (j = i; m; j++, m<<=1) if (!(fl_visual->green_mask & m)) break; + fl_greenshift = j-8; + fl_greenmask = (j-i >= 8) ? 0xFF : 0xFF-(255>>(j-i)); + + for (i = 0, m = 1; m; i++, m<<=1) if (fl_visual->blue_mask & m) break; + for (j = i; m; j++, m<<=1) if (!(fl_visual->blue_mask & m)) break; + fl_blueshift = j-8; + fl_bluemask = (j-i >= 8) ? 0xFF : 0xFF-(255>>(j-i)); + + i = fl_redshift; + if (fl_greenshift < i) i = fl_greenshift; + if (fl_blueshift < i) i = fl_blueshift; + if (i < 0) { + fl_extrashift = -i; + fl_redshift -= i; fl_greenshift -= i; fl_blueshift -= i; + } else + fl_extrashift = 0; + +} + +//////////////////////////////////////////////////////////////// +// Get an rgb color. This is easy for a truecolor visual. For +// colormapped it picks the closest color out of the fltk colormap +// but be warned that this results in *two* approximations: one +// to the fltk colormap, and another to whatever colors X allocates. + +ulong fl_xpixel(uchar r,uchar g,uchar b) { + if (!beenhere) figure_out_visual(); +#if USE_COLORMAP + if (!fl_redmask) { + Fl_Color i; + if (r == g && r == b) { // get it out of gray ramp + i = fl_gray_ramp(r*FL_NUM_GRAY/256); + } else { // get it out of color cube: + i = fl_color_cube(r*FL_NUM_RED/256,g*FL_NUM_GREEN/256,b*FL_NUM_BLUE/256); + } + return fl_xpixel(i); + } +#endif + return + (((r&fl_redmask) << fl_redshift)+ + ((g&fl_greenmask)<<fl_greenshift)+ + ((b&fl_bluemask)<< fl_blueshift) + ) >> fl_extrashift; +} + +void fl_color(uchar r,uchar g,uchar b) { + XSetForeground(fl_display, fl_gc, fl_xpixel(r,g,b)); +} + +//////////////////////////////////////////////////////////////// +// Get a color out of the the fltk colormap. Again for truecolor +// visuals this is easy. For colormap this actually tries to allocate +// an X color, and does a least-squares match to find the closest +// color if X cannot allocate that color. + +static unsigned fl_cmap[256] = { +#include "fl_cmap.h" // this is a file produced by "cmap.C": +}; + +#if HAVE_OVERLAY +Fl_XColor fl_xmap[2][256]; +uchar fl_overlay; +Colormap fl_overlay_colormap; +XVisualInfo* fl_overlay_visual; +ulong fl_transparent_pixel; +#else +Fl_XColor fl_xmap[1][256]; +#endif + +// calculate what color is actually on the screen for a mask: +static inline uchar realcolor(uchar color, uchar mask) { +#if 1 + // accurate version if the display has linear gamma, but fl_draw_image + // works better with the simpler version on most screens... + uchar m = mask; + uchar result = color&m; + for (;;) { + while (m&mask) {m>>=1; color>>=1;} + if (!m) break; + mask = m; + result |= color&m; + } + return result; +#else + return (color&mask) | (~mask)&(mask>>1); +#endif +} + +ulong fl_xpixel(Fl_Color i) { + +#if HAVE_OVERLAY + Fl_XColor &xmap = fl_xmap[fl_overlay][i]; +#else + Fl_XColor &xmap = fl_xmap[0][i]; +#endif + if (xmap.mapped) return xmap.pixel; + + if (!beenhere) figure_out_visual(); + uchar r,g,b; + {unsigned c = fl_cmap[i]; r=uchar(c>>24); g=uchar(c>>16); b=uchar(c>>8);} + +#if USE_COLORMAP + Colormap colormap; +#if HAVE_OVERLAY + if (fl_overlay) {colormap = fl_overlay_colormap; goto J1;} +#endif + if (!fl_redmask) { + colormap = fl_colormap; +#if HAVE_OVERLAY + J1: + static XColor* ac[2]; + XColor*& allcolors = ac[fl_overlay]; + static int nc[2]; + int& numcolors = nc[fl_overlay]; +#else + static XColor *allcolors; + static int numcolors; +#endif + + // I don't try to allocate colors with XAllocColor once it fails + // with any color. It is possible that it will work, since a color + // may have been freed, but some servers are extremely slow and this + // avoids one round trip: + if (!numcolors) { // don't try after a failure + XColor xcol; + xcol.red = r<<8; xcol.green = g<<8; xcol.blue = b<<8; + if (XAllocColor(fl_display, colormap, &xcol)) { + xmap.mapped = 1; + xmap.r = xcol.red>>8; + xmap.g = xcol.green>>8; + xmap.b = xcol.blue>>8; + return xmap.pixel = xcol.pixel; + } + + // I only read the colormap once. Again this is due to the slowness + // of round-trips to the X server, even though other programs may alter + // the colormap after this and make decisions here wrong. +#if HAVE_OVERLAY + if (fl_overlay) numcolors = fl_overlay_visual->colormap_size; else +#endif + numcolors = fl_visual->colormap_size; + if (!allcolors) allcolors = new XColor[numcolors]; + for (int p = numcolors; p--;) allcolors[p].pixel = p; + XQueryColors(fl_display, colormap, allcolors, numcolors); + } + + // find least-squares match: + int mindist = 0x7FFFFFFF; + unsigned int bestmatch = 0; + for (unsigned int n = numcolors; n--;) { +#if HAVE_OVERLAY + if (fl_overlay && n == fl_transparent_pixel) continue; +#endif + XColor &a = allcolors[n]; + int d, t; + t = int(r)-int(a.red>>8); d = t*t; + t = int(g)-int(a.green>>8); d += t*t; + t = int(b)-int(a.blue>>8); d += t*t; + if (d <= mindist) {bestmatch = n; mindist = d;} + } + XColor &p = allcolors[bestmatch]; + + // It appears to "work" to not call this XAllocColor, which will + // avoid another round-trip to the server. But then X does not + // know that this program "owns" this value, and can (and will) + // change it when the program that did allocate it exits: + if (XAllocColor(fl_display, colormap, &p)) { + xmap.mapped = 1; + xmap.pixel = p.pixel; + } else { + // However, if that XAllocColor fails, I have to give up and + // assumme the pixel is ok for the duration of the program. This + // is due to bugs (?) in the Solaris X and some X terminals + // where XAllocColor *always* fails when the colormap is full, + // even if we ask for a color already in it... + xmap.mapped = 2; // 2 prevents XFreeColor from being called + xmap.pixel = bestmatch; + } + xmap.r = p.red>>8; + xmap.g = p.green>>8; + xmap.b = p.blue>>8; + return xmap.pixel; + } +#endif + // return color for a truecolor visual: + xmap.mapped = 2; // 2 prevents XFreeColor from being called + xmap.r = realcolor(r, fl_redmask); + xmap.g = realcolor(g, fl_greenmask); + xmap.b = realcolor(b, fl_bluemask); + return xmap.pixel = fl_xpixel(r,g,b); +} + +Fl_Color fl_color_; + +void fl_color(Fl_Color i) { + fl_color_ = i; + XSetForeground(fl_display, fl_gc, fl_xpixel(i)); +} + +//////////////////////////////////////////////////////////////// +// Ways to modify the fltk colormap: + +Fl_Color contrast(Fl_Color fg, Fl_Color bg) { + // bright/dark is decided based on high bit of green: + if (fl_cmap[bg] & 0x800000) { + if (fl_cmap[fg] & 0x800000) return FL_GRAY_RAMP; // black from gray ramp + } else { + if (!(fl_cmap[fg] & 0x800000)) + return (Fl_Color)(FL_COLOR_CUBE-1); // white from gray ramp + } + return fg; // this color is ok +} + +void Fl::free_color(Fl_Color i, int overlay) { +#if HAVE_OVERLAY +#else + if (overlay) return; +#endif + if (fl_xmap[overlay][i].mapped) { +#if USE_COLORMAP +#if HAVE_OVERLAY + Colormap colormap = overlay ? fl_overlay_colormap : fl_colormap; +#else + Colormap colormap = fl_colormap; +#endif + if (fl_xmap[overlay][i].mapped == 1) + XFreeColors(fl_display, colormap, &(fl_xmap[overlay][i].pixel), 1, 0); +#endif + fl_xmap[overlay][i].mapped = 0; + } +} + +void Fl::set_color(Fl_Color i, unsigned c) { + if (fl_cmap[i] != c) { + free_color(i,0); +#if HAVE_OVERLAY + free_color(i,1); +#endif + fl_cmap[i] = c; + } +} + +unsigned Fl::get_color(Fl_Color i) { + return fl_cmap[i]; +} + +void Fl::set_color(Fl_Color i, uchar red, uchar green, uchar blue) { + Fl::set_color(i, + ((unsigned)red<<24)+((unsigned)green<<16)+((unsigned)blue<<8)); +} + +void Fl::get_color(Fl_Color i, uchar &red, uchar &green, uchar &blue) { + unsigned c = fl_cmap[i]; + red = uchar(c>>24); + green = uchar(c>>16); + blue = uchar(c>>8); +} + +#endif +// end of fl_color.C diff --git a/src/fl_color_win32.cxx b/src/fl_color_win32.cxx new file mode 100644 index 000000000..360bc1b96 --- /dev/null +++ b/src/fl_color_win32.cxx @@ -0,0 +1,204 @@ +// fl_color_win32.C + +// The fltk "colormap". This allows ui colors to be stored in 8-bit +// locations, and provides a level of indirection so that global color +// changes can be made. Not to be confused with the X colormap, which +// I try to hide completely. + +// SGI compiler seems to have problems with unsigned char arguments +// being used to index arrays. So I always copy them to an integer +// before use. + +#include <config.h> +#include <FL/Fl.H> +#include <FL/win32.H> +#include <FL/fl_draw.H> + +static unsigned fl_cmap[256] = { +#include "fl_cmap.h" // this is a file produced by "cmap.C": +}; + +// Translations to win32 data structures: +Fl_XMap fl_xmap[256]; + +Fl_XMap* fl_current_xmap; + +HPALETTE fl_palette; + +static void clear_xmap(Fl_XMap& xmap) { + if (xmap.pen) { + DeleteObject((HGDIOBJ)(xmap.pen)); + xmap.pen = 0; + xmap.brush = -1; + } +} + +static void set_xmap(Fl_XMap& xmap, COLORREF c) { + xmap.rgb = c; + xmap.pen = CreatePen(PS_SOLID, 1, xmap.rgb); + xmap.brush = -1; +} + +Fl_Color fl_color_; + +void fl_color(Fl_Color i) { + fl_color_ = i; + Fl_XMap &xmap = fl_xmap[i]; + if (!xmap.pen) { +#if USE_COLORMAP + if (fl_palette) { + set_xmap(xmap, PALETTEINDEX(i)); + } else { +#endif + unsigned c = fl_cmap[i]; + set_xmap(xmap, RGB(uchar(c>>24), uchar(c>>16), uchar(c>>8))); +#if USE_COLORMAP + } +#endif + } + fl_current_xmap = ⟼ + SelectObject(fl_gc, (HGDIOBJ)(xmap.pen)); +} + +void fl_color(uchar r, uchar g, uchar b) { + static Fl_XMap xmap; + COLORREF c = RGB(r,g,b); + if (!xmap.pen || c != xmap.rgb) { + clear_xmap(xmap); + set_xmap(xmap, c); + } + fl_current_xmap = ⟼ + SelectObject(fl_gc, (HGDIOBJ)(xmap.pen)); +} + +HBRUSH fl_brush() { + Fl_XMap *xmap = fl_current_xmap; + // Wonko: we use some statistics to cache only a limited number + // of brushes: +#define FL_N_BRUSH 16 + static struct Fl_Brush { + HBRUSH brush; + unsigned short usage; + Fl_XMap* backref; + } brushes[FL_N_BRUSH]; + + int i = xmap->brush; // find the associated brush + if (i != -1) { // if the brush was allready allocated + if (brushes[i].brush == NULL) goto CREATE_BRUSH; + if ( (++brushes[i].usage) > 32000 ) { // keep a usage statistic + for (int j=0; j<FL_N_BRUSH; j++) { + if (brushes[j].usage>16000) + brushes[j].usage -= 16000; + else + brushes[j].usage = 0; + } + } + return brushes[i].brush; + } else { + int umin = 32000, imin = 0; + for (i=0; i<FL_N_BRUSH; i++) { + if (brushes[i].brush == NULL) goto CREATE_BRUSH; + if (brushes[i].usage<umin) { + umin = brushes[i].usage; + imin = i; + } + } + i = imin; + DeleteObject(brushes[i].brush); + brushes[i].brush = NULL; + brushes[i].backref->brush = -1; + } +CREATE_BRUSH: + brushes[i].brush = CreateSolidBrush(xmap->rgb); + brushes[i].usage = 0; + brushes[i].backref = xmap; + xmap->brush = i; + return brushes[i].brush; +} + +Fl_Color contrast(Fl_Color fg, Fl_Color bg) { + // bright/dark is decided based on high bit of green: + if (fl_cmap[bg] & 0x800000) { + if (fl_cmap[fg] & 0x800000) return FL_GRAY_RAMP; // black from gray ramp + } else { + if (!(fl_cmap[fg] & 0x800000)) + return (Fl_Color)(FL_COLOR_CUBE-1); // white from gray ramp + } + return fg; // this color is ok +} + +void Fl::free_color(Fl_Color i, int overlay) { + if (overlay) return; // do something about GL overlay? + clear_xmap(fl_xmap[i]); +} + +void Fl::set_color(Fl_Color i, unsigned c) { + if (fl_cmap[i] != c) { + clear_xmap(fl_xmap[i]); + fl_cmap[i] = c; + } +} + +unsigned Fl::get_color(Fl_Color i) { + return fl_cmap[i]; +} + +void Fl::set_color(Fl_Color i, uchar red, uchar green, uchar blue) { + Fl::set_color(i, + ((unsigned)red<<24)+((unsigned)green<<16)+((unsigned)blue<<8)); +} + +void Fl::get_color(Fl_Color i, uchar &red, uchar &green, uchar &blue) { + unsigned c = fl_cmap[i]; + red = uchar(c>>24); + green = uchar(c>>16); + blue = uchar(c>>8); +} + +#if USE_COLORMAP + +// 'fl_select_palette()' - Make a color palette for 8-bit displays if necessary +// Thanks to Michael Sweet @ Easy Software Products for this + +HPALETTE +fl_select_palette(void) +{ + static char beenhere; + if (!beenhere) { + beenhere = 1; + + //if (GetDeviceCaps(fl_gc, BITSPIXEL) > 8) return NULL; + int nColors = GetDeviceCaps(fl_gc, SIZEPALETTE); + if (nColors <= 0 || nColors > 256) return NULL; + // this will try to work on < 256 color screens, but will probably + // come out quite badly. + + // I lamely try to get this variable-sized object allocated on stack: + ulong foo[(sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY))/sizeof(ulong)+1]; + LOGPALETTE *pPal = (LOGPALETTE*)foo; + + pPal->palVersion = 0x300; + pPal->palNumEntries = nColors; + + // Build 256 colors from the standard FLTK colormap... + + for (int i = 0; i < nColors; i ++) { + pPal->palPalEntry[i].peRed = (fl_cmap[i] >> 24) & 255; + pPal->palPalEntry[i].peGreen = (fl_cmap[i] >> 16) & 255; + pPal->palPalEntry[i].peBlue = (fl_cmap[i] >> 8) & 255; + pPal->palPalEntry[i].peFlags = 0; + }; + + // Create the palette: + fl_palette = CreatePalette(pPal); + } + if (fl_palette) { + SelectPalette(fl_gc, fl_palette, FALSE); + RealizePalette(fl_gc); + } + return fl_palette; +} + +#endif + +// end of fl_color_win32.C diff --git a/src/fl_cursor.cxx b/src/fl_cursor.cxx new file mode 100644 index 000000000..e058c3f7c --- /dev/null +++ b/src/fl_cursor.cxx @@ -0,0 +1,145 @@ +// fl_cursor.C + +// Change the current cursor. + +// Under X the cursor is attached to the X window. I tried to hide +// this and pretend that changing the cursor is a drawing function. +// This avoids a field in the Fl_Window, and I suspect is more +// portable to other systems. + +#include <FL/Fl.H> +#include <FL/Fl_Window.H> +#include <FL/x.H> +#ifndef WIN32 +#include <X11/cursorfont.h> +#endif +#include <FL/fl_draw.H> + +void fl_cursor(Fl_Cursor c, Fl_Color fg, Fl_Color bg) { + if (Fl::first_window()) Fl::first_window()->cursor(c,fg,bg); +} + +#ifdef WIN32 + +void Fl_Window::cursor(Fl_Cursor c, Fl_Color, Fl_Color) { + if (!shown()) return; + if (c > FL_CURSOR_NESW) { + i->cursor = 0; + } else if (c == FL_CURSOR_DEFAULT) { + i->cursor = fl_default_cursor; + } else { + LPSTR n; + switch (c) { + case FL_CURSOR_ARROW: n = IDC_ARROW; break; + case FL_CURSOR_CROSS: n = IDC_CROSS; break; + case FL_CURSOR_WAIT: n = IDC_WAIT; break; + case FL_CURSOR_INSERT: n = IDC_IBEAM; break; + case FL_CURSOR_HELP: n = IDC_HELP; break; + case FL_CURSOR_HAND: n = IDC_UPARROW; break; + case FL_CURSOR_MOVE: n = IDC_SIZEALL; break; + case FL_CURSOR_N: + case FL_CURSOR_S: + case FL_CURSOR_NS: n = IDC_SIZENS; break; + case FL_CURSOR_NE: + case FL_CURSOR_SW: + case FL_CURSOR_NESW: n = IDC_SIZENESW; break; + case FL_CURSOR_E: + case FL_CURSOR_W: + case FL_CURSOR_WE: n = IDC_SIZEWE; break; + case FL_CURSOR_SE: + case FL_CURSOR_NW: + case FL_CURSOR_NWSE: n = IDC_SIZENWSE; break; + default: n = IDC_NO; break; + } + i->cursor = LoadCursor(NULL, n); + } + SetCursor(i->cursor); +} + +#else + +// I like the MSWindows resize cursors, so I duplicate them here: + +#define CURSORSIZE 16 +#define HOTXY 7 +struct TableEntry { + uchar bits[CURSORSIZE*CURSORSIZE/8]; + uchar mask[CURSORSIZE*CURSORSIZE/8]; + Cursor cursor; +} table[] = { + {{ // FL_CURSOR_NS + 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0x80, 0x01, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00}, + { + 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, 0xf0, 0x0f, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, + 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01}}, + {{ // FL_CURSOR_EW + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, + 0x0c, 0x30, 0xfe, 0x7f, 0xfe, 0x7f, 0x0c, 0x30, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x1c, 0x38, + 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0x1c, 0x38, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{ // FL_CURSOR_NWSE + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x38, 0x00, 0x78, 0x00, + 0xe8, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x1c, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + { + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x7c, 0x00, 0xfc, 0x00, + 0xfc, 0x01, 0xec, 0x03, 0xc0, 0x37, 0x80, 0x3f, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00}}, + {{ // FL_CURSOR_NESW + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1e, + 0x00, 0x17, 0x80, 0x03, 0xc0, 0x01, 0xe8, 0x00, 0x78, 0x00, 0x38, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3f, + 0x80, 0x3f, 0xc0, 0x37, 0xec, 0x03, 0xfc, 0x01, 0xfc, 0x00, 0x7c, 0x00, + 0xfc, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0}, {0}} // FL_CURSOR_NONE & unknown +}; + +void Fl_Window::cursor(Fl_Cursor c, Fl_Color fg, Fl_Color bg) { + if (!shown()) return; + Cursor cursor; + int deleteit = 0; + if (!c) { + cursor = None; + } else { + if (c >= FL_CURSOR_NS) { + TableEntry *q = (c > FL_CURSOR_NESW) ? table+4 : table+(c-FL_CURSOR_NS); + if (!(q->cursor)) { + XColor dummy; + Pixmap p = XCreateBitmapFromData(fl_display, + RootWindow(fl_display, fl_screen), (const char*)(q->bits), + CURSORSIZE, CURSORSIZE); + Pixmap m = XCreateBitmapFromData(fl_display, + RootWindow(fl_display, fl_screen), (const char*)(q->mask), + CURSORSIZE, CURSORSIZE); + q->cursor = XCreatePixmapCursor(fl_display, p,m,&dummy, &dummy, + HOTXY, HOTXY); + XFreePixmap(fl_display, m); + XFreePixmap(fl_display, p); + } + cursor = q->cursor; + } else { + cursor = XCreateFontCursor(fl_display, (c-1)*2); + deleteit = 1; + } + XColor fgc; + uchar r,g,b; + Fl::get_color(fg,r,g,b); + fgc.red = r<<8; fgc.green = g<<8; fgc.blue = b<<8; + XColor bgc; + Fl::get_color(bg,r,g,b); + bgc.red = r<<8; bgc.green = g<<8; bgc.blue = b<<8; + XRecolorCursor(fl_display, cursor, &fgc, &bgc); + } + XDefineCursor(fl_display, fl_xid(this), cursor); + if (deleteit) XFreeCursor(fl_display, cursor); +} + +#endif diff --git a/src/fl_curve.cxx b/src/fl_curve.cxx new file mode 100644 index 000000000..0fc125e89 --- /dev/null +++ b/src/fl_curve.cxx @@ -0,0 +1,73 @@ +// fl_curve.C + +// Utility for drawing Bezier curves, adding the points to +// the current fl_begin/fl_vertex/fl_end path. + +// Incremental math implementation: +// I very much doubt this is optimal! From Foley/vanDam page 511. +// If anybody has a better algorithim, please send it! + +#include <FL/fl_draw.H> +#include <math.h> + +void fl_curve(double X0, double Y0, + double X1, double Y1, + double X2, double Y2, + double X3, double Y3) { + double x = fl_transform_x(X0,Y0); + double y = fl_transform_y(X0,Y0); + double x1 = fl_transform_x(X1,Y1); + double y1 = fl_transform_y(X1,Y1); + double x2 = fl_transform_x(X2,Y2); + double y2 = fl_transform_y(X2,Y2); + double x3 = fl_transform_x(X3,Y3); + double y3 = fl_transform_y(X3,Y3); + + int n; { // find smaller size of bounding box + double lx = x; if (x1<lx) lx=x1; if (x2<lx) lx=x2; if (x3<lx) lx=x3; + double rx = x; if (x1>rx) rx=x1; if (x2>rx) rx=x2; if (x3>rx) rx=x3; + double ly = y; if (y1<ly) ly=y1; if (y2<ly) ly=y2; if (y3<ly) ly=y3; + double ry = y; if (y1>ry) ry=y1; if (y2>ry) ry=y2; if (y3>ry) ry=y3; + // calculate number of pieces to cut curve into: + n = int((rx-lx+ry-ly)/8); if (n < 3) n = 3; + } + double e = 1.0/n; + + // calculate the coefficients of 3rd order equation: + double xa = (x3-3*x2+3*x1-x); + double xb = 3*(x2-2*x1+x); + double xc = 3*(x1-x); + // calculate the forward differences: + double dx1 = ((xa*e+xb)*e+xc)*e; + double dx3 = 6*xa*e*e*e; + double dx2 = dx3 + 2*xb*e*e; + + // calculate the coefficients of 3rd order equation: + double ya = (y3-3*y2+3*y1-y); + double yb = 3*(y2-2*y1+y); + double yc = 3*(y1-y); + // calculate the forward differences: + double dy1 = ((ya*e+yb)*e+yc)*e; + double dy3 = 6*ya*e*e*e; + double dy2 = dy3 + 2*yb*e*e; + + // draw point 0: + fl_transformed_vertex(x,y); + + // draw points 1 .. n-2: + for (int m=2; m<n; m++) { + x += dx1; + dx1 += dx2; + dx2 += dx3; + y += dy1; + dy1 += dy2; + dy2 += dy3; + fl_transformed_vertex(x,y); + } + + // draw point n-1: + fl_transformed_vertex(x+dx1, y+dy1); + + // draw point n: + fl_transformed_vertex(x3,y3); +} diff --git a/src/fl_diamond_box.cxx b/src/fl_diamond_box.cxx new file mode 100644 index 000000000..81e1c27cf --- /dev/null +++ b/src/fl_diamond_box.cxx @@ -0,0 +1,49 @@ +// fl_diamond_box.C + +// Box drawing code for an obscure box type. +// These box types are in seperate files so they are not linked +// in if not used. + +// The diamond box draws best if the area is square! + +#include <FL/Fl.H> +#include <FL/fl_draw.H> + +extern uchar* Fl_Gray_Ramp; + +static void fl_diamond_up_box(int x,int y,int w,int h,Fl_Color bgcolor) { + w &= -2; + h &= -2; + int x1 = x+w/2; + int y1 = y+h/2; + fl_color(bgcolor); fl_polygon(x+3, y1, x1,y+3, x+w-3,y1, x1,y+h-3); + fl_color(Fl_Gray_Ramp['W']); fl_line(x+1, y1, x1, y+1, x+w-1, y1); + fl_color(Fl_Gray_Ramp['U']); fl_line(x+2, y1, x1, y+2, x+w-2, y1); + fl_color(Fl_Gray_Ramp['S']); fl_line(x+3, y1, x1, y+3, x+w-3, y1); + fl_color(Fl_Gray_Ramp['P']); fl_line(x+3, y1, x1, y+h-3, x+w-3, y1); + fl_color(Fl_Gray_Ramp['N']); fl_line(x+2, y1, x1, y+h-2, x+w-2, y1); + fl_color(Fl_Gray_Ramp['H']); fl_line(x+1, y1, x1, y+h-1, x+w-1, y1); + fl_color(Fl_Gray_Ramp['A']); fl_loop(x, y1, x1, y, x+w, y1, x1, y+h); +} + +static void fl_diamond_down_box(int x,int y,int w,int h,Fl_Color bgcolor) { + w &= -2; + h &= -2; + int x1 = x+w/2; + int y1 = y+h/2; + fl_color(Fl_Gray_Ramp['P']); fl_line(x+0, y1, x1, y+0, x+w-0, y1); + fl_color(Fl_Gray_Ramp['N']); fl_line(x+1, y1, x1, y+1, x+w-1, y1); + fl_color(Fl_Gray_Ramp['H']); fl_line(x+2, y1, x1, y+2, x+w-2, y1); + fl_color(Fl_Gray_Ramp['W']); fl_line(x+2, y1, x1, y+h-2, x+w-2, y1); + fl_color(Fl_Gray_Ramp['U']); fl_line(x+1, y1, x1, y+h-1, x+w-1, y1); + fl_color(Fl_Gray_Ramp['S']); fl_line(x+0, y1, x1, y+h-0, x+w-0, y1); + fl_color(bgcolor); fl_polygon(x+3, y1, x1,y+3, x+w-3,y1, x1,y+h-3); + fl_color(Fl_Gray_Ramp['A']); fl_loop(x+3, y1, x1, y+3, x+w-3, y1, x1, y+h-3); +} + +extern void fl_internal_boxtype(Fl_Boxtype, Fl_Box_Draw_F*); +Fl_Boxtype define_FL_DIAMOND_BOX() { + fl_internal_boxtype(_FL_DIAMOND_DOWN_BOX, fl_diamond_down_box); + fl_internal_boxtype(_FL_DIAMOND_UP_BOX,fl_diamond_up_box); + return _FL_DIAMOND_UP_BOX; +} diff --git a/src/fl_draw.cxx b/src/fl_draw.cxx new file mode 100644 index 000000000..eda4bff29 --- /dev/null +++ b/src/fl_draw.cxx @@ -0,0 +1,175 @@ +// fl_draw.C + +// Implementation of fl_draw(const char*,int,int,int,int,Fl_Align) + +// Used to draw all the labels and text, this routine: + +// Word wraps the labels to fit into their bounding box. +// Breaks them into lines at the newlines. +// Expands all unprintable characters to ^X or \nnn notation +// Aligns them against the inside of the box. + +#include <FL/fl_draw.H> +#include <string.h> + +#define MAXBUF 1024 + +char fl_draw_shortcut; // set by fl_labeltypes.C +static char* underline_at; + +// Copy p to buf, replacing unprintable characters with ^X and \nnn +// Stop at a newline of if MAXBUF characters written to buffer. +// Also word-wrap if width exceeds maxw. +// Returns a pointer to the start of the next line of caharcters. +// Sets n to the number of characters put into the buffer. +// Sets width to the width of the string in the current font. + +static const char* +expand(const char* from, char* buf, double maxw, int& n, double &width, int wrap) { + + char* o = buf; + char* e = buf+(MAXBUF-4); + underline_at = 0; + char* word_end = o; + const char* word_start = from; + double w = 0; + + const char* p = from; + for (;; p++) { + + int c = *p & 255; + + if (!c || c == ' ' || c == '\n') { + // test for word-wrap: + if (word_start < p && wrap) { + double newwidth = w + fl_width(word_end, o-word_end); + if (word_end > buf && newwidth > maxw) { // break before this word + o = word_end; + p = word_start; + break; + } + word_end = o; + w = newwidth; + } + if (!c) break; + else if (c == '\n') {p++; break;} + word_start = p+1; + } + + if (o > e) break; // don't overflow buffer + + if (c == '\t') { + for (c = (o-buf)%8; c<8 && o<e; c++) *o++ = ' '; + + } else if (c == '&' && fl_draw_shortcut && *(p+1)) { + if (*(p+1) == '&') {p++; *o++ = '&';} + else if (fl_draw_shortcut != 2) underline_at = o; + + } else if (c < ' ' || c == 127) { // ^X + *o++ = '^'; + *o++ = c ^ 0x40; + + } else if (c >= 128 && c < 0xA0) { // \nnn + *o++ = '\\'; + *o++ = (c>>6)+'0'; + *o++ = ((c>>3)&7)+'0'; + *o++ = (c&7)+'0'; + + } else if (c == 0xA0) { // non-breaking space + *o++ = ' '; + + } else { + *o++ = c; + + } + } + + width = w + fl_width(word_end, o-word_end); + *o = 0; + n = o-buf; + return p; +} + +void fl_draw( + const char* str, // the (multi-line) string + int x, int y, int w, int h, // bounding box + Fl_Align align, + void (*callthis)(const char*,int,int,int) +) { + if (!str || !*str) return; + + // clip: + if (w && h && !fl_not_clipped(x, y, w, h)) return; + if (align & FL_ALIGN_CLIP) fl_clip(x, y, w, h); + + const char* p; + const char* e; + char buf[MAXBUF]; + int buflen; + + // count how many lines and put the last one into the buffer: + int lines; + double width; + for (p=str,lines=0; ;) { + e = expand(p, buf, w, buflen, width, align&FL_ALIGN_WRAP); + lines++; + if (!*e) break; + p = e; + } + + // figure out vertical position of the first line: + int ypos; + int height = fl_height(); + if (align & FL_ALIGN_BOTTOM) ypos = y+h-(lines-1)*height; + else if (align & FL_ALIGN_TOP) ypos = y+height; + else ypos = y+(h-lines*height)/2+height; + + // now draw all the lines: + int desc = fl_descent(); + for (p=str; ; ypos += height) { + if (lines>1) e = expand(p, buf, w, buflen, width, align&FL_ALIGN_WRAP); + + int xpos; + if (align & FL_ALIGN_LEFT) xpos = x; + else if (align & FL_ALIGN_RIGHT) xpos = x+w-int(width+.5); + else xpos = x+int((w-width)/2); + + callthis(buf,buflen,xpos,ypos-desc); + + if (underline_at) + callthis("_",1,xpos+int(fl_width(buf,underline_at-buf)),ypos-desc); + + if (!*e) break; + p = e; + } + + if (align & FL_ALIGN_CLIP) fl_pop_clip(); +} + +void fl_draw( + const char* str, // the (multi-line) string + int x, int y, int w, int h, // bounding box + Fl_Align align) { + fl_draw(str, x, y, w, h, align, fl_draw); +} + +void fl_measure(const char* str, int& w, int& h) { + h = fl_height(); + if (!str || !*str) {w = 0; return;} + const char* p; + const char* e; + char buf[MAXBUF]; + int buflen; + int lines; + double width; + int W = 0; + for (p=str,lines=0; ;) { + e = expand(p, buf, w, buflen, width, w!=0); + if (int(width) > W) W = int(width); + lines++; + if (!*e) break; + p = e; + } + w = W; + h = lines*h; +} diff --git a/src/fl_draw_image.cxx b/src/fl_draw_image.cxx new file mode 100644 index 000000000..e66384c56 --- /dev/null +++ b/src/fl_draw_image.cxx @@ -0,0 +1,605 @@ +// fl_draw_image.C + +// I hope a simple and portable method of drawing color and monochrome +// images. To keep this simple, only a single storage type is +// supported: 8 bit unsigned data, byte order RGB, and pixels are +// stored packed into rows with the origin at the top-left. It is +// possible to alter the size of pixels with the "delta" argument, to +// add alpha or other information per pixel. It is also possible to +// change the origin and direction of the image data by messing with +// the "delta" and "linedelta", making them negative, though this may +// defeat some of the shortcuts in translating the image for X. + +#ifdef WIN32 +#include "fl_draw_image_win32.C" +#else + +// A list of assumptions made about the X display: + +// bits_per_pixel must be one of 8, 16, 24, 32. + +// scanline_pad must be a power of 2 and greater or equal to 8. + +// PsuedoColor visuals must have 8 bits_per_pixel (although the depth +// may be less than 8). This is the only limitation that affects any +// modern X displays, you can't use 12 or 16 bit colormaps. + +// The mask bits in TrueColor visuals for each color are +// contiguous and have at least one bit of each color. This +// is not checked for. + +// For 24 and 32 bit visuals there must be at least 8 bits of each color. + +//////////////////////////////////////////////////////////////// + +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <FL/x.H> +#include "Fl_XColor.H" + +static XImage i; // template used to pass info to X +static int bytes_per_pixel; +static int scanline_add; +static int scanline_mask; + +static void (*converter)(const uchar *from, uchar *to, int w, int delta); +static void (*mono_converter)(const uchar *from, uchar *to, int w, int delta); + +static int dir; // direction-alternator +static int ri,gi,bi; // saved error-diffusion value + +#if USE_COLORMAP +//////////////////////////////////////////////////////////////// +// 8-bit converter with error diffusion + +// I make a 16x16x16 cube of the closest colors in the fltk colormap +// we could allocate to each of the colors in a 4-bit image. This is +// then used to find the pixel values and actual colors for error diffusion. +static uchar cube[16*16*16]; + +// calculate sum-of-squares error between 4-bit index and pixel colors: +static int calc_error(int r, int g, int b, int i) { + int t; int s; + t = ((r<<4)+8)-fl_xmap[0][i].r; s = t*t; + t = ((g<<4)+8)-fl_xmap[0][i].g; s += t*t; + t = ((b<<4)+8)-fl_xmap[0][i].b; s += t*t; + return s; +} + +// replace the color stored at a location with a better one: +static void improve(uchar *p, int& e, int r, int g, int b, int i) { + if (i < FL_GRAY_RAMP || i > 255) return; + int e1 = calc_error(r,g,b,i); + if (e1 < e) {*p = i; e = e1;} +} + +static int filled_color_cube; +static void fill_color_cube() { + filled_color_cube = 1; + int i; + // allocate all the colors in the fltk color cube and gray ramp: + // allocate widely seperated values first so that the bad ones are + // distributed evenly through the colormap: + for (i=0;;) { + fl_xpixel((Fl_Color)(i+FL_COLOR_CUBE)); + i = (i+109)%(FL_NUM_RED*FL_NUM_GREEN*FL_NUM_BLUE); if (!i) break; + } + for (i=0;;) { + fl_xpixel((Fl_Color)(i+FL_GRAY_RAMP)); + i = (i+7)%FL_NUM_GRAY; if (!i) break; + } + // fill in the 16x16x16 cube: + uchar *p = cube; + for (int r = 0; r<16; r++) { + for (int g = 0; g<16; g++) { + for (int b = 0; b<16; b++, p++) { + // initial try is value from color cube: + Fl_Color i = fl_color_cube(r*FL_NUM_RED/16, g*FL_NUM_GREEN/16, + b*FL_NUM_BLUE/16); + int e = calc_error(r,g,b,i); + *p = uchar(i); + // try neighbor pixels in the cube to see if they are better: + improve(p,e,r,g,b,i+FL_NUM_RED*FL_NUM_GREEN); + improve(p,e,r,g,b,i-FL_NUM_RED*FL_NUM_GREEN); + improve(p,e,r,g,b,i+FL_NUM_GREEN); + improve(p,e,r,g,b,i-FL_NUM_GREEN); + improve(p,e,r,g,b,i+1); + improve(p,e,r,g,b,i-1); + // try the gray ramp: + i = fl_gray_ramp(g*FL_NUM_GRAY/15); + improve(p,e,r,g,b,i); + improve(p,e,r,g,b,i+1); + improve(p,e,r,g,b,i-1); + } + } + } +} + +static void color8_converter(const uchar *from, uchar *to, int w, int delta) { + if (!filled_color_cube) fill_color_cube(); + int r=ri, g=gi, b=bi; + int d, td; + if (dir) { + dir = 0; + from = from+(w-1)*delta; + to = to+(w-1); + d = -delta; + td = -1; + } else { + dir = 1; + d = delta; + td = 1; + } + for (; w--; from += d, to += td) { + r += from[0]; if (r < 0) r = 0; else if (r>255) r = 255; + g += from[1]; if (g < 0) g = 0; else if (g>255) g = 255; + b += from[2]; if (b < 0) b = 0; else if (b>255) b = 255; + Fl_XColor* x = fl_xmap[0] + cube[((r<<4)&0xf00)+(g&0xf0)+(b>>4)]; + r -= x->r; + g -= x->g; + b -= x->b; + *to = uchar(x->pixel); + } + ri = r; gi = g; bi = b; +} + +static void mono8_converter(const uchar *from, uchar *to, int w, int delta) { + if (!filled_color_cube) fill_color_cube(); + int r=ri; + int d, td; + if (dir) { + dir = 0; + from = from+(w-1)*delta; + to = to+(w-1); + d = -delta; + td = -1; + } else { + dir = 1; + d = delta; + td = 1; + } + for (; w--; from += d, to += td) { + r += from[0]; if (r < 0) r = 0; else if (r>255) r = 255; + Fl_XColor* x = fl_xmap[0] + cube[(r>>4)*0x111]; + r -= x->g; + *to = uchar(x->pixel); + } + ri = r; +} + +#endif + +//////////////////////////////////////////////////////////////// +// 16 bit TrueColor converters with error diffusion +// Cray computers have no 16-bit type, so we use character pointers +// (which may be slow) + +#ifdef U16 +#define OUTTYPE U16 +#define OUTSIZE 1 +#define OUTASSIGN(v) *t = v +#else +#define OUTTYPE uchar +#define OUTSIZE 2 +#define OUTASSIGN(v) int tt=v; t[0] = uchar(tt>>8); t[1] = uchar(tt) +#endif + +static void color16_converter(const uchar *from, uchar *to, int w, int delta) { + OUTTYPE *t = (OUTTYPE *)to; + int d, td; + if (dir) { + dir = 0; + from = from+(w-1)*delta; + t = t+(w-1)*OUTSIZE; + d = -delta; + td = -OUTSIZE; + } else { + dir = 1; + d = delta; + td = OUTSIZE; + } + int r=ri, g=gi, b=bi; + for (; w--; from += d, t += td) { + r = (r&~fl_redmask) +from[0]; if (r>255) r = 255; + g = (g&~fl_greenmask)+from[1]; if (g>255) g = 255; + b = (b&~fl_bluemask) +from[2]; if (b>255) b = 255; + OUTASSIGN(( + ((r&fl_redmask)<<fl_redshift)+ + ((g&fl_greenmask)<<fl_greenshift)+ + ((b&fl_bluemask)<<fl_blueshift) + ) >> fl_extrashift); + } + ri = r; gi = g; bi = b; +} + +static void mono16_converter(const uchar *from,uchar *to,int w, int delta) { + OUTTYPE *t = (OUTTYPE *)to; + int d, td; + if (dir) { + dir = 0; + from = from+(w-1)*delta; + t = t+(w-1)*OUTSIZE; + d = -delta; + td = -OUTSIZE; + } else { + dir = 1; + d = delta; + td = OUTSIZE; + } + uchar mask = fl_redmask & fl_greenmask & fl_bluemask; + int r=ri; + for (; w--; from += d, t += td) { + r = (r&~mask) + *from; if (r > 255) r = 255; + uchar m = r&mask; + OUTASSIGN(( + (m<<fl_redshift)+ + (m<<fl_greenshift)+ + (m<<fl_blueshift) + ) >> fl_extrashift); + } + ri = r; +} + +// special-case the 5r6g5b layout used by XFree86: + +static void c565_converter(const uchar *from, uchar *to, int w, int delta) { + OUTTYPE *t = (OUTTYPE *)to; + int d, td; + if (dir) { + dir = 0; + from = from+(w-1)*delta; + t = t+(w-1)*OUTSIZE; + d = -delta; + td = -OUTSIZE; + } else { + dir = 1; + d = delta; + td = OUTSIZE; + } + int r=ri, g=gi, b=bi; + for (; w--; from += d, t += td) { + r = (r&7)+from[0]; if (r>255) r = 255; + g = (g&3)+from[1]; if (g>255) g = 255; + b = (b&7)+from[2]; if (b>255) b = 255; + OUTASSIGN(((r&0xf8)<<8) + ((g&0xfc)<<3) + (b>>3)); + } + ri = r; gi = g; bi = b; +} + +static void m565_converter(const uchar *from,uchar *to,int w, int delta) { + OUTTYPE *t = (OUTTYPE *)to; + int d, td; + if (dir) { + dir = 0; + from = from+(w-1)*delta; + t = t+(w-1)*OUTSIZE; + d = -delta; + td = -OUTSIZE; + } else { + dir = 1; + d = delta; + td = OUTSIZE; + } + int r=ri; + for (; w--; from += d, t += td) { + r = (r&7) + *from; if (r > 255) r = 255; + OUTASSIGN((r>>3) * 0x841); + } + ri = r; +} + +//////////////////////////////////////////////////////////////// +// 24bit TrueColor converters: + +static void rgb_converter(const uchar *from, uchar *to, int w, int delta) { + int d = delta-3; + for (; w--; from += d) { + *to++ = *from++; + *to++ = *from++; + *to++ = *from++; + } +} + +static void bgr_converter(const uchar *from, uchar *to, int w, int delta) { + for (; w--; from += delta) { + uchar r = from[0]; + uchar g = from[1]; + *to++ = from[2]; + *to++ = g; + *to++ = r; + } +} + +static void rrr_converter(const uchar *from, uchar *to, int w, int delta) { + for (; w--; from += delta) { + *to++ = *from; + *to++ = *from; + *to++ = *from; + } +} + +//////////////////////////////////////////////////////////////// +// 32bit TrueColor converters on a 32 or 64-bit machine: + +#ifdef U64 +#define STORETYPE U64 +#if WORDS_BIGENDIAN +#define INNARDS32(f) \ + U64 *t = (U64*)to; \ + int w1 = (w+1)/2; \ + for (; w1--; from += delta) {U64 i = f; from += delta; *t++ = (i<<32)|(f);} +#else +#define INNARDS32(f) \ + U64 *t = (U64*)to; \ + int w1 = (w+1)/2; \ + for (; w1--; from += delta) {U64 i=f; from+= delta; *t++ = ((U64)(f)<<32)|i;} +#endif +#else +#define STORETYPE U32 +#define INNARDS32(f) \ + U32 *t = (U32*)to; for (; w--; from += delta) *t++ = f +#endif + +static void rgbx_converter(const uchar *from, uchar *to, int w, int delta) { + INNARDS32((unsigned(from[0])<<24)+(from[1]<<16)+(from[2]<<8)); +} + +static void xbgr_converter(const uchar *from, uchar *to, int w, int delta) { + INNARDS32((from[0])+(from[1]<<8)+(from[2]<<16)); +} + +static void xrgb_converter(const uchar *from, uchar *to, int w, int delta) { + INNARDS32((from[0]<<16)+(from[1]<<8)+(from[2])); +} + +static void bgrx_converter(const uchar *from, uchar *to, int w, int delta) { + INNARDS32((from[0]<<8)+(from[1]<<16)+(unsigned(from[2])<<24)); +} + +static void rrrx_converter(const uchar *from, uchar *to, int w, int delta) { + INNARDS32(unsigned(*from) * 0x1010100U); +} + +static void xrrr_converter(const uchar *from, uchar *to, int w, int delta) { + INNARDS32(*from * 0x10101U); +} + +static void +color32_converter(const uchar *from, uchar *to, int w, int delta) { + INNARDS32( + (from[0]<<fl_redshift)+(from[1]<<fl_greenshift)+(from[2]<<fl_blueshift)); +} + +static void +mono32_converter(const uchar *from,uchar *to,int w, int delta) { + INNARDS32( + (*from << fl_redshift)+(*from << fl_greenshift)+(*from << fl_blueshift)); +} + +//////////////////////////////////////////////////////////////// + +static void figure_out_visual() { + + static XPixmapFormatValues *pfvlist; + static int FL_NUM_pfv; + if (!pfvlist) pfvlist = XListPixmapFormats(fl_display,&FL_NUM_pfv); + XPixmapFormatValues *pfv; + for (pfv = pfvlist; pfv < pfvlist+FL_NUM_pfv; pfv++) + if (pfv->depth == fl_visual->depth) break; + i.format = ZPixmap; + i.byte_order = ImageByteOrder(fl_display); +//i.bitmap_unit = 8; +//i.bitmap_bit_order = MSBFirst; +//i.bitmap_pad = 8; + i.depth = fl_visual->depth; + i.bits_per_pixel = pfv->bits_per_pixel; + + if (i.bits_per_pixel & 7) bytes_per_pixel = 0; // produce fatal error + else bytes_per_pixel = i.bits_per_pixel/8; + + unsigned int n = pfv->scanline_pad/8; + if (pfv->scanline_pad & 7 || (n&(n-1))) + Fl::fatal("Can't do scanline_pad of %d",pfv->scanline_pad); + if (n < sizeof(STORETYPE)) n = sizeof(STORETYPE); + scanline_add = n-1; + scanline_mask = -n; + +#if USE_COLORMAP + if (bytes_per_pixel == 1) { + converter = color8_converter; + mono_converter = mono8_converter; + return; + } + if (!fl_visual->red_mask) + Fl::fatal("Can't do %d bits_per_pixel colormap",i.bits_per_pixel); +#endif + + // otherwise it is a TrueColor visual: + fl_xpixel(0,0,0); // setup fl_redmask, etc, in fl_color.C + + int rs = fl_redshift; + int gs = fl_greenshift; + int bs = fl_blueshift; + + switch (bytes_per_pixel) { + + case 2: + // All 16-bit TrueColor visuals are supported on any machine with + // 24 or more bits per integer. +#ifdef U16 + ::i.byte_order = WORDS_BIGENDIAN; +#else + ::i.byte_order = 1; +#endif + if (rs == 11 && gs == 6 && bs == 0 && fl_extrashift == 3) { + converter = c565_converter; + mono_converter = m565_converter; + } else { + converter = color16_converter; + mono_converter = mono16_converter; + } + break; + + case 3: + if (::i.byte_order) {rs = 16-rs; gs = 16-gs; bs = 16-bs;} + if (rs == 0 && gs == 8 && bs == 16) { + converter = rgb_converter; + mono_converter = rrr_converter; + } else if (rs == 16 && gs == 8 && bs == 0) { + converter = bgr_converter; + mono_converter = rrr_converter; + } else { + Fl::fatal("Can't do arbitrary 24bit color"); + } + break; + + case 4: + if ((::i.byte_order!=0) != WORDS_BIGENDIAN) + {rs = 24-rs; gs = 24-gs; bs = 24-bs;} + if (rs == 0 && gs == 8 && bs == 16) { + converter = xbgr_converter; + mono_converter = xrrr_converter; + } else if (rs == 24 && gs == 16 && bs == 8) { + converter = rgbx_converter; + mono_converter = rrrx_converter; + } else if (rs == 8 && gs == 16 && bs == 24) { + converter = bgrx_converter; + mono_converter = rrrx_converter; + } else if (rs == 16 && gs == 8 && bs == 0) { + converter = xrgb_converter; + mono_converter = xrrr_converter; + } else { + ::i.byte_order = WORDS_BIGENDIAN; + converter = color32_converter; + mono_converter = mono32_converter; + } + break; + + default: + Fl::fatal("Can't do %d bits_per_pixel",i.bits_per_pixel); + } + +} + +#define MAXBUFFER 0x40000 // 256k + +static void innards(const uchar *buf, int X, int Y, int W, int H, + int delta, int linedelta, int mono, + Fl_Draw_Image_Cb cb, void* userdata) +{ + if (!linedelta) linedelta = W*delta; + + int dx, dy, w, h; + fl_clip_box(X,Y,W,H,dx,dy,w,h); + if (w<=0 || h<=0) return; + dx -= X; + dy -= Y; + + if (!bytes_per_pixel) figure_out_visual(); + i.width = w; + i.height = h; + + void (*conv)(const uchar *from, uchar *to, int w, int delta) = converter; + if (mono) conv = mono_converter; + + // See if the data is already in the right format. Unfortunately + // some 32-bit x servers (XFree86) care about the unknown 8 bits + // and they must be zero. I can't confirm this for user-supplied + // data, so the 32-bit shortcut is disabled... + // This can set bytes_per_line negative if image is bottom-to-top + // I tested it on Linux, but it may fail on other Xlib implementations: + if (buf && ( +#if 0 // set this to 1 to allow 32-bit shortcut + delta == 4 && +#if WORDS_BIGENDIAN + conv == rgbx_converter +#else + conv == xbgr_converter +#endif + || +#endif + conv == rgb_converter && delta==3 + ) && !(linedelta&scanline_add)) { + i.data = (char *)(buf+delta*dx+linedelta*dy); + i.bytes_per_line = linedelta; + + } else { + int linesize = ((w*bytes_per_pixel+scanline_add)&scanline_mask)/sizeof(STORETYPE); + int blocking = h; + static STORETYPE *buffer; // our storage, always word aligned + static long buffer_size; + {int size = linesize*h; + if (size > MAXBUFFER) { + size = MAXBUFFER; + blocking = MAXBUFFER/linesize; + } + if (size > buffer_size) { + delete[] buffer; + buffer_size = size; + buffer = new STORETYPE[size]; + }} + i.data = (char *)buffer; + i.bytes_per_line = linesize*sizeof(STORETYPE); + if (buf) { + buf += delta*dx+linedelta*dy; + for (int j=0; j<h; ) { + STORETYPE *to = buffer; + int k; + for (k = 0; j<h && k<blocking; k++, j++) { + conv(buf, (uchar*)to, w, delta); + buf += linedelta; + to += linesize; + } + XPutImage(fl_display,fl_window,fl_gc, &i, 0, 0, X+dx, Y+dy+j-k, w, k); + } + } else { +#ifdef __GNUC__ + STORETYPE linebuf[(W*delta+(sizeof(STORETYPE)-1))/sizeof(STORETYPE)]; +#else + STORETYPE* linebuf = new STORETYPE[(W*delta+(sizeof(STORETYPE)-1))/sizeof(STORETYPE)]; +#endif + for (int j=0; j<h; ) { + STORETYPE *to = buffer; + int k; + for (k = 0; j<h && k<blocking; k++, j++) { + cb(userdata, dx, dy+j, w, (uchar*)linebuf); + conv((uchar*)linebuf, (uchar*)to, w, delta); + to += linesize; + } + XPutImage(fl_display,fl_window,fl_gc, &i, 0, 0, X+dx, Y+dy+j-k, w, k); + } +#ifndef __GNUC__ + delete[] linebuf; +#endif + } + } +} + +void fl_draw_image(const uchar* buf, int x, int y, int w, int h, int d, int l){ + innards(buf,x,y,w,h,d,l,(d<3&&d>-3),0,0); +} +void fl_draw_image(Fl_Draw_Image_Cb cb, void* data, + int x, int y, int w, int h,int d) { + innards(0,x,y,w,h,d,0,(d<3&&d>-3),cb,data); +} +void fl_draw_image_mono(const uchar* buf, int x, int y, int w, int h, int d, int l){ + innards(buf,x,y,w,h,d,l,1,0,0); +} +void fl_draw_image_mono(Fl_Draw_Image_Cb cb, void* data, + int x, int y, int w, int h,int d) { + innards(0,x,y,w,h,d,0,1,cb,data); +} + +void fl_rectf(int x, int y, int w, int h, uchar r, uchar g, uchar b) { + if (fl_visual->depth > 16) { + fl_color(r,g,b); + fl_rectf(x,y,w,h); + } else { + uchar c[3]; + c[0] = r; c[1] = g; c[2] = b; + innards(c,x,y,w,h,0,0,0,0,0); + } +} + +#endif +// End of fl_draw_image.C diff --git a/src/fl_draw_image_win32.cxx b/src/fl_draw_image_win32.cxx new file mode 100644 index 000000000..ec58452c1 --- /dev/null +++ b/src/fl_draw_image_win32.cxx @@ -0,0 +1,237 @@ +// fl_draw_image_win32.C + +// I hope a simple and portable method of drawing color and monochrome +// images. To keep this simple, only a single storage type is +// supported: 8 bit unsigned data, byte order RGB, and pixels are +// stored packed into rows with the origin at the top-left. It is +// possible to alter the size of pixels with the "delta" argument, to +// add alpha or other information per pixel. It is also possible to +// change the origin and direction of the image data by messing with +// the "delta" and "linedelta", making them negative, though this may +// defeat some of the shortcuts in translating the image for X. + +// Unbelievably (since it conflicts with how most PC software works) +// Micro$oft picked a bottom-up and BGR storage format for their +// DIB images. I'm pretty certain there is a way around this, but +// I can't find any other than the brute-force method of drawing +// each line as a seperate image. This may also need to be done +// if the delta is any amount other than 1, 3, or 4. + +//////////////////////////////////////////////////////////////// + +#include <config.h> +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <FL/x.H> + +#define MAXBUFFER 0x40000 // 256k + +#if USE_COLORMAP + +// error-diffusion dither into the fltk colormap +static void dither(uchar* to, const uchar* from, int w, int delta) { + static int ri, gi, bi, dir; + int r=ri, g=gi, b=bi; + int d, td; + if (dir) { + dir = 0; + from = from+(w-1)*delta; + to = to+(w-1); + d = -delta; + td = -1; + } else { + dir = 1; + d = delta; + td = 1; + } + for (; w--; from += d, to += td) { + r += from[0]; if (r < 0) r = 0; else if (r>255) r = 255; + int rr = r*FL_NUM_RED/256; + r -= rr*255/(FL_NUM_RED-1); + g += from[1]; if (g < 0) g = 0; else if (g>255) g = 255; + int gg = g*FL_NUM_GREEN/256; + g -= gg*255/(FL_NUM_GREEN-1); + b += from[2]; if (b < 0) b = 0; else if (b>255) b = 255; + int bb = b*FL_NUM_BLUE/256; + b -= bb*255/(FL_NUM_BLUE-1); + *to = uchar(FL_COLOR_CUBE+(bb*FL_NUM_RED+rr)*FL_NUM_GREEN+gg); + } + ri = r; gi = g; bi = b; +} + +// error-diffusion dither into the fltk colormap +static void monodither(uchar* to, const uchar* from, int w, int delta) { + static int ri,dir; + int r=ri; + int d, td; + if (dir) { + dir = 0; + from = from+(w-1)*delta; + to = to+(w-1); + d = -delta; + td = -1; + } else { + dir = 1; + d = delta; + td = 1; + } + for (; w--; from += d, to += td) { + r += *from; if (r < 0) r = 0; else if (r>255) r = 255; + int rr = r*FL_NUM_GRAY/256; + r -= rr*255/(FL_NUM_GRAY-1); + *to = uchar(FL_GRAY_RAMP+rr); + } + ri = r; +} + +#endif // USE_COLORMAP + +static void innards(const uchar *buf, int X, int Y, int W, int H, + int delta, int linedelta, int mono, + Fl_Draw_Image_Cb cb, void* userdata) +{ +#if USE_COLORMAP + char indexed = (fl_palette != 0); +#endif + + if (!linedelta) linedelta = W*delta; + + int x, y, w, h; + fl_clip_box(X,Y,W,H,x,y,w,h); + if (w<=0 || h<=0) return; + if (buf) buf += (x-X)*delta + (y-Y)*linedelta; + + static U32 bmibuffer[256+12]; + BITMAPINFO &bmi = *((BITMAPINFO*)bmibuffer); + if (!bmi.bmiHeader.biSize) { + bmi.bmiHeader.biSize = sizeof(bmi)-4; // does it use this to determine type? + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biXPelsPerMeter = 0; + bmi.bmiHeader.biYPelsPerMeter = 0; + bmi.bmiHeader.biClrUsed = 0; + bmi.bmiHeader.biClrImportant = 0; + } +#if USE_COLORMAP + if (indexed) { + for (int i=0; i<256; i++) { + *((short*)(bmi.bmiColors)+i) = i; + } + } else +#endif + if (mono) { + for (int i=0; i<256; i++) { + bmi.bmiColors[i].rgbBlue = i; + bmi.bmiColors[i].rgbGreen = i; + bmi.bmiColors[i].rgbRed = i; + bmi.bmiColors[i].rgbReserved = i; + } + } + bmi.bmiHeader.biWidth = w; +#if USE_COLORMAP + bmi.bmiHeader.biBitCount = mono|indexed ? 8 : 24; + int pixelsize = mono|indexed ? 1 : 3; +#else + bmi.bmiHeader.biBitCount = mono ? 8 : 24; + int pixelsize = mono ? 1 : 3; +#endif + int linesize = (pixelsize*w+3)&~3; + + static U32* buffer; + int blocking = h; + {int size = linesize*h; + if (size > MAXBUFFER) { + size = MAXBUFFER; + blocking = MAXBUFFER/linesize; + } + static long buffer_size; + if (size > buffer_size) { + delete[] buffer; + buffer_size = size; + buffer = new U32[(size+3)/4]; + }} + bmi.bmiHeader.biHeight = blocking; + static U32* line_buffer; + if (!buf) { + int size = W*delta; + static int line_buf_size; + if (size > line_buf_size) { + delete[] line_buffer; + line_buf_size = size; + line_buffer = new U32[(size+3)/4]; + } + } + for (int j=0; j<h; ) { + int k; + for (k = 0; j<h && k<blocking; k++, j++) { + const uchar* from; + if (!buf) { // run the converter: + cb(userdata, x-X, y-Y+j, w, (uchar*)line_buffer); + from = (uchar*)line_buffer; + } else { + from = buf; + buf += linedelta; + } + uchar *to = (uchar*)buffer+(blocking-k-1)*linesize; +#if USE_COLORMAP + if (indexed) { + if (mono) + monodither(to, from, w, delta); + else + dither(to, from, w, delta); + to += w; + } else +#endif + if (mono) { + for (int i=w; i--; from += delta) *to++ = *from; + } else { + for (int i=w; i--; from += delta, to += 3) { + uchar r = from[0]; + to[0] = from[2]; + to[1] = from[1]; + to[2] = r; + } + } + } + SetDIBitsToDevice(fl_gc, x, y+j-k, w, k, 0, 0, 0, k, + (LPSTR)((uchar*)buffer+(blocking-k)*linesize), + &bmi, +#if USE_COLORMAP + indexed ? DIB_PAL_COLORS : DIB_RGB_COLORS +#else + DIB_RGB_COLORS +#endif + ); + } +} + +void fl_draw_image(const uchar* buf, int x, int y, int w, int h, int d, int l){ + innards(buf,x,y,w,h,d,l,(d<3&&d>-3),0,0); +} +void fl_draw_image(Fl_Draw_Image_Cb cb, void* data, + int x, int y, int w, int h,int d) { + innards(0,x,y,w,h,d,0,(d<3&&d>-3),cb,data); +} +void fl_draw_image_mono(const uchar* buf, int x, int y, int w, int h, int d, int l){ + innards(buf,x,y,w,h,d,l,1,0,0); +} +void fl_draw_image_mono(Fl_Draw_Image_Cb cb, void* data, + int x, int y, int w, int h,int d) { + innards(0,x,y,w,h,d,0,1,cb,data); +} + +void fl_rectf(int x, int y, int w, int h, uchar r, uchar g, uchar b) { +#if USE_COLORMAP + // use the error diffusion dithering code to produce a much nicer block: + if (fl_palette) { + uchar c[3]; + c[0] = r; c[1] = g; c[2] = b; + innards(c,x,y,w,h,0,0,0,0,0); + return; + } +#endif + fl_color(r,g,b); + fl_rectf(x,y,w,h); +} + +// End of fl_draw_image_win32.C diff --git a/src/fl_draw_pixmap.cxx b/src/fl_draw_pixmap.cxx new file mode 100644 index 000000000..2522093c0 --- /dev/null +++ b/src/fl_draw_pixmap.cxx @@ -0,0 +1,224 @@ +// fl_draw_pixmap.C + +// Implemented without using the xpm library (which I can't use because +// it interferes with the color cube used by fl_draw_image). + +// Current implementation is cheap and slow, and works best on a full-color +// display. Transparency is not handled, and colors are dithered to +// the color cube. Color index is achieved by adding the id +// characters together! Also mallocs a lot of temporary memory! + +// Notice that there is no pixmap file interface. This is on purpose, +// as I want to discourage programs that require support files to work. +// All data needed by a program ui should be compiled in!!! + +#include <config.h> +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <FL/x.H> +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +static int ncolors, chars_per_pixel; + +int fl_measure_pixmap(/*const*/char * const *data, int &w, int &h) { + int i = sscanf(data[0],"%d %d %d %d",&w,&h,&ncolors,&chars_per_pixel); + if (i<4 || w<=0 || h<=0 || + chars_per_pixel!=1 && chars_per_pixel!=2) return w=0; + return 1; +} + +#ifdef U64 + +// The callback from fl_draw_image to get a row of data passes this: +struct pixmap_data { + int w, h; + char*const* data; + U64 colors[256]; +}; + +// callback for 1 byte per pixel: +static void cb1(void*v, int x, int y, int w, uchar* buf) { + pixmap_data& d = *(pixmap_data*)v; + const char* p = d.data[y]+x; + U64* q = (U64*)buf; + for (int X=(w+1)/2; X--; p += 2) { +#if WORDS_BIGENDIAN + *q++ = (d.colors[p[0]&255]<<32) | d.colors[p[1]&255]; +#else + *q++ = (d.colors[p[1]&255]<<32) | d.colors[p[0]&255]; +#endif + } +} + +// callback for 2 bytes per pixel: +static void cb2(void*v, int x, int y, int w, uchar* buf) { + pixmap_data& d = *(pixmap_data*)v; + const char* p = d.data[y]+2*x; + U64* q = (U64*)buf; + for (int X=(w+1)/2; X--;) { + int index = *p++; int t = *p++; index += (t<<4)+(t>>4); + int index1= *p++; t = *p++; index1 += (t<<4)+(t>>4); +#if WORDS_BIGENDIAN + *q++ = (d.colors[index&255]<<32) | d.colors[index1&255]; +#else + *q++ = (d.colors[index1&255]<<32) | d.colors[index&255]; +#endif + } +} + +#else + +// The callback from fl_draw_image to get a row of data passes this: +struct pixmap_data { + int w, h; + char*const* data; + U32 colors[256]; +}; + +// callback for 1 byte per pixel: +static void cb1(void*v, int x, int y, int w, uchar* buf) { + pixmap_data& d = *(pixmap_data*)v; + const char* p = d.data[y]+x; + U32* q = (U32*)buf; + for (int X=w; X--;) *q++ = d.colors[(*p++)&255]; +} + +// callback for 2 bytes per pixel: +static void cb2(void*v, int x, int y, int w, uchar* buf) { + pixmap_data& d = *(pixmap_data*)v; + const char* p = d.data[y]+2*x; + U32* q = (U32*)buf; + for (int X=w; X--;) { + int index = *p++; int t = *p++; index += (t<<4)+(t>>4); + *q++ = d.colors[index&255]; + } +} + +#endif + +#ifdef WIN32 +// this is in Fl_arg.C: +extern int fl_parse_color(const char*, uchar&, uchar&, uchar&); +#endif + +uchar **fl_mask_bitmap; // if non-zero, create bitmap and store pointer here + +int fl_draw_pixmap(/*const*/char*const* data, int x, int y, Fl_Color bg) { + pixmap_data d; + if (!fl_measure_pixmap(data, d.w, d.h)) return 0; + data++; + int transparent_index = -1; + + if (ncolors < 0) { // fltk (non standard) compressed colormap + ncolors = -ncolors; + const char *p = *data++; + // if first color is ' ' it is transparent (put it later to make + // it not be transparent): + if (*p == ' ') { + uchar* c = (uchar*)&d.colors[' ']; +#ifdef U64 + *(U64*)c = 0; +#if WORDS_BIGENDIAN + c += 4; +#endif +#endif + transparent_index = ' '; + Fl::get_color(bg, c[0], c[1], c[2]); c[3] = 0; + p += 4; + ncolors--; + } + // read all the rest of the colors: + for (int i=0; i < ncolors; i++) { + uchar* c = (uchar*)&d.colors[(*p++)&255]; +#ifdef U64 + *(U64*)c = 0; +#if WORDS_BIGENDIAN + c += 4; +#endif +#endif + *c++ = *p++; + *c++ = *p++; + *c++ = *p++; + *c = 0; + } + } else { // normal XPM colormap with names + for (int i=0; i<ncolors; i++) { + const char *p = *data++; + // the first 1 or 2 characters are the color index: + int index = *p++; + if (chars_per_pixel>1) {int t = *p++; index += (t<<4)+(t>>4);} + // look for "c word", or last word if none: + const char *previous_word = p; + for (;;) { + while (*p && isspace(*p)) p++; char what = *p++; + while (*p && !isspace(*p)) p++; + while (*p && isspace(*p)) p++; + if (!*p) {p = previous_word; break;} + if (what == 'c') break; + previous_word = p; + while (*p && !isspace(*p)) p++; + } + // copy the color name and look it up: + char name[256]; + char *q; for (q = name; *p && !isspace(*p); *q++ = *p++); *q++ = 0; + uchar *c = (uchar *)&d.colors[index&255]; +#ifdef U64 + *(U64*)c = 0; +#if WORDS_BIGENDIAN + c += 4; +#endif +#endif +#ifdef WIN32 + if (fl_parse_color(name, c[0], c[1], c[2])) {; +#else + XColor x; + if (XParseColor(fl_display, fl_colormap, name, &x)) { + c[0] = x.red>>8; c[1] = x.green>>8; c[2] = x.blue>>8; +#endif + } else { // assumme "None" or "#transparent" for any errors + // this should be transparent... + Fl::get_color(bg, c[0], c[1], c[2]); + transparent_index = index&255; + } + } + } + d.data = data; + + // build the mask bitmap used by Fl_Pixmap: + if (fl_mask_bitmap && transparent_index >= 0) { + int W = (d.w+7)/8; + uchar *bitmap = new uchar[W * d.h]; + *fl_mask_bitmap = bitmap; + for (int y = 0; y < d.h; y++) { + uchar* p = (uchar*)data[y]; + if (chars_per_pixel <= 1) { + for (int x = 0; x < W; x++) { + int b = (*p++ != transparent_index); + if (*p++ != transparent_index) b |= 2; + if (*p++ != transparent_index) b |= 4; + if (*p++ != transparent_index) b |= 8; + if (*p++ != transparent_index) b |= 16; + if (*p++ != transparent_index) b |= 32; + if (*p++ != transparent_index) b |= 64; + if (*p++ != transparent_index) b |= 128; + *bitmap++ = b; + } + } else { + for (int x = 0; x < W; x++) { + int b = 0; + for (int i = 0; i < 8; i++) { + int index = *p++; int t = *p++; index += (t<<4)+(t>>4); + if ((index&255) != transparent_index) b |= (1<<i); + } + *bitmap++ = b; + } + } + } + } + + fl_draw_image(chars_per_pixel==1 ? cb1 : cb2, &d, x, y, d.w, d.h, 4); + return 1; +} + diff --git a/src/fl_engraved_label.cxx b/src/fl_engraved_label.cxx new file mode 100644 index 000000000..07a6523a7 --- /dev/null +++ b/src/fl_engraved_label.cxx @@ -0,0 +1,64 @@ +// fl_engraved_label.C + +// Drawing code for XForms style engraved & embossed labels + +#include <FL/Fl.H> +#include <FL/Fl_Widget.H> +#include <FL/fl_draw.H> + +// data[] is dx, dy, color triples + +static void innards( + const Fl_Label* o, int X, int Y, int W, int H, Fl_Align align, + int data[][3], int n) +{ + Fl_Align a1 = align; + if (a1 & FL_ALIGN_CLIP) { + fl_clip(X, Y, W, H); a1 = (Fl_Align)(a1&~FL_ALIGN_CLIP);} + fl_font((Fl_Font)o->font, o->size); + for (int i = 0; i < n; i++) { + fl_color((Fl_Color)(i < n-1 ? data[i][2] : o->color)); + fl_draw(o->value, X+data[i][0], Y+data[i][1], W, H, a1); + } + if (align & FL_ALIGN_CLIP) fl_pop_clip(); +} + +static void fl_shadow_label( + const Fl_Label* o, int X, int Y, int W, int H, Fl_Align align) +{ + static int data[2][3] = {{2,2,FL_DARK3},{0,0,0}}; + innards(o, X, Y, W, H, align, data, 2); +} + +static void fl_engraved_label( + const Fl_Label* o, int X, int Y, int W, int H, Fl_Align align) +{ + static int data[7][3] = { + {1,0,FL_LIGHT3},{1,1,FL_LIGHT3},{0,1,FL_LIGHT3}, + {-1,0,FL_DARK3},{-1,-1,FL_DARK3},{0,-1,FL_DARK3}, + {0,0,0}}; + innards(o, X, Y, W, H, align, data, 7); +} + +static void fl_embossed_label( + const Fl_Label* o, int X, int Y, int W, int H, Fl_Align align) +{ + static int data[7][3] = { + {-1,0,FL_LIGHT3},{-1,-1,FL_LIGHT3},{0,-1,FL_LIGHT3}, + {1,0,FL_DARK3},{1,1,FL_DARK3},{0,1,FL_DARK3}, + {0,0,0}}; + innards(o, X, Y, W, H, align, data, 7); +} + +Fl_Labeltype define_FL_SHADOW_LABEL() { + Fl::set_labeltype(_FL_SHADOW_LABEL, fl_shadow_label, 0); + return _FL_SHADOW_LABEL; +} +Fl_Labeltype define_FL_ENGRAVED_LABEL() { + Fl::set_labeltype(_FL_ENGRAVED_LABEL, fl_engraved_label, 0); + return _FL_ENGRAVED_LABEL; +} +Fl_Labeltype define_FL_EMBOSSED_LABEL() { + Fl::set_labeltype(_FL_EMBOSSED_LABEL, fl_embossed_label, 0); + return _FL_EMBOSSED_LABEL; +} diff --git a/src/fl_file_chooser.cxx b/src/fl_file_chooser.cxx new file mode 100644 index 000000000..35cbeb747 --- /dev/null +++ b/src/fl_file_chooser.cxx @@ -0,0 +1,600 @@ +// fl_file_chooser.c + +// fltk (Fast Light Tool Kit) version 0.99 +// Copyright (C) 1998 Bill Spitzak + +// The "completion" file chooser for fltk +// Designed and implemented by Bill Spitzak 12/17/93 +// Rewritten for fltk 4/29/96 +// Rewritten to use scandir() 1/7/97 + +#include <config.h> +#include <FL/fl_file_chooser.H> + +#include <FL/Fl.H> +#include <FL/Fl_Window.H> +#include <FL/Fl_Box.H> +#include <FL/Fl_Button.H> +#include <FL/Fl_Return_Button.H> +#include <FL/Fl_Browser_.H> +#include <FL/Fl_Input.H> +#include <FL/fl_draw.H> +#include <FL/filename.H> + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +static void default_callback(const char*) {} +static void (*current_callback)(const char*) = default_callback; +void fl_file_chooser_callback(void (*cb)(const char*)) { + current_callback = cb ? cb : default_callback; +} + +// "File Chooser Browser" widget: +class FCB : public Fl_Browser_ { + void* item_first() const ; + void* item_next(void*) const ; + void* item_prev(void*) const ; + int item_height(const dirent*, int) const ; + int item_height(void*) const ; + int item_width(const dirent*) const ; + int item_width(void*) const ; + int item_quick_height(void*) const ; + int incr_height() const ; + void item_draw(void*, int, int, int, int) const ; + int checkdir(const dirent*, char*) const ; + void draw(); + void clear_prev(); +public: + char listed[FL_PATH_MAX];// current dir & starname + int dirend; // points after last / before starname + int nameend; // length to trailing '*' or '\0' + const char* pattern; // default pattern + dirent** list; // the file names + dirent** last; // pointer after end of list + const char* message; // message if no file names + char preved[FL_PATH_MAX];// directory listed in prev + dirent** prev; // cached list of another directory + dirent** prev_last; // end of that list + int prev_count; + FCB(int x, int y, int w, int h) : Fl_Browser_(x, y, w, h, 0) { + type(FL_HOLD_BROWSER); + listed[0] = 0; + dirend = nameend = 1; + pattern = 0; + list = prev = 0; + message = 0; + } + // ~FCB nyi + void clear(); + void set(const char*); + int get(char*); +}; + +// "File Chooser Window" widget: +class FCW : public Fl_Window { +public: + int handle(int); + Fl_Input input; + Fl_Button* ok_button; + Fl_Button* cancel_button; + Fl_Button* normal_button; + FCB browser; + FCW(); +}; + +/* Files are marked as being directories by replacing the trailing null + with a '/' if it is a directory, a '\001' if it is *not* a directory. + An item has height (and is thus selectable) if it is either a directory + or if it matches the pattern. Quick-height assummes all unknown files + are directories, and thus saves the time needed to do a stat(). +*/ + +// return pointer to last character: +static const char* end_of_name(const dirent* d) { +#if HAVE_DIRENT_H + const char* e; + for (e = d->d_name; ;e++) switch (*e) { + case 0: case 1: case '/': return e; + } +#else + // warning: clobbers byte after end of name + return d->d_name + d->d_namelen; +#endif +} + +// return true if item is directory, when given pointer to last character: +int FCB::checkdir(const dirent* d, char* e) const { + if (*e == 1) return 0; + if (*e == '/') return 1; + char buf[FL_PATH_MAX]; + memcpy(buf, listed, dirend); + memcpy(buf+dirend, d->d_name, e-d->d_name); + *(buf+dirend+(e-d->d_name)) = 0; + if (filename_isdir(buf)) { + *e = '/'; return 1; + } else { + *e = 1; return 0; + } +} + +void* FCB::item_first() const {return list;} + +void* FCB::item_next(void* p) const { + if ((dirent**)p+1 >= last) return 0; + return (dirent**)p+1; +} + +void* FCB::item_prev(void* p) const { + if ((dirent**)p <= list) return 0; + return ((dirent**)p)-1; +} + +static int ido_matching(const dirent* p, const char* e, const char* n) { + // replace / or 1 at end with 0 and do match, then put back. yukko + int save = *e; *(char*)e = 0; + int r = filename_match(p->d_name, n); + *(char*)e = save; + return(r); +} + +int FCB::incr_height() const {return textsize()+2;} + +int FCB::item_height(const dirent* p, int slow) const { + const char* e = end_of_name(p); + if (listed[dirend]) { +// if (p->d_name[0]=='.' && listed[dirend]!='.') return 0; + if (listed[nameend-1]=='/') { + if (slow ? !checkdir(p, (char*)e) : *e==1) return 0; + ((char*)listed)[nameend-1] = 0; + int r = ido_matching(p, e, listed+dirend); + ((char*)listed)[nameend-1] = '/'; + if (!r) return 0; + } else { + if (!ido_matching(p, e, listed+dirend)) return 0; + } + } else { + if (p->d_name[0]=='.') return 0; + if (pattern && (slow ? !checkdir(p, (char*)e) : *e==1) && + !ido_matching(p, e, pattern)) return 0; + } + return textsize()+2; +} + +int FCB::item_height(void* x) const { + return item_height(*(const dirent**)x, 1); +} + +int FCB::item_quick_height(void* x) const { + return item_height(*(const dirent**)x, 0); +} + +void FCB::item_draw(void* v, int x, int y, int, int h) const { + const dirent* p = *(const dirent**)v; + const char* e = end_of_name(p); + if (checkdir(p, (char*)e)) e++; + if (v == selection()) fl_color(contrast(textcolor(), selection_color())); + else fl_color(textcolor()); + fl_font(textfont(), textsize(), default_font(), default_size()); + fl_draw(p->d_name, e-p->d_name, x+4, y+h-3); +} + +int FCB::item_width(const dirent* p) const { + const char* e = end_of_name(p); if (*e == '/') e++; + fl_font(textfont(), textsize(), default_font(), default_size()); + return (int)fl_width(p->d_name, e-p->d_name)+4; +} + +int FCB::item_width(void* x) const { + return item_width(*(const dirent**)x); +} + +// "get" the current value by copying the name of the selected file +// or if none are selected, by copying as many common letters as +// possible of the matched file list: +int FCB::get(char* buf) { + dirent** q = (dirent**)selection(); // the file to copy from + int n = 0; // number of letters + if (q) { // a file is selected + const char* e = end_of_name(*q); + n = e - (*q)->d_name; + if (*e == '/') n++; + } else { // do filename completion + for (q = list; q < last && !item_height(*q, 0); q++); + if (q < last) { + const char* e = end_of_name(*q); + n = e - (*q)->d_name; + if (*e == '/') n++; + for (dirent** r = q+1; n && r < last; r++) { + if (!item_height(*r, 0)) continue; + int i; + for (i=0; i<n && (*q)->d_name[i]==(*r)->d_name[i]; i++); + n = i; + } + } + } + if (n) { + memcpy(buf, listed, dirend); + memcpy(buf+dirend, (*q)->d_name, n); + buf[dirend+n]=0; + } + return n; +} + +// "set" the current value by changing the directory being listed and +// changing the highlighted item, if possible: +void FCB::set(const char* buf) { + + int bufdirend; + int ispattern = 0; + const char* c = buf; + for (bufdirend=0; *c;) switch(*c++) { + case '?': case '[': case '*': case '{': ispattern = 1; goto BREAK; +#if defined(WIN32) || defined(__EMX__) + case '\\': +#endif + case '/': bufdirend=c-buf; break; + } +#if defined(WIN32) || defined(__EMX__) + if ((!bufdirend) && isalpha(buf[0]) && (buf[1]==':')) bufdirend = 2; +#endif + BREAK: + int bufend = strlen(buf); + if (bufend<=bufdirend) ispattern = 1; + + // if directory is different, change list to xxx/ : + if (bufdirend != dirend || strncmp(buf, listed, bufdirend)) { + if (prev && + preved[bufdirend]==0 && !strncmp(buf, preved, bufdirend)) { + strcpy(preved, listed); preved[dirend] = 0; + dirent** t; + t = prev; prev = list; list = t; + t = prev_last; prev_last = last; last = t; + strcpy(listed, buf); + dirend = nameend = bufdirend; + message = 0; + } else { + if (list) { + clear_prev(); + strcpy(preved, listed); preved[dirend]=0; + prev = list; + prev_last = last; + } + list = last = 0; + message = "reading..."; redraw(); Fl::flush(); + strcpy(listed, buf); + dirend = nameend = bufdirend; + listed[dirend] = listed[dirend+1] = 0; + int n = filename_list(dirend ? listed : ".", &list); + if (n < 0) { + if (errno==ENOENT) message = "No such directory"; + else message = strerror(errno); + n = 0; list = 0; + } else message = 0; + last = list+n; + } + if (list && last <= list+2) message = "Empty directory"; + new_list(); + } + + dirent** q = 0; // will point to future selection + int any = 0; // true if any names shown + + // do we match one item in the previous list? + if (!ispattern && bufend >= nameend) { + for (q = list; ; q++) { + if (q >= last) {q = 0; break;} + if (item_height(*q, 0)==0) continue; + any = 1; + const char* a = (*q)->d_name; + const char* b = buf+bufdirend; + while (*b && *a==*b) {a++; b++;} + if (!*b && (*a==0 || /* *a=='/' ||*/ *a==1)) break; + } + } + + // no, change the list pattern to the new text + a star: + if (!q) { + strcpy(listed+dirend, buf+bufdirend); + nameend = bufend; + if (!ispattern) {listed[nameend]='*'; listed[nameend+1]=0;} + any = 0; + // search again for an exact match: + for (q = list; ; q++) { + if (q >= last) {q = 0; break;} + if (item_height(*q, 0)==0) continue; + any = 1; + const char* a = (*q)->d_name; + const char* b = buf+bufdirend; + while (*b && *a==*b) {a++; b++;} + if (!*b && (*a==0 || /* *a=='/' ||*/ *a==1)) break; + } + new_list(); + } + + if (any) message = 0; + else if (!message) message = "No matching files"; + select_only(q); + if (q) current_callback(buf); +} + +void FCB::draw() { + if (message) { + Fl_Boxtype b = box(); if (!b) b = Fl_Input_::default_box(); + draw_box(b,color()); + fl_color(FL_INACTIVE_COLOR); + fl_draw(message, x()+7, y()+3, w(), h()-3, FL_ALIGN_TOP_LEFT); + } else { + Fl_Browser_::draw(); + if (full_height()<=0) { + message = "No matching files"; + draw(); + } + } +} + +void FCB::clear_prev() { + if (prev) { + for (dirent**p=prev_last-1; p>=prev; p--) free((void*)*p); + free((void*)prev); + prev = prev_last = 0; + } +} + +void FCB::clear() { + if (list) { + for (dirent**p=last-1; p>=list; p--) free((void*)*p); + free((void*)list); + list = last = 0; + } + clear_prev(); + listed[0] = 0; dirend = 1; +} + +//////////////////////////////////////////////////////////////// + +static void fcb_cb(Fl_Widget*, void* v) { + FCW* w = (FCW*)v; + char buf[FL_PATH_MAX]; + if (w->browser.get(buf)) { + w->input.value(buf); + w->input.position(10000); +// w->input.position(10000, w->browser.dirend); + if (Fl::event_button()==1) { + if (Fl::event_clicks()) w->ok_button->do_callback(); + else w->browser.set(buf); + } else { + current_callback(buf); + } + } +} + +static void tab_cb(Fl_Widget*, void* v) { + FCW* w = (FCW*)v; + char buf[FL_PATH_MAX]; + if (w->browser.get(buf)) { + w->input.value(buf); + w->input.position(10000); + w->browser.set(buf); + } +} + +#if defined(WIN32) || defined(__EMX__) +// ':' needs very special handling! +static inline int isdirsep(char c) {return c=='/' || c=='\\';} +#else +#define isdirsep(c) ((c)=='/') +#endif + +static void input_cb(Fl_Widget*, void* v) { + FCW* w = (FCW*)v; + const char* buf = w->input.value(); + char localbuf[FL_PATH_MAX]; + if (buf[0] && isdirsep(buf[w->input.size()-1]) + && filename_expand(localbuf, buf)) { + buf = localbuf; + w->input.value(localbuf); + w->input.position(10000); + } + w->browser.set(buf); +} + +static void up_cb(Fl_Widget*, void* v) { // the .. button + FCW* w = (FCW*)v; + char* p; + char* newname; + char buf[FL_PATH_MAX]; + p = w->browser.listed+w->browser.dirend-1; // point right before last '/' + if (p < w->browser.listed) + newname = "../"; // go up from current directory + else { + for (; p>w->browser.listed; p--) if (isdirsep(*(p-1))) break; + if (isdirsep(*p) || *p=='.' && + (isdirsep(p[1]) || p[1]=='.' && isdirsep(p[2]))) { + p = w->browser.listed+w->browser.dirend; + memcpy(buf, w->browser.listed, p-w->browser.listed); + strcpy(buf+(p-w->browser.listed), "../"); + } else { + memcpy(buf, w->browser.listed, p-w->browser.listed); + buf[p-w->browser.listed] = 0; + } + newname = buf; + } + w->input.value(newname); + w->input.position(10000); + w->browser.set(newname); +} + +static void dir_cb(Fl_Widget* obj, void* v) { // directory buttons + FCW* w = (FCW*)v; + const char* p = obj->label(); if (*p=='&') p++; + char buf[FL_PATH_MAX]; + char* q; for (q=buf; *p && *p!=' '; *q++ = *p++); *q = 0; + filename_expand(buf, buf); + w->input.value(buf); + w->input.position(10000); + w->browser.set(buf); +} + +static void working_cb(Fl_Widget*, void* v) { // directory buttons + FCW*w = (FCW*)v; + char buf[FL_PATH_MAX]; + filename_absolute(buf, ""); + w->input.value(buf); + w->input.position(10000); + w->browser.set(buf); +} + +static void files_cb(Fl_Widget* obj, void* v) { // file pattern buttons + FCW* w = (FCW*)v; + char buf[FL_PATH_MAX]; + strcpy(buf, w->input.value()); + char* q = buf+w->browser.dirend; + if (obj != w->normal_button) { // tack on first word of label + const char* p = obj->label(); if (*p=='&') p++; + for (; *p && *p!=' '; *q++ = *p++); + } + *q = 0; + w->input.value(buf); + w->input.position(10000, w->browser.dirend); + w->browser.set(buf); +} + +/*----------------------- The Main Routine ----------------------*/ +#define HEIGHT_BOX (4*WIDTH_SPC+HEIGHT_BUT+HEIGHT_INPUT+HEIGHT_BROWSER) +#define HEIGHT_BUT 25 +#define HEIGHT_INPUT 30 +#define HEIGHT_BROWSER (9*HEIGHT_BUT+2) // must be > buttons*HEIGHT_BUT +#define WIDTH_BOX (3*WIDTH_SPC+WIDTH_BUT+WIDTH_BROWSER) +#define WIDTH_BROWSER 350 +#define WIDTH_BUT 125 +#define WIDTH_OK 70 +#define WIDTH_SPC 5 + +int FCW::handle(int event) { + if (Fl_Window::handle(event)) return 1; + if (event==FL_KEYBOARD && Fl::event_key()==FL_Tab) { + tab_cb(this, this); + return 1; + } + return 0; +} + +// set this to make extra directory-jumping button: +const char* fl_file_chooser_button; +extern const char* fl_ok; +extern const char* fl_cancel; + +FCW::FCW() : Fl_Window(WIDTH_BOX, HEIGHT_BOX), + input(WIDTH_SPC, HEIGHT_BOX-HEIGHT_BUT-2*WIDTH_SPC-HEIGHT_INPUT, + WIDTH_BOX-2*WIDTH_SPC, HEIGHT_INPUT, 0), + browser(2*WIDTH_SPC+WIDTH_BUT, WIDTH_SPC, + WIDTH_BROWSER, HEIGHT_BROWSER) +{ + int but_y = WIDTH_SPC; + input.callback(input_cb, this); + input.when(FL_WHEN_CHANGED); + // add(browser); + browser.callback(fcb_cb, this); + + begin(); + Fl_Widget* obj; + obj = ok_button = new Fl_Return_Button( + WIDTH_BOX-2*(WIDTH_SPC+WIDTH_OK), HEIGHT_BOX-WIDTH_SPC-HEIGHT_BUT, + WIDTH_OK, HEIGHT_BUT, fl_ok); + obj = cancel_button = new Fl_Button( + WIDTH_BOX-WIDTH_SPC-WIDTH_OK, HEIGHT_BOX-WIDTH_SPC-HEIGHT_BUT, + WIDTH_OK, HEIGHT_BUT, fl_cancel); + cancel_button->shortcut("^["); + + obj=new Fl_Button(WIDTH_SPC,but_y,WIDTH_BUT,HEIGHT_BUT, "&Up one directory"); + obj->callback(up_cb, this); + but_y += HEIGHT_BUT; + + obj = new Fl_Button(WIDTH_SPC, but_y, WIDTH_BUT, HEIGHT_BUT, "&~/ Home"); + obj->callback(dir_cb, this); + but_y += HEIGHT_BUT; + + obj = new Fl_Button(WIDTH_SPC, but_y, WIDTH_BUT, HEIGHT_BUT, "&/ Root"); + obj->callback(dir_cb, this); + but_y += HEIGHT_BUT; + + obj=new Fl_Button(WIDTH_SPC, but_y, WIDTH_BUT, HEIGHT_BUT, "&Current dir"); + obj->callback(working_cb, this); + but_y += HEIGHT_BUT; + + if (fl_file_chooser_button) { + obj=new Fl_Button(WIDTH_SPC,but_y,WIDTH_BUT,HEIGHT_BUT,fl_file_chooser_button); + obj->callback(dir_cb, this); + but_y += HEIGHT_BUT; + } + + normal_button = new Fl_Button(WIDTH_SPC, but_y, WIDTH_BUT, HEIGHT_BUT, ""); + normal_button->callback(files_cb, this); + but_y += HEIGHT_BUT; + + obj = new Fl_Button(WIDTH_SPC,but_y, WIDTH_BUT, HEIGHT_BUT, "* &All files"); + obj->callback(files_cb, this); + but_y += HEIGHT_BUT; + + obj = new Fl_Button(WIDTH_SPC,but_y,WIDTH_BUT,HEIGHT_BUT, ". &Hidden files"); + obj->callback(files_cb, this); + but_y += HEIGHT_BUT; + + obj = new Fl_Button(WIDTH_SPC,but_y,WIDTH_BUT,HEIGHT_BUT, "*/ &Directories"); + obj->callback(files_cb, this); + but_y += HEIGHT_BUT; + + resizable(new Fl_Box(browser.x(), but_y, + cancel_button->x()-browser.x(), + browser.y()+browser.h()-but_y)); + // add(input); // put last for better draw() speed + end(); + set_modal(); +} + +char* fl_file_chooser(const char* message, const char* pat, const char* fname) +{ + static FCW* f; if (!f) f = new FCW(); + f->ok_button->label(fl_ok); + f->cancel_button->label(fl_cancel); + + if (pat && !*pat) pat = 0; + if (fname && *fname) { + f->input.value(fname); + } else if (f->browser.pattern != pat && (!pat || !f->browser.pattern || + strcmp(pat,f->browser.pattern))) { + // if pattern is different, remove name but leave old directory: + const char* p = f->input.value(); + const char* q = filename_name(p); + f->input.value(p, q-p); + } + f->browser.pattern = pat; + f->normal_button->label(pat ? pat : "visible files"); + f->browser.set(f->input.value()); + f->input.position(10000, f->browser.dirend); + + f->label(message); + f->hotspot(f); + f->show(); + int ok = 0; + for (;;) { + Fl::wait(); + Fl_Widget* o = Fl::readqueue(); + if (o == f->ok_button) {ok = 1; break;} + else if (o == f->cancel_button || o == f) break; + } + f->hide(); + f->browser.clear(); + + if (!ok) return 0; + const char* r = f->input.value(); + const char* p; + for (p=r+f->browser.dirend; *p; p++) + if (*p=='*' || *p=='?' || *p=='[' || *p=='{') return 0; + return (char*)r; +} + +// end of fl_file_chooser.C diff --git a/src/fl_font.cxx b/src/fl_font.cxx new file mode 100644 index 000000000..746f65547 --- /dev/null +++ b/src/fl_font.cxx @@ -0,0 +1,283 @@ +// fl_font.C + +// Select fonts from the fltk font table. + +#ifdef WIN32 +#include "fl_font_win32.C" +#else + +#include <config.h> +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <FL/x.H> +#include "Fl_Font.H" + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +Fl_XFont::Fl_XFont(const char *name, int num) + : number(num) +{ + font = XLoadQueryFont(fl_display, name); + if (!font) { + Fl::warning("bad font: %s", name); + font = XLoadQueryFont(fl_display, "fixed"); // if fixed fails we crash + } + XCharStruct *p = font->per_char; + if (!p) { + free_this = per_char = 0; + } else if (font->min_char_or_byte2>0x20 || font->max_char_or_byte2<0xff) { + // fill in short fonts so fl_width does not crash: + XCharStruct *q = free_this = new XCharStruct[0xff-0x20+1]; + per_char = q - 0x20; + unsigned int i = 0x20; + for (; i<font->min_char_or_byte2; i++, q++) + q->width = font->min_bounds.width; + for (; i<=font->max_char_or_byte2; i++) + *q++ = *p++; + for (; i<=0xff; i++) + q->width = font->min_bounds.width; + } else { + free_this = 0; + per_char = p - font->min_char_or_byte2; + } +#if HAVE_GL + listbase = 0; +#endif +} + +Fl_XFont *fl_current_xfont; +Fl_XFont *fl_fixed_xfont; + +Fl_XFont::~Fl_XFont() { +#if HAVE_GL +// Delete list created by gl_draw(). This is not done by this code +// as it will link in GL unnecessarily. There should be some kind +// of "free" routine pointer, or a subclass? +// if (listbase) { +// int base = font->min_char_or_byte2; +// int size = font->max_char_or_byte2-base+1; +// int base = 0; int size = 256; +// glDeleteLists(listbase+base,size); +// } +#endif + if (this == fl_current_xfont) fl_current_xfont = 0; + delete[] free_this; + XFreeFont(fl_display, font); +} + +//////////////////////////////////////////////////////////////// + +// WARNING: if you add to this table, you must redefine FL_FREE_FONT +// in Enumerations.H & recompile!! +static Fl_Fontdesc built_in_table[] = { +{"-*-helvetica-medium-r-normal--*"}, +{"-*-helvetica-bold-r-normal--*"}, +{"-*-helvetica-medium-o-normal--*"}, +{"-*-helvetica-bold-o-normal--*"}, +{"-*-courier-medium-r-normal--*"}, +{"-*-courier-bold-r-normal--*"}, +{"-*-courier-medium-o-normal--*"}, +{"-*-courier-bold-o-normal--*"}, +{"-*-times-medium-r-normal--*"}, +{"-*-times-bold-r-normal--*"}, +{"-*-times-medium-i-normal--*"}, +{"-*-times-bold-i-normal--*"}, +{"-*-symbol-*"}, +{"-*-lucidatypewriter-medium-r-normal-sans-*"}, +{"-*-lucidatypewriter-bold-r-normal-sans-*"}, +{"-*-*zapf dingbats-*"} +}; + +Fl_Fontdesc *fl_fonts = built_in_table; + +#define MAXSIZE 32767 + +// return dash number N, or pointer to ending null if none: +const char* fl_font_word(const char *p, int n) { + while (*p) {if (*p=='-') {if (!--n) break;} p++;} + return p; +} + +// return a pointer to a number we think is "point size": +char *fl_find_fontsize(char* name) { + char *c = name; + // for standard x font names, try after 7th dash: + if (*c == '-') { + c = (char*)fl_font_word(c,7); + if (*c++ && isdigit(*c)) return c; + return 0; // malformed x font name? + } + char *r = 0; + // find last set of digits: + for (c++; *c; c++) + if (isdigit(*c) && !isdigit(*(c-1))) r = c; + return r; +} + +const char* fl_encoding = "iso8859-1"; + +// return true if this matches fl_encoding: +int fl_correct_encoding(const char* name) { + if (*name != '-') return 0; + const char* c = fl_font_word(name,13); + return (*c++ && !strcmp(c,fl_encoding)); +} + +// locate or create an Fl_XFont for a given Fl_Fontdesc and size: +static Fl_XFont *find(int fnum, int size) { + Fl_Fontdesc *s = fl_fonts+fnum; + if (!s->name) s = fl_fonts; // use font 0 if still undefined + Fl_XFont *f; + for (f = s->first; f; f = f->next) + if (f->minsize <= size && f->maxsize >= size) return f; + fl_open_display(); + if (!s->xlist) { + s->xlist = XListFonts(fl_display, s->name, 100, &(s->n)); + if (!s->xlist) { // use fixed if no matching font... + if (!fl_fixed_xfont) { + fl_fixed_xfont = new Fl_XFont("fixed",fnum); + fl_fixed_xfont->minsize = 0; + fl_fixed_xfont->maxsize = 32767; + } + s->first = fl_fixed_xfont; + return fl_fixed_xfont; + } + } + // search for largest <= font size: + char *name = s->xlist[0]; int ptsize = 0; // best one found so far + int matchedlength = 32767; + char namebuffer[1024]; // holds scalable font name + int found_encoding = 0; + int m = s->n; if (m<0) m = -m; + for (int n=0; n < m; n++) { + + char *thisname = s->xlist[n]; + if (fl_correct_encoding(thisname)) { + if (!found_encoding) ptsize = 0; // force it to choose this + found_encoding = 1; + } else { + if (found_encoding) continue; + } + char *c = fl_find_fontsize(thisname); + int thissize = c ? atoi(c) : MAXSIZE; + int thislength = strlen(thisname); + if (thissize == size && thislength < matchedlength) { + // exact match, use it: + name = thisname; + ptsize = size; + matchedlength = thislength; + } else if (!thissize && ptsize!=size) { + // whoa! A scalable font! Use unless exact match found: + int l = c-thisname; + memcpy(namebuffer,thisname,l); +#if 1 // this works if you don't want stdio + if (size>=100) namebuffer[l++] = size/100+'0'; + if (size>=10) namebuffer[l++] = (size/10)%10+'0'; + namebuffer[l++] = (size%10)+'0'; +#else + //for some reason, sprintf fails to return the right value under Solaris. + l += sprintf(namebuffer+l,"%d",size); +#endif + while (*c == '0') c++; + strcpy(namebuffer+l,c); + name = namebuffer; + ptsize = size; + } else if (!ptsize || // no fonts yet + thissize < ptsize && ptsize > size || // current font too big + thissize > ptsize && thissize <= size // current too small + ) { + name = thisname; ptsize = thissize; + matchedlength = thislength; + } + } + + if (ptsize != size) { // see if we already found this unscalable font: + for (f = s->first; f; f = f->next) { + if (f->minsize <= ptsize && f->maxsize >= ptsize) { + if (f->minsize > size) f->minsize = size; + if (f->maxsize < size) f->maxsize = size; + return f; + } + } + } + + // okay, we definately have some name, make the font: + f = new Fl_XFont(name,fnum); + if (ptsize < size) {f->minsize = ptsize; f->maxsize = size;} + else {f->minsize = size; f->maxsize = ptsize;} + f->next = s->first; + s->first = f; + return f; + +} + +//////////////////////////////////////////////////////////////// +// Public interface: + +int fl_font_; +int fl_size_; +static GC font_gc; + +void fl_font(int fnum, int size) { + if (fnum == fl_font_ && size == fl_size_) return; + fl_font_ = fnum; fl_size_ = size; + Fl_XFont *f = find(fnum, size); + if (f != fl_current_xfont) {fl_current_xfont = f; font_gc = 0;} +} + +void fl_font(int fnum, int size, Fl_Font default_font, int default_size) { + if (fnum<4) fnum |= default_font; + fl_font(fnum, size + default_size); +} + +int fl_height() { + return (fl_current_xfont->font->ascent + fl_current_xfont->font->descent); +} + +int fl_descent() { + return fl_current_xfont->font->descent; +} + +double fl_width(const char *c) { + XCharStruct *p = fl_current_xfont->per_char; + if (!p) return strlen(c)*fl_current_xfont->font->min_bounds.width; + int w = 0; + while (*c) + if (*c >= ' ') + w += p[(uchar)(*c++)].width; + else + c ++; + + return w; +} + +double fl_width(const char *c, int n) { + XCharStruct *p = fl_current_xfont->per_char; + if (!p) return n*fl_current_xfont->font->min_bounds.width; + int w = 0; + while (n--) w += p[(uchar)(*c++)].width; + return w; +} + +double fl_width(uchar c) { + XCharStruct *p = fl_current_xfont->per_char; + if (!p) return fl_current_xfont->font->min_bounds.width; + return p[c].width; +} + +void fl_draw(const char *str, int n, int x, int y) { + if (font_gc != fl_gc) { + font_gc = fl_gc; + XSetFont(fl_display, fl_gc, fl_current_xfont->font->fid); + } + XDrawString(fl_display, fl_window, fl_gc, x, y, str, n); +} + +void fl_draw(const char *str, int x, int y) { + fl_draw(str, strlen(str), x, y); +} + +#endif +// end of fl_font.C diff --git a/src/fl_font_win32.cxx b/src/fl_font_win32.cxx new file mode 100644 index 000000000..0f4e20e77 --- /dev/null +++ b/src/fl_font_win32.cxx @@ -0,0 +1,159 @@ +// fl_font_win32.C + +#include <config.h> +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <FL/win32.H> +#include "Fl_Font.H" + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +Fl_XFont::Fl_XFont(const char *name, int size, int num) { + int weight = FW_NORMAL; + int italic = 0; + switch (*name++) { + case 'I': italic = 1; break; + case 'P': italic = 1; + case 'B': weight = FW_BOLD; break; + case ' ': break; + default: name--; + } + fid = CreateFont( + -size, // negative makes it use "char size" + 0, // logical average character width + 0, // angle of escapement + 0, // base-line orientation angle + weight, + italic, + FALSE, // underline attribute flag + FALSE, // strikeout attribute flag + DEFAULT_CHARSET, // character set identifier + OUT_DEFAULT_PRECIS, // output precision + CLIP_DEFAULT_PRECIS,// clipping precision + DEFAULT_QUALITY, // output quality + DEFAULT_PITCH, // pitch and family + name // pointer to typeface name string + ); + if (!fl_gc) fl_gc = fl_GetDC(0); + SelectObject(fl_gc, fid); + GetTextMetrics(fl_gc, &metr); +// BOOL ret = GetCharWidthFloat(fl_gc, metr.tmFirstChar, metr.tmLastChar, font->width+metr.tmFirstChar); +// ...would be the right call, but is not implemented into Window95! (WinNT?) + GetCharWidth(fl_gc, 0, 255, width); +#if HAVE_GL + listbase = 0; +#endif + number = num; + minsize = maxsize = size; +} + +Fl_XFont *fl_current_xfont; + +Fl_XFont::~Fl_XFont() { +#if HAVE_GL +// Delete list created by gl_draw(). This is not done by this code +// as it will link in GL unnecessarily. There should be some kind +// of "free" routine pointer, or a subclass? +// if (listbase) { +// int base = font->min_char_or_byte2; +// int size = font->max_char_or_byte2-base+1; +// int base = 0; int size = 256; +// glDeleteLists(listbase+base,size); +// } +#endif + if (this == fl_current_xfont) fl_current_xfont = 0; + DeleteObject(fid); +} + +//////////////////////////////////////////////////////////////// + +// WARNING: if you add to this table, you must redefine FL_FREE_FONT +// in Enumerations.H & recompile!! +static Fl_Fontdesc built_in_table[] = { +{" Arial"}, +{"BArial"}, +{"IArial"}, +{"PArial"}, +{" Courier New"}, +{"BCourier New"}, +{"ICourier New"}, +{"PCourier New"}, +{" Times New Roman"}, +{"BTimes New Roman"}, +{"ITimes New Roman"}, +{"PTimes New Roman"}, +{" Symbol"}, +{" Terminal"}, +{"BTerminal"}, +{" Wingdings"}, +}; + +Fl_Fontdesc *fl_fonts = built_in_table; + +static Fl_XFont *find(int fnum, int size) { + Fl_Fontdesc *s = fl_fonts+fnum; + if (!s->name) s = fl_fonts; // use 0 if fnum undefined + Fl_XFont *f; + for (f = s->first; f; f = f->next) + if (f->minsize <= size && f->maxsize >= size) return f; + f = new Fl_XFont(s->name, size, fnum); + f->next = s->first; + s->first = f; + return f; +} + +//////////////////////////////////////////////////////////////// +// Public interface: + +int fl_font_; +int fl_size_; +static HDC font_gc; + +void fl_font(int fnum, int size) { + if (fnum == fl_font_ && size == fl_size_) return; + fl_font_ = fnum; fl_size_ = size; + fl_current_xfont = find(fnum, size); +} + +void fl_font(int fnum, int size, Fl_Font default_font, int default_size) { + if (fnum<4) fnum |= default_font; + fl_font(fnum, size + default_size); +} + +int fl_height() { + return (fl_current_xfont->metr.tmAscent + fl_current_xfont->metr.tmDescent); +} + +int fl_descent() { + return fl_current_xfont->metr.tmDescent; +} + +double fl_width(const char *c) { + double w = 0.0; + while (*c) w += fl_current_xfont->width[uchar(*c++)]; + return w; +} + +double fl_width(const char *c, int n) { + double w = 0.0; + while (n--) w += fl_current_xfont->width[uchar(*c++)]; + return w; +} + +double fl_width(uchar c) { + return fl_current_xfont->width[c]; +} + +void fl_draw(const char *str, int n, int x, int y) { + SetTextColor(fl_gc, fl_RGB()); + SelectObject(fl_gc, fl_current_xfont->fid); + TextOut(fl_gc, x, y, str, n); +} + +void fl_draw(const char *str, int x, int y) { + fl_draw(str, strlen(str), x, y); +} + +// end of fl_font_win32.C diff --git a/src/fl_labeltype.cxx b/src/fl_labeltype.cxx new file mode 100644 index 000000000..8b7755eaf --- /dev/null +++ b/src/fl_labeltype.cxx @@ -0,0 +1,104 @@ +// fl_labeltype.C + +// Drawing code for the (one) common label types. +// Other label types (symbols) are in their own source files +// to avoid linking if not used. + +#include <FL/Fl.H> +#include <FL/Fl_Widget.H> +#include <FL/Fl_Group.H> +#include <FL/fl_draw.H> + +void +fl_no_label(const Fl_Label*,int,int,int,int,Fl_Align) {} + +void +fl_normal_label(const Fl_Label* o, int X, int Y, int W, int H, Fl_Align align) +{ + fl_font(o->font, o->size); + fl_color((Fl_Color)o->color); + fl_draw(o->value, X, Y, W, H, align); +} + +void +fl_normal_measure(const Fl_Label* o, int& W, int& H) { + fl_font(o->font, o->size); + fl_measure(o->value, W, H); +} + +#define MAX_LABELTYPE 16 + +static Fl_Label_Draw_F* table[MAX_LABELTYPE] = { + fl_normal_label, + fl_no_label, + fl_normal_label, // _FL_SYMBOL_LABEL, + fl_normal_label, // _FL_SHADOW_LABEL, + fl_normal_label, // _FL_ENGRAVED_LABEL, + fl_normal_label, // _FL_EMBOSSED_LABEL, + fl_no_label, // _FL_BITMAP_LABEL, + fl_no_label, // _FL_PIXMAP_LABEL, + fl_no_label, // _FL_IMAGE_LABEL, + // FL_FREE_LABELTYPE+n: + fl_no_label, fl_no_label, fl_no_label, + fl_no_label, fl_no_label, fl_no_label, fl_no_label, +}; + +static Fl_Label_Measure_F* measure[MAX_LABELTYPE]; + +void Fl::set_labeltype(Fl_Labeltype t,Fl_Label_Draw_F* f,Fl_Label_Measure_F*m) +{ + table[t] = f; measure[t] = m; +} + +//////////////////////////////////////////////////////////////// + +// draw label with arbitrary alignment in arbitrary box: +void Fl_Label::draw(int X, int Y, int W, int H, Fl_Align align) const { + if (!value) return; + table[type](this, X, Y, W, H, align); +} + +void Fl_Label::measure(int& W, int& H) const { + if (!value) return; + Fl_Label_Measure_F* f = ::measure[type]; if (!f) f = fl_normal_measure; + f(this, W, H); +} + +// The normal call for a draw() method: +void Fl_Widget::draw_label() const { + int X = x_+Fl::box_dx(box()); + int W = w_-Fl::box_dw(box()); + if (W > 11 && align()&(FL_ALIGN_LEFT|FL_ALIGN_RIGHT)) {X += 3; W -= 6;} + draw_label(X, y_+Fl::box_dy(box()), W, h_-Fl::box_dh(box())); +} + +// draw() can use this instead to change the bounding box: +void Fl_Widget::draw_label(int X, int Y, int W, int H) const { + // quit if we are not drawing a label inside the widget: + if ((align()&15) && !(align() & FL_ALIGN_INSIDE)) return; + draw_label(X,Y,W,H,align()); +} + +Fl_Font Fl_Widget::default_font_; +int Fl_Widget::default_size_; + +// Anybody can call this to force the label to draw anywhere: +extern char fl_draw_shortcut; +void Fl_Widget::draw_label(int X, int Y, int W, int H, Fl_Align a) const { + if (flags()&SHORTCUT_LABEL) fl_draw_shortcut = 1; + Fl_Label l1 = label_; + if (!active_r()) l1.color |= 8; + if (l1.font<4) l1.font |= default_font_; + l1.size += default_size_; + l1.draw(X,Y,W,H,a); + fl_draw_shortcut = 0; +} + +// include these vars here so they can be referenced without including +// Fl_Input_ code: +#include <FL/Fl_Input_.H> + +Fl_Boxtype Fl_Input_::default_box_ = FL_DOWN_BOX; +Fl_Font Fl_Input_::default_font_; +int Fl_Input_::default_size_; + diff --git a/src/fl_oval_box.cxx b/src/fl_oval_box.cxx new file mode 100644 index 000000000..8e5c1c564 --- /dev/null +++ b/src/fl_oval_box.cxx @@ -0,0 +1,38 @@ +/* fl_oval_box.C + + Less-used box types are in seperate files so they are not linked + in if not used. + +*/ + +#include <FL/Fl.H> +#include <FL/fl_draw.H> + +static void fl_oval_flat_box(int x, int y, int w, int h, Fl_Color c) { + fl_color(c); + fl_pie(x, y, w, h, 0, 360); +} + +static void fl_oval_frame(int x, int y, int w, int h, Fl_Color c) { + fl_color(c); + fl_arc(x, y, w, h, 0, 360); +} + +static void fl_oval_box(int x, int y, int w, int h, Fl_Color c) { + fl_oval_flat_box(x,y,w-1,h-1,c); + fl_oval_frame(x,y,w,h,FL_BLACK); +} + +static void fl_oval_shadow_box(int x, int y, int w, int h, Fl_Color c) { + fl_oval_flat_box(x+3,y+3,w,h,FL_DARK3); + fl_oval_box(x,y,w,h,c); +} + +extern void fl_internal_boxtype(Fl_Boxtype, Fl_Box_Draw_F*); +Fl_Boxtype define_FL_OVAL_BOX() { + fl_internal_boxtype(_FL_OSHADOW_BOX,fl_oval_shadow_box); + fl_internal_boxtype(_FL_OVAL_FRAME,fl_oval_frame); + fl_internal_boxtype(_FL_OFLAT_BOX,fl_oval_flat_box); + fl_internal_boxtype(_FL_OVAL_BOX,fl_oval_box); + return _FL_OVAL_BOX; +} diff --git a/src/fl_overlay.cxx b/src/fl_overlay.cxx new file mode 100644 index 000000000..ed44ec9c6 --- /dev/null +++ b/src/fl_overlay.cxx @@ -0,0 +1,39 @@ +// fl_overlay.C + +// Extremely limited "overlay" support. You can use this to drag out +// a rectangle in response to mouse events. It is your responsibility +// to erase the overlay before drawing anything that might intersect +// it. + +#include <FL/x.H> +#include <FL/fl_draw.H> + +static int px,py,pw,ph; + +static void draw_current_rect() { +#ifdef WIN32 + int old = SetROP2(fl_gc, R2_NOT); + fl_rect(px, py, pw, ph); + SetROP2(fl_gc, old); +#else + XSetFunction(fl_display, fl_gc, GXxor); + XSetForeground(fl_display, fl_gc, 0xffffffff); + XDrawRectangle(fl_display, fl_window, fl_gc, px, py, pw, ph); + XSetFunction(fl_display, fl_gc, GXcopy); +#endif +} + +void fl_overlay_clear() { + if (pw > 0) {draw_current_rect(); pw = 0;} +} + +void fl_overlay_rect(int x, int y, int w, int h) { + if (w < 0) {x += w; w = -w;} else if (!w) w = 1; + if (h < 0) {y += h; h = -h;} else if (!h) h = 1; + if (pw > 0) { + if (x==px && y==py && w==pw && h==ph) return; + draw_current_rect(); + } + px = x; py = y; pw = w; ph = h; + draw_current_rect(); +} diff --git a/src/fl_overlay_visual.cxx b/src/fl_overlay_visual.cxx new file mode 100644 index 000000000..a176bf69c --- /dev/null +++ b/src/fl_overlay_visual.cxx @@ -0,0 +1,76 @@ +// Stupid X tricks + +// Return an overlay visual, if any. Also allocate a colormap and +// record the depth for fl_color() to use. +// Another disgusting X interface, based on code extracted and +// purified with great difficulty from XLayerUtil.C: + +#include <config.h> +#if HAVE_OVERLAY +#include <FL/Fl.H> +#include <FL/x.H> + +// SERVER_OVERLAY_VISUALS property element: +struct OverlayInfo { + long overlay_visual; + long transparent_type; + long value; + long layer; +}; + +extern Colormap fl_overlay_colormap; +extern XVisualInfo* fl_overlay_visual; +extern ulong fl_transparent_pixel; + +XVisualInfo *fl_find_overlay_visual() { + static char beenhere; + if (beenhere) return fl_overlay_visual; + beenhere = 1; + + fl_open_display(); + Atom overlayVisualsAtom = + XInternAtom(fl_display,"SERVER_OVERLAY_VISUALS",1); + if (!overlayVisualsAtom) return 0; + OverlayInfo *overlayInfo; + ulong sizeData, bytesLeft; + Atom actualType; + int actualFormat; + if (XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen), + overlayVisualsAtom, 0L, 10000L, False, + overlayVisualsAtom, &actualType, &actualFormat, + &sizeData, &bytesLeft, + (unsigned char **) &overlayInfo)) return 0; + + if (actualType == overlayVisualsAtom && actualFormat == 32) { + int n = int(sizeData/4); + XVisualInfo* v = 0; + // find the greatest depth that has a transparent pixel: + for (int i = 0; i < n; i++) { + if (overlayInfo[i].transparent_type != 1) continue; + if (overlayInfo[i].layer <= 0) continue; + XVisualInfo templt; + templt.visualid = overlayInfo[i].overlay_visual; + int num; + XVisualInfo *v1=XGetVisualInfo(fl_display, VisualIDMask, &templt, &num); + if (v1->screen == fl_screen && + !v1->red_mask && (!v || v1->depth >= v->depth && v1->depth <= 8)) { + if (v) XFree((char*)v); + v = v1; + fl_transparent_pixel = overlayInfo[i].value; + } else { + XFree((char*)v1); + } + } + if (v) { + fl_overlay_visual = v; + fl_overlay_colormap = + XCreateColormap(fl_display, RootWindow(fl_display, fl_screen), + v->visual, AllocNone); + } + } + XFree((char*)overlayInfo); + // printf("overlay visual %d selected\n", fl_overlay_visual->visualid); + return fl_overlay_visual; +} + +#endif diff --git a/src/fl_rect.cxx b/src/fl_rect.cxx new file mode 100644 index 000000000..2e294bd96 --- /dev/null +++ b/src/fl_rect.cxx @@ -0,0 +1,380 @@ +// fl_rect.C + +// These routines from fl_draw.H are used by the standard boxtypes +// and thus are always linked into an fltk program. + +// Also all fl_clip routines, since they are always linked in so +// that minimal update works. + +#include <FL/Fl_Widget.H> +#include <FL/fl_draw.H> +#include <FL/x.H> + +void fl_rect(int x, int y, int w, int h) { + if (w<=0 || h<=0) return; +#ifdef WIN32 + MoveToEx(fl_gc, x, y, 0L); + LineTo(fl_gc, x+w-1, y); + LineTo(fl_gc, x+w-1, y+h-1); + LineTo(fl_gc, x, y+h-1); + LineTo(fl_gc, x, y); +#else + XDrawRectangle(fl_display, fl_window, fl_gc, x, y, w-1, h-1); +#endif +} + +void fl_rectf(int x, int y, int w, int h) { + if (w<=0 || h<=0) return; +#ifdef WIN32 + RECT rect; + rect.left = x; rect.top = y; + rect.right = x + w; rect.bottom = y + h; + FillRect(fl_gc, &rect, fl_brush()); +#else + if (w && h) XFillRectangle(fl_display, fl_window, fl_gc, x, y, w, h); +#endif +} + +void fl_xyline(int x, int y, int x1) { +#ifdef WIN32 + MoveToEx(fl_gc, x, y, 0L); LineTo(fl_gc, x1+1, y); +#else + XDrawLine(fl_display, fl_window, fl_gc, x, y, x1, y); +#endif +} + +void fl_xyline(int x, int y, int x1, int y2) { +#ifdef WIN32 + if (y2 < y) y2--; + MoveToEx(fl_gc, x, y, 0L); + LineTo(fl_gc, x1, y); + LineTo(fl_gc, x1, y2); +#else + XPoint p[3]; + p[0].x = x; p[0].y = p[1].y = y; + p[1].x = p[2].x = x1; p[2].y = y2; + XDrawLines(fl_display, fl_window, fl_gc, p, 3, 0); +#endif +} + +void fl_xyline(int x, int y, int x1, int y2, int x3) { +#ifdef WIN32 + MoveToEx(fl_gc, x, y, 0L); + LineTo(fl_gc, x1, y); + LineTo(fl_gc, x1, y2); + LineTo(fl_gc, x3, y2); +#else + XPoint p[4]; + p[0].x = x; p[0].y = p[1].y = y; + p[1].x = p[2].x = x1; p[2].y = p[3].y = y2; + p[3].x = x3; + XDrawLines(fl_display, fl_window, fl_gc, p, 4, 0); +#endif +} + +void fl_yxline(int x, int y, int y1) { +#ifdef WIN32 + if (y1 < y) y1--; + MoveToEx(fl_gc, x, y, 0L); LineTo(fl_gc, x, y1); +#else + XDrawLine(fl_display, fl_window, fl_gc, x, y, x, y1); +#endif +} + +void fl_yxline(int x, int y, int y1, int x2) { +#ifdef WIN32 + if (x2 > x) x2++; + MoveToEx(fl_gc, x, y, 0L); + LineTo(fl_gc, x, y1); + LineTo(fl_gc, x2, y1); +#else + XPoint p[3]; + p[0].x = p[1].x = x; p[0].y = y; + p[1].y = p[2].y = y1; p[2].x = x2; + XDrawLines(fl_display, fl_window, fl_gc, p, 3, 0); +#endif +} + +void fl_yxline(int x, int y, int y1, int x2, int y3) { +#ifdef WIN32 + MoveToEx(fl_gc, x, y, 0L); + LineTo(fl_gc, x, y1); + LineTo(fl_gc, x2, y1); + LineTo(fl_gc, x2, y3); +#else + XPoint p[4]; + p[0].x = p[1].x = x; p[0].y = y; + p[1].y = p[2].y = y1; p[2].x = p[3].x = x2; + p[3].y = y3; + XDrawLines(fl_display, fl_window, fl_gc, p, 4, 0); +#endif +} + +void fl_line(int x, int y, int x1, int y1) { +#ifdef WIN32 + MoveToEx(fl_gc, x, y, 0L); + LineTo(fl_gc, x1, y1); +#else + XDrawLine(fl_display, fl_window, fl_gc, x, y, x1, y1); +#endif +} + +void fl_line(int x, int y, int x1, int y1, int x2, int y2) { +#ifdef WIN32 + MoveToEx(fl_gc, x, y, 0L); + LineTo(fl_gc, x1, y1); + LineTo(fl_gc, x2, y2); +#else + XPoint p[3]; + p[0].x = x; p[0].y = y; + p[1].x = x1; p[1].y = y1; + p[2].x = x2; p[2].y = y2; + XDrawLines(fl_display, fl_window, fl_gc, p, 3, 0); +#endif +} + +void fl_loop(int x, int y, int x1, int y1, int x2, int y2) { +#ifdef WIN32 + MoveToEx(fl_gc, x, y, 0L); + LineTo(fl_gc, x1, y1); + LineTo(fl_gc, x2, y2); + LineTo(fl_gc, x, y); +#else + XPoint p[4]; + p[0].x = x; p[0].y = y; + p[1].x = x1; p[1].y = y1; + p[2].x = x2; p[2].y = y2; + p[3].x = x; p[3].y = y; + XDrawLines(fl_display, fl_window, fl_gc, p, 4, 0); +#endif +} + +void fl_loop(int x, int y, int x1, int y1, int x2, int y2, int x3, int y3) { +#ifdef WIN32 + MoveToEx(fl_gc, x, y, 0L); + LineTo(fl_gc, x1, y1); + LineTo(fl_gc, x2, y2); + LineTo(fl_gc, x3, y3); + LineTo(fl_gc, x, y); +#else + XPoint p[5]; + p[0].x = x; p[0].y = y; + p[1].x = x1; p[1].y = y1; + p[2].x = x2; p[2].y = y2; + p[3].x = x3; p[3].y = y3; + p[4].x = x; p[4].y = y; + XDrawLines(fl_display, fl_window, fl_gc, p, 5, 0); +#endif +} + +void fl_polygon(int x, int y, int x1, int y1, int x2, int y2) { + XPoint p[4]; + p[0].x = x; p[0].y = y; + p[1].x = x1; p[1].y = y1; + p[2].x = x2; p[2].y = y2; +#ifdef WIN32 + SelectObject(fl_gc, fl_brush()); + Polygon(fl_gc, p, 3); +#else + p[3].x = x; p[3].y = y; + XFillPolygon(fl_display, fl_window, fl_gc, p, 3, Convex, 0); + XDrawLines(fl_display, fl_window, fl_gc, p, 4, 0); +#endif +} + +void fl_polygon(int x, int y, int x1, int y1, int x2, int y2, int x3, int y3) { + XPoint p[5]; + p[0].x = x; p[0].y = y; + p[1].x = x1; p[1].y = y1; + p[2].x = x2; p[2].y = y2; + p[3].x = x3; p[3].y = y3; +#ifdef WIN32 + SelectObject(fl_gc, fl_brush()); + Polygon(fl_gc, p, 4); +#else + p[4].x = x; p[4].y = y; + XFillPolygon(fl_display, fl_window, fl_gc, p, 4, Convex, 0); + XDrawLines(fl_display, fl_window, fl_gc, p, 5, 0); +#endif +} + +void fl_point(int x, int y) { +#ifdef WIN32 + SetPixel(fl_gc, x, y, fl_RGB()); +#else + XDrawPoint(fl_display, fl_window, fl_gc, x, y); +#endif +} + +//////////////////////////////////////////////////////////////// + +#ifdef WIN32 + +static struct rect {int notnull, x, y, r, b;} rstack[10]; +static int rstackptr; +int fl_clip_state_number; // used by gl_begin.C to update GL clip +extern char fl_direct_paint; // in Fl_win32.C + +void fl_clip(int x, int y, int w, int h) { + fl_clip_state_number++; + int r = x+w; + int b = y+h; + rect& current = rstack[rstackptr]; + if (current.notnull) { + if (current.x > x) x = current.x; + if (current.y > y) y = current.y; + if (current.r < r) r = current.r; + if (current.b < b) b = current.b; + } + rect& newrect = rstack[++rstackptr]; + newrect.notnull = 1; + newrect.x = x; + newrect.y = y; + newrect.r = r; + newrect.b = b; + if (rstackptr == 1 && fl_direct_paint) return; + HRGN R = CreateRectRgn(x,y,r,b); + SelectClipRgn(fl_gc, R); + DeleteObject(R); +} + +void fl_push_no_clip() { + fl_clip_state_number++; + if (rstack[rstackptr].notnull) SelectClipRgn(fl_gc, 0); + rstack[++rstackptr].notnull = 0; +} + +void fl_pop_clip() { + fl_clip_state_number++; + rect& r = rstack[--rstackptr]; + if (r.notnull) { + HRGN R = CreateRectRgn(r.x, r.y, r.r, r.b); + SelectClipRgn(fl_gc, R); + DeleteObject(R); + } else { + SelectClipRgn(fl_gc, 0); + } +} + +// does this rectangle intersect current clip? +int fl_not_clipped(int x, int y, int w, int h) { + rect& r = rstack[rstackptr]; + if (!r.notnull) return 2; + return (x < r.r && x+w > r.x && y < r.b && y+h > r.y); +} + +// return rectangle surrounding intersection of this rectangle and clip: +int fl_clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H){ + X = x; Y = y; W = w; H = h; + rect& r = rstack[rstackptr]; + if (!r.notnull) return 0; + int R = x+w; + int B = y+h; + int ret = 0; + if (r.x > x) {X = r.x; ret = 1;} + if (r.y > y) {Y = r.y; ret = 1;} + if (r.r < R) {R = r.r; ret = 1;} + if (r.b < B) {B = r.b; ret = 1;} + if (B <= Y || R <= X) {W = H = 0; return 2;} + W = R-X; + H = B-Y; + return ret; +} + +#else + +// Missing X call: (is this the fastest way to init a 1-rectangle region?) +Region XRectangleRegion(int x, int y, int w, int h) { + XRectangle R; + R.x = x; R.y = y; R.width = w; R.height = h; + Region r = XCreateRegion(); + XUnionRectWithRegion(&R, r, r); + return r; +} + +static Region rstack[10]; +static int rstackptr; +int fl_clip_state_number; // used by gl_begin.C to update GL clip + +// undo any clobbering of clip done by your program: +void fl_restore_clip() { + fl_clip_state_number++; + Region r = rstack[rstackptr]; + if (r) XSetRegion(fl_display, fl_gc, r); + else XSetClipMask(fl_display, fl_gc, 0); +} + +// Replace the top of the clip stack: +void fl_clip_region(Region r) { + Region oldr = rstack[rstackptr]; + if (oldr) XDestroyRegion(oldr); + rstack[rstackptr] = r; + fl_restore_clip(); +} + +// Intersect & push a new clip rectangle: +void fl_clip(int x, int y, int w, int h) { + Region r; + if (w > 0 && h > 0) { + r = XRectangleRegion(x,y,w,h); + Region current = rstack[rstackptr]; + if (current) { + Region temp = XCreateRegion(); + XIntersectRegion(current, r, temp); + XDestroyRegion(r); + r = temp; + } + } else { // make empty clip region: + r = XCreateRegion(); + } + rstack[++rstackptr] = r; + fl_restore_clip(); +} + +// make there be no clip (used by fl_begin_offscreen() only!) +void fl_push_no_clip() { + rstack[++rstackptr] = 0; + fl_restore_clip(); +} + +// pop back to previous clip: +void fl_pop_clip() { + Region oldr = rstack[rstackptr--]; + if (oldr) XDestroyRegion(oldr); + fl_restore_clip(); +} + +// does this rectangle intersect current clip? +int fl_not_clipped(int x, int y, int w, int h) { + Region r = rstack[rstackptr]; + return r ? XRectInRegion(r, x, y, w, h) : 1; +} + +// return rectangle surrounding intersection of this rectangle and clip: +int fl_clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H){ + X = x; Y = y; W = w; H = h; + Region r = rstack[rstackptr]; + if (!r) return 0; + switch (XRectInRegion(r, x, y, w, h)) { + case 0: // completely outside + W = H = 0; + return 2; + case 1: // completely inside: + return 0; + default: // partial: + break; + } + Region rr = XRectangleRegion(x,y,w,h); + Region temp = XCreateRegion(); + XIntersectRegion(r, rr, temp); + XRectangle rect; + XClipBox(temp, &rect); + X = rect.x; Y = rect.y; W = rect.width; H = rect.height; + XDestroyRegion(temp); + XDestroyRegion(rr); + return 1; +} + +#endif + +// end of fl_rect.C diff --git a/src/fl_round_box.cxx b/src/fl_round_box.cxx new file mode 100644 index 000000000..c165e22ec --- /dev/null +++ b/src/fl_round_box.cxx @@ -0,0 +1,94 @@ +/* fl_round_box.C + + Box drawing code for an obscure box type. + These box types are in seperate files so they are not linked + in if not used. + + 3/8/99: Complete rewrite to use XDrawArc +*/ + +#include <FL/Fl.H> +#include <FL/fl_draw.H> + +// A compiler from a certain very large software company will not compile +// the function pointer assignment due to the name conflict with fl_arc. +// This function is to fix that: +void fl_arc_i(int x,int y,int w,int h,double a1,double a2) { + fl_arc(x,y,w,h,a1,a2); +} + +enum {UPPER_LEFT, LOWER_RIGHT, CLOSED, FILL}; + +static void draw(int which, int x,int y,int w,int h, int inset, uchar color) +{ + if (inset*2 >= w) inset = (w-1)/2; + if (inset*2 >= h) inset = (h-1)/2; + x += inset; + y += inset; + w -= 2*inset; + h -= 2*inset; + int d = w <= h ? w : h; + if (d <= 1) return; + fl_color((Fl_Color)color); + void (*f)(int,int,int,int,double,double) = + (which==FILL) ? fl_pie : fl_arc_i; + if (which >= CLOSED) { + f(x+w-d, y, d, d, w<=h ? 0 : -90, w<=h ? 180 : 90); + f(x, y+h-d, d, d, w<=h ? 180 : 90, w<=h ? 360 : 270); + } else if (which == UPPER_LEFT) { + f(x+w-d, y, d, d, 45, w<=h ? 180 : 90); + f(x, y+h-d, d, d, w<=h ? 180 : 90, 225); + } else { // LOWER_RIGHT + f(x, y+h-d, d, d, 225, w<=h ? 360 : 270); + f(x+w-d, y, d, d, w<=h ? 360 : 270, 360+45); + } + if (which == FILL) { + if (w < h) + fl_rectf(x, y+d/2, w, h-(d&-2)); + else if (w > h) + fl_rectf(x+d/2, y, w-(d&-2), h); + } else { + if (w < h) { + if (which != UPPER_LEFT) fl_yxline(x+w-1, y+d/2, y+h-d/2); + if (which != LOWER_RIGHT) fl_yxline(x, y+d/2, y+h-d/2); + } else if (w > h) { + if (which != UPPER_LEFT) fl_xyline(x+d/2, y+h-1, x+w-d/2); + if (which != LOWER_RIGHT) fl_xyline(x+d/2, y, x+w-d/2); + } + } +} + +extern uchar* Fl_Gray_Ramp; + +static void fl_round_down_box(int x, int y, int w, int h, Fl_Color bgcolor) { + draw(FILL, x, y, w, h, 2, bgcolor); + draw(UPPER_LEFT, x+1, y, w-2, h, 0, Fl_Gray_Ramp['N']); + draw(UPPER_LEFT, x+1, y, w-2, h, 1, Fl_Gray_Ramp['H']); + draw(UPPER_LEFT, x, y, w, h, 0, Fl_Gray_Ramp['N']); + draw(UPPER_LEFT, x, y, w, h, 1, Fl_Gray_Ramp['H']); + draw(LOWER_RIGHT, x, y, w, h, 0, Fl_Gray_Ramp['S']); + draw(LOWER_RIGHT, x+1, y, w-2, h, 0, Fl_Gray_Ramp['U']); + draw(LOWER_RIGHT, x, y, w, h, 1, Fl_Gray_Ramp['U']); + draw(LOWER_RIGHT, x+1, y, w-2, h, 1, Fl_Gray_Ramp['W']); + draw(CLOSED, x, y, w, h, 2, Fl_Gray_Ramp['A']); +} + +static void fl_round_up_box(int x, int y, int w, int h, Fl_Color bgcolor) { + draw(FILL, x, y, w, h, 2, bgcolor); + draw(LOWER_RIGHT, x+1, y, w-2, h, 0, Fl_Gray_Ramp['H']); + draw(LOWER_RIGHT, x+1, y, w-2, h, 1, Fl_Gray_Ramp['N']); + draw(LOWER_RIGHT, x, y, w, h, 1, Fl_Gray_Ramp['H']); + draw(LOWER_RIGHT, x, y, w, h, 2, Fl_Gray_Ramp['N']); + draw(UPPER_LEFT, x, y, w, h, 2, Fl_Gray_Ramp['U']); + draw(UPPER_LEFT, x+1, y, w-2, h, 1, Fl_Gray_Ramp['S']); + draw(UPPER_LEFT, x, y, w, h, 1, Fl_Gray_Ramp['W']); + draw(UPPER_LEFT, x+1, y, w-2, h, 0, Fl_Gray_Ramp['U']); + draw(CLOSED, x, y, w, h, 0, Fl_Gray_Ramp['A']); +} + +extern void fl_internal_boxtype(Fl_Boxtype, Fl_Box_Draw_F*); +Fl_Boxtype define_FL_ROUND_UP_BOX() { + fl_internal_boxtype(_FL_ROUND_DOWN_BOX, fl_round_down_box); + fl_internal_boxtype(_FL_ROUND_UP_BOX, fl_round_up_box); + return _FL_ROUND_UP_BOX; +} diff --git a/src/fl_rounded_box.cxx b/src/fl_rounded_box.cxx new file mode 100644 index 000000000..f670726c1 --- /dev/null +++ b/src/fl_rounded_box.cxx @@ -0,0 +1,75 @@ +/* fl_rounded_box.C + + Less-used box types are in seperate files so they are not linked + in if not used. + +*/ + +#include <FL/Fl.H> +#include <FL/fl_draw.H> + +#define RN 5 +#define RS 15 +#define BW 3 + +static double offset[RN] = { 0.0, 0.07612, 0.29289, 0.61732, 1.0}; + +static void rbox(int fill, int x, int y, int w, int h) { + int i; + int rsx ,rsy, rs; + rsx = w*2/5; rsy = h*2/5; + if (rsx > rsy) rs = rsy; else rs = rsx; + if (rs > RS) rs = RS; + rsx = rs; rsy = rs; + + if (fill) fl_begin_polygon(); else fl_begin_loop(); + for (i=0; i<RN; i++) + fl_vertex(x + offset[RN-i-1]*rsx, y + offset[i] * rsy); + for (i=0; i<RN; i++) + fl_vertex(x + offset[i]*rsx, y + h-1 - offset[RN-i-1] * rsy); + for (i=0; i<RN; i++) + fl_vertex(x + w-1 - offset[RN-i-1]*rsx, y + h-1 - offset[i] * rsy); + for (i=0; i<RN; i++) + fl_vertex(x + w-1 - offset[i]*rsx, y + offset[RN-i-1] * rsy); + if (fill) fl_end_polygon(); else fl_end_loop(); +} + +static void fl_rflat_box(int x, int y, int w, int h, Fl_Color c) { + fl_color(c); rbox(1, x, y, w, h); rbox(0, x, y, w, h); +} + +static void fl_rounded_frame(int x, int y, int w, int h, Fl_Color c) { + fl_color(c); rbox(0, x, y, w, h); +} + +static void fl_rounded_box(int x, int y, int w, int h, Fl_Color c) { + fl_color(c); rbox(1, x, y, w, h); + fl_color(FL_BLACK); rbox(0, x, y, w, h); +} + +static void fl_rshadow_box(int x, int y, int w, int h, Fl_Color c) { + // draw shadow: + fl_color(FL_DARK3); + rbox(1, x+BW, y+BW, w, h); + rbox(0, x+BW, y+BW, w, h); + // draw the box: + fl_rounded_box(x, y, w, h, c); +} + +extern void fl_internal_boxtype(Fl_Boxtype, Fl_Box_Draw_F*); + +Fl_Boxtype define_FL_ROUNDED_BOX() { + fl_internal_boxtype(_FL_ROUNDED_FRAME, fl_rounded_frame); + fl_internal_boxtype(_FL_ROUNDED_BOX, fl_rounded_box); + return _FL_ROUNDED_BOX; +} + +Fl_Boxtype define_FL_RFLAT_BOX() { + fl_internal_boxtype(_FL_RFLAT_BOX, fl_rflat_box); + return _FL_RFLAT_BOX; +} + +Fl_Boxtype define_FL_RSHADOW_BOX() { + fl_internal_boxtype(_FL_RSHADOW_BOX, fl_rshadow_box); + return _FL_RSHADOW_BOX; +} diff --git a/src/fl_scroll_area.cxx b/src/fl_scroll_area.cxx new file mode 100644 index 000000000..2aecee7ad --- /dev/null +++ b/src/fl_scroll_area.cxx @@ -0,0 +1,66 @@ +// fl_scroll_area.C + +// Drawing function to move the contents of a rectangle. This is passed +// a "callback" which is called to draw rectangular areas that are moved +// into the drawing area. + +#include <FL/x.H> + +// scroll a rectangle and redraw the newly exposed portions: +void fl_scroll(int X, int Y, int W, int H, int dx, int dy, + void (*draw_area)(void*, int,int,int,int), void* data) +{ + if (!dx && !dy) return; + if (dx <= -W || dx >= W || dy <= -H || dy >= H) { + // no intersection of old an new scroll + draw_area(data,X,Y,W,H); + return; + } + int src_x, src_w, dest_x, clip_x, clip_w; + if (dx > 0) { + src_x = X; + dest_x = X+dx; + src_w = W-dx; + clip_x = X; + clip_w = dx; + } else { + src_x = X-dx; + dest_x = X; + src_w = W+dx; + clip_x = X+src_w; + clip_w = W-src_w; + } + int src_y, src_h, dest_y, clip_y, clip_h; + if (dy > 0) { + src_y = Y; + dest_y = Y+dy; + src_h = H-dy; + clip_y = Y; + clip_h = dy; + } else { + src_y = Y-dy; + dest_y = Y; + src_h = H+dy; + clip_y = Y+src_h; + clip_h = H-src_h; + } +#ifdef WIN32 + BitBlt(fl_gc, dest_x, dest_y, src_w, src_h, fl_gc, src_x, src_y,SRCCOPY); + // NYI: need to redraw areas that the source of BitBlt was bad due to + // overlapped windows, probably similar to X version: +#else + XCopyArea(fl_display, fl_window, fl_window, fl_gc, + src_x, src_y, src_w, src_h, dest_x, dest_y); + // we have to sync the display and get the GraphicsExpose events! (sigh) + for (;;) { + XEvent e; XWindowEvent(fl_display, fl_window, ExposureMask, &e); + if (e.type == NoExpose) break; + // otherwise assumme it is a GraphicsExpose event: + draw_area(data, e.xexpose.x, e.xexpose.y, + e.xexpose.width, e.xexpose.height); + if (!e.xgraphicsexpose.count) break; + } +#endif + if (dx) draw_area(data, clip_x, dest_y, clip_w, src_h); + if (dy) draw_area(data, X, clip_y, W, clip_h); +} diff --git a/src/fl_set_font.cxx b/src/fl_set_font.cxx new file mode 100644 index 000000000..2d166bf9f --- /dev/null +++ b/src/fl_set_font.cxx @@ -0,0 +1,51 @@ +// fl_set_font.C + +// Add a font to the internal table. +// Also see fl_set_fonts.C which adds all possible fonts. + +#include <config.h> +#include <FL/Fl.H> +#include <FL/x.H> +#include "Fl_Font.H" +#include <stdlib.h> +#include <string.h> + +static int table_size; + +void Fl::set_font(Fl_Font fnum, const char *name) { + if (fnum >= table_size) { + int i = table_size; + if (!i) { // don't realloc the built-in table + table_size = 2*FL_FREE_FONT; + i = FL_FREE_FONT; + Fl_Fontdesc *t = (Fl_Fontdesc*)malloc(table_size*sizeof(Fl_Fontdesc)); + memcpy(t, fl_fonts, FL_FREE_FONT*sizeof(Fl_Fontdesc)); + fl_fonts = t; + } else { + table_size = 2*table_size; + fl_fonts=(Fl_Fontdesc*)realloc(fl_fonts, table_size*sizeof(Fl_Fontdesc)); + } + for (; i < table_size; i++) fl_fonts[i].name = 0; + } + Fl_Fontdesc *s = fl_fonts+fnum; + if (s->name) { + if (!strcmp(s->name, name)) {s->name = name; return;} +#ifndef WIN32 + if (s->xlist && s->n >= 0) XFreeFontNames(s->xlist); +#endif + for (Fl_XFont *f = s->first; f;) { +#ifndef WIN32 + if (f == fl_fixed_xfont) break; +#endif + Fl_XFont *n = f->next; delete f; f = n; + } + s->first = 0; + } + s->name = name; +#ifndef WIN32 + s->xlist = 0; +#endif + s->first = 0; +} + +const char *Fl::get_font(Fl_Font fnum) {return fl_fonts[fnum].name;} diff --git a/src/fl_set_fonts.cxx b/src/fl_set_fonts.cxx new file mode 100644 index 000000000..e3834d32b --- /dev/null +++ b/src/fl_set_fonts.cxx @@ -0,0 +1,300 @@ +// fl_set_fonts.C + +// This function fills in the fltk font table with all the fonts that +// are found on the X server. It tries to place the fonts into families +// and to sort them so the first 4 in a family are normal, bold, italic, +// and bold italic. + +#ifdef WIN32 +#include "fl_set_fonts_win32.C" +#else + +// Standard X fonts are matched by a pattern that is always of +// this form, and this pattern is put in the table: +// "-*-family-weight-slant-width1-style-*-registry-encoding" + +// Non-standard font names (those not starting with '-') are matched +// by a pattern of the form "prefix*suffix", where the '*' is where +// fltk thinks the point size is, or by the actual font name if no +// point size is found. + +// Fltk knows how to pull an "attribute" out of a font name, such as +// bold or italic, by matching known x font field values. All words +// that don't match a known attribute are combined into the "name" +// of the font. Names are compared before attributes for sorting, this +// makes the bold and plain version of a font come out next to each +// other despite the poor X font naming scheme. + +// By default fl_set_fonts() only does iso8859-1 encoded fonts. You can +// do all normal X fonts by passing "-*" or every possible font with "*". + +// Fl::set_font will take strings other than the ones this stores +// and can identify any font on X that way. You may want to write your +// own system of font management and not use this code. + +#include <FL/Fl.H> +#include <FL/x.H> +#include "Fl_Font.H" +#include <ctype.h> +#include <string.h> +#include <stdlib.h> + +// turn word N of a X font name into either some attribute bits +// (right now 0, FL_BOLD, or FL_ITALIC), or into -1 indicating that +// the word should be put into the name: + +static int attribute(int n, const char *p) { + // don't put blank things into name: + if (!*p || *p=='-' || *p=='*') return 0; + if (n == 3) { // weight + if (!strncmp(p,"normal",6) || + !strncmp(p,"light",5) || + !strncmp(p,"medium",6) || + !strncmp(p,"book",4)) return 0; + if (!strncmp(p,"bold",4) || !strncmp(p,"demi",4)) return FL_BOLD; + } else if (n == 4) { // slant + if (*p == 'r') return 0; + if (*p == 'i' || *p == 'o') return FL_ITALIC; + } else if (n == 5) { // sWidth + if (!strncmp(p,"normal",6)) return 0; + } + return -1; +} + +// return non-zero if the registry-encoding should be used: +extern const char* fl_encoding; +static int use_registry(const char *p) { + return *p && *p!='*' && strcmp(p,fl_encoding); +} + +// turn a stored (with *'s) X font name into a pretty name: +const char* Fl::get_font_name(Fl_Font fnum, int* ap) { + const char* p = fl_fonts[fnum].name; + if (!p) return ""; + static char *buffer; if (!buffer) buffer = new char[128]; + char *o = buffer; + + if (*p != '-') { // non-standard font, just replace * with spaces: + if (ap) { + int type = 0; + if (strstr(p,"bold")) type = FL_BOLD; + if (strstr(p,"ital")) type |= FL_ITALIC; + *ap = type; + } + for (;*p; p++) { + if (*p == '*' || *p == ' ' || *p == '-') { + do p++; while (*p == '*' || *p == ' ' || *p == '-'); + if (!*p) break; + *o++ = ' '; + } + *o++ = *p; + } + *o = 0; + return buffer; + } + + // get the family: + const char *x = fl_font_word(p,2); if (*x) x++; if (*x=='*') x++; + if (!*x) return p; + const char *e = fl_font_word(x,1); + strncpy(o,x,e-x); o += e-x; + + // collect all the attribute words: + int type = 0; + for (int n = 3; n <= 6; n++) { + // get the next word: + if (*e) e++; x = e; e = fl_font_word(x,1); + int t = attribute(n,x); + if (t < 0) {*o++ = ' '; strncpy(o,x,e-x); o += e-x;} + else type |= t; + } + + // skip over the '*' for the size and get the registry-encoding: + x = fl_font_word(e,2); + if (*x) {x++; *o++ = '('; while (*x) *o++ = *x++; *o++ = ')';} + + *o = 0; + if (ap) *ap = type; + + return buffer; +} + +// sort raw (non-'*') X font names into perfect order: + +static int ultrasort(const void *aa, const void *bb) { + const char *a = *(char **)aa; + const char *b = *(char **)bb; + + // sort all non x-fonts at the end: + if (*a != '-') { + if (*b == '-') return 1; + // 2 non-x fonts are matched by "numeric sort" + int ret = 0; + for (;;) { + if (isdigit(*a) && isdigit(*b)) { + int na = strtol(a, (char **)&a, 10); + int nb = strtol(b, (char **)&b, 10); + if (!ret) ret = na-nb; + } else if (*a != *b) { + return (*a-*b); + } else if (!*a) { + return ret; + } else { + a++; b++; + } + } + } else { + if (*b != '-') return -1; + } + + // skip the foundry (assumme equal): + for (a++; *a && *a++!='-';); + for (b++; *b && *b++!='-';); + + // compare the family and all the attribute words: + int atype = 0; + int btype = 0; + for (int n = 2; n <= 6; n++) { + int at = attribute(n,a); + int bt = attribute(n,b); + if (at < 0) { + if (bt >= 0) return 1; + for (;;) {if (*a!=*b) return *a-*b; b++; if (!*a || *a++=='-') break;} + } else { + if (bt < 0) return -1; + a = fl_font_word(a,1); if (*a) a++; + b = fl_font_word(b,1); if (*b) b++; + atype |= at; btype |= bt; + } + } + + // remember the pixel size: + int asize = atoi(a); + int bsize = atoi(b); + + // compare the registry/encoding: + a = fl_font_word(a,6); if (*a) a++; + b = fl_font_word(b,6); if (*b) b++; + if (use_registry(a)) { + if (!use_registry(b)) return 1; + int r = strcmp(a,b); if (r) return r; + } else { + if (use_registry(b)) return -1; + } + + if (atype != btype) return atype-btype; + if (asize != bsize) return asize-bsize; + + // something wrong, just do a string compare... + return strcmp(*(char**)aa, *(char**)bb); +} + +// converts a X font name to a standard starname, returns point size: +static int to_canonical(char *to, const char *from) { + char* c = fl_find_fontsize((char*)from); + if (!c) return -1; // no point size found... + char* endptr; + int size = strtol(c,&endptr,10); + if (from[0] == '-') { + // replace the "foundry" with -*-: + *to++ = '-'; *to++ = '*'; + for (from++; *from && *from != '-'; from++); + // skip to the registry-encoding: + endptr = (char*)fl_font_word(endptr,6); + if (*endptr && !use_registry(endptr+1)) endptr = ""; + } + int n = c-from; + strncpy(to,from,n); + to[n++] = '*'; + strcpy(to+n,endptr); + return size; +} + +static int fl_free_font = FL_FREE_FONT; + +Fl_Font Fl::set_fonts(const char* xstarname) { + fl_open_display(); + int xlistsize; + char buf[20]; + if (!xstarname) { + strcpy(buf,"-*-"); strcpy(buf+3,fl_encoding); + xstarname = buf; + } + char **xlist = XListFonts(fl_display, xstarname, 10000, &xlistsize); + if (!xlist) return (Fl_Font)fl_free_font; + qsort(xlist, xlistsize, sizeof(*xlist), ultrasort); + int used_xlist = 0; + for (int i=0; i<xlistsize;) { + int first_xlist = i; + const char *p = xlist[i++]; + char canon[1024]; + int size = to_canonical(canon, p); + if (size >= 0) { + for (;;) { // find all matching fonts: + if (i >= xlistsize) break; + const char *q = xlist[i]; + char this_canon[1024]; + if (to_canonical(this_canon, q) < 0) break; + if (strcmp(canon, this_canon)) break; + i++; + } + /*if (*p=='-' || i > first_xlist+1)*/ p = canon; + } + int j; + for (j = 0;; j++) { + if (j < FL_FREE_FONT) { + // see if it is one of our built-in fonts: + // if so, set the list of x fonts, since we have it anyway + if (fl_fonts[j].name && !strcmp(fl_fonts[j].name, p)) break; + } else { + j = fl_free_font++; + if (p == canon) p = strdup(p); else used_xlist = 1; + Fl::set_font((Fl_Font)j, p); + break; + } + } + if (!fl_fonts[j].xlist) { + fl_fonts[j].xlist = xlist+first_xlist; + fl_fonts[j].n = -(i-first_xlist); + used_xlist = 1; + } + } + if (!used_xlist) XFreeFontNames(xlist); + return (Fl_Font)fl_free_font; +} + +int Fl::get_font_sizes(Fl_Font fnum, int*& sizep) { + Fl_Fontdesc *s = fl_fonts+fnum; + if (!s->name) s = fl_fonts; // empty slot in table, use entry 0 + if (!s->xlist) { + fl_open_display(); + s->xlist = XListFonts(fl_display, s->name, 100, &(s->n)); + if (!s->xlist) return 0; + } + int listsize = s->n; if (listsize<0) listsize = -listsize; + static int sizes[128]; + int numsizes = 0; + for (int i = 0; i < listsize; i++) { + char *q = s->xlist[i]; + char *d = fl_find_fontsize(q); + if (!d) continue; + int s = strtol(d,0,10); + if (!numsizes || sizes[numsizes-1] < s) { + sizes[numsizes++] = s; + } else { + // insert-sort the new size into list: + int n; + for (n = numsizes-1; n > 0; n--) if (sizes[n-1] < s) break; + if (sizes[n] != s) { + for (int m = numsizes; m > n; m--) sizes[m] = sizes[m-1]; + sizes[n] = s; + numsizes++; + } + } + } + sizep = sizes; + return numsizes; +} + +#endif +// end of fl_set_fonts.C diff --git a/src/fl_set_fonts_win32.cxx b/src/fl_set_fonts_win32.cxx new file mode 100755 index 000000000..2a00363e9 --- /dev/null +++ b/src/fl_set_fonts_win32.cxx @@ -0,0 +1,58 @@ +// fl_set_fonts_win32.C + +// This function fills in the fltk font table with all the fonts that +// are found on the X server. It tries to place the fonts into families +// and to sort them so the first 4 in a family are normal, bold, italic, +// and bold italic. + +#include <FL/Fl.H> +#include <FL/win32.H> +#include "Fl_Font.H" +#include <ctype.h> +#include <string.h> +#include <stdlib.h> + +// turn a stored font name into a pretty name: +const char* Fl::get_font_name(Fl_Font fnum, int* ap) { + const char* p = fl_fonts[fnum].name; + if (!p || !*p) {if (ap) *ap = 0; return "";} + if (ap) switch (*p) { + case 'B': *ap = FL_BOLD; break; + case 'I': *ap = FL_ITALIC; break; + case 'P': *ap = FL_BOLD | FL_ITALIC; break; + default: *ap = 0; break; + } + return p+1; +} + +static int fl_free_font = FL_FREE_FONT; + +static int CALLBACK enumcb(ENUMLOGFONT FAR *lpelf, + NEWTEXTMETRIC FAR *lpntm, int FontType, LPARAM p) { + if (!p && lpelf->elfLogFont.lfCharSet != ANSI_CHARSET) return 1; + char *n = (char*)(lpelf->elfFullName); + for (int i=0; i<FL_FREE_FONT; i++) // skip if one of our built-in fonts + if (!strcmp(Fl::get_font_name((Fl_Font)i),n)) return 1; + char buffer[128]; + strcpy(buffer+1, n); + buffer[0] = ' '; Fl::set_font((Fl_Font)(fl_free_font++), strdup(buffer)); + if (lpelf->elfLogFont.lfWeight <= 400) + buffer[0] = 'B', Fl::set_font((Fl_Font)(fl_free_font++), strdup(buffer)); + buffer[0] = 'I'; Fl::set_font((Fl_Font)(fl_free_font++), strdup(buffer)); + if (lpelf->elfLogFont.lfWeight <= 400) + buffer[0] = 'P', Fl::set_font((Fl_Font)(fl_free_font++), strdup(buffer)); + return 1; +} + +Fl_Font Fl::set_fonts(const char* xstarname) { + EnumFontFamilies(fl_gc, NULL, (FONTENUMPROC)enumcb, xstarname != 0); + return (Fl_Font)fl_free_font; +} + +int Fl::get_font_sizes(Fl_Font fnum, int*& sizep) { + // pretend all fonts are scalable (most are and I don't know how + // to tell anyways) + static int array[1]; + sizep = array; + return 1; +} diff --git a/src/fl_set_gray.cxx b/src/fl_set_gray.cxx new file mode 100644 index 000000000..c3b869f47 --- /dev/null +++ b/src/fl_set_gray.cxx @@ -0,0 +1,35 @@ +// fl_set_gray.C + +// -fg, -bg, and -bg2 switches + +#include <FL/Fl.H> + +void Fl::background(uchar r, uchar g, uchar b) { + // replace the gray ramp so that color 47 is this color + int i; + for (i = 32; i <= 47; i++) { + int m = (i-32)*255/23; + Fl::set_color(i,r*m/166,g*m/166,b*m/166); + } + for (; i < 56; i++) { + int m = 255-(i-32)*255/23; + Fl::set_color(i,255-(255-r)*m/89,255-(255-g)*m/89,255-(255-b)*m/89); + } +} + +static void set_others() { + uchar r,g,b; Fl::get_color(FL_BLACK,r,g,b); + uchar r1,g1,b1; Fl::get_color(FL_WHITE,r1,g1,b1); + Fl::set_color(FL_INACTIVE_COLOR,(2*r+r1)/3, (2*g+g1)/3, (2*b+b1)/3); + Fl::set_color(FL_SELECTION_COLOR,(2*r1+r)/3, (2*g1+g)/3, (2*b1+b)/3); +} + +void Fl::foreground(uchar r, uchar g, uchar b) { + Fl::set_color(FL_BLACK,r,g,b); + set_others(); +} + +void Fl::background2(uchar r, uchar g, uchar b) { + Fl::set_color(FL_WHITE,r,g,b); + set_others(); +} diff --git a/src/fl_shadow_box.cxx b/src/fl_shadow_box.cxx new file mode 100644 index 000000000..d48b6ba96 --- /dev/null +++ b/src/fl_shadow_box.cxx @@ -0,0 +1,31 @@ +// fl_shadow_box.C + +// Box drawing code for an obscure box type. +// These box types are in seperate files so they are not linked +// in if not used. + +#include <FL/Fl.H> +#include <FL/fl_draw.H> + +#define BW 3 + +static void fl_shadow_frame(int x, int y, int w, int h, Fl_Color c) { + fl_color(FL_DARK3); + fl_rectf(x+BW, y+h, w, BW); + fl_rectf(x+w, y+BW, BW, h); + fl_color(c); + fl_rect(x,y,w,h); +} + +static void fl_shadow_box(int x, int y, int w, int h, Fl_Color c) { + fl_color(c); + fl_rectf(x+1,y+1,w-2,h-2); + fl_shadow_frame(x,y,w,h,FL_GRAY0); +} + +extern void fl_internal_boxtype(Fl_Boxtype, Fl_Box_Draw_F*); +Fl_Boxtype define_FL_SHADOW_BOX() { + fl_internal_boxtype(_FL_SHADOW_FRAME, fl_shadow_frame); + fl_internal_boxtype(_FL_SHADOW_BOX, fl_shadow_box); + return _FL_SHADOW_BOX; +} diff --git a/src/fl_shortcut.cxx b/src/fl_shortcut.cxx new file mode 100644 index 000000000..b81853873 --- /dev/null +++ b/src/fl_shortcut.cxx @@ -0,0 +1,100 @@ +// fl_shortcut.C + +// Code to test and parse fltk shortcut numbers. + +// A shortcut is a keysym or'd with shift flags. In the simplest +// sense a shortcut is matched if the shift state is exactly as +// given and the key returning that keysym is pressed. + +// To make it easier to match some things it is more complex: + +// Only FL_META, FL_ALT, FL_SHIFT, and FL_CTRL must be "off". A +// zero in the other shift flags indicates "dont care". + +// It also checks against the first character of Fl::event_text(), +// and zero for FL_SHIFT means "don't care". +// This allows punctuation shortcuts like "#" to work (rather than +// calling it "shift+3") + +#include <FL/Fl.H> +#include <FL/Fl_Widget.H> +#include <ctype.h> +#include <string.h> +#ifndef WIN32 +#include <X11/Xlib.h> +#endif + +int Fl::test_shortcut(int shortcut) { + if (!shortcut) return 0; + + int shift = Fl::event_state(); + // see if any required shift flags are off: + if ((shortcut&shift) != (shortcut&0x7fff0000)) return 0; + // record shift flags that are wrong: + int mismatch = (shortcut^shift)&0x7fff0000; + // these three must always be correct: + if (mismatch&(FL_META|FL_ALT|FL_CTRL)) return 0; + + int key = shortcut & 0xffff; + + // if shift is also correct, check for exactly equal keysyms: + if (!(mismatch&(FL_SHIFT)) && key == Fl::event_key()) return 1; + + // try matching ascii, ignore shift: + if (key == event_text()[0]) return 1; + + // kludge so that Ctrl+'_' works (as opposed to Ctrl+'^_'): + if ((shift&FL_CTRL) && key >= 0x3f && key <= 0x5F + && event_text()[0]==(key^0x40)) return 1; + return 0; +} + +const char * fl_shortcut_label(int shortcut) { + static char buf[20]; + char *p = buf; + if (!shortcut) {*p = 0; return buf;} + if (shortcut & FL_META) {strcpy(p,"Meta+"); p += 5;} + if (shortcut & FL_ALT) {strcpy(p,"Alt+"); p += 4;} + if (shortcut & FL_SHIFT) {strcpy(p,"Shift+"); p += 6;} + if (shortcut & FL_CTRL) {strcpy(p,"Ctrl+"); p += 5;} + int key = shortcut & 0xFFFF; +#ifdef WIN32 + if (key >= FL_F && key <= FL_F_Last) { + *p++ = 'F'; + if (key > FL_F+9) *p++ = (key-FL_F)/10+'0'; + *p++ = (key-FL_F)%10 + '0'; + } else { + if (key == FL_Enter || key == '\r') {strcpy(p,"Enter"); return buf;} + *p++ = uchar(key); + } + *p = 0; + return buf; +#else + const char* q; + if (key == FL_Enter || key == '\r') q="Enter"; // don't use Xlib's "Return" + else if (key > 32 && key < 0x100) q = 0; + else q = XKeysymToString(key); + if (!q) {*p++ = uchar(key); *p = 0; return buf;} + if (p > buf) {strcpy(p,q); return buf;} else return q; +#endif +} + +// Tests for &x shortcuts in button labels: + +int Fl_Widget::test_shortcut(const char *label) { + char c = Fl::event_text()[0]; + if (!c || !label) return 0; + for (;;) { + if (!*label) return 0; + if (*label++ == '&' && *label) { + if (*label == '&') label++; + else if (*label == c) return 1; + else return 0; + } + } +} + +int Fl_Widget::test_shortcut() { + if (!(flags()&SHORTCUT_LABEL)) return 0; + return test_shortcut(label()); +} diff --git a/src/fl_show_colormap.cxx b/src/fl_show_colormap.cxx new file mode 100644 index 000000000..5e3b18c88 --- /dev/null +++ b/src/fl_show_colormap.cxx @@ -0,0 +1,124 @@ +// fl_show_colormap.C + +// Select a color from the colormap. +// Pretty much unchanged from Forms. + +#include <FL/Fl.H> +#include <FL/Fl_Single_Window.H> +#include <FL/fl_draw.H> +#include <FL/fl_show_colormap.H> +#include <config.h> + +#define BOXSIZE 14 +#define BORDER 4 + +class ColorMenu : public Fl_Window { + Fl_Color initial; + Fl_Color which, previous; + int done; + void drawbox(Fl_Color); + void draw(); + int handle(int); +public: + ColorMenu(Fl_Color oldcol); + Fl_Color run(); +}; + +ColorMenu::ColorMenu(Fl_Color oldcol) : + Fl_Window(BOXSIZE*8+1+2*BORDER, BOXSIZE*32+1+2*BORDER) { + clear_border(); + set_modal(); + initial = which = oldcol; +} + +void ColorMenu::drawbox(Fl_Color c) { + if (c < 0 || c > 255) return; + int x = (c%8)*BOXSIZE+BORDER; + int y = (c/8)*BOXSIZE+BORDER; +#if BORDER_WIDTH < 3 + if (c == which) fl_draw_box(FL_DOWN_BOX, x+1, y+1, BOXSIZE-1, BOXSIZE-1, c); + else fl_draw_box(FL_BORDER_BOX, x, y, BOXSIZE+1, BOXSIZE+1, c); +#else + fl_draw_box(c == which ? FL_DOWN_BOX : FL_BORDER_BOX, + x, y, BOXSIZE+1, BOXSIZE+1, c); +#endif +} + +void ColorMenu::draw() { + if (damage() != 1) { + fl_draw_box(FL_UP_BOX,0,0,w(),h(),color()); + for (int c = 0; c < 256; c++) drawbox((Fl_Color)c); + } else { + drawbox(previous); + drawbox(which); + } + previous = which; +} + +int ColorMenu::handle(int e) { + int c = which; + switch (e) { + case FL_PUSH: + case FL_DRAG: { + int X = (Fl::event_x_root() - x() - BORDER); + if (X >= 0) X = X/BOXSIZE; + int Y = (Fl::event_y_root() - y() - BORDER); + if (Y >= 0) Y = Y/BOXSIZE; + if (X >= 0 && X < 8 && Y >= 0 && Y < 32) + c = 8*Y + X; + else + c = initial; + } break; + case FL_RELEASE: + done = 1; + return 1; + case FL_KEYBOARD: + switch (Fl::event_key()) { + case FL_Up: if (c > 7) c -= 8; break; + case FL_Down: if (c < 256-8) c += 8; break; + case FL_Left: if (c > 0) c--; break; + case FL_Right: if (c < 255) c++; break; + case FL_Escape: which = initial; done = 1; return 1; + case FL_Enter: done = 1; return 1; + default: return 0; + } + break; + default: + return 0; + } + if (c != which) { + which = (Fl_Color)c; damage(1); + int bx = (c%8)*BOXSIZE+BORDER; + int by = (c/8)*BOXSIZE+BORDER; + int px = x(); + int py = y(); + if (px+bx+BOXSIZE+BORDER >= Fl::w()) px = Fl::w()-bx-BOXSIZE-BORDER; + if (py+by+BOXSIZE+BORDER >= Fl::h()) py = Fl::h()-by-BOXSIZE-BORDER; + if (px+bx < BORDER) px = BORDER-bx; + if (py+by < BORDER) py = BORDER-by; + position(px,py); + } + return 1; +} + +extern char fl_override_redirect; // hack for menus + +Fl_Color ColorMenu::run() { + if (which < 0 || which > 255) { + position(Fl::event_x_root()-w()/2, Fl::event_y_root()-y()/2); + } else { + position(Fl::event_x_root()-(initial%8)*BOXSIZE-BOXSIZE/2-BORDER, + Fl::event_y_root()-(initial/8)*BOXSIZE-BOXSIZE/2-BORDER); + } + Fl::grab(*this); + show(); + done = 0; + while (!done) Fl::wait(); + Fl::release(); + return which; +} + +Fl_Color fl_show_colormap(Fl_Color oldcol) { + ColorMenu m(oldcol); + return m.run(); +} diff --git a/src/fl_symbols.cxx b/src/fl_symbols.cxx new file mode 100644 index 000000000..df2f20926 --- /dev/null +++ b/src/fl_symbols.cxx @@ -0,0 +1,364 @@ +// fl_symbols.C + +// These are small graphics drawn by the normal label-drawing +// code when the string starts with an '@' sign. + +// Adapted from original code written by: + +// Written by Mark Overmars +// Version 2.1 a +// Date: Oct 2, 1992 + +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <string.h> + +typedef struct { + const char *name; + void (*drawit)(Fl_Color); + char scalable; + char notempty; +} SYMBOL; + +#define MAXSYMBOL 211 + /* Maximal number of symbols in table. Only half of them are + used. Should be prime. */ + +static SYMBOL symbols[MAXSYMBOL]; /* The symbols */ +static int symbnumb = -1; /* Their number */ + +static int find(const char *name) { +// returns hash entry if it exists, or first empty slot: + int pos = name[0] ? ( + name[1] ? ( + name[2] ? 71*name[0]+31*name[1]+name[2] : 31*name[0]+name[1] + ) : + name[0] + ) : 0; + pos %= MAXSYMBOL; + int hh2 = name[0] ? ( + (name[1]) ? 51*name[0]+3*name[1] : 3*name[0] + ) : 1; + hh2 %= MAXSYMBOL; if (!hh2) hh2 = 1; + for (;;) { + if (!symbols[pos].notempty) return pos; + if (!strcmp(symbols[pos].name,name)) return pos; + pos = (pos + hh2) % MAXSYMBOL; + } +} + +static void fl_init_symbols(void); + +/**************** The routines seen by the user *************************/ + +int fl_add_symbol(const char *name, void (*drawit)(Fl_Color), int scalable) +/* Adds a symbol to the system. Returns whether correct. */ +{ + fl_init_symbols(); + int pos; + if (symbnumb > MAXSYMBOL / 2) return 0; // table is full + pos = find(name); + symbols[pos].name = name; + symbols[pos].drawit = drawit; + symbols[pos].notempty = 1; + symbols[pos].scalable = scalable; + symbnumb++; + return 1; +} + +int fl_return_arrow(int x,int y,int w,int h); + +// provided for back compatability: +int fl_draw_symbol(const char *label,int x,int y,int w,int h,Fl_Color col) { + const char *p = label; + if (*p++ != '@') return 0; + fl_init_symbols(); + int equalscale = 0; + if (*p == '#') {equalscale = 1; p++;} + if (*p == '-' && p[1]>='1' && p[1]<='9') { + int n = p[1]-'0'; + x += n; y += n; w -= 2*n; h -= 2*n; + p += 2; + } else if (*p == '+' && p[1]>='1' && p[1]<='9') { + int n = p[1]-'0'; + x -= n; y -= n; w += 2*n; h += 2*n; + p += 2; + } + if (w < 10) {x -= (10-w)/2; w = 10;} + if (h < 10) {y -= (10-h)/2; h = 10;} + w = (w-1)|1; h = (h-1)|1; + int rotangle; + switch (*p++) { + case '0': + rotangle = 1000*(p[1]-'0') + 100*(p[2]-'0') + 10*(p[3]-'0'); + p += 4; + break; + case '1': rotangle = 2250; break; + case '2': rotangle = 2700; break; + case '3': rotangle = 3150; break; + case '4': rotangle = 1800; break; + case '5': + case '6': rotangle = 0; break; + case '7': rotangle = 1350; break; + case '8': rotangle = 900; break; + case '9': rotangle = 450; break; + default: rotangle = 0; p--; break; + } + int pos = find(p); + if (!symbols[pos].notempty) return 0; + if (symbols[pos].scalable == 3) { // kludge to detect return arrow + fl_return_arrow(x,y,w,h); + return 1; + } + fl_push_matrix(); + fl_translate(x+w/2,y+h/2); + if (symbols[pos].scalable) { + if (equalscale) {if (w<h) h = w; else w = h;} + fl_scale(0.5*w, 0.5*h); + fl_rotate(rotangle/10.0); + } + (symbols[pos].drawit)(col); + fl_pop_matrix(); + return 1; +} + +/******************** THE DEFAULT SYMBOLS ****************************/ + +/* Some help stuff */ + +#define BP fl_begin_polygon() +#define EP fl_end_polygon() +#define BL fl_begin_line() +#define EL fl_end_line() +#define BC fl_begin_loop() +#define EC fl_end_loop() +#define vv(x,y) fl_vertex(x,y) + +static void rectangle(double x,double y,double x2,double y2,Fl_Color c) { + fl_color(c); + BP; vv(x,y); vv(x2,y); vv(x2,y2); vv(x,y2); EP; + fl_color(FL_BLACK); + BC; vv(x,y); vv(x2,y); vv(x2,y2); vv(x,y2); EC; +} + +/* The drawing routines */ + +static void draw_arrow1(Fl_Color col) +{ + fl_color(col); + BP; vv(-0.8,-0.4); vv(-0.8,0.4); vv(0.0,0.4); vv(0.0,-0.4); EP; + BP; vv(0.0,0.8); vv(0.8,0.0); vv(0.0,-0.8); vv(0.0,-0.4); vv(0.0,0.4); EP; + fl_color(FL_BLACK); + BC; vv(-0.8,-0.4); vv(-0.8,0.4); vv(0.0,0.4); vv(0.0,0.8); vv(0.8,0.0); + vv(0.0,-0.8); vv(0.0,-0.4); EC; +} + +static void draw_arrow1bar(Fl_Color col) +{ + draw_arrow1(col); + rectangle(.6,-.8,.9,.8,col); +} + +static void draw_arrow2(Fl_Color col) +{ + fl_color(col); + BP; vv(-0.3,0.8); vv(0.50,0.0); vv(-0.3,-0.8); EP; + fl_color(FL_BLACK); + BC; vv(-0.3,0.8); vv(0.50,0.0); vv(-0.3,-0.8); EC; +} + +static void draw_arrow3(Fl_Color col) +{ + fl_color(col); + BP; vv(0.1,0.8); vv(0.9,0.0); vv(0.1,-0.8); EP; + BP; vv(-0.7,0.8); vv(0.1,0.0); vv(-0.7,-0.8); EP; + fl_color(FL_BLACK); + BC; vv(0.1,0.8); vv(0.9,0.0); vv(0.1,-0.8); EC; + BC; vv(-0.7,0.8); vv(0.1,0.0); vv(-0.7,-0.8); EC; +} + +static void draw_arrowbar(Fl_Color col) +{ + fl_color(col); + BP; vv(0.2,0.8); vv(0.6,0.8); vv(0.6,-0.8); vv(0.2,-0.8); EP; + BP; vv(-0.6,0.8); vv(0.2,0.0); vv(-0.6,-0.8); EP; + fl_color(FL_BLACK); + BC; vv(0.2,0.8); vv(0.6,0.8); vv(0.6,-0.8); vv(0.2,-0.8); EC; + BC; vv(-0.6,0.8); vv(0.2,0.0); vv(-0.6,-0.8); EC; +} + +static void draw_arrowbox(Fl_Color col) +{ + fl_color(col); + BP; vv(-0.6,0.8); vv(0.2,0.0); vv(-0.6,-0.8); EP; + fl_color(FL_BLACK); + BC; vv(0.2,0.8); vv(0.6,0.8); vv(0.6,-0.8); vv(0.2,-0.8); EC; + BC; vv(-0.6,0.8); vv(0.2,0.0); vv(-0.6,-0.8); EC; +} + +static void draw_bararrow(Fl_Color col) +{ + fl_color(col); + BP; vv(0.1,0.8); vv(0.9,0.0); vv(0.1,-0.8); EP; + BP; vv(-0.5,0.8); vv(-0.1,0.8); vv(-0.1,-0.8); vv(-0.5,-0.8); EP; + fl_color(FL_BLACK); + BC; vv(0.1,0.8); vv(0.9,0.0); vv(0.1,-0.8); EC; + BC; vv(-0.5,0.8); vv(-0.1,0.8); vv(-0.1,-0.8); vv(-0.5,-0.8); EC; +} + +static void draw_doublebar(Fl_Color col) { + rectangle(-0.6,-0.8,-.1,.8,col); + rectangle(.1,-0.8,.6,.8,col); +} + +static void draw_arrow01(Fl_Color col) + { fl_rotate(180); draw_arrow1(col); } + +static void draw_arrow02(Fl_Color col) + { fl_rotate(180); draw_arrow2(col); } + +static void draw_arrow03(Fl_Color col) + { fl_rotate(180); draw_arrow3(col); } + +static void draw_0arrowbar(Fl_Color col) + { fl_rotate(180); draw_arrowbar(col); } + +static void draw_0arrowbox(Fl_Color col) + { fl_rotate(180); draw_arrowbox(col); } + +static void draw_0bararrow(Fl_Color col) + { fl_rotate(180); draw_bararrow(col); } + +static void draw_doublearrow(Fl_Color col) +{ + fl_color(col); + BP; vv(-0.35,-0.4); vv(-0.35,0.4); vv(0.35,0.4); vv(0.35,-0.4); EP; + BP; vv(0.15,0.8); vv(0.95,0.0); vv(0.15,-0.8); EP; + BP; vv(-0.15,0.8); vv(-0.95,0.0); vv(-0.15,-0.8); EP; + fl_color(FL_BLACK); + BC; vv(-0.15,0.4); vv(0.15,0.4); vv(0.15,0.8); vv(0.95,0.0); + vv(0.15,-0.8); vv(0.15,-0.4); vv(-0.15,-0.4); vv(-0.15,-0.8); + vv(-0.95,0.0); vv(-0.15,0.8); EC; +} + +static void draw_arrow(Fl_Color col) +{ + fl_color(col); + BP; vv(0.65,0.1); vv(1.0,0.0); vv(0.65,-0.1); EP; + fl_color(FL_BLACK); + BL; vv(-1.0,0.0); vv(0.65,0.0); EL; + BC; vv(0.65,0.1); vv(1.0,0.0); vv(0.65,-0.1); EC; +} + +#if 0 +static void fl_draw_returnarrow(Fl_Color col) { + fl_color(col); + fl_begin_polygon(); + vv(-0.8,0.0); vv(-0.1,0.7); vv(-0.1,-0.7); + fl_end_polygon(); + fl_color(FL_BLACK); + fl_begin_loop(); vv(-0.8,0.0); vv(-0.1,0.7); vv(-0.1,-0.7);fl_end_loop(); + fl_begin_line(); vv(-0.1,0.0); vv(0.8,0.0); vv(0.8,-0.7); fl_end_line(); +} +#endif + +static void draw_square(Fl_Color col) + { rectangle(-1,-1,1,1,col); } + +static void draw_circle(Fl_Color col) { + fl_color(col); BP; fl_circle(0,0,1); EP; + fl_color(FL_BLACK); BC; fl_circle(0,0,1); EC; +} + +static void draw_line(Fl_Color col) + { fl_color(col); BL; vv(-1.0,0.0); vv(1.0,0.0); EL; } + +static void draw_plus(Fl_Color col) +{ + fl_color(col); + BP; vv(-0.9,-0.15); vv(-0.9,0.15); vv(0.9,0.15); vv(0.9,-0.15); EP; + BP; vv(-0.15,-0.9); vv(-0.15,0.9); vv(0.15,0.9); vv(0.15,-0.9); EP; + fl_color(FL_BLACK); + BC; + vv(-0.9,-0.15); vv(-0.9,0.15); vv(-0.15,0.15); vv(-0.15,0.9); + vv(0.15,0.9); vv(0.15,0.15); vv(0.9,0.15); vv(0.9,-0.15); + vv(0.15,-0.15); vv(0.15,-0.9); vv(-0.15,-0.9); vv(-0.15,-0.15); + EC; +} + +static void draw_uparrow(Fl_Color) { + fl_color(FL_LIGHT3); + BL; vv(-.8,.8); vv(-.8,-.8); vv(.8,0); EL; + fl_color(FL_DARK3); + BL; vv(-.8,.8); vv(.8, 0); EL; +} + +static void draw_downarrow(Fl_Color) { + fl_color(FL_DARK3); + BL; vv(-.8,.8); vv(-.8,-.8); vv(.8,0); EL; + fl_color(FL_LIGHT3); + BL; vv(-.8,.8); vv(.8, 0); EL; +} + +static void draw_menu(Fl_Color col) +{ + rectangle(-0.65, 0.85, 0.65, -0.25, col); + rectangle(-0.65, -0.6, 0.65, -1.0, col); +} + +static void fl_init_symbols(void) { + static char beenhere; + if (beenhere) return; + beenhere = 1; + symbnumb = 0; + + fl_add_symbol("", draw_arrow1, 1); + fl_add_symbol("->", draw_arrow1, 1); + fl_add_symbol(">", draw_arrow2, 1); + fl_add_symbol(">>", draw_arrow3, 1); + fl_add_symbol(">|", draw_arrowbar, 1); + fl_add_symbol(">[]", draw_arrowbox, 1); + fl_add_symbol("|>", draw_bararrow, 1); + fl_add_symbol("<-", draw_arrow01, 1); + fl_add_symbol("<", draw_arrow02, 1); + fl_add_symbol("<<", draw_arrow03, 1); + fl_add_symbol("|<", draw_0arrowbar, 1); + fl_add_symbol("[]<", draw_0arrowbox, 1); + fl_add_symbol("<|", draw_0bararrow, 1); + fl_add_symbol("<->", draw_doublearrow, 1); + fl_add_symbol("-->", draw_arrow, 1); + fl_add_symbol("+", draw_plus, 1); + fl_add_symbol("->|", draw_arrow1bar, 1); + fl_add_symbol("arrow", draw_arrow, 1); + fl_add_symbol("returnarrow", 0, 3); + fl_add_symbol("square", draw_square, 1); + fl_add_symbol("circle", draw_circle, 1); + fl_add_symbol("line", draw_line, 1); + fl_add_symbol("plus", draw_plus, 1); + fl_add_symbol("menu", draw_menu, 1); + fl_add_symbol("UpArrow", draw_uparrow, 1); + fl_add_symbol("DnArrow", draw_downarrow, 1); + fl_add_symbol("||", draw_doublebar, 1); +} + +//////////////////////////////////////////////////////////////// + +#include <FL/Fl_Widget.H> + +// this is the labeltype function: +extern void fl_normal_label(const Fl_Label*, int, int, int, int, Fl_Align); +static void fl_symbol_label( + const Fl_Label* o, int x, int y, int w, int h, Fl_Align align) +{ + if (!fl_draw_symbol(o->value, x, y, w, h, (Fl_Color)o->color)) + fl_normal_label(o, x, y, w, h, align); +} + +Fl_Labeltype define_FL_SYMBOL_LABEL() { + Fl::set_labeltype(_FL_SYMBOL_LABEL, fl_symbol_label, 0); + return _FL_SYMBOL_LABEL; +} + +void Fl::enable_symbols() { + Fl::set_labeltype(FL_NORMAL_LABEL, fl_symbol_label, 0); +} diff --git a/src/fl_vertex.cxx b/src/fl_vertex.cxx new file mode 100644 index 000000000..cdf9b1df0 --- /dev/null +++ b/src/fl_vertex.cxx @@ -0,0 +1,200 @@ +// fl_vertex.C + +// Portable drawing code for drawing arbitrary shapes with +// simple 2D transformations. See also fl_arc.C + +#include <FL/fl_draw.H> +#include <FL/x.H> +#include <FL/math.h> +#include <stdlib.h> + +struct matrix {double a, b, c, d, x, y;}; + +static matrix m = {1, 0, 0, 1, 0, 0}; + +static matrix stack[10]; +static int sptr = 0; + +void fl_push_matrix() {stack[sptr++] = m;} + +void fl_pop_matrix() {m = stack[--sptr];} + +void fl_mult_matrix(double a, double b, double c, double d, double x, double y) { + matrix o; + o.a = a*m.a + b*m.c; + o.b = a*m.b + b*m.d; + o.c = c*m.a + d*m.c; + o.d = c*m.b + d*m.d; + o.x = x*m.a + y*m.c + m.x; + o.y = x*m.b + y*m.d + m.y; + m = o; +} + +void fl_scale(double x,double y) {fl_mult_matrix(x,0,0,y,0,0);} + +void fl_scale(double x) {fl_mult_matrix(x,0,0,x,0,0);} + +void fl_translate(double x,double y) {fl_mult_matrix(1,0,0,1,x,y);} + +void fl_rotate(double d) { + if (d) { + double s, c; + if (d == 0) {s = 0; c = 1;} + else if (d == 90) {s = 1; c = 0;} + else if (d == 180) {s = 0; c = -1;} + else if (d == 270 || d == -90) {s = -1; c = 0;} + else {s = sin(d*M_PI/180); c = cos(d*M_PI/180);} + fl_mult_matrix(c,-s,s,c,0,0); + } +} + +static XPoint *p; +// typedef what the x,y fields in a point are: +#ifdef WIN32 +typedef int COORD_T; +#else +typedef short COORD_T; +#endif + +static int p_size; +static int n; +static int what; +enum {LINE, LOOP, POLYGON, POINT_}; + +void fl_begin_points() {n = 0; what = POINT_;} + +void fl_begin_line() {n = 0; what = LINE;} + +void fl_begin_loop() {n = 0; what = LOOP;} + +void fl_begin_polygon() {n = 0; what = POLYGON;} + +double fl_transform_x(double x, double y) {return x*m.a + y*m.c + m.x;} + +double fl_transform_y(double x, double y) {return x*m.b + y*m.d + m.y;} + +double fl_transform_dx(double x, double y) {return x*m.a + y*m.c;} + +double fl_transform_dy(double x, double y) {return x*m.b + y*m.d;} + +static void fl_transformed_vertex(COORD_T x, COORD_T y) { + if (!n || x != p[n-1].x || y != p[n-1].y) { + if (n >= p_size) { + p_size = p ? 2*p_size : 16; + p = (XPoint *)realloc((void*)p, p_size*sizeof(*p)); + } + p[n].x = x; + p[n].y = y; + n++; + } +} + +void fl_transformed_vertex(double xf, double yf) { + fl_transformed_vertex(COORD_T(xf+.5), COORD_T(yf+.5)); +} + +void fl_vertex(double x,double y) { + fl_transformed_vertex(x*m.a + y*m.c + m.x, x*m.b + y*m.d + m.y); +} + +void fl_end_points() { +#ifdef WIN32 + for (int i=0; i<n; i++) SetPixel(fl_gc, p[i].x, p[i].y, fl_RGB()); +#else + if (n>1) XDrawPoints(fl_display, fl_window, fl_gc, p, n, 0); +#endif +} + +void fl_end_line() { +#ifdef WIN32 + if (n>1) Polyline(fl_gc, p, n); +#else + if (n>1) XDrawLines(fl_display, fl_window, fl_gc, p, n, 0); +#endif +} + +static void fixloop() { // remove equal points from closed path + while (n>2 && p[n-1].x == p[0].x && p[n-1].y == p[0].y) n--; +} + +void fl_end_loop() { + fixloop(); + if (n>2) fl_transformed_vertex((COORD_T)p[0].x, (COORD_T)p[0].y); + fl_end_line(); +} + +void fl_end_polygon() { + fixloop(); +#ifdef WIN32 + if (n>2) { + SelectObject(fl_gc, fl_brush()); + Polygon(fl_gc, p, n); + } +#else + if (n>2) XFillPolygon(fl_display, fl_window, fl_gc, p, n, Convex, 0); +#endif +} + +static int gap; +#ifdef WIN32 +static int counts[20]; +static int numcount; +#endif + +void fl_begin_complex_polygon() { + fl_begin_polygon(); + gap = 0; +#ifdef WIN32 + numcount = 0; +#endif +} + +void fl_gap() { + while (n>gap+2 && p[n-1].x == p[gap].x && p[n-1].y == p[gap].y) n--; + if (n > gap+2) { + fl_transformed_vertex((COORD_T)p[gap].x, (COORD_T)p[gap].y); +#ifdef WIN32 + counts[numcount++] = n-gap; +#endif + gap = n; + } else { + n = gap; + } +} + +void fl_end_complex_polygon() { + fl_gap(); +#ifdef WIN32 + if (n>2) { + SelectObject(fl_gc, fl_brush()); + PolyPolygon(fl_gc, p, counts, numcount); + } +#else + if (n>2) XFillPolygon(fl_display, fl_window, fl_gc, p, n, 0, 0); +#endif +} + +// shortcut the closed circles so they use XDrawArc: +// warning: these do not draw rotated ellipses correctly! +// See fl_arc.c for portable version. + +void fl_circle(double x, double y,double r) { + double xt = fl_transform_x(x,y); + double yt = fl_transform_y(x,y); + double rx = r * (m.c ? sqrt(m.a*m.a+m.c*m.c) : fabs(m.a)); + double ry = r * (m.b ? sqrt(m.b*m.b+m.d*m.d) : fabs(m.d)); + int llx = int(xt-rx+.5); + int w = int(xt+rx+.5)-llx; + int lly = int(yt-ry+.5); + int h = int(yt+ry+.5)-lly; +#ifdef WIN32 + if (what==POLYGON) { + SelectObject(fl_gc, fl_brush()); + Pie(fl_gc, llx, lly, llx+w, lly+h, 0,0, 0,0); + } else + Arc(fl_gc, llx, lly, llx+w, lly+h, 0,0, 0,0); +#else + (what == POLYGON ? XFillArc : XDrawArc) + (fl_display, fl_window, fl_gc, llx, lly, w, h, 0, 360*64); +#endif +} diff --git a/src/forms_bitmap.cxx b/src/forms_bitmap.cxx new file mode 100644 index 000000000..1b0eae749 --- /dev/null +++ b/src/forms_bitmap.cxx @@ -0,0 +1,24 @@ +// forms_bitmap.C +// Forms compatability widget to draw a bitmap + +#include <FL/forms.H> + +Fl_FormsBitmap::Fl_FormsBitmap( + Fl_Boxtype t, int x, int y, int w, int h, const char* l) +: Fl_Widget(x, y, w, h, l) { + box(t); + b = 0; + color(FL_BLACK); + align(FL_ALIGN_BOTTOM); +} + +void Fl_FormsBitmap::set(int W, int H, const uchar *bits) { + delete b; + bitmap(new Fl_Bitmap(bits, W, H)); +} + +void Fl_FormsBitmap::draw() { + draw_box(box(), selection_color()); + if (b) {fl_color(color()); b->draw(x(), y(), w(), h());} + draw_label(); +} diff --git a/src/forms_compatability.cxx b/src/forms_compatability.cxx new file mode 100755 index 000000000..763269af6 --- /dev/null +++ b/src/forms_compatability.cxx @@ -0,0 +1,153 @@ +// forms.C + +// Forms library compatability functions. +// Many more functions are defined as inlines in forms.h! + +#include <FL/forms.H> +#include <stdlib.h> + +char fl_flip = 2; +void fl_end_form() { + while (Fl_Group::current()) Fl_Group::current()->forms_end(); +} +void Fl_Group::forms_end() { + // set the dimensions of a group to surround contents + if (children() && !w()) { + Fl_Widget*const* a = array(); + Fl_Widget* o = *a++; + int rx = o->x(); + int ry = o->y(); + int rw = rx+o->w(); + int rh = ry+o->h(); + for (int i=children_-1; i--;) { + o = *a++; + if (o->x() < rx) rx = o->x(); + if (o->y() < ry) ry = o->y(); + if (o->x()+o->w() > rw) rw = o->x()+o->w(); + if (o->y()+o->h() > rh) rh = o->y()+o->h(); + } + x(rx); + y(ry); + w(rw-rx); + h(rh-ry); + } + // flip all the children's coordinate systems: + if (fl_flip) { + Fl_Widget* o = (type()>=FL_WINDOW) ? this : window(); + int Y = o->h(); + Fl_Widget*const* a = array(); + for (int i=children(); i--;) { + Fl_Widget* o = *a++; + int newy = Y-o->y()-o->h(); + o->y(newy); + } + } + end(); +} + +static int initargc; +static char **initargv; + +void fl_initialize(int *argc, char **argv, const char *, FL_CMD_OPT *, int) { + initargc = *argc; + initargv = new char*[*argc+1]; + int i,j; + for (i=0; i<=*argc; i++) initargv[i] = argv[i]; + for (i=j=1; i<*argc; ) { + if (Fl::arg(*argc,argv,i)); + else argv[j++] = argv[i++]; + } + argv[j] = 0; + *argc = j; + if (fl_flip==2) fl_flip = 0; +} + +char fl_modal_next; // set by fl_freeze_forms() + +void fl_show_form(Fl_Window *f,int place,int b,const char *n) { + + Fl::enable_symbols(); + + f->label(n); + if (!b) f->clear_border(); + if (fl_modal_next || b==FL_TRANSIENT) {f->set_modal(); fl_modal_next = 0;} + + if (place & FL_PLACE_MOUSE) f->hotspot(f); + + if (place & FL_PLACE_CENTER) + f->position((Fl::w()-f->w())/2, (Fl::h()-f->h())/2); + + if (place & FL_PLACE_FULLSCREEN) + f->fullscreen(); + + if (place & (FL_PLACE_POSITION | FL_PLACE_GEOMETRY)) + f->position( + (f->x() < 0) ? Fl::w()-f->w()+f->x()-1 : f->x(), + (f->y() < 0) ? Fl::h()-f->h()+f->y()-1 : f->y()); + +// if (place & FL_PLACE_ASPECT) { +// this is not yet implemented +// it can be done by setting size_range(). + + if (place == FL_PLACE_FREE || place == FL_PLACE_SIZE) + f->free_position(); + + if (place == FL_PLACE_FREE || place & FL_FREE_SIZE) + if (!f->resizable()) f->resizable(f); + + if (initargc) {f->show(initargc,initargv); initargc = 0;} + else f->show(); +} + +Fl_Widget *fl_do_forms(void) { + Fl_Widget *obj; + while (!(obj = Fl::readqueue())) if (!Fl::wait()) exit(0); + return obj; +} + +Fl_Widget *fl_check_forms() { + Fl::check(); + return Fl::readqueue(); +} + +void fl_set_graphics_mode(int /*r*/,int /*d*/) {} + +void Fl_FormsText::draw() { + draw_box(); + align(align()|FL_ALIGN_INSIDE); // questionable method of compatability + draw_label(); +} + +// Create a forms button by selecting correct fltk subclass: + +#include <FL/Fl_Return_Button.H> +#include <FL/Fl_Repeat_Button.H> + +Fl_Button *fl_add_button(uchar t,int x,int y,int w,int h,const char *l) { + Fl_Button *b; + switch (t) { + case FL_RETURN_BUTTON: + case FL_HIDDEN_RET_BUTTON: + b = new Fl_Return_Button(x,y,w,h,l); + break; + case FL_TOUCH_BUTTON: + b = new Fl_Repeat_Button(x,y,w,h,l); + break; + default: + b = new Fl_Button(x,y,w,h,l); + } + switch (t) { + case FL_TOGGLE_BUTTON: + case FL_RADIO_BUTTON: + b->type(t); + break; + case FL_HIDDEN_BUTTON: + case FL_HIDDEN_RET_BUTTON: + b->type(FL_HIDDEN_BUTTON); + break; + case FL_INOUT_BUTTON: + b->when(FL_WHEN_CHANGED); + break; + } + return b; +} diff --git a/src/forms_free.cxx b/src/forms_free.cxx new file mode 100644 index 000000000..826211620 --- /dev/null +++ b/src/forms_free.cxx @@ -0,0 +1,50 @@ +// forms_free.C + +// Emulation of the Forms "free" widget. +// This emulation allows the free demo to run, and has allowed +// me to port several other programs, but it is in no way +// complete. + +#include <FL/Fl.H> +#include <FL/Fl_Free.H> + +void Fl_Free::step(void *v) { + Fl_Free *f = (Fl_Free *)v; + f->handle(FL_STEP); + Fl::add_timeout(.01,step,v); +} + +Fl_Free::Fl_Free(uchar t,int x,int y,int w,int h,const char *l, + FL_HANDLEPTR hdl) : +Fl_Widget(x,y,w,h,l) { + type(t); + hfunc = hdl; + if (t == FL_SLEEPING_FREE) deactivate(); + if (t == FL_CONTINUOUS_FREE || t == FL_ALL_FREE) + Fl::add_timeout(.01,step,this); +} + +Fl_Free::~Fl_Free() { + Fl::remove_timeout(step,this); + hfunc(this,FL_FREEMEM,0,0,0); +} + +void Fl_Free::draw() {hfunc(this,FL_DRAW,0,0,0);} + +int Fl_Free::handle(int e) { + char key = Fl::event_key(); + switch (e) { + case FL_FOCUS: + if (type()!=FL_INPUT_FREE && type()!=FL_ALL_FREE) return 0; + break; + case FL_PUSH: + case FL_DRAG: + case FL_RELEASE: + key = 4-Fl::event_button(); + break; + case FL_SHORTCUT: + return 0; + } + if (hfunc(this, e, float(Fl::event_x()), float(Fl::event_y()), key)) do_callback(); + return 1; +} diff --git a/src/forms_fselect.cxx b/src/forms_fselect.cxx new file mode 100644 index 000000000..077f69f32 --- /dev/null +++ b/src/forms_fselect.cxx @@ -0,0 +1,38 @@ +// fselect.C + +// Emulate the Forms file chooser using the fltk file chooser. + +#include <FL/forms.H> +#include <string.h> + +static char fl_directory[1024]; +static const char *fl_pattern; // assummed passed value is static +static char fl_filename[256]; + +char* fl_show_file_selector(const char *message,const char *dir, + const char *pat,const char *fname) { + if (dir && dir[0]) strncpy(fl_directory,dir,1023); + if (pat && pat[0]) fl_pattern = pat; + if (fname && fname[0]) strncpy(fl_filename,fname,255); + char *p = fl_directory+strlen(fl_directory); + if (p > fl_directory && *(p-1)!='/' +#ifdef WIN32 + && *(p-1)!='\\' && *(p-1)!=':' +#endif + ) *p++ = '/'; + strcpy(p,fl_filename); + const char *q = fl_file_chooser(message,fl_pattern,fl_directory); + if (!q) return 0; + strcpy(fl_directory, q); + p = (char *)filename_name(fl_directory); + strcpy(fl_filename, p); + if (p > fl_directory+1) p--; + *p = 0; + return (char *)q; +} + +char* fl_get_directory() {return fl_directory;} + +char* fl_get_pattern() {return (char *)fl_pattern;} + +char* fl_get_filename() {return fl_filename;} diff --git a/src/forms_pixmap.cxx b/src/forms_pixmap.cxx new file mode 100644 index 000000000..5263531ba --- /dev/null +++ b/src/forms_pixmap.cxx @@ -0,0 +1,24 @@ +// forms_pixmap.C +// Forms compatability widget to draw a pixmap + +#include <FL/forms.H> + +Fl_FormsPixmap::Fl_FormsPixmap( + Fl_Boxtype t, int x, int y, int w, int h, const char* l) +: Fl_Widget(x, y, w, h, l) { + box(t); + b = 0; + color(FL_BLACK); + align(FL_ALIGN_BOTTOM); +} + +void Fl_FormsPixmap::set(char*const* bits) { + delete b; + b = new Fl_Pixmap(bits); +} + +void Fl_FormsPixmap::draw() { + draw_box(box(), selection_color()); + if (b) {fl_color(color()); b->draw(x(), y(), w(), h());} + draw_label(); +} diff --git a/src/forms_timer.cxx b/src/forms_timer.cxx new file mode 100644 index 000000000..2b7694f80 --- /dev/null +++ b/src/forms_timer.cxx @@ -0,0 +1,127 @@ +// forms_timer.H + +// Emulate the Forms Timer object +// You don't want to use this if you just want a timeout, call +// Fl::add_timeout directly! + +#include <FL/Fl.H> +#include <FL/Fl_Timer.H> +#include <FL/fl_draw.H> +#ifdef WIN32 +# include <sys/types.h> +# include <sys/timeb.h> +#else +# include <sys/time.h> +#endif +#include <stdio.h> + +#define FL_TIMER_BLINKRATE 0.2 + +void fl_gettime(long* sec, long* usec) { +#ifdef WIN32 + struct timeb tp; + ftime(&tp); + *sec = tp.time; + *usec = tp.millitm * 1000; +#else + struct timeval tp; + struct timezone tzp; + gettimeofday(&tp, &tzp); + *sec = tp.tv_sec; + *usec = tp.tv_usec; +#endif +} + +void Fl_Timer::draw() { + int tt; + Fl_Color col; + char str[32]; + if (!on || delay>0.0) + col = color(); + else if ((int) (delay / FL_TIMER_BLINKRATE) % 2) + col = color(); + else + col = selection_color(); + draw_box(box(), col); + if (type() == FL_VALUE_TIMER && delay>0.0) { + double d = direction_ ? total-delay : delay; + if (d < 60.0) + sprintf(str, "%.1f", d); + else { + tt = (int) ((d+0.05) / 60.0); + sprintf(str, "%d:%04.1f", tt, d - 60.0 * tt); + } + fl_font(labelfont(), labelsize()); + fl_color(labelcolor()); + fl_draw(str, x(), y(), w(), h(), FL_ALIGN_CENTER); + } else + draw_label(); +} + +void Fl_Timer::stepcb(void* v) { + ((Fl_Timer*)v)->step(); +} + +void Fl_Timer::step() { + if (!on) return; + double lastdelay = delay; + long sec, usec; fl_gettime(&sec, &usec); + delay -= (double) (sec - lastsec) + (double) (usec - lastusec) / 1000000.0; + lastsec = sec; lastusec = usec; + if (lastdelay > 0.0 && delay <= 0.0) { + if (type() == FL_HIDDEN_TIMER) { + on = 0; + delay = 0; + } else { + redraw(); + Fl::add_timeout(FL_TIMER_BLINKRATE, stepcb, this); + } + do_callback(); + } else { + if (type() == FL_VALUE_TIMER) redraw(); + Fl::add_timeout(FL_TIMER_BLINKRATE, stepcb, this); + } +} + +int Fl_Timer::handle(int event) { + if (event == FL_RELEASE && delay <= 0) value(0.0); + return 0; +} + +Fl_Timer::~Fl_Timer() { + Fl::remove_timeout(stepcb, this); +} + +Fl_Timer::Fl_Timer(uchar t, int x, int y, int w, int h, const char* l) +: Fl_Widget(x, y, w, h, l) { + box(FL_DOWN_BOX); + selection_color(FL_RED); + delay = 0; + on = 0; + direction_ = 0; + type(t); + if (t == FL_HIDDEN_TIMER) clear_visible(); + if (t == FL_VALUE_TIMER) align(FL_ALIGN_LEFT); +} + +void Fl_Timer::value(double d) { + delay = total = d; + on = (d > 0.0); + fl_gettime(&(lastsec), &(lastusec)); + if (type() != FL_HIDDEN_TIMER) redraw(); + Fl::remove_timeout(stepcb, this); + if (on) Fl::add_timeout(FL_TIMER_BLINKRATE, stepcb, this); +} + +void Fl_Timer::suspended(char d) { + if (!d) { + if (on) return; + on = (delay > 0.0); + fl_gettime(&(lastsec), &(lastusec)); + if (on) Fl::add_timeout(FL_TIMER_BLINKRATE, stepcb, this); + } else { + if (!on) return; + on = 0; + Fl::remove_timeout(stepcb, this); + } +} diff --git a/src/gl_draw.cxx b/src/gl_draw.cxx new file mode 100644 index 000000000..574b50ced --- /dev/null +++ b/src/gl_draw.cxx @@ -0,0 +1,121 @@ +// gl_draw.C + +// Functions from <FL/gl.h> +// See also Fl_Gl_Window and gl_start.C + +#include <config.h> +#if HAVE_GL + +#include <FL/Fl.H> +#include <FL/gl.h> +#include <FL/x.H> +#include "Fl_Gl_Choice.H" +#include "Fl_Font.H" +#include <string.h> + +// stuff from fl_draw.H: +void fl_font(int fontid, int size); +int fl_height(); // using "size" should work ok +int fl_descent(); +void fl_measure(const char*, int& x, int& y); +double fl_width(const char*); +double fl_width(const char*, int n); +double fl_width(uchar); +unsigned long fl_xpixel(uchar i); + +void gl_font(int fontid, int size) {fl_font(fontid, size);} +int gl_height() {return fl_height();} +int gl_descent() {return fl_descent();} +double gl_width(const char* s) {return fl_width(s);} +double gl_width(const char* s, int n) {return fl_width(s,n);} +double gl_width(uchar c) {return fl_width(c);} + +void gl_draw(const char* str, int n) { +#ifdef WIN32 + if (!fl_current_xfont->listbase) { + int base = fl_current_xfont->metr.tmFirstChar; + int size = fl_current_xfont->metr.tmLastChar-base+1; + HFONT oldFid = (HFONT)SelectObject(fl_gc, fl_current_xfont->fid); + fl_current_xfont->listbase = glGenLists(size)-base; + wglUseFontBitmaps(fl_gc, base, size, fl_current_xfont->listbase+base); + SelectObject(fl_gc, oldFid); + } +#else + if (!fl_current_xfont->listbase) { + int base = fl_current_xfont->font->min_char_or_byte2; + int size = fl_current_xfont->font->max_char_or_byte2-base+1; +// int base = 0; int size = 256; + fl_current_xfont->listbase = glGenLists(size)-base; + glXUseXFont(fl_current_xfont->font->fid, base, size, + fl_current_xfont->listbase+base); + } +#endif + glListBase(fl_current_xfont->listbase); + glCallLists(n, GL_UNSIGNED_BYTE, str); +} + +void gl_draw(const char* str, int n, int x, int y) { + glRasterPos2i(x, y); + gl_draw(str, n); +} + +void gl_draw(const char* str) { + gl_draw(str, strlen(str)); +} + +void gl_draw(const char* str, int x, int y) { + gl_draw(str, strlen(str), x, y); +} + +static void gl_draw_invert(const char* str, int n, int x, int y) { + glRasterPos2i(x, -y); + gl_draw(str, n); +} + +void gl_draw( + const char* str, // the (multi-line) string + int x, int y, int w, int h, // bounding box + Fl_Align align) { + fl_draw(str, x, -y-h, w, h, align, gl_draw_invert); +} + +void gl_measure(const char* str, int& x, int& y) {fl_measure(str,x,y);} + +void gl_rect(int x, int y, int w, int h) { + if (w < 0) {w = -w; x = x-w;} + if (h < 0) {h = -h; y = y-h;} + glBegin(GL_LINE_STRIP); + glVertex2i(x+w-1, y+h-1); + glVertex2i(x+w-1, y); + glVertex2i(x, y); + glVertex2i(x, y+h-1); + glVertex2i(x+w, y+h-1); + glEnd(); +} + +#if HAVE_GL_OVERLAY +extern uchar fl_overlay; +#endif + +void gl_color(Fl_Color i) { +#if HAVE_GL_OVERLAY +#ifdef WIN32 + if (fl_overlay) {glIndexi(i ? i : FL_GRAY_RAMP); return;} +#else + if (fl_overlay) {glIndexi(int(fl_xpixel(i))); return;} +#endif +#endif + uchar red, green, blue; + Fl::get_color(i, red, green, blue); + glColor3ub(red, green, blue); +} + +void gl_draw_image(const uchar* b, int x, int y, int w, int h, int d, int ld) { + if (!ld) ld = w*d; + glPixelStorei(GL_UNPACK_ROW_LENGTH, ld/d); + glRasterPos2i(x,y); + glDrawPixels(w,h,d<4?GL_RGB:GL_RGBA,GL_UNSIGNED_BYTE,(const ulong*)b); +} + +#endif + diff --git a/src/gl_start.cxx b/src/gl_start.cxx new file mode 100644 index 000000000..255a78127 --- /dev/null +++ b/src/gl_start.cxx @@ -0,0 +1,98 @@ +// Code to switch current fltk drawing context in/out of GL "mode": + +// You MUST use gl_visual() to select the default visual before doing +// show() of any windows. Mesa will crash if you try to use a visual +// not returned by glxChooseVisual. + +// This does not work with Fl_Double_Window's! It will try to draw +// into the front buffer. Depending on the system this will either +// crash or do nothing (when pixmaps are being used as back buffer +// and GL is being done by hardware), work correctly (when GL is done +// with software, such as Mesa), or draw into the front buffer and +// be erased when the buffers are swapped (when double buffer hardware +// is being used) + +#include <config.h> +#if HAVE_GL + +#include <FL/Fl.H> +#include <FL/Fl_Window.H> +#include <FL/x.H> +#include <FL/fl_draw.H> + +#include "Fl_Gl_Choice.H" + +extern GLXContext fl_first_context; // in Fl_Gl_Choice.C +extern int fl_clip_state_number; // in fl_rect.C + +static GLXContext context; +static int clip_state_number; +static int pw, ph; + +#ifdef WIN32 +static int default_mode; +#endif + +Region XRectangleRegion(int x, int y, int w, int h); // in fl_rect.C + +void gl_start() { +#ifdef WIN32 + HDC hdc = fl_private_dc(Fl_Window::current(), default_mode,0); + if (!context) { + context = wglCreateContext(hdc); + if (!fl_first_context) fl_first_context = context; + else wglShareLists(fl_first_context, context); + } + wglMakeCurrent(hdc, context); +#else + if (!context) { + context = glXCreateContext(fl_display, fl_visual, fl_first_context, 1); + if (!context) Fl::fatal("OpenGL does not support this visual"); + if (!fl_first_context) fl_first_context = context; + } + glXMakeCurrent(fl_display, fl_window, context); + glXWaitX(); +#endif + if (pw != Fl_Window::current()->w() || ph != Fl_Window::current()->h()) { + pw = Fl_Window::current()->w(); + ph = Fl_Window::current()->h(); + glLoadIdentity(); + glViewport(0, 0, pw, ph); + glOrtho(0, pw, 0, ph, -1, 1); + glDrawBuffer(GL_FRONT); + } + if (clip_state_number != fl_clip_state_number) { + clip_state_number = fl_clip_state_number; + int x, y, w, h; + if (fl_clip_box(0, 0, Fl_Window::current()->w(), Fl_Window::current()->h(), + x, y, w, h)) { + fl_clip_region(XRectangleRegion(x,y,w,h)); + glScissor(x, Fl_Window::current()->h()-(y+h), w, h); + glEnable(GL_SCISSOR_TEST); + } else { + glDisable(GL_SCISSOR_TEST); + } + } +} + +void gl_finish() { +#ifdef WIN32 + glFlush(); +#else + glXWaitGL(); +#endif +} + +int Fl::gl_visual(int mode, int *alist) { +#ifdef WIN32 + default_mode = mode; +#else + Fl_Gl_Choice *c = Fl_Gl_Choice::find(mode,alist); + if (!c) return 0; + fl_visual = c->vis; + fl_colormap = c->colormap; +#endif + return 1; +} + +#endif diff --git a/src/glut_compatability.cxx b/src/glut_compatability.cxx new file mode 100755 index 000000000..e5d93bdc5 --- /dev/null +++ b/src/glut_compatability.cxx @@ -0,0 +1,377 @@ +// glut.C + +// Emulation of Glut using fltk. + +// GLUT is Copyright (c) Mark J. Kilgard, 1994, 1995, 1996. +// "This program is freely distributable without licensing fees and is +// provided without guarantee or warrantee expressed or implied. This +// program is -not- in the public domain." + +// Although I have copied the GLUT API, none of my code is based on +// any Glut implementation details and is therefore covered by the LGPL. + +#include <config.h> +#if HAVE_GL + +#include <FL/glut.H> + +#define MAXWINDOWS 32 +static Fl_Glut_Window *windows[MAXWINDOWS+1]; + +Fl_Glut_Window *glut_window; +int glut_menu; +void (*glut_menustate_function)(int); +void (*glut_menustatus_function)(int,int,int); + +static void default_reshape(int w, int h) {glViewport(0,0,w,h);} + +void Fl_Glut_Window::make_current() { + glut_window = this; + if (shown()) Fl_Gl_Window::make_current(); +} + +static int indraw; +void Fl_Glut_Window::draw() { + glut_window = this; + indraw = 1; + if (!valid()) {reshape(w(),h()); valid(1);} + display(); + indraw = 0; +} + +void glutSwapBuffers() { + if (!indraw) glut_window->swap_buffers(); +} + +void Fl_Glut_Window::draw_overlay() { + glut_window = this; + if (!valid()) {reshape(w(),h()); valid(1);} + overlaydisplay(); +} + +static void domenu(int, int, int); + +int Fl_Glut_Window::handle(int event) { + make_current(); + int ex = Fl::event_x(); + int ey = Fl::event_y(); + int button; + switch (event) { + + case FL_PUSH: + button = Fl::event_button()-1; + if (button<0) button = 0; + if (button>2) button = 2; + if (menu[button]) {domenu(menu[button],ex,ey); return 1;} + mouse_down |= 1<<button; + if (mouse) {mouse(button,GLUT_DOWN,ex,ey); return 1;} + if (motion) return 1; + break; + + case FL_RELEASE: + for (button = 0; button < 3; button++) if (mouse_down & 1<<button) { + if (mouse) mouse(button,GLUT_UP,ex,ey); + } + mouse_down = 0; + return 1; + + case FL_ENTER: + if (entry) {entry(GLUT_ENTERED); return 1;} + if (passivemotion) return 1; + break; + + case FL_LEAVE: + if (entry) {entry(GLUT_LEFT); return 1;} + if (passivemotion) return 1; + break; + + case FL_DRAG: + if (motion) {motion(ex, ey); return 1;} + break; + + case FL_MOVE: + if (passivemotion) {passivemotion(ex, ey); return 1;} + break; + + case FL_FOCUS: + if (keyboard || special) return 1; + break; + + case FL_KEYBOARD: + if (Fl::event_text()[0]) { + if (keyboard) {keyboard(Fl::event_text()[0],ex,ey); return 1;} + break; + } else { + if (special) { + int i = Fl::event_key(); + if (i >= FL_F && i < FL_F_Last) i = i-FL_F+1; + special(i,ex,ey); + return 1; + } + break; + } + + case FL_HIDE: + if (visibility) visibility(GLUT_NOT_VISIBLE); + break; + + case FL_SHOW: + if (visibility) visibility(GLUT_VISIBLE); + break; + } + + return Fl_Gl_Window::handle(event); +} + +static int glut_mode = GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH; + +void Fl_Glut_Window::_init() { + for (number=1; number<MAXWINDOWS; number++) if (!windows[number]) break; + windows[number] = this; + menu[0] = menu[1] = menu[2] = 0; + reshape = default_reshape; + keyboard = 0; + mouse = 0; + motion = 0; + passivemotion = 0; + entry = 0; + visibility = 0; + special = 0; + mouse_down = 0; + mode(glut_mode); +} + +Fl_Glut_Window::Fl_Glut_Window(int w, int h, const char *t) : + Fl_Gl_Window(w,h,t) {_init();} + +Fl_Glut_Window::Fl_Glut_Window(int x,int y,int w,int h, const char *t) : + Fl_Gl_Window(x,y,w,h,t) {_init();} + +static int initargc; +static char **initargv; + +void glutInit(int *argc, char **argv) { + initargc = *argc; + initargv = new char*[*argc+1]; + int i,j; + for (i=0; i<=*argc; i++) initargv[i] = argv[i]; + for (i=j=1; i<*argc; ) { + if (Fl::arg(*argc,argv,i)); + else argv[j++] = argv[i++]; + } + argv[j] = 0; + *argc = j; +} + +void glutInitDisplayMode(unsigned int mode) { + glut_mode = mode; +} + +void glutMainLoop() {Fl::run();} + +//////////////////////////////////////////////////////////////// + +static int initx, inity, initw=300, inith=300, initpos; + +void glutInitWindowPosition(int x, int y) { + initx = x; inity = y; initpos = 1; +} + +void glutInitWindowSize(int w, int h) { + initw = w; inith = h; +} + +int glutCreateWindow(char *title) { + Fl_Glut_Window *W; + if (initpos) { + W = new Fl_Glut_Window(initx,inity,initw,inith,title); + initpos = 0; + } else { + W = new Fl_Glut_Window(initw,inith,title); + } + W->resizable(W); + if (initargc) { + W->show(initargc,initargv); + initargc = 0; + } else { + W->show(); + } + W->make_current(); + return W->number; +} + +int glutCreateSubWindow(int win, int x, int y, int w, int h) { + Fl_Glut_Window *W = new Fl_Glut_Window(x,y,w,h,0); + windows[win]->add(W); + if (windows[win]->shown()) W->show(); + W->make_current(); + return W->number; +} + +Fl_Glut_Window::~Fl_Glut_Window() { + if (glut_window == this) glut_window = 0; + windows[number] = 0; +} + +void glutDestroyWindow(int win) { + // should destroy children!!! + delete windows[win]; +} + +void glutSetWindow(int win) { + windows[win]->make_current(); +} + +//////////////////////////////////////////////////////////////// +#include <FL/Fl_Menu_Item.H> + +struct menu { + void (*cb)(int); + Fl_Menu_Item *m; + int size; + int alloc; +}; + +#define MAXMENUS 32 +static menu menus[MAXMENUS+1]; + +static void domenu(int n, int ex, int ey) { + glut_menu = n; + menu *m = &menus[n]; + if (glut_menustate_function) glut_menustate_function(1); + if (glut_menustatus_function) glut_menustatus_function(1,ex,ey); + const Fl_Menu_Item* g = m->m->popup(Fl::event_x(), Fl::event_y(), 0); + if (g && g->callback_) ((void (*)(int))(g->callback_))(int(g->argument())); + if (glut_menustatus_function) glut_menustatus_function(0,ex,ey); + if (glut_menustate_function) glut_menustate_function(0); +} + +int glutCreateMenu(void (*cb)(int)) { + int i; + for (i=1; i<MAXMENUS; i++) if (!menus[i].cb) break; + menu *m = &menus[i]; + m->cb = cb; + return glut_menu = i; +} + +void glutDestroyMenu(int n) { + menu *m = &menus[n]; + delete[] m->m; + m->m = 0; + m->cb = 0; + m->size = m->alloc = 0; +} + +static Fl_Menu_Item* additem(menu *m) { + if (m->size+1 >= m->alloc) { + m->alloc = m->size*2+10; + Fl_Menu_Item* nm = new Fl_Menu_Item[m->alloc]; + for (int i=0; i<m->size; i++) nm[i] = m->m[i]; + delete[] m->m; + m->m = nm; + } + int n = m->size++; + m->m[n+1].text = 0; + Fl_Menu_Item* i = &(m->m[n]); + i->shortcut_ = 0; + i->flags = 0; + i->labeltype_ = i->labelfont_ = i->labelsize_ = i->labelcolor_ = 0; + return i; +} + +void glutAddMenuEntry(char *label, int value) { + menu *m = &menus[glut_menu]; + Fl_Menu_Item* i = additem(m); + i->text = label; + i->callback_ = (Fl_Callback*)(m->cb); + i->user_data_ = (void *)value; +} + +void glutAddSubMenu(char *label, int submenu) { + menu *m = &menus[glut_menu]; + Fl_Menu_Item* i = additem(m); + i->text = label; + i->callback_ = 0; + i->user_data_ = (void *)(menus[submenu].m); + i->flags = FL_PUP_SUBMENU; +} + +void glutChangeToMenuEntry(int item, char *label, int value) { + menu *m = &menus[glut_menu]; + Fl_Menu_Item* i = &m->m[item-1]; + i->text = label; + i->callback_ = (Fl_Callback*)(m->cb); + i->user_data_ = (void *)value; + i->flags = 0; +} + +void glutChangeToSubMenu(int item, char *label, int submenu) { + menu *m = &menus[glut_menu]; + Fl_Menu_Item* i = &m->m[item-1]; + i->text = label; + i->callback_ = 0; + i->user_data_ = (void *)(menus[submenu].m); + i->flags = FL_PUP_SUBMENU; +} + +void glutRemoveMenuItem(int item) { + menu *m = &menus[glut_menu]; + if (item > m->size || item < 1) return; + for (int i = item-1; i <= m->size; i++) m->m[i] = m->m[i+1]; + m->size--; +} + +//////////////////////////////////////////////////////////////// + +int glutGet(GLenum type) { + switch (type) { + case GLUT_RETURN_ZERO: return 0; + case GLUT_WINDOW_X: return glut_window->x(); + case GLUT_WINDOW_Y: return glut_window->y(); + case GLUT_WINDOW_WIDTH: return glut_window->w(); + case GLUT_WINDOW_HEIGHT: return glut_window->h(); + case GLUT_WINDOW_PARENT: + if (glut_window->parent()) + return ((Fl_Glut_Window *)(glut_window->parent()))->number; + else + return 0; +//case GLUT_WINDOW_NUM_CHILDREN: +//case GLUT_WINDOW_CURSOR: return + case GLUT_SCREEN_WIDTH: return Fl::w(); + case GLUT_SCREEN_HEIGHT: return Fl::h(); +//case GLUT_SCREEN_WIDTH_MM: +//case GLUT_SCREEN_HEIGHT_MM: + case GLUT_MENU_NUM_ITEMS: return menus[glut_menu].size; + case GLUT_DISPLAY_MODE_POSSIBLE: return Fl_Gl_Window::can_do(glut_mode); + case GLUT_INIT_WINDOW_X: return initx; + case GLUT_INIT_WINDOW_Y: return inity; + case GLUT_INIT_WINDOW_WIDTH: return initw; + case GLUT_INIT_WINDOW_HEIGHT: return inith; + case GLUT_INIT_DISPLAY_MODE: return glut_mode; +//case GLUT_ELAPSED_TIME: + case GLUT_WINDOW_BUFFER_SIZE: + if (glutGet(GLUT_WINDOW_RGBA)) + return glutGet(GLUT_WINDOW_RED_SIZE)+ + glutGet(GLUT_WINDOW_GREEN_SIZE)+ + glutGet(GLUT_WINDOW_BLUE_SIZE)+ + glutGet(GLUT_WINDOW_ALPHA_SIZE); + else + return glutGet(GLUT_WINDOW_COLORMAP_SIZE); + default: {GLint p; glGetIntegerv(type, &p); return p;} + } +} + +int glutLayerGet(GLenum type) { + switch (type) { + case GLUT_OVERLAY_POSSIBLE: return glut_window->can_do_overlay(); +//case GLUT_LAYER_IN_USE: +//case GLUT_HAS_OVERLAY: + case GLUT_TRANSPARENT_INDEX: return 0; // true for SGI + case GLUT_NORMAL_DAMAGED: return glut_window->damage(); + case GLUT_OVERLAY_DAMAGED: return 1; // kind of works... + default: return 0; + } +} + +#endif +// End of glut.C diff --git a/src/glut_font.cxx b/src/glut_font.cxx new file mode 100644 index 000000000..751fd6f8c --- /dev/null +++ b/src/glut_font.cxx @@ -0,0 +1,32 @@ +// glut_font.C + +// (sort of) emulation of Glut's bitmap drawing functions, using FL's +// font stuff. Not all the fonts match! + +#include <config.h> +#if HAVE_GL + +#include <FL/glut.H> +#include <FL/gl.h> + +Glut_Bitmap_Font glutBitmap9By15 = {FL_SCREEN, 15}; +Glut_Bitmap_Font glutBitmap8By13 = {FL_SCREEN, 13}; +Glut_Bitmap_Font glutBitmapTimesRoman10 = {FL_TIMES, 10}; +Glut_Bitmap_Font glutBitmapTimesRoman24 = {FL_TIMES, 24}; +Glut_Bitmap_Font glutBitmapHelvetica10 = {FL_HELVETICA, 10}; +Glut_Bitmap_Font glutBitmapHelvetica12 = {FL_HELVETICA, 12}; +Glut_Bitmap_Font glutBitmapHelvetica18 = {FL_HELVETICA, 18}; + +void glutBitmapCharacter(void *font, int character) { + gl_font(((Glut_Bitmap_Font *)font)->font,((Glut_Bitmap_Font *)font)->size); + char a[1]; a[0] = character; + gl_draw(a,1); +} + +int glutBitmapWidth(int font, int character) { + gl_font(((Glut_Bitmap_Font *)font)->font,((Glut_Bitmap_Font *)font)->size); + return int(gl_width(character)+.5); +} + +#endif + diff --git a/src/mediumarrow.h b/src/mediumarrow.h new file mode 100644 index 000000000..8a1fe8cbc --- /dev/null +++ b/src/mediumarrow.h @@ -0,0 +1,6 @@ +#define mediumarrow_width 16 +#define mediumarrow_height 16 +static unsigned char mediumarrow_bits[] = { + 0x40, 0x00, 0x60, 0x00, 0x70, 0x00, 0x78, 0x00, 0xfc, 0x3f, 0x78, 0x00, + 0x70, 0x00, 0x60, 0x02, 0x40, 0x06, 0x00, 0x0e, 0x00, 0x1e, 0xfc, 0x3f, + 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x02}; diff --git a/src/ns.xbm b/src/ns.xbm new file mode 100644 index 000000000..f1ea18e1b --- /dev/null +++ b/src/ns.xbm @@ -0,0 +1,8 @@ +#define ns_width 16 +#define ns_height 16 +#define ns_x_hot 8 +#define ns_y_hot 8 +static unsigned char ns_bits[] = { + 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0x80, 0x01, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00}; diff --git a/src/ns_mask.xbm b/src/ns_mask.xbm new file mode 100644 index 000000000..a69f261bb --- /dev/null +++ b/src/ns_mask.xbm @@ -0,0 +1,8 @@ +#define ns_mask_width 16 +#define ns_mask_height 16 +#define ns_mask_x_hot 8 +#define ns_mask_y_hot 8 +static unsigned char ns_mask_bits[] = { + 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, 0xf0, 0x0f, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, + 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01}; diff --git a/src/numericsort.c b/src/numericsort.c new file mode 100644 index 000000000..17149be6f --- /dev/null +++ b/src/numericsort.c @@ -0,0 +1,53 @@ +/* My own scandir sorting function, useful for the film industry where + we have many files with numbers in their names: */ + +#include <config.h> +#include <ctype.h> +#include <stdlib.h> +#include <sys/types.h> + +#ifdef WIN32 +#include <FL/filename.H> +#else +#if HAVE_DIRENT_H +# include <dirent.h> +#else +# define dirent direct +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif +#endif + +int numericsort(const struct dirent **A, const struct dirent **B) { + const char* a = (*A)->d_name; + const char* b = (*B)->d_name; + int ret = 0; + for (;;) { + if (isdigit(*a) && isdigit(*b)) { + int zdiff,diff,magdiff; + zdiff = 0; + while (*a == '0') {a++; zdiff++;} + while (*b == '0') {b++; zdiff--;} + while (isdigit(*a) && *a == *b) {a++; b++;} + diff = (isdigit(*a) && isdigit(*b)) ? *a - *b : 0; + magdiff = 0; + while (isdigit(*a)) {magdiff++; a++;} + while (isdigit(*b)) {magdiff--; b++;} + if (ret); + else if (magdiff) ret = magdiff; + else if (diff) ret = diff; + else if (zdiff) ret = zdiff; + } else if (*a == *b) { + if (!*a) return ret; + a++; b++; + } else + return (*a-*b); + } +} diff --git a/src/scandir.c b/src/scandir.c new file mode 100644 index 000000000..e3b60bedf --- /dev/null +++ b/src/scandir.c @@ -0,0 +1,128 @@ +/* Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C 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. + +The GNU C 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. */ + +#ifdef WIN32 +#include "scandir_win32.c" +#else + +#include <config.h> + +#if HAVE_SCANDIR +#else + +#include <ctype.h> +#include <stdlib.h> +#include <sys/types.h> +#include <string.h> +#include <errno.h> + +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif + +int +scandir (const char *dir, struct dirent ***namelist, + int (*select)(const struct dirent *), + int (*compar)(const struct dirent **, const struct dirent **)) +{ + DIR *dp = opendir (dir); + struct dirent **v = NULL; + size_t vsize = 0, i; + struct dirent *d; + int save; + + if (dp == NULL) + return -1; + + save = errno; + errno = 0; + + i = 0; + while ((d = readdir (dp)) != NULL) + if (select == NULL || (*select) (d)) + { + size_t dsize; + + if (i == vsize) + { + struct dirent **new; + if (vsize == 0) + vsize = 10; + else + vsize *= 2; + new = (struct dirent **) realloc (v, vsize * sizeof (*v)); + if (new == NULL) + { + lose: + errno = ENOMEM; + break; + } + v = new; + } + +#define _D_EXACT_NAMLEN(d) (strlen ((d)->d_name)) +#define _D_ALLOC_NAMLEN(d) (sizeof (d)->d_name > 1 ? sizeof (d)->d_name : \ + _D_EXACT_NAMLEN (d) + 1) + + dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d; + v[i] = (struct dirent *) malloc (dsize); + if (v[i] == NULL) + goto lose; + + memcpy (v[i++], d, dsize); + } + + if (errno != 0) + { + save = errno; + (void) closedir (dp); + while (i > 0) + free (v[--i]); + free (v); + errno = save; + return -1; + } + + (void) closedir (dp); + errno = save; + + /* Sort the list if we have a comparison function to sort with. */ + if (compar) qsort (v, i, sizeof (*v), compar); + *namelist = v; + return i; +} + +int alphasort (const struct dirent **a, const struct dirent **b) { + return strcmp ((*a)->d_name, (*b)->d_name); +} + +#endif +#endif diff --git a/src/scandir_win32.c b/src/scandir_win32.c new file mode 100644 index 000000000..ea3d18723 --- /dev/null +++ b/src/scandir_win32.c @@ -0,0 +1,79 @@ +// scandir_win32.C + +// Emulation of posix scandir() call + +#include <config.h> +#include <FL/filename.H> +#include <string.h> +#include <windows.h> + +int scandir(const char *dirname, struct dirent ***namelist, + int (*select)(const struct dirent *), + int (*compar)(const struct dirent **, const struct dirent **)) { + + int len = strlen(dirname); + char *findIn = new char[len+5]; strcpy(findIn, dirname); + for (char *d = findIn; *d; d++) if (*d=='/') *d='\\'; + if ((len==0)) { strcpy(findIn, ".\\*"); } + if ((len==1)&& (d[-1]=='.')) { strcpy(findIn, ".\\*"); } + if ((len>0) && (d[-1]=='\\')) { *d++ = '*'; *d = 0; } + if ((len>1) && (d[-1]=='.') && (d[-2]=='\\')) { d[-1] = '*'; } + + WIN32_FIND_DATA find; + HANDLE h; + int nDir = 0, NDir = 0; + struct dirent **dir = 0, *selectDir; + /* + selectDir = (struct dirent*)new char[sizeof(dirent)+1]; + strcpy(selectDir->d_name, "."); + dir[0] = selectDir; + selectDir = (struct dirent*)new char[sizeof(dirent)+2]; + strcpy(selectDir->d_name, ".."); + dir[1] = selectDir; + */ + unsigned long ret; + + if ((h=FindFirstFile(findIn, &find))==INVALID_HANDLE_VALUE) { + ret = GetLastError(); + if (ret != ERROR_NO_MORE_FILES) { + // TODO: return some error code + } + *namelist = dir; + return nDir; + } + do { + selectDir=(struct dirent*)new char[sizeof(dirent)+strlen(find.cFileName)]; + strcpy(selectDir->d_name, find.cFileName); + if (!select || (*select)(selectDir)) { + if (nDir==NDir) { + struct dirent **tempDir = new struct dirent*[NDir+33]; + if (NDir) memcpy(tempDir, dir, sizeof(struct dirent*)*NDir); + if (dir) delete dir; + dir = tempDir; + NDir += 32; + } + dir[nDir] = selectDir; + nDir++; + dir[nDir] = 0; + } else { + delete selectDir; + } + } while (FindNextFile(h, &find)); + ret = GetLastError(); + if (ret != ERROR_NO_MORE_FILES) { + // TODO: return some error code + } + FindClose(h); + + delete findIn; + + if (compar) qsort (dir, nDir, sizeof(*dir), + (int(*)(const void*, const void*))compar); + + *namelist = dir; + return nDir; +} + +int alphasort (const struct dirent **a, const struct dirent **b) { + return strcmp ((*a)->d_name, (*b)->d_name); +} diff --git a/src/slowarrow.h b/src/slowarrow.h new file mode 100644 index 000000000..46a572c97 --- /dev/null +++ b/src/slowarrow.h @@ -0,0 +1,6 @@ +#define slowarrow_width 16 +#define slowarrow_height 16 +static unsigned char slowarrow_bits[] = { + 0x40, 0x00, 0x40, 0x00, 0x60, 0x00, 0x60, 0x00, 0xf0, 0x0f, 0x60, 0x00, + 0x60, 0x00, 0x40, 0x02, 0x40, 0x02, 0x00, 0x06, 0x00, 0x06, 0xf0, 0x0f, + 0x00, 0x06, 0x00, 0x06, 0x00, 0x02, 0x00, 0x02}; |
