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