chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / menus-misc.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 "backgrounds.h"
26 #include "borders.h"
27 #include "desktops.h"
28 #include "ewins.h"
29 #include "file.h"
30 #include "groups.h"
31 #include "iclass.h"
32 #include "menus.h"
33 #include "parse.h"
34 #include "progress.h"
35 #include <errno.h>
36 #include <sys/stat.h>
37
38 static char         menu_scan_recursive = 0;
39
40 static Menu        *MenuCreateFromDirectory(const char *name, Menu * parent,
41                                             MenuStyle * ms, const char *dir);
42 static int          _ext_is_imagetype(const char *ext);
43
44 static MenuItem    *
45 MenuItemCreateFromBackground(const char *bgid, const char *file)
46 {
47    MenuItem           *mi;
48    Background         *bg;
49    ImageClass         *ic;
50    char                thumb[1024], buf[1024];
51
52    bg = BrackgroundCreateFromImage(bgid, file, thumb, sizeof(thumb));
53    if (!bg)
54       return NULL;
55
56    ic = ImageclassCreateSimple("`", thumb);
57
58    Esnprintf(buf, sizeof(buf), "bg use %s", bgid);
59
60    mi = MenuItemCreate(NULL, ic, buf, NULL);
61
62    return mi;
63 }
64
65 static const char  *
66 _dircache_filename(char *buf, unsigned int len, struct stat *st)
67 {
68    static const char   chmap[] =
69       "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
70    int                 aa, bb, cc;
71
72    aa = (int)st->st_ino;
73    bb = filedev_map((int)st->st_dev);
74    cc = (st->st_mtime > st->st_ctime) ? st->st_mtime : st->st_ctime;
75    Esnprintf(buf, len, ".%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
76              chmap[(aa >> 0) & 0x3f], chmap[(aa >> 6) & 0x3f],
77              chmap[(aa >> 12) & 0x3f], chmap[(aa >> 18) & 0x3f],
78              chmap[(aa >> 24) & 0x3f], chmap[(aa >> 28) & 0x3f],
79              chmap[(bb >> 0) & 0x3f], chmap[(bb >> 6) & 0x3f],
80              chmap[(bb >> 12) & 0x3f], chmap[(bb >> 18) & 0x3f],
81              chmap[(bb >> 24) & 0x3f], chmap[(bb >> 28) & 0x3f],
82              chmap[(cc >> 0) & 0x3f], chmap[(cc >> 6) & 0x3f],
83              chmap[(cc >> 12) & 0x3f], chmap[(cc >> 18) & 0x3f],
84              chmap[(cc >> 24) & 0x3f], chmap[(cc >> 28) & 0x3f]);
85
86    return buf;
87 }
88
89 static int
90 MenuLoadFromDirectory(Menu * m)
91 {
92    Progressbar        *p = NULL;
93    Menu               *mm;
94    int                 i, num, len;
95    const char         *dir;
96    char              **list, s[4096], ss[4096], cs[4096];
97    const char         *ext;
98    MenuItem           *mi;
99    struct stat         st;
100    FILE               *f;
101    time_t              lastmod;
102
103    dir = MenuGetData(m);
104    lastmod = moddate(dir);
105    if (!menu_scan_recursive && lastmod <= MenuGetTimestamp(m))
106       return 0;
107    MenuSetTimestamp(m, lastmod);
108
109    MenuEmpty(m, 0);
110
111    if (stat(dir, &st) < 0)
112       return 1;
113
114    if (Mode.backgrounds.force_scan)
115       goto skip_dir_cache;
116
117    Esnprintf(cs, sizeof(cs), "%s/cached/img/%s",
118              EDirUserCache(), _dircache_filename(ss, sizeof(ss), &st));
119    if (exists(cs))
120      {
121         /* cached dir listing - use it */
122
123         f = fopen(cs, "r");
124         if (!f)
125            return 1;
126         while (fgets(s, sizeof(s), f))
127           {
128              char                s2[4096];
129
130              s[strlen(s) - 1] = 0;
131              len = 0;
132              sscanf(s, "%1000s %1000s %n", ss, s2, &len);
133              if (!strcmp(ss, "BG"))
134                {
135                   Esnprintf(ss, sizeof(ss), "%s/%s", dir, s2);
136                   mi = MenuItemCreateFromBackground(s + len, ss);
137                   MenuAddItem(m, mi);
138                }
139              else if (!strcmp(ss, "EXE"))
140                {
141                   Esnprintf(ss, sizeof(ss), "exec %s/%s", dir, s2);
142                   mi = MenuItemCreate(NULL, NULL, ss, NULL);
143                   MenuAddItem(m, mi);
144                }
145              else if (!strcmp(ss, "DIR"))
146                {
147                   Esnprintf(ss, sizeof(ss), "%s/%s", dir, s2);
148                   mm = MenuCreateFromDirectory(ss, m, NULL, ss);
149                   mi = MenuItemCreate(s2, NULL, NULL, mm);
150                   MenuAddItem(m, mi);
151                }
152           }
153         fclose(f);
154         return 1;
155      }
156
157  skip_dir_cache:
158    Esnprintf(s, sizeof(s), "Scanning %s", dir);
159
160    p = ProgressbarCreate(s, 600, 16);
161    if (p)
162       ProgressbarShow(p);
163
164    f = fopen(cs, "w");
165
166    list = E_ls(dir, &num);
167    for (i = 0; i < num; i++)
168      {
169         if (p)
170            ProgressbarSet(p, (i * 100) / num);
171         Esnprintf(ss, sizeof(ss), "%s/%s", dir, list[i]);
172         /* skip "dot" files and dirs - senisble */
173         if ((*(list[i]) == '.') || (stat(ss, &st) < 0))
174            continue;
175
176         ext = fileext(ss);
177         if (S_ISDIR(st.st_mode))
178           {
179              /* Submenu */
180              mm = MenuCreateFromDirectory(ss, m, NULL, ss);
181              mi = MenuItemCreate(list[i], NULL, NULL, mm);
182              MenuAddItem(m, mi);
183              if (f)
184                 fprintf(f, "DIR %s\n", list[i]);
185           }
186 #if 0
187         else if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
188           {
189              /* Executable */
190              /* that's it - people are stupid and have executable images and just */
191              /* don't get it - so I'm disablign this to save people from their own */
192              /* stupidity */
193              mi = MenuItemCreate(list[i], NULL, ss, NULL);
194              MenuAddItem(m, mi);
195              if (f)
196                 fprintf(f, "EXE %s\n", list[i]);
197           }
198 #endif
199         else if (_ext_is_imagetype(ext))
200           {
201              /* Background */
202              char                s3[512];
203
204              _dircache_filename(s3, sizeof(s3), &st);
205
206              mi = MenuItemCreateFromBackground(s3, ss);
207              if (mi)
208                {
209                   MenuAddItem(m, mi);
210
211                   if (f)
212                      fprintf(f, "BG %s %s\n", list[i], s3);
213                }
214           }
215      }
216    if (f)
217       fclose(f);
218    if (p)
219       ProgressbarDestroy(p);
220    if (list)
221       StrlistFree(list, num);
222
223    return 1;
224 }
225
226 static Menu        *
227 MenuCreateFromDirectory(const char *name, Menu * parent, MenuStyle * ms,
228                         const char *dir)
229 {
230    static int          calls = 0;
231    Menu               *m;
232
233    if (calls > 32)
234       return NULL;
235    calls++;
236
237    m = MenuCreate(name, NULL, parent, ms);
238    MenuSetData(m, Estrdup(dir));
239    MenuSetLoader(m, MenuLoadFromDirectory);
240
241    if (menu_scan_recursive)
242       MenuLoadFromDirectory(m);
243
244    calls--;
245
246    return m;
247 }
248
249 void
250 ScanBackgroundMenu(void)
251 {
252    menu_scan_recursive = 1;
253    MenuLoad(MenuFind("BACKGROUNDS_MENU", NULL));
254    Mode.backgrounds.force_scan = 0;
255    menu_scan_recursive = 0;
256 }
257
258 static void
259 FillFlatFileMenu(Menu * m, const char *file)
260 {
261    FILE               *f;
262    char                first = 1;
263    char                s[4096];
264    unsigned int        len;
265
266    f = fopen(file, "r");
267    if (!f)
268      {
269         Eprintf("Unable to open menu file %s -- %s\n", file, strerror(errno));
270         return;
271      }
272
273    MenuSetIconSize(m, 0);       /* Scale to default */
274
275    while (fgets(s, 4096, f))
276      {
277         if (s[0] == '#')
278            continue;
279         len = strlen(s);
280         while (len && (s[len - 1] == '\n' || s[len - 1] == '\r'))
281            len--;
282         if (len == 0)
283            continue;
284         s[len] = '\0';
285
286         if (first)
287           {
288              char               *title, *style, *alias;
289
290              first = 0;
291
292              title = style = alias = NULL;
293              parse(s, "%S%S%S", &title, &style, &alias);
294
295              if (title)
296                 MenuSetTitle(m, title);
297              if (!style)
298                 style = (char *)"ROOT";
299              MenuSetStyle(m, MenuStyleFind(style));
300              if (alias)
301                 MenuSetAlias(m, alias);
302           }
303         else
304           {
305              char               *txt, *icon, *act, *params;
306              char                wd[4096];
307              MenuItem           *mi;
308              ImageClass         *icc = NULL;
309              Menu               *mm;
310
311              txt = icon = act = params = NULL;
312              parse(s, "%S%T%S%S", &txt, &icon, &act, &params);
313
314              if (icon && exists(icon))
315                {
316                   Esnprintf(wd, sizeof(wd), "__FM.%s", icon);
317                   icc = ImageclassFind(wd, 0);
318                   if (!icc)
319                      icc = ImageclassCreateSimple(wd, icon);
320                }
321              if ((act) && (!strcmp(act, "exec")) && (params))
322                {
323                   sscanf(params, "%4000s", wd);
324                   if (path_canexec(wd))
325                     {
326                        Esnprintf(wd, sizeof(wd), "exec %s", params);
327                        mi = MenuItemCreate(txt, icc, wd, NULL);
328                        MenuAddItem(m, mi);
329                     }
330                }
331              else if ((act) && (!strcmp(act, "menu")) && (params))
332                {
333                   mm = MenuFind(params, NULL);
334                   if (mm)
335                     {
336                        mi = MenuItemCreate(txt, icc, NULL, mm);
337                        MenuAddItem(m, mi);
338                     }
339                }
340              else if (act)
341                {
342                   mi = MenuItemCreate(txt, icc, act, NULL);
343                   MenuAddItem(m, mi);
344                }
345           }
346      }
347    fclose(f);
348 }
349
350 static int
351 MenuLoadFromFlatFile(Menu * m)
352 {
353    const char         *ff;
354    time_t              lastmod;
355
356    ff = MenuGetData(m);
357    lastmod = moddate(ff);
358    if (lastmod <= MenuGetTimestamp(m))
359       return 0;
360    MenuSetTimestamp(m, lastmod);
361
362    MenuEmpty(m, 0);
363    FillFlatFileMenu(m, ff);
364
365    return 1;
366 }
367
368 static Menu        *
369 MenuCreateFromFlatFile(const char *name, Menu * parent, MenuStyle * ms,
370                        const char *file)
371 {
372    Menu               *m = NULL;
373    char               *ff, buf[4096];
374    static int          calls = 0;
375
376    if (calls > 32)
377       return NULL;
378    calls++;
379
380    if (!file)
381       file = name;
382
383    if (isabspath(file))
384      {
385         ff = FindFile(file, NULL);
386      }
387    else
388      {
389         /* Check menus subdir first */
390         Esnprintf(buf, sizeof(buf), "menus/%s", file);
391         ff = FindFile(buf, NULL);
392         if (!ff)
393            ff = FindFile(file, NULL);
394      }
395    if (!ff)
396       goto done;
397
398    m = MenuCreate(file, NULL, parent, ms);
399    if (name != file)
400       MenuSetAlias(m, name);
401    MenuSetData(m, ff);
402    MenuSetLoader(m, MenuLoadFromFlatFile);
403
404  done:
405    calls--;
406
407    return m;
408 }
409
410 static int
411 MenuLoadFromThemes(Menu * m)
412 {
413    char              **lst;
414    int                 i, num;
415    char                ss[4096], *s;
416    MenuItem           *mi;
417
418    if (MenuGetTimestamp(m))
419       return 0;
420    MenuSetTimestamp(m, 1);
421
422    lst = ThemesList(&num);
423    for (i = 0; i < num; i++)
424      {
425         s = ThemePathName(lst[i]);
426         Esnprintf(ss, sizeof(ss), "theme use %s", s);
427         mi = MenuItemCreate(s, NULL, ss, NULL);
428         MenuAddItem(m, mi);
429         Efree(s);
430      }
431    if (lst)
432       StrlistFree(lst, i);
433
434    return 1;
435 }
436
437 static Menu        *
438 MenuCreateFromThemes(const char *name, MenuStyle * ms)
439 {
440    Menu               *m;
441
442    m = MenuCreate(name, NULL, NULL, ms);
443    MenuSetTitle(m, _("Themes"));
444    MenuSetInternal(m);
445    MenuSetLoader(m, MenuLoadFromThemes);
446
447    return m;
448 }
449
450 static int
451 BorderNameCompare(void *b1, void *b2)
452 {
453    if (b1 && b2)
454       return strcmp(BorderGetName((Border *) b1), BorderGetName((Border *) b2));
455
456    return 0;
457 }
458
459 static Menu        *
460 MenuCreateFromBorders(const char *name, MenuStyle * ms)
461 {
462    char                s[128];
463    Menu               *m;
464    Border            **lst;
465    int                 i, num;
466    MenuItem           *mi;
467
468    m = MenuCreate(name, NULL, NULL, ms);
469    MenuSetTitle(m, _("Border"));
470
471    lst = BordersGetList(&num);
472    if (lst)
473       Quicksort((void **)lst, 0, num - 1, BorderNameCompare);
474    for (i = 0; i < num; i++)
475      {
476         /* if its not internal (ie doesnt start with _ ) */
477         if (lst[i]->name[0] != '_')
478           {
479              Esnprintf(s, sizeof(s), "wop * bo %s", lst[i]->name);
480              mi = MenuItemCreate(lst[i]->name, NULL, s, NULL);
481              MenuAddItem(m, mi);
482           }
483      }
484    Efree(lst);
485
486    return m;
487 }
488
489 static int
490 MenuCheckShowEwinDesk(EWin * ewin, void *prm)
491 {
492    if (!EwinGetTitle(ewin) || ewin->props.skip_winlist)
493       return 0;
494    return prm == NULL || EwinGetDesk(ewin) == prm;
495 }
496
497 static void
498 MenuLoadFromEwins(Menu * m, int (*f) (EWin * ewin, void *prm), void *prm)
499 {
500    EWin               *const *lst;
501    int                 i, num;
502    char                s[256];
503    MenuItem           *mi;
504
505    lst = EwinListGetAll(&num);
506    for (i = 0; i < num; i++)
507      {
508         if (!f(lst[i], prm))
509            continue;
510
511         Esnprintf(s, sizeof(s), "wop %#lx focus", EwinGetClientXwin(lst[i]));
512         mi = MenuItemCreate(EwinGetTitle(lst[i]), NULL, s, NULL);
513         MenuAddItem(m, mi);
514      }
515 }
516
517 static int
518 MenuLoadFromAllEwins(Menu * m)
519 {
520    MenuEmpty(m, 0);
521    MenuLoadFromEwins(m, MenuCheckShowEwinDesk, NULL);
522    return 1;
523 }
524
525 static Menu        *
526 MenuCreateFromAllEWins(const char *name, MenuStyle * ms)
527 {
528    Menu               *m;
529
530    m = MenuCreate(name, NULL, NULL, ms);
531    MenuSetTitle(m, _("Window List"));
532    MenuSetInternal(m);
533    MenuSetDynamic(m);
534    MenuSetLoader(m, MenuLoadFromAllEwins);
535
536    return m;
537 }
538
539 static int
540 MenuLoadFromDesktops(Menu * m)
541 {
542    Menu               *mm;
543    unsigned int        i;
544    char                s[256];
545    MenuItem           *mi;
546
547    MenuEmpty(m, 0);
548
549    for (i = 0; i < DesksGetNumber(); i++)
550      {
551         mm = MenuCreate("__SUBMENUDESK_E", NULL, m, NULL);
552         Esnprintf(s, sizeof(s), "desk goto %i", i);
553         mi = MenuItemCreate(_("Go to this Desktop"), NULL, s, NULL);
554         MenuAddItem(mm, mi);
555         MenuLoadFromEwins(mm, MenuCheckShowEwinDesk, DeskGet(i));
556
557         Esnprintf(s, sizeof(s), _("Desktop %i"), i);
558         mi = MenuItemCreate(s, NULL, NULL, mm);
559         MenuAddItem(m, mi);
560      }
561
562    return 1;
563 }
564
565 static Menu        *
566 MenuCreateFromDesktops(const char *name, MenuStyle * ms)
567 {
568    Menu               *m;
569
570    m = MenuCreate(name, NULL, NULL, ms);
571    MenuSetTitle(m, _("Desks"));
572    MenuSetInternal(m);
573    MenuSetDynamic(m);
574    MenuSetLoader(m, MenuLoadFromDesktops);
575
576    return m;
577 }
578
579 static int
580 MenuLoadFromGroups(Menu * m)
581 {
582    Menu               *mm;
583    Group             **lst;
584    int                 i, j, num;
585    char                s[256];
586    MenuItem           *mi;
587
588    MenuEmpty(m, 0);
589
590    lst = GroupsGetList(&num);
591    if (!lst)
592       return 1;
593
594    for (i = 0; i < num; i++)
595      {
596         mm = MenuCreate("__SUBMENUGROUP_E", NULL, m, NULL);
597
598         Esnprintf(s, sizeof(s), "gop %li showhide",
599                   EwinGetClientXwin(lst[i]->members[0]));
600         mi = MenuItemCreate(_("Show/Hide this group"), NULL, s, NULL);
601
602         Esnprintf(s, sizeof(s), "wop %#lx ic",
603                   EwinGetClientXwin(lst[i]->members[0]));
604         MenuAddItem(mm, mi);
605         mi = MenuItemCreate(_("Iconify this group"), NULL, s, NULL);
606         MenuAddItem(mm, mi);
607
608         for (j = 0; j < lst[i]->num_members; j++)
609           {
610              Esnprintf(s, sizeof(s), "wop %#lx focus",
611                        EwinGetClientXwin(lst[i]->members[j]));
612              mi = MenuItemCreate(EwinGetTitle(lst[i]->members[j]), NULL,
613                                  s, NULL);
614              MenuAddItem(mm, mi);
615           }
616         Esnprintf(s, sizeof(s), _("Group %i"), i);
617         mi = MenuItemCreate(s, NULL, NULL, mm);
618         MenuAddItem(m, mi);
619      }
620    Efree(lst);
621
622    return 1;
623 }
624
625 static Menu        *
626 MenuCreateFromGroups(const char *name, MenuStyle * ms)
627 {
628    Menu               *m;
629
630    m = MenuCreate(name, NULL, NULL, ms);
631    MenuSetTitle(m, _("Groups"));
632    MenuSetInternal(m);
633    MenuSetDynamic(m);
634    MenuSetLoader(m, MenuLoadFromGroups);
635
636    return m;
637 }
638
639 #if 0                           /* Not finished */
640 Menu               *
641 MenuCreateMoveToDesktop(char *name, Menu * parent, MenuStyle * ms)
642 {
643    Menu               *m;
644    int                 i;
645    char                s1[256], s2[256];
646
647    MenuItem           *mi;
648
649    m = MenuCreate(name, NULL, parent, ms);
650
651    for (i = 0; i < Mode.numdesktops; i++)
652      {
653         Esnprintf(s1, sizeof(s1), _("Desktop %i"), i);
654         Esnprintf(s2, sizeof(s2), "%i", i);
655         mi = MenuItemCreate(s1, NULL, s2, NULL);
656         MenuAddItem(m, mi);
657      }
658
659    return m;
660 }
661 #endif
662
663 Menu               *
664 MenusCreateInternal(const char *type, const char *name, const char *style,
665                     const char *prm)
666 {
667    Menu               *m;
668    MenuStyle          *ms;
669
670    m = NULL;
671    ms = NULL;
672    if (style)
673       ms = MenuStyleFind(style);
674
675    if (!type)
676      {
677         if (!strstr(name, ".menu"))
678            type = name;
679      }
680
681    if (!type || !strcmp(type, "file"))
682      {
683         m = MenuCreateFromFlatFile(name, NULL, ms, prm);
684      }
685    else if (!strcmp(type, "dirscan"))
686      {
687         SoundPlay(SOUND_SCANNING);
688         m = MenuCreateFromDirectory(name, NULL, ms, prm);
689      }
690    else if (!strcmp(type, "borders"))
691      {
692         m = MenuCreateFromBorders(name, ms);
693      }
694    else if (!strcmp(type, "themes"))
695      {
696         m = MenuCreateFromThemes(name, ms);
697      }
698    else if (!strcmp(type, "windowlist"))
699      {
700         m = MenuCreateFromAllEWins(name, ms);
701      }
702    else if (!strcmp(type, "deskmenu"))
703      {
704         m = MenuCreateFromDesktops(name, ms);
705      }
706    else if (!strcmp(type, "groupmenu"))
707      {
708         m = MenuCreateFromGroups(name, ms);
709      }
710
711    return m;
712 }
713
714 static int
715 _ext_is_imagetype(const char *ext)
716 {
717    static const char  *const exts[] = {
718       "jpg", "jpeg", "gif", "png", "tif", "tiff",
719       "xpm", "ppm", "pgm", "pnm", "bmp", NULL
720    };
721    int                 i;
722
723    for (i = 0; exts[i]; i++)
724       if (!Estrcasecmp(exts[i], ext))
725          return 1;
726
727    return 0;
728 }