2 // "$Id: Fl_Group.cxx 8184 2011-01-04 18:28:01Z matt $"
4 // Group 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
28 // The Fl_Group is the only defined container type in FLTK.
30 // Fl_Window itself is a subclass of this, and most of the event
31 // handling is designed so windows themselves work correctly.
35 #include <FL/Fl_Group.H>
36 #include <FL/Fl_Window.H>
37 #include <FL/fl_draw.H>
40 Fl_Group
* Fl_Group::current_
;
42 // Hack: A single child is stored in the pointer to the array, while
43 // multiple children are stored in an allocated array:
46 Returns a pointer to the array of children. <I>This pointer is only
47 valid until the next time a child is added or removed.</I>
49 Fl_Widget
*const* Fl_Group::array() const {
50 return children_
<= 1 ? (Fl_Widget
**)(&array_
) : array_
;
54 Searches the child array for the widget and returns the index. Returns children()
55 if the widget is NULL or not found.
57 int Fl_Group::find(const Fl_Widget
* o
) const {
58 Fl_Widget
*const* a
= array();
59 int i
; for (i
=0; i
< children_
; i
++) if (*a
++ == o
) break;
63 // Metrowerks CodeWarrior and others can't export the static
64 // class member: current_, so these methods can't be inlined...
67 Sets the current group so you can build the widget
68 tree by just constructing the widgets.
70 begin() is automatically called by the constructor for Fl_Group (and thus for
71 Fl_Window as well). begin() <I>is exactly the same as</I> current(this).
72 <I>Don't forget to end() the group or window!</I>
74 void Fl_Group::begin() {current_
= this;}
77 <I>Exactly the same as</I> current(this->parent()). Any new widgets
78 added to the widget tree will be added to the parent of the group.
80 void Fl_Group::end() {current_
= parent();}
83 Returns the currently active group.
85 The Fl_Widget constructor automatically does current()->add(widget) if this
86 is not null. To prevent new widgets from being added to a group, call
89 Fl_Group
*Fl_Group::current() {return current_
;}
92 Sets the current group.
93 \see Fl_Group::current()
95 void Fl_Group::current(Fl_Group
*g
) {current_
= g
;}
97 extern Fl_Widget
* fl_oldfocus
; // set by Fl::focus
99 // For back-compatibility, we must adjust all events sent to child
100 // windows so they are relative to that window.
102 static int send(Fl_Widget
* o
, int event
) {
103 if (o
->type() < FL_WINDOW
) return o
->handle(event
);
106 case FL_DND_ENTER
: /* FALLTHROUGH */
108 // figure out correct type of event:
109 event
= (o
->contains(Fl::belowmouse())) ? FL_DND_DRAG
: FL_DND_ENTER
;
111 int save_x
= Fl::e_x
; Fl::e_x
-= o
->x();
112 int save_y
= Fl::e_y
; Fl::e_y
-= o
->y();
113 int ret
= o
->handle(event
);
118 case FL_ENTER
: /* FALLTHROUGH */
120 // Successful completion of FL_ENTER means the widget is now the
121 // belowmouse widget, but only call Fl::belowmouse if the child
122 // widget did not do so:
123 if (!o
->contains(Fl::belowmouse())) Fl::belowmouse(o
);
129 // translate the current keystroke into up/down/left/right for navigation:
130 #define ctrl(x) (x^0x40)
131 static int navkey() {
132 switch (Fl::event_key()) {
133 case 0: // not an FL_KEYBOARD/FL_SHORTCUT event
136 if (!Fl::event_state(FL_SHIFT
)) return FL_Right
;
150 int Fl_Group::handle(int event
) {
152 Fl_Widget
*const* a
= array();
161 if (savedfocus_
&& savedfocus_
->take_focus()) return 1;
164 for (i
= children(); i
--;) if ((*a
++)->take_focus()) return 1;
168 for (i
= children(); i
--;) if (a
[i
]->take_focus()) return 1;
174 savedfocus_
= fl_oldfocus
;
178 return navigation(navkey());
181 for (i
= children(); i
--;) {
183 if (o
->takesevents() && Fl::event_inside(o
) && send(o
,FL_SHORTCUT
))
186 for (i
= children(); i
--;) {
188 if (o
->takesevents() && !Fl::event_inside(o
) && send(o
,FL_SHORTCUT
))
191 if ((Fl::event_key() == FL_Enter
|| Fl::event_key() == FL_KP_Enter
)) return navigation(FL_Down
);
196 for (i
= children(); i
--;) {
198 if (o
->visible() && Fl::event_inside(o
)) {
199 if (o
->contains(Fl::belowmouse())) {
200 return send(o
,FL_MOVE
);
203 if (send(o
,FL_ENTER
)) return 1;
207 Fl::belowmouse(this);
212 for (i
= children(); i
--;) {
214 if (o
->takesevents() && Fl::event_inside(o
)) {
215 if (o
->contains(Fl::belowmouse())) {
216 return send(o
,FL_DND_DRAG
);
217 } else if (send(o
,FL_DND_ENTER
)) {
218 if (!o
->contains(Fl::belowmouse())) Fl::belowmouse(o
);
223 Fl::belowmouse(this);
227 for (i
= children(); i
--;) {
229 if (o
->takesevents() && Fl::event_inside(o
)) {
230 Fl_Widget_Tracker
wp(o
);
231 if (send(o
,FL_PUSH
)) {
232 if (Fl::pushed() && wp
.exists() && !o
->contains(Fl::pushed())) Fl::pushed(o
);
242 if (o
== this) return 0;
243 else if (o
) send(o
,event
);
245 for (i
= children(); i
--;) {
247 if (o
->takesevents() && Fl::event_inside(o
)) {
248 if (send(o
,event
)) return 1;
255 for (i
= children(); i
--;) {
257 if (o
->takesevents() && Fl::event_inside(o
) && send(o
,FL_MOUSEWHEEL
))
260 for (i
= children(); i
--;) {
262 if (o
->takesevents() && !Fl::event_inside(o
) && send(o
,FL_MOUSEWHEEL
))
269 for (i
= children(); i
--;) {
271 if (o
->active()) o
->handle(event
);
277 for (i
= children(); i
--;) {
279 if (event
== FL_HIDE
&& o
== Fl::focus()) {
280 // Give up input focus...
281 int old_event
= Fl::e_number
;
282 o
->handle(Fl::e_number
= FL_UNFOCUS
);
283 Fl::e_number
= old_event
;
286 if (o
->visible()) o
->handle(event
);
291 // For all other events, try to give to each child, starting at focus:
292 for (i
= 0; i
< children(); i
++)
293 if (Fl::focus_
== a
[i
]) break;
295 if (i
>= children()) i
= 0;
299 if (a
[j
]->takesevents()) if (send(a
[j
], event
)) return 1;
301 if (j
>= children()) j
= 0;
310 //void Fl_Group::focus(Fl_Widget *o) {Fl::focus(o); o->handle(FL_FOCUS);}
313 const char *nameof(Fl_Widget
*o
) {
314 if (!o
) return "NULL";
315 if (!o
->label()) return "<no label>";
320 // try to move the focus in response to a keystroke:
321 int Fl_Group::navigation(int key
) {
322 if (children() <= 1) return 0;
325 if (i
>= children_
) return 0;
326 if (array_
[i
]->contains(Fl::focus())) break;
328 Fl_Widget
*previous
= array_
[i
];
335 if (i
>= children_
) {
336 if (parent()) return 0;
344 if (parent()) return 0;
351 Fl_Widget
* o
= array_
[i
];
352 if (o
== previous
) return 0;
356 // for up/down, the widgets have to overlap horizontally:
357 if (o
->x() >= previous
->x()+previous
->w() ||
358 o
->x()+o
->w() <= previous
->x()) continue;
360 if (o
->take_focus()) return 1;
364 ////////////////////////////////////////////////////////////////
366 Fl_Group::Fl_Group(int X
,int Y
,int W
,int H
,const char *l
)
367 : Fl_Widget(X
,Y
,W
,H
,l
) {
373 sizes_
= 0; // this is allocated when first resize() is done
374 // Subclasses may want to construct child objects as part of their
375 // constructor, so make sure they are add()'d to this object.
376 // But you must end() the object!
381 Deletes all child widgets from memory recursively.
383 This method differs from the remove() method in that it
384 affects all child widgets and deletes them from memory.
386 void Fl_Group::clear() {
391 // we must change the Fl::pushed() widget, if it is one of
392 // the group's children. Otherwise fl_fix_focus() would send
393 // lots of events to children that are about to be deleted
396 Fl_Widget
*pushed
= Fl::pushed(); // save pushed() widget
397 if (contains(pushed
)) pushed
= this; // set it to be the group, if it's a child
398 Fl::pushed(this); // for fl_fix_focus etc.
400 // okay, now it is safe to destroy the children:
402 #define REVERSE_CHILDREN
403 #ifdef REVERSE_CHILDREN
404 // Reverse the order of the children. Doing this and deleting
405 // always the last child is much faster than the other way around.
408 Fl_Widget
**a
= (Fl_Widget
**)array();
409 for (int i
=0,j
=children_
-1; i
<children_
/2; i
++,j
--) {
415 #endif // REVERSE_CHILDREN
417 while (children_
) { // delete all children
418 int idx
= children_
-1; // last child's index
419 Fl_Widget
* w
= child(idx
); // last child widget
420 if (w
->parent()==this) { // should always be true
421 if (children_
>2) { // optimized removal
422 w
->parent_
= 0; // reset child's parent
423 children_
--; // update counter
424 } else { // slow removal
427 delete w
; // delete the child
428 } else { // should never happen
429 remove(idx
); // remove it anyway
433 if (pushed
!= this) Fl::pushed(pushed
); // reset pushed() widget
438 The destructor <I>also deletes all the children</I>. This allows a
439 whole tree to be deleted at once, without having to keep a pointer to
440 all the children in the user code.
442 It is allowed that the Fl_Group and all of its children are automatic
443 (local) variables, but you must declare the Fl_Group \e first, so that
444 it is destroyed last.
446 If you add static or automatic (local) variables to an Fl_Group, then it
447 is your responsibility to remove (or delete) all such static or automatic
448 child widgets \e \b before destroying the group - otherwise the child
449 widgets' destructors would be called twice!
451 Fl_Group::~Fl_Group() {
456 The widget is removed from its current group (if any) and then
457 inserted into this group. It is put at index n - or at the end,
458 if n >= children(). This can also be used to rearrange
459 the widgets inside a group.
461 void Fl_Group::insert(Fl_Widget
&o
, int index
) {
463 Fl_Group
* g
= o
.parent();
466 if (index
> n
) index
--;
467 if (index
== n
) return;
472 if (children_
== 0) { // use array pointer to point at single child
473 array_
= (Fl_Widget
**)&o
;
474 } else if (children_
== 1) { // go from 1 to 2 children
475 Fl_Widget
* t
= (Fl_Widget
*)array_
;
476 array_
= (Fl_Widget
**)malloc(2*sizeof(Fl_Widget
*));
477 if (index
) {array_
[0] = t
; array_
[1] = &o
;}
478 else {array_
[0] = &o
; array_
[1] = t
;}
480 if (!(children_
& (children_
-1))) // double number of children
481 array_
= (Fl_Widget
**)realloc((void*)array_
,
482 2*children_
*sizeof(Fl_Widget
*));
483 int j
; for (j
= children_
; j
> index
; j
--) array_
[j
] = array_
[j
-1];
491 The widget is removed from its current group (if any) and then added
492 to the end of this group.
494 void Fl_Group::add(Fl_Widget
&o
) {insert(o
, children_
);}
497 Removes the widget at \p index from the group but does not delete it.
499 This method does nothing if \p index is out of bounds.
501 This method differs from the clear() method in that it only affects
502 a single widget and does not delete it from memory.
506 void Fl_Group::remove(int index
) {
507 if (index
< 0 || index
>= children_
) return;
508 Fl_Widget
&o
= *child(index
);
509 if (&o
== savedfocus_
) savedfocus_
= 0;
510 if (o
.parent_
== this) { // this should always be true
514 // remove the widget from the group
517 if (children_
== 1) { // go from 2 to 1 child
518 Fl_Widget
*t
= array_
[!index
];
520 array_
= (Fl_Widget
**)t
;
521 } else if (children_
> 1) { // delete from array
522 for (; index
< children_
; index
++) array_
[index
] = array_
[index
+1];
528 Removes a widget from the group but does not delete it.
530 This method does nothing if the widget is not a child of the group.
532 This method differs from the clear() method in that it only affects
533 a single widget and does not delete it from memory.
535 \note If you have the child's index anyway, use remove(int index)
536 instead, because this doesn't need a child lookup in the group's
537 table of children. This can be much faster, if there are lots of
540 void Fl_Group::remove(Fl_Widget
&o
) {
541 if (!children_
) return;
543 if (i
< children_
) remove(i
);
546 ////////////////////////////////////////////////////////////////
548 // Rather lame kludge here, I need to detect windows and ignore the
549 // changes to X,Y, since all children are relative to X,Y. That
550 // is why I check type():
552 // sizes array stores the initial positions of widgets as
553 // left,right,top,bottom quads. The first quad is the group, the
554 // second is the resizable (clipped to the group), and the
555 // rest are the children. This is a convenient order for the
556 // algorithm. If you change this be sure to fix Fl_Tile which
557 // also uses this array!
560 Resets the internal array of widget sizes and positions.
562 The Fl_Group widget keeps track of the original widget sizes and
563 positions when resizing occurs so that if you resize a window back to its
564 original size the widgets will be in the correct places. If you rearrange
565 the widgets in your group, call this method to register the new arrangement
566 with the Fl_Group that contains them.
568 If you add or remove widgets, this will be done automatically.
570 \note The internal array of widget sizes and positions will be allocated and
571 filled when the next resize() occurs.
575 void Fl_Group::init_sizes() {
576 delete[] sizes_
; sizes_
= 0;
580 Returns the internal array of widget sizes and positions.
582 If the sizes() array does not exist, it will be allocated and filled
583 with the current widget sizes and positions.
585 \note You should never need to use this method directly, unless you have
586 special needs to rearrange the children of a Fl_Group. Fl_Tile uses
587 this to rearrange its widget positions.
591 \todo Should the internal representation of the sizes() array be documented?
593 int* Fl_Group::sizes() {
595 int* p
= sizes_
= new int[4*(children_
+2)];
596 // first thing in sizes array is the group's size:
597 if (type() < FL_WINDOW
) {p
[0] = x(); p
[2] = y();} else {p
[0] = p
[2] = 0;}
598 p
[1] = p
[0]+w(); p
[3] = p
[2]+h();
599 // next is the resizable's size:
600 p
[4] = p
[0]; // init to the group's size
604 Fl_Widget
* r
= resizable();
605 if (r
&& r
!= this) { // then clip the resizable to it
607 t
= r
->x(); if (t
> p
[0]) p
[4] = t
;
608 t
+=r
->w(); if (t
< p
[1]) p
[5] = t
;
609 t
= r
->y(); if (t
> p
[2]) p
[6] = t
;
610 t
+=r
->h(); if (t
< p
[3]) p
[7] = t
;
612 // next is all the children's sizes:
614 Fl_Widget
*const* a
= array();
615 for (int i
=children_
; i
--;) {
618 *p
++ = o
->x()+o
->w();
620 *p
++ = o
->y()+o
->h();
627 Resizes the Fl_Group widget and all of its children.
629 The Fl_Group widget first resizes itself, and then it moves and resizes
630 all its children according to the rules documented for
631 Fl_Group::resizable(Fl_Widget*)
633 \sa Fl_Group::resizable(Fl_Widget*)
634 \sa Fl_Group::resizable()
635 \sa Fl_Widget::resize(int,int,int,int)
637 void Fl_Group::resize(int X
, int Y
, int W
, int H
) {
644 int *p
= sizes(); // save initial sizes and positions
646 Fl_Widget::resize(X
,Y
,W
,H
); // make new xywh values visible for children
648 if (!resizable() || (dw
==0 && dh
==0) ) {
650 if (type() < FL_WINDOW
) {
651 Fl_Widget
*const* a
= array();
652 for (int i
=children_
; i
--;) {
654 o
->resize(o
->x()+dx
, o
->y()+dy
, o
->w(), o
->h());
658 } else if (children_
) {
660 // get changes in size/position from the initial size:
662 dw
= W
- (p
[1]-p
[0]);
664 dh
= H
- (p
[3]-p
[2]);
665 if (type() >= FL_WINDOW
) dx
= dy
= 0;
668 // get initial size of resizable():
674 Fl_Widget
*const* a
= array();
675 for (int i
=children_
; i
--;) {
679 if (XX
>= IR
) XX
+= dw
;
680 else if (XX
> IX
) XX
= IX
+((XX
-IX
)*(IR
+dw
-IX
)+(IR
-IX
)/2)/(IR
-IX
);
682 if (R
>= IR
) R
+= dw
;
683 else if (R
> IX
) R
= IX
+((R
-IX
)*(IR
+dw
-IX
)+(IR
-IX
)/2)/(IR
-IX
);
686 if (YY
>= IB
) YY
+= dh
;
687 else if (YY
> IY
) YY
= IY
+((YY
-IY
)*(IB
+dh
-IY
)+(IB
-IY
)/2)/(IB
-IY
);
689 if (B
>= IB
) B
+= dh
;
690 else if (B
> IY
) B
= IY
+((B
-IY
)*(IB
+dh
-IY
)+(IB
-IY
)/2)/(IB
-IY
);
691 #else // much simpler code from Francois Ostiguy:
693 if (XX
>= IR
) XX
+= dw
;
694 else if (XX
> IX
) XX
+= dw
* (XX
-IX
)/(IR
-IX
);
696 if (R
>= IR
) R
+= dw
;
697 else if (R
> IX
) R
= R
+ dw
* (R
-IX
)/(IR
-IX
);
700 if (YY
>= IB
) YY
+= dh
;
701 else if (YY
> IY
) YY
= YY
+ dh
*(YY
-IY
)/(IB
-IY
);
703 if (B
>= IB
) B
+= dh
;
704 else if (B
> IY
) B
= B
+ dh
*(B
-IY
)/(IB
-IY
);
706 o
->resize(XX
+dx
, YY
+dy
, R
-XX
, B
-YY
);
712 Draws all children of the group.
714 This is useful, if you derived a widget from Fl_Group and want to draw a special
715 border or background. You can call draw_children() from the derived draw() method
716 after drawing the box, border, or background.
718 void Fl_Group::draw_children() {
719 Fl_Widget
*const* a
= array();
721 if (clip_children()) {
722 fl_push_clip(x() + Fl::box_dx(box()),
723 y() + Fl::box_dy(box()),
724 w() - Fl::box_dw(box()),
725 h() - Fl::box_dh(box()));
728 if (damage() & ~FL_DAMAGE_CHILD
) { // redraw the entire thing:
729 // if ( damage() & FL_DAMAGE_ALL ) {
730 for (int i
=children_
; i
--;) {
731 Fl_Widget
& o
= **a
++;
733 draw_outside_label(o
);
735 } else { // only redraw the children that need it:
736 for (int i
=children_
; i
--;) update_child(**a
++);
739 if (clip_children()) fl_pop_clip();
742 void Fl_Group::draw() {
743 if (damage() & ~FL_DAMAGE_CHILD
) { // redraw the entire thing:
751 Draws a child only if it needs it.
753 This draws a child widget, if it is not clipped \em and if any damage() bits
754 are set. The damage bits are cleared after drawing.
756 \sa Fl_Group::draw_child(Fl_Widget& widget) const
758 void Fl_Group::update_child(Fl_Widget
& widget
) const {
759 if (widget
.damage() && widget
.visible() && widget
.type() < FL_WINDOW
&&
760 fl_not_clipped(widget
.x(), widget
.y(), widget
.w(), widget
.h())) {
762 widget
.clear_damage();
767 Forces a child to redraw.
769 This draws a child widget, if it is not clipped.
770 The damage bits are cleared after drawing.
772 void Fl_Group::draw_child(Fl_Widget
& widget
) const {
773 if (widget
.visible() && widget
.type() < FL_WINDOW
&&
774 fl_not_clipped(widget
.x(), widget
.y(), widget
.w(), widget
.h())) {
775 widget
.clear_damage(FL_DAMAGE_ALL
);
777 widget
.clear_damage();
781 extern char fl_draw_shortcut
;
783 /** Parents normally call this to draw outside labels of child widgets. */
784 void Fl_Group::draw_outside_label(const Fl_Widget
& widget
) const {
785 if (!widget
.visible()) return;
786 // skip any labels that are inside the widget:
787 if (!(widget
.align()&15) || (widget
.align() & FL_ALIGN_INSIDE
)) return;
788 // invent a box that is outside the widget:
789 Fl_Align a
= widget
.align();
795 if (const_cast<Fl_Group
*>(this)->as_window()) {
800 if ( (a
& 0x0f) == FL_ALIGN_LEFT_TOP
) {
801 a
= (a
&~0x0f ) | FL_ALIGN_TOP_RIGHT
;
804 } else if ( (a
& 0x0f) == FL_ALIGN_LEFT_BOTTOM
) {
805 a
= (a
&~0x0f ) | FL_ALIGN_BOTTOM_RIGHT
;
808 } else if ( (a
& 0x0f) == FL_ALIGN_RIGHT_TOP
) {
809 a
= (a
&~0x0f ) | FL_ALIGN_TOP_LEFT
;
812 } else if ( (a
& 0x0f) == FL_ALIGN_RIGHT_BOTTOM
) {
813 a
= (a
&~0x0f ) | FL_ALIGN_BOTTOM_LEFT
;
816 } else if (a
& FL_ALIGN_TOP
) {
817 a
^= (FL_ALIGN_BOTTOM
|FL_ALIGN_TOP
);
820 } else if (a
& FL_ALIGN_BOTTOM
) {
821 a
^= (FL_ALIGN_BOTTOM
|FL_ALIGN_TOP
);
824 } else if (a
& FL_ALIGN_LEFT
) {
825 a
^= (FL_ALIGN_LEFT
|FL_ALIGN_RIGHT
);
828 } else if (a
& FL_ALIGN_RIGHT
) {
829 a
^= (FL_ALIGN_LEFT
|FL_ALIGN_RIGHT
);
833 widget
.draw_label(X
,Y
,W
,H
,(Fl_Align
)a
);
837 // End of "$Id: Fl_Group.cxx 8184 2011-01-04 18:28:01Z matt $".