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,
33 #include <sys/types.h>
40 #include <X11/Xutil.h>
41 #include <X11/Xatom.h>
43 #include "WindowMaker.h"
50 #include "workspace.h"
54 #include "xmodifier.h"
56 #include <WINGs/WUtil.h>
58 #define MAX_SHORTCUT_LENGTH 32
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
, char *command
);
76 typedef struct Shortcut
{
77 struct Shortcut
*next
;
85 static Shortcut
*shortcutList
= NULL
;
91 * "Title" EXEC command_to_exec -params
93 * "Title" EXEC command_to_exec -params
95 * "Workspaces" WORKSPACE_MENU
96 * "Title" built_in_command
98 * "Quick Quit" EXIT QUICK
101 * Commands may be preceded by SHORTCUT key
105 * INFO_PANEL - shows the Info Panel
106 * LEGAL_PANEL - shows the Legal info panel
107 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
108 * REFRESH - forces the desktop to be repainted
109 * EXIT [QUICK] - exit the window manager [without confirmation]
110 * EXEC <program> - execute an external program
111 * SHEXEC <command> - execute a shell command
112 * WORKSPACE_MENU - places the workspace submenu
114 * RESTART [<window manager>] - restarts the window manager
115 * SHOW_ALL - unhide all windows on workspace
116 * HIDE_OTHERS - hides all windows excep the focused one
117 * OPEN_MENU file - read menu data from file which must be a valid menu file.
118 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
119 * - read menu data from directory(ies) and
120 * eventually precede each with a command.
121 * OPEN_MENU | command
122 * - opens command and uses its stdout to construct and insert
123 * the resulting menu in current position. The output of
124 * command must be a valid menu description.
125 * The space between '|' and command is optional.
126 * || will do the same, but will not cache the contents.
127 * SAVE_SESSION - saves the current state of the desktop, which include
128 * all running applications, all their hints (geometry,
129 * position on screen, workspace they live on, the dock
130 * or clip from where they were launched, and
131 * if minimized, shaded or hidden. Also saves the current
132 * workspace the user is on. All will be restored on every
133 * start of windowmaker until another SAVE_SESSION or
134 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
135 * WindowMaker domain file, then saving is automatically
136 * done on every windowmaker exit, overwriting any
137 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
139 * CLEAR_SESSION - clears any previous saved session. This will not have
140 * any effect if SaveSessionOnExit is True.
148 static void execCommand(WMenu
* menu
, WMenuEntry
* entry
)
152 cmdline
= ExpandOptions(menu
->frame
->screen_ptr
, (char *)entry
->clientdata
);
154 XGrabPointer(dpy
, menu
->frame
->screen_ptr
->root_win
, True
, 0,
155 GrabModeAsync
, GrabModeAsync
, None
, wCursor
[WCUR_WAIT
], CurrentTime
);
159 ExecuteShellCommand(menu
->frame
->screen_ptr
, cmdline
);
162 XUngrabPointer(dpy
, CurrentTime
);
166 static void exitCommand(WMenu
* menu
, WMenuEntry
* entry
)
168 static int inside
= 0;
171 /* prevent reentrant calls */
181 if ((long)entry
->clientdata
== M_QUICK
) {
184 int r
, oldSaveSessionFlag
;
186 oldSaveSessionFlag
= wPreferences
.save_session_on_exit
;
187 r
= wExitDialog(menu
->frame
->screen_ptr
, _("Exit"),
188 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL
);
190 if (r
== WAPRDefault
) {
192 } else if (r
== WAPRAlternate
) {
193 /* Don't modify the "save session on exit" flag if the
194 * user canceled the operation. */
195 wPreferences
.save_session_on_exit
= oldSaveSessionFlag
;
198 if (result
== R_EXIT
) {
200 printf("Exiting WindowMaker.\n");
202 Shutdown(WSExitMode
);
209 static void shutdownCommand(WMenu
* menu
, WMenuEntry
* entry
)
211 static int inside
= 0;
214 /* prevent reentrant calls */
224 if ((long)entry
->clientdata
== M_QUICK
)
228 if (wSessionIsManaged()) {
231 r
= wMessageDialog(menu
->frame
->screen_ptr
,
232 _("Close X session"),
233 _("Close Window System session?\n"
234 "Kill might close applications with unsaved data."),
235 _("Close"), _("Kill"), _("Cancel"));
236 if (r
== WAPRDefault
)
238 else if (r
== WAPRAlternate
)
243 int r
, oldSaveSessionFlag
;
245 oldSaveSessionFlag
= wPreferences
.save_session_on_exit
;
247 r
= wExitDialog(menu
->frame
->screen_ptr
,
249 _("Kill Window System session?\n"
250 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL
);
251 if (r
== WAPRDefault
) {
253 } else if (r
== WAPRAlternate
) {
254 /* Don't modify the "save session on exit" flag if the
255 * user canceled the operation. */
256 wPreferences
.save_session_on_exit
= oldSaveSessionFlag
;
261 if (result
!= R_CANCEL
) {
263 if (result
== R_CLOSE
) {
264 Shutdown(WSLogoutMode
);
266 #endif /* XSMP_ENABLED */
268 Shutdown(WSKillMode
);
277 static void restartCommand(WMenu
* menu
, WMenuEntry
* entry
)
279 Shutdown(WSRestartPreparationMode
);
280 Restart((char *)entry
->clientdata
, False
);
284 static void refreshCommand(WMenu
* menu
, WMenuEntry
* entry
)
286 wRefreshDesktop(menu
->frame
->screen_ptr
);
289 static void arrangeIconsCommand(WMenu
* menu
, WMenuEntry
* entry
)
291 wArrangeIcons(menu
->frame
->screen_ptr
, True
);
294 static void showAllCommand(WMenu
* menu
, WMenuEntry
* entry
)
296 wShowAllWindows(menu
->frame
->screen_ptr
);
299 static void hideOthersCommand(WMenu
* menu
, WMenuEntry
* entry
)
301 wHideOtherApplications(menu
->frame
->screen_ptr
->focused_window
);
304 static void saveSessionCommand(WMenu
* menu
, WMenuEntry
* entry
)
306 if (!wPreferences
.save_session_on_exit
)
307 wSessionSaveState(menu
->frame
->screen_ptr
);
309 wScreenSaveState(menu
->frame
->screen_ptr
);
312 static void clearSessionCommand(WMenu
* menu
, WMenuEntry
* entry
)
314 wSessionClearState(menu
->frame
->screen_ptr
);
315 wScreenSaveState(menu
->frame
->screen_ptr
);
318 static void infoPanelCommand(WMenu
* menu
, WMenuEntry
* entry
)
320 wShowInfoPanel(menu
->frame
->screen_ptr
);
323 static void legalPanelCommand(WMenu
* menu
, WMenuEntry
* entry
)
325 wShowLegalPanel(menu
->frame
->screen_ptr
);
328 /********************************************************************/
330 static char *getLocalizedMenuFile(char *menu
)
332 char *buffer
, *ptr
, *locale
;
338 len
= strlen(menu
) + strlen(Locale
) + 8;
339 buffer
= wmalloc(len
);
341 /* try menu.locale_name */
342 snprintf(buffer
, len
, "%s.%s", menu
, Locale
);
343 if (access(buffer
, F_OK
) == 0) {
347 /* position of locale in our buffer */
348 locale
= buffer
+ strlen(menu
) + 1;
350 /* check if it is in the form aa_bb.encoding and check for aa_bb */
351 ptr
= strchr(locale
, '.');
354 if (access(buffer
, F_OK
) == 0) {
358 /* now check for aa */
359 ptr
= strchr(locale
, '_');
362 if (access(buffer
, F_OK
) == 0) {
372 static void raiseMenus(WMenu
* menu
)
376 if (menu
->flags
.mapped
) {
377 wRaiseFrame(menu
->frame
->core
);
379 for (i
= 0; i
< menu
->cascade_no
; i
++) {
380 if (menu
->cascades
[i
])
381 raiseMenus(menu
->cascades
[i
]);
385 Bool
wRootMenuPerformShortcut(XEvent
* event
)
387 WScreen
*scr
= wScreenForRootWindow(event
->xkey
.root
);
392 /* ignore CapsLock */
393 modifiers
= event
->xkey
.state
& ValidModMask
;
395 for (ptr
= shortcutList
; ptr
!= NULL
; ptr
= ptr
->next
) {
396 if (ptr
->keycode
== 0 || ptr
->menu
->menu
->screen_ptr
!= scr
)
399 if (ptr
->keycode
== event
->xkey
.keycode
&& ptr
->modifier
== modifiers
) {
400 (*ptr
->entry
->callback
) (ptr
->menu
, ptr
->entry
);
408 void wRootMenuBindShortcuts(Window window
)
414 if (ptr
->modifier
!= AnyModifier
) {
415 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
| LockMask
,
416 window
, True
, GrabModeAsync
, GrabModeAsync
);
418 wHackedGrabKey(ptr
->keycode
, ptr
->modifier
, window
, True
, GrabModeAsync
, GrabModeAsync
);
421 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
, window
, True
, GrabModeAsync
, GrabModeAsync
);
426 static void rebindKeygrabs(WScreen
* scr
)
430 wwin
= scr
->focused_window
;
432 while (wwin
!= NULL
) {
433 XUngrabKey(dpy
, AnyKey
, AnyModifier
, wwin
->frame
->core
->window
);
435 if (!WFLAGP(wwin
, no_bind_keys
)) {
436 wWindowSetKeyGrabs(wwin
);
442 static void removeShortcutsForMenu(WMenu
* menu
)
445 Shortcut
*newList
= NULL
;
448 while (ptr
!= NULL
) {
450 if (ptr
->menu
== menu
) {
458 shortcutList
= newList
;
459 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
462 static Bool
addShortcut(char *file
, char *shortcutDefinition
, WMenu
* menu
, WMenuEntry
* entry
)
467 char buf
[MAX_SHORTCUT_LENGTH
], *b
;
469 ptr
= wmalloc(sizeof(Shortcut
));
471 strncpy(buf
, shortcutDefinition
, MAX_SHORTCUT_LENGTH
);
476 while ((k
= strchr(b
, '+')) != NULL
) {
480 mod
= wXModifierFromKey(b
);
482 wwarning(_("%s: invalid key modifier \"%s\""), file
, b
);
486 ptr
->modifier
|= mod
;
492 ksym
= XStringToKeysym(b
);
494 if (ksym
== NoSymbol
) {
495 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
496 file
, shortcutDefinition
, entry
->text
);
501 ptr
->keycode
= XKeysymToKeycode(dpy
, ksym
);
502 if (ptr
->keycode
== 0) {
503 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file
,
504 shortcutDefinition
, entry
->text
);
512 ptr
->next
= shortcutList
;
515 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
520 /*******************************/
522 static char *cropline(char *line
)
526 if (strlen(line
) == 0)
529 end
= &(line
[strlen(line
)]) - 1;
530 while (isspace(*line
) && *line
!= 0)
532 while (end
> line
&& isspace(*end
)) {
539 static char *next_token(char *line
, char **next
)
545 while (*line
== ' ' || *line
== '\t')
553 while (*tmp
!= 0 && *tmp
!= '"')
556 wwarning(_("%s: unmatched '\"' in menu file"), line
);
567 } while (*tmp
!= 0 && *tmp
!= ' ' && *tmp
!= '\t');
581 while (*tmp
== ' ' || *tmp
== '\t')
590 static void separateCommand(char *line
, char ***file
, char **command
)
592 char *token
, *tmp
= line
;
593 WMArray
*array
= WMCreateArray(4);
599 token
= next_token(tmp
, &tmp
);
601 if (strcmp(token
, "WITH") == 0) {
602 if (tmp
!= NULL
&& *tmp
!= 0)
603 *command
= wstrdup(tmp
);
605 wwarning(_("%s: missing command"), line
);
608 WMAddToArray(array
, token
);
610 } while (token
!= NULL
&& tmp
!= NULL
);
612 count
= WMGetArrayItemCount(array
);
614 *file
= wmalloc(sizeof(char *) * (count
+ 1));
615 (*file
)[count
] = NULL
;
616 for (i
= 0; i
< count
; i
++) {
617 (*file
)[i
] = WMGetFromArray(array
, i
);
623 static void constructMenu(WMenu
* menu
, WMenuEntry
* entry
)
626 struct stat stat_buf
;
633 separateCommand((char *)entry
->clientdata
, &path
, &cmd
);
634 if (path
== NULL
|| *path
== NULL
|| **path
== 0) {
635 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry
->clientdata
);
639 if (path
[0][0] == '|') {
642 if (!menu
->cascades
[entry
->cascade
] || menu
->cascades
[entry
->cascade
]->timestamp
== 0) {
645 submenu
= readMenuPipe(menu
->frame
->screen_ptr
, path
);
647 if (submenu
!= NULL
) {
648 if (path
[0][1] == '|')
649 submenu
->timestamp
= 0;
651 submenu
->timestamp
= 1; /* there's no automatic reloading */
659 while (path
[i
] != NULL
) {
662 if (strcmp(path
[i
], "-noext") == 0) {
667 tmp
= wexpandpath(path
[i
]);
669 lpath
= getLocalizedMenuFile(tmp
);
678 if (stat(path
[i
], &stat_buf
) == 0) {
679 if (last
< stat_buf
.st_mtime
)
680 last
= stat_buf
.st_mtime
;
684 wsyserror(_("%s:could not stat menu"), path
[i
]);
692 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry
->clientdata
);
695 stat(path
[first
], &stat_buf
);
696 if (!menu
->cascades
[entry
->cascade
]
697 || menu
->cascades
[entry
->cascade
]->timestamp
< last
) {
699 if (S_ISDIR(stat_buf
.st_mode
)) {
701 submenu
= readMenuDirectory(menu
->frame
->screen_ptr
, entry
->text
, path
, cmd
);
703 submenu
->timestamp
= last
;
704 } else if (S_ISREG(stat_buf
.st_mode
)) {
708 wwarning(_("too many parameters in OPEN_MENU: %s"),
709 (char *)entry
->clientdata
);
711 submenu
= readMenuFile(menu
->frame
->screen_ptr
, path
[first
]);
713 submenu
->timestamp
= stat_buf
.st_mtime
;
723 wMenuEntryRemoveCascade(menu
, entry
);
724 wMenuEntrySetCascade(menu
, entry
, submenu
);
729 while (path
[i
] != NULL
)
736 static void cleanupWorkspaceMenu(WMenu
* menu
)
738 if (menu
->frame
->screen_ptr
->workspace_menu
== menu
)
739 menu
->frame
->screen_ptr
->workspace_menu
= NULL
;
742 static WMenuEntry
*addWorkspaceMenu(WScreen
* scr
, WMenu
* menu
, char *title
)
747 if (scr
->flags
.added_workspace_menu
) {
749 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
752 scr
->flags
.added_workspace_menu
= 1;
754 wsmenu
= wWorkspaceMenuMake(scr
, True
);
755 wsmenu
->on_destroy
= cleanupWorkspaceMenu
;
757 scr
->workspace_menu
= wsmenu
;
758 entry
= wMenuAddCallback(menu
, title
, NULL
, NULL
);
759 wMenuEntrySetCascade(menu
, entry
, wsmenu
);
761 wWorkspaceMenuUpdate(scr
, wsmenu
);
766 static void cleanupWindowsMenu(WMenu
* menu
)
768 if (menu
->frame
->screen_ptr
->switch_menu
== menu
)
769 menu
->frame
->screen_ptr
->switch_menu
= NULL
;
772 static WMenuEntry
*addWindowsMenu(WScreen
* scr
, WMenu
* menu
, char *title
)
778 if (scr
->flags
.added_windows_menu
) {
780 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
783 scr
->flags
.added_windows_menu
= 1;
785 wwmenu
= wMenuCreate(scr
, _("Window List"), False
);
786 wwmenu
->on_destroy
= cleanupWindowsMenu
;
787 scr
->switch_menu
= wwmenu
;
788 wwin
= scr
->focused_window
;
790 UpdateSwitchMenu(scr
, wwin
, ACTION_ADD
);
794 entry
= wMenuAddCallback(menu
, title
, NULL
, NULL
);
795 wMenuEntrySetCascade(menu
, entry
, wwmenu
);
800 static WMenuEntry
*addMenuEntry(WMenu
* menu
, char *title
, char *shortcut
, char *command
,
801 char *params
, char *file_name
)
804 WMenuEntry
*entry
= NULL
;
805 Bool shortcutOk
= False
;
809 scr
= menu
->frame
->screen_ptr
;
810 if (strcmp(command
, "OPEN_MENU") == 0) {
812 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name
, command
);
817 path
= wfindfile(DEF_CONFIG_PATHS
, params
);
819 path
= wstrdup(params
);
821 dummy
= wMenuCreate(scr
, title
, False
);
822 dummy
->on_destroy
= removeShortcutsForMenu
;
823 entry
= wMenuAddCallback(menu
, title
, constructMenu
, path
);
824 entry
->free_cdata
= free
;
825 wMenuEntrySetCascade(menu
, entry
, dummy
);
827 } else if (strcmp(command
, "EXEC") == 0) {
829 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name
, command
);
831 entry
= wMenuAddCallback(menu
, title
, execCommand
, wstrconcat("exec ", params
));
832 entry
->free_cdata
= free
;
835 } else if (strcmp(command
, "SHEXEC") == 0) {
837 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name
, command
);
839 entry
= wMenuAddCallback(menu
, title
, execCommand
, wstrdup(params
));
840 entry
->free_cdata
= free
;
843 } else if (strcmp(command
, "EXIT") == 0) {
845 if (params
&& strcmp(params
, "QUICK") == 0)
846 entry
= wMenuAddCallback(menu
, title
, exitCommand
, (void *)M_QUICK
);
848 entry
= wMenuAddCallback(menu
, title
, exitCommand
, NULL
);
851 } else if (strcmp(command
, "SHUTDOWN") == 0) {
853 if (params
&& strcmp(params
, "QUICK") == 0)
854 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
, (void *)M_QUICK
);
856 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
, NULL
);
859 } else if (strcmp(command
, "REFRESH") == 0) {
860 entry
= wMenuAddCallback(menu
, title
, refreshCommand
, NULL
);
863 } else if (strcmp(command
, "WORKSPACE_MENU") == 0) {
864 entry
= addWorkspaceMenu(scr
, menu
, title
);
867 } else if (strcmp(command
, "WINDOWS_MENU") == 0) {
868 entry
= addWindowsMenu(scr
, menu
, title
);
871 } else if (strcmp(command
, "ARRANGE_ICONS") == 0) {
872 entry
= wMenuAddCallback(menu
, title
, arrangeIconsCommand
, NULL
);
875 } else if (strcmp(command
, "HIDE_OTHERS") == 0) {
876 entry
= wMenuAddCallback(menu
, title
, hideOthersCommand
, NULL
);
879 } else if (strcmp(command
, "SHOW_ALL") == 0) {
880 entry
= wMenuAddCallback(menu
, title
, showAllCommand
, NULL
);
883 } else if (strcmp(command
, "RESTART") == 0) {
884 entry
= wMenuAddCallback(menu
, title
, restartCommand
, params
? wstrdup(params
) : NULL
);
885 entry
->free_cdata
= free
;
887 } else if (strcmp(command
, "SAVE_SESSION") == 0) {
888 entry
= wMenuAddCallback(menu
, title
, saveSessionCommand
, NULL
);
891 } else if (strcmp(command
, "CLEAR_SESSION") == 0) {
892 entry
= wMenuAddCallback(menu
, title
, clearSessionCommand
, NULL
);
894 } else if (strcmp(command
, "INFO_PANEL") == 0) {
895 entry
= wMenuAddCallback(menu
, title
, infoPanelCommand
, NULL
);
897 } else if (strcmp(command
, "LEGAL_PANEL") == 0) {
898 entry
= wMenuAddCallback(menu
, title
, legalPanelCommand
, NULL
);
901 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name
, command
);
906 if (shortcut
&& entry
) {
908 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name
, title
);
910 if (addShortcut(file_name
, shortcut
, menu
, entry
)) {
912 entry
->rtext
= GetShortcutString(shortcut
);
914 entry->rtext = wstrdup(shortcut);
923 /******************* Menu Configuration From File *******************/
925 static void separateline(char *line
, char *title
, char *command
, char *parameter
, char *shortcut
)
936 while (isspace(*line
) && (*line
!= 0))
941 while (line
[i
] != '"' && (line
[i
] != 0))
947 while (!isspace(line
[i
]) && (line
[i
] != 0))
950 strncpy(title
, line
, i
);
954 /* get the command or shortcut keyword */
955 while (isspace(*line
) && (*line
!= 0))
960 while (!isspace(line
[i
]) && (line
[i
] != 0))
962 strncpy(command
, line
, i
);
966 if (strcmp(command
, "SHORTCUT") == 0) {
967 /* get the shortcut key */
968 while (isspace(*line
) && (*line
!= 0))
973 while (line
[i
] != '"' && (line
[i
] != 0))
979 while (!isspace(line
[i
]) && (line
[i
] != 0))
982 strncpy(shortcut
, line
, i
);
988 /* get the command */
989 while (isspace(*line
) && (*line
!= 0))
994 while (!isspace(line
[i
]) && (line
[i
] != 0))
996 strncpy(command
, line
, i
);
1001 /* get the parameters */
1002 while (isspace(*line
) && (*line
!= 0))
1010 while (line
[l
] != 0 && line
[l
] != '"') {
1011 parameter
[l
] = line
[l
];
1019 while (isspace(line
[l
]) && (l
> 0))
1021 strncpy(parameter
, line
, l
);
1025 static WMenu
*parseCascade(WScreen
* scr
, WMenu
* menu
, FILE * file
, char *file_name
)
1027 char linebuf
[MAXLINE
];
1028 char elinebuf
[MAXLINE
];
1029 char title
[MAXLINE
];
1030 char command
[MAXLINE
];
1031 char shortcut
[MAXLINE
];
1032 char params
[MAXLINE
];
1035 while (!feof(file
)) {
1039 fgets(linebuf
, MAXLINE
, file
);
1040 line
= cropline(linebuf
);
1041 lsize
= strlen(line
);
1043 if (line
[lsize
- 1] == '\\') {
1046 fgets(elinebuf
, MAXLINE
, file
);
1047 line2
= cropline(elinebuf
);
1048 lsize2
= strlen(line2
);
1049 if (lsize2
+ lsize
> MAXLINE
) {
1050 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1054 line
[lsize
- 1] = 0;
1055 lsize
+= lsize2
- 1;
1056 strcat(line
, line2
);
1061 } while (!ok
&& !feof(file
));
1065 if (line
[0] == 0 || line
[0] == '#' || (line
[0] == '/' && line
[1] == '/'))
1068 separateline(line
, title
, command
, params
, shortcut
);
1071 wwarning(_("%s:missing command in menu config: %s"), file_name
, line
);
1075 if (strcasecmp(command
, "MENU") == 0) {
1080 cascade
= wMenuCreate(scr
, title
, False
);
1081 cascade
->on_destroy
= removeShortcutsForMenu
;
1082 if (parseCascade(scr
, cascade
, file
, file_name
) == NULL
) {
1083 wMenuDestroy(cascade
, True
);
1085 wMenuEntrySetCascade(menu
, wMenuAddCallback(menu
, title
, NULL
, NULL
), cascade
);
1087 } else if (strcasecmp(command
, "END") == 0) {
1093 addMenuEntry(menu
, title
, shortcut
[0] ? shortcut
: NULL
, command
,
1094 params
[0] ? params
: NULL
, file_name
);
1098 wwarning(_("%s:syntax error in menu file:END declaration missing"), file_name
);
1105 static WMenu
*readMenuFile(WScreen
* scr
, char *file_name
)
1109 char linebuf
[MAXLINE
];
1110 char title
[MAXLINE
];
1111 char shortcut
[MAXLINE
];
1112 char command
[MAXLINE
];
1113 char params
[MAXLINE
];
1121 if (!wPreferences
.flags
.nocpp
) {
1122 args
= MakeCPPArgs(file_name
);
1124 wwarning(_("could not make arguments for menu file preprocessor"));
1126 snprintf(command
, sizeof(command
), "%s %s %s", CPP_PATH
, args
, file_name
);
1128 file
= popen(command
, "r");
1130 wsyserror(_("%s:could not open/preprocess menu file"), file_name
);
1139 file
= fopen(file_name
, "rb");
1141 wsyserror(_("%s:could not open menu file"), file_name
);
1146 while (!feof(file
)) {
1147 if (!fgets(linebuf
, MAXLINE
, file
))
1149 line
= cropline(linebuf
);
1150 if (line
[0] == 0 || line
[0] == '#' || (line
[0] == '/' && line
[1] == '/'))
1153 separateline(line
, title
, command
, params
, shortcut
);
1156 wwarning(_("%s:missing command in menu config: %s"), file_name
, line
);
1159 if (strcasecmp(command
, "MENU") == 0) {
1160 menu
= wMenuCreate(scr
, title
, True
);
1161 menu
->on_destroy
= removeShortcutsForMenu
;
1162 if (!parseCascade(scr
, menu
, file
, file_name
)) {
1163 wMenuDestroy(menu
, True
);
1167 wwarning(_("%s:invalid menu file. MENU command is missing"), file_name
);
1174 if (pclose(file
) == -1) {
1175 wsyserror(_("error reading preprocessed menu data"));
1187 /************ Menu Configuration From Pipe *************/
1189 static WMenu
*readMenuPipe(WScreen
* scr
, char **file_name
)
1193 char linebuf
[MAXLINE
];
1194 char title
[MAXLINE
];
1195 char command
[MAXLINE
];
1196 char params
[MAXLINE
];
1197 char shortcut
[MAXLINE
];
1200 char flat_file
[MAXLINE
];
1207 flat_file
[0] = '\0';
1209 for (i
= 0; file_name
[i
] != NULL
; i
++) {
1210 strcat(flat_file
, file_name
[i
]);
1211 strcat(flat_file
, " ");
1213 filename
= flat_file
+ (flat_file
[1] == '|' ? 2 : 1);
1216 if (!wPreferences
.flags
.nocpp
) {
1217 args
= MakeCPPArgs(filename
);
1219 wwarning(_("could not make arguments for menu file preprocessor"));
1221 snprintf(command
, sizeof(command
), "%s | %s %s", filename
, CPP_PATH
, args
);
1224 file
= popen(command
, "r");
1226 wsyserror(_("%s:could not open/preprocess menu file"), filename
);
1235 file
= popen(filename
, "rb");
1238 wsyserror(_("%s:could not open menu file"), filename
);
1243 while (!feof(file
)) {
1244 if (!fgets(linebuf
, MAXLINE
, file
))
1246 line
= cropline(linebuf
);
1247 if (line
[0] == 0 || line
[0] == '#' || (line
[0] == '/' && line
[1] == '/'))
1250 separateline(line
, title
, command
, params
, shortcut
);
1253 wwarning(_("%s:missing command in menu config: %s"), file_name
, line
);
1256 if (strcasecmp(command
, "MENU") == 0) {
1257 menu
= wMenuCreate(scr
, title
, True
);
1258 menu
->on_destroy
= removeShortcutsForMenu
;
1259 if (!parseCascade(scr
, menu
, file
, filename
)) {
1260 wMenuDestroy(menu
, True
);
1264 wwarning(_("%s:no title given for the root menu"), filename
);
1279 static int myCompare(const void *d1
, const void *d2
)
1281 dir_data
*p1
= *(dir_data
**) d1
;
1282 dir_data
*p2
= *(dir_data
**) d2
;
1284 return strcmp(p1
->name
, p2
->name
);
1287 /************ Menu Configuration From Directory *************/
1289 static Bool
isFilePackage(char *file
)
1293 /* check if the extension indicates this file is a
1294 * file package. For now, only recognize .themed */
1298 if (l
> 7 && strcmp(&(file
[l
- 7]), ".themed") == 0) {
1305 static WMenu
*readMenuDirectory(WScreen
* scr
, char *title
, char **path
, char *command
)
1308 struct dirent
*dentry
;
1309 struct stat stat_buf
;
1312 WMArray
*dirs
= NULL
, *files
= NULL
;
1313 WMArrayIterator iter
;
1314 int length
, i
, have_space
= 0;
1316 int stripExtension
= 0;
1318 dirs
= WMCreateArray(16);
1319 files
= WMCreateArray(16);
1322 while (path
[i
] != NULL
) {
1323 if (strcmp(path
[i
], "-noext") == 0) {
1329 dir
= opendir(path
[i
]);
1335 while ((dentry
= readdir(dir
))) {
1337 if (strcmp(dentry
->d_name
, ".") == 0 || strcmp(dentry
->d_name
, "..") == 0)
1340 if (dentry
->d_name
[0] == '.')
1343 buffer
= malloc(strlen(path
[i
]) + strlen(dentry
->d_name
) + 4);
1345 wsyserror(_("out of memory while constructing directory menu %s"), path
[i
]);
1349 strcpy(buffer
, path
[i
]);
1350 strcat(buffer
, "/");
1351 strcat(buffer
, dentry
->d_name
);
1353 if (stat(buffer
, &stat_buf
) != 0) {
1354 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1355 path
[i
], dentry
->d_name
);
1357 Bool isFilePack
= False
;
1360 if (S_ISDIR(stat_buf
.st_mode
)
1361 && !(isFilePack
= isFilePackage(dentry
->d_name
))) {
1363 /* access always returns success for user root */
1364 if (access(buffer
, X_OK
) == 0) {
1365 /* Directory is accesible. Add to directory list */
1367 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1368 data
->name
= wstrdup(dentry
->d_name
);
1371 WMAddToArray(dirs
, data
);
1373 } else if (S_ISREG(stat_buf
.st_mode
) || isFilePack
) {
1374 /* Hack because access always returns X_OK success for user root */
1375 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1376 if ((command
!= NULL
&& access(buffer
, R_OK
) == 0) ||
1377 (command
== NULL
&& access(buffer
, X_OK
) == 0 &&
1378 (stat_buf
.st_mode
& S_IXANY
))) {
1380 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1381 data
->name
= wstrdup(dentry
->d_name
);
1384 WMAddToArray(files
, data
);
1395 if (!WMGetArrayItemCount(dirs
) && !WMGetArrayItemCount(files
)) {
1401 WMSortArray(dirs
, myCompare
);
1402 WMSortArray(files
, myCompare
);
1404 menu
= wMenuCreate(scr
, title
, False
);
1405 menu
->on_destroy
= removeShortcutsForMenu
;
1407 WM_ITERATE_ARRAY(dirs
, data
, iter
) {
1408 /* New directory. Use same OPEN_MENU command that was used
1409 * for the current directory. */
1410 length
= strlen(path
[data
->index
]) + strlen(data
->name
) + 6;
1414 length
+= strlen(command
) + 6;
1415 buffer
= malloc(length
);
1417 wsyserror(_("out of memory while constructing directory menu %s"), path
[data
->index
]);
1423 strcat(buffer
, "-noext ");
1425 have_space
= strchr(path
[data
->index
], ' ') != NULL
|| strchr(data
->name
, ' ') != NULL
;
1428 strcat(buffer
, "\"");
1429 strcat(buffer
, path
[data
->index
]);
1431 strcat(buffer
, "/");
1432 strcat(buffer
, data
->name
);
1434 strcat(buffer
, "\"");
1436 strcat(buffer
, " WITH ");
1437 strcat(buffer
, command
);
1440 addMenuEntry(menu
, data
->name
, NULL
, "OPEN_MENU", buffer
, path
[data
->index
]);
1448 WM_ITERATE_ARRAY(files
, data
, iter
) {
1449 /* executable: add as entry */
1450 length
= strlen(path
[data
->index
]) + strlen(data
->name
) + 6;
1452 length
+= strlen(command
);
1454 buffer
= malloc(length
);
1456 wsyserror(_("out of memory while constructing directory menu %s"), path
[data
->index
]);
1460 have_space
= strchr(path
[data
->index
], ' ') != NULL
|| strchr(data
->name
, ' ') != NULL
;
1461 if (command
!= NULL
) {
1462 strcpy(buffer
, command
);
1463 strcat(buffer
, " ");
1465 strcat(buffer
, "\"");
1466 strcat(buffer
, path
[data
->index
]);
1471 strcat(buffer
, path
[data
->index
]);
1473 strcpy(buffer
, path
[data
->index
]);
1476 strcat(buffer
, "/");
1477 strcat(buffer
, data
->name
);
1479 strcat(buffer
, "\"");
1481 if (stripExtension
) {
1482 char *ptr
= strrchr(data
->name
, '.');
1483 if (ptr
&& ptr
!= data
->name
)
1486 addMenuEntry(menu
, data
->name
, NULL
, "SHEXEC", buffer
, path
[data
->index
]);
1500 /************ Menu Configuration From WMRootMenu *************/
1502 static WMenu
*makeDefaultMenu(WScreen
* scr
)
1506 menu
= wMenuCreate(scr
, _("Commands"), True
);
1507 wMenuAddCallback(menu
, "XTerm", execCommand
, "xterm");
1508 wMenuAddCallback(menu
, "rxvt", execCommand
, "rxvt");
1509 wMenuAddCallback(menu
, _("Restart"), restartCommand
, NULL
);
1510 wMenuAddCallback(menu
, _("Exit..."), exitCommand
, NULL
);
1515 *----------------------------------------------------------------------
1517 * Reads root menu configuration from defaults database.
1519 *----------------------------------------------------------------------
1521 static WMenu
*configureMenu(WScreen
* scr
, WMPropList
* definition
)
1526 WMPropList
*title
, *command
, *params
;
1529 if (WMIsPLString(definition
)) {
1530 struct stat stat_buf
;
1532 Bool menu_is_default
= False
;
1534 /* menu definition is a string. Probably a path, so parse the file */
1536 tmp
= wexpandpath(WMGetFromPLString(definition
));
1538 path
= getLocalizedMenuFile(tmp
);
1541 path
= wfindfile(DEF_CONFIG_PATHS
, tmp
);
1544 path
= wfindfile(DEF_CONFIG_PATHS
, DEF_MENU_FILE
);
1545 menu_is_default
= True
;
1549 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp
);
1554 if (stat(path
, &stat_buf
) < 0) {
1555 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path
);
1561 if (!scr
->root_menu
|| stat_buf
.st_mtime
> scr
->root_menu
->timestamp
1562 /* if the pointer in WMRootMenu has changed */
1563 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1565 if (menu_is_default
) {
1567 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1571 menu
= readMenuFile(scr
, path
);
1573 menu
->timestamp
= WMAX(stat_buf
.st_mtime
, WDRootMenu
->timestamp
);
1583 count
= WMGetPropListItemCount(definition
);
1587 elem
= WMGetFromPLArray(definition
, 0);
1588 if (!WMIsPLString(elem
)) {
1589 tmp
= WMGetPropListDescription(elem
, False
);
1590 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp
);
1594 mtitle
= WMGetFromPLString(elem
);
1596 menu
= wMenuCreate(scr
, mtitle
, False
);
1597 menu
->on_destroy
= removeShortcutsForMenu
;
1599 #ifdef GLOBAL_SUBMENU_FILE
1604 submenu
= readMenuFile(scr
, GLOBAL_SUBMENU_FILE
);
1607 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
, NULL
);
1608 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1613 for (i
= 1; i
< count
; i
++) {
1614 elem
= WMGetFromPLArray(definition
, i
);
1616 if (WMIsPLString(elem
)) {
1619 file
= WMGetFromPLString(elem
);
1623 if (!WMIsPLArray(elem
) || WMGetPropListItemCount(elem
) < 2)
1626 if (WMIsPLArray(WMGetFromPLArray(elem
, 1))) {
1631 submenu
= configureMenu(scr
, elem
);
1633 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
, NULL
);
1634 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1638 WMPropList
*shortcut
;
1641 title
= WMGetFromPLArray(elem
, idx
++);
1642 shortcut
= WMGetFromPLArray(elem
, idx
++);
1643 if (strcmp(WMGetFromPLString(shortcut
), "SHORTCUT") == 0) {
1644 shortcut
= WMGetFromPLArray(elem
, idx
++);
1645 command
= WMGetFromPLArray(elem
, idx
++);
1650 params
= WMGetFromPLArray(elem
, idx
++);
1652 if (!title
|| !command
)
1655 addMenuEntry(menu
, WMGetFromPLString(title
),
1656 shortcut
? WMGetFromPLString(shortcut
) : NULL
,
1657 WMGetFromPLString(command
),
1658 params
? WMGetFromPLString(params
) : NULL
, "WMRootMenu");
1663 tmp
= WMGetPropListDescription(elem
, False
);
1664 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp
);
1672 *----------------------------------------------------------------------
1674 * Opens the root menu, parsing the menu configuration from the
1675 * defaults database.
1676 * If the menu is already mapped and is not sticked to the
1677 * root window, it will be unmapped.
1680 * The menu may be remade.
1683 * Construction of OPEN_MENU entries are delayed to the moment the
1685 *----------------------------------------------------------------------
1687 void OpenRootMenu(WScreen
* scr
, int x
, int y
, int keyboard
)
1690 WMPropList
*definition
;
1692 static WMPropList *domain=NULL;
1695 domain = WMCreatePLString("WMRootMenu");
1699 scr
->flags
.root_menu_changed_shortcuts
= 0;
1700 scr
->flags
.added_workspace_menu
= 0;
1701 scr
->flags
.added_windows_menu
= 0;
1703 if (scr
->root_menu
&& scr
->root_menu
->flags
.mapped
) {
1704 menu
= scr
->root_menu
;
1705 if (!menu
->flags
.buttoned
) {
1708 wRaiseFrame(menu
->frame
->core
);
1711 wMenuMapAt(menu
, 0, 0, True
);
1713 wMenuMapCopyAt(menu
, x
- menu
->frame
->core
->width
/ 2, y
);
1718 definition
= WDRootMenu
->dictionary
;
1721 definition = PLGetDomain(domain);
1724 if (WMIsPLArray(definition
)) {
1725 if (!scr
->root_menu
|| WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1726 menu
= configureMenu(scr
, definition
);
1728 menu
->timestamp
= WDRootMenu
->timestamp
;
1733 menu
= configureMenu(scr
, definition
);
1738 /* menu hasn't changed or could not be read */
1739 if (!scr
->root_menu
) {
1740 wMessageDialog(scr
, _("Error"),
1741 _("The applications menu could not be loaded. "
1742 "Look at the console output for a detailed "
1743 "description of the errors."), _("OK"), NULL
, NULL
);
1745 menu
= makeDefaultMenu(scr
);
1746 scr
->root_menu
= menu
;
1748 menu
= scr
->root_menu
;
1751 if (scr
->root_menu
) {
1752 wMenuDestroy(scr
->root_menu
, True
);
1754 scr
->root_menu
= menu
;
1759 if (keyboard
&& x
== 0 && y
== 0) {
1761 } else if (keyboard
&& x
== scr
->scr_width
/ 2 && y
== scr
->scr_height
/ 2) {
1762 newx
= x
- menu
->frame
->core
->width
/ 2;
1763 newy
= y
- menu
->frame
->core
->height
/ 2;
1765 newx
= x
- menu
->frame
->core
->width
/ 2;
1768 wMenuMapAt(menu
, newx
, newy
, keyboard
);
1771 if (scr
->flags
.root_menu_changed_shortcuts
)
1772 rebindKeygrabs(scr
);