Fix possible missing NUL at end of string
[wmaker-crm.git] / src / rootmenu.c
blob0ecd3b4c2c7e86fd8350c0108064185c8c302c00
1 /* rootmenu.c- user defined menu
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "wconfig.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <sys/types.h>
31 #include <string.h>
32 #include <strings.h>
33 #include <ctype.h>
34 #include <time.h>
35 #include <dirent.h>
37 #include <X11/Xlib.h>
38 #include <X11/Xutil.h>
39 #include <X11/Xatom.h>
41 #include "WindowMaker.h"
42 #include "actions.h"
43 #include "menu.h"
44 #include "funcs.h"
45 #include "dialog.h"
46 #include "keybind.h"
47 #include "stacking.h"
48 #include "workspace.h"
49 #include "defaults.h"
50 #include "framewin.h"
51 #include "session.h"
52 #include "xmodifier.h"
54 #include <WINGs/WUtil.h>
56 #define MAX_SHORTCUT_LENGTH 32
58 extern char *Locale;
59 extern WDDomain *WDRootMenu;
60 extern Cursor wCursor[WCUR_LAST];
61 extern WPreferences wPreferences;
63 static WMenu *readMenuPipe(WScreen * scr, char **file_name);
64 static WMenu *readMenuFile(WScreen * scr, char *file_name);
65 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **file_name, char *command);
66 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals);
68 typedef struct Shortcut {
69 struct Shortcut *next;
71 int modifier;
72 KeyCode keycode;
73 WMenuEntry *entry;
74 WMenu *menu;
75 } Shortcut;
77 static Shortcut *shortcutList = NULL;
80 * Syntax:
81 * # main menu
82 * "Menu Name" MENU
83 * "Title" EXEC command_to_exec -params
84 * "Submenu" MENU
85 * "Title" EXEC command_to_exec -params
86 * "Submenu" END
87 * "Workspaces" WORKSPACE_MENU
88 * "Title" built_in_command
89 * "Quit" EXIT
90 * "Quick Quit" EXIT QUICK
91 * "Menu Name" END
93 * Commands may be preceded by SHORTCUT key
95 * Built-in commands:
97 * INFO_PANEL - shows the Info Panel
98 * LEGAL_PANEL - shows the Legal info panel
99 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
100 * REFRESH - forces the desktop to be repainted
101 * EXIT [QUICK] - exit the window manager [without confirmation]
102 * EXEC <program> - execute an external program
103 * SHEXEC <command> - execute a shell command
104 * WORKSPACE_MENU - places the workspace submenu
105 * ARRANGE_ICONS
106 * RESTART [<window manager>] - restarts the window manager
107 * SHOW_ALL - unhide all windows on workspace
108 * HIDE_OTHERS - hides all windows excep the focused one
109 * OPEN_MENU file - read menu data from file which must be a valid menu file.
110 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
111 * - read menu data from directory(ies) and
112 * eventually precede each with a command.
113 * OPEN_MENU | command
114 * - opens command and uses its stdout to construct and insert
115 * the resulting menu in current position. The output of
116 * command must be a valid menu description.
117 * The space between '|' and command is optional.
118 * || will do the same, but will not cache the contents.
119 * SAVE_SESSION - saves the current state of the desktop, which include
120 * all running applications, all their hints (geometry,
121 * position on screen, workspace they live on, the dock
122 * or clip from where they were launched, and
123 * if minimized, shaded or hidden. Also saves the current
124 * workspace the user is on. All will be restored on every
125 * start of windowmaker until another SAVE_SESSION or
126 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
127 * WindowMaker domain file, then saving is automatically
128 * done on every windowmaker exit, overwriting any
129 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
130 * dock state now.
131 * CLEAR_SESSION - clears any previous saved session. This will not have
132 * any effect if SaveSessionOnExit is True.
136 #define M_QUICK 1
138 /* menu commands */
140 static void execCommand(WMenu * menu, WMenuEntry * entry)
142 char *cmdline;
144 cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
146 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
147 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);
148 XSync(dpy, 0);
150 if (cmdline) {
151 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
152 wfree(cmdline);
154 XUngrabPointer(dpy, CurrentTime);
155 XSync(dpy, 0);
158 static void exitCommand(WMenu * menu, WMenuEntry * entry)
160 static int inside = 0;
161 int result;
163 /* prevent reentrant calls */
164 if (inside)
165 return;
166 inside = 1;
168 #define R_CANCEL 0
169 #define R_EXIT 1
171 result = R_CANCEL;
173 if ((long)entry->clientdata == M_QUICK) {
174 result = R_EXIT;
175 } else {
176 int r, oldSaveSessionFlag;
178 oldSaveSessionFlag = wPreferences.save_session_on_exit;
179 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
180 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
182 if (r == WAPRDefault) {
183 result = R_EXIT;
184 } else if (r == WAPRAlternate) {
185 /* Don't modify the "save session on exit" flag if the
186 * user canceled the operation. */
187 wPreferences.save_session_on_exit = oldSaveSessionFlag;
190 if (result == R_EXIT)
191 Shutdown(WSExitMode);
193 #undef R_EXIT
194 #undef R_CANCEL
195 inside = 0;
198 static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
200 static int inside = 0;
201 int result;
203 /* prevent reentrant calls */
204 if (inside)
205 return;
206 inside = 1;
208 #define R_CANCEL 0
209 #define R_CLOSE 1
210 #define R_KILL 2
212 result = R_CANCEL;
213 if ((long)entry->clientdata == M_QUICK)
214 result = R_CLOSE;
215 else {
217 int r, oldSaveSessionFlag;
219 oldSaveSessionFlag = wPreferences.save_session_on_exit;
221 r = wExitDialog(menu->frame->screen_ptr,
222 _("Kill X session"),
223 _("Kill Window System session?\n"
224 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
225 if (r == WAPRDefault) {
226 result = R_KILL;
227 } else if (r == WAPRAlternate) {
228 /* Don't modify the "save session on exit" flag if the
229 * user canceled the operation. */
230 wPreferences.save_session_on_exit = oldSaveSessionFlag;
235 if (result != R_CANCEL) {
237 Shutdown(WSKillMode);
240 #undef R_CLOSE
241 #undef R_CANCEL
242 #undef R_KILL
243 inside = 0;
246 static void restartCommand(WMenu * menu, WMenuEntry * entry)
248 Shutdown(WSRestartPreparationMode);
249 Restart((char *)entry->clientdata, False);
250 Restart(NULL, True);
253 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
255 wRefreshDesktop(menu->frame->screen_ptr);
258 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
260 wArrangeIcons(menu->frame->screen_ptr, True);
263 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
265 wShowAllWindows(menu->frame->screen_ptr);
268 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
270 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
273 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
275 if (!wPreferences.save_session_on_exit)
276 wSessionSaveState(menu->frame->screen_ptr);
278 wScreenSaveState(menu->frame->screen_ptr);
281 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
283 wSessionClearState(menu->frame->screen_ptr);
284 wScreenSaveState(menu->frame->screen_ptr);
287 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
289 wShowInfoPanel(menu->frame->screen_ptr);
292 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
294 wShowLegalPanel(menu->frame->screen_ptr);
297 /********************************************************************/
299 static char *getLocalizedMenuFile(char *menu)
301 char *buffer, *ptr, *locale;
302 int len;
304 if (!Locale)
305 return NULL;
307 len = strlen(menu) + strlen(Locale) + 8;
308 buffer = wmalloc(len);
310 /* try menu.locale_name */
311 snprintf(buffer, len, "%s.%s", menu, Locale);
312 if (access(buffer, F_OK) == 0) {
313 return buffer;
316 /* position of locale in our buffer */
317 locale = buffer + strlen(menu) + 1;
319 /* check if it is in the form aa_bb.encoding and check for aa_bb */
320 ptr = strchr(locale, '.');
321 if (ptr) {
322 *ptr = 0;
323 if (access(buffer, F_OK) == 0) {
324 return buffer;
327 /* now check for aa */
328 ptr = strchr(locale, '_');
329 if (ptr) {
330 *ptr = 0;
331 if (access(buffer, F_OK) == 0) {
332 return buffer;
336 wfree(buffer);
338 return NULL;
341 Bool wRootMenuPerformShortcut(XEvent * event)
343 WScreen *scr = wScreenForRootWindow(event->xkey.root);
344 Shortcut *ptr;
345 int modifiers;
346 int done = 0;
348 /* ignore CapsLock */
349 modifiers = event->xkey.state & ValidModMask;
351 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
352 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
353 continue;
355 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
356 (*ptr->entry->callback) (ptr->menu, ptr->entry);
357 done = True;
361 return done;
364 void wRootMenuBindShortcuts(Window window)
366 Shortcut *ptr;
368 ptr = shortcutList;
369 while (ptr) {
370 if (ptr->modifier != AnyModifier) {
371 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
372 window, True, GrabModeAsync, GrabModeAsync);
373 #ifdef NUMLOCK_HACK
374 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
375 #endif
377 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
378 ptr = ptr->next;
382 static void rebindKeygrabs(WScreen * scr)
384 WWindow *wwin;
386 wwin = scr->focused_window;
388 while (wwin != NULL) {
389 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
391 if (!WFLAGP(wwin, no_bind_keys)) {
392 wWindowSetKeyGrabs(wwin);
394 wwin = wwin->prev;
398 static void removeShortcutsForMenu(WMenu * menu)
400 Shortcut *ptr, *tmp;
401 Shortcut *newList = NULL;
403 ptr = shortcutList;
404 while (ptr != NULL) {
405 tmp = ptr->next;
406 if (ptr->menu == menu) {
407 wfree(ptr);
408 } else {
409 ptr->next = newList;
410 newList = ptr;
412 ptr = tmp;
414 shortcutList = newList;
415 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
418 static Bool addShortcut(char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
420 Shortcut *ptr;
421 KeySym ksym;
422 char *k;
423 char buf[MAX_SHORTCUT_LENGTH], *b;
425 ptr = wmalloc(sizeof(Shortcut));
427 wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
428 b = (char *)buf;
430 /* get modifiers */
431 ptr->modifier = 0;
432 while ((k = strchr(b, '+')) != NULL) {
433 int mod;
435 *k = 0;
436 mod = wXModifierFromKey(b);
437 if (mod < 0) {
438 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
439 wfree(ptr);
440 return False;
442 ptr->modifier |= mod;
444 b = k + 1;
447 /* get key */
448 ksym = XStringToKeysym(b);
450 if (ksym == NoSymbol) {
451 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
452 file, shortcutDefinition, entry->text);
453 wfree(ptr);
454 return False;
457 ptr->keycode = XKeysymToKeycode(dpy, ksym);
458 if (ptr->keycode == 0) {
459 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
460 shortcutDefinition, entry->text);
461 wfree(ptr);
462 return False;
465 ptr->menu = menu;
466 ptr->entry = entry;
468 ptr->next = shortcutList;
469 shortcutList = ptr;
471 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
473 return True;
476 /*******************************/
478 static char *cropline(char *line)
480 char *end;
482 if (strlen(line) == 0)
483 return line;
485 end = &(line[strlen(line)]) - 1;
486 while (isspace(*line) && *line != 0)
487 line++;
488 while (end > line && isspace(*end)) {
489 *end = 0;
490 end--;
492 return line;
495 static char *next_token(char *line, char **next)
497 char *tmp, c;
498 char *ret;
500 *next = NULL;
501 while (*line == ' ' || *line == '\t')
502 line++;
504 tmp = line;
506 if (*tmp == '"') {
507 tmp++;
508 line++;
509 while (*tmp != 0 && *tmp != '"')
510 tmp++;
511 if (*tmp != '"') {
512 wwarning(_("%s: unmatched '\"' in menu file"), line);
513 return NULL;
515 } else {
516 do {
517 if (*tmp == '\\')
518 tmp++;
520 if (*tmp != 0)
521 tmp++;
523 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
526 c = *tmp;
527 *tmp = 0;
528 ret = wstrdup(line);
529 *tmp = c;
531 if (c == 0)
532 return ret;
533 else
534 tmp++;
536 /* skip blanks */
537 while (*tmp == ' ' || *tmp == '\t')
538 tmp++;
540 if (*tmp != 0)
541 *next = tmp;
543 return ret;
546 static void separateCommand(char *line, char ***file, char **command)
548 char *token, *tmp = line;
549 WMArray *array = WMCreateArray(4);
550 int count, i;
552 *file = NULL;
553 *command = NULL;
554 do {
555 token = next_token(tmp, &tmp);
556 if (token) {
557 if (strcmp(token, "WITH") == 0) {
558 if (tmp != NULL && *tmp != 0)
559 *command = wstrdup(tmp);
560 else
561 wwarning(_("%s: missing command"), line);
562 break;
564 WMAddToArray(array, token);
566 } while (token != NULL && tmp != NULL);
568 count = WMGetArrayItemCount(array);
569 if (count > 0) {
570 *file = wmalloc(sizeof(char *) * (count + 1));
571 (*file)[count] = NULL;
572 for (i = 0; i < count; i++) {
573 (*file)[i] = WMGetFromArray(array, i);
576 WMFreeArray(array);
579 static WMenu *constructPLMenu(WScreen *screen, char *path)
581 WMPropList *pl = NULL;
582 WMenu *menu = NULL;
584 if (!path)
585 return NULL;
587 pl = WMReadPropListFromFile(path);
588 if (!pl)
589 return NULL;
591 menu = configureMenu(screen, pl, False);
592 if (!menu)
593 return NULL;
595 menu->on_destroy = removeShortcutsForMenu;
596 return menu;
599 static void constructMenu(WMenu * menu, WMenuEntry * entry)
601 WMenu *submenu;
602 struct stat stat_buf;
603 char **path;
604 char *cmd;
605 char *lpath = NULL;
606 int i, first = -1;
607 time_t last = 0;
609 separateCommand((char *)entry->clientdata, &path, &cmd);
610 if (path == NULL || *path == NULL || **path == 0) {
611 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
612 return;
615 if (path[0][0] == '|') {
616 /* pipe menu */
618 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
619 /* parse pipe */
621 submenu = readMenuPipe(menu->frame->screen_ptr, path);
623 if (submenu != NULL) {
624 if (path[0][1] == '|')
625 submenu->timestamp = 0;
626 else
627 submenu->timestamp = 1; /* there's no automatic reloading */
629 } else {
630 submenu = NULL;
633 } else {
635 /* try interpreting path as a proplist file */
636 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
637 /* if unsuccessful, try it as an old-style file */
638 if (!submenu) {
640 i = 0;
641 while (path[i] != NULL) {
642 char *tmp;
644 if (strcmp(path[i], "-noext") == 0) {
645 i++;
646 continue;
649 tmp = wexpandpath(path[i]);
650 wfree(path[i]);
651 lpath = getLocalizedMenuFile(tmp);
652 if (lpath) {
653 wfree(tmp);
654 path[i] = lpath;
655 lpath = NULL;
656 } else {
657 path[i] = tmp;
660 if (stat(path[i], &stat_buf) == 0) {
661 if (last < stat_buf.st_mtime)
662 last = stat_buf.st_mtime;
663 if (first < 0)
664 first = i;
665 } else {
666 werror(_("%s:could not stat menu"), path[i]);
667 /*goto finish; */
670 i++;
673 if (first < 0) {
674 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
675 goto finish;
677 stat(path[first], &stat_buf);
678 if (!menu->cascades[entry->cascade]
679 || menu->cascades[entry->cascade]->timestamp < last) {
681 if (S_ISDIR(stat_buf.st_mode)) {
682 /* menu directory */
683 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
684 if (submenu)
685 submenu->timestamp = last;
686 } else if (S_ISREG(stat_buf.st_mode)) {
687 /* menu file */
689 if (cmd || path[1])
690 wwarning(_("too many parameters in OPEN_MENU: %s"),
691 (char *)entry->clientdata);
693 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
694 if (submenu)
695 submenu->timestamp = stat_buf.st_mtime;
696 } else {
697 submenu = NULL;
699 } else {
700 submenu = NULL;
705 if (submenu) {
706 wMenuEntryRemoveCascade(menu, entry);
707 wMenuEntrySetCascade(menu, entry, submenu);
710 finish:
711 i = 0;
712 while (path[i] != NULL)
713 wfree(path[i++]);
714 wfree(path);
715 if (cmd)
716 wfree(cmd);
719 static void cleanupWorkspaceMenu(WMenu * menu)
721 if (menu->frame->screen_ptr->workspace_menu == menu)
722 menu->frame->screen_ptr->workspace_menu = NULL;
725 static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
727 WMenu *wsmenu;
728 WMenuEntry *entry;
730 if (scr->flags.added_workspace_menu) {
731 wwarning(_
732 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
733 return NULL;
734 } else {
735 scr->flags.added_workspace_menu = 1;
737 wsmenu = wWorkspaceMenuMake(scr, True);
738 wsmenu->on_destroy = cleanupWorkspaceMenu;
740 scr->workspace_menu = wsmenu;
741 entry = wMenuAddCallback(menu, title, NULL, NULL);
742 wMenuEntrySetCascade(menu, entry, wsmenu);
744 wWorkspaceMenuUpdate(scr, wsmenu);
746 return entry;
749 static void cleanupWindowsMenu(WMenu * menu)
751 if (menu->frame->screen_ptr->switch_menu == menu)
752 menu->frame->screen_ptr->switch_menu = NULL;
755 static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
757 WMenu *wwmenu;
758 WWindow *wwin;
759 WMenuEntry *entry;
761 if (scr->flags.added_windows_menu) {
762 wwarning(_
763 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
764 return NULL;
765 } else {
766 scr->flags.added_windows_menu = 1;
768 wwmenu = wMenuCreate(scr, _("Window List"), False);
769 wwmenu->on_destroy = cleanupWindowsMenu;
770 scr->switch_menu = wwmenu;
771 wwin = scr->focused_window;
772 while (wwin) {
773 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
775 wwin = wwin->prev;
777 entry = wMenuAddCallback(menu, title, NULL, NULL);
778 wMenuEntrySetCascade(menu, entry, wwmenu);
780 return entry;
783 static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
784 char *params, char *file_name)
786 WScreen *scr;
787 WMenuEntry *entry = NULL;
788 Bool shortcutOk = False;
790 if (!menu)
791 return NULL;
792 scr = menu->frame->screen_ptr;
793 if (strcmp(command, "OPEN_MENU") == 0) {
794 if (!params) {
795 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
796 } else {
797 WMenu *dummy;
798 char *path;
800 path = wfindfile(DEF_CONFIG_PATHS, params);
801 if (!path) {
802 path = wstrdup(params);
804 dummy = wMenuCreate(scr, title, False);
805 dummy->on_destroy = removeShortcutsForMenu;
806 entry = wMenuAddCallback(menu, title, constructMenu, path);
807 entry->free_cdata = free;
808 wMenuEntrySetCascade(menu, entry, dummy);
810 } else if (strcmp(command, "EXEC") == 0) {
811 if (!params)
812 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
813 else {
814 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
815 entry->free_cdata = free;
816 shortcutOk = True;
818 } else if (strcmp(command, "SHEXEC") == 0) {
819 if (!params)
820 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
821 else {
822 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
823 entry->free_cdata = free;
824 shortcutOk = True;
826 } else if (strcmp(command, "EXIT") == 0) {
828 if (params && strcmp(params, "QUICK") == 0)
829 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
830 else
831 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
833 shortcutOk = True;
834 } else if (strcmp(command, "SHUTDOWN") == 0) {
836 if (params && strcmp(params, "QUICK") == 0)
837 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
838 else
839 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
841 shortcutOk = True;
842 } else if (strcmp(command, "REFRESH") == 0) {
843 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
845 shortcutOk = True;
846 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
847 entry = addWorkspaceMenu(scr, menu, title);
849 shortcutOk = True;
850 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
851 entry = addWindowsMenu(scr, menu, title);
853 shortcutOk = True;
854 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
855 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
857 shortcutOk = True;
858 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
859 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
861 shortcutOk = True;
862 } else if (strcmp(command, "SHOW_ALL") == 0) {
863 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
865 shortcutOk = True;
866 } else if (strcmp(command, "RESTART") == 0) {
867 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
868 entry->free_cdata = free;
869 shortcutOk = True;
870 } else if (strcmp(command, "SAVE_SESSION") == 0) {
871 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
873 shortcutOk = True;
874 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
875 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
876 shortcutOk = True;
877 } else if (strcmp(command, "INFO_PANEL") == 0) {
878 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
879 shortcutOk = True;
880 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
881 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
882 shortcutOk = True;
883 } else {
884 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
886 return NULL;
889 if (shortcut && entry) {
890 if (!shortcutOk) {
891 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
892 } else {
893 if (addShortcut(file_name, shortcut, menu, entry)) {
895 entry->rtext = GetShortcutString(shortcut);
897 entry->rtext = wstrdup(shortcut);
903 return entry;
906 /******************* Menu Configuration From File *******************/
908 static void separateline(char *line, char *title, char *command, char *parameter, char *shortcut)
910 int l, i;
912 l = strlen(line);
914 *title = 0;
915 *command = 0;
916 *parameter = 0;
917 *shortcut = 0;
918 /* get the title */
919 while (isspace(*line) && (*line != 0))
920 line++;
921 if (*line == '"') {
922 line++;
923 i = 0;
924 while (line[i] != '"' && (line[i] != 0))
925 i++;
926 if (line[i] != '"')
927 return;
928 } else {
929 i = 0;
930 while (!isspace(line[i]) && (line[i] != 0))
931 i++;
933 strncpy(title, line, i);
934 title[i++] = 0;
935 line += i;
937 /* get the command or shortcut keyword */
938 while (isspace(*line) && (*line != 0))
939 line++;
940 if (*line == 0)
941 return;
942 i = 0;
943 while (!isspace(line[i]) && (line[i] != 0))
944 i++;
945 strncpy(command, line, i);
946 command[i++] = 0;
947 line += i;
949 if (strcmp(command, "SHORTCUT") == 0) {
950 /* get the shortcut key */
951 while (isspace(*line) && (*line != 0))
952 line++;
953 if (*line == '"') {
954 line++;
955 i = 0;
956 while (line[i] != '"' && (line[i] != 0))
957 i++;
958 if (line[i] != '"')
959 return;
960 } else {
961 i = 0;
962 while (!isspace(line[i]) && (line[i] != 0))
963 i++;
965 strncpy(shortcut, line, i);
966 shortcut[i++] = 0;
967 line += i;
969 *command = 0;
971 /* get the command */
972 while (isspace(*line) && (*line != 0))
973 line++;
974 if (*line == 0)
975 return;
976 i = 0;
977 while (!isspace(line[i]) && (line[i] != 0))
978 i++;
979 strncpy(command, line, i);
980 command[i++] = 0;
981 line += i;
984 /* get the parameters */
985 while (isspace(*line) && (*line != 0))
986 line++;
987 if (*line == 0)
988 return;
990 if (*line == '"') {
991 line++;
992 l = 0;
993 while (line[l] != 0 && line[l] != '"') {
994 parameter[l] = line[l];
995 l++;
997 parameter[l] = 0;
998 return;
1001 l = strlen(line);
1002 while (isspace(line[l]) && (l > 0))
1003 l--;
1004 strncpy(parameter, line, l);
1005 parameter[l] = 0;
1008 static WMenu *parseCascade(WScreen * scr, WMenu * menu, FILE * file, char *file_name)
1010 char linebuf[MAXLINE];
1011 char elinebuf[MAXLINE];
1012 char title[MAXLINE];
1013 char command[MAXLINE];
1014 char shortcut[MAXLINE];
1015 char params[MAXLINE];
1016 char *line;
1018 while (!feof(file)) {
1019 int lsize, ok;
1021 ok = 0;
1022 fgets(linebuf, MAXLINE, file);
1023 line = cropline(linebuf);
1024 lsize = strlen(line);
1025 do {
1026 if (line[lsize - 1] == '\\') {
1027 char *line2;
1028 int lsize2;
1029 fgets(elinebuf, MAXLINE, file);
1030 line2 = cropline(elinebuf);
1031 lsize2 = strlen(line2);
1032 if (lsize2 + lsize > MAXLINE) {
1033 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1034 file_name, line);
1035 ok = 2;
1036 } else {
1037 line[lsize - 1] = 0;
1038 lsize += lsize2 - 1;
1039 strcat(line, line2);
1041 } else {
1042 ok = 1;
1044 } while (!ok && !feof(file));
1045 if (ok == 2)
1046 continue;
1048 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1049 continue;
1051 separateline(line, title, command, params, shortcut);
1053 if (!command[0]) {
1054 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1055 goto error;
1058 if (strcasecmp(command, "MENU") == 0) {
1059 WMenu *cascade;
1061 /* start submenu */
1063 cascade = wMenuCreate(scr, M_(title), False);
1064 cascade->on_destroy = removeShortcutsForMenu;
1065 if (parseCascade(scr, cascade, file, file_name) == NULL) {
1066 wMenuDestroy(cascade, True);
1067 } else {
1068 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
1070 } else if (strcasecmp(command, "END") == 0) {
1071 /* end of menu */
1072 return menu;
1074 } else {
1075 /* normal items */
1076 addMenuEntry(menu, M_(title), shortcut[0] ? shortcut : NULL, command,
1077 params[0] ? params : NULL, file_name);
1081 wwarning(_("%s:syntax error in menu file:END declaration missing"), file_name);
1082 return menu;
1084 error:
1085 return menu;
1088 static WMenu *readMenuFile(WScreen * scr, char *file_name)
1090 WMenu *menu = NULL;
1091 FILE *file = NULL;
1092 char linebuf[MAXLINE];
1093 char title[MAXLINE];
1094 char shortcut[MAXLINE];
1095 char command[MAXLINE];
1096 char params[MAXLINE];
1097 char *line;
1098 #ifdef USECPP
1099 char *args;
1100 int cpp = 0;
1101 #endif
1103 #ifdef USECPP
1104 if (!wPreferences.flags.nocpp) {
1105 args = MakeCPPArgs(file_name);
1106 if (!args) {
1107 wwarning(_("could not make arguments for menu file preprocessor"));
1108 } else {
1109 snprintf(command, sizeof(command), "%s %s %s", CPP_PATH, args, file_name);
1110 wfree(args);
1111 file = popen(command, "r");
1112 if (!file) {
1113 werror(_("%s:could not open/preprocess menu file"), file_name);
1114 } else {
1115 cpp = 1;
1119 #endif /* USECPP */
1121 if (!file) {
1122 file = fopen(file_name, "rb");
1123 if (!file) {
1124 werror(_("%s:could not open menu file"), file_name);
1125 return NULL;
1129 while (!feof(file)) {
1130 if (!fgets(linebuf, MAXLINE, file))
1131 break;
1132 line = cropline(linebuf);
1133 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1134 continue;
1136 separateline(line, title, command, params, shortcut);
1138 if (!command[0]) {
1139 wwarning(_("%s:missing command in menu config: %s"), file_name, line);
1140 break;
1142 if (strcasecmp(command, "MENU") == 0) {
1143 menu = wMenuCreate(scr, M_(title), True);
1144 menu->on_destroy = removeShortcutsForMenu;
1145 if (!parseCascade(scr, menu, file, file_name)) {
1146 wMenuDestroy(menu, True);
1148 break;
1149 } else {
1150 wwarning(_("%s:invalid menu file. MENU command is missing"), file_name);
1151 break;
1155 #ifdef USECPP
1156 if (cpp) {
1157 if (pclose(file) == -1) {
1158 werror(_("error reading preprocessed menu data"));
1160 } else {
1161 fclose(file);
1163 #else
1164 fclose(file);
1165 #endif
1167 return menu;
1170 /************ Menu Configuration From Pipe *************/
1172 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1174 WMenu *menu = NULL;
1175 FILE *file = NULL;
1176 char linebuf[MAXLINE];
1177 char title[MAXLINE];
1178 char command[MAXLINE];
1179 char params[MAXLINE];
1180 char shortcut[MAXLINE];
1181 char *line;
1182 char *filename;
1183 char flat_file[MAXLINE];
1184 int i;
1185 #ifdef USECPP
1186 char *args;
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);
1212 #endif /* USECPP */
1214 if (!file) {
1215 file = popen(filename, "rb");
1217 if (!file) {
1218 werror(_("%s:could not open menu file"), filename);
1219 return NULL;
1223 while (!feof(file)) {
1224 if (!fgets(linebuf, MAXLINE, file))
1225 break;
1226 line = cropline(linebuf);
1227 if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
1228 continue;
1230 separateline(line, title, command, params, shortcut);
1232 if (!command[0]) {
1233 wwarning(_("%s:missing command in menu config: %s"), filename, line);
1234 break;
1236 if (strcasecmp(command, "MENU") == 0) {
1237 menu = wMenuCreate(scr, M_(title), True);
1238 menu->on_destroy = removeShortcutsForMenu;
1239 if (!parseCascade(scr, menu, file, filename)) {
1240 wMenuDestroy(menu, True);
1242 break;
1243 } else {
1244 wwarning(_("%s:no title given for the root menu"), filename);
1245 break;
1249 pclose(file);
1251 return menu;
1254 typedef struct {
1255 char *name;
1256 int index;
1257 } dir_data;
1259 static int myCompare(const void *d1, const void *d2)
1261 dir_data *p1 = *(dir_data **) d1;
1262 dir_data *p2 = *(dir_data **) d2;
1264 return strcmp(p1->name, p2->name);
1267 /************ Menu Configuration From Directory *************/
1269 static Bool isFilePackage(char *file)
1271 int l;
1273 /* check if the extension indicates this file is a
1274 * file package. For now, only recognize .themed */
1276 l = strlen(file);
1278 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1279 return True;
1280 } else {
1281 return False;
1285 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
1287 DIR *dir;
1288 struct dirent *dentry;
1289 struct stat stat_buf;
1290 WMenu *menu = NULL;
1291 char *buffer;
1292 WMArray *dirs = NULL, *files = NULL;
1293 WMArrayIterator iter;
1294 int length, i, have_space = 0;
1295 dir_data *data;
1296 int stripExtension = 0;
1298 dirs = WMCreateArray(16);
1299 files = WMCreateArray(16);
1301 i = 0;
1302 while (path[i] != NULL) {
1303 if (strcmp(path[i], "-noext") == 0) {
1304 stripExtension = 1;
1305 i++;
1306 continue;
1309 dir = opendir(path[i]);
1310 if (!dir) {
1311 i++;
1312 continue;
1315 while ((dentry = readdir(dir))) {
1317 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1318 continue;
1320 if (dentry->d_name[0] == '.')
1321 continue;
1323 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1324 if (!buffer) {
1325 werror(_("out of memory while constructing directory menu %s"), path[i]);
1326 break;
1329 strcpy(buffer, path[i]);
1330 strcat(buffer, "/");
1331 strcat(buffer, dentry->d_name);
1333 if (stat(buffer, &stat_buf) != 0) {
1334 werror(_("%s:could not stat file \"%s\" in menu directory"),
1335 path[i], dentry->d_name);
1336 } else {
1337 Bool isFilePack = False;
1339 data = NULL;
1340 if (S_ISDIR(stat_buf.st_mode)
1341 && !(isFilePack = isFilePackage(dentry->d_name))) {
1343 /* access always returns success for user root */
1344 if (access(buffer, X_OK) == 0) {
1345 /* Directory is accesible. Add to directory list */
1347 data = (dir_data *) wmalloc(sizeof(dir_data));
1348 data->name = wstrdup(dentry->d_name);
1349 data->index = i;
1351 WMAddToArray(dirs, data);
1353 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1354 /* Hack because access always returns X_OK success for user root */
1355 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1356 if ((command != NULL && access(buffer, R_OK) == 0) ||
1357 (command == NULL && access(buffer, X_OK) == 0 &&
1358 (stat_buf.st_mode & S_IXANY))) {
1360 data = (dir_data *) wmalloc(sizeof(dir_data));
1361 data->name = wstrdup(dentry->d_name);
1362 data->index = i;
1364 WMAddToArray(files, data);
1368 wfree(buffer);
1371 closedir(dir);
1372 i++;
1375 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1376 WMFreeArray(dirs);
1377 WMFreeArray(files);
1378 return NULL;
1381 WMSortArray(dirs, myCompare);
1382 WMSortArray(files, myCompare);
1384 menu = wMenuCreate(scr, M_(title), False);
1385 menu->on_destroy = removeShortcutsForMenu;
1387 WM_ITERATE_ARRAY(dirs, data, iter) {
1388 /* New directory. Use same OPEN_MENU command that was used
1389 * for the current directory. */
1390 length = strlen(path[data->index]) + strlen(data->name) + 6;
1391 if (stripExtension)
1392 length += 7;
1393 if (command)
1394 length += strlen(command) + 6;
1395 buffer = malloc(length);
1396 if (!buffer) {
1397 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1398 break;
1401 buffer[0] = '\0';
1402 if (stripExtension)
1403 strcat(buffer, "-noext ");
1405 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1407 if (have_space)
1408 strcat(buffer, "\"");
1409 strcat(buffer, path[data->index]);
1411 strcat(buffer, "/");
1412 strcat(buffer, data->name);
1413 if (have_space)
1414 strcat(buffer, "\"");
1415 if (command) {
1416 strcat(buffer, " WITH ");
1417 strcat(buffer, command);
1420 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1422 wfree(buffer);
1423 if (data->name)
1424 wfree(data->name);
1425 wfree(data);
1428 WM_ITERATE_ARRAY(files, data, iter) {
1429 /* executable: add as entry */
1430 length = strlen(path[data->index]) + strlen(data->name) + 6;
1431 if (command)
1432 length += strlen(command);
1434 buffer = malloc(length);
1435 if (!buffer) {
1436 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1437 break;
1440 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1441 if (command != NULL) {
1442 strcpy(buffer, command);
1443 strcat(buffer, " ");
1444 if (have_space)
1445 strcat(buffer, "\"");
1446 strcat(buffer, path[data->index]);
1447 } else {
1448 if (have_space) {
1449 buffer[0] = '"';
1450 buffer[1] = 0;
1451 strcat(buffer, path[data->index]);
1452 } else {
1453 strcpy(buffer, path[data->index]);
1456 strcat(buffer, "/");
1457 strcat(buffer, data->name);
1458 if (have_space)
1459 strcat(buffer, "\"");
1461 if (stripExtension) {
1462 char *ptr = strrchr(data->name, '.');
1463 if (ptr && ptr != data->name)
1464 *ptr = 0;
1466 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1468 wfree(buffer);
1469 if (data->name)
1470 wfree(data->name);
1471 wfree(data);
1474 WMFreeArray(files);
1475 WMFreeArray(dirs);
1477 return menu;
1480 /************ Menu Configuration From WMRootMenu *************/
1482 static WMenu *makeDefaultMenu(WScreen * scr)
1484 WMenu *menu = NULL;
1486 menu = wMenuCreate(scr, _("Commands"), True);
1487 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1488 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1489 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1490 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1491 return menu;
1495 *----------------------------------------------------------------------
1496 * configureMenu--
1497 * Reads root menu configuration from defaults database.
1499 *----------------------------------------------------------------------
1501 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1503 WMenu *menu = NULL;
1504 WMPropList *elem;
1505 int i, count;
1506 WMPropList *title, *command, *params;
1507 char *tmp, *mtitle;
1509 if (WMIsPLString(definition)) {
1510 struct stat stat_buf;
1511 char *path = NULL;
1512 Bool menu_is_default = False;
1514 /* menu definition is a string. Probably a path, so parse the file */
1516 tmp = wexpandpath(WMGetFromPLString(definition));
1518 path = getLocalizedMenuFile(tmp);
1520 if (!path)
1521 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1523 if (!path) {
1524 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1525 menu_is_default = True;
1528 if (!path) {
1529 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1530 wfree(tmp);
1531 return NULL;
1534 if (stat(path, &stat_buf) < 0) {
1535 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1536 wfree(path);
1537 wfree(tmp);
1538 return NULL;
1541 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1542 /* if the pointer in WMRootMenu has changed */
1543 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1545 if (menu_is_default) {
1546 wwarning(_
1547 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1548 path);
1551 menu = readMenuFile(scr, path);
1552 if (menu)
1553 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1554 } else {
1555 menu = NULL;
1557 wfree(path);
1558 wfree(tmp);
1560 return menu;
1563 count = WMGetPropListItemCount(definition);
1564 if (count == 0)
1565 return NULL;
1567 elem = WMGetFromPLArray(definition, 0);
1568 if (!WMIsPLString(elem)) {
1569 tmp = WMGetPropListDescription(elem, False);
1570 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1571 wfree(tmp);
1572 return NULL;
1574 mtitle = WMGetFromPLString(elem);
1576 menu = wMenuCreate(scr, M_(mtitle), False);
1577 menu->on_destroy = removeShortcutsForMenu;
1579 #ifdef GLOBAL_SUBMENU_FILE
1580 if (includeGlobals) {
1581 WMenu *submenu;
1582 WMenuEntry *mentry;
1584 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1586 if (submenu) {
1587 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1588 wMenuEntrySetCascade(menu, mentry, submenu);
1591 #endif
1593 for (i = 1; i < count; i++) {
1594 elem = WMGetFromPLArray(definition, i);
1595 #if 0
1596 if (WMIsPLString(elem)) {
1597 char *file;
1599 file = WMGetFromPLString(elem);
1602 #endif
1603 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1604 goto error;
1606 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1607 WMenu *submenu;
1608 WMenuEntry *mentry;
1610 /* submenu */
1611 submenu = configureMenu(scr, elem, True);
1612 if (submenu) {
1613 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1614 wMenuEntrySetCascade(menu, mentry, submenu);
1616 } else {
1617 int idx = 0;
1618 WMPropList *shortcut;
1619 /* normal entry */
1621 title = WMGetFromPLArray(elem, idx++);
1622 shortcut = WMGetFromPLArray(elem, idx++);
1623 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1624 shortcut = WMGetFromPLArray(elem, idx++);
1625 command = WMGetFromPLArray(elem, idx++);
1626 } else {
1627 command = shortcut;
1628 shortcut = NULL;
1630 params = WMGetFromPLArray(elem, idx++);
1632 if (!title || !command)
1633 goto error;
1635 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1636 shortcut ? WMGetFromPLString(shortcut) : NULL,
1637 WMGetFromPLString(command),
1638 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1640 continue;
1642 error:
1643 tmp = WMGetPropListDescription(elem, False);
1644 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1645 wfree(tmp);
1648 return menu;
1652 *----------------------------------------------------------------------
1653 * OpenRootMenu--
1654 * Opens the root menu, parsing the menu configuration from the
1655 * defaults database.
1656 * If the menu is already mapped and is not sticked to the
1657 * root window, it will be unmapped.
1659 * Side effects:
1660 * The menu may be remade.
1662 * Notes:
1663 * Construction of OPEN_MENU entries are delayed to the moment the
1664 * user map's them.
1665 *----------------------------------------------------------------------
1667 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1669 WMenu *menu = NULL;
1670 WMPropList *definition;
1672 static WMPropList *domain=NULL;
1674 if (!domain) {
1675 domain = WMCreatePLString("WMRootMenu");
1679 scr->flags.root_menu_changed_shortcuts = 0;
1680 scr->flags.added_workspace_menu = 0;
1681 scr->flags.added_windows_menu = 0;
1683 if (scr->root_menu && scr->root_menu->flags.mapped) {
1684 menu = scr->root_menu;
1685 if (!menu->flags.buttoned) {
1686 wMenuUnmap(menu);
1687 } else {
1688 wRaiseFrame(menu->frame->core);
1690 if (keyboard)
1691 wMenuMapAt(menu, 0, 0, True);
1692 else
1693 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1695 return;
1698 definition = WDRootMenu->dictionary;
1701 definition = PLGetDomain(domain);
1703 if (definition) {
1704 if (WMIsPLArray(definition)) {
1705 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1706 menu = configureMenu(scr, definition, True);
1707 if (menu)
1708 menu->timestamp = WDRootMenu->timestamp;
1710 } else
1711 menu = NULL;
1712 } else {
1713 menu = configureMenu(scr, definition, True);
1717 if (!menu) {
1718 /* menu hasn't changed or could not be read */
1719 if (!scr->root_menu) {
1720 wMessageDialog(scr, _("Error"),
1721 _("The applications menu could not be loaded. "
1722 "Look at the console output for a detailed "
1723 "description of the errors."), _("OK"), NULL, NULL);
1725 menu = makeDefaultMenu(scr);
1726 scr->root_menu = menu;
1728 menu = scr->root_menu;
1729 } else {
1730 /* new root menu */
1731 if (scr->root_menu) {
1732 wMenuDestroy(scr->root_menu, True);
1734 scr->root_menu = menu;
1736 if (menu) {
1737 int newx, newy;
1739 if (keyboard && x == 0 && y == 0) {
1740 newx = newy = 0;
1741 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1742 newx = x - menu->frame->core->width / 2;
1743 newy = y - menu->frame->core->height / 2;
1744 } else {
1745 newx = x - menu->frame->core->width / 2;
1746 newy = y;
1748 wMenuMapAt(menu, newx, newy, keyboard);
1751 if (scr->flags.root_menu_changed_shortcuts)
1752 rebindKeygrabs(scr);