]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilesystemmodel.c
Revert the patch to #137520, as 2.4.1 is for conservative bug fixes only.
[~andy/gtk] / gtk / gtkfilesystemmodel.c
index eca5a0b1c0941fc57a46c30a70d77b26b27cf578..79e08674ccc3f599043b0bcb67a8aff53b383832 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
+#include <config.h>
+#include <string.h>
+
 #include "gtkfilesystemmodel.h"
 #include "gtkfilesystem.h"
-#include <gtk/gtktreemodel.h>
-#include <string.h>
+#include "gtkintl.h"
+#include "gtktreednd.h"
+#include "gtktreemodel.h"
 
 typedef struct _GtkFileSystemModelClass GtkFileSystemModelClass;
 typedef struct _FileModelNode           FileModelNode;
@@ -43,6 +47,13 @@ struct _GtkFileSystemModel
   GtkFileInfoType types;
   FileModelNode  *roots;
   GtkFileFolder  *root_folder;
+  GtkFilePath    *root_path;
+
+  GtkFileSystemModelFilter filter_func;
+  gpointer filter_data;
+
+  GSList *idle_clears;
+  GSource *idle_clear_source;
 
   gushort max_depth;
   
@@ -50,6 +61,7 @@ struct _GtkFileSystemModel
   guint show_folders : 1;
   guint show_files : 1;
   guint folders_only : 1;
+  guint has_editable : 1;
 };
 
 struct _FileModelNode
@@ -65,6 +77,7 @@ struct _FileModelNode
   GtkFileSystemModel *model;
 
   guint ref_count;
+  guint n_referenced_children;
 
   gushort depth;
 
@@ -72,6 +85,7 @@ struct _FileModelNode
   guint is_dummy : 1;
   guint is_visible : 1;
   guint loaded : 1;
+  guint idle_clear : 1;
 };
 
 static void gtk_file_system_model_class_init   (GtkFileSystemModelClass *class);
@@ -79,6 +93,8 @@ static void gtk_file_system_model_iface_init   (GtkTreeModelIface       *iface);
 static void gtk_file_system_model_init         (GtkFileSystemModel      *model);
 static void gtk_file_system_model_finalize     (GObject                 *object);
 
+static void drag_source_iface_init (GtkTreeDragSourceIface *iface);
+
 static GtkTreeModelFlags gtk_file_system_model_get_flags       (GtkTreeModel *tree_model);
 static gint              gtk_file_system_model_get_n_columns   (GtkTreeModel *tree_model);
 static GType             gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
