1 //
2 // "$Id: Fl_Group.cxx 8184 2011-01-04 18:28:01Z matt $"
3 //
4 // Group widget for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2010 by Bill Spitzak and others.
7 //
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.
12 //
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.
17 //
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
21 // USA.
22 //
23 // Please report all bugs and problems on the following page:
24 //
25 // http://www.fltk.org/str.php
26 //
27
28 // The Fl_Group is the only defined container type in FLTK.
29
30 // Fl_Window itself is a subclass of this, and most of the event
31 // handling is designed so windows themselves work correctly.
32
33 #include <stdio.h>
34 #include <FL/Fl.H>
35 #include <FL/Fl_Group.H>
36 #include <FL/Fl_Window.H>
37 #include <FL/fl_draw.H>
38 #include <stdlib.h>
39
40 Fl_Group* Fl_Group::current_;
41
42 // Hack: A single child is stored in the pointer to the array, while
43 // multiple children are stored in an allocated array:
44
45 /**
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>
48 */
array() const49 Fl_Widget*const* Fl_Group::array() const {
50 return children_ <= 1 ? (Fl_Widget**)(&array_) : array_;
51 }
52
53 /**
54 Searches the child array for the widget and returns the index. Returns children()
55 if the widget is NULL or not found.
56 */
find(const Fl_Widget * o) const57 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;
60 return i;
61 }
62
63 // Metrowerks CodeWarrior and others can't export the static
64 // class member: current_, so these methods can't be inlined...
65
66 /**
67 Sets the current group so you can build the widget
68 tree by just constructing the widgets.
69
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>
73 */
begin()74 void Fl_Group::begin() {current_ = this;}
75
76 /**
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.
79 */
end()80 void Fl_Group::end() {current_ = parent();}
81
82 /**
83 Returns the currently active group.
84
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
87 Fl_Group::current(0).
88 */
current()89 Fl_Group *Fl_Group::current() {return current_;}
90
91 /**
92 Sets the current group.
93 \see Fl_Group::current()
94 */
current(Fl_Group * g)95 void Fl_Group::current(Fl_Group *g) {current_ = g;}
96
97 extern Fl_Widget* fl_oldfocus; // set by Fl::focus
98
99 // For back-compatibility, we must adjust all events sent to child
100 // windows so they are relative to that window.
101
send(Fl_Widget * o,int event)102 static int send(Fl_Widget* o, int event) {
103 if (o->type() < FL_WINDOW) return o->handle(event);
104 switch ( event )
105 {
106 case FL_DND_ENTER: /* FALLTHROUGH */
107 case FL_DND_DRAG:
108 // figure out correct type of event:
109 event = (o->contains(Fl::belowmouse())) ? FL_DND_DRAG : FL_DND_ENTER;
110 }
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);
114 Fl::e_y = save_y;
115 Fl::e_x = save_x;
116 switch ( event )
117 {
118 case FL_ENTER: /* FALLTHROUGH */
119 case FL_DND_ENTER:
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);
124 break;
125 }
126 return ret;
127 }
128
129 // translate the current keystroke into up/down/left/right for navigation:
130 #define ctrl(x) (x^0x40)
navkey()131 static int navkey() {
132 switch (Fl::event_key()) {
133 case 0: // not an FL_KEYBOARD/FL_SHORTCUT event
134 break;
135 case FL_Tab:
136 if (!Fl::event_state(FL_SHIFT)) return FL_Right;
137 return FL_Left;
138 case FL_Right:
139 return FL_Right;
140 case FL_Left:
141 return FL_Left;
142 case FL_Up:
143 return FL_Up;
144 case FL_Down:
145 return FL_Down;
146 }
147 return 0;
148 }
149
handle(int event)150 int Fl_Group::handle(int event) {
151
152 Fl_Widget*const* a = array();
153 int i;
154 Fl_Widget* o;
155
156 switch (event) {
157
158 case FL_FOCUS:
159 switch (navkey()) {
160 default:
161 if (savedfocus_ && savedfocus_->take_focus()) return 1;
162 case FL_Right:
163 case FL_Down:
164 for (i = children(); i--;) if ((*a++)->take_focus()) return 1;
165 break;
166 case FL_Left:
167 case FL_Up:
168 for (i = children(); i--;) if (a[i]->take_focus()) return 1;
169 break;
170 }
171 return 0;
172
173 case FL_UNFOCUS:
174 savedfocus_ = fl_oldfocus;
175 return 0;
176
177 case FL_KEYBOARD:
178 return navigation(navkey());
179
180 case FL_SHORTCUT:
181 for (i = children(); i--;) {
182 o = a[i];
183 if (o->takesevents() && Fl::event_inside(o) && send(o,FL_SHORTCUT))
184 return 1;
185 }
186 for (i = children(); i--;) {
187 o = a[i];
188 if (o->takesevents() && !Fl::event_inside(o) && send(o,FL_SHORTCUT))
189 return 1;
190 }
191 if ((Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter)) return navigation(FL_Down);
192 return 0;
193
194 case FL_ENTER:
195 case FL_MOVE:
196 for (i = children(); i--;) {
197 o = a[i];
198 if (o->visible() && Fl::event_inside(o)) {
199 if (o->contains(Fl::belowmouse())) {
200 return send(o,FL_MOVE);
201 } else {
202 Fl::belowmouse(o);
203 if (send(o,FL_ENTER)) return 1;
204 }
205 }
206 }
207 Fl::belowmouse(this);
208 return 1;
209
210 case FL_DND_ENTER:
211 case FL_DND_DRAG:
212 for (i = children(); i--;) {
213 o = a[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);
219 return 1;
220 }
221 }
222 }
223 Fl::belowmouse(this);
224 return 0;
225
226 case FL_PUSH:
227 for (i = children(); i--;) {
228 o = a[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);
233 return 1;
234 }
235 }
236 }
237 return 0;
238
239 case FL_RELEASE:
240 case FL_DRAG:
241 o = Fl::pushed();
242 if (o == this) return 0;
243 else if (o) send(o,event);
244 else {
245 for (i = children(); i--;) {
246 o = a[i];
247 if (o->takesevents() && Fl::event_inside(o)) {
248 if (send(o,event)) return 1;
249 }
250 }
251 }
252 return 0;
253
254 case FL_MOUSEWHEEL:
255 for (i = children(); i--;) {
256 o = a[i];
257 if (o->takesevents() && Fl::event_inside(o) && send(o,FL_MOUSEWHEEL))
258 return 1;
259 }
260 for (i = children(); i--;) {
261 o = a[i];
262 if (o->takesevents() && !Fl::event_inside(o) && send(o,FL_MOUSEWHEEL))
263 return 1;
264 }
265 return 0;
266
267 case FL_DEACTIVATE:
268 case FL_ACTIVATE:
269 for (i = children(); i--;) {
270 o = *a++;
271 if (o->active()) o->handle(event);
272 }
273 return 1;
274
275 case FL_SHOW:
276 case FL_HIDE:
277 for (i = children(); i--;) {
278 o = *a++;
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;
284 Fl::focus(0);
285 }
286 if (o->visible()) o->handle(event);
287 }
288 return 1;
289
290 default:
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;
294
295 if (i >= children()) i = 0;
296
297 if (children()) {
298 for (int j = i;;) {
299 if (a[j]->takesevents()) if (send(a[j], event)) return 1;
300 j++;
301 if (j >= children()) j = 0;
302 if (j == i) break;
303 }
304 }
305
306 return 0;
307 }
308 }
309
310 //void Fl_Group::focus(Fl_Widget *o) {Fl::focus(o); o->handle(FL_FOCUS);}
311
312 #if 0
313 const char *nameof(Fl_Widget *o) {
314 if (!o) return "NULL";
315 if (!o->label()) return "<no label>";
316 return o->label();
317 }
318 #endif
319
320 // try to move the focus in response to a keystroke:
navigation(int key)321 int Fl_Group::navigation(int key) {
322 if (children() <= 1) return 0;
323 int i;
324 for (i = 0; ; i++) {
325 if (i >= children_) return 0;
326 if (array_[i]->contains(Fl::focus())) break;
327 }
328 Fl_Widget *previous = array_[i];
329
330 for (;;) {
331 switch (key) {
332 case FL_Right:
333 case FL_Down:
334 i++;
335 if (i >= children_) {
336 if (parent()) return 0;
337 i = 0;
338 }
339 break;
340 case FL_Left:
341 case FL_Up:
342 if (i) i--;
343 else {
344 if (parent()) return 0;
345 i = children_-1;
346 }
347 break;
348 default:
349 return 0;
350 }
351 Fl_Widget* o = array_[i];
352 if (o == previous) return 0;
353 switch (key) {
354 case FL_Down:
355 case FL_Up:
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;
359 }
360 if (o->take_focus()) return 1;
361 }
362 }
363
364 ////////////////////////////////////////////////////////////////
365
Fl_Group(int X,int Y,int W,int H,const char * l)366 Fl_Group::Fl_Group(int X,int Y,int W,int H,const char *l)
367 : Fl_Widget(X,Y,W,H,l) {
368 align(FL_ALIGN_TOP);
369 children_ = 0;
370 array_ = 0;
371 savedfocus_ = 0;
372 resizable_ = this;
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!
377 begin();
378 }
379
380 /**
381 Deletes all child widgets from memory recursively.
382
383 This method differs from the remove() method in that it
384 affects all child widgets and deletes them from memory.
385 */
clear()386 void Fl_Group::clear() {
387 savedfocus_ = 0;
388 resizable_ = this;
389 init_sizes();
390
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
394 // anyway.
395
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.
399
400 // okay, now it is safe to destroy the children:
401
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.
406 if (children_ > 1) {
407 Fl_Widget *temp;
408 Fl_Widget **a = (Fl_Widget**)array();
409 for (int i=0,j=children_-1; i<children_/2; i++,j--) {
410 temp = a[i];
411 a[i] = a[j];
412 a[j] = temp;
413 }
414 }
415 #endif // REVERSE_CHILDREN
416
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
425 remove(idx);
426 }
427 delete w; // delete the child
428 } else { // should never happen
429 remove(idx); // remove it anyway
430 }
431 }
432
433 if (pushed != this) Fl::pushed(pushed); // reset pushed() widget
434
435 }
436
437 /**
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.
441
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.
445
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!
450 */
~Fl_Group()451 Fl_Group::~Fl_Group() {
452 clear();
453 }
454
455 /**
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.
460 */
insert(Fl_Widget & o,int index)461 void Fl_Group::insert(Fl_Widget &o, int index) {
462 if (o.parent()) {
463 Fl_Group* g = o.parent();
464 int n = g->find(o);
465 if (g == this) {
466 if (index > n) index--;
467 if (index == n) return;
468 }
469 g->remove(n);
470 }
471 o.parent_ = this;
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;}
479 } else {
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];
484 array_[j] = &o;
485 }
486 children_++;
487 init_sizes();
488 }
489
490 /**
491 The widget is removed from its current group (if any) and then added
492 to the end of this group.
493 */
add(Fl_Widget & o)494 void Fl_Group::add(Fl_Widget &o) {insert(o, children_);}
495
496 /**
497 Removes the widget at \p index from the group but does not delete it.
498
499 This method does nothing if \p index is out of bounds.
500
501 This method differs from the clear() method in that it only affects
502 a single widget and does not delete it from memory.
503
504 \since FLTK 1.3.0
505 */
remove(int index)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
511 o.parent_ = 0;
512 }
513
514 // remove the widget from the group
515
516 children_--;
517 if (children_ == 1) { // go from 2 to 1 child
518 Fl_Widget *t = array_[!index];
519 free((void*)array_);
520 array_ = (Fl_Widget**)t;
521 } else if (children_ > 1) { // delete from array
522 for (; index < children_; index++) array_[index] = array_[index+1];
523 }
524 init_sizes();
525 }
526
527 /**
528 Removes a widget from the group but does not delete it.
529
530 This method does nothing if the widget is not a child of the group.
531
532 This method differs from the clear() method in that it only affects
533 a single widget and does not delete it from memory.
534
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
538 children.
539 */
remove(Fl_Widget & o)540 void Fl_Group::remove(Fl_Widget &o) {
541 if (!children_) return;
542 int i = find(o);
543 if (i < children_) remove(i);
544 }
545
546 ////////////////////////////////////////////////////////////////
547
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():
551
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!
558
559 /**
560 Resets the internal array of widget sizes and positions.
561
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.
567
568 If you add or remove widgets, this will be done automatically.
569
570 \note The internal array of widget sizes and positions will be allocated and
571 filled when the next resize() occurs.
572
573 \sa sizes()
574 */
init_sizes()575 void Fl_Group::init_sizes() {
576 delete[] sizes_; sizes_ = 0;
577 }
578
579 /**
580 Returns the internal array of widget sizes and positions.
581
582 If the sizes() array does not exist, it will be allocated and filled
583 with the current widget sizes and positions.
584
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.
588
589 \sa init_sizes()
590
591 \todo Should the internal representation of the sizes() array be documented?
592 */
sizes()593 int* Fl_Group::sizes() {
594 if (!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
601 p[5] = p[1];
602 p[6] = p[2];
603 p[7] = p[3];
604 Fl_Widget* r = resizable();
605 if (r && r != this) { // then clip the resizable to it
606 int t;
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;
611 }
612 // next is all the children's sizes:
613 p += 8;
614 Fl_Widget*const* a = array();
615 for (int i=children_; i--;) {
616 Fl_Widget* o = *a++;
617 *p++ = o->x();
618 *p++ = o->x()+o->w();
619 *p++ = o->y();
620 *p++ = o->y()+o->h();
621 }
622 }
623 return sizes_;
624 }
625
626 /**
627 Resizes the Fl_Group widget and all of its children.
628
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*)
632
633 \sa Fl_Group::resizable(Fl_Widget*)
634 \sa Fl_Group::resizable()
635 \sa Fl_Widget::resize(int,int,int,int)
636 */
resize(int X,int Y,int W,int H)637 void Fl_Group::resize(int X, int Y, int W, int H) {
638
639 int dx = X-x();
640 int dy = Y-y();
641 int dw = W-w();
642 int dh = H-h();
643
644 int *p = sizes(); // save initial sizes and positions
645
646 Fl_Widget::resize(X,Y,W,H); // make new xywh values visible for children
647
648 if (!resizable() || (dw==0 && dh==0) ) {
649
650 if (type() < FL_WINDOW) {
651 Fl_Widget*const* a = array();
652 for (int i=children_; i--;) {
653 Fl_Widget* o = *a++;
654 o->resize(o->x()+dx, o->y()+dy, o->w(), o->h());
655 }
656 }
657
658 } else if (children_) {
659
660 // get changes in size/position from the initial size:
661 dx = X - p[0];
662 dw = W - (p[1]-p[0]);
663 dy = Y - p[2];
664 dh = H - (p[3]-p[2]);
665 if (type() >= FL_WINDOW) dx = dy = 0;
666 p += 4;
667
668 // get initial size of resizable():
669 int IX = *p++;
670 int IR = *p++;
671 int IY = *p++;
672 int IB = *p++;
673
674 Fl_Widget*const* a = array();
675 for (int i=children_; i--;) {
676 Fl_Widget* o = *a++;
677 #if 1
678 int XX = *p++;
679 if (XX >= IR) XX += dw;
680 else if (XX > IX) XX = IX+((XX-IX)*(IR+dw-IX)+(IR-IX)/2)/(IR-IX);
681 int R = *p++;
682 if (R >= IR) R += dw;
683 else if (R > IX) R = IX+((R-IX)*(IR+dw-IX)+(IR-IX)/2)/(IR-IX);
684
685 int YY = *p++;
686 if (YY >= IB) YY += dh;
687 else if (YY > IY) YY = IY+((YY-IY)*(IB+dh-IY)+(IB-IY)/2)/(IB-IY);
688 int B = *p++;
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:
692 int XX = *p++;
693 if (XX >= IR) XX += dw;
694 else if (XX > IX) XX += dw * (XX-IX)/(IR-IX);
695 int R = *p++;
696 if (R >= IR) R += dw;
697 else if (R > IX) R = R + dw * (R-IX)/(IR-IX);
698
699 int YY = *p++;
700 if (YY >= IB) YY += dh;
701 else if (YY > IY) YY = YY + dh*(YY-IY)/(IB-IY);
702 int B = *p++;
703 if (B >= IB) B += dh;
704 else if (B > IY) B = B + dh*(B-IY)/(IB-IY);
705 #endif
706 o->resize(XX+dx, YY+dy, R-XX, B-YY);
707 }
708 }
709 }
710
711 /**
712 Draws all children of the group.
713
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.
717 */
draw_children()718 void Fl_Group::draw_children() {
719 Fl_Widget*const* a = array();
720
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()));
726 }
727
728 if (damage() & ~FL_DAMAGE_CHILD) { // redraw the entire thing:
729 for (int i=children_; i--;) {
730 Fl_Widget& o = **a++;
731 draw_child(o);
732 draw_outside_label(o);
733 }
734 } else { // only redraw the children that need it:
735 for (int i=children_; i--;) update_child(**a++);
736 }
737
738 if (clip_children()) fl_pop_clip();
739 }
740
draw()741 void Fl_Group::draw() {
742 if (damage() & ~FL_DAMAGE_CHILD) { // redraw the entire thing:
743 draw_box();
744 draw_label();
745 }
746 draw_children();
747 }
748
749 /**
750 Draws a child only if it needs it.
751
752 This draws a child widget, if it is not clipped \em and if any damage() bits
753 are set. The damage bits are cleared after drawing.
754
755 \sa Fl_Group::draw_child(Fl_Widget& widget) const
756 */
update_child(Fl_Widget & widget) const757 void Fl_Group::update_child(Fl_Widget& widget) const {
758 if (widget.damage() && widget.visible() && widget.type() < FL_WINDOW &&
759 fl_not_clipped(widget.x(), widget.y(), widget.w(), widget.h())) {
760 widget.draw();
761 widget.clear_damage();
762 }
763 }
764
765 /**
766 Forces a child to redraw.
767
768 This draws a child widget, if it is not clipped.
769 The damage bits are cleared after drawing.
770 */
draw_child(Fl_Widget & widget) const771 void Fl_Group::draw_child(Fl_Widget& widget) const {
772 if (widget.visible() && widget.type() < FL_WINDOW &&
773 fl_not_clipped(widget.x(), widget.y(), widget.w(), widget.h())) {
774 widget.clear_damage(FL_DAMAGE_ALL);
775 widget.draw();
776 widget.clear_damage();
777 }
778 }
779
780 extern char fl_draw_shortcut;
781
782 /** Parents normally call this to draw outside labels of child widgets. */
draw_outside_label(const Fl_Widget & widget) const783 void Fl_Group::draw_outside_label(const Fl_Widget& widget) const {
784 if (!widget.visible()) return;
785 // skip any labels that are inside the widget:
786 if (!(widget.align()&15) || (widget.align() & FL_ALIGN_INSIDE)) return;
787 // invent a box that is outside the widget:
788 Fl_Align a = widget.align();
789 int X = widget.x();
790 int Y = widget.y();
791 int W = widget.w();
792 int H = widget.h();
793 int wx, wy;
794 if (const_cast<Fl_Group*>(this)->as_window()) {
795 wx = wy = 0;
796 } else {
797 wx = x(); wy = y();
798 }
799 if ( (a & 0x0f) == FL_ALIGN_LEFT_TOP ) {
800 a = (a &~0x0f ) | FL_ALIGN_TOP_RIGHT;
801 X = wx;
802 W = widget.x()-X-3;
803 } else if ( (a & 0x0f) == FL_ALIGN_LEFT_BOTTOM ) {
804 a = (a &~0x0f ) | FL_ALIGN_BOTTOM_RIGHT;
805 X = wx;
806 W = widget.x()-X-3;
807 } else if ( (a & 0x0f) == FL_ALIGN_RIGHT_TOP ) {
808 a = (a &~0x0f ) | FL_ALIGN_TOP_LEFT;
809 X = X+W+3;
810 W = wx+this->w()-X;
811 } else if ( (a & 0x0f) == FL_ALIGN_RIGHT_BOTTOM ) {
812 a = (a &~0x0f ) | FL_ALIGN_BOTTOM_LEFT;
813 X = X+W+3;
814 W = wx+this->w()-X;
815 } else if (a & FL_ALIGN_TOP) {
816 a ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP);
817 Y = wy;
818 H = widget.y()-Y;
819 } else if (a & FL_ALIGN_BOTTOM) {
820 a ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP);
821 Y = Y+H;
822 H = wy+h()-Y;
823 } else if (a & FL_ALIGN_LEFT) {
824 a ^= (FL_ALIGN_LEFT|FL_ALIGN_RIGHT);
825 X = wx;
826 W = widget.x()-X-3;
827 } else if (a & FL_ALIGN_RIGHT) {
828 a ^= (FL_ALIGN_LEFT|FL_ALIGN_RIGHT);
829 X = X+W+3;
830 W = wx+this->w()-X;
831 }
832 widget.draw_label(X,Y,W,H,(Fl_Align)a);
833 }
834
835 //
836 // End of "$Id: Fl_Group.cxx 8184 2011-01-04 18:28:01Z matt $".
837 //
838