e16/src/theme.c

531 lines
13 KiB
C

/*
* Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
* Copyright (C) 2004-2023 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 "user.h"
#include "util.h"
#include "session.h"
/* Update Mode.theme.paths (theme path list) */
static void
_ThemePathsUpdate(void)
{
char paths[FILEPATH_LEN_MAX];
Esnprintf(paths, sizeof(paths), "%s/themes:%s/.themes:%s/themes:%s",
EDirUserConf(), userhome(), EDirRoot(),
(Conf.theme.extra_path) ? Conf.theme.extra_path : "");
EFREE_DUP(Mode.theme.paths, paths);
}
/* Check if this is a theme dir */
static int
_ThemeCheckDir(const char *path, const char *px)
{
static const char *const theme_files[] = {
"init.cfg",
#if 0
"epplets/epplets.cfg",
#endif
NULL
};
const char *tf;
int i;
char s[FILEPATH_LEN_MAX];
if (EDebug(EDBUG_TYPE_CONFIG))
Eprintf("%s: %s%s\n", __func__, path, px);
for (i = 0; (tf = theme_files[i]); i++)
{
Esnprintf(s, sizeof(s), "%s%s/%s", path, px, tf);
if (!isfile(s))
return 0;
}
return 1;
}
static int
_ThemeCheckPath1(const char *path)
{
if (_ThemeCheckDir(path, ""))
return 1;
if (_ThemeCheckDir(path, "/e16"))
return 1;
return 0;
}
static char *
_ThemeCheckPath(const char *path)
{
char ss[FILEPATH_LEN_MAX];
if (EDebug(EDBUG_TYPE_CONFIG))
Eprintf("%s: %s\n", __func__, path);
if (_ThemeCheckDir(path, ""))
return Estrdup(path);
if (_ThemeCheckDir(path, "/e16"))
{
Esnprintf(ss, sizeof(ss), "%s/e16", path);
return Estrdup(ss);
}
return NULL;
}
char *
ThemePathName(const char *path)
{
const char *p;
char *s;
if (!path)
return NULL;
p = strrchr(path, '/');
if (!p)
return Estrdup(path); /* Name only */
if (strcmp(p + 1, "e16"))
return Estrdup(p + 1); /* Regular path */
/* <path>/<themename>/e16 */
s = Estrdup(path);
s[p - path] = '\0';
p = strrchr(s, '/');
if (!p)
return s; /* Should not happen */
p++;
memmove(s, p, strlen(p) + 1);
return s;
}
static void
_append_merge_dir(char *dir, char ***list, int *count)
{
char ss[4000], s1[FILEPATH_LEN_MAX];
char **str, *s;
int i, num;
str = E_ls(dir, &num);
if (!str)
return;
for (i = 0; i < num; i++)
{
if (!strcmp(str[i], "DEFAULT"))
continue;
Esnprintf(ss, sizeof(ss), "%s/%s", dir, str[i]);
if (isdir(ss))
{
if (_ThemeCheckPath1(ss))
goto got_one;
continue;
}
if (!isfile(ss))
continue;
s = strrchr(ss, '.');
if (!s)
continue;
if (strcmp(s + 1, "etheme") == 0)
{
Esnprintf(s1, sizeof(s1), "%s/themes/%s", EDirUserConf(), str[i]);
s = strrchr(s1, '.');
if (!s)
continue;
*s = '\0';
if (!isdir(s1))
goto got_one;
}
if (strcmp(s + 1, "theme") == 0)
{
Esnprintf(s1, sizeof(s1), "%s/.themes/%s", userhome(), str[i]);
s = strrchr(s1, '.');
if (!s)
continue;
*s = '\0';
if (!isdir(s1))
goto got_one;
}
continue;
got_one:
(*count)++;
*list = EREALLOC(char *, *list, *count);
(*list)[(*count) - 1] = Estrdup(ss);
}
StrlistFree(str, num);
}
char **
ThemesList(int *number)
{
char **lst, **list;
int i, num, count;
_ThemePathsUpdate();
lst = StrlistFromString(Mode.theme.paths, ':', &num);
count = 0;
list = NULL;
for (i = 0; i < num; i++)
_append_merge_dir(lst[i], &list, &count);
StrlistFree(lst, num);
*number = count;
return list;
}
static char *
_ThemeExtract(const char *path)
{
char th[4000];
FILE *f;
unsigned char buf[262];
size_t ret;
const char *p;
char name[128], *type;
if (EDebug(EDBUG_TYPE_CONFIG))
Eprintf("%s: %s\n", __func__, path);
/* its a file - check its type */
f = fopen(path, "r");
if (!f)
return NULL;
ret = fread(buf, 1, sizeof(buf), f);
memset(buf + ret, 0, sizeof(buf) - ret);
fclose(f);
p = strrchr(path, '/');
p = (p) ? p + 1 : path;
Esnprintf(name, sizeof(name), "%.127s", p);
type = strchr(name, '.');
if (type)
*type++ = '\0';
if (type && strcmp(type, "theme") == 0)
{
Esnprintf(th, sizeof(th), "%s/.themes", userhome());
if (!isdir(th))
E_md(th);
Esnprintf(th, sizeof(th), "%s/.themes/%s", userhome(), name);
}
else
Esnprintf(th, sizeof(th), "%s/themes/%s", EDirUserConf(), name);
/* check magic numbers */
if ((buf[0] == 31) && (buf[1] == 139))
{
/* gzipped tarball */
E_md(th);
Esystem("gzip -d -c < %s | (cd %s ; tar -xf -)", path, th);
}
else if ((buf[257] == 'u') && (buf[258] == 's') &&
(buf[259] == 't') && (buf[260] == 'a') && (buf[261] == 'r'))
{
/* vanilla tarball */
E_md(th);
Esystem("cat %s | (cd %s ; tar -xf -)", path, th);
}
else
return NULL;
return _ThemeCheckPath(th);
}
char *
ThemePathFind(const char *theme)
{
static const char *const default_themes[] = {
"DEFAULT", "winter", "BrushedMetal-Tigert", "ShinyMetal", NULL
};
char tpbuf[4000], *path;
char **lst;
int i, j, num;
if (EDebug(EDBUG_TYPE_CONFIG))
Eprintf("%s: %s\n", __func__, theme);
_ThemePathsUpdate();
if (!theme || !theme[0])
theme = NULL; /* Lookup default */
else if (!strcmp(theme, "-")) /* Use fallbacks */
return NULL;
else if (exists(theme))
goto check;
lst = StrlistFromString(Mode.theme.paths, ':', &num);
i = 0;
do
{
if (!theme)
goto next;
for (j = 0; j < num; j++)
{
Esnprintf(tpbuf, sizeof(tpbuf), "%s/%s", lst[j], theme);
if (exists(tpbuf))
{
theme = tpbuf;
goto done;
}
}
next:
theme = default_themes[i++];
}
while (theme);
done:
StrlistFree(lst, num);
check:
if (theme)
{
path = NULL;
if (isdir(theme))
path = _ThemeCheckPath(theme);
else if (isfile(theme))
path = _ThemeExtract(theme);
if (path)
return path;
}
/* No theme found yet, just find any theme */
lst = ThemesList(&num);
if (!lst)
return NULL;
path = Estrdup(lst[0]);
StrlistFree(lst, num);
return path;
}
void
ThemeFind(const char *theme)
{
char name[4000];
char namx[FILEPATH_LEN_MAX];
const char *p;
char *path, *s;
if (EDebug(EDBUG_TYPE_CONFIG))
Eprintf("%s: %s\n", __func__, theme);
name[0] = '\0';
p = (theme && *theme != ':') ? theme : Conf.theme.name;
if (p)
snprintf(name, sizeof(name), "%s", p);
s = strchr(name, ':');
if (s)
*s++ = '\0';
p = (theme && *theme == ':') ? theme + 1 : s;
EFREE_DUP(Mode.theme.variant, p);
path = ThemePathFind(name);
if (!path && strcmp(name, "-"))
{
Alert(_("No themes were found in the default directories:\n"
" %s\n"
"Proceeding from here is mostly pointless.\n"),
Mode.theme.paths);
}
if (theme)
{
Efree(Conf.theme.name);
if (isfile(theme))
Conf.theme.name = ThemePathName(path);
else
{
s = namx;
if (Mode.theme.variant)
snprintf(namx, sizeof(namx), "%s:%s", name, Mode.theme.variant);
else
s = name;
Conf.theme.name = Estrdup(s);
}
}
EFREE_SET(Mode.theme.path, (path) ? path : Estrdup("-"));
}
#if ENABLE_DIALOGS
#include "dialog.h"
#include "settings.h"
/*
* Configuration dialog
*/
static char tmp_use_theme_font;
static char tmp_use_alt_font;
static void
_DlgThemeApply(Dialog *d __UNUSED__, int val __UNUSED__, void *data __UNUSED__)
{
if (Conf.theme.use_theme_font_cfg == tmp_use_theme_font &&
Conf.theme.use_alt_font_cfg == tmp_use_alt_font)
return;
DialogOK(_("Message"), _("Changes will take effect after restart"));
Conf.theme.use_theme_font_cfg = tmp_use_theme_font;
Conf.theme.use_alt_font_cfg = tmp_use_alt_font;
autosave();
}
static void
_DlgThemeFill(Dialog *d __UNUSED__, DItem *table, void *data __UNUSED__)
{
DItem *di;
char buf[1024];
tmp_use_theme_font = Conf.theme.use_theme_font_cfg;
tmp_use_alt_font = Conf.theme.use_alt_font_cfg;
DialogItemTableSetOptions(table, 2, 0, 0, 0);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Use theme font configuration"));
DialogItemCheckButtonSetPtr(di, &tmp_use_theme_font);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
Esnprintf(buf, sizeof(buf), _("Use alternate font configuration (%s)"),
Conf.theme.font_cfg ? Conf.theme.font_cfg : _("not set"));
DialogItemSetText(di, buf);
DialogItemCheckButtonSetPtr(di, &tmp_use_alt_font);
}
const DialogDef DlgTheme = {
"CONFIGURE_AUDIO",
N_("Theme"), N_("Theme Settings"),
0,
SOUND_SETTINGS_MISCELLANEOUS,
"pix/miscellaneous.png",
N_("Enlightenment Theme\n" "Settings Dialog"),
_DlgThemeFill,
DLG_OAC, _DlgThemeApply, NULL
};
#endif /* ENABLE_DIALOGS */
/*
* Theme module
*/
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] == '?')
{
char *path;
IpcPrintf("Name: %s\n", (Conf.theme.name) ? Conf.theme.name : "-");
IpcPrintf("Full: %s\n", Mode.theme.path);
path = ThemePathFind(NULL);
IpcPrintf("Default: %s\n", path);
Efree(path);
IpcPrintf("Path: %s\n", Mode.theme.paths);
}
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" }
,
};
static const CfgItem ThemeCfgItems[] = {
CFG_ITEM_INT(Conf.theme, cache_ver, 0),
CFG_ITEM_STR(Conf.theme, name),
CFG_ITEM_STR(Conf.theme, extra_path),
CFG_ITEM_BOOL(Conf.theme, use_theme_font_cfg, 0),
CFG_ITEM_BOOL(Conf.theme, use_alt_font_cfg, 0),
CFG_ITEM_STR(Conf.theme, font_cfg),
};
/*
* Module descriptor
*/
extern const EModule ModTheme;
const EModule ModTheme = {
"theme", "th",
NULL,
MOD_ITEMS(ThemeIpcArray),
MOD_ITEMS(ThemeCfgItems)
};