commit 7a1f6e15982d407f12d64c2f305ef413d546568c
Author: Federico Mena Quintero <federico@suse.com>
Date:   Thu Jan 19 13:05:42 2017 -0600

    Update to filechooser and GtkEntry code from GTK+ 2.24.31
    
    This fixes Tab completion in GtkFileChooser, and some drag-and-drop crashes.
    
    https://bugzilla.suse.com/show_bug.cgi?id=957400

diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index b55bd89..ae7a0d3 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -490,9 +490,6 @@ static void         completion_insert_text_callback    (GtkEntry           *entr
 							gint                length,
 							gint                position,
 							GtkEntryCompletion *completion);
-static void         completion_changed                 (GtkEntryCompletion *completion,
-							GParamSpec         *pspec,
-							gpointer            data);
 static void         disconnect_completion_signals      (GtkEntry           *entry,
 							GtkEntryCompletion *completion);
 static void         connect_completion_signals         (GtkEntry           *entry,
@@ -2382,6 +2379,8 @@ begin_change (GtkEntry *entry)
   GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry);
 
   priv->change_count++;
+
+  g_object_freeze_notify (G_OBJECT (entry));
 }
 
 static void
@@ -2392,6 +2391,8 @@ end_change (GtkEntry *entry)
  
   g_return_if_fail (priv->change_count > 0);
 
+  g_object_thaw_notify (G_OBJECT (entry));
+
   priv->change_count--;
 
   if (priv->change_count == 0)
@@ -4537,8 +4538,12 @@ gtk_entry_real_insert_text (GtkEditable *editable,
    * following signal handlers: buffer_inserted_text(), buffer_notify_display_text(),
    * buffer_notify_text(), buffer_notify_length()
    */
+  begin_change (GTK_ENTRY (editable));
+
   n_inserted = gtk_entry_buffer_insert_text (get_buffer (GTK_ENTRY (editable)), *position, new_text, n_chars);
 
+  end_change (GTK_ENTRY (editable));
+
   if (n_inserted != n_chars)
       gtk_widget_error_bell (GTK_WIDGET (editable));
 
@@ -4556,7 +4561,11 @@ gtk_entry_real_delete_text (GtkEditable *editable,
    * buffer_notify_text(), buffer_notify_length()
    */
 
+  begin_change (GTK_ENTRY (editable));
+
   gtk_entry_buffer_delete_text (get_buffer (GTK_ENTRY (editable)), start_pos, end_pos - start_pos);
+
+  end_change (GTK_ENTRY (editable));
 }
 
 /* GtkEntryBuffer signal handlers
@@ -4569,12 +4578,18 @@ buffer_inserted_text (GtkEntryBuffer *buffer,
                       GtkEntry       *entry)
 {
   guint password_hint_timeout;
+  guint current_pos;
+  gint selection_bound;
 
-  if (entry->current_pos > position)
-    entry->current_pos += n_chars;
+  current_pos = entry->current_pos;
+  if (current_pos > position)
+    current_pos += n_chars;
 
-  if (entry->selection_bound > position)
-    entry->selection_bound += n_chars;
+  selection_bound = entry->selection_bound;
+  if (selection_bound > position)
+    selection_bound += n_chars;
+
+  gtk_entry_set_positions (entry, current_pos, selection_bound);
 
   /* Calculate the password hint if it needs to be displayed. */
   if (n_chars == 1 && !entry->visible)
@@ -6312,14 +6327,12 @@ paste_received (GtkClipboard *clipboard,
 	}
 
       begin_change (entry);
-      g_object_freeze_notify (G_OBJECT (entry));
       if (gtk_editable_get_selection_bounds (editable, &start, &end))
         gtk_editable_delete_text (editable, start, end);
 
       pos = entry->current_pos;
       gtk_editable_insert_text (editable, text, length, &pos);
       gtk_editable_set_position (editable, pos);
-      g_object_thaw_notify (G_OBJECT (entry));
       end_change (entry);
 
       if (completion &&
@@ -6797,11 +6810,9 @@ gtk_entry_set_text (GtkEntry    *entry,
     g_signal_handler_block (entry, completion->priv->changed_id);
 
   begin_change (entry);
-  g_object_freeze_notify (G_OBJECT (entry));
   gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
   tmp_pos = 0;
   gtk_editable_insert_text (GTK_EDITABLE (entry), text, strlen (text), &tmp_pos);
-  g_object_thaw_notify (G_OBJECT (entry));
   end_change (entry);
 
   if (completion && completion->priv->changed_id > 0)
@@ -8995,10 +9006,8 @@ gtk_entry_drag_data_received (GtkWidget        *widget,
 	{
 	  /* Replacing selection */
           begin_change (entry);
-          g_object_freeze_notify (G_OBJECT (entry));
 	  gtk_editable_delete_text (editable, sel1, sel2);
 	  gtk_editable_insert_text (editable, str, length, &sel1);
-          g_object_thaw_notify (G_OBJECT (entry));
           end_change (entry);
 	}
       
@@ -9431,6 +9440,7 @@ gtk_entry_completion_key_press (GtkWidget   *widget,
             {
 
               GtkTreeIter iter;
+              GtkTreeIter child_iter;
               GtkTreeModel *model = NULL;
               GtkTreeSelection *sel;
               gboolean entry_set;
@@ -9438,12 +9448,15 @@ gtk_entry_completion_key_press (GtkWidget   *widget,
               sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view));
               if (!gtk_tree_selection_get_selected (sel, &model, &iter))
                 return FALSE;
+
+              gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter);
+              model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
               
               if (completion->priv->completion_prefix == NULL)
                 completion->priv->completion_prefix = g_strdup (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)));
 
               g_signal_emit_by_name (completion, "cursor-on-match", model,
-                                     &iter, &entry_set);
+                                     &child_iter, &entry_set);
             }
         }
       else if (completion->priv->current_selected - matches >= 0)
