#ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_ACCESS_OBJECT_PROTECTED #include #include "elm_priv.h" #include "elm_prefs_eo.h" #include "elm_widget_prefs.h" #include "elm_prefs_edd.x" #define MY_CLASS ELM_PREFS_CLASS #define MY_CLASS_NAME "Elm_Prefs" #define MY_CLASS_NAME_LEGACY "elm_prefs" static const char SIG_PAGE_CHANGED[] = "page,changed"; static const char SIG_PAGE_SAVED[] = "page,saved"; static const char SIG_PAGE_RESET[] = "page,reset"; static const char SIG_PAGE_LOADED[] = "page,loaded"; static const char SIG_ITEM_CHANGED[] = "item,changed"; static const char SIG_ACTION[] = "action"; static const Evas_Smart_Cb_Description _elm_prefs_smart_callbacks[] = { { SIG_PAGE_CHANGED, "s" }, { SIG_PAGE_SAVED, "s" }, { SIG_PAGE_RESET, "s" }, { SIG_PAGE_LOADED, "s" }, { SIG_ITEM_CHANGED, "s" }, { SIG_ACTION, "ss" }, { NULL, NULL} }; static int _elm_prefs_init_count = 0; static Eina_Hash *_elm_prefs_page_widgets_map = NULL; static const Elm_Prefs_Page_Iface *_elm_prefs_page_default_widget = NULL; static Eina_Hash *_elm_prefs_item_widgets_map = NULL; static Eina_Hash *_elm_prefs_item_type_widgets_map = NULL; static const Elm_Prefs_Item_Iface *_elm_prefs_item_default_widget = NULL; static void _elm_prefs_values_get_default(Elm_Prefs_Page_Node *, Eina_Bool); static Eina_Bool _prefs_item_widget_value_from_self(Elm_Prefs_Item_Node *, Eina_Bool); EOLIAN static void _elm_prefs_efl_canvas_group_group_add(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED) { efl_canvas_group_add(efl_super(obj, MY_CLASS)); } static void _item_free(Elm_Prefs_Item_Node *it); static void _page_free(Elm_Prefs_Page_Node *p) { Elm_Prefs_Item_Node *it; if (!p) return; eina_stringshare_del(p->name); eina_stringshare_del(p->title); eina_stringshare_del(p->sub_title); eina_stringshare_del(p->widget); eina_stringshare_del(p->style); eina_stringshare_del(p->icon); evas_object_del(p->w_obj); EINA_LIST_FREE(p->items, it) _item_free(it); free(p); } static void _item_free(Elm_Prefs_Item_Node *it) { switch (it->type) { case ELM_PREFS_TYPE_ACTION: case ELM_PREFS_TYPE_BOOL: case ELM_PREFS_TYPE_INT: case ELM_PREFS_TYPE_FLOAT: case ELM_PREFS_TYPE_LABEL: case ELM_PREFS_TYPE_DATE: case ELM_PREFS_TYPE_RESET: case ELM_PREFS_TYPE_SAVE: case ELM_PREFS_TYPE_SEPARATOR: case ELM_PREFS_TYPE_SWALLOW: break; case ELM_PREFS_TYPE_PAGE: eina_stringshare_del(it->spec.p.source); _page_free(it->subpage); it->w_obj = NULL; break; case ELM_PREFS_TYPE_TEXT: case ELM_PREFS_TYPE_TEXTAREA: eina_stringshare_del(it->spec.s.placeholder); eina_stringshare_del(it->spec.s.accept); eina_stringshare_del(it->spec.s.deny); break; default: ERR("bad item (type = %d), skipping it", it->type); break; } eina_stringshare_del(it->name); eina_stringshare_del(it->label); eina_stringshare_del(it->icon); eina_stringshare_del(it->style); eina_stringshare_del(it->widget); evas_object_del(it->w_obj); /* we have to delete them ourselves * because of _prefs_item_del_cb() -- * it'll need the parent alive, to * gather its smart data bit */ free(it); } static Eina_Bool _elm_prefs_save(void *data) { ELM_PREFS_DATA_GET(data, sd); ELM_WIDGET_DATA_GET_OR_RETURN(data, wd, ECORE_CALLBACK_CANCEL); if (!sd->dirty || !sd->prefs_data) goto end; if (!elm_prefs_data_autosave_get(sd->prefs_data)) { elm_prefs_data_save(sd->prefs_data, NULL, NULL); efl_event_callback_legacy_call (wd->obj, ELM_PREFS_EVENT_PAGE_SAVED, (char *)sd->root->name); } sd->dirty = EINA_FALSE; end: sd->saving_poller = NULL; return ECORE_CALLBACK_CANCEL; } static void _root_node_free(Elm_Prefs_Data *sd) { _page_free(sd->root); } static Eina_Bool _prefs_data_types_match(const Eina_Value_Type *t, Elm_Prefs_Item_Type epd_t) { return (t == EINA_VALUE_TYPE_UCHAR && epd_t == ELM_PREFS_TYPE_BOOL) || (t == EINA_VALUE_TYPE_INT && epd_t == ELM_PREFS_TYPE_INT) || (t == EINA_VALUE_TYPE_FLOAT && epd_t == ELM_PREFS_TYPE_FLOAT) || (t == EINA_VALUE_TYPE_TIMEVAL && epd_t == ELM_PREFS_TYPE_DATE) || (t == EINA_VALUE_TYPE_STRINGSHARE && (epd_t == ELM_PREFS_TYPE_PAGE || epd_t == ELM_PREFS_TYPE_TEXT || epd_t == ELM_PREFS_TYPE_TEXTAREA)); } static Eina_Bool _prefs_data_type_fix(Elm_Prefs_Item_Node *it, Eina_Value *value) { Eina_Value v; Eina_Bool setup_err = EINA_FALSE; switch (it->type) { case ELM_PREFS_TYPE_BOOL: if (!eina_value_setup(&v, EINA_VALUE_TYPE_UCHAR)) setup_err = EINA_TRUE; break; case ELM_PREFS_TYPE_INT: if (!eina_value_setup(&v, EINA_VALUE_TYPE_INT)) setup_err = EINA_TRUE; break; case ELM_PREFS_TYPE_FLOAT: if (!eina_value_setup(&v, EINA_VALUE_TYPE_FLOAT)) setup_err = EINA_TRUE; break; case ELM_PREFS_TYPE_DATE: if (!eina_value_setup(&v, EINA_VALUE_TYPE_TIMEVAL)) setup_err = EINA_TRUE; break; case ELM_PREFS_TYPE_PAGE: case ELM_PREFS_TYPE_TEXT: case ELM_PREFS_TYPE_TEXTAREA: if (!eina_value_setup(&v, EINA_VALUE_TYPE_STRINGSHARE)) setup_err = EINA_TRUE; break; default: ERR("bad item (type = %d) found, skipping it", it->type); return EINA_FALSE; } if (setup_err) return EINA_FALSE; if (!eina_value_convert(value, &v) || !eina_value_copy(&v, value)) { ERR("problem converting mismatching item (%s) value", it->name); return EINA_FALSE; } eina_value_flush(&v); return EINA_TRUE; } static Eina_Bool _prefs_item_widget_value_from_data(Elm_Prefs_Data *sd, Elm_Prefs_Item_Node *it, Eina_Value *value) { char buf[PATH_MAX]; const Eina_Value_Type *t = eina_value_type_get(value); if ((it->type <= ELM_PREFS_TYPE_UNKNOWN) || (it->type > ELM_PREFS_TYPE_SWALLOW)) { ERR("bad item (type = %d) found on page %s, skipping it", it->type, sd->page); return EINA_FALSE; } snprintf(buf, sizeof(buf), "%s:%s", it->page->name, it->name); if (!_prefs_data_types_match(t, it->type)) { if (!_prefs_data_type_fix(it, value)) return EINA_FALSE; else { Eina_Bool v_set; sd->changing_from_ui = EINA_TRUE; v_set = elm_prefs_data_value_set(sd->prefs_data, buf, it->type, value); sd->changing_from_ui = EINA_FALSE; if (!v_set) return EINA_FALSE; } } if (!it->available) { ERR("widget of item %s has been deleted, we can't set values on it", it->name); return EINA_FALSE; } if (!it->w_impl->value_set(it->w_obj, value)) { ERR("failed to set value on widget of item %s", it->name); return EINA_FALSE; } return EINA_TRUE; } static void _elm_prefs_mark_as_dirty(Eo *obj) { ELM_PREFS_DATA_GET(obj, sd); sd->dirty = EINA_TRUE; if (sd->autosave) { if (sd->saving_poller) return; sd->saving_poller = ecore_poller_add (ECORE_POLLER_CORE, 1, _elm_prefs_save, obj); } } static void _elm_prefs_item_changed_report(Eo *obj, Elm_Prefs_Item_Node *it) { char buf[PATH_MAX]; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); snprintf(buf, sizeof(buf), "%s:%s", it->page->name, it->name); efl_event_callback_legacy_call (wd->obj, ELM_PREFS_EVENT_ITEM_CHANGED, buf); } static Elm_Prefs_Item_Node * _elm_prefs_page_item_by_name(Elm_Prefs_Page_Node *p, char **path) { Elm_Prefs_Item_Node *it; Eina_List *l; char *token; token = strsep(path, ":"); EINA_LIST_FOREACH(p->items, l, it) { if (strcmp(it->name, token)) continue; if (!*path) return it; else if (it->type == ELM_PREFS_TYPE_PAGE) return _elm_prefs_page_item_by_name(it->subpage, path); } return NULL; } static Elm_Prefs_Item_Node * _elm_prefs_item_node_by_name(Elm_Prefs_Data *sd, const char *name) { char buf[PATH_MAX]; char *token; char *aux = buf; strncpy(buf, name, PATH_MAX - 1); buf[PATH_MAX - 1] = '\0'; token = strsep(&aux, ":"); if (strcmp(sd->root->name, token)) return NULL; // first token should be the current page name return _elm_prefs_page_item_by_name(sd->root, &aux); } static Eina_List * _elm_prefs_item_list_node_by_name(Elm_Prefs_Data *sd, const char *name) { Elm_Prefs_Item_Node *it; Eina_List *l; EINA_LIST_FOREACH(sd->root->items, l, it) if (!strcmp(it->name, name)) return l; return NULL; } static void _prefs_data_item_changed_cb(void *cb_data, Elm_Prefs_Data_Event_Type type EINA_UNUSED, Elm_Prefs_Data *prefs_data, void *event_info) { Elm_Prefs_Data_Event_Changed *evt = event_info; Eo *obj = cb_data; Elm_Prefs_Item_Node *it; Eina_Value value; ELM_PREFS_DATA_GET(obj, sd); if (sd->changing_from_ui) return; it = _elm_prefs_item_node_by_name(sd, evt->key); if (!it) return; if (elm_prefs_data_value_get(prefs_data, evt->key, NULL, &value)) { if (!_prefs_item_widget_value_from_data(sd, it, &value)) goto end; _elm_prefs_item_changed_report(obj, it); _elm_prefs_mark_as_dirty(obj); } else ERR("failed to fetch value from data after changed event"); end: eina_value_flush(&value); return; } static void _prefs_data_autosaved_cb(void *cb_data, Elm_Prefs_Data_Event_Type type EINA_UNUSED, Elm_Prefs_Data *data EINA_UNUSED, void *event_info) { ELM_PREFS_DATA_GET(cb_data, sd); ELM_WIDGET_DATA_GET_OR_RETURN(cb_data, wd); efl_event_callback_legacy_call (wd->obj, ELM_PREFS_EVENT_PAGE_SAVED, event_info); sd->dirty = EINA_FALSE; } static Eina_Bool _elm_prefs_data_cbs_add(Eo *obj, Elm_Prefs_Data *prefs_data) { if (!elm_prefs_data_event_callback_add (prefs_data, ELM_PREFS_DATA_EVENT_ITEM_CHANGED, _prefs_data_item_changed_cb, obj) || !elm_prefs_data_event_callback_add (prefs_data, ELM_PREFS_DATA_EVENT_GROUP_AUTOSAVED, _prefs_data_autosaved_cb, obj)) { ERR("error while adding item changed event callback to " "prefs data handle, keeping previous data"); return EINA_FALSE; } return EINA_TRUE; } static void _elm_prefs_data_cbs_del(Eo *obj) { ELM_PREFS_DATA_GET(obj, sd); if (!sd->prefs_data) return; if (!elm_prefs_data_event_callback_del (sd->prefs_data, ELM_PREFS_DATA_EVENT_ITEM_CHANGED, _prefs_data_item_changed_cb, obj)) ERR("error while removing item changed event callback from " "prefs data handle"); if (!elm_prefs_data_event_callback_del (sd->prefs_data, ELM_PREFS_DATA_EVENT_GROUP_AUTOSAVED, _prefs_data_autosaved_cb, obj)) ERR("error while removing page autosave event callback from " "prefs data handle"); } EOLIAN static void _elm_prefs_efl_canvas_group_group_del(Eo *obj, Elm_Prefs_Data *sd) { sd->delete_me = EINA_TRUE; if (sd->saving_poller) ecore_poller_del(sd->saving_poller); _elm_prefs_data_cbs_del(obj); if (sd->root) { elm_prefs_data_version_set(sd->prefs_data, sd->root->version); _elm_prefs_save(obj); _root_node_free(sd); } if (sd->prefs_data) elm_prefs_data_unref(sd->prefs_data); eina_stringshare_del(sd->file); eina_stringshare_del(sd->page); efl_canvas_group_del(efl_super(obj, MY_CLASS)); } EAPI Evas_Object * elm_prefs_add(Evas_Object *parent) { EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL); if (!_elm_prefs_init_count) { CRI("prefs_iface module is not loaded! you can't" " create prefs widgets"); return NULL; } return elm_legacy_add(MY_CLASS, parent); } EOLIAN static Eo * _elm_prefs_efl_object_constructor(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED) { obj = efl_constructor(efl_super(obj, MY_CLASS)); efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY); evas_object_smart_callbacks_descriptions_set(obj, _elm_prefs_smart_callbacks); efl_access_object_role_set(obj, EFL_ACCESS_ROLE_REDUNDANT_OBJECT); return obj; } static Eina_Bool _elm_prefs_item_has_value(Elm_Prefs_Item_Node *it) { return (it->type != ELM_PREFS_TYPE_ACTION) && (it->type != ELM_PREFS_TYPE_LABEL) && (it->type != ELM_PREFS_TYPE_RESET) && (it->type != ELM_PREFS_TYPE_SAVE) && (it->type != ELM_PREFS_TYPE_PAGE) && (it->type != ELM_PREFS_TYPE_SEPARATOR) && (it->type != ELM_PREFS_TYPE_SWALLOW); } static void _item_changed_cb(Evas_Object *it_obj) { char buf[PATH_MAX]; Elm_Prefs_Item_Node *it = evas_object_data_get(it_obj, "prefs_item"); /* some widgets mark themselves changed as early as in their add() * interface methods */ if (!it) return; snprintf(buf, sizeof(buf), "%s:%s", it->page->name, it->name); ELM_PREFS_DATA_GET(it->prefs, sd); ELM_WIDGET_DATA_GET_OR_RETURN(it->prefs, wd); if (sd->values_fetching) goto end; /* we use the changed cb on ACTION/RESET/SAVE items specially */ if (it->type == ELM_PREFS_TYPE_ACTION) { efl_event_callback_legacy_call (wd->obj, ELM_PREFS_EVENT_ACTION, buf); return; } else if (it->type == ELM_PREFS_TYPE_RESET) { _elm_prefs_values_get_default(sd->root, EINA_TRUE); _elm_prefs_mark_as_dirty(it->prefs); return; } else if (it->type == ELM_PREFS_TYPE_SAVE) { if (sd->saving_poller) return; sd->saving_poller = ecore_poller_add (ECORE_POLLER_CORE, 1, _elm_prefs_save, it->prefs); return; } if (!it->persistent || !_elm_prefs_item_has_value(it)) return; if (it->w_impl->value_validate && !it->w_impl->value_validate(it->w_obj)) { if (sd->prefs_data) { Eina_Value value; // Restoring to the last valid value. if (!elm_prefs_data_value_get(sd->prefs_data, buf, NULL, &value)) goto restore_fail; if (!it->w_impl->value_set(it->w_obj, &value)) { eina_value_flush(&value); goto restore_fail; } } else { if (!_prefs_item_widget_value_from_self(it, EINA_FALSE)) goto restore_fail; } return; } end: if (sd->prefs_data) { Eina_Value value; if (!it->w_impl->value_get(it->w_obj, &value)) ERR("failed to fetch value from widget of item %s", buf); else { sd->changing_from_ui = EINA_TRUE; elm_prefs_data_value_set(sd->prefs_data, buf, it->type, &value); eina_value_flush(&value); sd->changing_from_ui = EINA_FALSE; } } if (!sd->values_fetching) _elm_prefs_item_changed_report(it->prefs, it); _elm_prefs_mark_as_dirty(it->prefs); return; restore_fail: ERR("failed to restore the last valid value from widget of item %s", buf); } static Eina_Bool _prefs_item_widget_value_from_self(Elm_Prefs_Item_Node *it, Eina_Bool mark_changed) { Eina_Value value; if (!_elm_prefs_item_has_value(it)) return EINA_TRUE; switch (it->type) { case ELM_PREFS_TYPE_BOOL: if (!eina_value_setup(&value, EINA_VALUE_TYPE_UCHAR)) goto err; if (!eina_value_set(&value, it->spec.b.def)) { eina_value_flush(&value); goto err; } break; case ELM_PREFS_TYPE_INT: if (!eina_value_setup(&value, EINA_VALUE_TYPE_INT)) goto err; if (!eina_value_set(&value, it->spec.i.def)) { eina_value_flush(&value); goto err; } break; case ELM_PREFS_TYPE_FLOAT: if (!eina_value_setup(&value, EINA_VALUE_TYPE_FLOAT)) goto err; if (!eina_value_set(&value, it->spec.f.def)) { eina_value_flush(&value); goto err; } break; case ELM_PREFS_TYPE_DATE: { struct timeval val; struct tm t; memset(&t, 0, sizeof t); memset(&val, 0, sizeof val); t.tm_year = it->spec.d.def.y - 1900; t.tm_mon = it->spec.d.def.m - 1; t.tm_mday = it->spec.d.def.d; val.tv_sec = mktime(&t); if (!eina_value_setup(&value, EINA_VALUE_TYPE_TIMEVAL)) goto err; if (!eina_value_set(&value, val)) { eina_value_flush(&value); goto err; } } break; case ELM_PREFS_TYPE_TEXT: case ELM_PREFS_TYPE_TEXTAREA: if (!eina_value_setup(&value, EINA_VALUE_TYPE_STRINGSHARE)) goto err; if (!eina_value_set(&value, it->spec.s.placeholder)) { eina_value_flush(&value); goto err; } break; case ELM_PREFS_TYPE_PAGE: case ELM_PREFS_TYPE_SEPARATOR: //page is the value setter for separators case ELM_PREFS_TYPE_SWALLOW: //prefs is the value setter for swallows return EINA_TRUE; default: ERR("bad item (type = %d) found, skipping it", it->type); return EINA_FALSE; } if (!it->available) { ERR("widget of item %s has been deleted, we can't set values on it", it->name); eina_value_flush(&value); return EINA_FALSE; } if (!it->w_impl->value_set(it->w_obj, &value)) goto err; else { if (mark_changed) _item_changed_cb(it->w_obj); eina_value_flush(&value); return EINA_TRUE; } err: ERR("failed to set value on widget of item %s", it->name); return EINA_FALSE; } static Eina_Bool _elm_prefs_page_widget_new(Evas_Object *obj, Elm_Prefs_Page_Node *page) { page->parent = obj; if (page->widget) { page->w_impl = eina_hash_find(_elm_prefs_page_widgets_map, page->widget); if (!page->w_impl) { ERR("widget %s is not eligible to implement page %s," "trying default widget for pages", page->widget, page->name); } else goto wid_found; } else INF("no explicit widget declared for page %s," " trying default widget for pages", page->name); page->w_impl = _elm_prefs_page_default_widget; ERR("no widget bound to this page, fallbacking to default widget"); wid_found: page->w_obj = page->w_impl->add(page->w_impl, obj); if (!page->w_obj) { ERR("error while adding UI element to prefs widget %p", obj); return EINA_FALSE; } if (page->title && page->w_impl->title_set) { if (!page->w_impl->title_set(page->w_obj, page->title)) { ERR("failed to set title %s on page %s", page->title, page->name); } } if (page->sub_title && page->w_impl->sub_title_set) { if (!page->w_impl->sub_title_set(page->w_obj, page->sub_title)) { ERR("failed to set sub_title %s on page %s", page->sub_title, page->name); } } if (page->icon && page->w_impl->icon_set) { if (!page->w_impl->icon_set(page->w_obj, page->icon)) { ERR("failed to set icon %s on page %s", page->icon, page->name); } } evas_object_data_set(page->w_obj, "prefs_page", page); evas_object_show(page->w_obj); return EINA_TRUE; } static void _elm_prefs_item_external_label_inject(Elm_Prefs_Item_Node *it) { Evas_Object *label = elm_label_add(it->w_obj); elm_layout_text_set(label, NULL, it->label); evas_object_data_set(it->w_obj, "label_widget", label); evas_object_show(label); } static void _elm_prefs_item_external_icon_inject(Elm_Prefs_Item_Node *it) { Evas_Object *icon = elm_icon_add(it->w_obj); elm_icon_standard_set(icon, it->icon); elm_image_resizable_set(icon, EINA_FALSE, EINA_FALSE); evas_object_data_set(it->w_obj, "icon_widget", icon); evas_object_show(icon); } static void _elm_prefs_item_properties_apply(Elm_Prefs_Item_Node *item) { if (item->label) { if (item->w_impl->label_set) { if (!item->w_impl->label_set(item->w_obj, item->label)) { ERR("failed to set label %s on item %s through widget" " implementation method, fallbacking to page's " "method", item->label, item->name); _elm_prefs_item_external_label_inject(item); } } else _elm_prefs_item_external_label_inject(item); } if (item->icon) { if (item->w_impl->icon_set) { if (!item->w_impl->icon_set(item->w_obj, item->icon)) { ERR("failed to set icon %s on item %s through widget" " implementation method, fallbacking to page's " "method", item->label, item->name); _elm_prefs_item_external_icon_inject(item); } } else _elm_prefs_item_external_icon_inject(item); } if (item->w_impl->editable_set && !item->w_impl->editable_set(item->w_obj, item->editable)) { ERR("failed to set editability (%d) on item %s", item->editable, item->name); } if (item->style && !elm_object_style_set(item->w_obj, item->style)) { ERR("failed to set style %s on item %s", item->style, item->name); } } static Eina_Bool _elm_prefs_item_widget_new(Evas_Object *obj, Elm_Prefs_Page_Node *parent, Elm_Prefs_Item_Node *item) { item->prefs = obj; item->available = EINA_TRUE; item->page = parent; if (item->widget) { item->w_impl = eina_hash_find(_elm_prefs_item_widgets_map, item->widget); if (!item->w_impl) { ERR("widget %s is not eligible to implement item of" " type %d, trying default widget for that type", item->widget, item->type); } else goto wid_found; } else INF("no explicit item widget declared for %s," " trying default widget for its type (%d)", item->name, item->type); item->w_impl = eina_hash_find(_elm_prefs_item_type_widgets_map, &item->type); if (!item->w_impl) { item->w_impl = _elm_prefs_item_default_widget; ERR("no widget bound to this type, fallbacking to " "default widget"); } wid_found: item->w_obj = item->w_impl->add( item->w_impl, obj, item->type, item->spec, _item_changed_cb); if (!item->w_obj) { ERR("error while adding UI element to prefs widget %p", obj); return EINA_FALSE; } evas_object_data_set(item->w_obj, "prefs_item", item); _elm_prefs_item_properties_apply(item); evas_object_show(item->w_obj); return EINA_TRUE; } static Elm_Prefs_Page_Node * _elm_prefs_page_load(Evas_Object *obj, const char *pname) { Eet_File *eet_file; Elm_Prefs_Page_Node *ret = NULL; ELM_PREFS_CHECK(obj) NULL; EINA_SAFETY_ON_NULL_RETURN_VAL(pname, NULL); ELM_PREFS_DATA_GET(obj, sd); eet_file = eet_open(sd->file, EET_FILE_MODE_READ); if (eet_file) { ret = eet_data_read(eet_file, _page_edd, pname); eet_close(eet_file); if (!ret) ERR("problem while reading from file %s, key %s", sd->file, pname); else ret->prefs = obj; } else ERR("failed to load from requested epb file (%s)", sd->file); return ret; } static Eina_Bool _elm_prefs_page_populate(Elm_Prefs_Page_Node *page, Evas_Object *parent) { Elm_Prefs_Page_Node *subpage; Elm_Prefs_Item_Node *it; Eina_List *l; EINA_SAFETY_ON_NULL_RETURN_VAL(page, EINA_FALSE); if (!_elm_prefs_page_widget_new(parent, page)) goto err; EINA_LIST_FOREACH(page->items, l, it) { if ((it->type <= ELM_PREFS_TYPE_UNKNOWN) || (it->type > ELM_PREFS_TYPE_SWALLOW)) { ERR("bad item (type = %d) found on page %s, skipping it", it->type, page->name); continue; } else if (it->type == ELM_PREFS_TYPE_PAGE) { subpage = _elm_prefs_page_load(page->prefs, it->spec.p.source); if (!subpage) { ERR("subpage %s could not be created inside %s, skipping it", it->name, page->name); continue; } eina_stringshare_del(subpage->name); subpage->name = eina_stringshare_printf("%s:%s", page->name, it->name); if (!_elm_prefs_page_populate(subpage, page->w_obj)) { _page_free(subpage); goto err; } it->prefs = page->prefs; it->page = page; it->w_obj = subpage->w_obj; it->w_impl = NULL; it->available = EINA_TRUE; it->subpage = subpage; } else if (!_elm_prefs_item_widget_new(page->prefs, page, it)) goto err; if (it->visible && !page->w_impl->item_pack (page->w_obj, it->w_obj, it->type, it->w_impl)) { ERR("item %s could not be packed inside page %s", it->name, page->name); goto err; } } return EINA_TRUE; err: EINA_LIST_FOREACH(page->items, l, it) { ELM_SAFE_FREE(it->w_obj, evas_object_del); it->w_impl = NULL; } ELM_SAFE_FREE(page->w_obj, evas_object_del); page->w_impl = NULL; return EINA_FALSE; } static void _elm_prefs_values_get_default(Elm_Prefs_Page_Node *page, Eina_Bool mark_changed) { Eina_List *l; Elm_Prefs_Item_Node *it; EINA_LIST_FOREACH(page->items, l, it) { if (it->type == ELM_PREFS_TYPE_PAGE) _elm_prefs_values_get_default(it->subpage, mark_changed); else _prefs_item_widget_value_from_self(it, mark_changed); } } static void _elm_prefs_values_get_user(Elm_Prefs_Data *sd, Elm_Prefs_Page_Node *p) { char buf[PATH_MAX]; Eina_List *l; Eina_Value value; Elm_Prefs_Item_Node *it; if (!sd->file) return; EINA_LIST_FOREACH(p->items, l, it) { Eina_Bool get_err = EINA_FALSE, set_err = EINA_FALSE; if (it->type == ELM_PREFS_TYPE_PAGE) { Elm_Prefs_Page_Node *subp = it->subpage; if (!elm_prefs_data_value_get (sd->prefs_data, subp->name, NULL, &value)) { INF("failed to fetch value for item %s on user data, " "writing UI value back on it", it->name); if (eina_value_setup(&value, EINA_VALUE_TYPE_STRINGSHARE) && eina_value_set(&value, subp->name)) { sd->changing_from_ui = EINA_TRUE; elm_prefs_data_value_set (sd->prefs_data, subp->name, it->type, &value); sd->changing_from_ui = EINA_FALSE; } } _elm_prefs_values_get_user(sd, subp); eina_value_flush(&value); continue; } if (!_elm_prefs_item_has_value(it)) continue; if (!it->persistent) continue; snprintf(buf, sizeof(buf), "%s:%s", p->name, it->name); if (!elm_prefs_data_value_get(sd->prefs_data, buf, NULL, &value)) get_err = EINA_TRUE; else if (!_prefs_item_widget_value_from_data(sd, it, &value)) set_err = EINA_TRUE; if (get_err || set_err) { if (get_err) INF("failed to fetch value for item %s on user data, " "writing UI value back on it", it->name); /* force writing back our default value for it */ if (it->available) { if (!it->w_impl->value_get(it->w_obj, &value)) ERR("failed to fetch value from widget of item %s", it->name); else { sd->changing_from_ui = EINA_TRUE; elm_prefs_data_value_set (sd->prefs_data, buf, it->type, &value); sd->changing_from_ui = EINA_FALSE; } } } eina_value_flush(&value); } } EOLIAN static Eina_Bool _elm_prefs_data_set(Eo *obj, Elm_Prefs_Data *sd, Elm_Prefs_Data *prefs_data) { if (!sd->root) return EINA_FALSE; if (prefs_data && !_elm_prefs_data_cbs_add(obj, prefs_data)) return EINA_FALSE; if (sd->prefs_data) { _elm_prefs_data_cbs_del(obj); elm_prefs_data_unref(sd->prefs_data); } sd->prefs_data = prefs_data; if (!sd->prefs_data) { INF("resetting prefs to default values"); _elm_prefs_values_get_default(sd->root, EINA_FALSE); goto end; } elm_prefs_data_ref(sd->prefs_data); sd->values_fetching = EINA_TRUE; _elm_prefs_values_get_user(sd, sd->root); sd->values_fetching = EINA_FALSE; end: efl_event_callback_legacy_call (obj, ELM_PREFS_EVENT_PAGE_CHANGED, (char *)sd->root->name); return EINA_TRUE; } EOLIAN static Elm_Prefs_Data* _elm_prefs_data_get(const Eo *obj EINA_UNUSED, Elm_Prefs_Data *sd) { if (!sd->root) return NULL; else return sd->prefs_data; } EOLIAN static void _elm_prefs_autosave_set(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, Eina_Bool autosave) { ELM_PREFS_DATA_GET(obj, sd); autosave = !!autosave; if (sd->autosave != autosave) sd->autosave = autosave; else return; if ((sd->autosave) && (sd->dirty)) { if (sd->saving_poller) return; sd->saving_poller = ecore_poller_add (ECORE_POLLER_CORE, 1, _elm_prefs_save, obj); } else if ((!sd->autosave) && (sd->saving_poller)) { ecore_poller_del(sd->saving_poller); sd->saving_poller = NULL; _elm_prefs_save(obj); } } EOLIAN static Eina_Bool _elm_prefs_autosave_get(const Eo *obj EINA_UNUSED, Elm_Prefs_Data *sd) { return sd->autosave; } EOLIAN static void _elm_prefs_reset(Eo *obj EINA_UNUSED, Elm_Prefs_Data *sd, Elm_Prefs_Reset_Mode mode) { EINA_SAFETY_ON_NULL_RETURN(sd->root); if (mode == ELM_PREFS_RESET_DEFAULTS) _elm_prefs_values_get_default(sd->root, EINA_TRUE); else if (mode == ELM_PREFS_RESET_LAST) WRN("ELM_PREFS_RESET_LAST not implemented yet"); } static Elm_Prefs_Item_Node * _elm_prefs_item_api_entry_common(const Evas_Object *obj, const char *it_name) { Elm_Prefs_Item_Node *ret; ELM_PREFS_CHECK(obj) NULL; ELM_PREFS_DATA_GET(obj, sd); EINA_SAFETY_ON_NULL_RETURN_VAL(it_name, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(sd->root, NULL); ret = _elm_prefs_item_node_by_name(sd, it_name); if (!ret) ERR("item with name %s does not exist on file %s", it_name, sd->file); return ret; } EOLIAN static Eina_Bool _elm_prefs_item_value_set(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name, const Eina_Value *value) { const Eina_Value_Type *t, *def_t; Elm_Prefs_Item_Node *it; Eina_Value it_val; it = _elm_prefs_item_api_entry_common(obj, name); if (!it) return EINA_FALSE; if (!_elm_prefs_item_has_value(it)) { ERR("item %s has no underlying value, you can't operate on it", it->name); return EINA_FALSE; } EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); t = eina_value_type_get(value); if (!t) return EINA_FALSE; if (!it->available) { ERR("widget of item %s has been deleted, we can't set values on it", it->name); return EINA_FALSE; } if (!it->w_impl->value_get(it->w_obj, &it_val)) { ERR("failed to fetch value from widget of item %s", it->name); goto err; } def_t = eina_value_type_get(&it_val); if ((t != def_t) && (!eina_value_convert(value, &it_val))) { eina_value_flush(&it_val); ERR("failed to convert value to proper type"); goto err; } else if (!eina_value_copy(value, &it_val) || (!it->w_impl->value_set(it->w_obj, &it_val))) { eina_value_flush(&it_val); ERR("failed to set value on widget of item %s", it->name); goto err; } eina_value_flush(&it_val); return EINA_TRUE; err: return EINA_FALSE; } EOLIAN static Eina_Bool _elm_prefs_item_value_get(const Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name, Eina_Value *value) { Elm_Prefs_Item_Node *it; it = _elm_prefs_item_api_entry_common(obj, name); if (!it) return EINA_FALSE; if (!_elm_prefs_item_has_value(it)) { ERR("item %s has no underlying value, you can't operate on it", it->name); return EINA_FALSE; } if (!value) return EINA_FALSE; if (!it->available) { ERR("widget of item %s has been deleted, we can't set values on it", it->name); return EINA_FALSE; } if (!it->w_impl->value_get(it->w_obj, value)) { ERR("failed to fetch value from widget of item %s", it->name); return EINA_FALSE; } return EINA_TRUE; } EOLIAN static const Evas_Object* _elm_prefs_item_object_get(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name) { Elm_Prefs_Item_Node *it; it = _elm_prefs_item_api_entry_common(obj, name); if (!it) return NULL; else return it->w_obj; } EOLIAN static void _elm_prefs_item_visible_set(Eo *obj EINA_UNUSED, Elm_Prefs_Data *sd, const char *name, Eina_Bool visible) { Elm_Prefs_Item_Node *it; Eina_List *l; Evas_Object *lbl, *icon; EINA_SAFETY_ON_NULL_RETURN(name); EINA_SAFETY_ON_NULL_RETURN(sd->root); l = _elm_prefs_item_list_node_by_name(sd, name); if (!l) return; it = eina_list_data_get(l); visible = !!visible; if (it->visible == visible) return; it->visible = visible; if (!it->available) { ERR("widget of item %s has been deleted, we can't act on it", it->name); return; } lbl = evas_object_data_get(it->w_obj, "label_widget"); icon = evas_object_data_get(it->w_obj, "icon_widget"); if (!it->visible) { if (!it->page->w_impl->item_unpack(it->page->w_obj, it->w_obj)) { ERR("failed to unpack item %s from page %s!", it->name, it->page->name); } else { if (lbl) evas_object_hide(lbl); if (icon) evas_object_hide(icon); evas_object_hide(it->w_obj); } } else if (it->available) { Eina_List *p_l; if ((p_l = eina_list_prev(l))) { Elm_Prefs_Item_Node *p_it = eina_list_data_get(p_l); if (!it->page->w_impl->item_pack_after (it->page->w_obj, it->w_obj, p_it->w_obj, it->type, it->w_impl)) { ERR("failed to pack item %s on page %s!", it->name, it->page->name); } } else if ((p_l = eina_list_next(l))) { Elm_Prefs_Item_Node *n_it = eina_list_data_get(p_l); if (!it->page->w_impl->item_pack_before (it->page->w_obj, it->w_obj, n_it->w_obj, it->type, it->w_impl)) { ERR("failed to pack item %s on page %s!", it->name, it->page->name); } } else if (!it->page->w_impl->item_pack (it->page->w_obj, it->w_obj, it->type, it->w_impl)) ERR("failed to pack item %s on page %s!", it->name, it->page->name); if (lbl) evas_object_show(lbl); if (icon) evas_object_show(icon); evas_object_show(it->w_obj); } } EOLIAN static Eina_Bool _elm_prefs_item_visible_get(const Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name) { Elm_Prefs_Item_Node *it; it = _elm_prefs_item_api_entry_common(obj, name); if (!it) return EINA_FALSE; if (!it->available) { ERR("widget of item %s has been deleted, we can't act on it", it->name); return EINA_FALSE; } return it->visible; } EOLIAN static void _elm_prefs_item_disabled_set(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name, Eina_Bool disabled) { Elm_Prefs_Item_Node *it; it = _elm_prefs_item_api_entry_common(obj, name); if (!it) return; if (!it->available) { ERR("widget of item %s has been deleted, we can't act on it", it->name); return; } elm_object_disabled_set(it->w_obj, disabled); } EOLIAN static Eina_Bool _elm_prefs_item_disabled_get(const Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name) { Elm_Prefs_Item_Node *it; it = _elm_prefs_item_api_entry_common(obj, name); if (!it) return EINA_FALSE; if (!it->available) { ERR("widget of item %s has been deleted, we can't act on it", it->name); return EINA_FALSE; } return elm_object_disabled_get(it->w_obj); } EOLIAN static void _elm_prefs_item_editable_set(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name, Eina_Bool editable) { Elm_Prefs_Item_Node *it; it = _elm_prefs_item_api_entry_common(obj, name); if (!it) return; if (!it->w_impl->editable_set) { ERR("the item %s does not implement the 'editable' " "property (using widget %s)", it->name, it->widget); return; } it->w_impl->editable_set(it->w_obj, editable); } EOLIAN static Eina_Bool _elm_prefs_item_editable_get(const Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name) { Elm_Prefs_Item_Node *it; it = _elm_prefs_item_api_entry_common(obj, name); if (!it) return EINA_FALSE; if (!it->w_impl->editable_get) { ERR("the item %s does not implement the 'editable' " "property (using widget %s)", it->name, it->widget); return EINA_FALSE; } return it->w_impl->editable_get(it->w_obj); } EOLIAN static Eina_Bool _elm_prefs_item_swallow(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name, Evas_Object *child) { Eina_Value v; Eina_Bool ret = EINA_FALSE; Elm_Prefs_Item_Node *it = _elm_prefs_item_api_entry_common(obj, name); if (!it) return EINA_FALSE; if (it->type != ELM_PREFS_TYPE_SWALLOW) { ERR("item %s does not match a SWALLOW item", name); return EINA_FALSE; } if (!eina_value_setup(&v, EINA_VALUE_TYPE_UINT64)) return EINA_FALSE; if (!eina_value_set(&v, child)) { eina_value_flush(&v); return EINA_FALSE; } ret = it->w_impl->value_set(it->w_obj, &v); eina_value_flush(&v); return ret; } EOLIAN static Evas_Object* _elm_prefs_item_unswallow(Eo *obj, Elm_Prefs_Data *_pd EINA_UNUSED, const char *name) { Eina_Value v; Evas_Object *ret = NULL; Elm_Prefs_Item_Node *it = _elm_prefs_item_api_entry_common(obj, name); if (!it) return NULL; if (it->type != ELM_PREFS_TYPE_SWALLOW) { ERR("item %s does not match a SWALLOW item", name); return NULL; } if (!(it->w_impl->value_get(it->w_obj, &v))) return NULL; if (eina_value_type_get(&v) != EINA_VALUE_TYPE_UINT64 || !eina_value_get(&v, ret)) { eina_value_flush(&v); return NULL; } eina_value_flush(&v); return ret; } static unsigned int elm_prefs_item_iface_abi_version_get(void) { return ELM_PREFS_ITEM_IFACE_ABI_VERSION; } EAPI void elm_prefs_item_iface_register(const Elm_Prefs_Item_Iface_Info *array) { const Elm_Prefs_Item_Iface_Info *itr; unsigned int abi_version = elm_prefs_item_iface_abi_version_get(); if (!array) return; for (itr = array; itr->widget_name; itr++) { const Elm_Prefs_Item_Type *t_itr; if (itr->info->abi_version != abi_version) { ERR("external prefs widget interface '%s' (%p) has incorrect ABI " "version. got %#x where %#x was expected.", itr->widget_name, itr->info, itr->info->abi_version, abi_version); continue; } /* FIXME: registering the 1st, for now */ if (!_elm_prefs_item_default_widget) _elm_prefs_item_default_widget = itr->info; eina_hash_direct_add (_elm_prefs_item_widgets_map, itr->widget_name, itr->info); for (t_itr = itr->info->types; *t_itr != ELM_PREFS_TYPE_UNKNOWN; t_itr++) eina_hash_add(_elm_prefs_item_type_widgets_map, t_itr, itr->info); } } EAPI void elm_prefs_item_iface_unregister(const Elm_Prefs_Item_Iface_Info *array) { const Elm_Prefs_Item_Iface_Info *itr; if (!array) return; for (itr = array; itr->widget_name; itr++) { const Elm_Prefs_Item_Type *t_itr; eina_hash_del(_elm_prefs_item_widgets_map, itr->widget_name, itr->info); for (t_itr = itr->info->types; *t_itr != ELM_PREFS_TYPE_UNKNOWN; t_itr++) eina_hash_del (_elm_prefs_item_type_widgets_map, t_itr, itr->info); } } static unsigned int elm_prefs_page_iface_abi_version_get(void) { return ELM_PREFS_PAGE_IFACE_ABI_VERSION; } EAPI void elm_prefs_page_iface_register(const Elm_Prefs_Page_Iface_Info *array) { const Elm_Prefs_Page_Iface_Info *itr; unsigned int abi_version = elm_prefs_page_iface_abi_version_get(); if (!array) return; for (itr = array; itr->widget_name; itr++) { if (itr->info->abi_version != abi_version) { ERR("external prefs widget interface '%s' (%p) has incorrect ABI " "version. got %#x where %#x was expected.", itr->widget_name, itr->info, itr->info->abi_version, abi_version); continue; } /* FIXME: registering the 1st, for now */ if (!_elm_prefs_page_default_widget) _elm_prefs_page_default_widget = itr->info; eina_hash_direct_add (_elm_prefs_page_widgets_map, itr->widget_name, itr->info); } } EAPI void elm_prefs_page_iface_unregister(const Elm_Prefs_Page_Iface_Info *array) { const Elm_Prefs_Page_Iface_Info *itr; if (!array) return; for (itr = array; itr->widget_name; itr++) eina_hash_del (_elm_prefs_page_widgets_map, itr->widget_name, itr->info); } /* TODO: evaluate if it's sane to handle it better */ /* static void */ /* _prefs_page_del_cb(void *data EINA_UNUSED, */ /* Evas *evas EINA_UNUSED, */ /* Evas_Object *obj, */ /* void *event_info EINA_UNUSED) */ /* { */ /* Elm_Prefs_Page_Node *page; */ /* evas_object_event_callback_del(obj, EVAS_CALLBACK_DEL, _prefs_page_del_cb); */ /* page = evas_object_data_get(obj, "prefs_page"); */ /* ELM_PREFS_DATA_GET(page->prefs, sd); */ /* if (!sd->delete_me) */ /* { */ /* Eina_List *l; */ /* Elm_Prefs_Item_Node *it; */ /* /\* force writing back the value for it *\/ */ /* EINA_LIST_FOREACH(page->items, l, it) */ /* _item_changed_cb(it->w_obj); */ /* } */ /* evas_object_data_del(obj, "prefs_page"); */ /* page->w_obj = NULL; */ /* } */ static void _prefs_item_del_cb(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Elm_Prefs_Item_Node *it; Evas_Object *lbl, *icon; evas_object_event_callback_del(obj, EVAS_CALLBACK_DEL, _prefs_item_del_cb); it = evas_object_data_get(obj, "prefs_item"); lbl = evas_object_data_del(it->w_obj, "label_widget"); evas_object_del(lbl); icon = evas_object_data_del(it->w_obj, "icon_widget"); evas_object_del(icon); ELM_PREFS_DATA_GET(it->prefs, sd); if (!sd->delete_me) /* force writing back the value for it */ _item_changed_cb(obj); evas_object_data_del(obj, "prefs_item"); it->available = EINA_FALSE; it->w_obj = NULL; } EAPI Eina_Bool elm_prefs_item_widget_common_add(Evas_Object *prefs EINA_UNUSED, Evas_Object *obj) { EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE); evas_object_event_callback_add (obj, EVAS_CALLBACK_DEL, _prefs_item_del_cb, NULL); return EINA_TRUE; } EAPI Eina_Bool elm_prefs_page_widget_common_add(Evas_Object *prefs EINA_UNUSED, Evas_Object *obj) { EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE); /* evas_object_event_callback_add */ /* (obj, EVAS_CALLBACK_DEL, _prefs_page_del_cb, NULL); */ return EINA_TRUE; } Eina_Bool _elm_prefs_init(void) { Elm_Module *m; if (++_elm_prefs_init_count != 1) return EINA_TRUE; _elm_prefs_descriptors_init(); _elm_prefs_data_init(); if (!(m = _elm_module_find_as("prefs_iface"))) { WRN("prefs iface module could not be loaded," " the prefs widget won't function"); --_elm_prefs_init_count; _elm_prefs_descriptors_shutdown(); _elm_prefs_data_shutdown(); return EINA_FALSE; } _elm_prefs_page_widgets_map = eina_hash_string_superfast_new(NULL); _elm_prefs_item_widgets_map = eina_hash_string_superfast_new(NULL); _elm_prefs_item_type_widgets_map = eina_hash_int32_new(NULL); m->init_func(m); return EINA_TRUE; } void _elm_prefs_shutdown(void) { if (_elm_prefs_init_count <= 0) { EINA_LOG_ERR("Init count not greater than 0 in shutdown."); return; } if (--_elm_prefs_init_count != 0) return; _elm_prefs_descriptors_shutdown(); _elm_prefs_data_shutdown(); eina_hash_free(_elm_prefs_page_widgets_map); eina_hash_free(_elm_prefs_item_widgets_map); eina_hash_free(_elm_prefs_item_type_widgets_map); /* all modules shutdown calls will taken place elsewhere */ } static void _elm_prefs_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass); } EAPI Eina_Bool elm_prefs_file_set(Eo *obj, const char *file, const char *page) { Elm_Prefs_Data *sd = efl_data_scope_get(obj, MY_CLASS); const char *prefix; EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE); if (!_elm_prefs_init_count) { CRI("prefs_iface module is not loaded! you can't" " create prefs widgets"); return EINA_FALSE; } prefix = elm_app_data_dir_get(); if (!strlen(prefix)) { WRN("we could not figure out the program's data" " dir, fallbacking to local directory."); prefix = "."; } if (!file) sd->file = eina_stringshare_printf("%s/%s", prefix, "preferences.epb"); else { #ifndef _WIN32 if (*file != '/') /* relative */ #else if (!evil_path_is_absolute(file)) /* relative */ #endif sd->file = eina_stringshare_printf("%s/%s", prefix, file); else sd->file = eina_stringshare_add(file); } sd->page = eina_stringshare_add(page ? page : "main"); sd->root = _elm_prefs_page_load(obj, sd->page); if (!sd->root) return EINA_FALSE; if (!_elm_prefs_page_populate(sd->root, obj)) { _root_node_free(sd); sd->root = NULL; return EINA_FALSE; } elm_widget_resize_object_set(obj, sd->root->w_obj); _elm_prefs_values_get_default(sd->root, EINA_FALSE); efl_event_callback_legacy_call (obj, ELM_PREFS_EVENT_PAGE_LOADED, (char *)sd->root->name); return EINA_TRUE; } EAPI Eina_Bool elm_prefs_file_get(const Eo *obj, const char **file, const char **page) { Elm_Prefs_Data *sd = efl_data_scope_get(obj, MY_CLASS); EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE); if (file) *file = sd->file; if (page) *page = sd->page; return EINA_TRUE; } /* Internal EO APIs and hidden overrides */ #define ELM_PREFS_EXTRA_OPS \ EFL_CANVAS_GROUP_ADD_DEL_OPS(elm_prefs) #include "elm_prefs_eo.c"