/*
 * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
 * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
 * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
 * Copyright 2005 Richard Wilson <info@tinct.net>
 *
 * This file is part of NetSurf, http://www.netsurf-browser.org/
 *
 * NetSurf 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; version 2 of the License.
 *
 * NetSurf 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, see <http://www.gnu.org/licenses/>.
 */

/** \file
 * Menu creation and handling (implementation).
 */

#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "oslib/os.h"
#include "oslib/osbyte.h"
#include "oslib/osgbpb.h"
#include "oslib/territory.h"
#include "oslib/wimp.h"
#include "content/content.h"
#include "content/hlcache.h"
#include "content/urldb.h"
#include "desktop/browser.h"
#include "desktop/gui.h"
#include "desktop/history_core.h"
#include "desktop/netsurf.h"
#include "desktop/selection.h"
#include "desktop/textinput.h"
#include "render/box.h"
#include "render/form.h"
#include "riscos/dialog.h"
#include "render/form.h"
#include "riscos/configure.h"
#include "riscos/cookies.h"
#include "riscos/gui.h"
#include "riscos/global_history.h"
#include "riscos/help.h"
#include "riscos/menus.h"
#include "riscos/options.h"
#include "riscos/save.h"
#include "riscos/tinct.h"
#include "riscos/theme.h"
#include "riscos/treeview.h"
#include "riscos/wimp.h"
#include "riscos/wimp_event.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/url.h"
#include "utils/utils.h"
#include "utils/utf8.h"


struct ns_menu_entry {
	const char *text;		/**< menu text (from messages) */
	menu_action action;		/**< associated action */
	wimp_w *sub_window;		/**< sub-window if any */
};

struct ns_menu {
	const char *title;
	struct ns_menu_entry entries[];
};

struct menu_definition_entry {
	menu_action action;			/**< menu action */
	wimp_menu_entry *menu_entry;		/**< corresponding menu entry */
	const char *entry_key;			/**< Messages key for entry text */
	struct menu_definition_entry *next;	/**< next menu entry */
};

struct menu_definition {
	wimp_menu *menu;			/**< corresponding menu */
	const char *title_key;			/**< Messages key for title text */
	int current_encoding;			/**< Identifier for current text encoding of menu text (as per OS_Byte,71,127) */
	struct menu_definition_entry *entries;	/**< menu entries */
	struct menu_definition *next;		/**< next menu */
};


static wimp_menu *ro_gui_menu_define_menu(const struct ns_menu *menu);
static void ro_gui_menu_define_menu_add(struct menu_definition *definition,
		const struct ns_menu *menu, int depth,
		wimp_menu_entry *parent_entry,
		int first, int last, const char *prefix, int prefix_length);
static struct menu_definition *ro_gui_menu_find_menu(wimp_menu *menu);
static struct menu_definition_entry *ro_gui_menu_find_entry(wimp_menu *menu,
		menu_action action);
static menu_action ro_gui_menu_find_action(wimp_menu *menu,
		wimp_menu_entry *menu_entry);
static void ro_gui_menu_set_entry_shaded(wimp_menu *menu, menu_action action,
		bool shaded);
static void ro_gui_menu_set_entry_ticked(wimp_menu *menu, menu_action action,
		bool ticked);
static void ro_gui_menu_get_window_details(wimp_w w, struct gui_window **g,
		struct browser_window **bw, hlcache_handle **h,
		struct toolbar **toolbar, struct tree **tree);
static int ro_gui_menu_get_checksum(void);
static bool ro_gui_menu_prepare_url_suggest(void);
static void ro_gui_menu_prepare_pageinfo(struct gui_window *g);
static void ro_gui_menu_prepare_objectinfo(hlcache_handle *object,
		const char *href);
static void ro_gui_menu_refresh_toolbar(struct toolbar *toolbar);
static bool ro_gui_menu_translate(struct menu_definition *menu);


/* default menu item flags */
#define DEFAULT_FLAGS (wimp_ICON_TEXT | wimp_ICON_FILLED | \
		(wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | \
		(wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT))

/** The currently defined menus to perform actions for */
static struct menu_definition *ro_gui_menu_definitions;
/** The current menu being worked with (may not be open) */
wimp_menu *current_menu;
/** Whether a menu is currently open */
bool current_menu_open = false;
/** Object under menu, or 0 if no object. */
static hlcache_handle *current_menu_object = 0;
/** URL of link under menu, or 0 if no link. */
static const char *current_menu_url = 0;
/** Menu of options for form select controls. */
static wimp_menu *gui_form_select_menu = 0;
/** Form control which gui_form_select_menu is for. */
static struct form_control *gui_form_select_control;
/** Window that owns the current menu */
wimp_w current_menu_window;
/** Icon that owns the current menu (only valid for popup menus) */
static wimp_i current_menu_icon;
/** The height of the iconbar menu */
int iconbar_menu_height = 5 * 44;
/** The available menus */
wimp_menu *iconbar_menu, *browser_menu, *hotlist_menu, *global_history_menu, *cookies_menu,
	*image_quality_menu, *browser_toolbar_menu,
	*tree_toolbar_menu, *proxy_type_menu, *languages_menu;
/** URL suggestion menu */
static wimp_MENU(GLOBAL_HISTORY_RECENT_URLS) url_suggest;
wimp_menu *url_suggest_menu = (wimp_menu *)&url_suggest;

/* the values given in PRM 3-157 for how to check menus/windows are
 * incorrect so we use a hack of checking if the sub-menu has bit 0
 * set which is undocumented but true of window handles on
 * all target OS versions */
#define IS_MENU(menu) !((int)(menu) & 1)

/**
 * Create menu structures.
 */