@@ -9518,18 +9531,13 @@ keypress_completion_out:
 	   event->keyval == GDK_KP_Tab ||
 	   event->keyval == GDK_ISO_Left_Tab) 
     {
-      GtkDirectionType dir = event->keyval == GDK_ISO_Left_Tab ? 
-	GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD;
-      
       _gtk_entry_reset_im_context (GTK_ENTRY (widget));
       _gtk_entry_completion_popdown (completion);
 
       g_free (completion->priv->completion_prefix);
       completion->priv->completion_prefix = NULL;
 
-      gtk_widget_child_focus (gtk_widget_get_toplevel (widget), dir);
-
-      return TRUE;
+      return FALSE;
     }
   else if (event->keyval == GDK_ISO_Enter ||
            event->keyval == GDK_KP_Enter ||
@@ -9537,6 +9545,8 @@ keypress_completion_out:
     {
       GtkTreeIter iter;
       GtkTreeModel *model = NULL;
+      GtkTreeModel *child_model;
+      GtkTreeIter child_iter;
       GtkTreeSelection *sel;
       gboolean retval = TRUE;
 
@@ -9550,9 +9560,11 @@ keypress_completion_out:
           sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view));
           if (gtk_tree_selection_get_selected (sel, &model, &iter))
             {
+              gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter);
+              child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
               g_signal_handler_block (widget, completion->priv->changed_id);
               g_signal_emit_by_name (completion, "match-selected",
-                                     model, &iter, &entry_set);
+                                     child_model, &child_iter, &entry_set);
               g_signal_handler_unblock (widget, completion->priv->changed_id);
 
               if (!entry_set)
