Remove curly brackets
[wmaker-crm.git] / src / rootmenu.c
blob94d35d9e939fa24c2cf0e0b1249f5a250e8bff5c
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 "dialog.h"
46 #include "keybind.h"
47 #include "stacking.h"
48 #include "workspace.h"
49 #include "defaults.h"
50 #include "framewin.h"
51 #include "session.h"
52 #include "xmodifier.h"
54 #include <WINGs/WUtil.h>
56 #define MAX_SHORTCUT_LENGTH 32
58 extern char *Locale;
59 extern WDDomain *WDRootMenu;
60 extern Cursor wCursor[WCUR_LAST];
61 extern WPreferences wPreferences;
63 static WMenu *readMenuPipe(WScreen * scr, char **file_name);
64 static WMenu *readMenuFile(WScreen * scr, char *file_name);
65 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **file_name, char *command);
66 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals);
68 typedef struct Shortcut {
69 struct Shortcut *next;
71 int modifier;
72 KeyCode keycode;
73 WMenuEntry *entry;
74 WMenu *menu;
75 } Shortcut;
77 static Shortcut *shortcutList = NULL;
80 * Syntax:
81 * # main menu
82 * "Menu Name" MENU
83 * "Title" EXEC command_to_exec -params
84 * "Submenu" MENU
85 * "Title" EXEC command_to_exec -params
86 * "Submenu" END
87 * "Workspaces" WORKSPACE_MENU
88 * "Title" built_in_command
89 * "Quit" EXIT
90 * "Quick Quit" EXIT QUICK
91 * "Menu Name" END
93 * Commands may be preceded by SHORTCUT key
95 * Built-in commands:
97 * INFO_PANEL - shows the Info Panel
98 * LEGAL_PANEL - shows the Legal info panel
99 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
100 * REFRESH - forces the desktop to be repainted
101 * EXIT [QUICK] - exit the window manager [without confirmation]
102 * EXEC <program> - execute an external program
103 * SHEXEC <command> - execute a shell command
104 * WORKSPACE_MENU - places the workspace submenu
105 * ARRANGE_ICONS
106 * RESTART [<window manager>] - restarts the window manager
107 * SHOW_ALL - unhide all windows on workspace
108 * HIDE_OTHERS - hides all windows excep the focused one
109 * OPEN_MENU file - read menu data from file which must be a valid menu file.
110 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
111 * - read menu data from directory(ies) and
112 * eventually precede each with a command.
113 * OPEN_MENU | command
114 * - opens command and uses its stdout to construct and insert
115 * the resulting menu in current position. The output of
116 * command must be a valid menu description.
117 * The space between '|' and command is optional.
118 * || will do the same, but will not cache the contents.
119 * SAVE_SESSION - saves the current state of the desktop, which include
120 * all running applications, all their hints (geometry,
121 * position on screen, workspace they live on, the dock
122 * or clip from where they were launched, and
123 * if minimized, shaded or hidden. Also saves the current
124 * workspace the user is on. All will be restored on every
125 * start of windowmaker until another SAVE_SESSION or
126 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
127 * WindowMaker domain file, then saving is automatically
128 * done on every windowmaker exit, overwriting any
129 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
130 * dock state now.
131 * CLEAR_SESSION - clears any previous saved session. This will not have
132 * any effect if SaveSessionOnExit is True.
136 #define M_QUICK 1
138 /* menu commands */
140 static void execCommand(WMenu * menu, WMenuEntry * entry)
142 char *cmdline;
144 cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
146 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
147 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);
148 XSync(dpy, 0);
150 if (cmdline) {
151 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
152 wfree(cmdline);
154 XUngrabPointer(dpy, CurrentTime);
155 XSync(dpy, 0);
158 static void exitCommand(WMenu * menu, WMenuEntry * entry)
160 static int inside = 0;
161 int result;
163 /* prevent reentrant calls */
164 if (inside)
165 return;
166 inside = 1;
168 #define R_CANCEL 0
169 #define R_EXIT 1
171 result = R_CANCEL;
173 if ((long)entry->clientdata == M_QUICK) {
174 result = R_EXIT;
175 } else {
176 int r, oldSaveSessionFlag;
178 oldSaveSessionFlag = wPreferences.save_session_on_exit;
179 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
180 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
182 if (r == WAPRDefault) {
183 result = R_EXIT;
184 } else if (r == WAPRAlternate) {
185 /* Don't modify the "save session on exit" flag if the
186 * user canceled the operation. */
187 wPreferences.save_session_on_exit = oldSaveSessionFlag;
190 if (result == R_EXIT)
191 Shutdown(WSExitMode);
193 #undef R_EXIT
194 #undef R_CANCEL
195 inside = 0;
198 static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
200 static int inside = 0;
201 int result;
203 /* prevent reentrant calls */
204 if (inside)
205 return;
206 inside = 1;
208 #define R_CANCEL 0
209 #define R_CLOSE 1
210 #define R_KILL 2
212 result = R_CANCEL;
213 if ((long)entry->clientdata == M_QUICK)
214 result = R_CLOSE;
215 else {
216 int r, oldSaveSessionFlag;
218 oldSaveSessionFlag = wPreferences.save_session_on_exit;
220 r = wExitDialog(menu->frame->screen_ptr,
221 _("Kill X session"),
222 _("Kill Window System session?\n"
223 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
224 if (r == WAPRDefault) {
225 result = R_KILL;
226 } else if (r == WAPRAlternate) {
227 /* Don't modify the "save session on exit" flag if the
228 * user canceled the operation. */
229 wPreferences.save_session_on_exit = oldSaveSessionFlag;
233 if (result != R_CANCEL) {
234 Shutdown(WSKillMode);
236 #undef R_CLOSE
237 #undef R_CANCEL
238 #undef R_KILL
239 inside = 0;
242 static void restartCommand(WMenu * menu, WMenuEntry * entry)
244 Shutdown(WSRestartPreparationMode);
245 Restart((char *)entry->clientdata, False);
246 Restart(NULL, True);
249 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
251 wRefreshDesktop(menu->frame->screen_ptr);
254 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
256 wArrangeIcons(menu->frame->screen_ptr, True);
259 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
261 wShowAllWindows(menu->frame->screen_ptr);
264 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
266 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
269 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
271 if (!wPreferences.save_session_on_exit)
272 wSessionSaveState(menu->frame->screen_ptr);
274 wScreenSaveState(menu->frame->screen_ptr);
277 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
279 wSessionClearState(menu->frame->screen_ptr);
280 wScreenSaveState(menu->frame->screen_ptr);
283 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
285 wShowInfoPanel(menu->frame->screen_ptr);
288 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
290 wShowLegalPanel(menu->frame->screen_ptr);
293 /********************************************************************/
295 static char * getLocalizedMenuFile(char *menu)
297 char *buffer, *ptr, *locale;
298 int len;
300 if (!Locale)
301 return NULL;
303 len = strlen(menu) + strlen(Locale) + 8;
304 buffer = wmalloc(len);
306 /* try menu.locale_name */
307 snprintf(buffer, len, "%s.%s", menu, Locale);
308 if (access(buffer, F_OK) == 0)
309 return buffer;
311 /* position of locale in our buffer */
312 locale = buffer + strlen(menu) + 1;
314 /* check if it is in the form aa_bb.encoding and check for aa_bb */
315 ptr = strchr(locale, '.');
316 if (ptr) {
317 *ptr = 0;
318 if (access(buffer, F_OK) == 0)
319 return buffer;
322 /* now check for aa */
323 ptr = strchr(locale, '_');
324 if (ptr) {
325 *ptr = 0;
326 if (access(buffer, F_OK) == 0)
327 return buffer;
330 wfree(buffer);
332 return NULL;
335 Bool wRootMenuPerformShortcut(XEvent * event)
337 WScreen *scr = wScreenForRootWindow(event->xkey.root);
338 Shortcut *ptr;
339 int modifiers;
340 int done = 0;
342 /* ignore CapsLock */
343 modifiers = event->xkey.state & ValidModMask;
345 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
346 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
347 continue;
349 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
350 (*ptr->entry->callback) (ptr->menu, ptr->entry);
351 done = True;
355 return done;
358 void wRootMenuBindShortcuts(Window window)
360 Shortcut *ptr;
362 ptr = shortcutList;
363 while (ptr) {
364 if (ptr->modifier != AnyModifier) {
365 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
366 window, True, GrabModeAsync, GrabModeAsync);
367 #ifdef NUMLOCK_HACK
368 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
369 #endif
371 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
372 ptr = ptr->next;
376 static void rebindKeygrabs(WScreen * scr)
378 WWindow *wwin;
380 wwin = scr->focused_window;
382 while (wwin != NULL) {
383 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
385 if (!WFLAGP(wwin, no_bind_keys)) {
386 wWindowSetKeyGrabs(wwin);
388 wwin = wwin->prev;
392 static void removeShortcutsForMenu(WMenu * menu)
394 Shortcut *ptr, *tmp;
395 Shortcut *newList = NULL;
397 ptr = shortcutList;
398 while (ptr != NULL) {
399 tmp = ptr->next;
400 if (ptr->menu == menu) {
401 wfree(ptr);
402 } else {
403 ptr->next = newList;
404 newList = ptr;
406 ptr = tmp;
408 shortcutList = newList;
409 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
412 static Bool addShortcut(char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
414 Shortcut *ptr;
415 KeySym ksym;
416 char *k;
417 char buf[MAX_SHORTCUT_LENGTH], *b;
419 ptr = wmalloc(sizeof(Shortcut));
421 wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
422 b = (char *)buf;
424 /* get modifiers */
425 ptr->modifier = 0;
426 while ((k = strchr(b, '+')) != NULL) {
427 int mod;
429 *k = 0;
430 mod = wXModifierFromKey(b);
431 if (mod < 0) {
432 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
433 wfree(ptr);
434 return False;
436 ptr->modifier |= mod;
438 b = k + 1;
441 /* get key */
442 ksym = XStringToKeysym(b);
444 if (ksym == NoSymbol) {
445 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
446 file, shortcutDefinition, entry->text);
447 wfree(ptr);
448 return False;
451 ptr->keycode = XKeysymToKeycode(dpy, ksym);
452 if (ptr->keycode == 0) {
453 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
454 shortcutDefinition, entry->text);
455 wfree(ptr);
456 return False;
459 ptr->menu = menu;
460 ptr->entry = entry;
462 ptr->next = shortcutList;
463 shortcutList = ptr;
465 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
467 return True;
470 static char *next_token(char *line, char **next)
472 char *tmp, c;
473 char *ret;
475 *next = NULL;
476 while (*line == ' ' || *line == '\t')
477 line++;
479 tmp = line;
481 if (*tmp == '"') {
482 tmp++;
483 line++;
484 while (*tmp != 0 && *tmp != '"')
485 tmp++;
486 if (*tmp != '"') {
487 wwarning(_("%s: unmatched '\"' in menu file"), line);
488 return NULL;
490 } else {
491 do {
492 if (*tmp == '\\')
493 tmp++;
495 if (*tmp != 0)
496 tmp++;
498 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
501 c = *tmp;
502 *tmp = 0;
503 ret = wstrdup(line);
504 *tmp = c;
506 if (c == 0)
507 return ret;
508 else
509 tmp++;
511 /* skip blanks */
512 while (*tmp == ' ' || *tmp == '\t')
513 tmp++;
515 if (*tmp != 0)
516 *next = tmp;
518 return ret;
521 static void separateCommand(char *line, char ***file, char **command)
523 char *token, *tmp = line;
524 WMArray *array = WMCreateArray(4);
525 int count, i;
527 *file = NULL;
528 *command = NULL;
529 do {
530 token = next_token(tmp, &tmp);
531 if (token) {
532 if (strcmp(token, "WITH") == 0) {
533 if (tmp != NULL && *tmp != 0)
534 *command = wstrdup(tmp);
535 else
536 wwarning(_("%s: missing command"), line);
537 break;
539 WMAddToArray(array, token);
541 } while (token != NULL && tmp != NULL);
543 count = WMGetArrayItemCount(array);
544 if (count > 0) {
545 *file = wmalloc(sizeof(char *) * (count + 1));
546 (*file)[count] = NULL;
547 for (i = 0; i < count; i++) {
548 (*file)[i] = WMGetFromArray(array, i);
551 WMFreeArray(array);
554 static WMenu *constructPLMenu(WScreen *screen, char *path)
556 WMPropList *pl = NULL;
557 WMenu *menu = NULL;
559 if (!path)
560 return NULL;
562 pl = WMReadPropListFromFile(path);
563 if (!pl)
564 return NULL;
566 menu = configureMenu(screen, pl, False);
567 if (!menu)
568 return NULL;
570 menu->on_destroy = removeShortcutsForMenu;
571 return menu;
574 static void constructMenu(WMenu * menu, WMenuEntry * entry)
576 WMenu *submenu;
577 struct stat stat_buf;
578 char **path;
579 char *cmd;
580 char *lpath = NULL;
581 int i, first = -1;
582 time_t last = 0;
584 separateCommand((char *)entry->clientdata, &path, &cmd);
585 if (path == NULL || *path == NULL || **path == 0) {
586 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
587 if (cmd)
588 wfree(cmd);
589 return;
592 if (path[0][0] == '|') {
593 /* pipe menu */
595 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
596 /* parse pipe */
598 submenu = readMenuPipe(menu->frame->screen_ptr, path);
600 if (submenu != NULL) {
601 if (path[0][1] == '|')
602 submenu->timestamp = 0;
603 else
604 submenu->timestamp = 1; /* there's no automatic reloading */
606 } else {
607 submenu = NULL;
610 } else {
612 /* try interpreting path as a proplist file */
613 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
614 /* if unsuccessful, try it as an old-style file */
615 if (!submenu) {
617 i = 0;
618 while (path[i] != NULL) {
619 char *tmp;
621 if (strcmp(path[i], "-noext") == 0) {
622 i++;
623 continue;
626 tmp = wexpandpath(path[i]);
627 wfree(path[i]);
628 lpath = getLocalizedMenuFile(tmp);
629 if (lpath) {
630 wfree(tmp);
631 path[i] = lpath;
632 lpath = NULL;
633 } else {
634 path[i] = tmp;
637 if (stat(path[i], &stat_buf) == 0) {
638 if (last < stat_buf.st_mtime)
639 last = stat_buf.st_mtime;
640 if (first < 0)
641 first = i;
642 } else {
643 werror(_("%s:could not stat menu"), path[i]);
644 /*goto finish; */
647 i++;
650 if (first < 0) {
651 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
652 goto finish;
654 stat(path[first], &stat_buf);
655 if (!menu->cascades[entry->cascade]
656 || menu->cascades[entry->cascade]->timestamp < last) {
658 if (S_ISDIR(stat_buf.st_mode)) {
659 /* menu directory */
660 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
661 if (submenu)
662 submenu->timestamp = last;
663 } else if (S_ISREG(stat_buf.st_mode)) {
664 /* menu file */
666 if (cmd || path[1])
667 wwarning(_("too many parameters in OPEN_MENU: %s"),
668 (char *)entry->clientdata);
670 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
671 if (submenu)
672 submenu->timestamp = stat_buf.st_mtime;
673 } else {
674 submenu = NULL;
676 } else {
677 submenu = NULL;
682 if (submenu) {
683 wMenuEntryRemoveCascade(menu, entry);
684 wMenuEntrySetCascade(menu, entry, submenu);
687 finish:
688 i = 0;
689 while (path[i] != NULL)
690 wfree(path[i++]);
691 wfree(path);
692 if (cmd)
693 wfree(cmd);
696 static void cleanupWorkspaceMenu(WMenu * menu)
698 if (menu->frame->screen_ptr->workspace_menu == menu)
699 menu->frame->screen_ptr->workspace_menu = NULL;
702 static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
704 WMenu *wsmenu;
705 WMenuEntry *entry;
707 if (scr->flags.added_workspace_menu) {
708 wwarning(_
709 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
710 return NULL;
711 } else {
712 scr->flags.added_workspace_menu = 1;
714 wsmenu = wWorkspaceMenuMake(scr, True);
715 wsmenu->on_destroy = cleanupWorkspaceMenu;
717 scr->workspace_menu = wsmenu;
718 entry = wMenuAddCallback(menu, title, NULL, NULL);
719 wMenuEntrySetCascade(menu, entry, wsmenu);
721 wWorkspaceMenuUpdate(scr, wsmenu);
723 return entry;
726 static void cleanupWindowsMenu(WMenu * menu)
728 if (menu->frame->screen_ptr->switch_menu == menu)
729 menu->frame->screen_ptr->switch_menu = NULL;
732 static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
734 WMenu *wwmenu;
735 WWindow *wwin;
736 WMenuEntry *entry;
738 if (scr->flags.added_windows_menu) {
739 wwarning(_
740 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
741 return NULL;
742 } else {
743 scr->flags.added_windows_menu = 1;
745 wwmenu = wMenuCreate(scr, _("Window List"), False);
746 wwmenu->on_destroy = cleanupWindowsMenu;
747 scr->switch_menu = wwmenu;
748 wwin = scr->focused_window;
749 while (wwin) {
750 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
752 wwin = wwin->prev;
754 entry = wMenuAddCallback(menu, title, NULL, NULL);
755 wMenuEntrySetCascade(menu, entry, wwmenu);
757 return entry;
760 static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
761 char *params, char *file_name)
763 WScreen *scr;
764 WMenuEntry *entry = NULL;
765 Bool shortcutOk = False;
767 if (!menu)
768 return NULL;
769 scr = menu->frame->screen_ptr;
770 if (strcmp(command, "OPEN_MENU") == 0) {
771 if (!params) {
772 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
773 } else {
774 WMenu *dummy;
775 char *path;
777 path = wfindfile(DEF_CONFIG_PATHS, params);
778 if (!path) {
779 path = wstrdup(params);
781 dummy = wMenuCreate(scr, title, False);
782 dummy->on_destroy = removeShortcutsForMenu;
783 entry = wMenuAddCallback(menu, title, constructMenu, path);
784 entry->free_cdata = wfree;
785 wMenuEntrySetCascade(menu, entry, dummy);
787 } else if (strcmp(command, "EXEC") == 0) {
788 if (!params)
789 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
790 else {
791 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
792 entry->free_cdata = wfree;
793 shortcutOk = True;
795 } else if (strcmp(command, "SHEXEC") == 0) {
796 if (!params)
797 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
798 else {
799 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
800 entry->free_cdata = wfree;
801 shortcutOk = True;
803 } else if (strcmp(command, "EXIT") == 0) {
805 if (params && strcmp(params, "QUICK") == 0)
806 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
807 else
808 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
810 shortcutOk = True;
811 } else if (strcmp(command, "SHUTDOWN") == 0) {
813 if (params && strcmp(params, "QUICK") == 0)
814 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
815 else
816 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
818 shortcutOk = True;
819 } else if (strcmp(command, "REFRESH") == 0) {
820 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
822 shortcutOk = True;
823 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
824 entry = addWorkspaceMenu(scr, menu, title);
826 shortcutOk = True;
827 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
828 entry = addWindowsMenu(scr, menu, title);
830 shortcutOk = True;
831 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
832 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
834 shortcutOk = True;
835 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
836 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
838 shortcutOk = True;
839 } else if (strcmp(command, "SHOW_ALL") == 0) {
840 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
842 shortcutOk = True;
843 } else if (strcmp(command, "RESTART") == 0) {
844 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
845 entry->free_cdata = wfree;
846 shortcutOk = True;
847 } else if (strcmp(command, "SAVE_SESSION") == 0) {
848 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
850 shortcutOk = True;
851 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
852 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
853 shortcutOk = True;
854 } else if (strcmp(command, "INFO_PANEL") == 0) {
855 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
856 shortcutOk = True;
857 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
858 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
859 shortcutOk = True;
860 } else {
861 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
863 return NULL;
866 if (shortcut && entry) {
867 if (!shortcutOk) {
868 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
869 } else {
870 if (addShortcut(file_name, shortcut, menu, entry)) {
872 entry->rtext = GetShortcutString(shortcut);
874 entry->rtext = wstrdup(shortcut);
880 return entry;
883 /******************* Menu Configuration From File *******************/
885 static void separateline(char *line, char *title, char *command, char *parameter, char *shortcut)
887 int l, i;
889 l = strlen(line);
891 *title = 0;
892 *command = 0;
893 *parameter = 0;
894 *shortcut = 0;
895 /* get the title */
896 while (isspace(*line) && (*line != 0))
897 line++;
898 if (*line == '"') {
899 line++;
900 i = 0;
901 while (line[i] != '"' && (line[i] != 0))
902 i++;
903 if (line[i] != '"')
904 return;
905 } else {
906 i = 0;
907 while (!isspace(line[i]) && (line[i] != 0))
908 i++;
910 strncpy(title, line, i);
911 title[i++] = 0;
912 line += i;
914 /* get the command or shortcut keyword */
915 while (isspace(*line) && (*line != 0))
916 line++;
917 if (*line == 0)
918 return;
919 i = 0;
920 while (!isspace(line[i]) && (line[i] != 0))
921 i++;
922 strncpy(command, line, i);
923 command[i++] = 0;
924 line += i;
926 if (strcmp(command, "SHORTCUT") == 0) {
927 /* get the shortcut key */
928 while (isspace(*line) && (*line != 0))
929 line++;
930 if (*line == '"') {
931 line++;
932 i = 0;
933 while (line[i] != '"' && (line[i] != 0))
934 i++;
935 if (line[i] != '"')
936 return;
937 } else {
938 i = 0;
939 while (!isspace(line[i]) && (line[i] != 0))
940 i++;
942 strncpy(shortcut, line, i);
943 shortcut[i++] = 0;
944 line += i;
946 *command = 0;
948 /* get the command */
949 while (isspace(*line) && (*line != 0))
950 line++;
951 if (*line == 0)
952 return;
953 i = 0;
954 while (!isspace(line[i]) && (line[i] != 0))
955 i++;
956 strncpy(command, line, i);
957 command[i++] = 0;
958 line += i;
961 /* get the parameters */
962 while (isspace(*line) && (*line != 0))
963 line++;
964 if (*line == 0)
965 return;
967 if (*line == '"') {
968 line++;
969 l = 0;
970 while (line[l] != 0 && line[l] != '"') {
971 parameter[l] = line[l];
972 l++;
974 parameter[l] = 0;
975 return;
978 l = strlen(line);
979 while (isspace(line[l]) && (l > 0))
980 l--;
981 strncpy(parameter, line, l);
982 parameter[l] = 0;
985 static WMenu *parseCascade(WScreen * scr, WMenu * menu, FILE * file, char *file_name)
987 char linebuf[MAXLINE];
988 char elinebuf[MAXLINE];
989 char title[MAXLINE];
990 char command[MAXLINE];
991 char shortcut[MAXLINE];
992 char params[MAXLINE];
993 char *line;
995 while (!feof(file)) {
996 int lsize, ok;
998 ok = 0;
999 fgets(linebuf, MAXLINE, file);
1000 line = wtrimspace(linebuf);
1001 lsize = strlen(line);
1002 do {
1003 if (line[lsize - 1] == '\\') {
1004 char *line2;
1005 int lsize2;
1006 fgets(elinebuf, MAXLINE, file);
1007 line2 = wtrimspace(elinebuf);
1008 lsize2 = strlen(line2);
1009 if (lsize2 + lsize > MAXLINE) {
1010 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1011 file_name, line);
1012 ok = 2;
1013 } else {
1014 line[lsize - 1] = 0;
1015 lsize += lsize2 - 1;
1016 strcat(line, line2);
1018 } else {
1019 ok = 1;
1021 } while (!ok && !feof(file));
1022 if (ok == 2)
1023 continue;
1025 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1026 continue;
1028 separateline(line, title, command, params, shortcut);
1030 if (!command[0]) {
1031 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1032 goto error;
1035 if (strcasecmp(command, "MENU") == 0) {
1036 WMenu *cascade;
1038 /* start submenu */
1040 cascade = wMenuCreate(scr, M_(title), False);
1041 cascade->on_destroy = removeShortcutsForMenu;
1042 if (parseCascade(scr, cascade, file, file_name) == NULL) {
1043 wMenuDestroy(cascade, True);
1044 } else {
1045 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
1047 } else if (strcasecmp(command, "END") == 0) {
1048 /* end of menu */
1049 return menu;
1051 } else {
1052 /* normal items */
1053 addMenuEntry(menu, M_(title), shortcut[0] ? shortcut : NULL, command,
1054 params[0] ? params : NULL, file_name);
1058 wwarning(_("%s:syntax error in menu file:END declaration missing"), file_name);
1059 return menu;
1061 error:
1062 return menu;
1065 static WMenu *readMenuFile(WScreen * scr, char *file_name)
1067 WMenu *menu = NULL;
1068 FILE *file = NULL;
1069 char linebuf[MAXLINE];
1070 char title[MAXLINE];
1071 char shortcut[MAXLINE];
1072 char command[MAXLINE];
1073 char params[MAXLINE];
1074 char *line;
1075 #ifdef USECPP
1076 char *args;
1077 int cpp = 0;
1078 #endif
1080 #ifdef USECPP
1081 if (!wPreferences.flags.nocpp) {
1082 args = MakeCPPArgs(file_name);
1083 if (!args) {
1084 wwarning(_("could not make arguments for menu file preprocessor"));
1085 } else {
1086 snprintf(command, sizeof(command), "%s %s %s", CPP_PATH, args, file_name);
1087 wfree(args);
1088 file = popen(command, "r");
1089 if (!file) {
1090 werror(_("%s:could not open/preprocess menu file"), file_name);
1091 } else {
1092 cpp = 1;
1096 #endif /* USECPP */
1098 if (!file) {
1099 file = fopen(file_name, "rb");
1100 if (!file) {
1101 werror(_("%s:could not open menu file"), file_name);
1102 return NULL;
1106 while (!feof(file)) {
1107 if (!fgets(linebuf, MAXLINE, file))
1108 break;
1109 line = wtrimspace(linebuf);
1110 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1111 continue;
1113 separateline(line, title, command, params, shortcut);
1115 if (!command[0]) {
1116 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1117 break;
1119 if (strcasecmp(command, "MENU") == 0) {
1120 menu = wMenuCreate(scr, M_(title), True);
1121 menu->on_destroy = removeShortcutsForMenu;
1122 if (!parseCascade(scr, menu, file, file_name)) {
1123 wMenuDestroy(menu, True);
1125 break;
1126 } else {
1127 wwarning(_("%s:invalid menu file. MENU command is missing"), file_name);
1128 break;
1132 #ifdef USECPP
1133 if (cpp) {
1134 if (pclose(file) == -1) {
1135 werror(_("error reading preprocessed menu data"));
1137 } else {
1138 fclose(file);
1140 #else
1141 fclose(file);
1142 #endif
1144 return menu;
1147 /************ Menu Configuration From Pipe *************/
1149 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1151 WMenu *menu = NULL;
1152 FILE *file = NULL;
1153 char linebuf[MAXLINE];
1154 char title[MAXLINE];
1155 char command[MAXLINE];
1156 char params[MAXLINE];
1157 char shortcut[MAXLINE];
1158 char *line;
1159 char *filename;
1160 char flat_file[MAXLINE];
1161 int i;
1162 #ifdef USECPP
1163 char *args;
1164 #endif
1166 flat_file[0] = '\0';
1168 for (i = 0; file_name[i] != NULL; i++) {
1169 strcat(flat_file, file_name[i]);
1170 strcat(flat_file, " ");
1172 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1174 #ifdef USECPP
1175 if (!wPreferences.flags.nocpp) {
1176 args = MakeCPPArgs(filename);
1177 if (!args) {
1178 wwarning(_("could not make arguments for menu file preprocessor"));
1179 } else {
1180 snprintf(command, sizeof(command), "%s | %s %s", filename, CPP_PATH, args);
1182 wfree(args);
1183 file = popen(command, "r");
1184 if (!file) {
1185 werror(_("%s:could not open/preprocess menu file"), filename);
1189 #endif /* USECPP */
1191 if (!file) {
1192 file = popen(filename, "rb");
1194 if (!file) {
1195 werror(_("%s:could not open menu file"), filename);
1196 return NULL;
1200 while (!feof(file)) {
1201 if (!fgets(linebuf, MAXLINE, file))
1202 break;
1203 line = wtrimspace(linebuf);
1204 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1205 continue;
1207 separateline(line, title, command, params, shortcut);
1209 if (!command[0]) {
1210 wwarning(_("%s:missing command in menu config: %s"), filename, line);
1211 break;
1213 if (strcasecmp(command, "MENU") == 0) {
1214 menu = wMenuCreate(scr, M_(title), True);
1215 menu->on_destroy = removeShortcutsForMenu;
1216 if (!parseCascade(scr, menu, file, filename)) {
1217 wMenuDestroy(menu, True);
1219 break;
1220 } else {
1221 wwarning(_("%s:no title given for the root menu"), filename);
1222 break;
1226 pclose(file);
1228 return menu;
1231 typedef struct {
1232 char *name;
1233 int index;
1234 } dir_data;
1236 static int myCompare(const void *d1, const void *d2)
1238 dir_data *p1 = *(dir_data **) d1;
1239 dir_data *p2 = *(dir_data **) d2;
1241 return strcmp(p1->name, p2->name);
1244 /************ Menu Configuration From Directory *************/
1246 static Bool isFilePackage(char *file)
1248 int l;
1250 /* check if the extension indicates this file is a
1251 * file package. For now, only recognize .themed */
1253 l = strlen(file);
1255 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1256 return True;
1257 } else {
1258 return False;
1262 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
1264 DIR *dir;
1265 struct dirent *dentry;
1266 struct stat stat_buf;
1267 WMenu *menu = NULL;
1268 char *buffer;
1269 WMArray *dirs = NULL, *files = NULL;
1270 WMArrayIterator iter;
1271 int length, i, have_space = 0;
1272 dir_data *data;
1273 int stripExtension = 0;
1275 dirs = WMCreateArray(16);
1276 files = WMCreateArray(16);
1278 i = 0;
1279 while (path[i] != NULL) {
1280 if (strcmp(path[i], "-noext") == 0) {
1281 stripExtension = 1;
1282 i++;
1283 continue;
1286 dir = opendir(path[i]);
1287 if (!dir) {
1288 i++;
1289 continue;
1292 while ((dentry = readdir(dir))) {
1294 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1295 continue;
1297 if (dentry->d_name[0] == '.')
1298 continue;
1300 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1301 if (!buffer) {
1302 werror(_("out of memory while constructing directory menu %s"), path[i]);
1303 break;
1306 strcpy(buffer, path[i]);
1307 strcat(buffer, "/");
1308 strcat(buffer, dentry->d_name);
1310 if (stat(buffer, &stat_buf) != 0) {
1311 werror(_("%s:could not stat file \"%s\" in menu directory"),
1312 path[i], dentry->d_name);
1313 } else {
1314 Bool isFilePack = False;
1316 data = NULL;
1317 if (S_ISDIR(stat_buf.st_mode)
1318 && !(isFilePack = isFilePackage(dentry->d_name))) {
1320 /* access always returns success for user root */
1321 if (access(buffer, X_OK) == 0) {
1322 /* Directory is accesible. Add to directory list */
1324 data = (dir_data *) wmalloc(sizeof(dir_data));
1325 data->name = wstrdup(dentry->d_name);
1326 data->index = i;
1328 WMAddToArray(dirs, data);
1330 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1331 /* Hack because access always returns X_OK success for user root */
1332 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1333 if ((command != NULL && access(buffer, R_OK) == 0) ||
1334 (command == NULL && access(buffer, X_OK) == 0 &&
1335 (stat_buf.st_mode & S_IXANY))) {
1337 data = (dir_data *) wmalloc(sizeof(dir_data));
1338 data->name = wstrdup(dentry->d_name);
1339 data->index = i;
1341 WMAddToArray(files, data);
1345 free(buffer);
1348 closedir(dir);
1349 i++;
1352 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1353 WMFreeArray(dirs);
1354 WMFreeArray(files);
1355 return NULL;
1358 WMSortArray(dirs, myCompare);
1359 WMSortArray(files, myCompare);
1361 menu = wMenuCreate(scr, M_(title), False);
1362 menu->on_destroy = removeShortcutsForMenu;
1364 WM_ITERATE_ARRAY(dirs, data, iter) {
1365 /* New directory. Use same OPEN_MENU command that was used
1366 * for the current directory. */
1367 length = strlen(path[data->index]) + strlen(data->name) + 6;
1368 if (stripExtension)
1369 length += 7;
1370 if (command)
1371 length += strlen(command) + 6;
1372 buffer = malloc(length);
1373 if (!buffer) {
1374 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1375 break;
1378 buffer[0] = '\0';
1379 if (stripExtension)
1380 strcat(buffer, "-noext ");
1382 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1384 if (have_space)
1385 strcat(buffer, "\"");
1386 strcat(buffer, path[data->index]);
1388 strcat(buffer, "/");
1389 strcat(buffer, data->name);
1390 if (have_space)
1391 strcat(buffer, "\"");
1392 if (command) {
1393 strcat(buffer, " WITH ");
1394 strcat(buffer, command);
1397 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1399 wfree(buffer);
1400 if (data->name)
1401 wfree(data->name);
1402 wfree(data);
1405 WM_ITERATE_ARRAY(files, data, iter) {
1406 /* executable: add as entry */
1407 length = strlen(path[data->index]) + strlen(data->name) + 6;
1408 if (command)
1409 length += strlen(command);
1411 buffer = malloc(length);
1412 if (!buffer) {
1413 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1414 break;
1417 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1418 if (command != NULL) {
1419 strcpy(buffer, command);
1420 strcat(buffer, " ");
1421 if (have_space)
1422 strcat(buffer, "\"");
1423 strcat(buffer, path[data->index]);
1424 } else {
1425 if (have_space) {
1426 buffer[0] = '"';
1427 buffer[1] = 0;
1428 strcat(buffer, path[data->index]);
1429 } else {
1430 strcpy(buffer, path[data->index]);
1433 strcat(buffer, "/");
1434 strcat(buffer, data->name);
1435 if (have_space)
1436 strcat(buffer, "\"");
1438 if (stripExtension) {
1439 char *ptr = strrchr(data->name, '.');
1440 if (ptr && ptr != data->name)
1441 *ptr = 0;
1443 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1445 wfree(buffer);
1446 if (data->name)
1447 wfree(data->name);
1448 wfree(data);
1451 WMFreeArray(files);
1452 WMFreeArray(dirs);
1454 return menu;
1457 /************ Menu Configuration From WMRootMenu *************/
1459 static WMenu *makeDefaultMenu(WScreen * scr)
1461 WMenu *menu = NULL;
1463 menu = wMenuCreate(scr, _("Commands"), True);
1464 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1465 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1466 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1467 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1468 return menu;
1472 *----------------------------------------------------------------------
1473 * configureMenu--
1474 * Reads root menu configuration from defaults database.
1476 *----------------------------------------------------------------------
1478 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1480 WMenu *menu = NULL;
1481 WMPropList *elem;
1482 int i, count;
1483 WMPropList *title, *command, *params;
1484 char *tmp, *mtitle;
1486 if (WMIsPLString(definition)) {
1487 struct stat stat_buf;
1488 char *path = NULL;
1489 Bool menu_is_default = False;
1491 /* menu definition is a string. Probably a path, so parse the file */
1493 tmp = wexpandpath(WMGetFromPLString(definition));
1495 path = getLocalizedMenuFile(tmp);
1497 if (!path)
1498 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1500 if (!path) {
1501 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1502 menu_is_default = True;
1505 if (!path) {
1506 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1507 wfree(tmp);
1508 return NULL;
1511 if (stat(path, &stat_buf) < 0) {
1512 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1513 wfree(path);
1514 wfree(tmp);
1515 return NULL;
1518 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1519 /* if the pointer in WMRootMenu has changed */
1520 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1522 if (menu_is_default) {
1523 wwarning(_
1524 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1525 path);
1528 menu = readMenuFile(scr, path);
1529 if (menu)
1530 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->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 #ifdef GLOBAL_SUBMENU_FILE
1557 if (includeGlobals) {
1558 WMenu *submenu;
1559 WMenuEntry *mentry;
1561 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1563 if (submenu) {
1564 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1565 wMenuEntrySetCascade(menu, mentry, submenu);
1568 #endif
1570 for (i = 1; i < count; i++) {
1571 elem = WMGetFromPLArray(definition, i);
1572 #if 0
1573 if (WMIsPLString(elem)) {
1574 char *file;
1576 file = WMGetFromPLString(elem);
1579 #endif
1580 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1581 goto error;
1583 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1584 WMenu *submenu;
1585 WMenuEntry *mentry;
1587 /* submenu */
1588 submenu = configureMenu(scr, elem, True);
1589 if (submenu) {
1590 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1591 wMenuEntrySetCascade(menu, mentry, submenu);
1593 } else {
1594 int idx = 0;
1595 WMPropList *shortcut;
1596 /* normal entry */
1598 title = WMGetFromPLArray(elem, idx++);
1599 shortcut = WMGetFromPLArray(elem, idx++);
1600 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1601 shortcut = WMGetFromPLArray(elem, idx++);
1602 command = WMGetFromPLArray(elem, idx++);
1603 } else {
1604 command = shortcut;
1605 shortcut = NULL;
1607 params = WMGetFromPLArray(elem, idx++);
1609 if (!title || !command)
1610 goto error;
1612 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1613 shortcut ? WMGetFromPLString(shortcut) : NULL,
1614 WMGetFromPLString(command),
1615 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1617 continue;
1619 error:
1620 tmp = WMGetPropListDescription(elem, False);
1621 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1622 wfree(tmp);
1625 return menu;
1629 *----------------------------------------------------------------------
1630 * OpenRootMenu--
1631 * Opens the root menu, parsing the menu configuration from the
1632 * defaults database.
1633 * If the menu is already mapped and is not sticked to the
1634 * root window, it will be unmapped.
1636 * Side effects:
1637 * The menu may be remade.
1639 * Notes:
1640 * Construction of OPEN_MENU entries are delayed to the moment the
1641 * user map's them.
1642 *----------------------------------------------------------------------
1644 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1646 WMenu *menu = NULL;
1647 WMPropList *definition;
1649 static WMPropList *domain=NULL;
1651 if (!domain) {
1652 domain = WMCreatePLString("WMRootMenu");
1656 scr->flags.root_menu_changed_shortcuts = 0;
1657 scr->flags.added_workspace_menu = 0;
1658 scr->flags.added_windows_menu = 0;
1660 if (scr->root_menu && scr->root_menu->flags.mapped) {
1661 menu = scr->root_menu;
1662 if (!menu->flags.buttoned) {
1663 wMenuUnmap(menu);
1664 } else {
1665 wRaiseFrame(menu->frame->core);
1667 if (keyboard)
1668 wMenuMapAt(menu, 0, 0, True);
1669 else
1670 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1672 return;
1675 definition = WDRootMenu->dictionary;
1678 definition = PLGetDomain(domain);
1680 if (definition) {
1681 if (WMIsPLArray(definition)) {
1682 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1683 menu = configureMenu(scr, definition, True);
1684 if (menu)
1685 menu->timestamp = WDRootMenu->timestamp;
1687 } else
1688 menu = NULL;
1689 } else {
1690 menu = configureMenu(scr, definition, True);
1694 if (!menu) {
1695 /* menu hasn't changed or could not be read */
1696 if (!scr->root_menu) {
1697 wMessageDialog(scr, _("Error"),
1698 _("The applications menu could not be loaded. "
1699 "Look at the console output for a detailed "
1700 "description of the errors."), _("OK"), NULL, NULL);
1702 menu = makeDefaultMenu(scr);
1703 scr->root_menu = menu;
1705 menu = scr->root_menu;
1706 } else {
1707 /* new root menu */
1708 if (scr->root_menu) {
1709 wMenuDestroy(scr->root_menu, True);
1711 scr->root_menu = menu;
1713 if (menu) {
1714 int newx, newy;
1716 if (keyboard && x == 0 && y == 0) {
1717 newx = newy = 0;
1718 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1719 newx = x - menu->frame->core->width / 2;
1720 newy = y - menu->frame->core->height / 2;
1721 } else {
1722 newx = x - menu->frame->core->width / 2;
1723 newy = y;
1725 wMenuMapAt(menu, newx, newy, keyboard);
1728 if (scr->flags.root_menu_changed_shortcuts)
1729 rebindKeygrabs(scr);