From 2159c6ec96392bbde5f0749747eba109cc56861a Mon Sep 17 00:00:00 2001 From: LRN Date: Thu, 20 Dec 2012 08:11:59 +0000 Subject: Finish fixing #2621 Ask for overwrite confirmation in SaveAs dialog. Add a filenamechange button ("...") next to DownloadAs entry. This button calls the SaveAs dialog, but without anonymity and recursiveness widgets (they are hidden), and it does not initiate the download, just changes the names in download panel. Add vsize group for download panel widgets, they all have the same height now. SaveAs dialog, "Download" and "Download recursively" context menu items now use the same code that "Download!" button uses (start_download2, etc). Old code that they used to need is now removed. Note that this _requires_ selection to follow context menu (right-clicking on a search result MUST select that search result before popping up context menu). Fixed small bugs in get_suggested_filename_anonymity2 (proper initialization for anonymity and result). GNUNET_FS_GTK_open_change_download_name_dialog() can be used to change download name from other files (it's public, unlike direct operations on treestore). Fixed some comments. Currently selected search result is now remembered in current_selected_search_result variable. It is checked on every item update, and if selection did not change, then download panel is not re-populated (re-population would have destroyed any user changes to download panel contents; item updates are made several times per second). Don't trigger GNUNET_FS_GTK_search_treeview_cursor_changed when updated item is not currently selected. Omit the "Download recursively" context menu item for search results that are known to not to be directories. --- contrib/gnunet_fs_gtk_download_as_dialog.glade | 3 +- contrib/gnunet_fs_gtk_main_window.glade | 32 ++ src/fs/gnunet-fs-gtk_download-save-as.c | 107 ++++- src/fs/gnunet-fs-gtk_download-save-as.h | 22 +- src/fs/gnunet-fs-gtk_event-handler.c | 627 ++++++++++--------------- src/fs/gnunet-fs-gtk_event-handler.h | 4 +- 6 files changed, 393 insertions(+), 402 deletions(-) diff --git a/contrib/gnunet_fs_gtk_download_as_dialog.glade b/contrib/gnunet_fs_gtk_download_as_dialog.glade index 31587f41..2fa82b48 100644 --- a/contrib/gnunet_fs_gtk_download_as_dialog.glade +++ b/contrib/gnunet_fs_gtk_download_as_dialog.glade @@ -19,6 +19,7 @@ dialog save False + True False @@ -28,7 +29,7 @@ vertical 2 - + True diff --git a/contrib/gnunet_fs_gtk_main_window.glade b/contrib/gnunet_fs_gtk_main_window.glade index d2bc993f..6fc375cd 100644 --- a/contrib/gnunet_fs_gtk_main_window.glade +++ b/contrib/gnunet_fs_gtk_main_window.glade @@ -807,6 +807,24 @@ 1 + + + + False + 1 + True + True + False + False + + + + False + False + end + 2 + + False @@ -1163,6 +1181,20 @@ + + vertical + True + + + + + + + + + + + 9999 1 diff --git a/src/fs/gnunet-fs-gtk_download-save-as.c b/src/fs/gnunet-fs-gtk_download-save-as.c index 9d54763f..76022afa 100644 --- a/src/fs/gnunet-fs-gtk_download-save-as.c +++ b/src/fs/gnunet-fs-gtk_download-save-as.c @@ -55,6 +55,14 @@ struct DownloadAsDialogContext */ 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; }; @@ -100,9 +108,7 @@ GNUNET_FS_GTK_free_download_entry (struct DownloadEntry *de) /** - * The 'save_as' dialog is being deleted. Check which state we're in - * and either simply clean up or start the download with the - * respective options (by calling the respective continuation). + * The 'save_as' dialog is being deleted. Clean up. * * @param widget the dialog object * @param event the deletion event @@ -151,39 +157,50 @@ GNUNET_GTK_save_as_dialog_response_cb (GtkDialog * dialog, 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")); - 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"))); + 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_non_null (dlc->dirname); dlc->dirname = NULL; clean_up_download_as_context (dlc); - GNUNET_FS_GTK_download_context_start_download (de); } - -/** - * 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. - * - * @param de download context for the file/directory +/* 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. */ -void -GNUNET_FS_GTK_open_download_as_dialog (struct DownloadEntry *de) +static void +open_saveas_dialog (struct DownloadEntry *de, int download_directly) { struct DownloadAsDialogContext *dlc; GtkWidget *cb; + GtkWidget *hb; dlc = GNUNET_malloc (sizeof (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); @@ -201,10 +218,19 @@ GNUNET_FS_GTK_open_download_as_dialog (struct DownloadEntry *de) 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")); - if (GNUNET_YES == de->is_directory) - gtk_widget_set_sensitive (cb, TRUE); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cb), - de->is_recursive); + 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) @@ -240,6 +266,35 @@ GNUNET_FS_GTK_open_download_as_dialog (struct DownloadEntry *de) 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 diff --git a/src/fs/gnunet-fs-gtk_download-save-as.h b/src/fs/gnunet-fs-gtk_download-save-as.h index db0f8018..9dae7d1a 100644 --- a/src/fs/gnunet-fs-gtk_download-save-as.h +++ b/src/fs/gnunet-fs-gtk_download-save-as.h @@ -43,16 +43,28 @@ GNUNET_FS_GTK_download_context_start_download (struct DownloadEntry *de); /** - * Open the 'save as' dialog for a download. Calls - * 'GNUNET_FS_GTK_download_context_start_download' when the dialog is - * complete. Will release the 'dc' resources if the dialog is - * cancelled. + * 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 dc download context for the file/directory + * @param de download context for the file/directory */ void GNUNET_FS_GTK_open_download_as_dialog (struct DownloadEntry *de); +/** + * 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); + /** * Free resources associated with the given download entry. diff --git a/src/fs/gnunet-fs-gtk_event-handler.c b/src/fs/gnunet-fs-gtk_event-handler.c index 737dfca5..42780d4b 100644 --- a/src/fs/gnunet-fs-gtk_event-handler.c +++ b/src/fs/gnunet-fs-gtk_event-handler.c @@ -262,10 +262,15 @@ static struct SearchTab *uri_tab; static struct PublishTab *publish_tab; /** - * Special tab we use to store publishing operations. + * Currently displayed search tab */ static struct SearchTab *current_search_tab = NULL; +/** + * Currently selected row in a search tab. + */ +static GtkTreePath *current_selected_search_result = NULL; + /** * Animation to display while publishing. */ @@ -392,120 +397,6 @@ get_default_download_directory (char *buffer, size_t size) return buffer; } -/** - * Called recursively to build a suggested filename by prepending - * suggested names for its parent directories (if any). - * - * @param tm tree model this function gets the data from - * @param iter current position in the tree, for which we want a suggested filename - * @param top GNUNET_YES for the original call to this function, - * GNUNET_NO for recursive calls; used to decide if the - * current call is eligible to set 'anonymity' and 'local_parents' - * @param local_parents set to GNUNET_YES if all parents are directories, and are downloaded. - * @param anonymity set to the anonymity level of the closest ancestor download (if any); - * set to "-1" for uninitialized initially (will be left at -1 if - * no suitable parent download was found) - * @param filename_is_absolute set to GNUNET_YES if the suggestion is an absolute filename, - * GNUNET_NO for relative filenames (gotten from meta data) - * @return suggested filename, possibly NULL - */ -static char * -get_suggested_filename_anonymity (GtkTreeModel *tm, - GtkTreeIter *iter, - int top, - int *local_parents, - int *anonymity, - int *filename_is_absolute) -{ - char *result; - char *dirname; - char *local_filename; - char *filename; - int downloaded_anonymity; - int have_a_parent; - struct GNUNET_CONTAINER_MetaData *meta; - GtkTreeIter parent; - const char *basename; - char *dot; - - /* FIXME-BUG-MAYBE: this function is likely responsible for not always - suggesting the best filename... To be investigated some more... */ - gtk_tree_model_get (tm, iter, - SEARCH_TAB_MC_METADATA, &meta, - SEARCH_TAB_MC_DOWNLOADED_FILENAME, &local_filename, - SEARCH_TAB_MC_DOWNLOADED_ANONYMITY, - &downloaded_anonymity, - -1); - if (GNUNET_NO == top) - { - if (NULL != local_filename) - *local_parents = GNUNET_YES; - else - *local_parents = GNUNET_NO; - if ( (downloaded_anonymity != -1) && - (*anonymity == -1) ) - *anonymity = downloaded_anonymity; - } - if (gtk_tree_model_iter_parent (tm, &parent, iter)) - { - have_a_parent = GNUNET_YES; - dirname = get_suggested_filename_anonymity (tm, &parent, GNUNET_NO, - local_parents, anonymity, - filename_is_absolute); - } - else - { - have_a_parent = GNUNET_NO; - dirname = NULL; - } - if (local_filename == NULL) - { - filename = GNUNET_FS_meta_data_suggest_filename (meta); - } - else - { - /* This directory was downloaded as /foo/bar/baz/somedirname - * Hopefully, "somedirname" is actually "somedir.gnd" - * We need to strip the ".gnd" part to get "somedir", which is - * what we're going to use instead of suggested original filename - * Without the .gnd extension we're going to just use a copy - * of the directory file name - and that would fail. Sad. - */ - if ( (NULL == dirname) && (GNUNET_NO == have_a_parent)) - { - /* This is the root. Use absolute path. */ - basename = (const char *) local_filename; - *filename_is_absolute = GNUNET_YES; - } - else - { - basename = GNUNET_STRINGS_get_short_name (local_filename); - } - if ( (NULL != basename) && (strlen (basename) > 0) ) - { - filename = GNUNET_strdup (basename); - dot = strrchr (filename, '.'); - if (dot) - *dot = '\0'; - } - else - { - filename = GNUNET_FS_meta_data_suggest_filename (meta); - } - } - if ( (NULL != dirname) && (NULL != filename) ) - { - GNUNET_asprintf (&result, "%s%s%s", dirname, DIR_SEPARATOR_STR, filename); - GNUNET_free (filename); - GNUNET_free (dirname); - return result; - } - if (NULL != filename) - return filename; - return NULL; -} - - /** * finished_chain - non-NULL for top-level call (for the item we're about to download), NULL otherwise * function sets it to GNUNET_YES if the item we're about to download was, in fact, already downloaded once, and thus we provide a name for it, @@ -586,6 +477,9 @@ build_relative_name (GtkTreeModel *tm, gchar *bname = g_path_get_basename (filename); int chain_ok; char *dot_gnd; + /* FIXME: Use better checking (don't compare paths as strings, + * only verify directory inode is the same, or something). + */ #if WINDOWS /* Kind of stricmp() for utf-8 */ gchar *tmp = g_utf8_casefold (our_dirname, -1); @@ -631,6 +525,18 @@ build_relative_name (GtkTreeModel *tm, } +/** + * Builds a suggested filename by prepending + * suggested names for its parent directories (if any). + * + * @param tm tree model this function gets the data from + * @param iter current position in the tree, for which we want a suggested filename + * @param download_directory will receive a pointer to download directory. + * free it with GNUNET_free() when done. + * Will never be NULL on return (CWD will be used as a fallback). + * @param anonymity will receive suggested anonymity (or -1 if anonymity can't be suggested) + * @return suggested filename relative to download directory (free with GNUNET_free()), or NULL + */ static char * get_suggested_filename_anonymity2 (GtkTreeModel *tm, GtkTreeIter *iter, @@ -642,7 +548,7 @@ get_suggested_filename_anonymity2 (GtkTreeModel *tm, char *relname; char *filename; char *tmp; - int downloaded_anonymity; + int downloaded_anonymity = -1; struct GNUNET_CONTAINER_MetaData *meta; size_t tmplen; int finished_chain; @@ -697,148 +603,16 @@ get_suggested_filename_anonymity2 (GtkTreeModel *tm, result = GNUNET_strdup (tmp); } } + else + result = NULL; *download_directory = GNUNET_strdup (downloaddir); + *anonymity = downloaded_anonymity; GNUNET_free (filename); g_free (downloaddir); g_free (relname); return result; } -/** - * This function is called when the user double-clicks on a search - * result. Begins the download, if necessary by opening the "save as" - * window. - * - * @param tree_view tree view with the details - * @param path path selecting which entry we want to download - * @param tab the search tab where the user triggered the download request - * @param is_recursive was the request for a recursive download? - * @param save_as force opening the 'save as' dialog? - */ -static void -start_download (GtkTreeView *tree_view, - GtkTreePath *path, - struct SearchTab *tab, - int is_recursive, - int save_as) -{ - GtkTreeModel *tm; - GtkTreeIter iter; - struct GNUNET_FS_Uri *uri; - struct GNUNET_CONTAINER_MetaData *meta; - struct SearchResult *sr; - struct DownloadEntry *de; - char *buf = NULL; - char *tmp; - size_t tmplen; - char cwd[FILENAME_MAX]; - char *download_directory; - char *filename; - int local_parents; - int have_a_suggestion; - int anonymity; - int filename_is_absolute; - - tm = gtk_tree_view_get_model (tree_view); - if (TRUE != gtk_tree_model_get_iter (tm, &iter, path)) - { - GNUNET_break (0); - return; - } - gtk_tree_model_get (tm, &iter, - SEARCH_TAB_MC_METADATA, &meta, - SEARCH_TAB_MC_URI, &uri, - SEARCH_TAB_MC_SEARCH_RESULT, &sr, - -1); - if (NULL == uri) - { - /* user clicked on directory that was opened (not downloaded!), so we - have no URI and downloading makes no sense. Ignore! */ - return; - } - if (NULL != sr->download) - { - /* download already active! */ - return; - } - if (!(GNUNET_FS_uri_test_chk (uri) || GNUNET_FS_uri_test_loc (uri))) - { - /* can only download chk/loc URIs, ignore */ - /* break because in this case, we should not have even gotten here */ - GNUNET_break (0); - return; - } - - download_directory = get_default_download_directory (cwd, sizeof (cwd)); - /* If no download directory is known, try working directory */ - if (download_directory == NULL) - download_directory = getcwd (cwd, sizeof (cwd)); - /* Calculate suggested filename */ - local_parents = GNUNET_NO; - anonymity = -1; - filename_is_absolute = GNUNET_NO; - filename = get_suggested_filename_anonymity (tm, &iter, GNUNET_YES, - &local_parents, &anonymity, - &filename_is_absolute); - have_a_suggestion = GNUNET_NO; - if (NULL != download_directory) - { - if (NULL == filename) - { - buf = GNUNET_strdup (download_directory); - } - else - { - have_a_suggestion = GNUNET_YES; - if (filename_is_absolute) - GNUNET_asprintf (&tmp, "%s", filename); - else - GNUNET_asprintf (&tmp, "%s%s%s", - download_directory, - DIR_SEPARATOR_STR, - filename); - tmplen = strlen (tmp); - /* now, if we have a directory, replace trailing '/' with ".gnd" */ - if (GNUNET_YES == - GNUNET_FS_meta_data_test_for_directory (meta)) - { - if ( (tmp[tmplen-1] == '/') || - (tmp[tmplen-1] == '\\') ) - tmp[tmplen-1] = '\0'; - GNUNET_asprintf (&buf, - "%s%s", - tmp, - GNUNET_FS_DIRECTORY_EXT); - GNUNET_free (tmp); - } - else - { - buf = tmp; - } - } - } - GNUNET_free_non_null (filename); - - /* now setup everything for the save-as dialog */ - de = GNUNET_malloc (sizeof (struct DownloadEntry)); - de->uri = GNUNET_FS_uri_dup (uri); - de->filename = buf; - de->sr = sr; - sr->download = de; - de->anonymity = anonymity; - de->is_recursive = is_recursive; - de->is_directory = GNUNET_FS_meta_data_test_for_directory (meta); - if (save_as) - have_a_suggestion = GNUNET_NO; - if ( (GNUNET_YES == local_parents) && - (GNUNET_YES == have_a_suggestion) ) - /* Skip the dialog, call directly */ - GNUNET_FS_GTK_download_context_start_download (de); - else - GNUNET_FS_GTK_open_download_as_dialog (de); -} - - /** * Context for the search list popup menu. */ @@ -879,79 +653,6 @@ search_list_popup_selection_done (GtkMenuShell *menushell, GNUNET_free (spc); } - -/** - * This function is called when the user double-clicks on a search - * result. Begins the download, if necessary by opening the "save as" - * window. - * - * @param tree_view tree view with the details - * @param path path selecting which entry we want to download - * @param column unused entry specifying which column the mouse was in - * @param user_data the 'struct SearchTab' that was activated - */ -void -GNUNET_FS_GTK_search_treeview_row_activated (GtkTreeView * tree_view, - GtkTreePath * path, - GtkTreeViewColumn * column, - gpointer user_data) -{ - struct SearchTab *tab = user_data; - struct GNUNET_FS_Uri *uri; - GtkTreeModel *tm; - GtkTreeIter iter; - - tm = gtk_tree_view_get_model (tree_view); - if (TRUE != gtk_tree_model_get_iter (tm, &iter, path)) - { - GNUNET_break (0); - return; - } - gtk_tree_model_get (tm, &iter, - SEARCH_TAB_MC_URI, &uri, -1); - if (NULL == uri) - { - /* user clicked on directory that was opened (not downloaded!), so we - have no URI and downloading makes no sense. Ignore! */ - return; - } - if (GNUNET_FS_uri_test_ksk (uri) || - GNUNET_FS_uri_test_sks (uri)) - { - GNUNET_FS_GTK_handle_uri (uri, 1); - return; - } - /* must be chk/loc URI, start download */ - start_download (tree_view, path, tab, GNUNET_NO, GNUNET_NO); -} - - -/** - * "Download" was selected in the current search context menu. - * - * @param spc the 'struct SearchListPopupContext' of the menu - * @param is_recursive was this the 'recursive' option? - * @parma save_as was this the 'save as' option? - */ -static void -start_download_ctx_menu_helper (struct SearchListPopupContext *spc, - int is_recursive, - int save_as) -{ - GtkTreePath *path; - GtkTreeView *tv; - - path = gtk_tree_row_reference_get_path (spc->rr); - tv = GTK_TREE_VIEW (gtk_builder_get_object - (spc->tab->builder, - "_search_result_frame")); - start_download (tv, path, spc->tab, - is_recursive, - save_as); - gtk_tree_path_free (path); -} - - /** * Selected row has changed in search result tree view, update preview * and metadata areas. @@ -964,8 +665,14 @@ GNUNET_FS_GTK_search_treeview_cursor_changed (GtkTreeView *tv, gpointer user_data); +/** + * save_as - GNUNET_YES to open SaveAs dialog, GNUNET_NO to start downloading. + * download_directly - GNUNET_YES to make SaveAs dialog initiate the download, + * GNUNET_NO to only change names on the download panel. + * Ingored if save_as is GNUNET_NO. + */ static void -start_download2 () +start_download2 (int save_as, int download_directly) { struct GNUNET_GTK_MainWindowContext *mctx = GNUNET_FS_GTK_get_main_context (); struct SearchTab *st = GNUNET_FS_GTK_get_current_search_tab (); @@ -998,6 +705,16 @@ start_download2 () SEARCH_TAB_MC_SEARCH_RESULT, &sr, -1); + if (uri == NULL) + return; + + if (GNUNET_FS_uri_test_ksk (uri) || + GNUNET_FS_uri_test_sks (uri)) + { + GNUNET_FS_GTK_handle_uri (uri, 1); + return; + } + if (!((NULL == sr->download) && (NULL != uri) && ((GNUNET_FS_uri_test_chk (uri) || GNUNET_FS_uri_test_loc (uri))))) return; @@ -1031,14 +748,56 @@ start_download2 () de->is_recursive = recursive; de->is_directory = GNUNET_FS_meta_data_test_for_directory (meta); - GNUNET_FS_GTK_download_context_start_download (de); + if (save_as == GNUNET_NO) + GNUNET_FS_GTK_download_context_start_download (de); + else if (download_directly == GNUNET_YES) + GNUNET_FS_GTK_open_download_as_dialog (de); + else + GNUNET_FS_GTK_open_change_download_name_dialog (de); gtk_tree_path_free (path); g_free (downloaddir); - if (GNUNET_GTK_tree_model_get_next_flat_iter (model, &iter, !recursive, &next_item)) - gtk_tree_selection_select_iter (sel, &next_item); - GNUNET_FS_GTK_search_treeview_cursor_changed (tv, st); + if (!save_as) + { + if (GNUNET_GTK_tree_model_get_next_flat_iter (model, &iter, !recursive, &next_item)) + gtk_tree_selection_select_iter (sel, &next_item); + GNUNET_FS_GTK_search_treeview_cursor_changed (tv, st); + } +} + +/** + * "Download" was selected in the current search context menu. + * + * @param spc the 'struct SearchListPopupContext' of the menu + * @param is_recursive was this the 'recursive' option? + * @parma save_as was this the 'save as' option? + */ +static void +start_download_ctx_menu_helper (struct SearchListPopupContext *spc, + int is_recursive, + int save_as) +{ + start_download2 (save_as, GNUNET_YES); +} + +/** + * This function is called when the user double-clicks on a search + * result. Begins the download, if necessary by opening the "save as" + * window. + * + * @param tree_view tree view with the details + * @param path path selecting which entry we want to download + * @param column unused entry specifying which column the mouse was in + * @param user_data the 'struct SearchTab' that was activated + */ +void +GNUNET_FS_GTK_search_treeview_row_activated (GtkTreeView * tree_view, + GtkTreePath * path, + GtkTreeViewColumn * column, + gpointer user_data) +{ + start_download2 (GNUNET_NO, GNUNET_NO); } @@ -1052,7 +811,21 @@ void GNUNET_GTK_search_frame_download_download_button_clicked_cb ( GtkButton *button, gpointer user_data) { - start_download2 (); + start_download2 (GNUNET_NO, GNUNET_NO); +} + +/** + * User clicked on "..." button at the download options panel, next + * to the Download As entry. + * + * @param button the "..." button + * @param user_data the main window context + */ +void +GNUNET_GTK_search_frame_download_filename_change_button_clicked_cb ( + GtkButton *button, gpointer user_data) +{ + start_download2 (GNUNET_YES, GNUNET_NO); } @@ -1093,10 +866,9 @@ start_download_recursively_ctx_menu (GtkMenuItem *item, gpointer user_data) * @param user_data the 'struct SearchListPopupContext' of the menu */ static void -start_download_as_ctx_menu (GtkMenuItem *item, gpointer user_data) +download_as_ctx_menu (GtkMenuItem *item, gpointer user_data) { struct SearchListPopupContext *spc = user_data; - start_download_ctx_menu_helper (spc, GNUNET_NO, GNUNET_YES); } @@ -1112,9 +884,12 @@ abort_download_ctx_menu (GtkMenuItem *item, gpointer user_data) { struct SearchListPopupContext *spc = user_data; struct DownloadEntry *de = spc->sr->download; + GtkTreeView *tv; GNUNET_assert (de->dc != NULL); GNUNET_FS_download_stop (de->dc, GNUNET_YES); + tv = GTK_TREE_VIEW (gtk_builder_get_object (spc->tab->builder, "_search_result_frame")); + GNUNET_FS_GTK_search_treeview_cursor_changed (tv, spc->tab); } @@ -1187,6 +962,8 @@ search_list_popup (GtkTreeModel *tm, struct SearchResult *sr; struct GNUNET_FS_Uri *uri; struct SearchListPopupContext *spc; + struct GNUNET_CONTAINER_MetaData *meta; + gboolean is_directory = FALSE; spc = GNUNET_malloc (sizeof (struct SearchListPopupContext)); spc->tab = tab; @@ -1195,8 +972,12 @@ search_list_popup (GtkTreeModel *tm, gtk_tree_path_free (path); gtk_tree_model_get (tm, iter, SEARCH_TAB_MC_URI, &uri, + SEARCH_TAB_MC_METADATA, &meta, SEARCH_TAB_MC_SEARCH_RESULT, &sr, -1); + if (meta != NULL) + is_directory = GNUNET_FS_meta_data_test_for_directory (meta); + spc->sr = sr; menu = GTK_MENU (gtk_menu_new ()); if ( (NULL == sr->download) && @@ -1212,19 +993,22 @@ search_list_popup (GtkTreeModel *tm, TRUE); gtk_widget_show (child); gtk_menu_shell_append (GTK_MENU_SHELL (menu), child); - - child = gtk_menu_item_new_with_label (_("Download _recursively")); - g_signal_connect (child, "activate", - G_CALLBACK (start_download_recursively_ctx_menu), spc); - gtk_label_set_use_underline (GTK_LABEL - (gtk_bin_get_child (GTK_BIN (child))), - TRUE); - gtk_widget_show (child); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), child); + + if (is_directory) + { + child = gtk_menu_item_new_with_label (_("Download _recursively")); + g_signal_connect (child, "activate", + G_CALLBACK (start_download_recursively_ctx_menu), spc); + gtk_label_set_use_underline (GTK_LABEL + (gtk_bin_get_child (GTK_BIN (child))), + TRUE); + gtk_widget_show (child); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), child); + } child = gtk_menu_item_new_with_label (_("Download _as...")); g_signal_connect (child, "activate", - G_CALLBACK (start_download_as_ctx_menu), spc); + G_CALLBACK (download_as_ctx_menu), spc); gtk_label_set_use_underline (GTK_LABEL (gtk_bin_get_child (GTK_BIN (child))), TRUE); @@ -1773,6 +1557,7 @@ GNUNET_FS_GTK_search_treeview_cursor_changed (GtkTreeView *tv, GtkTreeIter iter; struct GNUNET_CONTAINER_MetaData *meta; GdkPixbuf *pixbuf; + GtkTreePath *selpath; struct SearchResult *sr; struct GNUNET_FS_Uri *uri; struct GNUNET_GTK_MainWindowContext *mctx = GNUNET_FS_GTK_get_main_context (); @@ -1785,6 +1570,9 @@ GNUNET_FS_GTK_search_treeview_cursor_changed (GtkTreeView *tv, /* nothing selected, clear preview */ gtk_image_clear (mctx->preview_image); gtk_widget_hide (GTK_WIDGET (mctx->download_panel)); + if (current_selected_search_result != NULL) + gtk_tree_path_free (current_selected_search_result); + current_selected_search_result = NULL; return; } meta = NULL; @@ -1797,38 +1585,48 @@ GNUNET_FS_GTK_search_treeview_cursor_changed (GtkTreeView *tv, SEARCH_TAB_MC_SEARCH_RESULT, &sr, -1); - if ((NULL == sr->download) && (NULL != uri) && - ((GNUNET_FS_uri_test_chk (uri) || GNUNET_FS_uri_test_loc (uri)))) + selpath = gtk_tree_model_get_path (model, &iter); + if (current_selected_search_result == NULL || gtk_tree_path_compare (selpath, current_selected_search_result) != 0) { - char *download_directory; - char *filename; - int anonymity; - int is_directory; + if ((NULL == sr->download) && (NULL != uri) && + ((GNUNET_FS_uri_test_chk (uri) || GNUNET_FS_uri_test_loc (uri)))) + { + char *download_directory; + char *filename; + int anonymity; + int is_directory; - /* Calculate suggested filename */ - anonymity = -1; - download_directory = NULL; - filename = get_suggested_filename_anonymity2 (model, &iter, - &download_directory, &anonymity); + /* Calculate suggested filename */ + anonymity = -1; + download_directory = NULL; + filename = get_suggested_filename_anonymity2 (model, &iter, + &download_directory, &anonymity); - is_directory = GNUNET_FS_meta_data_test_for_directory (meta); - gtk_widget_set_sensitive (GTK_WIDGET (mctx->download_recursive_checkbutton), is_directory); + is_directory = GNUNET_FS_meta_data_test_for_directory (meta); + gtk_widget_set_sensitive (GTK_WIDGET (mctx->download_recursive_checkbutton), is_directory); - /* TODO: make this configurable */ - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mctx->download_recursive_checkbutton), is_directory); + /* TODO: make this configurable */ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mctx->download_recursive_checkbutton), is_directory); - /* TODO: make this configurable */ - GNUNET_GTK_select_anonymity_combo_level (mctx->download_anonymity_combo, anonymity >= 0 ? anonymity : 1); + /* TODO: make this configurable */ + GNUNET_GTK_select_anonymity_combo_level (mctx->download_anonymity_combo, anonymity >= 0 ? anonymity : 1); - gtk_entry_set_text (mctx->download_name_entry, filename != NULL ? filename : NULL); - gtk_file_chooser_set_current_folder (mctx->download_location_chooser, download_directory); + gtk_entry_set_text (mctx->download_name_entry, filename != NULL ? filename : NULL); + gtk_file_chooser_set_current_folder (mctx->download_location_chooser, download_directory); - gtk_widget_show_all (GTK_WIDGET (mctx->download_panel)); - GNUNET_free (filename); - GNUNET_free (download_directory); + gtk_widget_show_all (GTK_WIDGET (mctx->download_panel)); + GNUNET_free_non_null (filename); + GNUNET_free (download_directory); + } + else + gtk_widget_hide (GTK_WIDGET (mctx->download_panel)); + if (current_selected_search_result != NULL) + gtk_tree_path_free (current_selected_search_result); + current_selected_search_result = selpath; } else - gtk_widget_hide (GTK_WIDGET (mctx->download_panel)); + gtk_tree_path_free (selpath); + if (NULL != pixbuf) { @@ -1880,6 +1678,9 @@ GNUNET_GTK_main_window_notebook_switch_page_cb (GtkWidget * dummy, clear meta data and preview widgets */ gtk_image_clear (mctx->preview_image); gtk_list_store_clear (mctx->md_liststore); + if (current_selected_search_result != NULL) + gtk_tree_path_free (current_selected_search_result); + current_selected_search_result = NULL; } @@ -2122,7 +1923,6 @@ update_search_result (struct SearchResult *sr, GNUNET_break (0); return; } - gtk_tree_path_free (tp); desc = GNUNET_FS_GTK_get_description_from_metadata (meta, &desc_is_a_dup); mime = get_mimetype_from_metadata (meta); pixbuf = GNUNET_FS_GTK_get_thumbnail_from_meta_data (meta); @@ -2157,10 +1957,20 @@ update_search_result (struct SearchResult *sr, page = gtk_notebook_get_current_page (mctx->notebook); if (gtk_notebook_get_nth_page (mctx->notebook, page) == sr->tab->frame) { - tv = GTK_TREE_VIEW (gtk_builder_get_object - (sr->tab->builder, "_search_result_frame")); - GNUNET_FS_GTK_search_treeview_cursor_changed (tv, sr->tab); + GtkTreeSelection *sel; + GtkTreeModel *model; + GtkTreeIter iter; + tv = GTK_TREE_VIEW (gtk_builder_get_object (sr->tab->builder, "_search_result_frame")); + sel = gtk_tree_view_get_selection (tv); + if (gtk_tree_selection_get_selected (sel, &model, &iter)) + { + GtkTreePath *selpath = gtk_tree_model_get_path (model, &iter); + if (gtk_tree_path_compare (selpath, tp) == 0) + GNUNET_FS_GTK_search_treeview_cursor_changed (tv, sr->tab); + gtk_tree_path_free (selpath); + } } + gtk_tree_path_free (tp); } @@ -2312,6 +2122,85 @@ GNUNET_GTK_add_search_result (struct SearchTab *tab, } +/** + * Sets downloaded name on an item referenced by @rr + * in a tree store @ts to @filename. + * Used by SaveAs dialog to communicate back new filename + * (unless SaveAs dialog initiates the download by itself). + * Arguments can be taken from DownloadEntry. + * + * @param ts treestore + * @param rr row reference + * @param filename new filename + */ +void +GNUNET_FS_GTK_set_item_downloaded_name (GtkTreeStore *ts, GtkTreeRowReference *rr, gchar *filename) +{ + struct GNUNET_GTK_MainWindowContext *mctx = GNUNET_FS_GTK_get_main_context (); + GtkTreeIter iter; + GtkTreePath *path; + + path = gtk_tree_row_reference_get_path (rr); + if (path) + { + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (ts), &iter, path)) + { + /* TODO: maybe create a new store slot for user-defined filenames? + * Also - maybe separate slots for downloaddir and relative filename? + */ + /* This code relies on download panel contents being re-populated every 0.2 seconds, + * thus it updates the treestore item property, from which suggested filename + * is derived. + */ + /* + char *download_directory; + char *suggested_filename; + int anonymity = -1; + + gtk_tree_store_set (ts, &iter, SEARCH_TAB_MC_DOWNLOADED_FILENAME, filename, -1); + + download_directory = NULL; + suggested_filename = get_suggested_filename_anonymity2 (GTK_TREE_MODEL (ts), &iter, + &download_directory, &anonymity); + + gtk_entry_set_text (mctx->download_name_entry, suggested_filename != NULL ? suggested_filename : NULL); + gtk_file_chooser_set_current_folder (mctx->download_location_chooser, download_directory); + + GNUNET_free_non_null (suggested_filename); + GNUNET_free (download_directory); + */ + /* This code relies on download panel contents NOT being re-populated every 0.2 seconds, + * thus it only updates download panel contents - these changes will be lost after + * selecting a different item and then coming back to this one. + */ + gchar *current = g_strdup (filename); + gchar *dirname = NULL; + /* We take the filename user gave us, then check its parent directories until + * we find one that actually exists (SaveAs dialog might have some options about + * only picking existing names, but better be safe. + * gtk_file_chooser_set_current_folder() does NOT work with non-existing dirnames! + */ + do + { + dirname = g_path_get_dirname (current); + g_free (current); + if (g_file_test (dirname, G_FILE_TEST_EXISTS)) + { + gchar *relname = &filename[strlen (dirname)]; + while (relname[0] == '/' || relname[0] == '\\') + relname++; + gtk_entry_set_text (mctx->download_name_entry, relname); + gtk_file_chooser_set_current_folder (mctx->download_location_chooser, dirname); + break; + } + current = dirname; + } while (dirname[0] != '.'); /* FIXME: Check that this condition is correct */ + g_free (dirname); + } + gtk_tree_path_free (path); + } +} + /** * We have received a search result from the FS API. Add it to the * respective search tab. The search result can be an 'inner' diff --git a/src/fs/gnunet-fs-gtk_event-handler.h b/src/fs/gnunet-fs-gtk_event-handler.h index 472ae731..a7247c80 100644 --- a/src/fs/gnunet-fs-gtk_event-handler.h +++ b/src/fs/gnunet-fs-gtk_event-handler.h @@ -176,7 +176,6 @@ struct DownloadEntry * Has the download completed (or failed)? */ int is_done; - }; @@ -249,6 +248,9 @@ GNUNET_GTK_add_search_result (struct SearchTab *tab, uint32_t applicability_rank); +void +GNUNET_FS_GTK_set_item_downloaded_name (GtkTreeStore *ts, GtkTreeRowReference *rr, gchar *filename); + /** * Notification of FS to a client about the progress of an * operation. Callbacks of this type will be used for uploads, -- cgit v1.2.3