Avoid memory leak on error path in constructMenu.
[wmaker-crm.git] / src / rootmenu.c
blobf9b51ab2f4f73e02028e6701b09cdd599958b326
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 {
217 int r, oldSaveSessionFlag;
219 oldSaveSessionFlag = wPreferences.save_session_on_exit;
221 r = wExitDialog(menu->frame->screen_ptr,
222 _("Kill X session"),
223 _("Kill Window System session?\n"
224 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
225 if (r == WAPRDefault) {
226 result = R_KILL;
227 } else if (r == WAPRAlternate) {
228 /* Don't modify the "save session on exit" flag if the
229 * user canceled the operation. */
230 wPreferences.save_session_on_exit = oldSaveSessionFlag;
235 if (result != R_CANCEL) {
237 Shutdown(WSKillMode);
240 #undef R_CLOSE
241 #undef R_CANCEL
242 #undef R_KILL
243 inside = 0;
246 static void restartCommand(WMenu * menu, WMenuEntry * entry)
248 Shutdown(WSRestartPreparationMode);
249 Restart((char *)entry->clientdata, False);
250 Restart(NULL, True);
253 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
255 wRefreshDesktop(menu->frame->screen_ptr);
258 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
260 wArrangeIcons(menu->frame->screen_ptr, True);
263 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
265 wShowAllWindows(menu->frame->screen_ptr);
268 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
270 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
273 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
275 if (!wPreferences.save_session_on_exit)
276 wSessionSaveState(menu->frame->screen_ptr);
278 wScreenSaveState(menu->frame->screen_ptr);
281 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
283 wSessionClearState(menu->frame->screen_ptr);
284 wScreenSaveState(menu->frame->screen_ptr);
287 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
289 wShowInfoPanel(menu->frame->screen_ptr);
292 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
294 wShowLegalPanel(menu->frame->screen_ptr);
297 /********************************************************************/
299 static char * getLocalizedMenuFile(char *menu)
301 char *buffer, *ptr, *locale;
302 int len;
304 if (!Locale)
305 return NULL;
307 len = strlen(menu) + strlen(Locale) + 8;
308 buffer = wmalloc(len);
310 /* try menu.locale_name */
311 snprintf(buffer, len, "%s.%s", menu, Locale);
312 if (access(buffer, F_OK) == 0)
313 return buffer;
315 /* position of locale in our buffer */
316 locale = buffer + strlen(menu) + 1;
318 /* check if it is in the form aa_bb.encoding and check for aa_bb */
319 ptr = strchr(locale, '.');
320 if (ptr) {
321 *ptr = 0;
322 if (access(buffer, F_OK) == 0)
323 return buffer;
326 /* now check for aa */
327 ptr = strchr(locale, '_');
328 if (ptr) {
329 *ptr = 0;
330 if (access(buffer, F_OK) == 0)
331 return buffer;
334 wfree(buffer);
336 return NULL;
339 Bool wRootMenuPerformShortcut(XEvent * event)
341 WScreen *scr = wScreenForRootWindow(event->xkey.root);
342 Shortcut *ptr;
343 int modifiers;
344 int done = 0;
346 /* ignore CapsLock */
347 modifiers = event->xkey.state & ValidModMask;
349 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
350 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
351 continue;
353 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
354 (*ptr->entry->callback) (ptr->menu, ptr->entry);
355 done = True;
359 return done;
362 void wRootMenuBindShortcuts(Window window)
364 Shortcut *ptr;
366 ptr = shortcutList;
367 while (ptr) {
368 if (ptr->modifier != AnyModifier) {
369 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
370 window, True, GrabModeAsync, GrabModeAsync);
371 #ifdef NUMLOCK_HACK
372 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
373 #endif
375 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
376 ptr = ptr->next;
380 static void rebindKeygrabs(WScreen * scr)
382 WWindow *wwin;
384 wwin = scr->focused_window;
386 while (wwin != NULL) {
387 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
389 if (!WFLAGP(wwin, no_bind_keys)) {
390 wWindowSetKeyGrabs(wwin);
392 wwin = wwin->prev;
396 static void removeShortcutsForMenu(WMenu * menu)
398 Shortcut *ptr, *tmp;
399 Shortcut *newList = NULL;
401 ptr = shortcutList;
402 while (ptr != NULL) {
403 tmp = ptr->next;
404 if (ptr->menu == menu) {
405 wfree(ptr);
406 } else {
407 ptr->next = newList;
408 newList = ptr;
410 ptr = tmp;
412 shortcutList = newList;
413 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
416 static Bool addShortcut(char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
418 Shortcut *ptr;
419 KeySym ksym;
420 char *k;
421 char buf[MAX_SHORTCUT_LENGTH], *b;
423 ptr = wmalloc(sizeof(Shortcut));
425 wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
426 b = (char *)buf;
428 /* get modifiers */
429 ptr->modifier = 0;
430 while ((k = strchr(b, '+')) != NULL) {
431 int mod;
433 *k = 0;
434 mod = wXModifierFromKey(b);
435 if (mod < 0) {
436 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
437 wfree(ptr);
438 return False;
440 ptr->modifier |= mod;
442 b = k + 1;
445 /* get key */
446 ksym = XStringToKeysym(b);
448 if (ksym == NoSymbol) {
449 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
450 file, shortcutDefinition, entry->text);
451 wfree(ptr);
452 return False;
455 ptr->keycode = XKeysymToKeycode(dpy, ksym);
456 if (ptr->keycode == 0) {
457 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
458 shortcutDefinition, entry->text);
459 wfree(ptr);
460 return False;
463 ptr->menu = menu;
464 ptr->entry = entry;
466 ptr->next = shortcutList;
467 shortcutList = ptr;
469 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
471 return True;
474 static char *next_token(char *line, char **next)
476 char *tmp, c;
477 char *ret;
479 *next = NULL;
480 while (*line == ' ' || *line == '\t')
481 line++;
483 tmp = line;
485 if (*tmp == '"') {
486 tmp++;
487 line++;
488 while (*tmp != 0 && *tmp != '"')
489 tmp++;
490 if (*tmp != '"') {
491 wwarning(_("%s: unmatched '\"' in menu file"), line);
492 return NULL;
494 } else {
495 do {
496 if (*tmp == '\\')
497 tmp++;
499 if (*tmp != 0)
500 tmp++;
502 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
505 c = *tmp;
506 *tmp = 0;
507 ret = wstrdup(line);
508 *tmp = c;
510 if (c == 0)
511 return ret;
512 else
513 tmp++;
515 /* skip blanks */
516 while (*tmp == ' ' || *tmp == '\t')
517 tmp++;
519 if (*tmp != 0)
520 *next = tmp;
522 return ret;
525 static void separateCommand(char *line, char ***file, char **command)
527 char *token, *tmp = line;
528 WMArray *array = WMCreateArray(4);
529 int count, i;
531 *file = NULL;
532 *command = NULL;
533 do {
534 token = next_token(tmp, &tmp);
535 if (token) {
536 if (strcmp(token, "WITH") == 0) {
537 if (tmp != NULL && *tmp != 0)
538 *command = wstrdup(tmp);
539 else
540 wwarning(_("%s: missing command"), line);
541 break;
543 WMAddToArray(array, token);
545 } while (token != NULL && tmp != NULL);
547 count = WMGetArrayItemCount(array);
548 if (count > 0) {
549 *file = wmalloc(sizeof(char *) * (count + 1));
550 (*file)[count] = NULL;
551 for (i = 0; i < count; i++) {
552 (*file)[i] = WMGetFromArray(array, i);
555 WMFreeArray(array);
558 static WMenu *constructPLMenu(WScreen *screen, char *path)
560 WMPropList *pl = NULL;
561 WMenu *menu = NULL;
563 if (!path)
564 return NULL;
566 pl = WMReadPropListFromFile(path);
567 if (!pl)
568 return NULL;
570 menu = configureMenu(screen, pl, False);
571 if (!menu)
572 return NULL;
574 menu->on_destroy = removeShortcutsForMenu;
575 return menu;
578 static void constructMenu(WMenu * menu, WMenuEntry * entry)
580 WMenu *submenu;
581 struct stat stat_buf;
582 char **path;
583 char *cmd;
584 char *lpath = NULL;
585 int i, first = -1;
586 time_t last = 0;
588 separateCommand((char *)entry->clientdata, &path, &cmd);
589 if (path == NULL || *path == NULL || **path == 0) {
590 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
591 if (cmd)
592 wfree(cmd);
593 return;
596 if (path[0][0] == '|') {
597 /* pipe menu */
599 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
600 /* parse pipe */
602 submenu = readMenuPipe(menu->frame->screen_ptr, path);
604 if (submenu != NULL) {
605 if (path[0][1] == '|')
606 submenu->timestamp = 0;
607 else
608 submenu->timestamp = 1; /* there's no automatic reloading */
610 } else {
611 submenu = NULL;
614 } else {
616 /* try interpreting path as a proplist file */
617 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
618 /* if unsuccessful, try it as an old-style file */
619 if (!submenu) {
621 i = 0;
622 while (path[i] != NULL) {
623 char *tmp;
625 if (strcmp(path[i], "-noext") == 0) {
626 i++;
627 continue;
630 tmp = wexpandpath(path[i]);
631 wfree(path[i]);
632 lpath = getLocalizedMenuFile(tmp);
633 if (lpath) {
634 wfree(tmp);
635 path[i] = lpath;
636 lpath = NULL;
637 } else {
638 path[i] = tmp;
641 if (stat(path[i], &stat_buf) == 0) {
642 if (last < stat_buf.st_mtime)
643 last = stat_buf.st_mtime;
644 if (first < 0)
645 first = i;
646 } else {
647 werror(_("%s:could not stat menu"), path[i]);
648 /*goto finish; */
651 i++;
654 if (first < 0) {
655 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
656 goto finish;
658 stat(path[first], &stat_buf);
659 if (!menu->cascades[entry->cascade]
660 || menu->cascades[entry->cascade]->timestamp < last) {
662 if (S_ISDIR(stat_buf.st_mode)) {
663 /* menu directory */
664 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
665 if (submenu)
666 submenu->timestamp = last;
667 } else if (S_ISREG(stat_buf.st_mode)) {
668 /* menu file */
670 if (cmd || path[1])
671 wwarning(_("too many parameters in OPEN_MENU: %s"),
672 (char *)entry->clientdata);
674 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
675 if (submenu)
676 submenu->timestamp = stat_buf.st_mtime;
677 } else {
678 submenu = NULL;
680 } else {
681 submenu = NULL;
686 if (submenu) {
687 wMenuEntryRemoveCascade(menu, entry);
688 wMenuEntrySetCascade(menu, entry, submenu);
691 finish:
692 i = 0;
693 while (path[i] != NULL)
694 wfree(path[i++]);
695 wfree(path);
696 if (cmd)
697 wfree(cmd);
700 static void cleanupWorkspaceMenu(WMenu * menu)
702 if (menu->frame->screen_ptr->workspace_menu == menu)
703 menu->frame->screen_ptr->workspace_menu = NULL;
706 static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
708 WMenu *wsmenu;
709 WMenuEntry *entry;
711 if (scr->flags.added_workspace_menu) {
712 wwarning(_
713 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
714 return NULL;
715 } else {
716 scr->flags.added_workspace_menu = 1;
718 wsmenu = wWorkspaceMenuMake(scr, True);
719 wsmenu->on_destroy = cleanupWorkspaceMenu;
721 scr->workspace_menu = wsmenu;
722 entry = wMenuAddCallback(menu, title, NULL, NULL);
723 wMenuEntrySetCascade(menu, entry, wsmenu);
725 wWorkspaceMenuUpdate(scr, wsmenu);
727 return entry;
730 static void cleanupWindowsMenu(WMenu * menu)
732 if (menu->frame->screen_ptr->switch_menu == menu)
733 menu->frame->screen_ptr->switch_menu = NULL;
736 static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
738 WMenu *wwmenu;
739 WWindow *wwin;
740 WMenuEntry *entry;
742 if (scr->flags.added_windows_menu) {
743 wwarning(_
744 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
745 return NULL;
746 } else {
747 scr->flags.added_windows_menu = 1;
749 wwmenu = wMenuCreate(scr, _("Window List"), False);
750 wwmenu->on_destroy = cleanupWindowsMenu;
751 scr->switch_menu = wwmenu;
752 wwin = scr->focused_window;
753 while (wwin) {
754 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
756 wwin = wwin->prev;
758 entry = wMenuAddCallback(menu, title, NULL, NULL);
759 wMenuEntrySetCascade(menu, entry, wwmenu);
761 return entry;
764 static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
765 char *params, char *file_name)
767 WScreen *scr;
768 WMenuEntry *entry = NULL;
769 Bool shortcutOk = False;
771 if (!menu)
772 return NULL;
773 scr = menu->frame->screen_ptr;
774 if (strcmp(command, "OPEN_MENU") == 0) {
775 if (!params) {
776 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
777 } else {
778 WMenu *dummy;
779 char *path;
781 path = wfindfile(DEF_CONFIG_PATHS, params);
782 if (!path) {
783 path = wstrdup(params);
785 dummy = wMenuCreate(scr, title, False);
786 dummy->on_destroy = removeShortcutsForMenu;
787 entry = wMenuAddCallback(menu, title, constructMenu, path);
788 entry->free_cdata = wfree;
789 wMenuEntrySetCascade(menu, entry, dummy);
791 } else if (strcmp(command, "EXEC") == 0) {
792 if (!params)
793 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
794 else {
795 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
796 entry->free_cdata = wfree;
797 shortcutOk = True;
799 } else if (strcmp(command, "SHEXEC") == 0) {
800 if (!params)
801 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
802 else {
803 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
804 entry->free_cdata = wfree;
805 shortcutOk = True;
807 } else if (strcmp(command, "EXIT") == 0) {
809 if (params && strcmp(params, "QUICK") == 0)
810 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
811 else
812 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
814 shortcutOk = True;
815 } else if (strcmp(command, "SHUTDOWN") == 0) {
817 if (params && strcmp(params, "QUICK") == 0)
818 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
819 else
820 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
822 shortcutOk = True;
823 } else if (strcmp(command, "REFRESH") == 0) {
824 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
826 shortcutOk = True;
827 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
828 entry = addWorkspaceMenu(scr, menu, title);
830 shortcutOk = True;
831 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
832 entry = addWindowsMenu(scr, menu, title);
834 shortcutOk = True;
835 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
836 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
838 shortcutOk = True;
839 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
840 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
842 shortcutOk = True;
843 } else if (strcmp(command, "SHOW_ALL") == 0) {
844 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
846 shortcutOk = True;
847 } else if (strcmp(command, "RESTART") == 0) {
848 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
849 entry->free_cdata = wfree;
850 shortcutOk = True;
851 } else if (strcmp(command, "SAVE_SESSION") == 0) {
852 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
854 shortcutOk = True;
855 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
856 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
857 shortcutOk = True;
858 } else if (strcmp(command, "INFO_PANEL") == 0) {
859 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
860 shortcutOk = True;
861 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
862 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
863 shortcutOk = True;
864 } else {
865 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
867 return NULL;
870 if (shortcut && entry) {
871 if (!shortcutOk) {
872 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
873 } else {
874 if (addShortcut(file_name, shortcut, menu, entry)) {
876 entry->rtext = GetShortcutString(shortcut);
878 entry->rtext = wstrdup(shortcut);
884 return entry;
887 /******************* Menu Configuration From File *******************/
889 static void separateline(char *line, char *title, char *command, char *parameter, char *shortcut)
891 int l, i;
893 l = strlen(line);
895 *title = 0;
896 *command = 0;
897 *parameter = 0;
898 *shortcut = 0;
899 /* get the title */
900 while (isspace(*line) && (*line != 0))
901 line++;
902 if (*line == '"') {
903 line++;
904 i = 0;
905 while (line[i] != '"' && (line[i] != 0))
906 i++;
907 if (line[i] != '"')
908 return;
909 } else {
910 i = 0;
911 while (!isspace(line[i]) && (line[i] != 0))
912 i++;
914 strncpy(title, line, i);
915 title[i++] = 0;
916 line += i;
918 /* get the command or shortcut keyword */
919 while (isspace(*line) && (*line != 0))
920 line++;
921 if (*line == 0)
922 return;
923 i = 0;
924 while (!isspace(line[i]) && (line[i] != 0))
925 i++;
926 strncpy(command, line, i);
927 command[i++] = 0;
928 line += i;
930 if (strcmp(command, "SHORTCUT") == 0) {
931 /* get the shortcut key */
932 while (isspace(*line) && (*line != 0))
933 line++;
934 if (*line == '"') {
935 line++;
936 i = 0;
937 while (line[i] != '"' && (line[i] != 0))
938 i++;
939 if (line[i] != '"')
940 return;
941 } else {
942 i = 0;
943 while (!isspace(line[i]) && (line[i] != 0))
944 i++;
946 strncpy(shortcut, line, i);
947 shortcut[i++] = 0;
948 line += i;
950 *command = 0;
952 /* get the command */
953 while (isspace(*line) && (*line != 0))
954 line++;
955 if (*line == 0)
956 return;
957 i = 0;
958 while (!isspace(line[i]) && (line[i] != 0))
959 i++;
960 strncpy(command, line, i);
961 command[i++] = 0;
962 line += i;
965 /* get the parameters */
966 while (isspace(*line) && (*line != 0))
967 line++;
968 if (*line == 0)
969 return;
971 if (*line == '"') {
972 line++;
973 l = 0;
974 while (line[l] != 0 && line[l] != '"') {
975 parameter[l] = line[l];
976 l++;
978 parameter[l] = 0;
979 return;
982 l = strlen(line);
983 while (isspace(line[l]) && (l > 0))
984 l--;
985 strncpy(parameter, line, l);
986 parameter[l] = 0;
989 static WMenu *parseCascade(WScreen * scr, WMenu * menu, FILE * file, char *file_name)
991 char linebuf[MAXLINE];
992 char elinebuf[MAXLINE];
993 char title[MAXLINE];
994 char command[MAXLINE];
995 char shortcut[MAXLINE];
996 char params[MAXLINE];
997 char *line;
999 while (!feof(file)) {
1000 int lsize, ok;
1002 ok = 0;
1003 fgets(linebuf, MAXLINE, file);
1004 line = wtrimspace(linebuf);
1005 lsize = strlen(line);
1006 do {
1007 if (line[lsize - 1] == '\\') {
1008 char *line2;
1009 int lsize2;
1010 fgets(elinebuf, MAXLINE, file);
1011 line2 = wtrimspace(elinebuf);
1012 lsize2 = strlen(line2);
1013 if (lsize2 + lsize > MAXLINE) {
1014 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1015 file_name, line);
1016 ok = 2;
1017 } else {
1018 line[lsize - 1] = 0;
1019 lsize += lsize2 - 1;
1020 strcat(line, line2);
1022 } else {
1023 ok = 1;
1025 } while (!ok && !feof(file));
1026 if (ok == 2)
1027 continue;
1029 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1030 continue;
1032 separateline(line, title, command, params, shortcut);
1034 if (!command[0]) {
1035 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1036 goto error;
1039 if (strcasecmp(command, "MENU") == 0) {
1040 WMenu *cascade;
1042 /* start submenu */
1044 cascade = wMenuCreate(scr, M_(title), False);
1045 cascade->on_destroy = removeShortcutsForMenu;
1046 if (parseCascade(scr, cascade, file, file_name) == NULL) {
1047 wMenuDestroy(cascade, True);
1048 } else {
1049 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
1051 } else if (strcasecmp(command, "END") == 0) {
1052 /* end of menu */
1053 return menu;
1055 } else {
1056 /* normal items */
1057 addMenuEntry(menu, M_(title), shortcut[0] ? shortcut : NULL, command,
1058 params[0] ? params : NULL, file_name);
1062 wwarning(_("%s:syntax error in menu file:END declaration missing"), file_name);
1063 return menu;
1065 error:
1066 return menu;
1069 static WMenu *readMenuFile(WScreen * scr, char *file_name)
1071 WMenu *menu = NULL;
1072 FILE *file = NULL;
1073 char linebuf[MAXLINE];
1074 char title[MAXLINE];
1075 char shortcut[MAXLINE];
1076 char command[MAXLINE];
1077 char params[MAXLINE];
1078 char *line;
1079 #ifdef USECPP
1080 char *args;
1081 int cpp = 0;
1082 #endif
1084 #ifdef USECPP
1085 if (!wPreferences.flags.nocpp) {
1086 args = MakeCPPArgs(file_name);
1087 if (!args) {
1088 wwarning(_("could not make arguments for menu file preprocessor"));
1089 } else {
1090 snprintf(command, sizeof(command), "%s %s %s", CPP_PATH, args, file_name);
1091 wfree(args);
1092 file = popen(command, "r");
1093 if (!file) {
1094 werror(_("%s:could not open/preprocess menu file"), file_name);
1095 } else {
1096 cpp = 1;
1100 #endif /* USECPP */
1102 if (!file) {
1103 file = fopen(file_name, "rb");
1104 if (!file) {
1105 werror(_("%s:could not open menu file"), file_name);
1106 return NULL;
1110 while (!feof(file)) {
1111 if (!fgets(linebuf, MAXLINE, file))
1112 break;
1113 line = wtrimspace(linebuf);
1114 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1115 continue;
1117 separateline(line, title, command, params, shortcut);
1119 if (!command[0]) {
1120 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1121 break;
1123 if (strcasecmp(command, "MENU") == 0) {
1124 menu = wMenuCreate(scr, M_(title), True);
1125 menu->on_destroy = removeShortcutsForMenu;
1126 if (!parseCascade(scr, menu, file, file_name)) {
1127 wMenuDestroy(menu, True);
1129 break;
1130 } else {
1131 wwarning(_("%s:invalid menu file. MENU command is missing"), file_name);
1132 break;
1136 #ifdef USECPP
1137 if (cpp) {
1138 if (pclose(file) == -1) {
1139 werror(_("error reading preprocessed menu data"));
1141 } else {
1142 fclose(file);
1144 #else
1145 fclose(file);
1146 #endif
1148 return menu;
1151 /************ Menu Configuration From Pipe *************/
1153 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1155 WMenu *menu = NULL;
1156 FILE *file = NULL;
1157 char linebuf[MAXLINE];
1158 char title[MAXLINE];
1159 char command[MAXLINE];
1160 char params[MAXLINE];
1161 char shortcut[MAXLINE];
1162 char *line;
1163 char *filename;
1164 char flat_file[MAXLINE];
1165 int i;
1166 #ifdef USECPP
1167 char *args;
1168 #endif
1170 flat_file[0] = '\0';
1172 for (i = 0; file_name[i] != NULL; i++) {
1173 strcat(flat_file, file_name[i]);
1174 strcat(flat_file, " ");
1176 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1178 #ifdef USECPP
1179 if (!wPreferences.flags.nocpp) {
1180 args = MakeCPPArgs(filename);
1181 if (!args) {
1182 wwarning(_("could not make arguments for menu file preprocessor"));
1183 } else {
1184 snprintf(command, sizeof(command), "%s | %s %s", filename, CPP_PATH, args);
1186 wfree(args);
1187 file = popen(command, "r");
1188 if (!file) {
1189 werror(_("%s:could not open/preprocess menu file"), filename);
1193 #endif /* USECPP */
1195 if (!file) {
1196 file = popen(filename, "rb");
1198 if (!file) {
1199 werror(_("%s:could not open menu file"), filename);
1200 return NULL;
1204 while (!feof(file)) {
1205 if (!fgets(linebuf, MAXLINE, file))
1206 break;
1207 line = wtrimspace(linebuf);
1208 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1209 continue;
1211 separateline(line, title, command, params, shortcut);
1213 if (!command[0]) {
1214 wwarning(_("%s:missing command in menu config: %s"), filename, line);
1215 break;
1217 if (strcasecmp(command, "MENU") == 0) {
1218 menu = wMenuCreate(scr, M_(title), True);
1219 menu->on_destroy = removeShortcutsForMenu;
1220 if (!parseCascade(scr, menu, file, filename)) {
1221 wMenuDestroy(menu, True);
1223 break;
1224 } else {
1225 wwarning(_("%s:no title given for the root menu"), filename);
1226 break;
1230 pclose(file);
1232 return menu;
1235 typedef struct {
1236 char *name;
1237 int index;
1238 } dir_data;
1240 static int myCompare(const void *d1, const void *d2)
1242 dir_data *p1 = *(dir_data **) d1;
1243 dir_data *p2 = *(dir_data **) d2;
1245 return strcmp(p1->name, p2->name);
1248 /************ Menu Configuration From Directory *************/
1250 static Bool isFilePackage(char *file)
1252 int l;
1254 /* check if the extension indicates this file is a
1255 * file package. For now, only recognize .themed */
1257 l = strlen(file);
1259 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1260 return True;
1261 } else {
1262 return False;
1266 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
1268 DIR *dir;
1269 struct dirent *dentry;
1270 struct stat stat_buf;
1271 WMenu *menu = NULL;
1272 char *buffer;
1273 WMArray *dirs = NULL, *files = NULL;
1274 WMArrayIterator iter;
1275 int length, i, have_space = 0;
1276 dir_data *data;
1277 int stripExtension = 0;
1279 dirs = WMCreateArray(16);
1280 files = WMCreateArray(16);
1282 i = 0;
1283 while (path[i] != NULL) {
1284 if (strcmp(path[i], "-noext") == 0) {
1285 stripExtension = 1;
1286 i++;
1287 continue;
1290 dir = opendir(path[i]);
1291 if (!dir) {
1292 i++;
1293 continue;
1296 while ((dentry = readdir(dir))) {
1298 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1299 continue;
1301 if (dentry->d_name[0] == '.')
1302 continue;
1304 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1305 if (!buffer) {
1306 werror(_("out of memory while constructing directory menu %s"), path[i]);
1307 break;
1310 strcpy(buffer, path[i]);
1311 strcat(buffer, "/");
1312 strcat(buffer, dentry->d_name);
1314 if (stat(buffer, &stat_buf) != 0) {
1315 werror(_("%s:could not stat file \"%s\" in menu directory"),
1316 path[i], dentry->d_name);
1317 } else {
1318 Bool isFilePack = False;
1320 data = NULL;
1321 if (S_ISDIR(stat_buf.st_mode)
1322 && !(isFilePack = isFilePackage(dentry->d_name))) {
1324 /* access always returns success for user root */
1325 if (access(buffer, X_OK) == 0) {
1326 /* Directory is accesible. Add to directory list */
1328 data = (dir_data *) wmalloc(sizeof(dir_data));
1329 data->name = wstrdup(dentry->d_name);
1330 data->index = i;
1332 WMAddToArray(dirs, data);
1334 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1335 /* Hack because access always returns X_OK success for user root */
1336 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1337 if ((command != NULL && access(buffer, R_OK) == 0) ||
1338 (command == NULL && access(buffer, X_OK) == 0 &&
1339 (stat_buf.st_mode & S_IXANY))) {
1341 data = (dir_data *) wmalloc(sizeof(dir_data));
1342 data->name = wstrdup(dentry->d_name);
1343 data->index = i;
1345 WMAddToArray(files, data);
1349 free(buffer);
1352 closedir(dir);
1353 i++;
1356 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1357 WMFreeArray(dirs);
1358 WMFreeArray(files);
1359 return NULL;
1362 WMSortArray(dirs, myCompare);
1363 WMSortArray(files, myCompare);
1365 menu = wMenuCreate(scr, M_(title), False);
1366 menu->on_destroy = removeShortcutsForMenu;
1368 WM_ITERATE_ARRAY(dirs, data, iter) {
1369 /* New directory. Use same OPEN_MENU command that was used
1370 * for the current directory. */
1371 length = strlen(path[data->index]) + strlen(data->name) + 6;
1372 if (stripExtension)
1373 length += 7;
1374 if (command)
1375 length += strlen(command) + 6;
1376 buffer = malloc(length);
1377 if (!buffer) {
1378 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1379 break;
1382 buffer[0] = '\0';
1383 if (stripExtension)
1384 strcat(buffer, "-noext ");
1386 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1388 if (have_space)
1389 strcat(buffer, "\"");
1390 strcat(buffer, path[data->index]);
1392 strcat(buffer, "/");
1393 strcat(buffer, data->name);
1394 if (have_space)
1395 strcat(buffer, "\"");
1396 if (command) {
1397 strcat(buffer, " WITH ");
1398 strcat(buffer, command);
1401 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1403 wfree(buffer);
1404 if (data->name)
1405 wfree(data->name);
1406 wfree(data);
1409 WM_ITERATE_ARRAY(files, data, iter) {
1410 /* executable: add as entry */
1411 length = strlen(path[data->index]) + strlen(data->name) + 6;
1412 if (command)
1413 length += strlen(command);
1415 buffer = malloc(length);
1416 if (!buffer) {
1417 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1418 break;
1421 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1422 if (command != NULL) {
1423 strcpy(buffer, command);
1424 strcat(buffer, " ");
1425 if (have_space)
1426 strcat(buffer, "\"");
1427 strcat(buffer, path[data->index]);
1428 } else {
1429 if (have_space) {
1430 buffer[0] = '"';
1431 buffer[1] = 0;
1432 strcat(buffer, path[data->index]);
1433 } else {
1434 strcpy(buffer, path[data->index]);
1437 strcat(buffer, "/");
1438 strcat(buffer, data->name);
1439 if (have_space)
1440 strcat(buffer, "\"");
1442 if (stripExtension) {
1443 char *ptr = strrchr(data->name, '.');
1444 if (ptr && ptr != data->name)
1445 *ptr = 0;
1447 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1449 wfree(buffer);
1450 if (data->name)
1451 wfree(data->name);
1452 wfree(data);
1455 WMFreeArray(files);
1456 WMFreeArray(dirs);
1458 return menu;
1461 /************ Menu Configuration From WMRootMenu *************/
1463 static WMenu *makeDefaultMenu(WScreen * scr)
1465 WMenu *menu = NULL;
1467 menu = wMenuCreate(scr, _("Commands"), True);
1468 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1469 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1470 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1471 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1472 return menu;
1476 *----------------------------------------------------------------------
1477 * configureMenu--
1478 * Reads root menu configuration from defaults database.
1480 *----------------------------------------------------------------------
1482 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1484 WMenu *menu = NULL;
1485 WMPropList *elem;
1486 int i, count;
1487 WMPropList *title, *command, *params;
1488 char *tmp, *mtitle;
1490 if (WMIsPLString(definition)) {
1491 struct stat stat_buf;
1492 char *path = NULL;
1493 Bool menu_is_default = False;
1495 /* menu definition is a string. Probably a path, so parse the file */
1497 tmp = wexpandpath(WMGetFromPLString(definition));
1499 path = getLocalizedMenuFile(tmp);
1501 if (!path)
1502 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1504 if (!path) {
1505 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1506 menu_is_default = True;
1509 if (!path) {
1510 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1511 wfree(tmp);
1512 return NULL;
1515 if (stat(path, &stat_buf) < 0) {
1516 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1517 wfree(path);
1518 wfree(tmp);
1519 return NULL;
1522 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1523 /* if the pointer in WMRootMenu has changed */
1524 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1526 if (menu_is_default) {
1527 wwarning(_
1528 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1529 path);
1532 menu = readMenuFile(scr, path);
1533 if (menu)
1534 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1535 } else {
1536 menu = NULL;
1538 wfree(path);
1539 wfree(tmp);
1541 return menu;
1544 count = WMGetPropListItemCount(definition);
1545 if (count == 0)
1546 return NULL;
1548 elem = WMGetFromPLArray(definition, 0);
1549 if (!WMIsPLString(elem)) {
1550 tmp = WMGetPropListDescription(elem, False);
1551 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1552 wfree(tmp);
1553 return NULL;
1555 mtitle = WMGetFromPLString(elem);
1557 menu = wMenuCreate(scr, M_(mtitle), False);
1558 menu->on_destroy = removeShortcutsForMenu;
1560 #ifdef GLOBAL_SUBMENU_FILE
1561 if (includeGlobals) {
1562 WMenu *submenu;
1563 WMenuEntry *mentry;
1565 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1567 if (submenu) {
1568 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1569 wMenuEntrySetCascade(menu, mentry, submenu);
1572 #endif
1574 for (i = 1; i < count; i++) {
1575 elem = WMGetFromPLArray(definition, i);
1576 #if 0
1577 if (WMIsPLString(elem)) {
1578 char *file;
1580 file = WMGetFromPLString(elem);
1583 #endif
1584 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1585 goto error;
1587 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1588 WMenu *submenu;
1589 WMenuEntry *mentry;
1591 /* submenu */
1592 submenu = configureMenu(scr, elem, True);
1593 if (submenu) {
1594 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1595 wMenuEntrySetCascade(menu, mentry, submenu);
1597 } else {
1598 int idx = 0;
1599 WMPropList *shortcut;
1600 /* normal entry */
1602 title = WMGetFromPLArray(elem, idx++);
1603 shortcut = WMGetFromPLArray(elem, idx++);
1604 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1605 shortcut = WMGetFromPLArray(elem, idx++);
1606 command = WMGetFromPLArray(elem, idx++);
1607 } else {
1608 command = shortcut;
1609 shortcut = NULL;
1611 params = WMGetFromPLArray(elem, idx++);
1613 if (!title || !command)
1614 goto error;
1616 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1617 shortcut ? WMGetFromPLString(shortcut) : NULL,
1618 WMGetFromPLString(command),
1619 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1621 continue;
1623 error:
1624 tmp = WMGetPropListDescription(elem, False);
1625 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1626 wfree(tmp);
1629 return menu;
1633 *----------------------------------------------------------------------
1634 * OpenRootMenu--
1635 * Opens the root menu, parsing the menu configuration from the
1636 * defaults database.
1637 * If the menu is already mapped and is not sticked to the
1638 * root window, it will be unmapped.
1640 * Side effects:
1641 * The menu may be remade.
1643 * Notes:
1644 * Construction of OPEN_MENU entries are delayed to the moment the
1645 * user map's them.
1646 *----------------------------------------------------------------------
1648 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1650 WMenu *menu = NULL;
1651 WMPropList *definition;
1653 static WMPropList *domain=NULL;
1655 if (!domain) {
1656 domain = WMCreatePLString("WMRootMenu");
1660 scr->flags.root_menu_changed_shortcuts = 0;
1661 scr->flags.added_workspace_menu = 0;
1662 scr->flags.added_windows_menu = 0;
1664 if (scr->root_menu && scr->root_menu->flags.mapped) {
1665 menu = scr->root_menu;
1666 if (!menu->flags.buttoned) {
1667 wMenuUnmap(menu);
1668 } else {
1669 wRaiseFrame(menu->frame->core);
1671 if (keyboard)
1672 wMenuMapAt(menu, 0, 0, True);
1673 else
1674 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1676 return;
1679 definition = WDRootMenu->dictionary;
1682 definition = PLGetDomain(domain);
1684 if (definition) {
1685 if (WMIsPLArray(definition)) {
1686 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1687 menu = configureMenu(scr, definition, True);
1688 if (menu)
1689 menu->timestamp = WDRootMenu->timestamp;
1691 } else
1692 menu = NULL;
1693 } else {
1694 menu = configureMenu(scr, definition, True);
1698 if (!menu) {
1699 /* menu hasn't changed or could not be read */
1700 if (!scr->root_menu) {
1701 wMessageDialog(scr, _("Error"),
1702 _("The applications menu could not be loaded. "
1703 "Look at the console output for a detailed "
1704 "description of the errors."), _("OK"), NULL, NULL);
1706 menu = makeDefaultMenu(scr);
1707 scr->root_menu = menu;
1709 menu = scr->root_menu;
1710 } else {
1711 /* new root menu */
1712 if (scr->root_menu) {
1713 wMenuDestroy(scr->root_menu, True);
1715 scr->root_menu = menu;
1717 if (menu) {
1718 int newx, newy;
1720 if (keyboard && x == 0 && y == 0) {
1721 newx = newy = 0;
1722 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1723 newx = x - menu->frame->core->width / 2;
1724 newy = y - menu->frame->core->height / 2;
1725 } else {
1726 newx = x - menu->frame->core->width / 2;
1727 newy = y;
1729 wMenuMapAt(menu, newx, newy, keyboard);
1732 if (scr->flags.root_menu_changed_shortcuts)
1733 rebindKeygrabs(scr);