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