Use inotify to check for changes to the defaults database. Workaround for
[wmaker-crm.git] / src / rootmenu.c
blob58eedb2950b81ab7eea40365bc31fc658d156bd8
1 /* rootmenu.c- user defined menu
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21 * USA.
24 #include "wconfig.h"
26 #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);
341 wScreenSaveState(menu->frame->screen_ptr);
345 static void
346 infoPanelCommand(WMenu *menu, WMenuEntry *entry)
348 wShowInfoPanel(menu->frame->screen_ptr);
352 static void
353 legalPanelCommand(WMenu *menu, WMenuEntry *entry)
355 wShowLegalPanel(menu->frame->screen_ptr);
359 /********************************************************************/
362 static char*
363 getLocalizedMenuFile(char *menu)
365 char *buffer, *ptr, *locale;
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;
380 /* position of locale in our buffer */
381 locale = buffer + strlen(menu) + 1;
383 /* check if it is in the form aa_bb.encoding and check for aa_bb */
384 ptr = strchr(locale, '.');
385 if (ptr) {
386 *ptr = 0;
387 if (access(buffer, F_OK)==0) {
388 return buffer;
391 /* now check for aa */
392 ptr = strchr(locale, '_');
393 if (ptr) {
394 *ptr = 0;
395 if (access(buffer, F_OK)==0) {
396 return buffer;
400 wfree(buffer);
402 return NULL;
406 static void
407 raiseMenus(WMenu *menu)
409 int i;
411 if (menu->flags.mapped) {
412 wRaiseFrame(menu->frame->core);
414 for (i=0; i<menu->cascade_no; i++) {
415 if (menu->cascades[i])
416 raiseMenus(menu->cascades[i]);
422 Bool
423 wRootMenuPerformShortcut(XEvent *event)
425 WScreen *scr = wScreenForRootWindow(event->xkey.root);
426 Shortcut *ptr;
427 int modifiers;
428 int done = 0;
430 /* ignore CapsLock */
431 modifiers = event->xkey.state & ValidModMask;
433 for (ptr = shortcutList; ptr!=NULL; ptr = ptr->next) {
434 if (ptr->keycode==0 || ptr->menu->menu->screen_ptr!=scr)
435 continue;
437 if (ptr->keycode==event->xkey.keycode && ptr->modifier==modifiers) {
438 (*ptr->entry->callback)(ptr->menu, ptr->entry);
439 done = True;
443 return done;
447 void
448 wRootMenuBindShortcuts(Window window)
450 Shortcut *ptr;
452 ptr = shortcutList;
453 while (ptr) {
454 if (ptr->modifier!=AnyModifier) {
455 XGrabKey(dpy, ptr->keycode, ptr->modifier|LockMask,
456 window, True, GrabModeAsync, GrabModeAsync);
457 #ifdef NUMLOCK_HACK
458 wHackedGrabKey(ptr->keycode, ptr->modifier,
459 window, True, GrabModeAsync, GrabModeAsync);
460 #endif
462 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True,
463 GrabModeAsync, GrabModeAsync);
464 ptr = ptr->next;
469 static void
470 rebindKeygrabs(WScreen *scr)
472 WWindow *wwin;
474 wwin = scr->focused_window;
476 while (wwin!=NULL) {
477 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
479 if (!WFLAGP(wwin, no_bind_keys)) {
480 wWindowSetKeyGrabs(wwin);
482 wwin = wwin->prev;
487 static void
488 removeShortcutsForMenu(WMenu *menu)
490 Shortcut *ptr, *tmp;
491 Shortcut *newList = NULL;
493 ptr = shortcutList;
494 while (ptr!=NULL) {
495 tmp = ptr->next;
496 if (ptr->menu == menu) {
497 wfree(ptr);
498 } else {
499 ptr->next = newList;
500 newList = ptr;
502 ptr = tmp;
504 shortcutList = newList;
505 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
509 static Bool
510 addShortcut(char *file, char *shortcutDefinition, WMenu *menu,
511 WMenuEntry *entry)
513 Shortcut *ptr;
514 KeySym ksym;
515 char *k;
516 char buf[128], *b;
518 ptr = wmalloc(sizeof(Shortcut));
520 strcpy(buf, shortcutDefinition);
521 b = (char*)buf;
523 /* get modifiers */
524 ptr->modifier = 0;
525 while ((k = strchr(b, '+'))!=NULL) {
526 int mod;
528 *k = 0;
529 mod = wXModifierFromKey(b);
530 if (mod<0) {
531 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
532 wfree(ptr);
533 return False;
535 ptr->modifier |= mod;
537 b = k+1;
540 /* get key */
541 ksym = XStringToKeysym(b);
543 if (ksym==NoSymbol) {
544 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
545 file, shortcutDefinition, entry->text);
546 wfree(ptr);
547 return False;
550 ptr->keycode = XKeysymToKeycode(dpy, ksym);
551 if (ptr->keycode==0) {
552 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
553 shortcutDefinition, entry->text);
554 wfree(ptr);
555 return False;
558 ptr->menu = menu;
559 ptr->entry = entry;
561 ptr->next = shortcutList;
562 shortcutList = ptr;
564 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
566 return True;
570 /*******************************/
572 static char*
573 cropline(char *line)
575 char *end;
577 if (strlen(line)==0)
578 return line;
580 end = &(line[strlen(line)])-1;
581 while (isspace(*line) && *line!=0) line++;
582 while (end>line && isspace(*end)) {
583 *end=0;
584 end--;
586 return line;
590 static char*
591 next_token(char *line, char **next)
593 char *tmp, c;
594 char *ret;
596 *next = NULL;
597 while (*line==' ' || *line=='\t') line++;
599 tmp = line;
601 if (*tmp=='"') {
602 tmp++; line++;
603 while (*tmp!=0 && *tmp!='"') tmp++;
604 if (*tmp!='"') {
605 wwarning(_("%s: unmatched '\"' in menu file"), line);
606 return NULL;
608 } else {
609 do {
610 if (*tmp=='\\')
611 tmp++;
613 if (*tmp!=0)
614 tmp++;
616 } while (*tmp!=0 && *tmp!=' ' && *tmp!='\t');
619 c = *tmp;
620 *tmp = 0;
621 ret = wstrdup(line);
622 *tmp = c;
624 if (c==0)
625 return ret;
626 else
627 tmp++;
629 /* skip blanks */
630 while (*tmp==' ' || *tmp=='\t') tmp++;
632 if (*tmp!=0)
633 *next = tmp;
635 return ret;
639 static void
640 separateCommand(char *line, char ***file, char **command)
642 char *token, *tmp = line;
643 WMArray *array = WMCreateArray(4);
644 int count, i;
646 *file = NULL;
647 *command = NULL;
648 do {
649 token = next_token(tmp, &tmp);
650 if (token) {
651 if (strcmp(token, "WITH")==0) {
652 if (tmp!=NULL && *tmp!=0)
653 *command = wstrdup(tmp);
654 else
655 wwarning(_("%s: missing command"), line);
656 break;
658 WMAddToArray(array, token);
660 } while (token!=NULL && tmp!=NULL);
662 count = WMGetArrayItemCount(array);
663 if (count>0) {
664 *file = wmalloc(sizeof(char*)*(count+1));
665 (*file)[count] = NULL;
666 for (i = 0; i < count; i++) {
667 (*file)[i] = WMGetFromArray(array, i);
670 WMFreeArray(array);
674 static void
675 constructMenu(WMenu *menu, WMenuEntry *entry)
677 WMenu *submenu;
678 struct stat stat_buf;
679 char **path;
680 char *cmd;
681 char *lpath = NULL;
682 int i, first=-1;
683 time_t last=0;
685 separateCommand((char*)entry->clientdata, &path, &cmd);
686 if (path == NULL || *path==NULL || **path==0) {
687 wwarning(_("invalid OPEN_MENU specification: %s"),
688 (char*)entry->clientdata);
689 return;
692 if (path[0][0]=='|') {
693 /* pipe menu */
695 if (!menu->cascades[entry->cascade] ||
696 menu->cascades[entry->cascade]->timestamp == 0) {
697 /* parse pipe */
699 submenu = readMenuPipe(menu->frame->screen_ptr, path);
701 if(submenu != NULL) {
702 if (path[0][1] == '|')
703 submenu->timestamp = 0;
704 else
705 submenu->timestamp = 1; /* there's no automatic reloading */
707 } else {
708 submenu = NULL;
711 } else {
712 i=0;
713 while(path[i] != NULL) {
714 char *tmp;
716 if (strcmp(path[i], "-noext")==0) {
717 i++;
718 continue;
721 tmp = wexpandpath(path[i]);
722 wfree(path[i]);
723 lpath = getLocalizedMenuFile(tmp);
724 if (lpath) {
725 wfree(tmp);
726 path[i] = lpath;
727 lpath = NULL;
728 } else {
729 path[i] = tmp;
732 if (stat(path[i], &stat_buf)==0) {
733 if (last < stat_buf.st_mtime)
734 last = stat_buf.st_mtime;
735 if (first<0)
736 first=i;
737 } else {
738 wsyserror(_("%s:could not stat menu"), path[i]);
739 /*goto finish;*/
742 i++;
745 if (first < 0) {
746 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
747 (char*)entry->clientdata);
748 goto finish;
750 stat(path[first], &stat_buf);
751 if (!menu->cascades[entry->cascade]
752 || menu->cascades[entry->cascade]->timestamp < last) {
754 if (S_ISDIR(stat_buf.st_mode)) {
755 /* menu directory */
756 submenu = readMenuDirectory(menu->frame->screen_ptr,
757 entry->text, path, cmd);
758 if (submenu)
759 submenu->timestamp = last;
760 } else if (S_ISREG(stat_buf.st_mode)) {
761 /* menu file */
763 if (cmd || path[1])
764 wwarning(_("too many parameters in OPEN_MENU: %s"),
765 (char*)entry->clientdata);
767 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
768 if (submenu)
769 submenu->timestamp = stat_buf.st_mtime;
770 } else {
771 submenu = NULL;
773 } else {
774 submenu = NULL;
778 if (submenu) {
779 wMenuEntryRemoveCascade(menu, entry);
780 wMenuEntrySetCascade(menu, entry, submenu);
783 finish:
784 i = 0;
785 while (path[i]!=NULL)
786 wfree(path[i++]);
787 wfree(path);
788 if (cmd)
789 wfree(cmd);
793 static void
794 cleanupWorkspaceMenu(WMenu *menu)
796 if (menu->frame->screen_ptr->workspace_menu == menu)
797 menu->frame->screen_ptr->workspace_menu = NULL;
801 static WMenuEntry*
802 addWorkspaceMenu(WScreen *scr, WMenu *menu, char *title)
804 WMenu *wsmenu;
805 WMenuEntry *entry;
807 if (scr->flags.added_workspace_menu) {
808 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
809 return NULL;
810 } else {
811 scr->flags.added_workspace_menu = 1;
813 wsmenu = wWorkspaceMenuMake(scr, True);
814 wsmenu->on_destroy = cleanupWorkspaceMenu;
816 scr->workspace_menu = wsmenu;
817 entry = wMenuAddCallback(menu, title, NULL, NULL);
818 wMenuEntrySetCascade(menu, entry, wsmenu);
820 wWorkspaceMenuUpdate(scr, wsmenu);
822 return entry;
826 static void
827 cleanupWindowsMenu(WMenu *menu)
829 if (menu->frame->screen_ptr->switch_menu == menu)
830 menu->frame->screen_ptr->switch_menu = NULL;
834 static WMenuEntry*
835 addWindowsMenu(WScreen *scr, WMenu *menu, char *title)
837 WMenu *wwmenu;
838 WWindow *wwin;
839 WMenuEntry *entry;
841 if (scr->flags.added_windows_menu) {
842 wwarning(_("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
843 return NULL;
844 } else {
845 scr->flags.added_windows_menu = 1;
847 wwmenu = wMenuCreate(scr, _("Window List"), False);
848 wwmenu->on_destroy = cleanupWindowsMenu;
849 scr->switch_menu = wwmenu;
850 wwin = scr->focused_window;
851 while (wwin) {
852 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
854 wwin = wwin->prev;
856 entry = wMenuAddCallback(menu, title, NULL, NULL);
857 wMenuEntrySetCascade(menu, entry, wwmenu);
859 return entry;
863 static WMenuEntry*
864 addMenuEntry(WMenu *menu, char *title, char *shortcut, char *command,
865 char *params, char *file_name)
867 WScreen *scr;
868 WMenuEntry *entry = NULL;
869 Bool shortcutOk = False;
871 if (!menu)
872 return NULL;
873 scr = menu->frame->screen_ptr;
874 if (strcmp(command, "OPEN_MENU")==0) {
875 if (!params) {
876 wwarning(_("%s:missing parameter for menu command \"%s\""),
877 file_name, command);
878 } else {
879 WMenu *dummy;
880 char *path;
882 path = wfindfile(DEF_CONFIG_PATHS, params);
883 if (!path) {
884 path = wstrdup(params);
886 dummy = wMenuCreate(scr, title, False);
887 dummy->on_destroy = removeShortcutsForMenu;
888 entry = wMenuAddCallback(menu, title, constructMenu, path);
889 entry->free_cdata = free;
890 wMenuEntrySetCascade(menu, entry, dummy);
892 } else if (strcmp(command, "EXEC")==0) {
893 if (!params)
894 wwarning(_("%s:missing parameter for menu command \"%s\""),
895 file_name, command);
896 else {
897 entry = wMenuAddCallback(menu, title, execCommand,
898 wstrconcat("exec ", params));
899 entry->free_cdata = free;
900 shortcutOk = True;
902 } else if (strcmp(command, "SHEXEC")==0) {
903 if (!params)
904 wwarning(_("%s:missing parameter for menu command \"%s\""),
905 file_name, command);
906 else {
907 entry = wMenuAddCallback(menu, title, execCommand,
908 wstrdup(params));
909 entry->free_cdata = free;
910 shortcutOk = True;
912 } else if (strcmp(command, "EXIT")==0) {
914 if (params && strcmp(params, "QUICK")==0)
915 entry = wMenuAddCallback(menu, title, exitCommand, (void*)M_QUICK);
916 else
917 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
919 shortcutOk = True;
920 } else if (strcmp(command, "SHUTDOWN")==0) {
922 if (params && strcmp(params, "QUICK")==0)
923 entry = wMenuAddCallback(menu, title, shutdownCommand,
924 (void*)M_QUICK);
925 else
926 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
928 shortcutOk = True;
929 } else if (strcmp(command, "REFRESH")==0) {
930 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
932 shortcutOk = True;
933 } else if (strcmp(command, "WORKSPACE_MENU")==0) {
934 entry = addWorkspaceMenu(scr, menu, title);
936 shortcutOk = True;
937 } else if (strcmp(command, "WINDOWS_MENU")==0) {
938 entry = addWindowsMenu(scr, menu, title);
940 shortcutOk = True;
941 } else if (strcmp(command, "ARRANGE_ICONS")==0) {
942 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
944 shortcutOk = True;
945 } else if (strcmp(command, "HIDE_OTHERS")==0) {
946 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
948 shortcutOk = True;
949 } else if (strcmp(command, "SHOW_ALL")==0) {
950 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
952 shortcutOk = True;
953 } else if (strcmp(command, "RESTART")==0) {
954 entry = wMenuAddCallback(menu, title, restartCommand,
955 params ? wstrdup(params) : NULL);
956 entry->free_cdata = free;
957 shortcutOk = True;
958 } else if (strcmp(command, "SAVE_SESSION")==0) {
959 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
961 shortcutOk = True;
962 } else if (strcmp(command, "CLEAR_SESSION")==0) {
963 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
964 shortcutOk = True;
965 } else if (strcmp(command, "INFO_PANEL")==0) {
966 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
967 shortcutOk = True;
968 } else if (strcmp(command, "LEGAL_PANEL")==0) {
969 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
970 shortcutOk = True;
971 } else {
972 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name,
973 command);
975 return NULL;
978 if (shortcut && entry) {
979 if (!shortcutOk) {
980 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name,
981 title);
982 } else {
983 if (addShortcut(file_name, shortcut, menu, entry)) {
985 entry->rtext = GetShortcutString(shortcut);
987 entry->rtext = wstrdup(shortcut);
993 return entry;
998 /******************* Menu Configuration From File *******************/
1000 static void
1001 separateline(char *line, char *title, char *command, char *parameter,
1002 char *shortcut)
1004 int l, i;
1006 l = strlen(line);
1008 *title = 0;
1009 *command = 0;
1010 *parameter = 0;
1011 *shortcut = 0;
1012 /* get the title */
1013 while (isspace(*line) && (*line!=0)) line++;
1014 if (*line=='"') {
1015 line++;
1016 i=0;
1017 while (line[i]!='"' && (line[i]!=0)) i++;
1018 if (line[i]!='"')
1019 return;
1020 } else {
1021 i=0;
1022 while (!isspace(line[i]) && (line[i]!=0)) i++;
1024 strncpy(title, line, i);
1025 title[i++]=0;
1026 line+=i;
1028 /* get the command or shortcut keyword */
1029 while (isspace(*line) && (*line!=0)) line++;
1030 if (*line==0)
1031 return;
1032 i=0;
1033 while (!isspace(line[i]) && (line[i]!=0)) i++;
1034 strncpy(command, line, i);
1035 command[i++]=0;
1036 line+=i;
1038 if (strcmp(command, "SHORTCUT")==0) {
1039 /* get the shortcut key */
1040 while (isspace(*line) && (*line!=0)) line++;
1041 if (*line=='"') {
1042 line++;
1043 i=0;
1044 while (line[i]!='"' && (line[i]!=0)) i++;
1045 if (line[i]!='"')
1046 return;
1047 } else {
1048 i=0;
1049 while (!isspace(line[i]) && (line[i]!=0)) i++;
1051 strncpy(shortcut, line, i);
1052 shortcut[i++]=0;
1053 line+=i;
1055 *command=0;
1057 /* get the command */
1058 while (isspace(*line) && (*line!=0)) line++;
1059 if (*line==0)
1060 return;
1061 i=0;
1062 while (!isspace(line[i]) && (line[i]!=0)) i++;
1063 strncpy(command, line, i);
1064 command[i++]=0;
1065 line+=i;
1068 /* get the parameters */
1069 while (isspace(*line) && (*line!=0)) line++;
1070 if (*line==0)
1071 return;
1073 if (*line=='"') {
1074 line++;
1075 l = 0;
1076 while (line[l]!=0 && line[l]!='"') {
1077 parameter[l] = line[l];
1078 l++;
1080 parameter[l] = 0;
1081 return;
1084 l = strlen(line);
1085 while (isspace(line[l]) && (l>0)) l--;
1086 strncpy(parameter, line, l);
1087 parameter[l]=0;
1091 static WMenu*
1092 parseCascade(WScreen *scr, WMenu *menu, FILE *file, char *file_name)
1094 char linebuf[MAXLINE];
1095 char elinebuf[MAXLINE];
1096 char title[MAXLINE];
1097 char command[MAXLINE];
1098 char shortcut[MAXLINE];
1099 char params[MAXLINE];
1100 char *line;
1102 while (!feof(file)) {
1103 int lsize, ok;
1105 ok = 0;
1106 fgets(linebuf, MAXLINE, file);
1107 line = cropline(linebuf);
1108 lsize = strlen(line);
1109 do {
1110 if (line[lsize-1]=='\\') {
1111 char *line2;
1112 int lsize2;
1113 fgets(elinebuf, MAXLINE, file);
1114 line2=cropline(elinebuf);
1115 lsize2=strlen(line2);
1116 if (lsize2+lsize>MAXLINE) {
1117 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1118 file_name, line);
1119 ok=2;
1120 } else {
1121 line[lsize-1]=0;
1122 lsize+=lsize2-1;
1123 strcat(line, line2);
1125 } else {
1126 ok=1;
1128 } while (!ok && !feof(file));
1129 if (ok==2)
1130 continue;
1132 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1133 continue;
1136 separateline(line, title, command, params, shortcut);
1138 if (!command[0]) {
1139 wwarning(_("%s:missing command in menu config: %s"), file_name,
1140 line);
1141 goto error;
1144 if (strcasecmp(command, "MENU")==0) {
1145 WMenu *cascade;
1147 /* start submenu */
1149 cascade = wMenuCreate(scr, title, False);
1150 cascade->on_destroy = removeShortcutsForMenu;
1151 if (parseCascade(scr, cascade, file, file_name)==NULL) {
1152 wMenuDestroy(cascade, True);
1153 } else {
1154 wMenuEntrySetCascade(menu,
1155 wMenuAddCallback(menu, title, NULL, NULL),
1156 cascade);
1158 } else if (strcasecmp(command, "END")==0) {
1159 /* end of menu */
1160 return menu;
1162 } else {
1163 /* normal items */
1164 addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command,
1165 params[0] ? params : NULL, file_name);
1169 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1170 file_name);
1171 return menu;
1173 error:
1174 return menu;
1178 static WMenu*
1179 readMenuFile(WScreen *scr, char *file_name)
1181 WMenu *menu=NULL;
1182 FILE *file = NULL;
1183 char linebuf[MAXLINE];
1184 char title[MAXLINE];
1185 char shortcut[MAXLINE];
1186 char command[MAXLINE];
1187 char params[MAXLINE];
1188 char *line;
1189 #ifdef USECPP
1190 char *args;
1191 int cpp = 0;
1192 #endif
1194 #ifdef USECPP
1195 if (!wPreferences.flags.nocpp) {
1196 args = MakeCPPArgs(file_name);
1197 if (!args) {
1198 wwarning(_("could not make arguments for menu file preprocessor"));
1199 } else {
1200 snprintf(command, sizeof(command), "%s %s %s",
1201 CPP_PATH, args, file_name);
1202 wfree(args);
1203 file = popen(command, "r");
1204 if (!file) {
1205 wsyserror(_("%s:could not open/preprocess menu file"),
1206 file_name);
1207 } else {
1208 cpp = 1;
1212 #endif /* USECPP */
1214 if (!file) {
1215 file = fopen(file_name, "rb");
1216 if (!file) {
1217 wsyserror(_("%s:could not open menu file"), file_name);
1218 return NULL;
1222 while (!feof(file)) {
1223 if (!fgets(linebuf, MAXLINE, file))
1224 break;
1225 line = cropline(linebuf);
1226 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1227 continue;
1229 separateline(line, title, command, params, shortcut);
1231 if (!command[0]) {
1232 wwarning(_("%s:missing command in menu config: %s"), file_name,
1233 line);
1234 break;
1236 if (strcasecmp(command, "MENU")==0) {
1237 menu = wMenuCreate(scr, title, True);
1238 menu->on_destroy = removeShortcutsForMenu;
1239 if (!parseCascade(scr, menu, file, file_name)) {
1240 wMenuDestroy(menu, True);
1242 break;
1243 } else {
1244 wwarning(_("%s:invalid menu file. MENU command is missing"),
1245 file_name);
1246 break;
1250 #ifdef CPP
1251 if (cpp) {
1252 if (pclose(file)==-1) {
1253 wsyserror(_("error reading preprocessed menu data"));
1255 } else {
1256 fclose(file);
1258 #else
1259 fclose(file);
1260 #endif
1262 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 + (flat_file[1]=='|'?2:1);
1296 #ifdef USECPP
1297 if (!wPreferences.flags.nocpp) {
1298 args = MakeCPPArgs(filename);
1299 if (!args) {
1300 wwarning(_("could not make arguments for menu file preprocessor"));
1301 } else {
1302 snprintf(command, sizeof(command), "%s | %s %s",
1303 filename, CPP_PATH, args);
1305 wfree(args);
1306 file = popen(command, "r");
1307 if (!file) {
1308 wsyserror(_("%s:could not open/preprocess menu file"), filename);
1309 } else {
1310 cpp = 1;
1315 #endif /* USECPP */
1317 if (!file) {
1318 file = popen(filename, "rb");
1320 if (!file) {
1321 wsyserror(_("%s:could not open menu file"), filename);
1322 return NULL;
1326 while (!feof(file)) {
1327 if (!fgets(linebuf, MAXLINE, file))
1328 break;
1329 line = cropline(linebuf);
1330 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1331 continue;
1333 separateline(line, title, command, params, shortcut);
1335 if (!command[0]) {
1336 wwarning(_("%s:missing command in menu config: %s"), file_name,
1337 line);
1338 break;
1340 if (strcasecmp(command, "MENU")==0) {
1341 menu = wMenuCreate(scr, title, True);
1342 menu->on_destroy = removeShortcutsForMenu;
1343 if (!parseCascade(scr, menu, file, filename)) {
1344 wMenuDestroy(menu, True);
1346 break;
1347 } else {
1348 wwarning(_("%s:no title given for the root menu"), filename);
1349 break;
1353 pclose(file);
1355 return menu;
1360 typedef struct {
1361 char *name;
1362 int index;
1363 } dir_data;
1366 static int
1367 myCompare(const void *d1, const void *d2)
1369 dir_data *p1 = *(dir_data**)d1;
1370 dir_data *p2 = *(dir_data**)d2;
1372 return strcmp(p1->name, p2->name);
1376 /************ Menu Configuration From Directory *************/
1378 static Bool
1379 isFilePackage(char *file)
1381 int l;
1383 /* check if the extension indicates this file is a
1384 * file package. For now, only recognize .themed */
1386 l = strlen(file);
1388 if (l > 7 && strcmp(&(file[l-7]), ".themed")==0) {
1389 return True;
1390 } else {
1391 return False;
1396 static WMenu*
1397 readMenuDirectory(WScreen *scr, char *title, char **path, char *command)
1399 DIR *dir;
1400 struct dirent *dentry;
1401 struct stat stat_buf;
1402 WMenu *menu=NULL;
1403 char *buffer;
1404 WMArray *dirs = NULL, *files = NULL;
1405 WMArrayIterator iter;
1406 int length, i, have_space=0;
1407 dir_data *data;
1408 int stripExtension = 0;
1411 dirs = WMCreateArray(16);
1412 files = WMCreateArray(16);
1414 i=0;
1415 while (path[i]!=NULL) {
1416 if (strcmp(path[i], "-noext")==0) {
1417 stripExtension = 1;
1418 i++;
1419 continue;
1422 dir = opendir(path[i]);
1423 if (!dir) {
1424 i++;
1425 continue;
1428 while ((dentry = readdir(dir))) {
1430 if (strcmp(dentry->d_name, ".")==0 ||
1431 strcmp(dentry->d_name, "..")==0)
1432 continue;
1434 if (dentry->d_name[0] == '.')
1435 continue;
1437 buffer = malloc(strlen(path[i])+strlen(dentry->d_name)+4);
1438 if (!buffer) {
1439 wsyserror(_("out of memory while constructing directory menu %s"),
1440 path[i]);
1441 break;
1444 strcpy(buffer, path[i]);
1445 strcat(buffer, "/");
1446 strcat(buffer, dentry->d_name);
1448 if (stat(buffer, &stat_buf)!=0) {
1449 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1450 path[i], dentry->d_name);
1451 } else {
1452 Bool isFilePack = False;
1454 data = NULL;
1455 if (S_ISDIR(stat_buf.st_mode)
1456 && !(isFilePack = isFilePackage(dentry->d_name))) {
1458 /* access always returns success for user root */
1459 if (access(buffer, X_OK)==0) {
1460 /* Directory is accesible. Add to directory list */
1462 data = (dir_data*) wmalloc(sizeof(dir_data));
1463 data->name = wstrdup(dentry->d_name);
1464 data->index = i;
1466 WMAddToArray(dirs, data);
1468 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1469 /* Hack because access always returns X_OK success for user root */
1470 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1471 if ((command!=NULL && access(buffer, R_OK)==0) ||
1472 (command==NULL && access(buffer, X_OK)==0 &&
1473 (stat_buf.st_mode & S_IXANY))) {
1475 data = (dir_data*) wmalloc(sizeof(dir_data));
1476 data->name = wstrdup(dentry->d_name);
1477 data->index = i;
1479 WMAddToArray(files, data);
1483 wfree(buffer);
1486 closedir(dir);
1487 i++;
1490 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1491 WMFreeArray(dirs);
1492 WMFreeArray(files);
1493 return NULL;
1496 WMSortArray(dirs, myCompare);
1497 WMSortArray(files, myCompare);
1499 menu = wMenuCreate(scr, title, False);
1500 menu->on_destroy = removeShortcutsForMenu;
1502 WM_ITERATE_ARRAY(dirs, data, iter) {
1503 /* New directory. Use same OPEN_MENU command that was used
1504 * for the current directory. */
1505 length = strlen(path[data->index])+strlen(data->name)+6;
1506 if (stripExtension)
1507 length += 7;
1508 if (command)
1509 length += strlen(command) + 6;
1510 buffer = malloc(length);
1511 if (!buffer) {
1512 wsyserror(_("out of memory while constructing directory menu %s"),
1513 path[data->index]);
1514 break;
1517 buffer[0] = '\0';
1518 if (stripExtension)
1519 strcat(buffer, "-noext ");
1521 have_space = strchr(path[data->index], ' ')!=NULL ||
1522 strchr(data->name, ' ')!=NULL;
1524 if (have_space)
1525 strcat(buffer, "\"");
1526 strcat(buffer, path[data->index]);
1528 strcat(buffer, "/");
1529 strcat(buffer, data->name);
1530 if (have_space)
1531 strcat(buffer, "\"");
1532 if (command) {
1533 strcat(buffer, " WITH ");
1534 strcat(buffer, command);
1537 addMenuEntry(menu, data->name, NULL, "OPEN_MENU", buffer, path[data->index]);
1539 wfree(buffer);
1540 if (data->name)
1541 wfree(data->name);
1542 wfree(data);
1545 WM_ITERATE_ARRAY(files, data, iter) {
1546 /* executable: add as entry */
1547 length = strlen(path[data->index])+strlen(data->name)+6;
1548 if (command)
1549 length += strlen(command);
1551 buffer = malloc(length);
1552 if (!buffer) {
1553 wsyserror(_("out of memory while constructing directory menu %s"),
1554 path[data->index]);
1555 break;
1558 have_space = strchr(path[data->index], ' ')!=NULL ||
1559 strchr(data->name, ' ')!=NULL;
1560 if (command!=NULL) {
1561 strcpy(buffer, command);
1562 strcat(buffer, " ");
1563 if (have_space)
1564 strcat(buffer, "\"");
1565 strcat(buffer, path[data->index]);
1566 } else {
1567 if (have_space) {
1568 buffer[0] = '"';
1569 buffer[1] = 0;
1570 strcat(buffer, path[data->index]);
1571 } else {
1572 strcpy(buffer, path[data->index]);
1575 strcat(buffer, "/");
1576 strcat(buffer, data->name);
1577 if (have_space)
1578 strcat(buffer, "\"");
1580 if (stripExtension) {
1581 char *ptr = strrchr(data->name, '.');
1582 if (ptr && ptr!=data->name)
1583 *ptr = 0;
1585 addMenuEntry(menu, data->name, NULL, "SHEXEC", buffer, path[data->index]);
1587 wfree(buffer);
1588 if (data->name)
1589 wfree(data->name);
1590 wfree(data);
1593 WMFreeArray(files);
1594 WMFreeArray(dirs);
1596 return menu;
1600 /************ Menu Configuration From WMRootMenu *************/
1602 static WMenu*
1603 makeDefaultMenu(WScreen *scr)
1605 WMenu *menu=NULL;
1607 menu = wMenuCreate(scr, _("Commands"), True);
1608 wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
1609 wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
1610 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1611 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1612 return menu;
1620 *----------------------------------------------------------------------
1621 * configureMenu--
1622 * Reads root menu configuration from defaults database.
1624 *----------------------------------------------------------------------
1626 static WMenu*
1627 configureMenu(WScreen *scr, WMPropList *definition)
1629 WMenu *menu = NULL;
1630 WMPropList *elem;
1631 int i, count;
1632 WMPropList *title, *command, *params;
1633 char *tmp, *mtitle;
1636 if (WMIsPLString(definition)) {
1637 struct stat stat_buf;
1638 char *path = NULL;
1639 Bool menu_is_default = False;
1641 /* menu definition is a string. Probably a path, so parse the file */
1643 tmp = wexpandpath(WMGetFromPLString(definition));
1645 path = getLocalizedMenuFile(tmp);
1647 if (!path)
1648 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1650 if (!path) {
1651 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1652 menu_is_default = True;
1655 if (!path) {
1656 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1657 tmp);
1658 wfree(tmp);
1659 return NULL;
1662 if (stat(path, &stat_buf)<0) {
1663 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1664 wfree(path);
1665 wfree(tmp);
1666 return NULL;
1669 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1670 /* if the pointer in WMRootMenu has changed */
1671 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1673 if (menu_is_default) {
1674 wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1675 path);
1678 menu = readMenuFile(scr, path);
1679 if (menu)
1680 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1681 } else {
1682 menu = NULL;
1684 wfree(path);
1685 wfree(tmp);
1687 return menu;
1690 count = WMGetPropListItemCount(definition);
1691 if (count==0)
1692 return NULL;
1694 elem = WMGetFromPLArray(definition, 0);
1695 if (!WMIsPLString(elem)) {
1696 tmp = WMGetPropListDescription(elem, False);
1697 wwarning(_("%s:format error in root menu configuration \"%s\""),
1698 "WMRootMenu", tmp);
1699 wfree(tmp);
1700 return NULL;
1702 mtitle = WMGetFromPLString(elem);
1704 menu = wMenuCreate(scr, mtitle, False);
1705 menu->on_destroy = removeShortcutsForMenu;
1707 #ifdef GLOBAL_SUBMENU_FILE
1709 WMenu *submenu;
1710 WMenuEntry *mentry;
1712 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1714 if (submenu) {
1715 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1716 wMenuEntrySetCascade(menu, mentry, submenu);
1719 #endif
1721 for (i=1; i<count; i++) {
1722 elem = WMGetFromPLArray(definition, i);
1723 #if 0
1724 if (WMIsPLString(elem)) {
1725 char *file;
1727 file = WMGetFromPLString(elem);
1730 #endif
1731 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1732 goto error;
1734 if (WMIsPLArray(WMGetFromPLArray(elem,1))) {
1735 WMenu *submenu;
1736 WMenuEntry *mentry;
1738 /* submenu */
1739 submenu = configureMenu(scr, elem);
1740 if (submenu) {
1741 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL,
1742 NULL);
1743 wMenuEntrySetCascade(menu, mentry, submenu);
1745 } else {
1746 int idx = 0;
1747 WMPropList *shortcut;
1748 /* normal entry */
1750 title = WMGetFromPLArray(elem, idx++);
1751 shortcut = WMGetFromPLArray(elem, idx++);
1752 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT")==0) {
1753 shortcut = WMGetFromPLArray(elem, idx++);
1754 command = WMGetFromPLArray(elem, idx++);
1755 } else {
1756 command = shortcut;
1757 shortcut = NULL;
1759 params = WMGetFromPLArray(elem, idx++);
1761 if (!title || !command)
1762 goto error;
1764 addMenuEntry(menu, WMGetFromPLString(title),
1765 shortcut ? WMGetFromPLString(shortcut) : NULL,
1766 WMGetFromPLString(command),
1767 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1769 continue;
1771 error:
1772 tmp = WMGetPropListDescription(elem, False);
1773 wwarning(_("%s:format error in root menu configuration \"%s\""),
1774 "WMRootMenu", tmp);
1775 wfree(tmp);
1778 return menu;
1783 *----------------------------------------------------------------------
1784 * OpenRootMenu--
1785 * Opens the root menu, parsing the menu configuration from the
1786 * defaults database.
1787 * If the menu is already mapped and is not sticked to the
1788 * root window, it will be unmapped.
1790 * Side effects:
1791 * The menu may be remade.
1793 * Notes:
1794 * Construction of OPEN_MENU entries are delayed to the moment the
1795 * user map's them.
1796 *----------------------------------------------------------------------
1798 void
1799 OpenRootMenu(WScreen *scr, int x, int y, int keyboard)
1801 WMenu *menu=NULL;
1802 WMPropList *definition;
1804 static WMPropList *domain=NULL;
1806 if (!domain) {
1807 domain = WMCreatePLString("WMRootMenu");
1811 scr->flags.root_menu_changed_shortcuts = 0;
1812 scr->flags.added_workspace_menu = 0;
1813 scr->flags.added_windows_menu = 0;
1815 if (scr->root_menu && scr->root_menu->flags.mapped) {
1816 menu = scr->root_menu;
1817 if (!menu->flags.buttoned) {
1818 wMenuUnmap(menu);
1819 } else {
1820 wRaiseFrame(menu->frame->core);
1822 if (keyboard)
1823 wMenuMapAt(menu, 0, 0, True);
1824 else
1825 wMenuMapCopyAt(menu, x-menu->frame->core->width/2, y);
1827 return;
1831 definition = WDRootMenu->dictionary;
1834 definition = PLGetDomain(domain);
1836 if (definition) {
1837 if (WMIsPLArray(definition)) {
1838 if (!scr->root_menu
1839 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1840 menu = configureMenu(scr, definition);
1841 if (menu)
1842 menu->timestamp = WDRootMenu->timestamp;
1844 } else
1845 menu = NULL;
1846 } else {
1847 menu = configureMenu(scr, definition);
1851 if (!menu) {
1852 /* menu hasn't changed or could not be read */
1853 if (!scr->root_menu) {
1854 wMessageDialog(scr, _("Error"),
1855 _("The applications menu could not be loaded. "
1856 "Look at the console output for a detailed "
1857 "description of the errors."),
1858 _("OK"), NULL, NULL);
1860 menu = makeDefaultMenu(scr);
1861 scr->root_menu = menu;
1863 menu = scr->root_menu;
1864 } else {
1865 /* new root menu */
1866 if (scr->root_menu) {
1867 wMenuDestroy(scr->root_menu, True);
1869 scr->root_menu = menu;
1871 if (menu) {
1872 int newx, newy;
1874 if (keyboard && x==0 && y==0) {
1875 newx = newy = 0;
1876 } else if (keyboard && x==scr->scr_width/2 && y==scr->scr_height/2) {
1877 newx = x - menu->frame->core->width/2;
1878 newy = y - menu->frame->core->height/2;
1879 } else {
1880 newx = x - menu->frame->core->width/2;
1881 newy = y;
1883 wMenuMapAt(menu, newx, newy, keyboard);
1886 if (scr->flags.root_menu_changed_shortcuts)
1887 rebindKeygrabs(scr);
1890 #endif /* !LITE */