code cleanup
[wmaker-crm.git] / src / rootmenu.c
blob2c94c384bf3bce982eee00308a00ee77f14cd44c
1 /* rootmenu.c- user defined menu
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997, 1998 Alfredo K. Kojima
6 * Copyright (c) 1998 Dan Pascu
7 *
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 #ifndef LITE
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <sys/stat.h>
32 #include <sys/wait.h>
33 #include <sys/types.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <time.h>
37 #include <dirent.h>
39 #include <X11/Xlib.h>
40 #include <X11/Xutil.h>
41 #include <X11/Xatom.h>
43 #include "WindowMaker.h"
44 #include "actions.h"
45 #include "menu.h"
46 #include "funcs.h"
47 #include "dialog.h"
48 #include "keybind.h"
49 #include "stacking.h"
50 #include "workspace.h"
51 #include "defaults.h"
52 #include "framewin.h"
53 #include "session.h"
54 #include "xmodifier.h"
56 #include <WINGs/WUtil.h>
60 extern char *Locale;
62 extern WDDomain *WDRootMenu;
64 extern Cursor wCursor[WCUR_LAST];
66 extern Time LastTimestamp;
68 extern WPreferences wPreferences;
70 extern int wScreenCount;
72 static WMenu *readMenuPipe(WScreen *scr, char **file_name);
73 static WMenu *readMenuFile(WScreen *scr, char *file_name);
74 static WMenu *readMenuDirectory(WScreen *scr, char *title, char **file_name,
75 char *command);
78 typedef struct Shortcut {
79 struct Shortcut *next;
81 int modifier;
82 KeyCode keycode;
83 WMenuEntry *entry;
84 WMenu *menu;
85 } Shortcut;
89 static Shortcut *shortcutList = NULL;
93 * Syntax:
94 * # main menu
95 * "Menu Name" MENU
96 * "Title" EXEC command_to_exec -params
97 * "Submenu" MENU
98 * "Title" EXEC command_to_exec -params
99 * "Submenu" END
100 * "Workspaces" WORKSPACE_MENU
101 * "Title" built_in_command
102 * "Quit" EXIT
103 * "Quick Quit" EXIT QUICK
104 * "Menu Name" END
106 * Commands may be preceded by SHORTCUT key
108 * Built-in commands:
110 * INFO_PANEL - shows the Info Panel
111 * LEGAL_PANEL - shows the Legal info panel
112 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
113 * REFRESH - forces the desktop to be repainted
114 * EXIT [QUICK] - exit the window manager [without confirmation]
115 * EXEC <program> - execute an external program
116 * SHEXEC <command> - execute a shell command
117 * WORKSPACE_MENU - places the workspace submenu
118 * ARRANGE_ICONS
119 * RESTART [<window manager>] - restarts the window manager
120 * SHOW_ALL - unhide all windows on workspace
121 * HIDE_OTHERS - hides all windows excep the focused one
122 * OPEN_MENU file - read menu data from file which must be a valid menu file.
123 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
124 * - read menu data from directory(ies) and
125 * eventually precede each with a command.
126 * OPEN_MENU | command
127 * - opens command and uses its stdout to construct and insert
128 * the resulting menu in current position. The output of
129 * command must be a valid menu description.
130 * The space between '|' and command is optional.
131 * || will do the same, but will not cache the contents.
132 * SAVE_SESSION - saves the current state of the desktop, which include
133 * all running applications, all their hints (geometry,
134 * position on screen, workspace they live on, the dock
135 * or clip from where they were launched, and
136 * if minimized, shaded or hidden. Also saves the current
137 * workspace the user is on. All will be restored on every
138 * start of windowmaker until another SAVE_SESSION or
139 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
140 * WindowMaker domain file, then saving is automatically
141 * done on every windowmaker exit, overwriting any
142 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
143 * dock state now.
144 * CLEAR_SESSION - clears any previous saved session. This will not have
145 * any effect if SaveSessionOnExit is True.
149 #define M_QUICK 1
151 /* menu commands */
153 static void
154 execCommand(WMenu *menu, WMenuEntry *entry)
156 char *cmdline;
158 cmdline = ExpandOptions(menu->frame->screen_ptr, (char*)entry->clientdata);
160 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
161 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT],
162 CurrentTime);
163 XSync(dpy, 0);
165 if (cmdline) {
166 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
167 wfree(cmdline);
169 XUngrabPointer(dpy, CurrentTime);
170 XSync(dpy, 0);
174 static void
175 exitCommand(WMenu *menu, WMenuEntry *entry)
177 static int inside = 0;
178 int result;
180 /* prevent reentrant calls */
181 if (inside)
182 return;
183 inside = 1;
185 #define R_CANCEL 0
186 #define R_EXIT 1
188 result = R_CANCEL;
190 if ((long)entry->clientdata==M_QUICK) {
191 result = R_EXIT;
192 } else {
193 int r, oldSaveSessionFlag;
195 oldSaveSessionFlag = wPreferences.save_session_on_exit;
196 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
197 _("Exit window manager?"),
198 _("Exit"), _("Cancel"), NULL);
200 if (r==WAPRDefault) {
201 result = R_EXIT;
202 } else if (r==WAPRAlternate) {
203 /* Don't modify the "save session on exit" flag if the
204 * user canceled the operation. */
205 wPreferences.save_session_on_exit = oldSaveSessionFlag;
208 if (result==R_EXIT) {
209 #ifdef DEBUG
210 printf("Exiting WindowMaker.\n");
211 #endif
212 Shutdown(WSExitMode);
214 #undef R_EXIT
215 #undef R_CANCEL
216 inside = 0;
220 static void
221 shutdownCommand(WMenu *menu, WMenuEntry *entry)
223 static int inside = 0;
224 int result;
226 /* prevent reentrant calls */
227 if (inside)
228 return;
229 inside = 1;
231 #define R_CANCEL 0
232 #define R_CLOSE 1
233 #define R_KILL 2
236 result = R_CANCEL;
237 if ((long)entry->clientdata==M_QUICK)
238 result = R_CLOSE;
239 else {
240 #ifdef XSMP_ENABLED
241 if (wSessionIsManaged()) {
242 int r;
244 r = wMessageDialog(menu->frame->screen_ptr,
245 _("Close X session"),
246 _("Close Window System session?\n"
247 "Kill might close applications with unsaved data."),
248 _("Close"), _("Kill"), _("Cancel"));
249 if (r==WAPRDefault)
250 result = R_CLOSE;
251 else if (r==WAPRAlternate)
252 result = R_KILL;
253 } else
254 #endif
256 int r, oldSaveSessionFlag;
258 oldSaveSessionFlag = wPreferences.save_session_on_exit;
260 r = wExitDialog(menu->frame->screen_ptr,
261 _("Kill X session"),
262 _("Kill Window System session?\n"
263 "(all applications will be closed)"),
264 _("Kill"), _("Cancel"), NULL);
265 if (r==WAPRDefault) {
266 result = R_KILL;
267 } else if (r==WAPRAlternate) {
268 /* Don't modify the "save session on exit" flag if the
269 * user canceled the operation. */
270 wPreferences.save_session_on_exit = oldSaveSessionFlag;
275 if (result!=R_CANCEL) {
276 #ifdef XSMP_ENABLED
277 if (result == R_CLOSE) {
278 Shutdown(WSLogoutMode);
279 } else
280 #endif /* XSMP_ENABLED */
282 Shutdown(WSKillMode);
285 #undef R_CLOSE
286 #undef R_CANCEL
287 #undef R_KILL
288 inside = 0;
292 static void
293 restartCommand(WMenu *menu, WMenuEntry *entry)
295 Shutdown(WSRestartPreparationMode);
296 Restart((char*)entry->clientdata, False);
297 Restart(NULL, True);
301 static void
302 refreshCommand(WMenu *menu, WMenuEntry *entry)
304 wRefreshDesktop(menu->frame->screen_ptr);
308 static void
309 arrangeIconsCommand(WMenu *menu, WMenuEntry *entry)
311 wArrangeIcons(menu->frame->screen_ptr, True);
314 static void
315 showAllCommand(WMenu *menu, WMenuEntry *entry)
317 wShowAllWindows(menu->frame->screen_ptr);
320 static void
321 hideOthersCommand(WMenu *menu, WMenuEntry *entry)
323 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
327 static void
328 saveSessionCommand(WMenu *menu, WMenuEntry *entry)
330 if (!wPreferences.save_session_on_exit)
331 wSessionSaveState(menu->frame->screen_ptr);
333 wScreenSaveState(menu->frame->screen_ptr);
337 static void
338 clearSessionCommand(WMenu *menu, WMenuEntry *entry)
340 wSessionClearState(menu->frame->screen_ptr);
344 static void
345 infoPanelCommand(WMenu *menu, WMenuEntry *entry)
347 wShowInfoPanel(menu->frame->screen_ptr);
351 static void
352 legalPanelCommand(WMenu *menu, WMenuEntry *entry)
354 wShowLegalPanel(menu->frame->screen_ptr);
358 /********************************************************************/
361 static char*
362 getLocalizedMenuFile(char *menu)
364 char *buffer;
365 char *ptr;
366 int len;
368 if (!Locale)
369 return NULL;
371 len = strlen(menu)+strlen(Locale)+8;
372 buffer = wmalloc(len);
374 /* try menu.locale_name */
375 snprintf(buffer, len, "%s.%s", menu, Locale);
376 if (access(buffer, F_OK)==0) {
377 return buffer;
379 /* check if it is in the form aa_bb.encoding and check for aa_bb */
380 ptr = strchr(Locale, '.');
381 if (ptr) {
382 *ptr = 0;
383 if (access(buffer, F_OK)==0) {
384 return buffer;
387 /* now check for aa */
388 ptr = strchr(buffer, '_');
389 if (ptr) {
390 *ptr = 0;
391 if (access(buffer, F_OK)==0) {
392 return buffer;
396 return NULL;
400 static void
401 raiseMenus(WMenu *menu)
403 int i;
405 if (menu->flags.mapped) {
406 wRaiseFrame(menu->frame->core);
408 for (i=0; i<menu->cascade_no; i++) {
409 if (menu->cascades[i])
410 raiseMenus(menu->cascades[i]);
416 Bool
417 wRootMenuPerformShortcut(XEvent *event)
419 WScreen *scr = wScreenForRootWindow(event->xkey.root);
420 Shortcut *ptr;
421 int modifiers;
422 int done = 0;
424 /* ignore CapsLock */
425 modifiers = event->xkey.state & ValidModMask;
427 for (ptr = shortcutList; ptr!=NULL; ptr = ptr->next) {
428 if (ptr->keycode==0 || ptr->menu->menu->screen_ptr!=scr)
429 continue;
431 if (ptr->keycode==event->xkey.keycode && ptr->modifier==modifiers) {
432 (*ptr->entry->callback)(ptr->menu, ptr->entry);
433 done = True;
437 return done;
441 void
442 wRootMenuBindShortcuts(Window window)
444 Shortcut *ptr;
446 ptr = shortcutList;
447 while (ptr) {
448 if (ptr->modifier!=AnyModifier) {
449 XGrabKey(dpy, ptr->keycode, ptr->modifier|LockMask,
450 window, True, GrabModeAsync, GrabModeAsync);
451 #ifdef NUMLOCK_HACK
452 wHackedGrabKey(ptr->keycode, ptr->modifier,
453 window, True, GrabModeAsync, GrabModeAsync);
454 #endif
456 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True,
457 GrabModeAsync, GrabModeAsync);
458 ptr = ptr->next;
463 static void
464 rebindKeygrabs(WScreen *scr)
466 WWindow *wwin;
468 wwin = scr->focused_window;
470 while (wwin!=NULL) {
471 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
473 if (!WFLAGP(wwin, no_bind_keys)) {
474 wWindowSetKeyGrabs(wwin);
476 wwin = wwin->prev;
481 static void
482 removeShortcutsForMenu(WMenu *menu)
484 Shortcut *ptr, *tmp;
485 Shortcut *newList = NULL;
487 ptr = shortcutList;
488 while (ptr!=NULL) {
489 tmp = ptr->next;
490 if (ptr->menu == menu) {
491 wfree(ptr);
492 } else {
493 ptr->next = newList;
494 newList = ptr;
496 ptr = tmp;
498 shortcutList = newList;
499 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
503 static Bool
504 addShortcut(char *file, char *shortcutDefinition, WMenu *menu,
505 WMenuEntry *entry)
507 Shortcut *ptr;
508 KeySym ksym;
509 char *k;
510 char buf[128], *b;
512 ptr = wmalloc(sizeof(Shortcut));
514 strcpy(buf, shortcutDefinition);
515 b = (char*)buf;
517 /* get modifiers */
518 ptr->modifier = 0;
519 while ((k = strchr(b, '+'))!=NULL) {
520 int mod;
522 *k = 0;
523 mod = wXModifierFromKey(b);
524 if (mod<0) {
525 wwarning(_("%s:invalid key modifier \"%s\""), file, b);
526 wfree(ptr);
527 return False;
529 ptr->modifier |= mod;
531 b = k+1;
534 /* get key */
535 ksym = XStringToKeysym(b);
537 if (ksym==NoSymbol) {
538 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
539 file, shortcutDefinition, entry->text);
540 wfree(ptr);
541 return False;
544 ptr->keycode = XKeysymToKeycode(dpy, ksym);
545 if (ptr->keycode==0) {
546 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
547 shortcutDefinition, entry->text);
548 wfree(ptr);
549 return False;
552 ptr->menu = menu;
553 ptr->entry = entry;
555 ptr->next = shortcutList;
556 shortcutList = ptr;
558 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
560 return True;
564 /*******************************/
566 static char*
567 cropline(char *line)
569 char *end;
571 if (strlen(line)==0)
572 return line;
574 end = &(line[strlen(line)])-1;
575 while (isspace(*line) && *line!=0) line++;
576 while (end>line && isspace(*end)) {
577 *end=0;
578 end--;
580 return line;
584 static char*
585 next_token(char *line, char **next)
587 char *tmp, c;
588 char *ret;
590 *next = NULL;
591 while (*line==' ' || *line=='\t') line++;
593 tmp = line;
595 if (*tmp=='"') {
596 tmp++; line++;
597 while (*tmp!=0 && *tmp!='"') tmp++;
598 if (*tmp!='"') {
599 wwarning(_("%s: unmatched '\"' in menu file"), line);
600 return NULL;
602 } else {
603 do {
604 if (*tmp=='\\')
605 tmp++;
607 if (*tmp!=0)
608 tmp++;
610 } while (*tmp!=0 && *tmp!=' ' && *tmp!='\t');
613 c = *tmp;
614 *tmp = 0;
615 ret = wstrdup(line);
616 *tmp = c;
618 if (c==0)
619 return ret;
620 else
621 tmp++;
623 /* skip blanks */
624 while (*tmp==' ' || *tmp=='\t') tmp++;
626 if (*tmp!=0)
627 *next = tmp;
629 return ret;
633 static void
634 separateCommand(char *line, char ***file, char **command)
636 char *token, *tmp = line;
637 WMArray *array = WMCreateArray(4);
638 int count, i;
640 *file = NULL;
641 *command = NULL;
642 do {
643 token = next_token(tmp, &tmp);
644 if (token) {
645 if (strcmp(token, "WITH")==0) {
646 if (tmp!=NULL && *tmp!=0)
647 *command = wstrdup(tmp);
648 else
649 wwarning(_("%s: missing command"), line);
650 break;
652 WMAddToArray(array, token);
654 } while (token!=NULL && tmp!=NULL);
656 count = WMGetArrayItemCount(array);
657 if (count>0) {
658 *file = wmalloc(sizeof(char*)*(count+1));
659 (*file)[count] = NULL;
660 for (i = 0; i < count; i++) {
661 (*file)[i] = WMGetFromArray(array, i);
664 WMFreeArray(array);
668 static void
669 constructMenu(WMenu *menu, WMenuEntry *entry)
671 WMenu *submenu;
672 struct stat stat_buf;
673 char **path;
674 char *cmd;
675 char *lpath = NULL;
676 int i, first=-1;
677 time_t last=0;
679 separateCommand((char*)entry->clientdata, &path, &cmd);
680 if (path == NULL || *path==NULL || **path==0) {
681 wwarning(_("invalid OPEN_MENU specification: %s"),
682 (char*)entry->clientdata);
683 return;
686 if (path[0][0]=='|') {
687 /* pipe menu */
689 if (!menu->cascades[entry->cascade] ||
690 menu->cascades[entry->cascade]->timestamp == 0) {
691 /* parse pipe */
693 submenu = readMenuPipe(menu->frame->screen_ptr, path);
695 if(submenu != NULL) {
696 if (path[0][1] == '|')
697 submenu->timestamp = 0;
698 else
699 submenu->timestamp = 1; /* there's no automatic reloading */
701 } else {
702 submenu = NULL;
705 } else {
706 i=0;
707 while(path[i] != NULL) {
708 char *tmp;
710 if (strcmp(path[i], "-noext")==0) {
711 i++;
712 continue;
715 tmp = wexpandpath(path[i]);
716 wfree(path[i]);
717 lpath = getLocalizedMenuFile(tmp);
718 if (lpath) {
719 wfree(tmp);
720 path[i] = lpath;
721 lpath = NULL;
722 } else {
723 path[i] = tmp;
726 if (stat(path[i], &stat_buf)==0) {
727 if (last < stat_buf.st_mtime)
728 last = stat_buf.st_mtime;
729 if (first<0)
730 first=i;
731 } else {
732 wsyserror(_("%s:could not stat menu"), path[i]);
733 /*goto finish;*/
736 i++;
739 if (first < 0) {
740 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
741 (char*)entry->clientdata);
742 goto finish;
744 stat(path[first], &stat_buf);
745 if (!menu->cascades[entry->cascade]
746 || menu->cascades[entry->cascade]->timestamp < last) {
748 if (S_ISDIR(stat_buf.st_mode)) {
749 /* menu directory */
750 submenu = readMenuDirectory(menu->frame->screen_ptr,
751 entry->text, path, cmd);
752 if (submenu)
753 submenu->timestamp = last;
754 } else if (S_ISREG(stat_buf.st_mode)) {
755 /* menu file */
757 if (cmd || path[1])
758 wwarning(_("too many parameters in OPEN_MENU: %s"),
759 (char*)entry->clientdata);
761 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
762 if (submenu)
763 submenu->timestamp = stat_buf.st_mtime;
764 } else {
765 submenu = NULL;
767 } else {
768 submenu = NULL;
772 if (submenu) {
773 wMenuEntryRemoveCascade(menu, entry);
774 wMenuEntrySetCascade(menu, entry, submenu);
777 finish:
778 i = 0;
779 while (path[i]!=NULL)
780 wfree(path[i++]);
781 wfree(path);
782 if (cmd)
783 wfree(cmd);
787 static void
788 cleanupWorkspaceMenu(WMenu *menu)
790 if (menu->frame->screen_ptr->workspace_menu == menu)
791 menu->frame->screen_ptr->workspace_menu = NULL;
795 static WMenuEntry*
796 addWorkspaceMenu(WScreen *scr, WMenu *menu, char *title)
798 WMenu *wsmenu;
799 WMenuEntry *entry;
801 if (scr->flags.added_workspace_menu) {
802 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
803 return NULL;
804 } else {
805 scr->flags.added_workspace_menu = 1;
807 wsmenu = wWorkspaceMenuMake(scr, True);
808 wsmenu->on_destroy = cleanupWorkspaceMenu;
810 scr->workspace_menu = wsmenu;
811 entry = wMenuAddCallback(menu, title, NULL, NULL);
812 wMenuEntrySetCascade(menu, entry, wsmenu);
814 wWorkspaceMenuUpdate(scr, wsmenu);
816 return entry;
820 static void
821 cleanupWindowsMenu(WMenu *menu)
823 if (menu->frame->screen_ptr->switch_menu == menu)
824 menu->frame->screen_ptr->switch_menu = NULL;
828 static WMenuEntry*
829 addWindowsMenu(WScreen *scr, WMenu *menu, char *title)
831 WMenu *wwmenu;
832 WWindow *wwin;
833 WMenuEntry *entry;
835 if (scr->flags.added_windows_menu) {
836 wwarning(_("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
837 return NULL;
838 } else {
839 scr->flags.added_windows_menu = 1;
841 wwmenu = wMenuCreate(scr, _("Window List"), False);
842 wwmenu->on_destroy = cleanupWindowsMenu;
843 scr->switch_menu = wwmenu;
844 wwin = scr->focused_window;
845 while (wwin) {
846 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
848 wwin = wwin->prev;
850 entry = wMenuAddCallback(menu, title, NULL, NULL);
851 wMenuEntrySetCascade(menu, entry, wwmenu);
853 return entry;
857 static WMenuEntry*
858 addMenuEntry(WMenu *menu, char *title, char *shortcut, char *command,
859 char *params, char *file_name)
861 WScreen *scr;
862 WMenuEntry *entry = NULL;
863 Bool shortcutOk = False;
865 if (!menu)
866 return NULL;
867 scr = menu->frame->screen_ptr;
868 if (strcmp(command, "OPEN_MENU")==0) {
869 if (!params) {
870 wwarning(_("%s:missing parameter for menu command \"%s\""),
871 file_name, command);
872 } else {
873 WMenu *dummy;
874 char *path;
876 path = wfindfile(DEF_CONFIG_PATHS, params);
877 if (!path) {
878 path = wstrdup(params);
880 dummy = wMenuCreate(scr, title, False);
881 dummy->on_destroy = removeShortcutsForMenu;
882 entry = wMenuAddCallback(menu, title, constructMenu, path);
883 entry->free_cdata = free;
884 wMenuEntrySetCascade(menu, entry, dummy);
886 } else if (strcmp(command, "EXEC")==0) {
887 if (!params)
888 wwarning(_("%s:missing parameter for menu command \"%s\""),
889 file_name, command);
890 else {
891 entry = wMenuAddCallback(menu, title, execCommand,
892 wstrconcat("exec ", params));
893 entry->free_cdata = free;
894 shortcutOk = True;
896 } else if (strcmp(command, "SHEXEC")==0) {
897 if (!params)
898 wwarning(_("%s:missing parameter for menu command \"%s\""),
899 file_name, command);
900 else {
901 entry = wMenuAddCallback(menu, title, execCommand,
902 wstrdup(params));
903 entry->free_cdata = free;
904 shortcutOk = True;
906 } else if (strcmp(command, "EXIT")==0) {
908 if (params && strcmp(params, "QUICK")==0)
909 entry = wMenuAddCallback(menu, title, exitCommand, (void*)M_QUICK);
910 else
911 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
913 shortcutOk = True;
914 } else if (strcmp(command, "SHUTDOWN")==0) {
916 if (params && strcmp(params, "QUICK")==0)
917 entry = wMenuAddCallback(menu, title, shutdownCommand,
918 (void*)M_QUICK);
919 else
920 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
922 shortcutOk = True;
923 } else if (strcmp(command, "REFRESH")==0) {
924 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
926 shortcutOk = True;
927 } else if (strcmp(command, "WORKSPACE_MENU")==0) {
928 entry = addWorkspaceMenu(scr, menu, title);
930 shortcutOk = True;
931 } else if (strcmp(command, "WINDOWS_MENU")==0) {
932 entry = addWindowsMenu(scr, menu, title);
934 shortcutOk = True;
935 } else if (strcmp(command, "ARRANGE_ICONS")==0) {
936 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
938 shortcutOk = True;
939 } else if (strcmp(command, "HIDE_OTHERS")==0) {
940 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
942 shortcutOk = True;
943 } else if (strcmp(command, "SHOW_ALL")==0) {
944 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
946 shortcutOk = True;
947 } else if (strcmp(command, "RESTART")==0) {
948 entry = wMenuAddCallback(menu, title, restartCommand,
949 params ? wstrdup(params) : NULL);
950 entry->free_cdata = free;
951 shortcutOk = True;
952 } else if (strcmp(command, "SAVE_SESSION")==0) {
953 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
955 shortcutOk = True;
956 } else if (strcmp(command, "CLEAR_SESSION")==0) {
957 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
958 shortcutOk = True;
959 } else if (strcmp(command, "INFO_PANEL")==0) {
960 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
961 shortcutOk = True;
962 } else if (strcmp(command, "LEGAL_PANEL")==0) {
963 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
964 shortcutOk = True;
965 } else {
966 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name,
967 command);
969 return NULL;
972 if (shortcut && entry) {
973 if (!shortcutOk) {
974 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name,
975 title);
976 } else {
977 if (addShortcut(file_name, shortcut, menu, entry)) {
979 entry->rtext = GetShortcutString(shortcut);
981 entry->rtext = wstrdup(shortcut);
987 return entry;
992 /******************* Menu Configuration From File *******************/
994 static void
995 separateline(char *line, char *title, char *command, char *parameter,
996 char *shortcut)
998 int l, i;
1000 l = strlen(line);
1002 *title = 0;
1003 *command = 0;
1004 *parameter = 0;
1005 *shortcut = 0;
1006 /* get the title */
1007 while (isspace(*line) && (*line!=0)) line++;
1008 if (*line=='"') {
1009 line++;
1010 i=0;
1011 while (line[i]!='"' && (line[i]!=0)) i++;
1012 if (line[i]!='"')
1013 return;
1014 } else {
1015 i=0;
1016 while (!isspace(line[i]) && (line[i]!=0)) i++;
1018 strncpy(title, line, i);
1019 title[i++]=0;
1020 line+=i;
1022 /* get the command or shortcut keyword */
1023 while (isspace(*line) && (*line!=0)) line++;
1024 if (*line==0)
1025 return;
1026 i=0;
1027 while (!isspace(line[i]) && (line[i]!=0)) i++;
1028 strncpy(command, line, i);
1029 command[i++]=0;
1030 line+=i;
1032 if (strcmp(command, "SHORTCUT")==0) {
1033 /* get the shortcut key */
1034 while (isspace(*line) && (*line!=0)) line++;
1035 if (*line=='"') {
1036 line++;
1037 i=0;
1038 while (line[i]!='"' && (line[i]!=0)) i++;
1039 if (line[i]!='"')
1040 return;
1041 } else {
1042 i=0;
1043 while (!isspace(line[i]) && (line[i]!=0)) i++;
1045 strncpy(shortcut, line, i);
1046 shortcut[i++]=0;
1047 line+=i;
1049 *command=0;
1051 /* get the command */
1052 while (isspace(*line) && (*line!=0)) line++;
1053 if (*line==0)
1054 return;
1055 i=0;
1056 while (!isspace(line[i]) && (line[i]!=0)) i++;
1057 strncpy(command, line, i);
1058 command[i++]=0;
1059 line+=i;
1062 /* get the parameters */
1063 while (isspace(*line) && (*line!=0)) line++;
1064 if (*line==0)
1065 return;
1067 if (*line=='"') {
1068 line++;
1069 l = 0;
1070 while (line[l]!=0 && line[l]!='"') {
1071 parameter[l] = line[l];
1072 l++;
1074 parameter[l] = 0;
1075 return;
1078 l = strlen(line);
1079 while (isspace(line[l]) && (l>0)) l--;
1080 strncpy(parameter, line, l);
1081 parameter[l]=0;
1085 static WMenu*
1086 parseCascade(WScreen *scr, WMenu *menu, FILE *file, char *file_name)
1088 char linebuf[MAXLINE];
1089 char elinebuf[MAXLINE];
1090 char title[MAXLINE];
1091 char command[MAXLINE];
1092 char shortcut[MAXLINE];
1093 char params[MAXLINE];
1094 char *line;
1096 while (!IsEof(file)) {
1097 int lsize, ok;
1099 ok = 0;
1100 fgets(linebuf, MAXLINE, file);
1101 line = cropline(linebuf);
1102 lsize = strlen(line);
1103 do {
1104 if (line[lsize-1]=='\\') {
1105 char *line2;
1106 int lsize2;
1107 fgets(elinebuf, MAXLINE, file);
1108 line2=cropline(elinebuf);
1109 lsize2=strlen(line2);
1110 if (lsize2+lsize>MAXLINE) {
1111 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1112 file_name, line);
1113 ok=2;
1114 } else {
1115 line[lsize-1]=0;
1116 lsize+=lsize2-1;
1117 strcat(line, line2);
1119 } else {
1120 ok=1;
1122 } while (!ok && !IsEof(file));
1123 if (ok==2)
1124 continue;
1126 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1127 continue;
1130 separateline(line, title, command, params, shortcut);
1132 if (!command[0]) {
1133 wwarning(_("%s:missing command in menu config: %s"), file_name,
1134 line);
1135 goto error;
1138 if (strcasecmp(command, "MENU")==0) {
1139 WMenu *cascade;
1141 /* start submenu */
1143 cascade = wMenuCreate(scr, title, False);
1144 cascade->on_destroy = removeShortcutsForMenu;
1145 if (parseCascade(scr, cascade, file, file_name)==NULL) {
1146 wMenuDestroy(cascade, True);
1147 } else {
1148 wMenuEntrySetCascade(menu,
1149 wMenuAddCallback(menu, title, NULL, NULL),
1150 cascade);
1152 } else if (strcasecmp(command, "END")==0) {
1153 /* end of menu */
1154 return menu;
1156 } else {
1157 /* normal items */
1158 addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command,
1159 params[0] ? params : NULL, file_name);
1163 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1164 file_name);
1165 return menu;
1167 error:
1168 return menu;
1172 static WMenu*
1173 readMenuFile(WScreen *scr, char *file_name)
1175 WMenu *menu=NULL;
1176 FILE *file = NULL;
1177 char linebuf[MAXLINE];
1178 char title[MAXLINE];
1179 char shortcut[MAXLINE];
1180 char command[MAXLINE];
1181 char params[MAXLINE];
1182 char *line;
1183 #ifdef USECPP
1184 char *args;
1185 int cpp = 0;
1186 #endif
1188 #ifdef USECPP
1189 if (!wPreferences.flags.nocpp) {
1190 args = MakeCPPArgs(file_name);
1191 if (!args) {
1192 wwarning(_("could not make arguments for menu file preprocessor"));
1193 } else {
1194 snprintf(command, sizeof(command), "%s %s %s",
1195 CPP_PATH, args, file_name);
1196 wfree(args);
1197 file = popen(command, "r");
1198 if (!file) {
1199 wsyserror(_("%s:could not open/preprocess menu file"),
1200 file_name);
1201 } else {
1202 cpp = 1;
1206 #endif /* USECPP */
1208 if (!file) {
1209 file = fopen(file_name, "r");
1210 if (!file) {
1211 wsyserror(_("%s:could not open menu file"), file_name);
1212 return NULL;
1216 while (!IsEof(file)) {
1217 if (!fgets(linebuf, MAXLINE, file))
1218 break;
1219 line = cropline(linebuf);
1220 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1221 continue;
1223 separateline(line, title, command, params, shortcut);
1225 if (!command[0]) {
1226 wwarning(_("%s:missing command in menu config: %s"), file_name,
1227 line);
1228 break;
1230 if (strcasecmp(command, "MENU")==0) {
1231 menu = wMenuCreate(scr, title, True);
1232 menu->on_destroy = removeShortcutsForMenu;
1233 if (!parseCascade(scr, menu, file, file_name)) {
1234 wMenuDestroy(menu, True);
1236 break;
1237 } else {
1238 wwarning(_("%s:invalid menu file. MENU command is missing"),
1239 file_name);
1240 break;
1244 #ifdef CPP
1245 if (cpp) {
1246 if (pclose(file)==-1) {
1247 wsyserror(_("error reading preprocessed menu data"));
1249 } else {
1250 fclose(file);
1252 #else
1253 fclose(file);
1254 #endif
1256 return menu;
1259 /************ Menu Configuration From Pipe *************/
1261 static WMenu*
1262 readMenuPipe(WScreen *scr, char **file_name)
1264 WMenu *menu=NULL;
1265 FILE *file = NULL;
1266 char linebuf[MAXLINE];
1267 char title[MAXLINE];
1268 char command[MAXLINE];
1269 char params[MAXLINE];
1270 char shortcut[MAXLINE];
1271 char *line;
1272 char *filename;
1273 char flat_file[MAXLINE];
1274 int i;
1275 #ifdef USECPP
1276 char *args;
1277 int cpp = 0;
1278 #endif
1280 flat_file[0] = '\0';
1282 for(i=0; file_name[i]!=NULL; i++) {
1283 strcat(flat_file, file_name[i]);
1284 strcat(flat_file, " ");
1286 filename = flat_file+1;
1288 #ifdef USECPP
1289 if (!wPreferences.flags.nocpp) {
1290 args = MakeCPPArgs(filename);
1291 if (!args) {
1292 wwarning(_("could not make arguments for menu file preprocessor"));
1293 } else {
1294 snprintf(command, sizeof(command), "%s | %s %s",
1295 filename, CPP_PATH, args);
1297 wfree(args);
1298 file = popen(command, "r");
1299 if (!file) {
1300 wsyserror(_("%s:could not open/preprocess menu file"), filename);
1301 } else {
1302 cpp = 1;
1307 #endif /* USECPP */
1309 if (!file) {
1310 file = popen(filename, "r");
1312 if (!file) {
1313 wsyserror(_("%s:could not open menu file"), filename);
1314 return NULL;
1318 while (!IsEof(file)) {
1319 if (!fgets(linebuf, MAXLINE, file))
1320 break;
1321 line = cropline(linebuf);
1322 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1323 continue;
1325 separateline(line, title, command, params, shortcut);
1327 if (!command[0]) {
1328 wwarning(_("%s:missing command in menu config: %s"), file_name,
1329 line);
1330 break;
1332 if (strcasecmp(command, "MENU")==0) {
1333 menu = wMenuCreate(scr, title, True);
1334 menu->on_destroy = removeShortcutsForMenu;
1335 if (!parseCascade(scr, menu, file, filename)) {
1336 wMenuDestroy(menu, True);
1338 break;
1339 } else {
1340 wwarning(_("%s:no title given for the root menu"), filename);
1341 break;
1345 pclose(file);
1347 return menu;
1352 typedef struct {
1353 char *name;
1354 int index;
1355 } dir_data;
1358 static int
1359 myCompare(const void *d1, const void *d2)
1361 dir_data *p1 = *(dir_data**)d1;
1362 dir_data *p2 = *(dir_data**)d2;
1364 return strcmp(p1->name, p2->name);
1368 /************ Menu Configuration From Directory *************/
1370 static Bool
1371 isFilePackage(char *file)
1373 int l;
1375 /* check if the extension indicates this file is a
1376 * file package. For now, only recognize .themed */
1378 l = strlen(file);
1380 if (l > 7 && strcmp(&(file[l-7]), ".themed")==0) {
1381 return True;
1382 } else {
1383 return False;
1388 static WMenu*
1389 readMenuDirectory(WScreen *scr, char *title, char **path, char *command)
1391 DIR *dir;
1392 struct dirent *dentry;
1393 struct stat stat_buf;
1394 WMenu *menu=NULL;
1395 char *buffer;
1396 WMArray *dirs = NULL, *files = NULL;
1397 WMArrayIterator iter;
1398 int length, i, have_space=0;
1399 dir_data *data;
1400 int stripExtension = 0;
1403 dirs = WMCreateArray(16);
1404 files = WMCreateArray(16);
1406 i=0;
1407 while (path[i]!=NULL) {
1408 if (strcmp(path[i], "-noext")==0) {
1409 stripExtension = 1;
1410 i++;
1411 continue;
1414 dir = opendir(path[i]);
1415 if (!dir) {
1416 i++;
1417 continue;
1420 while ((dentry = readdir(dir))) {
1422 if (strcmp(dentry->d_name, ".")==0 ||
1423 strcmp(dentry->d_name, "..")==0)
1424 continue;
1426 if (dentry->d_name[0] == '.')
1427 continue;
1429 buffer = malloc(strlen(path[i])+strlen(dentry->d_name)+4);
1430 if (!buffer) {
1431 wsyserror(_("out of memory while constructing directory menu %s"),
1432 path[i]);
1433 break;
1436 strcpy(buffer, path[i]);
1437 strcat(buffer, "/");
1438 strcat(buffer, dentry->d_name);
1440 if (stat(buffer, &stat_buf)!=0) {
1441 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1442 path[i], dentry->d_name);
1443 } else {
1444 Bool isFilePack = False;
1446 data = NULL;
1447 if (S_ISDIR(stat_buf.st_mode)
1448 && !(isFilePack = isFilePackage(dentry->d_name))) {
1450 /* access always returns success for user root */
1451 if (access(buffer, X_OK)==0) {
1452 /* Directory is accesible. Add to directory list */
1454 data = (dir_data*) wmalloc(sizeof(dir_data));
1455 data->name = wstrdup(dentry->d_name);
1456 data->index = i;
1458 WMAddToArray(dirs, data);
1460 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1461 /* Hack because access always returns X_OK success for user root */
1462 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1463 if ((command!=NULL && access(buffer, R_OK)==0) ||
1464 (command==NULL && access(buffer, X_OK)==0 &&
1465 (stat_buf.st_mode & S_IXANY))) {
1467 data = (dir_data*) wmalloc(sizeof(dir_data));
1468 data->name = wstrdup(dentry->d_name);
1469 data->index = i;
1471 WMAddToArray(files, data);
1475 wfree(buffer);
1478 closedir(dir);
1479 i++;
1482 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1483 WMFreeArray(dirs);
1484 WMFreeArray(files);
1485 return NULL;
1488 WMSortArray(dirs, myCompare);
1489 WMSortArray(files, myCompare);
1491 menu = wMenuCreate(scr, title, False);
1492 menu->on_destroy = removeShortcutsForMenu;
1494 WM_ITERATE_ARRAY(dirs, data, iter) {
1495 /* New directory. Use same OPEN_MENU command that was used
1496 * for the current directory. */
1497 length = strlen(path[data->index])+strlen(data->name)+6;
1498 if (stripExtension)
1499 length += 7;
1500 if (command)
1501 length += strlen(command) + 6;
1502 buffer = malloc(length);
1503 if (!buffer) {
1504 wsyserror(_("out of memory while constructing directory menu %s"),
1505 path[data->index]);
1506 break;
1509 buffer[0] = '\0';
1510 if (stripExtension)
1511 strcat(buffer, "-noext ");
1513 have_space = strchr(path[data->index], ' ')!=NULL ||
1514 strchr(data->name, ' ')!=NULL;
1516 if (have_space)
1517 strcat(buffer, "\"");
1518 strcat(buffer, path[data->index]);
1520 strcat(buffer, "/");
1521 strcat(buffer, data->name);
1522 if (have_space)
1523 strcat(buffer, "\"");
1524 if (command) {
1525 strcat(buffer, " WITH ");
1526 strcat(buffer, command);
1529 addMenuEntry(menu, data->name, NULL, "OPEN_MENU", buffer, path[data->index]);
1531 wfree(buffer);
1532 if (data->name)
1533 wfree(data->name);
1534 wfree(data);
1537 WM_ITERATE_ARRAY(files, data, iter) {
1538 /* executable: add as entry */
1539 length = strlen(path[data->index])+strlen(data->name)+6;
1540 if (command)
1541 length += strlen(command);
1543 buffer = malloc(length);
1544 if (!buffer) {
1545 wsyserror(_("out of memory while constructing directory menu %s"),
1546 path[data->index]);
1547 break;
1550 have_space = strchr(path[data->index], ' ')!=NULL ||
1551 strchr(data->name, ' ')!=NULL;
1552 if (command!=NULL) {
1553 strcpy(buffer, command);
1554 strcat(buffer, " ");
1555 if (have_space)
1556 strcat(buffer, "\"");
1557 strcat(buffer, path[data->index]);
1558 } else {
1559 if (have_space) {
1560 buffer[0] = '"';
1561 buffer[1] = 0;
1562 strcat(buffer, path[data->index]);
1563 } else {
1564 strcpy(buffer, path[data->index]);
1567 strcat(buffer, "/");
1568 strcat(buffer, data->name);
1569 if (have_space)
1570 strcat(buffer, "\"");
1572 if (stripExtension) {
1573 char *ptr = strrchr(data->name, '.');
1574 if (ptr && ptr!=data->name)
1575 *ptr = 0;
1577 addMenuEntry(menu, data->name, NULL, "SHEXEC", buffer, path[data->index]);
1579 wfree(buffer);
1580 if (data->name)
1581 wfree(data->name);
1582 wfree(data);
1585 WMFreeArray(files);
1586 WMFreeArray(dirs);
1588 return menu;
1592 /************ Menu Configuration From WMRootMenu *************/
1594 static WMenu*
1595 makeDefaultMenu(WScreen *scr)
1597 WMenu *menu=NULL;
1599 menu = wMenuCreate(scr, _("Commands"), True);
1600 wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
1601 wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
1602 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1603 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1604 return menu;
1612 *----------------------------------------------------------------------
1613 * configureMenu--
1614 * Reads root menu configuration from defaults database.
1616 *----------------------------------------------------------------------
1618 static WMenu*
1619 configureMenu(WScreen *scr, WMPropList *definition)
1621 WMenu *menu = NULL;
1622 WMPropList *elem;
1623 int i, count;
1624 WMPropList *title, *command, *params;
1625 char *tmp, *mtitle;
1628 if (WMIsPLString(definition)) {
1629 struct stat stat_buf;
1630 char *path = NULL;
1631 Bool menu_is_default = False;
1633 /* menu definition is a string. Probably a path, so parse the file */
1635 tmp = wexpandpath(WMGetFromPLString(definition));
1637 path = getLocalizedMenuFile(tmp);
1639 if (!path)
1640 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1642 if (!path) {
1643 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1644 menu_is_default = True;
1647 if (!path) {
1648 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1649 tmp);
1650 wfree(tmp);
1651 return NULL;
1654 if (stat(path, &stat_buf)<0) {
1655 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1656 wfree(path);
1657 wfree(tmp);
1658 return NULL;
1661 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1662 /* if the pointer in WMRootMenu has changed */
1663 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1665 if (menu_is_default) {
1666 wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1667 path);
1670 menu = readMenuFile(scr, path);
1671 if (menu)
1672 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1673 } else {
1674 menu = NULL;
1676 wfree(path);
1677 wfree(tmp);
1679 return menu;
1682 count = WMGetPropListItemCount(definition);
1683 if (count==0)
1684 return NULL;
1686 elem = WMGetFromPLArray(definition, 0);
1687 if (!WMIsPLString(elem)) {
1688 tmp = WMGetPropListDescription(elem, False);
1689 wwarning(_("%s:format error in root menu configuration \"%s\""),
1690 "WMRootMenu", tmp);
1691 wfree(tmp);
1692 return NULL;
1694 mtitle = WMGetFromPLString(elem);
1696 menu = wMenuCreate(scr, mtitle, False);
1697 menu->on_destroy = removeShortcutsForMenu;
1699 #ifdef GLOBAL_SUBMENU_FILE
1701 WMenu *submenu;
1702 WMenuEntry *mentry;
1704 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1706 if (submenu) {
1707 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1708 wMenuEntrySetCascade(menu, mentry, submenu);
1711 #endif
1713 for (i=1; i<count; i++) {
1714 elem = WMGetFromPLArray(definition, i);
1715 #if 0
1716 if (WMIsPLString(elem)) {
1717 char *file;
1719 file = WMGetFromPLString(elem);
1722 #endif
1723 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1724 goto error;
1726 if (WMIsPLArray(WMGetFromPLArray(elem,1))) {
1727 WMenu *submenu;
1728 WMenuEntry *mentry;
1730 /* submenu */
1731 submenu = configureMenu(scr, elem);
1732 if (submenu) {
1733 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL,
1734 NULL);
1735 wMenuEntrySetCascade(menu, mentry, submenu);
1737 } else {
1738 int idx = 0;
1739 WMPropList *shortcut;
1740 /* normal entry */
1742 title = WMGetFromPLArray(elem, idx++);
1743 shortcut = WMGetFromPLArray(elem, idx++);
1744 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT")==0) {
1745 shortcut = WMGetFromPLArray(elem, idx++);
1746 command = WMGetFromPLArray(elem, idx++);
1747 } else {
1748 command = shortcut;
1749 shortcut = NULL;
1751 params = WMGetFromPLArray(elem, idx++);
1753 if (!title || !command)
1754 goto error;
1756 addMenuEntry(menu, WMGetFromPLString(title),
1757 shortcut ? WMGetFromPLString(shortcut) : NULL,
1758 WMGetFromPLString(command),
1759 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1761 continue;
1763 error:
1764 tmp = WMGetPropListDescription(elem, False);
1765 wwarning(_("%s:format error in root menu configuration \"%s\""),
1766 "WMRootMenu", tmp);
1767 wfree(tmp);
1770 return menu;
1781 *----------------------------------------------------------------------
1782 * OpenRootMenu--
1783 * Opens the root menu, parsing the menu configuration from the
1784 * defaults database.
1785 * If the menu is already mapped and is not sticked to the
1786 * root window, it will be unmapped.
1788 * Side effects:
1789 * The menu may be remade.
1791 * Notes:
1792 * Construction of OPEN_MENU entries are delayed to the moment the
1793 * user map's them.
1794 *----------------------------------------------------------------------
1796 void
1797 OpenRootMenu(WScreen *scr, int x, int y, int keyboard)
1799 WMenu *menu=NULL;
1800 WMPropList *definition;
1802 static WMPropList *domain=NULL;
1804 if (!domain) {
1805 domain = WMCreatePLString("WMRootMenu");
1809 scr->flags.root_menu_changed_shortcuts = 0;
1810 scr->flags.added_workspace_menu = 0;
1811 scr->flags.added_windows_menu = 0;
1813 if (scr->root_menu && scr->root_menu->flags.mapped) {
1814 menu = scr->root_menu;
1815 if (!menu->flags.buttoned) {
1816 wMenuUnmap(menu);
1817 } else {
1818 wRaiseFrame(menu->frame->core);
1820 if (keyboard)
1821 wMenuMapAt(menu, 0, 0, True);
1822 else
1823 wMenuMapCopyAt(menu, x-menu->frame->core->width/2, y);
1825 return;
1829 definition = WDRootMenu->dictionary;
1832 definition = PLGetDomain(domain);
1834 if (definition) {
1835 if (WMIsPLArray(definition)) {
1836 if (!scr->root_menu
1837 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1838 menu = configureMenu(scr, definition);
1839 if (menu)
1840 menu->timestamp = WDRootMenu->timestamp;
1842 } else
1843 menu = NULL;
1844 } else {
1845 menu = configureMenu(scr, definition);
1849 if (!menu) {
1850 /* menu hasn't changed or could not be read */
1851 if (!scr->root_menu) {
1852 wMessageDialog(scr, _("Error"),
1853 _("The applications menu could not be loaded. "
1854 "Look at the console output for a detailed "
1855 "description of the errors."),
1856 _("OK"), NULL, NULL);
1858 menu = makeDefaultMenu(scr);
1859 scr->root_menu = menu;
1861 menu = scr->root_menu;
1862 } else {
1863 /* new root menu */
1864 if (scr->root_menu) {
1865 wMenuDestroy(scr->root_menu, True);
1867 scr->root_menu = menu;
1869 if (menu) {
1870 int newx, newy;
1872 if (keyboard && x==0 && y==0) {
1873 newx = newy = 0;
1874 } else if (keyboard && x==scr->scr_width/2 && y==scr->scr_height/2) {
1875 newx = x - menu->frame->core->width/2;
1876 newy = y - menu->frame->core->height/2;
1877 } else {
1878 newx = x - menu->frame->core->width/2;
1879 newy = y;
1881 wMenuMapAt(menu, newx, newy, keyboard);
1884 if (scr->flags.root_menu_changed_shortcuts)
1885 rebindKeygrabs(scr);
1888 #endif /* !LITE */