util: clarify a bit of the code for parsing commands in wmgenmenu
[wmaker-crm.git] / src / rootmenu.c
bloba922f5ea9bd922879398221f8d806dda2ad4910b
1 /* rootmenu.c- user defined menu
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
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.
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.
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.
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 "misc.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"
55 #include "rootmenu.h"
56 #include "startup.h"
57 #include "switchmenu.h"
59 #include <WINGs/WUtil.h>
61 #define MAX_SHORTCUT_LENGTH 32
64 static WMenu *readMenuPipe(WScreen * scr, char **file_name);
65 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name);
66 static WMenu *readMenuFile(WScreen *scr, const char *file_name);
67 static WMenu *readMenuDirectory(WScreen *scr, const char *title, char **file_name, const char *command);
68 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals);
69 static void menu_parser_register_macros(WMenuParser parser);
71 typedef struct Shortcut {
72 struct Shortcut *next;
74 int modifier;
75 KeyCode keycode;
76 WMenuEntry *entry;
77 WMenu *menu;
78 } Shortcut;
80 static Shortcut *shortcutList = NULL;
83 * Syntax:
84 * # main menu
85 * "Menu Name" MENU
86 * "Title" EXEC command_to_exec -params
87 * "Submenu" MENU
88 * "Title" EXEC command_to_exec -params
89 * "Submenu" END
90 * "Workspaces" WORKSPACE_MENU
91 * "Title" built_in_command
92 * "Quit" EXIT
93 * "Quick Quit" EXIT QUICK
94 * "Menu Name" END
96 * Commands may be preceded by SHORTCUT key
98 * Built-in commands:
100 * INFO_PANEL - shows the Info Panel
101 * LEGAL_PANEL - shows the Legal info panel
102 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
103 * REFRESH - forces the desktop to be repainted
104 * EXIT [QUICK] - exit the window manager [without confirmation]
105 * EXEC <program> - execute an external program
106 * SHEXEC <command> - execute a shell command
107 * WORKSPACE_MENU - places the workspace submenu
108 * ARRANGE_ICONS
109 * RESTART [<window manager>] - restarts the window manager
110 * SHOW_ALL - unhide all windows on workspace
111 * HIDE_OTHERS - hides all windows excep the focused one
112 * OPEN_MENU file - read menu data from file which must be a valid menu file.
113 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
114 * - read menu data from directory(ies) and
115 * eventually precede each with a command.
116 * OPEN_MENU | command
117 * - opens command and uses its stdout to construct and insert
118 * the resulting menu in current position. The output of
119 * command must be a valid menu description.
120 * The space between '|' and command is optional.
121 * || will do the same, but will not cache the contents.
122 * OPEN_PLMENU | command
123 * - opens command and uses its stdout which must be in proplist
124 * fromat to construct and insert the resulting menu in current
125 * position.
126 * The space between '|' and command is optional.
127 * || will do the same, but will not cache the contents.
128 * SAVE_SESSION - saves the current state of the desktop, which include
129 * all running applications, all their hints (geometry,
130 * position on screen, workspace they live on, the dock
131 * or clip from where they were launched, and
132 * if minimized, shaded or hidden. Also saves the current
133 * workspace the user is on. All will be restored on every
134 * start of windowmaker until another SAVE_SESSION or
135 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
136 * WindowMaker domain file, then saving is automatically
137 * done on every windowmaker exit, overwriting any
138 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
139 * dock state now.
140 * CLEAR_SESSION - clears any previous saved session. This will not have
141 * any effect if SaveSessionOnExit is True.
145 #define M_QUICK 1
147 /* menu commands */
149 static void execCommand(WMenu * menu, WMenuEntry * entry)
151 char *cmdline;
153 cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
155 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
156 GrabModeAsync, GrabModeAsync, None, wPreferences.cursor[WCUR_WAIT], CurrentTime);
157 XSync(dpy, 0);
159 if (cmdline) {
160 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
161 wfree(cmdline);
163 XUngrabPointer(dpy, CurrentTime);
164 XSync(dpy, 0);
167 static void exitCommand(WMenu * menu, WMenuEntry * entry)
169 static int inside = 0;
170 int result;
172 /* prevent reentrant calls */
173 if (inside)
174 return;
175 inside = 1;
177 #define R_CANCEL 0
178 #define R_EXIT 1
180 result = R_CANCEL;
182 if ((long)entry->clientdata == M_QUICK) {
183 result = R_EXIT;
184 } else {
185 int r, oldSaveSessionFlag;
187 oldSaveSessionFlag = wPreferences.save_session_on_exit;
188 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
189 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
191 if (r == WAPRDefault) {
192 result = R_EXIT;
193 } else if (r == WAPRAlternate) {
194 /* Don't modify the "save session on exit" flag if the
195 * user canceled the operation. */
196 wPreferences.save_session_on_exit = oldSaveSessionFlag;
199 if (result == R_EXIT)
200 Shutdown(WSExitMode);
202 #undef R_EXIT
203 #undef R_CANCEL
204 inside = 0;
207 static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
209 static int inside = 0;
210 int result;
212 /* prevent reentrant calls */
213 if (inside)
214 return;
215 inside = 1;
217 #define R_CANCEL 0
218 #define R_CLOSE 1
219 #define R_KILL 2
221 result = R_CANCEL;
222 if ((long)entry->clientdata == M_QUICK)
223 result = R_CLOSE;
224 else {
225 int r, oldSaveSessionFlag;
227 oldSaveSessionFlag = wPreferences.save_session_on_exit;
229 r = wExitDialog(menu->frame->screen_ptr,
230 _("Kill X session"),
231 _("Kill Window System session?\n"
232 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
233 if (r == WAPRDefault) {
234 result = R_KILL;
235 } else if (r == WAPRAlternate) {
236 /* Don't modify the "save session on exit" flag if the
237 * user canceled the operation. */
238 wPreferences.save_session_on_exit = oldSaveSessionFlag;
242 if (result != R_CANCEL) {
243 Shutdown(WSKillMode);
245 #undef R_CLOSE
246 #undef R_CANCEL
247 #undef R_KILL
248 inside = 0;
251 static void restartCommand(WMenu * menu, WMenuEntry * entry)
253 /* Parameter not used, but tell the compiler that it is ok */
254 (void) menu;
255 (void) entry;
257 Shutdown(WSRestartPreparationMode);
258 Restart((char *)entry->clientdata, False);
259 Restart(NULL, True);
262 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
264 /* Parameter not used, but tell the compiler that it is ok */
265 (void) entry;
267 wRefreshDesktop(menu->frame->screen_ptr);
270 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
272 /* Parameter not used, but tell the compiler that it is ok */
273 (void) entry;
275 wArrangeIcons(menu->frame->screen_ptr, True);
278 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
280 /* Parameter not used, but tell the compiler that it is ok */
281 (void) entry;
283 wShowAllWindows(menu->frame->screen_ptr);
286 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
288 /* Parameter not used, but tell the compiler that it is ok */
289 (void) entry;
291 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
294 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
296 /* Parameter not used, but tell the compiler that it is ok */
297 (void) entry;
299 if (!wPreferences.save_session_on_exit)
300 wSessionSaveState(menu->frame->screen_ptr);
302 wScreenSaveState(menu->frame->screen_ptr);
305 static void clearSessionCommand(WMenu *menu, WMenuEntry *entry)
307 /* Parameter not used, but tell the compiler that it is ok */
308 (void) entry;
310 wSessionClearState();
311 wScreenSaveState(menu->frame->screen_ptr);
314 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
316 /* Parameter not used, but tell the compiler that it is ok */
317 (void) entry;
319 wShowInfoPanel(menu->frame->screen_ptr);
322 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
324 /* Parameter not used, but tell the compiler that it is ok */
325 (void) entry;
327 wShowLegalPanel(menu->frame->screen_ptr);
330 /********************************************************************/
332 static char *getLocalizedMenuFile(const char *menu)
334 char *buffer, *ptr, *locale;
335 int len;
337 if (!w_global.locale)
338 return NULL;
340 len = strlen(menu) + strlen(w_global.locale) + 8;
341 buffer = wmalloc(len);
343 /* try menu.locale_name */
344 snprintf(buffer, len, "%s.%s", menu, w_global.locale);
345 if (access(buffer, F_OK) == 0)
346 return buffer;
348 /* position of locale in our buffer */
349 locale = buffer + strlen(menu) + 1;
351 /* check if it is in the form aa_bb.encoding and check for aa_bb */
352 ptr = strchr(locale, '.');
353 if (ptr) {
354 *ptr = 0;
355 if (access(buffer, F_OK) == 0)
356 return buffer;
359 /* now check for aa */
360 ptr = strchr(locale, '_');
361 if (ptr) {
362 *ptr = 0;
363 if (access(buffer, F_OK) == 0)
364 return buffer;
367 wfree(buffer);
369 return NULL;
372 Bool wRootMenuPerformShortcut(XEvent * event)
374 WScreen *scr = wScreenForRootWindow(event->xkey.root);
375 Shortcut *ptr;
376 int modifiers;
377 int done = 0;
379 /* ignore CapsLock */
380 modifiers = event->xkey.state & w_global.shortcut.modifiers_mask;
382 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
383 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
384 continue;
386 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
387 (*ptr->entry->callback) (ptr->menu, ptr->entry);
388 done = True;
392 return done;
395 void wRootMenuBindShortcuts(Window window)
397 Shortcut *ptr;
399 ptr = shortcutList;
400 while (ptr) {
401 if (ptr->modifier != AnyModifier) {
402 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
403 window, True, GrabModeAsync, GrabModeAsync);
404 #ifdef NUMLOCK_HACK
405 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
406 #endif
408 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
409 ptr = ptr->next;
413 static void rebindKeygrabs(WScreen * scr)
415 WWindow *wwin;
417 wwin = scr->focused_window;
419 while (wwin != NULL) {
420 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
422 if (!WFLAGP(wwin, no_bind_keys)) {
423 wWindowSetKeyGrabs(wwin);
425 wwin = wwin->prev;
429 static void removeShortcutsForMenu(WMenu * menu)
431 Shortcut *ptr, *tmp;
432 Shortcut *newList = NULL;
434 ptr = shortcutList;
435 while (ptr != NULL) {
436 tmp = ptr->next;
437 if (ptr->menu == menu) {
438 wfree(ptr);
439 } else {
440 ptr->next = newList;
441 newList = ptr;
443 ptr = tmp;
445 shortcutList = newList;
446 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
449 static Bool addShortcut(const char *file, const char *shortcutDefinition, WMenu *menu, WMenuEntry *entry)
451 Shortcut *ptr;
452 KeySym ksym;
453 char *k;
454 char buf[MAX_SHORTCUT_LENGTH], *b;
456 ptr = wmalloc(sizeof(Shortcut));
458 wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
459 b = (char *)buf;
461 /* get modifiers */
462 ptr->modifier = 0;
463 while ((k = strchr(b, '+')) != NULL) {
464 int mod;
466 *k = 0;
467 mod = wXModifierFromKey(b);
468 if (mod < 0) {
469 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
470 wfree(ptr);
471 return False;
473 ptr->modifier |= mod;
475 b = k + 1;
478 /* get key */
479 ksym = XStringToKeysym(b);
481 if (ksym == NoSymbol) {
482 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
483 file, shortcutDefinition, entry->text);
484 wfree(ptr);
485 return False;
488 ptr->keycode = XKeysymToKeycode(dpy, ksym);
489 if (ptr->keycode == 0) {
490 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
491 shortcutDefinition, entry->text);
492 wfree(ptr);
493 return False;
496 ptr->menu = menu;
497 ptr->entry = entry;
499 ptr->next = shortcutList;
500 shortcutList = ptr;
502 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
504 return True;
507 static char *next_token(char *line, char **next)
509 char *tmp, c;
510 char *ret;
512 *next = NULL;
513 while (*line == ' ' || *line == '\t')
514 line++;
516 tmp = line;
518 if (*tmp == '"') {
519 tmp++;
520 line++;
521 while (*tmp != 0 && *tmp != '"')
522 tmp++;
523 if (*tmp != '"') {
524 wwarning(_("%s: unmatched '\"' in menu file"), line);
525 return NULL;
527 } else {
528 do {
529 if (*tmp == '\\')
530 tmp++;
532 if (*tmp != 0)
533 tmp++;
535 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
538 c = *tmp;
539 *tmp = 0;
540 ret = wstrdup(line);
541 *tmp = c;
543 if (c == 0)
544 return ret;
545 else
546 tmp++;
548 /* skip blanks */
549 while (*tmp == ' ' || *tmp == '\t')
550 tmp++;
552 if (*tmp != 0)
553 *next = tmp;
555 return ret;
558 static void separateCommand(char *line, char ***file, char **command)
560 char *token, *tmp = line;
561 WMArray *array = WMCreateArray(4);
562 int count, i;
564 *file = NULL;
565 *command = NULL;
566 do {
567 token = next_token(tmp, &tmp);
568 if (token) {
569 if (strcmp(token, "WITH") == 0) {
570 if (tmp != NULL && *tmp != 0)
571 *command = wstrdup(tmp);
572 else
573 wwarning(_("%s: missing command"), line);
574 wfree(token);
575 break;
577 WMAddToArray(array, token);
579 } while (token != NULL && tmp != NULL);
581 count = WMGetArrayItemCount(array);
582 if (count > 0) {
583 *file = wmalloc(sizeof(char *) * (count + 1));
584 (*file)[count] = NULL;
585 for (i = 0; i < count; i++) {
586 (*file)[i] = WMGetFromArray(array, i);
589 WMFreeArray(array);
592 static WMenu *constructPLMenu(WScreen *screen, const char *path)
594 WMPropList *pl = NULL;
595 WMenu *menu = NULL;
597 if (!path)
598 return NULL;
600 pl = WMReadPropListFromFile(path);
601 if (!pl)
602 return NULL;
604 menu = configureMenu(screen, pl, False);
606 WMReleasePropList(pl);
608 if (!menu)
609 return NULL;
611 menu->on_destroy = removeShortcutsForMenu;
612 return menu;
617 static void constructMenu(WMenu * menu, WMenuEntry * entry)
619 WMenu *submenu;
620 struct stat stat_buf;
621 char **path;
622 char *cmd;
623 char *lpath = NULL;
624 int i, first = -1;
625 time_t last = 0;
627 separateCommand((char *)entry->clientdata, &path, &cmd);
628 if (path == NULL || *path == NULL || **path == 0) {
629 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
630 if (path) {
631 for (i = 0; path[i] != NULL; i++)
632 wfree(path[i]);
633 wfree(path);
635 if (cmd)
636 wfree(cmd);
637 return;
640 if (path[0][0] == '|') {
641 /* pipe menu */
643 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
644 /* parse pipe */
646 submenu = readMenuPipe(menu->frame->screen_ptr, path);
648 if (submenu != NULL) {
649 if (path[0][1] == '|')
650 submenu->timestamp = 0;
651 else
652 submenu->timestamp = 1; /* there's no automatic reloading */
654 } else {
655 submenu = NULL;
658 } else {
660 /* try interpreting path as a proplist file */
661 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
662 /* if unsuccessful, try it as an old-style file */
663 if (!submenu) {
665 i = 0;
666 while (path[i] != NULL) {
667 char *tmp;
669 if (strcmp(path[i], "-noext") == 0) {
670 i++;
671 continue;
674 tmp = wexpandpath(path[i]);
675 wfree(path[i]);
676 lpath = getLocalizedMenuFile(tmp);
677 if (lpath) {
678 wfree(tmp);
679 path[i] = lpath;
680 lpath = NULL;
681 } else {
682 path[i] = tmp;
685 if (stat(path[i], &stat_buf) == 0) {
686 if (last < stat_buf.st_mtime)
687 last = stat_buf.st_mtime;
688 if (first < 0)
689 first = i;
690 } else {
691 werror(_("%s:could not stat menu"), path[i]);
692 /*goto finish; */
695 i++;
698 if (first < 0) {
699 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
700 goto finish;
702 stat(path[first], &stat_buf);
703 if (!menu->cascades[entry->cascade]
704 || menu->cascades[entry->cascade]->timestamp < last) {
706 if (S_ISDIR(stat_buf.st_mode)) {
707 /* menu directory */
708 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
709 if (submenu)
710 submenu->timestamp = last;
711 } else if (S_ISREG(stat_buf.st_mode)) {
712 /* menu file */
714 if (cmd || path[1])
715 wwarning(_("too many parameters in OPEN_MENU: %s"),
716 (char *)entry->clientdata);
718 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
719 if (submenu)
720 submenu->timestamp = stat_buf.st_mtime;
721 } else {
722 submenu = NULL;
724 } else {
725 submenu = NULL;
730 if (submenu) {
731 wMenuEntryRemoveCascade(menu, entry);
732 wMenuEntrySetCascade(menu, entry, submenu);
735 finish:
736 i = 0;
737 while (path[i] != NULL)
738 wfree(path[i++]);
739 wfree(path);
740 if (cmd)
741 wfree(cmd);
744 static void constructPLMenuFromPipe(WMenu * menu, WMenuEntry * entry)
746 WMenu *submenu = NULL;
747 char **path;
748 char *cmd;
749 int i;
751 separateCommand((char *)entry->clientdata, &path, &cmd);
752 if (path == NULL || *path == NULL || **path == 0) {
753 wwarning(_("invalid OPEN_PLMENU specification: %s"),
754 (char *)entry->clientdata);
755 if (path) {
756 for (i = 0; path[i] != NULL; i++)
757 wfree(path[i]);
758 wfree(path);
760 if (cmd)
761 wfree(cmd);
762 return;
765 if (path[0][0] == '|') {
766 /* pipe menu */
768 if (!menu->cascades[entry->cascade]
769 || menu->cascades[entry->cascade]->timestamp == 0) {
770 /* parse pipe */
771 submenu = readPLMenuPipe(menu->frame->screen_ptr, path);
773 if (submenu != NULL) {
774 if (path[0][1] == '|')
775 submenu->timestamp = 0;
776 else
777 submenu->timestamp = 1; /* there's no automatic reloading */
782 if (submenu) {
783 wMenuEntryRemoveCascade(menu, entry);
784 wMenuEntrySetCascade(menu, entry, submenu);
787 i = 0;
788 while (path[i] != NULL)
789 wfree(path[i++]);
791 wfree(path);
792 if (cmd)
793 wfree(cmd);
796 static void cleanupWorkspaceMenu(WMenu *menu)
798 if (w_global.workspace.menu == menu)
799 w_global.workspace.menu = NULL;
802 static WMenuEntry *addWorkspaceMenu(WScreen *scr, WMenu *menu, const char *title)
804 WMenu *wsmenu;
805 WMenuEntry *entry;
807 if (scr->flags.added_workspace_menu) {
808 wwarning(_
809 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
810 return NULL;
811 } else {
812 scr->flags.added_workspace_menu = 1;
814 wsmenu = wWorkspaceMenuMake(scr, True);
815 wsmenu->on_destroy = cleanupWorkspaceMenu;
817 w_global.workspace.menu = wsmenu;
818 entry = wMenuAddCallback(menu, title, NULL, NULL);
819 wMenuEntrySetCascade(menu, entry, wsmenu);
821 wWorkspaceMenuUpdate(wsmenu);
823 return entry;
826 static void cleanupWindowsMenu(WMenu * menu)
828 if (menu->frame->screen_ptr->switch_menu == menu)
829 menu->frame->screen_ptr->switch_menu = NULL;
832 static WMenuEntry *addWindowsMenu(WScreen *scr, WMenu *menu, const char *title)
834 WMenu *wwmenu;
835 WWindow *wwin;
836 WMenuEntry *entry;
838 if (scr->flags.added_windows_menu) {
839 wwarning(_
840 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
841 return NULL;
842 } else {
843 scr->flags.added_windows_menu = 1;
845 wwmenu = wMenuCreate(scr, _("Window List"), False);
846 wwmenu->on_destroy = cleanupWindowsMenu;
847 scr->switch_menu = wwmenu;
848 wwin = scr->focused_window;
849 while (wwin) {
850 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
852 wwin = wwin->prev;
854 entry = wMenuAddCallback(menu, title, NULL, NULL);
855 wMenuEntrySetCascade(menu, entry, wwmenu);
857 return entry;
860 static WMenuEntry *addMenuEntry(WMenu *menu, const char *title, const char *shortcut, const char *command,
861 const char *params, const char *file_name)
863 WScreen *scr;
864 WMenuEntry *entry = NULL;
865 Bool shortcutOk = False;
867 if (!menu)
868 return NULL;
869 scr = menu->frame->screen_ptr;
870 if (strcmp(command, "OPEN_MENU") == 0) {
871 if (!params) {
872 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
873 } else {
874 WMenu *dummy;
875 char *path;
877 path = wfindfile(DEF_CONFIG_PATHS, params);
878 if (!path) {
879 path = wstrdup(params);
881 dummy = wMenuCreate(scr, title, False);
882 dummy->on_destroy = removeShortcutsForMenu;
883 entry = wMenuAddCallback(menu, title, constructMenu, path);
884 entry->free_cdata = wfree;
885 wMenuEntrySetCascade(menu, entry, dummy);
887 } else if (strcmp(command, "OPEN_PLMENU") == 0) {
888 if (!params) {
889 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
890 } else {
891 WMenu *dummy;
892 char *path;
894 path = wfindfile(DEF_CONFIG_PATHS, params);
895 if (!path)
896 path = wstrdup(params);
898 dummy = wMenuCreate(scr, title, False);
899 dummy->on_destroy = removeShortcutsForMenu;
900 entry = wMenuAddCallback(menu, title, constructPLMenuFromPipe, path);
901 entry->free_cdata = wfree;
902 wMenuEntrySetCascade(menu, entry, dummy);
904 } else if (strcmp(command, "EXEC") == 0) {
905 if (!params)
906 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
907 else {
908 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
909 entry->free_cdata = wfree;
910 shortcutOk = True;
912 } else if (strcmp(command, "SHEXEC") == 0) {
913 if (!params)
914 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
915 else {
916 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
917 entry->free_cdata = wfree;
918 shortcutOk = True;
920 } else if (strcmp(command, "EXIT") == 0) {
922 if (params && strcmp(params, "QUICK") == 0)
923 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
924 else
925 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
927 shortcutOk = True;
928 } else if (strcmp(command, "SHUTDOWN") == 0) {
930 if (params && strcmp(params, "QUICK") == 0)
931 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
932 else
933 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
935 shortcutOk = True;
936 } else if (strcmp(command, "REFRESH") == 0) {
937 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
939 shortcutOk = True;
940 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
941 entry = addWorkspaceMenu(scr, menu, title);
943 shortcutOk = True;
944 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
945 entry = addWindowsMenu(scr, menu, title);
947 shortcutOk = True;
948 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
949 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
951 shortcutOk = True;
952 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
953 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
955 shortcutOk = True;
956 } else if (strcmp(command, "SHOW_ALL") == 0) {
957 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
959 shortcutOk = True;
960 } else if (strcmp(command, "RESTART") == 0) {
961 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
962 entry->free_cdata = wfree;
963 shortcutOk = True;
964 } else if (strcmp(command, "SAVE_SESSION") == 0) {
965 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
967 shortcutOk = True;
968 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
969 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
970 shortcutOk = True;
971 } else if (strcmp(command, "INFO_PANEL") == 0) {
972 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
973 shortcutOk = True;
974 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
975 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
976 shortcutOk = True;
977 } else {
978 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
980 return NULL;
983 if (shortcut && entry) {
984 if (!shortcutOk) {
985 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
986 } else {
987 if (addShortcut(file_name, shortcut, menu, entry)) {
989 entry->rtext = GetShortcutString(shortcut);
991 entry->rtext = wstrdup(shortcut);
997 return entry;
1000 /******************* Menu Configuration From File *******************/
1002 static void freeline(char *title, char *command, char *parameter, char *shortcut)
1004 wfree(title);
1005 wfree(command);
1006 wfree(parameter);
1007 wfree(shortcut);
1010 static WMenu *parseCascade(WScreen * scr, WMenu * menu, WMenuParser parser)
1012 char *command, *params, *shortcut, *title;
1014 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1016 if (command == NULL || !command[0]) {
1017 WMenuParserError(parser, _("missing command in menu config") );
1018 freeline(title, command, params, shortcut);
1019 goto error;
1022 if (strcasecmp(command, "MENU") == 0) {
1023 WMenu *cascade;
1025 /* start submenu */
1027 cascade = wMenuCreate(scr, M_(title), False);
1028 cascade->on_destroy = removeShortcutsForMenu;
1029 if (!parseCascade(scr, cascade, parser)) {
1030 wMenuDestroy(cascade, True);
1031 } else {
1032 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
1034 } else if (strcasecmp(command, "END") == 0) {
1035 /* end of menu */
1036 freeline(title, command, params, shortcut);
1037 return menu;
1038 } else {
1039 /* normal items */
1040 addMenuEntry(menu, M_(title), shortcut, command, params, WMenuParserGetFilename(parser));
1042 freeline(title, command, params, shortcut);
1045 WMenuParserError(parser, _("syntax error in menu file: END declaration missing") );
1047 error:
1048 return NULL;
1051 static WMenu *readMenuFile(WScreen *scr, const char *file_name)
1053 WMenu *menu = NULL;
1054 FILE *file = NULL;
1055 WMenuParser parser;
1056 char *command, *params, *shortcut, *title;
1058 file = fopen(file_name, "rb");
1059 if (!file) {
1060 werror(_("%s:could not open menu file"), file_name);
1061 return NULL;
1063 parser = WMenuParserCreate(file_name, file, DEF_CONFIG_PATHS);
1064 menu_parser_register_macros(parser);
1066 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1068 if (command == NULL || !command[0]) {
1069 WMenuParserError(parser, _("missing command in menu config") );
1070 freeline(title, command, params, shortcut);
1071 break;
1073 if (strcasecmp(command, "MENU") == 0) {
1074 menu = wMenuCreate(scr, M_(title), True);
1075 menu->on_destroy = removeShortcutsForMenu;
1076 if (!parseCascade(scr, menu, parser)) {
1077 wMenuDestroy(menu, True);
1078 menu = NULL;
1080 freeline(title, command, params, shortcut);
1081 break;
1082 } else {
1083 WMenuParserError(parser, _("invalid menu file, MENU command is missing") );
1084 freeline(title, command, params, shortcut);
1085 break;
1087 freeline(title, command, params, shortcut);
1090 WMenuParserDelete(parser);
1091 fclose(file);
1093 return menu;
1096 /************ Menu Configuration From Pipe *************/
1097 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name)
1099 WMPropList *plist = NULL;
1100 WMenu *menu = NULL;
1101 char *filename;
1102 char flat_file[MAXLINE];
1103 int i;
1105 flat_file[0] = '\0';
1107 for (i = 0; file_name[i] != NULL; i++) {
1108 strcat(flat_file, file_name[i]);
1109 strcat(flat_file, " ");
1111 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1113 plist = WMReadPropListFromPipe(filename);
1115 if (!plist)
1116 return NULL;
1118 menu = configureMenu(scr, plist, False);
1120 WMReleasePropList(plist);
1122 if (!menu)
1123 return NULL;
1125 menu->on_destroy = removeShortcutsForMenu;
1126 return menu;
1129 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1131 WMenu *menu = NULL;
1132 FILE *file = NULL;
1133 WMenuParser parser;
1134 char *command, *params, *shortcut, *title;
1135 char *filename;
1136 char flat_file[MAXLINE];
1137 int i;
1139 flat_file[0] = '\0';
1141 for (i = 0; file_name[i] != NULL; i++) {
1142 strcat(flat_file, file_name[i]);
1143 strcat(flat_file, " ");
1145 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1147 file = popen(filename, "r");
1148 if (!file) {
1149 werror(_("%s:could not open menu file"), filename);
1150 return NULL;
1152 parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS);
1153 menu_parser_register_macros(parser);
1155 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1157 if (command == NULL || !command[0]) {
1158 WMenuParserError(parser, _("missing command in menu config") );
1159 freeline(title, command, params, shortcut);
1160 break;
1162 if (strcasecmp(command, "MENU") == 0) {
1163 menu = wMenuCreate(scr, M_(title), True);
1164 menu->on_destroy = removeShortcutsForMenu;
1165 if (!parseCascade(scr, menu, parser)) {
1166 wMenuDestroy(menu, True);
1167 menu = NULL;
1169 freeline(title, command, params, shortcut);
1170 break;
1171 } else {
1172 WMenuParserError(parser, _("no title given for the root menu") );
1173 freeline(title, command, params, shortcut);
1174 break;
1177 freeline(title, command, params, shortcut);
1180 WMenuParserDelete(parser);
1181 pclose(file);
1183 return menu;
1186 typedef struct {
1187 char *name;
1188 int index;
1189 } dir_data;
1191 static int myCompare(const void *d1, const void *d2)
1193 dir_data *p1 = *(dir_data **) d1;
1194 dir_data *p2 = *(dir_data **) d2;
1196 return strcmp(p1->name, p2->name);
1199 /***** Preset some macro for file parser *****/
1200 static void menu_parser_register_macros(WMenuParser parser)
1202 Visual *visual;
1203 char buf[32];
1205 // Used to return CPP verion, now returns wmaker's version
1206 WMenuParserRegisterSimpleMacro(parser, "__VERSION__", VERSION);
1208 // All macros below were historically defined by WindowMaker
1209 visual = DefaultVisual(dpy, DefaultScreen(dpy));
1210 snprintf(buf, sizeof(buf), "%d", visual->class);
1211 WMenuParserRegisterSimpleMacro(parser, "VISUAL", buf);
1213 snprintf(buf, sizeof(buf), "%d", DefaultDepth(dpy, DefaultScreen(dpy)) );
1214 WMenuParserRegisterSimpleMacro(parser, "DEPTH", buf);
1216 snprintf(buf, sizeof(buf), "%d", WidthOfScreen(DefaultScreenOfDisplay(dpy)) );
1217 WMenuParserRegisterSimpleMacro(parser, "SCR_WIDTH", buf);
1219 snprintf(buf, sizeof(buf), "%d", HeightOfScreen(DefaultScreenOfDisplay(dpy)) );
1220 WMenuParserRegisterSimpleMacro(parser, "SCR_HEIGHT", buf);
1222 WMenuParserRegisterSimpleMacro(parser, "DISPLAY", XDisplayName(DisplayString(dpy)) );
1224 WMenuParserRegisterSimpleMacro(parser, "WM_VERSION", "\"" VERSION "\"");
1227 /************ Menu Configuration From Directory *************/
1229 static Bool isFilePackage(const char *file)
1231 int l;
1233 /* check if the extension indicates this file is a
1234 * file package. For now, only recognize .themed */
1236 l = strlen(file);
1238 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1239 return True;
1240 } else {
1241 return False;
1245 static WMenu *readMenuDirectory(WScreen *scr, const char *title, char **path, const char *command)
1247 DIR *dir;
1248 struct dirent *dentry;
1249 struct stat stat_buf;
1250 WMenu *menu = NULL;
1251 char *buffer;
1252 WMArray *dirs = NULL, *files = NULL;
1253 WMArrayIterator iter;
1254 int length, i, have_space = 0;
1255 dir_data *data;
1256 int stripExtension = 0;
1258 dirs = WMCreateArray(16);
1259 files = WMCreateArray(16);
1261 i = 0;
1262 while (path[i] != NULL) {
1263 if (strcmp(path[i], "-noext") == 0) {
1264 stripExtension = 1;
1265 i++;
1266 continue;
1269 dir = opendir(path[i]);
1270 if (!dir) {
1271 i++;
1272 continue;
1275 while ((dentry = readdir(dir))) {
1277 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1278 continue;
1280 if (dentry->d_name[0] == '.')
1281 continue;
1283 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1284 if (!buffer) {
1285 werror(_("out of memory while constructing directory menu %s"), path[i]);
1286 break;
1289 strcpy(buffer, path[i]);
1290 strcat(buffer, "/");
1291 strcat(buffer, dentry->d_name);
1293 if (stat(buffer, &stat_buf) != 0) {
1294 werror(_("%s:could not stat file \"%s\" in menu directory"),
1295 path[i], dentry->d_name);
1296 } else {
1297 Bool isFilePack = False;
1299 data = NULL;
1300 if (S_ISDIR(stat_buf.st_mode)
1301 && !(isFilePack = isFilePackage(dentry->d_name))) {
1303 /* access always returns success for user root */
1304 if (access(buffer, X_OK) == 0) {
1305 /* Directory is accesible. Add to directory list */
1307 data = (dir_data *) wmalloc(sizeof(dir_data));
1308 data->name = wstrdup(dentry->d_name);
1309 data->index = i;
1311 WMAddToArray(dirs, data);
1313 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1314 /* Hack because access always returns X_OK success for user root */
1315 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1316 if ((command != NULL && access(buffer, R_OK) == 0) ||
1317 (command == NULL && access(buffer, X_OK) == 0 &&
1318 (stat_buf.st_mode & S_IXANY))) {
1320 data = (dir_data *) wmalloc(sizeof(dir_data));
1321 data->name = wstrdup(dentry->d_name);
1322 data->index = i;
1324 WMAddToArray(files, data);
1328 free(buffer);
1331 closedir(dir);
1332 i++;
1335 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1336 WMFreeArray(dirs);
1337 WMFreeArray(files);
1338 return NULL;
1341 WMSortArray(dirs, myCompare);
1342 WMSortArray(files, myCompare);
1344 menu = wMenuCreate(scr, M_(title), False);
1345 menu->on_destroy = removeShortcutsForMenu;
1347 WM_ITERATE_ARRAY(dirs, data, iter) {
1348 /* New directory. Use same OPEN_MENU command that was used
1349 * for the current directory. */
1350 length = strlen(path[data->index]) + strlen(data->name) + 6;
1351 if (stripExtension)
1352 length += 7;
1353 if (command)
1354 length += strlen(command) + 6;
1355 buffer = malloc(length);
1356 if (!buffer) {
1357 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1358 break;
1361 buffer[0] = '\0';
1362 if (stripExtension)
1363 strcat(buffer, "-noext ");
1365 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1367 if (have_space)
1368 strcat(buffer, "\"");
1369 strcat(buffer, path[data->index]);
1371 strcat(buffer, "/");
1372 strcat(buffer, data->name);
1373 if (have_space)
1374 strcat(buffer, "\"");
1375 if (command) {
1376 strcat(buffer, " WITH ");
1377 strcat(buffer, command);
1380 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1382 wfree(buffer);
1383 if (data->name)
1384 wfree(data->name);
1385 wfree(data);
1388 WM_ITERATE_ARRAY(files, data, iter) {
1389 /* executable: add as entry */
1390 length = strlen(path[data->index]) + strlen(data->name) + 6;
1391 if (command)
1392 length += strlen(command);
1394 buffer = malloc(length);
1395 if (!buffer) {
1396 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1397 break;
1400 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1401 if (command != NULL) {
1402 strcpy(buffer, command);
1403 strcat(buffer, " ");
1404 if (have_space)
1405 strcat(buffer, "\"");
1406 strcat(buffer, path[data->index]);
1407 } else {
1408 if (have_space) {
1409 buffer[0] = '"';
1410 buffer[1] = 0;
1411 strcat(buffer, path[data->index]);
1412 } else {
1413 strcpy(buffer, path[data->index]);
1416 strcat(buffer, "/");
1417 strcat(buffer, data->name);
1418 if (have_space)
1419 strcat(buffer, "\"");
1421 if (stripExtension) {
1422 char *ptr = strrchr(data->name, '.');
1423 if (ptr && ptr != data->name)
1424 *ptr = 0;
1426 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1428 wfree(buffer);
1429 if (data->name)
1430 wfree(data->name);
1431 wfree(data);
1434 WMFreeArray(files);
1435 WMFreeArray(dirs);
1437 return menu;
1440 /************ Menu Configuration From WMRootMenu *************/
1442 static WMenu *makeDefaultMenu(WScreen * scr)
1444 WMenu *menu = NULL;
1446 menu = wMenuCreate(scr, _("Commands"), True);
1447 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1448 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1449 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1450 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1451 return menu;
1455 *----------------------------------------------------------------------
1456 * configureMenu--
1457 * Reads root menu configuration from defaults database.
1459 *----------------------------------------------------------------------
1461 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1463 WMenu *menu = NULL;
1464 WMPropList *elem;
1465 int i, count;
1466 WMPropList *title, *command, *params;
1467 char *tmp, *mtitle;
1469 if (WMIsPLString(definition)) {
1470 struct stat stat_buf;
1471 char *path = NULL;
1472 Bool menu_is_default = False;
1474 /* menu definition is a string. Probably a path, so parse the file */
1476 tmp = wexpandpath(WMGetFromPLString(definition));
1478 path = getLocalizedMenuFile(tmp);
1480 if (!path)
1481 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1483 if (!path) {
1484 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1485 menu_is_default = True;
1488 if (!path) {
1489 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1490 wfree(tmp);
1491 return NULL;
1494 if (stat(path, &stat_buf) < 0) {
1495 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1496 wfree(path);
1497 wfree(tmp);
1498 return NULL;
1501 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1502 /* if the pointer in WMRootMenu has changed */
1503 || w_global.domain.root_menu->timestamp > scr->root_menu->timestamp) {
1505 if (menu_is_default) {
1506 wwarning(_
1507 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1508 path);
1511 menu = readMenuFile(scr, path);
1512 if (menu)
1513 menu->timestamp = WMAX(stat_buf.st_mtime, w_global.domain.root_menu->timestamp);
1514 } else {
1515 menu = NULL;
1517 wfree(path);
1518 wfree(tmp);
1520 return menu;
1523 count = WMGetPropListItemCount(definition);
1524 if (count == 0)
1525 return NULL;
1527 elem = WMGetFromPLArray(definition, 0);
1528 if (!WMIsPLString(elem)) {
1529 tmp = WMGetPropListDescription(elem, False);
1530 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1531 wfree(tmp);
1532 return NULL;
1534 mtitle = WMGetFromPLString(elem);
1536 menu = wMenuCreate(scr, M_(mtitle), False);
1537 menu->on_destroy = removeShortcutsForMenu;
1539 #ifdef GLOBAL_SUBMENU_FILE
1540 if (includeGlobals) {
1541 WMenu *submenu;
1542 WMenuEntry *mentry;
1544 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1546 if (submenu) {
1547 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1548 wMenuEntrySetCascade(menu, mentry, submenu);
1551 #else
1552 /* Parameter not used, but tell the compiler that it is ok */
1553 (void) includeGlobals;
1554 #endif
1556 for (i = 1; i < count; i++) {
1557 elem = WMGetFromPLArray(definition, i);
1558 #if 0
1559 if (WMIsPLString(elem)) {
1560 char *file;
1562 file = WMGetFromPLString(elem);
1565 #endif
1566 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1567 goto error;
1569 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1570 WMenu *submenu;
1571 WMenuEntry *mentry;
1573 /* submenu */
1574 submenu = configureMenu(scr, elem, True);
1575 if (submenu) {
1576 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1577 wMenuEntrySetCascade(menu, mentry, submenu);
1579 } else {
1580 int idx = 0;
1581 WMPropList *shortcut;
1582 /* normal entry */
1584 title = WMGetFromPLArray(elem, idx++);
1585 shortcut = WMGetFromPLArray(elem, idx++);
1586 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1587 shortcut = WMGetFromPLArray(elem, idx++);
1588 command = WMGetFromPLArray(elem, idx++);
1589 } else {
1590 command = shortcut;
1591 shortcut = NULL;
1593 params = WMGetFromPLArray(elem, idx++);
1595 if (!title || !command)
1596 goto error;
1598 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1599 shortcut ? WMGetFromPLString(shortcut) : NULL,
1600 WMGetFromPLString(command),
1601 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1603 continue;
1605 error:
1606 tmp = WMGetPropListDescription(elem, False);
1607 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1608 wfree(tmp);
1611 return menu;
1615 *----------------------------------------------------------------------
1616 * OpenRootMenu--
1617 * Opens the root menu, parsing the menu configuration from the
1618 * defaults database.
1619 * If the menu is already mapped and is not sticked to the
1620 * root window, it will be unmapped.
1622 * Side effects:
1623 * The menu may be remade.
1625 * Notes:
1626 * Construction of OPEN_MENU entries are delayed to the moment the
1627 * user map's them.
1628 *----------------------------------------------------------------------
1630 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1632 WMenu *menu = NULL;
1633 WMPropList *definition;
1635 static WMPropList *domain=NULL;
1637 if (!domain) {
1638 domain = WMCreatePLString("WMRootMenu");
1642 scr->flags.root_menu_changed_shortcuts = 0;
1643 scr->flags.added_workspace_menu = 0;
1644 scr->flags.added_windows_menu = 0;
1646 if (scr->root_menu && scr->root_menu->flags.mapped) {
1647 menu = scr->root_menu;
1648 if (!menu->flags.buttoned) {
1649 wMenuUnmap(menu);
1650 } else {
1651 wRaiseFrame(menu->frame->core);
1653 if (keyboard)
1654 wMenuMapAt(menu, 0, 0, True);
1655 else
1656 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1658 return;
1661 definition = w_global.domain.root_menu->dictionary;
1664 definition = PLGetDomain(domain);
1666 if (definition) {
1667 if (WMIsPLArray(definition)) {
1668 if (!scr->root_menu || w_global.domain.root_menu->timestamp > scr->root_menu->timestamp) {
1669 menu = configureMenu(scr, definition, True);
1670 if (menu)
1671 menu->timestamp = w_global.domain.root_menu->timestamp;
1673 } else
1674 menu = NULL;
1675 } else {
1676 menu = configureMenu(scr, definition, True);
1680 if (!menu) {
1681 /* menu hasn't changed or could not be read */
1682 if (!scr->root_menu) {
1683 wMessageDialog(scr, _("Error"),
1684 _("The applications menu could not be loaded. "
1685 "Look at the console output for a detailed "
1686 "description of the errors."), _("OK"), NULL, NULL);
1688 menu = makeDefaultMenu(scr);
1689 scr->root_menu = menu;
1691 menu = scr->root_menu;
1692 } else {
1693 /* new root menu */
1694 if (scr->root_menu) {
1695 wMenuDestroy(scr->root_menu, True);
1697 scr->root_menu = menu;
1699 if (menu) {
1700 int newx, newy;
1702 if (keyboard && x == 0 && y == 0) {
1703 newx = newy = 0;
1704 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1705 newx = x - menu->frame->core->width / 2;
1706 newy = y - menu->frame->core->height / 2;
1707 } else {
1708 newx = x - menu->frame->core->width / 2;
1709 newy = y;
1711 wMenuMapAt(menu, newx, newy, keyboard);
1714 if (scr->flags.root_menu_changed_shortcuts)
1715 rebindKeygrabs(scr);