Add OPEN_PLMENU option to parse command generated proplist style menus
[wmaker-crm.git] / src / rootmenu.c
blob5b8ff9055acaa54e5a62c4d4d7f590c707af9687
1 /* rootmenu.c- user defined menu
2  *
3  *  Window Maker window manager
4  *
5  *  Copyright (c) 1997-2003 Alfredo K. Kojima
6  *  Copyright (c) 1998-2003 Dan Pascu
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License along
19  *  with this program; if not, write to the Free Software Foundation, Inc.,
20  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
23 #include "wconfig.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <sys/types.h>
31 #include <string.h>
32 #include <strings.h>
33 #include <ctype.h>
34 #include <time.h>
35 #include <dirent.h>
37 #include <X11/Xlib.h>
38 #include <X11/Xutil.h>
39 #include <X11/Xatom.h>
41 #include "WindowMaker.h"
42 #include "actions.h"
43 #include "menu.h"
44 #include "funcs.h"
45 #include "main.h"
46 #include "dialog.h"
47 #include "keybind.h"
48 #include "stacking.h"
49 #include "workspace.h"
50 #include "defaults.h"
51 #include "framewin.h"
52 #include "session.h"
53 #include "shutdown.h"
54 #include "xmodifier.h"
56 #include <WINGs/WUtil.h>
58 #define MAX_SHORTCUT_LENGTH 32
60 extern char *Locale;
61 extern WDDomain *WDRootMenu;
62 extern Cursor wCursor[WCUR_LAST];
63 extern WPreferences wPreferences;
65 static WMenu *readMenuPipe(WScreen * scr, char **file_name);
66 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name);
67 static WMenu *readMenuFile(WScreen * scr, char *file_name);
68 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **file_name, char *command);
69 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals);
70 static void menu_parser_register_macros(WMenuParser parser);
72 typedef struct Shortcut {
73         struct Shortcut *next;
75         int modifier;
76         KeyCode keycode;
77         WMenuEntry *entry;
78         WMenu *menu;
79 } Shortcut;
81 static Shortcut *shortcutList = NULL;
84  * Syntax:
85  * # main menu
86  * "Menu Name" MENU
87  *      "Title" EXEC command_to_exec -params
88  *      "Submenu" MENU
89  *              "Title" EXEC command_to_exec -params
90  *      "Submenu" END
91  *      "Workspaces" WORKSPACE_MENU
92  *      "Title" built_in_command
93  *      "Quit" EXIT
94  *      "Quick Quit" EXIT QUICK
95  * "Menu Name" END
96  *
97  * Commands may be preceded by SHORTCUT key
98  *
99  * Built-in commands:
101  * INFO_PANEL - shows the Info Panel
102  * LEGAL_PANEL - shows the Legal info panel
103  * SHUTDOWN [QUICK] - closes the X server [without confirmation]
104  * REFRESH - forces the desktop to be repainted
105  * EXIT [QUICK] - exit the window manager [without confirmation]
106  * EXEC <program> - execute an external program
107  * SHEXEC <command> - execute a shell command
108  * WORKSPACE_MENU - places the workspace submenu
109  * ARRANGE_ICONS
110  * RESTART [<window manager>] - restarts the window manager
111  * SHOW_ALL - unhide all windows on workspace
112  * HIDE_OTHERS - hides all windows excep the focused one
113  * OPEN_MENU file - read menu data from file which must be a valid menu file.
114  * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
115  *              - read menu data from directory(ies) and
116  *                eventually precede each with a command.
117  * OPEN_MENU | command
118  *              - opens command and uses its stdout to construct and insert
119  *                the resulting menu in current position. The output of
120  *                command must be a valid menu description.
121  *                The space between '|' and command is optional.
122  *                || will do the same, but will not cache the contents.
123  * OPEN_PLMENU | command
124  *              - opens command and uses its stdout which must be in proplist
125  *                fromat to construct and insert the resulting menu in current
126  *                position.
127  *                The space between '|' and command is optional.
128  *                || will do the same, but will not cache the contents.
129  * SAVE_SESSION - saves the current state of the desktop, which include
130  *                all running applications, all their hints (geometry,
131  *                position on screen, workspace they live on, the dock
132  *                or clip from where they were launched, and
133  *                if minimized, shaded or hidden. Also saves the current
134  *                workspace the user is on. All will be restored on every
135  *                start of windowmaker until another SAVE_SESSION or
136  *                CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
137  *                WindowMaker domain file, then saving is automatically
138  *                done on every windowmaker exit, overwriting any
139  *                SAVE_SESSION or CLEAR_SESSION (see below). Also save
140  *                dock state now.
141  * CLEAR_SESSION - clears any previous saved session. This will not have
142  *                any effect if SaveSessionOnExit is True.
144  */
146 #define M_QUICK         1
148 /* menu commands */
150 static void execCommand(WMenu * menu, WMenuEntry * entry)
152         char *cmdline;
154         cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
156         XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
157                      GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);
158         XSync(dpy, 0);
160         if (cmdline) {
161                 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
162                 wfree(cmdline);
163         }
164         XUngrabPointer(dpy, CurrentTime);
165         XSync(dpy, 0);
168 static void exitCommand(WMenu * menu, WMenuEntry * entry)
170         static int inside = 0;
171         int result;
173         /* prevent reentrant calls */
174         if (inside)
175                 return;
176         inside = 1;
178 #define R_CANCEL 0
179 #define R_EXIT   1
181         result = R_CANCEL;
183         if ((long)entry->clientdata == M_QUICK) {
184                 result = R_EXIT;
185         } else {
186                 int r, oldSaveSessionFlag;
188                 oldSaveSessionFlag = wPreferences.save_session_on_exit;
189                 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
190                                 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
192                 if (r == WAPRDefault) {
193                         result = R_EXIT;
194                 } else if (r == WAPRAlternate) {
195                         /* Don't modify the "save session on exit" flag if the
196                          * user canceled the operation. */
197                         wPreferences.save_session_on_exit = oldSaveSessionFlag;
198                 }
199         }
200         if (result == R_EXIT)
201                 Shutdown(WSExitMode);
203 #undef R_EXIT
204 #undef R_CANCEL
205         inside = 0;
208 static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
210         static int inside = 0;
211         int result;
213         /* prevent reentrant calls */
214         if (inside)
215                 return;
216         inside = 1;
218 #define R_CANCEL 0
219 #define R_CLOSE 1
220 #define R_KILL 2
222         result = R_CANCEL;
223         if ((long)entry->clientdata == M_QUICK)
224                 result = R_CLOSE;
225         else {
226                 int r, oldSaveSessionFlag;
228                 oldSaveSessionFlag = wPreferences.save_session_on_exit;
230                 r = wExitDialog(menu->frame->screen_ptr,
231                                 _("Kill X session"),
232                                 _("Kill Window System session?\n"
233                                   "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
234                 if (r == WAPRDefault) {
235                         result = R_KILL;
236                 } else if (r == WAPRAlternate) {
237                         /* Don't modify the "save session on exit" flag if the
238                          * user canceled the operation. */
239                         wPreferences.save_session_on_exit = oldSaveSessionFlag;
240                 }
241         }
243         if (result != R_CANCEL) {
244                 Shutdown(WSKillMode);
245         }
246 #undef R_CLOSE
247 #undef R_CANCEL
248 #undef R_KILL
249         inside = 0;
252 static void restartCommand(WMenu * menu, WMenuEntry * entry)
254         Shutdown(WSRestartPreparationMode);
255         Restart((char *)entry->clientdata, False);
256         Restart(NULL, True);
259 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
261         wRefreshDesktop(menu->frame->screen_ptr);
264 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
266         wArrangeIcons(menu->frame->screen_ptr, True);
269 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
271         wShowAllWindows(menu->frame->screen_ptr);
274 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
276         wHideOtherApplications(menu->frame->screen_ptr->focused_window);
279 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
281         if (!wPreferences.save_session_on_exit)
282                 wSessionSaveState(menu->frame->screen_ptr);
284         wScreenSaveState(menu->frame->screen_ptr);
287 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
289         wSessionClearState(menu->frame->screen_ptr);
290         wScreenSaveState(menu->frame->screen_ptr);
293 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
295         wShowInfoPanel(menu->frame->screen_ptr);
298 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
300         wShowLegalPanel(menu->frame->screen_ptr);
303 /********************************************************************/
305 static char * getLocalizedMenuFile(char *menu)
307         char *buffer, *ptr, *locale;
308         int len;
310         if (!Locale)
311                 return NULL;
313         len = strlen(menu) + strlen(Locale) + 8;
314         buffer = wmalloc(len);
316         /* try menu.locale_name */
317         snprintf(buffer, len, "%s.%s", menu, Locale);
318         if (access(buffer, F_OK) == 0)
319                 return buffer;
321         /* position of locale in our buffer */
322         locale = buffer + strlen(menu) + 1;
324         /* check if it is in the form aa_bb.encoding and check for aa_bb */
325         ptr = strchr(locale, '.');
326         if (ptr) {
327                 *ptr = 0;
328                 if (access(buffer, F_OK) == 0)
329                         return buffer;
330         }
332         /* now check for aa */
333         ptr = strchr(locale, '_');
334         if (ptr) {
335                 *ptr = 0;
336                 if (access(buffer, F_OK) == 0)
337                         return buffer;
338         }
340         wfree(buffer);
342         return NULL;
345 Bool wRootMenuPerformShortcut(XEvent * event)
347         WScreen *scr = wScreenForRootWindow(event->xkey.root);
348         Shortcut *ptr;
349         int modifiers;
350         int done = 0;
352         /* ignore CapsLock */
353         modifiers = event->xkey.state & ValidModMask;
355         for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
356                 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
357                         continue;
359                 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
360                         (*ptr->entry->callback) (ptr->menu, ptr->entry);
361                         done = True;
362                 }
363         }
365         return done;
368 void wRootMenuBindShortcuts(Window window)
370         Shortcut *ptr;
372         ptr = shortcutList;
373         while (ptr) {
374                 if (ptr->modifier != AnyModifier) {
375                         XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
376                                  window, True, GrabModeAsync, GrabModeAsync);
377 #ifdef NUMLOCK_HACK
378                         wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
379 #endif
380                 }
381                 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
382                 ptr = ptr->next;
383         }
386 static void rebindKeygrabs(WScreen * scr)
388         WWindow *wwin;
390         wwin = scr->focused_window;
392         while (wwin != NULL) {
393                 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
395                 if (!WFLAGP(wwin, no_bind_keys)) {
396                         wWindowSetKeyGrabs(wwin);
397                 }
398                 wwin = wwin->prev;
399         }
402 static void removeShortcutsForMenu(WMenu * menu)
404         Shortcut *ptr, *tmp;
405         Shortcut *newList = NULL;
407         ptr = shortcutList;
408         while (ptr != NULL) {
409                 tmp = ptr->next;
410                 if (ptr->menu == menu) {
411                         wfree(ptr);
412                 } else {
413                         ptr->next = newList;
414                         newList = ptr;
415                 }
416                 ptr = tmp;
417         }
418         shortcutList = newList;
419         menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
422 static Bool addShortcut(const char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
424         Shortcut *ptr;
425         KeySym ksym;
426         char *k;
427         char buf[MAX_SHORTCUT_LENGTH], *b;
429         ptr = wmalloc(sizeof(Shortcut));
431         wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
432         b = (char *)buf;
434         /* get modifiers */
435         ptr->modifier = 0;
436         while ((k = strchr(b, '+')) != NULL) {
437                 int mod;
439                 *k = 0;
440                 mod = wXModifierFromKey(b);
441                 if (mod < 0) {
442                         wwarning(_("%s: invalid key modifier \"%s\""), file, b);
443                         wfree(ptr);
444                         return False;
445                 }
446                 ptr->modifier |= mod;
448                 b = k + 1;
449         }
451         /* get key */
452         ksym = XStringToKeysym(b);
454         if (ksym == NoSymbol) {
455                 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
456                          file, shortcutDefinition, entry->text);
457                 wfree(ptr);
458                 return False;
459         }
461         ptr->keycode = XKeysymToKeycode(dpy, ksym);
462         if (ptr->keycode == 0) {
463                 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
464                          shortcutDefinition, entry->text);
465                 wfree(ptr);
466                 return False;
467         }
469         ptr->menu = menu;
470         ptr->entry = entry;
472         ptr->next = shortcutList;
473         shortcutList = ptr;
475         menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
477         return True;
480 static char *next_token(char *line, char **next)
482         char *tmp, c;
483         char *ret;
485         *next = NULL;
486         while (*line == ' ' || *line == '\t')
487                 line++;
489         tmp = line;
491         if (*tmp == '"') {
492                 tmp++;
493                 line++;
494                 while (*tmp != 0 && *tmp != '"')
495                         tmp++;
496                 if (*tmp != '"') {
497                         wwarning(_("%s: unmatched '\"' in menu file"), line);
498                         return NULL;
499                 }
500         } else {
501                 do {
502                         if (*tmp == '\\')
503                                 tmp++;
505                         if (*tmp != 0)
506                                 tmp++;
508                 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
509         }
511         c = *tmp;
512         *tmp = 0;
513         ret = wstrdup(line);
514         *tmp = c;
516         if (c == 0)
517                 return ret;
518         else
519                 tmp++;
521         /* skip blanks */
522         while (*tmp == ' ' || *tmp == '\t')
523                 tmp++;
525         if (*tmp != 0)
526                 *next = tmp;
528         return ret;
531 static void separateCommand(char *line, char ***file, char **command)
533         char *token, *tmp = line;
534         WMArray *array = WMCreateArray(4);
535         int count, i;
537         *file = NULL;
538         *command = NULL;
539         do {
540                 token = next_token(tmp, &tmp);
541                 if (token) {
542                         if (strcmp(token, "WITH") == 0) {
543                                 if (tmp != NULL && *tmp != 0)
544                                         *command = wstrdup(tmp);
545                                 else
546                                         wwarning(_("%s: missing command"), line);
547                                 break;
548                         }
549                         WMAddToArray(array, token);
550                 }
551         } while (token != NULL && tmp != NULL);
553         count = WMGetArrayItemCount(array);
554         if (count > 0) {
555                 *file = wmalloc(sizeof(char *) * (count + 1));
556                 (*file)[count] = NULL;
557                 for (i = 0; i < count; i++) {
558                         (*file)[i] = WMGetFromArray(array, i);
559                 }
560         }
561         WMFreeArray(array);
564 static WMenu *constructPLMenu(WScreen *screen, char *path)
566         WMPropList *pl = NULL;
567         WMenu *menu = NULL;
569         if (!path)
570                 return NULL;
572         pl = WMReadPropListFromFile(path);
573         if (!pl)
574                 return NULL;
576         menu = configureMenu(screen, pl, False);
577         if (!menu)
578                 return NULL;
580         menu->on_destroy = removeShortcutsForMenu;
581         return menu;
586 static void constructMenu(WMenu * menu, WMenuEntry * entry)
588         WMenu *submenu;
589         struct stat stat_buf;
590         char **path;
591         char *cmd;
592         char *lpath = NULL;
593         int i, first = -1;
594         time_t last = 0;
596         separateCommand((char *)entry->clientdata, &path, &cmd);
597         if (path == NULL || *path == NULL || **path == 0) {
598                 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
599                 if (cmd)
600                         wfree(cmd);
601                 return;
602         }
604         if (path[0][0] == '|') {
605                 /* pipe menu */
607                 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
608                         /* parse pipe */
610                         submenu = readMenuPipe(menu->frame->screen_ptr, path);
612                         if (submenu != NULL) {
613                                 if (path[0][1] == '|')
614                                         submenu->timestamp = 0;
615                                 else
616                                         submenu->timestamp = 1; /* there's no automatic reloading */
617                         }
618                 } else {
619                         submenu = NULL;
620                 }
622         } else {
624                 /* try interpreting path as a proplist file */
625                 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
626                 /* if unsuccessful, try it as an old-style file */
627                 if (!submenu) {
629                         i = 0;
630                         while (path[i] != NULL) {
631                                 char *tmp;
633                                 if (strcmp(path[i], "-noext") == 0) {
634                                         i++;
635                                         continue;
636                                 }
638                                 tmp = wexpandpath(path[i]);
639                                 wfree(path[i]);
640                                 lpath = getLocalizedMenuFile(tmp);
641                                 if (lpath) {
642                                         wfree(tmp);
643                                         path[i] = lpath;
644                                         lpath = NULL;
645                                 } else {
646                                         path[i] = tmp;
647                                 }
649                                 if (stat(path[i], &stat_buf) == 0) {
650                                         if (last < stat_buf.st_mtime)
651                                                 last = stat_buf.st_mtime;
652                                         if (first < 0)
653                                                 first = i;
654                                 } else {
655                                         werror(_("%s:could not stat menu"), path[i]);
656                                         /*goto finish; */
657                                 }
659                                 i++;
660                         }
662                         if (first < 0) {
663                                 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
664                                 goto finish;
665                         }
666                         stat(path[first], &stat_buf);
667                         if (!menu->cascades[entry->cascade]
668                                         || menu->cascades[entry->cascade]->timestamp < last) {
670                                 if (S_ISDIR(stat_buf.st_mode)) {
671                                         /* menu directory */
672                                         submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
673                                         if (submenu)
674                                                 submenu->timestamp = last;
675                                 } else if (S_ISREG(stat_buf.st_mode)) {
676                                         /* menu file */
678                                         if (cmd || path[1])
679                                                 wwarning(_("too many parameters in OPEN_MENU: %s"),
680                                                                 (char *)entry->clientdata);
682                                         submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
683                                         if (submenu)
684                                                 submenu->timestamp = stat_buf.st_mtime;
685                                 } else {
686                                         submenu = NULL;
687                                 }
688                         } else {
689                                 submenu = NULL;
690                         }
691                 }
692         }
694         if (submenu) {
695                 wMenuEntryRemoveCascade(menu, entry);
696                 wMenuEntrySetCascade(menu, entry, submenu);
697         }
699  finish:
700         i = 0;
701         while (path[i] != NULL)
702                 wfree(path[i++]);
703         wfree(path);
704         if (cmd)
705                 wfree(cmd);
708 static void constructPLMenuFromPipe(WMenu * menu, WMenuEntry * entry)
710         WMenu *submenu = NULL;
711         char **path;
712         char *cmd;
713         int i;
715         separateCommand((char *)entry->clientdata, &path, &cmd);
716         if (path == NULL || *path == NULL || **path == 0) {
717                 wwarning(_("invalid OPEN_PLMENU specification: %s"),
718                     (char *)entry->clientdata);
719                 if (cmd)
720                         wfree(cmd);
721                 return;
722         }
724         if (path[0][0] == '|') {
725                 /* pipe menu */
727                 if (!menu->cascades[entry->cascade]
728                 || menu->cascades[entry->cascade]->timestamp == 0) {
729                         /* parse pipe */
730                         submenu = readPLMenuPipe(menu->frame->screen_ptr, path);
732                         if (submenu != NULL) {
733                                 if (path[0][1] == '|')
734                                         submenu->timestamp = 0;
735                                 else
736                                         submenu->timestamp = 1; /* there's no automatic reloading */
737                         }
738                 }
739         }
741         if (submenu) {
742                 wMenuEntryRemoveCascade(menu, entry);
743                 wMenuEntrySetCascade(menu, entry, submenu);
744         }
746         i = 0;
747         while (path[i] != NULL)
748                 wfree(path[i++]);
750         wfree(path);
751         if (cmd)
752                 wfree(cmd);
755 static void cleanupWorkspaceMenu(WMenu * menu)
757         if (menu->frame->screen_ptr->workspace_menu == menu)
758                 menu->frame->screen_ptr->workspace_menu = NULL;
761 static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
763         WMenu *wsmenu;
764         WMenuEntry *entry;
766         if (scr->flags.added_workspace_menu) {
767                 wwarning(_
768                          ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
769                 return NULL;
770         } else {
771                 scr->flags.added_workspace_menu = 1;
773                 wsmenu = wWorkspaceMenuMake(scr, True);
774                 wsmenu->on_destroy = cleanupWorkspaceMenu;
776                 scr->workspace_menu = wsmenu;
777                 entry = wMenuAddCallback(menu, title, NULL, NULL);
778                 wMenuEntrySetCascade(menu, entry, wsmenu);
780                 wWorkspaceMenuUpdate(scr, wsmenu);
781         }
782         return entry;
785 static void cleanupWindowsMenu(WMenu * menu)
787         if (menu->frame->screen_ptr->switch_menu == menu)
788                 menu->frame->screen_ptr->switch_menu = NULL;
791 static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
793         WMenu *wwmenu;
794         WWindow *wwin;
795         WMenuEntry *entry;
797         if (scr->flags.added_windows_menu) {
798                 wwarning(_
799                          ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
800                 return NULL;
801         } else {
802                 scr->flags.added_windows_menu = 1;
804                 wwmenu = wMenuCreate(scr, _("Window List"), False);
805                 wwmenu->on_destroy = cleanupWindowsMenu;
806                 scr->switch_menu = wwmenu;
807                 wwin = scr->focused_window;
808                 while (wwin) {
809                         UpdateSwitchMenu(scr, wwin, ACTION_ADD);
811                         wwin = wwin->prev;
812                 }
813                 entry = wMenuAddCallback(menu, title, NULL, NULL);
814                 wMenuEntrySetCascade(menu, entry, wwmenu);
815         }
816         return entry;
819 static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
820                                 char *params, const char *file_name)
822         WScreen *scr;
823         WMenuEntry *entry = NULL;
824         Bool shortcutOk = False;
826         if (!menu)
827                 return NULL;
828         scr = menu->frame->screen_ptr;
829         if (strcmp(command, "OPEN_MENU") == 0) {
830                 if (!params) {
831                         wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
832                 } else {
833                         WMenu *dummy;
834                         char *path;
836                         path = wfindfile(DEF_CONFIG_PATHS, params);
837                         if (!path) {
838                                 path = wstrdup(params);
839                         }
840                         dummy = wMenuCreate(scr, title, False);
841                         dummy->on_destroy = removeShortcutsForMenu;
842                         entry = wMenuAddCallback(menu, title, constructMenu, path);
843                         entry->free_cdata = wfree;
844                         wMenuEntrySetCascade(menu, entry, dummy);
845                 }
846         } else if (strcmp(command, "OPEN_PLMENU") == 0) {
847                 if (!params) {
848                         wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
849                 } else {
850                         WMenu *dummy;
851                         char *path;
853                         path = wfindfile(DEF_CONFIG_PATHS, params);
854                         if (!path)
855                                 path = wstrdup(params);
857                         dummy = wMenuCreate(scr, title, False);
858                         dummy->on_destroy = removeShortcutsForMenu;
859                         entry = wMenuAddCallback(menu, title, constructPLMenuFromPipe, path);
860                         entry->free_cdata = wfree;
861                         wMenuEntrySetCascade(menu, entry, dummy);
862                 }
863         } else if (strcmp(command, "EXEC") == 0) {
864                 if (!params)
865                         wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
866                 else {
867                         entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
868                         entry->free_cdata = wfree;
869                         shortcutOk = True;
870                 }
871         } else if (strcmp(command, "SHEXEC") == 0) {
872                 if (!params)
873                         wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
874                 else {
875                         entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
876                         entry->free_cdata = wfree;
877                         shortcutOk = True;
878                 }
879         } else if (strcmp(command, "EXIT") == 0) {
881                 if (params && strcmp(params, "QUICK") == 0)
882                         entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
883                 else
884                         entry = wMenuAddCallback(menu, title, exitCommand, NULL);
886                 shortcutOk = True;
887         } else if (strcmp(command, "SHUTDOWN") == 0) {
889                 if (params && strcmp(params, "QUICK") == 0)
890                         entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
891                 else
892                         entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
894                 shortcutOk = True;
895         } else if (strcmp(command, "REFRESH") == 0) {
896                 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
898                 shortcutOk = True;
899         } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
900                 entry = addWorkspaceMenu(scr, menu, title);
902                 shortcutOk = True;
903         } else if (strcmp(command, "WINDOWS_MENU") == 0) {
904                 entry = addWindowsMenu(scr, menu, title);
906                 shortcutOk = True;
907         } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
908                 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
910                 shortcutOk = True;
911         } else if (strcmp(command, "HIDE_OTHERS") == 0) {
912                 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
914                 shortcutOk = True;
915         } else if (strcmp(command, "SHOW_ALL") == 0) {
916                 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
918                 shortcutOk = True;
919         } else if (strcmp(command, "RESTART") == 0) {
920                 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
921                 entry->free_cdata = wfree;
922                 shortcutOk = True;
923         } else if (strcmp(command, "SAVE_SESSION") == 0) {
924                 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
926                 shortcutOk = True;
927         } else if (strcmp(command, "CLEAR_SESSION") == 0) {
928                 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
929                 shortcutOk = True;
930         } else if (strcmp(command, "INFO_PANEL") == 0) {
931                 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
932                 shortcutOk = True;
933         } else if (strcmp(command, "LEGAL_PANEL") == 0) {
934                 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
935                 shortcutOk = True;
936         } else {
937                 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
939                 return NULL;
940         }
942         if (shortcut && entry) {
943                 if (!shortcutOk) {
944                         wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
945                 } else {
946                         if (addShortcut(file_name, shortcut, menu, entry)) {
948                                 entry->rtext = GetShortcutString(shortcut);
949                                 /*
950                                    entry->rtext = wstrdup(shortcut);
951                                  */
952                         }
953                 }
954         }
956         return entry;
959 /*******************   Menu Configuration From File   *******************/
961 static void freeline(char *title, char *command, char *parameter, char *shortcut)
963         wfree(title);
964         wfree(command);
965         wfree(parameter);
966         wfree(shortcut);
969 static WMenu *parseCascade(WScreen * scr, WMenu * menu, WMenuParser parser)
971         char *command, *params, *shortcut, *title;
973         while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
975                 if (command == NULL || !command[0]) {
976                         WMenuParserError(parser, _("missing command in menu config") );
977                         freeline(title, command, params, shortcut);
978                         goto error;
979                 }
981                 if (strcasecmp(command, "MENU") == 0) {
982                         WMenu *cascade;
984                         /* start submenu */
986                         cascade = wMenuCreate(scr, M_(title), False);
987                         cascade->on_destroy = removeShortcutsForMenu;
988                         if (!parseCascade(scr, cascade, parser)) {
989                                 wMenuDestroy(cascade, True);
990                         } else {
991                                 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
992                         }
993                 } else if (strcasecmp(command, "END") == 0) {
994                         /* end of menu */
995                         freeline(title, command, params, shortcut);
996                         return menu;
997                 } else {
998                         /* normal items */
999                         addMenuEntry(menu, M_(title), shortcut, command, params, WMenuParserGetFilename(parser));
1000                 }
1001                 freeline(title, command, params, shortcut);
1002         }
1004         WMenuParserError(parser, _("syntax error in menu file: END declaration missing") );
1006  error:
1007         return NULL;
1010 static WMenu *readMenuFile(WScreen * scr, char *file_name)
1012         WMenu *menu = NULL;
1013         FILE *file = NULL;
1014         WMenuParser parser;
1015         char *command, *params, *shortcut, *title;
1017         file = fopen(file_name, "rb");
1018         if (!file) {
1019                 werror(_("%s:could not open menu file"), file_name);
1020                 return NULL;
1021         }
1022         parser = WMenuParserCreate(file_name, file, DEF_CONFIG_PATHS);
1023         menu_parser_register_macros(parser);
1025         while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1027                 if (command == NULL || !command[0]) {
1028                         WMenuParserError(parser, _("missing command in menu config") );
1029                         freeline(title, command, params, shortcut);
1030                         break;
1031                 }
1032                 if (strcasecmp(command, "MENU") == 0) {
1033                         menu = wMenuCreate(scr, M_(title), True);
1034                         menu->on_destroy = removeShortcutsForMenu;
1035                         if (!parseCascade(scr, menu, parser)) {
1036                                 wMenuDestroy(menu, True);
1037                                 menu = NULL;
1038                         }
1039                         freeline(title, command, params, shortcut);
1040                         break;
1041                 } else {
1042                         WMenuParserError(parser, _("invalid menu file, MENU command is missing") );
1043                         freeline(title, command, params, shortcut);
1044                         break;
1045                 }
1046                 freeline(title, command, params, shortcut);
1047         }
1049         WMenuParserDelete(parser);
1050         fclose(file);
1052         return menu;
1055 /************    Menu Configuration From Pipe      *************/
1056 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name)
1058         WMPropList *plist = NULL;
1059         WMenu *menu = NULL;
1060         char *filename;
1061         char flat_file[MAXLINE];
1062         int i;
1064         flat_file[0] = '\0';
1066         for (i = 0; file_name[i] != NULL; i++) {
1067                 strcat(flat_file, file_name[i]);
1068                 strcat(flat_file, " ");
1069         }
1070         filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1072         plist = WMReadPropListFromPipe(filename);
1074         if (!plist)
1075                 return NULL;
1077         menu = configureMenu(scr, plist, False);
1078         if (!menu)
1079                 return NULL;
1081         menu->on_destroy = removeShortcutsForMenu;
1082         return menu;
1085 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1087         WMenu *menu = NULL;
1088         FILE *file = NULL;
1089         WMenuParser parser;
1090         char *command, *params, *shortcut, *title;
1091         char *filename;
1092         char flat_file[MAXLINE];
1093         int i;
1095         flat_file[0] = '\0';
1097         for (i = 0; file_name[i] != NULL; i++) {
1098                 strcat(flat_file, file_name[i]);
1099                 strcat(flat_file, " ");
1100         }
1101         filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1103         file = popen(filename, "r");
1104         if (!file) {
1105                 werror(_("%s:could not open menu file"), filename);
1106                 return NULL;
1107         }
1108         parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS);
1109         menu_parser_register_macros(parser);
1111         while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1113                 if (command == NULL || !command[0]) {
1114                         WMenuParserError(parser, _("missing command in menu config") );
1115                         freeline(title, command, params, shortcut);
1116                         break;
1117                 }
1118                 if (strcasecmp(command, "MENU") == 0) {
1119                         menu = wMenuCreate(scr, M_(title), True);
1120                         menu->on_destroy = removeShortcutsForMenu;
1121                         if (!parseCascade(scr, menu, parser)) {
1122                                 wMenuDestroy(menu, True);
1123                                 menu = NULL;
1124                         }
1125                         freeline(title, command, params, shortcut);
1126                         break;
1127                 } else {
1128                         WMenuParserError(parser, _("no title given for the root menu") );
1129                         freeline(title, command, params, shortcut);
1130                         break;
1131                 }
1133                 freeline(title, command, params, shortcut);
1134         }
1136         WMenuParserDelete(parser);
1137         pclose(file);
1139         return menu;
1142 typedef struct {
1143         char *name;
1144         int index;
1145 } dir_data;
1147 static int myCompare(const void *d1, const void *d2)
1149         dir_data *p1 = *(dir_data **) d1;
1150         dir_data *p2 = *(dir_data **) d2;
1152         return strcmp(p1->name, p2->name);
1155 /***** Preset some macro for file parser *****/
1156 static void menu_parser_register_macros(WMenuParser parser)
1158         Visual *visual;
1159         char buf[32];
1161         // Used to return CPP verion, now returns wmaker's version
1162         WMenuParserRegisterSimpleMacro(parser, "__VERSION__", VERSION);
1164         // All macros below were historically defined by WindowMaker
1165         visual = DefaultVisual(dpy, DefaultScreen(dpy));
1166         snprintf(buf, sizeof(buf), "%d", visual->class);
1167         WMenuParserRegisterSimpleMacro(parser, "VISUAL", buf);
1169         snprintf(buf, sizeof(buf), "%d", DefaultDepth(dpy, DefaultScreen(dpy)) );
1170         WMenuParserRegisterSimpleMacro(parser, "DEPTH", buf);
1172         snprintf(buf, sizeof(buf), "%d", WidthOfScreen(DefaultScreenOfDisplay(dpy)) );
1173         WMenuParserRegisterSimpleMacro(parser, "SCR_WIDTH", buf);
1175         snprintf(buf, sizeof(buf), "%d", HeightOfScreen(DefaultScreenOfDisplay(dpy)) );
1176         WMenuParserRegisterSimpleMacro(parser, "SCR_HEIGHT", buf);
1178         WMenuParserRegisterSimpleMacro(parser, "DISPLAY", XDisplayName(DisplayString(dpy)) );
1180         WMenuParserRegisterSimpleMacro(parser, "WM_VERSION", "\"" VERSION "\"");
1183 /************  Menu Configuration From Directory   *************/
1185 static Bool isFilePackage(char *file)
1187         int l;
1189         /* check if the extension indicates this file is a
1190          * file package. For now, only recognize .themed */
1192         l = strlen(file);
1194         if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1195                 return True;
1196         } else {
1197                 return False;
1198         }
1201 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
1203         DIR *dir;
1204         struct dirent *dentry;
1205         struct stat stat_buf;
1206         WMenu *menu = NULL;
1207         char *buffer;
1208         WMArray *dirs = NULL, *files = NULL;
1209         WMArrayIterator iter;
1210         int length, i, have_space = 0;
1211         dir_data *data;
1212         int stripExtension = 0;
1214         dirs = WMCreateArray(16);
1215         files = WMCreateArray(16);
1217         i = 0;
1218         while (path[i] != NULL) {
1219                 if (strcmp(path[i], "-noext") == 0) {
1220                         stripExtension = 1;
1221                         i++;
1222                         continue;
1223                 }
1225                 dir = opendir(path[i]);
1226                 if (!dir) {
1227                         i++;
1228                         continue;
1229                 }
1231                 while ((dentry = readdir(dir))) {
1233                         if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1234                                 continue;
1236                         if (dentry->d_name[0] == '.')
1237                                 continue;
1239                         buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1240                         if (!buffer) {
1241                                 werror(_("out of memory while constructing directory menu %s"), path[i]);
1242                                 break;
1243                         }
1245                         strcpy(buffer, path[i]);
1246                         strcat(buffer, "/");
1247                         strcat(buffer, dentry->d_name);
1249                         if (stat(buffer, &stat_buf) != 0) {
1250                                 werror(_("%s:could not stat file \"%s\" in menu directory"),
1251                                           path[i], dentry->d_name);
1252                         } else {
1253                                 Bool isFilePack = False;
1255                                 data = NULL;
1256                                 if (S_ISDIR(stat_buf.st_mode)
1257                                     && !(isFilePack = isFilePackage(dentry->d_name))) {
1259                                         /* access always returns success for user root */
1260                                         if (access(buffer, X_OK) == 0) {
1261                                                 /* Directory is accesible. Add to directory list */
1263                                                 data = (dir_data *) wmalloc(sizeof(dir_data));
1264                                                 data->name = wstrdup(dentry->d_name);
1265                                                 data->index = i;
1267                                                 WMAddToArray(dirs, data);
1268                                         }
1269                                 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1270                                         /* Hack because access always returns X_OK success for user root */
1271 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1272                                         if ((command != NULL && access(buffer, R_OK) == 0) ||
1273                                             (command == NULL && access(buffer, X_OK) == 0 &&
1274                                              (stat_buf.st_mode & S_IXANY))) {
1276                                                 data = (dir_data *) wmalloc(sizeof(dir_data));
1277                                                 data->name = wstrdup(dentry->d_name);
1278                                                 data->index = i;
1280                                                 WMAddToArray(files, data);
1281                                         }
1282                                 }
1283                         }
1284                         free(buffer);
1285                 }
1287                 closedir(dir);
1288                 i++;
1289         }
1291         if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1292                 WMFreeArray(dirs);
1293                 WMFreeArray(files);
1294                 return NULL;
1295         }
1297         WMSortArray(dirs, myCompare);
1298         WMSortArray(files, myCompare);
1300         menu = wMenuCreate(scr, M_(title), False);
1301         menu->on_destroy = removeShortcutsForMenu;
1303         WM_ITERATE_ARRAY(dirs, data, iter) {
1304                 /* New directory. Use same OPEN_MENU command that was used
1305                  * for the current directory. */
1306                 length = strlen(path[data->index]) + strlen(data->name) + 6;
1307                 if (stripExtension)
1308                         length += 7;
1309                 if (command)
1310                         length += strlen(command) + 6;
1311                 buffer = malloc(length);
1312                 if (!buffer) {
1313                         werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1314                         break;
1315                 }
1317                 buffer[0] = '\0';
1318                 if (stripExtension)
1319                         strcat(buffer, "-noext ");
1321                 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1323                 if (have_space)
1324                         strcat(buffer, "\"");
1325                 strcat(buffer, path[data->index]);
1327                 strcat(buffer, "/");
1328                 strcat(buffer, data->name);
1329                 if (have_space)
1330                         strcat(buffer, "\"");
1331                 if (command) {
1332                         strcat(buffer, " WITH ");
1333                         strcat(buffer, command);
1334                 }
1336                 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1338                 wfree(buffer);
1339                 if (data->name)
1340                         wfree(data->name);
1341                 wfree(data);
1342         }
1344         WM_ITERATE_ARRAY(files, data, iter) {
1345                 /* executable: add as entry */
1346                 length = strlen(path[data->index]) + strlen(data->name) + 6;
1347                 if (command)
1348                         length += strlen(command);
1350                 buffer = malloc(length);
1351                 if (!buffer) {
1352                         werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1353                         break;
1354                 }
1356                 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1357                 if (command != NULL) {
1358                         strcpy(buffer, command);
1359                         strcat(buffer, " ");
1360                         if (have_space)
1361                                 strcat(buffer, "\"");
1362                         strcat(buffer, path[data->index]);
1363                 } else {
1364                         if (have_space) {
1365                                 buffer[0] = '"';
1366                                 buffer[1] = 0;
1367                                 strcat(buffer, path[data->index]);
1368                         } else {
1369                                 strcpy(buffer, path[data->index]);
1370                         }
1371                 }
1372                 strcat(buffer, "/");
1373                 strcat(buffer, data->name);
1374                 if (have_space)
1375                         strcat(buffer, "\"");
1377                 if (stripExtension) {
1378                         char *ptr = strrchr(data->name, '.');
1379                         if (ptr && ptr != data->name)
1380                                 *ptr = 0;
1381                 }
1382                 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1384                 wfree(buffer);
1385                 if (data->name)
1386                         wfree(data->name);
1387                 wfree(data);
1388         }
1390         WMFreeArray(files);
1391         WMFreeArray(dirs);
1393         return menu;
1396 /************  Menu Configuration From WMRootMenu   *************/
1398 static WMenu *makeDefaultMenu(WScreen * scr)
1400         WMenu *menu = NULL;
1402         menu = wMenuCreate(scr, _("Commands"), True);
1403         wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1404         wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1405         wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1406         wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1407         return menu;
1411  *----------------------------------------------------------------------
1412  * configureMenu--
1413  *      Reads root menu configuration from defaults database.
1415  *----------------------------------------------------------------------
1416  */
1417 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1419         WMenu *menu = NULL;
1420         WMPropList *elem;
1421         int i, count;
1422         WMPropList *title, *command, *params;
1423         char *tmp, *mtitle;
1425         if (WMIsPLString(definition)) {
1426                 struct stat stat_buf;
1427                 char *path = NULL;
1428                 Bool menu_is_default = False;
1430                 /* menu definition is a string. Probably a path, so parse the file */
1432                 tmp = wexpandpath(WMGetFromPLString(definition));
1434                 path = getLocalizedMenuFile(tmp);
1436                 if (!path)
1437                         path = wfindfile(DEF_CONFIG_PATHS, tmp);
1439                 if (!path) {
1440                         path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1441                         menu_is_default = True;
1442                 }
1444                 if (!path) {
1445                         werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1446                         wfree(tmp);
1447                         return NULL;
1448                 }
1450                 if (stat(path, &stat_buf) < 0) {
1451                         werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1452                         wfree(path);
1453                         wfree(tmp);
1454                         return NULL;
1455                 }
1457                 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1458                     /* if the pointer in WMRootMenu has changed */
1459                     || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1461                         if (menu_is_default) {
1462                                 wwarning(_
1463                                          ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1464                                          path);
1465                         }
1467                         menu = readMenuFile(scr, path);
1468                         if (menu)
1469                                 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1470                 } else {
1471                         menu = NULL;
1472                 }
1473                 wfree(path);
1474                 wfree(tmp);
1476                 return menu;
1477         }
1479         count = WMGetPropListItemCount(definition);
1480         if (count == 0)
1481                 return NULL;
1483         elem = WMGetFromPLArray(definition, 0);
1484         if (!WMIsPLString(elem)) {
1485                 tmp = WMGetPropListDescription(elem, False);
1486                 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1487                 wfree(tmp);
1488                 return NULL;
1489         }
1490         mtitle = WMGetFromPLString(elem);
1492         menu = wMenuCreate(scr, M_(mtitle), False);
1493         menu->on_destroy = removeShortcutsForMenu;
1495 #ifdef GLOBAL_SUBMENU_FILE
1496         if (includeGlobals) {
1497                 WMenu *submenu;
1498                 WMenuEntry *mentry;
1500                 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1502                 if (submenu) {
1503                         mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1504                         wMenuEntrySetCascade(menu, mentry, submenu);
1505                 }
1506         }
1507 #endif
1509         for (i = 1; i < count; i++) {
1510                 elem = WMGetFromPLArray(definition, i);
1511 #if 0
1512                 if (WMIsPLString(elem)) {
1513                         char *file;
1515                         file = WMGetFromPLString(elem);
1517                 }
1518 #endif
1519                 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1520                         goto error;
1522                 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1523                         WMenu *submenu;
1524                         WMenuEntry *mentry;
1526                         /* submenu */
1527                         submenu = configureMenu(scr, elem, True);
1528                         if (submenu) {
1529                                 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1530                                 wMenuEntrySetCascade(menu, mentry, submenu);
1531                         }
1532                 } else {
1533                         int idx = 0;
1534                         WMPropList *shortcut;
1535                         /* normal entry */
1537                         title = WMGetFromPLArray(elem, idx++);
1538                         shortcut = WMGetFromPLArray(elem, idx++);
1539                         if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1540                                 shortcut = WMGetFromPLArray(elem, idx++);
1541                                 command = WMGetFromPLArray(elem, idx++);
1542                         } else {
1543                                 command = shortcut;
1544                                 shortcut = NULL;
1545                         }
1546                         params = WMGetFromPLArray(elem, idx++);
1548                         if (!title || !command)
1549                                 goto error;
1551                         addMenuEntry(menu, M_(WMGetFromPLString(title)),
1552                                      shortcut ? WMGetFromPLString(shortcut) : NULL,
1553                                      WMGetFromPLString(command),
1554                                      params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1555                 }
1556                 continue;
1558  error:
1559                 tmp = WMGetPropListDescription(elem, False);
1560                 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1561                 wfree(tmp);
1562         }
1564         return menu;
1568  *----------------------------------------------------------------------
1569  * OpenRootMenu--
1570  *      Opens the root menu, parsing the menu configuration from the
1571  * defaults database.
1572  *      If the menu is already mapped and is not sticked to the
1573  * root window, it will be unmapped.
1575  * Side effects:
1576  *      The menu may be remade.
1578  * Notes:
1579  * Construction of OPEN_MENU entries are delayed to the moment the
1580  * user map's them.
1581  *----------------------------------------------------------------------
1582  */
1583 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1585         WMenu *menu = NULL;
1586         WMPropList *definition;
1587         /*
1588            static WMPropList *domain=NULL;
1590            if (!domain) {
1591            domain = WMCreatePLString("WMRootMenu");
1592            }
1593          */
1595         scr->flags.root_menu_changed_shortcuts = 0;
1596         scr->flags.added_workspace_menu = 0;
1597         scr->flags.added_windows_menu = 0;
1599         if (scr->root_menu && scr->root_menu->flags.mapped) {
1600                 menu = scr->root_menu;
1601                 if (!menu->flags.buttoned) {
1602                         wMenuUnmap(menu);
1603                 } else {
1604                         wRaiseFrame(menu->frame->core);
1606                         if (keyboard)
1607                                 wMenuMapAt(menu, 0, 0, True);
1608                         else
1609                                 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1610                 }
1611                 return;
1612         }
1614         definition = WDRootMenu->dictionary;
1616         /*
1617            definition = PLGetDomain(domain);
1618          */
1619         if (definition) {
1620                 if (WMIsPLArray(definition)) {
1621                         if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1622                                 menu = configureMenu(scr, definition, True);
1623                                 if (menu)
1624                                         menu->timestamp = WDRootMenu->timestamp;
1626                         } else
1627                                 menu = NULL;
1628                 } else {
1629                         menu = configureMenu(scr, definition, True);
1630                 }
1631         }
1633         if (!menu) {
1634                 /* menu hasn't changed or could not be read */
1635                 if (!scr->root_menu) {
1636                         wMessageDialog(scr, _("Error"),
1637                                        _("The applications menu could not be loaded. "
1638                                          "Look at the console output for a detailed "
1639                                          "description of the errors."), _("OK"), NULL, NULL);
1641                         menu = makeDefaultMenu(scr);
1642                         scr->root_menu = menu;
1643                 }
1644                 menu = scr->root_menu;
1645         } else {
1646                 /* new root menu */
1647                 if (scr->root_menu) {
1648                         wMenuDestroy(scr->root_menu, True);
1649                 }
1650                 scr->root_menu = menu;
1651         }
1652         if (menu) {
1653                 int newx, newy;
1655                 if (keyboard && x == 0 && y == 0) {
1656                         newx = newy = 0;
1657                 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1658                         newx = x - menu->frame->core->width / 2;
1659                         newy = y - menu->frame->core->height / 2;
1660                 } else {
1661                         newx = x - menu->frame->core->width / 2;
1662                         newy = y;
1663                 }
1664                 wMenuMapAt(menu, newx, newy, keyboard);
1665         }
1667         if (scr->flags.root_menu_changed_shortcuts)
1668                 rebindKeygrabs(scr);