//| //| Dockable window widget for the Fast Light Tool Kit (FLTK). //| //| Copyright 1998-1999 by Vivality llc, Venice, CA, U.S.A. //| and Matthias Melcher. //| //| This library is free software; you can redistribute it and/or //| modify it under the terms of the GNU Library General Public //| License as published by the Free Software Foundation; either //| version 2 of the License, or (at your option) any later version. //| //| This library is distributed in the hope that it will be useful, //| but WITHOUT ANY WARRANTY; without even the implied warranty of //| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //| Library General Public License for more details. //| //| You should have received a copy of the GNU Library General Public //| License along with this library; if not, write to the Free Software //| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 //| USA. //| //| Please report all bugs and problems to "matthias@mediaone.net". //| // There are two widget classes in this module. // Fl_Dock is derived from Fl_New_Tile and adds some functionality. // - The user can move all widgets around within the Fl_Dock. // - The user can drag widgets out of the Fl_Dock. They will appear as // seperate windows on the desktop (Fl_Dockable) // - The user can drag those windows back into the Fl_Dock. // Fl_Dock tries to make smart decissions about placing. // Fl_Dockable is derived from Fl_Group. The Fl_Dock takes care of generating // Fl_Dockable widgets // planned: docking into an FL_Tab // planned: callback to let application know we docked/undocked etc. #include "Fl_Dock.H" #include #include #include #include #include #include #define N(t) (t->y()) #define S(t) (t->y()+t->h()) #define W(t) (t->x()) #define E(t) (t->x()+t->w()) //|---- //| Fl_Dock::Fl_Dock(int x, int y, int w, int h, char *t) : Fl_New_Tile(x, y, w, h, t) { mergeList = 0L; nMergeList = NMergeList = 0L; replaceList = 0L; nReplaceList = NReplaceList = 0L; } //|---- //| Fl_Dock::~Fl_Dock() { if (mergeList) free(mergeList); if (replaceList) free(replaceList); } //|---- //| return 1, if the widget can merge with another widget if this one is deleted //| int Fl_Dock::canMerge(Fl_Widget *w) { for (int i=0; i=0; i--) { //: search backwards, so that we will replace the most recent widget Fl_Widget *b = child(i); if (canReplace(b)) return b; } return 0; } //| //| create a (menu) window that takes the group we just removed from the dock //| void Fl_Dock::reparent(Fl_Widget *w, int x, int y) { int W = w->w(), H = w->h(); w->hide(); Fl_Group::current(0L); //: make sure, that the window is actually a desktop window Fl_Double_Window *win = new Fl_Double_Window(x, y, W, H); //++ should we make this a top window without frames?? if (!canMerge(w)) win->set_non_modal(); //+ stupid assumtion that non-mergeables are toolboxes... win->size_range(W, H, 0, 0); Fl_Dockable *d = new Fl_Dockable(0, 0, W, H); d->end(); win->end(); d->add(w); d->docks_to(this); w->position(0, 0); win->resizable(w); win->show(); win->resize(x, y, W, H); w->show(); } //| //| put any widget into the dock at some location //| void Fl_Dock::putIn(Fl_Widget *a, int x, int y) { char pos = 0; Fl_Widget *dst = dockWhere(x, y, pos); putIn(a, dst, pos); } //| //| protected: do it the cryptic way //| void Fl_Dock::putIn(Fl_Widget *a, Fl_Widget *b, char pos) { int delta; if (a==b) return; if (find(a)!=children()) remove(a); add(a); if (canReplace(b)) { a->damage_resize(b->x(), b->y(), b->w(), b->h()); remove(b); init_sizes(); return; } switch (pos) { case 0: //: North delta = ( a->h()>b->h()/2 ) ? b->h()/2 : a->h(); a->damage_resize(b->x(), b->y(), b->w(), delta); b->damage_resize(b->x(), b->y()+delta, b->w(), b->h()-delta); break; case 1: //: East delta = ( a->w()>b->w()/2 ) ? b->w()/2 : b->w()-a->w(); a->damage_resize(b->x()+delta, b->y(), b->w()-delta, b->h()); b->damage_resize(b->x(), b->y(), delta, b->h()); break; case 2: //: South delta = ( a->h()>b->h()/2 ) ? b->h()/2 : b->h()-a->h(); a->damage_resize(b->x(), b->y()+delta, b->w(), b->h()-delta); b->damage_resize(b->x(), b->y(), b->w(), delta); break; case 3: //: West delta = ( a->w()>b->w()/2 ) ? b->w()/2 : a->w(); a->damage_resize(b->x(), b->y(), delta, b->h()); b->damage_resize(b->x()+delta, b->y(), b->w()-delta, b->h()); break; } init_sizes(); } //| //| protected: find the widget we will drop the Dockable next to //| and find the border we will drop it on (N, E, W, S) //| // +-------------+ // |\ N /| // |W >-------< E| // |/ S \| // +-------------+ Fl_Widget *Fl_Dock::dockWhere(int x, int y, char &pos) { for (int i=0; i=W(b)) && (y>=N(b)) && (x<=E(b)) && (y<=S(b)) ) { int w = b->w(), h = b->h(); x -= b->x(); y -= b->y(); if (w>h) { if ((x<=h/2)&&(x=w-h/2)&&(w-xh/2)?2:0; } else { if ((y<=w/2)&&(y=h-w/2)&&(h-yw/2)?1:3; } return b; } } pos = -1; return 0; } //| //| try to remove the given widget as smart as possible from the docking view //| char Fl_Dock::takeOut(Fl_Widget *a) { if (!contains(a)) return 0; if ((W(a)==x())&&(N(a)==y())&&(E(a)==x()+w())&&(S(a)==y()+h())) return 0; //: reject takeOut uchar pattern = 0x0f, best = 0x0f; for (int i=0; iN(a)) pattern &= ~0x01; //: left side can't move if (S(b)>S(a) && N(b)=N(a)) best &= ~0x01; //: left side can't move } if ( (E(a)==W(b)) ) { //: touching on the right (e) if (N(b)N(a)) pattern &= ~0x02; //: right side can't move if (S(b)>S(a) && N(b)=N(a)) best &= ~0x02; //: right side can't move } if ( (N(a)==S(b)) ) { //: touching on the top (n) if (W(b)W(a)) pattern &= ~0x04; //: top side can't move if (E(b)>E(a) && W(b)=W(a)) best &= ~0x04; //: top side can't move } if ( (S(a)==N(b)) ) { //: touching on the bottom (s) if (W(b)W(a)) pattern &= ~0x08; //: bottom side can't move if (E(b)>E(a) && W(b)=W(a)) best &= ~0x08; //: bottom side can't move } } if (W(a)==x()) pattern &= ~0x01; if (N(a)==y()) pattern &= ~0x04; if (E(a)==x()+w()) pattern &= ~0x02; if (S(a)==y()+h()) pattern &= ~0x08; best = best & pattern; //printf("Pattern %02x\n", pattern); //printf("%c%c %c%c\n", (pattern&0x80)?'_':' ', (pattern&0x01)?'|':' ', (pattern&0x02)?'|':' ', (pattern&0x04)?'_':' '); //printf("%c O %c\n", (pattern&0x40)?'_':' ', (pattern&0x08)?'_':' '); //printf(" %c %c\n\n", (pattern&0x20)?'|':' ', (pattern&0x10)?'|':' '); if (best) pattern = best; if (canMerge(a)&&!best) { Fl_Group *gp = new Fl_Group(a->x(), a->y(), a->w(), a->h(), ""); gp->color(FL_DARK1); gp->box(FL_FLAT_BOX); gp->align(FL_ALIGN_INSIDE); gp->labelfont(FL_HELVETICA_BOLD_ITALIC); gp->labelcolor(FL_DARK2); gp->labelsize(48); gp->end(); add(gp); canMerge(gp, 1); canReplace(gp, 1); return 1; } // align all other widgets if ((pattern&0x04) == 0x04) { //: resize all north //printf("Resize north\n"); for (int i=0; i=W(a)) && (W(b)damage_resize(b->x(), b->y(), b->w(), b->h()+a->h()); } } else if ((pattern&0x08)==0x08) { //: resize all south //printf("Resize south\n"); for (int i=0; i=W(a)) && (W(b)damage_resize(b->x(), b->y()-a->h(), b->w(), b->h()+a->h()); } } else if ((pattern&0x01)==0x01) { //: resize all west //printf("Resize west\n"); for (int i=0; i=N(a)) && (N(b)damage_resize(b->x(), b->y(), b->w()+a->w(), b->h()); } } else if ((pattern&0x02)==0x02) { //: resize all east //printf("Resize east\n"); for (int i=0; i=N(a)) && (N(b)damage_resize(b->x()-a->w(), b->y(), b->w()+a->w(), b->h()); } } else { //: none of the moves worked: we have some cyclic dependency! //printf("Cyclic\n"); int ax=a->x(), ay=a->y(), aw=a->w(), ah=a->h(); if (a->y()==y()) position(a->x()+a->w()/2, a->y()+a->h(), a->x()+a->w()/2, a->y()); else if (a->y()+a->h()==y()+h()) position(a->x()+a->w()/2, a->y(), a->x()+a->w()/2, a->y()+a->h()); else { position(a->x()+a->w()/2, a->y(), a->x()+a->w()/2, a->y()+a->h()/2); position(a->x()+a->w()/2, a->y()+a->h(), a->x()+a->w()/2, a->y()+a->h()/2); } a->damage_resize(ax, ay, aw, ah); } return 1; } //| //| all events that drop through from the children and that are not handled by //| the Tile are good candidates for dragging around and outside the Dock //| int Fl_Dock::handle(int event) { static int x, y, w, h, n=-1, mx, my; int i, ret = Fl_New_Tile::handle(event); char pos; Fl_Widget *a, *b; if (ret==0) { switch (event) { case FL_PUSH: n = -1; for (i=0; ix(); y = child(n)->y(); w = child(n)->w(); h = child(n)->h(); window()->make_current(); fl_overlay_rect(x, y, w, h); ret = 1; } break; case FL_DRAG: if (n!=-1) { x += Fl::event_x()-mx; mx = Fl::event_x(); y += Fl::event_y()-my; my = Fl::event_y(); window()->make_current(); fl_overlay_rect(x, y, w, h); //dockWhere(Fl::event_x(), Fl::event_y(), pos); //fl_cursor(pos+1); ret = 1; } break; case FL_RELEASE: if (n!=-1) { a = child(n); fl_cursor(FL_CURSOR_DEFAULT); window()->make_current(); fl_overlay_clear(); b = dockWhere(Fl::event_x(), Fl::event_y(), pos); if ( (a!=b) && takeOut(a)) { if (!b) { reparent(a, x+window()->x(), y+window()->y()); } else { putIn(a, b, pos); } a->do_callback(a, a->user_data()); } window()->redraw(); n = -1; ret = 1; } break; } } return ret; } //| //| Events dropping through from the child (we assume ONE and only ONE child!) //| might be used to get us dropped back into the Dock //| int Fl_Dockable::handle(int event) { static int ax, ay, aw, ah, n=-1, mx, my, dx, dy; Fl_Widget *a; int ret=Fl_Group::handle(event); if (!dock_) return ret; if (ret==0) { switch (event) { case FL_PUSH: fl_cursor(FL_CURSOR_MOVE); mx = Fl::event_x(); my = Fl::event_y(); dx = dock_->window()->x()+dock_->x(); dy = dock_->window()->y()+dock_->y(); ax = window()->x()+x() - dx + dock_->x(); ay = window()->y()+y() - dy + dock_->y(); aw = w(); ah = h(); dock_->window()->make_current(); fl_overlay_rect(ax, ay, aw, ah); ret = 1; break; case FL_DRAG: ax += Fl::event_x()-mx; mx = Fl::event_x(); ay += Fl::event_y()-my; my = Fl::event_y(); dock_->window()->make_current(); fl_overlay_rect(ax, ay, aw, ah); ret = 1; break; case FL_RELEASE: dock_->window()->make_current(); fl_overlay_clear(); fl_cursor(FL_CURSOR_DEFAULT); a = child(0); if ( (Fl::event_x_root()>dx) && (Fl::event_x_root()w()) &&(Fl::event_y_root()>dy) && (Fl::event_y_root()h()) ) { dock_->putIn(a, Fl::event_x_root()-dx+dock_->x(), Fl::event_y_root()-dy+dock_->y()); delete parent(); a->do_callback(a, a->user_data()); } ret = 1; break; } } return ret; } #undef N #undef E #undef S #undef W