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