PageRenderTime 72ms CodeModel.GetById 2ms app.highlight 60ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/C0deMaver1ck/peeklinux
C | 1503 lines | 1096 code | 192 blank | 215 comment | 150 complexity | 2e5923d670d41795e5d124c410d4fe41 MD5 | raw file
   1/*
   2 * Copyright 2004-2007 James Bursa <bursa@users.sourceforge.net>
   3 * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
   4 *
   5 * This file is part of NetSurf, http://www.netsurf-browser.org/
   6 *
   7 * NetSurf is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; version 2 of the License.
  10 *
  11 * NetSurf is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20/** \file
  21 * Save dialog and drag and drop saving (implementation).
  22 */
  23
  24#include <assert.h>
  25#include <ctype.h>
  26#include <errno.h>
  27#include <stdbool.h>
  28#include <stdio.h>
  29#include <stdlib.h>
  30#include <string.h>
  31#include "oslib/dragasprite.h"
  32#include "oslib/osbyte.h"
  33#include "oslib/osfile.h"
  34#include "oslib/osmodule.h"
  35#include "oslib/osspriteop.h"
  36#include "oslib/wimp.h"
  37#include "oslib/wimpspriteop.h"
  38#include "content/content.h"
  39#include "content/hlcache.h"
  40#include "desktop/netsurf.h"
  41#include "desktop/save_complete.h"
  42#include "desktop/save_text.h"
  43#include "desktop/selection.h"
  44#include "image/bitmap.h"
  45#include "render/box.h"
  46#include "render/form.h"
  47#include "riscos/dialog.h"
  48#include "riscos/gui.h"
  49#include "riscos/menus.h"
  50#include "riscos/message.h"
  51#include "riscos/options.h"
  52#include "riscos/query.h"
  53#include "riscos/save.h"
  54#include "riscos/save_draw.h"
  55#include "riscos/save_pdf.h"
  56#include "riscos/textselection.h"
  57#include "riscos/thumbnail.h"
  58#include "riscos/wimp.h"
  59#include "riscos/wimp_event.h"
  60#include "utils/config.h"
  61#include "utils/log.h"
  62#include "utils/messages.h"
  63#include "utils/url.h"
  64#include "utils/utf8.h"
  65#include "utils/utils.h"
  66
  67//typedef enum
  68//{
  69//	QueryRsn_Quit,
  70//	QueryRsn_Abort,
  71//	QueryRsn_Overwrite
  72//} query_reason;
  73
  74
  75/**todo - much of the state information for a save should probably be moved into a structure
  76          now since we could have multiple saves outstanding */
  77
  78static gui_save_type gui_save_current_type;
  79static hlcache_handle *gui_save_content = NULL;
  80static struct selection *gui_save_selection = NULL;
  81static const char *gui_save_url = NULL;
  82static const char *gui_save_title = NULL;
  83static int gui_save_filetype;
  84static query_id gui_save_query;
  85static bool gui_save_send_dataload;
  86static wimp_message gui_save_message;
  87static bool gui_save_close_after = true;
  88
  89static bool dragbox_active = false;  /** in-progress Wimp_DragBox/DragASprite op */
  90static bool using_dragasprite = true;
  91static bool saving_from_dialog = true;
  92static osspriteop_area *saveas_area = NULL;
  93static wimp_w gui_save_sourcew = (wimp_w)-1;
  94#define LEAFNAME_MAX 200
  95static char save_leafname[LEAFNAME_MAX];
  96
  97/** Current save directory (updated by and used for dialog-based saving) */
  98static char *save_dir = NULL;
  99static size_t save_dir_len;
 100
 101typedef enum { LINK_ACORN, LINK_ANT, LINK_TEXT } link_format;
 102
 103static bool ro_gui_save_complete(hlcache_handle *h, char *path);
 104static bool ro_gui_save_content(hlcache_handle *h, char *path, bool force_overwrite);
 105static void ro_gui_save_done(void);
 106static void ro_gui_save_bounced(wimp_message *message);
 107static bool ro_gui_save_object_native(hlcache_handle *h, char *path);
 108static bool ro_gui_save_link(const char *url, const char *title, link_format format, char *path);
 109static void ro_gui_save_set_state(hlcache_handle *h, gui_save_type save_type,
 110		const char *url, char *leaf_buf, char *icon_buf);
 111static bool ro_gui_save_create_thumbnail(hlcache_handle *h, const char *name);
 112static void ro_gui_save_overwrite_confirmed(query_id, enum query_response res, void *p);
 113static void ro_gui_save_overwrite_cancelled(query_id, enum query_response res, void *p);
 114
 115static const query_callback overwrite_funcs =
 116{
 117	ro_gui_save_overwrite_confirmed,
 118	ro_gui_save_overwrite_cancelled
 119};
 120
 121
 122/** An entry in gui_save_table. */
 123struct gui_save_table_entry {
 124	int filetype;
 125	const char *name;
 126};
 127
 128/** Table of filetypes and default filenames. Must be in sync with
 129 * gui_save_type (riscos/gui.h). A filetype of 0 indicates the content should
 130 * be used.
 131 */
 132static const struct gui_save_table_entry gui_save_table[] = {
 133	/* GUI_SAVE_SOURCE,              */ {     0, "SaveSource" },
 134	/* GUI_SAVE_DRAW,                */ { 0xaff, "SaveDraw" },
 135	/* GUI_SAVE_PDF,                 */ { 0xadf, "SavePDF" },
 136	/* GUI_SAVE_TEXT,                */ { 0xfff, "SaveText" },
 137	/* GUI_SAVE_COMPLETE,            */ { 0xfaf, "SaveComplete" },
 138	/* GUI_SAVE_OBJECT_ORIG,         */ {     0, "SaveObject" },
 139	/* GUI_SAVE_OBJECT_NATIVE,       */ {     0, "SaveObject" },
 140	/* GUI_SAVE_LINK_URI,            */ { 0xf91, "SaveLink" },
 141	/* GUI_SAVE_LINK_URL,            */ { 0xb28, "SaveLink" },
 142	/* GUI_SAVE_LINK_TEXT,           */ { 0xfff, "SaveLink" },
 143	/* GUI_SAVE_HOTLIST_EXPORT_HTML, */ { 0xfaf, "Hotlist" },
 144	/* GUI_SAVE_HISTORY_EXPORT_HTML, */ { 0xfaf, "History" },
 145	/* GUI_SAVE_TEXT_SELECTION,      */ { 0xfff, "SaveSelection" },
 146};
 147
 148
 149/**
 150 * Create the saveas dialogue from the given template, and the sprite area
 151 * necessary for our thumbnail (full page save)
 152 *
 153 * \param  template_name  name of template to be used
 154 * \return window handle of created dialogue
 155 */
 156
 157wimp_w ro_gui_saveas_create(const char *template_name)
 158{
 159	const int sprite_size = (68 * 68 * 4) + ((68 * 68) / 8);  /* 32bpp with mask */
 160	int area_size = sizeof(osspriteop_area) + sizeof(osspriteop_header) +
 161			256 * 8 + sprite_size;
 162	void *area = NULL;
 163	wimp_window *window;
 164	os_error *error;
 165	wimp_icon *icons;
 166	wimp_w w;
 167
 168	window = ro_gui_dialog_load_template(template_name);
 169	assert(window);
 170
 171	icons = window->icons;
 172
 173	error = xosmodule_alloc(area_size, (void **) &area);
 174	if (error) {
 175		LOG(("xosmodule_alloc: 0x%x: %s", error->errnum, error->errmess));
 176		xwimp_close_template();
 177		die(error->errmess);
 178	} else {
 179		saveas_area = area;
 180		saveas_area->size = area_size;
 181		saveas_area->first = 16;
 182
 183		error = xosspriteop_clear_sprites(osspriteop_USER_AREA, saveas_area);
 184		if (error) {
 185			LOG(("xosspriteop_clear_sprites: 0x%x: %s",
 186				error->errnum, error->errmess));
 187			warn_user("MiscError", error->errmess);
 188
 189			xosmodule_free(saveas_area);
 190			saveas_area = NULL;
 191		}
 192	}
 193
 194	assert((icons[ICON_SAVE_ICON].flags &
 195		(wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_INDIRECTED)) ==
 196		(wimp_ICON_SPRITE | wimp_ICON_INDIRECTED));
 197	icons[ICON_SAVE_ICON].data.indirected_sprite.area = saveas_area;
 198
 199	/* create window */
 200	error = xwimp_create_window(window, &w);
 201	if (error) {
 202		LOG(("xwimp_create_window: 0x%x: %s",
 203				error->errnum, error->errmess));
 204		xwimp_close_template();
 205		die(error->errmess);
 206	}
 207
 208	/* the window definition is copied by the wimp and may be freed */
 209	free(window);
 210
 211	return w;
 212}
 213
 214
 215/**
 216 * Clean-up function that releases our sprite area and memory.
 217 */
 218
 219void ro_gui_saveas_quit(void)
 220{
 221	if (saveas_area) {
 222		os_error *error = xosmodule_free(saveas_area);
 223		if (error) {
 224			LOG(("xosmodule_free: 0x%x: %s", error->errnum, error->errmess));
 225			warn_user("MiscError", error->errmess);
 226		}
 227		saveas_area = NULL;
 228	}
 229
 230	free(save_dir);
 231	save_dir = NULL;
 232}
 233
 234/**
 235 * Prepares the save box to reflect gui_save_type and a content, and
 236 * opens it.
 237 *
 238 * \param  save_type  type of save
 239 * \param  h          content to save
 240 * \param  s          selection to save
 241 * \param  url        url to be saved (link types)
 242 * \param  title      title (if any), when saving links
 243 */
 244
 245void ro_gui_save_prepare(gui_save_type save_type, hlcache_handle *h,
 246		struct selection *s, const char *url, const char *title)
 247{
 248	char name_buf[FILENAME_MAX];
 249	size_t leaf_offset = 0;
 250	char icon_buf[20];
 251
 252	assert( (save_type == GUI_SAVE_LINK_URI) ||
 253			(save_type == GUI_SAVE_LINK_URL) ||
 254			(save_type == GUI_SAVE_LINK_TEXT) ||
 255			(save_type == GUI_SAVE_HOTLIST_EXPORT_HTML) ||
 256			(save_type == GUI_SAVE_HISTORY_EXPORT_HTML) ||
 257			(save_type == GUI_SAVE_TEXT_SELECTION) || h);
 258
 259	gui_save_selection = s;
 260	gui_save_url = url;
 261	gui_save_title = title;
 262
 263	if (save_dir) {
 264		leaf_offset = save_dir_len;
 265		memcpy(name_buf, save_dir, leaf_offset);
 266		name_buf[leaf_offset++] = '.';
 267	}
 268
 269	ro_gui_save_set_state(h, save_type, h ? content_get_url(h) : url,
 270			name_buf + leaf_offset, icon_buf);
 271
 272	ro_gui_set_icon_sprite(dialog_saveas, ICON_SAVE_ICON, saveas_area,
 273			icon_buf);
 274
 275	ro_gui_set_icon_string(dialog_saveas, ICON_SAVE_PATH, name_buf, true);
 276	ro_gui_wimp_event_memorise(dialog_saveas);
 277}
 278
 279/**
 280 * Starts a drag for the save dialog
 281 *
 282 * \param  pointer  mouse position info from Wimp
 283 */
 284void ro_gui_save_start_drag(wimp_pointer *pointer)
 285{
 286	if (pointer->buttons & (wimp_DRAG_SELECT | wimp_DRAG_ADJUST)) {
 287		const char *sprite = ro_gui_get_icon_string(pointer->w, pointer->i);
 288		int x = pointer->pos.x, y = pointer->pos.y;
 289		wimp_window_state wstate;
 290		wimp_icon_state istate;
 291		/* start the drag from the icon's exact location, rather than the pointer */
 292		istate.w = wstate.w = pointer->w;
 293		istate.i = pointer->i;
 294		if (!xwimp_get_window_state(&wstate) && !xwimp_get_icon_state(&istate)) {
 295			x = (istate.icon.extent.x1 + istate.icon.extent.x0)/2 +
 296					wstate.visible.x0 - wstate.xscroll;
 297			y = (istate.icon.extent.y1 + istate.icon.extent.y0)/2 +
 298					wstate.visible.y1 - wstate.yscroll;
 299		}
 300		gui_current_drag_type = GUI_DRAG_SAVE;
 301		gui_save_sourcew = pointer->w;
 302		saving_from_dialog = true;
 303		gui_save_close_after = !(pointer->buttons & wimp_DRAG_ADJUST);
 304		ro_gui_drag_icon(x, y, sprite);
 305	}
 306}
 307
 308
 309/**
 310 * Handle OK click/keypress in the save dialog.
 311 *
 312 * \param  w  window handle of save dialog
 313 * \return true on success, false on failure
 314 */
 315bool ro_gui_save_ok(wimp_w w)
 316{
 317	const char *name = ro_gui_get_icon_string(w, ICON_SAVE_PATH);
 318	wimp_pointer pointer;
 319	char path[256];
 320
 321	if (!strrchr(name, '.')) {
 322		warn_user("NoPathError", NULL);
 323		return false;
 324	}
 325
 326	ro_gui_convert_save_path(path, sizeof path, name);
 327	gui_save_sourcew = w;
 328	saving_from_dialog = true;
 329	gui_save_send_dataload = false;
 330	gui_save_close_after = xwimp_get_pointer_info(&pointer)
 331						|| !(pointer.buttons & wimp_CLICK_ADJUST);
 332	memcpy(&gui_save_message.data.data_xfer.file_name, path, 1 + strlen(path));
 333
 334	if (ro_gui_save_content(gui_save_content, path, !option_confirm_overwrite)) {
 335		ro_gui_save_done();
 336		return true;
 337	}
 338	return false;
 339}
 340
 341
 342/**
 343 * Initiates drag saving of an object directly from a browser window
 344 *
 345 * \param  save_type  type of save
 346 * \param  c          content to save
 347 * \param  g          gui window
 348 */
 349
 350void gui_drag_save_object(gui_save_type save_type, hlcache_handle *c,
 351		struct gui_window *g)
 352{
 353	wimp_pointer pointer;
 354	char icon_buf[20];
 355	os_error *error;
 356
 357	/* Close the save window because otherwise we need two contexts
 358	*/
 359	xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
 360	ro_gui_dialog_close(dialog_saveas);
 361
 362	gui_save_sourcew = g->window;
 363	saving_from_dialog = false;
 364
 365	error = xwimp_get_pointer_info(&pointer);
 366	if (error) {
 367		LOG(("xwimp_get_pointer_info: 0x%x: %s",
 368				error->errnum, error->errmess));
 369		warn_user("WimpError", error->errmess);
 370		return;
 371	}
 372
 373	ro_gui_save_set_state(c, save_type, content_get_url(c), save_leafname,
 374			icon_buf);
 375
 376	gui_current_drag_type = GUI_DRAG_SAVE;
 377
 378	ro_gui_drag_icon(pointer.pos.x, pointer.pos.y, icon_buf);
 379}
 380
 381
 382/**
 383 * Initiates drag saving of a selection from a browser window
 384 *
 385 * \param  s  selection object
 386 * \param  g  gui window
 387 */
 388
 389void gui_drag_save_selection(struct selection *s, struct gui_window *g)
 390{
 391	wimp_pointer pointer;
 392	char icon_buf[20];
 393	os_error *error;
 394
 395	/* Close the save window because otherwise we need two contexts
 396	*/
 397	xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
 398	ro_gui_dialog_close(dialog_saveas);
 399
 400	gui_save_sourcew = g->window;
 401	saving_from_dialog = false;
 402
 403	error = xwimp_get_pointer_info(&pointer);
 404	if (error) {
 405		LOG(("xwimp_get_pointer_info: 0x%x: %s",
 406				error->errnum, error->errmess));
 407		warn_user("WimpError", error->errmess);
 408		return;
 409	}
 410
 411	gui_save_selection = s;
 412
 413	ro_gui_save_set_state(NULL, GUI_SAVE_TEXT_SELECTION, NULL, save_leafname,
 414			icon_buf);
 415
 416	gui_current_drag_type = GUI_DRAG_SAVE;
 417
 418	ro_gui_drag_icon(pointer.pos.x, pointer.pos.y, icon_buf);
 419}
 420
 421
 422/**
 423 * Initiates drag saving of a link/URL file
 424 *
 425 * \param  save_type	format in which URL should be saved
 426 * \param  url			url to be saved
 427 * \param  title		title to be included in URI format, if any
 428 * \param  g			gui window to save from
 429 * \
 430 */
 431
 432void ro_gui_drag_save_link(gui_save_type save_type, const char *url,
 433		const char *title, struct gui_window *g)
 434{
 435	wimp_pointer pointer;
 436	char icon_buf[20];
 437	os_error *error;
 438
 439	/* Close the save window because otherwise we need two contexts
 440	*/
 441	xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
 442	ro_gui_dialog_close(dialog_saveas);
 443
 444	gui_save_url = url;
 445	gui_save_title = title;
 446	gui_save_sourcew = g->window;
 447	saving_from_dialog = false;
 448
 449	error = xwimp_get_pointer_info(&pointer);
 450	if (error) {
 451		LOG(("xwimp_get_pointer_info: 0x%x: %s",
 452				error->errnum, error->errmess));
 453		warn_user("WimpError", error->errmess);
 454		return;
 455	}
 456
 457	ro_gui_save_set_state(NULL, save_type, url, save_leafname, icon_buf);
 458
 459	gui_current_drag_type = GUI_DRAG_SAVE;
 460
 461	ro_gui_drag_icon(pointer.pos.x, pointer.pos.y, icon_buf);
 462}
 463
 464
 465/**
 466 * Start drag of icon under the pointer.
 467 */
 468
 469void ro_gui_drag_icon(int x, int y, const char *sprite)
 470{
 471	os_error *error;
 472	wimp_drag drag;
 473	int r2;
 474
 475	drag.initial.x0 = x - 34;
 476	drag.initial.y0 = y - 34;
 477	drag.initial.x1 = x + 34;
 478	drag.initial.y1 = y + 34;
 479
 480	if (sprite && (xosbyte2(osbyte_READ_CMOS, 28, 0, &r2) || (r2 & 2))) {
 481		osspriteop_area *area = (osspriteop_area*)1;
 482
 483		/* first try our local sprite area in case it's a thumbnail sprite */
 484		if (saveas_area) {
 485			error = xosspriteop_select_sprite(osspriteop_USER_AREA,
 486					saveas_area, (osspriteop_id)sprite, NULL);
 487			if (error) {
 488				if (error->errnum != error_SPRITE_OP_DOESNT_EXIST) {
 489					LOG(("xosspriteop_select_sprite: 0x%x: %s",
 490						error->errnum, error->errmess));
 491					warn_user("MiscError", error->errmess);
 492				}
 493			}
 494			else
 495				area = saveas_area;
 496		}
 497
 498		error = xdragasprite_start(dragasprite_HPOS_CENTRE |
 499				dragasprite_VPOS_CENTRE |
 500				dragasprite_BOUND_POINTER |
 501				dragasprite_DROP_SHADOW,
 502				area, sprite, &drag.initial, 0);
 503
 504		if (!error) {
 505			using_dragasprite = true;
 506			dragbox_active = true;
 507			return;
 508		}
 509
 510		LOG(("xdragasprite_start: 0x%x: %s",
 511				error->errnum, error->errmess));
 512	}
 513
 514	drag.type = wimp_DRAG_USER_FIXED;
 515	drag.bbox.x0 = -0x8000;
 516	drag.bbox.y0 = -0x8000;
 517	drag.bbox.x1 = 0x7fff;
 518	drag.bbox.y1 = 0x7fff;
 519
 520	using_dragasprite = false;
 521	error = xwimp_drag_box(&drag);
 522
 523	if (error) {
 524		LOG(("xwimp_drag_box: 0x%x: %s",
 525				error->errnum, error->errmess));
 526		warn_user("DragError", error->errmess);
 527	}
 528	else
 529		dragbox_active = true;
 530}
 531
 532
 533/**
 534 * Convert a ctrl-char terminated pathname possibly containing spaces
 535 * to a NUL-terminated one containing only hard spaces.
 536 *
 537 * \param  dp   destination buffer to receive pathname
 538 * \param  len  size of destination buffer
 539 * \param  p    source pathname, ctrl-char terminated
 540 */
 541
 542void ro_gui_convert_save_path(char *dp, size_t len, const char *p)
 543{
 544	char *ep = dp + len - 1;	/* leave room for NUL */
 545
 546	assert(p <= dp || p > ep);	/* in-situ conversion /is/ allowed */
 547
 548	while (dp < ep && *p >= ' ')	/* ctrl-char terminated */
 549	{
 550		*dp++ = (*p == ' ') ? 160 : *p;
 551		p++;
 552	}
 553	*dp = '\0';
 554}
 555
 556
 557void ro_gui_drag_box_cancel(void)
 558{
 559	if (dragbox_active) {
 560		os_error *error;
 561		if (using_dragasprite) {
 562			error = xdragasprite_stop();
 563			if (error) {
 564				LOG(("xdragasprite_stop: 0x%x: %s",
 565					error->errnum, error->errmess));
 566				warn_user("WimpError", error->errmess);
 567			}
 568		}
 569		else {
 570			error = xwimp_drag_box(NULL);
 571			if (error) {
 572				LOG(("xwimp_drag_box: 0x%x: %s",
 573					error->errnum, error->errmess));
 574				warn_user("WimpError", error->errmess);
 575			}
 576		}
 577		dragbox_active = false;
 578	}
 579}
 580
 581
 582/**
 583 * Handle User_Drag_Box event for a drag from the save dialog or browser window.
 584 */
 585
 586void ro_gui_save_drag_end(wimp_dragged *drag)
 587{
 588	const char *name;
 589	wimp_pointer pointer;
 590	wimp_message message;
 591	os_error *error;
 592	char *dp, *ep;
 593	char *local_name = NULL;
 594	utf8_convert_ret err;
 595
 596	if (dragbox_active)
 597		ro_gui_drag_box_cancel();
 598
 599	error = xwimp_get_pointer_info(&pointer);
 600	if (error) {
 601		LOG(("xwimp_get_pointer_info: 0x%x: %s",
 602				error->errnum, error->errmess));
 603		warn_user("WimpError", error->errmess);
 604		return;
 605	}
 606
 607	/* perform hit-test if the destination is the same as the source window;
 608		we want to allow drag-saving from a page into the input fields within
 609		the page, but avoid accidental replacements of the current page */
 610	if (gui_save_sourcew != (wimp_w)-1 && pointer.w == gui_save_sourcew) {
 611		int dx = (drag->final.x1 + drag->final.x0)/2;
 612		int dy = (drag->final.y1 + drag->final.y0)/2;
 613		struct gui_window *g;
 614		bool dest_ok = false;
 615		os_coord pos;
 616
 617		g = ro_gui_window_lookup(gui_save_sourcew);
 618
 619		if (g && ro_gui_window_to_window_pos(g, dx, dy, &pos)) {
 620			hlcache_handle *h = g->bw->current_content;
 621
 622			if (h && content_get_type(h) == CONTENT_HTML) {
 623				struct box *box = html_get_box_tree(h);
 624				int box_x, box_y;
 625
 626				/* Consider the margins of the html page now */
 627				box_x = box->margin[LEFT];
 628				box_y = box->margin[TOP];
 629
 630				while (!dest_ok && (box = box_at_point(box,
 631						pos.x, pos.y, &box_x, &box_y,
 632						&h))) {
 633					if (box->style && 
 634							css_computed_visibility(
 635								box->style) == 
 636							CSS_VISIBILITY_HIDDEN)
 637						continue;
 638
 639					if (box->gadget) {
 640						switch (box->gadget->type) {
 641							case GADGET_FILE:
 642							case GADGET_TEXTBOX:
 643							case GADGET_TEXTAREA:
 644							case GADGET_PASSWORD:
 645								dest_ok = true;
 646								break;
 647
 648							default:	/* appease compiler */
 649								break;
 650						}
 651					}
 652				}
 653			}
 654		}
 655		if (!dest_ok) {
 656			/* cancel the drag operation */
 657			gui_current_drag_type = GUI_DRAG_NONE;
 658			return;
 659		}
 660	}
 661
 662	if (!saving_from_dialog) {
 663		/* saving directly from browser window, choose a
 664		 * name based upon the URL */
 665		err = utf8_to_local_encoding(save_leafname, 0, &local_name);
 666		if (err != UTF8_CONVERT_OK) {
 667			/* badenc should never happen */
 668			assert(err != UTF8_CONVERT_BADENC);
 669			local_name = NULL;
 670		}
 671		name = local_name ? local_name : save_leafname;
 672	}
 673	else {
 674		char *dot;
 675
 676		/* saving from dialog, grab leafname from icon */
 677		name = ro_gui_get_icon_string(gui_save_sourcew, ICON_SAVE_PATH);
 678		dot = strrchr(name, '.');
 679		if (dot)
 680			name = dot + 1;
 681	}
 682
 683	dp = message.data.data_xfer.file_name;
 684	ep = dp + sizeof message.data.data_xfer.file_name;
 685
 686	if (gui_save_current_type == GUI_SAVE_COMPLETE) {
 687		message.data.data_xfer.file_type = 0x2000;
 688		if (*name != '!') *dp++ = '!';
 689	} else
 690		message.data.data_xfer.file_type = gui_save_filetype;
 691
 692	ro_gui_convert_save_path(dp, ep - dp, name);
 693
 694/* \todo - we're supposed to set this if drag-n-drop used */
 695	message.your_ref = 0;
 696
 697	message.action = message_DATA_SAVE;
 698	message.data.data_xfer.w = pointer.w;
 699	message.data.data_xfer.i = pointer.i;
 700	message.data.data_xfer.pos.x = pointer.pos.x;
 701	message.data.data_xfer.pos.y = pointer.pos.y;
 702	message.data.data_xfer.est_size = 1000;
 703	message.size = 44 + ((strlen(message.data.data_xfer.file_name) + 4) &
 704			(~3u));
 705
 706	ro_message_send_message_to_window(wimp_USER_MESSAGE_RECORDED, &message,
 707			pointer.w, pointer.i, ro_gui_save_bounced, NULL);
 708
 709	free(local_name);
 710}
 711
 712
 713
 714/**
 715 * Send DataSave message on behalf of clipboard code and remember that it's the
 716 * clipboard contents we're being asked for when the DataSaveAck reply arrives
 717 */
 718
 719void ro_gui_send_datasave(gui_save_type save_type,
 720		wimp_full_message_data_xfer *message, wimp_t to)
 721{
 722	/* Close the save window because otherwise we need two contexts
 723	*/
 724	xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
 725	ro_gui_dialog_close(dialog_saveas);
 726
 727	if (ro_message_send_message(wimp_USER_MESSAGE_RECORDED, (wimp_message*)message,
 728			to, ro_gui_save_bounced)) {
 729		gui_save_current_type = save_type;
 730		gui_save_sourcew = (wimp_w)-1;
 731		saving_from_dialog = false;
 732		gui_current_drag_type = GUI_DRAG_SAVE;
 733	}
 734}
 735
 736
 737/**
 738 * Handle lack of Message_DataSaveAck for drags, saveas dialogs and clipboard code
 739 */
 740
 741void ro_gui_save_bounced(wimp_message *message)
 742{
 743	gui_current_drag_type = GUI_DRAG_NONE;
 744}
 745
 746
 747/**
 748 * Handle Message_DataSaveAck for a drag from the save dialog or browser window,
 749 * or Clipboard protocol.
 750 */
 751
 752void ro_gui_save_datasave_ack(wimp_message *message)
 753{
 754	char *path = message->data.data_xfer.file_name;
 755	hlcache_handle *h = gui_save_content;
 756	bool force_overwrite;
 757
 758	switch (gui_save_current_type) {
 759		case GUI_SAVE_LINK_URI:
 760		case GUI_SAVE_LINK_URL:
 761		case GUI_SAVE_LINK_TEXT:
 762		case GUI_SAVE_HOTLIST_EXPORT_HTML:
 763		case GUI_SAVE_HISTORY_EXPORT_HTML:
 764		case GUI_SAVE_TEXT_SELECTION:
 765		case GUI_SAVE_CLIPBOARD_CONTENTS:
 766			break;
 767
 768		default:
 769			if (!gui_save_content) {
 770				LOG(("unexpected DataSaveAck: gui_save_content not set"));
 771				return;
 772			}
 773			break;
 774	}
 775
 776	if (saving_from_dialog)
 777		ro_gui_set_icon_string(gui_save_sourcew, ICON_SAVE_PATH,
 778				path, true);
 779
 780	gui_save_send_dataload = true;
 781	memcpy(&gui_save_message, message, sizeof(gui_save_message));
 782
 783	/* if saving/pasting to another application, don't request user
 784	   confirmation; a ScrapFile almost certainly exists already */
 785	if (message->data.data_xfer.est_size == -1)
 786		force_overwrite = true;
 787	else
 788		force_overwrite = !option_confirm_overwrite;
 789
 790	if (ro_gui_save_content(h, path, force_overwrite))
 791		ro_gui_save_done();
 792}
 793
 794
 795
 796/**
 797 * Does the actual saving
 798 *
 799 * \param  c               content to save (or NULL for other)
 800 * \param  path            path to save as
 801 * \param  force_overwrite true iff required to overwrite without prompting
 802 * \return true on success,
 803 *         false on (i) error and error reported
 804 *               or (ii) deferred awaiting user confirmation
 805 */
 806
 807bool ro_gui_save_content(hlcache_handle *h, char *path, bool force_overwrite)
 808{
 809	os_error *error;
 810	const char *source_data;
 811	unsigned long source_size;
 812
 813	/* does the user want to check for collisions when saving? */
 814	if (!force_overwrite) {
 815		fileswitch_object_type obj_type;
 816		/* check whether the destination file/dir already exists */
 817		error = xosfile_read_stamped(path, &obj_type,
 818				NULL, NULL, NULL, NULL, NULL);
 819		if (error) {
 820			LOG(("xosfile_read_stamped: 0x%x:%s", error->errnum, error->errmess));
 821			warn_user("SaveError", error->errmess);
 822			return false;
 823		}
 824
 825		switch (obj_type) {
 826			case osfile_NOT_FOUND:
 827				break;
 828
 829			case osfile_IS_FILE:
 830				gui_save_query = query_user("OverwriteFile", NULL, &overwrite_funcs, NULL,
 831								messages_get("Replace"), messages_get("DontReplace"));
 832//				gui_save_query_rsn = QueryRsn_Overwrite;
 833				return false;
 834
 835			default:
 836				error = xosfile_make_error(path, obj_type);
 837				assert(error);
 838				warn_user("SaveError", error->errmess);
 839				return false;
 840		}
 841	}
 842
 843	switch (gui_save_current_type) {
 844#ifdef WITH_DRAW_EXPORT
 845		case GUI_SAVE_DRAW:
 846			return save_as_draw(h, path);
 847#endif
 848#ifdef WITH_PDF_EXPORT
 849		case GUI_SAVE_PDF:
 850			return save_as_pdf(h, path);
 851#endif
 852		case GUI_SAVE_TEXT:
 853			save_as_text(h, path);
 854			xosfile_set_type(path, 0xfff);
 855			break;
 856		case GUI_SAVE_COMPLETE:
 857			assert(h);
 858			if (content_get_type(h) == CONTENT_HTML) {
 859				if (strcmp(path, "<Wimp$Scrap>"))
 860					return ro_gui_save_complete(h, path);
 861
 862				/* we can't send a whole directory to another
 863				 * application, so just send the HTML source */
 864				gui_save_current_type = GUI_SAVE_SOURCE;
 865			}
 866			else
 867				gui_save_current_type = GUI_SAVE_OBJECT_ORIG;	/* \todo do this earlier? */
 868			/* no break */
 869		case GUI_SAVE_SOURCE:
 870		case GUI_SAVE_OBJECT_ORIG:
 871			source_data = content_get_source_data(h, &source_size);
 872			error = xosfile_save_stamped(path,
 873					ro_content_filetype(h),
 874					(byte *) source_data,
 875					(byte *) source_data + source_size);
 876			if (error) {
 877				LOG(("xosfile_save_stamped: 0x%x: %s",
 878						error->errnum, error->errmess));
 879				warn_user("SaveError", error->errmess);
 880				return false;
 881			}
 882			break;
 883
 884		case GUI_SAVE_OBJECT_NATIVE:
 885			return ro_gui_save_object_native(h, path);
 886
 887		case GUI_SAVE_LINK_URI:
 888			return ro_gui_save_link(gui_save_url, gui_save_title,
 889					LINK_ACORN, path);
 890
 891		case GUI_SAVE_LINK_URL:
 892			return ro_gui_save_link(gui_save_url, gui_save_title,
 893					LINK_ANT, path);
 894
 895		case GUI_SAVE_LINK_TEXT:
 896			return ro_gui_save_link(gui_save_url, gui_save_title,
 897					LINK_TEXT, path);
 898
 899		case GUI_SAVE_HOTLIST_EXPORT_HTML:
 900			if (!options_save_tree(hotlist_tree, path,
 901					"NetSurf hotlist"))
 902				return false;
 903			error = xosfile_set_type(path, 0xfaf);
 904			if (error)
 905				LOG(("xosfile_set_type: 0x%x: %s",
 906						error->errnum, error->errmess));
 907			break;
 908		case GUI_SAVE_HISTORY_EXPORT_HTML:
 909			if (!options_save_tree(global_history_tree, path,
 910					"NetSurf history"))
 911				return false;
 912			error = xosfile_set_type(path, 0xfaf);
 913			if (error)
 914				LOG(("xosfile_set_type: 0x%x: %s",
 915						error->errnum, error->errmess));
 916			break;
 917
 918		case GUI_SAVE_TEXT_SELECTION:
 919			if (!selection_save_text(gui_save_selection, path))
 920				return false;
 921			xosfile_set_type(path, 0xfff);
 922			break;
 923
 924		case GUI_SAVE_CLIPBOARD_CONTENTS:
 925			return ro_gui_save_clipboard(path);
 926
 927		default:
 928			LOG(("Unexpected content type: %d, path %s",
 929					gui_save_current_type, path));
 930			return false;
 931	}
 932	return true;
 933}
 934
 935
 936/**
 937 * Save completed, inform recipient and close our 'save as' dialog.
 938 */
 939
 940void ro_gui_save_done(void)
 941{
 942	os_error *error;
 943
 944	if (gui_save_send_dataload) {
 945		/* Ack successful save with message_DATA_LOAD */
 946		wimp_message *message = &gui_save_message;
 947		message->action = message_DATA_LOAD;
 948		message->your_ref = message->my_ref;
 949		error = xwimp_send_message(wimp_USER_MESSAGE, message,
 950				message->sender);
 951		if (error) {
 952			LOG(("xwimp_send_message: 0x%x: %s",
 953					error->errnum, error->errmess));
 954			warn_user("SaveError", error->errmess);
 955		}
 956	}
 957
 958	if (saving_from_dialog) {
 959		/* remember the save directory if saving to the Filer */
 960		if (!gui_save_send_dataload ||
 961			gui_save_message.data.data_xfer.est_size != -1) {
 962			char *sp = gui_save_message.data.data_xfer.file_name;
 963			char *ep = sp + sizeof(gui_save_message.data.data_xfer.file_name);
 964			char *lastdot = NULL;
 965			char *p = sp;
 966
 967			while (p < ep && *p >= 0x20) {
 968				if (*p == '.') {
 969					/* don't remember the directory if it's a temporary file */
 970					if (!lastdot && p == sp + 12 &&
 971						!memcmp(sp, "<Wimp$Scrap>", 12)) break;
 972					lastdot = p;
 973				}
 974				p++;
 975			}
 976			if (lastdot) {
 977				/* remember the directory */
 978				char *new_dir = realloc(save_dir, (lastdot+1)-sp);
 979				if (new_dir) {
 980					save_dir_len = lastdot - sp;
 981					memcpy(new_dir, sp, save_dir_len);
 982					new_dir[save_dir_len] = '\0';
 983					save_dir = new_dir;
 984				}
 985			}
 986		}
 987
 988		if (gui_save_close_after) {
 989			/*	Close the save window */
 990			ro_gui_dialog_close(dialog_saveas);
 991			error = xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
 992			if (error) {
 993				LOG(("xwimp_create_menu: 0x%x: %s",
 994						error->errnum, error->errmess));
 995				warn_user("MenuError", error->errmess);
 996			}
 997		}
 998	}
 999
1000	if (!saving_from_dialog || gui_save_close_after)
1001		gui_save_content = 0;
1002}
1003
1004/**
1005* conducts the filesystem save appropriate to the gui
1006* \param path save path
1007* \param filename name of file to save
1008* \param len data length
1009* \param sourcedata pointer to data to save, NULL when all data in c
1010* \param type content type
1011* \return true for success
1012*/
1013
1014bool save_complete_gui_save(const char *path, const char *filename, size_t len,
1015		const char *sourcedata, content_type type)
1016{
1017	char *fullpath;
1018	os_error *error;
1019	int namelen = strlen(path) + strlen(filename) + 2;
1020	int rotype;
1021	fullpath = malloc(namelen);
1022	if (fullpath == NULL) {
1023		warn_user("NoMemory", 0);
1024		return false;
1025	}
1026	snprintf(fullpath, namelen, "%s.%s", path, filename);
1027	rotype = ro_content_filetype_from_type(type);
1028	error = xosfile_save_stamped(fullpath, rotype, (byte *) sourcedata, 
1029			(byte *) sourcedata + len);
1030	free(fullpath);
1031	if (error) {
1032		LOG(("xosfile_save_stamped: 0x%x: %s",
1033				error->errnum, error->errmess));
1034		warn_user("SaveError", error->errmess);
1035		return false;
1036	}
1037	return true;
1038}
1039
1040/**
1041* wrapper for lib function htmlSaveFileFormat; front sets path from 
1042* path + filename in a filesystem-specific way
1043*/
1044
1045int save_complete_htmlSaveFileFormat(const char *path, const char *filename, 
1046		xmlDocPtr cur, const char *encoding, int format)
1047{
1048	os_error *error;
1049	int ret;
1050	int len = strlen(path) + strlen(filename) + 2;
1051	char *fullpath = malloc(len);
1052	if (fullpath == NULL){
1053		warn_user("NoMemory", 0);
1054		return -1;
1055	}
1056	snprintf(fullpath, len, "%s.%s", path, filename);
1057
1058	ret = htmlSaveFileFormat(fullpath, cur, encoding, format);
1059
1060	error = xosfile_set_type(fullpath, 0xFAF);
1061	if (error) {
1062		LOG(("xosfile_save_stamped: 0x%x: %s",
1063				error->errnum, error->errmess));
1064		/* Don't warn the user here -- they probably don't care */
1065	}
1066
1067	free(fullpath);
1068	return ret;
1069}
1070
1071/**
1072 * Prepare an application directory and save_complete() to it.
1073 *
1074 * \param  h     content of type CONTENT_HTML to save
1075 * \param  path  path to save as
1076 * \return  true on success, false on error and error reported
1077 */
1078
1079#define WIDTH 64
1080#define HEIGHT 64
1081#define SPRITE_SIZE (16 + 44 + ((WIDTH / 2 + 3) & ~3) * HEIGHT / 2)
1082
1083bool ro_gui_save_complete(hlcache_handle *h, char *path)
1084{
1085	void *spr = ((byte *) saveas_area) + saveas_area->first;
1086	osspriteop_header *sprite = (osspriteop_header *) spr;
1087	char name[12];
1088	char buf[256];
1089	FILE *fp;
1090	os_error *error;
1091	size_t len;
1092	char *dot;
1093	int i;
1094
1095	/* Create dir */
1096	error = xosfile_create_dir(path, 0);
1097	if (error) {
1098		LOG(("xosfile_create_dir: 0x%x: %s",
1099				error->errnum, error->errmess));
1100		warn_user("SaveError", error->errmess);
1101		return false;
1102	}
1103
1104	/* Save !Run file */
1105	snprintf(buf, sizeof buf, "%s.!Run", path);
1106	fp = fopen(buf, "w");
1107	if (!fp) {
1108		LOG(("fopen(): errno = %i", errno));
1109		warn_user("SaveError", strerror(errno));
1110		return false;
1111	}
1112	fprintf(fp, "IconSprites <Obey$Dir>.!Sprites\n");
1113	fprintf(fp, "Filer_Run <Obey$Dir>.index\n");
1114	fclose(fp);
1115	error = xosfile_set_type(buf, 0xfeb);
1116	if (error) {
1117		LOG(("xosfile_set_type: 0x%x: %s",
1118				error->errnum, error->errmess));
1119		warn_user("SaveError", error->errmess);
1120		return false;
1121	}
1122
1123	/* Make sure the sprite name matches the directory name, because
1124	   the user may have renamed the directory since we created the
1125	   thumbnail sprite */
1126
1127	dot = strrchr(path, '.');
1128	if (dot) dot++; else dot = path;
1129	len = strlen(dot);
1130	if (len >= 12) len = 12;
1131
1132	memcpy(name, sprite->name, 12);  /* remember original name */
1133	memcpy(sprite->name, dot, len);
1134	memset(sprite->name + len, 0, 12 - len);
1135	for (i = 0; i < 12; i++) /* convert to lower case */
1136		if (sprite->name[i] != '\0')
1137			sprite->name[i] = tolower(sprite->name[i]);
1138
1139	/* Create !Sprites */
1140	snprintf(buf, sizeof buf, "%s.!Sprites", path);
1141
1142	error = xosspriteop_save_sprite_file(osspriteop_NAME, saveas_area, buf);
1143	if (error) {
1144		LOG(("xosspriteop_save_sprite_file: 0x%x: %s",
1145				error->errnum, error->errmess));
1146		warn_user("SaveError", error->errmess);
1147	        return false;
1148	}
1149
1150	/* restore sprite name in case the save fails and we need to try again */
1151	memcpy(sprite->name, name, 12);
1152
1153	/* save URL file with original URL */
1154	snprintf(buf, sizeof buf, "%s.URL", path);
1155	if (!ro_gui_save_link(content_get_url(h), content_get_title(h),
1156			LINK_ANT, buf))
1157		return false;
1158
1159	return save_complete(h, path);
1160}
1161
1162bool ro_gui_save_object_native(hlcache_handle *h, char *path)
1163{
1164	const char *source_data;
1165	unsigned long source_size;
1166
1167	switch (content_get_type(h)) {
1168#ifdef WITH_JPEG
1169		case CONTENT_JPEG:
1170#endif
1171#if defined(WITH_MNG) || defined(WITH_PNG)
1172		case CONTENT_PNG:
1173#endif
1174#ifdef WITH_MNG
1175		case CONTENT_JNG:
1176		case CONTENT_MNG:
1177#endif
1178#ifdef WITH_GIF
1179		case CONTENT_GIF:
1180#endif
1181#ifdef WITH_BMP
1182		case CONTENT_BMP:
1183		case CONTENT_ICO:
1184#endif
1185		{
1186			unsigned flags = (os_version == 0xA9) ?
1187					BITMAP_SAVE_FULL_ALPHA : 0;
1188			bitmap_save(content_get_bitmap(h), path, flags);
1189			return true;
1190		}
1191		break;
1192#ifdef WITH_SPRITE
1193		case CONTENT_SPRITE:
1194#endif
1195#ifdef WITH_DRAW
1196		case CONTENT_DRAW:
1197#endif
1198		{
1199			os_error *error;
1200			source_data = content_get_source_data(h, &source_size);
1201			error = xosfile_save_stamped(path,
1202					ro_content_filetype(h),
1203					(byte *) source_data,
1204					(byte *) source_data + source_size);
1205			if (error) {
1206				LOG(("xosfile_save_stamped: 0x%x: %s",
1207						error->errnum, error->errmess));
1208				warn_user("SaveError", error->errmess);
1209				return false;
1210			}
1211			return true;
1212		}
1213		break;
1214#if defined(WITH_NS_SVG) || defined(WITH_RSVG)
1215		case CONTENT_SVG:
1216			return save_as_draw(h, path);
1217#endif
1218		default:
1219			return false;
1220	}
1221}
1222
1223
1224/**
1225 * Save a link file.
1226 *
1227 * \param  url     url to be saved
1228 * \param  title   corresponding title, if any
1229 * \param  format  format of link file
1230 * \param  path    pathname for link file
1231 * \return  true on success, false on failure and reports the error
1232 */
1233
1234bool ro_gui_save_link(const char *url, const char *title, link_format format,
1235		char *path)
1236{
1237	FILE *fp = fopen(path, "w");
1238
1239	if (!fp) {
1240		warn_user("SaveError", strerror(errno));
1241		return false;
1242	}
1243
1244	switch (format) {
1245		case LINK_ACORN: /* URI */
1246			fprintf(fp, "%s\t%s\n", "URI", "100");
1247			fprintf(fp, "\t# NetSurf %s\n\n", netsurf_version);
1248			fprintf(fp, "\t%s\n", url);
1249			if (title)
1250				fprintf(fp, "\t%s\n", title);
1251			else
1252				fprintf(fp, "\t*\n");
1253			break;
1254		case LINK_ANT: /* URL */
1255		case LINK_TEXT: /* Text */
1256			fprintf(fp, "%s\n", url);
1257			break;
1258	}
1259
1260	fclose(fp);
1261
1262	switch (format) {
1263		case LINK_ACORN: /* URI */
1264			xosfile_set_type(path, 0xf91);
1265			break;
1266		case LINK_ANT: /* URL */
1267			xosfile_set_type(path, 0xb28);
1268			break;
1269		case LINK_TEXT: /* Text */
1270			xosfile_set_type(path, 0xfff);
1271			break;
1272	}
1273
1274	return true;
1275}
1276
1277
1278/**
1279 * Suggest a leafname and sprite name for the given content.
1280 *
1281 * \param  h          content being saved
1282 * \param  save_type  type of save operation being performed
1283 * \param  url        used to determine leafname
1284 * \param  leaf_buf   buffer to receive suggested leafname, length at least
1285 *                    LEAFNAME_MAX
1286 * \param  icon_buf   buffer to receive sprite name, length at least 13
1287 */
1288
1289void ro_gui_save_set_state(hlcache_handle *h, gui_save_type save_type,
1290		const char *url, char *leaf_buf, char *icon_buf)
1291{
1292	/* filename */
1293	const char *name = gui_save_table[save_type].name;
1294	bool done = false;
1295	char *nice = NULL;
1296	utf8_convert_ret err;
1297	char *local_name;
1298	size_t i;
1299
1300	/* parameters that we need to remember */
1301	gui_save_current_type = save_type;
1302	gui_save_content = h;
1303
1304	/* suggest a filetype based upon the content */
1305	gui_save_filetype = gui_save_table[save_type].filetype;
1306	if (!gui_save_filetype && h) {
1307		if (save_type == GUI_SAVE_OBJECT_NATIVE) {
1308			switch (content_get_type(h)) {
1309				/* bitmap images */
1310#ifdef WITH_JPEG
1311				case CONTENT_JPEG:
1312#endif
1313#if defined(WITH_MNG) || defined(WITH_PNG)
1314				case CONTENT_PNG:
1315#endif
1316#ifdef WITH_MNG
1317				case CONTENT_JNG:
1318				case CONTENT_MNG:
1319#endif
1320#ifdef WITH_GIF
1321				case CONTENT_GIF:
1322#endif
1323#ifdef WITH_BMP
1324				case CONTENT_BMP:
1325				case CONTENT_ICO:
1326#endif
1327					gui_save_filetype = osfile_TYPE_SPRITE;
1328					break;
1329				/* vector formats */
1330#if defined(WITH_NS_SVG) || defined(WITH_RSVG)
1331				case CONTENT_SVG:
1332					gui_save_filetype = osfile_TYPE_DRAW;
1333					break;
1334#endif
1335#ifdef WITH_DRAW
1336				case CONTENT_DRAW:
1337					gui_save_filetype = osfile_TYPE_DRAW;
1338					break;
1339#endif
1340				default:
1341					break;
1342			}
1343		}
1344		if (!gui_save_filetype)
1345			gui_save_filetype = ro_content_filetype(h);
1346	}
1347
1348	/* leafname */
1349	if (url && url_nice(url, &nice, option_strip_extensions) == 
1350			URL_FUNC_OK) {
1351		for (i = 0; nice[i]; i++) {
1352			if (nice[i] == '.')
1353				nice[i] = '/';
1354			else if (nice[i] <= ' ' ||
1355					strchr(":*#$&@^%\\", nice[i]))
1356				nice[i] = '_';
1357		}
1358		name = nice;
1359	} else {
1360		name = messages_get(name);
1361	}
1362
1363	/* filename is utf8 */
1364	strncpy(leaf_buf, name, LEAFNAME_MAX);
1365	leaf_buf[LEAFNAME_MAX - 1] = 0;
1366
1367	err = utf8_to_local_encoding(name, 0, &local_name);
1368	if (err != UTF8_CONVERT_OK) {
1369		/* badenc should never happen */
1370		assert(err != UTF8_CONVERT_BADENC);
1371		local_name = NULL;
1372	}
1373
1374	name = local_name ? local_name : name;
1375
1376	/* sprite name used for icon and dragging */
1377	if (save_type == GUI_SAVE_COMPLETE) {
1378		int index;
1379
1380		/* Paint gets confused with uppercase characters and we need to
1381		   convert spaces to hard spaces */
1382		icon_buf[0] = '!';
1383		for (index = 0; index < 11 && name[index]; ) {
1384			char ch = name[index];
1385			if (ch == ' ')
1386				icon_buf[++index] = 0xa0;
1387			else
1388				icon_buf[++index] = tolower(ch);
1389		}
1390		memset(&icon_buf[index + 1], 0, 11 - index);
1391		icon_buf[12] = '\0';
1392
1393		if (ro_gui_save_create_thumbnail(h, icon_buf))
1394			done = true;
1395	}
1396
1397	if (!done) {
1398		osspriteop_header *sprite;
1399		os_error *error;
1400
1401		sprintf(icon_buf, "file_%.3x", gui_save_filetype);
1402
1403		error = ro_gui_wimp_get_sprite(icon_buf, &sprite);
1404		if (error && error->errnum == error_SPRITE_OP_DOESNT_EXIST) {
1405			/* try the 'unknown' filetype sprite as a fallback */
1406			memcpy(icon_buf, "file_xxx", 9);
1407			error = ro_gui_wimp_get_sprite(icon_buf, &sprite);
1408		}
1409
1410		if (error) {
1411			LOG(("ro_gui_wimp_get_sprite: 0x%x: %s",
1412					error->errnum, error->errmess));
1413			warn_user("MiscError", error->errmess);
1414		} else {
1415			/* the sprite area should always be large enough for
1416			 * file_xxx sprites */
1417			assert(sprite->size <= saveas_area->size -
1418					saveas_area->first);
1419
1420			memcpy((byte*)saveas_area + saveas_area->first,
1421					sprite,
1422					sprite->size);
1423
1424			saveas_area->sprite_count = 1;
1425			saveas_area->used = saveas_area->first + sprite->size;
1426		}
1427	}
1428
1429	free(local_name);
1430	free(nice);
1431}
1432
1433
1434
1435/**
1436 * Create a thumbnail sprite for the page being saved.
1437 *
1438 * \param  h     content to be converted
1439 * \param  name  sprite name to use
1440 * \return true iff successful
1441 */
1442
1443bool ro_gui_save_create_thumbnail(hlcache_handle *h, const char *name)
1444{
1445	osspriteop_header *sprite_header;
1446	struct bitmap *bitmap;
1447	osspriteop_area *area;
1448
1449	bitmap = bitmap_create(34, 34, BITMAP_NEW | BITMAP_OPAQUE | BITMAP_CLEAR_MEMORY);
1450	if (!bitmap) {
1451		LOG(("Thumbnail initialisation failed."));
1452		return false;
1453	}
1454	thumbnail_create(h, bitmap, NULL);
1455	area = thumbnail_convert_8bpp(bitmap);
1456	bitmap_destroy(bitmap);
1457	if (!area) {
1458		LOG(("Thumbnail conversion failed."));
1459		return false;
1460	}
1461
1462	sprite_header = (osspriteop_header *)(area + 1);
1463	strncpy(sprite_header->name, name, 12);
1464
1465
1466	/* we can't resize the saveas sprite area because it may move and we have
1467	   no elegant way to update the window definition on all OS versions */
1468	assert(sprite_header->size <= saveas_area->size - saveas_area->first);
1469
1470	memcpy((byte*)saveas_area + saveas_area->first,
1471		sprite_header, sprite_header->size);
1472
1473	saveas_area->sprite_count = 1;
1474	saveas_area->used = saveas_area->first + sprite_header->size;
1475
1476	free(area);
1477
1478	return true;
1479}
1480
1481
1482/**
1483 * User has opted not to overwrite the existing file.
1484 */
1485
1486void ro_gui_save_overwrite_cancelled(query_id id, enum query_response res, void *p)
1487{
1488	if (!saving_from_dialog) {
1489//		ro_gui_save_prepare(gui_save_current_type, gui_save_content);
1490//		ro_gui_dialog_open_persistent(g->window, dialog_saveas, true);
1491	}
1492}
1493
1494
1495/**
1496 * Overwrite of existing file confirmed, proceed with the save.
1497 */
1498
1499void ro_gui_save_overwrite_confirmed(query_id id, enum query_response res, void *p)
1500{
1501	if (ro_gui_save_content(gui_save_content, gui_save_message.data.data_xfer.file_name, true))
1502		ro_gui_save_done();
1503}