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