PageRenderTime 94ms CodeModel.GetById 2ms app.highlight 78ms RepoModel.GetById 1ms app.codeStats 1ms

/peek-build/src/netsurf/riscos/theme.c

https://bitbucket.org/C0deMaver1ck/peeklinux
C | 2248 lines | 1719 code | 191 blank | 338 comment | 377 complexity | 5258496f5b9d407a85d243c1f2bfdf76 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
   3 *
   4 * This file is part of NetSurf, http://www.netsurf-browser.org/
   5 *
   6 * NetSurf is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; version 2 of the License.
   9 *
  10 * NetSurf is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18
  19/** \file
  20 * Window themes and toolbars (implementation).
  21 */
  22
  23#include <alloca.h>
  24#include <assert.h>
  25#include <stdio.h>
  26#include <stdbool.h>
  27#include <string.h>
  28#include "oslib/dragasprite.h"
  29#include "oslib/os.h"
  30#include "oslib/osgbpb.h"
  31#include "oslib/osfile.h"
  32#include "oslib/osfind.h"
  33#include "oslib/osspriteop.h"
  34#include "oslib/wimpspriteop.h"
  35#include "oslib/squash.h"
  36#include "oslib/wimp.h"
  37#include "oslib/wimpextend.h"
  38#include "oslib/wimpspriteop.h"
  39#include "content/content.h"
  40#include "desktop/gui.h"
  41#include "riscos/dialog.h"
  42#include "riscos/gui.h"
  43#include "riscos/menus.h"
  44#include "riscos/options.h"
  45#include "riscos/theme.h"
  46#include "riscos/treeview.h"
  47#include "riscos/wimp.h"
  48#include "riscos/wimp_event.h"
  49#include "riscos/wimputils.h"
  50#include "utils/log.h"
  51#include "utils/utils.h"
  52
  53#define THEME_URL_MEMORY 256
  54#define THEME_THROBBER_MEMORY 12
  55
  56static struct theme_descriptor *theme_current = NULL;
  57static struct theme_descriptor *theme_descriptors = NULL;
  58static struct toolbar *theme_toolbar_drag = NULL;
  59static struct toolbar_icon *theme_toolbar_icon_drag = NULL;
  60static bool theme_toolbar_editor_drag = false;
  61
  62/* these order of the icons must match the numbers defined in riscos/gui.h */
  63static const char * theme_browser_icons[] = {"back", "forward", "stop",
  64		"reload", "home", "history", "save", "print", "hotlist",
  65		"scale", "search", "up", NULL};
  66static const char * theme_hotlist_icons[] = {"delete", "expand", "open",
  67		"launch", "create", NULL};
  68static const char * theme_history_icons[] = {"delete", "expand", "open",
  69		"launch", NULL};
  70static const char * theme_cookies_icons[] = {"delete", "expand", "open",
  71		NULL};
  72
  73static bool ro_gui_theme_add_descriptor(const char *folder, const char *leafname);
  74static void ro_gui_theme_redraw(wimp_draw *redraw);
  75static void ro_gui_theme_get_available_in_dir(const char *directory);
  76static void ro_gui_theme_free(struct theme_descriptor *descriptor);
  77static struct toolbar_icon *ro_gui_theme_add_toolbar_icon(
  78		struct toolbar *toolbar, const char *name, int icon_number);
  79static void ro_gui_theme_update_toolbar_icon(struct toolbar *toolbar,
  80		struct toolbar_icon *icon);
  81static void ro_gui_theme_destroy_toolbar_icon(struct toolbar_icon *icon);
  82static void ro_gui_theme_link_toolbar_icon(struct toolbar *toolbar,
  83		struct toolbar_icon *icon, struct toolbar_icon *link,
  84		bool before);
  85static void ro_gui_theme_delink_toolbar_icon(struct toolbar *toolbar,
  86		struct toolbar_icon *icon);
  87static struct toolbar_icon *ro_gui_theme_toolbar_get_insert_icon(
  88		struct toolbar *toolbar, int x, int y, bool *before);
  89static void ro_gui_theme_add_toolbar_icons(struct toolbar *toolbar,
  90		const char* icons[], const char* ident);
  91static void ro_gui_theme_set_help_prefix(struct toolbar *toolbar);
  92
  93/*	A basic window for the toolbar and status
  94*/
  95static wimp_window theme_toolbar_window = {
  96	{0, 0, 1, 1},
  97	0,
  98	0,
  99	wimp_TOP,
 100	wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS |
 101			wimp_WINDOW_FURNITURE_WINDOW |
 102			wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT,
 103	wimp_COLOUR_BLACK,
 104	wimp_COLOUR_LIGHT_GREY,
 105	wimp_COLOUR_LIGHT_GREY,
 106	wimp_COLOUR_VERY_LIGHT_GREY,
 107	wimp_COLOUR_DARK_GREY,
 108	wimp_COLOUR_MID_LIGHT_GREY,
 109	wimp_COLOUR_CREAM,
 110	wimp_WINDOW_NEVER3D | 0x16u /* RISC OS 5.03+ */,
 111	{0, 0, 16384, 16384},
 112	0,
 113	0,
 114	wimpspriteop_AREA,
 115	1,
 116	1,
 117	{""},
 118	0,
 119	{ }
 120};
 121
 122
 123/*	Shared icon validation
 124*/
 125static char theme_url_validation[] = "Pptr_write;KN";
 126static char theme_null_text_string[] = "";
 127static char theme_separator_name[] = "separator";
 128static char theme_favicon_sprite[12];
 129
 130
 131/**
 132 * Initialise the theme handler
 133 */
 134void ro_gui_theme_initialise(void)
 135{
 136	struct theme_descriptor *descriptor;
 137
 138	theme_descriptors = ro_gui_theme_get_available();
 139	descriptor = ro_gui_theme_find(option_theme);
 140	if (!descriptor)
 141		descriptor = ro_gui_theme_find("Aletheia");
 142	ro_gui_theme_apply(descriptor);
 143}
 144
 145
 146/**
 147 * Finalise the theme handler
 148 */
 149void ro_gui_theme_finalise(void)
 150{
 151	ro_gui_theme_close(theme_current, false);
 152	ro_gui_theme_free(theme_descriptors);
 153}
 154
 155
 156/**
 157 * Finds a theme from the cached values.
 158 *
 159 * The returned theme is only guaranteed to be valid until the next call
 160 * to ro_gui_theme_get_available() unless it has been opened using
 161 * ro_gui_theme_open().
 162 *
 163 * \param leafname  the filename of the theme_descriptor to return
 164 * \return the requested theme_descriptor, or NULL if not found
 165 */
 166struct theme_descriptor *ro_gui_theme_find(const char *leafname)
 167{
 168	struct theme_descriptor *descriptor;
 169
 170	if (!leafname)
 171		return NULL;
 172
 173	for (descriptor = theme_descriptors; descriptor;
 174			descriptor = descriptor->next)
 175		if (!strcmp(leafname, descriptor->leafname))
 176			return descriptor;
 177	/* fallback for 10 chars on old filesystems */
 178	for (descriptor = theme_descriptors; descriptor;
 179			descriptor = descriptor->next)
 180		if (!strncmp(leafname, descriptor->leafname, 10))
 181			return descriptor;
 182	return NULL;
 183}
 184
 185
 186/**
 187 * Reads and caches the currently available themes.
 188 *
 189 * \return the requested theme_descriptor, or NULL if not found
 190 */
 191struct theme_descriptor *ro_gui_theme_get_available(void)
 192{
 193	struct theme_descriptor *current;
 194	struct theme_descriptor *test;
 195
 196	/* close any unused descriptors */
 197	ro_gui_theme_free(theme_descriptors);
 198
 199	/* add our default 'Aletheia' theme */
 200	ro_gui_theme_add_descriptor("NetSurf:Resources", "Aletheia");
 201
 202	/* scan our choices directory */
 203	ro_gui_theme_get_available_in_dir(option_theme_path);
 204
 205	/* sort alphabetically in a very rubbish way */
 206	if ((theme_descriptors) && (theme_descriptors->next)) {
 207		current = theme_descriptors;
 208		while ((test = current->next)) {
 209			if (strcmp(current->name, test->name) > 0) {
 210				current->next->previous = current->previous;
 211				if (current->previous)
 212					current->previous->next = current->next;
 213				current->next = test->next;
 214				test->next = current;
 215				current->previous = test;
 216				if (current->next)
 217					current->next->previous = current;
 218
 219				current = test->previous;
 220				if (!current) current = test;
 221			} else {
 222				current = current->next;
 223			}
 224		}
 225		while (theme_descriptors->previous)
 226			theme_descriptors = theme_descriptors->previous;
 227	}
 228
 229	return theme_descriptors;
 230}
 231
 232
 233/**
 234 * Adds the themes in a directory to the global cache.
 235 *
 236 * \param directory  the directory to scan
 237 */
 238static void ro_gui_theme_get_available_in_dir(const char *directory)
 239{
 240	int context = 0;
 241	int read_count;
 242	osgbpb_INFO(100) info;
 243	os_error *error;
 244
 245	while (context != -1) {
 246	  	/* read some directory info */
 247		error = xosgbpb_dir_entries_info(directory,
 248				(osgbpb_info_list *) &info, 1, context,
 249				sizeof(info), 0, &read_count, &context);
 250		if (error) {
 251			LOG(("xosgbpb_dir_entries_info: 0x%x: %s",
 252				error->errnum, error->errmess));
 253			if (error->errnum == 0xd6)	/* no such dir */
 254				return;
 255			warn_user("MiscError", error->errmess);
 256			break;
 257		}
 258
 259		/* only process files */
 260		if ((read_count != 0) && (info.obj_type == fileswitch_IS_FILE))
 261			ro_gui_theme_add_descriptor(directory, info.name);
 262	}
 263}
 264
 265
 266/**
 267 * Checks a theme is valid and adds it to the current list
 268 *
 269 * \param folder	the theme folder
 270 * \param leafname	the theme leafname
 271 * \return whether the theme was added
 272 */
 273bool ro_gui_theme_add_descriptor(const char *folder, const char *leafname)
 274{
 275	struct theme_file_header file_header;
 276	struct theme_descriptor *current;
 277	struct theme_descriptor *test;
 278	int output_left;
 279	os_fw file_handle;
 280	os_error *error;
 281	char *filename;
 282
 283	/* create a full filename */
 284	filename = malloc(strlen(folder) + strlen(leafname) + 2);
 285	if (!filename) {
 286	  	LOG(("No memory for malloc"));
 287	  	warn_user("NoMemory", 0);
 288	  	return false;
 289	}
 290	sprintf(filename, "%s.%s", folder, leafname);
 291
 292	/* get the header */
 293	error = xosfind_openinw(osfind_NO_PATH, filename, 0,
 294			&file_handle);
 295	if (error) {
 296		LOG(("xosfind_openinw: 0x%x: %s",
 297			error->errnum, error->errmess));
 298		warn_user("FileError", error->errmess);
 299		free(filename);
 300		return false;
 301	}
 302	if (file_handle == 0) {
 303		free(filename);
 304		return false;
 305	}
 306	error = xosgbpb_read_atw(file_handle,
 307			(byte *) &file_header,
 308			sizeof (struct theme_file_header),
 309			0, &output_left);
 310	xosfind_closew(file_handle);
 311	if (error) {
 312		LOG(("xosbgpb_read_atw: 0x%x: %s",
 313			error->errnum, error->errmess));
 314		warn_user("FileError", error->errmess);
 315		free(filename);
 316		return false;
 317	}
 318	if (output_left > 0) {	/* should try to read more? */
 319	  	free(filename);
 320	  	return false;
 321	}
 322
 323	/* create a new theme descriptor */
 324	current = (struct theme_descriptor *)calloc(1,
 325			sizeof(struct theme_descriptor));
 326	if (!current) {
 327		LOG(("calloc failed"));
 328		warn_user("NoMemory", 0);
 329		free(filename);
 330		return false;
 331	}
 332	if (!ro_gui_theme_read_file_header(current, &file_header)) {
 333		free(filename);
 334		free(current);
 335		return false;
 336	}
 337	current->filename = filename;
 338	current->leafname = current->filename + strlen(folder) + 1;
 339
 340	/* don't add duplicates */
 341	for (test = theme_descriptors; test; test = test->next) {
 342		if (!strcmp(current->name, test->name)) {
 343			free(current->filename);
 344			free(current);
 345			return false;
 346		}
 347	}
 348
 349	/* link in our new descriptor at the head*/
 350	if (theme_descriptors) {
 351		current->next = theme_descriptors;
 352		theme_descriptors->previous = current;
 353	}
 354	theme_descriptors = current;
 355	return true;
 356
 357}
 358
 359
 360/**
 361 * Fills in the basic details for a descriptor from a file header.
 362 * The filename string is not set.
 363 *
 364 * \param descriptor   the descriptor to set up
 365 * \param file_header  the header to read from
 366 * \return false for a badly formed theme, true otherwise
 367 */
 368bool ro_gui_theme_read_file_header(struct theme_descriptor *descriptor,
 369		struct theme_file_header *file_header)
 370{
 371	if ((file_header->magic_value != 0x4d54534e) ||
 372			(file_header->parser_version > 2))
 373		return false;
 374
 375	strcpy(descriptor->name, file_header->name);
 376	strcpy(descriptor->author, file_header->author);
 377	descriptor->browser_background = file_header->browser_bg;
 378	descriptor->hotlist_background = file_header->hotlist_bg;
 379	descriptor->status_background = file_header->status_bg;
 380	descriptor->status_foreground = file_header->status_fg;
 381	descriptor->decompressed_size = file_header->decompressed_sprite_size;
 382	descriptor->compressed_size = file_header->compressed_sprite_size;
 383	if (file_header->parser_version >= 2) {
 384		descriptor->throbber_right =
 385				!(file_header->theme_flags & (1 << 0));
 386		descriptor->throbber_redraw =
 387				file_header->theme_flags & (1 << 1);
 388	} else {
 389		descriptor->throbber_right =
 390				(file_header->theme_flags == 0x00);
 391		descriptor->throbber_redraw = true;
 392	}
 393	return true;
 394}
 395
 396
 397/**
 398 * Opens a theme ready for use.
 399 *
 400 * \param descriptor  the theme_descriptor to open
 401 * \param list	      whether to open all themes in the list
 402 * \return whether the operation was successful
 403 */
 404bool ro_gui_theme_open(struct theme_descriptor *descriptor, bool list)
 405{
 406	fileswitch_object_type obj_type;
 407	squash_output_status status;
 408	os_coord dimensions;
 409	os_mode mode;
 410	os_error *error;
 411	struct theme_descriptor *next_descriptor;
 412	char sprite_name[16];
 413	const char *name = sprite_name;
 414	bool result = true;
 415	int i, n;
 416	int workspace_size, file_size;
 417	char *raw_data, *workspace;
 418	osspriteop_area *decompressed;
 419
 420	/*	If we are freeing the whole of the list then we need to
 421		start at the first descriptor.
 422	*/
 423	if (list && descriptor)
 424		while (descriptor->previous) descriptor = descriptor->previous;
 425
 426	/*	Open the themes
 427	*/
 428	for (; descriptor; descriptor = next_descriptor) {
 429		/* see if we should iterate through the entire list */
 430		if (list)
 431			next_descriptor = descriptor->next;
 432		else
 433			next_descriptor = NULL;
 434
 435		/* if we are already loaded, increase the usage count */
 436		if (descriptor->theme) {
 437			descriptor->theme->users = descriptor->theme->users + 1;
 438			continue;
 439		}
 440
 441		/* create a new theme */
 442		descriptor->theme = (struct theme *)calloc(1,
 443				sizeof(struct theme));
 444		if (!descriptor->theme) {
 445			LOG(("calloc() failed"));
 446			warn_user("NoMemory", 0);
 447			continue;
 448		}
 449		descriptor->theme->users = 1;
 450
 451		/* try to load the associated file */
 452		error = xosfile_read_stamped_no_path(descriptor->filename,
 453				&obj_type, 0, 0, &file_size, 0, 0);
 454		if (error) {
 455			LOG(("xosfile_read_stamped_no_path: 0x%x: %s",
 456					error->errnum, error->errmess));
 457			warn_user("FileError", error->errmess);
 458			continue;
 459		}
 460		if (obj_type != fileswitch_IS_FILE)
 461			continue;
 462		raw_data = malloc(file_size);
 463		if (!raw_data) {
 464			LOG(("malloc() failed"));
 465			warn_user("NoMemory", 0);
 466			continue;
 467		}
 468		error = xosfile_load_stamped_no_path(descriptor->filename,
 469				(byte *)raw_data, 0, 0, 0, 0, 0);
 470		if (error) {
 471			free(raw_data);
 472			LOG(("xosfile_load_stamped_no_path: 0x%x: %s",
 473					error->errnum, error->errmess));
 474			warn_user("FileError", error->errmess);
 475			continue;
 476		}
 477
 478		/* decompress the new data */
 479		error = xsquash_decompress_return_sizes(-1, &workspace_size, 0);
 480		if (error) {
 481			free(raw_data);
 482			LOG(("xsquash_decompress_return_sizes: 0x%x: %s",
 483					error->errnum, error->errmess));
 484			warn_user("MiscError", error->errmess);
 485			continue;
 486		}
 487		decompressed = (osspriteop_area *)malloc(
 488				descriptor->decompressed_size);
 489		workspace = malloc(workspace_size);
 490		if ((!decompressed) || (!workspace)) {
 491			free(decompressed);
 492			free(raw_data);
 493			LOG(("malloc() failed"));
 494			warn_user("NoMemory", 0);
 495			continue;
 496		}
 497		error = xsquash_decompress(squash_INPUT_ALL_PRESENT, workspace,
 498				(byte *)(raw_data + sizeof(
 499						struct theme_file_header)),
 500				descriptor->compressed_size,
 501				(byte *)decompressed,
 502				descriptor->decompressed_size,
 503				&status, 0, 0, 0, 0);
 504		free(workspace);
 505		free(raw_data);
 506		if (error) {
 507			free(decompressed);
 508			LOG(("xsquash_decompress: 0x%x: %s",
 509					error->errnum, error->errmess));
 510			warn_user("MiscError", error->errmess);
 511			continue;
 512		}
 513		if (status != 0) {
 514			free(decompressed);
 515			continue;
 516		}
 517		descriptor->theme->sprite_area = decompressed;
 518
 519		/* find the highest sprite called 'throbber%i', and get the
 520		 * maximum dimensions for all 'thobber%i' icons. */
 521		for (i = 1; i <= descriptor->theme->sprite_area->sprite_count;
 522				i++) {
 523			error = xosspriteop_return_name(osspriteop_USER_AREA,
 524					descriptor->theme->sprite_area,
 525					sprite_name, 16, i, 0);
 526			if (error) {
 527				LOG(("xosspriteop_return_name: 0x%x: %s",
 528						error->errnum, error->errmess));
 529				warn_user("MiscError", error->errmess);
 530				continue;
 531			}
 532			if (strncmp(sprite_name, "throbber", 8))
 533				continue;
 534
 535			/* get the max sprite width/height */
 536			error = xosspriteop_read_sprite_info(
 537					osspriteop_USER_AREA,
 538					descriptor->theme->sprite_area,
 539					(osspriteop_id) name,
 540					&dimensions.x, &dimensions.y,
 541					(osbool *) 0, &mode);
 542			if (error) {
 543				LOG(("xosspriteop_read_sprite_info: 0x%x: %s",
 544						error->errnum, error->errmess));
 545				warn_user("MiscError", error->errmess);
 546				continue;
 547			}
 548			ro_convert_pixels_to_os_units(&dimensions, mode);
 549			if (descriptor->theme->throbber_width <	dimensions.x)
 550				descriptor->theme->throbber_width =
 551						dimensions.x;
 552			if (descriptor->theme->throbber_height < dimensions.y)
 553				descriptor->theme->throbber_height =
 554						dimensions.y;
 555
 556			/* get the throbber number */
 557			n = atoi(sprite_name + 8);
 558			if (descriptor->theme->throbber_frames < n)
 559				descriptor->theme->throbber_frames = n;
 560		}
 561	}
 562	return result;
 563}
 564
 565
 566/**
 567 * Applies the theme to all current windows and subsequent ones.
 568 *
 569 * \param descriptor  the theme_descriptor to open
 570 * \return whether the operation was successful
 571 */
 572bool ro_gui_theme_apply(struct theme_descriptor *descriptor)
 573{
 574	struct theme_descriptor *theme_previous;
 575
 576	/* check if the theme is already applied */
 577	if (descriptor == theme_current)
 578		return true;
 579
 580	/* re-open the new-theme and release the current theme */
 581	if (!ro_gui_theme_open(descriptor, false))
 582		return false;
 583	theme_previous = theme_current;
 584	theme_current = descriptor;
 585
 586	/* apply the theme to all the current windows */
 587	ro_gui_window_update_theme();
 588	ro_gui_tree_update_theme(hotlist_tree);
 589	ro_gui_tree_update_theme(global_history_tree);
 590	ro_gui_tree_update_theme(cookies_tree);
 591	ro_gui_theme_close(theme_previous, false);
 592	return true;
 593}
 594
 595
 596/**
 597 * Closes a theme after use.
 598 *
 599 * \param descriptor  the theme_descriptor to close
 600 * \param list	      whether to open all themes in the list
 601 * \return whether the operation was successful
 602 */
 603void ro_gui_theme_close(struct theme_descriptor *descriptor, bool list)
 604{
 605
 606	if (!descriptor)
 607		return;
 608
 609	/* move to the start of the list */
 610	while (list && descriptor->previous)
 611		descriptor = descriptor->previous;
 612
 613	/* close the themes */
 614	while (descriptor) {
 615		if (descriptor->theme) {
 616			descriptor->theme->users = descriptor->theme->users - 1;
 617			if (descriptor->theme->users <= 0) {
 618				free(descriptor->theme->sprite_area);
 619				free(descriptor->theme);
 620				descriptor->theme = NULL;
 621			}
 622		}
 623		if (!list)
 624			return;
 625		descriptor = descriptor->next;
 626	}
 627}
 628
 629
 630/**
 631 * Performs the redraw for a toolbar
 632 *
 633 * \param redraw   the redraw area
 634 * \param toolbar  the toolbar to redraw
 635 */
 636void ro_gui_theme_redraw(wimp_draw *redraw)
 637{
 638	struct toolbar *toolbar;
 639	struct gui_window *g;
 640
 641	struct toolbar_icon *icon;
 642	osbool more;
 643	wimp_icon separator_icon;
 644	os_error *error;
 645	bool perform_redraw = false;
 646
 647	toolbar = (struct toolbar *)ro_gui_wimp_event_get_user_data(redraw->w);
 648
 649	assert(toolbar);
 650
 651	/* set the content-type icon */
 652	g = ro_gui_toolbar_lookup(toolbar->toolbar_handle);
 653
 654	/* only set type for browser windows */
 655	sprintf(theme_favicon_sprite, "Ssmall_xxx");
 656	if (g) {
 657	  	assert(toolbar->type == THEME_BROWSER_TOOLBAR);
 658		assert(g->bw);
 659		if (g->bw->current_content) {
 660			sprintf(theme_favicon_sprite, "Ssmall_%.3x",
 661					ro_content_filetype_from_type(
 662					content_get_type(g->bw->current_content)));
 663			if (!ro_gui_wimp_sprite_exists(theme_favicon_sprite + 1))
 664				sprintf(theme_favicon_sprite, "Ssmall_xxx");
 665		}
 666	}
 667
 668	/* set up the icon */
 669	if ((toolbar->descriptor) && (toolbar->descriptor->theme) &&
 670			(toolbar->descriptor->theme->sprite_area)) {
 671		const char *name = theme_separator_name;
 672
 673		separator_icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED |
 674				wimp_ICON_HCENTRED | wimp_ICON_VCENTRED;
 675		separator_icon.data.indirected_sprite.id = (osspriteop_id) name;
 676		separator_icon.data.indirected_sprite.area =
 677				toolbar->descriptor->theme->sprite_area;
 678		separator_icon.data.indirected_sprite.size = 12;
 679		separator_icon.extent.y0 = 0;
 680		separator_icon.extent.y1 = toolbar->height;
 681		perform_redraw = true;
 682	}
 683	perform_redraw &= toolbar->display_buttons || toolbar->editor;
 684	if ((toolbar->editor) && (toolbar->editor->toolbar_handle == redraw->w))
 685		toolbar = toolbar->editor;
 686
 687	error = xwimp_redraw_window(redraw, &more);
 688	if (error) {
 689		LOG(("xwimp_redraw_window: 0x%x: %s",
 690				error->errnum, error->errmess));
 691		warn_user("WimpError", error->errmess);
 692		return;
 693	}
 694	while (more) {
 695		if (perform_redraw)
 696			for (icon = toolbar->icon; icon; icon = icon->next)
 697				if ((icon->icon_number == -1) &&
 698						(icon->display))  {
 699					separator_icon.extent.x0 = icon->x;
 700					separator_icon.extent.x1 = icon->x +
 701							icon->width;
 702					xwimp_plot_icon(&separator_icon);
 703				}
 704		error = xwimp_get_rectangle(redraw, &more);
 705		if (error) {
 706			LOG(("xwimp_get_rectangle: 0x%x: %s",
 707					error->errnum, error->errmess));
 708			warn_user("WimpError", error->errmess);
 709			return;
 710		}
 711	}
 712}
 713
 714
 715/**
 716 * Frees any unused theme descriptors.
 717 *
 718 * \param descriptor  the theme_descriptor to free
 719 * \param list	      whether to open all themes in the list
 720 * \return whether the operation was successful
 721 */
 722void ro_gui_theme_free(struct theme_descriptor *descriptor)
 723{
 724	struct theme_descriptor *next_descriptor;
 725
 726	if (!descriptor)
 727		return;
 728
 729	/* move to the start of the list */
 730	while (descriptor->previous)
 731		descriptor = descriptor->previous;
 732
 733	/* free closed themes */
 734	for (; descriptor; descriptor = next_descriptor) {
 735		next_descriptor = descriptor->next;
 736
 737		/* no theme? no descriptor */
 738		if (!descriptor->theme) {
 739			if (descriptor->previous)
 740				descriptor->previous->next = descriptor->next;
 741			if (descriptor->next)
 742				descriptor->next->previous =
 743						descriptor->previous;
 744
 745			/* keep the cached list in sync */
 746			if (theme_descriptors == descriptor)
 747				theme_descriptors = next_descriptor;
 748
 749			/* release any memory */
 750			free(descriptor->filename);
 751			free(descriptor);
 752		}
 753	}
 754}
 755
 756
 757/**
 758 * Creates a toolbar.
 759 *
 760 * \param descriptor  the theme to use, or NULL for current
 761 * \param type	      the toolbar type
 762 * \return a new toolbar, or NULL for failure
 763 */
 764struct toolbar *ro_gui_theme_create_toolbar(struct theme_descriptor *descriptor,
 765		toolbar_type type)
 766{
 767	struct toolbar *toolbar;
 768
 769	/*	Create a new toolbar
 770	*/
 771	toolbar = calloc(sizeof(struct toolbar), 1);
 772	if (!toolbar) {
 773		LOG(("No memory for malloc()"));
 774		warn_user("NoMemory", 0);
 775		return NULL;
 776	}
 777	toolbar->type = type;
 778
 779	/*	Store the theme
 780	*/
 781	if (!descriptor) descriptor = theme_current;
 782	toolbar->descriptor = descriptor;
 783
 784	/*	Apply the default settings
 785	*/
 786	toolbar->display_buttons = true;
 787	toolbar->toolbar_current = 16384;
 788	switch (type) {
 789		case THEME_BROWSER_TOOLBAR:
 790			toolbar->display_url = true;
 791			toolbar->display_throbber = true;
 792			ro_gui_theme_add_toolbar_icons(toolbar,
 793					theme_browser_icons,
 794					option_toolbar_browser);
 795			toolbar->suggest = ro_gui_theme_add_toolbar_icon(NULL,
 796			"gright",
 797					ICON_TOOLBAR_SUGGEST);
 798			break;
 799		case THEME_HOTLIST_TOOLBAR:
 800			ro_gui_theme_add_toolbar_icons(toolbar,
 801					theme_hotlist_icons,
 802					option_toolbar_hotlist);
 803			break;
 804		case THEME_HISTORY_TOOLBAR:
 805			ro_gui_theme_add_toolbar_icons(toolbar,
 806					theme_history_icons,
 807					option_toolbar_history);
 808			break;
 809		case THEME_COOKIES_TOOLBAR:
 810			ro_gui_theme_add_toolbar_icons(toolbar,
 811					theme_cookies_icons,
 812					option_toolbar_cookies);
 813			break;
 814		case THEME_BROWSER_EDIT_TOOLBAR:
 815			ro_gui_theme_add_toolbar_icons(toolbar,
 816					theme_browser_icons,
 817					"0123456789ab|");
 818			break;
 819		case THEME_HOTLIST_EDIT_TOOLBAR:
 820			ro_gui_theme_add_toolbar_icons(toolbar,
 821					theme_hotlist_icons,
 822					"40123|");
 823			break;
 824		case THEME_HISTORY_EDIT_TOOLBAR:
 825			ro_gui_theme_add_toolbar_icons(toolbar,
 826					theme_history_icons,
 827					"0123|");
 828			break;
 829		case THEME_COOKIES_EDIT_TOOLBAR:
 830			ro_gui_theme_add_toolbar_icons(toolbar,
 831					theme_cookies_icons,
 832					"012|");
 833			break;
 834	}
 835
 836	/*	Claim the memory for our Wimp indirection
 837	*/
 838	if (type == THEME_BROWSER_TOOLBAR) {
 839		toolbar->url_buffer = calloc(1, THEME_URL_MEMORY +
 840				THEME_THROBBER_MEMORY);
 841		if (!toolbar->url_buffer) {
 842			LOG(("No memory for calloc()"));
 843			ro_gui_theme_destroy_toolbar(toolbar);
 844			return NULL;
 845		}
 846		toolbar->throbber_buffer = toolbar->url_buffer +
 847				THEME_URL_MEMORY;
 848		sprintf(toolbar->throbber_buffer, "throbber0");
 849	}
 850
 851	/*	Apply the desired theme to the toolbar
 852	*/
 853	if (!ro_gui_theme_update_toolbar(descriptor, toolbar)) {
 854		ro_gui_theme_destroy_toolbar(toolbar);
 855		return NULL;
 856	}
 857	toolbar->old_height = ro_gui_theme_toolbar_full_height(toolbar);
 858	return toolbar;
 859}
 860
 861
 862/**
 863 * Updates a toolbar to use a particular theme.
 864 * The toolbar may be unstable on failure and should be destroyed.
 865 *
 866 * \param descriptor  the theme to use, or NULL for current
 867 * \param toolbar     the toolbar to update
 868 * \return whether the operation was successful
 869 */
 870bool ro_gui_theme_update_toolbar(struct theme_descriptor *descriptor,
 871		struct toolbar *toolbar)
 872{
 873	wimp_icon_create new_icon;
 874	os_error *error;
 875	osspriteop_area *sprite_area;
 876	struct toolbar_icon *toolbar_icon;
 877	int width, max_icon;
 878	wimp_icon_flags icon_flags;
 879	struct gui_window *g;
 880	if (!toolbar) return false;
 881
 882	/*	Set the theme and window sprite area
 883	*/
 884	if (!descriptor) descriptor = theme_current;
 885	toolbar->descriptor = descriptor;
 886	if ((toolbar->descriptor) && (toolbar->descriptor->theme))
 887		sprite_area = toolbar->descriptor->theme->sprite_area;
 888	else
 889		sprite_area = (osspriteop_area *)1;
 890	theme_toolbar_window.sprite_area = sprite_area;
 891
 892	/*	Update the icon sizes
 893	*/
 894	for (toolbar_icon = toolbar->icon; toolbar_icon;
 895			toolbar_icon = toolbar_icon->next)
 896		ro_gui_theme_update_toolbar_icon(toolbar, toolbar_icon);
 897	if (toolbar->suggest)
 898		ro_gui_theme_update_toolbar_icon(toolbar, toolbar->suggest);
 899
 900	/*	Recreate the toolbar window
 901	*/
 902	if (toolbar->descriptor) {
 903		if (toolbar->type == THEME_BROWSER_TOOLBAR)
 904			theme_toolbar_window.work_bg =
 905					toolbar->descriptor->browser_background;
 906		else
 907			theme_toolbar_window.work_bg =
 908					toolbar->descriptor->hotlist_background;
 909	} else {
 910		theme_toolbar_window.work_bg = wimp_COLOUR_VERY_LIGHT_GREY;
 911	}
 912
 913	theme_toolbar_window.work_flags &= ~wimp_ICON_BUTTON_TYPE;
 914	if ((toolbar->editor) ||
 915			(toolbar->type == THEME_HOTLIST_EDIT_TOOLBAR) ||
 916			(toolbar->type == THEME_HISTORY_EDIT_TOOLBAR) ||
 917			(toolbar->type == THEME_BROWSER_EDIT_TOOLBAR) ||
 918			(toolbar->type == THEME_COOKIES_EDIT_TOOLBAR))
 919		theme_toolbar_window.work_flags |= (wimp_BUTTON_CLICK_DRAG <<
 920				wimp_ICON_BUTTON_TYPE_SHIFT);
 921	theme_toolbar_window.sprite_area = sprite_area;
 922	if (toolbar->toolbar_handle) {
 923		error = xwimp_delete_window(toolbar->toolbar_handle);
 924		if (error)
 925			LOG(("xwimp_delete_window: 0x%x: %s",
 926					error->errnum, error->errmess));
 927		ro_gui_wimp_event_finalise(toolbar->toolbar_handle);
 928		toolbar->toolbar_handle = NULL;
 929	}
 930	error = xwimp_create_window(&theme_toolbar_window,
 931			&toolbar->toolbar_handle);
 932	if (error) {
 933		LOG(("xwimp_create_window: 0x%x: %s",
 934				error->errnum, error->errmess));
 935		warn_user("WimpError", error->errmess);
 936		return false;
 937	}
 938	ro_gui_wimp_event_register_redraw_window(toolbar->toolbar_handle,
 939			ro_gui_theme_redraw);
 940	ro_gui_wimp_event_set_user_data(toolbar->toolbar_handle, toolbar);
 941	switch (toolbar->type) {
 942	 	case THEME_BROWSER_TOOLBAR:
 943	 	case THEME_BROWSER_EDIT_TOOLBAR:
 944	 		ro_gui_wimp_event_register_mouse_click(toolbar->toolbar_handle,
 945	 				ro_gui_toolbar_click);
 946	 		break;
 947		case THEME_HOTLIST_TOOLBAR:
 948		case THEME_HOTLIST_EDIT_TOOLBAR:
 949		case THEME_HISTORY_TOOLBAR:
 950	  	case THEME_HISTORY_EDIT_TOOLBAR:
 951		case THEME_COOKIES_TOOLBAR:
 952	  	case THEME_COOKIES_EDIT_TOOLBAR:
 953			ro_gui_wimp_event_register_mouse_click(toolbar->toolbar_handle,
 954					ro_gui_tree_toolbar_click);
 955			break;
 956	}
 957
 958	/*	Create the basic icons
 959	*/
 960	if ((toolbar->type == THEME_HOTLIST_TOOLBAR) ||
 961			(toolbar->type == THEME_HOTLIST_EDIT_TOOLBAR))
 962		max_icon = ICON_TOOLBAR_HOTLIST_LAST;
 963	else if ((toolbar->type == THEME_HISTORY_TOOLBAR) ||
 964			(toolbar->type == THEME_HISTORY_EDIT_TOOLBAR))
 965		max_icon = ICON_TOOLBAR_HISTORY_LAST;
 966	else if ((toolbar->type == THEME_COOKIES_TOOLBAR) ||
 967			(toolbar->type == THEME_COOKIES_EDIT_TOOLBAR))
 968		max_icon = ICON_TOOLBAR_COOKIES_LAST;
 969	else
 970		max_icon = ICON_TOOLBAR_LAST;
 971	new_icon.w = toolbar->toolbar_handle;
 972	new_icon.icon.data.indirected_text.size = 1;
 973	new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE |
 974			wimp_ICON_INDIRECTED | wimp_ICON_HCENTRED |
 975			wimp_ICON_VCENTRED;
 976	if ((toolbar->editor) ||
 977			(toolbar->type == THEME_HOTLIST_EDIT_TOOLBAR) ||
 978			(toolbar->type == THEME_HISTORY_EDIT_TOOLBAR) ||
 979			(toolbar->type == THEME_COOKIES_EDIT_TOOLBAR) ||
 980			(toolbar->type == THEME_BROWSER_EDIT_TOOLBAR))
 981		new_icon.icon.flags |= (wimp_BUTTON_CLICK_DRAG <<
 982				wimp_ICON_BUTTON_TYPE_SHIFT);
 983	else
 984		new_icon.icon.flags |= (wimp_BUTTON_CLICK <<
 985				wimp_ICON_BUTTON_TYPE_SHIFT);
 986	if (toolbar->descriptor)
 987		new_icon.icon.flags |= (toolbar->descriptor->browser_background
 988				 << wimp_ICON_BG_COLOUR_SHIFT);
 989	else
 990		new_icon.icon.flags |= (wimp_COLOUR_VERY_LIGHT_GREY
 991				 << wimp_ICON_BG_COLOUR_SHIFT);
 992	icon_flags = new_icon.icon.flags;
 993
 994	for (int i = 0; i < max_icon; i++) {
 995		new_icon.icon.data.indirected_text.text =
 996				theme_null_text_string;
 997		new_icon.icon.data.indirected_text.validation =
 998				theme_null_text_string;
 999		toolbar_icon = toolbar->icon;
1000		while (toolbar_icon) {
1001			if (toolbar_icon->icon_number == i) {
1002				new_icon.icon.data.indirected_text.validation =
1003					toolbar_icon->validation;
1004				break;
1005			} else {
1006				toolbar_icon = toolbar_icon->next;
1007			}
1008		}
1009		error = xwimp_create_icon(&new_icon, 0);
1010		if (error) {
1011			LOG(("xwimp_create_icon: 0x%x: %s",
1012					error->errnum, error->errmess));
1013			warn_user("WimpError", error->errmess);
1014			return false;
1015		}
1016	}
1017
1018	/*	Create the URL/throbber icons
1019	*/
1020	if (toolbar->type == THEME_BROWSER_TOOLBAR) {
1021	  	/* container for all URL bits (ie border) */
1022		new_icon.icon.flags = wimp_ICON_BORDER | (wimp_COLOUR_BLACK <<
1023						wimp_ICON_FG_COLOUR_SHIFT);
1024		error = xwimp_create_icon(&new_icon, 0);
1025		if (error) {
1026			LOG(("xwimp_create_icon: 0x%x: %s",
1027					error->errnum, error->errmess));
1028			warn_user("WimpError", error->errmess);
1029			return false;
1030		}
1031
1032		/* favicon image */
1033		new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE |
1034				wimp_ICON_INDIRECTED | wimp_ICON_FILLED |
1035				wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
1036				(wimp_BUTTON_CLICK_DRAG <<
1037						wimp_ICON_BUTTON_TYPE_SHIFT);
1038		new_icon.icon.data.indirected_text.text = theme_null_text_string;
1039		new_icon.icon.data.indirected_text.validation =
1040				theme_favicon_sprite;
1041		new_icon.icon.data.indirected_text.size = 1;
1042		error = xwimp_create_icon(&new_icon, 0);
1043		if (error) {
1044			LOG(("xwimp_create_icon: 0x%x: %s",
1045					error->errnum, error->errmess));
1046			warn_user("WimpError", error->errmess);
1047			return false;
1048		}
1049
1050		/* Writable text portion */
1051		new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
1052				wimp_ICON_VCENTRED |
1053				wimp_ICON_FILLED | (wimp_COLOUR_BLACK <<
1054						wimp_ICON_FG_COLOUR_SHIFT) |
1055				(wimp_BUTTON_WRITE_CLICK_DRAG <<
1056						wimp_ICON_BUTTON_TYPE_SHIFT);
1057		new_icon.icon.data.indirected_text.text = toolbar->url_buffer;
1058		new_icon.icon.data.indirected_text.validation =
1059				theme_url_validation;
1060		new_icon.icon.data.indirected_text.size = THEME_URL_MEMORY;
1061		error = xwimp_create_icon(&new_icon, 0);
1062		if (error) {
1063			LOG(("xwimp_create_icon: 0x%x: %s",
1064					error->errnum, error->errmess));
1065			warn_user("WimpError", error->errmess);
1066			return false;
1067		}
1068
1069		/*	Now the URL suggestion icon
1070		*/
1071		new_icon.icon.data.indirected_text.text =
1072				theme_null_text_string;
1073		new_icon.icon.data.indirected_text.size = 1;
1074		new_icon.icon.flags = icon_flags | (wimp_BUTTON_CLICK <<
1075				wimp_ICON_BUTTON_TYPE_SHIFT);
1076		if (toolbar->suggest)
1077			new_icon.icon.data.indirected_text.validation =
1078					toolbar->suggest->validation;
1079		else
1080			new_icon.icon.data.indirected_text.validation =
1081					theme_null_text_string;
1082		error = xwimp_create_icon(&new_icon, 0);
1083		if (error) {
1084			LOG(("xwimp_create_icon: 0x%x: %s",
1085					error->errnum, error->errmess));
1086			warn_user("WimpError", error->errmess);
1087			return false;
1088		}
1089
1090		/*	Now the throbber
1091		*/
1092		new_icon.icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED |
1093				wimp_ICON_HCENTRED | wimp_ICON_VCENTRED;
1094		new_icon.icon.data.indirected_sprite.id =
1095				(osspriteop_id)toolbar->throbber_buffer;
1096		new_icon.icon.data.indirected_sprite.area = sprite_area;
1097		new_icon.icon.data.indirected_sprite.size =
1098				THEME_THROBBER_MEMORY;
1099		error = xwimp_create_icon(&new_icon, 0);
1100		if (error) {
1101			LOG(("xwimp_create_icon: 0x%x: %s",
1102					error->errnum, error->errmess));
1103			warn_user("WimpError", error->errmess);
1104			return false;
1105		}
1106	}
1107	if (toolbar->parent_handle)
1108		ro_gui_theme_attach_toolbar(toolbar, toolbar->parent_handle);
1109
1110
1111	/*	Force a re-processing of the toolbar
1112	*/
1113	width = toolbar->toolbar_current;
1114	toolbar->reformat_buttons = true;
1115	toolbar->toolbar_current = -1;
1116	ro_gui_theme_process_toolbar(toolbar, width);
1117
1118	/*	Keep menus up to date etc
1119	*/
1120	ro_gui_theme_set_help_prefix(toolbar);
1121	switch (toolbar->type) {
1122		case THEME_BROWSER_TOOLBAR:
1123			g = ro_gui_window_lookup(toolbar->parent_handle);
1124			if (g)
1125				ro_gui_prepare_navigate(g);
1126			break;
1127		case THEME_HOTLIST_TOOLBAR:
1128		case THEME_HISTORY_TOOLBAR:
1129		case THEME_COOKIES_TOOLBAR:
1130			ro_gui_menu_prepare_action(toolbar->parent_handle,
1131					TREE_SELECTION, false);
1132			ro_gui_menu_prepare_action(toolbar->parent_handle,
1133					TREE_EXPAND_ALL, false);
1134			break;
1135		default:
1136			break;
1137	}
1138	return true;
1139}
1140
1141
1142/**
1143 * Attaches a toolbar to a window.
1144 *
1145 * \param toolbar     the toolbar to update
1146 * \param parent      the window to contain the toolbar
1147 * \return whether the operation was successful
1148 */
1149bool ro_gui_theme_attach_toolbar(struct toolbar *toolbar, wimp_w parent)
1150{
1151	wimp_outline outline;
1152	wimp_window_state state;
1153	int height;
1154	os_error *error;
1155
1156	if (!toolbar)
1157		return false;
1158
1159	/*	Attach/close the windows
1160	*/
1161	toolbar->parent_handle = parent;
1162	height = ro_gui_theme_toolbar_height(toolbar);
1163	if (height > 0) {
1164		outline.w = parent;
1165		xwimp_get_window_outline(&outline);
1166		state.w = parent;
1167		xwimp_get_window_state(&state);
1168		state.w = toolbar->toolbar_handle;
1169		state.visible.x1 = outline.outline.x1 - 2;
1170		state.visible.y0 = state.visible.y1 - height + 2;
1171		state.xscroll = 0;
1172		state.yscroll = toolbar->height - 2; /* clipped by the WIMP */
1173		error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), parent,
1174				wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
1175						<< wimp_CHILD_XORIGIN_SHIFT |
1176				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
1177						<< wimp_CHILD_YORIGIN_SHIFT |
1178				wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
1179						<< wimp_CHILD_LS_EDGE_SHIFT |
1180				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
1181						<< wimp_CHILD_BS_EDGE_SHIFT |
1182				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
1183						<< wimp_CHILD_RS_EDGE_SHIFT |
1184				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
1185						<< wimp_CHILD_TS_EDGE_SHIFT);
1186		if (error) {
1187			LOG(("xwimp_open_window_nested: 0x%x: %s",
1188					error->errnum, error->errmess));
1189			warn_user("WimpError", error->errmess);
1190			return false;
1191		}
1192		if (!toolbar->editor)
1193			return true;
1194
1195		state.w = toolbar->editor->toolbar_handle;
1196		state.visible.y1 -= toolbar->height;
1197		state.yscroll = toolbar->editor->height - 2;
1198		error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state),
1199				toolbar->toolbar_handle,
1200				wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
1201						<< wimp_CHILD_XORIGIN_SHIFT |
1202				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
1203						<< wimp_CHILD_YORIGIN_SHIFT |
1204				wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
1205						<< wimp_CHILD_LS_EDGE_SHIFT |
1206				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
1207						<< wimp_CHILD_BS_EDGE_SHIFT |
1208				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
1209						<< wimp_CHILD_RS_EDGE_SHIFT |
1210				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
1211						<< wimp_CHILD_TS_EDGE_SHIFT);
1212		if (error) {
1213			LOG(("xwimp_open_window_nested: 0x%x: %s",
1214					error->errnum, error->errmess));
1215			warn_user("WimpError", error->errmess);
1216			return false;
1217		}
1218		return true;
1219	}
1220
1221	error = xwimp_close_window(toolbar->toolbar_handle);
1222	if (error) {
1223		LOG(("xwimp_close_window: 0x%x: %s",
1224				error->errnum, error->errmess));
1225		warn_user("WimpError", error->errmess);
1226		return false;
1227	}
1228	return true;
1229}
1230
1231
1232/**
1233 * Updates the toolbar to reflect changes to the icon flags and any reformatting
1234 * required due to the change in parent window size.
1235 *
1236 * \param toolbar     the toolbar to update
1237 * \param width	      a specific width to resize to, or -1 to use parent width
1238 * \return whether the operation was successful
1239 */
1240bool ro_gui_theme_process_toolbar(struct toolbar *toolbar, int width)
1241{
1242	wimp_caret caret;
1243	os_box extent = { 0, 0, 0, 0 };
1244	os_error *error;
1245	wimp_outline outline;
1246	wimp_window_state state;
1247	int height = -1;
1248	int throbber_x = -1;
1249	int left_edge, right_edge, bottom_edge;
1250	int old_height;
1251	int old_width;
1252	struct toolbar_icon *toolbar_icon;
1253	bool visible_icon = false;
1254	int collapse_height;
1255	int xeig, yeig;
1256	os_coord pixel = {1, 1};
1257	int top, bottom, right;
1258
1259	if (!toolbar)
1260		return false;
1261
1262	old_height = toolbar->height;
1263	old_width = toolbar->toolbar_current;
1264
1265	/* calculate 1px in OS units */
1266	ro_convert_pixels_to_os_units(&pixel, (os_mode)-1);
1267	xeig = pixel.x;
1268	yeig = pixel.y;
1269
1270	/* find the parent window handle if we need to process the status
1271	 * window, or the caller has requested we calculate the width ourself */
1272	if ((toolbar->parent_handle) && (width == -1)) {
1273		outline.w = toolbar->parent_handle;
1274		error = xwimp_get_window_outline(&outline);
1275		if (error) {
1276			LOG(("xwimp_get_window_outline: 0x%x: %s",
1277				error->errnum, error->errmess));
1278			warn_user("WimpError", error->errmess);
1279			return false;
1280		}
1281		if (width == -1)
1282			width = outline.outline.x1 - outline.outline.x0 - 2;
1283	}
1284
1285	/*	Find the parent visible height to clip our toolbar height to
1286	*/
1287	if ((toolbar->toolbar_handle) && (toolbar->parent_handle)) {
1288		/*	Get the current state
1289		*/
1290		state.w = toolbar->parent_handle;
1291		error = xwimp_get_window_state(&state);
1292		if (error) {
1293			LOG(("xwimp_get_window_state: 0x%x: %s",
1294				error->errnum, error->errmess));
1295			warn_user("WimpError", error->errmess);
1296			return false;
1297		}
1298		height = state.visible.y1 - state.visible.y0 + 2;
1299
1300		/*	We can't obscure the height of the scroll bar as we
1301			lose the resize icon if we do.
1302		*/
1303		if ((state.flags & wimp_WINDOW_SIZE_ICON) &&
1304				!(state.flags & wimp_WINDOW_HSCROLL))
1305			height -= ro_get_hscroll_height(0) - 2;
1306
1307		/*	Update our position
1308		*/
1309		if (height != toolbar->max_height) {
1310			if ((state.flags & wimp_WINDOW_SIZE_ICON) &&
1311					!(state.flags & wimp_WINDOW_HSCROLL) &&
1312					(toolbar->height > toolbar->max_height))
1313				xwimp_force_redraw(toolbar->parent_handle,
1314					0, -16384, 16384, 16384);
1315			toolbar->max_height = height;
1316			collapse_height = toolbar->height +
1317					(toolbar->editor ? toolbar->editor->height : 0);
1318			ro_gui_theme_attach_toolbar(toolbar, toolbar->parent_handle);
1319			if ((state.flags & wimp_WINDOW_SIZE_ICON) &&
1320					!(state.flags & wimp_WINDOW_HSCROLL) &&
1321					(collapse_height > toolbar->max_height))
1322				xwimp_force_redraw(toolbar->parent_handle,
1323					0, -16384, 16384, 16384);
1324		}
1325	}
1326
1327	/*	Reformat the buttons starting with the throbber
1328	*/
1329	if ((width != old_width) || (toolbar->reformat_buttons)) {
1330		left_edge = 6;
1331		right_edge = width - 8;
1332		toolbar->height = 0;
1333		if ((toolbar->descriptor) && (toolbar->descriptor->theme) &&
1334				(toolbar->type == THEME_BROWSER_TOOLBAR) &&
1335				(toolbar->display_throbber)) {
1336			if (!toolbar->descriptor->throbber_right) {
1337				throbber_x = left_edge;
1338				left_edge += toolbar->descriptor->theme->throbber_width + 8;
1339			}
1340			toolbar->height = toolbar->descriptor->theme->throbber_height + 8;
1341		}
1342		if ((toolbar->type == THEME_BROWSER_TOOLBAR) &&	(toolbar->display_url)) {
1343			if (toolbar->height < 52 + 8)
1344				toolbar->height = 52 + 8;
1345			if ((toolbar->suggest) && (toolbar->height <
1346					(toolbar->suggest->height + 8)))
1347				toolbar->height = toolbar->suggest->height + 8;
1348		}
1349
1350		/*	Get the minimum height of the icons
1351		*/
1352		bottom_edge = left_edge;
1353		if ((toolbar->display_buttons || toolbar->editor) && (toolbar->descriptor) &&
1354				(toolbar->descriptor->theme)) {
1355			toolbar_icon = toolbar->icon;
1356			while (toolbar_icon) {
1357				if (toolbar_icon->display) {
1358					bottom_edge += toolbar_icon->width;
1359					visible_icon = true;
1360					if ((toolbar_icon->height != 0) &&
1361						(toolbar->height < toolbar_icon->height + 8)) {
1362						toolbar->height = toolbar_icon->height + 8;
1363					}
1364				}
1365				toolbar_icon = toolbar_icon->next;
1366			}
1367			if (visible_icon) bottom_edge += 8;
1368		}
1369
1370		/*	Check for minimum widths
1371		*/
1372		if (toolbar->type == THEME_BROWSER_TOOLBAR) {
1373			if (!toolbar->reformat_buttons) left_edge = bottom_edge;
1374			if (toolbar->display_url) {
1375				bottom_edge += 112;
1376				if (toolbar->suggest)
1377					bottom_edge += toolbar->suggest->width + 8;
1378			}
1379			if (bottom_edge > right_edge)
1380				right_edge = bottom_edge;
1381			if ((toolbar->descriptor) && (toolbar->descriptor->theme) &&
1382					(toolbar->display_throbber) &&
1383					(toolbar->descriptor->throbber_right)) {
1384				bottom_edge += toolbar->descriptor->theme->throbber_width;
1385				if (bottom_edge > right_edge) right_edge = bottom_edge;
1386				throbber_x = right_edge -
1387						toolbar->descriptor->theme->throbber_width;
1388				right_edge -= toolbar->descriptor->theme->throbber_width + 8;
1389			}
1390		}
1391
1392		if (toolbar->height != 0)
1393			toolbar->height += 2;
1394		if (toolbar->reformat_buttons) {
1395			/*	Hide the URL bar if we should
1396			*/
1397			if ((!toolbar->display_url) &&
1398					(toolbar->type == THEME_BROWSER_TOOLBAR)) {
1399				if (!xwimp_get_caret_position(&caret)) {
1400					if ((caret.w == toolbar->toolbar_handle) &&
1401							(caret.i == ICON_TOOLBAR_URL)) {
1402						if (toolbar->parent_handle)
1403							xwimp_set_caret_position(
1404									toolbar->parent_handle,
1405									wimp_ICON_WINDOW,
1406									-100, -100, 32, -1);
1407						else
1408							xwimp_set_caret_position((wimp_w)-1,
1409									0, 0, 0, 0, 0);
1410					}
1411				}
1412				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_SURROUND,
1413						0, -16384, 0, -16384);
1414				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_FAVICON,
1415						0, -16384, 0, -16384);
1416				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_URL,
1417						0, -16384, 0, -16384);
1418				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_SUGGEST,
1419						0, -16384, 0, -16384);
1420			} else if (toolbar->type == THEME_BROWSER_TOOLBAR) {
1421				ro_gui_set_icon_shaded_state(toolbar->toolbar_handle,
1422						ICON_TOOLBAR_URL, !toolbar->display_url);
1423			}
1424			xwimp_force_redraw(toolbar->toolbar_handle,
1425					0, 0, 16384, 16384);
1426
1427			/*	Move the buttons
1428			*/
1429			toolbar_icon = toolbar->icon;
1430			while (toolbar_icon) {
1431				if ((toolbar->display_buttons || toolbar->editor) &&
1432						(toolbar_icon->display)
1433						&& (toolbar_icon->width > 0)) {
1434					visible_icon = true;
1435					bottom_edge = (toolbar->height -
1436							toolbar_icon->height) / 2;
1437					toolbar_icon->x = left_edge;
1438					toolbar_icon->y = bottom_edge;
1439					xwimp_resize_icon(toolbar->toolbar_handle,
1440							toolbar_icon->icon_number,
1441							left_edge, bottom_edge,
1442							left_edge + toolbar_icon->width,
1443							bottom_edge + toolbar_icon->height);
1444					left_edge += toolbar_icon->width;
1445				} else {
1446					xwimp_resize_icon(toolbar->toolbar_handle,
1447							toolbar_icon->icon_number,
1448							0, -16384, 0, -16384);
1449				}
1450				toolbar_icon = toolbar_icon->next;
1451			}
1452			if (visible_icon) left_edge += 8;
1453		}
1454
1455
1456		if (toolbar->type == THEME_BROWSER_TOOLBAR) {
1457			/*	Move the URL bar
1458			*/
1459			if (toolbar->display_url) {
1460			  	top = (toolbar->height / 2) + 26;
1461			  	bottom = (toolbar->height / 2) - 26;
1462				if (toolbar->suggest) {
1463					right = right_edge - toolbar->suggest->width - 8;
1464					xwimp_resize_icon(toolbar->toolbar_handle,
1465						ICON_TOOLBAR_SUGGEST,
1466						right_edge - toolbar->suggest->width,
1467						(toolbar->height - toolbar->suggest->height) / 2,
1468						right_edge,
1469						(toolbar->height + toolbar->suggest->height) / 2);
1470				} else {
1471				  	right = right_edge;
1472				}
1473				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_URL,
1474					left_edge + 52,
1475					bottom + yeig,
1476					right - xeig,
1477					top - yeig);
1478				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_FAVICON,
1479					left_edge + xeig,
1480					bottom + yeig,
1481					left_edge + 52,
1482					top - yeig);
1483				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_SURROUND,
1484					left_edge,
1485					bottom,
1486					right,
1487					top);
1488				xwimp_force_redraw(toolbar->toolbar_handle,
1489					right - xeig, 0, 16384, 16384);
1490				xwimp_force_redraw(toolbar->toolbar_handle,
1491					left_edge,
1492					bottom,
1493					right,
1494					bottom + yeig);
1495				xwimp_force_redraw(toolbar->toolbar_handle,
1496					left_edge,
1497					top - yeig,
1498					right,
1499					top);
1500				if (!xwimp_get_caret_position(&caret)) {
1501					if ((caret.w == toolbar->toolbar_handle) &&
1502							(caret.i == ICON_TOOLBAR_URL)) {
1503						xwimp_set_caret_position(toolbar->toolbar_handle,
1504								ICON_TOOLBAR_URL,
1505								caret.pos.x, caret.pos.y,
1506								-1, caret.index);
1507					}
1508				}
1509				ro_gui_redraw_icon(toolbar->toolbar_handle, ICON_TOOLBAR_URL);
1510			}
1511
1512			/*	Move the throbber
1513			*/
1514			if ((toolbar->descriptor) && (toolbar->descriptor->theme) &&
1515					(throbber_x >= 0) && (toolbar->display_throbber)) {
1516				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_THROBBER,
1517					throbber_x, 0,
1518					throbber_x + toolbar->descriptor->theme->throbber_width,
1519					toolbar->height);
1520				if (toolbar->descriptor->throbber_right) {
1521					xwimp_force_redraw(toolbar->toolbar_handle,
1522						old_width - width + throbber_x, 0, 16384, 16384);
1523					xwimp_force_redraw(toolbar->toolbar_handle,
1524						throbber_x, 0, 16384, 16384);
1525				}
1526
1527			} else {
1528				xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_THROBBER,
1529					0, -16384, 0, -16384);
1530			}
1531		}
1532
1533		/*	Re-attach to the parent
1534		*/
1535		toolbar->toolbar_current = width;
1536		if (toolbar->reformat_buttons) {
1537			extent.x1 = 16384;
1538			extent.y0 = (toolbar->editor ? -toolbar->editor->height : 0);
1539			extent.y1 = toolbar->height - 2;
1540			xwimp_set_extent(toolbar->toolbar_handle, &extent);
1541			if ((toolbar->parent_handle) && (old_height != toolbar->height))
1542				ro_gui_theme_attach_toolbar(toolbar, toolbar->parent_handle);
1543		}
1544		toolbar->reformat_buttons = false;
1545	}
1546	return true;
1547}
1548
1549
1550/**
1551 * Destroys a toolbar and frees any associated memory.
1552 *
1553 * \param toolbar     the toolbar to destroy
1554 */
1555void ro_gui_theme_destroy_toolbar(struct toolbar *toolbar)
1556{
1557	struct toolbar_icon *icon;
1558	struct toolbar_icon *next_icon;
1559	if (!toolbar) return;
1560
1561	/*	Destroy our editor
1562	*/
1563	if (toolbar->editor) {
1564		toolbar->editor = NULL;
1565		ro_gui_theme_destroy_toolbar(toolbar->editor);
1566	}
1567
1568	/*	Delete our windows
1569	*/
1570	if (toolbar->toolbar_handle) {
1571		xwimp_delete_window(toolbar->toolbar_handle);
1572		ro_gui_wimp_event_finalise(toolbar->toolbar_handle);
1573	}
1574	/*	Free the Wimp buffer (we only created one for them all)
1575	*/
1576	free(toolbar->url_buffer);
1577
1578	/*	Free all the icons
1579	*/
1580	next_icon = toolbar->icon;
1581	while ((icon = next_icon) != NULL) {
1582		next_icon = icon->next;
1583		ro_gui_theme_destroy_toolbar_icon(icon);
1584	}
1585	ro_gui_theme_destroy_toolbar_icon(toolbar->suggest);
1586	free(toolbar);
1587}
1588
1589
1590/**
1591 * Toggles the toolbar editing mode
1592 *
1593 * \param toolbar      the toolbar to toggle editing for
1594 */
1595void ro_gui_theme_toggle_edit(struct toolbar *toolbar)
1596{
1597	int icons = 0;
1598	struct toolbar_icon *icon;
1599	struct gui_window *g = NULL;
1600	wimp_window_state state;
1601	os_error *error;
1602	char *option;
1603	char hex_no[4];
1604
1605	if (!toolbar)
1606		return;
1607
1608	if ((toolbar->type == THEME_BROWSER_TOOLBAR) &&
1609			(toolbar->parent_handle))
1610		g = ro_gui_window_lookup(toolbar->parent_handle);
1611
1612	if (toolbar->editor) {
1613		/* save options */
1614		icons = 0;
1615		for (icon = toolbar->icon; icon; icon = icon->next)
1616			if (icon->display) icons++;
1617		option = calloc(icons + 1, 1);
1618		if (!option) {
1619			LOG(("No memory to save toolbar options"));
1620			warn_user("NoMemory", 0);
1621		} else {
1622			icons = 0;
1623			for (icon = toolbar->icon; icon; icon = icon->next)
1624				if (icon->display) {
1625					if (icon->icon_number == -1) {
1626						option[icons] = '|';
1627					} else {
1628						sprintf(hex_no, "%x", icon->icon_number);
1629						option[icons] = hex_no[0];
1630					}
1631					icons++;
1632				}
1633			switch (toolbar->type) {
1634				case THEME_BROWSER_TOOLBAR:
1635					free(option_toolbar_browser);
1636					option_toolbar_browser = option;
1637					break;
1638				case THEME_HOTLIST_TOOLBAR:
1639					free(option_toolbar_hotlist);
1640					option_toolbar_hotlist = option;
1641					break;
1642				case THEME_HISTORY_TOOLBAR:
1643					free(option_toolbar_history);
1644					option_toolbar_history = option;
1645					break;
1646				case THEME_COOKIES_TOOLBAR:
1647					free(option_toolbar_cookies);
1648					option_toolbar_cookies = option;
1649					break;
1650				default:
1651					break;
1652			}
1653			ro_gui_save_options();
1654		}
1655
1656		/* turn off editing */
1657		ro_gui_theme_destroy_toolbar(toolbar->editor);
1658		toolbar->editor = NULL;
1659		ro_gui_theme_update_toolbar(toolbar->descriptor, toolbar);
1660		switch (toolbar->type) {
1661			case THEME_BROWSER_TOOLBAR:
1662				if (g)
1663					gui_window_update_extent(g);
1664				break;
1665			default:
1666				if (toolbar->parent_handle)
1667					xwimp_force_redraw(toolbar->parent_handle,
1668							0, -16384, 16384, 16384);
1669				break;
1670		}
1671	} else {
1672		/* create/initialise the toolbar editor */
1673		switch (toolbar->type) {
1674			case THEME_BROWSER_TOOLBAR:
1675				toolbar->editor = ro_gui_theme_create_toolbar(
1676						toolbar->descriptor,
1677						THEME_BROWSER_EDIT_TOOLBAR);
1678				break;
1679			case THEME_HOTLIST_TOOLBAR:
1680				toolbar->editor = ro_gui_theme_create_toolbar(
1681						toolbar->descriptor,
1682						THEME_HOTLIST_EDIT_TOOLBAR);
1683				break;
1684			case THEME_HISTORY_TOOLBAR:
1685				toolbar->editor = ro_gui_theme_create_toolbar(
1686						toolbar->descriptor,
1687						THEME_HISTORY_EDIT_TOOLBAR);
1688				break;
1689			case THEME_COOKIES_TOOLBAR:
1690				toolbar->editor = ro_gui_theme_create_toolbar(
1691						toolbar->descriptor,
1692						THEME_COOKIES_EDIT_TOOLBAR);
1693				break;
1694			default:
1695				return;
1696		}
1697		if (!toolbar->editor) {
1698			LOG(("Unable to create toolbar editor"));
1699			return;
1700		}
1701		ro_gui_wimp_event_set_user_data(toolbar->editor->toolbar_handle,
1702				ro_gui_wimp_event_get_user_data(toolbar->toolbar_handle));
1703		ro_gui_theme_update_toolbar(toolbar->descriptor, toolbar);
1704		switch (toolbar->type) {
1705			case THEME_BROWSER_TOOLBAR:
1706				if (g)
1707					gui_window_update_extent(g);
1708				break;
1709			default:
1710				if (toolbar->parent_handle) {
1711					state.w = toolbar->parent_handle;
1712					error = xwimp_get_window_state(&state);
1713					if (error) {
1714						LOG(("xwimp_get_window_state: 0x%x: %s",
1715							error->errnum, error->errmess));
1716						warn_user("WimpError", error->errmess);
1717						return;
1718					}
1719					ro_gui_open_window_request(PTR_WIMP_OPEN(&state));
1720					xwimp_force_redraw(toolbar->parent_handle,
1721							0, -16384, 16384, 16384);
1722				}
1723				break;
1724		}
1725		ro_gui_theme_process_toolbar(toolbar, -1);
1726		ro_gui_theme_toolbar_editor_sync(toolbar);
1727	}
1728	ro_gui_theme_set_help

Large files files are truncated, but you can click here to view the full file