#include <FL/Fl_Tile.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Rect.H>
#include <stdlib.h>
static Fl_Cursor Fl_Tile_cursors[4] = {
FL_CURSOR_DEFAULT, FL_CURSOR_WE, FL_CURSOR_NS, FL_CURSOR_MOVE };
static int fl_min(int a, int b) { return a<b ? a : b; }
static int fl_max(int a, int b) { return a>b ? a : b; }
void Fl_Tile::request_shrink_l(int old_l, int &new_l, Fl_Rect *final_size) {
Fl_Rect *p = bounds();
int min_l = new_l;
for (int i=0; i<children(); i++) {
Fl_Rect *ri = p+i+2;
if (ri->x() == old_l) {
if (ri->w() == 0) {
if (final_size) final_size[i].x(new_l);
} else {
int min_w = size_range_[i].minw;
int may_l = fl_min(new_l, ri->r()-min_w); int new_l_right = ri->r();
if (may_l < new_l) {
int missing_w = new_l - may_l;
new_l_right = ri->r() + missing_w;
request_shrink_l(ri->r(), new_l_right, NULL);
new_l_right = fl_min(new_l_right, p->r());
if (final_size) {
request_shrink_l(ri->r(), new_l_right, final_size);
request_grow_r(ri->r(), new_l_right, final_size);
}
min_l = fl_min(min_l, new_l_right - min_w);
}
if (final_size) {
final_size[i].x(new_l);
final_size[i].w(new_l_right-new_l);
}
}
}
}
new_l = min_l;
}
void Fl_Tile::request_shrink_r(int old_r, int &new_r, Fl_Rect *final_size) {
Fl_Rect *p = bounds();
int min_r = new_r;
for (int i=0; i<children(); i++) {
Fl_Rect *ri = p+i+2;
if (ri->r() == old_r) {
if (ri->w() == 0) {
if (final_size) final_size[i].x(new_r);
} else {
int min_w = size_range_[i].minw;
int may_r = fl_max(new_r, ri->x()+min_w); int new_r_left = ri->x();
if (may_r > new_r) {
int missing_w = may_r - new_r;
new_r_left = ri->x() - missing_w;
request_shrink_r(ri->x(), new_r_left, NULL);
new_r_left = fl_max(new_r_left, p->x());
if (final_size) {
request_shrink_r(ri->x(), new_r_left, final_size);
request_grow_l(ri->x(), new_r_left, final_size);
}
min_r = fl_max(min_r, new_r_left + min_w);
}
if (final_size) {
final_size[i].x(new_r_left);
final_size[i].w(new_r-new_r_left);
}
}
}
}
new_r = min_r;
}
void Fl_Tile::request_shrink_t(int old_t, int &new_t, Fl_Rect *final_size) {
Fl_Rect *p = bounds();
int min_y = new_t;
for (int i=0; i<children(); i++) {
Fl_Rect *ri = p+i+2;
if (ri->y() == old_t) {
if (ri->h() == 0) {
if (final_size) final_size[i].y(new_t);
} else {
int min_h = size_range_[i].minh;
int may_y = fl_min(new_t, ri->b()-min_h); int new_y_below = ri->b();
if (may_y < new_t) {
int missing_h = new_t - may_y;
new_y_below = ri->b() + missing_h;
request_shrink_t(ri->b(), new_y_below, NULL);
new_y_below = fl_min(new_y_below, p->b());
if (final_size) {
request_shrink_t(ri->b(), new_y_below, final_size);
request_grow_b(ri->b(), new_y_below, final_size);
}
min_y = fl_min(min_y, new_y_below - min_h);
}
if (final_size) {
final_size[i].y(new_t);
final_size[i].h(new_y_below-new_t);
}
}
}
}
new_t = min_y;
}
void Fl_Tile::request_shrink_b(int old_b, int &new_b, Fl_Rect *final_size) {
Fl_Rect *p = bounds();
int min_b = new_b;
for (int i=0; i<children(); i++) {
Fl_Rect *ri = p+i+2;
if (ri->b() == old_b) {
if (ri->h() == 0) {
if (final_size) final_size[i].y(new_b);
} else {
int min_h = size_range_[i].minh;
int may_b = fl_max(new_b, ri->y()+min_h); int new_b_above = ri->y();
if (may_b > new_b) {
int missing_h = may_b - new_b;
new_b_above = ri->y() - missing_h;
request_shrink_b(ri->y(), new_b_above, NULL);
new_b_above = fl_max(new_b_above, p->y());
if (final_size) {
request_shrink_b(ri->y(), new_b_above, final_size);
request_grow_t(ri->y(), new_b_above, final_size);
}
min_b = fl_max(min_b, new_b_above + min_h);
}
if (final_size) {
final_size[i].y(new_b_above);
final_size[i].h(new_b-new_b_above);
}
}
}
}
new_b = min_b;
}
void Fl_Tile::request_grow_l(int old_l, int &new_l, Fl_Rect *final_size) {
Fl_Rect *p = bounds();
for (int i=0; i<children(); i++) {
Fl_Rect *ri = p+i+2;
if (ri->x() == old_l) {
final_size[i].w(final_size[i].r() - new_l);
final_size[i].x(new_l);
}
}
}
void Fl_Tile::request_grow_r(int old_r, int &new_r, Fl_Rect *final_size) {
Fl_Rect *p = bounds();
for (int i=0; i<children(); i++) {
Fl_Rect *ri = p+i+2;
if (ri->r() == old_r) {
final_size[i].r(new_r);
}
}
}
void Fl_Tile::request_grow_t(int old_t, int &new_t, Fl_Rect *final_size) {
Fl_Rect *p = bounds();
for (int i=0; i<children(); i++) {
Fl_Rect *ri = p+i+2;
if (ri->y() == old_t) {
final_size[i].h(final_size[i].b() - new_t);
final_size[i].y(new_t);
}
}
}
void Fl_Tile::request_grow_b(int old_b, int &new_b, Fl_Rect *final_size) {
Fl_Rect *p = bounds();
for (int i=0; i<children(); i++) {
Fl_Rect *ri = p+i+2;
if (ri->b() == old_b) {
final_size[i].b(new_b);
}
}
}
void Fl_Tile::move_intersection(int oldx, int oldy, int newx, int newy) {
if (size_range_) {
drag_intersection(oldx, oldy, newx, newy);
init_sizes();
} else {
Fl_Widget*const* a = array();
Fl_Rect *p = bounds();
p += 2; for (int i=children(); i--; p++) {
Fl_Widget* o = *a++;
if (o == resizable()) continue;
int X = o->x();
int R = X+o->w();
if (oldx) {
int t = p->x();
if (t == oldx || (t>oldx && X<newx) || (t<oldx && X>newx) ) X = newx;
t = p->r();
if (t == oldx || (t>oldx && R<newx) || (t<oldx && R>newx) ) R = newx;
}
int Y = o->y();
int B = Y+o->h();
if (oldy) {
int t = p->y();
if (t == oldy || (t>oldy && Y<newy) || (t<oldy && Y>newy) ) Y = newy;
t = p->b();
if (t == oldy || (t>oldy && B<newy) || (t<oldy && B>newy) ) B = newy;
}
o->damage_resize(X,Y,R-X,B-Y);
}
}
}
void Fl_Tile::drag_intersection(int oldx, int oldy, int newx, int newy) {
if (size_range_) {
int i;
Fl_Rect *p = bounds();
Fl_Rect *final_size = new Fl_Rect[children()];
for (i = 0; i < children(); i++) {
final_size[i] = p[i+2];
}
if ((oldy != 0) && (oldy != newy)) {
if (newy <= oldy) { int new_y = newy;
request_shrink_b(oldy, new_y, NULL); request_shrink_b(oldy, new_y, final_size); request_grow_t(oldy, new_y, final_size); }
if (newy > oldy) { int new_y = newy;
request_shrink_t(oldy, new_y, NULL); request_shrink_t(oldy, new_y, final_size); request_grow_b(oldy, new_y, final_size); }
}
if ((oldx != 0) && (oldx != newx)) {
if (newx <= oldx) { int new_x = newx;
request_shrink_r(oldx, new_x, NULL); request_shrink_r(oldx, new_x, final_size); request_grow_l(oldx, new_x, final_size); }
if (newx > oldx) { int new_x = newx;
request_shrink_l(oldx, new_x, NULL); request_shrink_l(oldx, new_x, final_size); request_grow_r(oldx, new_x, final_size); }
}
for (i = 0; i < children(); i++) {
Fl_Rect &r = final_size[i];
child(i)->damage_resize(r.x(), r.y(), r.w(), r.h());
}
delete[] final_size;
} else {
move_intersection(oldx, oldy, newx, newy);
}
}
void Fl_Tile::resize(int X,int Y,int W,int H) {
if (size_range_) {
int dx = X - x();
int dy = Y - y();
int dw = w() - W;
int dh = h() - H;
if ((dw==0) && (dh==0)) {
Fl_Group::resize(X, Y, W, H);
init_sizes();
redraw();
return;
}
if ((dx!=0) || (dy!=0)) {
for (int i = 0; i < children(); i++) {
Fl_Widget *c = child(i);
c->position(c->x()+dx, c->y()+dy);
}
}
init_sizes();
Fl_Rect *p = bounds();
int bbr = X, bbb = Y;
for (int i = 0; i < children(); i++) {
bbr = fl_max(bbr, p[i+2].r());
bbb = fl_max(bbb, p[i+2].b());
}
int r2 = X+W;
request_shrink_r(bbr, r2, NULL);
dw = bbr - r2;
int b2 = Y+H;
request_shrink_b(bbb, b2, NULL);
dh = bbb - b2;
if ((dw!=0) || (dh!=0)) {
Fl_Widget *r = resizable();
int trr = 0, trb = 0;
if (r) {
trr = r->x() + r->w() - dw;
trb = r->y() + r->h() - dh;
}
if ((dw < 0) && (dh < 0)) {
move_intersection(bbr, bbb, bbr-dw, bbb-dh);
} else if (dw < 0) {
move_intersection(bbr, bbb, bbr-dw, bbb);
} else if (dh < 0) {
move_intersection(bbr, bbb, bbr, bbb-dh);
}
if (r) {
int rr = r->x() + r->w(), rb = r->y() + r->h();
move_intersection(rr, rb, trr, trb);
}
if ((dw > 0) && (dh > 0)) {
move_intersection(bbr, bbb, bbr-dw, bbb-dh);
} else if (dw > 0) {
move_intersection(bbr, bbb, bbr-dw, bbb);
} else if (dh > 0) {
move_intersection(bbr, bbb, bbr, bbb-dh);
}
init_sizes();
}
if (Fl_Window::is_a_rescale())
Fl_Group::resize(X, Y, W, H);
else
Fl_Widget::resize(X, Y, W, H);
return;
}
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);
int OR = p[1].r(); int NR = X+W-(p[0].r()-OR); int OB = p[1].b(); int NB = Y+H-(p[0].b()-OB);
Fl_Widget*const* a = array();
p += 2;
for (int i=children(); i--; p++) {
Fl_Widget* o = *a++;
int xx = o->x()+dx;
int R = xx+o->w();
if (p->x() >= OR) xx += dw; else if (xx > NR) xx = NR;
if (p->r() >= OR) R += dw; else if (R > NR) R = NR;
int yy = o->y()+dy;
int B = yy+o->h();
if (p->y() >= OB) yy += dh; else if (yy > NB) yy = NB;
if (p->b() >= OB) B += dh; else if (B > NB) B = NB;
o->resize(xx,yy,R-xx,B-yy);
}
}
void Fl_Tile::set_cursor(int n) {
if (n < 0 || n > 3) n = 0; if (cursor_ == n) return; cursor_ = n; if (window())
window()->cursor(cursor(n));
}
#define DRAGH 1
#define DRAGV 2
#define GRABAREA 4
int Fl_Tile::handle(int event) {
static int sdrag;
static int sdx, sdy;
static int sx, sy;
int mx = Fl::event_x();
int my = Fl::event_y();
switch (event) {
case FL_MOVE:
case FL_ENTER:
case FL_PUSH:
if (!active()) break; {
int mindx = 100;
int mindy = 100;
int oldx = 0;
int oldy = 0;
Fl_Widget*const* a = array();
Fl_Rect *q = bounds();
Fl_Rect *p = q+2;
for (int i=children(); i--; p++) {
Fl_Widget* o = *a++;
if (!size_range_ && o == resizable()) continue;
if (p->r() < q->r() && o->y()<=my+GRABAREA && o->y()+o->h()>=my-GRABAREA) {
int t = mx - (o->x()+o->w());
if (abs(t) < mindx) {
sdx = t;
mindx = abs(t);
oldx = p->r();
}
}
if (p->b() < q->b() && o->x()<=mx+GRABAREA && o->x()+o->w()>=mx-GRABAREA) {
int t = my - (o->y()+o->h());
if (abs(t) < mindy) {
sdy = t;
mindy = abs(t);
oldy = p->b();
}
}
}
sdrag = 0; sx = sy = 0;
if (mindx <= GRABAREA) {sdrag = DRAGH; sx = oldx;}
if (mindy <= GRABAREA) {sdrag |= DRAGV; sy = oldy;}
set_cursor(sdrag);
if (sdrag) return 1;
return Fl_Group::handle(event);
}
case FL_LEAVE:
set_cursor(0); break;
case FL_DRAG:
case FL_RELEASE: {
if (!sdrag) break;
Fl_Widget* r = resizable();
if (size_range_ || !r) r = this;
int newx;
if (sdrag&DRAGH) {
newx = Fl::event_x()-sdx;
if (newx < r->x()) newx = r->x();
else if (newx > r->x()+r->w()) newx = r->x()+r->w();
} else
newx = sx;
int newy;
if (sdrag&DRAGV) {
newy = Fl::event_y()-sdy;
if (newy < r->y())
newy = r->y();
else if (newy > r->y()+r->h())
newy = r->y()+r->h();
} else {
newy = sy;
}
if (event == FL_DRAG) {
drag_intersection(sx, sy, newx, newy);
set_changed();
do_callback(FL_REASON_DRAGGED);
} else {
move_intersection(sx, sy, newx, newy);
do_callback(FL_REASON_CHANGED);
}
return 1;
}
}
return Fl_Group::handle(event);
}
int Fl_Tile::on_insert(Fl_Widget *candidate, int index) {
if (size_range_) {
if (index >= size_range_capacity_) {
size_range_capacity_ = (index+8) & ~7; size_range_ = (Size_Range*)::realloc(size_range_, sizeof(Size_Range)*size_range_capacity_);
}
if (index < size_range_size_)
memmove(size_range_+index+1, size_range_+index, sizeof(Size_Range)*(size_range_size_-index));
size_range_size_++;
size_range_[index].minw = default_min_w_;
size_range_[index].minh = default_min_h_;
size_range_[index].maxw = 0x7FFFFFFF;
size_range_[index].maxh = 0x7FFFFFFF;
}
return index;
}
int Fl_Tile::on_move(int oldIndex, int newIndex) {
if (size_range_) {
int delta = newIndex - oldIndex;
if (delta) {
Size_Range size_bak = size_range_[oldIndex];
if (delta > 0)
memmove(size_range_+oldIndex, size_range_+oldIndex+1, sizeof(Size_Range)*delta);
else
memmove(size_range_+newIndex+1, size_range_+newIndex, sizeof(Size_Range)*-delta);
size_range_[newIndex] = size_bak;
}
}
return newIndex;
}
void Fl_Tile::on_remove(int index) {
if (size_range_) {
int num_trailing = size_range_size_-index-1;
if ((index >= 0) && (index < size_range_size_) && (num_trailing > 0))
memmove(size_range_+index, size_range_+index+1, sizeof(Size_Range)*num_trailing);
size_range_size_--;
}
}
void Fl_Tile::size_range(int index, int minw, int minh, int maxw, int maxh) {
if (!size_range_)
init_size_range();
if ((index >= 0) && (index < children())) {
size_range_[index].minw = minw;
size_range_[index].minh = minh;
size_range_[index].maxw = maxw;
size_range_[index].maxh = maxh;
}
}
void Fl_Tile::size_range(Fl_Widget *w , int minw, int minh, int maxw, int maxh) {
int index = find(w);
if ((index >= 0) && (index < children()))
size_range(index, minw, minh, maxw, maxh);
}
void Fl_Tile::init_size_range(int default_min_w, int default_min_h) {
if (default_min_w > 0) default_min_w_ = default_min_w;
if (default_min_h > 0) default_min_h_ = default_min_h;
if (!size_range_) {
size_range_size_ = children();
size_range_capacity_ = (size_range_size_+8) & ~7; size_range_ = (Size_Range*)::realloc(size_range_, sizeof(Size_Range)*size_range_capacity_);
for (int i=0; i<size_range_size_; i++) {
size_range_[i].minw = default_min_w_;
size_range_[i].minh = default_min_h_;
size_range_[i].maxw = 0x7FFFFFFF;
size_range_[i].maxh = 0x7FFFFFFF;
}
}
}
Fl_Tile::Fl_Tile(int X,int Y,int W,int H,const char*L)
: Fl_Group(X,Y,W,H,L),
cursor_(0),
cursors_(Fl_Tile_cursors),
size_range_(NULL),
size_range_size_(0),
size_range_capacity_(0),
default_min_w_(GRABAREA),
default_min_h_(GRABAREA)
{
}
Fl_Tile::~Fl_Tile() {
if (size_range_)
::free(size_range_);
}