elementary/segment_control, panes, photocam, photo, win, toolbar, thumb, slideshow...
[framework/uifw/elementary.git] / src / lib / elm_spinner.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3 #include <ctype.h>
4
5 /**
6  * @defgroup Spinner
7  *
8  * A spinner is a widget which allows the user to increase or decrease
9  * numeric values. By default the spinner will not wrap and has a label
10  * of "%.0f" (just showing the integer value of the double).
11  *
12  * A spinner has a label that is formatted with floating
13  * point values and thus accepts a printf-style format string, like
14  * “%1.2f units”.
15  *
16  * Signals that you can add callbacks for are:
17  *
18  * "changed" - Whenever the spinner value is changed by the user.
19  * "delay,changed" - A short time after the value is changed by the user.
20  *                   This will be called only when the user stops dragging for a very short
21  *                   period or when they release their finger/mouse, so it avoids possibly
22  *                   expensive reactions to the value change.
23  */
24 typedef struct _Widget_Data Widget_Data;
25 typedef struct _Elm_Spinner_Special_Value Elm_Spinner_Special_Value;
26
27 struct _Widget_Data
28 {
29    Evas_Object *spinner, *ent;
30    const char *label;
31    double val, val_min, val_max, orig_val, step;
32    double drag_start_pos, spin_speed, interval, first_interval;
33    Ecore_Timer *delay, *spin;
34    Eina_List *special_values;
35    Eina_Bool wrap : 1;
36    Eina_Bool entry_visible : 1;
37    Eina_Bool dragging : 1;
38    Eina_Bool editable : 1;
39 };
40
41 struct _Elm_Spinner_Special_Value
42 {
43    double value;
44    const char *label;
45 };
46
47 static const char *widtype = NULL;
48 static void _del_hook(Evas_Object *obj);
49 static void _disable_hook(Evas_Object *obj);
50 static void _write_label(Evas_Object *obj);
51 static void _sizing_eval(Evas_Object *obj);
52 //static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
53 static Eina_Bool _value_set(Evas_Object *obj, double delta);
54 static void _on_focus_hook(void *data, Evas_Object *obj);
55 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
56                              Evas_Callback_Type type, void *event_info);
57
58 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
59
60 static const char SIG_CHANGED[] = "changed";
61 static const char SIG_DELAY_CHANGED[] = "delay,changed";
62
63 static const Evas_Smart_Cb_Description _signals[] = {
64    {SIG_CHANGED, ""},
65    {SIG_DELAY_CHANGED, ""},
66    {NULL, NULL}
67 };
68
69 static void
70 _del_hook(Evas_Object *obj)
71 {
72    Elm_Spinner_Special_Value *sv;
73    Widget_Data *wd = elm_widget_data_get(obj);
74    if (!wd) return;
75    if (wd->label) eina_stringshare_del(wd->label);
76    if (wd->delay) ecore_timer_del(wd->delay);
77    if (wd->spin) ecore_timer_del(wd->spin);
78    if (wd->special_values)
79      {
80         EINA_LIST_FREE(wd->special_values, sv)
81           {
82              eina_stringshare_del(sv->label);
83              free(sv);
84           }
85      }
86    free(wd);
87 }
88
89 static void
90 _disable_hook(Evas_Object *obj)
91 {
92    Widget_Data *wd = elm_widget_data_get(obj);
93    if (!wd) return;
94    if (elm_widget_disabled_get(obj))
95       edje_object_signal_emit(wd->spinner, "elm,state,disabled", "elm");
96    else
97       edje_object_signal_emit(wd->spinner, "elm,state,enabled", "elm");
98 }
99
100 static void
101 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
102 {
103    Widget_Data *wd = elm_widget_data_get(obj);
104    if (!wd) return;
105    edje_object_signal_emit(wd->spinner, emission, source);
106 }
107
108 static void
109 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
110 {
111    Widget_Data *wd = elm_widget_data_get(obj);
112    if (!wd) return;
113    edje_object_signal_callback_add(wd->spinner, emission,
114                                    source, func_cb, data);
115 }
116
117 static void
118 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
119 {
120    Widget_Data *wd = elm_widget_data_get(obj);
121    edje_object_signal_callback_del_full(wd->spinner, emission, source,
122                                         func_cb, data);
123 }
124
125 static void
126 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
127 {
128    Widget_Data *wd = elm_widget_data_get(obj);
129    if (!wd) return;
130    edje_object_mirrored_set(wd->spinner, rtl);
131 }
132
133 static void
134 _theme_hook(Evas_Object *obj)
135 {
136    Widget_Data *wd = elm_widget_data_get(obj);
137    if (!wd) return;
138    _elm_widget_mirrored_reload(obj);
139    _mirrored_set(obj, elm_widget_mirrored_get(obj));
140    _elm_theme_object_set(obj, wd->spinner, "spinner", "base", elm_widget_style_get(obj));
141    edje_object_part_swallow(wd->spinner, "elm.swallow.entry", wd->ent);
142    _write_label(obj);
143    if (elm_widget_focus_get(obj))
144       edje_object_signal_emit(wd->spinner, "elm,action,focus", "elm");
145    else
146       edje_object_signal_emit(wd->spinner, "elm,action,unfocus", "elm");
147    if (elm_widget_disabled_get(obj))
148       edje_object_signal_emit(wd->spinner, "elm,state,disabled", "elm");
149    edje_object_message_signal_process(wd->spinner);
150    edje_object_scale_set(wd->spinner, elm_widget_scale_get(obj) * _elm_config->scale);
151    _sizing_eval(obj);
152 }
153
154 static void
155 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
156 {
157    Widget_Data *wd = elm_widget_data_get(obj);
158    if (!wd) return;
159    if (elm_widget_focus_get(obj))
160      {
161         edje_object_signal_emit(wd->spinner, "elm,action,focus", "elm");
162         evas_object_focus_set(wd->spinner, EINA_TRUE);
163      }
164    else
165      {
166         edje_object_signal_emit(wd->spinner, "elm,action,unfocus", "elm");
167         evas_object_focus_set(wd->spinner, EINA_FALSE);
168      }
169 }
170
171 static Eina_Bool
172 _delay_change(void *data)
173 {
174    Widget_Data *wd = elm_widget_data_get(data);
175    if (!wd) return ECORE_CALLBACK_CANCEL;
176    wd->delay = NULL;
177    evas_object_smart_callback_call(data, SIG_DELAY_CHANGED, NULL);
178    return ECORE_CALLBACK_CANCEL;
179 }
180
181 static void
182 _entry_show(Widget_Data *wd)
183 {
184    char buf[32], fmt[32] = "%0.f";
185
186    /* try to construct just the format from given label
187     * completely ignoring pre/post words
188     */
189    if (wd->label)
190      {
191         const char *start = strchr(wd->label, '%');
192         while (start)
193           {
194              /* handle %% */
195              if (start[1] != '%')
196                break;
197              else
198                start = strchr(start + 2, '%');
199           }
200
201         if (start)
202           {
203              const char *itr, *end = NULL;
204              for (itr = start + 1; *itr != '\0'; itr++)
205                {
206                   /* allowing '%d' is quite dangerous, remove it? */
207                   if ((*itr == 'd') || (*itr == 'f'))
208                     {
209                        end = itr + 1;
210                        break;
211                     }
212                }
213
214              if ((end) && ((size_t)(end - start + 1) < sizeof(fmt)))
215                {
216                   memcpy(fmt, start, end - start);
217                   fmt[end - start] = '\0';
218                }
219           }
220      }
221    snprintf(buf, sizeof(buf), fmt, wd->val);
222    elm_entry_entry_set(wd->ent, buf);
223 }
224
225 static void
226 _write_label(Evas_Object *obj)
227 {
228    Eina_List *l;
229    Elm_Spinner_Special_Value *sv;
230    Widget_Data *wd = elm_widget_data_get(obj);
231    char buf[1024];
232    if (!wd) return;
233    EINA_LIST_FOREACH(wd->special_values, l, sv)
234      {
235         if (sv->value == wd->val)
236           {
237              snprintf(buf, sizeof(buf), "%s", sv->label);
238              goto apply;
239           }
240      }
241    if (wd->label)
242      snprintf(buf, sizeof(buf), wd->label, wd->val);
243    else
244      snprintf(buf, sizeof(buf), "%.0f", wd->val);
245
246 apply:
247    edje_object_part_text_set(wd->spinner, "elm.text", buf);
248    if (wd->entry_visible) _entry_show(wd);
249 }
250
251 static Eina_Bool
252 _value_set(Evas_Object *obj, double delta)
253 {
254    Widget_Data *wd = elm_widget_data_get(obj);
255    double new_val;
256    if (!wd) return EINA_FALSE;
257    new_val = wd->val + delta;
258    if (wd->wrap)
259      {
260         while (new_val < wd->val_min)
261           new_val = wd->val_max + new_val + 1 - wd->val_min;
262         while (new_val > wd->val_max)
263           new_val = wd->val_min + new_val - wd->val_max - 1;
264      }
265    else
266      {
267         if (new_val < wd->val_min)
268           new_val = wd->val_min;
269         else if (new_val > wd->val_max)
270           new_val = wd->val_max;
271      }
272
273    if (new_val == wd->val) return EINA_FALSE;
274    wd->val = new_val;
275
276    evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
277    if (wd->delay) ecore_timer_del(wd->delay);
278    wd->delay = ecore_timer_add(0.2, _delay_change, obj);
279
280    return EINA_TRUE;
281 }
282
283 static void
284 _sizing_eval(Evas_Object *obj)
285 {
286    Widget_Data *wd = elm_widget_data_get(obj);
287    Evas_Coord minw = -1, minh = -1;
288    if (!wd) return;
289    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
290    edje_object_size_min_restricted_calc(wd->spinner, &minw, &minh, minw, minh);
291    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
292    evas_object_size_hint_min_set(obj, minw, minh);
293    evas_object_size_hint_max_set(obj, -1, -1);
294 }
295
296 /*
297    static void
298    _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info)
299    {
300    _sizing_eval(data);
301    }
302  */
303
304 static void
305 _val_set(Evas_Object *obj)
306 {
307    Widget_Data *wd = elm_widget_data_get(obj);
308    double pos = 0.0;
309    if (!wd) return;
310    if (wd->val_max > wd->val_min)
311      pos = ((wd->val - wd->val_min) / (wd->val_max - wd->val_min));
312    if (pos < 0.0) pos = 0.0;
313    else if (pos > 1.0) pos = 1.0;
314    edje_object_part_drag_value_set(wd->spinner, "elm.dragable.slider",
315                                    pos, pos);
316 }
317
318 static void
319 _drag(void *data, Evas_Object *_obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
320 {
321    Evas_Object *obj = data;
322    Widget_Data *wd = elm_widget_data_get(obj);
323    double pos = 0.0, offset, delta;
324    if (!wd) return;
325    if (wd->entry_visible) return;
326    edje_object_part_drag_value_get(wd->spinner, "elm.dragable.slider",
327                                    &pos, NULL);
328    offset = wd->step;
329    delta = (pos - wd->drag_start_pos) * offset;
330    /* If we are on rtl mode, change the delta to be negative on such changes */
331    if (elm_widget_mirrored_get(obj))
332      delta *= -1;
333    if (_value_set(data, delta)) _write_label(data);
334    wd->drag_start_pos = pos;
335    wd->dragging = 1;
336 }
337
338 static void
339 _drag_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
340 {
341    Widget_Data *wd = elm_widget_data_get(data);
342    double pos;
343    if (!wd) return;
344    edje_object_part_drag_value_get(wd->spinner, "elm.dragable.slider",
345                                    &pos, NULL);
346    wd->drag_start_pos = pos;
347 }
348
349 static void
350 _drag_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
351 {
352    Widget_Data *wd = elm_widget_data_get(data);
353    if (!wd) return;
354    wd->drag_start_pos = 0;
355    edje_object_part_drag_value_set(wd->spinner, "elm.dragable.slider", 0.0, 0.0);
356 }
357
358 static void
359 _hide_entry(Evas_Object *obj)
360 {
361    Widget_Data *wd = elm_widget_data_get(obj);
362    if (!wd) return;
363    edje_object_signal_emit(wd->spinner, "elm,state,inactive", "elm");
364    wd->entry_visible = 0;
365 }
366
367 static void
368 _reset_value(Evas_Object *obj)
369 {
370    Widget_Data *wd = elm_widget_data_get(obj);
371    if (!wd) return;
372    _hide_entry(obj);
373    elm_spinner_value_set(obj, wd->orig_val);
374 }
375
376 static void
377 _apply_entry_value(Evas_Object *obj)
378 {
379    Widget_Data *wd = elm_widget_data_get(obj);
380    const char *str;
381    char *end;
382    double val;
383
384    if (!wd) return;
385    _hide_entry(obj);
386    str = elm_entry_entry_get(wd->ent);
387    if (!str) return;
388    val = strtod(str, &end);
389    if ((*end != '\0') && (!isspace(*end))) return;
390    elm_spinner_value_set(obj, val);
391 }
392
393 static void
394 _toggle_entry(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
395 {
396    Widget_Data *wd = elm_widget_data_get(data);
397    if (!wd) return;
398    if (wd->dragging)
399      {
400         wd->dragging = 0;
401         return;
402      }
403    if (elm_widget_disabled_get(data)) return;
404    if (!wd->editable) return;
405    if (wd->entry_visible) _apply_entry_value(data);
406    else
407      {
408         wd->orig_val = wd->val;
409         edje_object_signal_emit(wd->spinner, "elm,state,active", "elm");
410         _entry_show(wd);
411         elm_entry_select_all(wd->ent);
412         elm_widget_focus_set(wd->ent, 1);
413         wd->entry_visible = 1;
414      }
415 }
416
417 static Eina_Bool
418 _spin_value(void *data)
419 {
420    Widget_Data *wd = elm_widget_data_get(data);
421    if (!wd) return ECORE_CALLBACK_CANCEL;
422    if (_value_set(data, wd->spin_speed)) _write_label(data);
423    wd->interval = wd->interval / 1.05;
424    ecore_timer_interval_set(wd->spin, wd->interval);
425    return ECORE_CALLBACK_RENEW;
426 }
427
428 static void
429 _val_inc_start(Evas_Object *obj)
430 {
431    Widget_Data *wd = elm_widget_data_get(obj);
432    if (!wd) return;
433    wd->interval = wd->first_interval;
434    wd->spin_speed = wd->step;
435    if (wd->spin) ecore_timer_del(wd->spin);
436    wd->spin = ecore_timer_add(wd->interval, _spin_value, obj);
437    _spin_value(obj);
438 }
439
440 static void
441 _val_inc_stop(Evas_Object *obj)
442 {
443    Widget_Data *wd = elm_widget_data_get(obj);
444    if (!wd) return;
445    wd->interval = wd->first_interval;
446    wd->spin_speed = 0;
447    if (wd->spin) ecore_timer_del(wd->spin);
448    wd->spin = NULL;
449 }
450
451 static void
452 _val_dec_start(Evas_Object *obj)
453 {
454    Widget_Data *wd = elm_widget_data_get(obj);
455    if (!wd) return;
456    wd->interval = wd->first_interval;
457    wd->spin_speed = -wd->step;
458    if (wd->spin) ecore_timer_del(wd->spin);
459    wd->spin = ecore_timer_add(wd->interval, _spin_value, obj);
460    _spin_value(obj);
461 }
462
463 static void
464 _val_dec_stop(Evas_Object *obj)
465 {
466    Widget_Data *wd = elm_widget_data_get(obj);
467    if (!wd) return;
468    wd->interval = wd->first_interval;
469    wd->spin_speed = 0;
470    if (wd->spin) ecore_timer_del(wd->spin);
471    wd->spin = NULL;
472 }
473
474 static void
475 _button_inc_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
476 {
477    Widget_Data *wd = elm_widget_data_get(data);
478    if (!wd) return;
479    if (wd->entry_visible)
480      {
481         _reset_value(data);
482         return;
483      }
484    _val_inc_start(data);
485 }
486
487 static void
488 _button_inc_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
489 {
490    Widget_Data *wd = elm_widget_data_get(data);
491    if (!wd) return;
492    _val_inc_stop(data);
493 }
494
495 static void
496 _button_dec_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
497 {
498    Widget_Data *wd = elm_widget_data_get(data);
499    if (!wd) return;
500    if (wd->entry_visible)
501      {
502         _reset_value(data);
503         return;
504      }
505    _val_dec_start(data);
506 }
507
508 static void
509 _button_dec_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
510 {
511    Widget_Data *wd = elm_widget_data_get(data);
512    if (!wd) return;
513    _val_dec_stop(data);
514 }
515
516 static void
517 _entry_activated(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
518 {
519    Widget_Data *wd = elm_widget_data_get(data);
520    if (!wd) return;
521    _apply_entry_value(data);
522    evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
523    if (wd->delay) ecore_timer_del(wd->delay);
524    wd->delay = ecore_timer_add(0.2, _delay_change, data);
525 }
526
527 static Eina_Bool
528 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
529 {
530    Widget_Data *wd = elm_widget_data_get(obj);
531    if (!wd) return EINA_FALSE;
532    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
533    if (type == EVAS_CALLBACK_KEY_DOWN)
534      {
535         Evas_Event_Key_Down *ev = event_info;
536         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
537         else if (!strcmp(ev->keyname, "Left") || !strcmp(ev->keyname, "KP_Left")
538                  || !strcmp(ev->keyname, "Down") || !strcmp(ev->keyname, "KP_Down"))
539           {
540              _val_dec_start(obj);
541              edje_object_signal_emit(wd->spinner, "elm,left,anim,activate", "elm");
542              ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
543              return EINA_TRUE;
544           }
545         else if (!strcmp(ev->keyname, "Right") || !strcmp(ev->keyname, "KP_Right")
546                  || !strcmp(ev->keyname, "Up") || !strcmp(ev->keyname, "KP_Up"))
547           {
548              _val_inc_start(obj);
549              edje_object_signal_emit(wd->spinner, "elm,right,anim,activate", "elm");
550              ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
551              return EINA_TRUE;
552           }
553      }
554    else if (type == EVAS_CALLBACK_KEY_UP)
555      {
556         Evas_Event_Key_Down *ev = event_info;
557         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
558         if (!strcmp(ev->keyname, "Right") || !strcmp(ev->keyname, "KP_Right")
559             || !strcmp(ev->keyname, "Up") || !strcmp(ev->keyname, "KP_Up"))
560           _val_inc_stop(obj);
561         else if (!strcmp(ev->keyname, "Left") || !strcmp(ev->keyname, "KP_Left")
562                  || !strcmp(ev->keyname, "Down") || !strcmp(ev->keyname, "KP_Down"))
563           _val_dec_stop(obj);
564         else  return EINA_FALSE;
565         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
566         return EINA_TRUE;
567      }
568    return EINA_FALSE;
569 }
570
571 /**
572  * Add a new spinner to the parent
573  *
574  * @param parent The parent object
575  * @return The new object or NULL if it cannot be created
576  *
577  * @ingroup Spinner
578  */
579 EAPI Evas_Object *
580 elm_spinner_add(Evas_Object *parent)
581 {
582    Evas_Object *obj;
583    Evas *e;
584    Widget_Data *wd;
585
586    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
587
588    ELM_SET_WIDTYPE(widtype, "spinner");
589    elm_widget_type_set(obj, "spinner");
590    elm_widget_sub_object_add(parent, obj);
591    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
592    elm_widget_data_set(obj, wd);
593    elm_widget_del_hook_set(obj, _del_hook);
594    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
595    elm_widget_theme_hook_set(obj, _theme_hook);
596    elm_widget_disable_hook_set(obj, _disable_hook);
597    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
598    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
599    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
600    elm_widget_can_focus_set(obj, EINA_TRUE);
601    elm_widget_event_hook_set(obj, _event_hook);
602
603    wd->val = 0.0;
604    wd->val_min = 0.0;
605    wd->val_max = 100.0;
606    wd->wrap = 0;
607    wd->step = 1.0;
608    wd->first_interval = 0.85;
609    wd->entry_visible = 0;
610    wd->editable = EINA_TRUE;
611
612    wd->spinner = edje_object_add(e);
613    _elm_theme_object_set(obj, wd->spinner, "spinner", "base", "default");
614    elm_widget_resize_object_set(obj, wd->spinner);
615    edje_object_signal_callback_add(wd->spinner, "drag", "*", _drag, obj);
616    edje_object_signal_callback_add(wd->spinner, "drag,start", "*",
617                                    _drag_start, obj);
618    edje_object_signal_callback_add(wd->spinner, "drag,stop", "*",
619                                    _drag_stop, obj);
620    edje_object_signal_callback_add(wd->spinner, "drag,step", "*",
621                                    _drag_stop, obj);
622    edje_object_signal_callback_add(wd->spinner, "drag,page", "*",
623                                    _drag_stop, obj);
624
625    edje_object_signal_callback_add(wd->spinner, "elm,action,increment,start",
626                                    "*", _button_inc_start, obj);
627    edje_object_signal_callback_add(wd->spinner, "elm,action,increment,stop",
628                                    "*", _button_inc_stop, obj);
629    edje_object_signal_callback_add(wd->spinner, "elm,action,decrement,start",
630                                    "*", _button_dec_start, obj);
631    edje_object_signal_callback_add(wd->spinner, "elm,action,decrement,stop",
632                                    "*", _button_dec_stop, obj);
633    edje_object_part_drag_value_set(wd->spinner, "elm.dragable.slider",
634                                    0.0, 0.0);
635
636    wd->ent = elm_entry_add(obj);
637    elm_entry_single_line_set(wd->ent, 1);
638    evas_object_smart_callback_add(wd->ent, "activated", _entry_activated, obj);
639    edje_object_part_swallow(wd->spinner, "elm.swallow.entry", wd->ent);
640    edje_object_signal_callback_add(wd->spinner, "elm,action,entry,toggle",
641                                    "*", _toggle_entry, obj);
642
643    evas_object_smart_callbacks_descriptions_set(obj, _signals);
644
645    _mirrored_set(obj, elm_widget_mirrored_get(obj));
646    _write_label(obj);
647    _sizing_eval(obj);
648    return obj;
649 }
650
651 /**
652  * Set the format string of the label area
653  *
654  * If NULL, this sets the format to "%.0f". If not it sets the format
655  * string for the label text. The label text is provided a floating point
656  * value, so the label text can display up to 1 floating point value. Note that
657  * this is optional. Use a format string such as "%1.2f meters" for example.
658  *
659  * @param obj The spinner object
660  * @param fmt The format string for the label display
661  *
662  * @ingroup Spinner
663  */
664 EAPI void
665 elm_spinner_label_format_set(Evas_Object *obj, const char *fmt)
666 {
667    ELM_CHECK_WIDTYPE(obj, widtype);
668    Widget_Data *wd = elm_widget_data_get(obj);
669    if (!wd) return;
670    eina_stringshare_replace(&wd->label, fmt);
671    _write_label(obj);
672    _sizing_eval(obj);
673 }
674
675 /**
676  * Get the label format of the spinner
677  *
678  * @param obj The spinner object
679  * @return The text label format string in UTF-8
680  *
681  * @ingroup Spinner
682  */
683 EAPI const char *
684 elm_spinner_label_format_get(const Evas_Object *obj)
685 {
686    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
687    Widget_Data *wd = elm_widget_data_get(obj);
688    if (!wd) return NULL;
689    return wd->label;
690 }
691
692 /**
693  * Set the minimum and maximum values for the spinner
694  *
695  * Maximum must be greater than minimum.
696  *
697  * @param obj The spinner object
698  * @param min The minimum value
699  * @param max The maximum value
700  *
701  * @ingroup Spinner
702  */
703 EAPI void
704 elm_spinner_min_max_set(Evas_Object *obj, double min, double max)
705 {
706    ELM_CHECK_WIDTYPE(obj, widtype);
707    Widget_Data *wd = elm_widget_data_get(obj);
708    if (!wd) return;
709    if ((wd->val_min == min) && (wd->val_max == max)) return;
710    wd->val_min = min;
711    wd->val_max = max;
712    if (wd->val < wd->val_min) wd->val = wd->val_min;
713    if (wd->val > wd->val_max) wd->val = wd->val_max;
714    _val_set(obj);
715    _write_label(obj);
716 }
717
718 /**
719  * Get the minimum and maximum values of the spinner
720  *
721  * @param obj The spinner object
722  * @param min The minimum value
723  * @param max The maximum value
724  *
725  * @ingroup Spinner
726  */
727 EAPI void
728 elm_spinner_min_max_get(const Evas_Object *obj, double *min, double *max)
729 {
730    if (min) *min = 0.0;
731    if (max) *max = 0.0;
732    ELM_CHECK_WIDTYPE(obj, widtype);
733    Widget_Data *wd = elm_widget_data_get(obj);
734    if (!wd) return;
735    if (min) *min = wd->val_min;
736    if (max) *max = wd->val_max;
737 }
738
739 /**
740  * Set the step for the spinner
741  *
742  * @param obj The spinner object
743  * @param step The step value
744  *
745  * @ingroup Spinner
746  */
747 EAPI void
748 elm_spinner_step_set(Evas_Object *obj, double step)
749 {
750    ELM_CHECK_WIDTYPE(obj, widtype);
751    Widget_Data *wd = elm_widget_data_get(obj);
752    if (!wd) return;
753    wd->step = step;
754 }
755
756 /**
757  * Get the step of the spinner
758  *
759  * @param obj The spinner object
760  * @return The step value
761  *
762  * @ingroup Spinner
763  */
764 EAPI double
765 elm_spinner_step_get(const Evas_Object *obj)
766 {
767    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
768    Widget_Data *wd = elm_widget_data_get(obj);
769    if (!wd) return 0.0;
770    return wd->step;
771 }
772 /**
773  * Set the value the spinner indicates
774  *
775  * @param obj The spinner object
776  * @param val The value (must be between min and max for the spinner)
777  *
778  * @ingroup Spinner
779  */
780 EAPI void
781 elm_spinner_value_set(Evas_Object *obj, double val)
782 {
783    ELM_CHECK_WIDTYPE(obj, widtype);
784    Widget_Data *wd = elm_widget_data_get(obj);
785    if (!wd) return;
786    if (wd->val == val) return;
787    wd->val = val;
788    if (wd->val < wd->val_min) wd->val = wd->val_min;
789    if (wd->val > wd->val_max) wd->val = wd->val_max;
790    _val_set(obj);
791    _write_label(obj);
792 }
793
794 /**
795  * Get the value the spinner has
796  *
797  * @param obj The spinner object
798  * @return The value of the spinner
799  *
800  * @ingroup Spinner
801  */
802 EAPI double
803 elm_spinner_value_get(const Evas_Object *obj)
804 {
805    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
806    Widget_Data *wd = elm_widget_data_get(obj);
807    if (!wd) return 0.0;
808    return wd->val;
809 }
810
811 /**
812  * Sets whether the spinner should wrap when it reaches its
813  * minimum/maximum value
814  *
815  * @param obj The spinner object
816  * @param wrap True if it should wrap, false otherwise
817  *
818  * @ingroup Spinner
819  */
820 EAPI void
821 elm_spinner_wrap_set(Evas_Object *obj, Eina_Bool wrap)
822 {
823    ELM_CHECK_WIDTYPE(obj, widtype);
824    Widget_Data *wd = elm_widget_data_get(obj);
825    if (!wd) return;
826    wd->wrap = wrap;
827 }
828
829 /**
830  * Gets whether the spinner should wrap when it reaches its
831  * minimum/maximum value
832  *
833  * @param obj The spinner object
834  * @return Bool value of wrap option
835  * (0 = disabled, 1 = enabled)
836  *
837  * @ingroup Spinner
838  */
839 EAPI Eina_Bool
840 elm_spinner_wrap_get(const Evas_Object *obj)
841 {
842    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
843    Widget_Data *wd = elm_widget_data_get(obj);
844    if (!wd) return EINA_FALSE;
845    return wd->wrap;
846 }
847
848 /**
849  * Set a special value to display in the place of the numerical one.
850  *
851  * @param obj The spinner object
852  * @param value The value to be replaced
853  * @param label The label to be used
854  *
855  * @ingroup Spinner
856  */
857 EAPI void
858 elm_spinner_special_value_add(Evas_Object *obj, double value, const char *label)
859 {
860    Elm_Spinner_Special_Value *sv;
861    ELM_CHECK_WIDTYPE(obj, widtype);
862    Widget_Data *wd = elm_widget_data_get(obj);
863    if (!wd) return;
864
865    sv = calloc(1, sizeof(*sv));
866    if (!sv) return;
867    sv->value = value;
868    sv->label = eina_stringshare_add(label);
869
870    wd->special_values = eina_list_append(wd->special_values, sv);
871    _write_label(obj);
872 }
873
874 /**
875  * Set whether the spinner can be directly edited by the user or not.
876  * Default is editable.
877  *
878  * @param obj The spinner object
879  * @param editable Bool value of the edit option
880  * (EINA_FALSE = not editable, EINA_TRUE = editable)
881  */
882 EAPI void
883 elm_spinner_editable_set(Evas_Object *obj, Eina_Bool editable)
884 {
885    ELM_CHECK_WIDTYPE(obj, widtype);
886    Widget_Data *wd = elm_widget_data_get(obj);
887    if (!wd) return;
888    wd->editable = editable;
889 }
890
891 /**
892  * Gets whether the spinner is editable.
893  *
894  * @param obj The spinner object
895  * @return Bool value of edit option
896  * (EINA_FALSE = not editable, EINA_TRUE = editable)
897  */
898 EAPI Eina_Bool
899 elm_spinner_editable_get(const Evas_Object *obj)
900 {
901    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
902    Widget_Data *wd = elm_widget_data_get(obj);
903    if (!wd) return EINA_FALSE;
904    return wd->editable;
905 }
906
907 /**
908  * Set the interval for the spinner
909  *
910  * @param obj The spinner object
911  * @param interval The interval value in seconds
912  *
913  * The interval value is decreased while the user increments or decrements
914  * the spinner value. The next interval value is the previous interval / 1.05,
915  * so it speed up a bit. Default value is 0.85 seconds.
916  *
917  * @ingroup Spinner
918  */
919 EAPI void
920 elm_spinner_interval_set(Evas_Object *obj, double interval)
921 {
922    ELM_CHECK_WIDTYPE(obj, widtype);
923    Widget_Data *wd = elm_widget_data_get(obj);
924    if (!wd) return;
925    wd->first_interval = interval;
926 }
927
928 /**
929  * Get the interval of the spinner
930  *
931  * @param obj The spinner object
932  * @return The value of the first interval in seconds
933  *
934  * The interval value is decreased while the user increments or decrements
935  * the spinner value. The next interval value is the previous interval / 1.05,
936  * so it speed up a bit. Default value is 0.85 seconds.
937  *
938  * @ingroup Spinner
939  */
940 EAPI double
941 elm_spinner_interval_get(const Evas_Object *obj)
942 {
943    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
944    Widget_Data *wd = elm_widget_data_get(obj);
945    if (!wd) return 0.0;
946    return wd->first_interval;
947 }