summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael R Sweet <michael.r.sweet@gmail.com>1998-10-06 18:21:25 +0000
committerMichael R Sweet <michael.r.sweet@gmail.com>1998-10-06 18:21:25 +0000
commitf9039b2ae21988783feae9b362818e7923e82d14 (patch)
tree6d6fe3679d73448758f9794e7d4d4f6b22a4adad /src
parent67e89232f9ba067825a158734a09e0fa21aacbe3 (diff)
Initial revision
git-svn-id: file:///fltk/svn/fltk/trunk@2 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src')
-rw-r--r--src/Fl.cxx566
-rw-r--r--src/Fl_Adjuster.cxx105
-rw-r--r--src/Fl_Bitmap.cxx109
-rw-r--r--src/Fl_Box.cxx11
-rw-r--r--src/Fl_Browser.cxx421
-rw-r--r--src/Fl_Browser_.cxx606
-rw-r--r--src/Fl_Browser_load.cxx29
-rw-r--r--src/Fl_Button.cxx92
-rw-r--r--src/Fl_Chart.cxx344
-rw-r--r--src/Fl_Check_Button.cxx15
-rw-r--r--src/Fl_Choice.cxx63
-rw-r--r--src/Fl_Clock.cxx136
-rw-r--r--src/Fl_Color_Chooser.cxx397
-rw-r--r--src/Fl_Counter.cxx140
-rw-r--r--src/Fl_Dial.cxx113
-rw-r--r--src/Fl_Double_Window.cxx157
-rw-r--r--src/Fl_Font.H63
-rw-r--r--src/Fl_Gl_Choice.H57
-rw-r--r--src/Fl_Gl_Choice.cxx157
-rw-r--r--src/Fl_Gl_Overlay.cxx175
-rw-r--r--src/Fl_Gl_Window.cxx287
-rw-r--r--src/Fl_Group.cxx443
-rw-r--r--src/Fl_Image.cxx62
-rw-r--r--src/Fl_Input.cxx299
-rw-r--r--src/Fl_Input_.cxx704
-rw-r--r--src/Fl_Light_Button.cxx47
-rw-r--r--src/Fl_Menu.cxx694
-rw-r--r--src/Fl_Menu_.cxx96
-rw-r--r--src/Fl_Menu_Bar.cxx33
-rw-r--r--src/Fl_Menu_Button.cxx57
-rw-r--r--src/Fl_Menu_Window.cxx130
-rw-r--r--src/Fl_Menu_add.cxx151
-rw-r--r--src/Fl_Menu_global.cxx20
-rw-r--r--src/Fl_Multi_Label.cxx51
-rw-r--r--src/Fl_Output.cxx23
-rw-r--r--src/Fl_Overlay_Window.cxx115
-rw-r--r--src/Fl_Pack.cxx78
-rw-r--r--src/Fl_Pixmap.cxx113
-rw-r--r--src/Fl_Positioner.cxx106
-rw-r--r--src/Fl_Repeat_Button.cxx36
-rw-r--r--src/Fl_Return_Button.cxx42
-rw-r--r--src/Fl_Roller.cxx111
-rw-r--r--src/Fl_Round_Button.cxx15
-rw-r--r--src/Fl_Scroll.cxx210
-rw-r--r--src/Fl_Scrollbar.cxx158
-rw-r--r--src/Fl_Single_Window.cxx14
-rw-r--r--src/Fl_Slider.cxx200
-rw-r--r--src/Fl_Tabs.cxx234
-rw-r--r--src/Fl_Tile.cxx173
-rw-r--r--src/Fl_Valuator.cxx100
-rw-r--r--src/Fl_Value_Input.cxx100
-rw-r--r--src/Fl_Value_Output.cxx73
-rw-r--r--src/Fl_Value_Slider.cxx45
-rw-r--r--src/Fl_Widget.cxx164
-rw-r--r--src/Fl_Window.cxx83
-rw-r--r--src/Fl_Window_fullscreen.cxx46
-rw-r--r--src/Fl_Window_hotspot.cxx27
-rw-r--r--src/Fl_Window_iconize.cxx19
-rw-r--r--src/Fl_XColor.H18
-rw-r--r--src/Fl_abort.cxx51
-rw-r--r--src/Fl_add_idle.cxx62
-rw-r--r--src/Fl_arg.cxx377
-rw-r--r--src/Fl_cutpaste.cxx127
-rw-r--r--src/Fl_cutpaste_win32.cxx106
-rw-r--r--src/Fl_display.cxx16
-rw-r--r--src/Fl_get_key.cxx37
-rwxr-xr-xsrc/Fl_get_key_win32.cxx108
-rw-r--r--src/Fl_get_system_colors.cxx66
-rw-r--r--src/Fl_own_colormap.cxx50
-rw-r--r--src/Fl_visual.cxx78
-rw-r--r--src/Fl_win32.cxx696
-rw-r--r--src/Fl_x.cxx807
-rw-r--r--src/Makefile215
-rw-r--r--src/cmap.cxx120
-rw-r--r--src/d1.xbm6
-rw-r--r--src/d1_mask.xbm6
-rw-r--r--src/dump_compose.c26
-rw-r--r--src/ew.xbm8
-rw-r--r--src/ew_mask.xbm8
-rw-r--r--src/fastarrow.h6
-rw-r--r--src/filename_absolute.cxx67
-rw-r--r--src/filename_expand.cxx72
-rw-r--r--src/filename_ext.cxx17
-rw-r--r--src/filename_isdir.cxx12
-rw-r--r--src/filename_list.cxx36
-rw-r--r--src/filename_match.cxx74
-rw-r--r--src/filename_setext.cxx12
-rw-r--r--src/fl_arc.cxx50
-rw-r--r--src/fl_arci.cxx45
-rw-r--r--src/fl_ask.cxx165
-rw-r--r--src/fl_boxtype.cxx256
-rw-r--r--src/fl_cmap.h256
-rw-r--r--src/fl_color.cxx309
-rw-r--r--src/fl_color_win32.cxx204
-rw-r--r--src/fl_cursor.cxx145
-rw-r--r--src/fl_curve.cxx73
-rw-r--r--src/fl_diamond_box.cxx49
-rw-r--r--src/fl_draw.cxx175
-rw-r--r--src/fl_draw_image.cxx605
-rw-r--r--src/fl_draw_image_win32.cxx237
-rw-r--r--src/fl_draw_pixmap.cxx224
-rw-r--r--src/fl_engraved_label.cxx64
-rw-r--r--src/fl_file_chooser.cxx600
-rw-r--r--src/fl_font.cxx283
-rw-r--r--src/fl_font_win32.cxx159
-rw-r--r--src/fl_labeltype.cxx104
-rw-r--r--src/fl_oval_box.cxx38
-rw-r--r--src/fl_overlay.cxx39
-rw-r--r--src/fl_overlay_visual.cxx76
-rw-r--r--src/fl_rect.cxx380
-rw-r--r--src/fl_round_box.cxx94
-rw-r--r--src/fl_rounded_box.cxx75
-rw-r--r--src/fl_scroll_area.cxx66
-rw-r--r--src/fl_set_font.cxx51
-rw-r--r--src/fl_set_fonts.cxx300
-rwxr-xr-xsrc/fl_set_fonts_win32.cxx58
-rw-r--r--src/fl_set_gray.cxx35
-rw-r--r--src/fl_shadow_box.cxx31
-rw-r--r--src/fl_shortcut.cxx100
-rw-r--r--src/fl_show_colormap.cxx124
-rw-r--r--src/fl_symbols.cxx364
-rw-r--r--src/fl_vertex.cxx200
-rw-r--r--src/forms_bitmap.cxx24
-rwxr-xr-xsrc/forms_compatability.cxx153
-rw-r--r--src/forms_free.cxx50
-rw-r--r--src/forms_fselect.cxx38
-rw-r--r--src/forms_pixmap.cxx24
-rw-r--r--src/forms_timer.cxx127
-rw-r--r--src/gl_draw.cxx121
-rw-r--r--src/gl_start.cxx98
-rwxr-xr-xsrc/glut_compatability.cxx377
-rw-r--r--src/glut_font.cxx32
-rw-r--r--src/mediumarrow.h6
-rw-r--r--src/ns.xbm8
-rw-r--r--src/ns_mask.xbm8
-rw-r--r--src/numericsort.c53
-rw-r--r--src/scandir.c128
-rw-r--r--src/scandir_win32.c79
-rw-r--r--src/slowarrow.h6
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&nbsp</code>&nbsp&nbsp&nbsp%c\n",
+ p[0],(p-compose_pairs)/2+0xA0);
+ else
+ printf("<td><code>%c%c</code>&nbsp&nbsp&nbsp%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 = &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 = &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};