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