/* * Copyright 2008 Michael Lester * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "utils/log.h" #include "utils/utils.h" #include "utils/url.h" #include "utils/messages.h" #include "desktop/gui.h" #include "gtk/gui.h" #include "gtk/scaffolding.h" #include "utils/nsoption.h" #include "gtk/download.h" #include "gtk/window.h" #include "gtk/compat.h" #define UPDATE_RATE 500 /* In milliseconds */ struct download_context; enum { NSGTK_DOWNLOAD_PROGRESS, NSGTK_DOWNLOAD_INFO, NSGTK_DOWNLOAD_REMAINING, NSGTK_DOWNLOAD_SPEED, NSGTK_DOWNLOAD_PULSE, NSGTK_DOWNLOAD_STATUS, NSGTK_DOWNLOAD, NSGTK_DOWNLOAD_N_COLUMNS }; typedef enum { NSGTK_DOWNLOAD_NONE, NSGTK_DOWNLOAD_WORKING, NSGTK_DOWNLOAD_ERROR, NSGTK_DOWNLOAD_COMPLETE, NSGTK_DOWNLOAD_CANCELED } nsgtk_download_status; typedef enum { NSGTK_DOWNLOAD_PAUSE = 1 << 0, NSGTK_DOWNLOAD_RESUME = 1 << 1, NSGTK_DOWNLOAD_CANCEL = 1 << 2, NSGTK_DOWNLOAD_CLEAR = 1 << 3 } nsgtk_download_actions; struct gui_download_window { struct download_context *ctx; nsgtk_download_actions sensitivity; nsgtk_download_status status; GString *name; GString *time_left; gint size_total; gint size_downloaded; gint progress; gfloat time_remaining; gfloat start_time; gfloat speed; GtkTreeRowReference *row; GIOChannel *write; GError *error; }; typedef void (*nsgtk_download_selection_action)(struct gui_download_window *dl); static GtkWindow *nsgtk_download_window, *nsgtk_download_parent; static GtkProgressBar *nsgtk_download_progress_bar; static GtkTreeView *nsgtk_download_tree; static GtkListStore *nsgtk_download_store; static GtkTreeSelection *nsgtk_download_selection; static GtkTreeIter nsgtk_download_iter; static GTimer *nsgtk_downloads_timer; static GList *nsgtk_downloads_list; static GtkButton *nsgtk_download_button_pause; static GtkButton *nsgtk_download_button_clear; static GtkButton *nsgtk_download_button_cancel; static GtkButton *nsgtk_download_button_resume; static gint nsgtk_downloads_num_active; static const gchar* status_messages[] = { NULL, "gtkWorking", "gtkError", "gtkComplete", "gtkCanceled" }; static GtkTreeView* nsgtk_download_tree_view_new(GtkBuilder *gladeFile) { GtkTreeView *treeview; GtkCellRenderer *renderer; treeview = GTK_TREE_VIEW(gtk_builder_get_object(gladeFile, "treeDownloads")); /* Progress column */ renderer = gtk_cell_renderer_progress_new(); gtk_tree_view_insert_column_with_attributes (treeview, -1, messages_get("gtkProgress"), renderer, "value", NSGTK_DOWNLOAD_PROGRESS, "pulse", NSGTK_DOWNLOAD_PULSE, "text", NSGTK_DOWNLOAD_STATUS, NULL); /* Information column */ renderer = gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(renderer), "wrap-mode", PANGO_WRAP_WORD_CHAR, "wrap-width", 300, NULL); gtk_tree_view_insert_column_with_attributes (treeview, -1, messages_get("gtkDetails"), renderer, "text", NSGTK_DOWNLOAD_INFO, NULL); gtk_tree_view_column_set_expand(gtk_tree_view_get_column(treeview, NSGTK_DOWNLOAD_INFO), TRUE); /* Time remaining column */ renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (treeview, -1, messages_get("gtkRemaining"), renderer, "text", NSGTK_DOWNLOAD_REMAINING, NULL); /* Speed column */ renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (treeview, -1, messages_get("gtkSpeed"), renderer, "text", NSGTK_DOWNLOAD_SPEED, NULL); return treeview; } static gint nsgtk_download_sort(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata) { struct gui_download_window *dl1, *dl2; gtk_tree_model_get(model, a, NSGTK_DOWNLOAD, &dl1, -1); gtk_tree_model_get(model, b, NSGTK_DOWNLOAD, &dl2, -1); return dl1->status - dl2->status; } static void nsgtk_download_sensitivity_update_buttons(nsgtk_download_actions sensitivity) { /* Glade seems to pack the buttons in an arbitrary order */ enum { PAUSE_BUTTON, CLEAR_BUTTON, CANCEL_BUTTON, RESUME_BUTTON }; gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_pause), sensitivity & NSGTK_DOWNLOAD_PAUSE); gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_clear), sensitivity & NSGTK_DOWNLOAD_CLEAR); gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_cancel), sensitivity & NSGTK_DOWNLOAD_CANCEL); gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_resume), sensitivity & NSGTK_DOWNLOAD_RESUME); } static void nsgtk_download_sensitivity_evaluate(GtkTreeSelection *selection) { GtkTreeIter iter; GList *rows; gboolean selected = gtk_tree_selection_count_selected_rows(selection); GtkTreeModel *model = GTK_TREE_MODEL(nsgtk_download_store); nsgtk_download_actions sensitivity = 0; struct gui_download_window *dl; if (selected) { rows = gtk_tree_selection_get_selected_rows(selection, &model); while (rows != NULL) { gtk_tree_model_get_iter(model, &iter, (GtkTreePath*)rows->data); gtk_tree_model_get(model, &iter, NSGTK_DOWNLOAD, &dl, -1); sensitivity |= dl->sensitivity; rows = rows->next; } } else { rows = nsgtk_downloads_list; while (rows != NULL) { dl = rows->data; sensitivity |= (dl->sensitivity & NSGTK_DOWNLOAD_CLEAR); rows = rows->next; } } nsgtk_download_sensitivity_update_buttons(sensitivity); } static void nsgtk_download_do(nsgtk_download_selection_action action) { GList *rows, *dls = NULL; GtkTreeModel *model = GTK_TREE_MODEL(nsgtk_download_store); gboolean selection_exists = gtk_tree_selection_count_selected_rows( nsgtk_download_selection); if (selection_exists) { rows = gtk_tree_selection_get_selected_rows( nsgtk_download_selection, &model); while (rows != NULL) { struct gui_download_window *dl; gtk_tree_model_get_iter(GTK_TREE_MODEL( nsgtk_download_store), &nsgtk_download_iter, (GtkTreePath*)rows->data); gtk_tree_model_get(GTK_TREE_MODEL(nsgtk_download_store), &nsgtk_download_iter, NSGTK_DOWNLOAD, &dl, -1); dls = g_list_prepend(dls, dl); rows = rows->next; } g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL); g_list_foreach(rows, (GFunc)g_free, NULL); g_list_free(rows); } else dls = g_list_copy(nsgtk_downloads_list); g_list_foreach(dls, (GFunc)action, NULL); g_list_free(dls); } static gchar* nsgtk_download_info_to_string(struct gui_download_window *dl) { gchar *size_info = g_strdup_printf(messages_get("gtkSizeInfo"), human_friendly_bytesize(dl->size_downloaded), dl->size_total == 0 ? messages_get("gtkUnknownSize") : human_friendly_bytesize(dl->size_total)); gchar *r; if (dl->status != NSGTK_DOWNLOAD_ERROR) r = g_strdup_printf("%s\n%s", dl->name->str, size_info); else r = g_strdup_printf("%s\n%s", dl->name->str, dl->error->message); g_free(size_info); return r; } static gchar* nsgtk_download_time_to_string(gint seconds) { gint hours, minutes; if (seconds < 0) return g_strdup("-"); hours = seconds / 3600; seconds -= hours * 3600; minutes = seconds / 60; seconds -= minutes * 60; if (hours > 0) return g_strdup_printf("%u:%02u:%02u", hours, minutes, seconds); else return g_strdup_printf("%u:%02u", minutes, seconds); } static void nsgtk_download_store_update_item (struct gui_download_window *dl) { gchar *info = nsgtk_download_info_to_string(dl); char *human = human_friendly_bytesize(dl->speed); char speed[strlen(human) + SLEN("/s") + 1]; sprintf(speed, "%s/s", human); gchar *time = nsgtk_download_time_to_string(dl->time_remaining); gboolean pulse = dl->status == NSGTK_DOWNLOAD_WORKING; /* Updates iter (which is needed to set and get data) with the dl row */ gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store), &nsgtk_download_iter, gtk_tree_row_reference_get_path(dl->row)); gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter, NSGTK_DOWNLOAD_PULSE, pulse ? dl->progress : -1, NSGTK_DOWNLOAD_PROGRESS, pulse ? 0 : dl->progress, NSGTK_DOWNLOAD_INFO, info, NSGTK_DOWNLOAD_SPEED, dl->speed == 0 ? "-" : speed, NSGTK_DOWNLOAD_REMAINING, time, NSGTK_DOWNLOAD, dl, -1); g_free(info); g_free(time); } static gboolean nsgtk_download_update(gboolean force_update) { /* Be sure we need to update */ if (!nsgtk_widget_get_visible(GTK_WIDGET(nsgtk_download_window))) return TRUE; GList *list; gchar *text; gboolean update, pulse_mode = FALSE; gint downloaded = 0, total = 0, dls = 0; gfloat percent, elapsed = g_timer_elapsed(nsgtk_downloads_timer, NULL); nsgtk_downloads_num_active = 0; for (list = nsgtk_downloads_list; list != NULL; list = list->next) { struct gui_download_window *dl = list->data; update = force_update; switch (dl->status) { case NSGTK_DOWNLOAD_WORKING: pulse_mode = TRUE; case NSGTK_DOWNLOAD_NONE: dl->speed = dl->size_downloaded / (elapsed - dl->start_time); if (dl->status == NSGTK_DOWNLOAD_NONE) { dl->time_remaining = (dl->size_total - dl->size_downloaded)/ dl->speed; dl->progress = (gfloat) dl->size_downloaded / dl->size_total * 100; } else dl->progress++; nsgtk_downloads_num_active++; update = TRUE; case NSGTK_DOWNLOAD_COMPLETE: downloaded += dl->size_downloaded; total += dl->size_total; dls++; default: ;//Do nothing } if (update) nsgtk_download_store_update_item(dl); } if (pulse_mode) { text = g_strdup_printf( messages_get(nsgtk_downloads_num_active > 1 ? "gtkProgressBarPulse" : "gtkProgressBarPulseSingle"), nsgtk_downloads_num_active); gtk_progress_bar_pulse(nsgtk_download_progress_bar); gtk_progress_bar_set_text(nsgtk_download_progress_bar, text); } else { percent = total != 0 ? (gfloat)downloaded / total : 0; text = g_strdup_printf(messages_get("gtkProgressBar"), floor(percent*100), dls); gtk_progress_bar_set_fraction(nsgtk_download_progress_bar, percent); gtk_progress_bar_set_text(nsgtk_download_progress_bar, text); } g_free(text); if (nsgtk_downloads_num_active == 0) return FALSE; /* Returning FALSE here cancels the g_timeout */ else return TRUE; } static void nsgtk_download_store_clear_item(struct gui_download_window *dl) { if (dl->sensitivity & NSGTK_DOWNLOAD_CLEAR) { nsgtk_downloads_list = g_list_remove(nsgtk_downloads_list, dl); gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store), &nsgtk_download_iter, gtk_tree_row_reference_get_path(dl->row)); gtk_list_store_remove(nsgtk_download_store, &nsgtk_download_iter); download_context_destroy(dl->ctx); g_string_free(dl->name, TRUE); g_string_free(dl->time_left, TRUE); g_free(dl); nsgtk_download_sensitivity_evaluate(nsgtk_download_selection); nsgtk_download_update(FALSE); } } static void nsgtk_download_tree_view_row_activated(GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) { GtkTreeModel *model; GtkTreeIter iter; model = gtk_tree_view_get_model(tree); if (gtk_tree_model_get_iter(model, &iter, path)) { /* TODO: This will be a context action (pause, start, clear) */ nsgtk_download_do(nsgtk_download_store_clear_item); } } static void nsgtk_download_change_sensitivity(struct gui_download_window *dl, nsgtk_download_actions sensitivity) { dl->sensitivity = sensitivity; nsgtk_download_sensitivity_evaluate(nsgtk_download_selection); } static void nsgtk_download_change_status ( struct gui_download_window *dl, nsgtk_download_status status) { dl->status = status; if (status != NSGTK_DOWNLOAD_NONE) { gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store), &nsgtk_download_iter, gtk_tree_row_reference_get_path(dl->row)); gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter, NSGTK_DOWNLOAD_STATUS, messages_get(status_messages[status]), -1); } } static void nsgtk_download_store_cancel_item (struct gui_download_window *dl) { if (dl->sensitivity & NSGTK_DOWNLOAD_CANCEL) { dl->speed = 0; dl->size_downloaded = 0; dl->progress = 0; dl->time_remaining = -1; nsgtk_download_change_sensitivity(dl, NSGTK_DOWNLOAD_CLEAR); nsgtk_download_change_status(dl, NSGTK_DOWNLOAD_CANCELED); download_context_abort(dl->ctx); g_unlink(download_context_get_filename(dl->ctx)); nsgtk_download_update(TRUE); } } static gboolean nsgtk_download_hide (GtkWidget *window) { gtk_widget_hide(window); return TRUE; } bool nsgtk_download_init(const char *glade_file_location) { GError* error = NULL; GtkBuilder* builder = gtk_builder_new(); if (!gtk_builder_add_from_file(builder, glade_file_location, &error)) { g_warning("Couldn't load builder file: %s", error->message); g_error_free(error); return false; } nsgtk_download_button_pause = GTK_BUTTON(gtk_builder_get_object(builder, "buttonPause")); nsgtk_download_button_clear = GTK_BUTTON(gtk_builder_get_object(builder, "buttonClear")); nsgtk_download_button_cancel = GTK_BUTTON(gtk_builder_get_object(builder, "buttonCancel")); nsgtk_download_button_resume = GTK_BUTTON(gtk_builder_get_object(builder, "buttonPlay")); nsgtk_download_progress_bar = GTK_PROGRESS_BAR(gtk_builder_get_object(builder, "progressBar")); nsgtk_download_window = GTK_WINDOW(gtk_builder_get_object(builder, "wndDownloads")); nsgtk_download_parent = NULL; gtk_window_set_transient_for(GTK_WINDOW(nsgtk_download_window), nsgtk_download_parent); gtk_window_set_destroy_with_parent(GTK_WINDOW(nsgtk_download_window), FALSE); nsgtk_downloads_timer = g_timer_new(); nsgtk_download_tree = nsgtk_download_tree_view_new(builder); nsgtk_download_store = gtk_list_store_new(NSGTK_DOWNLOAD_N_COLUMNS, G_TYPE_INT, /* % complete */ G_TYPE_STRING, /* Description */ G_TYPE_STRING, /* Time remaining */ G_TYPE_STRING, /* Speed */ G_TYPE_INT, /* Pulse */ G_TYPE_STRING, /* Status */ G_TYPE_POINTER /* Download structure */ ); gtk_tree_view_set_model(nsgtk_download_tree, GTK_TREE_MODEL(nsgtk_download_store)); gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(nsgtk_download_store), NSGTK_DOWNLOAD_STATUS, (GtkTreeIterCompareFunc) nsgtk_download_sort, NULL, NULL); gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE(nsgtk_download_store), NSGTK_DOWNLOAD_STATUS, GTK_SORT_ASCENDING); g_object_unref(nsgtk_download_store); nsgtk_download_selection = gtk_tree_view_get_selection(nsgtk_download_tree); gtk_tree_selection_set_mode(nsgtk_download_selection, GTK_SELECTION_MULTIPLE); g_signal_connect(G_OBJECT(nsgtk_download_selection), "changed", G_CALLBACK(nsgtk_download_sensitivity_evaluate), NULL); g_signal_connect(nsgtk_download_tree, "row-activated", G_CALLBACK(nsgtk_download_tree_view_row_activated), NULL); g_signal_connect_swapped(gtk_builder_get_object(builder, "buttonClear"), "clicked", G_CALLBACK(nsgtk_download_do), nsgtk_download_store_clear_item); g_signal_connect_swapped(gtk_builder_get_object(builder, "buttonCancel"), "clicked", G_CALLBACK(nsgtk_download_do), nsgtk_download_store_cancel_item); g_signal_connect(G_OBJECT(nsgtk_download_window), "delete-event", G_CALLBACK(nsgtk_download_hide), NULL); return true; } void nsgtk_download_destroy () { nsgtk_download_do(nsgtk_download_store_cancel_item); } bool nsgtk_check_for_downloads (GtkWindow *parent) { if (nsgtk_downloads_num_active != 0) { GtkWidget *dialog; dialog = gtk_message_dialog_new_with_markup(parent, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, "%s\n\n" "%s", messages_get("gtkQuit"), messages_get("gtkDownloadsRunning")); gtk_dialog_add_buttons(GTK_DIALOG(dialog), "gtk-cancel", GTK_RESPONSE_CANCEL, "gtk-quit", GTK_RESPONSE_CLOSE, NULL); gint response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); if (response == GTK_RESPONSE_CANCEL) return true; } return false; } void nsgtk_download_show(GtkWindow *parent) { gtk_window_set_transient_for(nsgtk_download_window, nsgtk_download_parent); gtk_window_present(nsgtk_download_window); } static gchar* nsgtk_download_dialog_show (const gchar *filename, const gchar *domain, const gchar *size) { enum { GTK_RESPONSE_DOWNLOAD, GTK_RESPONSE_SAVE_AS }; GtkWidget *dialog; char *destination = NULL; gchar *message = g_strdup(messages_get("gtkStartDownload")); gchar *info = g_strdup_printf(messages_get("gtkInfo"), filename, domain, size); dialog = gtk_message_dialog_new_with_markup(nsgtk_download_parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s" "\n\n%s", message, info); gtk_dialog_add_buttons(GTK_DIALOG(dialog), GTK_STOCK_SAVE, GTK_RESPONSE_DOWNLOAD, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE_AS, GTK_RESPONSE_SAVE_AS, NULL); gint result = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); g_free(message); g_free(info); switch (result) { case GTK_RESPONSE_SAVE_AS: { dialog = gtk_file_chooser_dialog_new (messages_get("gtkSave"), nsgtk_download_parent, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(dialog), filename); gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(dialog), nsoption_charp(downloads_directory)); gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER(dialog), nsoption_bool(request_overwrite)); gint result = gtk_dialog_run(GTK_DIALOG(dialog)); if (result == GTK_RESPONSE_ACCEPT) destination = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog)); gtk_widget_destroy(dialog); break; } case GTK_RESPONSE_DOWNLOAD: { destination = malloc(strlen(nsoption_charp(downloads_directory)) + strlen(filename) + SLEN("/") + 1); if (destination == NULL) { warn_user(messages_get("NoMemory"), 0); break; } sprintf(destination, "%s/%s", nsoption_charp(downloads_directory), filename); /* Test if file already exists and display overwrite * confirmation if needed */ if (g_file_test(destination, G_FILE_TEST_EXISTS) && nsoption_bool(request_overwrite)) { message = g_strdup_printf(messages_get( "gtkOverwrite"), filename); info = g_strdup_printf(messages_get( "gtkOverwriteInfo"), nsoption_charp(downloads_directory)); dialog = gtk_message_dialog_new_with_markup( nsgtk_download_parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_CANCEL, "%s",message); gtk_message_dialog_format_secondary_markup( GTK_MESSAGE_DIALOG(dialog), "%s", info); GtkWidget *button = gtk_dialog_add_button( GTK_DIALOG(dialog), "_Replace", GTK_RESPONSE_DOWNLOAD); gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock( "gtk-save", GTK_ICON_SIZE_BUTTON)); gint result = gtk_dialog_run(GTK_DIALOG( dialog)); if (result == GTK_RESPONSE_CANCEL) destination = NULL; gtk_widget_destroy(dialog); g_free(message); g_free(info); } break; } } return destination; } static gboolean nsgtk_download_handle_error (GError *error) { if (error != NULL) { GtkWidget*dialog; gchar *message = g_strdup_printf(messages_get("gtkFileError"), error->message); dialog = gtk_message_dialog_new_with_markup (nsgtk_download_parent, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s\n\n" "%s", messages_get("gtkFailed"), message); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return TRUE; } return FALSE; } static void nsgtk_download_store_create_item (struct gui_download_window *dl) { nsgtk_download_store_update_item(dl); /* The iter has already been updated to this row */ gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter, NSGTK_DOWNLOAD, dl, -1); } struct gui_download_window *gui_download_window_create(download_context *ctx, struct gui_window *gui) { const char *url = download_context_get_url(ctx); unsigned long total_size = download_context_get_total_length(ctx); gchar *domain; gchar *destination; gboolean unknown_size = total_size == 0; const char *size = (total_size == 0 ? messages_get("gtkUnknownSize") : human_friendly_bytesize(total_size)); nsgtk_download_parent = nsgtk_scaffolding_window(nsgtk_get_scaffold(gui)); struct gui_download_window *download = malloc(sizeof *download); if (download == NULL) return NULL; if (url_host(url, &domain) != URL_FUNC_OK) { domain = g_strdup(messages_get("gtkUnknownHost")); if (domain == NULL) { free(download); return NULL; } } destination = nsgtk_download_dialog_show( download_context_get_filename(ctx), domain, size); if (destination == NULL) { g_free(domain); free(download); return NULL; } /* Add the new row and store the reference to it (which keeps track of * the tree changes) */ gtk_list_store_prepend(nsgtk_download_store, &nsgtk_download_iter); download->row = gtk_tree_row_reference_new( GTK_TREE_MODEL(nsgtk_download_store), gtk_tree_model_get_path( GTK_TREE_MODEL(nsgtk_download_store), &nsgtk_download_iter)); download->ctx = ctx; download->name = g_string_new(download_context_get_filename(ctx)); download->time_left = g_string_new(""); download->size_total = total_size; download->size_downloaded = 0; download->speed = 0; download->start_time = g_timer_elapsed(nsgtk_downloads_timer, NULL); download->time_remaining = -1; download->status = NSGTK_DOWNLOAD_NONE; download->progress = 0; download->error = NULL; download->write = g_io_channel_new_file(destination, "w", &download->error); if (nsgtk_download_handle_error(download->error)) { g_string_free(download->name, TRUE); g_string_free(download->time_left, TRUE); free(download); return NULL; } g_io_channel_set_encoding(download->write, NULL, &download->error); nsgtk_download_change_sensitivity(download, NSGTK_DOWNLOAD_CANCEL); nsgtk_download_store_create_item(download); nsgtk_download_show(nsgtk_download_parent); if (unknown_size) nsgtk_download_change_status(download, NSGTK_DOWNLOAD_WORKING); if (nsgtk_downloads_num_active == 0) { g_timeout_add(UPDATE_RATE, (GSourceFunc) nsgtk_download_update, FALSE); } nsgtk_downloads_list = g_list_prepend(nsgtk_downloads_list, download); return download; } nserror gui_download_window_data(struct gui_download_window *dw, const char *data, unsigned int size) { g_io_channel_write_chars(dw->write, data, size, NULL, &dw->error); if (dw->error != NULL) { dw->speed = 0; dw->time_remaining = -1; nsgtk_download_change_sensitivity(dw, NSGTK_DOWNLOAD_CLEAR); nsgtk_download_change_status(dw, NSGTK_DOWNLOAD_ERROR); nsgtk_download_update(TRUE); gtk_window_present(nsgtk_download_window); return NSERROR_SAVE_FAILED; } dw->size_downloaded += size; return NSERROR_OK; } void gui_download_window_error(struct gui_download_window *dw, const char *error_msg) { } void gui_download_window_done(struct gui_download_window *dw) { g_io_channel_shutdown(dw->write, TRUE, &dw->error); g_io_channel_unref(dw->write); dw->speed = 0; dw->time_remaining = -1; dw->progress = 100; dw->size_total = dw->size_downloaded; nsgtk_download_change_sensitivity(dw, NSGTK_DOWNLOAD_CLEAR); nsgtk_download_change_status(dw, NSGTK_DOWNLOAD_COMPLETE); if (nsoption_bool(downloads_clear)) nsgtk_download_store_clear_item(dw); else nsgtk_download_update(TRUE); }