Merge branch 'master' of jae.hwan.kim@165.213.180.234:/git/slp2.0/slp2.0-pkgs/EFL...
[framework/uifw/elementary.git] / src / lib / elm_radio.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Radio Radio
6  * @ingroup Elementary
7  *
8  * The radio button allows for 1 or more selectors to be created to select 1
9  * of a set of options.
10  *
11  * Signals that you can add callbacks for are:
12  *
13  * changed - This is called whenever the user changes the state of one of the
14  * radio objects within the group of radio objects that work together.
15  *
16  * A radio object contains an indicator, an optional Label and an optional
17  * icon object. They work normally in groups of 2 or more. When you create a
18  * radio (if it is not the first member of the group), simply add it to the
19  * group by adding it to any other member of the group that already exists
20  * (or the first member) with elm_radio_group_add() with the second parameter
21  * being the existing group member. The radio object(s) will select from one
22  * of a set of integer values, so any value they are configuring needs to be
23  * mapped to a set of integers. To configure what value that radio object
24  * represents, use  elm_radio_state_value_set() to set the integer it
25  * represents. To set the value the whole group is to indicate use
26  * elm_radio_value_set() on any group member, and to get the groups value use
27  * elm_radio_value_get(). For convenience the radio objects are also able to
28  * directly set an integer (int) to the value that is selected. To specify
29  * the pointer to this integer to modify, use elm_radio_value_pointer_set().
30  * The radio objects will modify this directly. That implies the pointer must
31  * point to valid memory for as long as the radio objects exist.
32  */
33
34 typedef struct _Widget_Data Widget_Data;
35 typedef struct _Group Group;
36
37 struct _Group
38 {
39    int value;
40    int *valuep;
41    Eina_List *radios;
42 };
43
44 struct _Widget_Data
45 {
46    Evas_Object *chk;
47    Evas_Object *icon;
48    int value;
49    const char *label;
50    Eina_Bool state;
51    Group *group;
52 };
53
54 static const char *widtype = NULL;
55 static void _state_set(Evas_Object *obj, Eina_Bool state);
56 static void _del_hook(Evas_Object *obj);
57 static void _theme_hook(Evas_Object *obj);
58 static void _disable_hook(Evas_Object *obj);
59 static void _sizing_eval(Evas_Object *obj);
60 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
61 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
62 static void _signal_radio_on(void *data, Evas_Object *obj, const char *emission, const char *source);
63
64 static const char SIG_CHANGED[] = "changed";
65 static const Evas_Smart_Cb_Description _signals[] = {
66   {SIG_CHANGED, ""},
67   {NULL, NULL}
68 };
69
70 static void
71 _del_hook(Evas_Object *obj)
72 {
73    Widget_Data *wd = elm_widget_data_get(obj);
74    if (!wd) return;
75    if (wd->label) eina_stringshare_del(wd->label);
76    wd->group->radios = eina_list_remove(wd->group->radios, obj);
77    if (!wd->group->radios) free(wd->group);
78    wd->group = NULL;
79    free(wd);
80 }
81
82 static void
83 _theme_hook(Evas_Object *obj)
84 {
85    Widget_Data *wd = elm_widget_data_get(obj);
86    if (!wd) return;
87    _elm_theme_object_set(obj, wd->chk, "radio", "base", elm_widget_style_get(obj));
88    if (wd->icon)
89      edje_object_signal_emit(wd->chk, "elm,state,icon,visible", "elm");
90    else
91      edje_object_signal_emit(wd->chk, "elm,state,icon,hidden", "elm");
92    if (wd->state)
93      edje_object_signal_emit(wd->chk, "elm,state,radio,on", "elm");
94    else
95      edje_object_signal_emit(wd->chk, "elm,state,radio,off", "elm");
96    if (wd->label)
97      edje_object_signal_emit(wd->chk, "elm,state,text,visible", "elm");
98    else
99      edje_object_signal_emit(wd->chk, "elm,state,text,hidden", "elm");
100    edje_object_part_text_set(wd->chk, "elm.text", wd->label);
101    edje_object_message_signal_process(wd->chk);
102    edje_object_scale_set(wd->chk, elm_widget_scale_get(obj) * _elm_config->scale);
103    _sizing_eval(obj);
104 }
105
106 static void
107 _disable_hook(Evas_Object *obj)
108 {
109    Widget_Data *wd = elm_widget_data_get(obj);
110    if (!wd) return;
111    if (elm_widget_disabled_get(obj))
112      {
113         edje_object_signal_emit(wd->chk, "elm,state,disabled", "elm");
114         if (wd->state) _state_set(obj, 0);
115      }
116    else
117      edje_object_signal_emit(wd->chk, "elm,state,enabled", "elm");
118 }
119
120 static void
121 _sizing_eval(Evas_Object *obj)
122 {
123    Widget_Data *wd = elm_widget_data_get(obj);
124    Evas_Coord minw = -1, minh = -1;
125    if (!wd) return;
126    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
127    edje_object_size_min_restricted_calc(wd->chk, &minw, &minh, minw, minh);
128    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
129    evas_object_size_hint_min_set(obj, minw, minh);
130    evas_object_size_hint_max_set(obj, -1, -1);
131 }
132
133 static void
134 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
135 {
136    Widget_Data *wd = elm_widget_data_get(data);
137    if (!wd) return;
138    if (obj != wd->icon) return;
139    _sizing_eval(data);
140 }
141
142 static void
143 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
144 {
145    Widget_Data *wd = elm_widget_data_get(obj);
146    Evas_Object *sub = event_info;
147    if (!wd) return;
148    if (sub == wd->icon)
149      {
150         edje_object_signal_emit(wd->chk, "elm,state,icon,hidden", "elm");
151         evas_object_event_callback_del_full
152           (sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, obj);
153         wd->icon = NULL;
154         _sizing_eval(obj);
155      }
156 }
157
158 static void
159 _state_set(Evas_Object *obj, Eina_Bool state)
160 {
161    Widget_Data *wd = elm_widget_data_get(obj);
162    if (!wd) return;
163    if ((state != wd->state) && (!elm_widget_disabled_get(obj)))
164      {
165         wd->state = state;
166         if (wd->state)
167           edje_object_signal_emit(wd->chk, "elm,state,radio,on", "elm");
168         else
169           edje_object_signal_emit(wd->chk, "elm,state,radio,off", "elm");
170      }
171 }
172
173 static void
174 _state_set_all(Widget_Data *wd)
175 {
176    const Eina_List *l;
177    Evas_Object *child, *selected = NULL;
178    Eina_Bool disabled = EINA_FALSE;
179    EINA_LIST_FOREACH(wd->group->radios, l, child)
180      {
181         Widget_Data *wd2 = elm_widget_data_get(child);
182         if (wd2->state) selected = child;
183         if (wd2->value == wd->group->value)
184           {
185              _state_set(child, 1);
186              if (!wd2->state) disabled = EINA_TRUE;
187           }
188         else _state_set(child, 0);
189      }
190    if (disabled && selected) _state_set(selected, 1);
191 }
192
193 static void
194 _signal_radio_on(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
195 {
196    Widget_Data *wd = elm_widget_data_get(data);
197    if (!wd) return;
198    if (wd->group->value == wd->value) return;
199    wd->group->value = wd->value;
200    if (wd->group->valuep) *(wd->group->valuep) = wd->group->value;
201    _state_set_all(wd);
202    evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
203 }
204
205 /**
206   * Add a new radio to the parent
207   *
208   * @param parent The parent object
209   * @return The new object or NULL if it cannot be created
210   *
211   * @ingroup Radio
212   */
213 EAPI Evas_Object *
214 elm_radio_add(Evas_Object *parent)
215 {
216    Evas_Object *obj;
217    Evas *e;
218    Widget_Data *wd;
219
220    wd = ELM_NEW(Widget_Data);
221    e = evas_object_evas_get(parent);
222    obj = elm_widget_add(e);
223    ELM_SET_WIDTYPE(widtype, "radio");
224    elm_widget_type_set(obj, "radio");
225    elm_widget_sub_object_add(parent, obj);
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
231    wd->chk = edje_object_add(e);
232    _elm_theme_object_set(obj, wd->chk, "radio", "base", "default");
233    edje_object_signal_callback_add(wd->chk, "elm,action,radio,on", "", _signal_radio_on, obj);
234    edje_object_signal_callback_add(wd->chk, "elm,action,radio,toggle", "", _signal_radio_on, obj);
235    elm_widget_resize_object_set(obj, wd->chk);
236
237    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
238
239    wd->group = calloc(1, sizeof(Group));
240    wd->group->radios = eina_list_append(wd->group->radios, obj);
241    wd->state = 0;
242
243    _sizing_eval(obj);
244
245    // TODO: convert Elementary to subclassing of Evas_Smart_Class
246    // TODO: and save some bytes, making descriptions per-class and not instance!
247    evas_object_smart_callbacks_descriptions_set(obj, _signals);
248    return obj;
249 }
250
251 /**
252  * Set the text label of the radio object
253  *
254  * @param obj The radio object
255  * @param label The text label string in UTF-8
256  *
257  * @ingroup Radio
258  */
259 EAPI void
260 elm_radio_label_set(Evas_Object *obj, const char *label)
261 {
262    ELM_CHECK_WIDTYPE(obj, widtype);
263    Widget_Data *wd = elm_widget_data_get(obj);
264    if (!wd) return;
265    eina_stringshare_replace(&wd->label, label);
266    if (label)
267      {
268         edje_object_signal_emit(wd->chk, "elm,state,text,visible", "elm");
269         edje_object_message_signal_process(wd->chk);
270      }
271    else
272      {
273         edje_object_signal_emit(wd->chk, "elm,state,text,hidden", "elm");
274         edje_object_message_signal_process(wd->chk);
275      }
276    edje_object_part_text_set(wd->chk, "elm.text", label);
277    _sizing_eval(obj);
278 }
279
280 /**
281  * Get the text label of the radio object
282  *
283  * @param obj The radio object
284  * @return The text label string in UTF-8
285  *
286  * @ingroup Radio
287  */
288 EAPI const char *
289 elm_radio_label_get(const Evas_Object *obj)
290 {
291    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
292    Widget_Data *wd = elm_widget_data_get(obj);
293    if (!wd) return NULL;
294    return wd->label;
295 }
296
297 /**
298  * Set the icon object of the radio object
299  *
300  * Once the icon object is set, it will become a child of the radio object and
301  * be deleted when the radio object is deleted. If another icon object is set
302  * then the previous one becomes orophaned and will no longer be deleted along
303  * with the radio.
304  *
305  * @param obj The radio object
306  * @param icon The icon object
307  *
308  * @ingroup Radio
309  */
310 EAPI void
311 elm_radio_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) && (wd->icon))
317      elm_widget_sub_object_del(obj, 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->chk, "elm.swallow.content", icon);
325         edje_object_signal_emit(wd->chk, "elm,state,icon,visible", "elm");
326         _sizing_eval(obj);
327      }
328 }
329
330 /**
331  * Get the icon object of the radio object
332  *
333  * @param obj The radio object
334  * @return The icon object
335  *
336  * @ingroup Radio
337  */
338 EAPI Evas_Object *
339 elm_radio_icon_get(const Evas_Object *obj)
340 {
341    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
342    Widget_Data *wd = elm_widget_data_get(obj);
343    if (!wd) return NULL;
344    return wd->icon;
345 }
346
347 /**
348  * Add this radio to a group of other radio objects
349  *
350  * Radio objects work in groups. Each member should have a different integer
351  * value assigned. In order ro have them work as a group, they need to know
352  * about eacthother. This adds the given radio object to the group of which
353  * the group object indicated is a member.
354  *
355  * @param obj The radio object
356  * @param group The object whose group the object is to join
357  *
358  * @ingroup Radio
359  */
360 EAPI void
361 elm_radio_group_add(Evas_Object *obj, Evas_Object *group)
362 {
363    ELM_CHECK_WIDTYPE(obj, widtype);
364    Widget_Data *wd = elm_widget_data_get(obj);
365    Widget_Data *wd2 = elm_widget_data_get(group);
366    if (!wd) return;
367    if (!wd2)
368      {
369         if (eina_list_count(wd->group->radios) == 1)
370           return;
371         wd->group->radios = eina_list_remove(wd->group->radios, obj);
372         wd->group = calloc(1, sizeof(Group));
373         wd->group->radios = eina_list_append(wd->group->radios, obj);
374      }
375    else if (wd->group == wd2->group) return;
376    else
377      {
378         wd->group->radios = eina_list_remove(wd->group->radios, obj);
379         if (!wd->group->radios) free(wd->group);
380         wd->group = wd2->group;
381         wd->group->radios = eina_list_append(wd->group->radios, obj);
382      }
383    if (wd->value == wd->group->value) _state_set(obj, 1);
384    else _state_set(obj, 0);
385 }
386
387 /**
388  * Set the integer value that this radio object represents
389  *
390  * This sets the value of the radio.
391  *
392  * @param obj The radio object
393  * @param value The value to use if this radio object is selected
394  *
395  * @ingroup Radio
396  */
397 EAPI void
398 elm_radio_state_value_set(Evas_Object *obj, int value)
399 {
400    ELM_CHECK_WIDTYPE(obj, widtype);
401    Widget_Data *wd = elm_widget_data_get(obj);
402    if (!wd) return;
403    wd->value = value;
404    if (wd->value == wd->group->value) _state_set(obj, 1);
405    else _state_set(obj, 0);
406 }
407
408 /**
409  * Set the value of the radio.
410  *
411  * This sets the value of the radio group and will also set the value if
412  * pointed to, to the value supplied, but will not call any callbacks.
413  *
414  * @param obj The radio object
415  * @param value The value to use for the group
416  *
417  * @ingroup Radio
418  */
419 EAPI void
420 elm_radio_value_set(Evas_Object *obj, int value)
421 {
422    ELM_CHECK_WIDTYPE(obj, widtype);
423    Widget_Data *wd = elm_widget_data_get(obj);
424    if (!wd) return;
425    if (value == wd->group->value) return;
426    wd->group->value = value;
427    if (wd->group->valuep) *(wd->group->valuep) = wd->group->value;
428    _state_set_all(wd);
429 }
430
431 /**
432  * Get the state of the radio object
433  *
434  * @param obj The radio object
435  * @return The integer state
436  *
437  * @ingroup Radio
438  */
439 EAPI int
440 elm_radio_value_get(const Evas_Object *obj)
441 {
442    ELM_CHECK_WIDTYPE(obj, widtype) 0;
443    Widget_Data *wd = elm_widget_data_get(obj);
444    if (!wd) return 0;
445    return wd->group->value;
446 }
447
448 /**
449  * Set a convenience pointer to a integer to change
450  *
451  * This sets a pointer to a integer, that, in addition to the radio objects
452  * state will also be modified directly. To stop setting the object pointed
453  * to simply use NULL as the valuep parameter. If valuep is not NULL, then
454  * when this is called, the radio objects state will also be modified to
455  * reflect the value of the integer valuep points to, just like calling
456  * elm_radio_value_set().
457  *
458  * @param obj The radio object
459  * @param valuep Pointer to the integer to modify
460  *
461  * @ingroup Radio
462  */
463 EAPI void
464 elm_radio_value_pointer_set(Evas_Object *obj, int *valuep)
465 {
466    ELM_CHECK_WIDTYPE(obj, widtype);
467    Widget_Data *wd = elm_widget_data_get(obj);
468    if (!wd) return;
469    if (valuep)
470      {
471         wd->group->valuep = valuep;
472         if (*(wd->group->valuep) != wd->group->value)
473           {
474              wd->group->value = *(wd->group->valuep);
475              _state_set_all(wd);
476           }
477      }
478    else
479      {
480         wd->group->valuep = NULL;
481      }
482 }