28bed8095b83559f0fdef8695aa9987d8a475350
[wmaker-crm.git] / src / rootmenu.c
blob28bed8095b83559f0fdef8695aa9987d8a475350
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"
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
97 * Commands may be preceded by SHORTCUT key
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.
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);
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;
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;
243 if (result != R_CANCEL) {
244 Shutdown(WSKillMode);
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;
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;
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;
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
381 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
382 ptr = ptr->next;
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);
398 wwin = wwin->prev;
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;
416 ptr = tmp;
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;
446 ptr->modifier |= mod;
448 b = k + 1;
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;
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;
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;
500 } else {
501 do {
502 if (*tmp == '\\')
503 tmp++;
505 if (*tmp != 0)
506 tmp++;
508 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
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;
549 WMAddToArray(array, token);
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);
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);
578 WMReleasePropList(pl);
580 if (!menu)
581 return NULL;
583 menu->on_destroy = removeShortcutsForMenu;
584 return menu;
589 static void constructMenu(WMenu * menu, WMenuEntry * entry)
591 WMenu *submenu;
592 struct stat stat_buf;
593 char **path;
594 char *cmd;
595 char *lpath = NULL;
596 int i, first = -1;
597 time_t last = 0;
599 separateCommand((char *)entry->clientdata, &path, &cmd);
600 if (path == NULL || *path == NULL || **path == 0) {
601 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
602 if (cmd)
603 wfree(cmd);
604 return;
607 if (path[0][0] == '|') {
608 /* pipe menu */
610 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
611 /* parse pipe */
613 submenu = readMenuPipe(menu->frame->screen_ptr, path);
615 if (submenu != NULL) {
616 if (path[0][1] == '|')
617 submenu->timestamp = 0;
618 else
619 submenu->timestamp = 1; /* there's no automatic reloading */
621 } else {
622 submenu = NULL;
625 } else {
627 /* try interpreting path as a proplist file */
628 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
629 /* if unsuccessful, try it as an old-style file */
630 if (!submenu) {
632 i = 0;
633 while (path[i] != NULL) {
634 char *tmp;
636 if (strcmp(path[i], "-noext") == 0) {
637 i++;
638 continue;
641 tmp = wexpandpath(path[i]);
642 wfree(path[i]);
643 lpath = getLocalizedMenuFile(tmp);
644 if (lpath) {
645 wfree(tmp);
646 path[i] = lpath;
647 lpath = NULL;
648 } else {
649 path[i] = tmp;
652 if (stat(path[i], &stat_buf) == 0) {
653 if (last < stat_buf.st_mtime)
654 last = stat_buf.st_mtime;
655 if (first < 0)
656 first = i;
657 } else {
658 werror(_("%s:could not stat menu"), path[i]);
659 /*goto finish; */
662 i++;
665 if (first < 0) {
666 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
667 goto finish;
669 stat(path[first], &stat_buf);
670 if (!menu->cascades[entry->cascade]
671 || menu->cascades[entry->cascade]->timestamp < last) {
673 if (S_ISDIR(stat_buf.st_mode)) {
674 /* menu directory */
675 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
676 if (submenu)
677 submenu->timestamp = last;
678 } else if (S_ISREG(stat_buf.st_mode)) {
679 /* menu file */
681 if (cmd || path[1])
682 wwarning(_("too many parameters in OPEN_MENU: %s"),
683 (char *)entry->clientdata);
685 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
686 if (submenu)
687 submenu->timestamp = stat_buf.st_mtime;
688 } else {
689 submenu = NULL;
691 } else {
692 submenu = NULL;
697 if (submenu) {
698 wMenuEntryRemoveCascade(menu, entry);
699 wMenuEntrySetCascade(menu, entry, submenu);
702 finish:
703 i = 0;
704 while (path[i] != NULL)
705 wfree(path[i++]);
706 wfree(path);
707 if (cmd)
708 wfree(cmd);
711 static void constructPLMenuFromPipe(WMenu * menu, WMenuEntry * entry)
713 WMenu *submenu = NULL;
714 char **path;
715 char *cmd;
716 int i;
718 separateCommand((char *)entry->clientdata, &path, &cmd);
719 if (path == NULL || *path == NULL || **path == 0) {
720 wwarning(_("invalid OPEN_PLMENU specification: %s"),
721 (char *)entry->clientdata);
722 if (cmd)
723 wfree(cmd);
724 return;
727 if (path[0][0] == '|') {
728 /* pipe menu */
730 if (!menu->cascades[entry->cascade]
731 || menu->cascades[entry->cascade]->timestamp == 0) {
732 /* parse pipe */
733 submenu = readPLMenuPipe(menu->frame->screen_ptr, path);
735 if (submenu != NULL) {
736 if (path[0][1] == '|')
737 submenu->timestamp = 0;
738 else
739 submenu->timestamp = 1; /* there's no automatic reloading */
744 if (submenu) {
745 wMenuEntryRemoveCascade(menu, entry);
746 wMenuEntrySetCascade(menu, entry, submenu);
749 i = 0;
750 while (path[i] != NULL)
751 wfree(path[i++]);
753 wfree(path);
754 if (cmd)
755 wfree(cmd);
758 static void cleanupWorkspaceMenu(WMenu * menu)
760 if (menu->frame->screen_ptr->workspace_menu == menu)
761 menu->frame->screen_ptr->workspace_menu = NULL;
764 static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
766 WMenu *wsmenu;
767 WMenuEntry *entry;
769 if (scr->flags.added_workspace_menu) {
770 wwarning(_
771 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
772 return NULL;
773 } else {
774 scr->flags.added_workspace_menu = 1;
776 wsmenu = wWorkspaceMenuMake(scr, True);
777 wsmenu->on_destroy = cleanupWorkspaceMenu;
779 scr->workspace_menu = wsmenu;
780 entry = wMenuAddCallback(menu, title, NULL, NULL);
781 wMenuEntrySetCascade(menu, entry, wsmenu);
783 wWorkspaceMenuUpdate(scr, wsmenu);
785 return entry;
788 static void cleanupWindowsMenu(WMenu * menu)
790 if (menu->frame->screen_ptr->switch_menu == menu)
791 menu->frame->screen_ptr->switch_menu = NULL;
794 static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
796 WMenu *wwmenu;
797 WWindow *wwin;
798 WMenuEntry *entry;
800 if (scr->flags.added_windows_menu) {
801 wwarning(_
802 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
803 return NULL;
804 } else {
805 scr->flags.added_windows_menu = 1;
807 wwmenu = wMenuCreate(scr, _("Window List"), False);
808 wwmenu->on_destroy = cleanupWindowsMenu;
809 scr->switch_menu = wwmenu;
810 wwin = scr->focused_window;
811 while (wwin) {
812 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
814 wwin = wwin->prev;
816 entry = wMenuAddCallback(menu, title, NULL, NULL);
817 wMenuEntrySetCascade(menu, entry, wwmenu);
819 return entry;
822 static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
823 char *params, const char *file_name)
825 WScreen *scr;
826 WMenuEntry *entry = NULL;
827 Bool shortcutOk = False;
829 if (!menu)
830 return NULL;
831 scr = menu->frame->screen_ptr;
832 if (strcmp(command, "OPEN_MENU") == 0) {
833 if (!params) {
834 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
835 } else {
836 WMenu *dummy;
837 char *path;
839 path = wfindfile(DEF_CONFIG_PATHS, params);
840 if (!path) {
841 path = wstrdup(params);
843 dummy = wMenuCreate(scr, title, False);
844 dummy->on_destroy = removeShortcutsForMenu;
845 entry = wMenuAddCallback(menu, title, constructMenu, path);
846 entry->free_cdata = wfree;
847 wMenuEntrySetCascade(menu, entry, dummy);
849 } else if (strcmp(command, "OPEN_PLMENU") == 0) {
850 if (!params) {
851 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
852 } else {
853 WMenu *dummy;
854 char *path;
856 path = wfindfile(DEF_CONFIG_PATHS, params);
857 if (!path)
858 path = wstrdup(params);
860 dummy = wMenuCreate(scr, title, False);
861 dummy->on_destroy = removeShortcutsForMenu;
862 entry = wMenuAddCallback(menu, title, constructPLMenuFromPipe, path);
863 entry->free_cdata = wfree;
864 wMenuEntrySetCascade(menu, entry, dummy);
866 } else if (strcmp(command, "EXEC") == 0) {
867 if (!params)
868 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
869 else {
870 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
871 entry->free_cdata = wfree;
872 shortcutOk = True;
874 } else if (strcmp(command, "SHEXEC") == 0) {
875 if (!params)
876 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
877 else {
878 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
879 entry->free_cdata = wfree;
880 shortcutOk = True;
882 } else if (strcmp(command, "EXIT") == 0) {
884 if (params && strcmp(params, "QUICK") == 0)
885 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
886 else
887 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
889 shortcutOk = True;
890 } else if (strcmp(command, "SHUTDOWN") == 0) {
892 if (params && strcmp(params, "QUICK") == 0)
893 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
894 else
895 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
897 shortcutOk = True;
898 } else if (strcmp(command, "REFRESH") == 0) {
899 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
901 shortcutOk = True;
902 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
903 entry = addWorkspaceMenu(scr, menu, title);
905 shortcutOk = True;
906 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
907 entry = addWindowsMenu(scr, menu, title);
909 shortcutOk = True;
910 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
911 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
913 shortcutOk = True;
914 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
915 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
917 shortcutOk = True;
918 } else if (strcmp(command, "SHOW_ALL") == 0) {
919 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
921 shortcutOk = True;
922 } else if (strcmp(command, "RESTART") == 0) {
923 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
924 entry->free_cdata = wfree;
925 shortcutOk = True;
926 } else if (strcmp(command, "SAVE_SESSION") == 0) {
927 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
929 shortcutOk = True;
930 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
931 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
932 shortcutOk = True;
933 } else if (strcmp(command, "INFO_PANEL") == 0) {
934 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
935 shortcutOk = True;
936 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
937 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
938 shortcutOk = True;
939 } else {
940 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
942 return NULL;
945 if (shortcut && entry) {
946 if (!shortcutOk) {
947 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
948 } else {
949 if (addShortcut(file_name, shortcut, menu, entry)) {
951 entry->rtext = GetShortcutString(shortcut);
953 entry->rtext = wstrdup(shortcut);
959 return entry;
962 /******************* Menu Configuration From File *******************/
964 static void freeline(char *title, char *command, char *parameter, char *shortcut)
966 wfree(title);
967 wfree(command);
968 wfree(parameter);
969 wfree(shortcut);
972 static WMenu *parseCascade(WScreen * scr, WMenu * menu, WMenuParser parser)
974 char *command, *params, *shortcut, *title;
976 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
978 if (command == NULL || !command[0]) {
979 WMenuParserError(parser, _("missing command in menu config") );
980 freeline(title, command, params, shortcut);
981 goto error;
984 if (strcasecmp(command, "MENU") == 0) {
985 WMenu *cascade;
987 /* start submenu */
989 cascade = wMenuCreate(scr, M_(title), False);
990 cascade->on_destroy = removeShortcutsForMenu;
991 if (!parseCascade(scr, cascade, parser)) {
992 wMenuDestroy(cascade, True);
993 } else {
994 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
996 } else if (strcasecmp(command, "END") == 0) {
997 /* end of menu */
998 freeline(title, command, params, shortcut);
999 return menu;
1000 } else {
1001 /* normal items */
1002 addMenuEntry(menu, M_(title), shortcut, command, params, WMenuParserGetFilename(parser));
1004 freeline(title, command, params, shortcut);
1007 WMenuParserError(parser, _("syntax error in menu file: END declaration missing") );
1009 error:
1010 return NULL;
1013 static WMenu *readMenuFile(WScreen * scr, char *file_name)
1015 WMenu *menu = NULL;
1016 FILE *file = NULL;
1017 WMenuParser parser;
1018 char *command, *params, *shortcut, *title;
1020 file = fopen(file_name, "rb");
1021 if (!file) {
1022 werror(_("%s:could not open menu file"), file_name);
1023 return NULL;
1025 parser = WMenuParserCreate(file_name, file, DEF_CONFIG_PATHS);
1026 menu_parser_register_macros(parser);
1028 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1030 if (command == NULL || !command[0]) {
1031 WMenuParserError(parser, _("missing command in menu config") );
1032 freeline(title, command, params, shortcut);
1033 break;
1035 if (strcasecmp(command, "MENU") == 0) {
1036 menu = wMenuCreate(scr, M_(title), True);
1037 menu->on_destroy = removeShortcutsForMenu;
1038 if (!parseCascade(scr, menu, parser)) {
1039 wMenuDestroy(menu, True);
1040 menu = NULL;
1042 freeline(title, command, params, shortcut);
1043 break;
1044 } else {
1045 WMenuParserError(parser, _("invalid menu file, MENU command is missing") );
1046 freeline(title, command, params, shortcut);
1047 break;
1049 freeline(title, command, params, shortcut);
1052 WMenuParserDelete(parser);
1053 fclose(file);
1055 return menu;
1058 /************ Menu Configuration From Pipe *************/
1059 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name)
1061 WMPropList *plist = NULL;
1062 WMenu *menu = NULL;
1063 char *filename;
1064 char flat_file[MAXLINE];
1065 int i;
1067 flat_file[0] = '\0';
1069 for (i = 0; file_name[i] != NULL; i++) {
1070 strcat(flat_file, file_name[i]);
1071 strcat(flat_file, " ");
1073 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1075 plist = WMReadPropListFromPipe(filename);
1077 if (!plist)
1078 return NULL;
1080 menu = configureMenu(scr, plist, False);
1082 WMReleasePropList(plist);
1084 if (!menu)
1085 return NULL;
1087 menu->on_destroy = removeShortcutsForMenu;
1088 return menu;
1091 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1093 WMenu *menu = NULL;
1094 FILE *file = NULL;
1095 WMenuParser parser;
1096 char *command, *params, *shortcut, *title;
1097 char *filename;
1098 char flat_file[MAXLINE];
1099 int i;
1101 flat_file[0] = '\0';
1103 for (i = 0; file_name[i] != NULL; i++) {
1104 strcat(flat_file, file_name[i]);
1105 strcat(flat_file, " ");
1107 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1109 file = popen(filename, "r");
1110 if (!file) {
1111 werror(_("%s:could not open menu file"), filename);
1112 return NULL;
1114 parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS);
1115 menu_parser_register_macros(parser);
1117 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1119 if (command == NULL || !command[0]) {
1120 WMenuParserError(parser, _("missing command in menu config") );
1121 freeline(title, command, params, shortcut);
1122 break;
1124 if (strcasecmp(command, "MENU") == 0) {
1125 menu = wMenuCreate(scr, M_(title), True);
1126 menu->on_destroy = removeShortcutsForMenu;
1127 if (!parseCascade(scr, menu, parser)) {
1128 wMenuDestroy(menu, True);
1129 menu = NULL;
1131 freeline(title, command, params, shortcut);
1132 break;
1133 } else {
1134 WMenuParserError(parser, _("no title given for the root menu") );
1135 freeline(title, command, params, shortcut);
1136 break;
1139 freeline(title, command, params, shortcut);
1142 WMenuParserDelete(parser);
1143 pclose(file);
1145 return menu;
1148 typedef struct {
1149 char *name;
1150 int index;
1151 } dir_data;
1153 static int myCompare(const void *d1, const void *d2)
1155 dir_data *p1 = *(dir_data **) d1;
1156 dir_data *p2 = *(dir_data **) d2;
1158 return strcmp(p1->name, p2->name);
1161 /***** Preset some macro for file parser *****/
1162 static void menu_parser_register_macros(WMenuParser parser)
1164 Visual *visual;
1165 char buf[32];
1167 // Used to return CPP verion, now returns wmaker's version
1168 WMenuParserRegisterSimpleMacro(parser, "__VERSION__", VERSION);
1170 // All macros below were historically defined by WindowMaker
1171 visual = DefaultVisual(dpy, DefaultScreen(dpy));
1172 snprintf(buf, sizeof(buf), "%d", visual->class);
1173 WMenuParserRegisterSimpleMacro(parser, "VISUAL", buf);
1175 snprintf(buf, sizeof(buf), "%d", DefaultDepth(dpy, DefaultScreen(dpy)) );
1176 WMenuParserRegisterSimpleMacro(parser, "DEPTH", buf);
1178 snprintf(buf, sizeof(buf), "%d", WidthOfScreen(DefaultScreenOfDisplay(dpy)) );
1179 WMenuParserRegisterSimpleMacro(parser, "SCR_WIDTH", buf);
1181 snprintf(buf, sizeof(buf), "%d", HeightOfScreen(DefaultScreenOfDisplay(dpy)) );
1182 WMenuParserRegisterSimpleMacro(parser, "SCR_HEIGHT", buf);
1184 WMenuParserRegisterSimpleMacro(parser, "DISPLAY", XDisplayName(DisplayString(dpy)) );
1186 WMenuParserRegisterSimpleMacro(parser, "WM_VERSION", "\"" VERSION "\"");
1189 /************ Menu Configuration From Directory *************/
1191 static Bool isFilePackage(char *file)
1193 int l;
1195 /* check if the extension indicates this file is a
1196 * file package. For now, only recognize .themed */
1198 l = strlen(file);
1200 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1201 return True;
1202 } else {
1203 return False;
1207 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
1209 DIR *dir;
1210 struct dirent *dentry;
1211 struct stat stat_buf;
1212 WMenu *menu = NULL;
1213 char *buffer;
1214 WMArray *dirs = NULL, *files = NULL;
1215 WMArrayIterator iter;
1216 int length, i, have_space = 0;
1217 dir_data *data;
1218 int stripExtension = 0;
1220 dirs = WMCreateArray(16);
1221 files = WMCreateArray(16);
1223 i = 0;
1224 while (path[i] != NULL) {
1225 if (strcmp(path[i], "-noext") == 0) {
1226 stripExtension = 1;
1227 i++;
1228 continue;
1231 dir = opendir(path[i]);
1232 if (!dir) {
1233 i++;
1234 continue;
1237 while ((dentry = readdir(dir))) {
1239 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1240 continue;
1242 if (dentry->d_name[0] == '.')
1243 continue;
1245 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1246 if (!buffer) {
1247 werror(_("out of memory while constructing directory menu %s"), path[i]);
1248 break;
1251 strcpy(buffer, path[i]);
1252 strcat(buffer, "/");
1253 strcat(buffer, dentry->d_name);
1255 if (stat(buffer, &stat_buf) != 0) {
1256 werror(_("%s:could not stat file \"%s\" in menu directory"),
1257 path[i], dentry->d_name);
1258 } else {
1259 Bool isFilePack = False;
1261 data = NULL;
1262 if (S_ISDIR(stat_buf.st_mode)
1263 && !(isFilePack = isFilePackage(dentry->d_name))) {
1265 /* access always returns success for user root */
1266 if (access(buffer, X_OK) == 0) {
1267 /* Directory is accesible. Add to directory list */
1269 data = (dir_data *) wmalloc(sizeof(dir_data));
1270 data->name = wstrdup(dentry->d_name);
1271 data->index = i;
1273 WMAddToArray(dirs, data);
1275 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1276 /* Hack because access always returns X_OK success for user root */
1277 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1278 if ((command != NULL && access(buffer, R_OK) == 0) ||
1279 (command == NULL && access(buffer, X_OK) == 0 &&
1280 (stat_buf.st_mode & S_IXANY))) {
1282 data = (dir_data *) wmalloc(sizeof(dir_data));
1283 data->name = wstrdup(dentry->d_name);
1284 data->index = i;
1286 WMAddToArray(files, data);
1290 free(buffer);
1293 closedir(dir);
1294 i++;
1297 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1298 WMFreeArray(dirs);
1299 WMFreeArray(files);
1300 return NULL;
1303 WMSortArray(dirs, myCompare);
1304 WMSortArray(files, myCompare);
1306 menu = wMenuCreate(scr, M_(title), False);
1307 menu->on_destroy = removeShortcutsForMenu;
1309 WM_ITERATE_ARRAY(dirs, data, iter) {
1310 /* New directory. Use same OPEN_MENU command that was used
1311 * for the current directory. */
1312 length = strlen(path[data->index]) + strlen(data->name) + 6;
1313 if (stripExtension)
1314 length += 7;
1315 if (command)
1316 length += strlen(command) + 6;
1317 buffer = malloc(length);
1318 if (!buffer) {
1319 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1320 break;
1323 buffer[0] = '\0';
1324 if (stripExtension)
1325 strcat(buffer, "-noext ");
1327 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1329 if (have_space)
1330 strcat(buffer, "\"");
1331 strcat(buffer, path[data->index]);
1333 strcat(buffer, "/");
1334 strcat(buffer, data->name);
1335 if (have_space)
1336 strcat(buffer, "\"");
1337 if (command) {
1338 strcat(buffer, " WITH ");
1339 strcat(buffer, command);
1342 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1344 wfree(buffer);
1345 if (data->name)
1346 wfree(data->name);
1347 wfree(data);
1350 WM_ITERATE_ARRAY(files, data, iter) {
1351 /* executable: add as entry */
1352 length = strlen(path[data->index]) + strlen(data->name) + 6;
1353 if (command)
1354 length += strlen(command);
1356 buffer = malloc(length);
1357 if (!buffer) {
1358 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1359 break;
1362 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1363 if (command != NULL) {
1364 strcpy(buffer, command);
1365 strcat(buffer, " ");
1366 if (have_space)
1367 strcat(buffer, "\"");
1368 strcat(buffer, path[data->index]);
1369 } else {
1370 if (have_space) {
1371 buffer[0] = '"';
1372 buffer[1] = 0;
1373 strcat(buffer, path[data->index]);
1374 } else {
1375 strcpy(buffer, path[data->index]);
1378 strcat(buffer, "/");
1379 strcat(buffer, data->name);
1380 if (have_space)
1381 strcat(buffer, "\"");
1383 if (stripExtension) {
1384 char *ptr = strrchr(data->name, '.');
1385 if (ptr && ptr != data->name)
1386 *ptr = 0;
1388 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1390 wfree(buffer);
1391 if (data->name)
1392 wfree(data->name);
1393 wfree(data);
1396 WMFreeArray(files);
1397 WMFreeArray(dirs);
1399 return menu;
1402 /************ Menu Configuration From WMRootMenu *************/
1404 static WMenu *makeDefaultMenu(WScreen * scr)
1406 WMenu *menu = NULL;
1408 menu = wMenuCreate(scr, _("Commands"), True);
1409 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1410 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1411 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1412 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1413 return menu;
1417 *----------------------------------------------------------------------
1418 * configureMenu--
1419 * Reads root menu configuration from defaults database.
1421 *----------------------------------------------------------------------
1423 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1425 WMenu *menu = NULL;
1426 WMPropList *elem;
1427 int i, count;
1428 WMPropList *title, *command, *params;
1429 char *tmp, *mtitle;
1431 if (WMIsPLString(definition)) {
1432 struct stat stat_buf;
1433 char *path = NULL;
1434 Bool menu_is_default = False;
1436 /* menu definition is a string. Probably a path, so parse the file */
1438 tmp = wexpandpath(WMGetFromPLString(definition));
1440 path = getLocalizedMenuFile(tmp);
1442 if (!path)
1443 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1445 if (!path) {
1446 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1447 menu_is_default = True;
1450 if (!path) {
1451 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1452 wfree(tmp);
1453 return NULL;
1456 if (stat(path, &stat_buf) < 0) {
1457 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1458 wfree(path);
1459 wfree(tmp);
1460 return NULL;
1463 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1464 /* if the pointer in WMRootMenu has changed */
1465 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1467 if (menu_is_default) {
1468 wwarning(_
1469 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1470 path);
1473 menu = readMenuFile(scr, path);
1474 if (menu)
1475 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1476 } else {
1477 menu = NULL;
1479 wfree(path);
1480 wfree(tmp);
1482 return menu;
1485 count = WMGetPropListItemCount(definition);
1486 if (count == 0)
1487 return NULL;
1489 elem = WMGetFromPLArray(definition, 0);
1490 if (!WMIsPLString(elem)) {
1491 tmp = WMGetPropListDescription(elem, False);
1492 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1493 wfree(tmp);
1494 return NULL;
1496 mtitle = WMGetFromPLString(elem);
1498 menu = wMenuCreate(scr, M_(mtitle), False);
1499 menu->on_destroy = removeShortcutsForMenu;
1501 #ifdef GLOBAL_SUBMENU_FILE
1502 if (includeGlobals) {
1503 WMenu *submenu;
1504 WMenuEntry *mentry;
1506 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1508 if (submenu) {
1509 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1510 wMenuEntrySetCascade(menu, mentry, submenu);
1513 #endif
1515 for (i = 1; i < count; i++) {
1516 elem = WMGetFromPLArray(definition, i);
1517 #if 0
1518 if (WMIsPLString(elem)) {
1519 char *file;
1521 file = WMGetFromPLString(elem);
1524 #endif
1525 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1526 goto error;
1528 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1529 WMenu *submenu;
1530 WMenuEntry *mentry;
1532 /* submenu */
1533 submenu = configureMenu(scr, elem, True);
1534 if (submenu) {
1535 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1536 wMenuEntrySetCascade(menu, mentry, submenu);
1538 } else {
1539 int idx = 0;
1540 WMPropList *shortcut;
1541 /* normal entry */
1543 title = WMGetFromPLArray(elem, idx++);
1544 shortcut = WMGetFromPLArray(elem, idx++);
1545 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1546 shortcut = WMGetFromPLArray(elem, idx++);
1547 command = WMGetFromPLArray(elem, idx++);
1548 } else {
1549 command = shortcut;
1550 shortcut = NULL;
1552 params = WMGetFromPLArray(elem, idx++);
1554 if (!title || !command)
1555 goto error;
1557 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1558 shortcut ? WMGetFromPLString(shortcut) : NULL,
1559 WMGetFromPLString(command),
1560 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1562 continue;
1564 error:
1565 tmp = WMGetPropListDescription(elem, False);
1566 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1567 wfree(tmp);
1570 return menu;
1574 *----------------------------------------------------------------------
1575 * OpenRootMenu--
1576 * Opens the root menu, parsing the menu configuration from the
1577 * defaults database.
1578 * If the menu is already mapped and is not sticked to the
1579 * root window, it will be unmapped.
1581 * Side effects:
1582 * The menu may be remade.
1584 * Notes:
1585 * Construction of OPEN_MENU entries are delayed to the moment the
1586 * user map's them.
1587 *----------------------------------------------------------------------
1589 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1591 WMenu *menu = NULL;
1592 WMPropList *definition;
1594 static WMPropList *domain=NULL;
1596 if (!domain) {
1597 domain = WMCreatePLString("WMRootMenu");
1601 scr->flags.root_menu_changed_shortcuts = 0;
1602 scr->flags.added_workspace_menu = 0;
1603 scr->flags.added_windows_menu = 0;
1605 if (scr->root_menu && scr->root_menu->flags.mapped) {
1606 menu = scr->root_menu;
1607 if (!menu->flags.buttoned) {
1608 wMenuUnmap(menu);
1609 } else {
1610 wRaiseFrame(menu->frame->core);
1612 if (keyboard)
1613 wMenuMapAt(menu, 0, 0, True);
1614 else
1615 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1617 return;
1620 definition = WDRootMenu->dictionary;
1623 definition = PLGetDomain(domain);
1625 if (definition) {
1626 if (WMIsPLArray(definition)) {
1627 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1628 menu = configureMenu(scr, definition, True);
1629 if (menu)
1630 menu->timestamp = WDRootMenu->timestamp;
1632 } else
1633 menu = NULL;
1634 } else {
1635 menu = configureMenu(scr, definition, True);
1639 if (!menu) {
1640 /* menu hasn't changed or could not be read */
1641 if (!scr->root_menu) {
1642 wMessageDialog(scr, _("Error"),
1643 _("The applications menu could not be loaded. "
1644 "Look at the console output for a detailed "
1645 "description of the errors."), _("OK"), NULL, NULL);
1647 menu = makeDefaultMenu(scr);
1648 scr->root_menu = menu;
1650 menu = scr->root_menu;
1651 } else {
1652 /* new root menu */
1653 if (scr->root_menu) {
1654 wMenuDestroy(scr->root_menu, True);
1656 scr->root_menu = menu;
1658 if (menu) {
1659 int newx, newy;
1661 if (keyboard && x == 0 && y == 0) {
1662 newx = newy = 0;
1663 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1664 newx = x - menu->frame->core->width / 2;
1665 newy = y - menu->frame->core->height / 2;
1666 } else {
1667 newx = x - menu->frame->core->width / 2;
1668 newy = y;
1670 wMenuMapAt(menu, newx, newy, keyboard);
1673 if (scr->flags.root_menu_changed_shortcuts)
1674 rebindKeygrabs(scr);