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"
49 #include "workspace.h"
54 #include "xmodifier.h"
57 #include "switchmenu.h"
59 #include <WINGs/WUtil.h>
61 #define MAX_SHORTCUT_LENGTH 32
64 extern WDDomain
*WDRootMenu
;
65 extern Cursor wCursor
[WCUR_LAST
];
66 extern WPreferences wPreferences
;
68 static WMenu
*readMenuPipe(WScreen
* scr
, char **file_name
);
69 static WMenu
*readPLMenuPipe(WScreen
* scr
, char **file_name
);
70 static WMenu
*readMenuFile(WScreen
*scr
, const char *file_name
);
71 static WMenu
*readMenuDirectory(WScreen
* scr
, char *title
, char **file_name
, char *command
);
72 static WMenu
*configureMenu(WScreen
* scr
, WMPropList
* definition
, Bool includeGlobals
);
73 static void menu_parser_register_macros(WMenuParser parser
);
75 typedef struct Shortcut
{
76 struct Shortcut
*next
;
84 static Shortcut
*shortcutList
= NULL
;
90 * "Title" EXEC command_to_exec -params
92 * "Title" EXEC command_to_exec -params
94 * "Workspaces" WORKSPACE_MENU
95 * "Title" built_in_command
97 * "Quick Quit" EXIT QUICK
100 * Commands may be preceded by SHORTCUT key
104 * INFO_PANEL - shows the Info Panel
105 * LEGAL_PANEL - shows the Legal info panel
106 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
107 * REFRESH - forces the desktop to be repainted
108 * EXIT [QUICK] - exit the window manager [without confirmation]
109 * EXEC <program> - execute an external program
110 * SHEXEC <command> - execute a shell command
111 * WORKSPACE_MENU - places the workspace submenu
113 * RESTART [<window manager>] - restarts the window manager
114 * SHOW_ALL - unhide all windows on workspace
115 * HIDE_OTHERS - hides all windows excep the focused one
116 * OPEN_MENU file - read menu data from file which must be a valid menu file.
117 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
118 * - read menu data from directory(ies) and
119 * eventually precede each with a command.
120 * OPEN_MENU | command
121 * - opens command and uses its stdout to construct and insert
122 * the resulting menu in current position. The output of
123 * command must be a valid menu description.
124 * The space between '|' and command is optional.
125 * || will do the same, but will not cache the contents.
126 * OPEN_PLMENU | command
127 * - opens command and uses its stdout which must be in proplist
128 * fromat to construct and insert the resulting menu in current
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
144 * CLEAR_SESSION - clears any previous saved session. This will not have
145 * any effect if SaveSessionOnExit is True.
153 static void execCommand(WMenu
* menu
, WMenuEntry
* entry
)
157 cmdline
= ExpandOptions(menu
->frame
->screen_ptr
, (char *)entry
->clientdata
);
159 XGrabPointer(dpy
, menu
->frame
->screen_ptr
->root_win
, True
, 0,
160 GrabModeAsync
, GrabModeAsync
, None
, wCursor
[WCUR_WAIT
], CurrentTime
);
164 ExecuteShellCommand(menu
->frame
->screen_ptr
, cmdline
);
167 XUngrabPointer(dpy
, CurrentTime
);
171 static void exitCommand(WMenu
* menu
, WMenuEntry
* entry
)
173 static int inside
= 0;
176 /* prevent reentrant calls */
186 if ((long)entry
->clientdata
== M_QUICK
) {
189 int r
, oldSaveSessionFlag
;
191 oldSaveSessionFlag
= wPreferences
.save_session_on_exit
;
192 r
= wExitDialog(menu
->frame
->screen_ptr
, _("Exit"),
193 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL
);
195 if (r
== WAPRDefault
) {
197 } else if (r
== WAPRAlternate
) {
198 /* Don't modify the "save session on exit" flag if the
199 * user canceled the operation. */
200 wPreferences
.save_session_on_exit
= oldSaveSessionFlag
;
203 if (result
== R_EXIT
)
204 Shutdown(WSExitMode
);
211 static void shutdownCommand(WMenu
* menu
, WMenuEntry
* entry
)
213 static int inside
= 0;
216 /* prevent reentrant calls */
226 if ((long)entry
->clientdata
== M_QUICK
)
229 int r
, oldSaveSessionFlag
;
231 oldSaveSessionFlag
= wPreferences
.save_session_on_exit
;
233 r
= wExitDialog(menu
->frame
->screen_ptr
,
235 _("Kill Window System session?\n"
236 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL
);
237 if (r
== WAPRDefault
) {
239 } else if (r
== WAPRAlternate
) {
240 /* Don't modify the "save session on exit" flag if the
241 * user canceled the operation. */
242 wPreferences
.save_session_on_exit
= oldSaveSessionFlag
;
246 if (result
!= R_CANCEL
) {
247 Shutdown(WSKillMode
);
255 static void restartCommand(WMenu
* menu
, WMenuEntry
* entry
)
257 Shutdown(WSRestartPreparationMode
);
258 Restart((char *)entry
->clientdata
, False
);
262 static void refreshCommand(WMenu
* menu
, WMenuEntry
* entry
)
264 wRefreshDesktop(menu
->frame
->screen_ptr
);
267 static void arrangeIconsCommand(WMenu
* menu
, WMenuEntry
* entry
)
269 wArrangeIcons(menu
->frame
->screen_ptr
, True
);
272 static void showAllCommand(WMenu
* menu
, WMenuEntry
* entry
)
274 wShowAllWindows(menu
->frame
->screen_ptr
);
277 static void hideOthersCommand(WMenu
* menu
, WMenuEntry
* entry
)
279 wHideOtherApplications(menu
->frame
->screen_ptr
->focused_window
);
282 static void saveSessionCommand(WMenu
* menu
, WMenuEntry
* entry
)
284 if (!wPreferences
.save_session_on_exit
)
285 wSessionSaveState(menu
->frame
->screen_ptr
);
287 wScreenSaveState(menu
->frame
->screen_ptr
);
290 static void clearSessionCommand(WMenu
* menu
, WMenuEntry
* entry
)
292 wSessionClearState(menu
->frame
->screen_ptr
);
293 wScreenSaveState(menu
->frame
->screen_ptr
);
296 static void infoPanelCommand(WMenu
* menu
, WMenuEntry
* entry
)
298 wShowInfoPanel(menu
->frame
->screen_ptr
);
301 static void legalPanelCommand(WMenu
* menu
, WMenuEntry
* entry
)
303 wShowLegalPanel(menu
->frame
->screen_ptr
);
306 /********************************************************************/
308 static char *getLocalizedMenuFile(const char *menu
)
310 char *buffer
, *ptr
, *locale
;
316 len
= strlen(menu
) + strlen(Locale
) + 8;
317 buffer
= wmalloc(len
);
319 /* try menu.locale_name */
320 snprintf(buffer
, len
, "%s.%s", menu
, Locale
);
321 if (access(buffer
, F_OK
) == 0)
324 /* position of locale in our buffer */
325 locale
= buffer
+ strlen(menu
) + 1;
327 /* check if it is in the form aa_bb.encoding and check for aa_bb */
328 ptr
= strchr(locale
, '.');
331 if (access(buffer
, F_OK
) == 0)
335 /* now check for aa */
336 ptr
= strchr(locale
, '_');
339 if (access(buffer
, F_OK
) == 0)
348 Bool
wRootMenuPerformShortcut(XEvent
* event
)
350 WScreen
*scr
= wScreenForRootWindow(event
->xkey
.root
);
355 /* ignore CapsLock */
356 modifiers
= event
->xkey
.state
& ValidModMask
;
358 for (ptr
= shortcutList
; ptr
!= NULL
; ptr
= ptr
->next
) {
359 if (ptr
->keycode
== 0 || ptr
->menu
->menu
->screen_ptr
!= scr
)
362 if (ptr
->keycode
== event
->xkey
.keycode
&& ptr
->modifier
== modifiers
) {
363 (*ptr
->entry
->callback
) (ptr
->menu
, ptr
->entry
);
371 void wRootMenuBindShortcuts(Window window
)
377 if (ptr
->modifier
!= AnyModifier
) {
378 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
| LockMask
,
379 window
, True
, GrabModeAsync
, GrabModeAsync
);
381 wHackedGrabKey(ptr
->keycode
, ptr
->modifier
, window
, True
, GrabModeAsync
, GrabModeAsync
);
384 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
, window
, True
, GrabModeAsync
, GrabModeAsync
);
389 static void rebindKeygrabs(WScreen
* scr
)
393 wwin
= scr
->focused_window
;
395 while (wwin
!= NULL
) {
396 XUngrabKey(dpy
, AnyKey
, AnyModifier
, wwin
->frame
->core
->window
);
398 if (!WFLAGP(wwin
, no_bind_keys
)) {
399 wWindowSetKeyGrabs(wwin
);
405 static void removeShortcutsForMenu(WMenu
* menu
)
408 Shortcut
*newList
= NULL
;
411 while (ptr
!= NULL
) {
413 if (ptr
->menu
== menu
) {
421 shortcutList
= newList
;
422 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
425 static Bool
addShortcut(const char *file
, const char *shortcutDefinition
, WMenu
*menu
, WMenuEntry
*entry
)
430 char buf
[MAX_SHORTCUT_LENGTH
], *b
;
432 ptr
= wmalloc(sizeof(Shortcut
));
434 wstrlcpy(buf
, shortcutDefinition
, MAX_SHORTCUT_LENGTH
);
439 while ((k
= strchr(b
, '+')) != NULL
) {
443 mod
= wXModifierFromKey(b
);
445 wwarning(_("%s: invalid key modifier \"%s\""), file
, b
);
449 ptr
->modifier
|= mod
;
455 ksym
= XStringToKeysym(b
);
457 if (ksym
== NoSymbol
) {
458 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
459 file
, shortcutDefinition
, entry
->text
);
464 ptr
->keycode
= XKeysymToKeycode(dpy
, ksym
);
465 if (ptr
->keycode
== 0) {
466 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file
,
467 shortcutDefinition
, entry
->text
);
475 ptr
->next
= shortcutList
;
478 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
483 static char *next_token(char *line
, char **next
)
489 while (*line
== ' ' || *line
== '\t')
497 while (*tmp
!= 0 && *tmp
!= '"')
500 wwarning(_("%s: unmatched '\"' in menu file"), line
);
511 } while (*tmp
!= 0 && *tmp
!= ' ' && *tmp
!= '\t');
525 while (*tmp
== ' ' || *tmp
== '\t')
534 static void separateCommand(char *line
, char ***file
, char **command
)
536 char *token
, *tmp
= line
;
537 WMArray
*array
= WMCreateArray(4);
543 token
= next_token(tmp
, &tmp
);
545 if (strcmp(token
, "WITH") == 0) {
546 if (tmp
!= NULL
&& *tmp
!= 0)
547 *command
= wstrdup(tmp
);
549 wwarning(_("%s: missing command"), line
);
552 WMAddToArray(array
, token
);
554 } while (token
!= NULL
&& tmp
!= NULL
);
556 count
= WMGetArrayItemCount(array
);
558 *file
= wmalloc(sizeof(char *) * (count
+ 1));
559 (*file
)[count
] = NULL
;
560 for (i
= 0; i
< count
; i
++) {
561 (*file
)[i
] = WMGetFromArray(array
, i
);
567 static WMenu
*constructPLMenu(WScreen
*screen
, const char *path
)
569 WMPropList
*pl
= NULL
;
575 pl
= WMReadPropListFromFile(path
);
579 menu
= configureMenu(screen
, pl
, False
);
581 WMReleasePropList(pl
);
586 menu
->on_destroy
= removeShortcutsForMenu
;
592 static void constructMenu(WMenu
* menu
, WMenuEntry
* entry
)
595 struct stat stat_buf
;
602 separateCommand((char *)entry
->clientdata
, &path
, &cmd
);
603 if (path
== NULL
|| *path
== NULL
|| **path
== 0) {
604 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry
->clientdata
);
610 if (path
[0][0] == '|') {
613 if (!menu
->cascades
[entry
->cascade
] || menu
->cascades
[entry
->cascade
]->timestamp
== 0) {
616 submenu
= readMenuPipe(menu
->frame
->screen_ptr
, path
);
618 if (submenu
!= NULL
) {
619 if (path
[0][1] == '|')
620 submenu
->timestamp
= 0;
622 submenu
->timestamp
= 1; /* there's no automatic reloading */
630 /* try interpreting path as a proplist file */
631 submenu
= constructPLMenu(menu
->frame
->screen_ptr
, path
[0]);
632 /* if unsuccessful, try it as an old-style file */
636 while (path
[i
] != NULL
) {
639 if (strcmp(path
[i
], "-noext") == 0) {
644 tmp
= wexpandpath(path
[i
]);
646 lpath
= getLocalizedMenuFile(tmp
);
655 if (stat(path
[i
], &stat_buf
) == 0) {
656 if (last
< stat_buf
.st_mtime
)
657 last
= stat_buf
.st_mtime
;
661 werror(_("%s:could not stat menu"), path
[i
]);
669 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry
->clientdata
);
672 stat(path
[first
], &stat_buf
);
673 if (!menu
->cascades
[entry
->cascade
]
674 || menu
->cascades
[entry
->cascade
]->timestamp
< last
) {
676 if (S_ISDIR(stat_buf
.st_mode
)) {
678 submenu
= readMenuDirectory(menu
->frame
->screen_ptr
, entry
->text
, path
, cmd
);
680 submenu
->timestamp
= last
;
681 } else if (S_ISREG(stat_buf
.st_mode
)) {
685 wwarning(_("too many parameters in OPEN_MENU: %s"),
686 (char *)entry
->clientdata
);
688 submenu
= readMenuFile(menu
->frame
->screen_ptr
, path
[first
]);
690 submenu
->timestamp
= stat_buf
.st_mtime
;
701 wMenuEntryRemoveCascade(menu
, entry
);
702 wMenuEntrySetCascade(menu
, entry
, submenu
);
707 while (path
[i
] != NULL
)
714 static void constructPLMenuFromPipe(WMenu
* menu
, WMenuEntry
* entry
)
716 WMenu
*submenu
= NULL
;
721 separateCommand((char *)entry
->clientdata
, &path
, &cmd
);
722 if (path
== NULL
|| *path
== NULL
|| **path
== 0) {
723 wwarning(_("invalid OPEN_PLMENU specification: %s"),
724 (char *)entry
->clientdata
);
730 if (path
[0][0] == '|') {
733 if (!menu
->cascades
[entry
->cascade
]
734 || menu
->cascades
[entry
->cascade
]->timestamp
== 0) {
736 submenu
= readPLMenuPipe(menu
->frame
->screen_ptr
, path
);
738 if (submenu
!= NULL
) {
739 if (path
[0][1] == '|')
740 submenu
->timestamp
= 0;
742 submenu
->timestamp
= 1; /* there's no automatic reloading */
748 wMenuEntryRemoveCascade(menu
, entry
);
749 wMenuEntrySetCascade(menu
, entry
, submenu
);
753 while (path
[i
] != NULL
)
761 static void cleanupWorkspaceMenu(WMenu
* menu
)
763 if (menu
->frame
->screen_ptr
->workspace_menu
== menu
)
764 menu
->frame
->screen_ptr
->workspace_menu
= NULL
;
767 static WMenuEntry
*addWorkspaceMenu(WScreen
* scr
, WMenu
* menu
, char *title
)
772 if (scr
->flags
.added_workspace_menu
) {
774 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
777 scr
->flags
.added_workspace_menu
= 1;
779 wsmenu
= wWorkspaceMenuMake(scr
, True
);
780 wsmenu
->on_destroy
= cleanupWorkspaceMenu
;
782 scr
->workspace_menu
= wsmenu
;
783 entry
= wMenuAddCallback(menu
, title
, NULL
, NULL
);
784 wMenuEntrySetCascade(menu
, entry
, wsmenu
);
786 wWorkspaceMenuUpdate(scr
, wsmenu
);
791 static void cleanupWindowsMenu(WMenu
* menu
)
793 if (menu
->frame
->screen_ptr
->switch_menu
== menu
)
794 menu
->frame
->screen_ptr
->switch_menu
= NULL
;
797 static WMenuEntry
*addWindowsMenu(WScreen
* scr
, WMenu
* menu
, char *title
)
803 if (scr
->flags
.added_windows_menu
) {
805 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
808 scr
->flags
.added_windows_menu
= 1;
810 wwmenu
= wMenuCreate(scr
, _("Window List"), False
);
811 wwmenu
->on_destroy
= cleanupWindowsMenu
;
812 scr
->switch_menu
= wwmenu
;
813 wwin
= scr
->focused_window
;
815 UpdateSwitchMenu(scr
, wwin
, ACTION_ADD
);
819 entry
= wMenuAddCallback(menu
, title
, NULL
, NULL
);
820 wMenuEntrySetCascade(menu
, entry
, wwmenu
);
825 static WMenuEntry
*addMenuEntry(WMenu
* menu
, char *title
, char *shortcut
, char *command
,
826 char *params
, const char *file_name
)
829 WMenuEntry
*entry
= NULL
;
830 Bool shortcutOk
= False
;
834 scr
= menu
->frame
->screen_ptr
;
835 if (strcmp(command
, "OPEN_MENU") == 0) {
837 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name
, command
);
842 path
= wfindfile(DEF_CONFIG_PATHS
, params
);
844 path
= wstrdup(params
);
846 dummy
= wMenuCreate(scr
, title
, False
);
847 dummy
->on_destroy
= removeShortcutsForMenu
;
848 entry
= wMenuAddCallback(menu
, title
, constructMenu
, path
);
849 entry
->free_cdata
= wfree
;
850 wMenuEntrySetCascade(menu
, entry
, dummy
);
852 } else if (strcmp(command
, "OPEN_PLMENU") == 0) {
854 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name
, command
);
859 path
= wfindfile(DEF_CONFIG_PATHS
, params
);
861 path
= wstrdup(params
);
863 dummy
= wMenuCreate(scr
, title
, False
);
864 dummy
->on_destroy
= removeShortcutsForMenu
;
865 entry
= wMenuAddCallback(menu
, title
, constructPLMenuFromPipe
, path
);
866 entry
->free_cdata
= wfree
;
867 wMenuEntrySetCascade(menu
, entry
, dummy
);
869 } else if (strcmp(command
, "EXEC") == 0) {
871 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name
, command
);
873 entry
= wMenuAddCallback(menu
, title
, execCommand
, wstrconcat("exec ", params
));
874 entry
->free_cdata
= wfree
;
877 } else if (strcmp(command
, "SHEXEC") == 0) {
879 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name
, command
);
881 entry
= wMenuAddCallback(menu
, title
, execCommand
, wstrdup(params
));
882 entry
->free_cdata
= wfree
;
885 } else if (strcmp(command
, "EXIT") == 0) {
887 if (params
&& strcmp(params
, "QUICK") == 0)
888 entry
= wMenuAddCallback(menu
, title
, exitCommand
, (void *)M_QUICK
);
890 entry
= wMenuAddCallback(menu
, title
, exitCommand
, NULL
);
893 } else if (strcmp(command
, "SHUTDOWN") == 0) {
895 if (params
&& strcmp(params
, "QUICK") == 0)
896 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
, (void *)M_QUICK
);
898 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
, NULL
);
901 } else if (strcmp(command
, "REFRESH") == 0) {
902 entry
= wMenuAddCallback(menu
, title
, refreshCommand
, NULL
);
905 } else if (strcmp(command
, "WORKSPACE_MENU") == 0) {
906 entry
= addWorkspaceMenu(scr
, menu
, title
);
909 } else if (strcmp(command
, "WINDOWS_MENU") == 0) {
910 entry
= addWindowsMenu(scr
, menu
, title
);
913 } else if (strcmp(command
, "ARRANGE_ICONS") == 0) {
914 entry
= wMenuAddCallback(menu
, title
, arrangeIconsCommand
, NULL
);
917 } else if (strcmp(command
, "HIDE_OTHERS") == 0) {
918 entry
= wMenuAddCallback(menu
, title
, hideOthersCommand
, NULL
);
921 } else if (strcmp(command
, "SHOW_ALL") == 0) {
922 entry
= wMenuAddCallback(menu
, title
, showAllCommand
, NULL
);
925 } else if (strcmp(command
, "RESTART") == 0) {
926 entry
= wMenuAddCallback(menu
, title
, restartCommand
, params
? wstrdup(params
) : NULL
);
927 entry
->free_cdata
= wfree
;
929 } else if (strcmp(command
, "SAVE_SESSION") == 0) {
930 entry
= wMenuAddCallback(menu
, title
, saveSessionCommand
, NULL
);
933 } else if (strcmp(command
, "CLEAR_SESSION") == 0) {
934 entry
= wMenuAddCallback(menu
, title
, clearSessionCommand
, NULL
);
936 } else if (strcmp(command
, "INFO_PANEL") == 0) {
937 entry
= wMenuAddCallback(menu
, title
, infoPanelCommand
, NULL
);
939 } else if (strcmp(command
, "LEGAL_PANEL") == 0) {
940 entry
= wMenuAddCallback(menu
, title
, legalPanelCommand
, NULL
);
943 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name
, command
);
948 if (shortcut
&& entry
) {
950 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name
, title
);
952 if (addShortcut(file_name
, shortcut
, menu
, entry
)) {
954 entry
->rtext
= GetShortcutString(shortcut
);
956 entry->rtext = wstrdup(shortcut);
965 /******************* Menu Configuration From File *******************/
967 static void freeline(char *title
, char *command
, char *parameter
, char *shortcut
)
975 static WMenu
*parseCascade(WScreen
* scr
, WMenu
* menu
, WMenuParser parser
)
977 char *command
, *params
, *shortcut
, *title
;
979 while (WMenuParserGetLine(parser
, &title
, &command
, ¶ms
, &shortcut
)) {
981 if (command
== NULL
|| !command
[0]) {
982 WMenuParserError(parser
, _("missing command in menu config") );
983 freeline(title
, command
, params
, shortcut
);
987 if (strcasecmp(command
, "MENU") == 0) {
992 cascade
= wMenuCreate(scr
, M_(title
), False
);
993 cascade
->on_destroy
= removeShortcutsForMenu
;
994 if (!parseCascade(scr
, cascade
, parser
)) {
995 wMenuDestroy(cascade
, True
);
997 wMenuEntrySetCascade(menu
, wMenuAddCallback(menu
, M_(title
), NULL
, NULL
), cascade
);
999 } else if (strcasecmp(command
, "END") == 0) {
1001 freeline(title
, command
, params
, shortcut
);
1005 addMenuEntry(menu
, M_(title
), shortcut
, command
, params
, WMenuParserGetFilename(parser
));
1007 freeline(title
, command
, params
, shortcut
);
1010 WMenuParserError(parser
, _("syntax error in menu file: END declaration missing") );
1016 static WMenu
*readMenuFile(WScreen
*scr
, const char *file_name
)
1021 char *command
, *params
, *shortcut
, *title
;
1023 file
= fopen(file_name
, "rb");
1025 werror(_("%s:could not open menu file"), file_name
);
1028 parser
= WMenuParserCreate(file_name
, file
, DEF_CONFIG_PATHS
);
1029 menu_parser_register_macros(parser
);
1031 while (WMenuParserGetLine(parser
, &title
, &command
, ¶ms
, &shortcut
)) {
1033 if (command
== NULL
|| !command
[0]) {
1034 WMenuParserError(parser
, _("missing command in menu config") );
1035 freeline(title
, command
, params
, shortcut
);
1038 if (strcasecmp(command
, "MENU") == 0) {
1039 menu
= wMenuCreate(scr
, M_(title
), True
);
1040 menu
->on_destroy
= removeShortcutsForMenu
;
1041 if (!parseCascade(scr
, menu
, parser
)) {
1042 wMenuDestroy(menu
, True
);
1045 freeline(title
, command
, params
, shortcut
);
1048 WMenuParserError(parser
, _("invalid menu file, MENU command is missing") );
1049 freeline(title
, command
, params
, shortcut
);
1052 freeline(title
, command
, params
, shortcut
);
1055 WMenuParserDelete(parser
);
1061 /************ Menu Configuration From Pipe *************/
1062 static WMenu
*readPLMenuPipe(WScreen
* scr
, char **file_name
)
1064 WMPropList
*plist
= NULL
;
1067 char flat_file
[MAXLINE
];
1070 flat_file
[0] = '\0';
1072 for (i
= 0; file_name
[i
] != NULL
; i
++) {
1073 strcat(flat_file
, file_name
[i
]);
1074 strcat(flat_file
, " ");
1076 filename
= flat_file
+ (flat_file
[1] == '|' ? 2 : 1);
1078 plist
= WMReadPropListFromPipe(filename
);
1083 menu
= configureMenu(scr
, plist
, False
);
1085 WMReleasePropList(plist
);
1090 menu
->on_destroy
= removeShortcutsForMenu
;
1094 static WMenu
*readMenuPipe(WScreen
* scr
, char **file_name
)
1099 char *command
, *params
, *shortcut
, *title
;
1101 char flat_file
[MAXLINE
];
1104 flat_file
[0] = '\0';
1106 for (i
= 0; file_name
[i
] != NULL
; i
++) {
1107 strcat(flat_file
, file_name
[i
]);
1108 strcat(flat_file
, " ");
1110 filename
= flat_file
+ (flat_file
[1] == '|' ? 2 : 1);
1112 file
= popen(filename
, "r");
1114 werror(_("%s:could not open menu file"), filename
);
1117 parser
= WMenuParserCreate(flat_file
, file
, DEF_CONFIG_PATHS
);
1118 menu_parser_register_macros(parser
);
1120 while (WMenuParserGetLine(parser
, &title
, &command
, ¶ms
, &shortcut
)) {
1122 if (command
== NULL
|| !command
[0]) {
1123 WMenuParserError(parser
, _("missing command in menu config") );
1124 freeline(title
, command
, params
, shortcut
);
1127 if (strcasecmp(command
, "MENU") == 0) {
1128 menu
= wMenuCreate(scr
, M_(title
), True
);
1129 menu
->on_destroy
= removeShortcutsForMenu
;
1130 if (!parseCascade(scr
, menu
, parser
)) {
1131 wMenuDestroy(menu
, True
);
1134 freeline(title
, command
, params
, shortcut
);
1137 WMenuParserError(parser
, _("no title given for the root menu") );
1138 freeline(title
, command
, params
, shortcut
);
1142 freeline(title
, command
, params
, shortcut
);
1145 WMenuParserDelete(parser
);
1156 static int myCompare(const void *d1
, const void *d2
)
1158 dir_data
*p1
= *(dir_data
**) d1
;
1159 dir_data
*p2
= *(dir_data
**) d2
;
1161 return strcmp(p1
->name
, p2
->name
);
1164 /***** Preset some macro for file parser *****/
1165 static void menu_parser_register_macros(WMenuParser parser
)
1170 // Used to return CPP verion, now returns wmaker's version
1171 WMenuParserRegisterSimpleMacro(parser
, "__VERSION__", VERSION
);
1173 // All macros below were historically defined by WindowMaker
1174 visual
= DefaultVisual(dpy
, DefaultScreen(dpy
));
1175 snprintf(buf
, sizeof(buf
), "%d", visual
->class);
1176 WMenuParserRegisterSimpleMacro(parser
, "VISUAL", buf
);
1178 snprintf(buf
, sizeof(buf
), "%d", DefaultDepth(dpy
, DefaultScreen(dpy
)) );
1179 WMenuParserRegisterSimpleMacro(parser
, "DEPTH", buf
);
1181 snprintf(buf
, sizeof(buf
), "%d", WidthOfScreen(DefaultScreenOfDisplay(dpy
)) );
1182 WMenuParserRegisterSimpleMacro(parser
, "SCR_WIDTH", buf
);
1184 snprintf(buf
, sizeof(buf
), "%d", HeightOfScreen(DefaultScreenOfDisplay(dpy
)) );
1185 WMenuParserRegisterSimpleMacro(parser
, "SCR_HEIGHT", buf
);
1187 WMenuParserRegisterSimpleMacro(parser
, "DISPLAY", XDisplayName(DisplayString(dpy
)) );
1189 WMenuParserRegisterSimpleMacro(parser
, "WM_VERSION", "\"" VERSION
"\"");
1192 /************ Menu Configuration From Directory *************/
1194 static Bool
isFilePackage(const char *file
)
1198 /* check if the extension indicates this file is a
1199 * file package. For now, only recognize .themed */
1203 if (l
> 7 && strcmp(&(file
[l
- 7]), ".themed") == 0) {
1210 static WMenu
*readMenuDirectory(WScreen
* scr
, char *title
, char **path
, char *command
)
1213 struct dirent
*dentry
;
1214 struct stat stat_buf
;
1217 WMArray
*dirs
= NULL
, *files
= NULL
;
1218 WMArrayIterator iter
;
1219 int length
, i
, have_space
= 0;
1221 int stripExtension
= 0;
1223 dirs
= WMCreateArray(16);
1224 files
= WMCreateArray(16);
1227 while (path
[i
] != NULL
) {
1228 if (strcmp(path
[i
], "-noext") == 0) {
1234 dir
= opendir(path
[i
]);
1240 while ((dentry
= readdir(dir
))) {
1242 if (strcmp(dentry
->d_name
, ".") == 0 || strcmp(dentry
->d_name
, "..") == 0)
1245 if (dentry
->d_name
[0] == '.')
1248 buffer
= malloc(strlen(path
[i
]) + strlen(dentry
->d_name
) + 4);
1250 werror(_("out of memory while constructing directory menu %s"), path
[i
]);
1254 strcpy(buffer
, path
[i
]);
1255 strcat(buffer
, "/");
1256 strcat(buffer
, dentry
->d_name
);
1258 if (stat(buffer
, &stat_buf
) != 0) {
1259 werror(_("%s:could not stat file \"%s\" in menu directory"),
1260 path
[i
], dentry
->d_name
);
1262 Bool isFilePack
= False
;
1265 if (S_ISDIR(stat_buf
.st_mode
)
1266 && !(isFilePack
= isFilePackage(dentry
->d_name
))) {
1268 /* access always returns success for user root */
1269 if (access(buffer
, X_OK
) == 0) {
1270 /* Directory is accesible. Add to directory list */
1272 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1273 data
->name
= wstrdup(dentry
->d_name
);
1276 WMAddToArray(dirs
, data
);
1278 } else if (S_ISREG(stat_buf
.st_mode
) || isFilePack
) {
1279 /* Hack because access always returns X_OK success for user root */
1280 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1281 if ((command
!= NULL
&& access(buffer
, R_OK
) == 0) ||
1282 (command
== NULL
&& access(buffer
, X_OK
) == 0 &&
1283 (stat_buf
.st_mode
& S_IXANY
))) {
1285 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1286 data
->name
= wstrdup(dentry
->d_name
);
1289 WMAddToArray(files
, data
);
1300 if (!WMGetArrayItemCount(dirs
) && !WMGetArrayItemCount(files
)) {
1306 WMSortArray(dirs
, myCompare
);
1307 WMSortArray(files
, myCompare
);
1309 menu
= wMenuCreate(scr
, M_(title
), False
);
1310 menu
->on_destroy
= removeShortcutsForMenu
;
1312 WM_ITERATE_ARRAY(dirs
, data
, iter
) {
1313 /* New directory. Use same OPEN_MENU command that was used
1314 * for the current directory. */
1315 length
= strlen(path
[data
->index
]) + strlen(data
->name
) + 6;
1319 length
+= strlen(command
) + 6;
1320 buffer
= malloc(length
);
1322 werror(_("out of memory while constructing directory menu %s"), path
[data
->index
]);
1328 strcat(buffer
, "-noext ");
1330 have_space
= strchr(path
[data
->index
], ' ') != NULL
|| strchr(data
->name
, ' ') != NULL
;
1333 strcat(buffer
, "\"");
1334 strcat(buffer
, path
[data
->index
]);
1336 strcat(buffer
, "/");
1337 strcat(buffer
, data
->name
);
1339 strcat(buffer
, "\"");
1341 strcat(buffer
, " WITH ");
1342 strcat(buffer
, command
);
1345 addMenuEntry(menu
, M_(data
->name
), NULL
, "OPEN_MENU", buffer
, path
[data
->index
]);
1353 WM_ITERATE_ARRAY(files
, data
, iter
) {
1354 /* executable: add as entry */
1355 length
= strlen(path
[data
->index
]) + strlen(data
->name
) + 6;
1357 length
+= strlen(command
);
1359 buffer
= malloc(length
);
1361 werror(_("out of memory while constructing directory menu %s"), path
[data
->index
]);
1365 have_space
= strchr(path
[data
->index
], ' ') != NULL
|| strchr(data
->name
, ' ') != NULL
;
1366 if (command
!= NULL
) {
1367 strcpy(buffer
, command
);
1368 strcat(buffer
, " ");
1370 strcat(buffer
, "\"");
1371 strcat(buffer
, path
[data
->index
]);
1376 strcat(buffer
, path
[data
->index
]);
1378 strcpy(buffer
, path
[data
->index
]);
1381 strcat(buffer
, "/");
1382 strcat(buffer
, data
->name
);
1384 strcat(buffer
, "\"");
1386 if (stripExtension
) {
1387 char *ptr
= strrchr(data
->name
, '.');
1388 if (ptr
&& ptr
!= data
->name
)
1391 addMenuEntry(menu
, M_(data
->name
), NULL
, "SHEXEC", buffer
, path
[data
->index
]);
1405 /************ Menu Configuration From WMRootMenu *************/
1407 static WMenu
*makeDefaultMenu(WScreen
* scr
)
1411 menu
= wMenuCreate(scr
, _("Commands"), True
);
1412 wMenuAddCallback(menu
, M_("XTerm"), execCommand
, "xterm");
1413 wMenuAddCallback(menu
, M_("rxvt"), execCommand
, "rxvt");
1414 wMenuAddCallback(menu
, _("Restart"), restartCommand
, NULL
);
1415 wMenuAddCallback(menu
, _("Exit..."), exitCommand
, NULL
);
1420 *----------------------------------------------------------------------
1422 * Reads root menu configuration from defaults database.
1424 *----------------------------------------------------------------------
1426 static WMenu
*configureMenu(WScreen
* scr
, WMPropList
* definition
, Bool includeGlobals
)
1431 WMPropList
*title
, *command
, *params
;
1434 if (WMIsPLString(definition
)) {
1435 struct stat stat_buf
;
1437 Bool menu_is_default
= False
;
1439 /* menu definition is a string. Probably a path, so parse the file */
1441 tmp
= wexpandpath(WMGetFromPLString(definition
));
1443 path
= getLocalizedMenuFile(tmp
);
1446 path
= wfindfile(DEF_CONFIG_PATHS
, tmp
);
1449 path
= wfindfile(DEF_CONFIG_PATHS
, DEF_MENU_FILE
);
1450 menu_is_default
= True
;
1454 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp
);
1459 if (stat(path
, &stat_buf
) < 0) {
1460 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path
);
1466 if (!scr
->root_menu
|| stat_buf
.st_mtime
> scr
->root_menu
->timestamp
1467 /* if the pointer in WMRootMenu has changed */
1468 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1470 if (menu_is_default
) {
1472 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1476 menu
= readMenuFile(scr
, path
);
1478 menu
->timestamp
= WMAX(stat_buf
.st_mtime
, WDRootMenu
->timestamp
);
1488 count
= WMGetPropListItemCount(definition
);
1492 elem
= WMGetFromPLArray(definition
, 0);
1493 if (!WMIsPLString(elem
)) {
1494 tmp
= WMGetPropListDescription(elem
, False
);
1495 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp
);
1499 mtitle
= WMGetFromPLString(elem
);
1501 menu
= wMenuCreate(scr
, M_(mtitle
), False
);
1502 menu
->on_destroy
= removeShortcutsForMenu
;
1504 #ifdef GLOBAL_SUBMENU_FILE
1505 if (includeGlobals
) {
1509 submenu
= readMenuFile(scr
, GLOBAL_SUBMENU_FILE
);
1512 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
, NULL
);
1513 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1518 for (i
= 1; i
< count
; i
++) {
1519 elem
= WMGetFromPLArray(definition
, i
);
1521 if (WMIsPLString(elem
)) {
1524 file
= WMGetFromPLString(elem
);
1528 if (!WMIsPLArray(elem
) || WMGetPropListItemCount(elem
) < 2)
1531 if (WMIsPLArray(WMGetFromPLArray(elem
, 1))) {
1536 submenu
= configureMenu(scr
, elem
, True
);
1538 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
, NULL
);
1539 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1543 WMPropList
*shortcut
;
1546 title
= WMGetFromPLArray(elem
, idx
++);
1547 shortcut
= WMGetFromPLArray(elem
, idx
++);
1548 if (strcmp(WMGetFromPLString(shortcut
), "SHORTCUT") == 0) {
1549 shortcut
= WMGetFromPLArray(elem
, idx
++);
1550 command
= WMGetFromPLArray(elem
, idx
++);
1555 params
= WMGetFromPLArray(elem
, idx
++);
1557 if (!title
|| !command
)
1560 addMenuEntry(menu
, M_(WMGetFromPLString(title
)),
1561 shortcut
? WMGetFromPLString(shortcut
) : NULL
,
1562 WMGetFromPLString(command
),
1563 params
? WMGetFromPLString(params
) : NULL
, "WMRootMenu");
1568 tmp
= WMGetPropListDescription(elem
, False
);
1569 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp
);
1577 *----------------------------------------------------------------------
1579 * Opens the root menu, parsing the menu configuration from the
1580 * defaults database.
1581 * If the menu is already mapped and is not sticked to the
1582 * root window, it will be unmapped.
1585 * The menu may be remade.
1588 * Construction of OPEN_MENU entries are delayed to the moment the
1590 *----------------------------------------------------------------------
1592 void OpenRootMenu(WScreen
* scr
, int x
, int y
, int keyboard
)
1595 WMPropList
*definition
;
1597 static WMPropList *domain=NULL;
1600 domain = WMCreatePLString("WMRootMenu");
1604 scr
->flags
.root_menu_changed_shortcuts
= 0;
1605 scr
->flags
.added_workspace_menu
= 0;
1606 scr
->flags
.added_windows_menu
= 0;
1608 if (scr
->root_menu
&& scr
->root_menu
->flags
.mapped
) {
1609 menu
= scr
->root_menu
;
1610 if (!menu
->flags
.buttoned
) {
1613 wRaiseFrame(menu
->frame
->core
);
1616 wMenuMapAt(menu
, 0, 0, True
);
1618 wMenuMapCopyAt(menu
, x
- menu
->frame
->core
->width
/ 2, y
);
1623 definition
= WDRootMenu
->dictionary
;
1626 definition = PLGetDomain(domain);
1629 if (WMIsPLArray(definition
)) {
1630 if (!scr
->root_menu
|| WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1631 menu
= configureMenu(scr
, definition
, True
);
1633 menu
->timestamp
= WDRootMenu
->timestamp
;
1638 menu
= configureMenu(scr
, definition
, True
);
1643 /* menu hasn't changed or could not be read */
1644 if (!scr
->root_menu
) {
1645 wMessageDialog(scr
, _("Error"),
1646 _("The applications menu could not be loaded. "
1647 "Look at the console output for a detailed "
1648 "description of the errors."), _("OK"), NULL
, NULL
);
1650 menu
= makeDefaultMenu(scr
);
1651 scr
->root_menu
= menu
;
1653 menu
= scr
->root_menu
;
1656 if (scr
->root_menu
) {
1657 wMenuDestroy(scr
->root_menu
, True
);
1659 scr
->root_menu
= menu
;
1664 if (keyboard
&& x
== 0 && y
== 0) {
1666 } else if (keyboard
&& x
== scr
->scr_width
/ 2 && y
== scr
->scr_height
/ 2) {
1667 newx
= x
- menu
->frame
->core
->width
/ 2;
1668 newy
= y
- menu
->frame
->core
->height
/ 2;
1670 newx
= x
- menu
->frame
->core
->width
/ 2;
1673 wMenuMapAt(menu
, newx
, newy
, keyboard
);
1676 if (scr
->flags
.root_menu_changed_shortcuts
)
1677 rebindKeygrabs(scr
);