Added scroll wheel input, and improved keyboard input regarding
[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             break;
143         case MUIA_Numeric_Min:
144             data->min = tag->ti_Data;
145             break;
146         case MUIA_Numeric_Reverse:
147             _handle_bool_tag(data->flags, tag->ti_Data, NUMERIC_REVERSE);
148             break;
149         case MUIA_Numeric_RevLeftRight:
150             _handle_bool_tag(data->flags, tag->ti_Data,
151                 NUMERIC_REVLEFTRIGHT);
152             break;
153         case MUIA_Numeric_RevUpDown:
154             _handle_bool_tag(data->flags, tag->ti_Data, NUMERIC_REVUPDOWN);
155             break;
156         case MUIA_Numeric_Value:
157             tag->ti_Data = CLAMP((LONG) tag->ti_Data, data->min, data->max);
158
159             if (data->value == (LONG) tag->ti_Data)
160                 tag->ti_Tag = TAG_IGNORE;
161             else
162                 data->value = (LONG) tag->ti_Data;
163
164             break;
165         }
166     }
167
168     /* If the max, min or format values changed, then the minimum and maximum
169        sizes of the string output by MUIM_Numeric_Stringify may have changed,
170        so give the subclass a chance to recalculate them and relayout the group
171        accordingly. Basically, the subclass will have to react on changes to
172        these values as well (by setting a notification on them, or by
173        overriding OM_SET) and then recalculate the minimum and maximum sizes
174        for the object. */
175     if (data->format != oldfmt || data->min != oldmin
176         || data->max != oldmax)
177     {
178         values_changed = TRUE;
179         Object *parent = _parent(obj);
180         if (parent)
181         {
182             DoMethod(parent, MUIM_Group_InitChange);
183             DoMethod(parent, MUIM_Group_ExitChange);
184         }
185     }
186
187     ret = DoSuperMethodA(cl, obj, (Msg) msg);
188
189     if (data->value != oldval || values_changed)
190     {
191         MUI_Redraw(obj, MADF_DRAWUPDATE);
192     }
193
194     return ret;
195 }
196
197 /**************************************************************************
198  OM_GET
199 **************************************************************************/
200 IPTR Numeric__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
201 {
202     struct MUI_NumericData *data = INST_DATA(cl, obj);
203     IPTR *store = msg->opg_Storage;
204     ULONG tag = msg->opg_AttrID;
205
206     switch (tag)
207     {
208     case MUIA_Numeric_CheckAllSizes:
209         *store = ((data->flags & NUMERIC_CHECKALLSIZES) != 0);
210         return TRUE;
211
212     case MUIA_Numeric_Default:
213         *store = data->defvalue;
214         return TRUE;
215
216     case MUIA_Numeric_Format:
217         *store = (IPTR) data->format;
218         return TRUE;
219
220     case MUIA_Numeric_Max:
221         *store = data->max;
222         return TRUE;
223
224     case MUIA_Numeric_Min:
225         *store = data->min;
226         return TRUE;
227
228     case MUIA_Numeric_Reverse:
229         *store = ((data->flags & NUMERIC_REVERSE) != 0);
230         return TRUE;
231
232     case MUIA_Numeric_RevLeftRight:
233         *store = ((data->flags & NUMERIC_REVLEFTRIGHT) != 0);
234         return TRUE;
235
236     case MUIA_Numeric_RevUpDown:
237         *store = ((data->flags & NUMERIC_REVUPDOWN) != 0);
238         return TRUE;
239
240     case MUIA_Numeric_Value:
241         *store = data->value;
242         return TRUE;
243     }
244
245     return DoSuperMethodA(cl, obj, (Msg) msg);
246 }
247
248 /**************************************************************************
249  MUIM_Setup
250 **************************************************************************/
251 IPTR Numeric__MUIM_Setup(struct IClass *cl, Object *obj,
252     struct MUIP_Setup *msg)
253 {
254     struct MUI_NumericData *data = INST_DATA(cl, obj);
255
256     if (!DoSuperMethodA(cl, obj, (Msg) msg))
257         return FALSE;
258
259     data->ehn.ehn_Events = IDCMP_RAWKEY;
260     data->ehn.ehn_Priority = 0;
261     data->ehn.ehn_Flags = 0;
262     data->ehn.ehn_Object = obj;
263     data->ehn.ehn_Class = cl;
264     DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) (&data->ehn));
265
266     return TRUE;
267 }
268
269 /**************************************************************************
270  MUIM_Cleanup
271 **************************************************************************/
272 IPTR Numeric__MUIM_Cleanup(struct IClass *cl, Object *obj,
273     struct MUIP_Cleanup *msg)
274 {
275     struct MUI_NumericData *data = INST_DATA(cl, obj);
276
277     DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) (&data->ehn));
278     return DoSuperMethodA(cl, obj, (Msg) msg);
279 }
280
281 /**************************************************************************
282  MUIM_HandleEvent
283 **************************************************************************/
284 IPTR Numeric__MUIM_HandleEvent(struct IClass *cl, Object *obj,
285     struct MUIP_HandleEvent *msg)
286 {
287     struct MUI_NumericData *data = INST_DATA(cl, obj);
288     IPTR result = 0;
289     BOOL increase, change;
290
291     if (msg->muikey != MUIKEY_NONE)
292     {
293         LONG step;
294         BOOL use_absolute = FALSE, is_horizontal = FALSE;
295
296         result = MUI_EventHandlerRC_Eat;
297
298         if (data->max - data->min < 10)
299             step = 1;
300         else
301             step = 10;
302
303         switch (msg->muikey)
304         {
305         case MUIKEY_PRESS:
306             return MUI_EventHandlerRC_Eat;
307
308         case MUIKEY_TOGGLE:
309             DoMethod(obj, MUIM_Numeric_SetDefault);
310             return MUI_EventHandlerRC_Eat;
311
312         case MUIKEY_RELEASE:
313             return MUI_EventHandlerRC_Eat;
314
315         case MUIKEY_TOP:
316         case MUIKEY_LINESTART:
317             use_absolute = TRUE;
318             is_horizontal = TRUE;
319             step = -1;
320             break;
321
322         case MUIKEY_BOTTOM:
323         case MUIKEY_LINEEND:
324             use_absolute = TRUE;
325             is_horizontal = TRUE;
326             step = 1;
327             break;
328
329         case MUIKEY_LEFT:
330             is_horizontal = TRUE;
331             step = -1;
332             break;
333
334         case MUIKEY_RIGHT:
335             is_horizontal = TRUE;
336             step = 1;
337             break;
338
339         case MUIKEY_UP:
340             step = -1;
341             break;
342
343         case MUIKEY_DOWN:
344             step = 1;
345             break;
346
347         case MUIKEY_PAGEDOWN:
348         case MUIKEY_WORDRIGHT:
349             break;
350
351         case MUIKEY_PAGEUP:
352         case MUIKEY_WORDLEFT:
353                 step = -step;
354             break;
355
356         default:
357             return 0;
358         }
359
360         /* Send gadget in proper direction */
361         if (step != 0)
362         {
363             if (data->flags & NUMERIC_REVERSE)
364                 step = -step;
365
366             if ((is_horizontal && (data->flags & NUMERIC_REVLEFTRIGHT) != 0)
367                 || (!is_horizontal && (data->flags & NUMERIC_REVUPDOWN) != 0))
368                 step = -step;
369
370             if (use_absolute)
371             {
372                 if (step > 0)
373                     step = data->max;
374                 else
375                     step = data->min;
376                 step -= data->value;
377             }
378
379             DoMethod(obj, MUIM_Numeric_Increase, step);
380         }
381     }
382     else if (msg->imsg->Class == IDCMP_RAWKEY
383         && _isinobject(obj, msg->imsg->MouseX, msg->imsg->MouseY))
384     {
385         change = TRUE;
386         switch (msg->imsg->Code)
387         {
388         case RAWKEY_NM_WHEEL_UP:
389             increase = FALSE;
390             break;
391         case RAWKEY_NM_WHEEL_DOWN:
392             increase = TRUE;
393             break;
394         default:
395             change = FALSE;
396         }
397         if (change)
398         {
399             if (data->flags & NUMERIC_REVERSE)
400                 increase = !increase;
401             DoMethod(obj, increase ?
402                 MUIM_Numeric_Increase : MUIM_Numeric_Decrease, 1);
403             result = MUI_EventHandlerRC_Eat;
404         }
405     }
406
407     return result;
408 }
409
410
411 /**************************************************************************
412  MUIM_Numeric_Decrease
413 **************************************************************************/
414 IPTR Numeric__MUIM_Decrease(struct IClass *cl, Object *obj,
415     struct MUIP_Numeric_Decrease *msg)
416 {
417     struct MUI_NumericData *data = INST_DATA(cl, obj);
418     LONG newval = CLAMP(data->value - msg->amount, data->min, data->max);
419     if (newval != data->value)
420         set(obj, MUIA_Numeric_Value, newval);
421
422     return 1;
423 }
424
425 /**************************************************************************
426  MUIM_Numeric_Increase
427 **************************************************************************/
428 IPTR Numeric__MUIM_Increase(struct IClass *cl, Object *obj,
429     struct MUIP_Numeric_Increase *msg)
430 {
431     struct MUI_NumericData *data = INST_DATA(cl, obj);
432     LONG newval = CLAMP(data->value + msg->amount, data->min, data->max);
433
434     if (newval != data->value)
435         set(obj, MUIA_Numeric_Value, newval);
436
437     return 1;
438 }
439
440
441 /**************************************************************************
442  MUIM_Numeric_ScaleToValue
443 **************************************************************************/
444 IPTR Numeric__MUIM_ScaleToValue(struct IClass *cl, Object *obj,
445     struct MUIP_Numeric_ScaleToValue *msg)
446 {
447     struct MUI_NumericData *data = INST_DATA(cl, obj);
448     LONG min, max;
449     LONG val;
450     LONG d;
451
452     min = (data->flags & NUMERIC_REVERSE) ? data->max : data->min;
453     max = (data->flags & NUMERIC_REVERSE) ? data->min : data->max;
454
455     val = CLAMP(msg->scale - msg->scalemin, msg->scalemin, msg->scalemax);
456     d = msg->scalemax - msg->scalemin;
457
458     // FIXME: watch out for overflow here.
459     val = val * (max - min);
460
461     if (d)
462         val /= d;
463
464     val += min;
465
466     return val;
467 }
468
469 /**************************************************************************
470  MUIM_Numeric_SetDefault
471 **************************************************************************/
472 IPTR Numeric__MUIM_SetDefault(struct IClass *cl, Object *obj, Msg msg)
473 {
474     struct MUI_NumericData *data = INST_DATA(cl, obj);
475
476     set(obj, MUIA_Numeric_Value, CLAMP(data->defvalue, data->min,
477             data->max));
478
479     return 0;
480 }
481
482 /**************************************************************************
483  MUIM_Numeric_Stringify
484 **************************************************************************/
485 IPTR Numeric__MUIM_Stringify(struct IClass *cl, Object *obj,
486     struct MUIP_Numeric_Stringify *msg)
487 {
488     struct MUI_NumericData *data = INST_DATA(cl, obj);
489
490     /* TODO: use RawDoFmt() and buffer overrun */
491     snprintf(data->buf, 49, data->format, (long)msg->value);
492     data->buf[49] = 0;
493
494     return (IPTR) data->buf;
495 }
496
497 /**************************************************************************
498  MUIM_Numeric_ValueToScale
499 **************************************************************************/
500 IPTR Numeric__MUIM_ValueToScale(struct IClass *cl, Object *obj,
501     struct MUIP_Numeric_ValueToScale *msg)
502 {
503     LONG val;
504     struct MUI_NumericData *data = INST_DATA(cl, obj);
505     LONG min, max;
506
507     min = (data->flags & NUMERIC_REVERSE) ? msg->scalemax : msg->scalemin;
508     max = (data->flags & NUMERIC_REVERSE) ? msg->scalemin : msg->scalemax;
509
510     if (data->max != data->min)
511     {
512         val =
513             min + ((data->value - data->min) * (max - min) + (data->max -
514                 data->min) / 2) / (data->max - data->min);
515     }
516     else
517     {
518         val = min;
519     }
520
521     val = CLAMP(val, msg->scalemin, msg->scalemax);
522
523     return val;
524 }
525
526 /**************************************************************************
527  MUIM_Numeric_ValueToScaleExt
528 **************************************************************************/
529 IPTR Numeric__MUIM_ValueToScaleExt(struct IClass *cl, Object *obj,
530     struct MUIP_Numeric_ValueToScaleExt *msg)
531 {
532     LONG scale;
533     LONG value;
534     struct MUI_NumericData *data = INST_DATA(cl, obj);
535     LONG min, max;
536
537     value = CLAMP(msg->value, data->min, data->max);
538     min = (data->flags & NUMERIC_REVERSE) ? msg->scalemax : msg->scalemin;
539     max = (data->flags & NUMERIC_REVERSE) ? msg->scalemin : msg->scalemax;
540
541     if (data->max != data->min)
542     {
543         scale =
544             min + ((value - data->min) * (max - min) + (data->max -
545                 data->min) / 2) / (data->max - data->min);
546     }
547     else
548     {
549         scale = min;
550     }
551
552     scale = CLAMP(scale, msg->scalemin, msg->scalemax);
553
554     return scale;
555 }
556
557 /**************************************************************************
558  MUIM_Export - to export an object's "contents" to a dataspace object.
559 **************************************************************************/
560 IPTR Numeric__MUIM_Export(struct IClass *cl, Object *obj,
561     struct MUIP_Export *msg)
562 {
563     struct MUI_NumericData *data = INST_DATA(cl, obj);
564     ULONG id;
565
566     if ((id = muiNotifyData(obj)->mnd_ObjectID))
567     {
568         LONG value = data->value;
569         DoMethod(msg->dataspace, MUIM_Dataspace_Add,
570             (IPTR) & value, sizeof(value), (IPTR) id);
571     }
572     return 0;
573 }
574
575 /**************************************************************************
576  MUIM_Import - to import an object's "contents" from a dataspace object.
577 **************************************************************************/
578 IPTR Numeric__MUIM_Import(struct IClass *cl, Object *obj,
579     struct MUIP_Import *msg)
580 {
581     ULONG id;
582     LONG *s;
583
584     if ((id = muiNotifyData(obj)->mnd_ObjectID))
585     {
586         if ((s = (LONG *) DoMethod(msg->dataspace, MUIM_Dataspace_Find,
587                     (IPTR) id)))
588         {
589             set(obj, MUIA_Numeric_Value, *s);
590         }
591     }
592
593     return 0;
594 }
595
596
597 BOOPSI_DISPATCHER(IPTR, Numeric_Dispatcher, cl, obj, msg)
598 {
599     switch (msg->MethodID)
600     {
601     case OM_NEW:
602         return Numeric__OM_NEW(cl, obj, (APTR) msg);
603     case OM_SET:
604         return Numeric__OM_SET(cl, obj, (APTR) msg);
605     case OM_GET:
606         return Numeric__OM_GET(cl, obj, (APTR) msg);
607
608     case MUIM_Setup:
609         return Numeric__MUIM_Setup(cl, obj, (APTR) msg);
610     case MUIM_Cleanup:
611         return Numeric__MUIM_Cleanup(cl, obj, (APTR) msg);
612     case MUIM_HandleEvent:
613         return Numeric__MUIM_HandleEvent(cl, obj, (APTR) msg);
614     case MUIM_Numeric_Decrease:
615         return Numeric__MUIM_Decrease(cl, obj, (APTR) msg);
616     case MUIM_Numeric_Increase:
617         return Numeric__MUIM_Increase(cl, obj, (APTR) msg);
618     case MUIM_Numeric_ScaleToValue:
619         return Numeric__MUIM_ScaleToValue(cl, obj, (APTR) msg);
620     case MUIM_Numeric_SetDefault:
621         return Numeric__MUIM_SetDefault(cl, obj, (APTR) msg);
622     case MUIM_Numeric_Stringify:
623         return Numeric__MUIM_Stringify(cl, obj, (APTR) msg);
624     case MUIM_Numeric_ValueToScale:
625         return Numeric__MUIM_ValueToScale(cl, obj, (APTR) msg);
626     case MUIM_Numeric_ValueToScaleExt:
627         return Numeric__MUIM_ValueToScaleExt(cl, obj, (APTR) msg);
628     case MUIM_Export:
629         return Numeric__MUIM_Export(cl, obj, (APTR) msg);
630     case MUIM_Import:
631         return Numeric__MUIM_Import(cl, obj, (APTR) msg);
632     }
633
634     return DoSuperMethodA(cl, obj, msg);
635 }
636 BOOPSI_DISPATCHER_END
637
638 /*
639  * Class descriptor.
640  */
641 const struct __MUIBuiltinClass _MUI_Numeric_desc =
642 {
643     MUIC_Numeric,
644     MUIC_Area,
645     sizeof(struct MUI_NumericData),
646     (void *) Numeric_Dispatcher
647 };