Moving header functions to main.h
[wmaker-crm.git] / src / rootmenu.c
blob6c4bb2d324e02a347cbdcd98ce4493186858063f
1 /* rootmenu.c- user defined menu
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "wconfig.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <sys/types.h>
31 #include <string.h>
32 #include <strings.h>
33 #include <ctype.h>
34 #include <time.h>
35 #include <dirent.h>
37 #include <X11/Xlib.h>
38 #include <X11/Xutil.h>
39 #include <X11/Xatom.h>
41 #include "WindowMaker.h"
42 #include "actions.h"
43 #include "menu.h"
44 #include "funcs.h"
45 #include "main.h"
46 #include "dialog.h"
47 #include "keybind.h"
48 #include "stacking.h"
49 #include "workspace.h"
50 #include "defaults.h"
51 #include "framewin.h"
52 #include "session.h"
53 #include "xmodifier.h"
55 #include <WINGs/WUtil.h>
57 #define MAX_SHORTCUT_LENGTH 32
59 extern char *Locale;
60 extern WDDomain *WDRootMenu;
61 extern Cursor wCursor[WCUR_LAST];
62 extern WPreferences wPreferences;
64 static WMenu *readMenuPipe(WScreen * scr, char **file_name);
65 static WMenu *readMenuFile(WScreen * scr, char *file_name);
66 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **file_name, char *command);
67 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals);
69 typedef struct Shortcut {
70 struct Shortcut *next;
72 int modifier;
73 KeyCode keycode;
74 WMenuEntry *entry;
75 WMenu *menu;
76 } Shortcut;
78 static Shortcut *shortcutList = NULL;
81 * Syntax:
82 * # main menu
83 * "Menu Name" MENU
84 * "Title" EXEC command_to_exec -params
85 * "Submenu" MENU
86 * "Title" EXEC command_to_exec -params
87 * "Submenu" END
88 * "Workspaces" WORKSPACE_MENU
89 * "Title" built_in_command
90 * "Quit" EXIT
91 * "Quick Quit" EXIT QUICK
92 * "Menu Name" END
94 * Commands may be preceded by SHORTCUT key
96 * Built-in commands:
98 * INFO_PANEL - shows the Info Panel
99 * LEGAL_PANEL - shows the Legal info panel
100 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
101 * REFRESH - forces the desktop to be repainted
102 * EXIT [QUICK] - exit the window manager [without confirmation]
103 * EXEC <program> - execute an external program
104 * SHEXEC <command> - execute a shell command
105 * WORKSPACE_MENU - places the workspace submenu
106 * ARRANGE_ICONS
107 * RESTART [<window manager>] - restarts the window manager
108 * SHOW_ALL - unhide all windows on workspace
109 * HIDE_OTHERS - hides all windows excep the focused one
110 * OPEN_MENU file - read menu data from file which must be a valid menu file.
111 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
112 * - read menu data from directory(ies) and
113 * eventually precede each with a command.
114 * OPEN_MENU | command
115 * - opens command and uses its stdout to construct and insert
116 * the resulting menu in current position. The output of
117 * command must be a valid menu description.
118 * The space between '|' and command is optional.
119 * || will do the same, but will not cache the contents.
120 * SAVE_SESSION - saves the current state of the desktop, which include
121 * all running applications, all their hints (geometry,
122 * position on screen, workspace they live on, the dock
123 * or clip from where they were launched, and
124 * if minimized, shaded or hidden. Also saves the current
125 * workspace the user is on. All will be restored on every
126 * start of windowmaker until another SAVE_SESSION or
127 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
128 * WindowMaker domain file, then saving is automatically
129 * done on every windowmaker exit, overwriting any
130 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
131 * dock state now.
132 * CLEAR_SESSION - clears any previous saved session. This will not have
133 * any effect if SaveSessionOnExit is True.
137 #define M_QUICK 1
139 /* menu commands */
141 static void execCommand(WMenu * menu, WMenuEntry * entry)
143 char *cmdline;
145 cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
147 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
148 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);
149 XSync(dpy, 0);
151 if (cmdline) {
152 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
153 wfree(cmdline);
155 XUngrabPointer(dpy, CurrentTime);
156 XSync(dpy, 0);
159 static void exitCommand(WMenu * menu, WMenuEntry * entry)
161 static int inside = 0;
162 int result;
164 /* prevent reentrant calls */
165 if (inside)
166 return;
167 inside = 1;
169 #define R_CANCEL 0
170 #define R_EXIT 1
172 result = R_CANCEL;
174 if ((long)entry->clientdata == M_QUICK) {
175 result = R_EXIT;
176 } else {
177 int r, oldSaveSessionFlag;
179 oldSaveSessionFlag = wPreferences.save_session_on_exit;
180 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
181 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
183 if (r == WAPRDefault) {
184 result = R_EXIT;
185 } else if (r == WAPRAlternate) {
186 /* Don't modify the "save session on exit" flag if the
187 * user canceled the operation. */
188 wPreferences.save_session_on_exit = oldSaveSessionFlag;
191 if (result == R_EXIT)
192 Shutdown(WSExitMode);
194 #undef R_EXIT
195 #undef R_CANCEL
196 inside = 0;
199 static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
201 static int inside = 0;
202 int result;
204 /* prevent reentrant calls */
205 if (inside)
206 return;
207 inside = 1;
209 #define R_CANCEL 0
210 #define R_CLOSE 1
211 #define R_KILL 2
213 result = R_CANCEL;
214 if ((long)entry->clientdata == M_QUICK)
215 result = R_CLOSE;
216 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;
234 if (result != R_CANCEL) {
235 Shutdown(WSKillMode);
237 #undef R_CLOSE
238 #undef R_CANCEL
239 #undef R_KILL
240 inside = 0;
243 static void restartCommand(WMenu * menu, WMenuEntry * entry)
245 Shutdown(WSRestartPreparationMode);
246 Restart((char *)entry->clientdata, False);
247 Restart(NULL, True);
250 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
252 wRefreshDesktop(menu->frame->screen_ptr);
255 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
257 wArrangeIcons(menu->frame->screen_ptr, True);
260 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
262 wShowAllWindows(menu->frame->screen_ptr);
265 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
267 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
270 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
272 if (!wPreferences.save_session_on_exit)
273 wSessionSaveState(menu->frame->screen_ptr);
275 wScreenSaveState(menu->frame->screen_ptr);
278 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
280 wSessionClearState(menu->frame->screen_ptr);
281 wScreenSaveState(menu->frame->screen_ptr);
284 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
286 wShowInfoPanel(menu->frame->screen_ptr);
289 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
291 wShowLegalPanel(menu->frame->screen_ptr);
294 /********************************************************************/
296 static char * getLocalizedMenuFile(char *menu)
298 char *buffer, *ptr, *locale;
299 int len;
301 if (!Locale)
302 return NULL;
304 len = strlen(menu) + strlen(Locale) + 8;
305 buffer = wmalloc(len);
307 /* try menu.locale_name */
308 snprintf(buffer, len, "%s.%s", menu, Locale);
309 if (access(buffer, F_OK) == 0)
310 return buffer;
312 /* position of locale in our buffer */
313 locale = buffer + strlen(menu) + 1;
315 /* check if it is in the form aa_bb.encoding and check for aa_bb */
316 ptr = strchr(locale, '.');
317 if (ptr) {
318 *ptr = 0;
319 if (access(buffer, F_OK) == 0)
320 return buffer;
323 /* now check for aa */
324 ptr = strchr(locale, '_');
325 if (ptr) {
326 *ptr = 0;
327 if (access(buffer, F_OK) == 0)
328 return buffer;
331 wfree(buffer);
333 return NULL;
336 Bool wRootMenuPerformShortcut(XEvent * event)
338 WScreen *scr = wScreenForRootWindow(event->xkey.root);
339 Shortcut *ptr;
340 int modifiers;
341 int done = 0;
343 /* ignore CapsLock */
344 modifiers = event->xkey.state & ValidModMask;
346 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
347 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
348 continue;
350 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
351 (*ptr->entry->callback) (ptr->menu, ptr->entry);
352 done = True;
356 return done;
359 void wRootMenuBindShortcuts(Window window)
361 Shortcut *ptr;
363 ptr = shortcutList;
364 while (ptr) {
365 if (ptr->modifier != AnyModifier) {
366 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
367 window, True, GrabModeAsync, GrabModeAsync);
368 #ifdef NUMLOCK_HACK
369 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
370 #endif
372 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
373 ptr = ptr->next;
377 static void rebindKeygrabs(WScreen * scr)
379 WWindow *wwin;
381 wwin = scr->focused_window;
383 while (wwin != NULL) {
384 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
386 if (!WFLAGP(wwin, no_bind_keys)) {
387 wWindowSetKeyGrabs(wwin);
389 wwin = wwin->prev;
393 static void removeShortcutsForMenu(WMenu * menu)
395 Shortcut *ptr, *tmp;
396 Shortcut *newList = NULL;
398 ptr = shortcutList;
399 while (ptr != NULL) {
400 tmp = ptr->next;
401 if (ptr->menu == menu) {
402 wfree(ptr);
403 } else {
404 ptr->next = newList;
405 newList = ptr;
407 ptr = tmp;
409 shortcutList = newList;
410 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
413 static Bool addShortcut(char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
415 Shortcut *ptr;
416 KeySym ksym;
417 char *k;
418 char buf[MAX_SHORTCUT_LENGTH], *b;
420 ptr = wmalloc(sizeof(Shortcut));
422 wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
423 b = (char *)buf;
425 /* get modifiers */
426 ptr->modifier = 0;
427 while ((k = strchr(b, '+')) != NULL) {
428 int mod;
430 *k = 0;
431 mod = wXModifierFromKey(b);
432 if (mod < 0) {
433 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
434 wfree(ptr);
435 return False;
437 ptr->modifier |= mod;
439 b = k + 1;
442 /* get key */
443 ksym = XStringToKeysym(b);
445 if (ksym == NoSymbol) {
446 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
447 file, shortcutDefinition, entry->text);
448 wfree(ptr);
449 return False;
452 ptr->keycode = XKeysymToKeycode(dpy, ksym);
453 if (ptr->keycode == 0) {
454 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
455 shortcutDefinition, entry->text);
456 wfree(ptr);
457 return False;
460 ptr->menu = menu;
461 ptr->entry = entry;
463 ptr->next = shortcutList;
464 shortcutList = ptr;
466 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
468 return True;
471 static char *next_token(char *line, char **next)
473 char *tmp, c;
474 char *ret;
476 *next = NULL;
477 while (*line == ' ' || *line == '\t')
478 line++;
480 tmp = line;
482 if (*tmp == '"') {
483 tmp++;
484 line++;
485 while (*tmp != 0 && *tmp != '"')
486 tmp++;
487 if (*tmp != '"') {
488 wwarning(_("%s: unmatched '\"' in menu file"), line);
489 return NULL;
491 } else {
492 do {
493 if (*tmp == '\\')
494 tmp++;
496 if (*tmp != 0)
497 tmp++;
499 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
502 c = *tmp;
503 *tmp = 0;
504 ret = wstrdup(line);
505 *tmp = c;
507 if (c == 0)
508 return ret;
509 else
510 tmp++;
512 /* skip blanks */
513 while (*tmp == ' ' || *tmp == '\t')
514 tmp++;
516 if (*tmp != 0)
517 *next = tmp;
519 return ret;
522 static void separateCommand(char *line, char ***file, char **command)
524 char *token, *tmp = line;
525 WMArray *array = WMCreateArray(4);
526 int count, i;
528 *file = NULL;
529 *command = NULL;
530 do {
531 token = next_token(tmp, &tmp);
532 if (token) {
533 if (strcmp(token, "WITH") == 0) {
534 if (tmp != NULL && *tmp != 0)
535 *command = wstrdup(tmp);
536 else
537 wwarning(_("%s: missing command"), line);
538 break;
540 WMAddToArray(array, token);
542 } while (token != NULL && tmp != NULL);
544 count = WMGetArrayItemCount(array);
545 if (count > 0) {
546 *file = wmalloc(sizeof(char *) * (count + 1));
547 (*file)[count] = NULL;
548 for (i = 0; i < count; i++) {
549 (*file)[i] = WMGetFromArray(array, i);
552 WMFreeArray(array);
555 static WMenu *constructPLMenu(WScreen *screen, char *path)
557 WMPropList *pl = NULL;
558 WMenu *menu = NULL;
560 if (!path)
561 return NULL;
563 pl = WMReadPropListFromFile(path);
564 if (!pl)
565 return NULL;
567 menu = configureMenu(screen, pl, False);
568 if (!menu)
569 return NULL;
571 menu->on_destroy = removeShortcutsForMenu;
572 return menu;
575 static void constructMenu(WMenu * menu, WMenuEntry * entry)
577 WMenu *submenu;
578 struct stat stat_buf;
579 char **path;
580 char *cmd;
581 char *lpath = NULL;
582 int i, first = -1;
583 time_t last = 0;
585 separateCommand((char *)entry->clientdata, &path, &cmd);
586 if (path == NULL || *path == NULL || **path == 0) {
587 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
588 if (cmd)
589 wfree(cmd);
590 return;
593 if (path[0][0] == '|') {
594 /* pipe menu */
596 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
597 /* parse pipe */
599 submenu = readMenuPipe(menu->frame->screen_ptr, path);
601 if (submenu != NULL) {
602 if (path[0][1] == '|')
603 submenu->timestamp = 0;
604 else
605 submenu->timestamp = 1; /* there's no automatic reloading */
607 } else {
608 submenu = NULL;
611 } else {
613 /* try interpreting path as a proplist file */
614 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
615 /* if unsuccessful, try it as an old-style file */
616 if (!submenu) {
618 i = 0;
619 while (path[i] != NULL) {
620 char *tmp;
622 if (strcmp(path[i], "-noext") == 0) {
623 i++;
624 continue;
627 tmp = wexpandpath(path[i]);
628 wfree(path[i]);
629 lpath = getLocalizedMenuFile(tmp);
630 if (lpath) {
631 wfree(tmp);
632 path[i] = lpath;
633 lpath = NULL;
634 } else {
635 path[i] = tmp;
638 if (stat(path[i], &stat_buf) == 0) {
639 if (last < stat_buf.st_mtime)
640 last = stat_buf.st_mtime;
641 if (first < 0)
642 first = i;
643 } else {
644 werror(_("%s:could not stat menu"), path[i]);
645 /*goto finish; */
648 i++;
651 if (first < 0) {
652 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
653 goto finish;
655 stat(path[first], &stat_buf);
656 if (!menu->cascades[entry->cascade]
657 || menu->cascades[entry->cascade]->timestamp < last) {
659 if (S_ISDIR(stat_buf.st_mode)) {
660 /* menu directory */
661 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
662 if (submenu)
663 submenu->timestamp = last;
664 } else if (S_ISREG(stat_buf.st_mode)) {
665 /* menu file */
667 if (cmd || path[1])
668 wwarning(_("too many parameters in OPEN_MENU: %s"),
669 (char *)entry->clientdata);
671 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
672 if (submenu)
673 submenu->timestamp = stat_buf.st_mtime;
674 } else {
675 submenu = NULL;
677 } else {
678 submenu = NULL;
683 if (submenu) {
684 wMenuEntryRemoveCascade(menu, entry);
685 wMenuEntrySetCascade(menu, entry, submenu);
688 finish:
689 i = 0;
690 while (path[i] != NULL)
691 wfree(path[i++]);
692 wfree(path);
693 if (cmd)
694 wfree(cmd);
697 static void cleanupWorkspaceMenu(WMenu * menu)
699 if (menu->frame->screen_ptr->workspace_menu == menu)
700 menu->frame->screen_ptr->workspace_menu = NULL;
703 static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
705 WMenu *wsmenu;
706 WMenuEntry *entry;
708 if (scr->flags.added_workspace_menu) {
709 wwarning(_
710 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
711 return NULL;
712 } else {
713 scr->flags.added_workspace_menu = 1;
715 wsmenu = wWorkspaceMenuMake(scr, True);
716 wsmenu->on_destroy = cleanupWorkspaceMenu;
718 scr->workspace_menu = wsmenu;
719 entry = wMenuAddCallback(menu, title, NULL, NULL);
720 wMenuEntrySetCascade(menu, entry, wsmenu);
722 wWorkspaceMenuUpdate(scr, wsmenu);
724 return entry;
727 static void cleanupWindowsMenu(WMenu * menu)
729 if (menu->frame->screen_ptr->switch_menu == menu)
730 menu->frame->screen_ptr->switch_menu = NULL;
733 static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
735 WMenu *wwmenu;
736 WWindow *wwin;
737 WMenuEntry *entry;
739 if (scr->flags.added_windows_menu) {
740 wwarning(_
741 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
742 return NULL;
743 } else {
744 scr->flags.added_windows_menu = 1;
746 wwmenu = wMenuCreate(scr, _("Window List"), False);
747 wwmenu->on_destroy = cleanupWindowsMenu;
748 scr->switch_menu = wwmenu;
749 wwin = scr->focused_window;
750 while (wwin) {
751 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
753 wwin = wwin->prev;
755 entry = wMenuAddCallback(menu, title, NULL, NULL);
756 wMenuEntrySetCascade(menu, entry, wwmenu);
758 return entry;
761 static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
762 char *params, char *file_name)
764 WScreen *scr;
765 WMenuEntry *entry = NULL;
766 Bool shortcutOk = False;
768 if (!menu)
769 return NULL;
770 scr = menu->frame->screen_ptr;
771 if (strcmp(command, "OPEN_MENU") == 0) {
772 if (!params) {
773 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
774 } else {
775 WMenu *dummy;
776 char *path;
778 path = wfindfile(DEF_CONFIG_PATHS, params);
779 if (!path) {
780 path = wstrdup(params);
782 dummy = wMenuCreate(scr, title, False);
783 dummy->on_destroy = removeShortcutsForMenu;
784 entry = wMenuAddCallback(menu, title, constructMenu, path);
785 entry->free_cdata = wfree;
786 wMenuEntrySetCascade(menu, entry, dummy);
788 } else if (strcmp(command, "EXEC") == 0) {
789 if (!params)
790 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
791 else {
792 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
793 entry->free_cdata = wfree;
794 shortcutOk = True;
796 } else if (strcmp(command, "SHEXEC") == 0) {
797 if (!params)
798 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
799 else {
800 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
801 entry->free_cdata = wfree;
802 shortcutOk = True;
804 } else if (strcmp(command, "EXIT") == 0) {
806 if (params && strcmp(params, "QUICK") == 0)
807 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
808 else
809 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
811 shortcutOk = True;
812 } else if (strcmp(command, "SHUTDOWN") == 0) {
814 if (params && strcmp(params, "QUICK") == 0)
815 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
816 else
817 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
819 shortcutOk = True;
820 } else if (strcmp(command, "REFRESH") == 0) {
821 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
823 shortcutOk = True;
824 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
825 entry = addWorkspaceMenu(scr, menu, title);
827 shortcutOk = True;
828 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
829 entry = addWindowsMenu(scr, menu, title);
831 shortcutOk = True;
832 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
833 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
835 shortcutOk = True;
836 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
837 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
839 shortcutOk = True;
840 } else if (strcmp(command, "SHOW_ALL") == 0) {
841 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
843 shortcutOk = True;
844 } else if (strcmp(command, "RESTART") == 0) {
845 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
846 entry->free_cdata = wfree;
847 shortcutOk = True;
848 } else if (strcmp(command, "SAVE_SESSION") == 0) {
849 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
851 shortcutOk = True;
852 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
853 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
854 shortcutOk = True;
855 } else if (strcmp(command, "INFO_PANEL") == 0) {
856 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
857 shortcutOk = True;
858 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
859 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
860 shortcutOk = True;
861 } else {
862 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
864 return NULL;
867 if (shortcut && entry) {
868 if (!shortcutOk) {
869 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
870 } else {
871 if (addShortcut(file_name, shortcut, menu, entry)) {
873 entry->rtext = GetShortcutString(shortcut);
875 entry->rtext = wstrdup(shortcut);
881 return entry;
884 /******************* Menu Configuration From File *******************/
886 static void freeline(char *title, char *command, char *parameter, char *shortcut)
888 wfree(title);
889 wfree(command);
890 wfree(parameter);
891 wfree(shortcut);
894 static void separateline(char *line, char **title, char **command, char **parameter, char **shortcut)
896 char *suffix, *next = line;
898 *title = NULL;
899 *command = NULL;
900 *parameter = NULL;
901 *shortcut = NULL;
903 /* get the title */
904 *title = wtokennext(line, &next);
905 if (next == NULL)
906 return;
907 line = next;
909 /* get the command or shortcut keyword */
910 *command = wtokennext(line, &next);
911 if (next == NULL)
912 return;
913 line = next;
915 if (*command != NULL && strcmp(*command, "SHORTCUT") == 0) {
916 /* get the shortcut */
917 *shortcut = wtokennext(line, &next);
918 if (next == NULL)
919 return;
920 line = next;
922 /* get the command */
923 *command = wtokennext(line, &next);
924 if (next == NULL)
925 return;
926 line = next;
929 /* get the parameters */
930 suffix = wtrimspace(line);
932 /* should we keep this weird old behavior? */
933 if (suffix[0] == '"') {
934 *parameter = wtokennext(suffix, &next);
935 wfree(suffix);
936 } else {
937 *parameter = suffix;
941 static char *getLine(FILE * file, const char *file_name)
943 char linebuf[MAXLINE];
944 char *line = NULL, *result = NULL;
945 size_t len;
946 int done;
948 again:
949 done = 0;
950 while (!done && fgets(linebuf, sizeof(linebuf), file) != NULL) {
951 line = wtrimspace(linebuf);
952 len = strlen(line);
954 /* allow line wrapping */
955 if (len > 0 && line[len - 1] == '\\') {
956 line[len - 1] = '\0';
957 } else {
958 done = 1;
961 if (result == NULL) {
962 result = line;
963 } else {
964 if (strlen(result) < MAXLINE) {
965 result = wstrappend(result, line);
967 wfree(line);
970 if (!done || ferror(file)) {
971 wfree(result);
972 result = NULL;
973 } else if (result != NULL && (result[0] == 0 || result[0] == '#' ||
974 (result[0] == '/' && result[1] == '/'))) {
975 wfree(result);
976 result = NULL;
977 goto again;
978 } else if (result != NULL && strlen(result) >= MAXLINE) {
979 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
980 file_name, line);
981 wfree(result);
982 result = NULL;
983 goto again;
986 return result;
989 static WMenu *parseCascade(WScreen * scr, WMenu * menu, FILE * file, char *file_name)
991 char *line;
992 char *command, *params, *shortcut, *title;
994 while ((line = getLine(file, file_name)) != NULL) {
995 separateline(line, &title, &command, &params, &shortcut);
997 if (command == NULL || !command[0]) {
998 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
999 freeline(title, command, params, shortcut);
1000 wfree(line);
1001 goto error;
1004 if (strcasecmp(command, "MENU") == 0) {
1005 WMenu *cascade;
1007 /* start submenu */
1009 cascade = wMenuCreate(scr, M_(title), False);
1010 cascade->on_destroy = removeShortcutsForMenu;
1011 if (!parseCascade(scr, cascade, file, file_name)) {
1012 wMenuDestroy(cascade, True);
1013 } else {
1014 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
1016 } else if (strcasecmp(command, "END") == 0) {
1017 /* end of menu */
1018 freeline(title, command, params, shortcut);
1019 wfree(line);
1020 return menu;
1021 } else {
1022 /* normal items */
1023 addMenuEntry(menu, M_(title), shortcut, command, params, file_name);
1025 freeline(title, command, params, shortcut);
1026 wfree(line);
1029 wwarning(_("%s:syntax error in menu file:END declaration missing"), file_name);
1031 error:
1032 return NULL;
1035 static WMenu *readMenuFile(WScreen * scr, char *file_name)
1037 WMenu *menu = NULL;
1038 FILE *file = NULL;
1039 char *line;
1040 char *command, *params, *shortcut, *title;
1041 char cmd[MAXLINE];
1042 #ifdef USECPP
1043 char *args;
1044 int cpp = 0;
1045 #endif
1047 #ifdef USECPP
1048 if (!wPreferences.flags.nocpp) {
1049 args = MakeCPPArgs(file_name);
1050 if (!args) {
1051 wwarning(_("could not make arguments for menu file preprocessor"));
1052 } else {
1053 snprintf(cmd, sizeof(cmd), "%s %s %s", CPP_PATH, args, file_name);
1054 wfree(args);
1055 file = popen(cmd, "r");
1056 if (!file) {
1057 werror(_("%s:could not open/preprocess menu file"), file_name);
1058 } else {
1059 cpp = 1;
1063 #endif /* USECPP */
1065 if (!file) {
1066 file = fopen(file_name, "rb");
1067 if (!file) {
1068 werror(_("%s:could not open menu file"), file_name);
1069 return NULL;
1073 while ((line = getLine(file, file_name)) != NULL) {
1074 separateline(line, &title, &command, &params, &shortcut);
1076 if (command == NULL || !command[0]) {
1077 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1078 freeline(title, command, params, shortcut);
1079 wfree(line);
1080 break;
1082 if (strcasecmp(command, "MENU") == 0) {
1083 menu = wMenuCreate(scr, M_(title), True);
1084 menu->on_destroy = removeShortcutsForMenu;
1085 if (!parseCascade(scr, menu, file, file_name)) {
1086 wMenuDestroy(menu, True);
1087 menu = NULL;
1089 freeline(title, command, params, shortcut);
1090 wfree(line);
1091 break;
1092 } else {
1093 wwarning(_("%s:invalid menu file. MENU command is missing"), file_name);
1094 freeline(title, command, params, shortcut);
1095 wfree(line);
1096 break;
1098 freeline(title, command, params, shortcut);
1099 wfree(line);
1102 #ifdef USECPP
1103 if (cpp) {
1104 if (pclose(file) == -1) {
1105 werror(_("error reading preprocessed menu data"));
1107 } else {
1108 fclose(file);
1110 #else
1111 fclose(file);
1112 #endif
1114 return menu;
1117 /************ Menu Configuration From Pipe *************/
1119 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1121 WMenu *menu = NULL;
1122 FILE *file = NULL;
1123 char *command, *params, *shortcut, *title;
1124 char *line;
1125 char *filename;
1126 char flat_file[MAXLINE];
1127 char cmd[MAXLINE];
1128 int i;
1129 #ifdef USECPP
1130 char *args;
1131 #endif
1133 flat_file[0] = '\0';
1135 for (i = 0; file_name[i] != NULL; i++) {
1136 strcat(flat_file, file_name[i]);
1137 strcat(flat_file, " ");
1139 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1141 #ifdef USECPP
1142 if (!wPreferences.flags.nocpp) {
1143 args = MakeCPPArgs(filename);
1144 if (!args) {
1145 wwarning(_("could not make arguments for menu file preprocessor"));
1146 } else {
1147 snprintf(cmd, sizeof(cmd), "%s | %s %s", filename, CPP_PATH, args);
1149 wfree(args);
1150 file = popen(cmd, "r");
1151 if (!file) {
1152 werror(_("%s:could not open/preprocess menu file"), filename);
1156 #endif /* USECPP */
1158 if (!file) {
1159 file = popen(filename, "rb");
1161 if (!file) {
1162 werror(_("%s:could not open menu file"), filename);
1163 return NULL;
1167 while ((line = getLine(file, filename)) != NULL) {
1168 separateline(line, &title, &command, &params, &shortcut);
1170 if (command == NULL || !command[0]) {
1171 wwarning(_("%s:missing command in menu config: %s"), filename, line);
1172 freeline(title, command, params, shortcut);
1173 wfree(line);
1174 break;
1176 if (strcasecmp(command, "MENU") == 0) {
1177 menu = wMenuCreate(scr, M_(title), True);
1178 menu->on_destroy = removeShortcutsForMenu;
1179 if (!parseCascade(scr, menu, file, filename)) {
1180 wMenuDestroy(menu, True);
1181 menu = NULL;
1183 freeline(title, command, params, shortcut);
1184 wfree(line);
1185 break;
1186 } else {
1187 wwarning(_("%s:no title given for the root menu"), filename);
1188 freeline(title, command, params, shortcut);
1189 wfree(line);
1190 break;
1193 freeline(title, command, params, shortcut);
1194 wfree(line);
1197 pclose(file);
1199 return menu;
1202 typedef struct {
1203 char *name;
1204 int index;
1205 } dir_data;
1207 static int myCompare(const void *d1, const void *d2)
1209 dir_data *p1 = *(dir_data **) d1;
1210 dir_data *p2 = *(dir_data **) d2;
1212 return strcmp(p1->name, p2->name);
1215 /************ Menu Configuration From Directory *************/
1217 static Bool isFilePackage(char *file)
1219 int l;
1221 /* check if the extension indicates this file is a
1222 * file package. For now, only recognize .themed */
1224 l = strlen(file);
1226 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1227 return True;
1228 } else {
1229 return False;
1233 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
1235 DIR *dir;
1236 struct dirent *dentry;
1237 struct stat stat_buf;
1238 WMenu *menu = NULL;
1239 char *buffer;
1240 WMArray *dirs = NULL, *files = NULL;
1241 WMArrayIterator iter;
1242 int length, i, have_space = 0;
1243 dir_data *data;
1244 int stripExtension = 0;
1246 dirs = WMCreateArray(16);
1247 files = WMCreateArray(16);
1249 i = 0;
1250 while (path[i] != NULL) {
1251 if (strcmp(path[i], "-noext") == 0) {
1252 stripExtension = 1;
1253 i++;
1254 continue;
1257 dir = opendir(path[i]);
1258 if (!dir) {
1259 i++;
1260 continue;
1263 while ((dentry = readdir(dir))) {
1265 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1266 continue;
1268 if (dentry->d_name[0] == '.')
1269 continue;
1271 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1272 if (!buffer) {
1273 werror(_("out of memory while constructing directory menu %s"), path[i]);
1274 break;
1277 strcpy(buffer, path[i]);
1278 strcat(buffer, "/");
1279 strcat(buffer, dentry->d_name);
1281 if (stat(buffer, &stat_buf) != 0) {
1282 werror(_("%s:could not stat file \"%s\" in menu directory"),
1283 path[i], dentry->d_name);
1284 } else {
1285 Bool isFilePack = False;
1287 data = NULL;
1288 if (S_ISDIR(stat_buf.st_mode)
1289 && !(isFilePack = isFilePackage(dentry->d_name))) {
1291 /* access always returns success for user root */
1292 if (access(buffer, X_OK) == 0) {
1293 /* Directory is accesible. Add to directory list */
1295 data = (dir_data *) wmalloc(sizeof(dir_data));
1296 data->name = wstrdup(dentry->d_name);
1297 data->index = i;
1299 WMAddToArray(dirs, data);
1301 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1302 /* Hack because access always returns X_OK success for user root */
1303 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1304 if ((command != NULL && access(buffer, R_OK) == 0) ||
1305 (command == NULL && access(buffer, X_OK) == 0 &&
1306 (stat_buf.st_mode & S_IXANY))) {
1308 data = (dir_data *) wmalloc(sizeof(dir_data));
1309 data->name = wstrdup(dentry->d_name);
1310 data->index = i;
1312 WMAddToArray(files, data);
1316 free(buffer);
1319 closedir(dir);
1320 i++;
1323 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1324 WMFreeArray(dirs);
1325 WMFreeArray(files);
1326 return NULL;
1329 WMSortArray(dirs, myCompare);
1330 WMSortArray(files, myCompare);
1332 menu = wMenuCreate(scr, M_(title), False);
1333 menu->on_destroy = removeShortcutsForMenu;
1335 WM_ITERATE_ARRAY(dirs, data, iter) {
1336 /* New directory. Use same OPEN_MENU command that was used
1337 * for the current directory. */
1338 length = strlen(path[data->index]) + strlen(data->name) + 6;
1339 if (stripExtension)
1340 length += 7;
1341 if (command)
1342 length += strlen(command) + 6;
1343 buffer = malloc(length);
1344 if (!buffer) {
1345 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1346 break;
1349 buffer[0] = '\0';
1350 if (stripExtension)
1351 strcat(buffer, "-noext ");
1353 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1355 if (have_space)
1356 strcat(buffer, "\"");
1357 strcat(buffer, path[data->index]);
1359 strcat(buffer, "/");
1360 strcat(buffer, data->name);
1361 if (have_space)
1362 strcat(buffer, "\"");
1363 if (command) {
1364 strcat(buffer, " WITH ");
1365 strcat(buffer, command);
1368 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1370 wfree(buffer);
1371 if (data->name)
1372 wfree(data->name);
1373 wfree(data);
1376 WM_ITERATE_ARRAY(files, data, iter) {
1377 /* executable: add as entry */
1378 length = strlen(path[data->index]) + strlen(data->name) + 6;
1379 if (command)
1380 length += strlen(command);
1382 buffer = malloc(length);
1383 if (!buffer) {
1384 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1385 break;
1388 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1389 if (command != NULL) {
1390 strcpy(buffer, command);
1391 strcat(buffer, " ");
1392 if (have_space)
1393 strcat(buffer, "\"");
1394 strcat(buffer, path[data->index]);
1395 } else {
1396 if (have_space) {
1397 buffer[0] = '"';
1398 buffer[1] = 0;
1399 strcat(buffer, path[data->index]);
1400 } else {
1401 strcpy(buffer, path[data->index]);
1404 strcat(buffer, "/");
1405 strcat(buffer, data->name);
1406 if (have_space)
1407 strcat(buffer, "\"");
1409 if (stripExtension) {
1410 char *ptr = strrchr(data->name, '.');
1411 if (ptr && ptr != data->name)
1412 *ptr = 0;
1414 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1416 wfree(buffer);
1417 if (data->name)
1418 wfree(data->name);
1419 wfree(data);
1422 WMFreeArray(files);
1423 WMFreeArray(dirs);
1425 return menu;
1428 /************ Menu Configuration From WMRootMenu *************/
1430 static WMenu *makeDefaultMenu(WScreen * scr)
1432 WMenu *menu = NULL;
1434 menu = wMenuCreate(scr, _("Commands"), True);
1435 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1436 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1437 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1438 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1439 return menu;
1443 *----------------------------------------------------------------------
1444 * configureMenu--
1445 * Reads root menu configuration from defaults database.
1447 *----------------------------------------------------------------------
1449 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1451 WMenu *menu = NULL;
1452 WMPropList *elem;
1453 int i, count;
1454 WMPropList *title, *command, *params;
1455 char *tmp, *mtitle;
1457 if (WMIsPLString(definition)) {
1458 struct stat stat_buf;
1459 char *path = NULL;
1460 Bool menu_is_default = False;
1462 /* menu definition is a string. Probably a path, so parse the file */
1464 tmp = wexpandpath(WMGetFromPLString(definition));
1466 path = getLocalizedMenuFile(tmp);
1468 if (!path)
1469 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1471 if (!path) {
1472 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1473 menu_is_default = True;
1476 if (!path) {
1477 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1478 wfree(tmp);
1479 return NULL;
1482 if (stat(path, &stat_buf) < 0) {
1483 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1484 wfree(path);
1485 wfree(tmp);
1486 return NULL;
1489 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1490 /* if the pointer in WMRootMenu has changed */
1491 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1493 if (menu_is_default) {
1494 wwarning(_
1495 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1496 path);
1499 menu = readMenuFile(scr, path);
1500 if (menu)
1501 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1502 } else {
1503 menu = NULL;
1505 wfree(path);
1506 wfree(tmp);
1508 return menu;
1511 count = WMGetPropListItemCount(definition);
1512 if (count == 0)
1513 return NULL;
1515 elem = WMGetFromPLArray(definition, 0);
1516 if (!WMIsPLString(elem)) {
1517 tmp = WMGetPropListDescription(elem, False);
1518 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1519 wfree(tmp);
1520 return NULL;
1522 mtitle = WMGetFromPLString(elem);
1524 menu = wMenuCreate(scr, M_(mtitle), False);
1525 menu->on_destroy = removeShortcutsForMenu;
1527 #ifdef GLOBAL_SUBMENU_FILE
1528 if (includeGlobals) {
1529 WMenu *submenu;
1530 WMenuEntry *mentry;
1532 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1534 if (submenu) {
1535 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1536 wMenuEntrySetCascade(menu, mentry, submenu);
1539 #endif
1541 for (i = 1; i < count; i++) {
1542 elem = WMGetFromPLArray(definition, i);
1543 #if 0
1544 if (WMIsPLString(elem)) {
1545 char *file;
1547 file = WMGetFromPLString(elem);
1550 #endif
1551 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1552 goto error;
1554 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1555 WMenu *submenu;
1556 WMenuEntry *mentry;
1558 /* submenu */
1559 submenu = configureMenu(scr, elem, True);
1560 if (submenu) {
1561 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1562 wMenuEntrySetCascade(menu, mentry, submenu);
1564 } else {
1565 int idx = 0;
1566 WMPropList *shortcut;
1567 /* normal entry */
1569 title = WMGetFromPLArray(elem, idx++);
1570 shortcut = WMGetFromPLArray(elem, idx++);
1571 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1572 shortcut = WMGetFromPLArray(elem, idx++);
1573 command = WMGetFromPLArray(elem, idx++);
1574 } else {
1575 command = shortcut;
1576 shortcut = NULL;
1578 params = WMGetFromPLArray(elem, idx++);
1580 if (!title || !command)
1581 goto error;
1583 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1584 shortcut ? WMGetFromPLString(shortcut) : NULL,
1585 WMGetFromPLString(command),
1586 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1588 continue;
1590 error:
1591 tmp = WMGetPropListDescription(elem, False);
1592 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1593 wfree(tmp);
1596 return menu;
1600 *----------------------------------------------------------------------
1601 * OpenRootMenu--
1602 * Opens the root menu, parsing the menu configuration from the
1603 * defaults database.
1604 * If the menu is already mapped and is not sticked to the
1605 * root window, it will be unmapped.
1607 * Side effects:
1608 * The menu may be remade.
1610 * Notes:
1611 * Construction of OPEN_MENU entries are delayed to the moment the
1612 * user map's them.
1613 *----------------------------------------------------------------------
1615 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1617 WMenu *menu = NULL;
1618 WMPropList *definition;
1620 static WMPropList *domain=NULL;
1622 if (!domain) {
1623 domain = WMCreatePLString("WMRootMenu");
1627 scr->flags.root_menu_changed_shortcuts = 0;
1628 scr->flags.added_workspace_menu = 0;
1629 scr->flags.added_windows_menu = 0;
1631 if (scr->root_menu && scr->root_menu->flags.mapped) {
1632 menu = scr->root_menu;
1633 if (!menu->flags.buttoned) {
1634 wMenuUnmap(menu);
1635 } else {
1636 wRaiseFrame(menu->frame->core);
1638 if (keyboard)
1639 wMenuMapAt(menu, 0, 0, True);
1640 else
1641 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1643 return;
1646 definition = WDRootMenu->dictionary;
1649 definition = PLGetDomain(domain);
1651 if (definition) {
1652 if (WMIsPLArray(definition)) {
1653 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1654 menu = configureMenu(scr, definition, True);
1655 if (menu)
1656 menu->timestamp = WDRootMenu->timestamp;
1658 } else
1659 menu = NULL;
1660 } else {
1661 menu = configureMenu(scr, definition, True);
1665 if (!menu) {
1666 /* menu hasn't changed or could not be read */
1667 if (!scr->root_menu) {
1668 wMessageDialog(scr, _("Error"),
1669 _("The applications menu could not be loaded. "
1670 "Look at the console output for a detailed "
1671 "description of the errors."), _("OK"), NULL, NULL);
1673 menu = makeDefaultMenu(scr);
1674 scr->root_menu = menu;
1676 menu = scr->root_menu;
1677 } else {
1678 /* new root menu */
1679 if (scr->root_menu) {
1680 wMenuDestroy(scr->root_menu, True);
1682 scr->root_menu = menu;
1684 if (menu) {
1685 int newx, newy;
1687 if (keyboard && x == 0 && y == 0) {
1688 newx = newy = 0;
1689 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1690 newx = x - menu->frame->core->width / 2;
1691 newy = y - menu->frame->core->height / 2;
1692 } else {
1693 newx = x - menu->frame->core->width / 2;
1694 newy = y;
1696 wMenuMapAt(menu, newx, newy, keyboard);
1699 if (scr->flags.root_menu_changed_shortcuts)
1700 rebindKeygrabs(scr);