elementary/map - map supports language,changed
[framework/uifw/elementary.git] / src / lib / elm_spinner.c
1 #include <Elementary.h>
2 //#include <ctype.h>
3 #include "elm_priv.h"
4 #include "elm_widget_spinner.h"
5
6 EAPI const char ELM_SPINNER_SMART_NAME[] = "elm_spinner";
7
8 static const char SIG_CHANGED[] = "changed";
9 static const char SIG_DELAY_CHANGED[] = "delay,changed";
10 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
11    {SIG_CHANGED, ""},
12    {SIG_DELAY_CHANGED, ""},
13    {NULL, NULL}
14 };
15
16 EVAS_SMART_SUBCLASS_NEW
17   (ELM_SPINNER_SMART_NAME, _elm_spinner, Elm_Spinner_Smart_Class,
18   Elm_Layout_Smart_Class, elm_layout_smart_class_get, _smart_callbacks);
19
20 static void
21 _entry_show(Elm_Spinner_Smart_Data *sd)
22 {
23    char buf[32], fmt[32] = "%0.f";
24
25    /* try to construct just the format from given label
26     * completely ignoring pre/post words
27     */
28    if (sd->label)
29      {
30         const char *start = strchr(sd->label, '%');
31         while (start)
32           {
33              /* handle %% */
34              if (start[1] != '%')
35                break;
36              else
37                start = strchr(start + 2, '%');
38           }
39
40         if (start)
41           {
42              const char *itr, *end = NULL;
43              for (itr = start + 1; *itr != '\0'; itr++)
44                {
45                   /* allowing '%d' is quite dangerous, remove it? */
46                   if ((*itr == 'd') || (*itr == 'f'))
47                     {
48                        end = itr + 1;
49                        break;
50                     }
51                }
52
53              if ((end) && ((size_t)(end - start + 1) < sizeof(fmt)))
54                {
55                   memcpy(fmt, start, end - start);
56                   fmt[end - start] = '\0';
57                }
58           }
59      }
60    snprintf(buf, sizeof(buf), fmt, sd->val);
61    elm_object_text_set(sd->ent, buf);
62 }
63
64 static void
65 _label_write(Evas_Object *obj)
66 {
67    Eina_List *l;
68    char buf[1024];
69    Elm_Spinner_Special_Value *sv;
70
71    ELM_SPINNER_DATA_GET(obj, sd);
72
73    EINA_LIST_FOREACH(sd->special_values, l, sv)
74      {
75         if (sv->value == sd->val)
76           {
77              snprintf(buf, sizeof(buf), "%s", sv->label);
78              goto apply;
79           }
80      }
81    if (sd->label)
82      snprintf(buf, sizeof(buf), sd->label, sd->val);
83    else
84      snprintf(buf, sizeof(buf), "%.0f", sd->val);
85
86 apply:
87    elm_layout_text_set(obj, "elm.text", buf);
88    if (sd->entry_visible) _entry_show(sd);
89 }
90
91 static Eina_Bool
92 _delay_change(void *data)
93 {
94    ELM_SPINNER_DATA_GET(data, sd);
95
96    sd->delay = NULL;
97    evas_object_smart_callback_call(data, SIG_DELAY_CHANGED, NULL);
98
99    return ECORE_CALLBACK_CANCEL;
100 }
101
102 static Eina_Bool
103 _value_set(Evas_Object *obj,
104            double new_val)
105 {
106    ELM_SPINNER_DATA_GET(obj, sd);
107
108    if (sd->round > 0)
109      new_val = sd->val_base +
110        (double)((((int)(new_val - sd->val_base)) / sd->round) * sd->round);
111
112    if (sd->wrap)
113      {
114         if (new_val < sd->val_min)
115           new_val = sd->val_max;
116         else if (new_val > sd->val_max)
117           new_val = sd->val_min;
118      }
119    else
120      {
121         if (new_val < sd->val_min)
122           new_val = sd->val_min;
123         else if (new_val > sd->val_max)
124           new_val = sd->val_max;
125      }
126
127    if (new_val == sd->val) return EINA_FALSE;
128    sd->val = new_val;
129
130    evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
131    if (sd->delay) ecore_timer_del(sd->delay);
132    sd->delay = ecore_timer_add(0.2, _delay_change, obj);
133
134    return EINA_TRUE;
135 }
136
137 static void
138 _val_set(Evas_Object *obj)
139 {
140    double pos = 0.0;
141
142    ELM_SPINNER_DATA_GET(obj, sd);
143
144    if (sd->val_max > sd->val_min)
145      pos = ((sd->val - sd->val_min) / (sd->val_max - sd->val_min));
146    if (pos < 0.0) pos = 0.0;
147    else if (pos > 1.0)
148      pos = 1.0;
149    edje_object_part_drag_value_set
150      (ELM_WIDGET_DATA(sd)->resize_obj, "elm.dragable.slider", pos, pos);
151 }
152
153 static void
154 _drag_cb(void *data,
155          Evas_Object *_obj __UNUSED__,
156          const char *emission __UNUSED__,
157          const char *source __UNUSED__)
158 {
159    double pos = 0.0, offset, delta;
160    Evas_Object *obj = data;
161
162    ELM_SPINNER_DATA_GET(obj, sd);
163
164    if (sd->entry_visible) return;
165    edje_object_part_drag_value_get
166      (ELM_WIDGET_DATA(sd)->resize_obj, "elm.dragable.slider", &pos, NULL);
167
168    offset = sd->step * _elm_config->scale;
169    delta = (pos - sd->drag_start_pos) * offset;
170    /* If we are on rtl mode, change the delta to be negative on such changes */
171    if (elm_widget_mirrored_get(obj)) delta *= -1;
172    if (_value_set(data, sd->drag_start_pos + delta)) _label_write(data);
173    sd->dragging = 1;
174 }
175
176 static void
177 _drag_start_cb(void *data,
178                Evas_Object *obj __UNUSED__,
179                const char *emission __UNUSED__,
180                const char *source __UNUSED__)
181 {
182    double pos;
183
184    ELM_SPINNER_DATA_GET(data, sd);
185
186    edje_object_part_drag_value_get
187      (ELM_WIDGET_DATA(sd)->resize_obj, "elm.dragable.slider", &pos, NULL);
188    sd->drag_start_pos = pos;
189 }
190
191 static void
192 _drag_stop_cb(void *data,
193               Evas_Object *obj __UNUSED__,
194               const char *emission __UNUSED__,
195               const char *source __UNUSED__)
196 {
197    ELM_SPINNER_DATA_GET(data, sd);
198
199    sd->drag_start_pos = 0;
200    edje_object_part_drag_value_set
201      (ELM_WIDGET_DATA(sd)->resize_obj, "elm.dragable.slider", 0.0, 0.0);
202 }
203
204 static void
205 _entry_hide(Evas_Object *obj)
206 {
207    ELM_SPINNER_DATA_GET(obj, sd);
208
209    elm_layout_signal_emit(obj, "elm,state,inactive", "elm");
210    sd->entry_visible = EINA_FALSE;
211 }
212
213 static void
214 _reset_value(Evas_Object *obj)
215 {
216    ELM_SPINNER_DATA_GET(obj, sd);
217
218    _entry_hide(obj);
219    elm_spinner_value_set(obj, sd->orig_val);
220 }
221
222 static void
223 _entry_value_apply(Evas_Object *obj)
224 {
225    const char *str;
226    double val;
227    char *end;
228
229    ELM_SPINNER_DATA_GET(obj, sd);
230
231    if (!sd->entry_visible) return;
232
233    _entry_hide(obj);
234    str = elm_object_text_get(sd->ent);
235    if (!str) return;
236    val = strtod(str, &end);
237    if ((*end != '\0') && (!isspace(*end))) return;
238    elm_spinner_value_set(obj, val);
239 }
240
241 static void
242 _entry_toggle_cb(void *data,
243                  Evas_Object *obj __UNUSED__,
244                  const char *emission __UNUSED__,
245                  const char *source __UNUSED__)
246 {
247    ELM_SPINNER_DATA_GET(data, sd);
248
249    if (sd->dragging)
250      {
251         sd->dragging = 0;
252         return;
253      }
254    if (elm_widget_disabled_get(data)) return;
255    if (!sd->editable) return;
256    if (sd->entry_visible) _entry_value_apply(data);
257    else
258      {
259         sd->orig_val = sd->val;
260         elm_layout_signal_emit(data, "elm,state,active", "elm");
261         _entry_show(sd);
262         elm_entry_select_all(sd->ent);
263         elm_widget_focus_set(sd->ent, 1);
264         sd->entry_visible = EINA_TRUE;
265      }
266 }
267
268 static Eina_Bool
269 _spin_value(void *data)
270 {
271    ELM_SPINNER_DATA_GET(data, sd);
272    double real_speed = sd->spin_speed;
273
274    /* Sanity check: our step size should be at least as large as our rounding value */
275    if ((sd->spin_speed != 0.0) && (abs(sd->spin_speed) < sd->round))
276      {
277         WRN("The spinning step is smaller than the rounding value, please check your code");
278         real_speed = sd->spin_speed > 0 ? sd->round : -sd->round;
279      }
280
281    if (_value_set(data, sd->val + real_speed)) _label_write(data);
282    sd->interval = sd->interval / 1.05;
283    ecore_timer_interval_set(sd->spin, sd->interval);
284
285    return ECORE_CALLBACK_RENEW;
286 }
287
288 static void
289 _val_inc_start(Evas_Object *obj)
290 {
291    ELM_SPINNER_DATA_GET(obj, sd);
292
293    sd->interval = sd->first_interval;
294    sd->spin_speed = sd->step;
295    if (sd->spin) ecore_timer_del(sd->spin);
296    sd->spin = ecore_timer_add(sd->interval, _spin_value, obj);
297    _spin_value(obj);
298 }
299
300 static void
301 _val_inc_stop(Evas_Object *obj)
302 {
303    ELM_SPINNER_DATA_GET(obj, sd);
304
305    sd->interval = sd->first_interval;
306    sd->spin_speed = 0;
307    if (sd->spin) ecore_timer_del(sd->spin);
308    sd->spin = NULL;
309 }
310
311 static void
312 _val_dec_start(Evas_Object *obj)
313 {
314    ELM_SPINNER_DATA_GET(obj, sd);
315
316    sd->interval = sd->first_interval;
317    sd->spin_speed = -sd->step;
318    if (sd->spin) ecore_timer_del(sd->spin);
319    sd->spin = ecore_timer_add(sd->interval, _spin_value, obj);
320    _spin_value(obj);
321 }
322
323 static void
324 _val_dec_stop(Evas_Object *obj)
325 {
326    ELM_SPINNER_DATA_GET(obj, sd);
327
328    sd->interval = sd->first_interval;
329    sd->spin_speed = 0;
330    if (sd->spin) ecore_timer_del(sd->spin);
331    sd->spin = NULL;
332 }
333
334 static void
335 _button_inc_start_cb(void *data,
336                      Evas_Object *obj __UNUSED__,
337                      const char *emission __UNUSED__,
338                      const char *source __UNUSED__)
339 {
340    ELM_SPINNER_DATA_GET(data, sd);
341
342    if (sd->entry_visible)
343      {
344         _reset_value(data);
345         return;
346      }
347    _val_inc_start(data);
348 }
349
350 static void
351 _button_inc_stop_cb(void *data,
352                     Evas_Object *obj __UNUSED__,
353                     const char *emission __UNUSED__,
354                     const char *source __UNUSED__)
355 {
356    _val_inc_stop(data);
357 }
358
359 static void
360 _button_dec_start_cb(void *data,
361                      Evas_Object *obj __UNUSED__,
362                      const char *emission __UNUSED__,
363                      const char *source __UNUSED__)
364 {
365    ELM_SPINNER_DATA_GET(data, sd);
366
367    if (sd->entry_visible)
368      {
369         _reset_value(data);
370         return;
371      }
372    _val_dec_start(data);
373 }
374
375 static void
376 _button_dec_stop_cb(void *data,
377                     Evas_Object *obj __UNUSED__,
378                     const char *emission __UNUSED__,
379                     const char *source __UNUSED__)
380 {
381    _val_dec_stop(data);
382 }
383
384 static void
385 _entry_activated_cb(void *data,
386                     Evas_Object *obj __UNUSED__,
387                     void *event_info __UNUSED__)
388 {
389    ELM_SPINNER_DATA_GET(data, sd);
390
391    _entry_value_apply(data);
392    evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
393    if (sd->delay) ecore_timer_del(sd->delay);
394    sd->delay = ecore_timer_add(0.2, _delay_change, data);
395 }
396
397 static void
398 _elm_spinner_smart_sizing_eval(Evas_Object *obj)
399 {
400    Evas_Coord minw = -1, minh = -1;
401
402    ELM_SPINNER_DATA_GET(obj, sd);
403
404    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
405    edje_object_size_min_restricted_calc
406      (ELM_WIDGET_DATA(sd)->resize_obj, &minw, &minh, minw, minh);
407    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
408    evas_object_size_hint_min_set(obj, minw, minh);
409    evas_object_size_hint_max_set(obj, -1, -1);
410 }
411
412 static Eina_Bool
413 _elm_spinner_smart_event(Evas_Object *obj,
414                          Evas_Object *src __UNUSED__,
415                          Evas_Callback_Type type,
416                          void *event_info)
417 {
418    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
419    if (type == EVAS_CALLBACK_KEY_DOWN)
420      {
421         Evas_Event_Key_Down *ev = event_info;
422
423         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
424         else if (!strcmp(ev->keyname, "Left") ||
425                  ((!strcmp(ev->keyname, "KP_Left")) && (!ev->string)) ||
426                  !strcmp(ev->keyname, "Down") ||
427                  ((!strcmp(ev->keyname, "KP_Down")) && (!ev->string)))
428           {
429              _val_dec_start(obj);
430              elm_layout_signal_emit(obj, "elm,left,anim,activate", "elm");
431              ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
432              return EINA_TRUE;
433           }
434         else if (!strcmp(ev->keyname, "Right") ||
435                  ((!strcmp(ev->keyname, "KP_Right")) && (!ev->string)) ||
436                  !strcmp(ev->keyname, "Up") ||
437                  ((!strcmp(ev->keyname, "KP_Up")) && (!ev->string)))
438           {
439              _val_inc_start(obj);
440              elm_layout_signal_emit(obj, "elm,right,anim,activate", "elm");
441              ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
442              return EINA_TRUE;
443           }
444      }
445    else if (type == EVAS_CALLBACK_KEY_UP)
446      {
447         Evas_Event_Key_Down *ev = event_info;
448
449         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
450         if (!strcmp(ev->keyname, "Right") ||
451             ((!strcmp(ev->keyname, "KP_Right")) && (!ev->string)) ||
452             !strcmp(ev->keyname, "Up") ||
453             ((!strcmp(ev->keyname, "KP_Up")) && (!ev->string)))
454           _val_inc_stop(obj);
455         else if (!strcmp(ev->keyname, "Left") ||
456                  ((!strcmp(ev->keyname, "KP_Left")) && (!ev->string)) ||
457                  !strcmp(ev->keyname, "Down") ||
458                  ((!strcmp(ev->keyname, "KP_Down")) && (!ev->string)))
459           _val_dec_stop(obj);
460         else return EINA_FALSE;
461
462         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
463
464         return EINA_TRUE;
465      }
466
467    return EINA_FALSE;
468 }
469
470 static Eina_Bool
471 _elm_spinner_smart_on_focus(Evas_Object *obj)
472 {
473    if (!ELM_WIDGET_CLASS(_elm_spinner_parent_sc)->on_focus(obj))
474      return EINA_FALSE;
475
476    if (!elm_widget_focus_get(obj))
477      _entry_value_apply(obj);
478
479    return EINA_TRUE;
480 }
481
482 static char *
483 _access_info_cb(void *data, Evas_Object *obj)
484 {
485    Evas_Object *spinner;
486    const char *txt = elm_widget_access_info_get(obj);
487
488    spinner = ELM_WIDGET_DATA(data)->obj;
489    if (!txt) txt = elm_layout_text_get(spinner, "elm.text");
490    if (txt) return strdup(txt);
491
492    return NULL;
493 }
494
495 static char *
496 _access_state_cb(void *data, Evas_Object *obj __UNUSED__)
497 {
498    if (elm_widget_disabled_get(ELM_WIDGET_DATA(data)->obj))
499      return strdup(E_("State: Disabled"));
500
501    return NULL;
502 }
503
504 static void
505 _access_activate_cb(void *data,
506                     Evas_Object *part_obj,
507                     Elm_Object_Item *item __UNUSED__)
508 {
509    char *text;
510    Eina_Strbuf *buf;
511    Evas_Object *eo, *inc_btn;
512    const char* increment_part;
513
514    if (!strcmp(elm_widget_style_get(data), "vertical"))
515      increment_part = "up_bt";
516    else
517      increment_part = "right_bt";
518
519    eo = elm_layout_edje_get(data);
520    inc_btn = (Evas_Object *)edje_object_part_object_get(eo, increment_part);
521
522    if (part_obj != inc_btn)
523      {
524         _val_dec_start(data);
525         elm_layout_signal_emit(data, "elm,left,anim,activate", "elm");
526         _val_dec_stop(data);
527         text = "decremented";
528      }
529    else
530      {
531         _val_inc_start(data);
532         elm_layout_signal_emit(data, "elm,right,anim,activate", "elm");
533         _val_inc_stop(data);
534         text = "incremented";
535      }
536
537    buf = eina_strbuf_new();
538
539    eina_strbuf_append_printf(buf, "%s, %s", text,
540             elm_layout_text_get(data, "elm.text"));
541
542    text = eina_strbuf_string_steal(buf);
543    eina_strbuf_free(buf);
544
545    _elm_access_say(text);
546 }
547
548 static void
549 _access_spinner_register(Evas_Object *obj, Eina_Bool is_access)
550 {
551    Evas_Object *ao;
552    Elm_Access_Info *ai;
553    const char* increment_part;
554    const char* decrement_part;
555
556    if (!strcmp(elm_widget_style_get(obj), "vertical"))
557      {
558         increment_part = "up_bt";
559         decrement_part = "down_bt";
560      }
561    else
562      {
563         increment_part = "right_bt";
564         decrement_part = "left_bt";
565      }
566
567    if (!is_access)
568      {
569         /* unregister increment button, decrement button and spinner label */
570         _elm_access_edje_object_part_object_unregister
571           (obj, elm_layout_edje_get(obj), increment_part);
572
573         _elm_access_edje_object_part_object_unregister
574           (obj, elm_layout_edje_get(obj), decrement_part);
575
576         _elm_access_edje_object_part_object_unregister
577           (obj, elm_layout_edje_get(obj), "access_text");
578
579         return;
580      }
581
582    /* register increment button */
583    ao = _elm_access_edje_object_part_object_register
584           (obj, elm_layout_edje_get(obj), increment_part);
585
586    ai = _elm_access_object_get(ao);
587    _elm_access_text_set(ai, ELM_ACCESS_TYPE,
588                         E_("spinner increment button"));
589    _elm_access_activate_callback_set(ai, _access_activate_cb, obj);
590
591    /* register decrement button */
592    ao = _elm_access_edje_object_part_object_register
593           (obj, elm_layout_edje_get(obj), decrement_part);
594
595    ai = _elm_access_object_get(ao);
596    _elm_access_text_set(ai, ELM_ACCESS_TYPE,
597                         E_("spinner decrement button"));
598    _elm_access_activate_callback_set(ai, _access_activate_cb, obj);
599
600    /* register spinner label */
601    ao = _elm_access_edje_object_part_object_register
602           (obj, elm_layout_edje_get(obj), "access_text");
603
604    ai = _elm_access_object_get(ao);
605    _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("spinner"));
606    _elm_access_callback_set(ai, ELM_ACCESS_INFO, _access_info_cb, obj);
607    _elm_access_callback_set(ai, ELM_ACCESS_STATE, _access_state_cb, obj);
608 }
609
610 static void
611 _elm_spinner_smart_add(Evas_Object *obj)
612 {
613    EVAS_SMART_DATA_ALLOC(obj, Elm_Spinner_Smart_Data);
614
615    ELM_WIDGET_CLASS(_elm_spinner_parent_sc)->base.add(obj);
616
617    priv->val = 0.0;
618    priv->val_min = 0.0;
619    priv->val_max = 100.0;
620    priv->wrap = 0;
621    priv->step = 1.0;
622    priv->first_interval = 0.85;
623    priv->entry_visible = EINA_FALSE;
624    priv->editable = EINA_TRUE;
625
626    elm_layout_theme_set(obj, "spinner", "base", elm_widget_style_get(obj));
627    elm_layout_signal_callback_add(obj, "drag", "*", _drag_cb, obj);
628    elm_layout_signal_callback_add(obj, "drag,start", "*", _drag_start_cb, obj);
629    elm_layout_signal_callback_add(obj, "drag,stop", "*", _drag_stop_cb, obj);
630    elm_layout_signal_callback_add(obj, "drag,step", "*", _drag_stop_cb, obj);
631    elm_layout_signal_callback_add(obj, "drag,page", "*", _drag_stop_cb, obj);
632
633    elm_layout_signal_callback_add
634      (obj, "elm,action,increment,start", "*", _button_inc_start_cb, obj);
635    elm_layout_signal_callback_add
636      (obj, "elm,action,increment,stop", "*", _button_inc_stop_cb, obj);
637    elm_layout_signal_callback_add
638      (obj, "elm,action,decrement,start", "*", _button_dec_start_cb, obj);
639    elm_layout_signal_callback_add
640      (obj, "elm,action,decrement,stop", "*", _button_dec_stop_cb, obj);
641
642    edje_object_part_drag_value_set
643      (ELM_WIDGET_DATA(priv)->resize_obj, "elm.dragable.slider", 0.0, 0.0);
644
645    priv->ent = elm_entry_add(obj);
646    elm_entry_single_line_set(priv->ent, EINA_TRUE);
647    evas_object_smart_callback_add
648      (priv->ent, "activated", _entry_activated_cb, obj);
649
650    elm_layout_content_set(obj, "elm.swallow.entry", priv->ent);
651    elm_layout_signal_callback_add
652      (obj, "elm,action,entry,toggle", "*", _entry_toggle_cb, obj);
653
654    _label_write(obj);
655    elm_widget_can_focus_set(obj, EINA_TRUE);
656
657    elm_layout_sizing_eval(obj);
658
659    /* access */
660    if (_elm_config->access_mode)
661      _access_spinner_register(obj, EINA_TRUE);
662 }
663
664 static void
665 _elm_spinner_smart_del(Evas_Object *obj)
666 {
667    Elm_Spinner_Special_Value *sv;
668
669    ELM_SPINNER_DATA_GET(obj, sd);
670
671    if (sd->label) eina_stringshare_del(sd->label);
672    if (sd->delay) ecore_timer_del(sd->delay);
673    if (sd->spin) ecore_timer_del(sd->spin);
674    if (sd->special_values)
675      {
676         EINA_LIST_FREE (sd->special_values, sv)
677           {
678              eina_stringshare_del(sv->label);
679              free(sv);
680           }
681      }
682
683    ELM_WIDGET_CLASS(_elm_spinner_parent_sc)->base.del(obj);
684 }
685
686 static Eina_Bool
687 _elm_spinner_smart_theme(Evas_Object *obj)
688 {
689    Eina_Bool ret;
690    ret = elm_layout_theme_set(obj, "spinner", "base",
691                               elm_widget_style_get(obj));
692
693    if (_elm_config->access_mode)
694      _access_spinner_register(obj, EINA_TRUE);
695
696    return ret;
697 }
698
699 static Evas_Object *
700 _access_object_get(const Evas_Object *obj, const char* part)
701 {
702    Evas_Object *eo, *po, *ao;
703
704    eo = elm_layout_edje_get(obj);
705
706    po = (Evas_Object *)edje_object_part_object_get(eo, part);
707    ao = evas_object_data_get(po, "_part_access_obj");
708
709    return ao;
710 }
711
712 static Eina_Bool
713 _elm_spinner_smart_focus_next(const Evas_Object *obj,
714                            Elm_Focus_Direction dir,
715                            Evas_Object **next)
716 {
717    Evas_Object *ao;
718    Eina_List *items = NULL;
719    const char* increment_part;
720    const char* decrement_part;
721
722    ELM_SPINNER_CHECK(obj) EINA_FALSE;
723
724    if (!strcmp(elm_widget_style_get(obj), "vertical"))
725      {
726         increment_part = "up_bt";
727         decrement_part = "down_bt";
728      }
729    else
730      {
731         increment_part = "right_bt";
732         decrement_part = "left_bt";
733      }
734
735    ao = _access_object_get(obj, "access_text");
736    items = eina_list_append(items, ao);
737
738    ao = _access_object_get(obj, decrement_part);
739    items = eina_list_append(items, ao);
740
741    ao = _access_object_get(obj, increment_part);
742    items = eina_list_append(items, ao);
743
744    return elm_widget_focus_list_next_get
745             (obj, items, eina_list_data_get, dir, next);
746 }
747
748 static void
749 _access_hook(Evas_Object *obj, Eina_Bool is_access)
750 {
751    ELM_SPINNER_CHECK(obj);
752    ELM_SPINNER_DATA_GET(obj, sd);
753
754    if (is_access)
755      ELM_WIDGET_CLASS(ELM_WIDGET_DATA(sd)->api)->focus_next =
756        _elm_spinner_smart_focus_next;
757    else
758      ELM_WIDGET_CLASS(ELM_WIDGET_DATA(sd)->api)->focus_next = NULL;
759
760    _access_spinner_register(obj, is_access);
761 }
762
763 static void
764 _elm_spinner_smart_set_user(Elm_Spinner_Smart_Class *sc)
765 {
766    ELM_WIDGET_CLASS(sc)->base.add = _elm_spinner_smart_add;
767    ELM_WIDGET_CLASS(sc)->base.del = _elm_spinner_smart_del;
768
769    /* not a 'focus chain manager' */
770    ELM_WIDGET_CLASS(sc)->focus_next = NULL;
771    ELM_WIDGET_CLASS(sc)->focus_direction = NULL;
772
773    ELM_WIDGET_CLASS(sc)->on_focus = _elm_spinner_smart_on_focus;
774    ELM_WIDGET_CLASS(sc)->event = _elm_spinner_smart_event;
775
776    ELM_LAYOUT_CLASS(sc)->sizing_eval = _elm_spinner_smart_sizing_eval;
777
778    ELM_WIDGET_CLASS(sc)->theme = _elm_spinner_smart_theme;
779
780    /* access */
781    if (_elm_config->access_mode)
782      ELM_WIDGET_CLASS(sc)->focus_next = _elm_spinner_smart_focus_next;
783
784    ELM_WIDGET_CLASS(sc)->access = _access_hook;
785 }
786
787 EAPI const Elm_Spinner_Smart_Class *
788 elm_spinner_smart_class_get(void)
789 {
790    static Elm_Spinner_Smart_Class _sc =
791      ELM_SPINNER_SMART_CLASS_INIT_NAME_VERSION(ELM_SPINNER_SMART_NAME);
792    static const Elm_Spinner_Smart_Class *class = NULL;
793    Evas_Smart_Class *esc = (Evas_Smart_Class *)&_sc;
794
795    if (class) return class;
796
797    _elm_spinner_smart_set(&_sc);
798    esc->callbacks = _smart_callbacks;
799    class = &_sc;
800
801    return class;
802 }
803
804 EAPI Evas_Object *
805 elm_spinner_add(Evas_Object *parent)
806 {
807    Evas_Object *obj;
808
809    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
810
811    obj = elm_widget_add(_elm_spinner_smart_class_new(), parent);
812    if (!obj) return NULL;
813
814    if (!elm_widget_sub_object_add(parent, obj))
815      ERR("could not add %p as sub object of %p", obj, parent);
816
817    return obj;
818 }
819
820 EAPI void
821 elm_spinner_label_format_set(Evas_Object *obj,
822                              const char *fmt)
823 {
824    ELM_SPINNER_CHECK(obj);
825    ELM_SPINNER_DATA_GET(obj, sd);
826
827    eina_stringshare_replace(&sd->label, fmt);
828    _label_write(obj);
829    elm_layout_sizing_eval(obj);
830 }
831
832 EAPI const char *
833 elm_spinner_label_format_get(const Evas_Object *obj)
834 {
835    ELM_SPINNER_CHECK(obj) NULL;
836    ELM_SPINNER_DATA_GET(obj, sd);
837
838    return sd->label;
839 }
840
841 EAPI void
842 elm_spinner_min_max_set(Evas_Object *obj,
843                         double min,
844                         double max)
845 {
846    ELM_SPINNER_CHECK(obj);
847    ELM_SPINNER_DATA_GET(obj, sd);
848
849    if ((sd->val_min == min) && (sd->val_max == max)) return;
850    sd->val_min = min;
851    sd->val_max = max;
852    if (sd->val < sd->val_min) sd->val = sd->val_min;
853    if (sd->val > sd->val_max) sd->val = sd->val_max;
854    _val_set(obj);
855    _label_write(obj);
856 }
857
858 EAPI void
859 elm_spinner_min_max_get(const Evas_Object *obj,
860                         double *min,
861                         double *max)
862 {
863    if (min) *min = 0.0;
864    if (max) *max = 0.0;
865
866    ELM_SPINNER_CHECK(obj);
867    ELM_SPINNER_DATA_GET(obj, sd);
868
869    if (min) *min = sd->val_min;
870    if (max) *max = sd->val_max;
871 }
872
873 EAPI void
874 elm_spinner_step_set(Evas_Object *obj,
875                      double step)
876 {
877    ELM_SPINNER_CHECK(obj);
878    ELM_SPINNER_DATA_GET(obj, sd);
879
880    sd->step = step;
881 }
882
883 EAPI double
884 elm_spinner_step_get(const Evas_Object *obj)
885 {
886    ELM_SPINNER_CHECK(obj) 0.0;
887    ELM_SPINNER_DATA_GET(obj, sd);
888
889    return sd->step;
890 }
891
892 EAPI void
893 elm_spinner_value_set(Evas_Object *obj,
894                       double val)
895 {
896    ELM_SPINNER_CHECK(obj);
897    ELM_SPINNER_DATA_GET(obj, sd);
898
899    if (sd->val == val) return;
900    sd->val = val;
901    if (sd->val < sd->val_min) sd->val = sd->val_min;
902    if (sd->val > sd->val_max) sd->val = sd->val_max;
903    _val_set(obj);
904    _label_write(obj);
905 }
906
907 EAPI double
908 elm_spinner_value_get(const Evas_Object *obj)
909 {
910    ELM_SPINNER_CHECK(obj) 0.0;
911    ELM_SPINNER_DATA_GET(obj, sd);
912
913    return sd->val;
914 }
915
916 EAPI void
917 elm_spinner_wrap_set(Evas_Object *obj,
918                      Eina_Bool wrap)
919 {
920    ELM_SPINNER_CHECK(obj);
921    ELM_SPINNER_DATA_GET(obj, sd);
922
923    sd->wrap = wrap;
924 }
925
926 EAPI Eina_Bool
927 elm_spinner_wrap_get(const Evas_Object *obj)
928 {
929    ELM_SPINNER_CHECK(obj) EINA_FALSE;
930    ELM_SPINNER_DATA_GET(obj, sd);
931
932    return sd->wrap;
933 }
934
935 EAPI void
936 elm_spinner_special_value_add(Evas_Object *obj,
937                               double value,
938                               const char *label)
939 {
940    Elm_Spinner_Special_Value *sv;
941    Eina_List *l;
942
943    ELM_SPINNER_CHECK(obj);
944    ELM_SPINNER_DATA_GET(obj, sd);
945
946    EINA_LIST_FOREACH(sd->special_values, l, sv)
947      {
948         if (sv->value != value)
949           continue;
950
951         eina_stringshare_replace(&sv->label, label);
952         _label_write(obj);
953         return;
954      }
955
956    sv = calloc(1, sizeof(*sv));
957    if (!sv) return;
958    sv->value = value;
959    sv->label = eina_stringshare_add(label);
960
961    sd->special_values = eina_list_append(sd->special_values, sv);
962    _label_write(obj);
963 }
964
965 EAPI void
966 elm_spinner_special_value_del(Evas_Object *obj,
967                               double value)
968 {
969    Elm_Spinner_Special_Value *sv;
970    Eina_List *l;
971
972    ELM_SPINNER_CHECK(obj);
973    ELM_SPINNER_DATA_GET(obj, sd);
974
975    EINA_LIST_FOREACH(sd->special_values, l, sv)
976      {
977         if (sv->value != value)
978           continue;
979
980         sd->special_values = eina_list_remove_list(sd->special_values, l);
981         eina_stringshare_del(sv->label);
982         free(sv);
983         _label_write(obj);
984         return;
985      }
986 }
987
988 EAPI const char *
989 elm_spinner_special_value_get(Evas_Object *obj,
990                               double value)
991 {
992    Elm_Spinner_Special_Value *sv;
993    Eina_List *l;
994
995    ELM_SPINNER_CHECK(obj) NULL;
996    ELM_SPINNER_DATA_GET(obj, sd);
997
998    EINA_LIST_FOREACH(sd->special_values, l, sv)
999      {
1000         if (sv->value == value)
1001           return sv->label;
1002      }
1003
1004    return NULL;
1005 }
1006
1007 EAPI void
1008 elm_spinner_editable_set(Evas_Object *obj,
1009                          Eina_Bool editable)
1010 {
1011    ELM_SPINNER_CHECK(obj);
1012    ELM_SPINNER_DATA_GET(obj, sd);
1013
1014    sd->editable = editable;
1015 }
1016
1017 EAPI Eina_Bool
1018 elm_spinner_editable_get(const Evas_Object *obj)
1019 {
1020    ELM_SPINNER_CHECK(obj) EINA_FALSE;
1021    ELM_SPINNER_DATA_GET(obj, sd);
1022
1023    return sd->editable;
1024 }
1025
1026 EAPI void
1027 elm_spinner_interval_set(Evas_Object *obj,
1028                          double interval)
1029 {
1030    ELM_SPINNER_CHECK(obj);
1031    ELM_SPINNER_DATA_GET(obj, sd);
1032
1033    sd->first_interval = interval;
1034 }
1035
1036 EAPI double
1037 elm_spinner_interval_get(const Evas_Object *obj)
1038 {
1039    ELM_SPINNER_CHECK(obj) 0.0;
1040    ELM_SPINNER_DATA_GET(obj, sd);
1041
1042    return sd->first_interval;
1043 }
1044
1045 EAPI void
1046 elm_spinner_base_set(Evas_Object *obj,
1047                      double base)
1048 {
1049    ELM_SPINNER_CHECK(obj);
1050    ELM_SPINNER_DATA_GET(obj, sd);
1051
1052    sd->val_base = base;
1053 }
1054
1055 EAPI double
1056 elm_spinner_base_get(const Evas_Object *obj)
1057 {
1058    ELM_SPINNER_CHECK(obj) 0.0;
1059    ELM_SPINNER_DATA_GET(obj, sd);
1060
1061    return sd->val_base;
1062 }
1063
1064 EAPI void
1065 elm_spinner_round_set(Evas_Object *obj,
1066                       int rnd)
1067 {
1068    ELM_SPINNER_CHECK(obj);
1069    ELM_SPINNER_DATA_GET(obj, sd);
1070
1071    sd->round = rnd;
1072 }
1073
1074 EAPI int
1075 elm_spinner_round_get(const Evas_Object *obj)
1076 {
1077    ELM_SPINNER_CHECK(obj) 0;
1078    ELM_SPINNER_DATA_GET(obj, sd);
1079
1080    return sd->round;
1081 }