/*
 * Copyright 2004, 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
 * Window themes and toolbars (implementation).
 */

#include <alloca.h>
#include <assert.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "oslib/dragasprite.h"
#include "oslib/os.h"
#include "oslib/osgbpb.h"
#include "oslib/osfile.h"
#include "oslib/osfind.h"
#include "oslib/osspriteop.h"
#include "oslib/wimpspriteop.h"
#include "oslib/squash.h"
#include "oslib/wimp.h"
#include "oslib/wimpextend.h"
#include "oslib/wimpspriteop.h"
#include "content/content.h"
#include "desktop/gui.h"
#include "riscos/dialog.h"
#include "riscos/gui.h"
#include "riscos/menus.h"
#include "riscos/options.h"
#include "riscos/theme.h"
#include "riscos/treeview.h"
#include "riscos/wimp.h"
#include "riscos/wimp_event.h"
#include "riscos/wimputils.h"
#include "utils/log.h"
#include "utils/utils.h"

#define THEME_URL_MEMORY 256
#define THEME_THROBBER_MEMORY 12

static struct theme_descriptor *theme_current = NULL;
static struct theme_descriptor *theme_descriptors = NULL;
static struct toolbar *theme_toolbar_drag = NULL;
static struct toolbar_icon *theme_toolbar_icon_drag = NULL;
static bool theme_toolbar_editor_drag = false;

/* these order of the icons must match the numbers defined in riscos/gui.h */
static const char * theme_browser_icons[] = {"back", "forward", "stop",
		"reload", "home", "history", "save", "print", "hotlist",
		"scale", "search", "up", NULL};
static const char * theme_hotlist_icons[] = {"delete", "expand", "open",
		"launch", "create", NULL};
static const char * theme_history_icons[] = {"delete", "expand", "open",
		"launch", NULL};
static const char * theme_cookies_icons[] = {"delete", "expand", "open",
		NULL};

static bool ro_gui_theme_add_descriptor(const char *folder, const char *leafname);
static void ro_gui_theme_redraw(wimp_draw *redraw);
static void ro_gui_theme_get_available_in_dir(const char *directory);
static void ro_gui_theme_free(struct theme_descriptor *descriptor);
static struct toolbar_icon *ro_gui_theme_add_toolbar_icon(
		struct toolbar *toolbar, const char *name, int icon_number);
static void ro_gui_theme_update_toolbar_icon(struct toolbar *toolbar,
		struct toolbar_icon *icon);
static void ro_gui_theme_destroy_toolbar_icon(struct toolbar_icon *icon);
static void ro_gui_theme_link_toolbar_icon(struct toolbar *toolbar,
		struct toolbar_icon *icon, struct toolbar_icon *link,
		bool before);
static void ro_gui_theme_delink_toolbar_icon(struct toolbar *toolbar,
		struct toolbar_icon *icon);
static struct toolbar_icon *ro_gui_theme_toolbar_get_insert_icon(
		struct toolbar *toolbar, int x, int y, bool *before);
static void ro_gui_theme_add_toolbar_icons(struct toolbar *toolbar,
		const char* icons[], const char* ident);
static void ro_gui_theme_set_help_prefix(struct toolbar *toolbar);

/*	A basic window for the toolbar and status
*/
static wimp_window theme_toolbar_window = {
	{0, 0, 1, 1},
	0,
	0,
	wimp_TOP,
	wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS |
			wimp_WINDOW_FURNITURE_WINDOW |
			wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT,
	wimp_COLOUR_BLACK,
	wimp_COLOUR_LIGHT_GREY,
	wimp_COLOUR_LIGHT_GREY,
	wimp_COLOUR_VERY_LIGHT_GREY,
	wimp_COLOUR_DARK_GREY,
	wimp_COLOUR_MID_LIGHT_GREY,
	wimp_COLOUR_CREAM,
	wimp_WINDOW_NEVER3D | 0x16u /* RISC OS 5.03+ */,
	{0, 0, 16384, 16384},
	0,
	0,
	wimpspriteop_AREA,
	1,
	1,
	{""},
	0,
	{ }
};


/*	Shared icon validation
*/
static char theme_url_validation[] = "Pptr_write;KN";
static char theme_null_text_string[] = "";
static char theme_separator_name[] = "separator";
static char theme_favicon_sprite[12];


/**
 * Initialise the theme handler
 */
void ro_gui_theme_initialise(void)
{
	struct theme_descriptor *descriptor;

	theme_descriptors = ro_gui_theme_get_available();
	descriptor = ro_gui_theme_find(option_theme);
	if (!descriptor)
		descriptor = ro_gui_theme_find("Aletheia");
	ro_gui_theme_apply(descriptor);
}


/**
 * Finalise the theme handler
 */
void ro_gui_theme_finalise(void)
{
	ro_gui_theme_close(theme_current, false);
	ro_gui_theme_free(theme_descriptors);
}


/**
 * Finds a theme from the cached values.
 *
 * The returned theme is only guaranteed to be valid until the next call
 * to ro_gui_theme_get_available() unless it has been opened using
 * ro_gui_theme_open().
 *
 * \param leafname  the filename of the theme_descriptor to return
 * \return the requested theme_descriptor, or NULL if not found
 */
struct theme_descriptor *ro_gui_theme_find(const char *leafname)
{
	struct theme_descriptor *descriptor;

	if (!leafname)
		return NULL;

	for (descriptor = theme_descriptors; descriptor;
			descriptor = descriptor->next)
		if (!strcmp(leafname, descriptor->leafname))
			return descriptor;
	/* fallback for 10 chars on old filesystems */
	for (descriptor = theme_descriptors; descriptor;
			descriptor = descriptor->next)
		if (!strncmp(leafname, descriptor->leafname, 10))
			return descriptor;
	return NULL;
}


/**
 * Reads and caches the currently available themes.
 *
 * \return the requested theme_descriptor, or NULL if not found
 */
struct theme_descriptor *ro_gui_theme_get_available(void)
{
	struct theme_descriptor *current;
	struct theme_descriptor *test;

	/* close any unused descriptors */
	ro_gui_theme_free(theme_descriptors);

	/* add our default 'Aletheia' theme */
	ro_gui_theme_add_descriptor("NetSurf:Resources", "Aletheia");

	/* scan our choices directory */
	ro_gui_theme_get_available_in_dir(option_theme_path);

	/* sort alphabetically in a very rubbish way */
	if ((theme_descriptors) && (theme_descriptors->next)) {
		current = theme_descriptors;
		while ((test = current->next)) {
			if (strcmp(current->name, test->name) > 0) {
				current->next->previous = current->previous;
				if (current->previous)
					current->previous->next = current->next;
				current->next = test->next;
				test->next = current;
				current->previous = test;
				if (current->next)
					current->next->previous = current;

				current = test->previous;
				if (!current) current = test;
			} else {
				current = current->next;
			}
		}
		while (theme_descriptors->previous)
			theme_descriptors = theme_descriptors->previous;
	}

	return theme_descriptors;
}


/**
 * Adds the themes in a directory to the global cache.
 *
 * \param directory  the directory to scan
 */
static void ro_gui_theme_get_available_in_dir(const char *directory)
{
	int context = 0;
	int read_count;
	osgbpb_INFO(100) info;
	os_error *error;

	while (context != -1) {
	  	/* read some directory info */
		error = xosgbpb_dir_entries_info(directory,
				(osgbpb_info_list *) &info, 1, context,
				sizeof(info), 0, &read_count, &context);
		if (error) {
			LOG(("xosgbpb_dir_entries_info: 0x%x: %s",
				error->errnum, error->errmess));
			if (error->errnum == 0xd6)	/* no such dir */
				return;
			warn_user("MiscError", error->errmess);
			break;
		}

		/* only process files */
		if ((read_count != 0) && (info.obj_type == fileswitch_IS_FILE))
			ro_gui_theme_add_descriptor(directory, info.name);
	}
}


/**
 * Checks a theme is valid and adds it to the current list
 *
 * \param folder	the theme folder
 * \param leafname	the theme leafname
 * \return whether the theme was added
 */
