DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame^] | 1 | // |
| 2 | // "$Id: Fl_Group.cxx 8184 2011-01-04 18:28:01Z matt $" |
| 3 | // |
| 4 | // Group widget for the Fast Light Tool Kit (FLTK). |
| 5 | // |
| 6 | // Copyright 1998-2010 by Bill Spitzak and others. |
| 7 | // |
| 8 | // This library is free software; you can redistribute it and/or |
| 9 | // modify it under the terms of the GNU Library General Public |
| 10 | // License as published by the Free Software Foundation; either |
| 11 | // version 2 of the License, or (at your option) any later version. |
| 12 | // |
| 13 | // This library is distributed in the hope that it will be useful, |
| 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 16 | // Library General Public License for more details. |
| 17 | // |
| 18 | // You should have received a copy of the GNU Library General Public |
| 19 | // License along with this library; if not, write to the Free Software |
| 20 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| 21 | // USA. |
| 22 | // |
| 23 | // Please report all bugs and problems on the following page: |
| 24 | // |
| 25 | // http://www.fltk.org/str.php |
| 26 | // |
| 27 | |
| 28 | // The Fl_Group is the only defined container type in FLTK. |
| 29 | |
| 30 | // Fl_Window itself is a subclass of this, and most of the event |
| 31 | // handling is designed so windows themselves work correctly. |
| 32 | |
| 33 | #include <stdio.h> |
| 34 | #include <FL/Fl.H> |
| 35 | #include <FL/Fl_Group.H> |
| 36 | #include <FL/Fl_Window.H> |
| 37 | #include <FL/fl_draw.H> |
| 38 | #include <stdlib.h> |
| 39 | |
| 40 | Fl_Group* Fl_Group::current_; |
| 41 | |
| 42 | // Hack: A single child is stored in the pointer to the array, while |
| 43 | // multiple children are stored in an allocated array: |
| 44 | |
| 45 | /** |
| 46 | Returns a pointer to the array of children. <I>This pointer is only |
| 47 | valid until the next time a child is added or removed.</I> |
| 48 | */ |
| 49 | Fl_Widget*const* Fl_Group::array() const { |
| 50 | return children_ <= 1 ? (Fl_Widget**)(&array_) : array_; |
| 51 | } |
| 52 | |
| 53 | /** |
| 54 | Searches the child array for the widget and returns the index. Returns children() |
| 55 | if the widget is NULL or not found. |
| 56 | */ |
| 57 | int Fl_Group::find(const Fl_Widget* o) const { |
| 58 | Fl_Widget*const* a = array(); |
| 59 | int i; for (i=0; i < children_; i++) if (*a++ == o) break; |
| 60 | return i; |
| 61 | } |
| 62 | |
| 63 | // Metrowerks CodeWarrior and others can't export the static |
| 64 | // class member: current_, so these methods can't be inlined... |
| 65 | |
| 66 | /** |
| 67 | Sets the current group so you can build the widget |
| 68 | tree by just constructing the widgets. |
| 69 | |
| 70 | begin() is automatically called by the constructor for Fl_Group (and thus for |
| 71 | Fl_Window as well). begin() <I>is exactly the same as</I> current(this). |
| 72 | <I>Don't forget to end() the group or window!</I> |
| 73 | */ |
| 74 | void Fl_Group::begin() {current_ = this;} |
| 75 | |
| 76 | /** |
| 77 | <I>Exactly the same as</I> current(this->parent()). Any new widgets |
| 78 | added to the widget tree will be added to the parent of the group. |
| 79 | */ |
| 80 | void Fl_Group::end() {current_ = parent();} |
| 81 | |
| 82 | /** |
| 83 | Returns the currently active group. |
| 84 | |
| 85 | The Fl_Widget constructor automatically does current()->add(widget) if this |
| 86 | is not null. To prevent new widgets from being added to a group, call |
| 87 | Fl_Group::current(0). |
| 88 | */ |
| 89 | Fl_Group *Fl_Group::current() {return current_;} |
| 90 | |
| 91 | /** |
| 92 | Sets the current group. |
| 93 | \see Fl_Group::current() |
| 94 | */ |
| 95 | void Fl_Group::current(Fl_Group *g) {current_ = g;} |
| 96 | |
| 97 | extern Fl_Widget* fl_oldfocus; // set by Fl::focus |
| 98 | |
| 99 | // For back-compatibility, we must adjust all events sent to child |
| 100 | // windows so they are relative to that window. |
| 101 | |
| 102 | static int send(Fl_Widget* o, int event) { |
| 103 | if (o->type() < FL_WINDOW) return o->handle(event); |
| 104 | switch ( event ) |
| 105 | { |
| 106 | case FL_DND_ENTER: /* FALLTHROUGH */ |
| 107 | case FL_DND_DRAG: |
| 108 | // figure out correct type of event: |
| 109 | event = (o->contains(Fl::belowmouse())) ? FL_DND_DRAG : FL_DND_ENTER; |
| 110 | } |
| 111 | int save_x = Fl::e_x; Fl::e_x -= o->x(); |
| 112 | int save_y = Fl::e_y; Fl::e_y -= o->y(); |
| 113 | int ret = o->handle(event); |
| 114 | Fl::e_y = save_y; |
| 115 | Fl::e_x = save_x; |
| 116 | switch ( event ) |
| 117 | { |
| 118 | case FL_ENTER: /* FALLTHROUGH */ |
| 119 | case FL_DND_ENTER: |
| 120 | // Successful completion of FL_ENTER means the widget is now the |
| 121 | // belowmouse widget, but only call Fl::belowmouse if the child |
| 122 | // widget did not do so: |
| 123 | if (!o->contains(Fl::belowmouse())) Fl::belowmouse(o); |
| 124 | break; |
| 125 | } |
| 126 | return ret; |
| 127 | } |
| 128 | |
| 129 | // translate the current keystroke into up/down/left/right for navigation: |
| 130 | #define ctrl(x) (x^0x40) |
| 131 | static int navkey() { |
| 132 | switch (Fl::event_key()) { |
| 133 | case 0: // not an FL_KEYBOARD/FL_SHORTCUT event |
| 134 | break; |
| 135 | case FL_Tab: |
| 136 | if (!Fl::event_state(FL_SHIFT)) return FL_Right; |
| 137 | return FL_Left; |
| 138 | case FL_Right: |
| 139 | return FL_Right; |
| 140 | case FL_Left: |
| 141 | return FL_Left; |
| 142 | case FL_Up: |
| 143 | return FL_Up; |
| 144 | case FL_Down: |
| 145 | return FL_Down; |
| 146 | } |
| 147 | return 0; |
| 148 | } |
| 149 | |
| 150 | int Fl_Group::handle(int event) { |
| 151 | |
| 152 | Fl_Widget*const* a = array(); |
| 153 | int i; |
| 154 | Fl_Widget* o; |
| 155 | |
| 156 | switch (event) { |
| 157 | |
| 158 | case FL_FOCUS: |
| 159 | switch (navkey()) { |
| 160 | default: |
| 161 | if (savedfocus_ && savedfocus_->take_focus()) return 1; |
| 162 | case FL_Right: |
| 163 | case FL_Down: |
| 164 | for (i = children(); i--;) if ((*a++)->take_focus()) return 1; |
| 165 | break; |
| 166 | case FL_Left: |
| 167 | case FL_Up: |
| 168 | for (i = children(); i--;) if (a[i]->take_focus()) return 1; |
| 169 | break; |
| 170 | } |
| 171 | return 0; |
| 172 | |
| 173 | case FL_UNFOCUS: |
| 174 | savedfocus_ = fl_oldfocus; |
| 175 | return 0; |
| 176 | |
| 177 | case FL_KEYBOARD: |
| 178 | return navigation(navkey()); |
| 179 | |
| 180 | case FL_SHORTCUT: |
| 181 | for (i = children(); i--;) { |
| 182 | o = a[i]; |
| 183 | if (o->takesevents() && Fl::event_inside(o) && send(o,FL_SHORTCUT)) |
| 184 | return 1; |
| 185 | } |
| 186 | for (i = children(); i--;) { |
| 187 | o = a[i]; |
| 188 | if (o->takesevents() && !Fl::event_inside(o) && send(o,FL_SHORTCUT)) |
| 189 | return 1; |
| 190 | } |
| 191 | if ((Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter)) return navigation(FL_Down); |
| 192 | return 0; |
| 193 | |
| 194 | case FL_ENTER: |
| 195 | case FL_MOVE: |
| 196 | for (i = children(); i--;) { |
| 197 | o = a[i]; |
| 198 | if (o->visible() && Fl::event_inside(o)) { |
| 199 | if (o->contains(Fl::belowmouse())) { |
| 200 | return send(o,FL_MOVE); |
| 201 | } else { |
| 202 | Fl::belowmouse(o); |
| 203 | if (send(o,FL_ENTER)) return 1; |
| 204 | } |
| 205 | } |
| 206 | } |
| 207 | Fl::belowmouse(this); |
| 208 | return 1; |
| 209 | |
| 210 | case FL_DND_ENTER: |
| 211 | case FL_DND_DRAG: |
| 212 | for (i = children(); i--;) { |
| 213 | o = a[i]; |
| 214 | if (o->takesevents() && Fl::event_inside(o)) { |
| 215 | if (o->contains(Fl::belowmouse())) { |
| 216 | return send(o,FL_DND_DRAG); |
| 217 | } else if (send(o,FL_DND_ENTER)) { |
| 218 | if (!o->contains(Fl::belowmouse())) Fl::belowmouse(o); |
| 219 | return 1; |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | Fl::belowmouse(this); |
| 224 | return 0; |
| 225 | |
| 226 | case FL_PUSH: |
| 227 | for (i = children(); i--;) { |
| 228 | o = a[i]; |
| 229 | if (o->takesevents() && Fl::event_inside(o)) { |
| 230 | Fl_Widget_Tracker wp(o); |
| 231 | if (send(o,FL_PUSH)) { |
| 232 | if (Fl::pushed() && wp.exists() && !o->contains(Fl::pushed())) Fl::pushed(o); |
| 233 | return 1; |
| 234 | } |
| 235 | } |
| 236 | } |
| 237 | return 0; |
| 238 | |
| 239 | case FL_RELEASE: |
| 240 | case FL_DRAG: |
| 241 | o = Fl::pushed(); |
| 242 | if (o == this) return 0; |
| 243 | else if (o) send(o,event); |
| 244 | else { |
| 245 | for (i = children(); i--;) { |
| 246 | o = a[i]; |
| 247 | if (o->takesevents() && Fl::event_inside(o)) { |
| 248 | if (send(o,event)) return 1; |
| 249 | } |
| 250 | } |
| 251 | } |
| 252 | return 0; |
| 253 | |
| 254 | case FL_MOUSEWHEEL: |
| 255 | for (i = children(); i--;) { |
| 256 | o = a[i]; |
| 257 | if (o->takesevents() && Fl::event_inside(o) && send(o,FL_MOUSEWHEEL)) |
| 258 | return 1; |
| 259 | } |
| 260 | for (i = children(); i--;) { |
| 261 | o = a[i]; |
| 262 | if (o->takesevents() && !Fl::event_inside(o) && send(o,FL_MOUSEWHEEL)) |
| 263 | return 1; |
| 264 | } |
| 265 | return 0; |
| 266 | |
| 267 | case FL_DEACTIVATE: |
| 268 | case FL_ACTIVATE: |
| 269 | for (i = children(); i--;) { |
| 270 | o = *a++; |
| 271 | if (o->active()) o->handle(event); |
| 272 | } |
| 273 | return 1; |
| 274 | |
| 275 | case FL_SHOW: |
| 276 | case FL_HIDE: |
| 277 | for (i = children(); i--;) { |
| 278 | o = *a++; |
| 279 | if (event == FL_HIDE && o == Fl::focus()) { |
| 280 | // Give up input focus... |
| 281 | int old_event = Fl::e_number; |
| 282 | o->handle(Fl::e_number = FL_UNFOCUS); |
| 283 | Fl::e_number = old_event; |
| 284 | Fl::focus(0); |
| 285 | } |
| 286 | if (o->visible()) o->handle(event); |
| 287 | } |
| 288 | return 1; |
| 289 | |
| 290 | default: |
| 291 | // For all other events, try to give to each child, starting at focus: |
| 292 | for (i = 0; i < children(); i ++) |
| 293 | if (Fl::focus_ == a[i]) break; |
| 294 | |
| 295 | if (i >= children()) i = 0; |
| 296 | |
| 297 | if (children()) { |
| 298 | for (int j = i;;) { |
| 299 | if (a[j]->takesevents()) if (send(a[j], event)) return 1; |
| 300 | j++; |
| 301 | if (j >= children()) j = 0; |
| 302 | if (j == i) break; |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | return 0; |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | //void Fl_Group::focus(Fl_Widget *o) {Fl::focus(o); o->handle(FL_FOCUS);} |
| 311 | |
| 312 | #if 0 |
| 313 | const char *nameof(Fl_Widget *o) { |
| 314 | if (!o) return "NULL"; |
| 315 | if (!o->label()) return "<no label>"; |
| 316 | return o->label(); |
| 317 | } |
| 318 | #endif |
| 319 | |
| 320 | // try to move the focus in response to a keystroke: |
| 321 | int Fl_Group::navigation(int key) { |
| 322 | if (children() <= 1) return 0; |
| 323 | int i; |
| 324 | for (i = 0; ; i++) { |
| 325 | if (i >= children_) return 0; |
| 326 | if (array_[i]->contains(Fl::focus())) break; |
| 327 | } |
| 328 | Fl_Widget *previous = array_[i]; |
| 329 | |
| 330 | for (;;) { |
| 331 | switch (key) { |
| 332 | case FL_Right: |
| 333 | case FL_Down: |
| 334 | i++; |
| 335 | if (i >= children_) { |
| 336 | if (parent()) return 0; |
| 337 | i = 0; |
| 338 | } |
| 339 | break; |
| 340 | case FL_Left: |
| 341 | case FL_Up: |
| 342 | if (i) i--; |
| 343 | else { |
| 344 | if (parent()) return 0; |
| 345 | i = children_-1; |
| 346 | } |
| 347 | break; |
| 348 | default: |
| 349 | return 0; |
| 350 | } |
| 351 | Fl_Widget* o = array_[i]; |
| 352 | if (o == previous) return 0; |
| 353 | switch (key) { |
| 354 | case FL_Down: |
| 355 | case FL_Up: |
| 356 | // for up/down, the widgets have to overlap horizontally: |
| 357 | if (o->x() >= previous->x()+previous->w() || |
| 358 | o->x()+o->w() <= previous->x()) continue; |
| 359 | } |
| 360 | if (o->take_focus()) return 1; |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | //////////////////////////////////////////////////////////////// |
| 365 | |
| 366 | Fl_Group::Fl_Group(int X,int Y,int W,int H,const char *l) |
| 367 | : Fl_Widget(X,Y,W,H,l) { |
| 368 | align(FL_ALIGN_TOP); |
| 369 | children_ = 0; |
| 370 | array_ = 0; |
| 371 | savedfocus_ = 0; |
| 372 | resizable_ = this; |
| 373 | sizes_ = 0; // this is allocated when first resize() is done |
| 374 | // Subclasses may want to construct child objects as part of their |
| 375 | // constructor, so make sure they are add()'d to this object. |
| 376 | // But you must end() the object! |
| 377 | begin(); |
| 378 | } |
| 379 | |
| 380 | /** |
| 381 | Deletes all child widgets from memory recursively. |
| 382 | |
| 383 | This method differs from the remove() method in that it |
| 384 | affects all child widgets and deletes them from memory. |
| 385 | */ |
| 386 | void Fl_Group::clear() { |
| 387 | savedfocus_ = 0; |
| 388 | resizable_ = this; |
| 389 | init_sizes(); |
| 390 | |
| 391 | // we must change the Fl::pushed() widget, if it is one of |
| 392 | // the group's children. Otherwise fl_fix_focus() would send |
| 393 | // lots of events to children that are about to be deleted |
| 394 | // anyway. |
| 395 | |
| 396 | Fl_Widget *pushed = Fl::pushed(); // save pushed() widget |
| 397 | if (contains(pushed)) pushed = this; // set it to be the group, if it's a child |
| 398 | Fl::pushed(this); // for fl_fix_focus etc. |
| 399 | |
| 400 | // okay, now it is safe to destroy the children: |
| 401 | |
| 402 | #define REVERSE_CHILDREN |
| 403 | #ifdef REVERSE_CHILDREN |
| 404 | // Reverse the order of the children. Doing this and deleting |
| 405 | // always the last child is much faster than the other way around. |
| 406 | if (children_ > 1) { |
| 407 | Fl_Widget *temp; |
| 408 | Fl_Widget **a = (Fl_Widget**)array(); |
| 409 | for (int i=0,j=children_-1; i<children_/2; i++,j--) { |
| 410 | temp = a[i]; |
| 411 | a[i] = a[j]; |
| 412 | a[j] = temp; |
| 413 | } |
| 414 | } |
| 415 | #endif // REVERSE_CHILDREN |
| 416 | |
| 417 | while (children_) { // delete all children |
| 418 | int idx = children_-1; // last child's index |
| 419 | Fl_Widget* w = child(idx); // last child widget |
| 420 | if (w->parent()==this) { // should always be true |
| 421 | if (children_>2) { // optimized removal |
| 422 | w->parent_ = 0; // reset child's parent |
| 423 | children_--; // update counter |
| 424 | } else { // slow removal |
| 425 | remove(idx); |
| 426 | } |
| 427 | delete w; // delete the child |
| 428 | } else { // should never happen |
| 429 | remove(idx); // remove it anyway |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | if (pushed != this) Fl::pushed(pushed); // reset pushed() widget |
| 434 | |
| 435 | } |
| 436 | |
| 437 | /** |
| 438 | The destructor <I>also deletes all the children</I>. This allows a |
| 439 | whole tree to be deleted at once, without having to keep a pointer to |
| 440 | all the children in the user code. |
| 441 | |
| 442 | It is allowed that the Fl_Group and all of its children are automatic |
| 443 | (local) variables, but you must declare the Fl_Group \e first, so that |
| 444 | it is destroyed last. |
| 445 | |
| 446 | If you add static or automatic (local) variables to an Fl_Group, then it |
| 447 | is your responsibility to remove (or delete) all such static or automatic |
| 448 | child widgets \e \b before destroying the group - otherwise the child |
| 449 | widgets' destructors would be called twice! |
| 450 | */ |
| 451 | Fl_Group::~Fl_Group() { |
| 452 | clear(); |
| 453 | } |
| 454 | |
| 455 | /** |
| 456 | The widget is removed from its current group (if any) and then |
| 457 | inserted into this group. It is put at index n - or at the end, |
| 458 | if n >= children(). This can also be used to rearrange |
| 459 | the widgets inside a group. |
| 460 | */ |
| 461 | void Fl_Group::insert(Fl_Widget &o, int index) { |
| 462 | if (o.parent()) { |
| 463 | Fl_Group* g = o.parent(); |
| 464 | int n = g->find(o); |
| 465 | if (g == this) { |
| 466 | if (index > n) index--; |
| 467 | if (index == n) return; |
| 468 | } |
| 469 | g->remove(n); |
| 470 | } |
| 471 | o.parent_ = this; |
| 472 | if (children_ == 0) { // use array pointer to point at single child |
| 473 | array_ = (Fl_Widget**)&o; |
| 474 | } else if (children_ == 1) { // go from 1 to 2 children |
| 475 | Fl_Widget* t = (Fl_Widget*)array_; |
| 476 | array_ = (Fl_Widget**)malloc(2*sizeof(Fl_Widget*)); |
| 477 | if (index) {array_[0] = t; array_[1] = &o;} |
| 478 | else {array_[0] = &o; array_[1] = t;} |
| 479 | } else { |
| 480 | if (!(children_ & (children_-1))) // double number of children |
| 481 | array_ = (Fl_Widget**)realloc((void*)array_, |
| 482 | 2*children_*sizeof(Fl_Widget*)); |
| 483 | int j; for (j = children_; j > index; j--) array_[j] = array_[j-1]; |
| 484 | array_[j] = &o; |
| 485 | } |
| 486 | children_++; |
| 487 | init_sizes(); |
| 488 | } |
| 489 | |
| 490 | /** |
| 491 | The widget is removed from its current group (if any) and then added |
| 492 | to the end of this group. |
| 493 | */ |
| 494 | void Fl_Group::add(Fl_Widget &o) {insert(o, children_);} |
| 495 | |
| 496 | /** |
| 497 | Removes the widget at \p index from the group but does not delete it. |
| 498 | |
| 499 | This method does nothing if \p index is out of bounds. |
| 500 | |
| 501 | This method differs from the clear() method in that it only affects |
| 502 | a single widget and does not delete it from memory. |
| 503 | |
| 504 | \since FLTK 1.3.0 |
| 505 | */ |
| 506 | void Fl_Group::remove(int index) { |
| 507 | if (index < 0 || index >= children_) return; |
| 508 | Fl_Widget &o = *child(index); |
| 509 | if (&o == savedfocus_) savedfocus_ = 0; |
| 510 | if (o.parent_ == this) { // this should always be true |
| 511 | o.parent_ = 0; |
| 512 | } |
| 513 | |
| 514 | // remove the widget from the group |
| 515 | |
| 516 | children_--; |
| 517 | if (children_ == 1) { // go from 2 to 1 child |
| 518 | Fl_Widget *t = array_[!index]; |
| 519 | free((void*)array_); |
| 520 | array_ = (Fl_Widget**)t; |
| 521 | } else if (children_ > 1) { // delete from array |
| 522 | for (; index < children_; index++) array_[index] = array_[index+1]; |
| 523 | } |
| 524 | init_sizes(); |
| 525 | } |
| 526 | |
| 527 | /** |
| 528 | Removes a widget from the group but does not delete it. |
| 529 | |
| 530 | This method does nothing if the widget is not a child of the group. |
| 531 | |
| 532 | This method differs from the clear() method in that it only affects |
| 533 | a single widget and does not delete it from memory. |
| 534 | |
| 535 | \note If you have the child's index anyway, use remove(int index) |
| 536 | instead, because this doesn't need a child lookup in the group's |
| 537 | table of children. This can be much faster, if there are lots of |
| 538 | children. |
| 539 | */ |
| 540 | void Fl_Group::remove(Fl_Widget &o) { |
| 541 | if (!children_) return; |
| 542 | int i = find(o); |
| 543 | if (i < children_) remove(i); |
| 544 | } |
| 545 | |
| 546 | //////////////////////////////////////////////////////////////// |
| 547 | |
| 548 | // Rather lame kludge here, I need to detect windows and ignore the |
| 549 | // changes to X,Y, since all children are relative to X,Y. That |
| 550 | // is why I check type(): |
| 551 | |
| 552 | // sizes array stores the initial positions of widgets as |
| 553 | // left,right,top,bottom quads. The first quad is the group, the |
| 554 | // second is the resizable (clipped to the group), and the |
| 555 | // rest are the children. This is a convenient order for the |
| 556 | // algorithm. If you change this be sure to fix Fl_Tile which |
| 557 | // also uses this array! |
| 558 | |
| 559 | /** |
| 560 | Resets the internal array of widget sizes and positions. |
| 561 | |
| 562 | The Fl_Group widget keeps track of the original widget sizes and |
| 563 | positions when resizing occurs so that if you resize a window back to its |
| 564 | original size the widgets will be in the correct places. If you rearrange |
| 565 | the widgets in your group, call this method to register the new arrangement |
| 566 | with the Fl_Group that contains them. |
| 567 | |
| 568 | If you add or remove widgets, this will be done automatically. |
| 569 | |
| 570 | \note The internal array of widget sizes and positions will be allocated and |
| 571 | filled when the next resize() occurs. |
| 572 | |
| 573 | \sa sizes() |
| 574 | */ |
| 575 | void Fl_Group::init_sizes() { |
| 576 | delete[] sizes_; sizes_ = 0; |
| 577 | } |
| 578 | |
| 579 | /** |
| 580 | Returns the internal array of widget sizes and positions. |
| 581 | |
| 582 | If the sizes() array does not exist, it will be allocated and filled |
| 583 | with the current widget sizes and positions. |
| 584 | |
| 585 | \note You should never need to use this method directly, unless you have |
| 586 | special needs to rearrange the children of a Fl_Group. Fl_Tile uses |
| 587 | this to rearrange its widget positions. |
| 588 | |
| 589 | \sa init_sizes() |
| 590 | |
| 591 | \todo Should the internal representation of the sizes() array be documented? |
| 592 | */ |
| 593 | int* Fl_Group::sizes() { |
| 594 | if (!sizes_) { |
| 595 | int* p = sizes_ = new int[4*(children_+2)]; |
| 596 | // first thing in sizes array is the group's size: |
| 597 | if (type() < FL_WINDOW) {p[0] = x(); p[2] = y();} else {p[0] = p[2] = 0;} |
| 598 | p[1] = p[0]+w(); p[3] = p[2]+h(); |
| 599 | // next is the resizable's size: |
| 600 | p[4] = p[0]; // init to the group's size |
| 601 | p[5] = p[1]; |
| 602 | p[6] = p[2]; |
| 603 | p[7] = p[3]; |
| 604 | Fl_Widget* r = resizable(); |
| 605 | if (r && r != this) { // then clip the resizable to it |
| 606 | int t; |
| 607 | t = r->x(); if (t > p[0]) p[4] = t; |
| 608 | t +=r->w(); if (t < p[1]) p[5] = t; |
| 609 | t = r->y(); if (t > p[2]) p[6] = t; |
| 610 | t +=r->h(); if (t < p[3]) p[7] = t; |
| 611 | } |
| 612 | // next is all the children's sizes: |
| 613 | p += 8; |
| 614 | Fl_Widget*const* a = array(); |
| 615 | for (int i=children_; i--;) { |
| 616 | Fl_Widget* o = *a++; |
| 617 | *p++ = o->x(); |
| 618 | *p++ = o->x()+o->w(); |
| 619 | *p++ = o->y(); |
| 620 | *p++ = o->y()+o->h(); |
| 621 | } |
| 622 | } |
| 623 | return sizes_; |
| 624 | } |
| 625 | |
| 626 | /** |
| 627 | Resizes the Fl_Group widget and all of its children. |
| 628 | |
| 629 | The Fl_Group widget first resizes itself, and then it moves and resizes |
| 630 | all its children according to the rules documented for |
| 631 | Fl_Group::resizable(Fl_Widget*) |
| 632 | |
| 633 | \sa Fl_Group::resizable(Fl_Widget*) |
| 634 | \sa Fl_Group::resizable() |
| 635 | \sa Fl_Widget::resize(int,int,int,int) |
| 636 | */ |
| 637 | void Fl_Group::resize(int X, int Y, int W, int H) { |
| 638 | |
| 639 | int dx = X-x(); |
| 640 | int dy = Y-y(); |
| 641 | int dw = W-w(); |
| 642 | int dh = H-h(); |
| 643 | |
| 644 | int *p = sizes(); // save initial sizes and positions |
| 645 | |
| 646 | Fl_Widget::resize(X,Y,W,H); // make new xywh values visible for children |
| 647 | |
| 648 | if (!resizable() || (dw==0 && dh==0) ) { |
| 649 | |
| 650 | if (type() < FL_WINDOW) { |
| 651 | Fl_Widget*const* a = array(); |
| 652 | for (int i=children_; i--;) { |
| 653 | Fl_Widget* o = *a++; |
| 654 | o->resize(o->x()+dx, o->y()+dy, o->w(), o->h()); |
| 655 | } |
| 656 | } |
| 657 | |
| 658 | } else if (children_) { |
| 659 | |
| 660 | // get changes in size/position from the initial size: |
| 661 | dx = X - p[0]; |
| 662 | dw = W - (p[1]-p[0]); |
| 663 | dy = Y - p[2]; |
| 664 | dh = H - (p[3]-p[2]); |
| 665 | if (type() >= FL_WINDOW) dx = dy = 0; |
| 666 | p += 4; |
| 667 | |
| 668 | // get initial size of resizable(): |
| 669 | int IX = *p++; |
| 670 | int IR = *p++; |
| 671 | int IY = *p++; |
| 672 | int IB = *p++; |
| 673 | |
| 674 | Fl_Widget*const* a = array(); |
| 675 | for (int i=children_; i--;) { |
| 676 | Fl_Widget* o = *a++; |
| 677 | #if 1 |
| 678 | int XX = *p++; |
| 679 | if (XX >= IR) XX += dw; |
| 680 | else if (XX > IX) XX = IX+((XX-IX)*(IR+dw-IX)+(IR-IX)/2)/(IR-IX); |
| 681 | int R = *p++; |
| 682 | if (R >= IR) R += dw; |
| 683 | else if (R > IX) R = IX+((R-IX)*(IR+dw-IX)+(IR-IX)/2)/(IR-IX); |
| 684 | |
| 685 | int YY = *p++; |
| 686 | if (YY >= IB) YY += dh; |
| 687 | else if (YY > IY) YY = IY+((YY-IY)*(IB+dh-IY)+(IB-IY)/2)/(IB-IY); |
| 688 | int B = *p++; |
| 689 | if (B >= IB) B += dh; |
| 690 | else if (B > IY) B = IY+((B-IY)*(IB+dh-IY)+(IB-IY)/2)/(IB-IY); |
| 691 | #else // much simpler code from Francois Ostiguy: |
| 692 | int XX = *p++; |
| 693 | if (XX >= IR) XX += dw; |
| 694 | else if (XX > IX) XX += dw * (XX-IX)/(IR-IX); |
| 695 | int R = *p++; |
| 696 | if (R >= IR) R += dw; |
| 697 | else if (R > IX) R = R + dw * (R-IX)/(IR-IX); |
| 698 | |
| 699 | int YY = *p++; |
| 700 | if (YY >= IB) YY += dh; |
| 701 | else if (YY > IY) YY = YY + dh*(YY-IY)/(IB-IY); |
| 702 | int B = *p++; |
| 703 | if (B >= IB) B += dh; |
| 704 | else if (B > IY) B = B + dh*(B-IY)/(IB-IY); |
| 705 | #endif |
| 706 | o->resize(XX+dx, YY+dy, R-XX, B-YY); |
| 707 | } |
| 708 | } |
| 709 | } |
| 710 | |
| 711 | /** |
| 712 | Draws all children of the group. |
| 713 | |
| 714 | This is useful, if you derived a widget from Fl_Group and want to draw a special |
| 715 | border or background. You can call draw_children() from the derived draw() method |
| 716 | after drawing the box, border, or background. |
| 717 | */ |
| 718 | void Fl_Group::draw_children() { |
| 719 | Fl_Widget*const* a = array(); |
| 720 | |
| 721 | if (clip_children()) { |
| 722 | fl_push_clip(x() + Fl::box_dx(box()), |
| 723 | y() + Fl::box_dy(box()), |
| 724 | w() - Fl::box_dw(box()), |
| 725 | h() - Fl::box_dh(box())); |
| 726 | } |
| 727 | |
| 728 | if (damage() & ~FL_DAMAGE_CHILD) { // redraw the entire thing: |
| 729 | for (int i=children_; i--;) { |
| 730 | Fl_Widget& o = **a++; |
| 731 | draw_child(o); |
| 732 | draw_outside_label(o); |
| 733 | } |
| 734 | } else { // only redraw the children that need it: |
| 735 | for (int i=children_; i--;) update_child(**a++); |
| 736 | } |
| 737 | |
| 738 | if (clip_children()) fl_pop_clip(); |
| 739 | } |
| 740 | |
| 741 | void Fl_Group::draw() { |
| 742 | if (damage() & ~FL_DAMAGE_CHILD) { // redraw the entire thing: |
| 743 | draw_box(); |
| 744 | draw_label(); |
| 745 | } |
| 746 | draw_children(); |
| 747 | } |
| 748 | |
| 749 | /** |
| 750 | Draws a child only if it needs it. |
| 751 | |
| 752 | This draws a child widget, if it is not clipped \em and if any damage() bits |
| 753 | are set. The damage bits are cleared after drawing. |
| 754 | |
| 755 | \sa Fl_Group::draw_child(Fl_Widget& widget) const |
| 756 | */ |
| 757 | void Fl_Group::update_child(Fl_Widget& widget) const { |
| 758 | if (widget.damage() && widget.visible() && widget.type() < FL_WINDOW && |
| 759 | fl_not_clipped(widget.x(), widget.y(), widget.w(), widget.h())) { |
| 760 | widget.draw(); |
| 761 | widget.clear_damage(); |
| 762 | } |
| 763 | } |
| 764 | |
| 765 | /** |
| 766 | Forces a child to redraw. |
| 767 | |
| 768 | This draws a child widget, if it is not clipped. |
| 769 | The damage bits are cleared after drawing. |
| 770 | */ |
| 771 | void Fl_Group::draw_child(Fl_Widget& widget) const { |
| 772 | if (widget.visible() && widget.type() < FL_WINDOW && |
| 773 | fl_not_clipped(widget.x(), widget.y(), widget.w(), widget.h())) { |
| 774 | widget.clear_damage(FL_DAMAGE_ALL); |
| 775 | widget.draw(); |
| 776 | widget.clear_damage(); |
| 777 | } |
| 778 | } |
| 779 | |
| 780 | extern char fl_draw_shortcut; |
| 781 | |
| 782 | /** Parents normally call this to draw outside labels of child widgets. */ |
| 783 | void Fl_Group::draw_outside_label(const Fl_Widget& widget) const { |
| 784 | if (!widget.visible()) return; |
| 785 | // skip any labels that are inside the widget: |
| 786 | if (!(widget.align()&15) || (widget.align() & FL_ALIGN_INSIDE)) return; |
| 787 | // invent a box that is outside the widget: |
| 788 | Fl_Align a = widget.align(); |
| 789 | int X = widget.x(); |
| 790 | int Y = widget.y(); |
| 791 | int W = widget.w(); |
| 792 | int H = widget.h(); |
| 793 | int wx, wy; |
| 794 | if (const_cast<Fl_Group*>(this)->as_window()) { |
| 795 | wx = wy = 0; |
| 796 | } else { |
| 797 | wx = x(); wy = y(); |
| 798 | } |
| 799 | if ( (a & 0x0f) == FL_ALIGN_LEFT_TOP ) { |
| 800 | a = (a &~0x0f ) | FL_ALIGN_TOP_RIGHT; |
| 801 | X = wx; |
| 802 | W = widget.x()-X-3; |
| 803 | } else if ( (a & 0x0f) == FL_ALIGN_LEFT_BOTTOM ) { |
| 804 | a = (a &~0x0f ) | FL_ALIGN_BOTTOM_RIGHT; |
| 805 | X = wx; |
| 806 | W = widget.x()-X-3; |
| 807 | } else if ( (a & 0x0f) == FL_ALIGN_RIGHT_TOP ) { |
| 808 | a = (a &~0x0f ) | FL_ALIGN_TOP_LEFT; |
| 809 | X = X+W+3; |
| 810 | W = wx+this->w()-X; |
| 811 | } else if ( (a & 0x0f) == FL_ALIGN_RIGHT_BOTTOM ) { |
| 812 | a = (a &~0x0f ) | FL_ALIGN_BOTTOM_LEFT; |
| 813 | X = X+W+3; |
| 814 | W = wx+this->w()-X; |
| 815 | } else if (a & FL_ALIGN_TOP) { |
| 816 | a ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP); |
| 817 | Y = wy; |
| 818 | H = widget.y()-Y; |
| 819 | } else if (a & FL_ALIGN_BOTTOM) { |
| 820 | a ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP); |
| 821 | Y = Y+H; |
| 822 | H = wy+h()-Y; |
| 823 | } else if (a & FL_ALIGN_LEFT) { |
| 824 | a ^= (FL_ALIGN_LEFT|FL_ALIGN_RIGHT); |
| 825 | X = wx; |
| 826 | W = widget.x()-X-3; |
| 827 | } else if (a & FL_ALIGN_RIGHT) { |
| 828 | a ^= (FL_ALIGN_LEFT|FL_ALIGN_RIGHT); |
| 829 | X = X+W+3; |
| 830 | W = wx+this->w()-X; |
| 831 | } |
| 832 | widget.draw_label(X,Y,W,H,(Fl_Align)a); |
| 833 | } |
| 834 | |
| 835 | // |
| 836 | // End of "$Id: Fl_Group.cxx 8184 2011-01-04 18:28:01Z matt $". |
| 837 | // |