2 // "$Id: Fl_Tabs.cxx 8658 2011-05-12 15:53:59Z manolo $"
4 // Tab widget for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2010 by Bill Spitzak and others.
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
29 // This is the "file card tabs" interface to allow you to put lots and lots
30 // of buttons and switches in a panel, as popularized by many toolkits.
32 // Each child widget is a card, and its label() is printed on the card tab.
33 // Clicking the tab makes that card visible.
37 #include <FL/Fl_Tabs.H>
38 #include <FL/fl_draw.H>
39 #include <FL/Fl_Tooltip.H>
43 #define SELECTION_BORDER 5
45 // Return the left edges of each tab (plus a fake left edge for a tab
46 // past the right-hand one). These positions are actually of the left
47 // edge of the slope. They are either separated by the correct distance
48 // or by EXTRASPACE or by zero.
49 // These positions are updated in the private arrays tab_pos[] and
50 // tab_width[], resp.. If needed, these arrays are (re)allocated.
51 // Return value is the index of the selected item.
53 int Fl_Tabs::tab_positions() {
55 if (nc
!= tab_count
) {
56 clear_tab_positions();
58 tab_pos
= (int*)malloc((nc
+1)*sizeof(int));
59 tab_width
= (int*)malloc((nc
+1)*sizeof(int));
63 if (nc
== 0) return 0;
65 Fl_Widget
*const* a
= array();
67 char prev_draw_shortcut
= fl_draw_shortcut
;
70 tab_pos
[0] = Fl::box_dx(box());
71 for (i
=0; i
<nc
; i
++) {
73 if (o
->visible()) selected
= i
;
75 int wt
= 0; int ht
= 0;
76 o
->measure_label(wt
,ht
);
78 tab_width
[i
] = wt
+ EXTRASPACE
;
79 tab_pos
[i
+1] = tab_pos
[i
] + tab_width
[i
] + BORDER
;
81 fl_draw_shortcut
= prev_draw_shortcut
;
84 if (tab_pos
[i
] <= r
) return selected
;
85 // uh oh, they are too big:
86 // pack them against right edge:
89 int l
= r
-tab_width
[i
];
90 if (tab_pos
[i
+1] < l
) l
= tab_pos
[i
+1];
91 if (tab_pos
[i
] <= l
) break;
95 // pack them against left edge and truncate width if they still don't fit:
96 for (i
= 0; i
<nc
; i
++) {
97 if (tab_pos
[i
] >= i
*EXTRASPACE
) break;
98 tab_pos
[i
] = i
*EXTRASPACE
;
99 int W
= w()-1-EXTRASPACE
*(children()-i
) - tab_pos
[i
];
100 if (tab_width
[i
] > W
) tab_width
[i
] = W
;
102 // adjust edges according to visiblity:
103 for (i
= nc
; i
> selected
; i
--) {
104 tab_pos
[i
] = tab_pos
[i
-1] + tab_width
[i
-1];
109 // Returns space (height) in pixels needed for tabs. Negative to put them on the bottom.
110 // Returns full height, if children() = 0.
111 int Fl_Tabs::tab_height() {
112 if (children() == 0) return h();
115 Fl_Widget
*const* a
= array();
116 for (int i
=children(); i
--;) {
118 if (o
->y() < y()+H
) H
= o
->y()-y();
119 if (o
->y()+o
->h() > H2
) H2
= o
->y()+o
->h();
122 if (H2
> H
) return (H2
<= 0) ? 0 : -H2
;
123 else return (H
<= 0) ? 0 : H
;
126 // This is used for event handling (clicks) and by fluid to pick tabs.
127 // Returns 0, if children() = 0, or if the event is outside of the tabs area.
128 Fl_Widget
*Fl_Tabs::which(int event_x
, int event_y
) {
129 if (children() == 0) return 0;
130 int H
= tab_height();
132 if (event_y
> y()+h() || event_y
< y()+h()+H
) return 0;
134 if (event_y
> y()+H
|| event_y
< y()) return 0;
136 if (event_x
< x()) return 0;
140 for (int i
=0; i
<nc
; i
++) {
141 if (event_x
< x()+tab_pos
[i
+1]) {
149 void Fl_Tabs::redraw_tabs()
151 int H
= tab_height();
153 H
+= Fl::box_dy(box());
154 damage(FL_DAMAGE_SCROLL
, x(), y(), w(), H
);
156 H
= Fl::box_dy(box()) - H
;
157 damage(FL_DAMAGE_SCROLL
, x(), y() + h() - H
, w(), H
);
161 int Fl_Tabs::handle(int event
) {
169 int H
= tab_height();
171 if (Fl::event_y() > y()+H
) return Fl_Group::handle(event
);
173 if (Fl::event_y() < y()+h()+H
) return Fl_Group::handle(event
);
178 o
= which(Fl::event_x(), Fl::event_y());
179 if (event
== FL_RELEASE
) {
181 if (o
&& Fl::visible_focus() && Fl::focus()!=this) {
186 Fl_Widget_Tracker
wp(o
);
189 if (wp
.deleted()) return 1;
191 Fl_Tooltip::current(o
);
197 int ret
= Fl_Group::handle(event
);
198 Fl_Widget
*o
= Fl_Tooltip::current(), *n
= o
;
199 int H
= tab_height();
200 if ( (H
>=0) && (Fl::event_y()>y()+H
) )
202 else if ( (H
<0) && (Fl::event_y() < y()+h()+H
) )
205 n
= which(Fl::event_x(), Fl::event_y());
209 Fl_Tooltip::enter(n
);
213 if (!Fl::visible_focus()) return Fl_Group::handle(event
);
214 if (Fl::event() == FL_RELEASE
||
215 Fl::event() == FL_SHORTCUT
||
216 Fl::event() == FL_KEYBOARD
||
217 Fl::event() == FL_FOCUS
||
218 Fl::event() == FL_UNFOCUS
) {
220 if (Fl::event() == FL_FOCUS
) return Fl_Group::handle(event
);
221 if (Fl::event() == FL_UNFOCUS
) return 0;
223 } else return Fl_Group::handle(event
);
225 switch (Fl::event_key()) {
227 if (child(0)->visible()) return 0;
228 for (i
= 1; i
< children(); i
++)
229 if (child(i
)->visible()) break;
235 if (child(children() - 1)->visible()) return 0;
236 for (i
= 0; i
< children(); i
++)
237 if (child(i
)->visible()) break;
244 return Fl_Group::handle(FL_FOCUS
);
248 return Fl_Group::handle(event
);
250 for (i
= 0; i
< children(); ++i
) {
251 Fl_Widget
*c
= child(i
);
252 if (c
->test_shortcut(c
->label())) {
253 char sc
= !c
->visible();
255 if (sc
) set_changed();
260 return Fl_Group::handle(event
);
262 value(); // update visibilities and fall through
264 return Fl_Group::handle(event
);
269 int Fl_Tabs::push(Fl_Widget
*o
) {
270 if (push_
== o
) return 0;
271 if ( (push_
&& !push_
->visible()) || (o
&& !o
->visible()) )
278 Gets the currently visible widget/tab.
279 The value() is the first visible child (or the last child if none
280 are visible) and this also hides any other children.
281 This allows the tabs to be deleted, moved to other groups, and
282 show()/hide() called without it screwing up.
284 Fl_Widget
* Fl_Tabs::value() {
286 Fl_Widget
*const* a
= array();
287 for (int i
=children(); i
--;) {
290 else if (o
->visible()) v
= o
;
291 else if (!i
) {o
->show(); v
= o
;}
297 Sets the widget to become the current visible widget/tab.
298 Setting the value hides all other children, and makes this one
299 visible, if it is really a child.
301 int Fl_Tabs::value(Fl_Widget
*newvalue
) {
302 Fl_Widget
*const* a
= array();
304 for (int i
=children(); i
--;) {
307 if (!o
->visible()) ret
= 1;
316 enum {LEFT
, RIGHT
, SELECTED
};
318 void Fl_Tabs::draw() {
319 Fl_Widget
*v
= value();
320 int H
= tab_height();
322 if (damage() & FL_DAMAGE_ALL
) { // redraw the entire thing:
323 Fl_Color c
= v
? v
->color() : color();
325 draw_box(box(), x(), y()+(H
>=0?H
:0), w(), h()-(H
>=0?H
:-H
), c
);
327 if (selection_color() != c
) {
328 // Draw the top or bottom SELECTION_BORDER lines of the tab pane in the
329 // selection color so that the user knows which tab is selected...
330 int clip_y
= (H
>= 0) ? y() + H
: y() + h() + H
- SELECTION_BORDER
;
331 fl_push_clip(x(), clip_y
, w(), SELECTION_BORDER
);
332 draw_box(box(), x(), clip_y
, w(), SELECTION_BORDER
, selection_color());
335 if (v
) draw_child(*v
);
336 } else { // redraw the child
337 if (v
) update_child(*v
);
339 if (damage() & (FL_DAMAGE_SCROLL
|FL_DAMAGE_ALL
)) {
341 int selected
= tab_positions();
343 Fl_Widget
*const* a
= array();
344 for (i
=0; i
<selected
; i
++)
345 draw_tab(x()+tab_pos
[i
], x()+tab_pos
[i
+1],
346 tab_width
[i
], H
, a
[i
], LEFT
);
347 for (i
=nc
-1; i
> selected
; i
--)
348 draw_tab(x()+tab_pos
[i
], x()+tab_pos
[i
+1],
349 tab_width
[i
], H
, a
[i
], RIGHT
);
352 draw_tab(x()+tab_pos
[i
], x()+tab_pos
[i
+1],
353 tab_width
[i
], H
, a
[i
], SELECTED
);
358 void Fl_Tabs::draw_tab(int x1
, int x2
, int W
, int H
, Fl_Widget
* o
, int what
) {
359 int sel
= (what
== SELECTED
);
360 int dh
= Fl::box_dh(box());
361 int dy
= Fl::box_dy(box());
362 char prev_draw_shortcut
= fl_draw_shortcut
;
363 fl_draw_shortcut
= 1;
365 Fl_Boxtype bt
= (o
==push_
&&!sel
) ? fl_down(box()) : box();
367 // compute offsets to make selected tab look bigger
368 int yofs
= sel
? 0 : BORDER
;
370 if ((x2
< x1
+W
) && what
== RIGHT
) x1
= x2
- W
;
373 if (sel
) fl_push_clip(x1
, y(), x2
- x1
, H
+ dh
- dy
);
374 else fl_push_clip(x1
, y(), x2
- x1
, H
);
378 Fl_Color c
= sel
? selection_color() : o
->selection_color();
380 draw_box(bt
, x1
, y() + yofs
, W
, H
+ 10 - yofs
, c
);
382 // Save the previous label color
383 Fl_Color oc
= o
->labelcolor();
385 // Draw the label using the current color...
386 o
->labelcolor(sel
? labelcolor() : o
->labelcolor());
387 o
->draw_label(x1
, y() + yofs
, W
, H
- yofs
, FL_ALIGN_CENTER
);
389 // Restore the original label color...
392 if (Fl::focus() == this && o
->visible())
393 draw_focus(box(), x1
, y(), W
, H
);
399 if (sel
) fl_push_clip(x1
, y() + h() - H
- dy
, x2
- x1
, H
+ dy
);
400 else fl_push_clip(x1
, y() + h() - H
, x2
- x1
, H
);
404 Fl_Color c
= sel
? selection_color() : o
->selection_color();
406 draw_box(bt
, x1
, y() + h() - H
- 10, W
, H
+ 10 - yofs
, c
);
408 // Save the previous label color
409 Fl_Color oc
= o
->labelcolor();
411 // Draw the label using the current color...
412 o
->labelcolor(sel
? labelcolor() : o
->labelcolor());
413 o
->draw_label(x1
, y() + h() - H
, W
, H
- yofs
, FL_ALIGN_CENTER
);
415 // Restore the original label color...
418 if (Fl::focus() == this && o
->visible())
419 draw_focus(box(), x1
, y() + h() - H
, W
, H
);
423 fl_draw_shortcut
= prev_draw_shortcut
;
427 Creates a new Fl_Tabs widget using the given position, size,
428 and label string. The default boxtype is FL_THIN_UP_BOX.
430 Use add(Fl_Widget*) to add each child, which are usually
431 Fl_Group widgets. The children should be sized to stay
432 away from the top or bottom edge of the Fl_Tabs widget,
433 which is where the tabs will be drawn.
435 All children of Fl_Tabs should have the same size and exactly fit on top of
436 each other. They should only leave space above or below where that tabs will
437 go, but not on the sides. If the first child of Fl_Tabs is set to
438 "resizable()", the riders will not resize when the tabs are resized.
440 The destructor <I>also deletes all the children</I>. This
441 allows a whole tree to be deleted at once, without having to
442 keep a pointer to all the children in the user code. A kludge
443 has been done so the Fl_Tabs and all of its children
444 can be automatic (local) variables, but you must declare the
445 Fl_Tabs widget <I>first</I> so that it is destroyed last.
447 Fl_Tabs::Fl_Tabs(int X
,int Y
,int W
, int H
, const char *l
) :
457 Fl_Tabs::~Fl_Tabs() {
458 clear_tab_positions();
462 Returns the position and size available to be used by its children.
464 If there isn't any child yet the \p tabh parameter will be used to
465 calculate the return values. This assumes that the children's labelsize
466 is the same as the Fl_Tabs' labelsize and adds a small border.
468 If there are already children, the values of child(0) are returned, and
471 \note Children should always use the same positions and sizes.
473 \p tabh can be one of
474 \li 0: calculate label size, tabs on top
475 \li -1: calculate label size, tabs on bottom
476 \li > 0: use given \p tabh value, tabs on top (height = tabh)
477 \li < -1: use given \p tabh value, tabs on bottom (height = -tabh)
479 \param[in] tabh position and optional height of tabs (see above)
480 \param[out] rx,ry,rw,rh (x,y,w,h) of client area for children
484 void Fl_Tabs::client_area(int &rx
, int &ry
, int &rw
, int &rh
, int tabh
) {
486 if (children()) { // use existing values
493 } else { // calculate values
496 int label_height
= fl_height(labelfont(), labelsize()) + BORDER
*2;
498 if (tabh
== 0) // use default (at top)
499 y_offset
= label_height
;
500 else if (tabh
== -1) // use default (at bottom)
501 y_offset
= -label_height
;
503 y_offset
= tabh
; // user given value
508 if (y_offset
>= 0) { // labels at top
511 } else { // labels at bottom
518 void Fl_Tabs::clear_tab_positions() {
530 // End of "$Id: Fl_Tabs.cxx 8658 2011-05-12 15:53:59Z manolo $".