blob: 96754a9d2f3a61c9f590a9e72709ed8d949c313e [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
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
43const int QUEUE_SIZE = 20;
44
45static Fl_Widget *obj_queue[QUEUE_SIZE];
46static int obj_head, obj_tail;
47
48void 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*/
70Fl_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*/
83static 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
106int Fl_Widget::handle(int) {
107 return 0;
108}
109
110/** Default font size for widgets */
111Fl_Fontsize FL_NORMAL_SIZE = 14;
112
113Fl_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
140void 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:
145int 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
152int 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
161extern 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*/
168Fl_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 */
186void
187Fl_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
231void 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
243void 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
255int 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
261void 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
273void 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
285int 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.
293int 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
299void
300Fl_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
313void
314Fl_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*/
334void
335Fl_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//