wmaker: Replaced local 'extern' definition of wPreferences by proper header usage
[wmaker-crm.git] / src / rootmenu.c
blob64939f7e2e5d351d4cac12b22f4ebd79c3158bc7
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
63 extern char *Locale;
64 extern WDDomain *WDRootMenu;
65 extern Cursor wCursor[WCUR_LAST];
67 static WMenu *readMenuPipe(WScreen * scr, char **file_name);
68 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name);
69 static WMenu *readMenuFile(WScreen *scr, const char *file_name);
70 static WMenu *readMenuDirectory(WScreen *scr, const char *title, char **file_name, const char *command);
71 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals);
72 static void menu_parser_register_macros(WMenuParser parser);
74 typedef struct Shortcut {
75 struct Shortcut *next;
77 int modifier;
78 KeyCode keycode;
79 WMenuEntry *entry;
80 WMenu *menu;
81 } Shortcut;
83 static Shortcut *shortcutList = NULL;
86 * Syntax:
87 * # main menu
88 * "Menu Name" MENU
89 * "Title" EXEC command_to_exec -params
90 * "Submenu" MENU
91 * "Title" EXEC command_to_exec -params
92 * "Submenu" END
93 * "Workspaces" WORKSPACE_MENU
94 * "Title" built_in_command
95 * "Quit" EXIT
96 * "Quick Quit" EXIT QUICK
97 * "Menu Name" END
99 * Commands may be preceded by SHORTCUT key
101 * Built-in commands:
103 * INFO_PANEL - shows the Info Panel
104 * LEGAL_PANEL - shows the Legal info panel
105 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
106 * REFRESH - forces the desktop to be repainted
107 * EXIT [QUICK] - exit the window manager [without confirmation]
108 * EXEC <program> - execute an external program
109 * SHEXEC <command> - execute a shell command
110 * WORKSPACE_MENU - places the workspace submenu
111 * ARRANGE_ICONS
112 * RESTART [<window manager>] - restarts the window manager
113 * SHOW_ALL - unhide all windows on workspace
114 * HIDE_OTHERS - hides all windows excep the focused one
115 * OPEN_MENU file - read menu data from file which must be a valid menu file.
116 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
117 * - read menu data from directory(ies) and
118 * eventually precede each with a command.
119 * OPEN_MENU | command
120 * - opens command and uses its stdout to construct and insert
121 * the resulting menu in current position. The output of
122 * command must be a valid menu description.
123 * The space between '|' and command is optional.
124 * || will do the same, but will not cache the contents.
125 * OPEN_PLMENU | command
126 * - opens command and uses its stdout which must be in proplist
127 * fromat to construct and insert the resulting menu in current
128 * position.
129 * The space between '|' and command is optional.
130 * || will do the same, but will not cache the contents.
131 * SAVE_SESSION - saves the current state of the desktop, which include
132 * all running applications, all their hints (geometry,
133 * position on screen, workspace they live on, the dock
134 * or clip from where they were launched, and
135 * if minimized, shaded or hidden. Also saves the current
136 * workspace the user is on. All will be restored on every
137 * start of windowmaker until another SAVE_SESSION or
138 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
139 * WindowMaker domain file, then saving is automatically
140 * done on every windowmaker exit, overwriting any
141 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
142 * dock state now.
143 * CLEAR_SESSION - clears any previous saved session. This will not have
144 * any effect if SaveSessionOnExit is True.
148 #define M_QUICK 1
150 /* menu commands */
152 static void execCommand(WMenu * menu, WMenuEntry * entry)
154 char *cmdline;
156 cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
158 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
159 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);
160 XSync(dpy, 0);
162 if (cmdline) {
163 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
164 wfree(cmdline);
166 XUngrabPointer(dpy, CurrentTime);
167 XSync(dpy, 0);
170 static void exitCommand(WMenu * menu, WMenuEntry * entry)
172 static int inside = 0;
173 int result;
175 /* prevent reentrant calls */
176 if (inside)
177 return;
178 inside = 1;
180 #define R_CANCEL 0
181 #define R_EXIT 1
183 result = R_CANCEL;
185 if ((long)entry->clientdata == M_QUICK) {
186 result = R_EXIT;
187 } else {
188 int r, oldSaveSessionFlag;
190 oldSaveSessionFlag = wPreferences.save_session_on_exit;
191 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
192 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
194 if (r == WAPRDefault) {
195 result = R_EXIT;
196 } else if (r == WAPRAlternate) {
197 /* Don't modify the "save session on exit" flag if the
198 * user canceled the operation. */
199 wPreferences.save_session_on_exit = oldSaveSessionFlag;
202 if (result == R_EXIT)
203 Shutdown(WSExitMode);
205 #undef R_EXIT
206 #undef R_CANCEL
207 inside = 0;
210 static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
212 static int inside = 0;
213 int result;
215 /* prevent reentrant calls */
216 if (inside)
217 return;
218 inside = 1;
220 #define R_CANCEL 0
221 #define R_CLOSE 1
222 #define R_KILL 2
224 result = R_CANCEL;
225 if ((long)entry->clientdata == M_QUICK)
226 result = R_CLOSE;
227 else {
228 int r, oldSaveSessionFlag;
230 oldSaveSessionFlag = wPreferences.save_session_on_exit;
232 r = wExitDialog(menu->frame->screen_ptr,
233 _("Kill X session"),
234 _("Kill Window System session?\n"
235 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
236 if (r == WAPRDefault) {
237 result = R_KILL;
238 } else if (r == WAPRAlternate) {
239 /* Don't modify the "save session on exit" flag if the
240 * user canceled the operation. */
241 wPreferences.save_session_on_exit = oldSaveSessionFlag;
245 if (result != R_CANCEL) {
246 Shutdown(WSKillMode);
248 #undef R_CLOSE
249 #undef R_CANCEL
250 #undef R_KILL
251 inside = 0;
254 static void restartCommand(WMenu * menu, WMenuEntry * entry)
256 Shutdown(WSRestartPreparationMode);
257 Restart((char *)entry->clientdata, False);
258 Restart(NULL, True);
261 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
263 wRefreshDesktop(menu->frame->screen_ptr);
266 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
268 wArrangeIcons(menu->frame->screen_ptr, True);
271 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
273 wShowAllWindows(menu->frame->screen_ptr);
276 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
278 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
281 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
283 if (!wPreferences.save_session_on_exit)
284 wSessionSaveState(menu->frame->screen_ptr);
286 wScreenSaveState(menu->frame->screen_ptr);
289 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
291 wSessionClearState(menu->frame->screen_ptr);
292 wScreenSaveState(menu->frame->screen_ptr);
295 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
297 wShowInfoPanel(menu->frame->screen_ptr);
300 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
302 wShowLegalPanel(menu->frame->screen_ptr);
305 /********************************************************************/
307 static char *getLocalizedMenuFile(const char *menu)
309 char *buffer, *ptr, *locale;
310 int len;
312 if (!Locale)
313 return NULL;
315 len = strlen(menu) + strlen(Locale) + 8;
316 buffer = wmalloc(len);
318 /* try menu.locale_name */
319 snprintf(buffer, len, "%s.%s", menu, Locale);
320 if (access(buffer, F_OK) == 0)
321 return buffer;
323 /* position of locale in our buffer */
324 locale = buffer + strlen(menu) + 1;
326 /* check if it is in the form aa_bb.encoding and check for aa_bb */
327 ptr = strchr(locale, '.');
328 if (ptr) {
329 *ptr = 0;
330 if (access(buffer, F_OK) == 0)
331 return buffer;
334 /* now check for aa */
335 ptr = strchr(locale, '_');
336 if (ptr) {
337 *ptr = 0;
338 if (access(buffer, F_OK) == 0)
339 return buffer;
342 wfree(buffer);
344 return NULL;
347 Bool wRootMenuPerformShortcut(XEvent * event)
349 WScreen *scr = wScreenForRootWindow(event->xkey.root);
350 Shortcut *ptr;
351 int modifiers;
352 int done = 0;
354 /* ignore CapsLock */
355 modifiers = event->xkey.state & ValidModMask;
357 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
358 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
359 continue;
361 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
362 (*ptr->entry->callback) (ptr->menu, ptr->entry);
363 done = True;
367 return done;
370 void wRootMenuBindShortcuts(Window window)
372 Shortcut *ptr;
374 ptr = shortcutList;
375 while (ptr) {
376 if (ptr->modifier != AnyModifier) {
377 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
378 window, True, GrabModeAsync, GrabModeAsync);
379 #ifdef NUMLOCK_HACK
380 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
381 #endif
383 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
384 ptr = ptr->next;
388 static void rebindKeygrabs(WScreen * scr)
390 WWindow *wwin;
392 wwin = scr->focused_window;
394 while (wwin != NULL) {
395 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
397 if (!WFLAGP(wwin, no_bind_keys)) {
398 wWindowSetKeyGrabs(wwin);
400 wwin = wwin->prev;
404 static void removeShortcutsForMenu(WMenu * menu)
406 Shortcut *ptr, *tmp;
407 Shortcut *newList = NULL;
409 ptr = shortcutList;
410 while (ptr != NULL) {
411 tmp = ptr->next;
412 if (ptr->menu == menu) {
413 wfree(ptr);
414 } else {
415 ptr->next = newList;
416 newList = ptr;
418 ptr = tmp;
420 shortcutList = newList;
421 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
424 static Bool addShortcut(const char *file, const char *shortcutDefinition, WMenu *menu, WMenuEntry *entry)
426 Shortcut *ptr;
427 KeySym ksym;
428 char *k;
429 char buf[MAX_SHORTCUT_LENGTH], *b;
431 ptr = wmalloc(sizeof(Shortcut));
433 wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
434 b = (char *)buf;
436 /* get modifiers */
437 ptr->modifier = 0;
438 while ((k = strchr(b, '+')) != NULL) {
439 int mod;
441 *k = 0;
442 mod = wXModifierFromKey(b);
443 if (mod < 0) {
444 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
445 wfree(ptr);
446 return False;
448 ptr->modifier |= mod;
450 b = k + 1;
453 /* get key */
454 ksym = XStringToKeysym(b);
456 if (ksym == NoSymbol) {
457 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
458 file, shortcutDefinition, entry->text);
459 wfree(ptr);
460 return False;
463 ptr->keycode = XKeysymToKeycode(dpy, ksym);
464 if (ptr->keycode == 0) {
465 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
466 shortcutDefinition, entry->text);
467 wfree(ptr);
468 return False;
471 ptr->menu = menu;
472 ptr->entry = entry;
474 ptr->next = shortcutList;
475 shortcutList = ptr;
477 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
479 return True;
482 static char *next_token(char *line, char **next)
484 char *tmp, c;
485 char *ret;
487 *next = NULL;
488 while (*line == ' ' || *line == '\t')
489 line++;
491 tmp = line;
493 if (*tmp == '"') {
494 tmp++;
495 line++;
496 while (*tmp != 0 && *tmp != '"')
497 tmp++;
498 if (*tmp != '"') {
499 wwarning(_("%s: unmatched '\"' in menu file"), line);
500 return NULL;
502 } else {
503 do {
504 if (*tmp == '\\')
505 tmp++;
507 if (*tmp != 0)
508 tmp++;
510 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
513 c = *tmp;
514 *tmp = 0;
515 ret = wstrdup(line);
516 *tmp = c;
518 if (c == 0)
519 return ret;
520 else
521 tmp++;
523 /* skip blanks */
524 while (*tmp == ' ' || *tmp == '\t')
525 tmp++;
527 if (*tmp != 0)
528 *next = tmp;
530 return ret;
533 static void separateCommand(char *line, char ***file, char **command)
535 char *token, *tmp = line;
536 WMArray *array = WMCreateArray(4);
537 int count, i;
539 *file = NULL;
540 *command = NULL;
541 do {
542 token = next_token(tmp, &tmp);
543 if (token) {
544 if (strcmp(token, "WITH") == 0) {
545 if (tmp != NULL && *tmp != 0)
546 *command = wstrdup(tmp);
547 else
548 wwarning(_("%s: missing command"), line);
549 break;
551 WMAddToArray(array, token);
553 } while (token != NULL && tmp != NULL);
555 count = WMGetArrayItemCount(array);
556 if (count > 0) {
557 *file = wmalloc(sizeof(char *) * (count + 1));
558 (*file)[count] = NULL;
559 for (i = 0; i < count; i++) {
560 (*file)[i] = WMGetFromArray(array, i);
563 WMFreeArray(array);
566 static WMenu *constructPLMenu(WScreen *screen, const char *path)
568 WMPropList *pl = NULL;
569 WMenu *menu = NULL;
571 if (!path)
572 return NULL;
574 pl = WMReadPropListFromFile(path);
575 if (!pl)
576 return NULL;
578 menu = configureMenu(screen, pl, False);
580 WMReleasePropList(pl);
582 if (!menu)
583 return NULL;
585 menu->on_destroy = removeShortcutsForMenu;
586 return menu;
591 static void constructMenu(WMenu * menu, WMenuEntry * entry)
593 WMenu *submenu;
594 struct stat stat_buf;
595 char **path;
596 char *cmd;
597 char *lpath = NULL;
598 int i, first = -1;
599 time_t last = 0;
601 separateCommand((char *)entry->clientdata, &path, &cmd);
602 if (path == NULL || *path == NULL || **path == 0) {
603 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
604 if (cmd)
605 wfree(cmd);
606 return;
609 if (path[0][0] == '|') {
610 /* pipe menu */
612 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
613 /* parse pipe */
615 submenu = readMenuPipe(menu->frame->screen_ptr, path);
617 if (submenu != NULL) {
618 if (path[0][1] == '|')
619 submenu->timestamp = 0;
620 else
621 submenu->timestamp = 1; /* there's no automatic reloading */
623 } else {
624 submenu = NULL;
627 } else {
629 /* try interpreting path as a proplist file */
630 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
631 /* if unsuccessful, try it as an old-style file */
632 if (!submenu) {
634 i = 0;
635 while (path[i] != NULL) {
636 char *tmp;
638 if (strcmp(path[i], "-noext") == 0) {
639 i++;
640 continue;
643 tmp = wexpandpath(path[i]);
644 wfree(path[i]);
645 lpath = getLocalizedMenuFile(tmp);
646 if (lpath) {
647 wfree(tmp);
648 path[i] = lpath;
649 lpath = NULL;
650 } else {
651 path[i] = tmp;
654 if (stat(path[i], &stat_buf) == 0) {
655 if (last < stat_buf.st_mtime)
656 last = stat_buf.st_mtime;
657 if (first < 0)
658 first = i;
659 } else {
660 werror(_("%s:could not stat menu"), path[i]);
661 /*goto finish; */
664 i++;
667 if (first < 0) {
668 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
669 goto finish;
671 stat(path[first], &stat_buf);
672 if (!menu->cascades[entry->cascade]
673 || menu->cascades[entry->cascade]->timestamp < last) {
675 if (S_ISDIR(stat_buf.st_mode)) {
676 /* menu directory */
677 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
678 if (submenu)
679 submenu->timestamp = last;
680 } else if (S_ISREG(stat_buf.st_mode)) {
681 /* menu file */
683 if (cmd || path[1])
684 wwarning(_("too many parameters in OPEN_MENU: %s"),
685 (char *)entry->clientdata);
687 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
688 if (submenu)
689 submenu->timestamp = stat_buf.st_mtime;
690 } else {
691 submenu = NULL;
693 } else {
694 submenu = NULL;
699 if (submenu) {
700 wMenuEntryRemoveCascade(menu, entry);
701 wMenuEntrySetCascade(menu, entry, submenu);
704 finish:
705 i = 0;
706 while (path[i] != NULL)
707 wfree(path[i++]);
708 wfree(path);
709 if (cmd)
710 wfree(cmd);
713 static void constructPLMenuFromPipe(WMenu * menu, WMenuEntry * entry)
715 WMenu *submenu = NULL;
716 char **path;
717 char *cmd;
718 int i;
720 separateCommand((char *)entry->clientdata, &path, &cmd);
721 if (path == NULL || *path == NULL || **path == 0) {
722 wwarning(_("invalid OPEN_PLMENU specification: %s"),
723 (char *)entry->clientdata);
724 if (cmd)
725 wfree(cmd);
726 return;
729 if (path[0][0] == '|') {
730 /* pipe menu */
732 if (!menu->cascades[entry->cascade]
733 || menu->cascades[entry->cascade]->timestamp == 0) {
734 /* parse pipe */
735 submenu = readPLMenuPipe(menu->frame->screen_ptr, path);
737 if (submenu != NULL) {
738 if (path[0][1] == '|')
739 submenu->timestamp = 0;
740 else
741 submenu->timestamp = 1; /* there's no automatic reloading */
746 if (submenu) {
747 wMenuEntryRemoveCascade(menu, entry);
748 wMenuEntrySetCascade(menu, entry, submenu);
751 i = 0;
752 while (path[i] != NULL)
753 wfree(path[i++]);
755 wfree(path);
756 if (cmd)
757 wfree(cmd);
760 static void cleanupWorkspaceMenu(WMenu * menu)
762 if (menu->frame->screen_ptr->workspace_menu == menu)
763 menu->frame->screen_ptr->workspace_menu = NULL;
766 static WMenuEntry *addWorkspaceMenu(WScreen *scr, WMenu *menu, const char *title)
768 WMenu *wsmenu;
769 WMenuEntry *entry;
771 if (scr->flags.added_workspace_menu) {
772 wwarning(_
773 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
774 return NULL;
775 } else {
776 scr->flags.added_workspace_menu = 1;
778 wsmenu = wWorkspaceMenuMake(scr, True);
779 wsmenu->on_destroy = cleanupWorkspaceMenu;
781 scr->workspace_menu = wsmenu;
782 entry = wMenuAddCallback(menu, title, NULL, NULL);
783 wMenuEntrySetCascade(menu, entry, wsmenu);
785 wWorkspaceMenuUpdate(scr, wsmenu);
787 return entry;
790 static void cleanupWindowsMenu(WMenu * menu)
792 if (menu->frame->screen_ptr->switch_menu == menu)
793 menu->frame->screen_ptr->switch_menu = NULL;
796 static WMenuEntry *addWindowsMenu(WScreen *scr, WMenu *menu, const char *title)
798 WMenu *wwmenu;
799 WWindow *wwin;
800 WMenuEntry *entry;
802 if (scr->flags.added_windows_menu) {
803 wwarning(_
804 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
805 return NULL;
806 } else {
807 scr->flags.added_windows_menu = 1;
809 wwmenu = wMenuCreate(scr, _("Window List"), False);
810 wwmenu->on_destroy = cleanupWindowsMenu;
811 scr->switch_menu = wwmenu;
812 wwin = scr->focused_window;
813 while (wwin) {
814 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
816 wwin = wwin->prev;
818 entry = wMenuAddCallback(menu, title, NULL, NULL);
819 wMenuEntrySetCascade(menu, entry, wwmenu);
821 return entry;
824 static WMenuEntry *addMenuEntry(WMenu *menu, const char *title, const char *shortcut, const char *command,
825 const char *params, const char *file_name)
827 WScreen *scr;
828 WMenuEntry *entry = NULL;
829 Bool shortcutOk = False;
831 if (!menu)
832 return NULL;
833 scr = menu->frame->screen_ptr;
834 if (strcmp(command, "OPEN_MENU") == 0) {
835 if (!params) {
836 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
837 } else {
838 WMenu *dummy;
839 char *path;
841 path = wfindfile(DEF_CONFIG_PATHS, params);
842 if (!path) {
843 path = wstrdup(params);
845 dummy = wMenuCreate(scr, title, False);
846 dummy->on_destroy = removeShortcutsForMenu;
847 entry = wMenuAddCallback(menu, title, constructMenu, path);
848 entry->free_cdata = wfree;
849 wMenuEntrySetCascade(menu, entry, dummy);
851 } else if (strcmp(command, "OPEN_PLMENU") == 0) {
852 if (!params) {
853 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
854 } else {
855 WMenu *dummy;
856 char *path;
858 path = wfindfile(DEF_CONFIG_PATHS, params);
859 if (!path)
860 path = wstrdup(params);
862 dummy = wMenuCreate(scr, title, False);
863 dummy->on_destroy = removeShortcutsForMenu;
864 entry = wMenuAddCallback(menu, title, constructPLMenuFromPipe, path);
865 entry->free_cdata = wfree;
866 wMenuEntrySetCascade(menu, entry, dummy);
868 } else if (strcmp(command, "EXEC") == 0) {
869 if (!params)
870 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
871 else {
872 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
873 entry->free_cdata = wfree;
874 shortcutOk = True;
876 } else if (strcmp(command, "SHEXEC") == 0) {
877 if (!params)
878 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
879 else {
880 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
881 entry->free_cdata = wfree;
882 shortcutOk = True;
884 } else if (strcmp(command, "EXIT") == 0) {
886 if (params && strcmp(params, "QUICK") == 0)
887 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
888 else
889 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
891 shortcutOk = True;
892 } else if (strcmp(command, "SHUTDOWN") == 0) {
894 if (params && strcmp(params, "QUICK") == 0)
895 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
896 else
897 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
899 shortcutOk = True;
900 } else if (strcmp(command, "REFRESH") == 0) {
901 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
903 shortcutOk = True;
904 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
905 entry = addWorkspaceMenu(scr, menu, title);
907 shortcutOk = True;
908 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
909 entry = addWindowsMenu(scr, menu, title);
911 shortcutOk = True;
912 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
913 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
915 shortcutOk = True;
916 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
917 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
919 shortcutOk = True;
920 } else if (strcmp(command, "SHOW_ALL") == 0) {
921 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
923 shortcutOk = True;
924 } else if (strcmp(command, "RESTART") == 0) {
925 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
926 entry->free_cdata = wfree;
927 shortcutOk = True;
928 } else if (strcmp(command, "SAVE_SESSION") == 0) {
929 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
931 shortcutOk = True;
932 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
933 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
934 shortcutOk = True;
935 } else if (strcmp(command, "INFO_PANEL") == 0) {
936 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
937 shortcutOk = True;
938 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
939 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
940 shortcutOk = True;
941 } else {
942 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
944 return NULL;
947 if (shortcut && entry) {
948 if (!shortcutOk) {
949 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
950 } else {
951 if (addShortcut(file_name, shortcut, menu, entry)) {
953 entry->rtext = GetShortcutString(shortcut);
955 entry->rtext = wstrdup(shortcut);
961 return entry;
964 /******************* Menu Configuration From File *******************/
966 static void freeline(char *title, char *command, char *parameter, char *shortcut)
968 wfree(title);
969 wfree(command);
970 wfree(parameter);
971 wfree(shortcut);
974 static WMenu *parseCascade(WScreen * scr, WMenu * menu, WMenuParser parser)
976 char *command, *params, *shortcut, *title;
978 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
980 if (command == NULL || !command[0]) {
981 WMenuParserError(parser, _("missing command in menu config") );
982 freeline(title, command, params, shortcut);
983 goto error;
986 if (strcasecmp(command, "MENU") == 0) {
987 WMenu *cascade;
989 /* start submenu */
991 cascade = wMenuCreate(scr, M_(title), False);
992 cascade->on_destroy = removeShortcutsForMenu;
993 if (!parseCascade(scr, cascade, parser)) {
994 wMenuDestroy(cascade, True);
995 } else {
996 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
998 } else if (strcasecmp(command, "END") == 0) {
999 /* end of menu */
1000 freeline(title, command, params, shortcut);
1001 return menu;
1002 } else {
1003 /* normal items */
1004 addMenuEntry(menu, M_(title), shortcut, command, params, WMenuParserGetFilename(parser));
1006 freeline(title, command, params, shortcut);
1009 WMenuParserError(parser, _("syntax error in menu file: END declaration missing") );
1011 error:
1012 return NULL;
1015 static WMenu *readMenuFile(WScreen *scr, const char *file_name)
1017 WMenu *menu = NULL;
1018 FILE *file = NULL;
1019 WMenuParser parser;
1020 char *command, *params, *shortcut, *title;
1022 file = fopen(file_name, "rb");
1023 if (!file) {
1024 werror(_("%s:could not open menu file"), file_name);
1025 return NULL;
1027 parser = WMenuParserCreate(file_name, file, DEF_CONFIG_PATHS);
1028 menu_parser_register_macros(parser);
1030 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1032 if (command == NULL || !command[0]) {
1033 WMenuParserError(parser, _("missing command in menu config") );
1034 freeline(title, command, params, shortcut);
1035 break;
1037 if (strcasecmp(command, "MENU") == 0) {
1038 menu = wMenuCreate(scr, M_(title), True);
1039 menu->on_destroy = removeShortcutsForMenu;
1040 if (!parseCascade(scr, menu, parser)) {
1041 wMenuDestroy(menu, True);
1042 menu = NULL;
1044 freeline(title, command, params, shortcut);
1045 break;
1046 } else {
1047 WMenuParserError(parser, _("invalid menu file, MENU command is missing") );
1048 freeline(title, command, params, shortcut);
1049 break;
1051 freeline(title, command, params, shortcut);
1054 WMenuParserDelete(parser);
1055 fclose(file);
1057 return menu;
1060 /************ Menu Configuration From Pipe *************/
1061 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name)
1063 WMPropList *plist = NULL;
1064 WMenu *menu = NULL;
1065 char *filename;
1066 char flat_file[MAXLINE];
1067 int i;
1069 flat_file[0] = '\0';
1071 for (i = 0; file_name[i] != NULL; i++) {
1072 strcat(flat_file, file_name[i]);
1073 strcat(flat_file, " ");
1075 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1077 plist = WMReadPropListFromPipe(filename);
1079 if (!plist)
1080 return NULL;
1082 menu = configureMenu(scr, plist, False);
1084 WMReleasePropList(plist);
1086 if (!menu)
1087 return NULL;
1089 menu->on_destroy = removeShortcutsForMenu;
1090 return menu;
1093 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1095 WMenu *menu = NULL;
1096 FILE *file = NULL;
1097 WMenuParser parser;
1098 char *command, *params, *shortcut, *title;
1099 char *filename;
1100 char flat_file[MAXLINE];
1101 int i;
1103 flat_file[0] = '\0';
1105 for (i = 0; file_name[i] != NULL; i++) {
1106 strcat(flat_file, file_name[i]);
1107 strcat(flat_file, " ");
1109 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1111 file = popen(filename, "r");
1112 if (!file) {
1113 werror(_("%s:could not open menu file"), filename);
1114 return NULL;
1116 parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS);
1117 menu_parser_register_macros(parser);
1119 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1121 if (command == NULL || !command[0]) {
1122 WMenuParserError(parser, _("missing command in menu config") );
1123 freeline(title, command, params, shortcut);
1124 break;
1126 if (strcasecmp(command, "MENU") == 0) {
1127 menu = wMenuCreate(scr, M_(title), True);
1128 menu->on_destroy = removeShortcutsForMenu;
1129 if (!parseCascade(scr, menu, parser)) {
1130 wMenuDestroy(menu, True);
1131 menu = NULL;
1133 freeline(title, command, params, shortcut);
1134 break;
1135 } else {
1136 WMenuParserError(parser, _("no title given for the root menu") );
1137 freeline(title, command, params, shortcut);
1138 break;
1141 freeline(title, command, params, shortcut);
1144 WMenuParserDelete(parser);
1145 pclose(file);
1147 return menu;
1150 typedef struct {
1151 char *name;
1152 int index;
1153 } dir_data;
1155 static int myCompare(const void *d1, const void *d2)
1157 dir_data *p1 = *(dir_data **) d1;
1158 dir_data *p2 = *(dir_data **) d2;
1160 return strcmp(p1->name, p2->name);
1163 /***** Preset some macro for file parser *****/
1164 static void menu_parser_register_macros(WMenuParser parser)
1166 Visual *visual;
1167 char buf[32];
1169 // Used to return CPP verion, now returns wmaker's version
1170 WMenuParserRegisterSimpleMacro(parser, "__VERSION__", VERSION);
1172 // All macros below were historically defined by WindowMaker
1173 visual = DefaultVisual(dpy, DefaultScreen(dpy));
1174 snprintf(buf, sizeof(buf), "%d", visual->class);
1175 WMenuParserRegisterSimpleMacro(parser, "VISUAL", buf);
1177 snprintf(buf, sizeof(buf), "%d", DefaultDepth(dpy, DefaultScreen(dpy)) );
1178 WMenuParserRegisterSimpleMacro(parser, "DEPTH", buf);
1180 snprintf(buf, sizeof(buf), "%d", WidthOfScreen(DefaultScreenOfDisplay(dpy)) );
1181 WMenuParserRegisterSimpleMacro(parser, "SCR_WIDTH", buf);
1183 snprintf(buf, sizeof(buf), "%d", HeightOfScreen(DefaultScreenOfDisplay(dpy)) );
1184 WMenuParserRegisterSimpleMacro(parser, "SCR_HEIGHT", buf);
1186 WMenuParserRegisterSimpleMacro(parser, "DISPLAY", XDisplayName(DisplayString(dpy)) );
1188 WMenuParserRegisterSimpleMacro(parser, "WM_VERSION", "\"" VERSION "\"");
1191 /************ Menu Configuration From Directory *************/
1193 static Bool isFilePackage(const char *file)
1195 int l;
1197 /* check if the extension indicates this file is a
1198 * file package. For now, only recognize .themed */
1200 l = strlen(file);
1202 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1203 return True;
1204 } else {
1205 return False;
1209 static WMenu *readMenuDirectory(WScreen *scr, const char *title, char **path, const char *command)
1211 DIR *dir;
1212 struct dirent *dentry;
1213 struct stat stat_buf;
1214 WMenu *menu = NULL;
1215 char *buffer;
1216 WMArray *dirs = NULL, *files = NULL;
1217 WMArrayIterator iter;
1218 int length, i, have_space = 0;
1219 dir_data *data;
1220 int stripExtension = 0;
1222 dirs = WMCreateArray(16);
1223 files = WMCreateArray(16);
1225 i = 0;
1226 while (path[i] != NULL) {
1227 if (strcmp(path[i], "-noext") == 0) {
1228 stripExtension = 1;
1229 i++;
1230 continue;
1233 dir = opendir(path[i]);
1234 if (!dir) {
1235 i++;
1236 continue;
1239 while ((dentry = readdir(dir))) {
1241 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1242 continue;
1244 if (dentry->d_name[0] == '.')
1245 continue;
1247 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1248 if (!buffer) {
1249 werror(_("out of memory while constructing directory menu %s"), path[i]);
1250 break;
1253 strcpy(buffer, path[i]);
1254 strcat(buffer, "/");
1255 strcat(buffer, dentry->d_name);
1257 if (stat(buffer, &stat_buf) != 0) {
1258 werror(_("%s:could not stat file \"%s\" in menu directory"),
1259 path[i], dentry->d_name);
1260 } else {
1261 Bool isFilePack = False;
1263 data = NULL;
1264 if (S_ISDIR(stat_buf.st_mode)
1265 && !(isFilePack = isFilePackage(dentry->d_name))) {
1267 /* access always returns success for user root */
1268 if (access(buffer, X_OK) == 0) {
1269 /* Directory is accesible. Add to directory list */
1271 data = (dir_data *) wmalloc(sizeof(dir_data));
1272 data->name = wstrdup(dentry->d_name);
1273 data->index = i;
1275 WMAddToArray(dirs, data);
1277 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1278 /* Hack because access always returns X_OK success for user root */
1279 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1280 if ((command != NULL && access(buffer, R_OK) == 0) ||
1281 (command == NULL && access(buffer, X_OK) == 0 &&
1282 (stat_buf.st_mode & S_IXANY))) {
1284 data = (dir_data *) wmalloc(sizeof(dir_data));
1285 data->name = wstrdup(dentry->d_name);
1286 data->index = i;
1288 WMAddToArray(files, data);
1292 free(buffer);
1295 closedir(dir);
1296 i++;
1299 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1300 WMFreeArray(dirs);
1301 WMFreeArray(files);
1302 return NULL;
1305 WMSortArray(dirs, myCompare);
1306 WMSortArray(files, myCompare);
1308 menu = wMenuCreate(scr, M_(title), False);
1309 menu->on_destroy = removeShortcutsForMenu;
1311 WM_ITERATE_ARRAY(dirs, data, iter) {
1312 /* New directory. Use same OPEN_MENU command that was used
1313 * for the current directory. */
1314 length = strlen(path[data->index]) + strlen(data->name) + 6;
1315 if (stripExtension)
1316 length += 7;
1317 if (command)
1318 length += strlen(command) + 6;
1319 buffer = malloc(length);
1320 if (!buffer) {
1321 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1322 break;
1325 buffer[0] = '\0';
1326 if (stripExtension)
1327 strcat(buffer, "-noext ");
1329 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1331 if (have_space)
1332 strcat(buffer, "\"");
1333 strcat(buffer, path[data->index]);
1335 strcat(buffer, "/");
1336 strcat(buffer, data->name);
1337 if (have_space)
1338 strcat(buffer, "\"");
1339 if (command) {
1340 strcat(buffer, " WITH ");
1341 strcat(buffer, command);
1344 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1346 wfree(buffer);
1347 if (data->name)
1348 wfree(data->name);
1349 wfree(data);
1352 WM_ITERATE_ARRAY(files, data, iter) {
1353 /* executable: add as entry */
1354 length = strlen(path[data->index]) + strlen(data->name) + 6;
1355 if (command)
1356 length += strlen(command);
1358 buffer = malloc(length);
1359 if (!buffer) {
1360 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1361 break;
1364 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1365 if (command != NULL) {
1366 strcpy(buffer, command);
1367 strcat(buffer, " ");
1368 if (have_space)
1369 strcat(buffer, "\"");
1370 strcat(buffer, path[data->index]);
1371 } else {
1372 if (have_space) {
1373 buffer[0] = '"';
1374 buffer[1] = 0;
1375 strcat(buffer, path[data->index]);
1376 } else {
1377 strcpy(buffer, path[data->index]);
1380 strcat(buffer, "/");
1381 strcat(buffer, data->name);
1382 if (have_space)
1383 strcat(buffer, "\"");
1385 if (stripExtension) {
1386 char *ptr = strrchr(data->name, '.');
1387 if (ptr && ptr != data->name)
1388 *ptr = 0;
1390 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1392 wfree(buffer);
1393 if (data->name)
1394 wfree(data->name);
1395 wfree(data);
1398 WMFreeArray(files);
1399 WMFreeArray(dirs);
1401 return menu;
1404 /************ Menu Configuration From WMRootMenu *************/
1406 static WMenu *makeDefaultMenu(WScreen * scr)
1408 WMenu *menu = NULL;
1410 menu = wMenuCreate(scr, _("Commands"), True);
1411 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1412 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1413 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1414 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1415 return menu;
1419 *----------------------------------------------------------------------
1420 * configureMenu--
1421 * Reads root menu configuration from defaults database.
1423 *----------------------------------------------------------------------
1425 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1427 WMenu *menu = NULL;
1428 WMPropList *elem;
1429 int i, count;
1430 WMPropList *title, *command, *params;
1431 char *tmp, *mtitle;
1433 if (WMIsPLString(definition)) {
1434 struct stat stat_buf;
1435 char *path = NULL;
1436 Bool menu_is_default = False;
1438 /* menu definition is a string. Probably a path, so parse the file */
1440 tmp = wexpandpath(WMGetFromPLString(definition));
1442 path = getLocalizedMenuFile(tmp);
1444 if (!path)
1445 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1447 if (!path) {
1448 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1449 menu_is_default = True;
1452 if (!path) {
1453 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1454 wfree(tmp);
1455 return NULL;
1458 if (stat(path, &stat_buf) < 0) {
1459 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1460 wfree(path);
1461 wfree(tmp);
1462 return NULL;
1465 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1466 /* if the pointer in WMRootMenu has changed */
1467 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1469 if (menu_is_default) {
1470 wwarning(_
1471 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1472 path);
1475 menu = readMenuFile(scr, path);
1476 if (menu)
1477 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1478 } else {
1479 menu = NULL;
1481 wfree(path);
1482 wfree(tmp);
1484 return menu;
1487 count = WMGetPropListItemCount(definition);
1488 if (count == 0)
1489 return NULL;
1491 elem = WMGetFromPLArray(definition, 0);
1492 if (!WMIsPLString(elem)) {
1493 tmp = WMGetPropListDescription(elem, False);
1494 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1495 wfree(tmp);
1496 return NULL;
1498 mtitle = WMGetFromPLString(elem);
1500 menu = wMenuCreate(scr, M_(mtitle), False);
1501 menu->on_destroy = removeShortcutsForMenu;
1503 #ifdef GLOBAL_SUBMENU_FILE
1504 if (includeGlobals) {
1505 WMenu *submenu;
1506 WMenuEntry *mentry;
1508 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1510 if (submenu) {
1511 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1512 wMenuEntrySetCascade(menu, mentry, submenu);
1515 #endif
1517 for (i = 1; i < count; i++) {
1518 elem = WMGetFromPLArray(definition, i);
1519 #if 0
1520 if (WMIsPLString(elem)) {
1521 char *file;
1523 file = WMGetFromPLString(elem);
1526 #endif
1527 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1528 goto error;
1530 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1531 WMenu *submenu;
1532 WMenuEntry *mentry;
1534 /* submenu */
1535 submenu = configureMenu(scr, elem, True);
1536 if (submenu) {
1537 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1538 wMenuEntrySetCascade(menu, mentry, submenu);
1540 } else {
1541 int idx = 0;
1542 WMPropList *shortcut;
1543 /* normal entry */
1545 title = WMGetFromPLArray(elem, idx++);
1546 shortcut = WMGetFromPLArray(elem, idx++);
1547 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1548 shortcut = WMGetFromPLArray(elem, idx++);
1549 command = WMGetFromPLArray(elem, idx++);
1550 } else {
1551 command = shortcut;
1552 shortcut = NULL;
1554 params = WMGetFromPLArray(elem, idx++);
1556 if (!title || !command)
1557 goto error;
1559 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1560 shortcut ? WMGetFromPLString(shortcut) : NULL,
1561 WMGetFromPLString(command),
1562 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1564 continue;
1566 error:
1567 tmp = WMGetPropListDescription(elem, False);
1568 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1569 wfree(tmp);
1572 return menu;
1576 *----------------------------------------------------------------------
1577 * OpenRootMenu--
1578 * Opens the root menu, parsing the menu configuration from the
1579 * defaults database.
1580 * If the menu is already mapped and is not sticked to the
1581 * root window, it will be unmapped.
1583 * Side effects:
1584 * The menu may be remade.
1586 * Notes:
1587 * Construction of OPEN_MENU entries are delayed to the moment the
1588 * user map's them.
1589 *----------------------------------------------------------------------
1591 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1593 WMenu *menu = NULL;
1594 WMPropList *definition;
1596 static WMPropList *domain=NULL;
1598 if (!domain) {
1599 domain = WMCreatePLString("WMRootMenu");
1603 scr->flags.root_menu_changed_shortcuts = 0;
1604 scr->flags.added_workspace_menu = 0;
1605 scr->flags.added_windows_menu = 0;
1607 if (scr->root_menu && scr->root_menu->flags.mapped) {
1608 menu = scr->root_menu;
1609 if (!menu->flags.buttoned) {
1610 wMenuUnmap(menu);
1611 } else {
1612 wRaiseFrame(menu->frame->core);
1614 if (keyboard)
1615 wMenuMapAt(menu, 0, 0, True);
1616 else
1617 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1619 return;
1622 definition = WDRootMenu->dictionary;
1625 definition = PLGetDomain(domain);
1627 if (definition) {
1628 if (WMIsPLArray(definition)) {
1629 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1630 menu = configureMenu(scr, definition, True);
1631 if (menu)
1632 menu->timestamp = WDRootMenu->timestamp;
1634 } else
1635 menu = NULL;
1636 } else {
1637 menu = configureMenu(scr, definition, True);
1641 if (!menu) {
1642 /* menu hasn't changed or could not be read */
1643 if (!scr->root_menu) {
1644 wMessageDialog(scr, _("Error"),
1645 _("The applications menu could not be loaded. "
1646 "Look at the console output for a detailed "
1647 "description of the errors."), _("OK"), NULL, NULL);
1649 menu = makeDefaultMenu(scr);
1650 scr->root_menu = menu;
1652 menu = scr->root_menu;
1653 } else {
1654 /* new root menu */
1655 if (scr->root_menu) {
1656 wMenuDestroy(scr->root_menu, True);
1658 scr->root_menu = menu;
1660 if (menu) {
1661 int newx, newy;
1663 if (keyboard && x == 0 && y == 0) {
1664 newx = newy = 0;
1665 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1666 newx = x - menu->frame->core->width / 2;
1667 newy = y - menu->frame->core->height / 2;
1668 } else {
1669 newx = x - menu->frame->core->width / 2;
1670 newy = y;
1672 wMenuMapAt(menu, newx, newy, keyboard);
1675 if (scr->flags.root_menu_changed_shortcuts)
1676 rebindKeygrabs(scr);