Proxy Elementary widget signals to Edje.
[platform/upstream/elementary.git] / src / lib / elm_button.c
1 /*
2  *
3  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
4  */
5 #include <Elementary.h>
6 #include "elm_priv.h"
7
8 /**
9  * @defgroup Button Button
10  *
11  * This is a push-button. Press it and run some function. It can contain
12  * a simple label and icon object.
13  */
14
15 typedef struct _Widget_Data Widget_Data;
16
17 struct _Widget_Data
18 {
19    Evas_Object *btn, *icon;
20    const char *label;
21
22    Eina_Bool autorepeat;
23    Eina_Bool repeating;
24    double ar_threshold;
25    double ar_interval;
26    Ecore_Timer *timer;
27 };
28
29 static const char *widtype = NULL;
30 static void _del_hook(Evas_Object *obj);
31 static void _theme_hook(Evas_Object *obj);
32 static void _disable_hook(Evas_Object *obj);
33 static void _sizing_eval(Evas_Object *obj);
34 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
35 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
36 static void _signal_clicked(void *data, Evas_Object *obj, const char *emission, const char *source);
37 static void _signal_pressed(void *data, Evas_Object *obj, const char *emission, const char *source);
38 static void _signal_unpressed(void *data, Evas_Object *obj, const char *emission, const char *source);
39 static void _on_focus_hook(void *data, Evas_Object *obj);
40
41 static const char SIG_CLICKED[] = "clicked";
42 static const char SIG_REPEATED[] = "repeated";
43 static const char SIG_UNPRESSED[] = "unpressed";
44 static const Evas_Smart_Cb_Description _signals[] = {
45   {SIG_CLICKED, ""},
46   {SIG_REPEATED, ""},
47   {SIG_UNPRESSED, ""},
48   {NULL, NULL}
49 };
50
51 static void
52 _del_hook(Evas_Object *obj)
53 {
54    Widget_Data *wd = elm_widget_data_get(obj);
55    if (!wd) return;
56    if (wd->label) eina_stringshare_del(wd->label);
57    free(wd);
58 }
59
60 static void
61 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
62 {
63    Widget_Data *wd = elm_widget_data_get(obj);
64    if (!wd) return;
65    if (elm_widget_focus_get(obj))
66      edje_object_signal_emit(wd->btn, "elm,action,focus", "elm");
67    else
68      edje_object_signal_emit(wd->btn, "elm,action,unfocus", "elm");
69 }
70
71 static void
72 _theme_hook(Evas_Object *obj)
73 {
74    Widget_Data *wd = elm_widget_data_get(obj);
75    if (!wd) return;
76    _elm_theme_set(wd->btn, "button", "base", elm_widget_style_get(obj));
77    if (wd->icon)
78      edje_object_part_swallow(wd->btn, "elm.swallow.content", wd->icon);
79    if (wd->label)
80      edje_object_signal_emit(wd->btn, "elm,state,text,visible", "elm");
81    else
82      edje_object_signal_emit(wd->btn, "elm,state,text,hidden", "elm");
83    if (wd->icon)
84      edje_object_signal_emit(wd->btn, "elm,state,icon,visible", "elm");
85    else
86      edje_object_signal_emit(wd->btn, "elm,state,icon,hidden", "elm");
87    edje_object_part_text_set(wd->btn, "elm.text", wd->label);
88    edje_object_message_signal_process(wd->btn);
89    edje_object_scale_set(wd->btn, elm_widget_scale_get(obj) * _elm_config->scale);
90    _sizing_eval(obj);
91 }
92
93 static void
94 _disable_hook(Evas_Object *obj)
95 {
96    Widget_Data *wd = elm_widget_data_get(obj);
97    if (!wd) return;
98    if (elm_widget_disabled_get(obj))
99      edje_object_signal_emit(wd->btn, "elm,state,disabled", "elm");
100    else
101      edje_object_signal_emit(wd->btn, "elm,state,enabled", "elm");
102 }
103
104 static void
105 _sizing_eval(Evas_Object *obj)
106 {
107    Widget_Data *wd = elm_widget_data_get(obj);
108    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
109
110    if (!wd) return;
111    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
112    edje_object_size_min_restricted_calc(wd->btn, &minw, &minh, minw, minh);
113    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
114    evas_object_size_hint_min_set(obj, minw, minh);
115    evas_object_size_hint_max_set(obj, maxw, maxh);
116 }
117
118 static void
119 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
120 {
121    Widget_Data *wd = elm_widget_data_get(data);
122    if (!wd) return;
123    if (obj != wd->icon) return;
124    _sizing_eval(data);
125 }
126
127 static void
128 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
129 {
130    Widget_Data *wd = elm_widget_data_get(obj);
131    Evas_Object *sub = event_info;
132    if (!wd) return;
133    if (sub == wd->icon)
134      {
135         edje_object_signal_emit(wd->btn, "elm,state,icon,hidden", "elm");
136         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
137                                        _changed_size_hints, obj);
138         wd->icon = NULL;
139         edje_object_message_signal_process(wd->btn);
140         _sizing_eval(obj);
141      }
142 }
143
144 static void
145 _signal_clicked(void *data, Evas_Object *obj, const char *emission, const char *source)
146 {
147    Widget_Data *wd = elm_widget_data_get(data);
148    if (!wd) return;
149    evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
150    _signal_unpressed(data, obj, emission, source); /* safe guard when the theme does not emit the 'unpress' signal */
151 }
152
153 static int
154 _autorepeat_send(void *data)
155 {
156    Widget_Data *wd = elm_widget_data_get(data);
157    if (!wd) return ECORE_CALLBACK_CANCEL;
158
159    evas_object_smart_callback_call(data, SIG_REPEATED, NULL);
160
161    return ECORE_CALLBACK_RENEW;
162 }
163
164 static int
165 _autorepeat_initial_send(void *data)
166 {
167    Widget_Data *wd = elm_widget_data_get(data);
168    if (!wd) return ECORE_CALLBACK_CANCEL;
169
170    _autorepeat_send(data);
171    wd->timer = ecore_timer_add(wd->ar_interval, _autorepeat_send, data);
172    wd->repeating = 1;
173
174    return ECORE_CALLBACK_CANCEL;
175 }
176
177 static void
178 _signal_pressed(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
179 {
180    Widget_Data *wd = elm_widget_data_get(data);
181    if (!wd) return;
182
183    if (wd->autorepeat)
184      {
185         if (wd->ar_threshold <= 0.0)
186           _autorepeat_initial_send(data); /* call immediately */
187         else
188           wd->timer = ecore_timer_add(wd->ar_threshold, _autorepeat_initial_send, data);
189      }
190 }
191
192 static void
193 _signal_unpressed(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
194 {
195    Widget_Data *wd = elm_widget_data_get(data);
196    if (!wd) return;
197    evas_object_smart_callback_call(data, SIG_UNPRESSED, NULL);
198
199    if (wd->timer)
200      {
201         ecore_timer_del(wd->timer);
202         wd->timer = NULL;
203      }
204    wd->repeating = 0;
205 }
206
207 /**
208  * Add a new button to the parent
209  * @param parent The parent object
210  * @return The new object or NULL if it cannot be created
211  *
212  * @ingroup Button
213  */
214 EAPI Evas_Object *
215 elm_button_add(Evas_Object *parent)
216 {
217    Evas_Object *obj;
218    Evas *e;
219    Widget_Data *wd;
220
221    wd = ELM_NEW(Widget_Data);
222    e = evas_object_evas_get(parent);
223    obj = elm_widget_add(e);
224    ELM_SET_WIDTYPE(widtype, "button");
225    elm_widget_type_set(obj, "button");
226    elm_widget_sub_object_add(parent, obj);
227    elm_widget_on_focus_hook_set( obj, _on_focus_hook, NULL );
228    elm_widget_data_set(obj, wd);
229    elm_widget_del_hook_set(obj, _del_hook);
230    elm_widget_theme_hook_set(obj, _theme_hook);
231    elm_widget_disable_hook_set(obj, _disable_hook);
232    elm_widget_can_focus_set(obj, 1 );                 
233
234    wd->btn = edje_object_add(e);
235    _elm_theme_set(wd->btn, "button", "base", "default");
236    edje_object_signal_callback_add(wd->btn, "elm,action,click", "",
237                                    _signal_clicked, obj);
238    edje_object_signal_callback_add(wd->btn, "elm,action,press", "",
239                                    _signal_pressed, obj);
240    edje_object_signal_callback_add(wd->btn, "elm,action,unpress", "",
241                                    _signal_unpressed, obj);
242    elm_widget_resize_object_set(obj, wd->btn);
243
244    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
245
246    _sizing_eval(obj);
247
248    // TODO: convert Elementary to subclassing of Evas_Smart_Class
249    // TODO: and save some bytes, making descriptions per-class and not instance!
250    evas_object_smart_callbacks_descriptions_set(obj, _signals);
251    return obj;
252 }
253
254 /**
255  * Set the label used in the button
256  *
257  * @param obj The button object
258  * @param label The text will be written on the button
259  *
260  * @ingroup Button
261  */
262 EAPI void
263 elm_button_label_set(Evas_Object *obj, const char *label)
264 {
265    ELM_CHECK_WIDTYPE(obj, widtype);
266    Widget_Data *wd = elm_widget_data_get(obj);
267    if (!wd) return;
268    eina_stringshare_replace(&wd->label, label);
269    if (label)
270      edje_object_signal_emit(wd->btn, "elm,state,text,visible", "elm");
271    else
272      edje_object_signal_emit(wd->btn, "elm,state,text,hidden", "elm");
273    edje_object_message_signal_process(wd->btn);
274    edje_object_part_text_set(wd->btn, "elm.text", label);
275    _sizing_eval(obj);
276 }
277
278 EAPI const char *
279 elm_button_label_get(const Evas_Object *obj)
280 {
281    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
282    Widget_Data *wd = elm_widget_data_get(obj);
283    if (!wd) return NULL;
284    return wd->label;
285 }
286
287 /**
288  * Set the icon used for the button
289  *
290  * @param obj The button object
291  * @param icon  The image for the button
292  *
293  * @ingroup Button
294  */
295 EAPI void
296 elm_button_icon_set(Evas_Object *obj, Evas_Object *icon)
297 {
298    ELM_CHECK_WIDTYPE(obj, widtype);
299    Widget_Data *wd = elm_widget_data_get(obj);
300    if (!wd) return;
301    if ((wd->icon != icon) && (wd->icon))
302      elm_widget_sub_object_del(obj, wd->icon);
303    if ((icon) && (wd->icon != icon))
304      {
305         wd->icon = icon;
306         elm_widget_sub_object_add(obj, icon);
307         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
308                                        _changed_size_hints, obj);
309         edje_object_part_swallow(wd->btn, "elm.swallow.content", icon);
310         edje_object_signal_emit(wd->btn, "elm,state,icon,visible", "elm");
311         edje_object_message_signal_process(wd->btn);
312         _sizing_eval(obj);
313      }
314    else
315      wd->icon = icon;
316 }
317
318 /**
319  * Get the icon used for the button
320  *
321  * @param obj The button object
322  * @return The image for the button
323  *
324  * @ingroup Button
325  */
326 EAPI Evas_Object *
327 elm_button_icon_get(const Evas_Object *obj)
328 {
329    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
330    Widget_Data *wd = elm_widget_data_get(obj);
331    if (!wd) return NULL;
332    return wd->icon;
333 }
334
335 /**
336  * Turn on/off the autorepeat event generated when the user keeps pressing on the button
337  *
338  * @param obj The button object
339  * @param on  A bool to turn on/off the event
340  *
341  * @ingroup Button
342  */
343 EAPI void
344 elm_button_autorepeat_set(Evas_Object *obj, Eina_Bool on)
345 {
346    ELM_CHECK_WIDTYPE(obj, widtype);
347    Widget_Data *wd = elm_widget_data_get(obj);
348    if (!wd) return;
349    if (wd->timer)
350      {
351         ecore_timer_del(wd->timer);
352         wd->timer = NULL;
353      }
354    wd->autorepeat = on;
355 }
356
357 /**
358  * Set the initial timeout before the autorepeat event is generated
359  *
360  * @param obj The button object
361  * @param t   Timeout
362  *
363  * @ingroup Button
364  */
365 EAPI void
366 elm_button_autorepeat_initial_timeout_set(Evas_Object *obj, double t)
367 {
368    ELM_CHECK_WIDTYPE(obj, widtype);
369    Widget_Data *wd = elm_widget_data_get(obj);
370    if (!wd) return;
371    if (wd->ar_threshold == t) return;
372    if (wd->timer)
373      {
374         ecore_timer_del(wd->timer);
375         wd->timer = NULL;
376      }
377    wd->ar_threshold = t;
378 }
379
380 /**
381  * Set the interval between each generated autorepeat event
382  *
383  * @param obj The button object
384  * @param t   Interval
385  *
386  * @ingroup Button
387  */
388 EAPI void         
389 elm_button_autorepeat_gap_timeout_set(Evas_Object *obj, double t)
390 {
391    ELM_CHECK_WIDTYPE(obj, widtype);
392    Widget_Data *wd = elm_widget_data_get(obj);
393    if (!wd) return;
394    if (wd->ar_interval == t) return;
395    if (wd->timer)
396      {
397         ecore_timer_del(wd->timer);
398         wd->timer = NULL;
399      }
400    wd->ar_interval = t;
401    if (wd->repeating)
402      wd->timer = ecore_timer_add(t, _autorepeat_send, obj);
403 }
404