1 // $Id: Flu_Spinner.cxx 900 2009-08-13 20:00:45Z larry $
2
3 /***************************************************************
4 * FLU - FLTK Utility Widgets
5 * Copyright (C) 2002 Ohio Supercomputer Center, Ohio State University
6 *
7 * This file and its content is protected by a software license.
8 * You should have received a copy of this license with this file.
9 * If not, please contact the Ohio Supercomputer Center immediately:
10 * Attn: Jason Bryan Re: FLU 1224 Kinnear Rd, Columbus, Ohio 43212
11 *
12 ***************************************************************/
13
14
15
16 #include <stdio.h>
17 #include <string.h>
18 #include <FL/Fl.H>
19 #include <FL/fl_draw.H>
20 #include <stdlib.h>
21 #include <FL/math.h>
22
23 #include "Flu_Spinner.h"
24
25 #define ABS( x ) ( (x)>0 ? (x) : -(x) )
26
NoTabInput(Flu_Spinner * s,int x,int y,int w,int h,const char * l)27 Flu_Spinner::NoTabInput::NoTabInput (Flu_Spinner * s, int x, int y, int w, int h,
28 const char *l)
29 :
30 Fl_Input (x, y, w, h, l)
31 {
32 spinner = s;
33 }
34
35 int
handle(int event)36 Flu_Spinner::NoTabInput::handle (int event)
37 {
38 switch (event) {
39 case FL_KEYDOWN:
40 {
41 switch (Fl::event_key ()) {
42 case FL_Tab:
43 redraw ();
44 return 0;
45
46 case FL_Enter:
47 case FL_KP_Enter:
48 Fl_Input::handle (event);
49 spinner->value (spinner->clamp (atof (value ())));
50 spinner->do_callback ();
51 return 1;
52
53 case FL_Down:
54 case FL_Up:
55 spinner->handle (event);
56 return 1;
57 }
58 }
59 break;
60
61 case FL_FOCUS:
62 case FL_UNFOCUS:
63 redraw ();
64 break;
65 }
66
67 return Fl_Input::handle (event);
68 }
69
70 void
draw()71 Flu_Spinner::NoTabInput::draw ()
72 {
73 if (spinner->active ())
74 activate ();
75 else
76 deactivate ();
77
78 if (spinner->_dragging) {
79 if ((spinner->align () & FL_ALIGN_INSIDE) || !spinner->editable ())
80 position (size ());
81 else
82 position (0, size ());
83 }
84
85 Fl_Input::draw ();
86 if (Fl::focus () == this
87 && ((spinner->align () & FL_ALIGN_INSIDE) || !spinner->editable ()))
88 draw_focus (box (), x (), y (), w (), h ());
89 }
90
Flu_Spinner(int X,int Y,int W,int H,const char * l)91 Flu_Spinner::Flu_Spinner (int X, int Y, int W, int H, const char *l)
92 :
93 Fl_Valuator (X, Y, W, H, l),
94 _input (this, X, Y, W, H, 0)
95 {
96 // we always want the buttons to be square and half the height of the widget
97 int wid = W * 15 / 100;
98
99 if (wid < H / 2)
100 wid = H / 2;
101
102 _dragging = false;
103 _editable = true;
104 _totalTime = 0.0f;
105 _initialDelay = 0.5f;
106 _repeatTime[0] = 0.1f;
107 _repeatTime[1] = 0.02f;
108 _rapidDelay = 2.0f;
109 _doRepeat = true;
110 _pushed = false;
111 _valbox[0] = _valbox[1] = FL_UP_BOX;
112
113 box (FL_DOWN_BOX);
114 align (FL_ALIGN_LEFT);
115 when (FL_WHEN_CHANGED);
116 precision (2);
117 range (0, 1);
118 value (0);
119
120 {
121 _input.callback (input_cb, this);
122 _input.resize (X, Y, W - wid - 1, H);
123 _input.color (FL_WHITE, FL_SELECTION_COLOR);
124 _input.textfont (FL_HELVETICA);
125 _input.textsize (FL_NORMAL_SIZE);
126 _input.textcolor (FL_FOREGROUND_COLOR);
127 _input.type (FL_FLOAT_INPUT);
128 value_damage ();
129 }
130 }
131
~Flu_Spinner()132 Flu_Spinner::~Flu_Spinner ()
133 {
134 Fl::remove_timeout (repeat_callback, this);
135 }
136
137 // taken from Fl_Counter.cxx
138 void
input_cb(Fl_Widget *,void * v)139 Flu_Spinner::input_cb (Fl_Widget *, void *v)
140 {
141 Flu_Spinner & t = *(Flu_Spinner *) v;
142 if (t.align () & FL_ALIGN_INSIDE)
143 return;
144 double nv;
145
146 if ((t.step () - floor (t.step ())) > 0.0 || t.step () == 0.0)
147 nv = strtod (t._input.value (), 0);
148 else
149 nv = strtol (t._input.value (), 0, 0);
150 if (nv != t.value () || t._input.when () & FL_WHEN_NOT_CHANGED) {
151 if (nv < t.minimum ()) {
152 t.set_value (t.minimum ());
153 t.value_damage ();
154 } else if (nv > t.maximum ()) {
155 t.set_value (t.maximum ());
156 t.value_damage ();
157 } else
158 t.set_value (nv);
159
160 if (t.when ()) {
161 t.clear_changed ();
162 t.do_callback ();
163 } else {
164 t.set_changed ();
165 }
166 }
167
168 t.value_damage ();
169 }
170
171 void
resize(int X,int Y,int W,int H)172 Flu_Spinner::resize (int X, int Y, int W, int H)
173 {
174 // we always want the buttons to be square and half the height of the widget
175 Fl_Valuator::resize (X, Y, W, H);
176 }
177
178 void
value_damage()179 Flu_Spinner::value_damage ()
180 {
181 char *buf;
182
183 if (align () & FL_ALIGN_INSIDE) {
184 int len = strlen (label ());
185
186 buf = (char *) malloc (len + 128);
187 sprintf (buf, "%s", label ());
188 format (buf + len);
189 } else {
190 buf = (char *) malloc (128);
191 format (buf);
192 }
193 _input.value (buf);
194
195 if (align () == FL_ALIGN_INSIDE || !_editable)
196 _input.position (_input.size ());
197 else
198 _input.position (0, _input.size ());
199
200 free (buf);
201 }
202
203 void
draw()204 Flu_Spinner::draw ()
205 {
206 int W = w () * 15 / 100;
207
208 if (W < h () / 2)
209 W = h () / 2;
210 int X = x () + w () - W, Y = y ();
211
212 // fltk 2.0 behavior
213 bool refresh;
214
215 if (step () >= 1.0) {
216 refresh = (_input.type () != FL_INT_INPUT);
217 _input.type (FL_INT_INPUT);
218 } else {
219 refresh = (_input.type () != FL_FLOAT_INPUT);
220 _input.type (FL_FLOAT_INPUT);
221 }
222 if (refresh)
223 value_damage ();
224
225 // draw the up/down arrow buttons
226 fl_draw_box ((Fl_Boxtype) _valbox[0], X, Y, W, h () / 2, color ());
227 fl_draw_box ((Fl_Boxtype) _valbox[1], X, Y + h () / 2, W, h () / 2, color ());
228 fl_color (active_r ()? FL_FOREGROUND_COLOR : fl_inactive (FL_FOREGROUND_COLOR));
229 fl_polygon (X + 4, Y + h () / 2 - 4, X + W / 2, Y + 4, X + W - 4, Y + h () / 2 - 4);
230 Y += h () / 2;
231 fl_polygon (X + 4, Y + 4, X + W / 2, Y + h () / 2 - 4, X + W - 4, Y + 4);
232
233 _input.resize (x (), y (), w () - h () / 2 - 1, h ());
234 _input.redraw ();
235 }
236
237 void
increment_cb()238 Flu_Spinner::increment_cb ()
239 {
240 int oldWhen = when ();
241
242 int amt = Fl::event_state (FL_SHIFT | FL_CTRL | FL_ALT) ? 10 : 1;
243
244 if (_up)
245 handle_drag (clamp (increment (value (), 1 * amt)));
246 else
247 handle_drag (clamp (increment (value (), -1 * amt)));
248 when (oldWhen);
249 _lastValue = value ();
250 }
251
252 void
repeat_callback(void * arg)253 Flu_Spinner::repeat_callback (void *arg)
254 {
255 Flu_Spinner *c = (Flu_Spinner *) arg;
256
257 if (!c->_pushed)
258 return;
259 c->increment_cb ();
260
261 float delay = c->_repeatTime[0];
262
263 if (c->_pushed && c->_totalTime >= c->_rapidDelay)
264 delay = c->_repeatTime[1];
265
266 c->_totalTime += delay;
267
268 Fl::repeat_timeout (delay, repeat_callback, c);
269 }
270
271 int
handle(int event)272 Flu_Spinner::handle (int event)
273 {
274 int W = w () * 15 / 100;
275
276 if (W < h () / 2)
277 W = h () / 2;
278 int X = x () + w () - W, Y = y ();
279
280 if ((align () & FL_ALIGN_INSIDE) || !_editable) {
281 _input.readonly (true);
282 _input.cursor_color (FL_WHITE);
283 } else {
284 _input.readonly (false);
285 _input.cursor_color (FL_BLACK);
286 }
287
288 switch (event) {
289 case FL_PUSH:
290 _dragging = true;
291 if (Fl::visible_focus () && handle (FL_FOCUS))
292 Fl::focus (this);
293 _lastValue = value ();
294 _lastY = Fl::event_y ();
295 Fl::remove_timeout (repeat_callback, this);
296 if (Fl::event_inside (X, Y, W, h () / 2)) // up button
297 {
298 _pushed = true;
299 _valbox[0] = FL_DOWN_BOX;
300 _up = true;
301 }
302 if (Fl::event_inside (X, Y + h () / 2, W, h () / 2)) // down button
303 {
304 _pushed = true;
305 _valbox[1] = FL_DOWN_BOX;
306 _up = false;
307 }
308 if (_pushed) {
309 increment_cb ();
310 _totalTime = _initialDelay;
311 if (_doRepeat)
312 Fl::add_timeout (_initialDelay, repeat_callback, this);
313 handle_push ();
314 take_focus ();
315 redraw ();
316 return 1;
317 }
318 break;
319
320 case FL_DRAG:
321 {
322 // only do the dragging if the last Y differs from the current Y by more than 3 pixels
323 if (ABS (_lastY - Fl::event_y ()) < 3)
324 break;
325 _dragging = true;
326 _pushed = false;
327 Fl::remove_timeout (repeat_callback, this);
328 int oldWhen = when ();
329
330 handle_drag (clamp
331 (increment
332 (_lastValue,
333 (_lastY -
334 Fl::event_y ()) *
335 (Fl::event_state (FL_SHIFT | FL_CTRL | FL_ALT) ? 10 : 1))));
336 _valbox[0] = _valbox[1] = FL_DOWN_BOX;
337 when (oldWhen);
338 fl_cursor ((Fl_Cursor) 22);
339 _input.redraw ();
340 redraw ();
341 }
342 break;
343
344 case FL_RELEASE:
345 {
346 bool doCB = ((when () & FL_WHEN_RELEASE)
347 || (when () & FL_WHEN_RELEASE_ALWAYS)) && (_pushed
348 ||
349 ((_valbox[0] ==
350 FL_DOWN_BOX) ^
351 (_valbox[1] ==
352 FL_DOWN_BOX)));
353 _pushed = false;
354 _dragging = false;
355 Fl::remove_timeout (repeat_callback, this);
356 _valbox[0] = _valbox[1] = FL_UP_BOX;
357 fl_cursor (FL_CURSOR_DEFAULT);
358 redraw ();
359 handle_release ();
360 if (doCB)
361 do_callback ();
362 _input.take_focus ();
363 }
364 break;
365
366 case FL_FOCUS:
367 case FL_UNFOCUS:
368 redraw ();
369 _input.take_focus ();
370 return 0;
371
372 case FL_ENTER:
373 if (Fl::event_inside (&_input))
374 return _input.handle (event);
375 else if (active_r ()) {
376 fl_cursor (FL_CURSOR_DEFAULT);
377 return 1;
378 }
379 break;
380
381 case FL_MOVE:
382 return 1;
383 break;
384
385 case FL_LEAVE:
386 if (Fl::event_inside (&_input))
387 return _input.handle (event);
388 else if (active_r ()) {
389 fl_cursor (FL_CURSOR_DEFAULT);
390 return 1;
391 }
392 break;
393
394 case FL_KEYBOARD:
395 switch (Fl::event_key ()) {
396 case FL_Down:
397 {
398 int oldWhen = when ();
399
400 when (FL_WHEN_CHANGED);
401 handle_drag (clamp
402 (increment
403 (value (),
404 -1 *
405 (Fl::
406 event_state (FL_SHIFT | FL_CTRL | FL_ALT) ? 10 : 1))));
407 when (oldWhen);
408 redraw ();
409 return 1;
410 }
411 case FL_Up:
412 {
413 int oldWhen = when ();
414
415 when (FL_WHEN_CHANGED);
416 handle_drag (clamp
417 (increment
418 (value (),
419 1 *
420 (Fl::
421 event_state (FL_SHIFT | FL_CTRL | FL_ALT) ? 10 : 1))));
422 when (oldWhen);
423 redraw ();
424 return 1;
425 }
426 }
427 break;
428 }
429
430 return _input.handle (event);
431 }
432