/peek-build/src/netsurf/riscos/download.c
C | 1604 lines | 1495 code | 44 blank | 65 comment | 30 complexity | bc9e49153b2322e8dab93236e5034d80 MD5 | raw file
1/* 2 * Copyright 2004 James Bursa <bursa@users.sourceforge.net> 3 * Copyright 2003 Rob Jackson <jacko@xms.ms> 4 * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net> 5 * 6 * This file is part of NetSurf, http://www.netsurf-browser.org/ 7 * 8 * NetSurf is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; version 2 of the License. 11 * 12 * NetSurf is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21/** \file 22 * Download windows (RISC OS implementation). 23 * 24 * This file implements the interface given by desktop/gui.h for download 25 * windows. Each download window has an associated fetch. Downloads start by 26 * writing received data to a temporary file. At some point the user chooses 27 * a destination (by drag & drop), and the temporary file is then moved to the 28 * destination and the download continues until complete. 29 */ 30 31#include <assert.h> 32#include <string.h> 33#include <sys/time.h> 34#include <time.h> 35#include <curl/curl.h> 36#include "oslib/mimemap.h" 37#include "oslib/osargs.h" 38#include "oslib/osfile.h" 39#include "oslib/osfind.h" 40#include "oslib/osfscontrol.h" 41#include "oslib/osgbpb.h" 42#include "oslib/wimp.h" 43#include "oslib/wimpspriteop.h" 44#include "desktop/gui.h" 45#include "desktop/netsurf.h" 46#include "riscos/dialog.h" 47#include "riscos/options.h" 48#include "riscos/save.h" 49#include "riscos/query.h" 50#include "riscos/wimp.h" 51#include "riscos/wimp_event.h" 52#include "utils/log.h" 53#include "utils/messages.h" 54#include "utils/url.h" 55#include "utils/utf8.h" 56#include "utils/utils.h" 57 58#define ICON_DOWNLOAD_ICON 0 59#define ICON_DOWNLOAD_URL 1 60#define ICON_DOWNLOAD_PATH 2 61#define ICON_DOWNLOAD_DESTINATION 3 62#define ICON_DOWNLOAD_PROGRESS 5 63#define ICON_DOWNLOAD_STATUS 6 64 65typedef enum 66{ 67 QueryRsn_Quit, 68 QueryRsn_Abort, 69 QueryRsn_Overwrite 70} query_reason; 71 72 73/** Data for a download window. */ 74struct gui_download_window { 75 /** Associated context, or 0 if the fetch has completed or aborted. */ 76 download_context *ctx; 77 unsigned int received; /**< Amount of data received so far. */ 78 unsigned int total_size; /**< Size of resource, or 0 if unknown. */ 79 80 wimp_w window; /**< RISC OS window handle. */ 81 bits file_type; /**< RISC OS file type. */ 82 83 char url[256]; /**< Buffer for URL icon. */ 84 char sprite_name[20]; /**< Buffer for sprite icon. */ 85 char path[256]; /**< Buffer for pathname icon. */ 86 char status[256]; /**< Buffer for status icon. */ 87 88 /** User has chosen the destination, and it is being written. */ 89 bool saved; 90 bool close_confirmed; 91 bool error; /**< Error occurred, aborted. */ 92 93 /** RISC OS file handle, of temporary file when !saved, and of 94 * destination when saved. */ 95 os_fw file; 96 97 query_id query; 98 query_reason query_rsn; 99 100 struct timeval start_time; /**< Time download started. */ 101 struct timeval last_time; /**< Time status was last updated. */ 102 unsigned int last_received; /**< Value of received at last_time. */ 103 104 bool send_dataload; /**< Should send DataLoad message when finished */ 105 wimp_message save_message; /**< Copy of wimp DataSaveAck message */ 106 107 struct gui_download_window *prev; /**< Previous in linked list. */ 108 struct gui_download_window *next; /**< Next in linked list. */ 109}; 110 111 112/** List of all download windows. */ 113static struct gui_download_window *download_window_list = 0; 114/** Download window with current save operation. */ 115static struct gui_download_window *download_window_current = 0; 116 117/** Template for a download window. */ 118static wimp_window *download_template; 119 120/** Width of progress bar at 100%. */ 121static int download_progress_width; 122/** Coordinates of progress bar. */ 123static int download_progress_x0; 124static int download_progress_y0; 125static int download_progress_y1; 126 127/** Current download directory. */ 128static char *download_dir = NULL; 129static size_t download_dir_len; 130 131 132static const char *ro_gui_download_temp_name(struct gui_download_window *dw); 133static void ro_gui_download_update_status(struct gui_download_window *dw); 134static void ro_gui_download_update_status_wrapper(void *p); 135static void ro_gui_download_window_hide_caret(struct gui_download_window *dw); 136static char *ro_gui_download_canonicalise(const char *path); 137static bool ro_gui_download_check_space(struct gui_download_window *dw, 138 const char *dest_file, const char *orig_file); 139static os_error *ro_gui_download_move(struct gui_download_window *dw, 140 const char *dest_file, const char *src_file); 141static void ro_gui_download_remember_dir(const char *path); 142static bool ro_gui_download_save(struct gui_download_window *dw, 143 const char *file_name, bool force_overwrite); 144static void ro_gui_download_send_dataload(struct gui_download_window *dw); 145static void ro_gui_download_window_destroy_wrapper(void *p); 146static bool ro_gui_download_window_destroy(struct gui_download_window *dw, bool quit); 147static void ro_gui_download_close_confirmed(query_id, enum query_response res, void *p); 148static void ro_gui_download_close_cancelled(query_id, enum query_response res, void *p); 149static void ro_gui_download_overwrite_confirmed(query_id, enum query_response res, void *p); 150static void ro_gui_download_overwrite_cancelled(query_id, enum query_response res, void *p); 151 152static bool ro_gui_download_click(wimp_pointer *pointer); 153static bool ro_gui_download_keypress(wimp_key *key); 154static void ro_gui_download_close(wimp_w w); 155 156static const query_callback close_funcs = 157{ 158 ro_gui_download_close_confirmed, 159 ro_gui_download_close_cancelled 160}; 161 162static const query_callback overwrite_funcs = 163{ 164 ro_gui_download_overwrite_confirmed, 165 ro_gui_download_overwrite_cancelled 166}; 167 168 169/** 170 * Load the download window template. 171 */ 172 173void ro_gui_download_init(void) 174{ 175 download_template = ro_gui_dialog_load_template("download"); 176 download_progress_width = 177 download_template->icons[ICON_DOWNLOAD_STATUS].extent.x1 - 178 download_template->icons[ICON_DOWNLOAD_STATUS].extent.x0; 179 download_progress_x0 = 180 download_template->icons[ICON_DOWNLOAD_PROGRESS].extent.x0; 181 download_progress_y0 = 182 download_template->icons[ICON_DOWNLOAD_PROGRESS].extent.y0; 183 download_progress_y1 = 184 download_template->icons[ICON_DOWNLOAD_PROGRESS].extent.y1; 185} 186 187 188/** 189 * Returns the pathname of a temporary file for this download. 190 * 191 * \param dw download window 192 * \return ptr to pathname 193 */ 194 195const char *ro_gui_download_temp_name(struct gui_download_window *dw) 196{ 197 static char temp_name[40]; 198 snprintf(temp_name, sizeof temp_name, "<Wimp$ScrapDir>.ns%x", 199 (unsigned int) dw); 200 return temp_name; 201} 202 203 204/** 205 * Create and open a download progress window. 206 * 207 * \param ctx Download context 208 * \return a new gui_download_window structure, or 0 on error and error 209 * reported 210 */ 211 212struct gui_download_window *gui_download_window_create(download_context *ctx, 213 struct gui_window *gui) 214{ 215 const char *url = download_context_get_url(ctx); 216 const char *mime_type = download_context_get_mime_type(ctx); 217 const char *temp_name; 218 char *nice, *scheme = NULL; 219 struct gui_download_window *dw; 220 bool space_warning = false; 221 os_error *error; 222 url_func_result res; 223 char *local_path; 224 utf8_convert_ret err; 225 size_t leaf_ofst; 226 size_t i; 227 228 dw = malloc(sizeof *dw); 229 if (!dw) { 230 warn_user("NoMemory", 0); 231 return 0; 232 } 233 234 dw->ctx = ctx; 235 dw->saved = false; 236 dw->close_confirmed = false; 237 dw->error = false; 238 dw->query = QUERY_INVALID; 239 dw->received = 0; 240 dw->total_size = download_context_get_total_length(ctx); 241 strncpy(dw->url, url, sizeof dw->url); 242 dw->url[sizeof dw->url - 1] = 0; 243 dw->status[0] = 0; 244 gettimeofday(&dw->start_time, 0); 245 dw->last_time = dw->start_time; 246 dw->last_received = 0; 247 dw->file_type = 0; 248 249 /* Get scheme from URL */ 250 res = url_scheme(url, &scheme); 251 if (res == URL_FUNC_NOMEM) { 252 warn_user("NoMemory", 0); 253 free(dw); 254 return 0; 255 } else if (res == URL_FUNC_OK) { 256 /* If we have a scheme and it's "file", then 257 * attempt to use the local filetype directly */ 258 if (strcasecmp(scheme, "file") == 0) { 259 char *path = NULL; 260 res = url_path(url, &path); 261 if (res == URL_FUNC_NOMEM) { 262 warn_user("NoMemory", 0); 263 free(scheme); 264 free(dw); 265 return 0; 266 } else if (res == URL_FUNC_OK) { 267 char *raw_path = curl_unescape(path, 268 strlen(path)); 269 if (raw_path == NULL) { 270 warn_user("NoMemory", 0); 271 free(path); 272 free(scheme); 273 free(dw); 274 return 0; 275 } 276 dw->file_type = 277 ro_filetype_from_unix_path(raw_path); 278 curl_free(raw_path); 279 free(path); 280 } 281 } 282 283 free(scheme); 284 } 285 286 /* If we still don't have a filetype (i.e. failed reading local 287 * one or fetching a remote object), then use the MIME type */ 288 if (dw->file_type == 0) { 289 /* convert MIME type to RISC OS file type */ 290 error = xmimemaptranslate_mime_type_to_filetype(mime_type, 291 &(dw->file_type)); 292 if (error) { 293 LOG(("xmimemaptranslate_mime_type_to_filetype: 0x%x: %s", 294 error->errnum, error->errmess)); 295 warn_user("MiscError", error->errmess); 296 dw->file_type = 0xffd; 297 } 298 } 299 300 /* open temporary output file */ 301 temp_name = ro_gui_download_temp_name(dw); 302 if (!ro_gui_download_check_space(dw, temp_name, NULL)) { 303 /* issue a warning but continue with the download because the 304 user can save it to another medium whilst it's downloading */ 305 space_warning = true; 306 } 307 error = xosfind_openoutw(osfind_NO_PATH | osfind_ERROR_IF_DIR, 308 temp_name, 0, &dw->file); 309 if (error) { 310 LOG(("xosfind_openoutw: 0x%x: %s", 311 error->errnum, error->errmess)); 312 warn_user("SaveError", error->errmess); 313 free(dw); 314 return 0; 315 } 316 317 /* fill in download window icons */ 318 download_template->icons[ICON_DOWNLOAD_URL].data.indirected_text.text = 319 dw->url; 320 download_template->icons[ICON_DOWNLOAD_URL].data.indirected_text.size = 321 sizeof dw->url; 322 323 download_template->icons[ICON_DOWNLOAD_STATUS].data.indirected_text. 324 text = dw->status; 325 download_template->icons[ICON_DOWNLOAD_STATUS].data.indirected_text. 326 size = sizeof dw->status; 327 328 sprintf(dw->sprite_name, "file_%.3x", dw->file_type); 329 if (!ro_gui_wimp_sprite_exists(dw->sprite_name)) 330 strcpy(dw->sprite_name, "file_xxx"); 331 download_template->icons[ICON_DOWNLOAD_ICON].data.indirected_sprite.id = 332 (osspriteop_id) dw->sprite_name; 333 334 if (download_dir) { 335 memcpy(dw->path, download_dir, download_dir_len); 336 dw->path[download_dir_len] = '.'; 337 leaf_ofst = download_dir_len + 1; 338 } else 339 leaf_ofst = 0; 340 341 if (url_nice(url, &nice, option_strip_extensions) == URL_FUNC_OK) { 342 size_t imax = sizeof dw->path - (leaf_ofst + 1); 343 for (i = 0; i < imax && nice[i]; i++) { 344 if (nice[i] == '.') 345 nice[i] = '/'; 346 else if (nice[i] <= ' ' || 347 strchr(":*#$&@^%\\", nice[i])) 348 nice[i] = '_'; 349 } 350 memcpy(dw->path + leaf_ofst, nice, i); 351 dw->path[leaf_ofst + i] = '\0'; 352 free(nice); 353 } else { 354 const char *leaf = messages_get("SaveObject"); 355 size_t len = strlen(leaf); 356 if (len >= sizeof dw->path - leaf_ofst) 357 len = sizeof dw->path - leaf_ofst - 1; 358 memcpy(dw->path + leaf_ofst, leaf, len); 359 dw->path[leaf_ofst + len] = '\0'; 360 } 361 362 err = utf8_to_local_encoding(dw->path, 0, &local_path); 363 if (err != UTF8_CONVERT_OK) { 364 /* badenc should never happen */ 365 assert(err != UTF8_CONVERT_BADENC); 366 LOG(("utf8_to_local_encoding failed")); 367 warn_user("NoMemory", 0); 368 free(dw); 369 return 0; 370 } 371 else { 372 strncpy(dw->path, local_path, sizeof dw->path); 373 free(local_path); 374 } 375 376 download_template->icons[ICON_DOWNLOAD_PATH].data.indirected_text.text = 377 dw->path; 378 download_template->icons[ICON_DOWNLOAD_PATH].data.indirected_text.size = 379 sizeof dw->path; 380 381 download_template->icons[ICON_DOWNLOAD_DESTINATION].data. 382 indirected_text.text = dw->path; 383 download_template->icons[ICON_DOWNLOAD_DESTINATION].data. 384 indirected_text.size = sizeof dw->path; 385 386 download_template->icons[ICON_DOWNLOAD_DESTINATION].flags |= 387 wimp_ICON_DELETED; 388 389 /* create and open the download window */ 390 error = xwimp_create_window(download_template, &dw->window); 391 if (error) { 392 LOG(("xwimp_create_window: 0x%x: %s", 393 error->errnum, error->errmess)); 394 warn_user("WimpError", error->errmess); 395 free(dw); 396 return 0; 397 } 398 399 dw->prev = 0; 400 dw->next = download_window_list; 401 if (download_window_list) 402 download_window_list->prev = dw; 403 download_window_list = dw; 404 405 ro_gui_download_update_status(dw); 406 407 ro_gui_dialog_open(dw->window); 408 409 ro_gui_wimp_event_set_user_data(dw->window, dw); 410 ro_gui_wimp_event_register_mouse_click(dw->window, ro_gui_download_click); 411 ro_gui_wimp_event_register_keypress(dw->window, ro_gui_download_keypress); 412 ro_gui_wimp_event_register_close_window(dw->window, ro_gui_download_close); 413 414 /* issue the warning now, so that it appears in front of the download 415 * window! */ 416 if (space_warning) 417 warn_user("DownloadWarn", messages_get("NoDiscSpace")); 418 419 return dw; 420} 421 422 423/** 424 * Handle received download data. 425 * 426 * \param dw download window 427 * \param data pointer to block of data received 428 * \param size size of data 429 * \return NSERROR_OK on success, appropriate error otherwise 430 */ 431 432nserror gui_download_window_data(struct gui_download_window *dw, 433 const char *data, unsigned int size) 434{ 435 while (true) { 436 const char *msg; 437 int unwritten; 438 os_error *error; 439 440 error = xosgbpb_writew(dw->file, (const byte *) data, size, 441 &unwritten); 442 if (error) { 443 LOG(("xosgbpb_writew: 0x%x: %s", 444 error->errnum, error->errmess)); 445 msg = error->errmess; 446 447 } else if (unwritten) { 448 LOG(("xosgbpb_writew: unwritten %i", unwritten)); 449 msg = messages_get("Unwritten"); 450 } 451 else { 452 dw->received += size; 453 return NSERROR_OK; 454 } 455 456 warn_user("SaveError", msg); 457 458 if (dw->saved) { 459 /* try to continue with the temporary file */ 460 const char *temp_name = ro_gui_download_temp_name(dw); 461 462 error = ro_gui_download_move(dw, temp_name, dw->path); 463 if (!error) { 464 465 /* re-allow saving */ 466 dw->saved = false; 467 468 error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON, 469 wimp_ICON_SHADED, 0); 470 if (error) { 471 LOG(("xwimp_set_icon_state: 0x%x: %s", 472 error->errnum, error->errmess)); 473 warn_user("WimpError", error->errmess); 474 } 475 476 error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_DESTINATION, 477 wimp_ICON_DELETED, wimp_ICON_DELETED); 478 if (error) { 479 LOG(("xwimp_set_icon_state: 0x%x: %s", 480 error->errnum, error->errmess)); 481 warn_user("WimpError", error->errmess); 482 } 483 error = xwimp_set_icon_state(dw->window, 484 ICON_DOWNLOAD_PATH, wimp_ICON_DELETED, 0); 485 if (error) { 486 LOG(("xwimp_set_icon_state: 0x%x: %s", 487 error->errnum, error->errmess)); 488 warn_user("WimpError", error->errmess); 489 } 490 491 continue; 492 } 493 } 494 495 /* give up then */ 496 assert(dw->ctx); 497 download_context_abort(dw->ctx); 498 gui_download_window_error(dw, msg); 499 500 return NSERROR_SAVE_FAILED; 501 } 502} 503 504 505/** 506 * Update the status text and progress bar. 507 * 508 * \param dw download window 509 */ 510 511void ro_gui_download_update_status(struct gui_download_window *dw) 512{ 513 char *received; 514 char *total_size; 515 char *speed; 516 char time[20] = "?"; 517 struct timeval t; 518 float dt; 519 unsigned int left; 520 float rate; 521 os_error *error; 522 int width; 523 char *local_status; 524 utf8_convert_ret err; 525 526 gettimeofday(&t, 0); 527 dt = (t.tv_sec + 0.000001 * t.tv_usec) - (dw->last_time.tv_sec + 528 0.000001 * dw->last_time.tv_usec); 529 if (dt == 0) 530 dt = 0.001; 531 532 total_size = human_friendly_bytesize(max(dw->received, dw->total_size)); 533 534 if (dw->ctx) { 535 rate = (dw->received - dw->last_received) / dt; 536 received = human_friendly_bytesize(dw->received); 537 speed = human_friendly_bytesize(rate); 538 if (dw->total_size) { 539 float f; 540 541 if (rate) { 542 left = (dw->total_size - dw->received) / rate; 543 sprintf(time, "%u:%.2u", left / 60, left % 60); 544 } 545 546 /* convert to local encoding */ 547 err = utf8_to_local_encoding( 548 messages_get("Download"), 0, &local_status); 549 if (err != UTF8_CONVERT_OK) { 550 /* badenc should never happen */ 551 assert(err != UTF8_CONVERT_BADENC); 552 /* hide nomem error */ 553 snprintf(dw->status, sizeof dw->status, 554 messages_get("Download"), 555 received, total_size, speed, time); 556 } 557 else { 558 snprintf(dw->status, sizeof dw->status, 559 local_status, 560 received, total_size, speed, time); 561 free(local_status); 562 } 563 564 f = (float) dw->received / (float) dw->total_size; 565 width = download_progress_width * f; 566 } else { 567 left = t.tv_sec - dw->start_time.tv_sec; 568 sprintf(time, "%u:%.2u", left / 60, left % 60); 569 570 err = utf8_to_local_encoding( 571 messages_get("DownloadU"), 0, &local_status); 572 if (err != UTF8_CONVERT_OK) { 573 /* badenc should never happen */ 574 assert(err != UTF8_CONVERT_BADENC); 575 /* hide nomem error */ 576 snprintf(dw->status, sizeof dw->status, 577 messages_get("DownloadU"), 578 received, speed, time); 579 } 580 else { 581 snprintf(dw->status, sizeof dw->status, 582 local_status, 583 received, speed, time); 584 free(local_status); 585 } 586 587 /* length unknown, stay at 0 til finished */ 588 width = 0; 589 } 590 } else { 591 left = dw->last_time.tv_sec - dw->start_time.tv_sec; 592 if (left == 0) 593 left = 1; 594 rate = (float) dw->received / (float) left; 595 sprintf(time, "%u:%.2u", left / 60, left % 60); 596 speed = human_friendly_bytesize(rate); 597 598 err = utf8_to_local_encoding(messages_get("Downloaded"), 0, 599 &local_status); 600 if (err != UTF8_CONVERT_OK) { 601 /* badenc should never happen */ 602 assert(err != UTF8_CONVERT_BADENC); 603 /* hide nomem error */ 604 snprintf(dw->status, sizeof dw->status, 605 messages_get("Downloaded"), 606 total_size, speed, time); 607 } 608 else { 609 snprintf(dw->status, sizeof dw->status, local_status, 610 total_size, speed, time); 611 free(local_status); 612 } 613 614 /* all done */ 615 width = download_progress_width; 616 } 617 618 dw->last_time = t; 619 dw->last_received = dw->received; 620 621 error = xwimp_resize_icon(dw->window, ICON_DOWNLOAD_PROGRESS, 622 download_progress_x0, 623 download_progress_y0, 624 download_progress_x0 + width, 625 download_progress_y1); 626 if (error) { 627 LOG(("xwimp_resize_icon: 0x%x: %s", 628 error->errnum, error->errmess)); 629 warn_user("WimpError", error->errmess); 630 } 631 632 error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_STATUS, 0, 0); 633 if (error) { 634 LOG(("xwimp_set_icon_state: 0x%x: %s", 635 error->errnum, error->errmess)); 636 warn_user("WimpError", error->errmess); 637 } 638 639 if (dw->ctx) 640 schedule(100, ro_gui_download_update_status_wrapper, dw); 641 else 642 schedule_remove(ro_gui_download_update_status_wrapper, dw); 643} 644 645 646/** 647 * Wrapper for ro_gui_download_update_status(), suitable for schedule(). 648 */ 649 650void ro_gui_download_update_status_wrapper(void *p) 651{ 652 ro_gui_download_update_status((struct gui_download_window *) p); 653} 654 655 656 657/** 658 * Hide the caret but preserve input focus. 659 * 660 * \param dw download window 661 */ 662 663void ro_gui_download_window_hide_caret(struct gui_download_window *dw) 664{ 665 wimp_caret caret; 666 os_error *error; 667 668 error = xwimp_get_caret_position(&caret); 669 if (error) { 670 LOG(("xwimp_get_caret_position: 0x%x : %s", 671 error->errnum, error->errmess)); 672 warn_user("WimpError", error->errmess); 673 } 674 else if (caret.w == dw->window) { 675 error = xwimp_set_caret_position(dw->window, (wimp_i)-1, 0, 0, 1 << 25, -1); 676 if (error) { 677 LOG(("xwimp_get_caret_position: 0x%x : %s", 678 error->errnum, error->errmess)); 679 warn_user("WimpError", error->errmess); 680 } 681 } 682} 683 684 685/** 686 * Handle failed downloads. 687 * 688 * \param dw download window 689 * \param error_msg error message 690 */ 691 692void gui_download_window_error(struct gui_download_window *dw, 693 const char *error_msg) 694{ 695 os_error *error; 696 697 if (dw->ctx != NULL) 698 download_context_destroy(dw->ctx); 699 dw->ctx = NULL; 700 dw->error = true; 701 702 schedule_remove(ro_gui_download_update_status_wrapper, dw); 703 704 /* place error message in status icon in red */ 705 strncpy(dw->status, error_msg, sizeof dw->status); 706 error = xwimp_set_icon_state(dw->window, 707 ICON_DOWNLOAD_STATUS, 708 wimp_COLOUR_RED << wimp_ICON_FG_COLOUR_SHIFT, 709 wimp_ICON_FG_COLOUR); 710 if (error) { 711 LOG(("xwimp_set_icon_state: 0x%x: %s", 712 error->errnum, error->errmess)); 713 warn_user("WimpError", error->errmess); 714 } 715 716 /* grey out pathname icon */ 717 error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_PATH, 718 wimp_ICON_SHADED, 0); 719 if (error) { 720 LOG(("xwimp_set_icon_state: 0x%x: %s", 721 error->errnum, error->errmess)); 722 warn_user("WimpError", error->errmess); 723 } 724 725 /* grey out file icon */ 726 error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON, 727 wimp_ICON_SHADED, wimp_ICON_SHADED); 728 if (error) { 729 LOG(("xwimp_set_icon_state: 0x%x: %s", 730 error->errnum, error->errmess)); 731 warn_user("WimpError", error->errmess); 732 } 733 734 ro_gui_download_window_hide_caret(dw); 735} 736 737 738/** 739 * Handle completed downloads. 740 * 741 * \param dw download window 742 */ 743 744void gui_download_window_done(struct gui_download_window *dw) 745{ 746 os_error *error; 747 748 if (dw->ctx != NULL) 749 download_context_destroy(dw->ctx); 750 dw->ctx = NULL; 751 ro_gui_download_update_status(dw); 752 753 error = xosfind_closew(dw->file); 754 if (error) { 755 LOG(("xosfind_closew: 0x%x: %s", 756 error->errnum, error->errmess)); 757 warn_user("SaveError", error->errmess); 758 } 759 dw->file = 0; 760 761 if (dw->saved) { 762 error = xosfile_set_type(dw->path, 763 dw->file_type); 764 if (error) { 765 LOG(("xosfile_set_type: 0x%x: %s", 766 error->errnum, error->errmess)); 767 warn_user("SaveError", error->errmess); 768 } 769 770 if (dw->send_dataload) 771 ro_gui_download_send_dataload(dw); 772 773 schedule(200, ro_gui_download_window_destroy_wrapper, dw); 774 } 775} 776 777 778/** 779 * Handle Mouse_Click events in a download window. 780 * 781 * \param dw download window 782 * \param pointer block returned by Wimp_Poll 783 */ 784 785bool ro_gui_download_click(wimp_pointer *pointer) 786{ 787 struct gui_download_window *dw; 788 char command[256] = "Filer_OpenDir "; 789 char *dot; 790 os_error *error; 791 792 dw = (struct gui_download_window *)ro_gui_wimp_event_get_user_data(pointer->w); 793 if (pointer->i == ICON_DOWNLOAD_ICON && !dw->error && 794 !dw->saved) { 795 const char *sprite = ro_gui_get_icon_string(pointer->w, pointer->i); 796 int x = pointer->pos.x, y = pointer->pos.y; 797 wimp_window_state wstate; 798 wimp_icon_state istate; 799 /* start the drag from the icon's exact location, rather than the pointer */ 800 istate.w = wstate.w = pointer->w; 801 istate.i = pointer->i; 802 if (!xwimp_get_window_state(&wstate) && !xwimp_get_icon_state(&istate)) { 803 x = (istate.icon.extent.x1 + istate.icon.extent.x0)/2 + 804 wstate.visible.x0 - wstate.xscroll; 805 y = (istate.icon.extent.y1 + istate.icon.extent.y0)/2 + 806 wstate.visible.y1 - wstate.yscroll; 807 } 808 gui_current_drag_type = GUI_DRAG_DOWNLOAD_SAVE; 809 download_window_current = dw; 810 ro_gui_drag_icon(x, y, sprite); 811 812 } else if (pointer->i == ICON_DOWNLOAD_DESTINATION) { 813 strncpy(command + 14, dw->path, 242); 814 command[255] = 0; 815 dot = strrchr(command, '.'); 816 if (dot) { 817 *dot = 0; 818 error = xos_cli(command); 819 if (error) { 820 LOG(("xos_cli: 0x%x: %s", 821 error->errnum, error->errmess)); 822 warn_user("MiscError", error->errmess); 823 } 824 } 825 } 826 return true; 827} 828 829 830/** 831 * Handler Key_Press events in a download window. 832 * 833 * \param dw download window 834 * \param key key press returned by Wimp_Poll 835 * \return true iff key press handled 836 */ 837 838bool ro_gui_download_keypress(wimp_key *key) 839{ 840 struct gui_download_window *dw; 841 842 dw = (struct gui_download_window *)ro_gui_wimp_event_get_user_data(key->w); 843 switch (key->c) 844 { 845 case wimp_KEY_ESCAPE: 846 ro_gui_download_window_destroy(dw, false); 847 return true; 848 849 case wimp_KEY_RETURN: { 850 const char *name = ro_gui_get_icon_string(dw->window, 851 ICON_DOWNLOAD_PATH); 852 if (!strrchr(name, '.')) { 853 warn_user("NoPathError", NULL); 854 return true; 855 } 856 ro_gui_convert_save_path(dw->path, sizeof dw->path, name); 857 858 dw->send_dataload = false; 859 if (ro_gui_download_save(dw, dw->path, 860 !option_confirm_overwrite) && !dw->ctx) 861 { 862 /* finished already */ 863 schedule(200, ro_gui_download_window_destroy_wrapper, dw); 864 } 865 return true; 866 } 867 break; 868 } 869 870 /* ignore all other keypresses (F12 etc) */ 871 return false; 872} 873 874 875/** 876 * Handle User_Drag_Box event for a drag from a download window. 877 * 878 * \param drag block returned by Wimp_Poll 879 */ 880 881void ro_gui_download_drag_end(wimp_dragged *drag) 882{ 883 wimp_pointer pointer; 884 wimp_message message; 885 struct gui_download_window *dw = download_window_current; 886 const char *leaf; 887 os_error *error; 888 889 if (dw->saved || dw->error) 890 return; 891 892 error = xwimp_get_pointer_info(&pointer); 893 if (error) { 894 LOG(("xwimp_get_pointer_info: 0x%x: %s", 895 error->errnum, error->errmess)); 896 warn_user("WimpError", error->errmess); 897 return; 898 } 899 900 /* ignore drags to the download window itself */ 901 if (pointer.w == dw->window) return; 902 903 leaf = strrchr(dw->path, '.'); 904 if (leaf) 905 leaf++; 906 else 907 leaf = dw->path; 908 ro_gui_convert_save_path(message.data.data_xfer.file_name, 212, leaf); 909 910 message.your_ref = 0; 911 message.action = message_DATA_SAVE; 912 message.data.data_xfer.w = pointer.w; 913 message.data.data_xfer.i = pointer.i; 914 message.data.data_xfer.pos.x = pointer.pos.x; 915 message.data.data_xfer.pos.y = pointer.pos.y; 916 message.data.data_xfer.est_size = dw->total_size ? dw->total_size : 917 dw->received; 918 message.data.data_xfer.file_type = dw->file_type; 919 message.size = 44 + ((strlen(message.data.data_xfer.file_name) + 4) & 920 (~3u)); 921 922 error = xwimp_send_message_to_window(wimp_USER_MESSAGE, &message, 923 pointer.w, pointer.i, 0); 924 if (error) { 925 LOG(("xwimp_send_message_to_window: 0x%x: %s", 926 error->errnum, error->errmess)); 927 warn_user("WimpError", error->errmess); 928 } 929} 930 931 932/** 933 * Handle Message_DataSaveAck for a drag from a download window. 934 * 935 * \param message block returned by Wimp_Poll 936 */ 937 938void ro_gui_download_datasave_ack(wimp_message *message) 939{ 940 struct gui_download_window *dw = download_window_current; 941 942 dw->send_dataload = true; 943 memcpy(&dw->save_message, message, sizeof(wimp_message)); 944 945 if (!ro_gui_download_save(dw, message->data.data_xfer.file_name, 946 !option_confirm_overwrite)) 947 return; 948 949 if (!dw->ctx) { 950 /* Ack successful completed save with message_DATA_LOAD immediately 951 to reduce the chance of the target app getting confused by it 952 being delayed */ 953 954 ro_gui_download_send_dataload(dw); 955 956 schedule(200, ro_gui_download_window_destroy_wrapper, dw); 957 } 958} 959 960 961/** 962 * Return a pathname in canonical form 963 * 964 * \param path pathnamee to be canonicalised 965 * \return ptr to pathname in malloc block, or NULL 966 */ 967 968char *ro_gui_download_canonicalise(const char *path) 969{ 970 os_error *error; 971 int spare = 0; 972 char *buf; 973 974 error = xosfscontrol_canonicalise_path(path, NULL, NULL, NULL, 0, &spare); 975 if (error) { 976 LOG(("xosfscontrol_canonicalise_path: 0x%x: %s", 977 error->errnum, error->errmess)); 978 return NULL; 979 } 980 981 buf = malloc(1 - spare); 982 if (buf) { 983 error = xosfscontrol_canonicalise_path(path, buf, NULL, NULL, 984 1 - spare, NULL); 985 if (error) { 986 LOG(("xosfscontrol_canonicalise_path: 0x%x: %s", 987 error->errnum, error->errmess)); 988 989 free(buf); 990 return NULL; 991 } 992 } 993 994 return buf; 995} 996 997 998/** 999 * Check the available space on the medium containing the destination file, 1000 * taking into account any space currently occupied by the file at its 1001 * original location. 1002 * 1003 * \param dw download window 1004 * \param dest_file destination pathname 1005 * \param orig_file current pathname, NULL if no existing file 1006 * \return true iff there's enough space 1007 */ 1008 1009bool ro_gui_download_check_space(struct gui_download_window *dw, 1010 const char *dest_file, const char *orig_file) 1011{ 1012 /* is there enough free space for this file? */ 1013 int dest_len = strlen(dest_file); 1014 os_error *error; 1015 int max_file; 1016 bits free_lo; 1017 int free_hi; 1018 char *dir; 1019 1020 dir = malloc(dest_len + 1); 1021 if (!dir) return true; 1022 1023 while (dest_len > 0 && dest_file[--dest_len] != '.'); 1024 1025 memcpy(dir, dest_file, dest_len); 1026 dir[dest_len] = '\0'; 1027 1028 /* try the 64-bit variant first (RO 3.6+) */ 1029 error = xosfscontrol_free_space64(dir, &free_lo, &free_hi, 1030 &max_file, NULL, NULL); 1031 if (error) { 1032 LOG(("xosfscontrol_free_space64: 0x%x: %s", 1033 error->errnum, error->errmess)); 1034 1035 free_hi = 0; 1036 error = xosfscontrol_free_space(dir, (int*)&free_lo, 1037 &max_file, NULL); 1038 if (error) { 1039 LOG(("xosfscontrol_free_space: 0x%x: %s", 1040 error->errnum, error->errmess)); 1041 /* close our eyes and hope */ 1042 free(dir); 1043 return true; 1044 } 1045 } 1046 1047 free(dir); 1048 1049 if ((bits)max_file < dw->total_size || (!free_hi && free_lo < dw->total_size)) { 1050 char *dest_canon, *orig_canon; 1051 bits space; 1052 1053 if (!orig_file || !dw->file) { 1054 /* no original file to take into account */ 1055 return false; 1056 } 1057 1058 space = min((bits)max_file, free_lo); 1059 1060 dest_canon = ro_gui_download_canonicalise(dest_file); 1061 if (!dest_canon) dest_canon = (char*)dest_file; 1062 1063 orig_canon = ro_gui_download_canonicalise(orig_file); 1064 if (!orig_canon) orig_canon = (char*)orig_file; 1065 1066 /* not enough space; allow for the file's original location 1067 when space is tight by comparing the first part of the two 1068 pathnames (and assuming the FS isn't brain damaged!) */ 1069 1070 char *dot = strchr(orig_canon, '.'); 1071 if (dot && !strncasecmp(dest_canon, orig_canon, (dot + 1) - orig_canon)) { 1072 int allocation; 1073 1074 error = xosargs_read_allocation(dw->file, 1075 &allocation); 1076 if (error) { 1077 LOG(("xosargs_read_allocation: 0x%x : %s", 1078 error->errnum, error->errmess)); 1079 } 1080 else { 1081 space += allocation; 1082 } 1083 } 1084 1085 if (dest_canon != dest_file) free(dest_canon); 1086 if (orig_canon != orig_file) free(orig_canon); 1087 1088 if (space >= dw->total_size) { 1089 /* OK, renaming should work */ 1090 return true; 1091 } 1092 1093 return false; 1094 } 1095 return true; 1096} 1097 1098/** 1099 * Move the downloading file to a new location and continue downloading there. 1100 * 1101 * \param dw download window 1102 * \param dest_file new location 1103 * \param src_file old location 1104 * \return error iff failed to move file 1105 */ 1106 1107os_error *ro_gui_download_move(struct gui_download_window *dw, 1108 const char *dest_file, const char *src_file) 1109{ 1110 os_error *error; 1111 1112 /* close temporary file */ 1113 if (dw->file) { 1114 error = xosfind_closew(dw->file); 1115 dw->file = 0; 1116 if (error) { 1117 LOG(("xosfind_closew: 0x%x: %s", 1118 error->errnum, error->errmess)); 1119 return error; 1120 } 1121 } 1122 1123 /* move or copy temporary file to destination file */ 1124 error = xosfscontrol_rename(src_file, dest_file); 1125 /* Errors from a filing system have number 0x1XXnn, where XX is the FS 1126 * number, and nn the error number. 0x9F is "Not same disc". */ 1127 if (error && (error->errnum == error_BAD_RENAME || 1128 (error->errnum & 0xFF00FFu) == 0x1009Fu)) { 1129 /* rename failed: copy with delete */ 1130 error = xosfscontrol_copy(src_file, dest_file, 1131 osfscontrol_COPY_FORCE | 1132 osfscontrol_COPY_DELETE | 1133 osfscontrol_COPY_LOOK, 1134 0, 0, 0, 0, 0); 1135 if (error) { 1136 LOG(("xosfscontrol_copy: 0x%x: %s", 1137 error->errnum, error->errmess)); 1138 return error; 1139 } 1140 } else if (error) { 1141 LOG(("xosfscontrol_rename: 0x%x: %s", 1142 error->errnum, error->errmess)); 1143 return error; 1144 } 1145 1146 if (dw->ctx) { 1147 /* open new destination file if still fetching */ 1148 error = xosfile_write(dest_file, 0xdeaddead, 0xdeaddead, 1149 fileswitch_ATTR_OWNER_READ | 1150 fileswitch_ATTR_OWNER_WRITE); 1151 if (error) { 1152 LOG(("xosfile_write: 0x%x: %s", 1153 error->errnum, error->errmess)); 1154 warn_user("SaveError", error->errmess); 1155 } 1156 1157 error = xosfind_openupw(osfind_NO_PATH | osfind_ERROR_IF_DIR, 1158 dest_file, 0, &dw->file); 1159 if (error) { 1160 LOG(("xosfind_openupw: 0x%x: %s", 1161 error->errnum, error->errmess)); 1162 return error; 1163 } 1164 1165 error = xosargs_set_ptrw(dw->file, dw->received); 1166 if (error) { 1167 LOG(("xosargs_set_ptrw: 0x%x: %s", 1168 error->errnum, error->errmess)); 1169 return error; 1170 } 1171 1172 } else { 1173 /* otherwise just set the file type */ 1174 error = xosfile_set_type(dest_file, 1175 dw->file_type); 1176 if (error) { 1177 LOG(("xosfile_set_type: 0x%x: %s", 1178 error->errnum, error->errmess)); 1179 warn_user("SaveError", error->errmess); 1180 } 1181 } 1182 1183 /* success */ 1184 return NULL; 1185} 1186 1187 1188/** 1189 * Remember the directory containing the given file, 1190 * for use in further downloads. 1191 * 1192 * \param path pathname of downloaded file 1193 * \return none 1194 */ 1195 1196void ro_gui_download_remember_dir(const char *path) 1197{ 1198 const char *lastdot = NULL; 1199 const char *p = path; 1200 1201 while (*p >= 0x20) { 1202 if (*p == '.') { 1203 /* don't remember the directory if it's a temporary file */ 1204 if (!lastdot && p == path + 12 && 1205 !memcmp(path, "<Wimp$Scrap>", 12)) break; 1206 lastdot = p; 1207 } 1208 p++; 1209 } 1210 1211 if (lastdot) { 1212 /* remember the directory */ 1213 char *new_dir = realloc(download_dir, (lastdot+1)-path); 1214 if (new_dir) { 1215 download_dir_len = lastdot - path; 1216 memcpy(new_dir, path, download_dir_len); 1217 new_dir[download_dir_len] = '\0'; 1218 download_dir = new_dir; 1219 } 1220 } 1221} 1222 1223/** 1224 * Start of save operation, user has specified where the file should be saved. 1225 * 1226 * \param dw download window 1227 * \param file_name pathname of destination file 1228 & \param force_overwrite true iff required to overwrite without prompting 1229 * \return true iff save successfully initiated 1230 */ 1231 1232bool ro_gui_download_save(struct gui_download_window *dw, 1233 const char *file_name, bool force_overwrite) 1234{ 1235 fileswitch_object_type obj_type; 1236 const char *temp_name; 1237 os_error *error; 1238 1239 if (dw->saved || dw->error) 1240 return true; 1241 1242 temp_name = ro_gui_download_temp_name(dw); 1243 1244 /* does the user want to check for collisions when saving? */ 1245 if (!force_overwrite) { 1246 /* check whether the destination file/dir already exists */ 1247 error = xosfile_read_stamped(file_name, &obj_type, 1248 NULL, NULL, NULL, NULL, NULL); 1249 if (error) { 1250 LOG(("xosfile_read_stamped: 0x%x:%s", error->errnum, error->errmess)); 1251 return false; 1252 } 1253 1254 switch (obj_type) { 1255 case osfile_NOT_FOUND: 1256 break; 1257 1258 case osfile_IS_FILE: 1259 dw->query = query_user("OverwriteFile", NULL, &overwrite_funcs, dw, 1260 messages_get("Replace"), messages_get("DontReplace")); 1261 dw->query_rsn = QueryRsn_Overwrite; 1262 return false; 1263 1264 default: 1265 error = xosfile_make_error(file_name, obj_type); 1266 assert(error); 1267 warn_user("SaveError", error->errmess); 1268 return false; 1269 } 1270 } 1271 1272 if (!ro_gui_download_check_space(dw, file_name, temp_name)) { 1273 warn_user("SaveError", messages_get("NoDiscSpace")); 1274 return false; 1275 } 1276 1277 error = ro_gui_download_move(dw, file_name, temp_name); 1278 if (error) { 1279 warn_user("SaveError", error->errmess); 1280 1281 /* try to reopen at old location so that the download can continue 1282 to the temporary file */ 1283 error = xosfind_openupw(osfind_NO_PATH | osfind_ERROR_IF_DIR, 1284 temp_name, 0, &dw->file); 1285 if (error) { 1286 LOG(("xosfind_openupw: 0x%x: %s", 1287 error->errnum, error->errmess)); 1288 1289 } else { 1290 error = xosargs_set_ptrw(dw->file, dw->received); 1291 if (error) { 1292 LOG(("xosargs_set_ptrw: 0x%x: %s", 1293 error->errnum, error->errmess)); 1294 } 1295 } 1296 1297 if (error) { 1298 if (dw->ctx) 1299 download_context_abort(dw->ctx); 1300 gui_download_window_error(dw, error->errmess); 1301 } 1302 return false; 1303 } 1304 1305 dw->saved = true; 1306 strncpy(dw->path, file_name, sizeof dw->path); 1307 1308 if (!dw->send_dataload || dw->save_message.data.data_xfer.est_size != -1) 1309 ro_gui_download_remember_dir(file_name); 1310 1311 /* grey out file icon */ 1312 error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON, 1313 wimp_ICON_SHADED, wimp_ICON_SHADED); 1314 if (error) { 1315 LOG(("xwimp_set_icon_state: 0x%x: %s", 1316 error->errnum, error->errmess)); 1317 warn_user("WimpError", error->errmess); 1318 } 1319 1320 /* hide writeable path icon and show destination icon 1321 Note: must redraw icon bounding box because the destination icon 1322 has rounded edges on RISC OS Select/Adjust and doesn't 1323 completely cover the writeable icon */ 1324 1325 ro_gui_force_redraw_icon(dw->window, ICON_DOWNLOAD_PATH); 1326 error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_PATH, 1327 wimp_ICON_DELETED, wimp_ICON_DELETED); 1328 if (error) { 1329 LOG(("xwimp_set_icon_state: 0x%x: %s", 1330 error->errnum, error->errmess)); 1331 warn_user("WimpError", error->errmess); 1332 } 1333 error = xwimp_set_icon_state(dw->window, 1334 ICON_DOWNLOAD_DESTINATION, wimp_ICON_DELETED, 0); 1335 if (error) { 1336 LOG(("xwimp_set_icon_state: 0x%x: %s", 1337 error->errnum, error->errmess)); 1338 warn_user("WimpError", error->errmess); 1339 } 1340 1341 ro_gui_download_window_hide_caret(dw); 1342 1343 return true; 1344} 1345 1346 1347/** 1348 * Send DataLoad message in response to DataSaveAck, informing the 1349 * target application that the transfer is complete. 1350 * 1351 * \param dw download window 1352 */ 1353 1354void ro_gui_download_send_dataload(struct gui_download_window *dw) 1355{ 1356 /* Ack successful save with message_DATA_LOAD */ 1357 wimp_message *message = &dw->save_message; 1358 os_error *error; 1359 1360 assert(dw->send_dataload); 1361 dw->send_dataload = false; 1362 1363 message->action = message_DATA_LOAD; 1364 message->your_ref = message->my_ref; 1365 error = xwimp_send_message_to_window(wimp_USER_MESSAGE, message, 1366 message->data.data_xfer.w, 1367 message->data.data_xfer.i, 0); 1368 /* The window we just attempted to send a message to may 1369 * have been closed before the message was sent. As we've 1370 * no clean way of detecting this, we'll just detect the 1371 * error return from the message send attempt and judiciously 1372 * ignore it. 1373 * 1374 * Ideally, we would have registered to receive Message_WindowClosed 1375 * and then cleared dw->send_dataload flag for the appropriate 1376 * window. Unfortunately, however, a long-standing bug in the 1377 * Pinboard module prevents this from being a viable solution. 1378 * 1379 * See http://groups.google.co.uk/group/comp.sys.acorn.tech/msg/e3fbf70d8393e6cf?dmode=source&hl=en 1380 * for the rather depressing details. 1381 */ 1382 if (error && error->errnum != error_WIMP_BAD_HANDLE) { 1383 LOG(("xwimp_set_icon_state: 0x%x: %s", 1384 error->errnum, error->errmess)); 1385 warn_user("WimpError", error->errmess); 1386 } 1387 1388 schedule(200, ro_gui_download_window_destroy_wrapper, dw); 1389} 1390 1391 1392/** 1393 * Handle closing of download window 1394 */ 1395void ro_gui_download_close(wimp_w w) 1396{ 1397 struct gui_download_window *dw; 1398 1399 dw = (struct gui_download_window *)ro_gui_wimp_event_get_user_data(w); 1400 ro_gui_download_window_destroy(dw, false); 1401} 1402 1403 1404/** 1405 * Close a download window and free any related resources. 1406 * 1407 * \param dw download window 1408 * \param quit destroying because we're quitting the whole app 1409 * \return true if window destroyed, not waiting for user confirmation 1410 */ 1411 1412bool ro_gui_download_window_destroy(struct gui_download_window *dw, bool quit) 1413{ 1414 bool safe = dw->saved && !dw->ctx; 1415 os_error *error; 1416 1417 if (!safe && !dw->close_confirmed) 1418 { 1419 query_reason rsn = quit ? QueryRsn_Quit : QueryRsn_Abort; 1420 1421 if (dw->query != QUERY_INVALID) { 1422 1423 /* can we just reuse the existing query? */ 1424 if (rsn == dw->query_rsn) { 1425 ro_gui_query_window_bring_to_front(dw->query); 1426 return false; 1427 } 1428 1429 query_close(dw->query); 1430 dw->query = QUERY_INVALID; 1431 } 1432 1433 if (quit) { 1434 /* bring all download windows to the front of the desktop as 1435 a convenience if there are lots of windows open */ 1436 1437 struct gui_download_window *d = download_window_list; 1438 while (d) { 1439 ro_gui_dialog_open_top(d->window, NULL, 0, 0); 1440 d = d->next; 1441 } 1442 } 1443 1444 dw->query_rsn = rsn; 1445 dw->query = query_user(quit ? "QuitDownload" : "AbortDownload", 1446 NULL, &close_funcs, dw, NULL, NULL); 1447 1448 return false; 1449 } 1450 1451 schedule_remove(ro_gui_download_update_status_wrapper, dw); 1452 schedule_remove(ro_gui_download_window_destroy_wrapper, dw); 1453 1454 /* remove from list */ 1455 if (dw->prev) 1456 dw->prev->next = dw->next; 1457 else 1458 download_window_list = dw->next; 1459 if (dw->next) 1460 dw->next->prev = dw->prev; 1461 1462 /* delete window */ 1463 error = xwimp_delete_window(dw->window); 1464 if (error) { 1465 LOG(("xwimp_delete_window: 0x%x: %s", 1466 error->errnum, error->errmess)); 1467 warn_user("WimpError", error->errmess); 1468 } 1469 ro_gui_wimp_event_finalise(dw->window); 1470 1471 /* close download file */ 1472 if (dw->file) { 1473 error = xosfind_closew(dw->file); 1474 if (error) { 1475 LOG(("xosfind_closew: 0x%x: %s", 1476 error->errnum, error->errmess)); 1477 warn_user("SaveError", error->errmess); 1478 } 1479 } 1480 1481 /* delete temporary file */ 1482 if (!dw->saved) { 1483 const char *temp_name = ro_gui_download_temp_name(dw); 1484 1485 error = xosfile_delete(temp_name, 0, 0, 0, 0, 0); 1486 if (error) { 1487 LOG(("xosfile_delete: 0x%x: %s", 1488 error->errnum, error->errmess)); 1489 warn_user("SaveError", error->errmess); 1490 } 1491 } 1492 1493 if (dw->ctx) { 1494 download_context_abort(dw->ctx); 1495 download_context_destroy(dw->ctx); 1496 } 1497 1498 free(dw); 1499 1500 return true; 1501} 1502 1503 1504/** 1505 * Wrapper for ro_gui_download_window_destroy(), suitable for schedule(). 1506 */ 1507 1508void ro_gui_download_window_destroy_wrapper(void *p) 1509{ 1510 struct gui_download_window *dw = p; 1511 if (dw->query != QUERY_INVALID) 1512 query_close(dw->query); 1513 dw->query = QUERY_INVALID; 1514 dw->close_confirmed = true; 1515 ro_gui_download_window_destroy(dw, false); 1516} 1517 1518 1519/** 1520 * User has opted to cancel the close, leaving the download to continue. 1521 */ 1522 1523void ro_gui_download_close_cancelled(query_id id, enum query_response res, void *p) 1524{ 1525 struct gui_download_window *dw = p; 1526 dw->query = QUERY_INVALID; 1527} 1528 1529 1530/** 1531 * Download aborted, close window and tidy up. 1532 */ 1533 1534void ro_gui_download_close_confirmed(query_id id, enum query_response res, void *p) 1535{ 1536 struct gui_download_window *dw = p; 1537 dw->query = QUERY_INVALID; 1538 dw->close_confirmed = true; 1539 if (dw->query_rsn == QueryRsn_Quit) { 1540 1541 /* destroy all our downloads */ 1542 while (download_window_list) 1543 ro_gui_download_window_destroy_wrapper(download_window_list); 1544 1545 /* and restart the shutdown */ 1546 if (ro_gui_prequit()) 1547 netsurf_quit = true; 1548 } 1549 else 1550 ro_gui_download_window_destroy(dw, false); 1551} 1552 1553 1554/** 1555 * User has opted not to overwrite the existing file. 1556 */ 1557 1558void ro_gui_download_overwrite_cancelled(query_id id, enum query_response res, void *p) 1559{ 1560 struct gui_download_window *dw = p; 1561 dw->query = QUERY_INVALID; 1562} 1563 1564 1565/** 1566 * Overwrite of existing file confirmed, proceed with the save. 1567 */ 1568 1569void ro_gui_download_overwrite_confirmed(query_id id, enum query_response res, void *p) 1570{ 1571 struct gui_download_window *dw = p; 1572 dw->query = QUERY_INVALID; 1573 1574 if (!ro_gui_download_save(dw, dw->save_message.data.data_xfer.file_name, true)) 1575 return; 1576 1577 if (!dw->ctx) { 1578 /* Ack successful completed save with message_DATA_LOAD immediately 1579 to reduce the chance of the target app getting confused by it 1580 being delayed */ 1581 1582 ro_gui_download_send_dataload(dw); 1583 1584 schedule(200, ro_gui_download_window_destroy_wrapper, dw); 1585 } 1586} 1587 1588 1589/** 1590 * Respond to PreQuit message, displaying a prompt message if we need 1591 * the user to confirm the shutdown. 1592 * 1593 * \return true if we can shutdown straightaway 1594 */ 1595 1596bool ro_gui_download_prequit(void) 1597{ 1598 while (download_window_list) 1599 { 1600 if (!ro_gui_download_window_destroy(download_window_list, true)) 1601 return false; /* awaiting user confirmation */ 1602 } 1603 return true; 1604}