Revalidate current value whenever MUIA_Numeric_(Min|Max) attributes are
[aros:aros.git] / AROS / workbench / libs / muimaster / classes / numeric.c
1 /*
2     Copyright © 2002-2013, The AROS Development Team. All rights reserved.
3     $Id$
4 */
5
6 #include <devices/rawkeycodes.h>
7 #include <stdio.h>
8
9 #include <clib/alib_protos.h>
10 #include <proto/exec.h>
11 #include <proto/intuition.h>
12 #include <proto/graphics.h>
13 #include <proto/utility.h>
14 #include <proto/muimaster.h>
15
16 #include "debug.h"
17 #include "mui.h"
18 #include "muimaster_intern.h"
19 #include "support.h"
20
21 struct MUI_NumericData
22 {
23     STRPTR format;
24     LONG defvalue;
25     LONG max;
26     LONG min;
27     LONG value;
28     ULONG flags;
29     struct MUI_EventHandlerNode ehn;
30     char buf[50];
31 };
32
33 enum numeric_flags
34 {
35     NUMERIC_REVERSE = (1 << 0),
36     NUMERIC_REVLEFTRIGHT = (1 << 1),
37     NUMERIC_REVUPDOWN = (1 << 2),
38     NUMERIC_CHECKALLSIZES = (1 << 3),
39 };
40
41 extern struct Library *MUIMasterBase;
42
43 /**************************************************************************
44  OM_NEW
45 **************************************************************************/
46 IPTR Numeric__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
47 {
48     struct MUI_NumericData *data;
49     struct TagItem *tags, *tag;
50
51     BOOL value_set = FALSE;
52
53     obj = (Object *) DoSuperMethodA(cl, obj, (Msg) msg);
54     if (!obj)
55         return 0;
56
57     data = INST_DATA(cl, obj);
58     data->format = "%ld";
59     data->max = 100;
60     data->min = 0;
61     data->flags = 0;
62
63     for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
64     {
65         switch (tag->ti_Tag)
66         {
67         case MUIA_Numeric_CheckAllSizes:
68             _handle_bool_tag(data->flags, tag->ti_Data,
69                 NUMERIC_CHECKALLSIZES);
70             break;
71         case MUIA_Numeric_Default:
72             /* data->defvalue = CLAMP(tag->ti_Data, data->min, data->max); */
73             data->defvalue = tag->ti_Data;
74             break;
75         case MUIA_Numeric_Format:
76             data->format = (STRPTR) tag->ti_Data;
77             break;
78         case MUIA_Numeric_Max:
79             data->max = tag->ti_Data;
80             break;
81         case MUIA_Numeric_Min:
82             data->min = tag->ti_Data;
83             break;
84         case MUIA_Numeric_Reverse:
85             _handle_bool_tag(data->flags, tag->ti_Data, NUMERIC_REVERSE);
86             break;
87         case MUIA_Numeric_RevLeftRight:
88             _handle_bool_tag(data->flags, tag->ti_Data,
89                 NUMERIC_REVLEFTRIGHT);
90             break;
91         case MUIA_Numeric_RevUpDown:
92             _handle_bool_tag(data->flags, tag->ti_Data, NUMERIC_REVUPDOWN);
93             break;
94         case MUIA_Numeric_Value:
95             value_set = TRUE;
96             data->value = (LONG) tag->ti_Data;
97             break;
98         }
99     }
100
101     data->value =
102         CLAMP(value_set ? data->value : data->defvalue, data->min,
103         data->max);
104
105     return (IPTR) obj;
106 }
107
108 /**************************************************************************
109  OM_SET
110 **************************************************************************/
111 IPTR Numeric__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
112 {
113     struct MUI_NumericData *data = INST_DATA(cl, obj);
114     struct TagItem *tags, *tag;
115     LONG oldval, oldmin, oldmax;
116     STRPTR oldfmt;
117     IPTR ret;
118     BOOL values_changed = FALSE;
119
120     oldval = data->value;
121     oldfmt = data->format;
122     oldmin = data->min;
123     oldmax = data->max;
124
125     for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
126     {
127         switch (tag->ti_Tag)
128         {
129         case MUIA_Numeric_CheckAllSizes:
130             _handle_bool_tag(data->flags, tag->ti_Data,
131                 NUMERIC_CHECKALLSIZES);
132             break;
133         case MUIA_Numeric_Default:
134             /* data->defvalue = CLAMP(tag->ti_Data, data->min, data->max); */
135             data->defvalue = tag->ti_Data;
136             break;
137         case MUIA_Numeric_Format:
138             data->format = (STRPTR) tag->ti_Data;
139             break;
140         case MUIA_Numeric_Max:
141             data->max = tag->ti_Data;
142             data->value = CLAMP(data->value, data->min, data->max);
143             break;
144         case MUIA_Numeric_Min:
145             data->min = tag->ti_Data;
146             data->value = CLAMP(data->value, data->min, data->max);
147             break;
148         case MUIA_Numeric_Reverse:
149             _handle_bool_tag(data->flags, tag->ti_Data, NUMERIC_REVERSE);
150             break;
151         case MUIA_Numeric_RevLeftRight:
152             _handle_bool_tag(data->flags, tag->ti_Data,
153                 NUMERIC_REVLEFTRIGHT);
154             break;
155         case MUIA_Numeric_RevUpDown:
156             _handle_bool_tag(data->flags, tag->ti_Data, NUMERIC_REVUPDOWN);
157             break;
158         case MUIA_Numeric_Value:
159             tag->ti_Data = CLAMP((LONG) tag->ti_Data, data->min, data->max);
160
161             if (data->value == (LONG) tag->ti_Data)
162                 tag->ti_Tag = TAG_IGNORE;
163             else
164                 data->value = (LONG) tag->ti_Data;
165
166             break;
167         }
168     }
169
170     /* If the max, min or format values changed, then the minimum and maximum
171        sizes of the string output by MUIM_Numeric_Stringify may have changed,
172        so give the subclass a chance to recalculate them and relayout the group
173        accordingly. Basically, the subclass will have to react on changes to
174        these values as well (by setting a notification on them, or by
175        overriding OM_SET) and then recalculate the minimum and maximum sizes
176        for the object. */
177     if (data->format != oldfmt || data->min != oldmin
178         || data->max != oldmax)
179     {
180         values_changed = TRUE;
181         Object *parent = _parent(obj);
182         if (parent)
183         {
184             DoMethod(parent, MUIM_Group_InitChange);
185             DoMethod(parent, MUIM_Group_ExitChange);
186         }
187     }
188
189     ret = DoSuperMethodA(cl, obj, (Msg) msg);
190
191     if (data->value != oldval || values_changed)
192     {
193         MUI_Redraw(obj, MADF_DRAWUPDATE);
194     }
195
196     return ret;
197 }
198
199 /**************************************************************************
200  OM_GET
201 **************************************************************************/
202 IPTR Numeric__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
203 {
204     struct MUI_NumericData *data = INST_DATA(cl, obj);
205     IPTR *store = msg->opg_Storage;
206     ULONG tag = msg->opg_AttrID;
207
208     switch (tag)
209     {
210     case MUIA_Numeric_CheckAllSizes:
211         *store = ((data->flags & NUMERIC_CHECKALLSIZES) != 0);
212         return TRUE;
213
214     case MUIA_Numeric_Default:
215         *store = data->defvalue;
216         return TRUE;
217
218     case MUIA_Numeric_Format:
219         *store = (IPTR) data->format;
220         return TRUE;
221
222     case MUIA_Numeric_Max:
223         *store = data->max;
224         return TRUE;
225
226     case MUIA_Numeric_Min:
227         *store = data->min;
228         return TRUE;
229
230     case MUIA_Numeric_Reverse:
231         *store = ((data->flags & NUMERIC_REVERSE) != 0);
232         return TRUE;
233
234     case MUIA_Numeric_RevLeftRight:
235         *store = ((data->flags & NUMERIC_REVLEFTRIGHT) != 0);
236         return TRUE;
237
238     case MUIA_Numeric_RevUpDown:
239         *store = ((data->flags & NUMERIC_REVUPDOWN) != 0);
240         return TRUE;
241
242     case MUIA_Numeric_Value:
243         *store = data->value;
244         return TRUE;
245     }
246
247     return DoSuperMethodA(cl, obj, (Msg) msg);
248 }
249
250 /**************************************************************************
251  MUIM_Setup
252 **************************************************************************/
253 IPTR Numeric__MUIM_Setup(struct IClass *cl, Object *obj,
254     struct MUIP_Setup *msg)
255 {
256     struct MUI_NumericData *data = INST_DATA(cl, obj);
257
258     if (!DoSuperMethodA(cl, obj, (Msg) msg))
259         return FALSE;
260
261     data->ehn.ehn_Events = IDCMP_RAWKEY;
262     data->ehn.ehn_Priority = 0;
263     data->ehn.ehn_Flags = 0;
264     data->ehn.ehn_Object = obj;
265     data->ehn.ehn_Class = cl;
266     DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) (&data->ehn));
267
268     return TRUE;
269 }
270
271 /**************************************************************************
272  MUIM_Cleanup
273 **************************************************************************/
274 IPTR Numeric__MUIM_Cleanup(struct IClass *cl, Object *obj,
275     struct MUIP_Cleanup *msg)
276 {
277     struct MUI_NumericData *data = INST_DATA(cl, obj);
278
279     DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) (&data->ehn));
280     return DoSuperMethodA(cl, obj, (Msg) msg);
281 }
282
283 /**************************************************************************
284  MUIM_HandleEvent
285 **************************************************************************/
286 IPTR Numeric__MUIM_HandleEvent(struct IClass *cl, Object *obj,
287     struct MUIP_HandleEvent *msg)
288 {
289     struct MUI_NumericData *data = INST_DATA(cl, obj);
290     IPTR result = 0;
291     BOOL increase, change;
292
293     if (msg->muikey != MUIKEY_NONE)
294     {
295         LONG step;
296         BOOL use_absolute = FALSE, is_horizontal = FALSE;
297
298         result = MUI_EventHandlerRC_Eat;
299
300         if (data->max - data->min < 10)
301             step = 1;
302         else
303             step = 10;
304
305         switch (msg->muikey)
306         {
307         case MUIKEY_PRESS:
308             return MUI_EventHandlerRC_Eat;
309
310         case MUIKEY_TOGGLE:
311             DoMethod(obj, MUIM_Numeric_SetDefault);
312             return MUI_EventHandlerRC_Eat;
313
314         case MUIKEY_RELEASE:
315             return MUI_EventHandlerRC_Eat;
316
317         case MUIKEY_TOP:
318         case MUIKEY_LINESTART:
319             use_absolute = TRUE;
320             is_horizontal = TRUE;
321             step = -1;
322             break;
323
324         case MUIKEY_BOTTOM:
325         case MUIKEY_LINEEND:
326             use_absolute = TRUE;
327             is_horizontal = TRUE;
328             step = 1;
329             break;
330
331         case MUIKEY_LEFT:
332             is_horizontal = TRUE;
333             step = -1;
334             break;
335
336         case MUIKEY_RIGHT:
337             is_horizontal = TRUE;
338             step = 1;
339             break;
340
341         case MUIKEY_UP:
342             step = -1;
343             break;
344
345         case MUIKEY_DOWN:
346             step = 1;
347             break;
348
349         case MUIKEY_PAGEDOWN:
350         case MUIKEY_WORDRIGHT:
351             break;
352
353         case MUIKEY_PAGEUP:
354         case MUIKEY_WORDLEFT:
355                 step = -step;
356             break;
357
358         default:
359             return 0;
360         }
361
362         /* Send gadget in proper direction */
363         if (step != 0)
364         {
365             if (data->flags & NUMERIC_REVERSE)
366                 step = -step;
367
368             if ((is_horizontal && (data->flags & NUMERIC_REVLEFTRIGHT) != 0)
369                 || (!is_horizontal && (data->flags & NUMERIC_REVUPDOWN) != 0))
370                 step = -step;
371
372             if (use_absolute)
373             {
374                 if (step > 0)
375                     step = data->max;
376                 else
377                     step = data->min;
378                 step -= data->value;
379             }
380
381             DoMethod(obj, MUIM_Numeric_Increase, step);
382         }
383     }
384     else if (msg->imsg->Class == IDCMP_RAWKEY
385         && _isinobject(obj, msg->imsg->MouseX, msg->imsg->MouseY))
386     {
387         change = TRUE;
388         switch (msg->imsg->Code)
389         {
390         case RAWKEY_NM_WHEEL_UP:
391             increase = FALSE;
392             break;
393         case RAWKEY_NM_WHEEL_DOWN:
394             increase = TRUE;
395             break;
396         default:
397             change = FALSE;
398         }
399         if (change)
400         {
401             if (data->flags & NUMERIC_REVERSE)
402                 increase = !increase;
403             DoMethod(obj, increase ?
404                 MUIM_Numeric_Increase : MUIM_Numeric_Decrease, 1);
405             result = MUI_EventHandlerRC_Eat;
406         }
407     }
408
409     return result;
410 }
411
412
413 /**************************************************************************
414  MUIM_Numeric_Decrease
415 **************************************************************************/
416 IPTR Numeric__MUIM_Decrease(struct IClass *cl, Object *obj,
417     struct MUIP_Numeric_Decrease *msg)
418 {
419     struct MUI_NumericData *data = INST_DATA(cl, obj);
420     LONG newval = CLAMP(data->value - msg->amount, data->min, data->max);
421     if (newval != data->value)
422         set(obj, MUIA_Numeric_Value, newval);
423
424     return 1;
425 }
426
427 /**************************************************************************
428  MUIM_Numeric_Increase
429 **************************************************************************/
430 IPTR Numeric__MUIM_Increase(struct IClass *cl, Object *obj,
431     struct MUIP_Numeric_Increase *msg)
432 {
433     struct MUI_NumericData *data = INST_DATA(cl, obj);
434     LONG newval = CLAMP(data->value + msg->amount, data->min, data->max);
435
436     if (newval != data->value)
437         set(obj, MUIA_Numeric_Value, newval);
438
439     return 1;
440 }
441
442
443 /**************************************************************************
444  MUIM_Numeric_ScaleToValue
445 **************************************************************************/
446 IPTR Numeric__MUIM_ScaleToValue(struct IClass *cl, Object *obj,
447     struct MUIP_Numeric_ScaleToValue *msg)
448 {
449     struct MUI_NumericData *data = INST_DATA(cl, obj);
450     LONG min, max;
451     LONG val;
452     LONG d;
453
454     min = (data->flags & NUMERIC_REVERSE) ? data->max : data->min;
455     max = (data->flags & NUMERIC_REVERSE) ? data->min : data->max;
456
457     val = CLAMP(msg->scale - msg->scalemin, msg->scalemin, msg->scalemax);
458     d = msg->scalemax - msg->scalemin;
459
460     // FIXME: watch out for overflow here.
461     val = val * (max - min);
462
463     if (d)
464         val /= d;
465
466     val += min;
467
468     return val;
469 }
470
471 /**************************************************************************
472  MUIM_Numeric_SetDefault
473 **************************************************************************/
474 IPTR Numeric__MUIM_SetDefault(struct IClass *cl, Object *obj, Msg msg)
475 {
476     struct MUI_NumericData *data = INST_DATA(cl, obj);
477
478     set(obj, MUIA_Numeric_Value, CLAMP(data->defvalue, data->min,
479             data->max));
480
481     return 0;
482 }
483
484 /**************************************************************************
485  MUIM_Numeric_Stringify
486 **************************************************************************/
487 IPTR Numeric__MUIM_Stringify(struct IClass *cl, Object *obj,
488     struct MUIP_Numeric_Stringify *msg)
489 {
490     struct MUI_NumericData *data = INST_DATA(cl, obj);
491
492     /* TODO: use RawDoFmt() and buffer overrun */
493     snprintf(data->buf, 49, data->format, (long)msg->value);
494     data->buf[49] = 0;
495
496     return (IPTR) data->buf;
497 }
498
499 /**************************************************************************
500  MUIM_Numeric_ValueToScale
501 **************************************************************************/
502 IPTR Numeric__MUIM_ValueToScale(struct IClass *cl, Object *obj,
503     struct MUIP_Numeric_ValueToScale *msg)
504 {
505     LONG val;
506     struct MUI_NumericData *data = INST_DATA(cl, obj);
507     LONG min, max;
508
509     min = (data->flags & NUMERIC_REVERSE) ? msg->scalemax : msg->scalemin;
510     max = (data->flags & NUMERIC_REVERSE) ? msg->scalemin : msg->scalemax;
511
512     if (data->max != data->min)
513     {
514         val =
515             min + ((data->value - data->min) * (max - min) + (data->max -
516                 data->min) / 2) / (data->max - data->min);
517     }
518     else
519     {
520         val = min;
521     }
522
523     val = CLAMP(val, msg->scalemin, msg->scalemax);
524
525     return val;
526 }
527
528 /**************************************************************************
529  MUIM_Numeric_ValueToScaleExt
530 **************************************************************************/
531 IPTR Numeric__MUIM_ValueToScaleExt(struct IClass *cl, Object *obj,
532     struct MUIP_Numeric_ValueToScaleExt *msg)
533 {
534     LONG scale;
535     LONG value;
536     struct MUI_NumericData *data = INST_DATA(cl, obj);
537     LONG min, max;
538
539     value = CLAMP(msg->value, data->min, data->max);
540     min = (data->flags & NUMERIC_REVERSE) ? msg->scalemax : msg->scalemin;
541     max = (data->flags & NUMERIC_REVERSE) ? msg->scalemin : msg->scalemax;
542
543     if (data->max != data->min)
544     {
545         scale =
546             min + ((value - data->min) * (max - min) + (data->max -
547                 data->min) / 2) / (data->max - data->min);
548     }
549     else
550     {
551         scale = min;
552     }
553
554     scale = CLAMP(scale, msg->scalemin, msg->scalemax);
555
556     return scale;
557 }
558
559 /**************************************************************************
560  MUIM_Export - to export an object's "contents" to a dataspace object.
561 **************************************************************************/
562 IPTR Numeric__MUIM_Export(struct IClass *cl, Object *obj,
563     struct MUIP_Export *msg)
564 {
565     struct MUI_NumericData *data = INST_DATA(cl, obj);
566     ULONG id;
567
568     if ((id = muiNotifyData(obj)->mnd_ObjectID))
569     {
570         LONG value = data->value;
571         DoMethod(msg->dataspace, MUIM_Dataspace_Add,
572             (IPTR) & value, sizeof(value), (IPTR) id);
573     }
574     return 0;
575 }
576
577 /**************************************************************************
578  MUIM_Import - to import an object's "contents" from a dataspace object.
579 **************************************************************************/
580 IPTR Numeric__MUIM_Import(struct IClass *cl, Object *obj,
581     struct MUIP_Import *msg)
582 {
583     ULONG id;
584     LONG *s;
585
586     if ((id = muiNotifyData(obj)->mnd_ObjectID))
587     {
588         if ((s = (LONG *) DoMethod(msg->dataspace, MUIM_Dataspace_Find,
589                     (IPTR) id)))
590         {
591             set(obj, MUIA_Numeric_Value, *s);
592         }
593     }
594
595     return 0;
596 }
597
598
599 BOOPSI_DISPATCHER(IPTR, Numeric_Dispatcher, cl, obj, msg)
600 {
601     switch (msg->MethodID)
602     {
603     case OM_NEW:
604         return Numeric__OM_NEW(cl, obj, (APTR) msg);
605     case OM_SET:
606         return Numeric__OM_SET(cl, obj, (APTR) msg);
607     case OM_GET:
608         return Numeric__OM_GET(cl, obj, (APTR) msg);
609
610     case MUIM_Setup:
611         return Numeric__MUIM_Setup(cl, obj, (APTR) msg);
612     case MUIM_Cleanup:
613         return Numeric__MUIM_Cleanup(cl, obj, (APTR) msg);
614     case MUIM_HandleEvent:
615         return Numeric__MUIM_HandleEvent(cl, obj, (APTR) msg);
616     case MUIM_Numeric_Decrease:
617         return Numeric__MUIM_Decrease(cl, obj, (APTR) msg);
618     case MUIM_Numeric_Increase:
619         return Numeric__MUIM_Increase(cl, obj, (APTR) msg);
620     case MUIM_Numeric_ScaleToValue:
621         return Numeric__MUIM_ScaleToValue(cl, obj, (APTR) msg);
622     case MUIM_Numeric_SetDefault:
623         return Numeric__MUIM_SetDefault(cl, obj, (APTR) msg);
624     case MUIM_Numeric_Stringify:
625         return Numeric__MUIM_Stringify(cl, obj, (APTR) msg);
626     case MUIM_Numeric_ValueToScale:
627         return Numeric__MUIM_ValueToScale(cl, obj, (APTR) msg);
628     case MUIM_Numeric_ValueToScaleExt:
629         return Numeric__MUIM_ValueToScaleExt(cl, obj, (APTR) msg);
630     case MUIM_Export:
631         return Numeric__MUIM_Export(cl, obj, (APTR) msg);
632     case MUIM_Import:
633         return Numeric__MUIM_Import(cl, obj, (APTR) msg);
634     }
635
636     return DoSuperMethodA(cl, obj, msg);
637 }
638 BOOPSI_DISPATCHER_END
639
640 /*
641  * Class descriptor.
642  */
643 const struct __MUIBuiltinClass _MUI_Numeric_desc =
644 {
645     MUIC_Numeric,
646     MUIC_Area,
647     sizeof(struct MUI_NumericData),
648     (void *) Numeric_Dispatcher
649 };