Remove dependency to CPP: added pre-defined macros
[wmaker-crm.git] / src / rootmenu.c
blob7e1b2ea04dc872254da20dddd7c72a165c0a36ed
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 "funcs.h"
45 #include "main.h"
46 #include "dialog.h"
47 #include "keybind.h"
48 #include "stacking.h"
49 #include "workspace.h"
50 #include "defaults.h"
51 #include "framewin.h"
52 #include "session.h"
53 #include "xmodifier.h"
55 #include <WINGs/WUtil.h>
57 #define MAX_SHORTCUT_LENGTH 32
59 extern char *Locale;
60 extern WDDomain *WDRootMenu;
61 extern Cursor wCursor[WCUR_LAST];
62 extern WPreferences wPreferences;
64 static WMenu *readMenuPipe(WScreen * scr, char **file_name);
65 static WMenu *readMenuFile(WScreen * scr, char *file_name);
66 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **file_name, char *command);
67 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals);
68 static void menu_parser_register_macros(WMenuParser parser);
70 typedef struct Shortcut {
71 struct Shortcut *next;
73 int modifier;
74 KeyCode keycode;
75 WMenuEntry *entry;
76 WMenu *menu;
77 } Shortcut;
79 static Shortcut *shortcutList = NULL;
82 * Syntax:
83 * # main menu
84 * "Menu Name" MENU
85 * "Title" EXEC command_to_exec -params
86 * "Submenu" MENU
87 * "Title" EXEC command_to_exec -params
88 * "Submenu" END
89 * "Workspaces" WORKSPACE_MENU
90 * "Title" built_in_command
91 * "Quit" EXIT
92 * "Quick Quit" EXIT QUICK
93 * "Menu Name" END
95 * Commands may be preceded by SHORTCUT key
97 * Built-in commands:
99 * INFO_PANEL - shows the Info Panel
100 * LEGAL_PANEL - shows the Legal info panel
101 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
102 * REFRESH - forces the desktop to be repainted
103 * EXIT [QUICK] - exit the window manager [without confirmation]
104 * EXEC <program> - execute an external program
105 * SHEXEC <command> - execute a shell command
106 * WORKSPACE_MENU - places the workspace submenu
107 * ARRANGE_ICONS
108 * RESTART [<window manager>] - restarts the window manager
109 * SHOW_ALL - unhide all windows on workspace
110 * HIDE_OTHERS - hides all windows excep the focused one
111 * OPEN_MENU file - read menu data from file which must be a valid menu file.
112 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
113 * - read menu data from directory(ies) and
114 * eventually precede each with a command.
115 * OPEN_MENU | command
116 * - opens command and uses its stdout to construct and insert
117 * the resulting menu in current position. The output of
118 * command must be a valid menu description.
119 * The space between '|' and command is optional.
120 * || will do the same, but will not cache the contents.
121 * SAVE_SESSION - saves the current state of the desktop, which include
122 * all running applications, all their hints (geometry,
123 * position on screen, workspace they live on, the dock
124 * or clip from where they were launched, and
125 * if minimized, shaded or hidden. Also saves the current
126 * workspace the user is on. All will be restored on every
127 * start of windowmaker until another SAVE_SESSION or
128 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
129 * WindowMaker domain file, then saving is automatically
130 * done on every windowmaker exit, overwriting any
131 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
132 * dock state now.
133 * CLEAR_SESSION - clears any previous saved session. This will not have
134 * any effect if SaveSessionOnExit is True.
138 #define M_QUICK 1
140 /* menu commands */
142 static void execCommand(WMenu * menu, WMenuEntry * entry)
144 char *cmdline;
146 cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
148 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
149 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);
150 XSync(dpy, 0);
152 if (cmdline) {
153 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
154 wfree(cmdline);
156 XUngrabPointer(dpy, CurrentTime);
157 XSync(dpy, 0);
160 static void exitCommand(WMenu * menu, WMenuEntry * entry)
162 static int inside = 0;
163 int result;
165 /* prevent reentrant calls */
166 if (inside)
167 return;
168 inside = 1;
170 #define R_CANCEL 0
171 #define R_EXIT 1
173 result = R_CANCEL;
175 if ((long)entry->clientdata == M_QUICK) {
176 result = R_EXIT;
177 } else {
178 int r, oldSaveSessionFlag;
180 oldSaveSessionFlag = wPreferences.save_session_on_exit;
181 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
182 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
184 if (r == WAPRDefault) {
185 result = R_EXIT;
186 } else if (r == WAPRAlternate) {
187 /* Don't modify the "save session on exit" flag if the
188 * user canceled the operation. */
189 wPreferences.save_session_on_exit = oldSaveSessionFlag;
192 if (result == R_EXIT)
193 Shutdown(WSExitMode);
195 #undef R_EXIT
196 #undef R_CANCEL
197 inside = 0;
200 static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
202 static int inside = 0;
203 int result;
205 /* prevent reentrant calls */
206 if (inside)
207 return;
208 inside = 1;
210 #define R_CANCEL 0
211 #define R_CLOSE 1
212 #define R_KILL 2
214 result = R_CANCEL;
215 if ((long)entry->clientdata == M_QUICK)
216 result = R_CLOSE;
217 else {
218 int r, oldSaveSessionFlag;
220 oldSaveSessionFlag = wPreferences.save_session_on_exit;
222 r = wExitDialog(menu->frame->screen_ptr,
223 _("Kill X session"),
224 _("Kill Window System session?\n"
225 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
226 if (r == WAPRDefault) {
227 result = R_KILL;
228 } else if (r == WAPRAlternate) {
229 /* Don't modify the "save session on exit" flag if the
230 * user canceled the operation. */
231 wPreferences.save_session_on_exit = oldSaveSessionFlag;
235 if (result != R_CANCEL) {
236 Shutdown(WSKillMode);
238 #undef R_CLOSE
239 #undef R_CANCEL
240 #undef R_KILL
241 inside = 0;
244 static void restartCommand(WMenu * menu, WMenuEntry * entry)
246 Shutdown(WSRestartPreparationMode);
247 Restart((char *)entry->clientdata, False);
248 Restart(NULL, True);
251 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
253 wRefreshDesktop(menu->frame->screen_ptr);
256 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
258 wArrangeIcons(menu->frame->screen_ptr, True);
261 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
263 wShowAllWindows(menu->frame->screen_ptr);
266 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
268 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
271 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
273 if (!wPreferences.save_session_on_exit)
274 wSessionSaveState(menu->frame->screen_ptr);
276 wScreenSaveState(menu->frame->screen_ptr);
279 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
281 wSessionClearState(menu->frame->screen_ptr);
282 wScreenSaveState(menu->frame->screen_ptr);
285 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
287 wShowInfoPanel(menu->frame->screen_ptr);
290 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
292 wShowLegalPanel(menu->frame->screen_ptr);
295 /********************************************************************/
297 static char * getLocalizedMenuFile(char *menu)
299 char *buffer, *ptr, *locale;
300 int len;
302 if (!Locale)
303 return NULL;
305 len = strlen(menu) + strlen(Locale) + 8;
306 buffer = wmalloc(len);
308 /* try menu.locale_name */
309 snprintf(buffer, len, "%s.%s", menu, Locale);
310 if (access(buffer, F_OK) == 0)
311 return buffer;
313 /* position of locale in our buffer */
314 locale = buffer + strlen(menu) + 1;
316 /* check if it is in the form aa_bb.encoding and check for aa_bb */
317 ptr = strchr(locale, '.');
318 if (ptr) {
319 *ptr = 0;
320 if (access(buffer, F_OK) == 0)
321 return buffer;
324 /* now check for aa */
325 ptr = strchr(locale, '_');
326 if (ptr) {
327 *ptr = 0;
328 if (access(buffer, F_OK) == 0)
329 return buffer;
332 wfree(buffer);
334 return NULL;
337 Bool wRootMenuPerformShortcut(XEvent * event)
339 WScreen *scr = wScreenForRootWindow(event->xkey.root);
340 Shortcut *ptr;
341 int modifiers;
342 int done = 0;
344 /* ignore CapsLock */
345 modifiers = event->xkey.state & ValidModMask;
347 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
348 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
349 continue;
351 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
352 (*ptr->entry->callback) (ptr->menu, ptr->entry);
353 done = True;
357 return done;
360 void wRootMenuBindShortcuts(Window window)
362 Shortcut *ptr;
364 ptr = shortcutList;
365 while (ptr) {
366 if (ptr->modifier != AnyModifier) {
367 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
368 window, True, GrabModeAsync, GrabModeAsync);
369 #ifdef NUMLOCK_HACK
370 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
371 #endif
373 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
374 ptr = ptr->next;
378 static void rebindKeygrabs(WScreen * scr)
380 WWindow *wwin;
382 wwin = scr->focused_window;
384 while (wwin != NULL) {
385 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
387 if (!WFLAGP(wwin, no_bind_keys)) {
388 wWindowSetKeyGrabs(wwin);
390 wwin = wwin->prev;
394 static void removeShortcutsForMenu(WMenu * menu)
396 Shortcut *ptr, *tmp;
397 Shortcut *newList = NULL;
399 ptr = shortcutList;
400 while (ptr != NULL) {
401 tmp = ptr->next;
402 if (ptr->menu == menu) {
403 wfree(ptr);
404 } else {
405 ptr->next = newList;
406 newList = ptr;
408 ptr = tmp;
410 shortcutList = newList;
411 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
414 static Bool addShortcut(const char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
416 Shortcut *ptr;
417 KeySym ksym;
418 char *k;
419 char buf[MAX_SHORTCUT_LENGTH], *b;
421 ptr = wmalloc(sizeof(Shortcut));
423 wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
424 b = (char *)buf;
426 /* get modifiers */
427 ptr->modifier = 0;
428 while ((k = strchr(b, '+')) != NULL) {
429 int mod;
431 *k = 0;
432 mod = wXModifierFromKey(b);
433 if (mod < 0) {
434 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
435 wfree(ptr);
436 return False;
438 ptr->modifier |= mod;
440 b = k + 1;
443 /* get key */
444 ksym = XStringToKeysym(b);
446 if (ksym == NoSymbol) {
447 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
448 file, shortcutDefinition, entry->text);
449 wfree(ptr);
450 return False;
453 ptr->keycode = XKeysymToKeycode(dpy, ksym);
454 if (ptr->keycode == 0) {
455 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
456 shortcutDefinition, entry->text);
457 wfree(ptr);
458 return False;
461 ptr->menu = menu;
462 ptr->entry = entry;
464 ptr->next = shortcutList;
465 shortcutList = ptr;
467 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
469 return True;
472 static char *next_token(char *line, char **next)
474 char *tmp, c;
475 char *ret;
477 *next = NULL;
478 while (*line == ' ' || *line == '\t')
479 line++;
481 tmp = line;
483 if (*tmp == '"') {
484 tmp++;
485 line++;
486 while (*tmp != 0 && *tmp != '"')
487 tmp++;
488 if (*tmp != '"') {
489 wwarning(_("%s: unmatched '\"' in menu file"), line);
490 return NULL;
492 } else {
493 do {
494 if (*tmp == '\\')
495 tmp++;
497 if (*tmp != 0)
498 tmp++;
500 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
503 c = *tmp;
504 *tmp = 0;
505 ret = wstrdup(line);
506 *tmp = c;
508 if (c == 0)
509 return ret;
510 else
511 tmp++;
513 /* skip blanks */
514 while (*tmp == ' ' || *tmp == '\t')
515 tmp++;
517 if (*tmp != 0)
518 *next = tmp;
520 return ret;
523 static void separateCommand(char *line, char ***file, char **command)
525 char *token, *tmp = line;
526 WMArray *array = WMCreateArray(4);
527 int count, i;
529 *file = NULL;
530 *command = NULL;
531 do {
532 token = next_token(tmp, &tmp);
533 if (token) {
534 if (strcmp(token, "WITH") == 0) {
535 if (tmp != NULL && *tmp != 0)
536 *command = wstrdup(tmp);
537 else
538 wwarning(_("%s: missing command"), line);
539 break;
541 WMAddToArray(array, token);
543 } while (token != NULL && tmp != NULL);
545 count = WMGetArrayItemCount(array);
546 if (count > 0) {
547 *file = wmalloc(sizeof(char *) * (count + 1));
548 (*file)[count] = NULL;
549 for (i = 0; i < count; i++) {
550 (*file)[i] = WMGetFromArray(array, i);
553 WMFreeArray(array);
556 static WMenu *constructPLMenu(WScreen *screen, char *path)
558 WMPropList *pl = NULL;
559 WMenu *menu = NULL;
561 if (!path)
562 return NULL;
564 pl = WMReadPropListFromFile(path);
565 if (!pl)
566 return NULL;
568 menu = configureMenu(screen, pl, False);
569 if (!menu)
570 return NULL;
572 menu->on_destroy = removeShortcutsForMenu;
573 return menu;
576 static void constructMenu(WMenu * menu, WMenuEntry * entry)
578 WMenu *submenu;
579 struct stat stat_buf;
580 char **path;
581 char *cmd;
582 char *lpath = NULL;
583 int i, first = -1;
584 time_t last = 0;
586 separateCommand((char *)entry->clientdata, &path, &cmd);
587 if (path == NULL || *path == NULL || **path == 0) {
588 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
589 if (cmd)
590 wfree(cmd);
591 return;
594 if (path[0][0] == '|') {
595 /* pipe menu */
597 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
598 /* parse pipe */
600 submenu = readMenuPipe(menu->frame->screen_ptr, path);
602 if (submenu != NULL) {
603 if (path[0][1] == '|')
604 submenu->timestamp = 0;
605 else
606 submenu->timestamp = 1; /* there's no automatic reloading */
608 } else {
609 submenu = NULL;
612 } else {
614 /* try interpreting path as a proplist file */
615 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
616 /* if unsuccessful, try it as an old-style file */
617 if (!submenu) {
619 i = 0;
620 while (path[i] != NULL) {
621 char *tmp;
623 if (strcmp(path[i], "-noext") == 0) {
624 i++;
625 continue;
628 tmp = wexpandpath(path[i]);
629 wfree(path[i]);
630 lpath = getLocalizedMenuFile(tmp);
631 if (lpath) {
632 wfree(tmp);
633 path[i] = lpath;
634 lpath = NULL;
635 } else {
636 path[i] = tmp;
639 if (stat(path[i], &stat_buf) == 0) {
640 if (last < stat_buf.st_mtime)
641 last = stat_buf.st_mtime;
642 if (first < 0)
643 first = i;
644 } else {
645 werror(_("%s:could not stat menu"), path[i]);
646 /*goto finish; */
649 i++;
652 if (first < 0) {
653 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
654 goto finish;
656 stat(path[first], &stat_buf);
657 if (!menu->cascades[entry->cascade]
658 || menu->cascades[entry->cascade]->timestamp < last) {
660 if (S_ISDIR(stat_buf.st_mode)) {
661 /* menu directory */
662 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
663 if (submenu)
664 submenu->timestamp = last;
665 } else if (S_ISREG(stat_buf.st_mode)) {
666 /* menu file */
668 if (cmd || path[1])
669 wwarning(_("too many parameters in OPEN_MENU: %s"),
670 (char *)entry->clientdata);
672 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
673 if (submenu)
674 submenu->timestamp = stat_buf.st_mtime;
675 } else {
676 submenu = NULL;
678 } else {
679 submenu = NULL;
684 if (submenu) {
685 wMenuEntryRemoveCascade(menu, entry);
686 wMenuEntrySetCascade(menu, entry, submenu);
689 finish:
690 i = 0;
691 while (path[i] != NULL)
692 wfree(path[i++]);
693 wfree(path);
694 if (cmd)
695 wfree(cmd);
698 static void cleanupWorkspaceMenu(WMenu * menu)
700 if (menu->frame->screen_ptr->workspace_menu == menu)
701 menu->frame->screen_ptr->workspace_menu = NULL;
704 static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
706 WMenu *wsmenu;
707 WMenuEntry *entry;
709 if (scr->flags.added_workspace_menu) {
710 wwarning(_
711 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
712 return NULL;
713 } else {
714 scr->flags.added_workspace_menu = 1;
716 wsmenu = wWorkspaceMenuMake(scr, True);
717 wsmenu->on_destroy = cleanupWorkspaceMenu;
719 scr->workspace_menu = wsmenu;
720 entry = wMenuAddCallback(menu, title, NULL, NULL);
721 wMenuEntrySetCascade(menu, entry, wsmenu);
723 wWorkspaceMenuUpdate(scr, wsmenu);
725 return entry;
728 static void cleanupWindowsMenu(WMenu * menu)
730 if (menu->frame->screen_ptr->switch_menu == menu)
731 menu->frame->screen_ptr->switch_menu = NULL;
734 static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
736 WMenu *wwmenu;
737 WWindow *wwin;
738 WMenuEntry *entry;
740 if (scr->flags.added_windows_menu) {
741 wwarning(_
742 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
743 return NULL;
744 } else {
745 scr->flags.added_windows_menu = 1;
747 wwmenu = wMenuCreate(scr, _("Window List"), False);
748 wwmenu->on_destroy = cleanupWindowsMenu;
749 scr->switch_menu = wwmenu;
750 wwin = scr->focused_window;
751 while (wwin) {
752 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
754 wwin = wwin->prev;
756 entry = wMenuAddCallback(menu, title, NULL, NULL);
757 wMenuEntrySetCascade(menu, entry, wwmenu);
759 return entry;
762 static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
763 char *params, const char *file_name)
765 WScreen *scr;
766 WMenuEntry *entry = NULL;
767 Bool shortcutOk = False;
769 if (!menu)
770 return NULL;
771 scr = menu->frame->screen_ptr;
772 if (strcmp(command, "OPEN_MENU") == 0) {
773 if (!params) {
774 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
775 } else {
776 WMenu *dummy;
777 char *path;
779 path = wfindfile(DEF_CONFIG_PATHS, params);
780 if (!path) {
781 path = wstrdup(params);
783 dummy = wMenuCreate(scr, title, False);
784 dummy->on_destroy = removeShortcutsForMenu;
785 entry = wMenuAddCallback(menu, title, constructMenu, path);
786 entry->free_cdata = wfree;
787 wMenuEntrySetCascade(menu, entry, dummy);
789 } else if (strcmp(command, "EXEC") == 0) {
790 if (!params)
791 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
792 else {
793 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
794 entry->free_cdata = wfree;
795 shortcutOk = True;
797 } else if (strcmp(command, "SHEXEC") == 0) {
798 if (!params)
799 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
800 else {
801 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
802 entry->free_cdata = wfree;
803 shortcutOk = True;
805 } else if (strcmp(command, "EXIT") == 0) {
807 if (params && strcmp(params, "QUICK") == 0)
808 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
809 else
810 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
812 shortcutOk = True;
813 } else if (strcmp(command, "SHUTDOWN") == 0) {
815 if (params && strcmp(params, "QUICK") == 0)
816 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
817 else
818 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
820 shortcutOk = True;
821 } else if (strcmp(command, "REFRESH") == 0) {
822 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
824 shortcutOk = True;
825 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
826 entry = addWorkspaceMenu(scr, menu, title);
828 shortcutOk = True;
829 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
830 entry = addWindowsMenu(scr, menu, title);
832 shortcutOk = True;
833 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
834 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
836 shortcutOk = True;
837 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
838 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
840 shortcutOk = True;
841 } else if (strcmp(command, "SHOW_ALL") == 0) {
842 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
844 shortcutOk = True;
845 } else if (strcmp(command, "RESTART") == 0) {
846 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
847 entry->free_cdata = wfree;
848 shortcutOk = True;
849 } else if (strcmp(command, "SAVE_SESSION") == 0) {
850 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
852 shortcutOk = True;
853 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
854 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
855 shortcutOk = True;
856 } else if (strcmp(command, "INFO_PANEL") == 0) {
857 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
858 shortcutOk = True;
859 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
860 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
861 shortcutOk = True;
862 } else {
863 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
865 return NULL;
868 if (shortcut && entry) {
869 if (!shortcutOk) {
870 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
871 } else {
872 if (addShortcut(file_name, shortcut, menu, entry)) {
874 entry->rtext = GetShortcutString(shortcut);
876 entry->rtext = wstrdup(shortcut);
882 return entry;
885 /******************* Menu Configuration From File *******************/
887 static void freeline(char *title, char *command, char *parameter, char *shortcut)
889 wfree(title);
890 wfree(command);
891 wfree(parameter);
892 wfree(shortcut);
895 static WMenu *parseCascade(WScreen * scr, WMenu * menu, WMenuParser parser)
897 char *command, *params, *shortcut, *title;
899 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
901 if (command == NULL || !command[0]) {
902 WMenuParserError(parser, _("missing command in menu config") );
903 freeline(title, command, params, shortcut);
904 goto error;
907 if (strcasecmp(command, "MENU") == 0) {
908 WMenu *cascade;
910 /* start submenu */
912 cascade = wMenuCreate(scr, M_(title), False);
913 cascade->on_destroy = removeShortcutsForMenu;
914 if (!parseCascade(scr, cascade, parser)) {
915 wMenuDestroy(cascade, True);
916 } else {
917 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
919 } else if (strcasecmp(command, "END") == 0) {
920 /* end of menu */
921 freeline(title, command, params, shortcut);
922 return menu;
923 } else {
924 /* normal items */
925 addMenuEntry(menu, M_(title), shortcut, command, params, WMenuParserGetFilename(parser));
927 freeline(title, command, params, shortcut);
930 WMenuParserError(parser, _("syntax error in menu file: END declaration missing") );
932 error:
933 return NULL;
936 static WMenu *readMenuFile(WScreen * scr, char *file_name)
938 WMenu *menu = NULL;
939 FILE *file = NULL;
940 WMenuParser parser;
941 char *command, *params, *shortcut, *title;
942 char cmd[MAXLINE];
943 #ifdef USECPP
944 char *args;
945 int cpp = 0;
946 #endif
948 #ifdef USECPP
949 if (!wPreferences.flags.nocpp) {
950 args = MakeCPPArgs(file_name);
951 if (!args) {
952 wwarning(_("could not make arguments for menu file preprocessor"));
953 } else {
954 snprintf(cmd, sizeof(cmd), "%s %s %s", CPP_PATH, args, file_name);
955 wfree(args);
956 file = popen(cmd, "r");
957 if (!file) {
958 werror(_("%s:could not open/preprocess menu file"), file_name);
959 } else {
960 cpp = 1;
964 #endif /* USECPP */
966 if (!file) {
967 file = fopen(file_name, "rb");
968 if (!file) {
969 werror(_("%s:could not open menu file"), file_name);
970 return NULL;
973 parser = WMenuParserCreate(file_name, file, DEF_CONFIG_PATHS);
974 menu_parser_register_macros(parser);
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 break;
983 if (strcasecmp(command, "MENU") == 0) {
984 menu = wMenuCreate(scr, M_(title), True);
985 menu->on_destroy = removeShortcutsForMenu;
986 if (!parseCascade(scr, menu, parser)) {
987 wMenuDestroy(menu, True);
988 menu = NULL;
990 freeline(title, command, params, shortcut);
991 break;
992 } else {
993 WMenuParserError(parser, _("invalid menu file, MENU command is missing") );
994 freeline(title, command, params, shortcut);
995 break;
997 freeline(title, command, params, shortcut);
1000 WMenuParserDelete(parser);
1001 #ifdef USECPP
1002 if (cpp) {
1003 if (pclose(file) == -1) {
1004 werror(_("error reading preprocessed menu data"));
1006 } else {
1007 fclose(file);
1009 #else
1010 fclose(file);
1011 #endif
1013 return menu;
1016 /************ Menu Configuration From Pipe *************/
1018 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1020 WMenu *menu = NULL;
1021 FILE *file = NULL;
1022 WMenuParser parser;
1023 char *command, *params, *shortcut, *title;
1024 char *filename;
1025 char flat_file[MAXLINE];
1026 char cmd[MAXLINE];
1027 int i;
1028 #ifdef USECPP
1029 char *args;
1030 #endif
1032 flat_file[0] = '\0';
1034 for (i = 0; file_name[i] != NULL; i++) {
1035 strcat(flat_file, file_name[i]);
1036 strcat(flat_file, " ");
1038 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1040 #ifdef USECPP
1041 if (!wPreferences.flags.nocpp) {
1042 args = MakeCPPArgs(filename);
1043 if (!args) {
1044 wwarning(_("could not make arguments for menu file preprocessor"));
1045 } else {
1046 snprintf(cmd, sizeof(cmd), "%s | %s %s", filename, CPP_PATH, args);
1048 wfree(args);
1049 file = popen(cmd, "r");
1050 if (!file) {
1051 werror(_("%s:could not open/preprocess menu file"), filename);
1055 #endif /* USECPP */
1057 if (!file) {
1058 file = popen(filename, "rb");
1060 if (!file) {
1061 werror(_("%s:could not open menu file"), filename);
1062 return NULL;
1065 parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS);
1066 menu_parser_register_macros(parser);
1068 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1070 if (command == NULL || !command[0]) {
1071 WMenuParserError(parser, _("missing command in menu config") );
1072 freeline(title, command, params, shortcut);
1073 break;
1075 if (strcasecmp(command, "MENU") == 0) {
1076 menu = wMenuCreate(scr, M_(title), True);
1077 menu->on_destroy = removeShortcutsForMenu;
1078 if (!parseCascade(scr, menu, parser)) {
1079 wMenuDestroy(menu, True);
1080 menu = NULL;
1082 freeline(title, command, params, shortcut);
1083 break;
1084 } else {
1085 WMenuParserError(parser, _("no title given for the root menu") );
1086 freeline(title, command, params, shortcut);
1087 break;
1090 freeline(title, command, params, shortcut);
1093 WMenuParserDelete(parser);
1094 pclose(file);
1096 return menu;
1099 typedef struct {
1100 char *name;
1101 int index;
1102 } dir_data;
1104 static int myCompare(const void *d1, const void *d2)
1106 dir_data *p1 = *(dir_data **) d1;
1107 dir_data *p2 = *(dir_data **) d2;
1109 return strcmp(p1->name, p2->name);
1112 /***** Preset some macro for file parser *****/
1113 static void menu_parser_register_macros(WMenuParser parser)
1115 Visual *visual;
1116 char buf[32];
1118 // Used to return CPP verion, now returns wmaker's version
1119 WMenuParserRegisterSimpleMacro(parser, "__VERSION__", VERSION);
1121 // All macros below were historically defined by WindowMaker
1122 visual = DefaultVisual(dpy, DefaultScreen(dpy));
1123 snprintf(buf, sizeof(buf), "%d", visual->class);
1124 WMenuParserRegisterSimpleMacro(parser, "VISUAL", buf);
1126 snprintf(buf, sizeof(buf), "%d", DefaultDepth(dpy, DefaultScreen(dpy)) );
1127 WMenuParserRegisterSimpleMacro(parser, "DEPTH", buf);
1129 snprintf(buf, sizeof(buf), "%d", WidthOfScreen(DefaultScreenOfDisplay(dpy)) );
1130 WMenuParserRegisterSimpleMacro(parser, "SCR_WIDTH", buf);
1132 snprintf(buf, sizeof(buf), "%d", HeightOfScreen(DefaultScreenOfDisplay(dpy)) );
1133 WMenuParserRegisterSimpleMacro(parser, "SCR_HEIGHT", buf);
1135 WMenuParserRegisterSimpleMacro(parser, "DISPLAY", XDisplayName(DisplayString(dpy)) );
1137 WMenuParserRegisterSimpleMacro(parser, "WM_VERSION", "\"" VERSION "\"");
1140 /************ Menu Configuration From Directory *************/
1142 static Bool isFilePackage(char *file)
1144 int l;
1146 /* check if the extension indicates this file is a
1147 * file package. For now, only recognize .themed */
1149 l = strlen(file);
1151 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1152 return True;
1153 } else {
1154 return False;
1158 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
1160 DIR *dir;
1161 struct dirent *dentry;
1162 struct stat stat_buf;
1163 WMenu *menu = NULL;
1164 char *buffer;
1165 WMArray *dirs = NULL, *files = NULL;
1166 WMArrayIterator iter;
1167 int length, i, have_space = 0;
1168 dir_data *data;
1169 int stripExtension = 0;
1171 dirs = WMCreateArray(16);
1172 files = WMCreateArray(16);
1174 i = 0;
1175 while (path[i] != NULL) {
1176 if (strcmp(path[i], "-noext") == 0) {
1177 stripExtension = 1;
1178 i++;
1179 continue;
1182 dir = opendir(path[i]);
1183 if (!dir) {
1184 i++;
1185 continue;
1188 while ((dentry = readdir(dir))) {
1190 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1191 continue;
1193 if (dentry->d_name[0] == '.')
1194 continue;
1196 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1197 if (!buffer) {
1198 werror(_("out of memory while constructing directory menu %s"), path[i]);
1199 break;
1202 strcpy(buffer, path[i]);
1203 strcat(buffer, "/");
1204 strcat(buffer, dentry->d_name);
1206 if (stat(buffer, &stat_buf) != 0) {
1207 werror(_("%s:could not stat file \"%s\" in menu directory"),
1208 path[i], dentry->d_name);
1209 } else {
1210 Bool isFilePack = False;
1212 data = NULL;
1213 if (S_ISDIR(stat_buf.st_mode)
1214 && !(isFilePack = isFilePackage(dentry->d_name))) {
1216 /* access always returns success for user root */
1217 if (access(buffer, X_OK) == 0) {
1218 /* Directory is accesible. Add to directory list */
1220 data = (dir_data *) wmalloc(sizeof(dir_data));
1221 data->name = wstrdup(dentry->d_name);
1222 data->index = i;
1224 WMAddToArray(dirs, data);
1226 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1227 /* Hack because access always returns X_OK success for user root */
1228 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1229 if ((command != NULL && access(buffer, R_OK) == 0) ||
1230 (command == NULL && access(buffer, X_OK) == 0 &&
1231 (stat_buf.st_mode & S_IXANY))) {
1233 data = (dir_data *) wmalloc(sizeof(dir_data));
1234 data->name = wstrdup(dentry->d_name);
1235 data->index = i;
1237 WMAddToArray(files, data);
1241 free(buffer);
1244 closedir(dir);
1245 i++;
1248 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1249 WMFreeArray(dirs);
1250 WMFreeArray(files);
1251 return NULL;
1254 WMSortArray(dirs, myCompare);
1255 WMSortArray(files, myCompare);
1257 menu = wMenuCreate(scr, M_(title), False);
1258 menu->on_destroy = removeShortcutsForMenu;
1260 WM_ITERATE_ARRAY(dirs, data, iter) {
1261 /* New directory. Use same OPEN_MENU command that was used
1262 * for the current directory. */
1263 length = strlen(path[data->index]) + strlen(data->name) + 6;
1264 if (stripExtension)
1265 length += 7;
1266 if (command)
1267 length += strlen(command) + 6;
1268 buffer = malloc(length);
1269 if (!buffer) {
1270 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1271 break;
1274 buffer[0] = '\0';
1275 if (stripExtension)
1276 strcat(buffer, "-noext ");
1278 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1280 if (have_space)
1281 strcat(buffer, "\"");
1282 strcat(buffer, path[data->index]);
1284 strcat(buffer, "/");
1285 strcat(buffer, data->name);
1286 if (have_space)
1287 strcat(buffer, "\"");
1288 if (command) {
1289 strcat(buffer, " WITH ");
1290 strcat(buffer, command);
1293 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1295 wfree(buffer);
1296 if (data->name)
1297 wfree(data->name);
1298 wfree(data);
1301 WM_ITERATE_ARRAY(files, data, iter) {
1302 /* executable: add as entry */
1303 length = strlen(path[data->index]) + strlen(data->name) + 6;
1304 if (command)
1305 length += strlen(command);
1307 buffer = malloc(length);
1308 if (!buffer) {
1309 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1310 break;
1313 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1314 if (command != NULL) {
1315 strcpy(buffer, command);
1316 strcat(buffer, " ");
1317 if (have_space)
1318 strcat(buffer, "\"");
1319 strcat(buffer, path[data->index]);
1320 } else {
1321 if (have_space) {
1322 buffer[0] = '"';
1323 buffer[1] = 0;
1324 strcat(buffer, path[data->index]);
1325 } else {
1326 strcpy(buffer, path[data->index]);
1329 strcat(buffer, "/");
1330 strcat(buffer, data->name);
1331 if (have_space)
1332 strcat(buffer, "\"");
1334 if (stripExtension) {
1335 char *ptr = strrchr(data->name, '.');
1336 if (ptr && ptr != data->name)
1337 *ptr = 0;
1339 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1341 wfree(buffer);
1342 if (data->name)
1343 wfree(data->name);
1344 wfree(data);
1347 WMFreeArray(files);
1348 WMFreeArray(dirs);
1350 return menu;
1353 /************ Menu Configuration From WMRootMenu *************/
1355 static WMenu *makeDefaultMenu(WScreen * scr)
1357 WMenu *menu = NULL;
1359 menu = wMenuCreate(scr, _("Commands"), True);
1360 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1361 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1362 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1363 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1364 return menu;
1368 *----------------------------------------------------------------------
1369 * configureMenu--
1370 * Reads root menu configuration from defaults database.
1372 *----------------------------------------------------------------------
1374 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1376 WMenu *menu = NULL;
1377 WMPropList *elem;
1378 int i, count;
1379 WMPropList *title, *command, *params;
1380 char *tmp, *mtitle;
1382 if (WMIsPLString(definition)) {
1383 struct stat stat_buf;
1384 char *path = NULL;
1385 Bool menu_is_default = False;
1387 /* menu definition is a string. Probably a path, so parse the file */
1389 tmp = wexpandpath(WMGetFromPLString(definition));
1391 path = getLocalizedMenuFile(tmp);
1393 if (!path)
1394 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1396 if (!path) {
1397 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1398 menu_is_default = True;
1401 if (!path) {
1402 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1403 wfree(tmp);
1404 return NULL;
1407 if (stat(path, &stat_buf) < 0) {
1408 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1409 wfree(path);
1410 wfree(tmp);
1411 return NULL;
1414 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1415 /* if the pointer in WMRootMenu has changed */
1416 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1418 if (menu_is_default) {
1419 wwarning(_
1420 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1421 path);
1424 menu = readMenuFile(scr, path);
1425 if (menu)
1426 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1427 } else {
1428 menu = NULL;
1430 wfree(path);
1431 wfree(tmp);
1433 return menu;
1436 count = WMGetPropListItemCount(definition);
1437 if (count == 0)
1438 return NULL;
1440 elem = WMGetFromPLArray(definition, 0);
1441 if (!WMIsPLString(elem)) {
1442 tmp = WMGetPropListDescription(elem, False);
1443 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1444 wfree(tmp);
1445 return NULL;
1447 mtitle = WMGetFromPLString(elem);
1449 menu = wMenuCreate(scr, M_(mtitle), False);
1450 menu->on_destroy = removeShortcutsForMenu;
1452 #ifdef GLOBAL_SUBMENU_FILE
1453 if (includeGlobals) {
1454 WMenu *submenu;
1455 WMenuEntry *mentry;
1457 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1459 if (submenu) {
1460 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1461 wMenuEntrySetCascade(menu, mentry, submenu);
1464 #endif
1466 for (i = 1; i < count; i++) {
1467 elem = WMGetFromPLArray(definition, i);
1468 #if 0
1469 if (WMIsPLString(elem)) {
1470 char *file;
1472 file = WMGetFromPLString(elem);
1475 #endif
1476 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1477 goto error;
1479 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1480 WMenu *submenu;
1481 WMenuEntry *mentry;
1483 /* submenu */
1484 submenu = configureMenu(scr, elem, True);
1485 if (submenu) {
1486 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1487 wMenuEntrySetCascade(menu, mentry, submenu);
1489 } else {
1490 int idx = 0;
1491 WMPropList *shortcut;
1492 /* normal entry */
1494 title = WMGetFromPLArray(elem, idx++);
1495 shortcut = WMGetFromPLArray(elem, idx++);
1496 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1497 shortcut = WMGetFromPLArray(elem, idx++);
1498 command = WMGetFromPLArray(elem, idx++);
1499 } else {
1500 command = shortcut;
1501 shortcut = NULL;
1503 params = WMGetFromPLArray(elem, idx++);
1505 if (!title || !command)
1506 goto error;
1508 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1509 shortcut ? WMGetFromPLString(shortcut) : NULL,
1510 WMGetFromPLString(command),
1511 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1513 continue;
1515 error:
1516 tmp = WMGetPropListDescription(elem, False);
1517 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1518 wfree(tmp);
1521 return menu;
1525 *----------------------------------------------------------------------
1526 * OpenRootMenu--
1527 * Opens the root menu, parsing the menu configuration from the
1528 * defaults database.
1529 * If the menu is already mapped and is not sticked to the
1530 * root window, it will be unmapped.
1532 * Side effects:
1533 * The menu may be remade.
1535 * Notes:
1536 * Construction of OPEN_MENU entries are delayed to the moment the
1537 * user map's them.
1538 *----------------------------------------------------------------------
1540 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1542 WMenu *menu = NULL;
1543 WMPropList *definition;
1545 static WMPropList *domain=NULL;
1547 if (!domain) {
1548 domain = WMCreatePLString("WMRootMenu");
1552 scr->flags.root_menu_changed_shortcuts = 0;
1553 scr->flags.added_workspace_menu = 0;
1554 scr->flags.added_windows_menu = 0;
1556 if (scr->root_menu && scr->root_menu->flags.mapped) {
1557 menu = scr->root_menu;
1558 if (!menu->flags.buttoned) {
1559 wMenuUnmap(menu);
1560 } else {
1561 wRaiseFrame(menu->frame->core);
1563 if (keyboard)
1564 wMenuMapAt(menu, 0, 0, True);
1565 else
1566 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1568 return;
1571 definition = WDRootMenu->dictionary;
1574 definition = PLGetDomain(domain);
1576 if (definition) {
1577 if (WMIsPLArray(definition)) {
1578 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1579 menu = configureMenu(scr, definition, True);
1580 if (menu)
1581 menu->timestamp = WDRootMenu->timestamp;
1583 } else
1584 menu = NULL;
1585 } else {
1586 menu = configureMenu(scr, definition, True);
1590 if (!menu) {
1591 /* menu hasn't changed or could not be read */
1592 if (!scr->root_menu) {
1593 wMessageDialog(scr, _("Error"),
1594 _("The applications menu could not be loaded. "
1595 "Look at the console output for a detailed "
1596 "description of the errors."), _("OK"), NULL, NULL);
1598 menu = makeDefaultMenu(scr);
1599 scr->root_menu = menu;
1601 menu = scr->root_menu;
1602 } else {
1603 /* new root menu */
1604 if (scr->root_menu) {
1605 wMenuDestroy(scr->root_menu, True);
1607 scr->root_menu = menu;
1609 if (menu) {
1610 int newx, newy;
1612 if (keyboard && x == 0 && y == 0) {
1613 newx = newy = 0;
1614 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1615 newx = x - menu->frame->core->width / 2;
1616 newy = y - menu->frame->core->height / 2;
1617 } else {
1618 newx = x - menu->frame->core->width / 2;
1619 newy = y;
1621 wMenuMapAt(menu, newx, newy, keyboard);
1624 if (scr->flags.root_menu_changed_shortcuts)
1625 rebindKeygrabs(scr);