]> 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 b7a29fe1e3a164f2027cdafcc07c24a3db88754e..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,9 +47,13 @@ struct _GtkFileSystemModel
   GtkFileInfoType types;
   FileModelNode  *roots;
   GtkFileFolder  *root_folder;
+  GtkFilePath    *root_path;
+
+  GtkFileSystemModelFilter filter_func;
+  gpointer filter_data;
 
-  GSource *dummy_idle;
-  GSList *dummy_idle_nodes;
+  GSList *idle_clears;
+  GSource *idle_clear_source;
 
   gushort max_depth;
   
@@ -53,11 +61,12 @@ struct _GtkFileSystemModel
   guint show_folders : 1;
   guint show_files : 1;
   guint folders_only : 1;
+  guint has_editable : 1;
 };
 
 struct _FileModelNode
 {
-  gchar *uri;
+  GtkFilePath *path;
   FileModelNode *next;
 
   GtkFileInfo *info;
@@ -65,15 +74,18 @@ struct _FileModelNode
   
   FileModelNode *children;
   FileModelNode *parent;
+  GtkFileSystemModel *model;
 
   guint ref_count;
+  guint n_referenced_children;
 
   gushort depth;
 
-  guint loaded : 1;
   guint has_dummy : 1;
+  guint is_dummy : 1;
   guint is_visible : 1;
-  guint has_children : 1;
+  guint loaded : 1;
+  guint idle_clear : 1;
 };
 
 static void gtk_file_system_model_class_init   (GtkFileSystemModelClass *class);
@@ -81,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,
@@ -115,17 +129,23 @@ static void              gtk_file_system_model_ref_node        (GtkTreeModel *tr
 static void              gtk_file_system_model_unref_node      (GtkTreeModel *tree_model,
                                                                GtkTreeIter  *iter);
 
-static void queue_dummy_idle   (GtkFileSystemModel *model,
-                               FileModelNode      *node);
-static void unqueue_dummy_idle (GtkFileSystemModel *model,
-                               FileModelNode      *node);
+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        (const gchar        *uri);
+static FileModelNode *file_model_node_new        (GtkFileSystemModel *model,
+                                                 const GtkFilePath  *path);
 static void           file_model_node_free       (FileModelNode      *node);
 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,
@@ -135,6 +155,37 @@ 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,
+                                   GSList        *paths,
+                                   FileModelNode *node);
+static void files_changed_callback (GtkFileFolder *folder,
+                                   GSList        *paths,
+                                   FileModelNode *node);
+static void files_removed_callback (GtkFileFolder *folder,
+                                   GSList        *paths,
+                                   FileModelNode *node);
+
+static void root_deleted_callback       (GtkFileFolder      *folder,
+                                        GtkFileSystemModel *model);
+static void root_files_added_callback   (GtkFileFolder      *folder,
+                                        GSList             *paths,
+                                        GtkFileSystemModel *model);
+static void root_files_changed_callback (GtkFileFolder      *folder,
+                                        GSList             *paths,
+                                        GtkFileSystemModel *model);
+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)
 {
@@ -158,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,
@@ -168,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;
@@ -177,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;
 }
 
@@ -212,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;
 }
 
 /*
@@ -235,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;
@@ -253,11 +333,11 @@ gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
 {
   switch (index)
     {
-    case GTK_FILE_SYSTEM_MODEL_URI:
-      return G_TYPE_STRING;
     case GTK_FILE_SYSTEM_MODEL_INFO:
-      return GTK_TYPE_FILE_INFO;
-    default:
+      return GTK_TYPE_FILE_INFO; 
+    case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
+      return G_TYPE_STRING;
+   default:
       g_assert_not_reached ();
       return G_TYPE_NONE;
     }
@@ -333,14 +413,32 @@ 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_URI:
-      g_value_set_string (value, node->uri);
-      break;
     case GTK_FILE_SYSTEM_MODEL_INFO:
-      g_value_set_boxed (value, file_model_node_get_info (model, node));
+      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, info);
+      break;
+    case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
+      {
+       g_value_init (value, G_TYPE_STRING);
+
+       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:
       g_assert_not_reached ();
@@ -393,23 +491,14 @@ gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
                                      GtkTreeIter  *iter)
 {
   FileModelNode *node = iter->user_data;
+  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
 
-  /* We never want to go into a directory just to
-   * find out if it has children
-   */
-  if (node->loaded)
-    return node->has_children;
+  if (node->depth == model->max_depth)
+    return FALSE;
   else
     {
-      GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
-
-      if (node->depth == model->max_depth)
-       return FALSE;
-      else
-       {
-         const GtkFileInfo *info = file_model_node_get_info (model, node);
-         return gtk_file_info_get_is_folder (info);
-       }
+      const GtkFileInfo *info = file_model_node_get_info (model, node);
+      return gtk_file_info_get_is_folder (info);
     }
 }
 
