80b2fc163120678cde070ee9a39a4e315bc2e51b
[framework/uifw/elementary.git] / src / lib / elm_toggle.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Toggle
6  *
7  * A toggle is a slider which can be used to toggle between
8  * two values.  It has two states: on and off.
9  *
10  * Signals that you can add callbacks for are:
11  *
12  * changed - Whenever the toggle value has been changed.  Is not called
13  * until the toggle is released by the cursor (assuming it has been triggered
14  * by the cursor in the first place).
15  */
16
17 typedef struct _Widget_Data Widget_Data;
18
19 struct _Widget_Data
20 {
21    Evas_Object *tgl;
22    Evas_Object *icon;
23    Eina_Bool state;
24    Eina_Bool *statep;
25    const char *label;
26    const char *ontext, *offtext;
27 };
28
29 static const char *widtype = NULL;
30 static void _del_hook(Evas_Object *obj);
31 static void _disable_hook(Evas_Object *obj);
32 static void _theme_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_toggle_off(void *data, Evas_Object *obj, const char *emission, const char *source);
37 static void _signal_toggle_on(void *data, Evas_Object *obj, const char *emission, const char *source);
38 static void _on_focus_hook(void *data, Evas_Object *obj);
39 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
40                              Evas_Callback_Type type, void *event_info);
41
42 static const char SIG_CHANGED[] = "changed";
43 static const Evas_Smart_Cb_Description _signals[] = {
44   {SIG_CHANGED, ""},
45   {NULL, NULL}
46 };
47
48 static Eina_Bool
49 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
50 {
51    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
52    Evas_Event_Key_Down *ev = event_info;
53    Widget_Data *wd = elm_widget_data_get(obj);
54    if (!wd) return EINA_FALSE;
55    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
56    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
57    if ((strcmp(ev->keyname, "Return")) &&
58        (strcmp(ev->keyname, "KP_Enter")) &&
59        (strcmp(ev->keyname, "space")))
60      return EINA_FALSE;
61    elm_toggle_state_set(obj, !wd->state);
62    evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
63    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
64    return EINA_TRUE;
65 }
66
67 static void
68 _del_hook(Evas_Object *obj)
69 {
70    Widget_Data *wd = elm_widget_data_get(obj);
71    if (!wd) return;
72    if (wd->label) eina_stringshare_del(wd->label);
73    if (wd->ontext) eina_stringshare_del(wd->ontext);
74    if (wd->offtext) eina_stringshare_del(wd->offtext);
75    free(wd);
76 }
77
78 static void
79 _disable_hook(Evas_Object *obj)
80 {
81    Widget_Data *wd = elm_widget_data_get(obj);
82    if (!wd) return;
83    if (elm_widget_disabled_get(obj))
84      edje_object_signal_emit(wd->tgl, "elm,state,disabled", "elm");
85    else
86      edje_object_signal_emit(wd->tgl, "elm,state,enabled", "elm");
87 }
88
89 static void
90 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
91 {
92    Widget_Data *wd = elm_widget_data_get(obj);
93    if (!wd) return;
94    if (elm_widget_focus_get(obj))
95      {
96         edje_object_signal_emit(wd->tgl, "elm,action,focus", "elm");
97         evas_object_focus_set(wd->tgl, EINA_TRUE);
98      }
99    else
100      {
101         edje_object_signal_emit(wd->tgl, "elm,action,unfocus", "elm");
102         evas_object_focus_set(wd->tgl, EINA_FALSE);
103      }
104 }
105
106 static void
107 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
108 {
109    Widget_Data *wd = elm_widget_data_get(obj);
110    if (!wd) return;
111    edje_object_mirrored_set(wd->tgl, rtl);
112 }
113
114 static void
115 _theme_hook(Evas_Object *obj)
116 {
117    Widget_Data *wd = elm_widget_data_get(obj);
118    if (!wd) return;
119    _elm_widget_mirrored_reload(obj);
120    _mirrored_set(obj, elm_widget_mirrored_get(obj));
121    _elm_theme_object_set(obj, wd->tgl, "toggle", "base", elm_widget_style_get(obj));
122    if (wd->icon)
123      edje_object_signal_emit(wd->tgl, "elm,state,icon,visible", "elm");
124    else
125      edje_object_signal_emit(wd->tgl, "elm,state,icon,hidden", "elm");
126    if (wd->state)
127      edje_object_signal_emit(wd->tgl, "elm,state,toggle,on", "elm");
128    else
129      edje_object_signal_emit(wd->tgl, "elm,state,toggle,off", "elm");
130    if (wd->label)
131      edje_object_signal_emit(wd->tgl, "elm,state,text,visible", "elm");
132    else
133      edje_object_signal_emit(wd->tgl, "elm,state,text,hidden", "elm");
134    edje_object_part_text_set(wd->tgl, "elm.text", wd->label);
135    edje_object_part_text_set(wd->tgl, "elm.ontext", wd->ontext);
136    edje_object_part_text_set(wd->tgl, "elm.offtext", wd->offtext);
137    if (elm_widget_disabled_get(obj))
138      edje_object_signal_emit(wd->tgl, "elm,state,disabled", "elm");
139    edje_object_message_signal_process(wd->tgl);
140    edje_object_scale_set(wd->tgl, elm_widget_scale_get(obj) * _elm_config->scale);
141    _sizing_eval(obj);
142 }
143
144 static void
145 _sizing_eval(Evas_Object *obj)
146 {
147    Widget_Data *wd = elm_widget_data_get(obj);
148    Evas_Coord minw = -1, minh = -1;
149
150    if (!wd) return;
151    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
152    edje_object_size_min_restricted_calc(wd->tgl, &minw, &minh, minw, minh);
153    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
154    evas_object_size_hint_min_set(obj, minw, minh);
155    evas_object_size_hint_max_set(obj, -1, -1);
156 }
157
158 static void
159 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
160 {
161    Widget_Data *wd = elm_widget_data_get(data);
162    if (!wd) return;
163    if (obj != wd->icon) return;
164    _sizing_eval(data);
165 }
166
167 static void
168 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
169 {
170    Widget_Data *wd = elm_widget_data_get(obj);
171    Evas_Object *sub = event_info;
172    if (!wd) return;
173    if (sub == wd->icon)
174      {
175         edje_object_signal_emit(wd->tgl, "elm,state,icon,hidden", "elm");
176         evas_object_event_callback_del_full
177           (sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, obj);
178         wd->icon = NULL;
179         edje_object_message_signal_process(wd->tgl);
180         _sizing_eval(obj);
181      }
182 }
183
184 static void
185 _signal_toggle_off(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
186 {
187    Widget_Data *wd = elm_widget_data_get(data);
188    if (!wd) return;
189    wd->state = 0;
190    if (wd->statep) *wd->statep = wd->state;
191    evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
192 }
193
194 static void
195 _signal_toggle_on(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
196 {
197    Widget_Data *wd = elm_widget_data_get(data);
198    if (!wd) return;
199    wd->state = 1;
200    if (wd->statep) *wd->statep = wd->state;
201    evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
202 }
203
204 /**
205  * Add a toggle to @p parent.
206  *
207  * @param parent The parent object
208  *
209  * @return The toggle object
210  *
211  * @ingroup Toggle
212  */
213 EAPI Evas_Object *
214 elm_toggle_add(Evas_Object *parent)
215 {
216    Evas_Object *obj;
217    Evas *e;
218    Widget_Data *wd;
219
220    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
221    
222    ELM_SET_WIDTYPE(widtype, "toggle");
223    elm_widget_type_set(obj, "toggle");
224    elm_widget_sub_object_add(parent, obj);
225    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
226    elm_widget_data_set(obj, wd);
227    elm_widget_del_hook_set(obj, _del_hook);
228    elm_widget_theme_hook_set(obj, _theme_hook);
229    elm_widget_disable_hook_set(obj, _disable_hook);
230    elm_widget_can_focus_set(obj, EINA_TRUE);
231    elm_widget_event_hook_set(obj, _event_hook);
232
233    wd->tgl = edje_object_add(e);
234    _mirrored_set(obj, elm_widget_mirrored_get(obj));
235    _elm_theme_object_set(obj, wd->tgl, "toggle", "base", "default");
236    wd->ontext = eina_stringshare_add("ON");
237    wd->offtext = eina_stringshare_add("OFF");
238    edje_object_signal_callback_add(wd->tgl, "elm,action,toggle,on", "",
239                                    _signal_toggle_on, obj);
240    edje_object_signal_callback_add(wd->tgl, "elm,action,toggle,off", "",
241                                    _signal_toggle_off, obj);
242    elm_widget_resize_object_set(obj, wd->tgl);
243    edje_object_part_text_set(wd->tgl, "elm.ontext", wd->ontext);
244    edje_object_part_text_set(wd->tgl, "elm.offtext", wd->offtext);
245
246    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
247    edje_object_signal_emit(wd->tgl, "elm,state,toggle,off", "elm");
248
249    _sizing_eval(obj);
250
251    // TODO: convert Elementary to subclassing of Evas_Smart_Class
252    // TODO: and save some bytes, making descriptions per-class and not instance!
253    evas_object_smart_callbacks_descriptions_set(obj, _signals);
254    return obj;
255 }
256
257 /**
258  * Sets the label to be displayed with the toggle.
259  *
260  * @param obj The toggle object
261  * @param label The label to be displayed
262  *
263  * @ingroup Toggle
264  */
265 EAPI void
266 elm_toggle_label_set(Evas_Object *obj, const char *label)
267 {
268    ELM_CHECK_WIDTYPE(obj, widtype);
269    Widget_Data *wd = elm_widget_data_get(obj);
270    if (!wd) return;
271    eina_stringshare_replace(&wd->label, label);
272    if (label)
273      edje_object_signal_emit(wd->tgl, "elm,state,text,visible", "elm");
274    else
275      edje_object_signal_emit(wd->tgl, "elm,state,text,hidden", "elm");
276    edje_object_message_signal_process(wd->tgl);
277    edje_object_part_text_set(wd->tgl, "elm.text", label);
278    _sizing_eval(obj);
279 }
280
281 /**
282  * Gets the label of the toggle
283  *
284  * @param obj  toggleeee object
285  * @return The label of the toggle
286  *
287  * @ingroup Toggle
288  */
289 EAPI const char *
290 elm_toggle_label_get(const Evas_Object *obj)
291 {
292    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
293    Widget_Data *wd = elm_widget_data_get(obj);
294    if (!wd) return NULL;
295    return wd->label;
296 }
297
298 /**
299  * Set the icon used for the toggle
300  *
301  * Once the icon object is set, a previously set one will be deleted
302  * If you want to keep that old content object, use the
303  * elm_toggle_icon_unset() function.
304  *
305  * @param obj The toggle object
306  * @param icon The icon object for the button
307  *
308  * @ingroup Toggle
309  */
310 EAPI void
311 elm_toggle_icon_set(Evas_Object *obj, Evas_Object *icon)
312 {
313    ELM_CHECK_WIDTYPE(obj, widtype);
314    Widget_Data *wd = elm_widget_data_get(obj);
315    if (!wd) return;
316    if (wd->icon == icon) return;
317    if (wd->icon) evas_object_del(wd->icon);
318    wd->icon = icon;
319    if (icon)
320      {
321         elm_widget_sub_object_add(obj, icon);
322         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
323               _changed_size_hints, obj);
324         edje_object_part_swallow(wd->tgl, "elm.swallow.content", icon);
325         edje_object_signal_emit(wd->tgl, "elm,state,icon,visible", "elm");
326         edje_object_message_signal_process(wd->tgl);
327      }
328    _sizing_eval(obj);
329 }
330
331 /**
332  * Get the icon used for the toggle
333  *
334  * Return the icon object which is set for this widget.
335  *
336  * @param obj The toggle object
337  * @return The icon object that is being used
338  *
339  * @ingroup Toggle
340  */
341 EAPI Evas_Object *
342 elm_toggle_icon_get(const Evas_Object *obj)
343 {
344    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
345    Widget_Data *wd = elm_widget_data_get(obj);
346    if (!wd) return NULL;
347    return wd->icon;
348 }
349
350 /**
351  * Unset the icon used for the toggle
352  *
353  * Unparent and return the icon object which was set for this widget.
354  *
355  * @param obj The toggle object
356  * @return The icon object that was being used
357  *
358  * @ingroup Toggle
359  */
360 EAPI Evas_Object *
361 elm_toggle_icon_unset(Evas_Object *obj)
362 {
363    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
364    Widget_Data *wd = elm_widget_data_get(obj);
365    if (!wd) return NULL;
366    if (!wd->icon) return NULL;
367    Evas_Object *icon = wd->icon;
368    elm_widget_sub_object_del(obj, wd->icon);
369    edje_object_part_unswallow(wd->tgl, wd->icon);
370    wd->icon = NULL;
371    return icon;
372 }
373
374 /**
375  * Sets the labels to be associated with the on and off states of the toggle.
376  *
377  * @param obj The toggle object
378  * @param onlabel The label displayed when the toggle is in the "on" state
379  * @param offlabel The label displayed when the toggle is in the "off" state
380  *
381  * @ingroup Toggle
382  */
383 EAPI void
384 elm_toggle_states_labels_set(Evas_Object *obj, const char *onlabel, const char *offlabel)
385 {
386    ELM_CHECK_WIDTYPE(obj, widtype);
387    Widget_Data *wd = elm_widget_data_get(obj);
388    if (!wd) return;
389    eina_stringshare_replace(&wd->ontext, onlabel);
390    eina_stringshare_replace(&wd->offtext, offlabel);
391    edje_object_part_text_set(wd->tgl, "elm.ontext", onlabel);
392    edje_object_part_text_set(wd->tgl, "elm.offtext", offlabel);
393    _sizing_eval(obj);
394 }
395
396
397 /**
398  * Gets the labels associated with the on and off states of the toggle.
399  *
400  * @param obj The toggle object
401  * @param onlabel A char** to place the onlabel of @p obj into
402  * @param offlabel A char** to place the offlabel of @p obj into
403  *
404  * @ingroup Toggle
405  */
406 EAPI void
407 elm_toggle_states_labels_get(const Evas_Object *obj, const char **onlabel, const char **offlabel)
408 {
409    if (onlabel) *onlabel = NULL;
410    if (offlabel) *offlabel = NULL;
411    ELM_CHECK_WIDTYPE(obj, widtype);
412    Widget_Data *wd = elm_widget_data_get(obj);
413    if (!wd) return;
414    if (onlabel) *onlabel = wd->ontext;
415    if (offlabel) *offlabel = wd->offtext;
416 }
417
418 /**
419  * Sets the state of the toggle to @p state.
420  *
421  * @param obj The toggle object
422  * @param state The state of @p obj
423  *
424  * @ingroup Toggle
425  */
426 EAPI void
427 elm_toggle_state_set(Evas_Object *obj, Eina_Bool state)
428 {
429    ELM_CHECK_WIDTYPE(obj, widtype);
430    Widget_Data *wd = elm_widget_data_get(obj);
431    if (!wd) return;
432    if (state != wd->state)
433      {
434         wd->state = state;
435         if (wd->statep) *wd->statep = wd->state;
436         if (wd->state)
437           edje_object_signal_emit(wd->tgl, "elm,state,toggle,on", "elm");
438         else
439           edje_object_signal_emit(wd->tgl, "elm,state,toggle,off", "elm");
440      }
441 }
442
443 /**
444  * Gets the state of the toggle to @p state.
445  *
446  * @param obj The toggle object
447  * @return The state of @p obj
448  *
449  * @ingroup Toggle
450  */
451 EAPI Eina_Bool
452 elm_toggle_state_get(const Evas_Object *obj)
453 {
454    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
455    Widget_Data *wd = elm_widget_data_get(obj);
456    if (!wd) return EINA_FALSE;
457    return wd->state;
458 }
459
460 /**
461  * Sets the state pointer of the toggle to @p statep.
462  *
463  * @param obj The toggle object
464  * @param statep The state pointer of @p obj
465  *
466  * @ingroup Toggle
467  */
468 EAPI void
469 elm_toggle_state_pointer_set(Evas_Object *obj, Eina_Bool *statep)
470 {
471    ELM_CHECK_WIDTYPE(obj, widtype);
472    Widget_Data *wd = elm_widget_data_get(obj);
473    if (!wd) return;
474    if (statep)
475      {
476         wd->statep = statep;
477         if (*wd->statep != wd->state)
478           {
479              wd->state = *wd->statep;
480              if (wd->state)
481                edje_object_signal_emit(wd->tgl, "elm,state,toggle,on", "elm");
482              else
483                edje_object_signal_emit(wd->tgl, "elm,state,toggle,off", "elm");
484           }
485      }
486    else
487      wd->statep = NULL;
488 }