source: rtems-graphics-toolkit/fltk-1.1.10/src/Fl_Group.cxx @ 513eea1

Last change on this file since 513eea1 was 513eea1, checked in by Joel Sherrill <joel.sherrill@…>, on 01/09/10 at 22:43:24

2010-01-08 Joel Sherrill <joel.sherrill@…>

fltk 1.1.10. imported

  • ORIGIN: Updated.
  • Property mode set to 100644
File size: 16.0 KB
Line 
1//
2// "$Id$"
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
40Fl_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:
44Fl_Widget*const* Fl_Group::array() const {
45  return children_ <= 1 ? (Fl_Widget**)(&array_) : array_;
46}
47
48int Fl_Group::find(const Fl_Widget* o) const {
49  Fl_Widget*const* a = array();
50  int i; for (i=0; i < children_; i++) if (*a++ == o) break;
51  return i;
52}
53
54// Metrowerks CodeWarrior and others can't export the static
55// class member: current_, so these methods can't be inlined...
56void Fl_Group::begin() {current_ = this;}
57void Fl_Group::end() {current_ = (Fl_Group*)parent();}
58Fl_Group *Fl_Group::current() {return current_;}
59void Fl_Group::current(Fl_Group *g) {current_ = g;}
60
61extern Fl_Widget* fl_oldfocus; // set by Fl::focus
62
63// For back-compatability, we must adjust all events sent to child
64// windows so they are relative to that window.
65
66static int send(Fl_Widget* o, int event) {
67  if (o->type() < FL_WINDOW) return o->handle(event);
68  switch ( event )
69  {
70  case FL_DND_ENTER:
71  case FL_DND_DRAG:
72    // figure out correct type of event:
73    event = (o->contains(Fl::belowmouse())) ? FL_DND_DRAG : FL_DND_ENTER;
74  }
75  int save_x = Fl::e_x; Fl::e_x -= o->x();
76  int save_y = Fl::e_y; Fl::e_y -= o->y();
77  int ret = o->handle(event);
78  Fl::e_y = save_y;
79  Fl::e_x = save_x;
80  switch ( event )
81  {
82  case FL_ENTER:
83  case FL_DND_ENTER:
84    // Successful completion of FL_ENTER means the widget is now the
85    // belowmouse widget, but only call Fl::belowmouse if the child
86    // widget did not do so:
87    if (!o->contains(Fl::belowmouse())) Fl::belowmouse(o);
88    break;
89  }
90  return ret;
91}
92
93// translate the current keystroke into up/down/left/right for navigation:
94#define ctrl(x) (x^0x40)
95static int navkey() {
96  switch (Fl::event_key()) {
97  case 0: // not an FL_KEYBOARD/FL_SHORTCUT event
98    break;
99  case FL_Tab:
100    if (!Fl::event_state(FL_SHIFT)) return FL_Right;
101  case 0xfe20: // XK_ISO_Left_Tab
102    return FL_Left;
103  case FL_Right:
104    return FL_Right;
105  case FL_Left:
106    return FL_Left;
107  case FL_Up:
108    return FL_Up;
109  case FL_Down:
110    return FL_Down;
111  }
112  return 0;
113}
114
115int Fl_Group::handle(int event) {
116
117  Fl_Widget*const* a = array();
118  int i;
119  Fl_Widget* o;
120
121  switch (event) {
122
123  case FL_FOCUS:
124    switch (navkey()) {
125    default:
126      if (savedfocus_ && savedfocus_->take_focus()) return 1;
127    case FL_Right:
128    case FL_Down:
129      for (i = children(); i--;) if ((*a++)->take_focus()) return 1;
130      break;
131    case FL_Left:
132    case FL_Up:
133      for (i = children(); i--;) if (a[i]->take_focus()) return 1;
134      break;
135    }
136    return 0;
137
138  case FL_UNFOCUS:
139    savedfocus_ = fl_oldfocus;
140    return 0;
141
142  case FL_KEYBOARD:
143    return navigation(navkey());
144
145  case FL_SHORTCUT:
146    for (i = children(); i--;) {
147      o = a[i];
148      if (o->takesevents() && Fl::event_inside(o) && send(o,FL_SHORTCUT))
149        return 1;
150    }
151    for (i = children(); i--;) {
152      o = a[i];
153      if (o->takesevents() && !Fl::event_inside(o) && send(o,FL_SHORTCUT))
154        return 1;
155    }
156    if ((Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter)) return navigation(FL_Down);
157    return 0;
158
159  case FL_ENTER:
160  case FL_MOVE:
161    for (i = children(); i--;) {
162      o = a[i];
163      if (o->visible() && Fl::event_inside(o)) {
164        if (o->contains(Fl::belowmouse())) {
165          return send(o,FL_MOVE);
166        } else {
167          Fl::belowmouse(o);
168          if (send(o,FL_ENTER)) return 1;
169        }
170      }
171    }
172    Fl::belowmouse(this);
173    return 1;
174
175  case FL_DND_ENTER:
176  case FL_DND_DRAG:
177    for (i = children(); i--;) {
178      o = a[i];
179      if (o->takesevents() && Fl::event_inside(o)) {
180        if (o->contains(Fl::belowmouse())) {
181          return send(o,FL_DND_DRAG);
182        } else if (send(o,FL_DND_ENTER)) {
183          if (!o->contains(Fl::belowmouse())) Fl::belowmouse(o);
184          return 1;
185        }
186      }
187    }
188    Fl::belowmouse(this);
189    return 0;
190
191  case FL_PUSH:
192    for (i = children(); i--;) {
193      o = a[i];
194      if (o->takesevents() && Fl::event_inside(o)) {
195        if (send(o,FL_PUSH)) {
196          if (Fl::pushed() && !o->contains(Fl::pushed())) Fl::pushed(o);
197          return 1;
198        }
199      }
200    }
201    return 0;
202
203  case FL_RELEASE:
204  case FL_DRAG:
205    o = Fl::pushed();
206    if (o == this) return 0;
207    else if (o) send(o,event);
208    else {
209      for (i = children(); i--;) {
210        o = a[i];
211        if (o->takesevents() && Fl::event_inside(o)) {
212          if (send(o,event)) return 1;
213        }
214      }
215    }
216    return 0;
217
218  case FL_MOUSEWHEEL:
219    for (i = children(); i--;) {
220      o = a[i];
221      if (o->takesevents() && Fl::event_inside(o) && send(o,FL_MOUSEWHEEL))
222        return 1;
223    }
224    for (i = children(); i--;) {
225      o = a[i];
226      if (o->takesevents() && !Fl::event_inside(o) && send(o,FL_MOUSEWHEEL))
227        return 1;
228    }
229    return 0;
230
231  case FL_DEACTIVATE:
232  case FL_ACTIVATE:
233    for (i = children(); i--;) {
234      o = *a++;
235      if (o->active()) o->handle(event);
236    }
237    return 1;
238
239  case FL_SHOW:
240  case FL_HIDE:
241    for (i = children(); i--;) {
242      o = *a++;
243      if (event == FL_HIDE && o == Fl::focus()) {
244        // Give up input focus...
245        int old_event = Fl::e_number;
246        o->handle(Fl::e_number = FL_UNFOCUS);
247        Fl::e_number = old_event;
248        Fl::focus(0);
249      }
250      if (o->visible()) o->handle(event);
251    }
252    return 1;
253
254  default:
255    // For all other events, try to give to each child, starting at focus:
256    for (i = 0; i < children(); i ++)
257      if (Fl::focus_ == a[i]) break;
258
259    if (i >= children()) i = 0;
260
261    if (children()) {
262      for (int j = i;;) {
263        if (a[j]->takesevents()) if (send(a[j], event)) return 1;
264        j++;
265        if (j >= children()) j = 0;
266        if (j == i) break;
267      }
268    }
269
270    return 0;
271  }
272}
273
274//void Fl_Group::focus(Fl_Widget *o) {Fl::focus(o); o->handle(FL_FOCUS);}
275
276#if 0
277const char *nameof(Fl_Widget *o) {
278  if (!o) return "NULL";
279  if (!o->label()) return "<no label>";
280  return o->label();
281}
282#endif
283
284// try to move the focus in response to a keystroke:
285int Fl_Group::navigation(int key) {
286  if (children() <= 1) return 0;
287  int i;
288  for (i = 0; ; i++) {
289    if (i >= children_) return 0;
290    if (array_[i]->contains(Fl::focus())) break;
291  }
292  Fl_Widget *previous = array_[i];
293
294  for (;;) {
295    switch (key) {
296    case FL_Right:
297    case FL_Down:
298      i++;
299      if (i >= children_) {
300        if (parent()) return 0;
301        i = 0;
302      }
303      break;
304    case FL_Left:
305    case FL_Up:
306      if (i) i--;
307      else {
308        if (parent()) return 0;
309        i = children_-1;
310      }
311      break;
312    default:
313      return 0;
314    }
315    Fl_Widget* o = array_[i];
316    if (o == previous) return 0;
317    switch (key) {
318    case FL_Down:
319    case FL_Up:
320      // for up/down, the widgets have to overlap horizontally:
321      if (o->x() >= previous->x()+previous->w() ||
322          o->x()+o->w() <= previous->x()) continue;
323    }
324    if (o->take_focus()) return 1;
325  }
326}
327
328////////////////////////////////////////////////////////////////
329
330Fl_Group::Fl_Group(int X,int Y,int W,int H,const char *l)
331: Fl_Widget(X,Y,W,H,l) {
332  align(FL_ALIGN_TOP);
333  children_ = 0;
334  array_ = 0;
335  savedfocus_ = 0;
336  resizable_ = this;
337  sizes_ = 0; // this is allocated when first resize() is done
338  // Subclasses may want to construct child objects as part of their
339  // constructor, so make sure they are add()'d to this object.
340  // But you must end() the object!
341  begin();
342}
343
344void Fl_Group::clear() {
345  Fl_Widget*const* old_array = array();
346  int old_children = children();
347  // clear everything now, in case fl_fix_focus recursively calls us:
348  children_ = 0;
349  //array_ = 0; //dont do this, it will clobber old_array if only one child
350  savedfocus_ = 0;
351  resizable_ = this;
352  init_sizes();
353  // okay, now it is safe to destroy the children:
354  Fl_Widget*const* a = old_array;
355  for (int i=old_children; i--;) {
356    Fl_Widget* o = *a++;
357    if (o->parent() == this) delete o;
358  }
359  if (old_children > 1) free((void*)old_array);
360}
361
362Fl_Group::~Fl_Group() {
363  clear();
364}
365
366void Fl_Group::insert(Fl_Widget &o, int index) {
367  if (o.parent()) {
368    Fl_Group* g = (Fl_Group*)(o.parent());
369    int n = g->find(o);
370    if (g == this) {
371      if (index > n) index--;
372      if (index == n) return;
373    }
374    g->remove(o);
375  }
376  o.parent_ = this;
377  if (children_ == 0) { // use array pointer to point at single child
378    array_ = (Fl_Widget**)&o;
379  } else if (children_ == 1) { // go from 1 to 2 children
380    Fl_Widget* t = (Fl_Widget*)array_;
381    array_ = (Fl_Widget**)malloc(2*sizeof(Fl_Widget*));
382    if (index) {array_[0] = t; array_[1] = &o;}
383    else {array_[0] = &o; array_[1] = t;}
384  } else {
385    if (!(children_ & (children_-1))) // double number of children
386      array_ = (Fl_Widget**)realloc((void*)array_,
387                                    2*children_*sizeof(Fl_Widget*));
388    int j; for (j = children_; j > index; j--) array_[j] = array_[j-1];
389    array_[j] = &o;
390  }
391  children_++;
392  init_sizes();
393}
394
395void Fl_Group::add(Fl_Widget &o) {insert(o, children_);}
396
397void Fl_Group::remove(Fl_Widget &o) {
398  if (!children_) return;
399  int i = find(o);
400  if (i >= children_) return;
401  if (&o == savedfocus_) savedfocus_ = 0;
402  o.parent_ = 0;
403  children_--;
404  if (children_ == 1) { // go from 2 to 1 child
405    Fl_Widget *t = array_[!i];
406    free((void*)array_);
407    array_ = (Fl_Widget**)t;
408  } else if (children_ > 1) { // delete from array
409    for (; i < children_; i++) array_[i] = array_[i+1];
410  }
411  init_sizes();
412}
413
414////////////////////////////////////////////////////////////////
415
416// Rather lame kludge here, I need to detect windows and ignore the
417// changes to X,Y, since all children are relative to X,Y.  That
418// is why I check type():
419
420// sizes array stores the initial positions of widgets as
421// left,right,top,bottom quads.  The first quad is the group, the
422// second is the resizable (clipped to the group), and the
423// rest are the children.  This is a convienent order for the
424// algorithim.  If you change this be sure to fix Fl_Tile which
425// also uses this array!
426
427void Fl_Group::init_sizes() {
428  delete[] sizes_; sizes_ = 0;
429}
430
431short* Fl_Group::sizes() {
432  if (!sizes_) {
433    short* p = sizes_ = new short[4*(children_+2)];
434    // first thing in sizes array is the group's size:
435    if (type() < FL_WINDOW) {p[0] = x(); p[2] = y();} else {p[0] = p[2] = 0;}
436    p[1] = p[0]+w(); p[3] = p[2]+h();
437    // next is the resizable's size:
438    p[4] = p[0]; // init to the group's size
439    p[5] = p[1];
440    p[6] = p[2];
441    p[7] = p[3];
442    Fl_Widget* r = resizable();
443    if (r && r != this) { // then clip the resizable to it
444      int t;
445      t = r->x(); if (t > p[0]) p[4] = t;
446      t +=r->w(); if (t < p[1]) p[5] = t;
447      t = r->y(); if (t > p[2]) p[6] = t;
448      t +=r->h(); if (t < p[3]) p[7] = t;
449    }
450    // next is all the children's sizes:
451    p += 8;
452    Fl_Widget*const* a = array();
453    for (int i=children_; i--;) {
454      Fl_Widget* o = *a++;
455      *p++ = o->x();
456      *p++ = o->x()+o->w();
457      *p++ = o->y();
458      *p++ = o->y()+o->h();
459    }
460  }
461  return sizes_;
462}
463
464void Fl_Group::resize(int X, int Y, int W, int H) {
465
466  int dx = X-x();
467  int dy = Y-y();
468  int dw = W-w();
469  int dh = H-h();
470 
471  short *p = sizes(); // save initial sizes and positions
472
473  Fl_Widget::resize(X,Y,W,H); // make new xywh values visible for children
474
475  if (!resizable() || dw==0 && dh==0 ) {
476
477    if (type() < FL_WINDOW) {
478      Fl_Widget*const* a = array();
479      for (int i=children_; i--;) {
480        Fl_Widget* o = *a++;
481        o->resize(o->x()+dx, o->y()+dy, o->w(), o->h());
482      }
483    }
484
485  } else if (children_) {
486
487    // get changes in size/position from the initial size:
488    dx = X - p[0];
489    dw = W - (p[1]-p[0]);
490    dy = Y - p[2];
491    dh = H - (p[3]-p[2]);
492    if (type() >= FL_WINDOW) dx = dy = 0;
493    p += 4;
494
495    // get initial size of resizable():
496    int IX = *p++;
497    int IR = *p++;
498    int IY = *p++;
499    int IB = *p++;
500
501    Fl_Widget*const* a = array();
502    for (int i=children_; i--;) {
503      Fl_Widget* o = *a++;
504#if 1
505      int XX = *p++;
506      if (XX >= IR) XX += dw;
507      else if (XX > IX) XX = IX+((XX-IX)*(IR+dw-IX)+(IR-IX)/2)/(IR-IX);
508      int R = *p++;
509      if (R >= IR) R += dw;
510      else if (R > IX) R = IX+((R-IX)*(IR+dw-IX)+(IR-IX)/2)/(IR-IX);
511
512      int YY = *p++;
513      if (YY >= IB) YY += dh;
514      else if (YY > IY) YY = IY+((YY-IY)*(IB+dh-IY)+(IB-IY)/2)/(IB-IY);
515      int B = *p++;
516      if (B >= IB) B += dh;
517      else if (B > IY) B = IY+((B-IY)*(IB+dh-IY)+(IB-IY)/2)/(IB-IY);
518#else // much simpler code from Francois Ostiguy:
519      int XX = *p++;
520      if (XX >= IR) XX += dw;
521      else if (XX > IX) XX += dw * (XX-IX)/(IR-IX);
522      int R = *p++;
523      if (R >= IR) R += dw;
524      else if (R > IX) R = R + dw * (R-IX)/(IR-IX);
525
526      int YY = *p++;
527      if (YY >= IB) YY += dh;
528      else if (YY > IY) YY = YY + dh*(YY-IY)/(IB-IY);
529      int B = *p++;
530      if (B >= IB) B += dh;
531      else if (B > IY) B = B + dh*(B-IY)/(IB-IY);
532#endif
533      o->resize(XX+dx, YY+dy, R-XX, B-YY);
534    }
535  }
536}
537
538void Fl_Group::draw_children() {
539  Fl_Widget*const* a = array();
540
541  if (clip_children()) {
542    fl_push_clip(x() + Fl::box_dx(box()),
543                 y() + Fl::box_dy(box()),
544                 w() - Fl::box_dw(box()),
545                 h() - Fl::box_dh(box()));
546  }
547
548  if (damage() & ~FL_DAMAGE_CHILD) { // redraw the entire thing:
549    for (int i=children_; i--;) {
550      Fl_Widget& o = **a++;
551      draw_child(o);
552      draw_outside_label(o);
553    }
554  } else {      // only redraw the children that need it:
555    for (int i=children_; i--;) update_child(**a++);
556  }
557
558  if (clip_children()) fl_pop_clip();
559}
560
561void Fl_Group::draw() {
562  if (damage() & ~FL_DAMAGE_CHILD) { // redraw the entire thing:
563    draw_box();
564    draw_label();
565  }
566  draw_children();
567}
568
569// Draw a child only if it needs it:
570void Fl_Group::update_child(Fl_Widget& widget) const {
571  if (widget.damage() && widget.visible() && widget.type() < FL_WINDOW &&
572      fl_not_clipped(widget.x(), widget.y(), widget.w(), widget.h())) {
573    widget.draw();     
574    widget.clear_damage();
575  }
576}
577
578// Force a child to redraw:
579void Fl_Group::draw_child(Fl_Widget& widget) const {
580  if (widget.visible() && widget.type() < FL_WINDOW &&
581      fl_not_clipped(widget.x(), widget.y(), widget.w(), widget.h())) {
582    widget.clear_damage(FL_DAMAGE_ALL);
583    widget.draw();
584    widget.clear_damage();
585  }
586}
587
588extern char fl_draw_shortcut;
589
590// Parents normally call this to draw outside labels:
591void Fl_Group::draw_outside_label(const Fl_Widget& widget) const {
592  if (!widget.visible()) return;
593  // skip any labels that are inside the widget:
594  if (!(widget.align()&15) || (widget.align() & FL_ALIGN_INSIDE)) return;
595  // invent a box that is outside the widget:
596  int a = widget.align();
597  int X = widget.x();
598  int Y = widget.y();
599  int W = widget.w();
600  int H = widget.h();
601  if (a & FL_ALIGN_TOP) {
602    a ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP);
603    Y = y();
604    H = widget.y()-Y;
605  } else if (a & FL_ALIGN_BOTTOM) {
606    a ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP);
607    Y = Y+H;
608    H = y()+h()-Y;
609  } else if (a & FL_ALIGN_LEFT) {
610    a ^= (FL_ALIGN_LEFT|FL_ALIGN_RIGHT);
611    X = x();
612    W = widget.x()-X-3;
613  } else if (a & FL_ALIGN_RIGHT) {
614    a ^= (FL_ALIGN_LEFT|FL_ALIGN_RIGHT);
615    X = X+W+3;
616    W = x()+this->w()-X;
617  }
618  widget.draw_label(X,Y,W,H,(Fl_Align)a);
619}
620
621//
622// End of "$Id$".
623//
Note: See TracBrowser for help on using the repository browser.