From 92d45e12f896693b009c0bb30d95a667c66d4df4 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 | 44 +++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c index 142dc2b2cd..d6e20faa9b 100644 --- a/gtk/gtkfilechooserentry.c +++ b/gtk/gtkfilechooserentry.c @@ -103,7 +103,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); @@ -234,7 +235,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); } @@ -360,7 +361,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); @@ -502,11 +503,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 (); @@ -577,24 +578,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; @@ -612,26 +620,26 @@ 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);