Add history to some dialog boxes
[wmaker-crm.git] / src / rootmenu.c
blobf3a521f44c572c024065111b00b6ef541e6f4b0e
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 {
225 #ifdef XSMP_ENABLED
226 if (wSessionIsManaged()) {
227 int r;
229 r = wMessageDialog(menu->frame->screen_ptr,
230 _("Close X session"),
231 _("Close Window System session?\n"
232 "Kill might close applications with unsaved data."),
233 _("Close"), _("Kill"), _("Cancel"));
234 if (r == WAPRDefault)
235 result = R_CLOSE;
236 else if (r == WAPRAlternate)
237 result = R_KILL;
238 } else
239 #endif
241 int r, oldSaveSessionFlag;
243 oldSaveSessionFlag = wPreferences.save_session_on_exit;
245 r = wExitDialog(menu->frame->screen_ptr,
246 _("Kill X session"),
247 _("Kill Window System session?\n"
248 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
249 if (r == WAPRDefault) {
250 result = R_KILL;
251 } else if (r == WAPRAlternate) {
252 /* Don't modify the "save session on exit" flag if the
253 * user canceled the operation. */
254 wPreferences.save_session_on_exit = oldSaveSessionFlag;
259 if (result != R_CANCEL) {
260 #ifdef XSMP_ENABLED
261 if (result == R_CLOSE) {
262 Shutdown(WSLogoutMode);
263 } else
264 #endif /* XSMP_ENABLED */
266 Shutdown(WSKillMode);
269 #undef R_CLOSE
270 #undef R_CANCEL
271 #undef R_KILL
272 inside = 0;
275 static void restartCommand(WMenu * menu, WMenuEntry * entry)
277 Shutdown(WSRestartPreparationMode);
278 Restart((char *)entry->clientdata, False);
279 Restart(NULL, True);
282 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
284 wRefreshDesktop(menu->frame->screen_ptr);
287 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
289 wArrangeIcons(menu->frame->screen_ptr, True);
292 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
294 wShowAllWindows(menu->frame->screen_ptr);
297 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
299 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
302 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
304 if (!wPreferences.save_session_on_exit)
305 wSessionSaveState(menu->frame->screen_ptr);
307 wScreenSaveState(menu->frame->screen_ptr);
310 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
312 wSessionClearState(menu->frame->screen_ptr);
313 wScreenSaveState(menu->frame->screen_ptr);
316 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
318 wShowInfoPanel(menu->frame->screen_ptr);
321 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
323 wShowLegalPanel(menu->frame->screen_ptr);
326 /********************************************************************/
328 static char *getLocalizedMenuFile(char *menu)
330 char *buffer, *ptr, *locale;
331 int len;
333 if (!Locale)
334 return NULL;
336 len = strlen(menu) + strlen(Locale) + 8;
337 buffer = wmalloc(len);
339 /* try menu.locale_name */
340 snprintf(buffer, len, "%s.%s", menu, Locale);
341 if (access(buffer, F_OK) == 0) {
342 return buffer;
345 /* position of locale in our buffer */
346 locale = buffer + strlen(menu) + 1;
348 /* check if it is in the form aa_bb.encoding and check for aa_bb */
349 ptr = strchr(locale, '.');
350 if (ptr) {
351 *ptr = 0;
352 if (access(buffer, F_OK) == 0) {
353 return buffer;
356 /* now check for aa */
357 ptr = strchr(locale, '_');
358 if (ptr) {
359 *ptr = 0;
360 if (access(buffer, F_OK) == 0) {
361 return buffer;
365 wfree(buffer);
367 return NULL;
370 static void raiseMenus(WMenu * menu)
372 int i;
374 if (menu->flags.mapped) {
375 wRaiseFrame(menu->frame->core);
377 for (i = 0; i < menu->cascade_no; i++) {
378 if (menu->cascades[i])
379 raiseMenus(menu->cascades[i]);
383 Bool wRootMenuPerformShortcut(XEvent * event)
385 WScreen *scr = wScreenForRootWindow(event->xkey.root);
386 Shortcut *ptr;
387 int modifiers;
388 int done = 0;
390 /* ignore CapsLock */
391 modifiers = event->xkey.state & ValidModMask;
393 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
394 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
395 continue;
397 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
398 (*ptr->entry->callback) (ptr->menu, ptr->entry);
399 done = True;
403 return done;
406 void wRootMenuBindShortcuts(Window window)
408 Shortcut *ptr;
410 ptr = shortcutList;
411 while (ptr) {
412 if (ptr->modifier != AnyModifier) {
413 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
414 window, True, GrabModeAsync, GrabModeAsync);
415 #ifdef NUMLOCK_HACK
416 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
417 #endif
419 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
420 ptr = ptr->next;
424 static void rebindKeygrabs(WScreen * scr)
426 WWindow *wwin;
428 wwin = scr->focused_window;
430 while (wwin != NULL) {
431 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
433 if (!WFLAGP(wwin, no_bind_keys)) {
434 wWindowSetKeyGrabs(wwin);
436 wwin = wwin->prev;
440 static void removeShortcutsForMenu(WMenu * menu)
442 Shortcut *ptr, *tmp;
443 Shortcut *newList = NULL;
445 ptr = shortcutList;
446 while (ptr != NULL) {
447 tmp = ptr->next;
448 if (ptr->menu == menu) {
449 wfree(ptr);
450 } else {
451 ptr->next = newList;
452 newList = ptr;
454 ptr = tmp;
456 shortcutList = newList;
457 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
460 static Bool addShortcut(char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
462 Shortcut *ptr;
463 KeySym ksym;
464 char *k;
465 char buf[MAX_SHORTCUT_LENGTH], *b;
467 ptr = wmalloc(sizeof(Shortcut));
469 strncpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
470 b = (char *)buf;
472 /* get modifiers */
473 ptr->modifier = 0;
474 while ((k = strchr(b, '+')) != NULL) {
475 int mod;
477 *k = 0;
478 mod = wXModifierFromKey(b);
479 if (mod < 0) {
480 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
481 wfree(ptr);
482 return False;
484 ptr->modifier |= mod;
486 b = k + 1;
489 /* get key */
490 ksym = XStringToKeysym(b);
492 if (ksym == NoSymbol) {
493 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
494 file, shortcutDefinition, entry->text);
495 wfree(ptr);
496 return False;
499 ptr->keycode = XKeysymToKeycode(dpy, ksym);
500 if (ptr->keycode == 0) {
501 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
502 shortcutDefinition, entry->text);
503 wfree(ptr);
504 return False;
507 ptr->menu = menu;
508 ptr->entry = entry;
510 ptr->next = shortcutList;
511 shortcutList = ptr;
513 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
515 return True;
518 /*******************************/
520 static char *cropline(char *line)
522 char *end;
524 if (strlen(line) == 0)
525 return line;
527 end = &(line[strlen(line)]) - 1;
528 while (isspace(*line) && *line != 0)
529 line++;
530 while (end > line && isspace(*end)) {
531 *end = 0;
532 end--;
534 return line;
537 static char *next_token(char *line, char **next)
539 char *tmp, c;
540 char *ret;
542 *next = NULL;
543 while (*line == ' ' || *line == '\t')
544 line++;
546 tmp = line;
548 if (*tmp == '"') {
549 tmp++;
550 line++;
551 while (*tmp != 0 && *tmp != '"')
552 tmp++;
553 if (*tmp != '"') {
554 wwarning(_("%s: unmatched '\"' in menu file"), line);
555 return NULL;
557 } else {
558 do {
559 if (*tmp == '\\')
560 tmp++;
562 if (*tmp != 0)
563 tmp++;
565 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
568 c = *tmp;
569 *tmp = 0;
570 ret = wstrdup(line);
571 *tmp = c;
573 if (c == 0)
574 return ret;
575 else
576 tmp++;
578 /* skip blanks */
579 while (*tmp == ' ' || *tmp == '\t')
580 tmp++;
582 if (*tmp != 0)
583 *next = tmp;
585 return ret;
588 static void separateCommand(char *line, char ***file, char **command)
590 char *token, *tmp = line;
591 WMArray *array = WMCreateArray(4);
592 int count, i;
594 *file = NULL;
595 *command = NULL;
596 do {
597 token = next_token(tmp, &tmp);
598 if (token) {
599 if (strcmp(token, "WITH") == 0) {
600 if (tmp != NULL && *tmp != 0)
601 *command = wstrdup(tmp);
602 else
603 wwarning(_("%s: missing command"), line);
604 break;
606 WMAddToArray(array, token);
608 } while (token != NULL && tmp != NULL);
610 count = WMGetArrayItemCount(array);
611 if (count > 0) {
612 *file = wmalloc(sizeof(char *) * (count + 1));
613 (*file)[count] = NULL;
614 for (i = 0; i < count; i++) {
615 (*file)[i] = WMGetFromArray(array, i);
618 WMFreeArray(array);
621 static void constructMenu(WMenu * menu, WMenuEntry * entry)
623 WMenu *submenu;
624 struct stat stat_buf;
625 char **path;
626 char *cmd;
627 char *lpath = NULL;
628 int i, first = -1;
629 time_t last = 0;
631 separateCommand((char *)entry->clientdata, &path, &cmd);
632 if (path == NULL || *path == NULL || **path == 0) {
633 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
634 return;
637 if (path[0][0] == '|') {
638 /* pipe menu */
640 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
641 /* parse pipe */
643 submenu = readMenuPipe(menu->frame->screen_ptr, path);
645 if (submenu != NULL) {
646 if (path[0][1] == '|')
647 submenu->timestamp = 0;
648 else
649 submenu->timestamp = 1; /* there's no automatic reloading */
651 } else {
652 submenu = NULL;
655 } else {
656 i = 0;
657 while (path[i] != NULL) {
658 char *tmp;
660 if (strcmp(path[i], "-noext") == 0) {
661 i++;
662 continue;
665 tmp = wexpandpath(path[i]);
666 wfree(path[i]);
667 lpath = getLocalizedMenuFile(tmp);
668 if (lpath) {
669 wfree(tmp);
670 path[i] = lpath;
671 lpath = NULL;
672 } else {
673 path[i] = tmp;
676 if (stat(path[i], &stat_buf) == 0) {
677 if (last < stat_buf.st_mtime)
678 last = stat_buf.st_mtime;
679 if (first < 0)
680 first = i;
681 } else {
682 wsyserror(_("%s:could not stat menu"), path[i]);
683 /*goto finish; */
686 i++;
689 if (first < 0) {
690 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
691 goto finish;
693 stat(path[first], &stat_buf);
694 if (!menu->cascades[entry->cascade]
695 || menu->cascades[entry->cascade]->timestamp < last) {
697 if (S_ISDIR(stat_buf.st_mode)) {
698 /* menu directory */
699 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
700 if (submenu)
701 submenu->timestamp = last;
702 } else if (S_ISREG(stat_buf.st_mode)) {
703 /* menu file */
705 if (cmd || path[1])
706 wwarning(_("too many parameters in OPEN_MENU: %s"),
707 (char *)entry->clientdata);
709 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
710 if (submenu)
711 submenu->timestamp = stat_buf.st_mtime;
712 } else {
713 submenu = NULL;
715 } else {
716 submenu = NULL;
720 if (submenu) {
721 wMenuEntryRemoveCascade(menu, entry);
722 wMenuEntrySetCascade(menu, entry, submenu);
725 finish:
726 i = 0;
727 while (path[i] != NULL)
728 wfree(path[i++]);
729 wfree(path);
730 if (cmd)
731 wfree(cmd);
734 static void cleanupWorkspaceMenu(WMenu * menu)
736 if (menu->frame->screen_ptr->workspace_menu == menu)
737 menu->frame->screen_ptr->workspace_menu = NULL;
740 static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
742 WMenu *wsmenu;
743 WMenuEntry *entry;
745 if (scr->flags.added_workspace_menu) {
746 wwarning(_
747 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
748 return NULL;
749 } else {
750 scr->flags.added_workspace_menu = 1;
752 wsmenu = wWorkspaceMenuMake(scr, True);
753 wsmenu->on_destroy = cleanupWorkspaceMenu;
755 scr->workspace_menu = wsmenu;
756 entry = wMenuAddCallback(menu, title, NULL, NULL);
757 wMenuEntrySetCascade(menu, entry, wsmenu);
759 wWorkspaceMenuUpdate(scr, wsmenu);
761 return entry;
764 static void cleanupWindowsMenu(WMenu * menu)
766 if (menu->frame->screen_ptr->switch_menu == menu)
767 menu->frame->screen_ptr->switch_menu = NULL;
770 static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
772 WMenu *wwmenu;
773 WWindow *wwin;
774 WMenuEntry *entry;
776 if (scr->flags.added_windows_menu) {
777 wwarning(_
778 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
779 return NULL;
780 } else {
781 scr->flags.added_windows_menu = 1;
783 wwmenu = wMenuCreate(scr, _("Window List"), False);
784 wwmenu->on_destroy = cleanupWindowsMenu;
785 scr->switch_menu = wwmenu;
786 wwin = scr->focused_window;
787 while (wwin) {
788 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
790 wwin = wwin->prev;
792 entry = wMenuAddCallback(menu, title, NULL, NULL);
793 wMenuEntrySetCascade(menu, entry, wwmenu);
795 return entry;
798 static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
799 char *params, char *file_name)
801 WScreen *scr;
802 WMenuEntry *entry = NULL;
803 Bool shortcutOk = False;
805 if (!menu)
806 return NULL;
807 scr = menu->frame->screen_ptr;
808 if (strcmp(command, "OPEN_MENU") == 0) {
809 if (!params) {
810 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
811 } else {
812 WMenu *dummy;
813 char *path;
815 path = wfindfile(DEF_CONFIG_PATHS, params);
816 if (!path) {
817 path = wstrdup(params);
819 dummy = wMenuCreate(scr, title, False);
820 dummy->on_destroy = removeShortcutsForMenu;
821 entry = wMenuAddCallback(menu, title, constructMenu, path);
822 entry->free_cdata = free;
823 wMenuEntrySetCascade(menu, entry, dummy);
825 } else if (strcmp(command, "EXEC") == 0) {
826 if (!params)
827 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
828 else {
829 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
830 entry->free_cdata = free;
831 shortcutOk = True;
833 } else if (strcmp(command, "SHEXEC") == 0) {
834 if (!params)
835 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
836 else {
837 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
838 entry->free_cdata = free;
839 shortcutOk = True;
841 } else if (strcmp(command, "EXIT") == 0) {
843 if (params && strcmp(params, "QUICK") == 0)
844 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
845 else
846 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
848 shortcutOk = True;
849 } else if (strcmp(command, "SHUTDOWN") == 0) {
851 if (params && strcmp(params, "QUICK") == 0)
852 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
853 else
854 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
856 shortcutOk = True;
857 } else if (strcmp(command, "REFRESH") == 0) {
858 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
860 shortcutOk = True;
861 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
862 entry = addWorkspaceMenu(scr, menu, title);
864 shortcutOk = True;
865 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
866 entry = addWindowsMenu(scr, menu, title);
868 shortcutOk = True;
869 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
870 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
872 shortcutOk = True;
873 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
874 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
876 shortcutOk = True;
877 } else if (strcmp(command, "SHOW_ALL") == 0) {
878 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
880 shortcutOk = True;
881 } else if (strcmp(command, "RESTART") == 0) {
882 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
883 entry->free_cdata = free;
884 shortcutOk = True;
885 } else if (strcmp(command, "SAVE_SESSION") == 0) {
886 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
888 shortcutOk = True;
889 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
890 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
891 shortcutOk = True;
892 } else if (strcmp(command, "INFO_PANEL") == 0) {
893 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
894 shortcutOk = True;
895 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
896 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
897 shortcutOk = True;
898 } else {
899 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
901 return NULL;
904 if (shortcut && entry) {
905 if (!shortcutOk) {
906 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
907 } else {
908 if (addShortcut(file_name, shortcut, menu, entry)) {
910 entry->rtext = GetShortcutString(shortcut);
912 entry->rtext = wstrdup(shortcut);
918 return entry;
921 /******************* Menu Configuration From File *******************/
923 static void separateline(char *line, char *title, char *command, char *parameter, char *shortcut)
925 int l, i;
927 l = strlen(line);
929 *title = 0;
930 *command = 0;
931 *parameter = 0;
932 *shortcut = 0;
933 /* get the title */
934 while (isspace(*line) && (*line != 0))
935 line++;
936 if (*line == '"') {
937 line++;
938 i = 0;
939 while (line[i] != '"' && (line[i] != 0))
940 i++;
941 if (line[i] != '"')
942 return;
943 } else {
944 i = 0;
945 while (!isspace(line[i]) && (line[i] != 0))
946 i++;
948 strncpy(title, line, i);
949 title[i++] = 0;
950 line += i;
952 /* get the command or shortcut keyword */
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;
964 if (strcmp(command, "SHORTCUT") == 0) {
965 /* get the shortcut key */
966 while (isspace(*line) && (*line != 0))
967 line++;
968 if (*line == '"') {
969 line++;
970 i = 0;
971 while (line[i] != '"' && (line[i] != 0))
972 i++;
973 if (line[i] != '"')
974 return;
975 } else {
976 i = 0;
977 while (!isspace(line[i]) && (line[i] != 0))
978 i++;
980 strncpy(shortcut, line, i);
981 shortcut[i++] = 0;
982 line += i;
984 *command = 0;
986 /* get the command */
987 while (isspace(*line) && (*line != 0))
988 line++;
989 if (*line == 0)
990 return;
991 i = 0;
992 while (!isspace(line[i]) && (line[i] != 0))
993 i++;
994 strncpy(command, line, i);
995 command[i++] = 0;
996 line += i;
999 /* get the parameters */
1000 while (isspace(*line) && (*line != 0))
1001 line++;
1002 if (*line == 0)
1003 return;
1005 if (*line == '"') {
1006 line++;
1007 l = 0;
1008 while (line[l] != 0 && line[l] != '"') {
1009 parameter[l] = line[l];
1010 l++;
1012 parameter[l] = 0;
1013 return;
1016 l = strlen(line);
1017 while (isspace(line[l]) && (l > 0))
1018 l--;
1019 strncpy(parameter, line, l);
1020 parameter[l] = 0;
1023 static WMenu *parseCascade(WScreen * scr, WMenu * menu, FILE * file, char *file_name)
1025 char linebuf[MAXLINE];
1026 char elinebuf[MAXLINE];
1027 char title[MAXLINE];
1028 char command[MAXLINE];
1029 char shortcut[MAXLINE];
1030 char params[MAXLINE];
1031 char *line;
1033 while (!feof(file)) {
1034 int lsize, ok;
1036 ok = 0;
1037 fgets(linebuf, MAXLINE, file);
1038 line = cropline(linebuf);
1039 lsize = strlen(line);
1040 do {
1041 if (line[lsize - 1] == '\\') {
1042 char *line2;
1043 int lsize2;
1044 fgets(elinebuf, MAXLINE, file);
1045 line2 = cropline(elinebuf);
1046 lsize2 = strlen(line2);
1047 if (lsize2 + lsize > MAXLINE) {
1048 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1049 file_name, line);
1050 ok = 2;
1051 } else {
1052 line[lsize - 1] = 0;
1053 lsize += lsize2 - 1;
1054 strcat(line, line2);
1056 } else {
1057 ok = 1;
1059 } while (!ok && !feof(file));
1060 if (ok == 2)
1061 continue;
1063 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1064 continue;
1066 separateline(line, title, command, params, shortcut);
1068 if (!command[0]) {
1069 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1070 goto error;
1073 if (strcasecmp(command, "MENU") == 0) {
1074 WMenu *cascade;
1076 /* start submenu */
1078 cascade = wMenuCreate(scr, title, False);
1079 cascade->on_destroy = removeShortcutsForMenu;
1080 if (parseCascade(scr, cascade, file, file_name) == NULL) {
1081 wMenuDestroy(cascade, True);
1082 } else {
1083 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, title, NULL, NULL), cascade);
1085 } else if (strcasecmp(command, "END") == 0) {
1086 /* end of menu */
1087 return menu;
1089 } else {
1090 /* normal items */
1091 addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command,
1092 params[0] ? params : NULL, file_name);
1096 wwarning(_("%s:syntax error in menu file:END declaration missing"), file_name);
1097 return menu;
1099 error:
1100 return menu;
1103 static WMenu *readMenuFile(WScreen * scr, char *file_name)
1105 WMenu *menu = NULL;
1106 FILE *file = NULL;
1107 char linebuf[MAXLINE];
1108 char title[MAXLINE];
1109 char shortcut[MAXLINE];
1110 char command[MAXLINE];
1111 char params[MAXLINE];
1112 char *line;
1113 #ifdef USECPP
1114 char *args;
1115 int cpp = 0;
1116 #endif
1118 #ifdef USECPP
1119 if (!wPreferences.flags.nocpp) {
1120 args = MakeCPPArgs(file_name);
1121 if (!args) {
1122 wwarning(_("could not make arguments for menu file preprocessor"));
1123 } else {
1124 snprintf(command, sizeof(command), "%s %s %s", CPP_PATH, args, file_name);
1125 wfree(args);
1126 file = popen(command, "r");
1127 if (!file) {
1128 wsyserror(_("%s:could not open/preprocess menu file"), file_name);
1129 } else {
1130 cpp = 1;
1134 #endif /* USECPP */
1136 if (!file) {
1137 file = fopen(file_name, "rb");
1138 if (!file) {
1139 wsyserror(_("%s:could not open menu file"), file_name);
1140 return NULL;
1144 while (!feof(file)) {
1145 if (!fgets(linebuf, MAXLINE, file))
1146 break;
1147 line = cropline(linebuf);
1148 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1149 continue;
1151 separateline(line, title, command, params, shortcut);
1153 if (!command[0]) {
1154 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1155 break;
1157 if (strcasecmp(command, "MENU") == 0) {
1158 menu = wMenuCreate(scr, title, True);
1159 menu->on_destroy = removeShortcutsForMenu;
1160 if (!parseCascade(scr, menu, file, file_name)) {
1161 wMenuDestroy(menu, True);
1163 break;
1164 } else {
1165 wwarning(_("%s:invalid menu file. MENU command is missing"), file_name);
1166 break;
1170 #ifdef CPP
1171 if (cpp) {
1172 if (pclose(file) == -1) {
1173 wsyserror(_("error reading preprocessed menu data"));
1175 } else {
1176 fclose(file);
1178 #else
1179 fclose(file);
1180 #endif
1182 return menu;
1185 /************ Menu Configuration From Pipe *************/
1187 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1189 WMenu *menu = NULL;
1190 FILE *file = NULL;
1191 char linebuf[MAXLINE];
1192 char title[MAXLINE];
1193 char command[MAXLINE];
1194 char params[MAXLINE];
1195 char shortcut[MAXLINE];
1196 char *line;
1197 char *filename;
1198 char flat_file[MAXLINE];
1199 int i;
1200 #ifdef USECPP
1201 char *args;
1202 int cpp = 0;
1203 #endif
1205 flat_file[0] = '\0';
1207 for (i = 0; file_name[i] != NULL; i++) {
1208 strcat(flat_file, file_name[i]);
1209 strcat(flat_file, " ");
1211 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1213 #ifdef USECPP
1214 if (!wPreferences.flags.nocpp) {
1215 args = MakeCPPArgs(filename);
1216 if (!args) {
1217 wwarning(_("could not make arguments for menu file preprocessor"));
1218 } else {
1219 snprintf(command, sizeof(command), "%s | %s %s", filename, CPP_PATH, args);
1221 wfree(args);
1222 file = popen(command, "r");
1223 if (!file) {
1224 wsyserror(_("%s:could not open/preprocess menu file"), filename);
1225 } else {
1226 cpp = 1;
1230 #endif /* USECPP */
1232 if (!file) {
1233 file = popen(filename, "rb");
1235 if (!file) {
1236 wsyserror(_("%s:could not open menu file"), filename);
1237 return NULL;
1241 while (!feof(file)) {
1242 if (!fgets(linebuf, MAXLINE, file))
1243 break;
1244 line = cropline(linebuf);
1245 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1246 continue;
1248 separateline(line, title, command, params, shortcut);
1250 if (!command[0]) {
1251 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1252 break;
1254 if (strcasecmp(command, "MENU") == 0) {
1255 menu = wMenuCreate(scr, title, True);
1256 menu->on_destroy = removeShortcutsForMenu;
1257 if (!parseCascade(scr, menu, file, filename)) {
1258 wMenuDestroy(menu, True);
1260 break;
1261 } else {
1262 wwarning(_("%s:no title given for the root menu"), filename);
1263 break;
1267 pclose(file);
1269 return menu;
1272 typedef struct {
1273 char *name;
1274 int index;
1275 } dir_data;
1277 static int myCompare(const void *d1, const void *d2)
1279 dir_data *p1 = *(dir_data **) d1;
1280 dir_data *p2 = *(dir_data **) d2;
1282 return strcmp(p1->name, p2->name);
1285 /************ Menu Configuration From Directory *************/
1287 static Bool isFilePackage(char *file)
1289 int l;
1291 /* check if the extension indicates this file is a
1292 * file package. For now, only recognize .themed */
1294 l = strlen(file);
1296 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1297 return True;
1298 } else {
1299 return False;
1303 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
1305 DIR *dir;
1306 struct dirent *dentry;
1307 struct stat stat_buf;
1308 WMenu *menu = NULL;
1309 char *buffer;
1310 WMArray *dirs = NULL, *files = NULL;
1311 WMArrayIterator iter;
1312 int length, i, have_space = 0;
1313 dir_data *data;
1314 int stripExtension = 0;
1316 dirs = WMCreateArray(16);
1317 files = WMCreateArray(16);
1319 i = 0;
1320 while (path[i] != NULL) {
1321 if (strcmp(path[i], "-noext") == 0) {
1322 stripExtension = 1;
1323 i++;
1324 continue;
1327 dir = opendir(path[i]);
1328 if (!dir) {
1329 i++;
1330 continue;
1333 while ((dentry = readdir(dir))) {
1335 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1336 continue;
1338 if (dentry->d_name[0] == '.')
1339 continue;
1341 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1342 if (!buffer) {
1343 wsyserror(_("out of memory while constructing directory menu %s"), path[i]);
1344 break;
1347 strcpy(buffer, path[i]);
1348 strcat(buffer, "/");
1349 strcat(buffer, dentry->d_name);
1351 if (stat(buffer, &stat_buf) != 0) {
1352 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1353 path[i], dentry->d_name);
1354 } else {
1355 Bool isFilePack = False;
1357 data = NULL;
1358 if (S_ISDIR(stat_buf.st_mode)
1359 && !(isFilePack = isFilePackage(dentry->d_name))) {
1361 /* access always returns success for user root */
1362 if (access(buffer, X_OK) == 0) {
1363 /* Directory is accesible. Add to directory list */
1365 data = (dir_data *) wmalloc(sizeof(dir_data));
1366 data->name = wstrdup(dentry->d_name);
1367 data->index = i;
1369 WMAddToArray(dirs, data);
1371 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1372 /* Hack because access always returns X_OK success for user root */
1373 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1374 if ((command != NULL && access(buffer, R_OK) == 0) ||
1375 (command == NULL && access(buffer, X_OK) == 0 &&
1376 (stat_buf.st_mode & S_IXANY))) {
1378 data = (dir_data *) wmalloc(sizeof(dir_data));
1379 data->name = wstrdup(dentry->d_name);
1380 data->index = i;
1382 WMAddToArray(files, data);
1386 wfree(buffer);
1389 closedir(dir);
1390 i++;
1393 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1394 WMFreeArray(dirs);
1395 WMFreeArray(files);
1396 return NULL;
1399 WMSortArray(dirs, myCompare);
1400 WMSortArray(files, myCompare);
1402 menu = wMenuCreate(scr, title, False);
1403 menu->on_destroy = removeShortcutsForMenu;
1405 WM_ITERATE_ARRAY(dirs, data, iter) {
1406 /* New directory. Use same OPEN_MENU command that was used
1407 * for the current directory. */
1408 length = strlen(path[data->index]) + strlen(data->name) + 6;
1409 if (stripExtension)
1410 length += 7;
1411 if (command)
1412 length += strlen(command) + 6;
1413 buffer = malloc(length);
1414 if (!buffer) {
1415 wsyserror(_("out of memory while constructing directory menu %s"), path[data->index]);
1416 break;
1419 buffer[0] = '\0';
1420 if (stripExtension)
1421 strcat(buffer, "-noext ");
1423 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1425 if (have_space)
1426 strcat(buffer, "\"");
1427 strcat(buffer, path[data->index]);
1429 strcat(buffer, "/");
1430 strcat(buffer, data->name);
1431 if (have_space)
1432 strcat(buffer, "\"");
1433 if (command) {
1434 strcat(buffer, " WITH ");
1435 strcat(buffer, command);
1438 addMenuEntry(menu, data->name, NULL, "OPEN_MENU", buffer, path[data->index]);
1440 wfree(buffer);
1441 if (data->name)
1442 wfree(data->name);
1443 wfree(data);
1446 WM_ITERATE_ARRAY(files, data, iter) {
1447 /* executable: add as entry */
1448 length = strlen(path[data->index]) + strlen(data->name) + 6;
1449 if (command)
1450 length += strlen(command);
1452 buffer = malloc(length);
1453 if (!buffer) {
1454 wsyserror(_("out of memory while constructing directory menu %s"), path[data->index]);
1455 break;
1458 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1459 if (command != NULL) {
1460 strcpy(buffer, command);
1461 strcat(buffer, " ");
1462 if (have_space)
1463 strcat(buffer, "\"");
1464 strcat(buffer, path[data->index]);
1465 } else {
1466 if (have_space) {
1467 buffer[0] = '"';
1468 buffer[1] = 0;
1469 strcat(buffer, path[data->index]);
1470 } else {
1471 strcpy(buffer, path[data->index]);
1474 strcat(buffer, "/");
1475 strcat(buffer, data->name);
1476 if (have_space)
1477 strcat(buffer, "\"");
1479 if (stripExtension) {
1480 char *ptr = strrchr(data->name, '.');
1481 if (ptr && ptr != data->name)
1482 *ptr = 0;
1484 addMenuEntry(menu, data->name, NULL, "SHEXEC", buffer, path[data->index]);
1486 wfree(buffer);
1487 if (data->name)
1488 wfree(data->name);
1489 wfree(data);
1492 WMFreeArray(files);
1493 WMFreeArray(dirs);
1495 return menu;
1498 /************ Menu Configuration From WMRootMenu *************/
1500 static WMenu *makeDefaultMenu(WScreen * scr)
1502 WMenu *menu = NULL;
1504 menu = wMenuCreate(scr, _("Commands"), True);
1505 wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
1506 wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
1507 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1508 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1509 return menu;
1513 *----------------------------------------------------------------------
1514 * configureMenu--
1515 * Reads root menu configuration from defaults database.
1517 *----------------------------------------------------------------------
1519 static WMenu *configureMenu(WScreen * scr, WMPropList * definition)
1521 WMenu *menu = NULL;
1522 WMPropList *elem;
1523 int i, count;
1524 WMPropList *title, *command, *params;
1525 char *tmp, *mtitle;
1527 if (WMIsPLString(definition)) {
1528 struct stat stat_buf;
1529 char *path = NULL;
1530 Bool menu_is_default = False;
1532 /* menu definition is a string. Probably a path, so parse the file */
1534 tmp = wexpandpath(WMGetFromPLString(definition));
1536 path = getLocalizedMenuFile(tmp);
1538 if (!path)
1539 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1541 if (!path) {
1542 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1543 menu_is_default = True;
1546 if (!path) {
1547 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1548 wfree(tmp);
1549 return NULL;
1552 if (stat(path, &stat_buf) < 0) {
1553 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1554 wfree(path);
1555 wfree(tmp);
1556 return NULL;
1559 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1560 /* if the pointer in WMRootMenu has changed */
1561 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1563 if (menu_is_default) {
1564 wwarning(_
1565 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1566 path);
1569 menu = readMenuFile(scr, path);
1570 if (menu)
1571 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1572 } else {
1573 menu = NULL;
1575 wfree(path);
1576 wfree(tmp);
1578 return menu;
1581 count = WMGetPropListItemCount(definition);
1582 if (count == 0)
1583 return NULL;
1585 elem = WMGetFromPLArray(definition, 0);
1586 if (!WMIsPLString(elem)) {
1587 tmp = WMGetPropListDescription(elem, False);
1588 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1589 wfree(tmp);
1590 return NULL;
1592 mtitle = WMGetFromPLString(elem);
1594 menu = wMenuCreate(scr, mtitle, False);
1595 menu->on_destroy = removeShortcutsForMenu;
1597 #ifdef GLOBAL_SUBMENU_FILE
1599 WMenu *submenu;
1600 WMenuEntry *mentry;
1602 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1604 if (submenu) {
1605 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1606 wMenuEntrySetCascade(menu, mentry, submenu);
1609 #endif
1611 for (i = 1; i < count; i++) {
1612 elem = WMGetFromPLArray(definition, i);
1613 #if 0
1614 if (WMIsPLString(elem)) {
1615 char *file;
1617 file = WMGetFromPLString(elem);
1620 #endif
1621 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1622 goto error;
1624 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1625 WMenu *submenu;
1626 WMenuEntry *mentry;
1628 /* submenu */
1629 submenu = configureMenu(scr, elem);
1630 if (submenu) {
1631 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1632 wMenuEntrySetCascade(menu, mentry, submenu);
1634 } else {
1635 int idx = 0;
1636 WMPropList *shortcut;
1637 /* normal entry */
1639 title = WMGetFromPLArray(elem, idx++);
1640 shortcut = WMGetFromPLArray(elem, idx++);
1641 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1642 shortcut = WMGetFromPLArray(elem, idx++);
1643 command = WMGetFromPLArray(elem, idx++);
1644 } else {
1645 command = shortcut;
1646 shortcut = NULL;
1648 params = WMGetFromPLArray(elem, idx++);
1650 if (!title || !command)
1651 goto error;
1653 addMenuEntry(menu, WMGetFromPLString(title),
1654 shortcut ? WMGetFromPLString(shortcut) : NULL,
1655 WMGetFromPLString(command),
1656 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1658 continue;
1660 error:
1661 tmp = WMGetPropListDescription(elem, False);
1662 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1663 wfree(tmp);
1666 return menu;
1670 *----------------------------------------------------------------------
1671 * OpenRootMenu--
1672 * Opens the root menu, parsing the menu configuration from the
1673 * defaults database.
1674 * If the menu is already mapped and is not sticked to the
1675 * root window, it will be unmapped.
1677 * Side effects:
1678 * The menu may be remade.
1680 * Notes:
1681 * Construction of OPEN_MENU entries are delayed to the moment the
1682 * user map's them.
1683 *----------------------------------------------------------------------
1685 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1687 WMenu *menu = NULL;
1688 WMPropList *definition;
1690 static WMPropList *domain=NULL;
1692 if (!domain) {
1693 domain = WMCreatePLString("WMRootMenu");
1697 scr->flags.root_menu_changed_shortcuts = 0;
1698 scr->flags.added_workspace_menu = 0;
1699 scr->flags.added_windows_menu = 0;
1701 if (scr->root_menu && scr->root_menu->flags.mapped) {
1702 menu = scr->root_menu;
1703 if (!menu->flags.buttoned) {
1704 wMenuUnmap(menu);
1705 } else {
1706 wRaiseFrame(menu->frame->core);
1708 if (keyboard)
1709 wMenuMapAt(menu, 0, 0, True);
1710 else
1711 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1713 return;
1716 definition = WDRootMenu->dictionary;
1719 definition = PLGetDomain(domain);
1721 if (definition) {
1722 if (WMIsPLArray(definition)) {
1723 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1724 menu = configureMenu(scr, definition);
1725 if (menu)
1726 menu->timestamp = WDRootMenu->timestamp;
1728 } else
1729 menu = NULL;
1730 } else {
1731 menu = configureMenu(scr, definition);
1735 if (!menu) {
1736 /* menu hasn't changed or could not be read */
1737 if (!scr->root_menu) {
1738 wMessageDialog(scr, _("Error"),
1739 _("The applications menu could not be loaded. "
1740 "Look at the console output for a detailed "
1741 "description of the errors."), _("OK"), NULL, NULL);
1743 menu = makeDefaultMenu(scr);
1744 scr->root_menu = menu;
1746 menu = scr->root_menu;
1747 } else {
1748 /* new root menu */
1749 if (scr->root_menu) {
1750 wMenuDestroy(scr->root_menu, True);
1752 scr->root_menu = menu;
1754 if (menu) {
1755 int newx, newy;
1757 if (keyboard && x == 0 && y == 0) {
1758 newx = newy = 0;
1759 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1760 newx = x - menu->frame->core->width / 2;
1761 newy = y - menu->frame->core->height / 2;
1762 } else {
1763 newx = x - menu->frame->core->width / 2;
1764 newy = y;
1766 wMenuMapAt(menu, newx, newy, keyboard);
1769 if (scr->flags.root_menu_changed_shortcuts)
1770 rebindKeygrabs(scr);