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,
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"
61 extern WDDomain
*WDRootMenu
;
63 extern Cursor wCursor
[WCUR_LAST
];
65 extern Time LastTimestamp
;
67 extern WPreferences wPreferences
;
69 extern int wScreenCount
;
71 static WMenu
*readMenuPipe(WScreen
*scr
, char **file_name
);
72 static WMenu
*readMenuFile(WScreen
*scr
, char *file_name
);
73 static WMenu
*readMenuDirectory(WScreen
*scr
, char *title
, char **file_name
,
77 typedef struct Shortcut
{
78 struct Shortcut
*next
;
88 static Shortcut
*shortcutList
= NULL
;
95 * "Title" EXEC command_to_exec -params
97 * "Title" EXEC command_to_exec -params
99 * "Workspaces" WORKSPACE_MENU
100 * "Title" built_in_command
102 * "Quick Quit" EXIT QUICK
105 * Commands may be preceded by SHORTCUT key
109 * INFO_PANEL - shows the Info Panel
110 * LEGAL_PANEL - shows the Legal info panel
111 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
112 * REFRESH - forces the desktop to be repainted
113 * EXIT [QUICK] - exit the window manager [without confirmation]
114 * EXEC <program> - execute an external program
115 * SHEXEC <command> - execute a shell command
116 * WORKSPACE_MENU - places the workspace submenu
118 * RESTART [<window manager>] - restarts the window manager
119 * SHOW_ALL - unhide all windows on workspace
120 * HIDE_OTHERS - hides all windows excep the focused one
121 * OPEN_MENU file - read menu data from file which must be a valid menu file.
122 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
123 * - read menu data from directory(ies) and
124 * eventually precede each with a command.
125 * OPEN_MENU | command
126 * - opens command and uses its stdout to construct and insert
127 * the resulting menu in current position. The output of
128 * command must be a valid menu description.
129 * The space between '|' and command is optional.
130 * SAVE_SESSION - saves the current state of the desktop, which include
131 * all running applications, all their hints (geometry,
132 * position on screen, workspace they live on, the dock
133 * or clip from where they were launched, and
134 * if minimized, shaded or hidden. Also saves the current
135 * workspace the user is on. All will be restored on every
136 * start of windowmaker until another SAVE_SESSION or
137 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
138 * WindowMaker domain file, then saving is automatically
139 * done on every windowmaker exit, overwriting any
140 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
142 * CLEAR_SESSION - clears any previous saved session. This will not have
143 * any effect if SaveSessionOnExit is True.
152 execCommand(WMenu
*menu
, WMenuEntry
*entry
)
156 cmdline
= ExpandOptions(menu
->frame
->screen_ptr
, (char*)entry
->clientdata
);
158 XGrabPointer(dpy
, menu
->frame
->screen_ptr
->root_win
, True
, 0,
159 GrabModeAsync
, GrabModeAsync
, None
, wCursor
[WCUR_WAIT
],
164 ExecuteShellCommand(menu
->frame
->screen_ptr
, cmdline
);
167 XUngrabPointer(dpy
, CurrentTime
);
173 exitCommand(WMenu
*menu
, WMenuEntry
*entry
)
175 static int inside
= 0;
177 /* prevent reentrant calls */
182 if ((long)entry
->clientdata
==M_QUICK
183 || wMessageDialog(menu
->frame
->screen_ptr
, _("Exit"),
184 _("Exit window manager?"),
185 _("Exit"), _("Cancel"), NULL
)==WAPRDefault
) {
187 printf("Exiting WindowMaker.\n");
189 Shutdown(WSExitMode
);
196 shutdownCommand(WMenu
*menu
, WMenuEntry
*entry
)
198 static int inside
= 0;
201 /* prevent reentrant calls */
212 if ((long)entry
->clientdata
==M_QUICK
)
216 if (wSessionIsManaged()) {
219 r
= wMessageDialog(menu
->frame
->screen_ptr
,
220 _("Close X session"),
221 _("Close Window System session?\n"
222 "Kill might close applications with unsaved data."),
223 _("Close"), _("Kill"), _("Cancel"));
226 else if (r
==WAPRAlternate
)
233 r
= wMessageDialog(menu
->frame
->screen_ptr
,
235 _("Kill Window System session?\n"
236 "(all applications will be closed)"),
237 _("Kill"), _("Cancel"), NULL
);
243 if (result
!=R_CANCEL
) {
245 if (result
== R_CLOSE
) {
246 Shutdown(WSLogoutMode
);
248 #endif /* XSMP_ENABLED */
250 Shutdown(WSKillMode
);
261 restartCommand(WMenu
*menu
, WMenuEntry
*entry
)
263 Shutdown(WSRestartPreparationMode
);
264 Restart((char*)entry
->clientdata
, False
);
270 refreshCommand(WMenu
*menu
, WMenuEntry
*entry
)
272 wRefreshDesktop(menu
->frame
->screen_ptr
);
277 arrangeIconsCommand(WMenu
*menu
, WMenuEntry
*entry
)
279 wArrangeIcons(menu
->frame
->screen_ptr
, True
);
283 showAllCommand(WMenu
*menu
, WMenuEntry
*entry
)
285 wShowAllWindows(menu
->frame
->screen_ptr
);
289 hideOthersCommand(WMenu
*menu
, WMenuEntry
*entry
)
291 wHideOtherApplications(menu
->frame
->screen_ptr
->focused_window
);
296 saveSessionCommand(WMenu
*menu
, WMenuEntry
*entry
)
298 if (!wPreferences
.save_session_on_exit
)
299 wSessionSaveState(menu
->frame
->screen_ptr
);
301 wScreenSaveState(menu
->frame
->screen_ptr
);
306 clearSessionCommand(WMenu
*menu
, WMenuEntry
*entry
)
308 wSessionClearState(menu
->frame
->screen_ptr
);
313 infoPanelCommand(WMenu
*menu
, WMenuEntry
*entry
)
315 wShowInfoPanel(menu
->frame
->screen_ptr
);
320 legalPanelCommand(WMenu
*menu
, WMenuEntry
*entry
)
322 wShowLegalPanel(menu
->frame
->screen_ptr
);
326 /********************************************************************/
330 getLocalizedMenuFile(char *menu
)
338 buffer
= wmalloc(strlen(menu
)+32);
340 /* try menu.locale_name */
341 sprintf(buffer
, "%s.%s", menu
, Locale
);
342 if (access(buffer
, F_OK
)==0) {
345 /* check if it is in the form aa_bb.encoding and check for aa_bb */
346 ptr
= strchr(Locale
, '.');
349 if (access(buffer
, F_OK
)==0) {
353 /* now check for aa */
354 ptr
= strchr(buffer
, '_');
357 if (access(buffer
, F_OK
)==0) {
367 raiseMenus(WMenu
*menu
)
371 if (menu
->flags
.mapped
) {
372 wRaiseFrame(menu
->frame
->core
);
374 for (i
=0; i
<menu
->cascade_no
; i
++) {
375 if (menu
->cascades
[i
])
376 raiseMenus(menu
->cascades
[i
]);
383 wRootMenuPerformShortcut(XEvent
*event
)
389 /* ignore CapsLock */
390 modifiers
= event
->xkey
.state
& ValidModMask
;
392 for (ptr
= shortcutList
; ptr
!=NULL
; ptr
= ptr
->next
) {
396 if (ptr
->keycode
==event
->xkey
.keycode
&& (ptr
->modifier
==modifiers
)) {
397 (*ptr
->entry
->callback
)(ptr
->menu
, ptr
->entry
);
406 wRootMenuBindShortcuts(Window window
)
412 if (ptr
->modifier
!=AnyModifier
) {
413 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
|LockMask
,
414 window
, True
, GrabModeAsync
, GrabModeAsync
);
416 wHackedGrabKey(ptr
->keycode
, ptr
->modifier
,
417 window
, True
, GrabModeAsync
, GrabModeAsync
);
420 XGrabKey(dpy
, ptr
->keycode
, ptr
->modifier
, window
, True
,
421 GrabModeAsync
, GrabModeAsync
);
428 rebindKeygrabs(WScreen
*scr
)
432 wwin
= scr
->focused_window
;
435 XUngrabKey(dpy
, AnyKey
, AnyModifier
, wwin
->frame
->core
->window
);
437 if (!WFLAGP(wwin
, no_bind_keys
)) {
438 wWindowSetKeyGrabs(wwin
);
446 removeShortcutsForMenu(WMenu
*menu
)
449 Shortcut
*newList
= NULL
;
454 if (ptr
->menu
== menu
) {
462 shortcutList
= newList
;
463 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
468 addShortcut(char *file
, char *shortcutDefinition
, WMenu
*menu
,
476 ptr
= wmalloc(sizeof(Shortcut
));
478 strcpy(buf
, shortcutDefinition
);
483 while ((k
= strchr(b
, '+'))!=NULL
) {
487 mod
= wXModifierFromKey(b
);
489 wwarning(_("%s:invalid key modifier \"%s\""), file
, b
);
493 ptr
->modifier
|= mod
;
499 ksym
= XStringToKeysym(b
);
501 if (ksym
==NoSymbol
) {
502 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
503 file
, shortcutDefinition
, entry
->text
);
508 ptr
->keycode
= XKeysymToKeycode(dpy
, ksym
);
509 if (ptr
->keycode
==0) {
510 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file
,
511 shortcutDefinition
, entry
->text
);
519 ptr
->next
= shortcutList
;
522 menu
->menu
->screen_ptr
->flags
.root_menu_changed_shortcuts
= 1;
528 /*******************************/
538 end
= &(line
[strlen(line
)])-1;
539 while (isspace(*line
) && *line
!=0) line
++;
540 while (end
>line
&& isspace(*end
)) {
549 next_token(char *line
, char **next
)
555 while (*line
==' ' || *line
=='\t') line
++;
561 while (*tmp
!=0 && *tmp
!='"') tmp
++;
563 wwarning(_("%s: unmatched '\"' in menu file"), line
);
574 } while (*tmp
!=0 && *tmp
!=' ' && *tmp
!='\t');
588 while (*tmp
==' ' || *tmp
=='\t') tmp
++;
598 separateCommand(char *line
, char ***file
, char **command
)
600 char *token
, *tmp
= line
;
601 WMArray
*array
= WMCreateArray(4);
607 token
= next_token(tmp
, &tmp
);
609 if (strcmp(token
, "WITH")==0) {
610 if (tmp
!=NULL
&& *tmp
!=0)
611 *command
= wstrdup(tmp
);
613 wwarning(_("%s: missing command"), line
);
616 WMAddToArray(array
, token
);
618 } while (token
!=NULL
&& tmp
!=NULL
);
620 count
= WMGetArrayItemCount(array
);
622 *file
= wmalloc(sizeof(char*)*(count
+1));
623 (*file
)[count
] = NULL
;
624 for (i
= 0; i
< count
; i
++) {
625 (*file
)[i
] = WMGetFromArray(array
, i
);
633 constructMenu(WMenu
*menu
, WMenuEntry
*entry
)
636 struct stat stat_buf
;
643 separateCommand((char*)entry
->clientdata
, &path
, &cmd
);
644 if (path
== NULL
|| *path
==NULL
|| **path
==0) {
645 wwarning(_("invalid OPEN_MENU specification: %s"),
646 (char*)entry
->clientdata
);
650 if (path
[0][0]=='|') {
653 if (!menu
->cascades
[entry
->cascade
] ||
654 menu
->cascades
[entry
->cascade
]->timestamp
== 0) {
657 submenu
= readMenuPipe(menu
->frame
->screen_ptr
, path
);
659 /* there's no automatic reloading */
661 submenu
->timestamp
= 1;
668 while(path
[i
] != NULL
) {
671 if (strcmp(path
[i
], "-noext")==0) {
676 tmp
= wexpandpath(path
[i
]);
678 lpath
= getLocalizedMenuFile(tmp
);
687 if (stat(path
[i
], &stat_buf
)==0) {
688 if (last
< stat_buf
.st_mtime
)
689 last
= stat_buf
.st_mtime
;
693 wsyserror(_("%s:could not stat menu"), path
[i
]);
701 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
702 (char*)entry
->clientdata
);
705 stat(path
[first
], &stat_buf
);
706 if (!menu
->cascades
[entry
->cascade
]
707 || menu
->cascades
[entry
->cascade
]->timestamp
< last
) {
709 if (S_ISDIR(stat_buf
.st_mode
)) {
711 submenu
= readMenuDirectory(menu
->frame
->screen_ptr
,
712 entry
->text
, path
, cmd
);
714 submenu
->timestamp
= last
;
715 } else if (S_ISREG(stat_buf
.st_mode
)) {
719 wwarning(_("too many parameters in OPEN_MENU: %s"),
720 (char*)entry
->clientdata
);
722 submenu
= readMenuFile(menu
->frame
->screen_ptr
, path
[first
]);
724 submenu
->timestamp
= stat_buf
.st_mtime
;
734 wMenuEntryRemoveCascade(menu
, entry
);
735 wMenuEntrySetCascade(menu
, entry
, submenu
);
740 while (path
[i
]!=NULL
)
749 addWorkspaceMenu(WScreen
*scr
, WMenu
*menu
, char *title
)
754 if (scr
->flags
.added_workspace_menu
) {
755 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
758 scr
->flags
.added_workspace_menu
= 1;
760 wsmenu
= wWorkspaceMenuMake(scr
, True
);
761 scr
->workspace_menu
= wsmenu
;
762 entry
= wMenuAddCallback(menu
, title
, NULL
, NULL
);
763 wMenuEntrySetCascade(menu
, entry
, wsmenu
);
765 wWorkspaceMenuUpdate(scr
, wsmenu
);
771 addWindowsMenu(WScreen
*scr
, WMenu
*menu
, char *title
)
777 if (scr
->flags
.added_windows_menu
) {
778 wwarning(_("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
781 scr
->flags
.added_windows_menu
= 1;
783 wwmenu
= wMenuCreate(scr
, _("Window List"), False
);
784 scr
->switch_menu
= wwmenu
;
785 wwin
= scr
->focused_window
;
787 UpdateSwitchMenu(scr
, wwin
, ACTION_ADD
);
791 entry
= wMenuAddCallback(menu
, title
, NULL
, NULL
);
792 wMenuEntrySetCascade(menu
, entry
, wwmenu
);
798 addMenuEntry(WMenu
*menu
, char *title
, char *shortcut
, char *command
,
799 char *params
, char *file_name
)
802 WMenuEntry
*entry
= NULL
;
803 Bool shortcutOk
= False
;
807 scr
= menu
->frame
->screen_ptr
;
808 if (strcmp(command
, "OPEN_MENU")==0) {
810 wwarning(_("%s:missing parameter for menu command \"%s\""),
816 path
= wfindfile(DEF_CONFIG_PATHS
, params
);
818 path
= wstrdup(params
);
820 dummy
= wMenuCreate(scr
, title
, False
);
821 dummy
->on_destroy
= removeShortcutsForMenu
;
822 entry
= wMenuAddCallback(menu
, title
, constructMenu
, path
);
823 entry
->free_cdata
= free
;
824 wMenuEntrySetCascade(menu
, entry
, dummy
);
826 } else if (strcmp(command
, "EXEC")==0) {
828 wwarning(_("%s:missing parameter for menu command \"%s\""),
831 entry
= wMenuAddCallback(menu
, title
, execCommand
,
832 wstrconcat("exec ", params
));
833 entry
->free_cdata
= free
;
836 } else if (strcmp(command
, "SHEXEC")==0) {
838 wwarning(_("%s:missing parameter for menu command \"%s\""),
841 entry
= wMenuAddCallback(menu
, title
, execCommand
,
843 entry
->free_cdata
= free
;
846 } else if (strcmp(command
, "EXIT")==0) {
848 if (params
&& strcmp(params
, "QUICK")==0)
849 entry
= wMenuAddCallback(menu
, title
, exitCommand
, (void*)M_QUICK
);
851 entry
= wMenuAddCallback(menu
, title
, exitCommand
, NULL
);
854 } else if (strcmp(command
, "SHUTDOWN")==0) {
856 if (params
&& strcmp(params
, "QUICK")==0)
857 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
,
860 entry
= wMenuAddCallback(menu
, title
, shutdownCommand
, NULL
);
863 } else if (strcmp(command
, "REFRESH")==0) {
864 entry
= wMenuAddCallback(menu
, title
, refreshCommand
, NULL
);
867 } else if (strcmp(command
, "WORKSPACE_MENU")==0) {
868 entry
= addWorkspaceMenu(scr
, menu
, title
);
871 } else if (strcmp(command
, "WINDOWS_MENU")==0) {
872 entry
= addWindowsMenu(scr
, menu
, title
);
875 } else if (strcmp(command
, "ARRANGE_ICONS")==0) {
876 entry
= wMenuAddCallback(menu
, title
, arrangeIconsCommand
, NULL
);
879 } else if (strcmp(command
, "HIDE_OTHERS")==0) {
880 entry
= wMenuAddCallback(menu
, title
, hideOthersCommand
, NULL
);
883 } else if (strcmp(command
, "SHOW_ALL")==0) {
884 entry
= wMenuAddCallback(menu
, title
, showAllCommand
, NULL
);
887 } else if (strcmp(command
, "RESTART")==0) {
888 entry
= wMenuAddCallback(menu
, title
, restartCommand
,
889 params
? wstrdup(params
) : NULL
);
890 entry
->free_cdata
= free
;
892 } else if (strcmp(command
, "SAVE_SESSION")==0) {
893 entry
= wMenuAddCallback(menu
, title
, saveSessionCommand
, NULL
);
896 } else if (strcmp(command
, "CLEAR_SESSION")==0) {
897 entry
= wMenuAddCallback(menu
, title
, clearSessionCommand
, NULL
);
899 } else if (strcmp(command
, "INFO_PANEL")==0) {
900 entry
= wMenuAddCallback(menu
, title
, infoPanelCommand
, NULL
);
902 } else if (strcmp(command
, "LEGAL_PANEL")==0) {
903 entry
= wMenuAddCallback(menu
, title
, legalPanelCommand
, NULL
);
906 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name
,
912 if (shortcut
&& entry
) {
914 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name
,
917 if (addShortcut(file_name
, shortcut
, menu
, entry
)) {
919 entry
->rtext
= GetShortcutString(shortcut
);
921 entry->rtext = wstrdup(shortcut);
932 /******************* Menu Configuration From File *******************/
935 separateline(char *line
, char *title
, char *command
, char *parameter
,
947 while (isspace(*line
) && (*line
!=0)) line
++;
951 while (line
[i
]!='"' && (line
[i
]!=0)) i
++;
956 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
958 strncpy(title
, line
, i
);
962 /* get the command or shortcut keyword */
963 while (isspace(*line
) && (*line
!=0)) line
++;
967 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
968 strncpy(command
, line
, i
);
972 if (strcmp(command
, "SHORTCUT")==0) {
973 /* get the shortcut key */
974 while (isspace(*line
) && (*line
!=0)) line
++;
978 while (line
[i
]!='"' && (line
[i
]!=0)) i
++;
983 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
985 strncpy(shortcut
, line
, i
);
991 /* get the command */
992 while (isspace(*line
) && (*line
!=0)) line
++;
996 while (!isspace(line
[i
]) && (line
[i
]!=0)) i
++;
997 strncpy(command
, line
, i
);
1002 /* get the parameters */
1003 while (isspace(*line
) && (*line
!=0)) line
++;
1010 while (line
[l
]!=0 && line
[l
]!='"') {
1011 parameter
[l
] = line
[l
];
1019 while (isspace(line
[l
]) && (l
>0)) l
--;
1020 strncpy(parameter
, line
, l
);
1026 parseCascade(WScreen
*scr
, WMenu
*menu
, FILE *file
, char *file_name
)
1028 char linebuf
[MAXLINE
];
1029 char elinebuf
[MAXLINE
];
1030 char title
[MAXLINE
];
1031 char command
[MAXLINE
];
1032 char shortcut
[MAXLINE
];
1033 char params
[MAXLINE
];
1036 while (!IsEof(file
)) {
1040 fgets(linebuf
, MAXLINE
, file
);
1041 line
= cropline(linebuf
);
1042 lsize
= strlen(line
);
1044 if (line
[lsize
-1]=='\\') {
1047 fgets(elinebuf
, MAXLINE
, file
);
1048 line2
=cropline(elinebuf
);
1049 lsize2
=strlen(line2
);
1050 if (lsize2
+lsize
>MAXLINE
) {
1051 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1057 strcat(line
, line2
);
1062 } while (!ok
&& !IsEof(file
));
1066 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1070 separateline(line
, title
, command
, params
, shortcut
);
1073 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1078 if (strcasecmp(command
, "MENU")==0) {
1083 cascade
= wMenuCreate(scr
, title
, False
);
1084 cascade
->on_destroy
= removeShortcutsForMenu
;
1085 if (parseCascade(scr
, cascade
, file
, file_name
)==NULL
) {
1086 wMenuDestroy(cascade
, True
);
1088 wMenuEntrySetCascade(menu
,
1089 wMenuAddCallback(menu
, title
, NULL
, NULL
),
1092 } else if (strcasecmp(command
, "END")==0) {
1098 addMenuEntry(menu
, title
, shortcut
[0] ? shortcut
: NULL
, command
,
1099 params
[0] ? params
: NULL
, file_name
);
1103 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1113 readMenuFile(WScreen
*scr
, char *file_name
)
1117 char linebuf
[MAXLINE
];
1118 char title
[MAXLINE
];
1119 char shortcut
[MAXLINE
];
1120 char command
[MAXLINE
];
1121 char params
[MAXLINE
];
1129 if (!wPreferences
.flags
.nocpp
) {
1130 args
= MakeCPPArgs(file_name
);
1132 wwarning(_("could not make arguments for menu file preprocessor"));
1134 sprintf(command
, "%s %s %s", CPP_PATH
, args
, file_name
);
1136 file
= popen(command
, "r");
1138 wsyserror(_("%s:could not open/preprocess menu file"),
1148 file
= fopen(file_name
, "r");
1150 wsyserror(_("%s:could not open menu file"), file_name
);
1155 while (!IsEof(file
)) {
1156 if (!fgets(linebuf
, MAXLINE
, file
))
1158 line
= cropline(linebuf
);
1159 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1162 separateline(line
, title
, command
, params
, shortcut
);
1165 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1169 if (strcasecmp(command
, "MENU")==0) {
1170 menu
= wMenuCreate(scr
, title
, True
);
1171 menu
->on_destroy
= removeShortcutsForMenu
;
1172 if (!parseCascade(scr
, menu
, file
, file_name
)) {
1173 wMenuDestroy(menu
, True
);
1177 wwarning(_("%s:invalid menu file. MENU command is missing"),
1185 if (pclose(file
)==-1) {
1186 wsyserror(_("error reading preprocessed menu data"));
1198 /************ Menu Configuration From Pipe *************/
1201 readMenuPipe(WScreen
*scr
, char **file_name
)
1205 char linebuf
[MAXLINE
];
1206 char title
[MAXLINE
];
1207 char command
[MAXLINE
];
1208 char params
[MAXLINE
];
1209 char shortcut
[MAXLINE
];
1212 char flat_file
[MAXLINE
];
1219 flat_file
[0] = '\0';
1221 for(i
= 0 ; file_name
[i
] != NULL
; i
++) {
1222 strcat(flat_file
, file_name
[i
]);
1223 strcat(flat_file
, " ");
1225 filename
= flat_file
+1;
1228 if (!wPreferences
.flags
.nocpp
) {
1229 args
= MakeCPPArgs(filename
);
1231 wwarning(_("could not make arguments for menu file preprocessor"));
1233 sprintf(command
, "%s | %s %s", filename
, CPP_PATH
, args
);
1236 file
= popen(command
, "r");
1238 wsyserror(_("%s:could not open/preprocess menu file"), filename
);
1248 file
= popen(filename
, "r");
1251 wsyserror(_("%s:could not open menu file"), filename
);
1256 while (!IsEof(file
)) {
1257 if (!fgets(linebuf
, MAXLINE
, file
))
1259 line
= cropline(linebuf
);
1260 if (line
[0]==0 || line
[0]=='#' || (line
[0]=='/' && line
[1]=='/'))
1263 separateline(line
, title
, command
, params
, shortcut
);
1266 wwarning(_("%s:missing command in menu config: %s"), file_name
,
1270 if (strcasecmp(command
, "MENU")==0) {
1271 menu
= wMenuCreate(scr
, title
, True
);
1272 menu
->on_destroy
= removeShortcutsForMenu
;
1273 if (!parseCascade(scr
, menu
, file
, filename
)) {
1274 wMenuDestroy(menu
, True
);
1278 wwarning(_("%s:no title given for the root menu"), filename
);
1297 myCompare(const void *d1
, const void *d2
)
1299 dir_data
*p1
= *(dir_data
**)d1
;
1300 dir_data
*p2
= *(dir_data
**)d2
;
1302 return strcmp(p1
->name
, p2
->name
);
1306 /************ Menu Configuration From Directory *************/
1309 isFilePackage(char *file
)
1313 /* check if the extension indicates this file is a
1314 * file package. For now, only recognize .themed */
1318 if (l
> 7 && strcmp(&(file
[l
-7]), ".themed")==0) {
1327 readMenuDirectory(WScreen
*scr
, char *title
, char **path
, char *command
)
1330 struct dirent
*dentry
;
1331 struct stat stat_buf
;
1334 WMArray
*dirs
= NULL
, *files
= NULL
;
1335 WMArrayIterator iter
;
1336 int length
, i
, have_space
=0;
1338 int stripExtension
= 0;
1341 dirs
= WMCreateArray(16);
1342 files
= WMCreateArray(16);
1345 while (path
[i
]!=NULL
) {
1346 if (strcmp(path
[i
], "-noext")==0) {
1352 dir
= opendir(path
[i
]);
1358 while ((dentry
= readdir(dir
))) {
1360 if (strcmp(dentry
->d_name
, ".")==0 ||
1361 strcmp(dentry
->d_name
, "..")==0)
1364 if (dentry
->d_name
[0] == '.')
1367 buffer
= malloc(strlen(path
[i
])+strlen(dentry
->d_name
)+4);
1369 wsyserror(_("out of memory while constructing directory menu %s"),
1374 strcpy(buffer
, path
[i
]);
1375 strcat(buffer
, "/");
1376 strcat(buffer
, dentry
->d_name
);
1378 if (stat(buffer
, &stat_buf
)!=0) {
1379 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1380 path
[i
], dentry
->d_name
);
1382 Bool isFilePack
= False
;
1385 if (S_ISDIR(stat_buf
.st_mode
)
1386 && !(isFilePack
= isFilePackage(dentry
->d_name
))) {
1388 /* access always returns success for user root */
1389 if (access(buffer
, X_OK
)==0) {
1390 /* Directory is accesible. Add to directory list */
1392 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1393 data
->name
= wstrdup(dentry
->d_name
);
1396 WMAddToArray(dirs
, data
);
1398 } else if (S_ISREG(stat_buf
.st_mode
) || isFilePack
) {
1399 /* Hack because access always returns X_OK success for user root */
1400 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1401 if ((command
!=NULL
&& access(buffer
, R_OK
)==0) ||
1402 (command
==NULL
&& access(buffer
, X_OK
)==0 &&
1403 (stat_buf
.st_mode
& S_IXANY
))) {
1405 data
= (dir_data
*) wmalloc(sizeof(dir_data
));
1406 data
->name
= wstrdup(dentry
->d_name
);
1409 WMAddToArray(files
, data
);
1420 if (!WMGetArrayItemCount(dirs
) && !WMGetArrayItemCount(files
)) {
1426 WMSortArray(dirs
, myCompare
);
1427 WMSortArray(files
, myCompare
);
1429 menu
= wMenuCreate(scr
, title
, False
);
1430 menu
->on_destroy
= removeShortcutsForMenu
;
1432 WM_ITERATE_ARRAY(dirs
, data
, iter
) {
1433 /* New directory. Use same OPEN_MENU command that was used
1434 * for the current directory. */
1435 length
= strlen(path
[data
->index
])+strlen(data
->name
)+6;
1439 length
+= strlen(command
) + 6;
1440 buffer
= malloc(length
);
1442 wsyserror(_("out of memory while constructing directory menu %s"),
1449 strcat(buffer
, "-noext ");
1451 have_space
= strchr(path
[data
->index
], ' ')!=NULL
||
1452 strchr(data
->name
, ' ')!=NULL
;
1455 strcat(buffer
, "\"");
1456 strcat(buffer
, path
[data
->index
]);
1458 strcat(buffer
, "/");
1459 strcat(buffer
, data
->name
);
1461 strcat(buffer
, "\"");
1463 strcat(buffer
, " WITH ");
1464 strcat(buffer
, command
);
1467 addMenuEntry(menu
, data
->name
, NULL
, "OPEN_MENU", buffer
, path
[data
->index
]);
1475 WM_ITERATE_ARRAY(files
, data
, iter
) {
1476 /* executable: add as entry */
1477 length
= strlen(path
[data
->index
])+strlen(data
->name
)+6;
1479 length
+= strlen(command
);
1481 buffer
= malloc(length
);
1483 wsyserror(_("out of memory while constructing directory menu %s"),
1488 have_space
= strchr(path
[data
->index
], ' ')!=NULL
||
1489 strchr(data
->name
, ' ')!=NULL
;
1490 if (command
!=NULL
) {
1491 strcpy(buffer
, command
);
1492 strcat(buffer
, " ");
1494 strcat(buffer
, "\"");
1495 strcat(buffer
, path
[data
->index
]);
1500 strcat(buffer
, path
[data
->index
]);
1502 strcpy(buffer
, path
[data
->index
]);
1505 strcat(buffer
, "/");
1506 strcat(buffer
, data
->name
);
1508 strcat(buffer
, "\"");
1510 if (stripExtension
) {
1511 char *ptr
= strrchr(data
->name
, '.');
1512 if (ptr
&& ptr
!=data
->name
)
1515 addMenuEntry(menu
, data
->name
, NULL
, "SHEXEC", buffer
, path
[data
->index
]);
1530 /************ Menu Configuration From WMRootMenu *************/
1533 makeDefaultMenu(WScreen
*scr
)
1537 menu
= wMenuCreate(scr
, _("Commands"), True
);
1538 wMenuAddCallback(menu
, "XTerm", execCommand
, "xterm");
1539 wMenuAddCallback(menu
, "rxvt", execCommand
, "rxvt");
1540 wMenuAddCallback(menu
, _("Restart"), restartCommand
, NULL
);
1541 wMenuAddCallback(menu
, _("Exit..."), exitCommand
, NULL
);
1550 *----------------------------------------------------------------------
1552 * Reads root menu configuration from defaults database.
1554 *----------------------------------------------------------------------
1557 configureMenu(WScreen
*scr
, proplist_t definition
)
1562 proplist_t title
, command
, params
;
1566 if (PLIsString(definition
)) {
1567 struct stat stat_buf
;
1569 Bool menu_is_default
= False
;
1571 /* menu definition is a string. Probably a path, so parse the file */
1573 tmp
= wexpandpath(PLGetString(definition
));
1575 path
= getLocalizedMenuFile(tmp
);
1578 path
= wfindfile(DEF_CONFIG_PATHS
, tmp
);
1581 path
= wfindfile(DEF_CONFIG_PATHS
, DEF_MENU_FILE
);
1582 menu_is_default
= True
;
1586 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1592 if (stat(path
, &stat_buf
)<0) {
1593 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path
);
1599 if (!scr
->root_menu
|| stat_buf
.st_mtime
> scr
->root_menu
->timestamp
1600 /* if the pointer in WMRootMenu has changed */
1601 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1603 if (menu_is_default
) {
1604 wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1608 menu
= readMenuFile(scr
, path
);
1610 menu
->timestamp
= WMAX(stat_buf
.st_mtime
, WDRootMenu
->timestamp
);
1620 count
= PLGetNumberOfElements(definition
);
1624 elem
= PLGetArrayElement(definition
, 0);
1625 if (!PLIsString(elem
)) {
1626 tmp
= PLGetDescription(elem
);
1627 wwarning(_("%s:format error in root menu configuration \"%s\""),
1632 mtitle
= PLGetString(elem
);
1634 menu
= wMenuCreate(scr
, mtitle
, False
);
1635 menu
->on_destroy
= removeShortcutsForMenu
;
1637 #ifdef GLOBAL_SUBMENU_FILE
1642 submenu
= readMenuFile(scr
, GLOBAL_SUBMENU_FILE
);
1645 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
, NULL
);
1646 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1651 for (i
=1; i
<count
; i
++) {
1652 elem
= PLGetArrayElement(definition
, i
);
1654 if (PLIsString(elem
)) {
1657 file
= PLGetString(elem
);
1661 if (!PLIsArray(elem
) || PLGetNumberOfElements(elem
) < 2)
1664 if (PLIsArray(PLGetArrayElement(elem
,1))) {
1669 submenu
= configureMenu(scr
, elem
);
1671 mentry
= wMenuAddCallback(menu
, submenu
->frame
->title
, NULL
,
1673 wMenuEntrySetCascade(menu
, mentry
, submenu
);
1680 title
= PLGetArrayElement(elem
, idx
++);
1681 shortcut
= PLGetArrayElement(elem
, idx
++);
1682 if (strcmp(PLGetString(shortcut
), "SHORTCUT")==0) {
1683 shortcut
= PLGetArrayElement(elem
, idx
++);
1684 command
= PLGetArrayElement(elem
, idx
++);
1689 params
= PLGetArrayElement(elem
, idx
++);
1691 if (!title
|| !command
)
1694 addMenuEntry(menu
, PLGetString(title
),
1695 shortcut
? PLGetString(shortcut
) : NULL
,
1696 PLGetString(command
),
1697 params
? PLGetString(params
) : NULL
, "WMRootMenu");
1702 tmp
= PLGetDescription(elem
);
1703 wwarning(_("%s:format error in root menu configuration \"%s\""),
1719 *----------------------------------------------------------------------
1721 * Opens the root menu, parsing the menu configuration from the
1722 * defaults database.
1723 * If the menu is already mapped and is not sticked to the
1724 * root window, it will be unmapped.
1727 * The menu may be remade.
1730 * Construction of OPEN_MENU entries are delayed to the moment the
1732 *----------------------------------------------------------------------
1735 OpenRootMenu(WScreen
*scr
, int x
, int y
, int keyboard
)
1738 proplist_t definition
;
1740 static proplist_t domain=NULL;
1743 domain = PLMakeString("WMRootMenu");
1747 scr
->flags
.root_menu_changed_shortcuts
= 0;
1748 scr
->flags
.added_workspace_menu
= 0;
1750 if (scr
->root_menu
&& scr
->root_menu
->flags
.mapped
) {
1751 menu
= scr
->root_menu
;
1752 if (!menu
->flags
.buttoned
) {
1755 wRaiseFrame(menu
->frame
->core
);
1758 wMenuMapAt(menu
, 0, 0, True
);
1760 wMenuMapCopyAt(menu
, x
-menu
->frame
->core
->width
/2, y
);
1766 definition
= WDRootMenu
->dictionary
;
1769 definition = PLGetDomain(domain);
1772 if (PLIsArray(definition
)) {
1774 || WDRootMenu
->timestamp
> scr
->root_menu
->timestamp
) {
1775 menu
= configureMenu(scr
, definition
);
1777 menu
->timestamp
= WDRootMenu
->timestamp
;
1782 menu
= configureMenu(scr
, definition
);
1787 /* menu hasn't changed or could not be read */
1788 if (!scr
->root_menu
) {
1789 wMessageDialog(scr
, _("Error"),
1790 _("The applications menu could not be loaded. "
1791 "Look at the console output for a detailed "
1792 "description of the errors."),
1793 _("OK"), NULL
, NULL
);
1795 menu
= makeDefaultMenu(scr
);
1796 scr
->root_menu
= menu
;
1798 menu
= scr
->root_menu
;
1802 wMenuDestroy(scr
->root_menu
, True
);
1803 scr
->root_menu
= menu
;
1808 if (keyboard
&& x
==0 && y
==0) {
1810 } else if (keyboard
&& x
==scr
->scr_width
/2 && y
==scr
->scr_height
/2) {
1811 newx
= x
- menu
->frame
->core
->width
/2;
1812 newy
= y
- menu
->frame
->core
->height
/2;
1814 newx
= x
- menu
->frame
->core
->width
/2;
1817 wMenuMapAt(menu
, newx
, newy
, keyboard
);
1820 if (scr
->flags
.root_menu_changed_shortcuts
)
1821 rebindKeygrabs(scr
);