1998-10-20 00:46:58 +04:00
|
|
|
//
|
2005-02-25 00:55:12 +03:00
|
|
|
// "$Id$"
|
1998-10-20 00:46:58 +04:00
|
|
|
//
|
|
|
|
// Tab widget for the Fast Light Tool Kit (FLTK).
|
|
|
|
//
|
2010-11-29 00:06:39 +03:00
|
|
|
// Copyright 1998-2010 by Bill Spitzak and others.
|
1998-10-20 00:46:58 +04:00
|
|
|
//
|
2011-07-19 08:49:30 +04:00
|
|
|
// This library is free software. Distribution and use rights are outlined in
|
|
|
|
// the file "COPYING" which should have been included with this file. If this
|
|
|
|
// file is missing or damaged, see the license at:
|
|
|
|
//
|
|
|
|
// http://www.fltk.org/COPYING.php
|
1998-10-20 00:46:58 +04:00
|
|
|
//
|
2005-04-16 04:13:17 +04:00
|
|
|
// Please report all bugs and problems on the following page:
|
|
|
|
//
|
|
|
|
// http://www.fltk.org/str.php
|
1998-10-20 00:46:58 +04:00
|
|
|
//
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2008-09-14 19:45:27 +04:00
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
// 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.
|
|
|
|
|
2008-12-13 21:31:54 +03:00
|
|
|
// Each child widget is a card, and its label() is printed on the card tab.
|
1998-10-06 22:21:25 +04:00
|
|
|
// Clicking the tab makes that card visible.
|
|
|
|
|
2001-08-05 19:34:28 +04:00
|
|
|
#include <stdio.h>
|
1998-10-06 22:21:25 +04:00
|
|
|
#include <FL/Fl.H>
|
|
|
|
#include <FL/Fl_Tabs.H>
|
|
|
|
#include <FL/fl_draw.H>
|
2006-05-21 13:41:47 +04:00
|
|
|
#include <FL/Fl_Tooltip.H>
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2001-12-01 16:59:50 +03:00
|
|
|
#define BORDER 2
|
|
|
|
#define EXTRASPACE 10
|
2010-12-18 18:31:44 +03:00
|
|
|
#define SELECTION_BORDER 5
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2010-12-22 16:06:03 +03:00
|
|
|
// 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
|
2008-12-13 21:31:54 +03:00
|
|
|
// edge of the slope. They are either separated by the correct distance
|
2001-12-01 16:59:50 +03:00
|
|
|
// or by EXTRASPACE or by zero.
|
2010-12-22 16:06:03 +03:00
|
|
|
// These positions are updated in the private arrays tab_pos[] and
|
|
|
|
// tab_width[], resp.. If needed, these arrays are (re)allocated.
|
1998-10-06 22:21:25 +04:00
|
|
|
// Return value is the index of the selected item.
|
|
|
|
|
2010-12-22 16:06:03 +03:00
|
|
|
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;
|
2002-09-09 06:04:46 +04:00
|
|
|
int selected = 0;
|
1998-10-06 22:21:25 +04:00
|
|
|
Fl_Widget*const* a = array();
|
|
|
|
int i;
|
2007-05-02 00:20:21 +04:00
|
|
|
char prev_draw_shortcut = fl_draw_shortcut;
|
|
|
|
fl_draw_shortcut = 1;
|
|
|
|
|
2010-12-22 16:06:03 +03:00
|
|
|
tab_pos[0] = Fl::box_dx(box());
|
|
|
|
for (i=0; i<nc; i++) {
|
1998-10-06 22:21:25 +04:00
|
|
|
Fl_Widget* o = *a++;
|
1999-10-15 13:01:48 +04:00
|
|
|
if (o->visible()) selected = i;
|
2002-01-29 03:56:19 +03:00
|
|
|
|
|
|
|
int wt = 0; int ht = 0;
|
|
|
|
o->measure_label(wt,ht);
|
|
|
|
|
2010-12-22 16:06:03 +03:00
|
|
|
tab_width[i] = wt + EXTRASPACE;
|
|
|
|
tab_pos[i+1] = tab_pos[i] + tab_width[i] + BORDER;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
2007-05-02 00:20:21 +04:00
|
|
|
fl_draw_shortcut = prev_draw_shortcut;
|
|
|
|
|
2002-08-09 07:17:30 +04:00
|
|
|
int r = w();
|
2010-12-22 16:06:03 +03:00
|
|
|
if (tab_pos[i] <= r) return selected;
|
1998-10-06 22:21:25 +04:00
|
|
|
// uh oh, they are too big:
|
|
|
|
// pack them against right edge:
|
2010-12-22 16:06:03 +03:00
|
|
|
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;
|
2001-12-01 16:59:50 +03:00
|
|
|
r -= EXTRASPACE;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
// pack them against left edge and truncate width if they still don't fit:
|
2010-12-22 16:06:03 +03:00
|
|
|
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;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
// adjust edges according to visiblity:
|
2010-12-22 16:06:03 +03:00
|
|
|
for (i = nc; i > selected; i--) {
|
|
|
|
tab_pos[i] = tab_pos[i-1] + tab_width[i-1];
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
return selected;
|
|
|
|
}
|
|
|
|
|
2010-12-22 16:06:03 +03:00
|
|
|
// Returns space (height) in pixels needed for tabs. Negative to put them on the bottom.
|
|
|
|
// Returns full height, if children() = 0.
|
1998-10-06 22:21:25 +04:00
|
|
|
int Fl_Tabs::tab_height() {
|
2010-12-22 16:06:03 +03:00
|
|
|
if (children() == 0) return h();
|
1998-10-06 22:21:25 +04:00
|
|
|
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;
|
2001-12-01 16:59:50 +03:00
|
|
|
if (H2 > H) return (H2 <= 0) ? 0 : -H2;
|
|
|
|
else return (H <= 0) ? 0 : H;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
2013-03-26 05:55:01 +04:00
|
|
|
/**
|
|
|
|
Return the widget of the tab the user clicked on at \p event_x / \p event_y.
|
|
|
|
This is used for event handling (clicks) and by fluid to pick tabs.
|
|
|
|
|
|
|
|
\returns The child widget of the tab the user clicked on, or<br>
|
|
|
|
0 if there are no children or if the event is outside of the tabs area.
|
|
|
|
*/
|
1998-10-06 22:21:25 +04:00
|
|
|
Fl_Widget *Fl_Tabs::which(int event_x, int event_y) {
|
2010-12-22 16:06:03 +03:00
|
|
|
if (children() == 0) return 0;
|
1998-10-06 22:21:25 +04:00
|
|
|
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;
|
2010-10-30 23:28:17 +04:00
|
|
|
Fl_Widget *ret = 0L;
|
|
|
|
int nc = children();
|
2010-12-22 16:06:03 +03:00
|
|
|
tab_positions();
|
|
|
|
for (int i=0; i<nc; i++) {
|
|
|
|
if (event_x < x()+tab_pos[i+1]) {
|
2010-10-30 23:28:17 +04:00
|
|
|
ret = child(i);
|
|
|
|
break;
|
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
2010-10-30 23:28:17 +04:00
|
|
|
return ret;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
2006-05-30 13:38:19 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
int Fl_Tabs::handle(int event) {
|
|
|
|
|
|
|
|
Fl_Widget *o;
|
2001-08-05 19:34:28 +04:00
|
|
|
int i;
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
|
|
|
|
case FL_PUSH: {
|
|
|
|
int H = tab_height();
|
|
|
|
if (H >= 0) {
|
2002-10-22 21:39:12 +04:00
|
|
|
if (Fl::event_y() > y()+H) return Fl_Group::handle(event);
|
1998-10-06 22:21:25 +04:00
|
|
|
} else {
|
2002-10-22 21:39:12 +04:00
|
|
|
if (Fl::event_y() < y()+h()+H) return Fl_Group::handle(event);
|
1998-10-06 22:21:25 +04:00
|
|
|
}}
|
2010-02-27 00:10:46 +03:00
|
|
|
/* FALLTHROUGH */
|
1998-10-06 22:21:25 +04:00
|
|
|
case FL_DRAG:
|
2013-04-09 00:18:40 +04:00
|
|
|
case FL_RELEASE: {
|
|
|
|
// PUSH, DRAG, RELEASE..
|
|
|
|
int do_cb=0;
|
|
|
|
if ((o = which(Fl::event_x(), Fl::event_y()))) { // get tab group for tab user is over
|
|
|
|
if (o != value()) set_changed(); // if over tab, handle change
|
|
|
|
else clear_changed();
|
|
|
|
}
|
2004-07-27 20:02:21 +04:00
|
|
|
if (event == FL_RELEASE) {
|
2013-04-09 00:18:40 +04:00
|
|
|
push(0); // no longer 'pushed'
|
|
|
|
// Over a tab?
|
|
|
|
if (o) {
|
|
|
|
// Handle taking keyboard focus w/visible focus indication
|
|
|
|
if (Fl::visible_focus() && Fl::focus()!=this) {
|
|
|
|
Fl::focus(this);
|
|
|
|
redraw_tabs();
|
|
|
|
}
|
|
|
|
if (value(o)) { // commit to value, see if it "changed"..
|
|
|
|
set_changed(); // it changed
|
|
|
|
do_cb =
|
|
|
|
( (when() & FL_WHEN_RELEASE) && // wants cb on RELEASE and..
|
|
|
|
(when() & FL_WHEN_CHANGED) // when changed?
|
|
|
|
) || ( // ..or..
|
|
|
|
(when() == FL_WHEN_RELEASE) // *only* WHEN_RELEASE specified? (default behavior)
|
|
|
|
) ? 1 : 0;
|
|
|
|
} else {
|
|
|
|
clear_changed(); // no change
|
|
|
|
do_cb = (when() & FL_WHEN_RELEASE && // wants cb when RELEASE and..
|
|
|
|
when() & FL_WHEN_NOT_CHANGED)?1:0; // ..when no change occurred?
|
|
|
|
}
|
2004-07-27 20:02:21 +04:00
|
|
|
}
|
2006-08-25 12:10:31 +04:00
|
|
|
Fl_Tooltip::current(o);
|
|
|
|
} else {
|
2013-04-09 00:18:40 +04:00
|
|
|
// PUSH or DRAG?
|
|
|
|
// Be careful; a user can PUSH on a tab, but can abort the change
|
|
|
|
// if they glide off the tab before a RELEASE. Callbacks on PUSH
|
|
|
|
// will see a value change, but it may change back on RELEASE if
|
|
|
|
// the user aborts the change.
|
|
|
|
//
|
|
|
|
push(o); // DRAG|PUSH -- indicate if still on tab
|
|
|
|
if (o && event == FL_PUSH) { // still on tab?
|
|
|
|
// See if we should do the callback
|
|
|
|
do_cb =
|
|
|
|
( !(when() & FL_WHEN_RELEASE) && // wants callback on PUSH? (ie. NOT RELEASE)
|
|
|
|
(
|
|
|
|
((when() & FL_WHEN_NOT_CHANGED) && !changed()) || // want cb if no change and no change occurred?
|
|
|
|
((when() & FL_WHEN_CHANGED) && changed()) // want cb if change and change occurred?
|
|
|
|
)
|
|
|
|
) ? 1 : 0;
|
|
|
|
}
|
2006-08-25 12:10:31 +04:00
|
|
|
}
|
2013-04-09 00:18:40 +04:00
|
|
|
if (do_cb)
|
|
|
|
do_callback();
|
|
|
|
return 1; }
|
2006-05-21 13:41:47 +04:00
|
|
|
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; }
|
2001-08-05 19:34:28 +04:00
|
|
|
case FL_FOCUS:
|
|
|
|
case FL_UNFOCUS:
|
2002-10-29 23:44:22 +03:00
|
|
|
if (!Fl::visible_focus()) return Fl_Group::handle(event);
|
2002-11-20 20:42:09 +03:00
|
|
|
if (Fl::event() == FL_RELEASE ||
|
2002-10-29 23:44:22 +03:00
|
|
|
Fl::event() == FL_SHORTCUT ||
|
2005-05-19 19:57:32 +04:00
|
|
|
Fl::event() == FL_KEYBOARD ||
|
|
|
|
Fl::event() == FL_FOCUS ||
|
|
|
|
Fl::event() == FL_UNFOCUS) {
|
2006-05-30 13:38:19 +04:00
|
|
|
redraw_tabs();
|
2011-02-06 17:48:36 +03:00
|
|
|
if (Fl::event() == FL_FOCUS) return Fl_Group::handle(event);
|
2011-05-12 19:53:59 +04:00
|
|
|
if (Fl::event() == FL_UNFOCUS) return 0;
|
2005-05-19 19:57:32 +04:00
|
|
|
else return 1;
|
2002-10-29 23:44:22 +03:00
|
|
|
} else return Fl_Group::handle(event);
|
2013-04-09 00:18:40 +04:00
|
|
|
/* NOTREACHED */
|
2001-08-05 19:34:28 +04:00
|
|
|
case FL_KEYBOARD:
|
2013-04-09 00:18:40 +04:00
|
|
|
// NOTE:
|
|
|
|
// 1) FL_KEYBOARD same as FL_KEYDN; we can receive a string of these
|
|
|
|
// without FL_KEYUP between during key-repeat.
|
|
|
|
// 2) We use push() to keep track of value() changes during key-repeat for FL_KEYUP.
|
|
|
|
// (It would be bad if app's callback saw changed()=false if key-repeat hit the
|
|
|
|
// left-most or right-most tabs)
|
|
|
|
//
|
2001-08-05 19:34:28 +04:00
|
|
|
switch (Fl::event_key()) {
|
|
|
|
case FL_Left:
|
2013-04-09 00:18:40 +04:00
|
|
|
// First push on tab? (ie. non key-repeat?)
|
|
|
|
// Save current value (so KEYUP can see if changed() during key-repeat)
|
|
|
|
// and clear_changed().
|
|
|
|
//
|
|
|
|
if (!push()) { push(value()); clear_changed(); }// first push on tab? (not key-repeat) save curr value for change
|
|
|
|
if (child(0)->visible()) { // already on left-most tab?
|
|
|
|
if (when() & FL_WHEN_NOT_CHANGED) // want callback if no change?
|
|
|
|
do_callback(); // do callback
|
|
|
|
// return 1 to 'handle' the arrow key, or 0 to let
|
|
|
|
// arrow key be used by fltk for focus navigation.
|
|
|
|
return (Fl::option(Fl::OPTION_ARROW_FOCUS) ? 0 : 1);
|
|
|
|
}
|
|
|
|
for (i = 1; i < children(); i ++) // Find currently visible child
|
2001-08-05 19:34:28 +04:00
|
|
|
if (child(i)->visible()) break;
|
2013-04-09 00:18:40 +04:00
|
|
|
value(child(i - 1)); // select the next tab to the left
|
2004-07-27 20:02:21 +04:00
|
|
|
set_changed();
|
2013-04-09 00:18:40 +04:00
|
|
|
if ( (when() & FL_WHEN_CHANGED) && // changed: want cb on change and..
|
|
|
|
!(when() & FL_WHEN_RELEASE)) // ..on PUSH?
|
|
|
|
do_callback(); // do callback
|
2001-08-05 19:34:28 +04:00
|
|
|
return 1;
|
|
|
|
case FL_Right:
|
2013-04-09 00:18:40 +04:00
|
|
|
if (!push()) { push(value()); clear_changed(); }// first push on tab? (not key-repeat)
|
|
|
|
if (child(children() - 1)->visible()) { // already on right-most tab?
|
|
|
|
if (when() & FL_WHEN_NOT_CHANGED) // want callback if no change?
|
|
|
|
do_callback(); // do callback
|
|
|
|
// return 1 to 'handle' the arrow key, or 0 to let
|
|
|
|
// arrow key be used by fltk for focus navigation.
|
|
|
|
return (Fl::option(Fl::OPTION_ARROW_FOCUS) ? 0 : 1);
|
|
|
|
}
|
|
|
|
for (i = 0; i < children(); i ++) // Find currently visible child
|
2001-08-05 19:34:28 +04:00
|
|
|
if (child(i)->visible()) break;
|
2013-04-09 00:18:40 +04:00
|
|
|
value(child(i + 1)); // select the next tab to the right
|
2004-07-27 20:02:21 +04:00
|
|
|
set_changed();
|
2013-04-09 00:18:40 +04:00
|
|
|
if ( (when() & FL_WHEN_CHANGED) && // change: want callback on change and..
|
|
|
|
!(when() & FL_WHEN_RELEASE)) // ..on PUSH?
|
|
|
|
do_callback(); // do callback
|
2001-08-05 19:34:28 +04:00
|
|
|
return 1;
|
|
|
|
case FL_Down:
|
|
|
|
redraw();
|
|
|
|
return Fl_Group::handle(FL_FOCUS);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2007-05-02 00:20:21 +04:00
|
|
|
return Fl_Group::handle(event);
|
2013-04-09 00:18:40 +04:00
|
|
|
case FL_KEYUP:
|
|
|
|
switch (Fl::event_key()) {
|
|
|
|
case FL_Left:
|
|
|
|
case FL_Right:
|
|
|
|
if (push()) {
|
|
|
|
// See if value() changed since first KEYDN
|
|
|
|
if (push() != value()) set_changed();
|
|
|
|
else clear_changed();
|
|
|
|
push(0); // key released, no longer push()ed
|
|
|
|
if ( when() & FL_WHEN_RELEASE) { // want cb on RELEASE?
|
|
|
|
if ( (changed() && ( // changed and..
|
|
|
|
(when() & FL_WHEN_CHANGED) || // want cb on change or..
|
|
|
|
(when()==FL_WHEN_RELEASE))) || // want cb on RELEASE only (implies change -- legacy default)
|
|
|
|
(!changed() && // ..or, not changed and..
|
|
|
|
(when() & FL_WHEN_NOT_CHANGED))) { // want cb on no change?
|
|
|
|
do_callback(); // do callback
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return Fl_Group::handle(event);
|
2007-05-02 00:20:21 +04:00
|
|
|
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);
|
2005-07-23 16:21:58 +04:00
|
|
|
case FL_SHOW:
|
|
|
|
value(); // update visibilities and fall through
|
2013-04-09 00:18:40 +04:00
|
|
|
/* FALLTHROUGH */
|
1998-10-06 22:21:25 +04:00
|
|
|
default:
|
|
|
|
return Fl_Group::handle(event);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-26 05:55:01 +04:00
|
|
|
/**
|
2013-03-29 07:37:04 +04:00
|
|
|
This is called by the tab widget's handle() method to set the
|
|
|
|
tab group widget the user last FL_PUSH'ed on. Set back to zero
|
|
|
|
on FL_RELEASE.
|
|
|
|
|
|
|
|
As of this writing, the value is mainly used by draw_tab()
|
|
|
|
to determine whether or not to draw a 'down' box for the tab
|
|
|
|
when it's clicked, and to turn it off if the user drags off it.
|
|
|
|
|
|
|
|
\see push().
|
2013-03-26 05:55:01 +04:00
|
|
|
*/
|
1998-10-06 22:21:25 +04:00
|
|
|
int Fl_Tabs::push(Fl_Widget *o) {
|
|
|
|
if (push_ == o) return 0;
|
2010-10-28 22:02:20 +04:00
|
|
|
if ( (push_ && !push_->visible()) || (o && !o->visible()) )
|
2007-01-21 18:54:53 +03:00
|
|
|
redraw_tabs();
|
1998-10-06 22:21:25 +04:00
|
|
|
push_ = o;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-09-14 19:45:27 +04:00
|
|
|
/**
|
|
|
|
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.
|
|
|
|
*/
|
2006-08-17 17:43:07 +04:00
|
|
|
Fl_Widget* Fl_Tabs::value() {
|
1999-10-15 13:01:48 +04:00
|
|
|
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;}
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2008-09-14 19:45:27 +04:00
|
|
|
/**
|
|
|
|
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.
|
2013-03-29 08:36:51 +04:00
|
|
|
\returns 1 if there was a change (new value different from previous),<BR>
|
|
|
|
0 if there was no change (new value already set)
|
2008-09-14 19:45:27 +04:00
|
|
|
*/
|
1999-10-15 13:01:48 +04:00
|
|
|
int Fl_Tabs::value(Fl_Widget *newvalue) {
|
|
|
|
Fl_Widget*const* a = array();
|
2000-08-30 09:51:39 +04:00
|
|
|
int ret = 0;
|
1999-10-15 13:01:48 +04:00
|
|
|
for (int i=children(); i--;) {
|
|
|
|
Fl_Widget* o = *a++;
|
|
|
|
if (o == newvalue) {
|
2000-08-30 09:51:39 +04:00
|
|
|
if (!o->visible()) ret = 1;
|
1999-10-15 13:01:48 +04:00
|
|
|
o->show();
|
|
|
|
} else {
|
|
|
|
o->hide();
|
|
|
|
}
|
|
|
|
}
|
2000-08-30 09:51:39 +04:00
|
|
|
return ret;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
enum {LEFT, RIGHT, SELECTED};
|
|
|
|
|
|
|
|
void Fl_Tabs::draw() {
|
|
|
|
Fl_Widget *v = value();
|
|
|
|
int H = tab_height();
|
2001-12-01 16:59:50 +03:00
|
|
|
|
1998-10-21 18:21:44 +04:00
|
|
|
if (damage() & FL_DAMAGE_ALL) { // redraw the entire thing:
|
2005-04-21 22:25:22 +04:00
|
|
|
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) {
|
2010-12-18 18:31:44 +03:00
|
|
|
// 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());
|
2005-04-21 22:25:22 +04:00
|
|
|
fl_pop_clip();
|
|
|
|
}
|
1998-11-05 19:04:53 +03:00
|
|
|
if (v) draw_child(*v);
|
1998-10-06 22:21:25 +04:00
|
|
|
} else { // redraw the child
|
1998-11-05 19:04:53 +03:00
|
|
|
if (v) update_child(*v);
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
2002-10-22 21:39:12 +04:00
|
|
|
if (damage() & (FL_DAMAGE_SCROLL|FL_DAMAGE_ALL)) {
|
2010-10-30 23:28:17 +04:00
|
|
|
int nc = children();
|
2010-12-22 16:06:03 +03:00
|
|
|
int selected = tab_positions();
|
1998-10-06 22:21:25 +04:00
|
|
|
int i;
|
|
|
|
Fl_Widget*const* a = array();
|
|
|
|
for (i=0; i<selected; i++)
|
2010-12-22 16:06:03 +03:00
|
|
|
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);
|
1999-10-15 13:01:48 +04:00
|
|
|
if (v) {
|
|
|
|
i = selected;
|
2010-12-22 16:06:03 +03:00
|
|
|
draw_tab(x()+tab_pos[i], x()+tab_pos[i+1],
|
|
|
|
tab_width[i], H, a[i], SELECTED);
|
1999-10-15 13:01:48 +04:00
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fl_Tabs::draw_tab(int x1, int x2, int W, int H, Fl_Widget* o, int what) {
|
|
|
|
int sel = (what == SELECTED);
|
2001-12-01 16:59:50 +03:00
|
|
|
int dh = Fl::box_dh(box());
|
|
|
|
int dy = Fl::box_dy(box());
|
2007-05-02 00:20:21 +04:00
|
|
|
char prev_draw_shortcut = fl_draw_shortcut;
|
|
|
|
fl_draw_shortcut = 1;
|
|
|
|
|
2007-01-21 18:54:53 +03:00
|
|
|
Fl_Boxtype bt = (o==push_ &&!sel) ? fl_down(box()) : box();
|
2001-12-01 16:59:50 +03:00
|
|
|
|
2005-05-26 17:54:53 +04:00
|
|
|
// compute offsets to make selected tab look bigger
|
|
|
|
int yofs = sel ? 0 : BORDER;
|
|
|
|
|
2001-12-01 16:59:50 +03:00
|
|
|
if ((x2 < x1+W) && what == RIGHT) x1 = x2 - W;
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
if (H >= 0) {
|
2008-12-06 17:59:52 +03:00
|
|
|
if (sel) fl_push_clip(x1, y(), x2 - x1, H + dh - dy);
|
|
|
|
else fl_push_clip(x1, y(), x2 - x1, H);
|
2001-12-01 16:59:50 +03:00
|
|
|
|
|
|
|
H += dh;
|
|
|
|
|
2005-04-21 22:25:22 +04:00
|
|
|
Fl_Color c = sel ? selection_color() : o->selection_color();
|
|
|
|
|
2007-01-21 18:54:53 +03:00
|
|
|
draw_box(bt, x1, y() + yofs, W, H + 10 - yofs, c);
|
2001-12-01 16:59:50 +03:00
|
|
|
|
2005-04-21 22:25:22 +04:00
|
|
|
// Save the previous label color
|
|
|
|
Fl_Color oc = o->labelcolor();
|
|
|
|
|
2005-05-19 16:13:35 +04:00
|
|
|
// Draw the label using the current color...
|
|
|
|
o->labelcolor(sel ? labelcolor() : o->labelcolor());
|
2005-05-26 17:54:53 +04:00
|
|
|
o->draw_label(x1, y() + yofs, W, H - yofs, FL_ALIGN_CENTER);
|
2001-12-01 16:59:50 +03:00
|
|
|
|
2005-04-21 22:25:22 +04:00
|
|
|
// Restore the original label color...
|
|
|
|
o->labelcolor(oc);
|
|
|
|
|
2001-12-01 16:59:50 +03:00
|
|
|
if (Fl::focus() == this && o->visible())
|
|
|
|
draw_focus(box(), x1, y(), W, H);
|
|
|
|
|
|
|
|
fl_pop_clip();
|
1998-10-06 22:21:25 +04:00
|
|
|
} else {
|
2001-12-01 16:59:50 +03:00
|
|
|
H = -H;
|
|
|
|
|
2008-12-06 17:59:52 +03:00
|
|
|
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);
|
2001-12-01 16:59:50 +03:00
|
|
|
|
|
|
|
H += dh;
|
|
|
|
|
2005-04-21 22:25:22 +04:00
|
|
|
Fl_Color c = sel ? selection_color() : o->selection_color();
|
|
|
|
|
2007-01-21 18:54:53 +03:00
|
|
|
draw_box(bt, x1, y() + h() - H - 10, W, H + 10 - yofs, c);
|
2005-04-21 22:25:22 +04:00
|
|
|
|
|
|
|
// Save the previous label color
|
|
|
|
Fl_Color oc = o->labelcolor();
|
|
|
|
|
2005-05-19 16:13:35 +04:00
|
|
|
// Draw the label using the current color...
|
|
|
|
o->labelcolor(sel ? labelcolor() : o->labelcolor());
|
2005-05-26 17:54:53 +04:00
|
|
|
o->draw_label(x1, y() + h() - H, W, H - yofs, FL_ALIGN_CENTER);
|
2001-08-05 19:34:28 +04:00
|
|
|
|
2005-04-21 22:25:22 +04:00
|
|
|
// Restore the original label color...
|
|
|
|
o->labelcolor(oc);
|
|
|
|
|
2001-08-05 19:34:28 +04:00
|
|
|
if (Fl::focus() == this && o->visible())
|
2001-12-01 16:59:50 +03:00
|
|
|
draw_focus(box(), x1, y() + h() - H, W, H);
|
|
|
|
|
|
|
|
fl_pop_clip();
|
2001-08-05 19:34:28 +04:00
|
|
|
}
|
2007-05-02 00:20:21 +04:00
|
|
|
fl_draw_shortcut = prev_draw_shortcut;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
2008-09-14 19:45:27 +04:00
|
|
|
/**
|
|
|
|
Creates a new Fl_Tabs widget using the given position, size,
|
|
|
|
and label string. The default boxtype is FL_THIN_UP_BOX.
|
|
|
|
|
2010-10-30 20:10:37 +04:00
|
|
|
Use add(Fl_Widget*) to add each child, which are usually
|
2008-09-14 19:45:27 +04:00
|
|
|
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.
|
|
|
|
|
2010-12-22 16:06:03 +03:00
|
|
|
All children of Fl_Tabs should have the same size and exactly fit on top of
|
2010-10-30 20:10:37 +04:00
|
|
|
each other. They should only leave space above or below where that tabs will
|
2010-12-22 16:06:03 +03:00
|
|
|
go, but not on the sides. If the first child of Fl_Tabs is set to
|
2010-10-30 20:10:37 +04:00
|
|
|
"resizable()", the riders will not resize when the tabs are resized.
|
2010-12-22 16:06:03 +03:00
|
|
|
|
2010-10-30 20:10:37 +04:00
|
|
|
The destructor <I>also deletes all the children</I>. This
|
2008-09-14 19:45:27 +04:00
|
|
|
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
|
2008-12-13 21:31:54 +03:00
|
|
|
has been done so the Fl_Tabs and all of its children
|
2008-09-14 19:45:27 +04:00
|
|
|
can be automatic (local) variables, but you must declare the
|
|
|
|
Fl_Tabs widget <I>first</I> so that it is destroyed last.
|
|
|
|
*/
|
1998-10-06 22:21:25 +04:00
|
|
|
Fl_Tabs::Fl_Tabs(int X,int Y,int W, int H, const char *l) :
|
1999-10-15 13:01:48 +04:00
|
|
|
Fl_Group(X,Y,W,H,l)
|
|
|
|
{
|
|
|
|
box(FL_THIN_UP_BOX);
|
|
|
|
push_ = 0;
|
2010-12-22 16:06:03 +03:00
|
|
|
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
|
2011-03-30 16:07:52 +04:00
|
|
|
|
|
|
|
\since FLTK 1.3.0
|
2010-12-22 16:06:03 +03:00
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
1998-10-20 00:46:58 +04:00
|
|
|
|
|
|
|
//
|
2005-02-25 00:55:12 +03:00
|
|
|
// End of "$Id$".
|
1998-10-20 00:46:58 +04:00
|
|
|
//
|