diff options
Diffstat (limited to 'fluid/Fl_Window_Type.cxx')
| -rw-r--r-- | fluid/Fl_Window_Type.cxx | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/fluid/Fl_Window_Type.cxx b/fluid/Fl_Window_Type.cxx new file mode 100644 index 000000000..044c8233f --- /dev/null +++ b/fluid/Fl_Window_Type.cxx @@ -0,0 +1,666 @@ +/* Fl_Window_Type.C + + The widget describing an Fl_Window. This is also all the code + for interacting with the overlay, which allows the user to + select, move, and resize the children widgets. + +*/ + +#include <FL/Fl.H> +#include <FL/Fl_Overlay_Window.H> +#include <FL/fl_message.H> +#include <FL/fl_draw.H> +#include <FL/Fl_Menu_Item.H> +#include "Fl_Widget_Type.H" +#include <math.h> +#include <stdlib.h> +#include "alignment_panel.H" +#include <stdio.h> + +int gridx = 5; +int gridy = 5; +int snap = 3; + +void alignment_cb(Fl_Input *i, long v) { + int n = atoi(i->value()); + if (n < 0) n = 0; + switch (v) { + case 1: gridx = n; break; + case 2: gridy = n; break; + case 3: snap = n; break; + } +} + +extern const char* header_file_name; +extern const char* code_file_name; + +void show_alignment_cb(Fl_Widget *, void *) { + make_alignment_window(); + header_file_input->value(header_file_name); + code_file_input->value(code_file_name); + char buf[128]; + sprintf(buf,"%d",gridx); horizontal_input->value(buf); + sprintf(buf,"%d",gridy); vertical_input->value(buf); + sprintf(buf,"%d",snap); snap_input->value(buf); + alignment_window->show(); +} + +void header_input_cb(Fl_Input* i, void*) { + header_file_name = i->value(); +} +void code_input_cb(Fl_Input* i, void*) { + code_file_name = i->value(); + printf("code file name set to %s\n", code_file_name); +} + +//////////////////////////////////////////////////////////////// + +static Fl_Menu_Item window_type_menu[] = { + {"Single",0,0,(void*)FL_WINDOW}, + {"Double",0,0,(void*)(FL_WINDOW+1)}, + {0}}; +class Fl_Window_Type : public Fl_Widget_Type { + Fl_Menu_Item* subtypes() {return window_type_menu;} + + friend class Overlay_Window; + int mx,my; // mouse position during dragging + int x1,y1; // initial position of selection box + int bx,by,br,bt; // bounding box of selection + int dx,dy; + int drag; // which parts of bbox are being moved + int numselected; // number of children selected + enum {LEFT=1,RIGHT=2,BOTTOM=4,TOP=8,DRAG=16,BOX=32}; + void draw_overlay(); + void newdx(); + void newposition(Fl_Widget_Type *,int &x,int &y,int &w,int &h); + int handle(int); + virtual void setlabel(const char *); + void write_code1(); + void write_code2(); + Fl_Widget_Type *_make() {return 0;} // we don't call this + Fl_Widget *widget(int,int,int,int) {return 0;} + int recalc; // set by fix_overlay() + +public: + + uchar modal, non_modal; + + Fl_Type *make(); + virtual const char *type_name() {return "Fl_Window";} + + void open(); + + void fix_overlay(); // update the bounding box, etc + + virtual void write_properties(); + virtual void read_property(const char *); + virtual int read_fdesign(const char*, const char*); + + void add_child(Fl_Type*, Fl_Type*); + void move_child(Fl_Type*, Fl_Type*); + void remove_child(Fl_Type*); + + int is_parent() const {return 1;} + int is_group() const {return 1;} + int is_window() const {return 1;} +}; + +static int overlays_invisible; + +// The following Fl_Widget is used to simulate the windows. It has +// an overlay for the fluid ui, and special-cases the FL_NO_BOX. + +class Overlay_Window : public Fl_Overlay_Window { + void draw(); + void draw_overlay(); +public: + Fl_Window_Type *window; + int handle(int); + Overlay_Window(int w,int h) : Fl_Overlay_Window(w,h) {Fl_Group::current(0);} + void resize(int,int,int,int); +}; +void Overlay_Window::draw() { + const int CHECKSIZE = 8; + // see if box is clear or a frame or rounded: + if ((damage()&128) && + (!box() || (box()>=4&&!(box()&2)) || box()>=_FL_ROUNDED_BOX)) { + // if so, draw checkerboard so user can see what areas are clear: + for (int y = 0; y < h(); y += CHECKSIZE) + for (int x = 0; x < w(); x += CHECKSIZE) { + fl_color(((y/(2*CHECKSIZE))&1) != ((x/(2*CHECKSIZE))&1) ? + FL_WHITE : FL_BLACK); + fl_rectf(x,y,CHECKSIZE,CHECKSIZE); + } + } + Fl_Overlay_Window::draw(); +} + +void Overlay_Window::draw_overlay() { + window->draw_overlay(); +} +int Overlay_Window::handle(int e) { + return window->handle(e); +} + +Fl_Type *Fl_Window_Type::make() { + Fl_Type *p = Fl_Type::current; + while (p && !p->is_code_block()) p = p->parent; + if (!p) { + fl_message("Please select a function"); + return 0; + } + Fl_Window_Type *o = new Fl_Window_Type(); + if (!this->o) {// template widget + this->o = new Fl_Window(100,100); + Fl_Group::current(0); + } + o->factory = this; + o->drag = 0; + o->numselected = 0; + Overlay_Window *w = new Overlay_Window(100,100); + w->window = o; + o->o = w; + o->add(p); + o->modal = 0; + o->non_modal = 0; + return o; +} + +void Fl_Window_Type::add_child(Fl_Type* cc, Fl_Type* before) { + Fl_Widget_Type* c = (Fl_Widget_Type*)cc; + Fl_Widget* b = before ? ((Fl_Widget_Type*)before)->o : 0; + ((Fl_Window*)o)->insert(*(c->o), b); + o->redraw(); +} + +void Fl_Window_Type::remove_child(Fl_Type* cc) { + Fl_Widget_Type* c = (Fl_Widget_Type*)cc; + ((Fl_Window*)o)->remove(c->o); + o->redraw(); +} + +void Fl_Window_Type::move_child(Fl_Type* cc, Fl_Type* before) { + Fl_Widget_Type* c = (Fl_Widget_Type*)cc; + ((Fl_Window*)o)->remove(c->o); + Fl_Widget* b = before ? ((Fl_Widget_Type*)before)->o : 0; + ((Fl_Window*)o)->insert(*(c->o), b); + o->redraw(); +} + +//////////////////////////////////////////////////////////////// + +// Double-click on window widget shows the window, or if already shown, +// it shows the control panel. +void Fl_Window_Type::open() { + Overlay_Window *w = (Overlay_Window *)o; + if (w->shown()) { + w->show(); + Fl_Widget_Type::open(); + } else { + Fl_Widget *p = w->resizable(); + if (!p) w->resizable(w); + w->show(); + w->resizable(p); + } +} + +// control panel items: +#include "widget_panel.H" + +void modal_cb(Fl_Light_Button* i, void* v) { + if (v == LOAD) { + if (!current_widget->is_window()) {i->hide(); return;} + i->show(); + i->value(((Fl_Window_Type *)current_widget)->modal); + } else { + ((Fl_Window_Type *)current_widget)->modal = i->value(); + } +} + +void non_modal_cb(Fl_Light_Button* i, void* v) { + if (v == LOAD) { + if (!current_widget->is_window()) {i->hide(); return;} + i->show(); + i->value(((Fl_Window_Type *)current_widget)->non_modal); + } else { + ((Fl_Window_Type *)current_widget)->non_modal = i->value(); + } +} + +void border_cb(Fl_Light_Button* i, void* v) { + if (v == LOAD) { + if (!current_widget->is_window()) {i->hide(); return;} + i->show(); + i->value(((Fl_Window*)(current_widget->o))->border()); + } else { + ((Fl_Window*)(current_widget->o))->border(i->value()); + } +} + +void xclass_cb(Fl_Input* i, void* v) { + if (v == LOAD) { + if (!current_widget->is_window()) {i->hide(); return;} + i->show(); + i->value(((Fl_Widget_Type *)current_widget)->xclass); + } else { + for (Fl_Type *o = Fl_Type::first; o; o = o->next) + if (o->selected && o->is_widget()) { + Fl_Widget_Type* w = (Fl_Widget_Type*)o; + if (w->is_window() || w->is_button()) + storestring(i->value(),w->xclass); + if (w->is_window()) ((Fl_Window*)(w->o))->xclass(w->xclass); + else if (w->is_menu_item()) w->redraw(); + } + } +} + +//////////////////////////////////////////////////////////////// + +void Fl_Window_Type::setlabel(const char *n) { + if (o) ((Fl_Window *)o)->label(n); +} + +// make() is called on this widget when user picks window off New menu: +Fl_Window_Type Fl_Window_type; + +// Resize from window manager, try to resize it back to a legal size. +// This is not proper X behavior, but works on 4DWM and fvwm +void Overlay_Window::resize(int X,int Y,int W,int H) { + if (!visible() || W==w() && H==h()) { + Fl_Overlay_Window::resize(X,Y,W,H); + return; + } + int nw = gridx&&W!=w() ? ((W+gridx/2)/gridx)*gridx : W; + int nh = gridy&&H!=h() ? ((H+gridy/2)/gridy)*gridy : H; + Fl_Widget* t = resizable(); resizable(0); + Fl_Overlay_Window::resize(X,Y,W,H); + resizable(t); + // make sure new window size surrounds the widgets: + int b = 0; + int r = 0; + for (Fl_Type *o=window->next; o && o->level>window->level; o=o->next) + if (o->is_widget() && !o->is_menu_item()) { + Fl_Widget* w = ((Fl_Widget_Type*)o)->o; + if (w->x()+w->w() > r) r = w->x()+w->w(); + if (w->y()+w->h() > b) b = w->y()+w->h(); + } + if (nh < b) nh = b; + if (nw < r) nw = r; + // If changed, tell the window manager. Skip really big windows + // that might be bigger than screen: + if (nw != W && nw < Fl::w()-100 || nh != H && nh < Fl::h()-100) size(nw,nh); +} + +// calculate actual move by moving mouse position (mx,my) to +// nearest multiple of gridsize, and snap to original position +void Fl_Window_Type::newdx() { + int dx, dy; + if (Fl::event_state(FL_ALT)) { + dx = mx-x1; + dy = my-y1; + } else { + int dx0 = mx-x1; + int ix = (drag&RIGHT) ? br : bx; + dx = gridx ? ((ix+dx0+gridx/2)/gridx)*gridx - ix : dx0; + if (dx0 > snap) { + if (dx < 0) dx = 0; + } else if (dx0 < -snap) { + if (dx > 0) dx = 0; + } else + dx = 0; + int dy0 = my-y1; + int iy = (drag&BOTTOM) ? by : bt; + dy = gridy ? ((iy+dy0+gridy/2)/gridy)*gridy - iy : dy0; + if (dy0 > snap) { + if (dy < 0) dy = 0; + } else if (dy0 < -snap) { + if (dy > 0) dy = 0; + } else + dy = 0; + } + if (this->dx != dx || this->dy != dy) { + this->dx = dx; this->dy = dy; + ((Overlay_Window *)(this->o))->redraw_overlay(); + } +} + +// Move a widget according to dx and dy calculated above +void Fl_Window_Type::newposition(Fl_Widget_Type *o,int &X,int &Y,int &R,int &T) { + X = o->o->x(); + Y = o->o->y(); + R = X+o->o->w(); + T = Y+o->o->h(); + if (!drag) return; + if (drag&DRAG) { + X += dx; + Y += dy; + R += dx; + T += dy; + } else { + if (drag&LEFT) if (X==bx) X += dx; else if (X<bx+dx) X = bx+dx; + if (drag&BOTTOM) if (Y==by) Y += dy; else if (Y<by+dy) Y = by+dy; + if (drag&RIGHT) if (R==br) R += dx; else if (R>br+dx) R = br+dx; + if (drag&TOP) if (T==bt) T += dy; else if (T>bt+dx) T = bt+dx; + } + if (R<X) {int n = X; X = R; R = n;} + if (T<Y) {int n = Y; Y = T; T = n;} +} + +void Fl_Window_Type::draw_overlay() { + if (recalc) { + bx = o->w(); by = o->h(); br = 0; bt = 0; + numselected = 0; + for (Fl_Type *q=next; q && q->level>level; q=q->next) + if (q->selected && q->is_widget() && !q->is_menu_item()) { + numselected++; + Fl_Widget_Type* o = (Fl_Widget_Type*)q; + if (o->o->x() < bx) bx = o->o->x(); + if (o->o->y() < by) by = o->o->y(); + if (o->o->x()+o->o->w() > br) br = o->o->x()+o->o->w(); + if (o->o->y()+o->o->h() > bt) bt = o->o->y()+o->o->h(); + } + recalc = 0; + } + fl_color(FL_RED); + if (drag==BOX && (x1 != mx || y1 != my)) { + int x = x1; int r = mx; if (x > r) {x = mx; r = x1;} + int y = y1; int b = my; if (y > b) {y = my; b = y1;} + fl_rect(x,y,r-x,b-y); + } + if (overlays_invisible) return; + if (selected) fl_rect(0,0,o->w(),o->h()); + if (!numselected) return; + int bx,by,br,bt; + bx = o->w(); by = o->h(); br = 0; bt = 0; + for (Fl_Type *q=next; q && q->level>level; q = q->next) + if (q->selected && q->is_widget() && !q->is_menu_item()) { + Fl_Widget_Type* o = (Fl_Widget_Type*)q; + int x,y,r,t; + newposition(o,x,y,r,t); + fl_rect(x,y,r-x,t-y); + if (x < bx) bx = x; + if (y < by) by = y; + if (r > br) br = r; + if (t > bt) bt = t; + } + if (selected) return; + if (numselected>1) fl_rect(bx,by,br-bx,bt-by); + fl_rectf(bx,by,5,5); + fl_rectf(br-5,by,5,5); + fl_rectf(br-5,bt-5,5,5); + fl_rectf(bx,bt-5,5,5); +} + +// Calculate new bounding box of selected widgets: +void Fl_Window_Type::fix_overlay() { + overlays_invisible = 0; + recalc = 1; + ((Overlay_Window *)(this->o))->redraw_overlay(); +} + +// do that for every window (when selected set changes): +void redraw_overlays() { + for (Fl_Type *o=Fl_Type::first; o; o=o->next) + if (o->is_window()) ((Fl_Window_Type*)o)->fix_overlay(); +} + +void toggle_overlays(Fl_Widget *,void *) { + overlays_invisible = !overlays_invisible; + for (Fl_Type *o=Fl_Type::first; o; o=o->next) + if (o->is_window()) { + Fl_Widget_Type* w = (Fl_Widget_Type*)o; + ((Overlay_Window*)(w->o))->redraw_overlay(); + } +} + +extern void select(Fl_Type *,int); +extern void select_only(Fl_Type *); +extern void deselect(); +extern Fl_Type* in_this_only; +extern void fix_group_size(Fl_Type *t); + +extern Fl_Menu_Item Main_Menu[]; +extern Fl_Menu_Item New_Menu[]; + +int Fl_Window_Type::handle(int event) { + static Fl_Type* selection; + switch (event) { + case FL_PUSH: + x1 = mx = Fl::event_x(); + y1 = my = Fl::event_y(); + drag = 0; + // test for popup menu: + if (Fl::event_button() >= 3) { + in_this_only = this; // modifies how some menu items work. + static const Fl_Menu_Item* prev; + const Fl_Menu_Item* m = New_Menu->popup(mx,my,"New",prev); + if (m && m->callback()) {prev = m; m->do_callback(this->o);} + in_this_only = 0; + return 1; + } + // find the innermost item clicked on: + selection = this; + {for (Fl_Type* i=next; i && i->level>level; i=i->next) + if (i->is_widget() && !i->is_menu_item()) { + Fl_Widget_Type* o = (Fl_Widget_Type*)i; + for (Fl_Widget *o1 = o->o; o1; o1 = o1->parent()) + if (!o1->visible()) goto CONTINUE2; + if (Fl::event_inside(o->o)) selection = o; + CONTINUE2:; + }} + // do object-specific selection of other objects: + {Fl_Type* t = selection->click_test(mx, my); + if (t) { + //if (t == selection) return 1; // indicates mouse eaten w/o change + if (Fl::event_state(FL_SHIFT)) { + Fl::event_is_click(0); + select(t, !t->selected); + } else { + deselect(); + select(t, 1); + if (t->is_menu_item()) t->open(); + } + selection = t; + return 1; + }} + // see if user grabs edges of selected region: + if (numselected && !overlays_invisible && !(Fl::event_state(FL_SHIFT)) && + mx<=br+snap && mx>=bx-snap && my<=bt+snap && my>=by-snap) { + int snap1 = snap>5 ? snap : 5; + int w1 = (br-bx)/4; if (w1 > snap1) w1 = snap1; + if (mx>=br-w1) drag |= RIGHT; + else if (mx<bx+w1) drag |= LEFT; + w1 = (bt-by)/4; if (w1 > snap1) w1 = snap1; + if (my<=by+w1) drag |= BOTTOM; + else if (my>bt-w1) drag |= TOP; + if (!drag) drag = DRAG; + } else { + drag = BOX; // start a new selection region + } + return 1; + case FL_DRAG: + if (!drag) return 0; + mx = Fl::event_x(); + my = Fl::event_y(); + newdx(); + return 1; + case FL_RELEASE: + if (!drag) return 0; + mx = Fl::event_x(); + my = Fl::event_y(); + newdx(); + if (drag != BOX && (dx || dy || !Fl::event_is_click())) { + if (dx || dy) { + Fl_Type *i; + for (i=next; i && i->level>level;) { + if (i->selected && i->is_widget() && !i->is_menu_item()) { + Fl_Widget_Type* o = (Fl_Widget_Type*)i; + int x,y,r,t; + newposition(o,x,y,r,t); + o->o->resize(x,y,r-x,t-y); + // move all the children, whether selected or not: + Fl_Type* p; + for (p = o->next; p && p->level>o->level; p = p->next) + if (p->is_widget() && !p->is_menu_item()) { + Fl_Widget_Type* o = (Fl_Widget_Type*)p; + int x,y,r,t; + newposition(o,x,y,r,t); + o->o->resize(x,y,r-x,t-y); + } + i = p; + } else { + i = i->next; + } + } + for (i=next; i && i->level>level; i=i->next) fix_group_size(i); + this->o->redraw(); + fix_overlay(); + modflag = 1; + } + } else if ((Fl::event_clicks() || Fl::event_state(FL_CTRL))) { + Fl_Widget_Type::open(); + } else { + if (mx<x1) {int t = x1; x1 = mx; mx = t;} + if (my<y1) {int t = y1; y1 = my; my = t;} + int n = 0; + int toggle = Fl::event_state(FL_SHIFT); + // clear selection on everything: + if (!toggle) deselect(); else Fl::event_is_click(0); + // select everything in box: + for (Fl_Type*i=next; i&&i->level>level; i=i->next) + if (i->is_widget() && !i->is_menu_item()) { + Fl_Widget_Type* o = (Fl_Widget_Type*)i; + for (Fl_Widget *o1 = o->o; o1; o1 = o1->parent()) + if (!o1->visible()) goto CONTINUE; + if (Fl::event_inside(o->o)) selection = o; + if (o->o->x()>=x1 && o->o->y()>y1 && + o->o->x()+o->o->w()<mx && o->o->y()+o->o->h()<my) { + n++; + select(o, toggle ? !o->selected : 1); + } + CONTINUE:; + } + // if nothing in box, select what was clicked on: + if (!n) { + select(selection, toggle ? !selection->selected : 1); + } + } + drag = 0; + return 1; + + case FL_KEYBOARD: { + if (Fl::event_key() == FL_Escape) {((Fl_Window*)o)->hide(); return 1;} + // find current child: + Fl_Type *i = Fl_Type::current; + while (i && (!i->is_widget() || i->is_menu_item())) i = i->parent; + if (!i) return 0; + Fl_Type *p = i->parent; + while (p && p != this) p = p->parent; + if (!p || !p->is_widget()) {i=next; if (!i || i->level <= level) return 0;} + p = i; + // try to navigate to another child: + for (;;) { + switch (Fl::event_key()) { + case FL_Tab: + if (Fl::event_state(FL_SHIFT)) goto LEFT; + case FL_Right: + case FL_Down: + i = i->next; break; + case FL_Left: + case FL_Up: + LEFT: + i = i->prev; break; + default: + return 0; + } + if (!i || i->level <= level) {i = p; break;} + if (!i->is_widget() || i->is_menu_item()) continue; + switch (Fl::event_key()) { + case FL_Up: + case FL_Down: if (p->is_widget() && !p->is_menu_item()) { + Fl_Widget* w = ((Fl_Widget_Type*)i)->o; + Fl_Widget* pw = ((Fl_Widget_Type*)p)->o; + if (w->x() >= pw->x()+pw->w() || + w->x()+w->w() <= pw->x()) continue; + }} + break; + } + // select it: + deselect(); select(i,1); + } return 1; + + case FL_SHORTCUT: { + in_this_only = this; // modifies how some menu items work. + const Fl_Menu_Item* m = Main_Menu->test_shortcut(); + if (m && m->callback()) m->do_callback(this->o); + in_this_only = 0; + return (m != 0); + } + default: + return 0; + } +} + +//////////////////////////////////////////////////////////////// + +#include <stdio.h> +#include <string.h> + +void Fl_Window_Type::write_code1() { + Fl_Widget_Type::write_code1(); +} + +void Fl_Window_Type::write_code2() { + if (modal) write_c("%so->set_modal();\n", indent()); + else if (non_modal) write_c("%so->set_non_modal();\n", indent()); + if (!((Fl_Window*)o)->border()) write_c("%so->clear_border();\n", indent()); + write_c("%so->end();\n", indent()); + if (((Fl_Window*)o)->resizable() == o) + write_c("%so->resizable(o);\n", indent()); + Fl_Widget_Type::write_code2(); +} + +void Fl_Window_Type::write_properties() { + Fl_Widget_Type::write_properties(); + if (modal) write_string("modal"); + else if (non_modal) write_string("non_modal"); + if (!((Fl_Window*)o)->border()) write_string("noborder"); + if (xclass) {write_string("xclass"); write_word(xclass);} + if (o->visible()) write_string("visible"); +} + +void Fl_Window_Type::read_property(const char *c) { + if (!strcmp(c,"modal")) { + modal = 1; + } else if (!strcmp(c,"non_modal")) { + non_modal = 1; + } else if (!strcmp(c, "visible")) { + if (Fl::first_window()) open(); // only if we are using user interface + } else if (!strcmp(c,"noborder")) { + ((Fl_Window*)o)->border(0); + } else if (!strcmp(c,"xclass")) { + storestring(read_word(),xclass); + ((Fl_Window*)o)->xclass(xclass); + } else { + Fl_Widget_Type::read_property(c); + } +} + +int Fl_Window_Type::read_fdesign(const char* name, const char* value) { + int x; + o->box(FL_NO_BOX); // because fdesign always puts an Fl_Box next + if (!strcmp(name,"Width")) { + if (sscanf(value,"%d",&x) == 1) o->size(x,o->h()); + } else if (!strcmp(name,"Height")) { + if (sscanf(value,"%d",&x) == 1) o->size(o->w(),x); + } else if (!strcmp(name,"NumberofWidgets")) { + return 1; // we can figure out count from file + } else if (!strcmp(name,"border")) { + if (sscanf(value,"%d",&x) == 1) ((Fl_Window*)o)->border(x); + } else if (!strcmp(name,"title")) { + label(value); + } else { + return Fl_Widget_Type::read_fdesign(name,value); + } + return 1; +} |
