#include <FL/Fl_Group.H>
#include "Fl_Window_Driver.H"
#include <FL/Fl_Rect.H>
#include <FL/fl_draw.H>
#include <stdlib.h>
Fl_Group* Fl_Group::current_;
Fl_Widget*const* Fl_Group::array() const {
return children_ <= 1 ? &child1_ : 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;
}
void Fl_Group::begin() {current_ = this;}
void Fl_Group::end() {current_ = parent();}
Fl_Group *Fl_Group::current() {return current_;}
void Fl_Group::current(Fl_Group *g) {current_ = g;}
extern Fl_Widget* fl_oldfocus;
static int send(Fl_Widget* o, int event) {
if (!o->as_window()) return o->handle(event);
switch ( event )
{
case FL_DND_ENTER:
case FL_DND_DRAG:
event = (o->contains(Fl::belowmouse())) ? FL_DND_DRAG : FL_DND_ENTER;
}
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;
switch ( event )
{
case FL_ENTER:
case FL_DND_ENTER:
if (!o->contains(Fl::belowmouse())) Fl::belowmouse(o);
break;
}
return ret;
}
static int navkey() {
if (Fl::event_state(FL_CTRL | FL_ALT | FL_META)) return 0;
switch (Fl::event_key()) {
case 0: break;
case FL_Tab:
if (!Fl::event_state(FL_SHIFT)) return FL_Right;
return FL_Left;
case FL_Right:
return FL_Right;
case FL_Left:
return FL_Left;
case FL_Up:
return FL_Up;
case FL_Down:
return FL_Down;
}
return 0;
}
int Fl_Group::handle(int event) {
Fl_Widget*const* a = array();
int i;
Fl_Widget* o;
switch (event) {
case FL_FOCUS:
switch (navkey()) {
default:
if (savedfocus_ && savedfocus_->take_focus()) return 1;
case FL_Right:
case FL_Down:
for (i = children(); i--;) if ((*a++)->take_focus()) return 1;
break;
case FL_Left:
case FL_Up:
for (i = children(); i--;) if (a[i]->take_focus()) return 1;
break;
}
return 0;
case FL_UNFOCUS:
savedfocus_ = fl_oldfocus;
return 0;
case FL_KEYBOARD:
return navigation(navkey());
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 || Fl::event_key() == FL_KP_Enter)) return navigation(FL_Down);
return 0;
case FL_ENTER:
case FL_MOVE:
for (i = children(); i--;) {
o = a[i];
if (o->visible() && Fl::event_inside(o)) {
if (o->contains(Fl::belowmouse())) {
return send(o,FL_MOVE);
} else {
Fl::belowmouse(o);
if (send(o,FL_ENTER)) return 1;
}
}
}
Fl::belowmouse(this);
return 1;
case FL_DND_ENTER:
case FL_DND_DRAG:
for (i = children(); i--;) {
o = a[i];
if (o->takesevents() && Fl::event_inside(o)) {
if (o->contains(Fl::belowmouse())) {
return send(o,FL_DND_DRAG);
} else if (send(o,FL_DND_ENTER)) {
if (!o->contains(Fl::belowmouse())) Fl::belowmouse(o);
return 1;
}
}
}
Fl::belowmouse(this);
return 0;
case FL_PUSH:
for (i = children(); i--;) {
o = a[i];
if (o->takesevents() && Fl::event_inside(o)) {
Fl_Widget_Tracker wp(o);
if (send(o,FL_PUSH)) {
if (Fl::pushed() && wp.exists() && !o->contains(Fl::pushed())) Fl::pushed(o);
return 1;
}
}
}
return 0;
case FL_RELEASE:
case FL_DRAG:
o = Fl::pushed();
if (o == this) return 0;
else if (o) send(o,event);
else {
for (i = children(); i--;) {
o = a[i];
if (o->takesevents() && Fl::event_inside(o)) {
if (send(o,event)) return 1;
}
}
}
return 0;
case FL_MOUSEWHEEL:
for (i = children(); i--;) {
o = a[i];
if (o->takesevents() && Fl::event_inside(o) && send(o,FL_MOUSEWHEEL))
return 1;
}
for (i = children(); i--;) {
o = a[i];
if (o->takesevents() && !Fl::event_inside(o) && send(o,FL_MOUSEWHEEL))
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 (event == FL_HIDE && o == Fl::focus()) {
int old_event = Fl::e_number;
o->handle(Fl::e_number = FL_UNFOCUS);
Fl::e_number = old_event;
Fl::focus(0);
}
if (o->visible()) o->handle(event);
}
return 1;
default:
for (i = 0; i < children(); i ++)
if (Fl::focus_ == a[i]) break;
if (i >= children()) i = 0;
if (children()) {
for (int j = i;;) {
if (a[j]->takesevents()) if (send(a[j], event)) return 1;
j++;
if (j >= children()) j = 0;
if (j == i) break;
}
}
return 0;
}
}
int Fl_Group::navigation(int key) {
if (children() <= 1) return 0;
int i;
for (i = 0; ; i++) {
if (i >= children_) return 0;
if (array_[i]->contains(Fl::focus())) break;
}
Fl_Widget *previous = array_[i];
for (;;) {
switch (key) {
case FL_Right:
case FL_Down:
i++;
if (i >= children_) {
if (parent()) return 0;
i = 0;
}
break;
case FL_Left:
case FL_Up:
if (i) i--;
else {
if (parent()) return 0;
i = children_-1;
}
break;
default:
return 0;
}
Fl_Widget* o = array_[i];
if (o == previous) return 0;
switch (key) {
case FL_Down:
case FL_Up:
if (o->x() >= previous->x()+previous->w() ||
o->x()+o->w() <= previous->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;
bounds_ = 0; sizes_ = 0;
begin();
}
void Fl_Group::clear() {
savedfocus_ = 0;
resizable_ = this;
init_sizes();
Fl_Widget *pushed = Fl::pushed(); if (contains(pushed)) pushed = this; Fl::pushed(this);
while (children_) { int idx = children_-1; Fl_Widget* w = child(idx); if (w->parent()==this) { if (children_>2) { w->parent_ = 0; on_remove(idx);
children_--; } else { remove(idx);
}
delete w; } else { remove(idx); }
}
if (pushed != this) Fl::pushed(pushed);
}
Fl_Group::~Fl_Group() {
if (current_ == this)
end();
clear();
}
int Fl_Group::on_insert(Fl_Widget *candidate, int index) {
(void)candidate;
return index;
}
int Fl_Group::on_move(int oldIndex, int newIndex) {
(void)oldIndex;
return newIndex;
}
void Fl_Group::insert(Fl_Widget &o, int index) {
if (o.parent()) {
Fl_Group* g = o.parent();
int n = g->find(o);
if (g == this) {
index = on_move(n, index);
if (index < 0) return; if (index > children_)
index = children_;
if (index > n) index--; if (index == n) return; if (index > n)
memmove(array_+n, array_+(n+1), (index-n) * sizeof(Fl_Widget*));
else
memmove(array_+(index+1), array_+index, (n-index) * sizeof(Fl_Widget*));
array_[index] = &o;
init_sizes();
return;
}
g->remove(n);
}
index = on_insert(&o, index);
if (index == -1) return;
o.parent_ = this;
if (children_ == 0) { child1_ = &o;
} else if (children_ == 1) { Fl_Widget* t = child1_;
array_ = (Fl_Widget**)malloc(2*sizeof(Fl_Widget*));
if (index) {array_[0] = t; array_[1] = &o;}
else {array_[0] = &o; array_[1] = t;}
} else {
if (!(children_ & (children_-1))) array_ = (Fl_Widget**)realloc((void*)array_,
2*children_*sizeof(Fl_Widget*));
int j; for (j = children_; j > index; j--) array_[j] = array_[j-1];
array_[j] = &o;
}
children_++;
init_sizes();
}
void Fl_Group::add(Fl_Widget &o) {insert(o, children_);}
void Fl_Group::on_remove(int index) {
(void)index;
}
void Fl_Group::remove(int index) {
if (index < 0 || index >= children_) return;
on_remove(index);
Fl_Widget &o = *child(index);
if (&o == savedfocus_) savedfocus_ = 0;
if (&o == resizable_) resizable_ = this;
if (o.parent_ == this) { o.parent_ = 0;
}
children_--;
if (children_ == 1) { Fl_Widget *t = array_[!index];
free((void*)array_);
child1_ = t;
} else if (children_ > 1) { for (; index < children_; index++) array_[index] = array_[index+1];
}
init_sizes();
}
void Fl_Group::remove(Fl_Widget &o) {
if (!children_) return;
int i = find(o);
if (i < children_) remove(i);
}
int Fl_Group::delete_child(int index) {
if (index < 0 || index >= children_)
return 1;
Fl_Widget *w = child(index);
remove(index);
delete w;
return 0;
}
void Fl_Group::init_sizes() {
delete[] bounds_;
bounds_ = 0;
delete[] sizes_; sizes_ = 0; }
Fl_Rect* Fl_Group::bounds() {
if (!bounds_) {
Fl_Rect* p = bounds_ = new Fl_Rect[children_+2];
if (as_window())
p[0] = Fl_Rect(w(),h()); else
p[0] = Fl_Rect(this);
int left = p->x(); int top = p->y();
int right = p->r();
int bottom = p->b();
Fl_Widget* r = resizable();
if (r && r != this) { int t;
t = r->x(); if (t > left) left = t;
t +=r->w(); if (t < right) right = t;
t = r->y(); if (t > top) top = t;
t +=r->h(); if (t < bottom) bottom = t;
}
p[1] = Fl_Rect(left, top, right-left, bottom-top);
p += 2;
Fl_Widget*const* a = array();
for (int i=children_; i--;) {
*p++ = Fl_Rect(*a++);
}
}
return bounds_;
}
int* Fl_Group::sizes()
{
if (sizes_) return sizes_;
int* pi = sizes_ = new int[4*(children_+2)];
Fl_Rect *rb = bounds();
for (int i = 0; i < children_+2; i++, rb++) {
*pi++ = rb->x();
*pi++ = rb->r();
*pi++ = rb->y();
*pi++ = rb->b();
}
return sizes_;
}
void Fl_Group::resize(int X, int Y, int W, int H) {
int dx = X - x();
int dy = Y - y();
int dw = W - w();
int dh = H - h();
Fl_Rect* p = bounds();
Fl_Widget::resize(X, Y, W, H);
if (!resizable() || (dw==0 && dh==0)) {
if (as_window())
dx = dy = 0;
if (Fl_Window::is_a_rescale() || dx || dy) {
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_) {
dx = X - p->x();
dw = W - p->w();
dy = Y - p->y();
dh = H - p->h();
if (as_window())
dx = dy = 0;
p++;
int RL = p->x();
int RR = RL + p->w();
int RT = p->y();
int RB = RT + p->h();
p++;
Fl_Widget*const* a = array();
for (int i = children_; i--; p++) {
Fl_Widget* o = *a++;
int L = p->x();
int R = L + p->w();
int T = p->y();
int B = T + p->h();
if (L >= RR) L += dw;
else if (L > RL) L += dw * (L-RL) / (RR-RL);
if (R >= RR) R += dw;
else if (R > RL) R += dw * (R-RL) / (RR-RL);
if (T >= RB) T += dh;
else if (T > RT) T += dh * (T-RT) / (RB-RT);
if (B >= RB) B += dh;
else if (B > RT) B += dh * (B-RT) / (RB-RT);
o->resize(L+dx, T+dy, R-L, B-T);
}
} }
void Fl_Group::draw_children() {
Fl_Widget*const* a = array();
if (clip_children()) {
fl_push_clip(x() + Fl::box_dx(box()),
y() + Fl::box_dy(box()),
w() - Fl::box_dw(box()),
h() - Fl::box_dh(box()));
}
if (damage() & ~FL_DAMAGE_CHILD) { for (int i=children_; i--;) {
Fl_Widget& o = **a++;
draw_child(o);
draw_outside_label(o);
}
} else { for (int i=children_; i--;) update_child(**a++);
}
if (clip_children()) fl_pop_clip();
}
void Fl_Group::draw() {
if (damage() & ~FL_DAMAGE_CHILD) { draw_box();
draw_label();
}
draw_children();
}
void Fl_Group::update_child(Fl_Widget& widget) const {
if (widget.damage() && widget.visible() && widget.type() < FL_WINDOW &&
fl_not_clipped(widget.x(), widget.y(), widget.w(), widget.h())) {
widget.draw();
widget.clear_damage();
}
}
void Fl_Group::draw_child(Fl_Widget& widget) const {
if (widget.visible() && widget.type() < FL_WINDOW &&
fl_not_clipped(widget.x(), widget.y(), widget.w(), widget.h())) {
widget.clear_damage(FL_DAMAGE_ALL);
widget.draw();
widget.clear_damage();
}
}
void Fl_Group::draw_outside_label(const Fl_Widget& widget) const {
if (!widget.visible()) return;
if (!(widget.align()&15) || (widget.align() & FL_ALIGN_INSIDE)) return;
Fl_Align a = widget.align();
int X = widget.x();
int Y = widget.y();
int W = widget.w();
int H = widget.h();
int wx, wy;
if (const_cast<Fl_Group*>(this)->as_window()) {
wx = wy = 0;
} else {
wx = x(); wy = y();
}
if ( (a & FL_ALIGN_POSITION_MASK) == FL_ALIGN_LEFT_TOP ) {
a = (a &(~FL_ALIGN_POSITION_MASK) ) | FL_ALIGN_TOP_RIGHT;
X = wx;
W = widget.x()-X-3;
} else if ( (a & FL_ALIGN_POSITION_MASK) == FL_ALIGN_LEFT_BOTTOM ) {
a = (a &(~FL_ALIGN_POSITION_MASK) ) | FL_ALIGN_BOTTOM_RIGHT;
X = wx;
W = widget.x()-X-3;
} else if ( (a & FL_ALIGN_POSITION_MASK) == FL_ALIGN_RIGHT_TOP ) {
a = (a &(~FL_ALIGN_POSITION_MASK) ) | FL_ALIGN_TOP_LEFT;
X = X+W+3;
W = wx+this->w()-X;
} else if ( (a & FL_ALIGN_POSITION_MASK) == FL_ALIGN_RIGHT_BOTTOM ) {
a = (a &(~FL_ALIGN_POSITION_MASK) ) | FL_ALIGN_BOTTOM_LEFT;
X = X+W+3;
W = wx+this->w()-X;
} else if (a & FL_ALIGN_TOP) {
a ^= FL_ALIGN_TOP;
a |= FL_ALIGN_BOTTOM;
Y = wy;
H = widget.y()-Y;
} else if (a & FL_ALIGN_BOTTOM) {
a ^= FL_ALIGN_BOTTOM;
a |= FL_ALIGN_TOP;
Y = Y+H;
H = wy+h()-Y;
} else if (a & FL_ALIGN_LEFT) {
a ^= FL_ALIGN_LEFT;
a |= FL_ALIGN_RIGHT;
X = wx;
W = widget.x()-X-3;
} else if (a & FL_ALIGN_RIGHT) {
a ^= FL_ALIGN_RIGHT;
a |= FL_ALIGN_LEFT;
X = X+W+3;
W = wx+this->w()-X;
}
widget.draw_label(X,Y,W,H,(Fl_Align)a);
}