void ro_gui_menu_init(void)
{
	/* iconbar menu */
	static const struct ns_menu iconbar_definition = {
		"NetSurf", {
			{ "Info", NO_ACTION, &dialog_info },
			{ "AppHelp", HELP_OPEN_CONTENTS, 0 },
			{ "Open", BROWSER_NAVIGATE_URL, 0 },
			{ "Open.OpenURL", BROWSER_NAVIGATE_URL, &dialog_openurl },
			{ "Open.HotlistShow", HOTLIST_SHOW, 0 },
			{ "Open.HistGlobal", HISTORY_SHOW_GLOBAL, 0 },
			{ "Open.ShowCookies", COOKIES_SHOW, 0 },
			{ "Choices", CHOICES_SHOW, 0 },
			{ "Quit", APPLICATION_QUIT, 0 },
			{NULL, 0, 0}
		}
	};
	iconbar_menu = ro_gui_menu_define_menu(&iconbar_definition);

	/* browser menu */
	static const struct ns_menu browser_definition = {
		"NetSurf", {
			{ "Page", BROWSER_PAGE, 0 },
			{ "Page.PageInfo",BROWSER_PAGE_INFO, &dialog_pageinfo },
			{ "Page.Save", BROWSER_SAVE, &dialog_saveas },
			{ "Page.SaveComp", BROWSER_SAVE_COMPLETE, &dialog_saveas },
			{ "Page.Export", NO_ACTION, 0 },
#ifdef WITH_DRAW_EXPORT
			{ "Page.Export.Draw", BROWSER_EXPORT_DRAW, &dialog_saveas },
#endif
#ifdef WITH_PDF_EXPORT
			{ "Page.Export.PDF", BROWSER_EXPORT_PDF, &dialog_saveas },
#endif
			{ "Page.Export.Text", BROWSER_EXPORT_TEXT, &dialog_saveas },
			{ "Page.SaveURL", NO_ACTION, 0 },
			{ "Page.SaveURL.URI", BROWSER_SAVE_URL_URI, &dialog_saveas },
			{ "Page.SaveURL.URL", BROWSER_SAVE_URL_URL, &dialog_saveas },
			{ "Page.SaveURL.LinkText", BROWSER_SAVE_URL_TEXT, &dialog_saveas },
			{ "_Page.Print", BROWSER_PRINT, &dialog_print },
			{ "Page.NewWindow", BROWSER_NEW_WINDOW, 0 },
			{ "Page.FindText", BROWSER_FIND_TEXT, &dialog_search },
			{ "Page.ViewSrc", BROWSER_VIEW_SOURCE, 0 },
			{ "Object", BROWSER_OBJECT, 0 },
			{ "Object.Object", BROWSER_OBJECT_OBJECT, 0 },
			{ "Object.Object.ObjInfo", BROWSER_OBJECT_INFO, &dialog_objinfo },
			{ "Object.Object.ObjSave", BROWSER_OBJECT_SAVE, &dialog_saveas },
			{ "Object.Object.Export", BROWSER_OBJECT_EXPORT, 0 },
			{ "Object.Object.Export.Sprite", BROWSER_OBJECT_EXPORT_SPRITE, &dialog_saveas },
#ifdef WITH_DRAW_EXPORT
			{ "Object.Object.Export.ObjDraw", BROWSER_OBJECT_EXPORT_DRAW, &dialog_saveas },
#endif
			{ "Object.Object.SaveURL", NO_ACTION, 0 },
			{ "Object.Object.SaveURL.URI", BROWSER_OBJECT_SAVE_URL_URI, &dialog_saveas },
			{ "Object.Object.SaveURL.URL", BROWSER_OBJECT_SAVE_URL_URL, &dialog_saveas },
			{ "Object.Object.SaveURL.LinkText", BROWSER_OBJECT_SAVE_URL_TEXT, &dialog_saveas },
			{ "Object.Object.ObjPrint", BROWSER_OBJECT_PRINT, 0 },
			{ "Object.Object.ObjReload", BROWSER_OBJECT_RELOAD, 0 },
			{ "Object.Link", BROWSER_OBJECT_LINK, 0 },
			{ "Object.Link.LinkSave", BROWSER_LINK_SAVE, 0 },
			{ "Object.Link.LinkSave.URI", BROWSER_LINK_SAVE_URI, &dialog_saveas },
			{ "Object.Link.LinkSave.URL", BROWSER_LINK_SAVE_URL, &dialog_saveas },
			{ "Object.Link.LinkSave.LinkText", BROWSER_LINK_SAVE_TEXT, &dialog_saveas },
			{ "_Object.Link.LinkDload", BROWSER_LINK_DOWNLOAD, 0 },
			{ "Object.Link.LinkNew", BROWSER_LINK_NEW_WINDOW, 0 },
			{ "Selection", BROWSER_SELECTION, 0 },
			{ "_Selection.SelSave", BROWSER_SELECTION_SAVE, &dialog_saveas },
			{ "Selection.Copy", BROWSER_SELECTION_COPY, 0 },
			{ "Selection.Cut", BROWSER_SELECTION_CUT, 0 },
			{ "_Selection.Paste", BROWSER_SELECTION_PASTE, 0 },
			{ "Selection.Clear", BROWSER_SELECTION_CLEAR, 0 },
			{ "Selection.SelectAll", BROWSER_SELECTION_ALL, 0 },
			{ "Navigate", NO_ACTION, 0 },
			{ "Navigate.Home", BROWSER_NAVIGATE_HOME, 0 },
			{ "Navigate.Back", BROWSER_NAVIGATE_BACK, 0 },
			{ "Navigate.Forward", BROWSER_NAVIGATE_FORWARD, 0 },
			{ "_Navigate.UpLevel", BROWSER_NAVIGATE_UP, 0 },
			{ "Navigate.Reload", BROWSER_NAVIGATE_RELOAD_ALL, 0 },
			{ "Navigate.Stop", BROWSER_NAVIGATE_STOP, 0 },
			{ "View", NO_ACTION, 0 },
			{ "View.ScaleView", BROWSER_SCALE_VIEW, &dialog_zoom },
			{ "View.Images", NO_ACTION, 0 },
			{ "View.Images.ForeImg", BROWSER_IMAGES_FOREGROUND, 0 },
			{ "View.Images.BackImg", BROWSER_IMAGES_BACKGROUND, 0 },
			{ "View.Toolbars", NO_ACTION, 0 },
			{ "View.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 },
			{ "View.Toolbars.ToolAddress", TOOLBAR_ADDRESS_BAR, 0 },
			{ "_View.Toolbars.ToolThrob", TOOLBAR_THROBBER, 0 },
			{ "View.Toolbars.EditToolbar", TOOLBAR_EDIT, 0 },
			{ "_View.Render", NO_ACTION, 0 },
			{ "View.Render.RenderAnims", BROWSER_BUFFER_ANIMS, 0 },
			{ "View.Render.RenderAll", BROWSER_BUFFER_ALL, 0 },
			{ "_View.OptDefault", BROWSER_SAVE_VIEW, 0 },
			{ "View.Window", NO_ACTION, 0 },
			{ "View.Window.WindowSave", BROWSER_WINDOW_DEFAULT, 0 },
			{ "View.Window.WindowStagr", BROWSER_WINDOW_STAGGER, 0 },
			{ "_View.Window.WindowSize", BROWSER_WINDOW_COPY, 0 },
			{ "View.Window.WindowReset", BROWSER_WINDOW_RESET, 0 },
			{ "Utilities", NO_ACTION, 0 },
			{ "Utilities.Hotlist", HOTLIST_SHOW, 0 },
			{ "Utilities.Hotlist.HotlistAdd", HOTLIST_ADD_URL, 0 },
			{ "Utilities.Hotlist.HotlistShow", HOTLIST_SHOW, 0 },
			{ "Utilities.History", HISTORY_SHOW_GLOBAL, 0 },
			{ "Utilities.History.HistLocal", HISTORY_SHOW_LOCAL, 0 },
			{ "Utilities.History.HistGlobal", HISTORY_SHOW_GLOBAL, 0 },
			{ "Utilities.Cookies", COOKIES_SHOW, 0 },
			{ "Utilities.Cookies.ShowCookies", COOKIES_SHOW, 0 },
			{ "Utilities.Cookies.DeleteCookies", COOKIES_DELETE, 0 },
			{ "Help", HELP_OPEN_CONTENTS, 0 },
			{ "Help.HelpContent", HELP_OPEN_CONTENTS, 0 },
			{ "Help.HelpGuide", HELP_OPEN_GUIDE, 0 },
			{ "_Help.HelpInfo", HELP_OPEN_INFORMATION, 0 },
			{ "_Help.HelpAbout", HELP_OPEN_ABOUT, 0 },
			{ "Help.HelpInter", HELP_LAUNCH_INTERACTIVE, 0 },
			{NULL, 0, 0}
		}
	};
	browser_menu = ro_gui_menu_define_menu(&browser_definition);

	/* hotlist menu */
	static wimp_w one = (wimp_w) 1;
	static const struct ns_menu hotlist_definition = {
		"Hotlist", {
			{ "Hotlist", NO_ACTION, 0 },
			{ "Hotlist.New", NO_ACTION, 0 },
			{ "Hotlist.New.Folder", TREE_NEW_FOLDER, &dialog_folder },
			{ "Hotlist.New.Link", TREE_NEW_LINK, &dialog_entry },
			{ "_Hotlist.Export", HOTLIST_EXPORT, &dialog_saveas },
			{ "Hotlist.Expand", TREE_EXPAND_ALL, 0 },
			{ "Hotlist.Expand.All", TREE_EXPAND_ALL, 0 },
			{ "Hotlist.Expand.Folders", TREE_EXPAND_FOLDERS, 0 },
			{ "Hotlist.Expand.Links", TREE_EXPAND_LINKS, 0 },
			{ "Hotlist.Collapse", TREE_COLLAPSE_ALL, 0 },
			{ "Hotlist.Collapse.All", TREE_COLLAPSE_ALL, 0 },
			{ "Hotlist.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 },
			{ "Hotlist.Collapse.Links", TREE_COLLAPSE_LINKS, 0 },
			{ "Hotlist.Toolbars", NO_ACTION, 0 },
			{ "_Hotlist.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 },
			{ "Hotlist.Toolbars.EditToolbar", TOOLBAR_EDIT, 0 },
			{ "Selection", TREE_SELECTION, 0 },
			/* We want a window, but it changes depending upon 
			 * context. Therefore, ensure that the structure is
			 * created so that we can dynamically modify the 
			 * actual item presented. We do this by creating a
			 * fake wimp_w with the value 1, which indicates a 
			 * window handle as opposed to a submenu. */
			{ "Selection.Edit", TREE_SELECTION_EDIT, &one },
			{ "Selection.Launch", TREE_SELECTION_LAUNCH, 0 },
			{ "Selection.Delete", TREE_SELECTION_DELETE, 0 },
			{ "SelectAll", TREE_SELECT_ALL, 0 },
			{ "Clear", TREE_CLEAR_SELECTION, 0 },
			{NULL, 0, 0}
		}
	};
	hotlist_menu = ro_gui_menu_define_menu(&hotlist_definition);

	/* history menu */
	static const struct ns_menu global_history_definition = {
		"History", {
			{ "History", NO_ACTION, 0 },
			{ "_History.Export", HISTORY_EXPORT, &dialog_saveas },
			{ "History.Expand", TREE_EXPAND_ALL, 0 },
			{ "History.Expand.All", TREE_EXPAND_ALL, 0 },
			{ "History.Expand.Folders", TREE_EXPAND_FOLDERS, 0 },
			{ "History.Expand.Links", TREE_EXPAND_LINKS, 0 },
			{ "History.Collapse", TREE_COLLAPSE_ALL, 0 },
			{ "History.Collapse.All", TREE_COLLAPSE_ALL, 0 },
			{ "History.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 },
			{ "History.Collapse.Links", TREE_COLLAPSE_LINKS, 0 },
			{ "History.Toolbars", NO_ACTION, 0 },
			{ "_History.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 },
			{ "History.Toolbars.EditToolbar",TOOLBAR_EDIT, 0 },
			{ "Selection", TREE_SELECTION, 0 },
			{ "Selection.Launch", TREE_SELECTION_LAUNCH, 0 },
			{ "Selection.Delete", TREE_SELECTION_DELETE, 0 },
			{ "SelectAll", TREE_SELECT_ALL, 0 },
			{ "Clear", TREE_CLEAR_SELECTION, 0 },
			{NULL, 0, 0}
		}
	};
	global_history_menu = ro_gui_menu_define_menu(
			&global_history_definition);

	/* history menu */
	static const struct ns_menu cookies_definition = {
		"Cookies", {
			{ "Cookies", NO_ACTION, 0 },
			{ "Cookies.Expand", TREE_EXPAND_ALL, 0 },
			{ "Cookies.Expand.All", TREE_EXPAND_ALL, 0 },
			{ "Cookies.Expand.Folders", TREE_EXPAND_FOLDERS, 0 },
			{ "Cookies.Expand.Links", TREE_EXPAND_LINKS, 0 },
			{ "Cookies.Collapse", TREE_COLLAPSE_ALL, 0 },
			{ "Cookies.Collapse.All", TREE_COLLAPSE_ALL, 0 },
			{ "Cookies.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 },
			{ "Cookies.Collapse.Links", TREE_COLLAPSE_LINKS, 0 },
			{ "Cookies.Toolbars", NO_ACTION, 0 },
			{ "_Cookies.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 },
			{ "Cookies.Toolbars.EditToolbar",TOOLBAR_EDIT, 0 },
			{ "Selection", TREE_SELECTION, 0 },
			{ "Selection.Delete", TREE_SELECTION_DELETE, 0 },
			{ "SelectAll", TREE_SELECT_ALL, 0 },
			{ "Clear", TREE_CLEAR_SELECTION, 0 },
			{NULL, 0, 0}
		}
	};
	cookies_menu = ro_gui_menu_define_menu(&cookies_definition);

	/* image quality menu */
	static const struct ns_menu images_definition = {
		"Display", {
			{ "ImgStyle0", NO_ACTION, 0 },
			{ "ImgStyle1", NO_ACTION, 0 },
			{ "ImgStyle2", NO_ACTION, 0 },
			{ "ImgStyle3", NO_ACTION, 0 },
			{NULL, 0, 0}
		}
	};
	image_quality_menu = ro_gui_menu_define_menu(&images_definition);

	/* browser toolbar menu */
	static const struct ns_menu browser_toolbar_definition = {
		"Toolbar", {
			{ "Toolbars", NO_ACTION, 0 },
			{ "Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 },
			{ "Toolbars.ToolAddress", TOOLBAR_ADDRESS_BAR, 0 },
			{ "Toolbars.ToolThrob", TOOLBAR_THROBBER, 0 },
			{ "EditToolbar", TOOLBAR_EDIT, 0 },
			{NULL, 0, 0}
		}
	};
	browser_toolbar_menu = ro_gui_menu_define_menu(
			&browser_toolbar_definition);

	/* tree toolbar menu */
	static const struct ns_menu tree_toolbar_definition = {
		"Toolbar", {
			{ "Toolbars", NO_ACTION, 0 },
			{ "Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 },
			{ "EditToolbar", TOOLBAR_EDIT, 0 },
			{NULL, 0, 0}
		}
	};
	tree_toolbar_menu = ro_gui_menu_define_menu(&tree_toolbar_definition);

	/* proxy menu */
	static const struct ns_menu proxy_type_definition = {
		"ProxyType", {
			{ "ProxyNone", NO_ACTION, 0 },
			{ "ProxyNoAuth", NO_ACTION, 0 },
			{ "ProxyBasic", NO_ACTION, 0 },
			{ "ProxyNTLM", NO_ACTION, 0 },
			{NULL, 0, 0}
		}
	};
	proxy_type_menu = ro_gui_menu_define_menu(&proxy_type_definition);

	/* special case menus */
	url_suggest_menu->title_data.indirected_text.text =
			(char *) messages_get("URLSuggest");
	ro_gui_menu_init_structure(url_suggest_menu, GLOBAL_HISTORY_RECENT_URLS);

	/* Note: This table *must* be kept in sync with the LangNames file */
	static const struct ns_menu lang_definition = {
		"Languages", {
			{ "lang_af", NO_ACTION, 0 },
			{ "lang_bm", NO_ACTION, 0 },
			{ "lang_ca", NO_ACTION, 0 },
			{ "lang_cs", NO_ACTION, 0 },
			{ "lang_cy", NO_ACTION, 0 },
			{ "lang_da", NO_ACTION, 0 },
			{ "lang_de", NO_ACTION, 0 },
			{ "lang_en", NO_ACTION, 0 },
			{ "lang_es", NO_ACTION, 0 },
			{ "lang_et", NO_ACTION, 0 },
			{ "lang_eu", NO_ACTION, 0 },
			{ "lang_ff", NO_ACTION, 0 },
			{ "lang_fi", NO_ACTION, 0 },
			{ "lang_fr", NO_ACTION, 0 },
			{ "lang_ga", NO_ACTION, 0 },
			{ "lang_gl", NO_ACTION, 0 },
			{ "lang_ha", NO_ACTION, 0 },
			{ "lang_hr", NO_ACTION, 0 },
			{ "lang_hu", NO_ACTION, 0 },
			{ "lang_id", NO_ACTION, 0 },
			{ "lang_is", NO_ACTION, 0 },
			{ "lang_it", NO_ACTION, 0 },
			{ "lang_lt", NO_ACTION, 0 },
			{ "lang_lv", NO_ACTION, 0 },
			{ "lang_ms", NO_ACTION, 0 },
			{ "lang_mt", NO_ACTION, 0 },
			{ "lang_nl", NO_ACTION, 0 },
			{ "lang_no", NO_ACTION, 0 },
			{ "lang_pl", NO_ACTION, 0 },
			{ "lang_pt", NO_ACTION, 0 },
			{ "lang_rn", NO_ACTION, 0 },
			{ "lang_ro", NO_ACTION, 0 },
			{ "lang_rw", NO_ACTION, 0 },
			{ "lang_sk", NO_ACTION, 0 },
			{ "lang_sl", NO_ACTION, 0 },
			{ "lang_so", NO_ACTION, 0 },
			{ "lang_sq", NO_ACTION, 0 },
			{ "lang_sr", NO_ACTION, 0 },
			{ "lang_sv", NO_ACTION, 0 },
			{ "lang_sw", NO_ACTION, 0 },
			{ "lang_tr", NO_ACTION, 0 },
			{ "lang_uz", NO_ACTION, 0 },
			{ "lang_vi", NO_ACTION, 0 },
			{ "lang_wo", NO_ACTION, 0 },
			{ "lang_xs", NO_ACTION, 0 },
			{ "lang_yo", NO_ACTION, 0 },
			{ "lang_zu", NO_ACTION, 0 },
			{ NULL, 0, 0 }
		}
	};
	languages_menu = ro_gui_menu_define_menu(&lang_definition);
}


/**
 * Display a menu.
 */
void ro_gui_menu_create(wimp_menu *menu, int x, int y, wimp_w w)
{
	struct gui_window *g;
	os_error *error;
	os_coord pos;
	int i;
	menu_action action;
	struct menu_definition *definition;

	/* translate menu, if necessary (this returns quickly
	 * if there's nothing to be done) */
	definition = ro_gui_menu_find_menu(menu);
	if (definition) {
		if (!ro_gui_menu_translate(definition)) {
			warn_user("NoMemory", 0);
			return;
		}
	}

	/* read the object under the pointer for a new gui_window menu */
	if ((!current_menu) && (menu == browser_menu)) {
		hlcache_handle *h;

		g = ro_gui_window_lookup(w);
		h = g->bw->current_content;

		if (!ro_gui_window_to_window_pos(g, x, y, &pos))
			return;
		current_menu_object = NULL;
		current_menu_url = NULL;
		if (h) {
			switch (content_get_type(h)) {
				case CONTENT_HTML: {
					struct box *box;
					box = box_object_at_point(h, pos.x,
							pos.y);
					current_menu_object = box ?
							box->object : NULL;
					box = box_href_at_point(h, pos.x,
							pos.y);
					current_menu_url = box ?
							box->href : NULL;
				}
				break;
				case CONTENT_TEXTPLAIN:
					/* no object, no url */
					break;
				default:
					current_menu_object = h;
					break;
			}
		}
	}

	/* store the menu characteristics */
	current_menu = menu;
	current_menu_window = w;
	current_menu_icon = -1;

	/* prepare the menu state */
	if (menu == url_suggest_menu) {
		if (!ro_gui_menu_prepare_url_suggest())
			return;
	} else if (menu == recent_search_menu) {
	  	if (!ro_gui_search_prepare_menu())
	  		return;
	} else {
		i = 0;
		do {
			action = ro_gui_menu_find_action(menu,
					&menu->entries[i]);
			if (action != NO_ACTION)
				ro_gui_menu_prepare_action(w, action, false);
		} while (!(menu->entries[i++].menu_flags & wimp_MENU_LAST));
	}

	/* create the menu */
	current_menu_open = true;
	error = xwimp_create_menu(menu, x - 64, y);
	if (error) {
		LOG(("xwimp_create_menu: 0x%x: %s",
				error->errnum, error->errmess));
		warn_user("MenuError", error->errmess);
		ro_gui_menu_closed(true);
	}
}


/**
 * Display a pop-up menu next to the specified icon.
 *
 * \param  menu  menu to open
 * \param  w	 window handle
 * \param  i	 icon handle
 */
void ro_gui_popup_menu(wimp_menu *menu, wimp_w w, wimp_i i)
{
	wimp_window_state state;
	wimp_icon_state icon_state;
	os_error *error;

	state.w = w;
	icon_state.w = w;
	icon_state.i = i;
	error = xwimp_get_window_state(&state);
	if (error) {
		LOG(("xwimp_get_window_state: 0x%x: %s",
				error->errnum, error->errmess));
		warn_user("MenuError", error->errmess);
		return;
	}

	error = xwimp_get_icon_state(&icon_state);
	if (error) {
		LOG(("xwimp_get_icon_state: 0x%x: %s",
				error->errnum, error->errmess));
		warn_user("MenuError", error->errmess);
		return;
	}

	ro_gui_menu_create(menu,
			state.visible.x0 + icon_state.icon.extent.x1 + 64,
			state.visible.y1 + icon_state.icon.extent.y1 -
			state.yscroll, w);
	current_menu_icon = i;
}


/**
 * Clean up after a menu has been closed, or forcible close an open menu.
 *
 * \param cleanup	Call any terminating functions (sub-window isn't going to be instantly re-opened)
 */
void ro_gui_menu_closed(bool cleanup)
{
	struct gui_window *g;
	struct browser_window *bw;
	hlcache_handle *h;
	struct toolbar *t;
	struct tree *tree;
	os_error *error;

	if (current_menu) {

		error = xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
		if (error) {
			LOG(("xwimp_create_menu: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("MenuError", error->errmess);
		}
		ro_gui_menu_get_window_details(current_menu_window,
				&g, &bw, &h, &t, &tree);
		current_menu = NULL;

		if (cleanup) {
		  	ro_gui_wimp_event_menus_closed();

			if (tree)
				ro_gui_tree_menu_closed(tree);
		}
	}

	current_menu_window = NULL;
	current_menu_icon = 0;
	current_menu_open = false;
	gui_form_select_control = NULL;
}


/**
 * The content has changed, reset object references
 */
void ro_gui_menu_objects_moved(void)
{
  	gui_form_select_control = NULL;
	current_menu_object = NULL;
	current_menu_url = NULL;

	ro_gui_menu_prepare_action(0, BROWSER_OBJECT, false);
	if ((current_menu) && (current_menu == gui_form_select_menu))
		ro_gui_menu_closed(true);
}


/**
 * Handle menu selection.
 */
void ro_gui_menu_selection(wimp_selection *selection)
{
	int i, j;
	wimp_menu_entry *menu_entry;
	menu_action action;
	wimp_pointer pointer;
	struct gui_window *g = NULL;
	wimp_menu *menu;
	os_error *error;
	int previous_menu_icon = current_menu_icon;
	char *url;

	/* if we are using gui_multitask then menu selection events
	 * may be delivered after the menu has been closed. As such,
	 * we simply ignore these events. */
	if (!current_menu)
		return

	assert(current_menu_window);

	/* get the menu entry and associated action */
	menu_entry = &current_menu->entries[selection->items[0]];
	for (i = 1; selection->items[i] != -1; i++)
		menu_entry = &menu_entry->sub_menu->
				entries[selection->items[i]];
	action = ro_gui_menu_find_action(current_menu, menu_entry);

	/* perform menu action */
	if (action != NO_ACTION)
		ro_gui_menu_handle_action(current_menu_window, action, false);

	/* perform non-automated actions */
	if (current_menu == url_suggest_menu) {
		g = ro_gui_toolbar_lookup(current_menu_window);
		if (g) {
		  	url = url_suggest_menu->entries[selection->items[0]].
					data.indirected_text.text;
			gui_window_set_url(g, url);
			browser_window_go(g->bw, url, 0, true);
			global_history_add_recent(url);
		}
	} else if (current_menu == gui_form_select_menu) {
		g = ro_gui_window_lookup(current_menu_window);
		assert(g);

		if (selection->items[0] >= 0) {
			form_select_process_selection(g->bw->current_content,
					gui_form_select_control,
					selection->items[0]);
		}
	}

	/* allow automatic menus to have their data updated */
	ro_gui_wimp_event_menu_selection(current_menu_window, current_menu_icon,
			current_menu, selection);

	/* re-open the menu for Adjust clicks */
	error = xwimp_get_pointer_info(&pointer);
	if (error) {
		LOG(("xwimp_get_pointer_info: 0x%x: %s",
				error->errnum, error->errmess));
		warn_user("WimpError", error->errmess);
		ro_gui_menu_closed(true);
		return;
	}

	if (pointer.buttons != wimp_CLICK_ADJUST) {
		ro_gui_menu_closed(true);
		return;
	}

	/* re-prepare all the visible entries */
	i = 0;
	menu = current_menu;
	do {
		j = 0;
		do {
			action = ro_gui_menu_find_action(current_menu,
					&menu->entries[j]);
			if (action != NO_ACTION)
				ro_gui_menu_prepare_action(current_menu_window,
						action, false);
		} while (!(menu->entries[j++].menu_flags & wimp_MENU_LAST));
		j = selection->items[i++];
		if (j != -1) {
			menu = menu->entries[j].sub_menu;
			if ((!menu) || (menu == wimp_NO_SUB_MENU))
				break;
		}
	} while (j != -1);

	if (current_menu == gui_form_select_menu) {
		assert(g); /* Keep scan-build happy */
		gui_create_form_select_menu(g->bw, gui_form_select_control);
	} else
		ro_gui_menu_create(current_menu, 0, 0, current_menu_window);

	current_menu_icon = previous_menu_icon;
}


/**
 * Handle Message_MenuWarning.
 */
void ro_gui_menu_warning(wimp_message_menu_warning *warning)
{
	int i;
	menu_action action;
	wimp_menu_entry *menu_entry;
	wimp_menu *sub_menu;
	os_error *error;

	assert(current_menu);
	assert(current_menu_window);

	/* get the sub-menu of the warning */
	if (warning->selection.items[0] == -1)
		return;
	menu_entry = &current_menu->entries[warning->selection.items[0]];
	for (i = 1; warning->selection.items[i] != -1; i++)
		menu_entry = &menu_entry->sub_menu->
				entries[warning->selection.items[i]];

	if (IS_MENU(menu_entry->sub_menu)) {
		ro_gui_wimp_event_register_submenu((wimp_w)0);
		sub_menu = menu_entry->sub_menu;
		i = 0;
		do {
			action = ro_gui_menu_find_action(current_menu,
					&sub_menu->entries[i]);
			if (action != NO_ACTION)
				ro_gui_menu_prepare_action(current_menu_window,
						action, false);
		} while (!(sub_menu->entries[i++].menu_flags & wimp_MENU_LAST));
	} else {
		ro_gui_wimp_event_register_submenu((wimp_w)menu_entry->sub_menu);
		action = ro_gui_menu_find_action(current_menu, menu_entry);
		if (action != NO_ACTION)
			ro_gui_menu_prepare_action(current_menu_window,
					action, true);
		/* remove the close icon */
		ro_gui_wimp_update_window_furniture((wimp_w)menu_entry->sub_menu,
				wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_BACK_ICON, 0);
	}

	/* open the sub-menu */
	error = xwimp_create_sub_menu(menu_entry->sub_menu,
			warning->pos.x, warning->pos.y);
	if (error) {
		LOG(("xwimp_create_sub_menu: 0x%x: %s",
				error->errnum, error->errmess));
		warn_user("MenuError", error->errmess);
	}
}


/**
 * Refresh a toolbar after it has been updated
 *
 * \param toolbar  the toolbar to update
 */
void ro_gui_menu_refresh_toolbar(struct toolbar *toolbar)
{

	assert(toolbar);

	toolbar->reformat_buttons = true;
	ro_gui_theme_process_toolbar(toolbar, -1);
	if (toolbar->type == THEME_BROWSER_TOOLBAR) {
		gui_window_update_extent(ro_gui_window_lookup(current_menu_window));
	} else if (toolbar->type == THEME_HOTLIST_TOOLBAR) {
		tree_resized(hotlist_tree);
		xwimp_force_redraw((wimp_w)hotlist_tree->handle,
				0,-16384, 16384, 16384);
	} else if (toolbar->type == THEME_HISTORY_TOOLBAR) {
		tree_resized(global_history_tree);
		xwimp_force_redraw((wimp_w)global_history_tree->handle,
				0,-16384, 16384, 16384);
	} else if (toolbar->type == THEME_COOKIES_TOOLBAR) {
		tree_resized(cookies_tree);
		xwimp_force_redraw((wimp_w)cookies_tree->handle,
				0,-16384, 16384, 16384);
	}
}


/**
 * Builds the URL suggestion menu
 */
bool ro_gui_menu_prepare_url_suggest(void) {
	char **suggest_text;
	int suggestions;
	int i;

	suggest_text = global_history_get_recent(&suggestions);
	if (suggestions < 1)
		return false;

	for (i = 0; i < suggestions; i++) {
		url_suggest_menu->entries[i].menu_flags = 0;
		url_suggest_menu->entries[i].data.indirected_text.text =
				suggest_text[i];
		url_suggest_menu->entries[i].data.indirected_text.size =
				strlen(suggest_text[i]) + 1;
	}

	url_suggest_menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED;
	url_suggest_menu->entries[suggestions - 1].menu_flags |= wimp_MENU_LAST;
	return true;
}


/**
 * Update navigate menu status and toolbar icons.
 *
 * /param gui  the gui_window to update
 */
void ro_gui_prepare_navigate(struct gui_window *gui)
{
	int suggestions;

	ro_gui_menu_prepare_action(gui->window, HOTLIST_SHOW, false);
	ro_gui_menu_prepare_action(gui->window, BROWSER_NAVIGATE_STOP, false);
	ro_gui_menu_prepare_action(gui->window, BROWSER_NAVIGATE_RELOAD_ALL,
			false);
	ro_gui_menu_prepare_action(gui->window, BROWSER_NAVIGATE_BACK, false);
	ro_gui_menu_prepare_action(gui->window, BROWSER_NAVIGATE_FORWARD,
			false);
	ro_gui_menu_prepare_action(gui->window, BROWSER_NAVIGATE_UP, false);
	ro_gui_menu_prepare_action(gui->window, HOTLIST_SHOW, false);
	ro_gui_menu_prepare_action(gui->window, BROWSER_SAVE, false);
	ro_gui_menu_prepare_action(gui->window, BROWSER_PRINT, false);
	ro_gui_menu_prepare_action(gui->window, BROWSER_SCALE_VIEW, false);
	ro_gui_menu_prepare_action(gui->window, BROWSER_FIND_TEXT, false);

	if (gui->toolbar) {
		global_history_get_recent(&suggestions);
		ro_gui_set_icon_shaded_state(gui->toolbar->toolbar_handle,
				ICON_TOOLBAR_SUGGEST, (suggestions <= 0));
	}
}


/**
 * Prepare the page info window for use
 *
 * \param g  the gui_window to set the display icons for
 */
void ro_gui_menu_prepare_pageinfo(struct gui_window *g)
{
	hlcache_handle *h = g->bw->current_content;
	char icon_buf[20] = "file_xxx";
	char enc_buf[40];
	char enc_token[10] = "Encoding0";
	const char *icon = icon_buf;
	const char *title, *url, *mime;
	const char *enc = "-";

	assert(h);

	title = content_get_title(h);
	if (title == NULL)
		title = "-";
	url = content_get_url(h);
	if (url == NULL)
		url = "-";
	mime = content_get_mime_type(h);
	if (mime == NULL)
		mime = "-";

	sprintf(icon_buf, "file_%x", ro_content_filetype(h));
	if (!ro_gui_wimp_sprite_exists(icon_buf))
		sprintf(icon_buf, "file_xxx");

	if (content_get_type(h) == CONTENT_HTML) {
		if (html_get_encoding(h)) {
			enc_token[8] = '0' + html_get_encoding_source(h);
			snprintf(enc_buf, sizeof enc_buf, "%s (%s)",
					html_get_encoding(h),
					messages_get(enc_token));
			enc = enc_buf;
		} else {
			enc = messages_get("EncodingUnk");
		}
	}

	ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_ICON, icon, true);
	ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_TITLE, title, true);
	ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_URL, url, true);
	ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_ENC, enc, true);
	ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_TYPE, mime, true);
}


/**
 * Prepare the object info window for use
 *
 * \param object  the object for which information is to be displayed
 * \param href    corresponding href, if any
 */
void ro_gui_menu_prepare_objectinfo(hlcache_handle *object, const char *href)
{
	char icon_buf[20] = "file_xxx";
	const char *url, *mime;
	const char *target = "-";

	sprintf(icon_buf, "file_%.3x",
			ro_content_filetype(object));
	if (!ro_gui_wimp_sprite_exists(icon_buf))
		sprintf(icon_buf, "file_xxx");

	url = content_get_url(object);
	if (url == NULL)
		url = "-";
	mime = content_get_mime_type(object);
	if (mime == NULL)
		mime = "-";
		
	if (href)
		target = href;

	ro_gui_set_icon_string(dialog_objinfo, ICON_OBJINFO_ICON, icon_buf, true);
	ro_gui_set_icon_string(dialog_objinfo, ICON_OBJINFO_URL, url, true);
	ro_gui_set_icon_string(dialog_objinfo, ICON_OBJINFO_TARGET, target, true);
	ro_gui_set_icon_string(dialog_objinfo, ICON_OBJINFO_TYPE, mime, true);
}


/**
 * Display a menu of options for a form select control.
 *
 * \param  bw	    browser window containing form control
 * \param  control  form control of type GADGET_SELECT
 */
void gui_create_form_select_menu(struct browser_window *bw,
		struct form_control *control)
{
	unsigned int i, entries;
	char *text_convert, *temp;
	struct form_option *option;
	wimp_pointer pointer;
	os_error *error;
	bool reopen = true;
	utf8_convert_ret err;

	assert(control);

	for (entries = 0, option = control->data.select.items; option;
			option = option->next)
		entries++;
	if (entries == 0) {
		ro_gui_menu_closed(true);
		return;
	}

	if ((gui_form_select_menu) && (control != gui_form_select_control)) {
		for (i = 0; ; i++) {
			free(gui_form_select_menu->entries[i].data.
					indirected_text.text);
			if (gui_form_select_menu->entries[i].menu_flags &
					wimp_MENU_LAST)
				break;
		}
		free(gui_form_select_menu->title_data.indirected_text.text);
		free(gui_form_select_menu);
		gui_form_select_menu = 0;
	}

	if (!gui_form_select_menu) {
		reopen = false;
		gui_form_select_menu = malloc(wimp_SIZEOF_MENU(entries));
		if (!gui_form_select_menu) {
			warn_user("NoMemory", 0);
			ro_gui_menu_closed(true);
			return;
		}
		err = utf8_to_local_encoding(messages_get("SelectMenu"), 0,
				&text_convert);
		if (err != UTF8_CONVERT_OK) {
			/* badenc should never happen */
			assert(err != UTF8_CONVERT_BADENC);
			LOG(("utf8_to_local_encoding failed"));
			warn_user("NoMemory", 0);
			ro_gui_menu_closed(true);
			return;
		}
		gui_form_select_menu->title_data.indirected_text.text =
				text_convert;
		ro_gui_menu_init_structure(gui_form_select_menu, entries);
	}

	for (i = 0, option = control->data.select.items; option;
			i++, option = option->next) {
		gui_form_select_menu->entries[i].menu_flags = 0;
		if (option->selected)
			gui_form_select_menu->entries[i].menu_flags =
					wimp_MENU_TICKED;
		if (!reopen) {

			/* convert spaces to hard spaces to stop things
			 * like 'Go Home' being treated as if 'Home' is a
			 * keyboard shortcut and right aligned in the menu.
			 */

			temp = cnv_space2nbsp(option->text);
			if (!temp) {
				LOG(("cnv_space2nbsp failed"));
				warn_user("NoMemory", 0);
				ro_gui_menu_closed(true);
				return;
			}

			err = utf8_to_local_encoding(temp,
				0, &text_convert);
			if (err != UTF8_CONVERT_OK) {
				/* A bad encoding should never happen,
				 * so assert this */
				assert(err != UTF8_CONVERT_BADENC);
				LOG(("utf8_to_enc failed"));
				warn_user("NoMemory", 0);
				ro_gui_menu_closed(true);
				return;
			}

			free(temp);

			gui_form_select_menu->entries[i].data.indirected_text.text =
					text_convert;
			gui_form_select_menu->entries[i].data.indirected_text.size =
					strlen(gui_form_select_menu->entries[i].
					data.indirected_text.text) + 1;
		}
	}

	gui_form_select_menu->entries[0].menu_flags |=
			wimp_MENU_TITLE_INDIRECTED;
	gui_form_select_menu->entries[i - 1].menu_flags |= wimp_MENU_LAST;

	error = xwimp_get_pointer_info(&pointer);
	if (error) {
		LOG(("xwimp_get_pointer_info: 0x%x: %s",
				error->errnum, error->errmess));
		warn_user("WimpError", error->errmess);
		ro_gui_menu_closed(true);
		return;
	}

	gui_form_select_control = control;
	ro_gui_menu_create(gui_form_select_menu,
			pointer.pos.x, pointer.pos.y, bw->window->window);
}


/**
 * Creates a wimp_menu and adds it to the list to handle actions for.
 *
 * \param menu  the data to create the menu with
 * \return the menu created, or NULL on failure
 */
wimp_menu *ro_gui_menu_define_menu(const struct ns_menu *menu)
{
	struct menu_definition *definition;
	int entry;

	definition = calloc(sizeof(struct menu_definition), 1);
	if (!definition) {
		die("No memory to create menu definition.");
		return NULL; /* For the benefit of scan-build */
	}

	/* link in the menu to our list */
	definition->next = ro_gui_menu_definitions;
	ro_gui_menu_definitions = definition;

	/* count number of menu entries */
	for (entry = 0; menu->entries[entry].text; entry++)
		/* do nothing */;

	/* create our definitions */
	ro_gui_menu_define_menu_add(definition, menu, 0, NULL,
			0, entry, NULL, 0);

	/* and translate menu into current encoding */
	if (!ro_gui_menu_translate(definition))
		die("No memory to translate menu.");

	return definition->menu;
}

/**
 * Create a wimp menu tree from ns_menu data.
 * This function does *not* deal with the menu textual content - it simply
 * creates and populates the appropriate structures. Textual content is
 * generated by ro_gui_menu_translate_menu()
 *
 * \param definition  Top level menu definition
 * \param menu  Menu declaration data
 * \param depth  Depth of menu we're currently building
 * \param parent_entry  Entry in parent menu, or NULL if root menu
 * \param first  First index in declaration data that is used by this menu
 * \param last Last index in declaration data that is used by this menu
 * \param prefix  Prefix pf menu declaration string already seen
 * \param prefix_length  Length of prefix
 */
void ro_gui_menu_define_menu_add(struct menu_definition *definition,
		const struct ns_menu *menu, int depth,
		wimp_menu_entry *parent_entry, int first, int last,
		const char *prefix, int prefix_length)
{
	int entry, id, cur_depth;
	int entries = 0;
	int matches[last - first + 1];
	const char *match;
	const char *text, *menu_text;
	wimp_menu *new_menu;
	struct menu_definition_entry *definition_entry;

	/* step 1: store the matches for depth and subset string */
	for (entry = first; entry < last; entry++) {
		cur_depth = 0;
		match = menu->entries[entry].text;

		/* skip specials at start of string */
		while (!isalnum(*match))
			match++;

		/* attempt prefix match */
		if ((prefix) && (strncmp(match, prefix, prefix_length)))
			continue;

		/* Find depth of this entry */
		while (*match)
			if (*match++ == '.')
				cur_depth++;

		if (depth == cur_depth)
			matches[entries++] = entry;
	}
	matches[entries] = last;

	/* no entries, so exit */
	if (entries == 0)
		return;

	/* step 2: build and link the menu. we must use realloc to stop
	 * our memory fragmenting so we can test for sub-menus easily */
	new_menu = (wimp_menu *)malloc(wimp_SIZEOF_MENU(entries));
	if (!new_menu)
		die("No memory to create menu.");

	if (parent_entry) {
		/* Fix up sub menu pointer */
		parent_entry->sub_menu = new_menu;
	} else {
		/* Root menu => fill in definition struct */
		definition->title_key = menu->title;
		definition->current_encoding = 0;
		definition->menu = new_menu;
	}

	/* this is fixed up in ro_gui_menu_translate() */
	new_menu->title_data.indirected_text.text = NULL;

	/* fill in menu flags */
	ro_gui_menu_init_structure(new_menu, entries);

	/* and then create the entries */
	for (entry = 0; entry < entries; entry++) {
		/* add the entry */
		id = matches[entry];

		text = menu->entries[id].text;

		/* fill in menu flags from specials at start of string */
		new_menu->entries[entry].menu_flags = 0;
		while (!isalnum(*text)) {
			if (*text == '_')
				new_menu->entries[entry].menu_flags |=
							wimp_MENU_SEPARATE;
			text++;
		}

		/* get messages key for menu entry */
		menu_text = strrchr(text, '.');
		if (!menu_text)
			/* no '.' => top-level entry */
			menu_text = text;
		else
			menu_text++; /* and move past the '.' */

		/* fill in submenu data */
		if (menu->entries[id].sub_window)
			new_menu->entries[entry].sub_menu =
				(wimp_menu *) (*menu->entries[id].sub_window);

		/* this is fixed up in ro_gui_menu_translate() */
		new_menu->entries[entry].data.indirected_text.text = NULL;

		/* create definition entry */
		definition_entry =
			malloc(sizeof(struct menu_definition_entry));
		if (!definition_entry)
			die("Unable to create menu definition entry");
		definition_entry->action = menu->entries[id].action;
		definition_entry->menu_entry = &new_menu->entries[entry];
		definition_entry->entry_key = menu_text;
		definition_entry->next = definition->entries;
		definition->entries = definition_entry;

		/* recurse */
		if (new_menu->entries[entry].sub_menu == wimp_NO_SUB_MENU) {
			ro_gui_menu_define_menu_add(definition, menu,
					depth + 1, &new_menu->entries[entry],
					matches[entry], matches[entry + 1],
					text, strlen(text));
		}

		/* give menu warnings */
		if (new_menu->entries[entry].sub_menu != wimp_NO_SUB_MENU)
			new_menu->entries[entry].menu_flags |=
						wimp_MENU_GIVE_WARNING;
	}
	new_menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED;
	new_menu->entries[entries - 1].menu_flags |= wimp_MENU_LAST;
}


/**
 * Initialise the basic state of a menu structure so all entries are
 * indirected text with no flags, no submenu.
 */
void ro_gui_menu_init_structure(wimp_menu *menu, int entries)
{
  	int i;

	menu->title_fg = wimp_COLOUR_BLACK;
	menu->title_bg = wimp_COLOUR_LIGHT_GREY;
	menu->work_fg = wimp_COLOUR_BLACK;
	menu->work_bg = wimp_COLOUR_WHITE;
	menu->width = 200;
	menu->height = wimp_MENU_ITEM_HEIGHT;
	menu->gap = wimp_MENU_ITEM_GAP;

	for (i = 0; i < entries; i++) {
		menu->entries[i].menu_flags = 0;
		menu->entries[i].sub_menu = wimp_NO_SUB_MENU;
		menu->entries[i].icon_flags =
				DEFAULT_FLAGS | wimp_ICON_INDIRECTED;
		menu->entries[i].data.indirected_text.validation =
				(char *)-1;
	}
	menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED;
	menu->entries[i - 1].menu_flags |= wimp_MENU_LAST;
}


/**
 * Finds the menu_definition corresponding to a wimp_menu.
 *
 * \param menu  the menu to find the definition for
 * \return the associated definition, or NULL if one could not be found
 */
struct menu_definition *ro_gui_menu_find_menu(wimp_menu *menu)
{
	struct menu_definition *definition;

	if (!menu)
		return NULL;

	for (definition = ro_gui_menu_definitions; definition;
			definition = definition->next)
		if (definition->menu == menu)
			return definition;
	return NULL;
}


/**
 * Finds the key associated with a menu entry translation.
 *
 * \param menu  the menu to search
 * \param translated  the translated text
 * \return the original message key, or NULL if one could not be found
 */
const char *ro_gui_menu_find_menu_entry_key(wimp_menu *menu,
		const char *translated)
{
	struct menu_definition_entry *entry;
	struct menu_definition *definition = ro_gui_menu_find_menu(menu);

	if (!definition)
		return NULL;

	for (entry = definition->entries; entry; entry = entry->next)
		if (!strcmp(entry->menu_entry->data.indirected_text.text, translated))
			return entry->entry_key;
	return NULL;
}


/**
 * Finds the menu_definition_entry corresponding to an action for a wimp_menu.
 *
 * \param menu	  the menu to search for an action within
 * \param action  the action to find
 * \return the associated menu entry, or NULL if one could not be found
 */
struct menu_definition_entry *ro_gui_menu_find_entry(wimp_menu *menu,
		menu_action action)
{
	struct menu_definition_entry *entry;
	struct menu_definition *definition = ro_gui_menu_find_menu(menu);

	if (!definition)
		return NULL;

	for (entry = definition->entries; entry; entry = entry->next)
		if (entry->action == action)
			return entry;
	return NULL;
}


/**
 * Finds the action corresponding to a wimp_menu_entry for a wimp_menu.
 *
 * \param menu	      the menu to search for an action within
 * \param menu_entry  the menu_entry to find
 * \return the associated action, or 0 if one could not be found
 */
menu_action ro_gui_menu_find_action(wimp_menu *menu, wimp_menu_entry *menu_entry)
{
	struct menu_definition_entry *entry;
	struct menu_definition *definition = ro_gui_menu_find_menu(menu);

	if (!definition)
		return NO_ACTION;

	for (entry = definition->entries; entry; entry = entry->next) {
		if (entry->menu_entry == menu_entry)
			return entry->action;
	}
	return NO_ACTION;
}


/**
 * Sets an action within a menu as having a specific ticked status.
 *
 * \param menu	  the menu containing the action
 * \param action  the action to tick/untick
 * \param ticked  whether to set the item as ticked
 */
void ro_gui_menu_set_entry_shaded(wimp_menu *menu, menu_action action,
		bool shaded)
{
	struct menu_definition_entry *entry;
	struct menu_definition *definition = ro_gui_menu_find_menu(menu);

	if (!definition)
		return;

	/* we can't use find_entry as multiple actions may appear in one menu */
	for (entry = definition->entries; entry; entry = entry->next)
		if (entry->action == action) {
			if (shaded)
				entry->menu_entry->icon_flags |= wimp_ICON_SHADED;
			else
				entry->menu_entry->icon_flags &= ~wimp_ICON_SHADED;
		}
}


/**
 * Sets an action within a menu as having a specific ticked status.
 *
 * \param menu	  the menu containing the action
 * \param action  the action to tick/untick
 * \param ticked  whether to set the item as ticked
 */
void ro_gui_menu_set_entry_ticked(wimp_menu *menu, menu_action action,
		bool ticked)
{
	struct menu_definition_entry *entry =
			ro_gui_menu_find_entry(menu, action);
	if (entry) {
		if (ticked)
			entry->menu_entry->menu_flags |= wimp_MENU_TICKED;
		else
			entry->menu_entry->menu_flags &= ~wimp_MENU_TICKED;
	}
}


/**
 * Handles an action.
 *
 * \param owner		      the window to handle the action for
 * \param action	      the action to handle
 * \param windows_at_pointer  whether to open any windows at the pointer location
 */
bool ro_gui_menu_handle_action(wimp_w owner, menu_action action,
		bool windows_at_pointer)
{
	wimp_window_state state;
	struct gui_window *g = NULL;
	struct browser_window *bw = NULL;
	hlcache_handle *h = NULL;
	struct toolbar *t = NULL;
	struct tree *tree;
	struct node *node;
	os_error *error;
	char url[80];
	const struct url_data *data;

	ro_gui_menu_get_window_details(owner, &g, &bw, &h, &t, &tree);

	switch (action) {

		/* help actions */
		case HELP_OPEN_CONTENTS:
			ro_gui_open_help_page("documentation/index");
			return true;
		case HELP_OPEN_GUIDE:
			ro_gui_open_help_page("documentation/guide");
			return true;
		case HELP_OPEN_INFORMATION:
			ro_gui_open_help_page("documentation/info");
			return true;
		case HELP_OPEN_ABOUT:
			ro_gui_open_help_page("about/index");
			return true;
		case HELP_LAUNCH_INTERACTIVE:
			if (!ro_gui_interactive_help_available()) {
				ro_gui_interactive_help_start();
				option_interactive_help = true;
			} else {
				option_interactive_help = !option_interactive_help;
			}
			return true;

		/* history actions */
		case HISTORY_SHOW_LOCAL:
			if ((!bw) || (!bw->history))
				return false;
			ro_gui_history_open(bw, bw->history,
						windows_at_pointer);
			return true;
		case HISTORY_SHOW_GLOBAL:
			ro_gui_tree_show(global_history_tree);
			return true;

		/* hotlist actions */
		case HOTLIST_ADD_URL:
			if ((!hotlist_tree) || (!h) || (!content_get_url(h)))
				return false;
			data = urldb_get_url_data(content_get_url(h));
			if (data) {
				node = tree_create_URL_node(hotlist_tree->root,
						content_get_url(h),
						data, data->title);
				if (node) {
					tree_redraw_area(hotlist_tree,
							node->box.x - NODE_INSTEP, 0,
							NODE_INSTEP, 16384);
					tree_handle_node_changed(hotlist_tree,
							node, false, true);
					ro_gui_tree_scroll_visible(hotlist_tree,
							&node->data);
					ro_gui_hotlist_save();
				}
			}
			return true;
		case HOTLIST_SHOW:
			ro_gui_tree_show(hotlist_tree);
			return true;

		/* cookies actions */
		case COOKIES_SHOW:
			ro_gui_tree_show(cookies_tree);
			return true;

		case COOKIES_DELETE:
			if (cookies_tree->root->child)
				tree_delete_node(cookies_tree, cookies_tree->root->child, true);
			return true;

		/* page actions */
		case BROWSER_PAGE_INFO:
			if (!h)
				return false;
			ro_gui_menu_prepare_action(owner, action, true);
			ro_gui_dialog_open_persistent(g->window,
					dialog_pageinfo, windows_at_pointer);
			return true;
		case BROWSER_PRINT:
			if (!h)
				return false;
			ro_gui_menu_prepare_action(owner, action, true);
			ro_gui_dialog_open_persistent(g->window,
					dialog_print, windows_at_pointer);
			return true;
		case BROWSER_NEW_WINDOW:
			if (!h)
				return false;
			browser_window_create(content_get_url(h), bw, 0, false,
					false);
			return true;
		case BROWSER_VIEW_SOURCE:
			if (!h)
				return false;
			ro_gui_view_source(h);
			return true;

		/* object actions */
		case BROWSER_OBJECT_INFO:
			if (!current_menu_object)
				return false;
			ro_gui_menu_prepare_action(owner, action, true);
			ro_gui_dialog_open_persistent(g->window,
					dialog_objinfo, windows_at_pointer);
			return true;
		case BROWSER_OBJECT_RELOAD:
			if (!current_menu_object)
				return false;
			content_invalidate_reuse_data(current_menu_object);
			browser_window_reload(bw, false);
			return true;

		/* link actions */
		case BROWSER_LINK_SAVE_URI:
		case BROWSER_LINK_SAVE_URL:
		case BROWSER_LINK_SAVE_TEXT:
			if (!current_menu_url)
				return false;
			ro_gui_menu_prepare_action(owner, action, true);
			ro_gui_dialog_open_persistent(owner, dialog_saveas,
					windows_at_pointer);
			break;
		case BROWSER_LINK_DOWNLOAD:
			if (!current_menu_url)
				return false;
			browser_window_download(bw, current_menu_url,
					content_get_url(h));
			break;
		case BROWSER_LINK_NEW_WINDOW:
			if (!current_menu_url)
				return false;
			browser_window_create(current_menu_url, bw,
					content_get_url(h), true, false);
			break;

		/* save actions */
		case BROWSER_OBJECT_SAVE:
		case BROWSER_OBJECT_EXPORT_SPRITE:
		case BROWSER_OBJECT_EXPORT_DRAW:
			h = current_menu_object;
			/* Fall through */
		case BROWSER_SAVE:
		case BROWSER_SAVE_COMPLETE:
		case BROWSER_EXPORT_DRAW:
		case BROWSER_EXPORT_PDF:
		case BROWSER_EXPORT_TEXT:
		case BROWSER_SAVE_URL_URI:
		case BROWSER_SAVE_URL_URL:
		case BROWSER_SAVE_URL_TEXT:
			if (!h)
				return false;
			/* Fall through */
		case HOTLIST_EXPORT:
		case HISTORY_EXPORT:
			ro_gui_menu_prepare_action(owner, action, true);
			ro_gui_dialog_open_persistent(owner, dialog_saveas,
					windows_at_pointer);
			return true;

		/* selection actions */
		case BROWSER_SELECTION_SAVE:
			ro_gui_menu_prepare_action(owner, action, true);
			ro_gui_dialog_open_persistent(owner, dialog_saveas,
					windows_at_pointer);
			return true;
		case BROWSER_SELECTION_COPY:
			browser_window_key_press(bw, KEY_COPY_SELECTION);
			break;
		case BROWSER_SELECTION_CUT:
			browser_window_key_press(bw, KEY_CUT_SELECTION);
			return true;
		case BROWSER_SELECTION_PASTE:
			browser_window_key_press(bw, KEY_PASTE);
			return true;
		case BROWSER_SELECTION_ALL:
			browser_window_key_press(bw, KEY_SELECT_ALL);
			break;
		case BROWSER_SELECTION_CLEAR:
			browser_window_key_press(bw, KEY_CLEAR_SELECTION);
			break;

		/* navigation actions */
		case BROWSER_NAVIGATE_HOME:
			if (!bw)
				return false;
			if ((option_homepage_url) &&
					(option_homepage_url[0])) {
				browser_window_go(g->bw,
						option_homepage_url, 0, true);
			} else {
				snprintf(url, sizeof url,
						"file:///<NetSurf$Dir>/Docs/welcome/index_%s",
						option_language);
				browser_window_go(g->bw, url, 0, true);
			}
			return true;
		case BROWSER_NAVIGATE_BACK:
			if ((!bw) || (!bw->history))
				return false;
			history_back(bw, bw->history);
			return true;
		case BROWSER_NAVIGATE_FORWARD:
			if ((!bw) || (!bw->history))
				return false;
			history_forward(bw, bw->history);
			return true;
		case BROWSER_NAVIGATE_UP:
			if ((!bw) || (!h))
				return false;
			return ro_gui_window_navigate_up(bw->window,
					content_get_url(h));
		case BROWSER_NAVIGATE_RELOAD:
		case BROWSER_NAVIGATE_RELOAD_ALL:
			if (!bw)
				return false;
			browser_window_reload(bw,
					(action == BROWSER_NAVIGATE_RELOAD_ALL));
			return true;
		case BROWSER_NAVIGATE_STOP:
			if (!bw)
				return false;
			browser_window_stop(bw);
			return true;
		case BROWSER_NAVIGATE_URL:
			ro_gui_menu_prepare_action(owner, action, true);
			ro_gui_dialog_open_persistent(NULL, dialog_openurl,
					windows_at_pointer);
			return true;

		/* browser window/display actions */
		case BROWSER_SCALE_VIEW:
			if (!h)
				return false;
			ro_gui_menu_prepare_action(owner, action, true);
			ro_gui_dialog_open_persistent(g->window, dialog_zoom,
					windows_at_pointer);
			return true;
		case BROWSER_FIND_TEXT:
			if (!h || (content_get_type(h) != CONTENT_TEXTPLAIN &&
					content_get_type(h) != CONTENT_HTML))
				return false;
			ro_gui_menu_prepare_action(owner, action, true);
			ro_gui_dialog_open_persistent(g->window,
					dialog_search, windows_at_pointer);
			return true;
		case BROWSER_IMAGES_BACKGROUND:
			if (!g)
				return false;
			g->option.background_images =
					!g->option.background_images;
			gui_window_redraw_window(g);
			return true;
		case BROWSER_BUFFER_ANIMS:
			if (!g)
				return false;
			g->option.buffer_animations =
					!g->option.buffer_animations;
			break;
		case BROWSER_BUFFER_ALL:
			if (!g)
				return false;
			g->option.buffer_everything =
					!g->option.buffer_everything;
			break;
		case BROWSER_SAVE_VIEW:
			if (!bw)
				return false;
			ro_gui_window_default_options(bw);
			ro_gui_save_options();
			return true;
		case BROWSER_WINDOW_DEFAULT:
			if (!g)
				return false;
			ro_gui_screen_size(&option_window_screen_width,
					&option_window_screen_height);
			state.w = current_menu_window;
			error = xwimp_get_window_state(&state);
			if (error) {
				LOG(("xwimp_get_window_state: 0x%x: %s",
						error->errnum,
						error->errmess));
				warn_user("WimpError", error->errmess);
			}
			option_window_x = state.visible.x0;
			option_window_y = state.visible.y0;
			option_window_width =
					state.visible.x1 - state.visible.x0;
			option_window_height =
					state.visible.y1 - state.visible.y0;
			ro_gui_save_options();
			return true;
		case BROWSER_WINDOW_STAGGER:
			option_window_stagger = !option_window_stagger;
			ro_gui_save_options();
			return true;
		case BROWSER_WINDOW_COPY:
			option_window_size_clone = !option_window_size_clone;
			ro_gui_save_options();
			return true;
		case BROWSER_WINDOW_RESET:
			option_window_screen_width = 0;
			option_window_screen_height = 0;
			ro_gui_save_options();
			return true;

		/* tree actions */
		case TREE_NEW_FOLDER:
			ro_gui_menu_prepare_action(owner, action, true);
			ro_gui_dialog_open_persistent((wimp_w)tree->handle,
					dialog_folder, windows_at_pointer);
			return true;
		case TREE_NEW_LINK:
			ro_gui_menu_prepare_action(owner, action, true);
			ro_gui_dialog_open_persistent((wimp_w)tree->handle,
					dialog_entry, windows_at_pointer);
			return true;
		case TREE_EXPAND_ALL:
		case TREE_EXPAND_FOLDERS:
		case TREE_EXPAND_LINKS:
			tree_handle_expansion(tree, tree->root, true,
					(action != TREE_EXPAND_LINKS),
					(action != TREE_EXPAND_FOLDERS));
			return true;
		case TREE_COLLAPSE_ALL:
		case TREE_COLLAPSE_FOLDERS:
		case TREE_COLLAPSE_LINKS:
			tree_handle_expansion(tree, tree->root, false,
					(action != TREE_COLLAPSE_LINKS),
					(action != TREE_COLLAPSE_FOLDERS));
			return true;
		case TREE_SELECTION_EDIT:
			return true;
		case TREE_SELECTION_LAUNCH:
			ro_gui_tree_launch_selected(tree);
			return true;
		case TREE_SELECTION_DELETE:
			ro_gui_tree_stop_edit(tree);
			tree_delete_selected_nodes(tree, tree->root);
			if (tree == hotlist_tree)
				ro_gui_hotlist_save();
			ro_gui_menu_prepare_action(owner, TREE_CLEAR_SELECTION, true);
			ro_gui_menu_prepare_action(owner, TREE_SELECTION, true);
			return true;
		case TREE_SELECT_ALL:
			ro_gui_tree_stop_edit(tree);
			if (tree->root->child) {
				tree->temp_selection = NULL;
				tree_set_node_selected(tree, tree->root, true);
			}
			ro_gui_menu_prepare_action(owner, TREE_CLEAR_SELECTION, true);
			ro_gui_menu_prepare_action(owner, TREE_SELECTION, true);
			return true;
		case TREE_CLEAR_SELECTION:
			tree->temp_selection = NULL;
			ro_gui_tree_stop_edit(tree);
			tree_set_node_selected(tree, tree->root, false);
			ro_gui_menu_prepare_action(owner, TREE_CLEAR_SELECTION, true);
			ro_gui_menu_prepare_action(owner, TREE_SELECTION, true);
			return true;

		/* toolbar actions */
		case TOOLBAR_BUTTONS:
			assert(t);
			t->display_buttons = !t->display_buttons;
			ro_gui_menu_refresh_toolbar(t);
			return true;
		case TOOLBAR_ADDRESS_BAR:
			assert(t);
			t->display_url = !t->display_url;
			ro_gui_menu_refresh_toolbar(t);
			if (t->display_url)
				ro_gui_set_caret_first(t->toolbar_handle);
			return true;
		case TOOLBAR_THROBBER:
			assert(t);
			t->display_throbber = !t->display_throbber;
			ro_gui_menu_refresh_toolbar(t);
			return true;
		case TOOLBAR_EDIT:
			assert(t);
			ro_gui_theme_toggle_edit(t);
			return true;

		/* misc actions */
		case APPLICATION_QUIT:
			if (ro_gui_prequit()) {
				LOG(("QUIT in response to user request"));
				netsurf_quit = true;
			}
			return true;
		case CHOICES_SHOW:
			ro_gui_configure_show();
			return true;

		/* unknown action */
		default:
			return false;
	}
	return false;
}


/**
 * Prepares an action for use.
 *
 * \param owner	   the window to prepare the action for
 * \param action   the action to prepare
 * \param windows  whether to update sub-windows
 */
void ro_gui_menu_prepare_action(wimp_w owner, menu_action action,
		bool windows)
{
	struct menu_definition_entry *entry;
	struct gui_window *g;
	struct browser_window *bw;
	hlcache_handle *h;
	struct toolbar *t;
	struct tree *tree;
	struct node *node;
	bool result = false;
	int checksum = 0;
	os_error *error;
	char *parent;
	url_func_result res;
	bool compare;

	ro_gui_menu_get_window_details(owner, &g, &bw, &h, &t, &tree);
	if (current_menu_open)
		checksum = ro_gui_menu_get_checksum();
	if (!h) {
		current_menu_object = NULL;
		current_menu_url = NULL;
	}

	switch (action) {

		/* help actions */
		case HELP_LAUNCH_INTERACTIVE:
			result = ro_gui_interactive_help_available()
					&& option_interactive_help;
			ro_gui_menu_set_entry_ticked(current_menu,
					action, result);
			ro_gui_save_options();
			break;

		/* history actions */
		case HISTORY_SHOW_LOCAL:
			ro_gui_menu_set_entry_shaded(current_menu, action,
				(!bw || (!bw->history) ||
				!(h || history_back_available(bw->history) ||
				history_forward_available(bw->history))));
			break;
		case HISTORY_SHOW_GLOBAL:
			ro_gui_menu_set_entry_shaded(current_menu, action,
				!global_history_tree);
			break;

		/* hotlist actions */
		case HOTLIST_ADD_URL:
			ro_gui_menu_set_entry_shaded(current_menu, action,
				(!h || !hotlist_tree));
			break;
		case HOTLIST_SHOW:
			ro_gui_menu_set_entry_shaded(current_menu, action,
				!hotlist_tree);
			if ((t) && (!t->editor) &&
					(t->type == THEME_BROWSER_TOOLBAR))
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_BOOKMARK,
						!hotlist_tree);
			break;

		/* cookies actions */
		case COOKIES_SHOW:
			ro_gui_menu_set_entry_shaded(current_menu, action,
				!cookies_tree);
			break;
		case COOKIES_DELETE:
			ro_gui_menu_set_entry_shaded(current_menu, action,
				!(cookies_tree && cookies_tree->root->child));
			break;

		/* page actions */
		case BROWSER_PAGE:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h ||
					(content_get_type(h) != CONTENT_HTML &&
					content_get_type(h) != CONTENT_TEXTPLAIN));
			break;
		case BROWSER_PAGE_INFO:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			if ((windows) && (h))
				ro_gui_menu_prepare_pageinfo(g);
			break;
		case BROWSER_PRINT:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			if ((t) && (t->type == THEME_BROWSER_TOOLBAR))
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_PRINT, !h);
			if ((windows) && (h))
				ro_gui_print_prepare(g);
			if ((t) && (!t->editor) &&
					(t->type == THEME_BROWSER_TOOLBAR))
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_PRINT, !h);
			break;
		case BROWSER_NEW_WINDOW:
		case BROWSER_VIEW_SOURCE:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			break;

		/* object actions */
		case BROWSER_OBJECT:
			ro_gui_menu_set_entry_shaded(current_menu, action,
					!current_menu_object && !current_menu_url);
			break;

		case BROWSER_OBJECT_LINK:
			ro_gui_menu_set_entry_shaded(current_menu, action,
					!current_menu_url);
			break;

		case BROWSER_OBJECT_INFO:
			if (windows && current_menu_object)
				ro_gui_menu_prepare_objectinfo(current_menu_object,
						current_menu_url);
			/* Fall through */
		case BROWSER_OBJECT_RELOAD:
		case BROWSER_OBJECT_OBJECT:
			ro_gui_menu_set_entry_shaded(current_menu, action,
					!current_menu_object);
			break;

		case BROWSER_OBJECT_PRINT:
			/* not yet implemented */
			ro_gui_menu_set_entry_shaded(current_menu, action, true);
			break;

		/* save actions (browser, hotlist, history) */
		case BROWSER_OBJECT_SAVE:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !current_menu_object);
			if (windows && current_menu_object)
				ro_gui_save_prepare(GUI_SAVE_OBJECT_ORIG, current_menu_object,
						NULL, NULL, NULL);
			break;
		case BROWSER_OBJECT_EXPORT:
		case BROWSER_OBJECT_EXPORT_SPRITE:
		case BROWSER_OBJECT_EXPORT_DRAW: {
			bool exp_sprite = false;
			bool exp_draw = false;

			if (current_menu_object)
				h = current_menu_object;

			if (h) {
				switch (content_get_type(h)) {
/* \todo - this classification should prob be done in content_() */
					/* bitmap types (Sprite export possible) */
#ifdef WITH_JPEG
					case CONTENT_JPEG:
#endif
#ifdef WITH_MNG
					case CONTENT_JNG:
					case CONTENT_MNG:
#endif
#ifdef WITH_GIF
					case CONTENT_GIF:
#endif
#ifdef WITH_BMP
					case CONTENT_BMP:
					case CONTENT_ICO:
#endif
#if defined(WITH_MNG) || defined(WITH_PNG)
					case CONTENT_PNG:
#endif
#ifdef WITH_SPRITE
					case CONTENT_SPRITE:
#endif
						exp_sprite = true;
						break;

					/* vector types (Draw export possible) */
#if defined(WITH_NS_SVG) || defined(WITH_RSVG)
					case CONTENT_SVG:
#endif
#ifdef WITH_DRAW
					case CONTENT_DRAW:
#endif
						exp_draw = true;
						break;

					default: break;
				}
			}

			switch (action) {
			case BROWSER_OBJECT_EXPORT_SPRITE:
				if (!exp_sprite)
					h = NULL;
				break;
			case BROWSER_OBJECT_EXPORT_DRAW:
				if (!exp_draw)
					h = NULL;
				break;
			default:
				if (!exp_sprite && !exp_draw)
					h = NULL;
				break;
			}

			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			if ((h) && (windows))
				ro_gui_save_prepare(GUI_SAVE_OBJECT_NATIVE, h, NULL, NULL, NULL);
		}
		break;
		case BROWSER_LINK_SAVE_URI:
		case BROWSER_LINK_SAVE_URL:
		case BROWSER_LINK_SAVE_TEXT:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !current_menu_url);
			if (windows && current_menu_url) {
				gui_save_type save_type;
				switch (action) {
					case BROWSER_LINK_SAVE_URI:
						save_type = GUI_SAVE_LINK_URI;
						break;
					case BROWSER_LINK_SAVE_URL:
						save_type = GUI_SAVE_LINK_URL;
						break;
					default:
						save_type = GUI_SAVE_LINK_TEXT;
						break;
				}
				ro_gui_save_prepare(save_type, NULL, NULL,
						current_menu_url, NULL);
			}
			break;

		case BROWSER_SELECTION:
			/* make menu available if there's anything that /could/
			 * be selected */
			ro_gui_menu_set_entry_shaded(current_menu, action,
				!h || (content_get_type(h) != CONTENT_HTML &&
				content_get_type(h) != CONTENT_TEXTPLAIN));
			break;
		case BROWSER_SELECTION_SAVE:
			if (h && (!bw->sel || !selection_defined(bw->sel)))
				h = NULL;
			ro_gui_menu_set_entry_shaded(current_menu, action, !h);
			if ((h) && (windows))
				ro_gui_save_prepare(GUI_SAVE_TEXT_SELECTION, NULL, bw->sel, NULL, NULL);
			break;
		case BROWSER_SELECTION_COPY:
			ro_gui_menu_set_entry_shaded(current_menu, action,
				!(h && bw->sel && selection_defined(bw->sel)));
			break;
		case BROWSER_SELECTION_CUT:
			ro_gui_menu_set_entry_shaded(current_menu, action,
				!(h && bw->sel && selection_defined(bw->sel)
					&& !selection_read_only(bw->sel)));
			break;
		case BROWSER_SELECTION_PASTE:
			ro_gui_menu_set_entry_shaded(current_menu, action,
					!(h && bw->paste_callback));
			break;
		case BROWSER_SAVE:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			if ((h) && (windows))
				ro_gui_save_prepare(GUI_SAVE_SOURCE, h, NULL, NULL, NULL);
			if ((t) && (!t->editor) &&
					(t->type == THEME_BROWSER_TOOLBAR))
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_SAVE, !h);
			break;
		case BROWSER_SAVE_COMPLETE:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			if ((h) && (windows))
				ro_gui_save_prepare(GUI_SAVE_COMPLETE, h, NULL, NULL, NULL);
			break;
		case BROWSER_EXPORT_DRAW:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			if ((h) && (windows))
				ro_gui_save_prepare(GUI_SAVE_DRAW, h, NULL, NULL, NULL);
			break;
		case BROWSER_EXPORT_PDF:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			if ((h) && (windows))
				ro_gui_save_prepare(GUI_SAVE_PDF, h, NULL, NULL, NULL);
			break;
		case BROWSER_EXPORT_TEXT:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			if ((h) && (windows))
				ro_gui_save_prepare(GUI_SAVE_TEXT, h, NULL, NULL, NULL);
			break;
		case BROWSER_OBJECT_SAVE_URL_URI:
			if (h && content_get_type(h) == CONTENT_HTML)
				h = current_menu_object;
			/* Fall through */
		case BROWSER_SAVE_URL_URI:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			if ((h) && (windows))
				ro_gui_save_prepare(GUI_SAVE_LINK_URI, NULL,
						NULL,
						content_get_url(h),
						content_get_title(h));
			break;
		case BROWSER_OBJECT_SAVE_URL_URL:
			if (h && content_get_type(h) == CONTENT_HTML)
				h = current_menu_object;
			/* Fall through */
		case BROWSER_SAVE_URL_URL:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			if ((h) && (windows))
				ro_gui_save_prepare(GUI_SAVE_LINK_URL, NULL,
						NULL,
						content_get_url(h),
						content_get_title(h));
			break;
		case BROWSER_OBJECT_SAVE_URL_TEXT:
			h = current_menu_object;
			/* Fall through */
		case BROWSER_SAVE_URL_TEXT:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			if ((h) && (windows))
				ro_gui_save_prepare(GUI_SAVE_LINK_TEXT, NULL,
						NULL,
						content_get_url(h),
						content_get_title(h));
			break;
		case HOTLIST_EXPORT:
			if ((tree) && (windows))
				ro_gui_save_prepare(GUI_SAVE_HOTLIST_EXPORT_HTML,
						NULL, NULL, NULL, NULL);
			break;
		case HISTORY_EXPORT:
			if ((tree) && (windows))
				ro_gui_save_prepare(GUI_SAVE_HISTORY_EXPORT_HTML,
						NULL, NULL, NULL, NULL);
			break;

		/* navigation actions */
		case BROWSER_NAVIGATE_BACK:
			result = browser_window_back_available(bw);
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !result);
			if ((t) && (!t->editor) &&
					(t->type == THEME_BROWSER_TOOLBAR))
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_BACK, !result);
			break;
		case BROWSER_NAVIGATE_FORWARD:
			result = browser_window_forward_available(bw);
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !result);
			if ((t) && (!t->editor) &&
					(t->type == THEME_BROWSER_TOOLBAR))
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_FORWARD, !result);
			break;
		case BROWSER_NAVIGATE_UP:
			result = (bw && h);
			if (result) {
				res = url_parent(content_get_url(h), &parent);
				if (res == URL_FUNC_OK) {
				  	res = url_compare(content_get_url(h),
				  			parent, false,
				  			&compare);
					if (res == URL_FUNC_OK)
					  	result = !compare;
				  	free(parent);
       	                	} else {
       	                	  	result = false;
       	                	}
			}
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !result);
			if ((t) && (!t->editor) &&
					(t->type == THEME_BROWSER_TOOLBAR))
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_UP, !result);
			break;
		case BROWSER_NAVIGATE_RELOAD:
		case BROWSER_NAVIGATE_RELOAD_ALL:
			result = browser_window_reload_available(bw);
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !result);
			if ((t) && (!t->editor) &&
					(t->type == THEME_BROWSER_TOOLBAR))
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_RELOAD, !result);
			break;
		case BROWSER_NAVIGATE_STOP:
			result = browser_window_stop_available(bw);
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !result);
			if ((t) && (!t->editor) &&
					(t->type == THEME_BROWSER_TOOLBAR))
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_STOP, !result);
			break;
		case BROWSER_NAVIGATE_URL:
			if (windows)
				ro_gui_dialog_prepare_open_url();
			break;

		/* display actions */
		case BROWSER_SCALE_VIEW:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !h);
			if ((h) && (windows))
				ro_gui_dialog_prepare_zoom(g);
			if ((t) && (!t->editor) &&
					(t->type == THEME_BROWSER_TOOLBAR))
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_SCALE, !h);
			break;
		case BROWSER_FIND_TEXT:
			result = !h || (content_get_type(h) != CONTENT_HTML &&
					content_get_type(h) != CONTENT_TEXTPLAIN);
			ro_gui_menu_set_entry_shaded(current_menu,
					action, result);
			if ((!result) && (windows)) {
				ro_gui_search_prepare(g->bw);
			}
			if ((t) && (!t->editor) &&
					(t->type == THEME_BROWSER_TOOLBAR))
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_SEARCH, result);
			break;
		case BROWSER_IMAGES_FOREGROUND:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, true);
			ro_gui_menu_set_entry_ticked(current_menu,
					action, true);
			break;
		case BROWSER_IMAGES_BACKGROUND:
			if (g)
				ro_gui_menu_set_entry_ticked(current_menu,
					action, g->option.background_images);
			break;
		case BROWSER_BUFFER_ANIMS:
			if (g) {
				ro_gui_menu_set_entry_shaded(current_menu,
					action, g->option.buffer_everything);
				ro_gui_menu_set_entry_ticked(current_menu,
					action,
					g->option.buffer_animations ||
					g->option.buffer_everything);
			}
			break;
		case BROWSER_BUFFER_ALL:
			if (g)
				ro_gui_menu_set_entry_ticked(current_menu,
					action, g->option.buffer_everything);
			break;
		case BROWSER_WINDOW_STAGGER:
			ro_gui_menu_set_entry_shaded(current_menu, action,
					option_window_screen_width == 0);
			ro_gui_menu_set_entry_ticked(current_menu, action,
					((option_window_screen_width == 0) ||
					option_window_stagger));
			break;
		case BROWSER_WINDOW_COPY:
			ro_gui_menu_set_entry_ticked(current_menu, action,
					option_window_size_clone);
			break;
		case BROWSER_WINDOW_RESET:
			ro_gui_menu_set_entry_shaded(current_menu, action,
					option_window_screen_width == 0);
			break;

		/* tree actions */
		case TREE_NEW_FOLDER:
			ro_gui_hotlist_prepare_folder_dialog(NULL);
			break;
		case TREE_NEW_LINK:
			ro_gui_hotlist_prepare_entry_dialog(NULL);
			break;
		case TREE_EXPAND_ALL:
		case TREE_EXPAND_FOLDERS:
		case TREE_EXPAND_LINKS:
		case TREE_COLLAPSE_ALL:
		case TREE_COLLAPSE_FOLDERS:
		case TREE_COLLAPSE_LINKS:
			if ((tree) && (tree->root)) {
				ro_gui_menu_set_entry_shaded(current_menu,
						action, !tree->root->child);

				if ((t) && (!t->editor) && (t->type != 
						THEME_BROWSER_TOOLBAR)) {
					ro_gui_set_icon_shaded_state(
							t->toolbar_handle,
							ICON_TOOLBAR_EXPAND, 
							!tree->root->child);
					ro_gui_set_icon_shaded_state(
							t->toolbar_handle,
							ICON_TOOLBAR_OPEN, 
							!tree->root->child);
				}
			}
			break;
		case TREE_SELECTION:
			if ((!tree) || (!tree->root))
				break;
			if (tree->root->child)
				result = tree_has_selection(tree->root->child);
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !result);
			if ((t) && (!t->editor) &&
					(t->type != THEME_BROWSER_TOOLBAR)) {
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_DELETE, !result);
				ro_gui_set_icon_shaded_state(
						t->toolbar_handle,
						ICON_TOOLBAR_LAUNCH, !result);
			}
			break;
		case TREE_SELECTION_EDIT:
			node = tree_get_selected_node(tree->root);
			entry = ro_gui_menu_find_entry(current_menu, action);
			if ((!node) || (!entry))
				break;
			if (node->folder) {
				entry->menu_entry->sub_menu =
						(wimp_menu *)dialog_folder;
				if (windows)
					ro_gui_hotlist_prepare_folder_dialog(node);
			} else {
				entry->menu_entry->sub_menu =
						(wimp_menu *)dialog_entry;
				if (windows)
					ro_gui_hotlist_prepare_entry_dialog(node);
			}
			break;
		case TREE_SELECTION_LAUNCH:
		case TREE_SELECTION_DELETE:
		case TREE_CLEAR_SELECTION:
			if ((!tree) || (!tree->root))
				break;
			if (tree->root->child)
				result = tree_has_selection(tree->root->child);
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !result);
			break;
		case TREE_SELECT_ALL:
			ro_gui_menu_set_entry_shaded(current_menu, action,
					!tree->root->child);
			break;

		/* toolbar actions */
		case TOOLBAR_BUTTONS:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, (!t || (t->editor)));
			ro_gui_menu_set_entry_ticked(current_menu,
					action, (t &&
					((t->display_buttons) || (t->editor))));
			break;
		case TOOLBAR_ADDRESS_BAR:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !t);
			ro_gui_menu_set_entry_ticked(current_menu, action,
					(t && t->display_url));
			break;
		case TOOLBAR_THROBBER:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !t);
			ro_gui_menu_set_entry_ticked(current_menu, action,
					(t && t->display_throbber));
			break;
		case TOOLBAR_EDIT:
			ro_gui_menu_set_entry_shaded(current_menu,
					action, !t);
			ro_gui_menu_set_entry_ticked(current_menu, action,
					(t && t->editor));
			break;

		/* unknown action */
		default:
			return;
	}

	/* update open menus */
	if ((current_menu_open) &&
			(checksum != ro_gui_menu_get_checksum())) {
		error = xwimp_create_menu(current_menu, 0, 0);
		if (error) {
			LOG(("xwimp_create_menu: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("MenuError", error->errmess);
		}
	}
}


/**
 * Gets various details relating to a window
 *
 * \param w  the window to complete information for
 */
void ro_gui_menu_get_window_details(wimp_w w, struct gui_window **g,
		struct browser_window **bw, hlcache_handle **h,
		struct toolbar **toolbar, struct tree **tree)
{
	*g = ro_gui_window_lookup(w);
	if (*g) {
		*bw = (*g)->bw;
		*toolbar = (*g)->toolbar;
		if (*bw)
			*h = (*bw)->current_content;
		*tree = NULL;
	} else {
		*bw = NULL;
		*h = NULL;
		if ((hotlist_tree) && (w == (wimp_w)hotlist_tree->handle))
			*tree = hotlist_tree;
		else if ((global_history_tree) &&
				(w == (wimp_w)global_history_tree->handle))
			*tree = global_history_tree;
		else if ((cookies_tree) && (w == (wimp_w)cookies_tree->handle))
			*tree = cookies_tree;
		else
			*tree = NULL;
		if (*tree)
			*toolbar = (*tree)->toolbar;
		else
			*toolbar = NULL;
	}
}


/**
 * Calculates a simple checksum for the current menu state
 */
int ro_gui_menu_get_checksum(void)
{
	wimp_selection menu_tree;
	int i = 0, j, checksum = 0;
	os_error *error;
	wimp_menu *menu;

	if (!current_menu_open)
		return 0;

	error = xwimp_get_menu_state((wimp_menu_state_flags)0,
			&menu_tree, 0, 0);
	if (error) {
		LOG(("xwimp_get_menu_state: 0x%x: %s",
				error->errnum, error->errmess));
		warn_user("MenuError", error->errmess);
		return 0;
	}

	menu = current_menu;
	do {
		j = 0;
		do {
			if (menu->entries[j].icon_flags & wimp_ICON_SHADED)
				checksum ^= (1 << (i + j * 2));
			if (menu->entries[j].menu_flags & wimp_MENU_TICKED)
				checksum ^= (2 << (i + j * 2));
		} while (!(menu->entries[j++].menu_flags & wimp_MENU_LAST));

		j = menu_tree.items[i++];
		if (j != -1) {
			menu = menu->entries[j].sub_menu;
			if ((!menu) || (menu == wimp_NO_SUB_MENU) || (!IS_MENU(menu)))
				break;
		}
	} while (j != -1);

	return checksum;
}

/**
 * Translate a menu's textual content into the system local encoding
 *
 * \param menu  The menu to translate
 * \return false if out of memory, true otherwise
 */
bool ro_gui_menu_translate(struct menu_definition *menu)
{
	os_error *error;
	int alphabet;
	struct menu_definition_entry *entry;
	char *translated;
	utf8_convert_ret err;

	/* read current alphabet */
	error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &alphabet);
	if (error) {
		LOG(("failed reading alphabet: 0x%x: %s",
				error->errnum, error->errmess));
		/* assume Latin1 */
		alphabet = territory_ALPHABET_LATIN1;
	}

	if (menu->current_encoding == alphabet)
		/* menu text is already in the correct encoding */
		return true;

	/* translate root menu title text */
	free(menu->menu->title_data.indirected_text.text);
	err = utf8_to_local_encoding(messages_get(menu->title_key),
			0, &translated);
	if (err != UTF8_CONVERT_OK) {
		assert(err != UTF8_CONVERT_BADENC);
		LOG(("utf8_to_enc failed"));
		return false;
	}

	/* and fill in WIMP menu field */
	menu->menu->title_data.indirected_text.text = translated;

	/* now the menu entries */
	for (entry = menu->entries; entry; entry = entry->next) {
		wimp_menu *submenu = entry->menu_entry->sub_menu;

		/* tranlate menu entry text */
		free(entry->menu_entry->data.indirected_text.text);
		err = utf8_to_local_encoding(messages_get(entry->entry_key),
				0, &translated);
		if (err != UTF8_CONVERT_OK) {
			assert(err != UTF8_CONVERT_BADENC);
			LOG(("utf8_to_enc failed"));
			return false;
		}

		/* fill in WIMP menu fields */
		entry->menu_entry->data.indirected_text.text = translated;
		entry->menu_entry->data.indirected_text.validation =
				(char *) -1;
		entry->menu_entry->data.indirected_text.size =
				strlen(translated);

		/* child menu title - this is the same as the text of
		 * the parent menu entry, so just copy the pointer */
		if (submenu != wimp_NO_SUB_MENU && IS_MENU(submenu)) {
			submenu->title_data.indirected_text.text =
					translated;
		}
	}

	/* finally, set the current encoding of the menu */
	menu->current_encoding = alphabet;

	return true;
}