@@ -113,6 +129,12 @@ static void              gtk_file_system_model_ref_node        (GtkTreeModel *tr
 static void              gtk_file_system_model_unref_node      (GtkTreeModel *tree_model,
                                                                GtkTreeIter  *iter);
 
+static gboolean drag_source_row_draggable (GtkTreeDragSource   *drag_source,
+                                          GtkTreePath         *path);
+static gboolean drag_source_drag_data_get (GtkTreeDragSource   *drag_source,
+                                          GtkTreePath         *path,
+                                          GtkSelectionData    *selection_data);
+
 static FileModelNode *file_model_node_new        (GtkFileSystemModel *model,
                                                  const GtkFilePath  *path);
 static void           file_model_node_free       (FileModelNode      *node);
@@ -120,6 +142,10 @@ static void           file_model_node_ref        (FileModelNode      *node);
 static void           file_model_node_unref      (GtkFileSystemModel *model,
                                                  FileModelNode      *node);
 
+static void file_model_node_idle_clear        (FileModelNode *node);
+static void file_model_node_idle_clear_cancel (FileModelNode *node);
+static void file_model_node_child_unref       (FileModelNode *parent);
+
 static const GtkFileInfo *file_model_node_get_info     (GtkFileSystemModel *model,
                                                        FileModelNode      *node);
 static gboolean           file_model_node_is_visible   (GtkFileSystemModel *model,
@@ -129,6 +155,11 @@ static void               file_model_node_clear        (GtkFileSystemModel *mode
 static FileModelNode *    file_model_node_get_children (GtkFileSystemModel *model,
                                                        FileModelNode      *node);
 
+#if 0
+static void roots_changed_callback (GtkFileSystem      *file_system,
+                                   GtkFileSystemModel *model);
+#endif
+                                   
 static void deleted_callback       (GtkFileFolder *folder,
                                    FileModelNode *node);
 static void files_added_callback   (GtkFileFolder *folder,
@@ -153,6 +184,8 @@ static void root_files_removed_callback (GtkFileFolder      *folder,
                                         GSList             *paths,
                                         GtkFileSystemModel *model);
 
+static GObjectClass *parent_class = NULL;
+
 GType
 _gtk_file_system_model_get_type (void)
 {
@@ -176,8 +209,15 @@ _gtk_file_system_model_get_type (void)
       static const GInterfaceInfo file_system_info =
       {
        (GInterfaceInitFunc) gtk_file_system_model_iface_init, /* interface_init */
-       NULL,                                                 /* interface_finalize */
-       NULL                                                  /* interface_data */
+       NULL,                                                  /* interface_finalize */
+       NULL                                                   /* interface_data */
+      };
+
+      static const GInterfaceInfo drag_source_info =
+      {
+       (GInterfaceInitFunc) drag_source_iface_init,           /* interface_init */
+       NULL,                                                  /* interface_finalize */
+       NULL                                                   /* interface_data */
       };
 
       file_system_model_type = g_type_register_static (G_TYPE_OBJECT,
@@ -186,6 +226,9 @@ _gtk_file_system_model_get_type (void)
       g_type_add_interface_static (file_system_model_type,
                                   GTK_TYPE_TREE_MODEL,
                                   &file_system_info);
+      g_type_add_interface_static (file_system_model_type,
+                                  GTK_TYPE_TREE_DRAG_SOURCE,
+                                  &drag_source_info);
     }
 
   return file_system_model_type;
@@ -195,7 +238,9 @@ static void
 gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
-  
+
+  parent_class = g_type_class_peek_parent (class);
+
   gobject_class->finalize = gtk_file_system_model_finalize;
 }
 
@@ -230,17 +275,34 @@ static void
 gtk_file_system_model_finalize (GObject *object)
 {
   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
-  FileModelNode *children;
+  FileModelNode *children, *next;
 
   if (model->root_folder)
     g_object_unref (model->root_folder);
 
+  if (model->root_path)
+    gtk_file_path_free (model->root_path);
+
+  if (model->file_system)
+    g_object_unref (model->file_system);
+
   children = model->roots;
   while (children)
     {
+      next = children->next;
       file_model_node_free (children);
-      children = children->next;
+      children = next;
     }
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+drag_source_iface_init (GtkTreeDragSourceIface *iface)
+{
+  iface->row_draggable = drag_source_row_draggable;
+  iface->drag_data_get = drag_source_drag_data_get;
+  iface->drag_data_delete = NULL;
 }
 
 /*
@@ -253,7 +315,7 @@ gtk_file_system_model_get_flags (GtkTreeModel *tree_model)
   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
   GtkTreeModelFlags flags = GTK_TREE_MODEL_ITERS_PERSIST;
 
-  if (model->max_depth == 1)
+  if (model->max_depth == 0)
     flags |= GTK_TREE_MODEL_LIST_ONLY;
 
   return flags;
@@ -351,18 +413,31 @@ gtk_file_system_model_get_value (GtkTreeModel *tree_model,
 {
   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
   FileModelNode *node = iter->user_data;
+  const GtkFileInfo *info;
   
   switch (column)
     {
     case GTK_FILE_SYSTEM_MODEL_INFO:
+      if (model->has_editable && node == model->roots)
+       info = NULL;
+      else
+       info = file_model_node_get_info (model, node);
+
       g_value_init (value, GTK_TYPE_FILE_INFO);
-      g_value_set_boxed (value, file_model_node_get_info (model, node));
+      g_value_set_boxed (value, info);
       break;
     case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
       {
-       const GtkFileInfo *info = file_model_node_get_info (model, node);
        g_value_init (value, G_TYPE_STRING);
-       g_value_set_string (value, gtk_file_info_get_display_name (info));
+
+       if (model->has_editable && node == model->roots)
+         g_value_set_string (value, "");
+       else
+         {
+           const GtkFileInfo *info = file_model_node_get_info (model, node);
+
+           g_value_set_string (value, gtk_file_info_get_display_name (info));
+         }
       }
       break;
     default:
@@ -518,12 +593,64 @@ gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
                         iter->user_data);
 }
 
+static gboolean
+drag_source_row_draggable (GtkTreeDragSource *drag_source,
+                          GtkTreePath       *path)
+{
+  GtkFileSystemModel *model;
+  GtkTreeIter iter;
+  FileModelNode *node;
+
+  model = GTK_FILE_SYSTEM_MODEL (drag_source);
+
+  if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
+    return FALSE;
+
+  if (!model->has_editable)
+    return TRUE;
+
+  node = iter.user_data;
+  return (node != model->roots);
+}
+
+static gboolean
+drag_source_drag_data_get (GtkTreeDragSource *drag_source,
+                          GtkTreePath       *path,
+                          GtkSelectionData  *selection_data)
+{
+  GtkFileSystemModel *model;
+  GtkTreeIter iter;
+  const GtkFilePath *file_path;
+  char *uri;
+  char *uris;
+
+  model = GTK_FILE_SYSTEM_MODEL (drag_source);
+
+  if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
+    return FALSE;
+
+  file_path = _gtk_file_system_model_get_path (model, &iter);
+  g_assert (file_path != NULL);
+
+  uri = gtk_file_system_path_to_uri (model->file_system, file_path);
+  uris = g_strconcat (uri, "\r\n", NULL);
+
+  gtk_selection_data_set (selection_data,
+                         gdk_atom_intern ("text/uri-list", FALSE),
+                         8,
+                         uris,
+                         strlen (uris) + 1);
+
+  g_free (uri);
+  g_free (uris);
+
+  return TRUE;
+}
+
 /**
  * _gtk_file_system_model_new:
  * @file_system: an object implementing #GtkFileSystem
- * @root_path: the path of root of the file system to display,
- *            or %NULL to display starting from the
- *            root or roots of the fielsystem.
+ * @root_path: the path of root of the file system to display
  * @max_depth: the maximum depth from the children of @root_path
  *             or the roots of the file system to display in
  *             the file selector). A depth of 0 displays
@@ -549,9 +676,11 @@ _gtk_file_system_model_new (GtkFileSystem     *file_system,
                            GtkFileInfoType    types)
 {
   GtkFileSystemModel *model;
-  GSList *roots, *tmp_list;
+  GSList *roots = NULL;
+  GSList *tmp_list;
 
   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+  g_return_val_if_fail (root_path != NULL, NULL);
 
   model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
   model->file_system = g_object_ref (file_system);
@@ -564,7 +693,8 @@ _gtk_file_system_model_new (GtkFileSystem     *file_system,
   if (root_path)
     {
       GSList *child_paths;
-      
+
+      model->root_path = gtk_file_path_copy (root_path);
       model->root_folder = gtk_file_system_get_folder (file_system, root_path,
                                                       model->types,
                                                       NULL);   /* NULL-GError */
@@ -576,18 +706,24 @@ _gtk_file_system_model_new (GtkFileSystem     *file_system,
        {
          roots = child_paths;
          
-         g_signal_connect (model->root_folder, "deleted",
-                           G_CALLBACK (root_deleted_callback), model);
-         g_signal_connect (model->root_folder, "files_added",
-                           G_CALLBACK (root_files_added_callback), model);
-         g_signal_connect (model->root_folder, "files_changed",
-                           G_CALLBACK (root_files_changed_callback), model);
-         g_signal_connect (model->root_folder, "files_removed",
-                           G_CALLBACK (root_files_removed_callback), model);
+         g_signal_connect_object (model->root_folder, "deleted",
+                                  G_CALLBACK (root_deleted_callback), model, 0);
+         g_signal_connect_object (model->root_folder, "files-added",
+                                  G_CALLBACK (root_files_added_callback), model, 0);
+         g_signal_connect_object (model->root_folder, "files-changed",
+                                  G_CALLBACK (root_files_changed_callback), model, 0);
+         g_signal_connect_object (model->root_folder, "files-removed",
+                                  G_CALLBACK (root_files_removed_callback), model, 0);
        }
     }
+#if 0
   else
-    roots = gtk_file_system_list_roots (file_system);
+    {
+      roots = gtk_file_system_list_roots (file_system);
+      g_signal_connect_object (file_system, "roots-changed",
+                              G_CALLBACK (roots_changed_callback), model, 0);
+    }
+#endif
 
   roots = gtk_file_paths_sort (roots);
   
@@ -646,9 +782,8 @@ model_refilter_recurse (GtkFileSystemModel *model,
          GtkTreeIter iter;
 
          iter.user_data = nodes;
-         gtk_tree_model_row_inserted (tree_model, path, &iter);
-
          nodes->is_visible = TRUE;
+         gtk_tree_model_row_inserted (tree_model, path, &iter);
        }
       else
        model_refilter_recurse (model, nodes, path);
@@ -670,6 +805,16 @@ model_refilter_recurse (GtkFileSystemModel *model,
     }
 }
 
+static void
+model_refilter_all (GtkFileSystemModel *model)
+{
+  GtkTreePath *path;
+
+  path = gtk_tree_path_new ();
+  model_refilter_recurse (model, NULL, path);
+  gtk_tree_path_free (path);
+}
+
 /**
  * _gtk_file_system_model_set_show_hidden:
  * @model: a #GtkFileSystemModel
@@ -686,13 +831,8 @@ _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
 
   if (show_hidden != model->show_hidden)
     {
-      GtkTreePath *path;
-
       model->show_hidden = show_hidden;
-
-      path = gtk_tree_path_new ();
-      model_refilter_recurse (model, NULL, path);
-      gtk_tree_path_free (path);
+      model_refilter_all (model);
     }
 }
 
@@ -712,13 +852,8 @@ _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
 
   if (show_folders != model->show_folders)
     {
-      GtkTreePath *path;
-
       model->show_folders = show_folders;
-
-      path = gtk_tree_path_new ();
-      model_refilter_recurse (model, NULL, path);
-      gtk_tree_path_free (path);
+      model_refilter_all (model);
     }
 }
 
@@ -739,13 +874,8 @@ _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
 
   if (show_files != model->show_files)
     {
-      GtkTreePath *path;
-
       model->show_files = show_files;
-
-      path = gtk_tree_path_new ();
-      model_refilter_recurse (model, NULL, path);
-      gtk_tree_path_free (path);
+      model_refilter_all (model);
     }
 }
 
@@ -763,13 +893,22 @@ _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
  *   is owned by @model and must not be modified or freed.
  *   If you want to save the information for later use,
  *   you must make a copy, since the structure may be
- *   freed on later changes to the file system.
+ *   freed on later changes to the file system.  If you have
+ *   called _gtk_file_system_model_add_editable() and the @iter
+ *   corresponds to the row that this function returned, the
+ *   return value will be NULL.
  **/
 const GtkFileInfo *
 _gtk_file_system_model_get_info (GtkFileSystemModel *model,
                                 GtkTreeIter        *iter)
 {
-  return file_model_node_get_info (model, iter->user_data);
+  FileModelNode *node;
+
+  node = iter->user_data;
+  if (model->has_editable && node == model->roots)
+    return NULL;
+  else
+    return file_model_node_get_info (model, node);
 }
 
 /**
@@ -790,7 +929,13 @@ _gtk_file_system_model_get_path (GtkFileSystemModel *model,
 {
   FileModelNode *node = iter->user_data;
 
-  return node->path;
+  if (model->has_editable && node == model->roots)
+    return NULL;
+
+  if (node->is_dummy)
+    return node->parent->path;
+  else
+    return node->path;
 }
 
 static void
@@ -829,24 +974,23 @@ find_child_node (GtkFileSystemModel *model,
                 
 
 static FileModelNode *
-find_and_ref_path (GtkFileSystemModel *model,
-                 const GtkFilePath   *path)
+find_and_ref_path (GtkFileSystemModel  *model,
+                  const GtkFilePath   *path,
+                  GSList             **cleanups)
 {
   GtkFilePath *parent_path;
   FileModelNode *parent_node;
   FileModelNode *child_node;
   GtkFileFolder *folder;
 
-  if (!gtk_file_system_get_parent (model->file_system, path, &parent_path, NULL))
+  if (gtk_file_path_compare (path, model->root_path) == 0
+      || !gtk_file_system_get_parent (model->file_system, path, &parent_path, NULL))
     return NULL;
 
   if (parent_path)
     {
-      parent_node = find_and_ref_path (model, parent_path);
+      parent_node = find_and_ref_path (model, parent_path, cleanups);
       gtk_file_path_free (parent_path);
-
-      if (!parent_node)
-       return NULL;
     }
   else
     parent_node = NULL;
@@ -862,18 +1006,45 @@ find_and_ref_path (GtkFileSystemModel *model,
                                       path,
                                       model->types,
                                       NULL);   /* NULL-GError */
-
-  child_node = find_child_node (model, parent_node, path);
-  if (child_node)
+  if (folder)
     {
-      file_model_node_ref (child_node);
-      return child_node;
+      *cleanups = g_slist_prepend (*cleanups, folder);
+
+      child_node = find_child_node (model, parent_node, path);
+      if (child_node)
+       {
+         file_model_node_ref (child_node);
+         return child_node;
+       }
     }
 
   if (parent_node)
     unref_node_and_parents (model, parent_node);
 
-  return FALSE;
+  return NULL;
+}
+
+/**
+ * _gtk_file_system_model_set_filter:
+ * @mode: a #GtkFileSystemModel
+ * @filter: function to be called for each file
+ * @user_data: data to pass to @filter
+ * 
+ * Sets a callback called for each file/directory to see whether
+ * it should be included in model. If this function was made
+ * public, we'd want to include a GDestroyNotify as well.
+ **/
+void
+_gtk_file_system_model_set_filter (GtkFileSystemModel      *model,
+                                  GtkFileSystemModelFilter filter,
+                                  gpointer                 user_data)
+{
+  g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
+  
+  model->filter_func = filter;
+  model->filter_data = user_data;
+
+  model_refilter_all (model);
 }
 
 /**
@@ -908,7 +1079,8 @@ _gtk_file_system_model_path_do (GtkFileSystemModel       *model,
                               GtkFileSystemModelPathFunc func,
                               gpointer                   user_data)
 {
-  FileModelNode *node = find_and_ref_path (model, path);
+  GSList *cleanups = NULL;
+  FileModelNode *node = find_and_ref_path (model, path, &cleanups);
 
   if (node)
     {
@@ -922,11 +1094,77 @@ _gtk_file_system_model_path_do (GtkFileSystemModel       *model,
 
       gtk_tree_path_free (path);
       unref_node_and_parents (model, node);
-
-      return TRUE;
     }
-  else
-    return FALSE;
+
+  g_slist_foreach (cleanups, (GFunc)g_object_unref, NULL);
+  g_slist_free (cleanups);
+
+  return node != NULL;
+}
+
+/**
+ * _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)
+{
+  FileModelNode *node;
+  GtkTreePath *path;
+
+  g_return_if_fail (!model->has_editable);
+
+  model->has_editable = TRUE;
+
+  node = file_model_node_new (model, NULL);
+  node->is_visible = TRUE;
+
+  node->next = model->roots;
+  model->roots = node;
+
+  file_model_node_ref (node);
+
+  path = gtk_tree_path_new ();
+  gtk_tree_path_append_index (path, 0);
+  iter->user_data = node;
+
+  gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, iter);
+
+  gtk_tree_path_free (path);
+}
+
+/**
+ * _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)
+{
+  GtkTreePath *path;
+
+  g_return_if_fail (model->has_editable);
+
+  model->has_editable = FALSE;
+  file_model_node_unref (model, model->roots);
+
+  model->roots = model->roots->next;
+
+  path = gtk_tree_path_new ();
+  gtk_tree_path_append_index (path, 0);
+
+  gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
+
+  gtk_tree_path_free (path);
 }
 
 static FileModelNode *
@@ -944,13 +1182,7 @@ file_model_node_new (GtkFileSystemModel *model,
 static void
 file_model_node_free (FileModelNode *node)
 {
-  if (node->children)
-    {
-      FileModelNode *children;
-      
-      for (children = node->children; children; children = children->next)
-       file_model_node_free (children);
-    }
+  file_model_node_clear (node->model, node);
   
   if (node->path)
     gtk_file_path_free (node->path);
@@ -958,9 +1190,6 @@ file_model_node_free (FileModelNode *node)
   if (node->info)
     gtk_file_info_free (node->info);
 
-  if (node->folder)
-    g_object_unref (node->folder);
-
   g_free (node);
 }
 
@@ -973,7 +1202,7 @@ file_model_node_get_info (GtkFileSystemModel *model,
       if (node->is_dummy)
        {
          node->info = gtk_file_info_new ();
-         gtk_file_info_set_display_name (node->info, "(Empty)");
+         gtk_file_info_set_display_name (node->info, _("(Empty)"));
        }
       else if (node->parent || model->root_folder)
        {
@@ -981,6 +1210,7 @@ file_model_node_get_info (GtkFileSystemModel *model,
                                                 node->path,
                                                 NULL); /* NULL-GError */
        }
+#if 0
       else
        {
          node->info = gtk_file_system_get_root_info (model->file_system,
@@ -988,6 +1218,7 @@ file_model_node_get_info (GtkFileSystemModel *model,
                                                      model->types,
                                                      NULL);  /* NULL-GError */
        }
+#endif
     }
 
   return node->info;
@@ -997,22 +1228,32 @@ static gboolean
 file_model_node_is_visible (GtkFileSystemModel *model,
                            FileModelNode      *node)
 {
-  if (model->show_hidden && model->show_folders && model->show_files)
-    return TRUE;
-  else
+  if (model->show_folders != model->show_files ||
+      !model->show_hidden ||
+      model->filter_func)
     {
       const GtkFileInfo *info = file_model_node_get_info (model, node);
-      gboolean is_folder = gtk_file_info_get_is_folder (info);
 
-      if (!model->show_folders && is_folder)
-       return FALSE;
-      if (!model->show_files && !is_folder)
+      if (!info)
+       {
+         /* File probably disappeared underneath us or resides in a
+            directory where we have only partial access rights.  */
+         return FALSE;
+       }
+
+      if (model->show_folders != model->show_files &&
+         model->show_folders != gtk_file_info_get_is_folder (info))
        return FALSE;
+
       if (!model->show_hidden && gtk_file_info_get_is_hidden (info))
        return FALSE;
 
-      return TRUE;
+      if (model->filter_func &&
+         !model->filter_func (model, node->path, info, model->filter_data))
+       return FALSE;
     }
+
+  return TRUE;
 }
 
 static void
@@ -1021,11 +1262,7 @@ file_model_node_clear (GtkFileSystemModel *model,
 {
   FileModelNode *children;
   
-  if (node->folder)
-    {
-      g_object_unref (node->folder);
-      node->folder = NULL;
-    }
+  file_model_node_idle_clear_cancel (node);
   
   children = node->children;
   node->children = NULL;
@@ -1035,19 +1272,90 @@ file_model_node_clear (GtkFileSystemModel *model,
     {
       FileModelNode *next = children->next;
       
-      file_model_node_clear (model, children);
       file_model_node_free (children);
       
       children = next;
     }
 
-  node->ref_count = 0;
+  if (node->folder)
+    {
+      /* Unreffing node->folder may cause roots_changed,
+       * so we need to be careful about ordering.
+       */
+      GtkFileFolder *folder = node->folder;
+      node->folder = NULL;
+
+      g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (deleted_callback), node);
+      g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_added_callback), node);
+      g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_changed_callback), node);
+      g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_removed_callback), node);
+      
+      g_object_unref (folder);
+    }
 }
 
 static void
 file_model_node_ref (FileModelNode *node)
 {
   node->ref_count++;
+  if (node->ref_count == 1 && node->parent)
+    node->parent->n_referenced_children++;
+}
+
+static gboolean
+idle_clear_callback (GtkFileSystemModel *model)
+{
+  while (model->idle_clears)
+    {
+      FileModelNode *node = model->idle_clears->data;
+      model->idle_clears = g_slist_delete_link (model->idle_clears, model->idle_clears);
+
+      node->idle_clear = FALSE;
+      file_model_node_clear (node->model, node);
+    }
+
+  return FALSE;
+}
+static void
+file_model_node_idle_clear (FileModelNode *node)
+{
+  if (!node->idle_clear)
+    {
+      GtkFileSystemModel *model = node->model;
+
+      node->idle_clear = TRUE;
+      if (!model->idle_clears)
+       {
+         model->idle_clear_source = g_idle_source_new ();
+         g_source_set_priority (model->idle_clear_source, G_PRIORITY_HIGH);
+         g_source_set_closure (model->idle_clear_source,
+                               g_cclosure_new_object (G_CALLBACK (idle_clear_callback),
+                                                      G_OBJECT (model)));
+         g_source_attach (model->idle_clear_source, NULL);
+       }
+
+      model->idle_clears = g_slist_prepend (model->idle_clears, node);
+      node->idle_clear = TRUE;
+    }
+}
+
+static void
+file_model_node_idle_clear_cancel (FileModelNode *node)
+{
+  if (node->idle_clear)
+    {
+      GtkFileSystemModel *model = node->model;
+
+      model->idle_clears = g_slist_remove (model->idle_clears, node);
+      if (!model->idle_clears)
+       {
+         g_source_destroy (model->idle_clear_source);
+         model->idle_clear_source = NULL;
+       }
+      
+      node->idle_clear = FALSE;
+    }
 }
 
 static void
