Remove XSMP_ENABLED constructs
[wmaker-crm.git] / src / rootmenu.c
blob3c8133b8cd04c4ed24c2c305beb6cae4202deca9
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
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21 * USA.
24 #include "wconfig.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 #include <sys/types.h>
32 #include <string.h>
33 #include <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;
60 extern WDDomain *WDRootMenu;
62 extern Cursor wCursor[WCUR_LAST];
64 extern Time LastTimestamp;
66 extern WPreferences wPreferences;
68 extern int wScreenCount;
70 static WMenu *readMenuPipe(WScreen * scr, char **file_name);
71 static WMenu *readMenuFile(WScreen * scr, char *file_name);
72 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **file_name, char *command);
74 typedef struct Shortcut {
75 struct Shortcut *next;
77 int modifier;
78 KeyCode keycode;
79 WMenuEntry *entry;
80 WMenu *menu;
81 } Shortcut;
83 static Shortcut *shortcutList = NULL;
86 * Syntax:
87 * # main menu
88 * "Menu Name" MENU
89 * "Title" EXEC command_to_exec -params
90 * "Submenu" MENU
91 * "Title" EXEC command_to_exec -params
92 * "Submenu" END
93 * "Workspaces" WORKSPACE_MENU
94 * "Title" built_in_command
95 * "Quit" EXIT
96 * "Quick Quit" EXIT QUICK
97 * "Menu Name" END
99 * Commands may be preceded by SHORTCUT key
101 * Built-in commands:
103 * INFO_PANEL - shows the Info Panel
104 * LEGAL_PANEL - shows the Legal info panel
105 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
106 * REFRESH - forces the desktop to be repainted
107 * EXIT [QUICK] - exit the window manager [without confirmation]
108 * EXEC <program> - execute an external program
109 * SHEXEC <command> - execute a shell command
110 * WORKSPACE_MENU - places the workspace submenu
111 * ARRANGE_ICONS
112 * RESTART [<window manager>] - restarts the window manager
113 * SHOW_ALL - unhide all windows on workspace
114 * HIDE_OTHERS - hides all windows excep the focused one
115 * OPEN_MENU file - read menu data from file which must be a valid menu file.
116 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
117 * - read menu data from directory(ies) and
118 * eventually precede each with a command.
119 * OPEN_MENU | command
120 * - opens command and uses its stdout to construct and insert
121 * the resulting menu in current position. The output of
122 * command must be a valid menu description.
123 * The space between '|' and command is optional.
124 * || will do the same, but will not cache the contents.
125 * SAVE_SESSION - saves the current state of the desktop, which include
126 * all running applications, all their hints (geometry,
127 * position on screen, workspace they live on, the dock
128 * or clip from where they were launched, and
129 * if minimized, shaded or hidden. Also saves the current
130 * workspace the user is on. All will be restored on every
131 * start of windowmaker until another SAVE_SESSION or
132 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
133 * WindowMaker domain file, then saving is automatically
134 * done on every windowmaker exit, overwriting any
135 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
136 * dock state now.
137 * CLEAR_SESSION - clears any previous saved session. This will not have
138 * any effect if SaveSessionOnExit is True.
142 #define M_QUICK 1
144 /* menu commands */
146 static void execCommand(WMenu * menu, WMenuEntry * entry)
148 char *cmdline;
150 cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
152 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
153 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);
154 XSync(dpy, 0);
156 if (cmdline) {
157 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
158 wfree(cmdline);
160 XUngrabPointer(dpy, CurrentTime);
161 XSync(dpy, 0);
164 static void exitCommand(WMenu * menu, WMenuEntry * entry)
166 static int inside = 0;
167 int result;
169 /* prevent reentrant calls */
170 if (inside)
171 return;
172 inside = 1;
174 #define R_CANCEL 0
175 #define R_EXIT 1
177 result = R_CANCEL;
179 if ((long)entry->clientdata == M_QUICK) {
180 result = R_EXIT;
181 } else {
182 int r, oldSaveSessionFlag;
184 oldSaveSessionFlag = wPreferences.save_session_on_exit;
185 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
186 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
188 if (r == WAPRDefault) {
189 result = R_EXIT;
190 } else if (r == WAPRAlternate) {
191 /* Don't modify the "save session on exit" flag if the
192 * user canceled the operation. */
193 wPreferences.save_session_on_exit = oldSaveSessionFlag;
196 if (result == R_EXIT) {
197 #ifdef DEBUG
198 printf("Exiting WindowMaker.\n");
199 #endif
200 Shutdown(WSExitMode);
202 #undef R_EXIT
203 #undef R_CANCEL
204 inside = 0;
207 static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
209 static int inside = 0;
210 int result;
212 /* prevent reentrant calls */
213 if (inside)
214 return;
215 inside = 1;
217 #define R_CANCEL 0
218 #define R_CLOSE 1
219 #define R_KILL 2
221 result = R_CANCEL;
222 if ((long)entry->clientdata == M_QUICK)
223 result = R_CLOSE;
224 else {
226 int r, oldSaveSessionFlag;
228 oldSaveSessionFlag = wPreferences.save_session_on_exit;
230 r = wExitDialog(menu->frame->screen_ptr,
231 _("Kill X session"),
232 _("Kill Window System session?\n"
233 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
234 if (r == WAPRDefault) {
235 result = R_KILL;
236 } else if (r == WAPRAlternate) {
237 /* Don't modify the "save session on exit" flag if the
238 * user canceled the operation. */
239 wPreferences.save_session_on_exit = oldSaveSessionFlag;
244 if (result != R_CANCEL) {
246 Shutdown(WSKillMode);
249 #undef R_CLOSE
250 #undef R_CANCEL
251 #undef R_KILL
252 inside = 0;
255 static void restartCommand(WMenu * menu, WMenuEntry * entry)
257 Shutdown(WSRestartPreparationMode);
258 Restart((char *)entry->clientdata, False);
259 Restart(NULL, True);
262 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
264 wRefreshDesktop(menu->frame->screen_ptr);
267 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
269 wArrangeIcons(menu->frame->screen_ptr, True);
272 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
274 wShowAllWindows(menu->frame->screen_ptr);
277 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
279 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
282 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
284 if (!wPreferences.save_session_on_exit)
285 wSessionSaveState(menu->frame->screen_ptr);
287 wScreenSaveState(menu->frame->screen_ptr);
290 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
292 wSessionClearState(menu->frame->screen_ptr);
293 wScreenSaveState(menu->frame->screen_ptr);
296 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
298 wShowInfoPanel(menu->frame->screen_ptr);
301 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
303 wShowLegalPanel(menu->frame->screen_ptr);
306 /********************************************************************/
308 static char *getLocalizedMenuFile(char *menu)
310 char *buffer, *ptr, *locale;
311 int len;
313 if (!Locale)
314 return NULL;
316 len = strlen(menu) + strlen(Locale) + 8;
317 buffer = wmalloc(len);
319 /* try menu.locale_name */
320 snprintf(buffer, len, "%s.%s", menu, Locale);
321 if (access(buffer, F_OK) == 0) {
322 return buffer;
325 /* position of locale in our buffer */
326 locale = buffer + strlen(menu) + 1;
328 /* check if it is in the form aa_bb.encoding and check for aa_bb */
329 ptr = strchr(locale, '.');
330 if (ptr) {
331 *ptr = 0;
332 if (access(buffer, F_OK) == 0) {
333 return buffer;
336 /* now check for aa */
337 ptr = strchr(locale, '_');
338 if (ptr) {
339 *ptr = 0;
340 if (access(buffer, F_OK) == 0) {
341 return buffer;
345 wfree(buffer);
347 return NULL;
350 static void raiseMenus(WMenu * menu)
352 int i;
354 if (menu->flags.mapped) {
355 wRaiseFrame(menu->frame->core);
357 for (i = 0; i < menu->cascade_no; i++) {
358 if (menu->cascades[i])
359 raiseMenus(menu->cascades[i]);
363 Bool wRootMenuPerformShortcut(XEvent * event)
365 WScreen *scr = wScreenForRootWindow(event->xkey.root);
366 Shortcut *ptr;
367 int modifiers;
368 int done = 0;
370 /* ignore CapsLock */
371 modifiers = event->xkey.state & ValidModMask;
373 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
374 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
375 continue;
377 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
378 (*ptr->entry->callback) (ptr->menu, ptr->entry);
379 done = True;
383 return done;
386 void wRootMenuBindShortcuts(Window window)
388 Shortcut *ptr;
390 ptr = shortcutList;
391 while (ptr) {
392 if (ptr->modifier != AnyModifier) {
393 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
394 window, True, GrabModeAsync, GrabModeAsync);
395 #ifdef NUMLOCK_HACK
396 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
397 #endif
399 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
400 ptr = ptr->next;
404 static void rebindKeygrabs(WScreen * scr)
406 WWindow *wwin;
408 wwin = scr->focused_window;
410 while (wwin != NULL) {
411 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
413 if (!WFLAGP(wwin, no_bind_keys)) {
414 wWindowSetKeyGrabs(wwin);
416 wwin = wwin->prev;
420 static void removeShortcutsForMenu(WMenu * menu)
422 Shortcut *ptr, *tmp;
423 Shortcut *newList = NULL;
425 ptr = shortcutList;
426 while (ptr != NULL) {
427 tmp = ptr->next;
428 if (ptr->menu == menu) {
429 wfree(ptr);
430 } else {
431 ptr->next = newList;
432 newList = ptr;
434 ptr = tmp;
436 shortcutList = newList;
437 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
440 static Bool addShortcut(char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
442 Shortcut *ptr;
443 KeySym ksym;
444 char *k;
445 char buf[MAX_SHORTCUT_LENGTH], *b;
447 ptr = wmalloc(sizeof(Shortcut));
449 strncpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
450 b = (char *)buf;
452 /* get modifiers */
453 ptr->modifier = 0;
454 while ((k = strchr(b, '+')) != NULL) {
455 int mod;
457 *k = 0;
458 mod = wXModifierFromKey(b);
459 if (mod < 0) {
460 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
461 wfree(ptr);
462 return False;
464 ptr->modifier |= mod;
466 b = k + 1;
469 /* get key */
470 ksym = XStringToKeysym(b);
472 if (ksym == NoSymbol) {
473 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
474 file, shortcutDefinition, entry->text);
475 wfree(ptr);
476 return False;
479 ptr->keycode = XKeysymToKeycode(dpy, ksym);
480 if (ptr->keycode == 0) {
481 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
482 shortcutDefinition, entry->text);
483 wfree(ptr);
484 return False;
487 ptr->menu = menu;
488 ptr->entry = entry;
490 ptr->next = shortcutList;
491 shortcutList = ptr;
493 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
495 return True;
498 /*******************************/
500 static char *cropline(char *line)
502 char *end;
504 if (strlen(line) == 0)
505 return line;
507 end = &(line[strlen(line)]) - 1;
508 while (isspace(*line) && *line != 0)
509 line++;
510 while (end > line && isspace(*end)) {
511 *end = 0;
512 end--;
514 return line;
517 static char *next_token(char *line, char **next)
519 char *tmp, c;
520 char *ret;
522 *next = NULL;
523 while (*line == ' ' || *line == '\t')
524 line++;
526 tmp = line;
528 if (*tmp == '"') {
529 tmp++;
530 line++;
531 while (*tmp != 0 && *tmp != '"')
532 tmp++;
533 if (*tmp != '"') {
534 wwarning(_("%s: unmatched '\"' in menu file"), line);
535 return NULL;
537 } else {
538 do {
539 if (*tmp == '\\')
540 tmp++;
542 if (*tmp != 0)
543 tmp++;
545 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
548 c = *tmp;
549 *tmp = 0;
550 ret = wstrdup(line);
551 *tmp = c;
553 if (c == 0)
554 return ret;
555 else
556 tmp++;
558 /* skip blanks */
559 while (*tmp == ' ' || *tmp == '\t')
560 tmp++;
562 if (*tmp != 0)
563 *next = tmp;
565 return ret;
568 static void separateCommand(char *line, char ***file, char **command)
570 char *token, *tmp = line;
571 WMArray *array = WMCreateArray(4);
572 int count, i;
574 *file = NULL;
575 *command = NULL;
576 do {
577 token = next_token(tmp, &tmp);
578 if (token) {
579 if (strcmp(token, "WITH") == 0) {
580 if (tmp != NULL && *tmp != 0)
581 *command = wstrdup(tmp);
582 else
583 wwarning(_("%s: missing command"), line);
584 break;
586 WMAddToArray(array, token);
588 } while (token != NULL && tmp != NULL);
590 count = WMGetArrayItemCount(array);
591 if (count > 0) {
592 *file = wmalloc(sizeof(char *) * (count + 1));
593 (*file)[count] = NULL;
594 for (i = 0; i < count; i++) {
595 (*file)[i] = WMGetFromArray(array, i);
598 WMFreeArray(array);
601 static void constructMenu(WMenu * menu, WMenuEntry * entry)
603 WMenu *submenu;
604 struct stat stat_buf;
605 char **path;
606 char *cmd;
607 char *lpath = NULL;
608 int i, first = -1;
609 time_t last = 0;
611 separateCommand((char *)entry->clientdata, &path, &cmd);
612 if (path == NULL || *path == NULL || **path == 0) {
613 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
614 return;
617 if (path[0][0] == '|') {
618 /* pipe menu */
620 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
621 /* parse pipe */
623 submenu = readMenuPipe(menu->frame->screen_ptr, path);
625 if (submenu != NULL) {
626 if (path[0][1] == '|')
627 submenu->timestamp = 0;
628 else
629 submenu->timestamp = 1; /* there's no automatic reloading */
631 } else {
632 submenu = NULL;
635 } else {
636 i = 0;
637 while (path[i] != NULL) {
638 char *tmp;
640 if (strcmp(path[i], "-noext") == 0) {
641 i++;
642 continue;
645 tmp = wexpandpath(path[i]);
646 wfree(path[i]);
647 lpath = getLocalizedMenuFile(tmp);
648 if (lpath) {
649 wfree(tmp);
650 path[i] = lpath;
651 lpath = NULL;
652 } else {
653 path[i] = tmp;
656 if (stat(path[i], &stat_buf) == 0) {
657 if (last < stat_buf.st_mtime)
658 last = stat_buf.st_mtime;
659 if (first < 0)
660 first = i;
661 } else {
662 wsyserror(_("%s:could not stat menu"), path[i]);
663 /*goto finish; */
666 i++;
669 if (first < 0) {
670 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
671 goto finish;
673 stat(path[first], &stat_buf);
674 if (!menu->cascades[entry->cascade]
675 || menu->cascades[entry->cascade]->timestamp < last) {
677 if (S_ISDIR(stat_buf.st_mode)) {
678 /* menu directory */
679 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
680 if (submenu)
681 submenu->timestamp = last;
682 } else if (S_ISREG(stat_buf.st_mode)) {
683 /* menu file */
685 if (cmd || path[1])
686 wwarning(_("too many parameters in OPEN_MENU: %s"),
687 (char *)entry->clientdata);
689 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
690 if (submenu)
691 submenu->timestamp = stat_buf.st_mtime;
692 } else {
693 submenu = NULL;
695 } else {
696 submenu = NULL;
700 if (submenu) {
701 wMenuEntryRemoveCascade(menu, entry);
702 wMenuEntrySetCascade(menu, entry, submenu);
705 finish:
706 i = 0;
707 while (path[i] != NULL)
708 wfree(path[i++]);
709 wfree(path);
710 if (cmd)
711 wfree(cmd);
714 static void cleanupWorkspaceMenu(WMenu * menu)
716 if (menu->frame->screen_ptr->workspace_menu == menu)
717 menu->frame->screen_ptr->workspace_menu = NULL;
720 static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
722 WMenu *wsmenu;
723 WMenuEntry *entry;
725 if (scr->flags.added_workspace_menu) {
726 wwarning(_
727 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
728 return NULL;
729 } else {
730 scr->flags.added_workspace_menu = 1;
732 wsmenu = wWorkspaceMenuMake(scr, True);
733 wsmenu->on_destroy = cleanupWorkspaceMenu;
735 scr->workspace_menu = wsmenu;
736 entry = wMenuAddCallback(menu, title, NULL, NULL);
737 wMenuEntrySetCascade(menu, entry, wsmenu);
739 wWorkspaceMenuUpdate(scr, wsmenu);
741 return entry;
744 static void cleanupWindowsMenu(WMenu * menu)
746 if (menu->frame->screen_ptr->switch_menu == menu)
747 menu->frame->screen_ptr->switch_menu = NULL;
750 static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
752 WMenu *wwmenu;
753 WWindow *wwin;
754 WMenuEntry *entry;
756 if (scr->flags.added_windows_menu) {
757 wwarning(_
758 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
759 return NULL;
760 } else {
761 scr->flags.added_windows_menu = 1;
763 wwmenu = wMenuCreate(scr, _("Window List"), False);
764 wwmenu->on_destroy = cleanupWindowsMenu;
765 scr->switch_menu = wwmenu;
766 wwin = scr->focused_window;
767 while (wwin) {
768 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
770 wwin = wwin->prev;
772 entry = wMenuAddCallback(menu, title, NULL, NULL);
773 wMenuEntrySetCascade(menu, entry, wwmenu);
775 return entry;
778 static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
779 char *params, char *file_name)
781 WScreen *scr;
782 WMenuEntry *entry = NULL;
783 Bool shortcutOk = False;
785 if (!menu)
786 return NULL;
787 scr = menu->frame->screen_ptr;
788 if (strcmp(command, "OPEN_MENU") == 0) {
789 if (!params) {
790 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
791 } else {
792 WMenu *dummy;
793 char *path;
795 path = wfindfile(DEF_CONFIG_PATHS, params);
796 if (!path) {
797 path = wstrdup(params);
799 dummy = wMenuCreate(scr, title, False);
800 dummy->on_destroy = removeShortcutsForMenu;
801 entry = wMenuAddCallback(menu, title, constructMenu, path);
802 entry->free_cdata = free;
803 wMenuEntrySetCascade(menu, entry, dummy);
805 } else if (strcmp(command, "EXEC") == 0) {
806 if (!params)
807 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
808 else {
809 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
810 entry->free_cdata = free;
811 shortcutOk = True;
813 } else if (strcmp(command, "SHEXEC") == 0) {
814 if (!params)
815 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
816 else {
817 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
818 entry->free_cdata = free;
819 shortcutOk = True;
821 } else if (strcmp(command, "EXIT") == 0) {
823 if (params && strcmp(params, "QUICK") == 0)
824 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
825 else
826 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
828 shortcutOk = True;
829 } else if (strcmp(command, "SHUTDOWN") == 0) {
831 if (params && strcmp(params, "QUICK") == 0)
832 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
833 else
834 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
836 shortcutOk = True;
837 } else if (strcmp(command, "REFRESH") == 0) {
838 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
840 shortcutOk = True;
841 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
842 entry = addWorkspaceMenu(scr, menu, title);
844 shortcutOk = True;
845 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
846 entry = addWindowsMenu(scr, menu, title);
848 shortcutOk = True;
849 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
850 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
852 shortcutOk = True;
853 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
854 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
856 shortcutOk = True;
857 } else if (strcmp(command, "SHOW_ALL") == 0) {
858 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
860 shortcutOk = True;
861 } else if (strcmp(command, "RESTART") == 0) {
862 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
863 entry->free_cdata = free;
864 shortcutOk = True;
865 } else if (strcmp(command, "SAVE_SESSION") == 0) {
866 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
868 shortcutOk = True;
869 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
870 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
871 shortcutOk = True;
872 } else if (strcmp(command, "INFO_PANEL") == 0) {
873 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
874 shortcutOk = True;
875 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
876 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
877 shortcutOk = True;
878 } else {
879 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
881 return NULL;
884 if (shortcut && entry) {
885 if (!shortcutOk) {
886 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
887 } else {
888 if (addShortcut(file_name, shortcut, menu, entry)) {
890 entry->rtext = GetShortcutString(shortcut);
892 entry->rtext = wstrdup(shortcut);
898 return entry;
901 /******************* Menu Configuration From File *******************/
903 static void separateline(char *line, char *title, char *command, char *parameter, char *shortcut)
905 int l, i;
907 l = strlen(line);
909 *title = 0;
910 *command = 0;
911 *parameter = 0;
912 *shortcut = 0;
913 /* get the title */
914 while (isspace(*line) && (*line != 0))
915 line++;
916 if (*line == '"') {
917 line++;
918 i = 0;
919 while (line[i] != '"' && (line[i] != 0))
920 i++;
921 if (line[i] != '"')
922 return;
923 } else {
924 i = 0;
925 while (!isspace(line[i]) && (line[i] != 0))
926 i++;
928 strncpy(title, line, i);
929 title[i++] = 0;
930 line += i;
932 /* get the command or shortcut keyword */
933 while (isspace(*line) && (*line != 0))
934 line++;
935 if (*line == 0)
936 return;
937 i = 0;
938 while (!isspace(line[i]) && (line[i] != 0))
939 i++;
940 strncpy(command, line, i);
941 command[i++] = 0;
942 line += i;
944 if (strcmp(command, "SHORTCUT") == 0) {
945 /* get the shortcut key */
946 while (isspace(*line) && (*line != 0))
947 line++;
948 if (*line == '"') {
949 line++;
950 i = 0;
951 while (line[i] != '"' && (line[i] != 0))
952 i++;
953 if (line[i] != '"')
954 return;
955 } else {
956 i = 0;
957 while (!isspace(line[i]) && (line[i] != 0))
958 i++;
960 strncpy(shortcut, line, i);
961 shortcut[i++] = 0;
962 line += i;
964 *command = 0;
966 /* get the command */
967 while (isspace(*line) && (*line != 0))
968 line++;
969 if (*line == 0)
970 return;
971 i = 0;
972 while (!isspace(line[i]) && (line[i] != 0))
973 i++;
974 strncpy(command, line, i);
975 command[i++] = 0;
976 line += i;
979 /* get the parameters */
980 while (isspace(*line) && (*line != 0))
981 line++;
982 if (*line == 0)
983 return;
985 if (*line == '"') {
986 line++;
987 l = 0;
988 while (line[l] != 0 && line[l] != '"') {
989 parameter[l] = line[l];
990 l++;
992 parameter[l] = 0;
993 return;
996 l = strlen(line);
997 while (isspace(line[l]) && (l > 0))
998 l--;
999 strncpy(parameter, line, l);
1000 parameter[l] = 0;
1003 static WMenu *parseCascade(WScreen * scr, WMenu * menu, FILE * file, char *file_name)
1005 char linebuf[MAXLINE];
1006 char elinebuf[MAXLINE];
1007 char title[MAXLINE];
1008 char command[MAXLINE];
1009 char shortcut[MAXLINE];
1010 char params[MAXLINE];
1011 char *line;
1013 while (!feof(file)) {
1014 int lsize, ok;
1016 ok = 0;
1017 fgets(linebuf, MAXLINE, file);
1018 line = cropline(linebuf);
1019 lsize = strlen(line);
1020 do {
1021 if (line[lsize - 1] == '\\') {
1022 char *line2;
1023 int lsize2;
1024 fgets(elinebuf, MAXLINE, file);
1025 line2 = cropline(elinebuf);
1026 lsize2 = strlen(line2);
1027 if (lsize2 + lsize > MAXLINE) {
1028 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1029 file_name, line);
1030 ok = 2;
1031 } else {
1032 line[lsize - 1] = 0;
1033 lsize += lsize2 - 1;
1034 strcat(line, line2);
1036 } else {
1037 ok = 1;
1039 } while (!ok && !feof(file));
1040 if (ok == 2)
1041 continue;
1043 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1044 continue;
1046 separateline(line, title, command, params, shortcut);
1048 if (!command[0]) {
1049 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1050 goto error;
1053 if (strcasecmp(command, "MENU") == 0) {
1054 WMenu *cascade;
1056 /* start submenu */
1058 cascade = wMenuCreate(scr, title, False);
1059 cascade->on_destroy = removeShortcutsForMenu;
1060 if (parseCascade(scr, cascade, file, file_name) == NULL) {
1061 wMenuDestroy(cascade, True);
1062 } else {
1063 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, title, NULL, NULL), cascade);
1065 } else if (strcasecmp(command, "END") == 0) {
1066 /* end of menu */
1067 return menu;
1069 } else {
1070 /* normal items */
1071 addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command,
1072 params[0] ? params : NULL, file_name);
1076 wwarning(_("%s:syntax error in menu file:END declaration missing"), file_name);
1077 return menu;
1079 error:
1080 return menu;
1083 static WMenu *readMenuFile(WScreen * scr, char *file_name)
1085 WMenu *menu = NULL;
1086 FILE *file = NULL;
1087 char linebuf[MAXLINE];
1088 char title[MAXLINE];
1089 char shortcut[MAXLINE];
1090 char command[MAXLINE];
1091 char params[MAXLINE];
1092 char *line;
1093 #ifdef USECPP
1094 char *args;
1095 int cpp = 0;
1096 #endif
1098 #ifdef USECPP
1099 if (!wPreferences.flags.nocpp) {
1100 args = MakeCPPArgs(file_name);
1101 if (!args) {
1102 wwarning(_("could not make arguments for menu file preprocessor"));
1103 } else {
1104 snprintf(command, sizeof(command), "%s %s %s", CPP_PATH, args, file_name);
1105 wfree(args);
1106 file = popen(command, "r");
1107 if (!file) {
1108 wsyserror(_("%s:could not open/preprocess menu file"), file_name);
1109 } else {
1110 cpp = 1;
1114 #endif /* USECPP */
1116 if (!file) {
1117 file = fopen(file_name, "rb");
1118 if (!file) {
1119 wsyserror(_("%s:could not open menu file"), file_name);
1120 return NULL;
1124 while (!feof(file)) {
1125 if (!fgets(linebuf, MAXLINE, file))
1126 break;
1127 line = cropline(linebuf);
1128 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1129 continue;
1131 separateline(line, title, command, params, shortcut);
1133 if (!command[0]) {
1134 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1135 break;
1137 if (strcasecmp(command, "MENU") == 0) {
1138 menu = wMenuCreate(scr, title, True);
1139 menu->on_destroy = removeShortcutsForMenu;
1140 if (!parseCascade(scr, menu, file, file_name)) {
1141 wMenuDestroy(menu, True);
1143 break;
1144 } else {
1145 wwarning(_("%s:invalid menu file. MENU command is missing"), file_name);
1146 break;
1150 #ifdef CPP
1151 if (cpp) {
1152 if (pclose(file) == -1) {
1153 wsyserror(_("error reading preprocessed menu data"));
1155 } else {
1156 fclose(file);
1158 #else
1159 fclose(file);
1160 #endif
1162 return menu;
1165 /************ Menu Configuration From Pipe *************/
1167 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1169 WMenu *menu = NULL;
1170 FILE *file = NULL;
1171 char linebuf[MAXLINE];
1172 char title[MAXLINE];
1173 char command[MAXLINE];
1174 char params[MAXLINE];
1175 char shortcut[MAXLINE];
1176 char *line;
1177 char *filename;
1178 char flat_file[MAXLINE];
1179 int i;
1180 #ifdef USECPP
1181 char *args;
1182 int cpp = 0;
1183 #endif
1185 flat_file[0] = '\0';
1187 for (i = 0; file_name[i] != NULL; i++) {
1188 strcat(flat_file, file_name[i]);
1189 strcat(flat_file, " ");
1191 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1193 #ifdef USECPP
1194 if (!wPreferences.flags.nocpp) {
1195 args = MakeCPPArgs(filename);
1196 if (!args) {
1197 wwarning(_("could not make arguments for menu file preprocessor"));
1198 } else {
1199 snprintf(command, sizeof(command), "%s | %s %s", filename, CPP_PATH, args);
1201 wfree(args);
1202 file = popen(command, "r");
1203 if (!file) {
1204 wsyserror(_("%s:could not open/preprocess menu file"), filename);
1205 } else {
1206 cpp = 1;
1210 #endif /* USECPP */
1212 if (!file) {
1213 file = popen(filename, "rb");
1215 if (!file) {
1216 wsyserror(_("%s:could not open menu file"), filename);
1217 return NULL;
1221 while (!feof(file)) {
1222 if (!fgets(linebuf, MAXLINE, file))
1223 break;
1224 line = cropline(linebuf);
1225 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1226 continue;
1228 separateline(line, title, command, params, shortcut);
1230 if (!command[0]) {
1231 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1232 break;
1234 if (strcasecmp(command, "MENU") == 0) {
1235 menu = wMenuCreate(scr, title, True);
1236 menu->on_destroy = removeShortcutsForMenu;
1237 if (!parseCascade(scr, menu, file, filename)) {
1238 wMenuDestroy(menu, True);
1240 break;
1241 } else {
1242 wwarning(_("%s:no title given for the root menu"), filename);
1243 break;
1247 pclose(file);
1249 return menu;
1252 typedef struct {
1253 char *name;
1254 int index;
1255 } dir_data;
1257 static int myCompare(const void *d1, const void *d2)
1259 dir_data *p1 = *(dir_data **) d1;
1260 dir_data *p2 = *(dir_data **) d2;
1262 return strcmp(p1->name, p2->name);
1265 /************ Menu Configuration From Directory *************/
1267 static Bool isFilePackage(char *file)
1269 int l;
1271 /* check if the extension indicates this file is a
1272 * file package. For now, only recognize .themed */
1274 l = strlen(file);
1276 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1277 return True;
1278 } else {
1279 return False;
1283 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
1285 DIR *dir;
1286 struct dirent *dentry;
1287 struct stat stat_buf;
1288 WMenu *menu = NULL;
1289 char *buffer;
1290 WMArray *dirs = NULL, *files = NULL;
1291 WMArrayIterator iter;
1292 int length, i, have_space = 0;
1293 dir_data *data;
1294 int stripExtension = 0;
1296 dirs = WMCreateArray(16);
1297 files = WMCreateArray(16);
1299 i = 0;
1300 while (path[i] != NULL) {
1301 if (strcmp(path[i], "-noext") == 0) {
1302 stripExtension = 1;
1303 i++;
1304 continue;
1307 dir = opendir(path[i]);
1308 if (!dir) {
1309 i++;
1310 continue;
1313 while ((dentry = readdir(dir))) {
1315 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1316 continue;
1318 if (dentry->d_name[0] == '.')
1319 continue;
1321 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1322 if (!buffer) {
1323 wsyserror(_("out of memory while constructing directory menu %s"), path[i]);
1324 break;
1327 strcpy(buffer, path[i]);
1328 strcat(buffer, "/");
1329 strcat(buffer, dentry->d_name);
1331 if (stat(buffer, &stat_buf) != 0) {
1332 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1333 path[i], dentry->d_name);
1334 } else {
1335 Bool isFilePack = False;
1337 data = NULL;
1338 if (S_ISDIR(stat_buf.st_mode)
1339 && !(isFilePack = isFilePackage(dentry->d_name))) {
1341 /* access always returns success for user root */
1342 if (access(buffer, X_OK) == 0) {
1343 /* Directory is accesible. Add to directory list */
1345 data = (dir_data *) wmalloc(sizeof(dir_data));
1346 data->name = wstrdup(dentry->d_name);
1347 data->index = i;
1349 WMAddToArray(dirs, data);
1351 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1352 /* Hack because access always returns X_OK success for user root */
1353 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1354 if ((command != NULL && access(buffer, R_OK) == 0) ||
1355 (command == NULL && access(buffer, X_OK) == 0 &&
1356 (stat_buf.st_mode & S_IXANY))) {
1358 data = (dir_data *) wmalloc(sizeof(dir_data));
1359 data->name = wstrdup(dentry->d_name);
1360 data->index = i;
1362 WMAddToArray(files, data);
1366 wfree(buffer);
1369 closedir(dir);
1370 i++;
1373 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1374 WMFreeArray(dirs);
1375 WMFreeArray(files);
1376 return NULL;
1379 WMSortArray(dirs, myCompare);
1380 WMSortArray(files, myCompare);
1382 menu = wMenuCreate(scr, title, False);
1383 menu->on_destroy = removeShortcutsForMenu;
1385 WM_ITERATE_ARRAY(dirs, data, iter) {
1386 /* New directory. Use same OPEN_MENU command that was used
1387 * for the current directory. */
1388 length = strlen(path[data->index]) + strlen(data->name) + 6;
1389 if (stripExtension)
1390 length += 7;
1391 if (command)
1392 length += strlen(command) + 6;
1393 buffer = malloc(length);
1394 if (!buffer) {
1395 wsyserror(_("out of memory while constructing directory menu %s"), path[data->index]);
1396 break;
1399 buffer[0] = '\0';
1400 if (stripExtension)
1401 strcat(buffer, "-noext ");
1403 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1405 if (have_space)
1406 strcat(buffer, "\"");
1407 strcat(buffer, path[data->index]);
1409 strcat(buffer, "/");
1410 strcat(buffer, data->name);
1411 if (have_space)
1412 strcat(buffer, "\"");
1413 if (command) {
1414 strcat(buffer, " WITH ");
1415 strcat(buffer, command);
1418 addMenuEntry(menu, data->name, NULL, "OPEN_MENU", buffer, path[data->index]);
1420 wfree(buffer);
1421 if (data->name)
1422 wfree(data->name);
1423 wfree(data);
1426 WM_ITERATE_ARRAY(files, data, iter) {
1427 /* executable: add as entry */
1428 length = strlen(path[data->index]) + strlen(data->name) + 6;
1429 if (command)
1430 length += strlen(command);
1432 buffer = malloc(length);
1433 if (!buffer) {
1434 wsyserror(_("out of memory while constructing directory menu %s"), path[data->index]);
1435 break;
1438 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1439 if (command != NULL) {
1440 strcpy(buffer, command);
1441 strcat(buffer, " ");
1442 if (have_space)
1443 strcat(buffer, "\"");
1444 strcat(buffer, path[data->index]);
1445 } else {
1446 if (have_space) {
1447 buffer[0] = '"';
1448 buffer[1] = 0;
1449 strcat(buffer, path[data->index]);
1450 } else {
1451 strcpy(buffer, path[data->index]);
1454 strcat(buffer, "/");
1455 strcat(buffer, data->name);
1456 if (have_space)
1457 strcat(buffer, "\"");
1459 if (stripExtension) {
1460 char *ptr = strrchr(data->name, '.');
1461 if (ptr && ptr != data->name)
1462 *ptr = 0;
1464 addMenuEntry(menu, data->name, NULL, "SHEXEC", buffer, path[data->index]);
1466 wfree(buffer);
1467 if (data->name)
1468 wfree(data->name);
1469 wfree(data);
1472 WMFreeArray(files);
1473 WMFreeArray(dirs);
1475 return menu;
1478 /************ Menu Configuration From WMRootMenu *************/
1480 static WMenu *makeDefaultMenu(WScreen * scr)
1482 WMenu *menu = NULL;
1484 menu = wMenuCreate(scr, _("Commands"), True);
1485 wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
1486 wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
1487 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1488 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1489 return menu;
1493 *----------------------------------------------------------------------
1494 * configureMenu--
1495 * Reads root menu configuration from defaults database.
1497 *----------------------------------------------------------------------
1499 static WMenu *configureMenu(WScreen * scr, WMPropList * definition)
1501 WMenu *menu = NULL;
1502 WMPropList *elem;
1503 int i, count;
1504 WMPropList *title, *command, *params;
1505 char *tmp, *mtitle;
1507 if (WMIsPLString(definition)) {
1508 struct stat stat_buf;
1509 char *path = NULL;
1510 Bool menu_is_default = False;
1512 /* menu definition is a string. Probably a path, so parse the file */
1514 tmp = wexpandpath(WMGetFromPLString(definition));
1516 path = getLocalizedMenuFile(tmp);
1518 if (!path)
1519 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1521 if (!path) {
1522 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1523 menu_is_default = True;
1526 if (!path) {
1527 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1528 wfree(tmp);
1529 return NULL;
1532 if (stat(path, &stat_buf) < 0) {
1533 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1534 wfree(path);
1535 wfree(tmp);
1536 return NULL;
1539 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1540 /* if the pointer in WMRootMenu has changed */
1541 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1543 if (menu_is_default) {
1544 wwarning(_
1545 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1546 path);
1549 menu = readMenuFile(scr, path);
1550 if (menu)
1551 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1552 } else {
1553 menu = NULL;
1555 wfree(path);
1556 wfree(tmp);
1558 return menu;
1561 count = WMGetPropListItemCount(definition);
1562 if (count == 0)
1563 return NULL;
1565 elem = WMGetFromPLArray(definition, 0);
1566 if (!WMIsPLString(elem)) {
1567 tmp = WMGetPropListDescription(elem, False);
1568 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1569 wfree(tmp);
1570 return NULL;
1572 mtitle = WMGetFromPLString(elem);
1574 menu = wMenuCreate(scr, mtitle, False);
1575 menu->on_destroy = removeShortcutsForMenu;
1577 #ifdef GLOBAL_SUBMENU_FILE
1579 WMenu *submenu;
1580 WMenuEntry *mentry;
1582 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1584 if (submenu) {
1585 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1586 wMenuEntrySetCascade(menu, mentry, submenu);
1589 #endif
1591 for (i = 1; i < count; i++) {
1592 elem = WMGetFromPLArray(definition, i);
1593 #if 0
1594 if (WMIsPLString(elem)) {
1595 char *file;
1597 file = WMGetFromPLString(elem);
1600 #endif
1601 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1602 goto error;
1604 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1605 WMenu *submenu;
1606 WMenuEntry *mentry;
1608 /* submenu */
1609 submenu = configureMenu(scr, elem);
1610 if (submenu) {
1611 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1612 wMenuEntrySetCascade(menu, mentry, submenu);
1614 } else {
1615 int idx = 0;
1616 WMPropList *shortcut;
1617 /* normal entry */
1619 title = WMGetFromPLArray(elem, idx++);
1620 shortcut = WMGetFromPLArray(elem, idx++);
1621 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1622 shortcut = WMGetFromPLArray(elem, idx++);
1623 command = WMGetFromPLArray(elem, idx++);
1624 } else {
1625 command = shortcut;
1626 shortcut = NULL;
1628 params = WMGetFromPLArray(elem, idx++);
1630 if (!title || !command)
1631 goto error;
1633 addMenuEntry(menu, WMGetFromPLString(title),
1634 shortcut ? WMGetFromPLString(shortcut) : NULL,
1635 WMGetFromPLString(command),
1636 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1638 continue;
1640 error:
1641 tmp = WMGetPropListDescription(elem, False);
1642 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1643 wfree(tmp);
1646 return menu;
1650 *----------------------------------------------------------------------
1651 * OpenRootMenu--
1652 * Opens the root menu, parsing the menu configuration from the
1653 * defaults database.
1654 * If the menu is already mapped and is not sticked to the
1655 * root window, it will be unmapped.
1657 * Side effects:
1658 * The menu may be remade.
1660 * Notes:
1661 * Construction of OPEN_MENU entries are delayed to the moment the
1662 * user map's them.
1663 *----------------------------------------------------------------------
1665 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1667 WMenu *menu = NULL;
1668 WMPropList *definition;
1670 static WMPropList *domain=NULL;
1672 if (!domain) {
1673 domain = WMCreatePLString("WMRootMenu");
1677 scr->flags.root_menu_changed_shortcuts = 0;
1678 scr->flags.added_workspace_menu = 0;
1679 scr->flags.added_windows_menu = 0;
1681 if (scr->root_menu && scr->root_menu->flags.mapped) {
1682 menu = scr->root_menu;
1683 if (!menu->flags.buttoned) {
1684 wMenuUnmap(menu);
1685 } else {
1686 wRaiseFrame(menu->frame->core);
1688 if (keyboard)
1689 wMenuMapAt(menu, 0, 0, True);
1690 else
1691 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1693 return;
1696 definition = WDRootMenu->dictionary;
1699 definition = PLGetDomain(domain);
1701 if (definition) {
1702 if (WMIsPLArray(definition)) {
1703 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1704 menu = configureMenu(scr, definition);
1705 if (menu)
1706 menu->timestamp = WDRootMenu->timestamp;
1708 } else
1709 menu = NULL;
1710 } else {
1711 menu = configureMenu(scr, definition);
1715 if (!menu) {
1716 /* menu hasn't changed or could not be read */
1717 if (!scr->root_menu) {
1718 wMessageDialog(scr, _("Error"),
1719 _("The applications menu could not be loaded. "
1720 "Look at the console output for a detailed "
1721 "description of the errors."), _("OK"), NULL, NULL);
1723 menu = makeDefaultMenu(scr);
1724 scr->root_menu = menu;
1726 menu = scr->root_menu;
1727 } else {
1728 /* new root menu */
1729 if (scr->root_menu) {
1730 wMenuDestroy(scr->root_menu, True);
1732 scr->root_menu = menu;
1734 if (menu) {
1735 int newx, newy;
1737 if (keyboard && x == 0 && y == 0) {
1738 newx = newy = 0;
1739 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1740 newx = x - menu->frame->core->width / 2;
1741 newy = y - menu->frame->core->height / 2;
1742 } else {
1743 newx = x - menu->frame->core->width / 2;
1744 newy = y;
1746 wMenuMapAt(menu, newx, newy, keyboard);
1749 if (scr->flags.root_menu_changed_shortcuts)
1750 rebindKeygrabs(scr);