wrlib: Fix typo in macro containing ImageMagick version
[wmaker-crm.git] / src / rootmenu.c
blobfc8774e0e6e6272ca247ca5f56077e5175fa5461
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
7 * Copyright (c) 2014 Window Maker Team
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "wconfig.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 #include <sys/types.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <ctype.h>
35 #include <time.h>
36 #include <dirent.h>
37 #include <errno.h>
39 #include <X11/Xlib.h>
40 #include <X11/Xutil.h>
41 #include <X11/Xatom.h>
43 #include "WindowMaker.h"
44 #include "actions.h"
45 #include "menu.h"
46 #include "misc.h"
47 #include "main.h"
48 #include "dialog.h"
49 #include "keybind.h"
50 #include "stacking.h"
51 #include "workspace.h"
52 #include "defaults.h"
53 #include "framewin.h"
54 #include "session.h"
55 #include "shutdown.h"
56 #include "xmodifier.h"
57 #include "rootmenu.h"
58 #include "startup.h"
59 #include "switchmenu.h"
61 #include <WINGs/WUtil.h>
63 #define MAX_SHORTCUT_LENGTH 32
65 static WMenu *readMenuPipe(WScreen * scr, char **file_name);
66 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name);
67 static WMenu *readMenuFile(WScreen *scr, const char *file_name);
68 static WMenu *readMenuDirectory(WScreen *scr, const char *title, char **file_name, const char *command);
69 static WMenu *configureMenu(WScreen *scr, WMPropList *definition);
70 static void menu_parser_register_macros(WMenuParser parser);
72 typedef struct Shortcut {
73 struct Shortcut *next;
75 int modifier;
76 KeyCode keycode;
77 WMenuEntry *entry;
78 WMenu *menu;
79 } Shortcut;
81 static Shortcut *shortcutList = NULL;
84 * Syntax:
85 * # main menu
86 * "Menu Name" MENU
87 * "Title" EXEC command_to_exec -params
88 * "Submenu" MENU
89 * "Title" EXEC command_to_exec -params
90 * "Submenu" END
91 * "Workspaces" WORKSPACE_MENU
92 * "Title" built_in_command
93 * "Quit" EXIT
94 * "Quick Quit" EXIT QUICK
95 * "Menu Name" END
97 * Commands may be preceded by SHORTCUT key
99 * Built-in commands:
101 * INFO_PANEL - shows the Info Panel
102 * LEGAL_PANEL - shows the Legal info panel
103 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
104 * REFRESH - forces the desktop to be repainted
105 * EXIT [QUICK] - exit the window manager [without confirmation]
106 * EXEC <program> - execute an external program
107 * SHEXEC <command> - execute a shell command
108 * WORKSPACE_MENU - places the workspace submenu
109 * ARRANGE_ICONS
110 * RESTART [<window manager>] - restarts the window manager
111 * SHOW_ALL - unhide all windows on workspace
112 * HIDE_OTHERS - hides all windows excep the focused one
113 * OPEN_MENU file - read menu data from file which must be a valid menu file.
114 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
115 * - read menu data from directory(ies) and
116 * eventually precede each with a command.
117 * OPEN_MENU | command
118 * - opens command and uses its stdout to construct and insert
119 * the resulting menu in current position. The output of
120 * command must be a valid menu description.
121 * The space between '|' and command is optional.
122 * || will do the same, but will not cache the contents.
123 * OPEN_PLMENU | command
124 * - opens command and uses its stdout which must be in proplist
125 * fromat to construct and insert the resulting menu in current
126 * position.
127 * The space between '|' and command is optional.
128 * || will do the same, but will not cache the contents.
129 * SAVE_SESSION - saves the current state of the desktop, which include
130 * all running applications, all their hints (geometry,
131 * position on screen, workspace they live on, the dock
132 * or clip from where they were launched, and
133 * if minimized, shaded or hidden. Also saves the current
134 * workspace the user is on. All will be restored on every
135 * start of windowmaker until another SAVE_SESSION or
136 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
137 * WindowMaker domain file, then saving is automatically
138 * done on every windowmaker exit, overwriting any
139 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
140 * dock state now.
141 * CLEAR_SESSION - clears any previous saved session. This will not have
142 * any effect if SaveSessionOnExit is True.
146 #define M_QUICK 1
148 /* menu commands */
150 static void execCommand(WMenu * menu, WMenuEntry * entry)
152 char *cmdline;
154 cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
156 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
157 GrabModeAsync, GrabModeAsync, None, wPreferences.cursor[WCUR_WAIT], CurrentTime);
158 XSync(dpy, 0);
160 if (cmdline) {
161 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
162 wfree(cmdline);
164 XUngrabPointer(dpy, CurrentTime);
165 XSync(dpy, 0);
168 static void exitCommand(WMenu * menu, WMenuEntry * entry)
170 static int inside = 0;
171 int result;
173 /* prevent reentrant calls */
174 if (inside)
175 return;
176 inside = 1;
178 #define R_CANCEL 0
179 #define R_EXIT 1
181 result = R_CANCEL;
183 if ((long)entry->clientdata == M_QUICK) {
184 result = R_EXIT;
185 } else {
186 int r, oldSaveSessionFlag;
188 oldSaveSessionFlag = wPreferences.save_session_on_exit;
189 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
190 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
192 if (r == WAPRDefault) {
193 result = R_EXIT;
194 } else if (r == WAPRAlternate) {
195 /* Don't modify the "save session on exit" flag if the
196 * user canceled the operation. */
197 wPreferences.save_session_on_exit = oldSaveSessionFlag;
200 if (result == R_EXIT)
201 Shutdown(WSExitMode);
203 #undef R_EXIT
204 #undef R_CANCEL
205 inside = 0;
208 static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
210 static int inside = 0;
211 int result;
213 /* prevent reentrant calls */
214 if (inside)
215 return;
216 inside = 1;
218 #define R_CANCEL 0
219 #define R_CLOSE 1
220 #define R_KILL 2
222 result = R_CANCEL;
223 if ((long)entry->clientdata == M_QUICK)
224 result = R_CLOSE;
225 else {
226 int r, oldSaveSessionFlag;
228 oldSaveSessionFlag = wPreferences.save_session_on_exit;
230 r = wExitDialog(menu->frame->screen_ptr,
231 _("Kill X session"),
232 _("Kill Window System session?\n"
233 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
234 if (r == WAPRDefault) {
235 result = R_KILL;
236 } else if (r == WAPRAlternate) {
237 /* Don't modify the "save session on exit" flag if the
238 * user canceled the operation. */
239 wPreferences.save_session_on_exit = oldSaveSessionFlag;
243 if (result != R_CANCEL) {
244 Shutdown(WSKillMode);
246 #undef R_CLOSE
247 #undef R_CANCEL
248 #undef R_KILL
249 inside = 0;
252 static void restartCommand(WMenu * menu, WMenuEntry * entry)
254 /* Parameter not used, but tell the compiler that it is ok */
255 (void) menu;
256 (void) entry;
258 Shutdown(WSRestartPreparationMode);
259 Restart((char *)entry->clientdata, False);
260 Restart(NULL, True);
263 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
265 /* Parameter not used, but tell the compiler that it is ok */
266 (void) entry;
268 wRefreshDesktop(menu->frame->screen_ptr);
271 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
273 /* Parameter not used, but tell the compiler that it is ok */
274 (void) entry;
276 wArrangeIcons(menu->frame->screen_ptr, True);
279 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
281 /* Parameter not used, but tell the compiler that it is ok */
282 (void) entry;
284 wShowAllWindows(menu->frame->screen_ptr);
287 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
289 /* Parameter not used, but tell the compiler that it is ok */
290 (void) entry;
292 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
295 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
297 /* Parameter not used, but tell the compiler that it is ok */
298 (void) entry;
300 if (!wPreferences.save_session_on_exit)
301 wSessionSaveState(menu->frame->screen_ptr);
303 wScreenSaveState(menu->frame->screen_ptr);
306 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
308 /* Parameter not used, but tell the compiler that it is ok */
309 (void) entry;
311 wSessionClearState(menu->frame->screen_ptr);
312 wScreenSaveState(menu->frame->screen_ptr);
315 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
317 /* Parameter not used, but tell the compiler that it is ok */
318 (void) entry;
320 wShowInfoPanel(menu->frame->screen_ptr);
323 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
325 /* Parameter not used, but tell the compiler that it is ok */
326 (void) entry;
328 wShowLegalPanel(menu->frame->screen_ptr);
331 /********************************************************************/
333 static char *getLocalizedMenuFile(const char *menu)
335 char *buffer, *ptr, *locale;
336 int len;
338 if (!w_global.locale)
339 return NULL;
341 len = strlen(menu) + strlen(w_global.locale) + 8;
342 buffer = wmalloc(len);
344 /* try menu.locale_name */
345 snprintf(buffer, len, "%s.%s", menu, w_global.locale);
346 if (access(buffer, F_OK) == 0)
347 return buffer;
349 /* position of locale in our buffer */
350 locale = buffer + strlen(menu) + 1;
352 /* check if it is in the form aa_bb.encoding and check for aa_bb */
353 ptr = strchr(locale, '.');
354 if (ptr) {
355 *ptr = 0;
356 if (access(buffer, F_OK) == 0)
357 return buffer;
360 /* now check for aa */
361 ptr = strchr(locale, '_');
362 if (ptr) {
363 *ptr = 0;
364 if (access(buffer, F_OK) == 0)
365 return buffer;
368 wfree(buffer);
370 return NULL;
373 Bool wRootMenuPerformShortcut(XEvent * event)
375 WScreen *scr = wScreenForRootWindow(event->xkey.root);
376 Shortcut *ptr;
377 int modifiers;
378 int done = 0;
380 /* ignore CapsLock */
381 modifiers = event->xkey.state & w_global.shortcut.modifiers_mask;
383 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
384 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
385 continue;
387 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
388 (*ptr->entry->callback) (ptr->menu, ptr->entry);
389 done = True;
393 return done;
396 void wRootMenuBindShortcuts(Window window)
398 Shortcut *ptr;
400 ptr = shortcutList;
401 while (ptr) {
402 if (ptr->modifier != AnyModifier) {
403 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
404 window, True, GrabModeAsync, GrabModeAsync);
405 #ifdef NUMLOCK_HACK
406 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
407 #endif
409 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
410 ptr = ptr->next;
414 static void rebindKeygrabs(WScreen * scr)
416 WWindow *wwin;
418 wwin = scr->focused_window;
420 while (wwin != NULL) {
421 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
423 if (!WFLAGP(wwin, no_bind_keys)) {
424 wWindowSetKeyGrabs(wwin);
426 wwin = wwin->prev;
430 static void removeShortcutsForMenu(WMenu * menu)
432 Shortcut *ptr, *tmp;
433 Shortcut *newList = NULL;
435 ptr = shortcutList;
436 while (ptr != NULL) {
437 tmp = ptr->next;
438 if (ptr->menu == menu) {
439 wfree(ptr);
440 } else {
441 ptr->next = newList;
442 newList = ptr;
444 ptr = tmp;
446 shortcutList = newList;
447 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
450 static Bool addShortcut(const char *file, const char *shortcutDefinition, WMenu *menu, WMenuEntry *entry)
452 Shortcut *ptr;
453 KeySym ksym;
454 char *k;
455 char buf[MAX_SHORTCUT_LENGTH], *b;
457 ptr = wmalloc(sizeof(Shortcut));
459 wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
460 b = (char *)buf;
462 /* get modifiers */
463 ptr->modifier = 0;
464 while ((k = strchr(b, '+')) != NULL) {
465 int mod;
467 *k = 0;
468 mod = wXModifierFromKey(b);
469 if (mod < 0) {
470 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
471 wfree(ptr);
472 return False;
474 ptr->modifier |= mod;
476 b = k + 1;
479 /* get key */
480 ksym = XStringToKeysym(b);
482 if (ksym == NoSymbol) {
483 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
484 file, shortcutDefinition, entry->text);
485 wfree(ptr);
486 return False;
489 ptr->keycode = XKeysymToKeycode(dpy, ksym);
490 if (ptr->keycode == 0) {
491 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
492 shortcutDefinition, entry->text);
493 wfree(ptr);
494 return False;
497 ptr->menu = menu;
498 ptr->entry = entry;
500 ptr->next = shortcutList;
501 shortcutList = ptr;
503 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
505 return True;
508 static char *next_token(char *line, char **next)
510 char *tmp, c;
511 char *ret;
513 *next = NULL;
514 while (*line == ' ' || *line == '\t')
515 line++;
517 tmp = line;
519 if (*tmp == '"') {
520 tmp++;
521 line++;
522 while (*tmp != 0 && *tmp != '"')
523 tmp++;
524 if (*tmp != '"') {
525 wwarning(_("%s: unmatched '\"' in menu file"), line);
526 return NULL;
528 } else {
529 do {
530 if (*tmp == '\\')
531 tmp++;
533 if (*tmp != 0)
534 tmp++;
536 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
539 c = *tmp;
540 *tmp = 0;
541 ret = wstrdup(line);
542 *tmp = c;
544 if (c == 0)
545 return ret;
546 else
547 tmp++;
549 /* skip blanks */
550 while (*tmp == ' ' || *tmp == '\t')
551 tmp++;
553 if (*tmp != 0)
554 *next = tmp;
556 return ret;
559 static void separateCommand(char *line, char ***file, char **command)
561 char *token, *tmp = line;
562 WMArray *array = WMCreateArray(4);
563 int count, i;
565 *file = NULL;
566 *command = NULL;
567 do {
568 token = next_token(tmp, &tmp);
569 if (token) {
570 if (strcmp(token, "WITH") == 0) {
571 if (tmp != NULL && *tmp != 0)
572 *command = wstrdup(tmp);
573 else
574 wwarning(_("%s: missing command"), line);
575 wfree(token);
576 break;
578 WMAddToArray(array, token);
580 } while (token != NULL && tmp != NULL);
582 count = WMGetArrayItemCount(array);
583 if (count > 0) {
584 *file = wmalloc(sizeof(char *) * (count + 1));
585 (*file)[count] = NULL;
586 for (i = 0; i < count; i++) {
587 (*file)[i] = WMGetFromArray(array, i);
590 WMFreeArray(array);
593 static WMenu *constructPLMenu(WScreen *screen, const char *path)
595 WMPropList *pl = NULL;
596 WMenu *menu = NULL;
598 if (!path)
599 return NULL;
601 pl = WMReadPropListFromFile(path);
602 if (!pl)
603 return NULL;
605 menu = configureMenu(screen, pl);
607 WMReleasePropList(pl);
609 if (!menu)
610 return NULL;
612 menu->on_destroy = removeShortcutsForMenu;
613 return menu;
618 static void constructMenu(WMenu * menu, WMenuEntry * entry)
620 WMenu *submenu;
621 struct stat stat_buf;
622 char **path;
623 char *cmd;
624 char *lpath = NULL;
625 int i, first = -1;
626 time_t last = 0;
628 separateCommand((char *)entry->clientdata, &path, &cmd);
629 if (path == NULL || *path == NULL || **path == 0) {
630 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
631 if (path) {
632 for (i = 0; path[i] != NULL; i++)
633 wfree(path[i]);
634 wfree(path);
636 if (cmd)
637 wfree(cmd);
638 return;
641 if (path[0][0] == '|') {
642 /* pipe menu */
644 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
645 /* parse pipe */
647 submenu = readMenuPipe(menu->frame->screen_ptr, path);
649 if (submenu != NULL) {
650 if (path[0][1] == '|')
651 submenu->timestamp = 0;
652 else
653 submenu->timestamp = 1; /* there's no automatic reloading */
655 } else {
656 submenu = NULL;
659 } else {
661 /* try interpreting path as a proplist file */
662 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
663 /* if unsuccessful, try it as an old-style file */
664 if (!submenu) {
666 i = 0;
667 while (path[i] != NULL) {
668 char *tmp;
670 if (strcmp(path[i], "-noext") == 0) {
671 i++;
672 continue;
675 tmp = wexpandpath(path[i]);
677 if (strstr(tmp, "#usergnusteppath#") == tmp)
678 tmp = wstrconcat(wusergnusteppath(),
679 tmp + 17);
681 wfree(path[i]);
682 lpath = getLocalizedMenuFile(tmp);
683 if (lpath) {
684 wfree(tmp);
685 path[i] = lpath;
686 lpath = NULL;
687 } else {
688 path[i] = tmp;
691 if (stat(path[i], &stat_buf) == 0) {
692 if (last < stat_buf.st_mtime)
693 last = stat_buf.st_mtime;
694 if (first < 0)
695 first = i;
696 } else {
697 werror(_("%s:could not stat menu"), path[i]);
698 /*goto finish; */
701 i++;
704 if (first < 0) {
705 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
706 goto finish;
708 stat(path[first], &stat_buf);
709 if (!menu->cascades[entry->cascade]
710 || menu->cascades[entry->cascade]->timestamp < last) {
712 if (S_ISDIR(stat_buf.st_mode)) {
713 /* menu directory */
714 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
715 if (submenu)
716 submenu->timestamp = last;
717 } else if (S_ISREG(stat_buf.st_mode)) {
718 /* menu file */
720 if (cmd || path[1])
721 wwarning(_("too many parameters in OPEN_MENU: %s"),
722 (char *)entry->clientdata);
724 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
725 if (submenu)
726 submenu->timestamp = stat_buf.st_mtime;
727 } else {
728 submenu = NULL;
730 } else {
731 submenu = NULL;
736 if (submenu) {
737 wMenuEntryRemoveCascade(menu, entry);
738 wMenuEntrySetCascade(menu, entry, submenu);
741 finish:
742 i = 0;
743 while (path[i] != NULL)
744 wfree(path[i++]);
745 wfree(path);
746 if (cmd)
747 wfree(cmd);
750 static void constructPLMenuFromPipe(WMenu * menu, WMenuEntry * entry)
752 WMenu *submenu = NULL;
753 char **path;
754 char *cmd;
755 int i;
757 separateCommand((char *)entry->clientdata, &path, &cmd);
758 if (path == NULL || *path == NULL || **path == 0) {
759 wwarning(_("invalid OPEN_PLMENU specification: %s"),
760 (char *)entry->clientdata);
761 if (path) {
762 for (i = 0; path[i] != NULL; i++)
763 wfree(path[i]);
764 wfree(path);
766 if (cmd)
767 wfree(cmd);
768 return;
771 if (path[0][0] == '|') {
772 /* pipe menu */
774 if (!menu->cascades[entry->cascade]
775 || menu->cascades[entry->cascade]->timestamp == 0) {
776 /* parse pipe */
777 submenu = readPLMenuPipe(menu->frame->screen_ptr, path);
779 if (submenu != NULL) {
780 if (path[0][1] == '|')
781 submenu->timestamp = 0;
782 else
783 submenu->timestamp = 1; /* there's no automatic reloading */
788 if (submenu) {
789 wMenuEntryRemoveCascade(menu, entry);
790 wMenuEntrySetCascade(menu, entry, submenu);
793 i = 0;
794 while (path[i] != NULL)
795 wfree(path[i++]);
797 wfree(path);
798 if (cmd)
799 wfree(cmd);
802 static void cleanupWorkspaceMenu(WMenu *menu)
804 if (menu->frame->screen_ptr->workspace_menu == menu)
805 menu->frame->screen_ptr->workspace_menu = NULL;
808 static WMenuEntry *addWorkspaceMenu(WScreen *scr, WMenu *menu, const char *title)
810 WMenu *wsmenu;
811 WMenuEntry *entry;
813 if (scr->flags.added_workspace_menu) {
814 wwarning(_
815 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
816 return NULL;
817 } else {
818 scr->flags.added_workspace_menu = 1;
820 wsmenu = wWorkspaceMenuMake(scr, True);
821 wsmenu->on_destroy = cleanupWorkspaceMenu;
823 scr->workspace_menu = wsmenu;
824 entry = wMenuAddCallback(menu, title, NULL, NULL);
825 wMenuEntrySetCascade(menu, entry, wsmenu);
827 wWorkspaceMenuUpdate(scr, wsmenu);
829 return entry;
832 static void cleanupWindowsMenu(WMenu * menu)
834 if (menu->frame->screen_ptr->switch_menu == menu)
835 menu->frame->screen_ptr->switch_menu = NULL;
838 static WMenuEntry *addWindowsMenu(WScreen *scr, WMenu *menu, const char *title)
840 WMenu *wwmenu;
841 WWindow *wwin;
842 WMenuEntry *entry;
844 if (scr->flags.added_windows_menu) {
845 wwarning(_
846 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
847 return NULL;
848 } else {
849 scr->flags.added_windows_menu = 1;
851 wwmenu = wMenuCreate(scr, _("Window List"), False);
852 wwmenu->on_destroy = cleanupWindowsMenu;
853 scr->switch_menu = wwmenu;
854 wwin = scr->focused_window;
855 while (wwin) {
856 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
858 wwin = wwin->prev;
860 entry = wMenuAddCallback(menu, title, NULL, NULL);
861 wMenuEntrySetCascade(menu, entry, wwmenu);
863 return entry;
866 static WMenuEntry *addMenuEntry(WMenu *menu, const char *title, const char *shortcut, const char *command,
867 const char *params, const char *file_name)
869 WScreen *scr;
870 WMenuEntry *entry = NULL;
871 Bool shortcutOk = False;
873 if (!menu)
874 return NULL;
875 scr = menu->frame->screen_ptr;
876 if (strcmp(command, "OPEN_MENU") == 0) {
877 if (!params) {
878 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
879 } else {
880 WMenu *dummy;
881 char *path;
883 path = wfindfile(DEF_CONFIG_PATHS, params);
884 if (!path) {
885 path = wstrdup(params);
887 dummy = wMenuCreate(scr, title, False);
888 dummy->on_destroy = removeShortcutsForMenu;
889 entry = wMenuAddCallback(menu, title, constructMenu, path);
890 entry->free_cdata = wfree;
891 wMenuEntrySetCascade(menu, entry, dummy);
893 } else if (strcmp(command, "OPEN_PLMENU") == 0) {
894 if (!params) {
895 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
896 } else {
897 WMenu *dummy;
898 char *path;
900 path = wfindfile(DEF_CONFIG_PATHS, params);
901 if (!path)
902 path = wstrdup(params);
904 dummy = wMenuCreate(scr, title, False);
905 dummy->on_destroy = removeShortcutsForMenu;
906 entry = wMenuAddCallback(menu, title, constructPLMenuFromPipe, path);
907 entry->free_cdata = wfree;
908 wMenuEntrySetCascade(menu, entry, dummy);
910 } else if (strcmp(command, "EXEC") == 0) {
911 if (!params)
912 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
913 else {
914 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
915 entry->free_cdata = wfree;
916 shortcutOk = True;
918 } else if (strcmp(command, "SHEXEC") == 0) {
919 if (!params)
920 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
921 else {
922 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
923 entry->free_cdata = wfree;
924 shortcutOk = True;
926 } else if (strcmp(command, "EXIT") == 0) {
928 if (params && strcmp(params, "QUICK") == 0)
929 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
930 else
931 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
933 shortcutOk = True;
934 } else if (strcmp(command, "SHUTDOWN") == 0) {
936 if (params && strcmp(params, "QUICK") == 0)
937 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
938 else
939 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
941 shortcutOk = True;
942 } else if (strcmp(command, "REFRESH") == 0) {
943 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
945 shortcutOk = True;
946 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
947 entry = addWorkspaceMenu(scr, menu, title);
949 shortcutOk = True;
950 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
951 entry = addWindowsMenu(scr, menu, title);
953 shortcutOk = True;
954 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
955 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
957 shortcutOk = True;
958 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
959 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
961 shortcutOk = True;
962 } else if (strcmp(command, "SHOW_ALL") == 0) {
963 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
965 shortcutOk = True;
966 } else if (strcmp(command, "RESTART") == 0) {
967 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
968 entry->free_cdata = wfree;
969 shortcutOk = True;
970 } else if (strcmp(command, "SAVE_SESSION") == 0) {
971 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
973 shortcutOk = True;
974 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
975 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
976 shortcutOk = True;
977 } else if (strcmp(command, "INFO_PANEL") == 0) {
978 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
979 shortcutOk = True;
980 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
981 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
982 shortcutOk = True;
983 } else {
984 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
986 return NULL;
989 if (shortcut && entry) {
990 if (!shortcutOk) {
991 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
992 } else {
993 if (addShortcut(file_name, shortcut, menu, entry)) {
995 entry->rtext = GetShortcutString(shortcut);
997 entry->rtext = wstrdup(shortcut);
1003 return entry;
1006 /******************* Menu Configuration From File *******************/
1008 static void freeline(char *title, char *command, char *parameter, char *shortcut)
1010 wfree(title);
1011 wfree(command);
1012 wfree(parameter);
1013 wfree(shortcut);
1016 static WMenu *parseCascade(WScreen * scr, WMenu * menu, WMenuParser parser)
1018 char *command, *params, *shortcut, *title;
1020 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1022 if (command == NULL || !command[0]) {
1023 WMenuParserError(parser, _("missing command in menu config") );
1024 freeline(title, command, params, shortcut);
1025 goto error;
1028 if (strcasecmp(command, "MENU") == 0) {
1029 WMenu *cascade;
1031 /* start submenu */
1033 cascade = wMenuCreate(scr, M_(title), False);
1034 cascade->on_destroy = removeShortcutsForMenu;
1035 if (!parseCascade(scr, cascade, parser)) {
1036 wMenuDestroy(cascade, True);
1037 } else {
1038 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
1040 } else if (strcasecmp(command, "END") == 0) {
1041 /* end of menu */
1042 freeline(title, command, params, shortcut);
1043 return menu;
1044 } else {
1045 /* normal items */
1046 addMenuEntry(menu, M_(title), shortcut, command, params, WMenuParserGetFilename(parser));
1048 freeline(title, command, params, shortcut);
1051 WMenuParserError(parser, _("syntax error in menu file: END declaration missing") );
1053 error:
1054 return NULL;
1057 static WMenu *readMenu(WScreen *scr, const char *flat_file, FILE *file)
1059 WMenu *menu = NULL;
1060 WMenuParser parser;
1061 char *title, *command, *params, *shortcut;
1063 parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS);
1064 menu_parser_register_macros(parser);
1066 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1068 if (command == NULL || !command[0]) {
1069 WMenuParserError(parser, _("missing command in menu config") );
1070 freeline(title, command, params, shortcut);
1071 break;
1073 if (strcasecmp(command, "MENU") == 0) {
1074 menu = wMenuCreate(scr, M_(title), True);
1075 menu->on_destroy = removeShortcutsForMenu;
1076 if (!parseCascade(scr, menu, parser)) {
1077 wMenuDestroy(menu, True);
1078 menu = NULL;
1080 freeline(title, command, params, shortcut);
1081 break;
1082 } else {
1083 WMenuParserError(parser, _("invalid menu, no menu title given") );
1084 freeline(title, command, params, shortcut);
1085 break;
1088 freeline(title, command, params, shortcut);
1091 WMenuParserDelete(parser);
1092 return menu;
1095 static WMenu *readMenuFile(WScreen *scr, const char *file_name)
1097 WMenu *menu = NULL;
1098 FILE *file = NULL;
1100 file = fopen(file_name, "rb");
1101 if (!file) {
1102 werror(_("could not open menu file \"%s\": %s"), file_name, strerror(errno));
1103 return NULL;
1105 menu = readMenu(scr, file_name, file);
1106 fclose(file);
1108 return menu;
1111 static inline int generate_command_from_list(char *buffer, size_t buffer_size, char **command_elements)
1113 char *rd;
1114 int wr_idx;
1115 int i;
1117 wr_idx = 0;
1118 for (i = 0; command_elements[i] != NULL; i++) {
1120 if (i > 0)
1121 if (wr_idx < buffer_size - 1)
1122 buffer[wr_idx++] = ' ';
1124 for (rd = command_elements[i]; *rd != '\0'; rd++) {
1125 if (wr_idx < buffer_size - 1)
1126 buffer[wr_idx++] = *rd;
1127 else
1128 return 1;
1131 buffer[wr_idx] = '\0';
1132 return 0;
1135 /************ Menu Configuration From Pipe *************/
1136 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name)
1138 WMPropList *plist = NULL;
1139 WMenu *menu = NULL;
1140 char *filename;
1141 char flat_file[MAXLINE];
1143 if (generate_command_from_list(flat_file, sizeof(flat_file), file_name)) {
1144 werror(_("could not open menu file \"%s\": %s"),
1145 file_name[0], _("pipe command for PropertyList is too long"));
1146 return NULL;
1148 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1150 plist = WMReadPropListFromPipe(filename);
1152 if (!plist)
1153 return NULL;
1155 menu = configureMenu(scr, plist);
1157 WMReleasePropList(plist);
1159 if (!menu)
1160 return NULL;
1162 menu->on_destroy = removeShortcutsForMenu;
1163 return menu;
1166 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1168 WMenu *menu = NULL;
1169 FILE *file = NULL;
1170 char *filename;
1171 char flat_file[MAXLINE];
1173 if (generate_command_from_list(flat_file, sizeof(flat_file), file_name)) {
1174 werror(_("could not open menu file \"%s\": %s"),
1175 file_name[0], _("pipe command is too long"));
1176 return NULL;
1178 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1181 * In case of memory problem, 'popen' will not set the errno, so we initialise it
1182 * to be able to display a meaningful message. For other problems, 'popen' will
1183 * properly set errno, so we'll still get a good message
1185 errno = ENOMEM;
1186 file = popen(filename, "r");
1187 if (!file) {
1188 werror(_("could not open menu file \"%s\": %s"), filename, strerror(errno));
1189 return NULL;
1191 menu = readMenu(scr, flat_file, file);
1192 pclose(file);
1194 return menu;
1197 typedef struct {
1198 char *name;
1199 int index;
1200 } dir_data;
1202 static int myCompare(const void *d1, const void *d2)
1204 dir_data *p1 = *(dir_data **) d1;
1205 dir_data *p2 = *(dir_data **) d2;
1207 return strcmp(p1->name, p2->name);
1210 /***** Preset some macro for file parser *****/
1211 static void menu_parser_register_macros(WMenuParser parser)
1213 Visual *visual;
1214 char buf[32];
1216 // Used to return CPP verion, now returns wmaker's version
1217 WMenuParserRegisterSimpleMacro(parser, "__VERSION__", VERSION);
1219 // All macros below were historically defined by WindowMaker
1220 visual = DefaultVisual(dpy, DefaultScreen(dpy));
1221 snprintf(buf, sizeof(buf), "%d", visual->class);
1222 WMenuParserRegisterSimpleMacro(parser, "VISUAL", buf);
1224 snprintf(buf, sizeof(buf), "%d", DefaultDepth(dpy, DefaultScreen(dpy)) );
1225 WMenuParserRegisterSimpleMacro(parser, "DEPTH", buf);
1227 snprintf(buf, sizeof(buf), "%d", WidthOfScreen(DefaultScreenOfDisplay(dpy)) );
1228 WMenuParserRegisterSimpleMacro(parser, "SCR_WIDTH", buf);
1230 snprintf(buf, sizeof(buf), "%d", HeightOfScreen(DefaultScreenOfDisplay(dpy)) );
1231 WMenuParserRegisterSimpleMacro(parser, "SCR_HEIGHT", buf);
1233 WMenuParserRegisterSimpleMacro(parser, "DISPLAY", XDisplayName(DisplayString(dpy)) );
1235 WMenuParserRegisterSimpleMacro(parser, "WM_VERSION", "\"" VERSION "\"");
1238 /************ Menu Configuration From Directory *************/
1240 static Bool isFilePackage(const char *file)
1242 int l;
1244 /* check if the extension indicates this file is a
1245 * file package. For now, only recognize .themed */
1247 l = strlen(file);
1249 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1250 return True;
1251 } else {
1252 return False;
1256 static WMenu *readMenuDirectory(WScreen *scr, const char *title, char **path, const char *command)
1258 DIR *dir;
1259 struct dirent *dentry;
1260 struct stat stat_buf;
1261 WMenu *menu = NULL;
1262 char *buffer;
1263 WMArray *dirs = NULL, *files = NULL;
1264 WMArrayIterator iter;
1265 int length, i, have_space = 0;
1266 dir_data *data;
1267 int stripExtension = 0;
1269 dirs = WMCreateArray(16);
1270 files = WMCreateArray(16);
1272 i = 0;
1273 while (path[i] != NULL) {
1274 if (strcmp(path[i], "-noext") == 0) {
1275 stripExtension = 1;
1276 i++;
1277 continue;
1280 dir = opendir(path[i]);
1281 if (!dir) {
1282 i++;
1283 continue;
1286 while ((dentry = readdir(dir))) {
1288 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1289 continue;
1291 if (dentry->d_name[0] == '.')
1292 continue;
1294 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1295 if (!buffer) {
1296 werror(_("out of memory while constructing directory menu %s"), path[i]);
1297 break;
1300 strcpy(buffer, path[i]);
1301 strcat(buffer, "/");
1302 strcat(buffer, dentry->d_name);
1304 if (stat(buffer, &stat_buf) != 0) {
1305 werror(_("%s:could not stat file \"%s\" in menu directory"),
1306 path[i], dentry->d_name);
1307 } else {
1308 Bool isFilePack = False;
1310 data = NULL;
1311 if (S_ISDIR(stat_buf.st_mode)
1312 && !(isFilePack = isFilePackage(dentry->d_name))) {
1314 /* access always returns success for user root */
1315 if (access(buffer, X_OK) == 0) {
1316 /* Directory is accesible. Add to directory list */
1318 data = (dir_data *) wmalloc(sizeof(dir_data));
1319 data->name = wstrdup(dentry->d_name);
1320 data->index = i;
1322 WMAddToArray(dirs, data);
1324 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1325 /* Hack because access always returns X_OK success for user root */
1326 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1327 if ((command != NULL && access(buffer, R_OK) == 0) ||
1328 (command == NULL && access(buffer, X_OK) == 0 &&
1329 (stat_buf.st_mode & S_IXANY))) {
1331 data = (dir_data *) wmalloc(sizeof(dir_data));
1332 data->name = wstrdup(dentry->d_name);
1333 data->index = i;
1335 WMAddToArray(files, data);
1339 free(buffer);
1342 closedir(dir);
1343 i++;
1346 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1347 WMFreeArray(dirs);
1348 WMFreeArray(files);
1349 return NULL;
1352 WMSortArray(dirs, myCompare);
1353 WMSortArray(files, myCompare);
1355 menu = wMenuCreate(scr, M_(title), False);
1356 menu->on_destroy = removeShortcutsForMenu;
1358 WM_ITERATE_ARRAY(dirs, data, iter) {
1359 /* New directory. Use same OPEN_MENU command that was used
1360 * for the current directory. */
1361 length = strlen(path[data->index]) + strlen(data->name) + 6;
1362 if (stripExtension)
1363 length += 7;
1364 if (command)
1365 length += strlen(command) + 6;
1366 buffer = malloc(length);
1367 if (!buffer) {
1368 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1369 break;
1372 buffer[0] = '\0';
1373 if (stripExtension)
1374 strcat(buffer, "-noext ");
1376 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1378 if (have_space)
1379 strcat(buffer, "\"");
1380 strcat(buffer, path[data->index]);
1382 strcat(buffer, "/");
1383 strcat(buffer, data->name);
1384 if (have_space)
1385 strcat(buffer, "\"");
1386 if (command) {
1387 strcat(buffer, " WITH ");
1388 strcat(buffer, command);
1391 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1393 wfree(buffer);
1394 wfree(data->name);
1395 wfree(data);
1398 WM_ITERATE_ARRAY(files, data, iter) {
1399 /* executable: add as entry */
1400 length = strlen(path[data->index]) + strlen(data->name) + 6;
1401 if (command)
1402 length += strlen(command);
1404 buffer = malloc(length);
1405 if (!buffer) {
1406 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1407 break;
1410 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1411 if (command != NULL) {
1412 strcpy(buffer, command);
1413 strcat(buffer, " ");
1414 if (have_space)
1415 strcat(buffer, "\"");
1416 strcat(buffer, path[data->index]);
1417 } else {
1418 if (have_space) {
1419 buffer[0] = '"';
1420 buffer[1] = 0;
1421 strcat(buffer, path[data->index]);
1422 } else {
1423 strcpy(buffer, path[data->index]);
1426 strcat(buffer, "/");
1427 strcat(buffer, data->name);
1428 if (have_space)
1429 strcat(buffer, "\"");
1431 if (stripExtension) {
1432 char *ptr = strrchr(data->name, '.');
1433 if (ptr && ptr != data->name)
1434 *ptr = 0;
1436 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1438 wfree(buffer);
1439 wfree(data->name);
1440 wfree(data);
1443 WMFreeArray(files);
1444 WMFreeArray(dirs);
1446 return menu;
1449 /************ Menu Configuration From WMRootMenu *************/
1451 static WMenu *makeDefaultMenu(WScreen * scr)
1453 WMenu *menu = NULL;
1455 menu = wMenuCreate(scr, _("Commands"), True);
1456 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1457 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1458 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1459 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1460 return menu;
1464 *----------------------------------------------------------------------
1465 * configureMenu--
1466 * Reads root menu configuration from defaults database.
1468 *----------------------------------------------------------------------
1470 static WMenu *configureMenu(WScreen *scr, WMPropList *definition)
1472 WMenu *menu = NULL;
1473 WMPropList *elem;
1474 int i, count;
1475 WMPropList *title, *command, *params;
1476 char *tmp, *mtitle;
1478 if (WMIsPLString(definition)) {
1479 struct stat stat_buf;
1480 char *path = NULL;
1481 Bool menu_is_default = False;
1483 /* menu definition is a string. Probably a path, so parse the file */
1485 tmp = wexpandpath(WMGetFromPLString(definition));
1487 path = getLocalizedMenuFile(tmp);
1489 if (!path)
1490 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1492 if (!path) {
1493 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1494 menu_is_default = True;
1497 if (!path) {
1498 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1499 wfree(tmp);
1500 return NULL;
1503 if (stat(path, &stat_buf) < 0) {
1504 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1505 wfree(path);
1506 wfree(tmp);
1507 return NULL;
1510 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1511 /* if the pointer in WMRootMenu has changed */
1512 || w_global.domain.root_menu->timestamp > scr->root_menu->timestamp) {
1513 WMPropList *menu_from_file = NULL;
1515 if (menu_is_default) {
1516 wwarning(_
1517 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1518 path);
1521 menu_from_file = WMReadPropListFromFile(path);
1522 if (menu_from_file == NULL) { /* old style menu */
1523 menu = readMenuFile(scr, path);
1524 } else {
1525 menu = configureMenu(scr, menu_from_file);
1526 WMReleasePropList(menu_from_file);
1529 if (menu)
1530 menu->timestamp = WMAX(stat_buf.st_mtime, w_global.domain.root_menu->timestamp);
1531 } else {
1532 menu = NULL;
1534 wfree(path);
1535 wfree(tmp);
1537 return menu;
1540 count = WMGetPropListItemCount(definition);
1541 if (count == 0)
1542 return NULL;
1544 elem = WMGetFromPLArray(definition, 0);
1545 if (!WMIsPLString(elem)) {
1546 tmp = WMGetPropListDescription(elem, False);
1547 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1548 wfree(tmp);
1549 return NULL;
1551 mtitle = WMGetFromPLString(elem);
1553 menu = wMenuCreate(scr, M_(mtitle), False);
1554 menu->on_destroy = removeShortcutsForMenu;
1556 for (i = 1; i < count; i++) {
1557 elem = WMGetFromPLArray(definition, i);
1558 #if 0
1559 if (WMIsPLString(elem)) {
1560 char *file;
1562 file = WMGetFromPLString(elem);
1565 #endif
1566 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1567 goto error;
1569 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1570 WMenu *submenu;
1571 WMenuEntry *mentry;
1573 /* submenu */
1574 submenu = configureMenu(scr, elem);
1575 if (submenu) {
1576 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1577 wMenuEntrySetCascade(menu, mentry, submenu);
1579 } else {
1580 int idx = 0;
1581 WMPropList *shortcut;
1582 /* normal entry */
1584 title = WMGetFromPLArray(elem, idx++);
1585 shortcut = WMGetFromPLArray(elem, idx++);
1586 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1587 shortcut = WMGetFromPLArray(elem, idx++);
1588 command = WMGetFromPLArray(elem, idx++);
1589 } else {
1590 command = shortcut;
1591 shortcut = NULL;
1593 params = WMGetFromPLArray(elem, idx++);
1595 if (!title || !command)
1596 goto error;
1598 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1599 shortcut ? WMGetFromPLString(shortcut) : NULL,
1600 WMGetFromPLString(command),
1601 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1603 continue;
1605 error:
1606 tmp = WMGetPropListDescription(elem, False);
1607 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1608 wfree(tmp);
1611 return menu;
1615 *----------------------------------------------------------------------
1616 * OpenRootMenu--
1617 * Opens the root menu, parsing the menu configuration from the
1618 * defaults database.
1619 * If the menu is already mapped and is not sticked to the
1620 * root window, it will be unmapped.
1622 * Side effects:
1623 * The menu may be remade.
1625 * Notes:
1626 * Construction of OPEN_MENU entries are delayed to the moment the
1627 * user map's them.
1628 *----------------------------------------------------------------------
1630 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1632 WMenu *menu = NULL;
1633 WMPropList *definition;
1635 static WMPropList *domain=NULL;
1637 if (!domain) {
1638 domain = WMCreatePLString("WMRootMenu");
1642 scr->flags.root_menu_changed_shortcuts = 0;
1643 scr->flags.added_workspace_menu = 0;
1644 scr->flags.added_windows_menu = 0;
1646 if (scr->root_menu && scr->root_menu->flags.mapped) {
1647 menu = scr->root_menu;
1648 if (!menu->flags.buttoned) {
1649 wMenuUnmap(menu);
1650 } else {
1651 wRaiseFrame(menu->frame->core);
1653 if (keyboard)
1654 wMenuMapAt(menu, 0, 0, True);
1655 else
1656 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1658 return;
1661 definition = w_global.domain.root_menu->dictionary;
1664 definition = PLGetDomain(domain);
1666 if (definition) {
1667 if (WMIsPLArray(definition)) {
1668 if (!scr->root_menu || w_global.domain.root_menu->timestamp > scr->root_menu->timestamp) {
1669 menu = configureMenu(scr, definition);
1670 if (menu)
1671 menu->timestamp = w_global.domain.root_menu->timestamp;
1673 } else
1674 menu = NULL;
1675 } else {
1676 menu = configureMenu(scr, definition);
1680 if (!menu) {
1681 /* menu hasn't changed or could not be read */
1682 if (!scr->root_menu) {
1683 wMessageDialog(scr, _("Error"),
1684 _("The applications menu could not be loaded. "
1685 "Look at the console output for a detailed "
1686 "description of the errors."), _("OK"), NULL, NULL);
1688 menu = makeDefaultMenu(scr);
1689 scr->root_menu = menu;
1691 menu = scr->root_menu;
1692 } else {
1693 /* new root menu */
1694 if (scr->root_menu) {
1695 wMenuDestroy(scr->root_menu, True);
1697 scr->root_menu = menu;
1699 if (menu) {
1700 int newx, newy;
1702 if (keyboard && x == 0 && y == 0) {
1703 newx = newy = 0;
1704 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1705 newx = x - menu->frame->core->width / 2;
1706 newy = y - menu->frame->core->height / 2;
1707 } else {
1708 newx = x - menu->frame->core->width / 2;
1709 newy = y;
1711 wMenuMapAt(menu, newx, newy, keyboard);
1714 if (scr->flags.root_menu_changed_shortcuts)
1715 rebindKeygrabs(scr);