[elm] Tweeks on button inheritance.
[platform/upstream/elementary.git] / src / lib / elm_button.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3 #include "elm_widget_button.h"
4
5 EAPI const char ELM_BUTTON_SMART_NAME[] = "elm_button";
6
7 static const char SIG_CLICKED[] = "clicked";
8 static const char SIG_REPEATED[] = "repeated";
9 static const char SIG_PRESSED[] = "pressed";
10 static const char SIG_UNPRESSED[] = "unpressed";
11
12 static const Elm_Layout_Part_Alias_Description _content_aliases[] =
13 {
14    {"icon", "elm.swallow.content"},
15    {NULL, NULL}
16 };
17
18 static const Elm_Layout_Part_Alias_Description _text_aliases[] =
19 {
20    {"default", "elm.text"},
21    {NULL, NULL}
22 };
23
24 /* smart callbacks coming from elm button objects (besides the ones
25  * coming from elm layout): */
26 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
27    {SIG_CLICKED, ""},
28    {SIG_REPEATED, ""},
29    {SIG_PRESSED, ""},
30    {SIG_UNPRESSED, ""},
31    {NULL, NULL}
32 };
33
34 /* Inheriting from elm_layout. Besides, we need no more than what is
35  * there */
36 EVAS_SMART_SUBCLASS_NEW
37   (ELM_BUTTON_SMART_NAME, _elm_button, Elm_Button_Smart_Class,
38   Elm_Layout_Smart_Class, elm_layout_smart_class_get, _smart_callbacks);
39
40 static void
41 _activate(Evas_Object *obj)
42 {
43    ELM_BUTTON_DATA_GET_OR_RETURN(obj, sd);
44
45    if (sd->timer)
46      {
47         ecore_timer_del(sd->timer);
48         sd->timer = NULL;
49      }
50
51    sd->repeating = EINA_FALSE;
52
53    if ((_elm_config->access_mode == ELM_ACCESS_MODE_OFF) ||
54        (_elm_access_2nd_click_timeout(obj)))
55      {
56         if (_elm_config->access_mode != ELM_ACCESS_MODE_OFF)
57           _elm_access_say(E_("Clicked"));
58         if (!elm_widget_disabled_get(obj) &&
59             !evas_object_freeze_events_get(obj))
60           evas_object_smart_callback_call(obj, SIG_CLICKED, NULL);
61      }
62 }
63
64 static void
65 _elm_button_smart_sizing_eval(Evas_Object *obj)
66 {
67    Evas_Coord minw = -1, minh = -1;
68
69    ELM_BUTTON_DATA_GET(obj, sd);
70
71    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
72    edje_object_size_min_restricted_calc
73      (ELM_WIDGET_DATA(sd)->resize_obj, &minw, &minh, minw, minh);
74    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
75    evas_object_size_hint_min_set(obj, minw, minh);
76 }
77
78 /* FIXME: replicated from elm_layout just because button's icon spot
79  * is elm.swallow.content, not elm.swallow.icon. Fix that whenever we
80  * can changed the theme API */
81 static void
82 _icon_signal_emit(Evas_Object *obj)
83 {
84    char buf[64];
85
86    snprintf(buf, sizeof(buf), "elm,state,icon,%s",
87             elm_layout_content_get(obj, "icon") ? "visible" : "hidden");
88
89    elm_layout_signal_emit(obj, buf, "elm");
90    edje_object_message_signal_process(elm_layout_edje_get(obj));
91    _elm_button_smart_sizing_eval(obj);
92 }
93
94 /* FIXME: replicated from elm_layout just because button's icon spot
95  * is elm.swallow.content, not elm.swallow.icon. Fix that whenever we
96  * can changed the theme API */
97 static Eina_Bool
98 _elm_button_smart_theme(Evas_Object *obj)
99 {
100    if (!ELM_WIDGET_CLASS(_elm_button_parent_sc)->theme(obj)) return EINA_FALSE;
101
102    _icon_signal_emit(obj);
103
104    return EINA_TRUE;
105 }
106
107 /* FIXME: replicated from elm_layout just because button's icon spot
108  * is elm.swallow.content, not elm.swallow.icon. Fix that whenever we
109  * can changed the theme API */
110 static Eina_Bool
111 _elm_button_smart_sub_object_del(Evas_Object *obj,
112                                  Evas_Object *sobj)
113 {
114    if (!ELM_WIDGET_CLASS(_elm_button_parent_sc)->sub_object_del(obj, sobj))
115      return EINA_FALSE;
116
117    _icon_signal_emit(obj);
118
119    return EINA_TRUE;
120 }
121
122 /* FIXME: replicated from elm_layout just because button's icon spot
123  * is elm.swallow.content, not elm.swallow.icon. Fix that whenever we
124  * can changed the theme API */
125 static Eina_Bool
126 _elm_button_smart_content_set(Evas_Object *obj,
127                               const char *part,
128                               Evas_Object *content)
129 {
130    if (!ELM_CONTAINER_CLASS(_elm_button_parent_sc)->content_set
131          (obj, part, content))
132      return EINA_FALSE;
133
134    _icon_signal_emit(obj);
135
136    return EINA_TRUE;
137 }
138
139 static Eina_Bool
140 _elm_button_smart_event(Evas_Object *obj,
141                         Evas_Object *src __UNUSED__,
142                         Evas_Callback_Type type,
143                         void *event_info)
144 {
145    Evas_Event_Key_Down *ev = event_info;
146
147    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
148
149    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
150    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
151
152    if ((strcmp(ev->keyname, "Return")) &&
153        (strcmp(ev->keyname, "KP_Enter")) &&
154        (strcmp(ev->keyname, "space")))
155      return EINA_FALSE;
156
157    _activate(obj);
158    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
159    elm_layout_signal_emit(obj, "elm,anim,activate", "elm");
160
161    return EINA_TRUE;
162 }
163
164 static void
165 _on_clicked_signal(void *data,
166                    Evas_Object *obj __UNUSED__,
167                    const char *emission __UNUSED__,
168                    const char *source __UNUSED__)
169 {
170    _activate(data);
171 }
172
173 static Eina_Bool
174 _autorepeat_send(void *data)
175 {
176    ELM_BUTTON_DATA_GET_OR_RETURN_VAL(data, sd, ECORE_CALLBACK_CANCEL);
177
178    evas_object_smart_callback_call(data, SIG_REPEATED, NULL);
179    if (!sd->repeating)
180      {
181         sd->timer = NULL;
182         return ECORE_CALLBACK_CANCEL;
183      }
184
185    return ECORE_CALLBACK_RENEW;
186 }
187
188 static Eina_Bool
189 _autorepeat_initial_send(void *data)
190 {
191    ELM_BUTTON_DATA_GET_OR_RETURN_VAL(data, sd, ECORE_CALLBACK_CANCEL);
192
193    if (sd->timer) ecore_timer_del(sd->timer);
194    sd->repeating = EINA_TRUE;
195    _autorepeat_send(data);
196    sd->timer = ecore_timer_add(sd->ar_interval, _autorepeat_send, data);
197
198    return ECORE_CALLBACK_CANCEL;
199 }
200
201 static void
202 _on_pressed_signal(void *data,
203                    Evas_Object *obj __UNUSED__,
204                    const char *emission __UNUSED__,
205                    const char *source __UNUSED__)
206 {
207    ELM_BUTTON_DATA_GET_OR_RETURN(data, sd);
208
209    if ((sd->autorepeat) && (!sd->repeating))
210      {
211         if (sd->ar_threshold <= 0.0)
212           _autorepeat_initial_send(data);  /* call immediately */
213         else
214           sd->timer = ecore_timer_add
215               (sd->ar_threshold, _autorepeat_initial_send, data);
216      }
217
218    evas_object_smart_callback_call(data, SIG_PRESSED, NULL);
219 }
220
221 static void
222 _on_unpressed_signal(void *data,
223                      Evas_Object *obj __UNUSED__,
224                      const char *emission __UNUSED__,
225                      const char *source __UNUSED__)
226 {
227    ELM_BUTTON_DATA_GET_OR_RETURN(data, sd);
228
229    if (sd->timer)
230      {
231         ecore_timer_del(sd->timer);
232         sd->timer = NULL;
233      }
234    sd->repeating = EINA_FALSE;
235    evas_object_smart_callback_call(data, SIG_UNPRESSED, NULL);
236 }
237
238 static char *
239 _access_info_cb(void *data __UNUSED__,
240                 Evas_Object *obj,
241                 Elm_Widget_Item *item __UNUSED__)
242 {
243    const char *txt = elm_widget_access_info_get(obj);
244
245    if (!txt) txt = elm_layout_text_get(obj, NULL);
246    if (txt) return strdup(txt);
247
248    return NULL;
249 }
250
251 static char *
252 _access_state_cb(void *data __UNUSED__,
253                  Evas_Object *obj,
254                  Elm_Widget_Item *item __UNUSED__)
255 {
256    if (elm_widget_disabled_get(obj))
257      return strdup(E_("State: Disabled"));
258
259    return NULL;
260 }
261
262 static void
263 _elm_button_smart_add(Evas_Object *obj)
264 {
265    EVAS_SMART_DATA_ALLOC(obj, Elm_Button_Smart_Data);
266
267    ELM_WIDGET_CLASS(_elm_button_parent_sc)->base.add(obj);
268
269    edje_object_signal_callback_add
270      (ELM_WIDGET_DATA(priv)->resize_obj, "elm,action,click", "",
271      _on_clicked_signal, obj);
272    edje_object_signal_callback_add
273      (ELM_WIDGET_DATA(priv)->resize_obj, "elm,action,press", "",
274      _on_pressed_signal, obj);
275    edje_object_signal_callback_add
276      (ELM_WIDGET_DATA(priv)->resize_obj, "elm,action,unpress", "",
277      _on_unpressed_signal, obj);
278
279    _elm_access_object_register(obj, ELM_WIDGET_DATA(priv)->resize_obj);
280    _elm_access_text_set
281      (_elm_access_object_get(obj), ELM_ACCESS_TYPE, E_("Button"));
282    _elm_access_callback_set
283      (_elm_access_object_get(obj), ELM_ACCESS_INFO, _access_info_cb, NULL);
284    _elm_access_callback_set
285      (_elm_access_object_get(obj), ELM_ACCESS_STATE, _access_state_cb, priv);
286
287    elm_widget_can_focus_set(obj, EINA_TRUE);
288
289    elm_layout_theme_set(obj, "button", "base", elm_widget_style_get(obj));
290 }
291
292 static void
293 _elm_button_smart_set_user(Elm_Button_Smart_Class *sc)
294 {
295    ELM_WIDGET_CLASS(sc)->base.add = _elm_button_smart_add;
296
297    ELM_WIDGET_CLASS(sc)->event = _elm_button_smart_event;
298    ELM_WIDGET_CLASS(sc)->theme = _elm_button_smart_theme;
299    ELM_WIDGET_CLASS(sc)->sub_object_del = _elm_button_smart_sub_object_del;
300
301    /* not a 'focus chain manager' */
302    ELM_WIDGET_CLASS(sc)->focus_next = NULL;
303    ELM_WIDGET_CLASS(sc)->focus_direction = NULL;
304
305    ELM_CONTAINER_CLASS(sc)->content_set = _elm_button_smart_content_set;
306
307    ELM_LAYOUT_CLASS(sc)->sizing_eval = _elm_button_smart_sizing_eval;
308
309    ELM_LAYOUT_CLASS(sc)->content_aliases = _content_aliases;
310    ELM_LAYOUT_CLASS(sc)->text_aliases = _text_aliases;
311
312    sc->admits_autorepeat = EINA_TRUE;
313 }
314
315 EAPI const Elm_Button_Smart_Class *
316 elm_button_smart_class_get(void)
317 {
318    static Elm_Button_Smart_Class _sc =
319      ELM_BUTTON_SMART_CLASS_INIT_NAME_VERSION(ELM_BUTTON_SMART_NAME);
320    static const Elm_Button_Smart_Class *class = NULL;
321    Evas_Smart_Class *esc = (Evas_Smart_Class *)&_sc;
322
323    if (class) return class;
324
325    _elm_button_smart_set(&_sc);
326    esc->callbacks = _smart_callbacks;
327    class = &_sc;
328
329    return class;
330 }
331
332 EAPI Evas_Object *
333 elm_button_add(Evas_Object *parent)
334 {
335    Evas_Object *obj;
336
337    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
338
339    obj = elm_widget_add(_elm_button_smart_class_new(), parent);
340    if (!obj) return NULL;
341
342    if (!elm_widget_sub_object_add(parent, obj))
343      ERR("could not add %p as sub object of %p", obj, parent);
344
345    return obj;
346 }
347
348 EAPI void
349 elm_button_autorepeat_set(Evas_Object *obj,
350                           Eina_Bool on)
351 {
352    ELM_BUTTON_CHECK(obj);
353    ELM_BUTTON_DATA_GET_OR_RETURN(obj, sd);
354
355    if (sd->timer)
356      {
357         ecore_timer_del(sd->timer);
358         sd->timer = NULL;
359      }
360    sd->autorepeat = on;
361    sd->repeating = EINA_FALSE;
362 }
363
364 #define _AR_CAPABLE(_sd) \
365   (ELM_BUTTON_CLASS(ELM_WIDGET_DATA(_sd)->api)->admits_autorepeat)
366
367 EAPI Eina_Bool
368 elm_button_autorepeat_get(const Evas_Object *obj)
369 {
370    ELM_BUTTON_CHECK(obj) EINA_FALSE;
371    ELM_BUTTON_DATA_GET_OR_RETURN_VAL(obj, sd, EINA_FALSE);
372
373    return _AR_CAPABLE(sd) & sd->autorepeat;
374 }
375
376 EAPI void
377 elm_button_autorepeat_initial_timeout_set(Evas_Object *obj,
378                                           double t)
379 {
380    ELM_BUTTON_CHECK(obj);
381    ELM_BUTTON_DATA_GET_OR_RETURN(obj, sd);
382
383    if (!_AR_CAPABLE(sd))
384      {
385         ERR("this widget does not support auto repetition of clicks.");
386         return;
387      }
388
389    if (sd->ar_threshold == t) return;
390    if (sd->timer)
391      {
392         ecore_timer_del(sd->timer);
393         sd->timer = NULL;
394      }
395    sd->ar_threshold = t;
396 }
397
398 EAPI double
399 elm_button_autorepeat_initial_timeout_get(const Evas_Object *obj)
400 {
401    ELM_BUTTON_CHECK(obj) 0.0;
402    ELM_BUTTON_DATA_GET_OR_RETURN_VAL(obj, sd, 0.0);
403
404    if (!_AR_CAPABLE(sd)) return 0.0;
405
406    return sd->ar_threshold;
407 }
408
409 EAPI void
410 elm_button_autorepeat_gap_timeout_set(Evas_Object *obj,
411                                       double t)
412 {
413    ELM_BUTTON_CHECK(obj);
414    ELM_BUTTON_DATA_GET_OR_RETURN(obj, sd);
415
416    if (!_AR_CAPABLE(sd))
417      {
418         ERR("this widget does not support auto repetition of clicks.");
419         return;
420      }
421
422    if (sd->ar_interval == t) return;
423
424    sd->ar_interval = t;
425    if ((sd->repeating) && (sd->timer)) ecore_timer_interval_set(sd->timer, t);
426 }
427
428 EAPI double
429 elm_button_autorepeat_gap_timeout_get(const Evas_Object *obj)
430 {
431    ELM_BUTTON_CHECK(obj) 0.0;
432    ELM_BUTTON_DATA_GET_OR_RETURN_VAL(obj, sd, 0.0);
433
434    return sd->ar_interval;
435 }