summaryrefslogtreecommitdiff
path: root/src/Fl_Group.cxx
diff options
context:
space:
mode:
authorMichael R Sweet <michael.r.sweet@gmail.com>1998-10-06 18:21:25 +0000
committerMichael R Sweet <michael.r.sweet@gmail.com>1998-10-06 18:21:25 +0000
commitf9039b2ae21988783feae9b362818e7923e82d14 (patch)
tree6d6fe3679d73448758f9794e7d4d4f6b22a4adad /src/Fl_Group.cxx
parent67e89232f9ba067825a158734a09e0fa21aacbe3 (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.cxx443
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);
+}
+