[elm_spinner] elm_spinner_value_set API now call "changed" callback function.
[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      }
272 }
273
274 static void
275 _entry_show_cb(void *data,
276                Evas *e __UNUSED__,
277                Evas_Object *obj,
278                void *event_info __UNUSED__)
279 {
280    ELM_SPINNER_DATA_GET(data, sd);
281
282    _entry_show(sd);
283    elm_object_focus_set(obj, EINA_TRUE);
284    elm_entry_select_all(obj);
285    sd->entry_visible = EINA_TRUE;
286 }
287
288 static Eina_Bool
289 _spin_value(void *data)
290 {
291    ELM_SPINNER_DATA_GET(data, sd);
292    double real_speed = sd->spin_speed;
293
294    /* Sanity check: our step size should be at least as large as our rounding value */
295    if ((sd->spin_speed != 0.0) && (abs(sd->spin_speed) < sd->round))
296      {
297         WRN("The spinning step is smaller than the rounding value, please check your code");
298         real_speed = sd->spin_speed > 0 ? sd->round : -sd->round;
299      }
300
301    if (_value_set(data, sd->val + real_speed)) _label_write(data);
302    sd->interval = sd->interval / 1.05;
303    ecore_timer_interval_set(sd->spin, sd->interval);
304
305    return ECORE_CALLBACK_RENEW;
306 }
307
308 static void
309 _val_inc_start(Evas_Object *obj)
310 {
311    ELM_SPINNER_DATA_GET(obj, sd);
312
313    sd->interval = sd->first_interval;
314    sd->spin_speed = sd->step;
315    if (sd->spin) ecore_timer_del(sd->spin);
316    sd->spin = ecore_timer_add(sd->interval, _spin_value, obj);
317    _spin_value(obj);
318 }
319
320 static void
321 _val_inc_stop(Evas_Object *obj)
322 {
323    ELM_SPINNER_DATA_GET(obj, sd);
324
325    sd->interval = sd->first_interval;
326    sd->spin_speed = 0;
327    if (sd->spin) ecore_timer_del(sd->spin);
328    sd->spin = NULL;
329 }
330
331 static void
332 _val_dec_start(Evas_Object *obj)
333 {
334    ELM_SPINNER_DATA_GET(obj, sd);
335
336    sd->interval = sd->first_interval;
337    sd->spin_speed = -sd->step;
338    if (sd->spin) ecore_timer_del(sd->spin);
339    sd->spin = ecore_timer_add(sd->interval, _spin_value, obj);
340    _spin_value(obj);
341 }
342
343 static void
344 _val_dec_stop(Evas_Object *obj)
345 {
346    ELM_SPINNER_DATA_GET(obj, sd);
347
348    sd->interval = sd->first_interval;
349    sd->spin_speed = 0;
350    if (sd->spin) ecore_timer_del(sd->spin);
351    sd->spin = NULL;
352 }
353
354 static void
355 _button_inc_start_cb(void *data,
356                      Evas_Object *obj __UNUSED__,
357                      const char *emission __UNUSED__,
358                      const char *source __UNUSED__)
359 {
360    ELM_SPINNER_DATA_GET(data, sd);
361
362    if (sd->entry_visible)
363      {
364         _reset_value(data);
365         return;
366      }
367    _val_inc_start(data);
368 }
369
370 static void
371 _button_inc_stop_cb(void *data,
372                     Evas_Object *obj __UNUSED__,
373                     const char *emission __UNUSED__,
374                     const char *source __UNUSED__)
375 {
376    _val_inc_stop(data);
377 }
378
379 static void
380 _button_dec_start_cb(void *data,
381                      Evas_Object *obj __UNUSED__,
382                      const char *emission __UNUSED__,
383                      const char *source __UNUSED__)
384 {
385    ELM_SPINNER_DATA_GET(data, sd);
386
387    if (sd->entry_visible)
388      {
389         _reset_value(data);
390         return;
391      }
392    _val_dec_start(data);
393 }
394
395 static void
396 _button_dec_stop_cb(void *data,
397                     Evas_Object *obj __UNUSED__,
398                     const char *emission __UNUSED__,
399                     const char *source __UNUSED__)
400 {
401    _val_dec_stop(data);
402 }
403
404 static void
405 _entry_activated_cb(void *data,
406                     Evas_Object *obj __UNUSED__,
407                     void *event_info __UNUSED__)
408 {
409    ELM_SPINNER_DATA_GET(data, sd);
410
411    _entry_value_apply(data);
412    evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
413    if (sd->delay) ecore_timer_del(sd->delay);
414    sd->delay = ecore_timer_add(0.2, _delay_change, data);
415 }
416
417 static void
418 _elm_spinner_smart_sizing_eval(Evas_Object *obj)
419 {
420    Evas_Coord minw = -1, minh = -1;
421
422    ELM_SPINNER_DATA_GET(obj, sd);
423
424    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
425    edje_object_size_min_restricted_calc
426      (ELM_WIDGET_DATA(sd)->resize_obj, &minw, &minh, minw, minh);
427    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
428    evas_object_size_hint_min_set(obj, minw, minh);
429    evas_object_size_hint_max_set(obj, -1, -1);
430 }
431
432 static Eina_Bool
433 _elm_spinner_smart_event(Evas_Object *obj,
434                          Evas_Object *src __UNUSED__,
435                          Evas_Callback_Type type,
436                          void *event_info)
437 {
438    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
439    if (type == EVAS_CALLBACK_KEY_DOWN)
440      {
441         Evas_Event_Key_Down *ev = event_info;
442
443         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
444         else if (!strcmp(ev->keyname, "Left") ||
445                  ((!strcmp(ev->keyname, "KP_Left")) && (!ev->string)) ||
446                  !strcmp(ev->keyname, "Down") ||
447                  ((!strcmp(ev->keyname, "KP_Down")) && (!ev->string)))
448           {
449              _val_dec_start(obj);
450              elm_layout_signal_emit(obj, "elm,left,anim,activate", "elm");
451              ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
452              return EINA_TRUE;
453           }
454         else if (!strcmp(ev->keyname, "Right") ||
455                  ((!strcmp(ev->keyname, "KP_Right")) && (!ev->string)) ||
456                  !strcmp(ev->keyname, "Up") ||
457                  ((!strcmp(ev->keyname, "KP_Up")) && (!ev->string)))
458           {
459              _val_inc_start(obj);
460              elm_layout_signal_emit(obj, "elm,right,anim,activate", "elm");
461              ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
462              return EINA_TRUE;
463           }
464      }
465    else if (type == EVAS_CALLBACK_KEY_UP)
466      {
467         Evas_Event_Key_Down *ev = event_info;
468
469         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
470         if (!strcmp(ev->keyname, "Right") ||
471             ((!strcmp(ev->keyname, "KP_Right")) && (!ev->string)) ||
472             !strcmp(ev->keyname, "Up") ||
473             ((!strcmp(ev->keyname, "KP_Up")) && (!ev->string)))
474           _val_inc_stop(obj);
475         else if (!strcmp(ev->keyname, "Left") ||
476                  ((!strcmp(ev->keyname, "KP_Left")) && (!ev->string)) ||
477                  !strcmp(ev->keyname, "Down") ||
478                  ((!strcmp(ev->keyname, "KP_Down")) && (!ev->string)))
479           _val_dec_stop(obj);
480         else return EINA_FALSE;
481
482         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
483
484         return EINA_TRUE;
485      }
486
487    return EINA_FALSE;
488 }
489
490 static Eina_Bool
491 _elm_spinner_smart_on_focus(Evas_Object *obj)
492 {
493    if (!ELM_WIDGET_CLASS(_elm_spinner_parent_sc)->on_focus(obj))
494      return EINA_FALSE;
495
496    if (!elm_widget_focus_get(obj))
497      _entry_value_apply(obj);
498
499    return EINA_TRUE;
500 }
501
502 static char *
503 _access_info_cb(void *data, Evas_Object *obj)
504 {
505    Evas_Object *spinner;
506    const char *txt = elm_widget_access_info_get(obj);
507
508    spinner = ELM_WIDGET_DATA(data)->obj;
509    if (!txt) txt = elm_layout_text_get(spinner, "elm.text");
510    if (txt) return strdup(txt);
511
512    return NULL;
513 }
514
515 static char *
516 _access_state_cb(void *data, Evas_Object *obj __UNUSED__)
517 {
518    if (elm_widget_disabled_get(ELM_WIDGET_DATA(data)->obj))
519      return strdup(E_("State: Disabled"));
520
521    return NULL;
522 }
523
524 static void
525 _access_activate_cb(void *data,
526                     Evas_Object *part_obj,
527                     Elm_Object_Item *item __UNUSED__)
528 {
529    char *text;
530    Eina_Strbuf *buf;
531    Evas_Object *eo, *inc_btn;
532    const char* increment_part;
533
534    if (!strcmp(elm_widget_style_get(data), "vertical"))
535      increment_part = "up_bt";
536    else
537      increment_part = "right_bt";
538
539    eo = elm_layout_edje_get(data);
540    inc_btn = (Evas_Object *)edje_object_part_object_get(eo, increment_part);
541
542    if (part_obj != inc_btn)
543      {
544         _val_dec_start(data);
545         elm_layout_signal_emit(data, "elm,left,anim,activate", "elm");
546         _val_dec_stop(data);
547         text = "decremented";
548      }
549    else
550      {
551         _val_inc_start(data);
552         elm_layout_signal_emit(data, "elm,right,anim,activate", "elm");
553         _val_inc_stop(data);
554         text = "incremented";
555      }
556
557    buf = eina_strbuf_new();
558
559    eina_strbuf_append_printf(buf, "%s, %s", text,
560             elm_layout_text_get(data, "elm.text"));
561
562    text = eina_strbuf_string_steal(buf);
563    eina_strbuf_free(buf);
564
565    _elm_access_say(text);
566 }
567
568 static void
569 _access_spinner_register(Evas_Object *obj, Eina_Bool is_access)
570 {
571    Evas_Object *ao;
572    Elm_Access_Info *ai;
573    const char* increment_part;
574    const char* decrement_part;
575
576    if (!strcmp(elm_widget_style_get(obj), "vertical"))
577      {
578         increment_part = "up_bt";
579         decrement_part = "down_bt";
580      }
581    else
582      {
583         increment_part = "right_bt";
584         decrement_part = "left_bt";
585      }
586
587    if (!is_access)
588      {
589         /* unregister increment button, decrement button and spinner label */
590         _elm_access_edje_object_part_object_unregister
591           (obj, elm_layout_edje_get(obj), increment_part);
592
593         _elm_access_edje_object_part_object_unregister
594           (obj, elm_layout_edje_get(obj), decrement_part);
595
596         _elm_access_edje_object_part_object_unregister
597           (obj, elm_layout_edje_get(obj), "access_text");
598
599         return;
600      }
601
602    /* register increment button */
603    ao = _elm_access_edje_object_part_object_register
604           (obj, elm_layout_edje_get(obj), increment_part);
605
606    ai = _elm_access_object_get(ao);
607    _elm_access_text_set(ai, ELM_ACCESS_TYPE,
608                         E_("spinner increment button"));
609    _elm_access_activate_callback_set(ai, _access_activate_cb, obj);
610
611    /* register decrement button */
612    ao = _elm_access_edje_object_part_object_register
613           (obj, elm_layout_edje_get(obj), decrement_part);
614
615    ai = _elm_access_object_get(ao);
616    _elm_access_text_set(ai, ELM_ACCESS_TYPE,
617                         E_("spinner decrement button"));
618    _elm_access_activate_callback_set(ai, _access_activate_cb, obj);
619
620    /* register spinner label */
621    ao = _elm_access_edje_object_part_object_register
622           (obj, elm_layout_edje_get(obj), "access_text");
623
624    ai = _elm_access_object_get(ao);
625    _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("spinner"));
626    _elm_access_callback_set(ai, ELM_ACCESS_INFO, _access_info_cb, obj);
627    _elm_access_callback_set(ai, ELM_ACCESS_STATE, _access_state_cb, obj);
628 }
629
630 static void
631 _elm_spinner_smart_add(Evas_Object *obj)
632 {
633    EVAS_SMART_DATA_ALLOC(obj, Elm_Spinner_Smart_Data);
634
635    ELM_WIDGET_CLASS(_elm_spinner_parent_sc)->base.add(obj);
636
637    priv->val = 0.0;
638    priv->val_min = 0.0;
639    priv->val_max = 100.0;
640    priv->wrap = 0;
641    priv->step = 1.0;
642    priv->first_interval = 0.85;
643    priv->entry_visible = EINA_FALSE;
644    priv->editable = EINA_TRUE;
645
646    elm_layout_theme_set(obj, "spinner", "base", elm_widget_style_get(obj));
647    elm_layout_signal_callback_add(obj, "drag", "*", _drag_cb, obj);
648    elm_layout_signal_callback_add(obj, "drag,start", "*", _drag_start_cb, obj);
649    elm_layout_signal_callback_add(obj, "drag,stop", "*", _drag_stop_cb, obj);
650    elm_layout_signal_callback_add(obj, "drag,step", "*", _drag_stop_cb, obj);
651    elm_layout_signal_callback_add(obj, "drag,page", "*", _drag_stop_cb, obj);
652
653    elm_layout_signal_callback_add
654      (obj, "elm,action,increment,start", "*", _button_inc_start_cb, obj);
655    elm_layout_signal_callback_add
656      (obj, "elm,action,increment,stop", "*", _button_inc_stop_cb, obj);
657    elm_layout_signal_callback_add
658      (obj, "elm,action,decrement,start", "*", _button_dec_start_cb, obj);
659    elm_layout_signal_callback_add
660      (obj, "elm,action,decrement,stop", "*", _button_dec_stop_cb, obj);
661
662    edje_object_part_drag_value_set
663      (ELM_WIDGET_DATA(priv)->resize_obj, "elm.dragable.slider", 0.0, 0.0);
664
665    priv->ent = elm_entry_add(obj);
666    elm_entry_single_line_set(priv->ent, EINA_TRUE);
667    evas_object_smart_callback_add
668      (priv->ent, "activated", _entry_activated_cb, obj);
669
670    elm_layout_content_set(obj, "elm.swallow.entry", priv->ent);
671    elm_layout_signal_callback_add
672      (obj, "elm,action,entry,toggle", "*", _entry_toggle_cb, obj);
673    evas_object_event_callback_add
674      (priv->ent, EVAS_CALLBACK_SHOW, _entry_show_cb, obj);
675
676    _label_write(obj);
677    elm_widget_can_focus_set(obj, EINA_TRUE);
678
679    elm_layout_sizing_eval(obj);
680
681    /* access */
682    if (_elm_config->access_mode)
683      _access_spinner_register(obj, EINA_TRUE);
684 }
685
686 static void
687 _elm_spinner_smart_del(Evas_Object *obj)
688 {
689    Elm_Spinner_Special_Value *sv;
690
691    ELM_SPINNER_DATA_GET(obj, sd);
692
693    if (sd->label) eina_stringshare_del(sd->label);
694    if (sd->delay) ecore_timer_del(sd->delay);
695    if (sd->spin) ecore_timer_del(sd->spin);
696    if (sd->special_values)
697      {
698         EINA_LIST_FREE (sd->special_values, sv)
699           {
700              eina_stringshare_del(sv->label);
701              free(sv);
702           }
703      }
704
705    ELM_WIDGET_CLASS(_elm_spinner_parent_sc)->base.del(obj);
706 }
707
708 static Eina_Bool
709 _elm_spinner_smart_theme(Evas_Object *obj)
710 {
711    Eina_Bool ret;
712    ret = elm_layout_theme_set(obj, "spinner", "base",
713                               elm_widget_style_get(obj));
714
715    if (_elm_config->access_mode)
716      _access_spinner_register(obj, EINA_TRUE);
717
718    return ret;
719 }
720
721 static Evas_Object *
722 _access_object_get(const Evas_Object *obj, const char* part)
723 {
724    Evas_Object *eo, *po, *ao;
725
726    eo = elm_layout_edje_get(obj);
727
728    po = (Evas_Object *)edje_object_part_object_get(eo, part);
729    ao = evas_object_data_get(po, "_part_access_obj");
730
731    return ao;
732 }
733
734 static Eina_Bool
735 _elm_spinner_smart_focus_next(const Evas_Object *obj,
736                            Elm_Focus_Direction dir,
737                            Evas_Object **next)
738 {
739    Evas_Object *ao;
740    Eina_List *items = NULL;
741    const char* increment_part;
742    const char* decrement_part;
743
744    ELM_SPINNER_CHECK(obj) EINA_FALSE;
745
746    if (!strcmp(elm_widget_style_get(obj), "vertical"))
747      {
748         increment_part = "up_bt";
749         decrement_part = "down_bt";
750      }
751    else
752      {
753         increment_part = "right_bt";
754         decrement_part = "left_bt";
755      }
756
757    ao = _access_object_get(obj, "access_text");
758    items = eina_list_append(items, ao);
759
760    ao = _access_object_get(obj, decrement_part);
761    items = eina_list_append(items, ao);
762
763    ao = _access_object_get(obj, increment_part);
764    items = eina_list_append(items, ao);
765
766    return elm_widget_focus_list_next_get
767             (obj, items, eina_list_data_get, dir, next);
768 }
769
770 static void
771 _access_hook(Evas_Object *obj, Eina_Bool is_access)
772 {
773    ELM_SPINNER_CHECK(obj);
774    ELM_SPINNER_DATA_GET(obj, sd);
775
776    if (is_access)
777      ELM_WIDGET_CLASS(ELM_WIDGET_DATA(sd)->api)->focus_next =
778        _elm_spinner_smart_focus_next;
779    else
780      ELM_WIDGET_CLASS(ELM_WIDGET_DATA(sd)->api)->focus_next = NULL;
781
782    _access_spinner_register(obj, is_access);
783 }
784
785 static void
786 _elm_spinner_smart_set_user(Elm_Spinner_Smart_Class *sc)
787 {
788    ELM_WIDGET_CLASS(sc)->base.add = _elm_spinner_smart_add;
789    ELM_WIDGET_CLASS(sc)->base.del = _elm_spinner_smart_del;
790
791    /* not a 'focus chain manager' */
792    ELM_WIDGET_CLASS(sc)->focus_next = NULL;
793    ELM_WIDGET_CLASS(sc)->focus_direction = NULL;
794
795    ELM_WIDGET_CLASS(sc)->on_focus = _elm_spinner_smart_on_focus;
796    ELM_WIDGET_CLASS(sc)->event = _elm_spinner_smart_event;
797
798    ELM_LAYOUT_CLASS(sc)->sizing_eval = _elm_spinner_smart_sizing_eval;
799
800    ELM_WIDGET_CLASS(sc)->theme = _elm_spinner_smart_theme;
801    ELM_WIDGET_CLASS(sc)->translate = _elm_spinner_smart_translate;
802
803    /* access */
804    if (_elm_config->access_mode)
805      ELM_WIDGET_CLASS(sc)->focus_next = _elm_spinner_smart_focus_next;
806
807    ELM_WIDGET_CLASS(sc)->access = _access_hook;
808 }
809
810 EAPI const Elm_Spinner_Smart_Class *
811 elm_spinner_smart_class_get(void)
812 {
813    static Elm_Spinner_Smart_Class _sc =
814      ELM_SPINNER_SMART_CLASS_INIT_NAME_VERSION(ELM_SPINNER_SMART_NAME);
815    static const Elm_Spinner_Smart_Class *class = NULL;
816    Evas_Smart_Class *esc = (Evas_Smart_Class *)&_sc;
817
818    if (class) return class;
819
820    _elm_spinner_smart_set(&_sc);
821    esc->callbacks = _smart_callbacks;
822    class = &_sc;
823
824    return class;
825 }
826
827 EAPI Evas_Object *
828 elm_spinner_add(Evas_Object *parent)
829 {
830    Evas_Object *obj;
831
832    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
833
834    obj = elm_widget_add(_elm_spinner_smart_class_new(), parent);
835    if (!obj) return NULL;
836
837    if (!elm_widget_sub_object_add(parent, obj))
838      ERR("could not add %p as sub object of %p", obj, parent);
839
840    return obj;
841 }
842
843 EAPI void
844 elm_spinner_label_format_set(Evas_Object *obj,
845                              const char *fmt)
846 {
847    ELM_SPINNER_CHECK(obj);
848    ELM_SPINNER_DATA_GET(obj, sd);
849
850    eina_stringshare_replace(&sd->label, fmt);
851    _label_write(obj);
852    elm_layout_sizing_eval(obj);
853 }
854
855 EAPI const char *
856 elm_spinner_label_format_get(const Evas_Object *obj)
857 {
858    ELM_SPINNER_CHECK(obj) NULL;
859    ELM_SPINNER_DATA_GET(obj, sd);
860
861    return sd->label;
862 }
863
864 EAPI void
865 elm_spinner_min_max_set(Evas_Object *obj,
866                         double min,
867                         double max)
868 {
869    ELM_SPINNER_CHECK(obj);
870    ELM_SPINNER_DATA_GET(obj, sd);
871
872    if ((sd->val_min == min) && (sd->val_max == max)) return;
873    sd->val_min = min;
874    sd->val_max = max;
875    if (sd->val < sd->val_min) sd->val = sd->val_min;
876    if (sd->val > sd->val_max) sd->val = sd->val_max;
877    _val_set(obj);
878    _label_write(obj);
879 }
880
881 EAPI void
882 elm_spinner_min_max_get(const Evas_Object *obj,
883                         double *min,
884                         double *max)
885 {
886    if (min) *min = 0.0;
887    if (max) *max = 0.0;
888
889    ELM_SPINNER_CHECK(obj);
890    ELM_SPINNER_DATA_GET(obj, sd);
891
892    if (min) *min = sd->val_min;
893    if (max) *max = sd->val_max;
894 }
895
896 EAPI void
897 elm_spinner_step_set(Evas_Object *obj,
898                      double step)
899 {
900    ELM_SPINNER_CHECK(obj);
901    ELM_SPINNER_DATA_GET(obj, sd);
902
903    sd->step = step;
904 }
905
906 EAPI double
907 elm_spinner_step_get(const Evas_Object *obj)
908 {
909    ELM_SPINNER_CHECK(obj) 0.0;
910    ELM_SPINNER_DATA_GET(obj, sd);
911
912    return sd->step;
913 }
914
915 EAPI void
916 elm_spinner_value_set(Evas_Object *obj,
917                       double val)
918 {
919    ELM_SPINNER_CHECK(obj);
920    ELM_SPINNER_DATA_GET(obj, sd);
921
922    if (sd->val == val) return;
923    sd->val = val;
924    if (sd->val < sd->val_min) sd->val = sd->val_min;
925    if (sd->val > sd->val_max) sd->val = sd->val_max;
926    _val_set(obj);
927    _label_write(obj);
928    evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
929 }
930
931 EAPI double
932 elm_spinner_value_get(const Evas_Object *obj)
933 {
934    ELM_SPINNER_CHECK(obj) 0.0;
935    ELM_SPINNER_DATA_GET(obj, sd);
936
937    return sd->val;
938 }
939
940 EAPI void
941 elm_spinner_wrap_set(Evas_Object *obj,
942                      Eina_Bool wrap)
943 {
944    ELM_SPINNER_CHECK(obj);
945    ELM_SPINNER_DATA_GET(obj, sd);
946
947    sd->wrap = wrap;
948 }
949
950 EAPI Eina_Bool
951 elm_spinner_wrap_get(const Evas_Object *obj)
952 {
953    ELM_SPINNER_CHECK(obj) EINA_FALSE;
954    ELM_SPINNER_DATA_GET(obj, sd);
955
956    return sd->wrap;
957 }
958
959 EAPI void
960 elm_spinner_special_value_add(Evas_Object *obj,
961                               double value,
962                               const char *label)
963 {
964    Elm_Spinner_Special_Value *sv;
965    Eina_List *l;
966
967    ELM_SPINNER_CHECK(obj);
968    ELM_SPINNER_DATA_GET(obj, sd);
969
970    EINA_LIST_FOREACH(sd->special_values, l, sv)
971      {
972         if (sv->value != value)
973           continue;
974
975         eina_stringshare_replace(&sv->label, label);
976         _label_write(obj);
977         return;
978      }
979
980    sv = calloc(1, sizeof(*sv));
981    if (!sv) return;
982    sv->value = value;
983    sv->label = eina_stringshare_add(label);
984
985    sd->special_values = eina_list_append(sd->special_values, sv);
986    _label_write(obj);
987 }
988
989 EAPI void
990 elm_spinner_special_value_del(Evas_Object *obj,
991                               double value)
992 {
993    Elm_Spinner_Special_Value *sv;
994    Eina_List *l;
995
996    ELM_SPINNER_CHECK(obj);
997    ELM_SPINNER_DATA_GET(obj, sd);
998
999    EINA_LIST_FOREACH(sd->special_values, l, sv)
1000      {
1001         if (sv->value != value)
1002           continue;
1003
1004         sd->special_values = eina_list_remove_list(sd->special_values, l);
1005         eina_stringshare_del(sv->label);
1006         free(sv);
1007         _label_write(obj);
1008         return;
1009      }
1010 }
1011
1012 EAPI const char *
1013 elm_spinner_special_value_get(Evas_Object *obj,
1014                               double value)
1015 {
1016    Elm_Spinner_Special_Value *sv;
1017    Eina_List *l;
1018
1019    ELM_SPINNER_CHECK(obj) NULL;
1020    ELM_SPINNER_DATA_GET(obj, sd);
1021
1022    EINA_LIST_FOREACH(sd->special_values, l, sv)
1023      {
1024         if (sv->value == value)
1025           return sv->label;
1026      }
1027
1028    return NULL;
1029 }
1030
1031 EAPI void
1032 elm_spinner_editable_set(Evas_Object *obj,
1033                          Eina_Bool editable)
1034 {
1035    ELM_SPINNER_CHECK(obj);
1036    ELM_SPINNER_DATA_GET(obj, sd);
1037
1038    sd->editable = editable;
1039 }
1040
1041 EAPI Eina_Bool
1042 elm_spinner_editable_get(const Evas_Object *obj)
1043 {
1044    ELM_SPINNER_CHECK(obj) EINA_FALSE;
1045    ELM_SPINNER_DATA_GET(obj, sd);
1046
1047    return sd->editable;
1048 }
1049
1050 EAPI void
1051 elm_spinner_interval_set(Evas_Object *obj,
1052                          double interval)
1053 {
1054    ELM_SPINNER_CHECK(obj);
1055    ELM_SPINNER_DATA_GET(obj, sd);
1056
1057    sd->first_interval = interval;
1058 }
1059
1060 EAPI double
1061 elm_spinner_interval_get(const Evas_Object *obj)
1062 {
1063    ELM_SPINNER_CHECK(obj) 0.0;
1064    ELM_SPINNER_DATA_GET(obj, sd);
1065
1066    return sd->first_interval;
1067 }
1068
1069 EAPI void
1070 elm_spinner_base_set(Evas_Object *obj,
1071                      double base)
1072 {
1073    ELM_SPINNER_CHECK(obj);
1074    ELM_SPINNER_DATA_GET(obj, sd);
1075
1076    sd->val_base = base;
1077 }
1078
1079 EAPI double
1080 elm_spinner_base_get(const Evas_Object *obj)
1081 {
1082    ELM_SPINNER_CHECK(obj) 0.0;
1083    ELM_SPINNER_DATA_GET(obj, sd);
1084
1085    return sd->val_base;
1086 }
1087
1088 EAPI void
1089 elm_spinner_round_set(Evas_Object *obj,
1090                       int rnd)
1091 {
1092    ELM_SPINNER_CHECK(obj);
1093    ELM_SPINNER_DATA_GET(obj, sd);
1094
1095    sd->round = rnd;
1096 }
1097
1098 EAPI int
1099 elm_spinner_round_get(const Evas_Object *obj)
1100 {
1101    ELM_SPINNER_CHECK(obj) 0;
1102    ELM_SPINNER_DATA_GET(obj, sd);
1103
1104    return sd->round;
1105 }