1 /* rootmenu.c- user defined menu
3 * WindowMaker 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
;
69 static WMenu
*readMenuPipe(WScreen
*scr
, char **file_name
);
70 static WMenu
*readMenuFile(WScreen
*scr
, char *file_name
);
71 static WMenu
*readMenuDirectory(WScreen
*scr
, char *title
, char **file_name
,
75 typedef struct Shortcut
{
76 struct Shortcut
*next
;
86 static Shortcut
*shortcutList
= NULL
;
93 * "Title" EXEC command_to_exec -params
95 * "Title" EXEC command_to_exec -params
97 * "Workspaces" WORKSPACE_MENU
98 * "Title" built_in_command
100 * "Quick Quit" EXIT QUICK
103 * Commands may be preceded by SHORTCUT key
107 * INFO_PANEL - shows the Info Panel
108 * LEGAL_PANEL - shows the Legal info panel
109 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
110 * REFRESH - forces the desktop to be repainted
111 * EXIT [QUICK] - exit the window manager [without confirmation]
112 * EXEC <program> - execute an external program
113 * WORKSPACE_MENU - places the workspace submenu
115 * RESTART [<window manager>] - restarts the window manager
116 * SHOW_ALL - unhide all windows on workspace
117 * HIDE_OTHERS - hides all windows excep the focused one
118 * OPEN_MENU file - read menu data from file which must be a valid menu file.
119 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
120 * - read menu data from directory(ies) and
121 * eventually precede each with a command.
122 * OPEN_MENU | command
123 * - opens command and uses its stdout to construct and insert
124 * the resulting menu in current position. The output of
125 * command must be a valid menu description.
126 * The space between '|' and command is optional.
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).
138 * CLEAR_SESSION - clears any previous saved session. This will not have
139 * any effect if SaveSessionOnExit is True.
143 #define MAX(a,b) ((a)>(b) ? (a) : (b))
151 execCommand(WMenu
*menu
, WMenuEntry
*entry
)
157 cmdline
= ExpandOptions(menu
->frame
->screen_ptr
, (char*)entry
->clientdata
);
159 path
= wstrdup(cmdline
);
160 chr
= strchr(path
, ' ');
164 if (access(path
, X_OK
)!=0) {
165 path
= realloc(path
, strlen(cmdline
)+128);
166 sprintf(path
, _("Program \"%s\" not found or cannot be executed."),
168 wMessageDialog(menu
->frame
->screen_ptr
, _("Error"), path
,
169 _("OK"), NULL
, NULL
);
177 XGrabPointer(dpy
, menu
->frame
->screen_ptr
->root_win
, True
, 0,
178 GrabModeAsync
, GrabModeAsync
, None
, wCursor
[WCUR_WAIT
],
183 /* We should not use ParseCommand() here, because it does not handle
184 * correctly '~/' expansion and '>' redirection.
185 * While we can do '~/' expansion ourselves, we can do nothing about
187 * So we better let /bin/sh to do that for us. -Dan
191 SetupEnvironment(menu
->frame
->screen_ptr
);
192 close(ConnectionNumber(dpy
));
196 execl("/bin/sh", "/bin/sh", "-c", cmdline
, NULL
);
201 XUngrabPointer(dpy
, CurrentTime
);
207 exitCommand(WMenu
*menu
, WMenuEntry
*entry
)
209 /* prevent reentrant calls */
210 wMenuSetEnabled(menu
, entry
->order
, False
);
211 if ((int)entry
->clientdata
==M_QUICK
212 || wMessageDialog(menu
->frame
->screen_ptr
, _("Exit"),
213 _("Exit window manager?"),
214 _("Exit"), _("Cancel"), NULL
)==WAPRDefault
) {
216 printf("Exiting WindowMaker.\n");
219 wScreenSaveState(menu
->frame
->screen_ptr
);
221 RestoreDesktop(menu
->frame
->screen_ptr
);
226 wMenuSetEnabled(menu
, entry
->order
, True
);
231 shutdownCommand(WMenu
*menu
, WMenuEntry
*entry
)
233 /* prevent reentrant calls */
234 wMenuSetEnabled(menu
, entry
->order
, False
);
235 if ((int)entry
->clientdata
==M_QUICK
236 || wMessageDialog(menu
->frame
->screen_ptr
, _("Close X session"),
237 _("Close Window System session?\n(all applications will be closed)"),
238 _("Exit"), _("Cancel"), NULL
)==WAPRDefault
) {
239 /* printf(_("Exiting...\n"));*/
241 wScreenSaveState(menu
->frame
->screen_ptr
);
243 WipeDesktop(menu
->frame
->screen_ptr
);
248 wMenuSetEnabled(menu
, entry
->order
, True
);
253 restartCommand(WMenu
*menu
, WMenuEntry
*entry
)
255 wScreenSaveState(menu
->frame
->screen_ptr
);
257 RestoreDesktop(menu
->frame
->screen_ptr
);
258 Restart((char*)entry
->clientdata
);
263 refreshCommand(WMenu
*menu
, WMenuEntry
*entry
)
265 wRefreshDesktop(menu
->frame
->screen_ptr
);
270 arrangeIconsCommand(WMenu
*menu
, WMenuEntry
*entry
)
272 wArrangeIcons(menu
->frame
->screen_ptr
, True
);
276 showAllCommand(WMenu
*menu
, WMenuEntry
*entry
)
278 wShowAllWindows(menu
->frame
->screen_ptr
);
282 hideOthersCommand(WMenu
*menu
, WMenuEntry
*entry
)
284 wHideOtherApplications(menu
->frame
->screen_ptr
->focused_window
);
289 saveSessionCommand(WMenu
*menu
, WMenuEntry
*entry
)
291 wSessionSaveState(menu
->frame
->screen_ptr
);
296 clearSessionCommand(WMenu
*menu
, WMenuEntry
*entry
)
298 wSessionClearState(menu
->frame
->screen_ptr
);
303 infoPanelCommand(WMenu
*menu
, WMenuEntry
*entry
)
305 wShowInfoPanel(menu
->frame
->screen_ptr
);
310 legalPanelCommand(WMenu
*menu
, WMenuEntry
*entry
)
312 wShowLegalPanel(menu
->frame
->screen_ptr
);
317 /********************************************************************/
320 raiseMenus(WMenu
*menu
)
324 if (menu
->flags
.mapped
) {
325 wRaiseFrame(menu
->frame
->core
);
327 for (i
=0; i
<menu
->cascade_no
; i
++) {
328 if (menu
->cascades
[i
])
329 raiseMenus(menu
->cascades
[i
]);
336 wRootMenuPerformShortcut(XEvent
*event
)
342 /* ignore CapsLock */
343 modifiers
= event
->xkey
.state
& ValidModMask
;
345 for (ptr
= shortcutList
; ptr
!=NULL
; ptr
= ptr
->next
) {
349 if (ptr
->keycode
==event
->xkey
.keycode
&& (ptr
->modifier
==modifiers
)) {
350 (*ptr
->entry
->callback
)(ptr
->menu
, ptr
->entry
);
359 wRootMenuBindShortcuts(Window window
)
365 if (ptr
->modifier
!=AnyModifier
) {
366 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
|LockMask
,
367 window
, True
, GrabModeAsync
, GrabModeAsync
);
369 wHackedGrabKey(ptr
->keycode
, ptr
->modifier
,
370 window
, True
, GrabModeAsync
, GrabModeAsync
);
373 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
, window
, True
,
374 GrabModeAsync
, GrabModeAsync
);
381 rebindKeygrabs(WScreen
*scr
)
385 wwin
= scr
->focused_window
;
388 XUngrabKey(dpy
, AnyKey
, AnyModifier
, wwin
->frame
->core
->window
);
390 if (!wwin
->window_flags
.no_bind_keys
) {
391 wWindowSetKeyGrabs(wwin
);
399 removeShortcutsForMenu(WMenu
*menu
)
402 Shortcut
*newList
= NULL
;
407 if (ptr
->menu
== menu
) {
415 shortcutList
= newList
;
416 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
421 addShortcut(char *file
, char *shortcutDefinition
, WMenu
*menu
,
429 ptr
= wmalloc(sizeof(Shortcut
));
431 strcpy(buf
, shortcutDefinition
);
436 while ((k
= strchr(b
, '+'))!=NULL
) {
440 mod
= wXModifierFromKey(b
);
442 wwarning(_("%s:invalid key modifier \"%s\""), file
,
447 ptr
->modifier
|= mod
;
453 ksym
= XStringToKeysym(b
);
455 if (ksym
==NoSymbol
) {
456 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
457 file
, shortcutDefinition
, entry
->text
);
462 ptr
->keycode
= XKeysymToKeycode(dpy
, ksym
);
463 if (ptr
->keycode
==0) {
464 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file
,
465 shortcutDefinition
, entry
->text
);
473 ptr
->next
= shortcutList
;
476 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
482 /*******************************/
492 end
= &(line
[strlen(line
)])-1;
493 while (isspace(*line
) && *line
!=0) line
++;
494 while (end
>line
&& isspace(*end
)) {
503 next_token(char *line
, char **next
)
509 while (*line
==' ' || *line
=='\t') line
++;
515 while (*tmp
!=0 && *tmp
!='"') tmp
++;
517 wwarning(_("%s: unmatched '\"' in menu file"), line
);
528 } while (*tmp
!=0 && *tmp
!=' ' && *tmp
!='\t');
542 while (*tmp
==' ' || *tmp
=='\t') tmp
++;
552 separateCommand(char *line
, char ***file
, char **command
)
554 char *token
, *tmp
= line
;
555 LinkedList
*list
= NULL
;
561 token
= next_token(tmp
, &tmp
);
563 if (strcmp(token
, "WITH")==0) {
564 if (tmp
!=NULL
&& *tmp
!=0)
565 *command
= wstrdup(tmp
);
567 wwarning(_("%s: missing command"), line
);
570 list
= list_cons(token
, list
);
572 } while (token
!=NULL
&& tmp
!=NULL
);
574 count
= list_length(list
);
576 *file
= wmalloc(sizeof(char*)*(count
+1));
578 (*file
)[count
] = NULL
;
580 (*file
)[--i
] = list
->head
;
581 list_remove_head(&list
);
588 constructMenu(WMenu
*menu
, WMenuEntry
*entry
)
591 struct stat stat_buf
;
598 separateCommand((char*)entry
->clientdata
, &path
, &cmd
);
599 if (!path
|| *path
==NULL
|| **path
==0) {
600 wwarning(_("invalid OPEN_MENU specification: %s"),
601 (char*)entry
->clientdata
);
605 if (path
[0][0]=='|') {
608 if (!menu
->cascades
[entry
->cascade
] ||
609 menu
->cascades
[entry
->cascade
]->timestamp
== 0) {
612 submenu
= readMenuPipe(menu
->frame
->screen_ptr
, path
);
614 /* there's no automatic reloading */
616 submenu
->timestamp
= 1;
623 while(path
[i
] != NULL
) {
626 tmp
= wexpandpath(path
[i
]);
631 lpath
= wmalloc(strlen(path
[i
])+32);
633 strcpy(lpath
, path
[i
]);
635 strcat(lpath
, Locale
);
636 if (stat(lpath
, &stat_buf
)<0) {
640 lpath
[strlen(lpath
)-(i
-2)]=0;
641 if (stat(lpath
, &stat_buf
)==0) {
659 if (stat(path
[i
], &stat_buf
)==0) {
660 if (last
< stat_buf
.st_mtime
)
661 last
= stat_buf
.st_mtime
;
665 wsyserror(_("%s:could not stat menu"), path
[i
]);
673 wsyserror(_("%s:could not stat menu :%s"), "OPEN_MENU",
674 (char*)entry
->clientdata
);
677 stat(path
[first
], &stat_buf
);
678 if (!menu
->cascades
[entry
->cascade
]
679 || menu
->cascades
[entry
->cascade
]->timestamp
< last
) {
681 if (S_ISDIR(stat_buf
.st_mode
)) {
683 submenu
= readMenuDirectory(menu
->frame
->screen_ptr
,
684 entry
->text
, &path
[first
], cmd
);
686 submenu
->timestamp
= last
;
687 } else if (S_ISREG(stat_buf
.st_mode
)) {
691 wwarning(_("too many parameters in OPEN_MENU: %s"),
692 (char*)entry
->clientdata
);
694 submenu
= readMenuFile(menu
->frame
->screen_ptr
, path
[first
]);
696 submenu
->timestamp
= stat_buf
.st_mtime
;
706 wMenuEntryRemoveCascade(menu
, entry
);
707 wMenuEntrySetCascade(menu
, entry
, submenu
);
712 while (path
[i
]!=NULL
)
721 addWorkspaceMenu(WScreen
*scr
, WMenu
*menu
, char *title
)
726 if (scr
->flags
.added_workspace_menu
) {
727 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
730 scr
->flags
.added_workspace_menu
= 1;
732 wsmenu
= wWorkspaceMenuMake(scr
, True
);
733 scr
->workspace_menu
= wsmenu
;
734 entry
=wMenuAddCallback(menu
, title
, NULL
, NULL
);
735 wMenuEntrySetCascade(menu
, entry
, wsmenu
);
737 wWorkspaceMenuUpdate(scr
, wsmenu
);
744 addMenuEntry(WMenu
*menu
, char *title
, char *shortcut
, char *command
,
745 char *params
, char *file_name
)
748 WMenuEntry
*entry
= NULL
;
749 Bool shortcutOk
= False
;
753 scr
= menu
->frame
->screen_ptr
;
754 if (strcmp(command
, "OPEN_MENU")==0) {
756 wwarning(_("%s:missing parameter for menu command \"%s\""),
762 path
= wfindfile(DEF_CONFIG_PATHS
, params
);
764 path
= wstrdup(params
);
766 dummy
= wMenuCreate(scr
, title
, False
);
767 dummy
->on_destroy
= removeShortcutsForMenu
;
768 entry
= wMenuAddCallback(menu
, title
, constructMenu
, path
);
769 entry
->free_cdata
= free
;
770 wMenuEntrySetCascade(menu
, entry
, dummy
);
772 } else if (strcmp(command
, "EXEC")==0) {
774 wwarning(_("%s:missing parameter for menu command \"%s\""),
777 entry
= wMenuAddCallback(menu
, title
, execCommand
, wstrdup(params
));
778 entry
->free_cdata
= free
;
781 } else if (strcmp(command
, "EXIT")==0) {
783 if (params
&& strcmp(params
, "QUICK")==0)
784 entry
= wMenuAddCallback(menu
, title
, exitCommand
, (void*)M_QUICK
);
786 entry
= wMenuAddCallback(menu
, title
, exitCommand
, NULL
);
789 } else if (strcmp(command
, "SHUTDOWN")==0) {
791 if (params
&& strcmp(params
, "QUICK")==0)
792 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
,
795 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
, NULL
);
798 } else if (strcmp(command
, "REFRESH")==0) {
799 entry
= wMenuAddCallback(menu
, title
, refreshCommand
, NULL
);
802 } else if (strcmp(command
, "WORKSPACE_MENU")==0) {
803 entry
= addWorkspaceMenu(scr
, menu
, title
);
806 } else if (strcmp(command
, "ARRANGE_ICONS")==0) {
807 entry
= wMenuAddCallback(menu
, title
, arrangeIconsCommand
, NULL
);
810 } else if (strcmp(command
, "HIDE_OTHERS")==0) {
811 entry
= wMenuAddCallback(menu
, title
, hideOthersCommand
, NULL
);
814 } else if (strcmp(command
, "SHOW_ALL")==0) {
815 entry
= wMenuAddCallback(menu
, title
, showAllCommand
, NULL
);
818 } else if (strcmp(command
, "RESTART")==0) {
819 entry
= wMenuAddCallback(menu
, title
, restartCommand
,
820 params
? wstrdup(params
) : NULL
);
821 entry
->free_cdata
= free
;
823 } else if (strcmp(command
, "SAVE_SESSION")==0) {
824 entry
= wMenuAddCallback(menu
, title
, saveSessionCommand
, NULL
);
827 } else if (strcmp(command
, "CLEAR_SESSION")==0) {
828 entry
= wMenuAddCallback(menu
, title
, clearSessionCommand
, NULL
);
830 } else if (strcmp(command
, "INFO_PANEL")==0) {
831 entry
= wMenuAddCallback(menu
, title
, infoPanelCommand
, NULL
);
833 } else if (strcmp(command
, "LEGAL_PANEL")==0) {
834 entry
= wMenuAddCallback(menu
, title
, legalPanelCommand
, NULL
);
837 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name
,
843 if (shortcut
&& entry
) {
845 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name
,
848 if (addShortcut(file_name
, shortcut
, menu
, entry
)) {
850 entry
->rtext
= GetShortcutString(shortcut
);
852 entry->rtext = wstrdup(shortcut);
863 /******************* Menu Configuration From File *******************/
866 separateline(char *line
, char *title
, char *command
, char *parameter
,
878 while (isspace(*line
) && (*line
!=0)) line
++;
882 while (line
[i
]!='"' && (line
[i
]!=0)) i
++;
887 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
889 strncpy(title
, line
, i
);
893 /* get the command or shortcut keyword */
894 while (isspace(*line
) && (*line
!=0)) line
++;
898 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
899 strncpy(command
, line
, i
);
903 if (strcmp(command
, "SHORTCUT")==0) {
904 /* get the shortcut key */
905 while (isspace(*line
) && (*line
!=0)) line
++;
909 while (line
[i
]!='"' && (line
[i
]!=0)) i
++;
914 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
916 strncpy(shortcut
, line
, i
);
922 /* get the command */
923 while (isspace(*line
) && (*line
!=0)) line
++;
927 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
928 strncpy(command
, line
, i
);
933 /* get the parameters */
934 while (isspace(*line
) && (*line
!=0)) line
++;
941 while (line
[l
]!=0 && line
[l
]!='"') {
942 parameter
[l
] = line
[l
];
950 while (isspace(line
[l
]) && (l
>0)) l
--;
951 strncpy(parameter
, line
, l
);
957 parseCascade(WScreen
*scr
, WMenu
*menu
, FILE *file
, char *file_name
)
959 char linebuf
[MAXLINE
];
960 char elinebuf
[MAXLINE
];
962 char command
[MAXLINE
];
963 char shortcut
[MAXLINE
];
964 char params
[MAXLINE
];
967 while (!IsEof(file
)) {
971 fgets(linebuf
, MAXLINE
, file
);
972 line
= cropline(linebuf
);
973 lsize
= strlen(line
);
975 if (line
[lsize
-1]=='\\') {
978 fgets(elinebuf
, MAXLINE
, file
);
979 line2
=cropline(elinebuf
);
980 lsize2
=strlen(line2
);
981 if (lsize2
+lsize
>MAXLINE
) {
982 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
993 } while (!ok
&& !IsEof(file
));
997 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1001 separateline(line
, title
, command
, params
, shortcut
);
1004 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1009 if (strcasecmp(command
, "MENU")==0) {
1014 cascade
= wMenuCreate(scr
, title
, False
);
1015 cascade
->on_destroy
= removeShortcutsForMenu
;
1016 if (parseCascade(scr
, cascade
, file
, file_name
)==NULL
) {
1017 wMenuDestroy(cascade
, True
);
1019 wMenuEntrySetCascade(menu
,
1020 wMenuAddCallback(menu
, title
, NULL
, NULL
),
1023 } else if (strcasecmp(command
, "END")==0) {
1029 addMenuEntry(menu
, title
, shortcut
[0] ? shortcut
: NULL
, command
,
1030 params
[0] ? params
: NULL
, file_name
);
1034 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1044 readMenuFile(WScreen
*scr
, char *file_name
)
1048 char linebuf
[MAXLINE
];
1049 char title
[MAXLINE
];
1050 char shortcut
[MAXLINE
];
1051 char command
[MAXLINE
];
1052 char params
[MAXLINE
];
1060 if (!wPreferences
.flags
.nocpp
) {
1061 args
= MakeCPPArgs(file_name
);
1063 wwarning(_("could not make arguments for menu file preprocessor"));
1065 sprintf(command
, "%s %s %s", CPP_PATH
, args
, file_name
);
1067 file
= popen(command
, "r");
1069 wsyserror(_("%s:could not open/preprocess menu file"),
1079 file
= fopen(file_name
, "r");
1081 wsyserror(_("%s:could not open menu file"), file_name
);
1086 while (!IsEof(file
)) {
1087 if (!fgets(linebuf
, MAXLINE
, file
))
1089 line
= cropline(linebuf
);
1090 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1093 separateline(line
, title
, command
, params
, shortcut
);
1096 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1100 if (strcasecmp(command
, "MENU")==0) {
1101 menu
= wMenuCreate(scr
, title
, True
);
1102 menu
->on_destroy
= removeShortcutsForMenu
;
1103 if (!parseCascade(scr
, menu
, file
, file_name
)) {
1104 wMenuDestroy(menu
, True
);
1108 wwarning(_("%s:invalid menu file. MENU command is missing"),
1116 if (pclose(file
)==-1) {
1117 wsyserror(_("error reading preprocessed menu data"));
1129 /************ Menu Configuration From Pipe *************/
1132 readMenuPipe(WScreen
*scr
, char **file_name
)
1136 char linebuf
[MAXLINE
];
1137 char title
[MAXLINE
];
1138 char command
[MAXLINE
];
1139 char params
[MAXLINE
];
1140 char shortcut
[MAXLINE
];
1143 char flat_file
[MAXLINE
];
1150 flat_file
[0] = '\0';
1152 for(i
= 0 ; file_name
[i
] != NULL
; i
++) {
1153 strcat(flat_file
, file_name
[i
]);
1154 strcat(flat_file
, " ");
1156 filename
= flat_file
+1;
1159 if (!wPreferences
.flags
.nocpp
) {
1160 args
= MakeCPPArgs(filename
);
1162 wwarning(_("could not make arguments for menu file preprocessor"));
1164 sprintf(command
, "%s | %s %s", filename
, CPP_PATH
, args
);
1167 file
= popen(command
, "r");
1169 wsyserror(_("%s:could not open/preprocess menu file"), filename
);
1179 file
= popen(filename
, "r");
1182 wsyserror(_("%s:could not open menu file"), filename
);
1187 while (!IsEof(file
)) {
1188 if (!fgets(linebuf
, MAXLINE
, file
))
1190 line
= cropline(linebuf
);
1191 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1194 separateline(line
, title
, command
, params
, shortcut
);
1197 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1201 if (strcasecmp(command
, "MENU")==0) {
1202 menu
= wMenuCreate(scr
, title
, True
);
1203 menu
->on_destroy
= removeShortcutsForMenu
;
1204 if (!parseCascade(scr
, menu
, file
, filename
)) {
1205 wMenuDestroy(menu
, True
);
1209 wwarning(_("%s:no title given for the root menu"), filename
);
1228 myCompare(dir_data
*d1
, dir_data
*d2
)
1230 return strcmp(d1
->name
, d2
->name
);
1234 /************ Menu Configuration From Directory *************/
1236 readMenuDirectory(WScreen
*scr
, char *title
, char **path
, char *command
)
1239 struct dirent
*dentry
;
1240 struct stat stat_buf
;
1243 LinkedList
*dirs
= NULL
, *files
= NULL
;
1244 int length
, i
, have_space
=0;
1248 while (path
[i
]!=NULL
) {
1249 dir
= opendir(path
[i
]);
1255 while ((dentry
= readdir(dir
))) {
1257 if (strcmp(dentry
->d_name
, ".")==0 ||
1258 strcmp(dentry
->d_name
, "..")==0)
1261 buffer
= wmalloc(strlen(path
[i
])+strlen(dentry
->d_name
)+4);
1263 wsyserror(_("out of memory while constructing directory menu %s"),
1268 strcpy(buffer
, path
[i
]);
1269 strcat(buffer
, "/");
1270 strcat(buffer
, dentry
->d_name
);
1272 if (stat(buffer
, &stat_buf
)!=0) {
1273 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1274 path
[i
], dentry
->d_name
);
1276 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1277 data
->name
= wstrdup(dentry
->d_name
);
1279 if (S_ISDIR(stat_buf
.st_mode
)) {
1280 /* access always returns success for user root */
1281 if (access(buffer
, X_OK
)==0) {
1282 /* Directory is accesible. Add to directory list */
1283 list_insert_sorted(data
, &dirs
, (int(*)())myCompare
);
1286 } else if (S_ISREG(stat_buf
.st_mode
)) {
1287 /* Hack because access always returns X_OK success for user root */
1288 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1289 if ((command
!=NULL
&& access(buffer
, R_OK
)==0) ||
1290 (command
==NULL
&& access(buffer
, X_OK
)==0 &&
1291 (stat_buf
.st_mode
& S_IXANY
))) {
1292 list_insert_sorted(data
, &files
, (int(*)())myCompare
);
1310 if (!dirs
&& !files
)
1313 menu
= wMenuCreate(scr
, title
, False
);
1314 menu
->on_destroy
= removeShortcutsForMenu
;
1316 while (dirs
!= NULL
) {
1317 /* New directory. Use same OPEN_MENU command that was used
1318 * for the current directory. */
1319 dir_data
*d
= (dir_data
*)dirs
->head
;
1321 length
= strlen(path
[d
->index
])+strlen(d
->name
)+6;
1323 length
+= strlen(command
) + 6;
1324 buffer
= wmalloc(length
);
1326 wsyserror(_("out of memory while constructing directory menu %s"),
1331 have_space
= strchr(path
[d
->index
], ' ')!=NULL
||
1332 strchr(d
->name
, ' ')!=NULL
;
1336 strcat(buffer
, path
[d
->index
]);
1338 strcpy(buffer
, path
[d
->index
]);
1340 strcat(buffer
, "/");
1341 strcat(buffer
, d
->name
);
1343 strcat(buffer
, "\"");
1345 strcat(buffer
, " WITH ");
1346 strcat(buffer
, command
);
1349 addMenuEntry(menu
, d
->name
, NULL
, "OPEN_MENU", buffer
, path
[d
->index
]);
1357 list_remove_head(&dirs
);
1360 while (files
!= NULL
) {
1361 /* executable: add as entry */
1362 dir_data
*f
= (dir_data
*) files
->head
;;
1364 length
= strlen(path
[f
->index
])+strlen(f
->name
)+6;
1366 length
+= strlen(command
);
1368 buffer
= wmalloc(length
);
1370 wsyserror(_("out of memory while constructing directory menu %s"),
1375 have_space
= strchr(path
[f
->index
], ' ')!=NULL
||
1376 strchr(f
->name
, ' ')!=NULL
;
1377 if (command
!=NULL
) {
1378 strcpy(buffer
, command
);
1379 strcat(buffer
, " ");
1381 strcat(buffer
, "\"");
1382 strcat(buffer
, path
[f
->index
]);
1387 strcat(buffer
, path
[f
->index
]);
1389 strcpy(buffer
, path
[f
->index
]);
1392 strcat(buffer
, "/");
1393 strcat(buffer
, f
->name
);
1395 strcat(buffer
, "\"");
1397 addMenuEntry(menu
, f
->name
, NULL
, "EXEC", buffer
, path
[f
->index
]);
1405 list_remove_head(&files
);
1412 /************ Menu Configuration From WMRootMenu *************/
1415 makeDefaultMenu(WScreen
*scr
)
1419 menu
= wMenuCreate(scr
, _("Commands"), True
);
1420 wMenuAddCallback(menu
, "XTerm", execCommand
, "xterm");
1421 wMenuAddCallback(menu
, _("Exit..."), exitCommand
, NULL
);
1430 *----------------------------------------------------------------------
1432 * Reads root menu configuration from defaults database.
1434 *----------------------------------------------------------------------
1437 configureMenu(WScreen
*scr
, proplist_t definition
)
1442 proplist_t title
, command
, params
;
1446 if (PLIsString(definition
)) {
1447 struct stat stat_buf
;
1450 /* menu definition is a string. Probably a path, so parse the file */
1452 tmp
= wexpandpath(PLGetString(definition
));
1455 path
= wmalloc(strlen(tmp
)+32);
1459 strcat(path
, Locale
);
1461 /* look for menu.xy */
1462 if (stat(path
, &stat_buf
)<0) {
1466 path
[strlen(path
)-(i
-2)]=0;
1467 /* look for menu.xy_zw */
1468 if (stat(path
, &stat_buf
)<0) {
1470 /* If did not find any localized menus, try
1471 * only menu. This can also mean that
1472 * the path in WMRootMenu was already the
1473 * path for the localized menu (eg: menu = "menu.ab")
1485 path
= wfindfile(DEF_CONFIG_PATHS
, tmp
);
1488 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1494 if (stat(path
, &stat_buf
)<0) {
1495 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path
);
1501 if (!scr
->root_menu
|| stat_buf
.st_mtime
> scr
->root_menu
->timestamp
1502 /* if the pointer in WMRootMenu has changed */
1503 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1504 menu
= readMenuFile(scr
, path
);
1506 menu
->timestamp
= MAX(stat_buf
.st_mtime
, WDRootMenu
->timestamp
);
1516 count
= PLGetNumberOfElements(definition
);
1520 elem
= PLGetArrayElement(definition
, 0);
1521 if (!PLIsString(elem
)) {
1522 tmp
= PLGetDescription(elem
);
1523 wwarning(_("%s:format error in root menu configuration \"%s\""),
1528 mtitle
= PLGetString(elem
);
1530 menu
= wMenuCreate(scr
, mtitle
, False
);
1531 menu
->on_destroy
= removeShortcutsForMenu
;
1533 for (i
=1; i
<count
; i
++) {
1534 elem
= PLGetArrayElement(definition
, i
);
1536 if (!PLIsArray(elem
) || PLGetNumberOfElements(elem
) < 2)
1539 if (PLIsArray(PLGetArrayElement(elem
,1))) {
1544 submenu
= configureMenu(scr
, elem
);
1546 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
,
1548 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1555 title
= PLGetArrayElement(elem
, idx
++);
1556 shortcut
= PLGetArrayElement(elem
, idx
++);
1557 if (strcmp(PLGetString(shortcut
), "SHORTCUT")==0) {
1558 shortcut
= PLGetArrayElement(elem
, idx
++);
1559 command
= PLGetArrayElement(elem
, idx
++);
1564 params
= PLGetArrayElement(elem
, idx
++);
1566 if (!title
|| !command
)
1569 addMenuEntry(menu
, PLGetString(title
),
1570 shortcut
? PLGetString(shortcut
) : NULL
,
1571 PLGetString(command
),
1572 params
? PLGetString(params
) : NULL
, "WMRootMenu");
1577 tmp
= PLGetDescription(elem
);
1578 wwarning(_("%s:format error in root menu configuration \"%s\""),
1594 *----------------------------------------------------------------------
1596 * Opens the root menu, parsing the menu configuration from the
1597 * defaults database.
1598 * If the menu is already mapped and is not sticked to the
1599 * root window, it will be unmapped.
1602 * The menu may be remade.
1605 * Construction of OPEN_MENU entries are delayed to the moment the
1607 *----------------------------------------------------------------------
1610 OpenRootMenu(WScreen
*scr
, int x
, int y
, int keyboard
)
1613 proplist_t definition
;
1615 static proplist_t domain=NULL;
1618 domain = PLMakeString("WMRootMenu");
1622 scr
->flags
.root_menu_changed_shortcuts
= 0;
1623 scr
->flags
.added_workspace_menu
= 0;
1625 if (scr
->root_menu
&& scr
->root_menu
->flags
.mapped
) {
1626 menu
= scr
->root_menu
;
1627 if (!menu
->flags
.buttoned
) {
1630 wRaiseFrame(menu
->frame
->core
);
1633 wMenuMapAt(menu
, 0, 0, True
);
1635 wMenuMapCopyAt(menu
, x
-menu
->frame
->core
->width
/2,
1636 y
-menu
->frame
->top_width
/2);
1642 definition
= WDRootMenu
->dictionary
;
1645 definition = PLGetDomain(domain);
1648 if (PLIsArray(definition
)) {
1650 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1651 menu
= configureMenu(scr
, definition
);
1653 menu
->timestamp
= WDRootMenu
->timestamp
;
1657 menu
= configureMenu(scr
, definition
);
1662 /* menu hasn't changed or could not be read */
1663 if (!scr
->root_menu
) {
1664 menu
= makeDefaultMenu(scr
);
1665 scr
->root_menu
= menu
;
1667 menu
= scr
->root_menu
;
1671 wMenuDestroy(scr
->root_menu
, True
);
1672 scr
->root_menu
= menu
;
1675 wMenuMapAt(menu
, x
-menu
->frame
->core
->width
/2, y
-menu
->frame
->top_width
/2,
1679 if (scr
->flags
.root_menu_changed_shortcuts
)
1680 rebindKeygrabs(scr
);