@@ -9605,6 +9617,9 @@ gtk_entry_completion_changed (GtkWidget *entry,
 {
   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
 
+  if (!completion->priv->popup_completion)
+    return;
+
   /* (re)install completion timeout */
   if (completion->priv->completion_timeout)
     g_source_remove (completion->priv->completion_timeout);
@@ -9642,13 +9657,14 @@ static void
 clear_completion_callback (GtkEntry   *entry,
 			   GParamSpec *pspec)
 {
+  GtkEntryCompletion *completion = gtk_entry_get_completion (entry);
+
+  if (!completion->priv->inline_completion)
+    return;
+
   if (pspec->name == I_("cursor-position") ||
       pspec->name == I_("selection-bound"))
-    {
-      GtkEntryCompletion *completion = gtk_entry_get_completion (entry);
-      
-      completion->priv->has_completion = FALSE;
-    }
+    completion->priv->has_completion = FALSE;
 }
 
 static gboolean
@@ -9656,6 +9672,9 @@ accept_completion_callback (GtkEntry *entry)
 {
   GtkEntryCompletion *completion = gtk_entry_get_completion (entry);
 
+  if (!completion->priv->inline_completion)
+    return FALSE;
+
   if (completion->priv->has_completion)
     gtk_editable_set_position (GTK_EDITABLE (entry),
 			       gtk_entry_buffer_get_length (get_buffer (entry)));
@@ -9670,6 +9689,9 @@ completion_insert_text_callback (GtkEntry           *entry,
 				 gint                position,
 				 GtkEntryCompletion *completion)
 {
+  if (!completion->priv->inline_completion)
+    return;
+
   /* idle to update the selection based on the file list */
   if (completion->priv->check_completion_idle == NULL)
     {
@@ -9683,26 +9705,9 @@ completion_insert_text_callback (GtkEntry           *entry,
 }
 
 static void
-completion_changed (GtkEntryCompletion *completion,
-		    GParamSpec         *pspec,
-		    gpointer            data)
-{
-  GtkEntry *entry = GTK_ENTRY (data);
-
-  if (pspec->name == I_("popup-completion") ||
-      pspec->name == I_("inline-completion"))
-    {
-      disconnect_completion_signals (entry, completion);
-      connect_completion_signals (entry, completion);
-    }
-}
-
-static void
 disconnect_completion_signals (GtkEntry           *entry,
 			       GtkEntryCompletion *completion)
 {
-  g_signal_handlers_disconnect_by_func (completion, 
-				       G_CALLBACK (completion_changed), entry);
   if (completion->priv->changed_id > 0 &&
       g_signal_handler_is_connected (entry, completion->priv->changed_id))
     {
@@ -9729,30 +9734,21 @@ static void
 connect_completion_signals (GtkEntry           *entry,
 			    GtkEntryCompletion *completion)
 {
-  if (completion->priv->popup_completion)
-    {
-      completion->priv->changed_id =
-	g_signal_connect (entry, "changed",
-			  G_CALLBACK (gtk_entry_completion_changed), completion);
-      g_signal_connect (entry, "key-press-event",
-			G_CALLBACK (gtk_entry_completion_key_press), completion);
-    }
- 
-  if (completion->priv->inline_completion)
-    {
-      completion->priv->insert_text_id =
-	g_signal_connect (entry, "insert-text",
-			  G_CALLBACK (completion_insert_text_callback), completion);
-      g_signal_connect (entry, "notify",
-			G_CALLBACK (clear_completion_callback), completion);
-      g_signal_connect (entry, "activate",
-			G_CALLBACK (accept_completion_callback), completion);
-      g_signal_connect (entry, "focus-out-event",
-			G_CALLBACK (accept_completion_callback), completion);
-    }
+  completion->priv->changed_id =
+    g_signal_connect (entry, "changed",
+                      G_CALLBACK (gtk_entry_completion_changed), completion);
+  g_signal_connect (entry, "key-press-event",
+                    G_CALLBACK (gtk_entry_completion_key_press), completion);
 
-  g_signal_connect (completion, "notify",
-		    G_CALLBACK (completion_changed), entry);
+  completion->priv->insert_text_id =
+    g_signal_connect (entry, "insert-text",
+                      G_CALLBACK (completion_insert_text_callback), completion);
+  g_signal_connect (entry, "notify",
+                    G_CALLBACK (clear_completion_callback), completion);
+  g_signal_connect (entry, "activate",
+                    G_CALLBACK (accept_completion_callback), completion);
+  g_signal_connect (entry, "focus-out-event",
+                    G_CALLBACK (accept_completion_callback), completion);
 }
 
 /**
@@ -9789,6 +9785,12 @@ gtk_entry_set_completion (GtkEntry           *entry,
           old->priv->completion_timeout = 0;
         }
 
+      if (old->priv->check_completion_idle)
+        {
+          g_source_destroy (old->priv->check_completion_idle);
+          old->priv->check_completion_idle = NULL;
+        }
+
       if (gtk_widget_get_mapped (old->priv->popup_window))
         _gtk_entry_completion_popdown (old);
 
diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c
index 2fa7b56..df5f6d8 100644
--- a/gtk/gtkentrycompletion.c
+++ b/gtk/gtkentrycompletion.c
@@ -178,20 +178,20 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
    * GtkEntryCompletion::insert-prefix:
    * @widget: the object which received the signal
    * @prefix: the common prefix of all possible completions
-   * 
-   * Gets emitted when the inline autocompletion is triggered. 
-   * The default behaviour is to make the entry display the 
+   *
+   * Gets emitted when the inline autocompletion is triggered.
+   * The default behaviour is to make the entry display the
    * whole prefix and select the newly inserted part.
    *
    * Applications may connect to this signal in order to insert only a
    * smaller part of the @prefix into the entry - e.g. the entry used in
-   * the #GtkFileChooser inserts only the part of the prefix up to the 
+   * the #GtkFileChooser inserts only the part of the prefix up to the
    * next '/'.
    *
    * Return value: %TRUE if the signal has been handled
-   * 
+   *
    * Since: 2.6
-   */ 
+   */
   entry_completion_signals[INSERT_PREFIX] =
     g_signal_new (I_("insert-prefix"),
                   G_TYPE_FROM_CLASS (klass),
@@ -207,16 +207,16 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
    * @widget: the object which received the signal
    * @model: the #GtkTreeModel containing the matches
    * @iter: a #GtkTreeIter positioned at the selected match
-   * 
-   * Gets emitted when a match from the list is selected. 
-   * The default behaviour is to replace the contents of the 
-   * entry with the contents of the text column in the row 
+   *
+   * Gets emitted when a match from the list is selected.
+   * The default behaviour is to replace the contents of the
+   * entry with the contents of the text column in the row
    * pointed to by @iter.
    *
    * Return value: %TRUE if the signal has been handled
-   * 
+   *
    * Since: 2.4
-   */ 
+   */
   entry_completion_signals[MATCH_SELECTED] =
     g_signal_new (I_("match-selected"),
                   G_TYPE_FROM_CLASS (klass),
@@ -227,22 +227,22 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
                   G_TYPE_BOOLEAN, 2,
                   GTK_TYPE_TREE_MODEL,
                   GTK_TYPE_TREE_ITER);
+
   /**
    * GtkEntryCompletion::cursor-on-match:
    * @widget: the object which received the signal
    * @model: the #GtkTreeModel containing the matches
    * @iter: a #GtkTreeIter positioned at the selected match
-   * 
+   *
    * Gets emitted when a match from the cursor is on a match
-   * of the list.The default behaviour is to replace the contents
-   * of the entry with the contents of the text column in the row 
+   * of the list. The default behaviour is to replace the contents
+   * of the entry with the contents of the text column in the row
    * pointed to by @iter.
    *
    * Return value: %TRUE if the signal has been handled
-   * 
+   *
    * Since: 2.12
-   */ 
-
+   */
   entry_completion_signals[CURSOR_ON_MATCH] =
     g_signal_new (I_("cursor-on-match"),
 		  G_TYPE_FROM_CLASS (klass),
@@ -253,7 +253,7 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
 		  G_TYPE_BOOLEAN, 2,
 		  GTK_TYPE_TREE_MODEL,
 		  GTK_TYPE_TREE_ITER);
-		  
+
   /**
    * GtkEntryCompletion::action-activated:
    * @widget: the object which received the signal
@@ -880,18 +880,23 @@ gtk_entry_completion_list_button_press (GtkWidget      *widget,
     {
       GtkTreeIter iter;
       gboolean entry_set;
+      GtkTreeModel *model;
+      GtkTreeIter child_iter;
 
       gtk_tree_model_get_iter (GTK_TREE_MODEL (completion->priv->filter_model),
                                &iter, path);
       gtk_tree_path_free (path);
+      gtk_tree_model_filter_convert_iter_to_child_iter (completion->priv->filter_model,
+                                                        &child_iter,
+                                                        &iter);
+      model = gtk_tree_model_filter_get_model (completion->priv->filter_model);
 
       g_signal_handler_block (completion->priv->entry,
-			      completion->priv->changed_id);
+                              completion->priv->changed_id);
       g_signal_emit (completion, entry_completion_signals[MATCH_SELECTED],
-                     0, GTK_TREE_MODEL (completion->priv->filter_model),
-                     &iter, &entry_set);
+                     0, model, &child_iter, &entry_set);
       g_signal_handler_unblock (completion->priv->entry,
-				completion->priv->changed_id);
+                                completion->priv->changed_id);
 
       _gtk_entry_completion_popdown (completion);
 
@@ -1571,19 +1576,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 +1756,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 2a75365..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 */	\
@@ -3098,9 +3100,11 @@ shortcuts_drop_uris (GtkFileChooserDefault *impl,
 /* Reorders the selected bookmark to the specified position */
 static void
 shortcuts_reorder (GtkFileChooserDefault *impl,
+                   GtkSelectionData      *selection_data,
 		   int                    new_position)
 {
   GtkTreeIter iter;
+  GtkTreeIter filter_iter;
   gpointer col_data;
   ShortcutType shortcut_type;
   GtkTreePath *path;
@@ -3109,13 +3113,23 @@ shortcuts_reorder (GtkFileChooserDefault *impl,
   GFile *file;
   GError *error;
   gchar *name;
+  GtkTreeModel *model;
 
   /* Get the selected path */
 
-  if (!shortcuts_get_selected (impl, &iter))
-    g_assert_not_reached ();
+  if (!gtk_tree_get_row_drag_data (selection_data, &model, &path))
+    return;
+
+  g_assert (model == impl->shortcuts_pane_filter_model);
+
+  gtk_tree_model_get_iter (model, &filter_iter, path);
+  gtk_tree_path_free (path);
 
+  gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
+                                                    &iter,
+						    &filter_iter);
   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
+
   old_position = *gtk_tree_path_get_indices (path);
   gtk_tree_path_free (path);
 
@@ -3197,7 +3211,7 @@ shortcuts_drag_data_received_cb (GtkWidget          *widget,
   if (gtk_targets_include_uri (&target, 1))
     shortcuts_drop_uris (impl, selection_data, position);
   else if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
-    shortcuts_reorder (impl, position);
+    shortcuts_reorder (impl, selection_data, position);
 
   g_signal_stop_emission_by_name (widget, "drag-data-received");
 }
@@ -4414,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);
@@ -6653,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;
     }
@@ -6663,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 ();
 }
@@ -6700,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)
         {
@@ -6934,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));
@@ -6966,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;
     }
 
@@ -7001,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), "");
     }
 }
 
