]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilesystemmodel.c
filechooserbutton: For the open-dialog-cancel-* tests, add a real nomenclature and...
[~andy/gtk] / gtk / gtkfilesystemmodel.c
index e10babcb0c8526091979063ed4408b25ee19bcc3..132902b31788851b2fecc1b75ac822a8f26b2085 100644 (file)
@@ -13,9 +13,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
@@ -31,7 +29,6 @@
 #include "gtktreedatalist.h"
 #include "gtktreednd.h"
 #include "gtktreemodel.h"
-#include "gtkalias.h"
 
 /*** Structure: how GtkFileSystemModel works
  *
@@ -45,6 +42,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
  *
  * 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:
  *
  * 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 +140,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 */
@@ -139,7 +157,7 @@ struct _GtkFileSystemModel
 
   GCancellable *        cancellable;    /* cancellable in use for all operations - cancelled on dispose */
   GArray *              files;          /* array of FileModelNode containing all our files */
-  GSize                 node_size;     /* Size of a FileModelNode structure once its ->values field has n_columns */
+  gsize                 node_size;     /* Size of a FileModelNode structure once its ->values field has n_columns */
   guint                 n_nodes_valid;  /* count of valid nodes (i.e. those whose node->row is accurate) */
   GHashTable *          file_lookup;    /* mapping of GFile => array index in model->files
                                         * This hash table doesn't always have the same number of entries as the files array;
@@ -170,6 +188,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 +204,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 +289,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 +304,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 +316,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 +333,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 +372,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 +428,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 +437,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 +579,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 +596,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
@@ -739,7 +808,7 @@ gtk_file_system_model_sort (GtkFileSystemModel *model)
                   continue;
                 }
 
-              new_order[r] = node->row;
+              new_order[r] = node->row - 1;
               r++;
               node->row = r;
             }
@@ -984,8 +1053,8 @@ gtk_file_system_model_finalize (GObject *object)
       if (node->info)
         g_object_unref (node->info);
 
-      for (v = 0; v < model->column_types; v++)
-       if (G_VALUE_TYPE (node->values[v]) != G_TYPE_INVALID)
+      for (v = 0; v < model->n_columns; v++)
+       if (G_VALUE_TYPE (&node->values[v]) != G_TYPE_INVALID)
          g_value_unset (&node->values[v]);
     }
   g_array_free (model->files, TRUE);
@@ -1000,7 +1069,7 @@ gtk_file_system_model_finalize (GObject *object)
   if (model->filter)
     g_object_unref (model->filter);
 
-  g_slice_free1 (sizeof (GType) * n_columns, model->column_types);
+  g_slice_free1 (sizeof (GType) * model->n_columns, model->column_types);
 
   _gtk_tree_data_list_header_free (model->sort_list);
   if (model->default_sort_destroy)
@@ -1033,6 +1102,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 +1123,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 +1145,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,
@@ -1113,24 +1183,25 @@ gtk_file_system_model_got_files (GObject *object, GAsyncResult *res, gpointer da
     }
   else
     {
-      g_file_enumerator_close_async (enumerator, 
-                                     IO_PRIORITY,
-                                     model->cancellable,
-                                     gtk_file_system_model_closed_enumerator,
-                                     model);
-      if (model->dir_thaw_source != 0)
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
         {
-          g_source_remove (model->dir_thaw_source);
-          model->dir_thaw_source = 0;
-          _gtk_file_system_model_thaw_updates (model);
-        }
+          g_file_enumerator_close_async (enumerator,
+                                         IO_PRIORITY,
+                                         model->cancellable,
+                                         gtk_file_system_model_closed_enumerator,
+                                         NULL);
+          if (model->dir_thaw_source != 0)
+            {
+              g_source_remove (model->dir_thaw_source);
+              model->dir_thaw_source = 0;
+              thaw_updates (model);
+            }
 
-      g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error);
+          g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error);
+        }
 
       if (error)
         g_error_free (error);
-
-      g_object_unref (model);
     }
 
   gdk_threads_leave ();
@@ -1144,12 +1215,20 @@ 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;
 
-  _gtk_file_system_model_update_file (model, file, info, TRUE);
+  gdk_threads_enter ();
+
+  _gtk_file_system_model_update_file (model, file, info);
+
+  id = node_get_for_file (model, file);
+  gtk_file_system_model_sort_node (model, id);
+
+  gdk_threads_leave ();
 }
 
 static void
@@ -1174,7 +1253,9 @@ gtk_file_system_model_monitor_change (GFileMonitor *      monitor,
                                  model);
         break;
       case G_FILE_MONITOR_EVENT_DELETED:
+       gdk_threads_enter ();
         remove_file (model, file);
+       gdk_threads_leave ();
         break;
       case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
         /* FIXME: use freeze/thaw with this somehow? */
