1 /* rootmenu.c- user defined menu
3 * Window Maker window manager
5 * Copyright (c) 1997, 1998 Alfredo K. Kojima
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
31 #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"
60 extern WDDomain
*WDRootMenu
;
62 extern wCursor
[WCUR_LAST
];
64 extern Time LastTimestamp
;
66 extern WPreferences wPreferences
;
68 extern int wScreenCount
;
70 static WMenu
*readMenuPipe(WScreen
*scr
, char **file_name
);
71 static WMenu
*readMenuFile(WScreen
*scr
, char *file_name
);
72 static WMenu
*readMenuDirectory(WScreen
*scr
, char *title
, char **file_name
,
76 typedef struct Shortcut
{
77 struct Shortcut
*next
;
87 static Shortcut
*shortcutList
= NULL
;
94 * "Title" EXEC command_to_exec -params
96 * "Title" EXEC command_to_exec -params
98 * "Workspaces" WORKSPACE_MENU
99 * "Title" built_in_command
101 * "Quick Quit" EXIT QUICK
104 * Commands may be preceded by SHORTCUT key
108 * INFO_PANEL - shows the Info Panel
109 * LEGAL_PANEL - shows the Legal info panel
110 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
111 * REFRESH - forces the desktop to be repainted
112 * EXIT [QUICK] - exit the window manager [without confirmation]
113 * EXEC <program> - execute an external program
114 * WORKSPACE_MENU - places the workspace submenu
116 * RESTART [<window manager>] - restarts the window manager
117 * SHOW_ALL - unhide all windows on workspace
118 * HIDE_OTHERS - hides all windows excep the focused one
119 * OPEN_MENU file - read menu data from file which must be a valid menu file.
120 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
121 * - read menu data from directory(ies) and
122 * eventually precede each with a command.
123 * OPEN_MENU | command
124 * - opens command and uses its stdout to construct and insert
125 * the resulting menu in current position. The output of
126 * command must be a valid menu description.
127 * The space between '|' and command is optional.
128 * SAVE_SESSION - saves the current state of the desktop, which include
129 * all running applications, all their hints (geometry,
130 * position on screen, workspace they live on, the dock
131 * or clip from where they were launched, and
132 * if minimized, shaded or hidden. Also saves the current
133 * workspace the user is on. All will be restored on every
134 * start of windowmaker until another SAVE_SESSION or
135 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
136 * WindowMaker domain file, then saving is automatically
137 * done on every windowmaker exit, overwriting any
138 * SAVE_SESSION or CLEAR_SESSION (see below).
139 * CLEAR_SESSION - clears any previous saved session. This will not have
140 * any effect if SaveSessionOnExit is True.
144 #define MAX(a,b) ((a)>(b) ? (a) : (b))
152 execCommand(WMenu
*menu
, WMenuEntry
*entry
)
155 static char *shell
= NULL
;
158 * This have a problem: if the shell is tcsh (not sure about others)
159 * and ~/.tcshrc have /bin/stty erase ^H somewhere on it, the shell
160 * will block and the command will not be executed.
162 shell = getenv("SHELL");
169 cmdline
= ExpandOptions(menu
->frame
->screen_ptr
, (char*)entry
->clientdata
);
171 XGrabPointer(dpy
, menu
->frame
->screen_ptr
->root_win
, True
, 0,
172 GrabModeAsync
, GrabModeAsync
, None
, wCursor
[WCUR_WAIT
],
177 /* We should not use ParseCommand() here, because it does not handle
178 * correctly '~/' expansion and '>' redirection.
179 * While we can do '~/' expansion ourselves, we can do nothing about
181 * So we better let /bin/sh to do that for us. Dan.
186 SetupEnvironment(menu
->frame
->screen_ptr
);
193 execl(shell
, shell
, "-c", cmdline
, NULL
);
194 wsyserror("could not exec %s -c %s\n", shell
, cmdline
);
199 XUngrabPointer(dpy
, CurrentTime
);
205 exitCommand(WMenu
*menu
, WMenuEntry
*entry
)
207 static int inside
= 0;
209 /* prevent reentrant calls */
214 if ((int)entry
->clientdata
==M_QUICK
215 || wMessageDialog(menu
->frame
->screen_ptr
, _("Exit"),
216 _("Exit window manager?"),
217 _("Exit"), _("Cancel"), NULL
)==WAPRDefault
) {
220 printf("Exiting WindowMaker.\n");
222 for (i
=0; i
<wScreenCount
; i
++) {
225 scr
= wScreenWithNumber(i
);
227 wScreenSaveState(scr
);
230 RestoreDesktop(NULL
);
240 shutdownCommand(WMenu
*menu
, WMenuEntry
*entry
)
242 static int inside
= 0;
246 /* prevent reentrant calls */
257 if ((int)entry
->clientdata
==M_QUICK
)
261 if (wSessionIsManaged()) {
264 r
= wMessageDialog(menu
->frame
->screen_ptr
,
265 _("Close X session"),
266 _("Close Window System session?\n"
267 "Kill might close applications with unsaved data."),
268 _("Close"), _("Kill"), _("Cancel"));
271 else if (r
==WAPRAlternate
)
278 r
= wMessageDialog(menu
->frame
->screen_ptr
,
280 _("Kill Window System session?\n"
281 "(all applications will be closed)"),
282 _("Kill"), _("Cancel"), NULL
);
288 if (result
!=R_CANCEL
) {
290 if (result
== R_CLOSE
) {
291 wSessionRequestShutdown();
295 for (i
=0; i
<wScreenCount
; i
++) {
298 scr
= wScreenWithNumber(i
);
300 wScreenSaveState(scr
);
316 restartCommand(WMenu
*menu
, WMenuEntry
*entry
)
320 for (i
=0; i
<wScreenCount
; i
++) {
323 scr
= wScreenWithNumber(i
);
325 wScreenSaveState(scr
);
327 RestoreDesktop(NULL
);
328 Restart((char*)entry
->clientdata
);
333 refreshCommand(WMenu
*menu
, WMenuEntry
*entry
)
335 wRefreshDesktop(menu
->frame
->screen_ptr
);
340 arrangeIconsCommand(WMenu
*menu
, WMenuEntry
*entry
)
342 wArrangeIcons(menu
->frame
->screen_ptr
, True
);
346 showAllCommand(WMenu
*menu
, WMenuEntry
*entry
)
348 wShowAllWindows(menu
->frame
->screen_ptr
);
352 hideOthersCommand(WMenu
*menu
, WMenuEntry
*entry
)
354 wHideOtherApplications(menu
->frame
->screen_ptr
->focused_window
);
359 saveSessionCommand(WMenu
*menu
, WMenuEntry
*entry
)
361 wSessionSaveState(menu
->frame
->screen_ptr
);
366 clearSessionCommand(WMenu
*menu
, WMenuEntry
*entry
)
368 wSessionClearState(menu
->frame
->screen_ptr
);
373 infoPanelCommand(WMenu
*menu
, WMenuEntry
*entry
)
375 wShowInfoPanel(menu
->frame
->screen_ptr
);
380 legalPanelCommand(WMenu
*menu
, WMenuEntry
*entry
)
382 wShowLegalPanel(menu
->frame
->screen_ptr
);
387 /********************************************************************/
390 raiseMenus(WMenu
*menu
)
394 if (menu
->flags
.mapped
) {
395 wRaiseFrame(menu
->frame
->core
);
397 for (i
=0; i
<menu
->cascade_no
; i
++) {
398 if (menu
->cascades
[i
])
399 raiseMenus(menu
->cascades
[i
]);
406 wRootMenuPerformShortcut(XEvent
*event
)
412 /* ignore CapsLock */
413 modifiers
= event
->xkey
.state
& ValidModMask
;
415 for (ptr
= shortcutList
; ptr
!=NULL
; ptr
= ptr
->next
) {
419 if (ptr
->keycode
==event
->xkey
.keycode
&& (ptr
->modifier
==modifiers
)) {
420 (*ptr
->entry
->callback
)(ptr
->menu
, ptr
->entry
);
429 wRootMenuBindShortcuts(Window window
)
435 if (ptr
->modifier
!=AnyModifier
) {
436 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
|LockMask
,
437 window
, True
, GrabModeAsync
, GrabModeAsync
);
439 wHackedGrabKey(ptr
->keycode
, ptr
->modifier
,
440 window
, True
, GrabModeAsync
, GrabModeAsync
);
443 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
, window
, True
,
444 GrabModeAsync
, GrabModeAsync
);
451 rebindKeygrabs(WScreen
*scr
)
455 wwin
= scr
->focused_window
;
458 XUngrabKey(dpy
, AnyKey
, AnyModifier
, wwin
->frame
->core
->window
);
460 if (!wwin
->window_flags
.no_bind_keys
) {
461 wWindowSetKeyGrabs(wwin
);
469 removeShortcutsForMenu(WMenu
*menu
)
472 Shortcut
*newList
= NULL
;
477 if (ptr
->menu
== menu
) {
485 shortcutList
= newList
;
486 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
491 addShortcut(char *file
, char *shortcutDefinition
, WMenu
*menu
,
499 ptr
= wmalloc(sizeof(Shortcut
));
501 strcpy(buf
, shortcutDefinition
);
506 while ((k
= strchr(b
, '+'))!=NULL
) {
510 mod
= wXModifierFromKey(b
);
512 wwarning(_("%s:invalid key modifier \"%s\""), file
,
517 ptr
->modifier
|= mod
;
523 ksym
= XStringToKeysym(b
);
525 if (ksym
==NoSymbol
) {
526 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
527 file
, shortcutDefinition
, entry
->text
);
532 ptr
->keycode
= XKeysymToKeycode(dpy
, ksym
);
533 if (ptr
->keycode
==0) {
534 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file
,
535 shortcutDefinition
, entry
->text
);
543 ptr
->next
= shortcutList
;
546 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
552 /*******************************/
562 end
= &(line
[strlen(line
)])-1;
563 while (isspace(*line
) && *line
!=0) line
++;
564 while (end
>line
&& isspace(*end
)) {
573 next_token(char *line
, char **next
)
579 while (*line
==' ' || *line
=='\t') line
++;
585 while (*tmp
!=0 && *tmp
!='"') tmp
++;
587 wwarning(_("%s: unmatched '\"' in menu file"), line
);
598 } while (*tmp
!=0 && *tmp
!=' ' && *tmp
!='\t');
612 while (*tmp
==' ' || *tmp
=='\t') tmp
++;
622 separateCommand(char *line
, char ***file
, char **command
)
624 char *token
, *tmp
= line
;
625 LinkedList
*list
= NULL
;
631 token
= next_token(tmp
, &tmp
);
633 if (strcmp(token
, "WITH")==0) {
634 if (tmp
!=NULL
&& *tmp
!=0)
635 *command
= wstrdup(tmp
);
637 wwarning(_("%s: missing command"), line
);
640 list
= list_cons(token
, list
);
642 } while (token
!=NULL
&& tmp
!=NULL
);
644 count
= list_length(list
);
646 *file
= wmalloc(sizeof(char*)*(count
+1));
648 (*file
)[count
] = NULL
;
650 (*file
)[--i
] = list
->head
;
651 list_remove_head(&list
);
658 constructMenu(WMenu
*menu
, WMenuEntry
*entry
)
661 struct stat stat_buf
;
668 separateCommand((char*)entry
->clientdata
, &path
, &cmd
);
669 if (!path
|| *path
==NULL
|| **path
==0) {
670 wwarning(_("invalid OPEN_MENU specification: %s"),
671 (char*)entry
->clientdata
);
675 if (path
[0][0]=='|') {
678 if (!menu
->cascades
[entry
->cascade
] ||
679 menu
->cascades
[entry
->cascade
]->timestamp
== 0) {
682 submenu
= readMenuPipe(menu
->frame
->screen_ptr
, path
);
684 /* there's no automatic reloading */
686 submenu
->timestamp
= 1;
693 while(path
[i
] != NULL
) {
696 tmp
= wexpandpath(path
[i
]);
701 lpath
= wmalloc(strlen(path
[i
])+32);
703 strcpy(lpath
, path
[i
]);
705 strcat(lpath
, Locale
);
706 if (stat(lpath
, &stat_buf
)<0) {
710 lpath
[strlen(lpath
)-(i
-2)]=0;
711 if (stat(lpath
, &stat_buf
)==0) {
729 if (stat(path
[i
], &stat_buf
)==0) {
730 if (last
< stat_buf
.st_mtime
)
731 last
= stat_buf
.st_mtime
;
735 wsyserror(_("%s:could not stat menu"), path
[i
]);
743 wsyserror(_("%s:could not stat menu :%s"), "OPEN_MENU",
744 (char*)entry
->clientdata
);
747 stat(path
[first
], &stat_buf
);
748 if (!menu
->cascades
[entry
->cascade
]
749 || menu
->cascades
[entry
->cascade
]->timestamp
< last
) {
751 if (S_ISDIR(stat_buf
.st_mode
)) {
753 submenu
= readMenuDirectory(menu
->frame
->screen_ptr
,
754 entry
->text
, &path
[first
], cmd
);
756 submenu
->timestamp
= last
;
757 } else if (S_ISREG(stat_buf
.st_mode
)) {
761 wwarning(_("too many parameters in OPEN_MENU: %s"),
762 (char*)entry
->clientdata
);
764 submenu
= readMenuFile(menu
->frame
->screen_ptr
, path
[first
]);
766 submenu
->timestamp
= stat_buf
.st_mtime
;
776 wMenuEntryRemoveCascade(menu
, entry
);
777 wMenuEntrySetCascade(menu
, entry
, submenu
);
782 while (path
[i
]!=NULL
)
791 addWorkspaceMenu(WScreen
*scr
, WMenu
*menu
, char *title
)
796 if (scr
->flags
.added_workspace_menu
) {
797 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
800 scr
->flags
.added_workspace_menu
= 1;
802 wsmenu
= wWorkspaceMenuMake(scr
, True
);
803 scr
->workspace_menu
= wsmenu
;
804 entry
=wMenuAddCallback(menu
, title
, NULL
, NULL
);
805 wMenuEntrySetCascade(menu
, entry
, wsmenu
);
807 wWorkspaceMenuUpdate(scr
, wsmenu
);
814 addMenuEntry(WMenu
*menu
, char *title
, char *shortcut
, char *command
,
815 char *params
, char *file_name
)
818 WMenuEntry
*entry
= NULL
;
819 Bool shortcutOk
= False
;
823 scr
= menu
->frame
->screen_ptr
;
824 if (strcmp(command
, "OPEN_MENU")==0) {
826 wwarning(_("%s:missing parameter for menu command \"%s\""),
832 path
= wfindfile(DEF_CONFIG_PATHS
, params
);
834 path
= wstrdup(params
);
836 dummy
= wMenuCreate(scr
, title
, False
);
837 dummy
->on_destroy
= removeShortcutsForMenu
;
838 entry
= wMenuAddCallback(menu
, title
, constructMenu
, path
);
839 entry
->free_cdata
= free
;
840 wMenuEntrySetCascade(menu
, entry
, dummy
);
842 } else if (strcmp(command
, "EXEC")==0) {
844 wwarning(_("%s:missing parameter for menu command \"%s\""),
847 entry
= wMenuAddCallback(menu
, title
, execCommand
, wstrdup(params
));
848 entry
->free_cdata
= free
;
851 } else if (strcmp(command
, "EXIT")==0) {
853 if (params
&& strcmp(params
, "QUICK")==0)
854 entry
= wMenuAddCallback(menu
, title
, exitCommand
, (void*)M_QUICK
);
856 entry
= wMenuAddCallback(menu
, title
, exitCommand
, NULL
);
859 } else if (strcmp(command
, "SHUTDOWN")==0) {
861 if (params
&& strcmp(params
, "QUICK")==0)
862 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
,
865 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
, NULL
);
868 } else if (strcmp(command
, "REFRESH")==0) {
869 entry
= wMenuAddCallback(menu
, title
, refreshCommand
, NULL
);
872 } else if (strcmp(command
, "WORKSPACE_MENU")==0) {
873 entry
= addWorkspaceMenu(scr
, menu
, title
);
876 } else if (strcmp(command
, "ARRANGE_ICONS")==0) {
877 entry
= wMenuAddCallback(menu
, title
, arrangeIconsCommand
, NULL
);
880 } else if (strcmp(command
, "HIDE_OTHERS")==0) {
881 entry
= wMenuAddCallback(menu
, title
, hideOthersCommand
, NULL
);
884 } else if (strcmp(command
, "SHOW_ALL")==0) {
885 entry
= wMenuAddCallback(menu
, title
, showAllCommand
, NULL
);
888 } else if (strcmp(command
, "RESTART")==0) {
889 entry
= wMenuAddCallback(menu
, title
, restartCommand
,
890 params
? wstrdup(params
) : NULL
);
891 entry
->free_cdata
= free
;
893 } else if (strcmp(command
, "SAVE_SESSION")==0) {
894 entry
= wMenuAddCallback(menu
, title
, saveSessionCommand
, NULL
);
897 } else if (strcmp(command
, "CLEAR_SESSION")==0) {
898 entry
= wMenuAddCallback(menu
, title
, clearSessionCommand
, NULL
);
900 } else if (strcmp(command
, "INFO_PANEL")==0) {
901 entry
= wMenuAddCallback(menu
, title
, infoPanelCommand
, NULL
);
903 } else if (strcmp(command
, "LEGAL_PANEL")==0) {
904 entry
= wMenuAddCallback(menu
, title
, legalPanelCommand
, NULL
);
907 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name
,
913 if (shortcut
&& entry
) {
915 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name
,
918 if (addShortcut(file_name
, shortcut
, menu
, entry
)) {
920 entry
->rtext
= GetShortcutString(shortcut
);
922 entry->rtext = wstrdup(shortcut);
933 /******************* Menu Configuration From File *******************/
936 separateline(char *line
, char *title
, char *command
, char *parameter
,
948 while (isspace(*line
) && (*line
!=0)) line
++;
952 while (line
[i
]!='"' && (line
[i
]!=0)) i
++;
957 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
959 strncpy(title
, line
, i
);
963 /* get the command or shortcut keyword */
964 while (isspace(*line
) && (*line
!=0)) line
++;
968 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
969 strncpy(command
, line
, i
);
973 if (strcmp(command
, "SHORTCUT")==0) {
974 /* get the shortcut key */
975 while (isspace(*line
) && (*line
!=0)) line
++;
979 while (line
[i
]!='"' && (line
[i
]!=0)) i
++;
984 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
986 strncpy(shortcut
, line
, i
);
992 /* get the command */
993 while (isspace(*line
) && (*line
!=0)) line
++;
997 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
998 strncpy(command
, line
, i
);
1003 /* get the parameters */
1004 while (isspace(*line
) && (*line
!=0)) line
++;
1011 while (line
[l
]!=0 && line
[l
]!='"') {
1012 parameter
[l
] = line
[l
];
1020 while (isspace(line
[l
]) && (l
>0)) l
--;
1021 strncpy(parameter
, line
, l
);
1027 parseCascade(WScreen
*scr
, WMenu
*menu
, FILE *file
, char *file_name
)
1029 char linebuf
[MAXLINE
];
1030 char elinebuf
[MAXLINE
];
1031 char title
[MAXLINE
];
1032 char command
[MAXLINE
];
1033 char shortcut
[MAXLINE
];
1034 char params
[MAXLINE
];
1037 while (!IsEof(file
)) {
1041 fgets(linebuf
, MAXLINE
, file
);
1042 line
= cropline(linebuf
);
1043 lsize
= strlen(line
);
1045 if (line
[lsize
-1]=='\\') {
1048 fgets(elinebuf
, MAXLINE
, file
);
1049 line2
=cropline(elinebuf
);
1050 lsize2
=strlen(line2
);
1051 if (lsize2
+lsize
>MAXLINE
) {
1052 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1058 strcat(line
, line2
);
1063 } while (!ok
&& !IsEof(file
));
1067 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1071 separateline(line
, title
, command
, params
, shortcut
);
1074 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1079 if (strcasecmp(command
, "MENU")==0) {
1084 cascade
= wMenuCreate(scr
, title
, False
);
1085 cascade
->on_destroy
= removeShortcutsForMenu
;
1086 if (parseCascade(scr
, cascade
, file
, file_name
)==NULL
) {
1087 wMenuDestroy(cascade
, True
);
1089 wMenuEntrySetCascade(menu
,
1090 wMenuAddCallback(menu
, title
, NULL
, NULL
),
1093 } else if (strcasecmp(command
, "END")==0) {
1099 addMenuEntry(menu
, title
, shortcut
[0] ? shortcut
: NULL
, command
,
1100 params
[0] ? params
: NULL
, file_name
);
1104 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1114 readMenuFile(WScreen
*scr
, char *file_name
)
1118 char linebuf
[MAXLINE
];
1119 char title
[MAXLINE
];
1120 char shortcut
[MAXLINE
];
1121 char command
[MAXLINE
];
1122 char params
[MAXLINE
];
1130 if (!wPreferences
.flags
.nocpp
) {
1131 args
= MakeCPPArgs(file_name
);
1133 wwarning(_("could not make arguments for menu file preprocessor"));
1135 sprintf(command
, "%s %s %s", CPP_PATH
, args
, file_name
);
1137 file
= popen(command
, "r");
1139 wsyserror(_("%s:could not open/preprocess menu file"),
1149 file
= fopen(file_name
, "r");
1151 wsyserror(_("%s:could not open menu file"), file_name
);
1156 while (!IsEof(file
)) {
1157 if (!fgets(linebuf
, MAXLINE
, file
))
1159 line
= cropline(linebuf
);
1160 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1163 separateline(line
, title
, command
, params
, shortcut
);
1166 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1170 if (strcasecmp(command
, "MENU")==0) {
1171 menu
= wMenuCreate(scr
, title
, True
);
1172 menu
->on_destroy
= removeShortcutsForMenu
;
1173 if (!parseCascade(scr
, menu
, file
, file_name
)) {
1174 wMenuDestroy(menu
, True
);
1178 wwarning(_("%s:invalid menu file. MENU command is missing"),
1186 if (pclose(file
)==-1) {
1187 wsyserror(_("error reading preprocessed menu data"));
1199 /************ Menu Configuration From Pipe *************/
1202 readMenuPipe(WScreen
*scr
, char **file_name
)
1206 char linebuf
[MAXLINE
];
1207 char title
[MAXLINE
];
1208 char command
[MAXLINE
];
1209 char params
[MAXLINE
];
1210 char shortcut
[MAXLINE
];
1213 char flat_file
[MAXLINE
];
1220 flat_file
[0] = '\0';
1222 for(i
= 0 ; file_name
[i
] != NULL
; i
++) {
1223 strcat(flat_file
, file_name
[i
]);
1224 strcat(flat_file
, " ");
1226 filename
= flat_file
+1;
1229 if (!wPreferences
.flags
.nocpp
) {
1230 args
= MakeCPPArgs(filename
);
1232 wwarning(_("could not make arguments for menu file preprocessor"));
1234 sprintf(command
, "%s | %s %s", filename
, CPP_PATH
, args
);
1237 file
= popen(command
, "r");
1239 wsyserror(_("%s:could not open/preprocess menu file"), filename
);
1249 file
= popen(filename
, "r");
1252 wsyserror(_("%s:could not open menu file"), filename
);
1257 while (!IsEof(file
)) {
1258 if (!fgets(linebuf
, MAXLINE
, file
))
1260 line
= cropline(linebuf
);
1261 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1264 separateline(line
, title
, command
, params
, shortcut
);
1267 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1271 if (strcasecmp(command
, "MENU")==0) {
1272 menu
= wMenuCreate(scr
, title
, True
);
1273 menu
->on_destroy
= removeShortcutsForMenu
;
1274 if (!parseCascade(scr
, menu
, file
, filename
)) {
1275 wMenuDestroy(menu
, True
);
1279 wwarning(_("%s:no title given for the root menu"), filename
);
1298 myCompare(dir_data
*d1
, dir_data
*d2
)
1300 return strcmp(d1
->name
, d2
->name
);
1304 /************ Menu Configuration From Directory *************/
1306 readMenuDirectory(WScreen
*scr
, char *title
, char **path
, char *command
)
1309 struct dirent
*dentry
;
1310 struct stat stat_buf
;
1313 LinkedList
*dirs
= NULL
, *files
= NULL
;
1314 int length
, i
, have_space
=0;
1318 while (path
[i
]!=NULL
) {
1319 dir
= opendir(path
[i
]);
1325 while ((dentry
= readdir(dir
))) {
1327 if (strcmp(dentry
->d_name
, ".")==0 ||
1328 strcmp(dentry
->d_name
, "..")==0)
1331 if (dentry
->d_name
[0] == '.')
1334 buffer
= wmalloc(strlen(path
[i
])+strlen(dentry
->d_name
)+4);
1336 wsyserror(_("out of memory while constructing directory menu %s"),
1341 strcpy(buffer
, path
[i
]);
1342 strcat(buffer
, "/");
1343 strcat(buffer
, dentry
->d_name
);
1345 if (stat(buffer
, &stat_buf
)!=0) {
1346 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1347 path
[i
], dentry
->d_name
);
1349 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1350 data
->name
= wstrdup(dentry
->d_name
);
1352 if (S_ISDIR(stat_buf
.st_mode
)) {
1353 /* access always returns success for user root */
1354 if (access(buffer
, X_OK
)==0) {
1355 /* Directory is accesible. Add to directory list */
1356 list_insert_sorted(data
, &dirs
, (int(*)())myCompare
);
1359 } else if (S_ISREG(stat_buf
.st_mode
)) {
1360 /* Hack because access always returns X_OK success for user root */
1361 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1362 if ((command
!=NULL
&& access(buffer
, R_OK
)==0) ||
1363 (command
==NULL
&& access(buffer
, X_OK
)==0 &&
1364 (stat_buf
.st_mode
& S_IXANY
))) {
1365 list_insert_sorted(data
, &files
, (int(*)())myCompare
);
1383 if (!dirs
&& !files
)
1386 menu
= wMenuCreate(scr
, title
, False
);
1387 menu
->on_destroy
= removeShortcutsForMenu
;
1389 while (dirs
!= NULL
) {
1390 /* New directory. Use same OPEN_MENU command that was used
1391 * for the current directory. */
1392 dir_data
*d
= (dir_data
*)dirs
->head
;
1394 length
= strlen(path
[d
->index
])+strlen(d
->name
)+6;
1396 length
+= strlen(command
) + 6;
1397 buffer
= wmalloc(length
);
1399 wsyserror(_("out of memory while constructing directory menu %s"),
1404 have_space
= strchr(path
[d
->index
], ' ')!=NULL
||
1405 strchr(d
->name
, ' ')!=NULL
;
1409 strcat(buffer
, path
[d
->index
]);
1411 strcpy(buffer
, path
[d
->index
]);
1413 strcat(buffer
, "/");
1414 strcat(buffer
, d
->name
);
1416 strcat(buffer
, "\"");
1418 strcat(buffer
, " WITH ");
1419 strcat(buffer
, command
);
1422 addMenuEntry(menu
, d
->name
, NULL
, "OPEN_MENU", buffer
, path
[d
->index
]);
1430 list_remove_head(&dirs
);
1433 while (files
!= NULL
) {
1434 /* executable: add as entry */
1435 dir_data
*f
= (dir_data
*) files
->head
;;
1437 length
= strlen(path
[f
->index
])+strlen(f
->name
)+6;
1439 length
+= strlen(command
);
1441 buffer
= wmalloc(length
);
1443 wsyserror(_("out of memory while constructing directory menu %s"),
1448 have_space
= strchr(path
[f
->index
], ' ')!=NULL
||
1449 strchr(f
->name
, ' ')!=NULL
;
1450 if (command
!=NULL
) {
1451 strcpy(buffer
, command
);
1452 strcat(buffer
, " ");
1454 strcat(buffer
, "\"");
1455 strcat(buffer
, path
[f
->index
]);
1460 strcat(buffer
, path
[f
->index
]);
1462 strcpy(buffer
, path
[f
->index
]);
1465 strcat(buffer
, "/");
1466 strcat(buffer
, f
->name
);
1468 strcat(buffer
, "\"");
1470 addMenuEntry(menu
, f
->name
, NULL
, "EXEC", buffer
, path
[f
->index
]);
1478 list_remove_head(&files
);
1485 /************ Menu Configuration From WMRootMenu *************/
1488 makeDefaultMenu(WScreen
*scr
)
1492 menu
= wMenuCreate(scr
, _("Commands"), True
);
1493 wMenuAddCallback(menu
, "XTerm", execCommand
, "xterm");
1494 wMenuAddCallback(menu
, _("Exit..."), exitCommand
, NULL
);
1503 *----------------------------------------------------------------------
1505 * Reads root menu configuration from defaults database.
1507 *----------------------------------------------------------------------
1510 configureMenu(WScreen
*scr
, proplist_t definition
)
1515 proplist_t title
, command
, params
;
1519 if (PLIsString(definition
)) {
1520 struct stat stat_buf
;
1522 Bool menu_is_default
= False
;
1524 /* menu definition is a string. Probably a path, so parse the file */
1526 tmp
= wexpandpath(PLGetString(definition
));
1529 path
= wmalloc(strlen(tmp
)+32);
1533 strcat(path
, Locale
);
1535 /* look for menu.xy */
1536 if (stat(path
, &stat_buf
)<0) {
1540 path
[strlen(path
)-(i
-2)]=0;
1541 /* look for menu.xy_zw */
1542 if (stat(path
, &stat_buf
)<0) {
1544 /* If did not find any localized menus, try
1545 * only menu. This can also mean that
1546 * the path in WMRootMenu was already the
1547 * path for the localized menu (eg: menu = "menu.ab")
1559 path
= wfindfile(DEF_CONFIG_PATHS
, tmp
);
1562 path
= wfindfile(DEF_CONFIG_PATHS
, DEF_MENU_FILE
);
1563 menu_is_default
= True
;
1567 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1573 if (stat(path
, &stat_buf
)<0) {
1574 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path
);
1580 if (!scr
->root_menu
|| stat_buf
.st_mtime
> scr
->root_menu
->timestamp
1581 /* if the pointer in WMRootMenu has changed */
1582 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1584 if (menu_is_default
) {
1585 wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1589 menu
= readMenuFile(scr
, path
);
1591 menu
->timestamp
= MAX(stat_buf
.st_mtime
, WDRootMenu
->timestamp
);
1601 count
= PLGetNumberOfElements(definition
);
1605 elem
= PLGetArrayElement(definition
, 0);
1606 if (!PLIsString(elem
)) {
1607 tmp
= PLGetDescription(elem
);
1608 wwarning(_("%s:format error in root menu configuration \"%s\""),
1613 mtitle
= PLGetString(elem
);
1615 menu
= wMenuCreate(scr
, mtitle
, False
);
1616 menu
->on_destroy
= removeShortcutsForMenu
;
1618 for (i
=1; i
<count
; i
++) {
1619 elem
= PLGetArrayElement(definition
, i
);
1621 if (!PLIsArray(elem
) || PLGetNumberOfElements(elem
) < 2)
1624 if (PLIsArray(PLGetArrayElement(elem
,1))) {
1629 submenu
= configureMenu(scr
, elem
);
1631 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
,
1633 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1640 title
= PLGetArrayElement(elem
, idx
++);
1641 shortcut
= PLGetArrayElement(elem
, idx
++);
1642 if (strcmp(PLGetString(shortcut
), "SHORTCUT")==0) {
1643 shortcut
= PLGetArrayElement(elem
, idx
++);
1644 command
= PLGetArrayElement(elem
, idx
++);
1649 params
= PLGetArrayElement(elem
, idx
++);
1651 if (!title
|| !command
)
1654 addMenuEntry(menu
, PLGetString(title
),
1655 shortcut
? PLGetString(shortcut
) : NULL
,
1656 PLGetString(command
),
1657 params
? PLGetString(params
) : NULL
, "WMRootMenu");
1662 tmp
= PLGetDescription(elem
);
1663 wwarning(_("%s:format error in root menu configuration \"%s\""),
1679 *----------------------------------------------------------------------
1681 * Opens the root menu, parsing the menu configuration from the
1682 * defaults database.
1683 * If the menu is already mapped and is not sticked to the
1684 * root window, it will be unmapped.
1687 * The menu may be remade.
1690 * Construction of OPEN_MENU entries are delayed to the moment the
1692 *----------------------------------------------------------------------
1695 OpenRootMenu(WScreen
*scr
, int x
, int y
, int keyboard
)
1698 proplist_t definition
;
1700 static proplist_t domain=NULL;
1703 domain = PLMakeString("WMRootMenu");
1707 scr
->flags
.root_menu_changed_shortcuts
= 0;
1708 scr
->flags
.added_workspace_menu
= 0;
1710 if (scr
->root_menu
&& scr
->root_menu
->flags
.mapped
) {
1711 menu
= scr
->root_menu
;
1712 if (!menu
->flags
.buttoned
) {
1715 wRaiseFrame(menu
->frame
->core
);
1718 wMenuMapAt(menu
, 0, 0, True
);
1720 wMenuMapCopyAt(menu
, x
-menu
->frame
->core
->width
/2,
1721 y
-menu
->frame
->top_width
/2);
1727 definition
= WDRootMenu
->dictionary
;
1730 definition = PLGetDomain(domain);
1733 if (PLIsArray(definition
)) {
1735 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1736 menu
= configureMenu(scr
, definition
);
1738 menu
->timestamp
= WDRootMenu
->timestamp
;
1743 menu
= configureMenu(scr
, definition
);
1748 /* menu hasn't changed or could not be read */
1749 if (!scr
->root_menu
) {
1750 menu
= makeDefaultMenu(scr
);
1751 scr
->root_menu
= menu
;
1753 menu
= scr
->root_menu
;
1757 wMenuDestroy(scr
->root_menu
, True
);
1758 scr
->root_menu
= menu
;
1761 wMenuMapAt(menu
, x
-menu
->frame
->core
->width
/2, y
-menu
->frame
->top_width
/2,
1765 if (scr
->flags
.root_menu_changed_shortcuts
)
1766 rebindKeygrabs(scr
);