tizen 2.4 release
[framework/uifw/elementary.git] / src / lib / elm_prefs.c
1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4
5 #define ELM_INTERFACE_ATSPI_ACCESSIBLE_PROTECTED
6
7 #include <Elementary.h>
8
9 #include "elm_priv.h"
10 #include "elm_widget_prefs.h"
11 #include "elm_prefs_edd.x"
12
13 #include "Eo.h"
14
15 #define MY_CLASS ELM_PREFS_CLASS
16
17 #define MY_CLASS_NAME "Elm_Prefs"
18 #define MY_CLASS_NAME_LEGACY "elm_prefs"
19
20 static const char SIG_PAGE_CHANGED[] = "page,changed";
21 static const char SIG_PAGE_SAVED[] = "page,saved";
22 static const char SIG_PAGE_RESET[] = "page,reset";
23 static const char SIG_PAGE_LOADED[] = "page,loaded";
24 static const char SIG_ITEM_CHANGED[] = "item,changed";
25 static const char SIG_ACTION[] = "action";
26 static const Evas_Smart_Cb_Description _elm_prefs_smart_callbacks[] = {
27    { SIG_PAGE_CHANGED, "s" },
28    { SIG_PAGE_SAVED, "s" },
29    { SIG_PAGE_RESET, "s" },
30    { SIG_PAGE_LOADED, "s" },
31    { SIG_ITEM_CHANGED, "s" },
32    { SIG_ACTION, "ss" },
33    { NULL, NULL}
34 };
35
36 static int _elm_prefs_init_count = 0;
37 static Eina_Hash *_elm_prefs_page_widgets_map = NULL;
38 static const Elm_Prefs_Page_Iface *_elm_prefs_page_default_widget = NULL;
39 static Eina_Hash *_elm_prefs_item_widgets_map = NULL;
40 static Eina_Hash *_elm_prefs_item_type_widgets_map = NULL;
41 static const Elm_Prefs_Item_Iface *_elm_prefs_item_default_widget = NULL;
42
43 static void _elm_prefs_values_get_default(Elm_Prefs_Page_Node *,
44                                           Eina_Bool);
45 static Eina_Bool _prefs_item_widget_value_from_self(Elm_Prefs_Item_Node *,
46                                                     Eina_Bool);
47
48 EOLIAN static void
49 _elm_prefs_evas_object_smart_add(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED)
50 {
51    eo_do_super(obj, MY_CLASS, evas_obj_smart_add());
52    elm_widget_sub_object_parent_add(obj);
53 }
54
55 static void _item_free(Elm_Prefs_Item_Node *it);
56
57 static void
58 _page_free(Elm_Prefs_Page_Node *p)
59 {
60    Elm_Prefs_Item_Node *it;
61
62    if (!p) return;
63
64    eina_stringshare_del(p->name);
65    eina_stringshare_del(p->title);
66    eina_stringshare_del(p->sub_title);
67    eina_stringshare_del(p->widget);
68    eina_stringshare_del(p->style);
69    eina_stringshare_del(p->icon);
70
71    evas_object_del(p->w_obj);
72
73    EINA_LIST_FREE(p->items, it)
74       _item_free(it);
75
76    free(p);
77 }
78
79 static void
80 _item_free(Elm_Prefs_Item_Node *it)
81 {
82    switch (it->type)
83      {
84       case ELM_PREFS_TYPE_ACTION:
85       case ELM_PREFS_TYPE_BOOL:
86       case ELM_PREFS_TYPE_INT:
87       case ELM_PREFS_TYPE_FLOAT:
88       case ELM_PREFS_TYPE_LABEL:
89       case ELM_PREFS_TYPE_DATE:
90       case ELM_PREFS_TYPE_RESET:
91       case ELM_PREFS_TYPE_SAVE:
92       case ELM_PREFS_TYPE_SEPARATOR:
93       case ELM_PREFS_TYPE_SWALLOW:
94         break;
95
96       case ELM_PREFS_TYPE_PAGE:
97         eina_stringshare_del(it->spec.p.source);
98         _page_free(it->subpage);
99         it->w_obj = NULL;
100         break;
101
102       case ELM_PREFS_TYPE_TEXT:
103       case ELM_PREFS_TYPE_TEXTAREA:
104         eina_stringshare_del(it->spec.s.placeholder);
105         eina_stringshare_del(it->spec.s.accept);
106         eina_stringshare_del(it->spec.s.deny);
107         break;
108
109       default:
110         ERR("bad item (type = %d), skipping it", it->type);
111         break;
112      }
113
114    eina_stringshare_del(it->name);
115    eina_stringshare_del(it->label);
116    eina_stringshare_del(it->icon);
117    eina_stringshare_del(it->style);
118    eina_stringshare_del(it->widget);
119
120    evas_object_del(it->w_obj); /* we have to delete them ourselves
121                                 * because of _prefs_item_del_cb() --
122                                 * it'll need the parent alive, to
123                                 * gather its smart data bit */
124    free(it);
125 }
126
127 static Eina_Bool
128 _elm_prefs_save(void *data)
129 {
130    ELM_PREFS_DATA_GET(data, sd);
131    ELM_WIDGET_DATA_GET_OR_RETURN(data, wd, ECORE_CALLBACK_CANCEL);
132
133    if (!sd->dirty || !sd->prefs_data) goto end;
134
135    if (!elm_prefs_data_autosave_get(sd->prefs_data))
136      {
137         elm_prefs_data_save(sd->prefs_data, NULL, NULL);
138
139         evas_object_smart_callback_call
140           (wd->obj, SIG_PAGE_SAVED, (char *)sd->root->name);
141      }
142
143    sd->dirty = EINA_FALSE;
144
145 end:
146    sd->saving_poller = NULL;
147    return ECORE_CALLBACK_CANCEL;
148 }
149
150 static void
151 _root_node_free(Elm_Prefs_Data *sd)
152 {
153    _page_free(sd->root);
154 }
155
156 static Eina_Bool
157 _prefs_data_types_match(const Eina_Value_Type *t,
158                         Elm_Prefs_Item_Type epd_t)
159 {
160    return (t == EINA_VALUE_TYPE_UCHAR && epd_t == ELM_PREFS_TYPE_BOOL) ||
161           (t == EINA_VALUE_TYPE_INT && epd_t == ELM_PREFS_TYPE_INT) ||
162           (t == EINA_VALUE_TYPE_FLOAT && epd_t == ELM_PREFS_TYPE_FLOAT) ||
163           (t == EINA_VALUE_TYPE_TIMEVAL && epd_t == ELM_PREFS_TYPE_DATE) ||
164           (t == EINA_VALUE_TYPE_STRINGSHARE &&
165            (epd_t == ELM_PREFS_TYPE_PAGE ||
166             epd_t == ELM_PREFS_TYPE_TEXT ||
167             epd_t == ELM_PREFS_TYPE_TEXTAREA));
168 }
169
170 static Eina_Bool
171 _prefs_data_type_fix(Elm_Prefs_Item_Node *it,
172                      Eina_Value *value)
173 {
174    Eina_Value v;
175    Eina_Bool setup_err = EINA_FALSE;
176
177    switch (it->type)
178      {
179       case ELM_PREFS_TYPE_BOOL:
180         if (!eina_value_setup(&v, EINA_VALUE_TYPE_UCHAR))
181           setup_err = EINA_TRUE;
182         break;
183
184       case ELM_PREFS_TYPE_INT:
185         if (!eina_value_setup(&v, EINA_VALUE_TYPE_INT))
186           setup_err = EINA_TRUE;
187         break;
188
189       case ELM_PREFS_TYPE_FLOAT:
190         if (!eina_value_setup(&v, EINA_VALUE_TYPE_FLOAT))
191           setup_err = EINA_TRUE;
192         break;
193
194       case ELM_PREFS_TYPE_DATE:
195         if (!eina_value_setup(&v, EINA_VALUE_TYPE_TIMEVAL))
196           setup_err = EINA_TRUE;
197         break;
198
199       case ELM_PREFS_TYPE_PAGE:
200       case ELM_PREFS_TYPE_TEXT:
201       case ELM_PREFS_TYPE_TEXTAREA:
202         if (!eina_value_setup(&v, EINA_VALUE_TYPE_STRINGSHARE))
203           setup_err = EINA_TRUE;
204         break;
205
206       default:
207         ERR("bad item (type = %d) found, skipping it", it->type);
208         return EINA_FALSE;
209      }
210
211    if (setup_err) return EINA_FALSE;
212
213    if (!eina_value_convert(value, &v) || !eina_value_copy(&v, value))
214      {
215         ERR("problem converting mismatching item (%s) value",
216             it->name);
217
218         return EINA_FALSE;
219      }
220
221    eina_value_flush(&v);
222
223    return EINA_TRUE;
224 }
225
226 static Eina_Bool
227 _prefs_item_widget_value_from_data(Elm_Prefs_Data *sd,
228                                    Elm_Prefs_Item_Node *it,
229                                    Eina_Value *value)
230 {
231    char buf[PATH_MAX];
232    const Eina_Value_Type *t = eina_value_type_get(value);
233
234    if ((it->type <= ELM_PREFS_TYPE_UNKNOWN) ||
235        (it->type > ELM_PREFS_TYPE_SWALLOW))
236      {
237         ERR("bad item (type = %d) found on page %s, skipping it",
238             it->type, sd->page);
239
240         return EINA_FALSE;
241      }
242
243    snprintf(buf, sizeof(buf), "%s:%s", it->page->name, it->name);
244
245    if (!_prefs_data_types_match(t, it->type))
246      {
247         if (!_prefs_data_type_fix(it, value)) return EINA_FALSE;
248         else
249           {
250              Eina_Bool v_set;
251
252              sd->changing_from_ui = EINA_TRUE;
253
254              v_set = elm_prefs_data_value_set(sd->prefs_data,
255                                               buf, it->type, value);
256
257              sd->changing_from_ui = EINA_FALSE;
258
259              if (!v_set) return EINA_FALSE;
260           }
261      }
262
263    if (!it->available)
264      {
265         ERR("widget of item %s has been deleted, we can't set values on it",
266             it->name);
267         return EINA_FALSE;
268      }
269
270    if (!it->w_impl->value_set(it->w_obj, value))
271      {
272         ERR("failed to set value on widget of item %s", it->name);
273         return EINA_FALSE;
274      }
275
276    return EINA_TRUE;
277 }
278
279 static void
280 _elm_prefs_mark_as_dirty(Eo *obj)
281 {
282    ELM_PREFS_DATA_GET(obj, sd);
283    sd->dirty = EINA_TRUE;
284
285    if (sd->autosave)
286      {
287         if (sd->saving_poller) return;
288
289         sd->saving_poller = ecore_poller_add
290             (ECORE_POLLER_CORE, 1, _elm_prefs_save, obj);
291      }
292 }
293
294 static void
295 _elm_prefs_item_changed_report(Eo *obj,
296                                Elm_Prefs_Item_Node *it)
297 {
298    char buf[PATH_MAX];
299    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
300
301    snprintf(buf, sizeof(buf), "%s:%s", it->page->name, it->name);
302
303    evas_object_smart_callback_call
304      (wd->obj, SIG_ITEM_CHANGED, buf);
305 }
306
307 static Elm_Prefs_Item_Node *
308 _elm_prefs_page_item_by_name(Elm_Prefs_Page_Node *p,
309                              char **path)
310 {
311    Elm_Prefs_Item_Node *it;
312    Eina_List *l;
313    char *token;
314
315    token = strsep(path, ":");
316
317    EINA_LIST_FOREACH(p->items, l, it)
318      {
319         if (strcmp(it->name, token)) continue;
320
321         if (!*path)
322           return it;
323         else if (it->type == ELM_PREFS_TYPE_PAGE)
324           return _elm_prefs_page_item_by_name(it->subpage, path);
325      }
326
327    return NULL;
328 }
329
330 static Elm_Prefs_Item_Node *
331 _elm_prefs_item_node_by_name(Elm_Prefs_Data *sd,
332                              const char *name)
333 {
334    char buf[PATH_MAX];
335    char *token;
336    char *aux = buf;
337
338    strncpy(buf, name, PATH_MAX - 1);
339    buf[PATH_MAX - 1] = '\0';
340
341    token = strsep(&aux, ":");
342
343    if (strcmp(sd->root->name, token))
344      return NULL; // first token should be the current page name
345
346    return _elm_prefs_page_item_by_name(sd->root, &aux);
347 }
348
349 static Eina_List *
350 _elm_prefs_item_list_node_by_name(Elm_Prefs_Data *sd,
351                                   const char *name)
352 {
353    Elm_Prefs_Item_Node *it;
354    Eina_List *l;
355
356    EINA_LIST_FOREACH(sd->root->items, l, it)
357      if (!strcmp(it->name, name)) return l;
358
359    return NULL;
360 }
361
362 static void
363 _prefs_data_item_changed_cb(void *cb_data,
364                             Elm_Prefs_Data_Event_Type type EINA_UNUSED,
365                             Elm_Prefs_Data *prefs_data,
366                             void *event_info)
367 {
368    Elm_Prefs_Data_Event_Changed *evt = event_info;
369    Eo *obj = cb_data;
370    Elm_Prefs_Item_Node *it;
371    Eina_Value value;
372
373    ELM_PREFS_DATA_GET(obj, sd);
374    if (sd->changing_from_ui) return;
375
376    it = _elm_prefs_item_node_by_name(sd, evt->key);
377    if (!it) return;
378
379    if (elm_prefs_data_value_get(prefs_data, evt->key, NULL, &value))
380      {
381         if (!_prefs_item_widget_value_from_data(sd, it, &value)) goto end;
382
383         _elm_prefs_item_changed_report(obj, it);
384         _elm_prefs_mark_as_dirty(obj);
385      }
386    else
387      ERR("failed to fetch value from data after changed event");
388
389 end:
390    eina_value_flush(&value);
391    return;
392 }
393
394 static void
395 _prefs_data_autosaved_cb(void *cb_data,
396                          Elm_Prefs_Data_Event_Type type EINA_UNUSED,
397                          Elm_Prefs_Data *data EINA_UNUSED,
398                          void *event_info)
399 {
400    ELM_PREFS_DATA_GET(cb_data, sd);
401    ELM_WIDGET_DATA_GET_OR_RETURN(cb_data, wd);
402
403    evas_object_smart_callback_call
404      (wd->obj, SIG_PAGE_SAVED, event_info);
405
406    sd->dirty = EINA_FALSE;
407 }
408
409 static Eina_Bool
410 _elm_prefs_data_cbs_add(Eo *obj,
411                         Elm_Prefs_Data *prefs_data)
412 {
413    if (!elm_prefs_data_event_callback_add
414          (prefs_data, ELM_PREFS_DATA_EVENT_ITEM_CHANGED,
415          _prefs_data_item_changed_cb, obj) ||
416        !elm_prefs_data_event_callback_add
417          (prefs_data, ELM_PREFS_DATA_EVENT_GROUP_AUTOSAVED,
418          _prefs_data_autosaved_cb, obj))
419      {
420         ERR("error while adding item changed event callback to "
421             "prefs data handle, keeping previous data");
422
423         return EINA_FALSE;
424      }
425
426    return EINA_TRUE;
427 }
428
429 static void
430 _elm_prefs_data_cbs_del(Eo *obj)
431 {
432    ELM_PREFS_DATA_GET(obj, sd);
433
434    if (!sd->prefs_data) return;
435
436    if (!elm_prefs_data_event_callback_del
437          (sd->prefs_data, ELM_PREFS_DATA_EVENT_ITEM_CHANGED,
438          _prefs_data_item_changed_cb, obj))
439      ERR("error while removing item changed event callback from "
440          "prefs data handle");
441
442    if (!elm_prefs_data_event_callback_del
443          (sd->prefs_data, ELM_PREFS_DATA_EVENT_GROUP_AUTOSAVED,
444          _prefs_data_autosaved_cb, obj))
445      ERR("error while removing page autosave event callback from "
446          "prefs data handle");
447 }
448
449 EOLIAN static void
450 _elm_prefs_evas_object_smart_del(Eo *obj, Elm_Prefs_Data *sd)
451 {
452    sd->delete_me = EINA_TRUE;
453
454    if (sd->saving_poller) ecore_poller_del(sd->saving_poller);
455
456    _elm_prefs_data_cbs_del(obj);
457
458    if (sd->root)
459      {
460         elm_prefs_data_version_set(sd->prefs_data, sd->root->version);
461
462         _elm_prefs_save(obj);
463
464         _root_node_free(sd);
465      }
466
467    if (sd->prefs_data) elm_prefs_data_unref(sd->prefs_data);
468
469    eina_stringshare_del(sd->file);
470    eina_stringshare_del(sd->page);
471
472    eo_do_super(obj, MY_CLASS, evas_obj_smart_del());
473 }
474
475 EOLIAN static Eina_Bool
476 _elm_prefs_elm_widget_focus_next(Eo *obj, Elm_Prefs_Data *sd, Elm_Focus_Direction dir, Evas_Object **next)
477 {
478    const Eina_List *items;
479
480    ELM_PREFS_CHECK(obj) EINA_FALSE;
481
482    items = elm_widget_focus_custom_chain_get(obj);
483    if (items)
484      {
485         return elm_widget_focus_list_next_get
486            (obj, items, eina_list_data_get, dir, next);
487      }
488
489    if (sd->root && sd->root->w_obj)
490      {
491         return elm_widget_focus_next_get(sd->root->w_obj, dir, next);
492      }
493
494    if (next) *next = NULL;
495
496    return EINA_FALSE;
497 }
498
499 EAPI Evas_Object *
500 elm_prefs_add(Evas_Object *parent)
501 {
502    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
503
504    if (!_elm_prefs_init_count)
505      {
506         CRI("prefs_iface module is not loaded! you can't"
507             " create prefs widgets");
508         return NULL;
509      }
510
511    Evas_Object *obj = eo_add(MY_CLASS, parent);
512    return obj;
513 }
514
515 EOLIAN static void
516 _elm_prefs_eo_base_constructor(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED)
517 {
518    eo_do_super(obj, MY_CLASS, eo_constructor());
519    eo_do(obj,
520          evas_obj_type_set(MY_CLASS_NAME_LEGACY),
521          evas_obj_smart_callbacks_descriptions_set(_elm_prefs_smart_callbacks),
522          elm_interface_atspi_accessible_role_set(ELM_ATSPI_ROLE_REDUNDANT_OBJECT));
523 }
524
525 static Eina_Bool
526 _elm_prefs_item_has_value(Elm_Prefs_Item_Node *it)
527 {
528    return (it->type != ELM_PREFS_TYPE_ACTION) &&
529           (it->type != ELM_PREFS_TYPE_LABEL) &&
530           (it->type != ELM_PREFS_TYPE_RESET) &&
531           (it->type != ELM_PREFS_TYPE_SAVE) &&
532           (it->type != ELM_PREFS_TYPE_PAGE) &&
533           (it->type != ELM_PREFS_TYPE_SEPARATOR) &&
534           (it->type != ELM_PREFS_TYPE_SWALLOW);
535 }
536
537 static void
538 _item_changed_cb(Evas_Object *it_obj)
539 {
540    char buf[PATH_MAX];
541    Elm_Prefs_Item_Node *it = evas_object_data_get(it_obj, "prefs_item");
542
543    /* some widgets mark themselves changed as early as in their add()
544     * interface methods */
545    if (!it) return;
546
547    snprintf(buf, sizeof(buf), "%s:%s", it->page->name, it->name);
548
549    ELM_PREFS_DATA_GET(it->prefs, sd);
550    ELM_WIDGET_DATA_GET_OR_RETURN(it->prefs, wd);
551
552    if (sd->values_fetching) goto end;
553
554    /* we use the changed cb on ACTION/RESET/SAVE items specially */
555    if (it->type == ELM_PREFS_TYPE_ACTION)
556      {
557         evas_object_smart_callback_call
558           (wd->obj, SIG_ACTION, buf);
559
560         return;
561      }
562    else if (it->type == ELM_PREFS_TYPE_RESET)
563      {
564         _elm_prefs_values_get_default(sd->root, EINA_TRUE);
565         _elm_prefs_mark_as_dirty(it->prefs);
566
567         return;
568      }
569    else if (it->type == ELM_PREFS_TYPE_SAVE)
570      {
571         if (sd->saving_poller) return;
572
573         sd->saving_poller = ecore_poller_add
574             (ECORE_POLLER_CORE, 1, _elm_prefs_save, it->prefs);
575
576         return;
577      }
578
579    if (!it->persistent || !_elm_prefs_item_has_value(it)) return;
580
581    if (it->w_impl->value_validate &&
582        !it->w_impl->value_validate(it->w_obj))
583      {
584         if (sd->prefs_data)
585           {
586              Eina_Value value;
587
588              // Restoring to the last valid value.
589              if (!elm_prefs_data_value_get(sd->prefs_data, buf, NULL, &value))
590                goto restore_fail;
591              if (!it->w_impl->value_set(it->w_obj, &value))
592                {
593                   eina_value_flush(&value);
594                   goto restore_fail;
595                }
596           }
597         else
598           {
599              if (!_prefs_item_widget_value_from_self(it, EINA_FALSE))
600                goto restore_fail;
601           }
602
603         return;
604      }
605
606 end:
607    if (sd->prefs_data)
608      {
609         Eina_Value value;
610
611         if (!it->w_impl->value_get(it->w_obj, &value))
612           ERR("failed to fetch value from widget of item %s", buf);
613         else
614           {
615              sd->changing_from_ui = EINA_TRUE;
616              elm_prefs_data_value_set(sd->prefs_data, buf, it->type, &value);
617              eina_value_flush(&value);
618              sd->changing_from_ui = EINA_FALSE;
619           }
620      }
621
622    if (!sd->values_fetching) _elm_prefs_item_changed_report(it->prefs, it);
623
624    _elm_prefs_mark_as_dirty(it->prefs);
625
626    return;
627
628 restore_fail:
629    ERR("failed to restore the last valid value from widget of item %s",
630        buf);
631 }
632
633 static Eina_Bool
634 _prefs_item_widget_value_from_self(Elm_Prefs_Item_Node *it,
635                                    Eina_Bool mark_changed)
636 {
637    Eina_Value value;
638
639    if (!_elm_prefs_item_has_value(it)) return EINA_TRUE;
640
641    switch (it->type)
642      {
643       case ELM_PREFS_TYPE_BOOL:
644         if (!eina_value_setup(&value, EINA_VALUE_TYPE_UCHAR)) goto err;
645         if (!eina_value_set(&value, it->spec.b.def))
646           {
647              eina_value_flush(&value);
648              goto err;
649           }
650         break;
651
652       case ELM_PREFS_TYPE_INT:
653         if (!eina_value_setup(&value, EINA_VALUE_TYPE_INT)) goto err;
654         if (!eina_value_set(&value, it->spec.i.def))
655           {
656              eina_value_flush(&value);
657              goto err;
658           }
659         break;
660
661       case ELM_PREFS_TYPE_FLOAT:
662         if (!eina_value_setup(&value, EINA_VALUE_TYPE_FLOAT)) goto err;
663         if (!eina_value_set(&value, it->spec.f.def))
664           {
665              eina_value_flush(&value);
666              goto err;
667           }
668         break;
669
670       case ELM_PREFS_TYPE_DATE:
671       {
672          struct timeval val;
673          struct tm t;
674
675          memset(&t, 0, sizeof t);
676          memset(&val, 0, sizeof val);
677
678          t.tm_year = it->spec.d.def.y - 1900;
679          t.tm_mon = it->spec.d.def.m - 1;
680          t.tm_mday = it->spec.d.def.d;
681          val.tv_sec = mktime(&t);
682
683          if (!eina_value_setup(&value, EINA_VALUE_TYPE_TIMEVAL)) goto err;
684          if (!eina_value_set(&value, val))
685            {
686               eina_value_flush(&value);
687               goto err;
688            }
689       }
690       break;
691
692       case ELM_PREFS_TYPE_TEXT:
693       case ELM_PREFS_TYPE_TEXTAREA:
694         if (!eina_value_setup(&value, EINA_VALUE_TYPE_STRINGSHARE)) goto err;
695         if (!eina_value_set(&value, it->spec.s.placeholder))
696           {
697              eina_value_flush(&value);
698              goto err;
699           }
700         break;
701
702       case ELM_PREFS_TYPE_PAGE:
703       case ELM_PREFS_TYPE_SEPARATOR: //page is the value setter for separators
704       case ELM_PREFS_TYPE_SWALLOW: //prefs is the value setter for swallows
705         return EINA_TRUE;
706
707       default:
708         ERR("bad item (type = %d) found, skipping it", it->type);
709
710         return EINA_FALSE;
711      }
712
713    if (!it->available)
714      {
715         ERR("widget of item %s has been deleted, we can't set values on it",
716             it->name);
717
718         eina_value_flush(&value);
719         return EINA_FALSE;
720      }
721
722    if (!it->w_impl->value_set(it->w_obj, &value)) goto err;
723    else
724      {
725         if (mark_changed) _item_changed_cb(it->w_obj);
726         eina_value_flush(&value);
727         return EINA_TRUE;
728      }
729
730 err:
731    ERR("failed to set value on widget of item %s", it->name);
732
733    return EINA_FALSE;
734 }
735
736 static Eina_Bool
737 _elm_prefs_page_widget_new(Evas_Object *obj,
738                            Elm_Prefs_Page_Node *page)
739 {
740    page->parent = obj;
741
742    if (page->widget)
743      {
744         page->w_impl =
745           eina_hash_find(_elm_prefs_page_widgets_map, page->widget);
746         if (!page->w_impl)
747           {
748              ERR("widget %s is not eligible to implement page %s,"
749                  "trying default widget for pages",
750                  page->widget, page->name);
751           }
752         else goto wid_found;
753      }
754    else
755      INF("no explicit widget declared for page %s,"
756          " trying default widget for pages", page->name);
757
758    page->w_impl = _elm_prefs_page_default_widget;
759    ERR("no widget bound to this page, fallbacking to default widget");
760
761 wid_found:
762
763    page->w_obj = page->w_impl->add(page->w_impl, obj);
764    if (!page->w_obj)
765      {
766         ERR("error while adding UI element to prefs widget %p", obj);
767         return EINA_FALSE;
768      }
769
770    if (page->title && page->w_impl->title_set)
771      {
772         if (!page->w_impl->title_set(page->w_obj, page->title))
773           {
774              ERR("failed to set title %s on page %s",
775                  page->title, page->name);
776           }
777      }
778
779    if (page->sub_title && page->w_impl->sub_title_set)
780      {
781         if (!page->w_impl->sub_title_set(page->w_obj, page->sub_title))
782           {
783              ERR("failed to set sub_title %s on page %s",
784                  page->sub_title, page->name);
785           }
786      }
787
788    if (page->icon && page->w_impl->icon_set)
789      {
790         if (!page->w_impl->icon_set(page->w_obj, page->icon))
791           {
792              ERR("failed to set icon %s on page %s",
793                  page->icon, page->name);
794           }
795      }
796
797    evas_object_data_set(page->w_obj, "prefs_page", page);
798
799    evas_object_show(page->w_obj);
800
801    return EINA_TRUE;
802 }
803
804 static void
805 _elm_prefs_item_external_label_inject(Elm_Prefs_Item_Node *it)
806 {
807    Evas_Object *label = elm_label_add(it->w_obj);
808    elm_layout_text_set(label, NULL, it->label);
809    evas_object_data_set(it->w_obj, "label_widget", label);
810    evas_object_show(label);
811 }
812
813 static void
814 _elm_prefs_item_external_icon_inject(Elm_Prefs_Item_Node *it)
815 {
816    Evas_Object *icon = elm_icon_add(it->w_obj);
817    elm_icon_standard_set(icon, it->icon);
818    elm_image_resizable_set(icon, EINA_FALSE, EINA_FALSE);
819    evas_object_data_set(it->w_obj, "icon_widget", icon);
820    evas_object_show(icon);
821 }
822
823 static void
824 _elm_prefs_item_properties_apply(Elm_Prefs_Item_Node *item)
825 {
826    if (item->label)
827      {
828         if (item->w_impl->label_set)
829           {
830              if (!item->w_impl->label_set(item->w_obj, item->label))
831                {
832                   ERR("failed to set label %s on item %s through widget"
833                       " implementation method, fallbacking to page's "
834                       "method", item->label, item->name);
835
836                   _elm_prefs_item_external_label_inject(item);
837                }
838           }
839         else
840           _elm_prefs_item_external_label_inject(item);
841      }
842
843    if (item->icon)
844      {
845         if (item->w_impl->icon_set)
846           {
847              if (!item->w_impl->icon_set(item->w_obj, item->icon))
848                {
849                   ERR("failed to set icon %s on item %s through widget"
850                       " implementation method, fallbacking to page's "
851                       "method", item->label, item->name);
852
853                   _elm_prefs_item_external_icon_inject(item);
854                }
855           }
856         else
857           _elm_prefs_item_external_icon_inject(item);
858      }
859
860    if (item->w_impl->editable_set &&
861        !item->w_impl->editable_set(item->w_obj, item->editable))
862      {
863         ERR("failed to set editability (%d) on item %s",
864             item->editable, item->name);
865      }
866
867    if (item->style && !elm_object_style_set(item->w_obj, item->style))
868      {
869         ERR("failed to set style %s on item %s",
870             item->style, item->name);
871      }
872 }
873
874 static Eina_Bool
875 _elm_prefs_item_widget_new(Evas_Object *obj,
876                            Elm_Prefs_Page_Node *parent,
877                            Elm_Prefs_Item_Node *item)
878 {
879    item->prefs = obj;
880    item->available = EINA_TRUE;
881    item->page = parent;
882
883    if (item->widget)
884      {
885         item->w_impl =
886           eina_hash_find(_elm_prefs_item_widgets_map, item->widget);
887         if (!item->w_impl)
888           {
889              ERR("widget %s is not eligible to implement item of"
890                  " type %d, trying default widget for that type",
891                  item->widget, item->type);
892           }
893         else goto wid_found;
894      }
895    else
896      INF("no explicit item widget declared for %s,"
897          " trying default widget for its type (%d)", item->name, item->type);
898
899    item->w_impl =
900      eina_hash_find(_elm_prefs_item_type_widgets_map, &item->type);
901
902    if (!item->w_impl)
903      {
904         item->w_impl = _elm_prefs_item_default_widget;
905         ERR("no widget bound to this type, fallbacking to "
906             "default widget");
907      }
908
909 wid_found:
910
911    item->w_obj = item->w_impl->add(
912        item->w_impl, obj, item->type, item->spec, _item_changed_cb);
913    if (!item->w_obj)
914      {
915         ERR("error while adding UI element to prefs widget %p", obj);
916         return EINA_FALSE;
917      }
918
919    evas_object_data_set(item->w_obj, "prefs_item", item);
920
921    _elm_prefs_item_properties_apply(item);
922
923    evas_object_show(item->w_obj);
924
925    return EINA_TRUE;
926 }
927
928 static Elm_Prefs_Page_Node *
929 _elm_prefs_page_load(Evas_Object *obj,
930                      const char  *pname)
931 {
932    Eet_File *eet_file;
933    Elm_Prefs_Page_Node *ret = NULL;
934
935    ELM_PREFS_CHECK(obj) NULL;
936    EINA_SAFETY_ON_NULL_RETURN_VAL(pname, NULL);
937
938    ELM_PREFS_DATA_GET(obj, sd);
939
940    eet_file = eet_open(sd->file, EET_FILE_MODE_READ);
941
942    if (eet_file)
943      {
944         ret = eet_data_read(eet_file, _page_edd, pname);
945         eet_close(eet_file);
946
947         if (!ret)
948           ERR("problem while reading from file %s, key %s", sd->file, pname);
949         else
950           ret->prefs = obj;
951      }
952    else
953      ERR("failed to load from requested epb file (%s)", sd->file);
954
955    return ret;
956 }
957
958 static Eina_Bool
959 _elm_prefs_page_populate(Elm_Prefs_Page_Node *page,
960                          Evas_Object         *parent)
961 {
962    Elm_Prefs_Page_Node *subpage;
963    Elm_Prefs_Item_Node *it;
964    Eina_List           *l;
965
966    EINA_SAFETY_ON_NULL_RETURN_VAL(page, EINA_FALSE);
967
968    if (!_elm_prefs_page_widget_new(parent, page)) goto err;
969
970    EINA_LIST_FOREACH(page->items, l, it)
971      {
972         if ((it->type <= ELM_PREFS_TYPE_UNKNOWN) ||
973             (it->type > ELM_PREFS_TYPE_SWALLOW))
974           {
975              ERR("bad item (type = %d) found on page %s, skipping it",
976                  it->type, page->name);
977              continue;
978           }
979         else if (it->type == ELM_PREFS_TYPE_PAGE)
980           {
981              subpage = _elm_prefs_page_load(page->prefs, it->spec.p.source);
982              if (!subpage)
983                {
984                   ERR("subpage %s could not be created inside %s, skipping it",
985                       it->name, page->name);
986                   continue;
987                }
988
989              eina_stringshare_del(subpage->name);
990              subpage->name = eina_stringshare_printf("%s:%s",
991                                                      page->name, it->name);
992
993              if (!_elm_prefs_page_populate(subpage, page->w_obj))
994                {
995                   _page_free(subpage);
996                   goto err;
997                }
998
999              it->prefs     = page->prefs;
1000              it->page      = page;
1001              it->w_obj     = subpage->w_obj;
1002              it->w_impl    = NULL;
1003              it->available = EINA_TRUE;
1004              it->subpage   = subpage;
1005           }
1006         else if (!_elm_prefs_item_widget_new(page->prefs, page, it)) goto err;
1007
1008         if (it->visible && !page->w_impl->item_pack
1009             (page->w_obj, it->w_obj, it->type, it->w_impl))
1010           {
1011              ERR("item %s could not be packed inside page %s",
1012                  it->name, page->name);
1013
1014              goto err;
1015           }
1016      }
1017
1018    return EINA_TRUE;
1019
1020 err:
1021    EINA_LIST_FOREACH(page->items, l, it)
1022      {
1023         ELM_SAFE_FREE(it->w_obj, evas_object_del);
1024         it->w_impl = NULL;
1025      }
1026
1027    ELM_SAFE_FREE(page->w_obj, evas_object_del);
1028    page->w_impl = NULL;
1029
1030    return EINA_FALSE;
1031 }
1032
1033 static void
1034 _elm_prefs_values_get_default(Elm_Prefs_Page_Node *page,
1035                               Eina_Bool mark_changed)
1036 {
1037    Eina_List *l;
1038    Elm_Prefs_Item_Node *it;
1039
1040    EINA_LIST_FOREACH(page->items, l, it)
1041      {
1042         if (it->type == ELM_PREFS_TYPE_PAGE)
1043           _elm_prefs_values_get_default(it->subpage, mark_changed);
1044         else
1045           _prefs_item_widget_value_from_self(it, mark_changed);
1046      }
1047 }
1048
1049 static void
1050 _elm_prefs_values_get_user(Elm_Prefs_Data *sd,
1051                            Elm_Prefs_Page_Node *p)
1052 {
1053    char buf[PATH_MAX];
1054    Eina_List *l;
1055    Eina_Value value;
1056    Elm_Prefs_Item_Node *it;
1057
1058    if (!sd->file) return;
1059
1060    EINA_LIST_FOREACH(p->items, l, it)
1061      {
1062         Eina_Bool get_err = EINA_FALSE, set_err = EINA_FALSE;
1063
1064         if (it->type == ELM_PREFS_TYPE_PAGE)
1065           {
1066              Elm_Prefs_Page_Node *subp = it->subpage;
1067
1068              if (!elm_prefs_data_value_get
1069                  (sd->prefs_data, subp->name, NULL, &value))
1070                {
1071                   INF("failed to fetch value for item %s on user data, "
1072                       "writing UI value back on it", it->name);
1073
1074                   if (eina_value_setup(&value, EINA_VALUE_TYPE_STRINGSHARE) &&
1075                       eina_value_set(&value, subp->name))
1076                     {
1077                        sd->changing_from_ui = EINA_TRUE;
1078                        elm_prefs_data_value_set
1079                           (sd->prefs_data, subp->name, it->type, &value);
1080                        sd->changing_from_ui = EINA_FALSE;
1081                     }
1082                }
1083
1084              _elm_prefs_values_get_user(sd, subp);
1085
1086              eina_value_flush(&value);
1087              continue;
1088           }
1089
1090         if (!_elm_prefs_item_has_value(it)) continue;
1091         if (!it->persistent) continue;
1092
1093         snprintf(buf, sizeof(buf), "%s:%s", p->name, it->name);
1094
1095         if (!elm_prefs_data_value_get(sd->prefs_data, buf, NULL, &value))
1096           get_err = EINA_TRUE;
1097         else if (!_prefs_item_widget_value_from_data(sd, it, &value))
1098           set_err = EINA_TRUE;
1099
1100         if (get_err || set_err)
1101           {
1102              if (get_err)
1103                INF("failed to fetch value for item %s on user data, "
1104                    "writing UI value back on it", it->name);
1105
1106              /* force writing back our default value for it */
1107              if (it->available)
1108                {
1109                   if (!it->w_impl->value_get(it->w_obj, &value))
1110                     ERR("failed to fetch value from widget of item %s",
1111                         it->name);
1112                   else
1113                     {
1114                        sd->changing_from_ui = EINA_TRUE;
1115                        elm_prefs_data_value_set
1116                           (sd->prefs_data, buf, it->type, &value);
1117                        sd->changing_from_ui = EINA_FALSE;
1118                     }
1119                }
1120           }
1121
1122         eina_value_flush(&value);
1123      }
1124 }
1125
1126 EOLIAN static Eina_Bool
1127 _elm_prefs_efl_file_file_set(Eo *obj, Elm_Prefs_Data *sd, const char *file, const char *page)
1128 {
1129    const char *prefix;
1130
1131    if (!_elm_prefs_init_count)
1132      {
1133         CRI("prefs_iface module is not loaded! you can't"
1134             " create prefs widgets");
1135         return EINA_FALSE;
1136      }
1137    prefix = elm_app_data_dir_get();
1138    if (!strlen(prefix))
1139      {
1140         WRN("we could not figure out the program's data"
1141             " dir, fallbacking to local directory.");
1142         prefix = ".";
1143      }
1144
1145    if (!file)
1146      sd->file = eina_stringshare_printf("%s/%s", prefix, "preferences.epb");
1147    else
1148      {
1149         if (*file != '/') /* relative */
1150           sd->file = eina_stringshare_printf("%s/%s", prefix, file);
1151         else
1152           sd->file = eina_stringshare_add(file);
1153      }
1154
1155    sd->page = eina_stringshare_add(page ? page : "main");
1156
1157    sd->root = _elm_prefs_page_load(obj, sd->page);
1158    if (!sd->root) return EINA_FALSE;
1159
1160    if (!_elm_prefs_page_populate(sd->root, obj))
1161      {
1162         _root_node_free(sd);
1163         sd->root = NULL;
1164
1165         return EINA_FALSE;
1166      }
1167
1168    elm_widget_resize_object_set(obj, sd->root->w_obj, EINA_TRUE);
1169
1170    _elm_prefs_values_get_default(sd->root, EINA_FALSE);
1171
1172    evas_object_smart_callback_call
1173       (obj, SIG_PAGE_LOADED, (char *)sd->root->name);
1174
1175    return EINA_TRUE;
1176 }
1177
1178 EOLIAN static void
1179 _elm_prefs_efl_file_file_get(Eo *obj EINA_UNUSED, Elm_Prefs_Data *sd, const char **file, const char **page)
1180 {
1181    if (file) *file = sd->file;
1182    if (page) *page = sd->page;
1183 }
1184
1185 EOLIAN static Eina_Bool
1186 _elm_prefs_data_set(Eo *obj, Elm_Prefs_Data *sd, Elm_Prefs_Data *prefs_data)
1187 {
1188    if (!sd->root) return EINA_FALSE;
1189
1190    if (prefs_data && !_elm_prefs_data_cbs_add(obj, prefs_data))
1191       return EINA_FALSE;
1192
1193    if (sd->prefs_data)
1194      {
1195         _elm_prefs_data_cbs_del(obj);
1196
1197         elm_prefs_data_unref(sd->prefs_data);
1198      }
1199
1200    sd->prefs_data = prefs_data;
1201
1202    if (!sd->prefs_data)
1203      {
1204         INF("resetting prefs to default values");
1205         _elm_prefs_values_get_default(sd->root, EINA_FALSE);
1206
1207         goto end;
1208      }
1209
1210    elm_prefs_data_ref(sd->prefs_data);
1211
1212    sd->values_fetching = EINA_TRUE;
1213    _elm_prefs_values_get_user(sd, sd->root);
1214    sd->values_fetching = EINA_FALSE;
1215
1216 end:
1217    evas_object_smart_callback_call
1218      (obj, SIG_PAGE_CHANGED, (char *)sd->root->name);
1219
1220    return EINA_TRUE;
1221 }
1222
1223 EOLIAN static Elm_Prefs_Data*
1224 _elm_prefs_data_get(Eo *obj EINA_UNUSED, Elm_Prefs_Data *sd)
1225 {
1226    if (!sd->root) return NULL;
1227    else return sd->prefs_data;
1228 }
1229
1230 EOLIAN static void
1231 _elm_prefs_autosave_set(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, Eina_Bool autosave)
1232 {
1233    ELM_PREFS_DATA_GET(obj, sd);
1234
1235    autosave = !!autosave;
1236
1237    if (sd->autosave != autosave)
1238      sd->autosave = autosave;
1239    else
1240      return;
1241
1242    if ((sd->autosave) && (sd->dirty))
1243      {
1244         if (sd->saving_poller) return;
1245
1246         sd->saving_poller = ecore_poller_add
1247             (ECORE_POLLER_CORE, 1, _elm_prefs_save, obj);
1248      }
1249    else if ((!sd->autosave) && (sd->saving_poller))
1250      {
1251         ecore_poller_del(sd->saving_poller);
1252         sd->saving_poller = NULL;
1253
1254         _elm_prefs_save(obj);
1255      }
1256 }
1257
1258 EOLIAN static Eina_Bool
1259 _elm_prefs_autosave_get(Eo *obj EINA_UNUSED, Elm_Prefs_Data *sd)
1260 {
1261    return sd->autosave;
1262 }
1263
1264 EOLIAN static void
1265 _elm_prefs_reset(Eo *obj EINA_UNUSED, Elm_Prefs_Data *sd, Elm_Prefs_Reset_Mode mode)
1266 {
1267    EINA_SAFETY_ON_NULL_RETURN(sd->root);
1268
1269    if (mode == ELM_PREFS_RESET_DEFAULTS)
1270      _elm_prefs_values_get_default(sd->root, EINA_TRUE);
1271    else if (mode == ELM_PREFS_RESET_LAST)
1272      WRN("ELM_PREFS_RESET_LAST not implemented yet");
1273 }
1274
1275 static Elm_Prefs_Item_Node *
1276 _elm_prefs_item_api_entry_common(const Evas_Object *obj,
1277                                  const char *it_name)
1278 {
1279    Elm_Prefs_Item_Node *ret;
1280
1281    ELM_PREFS_CHECK(obj) NULL;
1282    ELM_PREFS_DATA_GET(obj, sd);
1283
1284    EINA_SAFETY_ON_NULL_RETURN_VAL(it_name, NULL);
1285
1286    EINA_SAFETY_ON_NULL_RETURN_VAL(sd->root, NULL);
1287
1288    ret = _elm_prefs_item_node_by_name(sd, it_name);
1289
1290    if (!ret) ERR("item with name %s does not exist on file %s",
1291                  it_name, sd->file);
1292
1293    return ret;
1294 }
1295
1296 EOLIAN static Eina_Bool
1297 _elm_prefs_item_value_set(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name, const Eina_Value *value)
1298 {
1299    const Eina_Value_Type *t, *def_t;
1300    Elm_Prefs_Item_Node *it;
1301    Eina_Value it_val;
1302
1303    it = _elm_prefs_item_api_entry_common(obj, name);
1304    if (!it) return EINA_FALSE;
1305
1306    if (!_elm_prefs_item_has_value(it))
1307      {
1308         ERR("item %s has no underlying value, you can't operate on it",
1309             it->name);
1310         return EINA_FALSE;
1311      }
1312
1313    EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE);
1314    t = eina_value_type_get(value);
1315    if (!t) return EINA_FALSE;
1316
1317    if (!it->available)
1318      {
1319         ERR("widget of item %s has been deleted, we can't set values on it",
1320             it->name);
1321         return EINA_FALSE;
1322      }
1323
1324    if (!it->w_impl->value_get(it->w_obj, &it_val))
1325      {
1326         ERR("failed to fetch value from widget of item %s", it->name);
1327         goto err;
1328      }
1329
1330    def_t = eina_value_type_get(&it_val);
1331    if ((t != def_t) && (!eina_value_convert(value, &it_val)))
1332      {
1333         eina_value_flush(&it_val);
1334         ERR("failed to convert value to proper type");
1335         goto err;
1336      }
1337    else if (!eina_value_copy(value, &it_val) ||
1338             (!it->w_impl->value_set(it->w_obj, &it_val)))
1339      {
1340         eina_value_flush(&it_val);
1341         ERR("failed to set value on widget of item %s", it->name);
1342         goto err;
1343      }
1344
1345    eina_value_flush(&it_val);
1346    return EINA_TRUE;
1347
1348 err:
1349    return EINA_FALSE;
1350 }
1351
1352 EOLIAN static Eina_Bool
1353 _elm_prefs_item_value_get(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name, Eina_Value *value)
1354 {
1355    Elm_Prefs_Item_Node *it;
1356
1357    it = _elm_prefs_item_api_entry_common(obj, name);
1358    if (!it) return EINA_FALSE;
1359
1360    if (!_elm_prefs_item_has_value(it))
1361      {
1362         ERR("item %s has no underlying value, you can't operate on it",
1363             it->name);
1364         return EINA_FALSE;
1365      }
1366
1367    if (!value) return EINA_FALSE;
1368
1369    if (!it->available)
1370      {
1371         ERR("widget of item %s has been deleted, we can't set values on it",
1372             it->name);
1373         return EINA_FALSE;
1374      }
1375
1376    if (!it->w_impl->value_get(it->w_obj, value))
1377      {
1378         ERR("failed to fetch value from widget of item %s", it->name);
1379         return EINA_FALSE;
1380      }
1381
1382    return EINA_TRUE;
1383 }
1384
1385 EOLIAN static const Evas_Object*
1386 _elm_prefs_item_object_get(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name)
1387 {
1388    Elm_Prefs_Item_Node *it;
1389
1390    it = _elm_prefs_item_api_entry_common(obj, name);
1391    if (!it) return NULL;
1392    else return it->w_obj;
1393 }
1394
1395 EOLIAN static void
1396 _elm_prefs_item_visible_set(Eo *obj EINA_UNUSED, Elm_Prefs_Data *sd, const char *name, Eina_Bool visible)
1397 {
1398    Elm_Prefs_Item_Node *it;
1399    Eina_List *l;
1400    Evas_Object *lbl, *icon;
1401
1402    EINA_SAFETY_ON_NULL_RETURN(name);
1403    EINA_SAFETY_ON_NULL_RETURN(sd->root);
1404
1405    l = _elm_prefs_item_list_node_by_name(sd, name);
1406    if (!l) return;
1407
1408    it = eina_list_data_get(l);
1409
1410    visible = !!visible;
1411
1412    if (it->visible == visible) return;
1413    it->visible = visible;
1414
1415    if (!it->available)
1416      {
1417         ERR("widget of item %s has been deleted, we can't act on it",
1418             it->name);
1419         return;
1420      }
1421
1422    lbl = evas_object_data_get(it->w_obj, "label_widget");
1423    icon = evas_object_data_get(it->w_obj, "icon_widget");
1424
1425    if (!it->visible)
1426      {
1427         if (!it->page->w_impl->item_unpack(it->page->w_obj, it->w_obj))
1428           {
1429              ERR("failed to unpack item %s from page %s!",
1430                  it->name, it->page->name);
1431           }
1432         else
1433           {
1434              if (lbl) evas_object_hide(lbl);
1435              if (icon) evas_object_hide(icon);
1436              evas_object_hide(it->w_obj);
1437           }
1438      }
1439    else if (it->available)
1440      {
1441         Eina_List *p_l;
1442
1443         if ((p_l = eina_list_prev(l)))
1444           {
1445              Elm_Prefs_Item_Node *p_it = eina_list_data_get(p_l);
1446
1447              if (!it->page->w_impl->item_pack_after
1448                    (it->page->w_obj, it->w_obj,
1449                     p_it->w_obj, it->type, it->w_impl))
1450                {
1451                   ERR("failed to pack item %s on page %s!",
1452                       it->name, it->page->name);
1453                }
1454           }
1455         else if ((p_l = eina_list_next(l)))
1456           {
1457              Elm_Prefs_Item_Node *n_it = eina_list_data_get(p_l);
1458
1459              if (!it->page->w_impl->item_pack_before
1460                    (it->page->w_obj, it->w_obj,
1461                     n_it->w_obj, it->type, it->w_impl))
1462                {
1463                   ERR("failed to pack item %s on page %s!",
1464                       it->name, it->page->name);
1465                }
1466           }
1467         else if (!it->page->w_impl->item_pack
1468                    (it->page->w_obj, it->w_obj, it->type, it->w_impl))
1469           ERR("failed to pack item %s on page %s!", it->name, it->page->name);
1470
1471         if (lbl) evas_object_show(lbl);
1472         if (icon) evas_object_show(icon);
1473         evas_object_show(it->w_obj);
1474      }
1475 }
1476
1477 EOLIAN static Eina_Bool
1478 _elm_prefs_item_visible_get(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name)
1479 {
1480    Elm_Prefs_Item_Node *it;
1481
1482    it = _elm_prefs_item_api_entry_common(obj, name);
1483    if (!it) return EINA_FALSE;
1484
1485    if (!it->available)
1486      {
1487         ERR("widget of item %s has been deleted, we can't act on it",
1488             it->name);
1489         return EINA_FALSE;
1490      }
1491
1492    return it->visible;
1493 }
1494
1495 EOLIAN static void
1496 _elm_prefs_item_disabled_set(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name, Eina_Bool disabled)
1497 {
1498    Elm_Prefs_Item_Node *it;
1499
1500    it = _elm_prefs_item_api_entry_common(obj, name);
1501    if (!it) return;
1502
1503    if (!it->available)
1504      {
1505         ERR("widget of item %s has been deleted, we can't act on it",
1506             it->name);
1507         return;
1508      }
1509
1510    elm_object_disabled_set(it->w_obj, disabled);
1511 }
1512
1513 EOLIAN static Eina_Bool
1514 _elm_prefs_item_disabled_get(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name)
1515 {
1516    Elm_Prefs_Item_Node *it;
1517
1518    it = _elm_prefs_item_api_entry_common(obj, name);
1519    if (!it) return EINA_FALSE;
1520
1521    if (!it->available)
1522      {
1523         ERR("widget of item %s has been deleted, we can't act on it",
1524             it->name);
1525         return EINA_FALSE;
1526      }
1527
1528    return elm_object_disabled_get(it->w_obj);
1529 }
1530
1531 EOLIAN static void
1532 _elm_prefs_item_editable_set(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name, Eina_Bool editable)
1533 {
1534    Elm_Prefs_Item_Node *it;
1535
1536    it = _elm_prefs_item_api_entry_common(obj, name);
1537    if (!it) return;
1538
1539    if (!it->w_impl->editable_set)
1540      {
1541         ERR("the item %s does not implement the 'editable' "
1542             "property (using widget %s)", it->name, it->widget);
1543         return;
1544      }
1545
1546    it->w_impl->editable_set(it->w_obj, editable);
1547 }
1548
1549 EOLIAN static Eina_Bool
1550 _elm_prefs_item_editable_get(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name)
1551 {
1552    Elm_Prefs_Item_Node *it;
1553
1554    it = _elm_prefs_item_api_entry_common(obj, name);
1555    if (!it) return EINA_FALSE;
1556
1557    if (!it->w_impl->editable_get)
1558      {
1559         ERR("the item %s does not implement the 'editable' "
1560             "property (using widget %s)", it->name, it->widget);
1561         return EINA_FALSE;
1562      }
1563
1564    return it->w_impl->editable_get(it->w_obj);
1565 }
1566
1567 EOLIAN static Eina_Bool
1568 _elm_prefs_item_swallow(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name, Evas_Object *child)
1569 {
1570    Eina_Value v;
1571    Eina_Bool ret = EINA_FALSE;
1572
1573    Elm_Prefs_Item_Node *it = _elm_prefs_item_api_entry_common(obj, name);
1574    if (!it) return EINA_FALSE;
1575
1576    if (it->type != ELM_PREFS_TYPE_SWALLOW)
1577      {
1578         ERR("item %s does not match a SWALLOW item", name);
1579         return EINA_FALSE;
1580      }
1581
1582    if (!eina_value_setup(&v, EINA_VALUE_TYPE_UINT64)) return EINA_FALSE;
1583    if (!eina_value_set(&v, child))
1584      {
1585         eina_value_flush(&v);
1586         return EINA_FALSE;
1587      }
1588
1589    ret = it->w_impl->value_set(it->w_obj, &v);
1590    eina_value_flush(&v);
1591
1592    return ret;
1593 }
1594
1595 EOLIAN static Evas_Object*
1596 _elm_prefs_item_unswallow(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name)
1597 {
1598    Eina_Value v;
1599    Evas_Object *ret = NULL;
1600
1601    Elm_Prefs_Item_Node *it = _elm_prefs_item_api_entry_common(obj, name);
1602    if (!it) return NULL;
1603
1604    if (it->type != ELM_PREFS_TYPE_SWALLOW)
1605      {
1606         ERR("item %s does not match a SWALLOW item", name);
1607         return NULL;
1608      }
1609
1610    if (!(it->w_impl->value_get(it->w_obj, &v))) return NULL;
1611
1612    if (eina_value_type_get(&v) != EINA_VALUE_TYPE_UINT64 ||
1613        !eina_value_get(&v, ret))
1614      {
1615         eina_value_flush(&v);
1616         return NULL;
1617      }
1618
1619    eina_value_flush(&v);
1620
1621    return ret;
1622 }
1623
1624 static unsigned int
1625 elm_prefs_item_iface_abi_version_get(void)
1626 {
1627    return ELM_PREFS_ITEM_IFACE_ABI_VERSION;
1628 }
1629
1630 EAPI void
1631 elm_prefs_item_iface_register(const Elm_Prefs_Item_Iface_Info *array)
1632 {
1633    const Elm_Prefs_Item_Iface_Info *itr;
1634    unsigned int abi_version = elm_prefs_item_iface_abi_version_get();
1635
1636    if (!array)
1637      return;
1638
1639    for (itr = array; itr->widget_name; itr++)
1640      {
1641         const Elm_Prefs_Item_Type *t_itr;
1642
1643         if (itr->info->abi_version != abi_version)
1644           {
1645              ERR("external prefs widget interface '%s' (%p) has incorrect ABI "
1646                  "version. got %#x where %#x was expected.",
1647                  itr->widget_name, itr->info, itr->info->abi_version,
1648                  abi_version);
1649              continue;
1650           }
1651
1652         /* FIXME: registering the 1st, for now */
1653         if (!_elm_prefs_item_default_widget)
1654           _elm_prefs_item_default_widget = itr->info;
1655
1656         eina_hash_direct_add
1657           (_elm_prefs_item_widgets_map, itr->widget_name, itr->info);
1658
1659         for (t_itr = itr->info->types;
1660              *t_itr != ELM_PREFS_TYPE_UNKNOWN; t_itr++)
1661           eina_hash_add(_elm_prefs_item_type_widgets_map, t_itr, itr->info);
1662      }
1663 }
1664
1665 EAPI void
1666 elm_prefs_item_iface_unregister(const Elm_Prefs_Item_Iface_Info *array)
1667 {
1668    const Elm_Prefs_Item_Iface_Info *itr;
1669
1670    if (!array)
1671      return;
1672
1673    for (itr = array; itr->widget_name; itr++)
1674      {
1675         const Elm_Prefs_Item_Type *t_itr;
1676
1677         eina_hash_del(_elm_prefs_item_widgets_map, itr->widget_name, itr->info);
1678
1679         for (t_itr = itr->info->types;
1680              *t_itr != ELM_PREFS_TYPE_UNKNOWN; t_itr++)
1681           eina_hash_del
1682             (_elm_prefs_item_type_widgets_map, t_itr, itr->info);
1683      }
1684 }
1685
1686 static unsigned int
1687 elm_prefs_page_iface_abi_version_get(void)
1688 {
1689    return ELM_PREFS_PAGE_IFACE_ABI_VERSION;
1690 }
1691
1692 EAPI void
1693 elm_prefs_page_iface_register(const Elm_Prefs_Page_Iface_Info *array)
1694 {
1695    const Elm_Prefs_Page_Iface_Info *itr;
1696    unsigned int abi_version = elm_prefs_page_iface_abi_version_get();
1697
1698    if (!array)
1699      return;
1700
1701    for (itr = array; itr->widget_name; itr++)
1702      {
1703         if (itr->info->abi_version != abi_version)
1704           {
1705              ERR("external prefs widget interface '%s' (%p) has incorrect ABI "
1706                  "version. got %#x where %#x was expected.",
1707                  itr->widget_name, itr->info, itr->info->abi_version,
1708                  abi_version);
1709              continue;
1710           }
1711
1712         /* FIXME: registering the 1st, for now */
1713         if (!_elm_prefs_page_default_widget)
1714           _elm_prefs_page_default_widget = itr->info;
1715
1716         eina_hash_direct_add
1717           (_elm_prefs_page_widgets_map, itr->widget_name, itr->info);
1718      }
1719 }
1720
1721 EAPI void
1722 elm_prefs_page_iface_unregister(const Elm_Prefs_Page_Iface_Info *array)
1723 {
1724    const Elm_Prefs_Page_Iface_Info *itr;
1725
1726    if (!array)
1727      return;
1728
1729    for (itr = array; itr->widget_name; itr++)
1730      eina_hash_del
1731        (_elm_prefs_page_widgets_map, itr->widget_name, itr->info);
1732 }
1733
1734 /* TODO: evaluate if it's sane to handle it better */
1735 /* static void */
1736 /* _prefs_page_del_cb(void *data EINA_UNUSED, */
1737 /*                    Evas *evas EINA_UNUSED, */
1738 /*                    Evas_Object *obj, */
1739 /*                    void *event_info EINA_UNUSED) */
1740 /* { */
1741 /*    Elm_Prefs_Page_Node *page; */
1742
1743 /*    evas_object_event_callback_del(obj, EVAS_CALLBACK_DEL, _prefs_page_del_cb); */
1744
1745 /*    page = evas_object_data_get(obj, "prefs_page"); */
1746
1747 /*    ELM_PREFS_DATA_GET(page->prefs, sd); */
1748
1749 /*    if (!sd->delete_me) */
1750 /*      { */
1751 /*          Eina_List *l; */
1752 /*          Elm_Prefs_Item_Node *it; */
1753
1754 /*         /\* force writing back the value for it *\/ */
1755 /*         EINA_LIST_FOREACH(page->items, l, it) */
1756 /*           _item_changed_cb(it->w_obj); */
1757 /*      } */
1758
1759 /*    evas_object_data_del(obj, "prefs_page"); */
1760 /*    page->w_obj = NULL; */
1761 /* } */
1762
1763 static void
1764 _prefs_item_del_cb(void *data EINA_UNUSED,
1765                    Evas *evas EINA_UNUSED,
1766                    Evas_Object *obj,
1767                    void *event_info EINA_UNUSED)
1768 {
1769    Elm_Prefs_Item_Node *it;
1770    Evas_Object *lbl, *icon;
1771
1772    evas_object_event_callback_del(obj, EVAS_CALLBACK_DEL, _prefs_item_del_cb);
1773
1774    it = evas_object_data_get(obj, "prefs_item");
1775    lbl = evas_object_data_del(it->w_obj, "label_widget");
1776    evas_object_del(lbl);
1777
1778    icon = evas_object_data_del(it->w_obj, "icon_widget");
1779    evas_object_del(icon);
1780
1781    ELM_PREFS_DATA_GET(it->prefs, sd);
1782
1783    if (!sd->delete_me)
1784      /* force writing back the value for it */
1785      _item_changed_cb(obj);
1786
1787    evas_object_data_del(obj, "prefs_item");
1788    it->available = EINA_FALSE;
1789
1790    it->w_obj = NULL;
1791 }
1792
1793 EAPI Eina_Bool
1794 elm_prefs_item_widget_common_add(Evas_Object *prefs EINA_UNUSED,
1795                                  Evas_Object *obj)
1796 {
1797    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
1798
1799    evas_object_event_callback_add
1800      (obj, EVAS_CALLBACK_DEL, _prefs_item_del_cb, NULL);
1801
1802    return EINA_TRUE;
1803 }
1804
1805 EAPI Eina_Bool
1806 elm_prefs_page_widget_common_add(Evas_Object *prefs EINA_UNUSED,
1807                                  Evas_Object *obj)
1808 {
1809    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
1810
1811    /* evas_object_event_callback_add */
1812    /*   (obj, EVAS_CALLBACK_DEL, _prefs_page_del_cb, NULL); */
1813
1814    return EINA_TRUE;
1815 }
1816
1817 void
1818 _elm_prefs_init(void)
1819 {
1820    Elm_Module *m;
1821
1822    if (++_elm_prefs_init_count != 1)
1823      return;
1824
1825    _elm_prefs_descriptors_init();
1826    _elm_prefs_data_init();
1827
1828    if (!(m = _elm_module_find_as("prefs_iface")))
1829      {
1830         WRN("prefs iface module could not be loaded,"
1831             " the prefs widget won't function");
1832
1833         --_elm_prefs_init_count;
1834         _elm_prefs_descriptors_shutdown();
1835         _elm_prefs_data_shutdown();
1836         return;
1837      }
1838
1839    _elm_prefs_page_widgets_map = eina_hash_string_superfast_new(NULL);
1840    _elm_prefs_item_widgets_map = eina_hash_string_superfast_new(NULL);
1841    _elm_prefs_item_type_widgets_map = eina_hash_int32_new(NULL);
1842
1843    m->init_func(m);
1844 }
1845
1846 void
1847 _elm_prefs_shutdown(void)
1848 {
1849    if (_elm_prefs_init_count < 0)
1850      {
1851         EINA_LOG_ERR("Init count not greater than 0 in shutdown.");
1852         return;
1853      }
1854    if (--_elm_prefs_init_count != 0) return;
1855
1856    _elm_prefs_descriptors_shutdown();
1857    _elm_prefs_data_shutdown();
1858
1859    eina_hash_free(_elm_prefs_page_widgets_map);
1860    eina_hash_free(_elm_prefs_item_widgets_map);
1861    eina_hash_free(_elm_prefs_item_type_widgets_map);
1862
1863    /* all modules shutdown calls will taken place elsewhere */
1864 }
1865
1866 static void
1867 _elm_prefs_class_constructor(Eo_Class *klass)
1868 {
1869    evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
1870 }
1871
1872 EAPI Eina_Bool
1873 elm_prefs_file_set(Eo *obj, const char *file, const char *page)
1874 {
1875    return eo_do((Eo *) obj, efl_file_set(file, page));
1876 }
1877
1878 EAPI Eina_Bool
1879 elm_prefs_file_get(const Eo *obj, const char **file, const char **page)
1880 {
1881    eo_do((Eo *) obj, efl_file_get(file, page));
1882
1883    return EINA_TRUE;
1884 }
1885
1886 #include "elm_prefs.eo.c"