/* 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"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <dirent.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#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 <WINGs/WUtil.h>

#define MAX_SHORTCUT_LENGTH 32

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 <program> - execute an external program
 * SHEXEC <command> - execute a shell command
 * WORKSPACE_MENU - places the workspace submenu
 * ARRANGE_ICONS
 * RESTART [<window manager>] - 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; i < menu->cascade_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[MAX_SHORTCUT_LENGTH], *b;

	ptr = wmalloc(sizeof(Shortcut));

	strncpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
	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; i < count; i++) {
		elem = WMGetFromPLArray(definition, i);
#if 0
		if (WMIsPLString(elem)) {
			char *file;

			file = WMGetFromPLString(elem);

		}
#endif
		if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
			goto error;

		if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
			WMenu *submenu;
			WMenuEntry *mentry;

			/* submenu */
			submenu = configureMenu(scr, elem);
			if (submenu) {
				mentry = wMenuAddCallback(menu, submenu->frame->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);
}