@@ -504,16 +593,68 @@ 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_uri: the URI of root of the file system to display,
- *            or %NULL to display starting from the
- *            root or roots of the fielsystem.
- * @max_depth: the maximum depth from the children of @root_uri
+ * @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
- *             only the immediate children of @root_uri,
+ *             only the immediate children of @root_path,
  *             or the roots of the filesystem. -1 for no
  *             maximum depth.
  * @types: a bitmask indicating the types of information
@@ -523,21 +664,23 @@ gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
  *
  * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
  * object wraps a #GtkFileSystem interface as a #GtkTreeModel.
- * Using the @root_uri and @max_depth parameters, the tree model
+ * Using the @root_path and @max_depth parameters, the tree model
  * can be restricted to a subportion of the entire file system.
  * 
  * Return value: the newly created #GtkFileSystemModel object.
  **/
 GtkFileSystemModel *
-_gtk_file_system_model_new (GtkFileSystem  *file_system,
-                           const gchar    *root_uri,
-                           gint            max_depth,
-                           GtkFileInfoType types)
+_gtk_file_system_model_new (GtkFileSystem     *file_system,
+                           const GtkFilePath *root_path,
+                           gint               max_depth,
+                           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);
@@ -547,27 +690,47 @@ _gtk_file_system_model_new (GtkFileSystem  *file_system,
     model->max_depth = MIN (max_depth, G_MAXUSHORT);
   model->types = types | GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN;
 
-  if (root_uri)
+  if (root_path)
     {
-      GSList *child_uris;
-      
-      model->root_folder = gtk_file_system_get_folder (file_system, root_uri,
+      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 */
 
       if (model->root_folder &&
          gtk_file_folder_list_children (model->root_folder,
-                                        &child_uris,
+                                        &child_paths,
                                         NULL)) /* NULL-GError */
-       roots = child_uris;
+       {
+         roots = child_paths;
+         
+         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);
   
   for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
     {
-      FileModelNode *node = file_model_node_new (tmp_list->data);
-      g_free (tmp_list->data);
+      FileModelNode *node = file_model_node_new (model, tmp_list->data);
+      gtk_file_path_free (tmp_list->data);
       node->is_visible = file_model_node_is_visible (model, node);
       node->next = model->roots;
       node->depth = 0;
@@ -619,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);
@@ -637,15 +799,20 @@ model_refilter_recurse (GtkFileSystemModel *model,
       nodes = next;
     }
 
-  if (parent && (has_children != parent->has_children))
+  if (parent && !has_children)
     {
-      GtkTreeIter iter;
+      /* Fixme - need to insert dummy node here */
+    }
+}
 
-      parent->has_children = has_children;
+static void
+model_refilter_all (GtkFileSystemModel *model)
+{
+  GtkTreePath *path;
 
-      iter.user_data = parent;
-      gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
-    }
+  path = gtk_tree_path_new ();
+  model_refilter_recurse (model, NULL, path);
+  gtk_tree_path_free (path);
 }
 
 /**
@@ -664,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);
     }
 }
 
@@ -690,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);
     }
 }
 
@@ -717,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);
     }
 }
 
@@ -741,34 +893,49 @@ _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);
 }
 
 /**
- * _gtk_file_system_model_get_uri:
+ * _gtk_file_system_model_get_path:
  * @model: a #GtkFileSystemModel
  * @iter: a #GtkTreeIter pointing to a row of @model
  * 
- * Gets the URI for a particular row in @model. 
+ * Gets the path for a particular row in @model. 
  *
- * Return value: the URI. This string is owned by @model and
- *   or freed. If you want to save the URI for later use,
+ * Return value: the path. This string is owned by @model and
+ *   or freed. If you want to save the path for later use,
  *   you must make a copy, since the string may be freed
  *   on later changes to the file system.
  **/
-const gchar *
-_gtk_file_system_model_get_uri (GtkFileSystemModel *model,
-                               GtkTreeIter        *iter)
+const GtkFilePath *
+_gtk_file_system_model_get_path (GtkFileSystemModel *model,
+                                GtkTreeIter        *iter)
 {
   FileModelNode *node = iter->user_data;
 
-  return node->uri;
+  if (model->has_editable && node == model->roots)
+    return NULL;
+
+  if (node->is_dummy)
+    return node->parent->path;
+  else
+    return node->path;
 }
 
 static void
@@ -781,62 +948,117 @@ unref_node_and_parents (GtkFileSystemModel *model,
 }
 
 static FileModelNode *
-find_and_ref_uri (GtkFileSystemModel *model,
-                 const gchar        *uri)
+find_child_node (GtkFileSystemModel *model,
+                FileModelNode      *parent_node,
+                const GtkFilePath  *path)
 {
-  gchar *parent_uri;
-  FileModelNode *parent_node;
   FileModelNode *children;
   
-  if (!gtk_file_system_get_parent (model->file_system, uri, &parent_uri, NULL))
-    return NULL;
+  if (parent_node)
+    children = file_model_node_get_children (model, parent_node);
+  else
+    children = model->roots;
 
-  if (parent_uri)
+  while (children)
     {
-      parent_node = find_and_ref_uri (model, parent_uri);
-      g_free (parent_uri);
+      if (children->is_visible &&
+         children->path &&
+         gtk_file_path_compare (children->path, path) == 0)
+       return children;
+
+      children = children->next;
+    }
 
-      if (!parent_node)
-       return NULL;
+  return NULL;
+}
+                
+
+static FileModelNode *
+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_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, cleanups);
+      gtk_file_path_free (parent_path);
     }
   else
     parent_node = NULL;
 