@@ -7198,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
@@ -7303,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
@@ -7389,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);
@@ -7495,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;
@@ -7507,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)
     {
@@ -8806,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);
@@ -9768,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,
@@ -9906,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;
     }
     
@@ -9963,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);
 
@@ -9972,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;
@@ -10015,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() */
 
@@ -10041,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);
 
@@ -10055,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 */
@@ -10068,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);
 }
 
@@ -10085,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..7350793 100644
--- a/gtk/gtkfilechooserentry.c
+++ b/gtk/gtkfilechooserentry.c
@@ -19,13 +19,16 @@
  */
 
 #include "config.h"
+
+#include "gtkfilechooserentry.h"
+
 #include <string.h>
 
 #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,197 @@ 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);
-
-  g_object_unref (file);
-  g_free (display_name);
-
-  return TRUE;
-}
+                            path,
+                            -1,
+                            &pos);
 
-/* 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;
+  gtk_editable_set_position (GTK_EDITABLE (chooser_entry), pos);
 
-  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);
+  g_free (path);
 
   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)
-{
-  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)
+set_complete_on_load (GtkFileChooserEntry *chooser_entry,
+                      gboolean             complete_on_load)
 {
-  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 +442,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 +748,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 +759,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 +769,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 +799,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 +826,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 +876,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 +908,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 +953,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);
 
