wmaker: Removed non necessary macro for buffer size
[wmaker-crm.git] / src / rootmenu.c
blob10e2397d94139b68843822253b7d4498a2d6e2cf
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 Shutdown(WSRestartPreparationMode);
254 Restart((char *)entry->clientdata, False);
255 Restart(NULL, True);
258 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
260 wRefreshDesktop(menu->frame->screen_ptr);
263 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
265 wArrangeIcons(menu->frame->screen_ptr, True);
268 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
270 wShowAllWindows(menu->frame->screen_ptr);
273 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
275 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
278 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
280 if (!wPreferences.save_session_on_exit)
281 wSessionSaveState(menu->frame->screen_ptr);
283 wScreenSaveState(menu->frame->screen_ptr);
286 static void clearSessionCommand(WMenu *menu, WMenuEntry *entry)
288 wSessionClearState();
289 wScreenSaveState(menu->frame->screen_ptr);
292 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
294 wShowInfoPanel(menu->frame->screen_ptr);
297 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
299 wShowLegalPanel(menu->frame->screen_ptr);
302 /********************************************************************/
304 static char *getLocalizedMenuFile(const char *menu)
306 char *buffer, *ptr, *locale;
307 int len;
309 if (!w_global.locale)
310 return NULL;
312 len = strlen(menu) + strlen(w_global.locale) + 8;
313 buffer = wmalloc(len);
315 /* try menu.locale_name */
316 snprintf(buffer, len, "%s.%s", menu, w_global.locale);
317 if (access(buffer, F_OK) == 0)
318 return buffer;
320 /* position of locale in our buffer */
321 locale = buffer + strlen(menu) + 1;
323 /* check if it is in the form aa_bb.encoding and check for aa_bb */
324 ptr = strchr(locale, '.');
325 if (ptr) {
326 *ptr = 0;
327 if (access(buffer, F_OK) == 0)
328 return buffer;
331 /* now check for aa */
332 ptr = strchr(locale, '_');
333 if (ptr) {
334 *ptr = 0;
335 if (access(buffer, F_OK) == 0)
336 return buffer;
339 wfree(buffer);
341 return NULL;
344 Bool wRootMenuPerformShortcut(XEvent * event)
346 WScreen *scr = wScreenForRootWindow(event->xkey.root);
347 Shortcut *ptr;
348 int modifiers;
349 int done = 0;
351 /* ignore CapsLock */
352 modifiers = event->xkey.state & w_global.shortcut.modifiers_mask;
354 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
355 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
356 continue;
358 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
359 (*ptr->entry->callback) (ptr->menu, ptr->entry);
360 done = True;
364 return done;
367 void wRootMenuBindShortcuts(Window window)
369 Shortcut *ptr;
371 ptr = shortcutList;
372 while (ptr) {
373 if (ptr->modifier != AnyModifier) {
374 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
375 window, True, GrabModeAsync, GrabModeAsync);
376 #ifdef NUMLOCK_HACK
377 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
378 #endif
380 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
381 ptr = ptr->next;
385 static void rebindKeygrabs(WScreen * scr)
387 WWindow *wwin;
389 wwin = scr->focused_window;
391 while (wwin != NULL) {
392 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
394 if (!WFLAGP(wwin, no_bind_keys)) {
395 wWindowSetKeyGrabs(wwin);
397 wwin = wwin->prev;
401 static void removeShortcutsForMenu(WMenu * menu)
403 Shortcut *ptr, *tmp;
404 Shortcut *newList = NULL;
406 ptr = shortcutList;
407 while (ptr != NULL) {
408 tmp = ptr->next;
409 if (ptr->menu == menu) {
410 wfree(ptr);
411 } else {
412 ptr->next = newList;
413 newList = ptr;
415 ptr = tmp;
417 shortcutList = newList;
418 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
421 static Bool addShortcut(const char *file, const char *shortcutDefinition, WMenu *menu, WMenuEntry *entry)
423 Shortcut *ptr;
424 KeySym ksym;
425 char *k;
426 char buf[MAX_SHORTCUT_LENGTH], *b;
428 ptr = wmalloc(sizeof(Shortcut));
430 wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
431 b = (char *)buf;
433 /* get modifiers */
434 ptr->modifier = 0;
435 while ((k = strchr(b, '+')) != NULL) {
436 int mod;
438 *k = 0;
439 mod = wXModifierFromKey(b);
440 if (mod < 0) {
441 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
442 wfree(ptr);
443 return False;
445 ptr->modifier |= mod;
447 b = k + 1;
450 /* get key */
451 ksym = XStringToKeysym(b);
453 if (ksym == NoSymbol) {
454 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
455 file, shortcutDefinition, entry->text);
456 wfree(ptr);
457 return False;
460 ptr->keycode = XKeysymToKeycode(dpy, ksym);
461 if (ptr->keycode == 0) {
462 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
463 shortcutDefinition, entry->text);
464 wfree(ptr);
465 return False;
468 ptr->menu = menu;
469 ptr->entry = entry;
471 ptr->next = shortcutList;
472 shortcutList = ptr;
474 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
476 return True;
479 static char *next_token(char *line, char **next)
481 char *tmp, c;
482 char *ret;
484 *next = NULL;
485 while (*line == ' ' || *line == '\t')
486 line++;
488 tmp = line;
490 if (*tmp == '"') {
491 tmp++;
492 line++;
493 while (*tmp != 0 && *tmp != '"')
494 tmp++;
495 if (*tmp != '"') {
496 wwarning(_("%s: unmatched '\"' in menu file"), line);
497 return NULL;
499 } else {
500 do {
501 if (*tmp == '\\')
502 tmp++;
504 if (*tmp != 0)
505 tmp++;
507 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
510 c = *tmp;
511 *tmp = 0;
512 ret = wstrdup(line);
513 *tmp = c;
515 if (c == 0)
516 return ret;
517 else
518 tmp++;
520 /* skip blanks */
521 while (*tmp == ' ' || *tmp == '\t')
522 tmp++;
524 if (*tmp != 0)
525 *next = tmp;
527 return ret;
530 static void separateCommand(char *line, char ***file, char **command)
532 char *token, *tmp = line;
533 WMArray *array = WMCreateArray(4);
534 int count, i;
536 *file = NULL;
537 *command = NULL;
538 do {
539 token = next_token(tmp, &tmp);
540 if (token) {
541 if (strcmp(token, "WITH") == 0) {
542 if (tmp != NULL && *tmp != 0)
543 *command = wstrdup(tmp);
544 else
545 wwarning(_("%s: missing command"), line);
546 break;
548 WMAddToArray(array, token);
550 } while (token != NULL && tmp != NULL);
552 count = WMGetArrayItemCount(array);
553 if (count > 0) {
554 *file = wmalloc(sizeof(char *) * (count + 1));
555 (*file)[count] = NULL;
556 for (i = 0; i < count; i++) {
557 (*file)[i] = WMGetFromArray(array, i);
560 WMFreeArray(array);
563 static WMenu *constructPLMenu(WScreen *screen, const char *path)
565 WMPropList *pl = NULL;
566 WMenu *menu = NULL;
568 if (!path)
569 return NULL;
571 pl = WMReadPropListFromFile(path);
572 if (!pl)
573 return NULL;
575 menu = configureMenu(screen, pl, False);
577 WMReleasePropList(pl);
579 if (!menu)
580 return NULL;
582 menu->on_destroy = removeShortcutsForMenu;
583 return menu;
588 static void constructMenu(WMenu * menu, WMenuEntry * entry)
590 WMenu *submenu;
591 struct stat stat_buf;
592 char **path;
593 char *cmd;
594 char *lpath = NULL;
595 int i, first = -1;
596 time_t last = 0;
598 separateCommand((char *)entry->clientdata, &path, &cmd);
599 if (path == NULL || *path == NULL || **path == 0) {
600 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
601 if (cmd)
602 wfree(cmd);
603 return;
606 if (path[0][0] == '|') {
607 /* pipe menu */
609 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
610 /* parse pipe */
612 submenu = readMenuPipe(menu->frame->screen_ptr, path);
614 if (submenu != NULL) {
615 if (path[0][1] == '|')
616 submenu->timestamp = 0;
617 else
618 submenu->timestamp = 1; /* there's no automatic reloading */
620 } else {
621 submenu = NULL;
624 } else {
626 /* try interpreting path as a proplist file */
627 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
628 /* if unsuccessful, try it as an old-style file */
629 if (!submenu) {
631 i = 0;
632 while (path[i] != NULL) {
633 char *tmp;
635 if (strcmp(path[i], "-noext") == 0) {
636 i++;
637 continue;
640 tmp = wexpandpath(path[i]);
641 wfree(path[i]);
642 lpath = getLocalizedMenuFile(tmp);
643 if (lpath) {
644 wfree(tmp);
645 path[i] = lpath;
646 lpath = NULL;
647 } else {
648 path[i] = tmp;
651 if (stat(path[i], &stat_buf) == 0) {
652 if (last < stat_buf.st_mtime)
653 last = stat_buf.st_mtime;
654 if (first < 0)
655 first = i;
656 } else {
657 werror(_("%s:could not stat menu"), path[i]);
658 /*goto finish; */
661 i++;
664 if (first < 0) {
665 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
666 goto finish;
668 stat(path[first], &stat_buf);
669 if (!menu->cascades[entry->cascade]
670 || menu->cascades[entry->cascade]->timestamp < last) {
672 if (S_ISDIR(stat_buf.st_mode)) {
673 /* menu directory */
674 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
675 if (submenu)
676 submenu->timestamp = last;
677 } else if (S_ISREG(stat_buf.st_mode)) {
678 /* menu file */
680 if (cmd || path[1])
681 wwarning(_("too many parameters in OPEN_MENU: %s"),
682 (char *)entry->clientdata);
684 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
685 if (submenu)
686 submenu->timestamp = stat_buf.st_mtime;
687 } else {
688 submenu = NULL;
690 } else {
691 submenu = NULL;
696 if (submenu) {
697 wMenuEntryRemoveCascade(menu, entry);
698 wMenuEntrySetCascade(menu, entry, submenu);
701 finish:
702 i = 0;
703 while (path[i] != NULL)
704 wfree(path[i++]);
705 wfree(path);
706 if (cmd)
707 wfree(cmd);
710 static void constructPLMenuFromPipe(WMenu * menu, WMenuEntry * entry)
712 WMenu *submenu = NULL;
713 char **path;
714 char *cmd;
715 int i;
717 separateCommand((char *)entry->clientdata, &path, &cmd);
718 if (path == NULL || *path == NULL || **path == 0) {
719 wwarning(_("invalid OPEN_PLMENU specification: %s"),
720 (char *)entry->clientdata);
721 if (cmd)
722 wfree(cmd);
723 return;
726 if (path[0][0] == '|') {
727 /* pipe menu */
729 if (!menu->cascades[entry->cascade]
730 || menu->cascades[entry->cascade]->timestamp == 0) {
731 /* parse pipe */
732 submenu = readPLMenuPipe(menu->frame->screen_ptr, path);
734 if (submenu != NULL) {
735 if (path[0][1] == '|')
736 submenu->timestamp = 0;
737 else
738 submenu->timestamp = 1; /* there's no automatic reloading */
743 if (submenu) {
744 wMenuEntryRemoveCascade(menu, entry);
745 wMenuEntrySetCascade(menu, entry, submenu);
748 i = 0;
749 while (path[i] != NULL)
750 wfree(path[i++]);
752 wfree(path);
753 if (cmd)
754 wfree(cmd);
757 static void cleanupWorkspaceMenu(WMenu *menu)
759 if (w_global.workspace.menu == menu)
760 w_global.workspace.menu = NULL;
763 static WMenuEntry *addWorkspaceMenu(WScreen *scr, WMenu *menu, const char *title)
765 WMenu *wsmenu;
766 WMenuEntry *entry;
768 if (scr->flags.added_workspace_menu) {
769 wwarning(_
770 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
771 return NULL;
772 } else {
773 scr->flags.added_workspace_menu = 1;
775 wsmenu = wWorkspaceMenuMake(scr, True);
776 wsmenu->on_destroy = cleanupWorkspaceMenu;
778 w_global.workspace.menu = wsmenu;
779 entry = wMenuAddCallback(menu, title, NULL, NULL);
780 wMenuEntrySetCascade(menu, entry, wsmenu);
782 wWorkspaceMenuUpdate(wsmenu);
784 return entry;
787 static void cleanupWindowsMenu(WMenu * menu)
789 if (menu->frame->screen_ptr->switch_menu == menu)
790 menu->frame->screen_ptr->switch_menu = NULL;
793 static WMenuEntry *addWindowsMenu(WScreen *scr, WMenu *menu, const char *title)
795 WMenu *wwmenu;
796 WWindow *wwin;
797 WMenuEntry *entry;
799 if (scr->flags.added_windows_menu) {
800 wwarning(_
801 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
802 return NULL;
803 } else {
804 scr->flags.added_windows_menu = 1;
806 wwmenu = wMenuCreate(scr, _("Window List"), False);
807 wwmenu->on_destroy = cleanupWindowsMenu;
808 scr->switch_menu = wwmenu;
809 wwin = scr->focused_window;
810 while (wwin) {
811 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
813 wwin = wwin->prev;
815 entry = wMenuAddCallback(menu, title, NULL, NULL);
816 wMenuEntrySetCascade(menu, entry, wwmenu);
818 return entry;
821 static WMenuEntry *addMenuEntry(WMenu *menu, const char *title, const char *shortcut, const char *command,
822 const char *params, const char *file_name)
824 WScreen *scr;
825 WMenuEntry *entry = NULL;
826 Bool shortcutOk = False;
828 if (!menu)
829 return NULL;
830 scr = menu->frame->screen_ptr;
831 if (strcmp(command, "OPEN_MENU") == 0) {
832 if (!params) {
833 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
834 } else {
835 WMenu *dummy;
836 char *path;
838 path = wfindfile(DEF_CONFIG_PATHS, params);
839 if (!path) {
840 path = wstrdup(params);
842 dummy = wMenuCreate(scr, title, False);
843 dummy->on_destroy = removeShortcutsForMenu;
844 entry = wMenuAddCallback(menu, title, constructMenu, path);
845 entry->free_cdata = wfree;
846 wMenuEntrySetCascade(menu, entry, dummy);
848 } else if (strcmp(command, "OPEN_PLMENU") == 0) {
849 if (!params) {
850 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
851 } else {
852 WMenu *dummy;
853 char *path;
855 path = wfindfile(DEF_CONFIG_PATHS, params);
856 if (!path)
857 path = wstrdup(params);
859 dummy = wMenuCreate(scr, title, False);
860 dummy->on_destroy = removeShortcutsForMenu;
861 entry = wMenuAddCallback(menu, title, constructPLMenuFromPipe, path);
862 entry->free_cdata = wfree;
863 wMenuEntrySetCascade(menu, entry, dummy);
865 } else if (strcmp(command, "EXEC") == 0) {
866 if (!params)
867 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
868 else {
869 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
870 entry->free_cdata = wfree;
871 shortcutOk = True;
873 } else if (strcmp(command, "SHEXEC") == 0) {
874 if (!params)
875 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
876 else {
877 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
878 entry->free_cdata = wfree;
879 shortcutOk = True;
881 } else if (strcmp(command, "EXIT") == 0) {
883 if (params && strcmp(params, "QUICK") == 0)
884 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
885 else
886 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
888 shortcutOk = True;
889 } else if (strcmp(command, "SHUTDOWN") == 0) {
891 if (params && strcmp(params, "QUICK") == 0)
892 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
893 else
894 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
896 shortcutOk = True;
897 } else if (strcmp(command, "REFRESH") == 0) {
898 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
900 shortcutOk = True;
901 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
902 entry = addWorkspaceMenu(scr, menu, title);
904 shortcutOk = True;
905 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
906 entry = addWindowsMenu(scr, menu, title);
908 shortcutOk = True;
909 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
910 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
912 shortcutOk = True;
913 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
914 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
916 shortcutOk = True;
917 } else if (strcmp(command, "SHOW_ALL") == 0) {
918 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
920 shortcutOk = True;
921 } else if (strcmp(command, "RESTART") == 0) {
922 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
923 entry->free_cdata = wfree;
924 shortcutOk = True;
925 } else if (strcmp(command, "SAVE_SESSION") == 0) {
926 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
928 shortcutOk = True;
929 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
930 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
931 shortcutOk = True;
932 } else if (strcmp(command, "INFO_PANEL") == 0) {
933 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
934 shortcutOk = True;
935 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
936 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
937 shortcutOk = True;
938 } else {
939 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
941 return NULL;
944 if (shortcut && entry) {
945 if (!shortcutOk) {
946 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
947 } else {
948 if (addShortcut(file_name, shortcut, menu, entry)) {
950 entry->rtext = GetShortcutString(shortcut);
952 entry->rtext = wstrdup(shortcut);
958 return entry;
961 /******************* Menu Configuration From File *******************/
963 static void freeline(char *title, char *command, char *parameter, char *shortcut)
965 wfree(title);
966 wfree(command);
967 wfree(parameter);
968 wfree(shortcut);
971 static WMenu *parseCascade(WScreen * scr, WMenu * menu, WMenuParser parser)
973 char *command, *params, *shortcut, *title;
975 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
977 if (command == NULL || !command[0]) {
978 WMenuParserError(parser, _("missing command in menu config") );
979 freeline(title, command, params, shortcut);
980 goto error;
983 if (strcasecmp(command, "MENU") == 0) {
984 WMenu *cascade;
986 /* start submenu */
988 cascade = wMenuCreate(scr, M_(title), False);
989 cascade->on_destroy = removeShortcutsForMenu;
990 if (!parseCascade(scr, cascade, parser)) {
991 wMenuDestroy(cascade, True);
992 } else {
993 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
995 } else if (strcasecmp(command, "END") == 0) {
996 /* end of menu */
997 freeline(title, command, params, shortcut);
998 return menu;
999 } else {
1000 /* normal items */
1001 addMenuEntry(menu, M_(title), shortcut, command, params, WMenuParserGetFilename(parser));
1003 freeline(title, command, params, shortcut);
1006 WMenuParserError(parser, _("syntax error in menu file: END declaration missing") );
1008 error:
1009 return NULL;
1012 static WMenu *readMenuFile(WScreen *scr, const char *file_name)
1014 WMenu *menu = NULL;
1015 FILE *file = NULL;
1016 WMenuParser parser;
1017 char *command, *params, *shortcut, *title;
1019 file = fopen(file_name, "rb");
1020 if (!file) {
1021 werror(_("%s:could not open menu file"), file_name);
1022 return NULL;
1024 parser = WMenuParserCreate(file_name, file, DEF_CONFIG_PATHS);
1025 menu_parser_register_macros(parser);
1027 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1029 if (command == NULL || !command[0]) {
1030 WMenuParserError(parser, _("missing command in menu config") );
1031 freeline(title, command, params, shortcut);
1032 break;
1034 if (strcasecmp(command, "MENU") == 0) {
1035 menu = wMenuCreate(scr, M_(title), True);
1036 menu->on_destroy = removeShortcutsForMenu;
1037 if (!parseCascade(scr, menu, parser)) {
1038 wMenuDestroy(menu, True);
1039 menu = NULL;
1041 freeline(title, command, params, shortcut);
1042 break;
1043 } else {
1044 WMenuParserError(parser, _("invalid menu file, MENU command is missing") );
1045 freeline(title, command, params, shortcut);
1046 break;
1048 freeline(title, command, params, shortcut);
1051 WMenuParserDelete(parser);
1052 fclose(file);
1054 return menu;
1057 /************ Menu Configuration From Pipe *************/
1058 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name)
1060 WMPropList *plist = NULL;
1061 WMenu *menu = NULL;
1062 char *filename;
1063 char flat_file[MAXLINE];
1064 int i;
1066 flat_file[0] = '\0';
1068 for (i = 0; file_name[i] != NULL; i++) {
1069 strcat(flat_file, file_name[i]);
1070 strcat(flat_file, " ");
1072 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1074 plist = WMReadPropListFromPipe(filename);
1076 if (!plist)
1077 return NULL;
1079 menu = configureMenu(scr, plist, False);
1081 WMReleasePropList(plist);
1083 if (!menu)
1084 return NULL;
1086 menu->on_destroy = removeShortcutsForMenu;
1087 return menu;
1090 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1092 WMenu *menu = NULL;
1093 FILE *file = NULL;
1094 WMenuParser parser;
1095 char *command, *params, *shortcut, *title;
1096 char *filename;
1097 char flat_file[MAXLINE];
1098 int i;
1100 flat_file[0] = '\0';
1102 for (i = 0; file_name[i] != NULL; i++) {
1103 strcat(flat_file, file_name[i]);
1104 strcat(flat_file, " ");
1106 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1108 file = popen(filename, "r");
1109 if (!file) {
1110 werror(_("%s:could not open menu file"), filename);
1111 return NULL;
1113 parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS);
1114 menu_parser_register_macros(parser);
1116 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1118 if (command == NULL || !command[0]) {
1119 WMenuParserError(parser, _("missing command in menu config") );
1120 freeline(title, command, params, shortcut);
1121 break;
1123 if (strcasecmp(command, "MENU") == 0) {
1124 menu = wMenuCreate(scr, M_(title), True);
1125 menu->on_destroy = removeShortcutsForMenu;
1126 if (!parseCascade(scr, menu, parser)) {
1127 wMenuDestroy(menu, True);
1128 menu = NULL;
1130 freeline(title, command, params, shortcut);
1131 break;
1132 } else {
1133 WMenuParserError(parser, _("no title given for the root menu") );
1134 freeline(title, command, params, shortcut);
1135 break;
1138 freeline(title, command, params, shortcut);
1141 WMenuParserDelete(parser);
1142 pclose(file);
1144 return menu;
1147 typedef struct {
1148 char *name;
1149 int index;
1150 } dir_data;
1152 static int myCompare(const void *d1, const void *d2)
1154 dir_data *p1 = *(dir_data **) d1;
1155 dir_data *p2 = *(dir_data **) d2;
1157 return strcmp(p1->name, p2->name);
1160 /***** Preset some macro for file parser *****/
1161 static void menu_parser_register_macros(WMenuParser parser)
1163 Visual *visual;
1164 char buf[32];
1166 // Used to return CPP verion, now returns wmaker's version
1167 WMenuParserRegisterSimpleMacro(parser, "__VERSION__", VERSION);
1169 // All macros below were historically defined by WindowMaker
1170 visual = DefaultVisual(dpy, DefaultScreen(dpy));
1171 snprintf(buf, sizeof(buf), "%d", visual->class);
1172 WMenuParserRegisterSimpleMacro(parser, "VISUAL", buf);
1174 snprintf(buf, sizeof(buf), "%d", DefaultDepth(dpy, DefaultScreen(dpy)) );
1175 WMenuParserRegisterSimpleMacro(parser, "DEPTH", buf);
1177 snprintf(buf, sizeof(buf), "%d", WidthOfScreen(DefaultScreenOfDisplay(dpy)) );
1178 WMenuParserRegisterSimpleMacro(parser, "SCR_WIDTH", buf);
1180 snprintf(buf, sizeof(buf), "%d", HeightOfScreen(DefaultScreenOfDisplay(dpy)) );
1181 WMenuParserRegisterSimpleMacro(parser, "SCR_HEIGHT", buf);
1183 WMenuParserRegisterSimpleMacro(parser, "DISPLAY", XDisplayName(DisplayString(dpy)) );
1185 WMenuParserRegisterSimpleMacro(parser, "WM_VERSION", "\"" VERSION "\"");
1188 /************ Menu Configuration From Directory *************/
1190 static Bool isFilePackage(const char *file)
1192 int l;
1194 /* check if the extension indicates this file is a
1195 * file package. For now, only recognize .themed */
1197 l = strlen(file);
1199 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1200 return True;
1201 } else {
1202 return False;
1206 static WMenu *readMenuDirectory(WScreen *scr, const char *title, char **path, const char *command)
1208 DIR *dir;
1209 struct dirent *dentry;
1210 struct stat stat_buf;
1211 WMenu *menu = NULL;
1212 char *buffer;
1213 WMArray *dirs = NULL, *files = NULL;
1214 WMArrayIterator iter;
1215 int length, i, have_space = 0;
1216 dir_data *data;
1217 int stripExtension = 0;
1219 dirs = WMCreateArray(16);
1220 files = WMCreateArray(16);
1222 i = 0;
1223 while (path[i] != NULL) {
1224 if (strcmp(path[i], "-noext") == 0) {
1225 stripExtension = 1;
1226 i++;
1227 continue;
1230 dir = opendir(path[i]);
1231 if (!dir) {
1232 i++;
1233 continue;
1236 while ((dentry = readdir(dir))) {
1238 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1239 continue;
1241 if (dentry->d_name[0] == '.')
1242 continue;
1244 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1245 if (!buffer) {
1246 werror(_("out of memory while constructing directory menu %s"), path[i]);
1247 break;
1250 strcpy(buffer, path[i]);
1251 strcat(buffer, "/");
1252 strcat(buffer, dentry->d_name);
1254 if (stat(buffer, &stat_buf) != 0) {
1255 werror(_("%s:could not stat file \"%s\" in menu directory"),
1256 path[i], dentry->d_name);
1257 } else {
1258 Bool isFilePack = False;
1260 data = NULL;
1261 if (S_ISDIR(stat_buf.st_mode)
1262 && !(isFilePack = isFilePackage(dentry->d_name))) {
1264 /* access always returns success for user root */
1265 if (access(buffer, X_OK) == 0) {
1266 /* Directory is accesible. Add to directory list */
1268 data = (dir_data *) wmalloc(sizeof(dir_data));
1269 data->name = wstrdup(dentry->d_name);
1270 data->index = i;
1272 WMAddToArray(dirs, data);
1274 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1275 /* Hack because access always returns X_OK success for user root */
1276 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1277 if ((command != NULL && access(buffer, R_OK) == 0) ||
1278 (command == NULL && access(buffer, X_OK) == 0 &&
1279 (stat_buf.st_mode & S_IXANY))) {
1281 data = (dir_data *) wmalloc(sizeof(dir_data));
1282 data->name = wstrdup(dentry->d_name);
1283 data->index = i;
1285 WMAddToArray(files, data);
1289 free(buffer);
1292 closedir(dir);
1293 i++;
1296 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1297 WMFreeArray(dirs);
1298 WMFreeArray(files);
1299 return NULL;
1302 WMSortArray(dirs, myCompare);
1303 WMSortArray(files, myCompare);
1305 menu = wMenuCreate(scr, M_(title), False);
1306 menu->on_destroy = removeShortcutsForMenu;
1308 WM_ITERATE_ARRAY(dirs, data, iter) {
1309 /* New directory. Use same OPEN_MENU command that was used
1310 * for the current directory. */
1311 length = strlen(path[data->index]) + strlen(data->name) + 6;
1312 if (stripExtension)
1313 length += 7;
1314 if (command)
1315 length += strlen(command) + 6;
1316 buffer = malloc(length);
1317 if (!buffer) {
1318 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1319 break;
1322 buffer[0] = '\0';
1323 if (stripExtension)
1324 strcat(buffer, "-noext ");
1326 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1328 if (have_space)
1329 strcat(buffer, "\"");
1330 strcat(buffer, path[data->index]);
1332 strcat(buffer, "/");
1333 strcat(buffer, data->name);
1334 if (have_space)
1335 strcat(buffer, "\"");
1336 if (command) {
1337 strcat(buffer, " WITH ");
1338 strcat(buffer, command);
1341 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1343 wfree(buffer);
1344 if (data->name)
1345 wfree(data->name);
1346 wfree(data);
1349 WM_ITERATE_ARRAY(files, data, iter) {
1350 /* executable: add as entry */
1351 length = strlen(path[data->index]) + strlen(data->name) + 6;
1352 if (command)
1353 length += strlen(command);
1355 buffer = malloc(length);
1356 if (!buffer) {
1357 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1358 break;
1361 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1362 if (command != NULL) {
1363 strcpy(buffer, command);
1364 strcat(buffer, " ");
1365 if (have_space)
1366 strcat(buffer, "\"");
1367 strcat(buffer, path[data->index]);
1368 } else {
1369 if (have_space) {
1370 buffer[0] = '"';
1371 buffer[1] = 0;
1372 strcat(buffer, path[data->index]);
1373 } else {
1374 strcpy(buffer, path[data->index]);
1377 strcat(buffer, "/");
1378 strcat(buffer, data->name);
1379 if (have_space)
1380 strcat(buffer, "\"");
1382 if (stripExtension) {
1383 char *ptr = strrchr(data->name, '.');
1384 if (ptr && ptr != data->name)
1385 *ptr = 0;
1387 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1389 wfree(buffer);
1390 if (data->name)
1391 wfree(data->name);
1392 wfree(data);
1395 WMFreeArray(files);
1396 WMFreeArray(dirs);
1398 return menu;
1401 /************ Menu Configuration From WMRootMenu *************/
1403 static WMenu *makeDefaultMenu(WScreen * scr)
1405 WMenu *menu = NULL;
1407 menu = wMenuCreate(scr, _("Commands"), True);
1408 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1409 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1410 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1411 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1412 return menu;
1416 *----------------------------------------------------------------------
1417 * configureMenu--
1418 * Reads root menu configuration from defaults database.
1420 *----------------------------------------------------------------------
1422 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1424 WMenu *menu = NULL;
1425 WMPropList *elem;
1426 int i, count;
1427 WMPropList *title, *command, *params;
1428 char *tmp, *mtitle;
1430 if (WMIsPLString(definition)) {
1431 struct stat stat_buf;
1432 char *path = NULL;
1433 Bool menu_is_default = False;
1435 /* menu definition is a string. Probably a path, so parse the file */
1437 tmp = wexpandpath(WMGetFromPLString(definition));
1439 path = getLocalizedMenuFile(tmp);
1441 if (!path)
1442 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1444 if (!path) {
1445 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1446 menu_is_default = True;
1449 if (!path) {
1450 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1451 wfree(tmp);
1452 return NULL;
1455 if (stat(path, &stat_buf) < 0) {
1456 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1457 wfree(path);
1458 wfree(tmp);
1459 return NULL;
1462 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1463 /* if the pointer in WMRootMenu has changed */
1464 || w_global.domain.root_menu->timestamp > scr->root_menu->timestamp) {
1466 if (menu_is_default) {
1467 wwarning(_
1468 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1469 path);
1472 menu = readMenuFile(scr, path);
1473 if (menu)
1474 menu->timestamp = WMAX(stat_buf.st_mtime, w_global.domain.root_menu->timestamp);
1475 } else {
1476 menu = NULL;
1478 wfree(path);
1479 wfree(tmp);
1481 return menu;
1484 count = WMGetPropListItemCount(definition);
1485 if (count == 0)
1486 return NULL;
1488 elem = WMGetFromPLArray(definition, 0);
1489 if (!WMIsPLString(elem)) {
1490 tmp = WMGetPropListDescription(elem, False);
1491 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1492 wfree(tmp);
1493 return NULL;
1495 mtitle = WMGetFromPLString(elem);
1497 menu = wMenuCreate(scr, M_(mtitle), False);
1498 menu->on_destroy = removeShortcutsForMenu;
1500 #ifdef GLOBAL_SUBMENU_FILE
1501 if (includeGlobals) {
1502 WMenu *submenu;
1503 WMenuEntry *mentry;
1505 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1507 if (submenu) {
1508 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1509 wMenuEntrySetCascade(menu, mentry, submenu);
1512 #endif
1514 for (i = 1; i < count; i++) {
1515 elem = WMGetFromPLArray(definition, i);
1516 #if 0
1517 if (WMIsPLString(elem)) {
1518 char *file;
1520 file = WMGetFromPLString(elem);
1523 #endif
1524 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1525 goto error;
1527 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1528 WMenu *submenu;
1529 WMenuEntry *mentry;
1531 /* submenu */
1532 submenu = configureMenu(scr, elem, True);
1533 if (submenu) {
1534 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1535 wMenuEntrySetCascade(menu, mentry, submenu);
1537 } else {
1538 int idx = 0;
1539 WMPropList *shortcut;
1540 /* normal entry */
1542 title = WMGetFromPLArray(elem, idx++);
1543 shortcut = WMGetFromPLArray(elem, idx++);
1544 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1545 shortcut = WMGetFromPLArray(elem, idx++);
1546 command = WMGetFromPLArray(elem, idx++);
1547 } else {
1548 command = shortcut;
1549 shortcut = NULL;
1551 params = WMGetFromPLArray(elem, idx++);
1553 if (!title || !command)
1554 goto error;
1556 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1557 shortcut ? WMGetFromPLString(shortcut) : NULL,
1558 WMGetFromPLString(command),
1559 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1561 continue;
1563 error:
1564 tmp = WMGetPropListDescription(elem, False);
1565 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1566 wfree(tmp);
1569 return menu;
1573 *----------------------------------------------------------------------
1574 * OpenRootMenu--
1575 * Opens the root menu, parsing the menu configuration from the
1576 * defaults database.
1577 * If the menu is already mapped and is not sticked to the
1578 * root window, it will be unmapped.
1580 * Side effects:
1581 * The menu may be remade.
1583 * Notes:
1584 * Construction of OPEN_MENU entries are delayed to the moment the
1585 * user map's them.
1586 *----------------------------------------------------------------------
1588 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1590 WMenu *menu = NULL;
1591 WMPropList *definition;
1593 static WMPropList *domain=NULL;
1595 if (!domain) {
1596 domain = WMCreatePLString("WMRootMenu");
1600 scr->flags.root_menu_changed_shortcuts = 0;
1601 scr->flags.added_workspace_menu = 0;
1602 scr->flags.added_windows_menu = 0;
1604 if (scr->root_menu && scr->root_menu->flags.mapped) {
1605 menu = scr->root_menu;
1606 if (!menu->flags.buttoned) {
1607 wMenuUnmap(menu);
1608 } else {
1609 wRaiseFrame(menu->frame->core);
1611 if (keyboard)
1612 wMenuMapAt(menu, 0, 0, True);
1613 else
1614 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1616 return;
1619 definition = w_global.domain.root_menu->dictionary;
1622 definition = PLGetDomain(domain);
1624 if (definition) {
1625 if (WMIsPLArray(definition)) {
1626 if (!scr->root_menu || w_global.domain.root_menu->timestamp > scr->root_menu->timestamp) {
1627 menu = configureMenu(scr, definition, True);
1628 if (menu)
1629 menu->timestamp = w_global.domain.root_menu->timestamp;
1631 } else
1632 menu = NULL;
1633 } else {
1634 menu = configureMenu(scr, definition, True);
1638 if (!menu) {
1639 /* menu hasn't changed or could not be read */
1640 if (!scr->root_menu) {
1641 wMessageDialog(scr, _("Error"),
1642 _("The applications menu could not be loaded. "
1643 "Look at the console output for a detailed "
1644 "description of the errors."), _("OK"), NULL, NULL);
1646 menu = makeDefaultMenu(scr);
1647 scr->root_menu = menu;
1649 menu = scr->root_menu;
1650 } else {
1651 /* new root menu */
1652 if (scr->root_menu) {
1653 wMenuDestroy(scr->root_menu, True);
1655 scr->root_menu = menu;
1657 if (menu) {
1658 int newx, newy;
1660 if (keyboard && x == 0 && y == 0) {
1661 newx = newy = 0;
1662 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1663 newx = x - menu->frame->core->width / 2;
1664 newy = y - menu->frame->core->height / 2;
1665 } else {
1666 newx = x - menu->frame->core->width / 2;
1667 newy = y;
1669 wMenuMapAt(menu, newx, newy, keyboard);
1672 if (scr->flags.root_menu_changed_shortcuts)
1673 rebindKeygrabs(scr);