1 #include <Elementary.h>
5 typedef struct _Widget_Data Widget_Data;
6 typedef struct _Elm_Spinner_Special_Value Elm_Spinner_Special_Value;
10 Evas_Object *spinner, *ent;
12 double val, val_min, val_max, orig_val, step;
13 double drag_start_pos, spin_speed, interval, first_interval;
14 Ecore_Timer *delay, *spin;
15 Eina_List *special_values;
17 Eina_Bool entry_visible : 1;
18 Eina_Bool dragging : 1;
19 Eina_Bool editable : 1;
22 struct _Elm_Spinner_Special_Value
28 static const char *widtype = NULL;
29 static void _del_hook(Evas_Object *obj);
30 static void _disable_hook(Evas_Object *obj);
31 static void _write_label(Evas_Object *obj);
32 static void _sizing_eval(Evas_Object *obj);
33 //static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
34 static Eina_Bool _value_set(Evas_Object *obj, double delta);
35 static void _on_focus_hook(void *data, Evas_Object *obj);
36 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
37 Evas_Callback_Type type, void *event_info);
39 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
41 static const char SIG_CHANGED[] = "changed";
42 static const char SIG_DELAY_CHANGED[] = "delay,changed";
44 static const Evas_Smart_Cb_Description _signals[] = {
46 {SIG_DELAY_CHANGED, ""},
51 _del_hook(Evas_Object *obj)
53 Elm_Spinner_Special_Value *sv;
54 Widget_Data *wd = elm_widget_data_get(obj);
56 if (wd->label) eina_stringshare_del(wd->label);
57 if (wd->delay) ecore_timer_del(wd->delay);
58 if (wd->spin) ecore_timer_del(wd->spin);
59 if (wd->special_values)
61 EINA_LIST_FREE(wd->special_values, sv)
63 eina_stringshare_del(sv->label);
71 _disable_hook(Evas_Object *obj)
73 Widget_Data *wd = elm_widget_data_get(obj);
75 if (elm_widget_disabled_get(obj))
76 edje_object_signal_emit(wd->spinner, "elm,state,disabled", "elm");
78 edje_object_signal_emit(wd->spinner, "elm,state,enabled", "elm");
82 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
84 Widget_Data *wd = elm_widget_data_get(obj);
86 edje_object_signal_emit(wd->spinner, emission, source);
90 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
92 Widget_Data *wd = elm_widget_data_get(obj);
94 edje_object_signal_callback_add(wd->spinner, emission,
95 source, func_cb, data);
99 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
101 Widget_Data *wd = elm_widget_data_get(obj);
102 edje_object_signal_callback_del_full(wd->spinner, emission, source,
107 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
109 Widget_Data *wd = elm_widget_data_get(obj);
111 edje_object_mirrored_set(wd->spinner, rtl);
115 _theme_hook(Evas_Object *obj)
117 Widget_Data *wd = elm_widget_data_get(obj);
119 _elm_widget_mirrored_reload(obj);
120 _mirrored_set(obj, elm_widget_mirrored_get(obj));
121 _elm_theme_object_set(obj, wd->spinner, "spinner", "base", elm_widget_style_get(obj));
122 edje_object_part_swallow(wd->spinner, "elm.swallow.entry", wd->ent);
124 if (elm_widget_focus_get(obj))
125 edje_object_signal_emit(wd->spinner, "elm,action,focus", "elm");
127 edje_object_signal_emit(wd->spinner, "elm,action,unfocus", "elm");
128 if (elm_widget_disabled_get(obj))
129 edje_object_signal_emit(wd->spinner, "elm,state,disabled", "elm");
130 edje_object_message_signal_process(wd->spinner);
131 edje_object_scale_set(wd->spinner, elm_widget_scale_get(obj) * _elm_config->scale);
136 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
138 Widget_Data *wd = elm_widget_data_get(obj);
140 if (elm_widget_focus_get(obj))
142 edje_object_signal_emit(wd->spinner, "elm,action,focus", "elm");
143 evas_object_focus_set(wd->spinner, EINA_TRUE);
147 edje_object_signal_emit(wd->spinner, "elm,action,unfocus", "elm");
148 evas_object_focus_set(wd->spinner, EINA_FALSE);
153 _delay_change(void *data)
155 Widget_Data *wd = elm_widget_data_get(data);
156 if (!wd) return ECORE_CALLBACK_CANCEL;
158 evas_object_smart_callback_call(data, SIG_DELAY_CHANGED, NULL);
159 return ECORE_CALLBACK_CANCEL;
163 _entry_show(Widget_Data *wd)
165 char buf[32], fmt[32] = "%0.f";
167 /* try to construct just the format from given label
168 * completely ignoring pre/post words
172 const char *start = strchr(wd->label, '%');
179 start = strchr(start + 2, '%');
184 const char *itr, *end = NULL;
185 for (itr = start + 1; *itr != '\0'; itr++)
187 /* allowing '%d' is quite dangerous, remove it? */
188 if ((*itr == 'd') || (*itr == 'f'))
195 if ((end) && ((size_t)(end - start + 1) < sizeof(fmt)))
197 memcpy(fmt, start, end - start);
198 fmt[end - start] = '\0';
202 snprintf(buf, sizeof(buf), fmt, wd->val);
203 elm_object_text_set(wd->ent, buf);
207 _write_label(Evas_Object *obj)
210 Elm_Spinner_Special_Value *sv;
211 Widget_Data *wd = elm_widget_data_get(obj);
214 EINA_LIST_FOREACH(wd->special_values, l, sv)
216 if (sv->value == wd->val)
218 snprintf(buf, sizeof(buf), "%s", sv->label);
223 snprintf(buf, sizeof(buf), wd->label, wd->val);
225 snprintf(buf, sizeof(buf), "%.0f", wd->val);
228 edje_object_part_text_set(wd->spinner, "elm.text", buf);
229 if (wd->entry_visible) _entry_show(wd);
233 _value_set(Evas_Object *obj, double delta)
235 Widget_Data *wd = elm_widget_data_get(obj);
237 if (!wd) return EINA_FALSE;
238 new_val = wd->val + delta;
241 while (new_val < wd->val_min)
242 new_val = wd->val_max + new_val + 1 - wd->val_min;
243 while (new_val > wd->val_max)
244 new_val = wd->val_min + new_val - wd->val_max - 1;
248 if (new_val < wd->val_min)
249 new_val = wd->val_min;
250 else if (new_val > wd->val_max)
251 new_val = wd->val_max;
254 if (new_val == wd->val) return EINA_FALSE;
257 evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
258 if (wd->delay) ecore_timer_del(wd->delay);
259 wd->delay = ecore_timer_add(0.2, _delay_change, obj);
265 _sizing_eval(Evas_Object *obj)
267 Widget_Data *wd = elm_widget_data_get(obj);
268 Evas_Coord minw = -1, minh = -1;
270 elm_coords_finger_size_adjust(1, &minw, 1, &minh);
271 edje_object_size_min_restricted_calc(wd->spinner, &minw, &minh, minw, minh);
272 elm_coords_finger_size_adjust(1, &minw, 1, &minh);
273 evas_object_size_hint_min_set(obj, minw, minh);
274 evas_object_size_hint_max_set(obj, -1, -1);
279 _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info)
286 _val_set(Evas_Object *obj)
288 Widget_Data *wd = elm_widget_data_get(obj);
291 if (wd->val_max > wd->val_min)
292 pos = ((wd->val - wd->val_min) / (wd->val_max - wd->val_min));
293 if (pos < 0.0) pos = 0.0;
294 else if (pos > 1.0) pos = 1.0;
295 edje_object_part_drag_value_set(wd->spinner, "elm.dragable.slider",
300 _drag(void *data, Evas_Object *_obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
302 Evas_Object *obj = data;
303 Widget_Data *wd = elm_widget_data_get(obj);
304 double pos = 0.0, offset, delta;
306 if (wd->entry_visible) return;
307 edje_object_part_drag_value_get(wd->spinner, "elm.dragable.slider",
310 delta = (pos - wd->drag_start_pos) * offset;
311 /* If we are on rtl mode, change the delta to be negative on such changes */
312 if (elm_widget_mirrored_get(obj))
314 if (_value_set(data, delta)) _write_label(data);
315 wd->drag_start_pos = pos;
320 _drag_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
322 Widget_Data *wd = elm_widget_data_get(data);
325 edje_object_part_drag_value_get(wd->spinner, "elm.dragable.slider",
327 wd->drag_start_pos = pos;
331 _drag_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
333 Widget_Data *wd = elm_widget_data_get(data);
335 wd->drag_start_pos = 0;
336 edje_object_part_drag_value_set(wd->spinner, "elm.dragable.slider", 0.0, 0.0);
340 _hide_entry(Evas_Object *obj)
342 Widget_Data *wd = elm_widget_data_get(obj);
344 edje_object_signal_emit(wd->spinner, "elm,state,inactive", "elm");
345 wd->entry_visible = 0;
349 _reset_value(Evas_Object *obj)
351 Widget_Data *wd = elm_widget_data_get(obj);
354 elm_spinner_value_set(obj, wd->orig_val);
358 _apply_entry_value(Evas_Object *obj)
360 Widget_Data *wd = elm_widget_data_get(obj);
367 str = elm_object_text_get(wd->ent);
369 val = strtod(str, &end);
370 if ((*end != '\0') && (!isspace(*end))) return;
371 elm_spinner_value_set(obj, val);
375 _toggle_entry(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
377 Widget_Data *wd = elm_widget_data_get(data);
384 if (elm_widget_disabled_get(data)) return;
385 if (!wd->editable) return;
386 if (wd->entry_visible) _apply_entry_value(data);
389 wd->orig_val = wd->val;
390 edje_object_signal_emit(wd->spinner, "elm,state,active", "elm");
392 elm_entry_select_all(wd->ent);
393 elm_widget_focus_set(wd->ent, 1);
394 wd->entry_visible = 1;
399 _spin_value(void *data)
401 Widget_Data *wd = elm_widget_data_get(data);
402 if (!wd) return ECORE_CALLBACK_CANCEL;
403 if (_value_set(data, wd->spin_speed)) _write_label(data);
404 wd->interval = wd->interval / 1.05;
405 ecore_timer_interval_set(wd->spin, wd->interval);
406 return ECORE_CALLBACK_RENEW;
410 _val_inc_start(Evas_Object *obj)
412 Widget_Data *wd = elm_widget_data_get(obj);
414 wd->interval = wd->first_interval;
415 wd->spin_speed = wd->step;
416 if (wd->spin) ecore_timer_del(wd->spin);
417 wd->spin = ecore_timer_add(wd->interval, _spin_value, obj);
422 _val_inc_stop(Evas_Object *obj)
424 Widget_Data *wd = elm_widget_data_get(obj);
426 wd->interval = wd->first_interval;
428 if (wd->spin) ecore_timer_del(wd->spin);
433 _val_dec_start(Evas_Object *obj)
435 Widget_Data *wd = elm_widget_data_get(obj);
437 wd->interval = wd->first_interval;
438 wd->spin_speed = -wd->step;
439 if (wd->spin) ecore_timer_del(wd->spin);
440 wd->spin = ecore_timer_add(wd->interval, _spin_value, obj);
445 _val_dec_stop(Evas_Object *obj)
447 Widget_Data *wd = elm_widget_data_get(obj);
449 wd->interval = wd->first_interval;
451 if (wd->spin) ecore_timer_del(wd->spin);
456 _button_inc_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
458 Widget_Data *wd = elm_widget_data_get(data);
460 if (wd->entry_visible)
465 _val_inc_start(data);
469 _button_inc_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
471 Widget_Data *wd = elm_widget_data_get(data);
477 _button_dec_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
479 Widget_Data *wd = elm_widget_data_get(data);
481 if (wd->entry_visible)
486 _val_dec_start(data);
490 _button_dec_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
492 Widget_Data *wd = elm_widget_data_get(data);
498 _entry_activated(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
500 Widget_Data *wd = elm_widget_data_get(data);
502 _apply_entry_value(data);
503 evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
504 if (wd->delay) ecore_timer_del(wd->delay);
505 wd->delay = ecore_timer_add(0.2, _delay_change, data);
509 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
511 Widget_Data *wd = elm_widget_data_get(obj);
512 if (!wd) return EINA_FALSE;
513 if (elm_widget_disabled_get(obj)) return EINA_FALSE;
514 if (type == EVAS_CALLBACK_KEY_DOWN)
516 Evas_Event_Key_Down *ev = event_info;
517 if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
518 else if (!strcmp(ev->keyname, "Left") || !strcmp(ev->keyname, "KP_Left")
519 || !strcmp(ev->keyname, "Down") || !strcmp(ev->keyname, "KP_Down"))
522 edje_object_signal_emit(wd->spinner, "elm,left,anim,activate", "elm");
523 ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
526 else if (!strcmp(ev->keyname, "Right") || !strcmp(ev->keyname, "KP_Right")
527 || !strcmp(ev->keyname, "Up") || !strcmp(ev->keyname, "KP_Up"))
530 edje_object_signal_emit(wd->spinner, "elm,right,anim,activate", "elm");
531 ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
535 else if (type == EVAS_CALLBACK_KEY_UP)
537 Evas_Event_Key_Down *ev = event_info;
538 if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
539 if (!strcmp(ev->keyname, "Right") || !strcmp(ev->keyname, "KP_Right")
540 || !strcmp(ev->keyname, "Up") || !strcmp(ev->keyname, "KP_Up"))
542 else if (!strcmp(ev->keyname, "Left") || !strcmp(ev->keyname, "KP_Left")
543 || !strcmp(ev->keyname, "Down") || !strcmp(ev->keyname, "KP_Down"))
545 else return EINA_FALSE;
546 ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
553 elm_spinner_add(Evas_Object *parent)
559 ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
561 ELM_SET_WIDTYPE(widtype, "spinner");
562 elm_widget_type_set(obj, "spinner");
563 elm_widget_sub_object_add(parent, obj);
564 elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
565 elm_widget_data_set(obj, wd);
566 elm_widget_del_hook_set(obj, _del_hook);
567 elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
568 elm_widget_theme_hook_set(obj, _theme_hook);
569 elm_widget_disable_hook_set(obj, _disable_hook);
570 elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
571 elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
572 elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
573 elm_widget_can_focus_set(obj, EINA_TRUE);
574 elm_widget_event_hook_set(obj, _event_hook);
581 wd->first_interval = 0.85;
582 wd->entry_visible = 0;
583 wd->editable = EINA_TRUE;
585 wd->spinner = edje_object_add(e);
586 _elm_theme_object_set(obj, wd->spinner, "spinner", "base", "default");
587 elm_widget_resize_object_set(obj, wd->spinner);
588 edje_object_signal_callback_add(wd->spinner, "drag", "*", _drag, obj);
589 edje_object_signal_callback_add(wd->spinner, "drag,start", "*",
591 edje_object_signal_callback_add(wd->spinner, "drag,stop", "*",
593 edje_object_signal_callback_add(wd->spinner, "drag,step", "*",
595 edje_object_signal_callback_add(wd->spinner, "drag,page", "*",
598 edje_object_signal_callback_add(wd->spinner, "elm,action,increment,start",
599 "*", _button_inc_start, obj);
600 edje_object_signal_callback_add(wd->spinner, "elm,action,increment,stop",
601 "*", _button_inc_stop, obj);
602 edje_object_signal_callback_add(wd->spinner, "elm,action,decrement,start",
603 "*", _button_dec_start, obj);
604 edje_object_signal_callback_add(wd->spinner, "elm,action,decrement,stop",
605 "*", _button_dec_stop, obj);
606 edje_object_part_drag_value_set(wd->spinner, "elm.dragable.slider",
609 wd->ent = elm_entry_add(obj);
610 elm_entry_single_line_set(wd->ent, 1);
611 evas_object_smart_callback_add(wd->ent, "activated", _entry_activated, obj);
612 edje_object_part_swallow(wd->spinner, "elm.swallow.entry", wd->ent);
613 edje_object_signal_callback_add(wd->spinner, "elm,action,entry,toggle",
614 "*", _toggle_entry, obj);
616 evas_object_smart_callbacks_descriptions_set(obj, _signals);
618 _mirrored_set(obj, elm_widget_mirrored_get(obj));
625 elm_spinner_label_format_set(Evas_Object *obj, const char *fmt)
627 ELM_CHECK_WIDTYPE(obj, widtype);
628 Widget_Data *wd = elm_widget_data_get(obj);
630 eina_stringshare_replace(&wd->label, fmt);
636 elm_spinner_label_format_get(const Evas_Object *obj)
638 ELM_CHECK_WIDTYPE(obj, widtype) NULL;
639 Widget_Data *wd = elm_widget_data_get(obj);
640 if (!wd) return NULL;
645 elm_spinner_min_max_set(Evas_Object *obj, double min, double max)
647 ELM_CHECK_WIDTYPE(obj, widtype);
648 Widget_Data *wd = elm_widget_data_get(obj);
650 if ((wd->val_min == min) && (wd->val_max == max)) return;
653 if (wd->val < wd->val_min) wd->val = wd->val_min;
654 if (wd->val > wd->val_max) wd->val = wd->val_max;
660 elm_spinner_min_max_get(const Evas_Object *obj, double *min, double *max)
664 ELM_CHECK_WIDTYPE(obj, widtype);
665 Widget_Data *wd = elm_widget_data_get(obj);
667 if (min) *min = wd->val_min;
668 if (max) *max = wd->val_max;
672 elm_spinner_step_set(Evas_Object *obj, double step)
674 ELM_CHECK_WIDTYPE(obj, widtype);
675 Widget_Data *wd = elm_widget_data_get(obj);
681 elm_spinner_step_get(const Evas_Object *obj)
683 ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
684 Widget_Data *wd = elm_widget_data_get(obj);
690 elm_spinner_value_set(Evas_Object *obj, double val)
692 ELM_CHECK_WIDTYPE(obj, widtype);
693 Widget_Data *wd = elm_widget_data_get(obj);
695 if (wd->val == val) return;
697 if (wd->val < wd->val_min) wd->val = wd->val_min;
698 if (wd->val > wd->val_max) wd->val = wd->val_max;
704 elm_spinner_value_get(const Evas_Object *obj)
706 ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
707 Widget_Data *wd = elm_widget_data_get(obj);
713 elm_spinner_wrap_set(Evas_Object *obj, Eina_Bool wrap)
715 ELM_CHECK_WIDTYPE(obj, widtype);
716 Widget_Data *wd = elm_widget_data_get(obj);
722 elm_spinner_wrap_get(const Evas_Object *obj)
724 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
725 Widget_Data *wd = elm_widget_data_get(obj);
726 if (!wd) return EINA_FALSE;
731 elm_spinner_special_value_add(Evas_Object *obj, double value, const char *label)
733 Elm_Spinner_Special_Value *sv;
734 ELM_CHECK_WIDTYPE(obj, widtype);
735 Widget_Data *wd = elm_widget_data_get(obj);
738 sv = calloc(1, sizeof(*sv));
741 sv->label = eina_stringshare_add(label);
743 wd->special_values = eina_list_append(wd->special_values, sv);
748 elm_spinner_editable_set(Evas_Object *obj, Eina_Bool editable)
750 ELM_CHECK_WIDTYPE(obj, widtype);
751 Widget_Data *wd = elm_widget_data_get(obj);
753 wd->editable = editable;
757 elm_spinner_editable_get(const Evas_Object *obj)
759 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
760 Widget_Data *wd = elm_widget_data_get(obj);
761 if (!wd) return EINA_FALSE;
766 elm_spinner_interval_set(Evas_Object *obj, double interval)
768 ELM_CHECK_WIDTYPE(obj, widtype);
769 Widget_Data *wd = elm_widget_data_get(obj);
771 wd->first_interval = interval;
775 elm_spinner_interval_get(const Evas_Object *obj)
777 ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
778 Widget_Data *wd = elm_widget_data_get(obj);
780 return wd->first_interval;