8c8c005f60fc9b206adddf12c19f4f03dcc130b5
[framework/uifw/elementary.git] / src / lib / elm_check.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Check Check
6  * @ingroup Elementary
7  *
8  * The check widget allows for toggling a value between true or false (1 or 0).
9  *
10  * Signals that you can add callbacks for are:
11  *
12  * changed - This is called whenever the user changes the state of one of the
13  * check object.
14  *
15  * Check objects are a lot like radio objects in layout and functionality
16  * except they do not work as a group, but independently and only toggle the
17  * value of a boolean from false to true (0 or 1). elm_check_state_set() sets
18  * the boolean state (1 for true, 0 for false), and elm_check_state_get()
19  * returns the current state. For convenience, like the radio objects, you
20  * can set a pointer to a boolean directly with elm_check_state_pointer_set()
21  * for it to modify.
22  */
23 typedef struct _Widget_Data Widget_Data;
24
25 struct _Widget_Data
26 {
27    Evas_Object *chk, *icon;
28    Eina_Bool state;
29    Eina_Bool *statep;
30    const char *label;
31 };
32
33 static const char *widtype = NULL;
34 static void _del_hook(Evas_Object *obj);
35 static void _theme_hook(Evas_Object *obj);
36 static void _disable_hook(Evas_Object *obj);
37 static void _sizing_eval(Evas_Object *obj);
38 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
39 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
40 static void _signal_check_off(void *data, Evas_Object *obj, const char *emission, const char *source);
41 static void _signal_check_on(void *data, Evas_Object *obj, const char *emission, const char *source);
42 static void _signal_check_toggle(void *data, Evas_Object *obj, const char *emission, const char *source);
43 static void _on_focus_hook(void *data, Evas_Object *obj);
44 static const char SIG_CHANGED[] = "changed";
45 static const Evas_Smart_Cb_Description _signals[] = {
46   {SIG_CHANGED, ""},
47   {NULL, NULL}
48 };
49
50 static void
51 _del_hook(Evas_Object *obj)
52 {
53    Widget_Data *wd = elm_widget_data_get(obj);
54    if (!wd) return;
55    if (wd->label) eina_stringshare_del(wd->label);
56    free(wd);
57 }
58
59 static void
60 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
61 {
62    Widget_Data *wd = elm_widget_data_get(obj);
63    if (!wd) return;
64    if (elm_widget_focus_get(obj))
65      {
66         edje_object_signal_emit(wd->chk, "elm,action,focus", "elm");
67         evas_object_focus_set(wd->chk, EINA_TRUE);
68      }
69    else
70      {
71         edje_object_signal_emit(wd->chk, "elm,action,unfocus", "elm");        
72         evas_object_focus_set(wd->chk, EINA_FALSE);
73      }
74 }
75
76 static void
77 _theme_hook(Evas_Object *obj)
78 {
79    Widget_Data *wd = elm_widget_data_get(obj);
80    if (!wd) return;
81    _elm_theme_object_set(obj, wd->chk, "check", "base", elm_widget_style_get(obj));
82    if (wd->icon)
83      edje_object_signal_emit(wd->chk, "elm,state,icon,visible", "elm");
84    else
85      edje_object_signal_emit(wd->chk, "elm,state,icon,hidden", "elm");
86    if (wd->state)
87      edje_object_signal_emit(wd->chk, "elm,state,check,on", "elm");
88    else
89      edje_object_signal_emit(wd->chk, "elm,state,check,off", "elm");
90    if (wd->label)
91      edje_object_signal_emit(wd->chk, "elm,state,text,visible", "elm");
92    else
93      edje_object_signal_emit(wd->chk, "elm,state,text,hidden", "elm");
94    edje_object_part_text_set(wd->chk, "elm.text", wd->label);
95    if (elm_widget_disabled_get(obj))
96       edje_object_signal_emit(wd->chk, "elm,state,disabled", "elm");
97    edje_object_message_signal_process(wd->chk);
98    edje_object_scale_set(wd->chk, elm_widget_scale_get(obj) * _elm_config->scale);
99    _sizing_eval(obj);
100 }
101
102 static void
103 _disable_hook(Evas_Object *obj)
104 {
105    Widget_Data *wd = elm_widget_data_get(obj);
106    if (!wd) return;
107    if (elm_widget_disabled_get(obj))
108      edje_object_signal_emit(wd->chk, "elm,state,disabled", "elm");
109    else
110      edje_object_signal_emit(wd->chk, "elm,state,enabled", "elm");
111 }
112
113 static void
114 _sizing_eval(Evas_Object *obj)
115 {
116    Widget_Data *wd = elm_widget_data_get(obj);
117    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
118    if (!wd) return;
119    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
120    edje_object_size_min_restricted_calc(wd->chk, &minw, &minh, minw, minh);
121    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
122    evas_object_size_hint_min_set(obj, minw, minh);
123    evas_object_size_hint_max_set(obj, maxw, maxh);
124 }
125
126 static void
127 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
128 {
129    Widget_Data *wd = elm_widget_data_get(data);
130    if (!wd) return;
131    if (obj != wd->icon) return;
132    Evas_Coord mw, mh;
133    evas_object_size_hint_min_get(obj, &mw, &mh);
134    _sizing_eval(data);
135 }
136
137 static void
138 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
139 {
140    Widget_Data *wd = elm_widget_data_get(obj);
141    Evas_Object *sub = event_info;
142    if (!wd) return;
143    if (sub == wd->icon)
144      {
145         edje_object_signal_emit(wd->chk, "elm,state,icon,hidden", "elm");
146         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
147                                        _changed_size_hints, obj);
148         wd->icon = NULL;
149         _sizing_eval(obj);
150         edje_object_message_signal_process(wd->chk);
151      }
152 }
153
154 static void
155 _signal_check_off(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
156 {
157    Widget_Data *wd = elm_widget_data_get(data);
158    if (!wd) return;
159    wd->state = EINA_FALSE;
160    if (wd->statep) *wd->statep = wd->state;
161    edje_object_signal_emit(wd->chk, "elm,state,check,off", "elm");
162    evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
163 }
164
165 static void
166 _signal_check_on(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
167 {
168    Widget_Data *wd = elm_widget_data_get(data);
169    if (!wd) return;
170    wd->state = EINA_TRUE;
171    if (wd->statep) *wd->statep = wd->state;
172    edje_object_signal_emit(wd->chk, "elm,state,check,on", "elm");
173    evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
174 }
175
176 static void
177 _signal_check_toggle(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
178 {
179    Widget_Data *wd = elm_widget_data_get(data);
180    if (!wd) return;
181    wd->state = !wd->state;
182    if (wd->statep) *wd->statep = wd->state;
183    if (wd->state)
184      edje_object_signal_emit(wd->chk, "elm,state,check,on", "elm");
185    else
186      edje_object_signal_emit(wd->chk, "elm,state,check,off", "elm");
187    evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
188 }
189
190 /**
191  * Add a new Check object
192  *
193  * @param[in] parent The parent object
194  * @return The new object or NULL if it cannot be created
195  *
196  * @ingroup Check
197  */
198 EAPI Evas_Object *
199 elm_check_add(Evas_Object *parent)
200 {
201    Evas_Object *obj;
202    Evas *e;
203    Widget_Data *wd;
204
205    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
206
207    wd = ELM_NEW(Widget_Data);
208    e = evas_object_evas_get(parent);
209    obj = elm_widget_add(e);
210    ELM_SET_WIDTYPE(widtype, "check");
211    elm_widget_type_set(obj, "check");
212    elm_widget_can_focus_set(obj, EINA_TRUE);
213    elm_widget_sub_object_add(parent, obj);
214    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
215    elm_widget_data_set(obj, wd);
216    elm_widget_del_hook_set(obj, _del_hook);
217    elm_widget_theme_hook_set(obj, _theme_hook);
218    elm_widget_disable_hook_set(obj, _disable_hook);
219
220    wd->chk = edje_object_add(e);
221    _elm_theme_object_set(obj, wd->chk, "check", "base", "default");
222    edje_object_signal_callback_add(wd->chk, "elm,action,check,on", "",
223                                    _signal_check_on, obj);
224    edje_object_signal_callback_add(wd->chk, "elm,action,check,off", "",
225                                    _signal_check_off, obj);
226    edje_object_signal_callback_add(wd->chk, "elm,action,check,toggle", "",
227                                    _signal_check_toggle, obj);
228    elm_widget_resize_object_set(obj, wd->chk);
229
230    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
231
232    _sizing_eval(obj);
233
234    // TODO: convert Elementary to subclassing of Evas_Smart_Class
235    // TODO: and save some bytes, making descriptions per-class and not instance!
236    evas_object_smart_callbacks_descriptions_set(obj, _signals);
237    return obj;
238 }
239
240 /**
241  * Set the text label of the check object
242  *
243  * @param[in] obj The check object
244  * @param[in] label The text label string in UTF-8
245  *
246  * @ingroup Check
247  */
248 EAPI void
249 elm_check_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    if (!wd) return;
254    eina_stringshare_replace(&wd->label, label);
255    if (label)
256      edje_object_signal_emit(wd->chk, "elm,state,text,visible", "elm");
257    else
258      edje_object_signal_emit(wd->chk, "elm,state,text,hidden", "elm");
259    edje_object_message_signal_process(wd->chk);
260    edje_object_part_text_set(wd->chk, "elm.text", label);
261    _sizing_eval(obj);
262 }
263
264 /**
265  * Get the text label of the check object
266  *
267  * @param[in] obj The check object
268  * @return The text label string in UTF-8
269  *
270  * @ingroup Check
271  */
272 EAPI const char *
273 elm_check_label_get(const Evas_Object *obj)
274 {
275    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
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 object of the check object
283  *
284  * Once the icon object is set, a previously set one will be deleted.
285  * If you want to keep that old content object, use the
286  * elm_check_icon_unset() function.
287  *
288  * @param[in] obj The check object
289  * @param[in] icon The icon object
290  *
291  * @ingroup Check
292  */
293 EAPI void
294 elm_check_icon_set(Evas_Object *obj, Evas_Object *icon)
295 {
296    ELM_CHECK_WIDTYPE(obj, widtype);
297    Widget_Data *wd = elm_widget_data_get(obj);
298    if (!wd) return;
299    if (wd->icon == icon) return;
300    if (wd->icon) evas_object_del(wd->icon);
301    wd->icon = icon;
302    if (icon)
303      {
304         elm_widget_sub_object_add(obj, icon);
305         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
306         _changed_size_hints, obj);
307         edje_object_part_swallow(wd->chk, "elm.swallow.content", icon);
308         edje_object_signal_emit(wd->chk, "elm,state,icon,visible", "elm");
309         edje_object_message_signal_process(wd->chk);
310      }
311    _sizing_eval(obj);
312 }
313
314 /**
315  * Get the icon object of the check object
316  *
317  * @param[in] obj The check object
318  * @return The icon object
319  *
320  * @ingroup Check
321  */
322 EAPI Evas_Object *
323 elm_check_icon_get(const Evas_Object *obj)
324 {
325    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
326    Widget_Data *wd = elm_widget_data_get(obj);
327    if (!wd) return NULL;
328    return wd->icon;
329 }
330
331 /**
332  * Unset the icon used for the check object
333  *
334  * Unparent and return the icon object which was set for this widget.
335  *
336  * @param[in] obj The check object
337  * @return The icon object that was being used
338  *
339  * @ingroup Check
340  */
341 EAPI Evas_Object *
342 elm_check_icon_unset(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    if (!wd->icon) return NULL;
348    Evas_Object *icon = wd->icon;
349    elm_widget_sub_object_del(obj, wd->icon);
350    edje_object_part_unswallow(wd->chk, wd->icon);
351    wd->icon = NULL;
352    return icon;
353 }
354
355 /**
356  * Set the on/off state of the check object
357  *
358  * This sets the state of the check and will also set the value if pointed to
359  * to the state supplied, but will not call any callbacks.
360  *
361  * @param[in] obj The check object
362  * @param[in] state The state to use (1 == on, 0 == off)
363  *
364  * @ingroup Check
365  */
366 EAPI void
367 elm_check_state_set(Evas_Object *obj, Eina_Bool state)
368 {
369    ELM_CHECK_WIDTYPE(obj, widtype);
370    Widget_Data *wd = elm_widget_data_get(obj);
371    if (!wd) return;
372    if (state != wd->state)
373      {
374         wd->state = state;
375         if (wd->statep) *wd->statep = wd->state;
376         if (wd->state)
377           edje_object_signal_emit(wd->chk, "elm,state,check,on", "elm");
378         else
379           edje_object_signal_emit(wd->chk, "elm,state,check,off", "elm");
380      }
381 }
382
383 /**
384  * Get the state of the check object
385  *
386  * @param[in] obj The check object
387  * @return The boolean state
388  *
389  * @ingroup Check
390  */
391 EAPI Eina_Bool
392 elm_check_state_get(const Evas_Object *obj)
393 {
394    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
395    Widget_Data *wd = elm_widget_data_get(obj);
396    if (!wd) return EINA_FALSE;
397    return wd->state;
398 }
399
400 /**
401  * Set a convenience pointer to a boolean to change
402  *
403  * This sets a pointer to a boolean, that, in addition to the check objects
404  * state will also be modified directly. To stop setting the object pointed
405  * to simply use NULL as the statep parameter. If statep is not NULL, then
406  * when this is called, the check objects state will also be modified to
407  * reflect the value of the boolean statep points to, just like calling
408  * elm_check_state_set().
409  *
410  * @param[in] obj The check object
411  * @param[in] statep Pointer to the boolean to modify
412  *
413  * @ingroup Check
414  */
415 EAPI void
416 elm_check_state_pointer_set(Evas_Object *obj, Eina_Bool *statep)
417 {
418    ELM_CHECK_WIDTYPE(obj, widtype);
419    Widget_Data *wd = elm_widget_data_get(obj);
420    if (!wd) return;
421    if (statep)
422      {
423         wd->statep = statep;
424         if (*wd->statep != wd->state)
425           {
426              wd->state = *wd->statep;
427              if (wd->state)
428                edje_object_signal_emit(wd->chk, "elm,state,check,on", "elm");
429              else
430                edje_object_signal_emit(wd->chk, "elm,state,check,off", "elm");
431           }
432      }
433    else
434      wd->statep = NULL;
435 }