Include a stripped-down version of FLTK in tree and add a USE_INCLUDED_FLTK option to build against it.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4603 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/common/fltk/src/Fl_Tabs.cxx b/common/fltk/src/Fl_Tabs.cxx
new file mode 100644
index 0000000..14f309b
--- /dev/null
+++ b/common/fltk/src/Fl_Tabs.cxx
@@ -0,0 +1,531 @@
+//
+// "$Id: Fl_Tabs.cxx 8658 2011-05-12 15:53:59Z manolo $"
+//
+// Tab widget for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2010 by Bill Spitzak and others.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+// USA.
+//
+// Please report all bugs and problems on the following page:
+//
+// http://www.fltk.org/str.php
+//
+
+
+// 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 its label() is printed on the card tab.
+// Clicking the tab makes that card visible.
+
+#include <stdio.h>
+#include <FL/Fl.H>
+#include <FL/Fl_Tabs.H>
+#include <FL/fl_draw.H>
+#include <FL/Fl_Tooltip.H>
+
+#define BORDER 2
+#define EXTRASPACE 10
+#define SELECTION_BORDER 5
+
+// Return the left edges of each tab (plus a fake left edge for a tab
+// past the right-hand one). These positions are actually of the left
+// edge of the slope. They are either separated by the correct distance
+// or by EXTRASPACE or by zero.
+// These positions are updated in the private arrays tab_pos[] and
+// tab_width[], resp.. If needed, these arrays are (re)allocated.
+// Return value is the index of the selected item.
+
+int Fl_Tabs::tab_positions() {
+ int nc = children();
+ if (nc != tab_count) {
+ clear_tab_positions();
+ if (nc) {
+ tab_pos = (int*)malloc((nc+1)*sizeof(int));
+ tab_width = (int*)malloc((nc+1)*sizeof(int));
+ }
+ tab_count = nc;
+ }
+ if (nc == 0) return 0;
+ int selected = 0;
+ Fl_Widget*const* a = array();
+ int i;
+ char prev_draw_shortcut = fl_draw_shortcut;
+ fl_draw_shortcut = 1;
+
+ tab_pos[0] = Fl::box_dx(box());
+ for (i=0; i<nc; i++) {
+ Fl_Widget* o = *a++;
+ if (o->visible()) selected = i;
+
+ int wt = 0; int ht = 0;
+ o->measure_label(wt,ht);
+
+ tab_width[i] = wt + EXTRASPACE;
+ tab_pos[i+1] = tab_pos[i] + tab_width[i] + BORDER;
+ }
+ fl_draw_shortcut = prev_draw_shortcut;
+
+ int r = w();
+ if (tab_pos[i] <= r) return selected;
+ // uh oh, they are too big:
+ // pack them against right edge:
+ tab_pos[i] = r;
+ for (i = nc; i--;) {
+ int l = r-tab_width[i];
+ if (tab_pos[i+1] < l) l = tab_pos[i+1];
+ if (tab_pos[i] <= l) break;
+ tab_pos[i] = l;
+ r -= EXTRASPACE;
+ }
+ // pack them against left edge and truncate width if they still don't fit:
+ for (i = 0; i<nc; i++) {
+ if (tab_pos[i] >= i*EXTRASPACE) break;
+ tab_pos[i] = i*EXTRASPACE;
+ int W = w()-1-EXTRASPACE*(children()-i) - tab_pos[i];
+ if (tab_width[i] > W) tab_width[i] = W;
+ }
+ // adjust edges according to visiblity:
+ for (i = nc; i > selected; i--) {
+ tab_pos[i] = tab_pos[i-1] + tab_width[i-1];
+ }
+ return selected;
+}
+
+// Returns space (height) in pixels needed for tabs. Negative to put them on the bottom.
+// Returns full height, if children() = 0.
+int Fl_Tabs::tab_height() {
+ if (children() == 0) return h();
+ 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) return (H2 <= 0) ? 0 : -H2;
+ else return (H <= 0) ? 0 : H;
+}
+
+// This is used for event handling (clicks) and by fluid to pick tabs.
+// Returns 0, if children() = 0, or if the event is outside of the tabs area.
+Fl_Widget *Fl_Tabs::which(int event_x, int event_y) {
+ if (children() == 0) return 0;
+ 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;
+ Fl_Widget *ret = 0L;
+ int nc = children();
+ tab_positions();
+ for (int i=0; i<nc; i++) {
+ if (event_x < x()+tab_pos[i+1]) {
+ ret = child(i);
+ break;
+ }
+ }
+ return ret;
+}
+
+void Fl_Tabs::redraw_tabs()
+{
+ int H = tab_height();
+ if (H >= 0) {
+ H += Fl::box_dy(box());
+ damage(FL_DAMAGE_SCROLL, x(), y(), w(), H);
+ } else {
+ H = Fl::box_dy(box()) - H;
+ damage(FL_DAMAGE_SCROLL, x(), y() + h() - H, w(), H);
+ }
+}
+
+int Fl_Tabs::handle(int event) {
+
+ Fl_Widget *o;
+ int i;
+
+ switch (event) {
+
+ case FL_PUSH: {
+ int H = tab_height();
+ if (H >= 0) {
+ if (Fl::event_y() > y()+H) return Fl_Group::handle(event);
+ } else {
+ if (Fl::event_y() < y()+h()+H) return Fl_Group::handle(event);
+ }}
+ /* FALLTHROUGH */
+ case FL_DRAG:
+ case FL_RELEASE:
+ o = which(Fl::event_x(), Fl::event_y());
+ if (event == FL_RELEASE) {
+ push(0);
+ if (o && Fl::visible_focus() && Fl::focus()!=this) {
+ Fl::focus(this);
+ redraw_tabs();
+ }
+ if (o && value(o)) {
+ Fl_Widget_Tracker wp(o);
+ set_changed();
+ do_callback();
+ if (wp.deleted()) return 1;
+ }
+ Fl_Tooltip::current(o);
+ } else {
+ push(o);
+ }
+ return 1;
+ case FL_MOVE: {
+ int ret = Fl_Group::handle(event);
+ Fl_Widget *o = Fl_Tooltip::current(), *n = o;
+ int H = tab_height();
+ if ( (H>=0) && (Fl::event_y()>y()+H) )
+ return ret;
+ else if ( (H<0) && (Fl::event_y() < y()+h()+H) )
+ return ret;
+ else {
+ n = which(Fl::event_x(), Fl::event_y());
+ if (!n) n = this;
+ }
+ if (n!=o)
+ Fl_Tooltip::enter(n);
+ return ret; }
+ case FL_FOCUS:
+ case FL_UNFOCUS:
+ if (!Fl::visible_focus()) return Fl_Group::handle(event);
+ if (Fl::event() == FL_RELEASE ||
+ Fl::event() == FL_SHORTCUT ||
+ Fl::event() == FL_KEYBOARD ||
+ Fl::event() == FL_FOCUS ||
+ Fl::event() == FL_UNFOCUS) {
+ redraw_tabs();
+ if (Fl::event() == FL_FOCUS) return Fl_Group::handle(event);
+ if (Fl::event() == FL_UNFOCUS) return 0;
+ else return 1;
+ } else return Fl_Group::handle(event);
+ case FL_KEYBOARD:
+ switch (Fl::event_key()) {
+ case FL_Left:
+ if (child(0)->visible()) return 0;
+ for (i = 1; i < children(); i ++)
+ if (child(i)->visible()) break;
+ value(child(i - 1));
+ set_changed();
+ do_callback();
+ return 1;
+ case FL_Right:
+ if (child(children() - 1)->visible()) return 0;
+ for (i = 0; i < children(); i ++)
+ if (child(i)->visible()) break;
+ value(child(i + 1));
+ set_changed();
+ do_callback();
+ return 1;
+ case FL_Down:
+ redraw();
+ return Fl_Group::handle(FL_FOCUS);
+ default:
+ break;
+ }
+ return Fl_Group::handle(event);
+ case FL_SHORTCUT:
+ for (i = 0; i < children(); ++i) {
+ Fl_Widget *c = child(i);
+ if (c->test_shortcut(c->label())) {
+ char sc = !c->visible();
+ value(c);
+ if (sc) set_changed();
+ do_callback();
+ return 1;
+ }
+ }
+ return Fl_Group::handle(event);
+ case FL_SHOW:
+ value(); // update visibilities and fall through
+ default:
+ return Fl_Group::handle(event);
+
+ }
+}
+
+int Fl_Tabs::push(Fl_Widget *o) {
+ if (push_ == o) return 0;
+ if ( (push_ && !push_->visible()) || (o && !o->visible()) )
+ redraw_tabs();
+ push_ = o;
+ return 1;
+}
+
+/**
+ Gets the currently visible widget/tab.
+ The value() is the first visible child (or the last child if none
+ are visible) and this also hides any other children.
+ This allows the tabs to be deleted, moved to other groups, and
+ show()/hide() called without it screwing up.
+*/
+Fl_Widget* Fl_Tabs::value() {
+ Fl_Widget* v = 0;
+ Fl_Widget*const* a = array();
+ for (int i=children(); i--;) {
+ Fl_Widget* o = *a++;
+ if (v) o->hide();
+ else if (o->visible()) v = o;
+ else if (!i) {o->show(); v = o;}
+ }
+ return v;
+}
+
+/**
+ Sets the widget to become the current visible widget/tab.
+ Setting the value hides all other children, and makes this one
+ visible, if it is really a child.
+*/
+int Fl_Tabs::value(Fl_Widget *newvalue) {
+ Fl_Widget*const* a = array();
+ int ret = 0;
+ for (int i=children(); i--;) {
+ Fl_Widget* o = *a++;
+ if (o == newvalue) {
+ if (!o->visible()) ret = 1;
+ o->show();
+ } else {
+ o->hide();
+ }
+ }
+ return ret;
+}
+
+enum {LEFT, RIGHT, SELECTED};
+
+void Fl_Tabs::draw() {
+ Fl_Widget *v = value();
+ int H = tab_height();
+
+ if (damage() & FL_DAMAGE_ALL) { // redraw the entire thing:
+ Fl_Color c = v ? v->color() : color();
+
+ draw_box(box(), x(), y()+(H>=0?H:0), w(), h()-(H>=0?H:-H), c);
+
+ if (selection_color() != c) {
+ // Draw the top or bottom SELECTION_BORDER lines of the tab pane in the
+ // selection color so that the user knows which tab is selected...
+ int clip_y = (H >= 0) ? y() + H : y() + h() + H - SELECTION_BORDER;
+ fl_push_clip(x(), clip_y, w(), SELECTION_BORDER);
+ draw_box(box(), x(), clip_y, w(), SELECTION_BORDER, selection_color());
+ fl_pop_clip();
+ }
+ if (v) draw_child(*v);
+ } else { // redraw the child
+ if (v) update_child(*v);
+ }
+ if (damage() & (FL_DAMAGE_SCROLL|FL_DAMAGE_ALL)) {
+ int nc = children();
+ int selected = tab_positions();
+ int i;
+ Fl_Widget*const* a = array();
+ for (i=0; i<selected; i++)
+ draw_tab(x()+tab_pos[i], x()+tab_pos[i+1],
+ tab_width[i], H, a[i], LEFT);
+ for (i=nc-1; i > selected; i--)
+ draw_tab(x()+tab_pos[i], x()+tab_pos[i+1],
+ tab_width[i], H, a[i], RIGHT);
+ if (v) {
+ i = selected;
+ draw_tab(x()+tab_pos[i], x()+tab_pos[i+1],
+ tab_width[i], H, a[i], SELECTED);
+ }
+ }
+}
+
+void Fl_Tabs::draw_tab(int x1, int x2, int W, int H, Fl_Widget* o, int what) {
+ int sel = (what == SELECTED);
+ int dh = Fl::box_dh(box());
+ int dy = Fl::box_dy(box());
+ char prev_draw_shortcut = fl_draw_shortcut;
+ fl_draw_shortcut = 1;
+
+ Fl_Boxtype bt = (o==push_ &&!sel) ? fl_down(box()) : box();
+
+ // compute offsets to make selected tab look bigger
+ int yofs = sel ? 0 : BORDER;
+
+ if ((x2 < x1+W) && what == RIGHT) x1 = x2 - W;
+
+ if (H >= 0) {
+ if (sel) fl_push_clip(x1, y(), x2 - x1, H + dh - dy);
+ else fl_push_clip(x1, y(), x2 - x1, H);
+
+ H += dh;
+
+ Fl_Color c = sel ? selection_color() : o->selection_color();
+
+ draw_box(bt, x1, y() + yofs, W, H + 10 - yofs, c);
+
+ // Save the previous label color
+ Fl_Color oc = o->labelcolor();
+
+ // Draw the label using the current color...
+ o->labelcolor(sel ? labelcolor() : o->labelcolor());
+ o->draw_label(x1, y() + yofs, W, H - yofs, FL_ALIGN_CENTER);
+
+ // Restore the original label color...
+ o->labelcolor(oc);
+
+ if (Fl::focus() == this && o->visible())
+ draw_focus(box(), x1, y(), W, H);
+
+ fl_pop_clip();
+ } else {
+ H = -H;
+
+ if (sel) fl_push_clip(x1, y() + h() - H - dy, x2 - x1, H + dy);
+ else fl_push_clip(x1, y() + h() - H, x2 - x1, H);
+
+ H += dh;
+
+ Fl_Color c = sel ? selection_color() : o->selection_color();
+
+ draw_box(bt, x1, y() + h() - H - 10, W, H + 10 - yofs, c);
+
+ // Save the previous label color
+ Fl_Color oc = o->labelcolor();
+
+ // Draw the label using the current color...
+ o->labelcolor(sel ? labelcolor() : o->labelcolor());
+ o->draw_label(x1, y() + h() - H, W, H - yofs, FL_ALIGN_CENTER);
+
+ // Restore the original label color...
+ o->labelcolor(oc);
+
+ if (Fl::focus() == this && o->visible())
+ draw_focus(box(), x1, y() + h() - H, W, H);
+
+ fl_pop_clip();
+ }
+ fl_draw_shortcut = prev_draw_shortcut;
+}
+
+/**
+ Creates a new Fl_Tabs widget using the given position, size,
+ and label string. The default boxtype is FL_THIN_UP_BOX.
+
+ Use add(Fl_Widget*) to add each child, which are usually
+ Fl_Group widgets. The children should be sized to stay
+ away from the top or bottom edge of the Fl_Tabs widget,
+ which is where the tabs will be drawn.
+
+ All children of Fl_Tabs should have the same size and exactly fit on top of
+ each other. They should only leave space above or below where that tabs will
+ go, but not on the sides. If the first child of Fl_Tabs is set to
+ "resizable()", the riders will not resize when the tabs are resized.
+
+ The destructor <I>also deletes all the children</I>. This
+ allows a whole tree to be deleted at once, without having to
+ keep a pointer to all the children in the user code. A kludge
+ has been done so the Fl_Tabs and all of its children
+ can be automatic (local) variables, but you must declare the
+ Fl_Tabs widget <I>first</I> so that it is destroyed last.
+*/
+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);
+ push_ = 0;
+ tab_pos = 0;
+ tab_width = 0;
+ tab_count = 0;
+}
+
+Fl_Tabs::~Fl_Tabs() {
+ clear_tab_positions();
+}
+
+/**
+ Returns the position and size available to be used by its children.
+
+ If there isn't any child yet the \p tabh parameter will be used to
+ calculate the return values. This assumes that the children's labelsize
+ is the same as the Fl_Tabs' labelsize and adds a small border.
+
+ If there are already children, the values of child(0) are returned, and
+ \p tabh is ignored.
+
+ \note Children should always use the same positions and sizes.
+
+ \p tabh can be one of
+ \li 0: calculate label size, tabs on top
+ \li -1: calculate label size, tabs on bottom
+ \li > 0: use given \p tabh value, tabs on top (height = tabh)
+ \li < -1: use given \p tabh value, tabs on bottom (height = -tabh)
+
+ \param[in] tabh position and optional height of tabs (see above)
+ \param[out] rx,ry,rw,rh (x,y,w,h) of client area for children
+
+ \since FLTK 1.3.0
+*/
+void Fl_Tabs::client_area(int &rx, int &ry, int &rw, int &rh, int tabh) {
+
+ if (children()) { // use existing values
+
+ rx = child(0)->x();
+ ry = child(0)->y();
+ rw = child(0)->w();
+ rh = child(0)->h();
+
+ } else { // calculate values
+
+ int y_offset;
+ int label_height = fl_height(labelfont(), labelsize()) + BORDER*2;
+
+ if (tabh == 0) // use default (at top)
+ y_offset = label_height;
+ else if (tabh == -1) // use default (at bottom)
+ y_offset = -label_height;
+ else
+ y_offset = tabh; // user given value
+
+ rx = x();
+ rw = w();
+
+ if (y_offset >= 0) { // labels at top
+ ry = y() + y_offset;
+ rh = h() - y_offset;
+ } else { // labels at bottom
+ ry = y();
+ rh = h() + y_offset;
+ }
+ }
+}
+
+void Fl_Tabs::clear_tab_positions() {
+ if (tab_pos) {
+ free(tab_pos);
+ tab_pos = 0;
+ }
+ if (tab_width){
+ free(tab_width);
+ tab_width = 0;
+ }
+}
+
+//
+// End of "$Id: Fl_Tabs.cxx 8658 2011-05-12 15:53:59Z manolo $".
+//