PageRenderTime 17ms CodeModel.GetById 4ms app.highlight 163ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/C0deMaver1ck/peeklinux
C | 3468 lines | 2449 code | 479 blank | 540 comment | 481 complexity | fc8bb1d57c292c3e32af2212245e1337 MD5 | raw file
   1/*
   2 * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
   3 * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
   4 * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
   5 * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk>
   6 * Copyright 2005 Richard Wilson <info@tinct.net>
   7 * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
   8 *
   9 * This file is part of NetSurf, http://www.netsurf-browser.org/
  10 *
  11 * NetSurf is free software; you can redistribute it and/or modify
  12 * it under the terms of the GNU General Public License as published by
  13 * the Free Software Foundation; version 2 of the License.
  14 *
  15 * NetSurf is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  22 */
  23
  24/** \file
  25 * Browser window handling (implementation).
  26 */
  27
  28#include <assert.h>
  29#include <ctype.h>
  30#include <inttypes.h>
  31#include <math.h>
  32#include <stdio.h>
  33#include <stdbool.h>
  34#include <time.h>
  35#include <string.h>
  36#include "oslib/colourtrans.h"
  37#include "oslib/osbyte.h"
  38#include "oslib/osfile.h"
  39#include "oslib/osspriteop.h"
  40#include "oslib/wimp.h"
  41#include "oslib/wimpspriteop.h"
  42#include "utils/config.h"
  43#include "content/content.h"
  44#include "content/hlcache.h"
  45#include "content/urldb.h"
  46#include "css/css.h"
  47#include "desktop/browser.h"
  48#include "desktop/frames.h"
  49#include "desktop/mouse.h"
  50#include "desktop/plotters.h"
  51#include "desktop/textinput.h"
  52#include "desktop/tree.h"
  53#include "desktop/gui.h"
  54#include "render/box.h"
  55#include "render/form.h"
  56#include "riscos/bitmap.h"
  57#include "riscos/buffer.h"
  58#include "riscos/dialog.h"
  59#include "riscos/global_history.h"
  60#include "riscos/gui.h"
  61#include "riscos/gui/status_bar.h"
  62#include "riscos/menus.h"
  63#include "riscos/options.h"
  64#include "riscos/oslib_pre7.h"
  65#include "riscos/save.h"
  66#include "riscos/sprite.h"
  67#include "riscos/theme.h"
  68#include "riscos/thumbnail.h"
  69#include "riscos/url_complete.h"
  70#include "riscos/wimp.h"
  71#include "riscos/wimp_event.h"
  72#include "riscos/wimputils.h"
  73#include "utils/log.h"
  74#include "utils/talloc.h"
  75#include "utils/url.h"
  76#include "utils/utf8.h"
  77#include "utils/utils.h"
  78#include "utils/messages.h"
  79
  80#ifndef wimp_KEY_END
  81#define wimp_KEY_END wimp_KEY_COPY
  82#endif
  83
  84#ifndef wimp_WINDOW_GIVE_SHADED_ICON_INFO
  85	/* RISC OS 5+. Requires OSLib trunk. */
  86#define wimp_WINDOW_GIVE_SHADED_ICON_INFO ((wimp_extra_window_flags) 0x10u)
  87#endif
  88
  89#define SCROLL_VISIBLE_PADDING 32
  90
  91/** Remembers which iconised sprite numbers are in use */
  92static bool iconise_used[64];
  93static int iconise_next = 0;
  94
  95/** Whether a pressed mouse button has become a drag */
  96static bool mouse_drag;
  97
  98/** List of all browser windows. */
  99static struct gui_window *window_list = 0;
 100/** GUI window which is being redrawn. Valid only during redraw. */
 101struct gui_window *ro_gui_current_redraw_gui;
 102
 103static float scale_snap_to[] = {0.10, 0.125, 0.25, 0.333, 0.5, 0.75,
 104				1.0,
 105				1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 12.0, 16.0};
 106#define SCALE_SNAP_TO_SIZE (sizeof scale_snap_to) / (sizeof(float))
 107
 108
 109
 110/** An entry in ro_gui_pointer_table. */
 111struct ro_gui_pointer_entry {
 112	bool wimp_area;  /** The pointer is in the Wimp's sprite area. */
 113	char sprite_name[16];
 114	int xactive;
 115	int yactive;
 116};
 117
 118/** Map from gui_pointer_shape to pointer sprite data. Must be ordered as
 119 * enum gui_pointer_shape. */
 120struct ro_gui_pointer_entry ro_gui_pointer_table[] = {
 121	{ true, "ptr_default", 0, 0 },
 122	{ false, "ptr_point", 6, 0 },
 123	{ false, "ptr_caret", 4, 9 },
 124	{ false, "ptr_menu", 6, 4 },
 125	{ false, "ptr_ud", 6, 7 },
 126	{ false, "ptr_ud", 6, 7 },
 127	{ false, "ptr_lr", 7, 6 },
 128	{ false, "ptr_lr", 7, 6 },
 129	{ false, "ptr_ld", 7, 7 },
 130	{ false, "ptr_ld", 7, 7 },
 131	{ false, "ptr_rd", 7, 7 },
 132	{ false, "ptr_rd", 6, 7 },
 133	{ false, "ptr_cross", 7, 7 },
 134	{ false, "ptr_move", 8, 0 },
 135	{ false, "ptr_wait", 7, 10 },
 136	{ false, "ptr_help", 0, 0 },
 137	{ false, "ptr_nodrop", 0, 0 },
 138	{ false, "ptr_nt_allwd", 10, 10 },
 139	{ false, "ptr_progress", 0, 0 },
 140};
 141
 142
 143static void ro_gui_window_remove_update_boxes(struct gui_window *g);
 144static void gui_window_set_extent(struct gui_window *g, int width, int height);
 145static void ro_gui_window_open(wimp_open *open);
 146static void ro_gui_window_close(wimp_w w);
 147static void ro_gui_window_redraw(wimp_draw *redraw);
 148static bool ro_gui_window_click(wimp_pointer *mouse);
 149static bool ro_gui_window_keypress(wimp_key *key);
 150static void ro_gui_window_launch_url(struct gui_window *g, const char *url);
 151static void ro_gui_window_clone_options(struct browser_window *new_bw,
 152		struct browser_window *old_bw);
 153static browser_mouse_state ro_gui_mouse_drag_state(wimp_mouse_state buttons);
 154static bool ro_gui_window_import_text(struct gui_window *g,
 155		const char *filename, bool toolbar);
 156
 157struct update_box {
 158	int x0;
 159	int y0;
 160	int x1;
 161	int y1;
 162	bool use_buffer;
 163	struct gui_window *g;
 164	union content_msg_data data;
 165	struct update_box *next;
 166};
 167
 168struct update_box *pending_updates;
 169#define MARGIN 4
 170
 171
 172/**
 173 * Create and open a new browser window.
 174 *
 175 * \param  bw	  browser_window structure to update
 176 * \param  clone  the browser window to clone options from, or NULL for default
 177 * \return  gui_window, or 0 on error and error reported
 178 */
 179
 180struct gui_window *gui_create_browser_window(struct browser_window *bw,
 181		struct browser_window *clone, bool new_tab)
 182{
 183	int screen_width, screen_height, win_width, win_height, scroll_width;
 184	static int window_count = 2;
 185	wimp_window window;
 186	wimp_window_state state;
 187	os_error *error;
 188	bool open_centred = true;
 189	struct gui_window *g;
 190	struct browser_window *top;
 191
 192	g = malloc(sizeof *g);
 193	if (!g) {
 194		warn_user("NoMemory", 0);
 195		return 0;
 196	}
 197	g->bw = bw;
 198	g->toolbar = 0;
 199	g->status_bar = 0;
 200	g->old_width = 0;
 201	g->old_height = 0;
 202	g->update_extent = true;
 203	strcpy(g->title, "NetSurf");
 204	g->throbber = 0;
 205	g->throbtime = 0;
 206	g->iconise_icon = -1;
 207
 208	/* Set the window position */
 209	if (bw->parent) {
 210		window.visible.x0 = 0;
 211		window.visible.x1 = 64;
 212		window.visible.y0 = 0;
 213		window.visible.y1 = 64;
 214		open_centred = false;
 215	} else if (clone && clone->window && option_window_size_clone) {
 216		for (top = clone; top->parent; top = top->parent);
 217		state.w = top->window->window;
 218		error = xwimp_get_window_state(&state);
 219		if (error) {
 220			LOG(("xwimp_get_window_state: 0x%x: %s",
 221					error->errnum, error->errmess));
 222			warn_user("WimpError", error->errmess);
 223		}
 224		window.visible.x0 = state.visible.x0;
 225		window.visible.x1 = state.visible.x1;
 226		window.visible.y0 = state.visible.y0 - 48;
 227		window.visible.y1 = state.visible.y1 - 48;
 228		open_centred = false;
 229	} else {
 230		ro_gui_screen_size(&screen_width, &screen_height);
 231
 232		/* Check if we have a preferred position */
 233		if ((option_window_screen_width != 0) &&
 234				(option_window_screen_height != 0)) {
 235			win_width = (option_window_width * screen_width) /
 236					option_window_screen_width;
 237			win_height = (option_window_height * screen_height) /
 238					option_window_screen_height;
 239			window.visible.x0 = (option_window_x * screen_width) /
 240					option_window_screen_width;
 241			window.visible.y0 = (option_window_y * screen_height) /
 242					option_window_screen_height;
 243			if (option_window_stagger) {
 244				window.visible.y0 += 96 -
 245						(48 * (window_count % 5));
 246			}
 247			open_centred = false;
 248			if (win_width < 100)
 249				win_width = 100;
 250			if (win_height < 100)
 251				win_height = 100;
 252		} else {
 253
 254		       /* Base how we define the window height/width
 255			  on the compile time options set */
 256			win_width = screen_width * 3 / 4;
 257			if (1600 < win_width)
 258				win_width = 1600;
 259			win_height = win_width * 3 / 4;
 260
 261			window.visible.x0 = (screen_width - win_width) / 2;
 262			window.visible.y0 = ((screen_height - win_height) / 2) +
 263					96 - (48 * (window_count % 5));
 264		}
 265		window.visible.x1 = window.visible.x0 + win_width;
 266		window.visible.y1 = window.visible.y0 + win_height;
 267	}
 268
 269	/* General flags for a non-movable, non-resizable, no-title bar window */
 270	window.xscroll = 0;
 271	window.yscroll = 0;
 272	window.next = wimp_TOP;
 273	window.flags =	wimp_WINDOW_MOVEABLE |
 274			wimp_WINDOW_NEW_FORMAT |
 275			wimp_WINDOW_VSCROLL |
 276			wimp_WINDOW_HSCROLL |
 277			wimp_WINDOW_IGNORE_XEXTENT |
 278			wimp_WINDOW_IGNORE_YEXTENT |
 279			wimp_WINDOW_SCROLL_REPEAT;
 280	window.title_fg = wimp_COLOUR_BLACK;
 281	window.title_bg = wimp_COLOUR_LIGHT_GREY;
 282	window.work_fg = wimp_COLOUR_LIGHT_GREY;
 283	window.work_bg = wimp_COLOUR_TRANSPARENT;
 284	window.scroll_outer = wimp_COLOUR_DARK_GREY;
 285	window.scroll_inner = wimp_COLOUR_MID_LIGHT_GREY;
 286	window.highlight_bg = wimp_COLOUR_CREAM;
 287	window.extra_flags = wimp_WINDOW_USE_EXTENDED_SCROLL_REQUEST |
 288			wimp_WINDOW_GIVE_SHADED_ICON_INFO;
 289	window.extent.x0 = 0;
 290	window.extent.y0 = -(window.visible.y1 - window.visible.y0);
 291	window.extent.x1 = window.visible.x1 - window.visible.x0;
 292	window.extent.y1 = 0;
 293	window.title_flags = wimp_ICON_TEXT |
 294			wimp_ICON_INDIRECTED |
 295			wimp_ICON_HCENTRED;
 296	window.work_flags = wimp_BUTTON_CLICK_DRAG <<
 297			wimp_ICON_BUTTON_TYPE_SHIFT;
 298	window.sprite_area = wimpspriteop_AREA;
 299	window.xmin = 1;
 300	window.ymin = 1;
 301	window.title_data.indirected_text.text = g->title;
 302	window.title_data.indirected_text.validation = (char *) -1;
 303	window.title_data.indirected_text.size = 255;
 304	window.icon_count = 0;
 305
 306	/* Add in flags for our window type */
 307	switch (bw->browser_window_type) {
 308		case BROWSER_WINDOW_FRAMESET:
 309			window.flags &= ~(wimp_WINDOW_VSCROLL |
 310					wimp_WINDOW_HSCROLL);
 311			window.title_fg = 0xff;
 312			break;
 313		case BROWSER_WINDOW_IFRAME:
 314			window.flags |= wimp_WINDOW_NO_BOUNDS;
 315		case BROWSER_WINDOW_FRAME:
 316			if (bw->scrolling == SCROLLING_NO)
 317				window.flags &= ~(wimp_WINDOW_VSCROLL |
 318						wimp_WINDOW_HSCROLL);
 319			if (bw->scrolling == SCROLLING_AUTO)
 320				window.flags &= ~wimp_WINDOW_HSCROLL;
 321			if (!bw->border)
 322				window.title_fg = 0xff;
 323			else {
 324				/* set the correct border colour */
 325				unsigned int col;
 326				col = bw->border_colour & 0xffffff;
 327				sprintf(g->validation, "C%.6x", col);
 328				window.extra_flags |= wimp_WINDOW_USE_TITLE_VALIDATION_STRING;
 329				window.title_data.indirected_text.validation = g->validation;
 330			}
 331			break;
 332		case BROWSER_WINDOW_NORMAL:
 333			window.flags |=	wimp_WINDOW_SIZE_ICON |
 334					wimp_WINDOW_BACK_ICON |
 335					wimp_WINDOW_CLOSE_ICON |
 336					wimp_WINDOW_TITLE_ICON |
 337					wimp_WINDOW_TOGGLE_ICON;
 338			break;
 339	}
 340
 341	if (open_centred) {
 342		scroll_width = ro_get_vscroll_width(NULL);
 343		window.visible.x0 -= scroll_width;
 344	}
 345
 346	error = xwimp_create_window(&window, &g->window);
 347	if (error) {
 348		LOG(("xwimp_create_window: 0x%x: %s",
 349				error->errnum, error->errmess));
 350		warn_user("WimpError", error->errmess);
 351		free(g);
 352		return 0;
 353	}
 354
 355	/* Link into window list */
 356	g->prev = 0;
 357	g->next = window_list;
 358	if (window_list)
 359		window_list->prev = g;
 360	window_list = g;
 361	window_count++;
 362
 363	/* Add in a toolbar and status bar */
 364	if (bw->browser_window_type == BROWSER_WINDOW_NORMAL) {
 365		g->status_bar = ro_gui_status_bar_create(g->window, option_toolbar_status_width);
 366		g->toolbar = ro_gui_theme_create_toolbar(NULL, THEME_BROWSER_TOOLBAR);
 367		ro_gui_theme_attach_toolbar(g->toolbar, g->window);
 368	} else {
 369		g->toolbar = NULL;
 370	}
 371
 372	/* Set the window options */
 373	bw->window = g;
 374	ro_gui_window_clone_options(bw, clone);
 375	ro_gui_prepare_navigate(g);
 376
 377	/* Register event handlers */
 378	ro_gui_wimp_event_set_user_data(g->window, g);
 379	ro_gui_wimp_event_register_open_window(g->window, ro_gui_window_open);
 380	ro_gui_wimp_event_register_close_window(g->window, ro_gui_window_close);
 381	ro_gui_wimp_event_register_redraw_window(g->window, ro_gui_window_redraw);
 382	ro_gui_wimp_event_register_keypress(g->window, ro_gui_window_keypress);
 383	if (g->toolbar)
 384		ro_gui_wimp_event_register_keypress(g->toolbar->toolbar_handle,
 385				ro_gui_window_keypress);
 386	ro_gui_wimp_event_register_mouse_click(g->window, ro_gui_window_click);
 387
 388	/* Open the window at the top of the stack */
 389	state.w = g->window;
 390	error = xwimp_get_window_state(&state);
 391	if (error) {
 392		LOG(("xwimp_get_window_state: 0x%x: %s",
 393				error->errnum, error->errmess));
 394		warn_user("WimpError", error->errmess);
 395		return g;
 396	}
 397
 398	state.next = wimp_TOP;
 399	if (bw->parent) {
 400		top = browser_window_owner(bw);
 401		error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), 
 402				top->window->window,
 403				wimp_CHILD_LINKS_PARENT_WORK_AREA
 404						<< wimp_CHILD_XORIGIN_SHIFT |
 405				wimp_CHILD_LINKS_PARENT_WORK_AREA
 406						<< wimp_CHILD_YORIGIN_SHIFT);
 407		if (error) {
 408			LOG(("xwimp_open_window_nested: 0x%x: %s",
 409					error->errnum, error->errmess));
 410			warn_user("WimpError", error->errmess);
 411		}
 412	}
 413
 414	ro_gui_window_open(PTR_WIMP_OPEN(&state));
 415
 416	/* Claim the caret for top-level windows */
 417	if (bw->browser_window_type == BROWSER_WINDOW_NORMAL) {
 418		if (g->toolbar && g->toolbar->display_url) {
 419			error = xwimp_set_caret_position(
 420					g->toolbar->toolbar_handle,
 421					ICON_TOOLBAR_URL, -1, -1, -1, 0);
 422			ro_gui_url_complete_start(g);
 423			if (error) {
 424				LOG(("xwimp_set_caret_position: 0x%x: %s",
 425						error->errnum, error->errmess));
 426				warn_user("WimpError", error->errmess);
 427			}
 428		} else
 429			gui_window_place_caret(g, -100, -100, 0);
 430	}
 431	return g;
 432}
 433
 434
 435/**
 436 * Close a browser window and free any related resources.
 437 *
 438 * \param  g  gui_window to destroy
 439 */
 440
 441void gui_window_destroy(struct gui_window *g)
 442{
 443	os_error *error;
 444	wimp_w w;
 445
 446	assert(g);
 447
 448	/* stop any tracking */
 449	if (gui_track_gui_window == g) {
 450		gui_track_gui_window = NULL;
 451		gui_current_drag_type = GUI_DRAG_NONE;
 452	}
 453
 454	/* remove from list */
 455	if (g->prev)
 456		g->prev->next = g->next;
 457	else
 458		window_list = g->next;
 459	if (g->next)
 460		g->next->prev = g->prev;
 461
 462	/* destroy toolbar */
 463	if (g->toolbar)
 464		ro_gui_theme_destroy_toolbar(g->toolbar);
 465	if (g->status_bar)
 466		ro_gui_status_bar_destroy(g->status_bar);
 467
 468	w = g->window;
 469	ro_gui_url_complete_close(NULL, 0);
 470	ro_gui_dialog_close_persistent(w);
 471	if (current_menu_window == w)
 472		ro_gui_menu_closed(true);
 473	ro_gui_window_remove_update_boxes(g);
 474
 475	/* delete window */
 476	error = xwimp_delete_window(w);
 477	if (error) {
 478		LOG(("xwimp_delete_window: 0x%x: %s",
 479				error->errnum, error->errmess));
 480		warn_user("WimpError", error->errmess);
 481	}
 482	ro_gui_wimp_event_finalise(w);
 483
 484	free(g);
 485}
 486
 487
 488/**
 489 * Set the title of a browser window.
 490 *
 491 * \param  g	  gui_window to update
 492 * \param  title  new window title, copied
 493 */
 494
 495void gui_window_set_title(struct gui_window *g, const char *title)
 496{
 497	int scale_disp;
 498
 499	assert(g);
 500	assert(title);
 501
 502	if (g->bw->scale != 1.0) {
 503		scale_disp = g->bw->scale * 100;
 504		if (ABS((float)scale_disp - g->bw->scale * 100) >= 0.05)
 505			snprintf(g->title, sizeof g->title, "%s (%.1f%%)",
 506					title, g->bw->scale * 100);
 507		else
 508			snprintf(g->title, sizeof g->title, "%s (%i%%)",
 509					title, scale_disp);
 510	} else {
 511		strncpy(g->title, title, sizeof g->title);
 512	}
 513
 514	/* only top-level parents have titlebars */
 515	if (!g->bw->parent)
 516		ro_gui_set_window_title(g->window, g->title);
 517}
 518
 519
 520/**
 521 * Force a redraw of part of the contents of a browser window.
 522 *
 523 * \param  g   gui_window to redraw
 524 * \param  x0  rectangle to redraw
 525 * \param  y0  rectangle to redraw
 526 * \param  x1  rectangle to redraw
 527 * \param  y1  rectangle to redraw
 528 */
 529
 530void gui_window_redraw(struct gui_window *g, int x0, int y0, int x1, int y1)
 531{
 532	os_error *error;
 533
 534	assert(g);
 535
 536	error = xwimp_force_redraw(g->window, x0 * 2, -y1 * 2, x1 * 2, -y0 * 2);
 537	if (error) {
 538		LOG(("xwimp_force_redraw: 0x%x: %s",
 539				error->errnum, error->errmess));
 540		warn_user("WimpError", error->errmess);
 541	}
 542}
 543
 544
 545/**
 546 * Force a redraw of the entire contents of a browser window.
 547 *
 548 * \param  g   gui_window to redraw
 549 */
 550void gui_window_redraw_window(struct gui_window *g)
 551{
 552	wimp_window_info info;
 553	os_error *error;
 554
 555	assert(g);
 556	info.w = g->window;
 557	error = xwimp_get_window_info_header_only(&info);
 558	if (error) {
 559		LOG(("xwimp_get_window_info_header_only: 0x%x: %s",
 560				error->errnum, error->errmess));
 561		warn_user("WimpError", error->errmess);
 562		return;
 563	}
 564	error = xwimp_force_redraw(g->window, info.extent.x0, info.extent.y0,
 565			info.extent.x1, info.extent.y1);
 566	if (error) {
 567		LOG(("xwimp_force_redraw: 0x%x: %s",
 568				error->errnum, error->errmess));
 569		warn_user("WimpError", error->errmess);
 570	}
 571}
 572
 573
 574/**
 575 * Redraw an area of a window.
 576 *
 577 * \param  g   gui_window
 578 * \param  data  content_msg_data union with filled in redraw data
 579 */
 580
 581void gui_window_update_box(struct gui_window *g,
 582		const union content_msg_data *data)
 583{
 584	hlcache_handle *h = g->bw->current_content;
 585	bool use_buffer;
 586	int x0, y0, x1, y1;
 587	struct update_box *cur;
 588
 589	if (!h)
 590		return;
 591
 592	x0 = floorf(data->redraw.x * 2 * g->bw->scale);
 593	y0 = -ceilf((data->redraw.y + data->redraw.height) * 2 * g->bw->scale);
 594	x1 = ceilf((data->redraw.x + data->redraw.width) * 2 * g->bw->scale) + 1;
 595	y1 = -floorf(data->redraw.y * 2 * g->bw->scale) + 1;
 596	use_buffer = 
 597		(g->option.buffer_everything || g->option.buffer_animations);
 598
 599	/* try to optimise buffered redraws */
 600	if (use_buffer) {
 601		for (cur = pending_updates; cur != NULL; cur = cur->next) {
 602			if ((cur->g != g) || (!cur->use_buffer))
 603				continue;
 604			if ((((cur->x0 - x1) < MARGIN) || ((cur->x1 - x0) < MARGIN)) &&
 605					(((cur->y0 - y1) < MARGIN) || ((cur->y1 - y0) < MARGIN))) {
 606				cur->x0 = min(cur->x0, x0);
 607				cur->y0 = min(cur->y0, y0);
 608				cur->x1 = max(cur->x1, x1);
 609				cur->y1 = max(cur->y1, y1);
 610				return;
 611			}
 612
 613		}
 614	}
 615	cur = malloc(sizeof(struct update_box));
 616	if (!cur) {
 617		LOG(("No memory for malloc."));
 618		warn_user("NoMemory", 0);
 619		return;
 620	}
 621	cur->x0 = x0;
 622	cur->y0 = y0;
 623	cur->x1 = x1;
 624	cur->y1 = y1;
 625	cur->next = pending_updates;
 626	pending_updates = cur;
 627	cur->g = g;
 628	cur->use_buffer = use_buffer;
 629	cur->data = *data;
 630}
 631
 632
 633/**
 634 * Get the scroll position of a browser window.
 635 *
 636 * \param  g   gui_window
 637 * \param  sx  receives x ordinate of point at top-left of window
 638 * \param  sy  receives y ordinate of point at top-left of window
 639 * \return true iff successful
 640 */
 641
 642bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy)
 643{
 644	wimp_window_state state;
 645	os_error *error;
 646	int toolbar_height = 0;
 647
 648	assert(g);
 649
 650	state.w = g->window;
 651	error = xwimp_get_window_state(&state);
 652	if (error) {
 653		LOG(("xwimp_get_window_state: 0x%x: %s",
 654				error->errnum, error->errmess));
 655		warn_user("WimpError", error->errmess);
 656		return false;
 657	}
 658
 659	if (g->toolbar)
 660		toolbar_height = ro_gui_theme_toolbar_full_height(g->toolbar);
 661	*sx = state.xscroll / (2 * g->bw->scale);
 662	*sy = -(state.yscroll - toolbar_height) / (2 * g->bw->scale);
 663	return true;
 664}
 665
 666
 667/**
 668 * Set the scroll position of a browser window.
 669 *
 670 * \param  g   gui_window to scroll
 671 * \param  sx  point to place at top-left of window
 672 * \param  sy  point to place at top-left of window
 673 */
 674
 675void gui_window_set_scroll(struct gui_window *g, int sx, int sy)
 676{
 677	wimp_window_state state;
 678	os_error *error;
 679
 680	assert(g);
 681
 682	state.w = g->window;
 683	error = xwimp_get_window_state(&state);
 684	if (error) {
 685		LOG(("xwimp_get_window_state: 0x%x: %s",
 686				error->errnum, error->errmess));
 687		warn_user("WimpError", error->errmess);
 688		return;
 689	}
 690
 691	state.xscroll = sx * 2 * g->bw->scale;
 692	state.yscroll = -sy * 2 * g->bw->scale;
 693	if (g->toolbar)
 694		state.yscroll += ro_gui_theme_toolbar_full_height(g->toolbar);
 695	ro_gui_window_open(PTR_WIMP_OPEN(&state));
 696}
 697
 698
 699/**
 700 * Scrolls the specified area of a browser window into view.
 701 *
 702 * \param  g   gui_window to scroll
 703 * \param  x0  left point to ensure visible
 704 * \param  y0  bottom point to ensure visible
 705 * \param  x1  right point to ensure visible
 706 * \param  y1  top point to ensure visible
 707 */
 708void gui_window_scroll_visible(struct gui_window *g, int x0, int y0, int x1, int y1)
 709{
 710	wimp_window_state state;
 711	os_error *error;
 712	int cx0, cy0, width, height;
 713	int padding_available;
 714	int toolbar_height = 0;
 715	int correction;
 716
 717	assert(g);
 718
 719	state.w = g->window;
 720	error = xwimp_get_window_state(&state);
 721	if (error) {
 722		LOG(("xwimp_get_window_state: 0x%x: %s",
 723				error->errnum, error->errmess));
 724		warn_user("WimpError", error->errmess);
 725		return;
 726	}
 727
 728	if (g->toolbar)
 729		toolbar_height = ro_gui_theme_toolbar_full_height(g->toolbar);
 730
 731	x0 = x0 * 2 * g->bw->scale;
 732	y0 = y0 * 2 * g->bw->scale;
 733	x1 = x1 * 2 * g->bw->scale;
 734	y1 = y1 * 2 * g->bw->scale;
 735
 736	cx0 = state.xscroll;
 737	cy0 = -state.yscroll + toolbar_height;
 738	width = state.visible.x1 - state.visible.x0;
 739	height = state.visible.y1 - state.visible.y0 - toolbar_height;
 740
 741	/* make sure we're visible */
 742	correction = (x1 - cx0 - width);
 743	if (correction > 0)
 744		cx0 += correction;
 745	correction = (y1 - cy0 - height);
 746	if (correction > 0)
 747		cy0 += correction;
 748	if (x0 < cx0)
 749		cx0 = x0;
 750	if (y0 < cy0)
 751		cy0 = y0;
 752
 753	/* try to give a SCROLL_VISIBLE_PADDING border of space around us */
 754	padding_available = (width - x1 + x0) / 2;
 755	if (padding_available > 0) {
 756		if (padding_available > SCROLL_VISIBLE_PADDING)
 757			padding_available = SCROLL_VISIBLE_PADDING;
 758		correction = (cx0 + width - x1);
 759		if (correction < padding_available)
 760			cx0 += padding_available;
 761		correction = (x0 - cx0);
 762		if (correction < padding_available)
 763			cx0 -= padding_available;
 764	}
 765	padding_available = (height - y1 + y0) / 2;
 766	if (padding_available > 0) {
 767		if (padding_available > SCROLL_VISIBLE_PADDING)
 768			padding_available = SCROLL_VISIBLE_PADDING;
 769		correction = (cy0 + height - y1);
 770		if (correction < padding_available)
 771			cy0 += padding_available;
 772		correction = (y0 - cy0);
 773		if (correction < padding_available)
 774			cy0 -= padding_available;
 775	}
 776
 777	state.xscroll = cx0;
 778	state.yscroll = -cy0 + toolbar_height;
 779	ro_gui_window_open(PTR_WIMP_OPEN(&state));
 780}
 781
 782
 783/**
 784 * Opens a frame at a specified position.
 785 *
 786 * \param  g   child gui_window to open
 787 * \param  x0  left point to open at
 788 * \param  y0  bottom point to open at
 789 * \param  x1  right point to open at
 790 * \param  y1  top point to open at
 791 */
 792void gui_window_position_frame(struct gui_window *g, int x0, int y0, int x1, int y1)
 793{
 794	wimp_window_state state;
 795	os_error *error;
 796	int px0, py1;
 797	struct browser_window *bw;
 798	struct browser_window *parent;
 799	struct browser_window *top;
 800	float scale = 1.0;
 801
 802	assert(g);
 803	bw = g->bw;
 804	assert(bw);
 805	parent = bw->parent;
 806	assert(parent);
 807	top = browser_window_owner(bw);
 808
 809	/* store position for children */
 810	if (parent->browser_window_type == BROWSER_WINDOW_IFRAME) {
 811		bw->x0 = x0;
 812		bw->y0 = y0;
 813		bw->x1 = x1;
 814		bw->y1 = y1;
 815	} else {
 816		bw->x0 = x0 = parent->x0 + x0;
 817		bw->y0 = y0 = parent->y0 + y0;
 818		bw->x1 = x1 = parent->x0 + x1;
 819		bw->y1 = y1 = parent->y0 + y1;
 820	}
 821
 822	/* only scale iframe locations */
 823	if (bw->browser_window_type == BROWSER_WINDOW_IFRAME)
 824	  	scale = g->bw->scale;
 825
 826	/* get the position of the top level window */
 827	state.w = top->window->window;
 828	error = xwimp_get_window_state(&state);
 829	if (error) {
 830		LOG(("xwimp_get_window_state: 0x%x: %s",
 831				error->errnum, error->errmess));
 832		warn_user("WimpError", error->errmess);
 833		return;
 834	}
 835	px0 = state.visible.x0 - state.xscroll;
 836	py1 = state.visible.y1 - state.yscroll;
 837
 838	/* get our current window state */
 839	state.w = g->window;
 840	error = xwimp_get_window_state(&state);
 841	if (error) {
 842		LOG(("xwimp_get_window_state: 0x%x: %s",
 843				error->errnum, error->errmess));
 844		warn_user("WimpError", error->errmess);
 845		return;
 846	}
 847	if (!g->bw->border) {
 848		x0 -= 1;
 849		y0 -= 1;
 850		x1 += 1;
 851		y1 += 1;
 852	}
 853
 854	x1 = x1 * 2 * scale;
 855	y1 = y1 * 2 * scale;
 856
 857	/* scrollbars must go inside */
 858	if (state.flags & wimp_WINDOW_HSCROLL) {
 859		y1 -= ro_get_hscroll_height(NULL);
 860		if (g->bw->border)
 861			y1 += 2;
 862	}
 863	if (state.flags & wimp_WINDOW_VSCROLL) {
 864		x1 -= ro_get_vscroll_width(NULL);
 865		if (g->bw->border)
 866			x1 += 2;
 867	}
 868	state.visible.x0 = px0 + x0 * 2 * scale;
 869	state.visible.y0 = py1 - y1;
 870	state.visible.x1 = px0 + x1;
 871	state.visible.y1 = py1 - y0 * 2 * scale;
 872	g->update_extent = true;
 873	ro_gui_window_open(PTR_WIMP_OPEN(&state));
 874}
 875
 876
 877/**
 878 * Find the current dimensions of a browser window's content area.
 879 *
 880 * \param g	 gui_window to measure
 881 * \param width	 receives width of window
 882 * \param height receives height of window
 883 * \param scaled whether to return scaled values
 884 */
 885
 886void gui_window_get_dimensions(struct gui_window *g, int *width, int *height, bool scaled)
 887{
 888  	/* use the cached window sizes */
 889	*width = g->old_width / 2;
 890	*height = g->old_height / 2;
 891	if (scaled) {
 892		*width /= g->bw->scale;
 893		*height /= g->bw->scale;
 894	}
 895}
 896
 897
 898/**
 899 * Update the extent of the inside of a browser window to that of the current content.
 900 *
 901 * \param  g	   gui_window to update the extent of
 902 */
 903
 904void gui_window_update_extent(struct gui_window *g)
 905{
 906	os_error *error;
 907	wimp_window_state state;
 908	bool update;
 909	unsigned int flags;
 910	int scroll = 0;
 911
 912	assert(g);
 913
 914	state.w = g->window;
 915	error = xwimp_get_window_state(&state);
 916	if (error) {
 917		LOG(("xwimp_get_window_state: 0x%x: %s",
 918				error->errnum, error->errmess));
 919		warn_user("WimpError", error->errmess);
 920		return;
 921	}
 922
 923	/* scroll on toolbar height change */
 924	if (g->toolbar) {
 925		scroll = ro_gui_theme_height_change(g->toolbar);
 926		state.yscroll -= scroll;
 927	}
 928
 929	/* only allow a further reformat if we've gained/lost scrollbars */
 930	flags = state.flags & (wimp_WINDOW_HSCROLL | wimp_WINDOW_VSCROLL);
 931	update = g->bw->reformat_pending;
 932	g->update_extent = true;
 933	ro_gui_window_open(PTR_WIMP_OPEN(&state));
 934
 935	state.w = g->window;
 936	error = xwimp_get_window_state(&state);
 937	if (error) {
 938		LOG(("xwimp_get_window_state: 0x%x: %s",
 939				error->errnum, error->errmess));
 940		warn_user("WimpError", error->errmess);
 941		return;
 942	}
 943	if (flags == (state.flags & (wimp_WINDOW_HSCROLL | wimp_WINDOW_VSCROLL)))
 944		g->bw->reformat_pending = update;
 945	if ((scroll != 0) && (g->bw->children))
 946		browser_window_recalculate_frameset(g->bw);
 947}
 948
 949
 950/**
 951 * Set the status bar of a browser window.
 952 *
 953 * \param  g	 gui_window to update
 954 * \param  text  new status text
 955 */
 956
 957void gui_window_set_status(struct gui_window *g, const char *text)
 958{
 959	if (g->status_bar)
 960		ro_gui_status_bar_set_text(g->status_bar, text);
 961}
 962
 963
 964/**
 965 * Change mouse pointer shape
 966 */
 967
 968void gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape)
 969{
 970	static gui_pointer_shape curr_pointer = GUI_POINTER_DEFAULT;
 971	struct ro_gui_pointer_entry *entry;
 972	os_error *error;
 973
 974	if (shape == curr_pointer)
 975		return;
 976
 977	assert(shape < sizeof ro_gui_pointer_table /
 978			sizeof ro_gui_pointer_table[0]);
 979
 980	entry = &ro_gui_pointer_table[shape];
 981
 982	if (entry->wimp_area) {
 983		/* pointer in the Wimp's sprite area */
 984		error = xwimpspriteop_set_pointer_shape(entry->sprite_name,
 985				1, entry->xactive, entry->yactive, 0, 0);
 986		if (error) {
 987			LOG(("xwimpspriteop_set_pointer_shape: 0x%x: %s",
 988					error->errnum, error->errmess));
 989			warn_user("WimpError", error->errmess);
 990		}
 991	} else {
 992		/* pointer in our own sprite area */
 993		error = xosspriteop_set_pointer_shape(osspriteop_USER_AREA,
 994				gui_sprites,
 995				(osspriteop_id) entry->sprite_name,
 996				1, entry->xactive, entry->yactive, 0, 0);
 997		if (error) {
 998			LOG(("xosspriteop_set_pointer_shape: 0x%x: %s",
 999					error->errnum, error->errmess));
1000			warn_user("WimpError", error->errmess);
1001		}
1002	}
1003
1004	curr_pointer = shape;
1005}
1006
1007
1008/**
1009 * Remove the mouse pointer from the screen
1010 */
1011
1012void gui_window_hide_pointer(struct gui_window *g)
1013{
1014	os_error *error;
1015
1016	error = xwimpspriteop_set_pointer_shape(NULL, 0x30, 0, 0, 0, 0);
1017	if (error) {
1018		LOG(("xwimpspriteop_set_pointer_shape: 0x%x: %s",
1019				error->errnum, error->errmess));
1020		warn_user("WimpError", error->errmess);
1021	}
1022}
1023
1024
1025/**
1026 * Set the contents of a window's address bar.
1027 *
1028 * \param  g	gui_window to update
1029 * \param  url  new url for address bar
1030 */
1031
1032void gui_window_set_url(struct gui_window *g, const char *url)
1033{
1034	wimp_caret caret;
1035	os_error *error;
1036	const char *toolbar_url;
1037
1038	if (!g->toolbar)
1039		return;
1040
1041	ro_gui_set_icon_string(g->toolbar->toolbar_handle,
1042			ICON_TOOLBAR_URL, url, true);
1043	ro_gui_force_redraw_icon(g->toolbar->toolbar_handle,
1044			ICON_TOOLBAR_FAVICON);
1045
1046	/* if the caret is in the address bar, move it to the end */
1047	error = xwimp_get_caret_position(&caret);
1048	if (error) {
1049		LOG(("xwimp_get_caret_position: 0x%x: %s",
1050				error->errnum, error->errmess));
1051		warn_user("WimpError", error->errmess);
1052		return;
1053	}
1054
1055	if (!(caret.w == g->toolbar->toolbar_handle &&
1056			caret.i == ICON_TOOLBAR_URL))
1057		return;
1058
1059	toolbar_url = ro_gui_get_icon_string(g->toolbar->toolbar_handle,
1060			ICON_TOOLBAR_URL);
1061	error = xwimp_set_caret_position(g->toolbar->toolbar_handle,
1062			ICON_TOOLBAR_URL, 0, 0, -1, (int)strlen(toolbar_url));
1063	if (error) {
1064		LOG(("xwimp_set_caret_position: 0x%x: %s",
1065				error->errnum, error->errmess));
1066		warn_user("WimpError", error->errmess);
1067	}
1068	ro_gui_url_complete_start(g);
1069}
1070
1071
1072/**
1073 * Update the interface to reflect start of page loading.
1074 *
1075 * \param  g  window with start of load
1076 */
1077
1078void gui_window_start_throbber(struct gui_window *g)
1079{
1080	ro_gui_menu_objects_moved();
1081	ro_gui_prepare_navigate(g);
1082	xos_read_monotonic_time(&g->throbtime);
1083	g->throbber = 0;
1084}
1085
1086
1087
1088/**
1089 * Update the interface to reflect page loading stopped.
1090 *
1091 * \param  g  window with start of load
1092 */
1093
1094void gui_window_stop_throbber(struct gui_window *g)
1095{
1096	char throb_buf[12];
1097	ro_gui_prepare_navigate(g);
1098	g->throbber = 0;
1099	if (g->toolbar) {
1100		strcpy(throb_buf, "throbber0");
1101		ro_gui_set_icon_string(g->toolbar->toolbar_handle,
1102				ICON_TOOLBAR_THROBBER, throb_buf, true);
1103		if ((g->toolbar->descriptor) && (g->toolbar->descriptor->throbber_redraw))
1104			ro_gui_force_redraw_icon(g->toolbar->toolbar_handle,
1105					ICON_TOOLBAR_THROBBER);
1106	}
1107}
1108
1109/**
1110 * set favicon
1111 */
1112void gui_window_set_icon(struct gui_window *g, hlcache_handle *icon)
1113{	
1114}
1115
1116/**
1117* set gui display of a retrieved favicon representing the search provider
1118* \param ico may be NULL for local calls; then access current cache from
1119* search_web_ico()
1120*/
1121void gui_window_set_search_ico(hlcache_handle *ico)
1122{
1123}
1124
1125/**
1126 * Place the caret in a browser window.
1127 *
1128 * \param  g	   window with caret
1129 * \param  x	   coordinates of caret
1130 * \param  y	   coordinates of caret
1131 * \param  height  height of caret
1132 */
1133
1134void gui_window_place_caret(struct gui_window *g, int x, int y, int height)
1135{
1136	os_error *error;
1137
1138	error = xwimp_set_caret_position(g->window, -1,
1139			x * 2 * g->bw->scale,
1140			-(y + height) * 2 * g->bw->scale,
1141			height * 2 * g->bw->scale, -1);
1142	if (error) {
1143		LOG(("xwimp_set_caret_position: 0x%x: %s",
1144				error->errnum, error->errmess));
1145		warn_user("WimpError", error->errmess);
1146	}
1147}
1148
1149
1150/**
1151 * Remove the caret, if present.
1152 *
1153 * \param  g	   window with caret
1154 */
1155
1156void gui_window_remove_caret(struct gui_window *g)
1157{
1158	wimp_caret caret;
1159	os_error *error;
1160
1161	error = xwimp_get_caret_position(&caret);
1162	if (error) {
1163		LOG(("xwimp_get_caret_position: 0x%x: %s",
1164				error->errnum, error->errmess));
1165		warn_user("WimpError", error->errmess);
1166		return;
1167	}
1168
1169	if (caret.w != g->window)
1170		/* we don't have the caret: do nothing */
1171		return;
1172
1173	/* hide caret, but keep input focus */
1174	gui_window_place_caret(g, -100, -100, 0);
1175}
1176
1177
1178/**
1179 * Called when the gui_window has new content.
1180 *
1181 * \param  g  the gui_window that has new content
1182 */
1183
1184void gui_window_new_content(struct gui_window *g)
1185{
1186	ro_gui_menu_objects_moved();
1187	ro_gui_prepare_navigate(g);
1188	ro_gui_dialog_close_persistent(g->window);
1189}
1190
1191
1192/**
1193 * Starts drag scrolling of a browser window
1194 *
1195 * \param gw  gui window
1196 */
1197
1198bool gui_window_scroll_start(struct gui_window *g)
1199{
1200	wimp_window_info_base info;
1201	wimp_pointer pointer;
1202	os_error *error;
1203	wimp_drag drag;
1204	int height;
1205	int width;
1206
1207	error = xwimp_get_pointer_info(&pointer);
1208	if (error) {
1209		LOG(("xwimp_get_pointer_info 0x%x : %s",
1210				error->errnum, error->errmess));
1211		warn_user("WimpError", error->errmess);
1212		return false;
1213	}
1214
1215	info.w = g->window;
1216	error = xwimp_get_window_info_header_only((wimp_window_info*)&info);
1217	if (error) {
1218		LOG(("xwimp_get_window_state: 0x%x : %s",
1219				error->errnum, error->errmess));
1220		warn_user("WimpError", error->errmess);
1221		return false;
1222	}
1223
1224	width  = info.extent.x1 - info.extent.x0;
1225	height = info.extent.y1 - info.extent.y0;
1226
1227	drag.type = wimp_DRAG_USER_POINT;
1228	drag.bbox.x1 = pointer.pos.x + info.xscroll;
1229	drag.bbox.y0 = pointer.pos.y + info.yscroll;
1230	drag.bbox.x0 = drag.bbox.x1 - (width  - (info.visible.x1 - info.visible.x0));
1231	drag.bbox.y1 = drag.bbox.y0 + (height - (info.visible.y1 - info.visible.y0));
1232
1233	if (g->toolbar) {
1234		int tbar_height = ro_gui_theme_toolbar_full_height(g->toolbar);
1235		drag.bbox.y0 -= tbar_height;
1236		drag.bbox.y1 -= tbar_height;
1237	}
1238
1239	error = xwimp_drag_box(&drag);
1240	if (error) {
1241		LOG(("xwimp_drag_box: 0x%x : %s",
1242				error->errnum, error->errmess));
1243		warn_user("WimpError", error->errmess);
1244		return false;
1245	}
1246
1247	gui_track_gui_window = g;
1248	gui_current_drag_type = GUI_DRAG_SCROLL;
1249	return true;
1250}
1251
1252
1253/**
1254 * Platform-dependent part of starting a box scrolling operation,
1255 * for frames and textareas.
1256 *
1257 * \param  x0  minimum x ordinate of box relative to mouse pointer
1258 * \param  y0  minimum y ordinate
1259 * \param  x1  maximum x ordinate
1260 * \param  y1  maximum y ordinate
1261 * \return true iff succesful
1262 */
1263
1264bool gui_window_box_scroll_start(struct gui_window *g, int x0, int y0, int x1, int y1)
1265{
1266	wimp_pointer pointer;
1267	os_error *error;
1268	wimp_drag drag;
1269
1270	error = xwimp_get_pointer_info(&pointer);
1271	if (error) {
1272		LOG(("xwimp_get_pointer_info 0x%x : %s",
1273				error->errnum, error->errmess));
1274		warn_user("WimpError", error->errmess);
1275		return false;
1276	}
1277
1278	drag.type = wimp_DRAG_USER_POINT;
1279	drag.bbox.x0 = pointer.pos.x + (int)(x0 * 2 * g->bw->scale);
1280	drag.bbox.y0 = pointer.pos.y + (int)(y0 * 2 * g->bw->scale);
1281	drag.bbox.x1 = pointer.pos.x + (int)(x1 * 2 * g->bw->scale);
1282	drag.bbox.y1 = pointer.pos.y + (int)(y1 * 2 * g->bw->scale);
1283
1284	error = xwimp_drag_box(&drag);
1285	if (error) {
1286		LOG(("xwimp_drag_box: 0x%x : %s",
1287				error->errnum, error->errmess));
1288		warn_user("WimpError", error->errmess);
1289		return false;
1290	}
1291
1292	gui_current_drag_type = GUI_DRAG_SCROLL;
1293	return true;
1294}
1295
1296
1297/**
1298 * Starts drag resizing of a browser frame
1299 *
1300 * \param gw  gui window
1301 */
1302
1303bool gui_window_frame_resize_start(struct gui_window *g)
1304{
1305	wimp_pointer pointer;
1306	os_error *error;
1307	wimp_drag drag;
1308	int x0, y0, x1, y1;
1309	int row = -1, col = -1, i, toolbar_height = 0;
1310	struct browser_window *top, *bw, *parent;
1311	wimp_window_state state;
1312
1313	/* get the maximum drag box (collapse all surrounding frames */
1314	bw = g->bw;
1315	parent = bw->parent;
1316	x0 = bw->x0;
1317	y0 = bw->y0;
1318	x1 = bw->x1;
1319	y1 = bw->y1;
1320	for (i = 0; i < (parent->cols * parent->rows); i++) {
1321		  if (&parent->children[i] == bw) {
1322			col = i % parent->cols;
1323			row = i / parent->cols;
1324		  }
1325	}
1326	assert((row >= 0) && (col >= 0));
1327
1328	if (bw->drag_resize_left)
1329		x0 = parent->children[row * parent->cols + (col - 1)].x0;
1330	if (bw->drag_resize_right)
1331		x1 = parent->children[row * parent->cols + (col + 1)].x1;
1332	if (bw->drag_resize_up)
1333		y0 = parent->children[(row - 1) * parent->cols + col].y0;
1334	if (bw->drag_resize_down)
1335		y1 = parent->children[(row + 1) * parent->cols + col].y1;
1336
1337	/* convert to screen co-ordinates */
1338	top = browser_window_owner(bw);
1339	if (top->window->toolbar)
1340		toolbar_height = ro_gui_theme_toolbar_full_height(top->window->toolbar);
1341	state.w = top->window->window;
1342	error = xwimp_get_window_state(&state);
1343	if (error) {
1344		LOG(("xwimp_get_window_state: 0x%x: %s",
1345				error->errnum, error->errmess));
1346		warn_user("WimpError", error->errmess);
1347		return false;
1348	}
1349	x0 = state.visible.x0 + x0 * 2;
1350	y0 = state.visible.y1 - y0 * 2 - toolbar_height;
1351	x1 = state.visible.x0 + x1 * 2 - 1;
1352	y1 = state.visible.y1 - y1 * 2 - toolbar_height - 1;
1353
1354	/* get the pointer position */
1355	error = xwimp_get_pointer_info(&pointer);
1356	if (error) {
1357		LOG(("xwimp_get_pointer_info 0x%x : %s",
1358				error->errnum, error->errmess));
1359		warn_user("WimpError", error->errmess);
1360		return false;
1361	}
1362
1363	/* stop dragging in directions we can't extend */
1364	if (!(bw->drag_resize_left || bw->drag_resize_right)) {
1365		x0 = pointer.pos.x;
1366		x1 = pointer.pos.x;
1367	}
1368	if (!(bw->drag_resize_up || bw->drag_resize_down)) {
1369		y0 = pointer.pos.y;
1370		y1 = pointer.pos.y;
1371	}
1372
1373	/* start the drag */
1374	drag.type = wimp_DRAG_USER_POINT;
1375	drag.bbox.x0 = x0;
1376	drag.bbox.y0 = y1;
1377	drag.bbox.x1 = x1;
1378	drag.bbox.y1 = y0;
1379
1380	error = xwimp_drag_box(&drag);
1381	if (error) {
1382		LOG(("xwimp_drag_box: 0x%x : %s",
1383				error->errnum, error->errmess));
1384		warn_user("WimpError", error->errmess);
1385		return false;
1386	}
1387
1388	/* we may not be the window the pointer is currently over */
1389	gui_track_gui_window = bw->window;
1390	gui_current_drag_type = GUI_DRAG_FRAME;
1391	return true;
1392}
1393
1394
1395/**
1396 * Save the specified content as a link.
1397 *
1398 * \param  g  gui_window containing the content
1399 * \param  c  the content to save
1400 */
1401void gui_window_save_link(struct gui_window *g, const char *url, 
1402		const char *title)
1403{
1404	ro_gui_save_prepare(GUI_SAVE_LINK_URL, NULL, NULL, url, title);
1405	ro_gui_dialog_open_persistent(g->window, dialog_saveas, true);
1406}
1407
1408
1409/**
1410 * Set the scale setting of a window
1411 *
1412 * \param  g	  gui window
1413 * \param  scale  scale value (1.0 == normal scale)
1414 */
1415
1416void gui_window_set_scale(struct gui_window *g, float scale)
1417{
1418	ro_gui_dialog_update_zoom(g);
1419}
1420
1421
1422/**
1423 * Redraws the content for all windows.
1424 */
1425
1426void ro_gui_window_redraw_all(void)
1427{
1428	struct gui_window *g;
1429	for (g = window_list; g; g = g->next)
1430		gui_window_redraw_window(g);
1431}
1432
1433/**
1434 * Handle a Redraw_Window_Request for a browser window.
1435 */
1436
1437void ro_gui_window_redraw(wimp_draw *redraw)
1438{
1439	osbool more;
1440	struct gui_window *g = (struct gui_window *)ro_gui_wimp_event_get_user_data(redraw->w);
1441	float scale = g->bw->scale;
1442	hlcache_handle *h = g->bw->current_content;
1443	os_error *error;
1444
1445	/*	Handle no content quickly
1446	*/
1447	if (!h) {
1448		ro_gui_user_redraw(redraw, true, os_COLOUR_WHITE);
1449		return;
1450	}
1451
1452	/* We can't render locked content as it is being in the process of
1453	   being transformed.  We won't update anything (i.e. leaving
1454	   window area as is) instead of showing random data in case of
1455	   buffered redraw.  */
1456	if (content_is_locked(h))
1457		return;
1458
1459	plot = ro_plotters;
1460	ro_plot_set_scale(scale);
1461	ro_gui_current_redraw_gui = g;
1462	current_redraw_browser = g->bw;
1463
1464	/* HTML rendering handles scale itself */
1465	if (content_get_type(h) == CONTENT_HTML)
1466		scale = 1;
1467
1468	error = xwimp_redraw_window(redraw, &more);
1469	if (error) {
1470		LOG(("xwimp_redraw_window: 0x%x: %s",
1471				error->errnum, error->errmess));
1472		warn_user("WimpError", error->errmess);
1473		return;
1474	}
1475	while (more) {
1476		int clip_x0, clip_y0, clip_x1, clip_y1;
1477
1478		/* OS's redraw request coordinates are in screen coordinates,
1479		 * with an origin at the bottom left of the screen.
1480		 * Find the coordinate of the top left of the document in terms
1481		 * of OS screen coordinates.
1482		 * NOTE: OS units are 2 per px. */
1483		ro_plot_origin_x = redraw->box.x0 - redraw->xscroll;
1484		ro_plot_origin_y = redraw->box.y1 - redraw->yscroll;
1485
1486		/* Convert OS redraw rectangle request coordinates into NetSurf
1487		 * coordinates. NetSurf coordinates have origin at top left of
1488		 * document and units are in px. */
1489		clip_x0 = (redraw->clip.x0 - ro_plot_origin_x) / 2; /* left   */
1490		clip_y0 = (ro_plot_origin_y - redraw->clip.y1) / 2; /* top    */
1491		clip_x1 = (redraw->clip.x1 - ro_plot_origin_x) / 2; /* right  */
1492		clip_y1 = (ro_plot_origin_y - redraw->clip.y0) / 2; /* bottom */
1493
1494		if (ro_gui_current_redraw_gui->option.buffer_everything)
1495			ro_gui_buffer_open(redraw);
1496
1497		/* Set up NetSurf's plotters with current clip rectangle */
1498		plot.clip(clip_x0, clip_y0, clip_x1, clip_y1);
1499
1500		if (content_get_type(h) != CONTENT_HTML)
1501                    plot.rectangle(clip_x0, clip_y0, clip_x1, clip_y1,
1502                    		plot_style_fill_white);
1503
1504		/* Redraw the clip rectangle area of the content */
1505		content_redraw(h, 0, 0,
1506				content_get_width(h) * scale,
1507				content_get_height(h) * scale,
1508				clip_x0, clip_y0, clip_x1, clip_y1,
1509				g->bw->scale,
1510				0xFFFFFF);
1511
1512		if (ro_gui_current_redraw_gui->option.buffer_everything)
1513			ro_gui_buffer_close();
1514
1515		/* Check to see if there are more rectangles to draw and
1516		 * get next one */
1517		error = xwimp_get_rectangle(redraw, &more);
1518		/* RISC OS 3.7 returns an error here if enough buffer was
1519		   claimed to cause a new dynamic area to be created. It
1520		   doesn't actually stop anything working, so we mask it out
1521		   for now until a better fix is found. This appears to be a
1522		   bug in RISC OS. */
1523		if (error && !(ro_gui_current_redraw_gui->
1524				option.buffer_everything &&
1525				error->errnum == error_WIMP_GET_RECT)) {
1526			LOG(("xwimp_get_rectangle: 0x%x: %s",
1527					error->errnum, error->errmess));
1528			warn_user("WimpError", error->errmess);
1529			ro_gui_current_redraw_gui = NULL;
1530			current_redraw_browser = NULL;
1531			return;
1532		}
1533	}
1534	ro_gui_current_redraw_gui = NULL;
1535	current_redraw_browser = NULL;
1536}
1537
1538
1539
1540/**
1541 * Remove all pending update boxes for a window
1542 *
1543 * \param  g   gui_window
1544 */
1545void ro_gui_window_remove_update_boxes(struct gui_window *g) {
1546	struct update_box *cur;
1547
1548	for (cur = pending_updates; cur != NULL; cur = cur->next) {
1549		if (cur->g == g)
1550			cur->g = NULL;
1551	}
1552}
1553
1554
1555/**
1556 * Redraw any pending update boxes.
1557 */
1558void ro_gui_window_update_boxes(void) {
1559	hlcache_handle *h;
1560	osbool more;
1561	bool clear_background = false;
1562	bool use_buffer;
1563	wimp_draw update;
1564	int clip_x0, clip_y0, clip_x1, clip_y1;
1565	os_error *error;
1566	struct update_box *cur;
1567	struct gui_window *g;
1568	const union content_msg_data *data;
1569
1570	for (cur = pending_updates; cur != NULL; cur = cur->next) {
1571		g = cur->g;
1572		if (!g)
1573			continue;
1574		h = g->bw->current_content;
1575		data = &cur->data;
1576		use_buffer = cur->use_buffer;
1577		if (!h)
1578			continue;
1579
1580		update.w = g->window;
1581		update.box.x0 = cur->x0;
1582		update.box.y0 = cur->y0;
1583		update.box.x1 = cur->x1;
1584		update.box.y1 = cur->y1;
1585
1586		error = xwimp_update_window(&update, &more);
1587		if (error) {
1588			LOG(("xwimp_update_window: 0x%x: %s",
1589					error->errnum, error->errmess));
1590			warn_user("WimpError", error->errmess);
1591			continue;
1592		}
1593
1594		/* Set the current redraw gui_window to get options from */
1595		ro_gui_current_redraw_gui = g;
1596		current_redraw_browser = g->bw;
1597
1598		plot = ro_plotters;
1599		ro_plot_origin_x = update.box.x0 - update.xscroll;
1600		ro_plot_origin_y = update.box.y1 - update.yscroll;
1601		ro_plot_set_scale(g->bw->scale);
1602
1603		/* We should clear the background, except for HTML. */
1604		if (content_get_type(h) != CONTENT_HTML)
1605			clear_background = true;
1606
1607		while (more) {
1608			clip_x0 = (update.clip.x0 - ro_plot_origin_x) / 2;
1609			clip_y0 = (ro_plot_origin_y - update.clip.y1) / 2;
1610			clip_x1 = (update.clip.x1 - ro_plot_origin_x) / 2;
1611			clip_y1 = (ro_plot_origin_y - update.clip.y0) / 2;
1612
1613			if (use_buffer)
1614				ro_gui_buffer_open(&update);
1615
1616			if (clear_background) {
1617				error = xcolourtrans_set_gcol(
1618						os_COLOUR_WHITE,
1619						colourtrans_SET_BG_GCOL,
1620						os_ACTION_OVERWRITE, 0,
1621						0);
1622				if (error) {
1623					LOG(("xcolourtrans_set_gcol: "
1624							"0x%x: %s",
1625							error->errnum,
1626							error->errmess));
1627					warn_user("MiscError",
1628							error->errmess);
1629				}
1630				os_clg();
1631			}
1632
1633			content_redraw(h, 0, 0,
1634					content_get_width(h),
1635					content_get_height(h),
1636					clip_x0, clip_y0,
1637					clip_x1, clip_y1,
1638					g->bw->scale,
1639					0xFFFFFF);
1640
1641			if (use_buffer)
1642				ro_gui_buffer_close();
1643			error = xwimp_get_rectangle(&update, &more);
1644			/* RISC OS 3.7 returns an error here if enough buffer
1645			 * was claimed to cause a new dynamic area to be
1646			 * created. It doesn't actually stop anything working,
1647			 * so we mask it out for now until a better fix is
1648			 * found. This appears to be a bug in RISC OS. */
1649			if (error && !(use_buffer &&
1650					error->errnum == error_WIMP_GET_RECT)) {
1651				LOG(("xwimp_get_rectangle: 0x%x: %s",
1652						error->errnum, error->errmess));
1653				warn_user("WimpError", error->errmess);
1654				ro_gui_current_redraw_gui = NULL;
1655				current_redraw_browser = NULL;
1656				continue;
1657			}
1658		}
1659
1660		/* Reset the current redraw gui_window to prevent
1661		 * thumbnails from retaining options */
1662		ro_gui_current_redraw_gui = NULL;
1663		current_redraw_browser = NULL;
1664	}
1665	while (pending_updates) {
1666		cur = pending_updates;
1667		pending_updates = pending_updates->next;
1668		free(cur);
1669	}
1670
1671}
1672
1673
1674/**
1675 * Launch a new url in the given window.
1676 *
1677 * \param  g	gui_window to update
1678 * \param  url  url to be launched
1679 */
1680
1681void ro_gui_window_launch_url(struct gui_window *g, const char *url)
1682{
1683	url_func_result res;
1684	char *url_norm;
1685
1686	ro_gui_url_complete_close(NULL, 0);
1687	res = url_normalize(url, &url_norm);
1688	if (res == URL_FUNC_OK) {
1689		gui_window_set_url(g, url_norm);
1690		browser_window_go(g->bw, url_norm, 0, true);
1691		global_history_add_recent(url_norm);
1692		free(url_norm);
1693	}
1694}
1695
1696
1697/**
1698 * Forces all windows to be set to the current theme
1699 */
1700void ro_gui_window_update_theme(void) {
1701	struct gui_window *g;
1702	for (g = window_list; g; g = g->next) {
1703		if (g->toolbar) {
1704			if (g->toolbar->editor)
1705				if (!ro_gui_theme_update_toolbar(NULL, g->toolbar->editor))
1706					g->toolbar->editor = NULL;
1707			if (!ro_gui_theme_update_toolbar(NULL, g->toolbar)) {
1708				ro_gui_theme_destroy_toolbar(g->toolbar);
1709				g->toolbar = NULL;
1710			}
1711			ro_gui_theme_toolbar_editor_sync(g->toolbar);
1712			gui_window_update_extent(g);
1713		}
1714	}
1715}
1716
1717
1718/**
1719 * Updates a windows extent.
1720 *
1721 * \param  g  the gui_window to update
1722 * \param  width  the minimum width, or -1 to use window width
1723 * \param  height  the minimum height, or -1 to use window height
1724 */
1725
1726void gui_window_set_extent(struct gui_window *g, int width, int height)
1727{
1728  	int screen_width;
1729	int toolbar_height = 0;
1730	hlcache_handle *h;
1731	wimp_window_state state;
1732	os_error *error;
1733
1734	h = g->bw->current_content;
1735	if (g->toolbar)
1736		toolbar_height = ro_gui_theme_toolbar_full_height(g->toolbar);
1737
1738	/* get the current state */
1739	if ((height == -1) || (width == -1)) {
1740		state.w = g->window;
1741		error = xwimp_get_window_state(&state);
1742		if (error) {
1743			LOG(("xwimp_get_window_state: 0x%x: %s",
1744					error->errnum, error->errmess));
1745			warn_user("WimpError", error->errmess);
1746			return;
1747		}
1748		if (width == -1)
1749			width = state.visible.x1 - state.visible.x0;
1750		if (height == -1) {
1751			height = state.visible.y1 - state.visible.y0;
1752			height -= toolbar_height;
1753		}
1754	}
1755
1756	/* the top-level framed window is a total pain. to get it to maximise
1757	 * to the top of the screen we need to fake it having a suitably large
1758	 * extent */
1759	if (g->bw->children &&
1760			(g->bw->browser_window_type == BROWSER_WINDOW_NORMAL)) {
1761		ro_gui_screen_size(&screen_width, &height);
1762		if (g->toolbar)
1763			height -= ro_gui_theme_toolbar_full_height(g->toolbar);
1764		height -= ro_get_hscroll_height(g->window);
1765		height -= ro_get_title_height(g->window);
1766	}
1767	if (h) {
1768		width = max(width, content_get_width(h) * 2 * g->bw->scale);
1769		height = max(height, content_get_height(h) * 2 * g->bw->scale);
1770	}
1771	os_box extent = { 0, -height, width, toolbar_height };
1772	error = xwimp_set_extent(g->window, &extent);
1773	if (error) {
1774		LOG(("xwimp_set_extent: 0x%x: %s",
1775				error->errnum, error->errmess));
1776		warn_user("WimpError", error->errmess);
1777		return;
1778	}
1779}
1780
1781
1782/**
1783 * Open a window using the given wimp_open, handling toolbars and resizing.
1784 */
1785
1786void ro_gui_window_open(wimp_open *open)
1787{
1788	struct gui_window *g = (struct gui_window *)ro_gui_wimp_event_get_user_data(open->w);
1789	int width = open->visible.x1 - open->visible.x0;
1790	int height = open->visible.y1 - open->visible.y0;
1791	int size, fheight, fwidth, toolbar_height = 0;
1792	bool no_vscroll, no_hscroll;
1793	float new_scale = 0;
1794	hlcache_handle *h;
1795	wimp_window_state state;
1796	os_error *error;
1797	wimp_w parent;
1798	bits linkage;
1799
1800	if (open->next == wimp_TOP && g->iconise_icon >= 0) {
1801		/* window is no longer iconised, release its sprite number */
1802		iconise_used[g->iconise_icon] = false;
1803		g->iconise_icon = -1;
1804	}
1805
1806	h = g->bw->current_content;
1807
1808	/* get the current flags/nesting state */
1809	state.w = g->window;
1810	error = xwimp_get_window_state_and_nesting(&state, &parent, &linkage);
1811	if (error) {
1812		LOG(("xwimp_get_window_state: 0x%x: %s",
1813				error->errnum, error->errmess));
1814		warn_user("WimpError", error->errmess);
1815		return;
1816	}
1817
1818	/* account for toolbar height, if present */
1819	if (g->toolbar)
1820		toolbar_height = ro_gui_theme_toolbar_full_height(g->toolbar);
1821	height -= toolbar_height;
1822
1823	/* work with the state from now on so we can modify flags */
1824	state.visible = open->visible;
1825	state.xscroll = open->xscroll;
1826	state.yscroll = open->yscroll;
1827	state.next = open->next;
1828
1829	/* handle 'auto' scroll bars' and non-fitting scrollbar removal */
1830	if ((g->bw->scrolling == SCROLLING_AUTO) ||
1831			(g->bw->scrolling == SCROLLING_YES)) {
1832		/* windows lose scrollbars when containing a frameset */
1833		no_hscroll = (g->bw->children &&
1834				(g->bw->browser_window_type !=
1835				BROWSER_WINDOW_NORMAL));
1836		no_vscroll = g->bw->children;
1837
1838		/* hscroll */
1839		size = ro_get_hscroll_height(NULL);
1840		if (g->bw->border)
1841			size -= 2;
1842		fheight = height;
1843		if (state.flags & wimp_WINDOW_HSCROLL)
1844			fheight += size;
1845		if ((!no_hscroll) &&
1846				((fheight > size) ||
1847					(g->bw->browser_window_type ==
1848					BROWSER_WINDOW_NORMAL)) &&
1849				((h && width < content_get_width(h) *
1850						2 * g->bw->scale) ||
1851					(g->bw->browser_window_type ==
1852					BROWSER_WINDOW_NORMAL))) {
1853			if (!(state.flags & wimp_WINDOW_HSCROLL)) {
1854				height -= size;
1855				state.visible.y0 += size;
1856				if (h) {
1857					g->bw->reformat_pending = true;
1858					browser_reformat_pending = true;
1859				}
1860			}
1861			state.flags |= wimp_WINDOW_HSCROLL;
1862		} else {
1863			if (state.flags & wimp_WINDOW_HSCROLL) {
1864				height += size;
1865				state.visible.y0 -= size;
1866				if (h) {
1867					g->bw->reformat_pending = true;
1868					browser_reformat_pending = true;
1869				}
1870			}
1871			state.flags &= ~wimp_WINDOW_HSCROLL;
1872		}
1873
1874		/* vscroll */
1875		size = ro_get_vscroll_width(NULL);
1876		if (g->bw->border)
1877			size -= 2;
1878		fwidth = width;
1879		if (state.flags & wimp_WINDOW_VSCROLL)
1880			fwidth += size;
1881		if ((!no_vscroll) &&
1882				((fwidth > size) ||
1883					(g->bw->browser_window_type ==
1884					BROWSER_WINDOW_NORMAL)) &&
1885				((h && height < content_get_height(h) *
1886						2 * g->bw->scale) ||
1887					(g->bw->scrolling == SCROLLING_YES))) {
1888			if (!(state.flags & wimp_WINDOW_VSCROLL)) {
1889				width -= size;
1890				state.visible.x1 -= size;
1891				if (h) {
1892					g->bw->reformat_pending = true;
1893					browser_reformat_pending = true;
1894				}
1895			}
1896			state.flags |= wimp_WINDOW_VSCROLL;
1897		} else {
1898			if (state.flags & wimp_WINDOW_VSCROLL) {
1899				width += size;
1900				state.visible.x1 += size;
1901				if (h) {
1902					g->bw->reformat_pending = true;
1903					browser_reformat_pending = true;
1904				}
1905			}
1906			state.flags &= ~wimp_WINDOW_VSCROLL;
1907		}
1908	}
1909
1910	/* reformat or change extent if necessary */
1911	if ((h) && (g->old_width != width || g->old_height != height)) {
1912	  	/* Ctrl-resize of a top-level window scales the content size */
1913		if ((g->old_width > 0) && (g->old_width != width) &&
1914				(!g->bw->parent) &&
1915				(ro_gui_ctrl_pressed()))
1916			new_scale = (g->bw->scale * width) / g->old_width;
1917		g->bw->reformat_pending = true;
1918		browser_reformat_pending = true;
1919	}
1920	if (g->update_extent || g->old_width != width ||
1921			g->old_height != height) {
1922		g->old_width = width;
1923		g->old_height = height;
1924		g->update_extent = false;
1925		gui_window_set_extent(g, width, height);
1926	}
1927
1928	/* first resize stops any flickering by making the URL window on top */
1929	ro_gui_url_complete_resize(g, PTR_WIMP_OPEN(&state));
1930
1931	error = xwimp_open_window_nested_with_flags(&state, parent, linkage);
1932	if (error) {
1933		LOG(("xwimp_open_window: 0x%x: %s",
1934				error->errnum, error->errmess));
1935		warn_user("WimpError", error->errmess);
1936		return;
1937	}
1938
1939	/* update the toolbar */
1940	if (g->status_bar)
1941		ro_gui_status_bar_resize(g->status_bar);
1942	if (g->toolbar) {
1943		ro_gui_theme_process_toolbar(g->toolbar, -1);
1944		/* second resize updates to the new URL bar width */
1945		ro_gui_url_complete_resize(g, open);
1946	}
1947
1948	/* set the new scale from a ctrl-resize. this must be done at the end as
1949	 * it may cause a frameset recalculation based on the new window size.
1950	 */
1951	if (new_scale > 0)
1952		browser_window_set_scale(g->bw, new_scale, true);
1953}
1954
1955
1956/**
1957 * Handle wimp closing event
1958 */
1959void ro_gui_window_close(wimp_w w) {
1960	struct gui_window *g = (struct gui_window *)ro_gui_wimp_event_get_user_data(w);
1961	wimp_pointer pointer;
1962	os_error *error;
1963	char *temp_name, *r;
1964	char *filename;
1965	hlcache_handle *h = NULL;
1966	bool destroy;
1967
1968	error = xwimp_get_pointer_info(&pointer);
1969	if (error) {
1970		LOG(("xwimp_get_pointer_info: 0x%x: %s",
1971				error->errnum, error->errmess));
1972		warn_user("WimpError", error->errmess);
1973		return;
1974	}
1975	if (g->bw)
1976		h = g->bw->current_content;
1977	if (pointer.buttons & wimp_CLICK_ADJUST) {
1978		destroy = !ro_gui_shift_pressed();
1979		filename = (h && content_get_url(h)) ?
1980				url_to_path(content_get_url(h)) : NULL;
1981		if (filename) {
1982			temp_name = malloc(strlen(filename) + 32);
1983			if (temp_name) {
1984				sprintf(temp_name, "Filer_OpenDir %s",
1985						filename);
1986				r = temp_name + strlen(temp_name);
1987				while (r > temp_name) {
1988					if (*r == '.') {
1989						*r = '\0';
1990						break;
1991					}
1992					r--;
1993				}
1994				error = xos_cli(temp_name);
1995				if (error) {
1996					LOG(("xos_cli: 0x%x: %s",
1997							error->errnum,
1998							error->errmess));
1999					warn_user("MiscError", error->errmess);
2000					return;
2001				}
2002				free(temp_name);
2003			}
2004			free(filename);
2005		} else {
2006			/* this is pointless if we are about to close the
2007			 * window */
2008			if (!destroy)
2009				ro_gui_menu_handle_action(w,
2010						BROWSER_NAVIGATE_UP, true);
2011		}
2012	}
2013	else
2014		destroy = true;
2015
2016	if (destroy)
2017		browser_window_destroy(g->bw);
2018}
2019
2020
2021/**
2022 * Destroy all browser windows.
2023 */
2024
2025void ro_gui_window_quit(void)
2026{
2027	struct gui_window *cur;
2028
2029	while (window_list) {
2030		cur = window_list;
2031		window_list = window_list->next;
2032
2033		/* framesets and iframes are destroyed by their parents */
2034		if (!cur->bw->parent)
2035			browser_window_destroy(cur->bw);
2036	}
2037}
2038
2039
2040/**
2041 * Animate the "throbbers" of all browser windows.
2042 */
2043
2044void ro_gui_throb(void)
2045{
2046	os_t t;
2047	struct gui_window *g, *top_g;
2048	struct browser_window *top;
2049	char throb_buf[12];
2050
2051	xos_read_monotonic_time(&t);
2052
2053	for (g = window_list; g; g = g->next) {
2054		if (!g->bw->throbbing)
2055			continue;
2056		for (top = g->bw; top->parent; top = top->parent);
2057		top_g = top->window;
2058		if (!top_g->toolbar || !top_g->toolbar->display_throbber ||
2059				!top_g->toolbar->descriptor ||
2060				!top_g->toolbar->descriptor->theme ||
2061				(t < top_g->throbtime + 10))
2062			continue;
2063		top_g->throbtime = t;
2064		top_g->throbber++;
2065		if (top_g->toolbar->descriptor->theme->throbber_frames < top_g->throbber)
2066			top_g->throbber = 1;
2067		sprintf(throb_buf, "throbber%i", top_g->throbber);
2068		ro_gui_set_icon_string(top_g->toolbar->toolbar_handle,
2069				ICON_TOOLBAR_THROBBER, throb_buf, true);
2070		if (top_g->toolbar->descriptor->throbber_redraw)
2071			ro_gui_force_redraw_icon(top_g->toolbar->toolbar_handle,
2072					ICON_TOOLBAR_THROBBER);
2073	}
2074}
2075
2076
2077/**
2078 * Convert a RISC OS window handle to a gui_window.
2079 *
2080 * \param  w  RISC OS window handle
2081 * \return  pointer to a structure if found, 0 otherwise
2082 */
2083
2084struct gui_window *ro_gui_window_lookup(wimp_w window)
2085{
2086	struct gui_window *g;
2087	for (g = window_list; g; g = g->next)
2088		if (g->window == window)
2089			return g;
2090	return 0;
2091}
2092
2093
2094/**
2095 * Convert a toolbar RISC OS window handle to a gui_window.
2096 *
2097 * \param  w  RISC OS window handle of a toolbar
2098 * \return  pointer to a structure if found, 0 otherwise
2099 */
2100
2101struct gui_window *ro_gui_toolbar_lookup(wimp_w window)
2102{
2103	struct gui_window *g;
2104	for (g = window_list; g; g = g->next) {
2105		if ((g->toolbar) && ((g->toolbar->toolbar_handle == window) ||
2106				((g->toolbar->editor) &&
2107				(g->toolbar->editor->toolbar_handle == window))))
2108			return g;
2109	}
2110	return 0;
2111}
2112
2113
2114/**
2115 * Handle pointer movements in a browser window.
2116 *
2117 * \param  g	    browser window that the pointer is in
2118 * \param  pointer  new mouse position
2119 */
2120
2121void ro_gui_window_mouse_at(struct gui_window *g, wimp_pointer *pointer)
2122{
2123	os_coord pos;
2124
2125	if (ro_gui_window_to_window_pos(g, pointer->pos.x, pointer->pos.y, &pos))
2126		browser_window_mouse_track(g->bw,
2127				ro_gui_mouse_drag_state(pointer->buttons),
2128				pos.x, pos.y);
2129}
2130
2131
2132/**
2133 * Process Mouse_Click events in a toolbar.
2134 */
2135
2136bool ro_gui_toolbar_click(wimp_pointer *pointer)
2137{
2138	struct gui_window *g = ro_gui_toolbar_lookup(pointer->w);
2139	struct browser_window *new_bw;
2140
2141	/* toolbars in the options window have no gui_window */
2142	if (!g)
2143		return true;
2144
2145	/* try to close url-completion */
2146	ro_gui_url_complete_close(g, pointer->i);
2147
2148	/*	Handle Menu clicks */
2149	if (pointer->buttons == wimp_CLICK_MENU) {
2150		ro_gui_menu_create(browser_toolbar_menu, pointer->pos.x,
2151				pointer->pos.y, g->window);
2152		return true;
2153	}
2154
2155	/*	Handle toolbar edits */
2156	if ((g->toolbar->editor) && (pointer->i < ICON_TOOLBAR_URL)) {
2157		ro_gui_theme_toolbar_editor_click(g->toolbar, pointer);
2158		return true;
2159	}
2160
2161	/*	Handle the buttons appropriately */
2162	switch (pointer->i) {
2163		case ICON_TOOLBAR_BACK:
2164			if (pointer->buttons == wimp_CLICK_ADJUST) {
2165				new_bw = browser_window_create(NULL,
2166						g->bw, NULL, false, false);
2167				ro_gui_menu_handle_action(
2168						new_bw->window->window,
2169						BROWSER_NAVIGATE_BACK, true);
2170			} else {
2171				ro_gui_menu_handle_action(g->window,
2172						BROWSER_NAVIGATE_BACK, true);
2173			}
2174			break;
2175
2176		case ICON_TOOLBAR_FORWARD:
2177			if (pointer->buttons == wimp_CLICK_ADJUST) {
2178				new_bw = browser_window_create(NULL,
2179						g->bw, NULL, false, false);
2180				ro_gui_menu_handle_action(
2181						new_bw->window->window,
2182						BROWSER_NAVIGATE_FORWARD, true);
2183			} else {
2184				ro_gui_menu_handle_action(g->window,
2185						BROWSER_NAVIGATE_FORWARD, true);
2186			}
2187			break;
2188
2189		case ICON_TOOLBAR_STOP:
2190			ro_gui_menu_handle_action(g->window,
2191					BROWSER_NAVIGATE_STOP, true);
2192			break;
2193
2194		case ICON_TOOLBAR_RELOAD:
2195			if (pointer->buttons == wimp_CLICK_SELECT)
2196				ro_gui_menu_handle_action(g->window,
2197						BROWSER_NAVIGATE_RELOAD, true);
2198			else if (pointer->buttons == wimp_CLICK_ADJUST)
2199				ro_gui_menu_handle_action(g->window,
2200						BROWSER_NAVIGATE_RELOAD_ALL,
2201						true);
2202			break;
2203
2204		case ICON_TOOLBAR_HISTORY:
2205			if (pointer->buttons == wimp_CLICK_SELECT)
2206				ro_gui_menu_handle_action(g->window,
2207						HISTORY_SHOW_LOCAL, true);
2208			else
2209				ro_gui_menu_handle_action(g->window,
2210						HISTORY_SHOW_GLOBAL, true);
2211			break;
2212		case ICON_TOOLBAR_HOME:
2213			ro_gui_menu_handle_action(g->window,
2214					BROWSER_NAVIGATE_HOME, true);
2215			break;
2216		case ICON_TOOLBAR_SEARCH:
2217			ro_gui_menu_handle_action(g->window,
2218					BROWSER_FIND_TEXT, true);
2219			break;
2220		case ICON_TOOLBAR_SCALE:
2221			ro_gui_menu_handle_action(g->window,
2222					BROWSER_SCALE_VIEW, true);
2223			break;
2224
2225		case ICON_TOOLBAR_BOOKMARK:
2226			if (pointer->buttons == wimp_CLICK_ADJUST)
2227				ro_gui_menu_handle_action(g->window,
2228						HOTLIST_ADD_URL, true);
2229			else
2230				ro_gui_menu_handle_action(g->window,
2231						HOTLIST_SHOW, true);
2232			break;
2233
2234		case ICON_TOOLBAR_SAVE:
2235			if (pointer->buttons == wimp_CLICK_ADJUST)
2236				ro_gui_menu_handle_action(g->window,
2237						BROWSER_SAVE_COMPLETE, true);
2238			else
2239				ro_gui_menu_handle_action(g->window,
2240						BROWSER_SAVE, true);
2241			break;
2242		case ICON_TOOLBAR_PRINT:
2243			ro_gui_menu_handle_action(g->window,
2244					BROWSER_PRINT, true);
2245			break;
2246		case ICON_TOOLBAR_UP:
2247			if (pointer->buttons == wimp_CLICK_ADJUST) {
2248				if (g->bw && g->bw->current_content) {
2249					hlcache_handle *h =
2250							g->bw->current_content;
2251					new_bw = browser_window_create(NULL,
2252							g->bw, NULL, false,
2253							false);
2254					/* do it without loading the content
2255					 * into the new window */
2256					ro_gui_window_navigate_up(
2257							new_bw->window,
2258							content_get_url(h));
2259				}
2260			} else {
2261				ro_gui_menu_handle_action(g->window,
2262						BROWSER_NAVIGATE_UP, true);
2263			}
2264			break;
2265		case ICON_TOOLBAR_URL:
2266			if (pointer->buttons &
2267					(wimp_DRAG_SELECT | wimp_DRAG_ADJUST)) {
2268				if (g->bw->current_content) {
2269					hlcache_handle *h =
2270							g->bw->current_content;
2271					gui_save_type save_type;
2272
2273					if (ro_gui_shift_pressed())
2274						save_type = GUI_SAVE_LINK_URL;
2275					else
2276						save_type = GUI_SAVE_LINK_TEXT;
2277
2278					ro_gui_drag_save_link(save_type,
2279							content_get_url(h),
2280							content_get_title(h),
2281							g);
2282				}
2283			}
2284			else
2285				ro_gui_url_complete_start(g);
2286			break;
2287		case ICON_TOOLBAR_SUGGEST:
2288			ro_gui_popup_menu(url_suggest_menu,
2289					g->toolbar->toolbar_handle,
2290					ICON_TOOLBAR_SUGGEST);
2291			break;
2292	}
2293	return true;
2294}
2295
2296
2297/**
2298 * Handle Mouse_Click events in a browser window.
2299 *
2300 * \param  pointer  details of mouse click
2301 * \return true if click handled, false otherwise
2302 */
2303
2304bool ro_gui_window_click(wimp_pointer *pointer)
2305{
2306	struct gui_window *g;
2307	os_coord pos;
2308
2309	g = (struct gui_window *)ro_gui_wimp_event_get_user_data(pointer->w);
2310
2311	/* try to close url-completion */
2312	ro_gui_url_complete_close(g, pointer->i);
2313
2314	/* set input focus */
2315	if (pointer->buttons == wimp_CLICK_SELECT ||
2316			pointer->buttons == wimp_CLICK_ADJUST)
2317		gui_window_place_caret(g, -100, -100, 0);
2318
2319	if (pointer->buttons == wimp_CLICK_MENU) {
2320		ro_gui_menu_create(browser_menu, pointer->pos.x, pointer->pos.y, pointer->w);
2321	} else {
2322		if (ro_gui_window_to_window_pos(g, pointer->pos.x, pointer->pos.y, &pos))
2323			browser_window_mouse_click(g->bw,
2324					ro_gui_mouse_click_state(pointer->buttons),
2325					pos.x, pos.y);
2326	}
2327	return true;
2328}
2329
2330
2331/**
2332 * Process Key_Pressed events in a browser window.
2333 */
2334
2335bool ro_gui_window_keypress(wimp_key *key)
2336{
2337	struct gui_window *g;
2338	bool toolbar;
2339	hlcache_handle *h;
2340	wimp_window_state state;
2341	int y;
2342	const char *toolbar_url;
2343	os_error *error;
2344	wimp_pointer pointer;
2345	float scale;
2346	uint32_t c = (uint32_t) key->c;
2347
2348	/* Find gui window */
2349	if ((g = ro_gui_window_lookup(key->w)) != NULL) {
2350		toolbar = false;
2351	} else if ((g = ro_gui_toolbar_lookup(key->w)) != NULL) {
2352		toolbar = true;
2353	} else {
2354		/* nothing to do with us */
2355		return false;
2356	}
2357
2358	h = g->bw->current_content;
2359
2360	error = xwimp_get_pointer_info(&pointer);
2361	if (error) {
2362		LOG(("xwimp_get_pointer_info: 0x%x: %s\n",
2363				error->errnum, error->errmess));
2364		warn_user("WimpError", error->errmess);
2365		return false;
2366	}
2367
2368	/* First send the key to the browser window, eg. form fields. */
2369	if (!toolbar) {
2370		if ((unsigned)c < 0x20 || (0x7f <= c && c <= 0x9f) ||
2371				(c & IS_WIMP_KEY)) {
2372		/* Munge control keys into unused control chars */
2373		/* We can't map onto 1->26 (reserved for ctrl+<qwerty>
2374		   That leaves 27->31 and 128->159 */
2375			switch (c & ~IS_WIMP_KEY) {
2376			case wimp_KEY_TAB: c = 9; break;
2377			case wimp_KEY_SHIFT | wimp_KEY_TAB: c = 11; break;
2378
2379			/* cursor movement keys */
2380			case wimp_KEY_HOME:
2381			case wimp_KEY_CONTROL | wimp_KEY_LEFT:
2382				c = KEY_LINE_START;
2383				break;
2384			case wimp_KEY_END:
2385				if (os_version >= RISCOS5)
2386					c = KEY_LINE_END;
2387				else
2388					c = KEY_DELETE_RIGHT;
2389				break;
2390			case wimp_KEY_CONTROL | wimp_KEY_RIGHT: c = KEY_LINE_END;   break;
2391			case wimp_KEY_CONTROL | wimp_KEY_UP:	c = KEY_TEXT_START; break;
2392			case wimp_KEY_CONTROL | wimp_KEY_DOWN:  c = KEY_TEXT_END;   break;
2393			case wimp_KEY_SHIFT | wimp_KEY_LEFT:	c = KEY_WORD_LEFT ; break;
2394			case wimp_KEY_SHIFT | wimp_KEY_RIGHT:	c = KEY_WORD_RIGHT; break;
2395			case wimp_KEY_SHIFT | wimp_KEY_UP:	c = KEY_PAGE_UP;    break;
2396			case wimp_KEY_SHIFT | wimp_KEY_DOWN:	c = KEY_PAGE_DOWN;  break;
2397			case wimp_KEY_LEFT:  c = KEY_LEFT; break;
2398			case wimp_KEY_RIGHT: c = KEY_RIGHT; break;
2399			case wimp_KEY_UP:    c = KEY_UP; break;
2400			case wimp_KEY_DOWN:  c = KEY_DOWN; break;
2401
2402			/* editing */
2403			case wimp_KEY_CONTROL | wimp_KEY_END:
2404				c = KEY_DELETE_LINE_END;
2405				break;
2406			case wimp_KEY_DELETE:
2407				if (ro_gui_ctrl_pressed())
2408					c = KEY_DELETE_LINE_START;
2409				else if (os_version < RISCOS5)
2410					c = KEY_DELETE_LEFT;
2411				break;
2412
2413			default:
2414				break;
2415			}
2416		}
2417
2418		if (!(c & IS_WIMP_KEY)) {
2419			if (browser_window_key_press(g->bw, c))
2420				return true;
2421		}
2422
2423		/* Reset c to incoming character / key code
2424		 * as we may have corrupted it above */
2425		c = (uint32_t) key->c;
2426	}
2427
2428	switch (c) {
2429		case IS_WIMP_KEY + wimp_KEY_F1:	/* Help. */
2430			return ro_gui_menu_handle_action(g->window,
2431					HELP_OPEN_CONTENTS, false);
2432
2433		case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_F1:
2434			return ro_gui_menu_handle_action(g->window,
2435					BROWSER_PAGE_INFO, false);
2436
2437		case IS_WIMP_KEY + wimp_KEY_F2:
2438			if (!g->toolbar)
2439				return false;
2440			ro_gui_url_complete_close(NULL, 0);
2441			ro_gui_set_icon_string(g->toolbar->toolbar_handle,
2442					ICON_TOOLBAR_URL, "www.", true);
2443			xwimp_set_caret_position(g->toolbar->toolbar_handle,
2444					ICON_TOOLBAR_URL, 0, 0, -1, 4);
2445			ro_gui_url_complete_start(g);
2446			ro_gui_url_complete_keypress(g, wimp_KEY_DOWN);
2447			return true;
2448
2449		case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_F2:
2450			/* Close window. */
2451			ro_gui_url_complete_close(NULL, 0);
2452			browser_window_destroy(g->bw);
2453			return true;
2454
2455		case 19:		/* Ctrl + S */
2456		case IS_WIMP_KEY + wimp_KEY_F3:
2457			return ro_gui_menu_handle_action(g->window,
2458					BROWSER_SAVE, false);
2459
2460		case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_F3:
2461			return ro_gui_menu_handle_action(g->window,
2462					BROWSER_EXPORT_TEXT, false);
2463
2464		case IS_WIMP_KEY + wimp_KEY_SHIFT + wimp_KEY_F3:
2465			return ro_gui_menu_handle_action(g->window,
2466					BROWSER_SAVE_COMPLETE, false);
2467
2468		case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_SHIFT +
2469				wimp_KEY_F3:
2470			return ro_gui_menu_handle_action(g->window,
2471					BROWSER_EXPORT_DRAW, false);
2472
2473		case 6:			/* Ctrl + F */
2474		case IS_WIMP_KEY + wimp_KEY_F4:	/* Search */
2475			return ro_gui_menu_handle_action(g->window,
2476					BROWSER_FIND_TEXT, false);
2477
2478		case IS_WIMP_KEY + wimp_KEY_F5:	/* Reload */
2479			return ro_gui_menu_handle_action(g->window,
2480					BROWSER_NAVIGATE_RELOAD, false);
2481
2482		case 18:		/* Ctrl+R (Full reload) */
2483		case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_F5:
2484			/* Full reload */
2485			return ro_gui_menu_handle_action(g->window,
2486					BROWSER_NAVIGATE_RELOAD_ALL, false);
2487
2488		case IS_WIMP_KEY + wimp_KEY_F6:	/* Hotlist */
2489			return ro_gui_menu_handle_action(g->window,
2490					HOTLIST_SHOW, false);
2491
2492		case IS_WIMP_KEY + wimp_KEY_F7:	/* Show local history */
2493			return ro_gui_menu_handle_action(g->window,
2494					HISTORY_SHOW_LOCAL, false);
2495
2496		case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_F7:
2497			/* Show global history */
2498			return ro_gui_menu_handle_action(g->window,
2499					HISTORY_SHOW_GLOBAL, false);
2500
2501		case IS_WIMP_KEY + wimp_KEY_F8:	/* View source */
2502			ro_gui_view_source(h);
2503			return true;
2504
2505		case IS_WIMP_KEY + wimp_KEY_F9:
2506			/* Dump content for debugging. */
2507			ro_gui_dump_content(h);
2508			return true;
2509
2510		case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_F9:
2511			urldb_dump();
2512			return true;
2513
2514		case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_SHIFT +
2515				wimp_KEY_F9:
2516			talloc_report_full(0, stderr);
2517			return true;
2518
2519		case IS_WIMP_KEY + wimp_KEY_F11:	/* Zoom */
2520			return ro_gui_menu_handle_action(g->window,
2521					BROWSER_SCALE_VIEW, false);
2522
2523		case IS_WIMP_KEY + wimp_KEY_SHIFT + wimp_KEY_F11:
2524			/* Toggle display of box outlines. */
2525			html_redraw_debug = !html_redraw_debug;
2526			gui_window_redraw_window(g);
2527			return true;
2528
2529		case wimp_KEY_RETURN:
2530			if (!toolbar)
2531				break;
2532			toolbar_url = ro_gui_get_icon_string(
2533					g->toolbar->toolbar_handle,
2534					ICON_TOOLBAR_URL);
2535			ro_gui_window_launch_url(g, toolbar_url);
2536			return true;
2537
2538		case wimp_KEY_ESCAPE:
2539			if (ro_gui_url_complete_close(NULL, 0)) {
2540				ro_gui_url_complete_start(g);
2541				return true;
2542			}
2543			return ro_gui_menu_handle_action(g->window,
2544					BROWSER_NAVIGATE_STOP, false);
2545
2546		case  8:	/* CTRL+H / Backspace */
2547			if (toolbar)
2548				return ro_gui_url_complete_keypress(g, c);
2549			break;
2550
2551		case 14:	/* CTRL+N */
2552			return ro_gui_menu_handle_action(g->window,
2553					BROWSER_NEW_WINDOW, false);
2554
2555		case 17:       /* CTRL+Q (Zoom out) */
2556		case 23:       /* CTRL+W (Zoom in) */
2557			if (!h)
2558				break;
2559			scale = g->bw->scale;
2560			if (ro_gui_shift_pressed() && c == 17)
2561				scale = g->bw->scale - 0.1;
2562			else if (ro_gui_shift_pressed() && c == 23)
2563				scale = g->bw->scale + 0.1;
2564			else if (c == 17) {
2565				for (int i = SCALE_SNAP_TO_SIZE - 1;
2566						i >= 0; i--)
2567					if (scale_snap_to[i] <
2568							g->bw->scale) {
2569						scale = scale_snap_to[i];
2570						break;
2571					}
2572			} else {
2573				for (unsigned int i = 0;
2574						i < SCALE_SNAP_TO_SIZE; i++)
2575					if (scale_snap_to[i] >
2576							g->bw->scale) {
2577						scale = scale_snap_to[i];
2578						break;
2579					}
2580			}
2581			if (scale < scale_snap_to[0])
2582				scale = scale_snap_to[0];
2583			if (scale > scale_snap_to[SCALE_SNAP_TO_SIZE - 1])
2584				scale = scale_snap_to[SCALE_SNAP_TO_SIZE - 1];
2585			if (g->bw->scale != scale) {
2586				browser_window_set_scale(g->bw, scale, true);
2587//				g->reformat_pending = true;
2588//				if ((h) && (content_get_type(h) != CONTENT_HTML))
2589//					browser_window_update(g->bw, false);
2590//				browser_reformat_pending = true;
2591			}
2592			return true;
2593
2594		case IS_WIMP_KEY + wimp_KEY_PRINT:
2595			return ro_gui_menu_handle_action(g->window,
2596					BROWSER_PRINT, false);
2597
2598		case IS_WIMP_KEY | wimp_KEY_LEFT:
2599		case IS_WIMP_KEY | wimp_KEY_RIGHT:
2600		case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_LEFT:
2601		case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_RIGHT:
2602			if (toolbar)
2603				return false;
2604			break;
2605		case IS_WIMP_KEY + wimp_KEY_UP:
2606		case IS_WIMP_KEY + wimp_KEY_DOWN:
2607		case IS_WIMP_KEY + wimp_KEY_PAGE_UP:
2608		case IS_WIMP_KEY + wimp_KEY_PAGE_DOWN:
2609		case wimp_KEY_HOME:
2610		case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_UP:
2611		case IS_WIMP_KEY + wimp_KEY_END:
2612		case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_DOWN:
2613			if (toolbar)
2614				return ro_gui_url_complete_keypress(g, c);
2615			break;
2616		default:
2617			if (toolbar)
2618				return ro_gui_url_complete_keypress(g, c);
2619			return false;
2620	}
2621
2622	state.w = g->window;
2623	error = xwimp_get_window_state(&state);
2624	if (error) {
2625		LOG(("xwimp_get_window_state: 0x%x: %s",
2626				error->errnum, error->errmess));
2627		return true;
2628	}
2629
2630	y = state.visible.y1 - state.visible.y0 - 32;
2631	if (g->toolbar)
2632		y -= ro_gui_theme_toolbar_full_height(g->toolbar);
2633
2634	switch (c) {
2635		case IS_WIMP_KEY | wimp_KEY_LEFT:
2636			state.xscroll -= 32;
2637			break;
2638		case IS_WIMP_KEY | wimp_KEY_RIGHT:
2639			state.xscroll += 32;
2640			break;
2641		case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_LEFT:
2642			state.xscroll = -0x10000000;
2643			break;
2644		case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_RIGHT:
2645			state.xscroll = 0x10000000;
2646			break;
2647		case IS_WIMP_KEY | wimp_KEY_UP:
2648			state.yscroll += 32;
2649			break;
2650		case IS_WIMP_KEY | wimp_KEY_DOWN:
2651			state.yscroll -= 32;
2652			break;
2653		case IS_WIMP_KEY | wimp_KEY_PAGE_UP:
2654			state.yscroll += y;
2655			break;
2656		case IS_WIMP_KEY | wimp_KEY_PAGE_DOWN:
2657			state.yscroll -= y;
2658			break;
2659		case wimp_KEY_HOME:
2660		case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_UP:
2661			state.yscroll = 0x10000000;
2662			break;
2663		case IS_WIMP_KEY | wimp_KEY_END:
2664		case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_DOWN:
2665			state.yscroll = -0x10000000;
2666			break;
2667	}
2668
2669	error = xwimp_open_window(PTR_WIMP_OPEN(&state));
2670	if (error) {
2671		LOG(("xwimp_open_window: 0x%x: %s",
2672				error->errnum, error->errmess));
2673	}
2674
2675	return true;
2676}
2677
2678
2679/**
2680 * Process Scroll_Request events.
2681 */
2682void ro_gui_scroll_request(wimp_scroll *scroll)
2683{
2684	struct gui_window *g = ro_gui_window_lookup(scroll->w);
2685
2686	if (g && g->bw->current_content && ro_gui_shift_pressed()) {
2687		/* extended scroll request with shift held down; change zoom */
2688		float scale, inc;
2689
2690		if (scroll->ymin & 3)
2691			inc = 0.02;  /* RO5 sends the msg 5 times;
2692				      * don't ask me why */
2693		else
2694			inc = (1 << (ABS(scroll->ymin)>>2)) / 20.0F;
2695
2696		if (scroll->ymin > 0) {
2697			scale = g->bw->scale + inc;
2698			if (scale > scale_snap_to[SCALE_SNAP_TO_SIZE - 1])
2699				scale = scale_snap_to[SCALE_SNAP_TO_SIZE - 1];
2700		} else {
2701			scale = g->bw->scale - inc;
2702			if (scale < scale_snap_to[0])
2703				scale = scale_snap_to[0];
2704		}
2705		if (g->bw->scale != scale)
2706			browser_window_set_scale(g->bw, scale, true);
2707	} else {
2708		int x = scroll->visible.x1 - scroll->visible.x0 - 32;
2709		int y = scroll->visible.y1 - scroll->visible.y0 - 32;
2710		os_error *error;
2711
2712		if (g && g->toolbar)
2713			y -= ro_gui_theme_toolbar_full_height(g->toolbar);
2714
2715		switch (scroll->xmin) {
2716			case wimp_SCROLL_PAGE_LEFT:
2717				scroll->xscroll -= x;
2718				break;
2719			case wimp_SCROLL_COLUMN_LEFT:
2720				scroll->xscroll -= 32;
2721				break;
2722			case wimp_SCROLL_COLUMN_RIGHT:
2723				scroll->xscroll += 32;
2724				break;
2725			case wimp_SCROLL_PAGE_RIGHT:
2726				scroll->xscroll += x;
2727				break;
2728			default:
2729				scroll->xscroll += (x * (scroll->xmin>>2)) >> 2;
2730				break;
2731		}
2732
2733		switch (scroll->ymin) {
2734			case wimp_SCROLL_PAGE_UP:
2735				scroll->yscroll += y;
2736				break;
2737			case wimp_SCROLL_LINE_UP:
2738				scroll->yscroll += 32;
2739				break;
2740			case wimp_SCROLL_LINE_DOWN:
2741				scroll->yscroll -= 32;
2742				break;
2743			case wimp_SCROLL_PAGE_DOWN:
2744				scroll->yscroll -= y;
2745				break;
2746			default:
2747				scroll->yscroll += (y * (scroll->ymin>>2)) >> 2;
2748				break;
2749		}
2750
2751		error = xwimp_open_window((wimp_open *) scroll);
2752		if (error) {
2753			LOG(("xwimp_open_window: 0x%x: %s",
2754					error->errnum, error->errmess));
2755		}
2756	}
2757}
2758
2759
2760/**
2761 * Convert x,y screen co-ordinates into window co-ordinates.
2762 *
2763 * \param  g	gui window
2764 * \param  x	x ordinate
2765 * \param  y	y ordinate
2766 * \param  pos  receives position in window co-ordinatates
2767 * \return true iff conversion successful
2768 */
2769
2770bool ro_gui_window_to_window_pos(struct gui_window *g, int x, int y,
2771		os_coord *pos)
2772{
2773	wimp_window_state state;
2774	os_error *error;
2775
2776	assert(g);
2777
2778	state.w = g->window;
2779	error = xwimp_get_window_state(&state);
2780	if (error) {
2781		LOG(("xwimp_get_window_state: 0x%x:%s",
2782			error->errnum, error->errmess));
2783		warn_user("WimpError", error->errmess);
2784		return false;
2785	}
2786	pos->x = (x - (state.visible.x0 - state.xscroll)) / 2 / g->bw->scale;
2787	pos->y = ((state.visible.y1 - state.yscroll) - y) / 2 / g->bw->scale;
2788	return true;
2789}
2790
2791
2792/**
2793 * Convert x,y window co-ordinates into screen co-ordinates.
2794 *
2795 * \param  g	gui window
2796 * \param  x	x ordinate
2797 * \param  y	y ordinate
2798 * \param  pos  receives position in screen co-ordinatates
2799 * \return true iff conversion successful
2800 */
2801
2802bool ro_gui_window_to_screen_pos(struct gui_window *g, int x, int y,
2803		os_coord *pos)
2804{
2805	wimp_window_state state;
2806	os_error *error;
2807
2808	assert(g);
2809
2810	state.w = g->window;
2811	error = xwimp_get_window_state(&state);
2812	if (error) {
2813		LOG(("xwimp_get_window_state: 0x%x:%s",
2814			error->errnum, error->errmess));
2815		warn_user("WimpError", error->errmess);
2816		return false;
2817	}
2818	pos->x = (x * 2 * g->bw->scale) + (state.visible.x0 - state.xscroll);
2819	pos->y = (state.visible.y1 - state.yscroll) - (y * 2 * g->bw->scale);
2820	return true;
2821}
2822
2823
2824/**
2825 * Handle Message_DataLoad (file dragged in) for a window.
2826 *
2827 * \param  g	    window
2828 * \param  message  Message_DataLoad block
2829 * \return  true if the load was processed
2830 *
2831 * If the file was dragged into a form file input, it is used as the value.
2832 */
2833
2834bool ro_gui_window_dataload(struct gui_window *g, wimp_message *message)
2835{
2836	struct box *box;
2837	struct box *file_box = 0;
2838	struct box *text_box = 0;
2839	struct browser_window *bw = g->bw;
2840	hlcache_handle *h;
2841	int box_x, box_y;
2842	os_error *error;
2843	os_coord pos;
2844
2845	h = bw->current_content;
2846
2847	/* HTML content only. */
2848	if (!bw->current_content || content_get_type(h) != CONTENT_HTML)
2849		return false;
2850
2851	/* Ignore directories etc. */
2852	if (0x1000 <= message->data.data_xfer.file_type)
2853		return false;
2854
2855	if (!ro_gui_window_to_window_pos(g, message->data.data_xfer.pos.x,
2856			message->data.data_xfer.pos.y, &pos))
2857		return false;
2858
2859	box = html_get_box_tree(h);
2860
2861	/* Consider the margins of the html page now */
2862	box_x = box->margin[LEFT];
2863	box_y = box->margin[TOP];
2864
2865	while ((box = box_at_point(box, pos.x, pos.y, &box_x, &box_y, &h))) {
2866		if (box->style && css_computed_visibility(box->style) ==
2867				CSS_VISIBILITY_HIDDEN)
2868			continue;
2869
2870		if (box->gadget) {
2871			switch (box->gadget->type) {
2872				case GADGET_FILE:
2873					file_box = box;
2874				break;
2875
2876				case GADGET_TEXTBOX:
2877				case GADGET_TEXTAREA:
2878				case GADGET_PASSWORD:
2879					text_box = box;
2880					break;
2881
2882				default:	/* appease compiler */
2883					break;
2884			}
2885		}
2886	}
2887
2888	if (!file_box && !text_box)
2889		return false;
2890
2891	if (file_box) {
2892		utf8_convert_ret ret;
2893		char *utf8_fn;
2894
2895		ret = utf8_from_local_encoding(
2896				message->data.data_xfer.file_name, 0,
2897				&utf8_fn);
2898		if (ret != UTF8_CONVERT_OK) {
2899			/* A bad encoding should never happen */
2900			assert(ret != UTF8_CONVERT_BADENC);
2901			LOG(("utf8_from_local_encoding failed"));
2902			/* Load was for us - just no memory */
2903			return true;
2904		}
2905
2906		/* Found: update form input. */
2907		free(file_box->gadget->value);
2908		file_box->gadget->value = utf8_fn;
2909
2910		/* Redraw box. */
2911		box_coords(file_box, &pos.x, &pos.y);
2912		gui_window_redraw(bw->window, pos.x, pos.y,
2913				pos.x + file_box->width,
2914				pos.y + file_box->height);
2915	} else {
2916
2917		const char *filename = message->data.data_xfer.file_name;
2918
2919		browser_window_mouse_click(g->bw, BROWSER_MOUSE_PRESS_1, pos.x, pos.y);
2920
2921		if (!ro_gui_window_import_text(g, filename, false))
2922			return true;  /* it was for us, it just didn't work! */
2923	}
2924
2925	/* send DataLoadAck */
2926	message->action = message_DATA_LOAD_ACK;
2927	message->your_ref = message->my_ref;
2928	error = xwimp_send_message(wimp_USER_MESSAGE, message, message->sender);
2929	if (error) {
2930		LOG(("xwimp_send_message: 0x%x: %s\n",
2931				error->errnum, error->errmess));
2932		warn_user("WimpError", error->errmess);
2933	}
2934
2935	return true;
2936}
2937
2938
2939/**
2940 * Handle Message_DataLoad (file dragged in) for a toolbar
2941 *
2942 * \param  g	    window
2943 * \param  message  Message_DataLoad block
2944 * \return true if the load was processed
2945 */
2946
2947bool ro_gui_toolbar_dataload(struct gui_window *g, wimp_message *message)
2948{
2949	if (message->data.data_xfer.file_type == osfile_TYPE_TEXT &&
2950		ro_gui_window_import_text(g, message->data.data_xfer.file_name, true)) {
2951
2952		os_error *error;
2953
2954		/* send DataLoadAck */
2955		message->action = message_DATA_LOAD_ACK;
2956		message->your_ref = message->my_ref;
2957		error = xwimp_send_message(wimp_USER_MESSAGE, message, message->sender);
2958		if (error) {
2959			LOG(("xwimp_send_message: 0x%x: %s\n",
2960					error->errnum, error->errmess));
2961			warn_user("WimpError", error->errmess);
2962		}
2963		return true;
2964	}
2965	return false;
2966}
2967
2968
2969/**
2970 * Process pending reformats
2971 */
2972
2973void ro_gui_window_process_reformats(void)
2974{
2975	struct gui_window *g;
2976
2977	browser_reformat_pending = false;
2978	for (g = window_list; g; g = g->next) {
2979		if (!g->bw->reformat_pending)
2980			continue;
2981		g->bw->reformat_pending = false;
2982		browser_window_reformat(g->bw,
2983				g->old_width / 2,
2984				g->old_height / 2);
2985	}
2986}
2987
2988
2989/**
2990 * Clones a browser window's options.
2991 *
2992 * \param  new_bw  the new browser window
2993 * \param  old_bw  the browser window to clone from, or NULL for default
2994 */
2995
2996void ro_gui_window_clone_options(struct browser_window *new_bw,
2997		struct browser_window *old_bw) {
2998	struct gui_window *old_gui = NULL;
2999	struct gui_window *new_gui;
3000
3001	assert(new_bw);
3002
3003	/*	Get our GUIs
3004	*/
3005	new_gui = new_bw->window;
3006
3007	if (old_bw)
3008		old_gui = old_bw->window;
3009
3010	/*	Clone the basic options
3011	*/
3012	if (!old_gui) {
3013		new_bw->scale = ((float)option_scale) / 100;
3014		new_gui->option.background_images = option_background_images;
3015		new_gui->option.buffer_animations = option_buffer_animations;
3016		new_gui->option.buffer_everything = option_buffer_everything;
3017	} else {
3018		new_gui->option = old_gui->option;
3019	}
3020
3021	/*	Set up the toolbar
3022	*/
3023	if (new_gui->toolbar) {
3024		new_gui->toolbar->display_buttons = option_toolbar_show_buttons;
3025		new_gui->toolbar->display_url = option_toolbar_show_address;
3026		new_gui->toolbar->display_throbber = option_toolbar_show_throbber;
3027		if ((old_gui) && (old_gui->toolbar)) {
3028			new_gui->toolbar->display_buttons = old_gui->toolbar->display_buttons;
3029			new_gui->toolbar->display_url = old_gui->toolbar->display_url;
3030			new_gui->toolbar->display_throbber = old_gui->toolbar->display_throbber;
3031			new_gui->toolbar->reformat_buttons = true;
3032			ro_gui_theme_process_toolbar(new_gui->toolbar, -1);
3033		}
3034	}
3035}
3036
3037
3038/**
3039 * Makes a browser window's options the default.
3040 *
3041 * \param  bw  the browser window to read options from
3042 */
3043
3044void ro_gui_window_default_options(struct browser_window *bw) {
3045	struct gui_window *gui;
3046
3047	assert(bw);
3048
3049	/*	Get our GUI
3050	*/
3051	gui = bw->window;
3052	if (!gui) return;
3053
3054	/*	Save the basic options
3055	*/
3056	option_scale = bw->scale * 100;
3057	option_buffer_animations = gui->option.buffer_animations;
3058	option_buffer_everything = gui->option.buffer_everything;
3059
3060	/*	Set up the toolbar
3061	*/
3062	if (gui->toolbar) {
3063		option_toolbar_show_buttons = gui->toolbar->display_buttons;
3064		option_toolbar_show_address = gui->toolbar->display_url;
3065		option_toolbar_show_throbber = gui->toolbar->display_throbber;
3066	}
3067	if (gui->status_bar)
3068		option_toolbar_status_width = ro_gui_status_bar_get_width(gui->status_bar);
3069}
3070
3071
3072
3073
3074/**
3075 * Updates the navigation controls for all toolbars;
3076 *
3077 * \param  g	the gui_window to launch the URL into
3078 * \param  url  the URL to launch, or NULL to simply update the suggestion icon
3079 */
3080void ro_gui_window_prepare_navigate_all(void) {
3081	struct gui_window *g;
3082
3083	for (g = window_list; g; g = g->next)
3084		ro_gui_prepare_navigate(g);
3085}
3086
3087
3088/**
3089 * Returns the state of the mouse buttons and modifiers keys for a
3090 * click/release action, suitable for passing to the OS-independent
3091 * browser window code
3092 */
3093
3094browser_mouse_state ro_gui_mouse_click_state(wimp_mouse_state buttons)
3095{
3096	browser_mouse_state state = 0;
3097
3098	if (buttons & (wimp_CLICK_SELECT))
3099		state |= BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1;
3100	if (buttons & (wimp_CLICK_ADJUST))
3101		state |= BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2;
3102
3103	if (buttons & (wimp_DRAG_SELECT)) {
3104		state |= BROWSER_MOUSE_DRAG_1;
3105		mouse_drag = true;
3106	}
3107	if (buttons & (wimp_DRAG_ADJUST)) {
3108		state |= BROWSER_MOUSE_DRAG_2;
3109		mouse_drag = true;
3110	}
3111
3112	if (ro_gui_shift_pressed()) state |= BROWSER_MOUSE_MOD_1;
3113	if (ro_gui_ctrl_pressed())  state |= BROWSER_MOUSE_MOD_2;
3114
3115	return state;
3116}
3117
3118
3119/**
3120 * Returns the state of the mouse buttons and modifiers keys whilst
3121 * dragging, for passing to the OS-independent browser window code
3122 */
3123
3124browser_mouse_state ro_gui_mouse_drag_state(wimp_mouse_state buttons)
3125{
3126	browser_mouse_state state = 0;
3127
3128
3129	if (buttons & (wimp_CLICK_SELECT)) state |= BROWSER_MOUSE_HOLDING_1;
3130	if (buttons & (wimp_CLICK_ADJUST)) state |= BROWSER_MOUSE_HOLDING_2;
3131
3132	if (!(buttons & (wimp_CLICK_SELECT) || buttons & (wimp_CLICK_ADJUST)))
3133		mouse_drag = false;
3134	if (mouse_drag) state |= BROWSER_MOUSE_DRAG_ON;
3135
3136	if (ro_gui_shift_pressed()) state |= BROWSER_MOUSE_MOD_1;
3137	if (ro_gui_ctrl_pressed())  state |= BROWSER_MOUSE_MOD_2;
3138
3139	return state;
3140}
3141
3142
3143/**
3144 * Returns true iff one or more Shift keys is held down
3145 */
3146
3147bool ro_gui_shift_pressed(void)
3148{
3149	int shift = 0;
3150	xosbyte1(osbyte_SCAN_KEYBOARD, 0 ^ 0x80, 0, &shift);
3151	return (shift == 0xff);
3152}
3153
3154
3155/**
3156 * Returns true iff one or more Ctrl keys is held down
3157 */
3158
3159bool ro_gui_ctrl_pressed(void)
3160{
3161	int ctrl = 0;
3162	xosbyte1(osbyte_SCAN_KEYBOARD, 1 ^ 0x80, 0, &ctrl);
3163	return (ctrl == 0xff);
3164}
3165
3166
3167/**
3168 * Returns true iff one or more Alt keys is held down
3169 */
3170
3171bool ro_gui_alt_pressed(void)
3172{
3173	int alt = 0;
3174	xosbyte1(osbyte_SCAN_KEYBOARD, 2 ^ 0x80, 0, &alt);
3175	return (alt == 0xff);
3176}
3177
3178
3179/**
3180 * Completes scrolling of a browser window
3181 *
3182 * \param g  gui window
3183 */
3184
3185void ro_gui_window_scroll_end(struct gui_window *g, wimp_dragged *drag)
3186{
3187	wimp_pointer pointer;
3188	os_error *error;
3189	os_coord pos;
3190
3191	gui_current_drag_type = GUI_DRAG_NONE;
3192	if (!g)
3193		return;
3194
3195	error = xwimp_drag_box((wimp_drag*)-1);
3196	if (error) {
3197		LOG(("xwimp_drag_box: 0x%x : %s",
3198				error->errnum, error->errmess));
3199		warn_user("WimpError", error->errmess);
3200	}
3201
3202	error = xwimp_get_pointer_info(&pointer);
3203	if (error) {
3204		LOG(("xwimp_get_pointer_info 0x%x : %s",
3205				error->errnum, error->errmess));
3206		warn_user("WimpError", error->errmess);
3207		return;
3208	}
3209
3210	error = xwimpspriteop_set_pointer_shape("ptr_default", 0x31, 0, 0, 0, 0);
3211	if (error) {
3212		LOG(("xwimpspriteop_set_pointer_shape: 0x%x: %s",
3213				error->errnum, error->errmess));
3214		warn_user("WimpError", error->errmess);
3215	}
3216
3217	if (ro_gui_window_to_window_pos(g, drag->final.x0, drag->final.y0, &pos))
3218		browser_window_mouse_drag_end(g->bw,
3219				ro_gui_mouse_click_state(pointer.buttons),
3220				pos.x, pos.y);
3221}
3222
3223
3224/**
3225 * Completes resizing of a browser frame
3226 *
3227 * \param g  gui window
3228 */
3229
3230void ro_gui_window_frame_resize_end(struct gui_window *g, wimp_dragged *drag)
3231{
3232	/* our clean-up is the same as for page scrolling */
3233	ro_gui_window_scroll_end(g, drag);
3234}
3235
3236/**
3237 * Import text file into window or its toolbar
3238 *
3239 * \param  g	     gui window containing textarea
3240 * \param  filename  pathname of file to be imported
3241 * \param  toolbar   true iff imported to toolbar rather than main window
3242 * \return true iff successful
3243 */
3244
3245bool ro_gui_window_import_text(struct gui_window *g, const char *filename,
3246		bool toolbar)
3247{
3248	fileswitch_object_type obj_type;
3249	os_error *error;
3250	char *buf, *utf8_buf;
3251	int size;
3252	utf8_convert_ret ret;
3253
3254	error = xosfile_read_stamped(filename, &obj_type, NULL, NULL,
3255			&size, NULL, NULL);
3256	if (error) {
3257		LOG(("xosfile_read_stamped: 0x%x:%s",
3258			error->errnum, error->errmess));
3259		warn_user("FileError", error->errmess);
3260		return true;  /* was for us, but it didn't work! */
3261	}
3262
3263	buf = malloc(size);
3264	if (!buf) {
3265		warn_user("NoMemory", NULL);
3266		return true;
3267	}
3268
3269	error = xosfile_load_stamped(filename, (byte*)buf,
3270			NULL, NULL, NULL, NULL, NULL);
3271	if (error) {
3272		LOG(("xosfile_load_stamped: 0x%x:%s",
3273			error->errnum, error->errmess));
3274		warn_user("LoadError", error->errmess);
3275		free(buf);
3276		return true;
3277	}
3278
3279	ret = utf8_from_local_encoding(buf, size, &utf8_buf);
3280	if (ret != UTF8_CONVERT_OK) {
3281		/* bad encoding shouldn't happen */
3282		assert(ret != UTF8_CONVERT_BADENC);
3283		LOG(("utf8_from_local_encoding failed"));
3284		free(buf);
3285		warn_user("NoMemory", NULL);
3286		return true;
3287	}
3288	size = strlen(utf8_buf);
3289
3290	if (toolbar) {
3291		const char *ep = utf8_buf + size;
3292		const char *sp;
3293		char *p = utf8_buf;
3294
3295		/* skip leading whitespace */
3296		while (isspace(*p)) p++;
3297
3298		sp = p;
3299		while (*p && *p != '\r' && *p != '\n')
3300			p += utf8_next(p, ep - p, 0);
3301		*p = '\0';
3302
3303		if (p > sp)
3304			ro_gui_window_launch_url(g, sp);
3305	}
3306	else
3307		browser_window_paste_text(g->bw, utf8_buf, size, true);
3308
3309	free(buf);
3310	free(utf8_buf);
3311	return true;
3312}
3313
3314
3315/**
3316 * Window is being iconised. Create a suitable thumbnail sprite
3317 * (which, sadly, must be in the Wimp sprite pool), and return
3318 * the sprite name and truncated title to the iconiser
3319 *
3320 * \param  g   the gui window being iconised
3321 * \param  wi  the WindowInfo message from the iconiser
3322 */
3323
3324void ro_gui_window_iconise(struct gui_window *g,
3325		wimp_full_message_window_info *wi)
3326{
3327	/* sadly there is no 'legal' way to get the sprite into
3328	 * the Wimp sprite pool other than via a filing system */
3329	const char *temp_fname = "Pipe:$._tmpfile";
3330	struct browser_window *bw = g->bw;
3331	osspriteop_header *overlay = NULL;
3332	osspriteop_header *sprite_header;
3333	struct bitmap *bitmap;
3334	osspriteop_area *area;
3335	int width = 34, height = 34;
3336	hlcache_handle *h;
3337	os_error *error;
3338	int len, id;
3339
3340	assert(bw);
3341
3342	h = bw->current_content;
3343	if (!h) return;
3344
3345	/* if an overlay sprite is defined, locate it and gets its dimensions
3346	 * so that we can produce a thumbnail with the same dimensions */
3347	if (!ro_gui_wimp_get_sprite("ic_netsfxx", &overlay)) {
3348		error = xosspriteop_read_sprite_info(osspriteop_PTR,
3349				(osspriteop_area *)0x100,
3350				(osspriteop_id)overlay, &width, &height, NULL,
3351				NULL);
3352		if (error) {
3353			LOG(("xosspriteop_read_sprite_info: 0x%x: %s",
3354				error->errnum, error->errmess));
3355			warn_user("MiscError", error->errmess);
3356			overlay = NULL;
3357		}
3358		else if (sprite_bpp(overlay) != 8) {
3359			LOG(("overlay sprite is not 8bpp"));
3360			overlay = NULL;
3361		}
3362	}
3363
3364	/* create the thumbnail sprite */
3365	bitmap = bitmap_create(width, height, BITMAP_NEW | BITMAP_OPAQUE |
3366			BITMAP_CLEAR_MEMORY);
3367	if (!bitmap) {
3368		LOG(("Thumbnail initialisation failed."));
3369		return;
3370	}
3371	thumbnail_create(h, bitmap, NULL);
3372	if (overlay)
3373		bitmap_overlay_sprite(bitmap, overlay);
3374	area = thumbnail_convert_8bpp(bitmap);
3375	bitmap_destroy(bitmap);
3376	if (!area) {
3377		LOG(("Thumbnail conversion failed."));
3378		return;
3379	}
3380
3381	/* choose a suitable sprite name */
3382	id = 0;
3383	while (iconise_used[id])
3384		if ((unsigned)++id >= NOF_ELEMENTS(iconise_used)) {
3385			id = iconise_next;
3386			if ((unsigned)++iconise_next >=
3387					NOF_ELEMENTS(iconise_used))
3388				iconise_next = 0;
3389			break;
3390		}
3391
3392	sprite_header = (osspriteop_header *)(area + 1);
3393	len = sprintf(sprite_header->name, "ic_netsf%.2d", id);
3394
3395	error = xosspriteop_save_sprite_file(osspriteop_USER_AREA,
3396			area, temp_fname);
3397	if (error) {
3398		LOG(("xosspriteop_save_sprite_file: 0x%x:%s",
3399			error->errnum, error->errmess));
3400		warn_user("MiscError", error->errmess);
3401		free(area);
3402		return;
3403	}
3404
3405	error = xwimpspriteop_merge_sprite_file(temp_fname);
3406	if (error) {
3407		LOG(("xwimpspriteop_merge_sprite_file: 0x%x:%s",
3408			error->errnum, error->errmess));
3409		warn_user("WimpError", error->errmess);
3410		remove(temp_fname);
3411		free(area);
3412		return;
3413	}
3414
3415	memcpy(wi->sprite_name, sprite_header->name + 3, len - 2); /* inc NUL */
3416	strncpy(wi->title, g->title, sizeof(wi->title));
3417	wi->title[sizeof(wi->title) - 1] = '\0';
3418
3419	if (wimptextop_string_width(wi->title, 0) > 182) {
3420		/* work around bug in Pinboard where it will fail to display
3421		 * the icon if the text is very wide */
3422		if (strlen(wi->title) > 10)
3423			wi->title[10] = '\0';	/* pinboard does this anyway */
3424		while (wimptextop_string_width(wi->title, 0) > 182)
3425			wi->title[strlen(wi->title) - 1] = '\0';
3426	}
3427
3428	wi->size = sizeof(wimp_full_message_window_info);
3429	wi->your_ref = wi->my_ref;
3430	error = xwimp_send_message(wimp_USER_MESSAGE, (wimp_message*)wi,
3431			wi->sender);
3432	if (error) {
3433		LOG(("xwimp_send_message: 0x%x:%s",
3434			error->errnum, error->errmess));
3435		warn_user("WimpError", error->errmess);
3436	}
3437	else {
3438		g->iconise_icon = id;
3439		iconise_used[id] = true;
3440	}
3441
3442	free(area);
3443}
3444
3445
3446/**
3447 * Navigate up one level
3448 *
3449 * \param  g	the gui_window to open the parent link in
3450 * \param  url  the URL to open the parent of
3451 */
3452bool ro_gui_window_navigate_up(struct gui_window *g, const char *url) {
3453	char *parent;
3454	url_func_result res;
3455	bool compare;
3456
3457	if (!g || (!g->bw))
3458		return false;
3459
3460	res = url_parent(url, &parent);
3461	if (res == URL_FUNC_OK) {
3462		res = url_compare(url, parent, false, &compare);
3463		if ((res == URL_FUNC_OK) && !compare)
3464			browser_window_go(g->bw, parent, 0, true);
3465		free(parent);
3466	}
3467	return true;
3468}