-  if (parent_node)
-    children = file_model_node_get_children (model, parent_node);
-  else
-    children = model->roots;
+  child_node = find_child_node (model, parent_node, path);
+  if (child_node)
+    {
+      file_model_node_ref (child_node);
+      return child_node;
+    }
 
-  while (children)
+  folder = gtk_file_system_get_folder (model->file_system,
+                                      path,
+                                      model->types,
+                                      NULL);   /* NULL-GError */
+  if (folder)
     {
-      if (children->is_visible &&
-         strcmp (children->uri, uri) == 0)
+      *cleanups = g_slist_prepend (*cleanups, folder);
+
+      child_node = find_child_node (model, parent_node, path);
+      if (child_node)
        {
-         file_model_node_ref (children);
-         return children;
+         file_model_node_ref (child_node);
+         return child_node;
        }
-
-      children = children->next;
     }
 
   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);
 }
 
 /**
- * _gtk_file_system_model_uri_do:
+ * _gtk_file_system_model_path_do:
  * @model: a #GtkFileSystemModel
- * @uri: a URI pointing to a file in the filesystem
+ * @path: a path pointing to a file in the filesystem
  *       for @model.
  * @func: Function to call with the path and iter corresponding
- *        to @uri.
+ *        to @path.
  * @user_data: data to pass to @func
  * 
- * Locates @uri within @model, referencing
+ * Locates @path within @model, referencing
  * (gtk_tree_model_ref_node ()) all parent nodes,
- * calls @func passing in the path and iter for @uri,
+ * calls @func passing in the path and iter for @path,
  * then unrefs all the parent nodes.
  *
  * The reason for doing this operation as a callback
@@ -848,16 +1070,17 @@ find_and_ref_uri (GtkFileSystemModel *model,
  * This function is particularly useful for expanding
  * a #GtkTreeView to a particular point in the file system.
  * 
- * Return value: %TRUE if the URI was successfully
+ * Return value: %TRUE if the path was successfully
  *  found in @model and @func was called.
  **/
 gboolean
