1 //
2 // "$Id: Fl_Group.cxx 900 2009-08-13 20:00:45Z larry $"
3 //
4 // Group widget for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2005 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 *
41 Fl_Group::current_;
42
43 // Hack: A single child is stored in the pointer to the array, while
44 // multiple children are stored in an allocated array:
45 Fl_Widget *const *
array() const46 Fl_Group::array () const const
47 {
48 return children_ <= 1 ? (Fl_Widget **) (&array_) : array_;
49 }
50
51 int
find(const Fl_Widget * o) const52 Fl_Group::find (const Fl_Widget * o) const const
53 {
54 Fl_Widget *const *a = array ();
55
56 int i;
57
58 for (i = 0; i < children_; i++)
59 if (*a++ == o)
60 break;
61 return i;
62 }
63
64 // Metrowerks CodeWarrior and others can't export the static
65 // class member: current_, so these methods can't be inlined...
66 void
begin()67 Fl_Group::begin ()
68 {
69 current_ = this;
70 }
71
72 void
end()73 Fl_Group::end ()
74 {
75 current_ = (Fl_Group *) parent ();
76 }
77
78 Fl_Group *
current()79 Fl_Group::current ()
80 {
81 return current_;
82 }
83
84 void
current(Fl_Group * g)85 Fl_Group::current (Fl_Group * g)
86 {
87 current_ = g;
88 }
89
90 extern Fl_Widget *fl_oldfocus; // set by Fl::focus
91
92 // For back-compatability, we must adjust all events sent to child
93 // windows so they are relative to that window.
94
95 static int
send(Fl_Widget * o,int event)96 send (Fl_Widget * o, int event)
97 {
98 if (o->type () < FL_WINDOW)
99 return o->handle (event);
100 switch (event) {
101 case FL_DND_ENTER:
102 case FL_DND_DRAG:
103 // figure out correct type of event:
104 event = (o->contains (Fl::belowmouse ()))? FL_DND_DRAG : FL_DND_ENTER;
105 }
106 int save_x = Fl::e_x;
107
108 Fl::e_x -= o->x ();
109 int save_y = Fl::e_y;
110
111 Fl::e_y -= o->y ();
112 int ret = o->handle (event);
113
114 Fl::e_y = save_y;
115 Fl::e_x = save_x;
116 switch (event) {
117 case FL_ENTER:
118 case FL_DND_ENTER:
119 // Successful completion of FL_ENTER means the widget is now the
120 // belowmouse widget, but only call Fl::belowmouse if the child
121 // widget did not do so:
122 if (!o->contains (Fl::belowmouse ()))
123 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)
131 static int
navkey()132 navkey ()
133 {
134 switch (Fl::event_key ()) {
135 case 0: // not an FL_KEYBOARD/FL_SHORTCUT event
136 break;
137 case FL_Tab:
138 if (!Fl::event_state (FL_SHIFT))
139 return FL_Right;
140 case 0xfe20: // XK_ISO_Left_Tab
141 return FL_Left;
142 case FL_Right:
143 return FL_Right;
144 case FL_Left:
145 return FL_Left;
146 case FL_Up:
147 return FL_Up;
148 case FL_Down:
149 return FL_Down;
150 }
151 return 0;
152 }
153
154 int
handle(int event)155 Fl_Group::handle (int event)
156 {
157
158 Fl_Widget *const *a = array ();
159
160 int i;
161
162 Fl_Widget *o;
163
164 switch (event) {
165
166 case FL_FOCUS:
167 switch (navkey ()) {
168 default:
169 if (savedfocus_ && savedfocus_->take_focus ())
170 return 1;
171 case FL_Right:
172 case FL_Down:
173 for (i = children (); i--;)
174 if ((*a++)->take_focus ())
175 return 1;
176 break;
177 case FL_Left:
178 case FL_Up:
179 for (i = children (); i--;)
180 if (a[i]->take_focus ())
181 return 1;
182 break;
183 }
184 return 0;
185
186 case FL_UNFOCUS:
187 savedfocus_ = fl_oldfocus;
188 return 0;
189
190 case FL_KEYBOARD:
191 return navigation (navkey ());
192
193 case FL_SHORTCUT:
194 for (i = children (); i--;) {
195 o = a[i];
196 if (o->takesevents () && Fl::event_inside (o) && send (o, FL_SHORTCUT))
197 return 1;
198 }
199 for (i = children (); i--;) {
200 o = a[i];
201 if (o->takesevents () && !Fl::event_inside (o) && send (o, FL_SHORTCUT))
202 return 1;
203 }
204 if ((Fl::event_key () == FL_Enter || Fl::event_key () == FL_KP_Enter))
205 return navigation (FL_Down);
206 return 0;
207
208 case FL_ENTER:
209 case FL_MOVE:
210 for (i = children (); i--;) {
211 o = a[i];
212 if (o->visible () && Fl::event_inside (o)) {
213 if (o->contains (Fl::belowmouse ())) {
214 return send (o, FL_MOVE);
215 } else {
216 Fl::belowmouse (o);
217 if (send (o, FL_ENTER))
218 return 1;
219 }
220 }
221 }
222 Fl::belowmouse (this);
223 return 1;
224
225 case FL_DND_ENTER:
226 case FL_DND_DRAG:
227 for (i = children (); i--;) {
228 o = a[i];
229 if (o->takesevents () && Fl::event_inside (o)) {
230 if (o->contains (Fl::belowmouse ())) {
231 return send (o, FL_DND_DRAG);
232 } else if (send (o, FL_DND_ENTER)) {
233 if (!o->contains (Fl::belowmouse ()))
234 Fl::belowmouse (o);
235 return 1;
236 }
237 }
238 }
239 Fl::belowmouse (this);
240 return 0;
241
242 case FL_PUSH:
243 for (i = children (); i--;) {
244 o = a[i];
245 if (o->takesevents () && Fl::event_inside (o)) {
246 if (send (o, FL_PUSH)) {
247 if (Fl::pushed () && !o->contains (Fl::pushed ()))
248 Fl::pushed (o);
249 return 1;
250 }
251 }
252 }
253 return 0;
254
255 case FL_RELEASE:
256 case FL_DRAG:
257 o = Fl::pushed ();
258 if (o == this)
259 return 0;
260 else if (o)
261 send (o, event);
262 else {
263 for (i = children (); i--;) {
264 o = a[i];
265 if (o->takesevents () && Fl::event_inside (o)) {
266 if (send (o, event))
267 return 1;
268 }
269 }
270 }
271 return 0;
272
273 case FL_MOUSEWHEEL:
274 for (i = children (); i--;) {
275 o = a[i];
276 if (o->takesevents () && Fl::event_inside (o) && send (o, FL_MOUSEWHEEL))
277 return 1;
278 }
279 for (i = children (); i--;) {
280 o = a[i];
281 if (o->takesevents () && !Fl::event_inside (o) && send (o, FL_MOUSEWHEEL))
282 return 1;
283 }
284 return 0;
285
286 case FL_DEACTIVATE:
287 case FL_ACTIVATE:
288 for (i = children (); i--;) {
289 o = *a++;
290 if (o->active ())
291 o->handle (event);
292 }
293 return 1;
294
295 case FL_SHOW:
296 case FL_HIDE:
297 for (i = children (); i--;) {
298 o = *a++;
299 if (event == FL_HIDE && o == Fl::focus ()) {
300 // Give up input focus...
301 int old_event = Fl::e_number;
302
303 o->handle (Fl::e_number = FL_UNFOCUS);
304 Fl::e_number = old_event;
305 Fl::focus (0);
306 }
307 if (o->visible ())
308 o->handle (event);
309 }
310 return 1;
311
312 default:
313 // For all other events, try to give to each child, starting at focus:
314 for (i = 0; i < children (); i++)
315 if (Fl::focus_ == a[i])
316 break;
317
318 if (i >= children ())
319 i = 0;
320
321 if (children ()) {
322 for (int j = i;;) {
323 if (a[j]->takesevents () || event != FL_MOUSEWHEEL) {
324 if (send (a[j], event))
325 return 1;
326 }
327 j++;
328 if (j >= children ())
329 j = 0;
330 if (j == i)
331 break;
332 }
333 }
334
335 return 0;
336 }
337 }
338
339 //void Fl_Group::focus(Fl_Widget *o) {Fl::focus(o); o->handle(FL_FOCUS);}
340
341 #if 0
342 const char *
343 nameof (Fl_Widget * o)
344 {
345 if (!o)
346 return "NULL";
347 if (!o->label ())
348 return "<no label>";
349 return o->label ();
350 }
351 #endif
352
353 // try to move the focus in response to a keystroke:
354 int
navigation(int key)355 Fl_Group::navigation (int key)
356 {
357 if (children () <= 1)
358 return 0;
359 int i;
360
361 for (i = 0;; i++) {
362 if (i >= children_)
363 return 0;
364 if (array_[i]->contains (Fl::focus ()))
365 break;
366 }
367 Fl_Widget *previous = array_[i];
368
369 for (;;) {
370 switch (key) {
371 case FL_Right:
372 case FL_Down:
373 i++;
374 if (i >= children_) {
375 if (parent ())
376 return 0;
377 i = 0;
378 }
379 break;
380 case FL_Left:
381 case FL_Up:
382 if (i)
383 i--;
384 else {
385 if (parent ())
386 return 0;
387 i = children_ - 1;
388 }
389 break;
390 default:
391 return 0;
392 }
393 Fl_Widget *o = array_[i];
394
395 if (o == previous)
396 return 0;
397 switch (key) {
398 case FL_Down:
399 case FL_Up:
400 // for up/down, the widgets have to overlap horizontally:
401 if (o->x () >= previous->x () + previous->w () ||
402 o->x () + o->w () <= previous->x ())
403 continue;
404 }
405 if (o->take_focus ())
406 return 1;
407 }
408 }
409
410 ////////////////////////////////////////////////////////////////
411
Fl_Group(int X,int Y,int W,int H,const char * l)412 Fl_Group::Fl_Group (int X, int Y, int W, int H, const char *l)
413 : Fl_Widget (X, Y, W, H, l)
414 {
415 align (FL_ALIGN_TOP);
416 children_ = 0;
417 array_ = 0;
418 savedfocus_ = 0;
419 resizable_ = this;
420 sizes_ = 0; // this is allocated when first resize() is done
421 // Subclasses may want to construct child objects as part of their
422 // constructor, so make sure they are add()'d to this object.
423 // But you must end() the object!
424 begin ();
425 }
426
427 void
clear()428 Fl_Group::clear ()
429 {
430 Fl_Widget *const *old_array = array ();
431
432 int old_children = children ();
433
434 // clear everything now, in case fl_fix_focus recursively calls us:
435 children_ = 0;
436 //array_ = 0; //dont do this, it will clobber old_array if only one child
437 savedfocus_ = 0;
438 resizable_ = this;
439 init_sizes ();
440 // okay, now it is safe to destroy the children:
441 Fl_Widget *const *a = old_array;
442
443 for (int i = old_children; i--;) {
444 Fl_Widget *o = *a++;
445
446 if (o->parent () == this)
447 delete o;
448 }
449 if (old_children > 1)
450 free ((void *) old_array);
451 }
452
~Fl_Group()453 Fl_Group::~Fl_Group ()
454 {
455 clear ();
456 }
457
458 void
insert(Fl_Widget & o,int index)459 Fl_Group::insert (Fl_Widget & o, int index)
460 {
461 if (o.parent ()) {
462 Fl_Group *g = (Fl_Group *) (o.parent ());
463
464 int n = g->find (o);
465
466 if (g == this) {
467 if (index > n)
468 index--;
469 if (index == n)
470 return;
471 }
472 g->remove (o);
473 }
474 o.parent_ = this;
475 if (children_ == 0) { // use array pointer to point at single child
476 array_ = (Fl_Widget **) & o;
477 } else if (children_ == 1) { // go from 1 to 2 children
478 Fl_Widget *t = (Fl_Widget *) array_;
479
480 array_ = (Fl_Widget **) malloc (2 * sizeof (Fl_Widget *));
481 if (index) {
482 array_[0] = t;
483 array_[1] = &o;
484 } else {
485 array_[0] = &o;
486 array_[1] = t;
487 }
488 } else {
489 if (!(children_ & (children_ - 1))) // double number of children
490 array_ = (Fl_Widget **) realloc ((void *) array_,
491 2 * children_ * sizeof (Fl_Widget *));
492 int j;
493
494 for (j = children_; j > index; j--)
495 array_[j] = array_[j - 1];
496 array_[j] = &o;
497 }
498 children_++;
499 init_sizes ();
500 }
501
502 void
add(Fl_Widget & o)503 Fl_Group::add (Fl_Widget & o)
504 {
505 insert (o, children_);
506 }
507
508 void
remove(Fl_Widget & o)509 Fl_Group::remove (Fl_Widget & o)
510 {
511 if (!children_)
512 return;
513 int i = find (o);
514
515 if (i >= children_)
516 return;
517 if (&o == savedfocus_)
518 savedfocus_ = 0;
519 o.parent_ = 0;
520 children_--;
521 if (children_ == 1) { // go from 2 to 1 child
522 Fl_Widget *t = array_[!i];
523
524 free ((void *) array_);
525 array_ = (Fl_Widget **) t;
526 } else if (children_ > 1) { // delete from array
527 for (; i < children_; i++)
528 array_[i] = array_[i + 1];
529 }
530 init_sizes ();
531 }
532
533 ////////////////////////////////////////////////////////////////
534
535 // Rather lame kludge here, I need to detect windows and ignore the
536 // changes to X,Y, since all children are relative to X,Y. That
537 // is why I check type():
538
539 // sizes array stores the initial positions of widgets as
540 // left,right,top,bottom quads. The first quad is the group, the
541 // second is the resizable (clipped to the group), and the
542 // rest are the children. This is a convienent order for the
543 // algorithim. If you change this be sure to fix Fl_Tile which
544 // also uses this array!
545
546 void
init_sizes()547 Fl_Group::init_sizes ()
548 {
549 delete[]sizes_;
550 sizes_ = 0;
551 }
552
553 short *
sizes()554 Fl_Group::sizes ()
555 {
556 if (!sizes_) {
557 short *p = sizes_ = new short[4 * (children_ + 2)];
558
559 // first thing in sizes array is the group's size:
560 if (type () < FL_WINDOW) {
561 p[0] = x ();
562 p[2] = y ();
563 } else {
564 p[0] = p[2] = 0;
565 }
566 p[1] = p[0] + w ();
567 p[3] = p[2] + h ();
568 // next is the resizable's size:
569 p[4] = p[0]; // init to the group's size
570 p[5] = p[1];
571 p[6] = p[2];
572 p[7] = p[3];
573 Fl_Widget *r = resizable ();
574
575 if (r && r != this) { // then clip the resizable to it
576 int t;
577
578 t = r->x ();
579 if (t > p[0])
580 p[4] = t;
581 t += r->w ();
582 if (t < p[1])
583 p[5] = t;
584 t = r->y ();
585 if (t > p[2])
586 p[6] = t;
587 t += r->h ();
588 if (t < p[3])
589 p[7] = t;
590 }
591 // next is all the children's sizes:
592 p += 8;
593 Fl_Widget *const *a = array ();
594
595 for (int i = children_; i--;) {
596 Fl_Widget *o = *a++;
597
598 *p++ = o->x ();
599 *p++ = o->x () + o->w ();
600 *p++ = o->y ();
601 *p++ = o->y () + o->h ();
602 }
603 }
604 return sizes_;
605 }
606
607 void
resize(int X,int Y,int W,int H)608 Fl_Group::resize (int X, int Y, int W, int H)
609 {
610
611 if (!resizable () || W == w () && H == h ()) {
612
613 if (type () < FL_WINDOW) {
614 int dx = X - x ();
615
616 int dy = Y - y ();
617
618 Fl_Widget *const *a = array ();
619
620 for (int i = children_; i--;) {
621 Fl_Widget *o = *a++;
622
623 o->resize (o->x () + dx, o->y () + dy, o->w (), o->h ());
624 }
625 }
626
627 } else if (children_) {
628
629 short *p = sizes ();
630
631 // get changes in size/position from the initial size:
632 int dx = X - p[0];
633
634 int dw = W - (p[1] - p[0]);
635
636 int dy = Y - p[2];
637
638 int dh = H - (p[3] - p[2]);
639
640 if (type () >= FL_WINDOW)
641 dx = dy = 0;
642 p += 4;
643
644 // get initial size of resizable():
645 int IX = *p++;
646
647 int IR = *p++;
648
649 int IY = *p++;
650
651 int IB = *p++;
652
653 Fl_Widget *const *a = array ();
654
655 for (int i = children_; i--;) {
656 Fl_Widget *o = *a++;
657
658 #if 1
659 int XX = *p++;
660
661 if (XX >= IR)
662 XX += dw;
663 else if (XX > IX)
664 XX = IX + ((XX - IX) * (IR + dw - IX) + (IR - IX) / 2) / (IR - IX);
665 int R = *p++;
666
667 if (R >= IR)
668 R += dw;
669 else if (R > IX)
670 R = IX + ((R - IX) * (IR + dw - IX) + (IR - IX) / 2) / (IR - IX);
671
672 int YY = *p++;
673
674 if (YY >= IB)
675 YY += dh;
676 else if (YY > IY)
677 YY = IY + ((YY - IY) * (IB + dh - IY) + (IB - IY) / 2) / (IB - IY);
678 int B = *p++;
679
680 if (B >= IB)
681 B += dh;
682 else if (B > IY)
683 B = IY + ((B - IY) * (IB + dh - IY) + (IB - IY) / 2) / (IB - IY);
684 #else // much simpler code from Francois Ostiguy:
685 int XX = *p++;
686
687 if (XX >= IR)
688 XX += dw;
689 else if (XX > IX)
690 XX += dw * (XX - IX) / (IR - IX);
691 int R = *p++;
692
693 if (R >= IR)
694 R += dw;
695 else if (R > IX)
696 R = R + dw * (R - IX) / (IR - IX);
697
698 int YY = *p++;
699
700 if (YY >= IB)
701 YY += dh;
702 else if (YY > IY)
703 YY = YY + dh * (YY - IY) / (IB - IY);
704 int B = *p++;
705
706 if (B >= IB)
707 B += dh;
708 else if (B > IY)
709 B = B + dh * (B - IY) / (IB - IY);
710 #endif
711 o->resize (XX + dx, YY + dy, R - XX, B - YY);
712 }
713 }
714
715 Fl_Widget::resize (X, Y, W, H);
716 }
717
718 void
draw_children()719 Fl_Group::draw_children ()
720 {
721 Fl_Widget *const *a = array ();
722
723 if (damage () & ~FL_DAMAGE_CHILD) { // redraw the entire thing:
724 for (int i = children_; i--;) {
725 Fl_Widget & o = **a++;
726 draw_child (o);
727 draw_outside_label (o);
728 }
729 } else { // only redraw the children that need it:
730 for (int i = children_; i--;)
731 update_child (**a++);
732 }
733 }
734
735 void
draw()736 Fl_Group::draw ()
737 {
738 if (damage () & ~FL_DAMAGE_CHILD) { // redraw the entire thing:
739 draw_box ();
740 draw_label ();
741 }
742 draw_children ();
743 }
744
745 // Draw a child only if it needs it:
746 void
update_child(Fl_Widget & widget) const747 Fl_Group::update_child (Fl_Widget & widget) const const
748 {
749 if (widget.damage () && widget.visible () && widget.type () < FL_WINDOW &&
750 fl_not_clipped (widget.x (), widget.y (), widget.w (), widget.h ())) {
751 widget.draw ();
752 widget.clear_damage ();
753 }
754 }
755
756 // Force a child to redraw:
757 void
draw_child(Fl_Widget & widget) const758 Fl_Group::draw_child (Fl_Widget & widget) const const
759 {
760 if (widget.visible () && widget.type () < FL_WINDOW &&
761 fl_not_clipped (widget.x (), widget.y (), widget.w (), widget.h ())) {
762 widget.clear_damage (FL_DAMAGE_ALL);
763 widget.draw ();
764 widget.clear_damage ();
765 }
766 }
767
768 extern char fl_draw_shortcut;
769
770 // Parents normally call this to draw outside labels:
771 void
draw_outside_label(const Fl_Widget & widget) const772 Fl_Group::draw_outside_label (const Fl_Widget & widget) const const
773 {
774 if (!widget.visible ())
775 return;
776 // skip any labels that are inside the widget:
777 if (!(widget.align () & 15) || (widget.align () & FL_ALIGN_INSIDE))
778 return;
779 // invent a box that is outside the widget:
780 int a = widget.align ();
781
782 int X = widget.x ();
783
784 int Y = widget.y ();
785
786 int W = widget.w ();
787
788 int H = widget.h ();
789
790 if (a & FL_ALIGN_TOP) {
791 a ^= (FL_ALIGN_BOTTOM | FL_ALIGN_TOP);
792 Y = y ();
793 H = widget.y () - Y;
794 } else if (a & FL_ALIGN_BOTTOM) {
795 a ^= (FL_ALIGN_BOTTOM | FL_ALIGN_TOP);
796 Y = Y + H;
797 H = y () + h () - Y;
798 } else if (a & FL_ALIGN_LEFT) {
799 a ^= (FL_ALIGN_LEFT | FL_ALIGN_RIGHT);
800 X = x ();
801 W = widget.x () - X - 3;
802 } else if (a & FL_ALIGN_RIGHT) {
803 a ^= (FL_ALIGN_LEFT | FL_ALIGN_RIGHT);
804 X = X + W + 3;
805 W = x () + this->w () - X;
806 }
807 widget.draw_label (X, Y, W, H, (Fl_Align) a);
808 }
809
810 //
811 // End of "$Id: Fl_Group.cxx 900 2009-08-13 20:00:45Z larry $".
812 //
813