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>
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
,
78 typedef struct Shortcut
{
79 struct Shortcut
*next
;
89 static Shortcut
*shortcutList
= NULL
;
96 * "Title" EXEC command_to_exec -params
98 * "Title" EXEC command_to_exec -params
100 * "Workspaces" WORKSPACE_MENU
101 * "Title" built_in_command
103 * "Quick Quit" EXIT QUICK
106 * Commands may be preceded by SHORTCUT key
110 * INFO_PANEL - shows the Info Panel
111 * LEGAL_PANEL - shows the Legal info panel
112 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
113 * REFRESH - forces the desktop to be repainted
114 * EXIT [QUICK] - exit the window manager [without confirmation]
115 * EXEC <program> - execute an external program
116 * SHEXEC <command> - execute a shell command
117 * WORKSPACE_MENU - places the workspace submenu
119 * RESTART [<window manager>] - restarts the window manager
120 * SHOW_ALL - unhide all windows on workspace
121 * HIDE_OTHERS - hides all windows excep the focused one
122 * OPEN_MENU file - read menu data from file which must be a valid menu file.
123 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
124 * - read menu data from directory(ies) and
125 * eventually precede each with a command.
126 * OPEN_MENU | command
127 * - opens command and uses its stdout to construct and insert
128 * the resulting menu in current position. The output of
129 * command must be a valid menu description.
130 * The space between '|' and command is optional.
131 * || will do the same, but will not cache the contents.
132 * SAVE_SESSION - saves the current state of the desktop, which include
133 * all running applications, all their hints (geometry,
134 * position on screen, workspace they live on, the dock
135 * or clip from where they were launched, and
136 * if minimized, shaded or hidden. Also saves the current
137 * workspace the user is on. All will be restored on every
138 * start of windowmaker until another SAVE_SESSION or
139 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
140 * WindowMaker domain file, then saving is automatically
141 * done on every windowmaker exit, overwriting any
142 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
144 * CLEAR_SESSION - clears any previous saved session. This will not have
145 * any effect if SaveSessionOnExit is True.
154 execCommand(WMenu
*menu
, WMenuEntry
*entry
)
158 cmdline
= ExpandOptions(menu
->frame
->screen_ptr
, (char*)entry
->clientdata
);
160 XGrabPointer(dpy
, menu
->frame
->screen_ptr
->root_win
, True
, 0,
161 GrabModeAsync
, GrabModeAsync
, None
, wCursor
[WCUR_WAIT
],
166 ExecuteShellCommand(menu
->frame
->screen_ptr
, cmdline
);
169 XUngrabPointer(dpy
, CurrentTime
);
175 exitCommand(WMenu
*menu
, WMenuEntry
*entry
)
177 static int inside
= 0;
180 /* prevent reentrant calls */
190 if ((long)entry
->clientdata
==M_QUICK
) {
193 int r
, oldSaveSessionFlag
;
195 oldSaveSessionFlag
= wPreferences
.save_session_on_exit
;
196 r
= wExitDialog(menu
->frame
->screen_ptr
, _("Exit"),
197 _("Exit window manager?"),
198 _("Exit"), _("Cancel"), NULL
);
200 if (r
==WAPRDefault
) {
202 } else if (r
==WAPRAlternate
) {
203 /* Don't modify the "save session on exit" flag if the
204 * user canceled the operation. */
205 wPreferences
.save_session_on_exit
= oldSaveSessionFlag
;
208 if (result
==R_EXIT
) {
210 printf("Exiting WindowMaker.\n");
212 Shutdown(WSExitMode
);
221 shutdownCommand(WMenu
*menu
, WMenuEntry
*entry
)
223 static int inside
= 0;
226 /* prevent reentrant calls */
237 if ((long)entry
->clientdata
==M_QUICK
)
241 if (wSessionIsManaged()) {
244 r
= wMessageDialog(menu
->frame
->screen_ptr
,
245 _("Close X session"),
246 _("Close Window System session?\n"
247 "Kill might close applications with unsaved data."),
248 _("Close"), _("Kill"), _("Cancel"));
251 else if (r
==WAPRAlternate
)
256 int r
, oldSaveSessionFlag
;
258 oldSaveSessionFlag
= wPreferences
.save_session_on_exit
;
260 r
= wExitDialog(menu
->frame
->screen_ptr
,
262 _("Kill Window System session?\n"
263 "(all applications will be closed)"),
264 _("Kill"), _("Cancel"), NULL
);
265 if (r
==WAPRDefault
) {
267 } else if (r
==WAPRAlternate
) {
268 /* Don't modify the "save session on exit" flag if the
269 * user canceled the operation. */
270 wPreferences
.save_session_on_exit
= oldSaveSessionFlag
;
275 if (result
!=R_CANCEL
) {
277 if (result
== R_CLOSE
) {
278 Shutdown(WSLogoutMode
);
280 #endif /* XSMP_ENABLED */
282 Shutdown(WSKillMode
);
293 restartCommand(WMenu
*menu
, WMenuEntry
*entry
)
295 Shutdown(WSRestartPreparationMode
);
296 Restart((char*)entry
->clientdata
, False
);
302 refreshCommand(WMenu
*menu
, WMenuEntry
*entry
)
304 wRefreshDesktop(menu
->frame
->screen_ptr
);
309 arrangeIconsCommand(WMenu
*menu
, WMenuEntry
*entry
)
311 wArrangeIcons(menu
->frame
->screen_ptr
, True
);
315 showAllCommand(WMenu
*menu
, WMenuEntry
*entry
)
317 wShowAllWindows(menu
->frame
->screen_ptr
);
321 hideOthersCommand(WMenu
*menu
, WMenuEntry
*entry
)
323 wHideOtherApplications(menu
->frame
->screen_ptr
->focused_window
);
328 saveSessionCommand(WMenu
*menu
, WMenuEntry
*entry
)
330 if (!wPreferences
.save_session_on_exit
)
331 wSessionSaveState(menu
->frame
->screen_ptr
);
333 wScreenSaveState(menu
->frame
->screen_ptr
);
338 clearSessionCommand(WMenu
*menu
, WMenuEntry
*entry
)
340 wSessionClearState(menu
->frame
->screen_ptr
);
341 wScreenSaveState(menu
->frame
->screen_ptr
);
346 infoPanelCommand(WMenu
*menu
, WMenuEntry
*entry
)
348 wShowInfoPanel(menu
->frame
->screen_ptr
);
353 legalPanelCommand(WMenu
*menu
, WMenuEntry
*entry
)
355 wShowLegalPanel(menu
->frame
->screen_ptr
);
359 /********************************************************************/
363 getLocalizedMenuFile(char *menu
)
365 char *buffer
, *ptr
, *locale
;
371 len
= strlen(menu
)+strlen(Locale
)+8;
372 buffer
= wmalloc(len
);
374 /* try menu.locale_name */
375 snprintf(buffer
, len
, "%s.%s", menu
, Locale
);
376 if (access(buffer
, F_OK
)==0) {
380 /* position of locale in our buffer */
381 locale
= buffer
+ strlen(menu
) + 1;
383 /* check if it is in the form aa_bb.encoding and check for aa_bb */
384 ptr
= strchr(locale
, '.');
387 if (access(buffer
, F_OK
)==0) {
391 /* now check for aa */
392 ptr
= strchr(locale
, '_');
395 if (access(buffer
, F_OK
)==0) {
407 raiseMenus(WMenu
*menu
)
411 if (menu
->flags
.mapped
) {
412 wRaiseFrame(menu
->frame
->core
);
414 for (i
=0; i
<menu
->cascade_no
; i
++) {
415 if (menu
->cascades
[i
])
416 raiseMenus(menu
->cascades
[i
]);
423 wRootMenuPerformShortcut(XEvent
*event
)
425 WScreen
*scr
= wScreenForRootWindow(event
->xkey
.root
);
430 /* ignore CapsLock */
431 modifiers
= event
->xkey
.state
& ValidModMask
;
433 for (ptr
= shortcutList
; ptr
!=NULL
; ptr
= ptr
->next
) {
434 if (ptr
->keycode
==0 || ptr
->menu
->menu
->screen_ptr
!=scr
)
437 if (ptr
->keycode
==event
->xkey
.keycode
&& ptr
->modifier
==modifiers
) {
438 (*ptr
->entry
->callback
)(ptr
->menu
, ptr
->entry
);
448 wRootMenuBindShortcuts(Window window
)
454 if (ptr
->modifier
!=AnyModifier
) {
455 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
|LockMask
,
456 window
, True
, GrabModeAsync
, GrabModeAsync
);
458 wHackedGrabKey(ptr
->keycode
, ptr
->modifier
,
459 window
, True
, GrabModeAsync
, GrabModeAsync
);
462 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
, window
, True
,
463 GrabModeAsync
, GrabModeAsync
);
470 rebindKeygrabs(WScreen
*scr
)
474 wwin
= scr
->focused_window
;
477 XUngrabKey(dpy
, AnyKey
, AnyModifier
, wwin
->frame
->core
->window
);
479 if (!WFLAGP(wwin
, no_bind_keys
)) {
480 wWindowSetKeyGrabs(wwin
);
488 removeShortcutsForMenu(WMenu
*menu
)
491 Shortcut
*newList
= NULL
;
496 if (ptr
->menu
== menu
) {
504 shortcutList
= newList
;
505 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
510 addShortcut(char *file
, char *shortcutDefinition
, WMenu
*menu
,
518 ptr
= wmalloc(sizeof(Shortcut
));
520 strcpy(buf
, shortcutDefinition
);
525 while ((k
= strchr(b
, '+'))!=NULL
) {
529 mod
= wXModifierFromKey(b
);
531 wwarning(_("%s: invalid key modifier \"%s\""), file
, b
);
535 ptr
->modifier
|= mod
;
541 ksym
= XStringToKeysym(b
);
543 if (ksym
==NoSymbol
) {
544 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
545 file
, shortcutDefinition
, entry
->text
);
550 ptr
->keycode
= XKeysymToKeycode(dpy
, ksym
);
551 if (ptr
->keycode
==0) {
552 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file
,
553 shortcutDefinition
, entry
->text
);
561 ptr
->next
= shortcutList
;
564 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
570 /*******************************/
580 end
= &(line
[strlen(line
)])-1;
581 while (isspace(*line
) && *line
!=0) line
++;
582 while (end
>line
&& isspace(*end
)) {
591 next_token(char *line
, char **next
)
597 while (*line
==' ' || *line
=='\t') line
++;
603 while (*tmp
!=0 && *tmp
!='"') tmp
++;
605 wwarning(_("%s: unmatched '\"' in menu file"), line
);
616 } while (*tmp
!=0 && *tmp
!=' ' && *tmp
!='\t');
630 while (*tmp
==' ' || *tmp
=='\t') tmp
++;
640 separateCommand(char *line
, char ***file
, char **command
)
642 char *token
, *tmp
= line
;
643 WMArray
*array
= WMCreateArray(4);
649 token
= next_token(tmp
, &tmp
);
651 if (strcmp(token
, "WITH")==0) {
652 if (tmp
!=NULL
&& *tmp
!=0)
653 *command
= wstrdup(tmp
);
655 wwarning(_("%s: missing command"), line
);
658 WMAddToArray(array
, token
);
660 } while (token
!=NULL
&& tmp
!=NULL
);
662 count
= WMGetArrayItemCount(array
);
664 *file
= wmalloc(sizeof(char*)*(count
+1));
665 (*file
)[count
] = NULL
;
666 for (i
= 0; i
< count
; i
++) {
667 (*file
)[i
] = WMGetFromArray(array
, i
);
675 constructMenu(WMenu
*menu
, WMenuEntry
*entry
)
678 struct stat stat_buf
;
685 separateCommand((char*)entry
->clientdata
, &path
, &cmd
);
686 if (path
== NULL
|| *path
==NULL
|| **path
==0) {
687 wwarning(_("invalid OPEN_MENU specification: %s"),
688 (char*)entry
->clientdata
);
692 if (path
[0][0]=='|') {
695 if (!menu
->cascades
[entry
->cascade
] ||
696 menu
->cascades
[entry
->cascade
]->timestamp
== 0) {
699 submenu
= readMenuPipe(menu
->frame
->screen_ptr
, path
);
701 if(submenu
!= NULL
) {
702 if (path
[0][1] == '|')
703 submenu
->timestamp
= 0;
705 submenu
->timestamp
= 1; /* there's no automatic reloading */
713 while(path
[i
] != NULL
) {
716 if (strcmp(path
[i
], "-noext")==0) {
721 tmp
= wexpandpath(path
[i
]);
723 lpath
= getLocalizedMenuFile(tmp
);
732 if (stat(path
[i
], &stat_buf
)==0) {
733 if (last
< stat_buf
.st_mtime
)
734 last
= stat_buf
.st_mtime
;
738 wsyserror(_("%s:could not stat menu"), path
[i
]);
746 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
747 (char*)entry
->clientdata
);
750 stat(path
[first
], &stat_buf
);
751 if (!menu
->cascades
[entry
->cascade
]
752 || menu
->cascades
[entry
->cascade
]->timestamp
< last
) {
754 if (S_ISDIR(stat_buf
.st_mode
)) {
756 submenu
= readMenuDirectory(menu
->frame
->screen_ptr
,
757 entry
->text
, path
, cmd
);
759 submenu
->timestamp
= last
;
760 } else if (S_ISREG(stat_buf
.st_mode
)) {
764 wwarning(_("too many parameters in OPEN_MENU: %s"),
765 (char*)entry
->clientdata
);
767 submenu
= readMenuFile(menu
->frame
->screen_ptr
, path
[first
]);
769 submenu
->timestamp
= stat_buf
.st_mtime
;
779 wMenuEntryRemoveCascade(menu
, entry
);
780 wMenuEntrySetCascade(menu
, entry
, submenu
);
785 while (path
[i
]!=NULL
)
794 cleanupWorkspaceMenu(WMenu
*menu
)
796 if (menu
->frame
->screen_ptr
->workspace_menu
== menu
)
797 menu
->frame
->screen_ptr
->workspace_menu
= NULL
;
802 addWorkspaceMenu(WScreen
*scr
, WMenu
*menu
, char *title
)
807 if (scr
->flags
.added_workspace_menu
) {
808 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
811 scr
->flags
.added_workspace_menu
= 1;
813 wsmenu
= wWorkspaceMenuMake(scr
, True
);
814 wsmenu
->on_destroy
= cleanupWorkspaceMenu
;
816 scr
->workspace_menu
= wsmenu
;
817 entry
= wMenuAddCallback(menu
, title
, NULL
, NULL
);
818 wMenuEntrySetCascade(menu
, entry
, wsmenu
);
820 wWorkspaceMenuUpdate(scr
, wsmenu
);
827 cleanupWindowsMenu(WMenu
*menu
)
829 if (menu
->frame
->screen_ptr
->switch_menu
== menu
)
830 menu
->frame
->screen_ptr
->switch_menu
= NULL
;
835 addWindowsMenu(WScreen
*scr
, WMenu
*menu
, char *title
)
841 if (scr
->flags
.added_windows_menu
) {
842 wwarning(_("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
845 scr
->flags
.added_windows_menu
= 1;
847 wwmenu
= wMenuCreate(scr
, _("Window List"), False
);
848 wwmenu
->on_destroy
= cleanupWindowsMenu
;
849 scr
->switch_menu
= wwmenu
;
850 wwin
= scr
->focused_window
;
852 UpdateSwitchMenu(scr
, wwin
, ACTION_ADD
);
856 entry
= wMenuAddCallback(menu
, title
, NULL
, NULL
);
857 wMenuEntrySetCascade(menu
, entry
, wwmenu
);
864 addMenuEntry(WMenu
*menu
, char *title
, char *shortcut
, char *command
,
865 char *params
, char *file_name
)
868 WMenuEntry
*entry
= NULL
;
869 Bool shortcutOk
= False
;
873 scr
= menu
->frame
->screen_ptr
;
874 if (strcmp(command
, "OPEN_MENU")==0) {
876 wwarning(_("%s:missing parameter for menu command \"%s\""),
882 path
= wfindfile(DEF_CONFIG_PATHS
, params
);
884 path
= wstrdup(params
);
886 dummy
= wMenuCreate(scr
, title
, False
);
887 dummy
->on_destroy
= removeShortcutsForMenu
;
888 entry
= wMenuAddCallback(menu
, title
, constructMenu
, path
);
889 entry
->free_cdata
= free
;
890 wMenuEntrySetCascade(menu
, entry
, dummy
);
892 } else if (strcmp(command
, "EXEC")==0) {
894 wwarning(_("%s:missing parameter for menu command \"%s\""),
897 entry
= wMenuAddCallback(menu
, title
, execCommand
,
898 wstrconcat("exec ", params
));
899 entry
->free_cdata
= free
;
902 } else if (strcmp(command
, "SHEXEC")==0) {
904 wwarning(_("%s:missing parameter for menu command \"%s\""),
907 entry
= wMenuAddCallback(menu
, title
, execCommand
,
909 entry
->free_cdata
= free
;
912 } else if (strcmp(command
, "EXIT")==0) {
914 if (params
&& strcmp(params
, "QUICK")==0)
915 entry
= wMenuAddCallback(menu
, title
, exitCommand
, (void*)M_QUICK
);
917 entry
= wMenuAddCallback(menu
, title
, exitCommand
, NULL
);
920 } else if (strcmp(command
, "SHUTDOWN")==0) {
922 if (params
&& strcmp(params
, "QUICK")==0)
923 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
,
926 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
, NULL
);
929 } else if (strcmp(command
, "REFRESH")==0) {
930 entry
= wMenuAddCallback(menu
, title
, refreshCommand
, NULL
);
933 } else if (strcmp(command
, "WORKSPACE_MENU")==0) {
934 entry
= addWorkspaceMenu(scr
, menu
, title
);
937 } else if (strcmp(command
, "WINDOWS_MENU")==0) {
938 entry
= addWindowsMenu(scr
, menu
, title
);
941 } else if (strcmp(command
, "ARRANGE_ICONS")==0) {
942 entry
= wMenuAddCallback(menu
, title
, arrangeIconsCommand
, NULL
);
945 } else if (strcmp(command
, "HIDE_OTHERS")==0) {
946 entry
= wMenuAddCallback(menu
, title
, hideOthersCommand
, NULL
);
949 } else if (strcmp(command
, "SHOW_ALL")==0) {
950 entry
= wMenuAddCallback(menu
, title
, showAllCommand
, NULL
);
953 } else if (strcmp(command
, "RESTART")==0) {
954 entry
= wMenuAddCallback(menu
, title
, restartCommand
,
955 params
? wstrdup(params
) : NULL
);
956 entry
->free_cdata
= free
;
958 } else if (strcmp(command
, "SAVE_SESSION")==0) {
959 entry
= wMenuAddCallback(menu
, title
, saveSessionCommand
, NULL
);
962 } else if (strcmp(command
, "CLEAR_SESSION")==0) {
963 entry
= wMenuAddCallback(menu
, title
, clearSessionCommand
, NULL
);
965 } else if (strcmp(command
, "INFO_PANEL")==0) {
966 entry
= wMenuAddCallback(menu
, title
, infoPanelCommand
, NULL
);
968 } else if (strcmp(command
, "LEGAL_PANEL")==0) {
969 entry
= wMenuAddCallback(menu
, title
, legalPanelCommand
, NULL
);
972 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name
,
978 if (shortcut
&& entry
) {
980 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name
,
983 if (addShortcut(file_name
, shortcut
, menu
, entry
)) {
985 entry
->rtext
= GetShortcutString(shortcut
);
987 entry->rtext = wstrdup(shortcut);
998 /******************* Menu Configuration From File *******************/
1001 separateline(char *line
, char *title
, char *command
, char *parameter
,
1013 while (isspace(*line
) && (*line
!=0)) line
++;
1017 while (line
[i
]!='"' && (line
[i
]!=0)) i
++;
1022 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
1024 strncpy(title
, line
, i
);
1028 /* get the command or shortcut keyword */
1029 while (isspace(*line
) && (*line
!=0)) line
++;
1033 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
1034 strncpy(command
, line
, i
);
1038 if (strcmp(command
, "SHORTCUT")==0) {
1039 /* get the shortcut key */
1040 while (isspace(*line
) && (*line
!=0)) line
++;
1044 while (line
[i
]!='"' && (line
[i
]!=0)) i
++;
1049 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
1051 strncpy(shortcut
, line
, i
);
1057 /* get the command */
1058 while (isspace(*line
) && (*line
!=0)) line
++;
1062 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
1063 strncpy(command
, line
, i
);
1068 /* get the parameters */
1069 while (isspace(*line
) && (*line
!=0)) line
++;
1076 while (line
[l
]!=0 && line
[l
]!='"') {
1077 parameter
[l
] = line
[l
];
1085 while (isspace(line
[l
]) && (l
>0)) l
--;
1086 strncpy(parameter
, line
, l
);
1092 parseCascade(WScreen
*scr
, WMenu
*menu
, FILE *file
, char *file_name
)
1094 char linebuf
[MAXLINE
];
1095 char elinebuf
[MAXLINE
];
1096 char title
[MAXLINE
];
1097 char command
[MAXLINE
];
1098 char shortcut
[MAXLINE
];
1099 char params
[MAXLINE
];
1102 while (!feof(file
)) {
1106 fgets(linebuf
, MAXLINE
, file
);
1107 line
= cropline(linebuf
);
1108 lsize
= strlen(line
);
1110 if (line
[lsize
-1]=='\\') {
1113 fgets(elinebuf
, MAXLINE
, file
);
1114 line2
=cropline(elinebuf
);
1115 lsize2
=strlen(line2
);
1116 if (lsize2
+lsize
>MAXLINE
) {
1117 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1123 strcat(line
, line2
);
1128 } while (!ok
&& !feof(file
));
1132 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1136 separateline(line
, title
, command
, params
, shortcut
);
1139 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1144 if (strcasecmp(command
, "MENU")==0) {
1149 cascade
= wMenuCreate(scr
, title
, False
);
1150 cascade
->on_destroy
= removeShortcutsForMenu
;
1151 if (parseCascade(scr
, cascade
, file
, file_name
)==NULL
) {
1152 wMenuDestroy(cascade
, True
);
1154 wMenuEntrySetCascade(menu
,
1155 wMenuAddCallback(menu
, title
, NULL
, NULL
),
1158 } else if (strcasecmp(command
, "END")==0) {
1164 addMenuEntry(menu
, title
, shortcut
[0] ? shortcut
: NULL
, command
,
1165 params
[0] ? params
: NULL
, file_name
);
1169 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1179 readMenuFile(WScreen
*scr
, char *file_name
)
1183 char linebuf
[MAXLINE
];
1184 char title
[MAXLINE
];
1185 char shortcut
[MAXLINE
];
1186 char command
[MAXLINE
];
1187 char params
[MAXLINE
];
1195 if (!wPreferences
.flags
.nocpp
) {
1196 args
= MakeCPPArgs(file_name
);
1198 wwarning(_("could not make arguments for menu file preprocessor"));
1200 snprintf(command
, sizeof(command
), "%s %s %s",
1201 CPP_PATH
, args
, file_name
);
1203 file
= popen(command
, "r");
1205 wsyserror(_("%s:could not open/preprocess menu file"),
1215 file
= fopen(file_name
, "rb");
1217 wsyserror(_("%s:could not open menu file"), file_name
);
1222 while (!feof(file
)) {
1223 if (!fgets(linebuf
, MAXLINE
, file
))
1225 line
= cropline(linebuf
);
1226 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1229 separateline(line
, title
, command
, params
, shortcut
);
1232 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1236 if (strcasecmp(command
, "MENU")==0) {
1237 menu
= wMenuCreate(scr
, title
, True
);
1238 menu
->on_destroy
= removeShortcutsForMenu
;
1239 if (!parseCascade(scr
, menu
, file
, file_name
)) {
1240 wMenuDestroy(menu
, True
);
1244 wwarning(_("%s:invalid menu file. MENU command is missing"),
1252 if (pclose(file
)==-1) {
1253 wsyserror(_("error reading preprocessed menu data"));
1266 /************ Menu Configuration From Pipe *************/
1269 readMenuPipe(WScreen
*scr
, char **file_name
)
1273 char linebuf
[MAXLINE
];
1274 char title
[MAXLINE
];
1275 char command
[MAXLINE
];
1276 char params
[MAXLINE
];
1277 char shortcut
[MAXLINE
];
1280 char flat_file
[MAXLINE
];
1287 flat_file
[0] = '\0';
1289 for(i
=0; file_name
[i
]!=NULL
; i
++) {
1290 strcat(flat_file
, file_name
[i
]);
1291 strcat(flat_file
, " ");
1293 filename
= flat_file
+ (flat_file
[1]=='|'?2:1);
1297 if (!wPreferences
.flags
.nocpp
) {
1298 args
= MakeCPPArgs(filename
);
1300 wwarning(_("could not make arguments for menu file preprocessor"));
1302 snprintf(command
, sizeof(command
), "%s | %s %s",
1303 filename
, CPP_PATH
, args
);
1306 file
= popen(command
, "r");
1308 wsyserror(_("%s:could not open/preprocess menu file"), filename
);
1318 file
= popen(filename
, "rb");
1321 wsyserror(_("%s:could not open menu file"), filename
);
1326 while (!feof(file
)) {
1327 if (!fgets(linebuf
, MAXLINE
, file
))
1329 line
= cropline(linebuf
);
1330 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1333 separateline(line
, title
, command
, params
, shortcut
);
1336 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1340 if (strcasecmp(command
, "MENU")==0) {
1341 menu
= wMenuCreate(scr
, title
, True
);
1342 menu
->on_destroy
= removeShortcutsForMenu
;
1343 if (!parseCascade(scr
, menu
, file
, filename
)) {
1344 wMenuDestroy(menu
, True
);
1348 wwarning(_("%s:no title given for the root menu"), filename
);
1367 myCompare(const void *d1
, const void *d2
)
1369 dir_data
*p1
= *(dir_data
**)d1
;
1370 dir_data
*p2
= *(dir_data
**)d2
;
1372 return strcmp(p1
->name
, p2
->name
);
1376 /************ Menu Configuration From Directory *************/
1379 isFilePackage(char *file
)
1383 /* check if the extension indicates this file is a
1384 * file package. For now, only recognize .themed */
1388 if (l
> 7 && strcmp(&(file
[l
-7]), ".themed")==0) {
1397 readMenuDirectory(WScreen
*scr
, char *title
, char **path
, char *command
)
1400 struct dirent
*dentry
;
1401 struct stat stat_buf
;
1404 WMArray
*dirs
= NULL
, *files
= NULL
;
1405 WMArrayIterator iter
;
1406 int length
, i
, have_space
=0;
1408 int stripExtension
= 0;
1411 dirs
= WMCreateArray(16);
1412 files
= WMCreateArray(16);
1415 while (path
[i
]!=NULL
) {
1416 if (strcmp(path
[i
], "-noext")==0) {
1422 dir
= opendir(path
[i
]);
1428 while ((dentry
= readdir(dir
))) {
1430 if (strcmp(dentry
->d_name
, ".")==0 ||
1431 strcmp(dentry
->d_name
, "..")==0)
1434 if (dentry
->d_name
[0] == '.')
1437 buffer
= malloc(strlen(path
[i
])+strlen(dentry
->d_name
)+4);
1439 wsyserror(_("out of memory while constructing directory menu %s"),
1444 strcpy(buffer
, path
[i
]);
1445 strcat(buffer
, "/");
1446 strcat(buffer
, dentry
->d_name
);
1448 if (stat(buffer
, &stat_buf
)!=0) {
1449 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1450 path
[i
], dentry
->d_name
);
1452 Bool isFilePack
= False
;
1455 if (S_ISDIR(stat_buf
.st_mode
)
1456 && !(isFilePack
= isFilePackage(dentry
->d_name
))) {
1458 /* access always returns success for user root */
1459 if (access(buffer
, X_OK
)==0) {
1460 /* Directory is accesible. Add to directory list */
1462 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1463 data
->name
= wstrdup(dentry
->d_name
);
1466 WMAddToArray(dirs
, data
);
1468 } else if (S_ISREG(stat_buf
.st_mode
) || isFilePack
) {
1469 /* Hack because access always returns X_OK success for user root */
1470 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1471 if ((command
!=NULL
&& access(buffer
, R_OK
)==0) ||
1472 (command
==NULL
&& access(buffer
, X_OK
)==0 &&
1473 (stat_buf
.st_mode
& S_IXANY
))) {
1475 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1476 data
->name
= wstrdup(dentry
->d_name
);
1479 WMAddToArray(files
, data
);
1490 if (!WMGetArrayItemCount(dirs
) && !WMGetArrayItemCount(files
)) {
1496 WMSortArray(dirs
, myCompare
);
1497 WMSortArray(files
, myCompare
);
1499 menu
= wMenuCreate(scr
, title
, False
);
1500 menu
->on_destroy
= removeShortcutsForMenu
;
1502 WM_ITERATE_ARRAY(dirs
, data
, iter
) {
1503 /* New directory. Use same OPEN_MENU command that was used
1504 * for the current directory. */
1505 length
= strlen(path
[data
->index
])+strlen(data
->name
)+6;
1509 length
+= strlen(command
) + 6;
1510 buffer
= malloc(length
);
1512 wsyserror(_("out of memory while constructing directory menu %s"),
1519 strcat(buffer
, "-noext ");
1521 have_space
= strchr(path
[data
->index
], ' ')!=NULL
||
1522 strchr(data
->name
, ' ')!=NULL
;
1525 strcat(buffer
, "\"");
1526 strcat(buffer
, path
[data
->index
]);
1528 strcat(buffer
, "/");
1529 strcat(buffer
, data
->name
);
1531 strcat(buffer
, "\"");
1533 strcat(buffer
, " WITH ");
1534 strcat(buffer
, command
);
1537 addMenuEntry(menu
, data
->name
, NULL
, "OPEN_MENU", buffer
, path
[data
->index
]);
1545 WM_ITERATE_ARRAY(files
, data
, iter
) {
1546 /* executable: add as entry */
1547 length
= strlen(path
[data
->index
])+strlen(data
->name
)+6;
1549 length
+= strlen(command
);
1551 buffer
= malloc(length
);
1553 wsyserror(_("out of memory while constructing directory menu %s"),
1558 have_space
= strchr(path
[data
->index
], ' ')!=NULL
||
1559 strchr(data
->name
, ' ')!=NULL
;
1560 if (command
!=NULL
) {
1561 strcpy(buffer
, command
);
1562 strcat(buffer
, " ");
1564 strcat(buffer
, "\"");
1565 strcat(buffer
, path
[data
->index
]);
1570 strcat(buffer
, path
[data
->index
]);
1572 strcpy(buffer
, path
[data
->index
]);
1575 strcat(buffer
, "/");
1576 strcat(buffer
, data
->name
);
1578 strcat(buffer
, "\"");
1580 if (stripExtension
) {
1581 char *ptr
= strrchr(data
->name
, '.');
1582 if (ptr
&& ptr
!=data
->name
)
1585 addMenuEntry(menu
, data
->name
, NULL
, "SHEXEC", buffer
, path
[data
->index
]);
1600 /************ Menu Configuration From WMRootMenu *************/
1603 makeDefaultMenu(WScreen
*scr
)
1607 menu
= wMenuCreate(scr
, _("Commands"), True
);
1608 wMenuAddCallback(menu
, "XTerm", execCommand
, "xterm");
1609 wMenuAddCallback(menu
, "rxvt", execCommand
, "rxvt");
1610 wMenuAddCallback(menu
, _("Restart"), restartCommand
, NULL
);
1611 wMenuAddCallback(menu
, _("Exit..."), exitCommand
, NULL
);
1620 *----------------------------------------------------------------------
1622 * Reads root menu configuration from defaults database.
1624 *----------------------------------------------------------------------
1627 configureMenu(WScreen
*scr
, WMPropList
*definition
)
1632 WMPropList
*title
, *command
, *params
;
1636 if (WMIsPLString(definition
)) {
1637 struct stat stat_buf
;
1639 Bool menu_is_default
= False
;
1641 /* menu definition is a string. Probably a path, so parse the file */
1643 tmp
= wexpandpath(WMGetFromPLString(definition
));
1645 path
= getLocalizedMenuFile(tmp
);
1648 path
= wfindfile(DEF_CONFIG_PATHS
, tmp
);
1651 path
= wfindfile(DEF_CONFIG_PATHS
, DEF_MENU_FILE
);
1652 menu_is_default
= True
;
1656 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1662 if (stat(path
, &stat_buf
)<0) {
1663 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path
);
1669 if (!scr
->root_menu
|| stat_buf
.st_mtime
> scr
->root_menu
->timestamp
1670 /* if the pointer in WMRootMenu has changed */
1671 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1673 if (menu_is_default
) {
1674 wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1678 menu
= readMenuFile(scr
, path
);
1680 menu
->timestamp
= WMAX(stat_buf
.st_mtime
, WDRootMenu
->timestamp
);
1690 count
= WMGetPropListItemCount(definition
);
1694 elem
= WMGetFromPLArray(definition
, 0);
1695 if (!WMIsPLString(elem
)) {
1696 tmp
= WMGetPropListDescription(elem
, False
);
1697 wwarning(_("%s:format error in root menu configuration \"%s\""),
1702 mtitle
= WMGetFromPLString(elem
);
1704 menu
= wMenuCreate(scr
, mtitle
, False
);
1705 menu
->on_destroy
= removeShortcutsForMenu
;
1707 #ifdef GLOBAL_SUBMENU_FILE
1712 submenu
= readMenuFile(scr
, GLOBAL_SUBMENU_FILE
);
1715 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
, NULL
);
1716 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1721 for (i
=1; i
<count
; i
++) {
1722 elem
= WMGetFromPLArray(definition
, i
);
1724 if (WMIsPLString(elem
)) {
1727 file
= WMGetFromPLString(elem
);
1731 if (!WMIsPLArray(elem
) || WMGetPropListItemCount(elem
) < 2)
1734 if (WMIsPLArray(WMGetFromPLArray(elem
,1))) {
1739 submenu
= configureMenu(scr
, elem
);
1741 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
,
1743 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1747 WMPropList
*shortcut
;
1750 title
= WMGetFromPLArray(elem
, idx
++);
1751 shortcut
= WMGetFromPLArray(elem
, idx
++);
1752 if (strcmp(WMGetFromPLString(shortcut
), "SHORTCUT")==0) {
1753 shortcut
= WMGetFromPLArray(elem
, idx
++);
1754 command
= WMGetFromPLArray(elem
, idx
++);
1759 params
= WMGetFromPLArray(elem
, idx
++);
1761 if (!title
|| !command
)
1764 addMenuEntry(menu
, WMGetFromPLString(title
),
1765 shortcut
? WMGetFromPLString(shortcut
) : NULL
,
1766 WMGetFromPLString(command
),
1767 params
? WMGetFromPLString(params
) : NULL
, "WMRootMenu");
1772 tmp
= WMGetPropListDescription(elem
, False
);
1773 wwarning(_("%s:format error in root menu configuration \"%s\""),
1783 *----------------------------------------------------------------------
1785 * Opens the root menu, parsing the menu configuration from the
1786 * defaults database.
1787 * If the menu is already mapped and is not sticked to the
1788 * root window, it will be unmapped.
1791 * The menu may be remade.
1794 * Construction of OPEN_MENU entries are delayed to the moment the
1796 *----------------------------------------------------------------------
1799 OpenRootMenu(WScreen
*scr
, int x
, int y
, int keyboard
)
1802 WMPropList
*definition
;
1804 static WMPropList *domain=NULL;
1807 domain = WMCreatePLString("WMRootMenu");
1811 scr
->flags
.root_menu_changed_shortcuts
= 0;
1812 scr
->flags
.added_workspace_menu
= 0;
1813 scr
->flags
.added_windows_menu
= 0;
1815 if (scr
->root_menu
&& scr
->root_menu
->flags
.mapped
) {
1816 menu
= scr
->root_menu
;
1817 if (!menu
->flags
.buttoned
) {
1820 wRaiseFrame(menu
->frame
->core
);
1823 wMenuMapAt(menu
, 0, 0, True
);
1825 wMenuMapCopyAt(menu
, x
-menu
->frame
->core
->width
/2, y
);
1831 definition
= WDRootMenu
->dictionary
;
1834 definition = PLGetDomain(domain);
1837 if (WMIsPLArray(definition
)) {
1839 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1840 menu
= configureMenu(scr
, definition
);
1842 menu
->timestamp
= WDRootMenu
->timestamp
;
1847 menu
= configureMenu(scr
, definition
);
1852 /* menu hasn't changed or could not be read */
1853 if (!scr
->root_menu
) {
1854 wMessageDialog(scr
, _("Error"),
1855 _("The applications menu could not be loaded. "
1856 "Look at the console output for a detailed "
1857 "description of the errors."),
1858 _("OK"), NULL
, NULL
);
1860 menu
= makeDefaultMenu(scr
);
1861 scr
->root_menu
= menu
;
1863 menu
= scr
->root_menu
;
1866 if (scr
->root_menu
) {
1867 wMenuDestroy(scr
->root_menu
, True
);
1869 scr
->root_menu
= menu
;
1874 if (keyboard
&& x
==0 && y
==0) {
1876 } else if (keyboard
&& x
==scr
->scr_width
/2 && y
==scr
->scr_height
/2) {
1877 newx
= x
- menu
->frame
->core
->width
/2;
1878 newy
= y
- menu
->frame
->core
->height
/2;
1880 newx
= x
- menu
->frame
->core
->width
/2;
1883 wMenuMapAt(menu
, newx
, newy
, keyboard
);
1886 if (scr
->flags
.root_menu_changed_shortcuts
)
1887 rebindKeygrabs(scr
);