Spinner: Fix can't input number in case of min value is bigger than 1.
[platform/upstream/elementary.git] / src / lib / elm_spinner.c
1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4
5 #define ELM_INTERFACE_ATSPI_ACCESSIBLE_PROTECTED
6 #define ELM_INTERFACE_ATSPI_VALUE_PROTECTED
7 #define ELM_INTERFACE_ATSPI_WIDGET_ACTION_PROTECTED
8 #define ELM_INTERFACE_ATSPI_COMPONENT_PROTECTED
9
10 #include <Elementary.h>
11 #include <ctype.h>
12
13 #include "elm_priv.h"
14 #include "elm_widget_spinner.h"
15
16 #include "Eo.h"
17
18 #define MY_CLASS ELM_SPINNER_CLASS
19
20 #define MY_CLASS_NAME "Elm_Spinner"
21 #define MY_CLASS_NAME_LEGACY "elm_spinner"
22
23 #define ELM_SPINNER_DELAY_CHANGE_TIME 0.2
24
25 static const char SIG_CHANGED[] = "changed";
26 static const char SIG_DRAG_START[] = "spinner,drag,start";
27 static const char SIG_DRAG_STOP[] = "spinner,drag,stop";
28 static const char SIG_DELAY_CHANGED[] = "delay,changed";
29 static const char SIG_MIN_REACHED[] = "min,reached";
30 static const char SIG_MAX_REACHED[] = "max,reached";
31 //TIZEN_ONLY(20160419): Spinner entry changed callback support for datetime UX.
32 static const char SIG_ENTRY_CHANGED[] = "entry,changed";
33 //
34
35 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
36    {SIG_CHANGED, ""},
37    {SIG_DELAY_CHANGED, ""},
38    {SIG_DRAG_START, ""},
39    {SIG_DRAG_STOP, ""},
40    {SIG_MIN_REACHED, ""},
41    {SIG_MAX_REACHED, ""},
42    //TIZEN_ONLY(20160419): Spinner entry changed callback support for datetime UX.
43    {SIG_ENTRY_CHANGED, ""},
44    //
45    {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */
46    {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */
47    {SIG_LAYOUT_FOCUSED, ""}, /**< handled by elm_layout */
48    {SIG_LAYOUT_UNFOCUSED, ""}, /**< handled by elm_layout */
49    {NULL, NULL}
50 };
51
52 static Eina_Bool _key_action_toggle(Evas_Object *obj, const char *params);
53
54 static Eina_Bool
55 _inc_dec_button_clicked_cb(void *data, Eo *obj EINA_UNUSED,
56                        const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED);
57 static Eina_Bool
58 _inc_dec_button_pressed_cb(void *data, Eo *obj EINA_UNUSED,
59                        const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED);
60 static Eina_Bool
61 _inc_dec_button_unpressed_cb(void *data, Eo *obj EINA_UNUSED,
62                        const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED);
63 static Eina_Bool
64 _inc_dec_button_mouse_move_cb(void *data, Eo *obj EINA_UNUSED,
65                        const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED);
66 static Eina_Bool
67 _text_button_focused_cb(void *data, Eo *obj EINA_UNUSED,
68                         const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED);
69 static Eina_Bool
70 _entry_unfocused_cb(void *data, Eo *obj EINA_UNUSED,
71                     const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED);
72 static Eina_Bool
73 _entry_activated_cb(void *data, Eo *obj EINA_UNUSED,
74                     const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED);
75
76 EO_CALLBACKS_ARRAY_DEFINE(_inc_dec_button_cb,
77    { EVAS_CLICKABLE_INTERFACE_EVENT_CLICKED, _inc_dec_button_clicked_cb},
78    { EVAS_CLICKABLE_INTERFACE_EVENT_PRESSED, _inc_dec_button_pressed_cb},
79    { EVAS_CLICKABLE_INTERFACE_EVENT_UNPRESSED, _inc_dec_button_unpressed_cb},
80    { EVAS_OBJECT_EVENT_MOUSE_MOVE, _inc_dec_button_mouse_move_cb }
81 );
82
83 static void _access_increment_decrement_info_say(Evas_Object *obj,
84                                                  Eina_Bool is_incremented);
85
86 static Eina_Bool
87 _is_valid_digit(char x)
88 {
89    return ((x >= '0' && x <= '9') || (x == '.')) ? EINA_TRUE : EINA_FALSE;
90 }
91
92 static Elm_Spinner_Format_Type
93 _is_label_format_integer(const char *fmt)
94 {
95    const char *itr = NULL;
96    const char *start = NULL;
97    Eina_Bool found = EINA_FALSE;
98    Elm_Spinner_Format_Type ret_type = SPINNER_FORMAT_INVALID;
99
100    start = strchr(fmt, '%');
101    if (!start) return SPINNER_FORMAT_INVALID;
102
103    while (start)
104      {
105         if (found && start[1] != '%')
106           {
107              return SPINNER_FORMAT_INVALID;
108           }
109
110         if (start[1] != '%' && !found)
111           {
112              found = EINA_TRUE;
113              for (itr = start + 1; *itr != '\0'; itr++)
114                {
115                   if ((*itr == 'd') || (*itr == 'u') || (*itr == 'i') ||
116                       (*itr == 'o') || (*itr == 'x') || (*itr == 'X'))
117                     {
118                        ret_type = SPINNER_FORMAT_INT;
119                        break;
120                     }
121                   else if ((*itr == 'f') || (*itr == 'F'))
122                     {
123                        ret_type = SPINNER_FORMAT_FLOAT;
124                        break;
125                     }
126                   else if (_is_valid_digit(*itr))
127                     {
128                        continue;
129                     }
130                   else
131                     {
132                        return SPINNER_FORMAT_INVALID;
133                     }
134                }
135           }
136         start = strchr(start + 2, '%');
137      }
138
139    return ret_type;
140 }
141
142 static void
143 _entry_show(Elm_Spinner_Data *sd)
144 {
145    Eina_List *l;
146    Elm_Spinner_Special_Value *sv;
147    char buf[32], fmt[32] = "%0.f";
148
149    EINA_LIST_FOREACH(sd->special_values, l, sv)
150      {
151         if (sv->value == sd->val)
152           {
153              snprintf(buf, sizeof(buf), "%s", sv->label);
154              goto apply;
155           }
156      }
157    /* try to construct just the format from given label
158     * completely ignoring pre/post words
159     */
160    if (sd->label)
161      {
162         const char *start = strchr(sd->label, '%');
163         while (start)
164           {
165              /* handle %% */
166              if (start[1] != '%')
167                break;
168              else
169                start = strchr(start + 2, '%');
170           }
171
172         if (start)
173           {
174              const char *itr, *end = NULL;
175              for (itr = start + 1; *itr != '\0'; itr++)
176                {
177                   /* allowing '%d' is quite dangerous, remove it? */
178                   if ((*itr == 'd') || (*itr == 'f'))
179                     {
180                        end = itr + 1;
181                        break;
182                     }
183                }
184
185              if ((end) && ((size_t)(end - start + 1) < sizeof(fmt)))
186                {
187                   memcpy(fmt, start, end - start);
188                   fmt[end - start] = '\0';
189                }
190           }
191      }
192
193    if (_is_label_format_integer(fmt))
194      snprintf(buf, sizeof(buf), fmt, (int)sd->val);
195    else
196      snprintf(buf, sizeof(buf), fmt, sd->val);
197
198 apply:
199    elm_object_text_set(sd->ent, buf);
200 }
201
202 static void
203 _label_write(Evas_Object *obj)
204 {
205    Eina_List *l;
206    char buf[1024];
207    Elm_Spinner_Special_Value *sv;
208
209    ELM_SPINNER_DATA_GET(obj, sd);
210
211    EINA_LIST_FOREACH(sd->special_values, l, sv)
212      {
213         if (sv->value == sd->val)
214           {
215              snprintf(buf, sizeof(buf), "%s", sv->label);
216              goto apply;
217           }
218      }
219
220    if (sd->label)
221      {
222         if (_is_label_format_integer(sd->label))
223           snprintf(buf, sizeof(buf), sd->label, (int)sd->val);
224         else
225           snprintf(buf, sizeof(buf), sd->label, sd->val);
226      }
227    else
228      snprintf(buf, sizeof(buf), "%.0f", sd->val);
229
230 apply:
231    if (sd->button_layout)
232      elm_layout_text_set(sd->text_button, "elm.text", buf);
233    else
234      elm_layout_text_set(obj, "elm.text", buf);
235
236    elm_interface_atspi_accessible_name_changed_signal_emit(obj);
237    if (sd->entry_visible) _entry_show(sd);
238 }
239
240 static Eina_Bool
241 _delay_change_timer_cb(void *data)
242 {
243    ELM_SPINNER_DATA_GET(data, sd);
244
245    sd->delay_change_timer = NULL;
246    eo_do(data, eo_event_callback_call(ELM_SPINNER_EVENT_DELAY_CHANGED, NULL));
247
248    return ECORE_CALLBACK_CANCEL;
249 }
250
251 static Eina_Bool
252 _value_set(Evas_Object *obj,
253            double new_val)
254 {
255    ELM_SPINNER_DATA_GET(obj, sd);
256
257    if (sd->round > 0)
258      new_val = sd->val_base +
259        (double)((((int)(new_val - sd->val_base)) / sd->round) * sd->round);
260
261    if (sd->wrap)
262      {
263         if (new_val < sd->val_min)
264           new_val = sd->val_max;
265         else if (new_val > sd->val_max)
266           new_val = sd->val_min;
267      }
268    else
269      {
270         if (new_val < sd->val_min)
271           new_val = sd->val_min;
272         else if (new_val > sd->val_max)
273           new_val = sd->val_max;
274      }
275
276    if (new_val == sd->val) return EINA_FALSE;
277    sd->val = new_val;
278
279    if (sd->val == sd->val_min)
280      eo_do(obj, eo_event_callback_call(ELM_SPINNER_EVENT_MIN_REACHED, NULL));
281    else if (sd->val == sd->val_max)
282      eo_do(obj, eo_event_callback_call(ELM_SPINNER_EVENT_MAX_REACHED, NULL));
283
284    eo_do(obj, eo_event_callback_call(ELM_SPINNER_EVENT_CHANGED, NULL));
285    elm_interface_atspi_accessible_value_changed_signal_emit(obj);
286    ecore_timer_del(sd->delay_change_timer);
287    sd->delay_change_timer = ecore_timer_add(ELM_SPINNER_DELAY_CHANGE_TIME,
288                                             _delay_change_timer_cb, obj);
289
290    return EINA_TRUE;
291 }
292
293 static void
294 _val_set(Evas_Object *obj)
295 {
296    double pos = 0.0;
297
298    ELM_SPINNER_DATA_GET(obj, sd);
299    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
300
301    if (sd->val_max > sd->val_min)
302      pos = ((sd->val - sd->val_min) / (sd->val_max - sd->val_min));
303
304    if (pos < 0.0) pos = 0.0;
305    else if (pos > 1.0) pos = 1.0;
306
307    edje_object_part_drag_value_set
308      (wd->resize_obj, "elm.dragable.slider", pos, pos);
309 }
310
311 static void
312 _drag_cb(void *data,
313          Evas_Object *_obj EINA_UNUSED,
314          const char *emission EINA_UNUSED,
315          const char *source EINA_UNUSED)
316 {
317    double pos = 0.0, delta;
318    Evas_Object *obj = data;
319    const char *style;
320
321    ELM_SPINNER_DATA_GET(obj, sd);
322    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
323
324    if (sd->entry_visible) return;
325
326    style = elm_widget_style_get(obj);
327
328    if (sd->button_layout)
329      {
330         if (!strncmp(elm_widget_style_get(obj), "vertical", 8))
331           eo_do((Eo *)wd->resize_obj,
332                 edje_obj_part_drag_value_get("elm.dragable.slider", NULL, &pos));
333         else
334           eo_do((Eo *)wd->resize_obj,
335                 edje_obj_part_drag_value_get("elm.dragable.slider", &pos, NULL));
336      }
337    else
338      eo_do((Eo *)wd->resize_obj,
339            edje_obj_part_drag_value_get("elm.dragable.slider", &pos, NULL));
340    if (sd->drag_prev_pos != 0)
341      sd->drag_val_step = pow((pos - sd->drag_prev_pos), 2);
342    else
343      sd->drag_val_step = 1;
344
345
346    delta = sd->drag_val_step * sd->step * _elm_config->scale;
347    if (pos < sd->drag_prev_pos) delta *= -1;
348    sd->drag_prev_pos = pos;
349
350    /* Dragable is inverse of spinner value */
351    if (!strncmp(style, "vertical", 8)) delta *= -1;
352    /* If we are on rtl mode, change the delta to be negative on such changes */
353    if (elm_widget_mirrored_get(obj)) delta *= -1;
354    if (_value_set(data, sd->val + delta)) _label_write(data);
355    sd->dragging = 1;
356 }
357
358 static void
359 _drag_start_cb(void *data,
360                Evas_Object *obj EINA_UNUSED,
361                const char *emission EINA_UNUSED,
362                const char *source EINA_UNUSED)
363 {
364    ELM_SPINNER_DATA_GET(data, sd);
365
366    sd->drag_prev_pos = 0;
367    sd->drag_val_step = 1;
368
369    eo_do(obj, eo_event_callback_call
370      (ELM_SPINNER_EVENT_SPINNER_DRAG_START, NULL));
371 }
372
373 static void
374 _drag_stop_cb(void *data,
375               Evas_Object *obj EINA_UNUSED,
376               const char *emission EINA_UNUSED,
377               const char *source EINA_UNUSED)
378 {
379    ELM_SPINNER_DATA_GET(data, sd);
380    ELM_WIDGET_DATA_GET_OR_RETURN(data, wd);
381
382    sd->drag_prev_pos = 0;
383    sd->drag_val_step = 1;
384    edje_object_part_drag_value_set
385      (wd->resize_obj, "elm.dragable.slider", 0.0, 0.0);
386
387    eo_do(obj, eo_event_callback_call
388      (ELM_SPINNER_EVENT_SPINNER_DRAG_STOP, NULL));
389 }
390
391 static void
392 _entry_hide(Evas_Object *obj)
393 {
394    ELM_SPINNER_DATA_GET(obj, sd);
395
396    if (sd->button_layout)
397      {
398         elm_layout_signal_emit(obj, "elm,state,button,active", "elm");
399         evas_object_show(sd->text_button);
400         elm_layout_signal_emit(obj, "elm,state,entry,inactive", "elm");
401         evas_object_hide(sd->ent);
402      }
403    else
404      elm_layout_signal_emit(obj, "elm,state,inactive", "elm");
405
406
407    if (sd->entry_visible && !evas_focus_state_get(evas_object_evas_get(obj)))
408      sd->entry_reactivate = EINA_TRUE;
409
410    //TIZEN_ONLY(20160606): Forcefully setting highlight frame on spinner entry as on entry activation frame goes to window again.
411    if (_elm_atspi_enabled()) eo_do(obj, elm_interface_atspi_component_highlight_clear());
412    //
413    sd->entry_visible = EINA_FALSE;
414 }
415
416 static void
417 _entry_value_apply(Evas_Object *obj)
418 {
419    const char *str;
420    double val;
421    char *end;
422
423    ELM_SPINNER_DATA_GET(obj, sd);
424
425    if (!sd->entry_visible) return;
426
427    eo_do(sd->ent, eo_event_callback_del
428          (ELM_WIDGET_EVENT_UNFOCUSED, _entry_unfocused_cb, obj));
429    _entry_hide(obj);
430    str = elm_object_text_get(sd->ent);
431    if (!str) return;
432
433    //TIZEN_ONLY(20161007): If user set special value as number. spinner value
434    //                      should not set as special value.
435    const char *special_value = elm_spinner_special_value_get(obj, sd->val);
436    if (special_value && !strcmp(special_value, str)) return;
437    //
438
439    val = strtod(str, &end);
440    if (((*end != '\0') && (!isspace(*end))) || (fabs(val - sd->val) < DBL_EPSILON)) return;
441    elm_spinner_value_set(obj, val);
442
443    eo_do(obj, eo_event_callback_call(ELM_SPINNER_EVENT_CHANGED, NULL));
444    ecore_timer_del(sd->delay_change_timer);
445    sd->delay_change_timer = ecore_timer_add(ELM_SPINNER_DELAY_CHANGE_TIME,
446                                             _delay_change_timer_cb, obj);
447 }
448
449 static int
450 _decimal_points_get(const char *label)
451 {
452    char result[2] = "";
453    const char *start = strchr(label, '%');
454
455    while (start)
456      {
457         if (start[1] != '%')
458           {
459              start = strchr(start, '.');
460              if (start)
461                 start++;
462              break;
463           }
464         else
465           start = strchr(start + 2, '%');
466      }
467
468    if (start)
469       sscanf(start, "%[^f]", result);
470
471    return atoi(result);
472 }
473
474 //TIZEN_ONLY(20160419): Spinner entry changed callback support for datetime UX.
475 static void
476 _entry_changed_user_cb(void *data,
477                        Evas_Object *obj,
478                        void *event_info EINA_UNUSED)
479 {
480    Evas_Object *spinner;
481    const char *str;
482    int len, max_len;
483    double min, max, val;
484
485    spinner = data;
486    str = elm_object_text_get(obj);
487    len = strlen(str);
488    if (len < 1) return;
489    val = atof(str);
490    elm_spinner_min_max_get(spinner, &min, &max);
491    max_len = log10(abs(max)) + 1;
492    if (max_len == len)
493      {
494         val = (val < min) ? min : (val > max ? max : val);
495         elm_spinner_value_set(spinner, val);
496         _label_write(spinner);
497         elm_entry_cursor_end_set(obj);
498         str = elm_object_text_get(obj);
499      }
500
501    evas_object_smart_callback_call(spinner, SIG_ENTRY_CHANGED, (void *)str);
502 }
503 //
504
505 static void
506 _invalid_input_validity_filter(void *data EINA_UNUSED, Evas_Object *obj, char **text)
507 {
508    char *insert = NULL;
509    const char *str = NULL;
510    int cursor_pos = 0;
511    int read_idx = 0, read_char, cmp_char;
512
513    EINA_SAFETY_ON_NULL_RETURN(obj);
514    EINA_SAFETY_ON_NULL_RETURN(text);
515
516    insert = *text;
517    str = elm_object_text_get(obj);
518
519    evas_string_char_next_get(*text, 0, &read_char);
520    cursor_pos = elm_entry_cursor_pos_get(obj);
521    if (read_char)
522      {
523        if (read_char == '-')
524          {
525             if (cursor_pos != 0)
526               {
527                  goto invalid_input;
528               }
529          }
530        if (read_char == '.')
531          {
532             read_idx = evas_string_char_next_get(str, 0, &cmp_char);
533             while (cmp_char)
534               {
535                  if (read_char == cmp_char)
536                    {
537                       goto invalid_input;
538                    }
539                  read_idx = evas_string_char_next_get(str, read_idx, &cmp_char);
540                }
541          }
542        read_idx = evas_string_char_next_get(str, 0, &cmp_char);
543        if ((cmp_char == '-') && (cursor_pos == 0))
544          {
545             goto invalid_input;
546          }
547      }
548    return;
549
550 invalid_input:
551    *insert = 0;
552 }
553
554 static void
555 _entry_accept_filter_add(Evas_Object *obj)
556 {
557    ELM_SPINNER_DATA_GET(obj, sd);
558    static Elm_Entry_Filter_Accept_Set digits_filter_data;
559
560    if (!sd->ent) return;
561
562    elm_entry_markup_filter_remove(sd->ent, elm_entry_filter_accept_set, &digits_filter_data);
563
564    if (sd->decimal_points > 0)
565      digits_filter_data.accepted = "-.0123456789";
566    else
567      digits_filter_data.accepted = "-0123456789";
568
569    elm_entry_markup_filter_prepend(sd->ent, elm_entry_filter_accept_set, &digits_filter_data);
570 }
571
572 char *
573 _text_insert(const char *text, const char *input, int pos)
574 {
575    char *result = NULL;
576    int text_len, input_len;
577
578    text_len = evas_string_char_len_get(text);
579    input_len = evas_string_char_len_get(input);
580    result = (char *)calloc(text_len + input_len + 1, sizeof(char));
581    if (!result) return NULL;
582
583    strncpy(result, text, pos);
584    strcpy(result + pos, input);
585    strcpy(result + pos + input_len, text + pos);
586
587    return result;
588 }
589
590 static void
591 _min_max_validity_filter(void *data, Evas_Object *obj, char **text)
592 {
593    const char *str, *point;
594    char *insert, *new_str = NULL;
595    double val;
596    int max_len, len;
597
598    EINA_SAFETY_ON_NULL_RETURN(data);
599    EINA_SAFETY_ON_NULL_RETURN(obj);
600    EINA_SAFETY_ON_NULL_RETURN(text);
601
602    ELM_SPINNER_DATA_GET(data, sd);
603
604    str = elm_object_text_get(obj);
605    if (!str) return;
606
607    insert = *text;
608    new_str = _text_insert(str, insert, elm_entry_cursor_pos_get(obj));
609    if (!new_str) return;
610
611    if (sd->format_type == SPINNER_FORMAT_FLOAT)
612      {
613         point = strchr(new_str, '.');
614         if (point)
615           {
616              if ((int) strlen(point + 1) > sd->decimal_points)
617                {
618                   *insert = 0;
619                   goto end;
620                }
621           }
622      }
623
624    max_len = log10(fabs(sd->val_max)) + 1;
625    len = strlen(new_str);
626    if (len < max_len) goto end;
627
628    val = strtod(new_str, NULL);
629    if ((val < sd->val_min) || (val > sd->val_max))
630      *insert = 0;
631
632 end:
633    free(new_str);
634 }
635
636 static void
637 _entry_show_cb(void *data,
638                Evas *e EINA_UNUSED,
639                Evas_Object *obj,
640                void *event_info EINA_UNUSED)
641 {
642    ELM_SPINNER_DATA_GET(data, sd);
643
644    _entry_show(sd);
645    elm_object_focus_set(obj, EINA_TRUE);
646    elm_entry_select_all(obj);
647    sd->entry_visible = EINA_TRUE;
648    elm_layout_signal_emit(data, "elm,state,button,inactive", "elm");
649    evas_object_hide(sd->text_button);
650    //TIZEN_ONLY(20160606): Forcefully setting highlight frame on spinner entry as on entry activation frame goes to window again.
651    if (_elm_atspi_enabled()) eo_do(obj, elm_interface_atspi_component_highlight_grab());
652    //
653 }
654
655 static void
656 _toggle_entry(Evas_Object *obj)
657 {
658    ELM_SPINNER_DATA_GET(obj, sd);
659
660    if (sd->dragging)
661      {
662         sd->dragging = 0;
663         return;
664      }
665    if (elm_widget_disabled_get(obj)) return;
666    if (!sd->editable) return;
667    if (sd->entry_visible) _entry_value_apply(obj);
668    else
669      {
670         if (!sd->ent)
671           {
672              sd->ent = elm_entry_add(obj);
673              /* TIZEN_ONLY(20161031): apply color_class parent-child relationship to all widgets */
674              _elm_widget_color_class_parent_set(sd->ent, obj);
675              /* END */
676              Eina_Strbuf *buf = eina_strbuf_new();
677              eina_strbuf_append_printf(buf, "spinner/%s", elm_widget_style_get(obj));
678              elm_widget_style_set(sd->ent, eina_strbuf_string_get(buf));
679              eina_strbuf_free(buf);
680              //TIZEN_ONLY(20160419): Apply default properties for entry widget in the spinner.
681              elm_entry_context_menu_disabled_set(sd->ent, EINA_TRUE);
682              elm_entry_input_panel_layout_set(sd->ent, ELM_INPUT_PANEL_LAYOUT_NUMBERONLY);
683              elm_entry_cnp_mode_set(sd->ent, ELM_CNP_MODE_PLAINTEXT);
684              elm_entry_prediction_allow_set(sd->ent, EINA_FALSE);
685              //
686              if (sd->button_layout)
687                {
688                   evas_object_event_callback_add
689                     (sd->ent, EVAS_CALLBACK_SHOW, _entry_show_cb, obj);
690                   //TIZEN_ONLY(20160419): Spinner entry changed callback support for datetime UX.
691                   evas_object_smart_callback_add
692                      (sd->ent, "changed,user", _entry_changed_user_cb, obj);
693                   //
694                }
695              elm_entry_single_line_set(sd->ent, EINA_TRUE);
696              elm_layout_content_set(obj, "elm.swallow.entry", sd->ent);
697              _entry_accept_filter_add(obj);
698              elm_entry_markup_filter_append(sd->ent, _invalid_input_validity_filter, NULL);
699              if (_elm_config->spinner_min_max_filter_enable)
700                elm_entry_markup_filter_append(sd->ent, _min_max_validity_filter, obj);
701              eo_do(sd->ent, eo_event_callback_add
702                    (ELM_ENTRY_EVENT_ACTIVATED, _entry_activated_cb, obj));
703           }
704         if (!sd->button_layout)
705           {
706              elm_layout_signal_emit(obj, "elm,state,active", "elm");
707              _entry_show(sd);
708              elm_entry_select_all(sd->ent);
709              elm_widget_focus_set(sd->ent, EINA_TRUE);
710              sd->entry_visible = EINA_TRUE;
711           }
712
713         eo_do(sd->ent, eo_event_callback_add
714               (ELM_WIDGET_EVENT_UNFOCUSED, _entry_unfocused_cb, obj));
715         sd->entry_visible = EINA_TRUE;
716         elm_layout_signal_emit(obj, "elm,state,entry,active", "elm");
717         evas_object_show(sd->ent);
718      }
719 }
720
721 static void
722 _entry_toggle_cb(void *data EINA_UNUSED,
723                  Evas_Object *obj,
724                  const char *emission EINA_UNUSED,
725                  const char *source EINA_UNUSED)
726 {
727    _toggle_entry(obj);
728 }
729
730 static Eina_Bool
731 _spin_value(void *data)
732 {
733    ELM_SPINNER_DATA_GET(data, sd);
734    double real_speed = sd->spin_speed;
735
736    /* Sanity check: our step size should be at least as large as our rounding value */
737    if ((sd->spin_speed != 0.0) && (fabs(sd->spin_speed) < sd->round))
738      {
739         WRN("The spinning step is smaller than the rounding value, please check your code");
740         real_speed = sd->spin_speed > 0 ? sd->round : -sd->round;
741      }
742
743    //TIZEN_ONLY(20160419): This is not SPIN UX.
744    /*
745    sd->interval = sd->interval / 1.05;
746
747    // spin_timer does not exist when _spin_value() is called from wheel event
748    if (sd->spin_timer)
749      ecore_timer_interval_set(sd->spin_timer, sd->interval);
750    */
751    if (_value_set(data, sd->val + real_speed)) _label_write(data);
752
753    return ECORE_CALLBACK_RENEW;
754 }
755
756 static Eina_Bool
757 _val_inc_dec_start(void *data)
758 {
759    ELM_SPINNER_DATA_GET(data, sd);
760
761    sd->interval = sd->first_interval;
762    sd->spin_speed = sd->inc_btn_activated ? sd->step : -sd->step;
763    sd->longpress_timer = NULL;
764    //TIZEN_ONLY(20160419): This is not SPIN UX.
765    //ecore_timer_del(sd->spin_timer);
766    elm_layout_signal_emit(data, "elm,action,longpress", "elm");
767    if (sd->spin_timer) ecore_timer_del(sd->spin_timer);
768    //
769    sd->spin_timer = ecore_timer_add(sd->interval, _spin_value, data);
770    _spin_value(data);
771
772    elm_widget_scroll_freeze_push(data);
773
774    return ECORE_CALLBACK_CANCEL;
775 }
776
777 static void
778 _spin_stop(Evas_Object *obj)
779 {
780    ELM_SPINNER_DATA_GET(obj, sd);
781
782    sd->interval = sd->first_interval;
783    sd->spin_speed = 0;
784    ELM_SAFE_FREE(sd->spin_timer, ecore_timer_del);
785
786    elm_widget_scroll_freeze_pop(obj);
787 }
788
789 static Eina_Bool
790 _key_action_toggle(Evas_Object *obj, const char *params EINA_UNUSED)
791 {
792    ELM_SPINNER_DATA_GET(obj, sd);
793
794    if (sd->spin_timer) _spin_stop(obj);
795    else if (sd->entry_visible) _entry_toggle_cb(NULL, obj, NULL, NULL);
796
797    return EINA_FALSE;
798 }
799
800 static void
801 _button_inc_dec_start_cb(void *data,
802                      Evas_Object *obj,
803                      const char *emission,
804                      const char *source EINA_UNUSED)
805 {
806    ELM_SPINNER_DATA_GET(data, sd);
807
808    sd->inc_btn_activated =
809       !strcmp(emission, "elm,action,increment,start") ? EINA_TRUE : EINA_FALSE;
810
811    if (sd->entry_visible)
812      {
813         _entry_value_apply(obj);
814
815         if (sd->val_updated)
816           {
817              if (sd->inc_btn_activated)
818                {
819                   if (sd->val == sd->val_min) return;
820                }
821              else
822                {
823                   if (sd->val == sd->val_max) return;
824                }
825           }
826      }
827
828    ecore_timer_del(sd->longpress_timer);
829    sd->longpress_timer = ecore_timer_add
830      (_elm_config->longpress_timeout, _val_inc_dec_start, data);
831 }
832
833 static void
834 _button_inc_dec_stop_cb(void *data,
835                     Evas_Object *obj EINA_UNUSED,
836                     const char *emission EINA_UNUSED,
837                     const char *source EINA_UNUSED)
838 {
839    ELM_SPINNER_DATA_GET(data, sd);
840
841    if (sd->longpress_timer)
842      {
843         ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del);
844
845         if (sd->inc_btn_activated)
846           sd->spin_speed = sd->step;
847         else
848           sd->spin_speed = -sd->step;
849
850         _spin_value(data);
851      }
852
853    _spin_stop(data);
854 }
855
856 EOLIAN static Eina_Bool
857 _elm_spinner_elm_widget_event(Eo *obj, Elm_Spinner_Data *sd, Evas_Object *src EINA_UNUSED, Evas_Callback_Type type, void *event_info)
858 {
859    Evas_Event_Key_Down *ev = event_info;
860    Evas_Event_Mouse_Wheel *mev;
861
862    //TIZEN_ONLY(20170418): Support Tv 4.0 UX.
863    if ((!sd->button_layout) || (!elm_object_focus_allow_get(sd->inc_button)))
864      {
865         if ((type == EVAS_CALLBACK_KEY_DOWN))
866           {
867              if (!strcmp(ev->key, "Up"))
868                {
869                   _button_inc_dec_start_cb(obj, obj, "elm,action,increment,start", NULL);
870                   ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
871                   return EINA_TRUE;
872                }
873              if (!strcmp(ev->key, "Down"))
874                {
875                   _button_inc_dec_start_cb(obj, obj, "elm,action,decrement,start", NULL);
876                   ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
877                   return EINA_TRUE;
878                }
879           }
880         else if ((type == EVAS_CALLBACK_KEY_UP))
881           {
882              _button_inc_dec_stop_cb(obj, obj, NULL, NULL);
883           }
884      }
885    //
886    if (type == EVAS_CALLBACK_KEY_DOWN)
887      {
888         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
889         if (sd->spin_timer) _spin_stop(obj);
890         else return EINA_FALSE;
891         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
892      }
893    else if (type == EVAS_CALLBACK_KEY_UP)
894      {
895         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
896         if (sd->spin_timer) _spin_stop(obj);
897         else return EINA_FALSE;
898         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
899      }
900    else if (type == EVAS_CALLBACK_MOUSE_WHEEL)
901      {
902         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
903         mev = event_info;
904         sd->interval = sd->first_interval;
905         if (mev->z < 0)
906           {
907              sd->spin_speed = sd->step;
908              elm_layout_signal_emit(obj, "elm,right,anim,activate", "elm");
909           }
910         else
911           {
912              sd->spin_speed = -sd->step;
913              elm_layout_signal_emit(obj, "elm,left,anim,activate", "elm");
914           }
915         _spin_value(obj);
916         mev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
917      }
918    else return EINA_FALSE;
919
920    return EINA_TRUE;
921 }
922
923 static Eina_Bool
924 _inc_dec_button_clicked_cb(void *data,
925                        Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED,
926                        void *event_info EINA_UNUSED)
927 {
928    ELM_SPINNER_DATA_GET(data, sd);
929
930    _spin_stop(data);
931    sd->inc_btn_activated = sd->inc_button == obj ? EINA_TRUE : EINA_FALSE;
932    sd->spin_speed = sd->inc_btn_activated ? sd->step : -sd->step;
933    _spin_value(data);
934
935    if (_elm_config->access_mode)
936      _access_increment_decrement_info_say(data, EINA_TRUE);
937
938    return EINA_TRUE;
939 }
940
941 static Eina_Bool
942 _inc_dec_button_pressed_cb(void *data,
943                        Eo *obj, const Eo_Event_Description *desc EINA_UNUSED,
944                        void *event_info EINA_UNUSED)
945 {
946    ELM_SPINNER_DATA_GET(data, sd);
947
948    sd->inc_btn_activated = sd->inc_button == obj ? EINA_TRUE : EINA_FALSE;
949
950    if (sd->longpress_timer) ecore_timer_del(sd->longpress_timer);
951
952    sd->longpress_timer = ecore_timer_add
953                            (_elm_config->longpress_timeout,
954                             _val_inc_dec_start, data);
955
956    if (sd->entry_visible) _entry_value_apply(data);
957
958    return EINA_TRUE;
959 }
960
961 static Eina_Bool
962 _inc_dec_button_unpressed_cb(void *data,
963                          Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED,
964                          void *event_info EINA_UNUSED)
965 {
966    ELM_SPINNER_DATA_GET(data, sd);
967
968    if (sd->longpress_timer)
969      {
970         ecore_timer_del(sd->longpress_timer);
971         sd->longpress_timer = NULL;
972      }
973
974    _spin_stop(data);
975
976    return EINA_TRUE;
977 }
978
979 static Eina_Bool
980 _text_button_focused_cb(void *data,
981                    Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED,
982                    void *event_info EINA_UNUSED)
983 {
984    _toggle_entry(data);
985
986    return EINA_TRUE;
987 }
988
989 static Eina_Bool
990 _entry_activated_cb(void *data,
991                     Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED,
992                     void *event_info EINA_UNUSED)
993 {
994    _toggle_entry(data);
995    return EINA_TRUE;
996 }
997
998 static Eina_Bool
999 _entry_unfocused_cb(void *data,
1000                     Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED,
1001                     void *event_info EINA_UNUSED)
1002 {
1003    _toggle_entry(data);
1004
1005    return EINA_TRUE;
1006 }
1007
1008 static Eina_Bool
1009 _text_button_clicked_cb(void *data,
1010                         Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED,
1011                         void *event_info EINA_UNUSED)
1012 {
1013    ELM_SPINNER_DATA_GET(data, sd);
1014
1015    if (sd->entry_visible) return EINA_TRUE;
1016    _toggle_entry(data);
1017
1018    return EINA_TRUE;
1019 }
1020
1021 static Eina_Bool
1022 _inc_dec_button_mouse_move_cb(void *data,
1023                        Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED,
1024                        void *event_info)
1025 {
1026    Evas_Event_Mouse_Move *ev = event_info;
1027    ELM_SPINNER_DATA_GET(data, sd);
1028
1029    if ((ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) && sd->longpress_timer)
1030      {
1031         ecore_timer_del(sd->longpress_timer);
1032         sd->longpress_timer = NULL;
1033      }
1034
1035    return EINA_TRUE;
1036 }
1037
1038 EOLIAN static void
1039 _elm_spinner_elm_layout_sizing_eval(Eo *obj, Elm_Spinner_Data *_pd EINA_UNUSED)
1040 {
1041    Evas_Coord minw = -1, minh = -1;
1042    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
1043
1044    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
1045    edje_object_size_min_restricted_calc
1046      (wd->resize_obj, &minw, &minh, minw, minh);
1047    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
1048    evas_object_size_hint_min_set(obj, minw, minh);
1049    evas_object_size_hint_max_set(obj, -1, -1);
1050 }
1051
1052 EOLIAN static Eina_Bool
1053 _elm_spinner_elm_widget_on_focus(Eo *obj, Elm_Spinner_Data *sd, Elm_Object_Item *item EINA_UNUSED)
1054 {
1055    Eina_Bool int_ret = EINA_FALSE;
1056    eo_do_super(obj, MY_CLASS, int_ret = elm_obj_widget_on_focus(NULL));
1057    if (!int_ret) return EINA_FALSE;
1058
1059    if (!elm_widget_focus_get(obj))
1060      {
1061         ELM_SAFE_FREE(sd->delay_change_timer, ecore_timer_del);
1062         ELM_SAFE_FREE(sd->spin_timer, ecore_timer_del);
1063         ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del);
1064      }
1065    //TIZEN_ONLY(20160623): Add entry visible, focus check flag for reactivating entry.
1066    else
1067      {
1068         if (sd->entry_reactivate)
1069           {
1070              _toggle_entry(obj);
1071              sd->entry_reactivate = EINA_FALSE;
1072           }
1073      }
1074    //
1075
1076    return EINA_TRUE;
1077 }
1078
1079 static void
1080 _access_activate_cb(void *data,
1081                     Evas_Object *part_obj,
1082                     Elm_Object_Item *item EINA_UNUSED)
1083 {
1084    char *text;
1085    Eina_Strbuf *buf;
1086    Evas_Object *eo, *inc_btn;
1087    const char* increment_part;
1088    ELM_SPINNER_DATA_GET(data, sd);
1089
1090    if (!strncmp(elm_widget_style_get(data), "vertical", 8))
1091      increment_part = "up_bt";
1092    else
1093      increment_part = "right_bt";
1094
1095    eo = elm_layout_edje_get(data);
1096    inc_btn = (Evas_Object *)edje_object_part_object_get(eo, increment_part);
1097
1098    if (part_obj != inc_btn)
1099      {
1100         sd->inc_btn_activated = EINA_FALSE;
1101         _val_inc_dec_start(data);
1102         elm_layout_signal_emit(data, "elm,left,anim,activate", "elm");
1103         _spin_stop(data);
1104         text = "decremented";
1105      }
1106    else
1107      {
1108         sd->inc_btn_activated = EINA_TRUE;
1109         _val_inc_dec_start(data);
1110         elm_layout_signal_emit(data, "elm,right,anim,activate", "elm");
1111         _spin_stop(data);
1112         text = "incremented";
1113      }
1114
1115    buf = eina_strbuf_new();
1116
1117    eina_strbuf_append_printf(buf, "%s, %s", text,
1118                              elm_layout_text_get(data, "elm.text"));
1119
1120    _elm_access_say(eina_strbuf_string_get(buf));
1121    eina_strbuf_free(buf);
1122 }
1123
1124 static char *
1125 _access_info_cb(void *data, Evas_Object *obj EINA_UNUSED)
1126 {
1127    const char *txt = NULL;
1128    Evas_Object *spinner = (Evas_Object *)(data);
1129    ELM_SPINNER_DATA_GET(spinner, sd);
1130
1131    if (sd->button_layout)
1132      {
1133         if (sd->entry_visible)
1134           txt = elm_object_text_get(sd->ent);
1135         else
1136           txt = elm_object_text_get(sd->text_button);
1137      }
1138    else
1139      {
1140         txt = elm_layout_text_get(spinner, "elm.text");
1141      }
1142
1143    if (txt) return strdup(txt);
1144
1145    return NULL;
1146 }
1147
1148 static char *
1149 _access_state_cb(void *data, Evas_Object *obj EINA_UNUSED)
1150 {
1151    if (elm_widget_disabled_get(data))
1152      return strdup(E_("State: Disabled"));
1153
1154    return NULL;
1155 }
1156
1157 static void
1158 _access_activate_spinner_cb(void *data,
1159                             Evas_Object *part_obj EINA_UNUSED,
1160                             Elm_Object_Item *item EINA_UNUSED)
1161 {
1162    ELM_SPINNER_DATA_GET(data, sd);
1163
1164    if (elm_widget_disabled_get(data)) return;
1165    if (!sd->entry_visible)
1166      _toggle_entry(data);
1167 }
1168
1169 static void
1170 _access_increment_decrement_info_say(Evas_Object *obj,
1171                                      Eina_Bool is_incremented)
1172 {
1173    Eina_Strbuf *buf;
1174
1175    ELM_SPINNER_DATA_GET(obj, sd);
1176
1177     buf = eina_strbuf_new();
1178     if (is_incremented)
1179       {
1180          elm_object_signal_emit
1181             (sd->inc_button, "elm,action,anim,activate", "elm");
1182          eina_strbuf_append(buf, E_("incremented"));
1183       }
1184     else
1185       {
1186          elm_object_signal_emit
1187             (sd->dec_button, "elm,action,anim,activate", "elm");
1188          eina_strbuf_append(buf, E_("decremented"));
1189       }
1190
1191    eina_strbuf_append_printf
1192       (buf, "%s", elm_object_text_get(sd->text_button));
1193
1194    _elm_access_say(eina_strbuf_string_get(buf));
1195    eina_strbuf_free(buf);
1196 }
1197
1198 static void
1199 _access_spinner_register(Evas_Object *obj, Eina_Bool is_access)
1200 {
1201    Evas_Object *ao;
1202    Elm_Access_Info *ai;
1203
1204    ELM_SPINNER_DATA_GET(obj, sd);
1205
1206    if (sd->button_layout)
1207      {
1208         if (!is_access)
1209           {
1210              /* unregister access */
1211              _elm_access_edje_object_part_object_unregister
1212              (obj, elm_layout_edje_get(obj), "access");
1213              elm_layout_signal_emit(obj, "elm,state,access,inactive", "elm");
1214              return;
1215           }
1216         elm_layout_signal_emit(obj, "elm,state,access,active", "elm");
1217         ao = _elm_access_edje_object_part_object_register
1218           (obj, elm_layout_edje_get(obj), "access");
1219
1220         ai = _elm_access_info_get(ao);
1221         _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("spinner"));
1222         _elm_access_callback_set(ai, ELM_ACCESS_STATE, _access_state_cb, obj);
1223         _elm_access_activate_callback_set(ai, _access_activate_spinner_cb, obj);
1224
1225         /*Do not register spinner buttons if widget is disabled*/
1226         if (!elm_widget_disabled_get(obj))
1227           {
1228              ai = _elm_access_info_get(sd->inc_button);
1229              _elm_access_text_set(ai, ELM_ACCESS_TYPE,
1230                                   E_("spinner increment button"));
1231              ai = _elm_access_info_get(sd->dec_button);
1232              _elm_access_text_set(ai, ELM_ACCESS_TYPE,
1233                                   E_("spinner decrement button"));
1234              ai = _elm_access_info_get(sd->text_button);
1235              _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("spinner text"));
1236              _elm_access_callback_set(ai, ELM_ACCESS_INFO, _access_info_cb, obj);
1237           }
1238      }
1239    else
1240      {
1241         const char* increment_part;
1242         const char* decrement_part;
1243
1244         if (!strncmp(elm_widget_style_get(obj), "vertical", 8))
1245           {
1246              increment_part = "up_bt";
1247              decrement_part = "down_bt";
1248           }
1249         else
1250           {
1251              increment_part = "right_bt";
1252              decrement_part = "left_bt";
1253           }
1254         if (!is_access)
1255           {
1256              /* unregister increment button, decrement button and spinner label */
1257              _elm_access_edje_object_part_object_unregister
1258                (obj, elm_layout_edje_get(obj), increment_part);
1259              _elm_access_edje_object_part_object_unregister
1260                (obj, elm_layout_edje_get(obj), decrement_part);
1261              _elm_access_edje_object_part_object_unregister
1262                (obj, elm_layout_edje_get(obj), "access.text");
1263              return;
1264           }
1265
1266         /* register increment button */
1267         ao = _elm_access_edje_object_part_object_register
1268           (obj, elm_layout_edje_get(obj), increment_part);
1269         ai = _elm_access_info_get(ao);
1270         _elm_access_text_set(ai, ELM_ACCESS_TYPE,
1271                              E_("spinner increment button"));
1272         _elm_access_activate_callback_set(ai, _access_activate_cb, obj);
1273
1274         /* register decrement button */
1275         ao = _elm_access_edje_object_part_object_register
1276           (obj, elm_layout_edje_get(obj), decrement_part);
1277
1278         ai = _elm_access_info_get(ao);
1279         _elm_access_text_set(ai, ELM_ACCESS_TYPE,
1280                              E_("spinner decrement button"));
1281         _elm_access_activate_callback_set(ai, _access_activate_cb, obj);
1282
1283         /* register spinner label */
1284         ao = _elm_access_edje_object_part_object_register
1285           (obj, elm_layout_edje_get(obj), "access.text");
1286
1287         ai = _elm_access_info_get(ao);
1288         _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("spinner"));
1289         _elm_access_callback_set(ai, ELM_ACCESS_INFO, _access_info_cb, obj);
1290         _elm_access_callback_set(ai, ELM_ACCESS_STATE, _access_state_cb, obj);
1291      }
1292 }
1293
1294 EOLIAN static void
1295 _elm_spinner_evas_object_smart_add(Eo *obj, Elm_Spinner_Data *priv)
1296 {
1297    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
1298    ELM_SPINNER_DATA_GET(obj, sd);
1299
1300    eo_do_super(obj, MY_CLASS, evas_obj_smart_add());
1301    elm_widget_sub_object_parent_add(obj);
1302
1303    priv->val_max = 100.0;
1304    priv->step = 1.0;
1305    priv->first_interval = 0.85;
1306    //TIZEN_ONLY(20160419): Maintain compatibility.
1307    priv->editable = EINA_TRUE;
1308    //
1309
1310    if (!elm_layout_theme_set(obj, "spinner", "base",
1311                              elm_widget_style_get(obj)))
1312      CRI("Failed to set layout!");
1313
1314    if (edje_object_part_exists(wd->resize_obj, "elm.swallow.dec_button"))
1315      sd->button_layout = EINA_TRUE;
1316    else
1317      sd->button_layout = EINA_FALSE;
1318
1319    elm_layout_signal_callback_add(obj, "drag", "*", _drag_cb, obj);
1320    elm_layout_signal_callback_add(obj, "drag,start", "*", _drag_start_cb, obj);
1321    elm_layout_signal_callback_add(obj, "drag,stop", "*", _drag_stop_cb, obj);
1322    elm_layout_signal_callback_add(obj, "drag,step", "*", _drag_stop_cb, obj);
1323    elm_layout_signal_callback_add(obj, "drag,page", "*", _drag_stop_cb, obj);
1324
1325    if (sd->button_layout)
1326      {
1327         priv->inc_button = elm_button_add(obj);
1328         /* TIZEN_ONLY(20161031): apply color_class parent-child relationship to all widgets */
1329         _elm_widget_color_class_parent_set(priv->inc_button, obj);
1330         /* END */
1331
1332         /* TIZEN_ONLY(20161115): apply UI Mirroring for Tizen 3.0 UX.
1333            buttons inside a spinner are not mirrored */
1334         elm_object_mirrored_automatic_set(priv->inc_button, EINA_FALSE);
1335         elm_object_mirrored_set(priv->inc_button, EINA_FALSE);
1336         /* END */
1337
1338         elm_object_style_set(priv->inc_button, "spinner/increase/default");
1339
1340         eo_do(priv->inc_button,
1341               eo_event_callback_array_add(_inc_dec_button_cb(), obj));
1342
1343         elm_layout_content_set(obj, "elm.swallow.inc_button", priv->inc_button);
1344         elm_widget_sub_object_add(obj, priv->inc_button);
1345
1346         priv->text_button = elm_button_add(obj);
1347         /* TIZEN_ONLY(20161031): apply color_class parent-child relationship to all widgets */
1348         _elm_widget_color_class_parent_set(priv->text_button, obj);
1349         /* END */
1350
1351         /* TIZEN_ONLY(20161115): apply UI Mirroring for Tizen 3.0 UX.
1352            buttons inside a spinner are not mirrored */
1353         elm_object_mirrored_automatic_set(priv->text_button, EINA_FALSE);
1354         elm_object_mirrored_set(priv->text_button, EINA_FALSE);
1355         /* END */
1356
1357         elm_object_style_set(priv->text_button, "spinner/default");
1358
1359         eo_do(priv->text_button, eo_event_callback_add
1360           (EVAS_CLICKABLE_INTERFACE_EVENT_CLICKED, _text_button_clicked_cb, obj));
1361         eo_do(priv->text_button, eo_event_callback_add
1362               (ELM_WIDGET_EVENT_FOCUSED, _text_button_focused_cb, obj));
1363
1364         elm_layout_content_set(obj, "elm.swallow.text_button", priv->text_button);
1365         elm_widget_sub_object_add(obj, priv->text_button);
1366
1367         priv->dec_button = elm_button_add(obj);
1368         /* TIZEN_ONLY(20161031): apply color_class parent-child relationship to all widgets */
1369         _elm_widget_color_class_parent_set(priv->dec_button, obj);
1370         /* END */
1371
1372         /* TIZEN_ONLY(20161115): apply UI Mirroring for Tizen 3.0 UX.
1373            buttons inside a spinner are not mirrored */
1374         elm_object_mirrored_automatic_set(priv->dec_button, EINA_FALSE);
1375         elm_object_mirrored_set(priv->dec_button, EINA_FALSE);
1376         /* END */
1377
1378         elm_object_style_set(priv->dec_button, "spinner/decrease/default");
1379
1380         eo_do(priv->dec_button,
1381               eo_event_callback_array_add(_inc_dec_button_cb(), obj));
1382
1383         elm_layout_content_set(obj, "elm.swallow.dec_button", priv->dec_button);
1384         elm_widget_sub_object_add(obj, priv->dec_button);
1385      }
1386    else
1387      {
1388         elm_layout_signal_callback_add
1389           (obj, "elm,action,increment,start", "*", _button_inc_dec_start_cb, obj);
1390         elm_layout_signal_callback_add
1391           (obj, "elm,action,increment,stop", "*", _button_inc_dec_stop_cb, obj);
1392         elm_layout_signal_callback_add
1393           (obj, "elm,action,decrement,start", "*", _button_inc_dec_start_cb, obj);
1394         elm_layout_signal_callback_add
1395           (obj, "elm,action,decrement,stop", "*", _button_inc_dec_stop_cb, obj);
1396      }
1397
1398    edje_object_part_drag_value_set
1399      (wd->resize_obj, "elm.dragable.slider", 0.0, 0.0);
1400
1401    elm_layout_signal_callback_add
1402      (obj, "elm,action,entry,toggle", "*", _entry_toggle_cb, NULL);
1403
1404    _label_write(obj);
1405    elm_widget_can_focus_set(obj, EINA_TRUE);
1406
1407    elm_layout_sizing_eval(obj);
1408
1409    /* access */
1410    if (_elm_config->access_mode)
1411      _access_spinner_register(obj, EINA_TRUE);
1412 }
1413
1414 EOLIAN static void
1415 _elm_spinner_evas_object_smart_del(Eo *obj, Elm_Spinner_Data *sd)
1416 {
1417    Elm_Spinner_Special_Value *sv;
1418
1419    eina_stringshare_del(sd->label);
1420    ecore_timer_del(sd->delay_change_timer);
1421    ecore_timer_del(sd->spin_timer);
1422    ecore_timer_del(sd->longpress_timer);
1423
1424    if (sd->special_values)
1425      {
1426         EINA_LIST_FREE(sd->special_values, sv)
1427           {
1428              eina_stringshare_del(sv->label);
1429              free(sv);
1430           }
1431      }
1432
1433    eo_do_super(obj, MY_CLASS, evas_obj_smart_del());
1434 }
1435
1436 EOLIAN static Elm_Theme_Apply
1437 _elm_spinner_elm_widget_theme_apply(Eo *obj, Elm_Spinner_Data *sd)
1438 {
1439    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, ELM_THEME_APPLY_FAILED);
1440
1441    if (!elm_layout_theme_set(obj, "spinner", "base", elm_widget_style_get(obj)))
1442      CRI("Failed to set layout!");
1443
1444    if (edje_object_part_exists(wd->resize_obj, "elm.swallow.dec_button"))
1445      sd->button_layout = EINA_TRUE;
1446    else
1447      sd->button_layout = EINA_FALSE;
1448
1449    if (sd->ent)
1450      {
1451         Eina_Strbuf *buf = eina_strbuf_new();
1452         eina_strbuf_append_printf(buf, "spinner/%s", elm_widget_style_get(obj));
1453         elm_widget_style_set(sd->ent, eina_strbuf_string_get(buf));
1454         eina_strbuf_free(buf);
1455      }
1456
1457    if (sd->inc_button)
1458      {
1459         Eina_Strbuf *buf = eina_strbuf_new();
1460         eina_strbuf_append_printf(buf, "spinner/increase/%s", elm_widget_style_get(obj));
1461         elm_widget_style_set(sd->inc_button, eina_strbuf_string_get(buf));
1462         eina_strbuf_free(buf);
1463      }
1464
1465    if (sd->text_button)
1466      {
1467         Eina_Strbuf *buf = eina_strbuf_new();
1468         eina_strbuf_append_printf(buf, "spinner/%s", elm_widget_style_get(obj));
1469         elm_widget_style_set(sd->text_button, eina_strbuf_string_get(buf));
1470         eina_strbuf_free(buf);
1471      }
1472
1473    if (sd->dec_button)
1474      {
1475         Eina_Strbuf *buf = eina_strbuf_new();
1476         eina_strbuf_append_printf(buf, "spinner/decrease/%s", elm_widget_style_get(obj));
1477         elm_widget_style_set(sd->dec_button, eina_strbuf_string_get(buf));
1478         eina_strbuf_free(buf);
1479      }
1480
1481    if (_elm_config->access_mode)
1482      _access_spinner_register(obj, EINA_TRUE);
1483
1484    elm_layout_sizing_eval(obj);
1485    return ELM_THEME_APPLY_SUCCESS;
1486 }
1487
1488 //TIZEN_ONLY(20161111): apply UI Mirroring for Tizen 3.0 UX
1489 EOLIAN static Eina_Bool
1490 _elm_spinner_elm_widget_mirrored_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *_pd EINA_UNUSED)
1491 {
1492    return EINA_FALSE;
1493 }
1494 //
1495
1496 //TIZEN_ONLY(20161111): apply UI Mirroring for Tizen 3.0 UX
1497 EOLIAN static void
1498 _elm_spinner_elm_widget_mirrored_set(Eo *obj EINA_UNUSED, Elm_Spinner_Data *_pd EINA_UNUSED, Eina_Bool mirrored EINA_UNUSED)
1499 {
1500    return;
1501 }
1502 //
1503
1504 static Eina_Bool _elm_spinner_smart_focus_next_enable = EINA_FALSE;
1505
1506 EOLIAN static Eina_Bool
1507 _elm_spinner_elm_widget_focus_next_manager_is(Eo *obj EINA_UNUSED, Elm_Spinner_Data *_pd EINA_UNUSED)
1508 {
1509    ELM_SPINNER_DATA_GET(obj, sd);
1510
1511    return _elm_spinner_smart_focus_next_enable | sd->button_layout;
1512 }
1513
1514 EOLIAN static Eina_Bool
1515 _elm_spinner_elm_widget_focus_direction_manager_is(Eo *obj EINA_UNUSED, Elm_Spinner_Data *_pd EINA_UNUSED)
1516 {
1517    ELM_SPINNER_DATA_GET(obj, sd);
1518
1519    if (sd->button_layout) return EINA_TRUE;
1520    return EINA_FALSE;
1521 }
1522
1523 EOLIAN static Eina_Bool
1524 _elm_spinner_elm_widget_focus_direction(Eo *obj, Elm_Spinner_Data *_pd, const Evas_Object *base, double degree, Evas_Object **direction, Elm_Object_Item **direction_item, double *weight)
1525 {
1526    Eina_Bool ret;
1527    Eina_List *items = NULL;
1528    void *(*list_data_get)(const Eina_List *list);
1529
1530    ELM_SPINNER_CHECK(obj) EINA_FALSE;
1531
1532    if (!_pd)
1533      return EINA_FALSE;
1534
1535    list_data_get = eina_list_data_get;
1536
1537    items = eina_list_append(items, _pd->inc_button);
1538    items = eina_list_append(items, _pd->text_button);
1539    items = eina_list_append(items, _pd->dec_button);
1540
1541    ret = elm_widget_focus_list_direction_get
1542       (obj, base, items, list_data_get, degree, direction, direction_item, weight);
1543    eina_list_free(items);
1544
1545    return ret;
1546 }
1547
1548 static Evas_Object *
1549 _access_object_get(const Evas_Object *obj, const char* part)
1550 {
1551    Evas_Object *eo, *po, *ao;
1552
1553    eo = elm_layout_edje_get(obj);
1554
1555    po = (Evas_Object *)edje_object_part_object_get(eo, part);
1556    ao = evas_object_data_get(po, "_part_access_obj");
1557
1558    return ao;
1559 }
1560
1561 EOLIAN static Eina_Bool
1562 _elm_spinner_elm_widget_focus_next(Eo *obj, Elm_Spinner_Data *_pd, Elm_Focus_Direction dir, Evas_Object **next, Elm_Object_Item **next_item)
1563 {
1564    Evas_Object *ao;
1565    Eina_List *items = NULL;
1566    int ret;
1567
1568    ELM_SPINNER_CHECK(obj) EINA_FALSE;
1569
1570    if (_elm_config->access_mode)
1571      {
1572         ao = _access_object_get(obj, "access");
1573         items = eina_list_append(items, ao);
1574      }
1575    if (!elm_widget_disabled_get(obj))
1576      {
1577         items = eina_list_append(items, _pd->dec_button);
1578         items = eina_list_append(items, _pd->text_button);
1579         items = eina_list_append(items, _pd->inc_button);
1580      }
1581
1582    ret = elm_widget_focus_list_next_get
1583             (obj, items, eina_list_data_get, dir, next, next_item);
1584    eina_list_free(items);
1585
1586    return ret;
1587 }
1588
1589 EOLIAN static void
1590 _elm_spinner_elm_widget_access(Eo *obj, Elm_Spinner_Data *_pd EINA_UNUSED, Eina_Bool acs)
1591 {
1592    _elm_spinner_smart_focus_next_enable = acs;
1593    _access_spinner_register(obj, _elm_spinner_smart_focus_next_enable);
1594 }
1595
1596 EAPI Evas_Object *
1597 elm_spinner_add(Evas_Object *parent)
1598 {
1599    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
1600    Evas_Object *obj = eo_add(MY_CLASS, parent);
1601    return obj;
1602 }
1603
1604 EOLIAN static Eo *
1605 _elm_spinner_eo_base_constructor(Eo *obj, Elm_Spinner_Data *_pd EINA_UNUSED)
1606 {
1607    obj = eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor());
1608    eo_do(obj,
1609          evas_obj_type_set(MY_CLASS_NAME_LEGACY),
1610          evas_obj_smart_callbacks_descriptions_set(_smart_callbacks),
1611          elm_interface_atspi_accessible_role_set(ELM_ATSPI_ROLE_SPIN_BUTTON));
1612
1613    return obj;
1614 }
1615
1616 EOLIAN static void
1617 _elm_spinner_label_format_set(Eo *obj, Elm_Spinner_Data *sd, const char *fmt)
1618 {
1619    Elm_Spinner_Format_Type type = SPINNER_FORMAT_INVALID;
1620
1621    if (fmt)
1622      {
1623         type = _is_label_format_integer(fmt);
1624         if (type == SPINNER_FORMAT_INVALID)
1625           {
1626              ERR("format:\"%s\" is invalid, cannot be set", fmt);
1627              return;
1628           }
1629         else if (type == SPINNER_FORMAT_FLOAT)
1630           {
1631              sd->decimal_points = _decimal_points_get(fmt);
1632           }
1633      }
1634
1635    eina_stringshare_replace(&sd->label, fmt);
1636
1637    sd->format_type = type;
1638    _label_write(obj);
1639    elm_layout_sizing_eval(obj);
1640    _entry_accept_filter_add(obj);
1641 }
1642
1643 EOLIAN static const char*
1644 _elm_spinner_label_format_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd)
1645 {
1646    return sd->label;
1647 }
1648
1649 EOLIAN static void
1650 _elm_spinner_min_max_set(Eo *obj, Elm_Spinner_Data *sd, double min, double max)
1651 {
1652    if ((sd->val_min == min) && (sd->val_max == max)) return;
1653
1654    sd->val_min = min;
1655    sd->val_max = max;
1656
1657    if (sd->val < sd->val_min) sd->val = sd->val_min;
1658    if (sd->val > sd->val_max) sd->val = sd->val_max;
1659
1660    _val_set(obj);
1661    _label_write(obj);
1662    //TIZEN_ONLY(20160419): Added entry filter feature.
1663    _entry_accept_filter_add(obj);
1664    //
1665 }
1666
1667 EOLIAN static void
1668 _elm_spinner_min_max_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd, double *min, double *max)
1669 {
1670    if (min) *min = sd->val_min;
1671    if (max) *max = sd->val_max;
1672 }
1673
1674 EOLIAN static void
1675 _elm_spinner_step_set(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd, double step)
1676 {
1677    sd->step = step;
1678 }
1679
1680 EOLIAN static double
1681 _elm_spinner_step_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd)
1682 {
1683    return sd->step;
1684 }
1685
1686 EOLIAN static void
1687 _elm_spinner_value_set(Eo *obj, Elm_Spinner_Data *sd, double val)
1688 {
1689    if (sd->val == val) return;
1690
1691    sd->val = val;
1692    sd->val_updated = EINA_FALSE;
1693
1694    if (sd->val < sd->val_min)
1695      {
1696         sd->val = sd->val_min;
1697         sd->val_updated = EINA_TRUE;
1698      }
1699    if (sd->val > sd->val_max)
1700      {
1701         sd->val = sd->val_max;
1702         sd->val_updated = EINA_TRUE;
1703      }
1704
1705    _val_set(obj);
1706    _label_write(obj);
1707    //TIZEN_ONLY(20160419): Changed smart callback call for value update.
1708    evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
1709    //
1710 }
1711
1712 EOLIAN static double
1713 _elm_spinner_value_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd)
1714 {
1715    return sd->val;
1716 }
1717
1718 EOLIAN static void
1719 _elm_spinner_wrap_set(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd, Eina_Bool wrap)
1720 {
1721    sd->wrap = wrap;
1722 }
1723
1724 EOLIAN static Eina_Bool
1725 _elm_spinner_wrap_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd)
1726 {
1727    return sd->wrap;
1728 }
1729
1730 EOLIAN static void
1731 _elm_spinner_special_value_add(Eo *obj, Elm_Spinner_Data *sd, double value, const char *label)
1732 {
1733    Elm_Spinner_Special_Value *sv;
1734    Eina_List *l;
1735
1736    EINA_LIST_FOREACH(sd->special_values, l, sv)
1737      {
1738         if (sv->value != value)
1739           continue;
1740
1741         eina_stringshare_replace(&sv->label, label);
1742         _label_write(obj);
1743         return;
1744      }
1745
1746    sv = calloc(1, sizeof(*sv));
1747    if (!sv) return;
1748    sv->value = value;
1749    sv->label = eina_stringshare_add(label);
1750
1751    sd->special_values = eina_list_append(sd->special_values, sv);
1752    _label_write(obj);
1753 }
1754
1755 EAPI void
1756 elm_spinner_special_value_del(Evas_Object *obj,
1757                               double value)
1758 {
1759    Elm_Spinner_Special_Value *sv;
1760    Eina_List *l;
1761
1762    ELM_SPINNER_CHECK(obj);
1763    ELM_SPINNER_DATA_GET(obj, sd);
1764
1765    EINA_LIST_FOREACH(sd->special_values, l, sv)
1766      {
1767         if (sv->value != value)
1768           continue;
1769
1770         sd->special_values = eina_list_remove_list(sd->special_values, l);
1771         eina_stringshare_del(sv->label);
1772         free(sv);
1773         _label_write(obj);
1774         return;
1775      }
1776 }
1777
1778 EAPI const char *
1779 elm_spinner_special_value_get(Evas_Object *obj,
1780                               double value)
1781 {
1782    Elm_Spinner_Special_Value *sv;
1783    Eina_List *l;
1784
1785    ELM_SPINNER_CHECK(obj) NULL;
1786    ELM_SPINNER_DATA_GET(obj, sd);
1787
1788    EINA_LIST_FOREACH(sd->special_values, l, sv)
1789      {
1790         if (sv->value == value)
1791           return sv->label;
1792      }
1793
1794    return NULL;
1795 }
1796
1797 EOLIAN static void
1798 _elm_spinner_editable_set(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd, Eina_Bool editable)
1799 {
1800    sd->editable = editable;
1801    //TIZEN_ONLY(20161222): disable touch sound for uneditable mode
1802    if (sd->editable)
1803      elm_layout_signal_emit(sd->text_button, "elm,state,entry,editable", "elm");
1804    else
1805      elm_layout_signal_emit(sd->text_button, "elm,state,entry,uneditable", "elm");
1806    //
1807 }
1808
1809 EOLIAN static Eina_Bool
1810 _elm_spinner_editable_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd)
1811 {
1812    return sd->editable;
1813 }
1814
1815 EOLIAN static void
1816 _elm_spinner_interval_set(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd, double interval)
1817 {
1818    sd->first_interval = interval;
1819 }
1820
1821 EOLIAN static double
1822 _elm_spinner_interval_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd)
1823 {
1824    return sd->first_interval;
1825 }
1826
1827 EOLIAN static void
1828 _elm_spinner_base_set(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd, double base)
1829 {
1830    sd->val_base = base;
1831 }
1832
1833 EOLIAN static double
1834 _elm_spinner_base_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd)
1835 {
1836    return sd->val_base;
1837 }
1838
1839 EOLIAN static void
1840 _elm_spinner_round_set(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd, int rnd)
1841 {
1842    sd->round = rnd;
1843 }
1844
1845 EOLIAN static int
1846 _elm_spinner_round_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd)
1847 {
1848    return sd->round;
1849 }
1850
1851 EOLIAN static void
1852 _elm_spinner_class_constructor(Eo_Class *klass)
1853 {
1854    evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
1855
1856    if (_elm_config->access_mode)
1857       _elm_spinner_smart_focus_next_enable = EINA_TRUE;
1858 }
1859
1860 EOLIAN static const Elm_Atspi_Action *
1861 _elm_spinner_elm_interface_atspi_widget_action_elm_actions_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd EINA_UNUSED)
1862 {
1863    static Elm_Atspi_Action atspi_actions[] = {
1864       { "toggle", "toggle", NULL, _key_action_toggle},
1865       { NULL, NULL, NULL, NULL }
1866    };
1867    return &atspi_actions[0];
1868 }
1869
1870 // A11Y Accessibility
1871
1872 EOLIAN static void
1873 _elm_spinner_elm_interface_atspi_value_value_and_text_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd, double *value, const char **text)
1874 {
1875    if (value) *value = sd->val;
1876    if (text) *text = NULL;
1877 }
1878
1879 EOLIAN static Eina_Bool
1880 _elm_spinner_elm_interface_atspi_value_value_and_text_set(Eo *obj, Elm_Spinner_Data *sd, double value, const char *text EINA_UNUSED)
1881 {
1882    if (sd->val_min > value) return EINA_FALSE;
1883    if (sd->val_max < value) return EINA_FALSE;
1884
1885    sd->val = value;
1886    _val_set(obj);
1887
1888    return EINA_TRUE;
1889 }
1890
1891 EOLIAN static void
1892 _elm_spinner_elm_interface_atspi_value_range_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd, double *lower, double *upper, const char **descr)
1893 {
1894    if (lower) *lower = sd->val_min;
1895    if (upper) *upper = sd->val_max;
1896    if (descr) *descr = NULL;
1897 }
1898
1899 EOLIAN static double
1900 _elm_spinner_elm_interface_atspi_value_increment_get(Eo *obj EINA_UNUSED, Elm_Spinner_Data *sd)
1901 {
1902    return sd->step;
1903 }
1904
1905 EOLIAN static const char*
1906 _elm_spinner_elm_interface_atspi_accessible_name_get(Eo *obj, Elm_Spinner_Data *sd)
1907 {
1908    const char *name;
1909    eo_do_super(obj, MY_CLASS, name = elm_interface_atspi_accessible_name_get());
1910    if (name) return name;
1911    const char *ret = NULL;
1912    //TIZEN_ONLY(20160519): Spinner improve atspi support
1913    if (sd->button_layout)
1914      {
1915         if (sd->entry_visible)
1916           ret = elm_object_text_get(sd->ent);
1917         else
1918           ret = elm_object_text_get(sd->text_button);
1919      }
1920    else
1921      {
1922         ret = elm_layout_text_get(obj, "elm.text");
1923      }
1924    //
1925    return _elm_widget_accessible_plain_name_get(obj, ret);
1926 }
1927
1928 // A11Y Accessibility - END
1929
1930 #include "elm_spinner.eo.c"