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