diff options
| author | Michael R Sweet <michael.r.sweet@gmail.com> | 1998-10-06 18:21:25 +0000 |
|---|---|---|
| committer | Michael R Sweet <michael.r.sweet@gmail.com> | 1998-10-06 18:21:25 +0000 |
| commit | f9039b2ae21988783feae9b362818e7923e82d14 (patch) | |
| tree | 6d6fe3679d73448758f9794e7d4d4f6b22a4adad /src/Fl_Group.cxx | |
| parent | 67e89232f9ba067825a158734a09e0fa21aacbe3 (diff) | |
Initial revision
git-svn-id: file:///fltk/svn/fltk/trunk@2 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src/Fl_Group.cxx')
| -rw-r--r-- | src/Fl_Group.cxx | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/src/Fl_Group.cxx b/src/Fl_Group.cxx new file mode 100644 index 000000000..e1a0b43ad --- /dev/null +++ b/src/Fl_Group.cxx @@ -0,0 +1,443 @@ +// Fl_Group.C + +// The Fl_Group is the only defined container type in fltk. + +// Fl_Window itself is a subclass of this, and most of the event +// handling is designed so windows themselves work correctly. + +#include <FL/Fl.H> +#include <FL/Fl_Group.H> +#include <FL/Fl_Window.H> +#include <FL/fl_draw.H> +#include <stdlib.h> + +Fl_Group* Fl_Group::current_; + +// Hack: A single child is stored in the pointer to the array, while +// multiple children are stored in an allocated array: +Fl_Widget*const* Fl_Group::array() const { + return children_ <= 1 ? (Fl_Widget**)(&array_) : array_; +} + +int Fl_Group::find(const Fl_Widget* o) const { + Fl_Widget*const* a = array(); + int i; for (i=0; i < children_; i++) if (*a++ == o) break; + return i; +} + +extern Fl_Widget* fl_oldfocus; // set by Fl::focus + +// For back-compatability, we must adjust all events sent to child +// windows so they are relative to that window. + +static int send(Fl_Widget* o, int event) { + if (o->type() < FL_WINDOW) return o->handle(event); + int save_x = Fl::e_x; Fl::e_x -= o->x(); + int save_y = Fl::e_y; Fl::e_y -= o->y(); + int ret = o->handle(event); + Fl::e_y = save_y; + Fl::e_x = save_x; + return ret; +} + +int Fl_Group::handle(int event) { + + Fl_Widget*const* a = array(); + int i; + Fl_Widget* o; + + switch (event) { + + case FL_FOCUS: + if (savedfocus_ && savedfocus_->take_focus()) return 1; + for (i = children(); i--;) if ((*a++)->take_focus()) return 1; + return 0; + + case FL_UNFOCUS: + savedfocus_ = fl_oldfocus; + return 0; + + case FL_KEYBOARD: + return navigation(); + + case FL_SHORTCUT: + for (i = children(); i--;) { + o = a[i]; + if (o->takesevents() && Fl::event_inside(o) && send(o,FL_SHORTCUT)) + return 1; + } + for (i = children(); i--;) { + o = a[i]; + if (o->takesevents() && !Fl::event_inside(o) && send(o,FL_SHORTCUT)) + return 1; + } + if (Fl::event_key() == FL_Enter) return navigation(FL_Down); + return 0; + + case FL_ENTER: + case FL_MOVE: + for (i = children(); i--;) { + o = a[i]; + if (o->takesevents() && Fl::event_inside(o)) { + if (o->contains(Fl::belowmouse())) { + return send(o,FL_MOVE); + } else if (send(o,FL_ENTER)) { + if (!o->contains(Fl::belowmouse())) Fl::belowmouse(o); + return 1; + } + } + } + Fl::belowmouse(this); + return 1; + + case FL_PUSH: + for (i = children(); i--;) { + o = a[i]; + if (o->takesevents() && Fl::event_inside(o)) { + if (send(o,FL_PUSH)) { + if (Fl::pushed() && !o->contains(Fl::pushed())) Fl::pushed(o); + return 1; + } + } + } + return 0; + + case FL_DEACTIVATE: + case FL_ACTIVATE: + for (i = children(); i--;) { + o = *a++; + if (o->active()) o->handle(event); + } + return 1; + + case FL_SHOW: + case FL_HIDE: + for (i = children(); i--;) { + o = *a++; + if (o->visible()) o->handle(event); + } + return 1; + + default: + return 0; + + } +} + +// translate the current keystroke into up/down/left/right for navigation: +#define ctrl(x) (x^0x40) +int navkey() { + switch (Fl::event_key()) { + case FL_Tab: + return (Fl::event_state(FL_SHIFT) ? FL_Left : FL_Right); + case FL_Right: + return FL_Right; + case FL_Left: + return FL_Left; + case FL_Up: + return FL_Up; + case FL_Down: + return FL_Down; + default: + switch (Fl::event_text()[0]) { + case ctrl('N') : return FL_Down; + case ctrl('P') : return FL_Up; + case ctrl('F') : return FL_Right; + case ctrl('B') : return FL_Left; + } + } + return 0; +} + +//void Fl_Group::focus(Fl_Widget *o) {Fl::focus(o); o->handle(FL_FOCUS);} + +#if 0 +const char *nameof(Fl_Widget *o) { + if (!o) return "NULL"; + if (!o->label()) return "<no label>"; + return o->label(); +} +#endif + +// try to move the focus in response to a keystroke: +int Fl_Group::navigation(int key) { + if (children() <= 1) return 0; + if (!key) {key = navkey(); if (!key) return 0;} + Fl_Widget *focus_ = Fl::focus(); + int old_i; + for (old_i=0;;old_i++) { + if (old_i >= children_) return 0; + if (array_[old_i]->contains(focus_)) break; + } + int i = old_i; + + for (;;) { + switch (key) { + case FL_Right: + case FL_Down: + i++; if (i >= children_) i = 0; + break; + case FL_Left: + case FL_Up: + if (i) i--; else i = children_-1; + break; + default: + return 0; + } + if (i == old_i) return 0; + Fl_Widget* o = array_[i]; + switch (key) { + case FL_Down: + case FL_Up: + if (o->x() >= focus_->x()+focus_->w() || + o->x()+o->w() <= focus_->x()) continue; + } + if (o->take_focus()) return 1; + } +} + +//////////////////////////////////////////////////////////////// + +Fl_Group::Fl_Group(int X,int Y,int W,int H,const char *l) +: Fl_Widget(X,Y,W,H,l) { + align(FL_ALIGN_TOP); + children_ = 0; + array_ = 0; + savedfocus_ = 0; + resizable_ = this; + sizes_ = 0; // this is allocated when first resize() is done + // Subclasses may want to construct child objects as part of their + // constructor, so make sure they are add()'d to this object. + // But you must end() the object! + begin(); +} + +void Fl_Group::clear() { + Fl_Widget*const* a = array(); + for (int i=children(); i--;) { + Fl_Widget* o = *a++; + // test the parent to see if child already destructed: + if (o->parent() == this) delete o; + } + if (children() > 1) free((void*)array_); + children_ = 0; + array_ = 0; + savedfocus_ = 0; + resizable_ = this; + init_sizes(); +} + +Fl_Group::~Fl_Group() {clear();} + +void Fl_Group::insert(Fl_Widget &o, int i) { + if (o.parent()) ((Fl_Group*)(o.parent()))->remove(o); + o.parent_ = this; + if (children_ == 0) { // use array pointer to point at single child + array_ = (Fl_Widget**)&o; + } else if (children_ == 1) { // go from 1 to 2 children + Fl_Widget* t = (Fl_Widget*)array_; + array_ = (Fl_Widget**)malloc(2*sizeof(Fl_Widget*)); + array_[!i] = t; array_[i] = &o; + } else { + if (!(children_ & (children_-1))) // double number of children + array_ = (Fl_Widget**)realloc((void*)array_, + 2*children_*sizeof(Fl_Widget*)); + for (int j = children_; j > i; j--) array_[j] = array_[j-1]; + array_[i] = &o; + } + children_++; + init_sizes(); +} + +void Fl_Group::add(Fl_Widget &o) {insert(o, children_);} + +void Fl_Group::remove(Fl_Widget &o) { + int i = find(o); + if (i >= children_) return; + if (&o == savedfocus_) savedfocus_ = 0; + o.parent_ = 0; + children_--; + if (children_ == 1) { // go from 2 to 1 child + Fl_Widget *t = array_[!i]; + free((void*)array_); + array_ = (Fl_Widget**)t; + } else if (children_ > 1) { // delete from array + for (; i < children_; i++) array_[i] = array_[i+1]; + } + init_sizes(); +} + +//////////////////////////////////////////////////////////////// + +// Rather lame kludge here, I need to detect windows and ignore the +// changes to X,Y, since all children are relative to X,Y. That +// is why I check type(): + +// sizes array stores the initial positions of widgets as +// left,right,top,bottom quads. The first quad is the group, the +// second is the resizable (clipped to the group), and the +// rest are the children. This is a convienent order for the +// algorithim. If you change this be sure to fix Fl_Tile which +// also uses this array! + +void Fl_Group::init_sizes() { + delete[] sizes_; sizes_ = 0; +} + +short* Fl_Group::sizes() { + if (!sizes_) { + short* p = sizes_ = new short[4*(children_+2)]; + // first thing in sizes array is the group's size: + if (type() < FL_WINDOW) {p[0] = x(); p[2] = y();} else {p[0] = p[2] = 0;} + p[1] = p[0]+w(); p[3] = p[2]+h(); + // next is the resizable's size: + p[4] = p[0]; // init to the group's size + p[5] = p[1]; + p[6] = p[2]; + p[7] = p[3]; + Fl_Widget* r = resizable(); + if (r && r != this) { // then clip the resizable to it + int t; + t = r->x(); if (t > p[0]) p[4] = t; + t +=r->w(); if (t < p[1]) p[5] = t; + t = r->y(); if (t > p[2]) p[6] = t; + t +=r->h(); if (t < p[3]) p[7] = t; + } + // next is all the children's sizes: + p += 8; + Fl_Widget*const* a = array(); + for (int i=children_; i--;) { + Fl_Widget* o = *a++; + *p++ = o->x(); + *p++ = o->x()+o->w(); + *p++ = o->y(); + *p++ = o->y()+o->h(); + } + } + return sizes_; +} + +void Fl_Group::resize(int X, int Y, int W, int H) { + + if (!resizable() || W==w() && H==h()) { + + if (type() < FL_WINDOW) { + int dx = X-x(); + int dy = Y-y(); + Fl_Widget*const* a = array(); + for (int i=children_; i--;) { + Fl_Widget* o = *a++; + o->resize(o->x()+dx, o->y()+dy, o->w(), o->h()); + } + } + + } else if (children_) { + + short* p = sizes(); + + // get changes in size/position from the initial size: + int dx = X - p[0]; + int dw = W - (p[1]-p[0]); + int dy = Y - p[2]; + int dh = H - (p[3]-p[2]); + if (type() >= FL_WINDOW) dx = dy = 0; + p += 4; + + // get initial size of resizable(): + int IX = *p++; + int IR = *p++; + int IY = *p++; + int IB = *p++; + + Fl_Widget*const* a = array(); + for (int i=children_; i--;) { + Fl_Widget* o = *a++; + + int X = *p++; + if (X >= IR) X += dw; + else if (X > IX) X = IX+((X-IX)*(IR+dw-IX)+(IR-IX)/2)/(IR-IX); + int R = *p++; + if (R >= IR) R += dw; + else if (R > IX) R = IX+((R-IX)*(IR+dw-IX)+(IR-IX)/2)/(IR-IX); + + int Y = *p++; + if (Y >= IB) Y += dh; + else if (Y > IY) Y = IY+((Y-IY)*(IB+dh-IY)+(IB-IY)/2)/(IB-IY); + int B = *p++; + if (B >= IB) B += dh; + else if (B > IY) B = IY+((B-IY)*(IB+dh-IY)+(IB-IY)/2)/(IB-IY); + + o->resize(X+dx, Y+dy, R-X, B-Y); + } + } + + Fl_Widget::resize(X,Y,W,H); +} + +void Fl_Group::draw() { + Fl_Widget*const* a = array(); + if (damage() & ~1) { // redraw the entire thing: + draw_box(); + draw_label(); + for (int i=children_; i--;) { + Fl_Widget& o = **a++; + draw_child(o); + draw_outside_label(o); + } + } else { // only redraw the children that need it: + for (int i=children_; i--;) update_child(**a++); + } +} + +// Draw a child only if it needs it: +void Fl_Group::update_child(Fl_Widget& w) const { + if (w.damage() && w.visible() && w.type() < FL_WINDOW && + fl_not_clipped(w.x(), w.y(), w.w(), w.h())) { + w.draw(); + w.clear_damage(); + } +} + +// Force a child to redraw: +void Fl_Group::draw_child(Fl_Widget& w) const { + if (w.visible() && w.type() < FL_WINDOW && + fl_not_clipped(w.x(), w.y(), w.w(), w.h())) { + w.clear_damage(~0); + w.draw(); + w.clear_damage(); + } +} + +extern char fl_draw_shortcut; + +// Parents normally call this to draw outside labels: +void Fl_Group::draw_outside_label(const Fl_Widget& w) const { + if (!w.visible()) return; + // skip any labels that are inside the widget: + if (!(w.align()&15) || (w.align() & FL_ALIGN_INSIDE)) return; + // invent a box that is outside the widget: + int align = w.align(); + int X = w.x(); + int Y = w.y(); + int W = w.w(); + int H = w.h(); + if (align & FL_ALIGN_TOP) { + align ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP); + Y = y(); + H = w.y()-Y; + } else if (align & FL_ALIGN_BOTTOM) { + align ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP); + Y = Y+H; + H = y()+h()-Y; + } else if (align & FL_ALIGN_LEFT) { + align ^= (FL_ALIGN_LEFT|FL_ALIGN_RIGHT); + X = x(); + W = w.x()-X-3; + } else if (align & FL_ALIGN_RIGHT) { + align ^= (FL_ALIGN_LEFT|FL_ALIGN_RIGHT); + X = X+W+3; + W = x()+this->w()-X; + } + w.draw_label(X,Y,W,H,(Fl_Align)align); +} + |
