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