fb6a310647971ce291124fb44cc17ab149b9e6b8
[framework/uifw/elementary.git] / src / lib / elm_spinner.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3 #include <ctype.h>
4
5 typedef struct _Widget_Data Widget_Data;
6 typedef struct _Elm_Spinner_Special_Value Elm_Spinner_Special_Value;
7
8 struct _Widget_Data
9 {
10    Evas_Object *spinner, *ent;
11    const char *label;
12    double val, val_min, val_max, orig_val, step;
13    double drag_start_pos, spin_speed, interval, first_interval;
14    Ecore_Timer *delay, *spin;
15    Eina_List *special_values;
16    Eina_Bool wrap : 1;
17    Eina_Bool entry_visible : 1;
18    Eina_Bool dragging : 1;
19    Eina_Bool editable : 1;
20 };
21
22 struct _Elm_Spinner_Special_Value
23 {
24    double value;
25    const char *label;
26 };
27
28 static const char *widtype = NULL;
29 static void _del_hook(Evas_Object *obj);
30 static void _disable_hook(Evas_Object *obj);
31 static void _write_label(Evas_Object *obj);
32 static void _sizing_eval(Evas_Object *obj);
33 //static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
34 static Eina_Bool _value_set(Evas_Object *obj, double delta);
35 static void _on_focus_hook(void *data, Evas_Object *obj);
36 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
37                              Evas_Callback_Type type, void *event_info);
38
39 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
40
41 static const char SIG_CHANGED[] = "changed";
42 static const char SIG_DELAY_CHANGED[] = "delay,changed";
43
44 static const Evas_Smart_Cb_Description _signals[] = {
45    {SIG_CHANGED, ""},
46    {SIG_DELAY_CHANGED, ""},
47    {NULL, NULL}
48 };
49
50 static void
51 _del_hook(Evas_Object *obj)
52 {
53    Elm_Spinner_Special_Value *sv;
54    Widget_Data *wd = elm_widget_data_get(obj);
55    if (!wd) return;
56    if (wd->label) eina_stringshare_del(wd->label);
57    if (wd->delay) ecore_timer_del(wd->delay);
58    if (wd->spin) ecore_timer_del(wd->spin);
59    if (wd->special_values)
60      {
61         EINA_LIST_FREE(wd->special_values, sv)
62           {
63              eina_stringshare_del(sv->label);
64              free(sv);
65           }
66      }
67    free(wd);
68 }
69
70 static void
71 _disable_hook(Evas_Object *obj)
72 {
73    Widget_Data *wd = elm_widget_data_get(obj);
74    if (!wd) return;
75    if (elm_widget_disabled_get(obj))
76       edje_object_signal_emit(wd->spinner, "elm,state,disabled", "elm");
77    else
78       edje_object_signal_emit(wd->spinner, "elm,state,enabled", "elm");
79 }
80
81 static void
82 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
83 {
84    Widget_Data *wd = elm_widget_data_get(obj);
85    if (!wd) return;
86    edje_object_signal_emit(wd->spinner, emission, source);
87 }
88
89 static void
90 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
91 {
92    Widget_Data *wd = elm_widget_data_get(obj);
93    if (!wd) return;
94    edje_object_signal_callback_add(wd->spinner, emission,
95                                    source, func_cb, data);
96 }
97
98 static void
99 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
100 {
101    Widget_Data *wd = elm_widget_data_get(obj);
102    edje_object_signal_callback_del_full(wd->spinner, emission, source,
103                                         func_cb, data);
104 }
105
106 static void
107 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
108 {
109    Widget_Data *wd = elm_widget_data_get(obj);
110    if (!wd) return;
111    edje_object_mirrored_set(wd->spinner, rtl);
112 }
113
114 static void
115 _theme_hook(Evas_Object *obj)
116 {
117    Widget_Data *wd = elm_widget_data_get(obj);
118    if (!wd) return;
119    _elm_widget_mirrored_reload(obj);
120    _mirrored_set(obj, elm_widget_mirrored_get(obj));
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, SIG_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_object_text_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, SIG_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    Evas_Object *obj = data;
303    Widget_Data *wd = elm_widget_data_get(obj);
304    double pos = 0.0, offset, delta;
305    if (!wd) return;
306    if (wd->entry_visible) return;
307    edje_object_part_drag_value_get(wd->spinner, "elm.dragable.slider",
308                                    &pos, NULL);
309    offset = wd->step;
310    delta = (pos - wd->drag_start_pos) * offset;
311    /* If we are on rtl mode, change the delta to be negative on such changes */
312    if (elm_widget_mirrored_get(obj))
313      delta *= -1;
314    if (_value_set(data, delta)) _write_label(data);
315    wd->drag_start_pos = pos;
316    wd->dragging = 1;
317 }
318
319 static void
320 _drag_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
321 {
322    Widget_Data *wd = elm_widget_data_get(data);
323    double pos;
324    if (!wd) return;
325    edje_object_part_drag_value_get(wd->spinner, "elm.dragable.slider",
326                                    &pos, NULL);
327    wd->drag_start_pos = pos;
328 }
329
330 static void
331 _drag_stop(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    if (!wd) return;
335    wd->drag_start_pos = 0;
336    edje_object_part_drag_value_set(wd->spinner, "elm.dragable.slider", 0.0, 0.0);
337 }
338
339 static void
340 _hide_entry(Evas_Object *obj)
341 {
342    Widget_Data *wd = elm_widget_data_get(obj);
343    if (!wd) return;
344    edje_object_signal_emit(wd->spinner, "elm,state,inactive", "elm");
345    wd->entry_visible = 0;
346 }
347
348 static void
349 _reset_value(Evas_Object *obj)
350 {
351    Widget_Data *wd = elm_widget_data_get(obj);
352    if (!wd) return;
353    _hide_entry(obj);
354    elm_spinner_value_set(obj, wd->orig_val);
355 }
356
357 static void
358 _apply_entry_value(Evas_Object *obj)
359 {
360    Widget_Data *wd = elm_widget_data_get(obj);
361    const char *str;
362    char *end;
363    double val;
364
365    if (!wd) return;
366    _hide_entry(obj);
367    str = elm_object_text_get(wd->ent);
368    if (!str) return;
369    val = strtod(str, &end);
370    if ((*end != '\0') && (!isspace(*end))) return;
371    elm_spinner_value_set(obj, val);
372 }
373
374 static void
375 _toggle_entry(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
376 {
377    Widget_Data *wd = elm_widget_data_get(data);
378    if (!wd) return;
379    if (wd->dragging)
380      {
381         wd->dragging = 0;
382         return;
383      }
384    if (elm_widget_disabled_get(data)) return;
385    if (!wd->editable) return;
386    if (wd->entry_visible) _apply_entry_value(data);
387    else
388      {
389         wd->orig_val = wd->val;
390         edje_object_signal_emit(wd->spinner, "elm,state,active", "elm");
391         _entry_show(wd);
392         elm_entry_select_all(wd->ent);
393         elm_widget_focus_set(wd->ent, 1);
394         wd->entry_visible = 1;
395      }
396 }
397
398 static Eina_Bool
399 _spin_value(void *data)
400 {
401    Widget_Data *wd = elm_widget_data_get(data);
402    if (!wd) return ECORE_CALLBACK_CANCEL;
403    if (_value_set(data, wd->spin_speed)) _write_label(data);
404    wd->interval = wd->interval / 1.05;
405    ecore_timer_interval_set(wd->spin, wd->interval);
406    return ECORE_CALLBACK_RENEW;
407 }
408
409 static void
410 _val_inc_start(Evas_Object *obj)
411 {
412    Widget_Data *wd = elm_widget_data_get(obj);
413    if (!wd) return;
414    wd->interval = wd->first_interval;
415    wd->spin_speed = wd->step;
416    if (wd->spin) ecore_timer_del(wd->spin);
417    wd->spin = ecore_timer_add(wd->interval, _spin_value, obj);
418    _spin_value(obj);
419 }
420
421 static void
422 _val_inc_stop(Evas_Object *obj)
423 {
424    Widget_Data *wd = elm_widget_data_get(obj);
425    if (!wd) return;
426    wd->interval = wd->first_interval;
427    wd->spin_speed = 0;
428    if (wd->spin) ecore_timer_del(wd->spin);
429    wd->spin = NULL;
430 }
431
432 static void
433 _val_dec_start(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 = -wd->step;
439    if (wd->spin) ecore_timer_del(wd->spin);
440    wd->spin = ecore_timer_add(wd->interval, _spin_value, obj);
441    _spin_value(obj);
442 }
443
444 static void
445 _val_dec_stop(Evas_Object *obj)
446 {
447    Widget_Data *wd = elm_widget_data_get(obj);
448    if (!wd) return;
449    wd->interval = wd->first_interval;
450    wd->spin_speed = 0;
451    if (wd->spin) ecore_timer_del(wd->spin);
452    wd->spin = NULL;
453 }
454
455 static void
456 _button_inc_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
457 {
458    Widget_Data *wd = elm_widget_data_get(data);
459    if (!wd) return;
460    if (wd->entry_visible)
461      {
462         _reset_value(data);
463         return;
464      }
465    _val_inc_start(data);
466 }
467
468 static void
469 _button_inc_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
470 {
471    Widget_Data *wd = elm_widget_data_get(data);
472    if (!wd) return;
473    _val_inc_stop(data);
474 }
475
476 static void
477 _button_dec_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
478 {
479    Widget_Data *wd = elm_widget_data_get(data);
480    if (!wd) return;
481    if (wd->entry_visible)
482      {
483         _reset_value(data);
484         return;
485      }
486    _val_dec_start(data);
487 }
488
489 static void
490 _button_dec_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
491 {
492    Widget_Data *wd = elm_widget_data_get(data);
493    if (!wd) return;
494    _val_dec_stop(data);
495 }
496
497 static void
498 _entry_activated(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
499 {
500    Widget_Data *wd = elm_widget_data_get(data);
501    if (!wd) return;
502    _apply_entry_value(data);
503    evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
504    if (wd->delay) ecore_timer_del(wd->delay);
505    wd->delay = ecore_timer_add(0.2, _delay_change, data);
506 }
507
508 static Eina_Bool
509 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
510 {
511    Widget_Data *wd = elm_widget_data_get(obj);
512    if (!wd) return EINA_FALSE;
513    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
514    if (type == EVAS_CALLBACK_KEY_DOWN)
515      {
516         Evas_Event_Key_Down *ev = event_info;
517         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
518         else if (!strcmp(ev->keyname, "Left") || !strcmp(ev->keyname, "KP_Left")
519                  || !strcmp(ev->keyname, "Down") || !strcmp(ev->keyname, "KP_Down"))
520           {
521              _val_dec_start(obj);
522              edje_object_signal_emit(wd->spinner, "elm,left,anim,activate", "elm");
523              ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
524              return EINA_TRUE;
525           }
526         else if (!strcmp(ev->keyname, "Right") || !strcmp(ev->keyname, "KP_Right")
527                  || !strcmp(ev->keyname, "Up") || !strcmp(ev->keyname, "KP_Up"))
528           {
529              _val_inc_start(obj);
530              edje_object_signal_emit(wd->spinner, "elm,right,anim,activate", "elm");
531              ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
532              return EINA_TRUE;
533           }
534      }
535    else if (type == EVAS_CALLBACK_KEY_UP)
536      {
537         Evas_Event_Key_Down *ev = event_info;
538         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
539         if (!strcmp(ev->keyname, "Right") || !strcmp(ev->keyname, "KP_Right")
540             || !strcmp(ev->keyname, "Up") || !strcmp(ev->keyname, "KP_Up"))
541           _val_inc_stop(obj);
542         else if (!strcmp(ev->keyname, "Left") || !strcmp(ev->keyname, "KP_Left")
543                  || !strcmp(ev->keyname, "Down") || !strcmp(ev->keyname, "KP_Down"))
544           _val_dec_stop(obj);
545         else  return EINA_FALSE;
546         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
547         return EINA_TRUE;
548      }
549    return EINA_FALSE;
550 }
551
552 EAPI Evas_Object *
553 elm_spinner_add(Evas_Object *parent)
554 {
555    Evas_Object *obj;
556    Evas *e;
557    Widget_Data *wd;
558
559    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
560
561    ELM_SET_WIDTYPE(widtype, "spinner");
562    elm_widget_type_set(obj, "spinner");
563    elm_widget_sub_object_add(parent, obj);
564    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
565    elm_widget_data_set(obj, wd);
566    elm_widget_del_hook_set(obj, _del_hook);
567    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
568    elm_widget_theme_hook_set(obj, _theme_hook);
569    elm_widget_disable_hook_set(obj, _disable_hook);
570    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
571    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
572    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
573    elm_widget_can_focus_set(obj, EINA_TRUE);
574    elm_widget_event_hook_set(obj, _event_hook);
575
576    wd->val = 0.0;
577    wd->val_min = 0.0;
578    wd->val_max = 100.0;
579    wd->wrap = 0;
580    wd->step = 1.0;
581    wd->first_interval = 0.85;
582    wd->entry_visible = 0;
583    wd->editable = EINA_TRUE;
584
585    wd->spinner = edje_object_add(e);
586    _elm_theme_object_set(obj, wd->spinner, "spinner", "base", "default");
587    elm_widget_resize_object_set(obj, wd->spinner);
588    edje_object_signal_callback_add(wd->spinner, "drag", "*", _drag, obj);
589    edje_object_signal_callback_add(wd->spinner, "drag,start", "*",
590                                    _drag_start, obj);
591    edje_object_signal_callback_add(wd->spinner, "drag,stop", "*",
592                                    _drag_stop, obj);
593    edje_object_signal_callback_add(wd->spinner, "drag,step", "*",
594                                    _drag_stop, obj);
595    edje_object_signal_callback_add(wd->spinner, "drag,page", "*",
596                                    _drag_stop, obj);
597
598    edje_object_signal_callback_add(wd->spinner, "elm,action,increment,start",
599                                    "*", _button_inc_start, obj);
600    edje_object_signal_callback_add(wd->spinner, "elm,action,increment,stop",
601                                    "*", _button_inc_stop, obj);
602    edje_object_signal_callback_add(wd->spinner, "elm,action,decrement,start",
603                                    "*", _button_dec_start, obj);
604    edje_object_signal_callback_add(wd->spinner, "elm,action,decrement,stop",
605                                    "*", _button_dec_stop, obj);
606    edje_object_part_drag_value_set(wd->spinner, "elm.dragable.slider",
607                                    0.0, 0.0);
608
609    wd->ent = elm_entry_add(obj);
610    elm_entry_single_line_set(wd->ent, 1);
611    evas_object_smart_callback_add(wd->ent, "activated", _entry_activated, obj);
612    edje_object_part_swallow(wd->spinner, "elm.swallow.entry", wd->ent);
613    edje_object_signal_callback_add(wd->spinner, "elm,action,entry,toggle",
614                                    "*", _toggle_entry, obj);
615
616    evas_object_smart_callbacks_descriptions_set(obj, _signals);
617
618    _mirrored_set(obj, elm_widget_mirrored_get(obj));
619    _write_label(obj);
620    _sizing_eval(obj);
621    return obj;
622 }
623
624 EAPI void
625 elm_spinner_label_format_set(Evas_Object *obj, const char *fmt)
626 {
627    ELM_CHECK_WIDTYPE(obj, widtype);
628    Widget_Data *wd = elm_widget_data_get(obj);
629    if (!wd) return;
630    eina_stringshare_replace(&wd->label, fmt);
631    _write_label(obj);
632    _sizing_eval(obj);
633 }
634
635 EAPI const char *
636 elm_spinner_label_format_get(const Evas_Object *obj)
637 {
638    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
639    Widget_Data *wd = elm_widget_data_get(obj);
640    if (!wd) return NULL;
641    return wd->label;
642 }
643
644 EAPI void
645 elm_spinner_min_max_set(Evas_Object *obj, double min, double max)
646 {
647    ELM_CHECK_WIDTYPE(obj, widtype);
648    Widget_Data *wd = elm_widget_data_get(obj);
649    if (!wd) return;
650    if ((wd->val_min == min) && (wd->val_max == max)) return;
651    wd->val_min = min;
652    wd->val_max = max;
653    if (wd->val < wd->val_min) wd->val = wd->val_min;
654    if (wd->val > wd->val_max) wd->val = wd->val_max;
655    _val_set(obj);
656    _write_label(obj);
657 }
658
659 EAPI void
660 elm_spinner_min_max_get(const Evas_Object *obj, double *min, double *max)
661 {
662    if (min) *min = 0.0;
663    if (max) *max = 0.0;
664    ELM_CHECK_WIDTYPE(obj, widtype);
665    Widget_Data *wd = elm_widget_data_get(obj);
666    if (!wd) return;
667    if (min) *min = wd->val_min;
668    if (max) *max = wd->val_max;
669 }
670
671 EAPI void
672 elm_spinner_step_set(Evas_Object *obj, double step)
673 {
674    ELM_CHECK_WIDTYPE(obj, widtype);
675    Widget_Data *wd = elm_widget_data_get(obj);
676    if (!wd) return;
677    wd->step = step;
678 }
679
680 EAPI double
681 elm_spinner_step_get(const Evas_Object *obj)
682 {
683    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
684    Widget_Data *wd = elm_widget_data_get(obj);
685    if (!wd) return 0.0;
686    return wd->step;
687 }
688
689 EAPI void
690 elm_spinner_value_set(Evas_Object *obj, double val)
691 {
692    ELM_CHECK_WIDTYPE(obj, widtype);
693    Widget_Data *wd = elm_widget_data_get(obj);
694    if (!wd) return;
695    if (wd->val == val) return;
696    wd->val = val;
697    if (wd->val < wd->val_min) wd->val = wd->val_min;
698    if (wd->val > wd->val_max) wd->val = wd->val_max;
699    _val_set(obj);
700    _write_label(obj);
701 }
702
703 EAPI double
704 elm_spinner_value_get(const Evas_Object *obj)
705 {
706    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
707    Widget_Data *wd = elm_widget_data_get(obj);
708    if (!wd) return 0.0;
709    return wd->val;
710 }
711
712 EAPI void
713 elm_spinner_wrap_set(Evas_Object *obj, Eina_Bool wrap)
714 {
715    ELM_CHECK_WIDTYPE(obj, widtype);
716    Widget_Data *wd = elm_widget_data_get(obj);
717    if (!wd) return;
718    wd->wrap = wrap;
719 }
720
721 EAPI Eina_Bool
722 elm_spinner_wrap_get(const Evas_Object *obj)
723 {
724    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
725    Widget_Data *wd = elm_widget_data_get(obj);
726    if (!wd) return EINA_FALSE;
727    return wd->wrap;
728 }
729
730 EAPI void
731 elm_spinner_special_value_add(Evas_Object *obj, double value, const char *label)
732 {
733    Elm_Spinner_Special_Value *sv;
734    ELM_CHECK_WIDTYPE(obj, widtype);
735    Widget_Data *wd = elm_widget_data_get(obj);
736    if (!wd) return;
737
738    sv = calloc(1, sizeof(*sv));
739    if (!sv) return;
740    sv->value = value;
741    sv->label = eina_stringshare_add(label);
742
743    wd->special_values = eina_list_append(wd->special_values, sv);
744    _write_label(obj);
745 }
746
747 EAPI void
748 elm_spinner_editable_set(Evas_Object *obj, Eina_Bool editable)
749 {
750    ELM_CHECK_WIDTYPE(obj, widtype);
751    Widget_Data *wd = elm_widget_data_get(obj);
752    if (!wd) return;
753    wd->editable = editable;
754 }
755
756 EAPI Eina_Bool
757 elm_spinner_editable_get(const Evas_Object *obj)
758 {
759    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
760    Widget_Data *wd = elm_widget_data_get(obj);
761    if (!wd) return EINA_FALSE;
762    return wd->editable;
763 }
764
765 EAPI void
766 elm_spinner_interval_set(Evas_Object *obj, double interval)
767 {
768    ELM_CHECK_WIDTYPE(obj, widtype);
769    Widget_Data *wd = elm_widget_data_get(obj);
770    if (!wd) return;
771    wd->first_interval = interval;
772 }
773
774 EAPI double
775 elm_spinner_interval_get(const Evas_Object *obj)
776 {
777    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
778    Widget_Data *wd = elm_widget_data_get(obj);
779    if (!wd) return 0.0;
780    return wd->first_interval;
781 }