diff --git a/gtk+-2.24.30.tar.xz b/gtk+-2.24.30.tar.xz deleted file mode 100644 index da8efc5..0000000 --- a/gtk+-2.24.30.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0d15cec3b6d55c60eac205b1f3ba81a1ed4eadd9d0f8e7c508bc7065d0c4ca50 -size 12800276 diff --git a/gtk+-2.24.31.tar.xz b/gtk+-2.24.31.tar.xz new file mode 100644 index 0000000..5cec76b --- /dev/null +++ b/gtk+-2.24.31.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:68c1922732c7efc08df4656a5366dcc3afdc8791513400dac276009b40954658 +size 12805344 diff --git a/gtk2-bgo737777-fix-printing-authentication-crash.patch b/gtk2-bgo737777-fix-printing-authentication-crash.patch deleted file mode 100644 index b3006b6..0000000 --- a/gtk2-bgo737777-fix-printing-authentication-crash.patch +++ /dev/null @@ -1,14 +0,0 @@ -@@ -, +, @@ - gtk/gtkprintbackend.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) ---- a/gtk/gtkprintbackend.c -+++ a/gtk/gtkprintbackend.c -@@ -756,7 +756,7 @@ request_password (GtkPrintBackend *backend, - - priv->auth_info_required = g_strdupv (ai_required); - length = g_strv_length (ai_required); -- priv->auth_info = g_new0 (gchar *, length); -+ priv->auth_info = g_new0 (gchar *, length + 1); - priv->store_auth_info = FALSE; - - dialog = gtk_dialog_new_with_buttons ( _("Authentication"), NULL, GTK_DIALOG_MODAL, diff --git a/gtk2-bgo743166-remember-printing-authentication.patch b/gtk2-bgo743166-remember-printing-authentication.patch index e9ed7e2..6b7c3ae 100644 --- a/gtk2-bgo743166-remember-printing-authentication.patch +++ b/gtk2-bgo743166-remember-printing-authentication.patch @@ -124,7 +124,7 @@ index c487d33..7e61ca4 100644 @@ -745,6 +760,7 @@ request_password (GtkPrintBackend *backend, priv->auth_info_required = g_strdupv (ai_required); length = g_strv_length (ai_required); - priv->auth_info = g_new0 (gchar *, length); + priv->auth_info = g_new0 (gchar *, length + 1); + priv->store_auth_info = FALSE; dialog = gtk_dialog_new_with_buttons ( _("Authentication"), NULL, GTK_DIALOG_MODAL, diff --git a/gtk2-bnc957400-filechooserentry-update.patch b/gtk2-bnc957400-filechooserentry-update.patch deleted file mode 100644 index 2f7a9fc..0000000 --- a/gtk2-bnc957400-filechooserentry-update.patch +++ /dev/null @@ -1,3514 +0,0 @@ -diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c -index 2fa7b56..08b74d8 100644 ---- a/gtk/gtkentrycompletion.c -+++ b/gtk/gtkentrycompletion.c -@@ -1571,19 +1571,17 @@ gtk_entry_completion_cursor_on_match (GtkEntryCompletion *completion, - return TRUE; - } - --static gchar * --gtk_entry_completion_compute_prefix (GtkEntryCompletion *completion) -+gchar * -+_gtk_entry_completion_compute_prefix (GtkEntryCompletion *completion, -+ const char *key) - { - GtkTreeIter iter; - gchar *prefix = NULL; - gboolean valid; -- const gchar *key; - - if (completion->priv->text_column < 0) - return NULL; - -- key = gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)); -- - valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (completion->priv->filter_model), - &iter); - -@@ -1753,7 +1751,8 @@ gtk_entry_completion_insert_prefix (GtkEntryCompletion *completion) - g_signal_handler_block (completion->priv->entry, - completion->priv->insert_text_id); - -- prefix = gtk_entry_completion_compute_prefix (completion); -+ prefix = _gtk_entry_completion_compute_prefix (completion, -+ gtk_entry_get_text (GTK_ENTRY (completion->priv->entry))); - if (prefix) - { - g_signal_emit (completion, entry_completion_signals[INSERT_PREFIX], -diff --git a/gtk/gtkentryprivate.h b/gtk/gtkentryprivate.h -index a767800..bdb41cb 100644 ---- a/gtk/gtkentryprivate.h -+++ b/gtk/gtkentryprivate.h -@@ -74,6 +74,8 @@ struct _GtkEntryCompletionPrivate - gboolean _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion); - void _gtk_entry_completion_popup (GtkEntryCompletion *completion); - void _gtk_entry_completion_popdown (GtkEntryCompletion *completion); -+gchar * _gtk_entry_completion_compute_prefix (GtkEntryCompletion *completion, -+ const char *key); - - void _gtk_entry_get_borders (GtkEntry *entry, - gint *xborder, -diff --git a/gtk/gtkfilechooser.c b/gtk/gtkfilechooser.c -index 8dd0dec..62dd01e 100644 ---- a/gtk/gtkfilechooser.c -+++ b/gtk/gtkfilechooser.c -@@ -1498,10 +1498,18 @@ gtk_file_chooser_get_uri (GtkFileChooser *chooser) - if (file) - { - if (gtk_file_chooser_get_local_only (chooser)) -- result = file_to_uri_with_native_path (file); -+ { -+ gchar *local = g_file_get_path (file); -+ if (local) -+ { -+ result = g_filename_to_uri (local, NULL, NULL); -+ g_free (local); -+ } -+ } - else -+ { - result = g_file_get_uri (file); -- -+ } - g_object_unref (file); - } - -diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c -index 038b2f0..db097d4 100644 ---- a/gtk/gtkfilechooserdefault.c -+++ b/gtk/gtkfilechooserdefault.c -@@ -205,6 +205,7 @@ enum { - MODEL_COL_FILE, - MODEL_COL_NAME_COLLATED, - MODEL_COL_IS_FOLDER, -+ MODEL_COL_IS_SENSITIVE, - MODEL_COL_PIXBUF, - MODEL_COL_SIZE_TEXT, - MODEL_COL_MTIME_TEXT, -@@ -221,6 +222,7 @@ enum { - G_TYPE_FILE, /* MODEL_COL_FILE */ \ - G_TYPE_STRING, /* MODEL_COL_NAME_COLLATED */ \ - G_TYPE_BOOLEAN, /* MODEL_COL_IS_FOLDER */ \ -+ G_TYPE_BOOLEAN, /* MODEL_COL_IS_SENSITIVE */ \ - GDK_TYPE_PIXBUF, /* MODEL_COL_PIXBUF */ \ - G_TYPE_STRING, /* MODEL_COL_SIZE_TEXT */ \ - G_TYPE_STRING, /* MODEL_COL_MTIME_TEXT */ \ -@@ -4426,8 +4428,6 @@ location_entry_create (GtkFileChooserDefault *impl) - if (!impl->location_entry) - impl->location_entry = _gtk_file_chooser_entry_new (TRUE); - -- _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), -- impl->file_system); - _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->local_only); - _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action); - gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45); -@@ -6665,6 +6665,7 @@ file_system_model_got_thumbnail (GObject *object, GAsyncResult *res, gpointer da - /* file was deleted */ - if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file)) - { -+ g_object_unref (queried); - GDK_THREADS_LEAVE (); - return; - } -@@ -6675,9 +6676,10 @@ file_system_model_got_thumbnail (GObject *object, GAsyncResult *res, gpointer da - copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED); - copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON); - -- _gtk_file_system_model_update_file (model, file, info, FALSE); -+ _gtk_file_system_model_update_file (model, file, info); - - g_object_unref (info); -+ g_object_unref (queried); - - GDK_THREADS_LEAVE (); - } -@@ -6712,6 +6714,33 @@ file_system_model_set (GtkFileSystemModel *model, - case MODEL_COL_IS_FOLDER: - g_value_set_boolean (value, info == NULL || _gtk_file_info_consider_as_directory (info)); - break; -+ case MODEL_COL_IS_SENSITIVE: -+ if (info) -+ { -+ gboolean sensitive = TRUE; -+ -+ if (impl->action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && -+ impl->action != GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) -+ { -+ sensitive = TRUE; -+ } -+ else if (!_gtk_file_info_consider_as_directory (info)) -+ { -+ sensitive = FALSE; -+ } -+ else -+ { -+ GtkTreeIter iter; -+ if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file)) -+ g_assert_not_reached (); -+ sensitive = !_gtk_file_system_model_iter_is_filtered_out (model, &iter); -+ } -+ -+ g_value_set_boolean (value, sensitive); -+ } -+ else -+ g_value_set_boolean (value, TRUE); -+ break; - case MODEL_COL_PIXBUF: - if (info) - { -@@ -6946,7 +6975,7 @@ update_chooser_entry (GtkFileChooserDefault *impl) - - if (change_entry) - { -- _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->browse_files_last_selected_name); -+ gtk_entry_set_text (GTK_ENTRY (impl->location_entry), impl->browse_files_last_selected_name); - - if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) - _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (impl->location_entry)); -@@ -6978,7 +7007,7 @@ update_chooser_entry (GtkFileChooserDefault *impl) - g_free (impl->browse_files_last_selected_name); - impl->browse_files_last_selected_name = NULL; - -- _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), ""); -+ gtk_entry_set_text (GTK_ENTRY (impl->location_entry), ""); - return; - } - -@@ -7013,7 +7042,7 @@ update_chooser_entry (GtkFileChooserDefault *impl) - clear_entry = FALSE; - - if (clear_entry) -- _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), ""); -+ gtk_entry_set_text (GTK_ENTRY (impl->location_entry), ""); - } - } - -@@ -7210,7 +7239,7 @@ update_current_folder_get_info_cb (GCancellable *cancellable, - impl->current_folder); - - if (data->clear_entry) -- _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), ""); -+ gtk_entry_set_text (GTK_ENTRY (impl->location_entry), ""); - } - - /* Create a new list model. This is slightly evil; we store the result value -@@ -7315,7 +7344,7 @@ gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser, - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER); - - pending_select_files_free (impl); -- _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), name); -+ gtk_entry_set_text (GTK_ENTRY (impl->location_entry), name); - } - - static gboolean -@@ -7401,16 +7430,19 @@ maybe_select (GtkTreeModel *model, - { - GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data); - GtkTreeSelection *selection; -+ gboolean is_sensitive; - gboolean is_folder; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view)); - - gtk_tree_model_get (model, iter, - MODEL_COL_IS_FOLDER, &is_folder, -+ MODEL_COL_IS_SENSITIVE, &is_sensitive, - -1); - -- if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) || -- (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)) -+ if (is_sensitive && -+ ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) || -+ (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN))) - gtk_tree_selection_select_iter (selection, iter); - else - gtk_tree_selection_unselect_iter (selection, iter); -@@ -7507,7 +7539,7 @@ check_save_entry (GtkFileChooserDefault *impl, - - if (!file_part || file_part[0] == '\0') - { -- *file_ret = g_object_ref (current_folder); -+ *file_ret = current_folder; - *is_well_formed_ret = TRUE; - *is_file_part_empty_ret = TRUE; - *is_folder = TRUE; -@@ -7519,6 +7551,7 @@ check_save_entry (GtkFileChooserDefault *impl, - - error = NULL; - file = g_file_get_child_for_display_name (current_folder, file_part, &error); -+ g_object_unref (current_folder); - - if (!file) - { -@@ -8818,7 +8851,7 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) - data = g_new0 (struct FileExistsData, 1); - data->impl = g_object_ref (impl); - data->file = g_object_ref (file); -- data->parent_file = g_object_ref (_gtk_file_chooser_entry_get_current_folder (entry)); -+ data->parent_file = _gtk_file_chooser_entry_get_current_folder (entry); - - if (impl->file_exists_get_info_cancellable) - g_cancellable_cancel (impl->file_exists_get_info_cancellable); -@@ -9780,7 +9813,7 @@ shortcuts_activate_iter (GtkFileChooserDefault *impl, - if (impl->location_entry - && !(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE - || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)) -- _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), ""); -+ gtk_entry_set_text (GTK_ENTRY (impl->location_entry), ""); - - gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter, - SHORTCUTS_COL_DATA, &col_data, -@@ -9918,14 +9951,16 @@ list_select_func (GtkTreeSelection *selection, - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) - { - GtkTreeIter iter; -+ gboolean is_sensitive; - gboolean is_folder; - - if (!gtk_tree_model_get_iter (model, &iter, path)) - return FALSE; - gtk_tree_model_get (model, &iter, -+ MODEL_COL_IS_SENSITIVE, &is_sensitive, - MODEL_COL_IS_FOLDER, &is_folder, - -1); -- if (!is_folder) -+ if (!is_sensitive || !is_folder) - return FALSE; - } - -@@ -9975,6 +10010,7 @@ list_row_activated (GtkTreeView *tree_view, - GtkTreeIter iter; - GtkTreeModel *model; - gboolean is_folder; -+ gboolean is_sensitive; - - model = gtk_tree_view_get_model (tree_view); - -@@ -9984,9 +10020,10 @@ list_row_activated (GtkTreeView *tree_view, - gtk_tree_model_get (model, &iter, - MODEL_COL_FILE, &file, - MODEL_COL_IS_FOLDER, &is_folder, -+ MODEL_COL_IS_SENSITIVE, &is_sensitive, - -1); - -- if (is_folder && file) -+ if (is_sensitive && is_folder && file) - { - change_folder_and_display_error (impl, file, FALSE); - return; -@@ -10027,10 +10064,6 @@ update_cell_renderer_attributes (GtkFileChooserDefault *impl) - GtkTreeViewColumn *column; - GtkCellRenderer *renderer; - GList *walk, *list; -- gboolean always_sensitive; -- -- always_sensitive = impl->action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && -- impl->action != GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; - - /* Keep the following column numbers in sync with create_file_list() */ - -@@ -10053,10 +10086,8 @@ update_cell_renderer_attributes (GtkFileChooserDefault *impl) - "ellipsize", MODEL_COL_ELLIPSIZE, - NULL); - } -- if (always_sensitive) -- g_object_set (renderer, "sensitive", TRUE, NULL); -- else -- gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER); -+ -+ gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_SENSITIVE); - } - g_list_free (list); - -@@ -10067,10 +10098,8 @@ update_cell_renderer_attributes (GtkFileChooserDefault *impl) - gtk_tree_view_column_set_attributes (column, renderer, - "text", MODEL_COL_SIZE_TEXT, - NULL); -- if (always_sensitive) -- g_object_set (renderer, "sensitive", TRUE, NULL); -- else -- gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER); -+ -+ gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_SENSITIVE); - g_list_free (list); - - /* mtime */ -@@ -10080,10 +10109,7 @@ update_cell_renderer_attributes (GtkFileChooserDefault *impl) - gtk_tree_view_column_set_attributes (column, renderer, - "text", MODEL_COL_MTIME_TEXT, - NULL); -- if (always_sensitive) -- g_object_set (renderer, "sensitive", TRUE, NULL); -- else -- gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER); -+ gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_SENSITIVE); - g_list_free (list); - } - -@@ -10097,7 +10123,7 @@ static void - location_set_user_text (GtkFileChooserDefault *impl, - const gchar *path) - { -- _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), path); -+ gtk_entry_set_text (GTK_ENTRY (impl->location_entry), path); - gtk_editable_set_position (GTK_EDITABLE (impl->location_entry), -1); - } - -diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c -index 3caa7b8..cac29ba 100644 ---- a/gtk/gtkfilechooserentry.c -+++ b/gtk/gtkfilechooserentry.c -@@ -19,13 +19,16 @@ - */ - - #include "config.h" -+ -+#include "gtkfilechooserentry.h" -+ - #include - - #include "gtkalignment.h" - #include "gtkcelllayout.h" - #include "gtkcellrenderertext.h" --#include "gtkentry.h" --#include "gtkfilechooserentry.h" -+#include "gtkentryprivate.h" -+#include "gtkfilesystemmodel.h" - #include "gtklabel.h" - #include "gtkmain.h" - #include "gtkwindow.h" -@@ -45,50 +48,21 @@ struct _GtkFileChooserEntryClass - GtkEntryClass parent_class; - }; - --/* Action to take when the current folder finishes loading (for explicit or automatic completion) */ --typedef enum { -- LOAD_COMPLETE_NOTHING, -- LOAD_COMPLETE_AUTOCOMPLETE, -- LOAD_COMPLETE_EXPLICIT_COMPLETION --} LoadCompleteAction; -- --typedef enum --{ -- REFRESH_OK, -- REFRESH_INVALID_INPUT, -- REFRESH_INCOMPLETE_HOSTNAME, -- REFRESH_NONEXISTENT, -- REFRESH_NOT_LOCAL --} RefreshStatus; -- - struct _GtkFileChooserEntry - { - GtkEntry parent_instance; - - GtkFileChooserAction action; - -- GtkFileSystem *file_system; - GFile *base_folder; - GFile *current_folder_file; -+ gchar *dir_part; - gchar *file_part; -- gint file_part_pos; - -- /* Folder being loaded or already loaded */ -- GtkFolder *current_folder; -- GCancellable *load_folder_cancellable; -+ GtkTreeModel *completion_store; - -- LoadCompleteAction load_complete_action; -- -- GtkListStore *completion_store; -- -- guint start_autocompletion_idle_id; -- -- GtkWidget *completion_feedback_window; -- GtkWidget *completion_feedback_label; -- guint completion_feedback_timeout_id; -- -- guint has_completion : 1; -- guint in_change : 1; -+ guint current_folder_loaded : 1; -+ guint complete_on_load : 1; - guint eat_tabs : 1; - guint local_only : 1; - }; -@@ -96,35 +70,17 @@ struct _GtkFileChooserEntry - enum - { - DISPLAY_NAME_COLUMN, -- FILE_COLUMN, -+ FULL_PATH_COLUMN, - N_COLUMNS - }; - --#define COMPLETION_FEEDBACK_TIMEOUT_MS 2000 -- --static void gtk_file_chooser_entry_iface_init (GtkEditableClass *iface); -- - static void gtk_file_chooser_entry_finalize (GObject *object); - static void gtk_file_chooser_entry_dispose (GObject *object); - static void gtk_file_chooser_entry_grab_focus (GtkWidget *widget); --static void gtk_file_chooser_entry_unmap (GtkWidget *widget); --static gboolean gtk_file_chooser_entry_key_press_event (GtkWidget *widget, -- GdkEventKey *event); -+static gboolean gtk_file_chooser_entry_tab_handler (GtkWidget *widget, -+ GdkEventKey *event); - static gboolean gtk_file_chooser_entry_focus_out_event (GtkWidget *widget, - GdkEventFocus *event); --static void gtk_file_chooser_entry_activate (GtkEntry *entry); --static void gtk_file_chooser_entry_do_insert_text (GtkEditable *editable, -- const gchar *new_text, -- gint new_text_length, -- gint *position); --static void gtk_file_chooser_entry_do_delete_text (GtkEditable *editable, -- gint start_pos, -- gint end_pos); --static void gtk_file_chooser_entry_set_position (GtkEditable *editable, -- gint position); --static void gtk_file_chooser_entry_set_selection_bounds (GtkEditable *editable, -- gint start_pos, -- gint end_pos); - - #ifdef G_OS_WIN32 - static gint insert_text_callback (GtkFileChooserEntry *widget, -@@ -142,63 +98,71 @@ static gboolean match_selected_callback (GtkEntryCompletion *completion, - GtkTreeModel *model, - GtkTreeIter *iter, - GtkFileChooserEntry *chooser_entry); --static gboolean completion_match_func (GtkEntryCompletion *comp, -- const char *key, -- GtkTreeIter *iter, -- gpointer data); --static char *maybe_append_separator_to_file (GtkFileChooserEntry *chooser_entry, -- GFile *file, -- gchar *display_name, -- gboolean *appended); -- --typedef enum { -- REFRESH_UP_TO_CURSOR_POSITION, -- REFRESH_WHOLE_TEXT --} RefreshMode; -- --static RefreshStatus refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry, -- RefreshMode refresh_mode); --static void finished_loading_cb (GtkFolder *folder, -- gpointer data); --static void autocomplete (GtkFileChooserEntry *chooser_entry); --static void install_start_autocompletion_idle (GtkFileChooserEntry *chooser_entry); --static void remove_completion_feedback (GtkFileChooserEntry *chooser_entry); --static void pop_up_completion_feedback (GtkFileChooserEntry *chooser_entry, -- const gchar *feedback); -- --static GtkEditableClass *parent_editable_iface; -- --G_DEFINE_TYPE_WITH_CODE (GtkFileChooserEntry, _gtk_file_chooser_entry, GTK_TYPE_ENTRY, -- G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, -- gtk_file_chooser_entry_iface_init)) -+ -+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, -+ char *dir_part); -+static void finished_loading_cb (GtkFileSystemModel *model, -+ GError *error, -+ GtkFileChooserEntry *chooser_entry); -+ -+G_DEFINE_TYPE (GtkFileChooserEntry, _gtk_file_chooser_entry, GTK_TYPE_ENTRY) -+ -+static char * -+gtk_file_chooser_entry_get_completion_text (GtkFileChooserEntry *chooser_entry) -+{ -+ GtkEditable *editable = GTK_EDITABLE (chooser_entry); -+ int start, end; -+ -+ gtk_editable_get_selection_bounds (editable, &start, &end); -+ return gtk_editable_get_chars (editable, 0, MIN (start, end)); -+} -+ -+static void -+gtk_file_chooser_entry_dispatch_properties_changed (GObject *object, -+ guint n_pspecs, -+ GParamSpec **pspecs) -+{ -+ GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (object); -+ guint i; -+ -+ G_OBJECT_CLASS (_gtk_file_chooser_entry_parent_class)->dispatch_properties_changed (object, n_pspecs, pspecs); -+ -+ /* Don't do this during or after disposal */ -+ if (gtk_widget_get_parent (GTK_WIDGET (object)) != NULL) -+ { -+ /* What we are after: The text in front of the cursor was modified. -+ * Unfortunately, there's no other way to catch this. -+ */ -+ for (i = 0; i < n_pspecs; i++) -+ { -+ if (pspecs[i]->name == I_("cursor-position") || -+ pspecs[i]->name == I_("selection-bound") || -+ pspecs[i]->name == I_("text")) -+ { -+ set_complete_on_load (chooser_entry, FALSE); -+ refresh_current_folder_and_file_part (chooser_entry); -+ break; -+ } -+ } -+ } -+} - - static void - _gtk_file_chooser_entry_class_init (GtkFileChooserEntryClass *class) - { - GObjectClass *gobject_class = G_OBJECT_CLASS (class); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); -- GtkEntryClass *entry_class = GTK_ENTRY_CLASS (class); - - gobject_class->finalize = gtk_file_chooser_entry_finalize; - gobject_class->dispose = gtk_file_chooser_entry_dispose; -+ gobject_class->dispatch_properties_changed = gtk_file_chooser_entry_dispatch_properties_changed; - - widget_class->grab_focus = gtk_file_chooser_entry_grab_focus; -- widget_class->unmap = gtk_file_chooser_entry_unmap; -- widget_class->key_press_event = gtk_file_chooser_entry_key_press_event; - widget_class->focus_out_event = gtk_file_chooser_entry_focus_out_event; -- -- entry_class->activate = gtk_file_chooser_entry_activate; --} -- --static void --gtk_file_chooser_entry_iface_init (GtkEditableClass *iface) --{ -- parent_editable_iface = g_type_interface_peek_parent (iface); -- -- iface->do_insert_text = gtk_file_chooser_entry_do_insert_text; -- iface->do_delete_text = gtk_file_chooser_entry_do_delete_text; -- iface->set_position = gtk_file_chooser_entry_set_position; -- iface->set_selection_bounds = gtk_file_chooser_entry_set_selection_bounds; - } - - static void -@@ -213,9 +177,14 @@ _gtk_file_chooser_entry_init (GtkFileChooserEntry *chooser_entry) - - comp = gtk_entry_completion_new (); - gtk_entry_completion_set_popup_single_match (comp, FALSE); -+ gtk_entry_completion_set_minimum_key_length (comp, 0); -+ /* see docs for gtk_entry_completion_set_text_column() */ -+ g_object_set (comp, "text-column", FULL_PATH_COLUMN, NULL); - -+ /* Need a match func here or entry completion uses a wrong one. -+ * We do our own filtering after all. */ - gtk_entry_completion_set_match_func (comp, -- completion_match_func, -+ (GtkEntryCompletionMatchFunc) gtk_true, - chooser_entry, - NULL); - -@@ -224,13 +193,17 @@ _gtk_file_chooser_entry_init (GtkFileChooserEntry *chooser_entry) - cell, TRUE); - gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (comp), - cell, -- "text", 0); -+ "text", DISPLAY_NAME_COLUMN); - - g_signal_connect (comp, "match-selected", - G_CALLBACK (match_selected_callback), chooser_entry); - - gtk_entry_set_completion (GTK_ENTRY (chooser_entry), comp); - g_object_unref (comp); -+ /* NB: This needs to happen after the completion is set, so this handler -+ * runs before the handler installed by entrycompletion */ -+ g_signal_connect (chooser_entry, "key-press-event", -+ G_CALLBACK (gtk_file_chooser_entry_tab_handler), NULL); - - #ifdef G_OS_WIN32 - g_signal_connect (chooser_entry, "insert-text", -@@ -248,78 +221,21 @@ gtk_file_chooser_entry_finalize (GObject *object) - if (chooser_entry->base_folder) - g_object_unref (chooser_entry->base_folder); - -- if (chooser_entry->current_folder) -- g_object_unref (chooser_entry->current_folder); -- - if (chooser_entry->current_folder_file) - g_object_unref (chooser_entry->current_folder_file); - -+ g_free (chooser_entry->dir_part); - g_free (chooser_entry->file_part); - - G_OBJECT_CLASS (_gtk_file_chooser_entry_parent_class)->finalize (object); - } - - static void --discard_current_folder (GtkFileChooserEntry *chooser_entry) --{ -- if (chooser_entry->current_folder) -- { -- g_signal_handlers_disconnect_by_func (chooser_entry->current_folder, -- G_CALLBACK (finished_loading_cb), chooser_entry); -- g_object_unref (chooser_entry->current_folder); -- chooser_entry->current_folder = NULL; -- } --} -- --static void --discard_loading_and_current_folder_file (GtkFileChooserEntry *chooser_entry) --{ -- if (chooser_entry->load_folder_cancellable) -- { -- g_cancellable_cancel (chooser_entry->load_folder_cancellable); -- chooser_entry->load_folder_cancellable = NULL; -- } -- -- if (chooser_entry->current_folder_file) -- { -- g_object_unref (chooser_entry->current_folder_file); -- chooser_entry->current_folder_file = NULL; -- } --} -- --static void --discard_completion_store (GtkFileChooserEntry *chooser_entry) --{ -- if (!chooser_entry->completion_store) -- return; -- -- gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)), NULL); -- g_object_unref (chooser_entry->completion_store); -- chooser_entry->completion_store = NULL; --} -- --static void - gtk_file_chooser_entry_dispose (GObject *object) - { - GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (object); - -- remove_completion_feedback (chooser_entry); -- discard_current_folder (chooser_entry); -- discard_loading_and_current_folder_file (chooser_entry); -- -- if (chooser_entry->start_autocompletion_idle_id != 0) -- { -- g_source_remove (chooser_entry->start_autocompletion_idle_id); -- chooser_entry->start_autocompletion_idle_id = 0; -- } -- -- discard_completion_store (chooser_entry); -- -- if (chooser_entry->file_system) -- { -- g_object_unref (chooser_entry->file_system); -- chooser_entry->file_system = NULL; -- } -+ set_completion_folder (chooser_entry, NULL, NULL); - - G_OBJECT_CLASS (_gtk_file_chooser_entry_parent_class)->dispose (object); - } -@@ -327,948 +243,195 @@ gtk_file_chooser_entry_dispose (GObject *object) - /* Match functions for the GtkEntryCompletion */ - static gboolean - match_selected_callback (GtkEntryCompletion *completion, -- GtkTreeModel *model, -- GtkTreeIter *iter, -- GtkFileChooserEntry *chooser_entry) -+ GtkTreeModel *model, -+ GtkTreeIter *iter, -+ GtkFileChooserEntry *chooser_entry) - { -- char *display_name; -- GFile *file; -+ char *path; - gint pos; -- gboolean dummy; -- -- gtk_tree_model_get (model, iter, -- DISPLAY_NAME_COLUMN, &display_name, -- FILE_COLUMN, &file, -- -1); -- -- if (!display_name || !file) -- { -- /* these shouldn't complain if passed NULL */ -- g_object_unref (file); -- g_free (display_name); -- return FALSE; -- } -- -- display_name = maybe_append_separator_to_file (chooser_entry, file, display_name, &dummy); - -- pos = chooser_entry->file_part_pos; -+ gtk_tree_model_get (model, iter, -+ FULL_PATH_COLUMN, &path, -+ -1); - -- /* We don't set in_change here as we want to update the current_folder -- * variable */ - gtk_editable_delete_text (GTK_EDITABLE (chooser_entry), -- pos, -1); -+ 0, -+ gtk_editable_get_position (GTK_EDITABLE (chooser_entry))); -+ pos = 0; - gtk_editable_insert_text (GTK_EDITABLE (chooser_entry), -- display_name, -1, -- &pos); -- gtk_editable_set_position (GTK_EDITABLE (chooser_entry), -1); -+ path, -+ -1, -+ &pos); - -- g_object_unref (file); -- g_free (display_name); -+ g_free (path); - - return TRUE; - } - --/* Match function for the GtkEntryCompletion */ --static gboolean --completion_match_func (GtkEntryCompletion *comp, -- const char *key_unused, -- GtkTreeIter *iter, -- gpointer data) --{ -- GtkFileChooserEntry *chooser_entry; -- char *name; -- gboolean result; -- char *norm_file_part; -- char *norm_name; -- -- chooser_entry = GTK_FILE_CHOOSER_ENTRY (data); -- -- /* We ignore the key because it is the contents of the entry. Instead, we -- * just use our precomputed file_part. -- */ -- if (!chooser_entry->file_part) -- { -- return FALSE; -- } -- -- gtk_tree_model_get (GTK_TREE_MODEL (chooser_entry->completion_store), iter, DISPLAY_NAME_COLUMN, &name, -1); -- if (!name) -- { -- return FALSE; /* Uninitialized row, ugh */ -- } -- -- /* If we have an empty file_part, then we're at the root of a directory. In -- * that case, we want to match all non-dot files. We might want to match -- * dot_files too if show_hidden is TRUE on the fileselector in the future. -- */ -- /* Additionally, support for gnome .hidden files would be sweet, too */ -- if (chooser_entry->file_part[0] == '\000') -- { -- if (name[0] == '.') -- result = FALSE; -- else -- result = TRUE; -- g_free (name); -- -- return result; -- } -- -- -- norm_file_part = g_utf8_normalize (chooser_entry->file_part, -1, G_NORMALIZE_ALL); -- norm_name = g_utf8_normalize (name, -1, G_NORMALIZE_ALL); -- --#ifdef G_PLATFORM_WIN32 -- { -- gchar *temp; -- -- temp = norm_file_part; -- norm_file_part = g_utf8_casefold (norm_file_part, -1); -- g_free (temp); -- -- temp = norm_name; -- norm_name = g_utf8_casefold (norm_name, -1); -- g_free (temp); -- } --#endif -- -- result = (strncmp (norm_file_part, norm_name, strlen (norm_file_part)) == 0); -- -- g_free (norm_file_part); -- g_free (norm_name); -- g_free (name); -- -- return result; --} -- --static void --clear_completions (GtkFileChooserEntry *chooser_entry) --{ -- chooser_entry->has_completion = FALSE; -- chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING; -- -- remove_completion_feedback (chooser_entry); --} -- --static void --beep (GtkFileChooserEntry *chooser_entry) --{ -- gtk_widget_error_bell (GTK_WIDGET (chooser_entry)); --} -- --/* This function will append a directory separator to paths to -- * display_name iff the path associated with it is a directory. -- * maybe_append_separator_to_file will g_free the display_name and -- * return a new one if needed. Otherwise, it will return the old one. -- * You should be safe calling -- * -- * display_name = maybe_append_separator_to_file (entry, file, display_name, &appended); -- * ... -- * g_free (display_name); -- */ --static char * --maybe_append_separator_to_file (GtkFileChooserEntry *chooser_entry, -- GFile *file, -- gchar *display_name, -- gboolean *appended) --{ -- *appended = FALSE; -- -- if (!g_str_has_suffix (display_name, G_DIR_SEPARATOR_S) && file) -- { -- GFileInfo *info; -- -- info = _gtk_folder_get_info (chooser_entry->current_folder, file); -- -- if (info) -- { -- if (_gtk_file_info_consider_as_directory (info)) -- { -- gchar *tmp = display_name; -- display_name = g_strconcat (tmp, G_DIR_SEPARATOR_S, NULL); -- *appended = TRUE; -- g_free (tmp); -- } -- -- g_object_unref (info); -- } -- } -- -- return display_name; --} -- --static char * --trim_dir_separator_suffix (const char *str) --{ -- int len; -- -- len = strlen (str); -- if (len > 0 && G_IS_DIR_SEPARATOR (str[len - 1])) -- return g_strndup (str, len - 1); -- else -- return g_strdup (str); --} -- --/* Determines if the completion model has entries with a common prefix relative -- * to the current contents of the entry. Also, if there's one and only one such -- * path, stores it in unique_path_ret. -- */ --static gboolean --find_common_prefix (GtkFileChooserEntry *chooser_entry, -- gchar **common_prefix_ret, -- GFile **unique_file_ret, -- gboolean *is_complete_not_unique_ret, -- gboolean *prefix_expands_the_file_part_ret, -- GError **error) --{ -- GtkEditable *editable; -- GtkTreeIter iter; -- gboolean parsed; -- gboolean valid; -- char *text_up_to_cursor; -- GFile *parsed_folder_file; -- char *parsed_file_part; -- -- *common_prefix_ret = NULL; -- *unique_file_ret = NULL; -- *is_complete_not_unique_ret = FALSE; -- *prefix_expands_the_file_part_ret = FALSE; -- -- editable = GTK_EDITABLE (chooser_entry); -- -- text_up_to_cursor = gtk_editable_get_chars (editable, 0, gtk_editable_get_position (editable)); -- -- parsed = _gtk_file_system_parse (chooser_entry->file_system, -- chooser_entry->base_folder, -- text_up_to_cursor, -- &parsed_folder_file, -- &parsed_file_part, -- error); -- -- g_free (text_up_to_cursor); -- -- if (!parsed) -- return FALSE; -- -- g_assert (parsed_folder_file != NULL -- && chooser_entry->current_folder != NULL -- && g_file_equal (parsed_folder_file, chooser_entry->current_folder_file)); -- -- g_object_unref (parsed_folder_file); -- -- /* First pass: find the common prefix */ -- -- valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (chooser_entry->completion_store), &iter); -- -- while (valid) -- { -- gchar *display_name; -- GFile *file; -- -- gtk_tree_model_get (GTK_TREE_MODEL (chooser_entry->completion_store), -- &iter, -- DISPLAY_NAME_COLUMN, &display_name, -- FILE_COLUMN, &file, -- -1); -- -- if (g_str_has_prefix (display_name, parsed_file_part)) -- { -- if (!*common_prefix_ret) -- { -- *common_prefix_ret = trim_dir_separator_suffix (display_name); -- *unique_file_ret = g_object_ref (file); -- } -- else -- { -- gchar *p = *common_prefix_ret; -- const gchar *q = display_name; -- -- while (*p && *p == *q) -- { -- p++; -- q++; -- } -- -- *p = '\0'; -- -- if (*unique_file_ret) -- { -- g_object_unref (*unique_file_ret); -- *unique_file_ret = NULL; -- } -- } -- } -- -- g_free (display_name); -- g_object_unref (file); -- valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser_entry->completion_store), &iter); -- } -- -- /* Second pass: see if the prefix we found is a complete match */ -- -- if (*common_prefix_ret != NULL) -- { -- valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (chooser_entry->completion_store), &iter); -- -- while (valid) -- { -- gchar *display_name; -- int len; -- -- gtk_tree_model_get (GTK_TREE_MODEL (chooser_entry->completion_store), -- &iter, -- DISPLAY_NAME_COLUMN, &display_name, -- -1); -- len = strlen (display_name); -- g_assert (len > 0); -- -- if (G_IS_DIR_SEPARATOR (display_name[len - 1])) -- len--; -- -- if (*unique_file_ret == NULL && strncmp (*common_prefix_ret, display_name, len) == 0) -- *is_complete_not_unique_ret = TRUE; -- -- g_free (display_name); -- valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser_entry->completion_store), &iter); -- } -- -- /* Finally: Did we generate a new completion, or was the user's input already completed as far as it could go? */ -- -- *prefix_expands_the_file_part_ret = g_utf8_strlen (*common_prefix_ret, -1) > g_utf8_strlen (parsed_file_part, -1); -- } -- -- g_free (parsed_file_part); -- -- return TRUE; --} -- --static gboolean --char_after_cursor_is_directory_separator (GtkFileChooserEntry *chooser_entry) --{ -- int cursor_pos; -- gboolean result; -- -- result = FALSE; -- -- cursor_pos = gtk_editable_get_position (GTK_EDITABLE (chooser_entry)); -- if (cursor_pos < gtk_entry_get_text_length (GTK_ENTRY (chooser_entry))) -- { -- char *next_char_str; -- -- next_char_str = gtk_editable_get_chars (GTK_EDITABLE (chooser_entry), cursor_pos, cursor_pos + 1); -- if (G_IS_DIR_SEPARATOR (*next_char_str)) -- result = TRUE; -- -- g_free (next_char_str); -- } -- -- return result; --} -- --typedef enum { -- INVALID_INPUT, /* what the user typed is bogus */ -- NO_MATCH, /* no matches based on what the user typed */ -- NOTHING_INSERTED_COMPLETE, /* what the user typed is already completed as far as it will go */ -- NOTHING_INSERTED_UNIQUE, /* what the user typed is already completed, and is a unique match */ -- COMPLETED, /* completion inserted (ambiguous suffix) */ -- COMPLETED_UNIQUE, /* completion inserted, and it is a complete name and a unique match */ -- COMPLETE_BUT_NOT_UNIQUE /* completion inserted, it is a complete name but not unique */ --} CommonPrefixResult; -- --/* Finds a common prefix based on the contents of the entry -- * and mandatorily appends it -- */ --static CommonPrefixResult --append_common_prefix (GtkFileChooserEntry *chooser_entry, -- gboolean highlight, -- gboolean show_errors) --{ -- gchar *common_prefix; -- GFile *unique_file; -- gboolean is_complete_not_unique; -- gboolean prefix_expands_the_file_part; -- GError *error; -- CommonPrefixResult result = NO_MATCH; -- gboolean have_result; -- -- clear_completions (chooser_entry); -- -- if (chooser_entry->completion_store == NULL) -- return NO_MATCH; -- -- error = NULL; -- if (!find_common_prefix (chooser_entry, &common_prefix, &unique_file, &is_complete_not_unique, &prefix_expands_the_file_part, &error)) -- { -- /* If the user types an incomplete hostname ("http://foo" without -- * a slash after that), it's not an error. We just don't want to -- * pop up a meaningless completion window in that state. -- */ -- if (!g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME) -- && show_errors) -- { -- beep (chooser_entry); -- pop_up_completion_feedback (chooser_entry, _("Invalid path")); -- } -- -- g_error_free (error); -- -- return INVALID_INPUT; -- } -- -- have_result = FALSE; -- -- if (unique_file) -- { -- if (!char_after_cursor_is_directory_separator (chooser_entry)) -- { -- gboolean appended; -- -- common_prefix = maybe_append_separator_to_file (chooser_entry, -- unique_file, -- common_prefix, -- &appended); -- if (appended) -- prefix_expands_the_file_part = TRUE; -- } -- -- g_object_unref (unique_file); -- -- if (prefix_expands_the_file_part) -- result = COMPLETED_UNIQUE; -- else -- result = NOTHING_INSERTED_UNIQUE; -- -- have_result = TRUE; -- } -- else -- { -- if (is_complete_not_unique) -- { -- result = COMPLETE_BUT_NOT_UNIQUE; -- have_result = TRUE; -- } -- } -- -- if (common_prefix) -- { -- gint cursor_pos; -- gint pos; -- -- cursor_pos = gtk_editable_get_position (GTK_EDITABLE (chooser_entry)); -- -- pos = chooser_entry->file_part_pos; -- -- if (prefix_expands_the_file_part) -- { -- chooser_entry->in_change = TRUE; -- gtk_editable_delete_text (GTK_EDITABLE (chooser_entry), -- pos, cursor_pos); -- gtk_editable_insert_text (GTK_EDITABLE (chooser_entry), -- common_prefix, -1, -- &pos); -- chooser_entry->in_change = FALSE; -- -- if (highlight) -- { -- /* equivalent to cursor_pos + common_prefix_len); */ -- gtk_editable_select_region (GTK_EDITABLE (chooser_entry), -- cursor_pos, -- pos); -- chooser_entry->has_completion = TRUE; -- } -- else -- gtk_editable_set_position (GTK_EDITABLE (chooser_entry), pos); -- } -- else if (!have_result) -- { -- result = NOTHING_INSERTED_COMPLETE; -- have_result = TRUE; -- } -- -- g_free (common_prefix); -- -- if (have_result) -- return result; -- else -- return COMPLETED; -- } -- else -- { -- if (have_result) -- return result; -- else -- return NO_MATCH; -- } --} -- --static void --gtk_file_chooser_entry_do_insert_text (GtkEditable *editable, -- const gchar *new_text, -- gint new_text_length, -- gint *position) --{ -- GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (editable); -- gint old_text_len; -- gint insert_pos; -- -- old_text_len = gtk_entry_get_text_length (GTK_ENTRY (chooser_entry)); -- insert_pos = *position; -- -- parent_editable_iface->do_insert_text (editable, new_text, new_text_length, position); -- -- if (chooser_entry->in_change) -- return; -- -- remove_completion_feedback (chooser_entry); -- -- if ((chooser_entry->action == GTK_FILE_CHOOSER_ACTION_OPEN -- || chooser_entry->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) -- && insert_pos == old_text_len) -- install_start_autocompletion_idle (chooser_entry); --} -- --static void --clear_completions_if_not_in_change (GtkFileChooserEntry *chooser_entry) --{ -- if (chooser_entry->in_change) -- return; -- -- clear_completions (chooser_entry); --} -- - static void --gtk_file_chooser_entry_do_delete_text (GtkEditable *editable, -- gint start_pos, -- gint end_pos) -+set_complete_on_load (GtkFileChooserEntry *chooser_entry, -+ gboolean complete_on_load) - { -- GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (editable); -- -- parent_editable_iface->do_delete_text (editable, start_pos, end_pos); -- -- clear_completions_if_not_in_change (chooser_entry); --} -- --static void --gtk_file_chooser_entry_set_position (GtkEditable *editable, -- gint position) --{ -- GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (editable); -- -- parent_editable_iface->set_position (editable, position); -- -- clear_completions_if_not_in_change (chooser_entry); --} -- --static void --gtk_file_chooser_entry_set_selection_bounds (GtkEditable *editable, -- gint start_pos, -- gint end_pos) --{ -- GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (editable); -- -- parent_editable_iface->set_selection_bounds (editable, start_pos, end_pos); -- -- clear_completions_if_not_in_change (chooser_entry); --} -- --static void --gtk_file_chooser_entry_grab_focus (GtkWidget *widget) --{ -- GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->grab_focus (widget); -- _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (widget)); --} -- --static void --gtk_file_chooser_entry_unmap (GtkWidget *widget) --{ -- GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget); -- -- remove_completion_feedback (chooser_entry); -- -- GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->unmap (widget); --} -- --static gboolean --completion_feedback_window_expose_event_cb (GtkWidget *widget, -- GdkEventExpose *event, -- gpointer data) --{ -- /* Stolen from gtk_tooltip_paint_window() */ -- -- GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data); -- -- gtk_paint_flat_box (chooser_entry->completion_feedback_window->style, -- chooser_entry->completion_feedback_window->window, -- GTK_STATE_NORMAL, -- GTK_SHADOW_OUT, -- NULL, -- chooser_entry->completion_feedback_window, -- "tooltip", -- 0, 0, -- chooser_entry->completion_feedback_window->allocation.width, -- chooser_entry->completion_feedback_window->allocation.height); -- -- return FALSE; --} -- --static void --set_invisible_mouse_cursor (GdkWindow *window) --{ -- GdkDisplay *display; -- GdkCursor *cursor; -- -- display = gdk_window_get_display (window); -- cursor = gdk_cursor_new_for_display (display, GDK_BLANK_CURSOR); -- -- gdk_window_set_cursor (window, cursor); -- -- gdk_cursor_unref (cursor); --} -- --static void --completion_feedback_window_realize_cb (GtkWidget *widget, -- gpointer data) --{ -- /* We hide the mouse cursor inside the completion feedback window, since -- * GtkEntry hides the cursor when the user types. We don't want the cursor to -- * come back if the completion feedback ends up where the mouse is. -+ /* a completion was triggered, but we couldn't do it. -+ * So no text was inserted when pressing tab, so we beep - */ -- set_invisible_mouse_cursor (gtk_widget_get_window (widget)); --} -+ if (chooser_entry->complete_on_load && !complete_on_load) -+ gtk_widget_error_bell (GTK_WIDGET (chooser_entry)); - --static void --create_completion_feedback_window (GtkFileChooserEntry *chooser_entry) --{ -- /* Stolen from gtk_tooltip_init() */ -- -- GtkWidget *alignment; -- -- chooser_entry->completion_feedback_window = gtk_window_new (GTK_WINDOW_POPUP); -- gtk_window_set_type_hint (GTK_WINDOW (chooser_entry->completion_feedback_window), -- GDK_WINDOW_TYPE_HINT_TOOLTIP); -- gtk_widget_set_app_paintable (chooser_entry->completion_feedback_window, TRUE); -- gtk_window_set_resizable (GTK_WINDOW (chooser_entry->completion_feedback_window), FALSE); -- gtk_widget_set_name (chooser_entry->completion_feedback_window, "gtk-tooltip"); -- -- alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0); -- gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), -- chooser_entry->completion_feedback_window->style->ythickness, -- chooser_entry->completion_feedback_window->style->ythickness, -- chooser_entry->completion_feedback_window->style->xthickness, -- chooser_entry->completion_feedback_window->style->xthickness); -- gtk_container_add (GTK_CONTAINER (chooser_entry->completion_feedback_window), alignment); -- gtk_widget_show (alignment); -- -- g_signal_connect (chooser_entry->completion_feedback_window, "expose-event", -- G_CALLBACK (completion_feedback_window_expose_event_cb), chooser_entry); -- g_signal_connect (chooser_entry->completion_feedback_window, "realize", -- G_CALLBACK (completion_feedback_window_realize_cb), chooser_entry); -- /* FIXME: connect to motion-notify-event, and *show* the cursor when the mouse moves */ -- -- chooser_entry->completion_feedback_label = gtk_label_new (NULL); -- gtk_container_add (GTK_CONTAINER (alignment), chooser_entry->completion_feedback_label); -- gtk_widget_show (chooser_entry->completion_feedback_label); -+ chooser_entry->complete_on_load = complete_on_load; - } - - static gboolean --completion_feedback_timeout_cb (gpointer data) --{ -- GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data); -- -- chooser_entry->completion_feedback_timeout_id = 0; -- -- remove_completion_feedback (chooser_entry); -- return FALSE; --} -- --static void --install_completion_feedback_timer (GtkFileChooserEntry *chooser_entry) --{ -- if (chooser_entry->completion_feedback_timeout_id != 0) -- g_source_remove (chooser_entry->completion_feedback_timeout_id); -- -- chooser_entry->completion_feedback_timeout_id = gdk_threads_add_timeout (COMPLETION_FEEDBACK_TIMEOUT_MS, -- completion_feedback_timeout_cb, -- chooser_entry); --} -- --/* Gets the x position of the text cursor in the entry, in widget coordinates */ --static void --get_entry_cursor_x (GtkFileChooserEntry *chooser_entry, -- gint *x_ret) --{ -- /* FIXME: see the docs for gtk_entry_get_layout_offsets(). As an exercise for -- * the reader, you have to implement support for the entry's scroll offset and -- * RTL layouts and all the fancy Pango stuff. -- */ -- -- PangoLayout *layout; -- gint layout_x, layout_y; -- gint layout_index; -- PangoRectangle strong_pos; -- gint start_pos, end_pos; -- -- layout = gtk_entry_get_layout (GTK_ENTRY (chooser_entry)); -- -- gtk_entry_get_layout_offsets (GTK_ENTRY (chooser_entry), &layout_x, &layout_y); -- -- gtk_editable_get_selection_bounds (GTK_EDITABLE (chooser_entry), &start_pos, &end_pos); -- layout_index = gtk_entry_text_index_to_layout_index (GTK_ENTRY (chooser_entry), -- end_pos); -- -- -- pango_layout_get_cursor_pos (layout, layout_index, &strong_pos, NULL); -- -- *x_ret = layout_x + strong_pos.x / PANGO_SCALE; --} -- --static void --show_completion_feedback_window (GtkFileChooserEntry *chooser_entry) --{ -- /* More or less stolen from gtk_tooltip_position() */ -- -- GtkRequisition feedback_req; -- gint entry_x, entry_y; -- gint cursor_x; -- GtkAllocation *entry_allocation; -- int feedback_x, feedback_y; -- -- gtk_widget_size_request (chooser_entry->completion_feedback_window, &feedback_req); -- -- gdk_window_get_origin (GTK_WIDGET (chooser_entry)->window, &entry_x, &entry_y); -- entry_allocation = &(GTK_WIDGET (chooser_entry)->allocation); -- -- get_entry_cursor_x (chooser_entry, &cursor_x); -- -- /* FIXME: fit to the screen if we bump on the screen's edge */ -- /* cheap "half M-width", use height as approximation of character em-size */ -- feedback_x = entry_x + cursor_x + entry_allocation->height / 2; -- feedback_y = entry_y + (entry_allocation->height - feedback_req.height) / 2; -- -- gtk_window_move (GTK_WINDOW (chooser_entry->completion_feedback_window), feedback_x, feedback_y); -- gtk_widget_show (chooser_entry->completion_feedback_window); -- -- install_completion_feedback_timer (chooser_entry); --} -- --static void --pop_up_completion_feedback (GtkFileChooserEntry *chooser_entry, -- const gchar *feedback) --{ -- if (chooser_entry->completion_feedback_window == NULL) -- create_completion_feedback_window (chooser_entry); -- -- gtk_label_set_text (GTK_LABEL (chooser_entry->completion_feedback_label), feedback); -- -- show_completion_feedback_window (chooser_entry); --} -- --static void --remove_completion_feedback (GtkFileChooserEntry *chooser_entry) -+is_valid_scheme_character (char c) - { -- if (chooser_entry->completion_feedback_window) -- gtk_widget_destroy (chooser_entry->completion_feedback_window); -- -- chooser_entry->completion_feedback_window = NULL; -- chooser_entry->completion_feedback_label = NULL; -- -- if (chooser_entry->completion_feedback_timeout_id != 0) -- { -- g_source_remove (chooser_entry->completion_feedback_timeout_id); -- chooser_entry->completion_feedback_timeout_id = 0; -- } -+ return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.'; - } - --static void --explicitly_complete (GtkFileChooserEntry *chooser_entry) -+static gboolean -+has_uri_scheme (const char *str) - { -- CommonPrefixResult result; -- -- g_assert (chooser_entry->current_folder != NULL); -- g_assert (_gtk_folder_is_finished_loading (chooser_entry->current_folder)); -- -- /* FIXME: see what Emacs does in case there is no common prefix, or there is more than one match: -- * -- * - If there is a common prefix, insert it (done) -- * - If there is no common prefix, pop up the suggestion window -- * - If there are no matches at all, beep and bring up a tooltip (done) -- * - If the suggestion window is already up, scroll it -- */ -- result = append_common_prefix (chooser_entry, FALSE, TRUE); -+ const char *p; - -- switch (result) -- { -- case INVALID_INPUT: -- /* We already beeped in append_common_prefix(); do nothing here */ -- break; -- -- case NO_MATCH: -- beep (chooser_entry); -- /* translators: this text is shown when there are no completions -- * for something the user typed in a file chooser entry -- */ -- pop_up_completion_feedback (chooser_entry, _("No match")); -- break; -- -- case NOTHING_INSERTED_COMPLETE: -- /* FIXME: pop up the suggestion window or scroll it */ -- break; -- -- case NOTHING_INSERTED_UNIQUE: -- /* translators: this text is shown when there is exactly one completion -- * for something the user typed in a file chooser entry -- */ -- pop_up_completion_feedback (chooser_entry, _("Sole completion")); -- break; -- -- case COMPLETED: -- /* Nothing to do */ -- break; -+ p = str; - -- case COMPLETED_UNIQUE: -- /* Nothing to do */ -- break; -+ if (!is_valid_scheme_character (*p)) -+ return FALSE; - -- case COMPLETE_BUT_NOT_UNIQUE: -- /* translators: this text is shown when the text in a file chooser -- * entry is a complete filename, but could be continued to find -- * a longer match -- */ -- pop_up_completion_feedback (chooser_entry, _("Complete, but not unique")); -- break; -+ do -+ p++; -+ while (is_valid_scheme_character (*p)); - -- default: -- g_assert_not_reached (); -- } -+ return (strncmp (p, "://", 3) == 0); - } - --static void --start_explicit_completion (GtkFileChooserEntry *chooser_entry) --{ -- RefreshStatus status; -- gboolean is_error; -- char *feedback_msg; -- -- status = refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION); -- -- is_error = FALSE; -+static GFile * -+gtk_file_chooser_get_file_for_text (GtkFileChooserEntry *chooser_entry, -+ const gchar *str) -+{ -+ GFile *file; - -- switch (status) -- { -- case REFRESH_OK: -- g_assert (chooser_entry->current_folder_file != NULL); -+ if (str[0] == '~' || g_path_is_absolute (str) || has_uri_scheme (str)) -+ file = g_file_parse_name (str); -+ else if (chooser_entry->base_folder != NULL) -+ file = g_file_resolve_relative_path (chooser_entry->base_folder, str); -+ else -+ file = NULL; - -- if (chooser_entry->current_folder && _gtk_folder_is_finished_loading (chooser_entry->current_folder)) -- explicitly_complete (chooser_entry); -- else -- { -- chooser_entry->load_complete_action = LOAD_COMPLETE_EXPLICIT_COMPLETION; -+ return file; -+} - -- /* Translators: this text is shown while the system is searching -- * for possible completions for filenames in a file chooser entry. */ -- pop_up_completion_feedback (chooser_entry, _("Completing...")); -- } -+static gboolean -+is_directory_shortcut (const char *text) -+{ -+ return strcmp (text, ".") == 0 || -+ strcmp (text, "..") == 0 || -+ strcmp (text, "~" ) == 0; -+} - -- break; -+static GFile * -+gtk_file_chooser_get_directory_for_text (GtkFileChooserEntry *chooser_entry, -+ const char * text) -+{ -+ GFile *file, *parent; - -- case REFRESH_INVALID_INPUT: -- is_error = TRUE; -- /* Translators: this is shown in the feedback for Tab-completion in a file -- * chooser's text entry, when the user enters an invalid path. */ -- feedback_msg = _("Invalid path"); -- break; -+ file = gtk_file_chooser_get_file_for_text (chooser_entry, text); - -- case REFRESH_INCOMPLETE_HOSTNAME: -- is_error = TRUE; -+ if (file == NULL) -+ return NULL; - -- if (chooser_entry->local_only) -- { -- /* hostnames in a local_only file chooser? user error */ -+ if (text[0] == 0 || text[strlen (text) - 1] == G_DIR_SEPARATOR || -+ is_directory_shortcut (text)) -+ return file; - -- /* Translators: this is shown in the feedback for Tab-completion in a -- * file chooser's text entry when the user enters something like -- * "sftp://blahblah" in an app that only supports local filenames. */ -- feedback_msg = _("Only local files may be selected"); -- } -- else -- { -- /* Another option is to complete the hostname based on the remote volumes that are mounted */ -+ parent = g_file_get_parent (file); -+ g_object_unref (file); - -- /* Translators: this is shown in the feedback for Tab-completion in a -- * file chooser's text entry when the user hasn't entered the first '/' -- * after a hostname and yet hits Tab (such as "sftp://blahblah[Tab]") */ -- feedback_msg = _("Incomplete hostname; end it with '/'"); -- } -+ return parent; -+} - -- break; -+/* Finds a common prefix based on the contents of the entry -+ * and mandatorily appends it -+ */ -+static void -+explicitly_complete (GtkFileChooserEntry *chooser_entry) -+{ -+ chooser_entry->complete_on_load = FALSE; - -- case REFRESH_NONEXISTENT: -- is_error = TRUE; -+ if (chooser_entry->completion_store) -+ { -+ char *completion, *text; -+ gsize completion_len, text_len; - -- /* Translators: this is shown in the feedback for Tab-completion in a file -- * chooser's text entry when the user enters a path that does not exist -- * and then hits Tab */ -- feedback_msg = _("Path does not exist"); -- break; -+ 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); -+ completion_len = completion ? strlen (completion) : 0; - -- case REFRESH_NOT_LOCAL: -- is_error = TRUE; -- feedback_msg = _("Only local files may be selected"); -- break; -+ if (completion_len > text_len) -+ { -+ GtkEditable *editable = GTK_EDITABLE (chooser_entry); -+ int pos = gtk_editable_get_position (editable); - -- default: -- g_assert_not_reached (); -- return; -+ gtk_editable_insert_text (editable, -+ completion + text_len, -+ completion_len - text_len, -+ &pos); -+ gtk_editable_set_position (editable, pos); -+ return; -+ } - } - -- if (is_error) -- { -- g_assert (chooser_entry->current_folder_file == NULL); -+ gtk_widget_error_bell (GTK_WIDGET (chooser_entry)); -+} - -- beep (chooser_entry); -- pop_up_completion_feedback (chooser_entry, feedback_msg); -- chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING; -- } -+static void -+gtk_file_chooser_entry_grab_focus (GtkWidget *widget) -+{ -+ GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->grab_focus (widget); -+ _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (widget)); -+} -+ -+static void -+start_explicit_completion (GtkFileChooserEntry *chooser_entry) -+{ -+ if (chooser_entry->current_folder_loaded) -+ explicitly_complete (chooser_entry); -+ else -+ set_complete_on_load (chooser_entry, TRUE); - } - - static gboolean --gtk_file_chooser_entry_key_press_event (GtkWidget *widget, -- GdkEventKey *event) -+gtk_file_chooser_entry_tab_handler (GtkWidget *widget, -+ GdkEventKey *event) - { - GtkFileChooserEntry *chooser_entry; - GtkEditable *editable; -- GtkEntry *entry; - GdkModifierType state; -- gboolean control_pressed; -+ gint start, end; - - chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget); - editable = GTK_EDITABLE (widget); -- entry = GTK_ENTRY (widget); - - if (!chooser_entry->eat_tabs) -- return GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->key_press_event (widget, event); -+ return FALSE; - -- control_pressed = FALSE; -+ if (event->keyval != GDK_KEY_Tab) -+ return FALSE; - -- if (gtk_get_current_event_state (&state)) -- { -- if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) -- control_pressed = TRUE; -- } -+ if (gtk_get_current_event_state (&state) && -+ (state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) -+ return FALSE; - - /* This is a bit evil -- it makes Tab never leave the entry. It basically - * makes it 'safe' for people to hit. */ -- if (event->keyval == GDK_Tab && !control_pressed) -- { -- if (chooser_entry->has_completion) -- gtk_editable_set_position (editable, gtk_entry_get_text_length (entry)); -- else -- start_explicit_completion (chooser_entry); -- -- return TRUE; -- } -- -- return GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->key_press_event (widget, event); -+ gtk_editable_get_selection_bounds (editable, &start, &end); -+ -+ if (start != end) -+ gtk_editable_set_position (editable, MAX (start, end)); -+ else -+ start_explicit_completion (chooser_entry); - -+ return TRUE; - } - - static gboolean -@@ -1277,420 +440,234 @@ gtk_file_chooser_entry_focus_out_event (GtkWidget *widget, - { - GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget); - -- chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING; -+ set_complete_on_load (chooser_entry, FALSE); - - return GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->focus_out_event (widget, event); - } - - static void --commit_completion_and_refresh (GtkFileChooserEntry *chooser_entry) -+update_inline_completion (GtkFileChooserEntry *chooser_entry) - { -- if (chooser_entry->has_completion) -+ GtkEntryCompletion *completion = gtk_entry_get_completion (GTK_ENTRY (chooser_entry)); -+ -+ if (!chooser_entry->current_folder_loaded) - { -- gtk_editable_set_position (GTK_EDITABLE (chooser_entry), -- gtk_entry_get_text_length (GTK_ENTRY (chooser_entry))); -+ gtk_entry_completion_set_inline_completion (completion, FALSE); -+ return; - } - -- /* Here we ignore the result of refresh_current_folder_and_file_part(); there is nothing we can do with it */ -- refresh_current_folder_and_file_part (chooser_entry, REFRESH_WHOLE_TEXT); -+ switch (chooser_entry->action) -+ { -+ case GTK_FILE_CHOOSER_ACTION_OPEN: -+ case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER: -+ gtk_entry_completion_set_inline_completion (completion, TRUE); -+ break; -+ case GTK_FILE_CHOOSER_ACTION_SAVE: -+ case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER: -+ gtk_entry_completion_set_inline_completion (completion, FALSE); -+ break; -+ } - } - - static void --gtk_file_chooser_entry_activate (GtkEntry *entry) -+discard_completion_store (GtkFileChooserEntry *chooser_entry) - { -- GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (entry); -+ if (!chooser_entry->completion_store) -+ return; - -- commit_completion_and_refresh (chooser_entry); -- GTK_ENTRY_CLASS (_gtk_file_chooser_entry_parent_class)->activate (entry); -+ gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)), NULL); -+ update_inline_completion (chooser_entry); -+ g_object_unref (chooser_entry->completion_store); -+ chooser_entry->completion_store = NULL; - } - --/* Fills the completion store from the contents of the current folder */ --static void --populate_completion_store (GtkFileChooserEntry *chooser_entry) -+static gboolean -+completion_store_set (GtkFileSystemModel *model, -+ GFile *file, -+ GFileInfo *info, -+ int column, -+ GValue *value, -+ gpointer data) - { -- GSList *files; -- GSList *tmp_list; -- -- discard_completion_store (chooser_entry); -- -- files = _gtk_folder_list_children (chooser_entry->current_folder); -- -- chooser_entry->completion_store = gtk_list_store_new (N_COLUMNS, -- G_TYPE_STRING, -- G_TYPE_FILE); -- -- for (tmp_list = files; tmp_list; tmp_list = tmp_list->next) -- { -- GFileInfo *info; -- GFile *file; -- -- file = tmp_list->data; -- -- info = _gtk_folder_get_info (chooser_entry->current_folder, file); -- -- if (info) -- { -- gchar *display_name = g_strdup (g_file_info_get_display_name (info)); -- GtkTreeIter iter; -- gboolean dummy; -- -- display_name = maybe_append_separator_to_file (chooser_entry, file, display_name, &dummy); -- -- gtk_list_store_append (chooser_entry->completion_store, &iter); -- gtk_list_store_set (chooser_entry->completion_store, &iter, -- DISPLAY_NAME_COLUMN, display_name, -- FILE_COLUMN, file, -- -1); -- -- g_object_unref (info); -- g_free (display_name); -- } -- } -- -- g_slist_foreach (files, (GFunc) g_object_unref, NULL); -- g_slist_free (files); -- -- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (chooser_entry->completion_store), -- DISPLAY_NAME_COLUMN, GTK_SORT_ASCENDING); -+ GtkFileChooserEntry *chooser_entry = data; - -- gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)), -- GTK_TREE_MODEL (chooser_entry->completion_store)); --} -+ const char *prefix = ""; -+ const char *suffix = ""; - --/* When we finish loading the current folder, this function should get called to -- * perform the deferred autocompletion or explicit completion. -- */ --static void --perform_load_complete_action (GtkFileChooserEntry *chooser_entry) --{ -- switch (chooser_entry->load_complete_action) -+ switch (column) - { -- case LOAD_COMPLETE_NOTHING: -- break; -- -- case LOAD_COMPLETE_AUTOCOMPLETE: -- autocomplete (chooser_entry); -- break; -+ case FULL_PATH_COLUMN: -+ prefix = chooser_entry->dir_part; -+ /* fall through */ -+ case DISPLAY_NAME_COLUMN: -+ if (_gtk_file_info_consider_as_directory (info)) -+ suffix = G_DIR_SEPARATOR_S; - -- case LOAD_COMPLETE_EXPLICIT_COMPLETION: -- explicitly_complete (chooser_entry); -+ g_value_take_string (value, -+ g_strconcat (prefix, -+ g_file_info_get_display_name (info), -+ suffix, -+ NULL)); - break; -- - default: - g_assert_not_reached (); -+ break; - } - -- chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING; -+ return TRUE; - } - -+/* Fills the completion store from the contents of the current folder */ - static void --finish_folder_load (GtkFileChooserEntry *chooser_entry) -+populate_completion_store (GtkFileChooserEntry *chooser_entry) - { -- populate_completion_store (chooser_entry); -- perform_load_complete_action (chooser_entry); -+ chooser_entry->completion_store = GTK_TREE_MODEL ( -+ _gtk_file_system_model_new_for_directory (chooser_entry->current_folder_file, -+ "standard::name,standard::display-name,standard::type", -+ completion_store_set, -+ chooser_entry, -+ N_COLUMNS, -+ G_TYPE_STRING, -+ G_TYPE_STRING)); -+ g_signal_connect (chooser_entry->completion_store, "finished-loading", -+ G_CALLBACK (finished_loading_cb), chooser_entry); -+ -+ _gtk_file_system_model_set_filter_folders (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store), -+ TRUE); -+ _gtk_file_system_model_set_show_files (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store), -+ chooser_entry->action == GTK_FILE_CHOOSER_ACTION_OPEN || -+ chooser_entry->action == GTK_FILE_CHOOSER_ACTION_SAVE); -+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (chooser_entry->completion_store), -+ DISPLAY_NAME_COLUMN, GTK_SORT_ASCENDING); - -- gtk_widget_set_tooltip_text (GTK_WIDGET (chooser_entry), NULL); -+ gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)), -+ chooser_entry->completion_store); - } - - /* Callback when the current folder finishes loading */ - static void --finished_loading_cb (GtkFolder *folder, -- gpointer data) --{ -- GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data); -- -- finish_folder_load (chooser_entry); --} -- --/* Callback when the current folder's handle gets obtained (not necessarily loaded completely) */ --static void --load_directory_get_folder_callback (GCancellable *cancellable, -- GtkFolder *folder, -- const GError *error, -- gpointer data) -+finished_loading_cb (GtkFileSystemModel *model, -+ GError *error, -+ GtkFileChooserEntry *chooser_entry) - { -- gboolean cancelled = g_cancellable_is_cancelled (cancellable); -- GtkFileChooserEntry *chooser_entry = data; -- -- if (cancellable != chooser_entry->load_folder_cancellable) -- goto out; -+ GtkEntryCompletion *completion; - -- chooser_entry->load_folder_cancellable = NULL; -+ chooser_entry->current_folder_loaded = TRUE; - - if (error) - { -- LoadCompleteAction old_load_complete_action; -- -- old_load_complete_action = chooser_entry->load_complete_action; -- - discard_completion_store (chooser_entry); -- clear_completions (chooser_entry); -- -- if (old_load_complete_action == LOAD_COMPLETE_EXPLICIT_COMPLETION) -- { -- /* Since this came from explicit user action (Tab completion), we'll present errors visually */ -- -- beep (chooser_entry); -- pop_up_completion_feedback (chooser_entry, error->message); -- } -- -- discard_current_folder (chooser_entry); -+ set_complete_on_load (chooser_entry, FALSE); -+ return; - } - -- if (cancelled || error) -- goto out; -+ if (chooser_entry->complete_on_load) -+ explicitly_complete (chooser_entry); - -- g_assert (folder != NULL); -- chooser_entry->current_folder = g_object_ref (folder); -- -- discard_completion_store (chooser_entry); -+ gtk_widget_set_tooltip_text (GTK_WIDGET (chooser_entry), NULL); - -- if (_gtk_folder_is_finished_loading (chooser_entry->current_folder)) -- finish_folder_load (chooser_entry); -- else -- g_signal_connect (chooser_entry->current_folder, "finished-loading", -- G_CALLBACK (finished_loading_cb), chooser_entry); -+ completion = gtk_entry_get_completion (GTK_ENTRY (chooser_entry)); -+ update_inline_completion (chooser_entry); - --out: -- g_object_unref (chooser_entry); -- g_object_unref (cancellable); -+ if (gtk_widget_has_focus (GTK_WIDGET (chooser_entry))) -+ { -+ gtk_entry_completion_complete (completion); -+ gtk_entry_completion_insert_prefix (completion); -+ } - } - --static RefreshStatus --start_loading_current_folder (GtkFileChooserEntry *chooser_entry) -+static void -+set_completion_folder (GtkFileChooserEntry *chooser_entry, -+ GFile *folder_file, -+ char *dir_part) - { -- if (chooser_entry->file_system == NULL) -- return REFRESH_OK; -+ if (folder_file && -+ chooser_entry->local_only -+ && !_gtk_file_has_native_path (folder_file)) -+ folder_file = NULL; - -- g_assert (chooser_entry->current_folder_file != NULL); -- g_assert (chooser_entry->current_folder == NULL); -- g_assert (chooser_entry->load_folder_cancellable == 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) -+ && g_strcmp0 (dir_part, chooser_entry->dir_part) == 0) -+ { -+ return; -+ } - -- if (chooser_entry->local_only -- && !_gtk_file_has_native_path (chooser_entry->current_folder_file)) -+ if (chooser_entry->current_folder_file) - { - g_object_unref (chooser_entry->current_folder_file); - chooser_entry->current_folder_file = NULL; -- -- return REFRESH_NOT_LOCAL; - } - -- chooser_entry->load_folder_cancellable = -- _gtk_file_system_get_folder (chooser_entry->file_system, -- chooser_entry->current_folder_file, -- "standard::name,standard::display-name,standard::type", -- load_directory_get_folder_callback, -- g_object_ref (chooser_entry)); -- -- return REFRESH_OK; --} -- --static RefreshStatus --reload_current_folder (GtkFileChooserEntry *chooser_entry, -- GFile *folder_file, -- gboolean force_reload) --{ -- gboolean reload = FALSE; -- -- g_assert (folder_file != NULL); -- -- if (chooser_entry->current_folder_file) -- { -- if ((!(g_file_equal (folder_file, chooser_entry->current_folder_file) -- && chooser_entry->load_folder_cancellable)) -- || force_reload) -- { -- reload = TRUE; -+ g_free (chooser_entry->dir_part); -+ chooser_entry->dir_part = g_strdup (dir_part); -+ -+ chooser_entry->current_folder_loaded = FALSE; - -- discard_current_folder (chooser_entry); -- discard_loading_and_current_folder_file (chooser_entry); -+ discard_completion_store (chooser_entry); - -- chooser_entry->current_folder_file = g_object_ref (folder_file); -- } -- } -- else -+ if (folder_file) - { - chooser_entry->current_folder_file = g_object_ref (folder_file); -- reload = TRUE; -+ populate_completion_store (chooser_entry); - } -- -- if (reload) -- return start_loading_current_folder (chooser_entry); -- else -- return REFRESH_OK; - } - --static RefreshStatus --refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry, -- RefreshMode refresh_mode) -+static void -+refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry) - { -- GtkEditable *editable; -- gint end_pos; -- gchar *text; - GFile *folder_file; -- gchar *file_part; -- gsize total_len, file_part_len; -- gint file_part_pos; -- GError *error; -- RefreshStatus result; -+ char *text, *last_slash, *old_file_part; -+ char *dir_part; - -- editable = GTK_EDITABLE (chooser_entry); -+ old_file_part = chooser_entry->file_part; - -- switch (refresh_mode) -- { -- case REFRESH_UP_TO_CURSOR_POSITION: -- end_pos = gtk_editable_get_position (editable); -- break; -- -- case REFRESH_WHOLE_TEXT: -- end_pos = gtk_entry_get_text_length (GTK_ENTRY (chooser_entry)); -- break; -- -- default: -- g_assert_not_reached (); -- return REFRESH_INVALID_INPUT; -- } -+ text = gtk_file_chooser_entry_get_completion_text (chooser_entry); - -- text = gtk_editable_get_chars (editable, 0, end_pos); -- -- error = NULL; -- if (!chooser_entry->file_system || -- !_gtk_file_system_parse (chooser_entry->file_system, -- chooser_entry->base_folder, text, -- &folder_file, &file_part, &error)) -+ last_slash = strrchr (text, G_DIR_SEPARATOR); -+ if (last_slash) - { -- if (g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME)) -- { -- folder_file = NULL; -- result = REFRESH_INCOMPLETE_HOSTNAME; -- } -- else -- { -- folder_file = (chooser_entry->base_folder) ? g_object_ref (chooser_entry->base_folder) : NULL; -- -- if (g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_NONEXISTENT)) -- result = REFRESH_NONEXISTENT; -- else -- result = REFRESH_INVALID_INPUT; -- } -- -- if (error) -- g_error_free (error); -- -- file_part = g_strdup (""); -- file_part_pos = -1; -+ dir_part = g_strndup (text, last_slash - text + 1); -+ chooser_entry->file_part = g_strdup (last_slash + 1); - } - else - { -- g_assert (folder_file != NULL); -- -- file_part_len = strlen (file_part); -- total_len = strlen (text); -- if (total_len > file_part_len) -- file_part_pos = g_utf8_strlen (text, total_len - file_part_len); -- else -- file_part_pos = 0; -- -- result = REFRESH_OK; -+ dir_part = g_strdup (""); -+ chooser_entry->file_part = g_strdup (text); - } - -- g_free (text); -- -- g_free (chooser_entry->file_part); -- -- chooser_entry->file_part = file_part; -- chooser_entry->file_part_pos = file_part_pos; -+ folder_file = gtk_file_chooser_get_directory_for_text (chooser_entry, text); - -- if (result == REFRESH_OK) -- { -- result = reload_current_folder (chooser_entry, folder_file, file_part_pos == -1); -- } -- else -- { -- discard_current_folder (chooser_entry); -- discard_loading_and_current_folder_file (chooser_entry); -- } -+ set_completion_folder (chooser_entry, folder_file, dir_part); - - if (folder_file) - g_object_unref (folder_file); - -- g_assert (/* we are OK and we have a current folder file and (loading process or folder handle)... */ -- ((result == REFRESH_OK) -- && (chooser_entry->current_folder_file != NULL) -- && (((chooser_entry->load_folder_cancellable != NULL) && (chooser_entry->current_folder == NULL)) -- || ((chooser_entry->load_folder_cancellable == NULL) && (chooser_entry->current_folder != NULL)))) -- /* ... OR we have an error, and we don't have a current folder file nor a loading process nor a folder handle */ -- || ((result != REFRESH_OK) -- && (chooser_entry->current_folder_file == NULL) -- && (chooser_entry->load_folder_cancellable == NULL) -- && (chooser_entry->current_folder == NULL))); -- -- return result; --} -- --static void --autocomplete (GtkFileChooserEntry *chooser_entry) --{ -- if (!(chooser_entry->current_folder != NULL -- && _gtk_folder_is_finished_loading (chooser_entry->current_folder) -- && gtk_editable_get_position (GTK_EDITABLE (chooser_entry)) == gtk_entry_get_text_length (GTK_ENTRY (chooser_entry)))) -- return; -- -- append_common_prefix (chooser_entry, TRUE, FALSE); --} -- --static void --start_autocompletion (GtkFileChooserEntry *chooser_entry) --{ -- RefreshStatus status; -- -- status = refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION); -+ g_free (dir_part); - -- switch (status) -+ if (chooser_entry->completion_store && -+ (g_strcmp0 (old_file_part, chooser_entry->file_part) != 0)) - { -- case REFRESH_OK: -- g_assert (chooser_entry->current_folder_file != NULL); -+ GtkFileFilter *filter; -+ char *pattern; - -- if (chooser_entry->current_folder && _gtk_folder_is_finished_loading (chooser_entry->current_folder)) -- autocomplete (chooser_entry); -- else -- chooser_entry->load_complete_action = LOAD_COMPLETE_AUTOCOMPLETE; -+ filter = gtk_file_filter_new (); -+ pattern = g_strconcat (chooser_entry->file_part, "*", NULL); -+ gtk_file_filter_add_pattern (filter, pattern); - -- break; -+ g_object_ref_sink (filter); - -- case REFRESH_INVALID_INPUT: -- case REFRESH_INCOMPLETE_HOSTNAME: -- case REFRESH_NONEXISTENT: -- case REFRESH_NOT_LOCAL: -- /* We don't beep or anything, since this is autocompletion - the user -- * didn't request any action explicitly. -- */ -- break; -+ _gtk_file_system_model_set_filter (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store), -+ filter); - -- default: -- g_assert_not_reached (); -+ g_free (pattern); -+ g_object_unref (filter); - } --} -- --static gboolean --start_autocompletion_idle_handler (gpointer data) --{ -- GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data); -- -- start_autocompletion (chooser_entry); - -- chooser_entry->start_autocompletion_idle_id = 0; -- -- return FALSE; --} -- --static void --install_start_autocompletion_idle (GtkFileChooserEntry *chooser_entry) --{ -- if (chooser_entry->start_autocompletion_idle_id != 0) -- return; -- -- chooser_entry->start_autocompletion_idle_id = gdk_threads_add_idle (start_autocompletion_idle_handler, chooser_entry); -+ g_free (text); -+ g_free (old_file_part); - } - - #ifdef G_OS_WIN32 -@@ -1769,7 +746,7 @@ delete_text_callback (GtkFileChooserEntry *chooser_entry, - * Return value: the newly created #GtkFileChooserEntry - **/ - GtkWidget * --_gtk_file_chooser_entry_new (gboolean eat_tabs) -+_gtk_file_chooser_entry_new (gboolean eat_tabs) - { - GtkFileChooserEntry *chooser_entry; - -@@ -1780,29 +757,6 @@ _gtk_file_chooser_entry_new (gboolean eat_tabs) - } - - /** -- * _gtk_file_chooser_entry_set_file_system: -- * @chooser_entry: a #GtkFileChooser -- * @file_system: an object implementing #GtkFileSystem -- * -- * Sets the file system for @chooser_entry. -- **/ --void --_gtk_file_chooser_entry_set_file_system (GtkFileChooserEntry *chooser_entry, -- GtkFileSystem *file_system) --{ -- g_return_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry)); -- g_return_if_fail (GTK_IS_FILE_SYSTEM (file_system)); -- -- if (file_system != chooser_entry->file_system) -- { -- if (chooser_entry->file_system) -- g_object_unref (chooser_entry->file_system); -- -- chooser_entry->file_system = g_object_ref (file_system); -- } --} -- --/** - * _gtk_file_chooser_entry_set_base_folder: - * @chooser_entry: a #GtkFileChooserEntry - * @file: file for a folder in the chooser entries current file system. -@@ -1813,15 +767,23 @@ void - _gtk_file_chooser_entry_set_base_folder (GtkFileChooserEntry *chooser_entry, - GFile *file) - { -+ g_return_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry)); -+ g_return_if_fail (file == NULL || G_IS_FILE (file)); -+ -+ if (chooser_entry->base_folder == file || -+ (file != NULL && chooser_entry->base_folder != NULL -+ && g_file_equal (chooser_entry->base_folder, file))) -+ return; -+ -+ if (file) -+ g_object_ref (file); -+ - if (chooser_entry->base_folder) - g_object_unref (chooser_entry->base_folder); - - chooser_entry->base_folder = file; - -- if (chooser_entry->base_folder) -- g_object_ref (chooser_entry->base_folder); -- -- clear_completions (chooser_entry); -+ refresh_current_folder_and_file_part (chooser_entry); - } - - /** -@@ -1835,14 +797,16 @@ _gtk_file_chooser_entry_set_base_folder (GtkFileChooserEntry *chooser_entry, - * be different. If the user has entered unparsable text, or text which - * the entry cannot handle, this will return %NULL. - * -- * Return value: the file for the current folder - this value is owned by the -- * chooser entry and must not be modified or freed. -+ * Return value: the file for the current folder - you must g_object_unref() -+ * the value after use. - **/ - GFile * - _gtk_file_chooser_entry_get_current_folder (GtkFileChooserEntry *chooser_entry) - { -- commit_completion_and_refresh (chooser_entry); -- return chooser_entry->current_folder_file; -+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry), NULL); -+ -+ return gtk_file_chooser_get_directory_for_text (chooser_entry, -+ gtk_entry_get_text (GTK_ENTRY (chooser_entry))); - } - - /** -@@ -1860,30 +824,20 @@ _gtk_file_chooser_entry_get_current_folder (GtkFileChooserEntry *chooser_entry) - const gchar * - _gtk_file_chooser_entry_get_file_part (GtkFileChooserEntry *chooser_entry) - { -- commit_completion_and_refresh (chooser_entry); -- return chooser_entry->file_part; --} -+ const char *last_slash, *text; - --/** -- * _gtk_file_chooser_entry_set_file_part: -- * @chooser_entry: a #GtkFileChooserEntry -- * @file_part: text to display in the entry, in UTF-8 -- * -- * Sets the current text shown in the file chooser entry. -- **/ --void --_gtk_file_chooser_entry_set_file_part (GtkFileChooserEntry *chooser_entry, -- const gchar *file_part) --{ -- g_return_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry)); -+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry), NULL); - -- chooser_entry->in_change = TRUE; -- clear_completions (chooser_entry); -- gtk_entry_set_text (GTK_ENTRY (chooser_entry), file_part); -- chooser_entry->in_change = FALSE; -+ text = gtk_entry_get_text (GTK_ENTRY (chooser_entry)); -+ last_slash = strrchr (text, G_DIR_SEPARATOR); -+ if (last_slash) -+ return last_slash + 1; -+ else if (is_directory_shortcut (text)) -+ return ""; -+ else -+ return text; - } - -- - /** - * _gtk_file_chooser_entry_set_action: - * @chooser_entry: a #GtkFileChooserEntry -@@ -1920,6 +874,13 @@ _gtk_file_chooser_entry_set_action (GtkFileChooserEntry *chooser_entry, - gtk_entry_completion_set_popup_single_match (comp, TRUE); - break; - } -+ -+ if (chooser_entry->completion_store) -+ _gtk_file_system_model_set_show_files (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store), -+ action == GTK_FILE_CHOOSER_ACTION_OPEN || -+ action == GTK_FILE_CHOOSER_ACTION_SAVE); -+ -+ update_inline_completion (chooser_entry); - } - } - -@@ -1945,22 +906,19 @@ gboolean - _gtk_file_chooser_entry_get_is_folder (GtkFileChooserEntry *chooser_entry, - GFile *file) - { -- gboolean retval = FALSE; -- -- if (chooser_entry->current_folder) -- { -- GFileInfo *file_info; -+ GtkTreeIter iter; -+ GFileInfo *info; - -- file_info = _gtk_folder_get_info (chooser_entry->current_folder, file); -+ if (chooser_entry->completion_store == NULL || -+ !_gtk_file_system_model_get_iter_for_file (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store), -+ &iter, -+ file)) -+ return FALSE; - -- if (file_info) -- { -- retval = _gtk_file_info_consider_as_directory (file_info); -- g_object_unref (file_info); -- } -- } -+ info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store), -+ &iter); - -- return retval; -+ return _gtk_file_info_consider_as_directory (info); - } - - -@@ -1993,7 +951,7 @@ _gtk_file_chooser_entry_set_local_only (GtkFileChooserEntry *chooser_entry, - gboolean local_only) - { - chooser_entry->local_only = local_only; -- clear_completions (chooser_entry); -+ refresh_current_folder_and_file_part (chooser_entry); - } - - gboolean -diff --git a/gtk/gtkfilechooserentry.h b/gtk/gtkfilechooserentry.h -index a9c9f83..216b8ae 100644 ---- a/gtk/gtkfilechooserentry.h -+++ b/gtk/gtkfilechooserentry.h -@@ -33,16 +33,12 @@ G_BEGIN_DECLS - typedef struct _GtkFileChooserEntry GtkFileChooserEntry; - - GType _gtk_file_chooser_entry_get_type (void) G_GNUC_CONST; --GtkWidget * _gtk_file_chooser_entry_new (gboolean eat_tab); -+GtkWidget * _gtk_file_chooser_entry_new (gboolean eat_tab); - void _gtk_file_chooser_entry_set_action (GtkFileChooserEntry *chooser_entry, - GtkFileChooserAction action); - GtkFileChooserAction _gtk_file_chooser_entry_get_action (GtkFileChooserEntry *chooser_entry); --void _gtk_file_chooser_entry_set_file_system (GtkFileChooserEntry *chooser_entry, -- GtkFileSystem *file_system); - void _gtk_file_chooser_entry_set_base_folder (GtkFileChooserEntry *chooser_entry, - GFile *folder); --void _gtk_file_chooser_entry_set_file_part (GtkFileChooserEntry *chooser_entry, -- const gchar *file_part); - GFile * _gtk_file_chooser_entry_get_current_folder (GtkFileChooserEntry *chooser_entry); - const gchar * _gtk_file_chooser_entry_get_file_part (GtkFileChooserEntry *chooser_entry); - gboolean _gtk_file_chooser_entry_get_is_folder (GtkFileChooserEntry *chooser_entry, -diff --git a/gtk/gtkfilesystem.c b/gtk/gtkfilesystem.c -index d0ec4b4..5f9e054 100644 ---- a/gtk/gtkfilesystem.c -+++ b/gtk/gtkfilesystem.c -@@ -680,146 +680,6 @@ _gtk_file_system_list_bookmarks (GtkFileSystem *file_system) - return g_slist_reverse (files); - } - --static gboolean --is_valid_scheme_character (char c) --{ -- return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.'; --} -- --static gboolean --has_uri_scheme (const char *str) --{ -- const char *p; -- -- p = str; -- -- if (!is_valid_scheme_character (*p)) -- return FALSE; -- -- do -- p++; -- while (is_valid_scheme_character (*p)); -- -- return (strncmp (p, "://", 3) == 0); --} -- --gboolean --_gtk_file_system_parse (GtkFileSystem *file_system, -- GFile *base_file, -- const gchar *str, -- GFile **folder, -- gchar **file_part, -- GError **error) --{ -- GFile *file; -- gboolean result = FALSE; -- gboolean is_dir = FALSE; -- gchar *last_slash = NULL; -- gboolean is_uri; -- -- DEBUG ("parse"); -- -- if (str && *str) -- is_dir = (str [strlen (str) - 1] == G_DIR_SEPARATOR); -- -- last_slash = strrchr (str, G_DIR_SEPARATOR); -- -- is_uri = has_uri_scheme (str); -- -- if (is_uri) -- { -- const char *colon; -- const char *slash_after_hostname; -- -- colon = strchr (str, ':'); -- g_assert (colon != NULL); -- g_assert (strncmp (colon, "://", 3) == 0); -- -- slash_after_hostname = strchr (colon + 3, '/'); -- -- if (slash_after_hostname == NULL) -- { -- /* We don't have a full hostname yet. So, don't switch the folder -- * until we have seen a full hostname. Otherwise, completion will -- * happen for every character the user types for the hostname. -- */ -- -- *folder = NULL; -- *file_part = NULL; -- g_set_error (error, -- GTK_FILE_CHOOSER_ERROR, -- GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME, -- "Incomplete hostname"); -- return FALSE; -- } -- } -- -- if (str[0] == '~' || g_path_is_absolute (str) || is_uri) -- file = g_file_parse_name (str); -- else -- { -- if (base_file) -- file = g_file_resolve_relative_path (base_file, str); -- else -- { -- *folder = NULL; -- *file_part = NULL; -- g_set_error (error, -- GTK_FILE_CHOOSER_ERROR, -- GTK_FILE_CHOOSER_ERROR_BAD_FILENAME, -- _("Invalid path")); -- return FALSE; -- } -- } -- -- if (base_file && g_file_equal (base_file, file)) -- { -- /* this is when user types '.', could be the -- * beginning of a hidden file, ./ or ../ -- */ -- *folder = g_object_ref (file); -- *file_part = g_strdup (str); -- result = TRUE; -- } -- else if (is_dir) -- { -- /* it's a dir, or at least it ends with the dir separator */ -- *folder = g_object_ref (file); -- *file_part = g_strdup (""); -- result = TRUE; -- } -- else -- { -- GFile *parent_file; -- -- parent_file = g_file_get_parent (file); -- -- if (!parent_file) -- { -- g_set_error (error, -- GTK_FILE_CHOOSER_ERROR, -- GTK_FILE_CHOOSER_ERROR_NONEXISTENT, -- "Could not get parent file"); -- *folder = NULL; -- *file_part = NULL; -- } -- else -- { -- *folder = parent_file; -- result = TRUE; -- -- if (last_slash) -- *file_part = g_strdup (last_slash + 1); -- else -- *file_part = g_strdup (str); -- } -- } -- -- g_object_unref (file); -- -- return result; --} -- - static void - free_async_data (AsyncFuncData *async_data) - { -@@ -832,79 +692,6 @@ free_async_data (AsyncFuncData *async_data) - } - - static void --enumerate_children_callback (GObject *source_object, -- GAsyncResult *result, -- gpointer user_data) --{ -- GFileEnumerator *enumerator; -- AsyncFuncData *async_data; -- GtkFolder *folder = NULL; -- GFile *file; -- GError *error = NULL; -- -- file = G_FILE (source_object); -- async_data = (AsyncFuncData *) user_data; -- enumerator = g_file_enumerate_children_finish (file, result, &error); -- -- if (enumerator) -- { -- folder = g_object_new (GTK_TYPE_FOLDER, -- "file", source_object, -- "enumerator", enumerator, -- "attributes", async_data->attributes, -- NULL); -- g_object_unref (enumerator); -- } -- -- gdk_threads_enter (); -- ((GtkFileSystemGetFolderCallback) async_data->callback) (async_data->cancellable, -- folder, error, async_data->data); -- gdk_threads_leave (); -- -- free_async_data (async_data); -- -- if (folder) -- g_object_unref (folder); -- -- if (error) -- g_error_free (error); --} -- --GCancellable * --_gtk_file_system_get_folder (GtkFileSystem *file_system, -- GFile *file, -- const gchar *attributes, -- GtkFileSystemGetFolderCallback callback, -- gpointer data) --{ -- GCancellable *cancellable; -- AsyncFuncData *async_data; -- -- g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); -- g_return_val_if_fail (G_IS_FILE (file), NULL); -- -- cancellable = g_cancellable_new (); -- -- async_data = g_new0 (AsyncFuncData, 1); -- async_data->file_system = g_object_ref (file_system); -- async_data->file = g_object_ref (file); -- async_data->cancellable = g_object_ref (cancellable); -- async_data->attributes = g_strdup (attributes); -- -- async_data->callback = callback; -- async_data->data = data; -- -- g_file_enumerate_children_async (file, -- attributes, -- G_FILE_QUERY_INFO_NONE, -- G_PRIORITY_DEFAULT, -- cancellable, -- enumerate_children_callback, -- async_data); -- return cancellable; --} -- --static void - query_info_callback (GObject *source_object, - GAsyncResult *result, - gpointer user_data) -diff --git a/gtk/gtkfilesystem.h b/gtk/gtkfilesystem.h -index 6f3be36..7fc539e 100644 ---- a/gtk/gtkfilesystem.h -+++ b/gtk/gtkfilesystem.h -@@ -100,18 +100,6 @@ GtkFileSystem * _gtk_file_system_new (void); - GSList * _gtk_file_system_list_volumes (GtkFileSystem *file_system); - GSList * _gtk_file_system_list_bookmarks (GtkFileSystem *file_system); - --gboolean _gtk_file_system_parse (GtkFileSystem *file_system, -- GFile *base_file, -- const gchar *str, -- GFile **folder, -- gchar **file_part, -- GError **error); -- --GCancellable * _gtk_file_system_get_folder (GtkFileSystem *file_system, -- GFile *file, -- const gchar *attributes, -- GtkFileSystemGetFolderCallback callback, -- gpointer data); - GCancellable * _gtk_file_system_get_info (GtkFileSystem *file_system, - GFile *file, - const gchar *attributes, -diff --git a/gtk/gtkfilesystemmodel.c b/gtk/gtkfilesystemmodel.c -index 1ba14d3..840f1e8 100644 ---- a/gtk/gtkfilesystemmodel.c -+++ b/gtk/gtkfilesystemmodel.c -@@ -45,6 +45,9 @@ - * the special kind of usage for "search" and "recent-files", where the file chooser gives the model the - * files to be displayed. - * -+ * Internal data structure -+ * ----------------------- -+ * - * Each file is kept in a FileModelNode structure. Each FileModelNode holds a GFile* and other data. All the - * node structures have the same size, determined at runtime, depending on the number of columns that were passed - * to _gtk_file_system_model_new() or _gtk_file_system_model_new_for_directory() (that is, the size of a node is -@@ -69,7 +72,14 @@ - * - * Each FileModelNode has a node->visible field, which indicates whether the node is visible in the GtkTreeView. - * A node may be invisible if, for example, it corresponds to a hidden file and the file chooser is not showing -- * hidden files. -+ * hidden files. Also, a file filter may be explicitly set onto the model, for example, to only show files that -+ * match "*.jpg". In this case, node->filtered_out says whether the node failed the filter. The ultimate -+ * decision on whether a node is visible or not in the treeview is distilled into the node->visible field. -+ * The reason for having a separate node->filtered_out field is so that the file chooser can query whether -+ * a (filtered-out) folder should be made sensitive in the GUI. -+ * -+ * Visible rows vs. possibly-invisible nodes -+ * ----------------------------------------- - * - * Since not all nodes in the model->files array may be visible, we need a way to map visible row indexes from - * the treeview to array indexes in our array of files. And thus we introduce a bit of terminology: -@@ -98,6 +108,16 @@ - * - * You never access a node->row directly. Instead, call node_get_tree_row(). That function will validate the nodes - * up to the sought one if the node is not valid yet, and it will return a proper 0-based row. -+ * -+ * Sorting -+ * ------- -+ * -+ * The model implements the GtkTreeSortable interface. To avoid re-sorting -+ * every time a node gets added (which would lead to O(n^2) performance during -+ * the initial population of the model), the model can freeze itself (with -+ * freeze_updates()) during the intial population process. When the model is -+ * frozen, sorting will not happen. The model will sort itself when the freeze -+ * count goes back to zero, via corresponding calls to thaw_updates(). - */ - - /*** DEFINES ***/ -@@ -123,6 +143,7 @@ struct _FileModelNode - */ - - guint visible :1; /* if the file is currently visible */ -+ guint filtered_out :1;/* if the file is currently filtered out (i.e. it didn't pass the filters) */ - guint frozen_add :1; /* true if the model was frozen and the entry has not been added yet */ - - GValue values[1]; /* actually n_columns values */ -@@ -170,6 +191,7 @@ struct _GtkFileSystemModel - guint show_hidden :1; /* whether to show hidden files */ - guint show_folders :1;/* whether to show folders */ - guint show_files :1; /* whether to show files */ -+ guint filter_folders :1;/* whether filter applies to folders */ - }; - - #define GTK_FILE_SYSTEM_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass)) -@@ -185,6 +207,12 @@ struct _GtkFileSystemModelClass - void (*finished_loading) (GtkFileSystemModel *model, GError *error); - }; - -+static void freeze_updates (GtkFileSystemModel *model); -+static void thaw_updates (GtkFileSystemModel *model); -+ -+static guint node_get_for_file (GtkFileSystemModel *model, -+ GFile *file); -+ - static void add_file (GtkFileSystemModel *model, - GFile *file, - GFileInfo *info); -@@ -264,13 +292,13 @@ node_invalidate_index (GtkFileSystemModel *model, guint id) - } - - static GtkTreePath * --gtk_tree_path_new_from_node (GtkFileSystemModel *model, guint id) -+tree_path_new_from_node (GtkFileSystemModel *model, guint id) - { -- guint i = node_get_tree_row (model, id); -+ guint r = node_get_tree_row (model, id); - -- g_assert (i < model->files->len); -+ g_assert (r < model->files->len); - -- return gtk_tree_path_new_from_indices (i, -1); -+ return gtk_tree_path_new_from_indices (r, -1); - } - - static void -@@ -279,7 +307,7 @@ emit_row_inserted_for_node (GtkFileSystemModel *model, guint id) - GtkTreePath *path; - GtkTreeIter iter; - -- path = gtk_tree_path_new_from_node (model, id); -+ path = tree_path_new_from_node (model, id); - ITER_INIT_FROM_INDEX (model, &iter, id); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter); - gtk_tree_path_free (path); -@@ -291,7 +319,7 @@ emit_row_changed_for_node (GtkFileSystemModel *model, guint id) - GtkTreePath *path; - GtkTreeIter iter; - -- path = gtk_tree_path_new_from_node (model, id); -+ path = tree_path_new_from_node (model, id); - ITER_INIT_FROM_INDEX (model, &iter, id); - gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter); - gtk_tree_path_free (path); -@@ -308,10 +336,21 @@ emit_row_deleted_for_row (GtkFileSystemModel *model, guint row) - } - - static void --node_set_visible (GtkFileSystemModel *model, guint id, gboolean visible) -+node_set_visible_and_filtered_out (GtkFileSystemModel *model, guint id, gboolean visible, gboolean filtered_out) - { - FileModelNode *node = get_node (model, id); - -+ /* Filteredness */ -+ -+ if (node->filtered_out != filtered_out) -+ { -+ node->filtered_out = filtered_out; -+ if (node->visible && visible) -+ emit_row_changed_for_node (model, id); -+ } -+ -+ /* Visibility */ -+ - if (node->visible == visible || - node->frozen_add) - return; -@@ -336,35 +375,21 @@ node_set_visible (GtkFileSystemModel *model, guint id, gboolean visible) - } - - static gboolean --node_should_be_visible (GtkFileSystemModel *model, guint id) -+node_should_be_filtered_out (GtkFileSystemModel *model, guint id) - { - FileModelNode *node = get_node (model, id); - GtkFileFilterInfo filter_info = { 0, }; - GtkFileFilterFlags required; -- gboolean is_folder, result; -+ gboolean result; - char *mime_type = NULL; - char *filename = NULL; - char *uri = NULL; - - if (node->info == NULL) -- return FALSE; -- -- if (!model->show_hidden && -- (g_file_info_get_is_hidden (node->info) || g_file_info_get_is_backup (node->info))) -- return FALSE; -- -- is_folder = _gtk_file_info_consider_as_directory (node->info); -- -- /* wtf? */ -- if (model->show_folders != model->show_files && -- model->show_folders != is_folder) -- return FALSE; -- -- if (is_folder) - return TRUE; - - if (model->filter == NULL) -- return TRUE; -+ return FALSE; - - /* fill info */ - required = gtk_file_filter_get_needed (model->filter); -@@ -406,7 +431,7 @@ node_should_be_visible (GtkFileSystemModel *model, guint id) - } - } - -- result = gtk_file_filter_filter (model->filter, &filter_info); -+ result = !gtk_file_filter_filter (model->filter, &filter_info); - - g_free (mime_type); - g_free (filename); -@@ -415,6 +440,50 @@ node_should_be_visible (GtkFileSystemModel *model, guint id) - return result; - } - -+static gboolean -+node_should_be_visible (GtkFileSystemModel *model, guint id, gboolean filtered_out) -+{ -+ FileModelNode *node = get_node (model, id); -+ gboolean result; -+ -+ if (node->info == NULL) -+ return FALSE; -+ -+ if (!model->show_hidden && -+ (g_file_info_get_is_hidden (node->info) || g_file_info_get_is_backup (node->info))) -+ return FALSE; -+ -+ if (_gtk_file_info_consider_as_directory (node->info)) -+ { -+ if (!model->show_folders) -+ return FALSE; -+ -+ if (!model->filter_folders) -+ return TRUE; -+ } -+ else -+ { -+ if (!model->show_files) -+ return FALSE; -+ } -+ -+ result = !filtered_out; -+ -+ return result; -+} -+ -+static void -+node_compute_visibility_and_filters (GtkFileSystemModel *model, guint id) -+{ -+ gboolean filtered_out; -+ gboolean visible; -+ -+ filtered_out = node_should_be_filtered_out (model, id); -+ visible = node_should_be_visible (model, id, filtered_out); -+ -+ node_set_visible_and_filtered_out (model, id, visible, filtered_out); -+} -+ - /*** GtkTreeModel ***/ - - static GtkTreeModelFlags -@@ -513,6 +582,9 @@ gtk_file_system_model_get_iter (GtkTreeModel *tree_model, - { - g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE); - -+ if (gtk_tree_path_get_depth (path) > 1) -+ return FALSE; -+ - return gtk_file_system_model_iter_nth_child (tree_model, - iter, - NULL, -@@ -527,7 +599,7 @@ gtk_file_system_model_get_path (GtkTreeModel *tree_model, - - g_return_val_if_fail (ITER_IS_VALID (model, iter), NULL); - -- return gtk_tree_path_new_from_node (model, ITER_INDEX (iter)); -+ return tree_path_new_from_node (model, ITER_INDEX (iter)); - } - - static void -@@ -1033,6 +1105,7 @@ _gtk_file_system_model_init (GtkFileSystemModel *model) - model->show_files = TRUE; - model->show_folders = TRUE; - model->show_hidden = FALSE; -+ model->filter_folders = FALSE; - - model->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID; - -@@ -1053,7 +1126,7 @@ thaw_func (gpointer data) - { - GtkFileSystemModel *model = data; - -- _gtk_file_system_model_thaw_updates (model); -+ thaw_updates (model); - model->dir_thaw_source = 0; - - return FALSE; -@@ -1075,7 +1148,7 @@ gtk_file_system_model_got_files (GObject *object, GAsyncResult *res, gpointer da - { - if (model->dir_thaw_source == 0) - { -- _gtk_file_system_model_freeze_updates (model); -+ freeze_updates (model); - model->dir_thaw_source = gdk_threads_add_timeout_full (IO_PRIORITY + 1, - 50, - thaw_func, -@@ -1124,7 +1197,7 @@ gtk_file_system_model_got_files (GObject *object, GAsyncResult *res, gpointer da - { - g_source_remove (model->dir_thaw_source); - model->dir_thaw_source = 0; -- _gtk_file_system_model_thaw_updates (model); -+ thaw_updates (model); - } - - g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error); -@@ -1145,13 +1218,21 @@ gtk_file_system_model_query_done (GObject * object, - GtkFileSystemModel *model = data; /* only a valid pointer if not cancelled */ - GFile *file = G_FILE (object); - GFileInfo *info; -+ guint id; - - info = g_file_query_info_finish (file, res, NULL); - if (info == NULL) - return; - - gdk_threads_enter (); -- _gtk_file_system_model_update_file (model, file, info, TRUE); -+ -+ _gtk_file_system_model_update_file (model, file, info); -+ -+ id = node_get_for_file (model, file); -+ gtk_file_system_model_sort_node (model, id); -+ -+ g_object_unref (info); -+ - gdk_threads_leave (); - } - -@@ -1389,16 +1470,14 @@ gtk_file_system_model_refilter_all (GtkFileSystemModel *model) - return; - } - -- _gtk_file_system_model_freeze_updates (model); -+ freeze_updates (model); - - /* start at index 1, don't change the editable */ - for (i = 1; i < model->files->len; i++) -- { -- node_set_visible (model, i, node_should_be_visible (model, i)); -- } -+ node_compute_visibility_and_filters (model, i); - - model->filter_on_thaw = FALSE; -- _gtk_file_system_model_thaw_updates (model); -+ thaw_updates (model); - } - - /** -@@ -1472,6 +1551,30 @@ _gtk_file_system_model_set_show_files (GtkFileSystemModel *model, - } - - /** -+ * _gtk_file_system_model_set_filter_folders: -+ * @model: a #GtkFileSystemModel -+ * @filter_folders: whether the filter applies to folders -+ * -+ * Sets whether the filter set by _gtk_file_system_model_set_filter() -+ * applies to folders. By default, it does not and folders are always -+ * visible. -+ **/ -+void -+_gtk_file_system_model_set_filter_folders (GtkFileSystemModel *model, -+ gboolean filter_folders) -+{ -+ g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); -+ -+ filter_folders = filter_folders != FALSE; -+ -+ if (filter_folders != model->filter_folders) -+ { -+ model->filter_folders = filter_folders; -+ gtk_file_system_model_refilter_all (model); -+ } -+} -+ -+/** - * _gtk_file_system_model_get_cancellable: - * @model: the model - * -@@ -1498,7 +1601,7 @@ _gtk_file_system_model_get_cancellable (GtkFileSystemModel *model) - * Checks if the iterator is visible. A visible iterator references - * a row that is currently exposed using the #GtkTreeModel API. If - * the iterator is invisible, it references a file that is not shown -- * for some reason, such as being filtered by the current filter or -+ * for some reason, such as being filtered out by the current filter or - * being a hidden file. - * - * Returns: %TRUE if the iterator is visible -@@ -1517,6 +1620,32 @@ _gtk_file_system_model_iter_is_visible (GtkFileSystemModel *model, - } - - /** -+ * _gtk_file_system_model_iter_is_filtered_out: -+ * @model: the model -+ * @iter: a valid iterator -+ * -+ * Checks if the iterator is filtered out. This is only useful for rows -+ * that refer to folders, as those are always visible regardless -+ * of what the current filter says. This function lets you see -+ * the results of the filter. -+ * -+ * Returns: %TRUE if the iterator passed the current filter; %FALSE if the -+ * filter would not have let the row pass. -+ **/ -+gboolean -+_gtk_file_system_model_iter_is_filtered_out (GtkFileSystemModel *model, -+ GtkTreeIter *iter) -+{ -+ FileModelNode *node; -+ -+ g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), FALSE); -+ g_return_val_if_fail (iter != NULL, FALSE); -+ -+ node = get_node (model, ITER_INDEX (iter)); -+ return node->filtered_out; -+} -+ -+/** - * _gtk_file_system_model_get_info: - * @model: a #GtkFileSystemModel - * @iter: a #GtkTreeIter pointing to a row of @model -@@ -1683,6 +1812,33 @@ _gtk_file_system_model_get_iter_for_file (GtkFileSystemModel *model, - return TRUE; - } - -+/* When an element is added or removed to the model->files array, we need to -+ * update the model->file_lookup mappings of (node, index), as the indexes -+ * change. This function adds the specified increment to the index in that pair -+ * if the index is equal or after the specified id. We use this to slide the -+ * mappings up or down when a node is added or removed, respectively. -+ */ -+static void -+adjust_file_lookup (GtkFileSystemModel *model, guint id, int increment) -+{ -+ GHashTableIter iter; -+ gpointer key; -+ gpointer value; -+ -+ g_hash_table_iter_init (&iter, model->file_lookup); -+ -+ while (g_hash_table_iter_next (&iter, &key, &value)) -+ { -+ guint index = GPOINTER_TO_UINT (value); -+ -+ if (index >= id) -+ { -+ index += increment; -+ g_hash_table_iter_replace (&iter, GUINT_TO_POINTER (index)); -+ } -+ } -+} -+ - /** - * add_file: - * @model: the model -@@ -1713,8 +1869,8 @@ add_file (GtkFileSystemModel *model, - g_slice_free1 (model->node_size, node); - - if (!model->frozen) -- node_set_visible (model, model->files->len -1, -- node_should_be_visible (model, model->files->len - 1)); -+ node_compute_visibility_and_filters (model, model->files->len -1); -+ - gtk_file_system_model_sort_node (model, model->files->len -1); - } - -@@ -1732,7 +1888,9 @@ remove_file (GtkFileSystemModel *model, - GFile *file) - { - FileModelNode *node; -+ gboolean was_visible; - guint id; -+ guint row; - - g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); - g_return_if_fail (G_IS_FILE (file)); -@@ -1742,17 +1900,24 @@ remove_file (GtkFileSystemModel *model, - return; - - node = get_node (model, id); -- node_set_visible (model, id, FALSE); -+ was_visible = node->visible; -+ row = node_get_tree_row (model, id); -+ -+ node_invalidate_index (model, id); - - g_hash_table_remove (model->file_lookup, file); - g_object_unref (node->file); -+ adjust_file_lookup (model, id, -1); - - if (node->info) - g_object_unref (node->info); - - g_array_remove_index (model->files, id); -- g_hash_table_remove_all (model->file_lookup); -- /* We don't need to resort, as removing a row doesn't change the sorting order */ -+ -+ /* We don't need to resort, as removing a row doesn't change the sorting order of the other rows */ -+ -+ if (was_visible) -+ emit_row_deleted_for_row (model, row); - } - - /** -@@ -1760,7 +1925,6 @@ remove_file (GtkFileSystemModel *model, - * @model: the model - * @file: the file - * @info: the new file info -- * @requires_resort: FIXME: get rid of this argument - * - * Tells the file system model that the file changed and that the - * new @info should be used for it now. If the file is not part of -@@ -1769,8 +1933,7 @@ remove_file (GtkFileSystemModel *model, - void - _gtk_file_system_model_update_file (GtkFileSystemModel *model, - GFile *file, -- GFileInfo *info, -- gboolean requires_resort) -+ GFileInfo *info) - { - FileModelNode *node; - guint i, id; -@@ -1802,9 +1965,6 @@ _gtk_file_system_model_update_file (GtkFileSystemModel *model, - - if (node->visible) - emit_row_changed_for_node (model, id); -- -- if (requires_resort) -- gtk_file_system_model_sort_node (model, id); - } - - /** -@@ -1813,7 +1973,8 @@ _gtk_file_system_model_update_file (GtkFileSystemModel *model, - * @filter: (allow-none): %NULL or filter to use - * - * Sets a filter to be used for deciding if a row should be visible or not. -- * Directories are always visible. -+ * Whether this filter applies to directories can be toggled with -+ * _gtk_file_system_model_set_filter_folders(). - **/ - void - _gtk_file_system_model_set_filter (GtkFileSystemModel *model, -@@ -1837,54 +1998,16 @@ _gtk_file_system_model_set_filter (GtkFileSystemModel *model, - } - - /** -- * _gtk_file_system_model_add_editable: -- * @model: a #GtkFileSystemModel -- * @iter: Location to return the iter corresponding to the editable row -- * -- * Adds an "empty" row at the beginning of the model. This does not refer to -- * any file, but is a temporary placeholder for a file name that the user will -- * type when a corresponding cell is made editable. When your code is done -- * using this temporary row, call _gtk_file_system_model_remove_editable(). -- **/ --void --_gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *iter) --{ -- g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); -- g_return_if_fail (!get_node (model, 0)->visible); -- -- node_set_visible (model, 0, TRUE); -- ITER_INIT_FROM_INDEX (model, iter, 0); --} -- --/** -- * _gtk_file_system_model_remove_editable: -- * @model: a #GtkFileSystemModel -- * -- * Removes the "empty" row at the beginning of the model that was -- * created with _gtk_file_system_model_add_editable(). You should call -- * this function when your code is finished editing this temporary row. -- **/ --void --_gtk_file_system_model_remove_editable (GtkFileSystemModel *model) --{ -- g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); -- g_return_if_fail (get_node (model, 0)->visible); -- -- node_set_visible (model, 0, FALSE); --} -- --/** -- * _gtk_file_system_model_freeze_updates: -+ * freeze_updates: - * @model: a #GtkFileSystemModel - * -- * Freezes most updates on the model, so that performing multiple -- * operations on the files in the model do not cause any events. -- * Use _gtk_file_system_model_thaw_updates() to resume proper -- * operations. It is fine to call this function multiple times as -- * long as freeze and thaw calls are balanced. -+ * Freezes most updates on the model, so that performing multiple operations on -+ * the files in the model do not cause any events. Use thaw_updates() to resume -+ * proper operations. It is fine to call this function multiple times as long as -+ * freeze and thaw calls are balanced. - **/ --void --_gtk_file_system_model_freeze_updates (GtkFileSystemModel *model) -+static void -+freeze_updates (GtkFileSystemModel *model) - { - g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); - -@@ -1892,14 +2015,13 @@ _gtk_file_system_model_freeze_updates (GtkFileSystemModel *model) - } - - /** -- * _gtk_file_system_model_thaw_updates: -+ * thaw_updates: - * @model: a #GtkFileSystemModel - * -- * Undoes the effect of a previous call to -- * _gtk_file_system_model_freeze_updates() -+ * Undoes the effect of a previous call to freeze_updates() - **/ --void --_gtk_file_system_model_thaw_updates (GtkFileSystemModel *model) -+static void -+thaw_updates (GtkFileSystemModel *model) - { - gboolean stuff_added; - -@@ -1927,7 +2049,7 @@ _gtk_file_system_model_thaw_updates (GtkFileSystemModel *model) - if (!node->frozen_add) - continue; - node->frozen_add = FALSE; -- node_set_visible (model, i, node_should_be_visible (model, i)); -+ node_compute_visibility_and_filters (model, i); - } - } - } -@@ -2012,3 +2134,47 @@ _gtk_file_system_model_add_and_query_file (GtkFileSystemModel *model, - gtk_file_system_model_query_done, - model); - } -+ -+/** -+ * _gtk_file_system_model_add_editable: -+ * @model: a #GtkFileSystemModel -+ * @iter: Location to return the iter corresponding to the editable row -+ * -+ * Adds an “empty” row at the beginning of the model. This does not refer to -+ * any file, but is a temporary placeholder for a file name that the user will -+ * type when a corresponding cell is made editable. When your code is done -+ * using this temporary row, call _gtk_file_system_model_remove_editable(). -+ **/ -+void -+_gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *iter) -+{ -+ g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); -+ g_return_if_fail (!get_node (model, 0)->visible); -+ -+ node_set_visible_and_filtered_out (model, 0, TRUE, FALSE); -+ ITER_INIT_FROM_INDEX (model, iter, 0); -+ -+ /* we don't want file system changes to affect the model while -+ * editing is in place -+ */ -+ freeze_updates (model); -+} -+ -+/** -+ * _gtk_file_system_model_remove_editable: -+ * @model: a #GtkFileSystemModel -+ * -+ * Removes the “empty” row at the beginning of the model that was -+ * created with _gtk_file_system_model_add_editable(). You should call -+ * this function when your code is finished editing this temporary row. -+ **/ -+void -+_gtk_file_system_model_remove_editable (GtkFileSystemModel *model) -+{ -+ g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); -+ g_return_if_fail (get_node (model, 0)->visible); -+ -+ thaw_updates (model); -+ -+ node_set_visible_and_filtered_out (model, 0, FALSE, FALSE); -+} -diff --git a/gtk/gtkfilesystemmodel.h b/gtk/gtkfilesystemmodel.h -index a6fbab9..46743f1 100644 ---- a/gtk/gtkfilesystemmodel.h -+++ b/gtk/gtkfilesystemmodel.h -@@ -55,6 +55,8 @@ GtkFileSystemModel *_gtk_file_system_model_new_for_directory(GFile * - GCancellable * _gtk_file_system_model_get_cancellable (GtkFileSystemModel *model); - gboolean _gtk_file_system_model_iter_is_visible (GtkFileSystemModel *model, - GtkTreeIter *iter); -+gboolean _gtk_file_system_model_iter_is_filtered_out (GtkFileSystemModel *model, -+ GtkTreeIter *iter); - GFileInfo * _gtk_file_system_model_get_info (GtkFileSystemModel *model, - GtkTreeIter *iter); - gboolean _gtk_file_system_model_get_iter_for_file(GtkFileSystemModel *model, -@@ -71,8 +73,7 @@ void _gtk_file_system_model_add_and_query_file (GtkFileSystemMode - const char *attributes); - void _gtk_file_system_model_update_file (GtkFileSystemModel *model, - GFile *file, -- GFileInfo *info, -- gboolean requires_resort); -+ GFileInfo *info); - - void _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model, - gboolean show_hidden); -@@ -80,8 +81,8 @@ void _gtk_file_system_model_set_show_folders (GtkFileSystemModel - gboolean show_folders); - void _gtk_file_system_model_set_show_files (GtkFileSystemModel *model, - gboolean show_files); --void _gtk_file_system_model_freeze_updates (GtkFileSystemModel *model); --void _gtk_file_system_model_thaw_updates (GtkFileSystemModel *model); -+void _gtk_file_system_model_set_filter_folders (GtkFileSystemModel *model, -+ gboolean show_folders); - void _gtk_file_system_model_clear_cache (GtkFileSystemModel *model, - int column); - -diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c -index cac29ba..7350793 100644 ---- a/gtk/gtkfilechooserentry.c -+++ b/gtk/gtkfilechooserentry.c -@@ -263,6 +263,8 @@ match_selected_callback (GtkEntryCompletion *completion, - -1, - &pos); - -+ gtk_editable_set_position (GTK_EDITABLE (chooser_entry), pos); -+ - g_free (path); - - return TRUE; diff --git a/gtk2.changes b/gtk2.changes index ed8835d..c15d5b3 100644 --- a/gtk2.changes +++ b/gtk2.changes @@ -1,3 +1,18 @@ +------------------------------------------------------------------- +Tue Sep 13 03:13:00 UTC 2016 - mgorse@suse.com + +- Update to version 2.24.31: + + backport many file chooser entry fixes and cleanups + + don't crash if invisible files are deleted + + Bugs fixed: bgo#555087, bgo#586367, bgo#635287, bgo#640698, + bgo#648419, bgo#672271, bgo#679333, bgo#687196, bgo#703220 + (CVE-2013-7447), bgo#720330, bgo#729927, bgo#737777, bgo#752707, + bgo#756450, bgo#765120, bgo#765193, bgo#768163, bgo#764996, + bgo#769126 +- Rebase gtk2-bgo743166-remember-printing-authentication.patch. +- Drop gtk2-bgo737777-fix-printing-authentication-crash.patch and + gtk2-bnc957400-filechooserentry-update.patch: fixed upstream. + ------------------------------------------------------------------- Thu Aug 11 10:14:03 UTC 2016 - dimstar@opensuse.org diff --git a/gtk2.spec b/gtk2.spec index 51944de..b34707f 100644 --- a/gtk2.spec +++ b/gtk2.spec @@ -21,7 +21,7 @@ Name: gtk2 %define _name gtk+ -Version: 2.24.30 +Version: 2.24.31 Release: 0 # FIXME: when updating to next version, check whether we can remove the workaround for bgo#596977 below (removing -fomit-frame-pointer) Summary: The GTK+ toolkit library (version 2) @@ -57,10 +57,6 @@ Patch55: gtk2-default-printer.patch Patch56: gtk2-bgo625202-30-bit-drawables-remain-black.patch # PATCH-FIX-UPSTREAM gtk2-bgo743166-remember-printing-authentication.patch bgo#674264 joschibrauchle@gmx.de -- Credentials from gnome-keyring is not used while printing in GTK 2 Patch57: gtk2-bgo743166-remember-printing-authentication.patch -# PATCH-FIX-UPSTREAM gtk2-bgo737777-fix-printing-authentication-crash.patch bgo#737777 joschibrauchle@gmx.de -- Applications crash randomly while printing with a password-secured SMB printer -Patch58: gtk2-bgo737777-fix-printing-authentication-crash.patch -# PATCH-FIX-UPSTREAM gtk2-bnc957400-filechooserentry-update.patch bnc#957400 federico@suse.com -- Update the Tab completion code in GtkFileChooser working same as in gtk3(solved differently in git) -Patch59: gtk2-bnc957400-filechooserentry-update.patch BuildRequires: atk-devel BuildRequires: cairo-devel BuildRequires: cups-devel @@ -340,8 +336,6 @@ cp -a %{SOURCE2} . %patch55 -p1 %patch56 -p1 %patch57 -p1 -%patch58 -p1 -%patch59 -p1 gnome-patch-translation-update %build