1 /* rootmenu.c- user defined menu
3 * Window Maker window manager
5 * Copyright (c) 1997, 1998 Alfredo K. Kojima
6 * Copyright (c) 1998 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,
34 #include <sys/types.h>
41 #include <X11/Xutil.h>
42 #include <X11/Xatom.h>
44 #include "WindowMaker.h"
51 #include "workspace.h"
55 #include "xmodifier.h"
63 extern WDDomain
*WDRootMenu
;
65 extern Cursor wCursor
[WCUR_LAST
];
67 extern Time LastTimestamp
;
69 extern WPreferences wPreferences
;
71 extern int wScreenCount
;
73 static WMenu
*readMenuPipe(WScreen
*scr
, char **file_name
);
74 static WMenu
*readMenuFile(WScreen
*scr
, char *file_name
);
75 static WMenu
*readMenuDirectory(WScreen
*scr
, char *title
, char **file_name
,
79 typedef struct Shortcut
{
80 struct Shortcut
*next
;
90 static Shortcut
*shortcutList
= NULL
;
97 * "Title" EXEC command_to_exec -params
99 * "Title" EXEC command_to_exec -params
101 * "Workspaces" WORKSPACE_MENU
102 * "Title" built_in_command
104 * "Quick Quit" EXIT QUICK
107 * Commands may be preceded by SHORTCUT key
111 * INFO_PANEL - shows the Info Panel
112 * LEGAL_PANEL - shows the Legal info panel
113 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
114 * REFRESH - forces the desktop to be repainted
115 * EXIT [QUICK] - exit the window manager [without confirmation]
116 * EXEC <program> - execute an external program
117 * SHEXEC <command> - execute a shell command
118 * WORKSPACE_MENU - places the workspace submenu
120 * RESTART [<window manager>] - restarts the window manager
121 * SHOW_ALL - unhide all windows on workspace
122 * HIDE_OTHERS - hides all windows excep the focused one
123 * OPEN_MENU file - read menu data from file which must be a valid menu file.
124 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
125 * - read menu data from directory(ies) and
126 * eventually precede each with a command.
127 * OPEN_MENU | command
128 * - opens command and uses its stdout to construct and insert
129 * the resulting menu in current position. The output of
130 * command must be a valid menu description.
131 * The space between '|' and command is optional.
132 * SAVE_SESSION - saves the current state of the desktop, which include
133 * all running applications, all their hints (geometry,
134 * position on screen, workspace they live on, the dock
135 * or clip from where they were launched, and
136 * if minimized, shaded or hidden. Also saves the current
137 * workspace the user is on. All will be restored on every
138 * start of windowmaker until another SAVE_SESSION or
139 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
140 * WindowMaker domain file, then saving is automatically
141 * done on every windowmaker exit, overwriting any
142 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
144 * CLEAR_SESSION - clears any previous saved session. This will not have
145 * any effect if SaveSessionOnExit is True.
149 #define MAX(a,b) ((a)>(b) ? (a) : (b))
157 execCommand(WMenu
*menu
, WMenuEntry
*entry
)
161 cmdline
= ExpandOptions(menu
->frame
->screen_ptr
, (char*)entry
->clientdata
);
163 XGrabPointer(dpy
, menu
->frame
->screen_ptr
->root_win
, True
, 0,
164 GrabModeAsync
, GrabModeAsync
, None
, wCursor
[WCUR_WAIT
],
169 ExecuteShellCommand(menu
->frame
->screen_ptr
, cmdline
);
172 XUngrabPointer(dpy
, CurrentTime
);
178 exitCommand(WMenu
*menu
, WMenuEntry
*entry
)
180 static int inside
= 0;
182 /* prevent reentrant calls */
187 if ((long)entry
->clientdata
==M_QUICK
188 || wMessageDialog(menu
->frame
->screen_ptr
, _("Exit"),
189 _("Exit window manager?"),
190 _("Exit"), _("Cancel"), NULL
)==WAPRDefault
) {
192 printf("Exiting WindowMaker.\n");
194 Shutdown(WSExitMode
);
201 shutdownCommand(WMenu
*menu
, WMenuEntry
*entry
)
203 static int inside
= 0;
206 /* prevent reentrant calls */
217 if ((long)entry
->clientdata
==M_QUICK
)
221 if (wSessionIsManaged()) {
224 r
= wMessageDialog(menu
->frame
->screen_ptr
,
225 _("Close X session"),
226 _("Close Window System session?\n"
227 "Kill might close applications with unsaved data."),
228 _("Close"), _("Kill"), _("Cancel"));
231 else if (r
==WAPRAlternate
)
238 r
= wMessageDialog(menu
->frame
->screen_ptr
,
240 _("Kill Window System session?\n"
241 "(all applications will be closed)"),
242 _("Kill"), _("Cancel"), NULL
);
248 if (result
!=R_CANCEL
) {
250 if (result
== R_CLOSE
) {
251 Shutdown(WSLogoutMode
);
253 #endif /* XSMP_ENABLED */
255 Shutdown(WSKillMode
);
266 restartCommand(WMenu
*menu
, WMenuEntry
*entry
)
268 Shutdown(WSRestartPreparationMode
);
269 Restart((char*)entry
->clientdata
, False
);
275 refreshCommand(WMenu
*menu
, WMenuEntry
*entry
)
277 wRefreshDesktop(menu
->frame
->screen_ptr
);
282 arrangeIconsCommand(WMenu
*menu
, WMenuEntry
*entry
)
284 wArrangeIcons(menu
->frame
->screen_ptr
, True
);
288 showAllCommand(WMenu
*menu
, WMenuEntry
*entry
)
290 wShowAllWindows(menu
->frame
->screen_ptr
);
294 hideOthersCommand(WMenu
*menu
, WMenuEntry
*entry
)
296 wHideOtherApplications(menu
->frame
->screen_ptr
->focused_window
);
301 saveSessionCommand(WMenu
*menu
, WMenuEntry
*entry
)
303 if (!wPreferences
.save_session_on_exit
)
304 wSessionSaveState(menu
->frame
->screen_ptr
);
306 wScreenSaveState(menu
->frame
->screen_ptr
);
311 clearSessionCommand(WMenu
*menu
, WMenuEntry
*entry
)
313 wSessionClearState(menu
->frame
->screen_ptr
);
318 infoPanelCommand(WMenu
*menu
, WMenuEntry
*entry
)
320 wShowInfoPanel(menu
->frame
->screen_ptr
);
325 legalPanelCommand(WMenu
*menu
, WMenuEntry
*entry
)
327 wShowLegalPanel(menu
->frame
->screen_ptr
);
331 /********************************************************************/
335 getLocalizedMenuFile(char *menu
)
343 buffer
= wmalloc(strlen(menu
)+32);
345 /* try menu.locale_name */
346 sprintf(buffer
, "%s.%s", menu
, Locale
);
347 if (access(buffer
, F_OK
)==0) {
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(buffer
, '_');
362 if (access(buffer
, F_OK
)==0) {
372 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
]);
388 wRootMenuPerformShortcut(XEvent
*event
)
394 /* ignore CapsLock */
395 modifiers
= event
->xkey
.state
& ValidModMask
;
397 for (ptr
= shortcutList
; ptr
!=NULL
; ptr
= ptr
->next
) {
401 if (ptr
->keycode
==event
->xkey
.keycode
&& (ptr
->modifier
==modifiers
)) {
402 (*ptr
->entry
->callback
)(ptr
->menu
, ptr
->entry
);
411 wRootMenuBindShortcuts(Window window
)
417 if (ptr
->modifier
!=AnyModifier
) {
418 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
|LockMask
,
419 window
, True
, GrabModeAsync
, GrabModeAsync
);
421 wHackedGrabKey(ptr
->keycode
, ptr
->modifier
,
422 window
, True
, GrabModeAsync
, GrabModeAsync
);
425 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
, window
, True
,
426 GrabModeAsync
, GrabModeAsync
);
433 rebindKeygrabs(WScreen
*scr
)
437 wwin
= scr
->focused_window
;
440 XUngrabKey(dpy
, AnyKey
, AnyModifier
, wwin
->frame
->core
->window
);
442 if (!WFLAGP(wwin
, no_bind_keys
)) {
443 wWindowSetKeyGrabs(wwin
);
451 removeShortcutsForMenu(WMenu
*menu
)
454 Shortcut
*newList
= NULL
;
459 if (ptr
->menu
== menu
) {
467 shortcutList
= newList
;
468 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
473 addShortcut(char *file
, char *shortcutDefinition
, WMenu
*menu
,
481 ptr
= wmalloc(sizeof(Shortcut
));
483 strcpy(buf
, shortcutDefinition
);
488 while ((k
= strchr(b
, '+'))!=NULL
) {
492 mod
= wXModifierFromKey(b
);
494 wwarning(_("%s:invalid key modifier \"%s\""), file
, b
);
498 ptr
->modifier
|= mod
;
504 ksym
= XStringToKeysym(b
);
506 if (ksym
==NoSymbol
) {
507 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
508 file
, shortcutDefinition
, entry
->text
);
513 ptr
->keycode
= XKeysymToKeycode(dpy
, ksym
);
514 if (ptr
->keycode
==0) {
515 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file
,
516 shortcutDefinition
, entry
->text
);
524 ptr
->next
= shortcutList
;
527 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
533 /*******************************/
543 end
= &(line
[strlen(line
)])-1;
544 while (isspace(*line
) && *line
!=0) line
++;
545 while (end
>line
&& isspace(*end
)) {
554 next_token(char *line
, char **next
)
560 while (*line
==' ' || *line
=='\t') line
++;
566 while (*tmp
!=0 && *tmp
!='"') tmp
++;
568 wwarning(_("%s: unmatched '\"' in menu file"), line
);
579 } while (*tmp
!=0 && *tmp
!=' ' && *tmp
!='\t');
593 while (*tmp
==' ' || *tmp
=='\t') tmp
++;
603 separateCommand(char *line
, char ***file
, char **command
)
605 char *token
, *tmp
= line
;
606 LinkedList
*list
= NULL
;
612 token
= next_token(tmp
, &tmp
);
614 if (strcmp(token
, "WITH")==0) {
615 if (tmp
!=NULL
&& *tmp
!=0)
616 *command
= wstrdup(tmp
);
618 wwarning(_("%s: missing command"), line
);
621 list
= list_cons(token
, list
);
623 } while (token
!=NULL
&& tmp
!=NULL
);
625 count
= list_length(list
);
627 *file
= wmalloc(sizeof(char*)*(count
+1));
629 (*file
)[count
] = NULL
;
631 (*file
)[--i
] = list
->head
;
632 list_remove_head(&list
);
639 constructMenu(WMenu
*menu
, WMenuEntry
*entry
)
642 struct stat stat_buf
;
649 separateCommand((char*)entry
->clientdata
, &path
, &cmd
);
650 if (!path
|| *path
==NULL
|| **path
==0) {
651 wwarning(_("invalid OPEN_MENU specification: %s"),
652 (char*)entry
->clientdata
);
656 if (path
[0][0]=='|') {
659 if (!menu
->cascades
[entry
->cascade
] ||
660 menu
->cascades
[entry
->cascade
]->timestamp
== 0) {
663 submenu
= readMenuPipe(menu
->frame
->screen_ptr
, path
);
665 /* there's no automatic reloading */
667 submenu
->timestamp
= 1;
674 while(path
[i
] != NULL
) {
677 if (strcmp(path
[i
], "-noext")==0) {
682 tmp
= wexpandpath(path
[i
]);
684 lpath
= getLocalizedMenuFile(tmp
);
693 if (stat(path
[i
], &stat_buf
)==0) {
694 if (last
< stat_buf
.st_mtime
)
695 last
= stat_buf
.st_mtime
;
699 wsyserror(_("%s:could not stat menu"), path
[i
]);
707 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
708 (char*)entry
->clientdata
);
711 stat(path
[first
], &stat_buf
);
712 if (!menu
->cascades
[entry
->cascade
]
713 || menu
->cascades
[entry
->cascade
]->timestamp
< last
) {
715 if (S_ISDIR(stat_buf
.st_mode
)) {
717 submenu
= readMenuDirectory(menu
->frame
->screen_ptr
,
718 entry
->text
, path
, cmd
);
720 submenu
->timestamp
= last
;
721 } else if (S_ISREG(stat_buf
.st_mode
)) {
725 wwarning(_("too many parameters in OPEN_MENU: %s"),
726 (char*)entry
->clientdata
);
728 submenu
= readMenuFile(menu
->frame
->screen_ptr
, path
[first
]);
730 submenu
->timestamp
= stat_buf
.st_mtime
;
740 wMenuEntryRemoveCascade(menu
, entry
);
741 wMenuEntrySetCascade(menu
, entry
, submenu
);
746 while (path
[i
]!=NULL
)
755 addWorkspaceMenu(WScreen
*scr
, WMenu
*menu
, char *title
)
760 if (scr
->flags
.added_workspace_menu
) {
761 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
764 scr
->flags
.added_workspace_menu
= 1;
766 wsmenu
= wWorkspaceMenuMake(scr
, True
);
767 scr
->workspace_menu
= wsmenu
;
768 entry
= wMenuAddCallback(menu
, title
, NULL
, NULL
);
769 wMenuEntrySetCascade(menu
, entry
, wsmenu
);
771 wWorkspaceMenuUpdate(scr
, wsmenu
);
778 addMenuEntry(WMenu
*menu
, char *title
, char *shortcut
, char *command
,
779 char *params
, char *file_name
)
782 WMenuEntry
*entry
= NULL
;
783 Bool shortcutOk
= False
;
787 scr
= menu
->frame
->screen_ptr
;
788 if (strcmp(command
, "OPEN_MENU")==0) {
790 wwarning(_("%s:missing parameter for menu command \"%s\""),
796 path
= wfindfile(DEF_CONFIG_PATHS
, params
);
798 path
= wstrdup(params
);
800 dummy
= wMenuCreate(scr
, title
, False
);
801 dummy
->on_destroy
= removeShortcutsForMenu
;
802 entry
= wMenuAddCallback(menu
, title
, constructMenu
, path
);
803 entry
->free_cdata
= free
;
804 wMenuEntrySetCascade(menu
, entry
, dummy
);
806 } else if (strcmp(command
, "EXEC")==0) {
808 wwarning(_("%s:missing parameter for menu command \"%s\""),
811 entry
= wMenuAddCallback(menu
, title
, execCommand
,
812 wstrappend("exec ", params
));
813 entry
->free_cdata
= free
;
816 } else if (strcmp(command
, "SHEXEC")==0) {
818 wwarning(_("%s:missing parameter for menu command \"%s\""),
821 entry
= wMenuAddCallback(menu
, title
, execCommand
,
823 entry
->free_cdata
= free
;
826 } else if (strcmp(command
, "EXIT")==0) {
828 if (params
&& strcmp(params
, "QUICK")==0)
829 entry
= wMenuAddCallback(menu
, title
, exitCommand
, (void*)M_QUICK
);
831 entry
= wMenuAddCallback(menu
, title
, exitCommand
, NULL
);
834 } else if (strcmp(command
, "SHUTDOWN")==0) {
836 if (params
&& strcmp(params
, "QUICK")==0)
837 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
,
840 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
, NULL
);
843 } else if (strcmp(command
, "REFRESH")==0) {
844 entry
= wMenuAddCallback(menu
, title
, refreshCommand
, NULL
);
847 } else if (strcmp(command
, "WORKSPACE_MENU")==0) {
848 entry
= addWorkspaceMenu(scr
, menu
, title
);
851 } else if (strcmp(command
, "ARRANGE_ICONS")==0) {
852 entry
= wMenuAddCallback(menu
, title
, arrangeIconsCommand
, NULL
);
855 } else if (strcmp(command
, "HIDE_OTHERS")==0) {
856 entry
= wMenuAddCallback(menu
, title
, hideOthersCommand
, NULL
);
859 } else if (strcmp(command
, "SHOW_ALL")==0) {
860 entry
= wMenuAddCallback(menu
, title
, showAllCommand
, NULL
);
863 } else if (strcmp(command
, "RESTART")==0) {
864 entry
= wMenuAddCallback(menu
, title
, restartCommand
,
865 params
? wstrdup(params
) : NULL
);
866 entry
->free_cdata
= free
;
868 } else if (strcmp(command
, "SAVE_SESSION")==0) {
869 entry
= wMenuAddCallback(menu
, title
, saveSessionCommand
, NULL
);
872 } else if (strcmp(command
, "CLEAR_SESSION")==0) {
873 entry
= wMenuAddCallback(menu
, title
, clearSessionCommand
, NULL
);
875 } else if (strcmp(command
, "INFO_PANEL")==0) {
876 entry
= wMenuAddCallback(menu
, title
, infoPanelCommand
, NULL
);
878 } else if (strcmp(command
, "LEGAL_PANEL")==0) {
879 entry
= wMenuAddCallback(menu
, title
, legalPanelCommand
, NULL
);
882 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name
,
888 if (shortcut
&& entry
) {
890 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name
,
893 if (addShortcut(file_name
, shortcut
, menu
, entry
)) {
895 entry
->rtext
= GetShortcutString(shortcut
);
897 entry->rtext = wstrdup(shortcut);
908 /******************* Menu Configuration From File *******************/
911 separateline(char *line
, char *title
, char *command
, char *parameter
,
923 while (isspace(*line
) && (*line
!=0)) line
++;
927 while (line
[i
]!='"' && (line
[i
]!=0)) i
++;
932 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
934 strncpy(title
, line
, i
);
938 /* get the command or shortcut keyword */
939 while (isspace(*line
) && (*line
!=0)) line
++;
943 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
944 strncpy(command
, line
, i
);
948 if (strcmp(command
, "SHORTCUT")==0) {
949 /* get the shortcut key */
950 while (isspace(*line
) && (*line
!=0)) line
++;
954 while (line
[i
]!='"' && (line
[i
]!=0)) i
++;
959 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
961 strncpy(shortcut
, line
, i
);
967 /* get the command */
968 while (isspace(*line
) && (*line
!=0)) line
++;
972 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
973 strncpy(command
, line
, i
);
978 /* get the parameters */
979 while (isspace(*line
) && (*line
!=0)) line
++;
986 while (line
[l
]!=0 && line
[l
]!='"') {
987 parameter
[l
] = line
[l
];
995 while (isspace(line
[l
]) && (l
>0)) l
--;
996 strncpy(parameter
, line
, l
);
1002 parseCascade(WScreen
*scr
, WMenu
*menu
, FILE *file
, char *file_name
)
1004 char linebuf
[MAXLINE
];
1005 char elinebuf
[MAXLINE
];
1006 char title
[MAXLINE
];
1007 char command
[MAXLINE
];
1008 char shortcut
[MAXLINE
];
1009 char params
[MAXLINE
];
1012 while (!IsEof(file
)) {
1016 fgets(linebuf
, MAXLINE
, file
);
1017 line
= cropline(linebuf
);
1018 lsize
= strlen(line
);
1020 if (line
[lsize
-1]=='\\') {
1023 fgets(elinebuf
, MAXLINE
, file
);
1024 line2
=cropline(elinebuf
);
1025 lsize2
=strlen(line2
);
1026 if (lsize2
+lsize
>MAXLINE
) {
1027 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1033 strcat(line
, line2
);
1038 } while (!ok
&& !IsEof(file
));
1042 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1046 separateline(line
, title
, command
, params
, shortcut
);
1049 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1054 if (strcasecmp(command
, "MENU")==0) {
1059 cascade
= wMenuCreate(scr
, title
, False
);
1060 cascade
->on_destroy
= removeShortcutsForMenu
;
1061 if (parseCascade(scr
, cascade
, file
, file_name
)==NULL
) {
1062 wMenuDestroy(cascade
, True
);
1064 wMenuEntrySetCascade(menu
,
1065 wMenuAddCallback(menu
, title
, NULL
, NULL
),
1068 } else if (strcasecmp(command
, "END")==0) {
1074 addMenuEntry(menu
, title
, shortcut
[0] ? shortcut
: NULL
, command
,
1075 params
[0] ? params
: NULL
, file_name
);
1079 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1089 readMenuFile(WScreen
*scr
, char *file_name
)
1093 char linebuf
[MAXLINE
];
1094 char title
[MAXLINE
];
1095 char shortcut
[MAXLINE
];
1096 char command
[MAXLINE
];
1097 char params
[MAXLINE
];
1105 if (!wPreferences
.flags
.nocpp
) {
1106 args
= MakeCPPArgs(file_name
);
1108 wwarning(_("could not make arguments for menu file preprocessor"));
1110 sprintf(command
, "%s %s %s", CPP_PATH
, args
, file_name
);
1112 file
= popen(command
, "r");
1114 wsyserror(_("%s:could not open/preprocess menu file"),
1124 file
= fopen(file_name
, "r");
1126 wsyserror(_("%s:could not open menu file"), file_name
);
1131 while (!IsEof(file
)) {
1132 if (!fgets(linebuf
, MAXLINE
, file
))
1134 line
= cropline(linebuf
);
1135 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1138 separateline(line
, title
, command
, params
, shortcut
);
1141 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1145 if (strcasecmp(command
, "MENU")==0) {
1146 menu
= wMenuCreate(scr
, title
, True
);
1147 menu
->on_destroy
= removeShortcutsForMenu
;
1148 if (!parseCascade(scr
, menu
, file
, file_name
)) {
1149 wMenuDestroy(menu
, True
);
1153 wwarning(_("%s:invalid menu file. MENU command is missing"),
1161 if (pclose(file
)==-1) {
1162 wsyserror(_("error reading preprocessed menu data"));
1174 /************ Menu Configuration From Pipe *************/
1177 readMenuPipe(WScreen
*scr
, char **file_name
)
1181 char linebuf
[MAXLINE
];
1182 char title
[MAXLINE
];
1183 char command
[MAXLINE
];
1184 char params
[MAXLINE
];
1185 char shortcut
[MAXLINE
];
1188 char flat_file
[MAXLINE
];
1195 flat_file
[0] = '\0';
1197 for(i
= 0 ; file_name
[i
] != NULL
; i
++) {
1198 strcat(flat_file
, file_name
[i
]);
1199 strcat(flat_file
, " ");
1201 filename
= flat_file
+1;
1204 if (!wPreferences
.flags
.nocpp
) {
1205 args
= MakeCPPArgs(filename
);
1207 wwarning(_("could not make arguments for menu file preprocessor"));
1209 sprintf(command
, "%s | %s %s", filename
, CPP_PATH
, args
);
1212 file
= popen(command
, "r");
1214 wsyserror(_("%s:could not open/preprocess menu file"), filename
);
1224 file
= popen(filename
, "r");
1227 wsyserror(_("%s:could not open menu file"), filename
);
1232 while (!IsEof(file
)) {
1233 if (!fgets(linebuf
, MAXLINE
, file
))
1235 line
= cropline(linebuf
);
1236 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1239 separateline(line
, title
, command
, params
, shortcut
);
1242 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1246 if (strcasecmp(command
, "MENU")==0) {
1247 menu
= wMenuCreate(scr
, title
, True
);
1248 menu
->on_destroy
= removeShortcutsForMenu
;
1249 if (!parseCascade(scr
, menu
, file
, filename
)) {
1250 wMenuDestroy(menu
, True
);
1254 wwarning(_("%s:no title given for the root menu"), filename
);
1273 myCompare(dir_data
*d1
, dir_data
*d2
)
1275 return strcmp(d1
->name
, d2
->name
);
1279 /************ Menu Configuration From Directory *************/
1282 isFilePackage(char *file
)
1286 /* check if the extension indicates this file is a
1287 * file package. For now, only recognize .themed */
1291 if (l
> 7 && strcmp(&(file
[l
-7]), ".themed")==0) {
1300 readMenuDirectory(WScreen
*scr
, char *title
, char **path
, char *command
)
1303 struct dirent
*dentry
;
1304 struct stat stat_buf
;
1307 LinkedList
*dirs
= NULL
, *files
= NULL
;
1308 int length
, i
, have_space
=0;
1310 int stripExtension
= 0;
1313 while (path
[i
]!=NULL
) {
1314 if (strcmp(path
[i
], "-noext")==0) {
1320 dir
= opendir(path
[i
]);
1326 while ((dentry
= readdir(dir
))) {
1328 if (strcmp(dentry
->d_name
, ".")==0 ||
1329 strcmp(dentry
->d_name
, "..")==0)
1332 if (dentry
->d_name
[0] == '.')
1335 buffer
= wmalloc(strlen(path
[i
])+strlen(dentry
->d_name
)+4);
1337 wsyserror(_("out of memory while constructing directory menu %s"),
1342 strcpy(buffer
, path
[i
]);
1343 strcat(buffer
, "/");
1344 strcat(buffer
, dentry
->d_name
);
1346 if (stat(buffer
, &stat_buf
)!=0) {
1347 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1348 path
[i
], dentry
->d_name
);
1350 Bool isFilePack
= False
;
1353 if (S_ISDIR(stat_buf
.st_mode
)
1354 && !(isFilePack
= isFilePackage(dentry
->d_name
))) {
1356 /* access always returns success for user root */
1357 if (access(buffer
, X_OK
)==0) {
1358 /* Directory is accesible. Add to directory list */
1360 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1361 data
->name
= wstrdup(dentry
->d_name
);
1364 list_insert_sorted(data
, &dirs
, (int(*)())myCompare
);
1366 } else if (S_ISREG(stat_buf
.st_mode
) || isFilePack
) {
1367 /* Hack because access always returns X_OK success for user root */
1368 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1369 if ((command
!=NULL
&& access(buffer
, R_OK
)==0) ||
1370 (command
==NULL
&& access(buffer
, X_OK
)==0 &&
1371 (stat_buf
.st_mode
& S_IXANY
))) {
1373 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1374 data
->name
= wstrdup(dentry
->d_name
);
1377 list_insert_sorted(data
, &files
, (int(*)())myCompare
);
1388 if (!dirs
&& !files
)
1391 menu
= wMenuCreate(scr
, title
, False
);
1392 menu
->on_destroy
= removeShortcutsForMenu
;
1394 while (dirs
!= NULL
) {
1395 /* New directory. Use same OPEN_MENU command that was used
1396 * for the current directory. */
1397 dir_data
*d
= (dir_data
*)dirs
->head
;
1399 length
= strlen(path
[d
->index
])+strlen(d
->name
)+6;
1401 length
+= strlen(command
) + 6;
1402 buffer
= wmalloc(length
);
1404 wsyserror(_("out of memory while constructing directory menu %s"),
1409 have_space
= strchr(path
[d
->index
], ' ')!=NULL
||
1410 strchr(d
->name
, ' ')!=NULL
;
1414 strcat(buffer
, path
[d
->index
]);
1416 strcpy(buffer
, path
[d
->index
]);
1418 strcat(buffer
, "/");
1419 strcat(buffer
, d
->name
);
1421 strcat(buffer
, "\"");
1423 strcat(buffer
, " WITH ");
1424 strcat(buffer
, command
);
1427 addMenuEntry(menu
, d
->name
, NULL
, "OPEN_MENU", buffer
, path
[d
->index
]);
1435 list_remove_head(&dirs
);
1438 while (files
!= NULL
) {
1439 /* executable: add as entry */
1440 dir_data
*f
= (dir_data
*) files
->head
;;
1442 length
= strlen(path
[f
->index
])+strlen(f
->name
)+6;
1444 length
+= strlen(command
);
1446 buffer
= wmalloc(length
);
1448 wsyserror(_("out of memory while constructing directory menu %s"),
1453 have_space
= strchr(path
[f
->index
], ' ')!=NULL
||
1454 strchr(f
->name
, ' ')!=NULL
;
1455 if (command
!=NULL
) {
1456 strcpy(buffer
, command
);
1457 strcat(buffer
, " ");
1459 strcat(buffer
, "\"");
1460 strcat(buffer
, path
[f
->index
]);
1465 strcat(buffer
, path
[f
->index
]);
1467 strcpy(buffer
, path
[f
->index
]);
1470 strcat(buffer
, "/");
1471 strcat(buffer
, f
->name
);
1473 strcat(buffer
, "\"");
1475 if (stripExtension
) {
1476 char *ptr
= strrchr(f
->name
, '.');
1477 if (ptr
&& ptr
!=f
->name
)
1480 addMenuEntry(menu
, f
->name
, NULL
, "EXEC", buffer
, path
[f
->index
]);
1488 list_remove_head(&files
);
1495 /************ Menu Configuration From WMRootMenu *************/
1498 makeDefaultMenu(WScreen
*scr
)
1502 menu
= wMenuCreate(scr
, _("Commands"), True
);
1503 wMenuAddCallback(menu
, "XTerm", execCommand
, "xterm");
1504 wMenuAddCallback(menu
, "rxvt", execCommand
, "rxvt");
1505 wMenuAddCallback(menu
, _("Restart"), restartCommand
, NULL
);
1506 wMenuAddCallback(menu
, _("Exit..."), exitCommand
, NULL
);
1515 *----------------------------------------------------------------------
1517 * Reads root menu configuration from defaults database.
1519 *----------------------------------------------------------------------
1522 configureMenu(WScreen
*scr
, proplist_t definition
)
1527 proplist_t title
, command
, params
;
1531 if (PLIsString(definition
)) {
1532 struct stat stat_buf
;
1534 Bool menu_is_default
= False
;
1536 /* menu definition is a string. Probably a path, so parse the file */
1538 tmp
= wexpandpath(PLGetString(definition
));
1540 path
= getLocalizedMenuFile(tmp
);
1543 path
= wfindfile(DEF_CONFIG_PATHS
, tmp
);
1546 path
= wfindfile(DEF_CONFIG_PATHS
, DEF_MENU_FILE
);
1547 menu_is_default
= True
;
1551 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1557 if (stat(path
, &stat_buf
)<0) {
1558 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path
);
1564 if (!scr
->root_menu
|| stat_buf
.st_mtime
> scr
->root_menu
->timestamp
1565 /* if the pointer in WMRootMenu has changed */
1566 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1568 if (menu_is_default
) {
1569 wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1573 menu
= readMenuFile(scr
, path
);
1575 menu
->timestamp
= MAX(stat_buf
.st_mtime
, WDRootMenu
->timestamp
);
1585 count
= PLGetNumberOfElements(definition
);
1589 elem
= PLGetArrayElement(definition
, 0);
1590 if (!PLIsString(elem
)) {
1591 tmp
= PLGetDescription(elem
);
1592 wwarning(_("%s:format error in root menu configuration \"%s\""),
1597 mtitle
= PLGetString(elem
);
1599 menu
= wMenuCreate(scr
, mtitle
, False
);
1600 menu
->on_destroy
= removeShortcutsForMenu
;
1602 #ifdef GLOBAL_SUBMENU_FILE
1607 submenu
= readMenuFile(scr
, GLOBAL_SUBMENU_FILE
);
1610 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
, NULL
);
1611 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1616 for (i
=1; i
<count
; i
++) {
1617 elem
= PLGetArrayElement(definition
, i
);
1619 if (PLIsString(elem
)) {
1622 file
= PLGetString(elem
);
1626 if (!PLIsArray(elem
) || PLGetNumberOfElements(elem
) < 2)
1629 if (PLIsArray(PLGetArrayElement(elem
,1))) {
1634 submenu
= configureMenu(scr
, elem
);
1636 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
,
1638 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1645 title
= PLGetArrayElement(elem
, idx
++);
1646 shortcut
= PLGetArrayElement(elem
, idx
++);
1647 if (strcmp(PLGetString(shortcut
), "SHORTCUT")==0) {
1648 shortcut
= PLGetArrayElement(elem
, idx
++);
1649 command
= PLGetArrayElement(elem
, idx
++);
1654 params
= PLGetArrayElement(elem
, idx
++);
1656 if (!title
|| !command
)
1659 addMenuEntry(menu
, PLGetString(title
),
1660 shortcut
? PLGetString(shortcut
) : NULL
,
1661 PLGetString(command
),
1662 params
? PLGetString(params
) : NULL
, "WMRootMenu");
1667 tmp
= PLGetDescription(elem
);
1668 wwarning(_("%s:format error in root menu configuration \"%s\""),
1684 *----------------------------------------------------------------------
1686 * Opens the root menu, parsing the menu configuration from the
1687 * defaults database.
1688 * If the menu is already mapped and is not sticked to the
1689 * root window, it will be unmapped.
1692 * The menu may be remade.
1695 * Construction of OPEN_MENU entries are delayed to the moment the
1697 *----------------------------------------------------------------------
1700 OpenRootMenu(WScreen
*scr
, int x
, int y
, int keyboard
)
1703 proplist_t definition
;
1705 static proplist_t domain=NULL;
1708 domain = PLMakeString("WMRootMenu");
1712 scr
->flags
.root_menu_changed_shortcuts
= 0;
1713 scr
->flags
.added_workspace_menu
= 0;
1715 if (scr
->root_menu
&& scr
->root_menu
->flags
.mapped
) {
1716 menu
= scr
->root_menu
;
1717 if (!menu
->flags
.buttoned
) {
1720 wRaiseFrame(menu
->frame
->core
);
1723 wMenuMapAt(menu
, 0, 0, True
);
1725 wMenuMapCopyAt(menu
, x
-menu
->frame
->core
->width
/2, y
);
1731 definition
= WDRootMenu
->dictionary
;
1734 definition = PLGetDomain(domain);
1737 if (PLIsArray(definition
)) {
1739 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1740 menu
= configureMenu(scr
, definition
);
1742 menu
->timestamp
= WDRootMenu
->timestamp
;
1747 menu
= configureMenu(scr
, definition
);
1752 /* menu hasn't changed or could not be read */
1753 if (!scr
->root_menu
) {
1754 wMessageDialog(scr
, _("Error"),
1755 _("The applications menu could not be loaded."
1756 "Look at the console output for a detailed"
1757 "description of the errors"),
1758 _("OK"), NULL
, NULL
);
1760 menu
= makeDefaultMenu(scr
);
1761 scr
->root_menu
= menu
;
1763 menu
= scr
->root_menu
;
1767 wMenuDestroy(scr
->root_menu
, True
);
1768 scr
->root_menu
= menu
;
1771 wMenuMapAt(menu
, x
-menu
->frame
->core
->width
/2, y
, keyboard
);
1774 if (scr
->flags
.root_menu_changed_shortcuts
)
1775 rebindKeygrabs(scr
);