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