bool ro_gui_theme_add_descriptor(const char *folder, const char *leafname)
{
	struct theme_file_header file_header;
	struct theme_descriptor *current;
	struct theme_descriptor *test;
	int output_left;
	os_fw file_handle;
	os_error *error;
	char *filename;

	/* create a full filename */
	filename = malloc(strlen(folder) + strlen(leafname) + 2);
	if (!filename) {
	  	LOG(("No memory for malloc"));
	  	warn_user("NoMemory", 0);
	  	return false;
	}
	sprintf(filename, "%s.%s", folder, leafname);

	/* get the header */
	error = xosfind_openinw(osfind_NO_PATH, filename, 0,
			&file_handle);
	if (error) {
		LOG(("xosfind_openinw: 0x%x: %s",
			error->errnum, error->errmess));
		warn_user("FileError", error->errmess);
		free(filename);
		return false;
	}
	if (file_handle == 0) {
		free(filename);
		return false;
	}
	error = xosgbpb_read_atw(file_handle,
			(byte *) &file_header,
			sizeof (struct theme_file_header),
			0, &output_left);
	xosfind_closew(file_handle);
	if (error) {
		LOG(("xosbgpb_read_atw: 0x%x: %s",
			error->errnum, error->errmess));
		warn_user("FileError", error->errmess);
		free(filename);
		return false;
	}
	if (output_left > 0) {	/* should try to read more? */
	  	free(filename);
	  	return false;
	}

	/* create a new theme descriptor */
	current = (struct theme_descriptor *)calloc(1,
			sizeof(struct theme_descriptor));
	if (!current) {
		LOG(("calloc failed"));
		warn_user("NoMemory", 0);
		free(filename);
		return false;
	}
	if (!ro_gui_theme_read_file_header(current, &file_header)) {
		free(filename);
		free(current);
		return false;
	}
	current->filename = filename;
	current->leafname = current->filename + strlen(folder) + 1;

	/* don't add duplicates */
	for (test = theme_descriptors; test; test = test->next) {
		if (!strcmp(current->name, test->name)) {
			free(current->filename);
			free(current);
			return false;
		}
	}

	/* link in our new descriptor at the head*/
	if (theme_descriptors) {
		current->next = theme_descriptors;
		theme_descriptors->previous = current;
	}
	theme_descriptors = current;
	return true;

}


/**
 * Fills in the basic details for a descriptor from a file header.
 * The filename string is not set.
 *
 * \param descriptor   the descriptor to set up
 * \param file_header  the header to read from
 * \return false for a badly formed theme, true otherwise
 */
bool ro_gui_theme_read_file_header(struct theme_descriptor *descriptor,
		struct theme_file_header *file_header)
{
	if ((file_header->magic_value != 0x4d54534e) ||
			(file_header->parser_version > 2))
		return false;

	strcpy(descriptor->name, file_header->name);
	strcpy(descriptor->author, file_header->author);
	descriptor->browser_background = file_header->browser_bg;
	descriptor->hotlist_background = file_header->hotlist_bg;
	descriptor->status_background = file_header->status_bg;
	descriptor->status_foreground = file_header->status_fg;
	descriptor->decompressed_size = file_header->decompressed_sprite_size;
	descriptor->compressed_size = file_header->compressed_sprite_size;
	if (file_header->parser_version >= 2) {
		descriptor->throbber_right =
				!(file_header->theme_flags & (1 << 0));
		descriptor->throbber_redraw =
				file_header->theme_flags & (1 << 1);
	} else {
		descriptor->throbber_right =
				(file_header->theme_flags == 0x00);
		descriptor->throbber_redraw = true;
	}
	return true;
}


/**
 * Opens a theme ready for use.
 *
 * \param descriptor  the theme_descriptor to open
 * \param list	      whether to open all themes in the list
 * \return whether the operation was successful
 */
