forked from enlightenment/enlightenment
594 lines
15 KiB
C
594 lines
15 KiB
C
/*
|
|
* vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
|
|
*/
|
|
#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 Eina_List *_e_theme_collection_items_find(const char *base, const char *collname);
|
|
|
|
|
|
/* local subsystem globals */
|
|
static Eina_Hash *mappings = NULL;
|
|
static Eina_Hash *group_cache = NULL;
|
|
|
|
static Eina_List *categories = NULL;
|
|
static Eina_List *transitions = NULL;
|
|
static Eina_List *borders = NULL;
|
|
static Eina_List *shelfs = NULL;
|
|
static E_Fm2_Mime_Handler *theme_hdl = NULL;
|
|
|
|
/* externally accessible functions */
|
|
|
|
EAPI 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);
|
|
}
|
|
|
|
/* Find transitions */
|
|
transitions = _e_theme_collection_items_find("base/theme/transitions", "e/transitions");
|
|
borders = _e_theme_collection_items_find("base/theme/borders", "e/widgets/border");
|
|
shelfs = _e_theme_collection_items_find("base/theme/shelf", "e/shelf");
|
|
if (!mappings)
|
|
mappings = eina_hash_string_superfast_new(NULL);
|
|
group_cache = eina_hash_string_superfast_new(NULL);
|
|
|
|
return 1;
|
|
}
|
|
|
|
EAPI 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);
|
|
EINA_LIST_FREE(transitions, str)
|
|
eina_stringshare_del(str);
|
|
EINA_LIST_FREE(borders, str)
|
|
eina_stringshare_del(str);
|
|
EINA_LIST_FREE(shelfs, str)
|
|
eina_stringshare_del(str);
|
|
return 1;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
EAPI const char *
|
|
e_theme_edje_file_get(const char *category, const char *group)
|
|
{
|
|
E_Theme_Result *res;
|
|
char buf[4096];
|
|
const char *q;
|
|
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;
|
|
Eina_List *coll, *l;
|
|
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)
|
|
{
|
|
const char *col;
|
|
|
|
res->quickfind = eina_hash_string_superfast_new(NULL);
|
|
/* great a quick find hash of all group entires */
|
|
coll = edje_file_collection_list(str);
|
|
EINA_LIST_FOREACH(coll, l, col)
|
|
{
|
|
q = eina_stringshare_add(col);
|
|
eina_hash_direct_add(res->quickfind, q, q);
|
|
}
|
|
if (coll) edje_file_collection_list_free(coll);
|
|
}
|
|
/* 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);
|
|
}
|
|
|
|
/*
|
|
* 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 (!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)
|
|
{
|
|
if (eina_list_search_sorted(transitions, EINA_COMPARE_CB(strcmp), transition))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
EAPI Eina_List *
|
|
e_theme_transition_list(void)
|
|
{
|
|
return transitions;
|
|
}
|
|
|
|
EAPI int
|
|
e_theme_border_find(const char *border)
|
|
{
|
|
if (eina_list_search_sorted(borders, EINA_COMPARE_CB(strcmp), border))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
EAPI Eina_List *
|
|
e_theme_border_list(void)
|
|
{
|
|
return borders;
|
|
}
|
|
|
|
EAPI int
|
|
e_theme_shelf_find(const char *shelf)
|
|
{
|
|
if (eina_list_search_sorted(shelfs, EINA_COMPARE_CB(strcmp), shelf))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
EAPI Eina_List *
|
|
e_theme_shelf_list(void)
|
|
{
|
|
return shelfs;
|
|
}
|
|
|
|
EAPI void
|
|
e_theme_handler_set(Evas_Object *obj, const char *path, void *data)
|
|
{
|
|
E_Action *a;
|
|
|
|
if (!path) return;
|
|
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);
|
|
}
|
|
|
|
EAPI int
|
|
e_theme_handler_test(Evas_Object *obj, const char *path, void *data)
|
|
{
|
|
if (!path) return 0;
|
|
if (!edje_file_group_exists(path, "e/widgets/border/default/border"))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* local subsystem functions */
|
|
static Eina_Bool
|
|
_e_theme_mappings_free_cb(const Eina_Hash *hash, const void *key, void *data, void *fdata)
|
|
{
|
|
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, const void *key, void *data, void *fdata)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static Eina_List *
|
|
_e_theme_collection_items_find(const char *base, const char *collname)
|
|
{
|
|
Eina_List *list = NULL;
|
|
E_Theme_Result *res;
|
|
char *category, *p, *p2;
|
|
int collname_len;
|
|
|
|
collname_len = strlen(collname);
|
|
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, *l;
|
|
|
|
coll = edje_file_collection_list(str);
|
|
if (coll)
|
|
{
|
|
const char *c;
|
|
|
|
EINA_LIST_FOREACH(coll, l, c)
|
|
{
|
|
if (!strncmp(c, collname, collname_len))
|
|
{
|
|
char *trans;
|
|
|
|
trans = strdup(c);
|
|
p = trans + collname_len + 1;
|
|
if (*p)
|
|
{
|
|
p2 = strchr(p, '/');
|
|
if (p2) *p2 = 0;
|
|
list = _e_theme_collection_item_register(list, p);
|
|
}
|
|
free(trans);
|
|
}
|
|
}
|
|
edje_file_collection_list_free(coll);
|
|
}
|
|
}
|
|
}
|
|
p = strrchr(category, '/');
|
|
if (p) *p = 0;
|
|
}
|
|
while (p);
|
|
|
|
list = eina_list_sort(list, 0, EINA_COMPARE_CB(strcmp));
|
|
return list;
|
|
}
|