enlightenment/src/bin/e_theme.c

698 lines
19 KiB
C

#include "e.h"
/* local subsystem functions */
typedef struct _E_Theme_Result E_Theme_Result;
struct _E_Theme_Result
{
const char *file;
const char *cache;
Eina_Hash *quickfind;
};
static Eina_Bool _e_theme_mappings_free_cb(const Eina_Hash *hash, const void *key, void *data, void *fdata);
static Eina_Bool _e_theme_mappings_quickfind_free_cb(const Eina_Hash *hash, const void *key, void *data, void *fdata);
static void _e_theme_category_register(const char *category);
static Eina_List *_e_theme_collection_item_register(Eina_List *list, const char *name);
static void e_theme_handler_set(void *data __UNUSED__, Evas_Object *obj __UNUSED__, const char *path);
static int e_theme_handler_test(void *data __UNUSED__, Evas_Object *obj __UNUSED__, const char *path);
/* local subsystem globals */
static Eina_Hash *mappings = NULL;
static Eina_Hash *group_cache = NULL;
static Eina_List *categories = NULL;
static E_Fm2_Mime_Handler *theme_hdl = NULL;
/* externally accessible functions */
EINTERN int
e_theme_init(void)
{
E_Config_Theme *et;
Eina_List *l = NULL;
/* Register mime handler */
theme_hdl = e_fm2_mime_handler_new(_("Set As Theme"), "preferences-desktop-theme",
e_theme_handler_set, NULL,
e_theme_handler_test, NULL);
if (theme_hdl) e_fm2_mime_handler_glob_add(theme_hdl, "*.edj");
/* this is a fallback that is ALWAYS there - if all fails things will */
/* always fall back to the default theme. the rest after this are config */
/* values users can set */
e_theme_file_set("base", "default.edj");
EINA_LIST_FOREACH(e_config->themes, l, et)
{
char buf[256];
snprintf(buf, sizeof(buf), "base/%s", et->category);
e_theme_file_set(buf, et->file);
}
if (!mappings) mappings = eina_hash_string_superfast_new(NULL);
group_cache = eina_hash_string_superfast_new(NULL);
return 1;
}
EINTERN int
e_theme_shutdown(void)
{
const char *str;
if (theme_hdl)
{
e_fm2_mime_handler_glob_del(theme_hdl, "*.edj");
e_fm2_mime_handler_free(theme_hdl);
}
if (mappings)
{
eina_hash_foreach(mappings, _e_theme_mappings_free_cb, NULL);
eina_hash_free(mappings);
mappings = NULL;
}
if (group_cache)
{
eina_hash_free(group_cache);
group_cache = NULL;
}
EINA_LIST_FREE(categories, str)
eina_stringshare_del(str);
return 1;
}
/**
* Return a list of all the groups matching a category and prefix
*
* @param base The category to look under
* @param collname The group to match
* @return A list of stringshared groups which must be freed
*/
EAPI Eina_List *
e_theme_collection_items_find(const char *base, const char *collname)
{
Eina_List *list = NULL;
E_Theme_Result *res;
char *category, *p;
category = alloca(strlen(base) + 1);
strcpy(category, base);
do
{
res = eina_hash_find(mappings, category);
if (res)
{
const char *str;
/* if found check cached path */
str = res->cache;
if (!str)
{
/* no cached path */
str = res->file;
/* if its not an absolute path find it */
if (str[0] != '/') str = e_path_find(path_themes, str);
/* save cached value */
if (str) res->cache = str;
}
if (str)
{
Eina_List *coll;
Eina_Stringshare *c;
int collname_len;
coll = edje_file_collection_list(str);
if (coll) collname_len = strlen(collname);
EINA_LIST_FREE(coll, c)
{
if (!strncmp(c, collname, collname_len))
{
char *trans, *p2;
trans = strdupa(c);
p = trans + collname_len + 1;
if (*p)
{
p2 = strchr(p, '/');
if (p2) *p2 = 0;
list = _e_theme_collection_item_register(list, p);
}
}
eina_stringshare_del(c);
}
}
}
p = strrchr(category, '/');
if (p) *p = 0;
}
while (p);
list = eina_list_sort(list, 0, EINA_COMPARE_CB(strcmp));
return list;
}
/**
* Assigns a edje group from the current theme to
* a recently created edje object
*
* @param o edje object to assign group to
* @param category that hold the required edj file
* @param group the edje group to use
* @return returns true on success, false if unable to assign group
*/
EAPI int
e_theme_edje_object_set(Evas_Object *o, const char *category, const char *group)
{
E_Theme_Result *res;
char buf[256];
char *p;
/* find category -> edje mapping */
_e_theme_category_register(category);
res = eina_hash_find(mappings, category);
if (res)
{
const char *str;
/* if found check cached path */
str = res->cache;
if (!str)
{
/* no cached path */
str = res->file;
/* if its not an absolute path find it */
if (str[0] != '/')
str = e_path_find(path_themes, str);
/* save cached value */
if (str) res->cache = str;
}
if (str)
{
void *tres;
int ok;
snprintf(buf, sizeof(buf), "%s/::/%s", str, group);
tres = eina_hash_find(group_cache, buf);
if (!tres)
{
ok = edje_object_file_set(o, str, group);
/* save in the group cache hash */
if (ok)
eina_hash_add(group_cache, buf, res);
else
eina_hash_add(group_cache, buf, (void *)1);
}
else if (tres == (void *)1)
ok = 0;
else
ok = 1;
if (ok)
{
if (tres)
edje_object_file_set(o, str, group);
return 1;
}
}
}
/* no mapping or set failed - fall back */
eina_strlcpy(buf, category, sizeof(buf));
/* shorten string up to and not including last / char */
p = strrchr(buf, '/');
if (p) *p = 0;
/* no / anymore - we are already as far back as we can go */
else return 0;
/* try this category */
return e_theme_edje_object_set(o, buf, group);
}
const char *
_e_theme_edje_file_get(const char *category, const char *group, Eina_Bool fallback_icon)
{
E_Theme_Result *res;
char buf[4096];
char *p;
/* find category -> edje mapping */
_e_theme_category_register(category);
res = eina_hash_find(mappings, category);
if (e_config->icon_theme &&
(!fallback_icon) &&
(!strcmp(category, "base")) &&
(!strncmp(group, "e/icons", 7)))
return "";
if (res)
{
const char *str;
/* if found check cached path */
str = res->cache;
if (!str)
{
/* no cached path */
str = res->file;
/* if its not an absolute path find it */
if (str[0] != '/')
str = e_path_find(path_themes, str);
/* save cached value */
if (str) res->cache = str;
}
if (str)
{
void *tres;
Eina_List *coll;
int ok;
snprintf(buf, sizeof(buf), "%s/::/%s", str, group);
tres = eina_hash_find(group_cache, buf);
if (!tres)
{
/* if the group exists - return */
if (!res->quickfind)
{
Eina_Stringshare *col;
res->quickfind = eina_hash_string_superfast_new(NULL);
/* create a quick find hash of all group entries */
coll = edje_file_collection_list(str);
EINA_LIST_FREE(coll, col)
eina_hash_direct_add(res->quickfind, col, col);
}
/* save in the group cache hash */
if (eina_hash_find(res->quickfind, group))
{
eina_hash_add(group_cache, buf, res);
ok = 1;
}
else
{
eina_hash_add(group_cache, buf, (void *)1);
ok = 0;
}
}
else if (tres == (void *)1) /* special pointer "1" == not there */
ok = 0;
else
ok = 1;
if (ok) return str;
}
}
/* no mapping or set failed - fall back */
eina_strlcpy(buf, category, sizeof(buf));
/* shorten string up to and not including last / char */
p = strrchr(buf, '/');
if (p) *p = 0;
/* no / anymore - we are already as far back as we can go */
else return "";
/* try this category */
return e_theme_edje_file_get(buf, group);
}
EAPI const char *
e_theme_edje_file_get(const char *category, const char *group)
{
return _e_theme_edje_file_get(category, group, EINA_FALSE);
}
EAPI const char *
e_theme_edje_icon_fallback_file_get(const char *group)
{
return _e_theme_edje_file_get("base", group, EINA_TRUE);
}
/*
* this is used to set the theme for a CATEGORY of E17. "base" is always set
* to the default theme - because if a selected theme wants "base/theme", but
* does not provide theme elements, it can fall back to the default theme.
*
* the idea is you can actually set a different theme for different parts of
* the desktop... :)
*
* other possible categories...
* e_theme_file_set("base/theme/about", "default.edj");
* e_theme_file_set("base/theme/borders", "default.edj");
* e_theme_file_set("base/theme/background", "default.edj");
* e_theme_file_set("base/theme/configure", "default.edj");
* e_theme_file_set("base/theme/dialog", "default.edj");
* e_theme_file_set("base/theme/menus", "default.edj");
* e_theme_file_set("base/theme/error", "default.edj");
* e_theme_file_set("base/theme/gadman", "default.edj");
* e_theme_file_set("base/theme/dnd", "default.edj");
* e_theme_file_set("base/theme/icons", "default.edj");
* e_theme_file_set("base/theme/pointer", "default.edj");
* e_theme_file_set("base/theme/transitions", "default.edj");
* e_theme_file_set("base/theme/widgets", "default.edj");
* e_theme_file_set("base/theme/winlist", "default.edj");
* e_theme_file_set("base/theme/modules", "default.edj");
* e_theme_file_set("base/theme/modules/pager", "default.edj");
* e_theme_file_set("base/theme/modules/ibar", "default.edj");
* e_theme_file_set("base/theme/modules/ibox", "default.edj");
* e_theme_file_set("base/theme/modules/clock", "default.edj");
* e_theme_file_set("base/theme/modules/battery", "default.edj");
* e_theme_file_set("base/theme/modules/cpufreq", "default.edj");
* e_theme_file_set("base/theme/modules/start", "default.edj");
* e_theme_file_set("base/theme/modules/temperature", "default.edj");
*/
EAPI void
e_theme_file_set(const char *category, const char *file)
{
E_Theme_Result *res;
if (group_cache)
{
eina_hash_free(group_cache);
group_cache = NULL;
}
_e_theme_category_register(category);
res = eina_hash_find(mappings, category);
if (res)
{
eina_hash_del(mappings, category, res);
if (res->file)
{
e_filereg_deregister(res->file);
eina_stringshare_del(res->file);
}
if (res->cache) eina_stringshare_del(res->cache);
E_FREE(res);
}
res = E_NEW(E_Theme_Result, 1);
res->file = eina_stringshare_add(file);
e_filereg_register(res->file);
if (!mappings)
mappings = eina_hash_string_superfast_new(NULL);
eina_hash_add(mappings, category, res);
}
EAPI int
e_theme_config_set(const char *category, const char *file)
{
E_Config_Theme *ect;
Eina_List *next;
/* Don't accept unused categories */
#if 0
if (!e_theme_category_find(category)) return 0;
#endif
/* search for the category */
EINA_LIST_FOREACH(e_config->themes, next, ect)
{
if (!strcmp(ect->category, category))
{
if (ect->file) eina_stringshare_del(ect->file);
ect->file = eina_stringshare_add(file);
return 1;
}
}
/* the text class doesnt exist */
ect = E_NEW(E_Config_Theme, 1);
ect->category = eina_stringshare_add(category);
ect->file = eina_stringshare_add(file);
e_config->themes = eina_list_append(e_config->themes, ect);
return 1;
}
/*
* returns a pointer to the data, return null if nothing if found.
*/
EAPI E_Config_Theme *
e_theme_config_get(const char *category)
{
E_Config_Theme *ect = NULL;
Eina_List *next;
/* search for the category */
EINA_LIST_FOREACH(e_config->themes, next, ect)
{
if (!strcmp(ect->category, category))
return ect;
}
return NULL;
}
EAPI int
e_theme_config_remove(const char *category)
{
E_Config_Theme *ect;
Eina_List *next;
/* search for the category */
EINA_LIST_FOREACH(e_config->themes, next, ect)
{
if (!e_util_strcmp(ect->category, category))
{
e_config->themes = eina_list_remove_list(e_config->themes, next);
if (ect->category) eina_stringshare_del(ect->category);
if (ect->file) eina_stringshare_del(ect->file);
free(ect);
return 1;
}
}
return 1;
}
EAPI Eina_List *
e_theme_config_list(void)
{
return e_config->themes;
}
EAPI int
e_theme_category_find(const char *category)
{
if (eina_list_search_sorted(categories, EINA_COMPARE_CB(strcmp), category))
return 1;
return 0;
}
EAPI Eina_List *
e_theme_category_list(void)
{
return categories;
}
EAPI int
e_theme_transition_find(const char *transition)
{
Eina_List *trans = NULL;
int found = 0;
const char *str;
trans =
e_theme_collection_items_find("base/theme/transitions", "e/transitions");
if (eina_list_search_sorted(trans, EINA_COMPARE_CB(strcmp), transition))
found = 1;
EINA_LIST_FREE(trans, str)
eina_stringshare_del(str);
return found;
}
EAPI Eina_List *
e_theme_transition_list(void)
{
return e_theme_collection_items_find("base/theme/transitions",
"e/transitions");
}
EAPI int
e_theme_border_find(const char *border)
{
Eina_List *bds = NULL;
int found = 0;
const char *str;
bds =
e_theme_collection_items_find("base/theme/borders", "e/widgets/border");
if (eina_list_search_sorted(bds, EINA_COMPARE_CB(strcmp), border))
found = 1;
EINA_LIST_FREE(bds, str)
eina_stringshare_del(str);
return found;
}
EAPI Eina_List *
e_theme_border_list(void)
{
return e_theme_collection_items_find("base/theme/borders",
"e/widgets/border");
}
EAPI int
e_theme_shelf_find(const char *shelf)
{
Eina_List *shelfs = NULL;
int found = 0;
const char *str;
shelfs =
e_theme_collection_items_find("base/theme/shelf", "e/shelf");
if (eina_list_search_sorted(shelfs, EINA_COMPARE_CB(strcmp), shelf))
found = 1;
EINA_LIST_FREE(shelfs, str)
eina_stringshare_del(str);
return found;
}
EAPI Eina_List *
e_theme_shelf_list(void)
{
return e_theme_collection_items_find("base/theme/shelf", "e/shelf");
}
EAPI int
e_theme_comp_border_find(const char *comp)
{
Eina_List *comps = NULL;
int found = 0;
const char *str;
comps = e_theme_collection_items_find("base/theme/borders", "e/comp/border");
if (eina_list_search_sorted(comps, EINA_COMPARE_CB(strcmp), comp))
found = 1;
EINA_LIST_FREE(comps, str)
eina_stringshare_del(str);
return found;
}
EAPI Eina_List *
e_theme_comp_border_list(void)
{
return e_theme_collection_items_find("base/theme/borders", "e/comp/border");
}
/* local subsystem functions */
static void
e_theme_handler_set(void *data __UNUSED__, Evas_Object *obj __UNUSED__, const char *path)
{
E_Action *a;
char buf[PATH_MAX];
int copy = 1;
if (!path) return;
/* if not in system dir or user dir, copy to user dir */
e_prefix_data_concat_static(buf, "data/themes");
if (!strncmp(buf, path, strlen(buf)))
copy = 0;
if (copy)
{
e_user_dir_concat_static(buf, "themes");
if (!strncmp(buf, path, strlen(buf)))
copy = 0;
}
if (copy)
{
const char *file;
char *name;
file = ecore_file_file_get(path);
name = ecore_file_strip_ext(file);
e_user_dir_snprintf(buf, sizeof(buf), "themes/%s-%f.edj", name, ecore_time_unix_get());
free(name);
if (!ecore_file_exists(buf))
{
ecore_file_cp(path, buf);
e_theme_config_set("theme", buf);
}
else
e_theme_config_set("theme", path);
}
else
e_theme_config_set("theme", path);
e_config_save_queue();
a = e_action_find("restart");
if ((a) && (a->func.go)) a->func.go(NULL, NULL);
}
static int
e_theme_handler_test(void *data __UNUSED__, Evas_Object *obj __UNUSED__, const char *path)
{
if (!path) return 0;
if (!edje_file_group_exists(path, "e/widgets/border/default/border"))
return 0;
return 1;
}
static Eina_Bool
_e_theme_mappings_free_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata __UNUSED__)
{
E_Theme_Result *res;
res = data;
if (res->file) eina_stringshare_del(res->file);
if (res->cache) eina_stringshare_del(res->cache);
if (res->quickfind)
{
eina_hash_foreach(res->quickfind, _e_theme_mappings_quickfind_free_cb, NULL);
eina_hash_free(res->quickfind);
}
free(res);
return EINA_TRUE;
}
static Eina_Bool
_e_theme_mappings_quickfind_free_cb(const Eina_Hash *hash __UNUSED__, const void *key, void *data __UNUSED__, void *fdata __UNUSED__)
{
eina_stringshare_del(key);
return EINA_TRUE;
}
static void
_e_theme_category_register(const char *category)
{
Eina_List *l;
int ret;
if (!categories)
categories = eina_list_append(categories, eina_stringshare_add(category));
l = eina_list_search_sorted_near_list(categories, EINA_COMPARE_CB(strcmp),
category, &ret);
if (!ret) return;
if (ret < 0)
categories = eina_list_append_relative_list(categories, eina_stringshare_add(category), l);
else
categories = eina_list_prepend_relative_list(categories, eina_stringshare_add(category), l);
}
static Eina_List *
_e_theme_collection_item_register(Eina_List *list, const char *name)
{
const char *item;
Eina_List *l;
EINA_LIST_FOREACH(list, l, item)
{
if (!strcmp(name, item)) return list;
}
list = eina_list_append(list, eina_stringshare_add(name));
return list;
}