From 81059c35d56f277797183883f8ed2347fb0f53a8 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Thu, 10 Mar 2016 15:05:16 -0600 Subject: [PATCH] GtkFileChooserEntry: regenerate the completions if the dir_part changes Consider this bug: 1. Open a file chooser; switch it to $HOME 2. Start typing "~/Dow" with some file that *does* exist in your $HOME 3. Delete the inline-completion selection (e.g. the "nloads" after "~/Down"). 4. While you are at "~/Dow_" hit Tab. No completion will occur. This happens because of the following. Say the GtkFileChooserEntry is in the process of loading $HOME, because _set_base_folder() was called. If the entry contains no text, then the FULL_PATH_COLUMN of the file system model will be set to unprefixed filenames from $HOME, like .ssh/ Documents/ Downloads/ somefile.txt Later we avoid reloading the folder if g_file_equal(old_folder, new_folder). However, the FULL_PATH_COLUMN gets populated in completion_store_set() out of the actual filenames that GIO returned, plus the chooser_entry->dir_part. If the user starts typing "~/Dow" then dir_part changes to "~/", *but* the folder won't be reloaded since it is also $HOME. However, the completion machinery assumes that FULL_PATH_COLUMN will contain prefixed entries like ~/.ssh/ ~/Documents/ ~/Downloads/ ~/somefile.txt So, we add an invariant that chooser_entry->dir_part and chooser_entry->current_folder_file must change at the same time, and must not get out of sync: If any of them changes, then the completions are regenerated. --- gtk/gtkfilechooserentry.c | 48 ++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c index be310ee3d2..71c615eea4 100644 --- a/gtk/gtkfilechooserentry.c +++ b/gtk/gtkfilechooserentry.c @@ -108,7 +108,8 @@ static void set_complete_on_load (GtkFileChooserEntry *chooser_entry, gboolean complete_on_load); static void refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry); static void set_completion_folder (GtkFileChooserEntry *chooser_entry, - GFile *folder); + GFile *folder, + char *dir_part); static void finished_loading_cb (GtkFileSystemModel *model, GError *error, GtkFileChooserEntry *chooser_entry); @@ -248,7 +249,7 @@ gtk_file_chooser_entry_dispose (GObject *object) { GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (object); - set_completion_folder (chooser_entry, NULL); + set_completion_folder (chooser_entry, NULL, NULL); G_OBJECT_CLASS (_gtk_file_chooser_entry_parent_class)->dispose (object); } @@ -374,7 +375,7 @@ explicitly_complete (GtkFileChooserEntry *chooser_entry) { char *completion, *text; gsize completion_len, text_len; - + text = gtk_file_chooser_entry_get_completion_text (chooser_entry); text_len = strlen (text); completion = gtk_entry_completion_compute_prefix (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)), text); @@ -523,11 +524,11 @@ completion_store_set (GtkFileSystemModel *model, if (_gtk_file_info_consider_as_directory (info)) suffix = G_DIR_SEPARATOR_S; - g_value_take_string (value, g_strconcat ( - prefix, - g_file_info_get_display_name (info), - suffix, - NULL)); + g_value_take_string (value, + g_strconcat (prefix, + g_file_info_get_display_name (info), + suffix, + NULL)); break; default: g_assert_not_reached (); @@ -598,24 +599,31 @@ finished_loading_cb (GtkFileSystemModel *model, static void set_completion_folder (GtkFileChooserEntry *chooser_entry, - GFile *folder_file) + GFile *folder_file, + char *dir_part) { if (folder_file && chooser_entry->local_only && !_gtk_file_has_native_path (folder_file)) folder_file = NULL; - if ((chooser_entry->current_folder_file - && folder_file - && g_file_equal (folder_file, chooser_entry->current_folder_file)) - || chooser_entry->current_folder_file == folder_file) - return; + if (((chooser_entry->current_folder_file + && folder_file + && g_file_equal (folder_file, chooser_entry->current_folder_file)) + || chooser_entry->current_folder_file == folder_file) + && g_strcmp0 (dir_part, chooser_entry->dir_part) == 0) + { + return; + } if (chooser_entry->current_folder_file) { g_object_unref (chooser_entry->current_folder_file); chooser_entry->current_folder_file = NULL; } + + g_free (chooser_entry->dir_part); + chooser_entry->dir_part = g_strdup (dir_part); chooser_entry->current_folder_loaded = FALSE; @@ -633,29 +641,33 @@ refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry) { GFile *folder_file; char *text, *last_slash, *old_file_part; + char *dir_part; old_file_part = chooser_entry->file_part; - g_free (chooser_entry->dir_part); text = gtk_file_chooser_entry_get_completion_text (chooser_entry); last_slash = strrchr (text, G_DIR_SEPARATOR); if (last_slash) { - chooser_entry->dir_part = g_strndup (text, last_slash - text + 1); + dir_part = g_strndup (text, last_slash - text + 1); chooser_entry->file_part = g_strdup (last_slash + 1); } else { - chooser_entry->dir_part = g_strdup (""); + dir_part = g_strdup (""); chooser_entry->file_part = g_strdup (text); } folder_file = gtk_file_chooser_get_directory_for_text (chooser_entry, text); - set_completion_folder (chooser_entry, folder_file); + + set_completion_folder (chooser_entry, folder_file, dir_part); + if (folder_file) g_object_unref (folder_file); + g_free (dir_part); + if (chooser_entry->completion_store && (g_strcmp0 (old_file_part, chooser_entry->file_part) != 0)) {