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