e16/src/theme.c

517 lines
11 KiB
C

/*
* Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
* Copyright (C) 2004-2008 Kim Woelders
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of the Software, its documentation and marketing & publicity
* materials, and acknowledgment shall be given in the documentation, materials
* and software packages that this Software was used.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "E.h"
#include "emodule.h"
#include "file.h"
#include "session.h"
#define ENABLE_THEME_SANITY_CHECKING 0
#if ENABLE_THEME_SANITY_CHECKING
static char *badtheme = NULL;
static char *badreason = NULL;
#endif
static const char *const theme_files[] = {
#if ENABLE_THEME_SANITY_CHECKING
"borders.cfg",
"buttons.cfg",
"cursors.cfg",
"desktops.cfg",
"imageclasses.cfg",
#endif
"init.cfg",
#if ENABLE_THEME_SANITY_CHECKING
"menustyles.cfg",
"slideouts.cfg",
"tooltips.cfg",
#endif
NULL
};
/* Check for files being in theme */
static int
SanitiseThemeDir(const char *dir)
{
const char *tf;
int i;
char s[4096];
for (i = 0; (tf = theme_files[i]) != NULL; i++)
{
Esnprintf(s, sizeof(s), "%s/%s", dir, tf);
if (isfile(s))
continue;
#if 0
Esnprintf(s, sizeof(s), _("Theme %s does not contain a %s file\n"), dir,
tf);
badreason = Estrdup(s);
#endif
return -1;
}
return 0;
}
static const char *
ThemeCheckPath(const char *path)
{
char s1[FILEPATH_LEN_MAX];
Esnprintf(s1, sizeof(s1), "%s/epplets/epplets.cfg", path);
if (exists(s1))
return path; /* OK */
return NULL; /* Not OK */
}
static char *
append_merge_dir(char *dir, char ***list, int *count)
{
char s[FILEPATH_LEN_MAX], ss[FILEPATH_LEN_MAX];
char **str = NULL, *def = NULL;
char already, *tmp, *tmp2;
int i, j, num, len;
str = E_ls(dir, &num);
if (!str)
return NULL;
for (i = 0; i < num; i++)
{
already = 0;
for (j = 0; (j < (*count)) && (!already); j++)
{
tmp = fileof((*list)[j]);
tmp2 = fileof(str[i]);
if ((tmp != NULL) && (tmp2 != NULL) && (!strcmp(tmp, tmp2)))
already = 1;
Efree(tmp);
Efree(tmp2);
}
if (already)
continue;
Esnprintf(ss, sizeof(ss), "%s/%s", dir, str[i]);
if (!strcmp(str[i], "DEFAULT"))
{
memset(s, 0, sizeof(s));
len = readlink(ss, s, sizeof(s) - 1);
if (len > 0)
{
s[len] = '\0'; /* Redundant due to memset */
if (s[0] == '/')
def = Estrdup(s);
else
{
Esnprintf(s, sizeof(s), "%s/%s", dir, s);
def = Estrdup(s);
}
}
}
else
{
if (isdir(ss))
{
if (SanitiseThemeDir(ss))
continue;
}
else if (isfile(ss))
{
if (!fileext(ss) || strcmp(fileext(ss), "etheme"))
continue;
}
(*count)++;
*list = EREALLOC(char *, *list, *count);
(*list)[(*count) - 1] = Estrdup(ss);
}
}
StrlistFree(str, num);
return def;
}
char **
ThemesList(int *number)
{
char paths[4096], *p, *s;
char **list;
int count;
Esnprintf(paths, sizeof(paths), "%s/themes:%s/themes:%s", EDirUser(),
EDirRoot(), (Conf.theme.extra_path) ? Conf.theme.extra_path : "");
count = 0;
list = NULL;
for (p = paths; p; p = s)
{
s = strchr(p, ':');
if (s)
*s++ = '\0';
if (*p == ':')
continue;
append_merge_dir(p, &list, &count);
}
*number = count;
return list;
}
static char *
ThemeGetPath(const char *path)
{
const char *s;
char *ss, s1[FILEPATH_LEN_MAX], s2[FILEPATH_LEN_MAX];
int len;
/* We only attempt to dereference a DEFAULT link */
s = strstr(path, "/DEFAULT");
if (s == NULL)
return Estrdup(path);
len = readlink(path, s1, sizeof(s1) - 1);
if (len < 0)
return Estrdup(path);
s1[len] = '\0';
if (isabspath(s1))
return Estrdup(s1);
Esnprintf(s2, sizeof(s2) - strlen(s1), "%s", path);
ss = strstr(s2, "/DEFAULT");
if (ss == NULL)
return NULL;
strcpy(ss + 1, s1);
if (isdir(s2))
return Estrdup(s2);
/* We should never get here */
return NULL;
}
static char *
ThemeGetDefault(void)
{
static const char *const dts[] = {
"DEFAULT", "winter", "BrushedMetal-Tigert", "ShinyMetal"
};
char s[FILEPATH_LEN_MAX];
const char *path;
char **lst;
int i, num;
/* First, try out some defaults */
num = sizeof(dts) / sizeof(char *);
for (i = 0; i < num; i++)
{
Esnprintf(s, sizeof(s), "%s/themes/%s", EDirUser(), dts[i]);
path = ThemeCheckPath(s);
if (path)
return ThemeGetPath(path);
Esnprintf(s, sizeof(s), "%s/themes/%s", EDirRoot(), dts[i]);
path = ThemeCheckPath(s);
if (path)
return ThemeGetPath(path);
}
/* Then, try out all installed themes */
path = NULL;
lst = ThemesList(&num);
if (lst)
{
for (i = 0; i < num; i++)
{
path = ThemeCheckPath(lst[i]);
if (path)
break;
}
StrlistFree(lst, num);
}
if (path)
return ThemeGetPath(path);
return NULL;
}
static void
ThemeCleanup(void)
{
/* TBD */
}
static char *
ThemeExtract(const char *theme)
{
char s[FILEPATH_LEN_MAX];
char th[FILEPATH_LEN_MAX];
FILE *f;
unsigned char buf[320];
const char *oktheme = NULL;
char *name;
/* its a directory - just use it "as is" */
if (isdir(theme))
{
oktheme = theme;
goto done;
}
/* its a file - check its type */
if (isfile(theme))
{
f = fopen(theme, "r");
if (!f)
goto done;
fread(buf, 1, 320, f);
fclose(f);
/* make the temp dir */
name = fileof(theme);
Esnprintf(th, sizeof(th), "%s/themes/%s", EDirUser(), name);
Efree(name);
E_md(th);
/* check magic numbers */
if ((buf[0] == 31) && (buf[1] == 139))
{
/* gzipped tarball */
Esnprintf(s, sizeof(s),
"gzip -d -c < %s | (cd %s ; tar -xf -)", theme, th);
}
else if ((buf[257] == 'u') && (buf[258] == 's')
&& (buf[259] == 't') && (buf[260] == 'a') && (buf[261] == 'r'))
{
/* vanilla tarball */
Esnprintf(s, sizeof(s), "(cd %s ; tar -xf %s)", th, theme);
}
else
goto done;
/* exec the untar if tarred */
system(s);
oktheme = th;
}
done:
if (oktheme && !SanitiseThemeDir(oktheme))
return Estrdup(oktheme);
/* failed */
ThemeCleanup();
return NULL;
}
static char *
ThemeFind(const char *theme)
{
char paths[4096], tdir[4096], *p, *s;
char *ret;
#if ENABLE_THEME_SANITY_CHECKING
badreason = _("Unknown\n");
#endif
if (!theme || !theme[0])
return ThemeGetDefault();
ret = NULL;
if (isabspath(theme))
ret = ThemeExtract(theme);
if (ret)
return ret;
Esnprintf(paths, sizeof(paths), "%s/themes:%s/themes:%s", EDirUser(),
EDirRoot(), (Conf.theme.extra_path) ? Conf.theme.extra_path : "");
for (p = paths; p; p = s)
{
s = strchr(p, ':');
if (s)
*s++ = '\0';
if (*p == ':')
continue;
Esnprintf(tdir, sizeof(tdir), "%s/%s", p, theme);
ret = ThemeExtract(tdir);
if (ret)
return ret;
}
ret = ThemeGetDefault();
#if ENABLE_THEME_SANITY_CHECKING
badtheme = Estrdup(theme);
#endif
return ret;
}
#if 0
void
ThemeBadDialog(void)
{
if (!badtheme)
return;
DialogOK(_("Bad Theme"),
_("The theme:\n" "%s\n"
"Is a badly formed theme package and is thus not being used.\n"
"Enlightenment has fallen back to using the DEFAULT theme.\n"
"\n" "The reason this theme is bad is:\n" "%s"),
badtheme, badreason);
}
#endif
/*
* Theme module
*/
void
ThemePathFind(void)
{
char *theme;
/*
* Conf.theme.name is read from the configuration.
* Mode.theme.path may be assigned on the command line.
*/
theme = (Mode.theme.path) ? Mode.theme.path : Conf.theme.name;
theme = ThemeFind(theme);
if (!theme)
{
Alert(_("No themes were found in the default theme directory:\n"
" %s/themes/\n"
"or in the user theme directory:\n"
" %s/themes/\n"
"Proceeding from here is mostly pointless.\n"),
EDirRoot(), EDirUser());
}
Efree(Conf.theme.name);
Conf.theme.name = (theme) ? fullfileof(theme) : NULL;
Efree(Mode.theme.path);
Mode.theme.path = theme;
}
static void
ThemesSighan(int sig, void *prm __UNUSED__)
{
switch (sig)
{
case ESIGNAL_CONFIGURE:
break;
case ESIGNAL_EXIT:
ThemeCleanup();
break;
}
}
static void
ThemesIpc(const char *params)
{
const char *p;
char cmd[128], prm[128];
int len;
cmd[0] = prm[0] = '\0';
p = params;
if (p)
{
len = 0;
sscanf(p, "%100s %100s %n", cmd, prm, &len);
p += len;
}
if (!p || cmd[0] == '?')
{
IpcPrintf("Name: %s\n", Conf.theme.name);
IpcPrintf("Full: %s\n", Mode.theme.path);
IpcPrintf("Default: %s\n", ThemeGetDefault());
IpcPrintf("Path: %s/themes:%s/themes:%s\n", EDirUser(), EDirRoot(),
(Conf.theme.extra_path) ? Conf.theme.extra_path : "");
}
else if (!strncmp(cmd, "list", 2))
{
char **lst;
int i, num;
lst = ThemesList(&num);
if (!lst)
return;
for (i = 0; i < num; i++)
IpcPrintf("%s\n", lst[i]);
StrlistFree(lst, num);
}
else if (!strcmp(cmd, "use"))
{
/* FIXME - ThemeCheckIfValid(s) */
SessionExit(EEXIT_THEME, prm);
}
}
static const IpcItem ThemeIpcArray[] = {
{
ThemesIpc,
"theme", "th",
"Theme commands",
" theme Show current theme\n"
" theme list Show all themes\n"
" theme use <name> Switch to theme <name>\n"}
,
};
#define N_IPC_FUNCS (sizeof(ThemeIpcArray)/sizeof(IpcItem))
static const CfgItem ThemeCfgItems[] = {
CFG_ITEM_BOOL(Conf.theme, localise, 0),
CFG_ITEM_STR(Conf.theme, name),
CFG_ITEM_STR(Conf.theme, extra_path),
CFG_ITEM_STR(Conf.theme, ttfont_path),
CFG_ITEM_BOOL(Conf.theme, use_alt_font_cfg, 1),
CFG_ITEM_STR(Conf.theme, font_cfg),
};
#define N_CFG_ITEMS (sizeof(ThemeCfgItems)/sizeof(CfgItem))
/*
* Module descriptor
*/
extern const EModule ModTheme;
const EModule ModTheme = {
"theme", "th",
ThemesSighan,
{N_IPC_FUNCS, ThemeIpcArray},
{N_CFG_ITEMS, ThemeCfgItems}
};