-_gtk_file_system_model_uri_do (GtkFileSystemModel       *model,
-                              const gchar              *uri,
-                              GtkFileSystemModelURIFunc func,
-                              gpointer                  user_data)
+_gtk_file_system_model_path_do (GtkFileSystemModel       *model,
+                              const GtkFilePath         *path,
+                              GtkFileSystemModelPathFunc func,
+                              gpointer                   user_data)
 {
-  FileModelNode *node = find_and_ref_uri (model, uri);
+  GSList *cleanups = NULL;
+  FileModelNode *node = find_and_ref_path (model, path, &cleanups);
 
   if (node)
     {
@@ -871,83 +1094,87 @@ _gtk_file_system_model_uri_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;
 }
 
-static gboolean
-dummy_idle_callback (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)
 {
-  GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
-  GSList *tmp_list;
+  FileModelNode *node;
+  GtkTreePath *path;
 
-  for (tmp_list = model->dummy_idle_nodes; tmp_list; tmp_list = tmp_list->next)
-    {
-      GtkTreeIter iter;
-      GtkTreePath *path;
-      
-      FileModelNode *node = tmp_list->data;
-      g_assert (node->children && !node->children->next && !node->children->children);
+  g_return_if_fail (!model->has_editable);
 
-      iter.user_data = node->children;
-      path = gtk_tree_model_get_path (tree_model, &iter);
-      
-      if (node->children->ref_count)
-       gtk_tree_model_row_deleted (tree_model, path);
+  model->has_editable = TRUE;
 
-      gtk_tree_path_up (path);
-      iter.user_data = node;
-      gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
+  node = file_model_node_new (model, NULL);
+  node->is_visible = TRUE;
 
-      gtk_tree_path_free (path);
+  node->next = model->roots;
+  model->roots = node;
 
-      file_model_node_free (node->children);
-      node->children = NULL;
-      node->has_children = FALSE;
-      node->has_dummy = FALSE;
-    }
+  file_model_node_ref (node);
+
+  path = gtk_tree_path_new ();
+  gtk_tree_path_append_index (path, 0);
+  iter->user_data = node;
 
-  model->dummy_idle_nodes = FALSE;
-  model->dummy_idle = NULL;
+  gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, iter);
 
-  return FALSE;
+  gtk_tree_path_free (path);
 }
 
-static void
-queue_dummy_idle (GtkFileSystemModel *model,
-                 FileModelNode      *node)
+/**
+ * _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)
 {
-  model->dummy_idle_nodes = g_slist_prepend (model->dummy_idle_nodes, node);
+  GtkTreePath *path;
 
-  if (!model->dummy_idle)
-    {
-      model->dummy_idle = g_idle_source_new ();
-      g_source_set_priority (model->dummy_idle, G_PRIORITY_HIGH_IDLE);
-      g_source_set_closure (model->dummy_idle,
-                           g_cclosure_new_object (G_CALLBACK (dummy_idle_callback), G_OBJECT (model)));
-      g_source_attach (model->dummy_idle, NULL);
-    }
-}
+  g_return_if_fail (model->has_editable);
 
-static void
-unqueue_dummy_idle (GtkFileSystemModel *model,
-                   FileModelNode      *node)
-{
-  model->dummy_idle_nodes = g_slist_remove (model->dummy_idle_nodes, node);
-  
-  if (!model->dummy_idle_nodes)
-    g_source_destroy (model->dummy_idle);
+  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 *
-file_model_node_new (const gchar *uri)
+file_model_node_new (GtkFileSystemModel *model,
+                    const GtkFilePath  *path)
 {
   FileModelNode *node = g_new0 (FileModelNode, 1);
 
-  node->uri = g_strdup (uri);
+  node->model = model;
+  node->path = path ? gtk_file_path_copy (path) : NULL;
 
   return node;
 }
@@ -955,23 +1182,14 @@ file_model_node_new (const gchar *uri)
 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->uri)
-    g_free (node->uri);
+  if (node->path)
+    gtk_file_path_free (node->path);
 
   if (node->info)
     gtk_file_info_free (node->info);
 
-  if (node->folder)
-    g_object_unref (node->folder);
-
   g_free (node);
 }
 
@@ -981,24 +1199,26 @@ file_model_node_get_info (GtkFileSystemModel *model,
 {
   if (!node->info)
     {
-      if (node->parent && node->parent->has_dummy)
+      if (node->is_dummy)
        {
          node->info = gtk_file_info_new ();
-         gtk_file_info_set_display_name (node->info, "Loading...");
+         gtk_file_info_set_display_name (node->info, _("(Empty)"));
        }
       else if (node->parent || model->root_folder)
        {
          node->info = gtk_file_folder_get_info (node->parent ? node->parent->folder : model->root_folder,
-                                                node->uri,
+                                                node->path,
                                                 NULL); /* NULL-GError */
        }
