Merge branch 'master' into svn_merge
[framework/uifw/elementary.git] / src / lib / elm_button.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @ingroup Elementary
6  */
7
8 typedef struct _Widget_Data Widget_Data;
9
10 enum
11 {
12    DEFAULT = 0,
13    HIGHLIGHTED,
14    FOCUSED,
15    DISABLED,
16 };
17
18 struct _Widget_Data
19 {
20    Evas_Object *btn, *icon;
21    const char *label;
22    Eina_Bool autorepeat;
23    Eina_Bool repeating;
24    double ar_threshold;
25    double ar_interval;
26    Ecore_Timer *timer;
27    const char *statelabel[4];
28    int statetype[4];
29 };
30
31 static const char *widtype = NULL;
32 static void _del_hook(Evas_Object *obj);
33 static void _theme_hook(Evas_Object *obj);
34 static void _disable_hook(Evas_Object *obj);
35 static void _sizing_eval(Evas_Object *obj);
36 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
37 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
38 static void _signal_clicked(void *data, Evas_Object *obj, const char *emission, const char *source);
39 static void _signal_pressed(void *data, Evas_Object *obj, const char *emission, const char *source);
40 static void _signal_unpressed(void *data, Evas_Object *obj, const char *emission, const char *source);
41 static void _on_focus_hook(void *data, Evas_Object *obj);
42 static void _activate(Evas_Object *obj);
43 static void _activate_hook(Evas_Object *obj);
44 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
45                              Evas_Callback_Type type, void *event_info);
46
47 static void _set_label(Evas_Object *obj, const char *label);
48 static void _signal_default_text_set(void *data, Evas_Object *obj, const char *emission, const char *source);
49
50 static const char SIG_CLICKED[] = "clicked";
51 static const char SIG_REPEATED[] = "repeated";
52 static const char SIG_PRESSED[] = "pressed";
53 static const char SIG_UNPRESSED[] = "unpressed";
54 static const Evas_Smart_Cb_Description _signals[] = {
55        {SIG_CLICKED, ""},
56        {SIG_REPEATED, ""},
57        {SIG_PRESSED, ""},
58        {SIG_UNPRESSED, ""},
59        {NULL, NULL}
60 };
61
62 static Eina_Bool
63 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
64 {
65    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
66    Evas_Event_Key_Down *ev = event_info;
67    Widget_Data *wd = elm_widget_data_get(obj);
68    if (!wd) return EINA_FALSE;
69    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
70    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
71    if ((strcmp(ev->keyname, "Return")) &&
72        (strcmp(ev->keyname, "KP_Enter")) &&
73        (strcmp(ev->keyname, "space")))
74      return EINA_FALSE;
75    _activate(obj);
76    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
77    edje_object_signal_emit(wd->btn, "elm,anim,activate", "elm");
78    return EINA_TRUE;
79 }
80
81 static void
82 _del_hook(Evas_Object *obj)
83 {
84    Widget_Data *wd = elm_widget_data_get(obj);
85    if (!wd) return;
86    if (wd->label) eina_stringshare_del(wd->label);
87    if (wd->statelabel[DEFAULT]) eina_stringshare_del(wd->statelabel[DEFAULT]);
88    if (wd->statelabel[HIGHLIGHTED]) eina_stringshare_del(wd->statelabel[HIGHLIGHTED]);
89    if (wd->statelabel[FOCUSED]) eina_stringshare_del(wd->statelabel[FOCUSED]);
90    if (wd->statelabel[DISABLED]) eina_stringshare_del(wd->statelabel[DISABLED]);
91    free(wd);
92 }
93
94 static void
95 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
96 {
97    Widget_Data *wd = elm_widget_data_get(obj);
98    if (!wd) return;
99    if (elm_widget_focus_get(obj))
100      {
101         if (wd->statelabel[FOCUSED])
102           {
103              _set_label(obj, wd->statelabel[FOCUSED]);
104           }
105    edje_object_signal_emit(wd->btn, "elm,action,focus", "elm");
106    evas_object_focus_set(wd->btn, EINA_TRUE);
107      }
108    else
109      {
110         if (wd->statelabel[DEFAULT])
111           _set_label(obj, wd->statelabel[DEFAULT]);
112    edje_object_signal_emit(wd->btn, "elm,action,unfocus", "elm");
113    evas_object_focus_set(wd->btn, EINA_FALSE);
114      }
115 }
116
117 static void
118 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
119 {
120    Widget_Data *wd = elm_widget_data_get(obj);
121    if (!wd) return;
122    edje_object_mirrored_set(wd->btn, rtl);
123 }
124
125 static void
126 _theme_hook(Evas_Object *obj)
127 {
128    Widget_Data *wd = elm_widget_data_get(obj);
129    const char *str;
130    if (!wd) return;
131    _elm_widget_mirrored_reload(obj);
132    _mirrored_set(obj, elm_widget_mirrored_get(obj));
133    _elm_theme_object_set(obj, wd->btn, "button", "base", elm_widget_style_get(obj));
134    if (elm_object_disabled_get(obj))
135      edje_object_signal_emit(wd->btn, "elm,state,disabled", "elm");
136    if (wd->icon)
137      edje_object_part_swallow(wd->btn, "elm.swallow.content", wd->icon);
138    if (wd->label)
139      edje_object_signal_emit(wd->btn, "elm,state,text,visible", "elm");
140    else
141      edje_object_signal_emit(wd->btn, "elm,state,text,hidden", "elm");
142    if (wd->icon)
143      edje_object_signal_emit(wd->btn, "elm,state,icon,visible", "elm");
144    else
145      edje_object_signal_emit(wd->btn, "elm,state,icon,hidden", "elm");
146    edje_object_part_text_set(wd->btn, "elm.text", wd->label);
147    edje_object_message_signal_process(wd->btn);
148    edje_object_scale_set(wd->btn, elm_widget_scale_get(obj) * _elm_config->scale);
149    str = edje_object_data_get(wd->btn, "focus_highlight");
150    if ((str) && (!strcmp(str, "on")))
151      elm_widget_highlight_in_theme_set(obj, EINA_TRUE);
152    else
153      elm_widget_highlight_in_theme_set(obj, EINA_FALSE);
154    _sizing_eval(obj);
155 }
156
157 static void
158 _disable_hook(Evas_Object *obj)
159 {
160    Widget_Data *wd = elm_widget_data_get(obj);
161    if (!wd) return;
162    if (elm_widget_disabled_get(obj))
163      {
164         if (wd->statelabel[DISABLED] )
165           {
166              _set_label(obj, wd->statelabel[DISABLED]);
167           }
168         edje_object_signal_emit(wd->btn, "elm,state,disabled", "elm");
169      }
170    else
171      {
172         if (wd->statelabel[DEFAULT])
173           _set_label(obj, wd->statelabel[DEFAULT]);
174         edje_object_signal_emit(wd->btn, "elm,state,enabled", "elm");
175      }
176 }
177
178 static void
179 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
180 {
181    Widget_Data *wd = elm_widget_data_get(obj);
182    if (!wd) return;
183    edje_object_signal_emit(wd->btn, emission, source);
184 }
185
186 static void
187 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
188 {
189    Widget_Data *wd = elm_widget_data_get(obj);
190    if (!wd) return;
191    edje_object_signal_callback_add(wd->btn, emission, source, func_cb, data);
192 }
193
194 static void
195 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
196 {
197    Widget_Data *wd = elm_widget_data_get(obj);
198    edje_object_signal_callback_del_full(wd->btn, emission, source, func_cb,
199                                         data);
200 }
201
202 static void
203 _sizing_eval(Evas_Object *obj)
204 {
205    Widget_Data *wd = elm_widget_data_get(obj);
206    Evas_Coord minw = -1, minh = -1;
207    Evas_Coord w, h;
208
209    if (!wd) return;
210    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
211    edje_object_size_min_restricted_calc(wd->btn, &minw, &minh, minw, minh);
212    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
213    evas_object_size_hint_min_get(obj, &w, &h);
214    if (h > minh) minh = h;
215
216    evas_object_size_hint_min_set(obj, minw, minh);
217 }
218
219 static void
220 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
221 {
222    Widget_Data *wd = elm_widget_data_get(data);
223    if (!wd) return;
224    if (obj != wd->icon) return;
225    _sizing_eval(data);
226 }
227
228 static void
229 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
230 {
231    Widget_Data *wd = elm_widget_data_get(obj);
232    Evas_Object *sub = event_info;
233    if (!wd) return;
234    if (sub == wd->icon)
235      {
236         edje_object_signal_emit(wd->btn, "elm,state,icon,hidden", "elm");
237         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
238                                             _changed_size_hints, obj);
239         wd->icon = NULL;
240         edje_object_message_signal_process(wd->btn);
241         _sizing_eval(obj);
242      }
243 }
244
245 static void
246 _activate(Evas_Object *obj)
247 {
248    Widget_Data *wd = elm_widget_data_get(obj);
249    if (!wd) return;
250    if (wd->timer)
251      {
252         ecore_timer_del(wd->timer);
253         wd->timer = NULL;
254      }
255    wd->repeating = EINA_FALSE;
256    evas_object_smart_callback_call(obj, SIG_CLICKED, NULL);
257 }
258
259 static void
260 _activate_hook(Evas_Object *obj)
261 {
262    _activate(obj);
263 }
264
265 static void
266 _signal_clicked(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
267 {
268    _activate(data);
269 }
270
271 static Eina_Bool
272 _autorepeat_send(void *data)
273 {
274    Widget_Data *wd = elm_widget_data_get(data);
275    if (!wd) return ECORE_CALLBACK_CANCEL;
276
277    evas_object_smart_callback_call(data, SIG_REPEATED, NULL);
278    if (!wd->repeating)
279      {
280         wd->timer = NULL;
281         return ECORE_CALLBACK_CANCEL;
282      }
283
284    return ECORE_CALLBACK_RENEW;
285 }
286
287 static Eina_Bool
288 _autorepeat_initial_send(void *data)
289 {
290    Widget_Data *wd = elm_widget_data_get(data);
291    if (!wd) return ECORE_CALLBACK_CANCEL;
292
293    if (wd->timer) ecore_timer_del(wd->timer);
294    wd->repeating = EINA_TRUE;
295    _autorepeat_send(data);
296    wd->timer = ecore_timer_add(wd->ar_interval, _autorepeat_send, data);
297
298    return ECORE_CALLBACK_CANCEL;
299 }
300
301 static void
302 _signal_pressed(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
303 {
304    Widget_Data *wd = elm_widget_data_get(data);
305    if (!wd) return;
306
307    if (wd->statelabel[HIGHLIGHTED])
308      {
309         _set_label(data, wd->statelabel[HIGHLIGHTED]);
310      }
311    if ((wd->autorepeat) && (!wd->repeating))
312      {
313         if (wd->ar_threshold <= 0.0)
314           _autorepeat_initial_send(data); /* call immediately */
315         else
316           wd->timer = ecore_timer_add(wd->ar_threshold, _autorepeat_initial_send, data);
317      }
318
319    evas_object_smart_callback_call(data, SIG_PRESSED, NULL);
320 }
321
322 static void
323 _signal_unpressed(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
324 {
325    Widget_Data *wd = elm_widget_data_get(data);
326    if (!wd) return;
327    if (wd->statelabel[DEFAULT])
328      _set_label(data, wd->statelabel[DEFAULT]);
329
330    if (wd->timer)
331      {
332         ecore_timer_del(wd->timer);
333         wd->timer = NULL;
334      }
335    wd->repeating = EINA_FALSE;
336    evas_object_smart_callback_call(data, SIG_UNPRESSED, NULL);
337 }
338
339 static void
340 _signal_default_text_set(void *data, Evas_Object *obj, const char *emission, const char *source)
341 {
342    Widget_Data *wd = elm_widget_data_get(data);
343    if (!wd) return;
344    if (wd->statelabel[DEFAULT])
345      _set_label(data, wd->statelabel[DEFAULT]);
346 }
347
348 _elm_button_label_set(Evas_Object *obj, const char *item, const char *label)
349 {
350    ELM_CHECK_WIDTYPE(obj, widtype);
351    Widget_Data *wd = elm_widget_data_get(obj);
352    if (item && strcmp(item, "default")) return;
353    if (!wd) return;
354    eina_stringshare_replace(&wd->label, label);
355    if (label)
356      edje_object_signal_emit(wd->btn, "elm,state,text,visible", "elm");
357    else
358      edje_object_signal_emit(wd->btn, "elm,state,text,hidden", "elm");
359    edje_object_message_signal_process(wd->btn);
360    edje_object_part_text_set(wd->btn, "elm.text", label);
361    _sizing_eval(obj);
362 }
363
364 static const char *
365 _elm_button_label_get(const Evas_Object *obj, const char *item)
366 {
367    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
368    Widget_Data *wd = elm_widget_data_get(obj);
369    if (item && strcmp(item, "default")) return NULL;
370    if (!wd) return NULL;
371    return wd->label;
372 }
373
374 EAPI Evas_Object *
375 elm_button_add(Evas_Object *parent)
376 {
377    Evas_Object *obj;
378    Evas *e;
379    Widget_Data *wd;
380
381    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
382
383    ELM_SET_WIDTYPE(widtype, "button");
384    elm_widget_type_set(obj, "button");
385    elm_widget_sub_object_add(parent, obj);
386    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
387    elm_widget_data_set(obj, wd);
388    elm_widget_del_hook_set(obj, _del_hook);
389    elm_widget_theme_hook_set(obj, _theme_hook);
390    elm_widget_disable_hook_set(obj, _disable_hook);
391    elm_widget_can_focus_set(obj, EINA_TRUE);
392    elm_widget_activate_hook_set(obj, _activate_hook);
393    elm_widget_event_hook_set(obj, _event_hook);
394    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
395    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
396    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
397    elm_widget_text_set_hook_set(obj, _elm_button_label_set);
398    elm_widget_text_get_hook_set(obj, _elm_button_label_get);
399
400    wd->btn = edje_object_add(e);
401    _elm_theme_object_set(obj, wd->btn, "button", "base", "default");
402    wd->statetype[DEFAULT] = 0;
403    wd->statetype[HIGHLIGHTED] = 0;
404    wd->statetype[FOCUSED] = 0;
405    wd->statetype[DISABLED] = 0;
406    wd->statelabel[DEFAULT] = 0;
407    wd->statelabel[HIGHLIGHTED] = 0;
408    wd->statelabel[FOCUSED] = 0;
409    wd->statelabel[DISABLED] = 0;
410    edje_object_signal_callback_add(wd->btn, "elm,action,click", "",
411                                    _signal_clicked, obj);
412    edje_object_signal_callback_add(wd->btn, "elm,action,press", "",
413                                    _signal_pressed, obj);
414    edje_object_signal_callback_add(wd->btn, "elm,action,unpress", "",
415                                    _signal_unpressed, obj);
416    edje_object_signal_callback_add(wd->btn, "elm,action,default,text,set", "",
417                                    _signal_default_text_set, obj);
418    elm_widget_resize_object_set(obj, wd->btn);
419
420    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
421
422    _theme_hook(obj);
423
424    // TODO: convert Elementary to subclassing of Evas_Smart_Class
425    // TODO: and save some bytes, making descriptions per-class and not instance!
426    evas_object_smart_callbacks_descriptions_set(obj, _signals);
427    return obj;
428 }
429
430 static void
431 _set_label(Evas_Object *obj, const char *label)
432 {
433    Widget_Data *wd = elm_widget_data_get(obj);
434
435    edje_object_part_text_set(wd->btn, "elm.text", label);
436    _sizing_eval(obj);
437 }
438
439 EAPI void
440 elm_button_label_set_for_state(Evas_Object *obj, const char *label, UIControlState state)
441 {
442    Widget_Data *wd = elm_widget_data_get(obj);
443
444    if (!wd) return;
445    if (label == NULL) return;
446
447    if (state == UIControlStateDefault)
448      {
449         wd->statetype[DEFAULT] = UIControlStateDefault;
450         eina_stringshare_replace(&wd->statelabel[DEFAULT], label);
451         return;
452      }
453    if (state == UIControlStateHighlighted)
454      {
455         wd->statetype[HIGHLIGHTED] = UIControlStateHighlighted;
456         eina_stringshare_replace(&wd->statelabel[HIGHLIGHTED], label);
457         return;
458      }
459    if (state == UIControlStateFocused)
460      {
461         wd->statetype[FOCUSED] = UIControlStateFocused;
462         eina_stringshare_replace(&wd->statelabel[FOCUSED], label);
463         return;
464      }
465    if (state == UIControlStateDisabled)
466      {
467         wd->statetype[DISABLED] = UIControlStateDisabled;
468         eina_stringshare_replace(&wd->statelabel[DISABLED], label);
469         return;
470      }
471 }
472
473 EAPI const char*
474 elm_button_label_get_for_state(const Evas_Object *obj, UIControlState state)
475 {
476    Widget_Data *wd = elm_widget_data_get(obj);
477    if (!wd) return NULL;
478
479    if (state == UIControlStateDefault)
480      return wd->statelabel[DEFAULT];
481    else if (state == UIControlStateHighlighted)
482      return wd->statelabel[HIGHLIGHTED];
483    else if (state == UIControlStateFocused)
484      return wd->statelabel[FOCUSED];
485    else if (state == UIControlStateDisabled)
486      return wd->statelabel[DISABLED];
487    else
488      return NULL;
489 }
490
491 EAPI void
492 elm_button_label_set(Evas_Object *obj, const char *label)
493 {
494    _elm_button_label_set(obj, NULL, label);
495 }
496
497 EAPI const char *
498 elm_button_label_get(const Evas_Object *obj)
499 {
500    return _elm_button_label_get(obj, NULL);
501 }
502
503 EAPI void
504 elm_button_icon_set(Evas_Object *obj, Evas_Object *icon)
505 {
506    ELM_CHECK_WIDTYPE(obj, widtype);
507    Widget_Data *wd = elm_widget_data_get(obj);
508    if (!wd) return;
509    if (wd->icon == icon) return;
510    if (wd->icon) evas_object_del(wd->icon);
511    wd->icon = icon;
512    if (icon)
513      {
514         elm_widget_sub_object_add(obj, icon);
515         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
516                                        _changed_size_hints, obj);
517         edje_object_part_swallow(wd->btn, "elm.swallow.content", icon);
518         edje_object_signal_emit(wd->btn, "elm,state,icon,visible", "elm");
519         edje_object_message_signal_process(wd->btn);
520      }
521    _sizing_eval(obj);
522 }
523
524 EAPI Evas_Object *
525 elm_button_icon_get(const Evas_Object *obj)
526 {
527    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
528    Widget_Data *wd = elm_widget_data_get(obj);
529    if (!wd) return NULL;
530    return wd->icon;
531 }
532
533 EAPI Evas_Object *
534 elm_button_icon_unset(Evas_Object *obj)
535 {
536    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
537    Widget_Data *wd = elm_widget_data_get(obj);
538    if (!wd) return NULL;
539    if (!wd->icon) return NULL;
540    Evas_Object *icon = wd->icon;
541    elm_widget_sub_object_del(obj, wd->icon);
542    edje_object_part_unswallow(wd->btn, wd->icon);
543    wd->icon = NULL;
544    return icon;
545 }
546
547 EAPI void
548 elm_button_autorepeat_set(Evas_Object *obj, Eina_Bool on)
549 {
550    ELM_CHECK_WIDTYPE(obj, widtype);
551    Widget_Data *wd = elm_widget_data_get(obj);
552    if (!wd) return;
553    if (wd->timer)
554      {
555         ecore_timer_del(wd->timer);
556         wd->timer = NULL;
557      }
558    wd->autorepeat = on;
559    wd->repeating = EINA_FALSE;
560 }
561
562 EAPI Eina_Bool
563 elm_button_autorepeat_get(const Evas_Object *obj)
564 {
565    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
566    Widget_Data *wd = elm_widget_data_get(obj);
567    if (!wd) return EINA_FALSE;
568    return wd->autorepeat;
569 }
570
571 EAPI void
572 elm_button_autorepeat_initial_timeout_set(Evas_Object *obj, double t)
573 {
574    ELM_CHECK_WIDTYPE(obj, widtype);
575    Widget_Data *wd = elm_widget_data_get(obj);
576    if (!wd) return;
577    if (wd->ar_threshold == t) return;
578    if (wd->timer)
579      {
580         ecore_timer_del(wd->timer);
581         wd->timer = NULL;
582      }
583    wd->ar_threshold = t;
584 }
585
586 EAPI double
587 elm_button_autorepeat_initial_timeout_get(const Evas_Object *obj)
588 {
589    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
590    Widget_Data *wd = elm_widget_data_get(obj);
591    if (!wd) return 0.0;
592    return wd->ar_threshold;
593 }
594
595 EAPI void
596 elm_button_autorepeat_gap_timeout_set(Evas_Object *obj, double t)
597 {
598    ELM_CHECK_WIDTYPE(obj, widtype);
599    Widget_Data *wd = elm_widget_data_get(obj);
600    if (!wd) return;
601    if (wd->ar_interval == t) return;
602
603    wd->ar_interval = t;
604    if ((wd->repeating) && (wd->timer)) ecore_timer_interval_set(wd->timer, t);
605 }
606
607 EAPI double
608 elm_button_autorepeat_gap_timeout_get(const Evas_Object *obj)
609 {
610    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
611    Widget_Data *wd = elm_widget_data_get(obj);
612    if (!wd) return 0.0;
613    return wd->ar_interval;
614 }