/* rootmenu.c- user defined menu * * Window Maker window manager * * Copyright (c) 1997-2003 Alfredo K. Kojima * Copyright (c) 1998-2003 Dan Pascu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #include "wconfig.h" #ifndef LITE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "WindowMaker.h" #include "actions.h" #include "menu.h" #include "funcs.h" #include "dialog.h" #include "keybind.h" #include "stacking.h" #include "workspace.h" #include "defaults.h" #include "framewin.h" #include "session.h" #include "xmodifier.h" #include extern char *Locale; extern WDDomain *WDRootMenu; extern Cursor wCursor[WCUR_LAST]; extern Time LastTimestamp; extern WPreferences wPreferences; extern int wScreenCount; static WMenu *readMenuPipe(WScreen *scr, char **file_name); static WMenu *readMenuFile(WScreen *scr, char *file_name); static WMenu *readMenuDirectory(WScreen *scr, char *title, char **file_name, char *command); typedef struct Shortcut { struct Shortcut *next; int modifier; KeyCode keycode; WMenuEntry *entry; WMenu *menu; } Shortcut; static Shortcut *shortcutList = NULL; /* * Syntax: * # main menu * "Menu Name" MENU * "Title" EXEC command_to_exec -params * "Submenu" MENU * "Title" EXEC command_to_exec -params * "Submenu" END * "Workspaces" WORKSPACE_MENU * "Title" built_in_command * "Quit" EXIT * "Quick Quit" EXIT QUICK * "Menu Name" END * * Commands may be preceded by SHORTCUT key * * Built-in commands: * * INFO_PANEL - shows the Info Panel * LEGAL_PANEL - shows the Legal info panel * SHUTDOWN [QUICK] - closes the X server [without confirmation] * REFRESH - forces the desktop to be repainted * EXIT [QUICK] - exit the window manager [without confirmation] * EXEC - execute an external program * SHEXEC - execute a shell command * WORKSPACE_MENU - places the workspace submenu * ARRANGE_ICONS * RESTART [] - restarts the window manager * SHOW_ALL - unhide all windows on workspace * HIDE_OTHERS - hides all windows excep the focused one * OPEN_MENU file - read menu data from file which must be a valid menu file. * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options] * - read menu data from directory(ies) and * eventually precede each with a command. * OPEN_MENU | command * - opens command and uses its stdout to construct and insert * the resulting menu in current position. The output of * command must be a valid menu description. * The space between '|' and command is optional. * || will do the same, but will not cache the contents. * SAVE_SESSION - saves the current state of the desktop, which include * all running applications, all their hints (geometry, * position on screen, workspace they live on, the dock * or clip from where they were launched, and * if minimized, shaded or hidden. Also saves the current * workspace the user is on. All will be restored on every * start of windowmaker until another SAVE_SESSION or * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in * WindowMaker domain file, then saving is automatically * done on every windowmaker exit, overwriting any * SAVE_SESSION or CLEAR_SESSION (see below). Also save * dock state now. * CLEAR_SESSION - clears any previous saved session. This will not have * any effect if SaveSessionOnExit is True. * */ #define M_QUICK 1 /* menu commands */ static void execCommand(WMenu *menu, WMenuEntry *entry) { char *cmdline; cmdline = ExpandOptions(menu->frame->screen_ptr, (char*)entry->clientdata); XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0, GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime); XSync(dpy, 0); if (cmdline) { ExecuteShellCommand(menu->frame->screen_ptr, cmdline); wfree(cmdline); } XUngrabPointer(dpy, CurrentTime); XSync(dpy, 0); } static void exitCommand(WMenu *menu, WMenuEntry *entry) { static int inside = 0; int result; /* prevent reentrant calls */ if (inside) return; inside = 1; #define R_CANCEL 0 #define R_EXIT 1 result = R_CANCEL; if ((long)entry->clientdata==M_QUICK) { result = R_EXIT; } else { int r, oldSaveSessionFlag; oldSaveSessionFlag = wPreferences.save_session_on_exit; r = wExitDialog(menu->frame->screen_ptr, _("Exit"), _("Exit window manager?"), _("Exit"), _("Cancel"), NULL); if (r==WAPRDefault) { result = R_EXIT; } else if (r==WAPRAlternate) { /* Don't modify the "save session on exit" flag if the * user canceled the operation. */ wPreferences.save_session_on_exit = oldSaveSessionFlag; } } if (result==R_EXIT) { #ifdef DEBUG printf("Exiting WindowMaker.\n"); #endif Shutdown(WSExitMode); } #undef R_EXIT #undef R_CANCEL inside = 0; } static void shutdownCommand(WMenu *menu, WMenuEntry *entry) { static int inside = 0; int result; /* prevent reentrant calls */ if (inside) return; inside = 1; #define R_CANCEL 0 #define R_CLOSE 1 #define R_KILL 2 result = R_CANCEL; if ((long)entry->clientdata==M_QUICK) result = R_CLOSE; else { #ifdef XSMP_ENABLED if (wSessionIsManaged()) { int r; r = wMessageDialog(menu->frame->screen_ptr, _("Close X session"), _("Close Window System session?\n" "Kill might close applications with unsaved data."), _("Close"), _("Kill"), _("Cancel")); if (r==WAPRDefault) result = R_CLOSE; else if (r==WAPRAlternate) result = R_KILL; } else #endif { int r, oldSaveSessionFlag; oldSaveSessionFlag = wPreferences.save_session_on_exit; r = wExitDialog(menu->frame->screen_ptr, _("Kill X session"), _("Kill Window System session?\n" "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL); if (r==WAPRDefault) { result = R_KILL; } else if (r==WAPRAlternate) { /* Don't modify the "save session on exit" flag if the * user canceled the operation. */ wPreferences.save_session_on_exit = oldSaveSessionFlag; } } } if (result!=R_CANCEL) { #ifdef XSMP_ENABLED if (result == R_CLOSE) { Shutdown(WSLogoutMode); } else #endif /* XSMP_ENABLED */ { Shutdown(WSKillMode); } } #undef R_CLOSE #undef R_CANCEL #undef R_KILL inside = 0; } static void restartCommand(WMenu *menu, WMenuEntry *entry) { Shutdown(WSRestartPreparationMode); Restart((char*)entry->clientdata, False); Restart(NULL, True); } static void refreshCommand(WMenu *menu, WMenuEntry *entry) { wRefreshDesktop(menu->frame->screen_ptr); } static void arrangeIconsCommand(WMenu *menu, WMenuEntry *entry) { wArrangeIcons(menu->frame->screen_ptr, True); } static void showAllCommand(WMenu *menu, WMenuEntry *entry) { wShowAllWindows(menu->frame->screen_ptr); } static void hideOthersCommand(WMenu *menu, WMenuEntry *entry) { wHideOtherApplications(menu->frame->screen_ptr->focused_window); } static void saveSessionCommand(WMenu *menu, WMenuEntry *entry) { if (!wPreferences.save_session_on_exit) wSessionSaveState(menu->frame->screen_ptr); wScreenSaveState(menu->frame->screen_ptr); } static void clearSessionCommand(WMenu *menu, WMenuEntry *entry) { wSessionClearState(menu->frame->screen_ptr); wScreenSaveState(menu->frame->screen_ptr); } static void infoPanelCommand(WMenu *menu, WMenuEntry *entry) { wShowInfoPanel(menu->frame->screen_ptr); } static void legalPanelCommand(WMenu *menu, WMenuEntry *entry) { wShowLegalPanel(menu->frame->screen_ptr); } /********************************************************************/ static char* getLocalizedMenuFile(char *menu) { char *buffer, *ptr, *locale; int len; if (!Locale) return NULL; len = strlen(menu)+strlen(Locale)+8; buffer = wmalloc(len); /* try menu.locale_name */ snprintf(buffer, len, "%s.%s", menu, Locale); if (access(buffer, F_OK)==0) { return buffer; } /* position of locale in our buffer */ locale = buffer + strlen(menu) + 1; /* check if it is in the form aa_bb.encoding and check for aa_bb */ ptr = strchr(locale, '.'); if (ptr) { *ptr = 0; if (access(buffer, F_OK)==0) { return buffer; } } /* now check for aa */ ptr = strchr(locale, '_'); if (ptr) { *ptr = 0; if (access(buffer, F_OK)==0) { return buffer; } } wfree(buffer); return NULL; } static void raiseMenus(WMenu *menu) { int i; if (menu->flags.mapped) { wRaiseFrame(menu->frame->core); } for (i=0; icascade_no; i++) { if (menu->cascades[i]) raiseMenus(menu->cascades[i]); } } Bool wRootMenuPerformShortcut(XEvent *event) { WScreen *scr = wScreenForRootWindow(event->xkey.root); Shortcut *ptr; int modifiers; int done = 0; /* ignore CapsLock */ modifiers = event->xkey.state & ValidModMask; for (ptr = shortcutList; ptr!=NULL; ptr = ptr->next) { if (ptr->keycode==0 || ptr->menu->menu->screen_ptr!=scr) continue; if (ptr->keycode==event->xkey.keycode && ptr->modifier==modifiers) { (*ptr->entry->callback)(ptr->menu, ptr->entry); done = True; } } return done; } void wRootMenuBindShortcuts(Window window) { Shortcut *ptr; ptr = shortcutList; while (ptr) { if (ptr->modifier!=AnyModifier) { XGrabKey(dpy, ptr->keycode, ptr->modifier|LockMask, window, True, GrabModeAsync, GrabModeAsync); #ifdef NUMLOCK_HACK wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync); #endif } XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync); ptr = ptr->next; } } static void rebindKeygrabs(WScreen *scr) { WWindow *wwin; wwin = scr->focused_window; while (wwin!=NULL) { XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window); if (!WFLAGP(wwin, no_bind_keys)) { wWindowSetKeyGrabs(wwin); } wwin = wwin->prev; } } static void removeShortcutsForMenu(WMenu *menu) { Shortcut *ptr, *tmp; Shortcut *newList = NULL; ptr = shortcutList; while (ptr!=NULL) { tmp = ptr->next; if (ptr->menu == menu) { wfree(ptr); } else { ptr->next = newList; newList = ptr; } ptr = tmp; } shortcutList = newList; menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1; } static Bool addShortcut(char *file, char *shortcutDefinition, WMenu *menu, WMenuEntry *entry) { Shortcut *ptr; KeySym ksym; char *k; char buf[128], *b; ptr = wmalloc(sizeof(Shortcut)); strcpy(buf, shortcutDefinition); b = (char*)buf; /* get modifiers */ ptr->modifier = 0; while ((k = strchr(b, '+'))!=NULL) { int mod; *k = 0; mod = wXModifierFromKey(b); if (mod<0) { wwarning(_("%s:invalid key modifier \"%s\""), file, b); wfree(ptr); return False; } ptr->modifier |= mod; b = k+1; } /* get key */ ksym = XStringToKeysym(b); if (ksym==NoSymbol) { wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"), file, shortcutDefinition, entry->text); wfree(ptr); return False; } ptr->keycode = XKeysymToKeycode(dpy, ksym); if (ptr->keycode==0) { wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file, shortcutDefinition, entry->text); wfree(ptr); return False; } ptr->menu = menu; ptr->entry = entry; ptr->next = shortcutList; shortcutList = ptr; menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1; return True; } /*******************************/ static char* cropline(char *line) { char *end; if (strlen(line)==0) return line; end = &(line[strlen(line)])-1; while (isspace(*line) && *line!=0) line++; while (end>line && isspace(*end)) { *end=0; end--; } return line; } static char* next_token(char *line, char **next) { char *tmp, c; char *ret; *next = NULL; while (*line==' ' || *line=='\t') line++; tmp = line; if (*tmp=='"') { tmp++; line++; while (*tmp!=0 && *tmp!='"') tmp++; if (*tmp!='"') { wwarning(_("%s: unmatched '\"' in menu file"), line); return NULL; } } else { do { if (*tmp=='\\') tmp++; if (*tmp!=0) tmp++; } while (*tmp!=0 && *tmp!=' ' && *tmp!='\t'); } c = *tmp; *tmp = 0; ret = wstrdup(line); *tmp = c; if (c==0) return ret; else tmp++; /* skip blanks */ while (*tmp==' ' || *tmp=='\t') tmp++; if (*tmp!=0) *next = tmp; return ret; } static void separateCommand(char *line, char ***file, char **command) { char *token, *tmp = line; WMArray *array = WMCreateArray(4); int count, i; *file = NULL; *command = NULL; do { token = next_token(tmp, &tmp); if (token) { if (strcmp(token, "WITH")==0) { if (tmp!=NULL && *tmp!=0) *command = wstrdup(tmp); else wwarning(_("%s: missing command"), line); break; } WMAddToArray(array, token); } } while (token!=NULL && tmp!=NULL); count = WMGetArrayItemCount(array); if (count>0) { *file = wmalloc(sizeof(char*)*(count+1)); (*file)[count] = NULL; for (i = 0; i < count; i++) { (*file)[i] = WMGetFromArray(array, i); } } WMFreeArray(array); } static void constructMenu(WMenu *menu, WMenuEntry *entry) { WMenu *submenu; struct stat stat_buf; char **path; char *cmd; char *lpath = NULL; int i, first=-1; time_t last=0; separateCommand((char*)entry->clientdata, &path, &cmd); if (path == NULL || *path==NULL || **path==0) { wwarning(_("invalid OPEN_MENU specification: %s"), (char*)entry->clientdata); return; } if (path[0][0]=='|') { /* pipe menu */ if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) { /* parse pipe */ submenu = readMenuPipe(menu->frame->screen_ptr, path); if(submenu != NULL) { if (path[0][1] == '|') submenu->timestamp = 0; else submenu->timestamp = 1; /* there's no automatic reloading */ } } else { submenu = NULL; } } else { i=0; while(path[i] != NULL) { char *tmp; if (strcmp(path[i], "-noext")==0) { i++; continue; } tmp = wexpandpath(path[i]); wfree(path[i]); lpath = getLocalizedMenuFile(tmp); if (lpath) { wfree(tmp); path[i] = lpath; lpath = NULL; } else { path[i] = tmp; } if (stat(path[i], &stat_buf)==0) { if (last < stat_buf.st_mtime) last = stat_buf.st_mtime; if (first<0) first=i; } else { wsyserror(_("%s:could not stat menu"), path[i]); /*goto finish;*/ } i++; } if (first < 0) { wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char*)entry->clientdata); goto finish; } stat(path[first], &stat_buf); if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp < last) { if (S_ISDIR(stat_buf.st_mode)) { /* menu directory */ submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd); if (submenu) submenu->timestamp = last; } else if (S_ISREG(stat_buf.st_mode)) { /* menu file */ if (cmd || path[1]) wwarning(_("too many parameters in OPEN_MENU: %s"), (char*)entry->clientdata); submenu = readMenuFile(menu->frame->screen_ptr, path[first]); if (submenu) submenu->timestamp = stat_buf.st_mtime; } else { submenu = NULL; } } else { submenu = NULL; } } if (submenu) { wMenuEntryRemoveCascade(menu, entry); wMenuEntrySetCascade(menu, entry, submenu); } finish: i = 0; while (path[i]!=NULL) wfree(path[i++]); wfree(path); if (cmd) wfree(cmd); } static void cleanupWorkspaceMenu(WMenu *menu) { if (menu->frame->screen_ptr->workspace_menu == menu) menu->frame->screen_ptr->workspace_menu = NULL; } static WMenuEntry* addWorkspaceMenu(WScreen *scr, WMenu *menu, char *title) { WMenu *wsmenu; WMenuEntry *entry; if (scr->flags.added_workspace_menu) { wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed.")); return NULL; } else { scr->flags.added_workspace_menu = 1; wsmenu = wWorkspaceMenuMake(scr, True); wsmenu->on_destroy = cleanupWorkspaceMenu; scr->workspace_menu = wsmenu; entry = wMenuAddCallback(menu, title, NULL, NULL); wMenuEntrySetCascade(menu, entry, wsmenu); wWorkspaceMenuUpdate(scr, wsmenu); } return entry; } static void cleanupWindowsMenu(WMenu *menu) { if (menu->frame->screen_ptr->switch_menu == menu) menu->frame->screen_ptr->switch_menu = NULL; } static WMenuEntry* addWindowsMenu(WScreen *scr, WMenu *menu, char *title) { WMenu *wwmenu; WWindow *wwin; WMenuEntry *entry; if (scr->flags.added_windows_menu) { wwarning(_("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed.")); return NULL; } else { scr->flags.added_windows_menu = 1; wwmenu = wMenuCreate(scr, _("Window List"), False); wwmenu->on_destroy = cleanupWindowsMenu; scr->switch_menu = wwmenu; wwin = scr->focused_window; while (wwin) { UpdateSwitchMenu(scr, wwin, ACTION_ADD); wwin = wwin->prev; } entry = wMenuAddCallback(menu, title, NULL, NULL); wMenuEntrySetCascade(menu, entry, wwmenu); } return entry; } static WMenuEntry* addMenuEntry(WMenu *menu, char *title, char *shortcut, char *command, char *params, char *file_name) { WScreen *scr; WMenuEntry *entry = NULL; Bool shortcutOk = False; if (!menu) return NULL; scr = menu->frame->screen_ptr; if (strcmp(command, "OPEN_MENU")==0) { if (!params) { wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command); } else { WMenu *dummy; char *path; path = wfindfile(DEF_CONFIG_PATHS, params); if (!path) { path = wstrdup(params); } dummy = wMenuCreate(scr, title, False); dummy->on_destroy = removeShortcutsForMenu; entry = wMenuAddCallback(menu, title, constructMenu, path); entry->free_cdata = free; wMenuEntrySetCascade(menu, entry, dummy); } } else if (strcmp(command, "EXEC")==0) { if (!params) wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command); else { entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params)); entry->free_cdata = free; shortcutOk = True; } } else if (strcmp(command, "SHEXEC")==0) { if (!params) wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command); else { entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params)); entry->free_cdata = free; shortcutOk = True; } } else if (strcmp(command, "EXIT")==0) { if (params && strcmp(params, "QUICK")==0) entry = wMenuAddCallback(menu, title, exitCommand, (void*)M_QUICK); else entry = wMenuAddCallback(menu, title, exitCommand, NULL); shortcutOk = True; } else if (strcmp(command, "SHUTDOWN")==0) { if (params && strcmp(params, "QUICK")==0) entry = wMenuAddCallback(menu, title, shutdownCommand, (void*)M_QUICK); else entry = wMenuAddCallback(menu, title, shutdownCommand, NULL); shortcutOk = True; } else if (strcmp(command, "REFRESH")==0) { entry = wMenuAddCallback(menu, title, refreshCommand, NULL); shortcutOk = True; } else if (strcmp(command, "WORKSPACE_MENU")==0) { entry = addWorkspaceMenu(scr, menu, title); shortcutOk = True; } else if (strcmp(command, "WINDOWS_MENU")==0) { entry = addWindowsMenu(scr, menu, title); shortcutOk = True; } else if (strcmp(command, "ARRANGE_ICONS")==0) { entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL); shortcutOk = True; } else if (strcmp(command, "HIDE_OTHERS")==0) { entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL); shortcutOk = True; } else if (strcmp(command, "SHOW_ALL")==0) { entry = wMenuAddCallback(menu, title, showAllCommand, NULL); shortcutOk = True; } else if (strcmp(command, "RESTART")==0) { entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL); entry->free_cdata = free; shortcutOk = True; } else if (strcmp(command, "SAVE_SESSION")==0) { entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL); shortcutOk = True; } else if (strcmp(command, "CLEAR_SESSION")==0) { entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL); shortcutOk = True; } else if (strcmp(command, "INFO_PANEL")==0) { entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL); shortcutOk = True; } else if (strcmp(command, "LEGAL_PANEL")==0) { entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL); shortcutOk = True; } else { wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command); return NULL; } if (shortcut && entry) { if (!shortcutOk) { wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title); } else { if (addShortcut(file_name, shortcut, menu, entry)) { entry->rtext = GetShortcutString(shortcut); /* entry->rtext = wstrdup(shortcut); */ } } } return entry; } /******************* Menu Configuration From File *******************/ static void separateline(char *line, char *title, char *command, char *parameter, char *shortcut) { int l, i; l = strlen(line); *title = 0; *command = 0; *parameter = 0; *shortcut = 0; /* get the title */ while (isspace(*line) && (*line!=0)) line++; if (*line=='"') { line++; i=0; while (line[i]!='"' && (line[i]!=0)) i++; if (line[i]!='"') return; } else { i=0; while (!isspace(line[i]) && (line[i]!=0)) i++; } strncpy(title, line, i); title[i++]=0; line+=i; /* get the command or shortcut keyword */ while (isspace(*line) && (*line!=0)) line++; if (*line==0) return; i=0; while (!isspace(line[i]) && (line[i]!=0)) i++; strncpy(command, line, i); command[i++]=0; line+=i; if (strcmp(command, "SHORTCUT")==0) { /* get the shortcut key */ while (isspace(*line) && (*line!=0)) line++; if (*line=='"') { line++; i=0; while (line[i]!='"' && (line[i]!=0)) i++; if (line[i]!='"') return; } else { i=0; while (!isspace(line[i]) && (line[i]!=0)) i++; } strncpy(shortcut, line, i); shortcut[i++]=0; line+=i; *command=0; /* get the command */ while (isspace(*line) && (*line!=0)) line++; if (*line==0) return; i=0; while (!isspace(line[i]) && (line[i]!=0)) i++; strncpy(command, line, i); command[i++]=0; line+=i; } /* get the parameters */ while (isspace(*line) && (*line!=0)) line++; if (*line==0) return; if (*line=='"') { line++; l = 0; while (line[l]!=0 && line[l]!='"') { parameter[l] = line[l]; l++; } parameter[l] = 0; return; } l = strlen(line); while (isspace(line[l]) && (l>0)) l--; strncpy(parameter, line, l); parameter[l]=0; } static WMenu* parseCascade(WScreen *scr, WMenu *menu, FILE *file, char *file_name) { char linebuf[MAXLINE]; char elinebuf[MAXLINE]; char title[MAXLINE]; char command[MAXLINE]; char shortcut[MAXLINE]; char params[MAXLINE]; char *line; while (!feof(file)) { int lsize, ok; ok = 0; fgets(linebuf, MAXLINE, file); line = cropline(linebuf); lsize = strlen(line); do { if (line[lsize-1]=='\\') { char *line2; int lsize2; fgets(elinebuf, MAXLINE, file); line2=cropline(elinebuf); lsize2=strlen(line2); if (lsize2+lsize>MAXLINE) { wwarning(_("%s:maximal line size exceeded in menu config: %s"), file_name, line); ok=2; } else { line[lsize-1]=0; lsize+=lsize2-1; strcat(line, line2); } } else { ok=1; } } while (!ok && !feof(file)); if (ok==2) continue; if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/')) continue; separateline(line, title, command, params, shortcut); if (!command[0]) { wwarning(_("%s:missing command in menu config: %s"), file_name, line); goto error; } if (strcasecmp(command, "MENU")==0) { WMenu *cascade; /* start submenu */ cascade = wMenuCreate(scr, title, False); cascade->on_destroy = removeShortcutsForMenu; if (parseCascade(scr, cascade, file, file_name)==NULL) { wMenuDestroy(cascade, True); } else { wMenuEntrySetCascade(menu, wMenuAddCallback(menu, title, NULL, NULL), cascade); } } else if (strcasecmp(command, "END")==0) { /* end of menu */ return menu; } else { /* normal items */ addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command, params[0] ? params : NULL, file_name); } } wwarning(_("%s:syntax error in menu file:END declaration missing"), file_name); return menu; error: return menu; } static WMenu* readMenuFile(WScreen *scr, char *file_name) { WMenu *menu=NULL; FILE *file = NULL; char linebuf[MAXLINE]; char title[MAXLINE]; char shortcut[MAXLINE]; char command[MAXLINE]; char params[MAXLINE]; char *line; #ifdef USECPP char *args; int cpp = 0; #endif #ifdef USECPP if (!wPreferences.flags.nocpp) { args = MakeCPPArgs(file_name); if (!args) { wwarning(_("could not make arguments for menu file preprocessor")); } else { snprintf(command, sizeof(command), "%s %s %s", CPP_PATH, args, file_name); wfree(args); file = popen(command, "r"); if (!file) { wsyserror(_("%s:could not open/preprocess menu file"), file_name); } else { cpp = 1; } } } #endif /* USECPP */ if (!file) { file = fopen(file_name, "rb"); if (!file) { wsyserror(_("%s:could not open menu file"), file_name); return NULL; } } while (!feof(file)) { if (!fgets(linebuf, MAXLINE, file)) break; line = cropline(linebuf); if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/')) continue; separateline(line, title, command, params, shortcut); if (!command[0]) { wwarning(_("%s:missing command in menu config: %s"), file_name, line); break; } if (strcasecmp(command, "MENU")==0) { menu = wMenuCreate(scr, title, True); menu->on_destroy = removeShortcutsForMenu; if (!parseCascade(scr, menu, file, file_name)) { wMenuDestroy(menu, True); } break; } else { wwarning(_("%s:invalid menu file. MENU command is missing"), file_name); break; } } #ifdef CPP if (cpp) { if (pclose(file)==-1) { wsyserror(_("error reading preprocessed menu data")); } } else { fclose(file); } #else fclose(file); #endif return menu; } /************ Menu Configuration From Pipe *************/ static WMenu* readMenuPipe(WScreen *scr, char **file_name) { WMenu *menu=NULL; FILE *file = NULL; char linebuf[MAXLINE]; char title[MAXLINE]; char command[MAXLINE]; char params[MAXLINE]; char shortcut[MAXLINE]; char *line; char *filename; char flat_file[MAXLINE]; int i; #ifdef USECPP char *args; int cpp = 0; #endif flat_file[0] = '\0'; for(i=0; file_name[i]!=NULL; i++) { strcat(flat_file, file_name[i]); strcat(flat_file, " "); } filename = flat_file + (flat_file[1]=='|'?2:1); #ifdef USECPP if (!wPreferences.flags.nocpp) { args = MakeCPPArgs(filename); if (!args) { wwarning(_("could not make arguments for menu file preprocessor")); } else { snprintf(command, sizeof(command), "%s | %s %s", filename, CPP_PATH, args); wfree(args); file = popen(command, "r"); if (!file) { wsyserror(_("%s:could not open/preprocess menu file"), filename); } else { cpp = 1; } } } #endif /* USECPP */ if (!file) { file = popen(filename, "rb"); if (!file) { wsyserror(_("%s:could not open menu file"), filename); return NULL; } } while (!feof(file)) { if (!fgets(linebuf, MAXLINE, file)) break; line = cropline(linebuf); if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/')) continue; separateline(line, title, command, params, shortcut); if (!command[0]) { wwarning(_("%s:missing command in menu config: %s"), file_name, line); break; } if (strcasecmp(command, "MENU")==0) { menu = wMenuCreate(scr, title, True); menu->on_destroy = removeShortcutsForMenu; if (!parseCascade(scr, menu, file, filename)) { wMenuDestroy(menu, True); } break; } else { wwarning(_("%s:no title given for the root menu"), filename); break; } } pclose(file); return menu; } typedef struct { char *name; int index; } dir_data; static int myCompare(const void *d1, const void *d2) { dir_data *p1 = *(dir_data**)d1; dir_data *p2 = *(dir_data**)d2; return strcmp(p1->name, p2->name); } /************ Menu Configuration From Directory *************/ static Bool isFilePackage(char *file) { int l; /* check if the extension indicates this file is a * file package. For now, only recognize .themed */ l = strlen(file); if (l > 7 && strcmp(&(file[l-7]), ".themed")==0) { return True; } else { return False; } } static WMenu* readMenuDirectory(WScreen *scr, char *title, char **path, char *command) { DIR *dir; struct dirent *dentry; struct stat stat_buf; WMenu *menu=NULL; char *buffer; WMArray *dirs = NULL, *files = NULL; WMArrayIterator iter; int length, i, have_space=0; dir_data *data; int stripExtension = 0; dirs = WMCreateArray(16); files = WMCreateArray(16); i=0; while (path[i]!=NULL) { if (strcmp(path[i], "-noext")==0) { stripExtension = 1; i++; continue; } dir = opendir(path[i]); if (!dir) { i++; continue; } while ((dentry = readdir(dir))) { if (strcmp(dentry->d_name, ".")==0 || strcmp(dentry->d_name, "..")==0) continue; if (dentry->d_name[0] == '.') continue; buffer = malloc(strlen(path[i])+strlen(dentry->d_name)+4); if (!buffer) { wsyserror(_("out of memory while constructing directory menu %s"), path[i]); break; } strcpy(buffer, path[i]); strcat(buffer, "/"); strcat(buffer, dentry->d_name); if (stat(buffer, &stat_buf)!=0) { wsyserror(_("%s:could not stat file \"%s\" in menu directory"), path[i], dentry->d_name); } else { Bool isFilePack = False; data = NULL; if (S_ISDIR(stat_buf.st_mode) && !(isFilePack = isFilePackage(dentry->d_name))) { /* access always returns success for user root */ if (access(buffer, X_OK)==0) { /* Directory is accesible. Add to directory list */ data = (dir_data*) wmalloc(sizeof(dir_data)); data->name = wstrdup(dentry->d_name); data->index = i; WMAddToArray(dirs, data); } } else if (S_ISREG(stat_buf.st_mode) || isFilePack) { /* Hack because access always returns X_OK success for user root */ #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH) if ((command!=NULL && access(buffer, R_OK)==0) || (command==NULL && access(buffer, X_OK)==0 && (stat_buf.st_mode & S_IXANY))) { data = (dir_data*) wmalloc(sizeof(dir_data)); data->name = wstrdup(dentry->d_name); data->index = i; WMAddToArray(files, data); } } } wfree(buffer); } closedir(dir); i++; } if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) { WMFreeArray(dirs); WMFreeArray(files); return NULL; } WMSortArray(dirs, myCompare); WMSortArray(files, myCompare); menu = wMenuCreate(scr, title, False); menu->on_destroy = removeShortcutsForMenu; WM_ITERATE_ARRAY(dirs, data, iter) { /* New directory. Use same OPEN_MENU command that was used * for the current directory. */ length = strlen(path[data->index])+strlen(data->name)+6; if (stripExtension) length += 7; if (command) length += strlen(command) + 6; buffer = malloc(length); if (!buffer) { wsyserror(_("out of memory while constructing directory menu %s"), path[data->index]); break; } buffer[0] = '\0'; if (stripExtension) strcat(buffer, "-noext "); have_space = strchr(path[data->index], ' ')!=NULL || strchr(data->name, ' ')!=NULL; if (have_space) strcat(buffer, "\""); strcat(buffer, path[data->index]); strcat(buffer, "/"); strcat(buffer, data->name); if (have_space) strcat(buffer, "\""); if (command) { strcat(buffer, " WITH "); strcat(buffer, command); } addMenuEntry(menu, data->name, NULL, "OPEN_MENU", buffer, path[data->index]); wfree(buffer); if (data->name) wfree(data->name); wfree(data); } WM_ITERATE_ARRAY(files, data, iter) { /* executable: add as entry */ length = strlen(path[data->index])+strlen(data->name)+6; if (command) length += strlen(command); buffer = malloc(length); if (!buffer) { wsyserror(_("out of memory while constructing directory menu %s"), path[data->index]); break; } have_space = strchr(path[data->index], ' ')!=NULL || strchr(data->name, ' ')!=NULL; if (command!=NULL) { strcpy(buffer, command); strcat(buffer, " "); if (have_space) strcat(buffer, "\""); strcat(buffer, path[data->index]); } else { if (have_space) { buffer[0] = '"'; buffer[1] = 0; strcat(buffer, path[data->index]); } else { strcpy(buffer, path[data->index]); } } strcat(buffer, "/"); strcat(buffer, data->name); if (have_space) strcat(buffer, "\""); if (stripExtension) { char *ptr = strrchr(data->name, '.'); if (ptr && ptr!=data->name) *ptr = 0; } addMenuEntry(menu, data->name, NULL, "SHEXEC", buffer, path[data->index]); wfree(buffer); if (data->name) wfree(data->name); wfree(data); } WMFreeArray(files); WMFreeArray(dirs); return menu; } /************ Menu Configuration From WMRootMenu *************/ static WMenu* makeDefaultMenu(WScreen *scr) { WMenu *menu=NULL; menu = wMenuCreate(scr, _("Commands"), True); wMenuAddCallback(menu, "XTerm", execCommand, "xterm"); wMenuAddCallback(menu, "rxvt", execCommand, "rxvt"); wMenuAddCallback(menu, _("Restart"), restartCommand, NULL); wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL); return menu; } /* *---------------------------------------------------------------------- * configureMenu-- * Reads root menu configuration from defaults database. * *---------------------------------------------------------------------- */ static WMenu* configureMenu(WScreen *scr, WMPropList *definition) { WMenu *menu = NULL; WMPropList *elem; int i, count; WMPropList *title, *command, *params; char *tmp, *mtitle; if (WMIsPLString(definition)) { struct stat stat_buf; char *path = NULL; Bool menu_is_default = False; /* menu definition is a string. Probably a path, so parse the file */ tmp = wexpandpath(WMGetFromPLString(definition)); path = getLocalizedMenuFile(tmp); if (!path) path = wfindfile(DEF_CONFIG_PATHS, tmp); if (!path) { path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE); menu_is_default = True; } if (!path) { wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp); wfree(tmp); return NULL; } if (stat(path, &stat_buf)<0) { wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path); wfree(path); wfree(tmp); return NULL; } if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp /* if the pointer in WMRootMenu has changed */ || WDRootMenu->timestamp > scr->root_menu->timestamp) { if (menu_is_default) { wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "), path); } menu = readMenuFile(scr, path); if (menu) menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp); } else { menu = NULL; } wfree(path); wfree(tmp); return menu; } count = WMGetPropListItemCount(definition); if (count==0) return NULL; elem = WMGetFromPLArray(definition, 0); if (!WMIsPLString(elem)) { tmp = WMGetPropListDescription(elem, False); wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp); wfree(tmp); return NULL; } mtitle = WMGetFromPLString(elem); menu = wMenuCreate(scr, mtitle, False); menu->on_destroy = removeShortcutsForMenu; #ifdef GLOBAL_SUBMENU_FILE { WMenu *submenu; WMenuEntry *mentry; submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE); if (submenu) { mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL); wMenuEntrySetCascade(menu, mentry, submenu); } } #endif for (i=1; iframe->title, NULL, NULL); wMenuEntrySetCascade(menu, mentry, submenu); } } else { int idx = 0; WMPropList *shortcut; /* normal entry */ title = WMGetFromPLArray(elem, idx++); shortcut = WMGetFromPLArray(elem, idx++); if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT")==0) { shortcut = WMGetFromPLArray(elem, idx++); command = WMGetFromPLArray(elem, idx++); } else { command = shortcut; shortcut = NULL; } params = WMGetFromPLArray(elem, idx++); if (!title || !command) goto error; addMenuEntry(menu, WMGetFromPLString(title), shortcut ? WMGetFromPLString(shortcut) : NULL, WMGetFromPLString(command), params ? WMGetFromPLString(params) : NULL, "WMRootMenu"); } continue; error: tmp = WMGetPropListDescription(elem, False); wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp); wfree(tmp); } return menu; } /* *---------------------------------------------------------------------- * OpenRootMenu-- * Opens the root menu, parsing the menu configuration from the * defaults database. * If the menu is already mapped and is not sticked to the * root window, it will be unmapped. * * Side effects: * The menu may be remade. * * Notes: * Construction of OPEN_MENU entries are delayed to the moment the * user map's them. *---------------------------------------------------------------------- */ void OpenRootMenu(WScreen *scr, int x, int y, int keyboard) { WMenu *menu=NULL; WMPropList *definition; /* static WMPropList *domain=NULL; if (!domain) { domain = WMCreatePLString("WMRootMenu"); } */ scr->flags.root_menu_changed_shortcuts = 0; scr->flags.added_workspace_menu = 0; scr->flags.added_windows_menu = 0; if (scr->root_menu && scr->root_menu->flags.mapped) { menu = scr->root_menu; if (!menu->flags.buttoned) { wMenuUnmap(menu); } else { wRaiseFrame(menu->frame->core); if (keyboard) wMenuMapAt(menu, 0, 0, True); else wMenuMapCopyAt(menu, x-menu->frame->core->width/2, y); } return; } definition = WDRootMenu->dictionary; /* definition = PLGetDomain(domain); */ if (definition) { if (WMIsPLArray(definition)) { if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) { menu = configureMenu(scr, definition); if (menu) menu->timestamp = WDRootMenu->timestamp; } else menu = NULL; } else { menu = configureMenu(scr, definition); } } if (!menu) { /* menu hasn't changed or could not be read */ if (!scr->root_menu) { wMessageDialog(scr, _("Error"), _("The applications menu could not be loaded. " "Look at the console output for a detailed " "description of the errors."), _("OK"), NULL, NULL); menu = makeDefaultMenu(scr); scr->root_menu = menu; } menu = scr->root_menu; } else { /* new root menu */ if (scr->root_menu) { wMenuDestroy(scr->root_menu, True); } scr->root_menu = menu; } if (menu) { int newx, newy; if (keyboard && x==0 && y==0) { newx = newy = 0; } else if (keyboard && x==scr->scr_width/2 && y==scr->scr_height/2) { newx = x - menu->frame->core->width/2; newy = y - menu->frame->core->height/2; } else { newx = x - menu->frame->core->width/2; newy = y; } wMenuMapAt(menu, newx, newy, keyboard); } if (scr->flags.root_menu_changed_shortcuts) rebindKeygrabs(scr); } #endif /* !LITE */