bool ro_gui_theme_open(struct theme_descriptor *descriptor, bool list)
{
	fileswitch_object_type obj_type;
	squash_output_status status;
	os_coord dimensions;
	os_mode mode;
	os_error *error;
	struct theme_descriptor *next_descriptor;
	char sprite_name[16];
	const char *name = sprite_name;
	bool result = true;
	int i, n;
	int workspace_size, file_size;
	char *raw_data, *workspace;
	osspriteop_area *decompressed;

	/*	If we are freeing the whole of the list then we need to
		start at the first descriptor.
	*/
	if (list && descriptor)
		while (descriptor->previous) descriptor = descriptor->previous;

	/*	Open the themes
	*/
	for (; descriptor; descriptor = next_descriptor) {
		/* see if we should iterate through the entire list */
		if (list)
			next_descriptor = descriptor->next;
		else
			next_descriptor = NULL;

		/* if we are already loaded, increase the usage count */
		if (descriptor->theme) {
			descriptor->theme->users = descriptor->theme->users + 1;
			continue;
		}

		/* create a new theme */
		descriptor->theme = (struct theme *)calloc(1,
				sizeof(struct theme));
		if (!descriptor->theme) {
			LOG(("calloc() failed"));
			warn_user("NoMemory", 0);
			continue;
		}
		descriptor->theme->users = 1;

		/* try to load the associated file */
		error = xosfile_read_stamped_no_path(descriptor->filename,
				&obj_type, 0, 0, &file_size, 0, 0);
		if (error) {
			LOG(("xosfile_read_stamped_no_path: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("FileError", error->errmess);
			continue;
		}
		if (obj_type != fileswitch_IS_FILE)
			continue;
		raw_data = malloc(file_size);
		if (!raw_data) {
			LOG(("malloc() failed"));
			warn_user("NoMemory", 0);
			continue;
		}
		error = xosfile_load_stamped_no_path(descriptor->filename,
				(byte *)raw_data, 0, 0, 0, 0, 0);
		if (error) {
			free(raw_data);
			LOG(("xosfile_load_stamped_no_path: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("FileError", error->errmess);
			continue;
		}

		/* decompress the new data */
		error = xsquash_decompress_return_sizes(-1, &workspace_size, 0);
		if (error) {
			free(raw_data);
			LOG(("xsquash_decompress_return_sizes: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("MiscError", error->errmess);
			continue;
		}
		decompressed = (osspriteop_area *)malloc(
				descriptor->decompressed_size);
		workspace = malloc(workspace_size);
		if ((!decompressed) || (!workspace)) {
			free(decompressed);
			free(raw_data);
			LOG(("malloc() failed"));
			warn_user("NoMemory", 0);
			continue;
		}
		error = xsquash_decompress(squash_INPUT_ALL_PRESENT, workspace,
				(byte *)(raw_data + sizeof(
						struct theme_file_header)),
				descriptor->compressed_size,
				(byte *)decompressed,
				descriptor->decompressed_size,
				&status, 0, 0, 0, 0);
		free(workspace);
		free(raw_data);
		if (error) {
			free(decompressed);
			LOG(("xsquash_decompress: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("MiscError", error->errmess);
			continue;
		}
		if (status != 0) {
			free(decompressed);
			continue;
		}
		descriptor->theme->sprite_area = decompressed;

		/* find the highest sprite called 'throbber%i', and get the
		 * maximum dimensions for all 'thobber%i' icons. */
		for (i = 1; i <= descriptor->theme->sprite_area->sprite_count;
				i++) {
			error = xosspriteop_return_name(osspriteop_USER_AREA,
					descriptor->theme->sprite_area,
					sprite_name, 16, i, 0);
			if (error) {
				LOG(("xosspriteop_return_name: 0x%x: %s",
						error->errnum, error->errmess));
				warn_user("MiscError", error->errmess);
				continue;
			}
			if (strncmp(sprite_name, "throbber", 8))
				continue;

			/* get the max sprite width/height */
			error = xosspriteop_read_sprite_info(
					osspriteop_USER_AREA,
					descriptor->theme->sprite_area,
					(osspriteop_id) name,
					&dimensions.x, &dimensions.y,
					(osbool *) 0, &mode);
			if (error) {
				LOG(("xosspriteop_read_sprite_info: 0x%x: %s",
						error->errnum, error->errmess));
				warn_user("MiscError", error->errmess);
				continue;
			}
			ro_convert_pixels_to_os_units(&dimensions, mode);
			if (descriptor->theme->throbber_width <	dimensions.x)
				descriptor->theme->throbber_width =
						dimensions.x;
			if (descriptor->theme->throbber_height < dimensions.y)
				descriptor->theme->throbber_height =
						dimensions.y;

			/* get the throbber number */
			n = atoi(sprite_name + 8);
			if (descriptor->theme->throbber_frames < n)
				descriptor->theme->throbber_frames = n;
		}
	}
	return result;
}


/**
 * Applies the theme to all current windows and subsequent ones.
 *
 * \param descriptor  the theme_descriptor to open
 * \return whether the operation was successful
 */
bool ro_gui_theme_apply(struct theme_descriptor *descriptor)
{
	struct theme_descriptor *theme_previous;

	/* check if the theme is already applied */
	if (descriptor == theme_current)
		return true;

	/* re-open the new-theme and release the current theme */
	if (!ro_gui_theme_open(descriptor, false))
		return false;
	theme_previous = theme_current;
	theme_current = descriptor;

	/* apply the theme to all the current windows */
	ro_gui_window_update_theme();
	ro_gui_tree_update_theme(hotlist_tree);
	ro_gui_tree_update_theme(global_history_tree);
	ro_gui_tree_update_theme(cookies_tree);
	ro_gui_theme_close(theme_previous, false);
	return true;
}


/**
 * Closes a theme after use.
 *
 * \param descriptor  the theme_descriptor to close
 * \param list	      whether to open all themes in the list
 * \return whether the operation was successful
 */
void ro_gui_theme_close(struct theme_descriptor *descriptor, bool list)
{

	if (!descriptor)
		return;

	/* move to the start of the list */
	while (list && descriptor->previous)
		descriptor = descriptor->previous;

	/* close the themes */
	while (descriptor) {
		if (descriptor->theme) {
			descriptor->theme->users = descriptor->theme->users - 1;
			if (descriptor->theme->users <= 0) {
				free(descriptor->theme->sprite_area);
				free(descriptor->theme);
				descriptor->theme = NULL;
			}
		}
		if (!list)
			return;
		descriptor = descriptor->next;
	}
}


/**
 * Performs the redraw for a toolbar
 *
 * \param redraw   the redraw area
 * \param toolbar  the toolbar to redraw
 */
void ro_gui_theme_redraw(wimp_draw *redraw)
{
	struct toolbar *toolbar;
	struct gui_window *g;

	struct toolbar_icon *icon;
	osbool more;
	wimp_icon separator_icon;
	os_error *error;
	bool perform_redraw = false;

	toolbar = (struct toolbar *)ro_gui_wimp_event_get_user_data(redraw->w);

	assert(toolbar);

	/* set the content-type icon */
	g = ro_gui_toolbar_lookup(toolbar->toolbar_handle);

	/* only set type for browser windows */
	sprintf(theme_favicon_sprite, "Ssmall_xxx");
	if (g) {
	  	assert(toolbar->type == THEME_BROWSER_TOOLBAR);
		assert(g->bw);
		if (g->bw->current_content) {
			sprintf(theme_favicon_sprite, "Ssmall_%.3x",
					ro_content_filetype_from_type(
					content_get_type(g->bw->current_content)));
			if (!ro_gui_wimp_sprite_exists(theme_favicon_sprite + 1))
				sprintf(theme_favicon_sprite, "Ssmall_xxx");
		}
	}

	/* set up the icon */
	if ((toolbar->descriptor) && (toolbar->descriptor->theme) &&
			(toolbar->descriptor->theme->sprite_area)) {
		const char *name = theme_separator_name;

		separator_icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED |
				wimp_ICON_HCENTRED | wimp_ICON_VCENTRED;
		separator_icon.data.indirected_sprite.id = (osspriteop_id) name;
		separator_icon.data.indirected_sprite.area =
				toolbar->descriptor->theme->sprite_area;
		separator_icon.data.indirected_sprite.size = 12;
		separator_icon.extent.y0 = 0;
		separator_icon.extent.y1 = toolbar->height;
		perform_redraw = true;
	}
	perform_redraw &= toolbar->display_buttons || toolbar->editor;
	if ((toolbar->editor) && (toolbar->editor->toolbar_handle == redraw->w))
		toolbar = toolbar->editor;

	error = xwimp_redraw_window(redraw, &more);
	if (error) {
		LOG(("xwimp_redraw_window: 0x%x: %s",
				error->errnum, error->errmess));
		warn_user("WimpError", error->errmess);
		return;
	}
	while (more) {
		if (perform_redraw)
			for (icon = toolbar->icon; icon; icon = icon->next)
				if ((icon->icon_number == -1) &&
						(icon->display))  {
					separator_icon.extent.x0 = icon->x;
					separator_icon.extent.x1 = icon->x +
							icon->width;
					xwimp_plot_icon(&separator_icon);
				}
		error = xwimp_get_rectangle(redraw, &more);
		if (error) {
			LOG(("xwimp_get_rectangle: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("WimpError", error->errmess);
			return;
		}
	}
}


/**
 * Frees any unused theme descriptors.
 *
 * \param descriptor  the theme_descriptor to free
 * \param list	      whether to open all themes in the list
 * \return whether the operation was successful
 */
void ro_gui_theme_free(struct theme_descriptor *descriptor)
{
	struct theme_descriptor *next_descriptor;

	if (!descriptor)
		return;

	/* move to the start of the list */
	while (descriptor->previous)
		descriptor = descriptor->previous;

	/* free closed themes */
	for (; descriptor; descriptor = next_descriptor) {
		next_descriptor = descriptor->next;

		/* no theme? no descriptor */
		if (!descriptor->theme) {
			if (descriptor->previous)
				descriptor->previous->next = descriptor->next;
			if (descriptor->next)
				descriptor->next->previous =
						descriptor->previous;

			/* keep the cached list in sync */
			if (theme_descriptors == descriptor)
				theme_descriptors = next_descriptor;

			/* release any memory */
			free(descriptor->filename);
			free(descriptor);
		}
	}
}


/**
 * Creates a toolbar.
 *
 * \param descriptor  the theme to use, or NULL for current
 * \param type	      the toolbar type
 * \return a new toolbar, or NULL for failure
 */
struct toolbar *ro_gui_theme_create_toolbar(struct theme_descriptor *descriptor,
		toolbar_type type)
{
	struct toolbar *toolbar;

	/*	Create a new toolbar
	*/
	toolbar = calloc(sizeof(struct toolbar), 1);
	if (!toolbar) {
		LOG(("No memory for malloc()"));
		warn_user("NoMemory", 0);
		return NULL;
	}
	toolbar->type = type;

	/*	Store the theme
	*/
	if (!descriptor) descriptor = theme_current;
	toolbar->descriptor = descriptor;

	/*	Apply the default settings
	*/
	toolbar->display_buttons = true;
	toolbar->toolbar_current = 16384;
	switch (type) {
		case THEME_BROWSER_TOOLBAR:
			toolbar->display_url = true;
			toolbar->display_throbber = true;
			ro_gui_theme_add_toolbar_icons(toolbar,
					theme_browser_icons,
					option_toolbar_browser);
			toolbar->suggest = ro_gui_theme_add_toolbar_icon(NULL,
			"gright",
					ICON_TOOLBAR_SUGGEST);
			break;
		case THEME_HOTLIST_TOOLBAR:
			ro_gui_theme_add_toolbar_icons(toolbar,
					theme_hotlist_icons,
					option_toolbar_hotlist);
			break;
		case THEME_HISTORY_TOOLBAR:
			ro_gui_theme_add_toolbar_icons(toolbar,
					theme_history_icons,
					option_toolbar_history);
			break;
		case THEME_COOKIES_TOOLBAR:
			ro_gui_theme_add_toolbar_icons(toolbar,
					theme_cookies_icons,
					option_toolbar_cookies);
			break;
		case THEME_BROWSER_EDIT_TOOLBAR:
			ro_gui_theme_add_toolbar_icons(toolbar,
					theme_browser_icons,
					"0123456789ab|");
			break;
		case THEME_HOTLIST_EDIT_TOOLBAR:
			ro_gui_theme_add_toolbar_icons(toolbar,
					theme_hotlist_icons,
					"40123|");
			break;
		case THEME_HISTORY_EDIT_TOOLBAR:
			ro_gui_theme_add_toolbar_icons(toolbar,
					theme_history_icons,
					"0123|");
			break;
		case THEME_COOKIES_EDIT_TOOLBAR:
			ro_gui_theme_add_toolbar_icons(toolbar,
					theme_cookies_icons,
					"012|");
			break;
	}

	/*	Claim the memory for our Wimp indirection
	*/
	if (type == THEME_BROWSER_TOOLBAR) {
		toolbar->url_buffer = calloc(1, THEME_URL_MEMORY +
				THEME_THROBBER_MEMORY);
		if (!toolbar->url_buffer) {
			LOG(("No memory for calloc()"));
			ro_gui_theme_destroy_toolbar(toolbar);
			return NULL;
		}
		toolbar->throbber_buffer = toolbar->url_buffer +
				THEME_URL_MEMORY;
		sprintf(toolbar->throbber_buffer, "throbber0");
	}

	/*	Apply the desired theme to the toolbar
	*/
	if (!ro_gui_theme_update_toolbar(descriptor, toolbar)) {
		ro_gui_theme_destroy_toolbar(toolbar);
		return NULL;
	}
	toolbar->old_height = ro_gui_theme_toolbar_full_height(toolbar);
	return toolbar;
}


/**
 * Updates a toolbar to use a particular theme.
 * The toolbar may be unstable on failure and should be destroyed.
 *
 * \param descriptor  the theme to use, or NULL for current
 * \param toolbar     the toolbar to update
 * \return whether the operation was successful
 */
bool ro_gui_theme_update_toolbar(struct theme_descriptor *descriptor,
		struct toolbar *toolbar)
{
	wimp_icon_create new_icon;
	os_error *error;
	osspriteop_area *sprite_area;
	struct toolbar_icon *toolbar_icon;
	int width, max_icon;
	wimp_icon_flags icon_flags;
	struct gui_window *g;
	if (!toolbar) return false;

	/*	Set the theme and window sprite area
	*/
	if (!descriptor) descriptor = theme_current;
	toolbar->descriptor = descriptor;
	if ((toolbar->descriptor) && (toolbar->descriptor->theme))
		sprite_area = toolbar->descriptor->theme->sprite_area;
	else
		sprite_area = (osspriteop_area *)1;
	theme_toolbar_window.sprite_area = sprite_area;

	/*	Update the icon sizes
	*/
	for (toolbar_icon = toolbar->icon; toolbar_icon;
			toolbar_icon = toolbar_icon->next)
		ro_gui_theme_update_toolbar_icon(toolbar, toolbar_icon);
	if (toolbar->suggest)
		ro_gui_theme_update_toolbar_icon(toolbar, toolbar->suggest);

	/*	Recreate the toolbar window
	*/
	if (toolbar->descriptor) {
		if (toolbar->type == THEME_BROWSER_TOOLBAR)
			theme_toolbar_window.work_bg =
					toolbar->descriptor->browser_background;
		else
			theme_toolbar_window.work_bg =
					toolbar->descriptor->hotlist_background;
	} else {
		theme_toolbar_window.work_bg = wimp_COLOUR_VERY_LIGHT_GREY;
	}

	theme_toolbar_window.work_flags &= ~wimp_ICON_BUTTON_TYPE;
	if ((toolbar->editor) ||
			(toolbar->type == THEME_HOTLIST_EDIT_TOOLBAR) ||
			(toolbar->type == THEME_HISTORY_EDIT_TOOLBAR) ||
			(toolbar->type == THEME_BROWSER_EDIT_TOOLBAR) ||
			(toolbar->type == THEME_COOKIES_EDIT_TOOLBAR))
		theme_toolbar_window.work_flags |= (wimp_BUTTON_CLICK_DRAG <<
				wimp_ICON_BUTTON_TYPE_SHIFT);
	theme_toolbar_window.sprite_area = sprite_area;
	if (toolbar->toolbar_handle) {
		error = xwimp_delete_window(toolbar->toolbar_handle);
		if (error)
			LOG(("xwimp_delete_window: 0x%x: %s",
					error->errnum, error->errmess));
		ro_gui_wimp_event_finalise(toolbar->toolbar_handle);
		toolbar->toolbar_handle = NULL;
	}
	error = xwimp_create_window(&theme_toolbar_window,
			&toolbar->toolbar_handle);
	if (error) {
		LOG(("xwimp_create_window: 0x%x: %s",
				error->errnum, error->errmess));
		warn_user("WimpError", error->errmess);
		return false;
	}
	ro_gui_wimp_event_register_redraw_window(toolbar->toolbar_handle,
			ro_gui_theme_redraw);
	ro_gui_wimp_event_set_user_data(toolbar->toolbar_handle, toolbar);
	switch (toolbar->type) {
	 	case THEME_BROWSER_TOOLBAR:
	 	case THEME_BROWSER_EDIT_TOOLBAR:
	 		ro_gui_wimp_event_register_mouse_click(toolbar->toolbar_handle,
	 				ro_gui_toolbar_click);
	 		break;
		case THEME_HOTLIST_TOOLBAR:
		case THEME_HOTLIST_EDIT_TOOLBAR:
		case THEME_HISTORY_TOOLBAR:
	  	case THEME_HISTORY_EDIT_TOOLBAR:
		case THEME_COOKIES_TOOLBAR:
	  	case THEME_COOKIES_EDIT_TOOLBAR:
			ro_gui_wimp_event_register_mouse_click(toolbar->toolbar_handle,
					ro_gui_tree_toolbar_click);
			break;
	}

	/*	Create the basic icons
	*/
	if ((toolbar->type == THEME_HOTLIST_TOOLBAR) ||
			(toolbar->type == THEME_HOTLIST_EDIT_TOOLBAR))
		max_icon = ICON_TOOLBAR_HOTLIST_LAST;
	else if ((toolbar->type == THEME_HISTORY_TOOLBAR) ||
			(toolbar->type == THEME_HISTORY_EDIT_TOOLBAR))
		max_icon = ICON_TOOLBAR_HISTORY_LAST;
	else if ((toolbar->type == THEME_COOKIES_TOOLBAR) ||
			(toolbar->type == THEME_COOKIES_EDIT_TOOLBAR))
		max_icon = ICON_TOOLBAR_COOKIES_LAST;
	else
		max_icon = ICON_TOOLBAR_LAST;
	new_icon.w = toolbar->toolbar_handle;
	new_icon.icon.data.indirected_text.size = 1;
	new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE |
			wimp_ICON_INDIRECTED | wimp_ICON_HCENTRED |
			wimp_ICON_VCENTRED;
	if ((toolbar->editor) ||
			(toolbar->type == THEME_HOTLIST_EDIT_TOOLBAR) ||
			(toolbar->type == THEME_HISTORY_EDIT_TOOLBAR) ||
			(toolbar->type == THEME_COOKIES_EDIT_TOOLBAR) ||
			(toolbar->type == THEME_BROWSER_EDIT_TOOLBAR))
		new_icon.icon.flags |= (wimp_BUTTON_CLICK_DRAG <<
				wimp_ICON_BUTTON_TYPE_SHIFT);
	else
		new_icon.icon.flags |= (wimp_BUTTON_CLICK <<
				wimp_ICON_BUTTON_TYPE_SHIFT);
	if (toolbar->descriptor)
		new_icon.icon.flags |= (toolbar->descriptor->browser_background
				 << wimp_ICON_BG_COLOUR_SHIFT);
	else
		new_icon.icon.flags |= (wimp_COLOUR_VERY_LIGHT_GREY
				 << wimp_ICON_BG_COLOUR_SHIFT);
	icon_flags = new_icon.icon.flags;

	for (int i = 0; i < max_icon; i++) {
		new_icon.icon.data.indirected_text.text =
				theme_null_text_string;
		new_icon.icon.data.indirected_text.validation =
				theme_null_text_string;
		toolbar_icon = toolbar->icon;
		while (toolbar_icon) {
			if (toolbar_icon->icon_number == i) {
				new_icon.icon.data.indirected_text.validation =
					toolbar_icon->validation;
				break;
			} else {
				toolbar_icon = toolbar_icon->next;
			}
		}
		error = xwimp_create_icon(&new_icon, 0);
		if (error) {
			LOG(("xwimp_create_icon: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("WimpError", error->errmess);
			return false;
		}
	}

	/*	Create the URL/throbber icons
	*/
	if (toolbar->type == THEME_BROWSER_TOOLBAR) {
	  	/* container for all URL bits (ie border) */
		new_icon.icon.flags = wimp_ICON_BORDER | (wimp_COLOUR_BLACK <<
						wimp_ICON_FG_COLOUR_SHIFT);
		error = xwimp_create_icon(&new_icon, 0);
		if (error) {
			LOG(("xwimp_create_icon: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("WimpError", error->errmess);
			return false;
		}

		/* favicon image */
		new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE |
				wimp_ICON_INDIRECTED | wimp_ICON_FILLED |
				wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
				(wimp_BUTTON_CLICK_DRAG <<
						wimp_ICON_BUTTON_TYPE_SHIFT);
		new_icon.icon.data.indirected_text.text = theme_null_text_string;
		new_icon.icon.data.indirected_text.validation =
				theme_favicon_sprite;
		new_icon.icon.data.indirected_text.size = 1;
		error = xwimp_create_icon(&new_icon, 0);
		if (error) {
			LOG(("xwimp_create_icon: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("WimpError", error->errmess);
			return false;
		}

		/* Writable text portion */
		new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
				wimp_ICON_VCENTRED |
				wimp_ICON_FILLED | (wimp_COLOUR_BLACK <<
						wimp_ICON_FG_COLOUR_SHIFT) |
				(wimp_BUTTON_WRITE_CLICK_DRAG <<
						wimp_ICON_BUTTON_TYPE_SHIFT);
		new_icon.icon.data.indirected_text.text = toolbar->url_buffer;
		new_icon.icon.data.indirected_text.validation =
				theme_url_validation;
		new_icon.icon.data.indirected_text.size = THEME_URL_MEMORY;
		error = xwimp_create_icon(&new_icon, 0);
		if (error) {
			LOG(("xwimp_create_icon: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("WimpError", error->errmess);
			return false;
		}

		/*	Now the URL suggestion icon
		*/
		new_icon.icon.data.indirected_text.text =
				theme_null_text_string;
		new_icon.icon.data.indirected_text.size = 1;
		new_icon.icon.flags = icon_flags | (wimp_BUTTON_CLICK <<
				wimp_ICON_BUTTON_TYPE_SHIFT);
		if (toolbar->suggest)
			new_icon.icon.data.indirected_text.validation =
					toolbar->suggest->validation;
		else
			new_icon.icon.data.indirected_text.validation =
					theme_null_text_string;
		error = xwimp_create_icon(&new_icon, 0);
		if (error) {
			LOG(("xwimp_create_icon: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("WimpError", error->errmess);
			return false;
		}

		/*	Now the throbber
		*/
		new_icon.icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED |
				wimp_ICON_HCENTRED | wimp_ICON_VCENTRED;
		new_icon.icon.data.indirected_sprite.id =
				(osspriteop_id)toolbar->throbber_buffer;
		new_icon.icon.data.indirected_sprite.area = sprite_area;
		new_icon.icon.data.indirected_sprite.size =
				THEME_THROBBER_MEMORY;
		error = xwimp_create_icon(&new_icon, 0);
		if (error) {
			LOG(("xwimp_create_icon: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("WimpError", error->errmess);
			return false;
		}
	}
	if (toolbar->parent_handle)
		ro_gui_theme_attach_toolbar(toolbar, toolbar->parent_handle);


	/*	Force a re-processing of the toolbar
	*/
	width = toolbar->toolbar_current;
	toolbar->reformat_buttons = true;
	toolbar->toolbar_current = -1;
	ro_gui_theme_process_toolbar(toolbar, width);

	/*	Keep menus up to date etc
	*/
	ro_gui_theme_set_help_prefix(toolbar);
	switch (toolbar->type) {
		case THEME_BROWSER_TOOLBAR:
			g = ro_gui_window_lookup(toolbar->parent_handle);
			if (g)
				ro_gui_prepare_navigate(g);
			break;
		case THEME_HOTLIST_TOOLBAR:
		case THEME_HISTORY_TOOLBAR:
		case THEME_COOKIES_TOOLBAR:
			ro_gui_menu_prepare_action(toolbar->parent_handle,
					TREE_SELECTION, false);
			ro_gui_menu_prepare_action(toolbar->parent_handle,
					TREE_EXPAND_ALL, false);
			break;
		default:
			break;
	}
	return true;
}


/**
 * Attaches a toolbar to a window.
 *
 * \param toolbar     the toolbar to update
 * \param parent      the window to contain the toolbar
 * \return whether the operation was successful
 */
bool ro_gui_theme_attach_toolbar(struct toolbar *toolbar, wimp_w parent)
{
	wimp_outline outline;
	wimp_window_state state;
	int height;
	os_error *error;

	if (!toolbar)
		return false;

	/*	Attach/close the windows
	*/
	toolbar->parent_handle = parent;
	height = ro_gui_theme_toolbar_height(toolbar);
	if (height > 0) {
		outline.w = parent;
		xwimp_get_window_outline(&outline);
		state.w = parent;
		xwimp_get_window_state(&state);
		state.w = toolbar->toolbar_handle;
		state.visible.x1 = outline.outline.x1 - 2;
		state.visible.y0 = state.visible.y1 - height + 2;
		state.xscroll = 0;
		state.yscroll = toolbar->height - 2; /* clipped by the WIMP */
		error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), parent,
				wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
						<< wimp_CHILD_XORIGIN_SHIFT |
				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
						<< wimp_CHILD_YORIGIN_SHIFT |
				wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
						<< wimp_CHILD_LS_EDGE_SHIFT |
				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
						<< wimp_CHILD_BS_EDGE_SHIFT |
				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
						<< wimp_CHILD_RS_EDGE_SHIFT |
				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
						<< wimp_CHILD_TS_EDGE_SHIFT);
		if (error) {
			LOG(("xwimp_open_window_nested: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("WimpError", error->errmess);
			return false;
		}
		if (!toolbar->editor)
			return true;

		state.w = toolbar->editor->toolbar_handle;
		state.visible.y1 -= toolbar->height;
		state.yscroll = toolbar->editor->height - 2;
		error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state),
				toolbar->toolbar_handle,
				wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
						<< wimp_CHILD_XORIGIN_SHIFT |
				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
						<< wimp_CHILD_YORIGIN_SHIFT |
				wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
						<< wimp_CHILD_LS_EDGE_SHIFT |
				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
						<< wimp_CHILD_BS_EDGE_SHIFT |
				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
						<< wimp_CHILD_RS_EDGE_SHIFT |
				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
						<< wimp_CHILD_TS_EDGE_SHIFT);
		if (error) {
			LOG(("xwimp_open_window_nested: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("WimpError", error->errmess);
			return false;
		}
		return true;
	}

	error = xwimp_close_window(toolbar->toolbar_handle);
	if (error) {
		LOG(("xwimp_close_window: 0x%x: %s",
				error->errnum, error->errmess));
		warn_user("WimpError", error->errmess);
		return false;
	}
	return true;
}


/**
 * Updates the toolbar to reflect changes to the icon flags and any reformatting
 * required due to the change in parent window size.
 *
 * \param toolbar     the toolbar to update
 * \param width	      a specific width to resize to, or -1 to use parent width
 * \return whether the operation was successful
 */
bool ro_gui_theme_process_toolbar(struct toolbar *toolbar, int width)
{
	wimp_caret caret;
	os_box extent = { 0, 0, 0, 0 };
	os_error *error;
	wimp_outline outline;
	wimp_window_state state;
	int height = -1;
	int throbber_x = -1;
	int left_edge, right_edge, bottom_edge;
	int old_height;
	int old_width;
	struct toolbar_icon *toolbar_icon;
	bool visible_icon = false;
	int collapse_height;
	int xeig, yeig;
	os_coord pixel = {1, 1};
	int top, bottom, right;

	if (!toolbar)
		return false;

	old_height = toolbar->height;
	old_width = toolbar->toolbar_current;

	/* calculate 1px in OS units */
	ro_convert_pixels_to_os_units(&pixel, (os_mode)-1);
	xeig = pixel.x;
	yeig = pixel.y;

	/* find the parent window handle if we need to process the status
	 * window, or the caller has requested we calculate the width ourself */
	if ((toolbar->parent_handle) && (width == -1)) {
		outline.w = toolbar->parent_handle;
		error = xwimp_get_window_outline(&outline);
		if (error) {
			LOG(("xwimp_get_window_outline: 0x%x: %s",
				error->errnum, error->errmess));
			warn_user("WimpError", error->errmess);
			return false;
		}
		if (width == -1)
			width = outline.outline.x1 - outline.outline.x0 - 2;
	}

	/*	Find the parent visible height to clip our toolbar height to
	*/
	if ((toolbar->toolbar_handle) && (toolbar->parent_handle)) {
		/*	Get the current state
		*/
		state.w = toolbar->parent_handle;
		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);
			return false;
		}
		height = state.visible.y1 - state.visible.y0 + 2;

		/*	We can't obscure the height of the scroll bar as we
			lose the resize icon if we do.
		*/
		if ((state.flags & wimp_WINDOW_SIZE_ICON) &&
				!(state.flags & wimp_WINDOW_HSCROLL))
			height -= ro_get_hscroll_height(0) - 2;

		/*	Update our position
		*/
		if (height != toolbar->max_height) {
			if ((state.flags & wimp_WINDOW_SIZE_ICON) &&
					!(state.flags & wimp_WINDOW_HSCROLL) &&
					(toolbar->height > toolbar->max_height))
				xwimp_force_redraw(toolbar->parent_handle,
					0, -16384, 16384, 16384);
			toolbar->max_height = height;
			collapse_height = toolbar->height +
					(toolbar->editor ? toolbar->editor->height : 0);
			ro_gui_theme_attach_toolbar(toolbar, toolbar->parent_handle);
			if ((state.flags & wimp_WINDOW_SIZE_ICON) &&
					!(state.flags & wimp_WINDOW_HSCROLL) &&
					(collapse_height > toolbar->max_height))
				xwimp_force_redraw(toolbar->parent_handle,
					0, -16384, 16384, 16384);
		}
	}

	/*	Reformat the buttons starting with the throbber
	*/
	if ((width != old_width) || (toolbar->reformat_buttons)) {
		left_edge = 6;
		right_edge = width - 8;
		toolbar->height = 0;
		if ((toolbar->descriptor) && (toolbar->descriptor->theme) &&
				(toolbar->type == THEME_BROWSER_TOOLBAR) &&
				(toolbar->display_throbber)) {
			if (!toolbar->descriptor->throbber_right) {
				throbber_x = left_edge;
				left_edge += toolbar->descriptor->theme->throbber_width + 8;
			}
			toolbar->height = toolbar->descriptor->theme->throbber_height + 8;
		}
		if ((toolbar->type == THEME_BROWSER_TOOLBAR) &&	(toolbar->display_url)) {
			if (toolbar->height < 52 + 8)
				toolbar->height = 52 + 8;
			if ((toolbar->suggest) && (toolbar->height <
					(toolbar->suggest->height + 8)))
				toolbar->height = toolbar->suggest->height + 8;
		}

		/*	Get the minimum height of the icons
		*/
		bottom_edge = left_edge;
		if ((toolbar->display_buttons || toolbar->editor) && (toolbar->descriptor) &&
				(toolbar->descriptor->theme)) {
			toolbar_icon = toolbar->icon;
			while (toolbar_icon) {
				if (toolbar_icon->display) {
					bottom_edge += toolbar_icon->width;
					visible_icon = true;
					if ((toolbar_icon->height != 0) &&
						(toolbar->height < toolbar_icon->height + 8)) {
						toolbar->height = toolbar_icon->height + 8;
					}
				}
				toolbar_icon = toolbar_icon->next;
			}
			if (visible_icon) bottom_edge += 8;
		}

		/*	Check for minimum widths
		*/
		if (toolbar->type == THEME_BROWSER_TOOLBAR) {
			if (!toolbar->reformat_buttons) left_edge = bottom_edge;
			if (toolbar->display_url) {
				bottom_edge += 112;
				if (toolbar->suggest)
					bottom_edge += toolbar->suggest->width + 8;
			}
			if (bottom_edge > right_edge)
				right_edge = bottom_edge;
			if ((toolbar->descriptor) && (toolbar->descriptor->theme) &&
					(toolbar->display_throbber) &&
					(toolbar->descriptor->throbber_right)) {
				bottom_edge += toolbar->descriptor->theme->throbber_width;
				if (bottom_edge > right_edge) right_edge = bottom_edge;
				throbber_x = right_edge -
						toolbar->descriptor->theme->throbber_width;
				right_edge -= toolbar->descriptor->theme->throbber_width + 8;
			}
		}

		if (toolbar->height != 0)
			toolbar->height += 2;
		if (toolbar->reformat_buttons) {
			/*	Hide the URL bar if we should
			*/
			if ((!toolbar->display_url) &&
					(toolbar->type == THEME_BROWSER_TOOLBAR)) {
				if (!xwimp_get_caret_position(&caret)) {
					if ((caret.w == toolbar->toolbar_handle) &&
							(caret.i == ICON_TOOLBAR_URL)) {
						if (toolbar->parent_handle)
							xwimp_set_caret_position(
									toolbar->parent_handle,
									wimp_ICON_WINDOW,
									-100, -100, 32, -1);
						else
							xwimp_set_caret_position((wimp_w)-1,
									0, 0, 0, 0, 0);
					}
				}
				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_SURROUND,
						0, -16384, 0, -16384);
				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_FAVICON,
						0, -16384, 0, -16384);
				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_URL,
						0, -16384, 0, -16384);
				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_SUGGEST,
						0, -16384, 0, -16384);
			} else if (toolbar->type == THEME_BROWSER_TOOLBAR) {
				ro_gui_set_icon_shaded_state(toolbar->toolbar_handle,
						ICON_TOOLBAR_URL, !toolbar->display_url);
			}
			xwimp_force_redraw(toolbar->toolbar_handle,
					0, 0, 16384, 16384);

			/*	Move the buttons
			*/
			toolbar_icon = toolbar->icon;
			while (toolbar_icon) {
				if ((toolbar->display_buttons || toolbar->editor) &&
						(toolbar_icon->display)
						&& (toolbar_icon->width > 0)) {
					visible_icon = true;
					bottom_edge = (toolbar->height -
							toolbar_icon->height) / 2;
					toolbar_icon->x = left_edge;
					toolbar_icon->y = bottom_edge;
					xwimp_resize_icon(toolbar->toolbar_handle,
							toolbar_icon->icon_number,
							left_edge, bottom_edge,
							left_edge + toolbar_icon->width,
							bottom_edge + toolbar_icon->height);
					left_edge += toolbar_icon->width;
				} else {
					xwimp_resize_icon(toolbar->toolbar_handle,
							toolbar_icon->icon_number,
							0, -16384, 0, -16384);
				}
				toolbar_icon = toolbar_icon->next;
			}
			if (visible_icon) left_edge += 8;
		}


		if (toolbar->type == THEME_BROWSER_TOOLBAR) {
			/*	Move the URL bar
			*/
			if (toolbar->display_url) {
			  	top = (toolbar->height / 2) + 26;
			  	bottom = (toolbar->height / 2) - 26;
				if (toolbar->suggest) {
					right = right_edge - toolbar->suggest->width - 8;
					xwimp_resize_icon(toolbar->toolbar_handle,
						ICON_TOOLBAR_SUGGEST,
						right_edge - toolbar->suggest->width,
						(toolbar->height - toolbar->suggest->height) / 2,
						right_edge,
						(toolbar->height + toolbar->suggest->height) / 2);
				} else {
				  	right = right_edge;
				}
				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_URL,
					left_edge + 52,
					bottom + yeig,
					right - xeig,
					top - yeig);
				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_FAVICON,
					left_edge + xeig,
					bottom + yeig,
					left_edge + 52,
					top - yeig);
				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_SURROUND,
					left_edge,
					bottom,
					right,
					top);
				xwimp_force_redraw(toolbar->toolbar_handle,
					right - xeig, 0, 16384, 16384);
				xwimp_force_redraw(toolbar->toolbar_handle,
					left_edge,
					bottom,
					right,
					bottom + yeig);
				xwimp_force_redraw(toolbar->toolbar_handle,
					left_edge,
					top - yeig,
					right,
					top);
				if (!xwimp_get_caret_position(&caret)) {
					if ((caret.w == toolbar->toolbar_handle) &&
							(caret.i == ICON_TOOLBAR_URL)) {
						xwimp_set_caret_position(toolbar->toolbar_handle,
								ICON_TOOLBAR_URL,
								caret.pos.x, caret.pos.y,
								-1, caret.index);
					}
				}
				ro_gui_redraw_icon(toolbar->toolbar_handle, ICON_TOOLBAR_URL);
			}

			/*	Move the throbber
			*/
			if ((toolbar->descriptor) && (toolbar->descriptor->theme) &&
					(throbber_x >= 0) && (toolbar->display_throbber)) {
				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_THROBBER,
					throbber_x, 0,
					throbber_x + toolbar->descriptor->theme->throbber_width,
					toolbar->height);
				if (toolbar->descriptor->throbber_right) {
					xwimp_force_redraw(toolbar->toolbar_handle,
						old_width - width + throbber_x, 0, 16384, 16384);
					xwimp_force_redraw(toolbar->toolbar_handle,
						throbber_x, 0, 16384, 16384);
				}

			} else {
				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_THROBBER,
					0, -16384, 0, -16384);
			}
		}

		/*	Re-attach to the parent
		*/
		toolbar->toolbar_current = width;
		if (toolbar->reformat_buttons) {
			extent.x1 = 16384;
			extent.y0 = (toolbar->editor ? -toolbar->editor->height : 0);
			extent.y1 = toolbar->height - 2;
			xwimp_set_extent(toolbar->toolbar_handle, &extent);
			if ((toolbar->parent_handle) && (old_height != toolbar->height))
				ro_gui_theme_attach_toolbar(toolbar, toolbar->parent_handle);
		}
		toolbar->reformat_buttons = false;
	}
	return true;
}


/**
 * Destroys a toolbar and frees any associated memory.
 *
 * \param toolbar     the toolbar to destroy
 */
void ro_gui_theme_destroy_toolbar(struct toolbar *toolbar)
{
	struct toolbar_icon *icon;
	struct toolbar_icon *next_icon;
	if (!toolbar) return;

	/*	Destroy our editor
	*/
	if (toolbar->editor) {
		toolbar->editor = NULL;
		ro_gui_theme_destroy_toolbar(toolbar->editor);
	}

	/*	Delete our windows
	*/
	if (toolbar->toolbar_handle) {
		xwimp_delete_window(toolbar->toolbar_handle);
		ro_gui_wimp_event_finalise(toolbar->toolbar_handle);
	}
	/*	Free the Wimp buffer (we only created one for them all)
	*/
	free(toolbar->url_buffer);

	/*	Free all the icons
	*/
	next_icon = toolbar->icon;
	while ((icon = next_icon) != NULL) {
		next_icon = icon->next;
		ro_gui_theme_destroy_toolbar_icon(icon);
	}
	ro_gui_theme_destroy_toolbar_icon(toolbar->suggest);
	free(toolbar);
}


/**
 * Toggles the toolbar editing mode
 *
 * \param toolbar      the toolbar to toggle editing for
 */
void ro_gui_theme_toggle_edit(struct toolbar *toolbar)
{
	int icons = 0;
	struct toolbar_icon *icon;
	struct gui_window *g = NULL;
	wimp_window_state state;
	os_error *error;
	char *option;
	char hex_no[4];

	if (!toolbar)
		return;

	if ((toolbar->type == THEME_BROWSER_TOOLBAR) &&
			(toolbar->parent_handle))
		g = ro_gui_window_lookup(toolbar->parent_handle);

	if (toolbar->editor) {
		/* save options */
		icons = 0;
		for (icon = toolbar->icon; icon; icon = icon->next)
			if (icon->display) icons++;
		option = calloc(icons + 1, 1);
		if (!option) {
			LOG(("No memory to save toolbar options"));
			warn_user("NoMemory", 0);
		} else {
			icons = 0;
			for (icon = toolbar->icon; icon; icon = icon->next)
				if (icon->display) {
					if (icon->icon_number == -1) {
						option[icons] = '|';
					} else {
						sprintf(hex_no, "%x", icon->icon_number);
						option[icons] = hex_no[0];
					}
					icons++;
				}
			switch (toolbar->type) {
				case THEME_BROWSER_TOOLBAR:
					free(option_toolbar_browser);
					option_toolbar_browser = option;
					break;
				case THEME_HOTLIST_TOOLBAR:
					free(option_toolbar_hotlist);
					option_toolbar_hotlist = option;
					break;
				case THEME_HISTORY_TOOLBAR:
					free(option_toolbar_history);
					option_toolbar_history = option;
					break;
				case THEME_COOKIES_TOOLBAR:
					free(option_toolbar_cookies);
					option_toolbar_cookies = option;
					break;
				default:
					break;
			}
			ro_gui_save_options();
		}

		/* turn off editing */
		ro_gui_theme_destroy_toolbar(toolbar->editor);
		toolbar->editor = NULL;
		ro_gui_theme_update_toolbar(toolbar->descriptor, toolbar);
		switch (toolbar->type) {
			case THEME_BROWSER_TOOLBAR:
				if (g)
					gui_window_update_extent(g);
				break;
			default:
				if (toolbar->parent_handle)
					xwimp_force_redraw(toolbar->parent_handle,
							0, -16384, 16384, 16384);
				break;
		}
	} else {
		/* create/initialise the toolbar editor */
		switch (toolbar->type) {
			case THEME_BROWSER_TOOLBAR:
				toolbar->editor = ro_gui_theme_create_toolbar(
						toolbar->descriptor,
						THEME_BROWSER_EDIT_TOOLBAR);
				break;
			case THEME_HOTLIST_TOOLBAR:
				toolbar->editor = ro_gui_theme_create_toolbar(
						toolbar->descriptor,
						THEME_HOTLIST_EDIT_TOOLBAR);
				break;
			case THEME_HISTORY_TOOLBAR:
				toolbar->editor = ro_gui_theme_create_toolbar(
						toolbar->descriptor,
						THEME_HISTORY_EDIT_TOOLBAR);
				break;
			case THEME_COOKIES_TOOLBAR:
				toolbar->editor = ro_gui_theme_create_toolbar(
						toolbar->descriptor,
						THEME_COOKIES_EDIT_TOOLBAR);
				break;
			default:
				return;
		}
		if (!toolbar->editor) {
			LOG(("Unable to create toolbar editor"));
			return;
		}
		ro_gui_wimp_event_set_user_data(toolbar->editor->toolbar_handle,
				ro_gui_wimp_event_get_user_data(toolbar->toolbar_handle));
		ro_gui_theme_update_toolbar(toolbar->descriptor, toolbar);
		switch (toolbar->type) {
			case THEME_BROWSER_TOOLBAR:
				if (g)
					gui_window_update_extent(g);
				break;
			default:
				if (toolbar->parent_handle) {
					state.w = toolbar->parent_handle;
					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);
						return;
					}
					ro_gui_open_window_request(PTR_WIMP_OPEN(&state));
					xwimp_force_redraw(toolbar->parent_handle,
							0, -16384, 16384, 16384);
				}
				break;
		}
		ro_gui_theme_process_toolbar(toolbar, -1);
		ro_gui_theme_toolbar_editor_sync(toolbar);
	}
	ro_gui_theme_set_help_prefix(toolbar);
}


/**
 * Synchronise a toolbar window with the associated editor.
 *
 * \param toolbar  the toolbar to synchronise
 */
void ro_gui_theme_toolbar_editor_sync(struct toolbar *toolbar)
{
	struct toolbar_icon *icon;
	struct toolbar_icon *icon_edit;

	if ((!toolbar) || (!toolbar->editor))
		return;

	for (icon = toolbar->icon; icon; icon = icon->next)
		if ((icon->icon_number >= 0) && (icon->width > 0))
			for (icon_edit = toolbar->editor->icon; icon_edit;
					icon_edit = icon_edit->next)
				if (icon_edit->icon_number == icon->icon_number)
					ro_gui_set_icon_shaded_state(
							toolbar->editor->toolbar_handle,
							icon_edit->icon_number,
							icon->display);
}


/**
 * Handle a toolbar click during an editor session
 *
 * \param toolbar  the base toolbar (ie not editor) to respond to a click for
 * \param pointer  the WIMP pointer details
 */
void ro_gui_theme_toolbar_editor_click(struct toolbar *toolbar,
		wimp_pointer *pointer) {
	wimp_window_state state;
	os_error *error;
	os_box box;

	if (!toolbar->editor)
		return;
	if ((pointer->buttons != (wimp_CLICK_SELECT << 4)) &&
			(pointer->buttons != (wimp_CLICK_ADJUST << 4)))
		return;

	state.w = pointer->w;
	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);
		return;
	}

	gui_current_drag_type = GUI_DRAG_TOOLBAR_CONFIG;
	theme_toolbar_drag = toolbar;
	theme_toolbar_editor_drag = !(pointer->w == toolbar->toolbar_handle);
	if (theme_toolbar_editor_drag)
		theme_toolbar_icon_drag =
				ro_gui_theme_toolbar_get_icon(toolbar->editor,
				pointer->pos.x - state.visible.x0,
				state.visible.y1 - pointer->pos.y);
	else
		theme_toolbar_icon_drag =
				ro_gui_theme_toolbar_get_icon(toolbar,
				pointer->pos.x - state.visible.x0,
				state.visible.y1 - pointer->pos.y);
	if (!theme_toolbar_icon_drag)
		return;
	if ((theme_toolbar_icon_drag->icon_number >= 0) &&
			(pointer->w == toolbar->editor->toolbar_handle) &&
			(ro_gui_get_icon_shaded_state(
					toolbar->editor->toolbar_handle,
					theme_toolbar_icon_drag->icon_number)))
		return;

	box.x0 = pointer->pos.x - theme_toolbar_icon_drag->width / 2;
	box.x1 = box.x0 + theme_toolbar_icon_drag->width;
	box.y0 = pointer->pos.y - theme_toolbar_icon_drag->height / 2;
	box.y1 = box.y0 + theme_toolbar_icon_drag->height;
	error = xdragasprite_start(dragasprite_HPOS_CENTRE |
			dragasprite_VPOS_CENTRE |
			dragasprite_BOUND_POINTER |
			dragasprite_DROP_SHADOW,
			toolbar->descriptor->theme->sprite_area,
			theme_toolbar_icon_drag->name, &box, 0);
	if (error)
		LOG(("xdragasprite_start: 0x%x: %s",
				error->errnum, error->errmess));
}


/**
 * Handle the end of a drag
 *
 * \param drag  the details for the drag end
 */
void ro_gui_theme_toolbar_editor_drag_end(wimp_dragged *drag)
{
	wimp_window_state state;
	os_error *error;
	wimp_pointer pointer;
	struct toolbar_icon *insert_icon;
	struct toolbar_icon *local_icon = NULL;
	struct toolbar_icon *icon;
	bool before = false;

	if ((!theme_toolbar_drag) || (!theme_toolbar_icon_drag) ||
			(!theme_toolbar_drag->editor))
		return;

	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);
		return;
	}

	if (pointer.w == theme_toolbar_drag->toolbar_handle) {
		/* drag from editor or toolbar to toolbar */
		state.w = pointer.w;
		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);
			return;
		}
		insert_icon = ro_gui_theme_toolbar_get_insert_icon(
				theme_toolbar_drag,
				pointer.pos.x - state.visible.x0,
				state.visible.y1 - pointer.pos.y, &before);
		if (theme_toolbar_icon_drag->icon_number == -1) {
			if (theme_toolbar_editor_drag) {
				theme_toolbar_icon_drag =
						ro_gui_theme_add_toolbar_icon(
							theme_toolbar_drag,
							NULL, -1);
				ro_gui_theme_update_toolbar_icon(
						theme_toolbar_drag,
						theme_toolbar_icon_drag);
			}
			/* move the separator */
			if (theme_toolbar_icon_drag != insert_icon) {
				ro_gui_theme_delink_toolbar_icon(
						theme_toolbar_drag,
						theme_toolbar_icon_drag);
				ro_gui_theme_link_toolbar_icon(
						theme_toolbar_drag,
						theme_toolbar_icon_drag,
						insert_icon, before);
			}
		} else {
			/* move/enable the icon */
			for (icon = theme_toolbar_drag->icon; icon;
					icon = icon->next)
				if (theme_toolbar_icon_drag->icon_number ==
						icon->icon_number)
					local_icon = icon;
			if (!local_icon)
				return;
			if (local_icon != insert_icon) {
				ro_gui_theme_delink_toolbar_icon(
						theme_toolbar_drag, local_icon);
				ro_gui_theme_link_toolbar_icon(
						theme_toolbar_drag, local_icon,
						insert_icon, before);
			}
			local_icon->display = true;
		}
	} else if ((pointer.w == theme_toolbar_drag->editor->toolbar_handle) &&
			(!theme_toolbar_editor_drag)) {
		/* drag from toolbar to editor */
		if (theme_toolbar_icon_drag->icon_number == -1) {
			/* delete separators */
			ro_gui_theme_delink_toolbar_icon(theme_toolbar_drag,
					theme_toolbar_icon_drag);
			ro_gui_theme_destroy_toolbar_icon(
					theme_toolbar_icon_drag);
		} else {
			/* hide icons */
			theme_toolbar_icon_drag->display = false;
		}
	}
	theme_toolbar_drag->reformat_buttons = true;
	ro_gui_theme_process_toolbar(theme_toolbar_drag, -1);
	ro_gui_theme_toolbar_editor_sync(theme_toolbar_drag);
}


/**
 * Adds a toolbar icon to the end of a toolbar
 *
 * \param toolbar      the toolbar to add the icon to the end of (or NULL)
 * \param name	       the icon name, or NULL for a separator
 * \param icon_number  RISC OS wimp icon number for the icon (not separators)
 */
struct toolbar_icon *ro_gui_theme_add_toolbar_icon(struct toolbar *toolbar,
		const char *name, int icon_number)
{
	struct toolbar_icon *toolbar_icon;
	struct toolbar_icon *link_icon;

	/*	Separators are really a sprite called "separator"
	*/
	if (name == NULL) {
		name = "separator";
		icon_number = -1;
	}

	/*	Create a new toolbar
	*/
	toolbar_icon = calloc(sizeof(struct toolbar_icon), 1);
	if (!toolbar_icon) {
		LOG(("No memory for malloc()"));
		warn_user("NoMemory", 0);
		return NULL;
	}

	/*	Set up and link in the icon
	*/
	sprintf(toolbar_icon->name, name);
	sprintf(toolbar_icon->validation, "R5;S%s,p%s", name, name);
	toolbar_icon->icon_number = icon_number;
	toolbar_icon->display = true;
	if (toolbar) {
		if (!toolbar->icon) {
			toolbar->icon = toolbar_icon;
		} else {
			link_icon = toolbar->icon;
			while (link_icon->next) link_icon = link_icon->next;
			link_icon->next = toolbar_icon;
		}
	}
	return toolbar_icon;
}


/**
 * Updates a toolbar icon with respect to the associated sprite.
 *
 * \param icon	   the toolbar icon to update
 */
void ro_gui_theme_update_toolbar_icon(struct toolbar *toolbar,
		struct toolbar_icon *icon)
{
	os_coord dimensions = {0, 0};
	os_mode mode;
	os_error *error = NULL;
	int default_width = 0;
	osspriteop_area *sprite_area = NULL;

	/* Separators default to a width of 16 */
	if (icon->icon_number == -1)
		default_width = 16;

	/* Handle no theme/no sprite area */
	if (!toolbar)
		return;
	if ((toolbar->descriptor) && (toolbar->descriptor->theme))
		sprite_area = toolbar->descriptor->theme->sprite_area;

	/* Get the sprite details */
	if (sprite_area)
		error = xosspriteop_read_sprite_info(osspriteop_USER_AREA,
				sprite_area, (osspriteop_id)icon->name,
				&dimensions.x, &dimensions.y, 0, &mode);

	/* fallback to Wimp pool, if necessary */
	if (error || sprite_area == NULL)
		error = xwimpspriteop_read_sprite_info(icon->name,
				&dimensions.x, &dimensions.y, 0, &mode);

	/* Give up, if both failed */
	if (error) {
		icon->width = default_width;
		icon->height = 0;
		if (error->errnum != error_SPRITE_OP_DOESNT_EXIST) {
			LOG(("xosspriteop_read_sprite_info: 0x%x: %s",
					error->errnum, error->errmess));
			warn_user("MiscError", error->errmess);
		}
		return;
	}

	/* Store the details */
	ro_convert_pixels_to_os_units(&dimensions, mode);
	icon->width = dimensions.x;
	icon->height = dimensions.y;
}


/**
 * Destroys a toolbar icon and frees any associated memory.
 * The icon is not removed from any linked list.
 *
 * \param icon	   the toolbar icon to destroy
 */
void ro_gui_theme_destroy_toolbar_icon(struct toolbar_icon *icon)
{
	free(icon);
}


/**
 * Links a toolbar icon
 *
 * \param icon	   the toolbar icon to link
 */
void ro_gui_theme_link_toolbar_icon(struct toolbar *toolbar,
		struct toolbar_icon *icon, struct toolbar_icon *link,
		bool before)
{
	struct toolbar_icon *temp;
	assert(toolbar);
	assert(icon);
	assert(icon != link);

	/* no icon set, no link icon, or insert at head of list */
	if ((!toolbar->icon) || (!link) ||
			(before && (toolbar->icon == link))) {
		if (toolbar->icon != icon) {
			icon->next = toolbar->icon;
			toolbar->icon = icon;
		}
		return;
	}

	if (before) {
		for (temp = toolbar->icon; temp; temp = temp->next)
			if (temp->next == link) {
				temp->next = icon;
				icon->next = link;
				return;
			}
	} else {
		icon->next = link->next;
		link->next = icon;
	}
}

/**
 * Delinks a toolbar icon
 *
 * \param icon	   the toolbar icon to delink
 */
void ro_gui_theme_delink_toolbar_icon(struct toolbar *toolbar,
		struct toolbar_icon *icon)
{
	struct toolbar_icon *link;
	assert(toolbar);
	assert(icon);

	if (toolbar->icon == icon) {
		toolbar->icon = icon->next;
		icon->next = NULL;
		return;
	}

	for (link = toolbar->icon; link; link = link->next)
		if (link->next == icon) {
			link->next = icon->next;
			icon->next = NULL;
			return;
		}
}


/**
 * Returns the toolbar icon at a specified position
 *
 * \param toolbar  the toolbar to examine
 * \param x	   the x co-ordinate to check
 * \param y	   the y co-ordinate to check
 * \return the toolbar icon at the specified position, or NULL for no icon
 */
struct toolbar_icon *ro_gui_theme_toolbar_get_icon(struct toolbar *toolbar,
		int x, int y)
{
	struct toolbar_icon *icon;

	for (icon = toolbar->icon; icon; icon = icon->next)
		if ((icon->display) && (icon->width > 0) &&
				(icon->x <= x) && (icon->y <= y) &&
				(icon->x + icon->width > x) &&
				(icon->y + icon->height > y))
			return icon;
	return NULL;
}


/**
 * Returns the toolbar icon closest to the specified position, and whether the
 * position is before (left) or after (right) of it.
 *
 * \param toolbar  the toolbar to examine
 * \param x	   the x co-ordinate to check
 * \param y	   the y co-ordinate to check
 * \return the toolbar icon closest to the specified position, or NULL
 */
struct toolbar_icon *ro_gui_theme_toolbar_get_insert_icon(
		struct toolbar *toolbar, int x, int y, bool *before)
{
	struct toolbar_icon *match = NULL;
	struct toolbar_icon *icon;
	int closest = 65536;
	int distance;

	if (!toolbar->icon)
		return NULL;

	for (icon = toolbar->icon; icon; icon = icon->next) {
		if ((icon->display) && (icon->width > 0)) {
			distance = icon->x + icon->width / 2 - x;
			if (distance < 0)
				distance = -distance;
			if (distance < closest) {
				closest = distance;
				match = icon;
				*before = (icon->x + icon->width / 2 - x) > 0;
			}
		}
	}
	return match;
}


/**
 * Sets up a toolbar with icons according to an identifier string
 */
void ro_gui_theme_add_toolbar_icons(struct toolbar *toolbar,
		const char* icons[], const char* ident)
{
	struct toolbar_icon *icon;
	int index = 0;
	int number = 0;
	char hex_no[4];

	/* step 1: add all main icons in their correct state */
	while (icons[index]) {
		icon = ro_gui_theme_add_toolbar_icon(toolbar, icons[index],
				index);
		sprintf(hex_no, "%x", index);
		if ((icon) && (!strchr(ident, hex_no[0])))
			icon->display = false;
		index++;
	}

	/* step 2: re-order and add separators */
	index = strlen(ident);
	while (index--) {
		if (ident[index] == '|') {
			icon = ro_gui_theme_add_toolbar_icon(NULL, NULL, -1);
			if (icon)
				ro_gui_theme_link_toolbar_icon(toolbar, icon,
						NULL, NULL);
		} else {
			hex_no[0] = ident[index];
			hex_no[1] = '\0';
			number = strtol(hex_no, NULL, 16);
			for (icon = toolbar->icon; icon; icon = icon->next)
				if (icon->icon_number == number) {
					ro_gui_theme_delink_toolbar_icon(
							toolbar, icon);
					ro_gui_theme_link_toolbar_icon(toolbar,
							icon, NULL, NULL);
				}
		}
	}
}


/**
 * Sets the correct help prefix for a toolbar
 */
void ro_gui_theme_set_help_prefix(struct toolbar *toolbar)
{
	if (toolbar->editor) {
		ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
				"HelpEditToolbar");
		return;
	}
	switch (toolbar->type) {
	 	case THEME_BROWSER_TOOLBAR:
			ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
					"HelpToolbar");
			break;
		case THEME_HOTLIST_TOOLBAR:
			ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
					"HelpHotToolbar");
			break;
		case THEME_HISTORY_TOOLBAR:
			ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
					"HelpGHistToolbar");
			break;
		case THEME_COOKIES_TOOLBAR:
			ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
					"HelpCookiesToolbar");
			break;
	 	case THEME_BROWSER_EDIT_TOOLBAR:
		case THEME_HOTLIST_EDIT_TOOLBAR:
	  	case THEME_HISTORY_EDIT_TOOLBAR:
	  	case THEME_COOKIES_EDIT_TOOLBAR:
			ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
					"HelpEditToolbar");
			break;
	}
}

int ro_gui_theme_height_change(struct toolbar *toolbar)
{
  	int height, cur_height;

  	cur_height = ro_gui_theme_toolbar_full_height(toolbar);
  	height = toolbar->old_height - cur_height;
  	toolbar->old_height = cur_height;
	return height;
}
