DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1 | // |
| 2 | // "$Id: Fl_Widget.cxx 7940 2010-12-02 17:58:58Z greg.ercolano $" |
| 3 | // |
| 4 | // Base widget class 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 | #include <FL/Fl.H> |
| 29 | #include <FL/Fl_Widget.H> |
| 30 | #include <FL/Fl_Group.H> |
| 31 | #include <FL/Fl_Tooltip.H> |
| 32 | #include <FL/fl_draw.H> |
| 33 | #include <stdlib.h> |
| 34 | #include "flstring.h" |
| 35 | |
| 36 | |
| 37 | //////////////////////////////////////////////////////////////// |
| 38 | // for compatibility with Forms, all widgets without callbacks are |
| 39 | // inserted into a "queue" when they are activated, and the forms |
| 40 | // compatibility interaction functions (fl_do_events, etc.) will |
| 41 | // read one widget at a time from this queue and return it: |
| 42 | |
| 43 | const int QUEUE_SIZE = 20; |
| 44 | |
| 45 | static Fl_Widget *obj_queue[QUEUE_SIZE]; |
| 46 | static int obj_head, obj_tail; |
| 47 | |
| 48 | void Fl_Widget::default_callback(Fl_Widget *o, void * /*v*/) { |
| 49 | #if 0 |
| 50 | // This is necessary for strict forms compatibility but is confusing. |
| 51 | // Use the parent's callback if this widget does not have one. |
| 52 | for (Fl_Widget *p = o->parent(); p; p = p->parent()) |
| 53 | if (p->callback() != default_callback) { |
| 54 | p->do_callback(o,v); |
| 55 | return; |
| 56 | } |
| 57 | #endif |
| 58 | obj_queue[obj_head++] = o; |
| 59 | if (obj_head >= QUEUE_SIZE) obj_head = 0; |
| 60 | if (obj_head == obj_tail) { |
| 61 | obj_tail++; |
| 62 | if (obj_tail >= QUEUE_SIZE) obj_tail = 0; |
| 63 | } |
| 64 | } |
| 65 | /** |
| 66 | All Fl_Widgets that don't have a callback defined use a |
| 67 | default callback that puts a pointer to the widget in this queue, |
| 68 | and this method reads the oldest widget out of this queue. |
| 69 | */ |
| 70 | Fl_Widget *Fl::readqueue() { |
| 71 | if (obj_tail==obj_head) return 0; |
| 72 | Fl_Widget *o = obj_queue[obj_tail++]; |
| 73 | if (obj_tail >= QUEUE_SIZE) obj_tail = 0; |
| 74 | return o; |
| 75 | } |
| 76 | /* |
| 77 | This static internal function removes all pending callbacks for a |
| 78 | specific widget from the default callback queue (Fl::readqueue()). |
| 79 | It is only called from Fl_Widget's destructor if the widget |
| 80 | doesn't have an own callback. |
| 81 | Note: There's no need to have this in the Fl:: namespace. |
| 82 | */ |
| 83 | static void cleanup_readqueue(Fl_Widget *w) { |
| 84 | |
| 85 | if (obj_tail==obj_head) return; |
| 86 | |
| 87 | // Read the entire queue and copy over all valid entries. |
| 88 | // The new head will be determined after the last copied entry. |
| 89 | |
| 90 | int old_head = obj_head; // save newest entry |
| 91 | int entry = obj_tail; // oldest entry |
| 92 | obj_head = obj_tail; // new queue start |
| 93 | for (;;) { |
| 94 | Fl_Widget *o = obj_queue[entry++]; |
| 95 | if (entry >= QUEUE_SIZE) entry = 0; |
| 96 | if (o != w) { // valid entry |
| 97 | obj_queue[obj_head++] = o; |
| 98 | if (obj_head >= QUEUE_SIZE) obj_head = 0; |
| 99 | } // valid entry |
| 100 | if (entry == old_head) break; |
| 101 | } |
| 102 | return; |
| 103 | } |
| 104 | //////////////////////////////////////////////////////////////// |
| 105 | |
| 106 | int Fl_Widget::handle(int) { |
| 107 | return 0; |
| 108 | } |
| 109 | |
| 110 | /** Default font size for widgets */ |
| 111 | Fl_Fontsize FL_NORMAL_SIZE = 14; |
| 112 | |
| 113 | Fl_Widget::Fl_Widget(int X, int Y, int W, int H, const char* L) { |
| 114 | |
| 115 | x_ = X; y_ = Y; w_ = W; h_ = H; |
| 116 | |
| 117 | label_.value = L; |
| 118 | label_.image = 0; |
| 119 | label_.deimage = 0; |
| 120 | label_.type = FL_NORMAL_LABEL; |
| 121 | label_.font = FL_HELVETICA; |
| 122 | label_.size = FL_NORMAL_SIZE; |
| 123 | label_.color = FL_FOREGROUND_COLOR; |
| 124 | label_.align_ = FL_ALIGN_CENTER; |
| 125 | tooltip_ = 0; |
| 126 | callback_ = default_callback; |
| 127 | user_data_ = 0; |
| 128 | type_ = 0; |
| 129 | flags_ = VISIBLE_FOCUS; |
| 130 | damage_ = 0; |
| 131 | box_ = FL_NO_BOX; |
| 132 | color_ = FL_GRAY; |
| 133 | color2_ = FL_GRAY; |
| 134 | when_ = FL_WHEN_RELEASE; |
| 135 | |
| 136 | parent_ = 0; |
| 137 | if (Fl_Group::current()) Fl_Group::current()->add(this); |
| 138 | } |
| 139 | |
| 140 | void Fl_Widget::resize(int X, int Y, int W, int H) { |
| 141 | x_ = X; y_ = Y; w_ = W; h_ = H; |
| 142 | } |
| 143 | |
| 144 | // this is useful for parent widgets to call to resize children: |
| 145 | int Fl_Widget::damage_resize(int X, int Y, int W, int H) { |
| 146 | if (x() == X && y() == Y && w() == W && h() == H) return 0; |
| 147 | resize(X, Y, W, H); |
| 148 | redraw(); |
| 149 | return 1; |
| 150 | } |
| 151 | |
| 152 | int Fl_Widget::take_focus() { |
| 153 | if (!takesevents()) return 0; |
| 154 | if (!visible_focus()) return 0; |
| 155 | if (!handle(FL_FOCUS)) return 0; // see if it wants it |
| 156 | if (contains(Fl::focus())) return 1; // it called Fl::focus for us |
| 157 | Fl::focus(this); |
| 158 | return 1; |
| 159 | } |
| 160 | |
| 161 | extern void fl_throw_focus(Fl_Widget*); // in Fl_x.cxx |
| 162 | |
| 163 | /** |
| 164 | Destroys the widget, taking care of throwing focus before if any. |
| 165 | Destruction removes the widget from any parent group! And groups when |
| 166 | destroyed destroy all their children. This is convenient and fast. |
| 167 | */ |
| 168 | Fl_Widget::~Fl_Widget() { |
| 169 | Fl::clear_widget_pointer(this); |
| 170 | if (flags() & COPIED_LABEL) free((void *)(label_.value)); |
| 171 | if (flags() & COPIED_TOOLTIP) free((void *)(tooltip_)); |
| 172 | // remove from parent group |
| 173 | if (parent_) parent_->remove(this); |
| 174 | #ifdef DEBUG_DELETE |
| 175 | if (parent_) { // this should never happen |
| 176 | printf("*** Fl_Widget: parent_->remove(this) failed [%p,%p]\n",parent_,this); |
| 177 | } |
| 178 | #endif // DEBUG_DELETE |
| 179 | parent_ = 0; // Don't throw focus to a parent widget. |
| 180 | fl_throw_focus(this); |
| 181 | // remove stale entries from default callback queue (Fl::readqueue()) |
| 182 | if (callback_ == default_callback) cleanup_readqueue(this); |
| 183 | } |
| 184 | |
| 185 | /** Draws a focus box for the widget at the given position and size */ |
| 186 | void |
| 187 | Fl_Widget::draw_focus(Fl_Boxtype B, int X, int Y, int W, int H) const { |
| 188 | if (!Fl::visible_focus()) return; |
| 189 | switch (B) { |
| 190 | case FL_DOWN_BOX: |
| 191 | case FL_DOWN_FRAME: |
| 192 | case FL_THIN_DOWN_BOX: |
| 193 | case FL_THIN_DOWN_FRAME: |
| 194 | X ++; |
| 195 | Y ++; |
| 196 | default: |
| 197 | break; |
| 198 | } |
| 199 | |
| 200 | fl_color(fl_contrast(FL_BLACK, color())); |
| 201 | |
| 202 | #if defined(USE_X11) || defined(__APPLE_QUARTZ__) |
| 203 | fl_line_style(FL_DOT); |
| 204 | fl_rect(X + Fl::box_dx(B), Y + Fl::box_dy(B), |
| 205 | W - Fl::box_dw(B) - 1, H - Fl::box_dh(B) - 1); |
| 206 | fl_line_style(FL_SOLID); |
| 207 | #elif defined(WIN32) |
| 208 | // Windows 95/98/ME do not implement the dotted line style, so draw |
| 209 | // every other pixel around the focus area... |
| 210 | // |
| 211 | // Also, QuickDraw (MacOS) does not support line styles specifically, |
| 212 | // and the hack we use in fl_line_style() will not draw horizontal lines |
| 213 | // on odd-numbered rows... |
| 214 | int i, xx, yy; |
| 215 | |
| 216 | X += Fl::box_dx(B); |
| 217 | Y += Fl::box_dy(B); |
| 218 | W -= Fl::box_dw(B) + 2; |
| 219 | H -= Fl::box_dh(B) + 2; |
| 220 | |
| 221 | for (xx = 0, i = 1; xx < W; xx ++, i ++) if (i & 1) fl_point(X + xx, Y); |
| 222 | for (yy = 0; yy < H; yy ++, i ++) if (i & 1) fl_point(X + W, Y + yy); |
| 223 | for (xx = W; xx > 0; xx --, i ++) if (i & 1) fl_point(X + xx, Y + H); |
| 224 | for (yy = H; yy > 0; yy --, i ++) if (i & 1) fl_point(X, Y + yy); |
| 225 | #else |
| 226 | # error unsupported platform |
| 227 | #endif // WIN32 |
| 228 | } |
| 229 | |
| 230 | |
| 231 | void Fl_Widget::activate() { |
| 232 | if (!active()) { |
| 233 | clear_flag(INACTIVE); |
| 234 | if (active_r()) { |
| 235 | redraw(); |
| 236 | redraw_label(); |
| 237 | handle(FL_ACTIVATE); |
| 238 | if (inside(Fl::focus())) Fl::focus()->take_focus(); |
| 239 | } |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | void Fl_Widget::deactivate() { |
| 244 | if (active_r()) { |
| 245 | set_flag(INACTIVE); |
| 246 | redraw(); |
| 247 | redraw_label(); |
| 248 | handle(FL_DEACTIVATE); |
| 249 | fl_throw_focus(this); |
| 250 | } else { |
| 251 | set_flag(INACTIVE); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | int Fl_Widget::active_r() const { |
| 256 | for (const Fl_Widget* o = this; o; o = o->parent()) |
| 257 | if (!o->active()) return 0; |
| 258 | return 1; |
| 259 | } |
| 260 | |
| 261 | void Fl_Widget::show() { |
| 262 | if (!visible()) { |
| 263 | clear_flag(INVISIBLE); |
| 264 | if (visible_r()) { |
| 265 | redraw(); |
| 266 | redraw_label(); |
| 267 | handle(FL_SHOW); |
| 268 | if (inside(Fl::focus())) Fl::focus()->take_focus(); |
| 269 | } |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | void Fl_Widget::hide() { |
| 274 | if (visible_r()) { |
| 275 | set_flag(INVISIBLE); |
| 276 | for (Fl_Widget *p = parent(); p; p = p->parent()) |
| 277 | if (p->box() || !p->parent()) {p->redraw(); break;} |
| 278 | handle(FL_HIDE); |
| 279 | fl_throw_focus(this); |
| 280 | } else { |
| 281 | set_flag(INVISIBLE); |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | int Fl_Widget::visible_r() const { |
| 286 | for (const Fl_Widget* o = this; o; o = o->parent()) |
| 287 | if (!o->visible()) return 0; |
| 288 | return 1; |
| 289 | } |
| 290 | |
| 291 | // return true if widget is inside (or equal to) this: |
| 292 | // Returns false for null widgets. |
| 293 | int Fl_Widget::contains(const Fl_Widget *o) const { |
| 294 | for (; o; o = o->parent_) if (o == this) return 1; |
| 295 | return 0; |
| 296 | } |
| 297 | |
| 298 | |
| 299 | void |
| 300 | Fl_Widget::label(const char *a) { |
| 301 | if (flags() & COPIED_LABEL) { |
| 302 | // reassigning a copied label remains the same copied label |
| 303 | if (label_.value == a) |
| 304 | return; |
| 305 | free((void *)(label_.value)); |
| 306 | clear_flag(COPIED_LABEL); |
| 307 | } |
| 308 | label_.value=a; |
| 309 | redraw_label(); |
| 310 | } |
| 311 | |
| 312 | |
| 313 | void |
| 314 | Fl_Widget::copy_label(const char *a) { |
| 315 | if (flags() & COPIED_LABEL) free((void *)(label_.value)); |
| 316 | if (a) { |
| 317 | set_flag(COPIED_LABEL); |
| 318 | label_.value=strdup(a); |
| 319 | } else { |
| 320 | clear_flag(COPIED_LABEL); |
| 321 | label_.value=(char *)0; |
| 322 | } |
| 323 | redraw_label(); |
| 324 | } |
| 325 | |
| 326 | /** Calls the widget callback. |
| 327 | |
| 328 | Causes a widget to invoke its callback function with arbitrary arguments. |
| 329 | |
| 330 | \param[in] o call the callback with \p o as the widget argument |
| 331 | \param[in] arg use \p arg as the user data argument |
| 332 | \see callback() |
| 333 | */ |
| 334 | void |
| 335 | Fl_Widget::do_callback(Fl_Widget* o,void* arg) { |
| 336 | Fl_Widget_Tracker wp(this); |
| 337 | callback_(o,arg); |
| 338 | if (wp.deleted()) return; |
| 339 | if (callback_ != default_callback) |
| 340 | clear_changed(); |
| 341 | } |
| 342 | |
| 343 | // |
| 344 | // End of "$Id: Fl_Widget.cxx 7940 2010-12-02 17:58:58Z greg.ercolano $". |
| 345 | // |