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