NetSurf
download.c
Go to the documentation of this file.
1/*
2 * Copyright 2008 Michael Lester <element3260@gmail.com>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <stdlib.h>
20#include <string.h>
21#include <math.h>
22
23#include <gtk/gtk.h>
24#include <glib/gstdio.h>
25
26#include "utils/log.h"
27#include "utils/utils.h"
28#include "utils/nsurl.h"
29#include "utils/messages.h"
30#include "utils/nsoption.h"
31#include "utils/string.h"
32#include "desktop/download.h"
33#include "netsurf/download.h"
34
35#include "gtk/warn.h"
36#include "gtk/scaffolding.h"
37#include "gtk/toolbar_items.h"
38#include "gtk/window.h"
39#include "gtk/compat.h"
40#include "gtk/resources.h"
41#include "gtk/download.h"
42
43#define UPDATE_RATE 500 /* In milliseconds */
44
45struct download_context;
46
47enum {
55
57};
58
59typedef enum {
66
67typedef enum {
73
74static const gchar* status_messages[] = {
75 NULL,
76 "gtkWorking",
77 "gtkError",
78 "gtkComplete",
79 "gtkCanceled"
80};
81
82/**
83 * context for each download.
84 */
86 struct download_context *ctx;
89
90 GString *name;
91 GString *time_left;
92 unsigned long long int size_total;
93 unsigned long long int size_downloaded;
96 gfloat start_time;
97 gfloat speed;
98
99 GtkTreeRowReference *row;
100 GIOChannel *write;
101 GError *error;
102};
103
105 void *user_data);
106
107/**
108 * context for a nsgtk download window.
109 */
111 GtkWindow *window;
112 GtkWindow *parent;
113
114 GtkProgressBar *progress;
115
116 GtkTreeView *tree;
117 GtkListStore *store;
118 GtkTreeSelection *selection;
119 GtkTreeIter iter;
120
121 GTimer *timer;
122 GList *list;
123 GtkButton *pause;
124 GtkButton *clear;
125 GtkButton *cancel;
126 GtkButton *resume;
127
129};
130
131/**
132 * global instance of the download window
133 */
135
136
137static GtkTreeView* nsgtk_download_tree_view_new(GtkBuilder *gladeFile)
138{
139 GtkTreeView *treeview;
140 GtkCellRenderer *renderer;
141
142 treeview = GTK_TREE_VIEW(gtk_builder_get_object(gladeFile,
143 "treeDownloads"));
144
145 /* Progress column */
146 renderer = gtk_cell_renderer_progress_new();
147 gtk_tree_view_insert_column_with_attributes(treeview,
148 -1,
149 messages_get("gtkProgress"),
150 renderer,
151 "value",
153 "pulse",
155 "text",
157 NULL);
158
159 /* Information column */
160 renderer = gtk_cell_renderer_text_new();
161 g_object_set(G_OBJECT(renderer),
162 "wrap-mode",
163 PANGO_WRAP_WORD_CHAR,
164 "wrap-width",
165 300,
166 NULL);
167 gtk_tree_view_insert_column_with_attributes(treeview,
168 -1,
169 messages_get("gtkDetails"),
170 renderer,
171 "text",
173 NULL);
174 gtk_tree_view_column_set_expand(
175 gtk_tree_view_get_column(treeview,
176 NSGTK_DOWNLOAD_INFO), TRUE);
177
178 /* Time remaining column */
179 renderer = gtk_cell_renderer_text_new();
180 gtk_tree_view_insert_column_with_attributes(treeview,
181 -1,
182 messages_get("gtkRemaining"),
183 renderer,
184 "text",
186 NULL);
187
188 /* Speed column */
189 renderer = gtk_cell_renderer_text_new();
190 gtk_tree_view_insert_column_with_attributes(treeview,
191 -1,
192 messages_get("gtkSpeed"),
193 renderer,
194 "text",
196 NULL);
197
198 return treeview;
199}
200
201
202static gint
203nsgtk_download_sort(GtkTreeModel *model,
204 GtkTreeIter *a,
205 GtkTreeIter *b,
206 gpointer userdata)
207{
208 struct gui_download_window *dl1, *dl2;
209
210 gtk_tree_model_get(model, a, NSGTK_DOWNLOAD, &dl1, -1);
211 gtk_tree_model_get(model, b, NSGTK_DOWNLOAD, &dl2, -1);
212
213 return dl1->status - dl2->status;
214}
215
216
217static void
219{
220 /* Glade seems to pack the buttons in an arbitrary order */
221 enum { PAUSE_BUTTON, CLEAR_BUTTON, CANCEL_BUTTON, RESUME_BUTTON };
222
223 gtk_widget_set_sensitive(GTK_WIDGET(dl_ctx.pause),
225 gtk_widget_set_sensitive(GTK_WIDGET(dl_ctx.clear),
227 gtk_widget_set_sensitive(GTK_WIDGET(dl_ctx.cancel),
229 gtk_widget_set_sensitive(GTK_WIDGET(dl_ctx.resume),
231}
232
233
234static void nsgtk_download_sensitivity_evaluate(GtkTreeSelection *selection)
235{
236 GtkTreeIter iter;
237 GList *rows;
238 gboolean selected;
239 GtkTreeModel *model;
241 struct gui_download_window *dl;
242
243 model = GTK_TREE_MODEL(dl_ctx.store);
244
245 selected = gtk_tree_selection_count_selected_rows(selection);
246 if (selected) {
247 rows = gtk_tree_selection_get_selected_rows(selection, &model);
248 while (rows != NULL) {
249 gtk_tree_model_get_iter(model,
250 &iter,
251 (GtkTreePath*)rows->data);
252 gtk_tree_model_get(model,
253 &iter,
255 &dl,
256 -1);
258 rows = rows->next;
259 }
260 } else {
261 rows = dl_ctx.list;
262 while (rows != NULL) {
263 dl = rows->data;
265 rows = rows->next;
266 }
267 }
268
270}
271
272
273/**
274 * Wrapper to GFunc-ify gtk_tree_path_free for g_list_foreach.
275 */
276static void
277nsgtk_download_gfunc__gtk_tree_path_free(gpointer data, gpointer user_data)
278{
279 gtk_tree_path_free(data);
280}
281
282
283/**
284 * Wrapper to GFunc-ify g_free for g_list_foreach.
285 */
286static void
287nsgtk_download_gfunc__g_free(gpointer data, gpointer user_data)
288{
289 g_free(data);
290}
291
292
294{
295 GList *rows, *dls = NULL;
296 GtkTreeModel *model;
297
298 if (gtk_tree_selection_count_selected_rows(dl_ctx.selection)) {
299 model = GTK_TREE_MODEL(dl_ctx.store);
300
301 rows = gtk_tree_selection_get_selected_rows(dl_ctx.selection,
302 &model);
303 while (rows != NULL) {
304 struct gui_download_window *dl;
305
306 gtk_tree_model_get_iter(GTK_TREE_MODEL(dl_ctx.store),
307 &dl_ctx.iter,
308 (GtkTreePath*)rows->data);
309
310 gtk_tree_model_get(GTK_TREE_MODEL(dl_ctx.store),
311 &dl_ctx.iter,
313 &dl,
314 -1);
315
316 dls = g_list_prepend(dls, dl);
317
318 rows = rows->next;
319 }
320 g_list_foreach(rows,
322 NULL);
323 g_list_foreach(rows,
325 NULL);
326 g_list_free(rows);
327 } else {
328 dls = g_list_copy(dl_ctx.list);
329 }
330
331 g_list_foreach(dls, (GFunc)action, NULL);
332 g_list_free(dls);
333}
334
335
337{
338 gchar *size_info;
339 gchar *r;
340
341 size_info = g_strdup_printf(messages_get("gtkSizeInfo"),
343 dl->size_total == 0 ?
344 messages_get("gtkUnknownSize") :
346
347 if (dl->status != NSGTK_DOWNLOAD_ERROR) {
348 r = g_strdup_printf("%s\n%s", dl->name->str, size_info);
349 } else {
350 r = g_strdup_printf("%s\n%s", dl->name->str, dl->error->message);
351 }
352
353 g_free(size_info);
354
355 return r;
356}
357
358
359static gchar* nsgtk_download_time_to_string(gint seconds)
360{
361 gint hours, minutes;
362
363 if (seconds < 0) {
364 return g_strdup("-");
365 }
366
367 hours = seconds / 3600;
368 seconds -= hours * 3600;
369 minutes = seconds / 60;
370 seconds -= minutes * 60;
371
372 if (hours > 0) {
373 return g_strdup_printf("%u:%02u:%02u",
374 hours,
375 minutes,
376 seconds);
377 } else {
378 return g_strdup_printf("%u:%02u", minutes, seconds);
379 }
380}
381
382
384{
385 gchar *info = nsgtk_download_info_to_string(dl);
386 char *human = human_friendly_bytesize(dl->speed);
387 char speed[strlen(human) + SLEN("/s") + 1];
388 sprintf(speed, "%s/s", human);
390 gboolean pulse = dl->status == NSGTK_DOWNLOAD_WORKING;
391
392 /* Updates iter (which is needed to set and get data) with the dl row */
393 gtk_tree_model_get_iter(GTK_TREE_MODEL(dl_ctx.store),
394 &dl_ctx.iter,
395 gtk_tree_row_reference_get_path(dl->row));
396
397 gtk_list_store_set(dl_ctx.store, &dl_ctx.iter,
398 NSGTK_DOWNLOAD_PULSE, pulse ? dl->progress : -1,
399 NSGTK_DOWNLOAD_PROGRESS, pulse ? 0 : dl->progress,
401 NSGTK_DOWNLOAD_SPEED, dl->speed == 0 ? "-" : speed,
403 NSGTK_DOWNLOAD, dl,
404 -1);
405
406 g_free(info);
407 g_free(time);
408}
409
410
411static gboolean nsgtk_download_update(gboolean force_update)
412{
413 /* Be sure we need to update */
414 if (!nsgtk_widget_get_visible(GTK_WIDGET(dl_ctx.window))) {
415 return TRUE;
416 }
417
418 GList *list;
419 gchar *text;
420 gboolean update, pulse_mode = FALSE;
421 unsigned long long int downloaded = 0;
422 unsigned long long int total = 0;
423 gint dls = 0;
424 gfloat percent, elapsed = g_timer_elapsed(dl_ctx.timer, NULL);
425
426 dl_ctx.num_active = 0;
427
428 for (list = dl_ctx.list; list != NULL; list = list->next) {
429 struct gui_download_window *dl = list->data;
430 update = force_update;
431
432 switch (dl->status) {
434 pulse_mode = TRUE;
436
438 dl->speed = dl->size_downloaded /
439 (elapsed - dl->start_time);
440 if (dl->status == NSGTK_DOWNLOAD_NONE) {
441 dl->time_remaining = (dl->size_total -
442 dl->size_downloaded)/
443 dl->speed;
444 dl->progress = (double)dl->size_downloaded /
445 (double)dl->size_total * 100;
446 } else {
447 dl->progress++;
448 }
449
451 update = TRUE;
453
456 total += dl->size_total;
457 dls++;
459
460 default:
461 ;//Do nothing
462
463 }
464 if (update) {
466 }
467 }
468
469 if (pulse_mode) {
470 text = g_strdup_printf(
472 "gtkProgressBarPulse" :
473 "gtkProgressBarPulseSingle"),
475 gtk_progress_bar_pulse(dl_ctx.progress);
476 gtk_progress_bar_set_text(dl_ctx.progress, text);
477 } else {
478 percent = total != 0 ? (double)downloaded / (double)total : 0;
479 text = g_strdup_printf(messages_get("gtkProgressBar"),
480 floor(percent * 100), dls);
481 gtk_progress_bar_set_fraction(dl_ctx.progress,
482 percent);
483 gtk_progress_bar_set_text(dl_ctx.progress, text);
484 }
485
486 g_free(text);
487
488 if (dl_ctx.num_active == 0) {
489 return FALSE; /* Returning FALSE here cancels the g_timeout */
490 } else {
491 return TRUE;
492 }
493}
494
495
496static void
498{
500 dl_ctx.list = g_list_remove(dl_ctx.list, dl);
501
502 gtk_tree_model_get_iter(GTK_TREE_MODEL(dl_ctx.store),
503 &dl_ctx.iter,
504 gtk_tree_row_reference_get_path(dl->row));
505 gtk_list_store_remove(dl_ctx.store,
506 &dl_ctx.iter);
507
509 g_string_free(dl->name, TRUE);
510 g_string_free(dl->time_left, TRUE);
511 g_free(dl);
512
515 }
516}
517
518
519static void
521 GtkTreePath *path,
522 GtkTreeViewColumn *column,
523 gpointer data)
524{
525 GtkTreeModel *model;
526 GtkTreeIter iter;
527
528 model = gtk_tree_view_get_model(tree);
529
530 if (gtk_tree_model_get_iter(model, &iter, path)) {
531 /* TODO: This will be a context action (pause, start, clear) */
533 }
534}
535
536
537static void
540{
543}
544
545
546static void
549{
550 dl->status = status;
552 gtk_tree_model_get_iter(GTK_TREE_MODEL(dl_ctx.store),
553 &dl_ctx.iter,
554 gtk_tree_row_reference_get_path(dl->row));
555
556 gtk_list_store_set(dl_ctx.store, &dl_ctx.iter,
559 }
560}
561
562
563static void
565 void *user_data)
566{
568 dl->speed = 0;
569 dl->size_downloaded = 0;
570 dl->progress = 0;
571 dl->time_remaining = -1;
574
576
577 g_unlink(download_context_get_filename(dl->ctx));
578
580 }
581}
582
583
584static gboolean nsgtk_download_hide(GtkWidget *window)
585{
586 gtk_widget_hide(window);
587 return TRUE;
588}
589
590
591/**
592 * Prompt user for downloaded file name
593 *
594 * \param filename The original name of the file
595 * \param domain the domain the file is being downloaded from
596 * \param size The size of the file being downloaded
597 */
598static gchar*
600 const gchar *domain,
601 const gchar *size)
602{
603 enum { GTK_RESPONSE_DOWNLOAD, GTK_RESPONSE_SAVE_AS };
604 GtkWidget *dialog;
605 char *destination = NULL;
606 gchar *message;
607 gchar *info;
608
609 message = g_strdup(messages_get("gtkStartDownload"));
610 info = g_strdup_printf(messages_get("gtkInfo"), filename, domain, size);
611
612 dialog = gtk_message_dialog_new_with_markup(
614 GTK_DIALOG_DESTROY_WITH_PARENT,
615 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
616 "<span size=\"x-large\" weight=\"ultrabold\">%s</span>"
617 "\n\n<small>%s</small>",
618 message,
619 info);
620
621 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
622 NSGTK_STOCK_SAVE, GTK_RESPONSE_DOWNLOAD,
623 NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
624 NSGTK_STOCK_SAVE_AS, GTK_RESPONSE_SAVE_AS,
625 NULL);
626
627 gint result = gtk_dialog_run(GTK_DIALOG(dialog));
628 gtk_widget_destroy(dialog);
629 g_free(message);
630 g_free(info);
631
632 switch (result) {
633 case GTK_RESPONSE_SAVE_AS: {
634 dialog = gtk_file_chooser_dialog_new(
635 messages_get("gtkSave"),
637 GTK_FILE_CHOOSER_ACTION_SAVE,
638 NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
639 NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
640 NULL);
641 gtk_file_chooser_set_current_name
642 (GTK_FILE_CHOOSER(dialog), filename);
643 gtk_file_chooser_set_current_folder
644 (GTK_FILE_CHOOSER(dialog),
645 nsoption_charp(downloads_directory));
646 gtk_file_chooser_set_do_overwrite_confirmation
647 (GTK_FILE_CHOOSER(dialog),
648 nsoption_bool(request_overwrite));
649
650 gint result = gtk_dialog_run(GTK_DIALOG(dialog));
651 if (result == GTK_RESPONSE_ACCEPT)
652 destination = gtk_file_chooser_get_filename
653 (GTK_FILE_CHOOSER(dialog));
654 gtk_widget_destroy(dialog);
655 break;
656 }
657 case GTK_RESPONSE_DOWNLOAD: {
658 destination = malloc(strlen(nsoption_charp(downloads_directory))
659 + strlen(filename) + SLEN("/") + 1);
660 if (destination == NULL) {
661 nsgtk_warning(messages_get("NoMemory"), 0);
662 break;
663 }
664 sprintf(destination, "%s/%s",
665 nsoption_charp(downloads_directory), filename);
666 /* Test if file already exists and display overwrite
667 * confirmation if needed */
668 if (g_file_test(destination, G_FILE_TEST_EXISTS) &&
669 nsoption_bool(request_overwrite)) {
670 GtkWidget *button;
671
672 message = g_strdup_printf(messages_get("gtkOverwrite"),
673 filename);
674 info = g_strdup_printf(messages_get("gtkOverwriteInfo"),
675 nsoption_charp(downloads_directory));
676
677 dialog = gtk_message_dialog_new_with_markup(
679 GTK_DIALOG_DESTROY_WITH_PARENT,
680 GTK_MESSAGE_QUESTION,
681 GTK_BUTTONS_CANCEL,
682 "<b>%s</b>",
683 message);
684 gtk_message_dialog_format_secondary_markup(
685 GTK_MESSAGE_DIALOG(dialog),
686 "%s",
687 info);
688
689 button = gtk_dialog_add_button(GTK_DIALOG(dialog),
690 "_Replace",
691 GTK_RESPONSE_DOWNLOAD);
692 gtk_button_set_image(GTK_BUTTON(button),
695 GTK_ICON_SIZE_BUTTON));
696
697 gint result = gtk_dialog_run(GTK_DIALOG(dialog));
698 if (result == GTK_RESPONSE_CANCEL)
699 destination = NULL;
700
701 gtk_widget_destroy(dialog);
702 g_free(message);
703 g_free(info);
704 }
705 break;
706 }
707 }
708 return destination;
709}
710
711
712static gboolean nsgtk_download_handle_error(GError *error)
713{
714 GtkWidget*dialog;
715 gchar *message;
716
717 if (error != NULL) {
718 message = g_strdup_printf(messages_get("gtkFileError"),
719 error->message);
720
721 dialog = gtk_message_dialog_new_with_markup(
723 GTK_DIALOG_MODAL,
724 GTK_MESSAGE_ERROR,
725 GTK_BUTTONS_OK,
726 "<big><b>%s</b></big>\n\n"
727 "<small>%s</small>",
728 messages_get("gtkFailed"),
729 message);
730
731 gtk_dialog_run(GTK_DIALOG(dialog));
732 gtk_widget_destroy(dialog);
733 return TRUE;
734 }
735 return FALSE;
736}
737
738
740{
742 /* The iter has already been updated to this row */
743 gtk_list_store_set(dl_ctx.store,
744 &dl_ctx.iter,
746 dl,
747 -1);
748}
749
750
751/**
752 * Wrapper to GSourceFunc-ify nsgtk_download_update.
753 */
754static gboolean
756{
757 return nsgtk_download_update(FALSE);
758}
759
760
761/**
762 * core callback on creating a new download
763 */
764static struct gui_download_window *
766{
767 nsurl *url;
768 unsigned long long int total_size;
769 gchar *domain;
770 gchar *destination;
771 gboolean unknown_size;
772 struct gui_download_window *download;
773 const char *size;
774
777 unknown_size = total_size == 0;
778 size = (total_size == 0 ?
779 messages_get("gtkUnknownSize") :
781
783
784 download = malloc(sizeof *download);
785 if (download == NULL) {
786 return NULL;
787 }
788
789 /* set the domain to the host component of the url if it exists */
791 domain = g_strdup(lwc_string_data(nsurl_get_component(url, NSURL_HOST)));
792 } else {
793 domain = g_strdup(messages_get("gtkUnknownHost"));
794 }
795 if (domain == NULL) {
796 free(download);
797 return NULL;
798 }
799
800 /* show the dialog */
803 if (destination == NULL) {
804 g_free(domain);
805 free(download);
806 return NULL;
807 }
808
809 /* Add the new row and store the reference to it (which keeps track of
810 * the tree changes) */
811 gtk_list_store_prepend(dl_ctx.store, &dl_ctx.iter);
812 download->row = gtk_tree_row_reference_new(
813 GTK_TREE_MODEL(dl_ctx.store),
814 gtk_tree_model_get_path(
815 GTK_TREE_MODEL(dl_ctx.store),
816 &dl_ctx.iter));
817
818 download->ctx = ctx;
819 download->name = g_string_new(download_context_get_filename(ctx));
820 download->time_left = g_string_new("");
821 download->size_total = total_size;
822 download->size_downloaded = 0;
823 download->speed = 0;
824 download->start_time = g_timer_elapsed(dl_ctx.timer, NULL);
825 download->time_remaining = -1;
826 download->status = NSGTK_DOWNLOAD_NONE;
827 download->progress = 0;
828 download->error = NULL;
829 download->write = g_io_channel_new_file(destination,
830 "w",
831 &download->error);
832
833 if (nsgtk_download_handle_error(download->error)) {
834 g_string_free(download->name, TRUE);
835 g_string_free(download->time_left, TRUE);
836 free(download);
837 return NULL;
838 }
839 g_io_channel_set_encoding(download->write, NULL, &download->error);
840
842
845
846 if (unknown_size) {
848 }
849
850 if (dl_ctx.num_active == 0) {
851 g_timeout_add(
854 NULL);
855 }
856
857 dl_ctx.list = g_list_prepend(dl_ctx.list, download);
858
859 return download;
860}
861
862
863/**
864 * core callback on receipt of data
865 */
866static nserror
868 const char *data,
869 unsigned int size)
870{
871 g_io_channel_write_chars(dw->write, data, size, NULL, &dw->error);
872 if (dw->error != NULL) {
873 dw->speed = 0;
874 dw->time_remaining = -1;
875
878
880
881 gtk_window_present(dl_ctx.window);
882
883 return NSERROR_SAVE_FAILED;
884 }
885 dw->size_downloaded += size;
886
887 return NSERROR_OK;
888}
889
890
891/**
892 * core callback on error
893 */
894static void
895gui_download_window_error(struct gui_download_window *dw, const char *error_msg)
896{
897}
898
899
900/**
901 * core callback when core download is complete
902 */
904{
905 g_io_channel_shutdown(dw->write, TRUE, &dw->error);
906 g_io_channel_unref(dw->write);
907
908 dw->speed = 0;
909 dw->time_remaining = -1;
910 dw->progress = 100;
911 dw->size_total = dw->size_downloaded;
914
915 if (nsoption_bool(downloads_clear)) {
917 } else {
919 }
920}
921
922
928};
929
931
932
933/* exported interface documented in gtk/download.h */
935{
936 GtkBuilder* builder;
937 nserror res;
938
939 res = nsgtk_builder_new_from_resname("downloads", &builder);
940 if (res != NSERROR_OK) {
941 NSLOG(netsurf, INFO, "Download UI builder init failed");
942 return res;
943 }
944
945 gtk_builder_connect_signals(builder, NULL);
946
947 dl_ctx.pause = GTK_BUTTON(gtk_builder_get_object(builder,
948 "buttonPause"));
949 dl_ctx.clear = GTK_BUTTON(gtk_builder_get_object(builder,
950 "buttonClear"));
951 dl_ctx.cancel = GTK_BUTTON(gtk_builder_get_object(builder,
952 "buttonCancel"));
953 dl_ctx.resume = GTK_BUTTON(gtk_builder_get_object(builder,
954 "buttonPlay"));
955
956 dl_ctx.progress = GTK_PROGRESS_BAR(gtk_builder_get_object(builder,
957 "progressBar"));
958 dl_ctx.window = GTK_WINDOW(gtk_builder_get_object(builder,
959 "wndDownloads"));
960 dl_ctx.parent = NULL;
961
962 gtk_window_set_transient_for(GTK_WINDOW(dl_ctx.window),
963 dl_ctx.parent);
964 gtk_window_set_destroy_with_parent(GTK_WINDOW(dl_ctx.window),
965 FALSE);
966
967 dl_ctx.timer = g_timer_new();
968
970
971 dl_ctx.store = gtk_list_store_new(NSGTK_DOWNLOAD_N_COLUMNS,
972 G_TYPE_INT, /* % complete */
973 G_TYPE_STRING, /* Description */
974 G_TYPE_STRING, /* Time remaining */
975 G_TYPE_STRING, /* Speed */
976 G_TYPE_INT, /* Pulse */
977 G_TYPE_STRING, /* Status */
978 G_TYPE_POINTER /* Download structure */
979 );
980
981
982 gtk_tree_view_set_model(dl_ctx.tree, GTK_TREE_MODEL(dl_ctx.store));
983
984 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(dl_ctx.store),
986 (GtkTreeIterCompareFunc)nsgtk_download_sort, NULL, NULL);
987 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dl_ctx.store),
989 GTK_SORT_ASCENDING);
990
991 g_object_unref(dl_ctx.store);
992
993 dl_ctx.selection = gtk_tree_view_get_selection(dl_ctx.tree);
994 gtk_tree_selection_set_mode(dl_ctx.selection, GTK_SELECTION_MULTIPLE);
995
996 g_signal_connect(G_OBJECT(dl_ctx.selection),
997 "changed",
999 NULL);
1000
1001 g_signal_connect(dl_ctx.tree,
1002 "row-activated",
1004 NULL);
1005
1006 g_signal_connect_swapped(gtk_builder_get_object(builder, "buttonClear"),
1007 "clicked",
1008 G_CALLBACK(nsgtk_download_do),
1010
1011 g_signal_connect_swapped(gtk_builder_get_object(builder, "buttonCancel"),
1012 "clicked",
1013 G_CALLBACK(nsgtk_download_do),
1015
1016 g_signal_connect(G_OBJECT(dl_ctx.window),
1017 "delete-event",
1018 G_CALLBACK(nsgtk_download_hide),
1019 NULL);
1020
1021 return NSERROR_OK;
1022}
1023
1024
1025/* exported interface documented in gtk/download.h */
1027{
1029}
1030
1031
1032/* exported interface documented in gtk/download.h */
1034{
1035 GtkWidget *dialog;
1036 gint response;
1037
1038 if (dl_ctx.num_active == 0) {
1039 return false;
1040 }
1041
1042 dialog = gtk_message_dialog_new_with_markup(
1043 parent,
1044 GTK_DIALOG_MODAL,
1045 GTK_MESSAGE_WARNING,
1046 GTK_BUTTONS_NONE,
1047 "<big><b>%s</b></big>\n\n"
1048 "<small>%s</small>",
1049 messages_get("gtkQuit"),
1050 messages_get("gtkDownloadsRunning"));
1051
1052 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
1053 "gtk-cancel", GTK_RESPONSE_CANCEL,
1054 "gtk-quit", GTK_RESPONSE_CLOSE,
1055 NULL);
1056
1057 response = gtk_dialog_run(GTK_DIALOG(dialog));
1058 gtk_widget_destroy(dialog);
1059
1060 if (response == GTK_RESPONSE_CANCEL) {
1061 return true;
1062 }
1063
1064 return false;
1065}
1066
1067
1068/* exported interface documented in gtk/download.h */
1070{
1071 gtk_window_set_transient_for(dl_ctx.window, dl_ctx.parent);
1072 gtk_window_present(dl_ctx.window);
1073}
STATIC char result[100]
Definition: arexx.c:77
GtkWidget * nsgtk_image_new_from_stock(const gchar *id, GtkIconSize size)
Creates a GtkImage displaying a stock icon.
Definition: compat.c:198
gboolean nsgtk_widget_get_visible(GtkWidget *widget)
Definition: compat.c:69
Compatibility functions for older GTK versions (interface)
#define NSGTK_STOCK_SAVE
Definition: compat.h:61
#define NSGTK_STOCK_SAVE_AS
Definition: compat.h:62
#define NSGTK_STOCK_CANCEL
Definition: compat.h:55
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
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)
struct treeview treeview
Definition: treeview.h:36
wimp_w parent
Definition: dialog.c:88
wimp_w dialog
Definition: dialog.c:87
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_SAVE_FAILED
Failed to save data.
Definition: errors.h:36
@ NSERROR_OK
No error.
Definition: errors.h:30
static gboolean nsgtk_download_update(gboolean force_update)
Definition: download.c:411
static struct gui_download_table download_table
Definition: download.c:923
static void nsgtk_download_gfunc__gtk_tree_path_free(gpointer data, gpointer user_data)
Wrapper to GFunc-ify gtk_tree_path_free for g_list_foreach.
Definition: download.c:277
static gboolean nsgtk_download_gsourcefunc__nsgtk_download_update(gpointer user_data)
Wrapper to GSourceFunc-ify nsgtk_download_update.
Definition: download.c:755
@ NSGTK_DOWNLOAD_N_COLUMNS
Definition: download.c:56
@ NSGTK_DOWNLOAD
Definition: download.c:54
@ NSGTK_DOWNLOAD_INFO
Definition: download.c:49
@ NSGTK_DOWNLOAD_SPEED
Definition: download.c:51
@ NSGTK_DOWNLOAD_PROGRESS
Definition: download.c:48
@ NSGTK_DOWNLOAD_REMAINING
Definition: download.c:50
@ NSGTK_DOWNLOAD_STATUS
Definition: download.c:53
@ NSGTK_DOWNLOAD_PULSE
Definition: download.c:52
void nsgtk_download_show(GtkWindow *parent)
Show the download window.
Definition: download.c:1069
static void nsgtk_download_do(nsgtk_download_selection_action action)
Definition: download.c:293
static void nsgtk_download_sensitivity_update_buttons(nsgtk_download_actions sensitivity)
Definition: download.c:218
static nserror gui_download_window_data(struct gui_download_window *dw, const char *data, unsigned int size)
core callback on receipt of data
Definition: download.c:867
static struct gui_download_window * gui_download_window_create(download_context *ctx, struct gui_window *gui)
core callback on creating a new download
Definition: download.c:765
static gchar * nsgtk_download_info_to_string(struct gui_download_window *dl)
Definition: download.c:336
nsgtk_download_status
Definition: download.c:59
@ NSGTK_DOWNLOAD_NONE
Definition: download.c:60
@ NSGTK_DOWNLOAD_WORKING
Definition: download.c:61
@ NSGTK_DOWNLOAD_CANCELED
Definition: download.c:64
@ NSGTK_DOWNLOAD_COMPLETE
Definition: download.c:63
@ NSGTK_DOWNLOAD_ERROR
Definition: download.c:62
void nsgtk_download_destroy(void)
Destroy download window.
Definition: download.c:1026
static void nsgtk_download_change_status(struct gui_download_window *dl, nsgtk_download_status status)
Definition: download.c:547
static void nsgtk_download_gfunc__g_free(gpointer data, gpointer user_data)
Wrapper to GFunc-ify g_free for g_list_foreach.
Definition: download.c:287
static gboolean nsgtk_download_handle_error(GError *error)
Definition: download.c:712
static gboolean nsgtk_download_hide(GtkWidget *window)
Definition: download.c:584
static void nsgtk_download_change_sensitivity(struct gui_download_window *dl, nsgtk_download_actions sensitivity)
Definition: download.c:538
static void nsgtk_download_store_cancel_item(struct gui_download_window *dl, void *user_data)
Definition: download.c:564
static GtkTreeView * nsgtk_download_tree_view_new(GtkBuilder *gladeFile)
Definition: download.c:137
static const gchar * status_messages[]
Definition: download.c:74
static void gui_download_window_error(struct gui_download_window *dw, const char *error_msg)
core callback on error
Definition: download.c:895
static void nsgtk_download_tree_view_row_activated(GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data)
Definition: download.c:520
static void nsgtk_download_store_clear_item(struct gui_download_window *dl, void *user_data)
Definition: download.c:497
static void nsgtk_download_store_create_item(struct gui_download_window *dl)
Definition: download.c:739
bool nsgtk_check_for_downloads(GtkWindow *parent)
Check with user if download is in progress they want to complete.
Definition: download.c:1033
static void gui_download_window_done(struct gui_download_window *dw)
core callback when core download is complete
Definition: download.c:903
static void nsgtk_download_store_update_item(struct gui_download_window *dl)
Definition: download.c:383
static gchar * nsgtk_download_time_to_string(gint seconds)
Definition: download.c:359
nserror nsgtk_download_init(void)
Initialise download window ready for use.
Definition: download.c:934
nsgtk_download_actions
Definition: download.c:67
@ NSGTK_DOWNLOAD_CLEAR
Definition: download.c:71
@ NSGTK_DOWNLOAD_PAUSE
Definition: download.c:68
@ NSGTK_DOWNLOAD_CANCEL
Definition: download.c:70
@ NSGTK_DOWNLOAD_RESUME
Definition: download.c:69
void(* nsgtk_download_selection_action)(struct gui_download_window *dl, void *user_data)
Definition: download.c:104
#define UPDATE_RATE
Definition: download.c:43
static struct download_window_ctx dl_ctx
global instance of the download window
Definition: download.c:134
static gint nsgtk_download_sort(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
Definition: download.c:203
struct gui_download_table * nsgtk_download_table
download operation table for gtk
Definition: download.c:930
static gchar * nsgtk_download_dialog_show(const gchar *filename, const gchar *domain, const gchar *size)
Prompt user for downloaded file name.
Definition: download.c:599
static void nsgtk_download_sensitivity_evaluate(GtkTreeSelection *selection)
Definition: download.c:234
nserror nsgtk_warning(const char *warning, const char *detail)
Warn the user of an event.
Definition: gui.c:96
struct nsgtk_scaffolding * nsgtk_get_scaffold(struct gui_window *g)
get containing nsgtk scaffolding handle from gui window handle
Definition: window.c:1612
Interface to platform-specific download operations.
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
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).
bool nsurl_has_component(const nsurl *url, nsurl_component part)
Enquire about the existence of componenets in a given URL.
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_HOST
Definition: nsurl.h:49
struct nsurl nsurl
NetSurf URL object.
Definition: nsurl.h:31
nserror nsgtk_builder_new_from_resname(const char *resname, GtkBuilder **builder_out)
Create gtk builder object for the named ui resource.
Definition: resources.c:526
Interface to gtk builtin resource handling.
GtkWindow * nsgtk_scaffolding_window(struct nsgtk_scaffolding *g)
Get the gtk window for a scaffolding.
Definition: scaffolding.c:1310
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
Table column data.
Definition: box.h:114
A context for a download.
Definition: download.c:40
context for a nsgtk download window.
Definition: download.c:110
GtkWindow * parent
Definition: download.c:112
GtkTreeView * tree
Definition: download.c:116
GtkProgressBar * progress
Definition: download.c:114
GtkButton * resume
Definition: download.c:126
GtkListStore * store
Definition: download.c:117
GtkButton * clear
Definition: download.c:124
GtkTreeIter iter
Definition: download.c:119
GtkTreeSelection * selection
Definition: download.c:118
GtkButton * pause
Definition: download.c:123
GtkWindow * window
Definition: download.c:111
GtkButton * cancel
Definition: download.c:125
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
uint32_t size_total
Definition: download.h:55
unsigned int total_size
Size of resource, or 0 if unknown.
Definition: download.c:92
uint32_t size_downloaded
Definition: download.h:56
unsigned long long int size_downloaded
Definition: download.c:93
nsgtk_download_status status
Definition: download.c:88
nsatari_download_status status
Definition: download.h:46
GtkTreeRowReference * row
Definition: download.c:99
unsigned long long int size_total
Definition: download.c:92
nsgtk_download_actions sensitivity
Definition: download.c:87
const char * url
Definition: download.c:102
char * destination
Definition: download.h:47
GString * time_left
Definition: download.c:91
struct download_context * ctx
Associated context, or 0 if the fetch has completed or aborted.
Definition: download.c:101
NSDownloadWindow * window
Definition: download.cpp:60
GString * name
Definition: download.c:90
gfloat time_remaining
Definition: download.c:95
GIOChannel * write
Definition: download.c:100
first entry in window list
Definition: gui.c:297
The treeview context.
Definition: treeview.c:232
Option reading and saving interface.
#define nsoption_charp(OPTION)
Get the value of a string option.
Definition: nsoption.h:331
#define nsoption_bool(OPTION)
Get the value of a boolean option.
Definition: nsoption.h:304
Interface to a number of general purpose functionality.
#define fallthrough
switch fall through
Definition: utils.h:119
#define SLEN(x)
Calculate length of constant C string.
Definition: utils.h:88
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