make toggle just a feature of check - on/off state and a togle style.
[framework/uifw/elementary.git] / src / lib / elm_check.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 typedef struct _Widget_Data Widget_Data;
5
6 struct _Widget_Data
7 {
8    Evas_Object *chk, *icon;
9    Eina_Bool state;
10    Eina_Bool *statep;
11    const char *label;
12    const char *ontext, *offtext;
13 };
14
15 static const char *widtype = NULL;
16 static void _del_hook(Evas_Object *obj);
17 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
18 static void _theme_hook(Evas_Object *obj);
19 static void _disable_hook(Evas_Object *obj);
20 static void _sizing_eval(Evas_Object *obj);
21 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
22 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
23 static void _signal_check_off(void *data, Evas_Object *obj, const char *emission, const char *source);
24 static void _signal_check_on(void *data, Evas_Object *obj, const char *emission, const char *source);
25 static void _signal_check_toggle(void *data, Evas_Object *obj, const char *emission, const char *source);
26 static void _on_focus_hook(void *data, Evas_Object *obj);
27 static void _activate_hook(Evas_Object *obj);
28 static void _content_set_hook(Evas_Object *obj, const char *part, Evas_Object *content);
29 static Evas_Object *_content_get_hook(const Evas_Object *obj, const char *part);
30 static Evas_Object *_content_unset_hook(Evas_Object *obj, const char *part);
31
32 static void _activate(Evas_Object *obj);
33 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
34                              Evas_Callback_Type type, void *event_info);
35
36 static const char SIG_CHANGED[] = "changed";
37 static const Evas_Smart_Cb_Description _signals[] = {
38        {SIG_CHANGED, ""},
39        {NULL, NULL}
40 };
41
42 static Eina_Bool
43 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
44 {
45    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
46    Evas_Event_Key_Down *ev = event_info;
47    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
48    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
49    if ((strcmp(ev->keyname, "Return")) &&
50        (strcmp(ev->keyname, "KP_Enter")) &&
51        (strcmp(ev->keyname, "space")))
52      return EINA_FALSE;
53    _activate(obj);
54    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
55    return EINA_TRUE;
56 }
57
58
59 static void
60 _del_hook(Evas_Object *obj)
61 {
62    Widget_Data *wd = elm_widget_data_get(obj);
63    if (!wd) return;
64    if (wd->label) eina_stringshare_del(wd->label);
65    if (wd->ontext) eina_stringshare_del(wd->ontext);
66    if (wd->offtext) eina_stringshare_del(wd->offtext);
67    free(wd);
68 }
69
70 static void
71 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
72 {
73    Widget_Data *wd = elm_widget_data_get(obj);
74    if (!wd) return;
75    if (elm_widget_focus_get(obj))
76      {
77         edje_object_signal_emit(wd->chk, "elm,action,focus", "elm");
78         evas_object_focus_set(wd->chk, EINA_TRUE);
79      }
80    else
81      {
82         edje_object_signal_emit(wd->chk, "elm,action,unfocus", "elm");
83         evas_object_focus_set(wd->chk, EINA_FALSE);
84      }
85 }
86
87 static void
88 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
89 {
90    Widget_Data *wd = elm_widget_data_get(obj);
91    if (!wd) return;
92    edje_object_mirrored_set(wd->chk, rtl);
93 }
94
95 static void
96 _theme_hook(Evas_Object *obj)
97 {
98    unsigned int counter = 0;
99    unsigned int i = 1;
100    unsigned int length = 0;
101    const char *str = NULL;
102    char labels[128] ;
103    char buffer[PATH_MAX]={'\0',};
104    char s1[PATH_MAX] = {'\0',};
105    Widget_Data *wd = elm_widget_data_get(obj);
106    if (!wd) return;
107
108    _elm_widget_mirrored_reload(obj);
109    _mirrored_set(obj, elm_widget_mirrored_get(obj));
110    _elm_theme_object_set(obj, wd->chk, "check", "base", elm_widget_style_get(obj));
111    if (wd->icon)
112      edje_object_signal_emit(wd->chk, "elm,state,icon,visible", "elm");
113    else
114      edje_object_signal_emit(wd->chk, "elm,state,icon,hidden", "elm");
115    if (wd->state)
116      {
117         edje_object_signal_emit(wd->chk, "elm,state,check,on", "elm");
118         edje_object_signal_emit(wd->chk, "elm,state,toggle,on", "elm");
119      }
120    else
121      {
122         edje_object_signal_emit(wd->chk, "elm,state,check,off", "elm");
123         edje_object_signal_emit(wd->chk, "elm,state,toggle,off", "elm");
124      }
125    if (wd->label)
126      edje_object_signal_emit(wd->chk, "elm,state,text,visible", "elm");
127    else
128      edje_object_signal_emit(wd->chk, "elm,state,text,hidden", "elm");
129    edje_object_part_text_set(wd->chk, "elm.text", wd->label);
130    edje_object_part_text_set(wd->chk, "elm.ontext", wd->ontext);
131    edje_object_part_text_set(wd->chk, "elm.offtext", wd->offtext);
132    if (elm_widget_disabled_get(obj))
133      edje_object_signal_emit(wd->chk, "elm,state,disabled", "elm");
134    edje_object_message_signal_process(wd->chk);
135    edje_object_scale_set(wd->chk, elm_widget_scale_get(obj) * _elm_config->scale);
136
137    //introduced internationalization of additional text parts used in style
138    while (1)
139      {
140         // s1 is  used  to store part name while buffer is used to store the part's value string
141         snprintf(labels,sizeof(labels),"label_%d",i++);
142         str = edje_object_data_get(wd->chk,labels);
143         if (!str) break;
144         length = strlen(str);
145         while ((str[counter]!= ' ') && (counter < length))
146           counter++;
147         if (counter == length)
148           continue;
149         strncpy(s1, str, counter);
150         s1[counter] = '\0';
151         strncpy(buffer, str + counter, sizeof(buffer));
152         edje_object_part_text_set(wd->chk, s1, E_(buffer));
153      }
154    _sizing_eval(obj);
155 }
156
157 static void
158 _disable_hook(Evas_Object *obj)
159 {
160    Widget_Data *wd = elm_widget_data_get(obj);
161    if (!wd) return;
162    if (elm_widget_disabled_get(obj))
163      edje_object_signal_emit(wd->chk, "elm,state,disabled", "elm");
164    else
165      edje_object_signal_emit(wd->chk, "elm,state,enabled", "elm");
166 }
167
168 static void
169 _sizing_eval(Evas_Object *obj)
170 {
171    Widget_Data *wd = elm_widget_data_get(obj);
172    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
173    if (!wd) return;
174    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
175    edje_object_size_min_restricted_calc(wd->chk, &minw, &minh, minw, minh);
176    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
177    evas_object_size_hint_min_set(obj, minw, minh);
178    evas_object_size_hint_max_set(obj, maxw, maxh);
179 }
180
181 static void
182 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
183 {
184    Widget_Data *wd = elm_widget_data_get(data);
185    if (!wd) return;
186    if (obj != wd->icon) return;
187    _sizing_eval(data);
188 }
189
190 static void
191 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
192 {
193    Widget_Data *wd = elm_widget_data_get(obj);
194    Evas_Object *sub = event_info;
195    if (!wd) return;
196    if (sub == wd->icon)
197      {
198         edje_object_signal_emit(wd->chk, "elm,state,icon,hidden", "elm");
199         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
200                                             _changed_size_hints, obj);
201         wd->icon = NULL;
202         _sizing_eval(obj);
203         edje_object_message_signal_process(wd->chk);
204      }
205 }
206
207 static void
208 _signal_check_off(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
209 {
210    Widget_Data *wd = elm_widget_data_get(data);
211    if (!wd) return;
212    wd->state = EINA_FALSE;
213    if (wd->statep) *wd->statep = wd->state;
214    edje_object_signal_emit(wd->chk, "elm,state,check,off", "elm");
215    edje_object_signal_emit(wd->chk, "elm,state,toggle,off", "elm");
216    evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
217 }
218
219 static void
220 _signal_check_on(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
221 {
222    Widget_Data *wd = elm_widget_data_get(data);
223    if (!wd) return;
224    wd->state = EINA_TRUE;
225    if (wd->statep) *wd->statep = wd->state;
226    edje_object_signal_emit(wd->chk, "elm,state,check,on", "elm");
227    edje_object_signal_emit(wd->chk, "elm,state,toggle,on", "elm");
228    evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
229 }
230
231 static void
232 _signal_check_toggle(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
233 {
234    _activate(data);
235 }
236
237 static void
238 _activate_hook(Evas_Object *obj)
239 {
240    _activate(obj);
241 }
242
243 static void
244 _content_set_hook(Evas_Object *obj, const char *part __UNUSED__, Evas_Object *content)
245 {
246    ELM_CHECK_WIDTYPE(obj, widtype);
247    Widget_Data *wd = elm_widget_data_get(obj);
248    if (!wd) return;
249    if (wd->icon == content) return;
250    if (wd->icon) evas_object_del(wd->icon);
251    wd->icon = content;
252    if (content)
253      {
254         elm_widget_sub_object_add(obj, content);
255         evas_object_event_callback_add(content,
256                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
257                                        _changed_size_hints, obj);
258         edje_object_part_swallow(wd->chk, "elm.swallow.content", content);
259         edje_object_signal_emit(wd->chk, "elm,state,icon,visible", "elm");
260         edje_object_message_signal_process(wd->chk);
261      }
262    _sizing_eval(obj);
263 }
264
265 static Evas_Object *
266 _content_get_hook(const Evas_Object *obj, const char *part __UNUSED__)
267 {
268    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
269    Widget_Data *wd = elm_widget_data_get(obj);
270    if (!wd) return NULL;
271    return wd->icon;
272 }
273
274 static Evas_Object *
275 _content_unset_hook(Evas_Object *obj, const char *part __UNUSED__)
276 {
277    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
278    Widget_Data *wd = elm_widget_data_get(obj);
279    if (!wd) return NULL;
280    if (!wd->icon) return NULL;
281    Evas_Object *icon = wd->icon;
282    elm_widget_sub_object_del(obj, wd->icon);
283    evas_object_event_callback_del_full(wd->icon,
284                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
285                                        _changed_size_hints, obj);
286    edje_object_part_unswallow(wd->chk, wd->icon);
287    wd->icon = NULL;
288    return icon;
289 }
290
291 static void
292 _activate(Evas_Object *obj)
293 {
294    Widget_Data *wd = elm_widget_data_get(obj);
295    if (!wd) return;
296    if ((_elm_config->access_mode == ELM_ACCESS_MODE_OFF) ||
297        (_elm_access_2nd_click_timeout(obj)))
298      {
299         wd->state = !wd->state;
300         if (wd->statep) *wd->statep = wd->state;
301         if (wd->state)
302           {
303              edje_object_signal_emit(wd->chk, "elm,state,check,on", "elm");
304              if (_elm_config->access_mode != ELM_ACCESS_MODE_OFF)
305                {
306                   if (!wd->ontext)
307                     {
308                        _elm_access_say(E_("State: On"));
309                     }
310                   else
311                      _elm_access_say(E_("State: On"));
312                }
313           }
314         else
315           {
316              edje_object_signal_emit(wd->chk, "elm,state,check,off", "elm");
317              if (_elm_config->access_mode != ELM_ACCESS_MODE_OFF)
318                {
319                   if (!wd->offtext)
320                     {
321                        _elm_access_say(E_("State: Off"));
322                     }
323                   else
324                      _elm_access_say(E_("State: Off"));
325                }
326           }
327         evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
328      }
329 }
330
331 static void
332 _elm_check_label_set(Evas_Object *obj, const char *item, const char *label)
333 {
334    ELM_CHECK_WIDTYPE(obj, widtype);
335    Widget_Data *wd = elm_widget_data_get(obj);
336    if (!wd) return;
337    if ((!item) || (!strcmp(item, "default")))
338      {
339         eina_stringshare_replace(&wd->label, label);
340         if (label)
341            edje_object_signal_emit(wd->chk, "elm,state,text,visible", "elm");
342         else
343            edje_object_signal_emit(wd->chk, "elm,state,text,hidden", "elm");
344         edje_object_message_signal_process(wd->chk);
345         edje_object_part_text_set(wd->chk, "elm.text", label);
346      }
347    else if ((item) && (!strcmp(item, "on")))
348      {
349         eina_stringshare_replace(&wd->ontext, label);
350         edje_object_part_text_set(wd->chk, "elm.ontext", wd->ontext);
351      }
352    else if ((item) && (!strcmp(item, "off")))
353      {
354         eina_stringshare_replace(&wd->offtext, label);
355         edje_object_part_text_set(wd->chk, "elm.offtext", wd->offtext);
356      }
357    _sizing_eval(obj);
358 }
359
360 static const char *
361 _elm_check_label_get(const Evas_Object *obj, const char *item)
362 {
363    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
364    Widget_Data *wd = elm_widget_data_get(obj);
365    if (!wd) return NULL;
366    if ((!item) || (!strcmp(item, "default")))
367       return wd->label;
368    else if ((item) && (!strcmp(item, "on")))
369       return wd->ontext;
370    else if ((item) && (!strcmp(item, "off")))
371       return wd->offtext;
372    return NULL;
373 }
374
375 static char *
376 _access_info_cb(void *data __UNUSED__, Evas_Object *obj, Elm_Widget_Item *item __UNUSED__)
377 {
378    const char *txt = elm_widget_access_info_get(obj);
379    if (!txt) txt = _elm_check_label_get(obj, NULL);
380    if (txt) return strdup(txt);
381    return NULL;
382 }
383
384 static char *
385 _access_state_cb(void *data, Evas_Object *obj, Elm_Widget_Item *item __UNUSED__)
386 {
387    Evas_Object *o = data;
388    Widget_Data *wd = elm_widget_data_get(o);
389    if (!wd) return NULL;
390    if (elm_widget_disabled_get(obj))
391      return strdup(E_("State: Disabled"));
392    if (wd->state)
393      {
394         if (wd->ontext)
395           {
396              char buf[1024];
397              
398              snprintf(buf, sizeof(buf), "%s: %s", E_("State"), wd->ontext);
399              return strdup(buf);
400           }
401         else
402            return strdup(E_("State: On"));
403      }
404    if (wd->offtext)
405      {
406         char buf[1024];
407         
408         snprintf(buf, sizeof(buf), "%s: %s", E_("State"), wd->offtext);
409         return strdup(buf);
410      }
411    return strdup(E_("State: Off"));
412 }
413
414 EAPI Evas_Object *
415 elm_check_add(Evas_Object *parent)
416 {
417    Evas_Object *obj;
418    Evas *e;
419    Widget_Data *wd;
420
421    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
422
423    ELM_SET_WIDTYPE(widtype, "check");
424    elm_widget_type_set(obj, "check");
425    elm_widget_sub_object_add(parent, obj);
426    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
427    elm_widget_data_set(obj, wd);
428    elm_widget_del_hook_set(obj, _del_hook);
429    elm_widget_theme_hook_set(obj, _theme_hook);
430    elm_widget_disable_hook_set(obj, _disable_hook);
431    elm_widget_can_focus_set(obj, EINA_TRUE);
432    elm_widget_activate_hook_set(obj, _activate_hook);
433    elm_widget_event_hook_set(obj, _event_hook);
434    elm_widget_text_set_hook_set(obj, _elm_check_label_set);
435    elm_widget_text_get_hook_set(obj, _elm_check_label_get);
436    elm_widget_content_set_hook_set(obj, _content_set_hook);
437    elm_widget_content_get_hook_set(obj, _content_get_hook);
438    elm_widget_content_unset_hook_set(obj, _content_unset_hook);
439
440    wd->chk = edje_object_add(e);
441    _elm_theme_object_set(obj, wd->chk, "check", "base", "default");
442    edje_object_signal_callback_add(wd->chk, "elm,action,check,on", "",
443                                    _signal_check_on, obj);
444    edje_object_signal_callback_add(wd->chk, "elm,action,check,off", "",
445                                    _signal_check_off, obj);
446    edje_object_signal_callback_add(wd->chk, "elm,action,check,toggle", "",
447                                    _signal_check_toggle, obj);
448    elm_widget_resize_object_set(obj, wd->chk);
449
450    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
451
452    _mirrored_set(obj, elm_widget_mirrored_get(obj));
453    _sizing_eval(obj);
454
455    // TODO: convert Elementary to subclassing of Evas_Smart_Class
456    // TODO: and save some bytes, making descriptions per-class and not instance!
457    evas_object_smart_callbacks_descriptions_set(obj, _signals);
458
459    _elm_access_object_register(obj, wd->chk);
460    _elm_access_text_set(_elm_access_object_get(obj),
461                         ELM_ACCESS_TYPE, E_("Check"));
462    _elm_access_callback_set(_elm_access_object_get(obj),
463                             ELM_ACCESS_INFO, _access_info_cb, obj);
464    _elm_access_callback_set(_elm_access_object_get(obj),
465                             ELM_ACCESS_STATE, _access_state_cb, obj);
466    return obj;
467 }
468
469 EAPI void
470 elm_check_label_set(Evas_Object *obj, const char *label)
471 {
472    _elm_check_label_set(obj, NULL, label);
473 }
474
475 EAPI const char *
476 elm_check_label_get(const Evas_Object *obj)
477 {
478    return _elm_check_label_get(obj, NULL);
479 }
480
481 EAPI void
482 elm_check_states_labels_set(Evas_Object *obj, const char *ontext, const char *offtext)
483 {
484    _elm_check_label_set(obj, "on", ontext);
485    _elm_check_label_set(obj, "off", offtext);
486 }
487
488 EAPI void
489 elm_check_states_labels_get(const Evas_Object *obj, const char **ontext, const char **offtext)
490 {
491    if (ontext) *ontext = _elm_check_label_get(obj, "on");
492    if (offtext) *offtext = _elm_check_label_get(obj, "off");
493 }
494
495 EAPI void
496 elm_check_icon_set(Evas_Object *obj, Evas_Object *icon)
497 {
498    _content_set_hook(obj, NULL, icon);
499 }
500
501 EAPI Evas_Object *
502 elm_check_icon_get(const Evas_Object *obj)
503 {
504    return _content_get_hook(obj, NULL);
505 }
506
507 EAPI Evas_Object *
508 elm_check_icon_unset(Evas_Object *obj)
509 {
510    return _content_unset_hook(obj, NULL);
511 }
512
513 EAPI void
514 elm_check_state_set(Evas_Object *obj, Eina_Bool state)
515 {
516    ELM_CHECK_WIDTYPE(obj, widtype);
517    Widget_Data *wd = elm_widget_data_get(obj);
518    if (!wd) return;
519    if (state != wd->state)
520      {
521         wd->state = state;
522         if (wd->statep) *wd->statep = wd->state;
523         if (wd->state)
524           {
525              edje_object_signal_emit(wd->chk, "elm,state,check,on", "elm");
526              edje_object_signal_emit(wd->chk, "elm,state,toggle,on", "elm");
527           }
528         else
529           {
530              edje_object_signal_emit(wd->chk, "elm,state,check,off", "elm");
531              edje_object_signal_emit(wd->chk, "elm,state,toggle,off", "elm");
532           }
533      }
534    edje_object_message_signal_process(wd->chk);
535 }
536
537 EAPI Eina_Bool
538 elm_check_state_get(const Evas_Object *obj)
539 {
540    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
541    Widget_Data *wd = elm_widget_data_get(obj);
542    if (!wd) return EINA_FALSE;
543    return wd->state;
544 }
545
546 EAPI void
547 elm_check_state_pointer_set(Evas_Object *obj, Eina_Bool *statep)
548 {
549    ELM_CHECK_WIDTYPE(obj, widtype);
550    Widget_Data *wd = elm_widget_data_get(obj);
551    if (!wd) return;
552    if (statep)
553      {
554         wd->statep = statep;
555         if (*wd->statep != wd->state)
556           {
557              wd->state = *wd->statep;
558              if (wd->state)
559                {
560                   edje_object_signal_emit(wd->chk, "elm,state,check,on", "elm");
561                   edje_object_signal_emit(wd->chk, "elm,state,toggle,on", "elm");
562                }
563              else
564                {
565                   edje_object_signal_emit(wd->chk, "elm,state,check,off", "elm");
566                   edje_object_signal_emit(wd->chk, "elm,state,toggle,off", "elm");
567                }
568           }
569      }
570    else
571      wd->statep = NULL;
572 }