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