1 /*
2 * This file is part of the XForms library package.
3 *
4 * XForms is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation; either version 2.1, or
7 * (at your option) any later version.
8 *
9 * XForms is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with XForms. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "include/forms.h"
24 #include "flinternal.h"
25 #include "private/pspinner.h"
26
27 #include <string.h>
28 #include <stdlib.h>
29 #include <float.h>
30
31
32 static void
33 set_spinner_return( FL_OBJECT *,
34 unsigned int );
35
36
37 /***************************************
38 * This function got to be called before a redraw, at least if
39 * the form the spinner belongs to has been resized or proper-
40 * ties of the spinner have been changed. It calculates the
41 * new positions and sizes of the objects the spinner widget
42 * is made up from.
43 ***************************************/
44
45 static void
set_geom(FL_OBJECT * obj)46 set_geom( FL_OBJECT * obj )
47 {
48 FLI_SPINNER_SPEC *sp = obj->spec;
49 FL_Coord bwh;
50
51 if ( obj->w >= obj->h )
52 {
53 bwh = obj->h / 2;
54 bwh = FL_max( bwh, 1 );
55 obj->h = 2 * bwh;
56
57 sp->input->x = obj->x;
58 sp->input->y = obj->y;
59 sp->input->w = obj->w - bwh - 1;
60 sp->input->h = obj->h;
61
62 sp->up->x = sp->down->x = obj->x + obj->w - bwh - 1;
63 sp->up->y = obj->y;
64 sp->down->y = obj->y + bwh;
65 sp->up->w = sp->up->h = sp->down->w = sp->down->h = bwh;
66
67 if ( sp->orient == 1 )
68 {
69 sp->orient = 0;
70 fl_set_object_label( sp->up, "@8>" );
71 fl_set_object_label( sp->down, "@2>" );
72 }
73 }
74 else
75 {
76 bwh = obj->w / 2;
77 bwh = FL_max( bwh, 1 );
78 obj->w = 2 * bwh;
79
80 sp->input->x = obj->x;
81 sp->input->y = obj->y;
82 sp->input->w = obj->w;
83 sp->input->h = obj->h - bwh - 1;
84
85 sp->up->y = sp->down->y = obj->y + obj->h - bwh - 1;
86 sp->up->x = obj->x + bwh;
87 sp->down->x = obj->x;
88 sp->up->w = sp->up->h = sp->down->w = sp->down->h = bwh;
89
90 if ( sp->orient == 0 )
91 {
92 sp->orient = 1;
93 fl_set_object_label( sp->up, "@6>" );
94 fl_set_object_label( sp->down, "@4>" );
95 }
96 }
97
98 /* Also avoid changes of the box type and check for color changes
99 (that must be passed on to the child input object) */
100
101 obj->boxtype = FL_NO_BOX;
102 sp->input->col1 = obj->col1;
103 sp->input->col2 = obj->col2;
104 }
105
106
107 /***************************************
108 * There aren't many types of events a spinner object must directly
109 * react to. If it got resized the positions and sizes of the child
110 * objects it's made up from need to be recalculated, but that can
111 * be deferred until it's redrawn. Drawing it works mostly auto-
112 * matically except the redraw of the label. And on deletion only
113 * the FLI_SPINNER_SPEC needs to be deallocated (the child objects
114 * referenced there-in have already been deallocated before).
115 ***************************************/
116
117 static int
handle_spinner(FL_OBJECT * obj,int event,FL_Coord mx FL_UNUSED_ARG,FL_Coord my FL_UNUSED_ARG,int key FL_UNUSED_ARG,void * ev FL_UNUSED_ARG)118 handle_spinner( FL_OBJECT * obj,
119 int event,
120 FL_Coord mx FL_UNUSED_ARG,
121 FL_Coord my FL_UNUSED_ARG,
122 int key FL_UNUSED_ARG,
123 void * ev FL_UNUSED_ARG )
124 {
125 FLI_SPINNER_SPEC *sp = obj->spec;
126
127 switch ( event )
128 {
129 case FL_ATTRIB :
130 case FL_RESIZED :
131 obj->align = fl_to_outside_lalign( obj->align );
132 sp->attrib = 1;
133 break;
134
135 case FL_DRAW :
136 if ( sp->attrib )
137 {
138 set_geom( obj );
139 sp->attrib = 0;
140 }
141
142 /* fall through */
143
144 case FL_DRAWLABEL :
145 fl_draw_object_label_outside( obj );
146 break;
147
148 case FL_FREEMEM :
149 fli_safe_free( obj->spec );
150 break;
151 }
152
153 return 0;
154 }
155
156
157 /***************************************
158 * Callback for the input and button objects of the spinner,
159 * called with 'data' set to 0 for the input object and with
160 * 'data' being 1 or -1 for the up and down buttons
161 ***************************************/
162
163 static void
spinner_callback(FL_OBJECT * obj,long data)164 spinner_callback( FL_OBJECT * obj,
165 long data )
166 {
167 FLI_SPINNER_SPEC *sp = obj->parent->spec;
168 const char *s_val = fl_get_input( sp->input );
169 int max_len = 4 + sp->prec + log10( DBL_MAX );
170 char *buf = NULL;
171
172 /* Don't react to editing the input field unless user ended interaction */
173
174 if ( data == 0 && ! ( obj->returned & FL_RETURN_END ) )
175 return;
176
177 obj->parent->returned = FL_RETURN_NONE;
178
179 if ( obj->parent->type == FL_INT_SPINNER )
180 {
181 int old_i_val = sp->i_val;
182
183 if ( data == 0 ) /* spinners input object was modified */
184 {
185 char *eptr;
186 long i_val = strtol( s_val, &eptr, 10 );
187
188 /* Check for an invalid value entered */
189
190 if ( eptr == s_val || i_val > sp->i_max || i_val < sp->i_min )
191 {
192 if ( ! ( obj->returned & FL_RETURN_END ) )
193 return;
194
195 if ( i_val > sp->i_max )
196 sp->i_val = sp->i_max;
197 else if ( i_val < sp->i_min )
198 sp->i_val = sp->i_min;
199 }
200 else
201 sp->i_val = i_val;
202 }
203 else if ( data == 1 && obj->returned & FL_RETURN_CHANGED )
204 {
205 if ( sp->i_val <= sp->i_max - sp->i_incr )
206 sp->i_val += sp->i_incr;
207 else
208 sp->i_val = sp->i_max;
209 }
210 else if ( data == -1 && obj->returned & FL_RETURN_CHANGED )
211 {
212 if ( sp->i_val >= sp->i_min + sp->i_incr )
213 sp->i_val -= sp->i_incr;
214 else
215 sp->i_val = sp->i_min;
216 }
217
218 buf = fl_malloc( max_len );
219 sprintf( buf, "%d", sp->i_val );
220 fl_set_input( sp->input, buf );
221 fl_free( buf );
222 buf = NULL;
223
224 if ( data != 0 )
225 {
226 if ( sp->i_val != old_i_val )
227 obj->parent->returned |= FL_RETURN_CHANGED;
228 }
229 else if ( obj->returned & FL_RETURN_END && sp->i_val != sp->old_ival )
230 obj->parent->returned |= FL_RETURN_CHANGED;
231
232 if ( obj->returned & FL_RETURN_END )
233 {
234 obj->parent->returned |= FL_RETURN_END;
235 sp->old_ival = sp->i_val;
236 }
237 }
238 else
239 {
240 double old_f_val = sp->f_val;
241
242 if ( data == 0 )
243 {
244 char *eptr;
245 double f_val = strtod( s_val, &eptr );
246
247 if ( eptr == s_val || f_val > sp->f_max || f_val < sp->f_min )
248 {
249 if ( ! ( obj->returned & FL_RETURN_END ) )
250 return;
251
252 if ( f_val > sp->f_max )
253 sp->f_val = sp->f_max;
254 if ( f_val < sp->f_min )
255 sp->f_val = sp->f_min;
256 }
257 else
258 sp->f_val = f_val;
259 }
260 else if ( data == 1 && obj->returned & FL_RETURN_CHANGED )
261 {
262 if ( sp->f_val <= sp->f_max - sp->f_incr )
263 sp->f_val += sp->f_incr;
264 else
265 sp->f_val = sp->f_max;
266 }
267 else if ( data == -1 && obj->returned & FL_RETURN_CHANGED )
268 {
269 if ( sp->f_val >= sp->f_min + sp->f_incr )
270 sp->f_val -= sp->f_incr;
271 else
272 sp->f_val = sp->f_min;
273 }
274
275 buf = malloc( max_len );
276 sprintf( buf, "%.*f", sp->prec, sp->f_val );
277 fl_set_input( sp->input, buf );
278 fl_free( buf );
279 buf = NULL;
280
281 if ( data != 0 )
282 {
283 if ( sp->f_val != old_f_val )
284 obj->parent->returned |= FL_RETURN_CHANGED;
285 }
286 else if ( obj->returned & FL_RETURN_END && sp->f_val != sp->old_fval )
287 obj->parent->returned |= FL_RETURN_CHANGED;
288
289 if ( obj->returned & FL_RETURN_END )
290 {
291 obj->parent->returned |= FL_RETURN_END;
292 sp->old_fval = sp->f_val;
293 }
294 }
295 }
296
297
298 /***************************************
299 ***************************************/
300
301 FL_OBJECT *
fl_create_spinner(int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * label)302 fl_create_spinner( int type,
303 FL_Coord x,
304 FL_Coord y,
305 FL_Coord w,
306 FL_Coord h,
307 const char * label )
308 {
309 FL_OBJECT *obj;
310 FLI_SPINNER_SPEC *sp;
311 int orient = ( w < h );
312
313 obj = fl_make_object( FL_SPINNER, type, x, y, w, h, label, handle_spinner );
314 obj->boxtype = FL_NO_BOX;
315 obj->align = FL_ALIGN_LEFT;
316 obj->set_return = set_spinner_return;
317 obj->spec = sp = malloc( sizeof *sp );
318
319 sp->input = fl_create_input( type == FL_INT_SPINNER ?
320 FL_INT_INPUT : FL_FLOAT_INPUT,
321 0, 0, 0, 0, NULL );
322 sp->up = fl_create_button( FL_TOUCH_BUTTON, 0, 0, 0, 0,
323 orient == 0 ? "@8>" : "@6>" );
324 sp->down = fl_create_button( FL_TOUCH_BUTTON, 0, 0, 0, 0,
325 orient == 0 ? "@2>" : "@4>" );
326
327 fl_set_object_callback( sp->input, spinner_callback, 0 );
328 fl_set_object_callback( sp->up, spinner_callback, 1 );
329 fl_set_object_callback( sp->down, spinner_callback, -1 );
330
331 fl_set_button_mouse_buttons( sp->up, 1 );
332 fl_set_button_mouse_buttons( sp->down, 1 );
333
334 fl_set_object_lcolor( sp->up, FL_BLUE );
335 fl_set_object_lcolor( sp->down, FL_BLUE );
336
337 obj->col1 = sp->input->col1;
338 obj->col2 = sp->input->col2;
339
340 sp->i_val = sp->old_ival = 0;
341 sp->i_min = - 10000;
342 sp->i_max = 10000;
343 sp->i_incr = 1;
344
345 sp->f_val = sp->old_fval = 0.0;
346 sp->f_min = - 10000.0;
347 sp->f_max = 10000.0;
348 sp->f_incr = 1.0;
349
350 sp->orient = orient;
351 sp->prec = DEFAULT_SPINNER_PRECISION;
352 sp->attrib = 1;
353
354 fl_add_child( obj, sp->input );
355 fl_add_child( obj, sp->up );
356 fl_add_child( obj, sp->down );
357
358 fl_set_input( sp->input, type == FL_INT_SPINNER ? "0" : "0.0" );
359
360 /* Set default return policy for a spinner object */
361
362 fl_set_object_return( obj, FL_RETURN_CHANGED );
363
364 return obj;
365 }
366
367
368 /***************************************
369 ***************************************/
370
371 FL_OBJECT *
fl_add_spinner(int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * label)372 fl_add_spinner( int type,
373 FL_Coord x,
374 FL_Coord y,
375 FL_Coord w,
376 FL_Coord h,
377 const char * label )
378 {
379 FL_OBJECT *obj = fl_create_spinner( type, x, y, w, h, label );
380
381 fl_add_object( fl_current_form, obj );
382
383 return obj;
384 }
385
386
387 /***************************************
388 * Returns the spinner value
389 ***************************************/
390
391 double
fl_get_spinner_value(FL_OBJECT * obj)392 fl_get_spinner_value( FL_OBJECT * obj )
393 {
394 FLI_SPINNER_SPEC *sp = obj->spec;
395 const char *s_val = fl_get_input( sp->input );
396 char *eptr;
397
398 if ( obj->type == FL_INT_SPINNER )
399 {
400 long i_val = strtol( s_val, &eptr, 10 );
401
402 if ( eptr == s_val || i_val > sp->i_max || i_val < sp->i_min )
403 i_val = sp->i_val;
404
405 return sp->i_val = i_val;
406 }
407 else
408 {
409 double f_val = strtod( s_val, &eptr );
410
411 if ( ( *eptr && *eptr != 'e' && *eptr != 'E' )
412 || errno == ERANGE
413 || f_val > sp->f_max
414 || f_val < sp->f_min )
415 f_val = sp->f_val;
416
417 if ( *eptr )
418 {
419 int max_len = 4 + sp->prec + log10( DBL_MAX );
420 char *buf = fl_malloc( max_len );
421
422 sprintf( buf, "%.*f", sp->prec, f_val );
423 fl_set_input( sp->input, buf );
424 fl_free( buf );
425 }
426
427 return sp->f_val = f_val;
428 }
429 }
430
431
432 /***************************************
433 * Sets the spinner value
434 ***************************************/
435
436 double
fl_set_spinner_value(FL_OBJECT * obj,double val)437 fl_set_spinner_value( FL_OBJECT * obj,
438 double val )
439 {
440 FLI_SPINNER_SPEC *sp = obj->spec;
441
442 if ( obj->type == FL_INT_SPINNER )
443 {
444 sp->i_val = FL_nint( val );
445
446 if ( val > sp->i_max )
447 sp->i_val = sp->i_max;
448 else if ( val < sp->i_min )
449 sp->i_val = sp->i_min;
450
451 fl_set_input_f( sp->input, "%d", sp->i_val );
452
453 return sp->old_ival = sp->i_val;
454 }
455
456 sp->f_val = val;
457
458 if ( val > sp->f_max )
459 sp->f_val = sp->f_max;
460 else if ( val < sp->f_min )
461 sp->f_val = sp->f_min;
462
463 fl_set_input_f( sp->input, "%.*f", sp->prec, sp->f_val );
464
465 return sp->old_fval = sp->f_val;
466 }
467
468
469 /***************************************
470 * Sets the lower and upper bound of the spinner value
471 ***************************************/
472
473 void
fl_set_spinner_bounds(FL_OBJECT * obj,double min,double max)474 fl_set_spinner_bounds( FL_OBJECT * obj,
475 double min,
476 double max )
477 {
478 FLI_SPINNER_SPEC *sp = obj->spec;
479
480 if ( min > max )
481 {
482 double tmp = min;
483 min = max;
484 max = tmp;
485 }
486
487 if ( obj->type == FL_INT_SPINNER )
488 {
489 sp->i_min = FL_nint( min );
490 sp->i_max = FL_nint( max );
491
492 if ( min < INT_MIN )
493 sp->i_min = INT_MIN;
494 else if ( min > INT_MAX )
495 sp->i_min = INT_MAX;
496
497 if ( max < INT_MIN )
498 sp->i_max = INT_MIN;
499 else if ( max > INT_MAX )
500 sp->i_max = INT_MAX;
501
502 fl_get_spinner_value( obj );
503 fl_set_spinner_value( obj, sp->i_val );
504 }
505 else
506 {
507 sp->f_min = min;
508 sp->f_max = max;
509
510 if ( min < - DBL_MAX )
511 sp->f_min = - DBL_MAX;
512 else if ( min > DBL_MAX )
513 sp->f_min = DBL_MAX;
514
515 if ( max < - DBL_MAX )
516 sp->f_max = - DBL_MAX;
517 else if ( max > DBL_MAX )
518 sp->f_max = DBL_MAX;
519
520 fl_get_spinner_value( obj );
521 fl_set_spinner_value( obj, sp->f_val );
522 }
523 }
524
525
526 /***************************************
527 * Returns the lower and upper limit of the spinner value
528 ***************************************/
529
530 void
fl_get_spinner_bounds(FL_OBJECT * obj,double * min,double * max)531 fl_get_spinner_bounds( FL_OBJECT * obj,
532 double * min,
533 double * max )
534 {
535 FLI_SPINNER_SPEC *sp = obj->spec;
536
537 if ( obj->type == FL_INT_SPINNER )
538 {
539 *min = sp->i_min;
540 *max = sp->i_max;
541 }
542 else
543 {
544 *min = sp->f_min;
545 *max = sp->f_max;
546 }
547 }
548
549
550 /***************************************
551 * Sets the step size (increment or decrement value when the up
552 * or down button is clicked on) for the spinner. For FL_INT_SPINNER
553 * objects the number is rounded to the nearest integer.
554 ***************************************/
555
556 void
fl_set_spinner_step(FL_OBJECT * obj,double step)557 fl_set_spinner_step( FL_OBJECT * obj,
558 double step )
559 {
560 FLI_SPINNER_SPEC *sp = obj->spec;
561
562 if ( step <= 0.0 )
563 return;
564
565 if ( obj->type == FL_INT_SPINNER )
566 {
567 int istep = FL_nint( step );
568
569 if ( istep == 0 )
570 istep = 1;
571
572 if ( istep > sp->i_max - sp->i_min )
573 sp->i_incr = sp->i_max - sp->i_min;
574 else
575 sp->i_incr = istep;
576 }
577 else
578 {
579 if ( step > sp->f_max - sp->f_min )
580 sp->f_incr = sp->f_max - sp->f_min;
581 else
582 sp->f_incr = step;
583 }
584 }
585
586
587 /***************************************
588 * Returns the step size (increment or decrement value when the up
589 * or down button is clicked on) for the spinner
590 ***************************************/
591
592 double
fl_get_spinner_step(FL_OBJECT * obj)593 fl_get_spinner_step( FL_OBJECT * obj )
594 {
595 FLI_SPINNER_SPEC *sp = obj->spec;
596
597 return obj->type == FL_INT_SPINNER ? sp->i_incr : sp->f_incr;
598 }
599
600
601 /***************************************
602 * Sets the number of digits to be shown agter the decimal point (works
603 * on FL_FLOAT_SPINNER objects only)
604 ***************************************/
605
606 void
fl_set_spinner_precision(FL_OBJECT * obj,int prec)607 fl_set_spinner_precision( FL_OBJECT * obj,
608 int prec )
609 {
610 FLI_SPINNER_SPEC *sp = obj->spec;
611
612 if ( obj->type == FL_INT_SPINNER || prec < 0 )
613 return;
614
615 if ( prec > DBL_DIG )
616 prec = DBL_DIG;
617 if ( prec < 0 )
618 prec = 0;
619
620 if ( sp->prec != prec )
621 {
622 sp->prec = prec;
623 fl_set_spinner_value( obj, fl_get_spinner_value( obj ) );
624 }
625 }
626
627
628 /***************************************
629 * Returns the number of digits shown after the decimal point
630 * (always returns 0 for FL_INT_SPINNER objects)
631 ***************************************/
632
633 int
fl_get_spinner_precision(FL_OBJECT * obj)634 fl_get_spinner_precision( FL_OBJECT * obj )
635 {
636 if ( obj->type == FL_INT_SPINNER )
637 return 0;
638
639 return ( ( FLI_SPINNER_SPEC * ) obj->spec )->prec;
640 }
641
642
643 /***************************************
644 * Returns the input sub-object
645 ***************************************/
646
647 FL_OBJECT *
fl_get_spinner_input(FL_OBJECT * obj)648 fl_get_spinner_input( FL_OBJECT * obj )
649 {
650 return ( ( FLI_SPINNER_SPEC * ) obj->spec )->input;
651 }
652
653
654 /***************************************
655 * Returns the button sub-object for raising the spinner value
656 ***************************************/
657
658 FL_OBJECT *
fl_get_spinner_up_button(FL_OBJECT * obj)659 fl_get_spinner_up_button( FL_OBJECT * obj )
660 {
661 return ( ( FLI_SPINNER_SPEC * ) obj->spec )->up;
662 }
663
664
665 /***************************************
666 * Returns the button sub-object for lowering the spinner value
667 ***************************************/
668
669 FL_OBJECT *
fl_get_spinner_down_button(FL_OBJECT * obj)670 fl_get_spinner_down_button( FL_OBJECT * obj )
671 {
672 return ( ( FLI_SPINNER_SPEC * ) obj->spec )->down;
673 }
674
675
676 /***************************************
677 * Sets under which conditions the object is to be returned to the
678 * application. This function is for internal use only and to set
679 * the return policy for a spinner fl_set_object_return() should
680 * be used instead (which then will call this function).
681 ***************************************/
682
683 static void
set_spinner_return(FL_OBJECT * obj,unsigned int when)684 set_spinner_return( FL_OBJECT * obj,
685 unsigned int when )
686 {
687 FLI_SPINNER_SPEC *sp = obj->spec;
688
689 obj->how_return = when;
690 fl_set_object_return( sp->input, FL_RETURN_ALWAYS );
691 fl_set_object_return( sp->up, FL_RETURN_CHANGED | FL_RETURN_END );
692 fl_set_object_return( sp->down, FL_RETURN_CHANGED | FL_RETURN_END );
693 }
694
695
696 /*
697 * Local variables:
698 * tab-width: 4
699 * indent-tabs-mode: nil
700 * End:
701 */
702