+#if 0
       else
        {
          node->info = gtk_file_system_get_root_info (model->file_system,
-                                                     node->uri,
+                                                     node->path,
                                                      model->types,
                                                      NULL);  /* NULL-GError */
        }
+#endif
     }
 
   return node->info;
@@ -1008,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
@@ -1032,37 +1262,100 @@ file_model_node_clear (GtkFileSystemModel *model,
 {
   FileModelNode *children;
   
-  if (node->has_dummy)
-    unqueue_dummy_idle (model, node);
-  
-  if (node->folder)
-    {
-      g_object_unref (node->folder);
-      node->folder = NULL;
-    }
+  file_model_node_idle_clear_cancel (node);
   
   children = node->children;
   node->children = NULL;
-  node->has_children = FALSE;
   node->loaded = FALSE;
   
   while (children)
     {
       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
@@ -1071,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 *
@@ -1086,23 +1391,27 @@ file_model_node_get_children (GtkFileSystemModel *model,
       const GtkFileInfo *info = file_model_node_get_info (model, node);
       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->uri,
+                                                  node->path,
                                                   model->types,
                                                   NULL);       /* NULL-GError */
 
       if (node->folder)
        {
-         GSList *child_uris, *tmp_list;
+         GSList *child_paths, *tmp_list;
          
-         if (gtk_file_folder_list_children (node->folder, &child_uris, NULL)) /* NULL-GError */
+         if (gtk_file_folder_list_children (node->folder, &child_paths, NULL)) /* NULL-GError */
            {
-             for (tmp_list = child_uris; tmp_list; tmp_list = tmp_list->next)
+             child_paths = gtk_file_paths_sort (child_paths);
+
+             for (tmp_list = child_paths; tmp_list; tmp_list = tmp_list->next)
                {
-                 FileModelNode *child_node = file_model_node_new (tmp_list->data);
-                 g_free (tmp_list->data);
+                 FileModelNode *child_node = file_model_node_new (model, tmp_list->data);
+                 gtk_file_path_free (tmp_list->data);
                  child_node->next = node->children;
                  child_node->parent = node;
                  child_node->depth = node->depth + 1;
@@ -1111,28 +1420,35 @@ file_model_node_get_children (GtkFileSystemModel *model,
                    has_children = TRUE;
                  node->children = child_node;
                }
-             g_slist_free (child_uris);
+             g_slist_free (child_paths);
            }
 
          node->children = (FileModelNode *)g_slist_reverse ((GSList *)node->children);
+
+         g_signal_connect (node->folder, "deleted",
+                           G_CALLBACK (deleted_callback), node);
+         g_signal_connect (node->folder, "files-added",
+                           G_CALLBACK (files_added_callback), node);
+         g_signal_connect (node->folder, "files-changed",
+                           G_CALLBACK (files_changed_callback), node);
+         g_signal_connect (node->folder, "files-removed",
+                           G_CALLBACK (files_removed_callback), node);
+
+         g_object_set_data (G_OBJECT (node->folder), "model-node", node);
        }
       
-      node->has_children = has_children;
-
-      if (is_folder && !node->has_children)
+      if (is_folder && !has_children)
        {
          /* The hard case ... we claimed this folder had children, but actually
-          * it didn't. We have to add a dummy child, then remove it later
+          * it didn't. We have to add a dummy child, possibly to remove later.
           */
-         FileModelNode *child_node = file_model_node_new ("***dummy***");
+         FileModelNode *child_node = file_model_node_new (model, NULL);
          child_node->is_visible = TRUE;
          child_node->parent = node;
+         child_node->is_dummy = TRUE;
 
          node->children = child_node;
-         node->has_children = TRUE;
          node->has_dummy = TRUE;
-
-         queue_dummy_idle (model, node);
        }
 
       node->loaded = TRUE;
@@ -1140,3 +1456,459 @@ file_model_node_get_children (GtkFileSystemModel *model,
 
   return node->children;
 }
+
+static void
+do_files_added (GtkFileSystemModel *model,
+               FileModelNode      *parent_node,
+               GSList             *paths)
+{
+  GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
+  FileModelNode *children;
+  FileModelNode *prev = NULL;
+  GtkTreeIter iter;
+  GtkTreePath *path;
+  GSList *sorted_paths;
+  GSList *tmp_list;
+
+  sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
+  
+  if (parent_node)
+    {
+      iter.user_data = parent_node;
+      path = gtk_tree_model_get_path (tree_model, &iter);
+      children = parent_node->children;
+    }
+  else
+    {
+      path = gtk_tree_path_new ();
+      children = model->roots;
+    }
+
+  gtk_tree_path_down (path);
+  
+  if (parent_node && parent_node->has_dummy)
+    {
+      prev = children;
+      children = children->next;
+      gtk_tree_path_next (path);
+    }
+
+  for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
+    {
+      const GtkFilePath *file_path = tmp_list->data;
+      
+      while (children &&
+            (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
+       {
+         prev = children;
+         if (children->is_visible)
+           gtk_tree_path_next (path);
+         
+         children = children->next;
+       }
+  
+      if (children &&
+         children->path && gtk_file_path_compare (children->path, file_path) == 0)
+       {
+         /* Shouldn't happen */
+       }
+      else
+       {
+         FileModelNode *new;
+         
+         new = file_model_node_new (model, file_path);
+         
+         if (children)
+           new->next = children;
+         if (prev)
+           prev->next = new;
+         else if (parent_node)
+           parent_node->children = new;
+         else
+           model->roots = new;
+
+         prev = new;
+         
+         if (parent_node)
+           {
+             new->parent = parent_node;
+             new->depth = parent_node->depth + 1;
+           }
+         
+         new->is_visible = file_model_node_is_visible (model, new);
+         
+         if (new->is_visible)
+           {
+             iter.user_data = new;
+             path = gtk_tree_model_get_path (tree_model, &iter);
+             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);
+             
+             if (parent_node && parent_node->has_dummy)
+               {
+                 FileModelNode *dummy = parent_node->children;
+                 GtkTreePath *dummy_path;
+                 
+                 parent_node->children = parent_node->children->next;
+                 parent_node->has_dummy = FALSE;
+
+                 dummy_path = gtk_tree_path_copy (path);
+                 gtk_tree_path_up (dummy_path);
+                 gtk_tree_path_down (dummy_path);
+                 
+                 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);
+           }
+       }
+    }
+
+  gtk_tree_path_free (path);
+  g_slist_free (sorted_paths);
+}
+
+static void
+do_files_changed (GtkFileSystemModel *model,
+                 FileModelNode      *parent_node,
+                 GSList             *paths)
+{
+  GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
+  FileModelNode *children;
+  FileModelNode *prev = NULL;
+  GtkTreeIter iter;
+  GtkTreePath *path;
+  GSList *sorted_paths;
+  GSList *tmp_list;
+
+  sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
+  
+  if (parent_node)
+    {
+      iter.user_data = parent_node;
+      path = gtk_tree_model_get_path (tree_model, &iter);
+      children = parent_node->children;
+    }
+  else
+    {
+      path = gtk_tree_path_new ();
+      children = model->roots;
+    }
+
+  gtk_tree_path_down (path);
+  
+  if (parent_node && parent_node->has_dummy)
+    {
+      prev = children;
+      children = children->next;
+      gtk_tree_path_next (path);
+    }
+
+  for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
+    {
+      const GtkFilePath *file_path = tmp_list->data;
+      
+      while (children &&
+            (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
+       {
+         prev = children;
+         if (children->is_visible)
+           gtk_tree_path_next (path);
+         
+         children = children->next;
+       }
+  
+      if (children &&
+         children->path && gtk_file_path_compare (children->path, file_path) == 0)
+       {
+         gtk_tree_model_row_changed (tree_model, path, &iter);
+       }
+      else
+       {
+         /* Shouldn't happen */
+       }
+    }
+
+  gtk_tree_path_free (path);
+  g_slist_free (sorted_paths);
+}
+
+static void
+do_files_removed (GtkFileSystemModel *model,
+                 FileModelNode      *parent_node,
+                 GSList             *paths)
+{
+  GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
+  FileModelNode *children;
+  FileModelNode *prev = NULL;
+  GtkTreeIter iter;
+  GtkTreePath *path;
+  GSList *sorted_paths;
+  GSList *tmp_list;
+  FileModelNode *tmp_child;
+  gint n_visible;
+
+  sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
+  
+  if (parent_node)
+    {
+      iter.user_data = parent_node;
+      path = gtk_tree_model_get_path (tree_model, &iter);
+      children = parent_node->children;
+    }
+  else
+    {
+      path = gtk_tree_path_new ();
+      children = model->roots;
+    }
+
+  /* Count the number of currently visible children, so that
+   * can catch when we need to insert a dummy node.
+   */
+  n_visible = 0;
+  for (tmp_child = children; tmp_child; tmp_child = tmp_child->next)
+    {
+      if (tmp_child->is_visible)
+       n_visible++;
+    }
+
+  gtk_tree_path_down (path);
+  
+  if (parent_node && parent_node->has_dummy)
+    {
+      prev = children;
+      children = children->next;
+      gtk_tree_path_next (path);
+    }
+
+  for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
+    {
+      const GtkFilePath *file_path = tmp_list->data;
+      
+      while (children &&
+            (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
+       {
+         prev = children;
+         if (children->is_visible)
+           gtk_tree_path_next (path);
+         
+         children = children->next;
+       }
+  
+      if (children &&
+         children->path && gtk_file_path_compare (children->path, file_path) == 0)
+       {
+         FileModelNode *next = children->next;
+
+         if (children->is_visible)
+           n_visible--;
+         
+         if (n_visible == 0)
+           {
+             FileModelNode *dummy = file_model_node_new (model, NULL);
+             dummy->is_visible = TRUE;
+             dummy->parent = parent_node;
+             dummy->is_dummy = TRUE;
+
+             parent_node->children = dummy;
+             parent_node->has_dummy = TRUE;
+
+             iter.user_data = dummy;
+             gtk_tree_model_row_inserted (tree_model, path, &iter);
+             gtk_tree_path_next (path);
+
+             prev = dummy;
+           }
+         
+         if (prev)
+           prev->next = next;
+         else if (parent_node)
+           parent_node->children = next;
+         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);
+
+         file_model_node_free (children);
+
+         children = next;
+       }
+      else
+       {
+         /* Shouldn't happen */
+       }
+    }
+
+  gtk_tree_path_free (path);
+  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)
+{
+}
+
+static void
+files_added_callback (GtkFileFolder      *folder,
+                     GSList             *paths,
+                     FileModelNode      *node)
+{
+  do_files_added (node->model, node, paths);
+}
+
+static void
+files_changed_callback (GtkFileFolder      *folder,
+                       GSList             *paths,
+                       FileModelNode      *node)
+{
+  do_files_changed (node->model, node, paths);
+}
+
+static void
+files_removed_callback (GtkFileFolder      *folder,
+                       GSList             *paths,
+                       FileModelNode      *node)
+{
+  do_files_removed (node->model, node, paths);
+}
+
+static void
+root_deleted_callback (GtkFileFolder      *folder,
+                      GtkFileSystemModel *model)
+{
+}
+
+static void
+root_files_added_callback (GtkFileFolder      *folder,
+                          GSList             *paths,
+                          GtkFileSystemModel *model)
+{
+  do_files_added (model, NULL, paths);
+}
+
+static void
+root_files_changed_callback (GtkFileFolder      *folder,
+                            GSList             *paths,
+                            GtkFileSystemModel *model)
+{
+  do_files_changed (model, NULL, paths);
+}
+
+static void
+root_files_removed_callback (GtkFileFolder      *folder,
+                            GSList             *paths,
+                            GtkFileSystemModel *model)
+{
+  do_files_removed (model, NULL, paths);
+}