chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / theme.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2009 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #include "emodule.h"
26 #include "file.h"
27 #include "util.h"
28 #include "session.h"
29
30 /* Update Mode.theme.paths (theme path list) */
31 static void
32 ThemePathsUpdate(void)
33 {
34    char                paths[4096];
35
36    Esnprintf(paths, sizeof(paths), "%s/themes:%s/themes:%s", EDirUser(),
37              EDirRoot(), (Conf.theme.extra_path) ? Conf.theme.extra_path : "");
38    _EFDUP(Mode.theme.paths, paths);
39 }
40
41 /* Check if this is a theme dir */
42 static const char  *
43 ThemeCheckPath(const char *path)
44 {
45    static const char  *const theme_files[] = {
46       "init.cfg",
47 #if 0
48       "epplets/epplets.cfg",
49 #endif
50       NULL
51    };
52    const char         *tf;
53    int                 i;
54    char                s[4096];
55
56    for (i = 0; (tf = theme_files[i]) != NULL; i++)
57      {
58         Esnprintf(s, sizeof(s), "%s/%s", path, tf);
59         if (!isfile(s))
60            return NULL;
61      }
62
63    return path;
64 }
65
66 char               *
67 ThemePathName(const char *path)
68 {
69    const char         *p;
70    char               *s;
71
72    if (!path)
73       return NULL;
74    p = strrchr(path, '/');
75    if (!p)
76       return Estrdup(path);     /* Name only */
77    if (strcmp(p + 1, "e16"))
78       return Estrdup(p + 1);    /* Regular path */
79
80    /* <path>/<themename>/e16 */
81    s = strdup(path);
82    s[p - path] = '\0';
83    p = strrchr(s, '/');
84    if (!p)
85       return Estrdup(path);     /* Should not happen */
86    p++;
87    memmove(s, p, strlen(p) + 1);
88    return s;
89 }
90
91 static void
92 append_merge_dir(char *dir, char ***list, int *count)
93 {
94    char                ss[FILEPATH_LEN_MAX], s1[FILEPATH_LEN_MAX];
95    char              **str = NULL, *s;
96    int                 i, num;
97
98    str = E_ls(dir, &num);
99    if (!str)
100       return;
101
102    for (i = 0; i < num; i++)
103      {
104         if (!strcmp(str[i], "DEFAULT"))
105            continue;
106
107         Esnprintf(ss, sizeof(ss), "%s/%s", dir, str[i]);
108
109         if (isdir(ss))
110           {
111              if (ThemeCheckPath(ss))
112                 goto got_one;
113              Esnprintf(ss, sizeof(ss), "%s/%s/e16", dir, str[i]);
114              if (ThemeCheckPath(ss))
115                 goto got_one;
116              continue;
117           }
118         else if (isfile(ss))
119           {
120              s = strstr(str[i], ".etheme");
121              if (!s)
122                 continue;
123              Esnprintf(s1, sizeof(s1), "%s/themes/%s", EDirUser(), str[i]);
124              s = strstr(s1, ".etheme");
125              if (!s)
126                 continue;
127              *s = '\0';
128              if (isdir(s1))
129                 continue;
130           }
131         else
132           {
133              continue;
134           }
135
136       got_one:
137         (*count)++;
138         *list = EREALLOC(char *, *list, *count);
139
140         (*list)[(*count) - 1] = Estrdup(ss);
141      }
142    StrlistFree(str, num);
143 }
144
145 char              **
146 ThemesList(int *number)
147 {
148    char              **lst, **list;
149    int                 i, num, count;
150
151    ThemePathsUpdate();
152    lst = StrlistFromString(Mode.theme.paths, ':', &num);
153
154    count = 0;
155    list = NULL;
156    for (i = 0; i < num; i++)
157       append_merge_dir(lst[i], &list, &count);
158
159    StrlistFree(lst, num);
160
161    *number = count;
162    return list;
163 }
164
165 static const char  *
166 ThemeGetPath(const char *path, char *buf, unsigned int len)
167 {
168    const char         *s;
169    char                s1[FILEPATH_LEN_MAX];
170    int                 l;
171
172    /* We only attempt to dereference a DEFAULT link */
173    s = strstr(path, "/DEFAULT");
174    if (s == NULL)
175       return path;
176
177    l = readlink(path, s1, sizeof(s1) - 1);
178    if (l < 0)
179       return path;
180    s1[l] = '\0';
181
182    if (isabspath(s1))
183      {
184         Esnprintf(buf, len, "%s", s1);
185         return buf;
186      }
187
188    Esnprintf(buf, len, "%s", path);     /* Copy path */
189    l = s + 1 - path;
190    Esnprintf(buf + l, len - l, "%s", s1);       /* Substitute link */
191
192    return buf;
193 }
194
195 static char        *
196 ThemeExtract(const char *path)
197 {
198    char                s[FILEPATH_LEN_MAX];
199    char                th[FILEPATH_LEN_MAX];
200    FILE               *f;
201    unsigned char       buf[320];
202    char               *name;
203
204    /* its a directory - just use it "as is" */
205    if (isdir(path))
206      {
207         path = ThemeGetPath(path, s, sizeof(s));
208         goto done;
209      }
210
211    if (!isfile(path))
212       return NULL;
213
214    /* its a file - check its type */
215    f = fopen(path, "r");
216    if (!f)
217       return NULL;
218    fread(buf, 1, 320, f);
219    fclose(f);
220
221    name = fileof(path);
222    Esnprintf(th, sizeof(th), "%s/themes/%s", EDirUser(), name);
223    Efree(name);
224
225    /* check magic numbers */
226    if ((buf[0] == 31) && (buf[1] == 139))
227      {
228         /* gzipped tarball */
229         Esnprintf(s, sizeof(s),
230                   "gzip -d -c < %s | (cd %s ; tar -xf -)", path, th);
231      }
232    else if ((buf[257] == 'u') && (buf[258] == 's') &&
233             (buf[259] == 't') && (buf[260] == 'a') && (buf[261] == 'r'))
234      {
235         /* vanilla tarball */
236         Esnprintf(s, sizeof(s), "(cd %s ; tar -xf %s)", th, path);
237      }
238    else
239       return NULL;
240
241    E_md(th);
242    path = th;
243
244    /* exec the untar if tarred */
245    system(s);
246
247  done:
248    if (ThemeCheckPath(path))
249       return Estrdup(path);
250
251    /* failed */
252    return NULL;
253 }
254
255 char               *
256 ThemeFind(const char *theme)
257 {
258    static const char  *const default_themes[] = {
259       "DEFAULT", "winter", "BrushedMetal-Tigert", "ShinyMetal", NULL
260    };
261    char                tdir[4096], *path;
262    char              **lst;
263    int                 i, j, num;
264
265    ThemePathsUpdate();
266
267    path = NULL;
268
269    if (!theme || !theme[0])
270      {
271         theme = NULL;
272      }
273    else if (!strcmp(theme, "-"))        /* Use fallbacks */
274      {
275         return NULL;
276      }
277    else if (isabspath(theme))
278      {
279         path = ThemeExtract(theme);
280         if (path)
281            return path;
282         theme = NULL;
283      }
284
285    lst = StrlistFromString(Mode.theme.paths, ':', &num);
286
287    i = 0;
288    do
289      {
290         if (!theme)
291            goto next;
292         for (j = 0; j < num; j++)
293           {
294              Esnprintf(tdir, sizeof(tdir), "%s/%s", lst[j], theme);
295              path = ThemeExtract(tdir);
296              if (path)
297                 goto done;
298
299              Esnprintf(tdir, sizeof(tdir), "%s/%s/e16", lst[j], theme);
300              path = ThemeExtract(tdir);
301              if (path)
302                 goto done;
303           }
304       next:
305         theme = default_themes[i++];
306      }
307    while (theme);
308
309  done:
310    StrlistFree(lst, num);
311
312    if (path)
313       return path;
314
315    /* No theme found yet, just find any theme */
316    lst = ThemesList(&num);
317    if (!lst)
318       return NULL;
319    path = Estrdup(lst[0]);
320    StrlistFree(lst, num);
321
322    return path;
323 }
324
325 void
326 ThemePathFind(void)
327 {
328    char               *name, *path;
329
330    /*
331     * Conf.theme.name is read from the configuration.
332     * Mode.theme.path may be assigned on the command line.
333     */
334    name = (Mode.theme.path) ? Mode.theme.path : Conf.theme.name;
335    path = ThemeFind(name);
336
337    if (!path && (!name || strcmp(name, "-")))
338      {
339         Alert(_("No themes were found in the default directories:\n"
340                 " %s\n"
341                 "Proceeding from here is mostly pointless.\n"),
342               Mode.theme.paths);
343      }
344
345    Efree(Conf.theme.name);
346    Conf.theme.name = ThemePathName(path);
347
348    Efree(Mode.theme.path);
349    Mode.theme.path = (path) ? path : Estrdup("-");
350 }
351
352 #if ENABLE_DIALOGS
353 #include "dialog.h"
354 /*
355  * Configuration dialog
356  */
357 static char         tmp_use_theme_font;
358 static char         tmp_use_alt_font;
359
360 static void
361 _DlgThemeConfigure(Dialog * d __UNUSED__, int val, void *data __UNUSED__)
362 {
363    if (val >= 2)
364       return;
365    if (Conf.theme.use_theme_font_cfg == tmp_use_theme_font &&
366        Conf.theme.use_alt_font_cfg == tmp_use_alt_font)
367       return;
368
369    DialogOK(_("Message"), _("Changes will take effect after restart"));
370
371    Conf.theme.use_theme_font_cfg = tmp_use_theme_font;
372    Conf.theme.use_alt_font_cfg = tmp_use_alt_font;
373    autosave();
374 }
375
376 static void
377 _DlgThemeFill(Dialog * d __UNUSED__, DItem * table, void *data __UNUSED__)
378 {
379    DItem              *di;
380    char                buf[1024];
381
382    tmp_use_theme_font = Conf.theme.use_theme_font_cfg;
383    tmp_use_alt_font = Conf.theme.use_alt_font_cfg;
384
385    DialogItemTableSetOptions(table, 2, 0, 0, 0);
386
387    di = DialogAddItem(table, DITEM_CHECKBUTTON);
388    DialogItemSetColSpan(di, 2);
389    DialogItemSetText(di, _("Use theme font configuration"));
390    DialogItemCheckButtonSetPtr(di, &tmp_use_theme_font);
391
392    di = DialogAddItem(table, DITEM_CHECKBUTTON);
393    DialogItemSetColSpan(di, 2);
394    Esnprintf(buf, sizeof(buf), _("Use alternate font configuration (%s)"),
395              Conf.theme.font_cfg ? Conf.theme.font_cfg : _("Not set"));
396    DialogItemSetText(di, buf);
397    DialogItemCheckButtonSetPtr(di, &tmp_use_alt_font);
398 }
399
400 const DialogDef     DlgTheme = {
401    "CONFIGURE_AUDIO",
402    N_("Theme"),
403    N_("Theme Settings"),
404    SOUND_SETTINGS_MISCELLANEOUS,
405    "pix/miscellaneous.png",
406    N_("Enlightenment Theme\n" "Settings Dialog\n"),
407    _DlgThemeFill,
408    DLG_OAC, _DlgThemeConfigure,
409 };
410 #endif /* ENABLE_DIALOGS */
411
412 /*
413  * Theme module
414  */
415
416 static void
417 ThemesIpc(const char *params)
418 {
419    const char         *p;
420    char                cmd[128], prm[128];
421    int                 len;
422
423    cmd[0] = prm[0] = '\0';
424    p = params;
425    if (p)
426      {
427         len = 0;
428         sscanf(p, "%100s %100s %n", cmd, prm, &len);
429         p += len;
430      }
431
432    if (!p || cmd[0] == '?')
433      {
434         char               *path;
435
436         IpcPrintf("Name: %s\n", (Conf.theme.name) ? Conf.theme.name : "-");
437         IpcPrintf("Full: %s\n", Mode.theme.path);
438         path = ThemeFind(NULL);
439         IpcPrintf("Default: %s\n", path);
440         Efree(path);
441         IpcPrintf("Path: %s\n", Mode.theme.paths);
442      }
443    else if (!strncmp(cmd, "list", 2))
444      {
445         char              **lst;
446         int                 i, num;
447
448         lst = ThemesList(&num);
449         if (!lst)
450            return;
451         for (i = 0; i < num; i++)
452            IpcPrintf("%s\n", lst[i]);
453         StrlistFree(lst, num);
454      }
455    else if (!strcmp(cmd, "use"))
456      {
457         /* FIXME - ThemeCheckIfValid(s) */
458         SessionExit(EEXIT_THEME, prm);
459      }
460 }
461
462 static const IpcItem ThemeIpcArray[] = {
463    {
464     ThemesIpc,
465     "theme", "th",
466     "Theme commands",
467     "  theme             Show current theme\n"
468     "  theme list        Show all themes\n"
469     "  theme use <name>  Switch to theme <name>\n"}
470    ,
471 };
472 #define N_IPC_FUNCS (sizeof(ThemeIpcArray)/sizeof(IpcItem))
473
474 static const CfgItem ThemeCfgItems[] = {
475    CFG_ITEM_STR(Conf.theme, name),
476    CFG_ITEM_STR(Conf.theme, extra_path),
477    CFG_ITEM_BOOL(Conf.theme, use_theme_font_cfg, 0),
478    CFG_ITEM_BOOL(Conf.theme, use_alt_font_cfg, 0),
479    CFG_ITEM_STR(Conf.theme, font_cfg),
480 };
481 #define N_CFG_ITEMS (sizeof(ThemeCfgItems)/sizeof(CfgItem))
482
483 /*
484  * Module descriptor
485  */
486 extern const EModule ModTheme;
487 const EModule       ModTheme = {
488    "theme", "th",
489    NULL,
490    {N_IPC_FUNCS, ThemeIpcArray},
491    {N_CFG_ITEMS, ThemeCfgItems}
492 };