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