Update local copy of GPLv2 and FSF address in copyrights
[wmaker-crm.git] / src / rootmenu.c
blob17306880367d2a9ed951d93920c7641527d89c63
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 <ctype.h>
33 #include <time.h>
34 #include <dirent.h>
36 #include <X11/Xlib.h>
37 #include <X11/Xutil.h>
38 #include <X11/Xatom.h>
40 #include "WindowMaker.h"
41 #include "actions.h"
42 #include "menu.h"
43 #include "funcs.h"
44 #include "dialog.h"
45 #include "keybind.h"
46 #include "stacking.h"
47 #include "workspace.h"
48 #include "defaults.h"
49 #include "framewin.h"
50 #include "session.h"
51 #include "xmodifier.h"
53 #include <WINGs/WUtil.h>
55 #define MAX_SHORTCUT_LENGTH 32
57 extern char *Locale;
58 extern WDDomain *WDRootMenu;
59 extern Cursor wCursor[WCUR_LAST];
60 extern WPreferences wPreferences;
62 static WMenu *readMenuPipe(WScreen * scr, char **file_name);
63 static WMenu *readMenuFile(WScreen * scr, char *file_name);
64 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **file_name, char *command);
65 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals);
67 typedef struct Shortcut {
68 struct Shortcut *next;
70 int modifier;
71 KeyCode keycode;
72 WMenuEntry *entry;
73 WMenu *menu;
74 } Shortcut;
76 static Shortcut *shortcutList = NULL;
79 * Syntax:
80 * # main menu
81 * "Menu Name" MENU
82 * "Title" EXEC command_to_exec -params
83 * "Submenu" MENU
84 * "Title" EXEC command_to_exec -params
85 * "Submenu" END
86 * "Workspaces" WORKSPACE_MENU
87 * "Title" built_in_command
88 * "Quit" EXIT
89 * "Quick Quit" EXIT QUICK
90 * "Menu Name" END
92 * Commands may be preceded by SHORTCUT key
94 * Built-in commands:
96 * INFO_PANEL - shows the Info Panel
97 * LEGAL_PANEL - shows the Legal info panel
98 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
99 * REFRESH - forces the desktop to be repainted
100 * EXIT [QUICK] - exit the window manager [without confirmation]
101 * EXEC <program> - execute an external program
102 * SHEXEC <command> - execute a shell command
103 * WORKSPACE_MENU - places the workspace submenu
104 * ARRANGE_ICONS
105 * RESTART [<window manager>] - restarts the window manager
106 * SHOW_ALL - unhide all windows on workspace
107 * HIDE_OTHERS - hides all windows excep the focused one
108 * OPEN_MENU file - read menu data from file which must be a valid menu file.
109 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
110 * - read menu data from directory(ies) and
111 * eventually precede each with a command.
112 * OPEN_MENU | command
113 * - opens command and uses its stdout to construct and insert
114 * the resulting menu in current position. The output of
115 * command must be a valid menu description.
116 * The space between '|' and command is optional.
117 * || will do the same, but will not cache the contents.
118 * SAVE_SESSION - saves the current state of the desktop, which include
119 * all running applications, all their hints (geometry,
120 * position on screen, workspace they live on, the dock
121 * or clip from where they were launched, and
122 * if minimized, shaded or hidden. Also saves the current
123 * workspace the user is on. All will be restored on every
124 * start of windowmaker until another SAVE_SESSION or
125 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
126 * WindowMaker domain file, then saving is automatically
127 * done on every windowmaker exit, overwriting any
128 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
129 * dock state now.
130 * CLEAR_SESSION - clears any previous saved session. This will not have
131 * any effect if SaveSessionOnExit is True.
135 #define M_QUICK 1
137 /* menu commands */
139 static void execCommand(WMenu * menu, WMenuEntry * entry)
141 char *cmdline;
143 cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
145 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
146 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);
147 XSync(dpy, 0);
149 if (cmdline) {
150 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
151 wfree(cmdline);
153 XUngrabPointer(dpy, CurrentTime);
154 XSync(dpy, 0);
157 static void exitCommand(WMenu * menu, WMenuEntry * entry)
159 static int inside = 0;
160 int result;
162 /* prevent reentrant calls */
163 if (inside)
164 return;
165 inside = 1;
167 #define R_CANCEL 0
168 #define R_EXIT 1
170 result = R_CANCEL;
172 if ((long)entry->clientdata == M_QUICK) {
173 result = R_EXIT;
174 } else {
175 int r, oldSaveSessionFlag;
177 oldSaveSessionFlag = wPreferences.save_session_on_exit;
178 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
179 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
181 if (r == WAPRDefault) {
182 result = R_EXIT;
183 } else if (r == WAPRAlternate) {
184 /* Don't modify the "save session on exit" flag if the
185 * user canceled the operation. */
186 wPreferences.save_session_on_exit = oldSaveSessionFlag;
189 if (result == R_EXIT)
190 Shutdown(WSExitMode);
192 #undef R_EXIT
193 #undef R_CANCEL
194 inside = 0;
197 static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
199 static int inside = 0;
200 int result;
202 /* prevent reentrant calls */
203 if (inside)
204 return;
205 inside = 1;
207 #define R_CANCEL 0
208 #define R_CLOSE 1
209 #define R_KILL 2
211 result = R_CANCEL;
212 if ((long)entry->clientdata == M_QUICK)
213 result = R_CLOSE;
214 else {
216 int r, oldSaveSessionFlag;
218 oldSaveSessionFlag = wPreferences.save_session_on_exit;
220 r = wExitDialog(menu->frame->screen_ptr,
221 _("Kill X session"),
222 _("Kill Window System session?\n"
223 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
224 if (r == WAPRDefault) {
225 result = R_KILL;
226 } else if (r == WAPRAlternate) {
227 /* Don't modify the "save session on exit" flag if the
228 * user canceled the operation. */
229 wPreferences.save_session_on_exit = oldSaveSessionFlag;
234 if (result != R_CANCEL) {
236 Shutdown(WSKillMode);
239 #undef R_CLOSE
240 #undef R_CANCEL
241 #undef R_KILL
242 inside = 0;
245 static void restartCommand(WMenu * menu, WMenuEntry * entry)
247 Shutdown(WSRestartPreparationMode);
248 Restart((char *)entry->clientdata, False);
249 Restart(NULL, True);
252 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
254 wRefreshDesktop(menu->frame->screen_ptr);
257 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
259 wArrangeIcons(menu->frame->screen_ptr, True);
262 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
264 wShowAllWindows(menu->frame->screen_ptr);
267 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
269 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
272 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
274 if (!wPreferences.save_session_on_exit)
275 wSessionSaveState(menu->frame->screen_ptr);
277 wScreenSaveState(menu->frame->screen_ptr);
280 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
282 wSessionClearState(menu->frame->screen_ptr);
283 wScreenSaveState(menu->frame->screen_ptr);
286 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
288 wShowInfoPanel(menu->frame->screen_ptr);
291 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
293 wShowLegalPanel(menu->frame->screen_ptr);
296 /********************************************************************/
298 static char *getLocalizedMenuFile(char *menu)
300 char *buffer, *ptr, *locale;
301 int len;
303 if (!Locale)
304 return NULL;
306 len = strlen(menu) + strlen(Locale) + 8;
307 buffer = wmalloc(len);
309 /* try menu.locale_name */
310 snprintf(buffer, len, "%s.%s", menu, Locale);
311 if (access(buffer, F_OK) == 0) {
312 return buffer;
315 /* position of locale in our buffer */
316 locale = buffer + strlen(menu) + 1;
318 /* check if it is in the form aa_bb.encoding and check for aa_bb */
319 ptr = strchr(locale, '.');
320 if (ptr) {
321 *ptr = 0;
322 if (access(buffer, F_OK) == 0) {
323 return buffer;
326 /* now check for aa */
327 ptr = strchr(locale, '_');
328 if (ptr) {
329 *ptr = 0;
330 if (access(buffer, F_OK) == 0) {
331 return buffer;
335 wfree(buffer);
337 return NULL;
340 Bool wRootMenuPerformShortcut(XEvent * event)
342 WScreen *scr = wScreenForRootWindow(event->xkey.root);
343 Shortcut *ptr;
344 int modifiers;
345 int done = 0;
347 /* ignore CapsLock */
348 modifiers = event->xkey.state & ValidModMask;
350 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
351 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
352 continue;
354 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
355 (*ptr->entry->callback) (ptr->menu, ptr->entry);
356 done = True;
360 return done;
363 void wRootMenuBindShortcuts(Window window)
365 Shortcut *ptr;
367 ptr = shortcutList;
368 while (ptr) {
369 if (ptr->modifier != AnyModifier) {
370 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
371 window, True, GrabModeAsync, GrabModeAsync);
372 #ifdef NUMLOCK_HACK
373 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
374 #endif
376 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
377 ptr = ptr->next;
381 static void rebindKeygrabs(WScreen * scr)
383 WWindow *wwin;
385 wwin = scr->focused_window;
387 while (wwin != NULL) {
388 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
390 if (!WFLAGP(wwin, no_bind_keys)) {
391 wWindowSetKeyGrabs(wwin);
393 wwin = wwin->prev;
397 static void removeShortcutsForMenu(WMenu * menu)
399 Shortcut *ptr, *tmp;
400 Shortcut *newList = NULL;
402 ptr = shortcutList;
403 while (ptr != NULL) {
404 tmp = ptr->next;
405 if (ptr->menu == menu) {
406 wfree(ptr);
407 } else {
408 ptr->next = newList;
409 newList = ptr;
411 ptr = tmp;
413 shortcutList = newList;
414 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
417 static Bool addShortcut(char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
419 Shortcut *ptr;
420 KeySym ksym;
421 char *k;
422 char buf[MAX_SHORTCUT_LENGTH], *b;
424 ptr = wmalloc(sizeof(Shortcut));
426 strncpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
427 b = (char *)buf;
429 /* get modifiers */
430 ptr->modifier = 0;
431 while ((k = strchr(b, '+')) != NULL) {
432 int mod;
434 *k = 0;
435 mod = wXModifierFromKey(b);
436 if (mod < 0) {
437 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
438 wfree(ptr);
439 return False;
441 ptr->modifier |= mod;
443 b = k + 1;
446 /* get key */
447 ksym = XStringToKeysym(b);
449 if (ksym == NoSymbol) {
450 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
451 file, shortcutDefinition, entry->text);
452 wfree(ptr);
453 return False;
456 ptr->keycode = XKeysymToKeycode(dpy, ksym);
457 if (ptr->keycode == 0) {
458 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
459 shortcutDefinition, entry->text);
460 wfree(ptr);
461 return False;
464 ptr->menu = menu;
465 ptr->entry = entry;
467 ptr->next = shortcutList;
468 shortcutList = ptr;
470 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
472 return True;
475 /*******************************/
477 static char *cropline(char *line)
479 char *end;
481 if (strlen(line) == 0)
482 return line;
484 end = &(line[strlen(line)]) - 1;
485 while (isspace(*line) && *line != 0)
486 line++;
487 while (end > line && isspace(*end)) {
488 *end = 0;
489 end--;
491 return line;
494 static char *next_token(char *line, char **next)
496 char *tmp, c;
497 char *ret;
499 *next = NULL;
500 while (*line == ' ' || *line == '\t')
501 line++;
503 tmp = line;
505 if (*tmp == '"') {
506 tmp++;
507 line++;
508 while (*tmp != 0 && *tmp != '"')
509 tmp++;
510 if (*tmp != '"') {
511 wwarning(_("%s: unmatched '\"' in menu file"), line);
512 return NULL;
514 } else {
515 do {
516 if (*tmp == '\\')
517 tmp++;
519 if (*tmp != 0)
520 tmp++;
522 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
525 c = *tmp;
526 *tmp = 0;
527 ret = wstrdup(line);
528 *tmp = c;
530 if (c == 0)
531 return ret;
532 else
533 tmp++;
535 /* skip blanks */
536 while (*tmp == ' ' || *tmp == '\t')
537 tmp++;
539 if (*tmp != 0)
540 *next = tmp;
542 return ret;
545 static void separateCommand(char *line, char ***file, char **command)
547 char *token, *tmp = line;
548 WMArray *array = WMCreateArray(4);
549 int count, i;
551 *file = NULL;
552 *command = NULL;
553 do {
554 token = next_token(tmp, &tmp);
555 if (token) {
556 if (strcmp(token, "WITH") == 0) {
557 if (tmp != NULL && *tmp != 0)
558 *command = wstrdup(tmp);
559 else
560 wwarning(_("%s: missing command"), line);
561 break;
563 WMAddToArray(array, token);
565 } while (token != NULL && tmp != NULL);
567 count = WMGetArrayItemCount(array);
568 if (count > 0) {
569 *file = wmalloc(sizeof(char *) * (count + 1));
570 (*file)[count] = NULL;
571 for (i = 0; i < count; i++) {
572 (*file)[i] = WMGetFromArray(array, i);
575 WMFreeArray(array);
578 static WMenu *constructPLMenu(WScreen *screen, char *path)
580 WMPropList *pl = NULL;
581 WMenu *menu = NULL;
583 if (!path)
584 return NULL;
586 pl = WMReadPropListFromFile(path);
587 if (!pl)
588 return NULL;
590 menu = configureMenu(screen, pl, False);
591 if (!menu)
592 return NULL;
594 menu->on_destroy = removeShortcutsForMenu;
595 return menu;
598 static void constructMenu(WMenu * menu, WMenuEntry * entry)
600 WMenu *submenu;
601 struct stat stat_buf;
602 char **path;
603 char *cmd;
604 char *lpath = NULL;
605 int i, first = -1;
606 time_t last = 0;
608 separateCommand((char *)entry->clientdata, &path, &cmd);
609 if (path == NULL || *path == NULL || **path == 0) {
610 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
611 return;
614 if (path[0][0] == '|') {
615 /* pipe menu */
617 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
618 /* parse pipe */
620 submenu = readMenuPipe(menu->frame->screen_ptr, path);
622 if (submenu != NULL) {
623 if (path[0][1] == '|')
624 submenu->timestamp = 0;
625 else
626 submenu->timestamp = 1; /* there's no automatic reloading */
628 } else {
629 submenu = NULL;
632 } else {
634 /* try interpreting path as a proplist file */
635 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
636 /* if unsuccessful, try it as an old-style file */
637 if (!submenu) {
639 i = 0;
640 while (path[i] != NULL) {
641 char *tmp;
643 if (strcmp(path[i], "-noext") == 0) {
644 i++;
645 continue;
648 tmp = wexpandpath(path[i]);
649 wfree(path[i]);
650 lpath = getLocalizedMenuFile(tmp);
651 if (lpath) {
652 wfree(tmp);
653 path[i] = lpath;
654 lpath = NULL;
655 } else {
656 path[i] = tmp;
659 if (stat(path[i], &stat_buf) == 0) {
660 if (last < stat_buf.st_mtime)
661 last = stat_buf.st_mtime;
662 if (first < 0)
663 first = i;
664 } else {
665 werror(_("%s:could not stat menu"), path[i]);
666 /*goto finish; */
669 i++;
672 if (first < 0) {
673 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
674 goto finish;
676 stat(path[first], &stat_buf);
677 if (!menu->cascades[entry->cascade]
678 || menu->cascades[entry->cascade]->timestamp < last) {
680 if (S_ISDIR(stat_buf.st_mode)) {
681 /* menu directory */
682 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
683 if (submenu)
684 submenu->timestamp = last;
685 } else if (S_ISREG(stat_buf.st_mode)) {
686 /* menu file */
688 if (cmd || path[1])
689 wwarning(_("too many parameters in OPEN_MENU: %s"),
690 (char *)entry->clientdata);
692 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
693 if (submenu)
694 submenu->timestamp = stat_buf.st_mtime;
695 } else {
696 submenu = NULL;
698 } else {
699 submenu = NULL;
704 if (submenu) {
705 wMenuEntryRemoveCascade(menu, entry);
706 wMenuEntrySetCascade(menu, entry, submenu);
709 finish:
710 i = 0;
711 while (path[i] != NULL)
712 wfree(path[i++]);
713 wfree(path);
714 if (cmd)
715 wfree(cmd);
718 static void cleanupWorkspaceMenu(WMenu * menu)
720 if (menu->frame->screen_ptr->workspace_menu == menu)
721 menu->frame->screen_ptr->workspace_menu = NULL;
724 static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
726 WMenu *wsmenu;
727 WMenuEntry *entry;
729 if (scr->flags.added_workspace_menu) {
730 wwarning(_
731 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
732 return NULL;
733 } else {
734 scr->flags.added_workspace_menu = 1;
736 wsmenu = wWorkspaceMenuMake(scr, True);
737 wsmenu->on_destroy = cleanupWorkspaceMenu;
739 scr->workspace_menu = wsmenu;
740 entry = wMenuAddCallback(menu, title, NULL, NULL);
741 wMenuEntrySetCascade(menu, entry, wsmenu);
743 wWorkspaceMenuUpdate(scr, wsmenu);
745 return entry;
748 static void cleanupWindowsMenu(WMenu * menu)
750 if (menu->frame->screen_ptr->switch_menu == menu)
751 menu->frame->screen_ptr->switch_menu = NULL;
754 static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
756 WMenu *wwmenu;
757 WWindow *wwin;
758 WMenuEntry *entry;
760 if (scr->flags.added_windows_menu) {
761 wwarning(_
762 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
763 return NULL;
764 } else {
765 scr->flags.added_windows_menu = 1;
767 wwmenu = wMenuCreate(scr, _("Window List"), False);
768 wwmenu->on_destroy = cleanupWindowsMenu;
769 scr->switch_menu = wwmenu;
770 wwin = scr->focused_window;
771 while (wwin) {
772 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
774 wwin = wwin->prev;
776 entry = wMenuAddCallback(menu, title, NULL, NULL);
777 wMenuEntrySetCascade(menu, entry, wwmenu);
779 return entry;
782 static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
783 char *params, char *file_name)
785 WScreen *scr;
786 WMenuEntry *entry = NULL;
787 Bool shortcutOk = False;
789 if (!menu)
790 return NULL;
791 scr = menu->frame->screen_ptr;
792 if (strcmp(command, "OPEN_MENU") == 0) {
793 if (!params) {
794 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
795 } else {
796 WMenu *dummy;
797 char *path;
799 path = wfindfile(DEF_CONFIG_PATHS, params);
800 if (!path) {
801 path = wstrdup(params);
803 dummy = wMenuCreate(scr, title, False);
804 dummy->on_destroy = removeShortcutsForMenu;
805 entry = wMenuAddCallback(menu, title, constructMenu, path);
806 entry->free_cdata = free;
807 wMenuEntrySetCascade(menu, entry, dummy);
809 } else if (strcmp(command, "EXEC") == 0) {
810 if (!params)
811 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
812 else {
813 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
814 entry->free_cdata = free;
815 shortcutOk = True;
817 } else if (strcmp(command, "SHEXEC") == 0) {
818 if (!params)
819 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
820 else {
821 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
822 entry->free_cdata = free;
823 shortcutOk = True;
825 } else if (strcmp(command, "EXIT") == 0) {
827 if (params && strcmp(params, "QUICK") == 0)
828 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
829 else
830 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
832 shortcutOk = True;
833 } else if (strcmp(command, "SHUTDOWN") == 0) {
835 if (params && strcmp(params, "QUICK") == 0)
836 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
837 else
838 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
840 shortcutOk = True;
841 } else if (strcmp(command, "REFRESH") == 0) {
842 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
844 shortcutOk = True;
845 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
846 entry = addWorkspaceMenu(scr, menu, title);
848 shortcutOk = True;
849 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
850 entry = addWindowsMenu(scr, menu, title);
852 shortcutOk = True;
853 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
854 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
856 shortcutOk = True;
857 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
858 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
860 shortcutOk = True;
861 } else if (strcmp(command, "SHOW_ALL") == 0) {
862 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
864 shortcutOk = True;
865 } else if (strcmp(command, "RESTART") == 0) {
866 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
867 entry->free_cdata = free;
868 shortcutOk = True;
869 } else if (strcmp(command, "SAVE_SESSION") == 0) {
870 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
872 shortcutOk = True;
873 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
874 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
875 shortcutOk = True;
876 } else if (strcmp(command, "INFO_PANEL") == 0) {
877 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
878 shortcutOk = True;
879 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
880 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
881 shortcutOk = True;
882 } else {
883 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
885 return NULL;
888 if (shortcut && entry) {
889 if (!shortcutOk) {
890 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
891 } else {
892 if (addShortcut(file_name, shortcut, menu, entry)) {
894 entry->rtext = GetShortcutString(shortcut);
896 entry->rtext = wstrdup(shortcut);
902 return entry;
905 /******************* Menu Configuration From File *******************/
907 static void separateline(char *line, char *title, char *command, char *parameter, char *shortcut)
909 int l, i;
911 l = strlen(line);
913 *title = 0;
914 *command = 0;
915 *parameter = 0;
916 *shortcut = 0;
917 /* get the title */
918 while (isspace(*line) && (*line != 0))
919 line++;
920 if (*line == '"') {
921 line++;
922 i = 0;
923 while (line[i] != '"' && (line[i] != 0))
924 i++;
925 if (line[i] != '"')
926 return;
927 } else {
928 i = 0;
929 while (!isspace(line[i]) && (line[i] != 0))
930 i++;
932 strncpy(title, line, i);
933 title[i++] = 0;
934 line += i;
936 /* get the command or shortcut keyword */
937 while (isspace(*line) && (*line != 0))
938 line++;
939 if (*line == 0)
940 return;
941 i = 0;
942 while (!isspace(line[i]) && (line[i] != 0))
943 i++;
944 strncpy(command, line, i);
945 command[i++] = 0;
946 line += i;
948 if (strcmp(command, "SHORTCUT") == 0) {
949 /* get the shortcut key */
950 while (isspace(*line) && (*line != 0))
951 line++;
952 if (*line == '"') {
953 line++;
954 i = 0;
955 while (line[i] != '"' && (line[i] != 0))
956 i++;
957 if (line[i] != '"')
958 return;
959 } else {
960 i = 0;
961 while (!isspace(line[i]) && (line[i] != 0))
962 i++;
964 strncpy(shortcut, line, i);
965 shortcut[i++] = 0;
966 line += i;
968 *command = 0;
970 /* get the command */
971 while (isspace(*line) && (*line != 0))
972 line++;
973 if (*line == 0)
974 return;
975 i = 0;
976 while (!isspace(line[i]) && (line[i] != 0))
977 i++;
978 strncpy(command, line, i);
979 command[i++] = 0;
980 line += i;
983 /* get the parameters */
984 while (isspace(*line) && (*line != 0))
985 line++;
986 if (*line == 0)
987 return;
989 if (*line == '"') {
990 line++;
991 l = 0;
992 while (line[l] != 0 && line[l] != '"') {
993 parameter[l] = line[l];
994 l++;
996 parameter[l] = 0;
997 return;
1000 l = strlen(line);
1001 while (isspace(line[l]) && (l > 0))
1002 l--;
1003 strncpy(parameter, line, l);
1004 parameter[l] = 0;
1007 static WMenu *parseCascade(WScreen * scr, WMenu * menu, FILE * file, char *file_name)
1009 char linebuf[MAXLINE];
1010 char elinebuf[MAXLINE];
1011 char title[MAXLINE];
1012 char command[MAXLINE];
1013 char shortcut[MAXLINE];
1014 char params[MAXLINE];
1015 char *line;
1017 while (!feof(file)) {
1018 int lsize, ok;
1020 ok = 0;
1021 fgets(linebuf, MAXLINE, file);
1022 line = cropline(linebuf);
1023 lsize = strlen(line);
1024 do {
1025 if (line[lsize - 1] == '\\') {
1026 char *line2;
1027 int lsize2;
1028 fgets(elinebuf, MAXLINE, file);
1029 line2 = cropline(elinebuf);
1030 lsize2 = strlen(line2);
1031 if (lsize2 + lsize > MAXLINE) {
1032 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1033 file_name, line);
1034 ok = 2;
1035 } else {
1036 line[lsize - 1] = 0;
1037 lsize += lsize2 - 1;
1038 strcat(line, line2);
1040 } else {
1041 ok = 1;
1043 } while (!ok && !feof(file));
1044 if (ok == 2)
1045 continue;
1047 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1048 continue;
1050 separateline(line, title, command, params, shortcut);
1052 if (!command[0]) {
1053 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1054 goto error;
1057 if (strcasecmp(command, "MENU") == 0) {
1058 WMenu *cascade;
1060 /* start submenu */
1062 cascade = wMenuCreate(scr, M_(title), False);
1063 cascade->on_destroy = removeShortcutsForMenu;
1064 if (parseCascade(scr, cascade, file, file_name) == NULL) {
1065 wMenuDestroy(cascade, True);
1066 } else {
1067 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
1069 } else if (strcasecmp(command, "END") == 0) {
1070 /* end of menu */
1071 return menu;
1073 } else {
1074 /* normal items */
1075 addMenuEntry(menu, M_(title), shortcut[0] ? shortcut : NULL, command,
1076 params[0] ? params : NULL, file_name);
1080 wwarning(_("%s:syntax error in menu file:END declaration missing"), file_name);
1081 return menu;
1083 error:
1084 return menu;
1087 static WMenu *readMenuFile(WScreen * scr, char *file_name)
1089 WMenu *menu = NULL;
1090 FILE *file = NULL;
1091 char linebuf[MAXLINE];
1092 char title[MAXLINE];
1093 char shortcut[MAXLINE];
1094 char command[MAXLINE];
1095 char params[MAXLINE];
1096 char *line;
1097 #ifdef USECPP
1098 char *args;
1099 int cpp = 0;
1100 #endif
1102 #ifdef USECPP
1103 if (!wPreferences.flags.nocpp) {
1104 args = MakeCPPArgs(file_name);
1105 if (!args) {
1106 wwarning(_("could not make arguments for menu file preprocessor"));
1107 } else {
1108 snprintf(command, sizeof(command), "%s %s %s", CPP_PATH, args, file_name);
1109 wfree(args);
1110 file = popen(command, "r");
1111 if (!file) {
1112 werror(_("%s:could not open/preprocess menu file"), file_name);
1113 } else {
1114 cpp = 1;
1118 #endif /* USECPP */
1120 if (!file) {
1121 file = fopen(file_name, "rb");
1122 if (!file) {
1123 werror(_("%s:could not open menu file"), file_name);
1124 return NULL;
1128 while (!feof(file)) {
1129 if (!fgets(linebuf, MAXLINE, file))
1130 break;
1131 line = cropline(linebuf);
1132 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1133 continue;
1135 separateline(line, title, command, params, shortcut);
1137 if (!command[0]) {
1138 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1139 break;
1141 if (strcasecmp(command, "MENU") == 0) {
1142 menu = wMenuCreate(scr, M_(title), True);
1143 menu->on_destroy = removeShortcutsForMenu;
1144 if (!parseCascade(scr, menu, file, file_name)) {
1145 wMenuDestroy(menu, True);
1147 break;
1148 } else {
1149 wwarning(_("%s:invalid menu file. MENU command is missing"), file_name);
1150 break;
1154 #ifdef CPP
1155 if (cpp) {
1156 if (pclose(file) == -1) {
1157 werror(_("error reading preprocessed menu data"));
1159 } else {
1160 fclose(file);
1162 #else
1163 fclose(file);
1164 #endif
1166 return menu;
1169 /************ Menu Configuration From Pipe *************/
1171 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1173 WMenu *menu = NULL;
1174 FILE *file = NULL;
1175 char linebuf[MAXLINE];
1176 char title[MAXLINE];
1177 char command[MAXLINE];
1178 char params[MAXLINE];
1179 char shortcut[MAXLINE];
1180 char *line;
1181 char *filename;
1182 char flat_file[MAXLINE];
1183 int i;
1184 #ifdef USECPP
1185 char *args;
1186 int cpp = 0;
1187 #endif
1189 flat_file[0] = '\0';
1191 for (i = 0; file_name[i] != NULL; i++) {
1192 strcat(flat_file, file_name[i]);
1193 strcat(flat_file, " ");
1195 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1197 #ifdef USECPP
1198 if (!wPreferences.flags.nocpp) {
1199 args = MakeCPPArgs(filename);
1200 if (!args) {
1201 wwarning(_("could not make arguments for menu file preprocessor"));
1202 } else {
1203 snprintf(command, sizeof(command), "%s | %s %s", filename, CPP_PATH, args);
1205 wfree(args);
1206 file = popen(command, "r");
1207 if (!file) {
1208 werror(_("%s:could not open/preprocess menu file"), filename);
1209 } else {
1210 cpp = 1;
1214 #endif /* USECPP */
1216 if (!file) {
1217 file = popen(filename, "rb");
1219 if (!file) {
1220 werror(_("%s:could not open menu file"), filename);
1221 return NULL;
1225 while (!feof(file)) {
1226 if (!fgets(linebuf, MAXLINE, file))
1227 break;
1228 line = cropline(linebuf);
1229 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1230 continue;
1232 separateline(line, title, command, params, shortcut);
1234 if (!command[0]) {
1235 wwarning(_("%s:missing command in menu config: %s"), filename, line);
1236 break;
1238 if (strcasecmp(command, "MENU") == 0) {
1239 menu = wMenuCreate(scr, M_(title), True);
1240 menu->on_destroy = removeShortcutsForMenu;
1241 if (!parseCascade(scr, menu, file, filename)) {
1242 wMenuDestroy(menu, True);
1244 break;
1245 } else {
1246 wwarning(_("%s:no title given for the root menu"), filename);
1247 break;
1251 pclose(file);
1253 return menu;
1256 typedef struct {
1257 char *name;
1258 int index;
1259 } dir_data;
1261 static int myCompare(const void *d1, const void *d2)
1263 dir_data *p1 = *(dir_data **) d1;
1264 dir_data *p2 = *(dir_data **) d2;
1266 return strcmp(p1->name, p2->name);
1269 /************ Menu Configuration From Directory *************/
1271 static Bool isFilePackage(char *file)
1273 int l;
1275 /* check if the extension indicates this file is a
1276 * file package. For now, only recognize .themed */
1278 l = strlen(file);
1280 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1281 return True;
1282 } else {
1283 return False;
1287 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
1289 DIR *dir;
1290 struct dirent *dentry;
1291 struct stat stat_buf;
1292 WMenu *menu = NULL;
1293 char *buffer;
1294 WMArray *dirs = NULL, *files = NULL;
1295 WMArrayIterator iter;
1296 int length, i, have_space = 0;
1297 dir_data *data;
1298 int stripExtension = 0;
1300 dirs = WMCreateArray(16);
1301 files = WMCreateArray(16);
1303 i = 0;
1304 while (path[i] != NULL) {
1305 if (strcmp(path[i], "-noext") == 0) {
1306 stripExtension = 1;
1307 i++;
1308 continue;
1311 dir = opendir(path[i]);
1312 if (!dir) {
1313 i++;
1314 continue;
1317 while ((dentry = readdir(dir))) {
1319 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1320 continue;
1322 if (dentry->d_name[0] == '.')
1323 continue;
1325 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1326 if (!buffer) {
1327 werror(_("out of memory while constructing directory menu %s"), path[i]);
1328 break;
1331 strcpy(buffer, path[i]);
1332 strcat(buffer, "/");
1333 strcat(buffer, dentry->d_name);
1335 if (stat(buffer, &stat_buf) != 0) {
1336 werror(_("%s:could not stat file \"%s\" in menu directory"),
1337 path[i], dentry->d_name);
1338 } else {
1339 Bool isFilePack = False;
1341 data = NULL;
1342 if (S_ISDIR(stat_buf.st_mode)
1343 && !(isFilePack = isFilePackage(dentry->d_name))) {
1345 /* access always returns success for user root */
1346 if (access(buffer, X_OK) == 0) {
1347 /* Directory is accesible. Add to directory list */
1349 data = (dir_data *) wmalloc(sizeof(dir_data));
1350 data->name = wstrdup(dentry->d_name);
1351 data->index = i;
1353 WMAddToArray(dirs, data);
1355 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1356 /* Hack because access always returns X_OK success for user root */
1357 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1358 if ((command != NULL && access(buffer, R_OK) == 0) ||
1359 (command == NULL && access(buffer, X_OK) == 0 &&
1360 (stat_buf.st_mode & S_IXANY))) {
1362 data = (dir_data *) wmalloc(sizeof(dir_data));
1363 data->name = wstrdup(dentry->d_name);
1364 data->index = i;
1366 WMAddToArray(files, data);
1370 wfree(buffer);
1373 closedir(dir);
1374 i++;
1377 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1378 WMFreeArray(dirs);
1379 WMFreeArray(files);
1380 return NULL;
1383 WMSortArray(dirs, myCompare);
1384 WMSortArray(files, myCompare);
1386 menu = wMenuCreate(scr, M_(title), False);
1387 menu->on_destroy = removeShortcutsForMenu;
1389 WM_ITERATE_ARRAY(dirs, data, iter) {
1390 /* New directory. Use same OPEN_MENU command that was used
1391 * for the current directory. */
1392 length = strlen(path[data->index]) + strlen(data->name) + 6;
1393 if (stripExtension)
1394 length += 7;
1395 if (command)
1396 length += strlen(command) + 6;
1397 buffer = malloc(length);
1398 if (!buffer) {
1399 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1400 break;
1403 buffer[0] = '\0';
1404 if (stripExtension)
1405 strcat(buffer, "-noext ");
1407 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1409 if (have_space)
1410 strcat(buffer, "\"");
1411 strcat(buffer, path[data->index]);
1413 strcat(buffer, "/");
1414 strcat(buffer, data->name);
1415 if (have_space)
1416 strcat(buffer, "\"");
1417 if (command) {
1418 strcat(buffer, " WITH ");
1419 strcat(buffer, command);
1422 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1424 wfree(buffer);
1425 if (data->name)
1426 wfree(data->name);
1427 wfree(data);
1430 WM_ITERATE_ARRAY(files, data, iter) {
1431 /* executable: add as entry */
1432 length = strlen(path[data->index]) + strlen(data->name) + 6;
1433 if (command)
1434 length += strlen(command);
1436 buffer = malloc(length);
1437 if (!buffer) {
1438 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1439 break;
1442 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1443 if (command != NULL) {
1444 strcpy(buffer, command);
1445 strcat(buffer, " ");
1446 if (have_space)
1447 strcat(buffer, "\"");
1448 strcat(buffer, path[data->index]);
1449 } else {
1450 if (have_space) {
1451 buffer[0] = '"';
1452 buffer[1] = 0;
1453 strcat(buffer, path[data->index]);
1454 } else {
1455 strcpy(buffer, path[data->index]);
1458 strcat(buffer, "/");
1459 strcat(buffer, data->name);
1460 if (have_space)
1461 strcat(buffer, "\"");
1463 if (stripExtension) {
1464 char *ptr = strrchr(data->name, '.');
1465 if (ptr && ptr != data->name)
1466 *ptr = 0;
1468 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1470 wfree(buffer);
1471 if (data->name)
1472 wfree(data->name);
1473 wfree(data);
1476 WMFreeArray(files);
1477 WMFreeArray(dirs);
1479 return menu;
1482 /************ Menu Configuration From WMRootMenu *************/
1484 static WMenu *makeDefaultMenu(WScreen * scr)
1486 WMenu *menu = NULL;
1488 menu = wMenuCreate(scr, _("Commands"), True);
1489 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1490 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1491 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1492 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1493 return menu;
1497 *----------------------------------------------------------------------
1498 * configureMenu--
1499 * Reads root menu configuration from defaults database.
1501 *----------------------------------------------------------------------
1503 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1505 WMenu *menu = NULL;
1506 WMPropList *elem;
1507 int i, count;
1508 WMPropList *title, *command, *params;
1509 char *tmp, *mtitle;
1511 if (WMIsPLString(definition)) {
1512 struct stat stat_buf;
1513 char *path = NULL;
1514 Bool menu_is_default = False;
1516 /* menu definition is a string. Probably a path, so parse the file */
1518 tmp = wexpandpath(WMGetFromPLString(definition));
1520 path = getLocalizedMenuFile(tmp);
1522 if (!path)
1523 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1525 if (!path) {
1526 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1527 menu_is_default = True;
1530 if (!path) {
1531 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1532 wfree(tmp);
1533 return NULL;
1536 if (stat(path, &stat_buf) < 0) {
1537 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1538 wfree(path);
1539 wfree(tmp);
1540 return NULL;
1543 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1544 /* if the pointer in WMRootMenu has changed */
1545 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1547 if (menu_is_default) {
1548 wwarning(_
1549 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1550 path);
1553 menu = readMenuFile(scr, path);
1554 if (menu)
1555 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1556 } else {
1557 menu = NULL;
1559 wfree(path);
1560 wfree(tmp);
1562 return menu;
1565 count = WMGetPropListItemCount(definition);
1566 if (count == 0)
1567 return NULL;
1569 elem = WMGetFromPLArray(definition, 0);
1570 if (!WMIsPLString(elem)) {
1571 tmp = WMGetPropListDescription(elem, False);
1572 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1573 wfree(tmp);
1574 return NULL;
1576 mtitle = WMGetFromPLString(elem);
1578 menu = wMenuCreate(scr, M_(mtitle), False);
1579 menu->on_destroy = removeShortcutsForMenu;
1581 #ifdef GLOBAL_SUBMENU_FILE
1582 if (includeGlobals) {
1583 WMenu *submenu;
1584 WMenuEntry *mentry;
1586 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1588 if (submenu) {
1589 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1590 wMenuEntrySetCascade(menu, mentry, submenu);
1593 #endif
1595 for (i = 1; i < count; i++) {
1596 elem = WMGetFromPLArray(definition, i);
1597 #if 0
1598 if (WMIsPLString(elem)) {
1599 char *file;
1601 file = WMGetFromPLString(elem);
1604 #endif
1605 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1606 goto error;
1608 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1609 WMenu *submenu;
1610 WMenuEntry *mentry;
1612 /* submenu */
1613 submenu = configureMenu(scr, elem, True);
1614 if (submenu) {
1615 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1616 wMenuEntrySetCascade(menu, mentry, submenu);
1618 } else {
1619 int idx = 0;
1620 WMPropList *shortcut;
1621 /* normal entry */
1623 title = WMGetFromPLArray(elem, idx++);
1624 shortcut = WMGetFromPLArray(elem, idx++);
1625 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1626 shortcut = WMGetFromPLArray(elem, idx++);
1627 command = WMGetFromPLArray(elem, idx++);
1628 } else {
1629 command = shortcut;
1630 shortcut = NULL;
1632 params = WMGetFromPLArray(elem, idx++);
1634 if (!title || !command)
1635 goto error;
1637 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1638 shortcut ? WMGetFromPLString(shortcut) : NULL,
1639 WMGetFromPLString(command),
1640 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1642 continue;
1644 error:
1645 tmp = WMGetPropListDescription(elem, False);
1646 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1647 wfree(tmp);
1650 return menu;
1654 *----------------------------------------------------------------------
1655 * OpenRootMenu--
1656 * Opens the root menu, parsing the menu configuration from the
1657 * defaults database.
1658 * If the menu is already mapped and is not sticked to the
1659 * root window, it will be unmapped.
1661 * Side effects:
1662 * The menu may be remade.
1664 * Notes:
1665 * Construction of OPEN_MENU entries are delayed to the moment the
1666 * user map's them.
1667 *----------------------------------------------------------------------
1669 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1671 WMenu *menu = NULL;
1672 WMPropList *definition;
1674 static WMPropList *domain=NULL;
1676 if (!domain) {
1677 domain = WMCreatePLString("WMRootMenu");
1681 scr->flags.root_menu_changed_shortcuts = 0;
1682 scr->flags.added_workspace_menu = 0;
1683 scr->flags.added_windows_menu = 0;
1685 if (scr->root_menu && scr->root_menu->flags.mapped) {
1686 menu = scr->root_menu;
1687 if (!menu->flags.buttoned) {
1688 wMenuUnmap(menu);
1689 } else {
1690 wRaiseFrame(menu->frame->core);
1692 if (keyboard)
1693 wMenuMapAt(menu, 0, 0, True);
1694 else
1695 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1697 return;
1700 definition = WDRootMenu->dictionary;
1703 definition = PLGetDomain(domain);
1705 if (definition) {
1706 if (WMIsPLArray(definition)) {
1707 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1708 menu = configureMenu(scr, definition, True);
1709 if (menu)
1710 menu->timestamp = WDRootMenu->timestamp;
1712 } else
1713 menu = NULL;
1714 } else {
1715 menu = configureMenu(scr, definition, True);
1719 if (!menu) {
1720 /* menu hasn't changed or could not be read */
1721 if (!scr->root_menu) {
1722 wMessageDialog(scr, _("Error"),
1723 _("The applications menu could not be loaded. "
1724 "Look at the console output for a detailed "
1725 "description of the errors."), _("OK"), NULL, NULL);
1727 menu = makeDefaultMenu(scr);
1728 scr->root_menu = menu;
1730 menu = scr->root_menu;
1731 } else {
1732 /* new root menu */
1733 if (scr->root_menu) {
1734 wMenuDestroy(scr->root_menu, True);
1736 scr->root_menu = menu;
1738 if (menu) {
1739 int newx, newy;
1741 if (keyboard && x == 0 && y == 0) {
1742 newx = newy = 0;
1743 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1744 newx = x - menu->frame->core->width / 2;
1745 newy = y - menu->frame->core->height / 2;
1746 } else {
1747 newx = x - menu->frame->core->width / 2;
1748 newy = y;
1750 wMenuMapAt(menu, newx, newy, keyboard);
1753 if (scr->flags.root_menu_changed_shortcuts)
1754 rebindKeygrabs(scr);