1 /* rootmenu.c- user defined menu
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include <sys/types.h>
38 #include <X11/Xutil.h>
39 #include <X11/Xatom.h>
41 #include "WindowMaker.h"
48 #include "workspace.h"
52 #include "xmodifier.h"
54 #include <WINGs/WUtil.h>
56 #define MAX_SHORTCUT_LENGTH 32
59 extern WDDomain
*WDRootMenu
;
60 extern Cursor wCursor
[WCUR_LAST
];
61 extern WPreferences wPreferences
;
63 static WMenu
*readMenuPipe(WScreen
* scr
, char **file_name
);
64 static WMenu
*readMenuFile(WScreen
* scr
, char *file_name
);
65 static WMenu
*readMenuDirectory(WScreen
* scr
, char *title
, char **file_name
, char *command
);
66 static WMenu
*configureMenu(WScreen
* scr
, WMPropList
* definition
, Bool includeGlobals
);
68 typedef struct Shortcut
{
69 struct Shortcut
*next
;
77 static Shortcut
*shortcutList
= NULL
;
83 * "Title" EXEC command_to_exec -params
85 * "Title" EXEC command_to_exec -params
87 * "Workspaces" WORKSPACE_MENU
88 * "Title" built_in_command
90 * "Quick Quit" EXIT QUICK
93 * Commands may be preceded by SHORTCUT key
97 * INFO_PANEL - shows the Info Panel
98 * LEGAL_PANEL - shows the Legal info panel
99 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
100 * REFRESH - forces the desktop to be repainted
101 * EXIT [QUICK] - exit the window manager [without confirmation]
102 * EXEC <program> - execute an external program
103 * SHEXEC <command> - execute a shell command
104 * WORKSPACE_MENU - places the workspace submenu
106 * RESTART [<window manager>] - restarts the window manager
107 * SHOW_ALL - unhide all windows on workspace
108 * HIDE_OTHERS - hides all windows excep the focused one
109 * OPEN_MENU file - read menu data from file which must be a valid menu file.
110 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
111 * - read menu data from directory(ies) and
112 * eventually precede each with a command.
113 * OPEN_MENU | command
114 * - opens command and uses its stdout to construct and insert
115 * the resulting menu in current position. The output of
116 * command must be a valid menu description.
117 * The space between '|' and command is optional.
118 * || will do the same, but will not cache the contents.
119 * SAVE_SESSION - saves the current state of the desktop, which include
120 * all running applications, all their hints (geometry,
121 * position on screen, workspace they live on, the dock
122 * or clip from where they were launched, and
123 * if minimized, shaded or hidden. Also saves the current
124 * workspace the user is on. All will be restored on every
125 * start of windowmaker until another SAVE_SESSION or
126 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
127 * WindowMaker domain file, then saving is automatically
128 * done on every windowmaker exit, overwriting any
129 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
131 * CLEAR_SESSION - clears any previous saved session. This will not have
132 * any effect if SaveSessionOnExit is True.
140 static void execCommand(WMenu
* menu
, WMenuEntry
* entry
)
144 cmdline
= ExpandOptions(menu
->frame
->screen_ptr
, (char *)entry
->clientdata
);
146 XGrabPointer(dpy
, menu
->frame
->screen_ptr
->root_win
, True
, 0,
147 GrabModeAsync
, GrabModeAsync
, None
, wCursor
[WCUR_WAIT
], CurrentTime
);
151 ExecuteShellCommand(menu
->frame
->screen_ptr
, cmdline
);
154 XUngrabPointer(dpy
, CurrentTime
);
158 static void exitCommand(WMenu
* menu
, WMenuEntry
* entry
)
160 static int inside
= 0;
163 /* prevent reentrant calls */
173 if ((long)entry
->clientdata
== M_QUICK
) {
176 int r
, oldSaveSessionFlag
;
178 oldSaveSessionFlag
= wPreferences
.save_session_on_exit
;
179 r
= wExitDialog(menu
->frame
->screen_ptr
, _("Exit"),
180 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL
);
182 if (r
== WAPRDefault
) {
184 } else if (r
== WAPRAlternate
) {
185 /* Don't modify the "save session on exit" flag if the
186 * user canceled the operation. */
187 wPreferences
.save_session_on_exit
= oldSaveSessionFlag
;
190 if (result
== R_EXIT
)
191 Shutdown(WSExitMode
);
198 static void shutdownCommand(WMenu
* menu
, WMenuEntry
* entry
)
200 static int inside
= 0;
203 /* prevent reentrant calls */
213 if ((long)entry
->clientdata
== M_QUICK
)
216 int r
, oldSaveSessionFlag
;
218 oldSaveSessionFlag
= wPreferences
.save_session_on_exit
;
220 r
= wExitDialog(menu
->frame
->screen_ptr
,
222 _("Kill Window System session?\n"
223 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL
);
224 if (r
== WAPRDefault
) {
226 } else if (r
== WAPRAlternate
) {
227 /* Don't modify the "save session on exit" flag if the
228 * user canceled the operation. */
229 wPreferences
.save_session_on_exit
= oldSaveSessionFlag
;
233 if (result
!= R_CANCEL
) {
234 Shutdown(WSKillMode
);
242 static void restartCommand(WMenu
* menu
, WMenuEntry
* entry
)
244 Shutdown(WSRestartPreparationMode
);
245 Restart((char *)entry
->clientdata
, False
);
249 static void refreshCommand(WMenu
* menu
, WMenuEntry
* entry
)
251 wRefreshDesktop(menu
->frame
->screen_ptr
);
254 static void arrangeIconsCommand(WMenu
* menu
, WMenuEntry
* entry
)
256 wArrangeIcons(menu
->frame
->screen_ptr
, True
);
259 static void showAllCommand(WMenu
* menu
, WMenuEntry
* entry
)
261 wShowAllWindows(menu
->frame
->screen_ptr
);
264 static void hideOthersCommand(WMenu
* menu
, WMenuEntry
* entry
)
266 wHideOtherApplications(menu
->frame
->screen_ptr
->focused_window
);
269 static void saveSessionCommand(WMenu
* menu
, WMenuEntry
* entry
)
271 if (!wPreferences
.save_session_on_exit
)
272 wSessionSaveState(menu
->frame
->screen_ptr
);
274 wScreenSaveState(menu
->frame
->screen_ptr
);
277 static void clearSessionCommand(WMenu
* menu
, WMenuEntry
* entry
)
279 wSessionClearState(menu
->frame
->screen_ptr
);
280 wScreenSaveState(menu
->frame
->screen_ptr
);
283 static void infoPanelCommand(WMenu
* menu
, WMenuEntry
* entry
)
285 wShowInfoPanel(menu
->frame
->screen_ptr
);
288 static void legalPanelCommand(WMenu
* menu
, WMenuEntry
* entry
)
290 wShowLegalPanel(menu
->frame
->screen_ptr
);
293 /********************************************************************/
295 static char * getLocalizedMenuFile(char *menu
)
297 char *buffer
, *ptr
, *locale
;
303 len
= strlen(menu
) + strlen(Locale
) + 8;
304 buffer
= wmalloc(len
);
306 /* try menu.locale_name */
307 snprintf(buffer
, len
, "%s.%s", menu
, Locale
);
308 if (access(buffer
, F_OK
) == 0)
311 /* position of locale in our buffer */
312 locale
= buffer
+ strlen(menu
) + 1;
314 /* check if it is in the form aa_bb.encoding and check for aa_bb */
315 ptr
= strchr(locale
, '.');
318 if (access(buffer
, F_OK
) == 0)
322 /* now check for aa */
323 ptr
= strchr(locale
, '_');
326 if (access(buffer
, F_OK
) == 0)
335 Bool
wRootMenuPerformShortcut(XEvent
* event
)
337 WScreen
*scr
= wScreenForRootWindow(event
->xkey
.root
);
342 /* ignore CapsLock */
343 modifiers
= event
->xkey
.state
& ValidModMask
;
345 for (ptr
= shortcutList
; ptr
!= NULL
; ptr
= ptr
->next
) {
346 if (ptr
->keycode
== 0 || ptr
->menu
->menu
->screen_ptr
!= scr
)
349 if (ptr
->keycode
== event
->xkey
.keycode
&& ptr
->modifier
== modifiers
) {
350 (*ptr
->entry
->callback
) (ptr
->menu
, ptr
->entry
);
358 void wRootMenuBindShortcuts(Window window
)
364 if (ptr
->modifier
!= AnyModifier
) {
365 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
| LockMask
,
366 window
, True
, GrabModeAsync
, GrabModeAsync
);
368 wHackedGrabKey(ptr
->keycode
, ptr
->modifier
, window
, True
, GrabModeAsync
, GrabModeAsync
);
371 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
, window
, True
, GrabModeAsync
, GrabModeAsync
);
376 static void rebindKeygrabs(WScreen
* scr
)
380 wwin
= scr
->focused_window
;
382 while (wwin
!= NULL
) {
383 XUngrabKey(dpy
, AnyKey
, AnyModifier
, wwin
->frame
->core
->window
);
385 if (!WFLAGP(wwin
, no_bind_keys
)) {
386 wWindowSetKeyGrabs(wwin
);
392 static void removeShortcutsForMenu(WMenu
* menu
)
395 Shortcut
*newList
= NULL
;
398 while (ptr
!= NULL
) {
400 if (ptr
->menu
== menu
) {
408 shortcutList
= newList
;
409 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
412 static Bool
addShortcut(char *file
, char *shortcutDefinition
, WMenu
* menu
, WMenuEntry
* entry
)
417 char buf
[MAX_SHORTCUT_LENGTH
], *b
;
419 ptr
= wmalloc(sizeof(Shortcut
));
421 wstrlcpy(buf
, shortcutDefinition
, MAX_SHORTCUT_LENGTH
);
426 while ((k
= strchr(b
, '+')) != NULL
) {
430 mod
= wXModifierFromKey(b
);
432 wwarning(_("%s: invalid key modifier \"%s\""), file
, b
);
436 ptr
->modifier
|= mod
;
442 ksym
= XStringToKeysym(b
);
444 if (ksym
== NoSymbol
) {
445 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
446 file
, shortcutDefinition
, entry
->text
);
451 ptr
->keycode
= XKeysymToKeycode(dpy
, ksym
);
452 if (ptr
->keycode
== 0) {
453 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file
,
454 shortcutDefinition
, entry
->text
);
462 ptr
->next
= shortcutList
;
465 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
470 static char *next_token(char *line
, char **next
)
476 while (*line
== ' ' || *line
== '\t')
484 while (*tmp
!= 0 && *tmp
!= '"')
487 wwarning(_("%s: unmatched '\"' in menu file"), line
);
498 } while (*tmp
!= 0 && *tmp
!= ' ' && *tmp
!= '\t');
512 while (*tmp
== ' ' || *tmp
== '\t')
521 static void separateCommand(char *line
, char ***file
, char **command
)
523 char *token
, *tmp
= line
;
524 WMArray
*array
= WMCreateArray(4);
530 token
= next_token(tmp
, &tmp
);
532 if (strcmp(token
, "WITH") == 0) {
533 if (tmp
!= NULL
&& *tmp
!= 0)
534 *command
= wstrdup(tmp
);
536 wwarning(_("%s: missing command"), line
);
539 WMAddToArray(array
, token
);
541 } while (token
!= NULL
&& tmp
!= NULL
);
543 count
= WMGetArrayItemCount(array
);
545 *file
= wmalloc(sizeof(char *) * (count
+ 1));
546 (*file
)[count
] = NULL
;
547 for (i
= 0; i
< count
; i
++) {
548 (*file
)[i
] = WMGetFromArray(array
, i
);
554 static WMenu
*constructPLMenu(WScreen
*screen
, char *path
)
556 WMPropList
*pl
= NULL
;
562 pl
= WMReadPropListFromFile(path
);
566 menu
= configureMenu(screen
, pl
, False
);
570 menu
->on_destroy
= removeShortcutsForMenu
;
574 static void constructMenu(WMenu
* menu
, WMenuEntry
* entry
)
577 struct stat stat_buf
;
584 separateCommand((char *)entry
->clientdata
, &path
, &cmd
);
585 if (path
== NULL
|| *path
== NULL
|| **path
== 0) {
586 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry
->clientdata
);
592 if (path
[0][0] == '|') {
595 if (!menu
->cascades
[entry
->cascade
] || menu
->cascades
[entry
->cascade
]->timestamp
== 0) {
598 submenu
= readMenuPipe(menu
->frame
->screen_ptr
, path
);
600 if (submenu
!= NULL
) {
601 if (path
[0][1] == '|')
602 submenu
->timestamp
= 0;
604 submenu
->timestamp
= 1; /* there's no automatic reloading */
612 /* try interpreting path as a proplist file */
613 submenu
= constructPLMenu(menu
->frame
->screen_ptr
, path
[0]);
614 /* if unsuccessful, try it as an old-style file */
618 while (path
[i
] != NULL
) {
621 if (strcmp(path
[i
], "-noext") == 0) {
626 tmp
= wexpandpath(path
[i
]);
628 lpath
= getLocalizedMenuFile(tmp
);
637 if (stat(path
[i
], &stat_buf
) == 0) {
638 if (last
< stat_buf
.st_mtime
)
639 last
= stat_buf
.st_mtime
;
643 werror(_("%s:could not stat menu"), path
[i
]);
651 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry
->clientdata
);
654 stat(path
[first
], &stat_buf
);
655 if (!menu
->cascades
[entry
->cascade
]
656 || menu
->cascades
[entry
->cascade
]->timestamp
< last
) {
658 if (S_ISDIR(stat_buf
.st_mode
)) {
660 submenu
= readMenuDirectory(menu
->frame
->screen_ptr
, entry
->text
, path
, cmd
);
662 submenu
->timestamp
= last
;
663 } else if (S_ISREG(stat_buf
.st_mode
)) {
667 wwarning(_("too many parameters in OPEN_MENU: %s"),
668 (char *)entry
->clientdata
);
670 submenu
= readMenuFile(menu
->frame
->screen_ptr
, path
[first
]);
672 submenu
->timestamp
= stat_buf
.st_mtime
;
683 wMenuEntryRemoveCascade(menu
, entry
);
684 wMenuEntrySetCascade(menu
, entry
, submenu
);
689 while (path
[i
] != NULL
)
696 static void cleanupWorkspaceMenu(WMenu
* menu
)
698 if (menu
->frame
->screen_ptr
->workspace_menu
== menu
)
699 menu
->frame
->screen_ptr
->workspace_menu
= NULL
;
702 static WMenuEntry
*addWorkspaceMenu(WScreen
* scr
, WMenu
* menu
, char *title
)
707 if (scr
->flags
.added_workspace_menu
) {
709 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
712 scr
->flags
.added_workspace_menu
= 1;
714 wsmenu
= wWorkspaceMenuMake(scr
, True
);
715 wsmenu
->on_destroy
= cleanupWorkspaceMenu
;
717 scr
->workspace_menu
= wsmenu
;
718 entry
= wMenuAddCallback(menu
, title
, NULL
, NULL
);
719 wMenuEntrySetCascade(menu
, entry
, wsmenu
);
721 wWorkspaceMenuUpdate(scr
, wsmenu
);
726 static void cleanupWindowsMenu(WMenu
* menu
)
728 if (menu
->frame
->screen_ptr
->switch_menu
== menu
)
729 menu
->frame
->screen_ptr
->switch_menu
= NULL
;
732 static WMenuEntry
*addWindowsMenu(WScreen
* scr
, WMenu
* menu
, char *title
)
738 if (scr
->flags
.added_windows_menu
) {
740 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
743 scr
->flags
.added_windows_menu
= 1;
745 wwmenu
= wMenuCreate(scr
, _("Window List"), False
);
746 wwmenu
->on_destroy
= cleanupWindowsMenu
;
747 scr
->switch_menu
= wwmenu
;
748 wwin
= scr
->focused_window
;
750 UpdateSwitchMenu(scr
, wwin
, ACTION_ADD
);
754 entry
= wMenuAddCallback(menu
, title
, NULL
, NULL
);
755 wMenuEntrySetCascade(menu
, entry
, wwmenu
);
760 static WMenuEntry
*addMenuEntry(WMenu
* menu
, char *title
, char *shortcut
, char *command
,
761 char *params
, char *file_name
)
764 WMenuEntry
*entry
= NULL
;
765 Bool shortcutOk
= False
;
769 scr
= menu
->frame
->screen_ptr
;
770 if (strcmp(command
, "OPEN_MENU") == 0) {
772 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name
, command
);
777 path
= wfindfile(DEF_CONFIG_PATHS
, params
);
779 path
= wstrdup(params
);
781 dummy
= wMenuCreate(scr
, title
, False
);
782 dummy
->on_destroy
= removeShortcutsForMenu
;
783 entry
= wMenuAddCallback(menu
, title
, constructMenu
, path
);
784 entry
->free_cdata
= wfree
;
785 wMenuEntrySetCascade(menu
, entry
, dummy
);
787 } else if (strcmp(command
, "EXEC") == 0) {
789 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name
, command
);
791 entry
= wMenuAddCallback(menu
, title
, execCommand
, wstrconcat("exec ", params
));
792 entry
->free_cdata
= wfree
;
795 } else if (strcmp(command
, "SHEXEC") == 0) {
797 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name
, command
);
799 entry
= wMenuAddCallback(menu
, title
, execCommand
, wstrdup(params
));
800 entry
->free_cdata
= wfree
;
803 } else if (strcmp(command
, "EXIT") == 0) {
805 if (params
&& strcmp(params
, "QUICK") == 0)
806 entry
= wMenuAddCallback(menu
, title
, exitCommand
, (void *)M_QUICK
);
808 entry
= wMenuAddCallback(menu
, title
, exitCommand
, NULL
);
811 } else if (strcmp(command
, "SHUTDOWN") == 0) {
813 if (params
&& strcmp(params
, "QUICK") == 0)
814 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
, (void *)M_QUICK
);
816 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
, NULL
);
819 } else if (strcmp(command
, "REFRESH") == 0) {
820 entry
= wMenuAddCallback(menu
, title
, refreshCommand
, NULL
);
823 } else if (strcmp(command
, "WORKSPACE_MENU") == 0) {
824 entry
= addWorkspaceMenu(scr
, menu
, title
);
827 } else if (strcmp(command
, "WINDOWS_MENU") == 0) {
828 entry
= addWindowsMenu(scr
, menu
, title
);
831 } else if (strcmp(command
, "ARRANGE_ICONS") == 0) {
832 entry
= wMenuAddCallback(menu
, title
, arrangeIconsCommand
, NULL
);
835 } else if (strcmp(command
, "HIDE_OTHERS") == 0) {
836 entry
= wMenuAddCallback(menu
, title
, hideOthersCommand
, NULL
);
839 } else if (strcmp(command
, "SHOW_ALL") == 0) {
840 entry
= wMenuAddCallback(menu
, title
, showAllCommand
, NULL
);
843 } else if (strcmp(command
, "RESTART") == 0) {
844 entry
= wMenuAddCallback(menu
, title
, restartCommand
, params
? wstrdup(params
) : NULL
);
845 entry
->free_cdata
= wfree
;
847 } else if (strcmp(command
, "SAVE_SESSION") == 0) {
848 entry
= wMenuAddCallback(menu
, title
, saveSessionCommand
, NULL
);
851 } else if (strcmp(command
, "CLEAR_SESSION") == 0) {
852 entry
= wMenuAddCallback(menu
, title
, clearSessionCommand
, NULL
);
854 } else if (strcmp(command
, "INFO_PANEL") == 0) {
855 entry
= wMenuAddCallback(menu
, title
, infoPanelCommand
, NULL
);
857 } else if (strcmp(command
, "LEGAL_PANEL") == 0) {
858 entry
= wMenuAddCallback(menu
, title
, legalPanelCommand
, NULL
);
861 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name
, command
);
866 if (shortcut
&& entry
) {
868 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name
, title
);
870 if (addShortcut(file_name
, shortcut
, menu
, entry
)) {
872 entry
->rtext
= GetShortcutString(shortcut
);
874 entry->rtext = wstrdup(shortcut);
883 /******************* Menu Configuration From File *******************/
885 static void freeline(char *title
, char *command
, char *parameter
, char *shortcut
)
893 static void separateline(char *line
, char **title
, char **command
, char **parameter
, char **shortcut
)
895 char *suffix
, *next
= line
;
903 *title
= wtokennext(line
, &next
);
908 /* get the command or shortcut keyword */
909 *command
= wtokennext(line
, &next
);
914 if (*command
!= NULL
&& strcmp(*command
, "SHORTCUT") == 0) {
915 /* get the shortcut */
916 *shortcut
= wtokennext(line
, &next
);
921 /* get the command */
922 *command
= wtokennext(line
, &next
);
928 /* get the parameters */
929 suffix
= wtrimspace(line
);
931 /* should we keep this weird old behavior? */
932 if (suffix
[0] == '"') {
933 *parameter
= wtokennext(suffix
, &next
);
940 static char *getLine(FILE * file
, const char *file_name
)
942 char linebuf
[MAXLINE
];
943 char *line
= NULL
, *result
= NULL
;
949 while (!done
&& fgets(linebuf
, sizeof(linebuf
), file
) != NULL
) {
950 line
= wtrimspace(linebuf
);
953 /* allow line wrapping */
954 if (len
> 0 && line
[len
- 1] == '\\') {
955 line
[len
- 1] = '\0';
960 if (result
== NULL
) {
963 if (strlen(result
) < MAXLINE
) {
964 result
= wstrappend(result
, line
);
969 if (!done
|| ferror(file
)) {
972 } else if (result
!= NULL
&& (result
[0] == 0 || result
[0] == '#' ||
973 (result
[0] == '/' && result
[1] == '/'))) {
977 } else if (result
!= NULL
&& strlen(result
) >= MAXLINE
) {
978 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
988 static WMenu
*parseCascade(WScreen
* scr
, WMenu
* menu
, FILE * file
, char *file_name
)
991 char *command
, *params
, *shortcut
, *title
;
993 while ((line
= getLine(file
, file_name
)) != NULL
) {
994 separateline(line
, &title
, &command
, ¶ms
, &shortcut
);
996 if (command
== NULL
|| !command
[0]) {
997 freeline(title
, command
, params
, shortcut
);
998 wwarning(_("%s:missing command in menu config: %s"), file_name
, line
);
1002 if (strcasecmp(command
, "MENU") == 0) {
1007 cascade
= wMenuCreate(scr
, M_(title
), False
);
1008 cascade
->on_destroy
= removeShortcutsForMenu
;
1009 if (parseCascade(scr
, cascade
, file
, file_name
) == NULL
) {
1010 wMenuDestroy(cascade
, True
);
1012 wMenuEntrySetCascade(menu
, wMenuAddCallback(menu
, M_(title
), NULL
, NULL
), cascade
);
1014 } else if (strcasecmp(command
, "END") == 0) {
1016 freeline(title
, command
, params
, shortcut
);
1020 addMenuEntry(menu
, M_(title
), shortcut
, command
, params
, file_name
);
1022 freeline(title
, command
, params
, shortcut
);
1025 wwarning(_("%s:syntax error in menu file:END declaration missing"), file_name
);
1031 static WMenu
*readMenuFile(WScreen
* scr
, char *file_name
)
1036 char *command
, *params
, *shortcut
, *title
;
1044 if (!wPreferences
.flags
.nocpp
) {
1045 args
= MakeCPPArgs(file_name
);
1047 wwarning(_("could not make arguments for menu file preprocessor"));
1049 snprintf(cmd
, sizeof(cmd
), "%s %s %s", CPP_PATH
, args
, file_name
);
1051 file
= popen(cmd
, "r");
1053 werror(_("%s:could not open/preprocess menu file"), file_name
);
1062 file
= fopen(file_name
, "rb");
1064 werror(_("%s:could not open menu file"), file_name
);
1069 while ((line
= getLine(file
, file_name
)) != NULL
) {
1070 separateline(line
, &title
, &command
, ¶ms
, &shortcut
);
1072 if (command
== NULL
|| !command
[0]) {
1073 wwarning(_("%s:missing command in menu config: %s"), file_name
, line
);
1076 if (strcasecmp(command
, "MENU") == 0) {
1077 menu
= wMenuCreate(scr
, M_(title
), True
);
1078 menu
->on_destroy
= removeShortcutsForMenu
;
1079 if (!parseCascade(scr
, menu
, file
, file_name
)) {
1080 wMenuDestroy(menu
, True
);
1084 wwarning(_("%s:invalid menu file. MENU command is missing"), file_name
);
1088 freeline(title
, command
, params
, shortcut
);
1092 if (pclose(file
) == -1) {
1093 werror(_("error reading preprocessed menu data"));
1105 /************ Menu Configuration From Pipe *************/
1107 static WMenu
*readMenuPipe(WScreen
* scr
, char **file_name
)
1111 char *command
, *params
, *shortcut
, *title
;
1114 char flat_file
[MAXLINE
];
1120 flat_file
[0] = '\0';
1122 for (i
= 0; file_name
[i
] != NULL
; i
++) {
1123 strcat(flat_file
, file_name
[i
]);
1124 strcat(flat_file
, " ");
1126 filename
= flat_file
+ (flat_file
[1] == '|' ? 2 : 1);
1129 if (!wPreferences
.flags
.nocpp
) {
1130 args
= MakeCPPArgs(filename
);
1132 wwarning(_("could not make arguments for menu file preprocessor"));
1134 snprintf(command
, sizeof(command
), "%s | %s %s", filename
, CPP_PATH
, args
);
1137 file
= popen(command
, "r");
1139 werror(_("%s:could not open/preprocess menu file"), filename
);
1146 file
= popen(filename
, "rb");
1149 werror(_("%s:could not open menu file"), filename
);
1154 while ((line
= getLine(file
, filename
)) != NULL
) {
1155 separateline(line
, &title
, &command
, ¶ms
, &shortcut
);
1157 if (command
== NULL
|| !command
[0]) {
1158 wwarning(_("%s:missing command in menu config: %s"), filename
, line
);
1161 if (strcasecmp(command
, "MENU") == 0) {
1162 menu
= wMenuCreate(scr
, M_(title
), True
);
1163 menu
->on_destroy
= removeShortcutsForMenu
;
1164 if (!parseCascade(scr
, menu
, file
, filename
)) {
1165 wMenuDestroy(menu
, True
);
1169 wwarning(_("%s:no title given for the root menu"), filename
);
1173 freeline(title
, command
, params
, shortcut
);
1185 static int myCompare(const void *d1
, const void *d2
)
1187 dir_data
*p1
= *(dir_data
**) d1
;
1188 dir_data
*p2
= *(dir_data
**) d2
;
1190 return strcmp(p1
->name
, p2
->name
);
1193 /************ Menu Configuration From Directory *************/
1195 static Bool
isFilePackage(char *file
)
1199 /* check if the extension indicates this file is a
1200 * file package. For now, only recognize .themed */
1204 if (l
> 7 && strcmp(&(file
[l
- 7]), ".themed") == 0) {
1211 static WMenu
*readMenuDirectory(WScreen
* scr
, char *title
, char **path
, char *command
)
1214 struct dirent
*dentry
;
1215 struct stat stat_buf
;
1218 WMArray
*dirs
= NULL
, *files
= NULL
;
1219 WMArrayIterator iter
;
1220 int length
, i
, have_space
= 0;
1222 int stripExtension
= 0;
1224 dirs
= WMCreateArray(16);
1225 files
= WMCreateArray(16);
1228 while (path
[i
] != NULL
) {
1229 if (strcmp(path
[i
], "-noext") == 0) {
1235 dir
= opendir(path
[i
]);
1241 while ((dentry
= readdir(dir
))) {
1243 if (strcmp(dentry
->d_name
, ".") == 0 || strcmp(dentry
->d_name
, "..") == 0)
1246 if (dentry
->d_name
[0] == '.')
1249 buffer
= malloc(strlen(path
[i
]) + strlen(dentry
->d_name
) + 4);
1251 werror(_("out of memory while constructing directory menu %s"), path
[i
]);
1255 strcpy(buffer
, path
[i
]);
1256 strcat(buffer
, "/");
1257 strcat(buffer
, dentry
->d_name
);
1259 if (stat(buffer
, &stat_buf
) != 0) {
1260 werror(_("%s:could not stat file \"%s\" in menu directory"),
1261 path
[i
], dentry
->d_name
);
1263 Bool isFilePack
= False
;
1266 if (S_ISDIR(stat_buf
.st_mode
)
1267 && !(isFilePack
= isFilePackage(dentry
->d_name
))) {
1269 /* access always returns success for user root */
1270 if (access(buffer
, X_OK
) == 0) {
1271 /* Directory is accesible. Add to directory list */
1273 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1274 data
->name
= wstrdup(dentry
->d_name
);
1277 WMAddToArray(dirs
, data
);
1279 } else if (S_ISREG(stat_buf
.st_mode
) || isFilePack
) {
1280 /* Hack because access always returns X_OK success for user root */
1281 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1282 if ((command
!= NULL
&& access(buffer
, R_OK
) == 0) ||
1283 (command
== NULL
&& access(buffer
, X_OK
) == 0 &&
1284 (stat_buf
.st_mode
& S_IXANY
))) {
1286 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1287 data
->name
= wstrdup(dentry
->d_name
);
1290 WMAddToArray(files
, data
);
1301 if (!WMGetArrayItemCount(dirs
) && !WMGetArrayItemCount(files
)) {
1307 WMSortArray(dirs
, myCompare
);
1308 WMSortArray(files
, myCompare
);
1310 menu
= wMenuCreate(scr
, M_(title
), False
);
1311 menu
->on_destroy
= removeShortcutsForMenu
;
1313 WM_ITERATE_ARRAY(dirs
, data
, iter
) {
1314 /* New directory. Use same OPEN_MENU command that was used
1315 * for the current directory. */
1316 length
= strlen(path
[data
->index
]) + strlen(data
->name
) + 6;
1320 length
+= strlen(command
) + 6;
1321 buffer
= malloc(length
);
1323 werror(_("out of memory while constructing directory menu %s"), path
[data
->index
]);
1329 strcat(buffer
, "-noext ");
1331 have_space
= strchr(path
[data
->index
], ' ') != NULL
|| strchr(data
->name
, ' ') != NULL
;
1334 strcat(buffer
, "\"");
1335 strcat(buffer
, path
[data
->index
]);
1337 strcat(buffer
, "/");
1338 strcat(buffer
, data
->name
);
1340 strcat(buffer
, "\"");
1342 strcat(buffer
, " WITH ");
1343 strcat(buffer
, command
);
1346 addMenuEntry(menu
, M_(data
->name
), NULL
, "OPEN_MENU", buffer
, path
[data
->index
]);
1354 WM_ITERATE_ARRAY(files
, data
, iter
) {
1355 /* executable: add as entry */
1356 length
= strlen(path
[data
->index
]) + strlen(data
->name
) + 6;
1358 length
+= strlen(command
);
1360 buffer
= malloc(length
);
1362 werror(_("out of memory while constructing directory menu %s"), path
[data
->index
]);
1366 have_space
= strchr(path
[data
->index
], ' ') != NULL
|| strchr(data
->name
, ' ') != NULL
;
1367 if (command
!= NULL
) {
1368 strcpy(buffer
, command
);
1369 strcat(buffer
, " ");
1371 strcat(buffer
, "\"");
1372 strcat(buffer
, path
[data
->index
]);
1377 strcat(buffer
, path
[data
->index
]);
1379 strcpy(buffer
, path
[data
->index
]);
1382 strcat(buffer
, "/");
1383 strcat(buffer
, data
->name
);
1385 strcat(buffer
, "\"");
1387 if (stripExtension
) {
1388 char *ptr
= strrchr(data
->name
, '.');
1389 if (ptr
&& ptr
!= data
->name
)
1392 addMenuEntry(menu
, M_(data
->name
), NULL
, "SHEXEC", buffer
, path
[data
->index
]);
1406 /************ Menu Configuration From WMRootMenu *************/
1408 static WMenu
*makeDefaultMenu(WScreen
* scr
)
1412 menu
= wMenuCreate(scr
, _("Commands"), True
);
1413 wMenuAddCallback(menu
, M_("XTerm"), execCommand
, "xterm");
1414 wMenuAddCallback(menu
, M_("rxvt"), execCommand
, "rxvt");
1415 wMenuAddCallback(menu
, _("Restart"), restartCommand
, NULL
);
1416 wMenuAddCallback(menu
, _("Exit..."), exitCommand
, NULL
);
1421 *----------------------------------------------------------------------
1423 * Reads root menu configuration from defaults database.
1425 *----------------------------------------------------------------------
1427 static WMenu
*configureMenu(WScreen
* scr
, WMPropList
* definition
, Bool includeGlobals
)
1432 WMPropList
*title
, *command
, *params
;
1435 if (WMIsPLString(definition
)) {
1436 struct stat stat_buf
;
1438 Bool menu_is_default
= False
;
1440 /* menu definition is a string. Probably a path, so parse the file */
1442 tmp
= wexpandpath(WMGetFromPLString(definition
));
1444 path
= getLocalizedMenuFile(tmp
);
1447 path
= wfindfile(DEF_CONFIG_PATHS
, tmp
);
1450 path
= wfindfile(DEF_CONFIG_PATHS
, DEF_MENU_FILE
);
1451 menu_is_default
= True
;
1455 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp
);
1460 if (stat(path
, &stat_buf
) < 0) {
1461 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path
);
1467 if (!scr
->root_menu
|| stat_buf
.st_mtime
> scr
->root_menu
->timestamp
1468 /* if the pointer in WMRootMenu has changed */
1469 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1471 if (menu_is_default
) {
1473 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1477 menu
= readMenuFile(scr
, path
);
1479 menu
->timestamp
= WMAX(stat_buf
.st_mtime
, WDRootMenu
->timestamp
);
1489 count
= WMGetPropListItemCount(definition
);
1493 elem
= WMGetFromPLArray(definition
, 0);
1494 if (!WMIsPLString(elem
)) {
1495 tmp
= WMGetPropListDescription(elem
, False
);
1496 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp
);
1500 mtitle
= WMGetFromPLString(elem
);
1502 menu
= wMenuCreate(scr
, M_(mtitle
), False
);
1503 menu
->on_destroy
= removeShortcutsForMenu
;
1505 #ifdef GLOBAL_SUBMENU_FILE
1506 if (includeGlobals
) {
1510 submenu
= readMenuFile(scr
, GLOBAL_SUBMENU_FILE
);
1513 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
, NULL
);
1514 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1519 for (i
= 1; i
< count
; i
++) {
1520 elem
= WMGetFromPLArray(definition
, i
);
1522 if (WMIsPLString(elem
)) {
1525 file
= WMGetFromPLString(elem
);
1529 if (!WMIsPLArray(elem
) || WMGetPropListItemCount(elem
) < 2)
1532 if (WMIsPLArray(WMGetFromPLArray(elem
, 1))) {
1537 submenu
= configureMenu(scr
, elem
, True
);
1539 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
, NULL
);
1540 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1544 WMPropList
*shortcut
;
1547 title
= WMGetFromPLArray(elem
, idx
++);
1548 shortcut
= WMGetFromPLArray(elem
, idx
++);
1549 if (strcmp(WMGetFromPLString(shortcut
), "SHORTCUT") == 0) {
1550 shortcut
= WMGetFromPLArray(elem
, idx
++);
1551 command
= WMGetFromPLArray(elem
, idx
++);
1556 params
= WMGetFromPLArray(elem
, idx
++);
1558 if (!title
|| !command
)
1561 addMenuEntry(menu
, M_(WMGetFromPLString(title
)),
1562 shortcut
? WMGetFromPLString(shortcut
) : NULL
,
1563 WMGetFromPLString(command
),
1564 params
? WMGetFromPLString(params
) : NULL
, "WMRootMenu");
1569 tmp
= WMGetPropListDescription(elem
, False
);
1570 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp
);
1578 *----------------------------------------------------------------------
1580 * Opens the root menu, parsing the menu configuration from the
1581 * defaults database.
1582 * If the menu is already mapped and is not sticked to the
1583 * root window, it will be unmapped.
1586 * The menu may be remade.
1589 * Construction of OPEN_MENU entries are delayed to the moment the
1591 *----------------------------------------------------------------------
1593 void OpenRootMenu(WScreen
* scr
, int x
, int y
, int keyboard
)
1596 WMPropList
*definition
;
1598 static WMPropList *domain=NULL;
1601 domain = WMCreatePLString("WMRootMenu");
1605 scr
->flags
.root_menu_changed_shortcuts
= 0;
1606 scr
->flags
.added_workspace_menu
= 0;
1607 scr
->flags
.added_windows_menu
= 0;
1609 if (scr
->root_menu
&& scr
->root_menu
->flags
.mapped
) {
1610 menu
= scr
->root_menu
;
1611 if (!menu
->flags
.buttoned
) {
1614 wRaiseFrame(menu
->frame
->core
);
1617 wMenuMapAt(menu
, 0, 0, True
);
1619 wMenuMapCopyAt(menu
, x
- menu
->frame
->core
->width
/ 2, y
);
1624 definition
= WDRootMenu
->dictionary
;
1627 definition = PLGetDomain(domain);
1630 if (WMIsPLArray(definition
)) {
1631 if (!scr
->root_menu
|| WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1632 menu
= configureMenu(scr
, definition
, True
);
1634 menu
->timestamp
= WDRootMenu
->timestamp
;
1639 menu
= configureMenu(scr
, definition
, True
);
1644 /* menu hasn't changed or could not be read */
1645 if (!scr
->root_menu
) {
1646 wMessageDialog(scr
, _("Error"),
1647 _("The applications menu could not be loaded. "
1648 "Look at the console output for a detailed "
1649 "description of the errors."), _("OK"), NULL
, NULL
);
1651 menu
= makeDefaultMenu(scr
);
1652 scr
->root_menu
= menu
;
1654 menu
= scr
->root_menu
;
1657 if (scr
->root_menu
) {
1658 wMenuDestroy(scr
->root_menu
, True
);
1660 scr
->root_menu
= menu
;
1665 if (keyboard
&& x
== 0 && y
== 0) {
1667 } else if (keyboard
&& x
== scr
->scr_width
/ 2 && y
== scr
->scr_height
/ 2) {
1668 newx
= x
- menu
->frame
->core
->width
/ 2;
1669 newy
= y
- menu
->frame
->core
->height
/ 2;
1671 newx
= x
- menu
->frame
->core
->width
/ 2;
1674 wMenuMapAt(menu
, newx
, newy
, keyboard
);
1677 if (scr
->flags
.root_menu_changed_shortcuts
)
1678 rebindKeygrabs(scr
);