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