elementary/map - map supports language,changed
[framework/uifw/elementary.git] / src / lib / elm_radio.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3 #include "elm_widget_radio.h"
4
5 EAPI const char ELM_RADIO_SMART_NAME[] = "elm_radio";
6
7 static const Elm_Layout_Part_Alias_Description _content_aliases[] =
8 {
9    {"icon", "elm.swallow.content"},
10    {NULL, NULL}
11 };
12
13 static const Elm_Layout_Part_Alias_Description _text_aliases[] =
14 {
15    {"default", "elm.text"},
16    {NULL, NULL}
17 };
18
19 static const char SIG_CHANGED[] = "changed";
20 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
21    {SIG_CHANGED, ""},
22    {NULL, NULL}
23 };
24
25 EVAS_SMART_SUBCLASS_NEW
26   (ELM_RADIO_SMART_NAME, _elm_radio, Elm_Radio_Smart_Class,
27   Elm_Layout_Smart_Class, elm_layout_smart_class_get, _smart_callbacks);
28
29 static void
30 _state_set(Evas_Object *obj, Eina_Bool state)
31 {
32    ELM_RADIO_DATA_GET(obj, sd);
33
34    if ((state != sd->state) && (!elm_widget_disabled_get(obj)))
35      {
36         sd->state = state;
37         if (sd->state)
38           elm_layout_signal_emit(obj, "elm,state,radio,on", "elm");
39         else
40           elm_layout_signal_emit(obj, "elm,state,radio,off", "elm");
41      }
42 }
43
44 static void
45 _state_set_all(Elm_Radio_Smart_Data *sd)
46 {
47    const Eina_List *l;
48    Eina_Bool disabled = EINA_FALSE;
49    Evas_Object *child, *selected = NULL;
50
51    EINA_LIST_FOREACH(sd->group->radios, l, child)
52      {
53         ELM_RADIO_DATA_GET(child, sdc);
54
55         if (sdc->state) selected = child;
56         if (sdc->value == sd->group->value)
57           {
58              _state_set(child, EINA_TRUE);
59              if (!sdc->state) disabled = EINA_TRUE;
60           }
61         else _state_set(child, EINA_FALSE);
62      }
63
64    if ((disabled) && (selected)) _state_set(selected, 1);
65 }
66
67 static void
68 _activate(Evas_Object *obj)
69 {
70    ELM_RADIO_DATA_GET(obj, sd);
71
72    if (sd->group->value == sd->value) return;
73
74    if ((!_elm_config->access_mode) ||
75        (_elm_access_2nd_click_timeout(obj)))
76      {
77         sd->group->value = sd->value;
78         if (sd->group->valuep) *(sd->group->valuep) = sd->group->value;
79
80         _state_set_all(sd);
81
82         if (_elm_config->access_mode)
83           _elm_access_say(E_("State: On"));
84         evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
85      }
86 }
87
88 /* FIXME: replicated from elm_layout just because radio's icon spot
89  * is elm.swallow.content, not elm.swallow.icon. Fix that whenever we
90  * can changed the theme API */
91 static void
92 _icon_signal_emit(Evas_Object *obj)
93 {
94    char buf[64];
95
96    snprintf(buf, sizeof(buf), "elm,state,icon,%s",
97             elm_layout_content_get(obj, "icon") ? "visible" : "hidden");
98
99    elm_layout_signal_emit(obj, buf, "elm");
100 }
101
102 /* FIXME: replicated from elm_layout just because radio's icon spot
103  * is elm.swallow.content, not elm.swallow.icon. Fix that whenever we
104  * can changed the theme API */
105 static Eina_Bool
106 _elm_radio_smart_sub_object_del(Evas_Object *obj,
107                                  Evas_Object *sobj)
108 {
109    if (!ELM_WIDGET_CLASS(_elm_radio_parent_sc)->sub_object_del(obj, sobj))
110      return EINA_FALSE;
111
112    _icon_signal_emit(obj);
113
114    return EINA_TRUE;
115 }
116
117 /* FIXME: replicated from elm_layout just because radio's icon spot
118  * is elm.swallow.content, not elm.swallow.icon. Fix that whenever we
119  * can changed the theme API */
120 static Eina_Bool
121 _elm_radio_smart_content_set(Evas_Object *obj,
122                               const char *part,
123                               Evas_Object *content)
124 {
125    if (!ELM_CONTAINER_CLASS(_elm_radio_parent_sc)->content_set
126          (obj, part, content))
127      return EINA_FALSE;
128
129    _icon_signal_emit(obj);
130
131    return EINA_TRUE;
132 }
133
134 static Eina_Bool
135 _elm_radio_smart_event(Evas_Object *obj,
136                        Evas_Object *src __UNUSED__,
137                        Evas_Callback_Type type,
138                        void *event_info)
139 {
140    Evas_Event_Key_Down *ev;
141
142    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
143
144    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
145    ev = event_info;
146
147    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
148
149    if ((strcmp(ev->keyname, "Return")) &&
150        (strcmp(ev->keyname, "KP_Enter")) &&
151        (strcmp(ev->keyname, "space")))
152      return EINA_FALSE;
153
154    _activate(obj);
155    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
156
157    return EINA_TRUE;
158 }
159
160 static Eina_Bool
161 _elm_radio_smart_theme(Evas_Object *obj)
162 {
163    ELM_RADIO_DATA_GET(obj, sd);
164
165    if (!ELM_WIDGET_CLASS(_elm_radio_parent_sc)->theme(obj)) return EINA_FALSE;
166
167    if (sd->state) elm_layout_signal_emit(obj, "elm,state,radio,on", "elm");
168    else elm_layout_signal_emit(obj, "elm,state,radio,off", "elm");
169
170    if (elm_widget_disabled_get(obj) && sd->state) _state_set(obj, EINA_FALSE);
171
172    edje_object_message_signal_process(ELM_WIDGET_DATA(sd)->resize_obj);
173
174    /* FIXME: replicated from elm_layout just because radio's icon
175     * spot is elm.swallow.content, not elm.swallow.icon. Fix that
176     * whenever we can changed the theme API */
177    _icon_signal_emit(obj);
178
179    elm_layout_sizing_eval(obj);
180
181    return EINA_TRUE;
182 }
183
184 static Eina_Bool
185 _elm_radio_smart_disable(Evas_Object *obj)
186 {
187    ELM_RADIO_DATA_GET(obj, sd);
188
189    if (!ELM_WIDGET_CLASS(_elm_radio_parent_sc)->disable(obj))
190      return EINA_FALSE;
191
192    if (elm_widget_disabled_get(obj) && sd->state) _state_set(obj, EINA_FALSE);
193
194    return EINA_TRUE;
195 }
196
197 static void
198 _elm_radio_smart_sizing_eval(Evas_Object *obj)
199 {
200    Evas_Coord minw = -1, minh = -1;
201
202    ELM_RADIO_DATA_GET(obj, sd);
203
204    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
205    edje_object_size_min_restricted_calc
206      (ELM_WIDGET_DATA(sd)->resize_obj, &minw, &minh, minw, minh);
207    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
208    evas_object_size_hint_min_set(obj, minw, minh);
209    evas_object_size_hint_max_set(obj, -1, -1);
210 }
211
212 static void
213 _radio_on_cb(void *data,
214              Evas_Object *obj __UNUSED__,
215              const char *emission __UNUSED__,
216              const char *source __UNUSED__)
217 {
218    _activate(data);
219 }
220
221 static char *
222 _access_info_cb(void *data __UNUSED__, Evas_Object *obj)
223 {
224    const char *txt = elm_widget_access_info_get(obj);
225
226    if (!txt) txt = elm_layout_text_get(obj, NULL);
227    if (txt) return strdup(txt);
228
229    return NULL;
230 }
231
232 static char *
233 _access_state_cb(void *data __UNUSED__, Evas_Object *obj)
234 {
235    ELM_RADIO_DATA_GET(obj, sd);
236
237    if (elm_widget_disabled_get(obj)) return strdup(E_("State: Disabled"));
238    if (sd->state) return strdup(E_("State: On"));
239
240    return strdup(E_("State: Off"));
241 }
242
243 static void
244 _elm_radio_smart_add(Evas_Object *obj)
245 {
246    EVAS_SMART_DATA_ALLOC(obj, Elm_Radio_Smart_Data);
247
248    ELM_WIDGET_CLASS(_elm_radio_parent_sc)->base.add(obj);
249
250    elm_layout_theme_set(obj, "radio", "base", elm_widget_style_get(obj));
251
252    elm_layout_signal_callback_add
253      (obj, "elm,action,radio,on", "", _radio_on_cb, obj);
254    elm_layout_signal_callback_add
255      (obj, "elm,action,radio,toggle", "", _radio_on_cb, obj);
256
257    priv->group = calloc(1, sizeof(Group));
258    priv->group->radios = eina_list_append(priv->group->radios, obj);
259    priv->state = EINA_FALSE;
260
261    elm_widget_can_focus_set(obj, EINA_TRUE);
262
263    elm_layout_sizing_eval(obj);
264
265    _elm_access_object_register(obj, ELM_WIDGET_DATA(priv)->resize_obj);
266    _elm_access_text_set
267      (_elm_access_object_get(obj), ELM_ACCESS_TYPE, E_("Radio"));
268    _elm_access_callback_set
269      (_elm_access_object_get(obj), ELM_ACCESS_INFO, _access_info_cb, obj);
270    _elm_access_callback_set
271      (_elm_access_object_get(obj), ELM_ACCESS_STATE, _access_state_cb, obj);
272 }
273
274 static void
275 _elm_radio_smart_del(Evas_Object *obj)
276 {
277    ELM_RADIO_DATA_GET(obj, sd);
278
279    sd->group->radios = eina_list_remove(sd->group->radios, obj);
280    if (!sd->group->radios) free(sd->group);
281
282    ELM_WIDGET_CLASS(_elm_radio_parent_sc)->base.del(obj);
283 }
284
285 static Eina_Bool
286 _elm_radio_smart_activate(Evas_Object *obj, Elm_Activate act)
287 {
288    if (act != ELM_ACTIVATE_DEFAULT) return EINA_FALSE;
289
290    _activate(obj);
291
292    return EINA_TRUE;
293 }
294
295 static void
296 _elm_radio_smart_set_user(Elm_Radio_Smart_Class *sc)
297 {
298    ELM_WIDGET_CLASS(sc)->base.add = _elm_radio_smart_add;
299    ELM_WIDGET_CLASS(sc)->base.del = _elm_radio_smart_del;
300
301    ELM_WIDGET_CLASS(sc)->disable = _elm_radio_smart_disable;
302    ELM_WIDGET_CLASS(sc)->theme = _elm_radio_smart_theme;
303    ELM_WIDGET_CLASS(sc)->sub_object_del = _elm_radio_smart_sub_object_del;
304    ELM_WIDGET_CLASS(sc)->event = _elm_radio_smart_event;
305
306    /* not a 'focus chain manager' */
307    ELM_WIDGET_CLASS(sc)->focus_next = NULL;
308    ELM_WIDGET_CLASS(sc)->focus_direction = NULL;
309    ELM_WIDGET_CLASS(sc)->activate = _elm_radio_smart_activate;
310
311    ELM_CONTAINER_CLASS(sc)->content_set = _elm_radio_smart_content_set;
312
313    ELM_LAYOUT_CLASS(sc)->sizing_eval = _elm_radio_smart_sizing_eval;
314
315    ELM_LAYOUT_CLASS(sc)->content_aliases = _content_aliases;
316    ELM_LAYOUT_CLASS(sc)->text_aliases = _text_aliases;
317 }
318
319 EAPI const Elm_Radio_Smart_Class *
320 elm_radio_smart_class_get(void)
321 {
322    static Elm_Radio_Smart_Class _sc =
323      ELM_RADIO_SMART_CLASS_INIT_NAME_VERSION(ELM_RADIO_SMART_NAME);
324    static const Elm_Radio_Smart_Class *class = NULL;
325    Evas_Smart_Class *esc = (Evas_Smart_Class *)&_sc;
326
327    if (class)
328      return class;
329
330    _elm_radio_smart_set(&_sc);
331    esc->callbacks = _smart_callbacks;
332    class = &_sc;
333
334    return class;
335 }
336
337 EAPI Evas_Object *
338 elm_radio_add(Evas_Object *parent)
339 {
340    Evas_Object *obj;
341
342    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
343
344    obj = elm_widget_add(_elm_radio_smart_class_new(), parent);
345    if (!obj) return NULL;
346
347    if (!elm_widget_sub_object_add(parent, obj))
348      ERR("could not add %p as sub object of %p", obj, parent);
349
350    return obj;
351 }
352
353 EAPI void
354 elm_radio_group_add(Evas_Object *obj,
355                     Evas_Object *group)
356 {
357    ELM_RADIO_CHECK(obj);
358    ELM_RADIO_DATA_GET(obj, sd);
359    ELM_RADIO_DATA_GET(group, sdg);
360
361    if (!sdg)
362      {
363         if (eina_list_count(sd->group->radios) == 1) return;
364         sd->group->radios = eina_list_remove(sd->group->radios, obj);
365         sd->group = calloc(1, sizeof(Group));
366         sd->group->radios = eina_list_append(sd->group->radios, obj);
367      }
368    else if (sd->group == sdg->group)
369      return;
370    else
371      {
372         sd->group->radios = eina_list_remove(sd->group->radios, obj);
373         if (!sd->group->radios) free(sd->group);
374         sd->group = sdg->group;
375         sd->group->radios = eina_list_append(sd->group->radios, obj);
376      }
377    if (sd->value == sd->group->value) _state_set(obj, EINA_TRUE);
378    else _state_set(obj, EINA_FALSE);
379 }
380
381 EAPI void
382 elm_radio_state_value_set(Evas_Object *obj,
383                           int value)
384 {
385    ELM_RADIO_CHECK(obj);
386    ELM_RADIO_DATA_GET(obj, sd);
387
388    sd->value = value;
389    if (sd->value == sd->group->value) _state_set(obj, EINA_TRUE);
390    else _state_set(obj, EINA_FALSE);
391 }
392
393 EAPI int
394 elm_radio_state_value_get(const Evas_Object *obj)
395 {
396    ELM_RADIO_CHECK(obj) 0;
397    ELM_RADIO_DATA_GET(obj, sd);
398
399    return sd->value;
400 }
401
402 EAPI void
403 elm_radio_value_set(Evas_Object *obj,
404                     int value)
405 {
406    ELM_RADIO_CHECK(obj);
407    ELM_RADIO_DATA_GET(obj, sd);
408
409    if (value == sd->group->value) return;
410    sd->group->value = value;
411    if (sd->group->valuep) *(sd->group->valuep) = sd->group->value;
412    _state_set_all(sd);
413 }
414
415 EAPI int
416 elm_radio_value_get(const Evas_Object *obj)
417 {
418    ELM_RADIO_CHECK(obj) 0;
419    ELM_RADIO_DATA_GET(obj, sd);
420
421    return sd->group->value;
422 }
423
424 EAPI void
425 elm_radio_value_pointer_set(Evas_Object *obj,
426                             int *valuep)
427 {
428    ELM_RADIO_CHECK(obj);
429    ELM_RADIO_DATA_GET(obj, sd);
430
431    if (valuep)
432      {
433         sd->group->valuep = valuep;
434         if (*(sd->group->valuep) != sd->group->value)
435           {
436              sd->group->value = *(sd->group->valuep);
437              _state_set_all(sd);
438           }
439      }
440    else sd->group->valuep = NULL;
441 }
442
443 EAPI Evas_Object *
444 elm_radio_selected_object_get(Evas_Object *obj)
445 {
446    Eina_List *l;
447    Evas_Object *child;
448
449    ELM_RADIO_CHECK(obj) NULL;
450    ELM_RADIO_DATA_GET(obj, sd);
451
452    EINA_LIST_FOREACH(sd->group->radios, l, child)
453      {
454         ELM_RADIO_DATA_GET(child, sdc);
455
456         if (sdc->value == sd->group->value) return child;
457      }
458
459    return NULL;
460 }