/* This file is part of GNUnet. Copyright (C) 2010, 2011, 2012 GNUnet e.V. GNUnet 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; either version 3, or (at your option) any later version. GNUnet 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 GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file src/fs/gnunet-fs-gtk_download-save-as.c * @brief functions for managing the 'save as' dialog * @author Christian Grothoff */ #include "gnunet-fs-gtk_download-save-as.h" #include "gnunet-fs-gtk.h" #include "gnunet-fs-gtk_event-handler.h" /** * State for a 'save as' dialog of a download. */ struct DownloadAsDialogContext { /** * Download entry for which this dialog determines the * filename (and other options). */ struct DownloadEntry *de; /** * Builder for the dialog. */ GtkBuilder *builder; /** * Main dialog object. */ GtkWidget *dialog; /** * Name of the directory that should be unlinked if the operation is aborted, * can be NULL. */ char *dirname; /** * Set to #GNUNET_YES if pressing "Save" button should initiate the download * right away, set to #GNUNET_NO if it should only change directory and file * names on the download panel. * Popup menu sets it to #GNUNET_YES, download panel "Change name" button sets * it to #GNUNET_NO. */ int download_directly; }; /** * Clean up the given DLC context, including possibly removing * the left-over (empty) directory. * * @param dlc context to clean up */ static void clean_up_download_as_context (struct DownloadAsDialogContext *dlc) { g_object_unref (G_OBJECT (dlc->builder)); if (NULL != dlc->dirname) { if (0 != rmdir (dlc->dirname)) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dlc->dirname); GNUNET_free (dlc->dirname); } GNUNET_free (dlc); } /** * Free resources associated with the given download entry. * * @param de context to free */ void GNUNET_FS_GTK_free_download_entry (struct DownloadEntry *de) { GtkTreePath *path; GtkTreeIter iter; GNUNET_assert (NULL == de->dc); if (NULL != de->sr) { GNUNET_assert (de->sr->download == de); de->sr->download = NULL; de->sr = NULL; } GNUNET_free (de->filename); GNUNET_FS_uri_destroy (de->uri); if (NULL != de->rr) { path = gtk_tree_row_reference_get_path (de->rr); GNUNET_assert ( gtk_tree_model_get_iter (GTK_TREE_MODEL (downloads_treestore), &iter, path)); gtk_tree_path_free (path); gtk_tree_row_reference_free (de->rr); de->rr = NULL; gtk_tree_store_remove (downloads_treestore, &iter); } GNUNET_free (de); } /** * The 'save_as' dialog is being deleted. Clean up. * * @param widget the dialog object * @param event the deletion event * @param user_data the 'structDownloadAsDialogContext' of the dialog * @return always FALSE (destroy the window) */ gboolean GNUNET_GTK_save_as_dialog_delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data) { struct DownloadAsDialogContext *dlc = user_data; GNUNET_FS_GTK_free_download_entry (dlc->de); clean_up_download_as_context (dlc); return FALSE; } /** * The user clicked on the 'save as' button of the dialog. * Delete the window and start the download. * * @param dialog the dialog object * @param response_id response_id associated with the button * @param user_data the 'structDownloadAsDialogContext' of the dialog */ void GNUNET_GTK_save_as_dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data) { struct DownloadAsDialogContext *dlc = user_data; struct DownloadEntry *de; GtkToggleButton *cb; de = dlc->de; if (GTK_RESPONSE_OK != response_id) { GNUNET_FS_GTK_free_download_entry (de); gtk_widget_destroy (GTK_WIDGET (dialog)); clean_up_download_as_context (dlc); return; } GNUNET_free (de->filename); de->filename = GNUNET_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dlc->dialog)); cb = GTK_TOGGLE_BUTTON ( gtk_builder_get_object (dlc->builder, "GNUNET_GTK_save_as_recursive_check_button")); if (dlc->download_directly) { de->is_recursive = (TRUE == gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (cb))) ? GNUNET_YES : GNUNET_NO; de->anonymity = (uint32_t) gtk_spin_button_get_value (GTK_SPIN_BUTTON ( gtk_builder_get_object (dlc->builder, "GNUNET_GTK_save_as_dialog_anonymity_spin_button"))); } gtk_widget_destroy (GTK_WIDGET (dialog)); if (dlc->download_directly) GNUNET_FS_GTK_download_context_start_download (de); else if (de->sr != NULL && de->sr->tab != NULL && de->sr->rr != NULL) { /* Update download panel */ GNUNET_FS_GTK_set_item_downloaded_name (de->sr->tab->ts, de->sr->rr, de->filename); GNUNET_FS_GTK_free_download_entry (de); } /* we were successful, do not remove the directory (if we created one) */ GNUNET_free (dlc->dirname); dlc->dirname = NULL; clean_up_download_as_context (dlc); } /* ** FIXME: implement handling for corner cases: * A) Selection changes in search tab while SaveAs dialog is open. * If it's in direct download mode, ignore that; otherwise destroy the dialog. * B) Download! is pressed in download panel (or the search result is otherwise * being downloaded without using saveas dialog) while SaveAs dialog is open. * Destroy SaveAs dialog. */ static void open_saveas_dialog (struct DownloadEntry *de, int download_directly) { struct DownloadAsDialogContext *dlc; GtkWidget *cb; GtkWidget *hb; dlc = GNUNET_new (struct DownloadAsDialogContext); dlc->de = de; dlc->download_directly = download_directly; dlc->builder = GNUNET_GTK_get_new_builder ("gnunet_fs_gtk_download_as_dialog.glade", dlc); if (NULL == dlc->builder) { GNUNET_break (0); GNUNET_FS_GTK_free_download_entry (de); GNUNET_free (dlc); return; } dlc->dialog = GTK_WIDGET ( gtk_builder_get_object (dlc->builder, "GNUNET_GTK_save_as_dialog")); /* Enable recursive button for directories and set recursive 'default' value based on what the 'dc' tells us */ cb = GTK_WIDGET ( gtk_builder_get_object (dlc->builder, "GNUNET_GTK_save_as_recursive_check_button")); hb = GTK_WIDGET ( gtk_builder_get_object (dlc->builder, "GNUNET_GTK_save_as_dialog_options_hbox")); if (download_directly) { if (GNUNET_YES == de->is_directory) gtk_widget_set_sensitive (cb, TRUE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cb), de->is_recursive); gtk_widget_show (hb); } else { gtk_widget_hide (hb); } /* initialize filename based on filename from 'dc' */ if (NULL != de->filename) { char *dirname; char *basename; struct stat sbuf; dirname = GNUNET_strdup (de->filename); basename = (char *) GNUNET_STRINGS_get_short_name (dirname); /* basename now points into 'dirname'; cut 'dirname' off at the '/' before * basename */ if (basename > dirname) basename[-1] = '\0'; if (0 != stat (dirname, &sbuf)) { if (GNUNET_OK != GNUNET_DISK_directory_create (dirname)) { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "mkdir", dirname); } else { dlc->dirname = dirname; } } gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dlc->dialog), dirname); gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dlc->dialog), basename); if (NULL == dlc->dirname) GNUNET_free (dirname); } gtk_dialog_set_default_response (GTK_DIALOG (dlc->dialog), GTK_RESPONSE_OK); gtk_window_present (GTK_WINDOW (dlc->dialog)); } /** * Open the 'save as' dialog for a download. Calls the 'dc->cb' * continutation when the dialog is complete. Will release the 'dc' * resources if the dialog is cancelled. * Pressing 'Save' button will initiate the download. * * @param de download context for the file/directory */ void GNUNET_FS_GTK_open_download_as_dialog (struct DownloadEntry *de) { open_saveas_dialog (de, GNUNET_YES); } /** * Open the 'save as' dialog for a download. Calls the 'dc->cb' * continutation when the dialog is complete. Will release the 'dc' * resources if the dialog is cancelled. * Pressing 'Save' button will change selected directory and * file name in download panel, but will not initiate the download. * * @param de download context for the file/directory */ void GNUNET_FS_GTK_open_change_download_name_dialog (struct DownloadEntry *de) { open_saveas_dialog (de, GNUNET_NO); } /** * Actually start the download that is specified by the given download * context and its options. Will add a download entry to the * respective tree model and trigger a start of the download using the * FS-API. * * @param de download context of the download to execute */ void GNUNET_FS_GTK_download_context_start_download (struct DownloadEntry *de) { enum GNUNET_FS_DownloadOptions opt; struct GNUNET_FS_Handle *fs; uint64_t len; fs = GNUNET_FS_GTK_get_fs_handle (); opt = GNUNET_FS_DOWNLOAD_OPTION_NONE; if (de->is_recursive) opt |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE; len = GNUNET_FS_uri_chk_get_file_size (de->uri); if ((NULL != de->sr) && (NULL != de->sr->result)) { GNUNET_break (NULL != GNUNET_FS_download_start_from_search (fs, de->sr->result, de->filename, NULL /* tempname */, 0 /* offset */, len, de->anonymity, opt, de)); } else { GNUNET_break ( NULL != GNUNET_FS_download_start (fs, de->uri, NULL /* meta data */, de->filename, NULL /* tempname */, 0 /* offset */, len, de->anonymity, opt, de, (NULL != de->pde) ? de->pde->dc : NULL)); } } /* end of gnunet-fs-gtk-download.c */