@@ -1198,9 +1279,11 @@ gtk_file_system_model_got_enumerator (GObject *dir, GAsyncResult *res, gpointer
   enumerator = g_file_enumerate_children_finish (G_FILE (dir), res, &error);
   if (enumerator == NULL)
     {
-      g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error);
-      g_object_unref (model);
-      g_error_free (error);
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+      {
+        g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error);
+        g_error_free (error);
+      }
     }
   else
     {
@@ -1270,7 +1353,6 @@ gtk_file_system_model_set_directory (GtkFileSystemModel *model,
   model->dir = g_object_ref (dir);
   model->attributes = g_strdup (attributes);
 
-  g_object_ref (model);
   g_file_enumerate_children_async (model->dir,
                                    attributes,
                                    G_FILE_QUERY_INFO_NONE,
@@ -1333,7 +1415,7 @@ _gtk_file_system_model_new (GtkFileSystemModelGetValue get_func,
 /**
  * _gtk_file_system_model_new_for_directory:
  * @directory: the directory to show.
- * @attributes: attributes to immediately load or %NULL for all
+ * @attributes: (allow-none): attributes to immediately load or %NULL for all
  * @get_func: function that the model should call to query data about a file
  * @get_data: user data to pass to the @get_func
  * @n_columns: number of columns
@@ -1383,16 +1465,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);
 }
 
 /**
@@ -1465,6 +1545,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
@@ -1492,7 +1596,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
@@ -1510,6 +1614,32 @@ _gtk_file_system_model_iter_is_visible (GtkFileSystemModel *model,
   return node->visible;
 }
 
+/**
+ * _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
@@ -1677,6 +1807,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
@@ -1707,8 +1864,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);
 }
 
@@ -1727,6 +1884,7 @@ remove_file (GtkFileSystemModel *model,
 {
   FileModelNode *node;
   guint id;
+  guint row;
 
   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
   g_return_if_fail (G_IS_FILE (file));
@@ -1736,16 +1894,22 @@ remove_file (GtkFileSystemModel *model,
     return;
 
   node = get_node (model, id);
-  node_set_visible (model, id, FALSE);
+  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);
-  /* 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 */
+
+  emit_row_deleted_for_row (model, row);
 }
 
 /**
@@ -1753,7 +1917,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 
@@ -1762,8 +1925,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;
@@ -1775,7 +1937,10 @@ _gtk_file_system_model_update_file (GtkFileSystemModel *model,
 
   id = node_get_for_file (model, file);
   if (id == 0)
-    add_file (model, file, info);
+    {
+      add_file (model, file, info);
+      id = node_get_for_file (model, file);
+    }
 
   node = get_node (model, id);
 
@@ -1792,32 +1957,35 @@ _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);
 }
 
 /**
  * _gtk_file_system_model_set_filter:
  * @mode: a #GtkFileSystemModel
- * @filter: %NULL or filter to use
+ * @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,
                                   GtkFileFilter *          filter)
 {
+  GtkFileFilter *old_filter;
+
   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
   g_return_if_fail (filter == NULL || GTK_IS_FILE_FILTER (filter));
   
   if (filter)
     g_object_ref (filter);
-  if (model->filter)
-    g_object_unref (model->filter);
+
+  old_filter = model->filter;
   model->filter = filter;
 
+  if (old_filter)
+    g_object_unref (old_filter);
+
   gtk_file_system_model_refilter_all (model);
 }
 
@@ -1837,7 +2005,7 @@ _gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *ite
   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);
+  node_set_visible_and_filtered_out (model, 0, TRUE, FALSE);
   ITER_INIT_FROM_INDEX (model, iter, 0);
 }
 
@@ -1855,21 +2023,20 @@ _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);
+  node_set_visible_and_filtered_out (model, 0, FALSE, 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));
 
@@ -1877,14 +2044,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;
 
@@ -1912,7 +2078,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);
         }
     }
 }