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