@@ -1056,7 +1364,19 @@ file_model_node_unref (GtkFileSystemModel *model,
 {
   node->ref_count--;
   if (node->ref_count == 0)
-    file_model_node_clear (model, node);
+    {
+      file_model_node_clear (model, node);
+      if (node->parent)
+       file_model_node_child_unref (node->parent);
+    }
+}
+
+static void
+file_model_node_child_unref (FileModelNode *parent)
+{
+  parent->n_referenced_children--;
+  if (parent->n_referenced_children == 0)
+    file_model_node_idle_clear (parent);
 }
 
 static FileModelNode *
@@ -1072,6 +1392,8 @@ file_model_node_get_children (GtkFileSystemModel *model,
       gboolean has_children = FALSE;
       gboolean is_folder = node->depth < model->max_depth && gtk_file_info_get_is_folder (info);
 
+      file_model_node_idle_clear_cancel (node);
+
       if (is_folder)
        node->folder = gtk_file_system_get_folder (model->file_system,
                                                   node->path,
@@ -1105,11 +1427,11 @@ file_model_node_get_children (GtkFileSystemModel *model,
 
          g_signal_connect (node->folder, "deleted",
                            G_CALLBACK (deleted_callback), node);
-         g_signal_connect (node->folder, "files_added",
+         g_signal_connect (node->folder, "files-added",
                            G_CALLBACK (files_added_callback), node);
-         g_signal_connect (node->folder, "files_changed",
+         g_signal_connect (node->folder, "files-changed",
                            G_CALLBACK (files_changed_callback), node);
-         g_signal_connect (node->folder, "files_removed",
+         g_signal_connect (node->folder, "files-removed",
                            G_CALLBACK (files_removed_callback), node);
 
          g_object_set_data (G_OBJECT (node->folder), "model-node", node);
@@ -1231,7 +1553,6 @@ do_files_added (GtkFileSystemModel *model,
                  
                  parent_node->children = parent_node->children->next;
                  parent_node->has_dummy = FALSE;
-                 file_model_node_free (dummy);
 
                  dummy_path = gtk_tree_path_copy (path);
                  gtk_tree_path_up (dummy_path);
@@ -1239,6 +1560,10 @@ do_files_added (GtkFileSystemModel *model,
                  
                  gtk_tree_model_row_deleted (tree_model, dummy_path);
                  gtk_tree_path_free (dummy_path);
+
+                 if (dummy->ref_count)
+                   file_model_node_child_unref (parent_node);
+                 file_model_node_free (dummy);
                }
              
              gtk_tree_path_next (path);
@@ -1391,7 +1716,7 @@ do_files_removed (GtkFileSystemModel *model,
              dummy->is_visible = TRUE;
              dummy->parent = parent_node;
              dummy->is_dummy = TRUE;
-             
+
              parent_node->children = dummy;
              parent_node->has_dummy = TRUE;
 
@@ -1409,6 +1734,9 @@ do_files_removed (GtkFileSystemModel *model,
          else
            model->roots = next;
 
+         if (parent_node && children->ref_count)
+           file_model_node_child_unref (parent_node);
+             
          if (children->is_visible)
            gtk_tree_model_row_deleted (tree_model, path);
 
@@ -1426,6 +1754,105 @@ do_files_removed (GtkFileSystemModel *model,
   g_slist_free (sorted_paths);
 }
 
+#if 0
+static void
+roots_changed_callback (GtkFileSystem      *file_system,
+                       GtkFileSystemModel *model)
+{
+  GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
+  GSList *new_roots;
+  GSList *tmp_list;
+  FileModelNode *children;
+  FileModelNode *prev = NULL;
+  GtkTreePath *path;
+      
+  new_roots = gtk_file_system_list_roots (file_system);
+  new_roots = gtk_file_paths_sort (new_roots);
+
+  children = model->roots;
+  tmp_list = new_roots;
+  path = gtk_tree_path_new ();
+  gtk_tree_path_down (path);
+
+  while (children || tmp_list)
+    {
+      FileModelNode *next = NULL;
+      int cmp;
+
+      if (tmp_list && children)
+       cmp = gtk_file_path_compare (children->path, tmp_list->data);
+      else if (children)
+       cmp = -1;
+      else
+       cmp = 1;
+
+      if (cmp < 0)
+       {
+         next = children->next;
+         
+         if (prev)
+           prev->next = children->next;
+         else
+           model->roots = children->next;
+
+         if (children->is_visible)
+           gtk_tree_model_row_deleted (tree_model, path);
+
+         file_model_node_free (children);
+       }
+      else if (cmp == 0)
+       {
+         /* Already there
+          */
+         next = children->next;
+         prev = children;
+         if (children->is_visible)
+           gtk_tree_path_next (path);
+       }
+      else 
+       {
+         GtkTreeIter iter;
+         FileModelNode *node = file_model_node_new (model, tmp_list->data);
+         node->is_visible = file_model_node_is_visible (model, node);
+         node->next = children;
+         node->depth = 0;
+
+         if (prev)
+           prev->next = node;
+         else
+           model->roots = node;
+
+         if (node->is_visible)
+           {
+             iter.user_data = node;
+             gtk_tree_model_row_inserted (tree_model, path, &iter);
+
+             if (gtk_file_system_model_iter_has_child (tree_model, &iter))
+               gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
+             
+             gtk_tree_path_next (path);
+           }
+         
+         prev = node;
+       }
+
+      if (cmp <= 0)
+       {
+         children = next;
+       }
+
+      if (cmp >= 0)
+       {
+         gtk_file_path_free (tmp_list->data);
+         tmp_list = tmp_list->next;
+       }
+    }
+  
+  g_slist_free (new_roots);
+  gtk_tree_path_free (path);
+}
+#endif
+
 static void
 deleted_callback (GtkFileFolder      *folder,
                  FileModelNode      *node)
@@ -1485,5 +1912,3 @@ root_files_removed_callback (GtkFileFolder      *folder,
 {
   do_files_removed (model, NULL, paths);
 }
-
-