Prevent crash when switchpanel is not initialised.
[wmaker-crm.git] / src / rootmenu.c
blobf93e5bf05de3b2c090261096596afbe7542bbc2e
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"
57 #include <WINGs/WUtil.h>
59 #define MAX_SHORTCUT_LENGTH 32
61 extern char *Locale;
62 extern WDDomain *WDRootMenu;
63 extern Cursor wCursor[WCUR_LAST];
64 extern WPreferences wPreferences;
66 static WMenu *readMenuPipe(WScreen * scr, char **file_name);
67 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name);
68 static WMenu *readMenuFile(WScreen * scr, char *file_name);
69 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **file_name, char *command);
70 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals);
71 static void menu_parser_register_macros(WMenuParser parser);
73 typedef struct Shortcut {
74 struct Shortcut *next;
76 int modifier;
77 KeyCode keycode;
78 WMenuEntry *entry;
79 WMenu *menu;
80 } Shortcut;
82 static Shortcut *shortcutList = NULL;
85 * Syntax:
86 * # main menu
87 * "Menu Name" MENU
88 * "Title" EXEC command_to_exec -params
89 * "Submenu" MENU
90 * "Title" EXEC command_to_exec -params
91 * "Submenu" END
92 * "Workspaces" WORKSPACE_MENU
93 * "Title" built_in_command
94 * "Quit" EXIT
95 * "Quick Quit" EXIT QUICK
96 * "Menu Name" END
98 * Commands may be preceded by SHORTCUT key
100 * Built-in commands:
102 * INFO_PANEL - shows the Info Panel
103 * LEGAL_PANEL - shows the Legal info panel
104 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
105 * REFRESH - forces the desktop to be repainted
106 * EXIT [QUICK] - exit the window manager [without confirmation]
107 * EXEC <program> - execute an external program
108 * SHEXEC <command> - execute a shell command
109 * WORKSPACE_MENU - places the workspace submenu
110 * ARRANGE_ICONS
111 * RESTART [<window manager>] - restarts the window manager
112 * SHOW_ALL - unhide all windows on workspace
113 * HIDE_OTHERS - hides all windows excep the focused one
114 * OPEN_MENU file - read menu data from file which must be a valid menu file.
115 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
116 * - read menu data from directory(ies) and
117 * eventually precede each with a command.
118 * OPEN_MENU | command
119 * - opens command and uses its stdout to construct and insert
120 * the resulting menu in current position. The output of
121 * command must be a valid menu description.
122 * The space between '|' and command is optional.
123 * || will do the same, but will not cache the contents.
124 * OPEN_PLMENU | command
125 * - opens command and uses its stdout which must be in proplist
126 * fromat to construct and insert the resulting menu in current
127 * position.
128 * The space between '|' and command is optional.
129 * || will do the same, but will not cache the contents.
130 * SAVE_SESSION - saves the current state of the desktop, which include
131 * all running applications, all their hints (geometry,
132 * position on screen, workspace they live on, the dock
133 * or clip from where they were launched, and
134 * if minimized, shaded or hidden. Also saves the current
135 * workspace the user is on. All will be restored on every
136 * start of windowmaker until another SAVE_SESSION or
137 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
138 * WindowMaker domain file, then saving is automatically
139 * done on every windowmaker exit, overwriting any
140 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
141 * dock state now.
142 * CLEAR_SESSION - clears any previous saved session. This will not have
143 * any effect if SaveSessionOnExit is True.
147 #define M_QUICK 1
149 /* menu commands */
151 static void execCommand(WMenu * menu, WMenuEntry * entry)
153 char *cmdline;
155 cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
157 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
158 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);
159 XSync(dpy, 0);
161 if (cmdline) {
162 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
163 wfree(cmdline);
165 XUngrabPointer(dpy, CurrentTime);
166 XSync(dpy, 0);
169 static void exitCommand(WMenu * menu, WMenuEntry * entry)
171 static int inside = 0;
172 int result;
174 /* prevent reentrant calls */
175 if (inside)
176 return;
177 inside = 1;
179 #define R_CANCEL 0
180 #define R_EXIT 1
182 result = R_CANCEL;
184 if ((long)entry->clientdata == M_QUICK) {
185 result = R_EXIT;
186 } else {
187 int r, oldSaveSessionFlag;
189 oldSaveSessionFlag = wPreferences.save_session_on_exit;
190 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
191 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
193 if (r == WAPRDefault) {
194 result = R_EXIT;
195 } else if (r == WAPRAlternate) {
196 /* Don't modify the "save session on exit" flag if the
197 * user canceled the operation. */
198 wPreferences.save_session_on_exit = oldSaveSessionFlag;
201 if (result == R_EXIT)
202 Shutdown(WSExitMode);
204 #undef R_EXIT
205 #undef R_CANCEL
206 inside = 0;
209 static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
211 static int inside = 0;
212 int result;
214 /* prevent reentrant calls */
215 if (inside)
216 return;
217 inside = 1;
219 #define R_CANCEL 0
220 #define R_CLOSE 1
221 #define R_KILL 2
223 result = R_CANCEL;
224 if ((long)entry->clientdata == M_QUICK)
225 result = R_CLOSE;
226 else {
227 int r, oldSaveSessionFlag;
229 oldSaveSessionFlag = wPreferences.save_session_on_exit;
231 r = wExitDialog(menu->frame->screen_ptr,
232 _("Kill X session"),
233 _("Kill Window System session?\n"
234 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
235 if (r == WAPRDefault) {
236 result = R_KILL;
237 } else if (r == WAPRAlternate) {
238 /* Don't modify the "save session on exit" flag if the
239 * user canceled the operation. */
240 wPreferences.save_session_on_exit = oldSaveSessionFlag;
244 if (result != R_CANCEL) {
245 Shutdown(WSKillMode);
247 #undef R_CLOSE
248 #undef R_CANCEL
249 #undef R_KILL
250 inside = 0;
253 static void restartCommand(WMenu * menu, WMenuEntry * entry)
255 Shutdown(WSRestartPreparationMode);
256 Restart((char *)entry->clientdata, False);
257 Restart(NULL, True);
260 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
262 wRefreshDesktop(menu->frame->screen_ptr);
265 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
267 wArrangeIcons(menu->frame->screen_ptr, True);
270 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
272 wShowAllWindows(menu->frame->screen_ptr);
275 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
277 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
280 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
282 if (!wPreferences.save_session_on_exit)
283 wSessionSaveState(menu->frame->screen_ptr);
285 wScreenSaveState(menu->frame->screen_ptr);
288 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
290 wSessionClearState(menu->frame->screen_ptr);
291 wScreenSaveState(menu->frame->screen_ptr);
294 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
296 wShowInfoPanel(menu->frame->screen_ptr);
299 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
301 wShowLegalPanel(menu->frame->screen_ptr);
304 /********************************************************************/
306 static char * getLocalizedMenuFile(char *menu)
308 char *buffer, *ptr, *locale;
309 int len;
311 if (!Locale)
312 return NULL;
314 len = strlen(menu) + strlen(Locale) + 8;
315 buffer = wmalloc(len);
317 /* try menu.locale_name */
318 snprintf(buffer, len, "%s.%s", menu, Locale);
319 if (access(buffer, F_OK) == 0)
320 return buffer;
322 /* position of locale in our buffer */
323 locale = buffer + strlen(menu) + 1;
325 /* check if it is in the form aa_bb.encoding and check for aa_bb */
326 ptr = strchr(locale, '.');
327 if (ptr) {
328 *ptr = 0;
329 if (access(buffer, F_OK) == 0)
330 return buffer;
333 /* now check for aa */
334 ptr = strchr(locale, '_');
335 if (ptr) {
336 *ptr = 0;
337 if (access(buffer, F_OK) == 0)
338 return buffer;
341 wfree(buffer);
343 return NULL;
346 Bool wRootMenuPerformShortcut(XEvent * event)
348 WScreen *scr = wScreenForRootWindow(event->xkey.root);
349 Shortcut *ptr;
350 int modifiers;
351 int done = 0;
353 /* ignore CapsLock */
354 modifiers = event->xkey.state & ValidModMask;
356 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
357 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
358 continue;
360 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
361 (*ptr->entry->callback) (ptr->menu, ptr->entry);
362 done = True;
366 return done;
369 void wRootMenuBindShortcuts(Window window)
371 Shortcut *ptr;
373 ptr = shortcutList;
374 while (ptr) {
375 if (ptr->modifier != AnyModifier) {
376 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
377 window, True, GrabModeAsync, GrabModeAsync);
378 #ifdef NUMLOCK_HACK
379 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
380 #endif
382 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
383 ptr = ptr->next;
387 static void rebindKeygrabs(WScreen * scr)
389 WWindow *wwin;
391 wwin = scr->focused_window;
393 while (wwin != NULL) {
394 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
396 if (!WFLAGP(wwin, no_bind_keys)) {
397 wWindowSetKeyGrabs(wwin);
399 wwin = wwin->prev;
403 static void removeShortcutsForMenu(WMenu * menu)
405 Shortcut *ptr, *tmp;
406 Shortcut *newList = NULL;
408 ptr = shortcutList;
409 while (ptr != NULL) {
410 tmp = ptr->next;
411 if (ptr->menu == menu) {
412 wfree(ptr);
413 } else {
414 ptr->next = newList;
415 newList = ptr;
417 ptr = tmp;
419 shortcutList = newList;
420 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
423 static Bool addShortcut(const char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
425 Shortcut *ptr;
426 KeySym ksym;
427 char *k;
428 char buf[MAX_SHORTCUT_LENGTH], *b;
430 ptr = wmalloc(sizeof(Shortcut));
432 wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
433 b = (char *)buf;
435 /* get modifiers */
436 ptr->modifier = 0;
437 while ((k = strchr(b, '+')) != NULL) {
438 int mod;
440 *k = 0;
441 mod = wXModifierFromKey(b);
442 if (mod < 0) {
443 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
444 wfree(ptr);
445 return False;
447 ptr->modifier |= mod;
449 b = k + 1;
452 /* get key */
453 ksym = XStringToKeysym(b);
455 if (ksym == NoSymbol) {
456 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
457 file, shortcutDefinition, entry->text);
458 wfree(ptr);
459 return False;
462 ptr->keycode = XKeysymToKeycode(dpy, ksym);
463 if (ptr->keycode == 0) {
464 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
465 shortcutDefinition, entry->text);
466 wfree(ptr);
467 return False;
470 ptr->menu = menu;
471 ptr->entry = entry;
473 ptr->next = shortcutList;
474 shortcutList = ptr;
476 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
478 return True;
481 static char *next_token(char *line, char **next)
483 char *tmp, c;
484 char *ret;
486 *next = NULL;
487 while (*line == ' ' || *line == '\t')
488 line++;
490 tmp = line;
492 if (*tmp == '"') {
493 tmp++;
494 line++;
495 while (*tmp != 0 && *tmp != '"')
496 tmp++;
497 if (*tmp != '"') {
498 wwarning(_("%s: unmatched '\"' in menu file"), line);
499 return NULL;
501 } else {
502 do {
503 if (*tmp == '\\')
504 tmp++;
506 if (*tmp != 0)
507 tmp++;
509 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
512 c = *tmp;
513 *tmp = 0;
514 ret = wstrdup(line);
515 *tmp = c;
517 if (c == 0)
518 return ret;
519 else
520 tmp++;
522 /* skip blanks */
523 while (*tmp == ' ' || *tmp == '\t')
524 tmp++;
526 if (*tmp != 0)
527 *next = tmp;
529 return ret;
532 static void separateCommand(char *line, char ***file, char **command)
534 char *token, *tmp = line;
535 WMArray *array = WMCreateArray(4);
536 int count, i;
538 *file = NULL;
539 *command = NULL;
540 do {
541 token = next_token(tmp, &tmp);
542 if (token) {
543 if (strcmp(token, "WITH") == 0) {
544 if (tmp != NULL && *tmp != 0)
545 *command = wstrdup(tmp);
546 else
547 wwarning(_("%s: missing command"), line);
548 break;
550 WMAddToArray(array, token);
552 } while (token != NULL && tmp != NULL);
554 count = WMGetArrayItemCount(array);
555 if (count > 0) {
556 *file = wmalloc(sizeof(char *) * (count + 1));
557 (*file)[count] = NULL;
558 for (i = 0; i < count; i++) {
559 (*file)[i] = WMGetFromArray(array, i);
562 WMFreeArray(array);
565 static WMenu *constructPLMenu(WScreen *screen, char *path)
567 WMPropList *pl = NULL;
568 WMenu *menu = NULL;
570 if (!path)
571 return NULL;
573 pl = WMReadPropListFromFile(path);
574 if (!pl)
575 return NULL;
577 menu = configureMenu(screen, pl, False);
579 WMReleasePropList(pl);
581 if (!menu)
582 return NULL;
584 menu->on_destroy = removeShortcutsForMenu;
585 return menu;
590 static void constructMenu(WMenu * menu, WMenuEntry * entry)
592 WMenu *submenu;
593 struct stat stat_buf;
594 char **path;
595 char *cmd;
596 char *lpath = NULL;
597 int i, first = -1;
598 time_t last = 0;
600 separateCommand((char *)entry->clientdata, &path, &cmd);
601 if (path == NULL || *path == NULL || **path == 0) {
602 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
603 if (cmd)
604 wfree(cmd);
605 return;
608 if (path[0][0] == '|') {
609 /* pipe menu */
611 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
612 /* parse pipe */
614 submenu = readMenuPipe(menu->frame->screen_ptr, path);
616 if (submenu != NULL) {
617 if (path[0][1] == '|')
618 submenu->timestamp = 0;
619 else
620 submenu->timestamp = 1; /* there's no automatic reloading */
622 } else {
623 submenu = NULL;
626 } else {
628 /* try interpreting path as a proplist file */
629 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
630 /* if unsuccessful, try it as an old-style file */
631 if (!submenu) {
633 i = 0;
634 while (path[i] != NULL) {
635 char *tmp;
637 if (strcmp(path[i], "-noext") == 0) {
638 i++;
639 continue;
642 tmp = wexpandpath(path[i]);
643 wfree(path[i]);
644 lpath = getLocalizedMenuFile(tmp);
645 if (lpath) {
646 wfree(tmp);
647 path[i] = lpath;
648 lpath = NULL;
649 } else {
650 path[i] = tmp;
653 if (stat(path[i], &stat_buf) == 0) {
654 if (last < stat_buf.st_mtime)
655 last = stat_buf.st_mtime;
656 if (first < 0)
657 first = i;
658 } else {
659 werror(_("%s:could not stat menu"), path[i]);
660 /*goto finish; */
663 i++;
666 if (first < 0) {
667 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
668 goto finish;
670 stat(path[first], &stat_buf);
671 if (!menu->cascades[entry->cascade]
672 || menu->cascades[entry->cascade]->timestamp < last) {
674 if (S_ISDIR(stat_buf.st_mode)) {
675 /* menu directory */
676 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
677 if (submenu)
678 submenu->timestamp = last;
679 } else if (S_ISREG(stat_buf.st_mode)) {
680 /* menu file */
682 if (cmd || path[1])
683 wwarning(_("too many parameters in OPEN_MENU: %s"),
684 (char *)entry->clientdata);
686 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
687 if (submenu)
688 submenu->timestamp = stat_buf.st_mtime;
689 } else {
690 submenu = NULL;
692 } else {
693 submenu = NULL;
698 if (submenu) {
699 wMenuEntryRemoveCascade(menu, entry);
700 wMenuEntrySetCascade(menu, entry, submenu);
703 finish:
704 i = 0;
705 while (path[i] != NULL)
706 wfree(path[i++]);
707 wfree(path);
708 if (cmd)
709 wfree(cmd);
712 static void constructPLMenuFromPipe(WMenu * menu, WMenuEntry * entry)
714 WMenu *submenu = NULL;
715 char **path;
716 char *cmd;
717 int i;
719 separateCommand((char *)entry->clientdata, &path, &cmd);
720 if (path == NULL || *path == NULL || **path == 0) {
721 wwarning(_("invalid OPEN_PLMENU specification: %s"),
722 (char *)entry->clientdata);
723 if (cmd)
724 wfree(cmd);
725 return;
728 if (path[0][0] == '|') {
729 /* pipe menu */
731 if (!menu->cascades[entry->cascade]
732 || menu->cascades[entry->cascade]->timestamp == 0) {
733 /* parse pipe */
734 submenu = readPLMenuPipe(menu->frame->screen_ptr, path);
736 if (submenu != NULL) {
737 if (path[0][1] == '|')
738 submenu->timestamp = 0;
739 else
740 submenu->timestamp = 1; /* there's no automatic reloading */
745 if (submenu) {
746 wMenuEntryRemoveCascade(menu, entry);
747 wMenuEntrySetCascade(menu, entry, submenu);
750 i = 0;
751 while (path[i] != NULL)
752 wfree(path[i++]);
754 wfree(path);
755 if (cmd)
756 wfree(cmd);
759 static void cleanupWorkspaceMenu(WMenu * menu)
761 if (menu->frame->screen_ptr->workspace_menu == menu)
762 menu->frame->screen_ptr->workspace_menu = NULL;
765 static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
767 WMenu *wsmenu;
768 WMenuEntry *entry;
770 if (scr->flags.added_workspace_menu) {
771 wwarning(_
772 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
773 return NULL;
774 } else {
775 scr->flags.added_workspace_menu = 1;
777 wsmenu = wWorkspaceMenuMake(scr, True);
778 wsmenu->on_destroy = cleanupWorkspaceMenu;
780 scr->workspace_menu = wsmenu;
781 entry = wMenuAddCallback(menu, title, NULL, NULL);
782 wMenuEntrySetCascade(menu, entry, wsmenu);
784 wWorkspaceMenuUpdate(scr, wsmenu);
786 return entry;
789 static void cleanupWindowsMenu(WMenu * menu)
791 if (menu->frame->screen_ptr->switch_menu == menu)
792 menu->frame->screen_ptr->switch_menu = NULL;
795 static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
797 WMenu *wwmenu;
798 WWindow *wwin;
799 WMenuEntry *entry;
801 if (scr->flags.added_windows_menu) {
802 wwarning(_
803 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
804 return NULL;
805 } else {
806 scr->flags.added_windows_menu = 1;
808 wwmenu = wMenuCreate(scr, _("Window List"), False);
809 wwmenu->on_destroy = cleanupWindowsMenu;
810 scr->switch_menu = wwmenu;
811 wwin = scr->focused_window;
812 while (wwin) {
813 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
815 wwin = wwin->prev;
817 entry = wMenuAddCallback(menu, title, NULL, NULL);
818 wMenuEntrySetCascade(menu, entry, wwmenu);
820 return entry;
823 static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
824 char *params, const char *file_name)
826 WScreen *scr;
827 WMenuEntry *entry = NULL;
828 Bool shortcutOk = False;
830 if (!menu)
831 return NULL;
832 scr = menu->frame->screen_ptr;
833 if (strcmp(command, "OPEN_MENU") == 0) {
834 if (!params) {
835 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
836 } else {
837 WMenu *dummy;
838 char *path;
840 path = wfindfile(DEF_CONFIG_PATHS, params);
841 if (!path) {
842 path = wstrdup(params);
844 dummy = wMenuCreate(scr, title, False);
845 dummy->on_destroy = removeShortcutsForMenu;
846 entry = wMenuAddCallback(menu, title, constructMenu, path);
847 entry->free_cdata = wfree;
848 wMenuEntrySetCascade(menu, entry, dummy);
850 } else if (strcmp(command, "OPEN_PLMENU") == 0) {
851 if (!params) {
852 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
853 } else {
854 WMenu *dummy;
855 char *path;
857 path = wfindfile(DEF_CONFIG_PATHS, params);
858 if (!path)
859 path = wstrdup(params);
861 dummy = wMenuCreate(scr, title, False);
862 dummy->on_destroy = removeShortcutsForMenu;
863 entry = wMenuAddCallback(menu, title, constructPLMenuFromPipe, path);
864 entry->free_cdata = wfree;
865 wMenuEntrySetCascade(menu, entry, dummy);
867 } else if (strcmp(command, "EXEC") == 0) {
868 if (!params)
869 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
870 else {
871 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
872 entry->free_cdata = wfree;
873 shortcutOk = True;
875 } else if (strcmp(command, "SHEXEC") == 0) {
876 if (!params)
877 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
878 else {
879 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
880 entry->free_cdata = wfree;
881 shortcutOk = True;
883 } else if (strcmp(command, "EXIT") == 0) {
885 if (params && strcmp(params, "QUICK") == 0)
886 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
887 else
888 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
890 shortcutOk = True;
891 } else if (strcmp(command, "SHUTDOWN") == 0) {
893 if (params && strcmp(params, "QUICK") == 0)
894 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
895 else
896 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
898 shortcutOk = True;
899 } else if (strcmp(command, "REFRESH") == 0) {
900 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
902 shortcutOk = True;
903 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
904 entry = addWorkspaceMenu(scr, menu, title);
906 shortcutOk = True;
907 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
908 entry = addWindowsMenu(scr, menu, title);
910 shortcutOk = True;
911 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
912 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
914 shortcutOk = True;
915 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
916 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
918 shortcutOk = True;
919 } else if (strcmp(command, "SHOW_ALL") == 0) {
920 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
922 shortcutOk = True;
923 } else if (strcmp(command, "RESTART") == 0) {
924 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
925 entry->free_cdata = wfree;
926 shortcutOk = True;
927 } else if (strcmp(command, "SAVE_SESSION") == 0) {
928 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
930 shortcutOk = True;
931 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
932 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
933 shortcutOk = True;
934 } else if (strcmp(command, "INFO_PANEL") == 0) {
935 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
936 shortcutOk = True;
937 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
938 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
939 shortcutOk = True;
940 } else {
941 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
943 return NULL;
946 if (shortcut && entry) {
947 if (!shortcutOk) {
948 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
949 } else {
950 if (addShortcut(file_name, shortcut, menu, entry)) {
952 entry->rtext = GetShortcutString(shortcut);
954 entry->rtext = wstrdup(shortcut);
960 return entry;
963 /******************* Menu Configuration From File *******************/
965 static void freeline(char *title, char *command, char *parameter, char *shortcut)
967 wfree(title);
968 wfree(command);
969 wfree(parameter);
970 wfree(shortcut);
973 static WMenu *parseCascade(WScreen * scr, WMenu * menu, WMenuParser parser)
975 char *command, *params, *shortcut, *title;
977 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
979 if (command == NULL || !command[0]) {
980 WMenuParserError(parser, _("missing command in menu config") );
981 freeline(title, command, params, shortcut);
982 goto error;
985 if (strcasecmp(command, "MENU") == 0) {
986 WMenu *cascade;
988 /* start submenu */
990 cascade = wMenuCreate(scr, M_(title), False);
991 cascade->on_destroy = removeShortcutsForMenu;
992 if (!parseCascade(scr, cascade, parser)) {
993 wMenuDestroy(cascade, True);
994 } else {
995 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
997 } else if (strcasecmp(command, "END") == 0) {
998 /* end of menu */
999 freeline(title, command, params, shortcut);
1000 return menu;
1001 } else {
1002 /* normal items */
1003 addMenuEntry(menu, M_(title), shortcut, command, params, WMenuParserGetFilename(parser));
1005 freeline(title, command, params, shortcut);
1008 WMenuParserError(parser, _("syntax error in menu file: END declaration missing") );
1010 error:
1011 return NULL;
1014 static WMenu *readMenuFile(WScreen * scr, char *file_name)
1016 WMenu *menu = NULL;
1017 FILE *file = NULL;
1018 WMenuParser parser;
1019 char *command, *params, *shortcut, *title;
1021 file = fopen(file_name, "rb");
1022 if (!file) {
1023 werror(_("%s:could not open menu file"), file_name);
1024 return NULL;
1026 parser = WMenuParserCreate(file_name, file, DEF_CONFIG_PATHS);
1027 menu_parser_register_macros(parser);
1029 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1031 if (command == NULL || !command[0]) {
1032 WMenuParserError(parser, _("missing command in menu config") );
1033 freeline(title, command, params, shortcut);
1034 break;
1036 if (strcasecmp(command, "MENU") == 0) {
1037 menu = wMenuCreate(scr, M_(title), True);
1038 menu->on_destroy = removeShortcutsForMenu;
1039 if (!parseCascade(scr, menu, parser)) {
1040 wMenuDestroy(menu, True);
1041 menu = NULL;
1043 freeline(title, command, params, shortcut);
1044 break;
1045 } else {
1046 WMenuParserError(parser, _("invalid menu file, MENU command is missing") );
1047 freeline(title, command, params, shortcut);
1048 break;
1050 freeline(title, command, params, shortcut);
1053 WMenuParserDelete(parser);
1054 fclose(file);
1056 return menu;
1059 /************ Menu Configuration From Pipe *************/
1060 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name)
1062 WMPropList *plist = NULL;
1063 WMenu *menu = NULL;
1064 char *filename;
1065 char flat_file[MAXLINE];
1066 int i;
1068 flat_file[0] = '\0';
1070 for (i = 0; file_name[i] != NULL; i++) {
1071 strcat(flat_file, file_name[i]);
1072 strcat(flat_file, " ");
1074 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1076 plist = WMReadPropListFromPipe(filename);
1078 if (!plist)
1079 return NULL;
1081 menu = configureMenu(scr, plist, False);
1083 WMReleasePropList(plist);
1085 if (!menu)
1086 return NULL;
1088 menu->on_destroy = removeShortcutsForMenu;
1089 return menu;
1092 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1094 WMenu *menu = NULL;
1095 FILE *file = NULL;
1096 WMenuParser parser;
1097 char *command, *params, *shortcut, *title;
1098 char *filename;
1099 char flat_file[MAXLINE];
1100 int i;
1102 flat_file[0] = '\0';
1104 for (i = 0; file_name[i] != NULL; i++) {
1105 strcat(flat_file, file_name[i]);
1106 strcat(flat_file, " ");
1108 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1110 file = popen(filename, "r");
1111 if (!file) {
1112 werror(_("%s:could not open menu file"), filename);
1113 return NULL;
1115 parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS);
1116 menu_parser_register_macros(parser);
1118 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1120 if (command == NULL || !command[0]) {
1121 WMenuParserError(parser, _("missing command in menu config") );
1122 freeline(title, command, params, shortcut);
1123 break;
1125 if (strcasecmp(command, "MENU") == 0) {
1126 menu = wMenuCreate(scr, M_(title), True);
1127 menu->on_destroy = removeShortcutsForMenu;
1128 if (!parseCascade(scr, menu, parser)) {
1129 wMenuDestroy(menu, True);
1130 menu = NULL;
1132 freeline(title, command, params, shortcut);
1133 break;
1134 } else {
1135 WMenuParserError(parser, _("no title given for the root menu") );
1136 freeline(title, command, params, shortcut);
1137 break;
1140 freeline(title, command, params, shortcut);
1143 WMenuParserDelete(parser);
1144 pclose(file);
1146 return menu;
1149 typedef struct {
1150 char *name;
1151 int index;
1152 } dir_data;
1154 static int myCompare(const void *d1, const void *d2)
1156 dir_data *p1 = *(dir_data **) d1;
1157 dir_data *p2 = *(dir_data **) d2;
1159 return strcmp(p1->name, p2->name);
1162 /***** Preset some macro for file parser *****/
1163 static void menu_parser_register_macros(WMenuParser parser)
1165 Visual *visual;
1166 char buf[32];
1168 // Used to return CPP verion, now returns wmaker's version
1169 WMenuParserRegisterSimpleMacro(parser, "__VERSION__", VERSION);
1171 // All macros below were historically defined by WindowMaker
1172 visual = DefaultVisual(dpy, DefaultScreen(dpy));
1173 snprintf(buf, sizeof(buf), "%d", visual->class);
1174 WMenuParserRegisterSimpleMacro(parser, "VISUAL", buf);
1176 snprintf(buf, sizeof(buf), "%d", DefaultDepth(dpy, DefaultScreen(dpy)) );
1177 WMenuParserRegisterSimpleMacro(parser, "DEPTH", buf);
1179 snprintf(buf, sizeof(buf), "%d", WidthOfScreen(DefaultScreenOfDisplay(dpy)) );
1180 WMenuParserRegisterSimpleMacro(parser, "SCR_WIDTH", buf);
1182 snprintf(buf, sizeof(buf), "%d", HeightOfScreen(DefaultScreenOfDisplay(dpy)) );
1183 WMenuParserRegisterSimpleMacro(parser, "SCR_HEIGHT", buf);
1185 WMenuParserRegisterSimpleMacro(parser, "DISPLAY", XDisplayName(DisplayString(dpy)) );
1187 WMenuParserRegisterSimpleMacro(parser, "WM_VERSION", "\"" VERSION "\"");
1190 /************ Menu Configuration From Directory *************/
1192 static Bool isFilePackage(char *file)
1194 int l;
1196 /* check if the extension indicates this file is a
1197 * file package. For now, only recognize .themed */
1199 l = strlen(file);
1201 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1202 return True;
1203 } else {
1204 return False;
1208 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
1210 DIR *dir;
1211 struct dirent *dentry;
1212 struct stat stat_buf;
1213 WMenu *menu = NULL;
1214 char *buffer;
1215 WMArray *dirs = NULL, *files = NULL;
1216 WMArrayIterator iter;
1217 int length, i, have_space = 0;
1218 dir_data *data;
1219 int stripExtension = 0;
1221 dirs = WMCreateArray(16);
1222 files = WMCreateArray(16);
1224 i = 0;
1225 while (path[i] != NULL) {
1226 if (strcmp(path[i], "-noext") == 0) {
1227 stripExtension = 1;
1228 i++;
1229 continue;
1232 dir = opendir(path[i]);
1233 if (!dir) {
1234 i++;
1235 continue;
1238 while ((dentry = readdir(dir))) {
1240 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1241 continue;
1243 if (dentry->d_name[0] == '.')
1244 continue;
1246 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1247 if (!buffer) {
1248 werror(_("out of memory while constructing directory menu %s"), path[i]);
1249 break;
1252 strcpy(buffer, path[i]);
1253 strcat(buffer, "/");
1254 strcat(buffer, dentry->d_name);
1256 if (stat(buffer, &stat_buf) != 0) {
1257 werror(_("%s:could not stat file \"%s\" in menu directory"),
1258 path[i], dentry->d_name);
1259 } else {
1260 Bool isFilePack = False;
1262 data = NULL;
1263 if (S_ISDIR(stat_buf.st_mode)
1264 && !(isFilePack = isFilePackage(dentry->d_name))) {
1266 /* access always returns success for user root */
1267 if (access(buffer, X_OK) == 0) {
1268 /* Directory is accesible. Add to directory list */
1270 data = (dir_data *) wmalloc(sizeof(dir_data));
1271 data->name = wstrdup(dentry->d_name);
1272 data->index = i;
1274 WMAddToArray(dirs, data);
1276 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1277 /* Hack because access always returns X_OK success for user root */
1278 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1279 if ((command != NULL && access(buffer, R_OK) == 0) ||
1280 (command == NULL && access(buffer, X_OK) == 0 &&
1281 (stat_buf.st_mode & S_IXANY))) {
1283 data = (dir_data *) wmalloc(sizeof(dir_data));
1284 data->name = wstrdup(dentry->d_name);
1285 data->index = i;
1287 WMAddToArray(files, data);
1291 free(buffer);
1294 closedir(dir);
1295 i++;
1298 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1299 WMFreeArray(dirs);
1300 WMFreeArray(files);
1301 return NULL;
1304 WMSortArray(dirs, myCompare);
1305 WMSortArray(files, myCompare);
1307 menu = wMenuCreate(scr, M_(title), False);
1308 menu->on_destroy = removeShortcutsForMenu;
1310 WM_ITERATE_ARRAY(dirs, data, iter) {
1311 /* New directory. Use same OPEN_MENU command that was used
1312 * for the current directory. */
1313 length = strlen(path[data->index]) + strlen(data->name) + 6;
1314 if (stripExtension)
1315 length += 7;
1316 if (command)
1317 length += strlen(command) + 6;
1318 buffer = malloc(length);
1319 if (!buffer) {
1320 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1321 break;
1324 buffer[0] = '\0';
1325 if (stripExtension)
1326 strcat(buffer, "-noext ");
1328 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1330 if (have_space)
1331 strcat(buffer, "\"");
1332 strcat(buffer, path[data->index]);
1334 strcat(buffer, "/");
1335 strcat(buffer, data->name);
1336 if (have_space)
1337 strcat(buffer, "\"");
1338 if (command) {
1339 strcat(buffer, " WITH ");
1340 strcat(buffer, command);
1343 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1345 wfree(buffer);
1346 if (data->name)
1347 wfree(data->name);
1348 wfree(data);
1351 WM_ITERATE_ARRAY(files, data, iter) {
1352 /* executable: add as entry */
1353 length = strlen(path[data->index]) + strlen(data->name) + 6;
1354 if (command)
1355 length += strlen(command);
1357 buffer = malloc(length);
1358 if (!buffer) {
1359 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1360 break;
1363 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1364 if (command != NULL) {
1365 strcpy(buffer, command);
1366 strcat(buffer, " ");
1367 if (have_space)
1368 strcat(buffer, "\"");
1369 strcat(buffer, path[data->index]);
1370 } else {
1371 if (have_space) {
1372 buffer[0] = '"';
1373 buffer[1] = 0;
1374 strcat(buffer, path[data->index]);
1375 } else {
1376 strcpy(buffer, path[data->index]);
1379 strcat(buffer, "/");
1380 strcat(buffer, data->name);
1381 if (have_space)
1382 strcat(buffer, "\"");
1384 if (stripExtension) {
1385 char *ptr = strrchr(data->name, '.');
1386 if (ptr && ptr != data->name)
1387 *ptr = 0;
1389 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1391 wfree(buffer);
1392 if (data->name)
1393 wfree(data->name);
1394 wfree(data);
1397 WMFreeArray(files);
1398 WMFreeArray(dirs);
1400 return menu;
1403 /************ Menu Configuration From WMRootMenu *************/
1405 static WMenu *makeDefaultMenu(WScreen * scr)
1407 WMenu *menu = NULL;
1409 menu = wMenuCreate(scr, _("Commands"), True);
1410 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1411 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1412 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1413 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1414 return menu;
1418 *----------------------------------------------------------------------
1419 * configureMenu--
1420 * Reads root menu configuration from defaults database.
1422 *----------------------------------------------------------------------
1424 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1426 WMenu *menu = NULL;
1427 WMPropList *elem;
1428 int i, count;
1429 WMPropList *title, *command, *params;
1430 char *tmp, *mtitle;
1432 if (WMIsPLString(definition)) {
1433 struct stat stat_buf;
1434 char *path = NULL;
1435 Bool menu_is_default = False;
1437 /* menu definition is a string. Probably a path, so parse the file */
1439 tmp = wexpandpath(WMGetFromPLString(definition));
1441 path = getLocalizedMenuFile(tmp);
1443 if (!path)
1444 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1446 if (!path) {
1447 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1448 menu_is_default = True;
1451 if (!path) {
1452 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1453 wfree(tmp);
1454 return NULL;
1457 if (stat(path, &stat_buf) < 0) {
1458 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1459 wfree(path);
1460 wfree(tmp);
1461 return NULL;
1464 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1465 /* if the pointer in WMRootMenu has changed */
1466 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1468 if (menu_is_default) {
1469 wwarning(_
1470 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1471 path);
1474 menu = readMenuFile(scr, path);
1475 if (menu)
1476 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1477 } else {
1478 menu = NULL;
1480 wfree(path);
1481 wfree(tmp);
1483 return menu;
1486 count = WMGetPropListItemCount(definition);
1487 if (count == 0)
1488 return NULL;
1490 elem = WMGetFromPLArray(definition, 0);
1491 if (!WMIsPLString(elem)) {
1492 tmp = WMGetPropListDescription(elem, False);
1493 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1494 wfree(tmp);
1495 return NULL;
1497 mtitle = WMGetFromPLString(elem);
1499 menu = wMenuCreate(scr, M_(mtitle), False);
1500 menu->on_destroy = removeShortcutsForMenu;
1502 #ifdef GLOBAL_SUBMENU_FILE
1503 if (includeGlobals) {
1504 WMenu *submenu;
1505 WMenuEntry *mentry;
1507 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1509 if (submenu) {
1510 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1511 wMenuEntrySetCascade(menu, mentry, submenu);
1514 #endif
1516 for (i = 1; i < count; i++) {
1517 elem = WMGetFromPLArray(definition, i);
1518 #if 0
1519 if (WMIsPLString(elem)) {
1520 char *file;
1522 file = WMGetFromPLString(elem);
1525 #endif
1526 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1527 goto error;
1529 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1530 WMenu *submenu;
1531 WMenuEntry *mentry;
1533 /* submenu */
1534 submenu = configureMenu(scr, elem, True);
1535 if (submenu) {
1536 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1537 wMenuEntrySetCascade(menu, mentry, submenu);
1539 } else {
1540 int idx = 0;
1541 WMPropList *shortcut;
1542 /* normal entry */
1544 title = WMGetFromPLArray(elem, idx++);
1545 shortcut = WMGetFromPLArray(elem, idx++);
1546 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1547 shortcut = WMGetFromPLArray(elem, idx++);
1548 command = WMGetFromPLArray(elem, idx++);
1549 } else {
1550 command = shortcut;
1551 shortcut = NULL;
1553 params = WMGetFromPLArray(elem, idx++);
1555 if (!title || !command)
1556 goto error;
1558 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1559 shortcut ? WMGetFromPLString(shortcut) : NULL,
1560 WMGetFromPLString(command),
1561 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1563 continue;
1565 error:
1566 tmp = WMGetPropListDescription(elem, False);
1567 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1568 wfree(tmp);
1571 return menu;
1575 *----------------------------------------------------------------------
1576 * OpenRootMenu--
1577 * Opens the root menu, parsing the menu configuration from the
1578 * defaults database.
1579 * If the menu is already mapped and is not sticked to the
1580 * root window, it will be unmapped.
1582 * Side effects:
1583 * The menu may be remade.
1585 * Notes:
1586 * Construction of OPEN_MENU entries are delayed to the moment the
1587 * user map's them.
1588 *----------------------------------------------------------------------
1590 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1592 WMenu *menu = NULL;
1593 WMPropList *definition;
1595 static WMPropList *domain=NULL;
1597 if (!domain) {
1598 domain = WMCreatePLString("WMRootMenu");
1602 scr->flags.root_menu_changed_shortcuts = 0;
1603 scr->flags.added_workspace_menu = 0;
1604 scr->flags.added_windows_menu = 0;
1606 if (scr->root_menu && scr->root_menu->flags.mapped) {
1607 menu = scr->root_menu;
1608 if (!menu->flags.buttoned) {
1609 wMenuUnmap(menu);
1610 } else {
1611 wRaiseFrame(menu->frame->core);
1613 if (keyboard)
1614 wMenuMapAt(menu, 0, 0, True);
1615 else
1616 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1618 return;
1621 definition = WDRootMenu->dictionary;
1624 definition = PLGetDomain(domain);
1626 if (definition) {
1627 if (WMIsPLArray(definition)) {
1628 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1629 menu = configureMenu(scr, definition, True);
1630 if (menu)
1631 menu->timestamp = WDRootMenu->timestamp;
1633 } else
1634 menu = NULL;
1635 } else {
1636 menu = configureMenu(scr, definition, True);
1640 if (!menu) {
1641 /* menu hasn't changed or could not be read */
1642 if (!scr->root_menu) {
1643 wMessageDialog(scr, _("Error"),
1644 _("The applications menu could not be loaded. "
1645 "Look at the console output for a detailed "
1646 "description of the errors."), _("OK"), NULL, NULL);
1648 menu = makeDefaultMenu(scr);
1649 scr->root_menu = menu;
1651 menu = scr->root_menu;
1652 } else {
1653 /* new root menu */
1654 if (scr->root_menu) {
1655 wMenuDestroy(scr->root_menu, True);
1657 scr->root_menu = menu;
1659 if (menu) {
1660 int newx, newy;
1662 if (keyboard && x == 0 && y == 0) {
1663 newx = newy = 0;
1664 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1665 newx = x - menu->frame->core->width / 2;
1666 newy = y - menu->frame->core->height / 2;
1667 } else {
1668 newx = x - menu->frame->core->width / 2;
1669 newy = y;
1671 wMenuMapAt(menu, newx, newy, keyboard);
1674 if (scr->flags.root_menu_changed_shortcuts)
1675 rebindKeygrabs(scr);