- Fixed a bug that crashed wmaker when closing a window if multiple screens
[wmaker-crm.git] / src / rootmenu.c
bloba838c361ebd202637527f6be2f38660504074cc3
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 Shortcut *ptr;
420 int modifiers;
421 int done = 0;
422 Window dummy;
423 int foo;
425 if (wScreenCount>1 &&
426 XQueryPointer(dpy, event->xkey.root, &dummy, &dummy, &foo, &foo, &foo,
427 &foo, &foo)==False) {
428 /* Pointer is not on this screen. */
429 return True;
432 /* ignore CapsLock */
433 modifiers = event->xkey.state & ValidModMask;
435 for (ptr = shortcutList; ptr!=NULL; ptr = ptr->next) {
436 if (ptr->keycode==0)
437 continue;
439 if (ptr->keycode==event->xkey.keycode && (ptr->modifier==modifiers)) {
440 (*ptr->entry->callback)(ptr->menu, ptr->entry);
441 done = True;
444 return done;
448 void
449 wRootMenuBindShortcuts(Window window)
451 Shortcut *ptr;
453 ptr = shortcutList;
454 while (ptr) {
455 if (ptr->modifier!=AnyModifier) {
456 XGrabKey(dpy, ptr->keycode, ptr->modifier|LockMask,
457 window, True, GrabModeAsync, GrabModeAsync);
458 #ifdef NUMLOCK_HACK
459 wHackedGrabKey(ptr->keycode, ptr->modifier,
460 window, True, GrabModeAsync, GrabModeAsync);
461 #endif
463 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True,
464 GrabModeAsync, GrabModeAsync);
465 ptr = ptr->next;
470 static void
471 rebindKeygrabs(WScreen *scr)
473 WWindow *wwin;
475 wwin = scr->focused_window;
477 while (wwin!=NULL) {
478 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
480 if (!WFLAGP(wwin, no_bind_keys)) {
481 wWindowSetKeyGrabs(wwin);
483 wwin = wwin->prev;
488 static void
489 removeShortcutsForMenu(WMenu *menu)
491 Shortcut *ptr, *tmp;
492 Shortcut *newList = NULL;
494 ptr = shortcutList;
495 while (ptr!=NULL) {
496 tmp = ptr->next;
497 if (ptr->menu == menu) {
498 wfree(ptr);
499 } else {
500 ptr->next = newList;
501 newList = ptr;
503 ptr = tmp;
505 shortcutList = newList;
506 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
510 static Bool
511 addShortcut(char *file, char *shortcutDefinition, WMenu *menu,
512 WMenuEntry *entry)
514 Shortcut *ptr;
515 KeySym ksym;
516 char *k;
517 char buf[128], *b;
519 ptr = wmalloc(sizeof(Shortcut));
521 strcpy(buf, shortcutDefinition);
522 b = (char*)buf;
524 /* get modifiers */
525 ptr->modifier = 0;
526 while ((k = strchr(b, '+'))!=NULL) {
527 int mod;
529 *k = 0;
530 mod = wXModifierFromKey(b);
531 if (mod<0) {
532 wwarning(_("%s:invalid key modifier \"%s\""), file, b);
533 wfree(ptr);
534 return False;
536 ptr->modifier |= mod;
538 b = k+1;
541 /* get key */
542 ksym = XStringToKeysym(b);
544 if (ksym==NoSymbol) {
545 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
546 file, shortcutDefinition, entry->text);
547 wfree(ptr);
548 return False;
551 ptr->keycode = XKeysymToKeycode(dpy, ksym);
552 if (ptr->keycode==0) {
553 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
554 shortcutDefinition, entry->text);
555 wfree(ptr);
556 return False;
559 ptr->menu = menu;
560 ptr->entry = entry;
562 ptr->next = shortcutList;
563 shortcutList = ptr;
565 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
567 return True;
571 /*******************************/
573 static char*
574 cropline(char *line)
576 char *end;
578 if (strlen(line)==0)
579 return line;
581 end = &(line[strlen(line)])-1;
582 while (isspace(*line) && *line!=0) line++;
583 while (end>line && isspace(*end)) {
584 *end=0;
585 end--;
587 return line;
591 static char*
592 next_token(char *line, char **next)
594 char *tmp, c;
595 char *ret;
597 *next = NULL;
598 while (*line==' ' || *line=='\t') line++;
600 tmp = line;
602 if (*tmp=='"') {
603 tmp++; line++;
604 while (*tmp!=0 && *tmp!='"') tmp++;
605 if (*tmp!='"') {
606 wwarning(_("%s: unmatched '\"' in menu file"), line);
607 return NULL;
609 } else {
610 do {
611 if (*tmp=='\\')
612 tmp++;
614 if (*tmp!=0)
615 tmp++;
617 } while (*tmp!=0 && *tmp!=' ' && *tmp!='\t');
620 c = *tmp;
621 *tmp = 0;
622 ret = wstrdup(line);
623 *tmp = c;
625 if (c==0)
626 return ret;
627 else
628 tmp++;
630 /* skip blanks */
631 while (*tmp==' ' || *tmp=='\t') tmp++;
633 if (*tmp!=0)
634 *next = tmp;
636 return ret;
640 static void
641 separateCommand(char *line, char ***file, char **command)
643 char *token, *tmp = line;
644 WMArray *array = WMCreateArray(4);
645 int count, i;
647 *file = NULL;
648 *command = NULL;
649 do {
650 token = next_token(tmp, &tmp);
651 if (token) {
652 if (strcmp(token, "WITH")==0) {
653 if (tmp!=NULL && *tmp!=0)
654 *command = wstrdup(tmp);
655 else
656 wwarning(_("%s: missing command"), line);
657 break;
659 WMAddToArray(array, token);
661 } while (token!=NULL && tmp!=NULL);
663 count = WMGetArrayItemCount(array);
664 if (count>0) {
665 *file = wmalloc(sizeof(char*)*(count+1));
666 (*file)[count] = NULL;
667 for (i = 0; i < count; i++) {
668 (*file)[i] = WMGetFromArray(array, i);
671 WMFreeArray(array);
675 static void
676 constructMenu(WMenu *menu, WMenuEntry *entry)
678 WMenu *submenu;
679 struct stat stat_buf;
680 char **path;
681 char *cmd;
682 char *lpath = NULL;
683 int i, first=-1;
684 time_t last=0;
686 separateCommand((char*)entry->clientdata, &path, &cmd);
687 if (path == NULL || *path==NULL || **path==0) {
688 wwarning(_("invalid OPEN_MENU specification: %s"),
689 (char*)entry->clientdata);
690 return;
693 if (path[0][0]=='|') {
694 /* pipe menu */
696 if (!menu->cascades[entry->cascade] ||
697 menu->cascades[entry->cascade]->timestamp == 0) {
698 /* parse pipe */
700 submenu = readMenuPipe(menu->frame->screen_ptr, path);
702 if(submenu != NULL) {
703 if (path[0][1] == '|')
704 submenu->timestamp = 0;
705 else
706 submenu->timestamp = 1; /* there's no automatic reloading */
708 } else {
709 submenu = NULL;
712 } else {
713 i=0;
714 while(path[i] != NULL) {
715 char *tmp;
717 if (strcmp(path[i], "-noext")==0) {
718 i++;
719 continue;
722 tmp = wexpandpath(path[i]);
723 wfree(path[i]);
724 lpath = getLocalizedMenuFile(tmp);
725 if (lpath) {
726 wfree(tmp);
727 path[i] = lpath;
728 lpath = NULL;
729 } else {
730 path[i] = tmp;
733 if (stat(path[i], &stat_buf)==0) {
734 if (last < stat_buf.st_mtime)
735 last = stat_buf.st_mtime;
736 if (first<0)
737 first=i;
738 } else {
739 wsyserror(_("%s:could not stat menu"), path[i]);
740 /*goto finish;*/
743 i++;
746 if (first < 0) {
747 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
748 (char*)entry->clientdata);
749 goto finish;
751 stat(path[first], &stat_buf);
752 if (!menu->cascades[entry->cascade]
753 || menu->cascades[entry->cascade]->timestamp < last) {
755 if (S_ISDIR(stat_buf.st_mode)) {
756 /* menu directory */
757 submenu = readMenuDirectory(menu->frame->screen_ptr,
758 entry->text, path, cmd);
759 if (submenu)
760 submenu->timestamp = last;
761 } else if (S_ISREG(stat_buf.st_mode)) {
762 /* menu file */
764 if (cmd || path[1])
765 wwarning(_("too many parameters in OPEN_MENU: %s"),
766 (char*)entry->clientdata);
768 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
769 if (submenu)
770 submenu->timestamp = stat_buf.st_mtime;
771 } else {
772 submenu = NULL;
774 } else {
775 submenu = NULL;
779 if (submenu) {
780 wMenuEntryRemoveCascade(menu, entry);
781 wMenuEntrySetCascade(menu, entry, submenu);
784 finish:
785 i = 0;
786 while (path[i]!=NULL)
787 wfree(path[i++]);
788 wfree(path);
789 if (cmd)
790 wfree(cmd);
794 static void
795 cleanupWorkspaceMenu(WMenu *menu)
797 if (menu->frame->screen_ptr->workspace_menu == menu)
798 menu->frame->screen_ptr->workspace_menu = NULL;
802 static WMenuEntry*
803 addWorkspaceMenu(WScreen *scr, WMenu *menu, char *title)
805 WMenu *wsmenu;
806 WMenuEntry *entry;
808 if (scr->flags.added_workspace_menu) {
809 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
810 return NULL;
811 } else {
812 scr->flags.added_workspace_menu = 1;
814 wsmenu = wWorkspaceMenuMake(scr, True);
815 wsmenu->on_destroy = cleanupWorkspaceMenu;
817 scr->workspace_menu = wsmenu;
818 entry = wMenuAddCallback(menu, title, NULL, NULL);
819 wMenuEntrySetCascade(menu, entry, wsmenu);
821 wWorkspaceMenuUpdate(scr, wsmenu);
823 return entry;
827 static void
828 cleanupWindowsMenu(WMenu *menu)
830 if (menu->frame->screen_ptr->switch_menu == menu)
831 menu->frame->screen_ptr->switch_menu = NULL;
835 static WMenuEntry*
836 addWindowsMenu(WScreen *scr, WMenu *menu, char *title)
838 WMenu *wwmenu;
839 WWindow *wwin;
840 WMenuEntry *entry;
842 if (scr->flags.added_windows_menu) {
843 wwarning(_("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
844 return NULL;
845 } else {
846 scr->flags.added_windows_menu = 1;
848 wwmenu = wMenuCreate(scr, _("Window List"), False);
849 wwmenu->on_destroy = cleanupWindowsMenu;
850 scr->switch_menu = wwmenu;
851 wwin = scr->focused_window;
852 while (wwin) {
853 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
855 wwin = wwin->prev;
857 entry = wMenuAddCallback(menu, title, NULL, NULL);
858 wMenuEntrySetCascade(menu, entry, wwmenu);
860 return entry;
864 static WMenuEntry*
865 addMenuEntry(WMenu *menu, char *title, char *shortcut, char *command,
866 char *params, char *file_name)
868 WScreen *scr;
869 WMenuEntry *entry = NULL;
870 Bool shortcutOk = False;
872 if (!menu)
873 return NULL;
874 scr = menu->frame->screen_ptr;
875 if (strcmp(command, "OPEN_MENU")==0) {
876 if (!params) {
877 wwarning(_("%s:missing parameter for menu command \"%s\""),
878 file_name, command);
879 } else {
880 WMenu *dummy;
881 char *path;
883 path = wfindfile(DEF_CONFIG_PATHS, params);
884 if (!path) {
885 path = wstrdup(params);
887 dummy = wMenuCreate(scr, title, False);
888 dummy->on_destroy = removeShortcutsForMenu;
889 entry = wMenuAddCallback(menu, title, constructMenu, path);
890 entry->free_cdata = free;
891 wMenuEntrySetCascade(menu, entry, dummy);
893 } else if (strcmp(command, "EXEC")==0) {
894 if (!params)
895 wwarning(_("%s:missing parameter for menu command \"%s\""),
896 file_name, command);
897 else {
898 entry = wMenuAddCallback(menu, title, execCommand,
899 wstrconcat("exec ", params));
900 entry->free_cdata = free;
901 shortcutOk = True;
903 } else if (strcmp(command, "SHEXEC")==0) {
904 if (!params)
905 wwarning(_("%s:missing parameter for menu command \"%s\""),
906 file_name, command);
907 else {
908 entry = wMenuAddCallback(menu, title, execCommand,
909 wstrdup(params));
910 entry->free_cdata = free;
911 shortcutOk = True;
913 } else if (strcmp(command, "EXIT")==0) {
915 if (params && strcmp(params, "QUICK")==0)
916 entry = wMenuAddCallback(menu, title, exitCommand, (void*)M_QUICK);
917 else
918 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
920 shortcutOk = True;
921 } else if (strcmp(command, "SHUTDOWN")==0) {
923 if (params && strcmp(params, "QUICK")==0)
924 entry = wMenuAddCallback(menu, title, shutdownCommand,
925 (void*)M_QUICK);
926 else
927 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
929 shortcutOk = True;
930 } else if (strcmp(command, "REFRESH")==0) {
931 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
933 shortcutOk = True;
934 } else if (strcmp(command, "WORKSPACE_MENU")==0) {
935 entry = addWorkspaceMenu(scr, menu, title);
937 shortcutOk = True;
938 } else if (strcmp(command, "WINDOWS_MENU")==0) {
939 entry = addWindowsMenu(scr, menu, title);
941 shortcutOk = True;
942 } else if (strcmp(command, "ARRANGE_ICONS")==0) {
943 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
945 shortcutOk = True;
946 } else if (strcmp(command, "HIDE_OTHERS")==0) {
947 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
949 shortcutOk = True;
950 } else if (strcmp(command, "SHOW_ALL")==0) {
951 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
953 shortcutOk = True;
954 } else if (strcmp(command, "RESTART")==0) {
955 entry = wMenuAddCallback(menu, title, restartCommand,
956 params ? wstrdup(params) : NULL);
957 entry->free_cdata = free;
958 shortcutOk = True;
959 } else if (strcmp(command, "SAVE_SESSION")==0) {
960 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
962 shortcutOk = True;
963 } else if (strcmp(command, "CLEAR_SESSION")==0) {
964 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
965 shortcutOk = True;
966 } else if (strcmp(command, "INFO_PANEL")==0) {
967 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
968 shortcutOk = True;
969 } else if (strcmp(command, "LEGAL_PANEL")==0) {
970 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
971 shortcutOk = True;
972 } else {
973 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name,
974 command);
976 return NULL;
979 if (shortcut && entry) {
980 if (!shortcutOk) {
981 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name,
982 title);
983 } else {
984 if (addShortcut(file_name, shortcut, menu, entry)) {
986 entry->rtext = GetShortcutString(shortcut);
988 entry->rtext = wstrdup(shortcut);
994 return entry;
999 /******************* Menu Configuration From File *******************/
1001 static void
1002 separateline(char *line, char *title, char *command, char *parameter,
1003 char *shortcut)
1005 int l, i;
1007 l = strlen(line);
1009 *title = 0;
1010 *command = 0;
1011 *parameter = 0;
1012 *shortcut = 0;
1013 /* get the title */
1014 while (isspace(*line) && (*line!=0)) line++;
1015 if (*line=='"') {
1016 line++;
1017 i=0;
1018 while (line[i]!='"' && (line[i]!=0)) i++;
1019 if (line[i]!='"')
1020 return;
1021 } else {
1022 i=0;
1023 while (!isspace(line[i]) && (line[i]!=0)) i++;
1025 strncpy(title, line, i);
1026 title[i++]=0;
1027 line+=i;
1029 /* get the command or shortcut keyword */
1030 while (isspace(*line) && (*line!=0)) line++;
1031 if (*line==0)
1032 return;
1033 i=0;
1034 while (!isspace(line[i]) && (line[i]!=0)) i++;
1035 strncpy(command, line, i);
1036 command[i++]=0;
1037 line+=i;
1039 if (strcmp(command, "SHORTCUT")==0) {
1040 /* get the shortcut key */
1041 while (isspace(*line) && (*line!=0)) line++;
1042 if (*line=='"') {
1043 line++;
1044 i=0;
1045 while (line[i]!='"' && (line[i]!=0)) i++;
1046 if (line[i]!='"')
1047 return;
1048 } else {
1049 i=0;
1050 while (!isspace(line[i]) && (line[i]!=0)) i++;
1052 strncpy(shortcut, line, i);
1053 shortcut[i++]=0;
1054 line+=i;
1056 *command=0;
1058 /* get the command */
1059 while (isspace(*line) && (*line!=0)) line++;
1060 if (*line==0)
1061 return;
1062 i=0;
1063 while (!isspace(line[i]) && (line[i]!=0)) i++;
1064 strncpy(command, line, i);
1065 command[i++]=0;
1066 line+=i;
1069 /* get the parameters */
1070 while (isspace(*line) && (*line!=0)) line++;
1071 if (*line==0)
1072 return;
1074 if (*line=='"') {
1075 line++;
1076 l = 0;
1077 while (line[l]!=0 && line[l]!='"') {
1078 parameter[l] = line[l];
1079 l++;
1081 parameter[l] = 0;
1082 return;
1085 l = strlen(line);
1086 while (isspace(line[l]) && (l>0)) l--;
1087 strncpy(parameter, line, l);
1088 parameter[l]=0;
1092 static WMenu*
1093 parseCascade(WScreen *scr, WMenu *menu, FILE *file, char *file_name)
1095 char linebuf[MAXLINE];
1096 char elinebuf[MAXLINE];
1097 char title[MAXLINE];
1098 char command[MAXLINE];
1099 char shortcut[MAXLINE];
1100 char params[MAXLINE];
1101 char *line;
1103 while (!IsEof(file)) {
1104 int lsize, ok;
1106 ok = 0;
1107 fgets(linebuf, MAXLINE, file);
1108 line = cropline(linebuf);
1109 lsize = strlen(line);
1110 do {
1111 if (line[lsize-1]=='\\') {
1112 char *line2;
1113 int lsize2;
1114 fgets(elinebuf, MAXLINE, file);
1115 line2=cropline(elinebuf);
1116 lsize2=strlen(line2);
1117 if (lsize2+lsize>MAXLINE) {
1118 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1119 file_name, line);
1120 ok=2;
1121 } else {
1122 line[lsize-1]=0;
1123 lsize+=lsize2-1;
1124 strcat(line, line2);
1126 } else {
1127 ok=1;
1129 } while (!ok && !IsEof(file));
1130 if (ok==2)
1131 continue;
1133 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1134 continue;
1137 separateline(line, title, command, params, shortcut);
1139 if (!command[0]) {
1140 wwarning(_("%s:missing command in menu config: %s"), file_name,
1141 line);
1142 goto error;
1145 if (strcasecmp(command, "MENU")==0) {
1146 WMenu *cascade;
1148 /* start submenu */
1150 cascade = wMenuCreate(scr, title, False);
1151 cascade->on_destroy = removeShortcutsForMenu;
1152 if (parseCascade(scr, cascade, file, file_name)==NULL) {
1153 wMenuDestroy(cascade, True);
1154 } else {
1155 wMenuEntrySetCascade(menu,
1156 wMenuAddCallback(menu, title, NULL, NULL),
1157 cascade);
1159 } else if (strcasecmp(command, "END")==0) {
1160 /* end of menu */
1161 return menu;
1163 } else {
1164 /* normal items */
1165 addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command,
1166 params[0] ? params : NULL, file_name);
1170 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1171 file_name);
1172 return menu;
1174 error:
1175 return menu;
1179 static WMenu*
1180 readMenuFile(WScreen *scr, char *file_name)
1182 WMenu *menu=NULL;
1183 FILE *file = NULL;
1184 char linebuf[MAXLINE];
1185 char title[MAXLINE];
1186 char shortcut[MAXLINE];
1187 char command[MAXLINE];
1188 char params[MAXLINE];
1189 char *line;
1190 #ifdef USECPP
1191 char *args;
1192 int cpp = 0;
1193 #endif
1195 #ifdef USECPP
1196 if (!wPreferences.flags.nocpp) {
1197 args = MakeCPPArgs(file_name);
1198 if (!args) {
1199 wwarning(_("could not make arguments for menu file preprocessor"));
1200 } else {
1201 snprintf(command, sizeof(command), "%s %s %s",
1202 CPP_PATH, args, file_name);
1203 wfree(args);
1204 file = popen(command, "r");
1205 if (!file) {
1206 wsyserror(_("%s:could not open/preprocess menu file"),
1207 file_name);
1208 } else {
1209 cpp = 1;
1213 #endif /* USECPP */
1215 if (!file) {
1216 file = fopen(file_name, "r");
1217 if (!file) {
1218 wsyserror(_("%s:could not open menu file"), file_name);
1219 return NULL;
1223 while (!IsEof(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"), file_name,
1234 line);
1235 break;
1237 if (strcasecmp(command, "MENU")==0) {
1238 menu = wMenuCreate(scr, title, True);
1239 menu->on_destroy = removeShortcutsForMenu;
1240 if (!parseCascade(scr, menu, file, file_name)) {
1241 wMenuDestroy(menu, True);
1243 break;
1244 } else {
1245 wwarning(_("%s:invalid menu file. MENU command is missing"),
1246 file_name);
1247 break;
1251 #ifdef CPP
1252 if (cpp) {
1253 if (pclose(file)==-1) {
1254 wsyserror(_("error reading preprocessed menu data"));
1256 } else {
1257 fclose(file);
1259 #else
1260 fclose(file);
1261 #endif
1263 return menu;
1266 /************ Menu Configuration From Pipe *************/
1268 static WMenu*
1269 readMenuPipe(WScreen *scr, char **file_name)
1271 WMenu *menu=NULL;
1272 FILE *file = NULL;
1273 char linebuf[MAXLINE];
1274 char title[MAXLINE];
1275 char command[MAXLINE];
1276 char params[MAXLINE];
1277 char shortcut[MAXLINE];
1278 char *line;
1279 char *filename;
1280 char flat_file[MAXLINE];
1281 int i;
1282 #ifdef USECPP
1283 char *args;
1284 int cpp = 0;
1285 #endif
1287 flat_file[0] = '\0';
1289 for(i=0; file_name[i]!=NULL; i++) {
1290 strcat(flat_file, file_name[i]);
1291 strcat(flat_file, " ");
1293 filename = flat_file+1;
1295 #ifdef USECPP
1296 if (!wPreferences.flags.nocpp) {
1297 args = MakeCPPArgs(filename);
1298 if (!args) {
1299 wwarning(_("could not make arguments for menu file preprocessor"));
1300 } else {
1301 snprintf(command, sizeof(command), "%s | %s %s",
1302 filename, CPP_PATH, args);
1304 wfree(args);
1305 file = popen(command, "r");
1306 if (!file) {
1307 wsyserror(_("%s:could not open/preprocess menu file"), filename);
1308 } else {
1309 cpp = 1;
1314 #endif /* USECPP */
1316 if (!file) {
1317 file = popen(filename, "r");
1319 if (!file) {
1320 wsyserror(_("%s:could not open menu file"), filename);
1321 return NULL;
1325 while (!IsEof(file)) {
1326 if (!fgets(linebuf, MAXLINE, file))
1327 break;
1328 line = cropline(linebuf);
1329 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1330 continue;
1332 separateline(line, title, command, params, shortcut);
1334 if (!command[0]) {
1335 wwarning(_("%s:missing command in menu config: %s"), file_name,
1336 line);
1337 break;
1339 if (strcasecmp(command, "MENU")==0) {
1340 menu = wMenuCreate(scr, title, True);
1341 menu->on_destroy = removeShortcutsForMenu;
1342 if (!parseCascade(scr, menu, file, filename)) {
1343 wMenuDestroy(menu, True);
1345 break;
1346 } else {
1347 wwarning(_("%s:no title given for the root menu"), filename);
1348 break;
1352 pclose(file);
1354 return menu;
1359 typedef struct {
1360 char *name;
1361 int index;
1362 } dir_data;
1365 static int
1366 myCompare(const void *d1, const void *d2)
1368 dir_data *p1 = *(dir_data**)d1;
1369 dir_data *p2 = *(dir_data**)d2;
1371 return strcmp(p1->name, p2->name);
1375 /************ Menu Configuration From Directory *************/
1377 static Bool
1378 isFilePackage(char *file)
1380 int l;
1382 /* check if the extension indicates this file is a
1383 * file package. For now, only recognize .themed */
1385 l = strlen(file);
1387 if (l > 7 && strcmp(&(file[l-7]), ".themed")==0) {
1388 return True;
1389 } else {
1390 return False;
1395 static WMenu*
1396 readMenuDirectory(WScreen *scr, char *title, char **path, char *command)
1398 DIR *dir;
1399 struct dirent *dentry;
1400 struct stat stat_buf;
1401 WMenu *menu=NULL;
1402 char *buffer;
1403 WMArray *dirs = NULL, *files = NULL;
1404 WMArrayIterator iter;
1405 int length, i, have_space=0;
1406 dir_data *data;
1407 int stripExtension = 0;
1410 dirs = WMCreateArray(16);
1411 files = WMCreateArray(16);
1413 i=0;
1414 while (path[i]!=NULL) {
1415 if (strcmp(path[i], "-noext")==0) {
1416 stripExtension = 1;
1417 i++;
1418 continue;
1421 dir = opendir(path[i]);
1422 if (!dir) {
1423 i++;
1424 continue;
1427 while ((dentry = readdir(dir))) {
1429 if (strcmp(dentry->d_name, ".")==0 ||
1430 strcmp(dentry->d_name, "..")==0)
1431 continue;
1433 if (dentry->d_name[0] == '.')
1434 continue;
1436 buffer = malloc(strlen(path[i])+strlen(dentry->d_name)+4);
1437 if (!buffer) {
1438 wsyserror(_("out of memory while constructing directory menu %s"),
1439 path[i]);
1440 break;
1443 strcpy(buffer, path[i]);
1444 strcat(buffer, "/");
1445 strcat(buffer, dentry->d_name);
1447 if (stat(buffer, &stat_buf)!=0) {
1448 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1449 path[i], dentry->d_name);
1450 } else {
1451 Bool isFilePack = False;
1453 data = NULL;
1454 if (S_ISDIR(stat_buf.st_mode)
1455 && !(isFilePack = isFilePackage(dentry->d_name))) {
1457 /* access always returns success for user root */
1458 if (access(buffer, X_OK)==0) {
1459 /* Directory is accesible. Add to directory list */
1461 data = (dir_data*) wmalloc(sizeof(dir_data));
1462 data->name = wstrdup(dentry->d_name);
1463 data->index = i;
1465 WMAddToArray(dirs, data);
1467 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1468 /* Hack because access always returns X_OK success for user root */
1469 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1470 if ((command!=NULL && access(buffer, R_OK)==0) ||
1471 (command==NULL && access(buffer, X_OK)==0 &&
1472 (stat_buf.st_mode & S_IXANY))) {
1474 data = (dir_data*) wmalloc(sizeof(dir_data));
1475 data->name = wstrdup(dentry->d_name);
1476 data->index = i;
1478 WMAddToArray(files, data);
1482 wfree(buffer);
1485 closedir(dir);
1486 i++;
1489 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1490 WMFreeArray(dirs);
1491 WMFreeArray(files);
1492 return NULL;
1495 WMSortArray(dirs, myCompare);
1496 WMSortArray(files, myCompare);
1498 menu = wMenuCreate(scr, title, False);
1499 menu->on_destroy = removeShortcutsForMenu;
1501 WM_ITERATE_ARRAY(dirs, data, iter) {
1502 /* New directory. Use same OPEN_MENU command that was used
1503 * for the current directory. */
1504 length = strlen(path[data->index])+strlen(data->name)+6;
1505 if (stripExtension)
1506 length += 7;
1507 if (command)
1508 length += strlen(command) + 6;
1509 buffer = malloc(length);
1510 if (!buffer) {
1511 wsyserror(_("out of memory while constructing directory menu %s"),
1512 path[data->index]);
1513 break;
1516 buffer[0] = '\0';
1517 if (stripExtension)
1518 strcat(buffer, "-noext ");
1520 have_space = strchr(path[data->index], ' ')!=NULL ||
1521 strchr(data->name, ' ')!=NULL;
1523 if (have_space)
1524 strcat(buffer, "\"");
1525 strcat(buffer, path[data->index]);
1527 strcat(buffer, "/");
1528 strcat(buffer, data->name);
1529 if (have_space)
1530 strcat(buffer, "\"");
1531 if (command) {
1532 strcat(buffer, " WITH ");
1533 strcat(buffer, command);
1536 addMenuEntry(menu, data->name, NULL, "OPEN_MENU", buffer, path[data->index]);
1538 wfree(buffer);
1539 if (data->name)
1540 wfree(data->name);
1541 wfree(data);
1544 WM_ITERATE_ARRAY(files, data, iter) {
1545 /* executable: add as entry */
1546 length = strlen(path[data->index])+strlen(data->name)+6;
1547 if (command)
1548 length += strlen(command);
1550 buffer = malloc(length);
1551 if (!buffer) {
1552 wsyserror(_("out of memory while constructing directory menu %s"),
1553 path[data->index]);
1554 break;
1557 have_space = strchr(path[data->index], ' ')!=NULL ||
1558 strchr(data->name, ' ')!=NULL;
1559 if (command!=NULL) {
1560 strcpy(buffer, command);
1561 strcat(buffer, " ");
1562 if (have_space)
1563 strcat(buffer, "\"");
1564 strcat(buffer, path[data->index]);
1565 } else {
1566 if (have_space) {
1567 buffer[0] = '"';
1568 buffer[1] = 0;
1569 strcat(buffer, path[data->index]);
1570 } else {
1571 strcpy(buffer, path[data->index]);
1574 strcat(buffer, "/");
1575 strcat(buffer, data->name);
1576 if (have_space)
1577 strcat(buffer, "\"");
1579 if (stripExtension) {
1580 char *ptr = strrchr(data->name, '.');
1581 if (ptr && ptr!=data->name)
1582 *ptr = 0;
1584 addMenuEntry(menu, data->name, NULL, "SHEXEC", buffer, path[data->index]);
1586 wfree(buffer);
1587 if (data->name)
1588 wfree(data->name);
1589 wfree(data);
1592 WMFreeArray(files);
1593 WMFreeArray(dirs);
1595 return menu;
1599 /************ Menu Configuration From WMRootMenu *************/
1601 static WMenu*
1602 makeDefaultMenu(WScreen *scr)
1604 WMenu *menu=NULL;
1606 menu = wMenuCreate(scr, _("Commands"), True);
1607 wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
1608 wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
1609 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1610 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1611 return menu;
1619 *----------------------------------------------------------------------
1620 * configureMenu--
1621 * Reads root menu configuration from defaults database.
1623 *----------------------------------------------------------------------
1625 static WMenu*
1626 configureMenu(WScreen *scr, WMPropList *definition)
1628 WMenu *menu = NULL;
1629 WMPropList *elem;
1630 int i, count;
1631 WMPropList *title, *command, *params;
1632 char *tmp, *mtitle;
1635 if (WMIsPLString(definition)) {
1636 struct stat stat_buf;
1637 char *path = NULL;
1638 Bool menu_is_default = False;
1640 /* menu definition is a string. Probably a path, so parse the file */
1642 tmp = wexpandpath(WMGetFromPLString(definition));
1644 path = getLocalizedMenuFile(tmp);
1646 if (!path)
1647 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1649 if (!path) {
1650 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1651 menu_is_default = True;
1654 if (!path) {
1655 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1656 tmp);
1657 wfree(tmp);
1658 return NULL;
1661 if (stat(path, &stat_buf)<0) {
1662 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1663 wfree(path);
1664 wfree(tmp);
1665 return NULL;
1668 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1669 /* if the pointer in WMRootMenu has changed */
1670 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1672 if (menu_is_default) {
1673 wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1674 path);
1677 menu = readMenuFile(scr, path);
1678 if (menu)
1679 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1680 } else {
1681 menu = NULL;
1683 wfree(path);
1684 wfree(tmp);
1686 return menu;
1689 count = WMGetPropListItemCount(definition);
1690 if (count==0)
1691 return NULL;
1693 elem = WMGetFromPLArray(definition, 0);
1694 if (!WMIsPLString(elem)) {
1695 tmp = WMGetPropListDescription(elem, False);
1696 wwarning(_("%s:format error in root menu configuration \"%s\""),
1697 "WMRootMenu", tmp);
1698 wfree(tmp);
1699 return NULL;
1701 mtitle = WMGetFromPLString(elem);
1703 menu = wMenuCreate(scr, mtitle, False);
1704 menu->on_destroy = removeShortcutsForMenu;
1706 #ifdef GLOBAL_SUBMENU_FILE
1708 WMenu *submenu;
1709 WMenuEntry *mentry;
1711 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1713 if (submenu) {
1714 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1715 wMenuEntrySetCascade(menu, mentry, submenu);
1718 #endif
1720 for (i=1; i<count; i++) {
1721 elem = WMGetFromPLArray(definition, i);
1722 #if 0
1723 if (WMIsPLString(elem)) {
1724 char *file;
1726 file = WMGetFromPLString(elem);
1729 #endif
1730 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1731 goto error;
1733 if (WMIsPLArray(WMGetFromPLArray(elem,1))) {
1734 WMenu *submenu;
1735 WMenuEntry *mentry;
1737 /* submenu */
1738 submenu = configureMenu(scr, elem);
1739 if (submenu) {
1740 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL,
1741 NULL);
1742 wMenuEntrySetCascade(menu, mentry, submenu);
1744 } else {
1745 int idx = 0;
1746 WMPropList *shortcut;
1747 /* normal entry */
1749 title = WMGetFromPLArray(elem, idx++);
1750 shortcut = WMGetFromPLArray(elem, idx++);
1751 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT")==0) {
1752 shortcut = WMGetFromPLArray(elem, idx++);
1753 command = WMGetFromPLArray(elem, idx++);
1754 } else {
1755 command = shortcut;
1756 shortcut = NULL;
1758 params = WMGetFromPLArray(elem, idx++);
1760 if (!title || !command)
1761 goto error;
1763 addMenuEntry(menu, WMGetFromPLString(title),
1764 shortcut ? WMGetFromPLString(shortcut) : NULL,
1765 WMGetFromPLString(command),
1766 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1768 continue;
1770 error:
1771 tmp = WMGetPropListDescription(elem, False);
1772 wwarning(_("%s:format error in root menu configuration \"%s\""),
1773 "WMRootMenu", tmp);
1774 wfree(tmp);
1777 return menu;
1788 *----------------------------------------------------------------------
1789 * OpenRootMenu--
1790 * Opens the root menu, parsing the menu configuration from the
1791 * defaults database.
1792 * If the menu is already mapped and is not sticked to the
1793 * root window, it will be unmapped.
1795 * Side effects:
1796 * The menu may be remade.
1798 * Notes:
1799 * Construction of OPEN_MENU entries are delayed to the moment the
1800 * user map's them.
1801 *----------------------------------------------------------------------
1803 void
1804 OpenRootMenu(WScreen *scr, int x, int y, int keyboard)
1806 WMenu *menu=NULL;
1807 WMPropList *definition;
1809 static WMPropList *domain=NULL;
1811 if (!domain) {
1812 domain = WMCreatePLString("WMRootMenu");
1816 scr->flags.root_menu_changed_shortcuts = 0;
1817 scr->flags.added_workspace_menu = 0;
1818 scr->flags.added_windows_menu = 0;
1820 if (scr->root_menu && scr->root_menu->flags.mapped) {
1821 menu = scr->root_menu;
1822 if (!menu->flags.buttoned) {
1823 wMenuUnmap(menu);
1824 } else {
1825 wRaiseFrame(menu->frame->core);
1827 if (keyboard)
1828 wMenuMapAt(menu, 0, 0, True);
1829 else
1830 wMenuMapCopyAt(menu, x-menu->frame->core->width/2, y);
1832 return;
1836 definition = WDRootMenu->dictionary;
1839 definition = PLGetDomain(domain);
1841 if (definition) {
1842 if (WMIsPLArray(definition)) {
1843 if (!scr->root_menu
1844 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1845 menu = configureMenu(scr, definition);
1846 if (menu)
1847 menu->timestamp = WDRootMenu->timestamp;
1849 } else
1850 menu = NULL;
1851 } else {
1852 menu = configureMenu(scr, definition);
1856 if (!menu) {
1857 /* menu hasn't changed or could not be read */
1858 if (!scr->root_menu) {
1859 wMessageDialog(scr, _("Error"),
1860 _("The applications menu could not be loaded. "
1861 "Look at the console output for a detailed "
1862 "description of the errors."),
1863 _("OK"), NULL, NULL);
1865 menu = makeDefaultMenu(scr);
1866 scr->root_menu = menu;
1868 menu = scr->root_menu;
1869 } else {
1870 /* new root menu */
1871 if (scr->root_menu) {
1872 wMenuDestroy(scr->root_menu, True);
1874 scr->root_menu = menu;
1876 if (menu) {
1877 int newx, newy;
1879 if (keyboard && x==0 && y==0) {
1880 newx = newy = 0;
1881 } else if (keyboard && x==scr->scr_width/2 && y==scr->scr_height/2) {
1882 newx = x - menu->frame->core->width/2;
1883 newy = y - menu->frame->core->height/2;
1884 } else {
1885 newx = x - menu->frame->core->width/2;
1886 newy = y;
1888 wMenuMapAt(menu, newx, newy, keyboard);
1891 if (scr->flags.root_menu_changed_shortcuts)
1892 rebindKeygrabs(scr);
1895 #endif /* !LITE */