/* * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors * Copyright (C) 2004-2022 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 "config.h" #include #include #include "E.h" #include "backgrounds.h" #include "borders.h" #include "desktops.h" #include "ewins.h" #include "file.h" #include "groups.h" #include "menus.h" #include "parse.h" #include "progress.h" static char menu_scan_recursive = 0; static Menu *MenuCreateFromDirectory(const char *name, Menu * parent, MenuStyle * ms, const char *dir); static int _ext_is_imagetype(const char *ext); static MenuItem * MenuItemCreateFromBackground(const char *bgid, const char *file) { MenuItem *mi; Background *bg; char thumb[1024], buf[1048]; bg = BrackgroundCreateFromImage(bgid, file, thumb, sizeof(thumb)); if (!bg) return NULL; Esnprintf(buf, sizeof(buf), "bg use %s", bgid); mi = MenuItemCreate(NULL, thumb, buf, NULL); return mi; } static const char * _dircache_filename(char *buf, unsigned int len, struct stat *st) { static const char chmap[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"; int aa, bb, cc; aa = (int)st->st_ino; bb = filedev_map((int)st->st_dev); cc = (st->st_mtime > st->st_ctime) ? st->st_mtime : st->st_ctime; Esnprintf(buf, len, ".%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", chmap[(aa >> 0) & 0x3f], chmap[(aa >> 6) & 0x3f], chmap[(aa >> 12) & 0x3f], chmap[(aa >> 18) & 0x3f], chmap[(aa >> 24) & 0x3f], chmap[(aa >> 28) & 0x3f], chmap[(bb >> 0) & 0x3f], chmap[(bb >> 6) & 0x3f], chmap[(bb >> 12) & 0x3f], chmap[(bb >> 18) & 0x3f], chmap[(bb >> 24) & 0x3f], chmap[(bb >> 28) & 0x3f], chmap[(cc >> 0) & 0x3f], chmap[(cc >> 6) & 0x3f], chmap[(cc >> 12) & 0x3f], chmap[(cc >> 18) & 0x3f], chmap[(cc >> 24) & 0x3f], chmap[(cc >> 28) & 0x3f]); return buf; } static int MenuLoadFromDirectory(Menu * m) { Progressbar *p = NULL; Menu *mm; int i, num, len; const char *dir; char **list, s[4096], ss[4096], cs[4096]; const char *ext; MenuItem *mi; struct stat st; FILE *f; time_t lastmod; dir = MenuGetData(m); lastmod = moddate(dir); if (!menu_scan_recursive && lastmod <= MenuGetTimestamp(m)) return 0; MenuSetTimestamp(m, lastmod); MenuEmpty(m, 0); if (stat(dir, &st) < 0) return 1; Esnprintf(cs, sizeof(cs), "%s/cached/img/%s.dir", EDirUserCache(), _dircache_filename(ss, sizeof(ss), &st)); if (Mode.backgrounds.force_scan) goto skip_dir_cache; if (exists(cs)) { /* cached dir listing - use it */ f = fopen(cs, "r"); if (!f) return 1; while (fgets(s, sizeof(s), f)) { char s2[1024]; s[strlen(s) - 1] = 0; len = 0; sscanf(s, "%1000s %1000s %n", ss, s2, &len); if (!strcmp(ss, "BG")) { Esnprintf(ss, sizeof(ss), "%s/%s", dir, s + len); mi = MenuItemCreateFromBackground(s2, ss); MenuAddItem(m, mi); } else if (!strcmp(ss, "EXE")) { Esnprintf(ss, sizeof(ss), "exec %s/%s", dir, s2); mi = MenuItemCreate(NULL, NULL, ss, NULL); MenuAddItem(m, mi); } else if (!strcmp(ss, "DIR")) { Esnprintf(ss, sizeof(ss), "%s/%s", dir, s2); mm = MenuCreateFromDirectory(ss, m, NULL, ss); mi = MenuItemCreate(s2, NULL, NULL, mm); MenuAddItem(m, mi); } } fclose(f); return 1; } skip_dir_cache: Esnprintf(s, sizeof(s), "Scanning %s", dir); p = ProgressbarCreate(s, 600, 16); if (p) ProgressbarShow(p); f = fopen(cs, "w"); list = E_ls(dir, &num); for (i = 0; i < num; i++) { if (p) ProgressbarSet(p, (i * 100) / num); Esnprintf(ss, sizeof(ss), "%s/%s", dir, list[i]); /* skip "dot" files and dirs - senisble */ if ((*(list[i]) == '.') || (stat(ss, &st) < 0)) continue; ext = fileext(ss); if (S_ISDIR(st.st_mode)) { /* Submenu */ mm = MenuCreateFromDirectory(ss, m, NULL, ss); mi = MenuItemCreate(list[i], NULL, NULL, mm); MenuAddItem(m, mi); if (f) fprintf(f, "DIR %s\n", list[i]); } #if 0 else if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { /* Executable */ /* that's it - people are stupid and have executable images and just */ /* don't get it - so I'm disablign this to save people from their own */ /* stupidity */ mi = MenuItemCreate(list[i], NULL, ss, NULL); MenuAddItem(m, mi); if (f) fprintf(f, "EXE %s\n", list[i]); } #endif else if (_ext_is_imagetype(ext)) { /* Background */ char s3[512]; _dircache_filename(s3, sizeof(s3), &st); mi = MenuItemCreateFromBackground(s3, ss); if (mi) { MenuAddItem(m, mi); if (f) fprintf(f, "BG %s %s\n", s3, list[i]); } } } if (f) fclose(f); if (p) ProgressbarDestroy(p); if (list) StrlistFree(list, num); return 1; } static Menu * MenuCreateFromDirectory(const char *name, Menu * parent, MenuStyle * ms, const char *dir) { static int calls = 0; Menu *m; if (calls > 32) return NULL; calls++; m = MenuCreate(name, NULL, parent, ms); MenuSetData(m, Estrdup(dir)); MenuSetLoader(m, MenuLoadFromDirectory); if (menu_scan_recursive) MenuLoadFromDirectory(m); calls--; return m; } void ScanBackgroundMenu(void) { menu_scan_recursive = 1; MenuLoad(MenuFind("backgrounds", NULL)); Mode.backgrounds.force_scan = 0; menu_scan_recursive = 0; } static void FillFlatFileMenu(Menu * m, const char *file) { FILE *f; char first = 1; char s[4096]; unsigned int len; f = fopen(file, "r"); if (!f) { Eprintf("Unable to open menu file %s -- %s\n", file, strerror(errno)); return; } MenuSetIconSize(m, 0); /* Scale to default */ while (fgets(s, 4096, f)) { if (s[0] == '#') continue; len = strlen(s); while (len && (s[len - 1] == '\n' || s[len - 1] == '\r')) len--; if (len == 0) continue; s[len] = '\0'; if (first) { char *title, *style, *alias; first = 0; title = style = alias = NULL; parse(s, "%S%S%S", &title, &style, &alias); if (title) MenuSetTitle(m, title); if (!style) style = (char *)"ROOT"; MenuSetStyle(m, MenuStyleFind(style)); if (alias) MenuSetAlias(m, alias); } else { char *txt, *icon, *act, *params; char cmd[4080]; char wd[4096]; MenuItem *mi; Menu *mm; txt = icon = act = params = NULL; wd[0] = '\0'; parse(s, "%S%T%S%S", &txt, &icon, &act, ¶ms); if (icon) icon = FindFile(icon, NULL, FILE_TYPE_ICON); if ((act) && (!strcmp(act, "exec")) && (params)) { EnvSubst(params, cmd, sizeof(cmd)); if (path_canexec0(cmd)) { Esnprintf(wd, sizeof(wd), "exec %s", cmd); mi = MenuItemCreate(txt, icon, wd, NULL); MenuAddItem(m, mi); } } else if ((act) && (!strcmp(act, "menu")) && (params)) { mm = MenuFind(params, NULL); if (mm) { mi = MenuItemCreate(txt, icon, NULL, mm); MenuAddItem(m, mi); } } else if (act) { mi = MenuItemCreate(txt, icon, act, NULL); MenuAddItem(m, mi); } Efree(icon); } } fclose(f); } static int MenuLoadFromFlatFile(Menu * m) { const char *ff; time_t lastmod; ff = MenuGetData(m); lastmod = moddate(ff); if (lastmod <= MenuGetTimestamp(m)) return 0; MenuSetTimestamp(m, lastmod); MenuEmpty(m, 0); FillFlatFileMenu(m, ff); return 1; } static Menu * MenuCreateFromFlatFile(const char *name, Menu * parent, MenuStyle * ms, const char *file) { Menu *m = NULL; char *ff; static int calls = 0; if (calls > 32) return NULL; calls++; if (!file) file = name; ff = FindFile(file, Mode.theme.path, FILE_TYPE_MENU); if (!ff) goto done; m = MenuCreate(file, NULL, parent, ms); if (name != file) MenuSetAlias(m, name); MenuSetData(m, ff); MenuSetLoader(m, MenuLoadFromFlatFile); done: calls--; return m; } static Menu * MenuCreateFromBackgrounds(const char *name, MenuStyle * ms) { Menu *m; char s[FILEPATH_LEN_MAX]; Esnprintf(s, sizeof(s), "%s/backgrounds", EDirUserConf()); m = MenuCreateFromDirectory(name, NULL, ms, s); if (!m) return NULL; MenuSetTitle(m, _("Backgrounds")); MenuSetInternal(m); return m; } static int MenuLoadFromThemes(Menu * m) { char **lst; int i, num; char ss[4096], *s; MenuItem *mi; if (MenuGetTimestamp(m)) return 0; MenuSetTimestamp(m, 1); lst = ThemesList(&num); for (i = 0; i < num; i++) { s = ThemePathName(lst[i]); Esnprintf(ss, sizeof(ss), "theme use %s", s); mi = MenuItemCreate(s, NULL, ss, NULL); MenuAddItem(m, mi); Efree(s); } if (lst) StrlistFree(lst, i); return 1; } static Menu * MenuCreateFromThemes(const char *name, MenuStyle * ms) { Menu *m; m = MenuCreate(name, _("Themes"), NULL, ms); MenuSetInternal(m); MenuSetLoader(m, MenuLoadFromThemes); return m; } static Menu * MenuCreateFromBorders(const char *name, MenuStyle * ms) { char s[128]; Menu *m; Border **lst; int i, num; MenuItem *mi; const char *bname; m = MenuCreate(name, _("Border"), NULL, ms); lst = BordersGetList(&num); if (!lst) return m; for (i = 0; i < num; i++) { bname = BorderGetName(lst[i]); if (*bname == '_') continue; /* if its not internal (ie doesnt start with _ ) */ Esnprintf(s, sizeof(s), "wop * bo %s", bname); mi = MenuItemCreate(bname, NULL, s, NULL); MenuAddItem(m, mi); } Efree(lst); return m; } static int MenuCheckShowEwinDesk(EWin * ewin, void *prm) { if (!EwinGetTitle(ewin) || ewin->props.skip_winlist) return 0; return !prm || EoGetDesk(ewin) == prm; } static void MenuLoadFromEwins(Menu * m, int (*f)(EWin * ewin, void *prm), void *prm) { EWin *const *lst; int i, num; char s[256]; MenuItem *mi; lst = EwinListGetAll(&num); for (i = 0; i < num; i++) { if (!f(lst[i], prm)) continue; Esnprintf(s, sizeof(s), "wop %#x focus", EwinGetClientXwin(lst[i])); mi = MenuItemCreate(EwinGetTitle(lst[i]), NULL, s, NULL); MenuAddItem(m, mi); } } static int MenuLoadFromAllEwins(Menu * m) { MenuEmpty(m, 0); MenuLoadFromEwins(m, MenuCheckShowEwinDesk, NULL); return 1; } static Menu * MenuCreateFromAllEWins(const char *name, MenuStyle * ms) { Menu *m; m = MenuCreate(name, _("Window List"), NULL, ms); MenuSetInternal(m); MenuSetDynamic(m); MenuSetLoader(m, MenuLoadFromAllEwins); return m; } static int MenuLoadFromDesktops(Menu * m) { Menu *mm; unsigned int i; char s[256]; MenuItem *mi; MenuEmpty(m, 0); for (i = 0; i < DesksGetNumber(); i++) { mm = MenuCreate("__SUBMENUDESK_E", NULL, m, NULL); Esnprintf(s, sizeof(s), "desk goto %i", i); mi = MenuItemCreate(_("Go to this Desktop"), NULL, s, NULL); MenuAddItem(mm, mi); MenuLoadFromEwins(mm, MenuCheckShowEwinDesk, DeskGet(i)); Esnprintf(s, sizeof(s), _("Desktop %i"), i); mi = MenuItemCreate(s, NULL, NULL, mm); MenuAddItem(m, mi); } return 1; } static Menu * MenuCreateFromDesktops(const char *name, MenuStyle * ms) { Menu *m; m = MenuCreate(name, _("Desks"), NULL, ms); MenuSetInternal(m); MenuSetDynamic(m); MenuSetLoader(m, MenuLoadFromDesktops); return m; } static int MenuLoadFromGroups(Menu * m) { Menu *mm; Group **lst; EWin *const *ewlst; int i, j, num, ewnum; char s[256]; MenuItem *mi; MenuEmpty(m, 0); lst = GroupsGetList(&num); if (!lst) return 1; for (i = 0; i < num; i++) { ewlst = GroupGetMembers(lst[i], &ewnum); mm = MenuCreate("__SUBMENUGROUP_E", NULL, m, NULL); Esnprintf(s, sizeof(s), "gop %i showhide", EwinGetClientXwin(ewlst[0])); mi = MenuItemCreate(_("Show/Hide this group"), NULL, s, NULL); Esnprintf(s, sizeof(s), "wop %#x ic", EwinGetClientXwin(ewlst[0])); MenuAddItem(mm, mi); mi = MenuItemCreate(_("Iconify this group"), NULL, s, NULL); MenuAddItem(mm, mi); for (j = 0; j < ewnum; j++) { Esnprintf(s, sizeof(s), "wop %#x focus", EwinGetClientXwin(ewlst[j])); mi = MenuItemCreate(EwinGetTitle(ewlst[j]), NULL, s, NULL); MenuAddItem(mm, mi); } Esnprintf(s, sizeof(s), _("Group %i"), i); mi = MenuItemCreate(s, NULL, NULL, mm); MenuAddItem(m, mi); } Efree(lst); return 1; } static Menu * MenuCreateFromGroups(const char *name, MenuStyle * ms) { Menu *m; m = MenuCreate(name, _("Groups"), NULL, ms); MenuSetInternal(m); MenuSetDynamic(m); MenuSetLoader(m, MenuLoadFromGroups); return m; } #if 0 /* Not finished */ Menu * MenuCreateMoveToDesktop(char *name, Menu * parent, MenuStyle * ms) { Menu *m; int i; char s1[256], s2[256]; MenuItem *mi; m = MenuCreate(name, NULL, parent, ms); for (i = 0; i < Mode.numdesktops; i++) { Esnprintf(s1, sizeof(s1), _("Desktop %i"), i); Esnprintf(s2, sizeof(s2), "%i", i); mi = MenuItemCreate(s1, NULL, s2, NULL); MenuAddItem(m, mi); } return m; } #endif Menu * MenusCreateInternal(const char *type, const char *name, const char *style, const char *prm) { Menu *m; MenuStyle *ms; m = NULL; ms = NULL; if (style) ms = MenuStyleFind(style); if (!type) { if (!strstr(name, ".menu")) type = name; } if (!type || !strcmp(type, "file")) { m = MenuCreateFromFlatFile(name, NULL, ms, prm); } else if (!strcmp(type, "dirscan")) { SoundPlay(SOUND_SCANNING); m = MenuCreateFromDirectory(name, NULL, ms, prm); } else if (!strcmp(type, "backgrounds")) { m = MenuCreateFromBackgrounds(name, ms); } else if (!strcmp(type, "borders")) { m = MenuCreateFromBorders(name, ms); } else if (!strcmp(type, "themes")) { m = MenuCreateFromThemes(name, ms); } else if (!strcmp(type, "windowlist")) { m = MenuCreateFromAllEWins(name, ms); } else if (!strcmp(type, "deskmenu")) { m = MenuCreateFromDesktops(name, ms); } else if (!strcmp(type, "groupmenu")) { m = MenuCreateFromGroups(name, ms); } return m; } static int _ext_is_imagetype(const char *ext) { static const char *const exts[] = { "jpg", "jpeg", "gif", "png", "tif", "tiff", "xpm", "ppm", "pgm", "pnm", "bmp", NULL }; int i; for (i = 0; exts[i]; i++) if (!Estrcasecmp(exts[i], ext)) return 1; return 0; }