diff --git a/gtk2-bnc957400-filechooserentry-update.patch b/gtk2-bnc957400-filechooserentry-update.patch new file mode 100644 index 0000000..2f7a9fc --- /dev/null +++ b/gtk2-bnc957400-filechooserentry-update.patch @@ -0,0 +1,3514 @@ +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 b73cea1..ed8835d 100644 --- a/gtk2.changes +++ b/gtk2.changes @@ -3,6 +3,19 @@ Thu Aug 11 10:14:03 UTC 2016 - dimstar@opensuse.org - Fix update-alterntatives usage. +------------------------------------------------------------------- +Thu Apr 14 11:39:01 UTC 2016 - idonmez@suse.com + +- Update to GNOME 3.20 Fate#318572 + +------------------------------------------------------------------- +Thu Mar 10 16:10:15 CST 2016 - federico@suse.com + +- Add gtk2-bnc957400-filechooserentry-update.patch. This updates + the Tab completion code in GtkFileChooser, so it works the same + as in gtk3 (bnc#957400). The patch was associated with upstream + commits: 55764ddd, 191d1cd90, 92d45e12, 8f9487f + ------------------------------------------------------------------- Sun Mar 6 10:56:37 UTC 2016 - zaitor@opensuse.org @@ -50,11 +63,11 @@ Mon Aug 31 08:17:36 UTC 2015 - dimstar@opensuse.org Mon Jun 1 11:16:14 UTC 2015 - joschibrauchle@gmx.de - Add two patches to fix boo#933034: - + Add gtk2-bgo743166-remember-printing-authentication.patch: - Allow credentials from gnome-keyring to be used for printing + + Add gtk2-bgo743166-remember-printing-authentication.patch: + Allow credentials from gnome-keyring to be used for printing in GTK 2 (bgo#743166) - + Add gtk2-bgo737777-fix-printing-authentication-crash.patch: - Fix applications from randomly crashing while printing with + + Add gtk2-bgo737777-fix-printing-authentication-crash.patch: + Fix applications from randomly crashing while printing with a password-secured SMB printer (bgo#737777) ------------------------------------------------------------------- @@ -130,7 +143,7 @@ Tue Aug 05 12:00:00 UTC 2014 - ku.b@gmx.de - Add gtk2-bgo625202-30-bit-drawables-remain-black.patch: fix 30-bit drawables remain black (bgo#625202). - + ------------------------------------------------------------------- Tue Jul 1 12:52:34 UTC 2014 - dimstar@opensuse.org @@ -170,7 +183,7 @@ Wed Oct 30 20:56:50 UTC 2013 - mgorse@suse.com - Add gtk2-print-to-file.patch: fix printing to file with no directory specified (bnc#839089). - + ------------------------------------------------------------------- Fri Oct 11 15:27:31 UTC 2013 - dimstar@opensuse.org @@ -1007,21 +1020,21 @@ Tue Feb 23 23:03:13 CET 2010 - lmedinas@opensuse.org - bgo#610701: gnome-shell crashes frequently - bgo#604799: Crash when button is pressed. - bgo#609744: crash at parse_data_for_row_pseudocolor - - bgo#600789: gdk/gdkwindow.c "find_native_sibling_above" will + - bgo#600789: gdk/gdkwindow.c "find_native_sibling_above" will crash - - bgo#610141: gtk_assistant_get_nth_page() function fails to + - bgo#610141: gtk_assistant_get_nth_page() function fails to deliver... - - bgo#609952: destroying a notebook window when the last tab + - bgo#609952: destroying a notebook window when the last tab got dragged... - bgo#603923: [annotations] gtk_tree_store_newv/set_column_types - bgo#610474: [annotations] Add allow-none - bgo#609650: GtkPlug Embedded signal is not emitted when plug.. - bgo#610381: More space between toolbar icon and label - bgo#548026: No accessor for GtkWidget.requisition - - bgo#609514: fix introspection comments for + - bgo#609514: fix introspection comments for gtk_tree_view_get_path_at_pos - bgo#610235: msgid in bad English - - bgo#607697: GDK_META_MASK always set on Alt-Enter with gtk+ + - bgo#607697: GDK_META_MASK always set on Alt-Enter with gtk+ 2.19.x - bgo#610632: gtk_info_bar_set_default_response problem - bgo#609172: gdk/directfb: little cleanups @@ -1923,7 +1936,7 @@ Wed Feb 18 03:16:41 CET 2009 - mboman@suse.de + GDK * Use Xrandr 1.3 for tracking monitor information, if available - + Bugs fixed: + + Bugs fixed: * bgo#538439 tooltip may appear in upper left corner... * bgo#571015 libprintbackend-cups has unlocalized strings * bgo#353196 Add a file-set signal to GtkFileChooserButton @@ -1974,10 +1987,10 @@ Tue Feb 03 08:37:18 EST 2009 - mboman@suse.de Sat Jan 31 08:37:18 EST 2009 - mboman@suse.de - Update to version 2.15.2: - + Too many fixes/changes to list. Please see NEWS for full details + + Too many fixes/changes to list. Please see NEWS for full details - Remove gtk2-191223-java-selection-fix-updated.patch. Fixed upstream - Add gtk2-fix-abuild-error.patch to make rpmlint happy - + ------------------------------------------------------------------- Fri Jan 23 07:45:30 CET 2009 - vuntz@novell.com @@ -1987,7 +2000,7 @@ Fri Jan 23 07:45:30 CET 2009 - vuntz@novell.com Mon Jan 12 09:55:06 EST 2009 - mboman@suse.de - Update to version 2.15.0: - + Too many fixes/changes to list. Please see NEWS for full details + + Too many fixes/changes to list. Please see NEWS for full details - Remove gtk2-set-invisible-char-to-circle.patch. Configurable now - Comment out gtk2-bnc355503-file-chooser-remember-geometry.diff for now. Should include this patch once it's fixed properly @@ -2064,15 +2077,15 @@ Mon Nov 24 12:34:56 CET 2008 - olh@suse.de Wed Nov 19 03:54:57 CET 2008 - pwu@suse.de - Change gtk_selection_data_get_targets to java X Selection type. - The java X Selection "TARGETS"'s type is "TARGETS" not "ATOM". + The java X Selection "TARGETS"'s type is "TARGETS" not "ATOM". Fixes https://bugzilla.novell.com/show_bug.cgi?id=191223. - Add new patch gtk2-191223-java-selection-fix-updated.patch, + Add new patch gtk2-191223-java-selection-fix-updated.patch, updated version for sle11. ------------------------------------------------------------------- Tue Nov 11 16:54:14 CET 2008 - ro@suse.de -- SLE-11 uses PPC64 instead of PPC, adapt baselibs.conf +- SLE-11 uses PPC64 instead of PPC, adapt baselibs.conf ------------------------------------------------------------------- Thu Nov 6 10:07:11 CST 2008 - federico@novell.com @@ -2121,12 +2134,12 @@ Mon Oct 6 16:03:28 CEST 2008 - sbrabec@suse.cz ------------------------------------------------------------------- Mon Sep 29 00:26:51 CEST 2008 - ro@suse.de -- add gcc-c++ to buildrequires to work around libtool problem +- add gcc-c++ to buildrequires to work around libtool problem ------------------------------------------------------------------- Mon Sep 29 00:26:22 CEST 2008 - ro@suse.de -- add gcc-c++ to buildrequires to work around libtool problem +- add gcc-c++ to buildrequires to work around libtool problem ------------------------------------------------------------------- Mon Sep 8 18:55:08 CDT 2008 - maw@suse.de @@ -2291,7 +2304,7 @@ Fri Jun 6 21:51:09 CEST 2008 - maw@suse.de more monitor information + Accessibility: * The gail module is now shipped as part of GTK+ - * GtkStatusIcon supports keyboard navigation + * GtkStatusIcon supports keyboard navigation + GtkCalendar: * Support displaying details for each day + GtkBuilder: @@ -2300,11 +2313,11 @@ Fri Jun 6 21:51:09 CEST 2008 - maw@suse.de * Support accessibility + Testing support: * Add utilities for testing GTK+ applications - * Add some unit tests for GTK+ + * Add some unit tests for GTK+ + New settings: * for disabling display of accelerators and mnemonics * for position of vertical scrollbars in scrolled windows - * for the default input method + * for the default input method + GtkToolShell: new interface for containers of GtkToolItems + Many bugfixes + Updated translations @@ -2369,12 +2382,12 @@ Thu Apr 10 12:54:45 CEST 2008 - ro@suse.de Thu Apr 3 19:51:23 CEST 2008 - maw@suse.de - Don't own uz@cyrillic locale directories; they're now part of - the filesystem package. + the filesystem package. ------------------------------------------------------------------- Tue Apr 1 18:42:20 CEST 2008 - coolo@suse.de -- moving recommends of gvfs from glib2 +- moving recommends of gvfs from glib2 ------------------------------------------------------------------- Thu Mar 27 19:34:53 CET 2008 - federico@novell.com @@ -2405,7 +2418,7 @@ Sat Mar 15 07:23:26 CET 2008 - aj@suse.de Thu Mar 13 13:52:41 CET 2008 - rodrigo@suse.de - Update to version 2.12.9: - + http://ftp.gnome.org/pub/GNOME/sources/gtk+/2.12/gtk+-2.12.9.news + + http://ftp.gnome.org/pub/GNOME/sources/gtk+/2.12/gtk+-2.12.9.news ------------------------------------------------------------------- Fri Mar 7 17:25:47 CET 2008 - sbrabec@suse.cz @@ -2418,7 +2431,7 @@ Mon Feb 18 17:21:11 CET 2008 - maw@suse.de - Update to version 2.12.8: + Make the directfb backend build with GLib 2.15 + Improve the handling of modal windows on win32 - + GtkFileChooser: + + GtkFileChooser: + Activate bookmarks on single-click + Improve some focus handling issues + Don't select filename extensions in the save dialog @@ -2504,7 +2517,7 @@ Mon Sep 17 13:24:52 CEST 2007 - sbrabec@suse.cz - Updated to version 2.12.0: * GtkTooltips: All widgets have been ported to the new tooltips - code, tooltips are disabled in touchscreen mode + code, tooltips are disabled in touchscreen mode * GtkBuilder: Support custom tabs in GtkPrintUnixDialog * Many bugs fixed * Translations update @@ -2525,7 +2538,7 @@ Thu Sep 13 10:15:58 CEST 2007 - stbinner@suse.de ------------------------------------------------------------------- Thu Sep 13 00:08:54 CEST 2007 - federico@novell.com -- Added gtk2-303869-disable-icon-cache-validation.diff to fix the +- Added gtk2-303869-disable-icon-cache-validation.diff to fix the critical part of https://bugzillanovell.com/show_bug.cgi?id=303869 - icon caches now only get validated if GTK_DEBUG=icontheme is set. This prevents major page-ins at application startup. @@ -2571,12 +2584,12 @@ Thu Jul 26 19:29:58 CEST 2007 - maw@suse.de - GtkBuilder: + The gtk-builder-convert script is more versatile + GtkBuilder suppports GdkPixbuf properties - + GtkBuilder parses, but doesn't yet implement + + GtkBuilder parses, but doesn't yet implement + Support for accelerators in actions has been added - GtkTooltips: + The old tooltips API has been deprecated + The has-tooltip property has getter and setter -- GtkTreeView: +- GtkTreeView: + Convenience API to set tooltips: gtk_tree_view_set_tooltip_row(), gtk_tree_view_set_tooltip_cell(), gtk_tree_view_get_tooltip_context(), gtk_tree_view_set_tooltip_column() @@ -2630,7 +2643,7 @@ Thu Jul 26 19:29:58 CEST 2007 - maw@suse.de 458298 broken cursor movement with inline selection 456258 GtkScaleButton: value parameter of signal "value-changed"... 457774 GtkTreeView::test_expand_row and test_collapse_row - 455645 intern action names + 455645 intern action names - Updated translations - s#%run_ldconfig#/sbin/ldconfig/ in %post and %postun. @@ -2743,7 +2756,7 @@ Sat Nov 11 01:08:38 CET 2006 - danw@suse.de ------------------------------------------------------------------- Wed Nov 8 01:06:19 CET 2006 - jhargadon@suse.de -- removed the execute bit from /etc/opt/gnome/gtk-2.0/gtkrc +- removed the execute bit from /etc/opt/gnome/gtk-2.0/gtkrc ------------------------------------------------------------------- Tue Oct 17 22:57:19 CEST 2006 - jhargadon@suse.de @@ -2754,7 +2767,7 @@ Tue Oct 17 22:57:19 CEST 2006 - jhargadon@suse.de - 357280 Compile crashes - 359053 Reduce relocations - 359052 gtk_print_settings_get_duplex() return wrong - value for one of vertical setting + value for one of vertical setting ------------------------------------------------------------------- Sat Oct 14 23:31:48 CEST 2006 - danw@suse.de @@ -2805,7 +2818,7 @@ Thu Sep 14 17:46:37 CEST 2006 - jhargadon@suse.de gtk_recent_chooser_menu_set_current_uri 329604 do not scroll on copy to clipboard 354035 Typo in the GtkWidget::drag-drop doc blurb -- translation updates +- translation updates ------------------------------------------------------------------- Fri Aug 18 18:32:11 CEST 2006 - jhargadon@suse.de @@ -2846,7 +2859,7 @@ Wed Jun 21 07:43:10 CEST 2006 - federico@novell.com - Added gtk2-184875-filechooser-location-entry-set-path.diff to fix https://bugzilla.novell.com/show_bug.cgi?id=184875. This makes the location entry in the file chooser preserve the filename from - gtk_file_chooser_set_filename() and set_uri(). It was incorrectly + gtk_file_chooser_set_filename() and set_uri(). It was incorrectly using just the directory name instead of the file name. ------------------------------------------------------------------- @@ -2918,7 +2931,7 @@ Thu Mar 30 02:36:31 CEST 2006 - federico@novell.com ------------------------------------------------------------------- Fri Mar 24 19:12:59 CET 2006 - rml@suse.de -- Set default invisible char to something stetic (bug #160688) +- Set default invisible char to something stetic (bug #160688) ------------------------------------------------------------------- Fri Mar 24 03:51:56 CET 2006 - federico@novell.com @@ -2935,7 +2948,7 @@ Mon Mar 13 12:25:19 CET 2006 - sbrabec@suse.cz ------------------------------------------------------------------- Tue Mar 7 04:40:12 CET 2006 - zsu@suse.de -- Fixed gtkvts crash bug introduced by gtk+-2.8.6-fontsel.patch [#153099]. +- Fixed gtkvts crash bug introduced by gtk+-2.8.6-fontsel.patch [#153099]. ------------------------------------------------------------------- Thu Feb 23 20:38:53 CET 2006 - federico@novell.com @@ -3005,7 +3018,7 @@ Wed Jan 11 10:49:10 CET 2006 - meissner@suse.de Mon Jan 09 12:17:13 CET 2006 - mfabian@suse.de - Bugzilla #131498: don't limit the default for the GTK2 xim module - to "ko:ja:th:zh", use "*" (all languages) instead. + to "ko:ja:th:zh", use "*" (all languages) instead. ------------------------------------------------------------------- Fri Dec 30 14:29:34 CET 2005 - mfabian@suse.de @@ -3018,7 +3031,7 @@ Fri Dec 30 14:29:34 CET 2005 - mfabian@suse.de ------------------------------------------------------------------- Tue Dec 20 12:24:58 CET 2005 - ro@suse.de -- also pack translations for gtk20-properties +- also pack translations for gtk20-properties ------------------------------------------------------------------- Wed Dec 14 19:16:16 CET 2005 - sbrabec@suse.cz @@ -3048,7 +3061,7 @@ Tue Nov 15 15:56:20 CET 2005 - sbrabec@suse.cz ------------------------------------------------------------------- Fri Oct 28 19:10:33 CEST 2005 - federico@novell.com -- Added gtk2-127646-dnd-cursor-offset.diff to fix the position of the default +- Added gtk2-127646-dnd-cursor-offset.diff to fix the position of the default cursor for drag and drop. ------------------------------------------------------------------- @@ -3082,7 +3095,7 @@ Fri Sep 30 01:34:48 CEST 2005 - federico@novell.com ------------------------------------------------------------------- Thu Sep 15 22:13:15 CEST 2005 - gekker@suse.de -- Fix quoting in SuSEconfig.gtk2 (113511) +- Fix quoting in SuSEconfig.gtk2 (113511) ------------------------------------------------------------------- Thu Sep 8 15:55:39 CEST 2005 - sbrabec@suse.cz @@ -3098,7 +3111,7 @@ Wed Sep 7 18:55:54 CEST 2005 - sbrabec@suse.cz Thu Sep 1 19:46:12 CEST 2005 - gekker@suse.de - Update to version 2.8.3 -- Remove upstreamed patch +- Remove upstreamed patch ------------------------------------------------------------------- Fri Aug 26 19:54:13 CEST 2005 - jpr@suse.de @@ -3109,7 +3122,7 @@ Fri Aug 26 19:54:13 CEST 2005 - jpr@suse.de ------------------------------------------------------------------- Wed Aug 24 22:40:56 CEST 2005 - gekker@suse.de -- Update to version 2.8.2 +- Update to version 2.8.2 ------------------------------------------------------------------- Wed Aug 24 16:50:19 CEST 2005 - rodrigo@suse.de @@ -3120,7 +3133,7 @@ Wed Aug 24 16:50:19 CEST 2005 - rodrigo@suse.de ------------------------------------------------------------------- Thu Aug 18 06:33:24 CEST 2005 - gekker@suse.de -- Update version to 2.8.0 (GNOME2.12) +- Update version to 2.8.0 (GNOME2.12) ------------------------------------------------------------------- Fri Aug 12 21:42:00 CEST 2005 - gekker@suse.de @@ -3131,7 +3144,7 @@ Fri Aug 12 21:42:00 CEST 2005 - gekker@suse.de Thu Aug 11 02:49:18 CEST 2005 - gekker@suse.de - Update to version 2.7.5 -- Remove upstreamed patch +- Remove upstreamed patch ------------------------------------------------------------------- Wed Aug 10 23:07:21 CEST 2005 - clahey@suse.de @@ -3153,14 +3166,14 @@ Thu Aug 4 13:44:35 CEST 2005 - sbrabec@suse.cz ------------------------------------------------------------------- Tue Aug 2 13:05:39 CEST 2005 - ro@suse.de -- fix and re-enable gtk64.patch +- fix and re-enable gtk64.patch ------------------------------------------------------------------- Mon Aug 1 19:03:55 CEST 2005 - gekker@suse.de - Update to version 2.7.4 - Add cairo, libpixman, and glitz to nfb -- Remove upstreamed patch +- Remove upstreamed patch ------------------------------------------------------------------- Fri Jun 24 12:57:55 CEST 2005 - sbrabec@suse.cz @@ -3177,13 +3190,13 @@ Mon Jun 20 14:45:25 CEST 2005 - sbrabec@suse.cz ------------------------------------------------------------------- Fri Jun 17 05:50:47 CEST 2005 - gekker@suse.de -- Fix gtk64.patch and build on x86_64. +- Fix gtk64.patch and build on x86_64. ------------------------------------------------------------------- Fri Jun 17 00:09:27 CEST 2005 - gekker@suse.de - Update to version 2.6.8. -- Fix sentinel patch to work with new version of glib2. +- Fix sentinel patch to work with new version of glib2. ------------------------------------------------------------------- Wed Jun 1 15:36:31 CEST 2005 - sbrabec@suse.cz @@ -3193,23 +3206,23 @@ Wed Jun 1 15:36:31 CEST 2005 - sbrabec@suse.cz ------------------------------------------------------------------- Mon May 2 23:00:03 CEST 2005 - gekker@suse.de -- Fix crasher in gtktextview (380). +- Fix crasher in gtktextview (380). ------------------------------------------------------------------- Mon Apr 4 07:40:13 CEST 2005 - aj@suse.de -- Disable visibility to build with GCC4. This should be +- Disable visibility to build with GCC4. This should be enabled again once gtk is fixed. ------------------------------------------------------------------- Fri Mar 18 22:09:04 CET 2005 - gekker@suse.de -- Add gtk-esc-closes.diff +- Add gtk-esc-closes.diff ------------------------------------------------------------------- Wed Mar 2 22:50:31 CET 2005 - gekker@suse.de -- Updated to version 2.6.4 +- Updated to version 2.6.4 ------------------------------------------------------------------- Mon Feb 7 15:48:24 CET 2005 - sbrabec@suse.cz @@ -3233,7 +3246,7 @@ Fri Jan 21 11:17:37 CET 2005 - meissner@suse.de ------------------------------------------------------------------- Mon Jan 17 12:48:46 CET 2005 - ro@suse.de -- added c++ to neededforbuild (for libtiff) +- added c++ to neededforbuild (for libtiff) ------------------------------------------------------------------- Mon Jan 17 10:54:33 CET 2005 - clahey@suse.de @@ -3243,18 +3256,18 @@ Mon Jan 17 10:54:33 CET 2005 - clahey@suse.de ------------------------------------------------------------------- Wed Jan 12 12:02:43 CET 2005 - gekker@suse.de -- Update to version 2.6.0 +- Update to version 2.6.0 ------------------------------------------------------------------- Tue Nov 23 16:48:50 CET 2004 - hhetter@suse.de -- add some information on how to manually regenerate +- add some information on how to manually regenerate gdk-pixbuf-modules if needed ------------------------------------------------------------------- Mon Nov 22 15:24:27 CET 2004 - ro@suse.de -- fix build on 9.2 (missing so links for libXfixes) +- fix build on 9.2 (missing so links for libXfixes) ------------------------------------------------------------------- Fri Nov 19 17:06:33 CET 2004 - ro@suse.de @@ -3264,7 +3277,7 @@ Fri Nov 19 17:06:33 CET 2004 - ro@suse.de ------------------------------------------------------------------- Wed Nov 17 18:00:53 CET 2004 - gekker@suse.de -- Update version to 2.5.5. +- Update version to 2.5.5. ------------------------------------------------------------------- Tue Oct 26 22:35:40 CEST 2004 - mmj@suse.de @@ -3289,7 +3302,7 @@ Mon Sep 6 15:21:00 CEST 2004 - meissner@suse.de ------------------------------------------------------------------- Tue Aug 31 23:13:45 CEST 2004 - dave@suse.de -- Added gtk2-cancel-rename.patch, ximian #59669 +- Added gtk2-cancel-rename.patch, ximian #59669 ------------------------------------------------------------------- Fri Aug 27 16:46:00 CEST 2004 - federico@ximian.com @@ -3360,7 +3373,7 @@ Thu Mar 11 13:52:32 CET 2004 - sbrabec@suse.cz ------------------------------------------------------------------- Fri Mar 5 10:07:00 CET 2004 - hhetter@suse.de -- apply reworked patch gtk2-filesel-navbuttons.patch: +- apply reworked patch gtk2-filesel-navbuttons.patch: Default to ~/Desktop ------------------------------------------------------------------- @@ -3380,7 +3393,7 @@ Mon Mar 01 14:12:05 CET 2004 - sbrabec@suse.cz ------------------------------------------------------------------- Thu Feb 26 16:42:59 CET 2004 - hhetter@suse.de -- applied ximian patches +- applied ximian patches ------------------------------------------------------------------- Mon Feb 23 18:36:47 CET 2004 - sbrabec@suse.cz @@ -3395,12 +3408,12 @@ Sat Jan 10 11:18:14 CET 2004 - adrian@suse.de ------------------------------------------------------------------- Thu Jan 8 14:53:31 CET 2004 - ro@suse.de -- fix build with current freetype +- fix build with current freetype ------------------------------------------------------------------- Wed Nov 5 16:43:59 CET 2003 - hhetter@suse.de -- added xfree86-devel to Requires for the devel subpackage +- added xfree86-devel to Requires for the devel subpackage ------------------------------------------------------------------- Fri Oct 10 12:01:04 CEST 2003 - sbrabec@suse.cz @@ -3415,7 +3428,7 @@ Thu Aug 28 15:42:16 CEST 2003 - sbrabec@suse.cz ------------------------------------------------------------------- Thu Jul 24 02:17:07 CEST 2003 - hhetter@suse.de -- fix SuSEconfig.gtk2 query paths +- fix SuSEconfig.gtk2 query paths ------------------------------------------------------------------- Wed Jul 16 16:07:02 CEST 2003 - sbrabec@suse.cz @@ -3452,7 +3465,7 @@ Wed May 28 12:52:36 CEST 2003 - sbrabec@suse.cz ------------------------------------------------------------------- Wed May 28 01:44:13 CEST 2003 - ro@suse.de -- remove unpackaged files from buildroot +- remove unpackaged files from buildroot ------------------------------------------------------------------- Thu May 22 11:34:09 CEST 2003 - sbrabec@suse.cz @@ -3483,7 +3496,7 @@ Wed Mar 12 17:00:13 CET 2003 - sbrabec@suse.cz ------------------------------------------------------------------- Mon Feb 10 02:00:53 CET 2003 - ro@suse.de -- create /etc/gtk-2.0 in SuSEconfig.gtk2 if needed +- create /etc/gtk-2.0 in SuSEconfig.gtk2 if needed ------------------------------------------------------------------- Thu Feb 06 12:24:25 CET 2003 - sbrabec@suse.cz @@ -3516,10 +3529,10 @@ Thu Jan 09 18:21:36 CET 2003 - sbrabec@suse.cz Mon Nov 25 14:20:17 CET 2002 - hhetter@suse.de - updated to version 2.0.9 - * Fix colormap refcounting, which caused + * Fix colormap refcounting, which caused frequent metacity crashes - * GtkTreeView bug fixes - - Make TreeView reordering work on FreeBSD/Solaris + * GtkTreeView bug fixes + - Make TreeView reordering work on FreeBSD/Solaris * Updated translations (be, ru, nl, fr) ------------------------------------------------------------------- @@ -3537,22 +3550,22 @@ Tue Nov 5 11:33:30 CET 2002 - hhetter@suse.de - updated to 2.0.7 bugfix release - removed x86_64 patch as it's upstream, from Changelog: * Fix some memory leaks in gdk-pixbuf - * Pixbuf loader fixes - * Support depth 8 StaticColor in GdkRGB - * Win32 fixes and improvements - * Improve tracking of toplevel focus state - * XIM input method fixes - * Fix the longstanding problem with <,> keys and XIM - * Fix GtkIMContextSimple for us-intl keyboards - * GtkIMContextSimple updates for Eastern Europe - * Fix the "key bindings randomly stop working" problem - * GtkTextView fixes - * GtkTreeView bugfixes - * GtkCombo fixes - * Fix 64-bit problem with GtkFundamentalType - * New and updated translations + * Pixbuf loader fixes + * Support depth 8 StaticColor in GdkRGB + * Win32 fixes and improvements + * Improve tracking of toplevel focus state + * XIM input method fixes + * Fix the longstanding problem with <,> keys and XIM + * Fix GtkIMContextSimple for us-intl keyboards + * GtkIMContextSimple updates for Eastern Europe + * Fix the "key bindings randomly stop working" problem + * GtkTextView fixes + * GtkTreeView bugfixes + * GtkCombo fixes + * Fix 64-bit problem with GtkFundamentalType + * New and updated translations * Many miscellaneous bug fixes - + ------------------------------------------------------------------- Tue Oct 22 13:45:08 CEST 2002 - sbrabec@suse.cz @@ -3573,7 +3586,7 @@ Tue Oct 01 16:06:53 CEST 2002 - sbrabec@suse.cz Fri Sep 6 10:06:05 CEST 2002 - sf@suse.de - added patch to avoid wrong va_arg handling - which leads to almost any GTK-Widget segfaulting + which leads to almost any GTK-Widget segfaulting ------------------------------------------------------------------- Wed Aug 21 18:36:57 CEST 2002 - kukuk@suse.de @@ -3583,43 +3596,43 @@ Wed Aug 21 18:36:57 CEST 2002 - kukuk@suse.de ------------------------------------------------------------------- Thu Aug 15 14:28:35 CEST 2002 - hhetter@suse.de -- branch gtk2-doc package +- branch gtk2-doc package ------------------------------------------------------------------- Tue Aug 13 09:38:12 CEST 2002 - hhetter@suse.de - added fbmanager and gdktarget build options (#17640) -- devel package requires main package +- devel package requires main package ------------------------------------------------------------------- Thu Aug 8 09:50:30 CEST 2002 - hhetter@suse.de - use %{version} tag in LIBRARY_PATH -- enable xim and shm +- enable xim and shm ------------------------------------------------------------------- Mon Aug 5 11:50:26 CEST 2002 - hhetter@suse.de - updated to version 2.0.6 - * GtkTreeView bug fixes - * Fix problem with keynav and insensitive menu items - * Fix pixbuf_from_drawable() for LSB -> MSB - * Use GTK2_RC_FILES envvar instead of GTK_RC_FILES - * Focus check/radio buttons when activating with a mnemonic + * GtkTreeView bug fixes + * Fix problem with keynav and insensitive menu items + * Fix pixbuf_from_drawable() for LSB -> MSB + * Use GTK2_RC_FILES envvar instead of GTK_RC_FILES + * Focus check/radio buttons when activating with a mnemonic * Cycle between multiple menubars with F10, not control-tab - * Misc bug fixes - * Build fixes for cross-compiling and portability + * Misc bug fixes + * Build fixes for cross-compiling and portability * Updated translations (bg,ca,da,fr,ja,ko,lv,no,pl,ru,sk,sv,vi) ------------------------------------------------------------------- Tue Jul 30 13:58:28 CEST 2002 - jordi@suse.de -- added missing documentation for gtk2-devel +- added missing documentation for gtk2-devel ------------------------------------------------------------------- Wed Jul 10 10:16:21 CEST 2002 - hhetter@suse.de -- fix requires of devel package, still pointed to GTK 1.x packages +- fix requires of devel package, still pointed to GTK 1.x packages ------------------------------------------------------------------- Mon Jun 17 19:15:45 CEST 2002 - jordi@suse.de @@ -3627,31 +3640,31 @@ Mon Jun 17 19:15:45 CEST 2002 - jordi@suse.de - Updated to version 2.0.5 * Fix a wrong assertion that broke gtk_file_selection_set_filename(); also another fix from testing this function with non-UTF-8 filenames. - * Fix incorrect property notification in GtkTextView. + * Fix incorrect property notification in GtkTextView. Overview of Changes in GTK+ 2.0.4 - * Fix a number of types which were registered with the - type system with the wrong names - * Support missing data types in GtkList/TreeStore - * Misc GtkTreeView bug fixes - * Drag and drop fixes, including a stuck grab. - * Calculate screen size on win32 from the "logical DPI" - * Misc Win32 bug fixes. - * Fix theme changes for GtkMenu - * Fix gdk_pixbuf_from_drawable() for big endian. + * Fix a number of types which were registered with the + type system with the wrong names + * Support missing data types in GtkList/TreeStore + * Misc GtkTreeView bug fixes + * Drag and drop fixes, including a stuck grab. + * Calculate screen size on win32 from the "logical DPI" + * Misc Win32 bug fixes. + * Fix theme changes for GtkMenu + * Fix gdk_pixbuf_from_drawable() for big endian. * Fix encoding handling for gtk_file_selection_set_filename() - * Fix crash with DND, Qt and Metacity - * Fixes for DirectColor visuals - * Memory leak and UMR fixes + * Fix crash with DND, Qt and Metacity + * Fixes for DirectColor visuals + * Memory leak and UMR fixes * Misc bug fixes - * Updated translations - + * Updated translations + ------------------------------------------------------------------- Thu Jun 13 01:06:11 CEST 2002 - ro@suse.de -- use libpng-devel-packages in nededforbuild +- use libpng-devel-packages in nededforbuild ------------------------------------------------------------------- Mon Jun 10 14:10:34 CEST 2002 - meissner@suse.de @@ -3661,88 +3674,88 @@ Mon Jun 10 14:10:34 CEST 2002 - meissner@suse.de ------------------------------------------------------------------- Tue Jun 4 09:58:03 CEST 2002 - ro@suse.de -- re-add libdir fixes +- re-add libdir fixes ------------------------------------------------------------------- Tue Jun 4 09:13:47 CEST 2002 - hhetter@suse.de - updated to stable version 2.0.3: - * GtkTreeView fixes - * Improve GdkRGB support for low color depths + * GtkTreeView fixes + * Improve GdkRGB support for low color depths * Tweak F10 behavior to focus GtkMenuBar * Include internal children when focusing - * Allow use of a pixmap as the drawable in gdk_pixmap_new(). - * GdkPixbuf fixes - * GtkMenu fixes. - * Misc input-method related fixes - * Fix stuck grab during DND. - * Remove in-bevel from scrolled-offscreen menus. - * Various plug/socket fixes - * Handle Xlib internal connections - * Many miscellaneous bug fixes. + * Allow use of a pixmap as the drawable in gdk_pixmap_new(). + * GdkPixbuf fixes + * GtkMenu fixes. + * Misc input-method related fixes + * Fix stuck grab during DND. + * Remove in-bevel from scrolled-offscreen menus. + * Various plug/socket fixes + * Handle Xlib internal connections + * Many miscellaneous bug fixes. ------------------------------------------------------------------- Tue Apr 9 10:15:26 CEST 2002 - hhetter@suse.de -- update to stable version 2.0.2 +- update to stable version 2.0.2 ------------------------------------------------------------------- Tue Apr 9 08:28:11 CEST 2002 - ro@suse.de -- build with "atk-1.0" (was "atk") +- build with "atk-1.0" (was "atk") ------------------------------------------------------------------- Fri Feb 1 09:03:33 CET 2002 - hhetter@suse.de - updated to 1.1.13 [gnome desktop alpha2] -- build with libpng-devel +- build with libpng-devel ------------------------------------------------------------------- Fri Jan 25 09:50:40 CET 2002 - hhetter@suse.de -- rename SuSEconfig.gtk to SuSEconfig.gtk2 +- rename SuSEconfig.gtk to SuSEconfig.gtk2 ------------------------------------------------------------------- Thu Jan 17 14:34:42 CET 2002 - hhetter@suse.de - new SuSEconfig.gtk script ensures that all input-method modules - are registered + are registered ------------------------------------------------------------------- Fri Jan 11 11:35:26 CET 2002 - hhetter@suse.de - use version tag in patch filename - updated to version 1.3.12: - * Fix problems with PNG saving - * Cleanups of deprecated usages - * Frame buffer port fixes - * GtkTextView bug fixes - * Menu behavior improvements - * Make focus line width configurable, focus color work on + * Fix problems with PNG saving + * Cleanups of deprecated usages + * Frame buffer port fixes + * GtkTextView bug fixes + * Menu behavior improvements + * Make focus line width configurable, focus color work on dark themes. - * Add state argument to gtk_paint_focus() - * Added incremental revalidation to tree view, for better apparent speed - * Remove useless gtk_tree_view_column_cell_event() - * Display XIM status in a separate window - * Add menu of Unicode control characters to GtkEntry, GtkTextView + * Add state argument to gtk_paint_focus() + * Added incremental revalidation to tree view, for better apparent speed + * Remove useless gtk_tree_view_column_cell_event() + * Display XIM status in a separate window + * Add menu of Unicode control characters to GtkEntry, GtkTextView * Pass key releases along to input methods [Owen] -- specfile cleanup +- specfile cleanup ------------------------------------------------------------------- Thu Jan 10 13:53:13 CET 2002 - hhetter@suse.de -- no longer require glib (1.x) +- no longer require glib (1.x) ------------------------------------------------------------------- Thu Jan 10 10:51:42 CET 2002 - hhetter@suse.de -- include gdk-pixbuf's tools +- include gdk-pixbuf's tools - build with freetype2 support ------------------------------------------------------------------- Tue Jan 8 09:45:48 CET 2002 - hhetter@suse.de -- include links for default library names +- include links for default library names ------------------------------------------------------------------- Wed Dec 12 10:33:02 CET 2001 - hhetter@suse.de @@ -3750,5 +3763,5 @@ Wed Dec 12 10:33:02 CET 2001 - hhetter@suse.de - initial SuSE release for GNOME 2.0 platform - added LIBRARY_PATH for broken libtool - build with automake 1.5 - + diff --git a/gtk2.spec b/gtk2.spec index 97911d2..51944de 100644 --- a/gtk2.spec +++ b/gtk2.spec @@ -59,6 +59,8 @@ Patch56: gtk2-bgo625202-30-bit-drawables-remain-black.patch 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 @@ -339,6 +341,7 @@ cp -a %{SOURCE2} . %patch56 -p1 %patch57 -p1 %patch58 -p1 +%patch59 -p1 gnome-patch-translation-update %build