]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilesystemmodel.c
Reinstate the ruler metrics strings, since they are used by glade, pointed
[~andy/gtk] / gtk / gtkfilesystemmodel.c
index 5927983ca203d8c3f990ff232b975ed94ae8ab8b..32aebbcdc257897ca90bfa82c5248a07c48c3656 100644 (file)
 #include <config.h>
 #include <string.h>
 
+#include "gtkfilechooserprivate.h"
 #include "gtkfilesystemmodel.h"
 #include "gtkfilesystem.h"
 #include "gtkintl.h"
 #include "gtkmarshalers.h"
 #include "gtktreednd.h"
 #include "gtktreemodel.h"
+#include "gtkalias.h"
 
 typedef struct _GtkFileSystemModelClass GtkFileSystemModelClass;
-typedef struct _FileModelNode           FileModelNode;
 
 #define GTK_FILE_SYSTEM_MODEL_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
 #define GTK_IS_FILE_SYSTEM_MODEL_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_MODEL))
@@ -44,60 +45,10 @@ struct _GtkFileSystemModelClass
   void (*finished_loading) (GtkFileSystemModel *model);
 };
 
-struct _GtkFileSystemModel
-{
-  GObject parent_instance;
-
-  GtkFileSystem  *file_system;
-  GtkFileInfoType types;
-  FileModelNode  *roots;
-  GtkFileFolder  *root_folder;
-  GtkFilePath    *root_path;
-
-  GtkFileSystemModelFilter filter_func;
-  gpointer filter_data;
 
-  GSList *idle_clears;
-  GSource *idle_clear_source;
-  GSource *idle_finished_loading_source;
-
-  gushort max_depth;
-  
-  guint show_hidden : 1;
-  guint show_folders : 1;
-  guint show_files : 1;
-  guint folders_only : 1;
-  guint has_editable : 1;
-};
-
-struct _FileModelNode
-{
-  GtkFilePath *path;
-  FileModelNode *next;
-
-  GtkFileInfo *info;
-  GtkFileFolder *folder;
-  
-  FileModelNode *children;
-  FileModelNode *parent;
-  GtkFileSystemModel *model;
-
-  guint ref_count;
-  guint n_referenced_children;
-
-  gushort depth;
-
-  guint has_dummy : 1;
-  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);
 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 gtk_file_system_model_dispose      (GObject                 *object);
 
 static void drag_source_iface_init (GtkTreeDragSourceIface *iface);
 
@@ -161,11 +112,6 @@ 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,
@@ -190,8 +136,6 @@ static void root_files_removed_callback (GtkFileFolder      *folder,
                                         GSList             *paths,
                                         GtkFileSystemModel *model);
 
-static GObjectClass *parent_class = NULL;
-
 /* Signal IDs */
 enum {
   FINISHED_LOADING,
@@ -202,65 +146,22 @@ static guint file_system_model_signals[LAST_SIGNAL] = { 0 };
 
 \f
 
-GType
-_gtk_file_system_model_get_type (void)
-{
-  static GType file_system_model_type = 0;
-
-  if (!file_system_model_type)
-    {
-      static const GTypeInfo file_system_model_info =
-      {
-       sizeof (GtkFileSystemModelClass),
-       NULL,           /* base_init */
-       NULL,           /* base_finalize */
-       (GClassInitFunc) gtk_file_system_model_class_init,
-       NULL,           /* class_finalize */
-       NULL,           /* class_data */
-       sizeof (GtkFileSystemModel),
-       0,              /* n_preallocs */
-       (GInstanceInitFunc) gtk_file_system_model_init,
-      };
-      
-      static const GInterfaceInfo file_system_info =
-      {
-       (GInterfaceInitFunc) gtk_file_system_model_iface_init, /* interface_init */
-       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,
-                                                     "GtkFileSystemModel",
-                                                     &file_system_model_info, 0);
-      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;
-}
+G_DEFINE_TYPE_WITH_CODE (GtkFileSystemModel, _gtk_file_system_model, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
+                                               gtk_file_system_model_iface_init)
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
+                                               drag_source_iface_init));
 
 static void
-gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
+_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;
+  gobject_class->dispose = gtk_file_system_model_dispose;
 
   file_system_model_signals[FINISHED_LOADING] =
-    g_signal_new ("finished-loading",
+    g_signal_new (I_("finished-loading"),
                  G_OBJECT_CLASS_TYPE (gobject_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkFileSystemModelClass, finished_loading),
@@ -289,7 +190,7 @@ gtk_file_system_model_iface_init (GtkTreeModelIface *iface)
 }
 
 static void
-gtk_file_system_model_init (GtkFileSystemModel *model)
+_gtk_file_system_model_init (GtkFileSystemModel *model)
 {
   model->show_files = TRUE;
   model->show_folders = TRUE;
@@ -311,9 +212,6 @@ gtk_file_system_model_finalize (GObject *object)
   if (model->file_system)
     g_object_unref (model->file_system);
 
-  if (model->idle_finished_loading_source)
-    g_source_destroy (model->idle_finished_loading_source);
-
   children = model->roots;
   while (children)
     {
@@ -322,7 +220,26 @@ gtk_file_system_model_finalize (GObject *object)
       children = next;
     }
 
-  G_OBJECT_CLASS (parent_class)->finalize (object);
+  G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->finalize (object);
+}
+
+
+static void
+gtk_file_system_model_dispose (GObject *object)
+{
+  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
+
+  if (model->pending_handles)
+    {
+      GSList *l;
+
+      for (l = model->pending_handles; l; l = l->next)
+        gtk_file_system_cancel_operation (l->data);
+      g_slist_free (model->pending_handles);
+      model->pending_handles = NULL;
+    }
+
+  G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->dispose (object);
 }
 
 static void
@@ -664,7 +581,7 @@ drag_source_drag_data_get (GtkTreeDragSource *drag_source,
   uris = g_strconcat (uri, "\r\n", NULL);
 
   gtk_selection_data_set (selection_data,
-                         gdk_atom_intern ("text/uri-list", FALSE),
+                         gdk_atom_intern_static_string ("text/uri-list"),
                          8,
                          uris,
                          strlen (uris) + 1);
@@ -683,29 +600,75 @@ root_folder_finished_loading_cb (GtkFileFolder      *folder,
   g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
 }
 
-/* Emits the "finished-loading" signal as an idle handler; see the comment in
- * _gtk_file_system_model_new()
- */
-static gboolean
-idle_finished_loading_cb (GtkFileSystemModel *model)
+static void
+got_root_folder_cb (GtkFileSystemHandle *handle,
+                   GtkFileFolder       *folder,
+                   const GError        *error,
+                   gpointer             data)
 {
-  g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
+  GSList *roots = NULL;
+  GSList *tmp_list;
+  gboolean cancelled = handle->cancelled;
+  GtkFileSystemModel *model = data;
 
-  g_source_destroy (model->idle_finished_loading_source);
-  model->idle_finished_loading_source = NULL;
+  tmp_list = g_slist_find (model->pending_handles, handle);
+  if (!tmp_list)
+    goto out;
 
-  return FALSE;
-}
+  model->pending_handles = g_slist_remove_link (model->pending_handles,
+                                               tmp_list);
 
-/* Queues an idle handler to emit the "finished-loading" signal */
-static void
-queue_finished_loading (GtkFileSystemModel *model)
-{
-  model->idle_finished_loading_source = g_idle_source_new ();
-  g_source_set_closure (model->idle_finished_loading_source,
-                       g_cclosure_new_object (G_CALLBACK (idle_finished_loading_cb),
-                                              G_OBJECT (model)));
-  g_source_attach (model->idle_finished_loading_source, NULL);
+  if (cancelled || !folder)
+    goto out;
+
+  model->root_folder = folder;
+
+  if (gtk_file_folder_is_finished_loading (model->root_folder))
+    g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
+  else
+    g_signal_connect_object (model->root_folder, "finished-loading",
+                            G_CALLBACK (root_folder_finished_loading_cb), model, 0);
+
+  gtk_file_folder_list_children (model->root_folder, &roots, NULL);
+
+  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);
+
+  roots = gtk_file_paths_sort (roots);
+  
+  for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
+    {
+      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;
+      model->roots = node;
+
+      if (node->is_visible)
+        {
+         GtkTreeIter iter;
+         GtkTreePath *path;
+
+         iter.user_data = node;
+         path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
+         gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
+         gtk_tree_path_free (path);
+       }
+    }
+  g_slist_free (roots);
+
+  model->roots = (FileModelNode *) g_slist_reverse ((GSList *)model->roots);
+
+out:
+  g_object_unref (model);
+  g_object_unref (handle);
 }
 
 /**
@@ -722,26 +685,35 @@ queue_finished_loading (GtkFileSystemModel *model)
  *         that is desired about the files. This will
  *         determine what information is returned by
  *         _gtk_file_system_model_get_info().
+ * @error: location to store error, or %NULL.
  *
  * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
  * object wraps a #GtkFileSystem interface as a #GtkTreeModel.
  * 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.
+ * Return value: the newly created #GtkFileSystemModel object, or NULL if there
+ * was an error.
  **/
 GtkFileSystemModel *
 _gtk_file_system_model_new (GtkFileSystem     *file_system,
                            const GtkFilePath *root_path,
                            gint               max_depth,
-                           GtkFileInfoType    types)
+                           GtkFileInfoType    types,
+                           GError           **error)
 {
   GtkFileSystemModel *model;
-  GSList *roots = NULL;
-  GSList *tmp_list;
+  GtkFileSystemHandle *handle;
 
   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
   g_return_val_if_fail (root_path != NULL, NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  /* First, start loading the root folder */
+
+  types |= GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN;
+
+  /* Then, actually create the model and the root nodes */
 
   model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
   model->file_system = g_object_ref (file_system);
@@ -749,64 +721,34 @@ _gtk_file_system_model_new (GtkFileSystem     *file_system,
     model->max_depth = G_MAXUSHORT;
   else
     model->max_depth = MIN (max_depth, G_MAXUSHORT);
-  model->types = types | GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN;
 
-  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 */
+  model->types = types;
+  model->root_folder = NULL;
+  model->root_path = gtk_file_path_copy (root_path);
 
-      if (model->root_folder)
-       {
-         if (gtk_file_folder_list_children (model->root_folder,
-                                            &child_paths,
-                                            NULL)) /* NULL-GError */
-           roots = child_paths;
+  model->roots = NULL;
 
-         if (gtk_file_folder_is_finished_loading (model->root_folder))
-           queue_finished_loading (model); /* done in an idle because we are being created */
-         else
-           g_signal_connect_object (model->root_folder, "finished-loading",
-                                    G_CALLBACK (root_folder_finished_loading_cb), model, 0);
-
-         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
+  handle = gtk_file_system_get_folder (file_system, root_path, types,
+                                      got_root_folder_cb,
+                                      g_object_ref (model));
+  if (!handle)
     {
-      roots = gtk_file_system_list_roots (file_system);
-      g_signal_connect_object (file_system, "roots-changed",
-                              G_CALLBACK (roots_changed_callback), model, 0);
-    }
-#endif
+      /* In this case got_root_folder_cb() will never be called, so we
+       * need to unref model twice.
+       */
+      g_object_unref (model);
+      g_object_unref (model);
 
-  roots = gtk_file_paths_sort (roots);
-  
-  for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
-    {
-      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;
-      model->roots = node;
+      g_set_error (error,
+                  GTK_FILE_CHOOSER_ERROR,
+                  GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
+                  _("Could not obtain root folder"));
+
+      return NULL;
     }
-  g_slist_free (roots);
 
-  model->roots = (FileModelNode *)g_slist_reverse ((GSList *)model->roots);
-  
+  model->pending_handles = g_slist_append (model->pending_handles, handle);
+
   return model;
 }
 
@@ -1038,58 +980,6 @@ find_child_node (GtkFileSystemModel *model,
 
   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;
-
-  child_node = find_child_node (model, parent_node, path);
-  if (child_node)
-    {
-      file_model_node_ref (child_node);
-      return child_node;
-    }
-
-  folder = gtk_file_system_get_folder (model->file_system,
-                                      path,
-                                      model->types,
-                                      NULL);   /* NULL-GError */
-  if (folder)
-    {
-      *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 NULL;
-}
 
 /**
  * _gtk_file_system_model_set_filter:
@@ -1114,6 +1004,126 @@ _gtk_file_system_model_set_filter (GtkFileSystemModel      *model,
   model_refilter_all (model);
 }
 
+
+struct RefPathData
+{
+  GtkFileSystemModel *model;
+  FileModelNode *node;
+  FileModelNode *parent_node;
+  GSList *paths;
+  GSList *cleanups;
+  GtkFileSystemModelPathFunc func;
+  gpointer user_data;
+};
+
+/* FIXME: maybe we have to wait on finished-loading? */
+static void
+ref_path_cb (GtkFileSystemHandle *handle,
+            GtkFileFolder       *folder,
+            const GError        *error,
+            gpointer             data)
+{
+  struct RefPathData *info = data;
+  gboolean cancelled = handle->cancelled;
+
+  if (!g_slist_find (info->model->pending_handles, handle))
+    goto out;
+
+  info->model->pending_handles = g_slist_remove (info->model->pending_handles, handle);
+
+  /* Note that !folder means that the child node was already
+   * found, without using get_folder.
+   */
+  if (cancelled || error)
+    goto out;
+
+  if (folder)
+    info->cleanups = g_slist_prepend (info->cleanups, folder);
+  else if (g_slist_length (info->paths) == 1
+           && gtk_file_path_compare (info->node->path, info->paths->data) == 0)
+    {
+      /* Done, now call the function */
+      if (info->node)
+        {
+          GtkTreeIter iter;
+          GtkTreePath *path;
+
+          iter.user_data = info->node;
+          path = gtk_tree_model_get_path (GTK_TREE_MODEL (info->model), &iter);
+
+          (* info->func) (info->model, path, &iter, info->user_data);
+
+          gtk_tree_path_free (path);
+        }
+
+      goto out;
+    }
+
+  info->node = find_child_node (info->model, info->parent_node, info->paths->data);
+  if (info->node)
+    file_model_node_ref (info->node);
+  else
+    {
+      goto out;
+    }
+
+  gtk_file_path_free (info->paths->data);
+  info->paths = g_slist_remove (info->paths, info->paths->data);
+
+  if (g_slist_length (info->paths) < 1)
+    {
+      /* Done, now call the function */
+      if (info->node)
+        {
+          GtkTreeIter iter;
+          GtkTreePath *path;
+
+          iter.user_data = info->node;
+          path = gtk_tree_model_get_path (GTK_TREE_MODEL (info->model), &iter);
+
+          (* info->func) (info->model, path, &iter, info->user_data);
+
+          gtk_tree_path_free (path);
+        }
+
+      goto out;
+    }
+  else
+    {
+      info->parent_node = info->node;
+
+      if (info->parent_node->loaded)
+        {
+          info->node = find_child_node (info->model, info->parent_node, info->paths->data);
+          ref_path_cb (NULL, NULL, NULL, info);
+        }
+      else
+        {
+         GtkFileSystemHandle *handle;
+
+          handle = gtk_file_system_get_folder (info->model->file_system,
+                                              info->paths->data,
+                                              info->model->types,
+                                              ref_path_cb, data);
+         info->model->pending_handles =
+           g_slist_append (info->model->pending_handles, handle);
+        }
+
+      return;
+    }
+
+out:
+  if (info->node)
+    unref_node_and_parents (info->model, info->node);
+  gtk_file_paths_free (info->paths);
+  g_slist_foreach (info->cleanups, (GFunc)g_object_unref, NULL);
+  g_slist_free (info->cleanups);
+  g_object_unref (info->model);
+  g_free (info);
+
+  g_object_unref (handle);
+}
+
 /**
  * _gtk_file_system_model_path_do:
  * @model: a #GtkFileSystemModel
@@ -1129,8 +1139,8 @@ _gtk_file_system_model_set_filter (GtkFileSystemModel      *model,
  * then unrefs all the parent nodes.
  *
  * The reason for doing this operation as a callback
- * is so that if the operation performed with the the
- * path and iter results in referencing the the node
+ * is so that if the operation performed with the
+ * path and iter results in referencing the node
  * and/or parent nodes, we don't load all the information
  * about the nodes.
  *
@@ -1140,33 +1150,90 @@ _gtk_file_system_model_set_filter (GtkFileSystemModel      *model,
  * Return value: %TRUE if the path was successfully
  *  found in @model and @func was called.
  **/
-gboolean
-_gtk_file_system_model_path_do (GtkFileSystemModel       *model,
-                              const GtkFilePath         *path,
-                              GtkFileSystemModelPathFunc func,
-                              gpointer                   user_data)
+void
+_gtk_file_system_model_path_do (GtkFileSystemModel        *model,
+                               const GtkFilePath         *path,
+                               GtkFileSystemModelPathFunc func,
+                               gpointer                   user_data)
 {
-  GSList *cleanups = NULL;
-  FileModelNode *node = find_and_ref_path (model, path, &cleanups);
+  GtkFilePath *parent_path;
+  GSList *paths = NULL;
+  FileModelNode *node;
+  struct RefPathData *info;
 
-  if (node)
-    {
-      GtkTreeIter iter;
-      GtkTreePath *path;
+  if (gtk_file_path_compare (path, model->root_path) == 0
+      || !gtk_file_system_get_parent (model->file_system, path, &parent_path, NULL))
+    return;
 
-      iter.user_data = node;
-      path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
+  paths = g_slist_prepend (paths, gtk_file_path_copy (path));
+  while (gtk_file_path_compare (parent_path, model->root_path) != 0)
+    {
+      paths = g_slist_prepend (paths, parent_path);
+      if (!gtk_file_system_get_parent (model->file_system, parent_path, &parent_path, NULL))
+        {
+         gtk_file_paths_free (paths);
+         return;
+       }
+    }
 
-      (*func) (model, path, &iter, user_data);
+  if (g_slist_length (paths) < 1)
+    return;
 
-      gtk_tree_path_free (path);
-      unref_node_and_parents (model, node);
+  /* Now we have all paths, except the root path */
+  node = find_child_node (model, NULL, paths->data);
+  if (!node)
+    {
+      gtk_file_paths_free (paths);
+      return;
     }
 
-  g_slist_foreach (cleanups, (GFunc)g_object_unref, NULL);
-  g_slist_free (cleanups);
+  file_model_node_ref (node);
 
-  return node != NULL;
+  gtk_file_path_free (paths->data);
+  paths = g_slist_remove (paths, paths->data);
+
+  if (g_slist_length (paths) < 1)
+    {
+      /* Done, now call the function */
+      if (node)
+        {
+          GtkTreeIter iter;
+          GtkTreePath *path;
+
+          iter.user_data = node;
+          path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
+
+          (* func) (model, path, &iter, user_data);
+
+          gtk_tree_path_free (path);
+          unref_node_and_parents (model, node);
+        }
+    }
+  else
+    {
+      info = g_new0 (struct RefPathData, 1);
+      info->paths = paths;
+      info->model = g_object_ref (model);
+      info->func = func;
+      info->user_data = user_data;
+      info->node = node;
+
+      if (info->node->loaded)
+        {
+         info->parent_node = info->node;
+          info->node = find_child_node (model, info->parent_node, info->paths->data);
+          ref_path_cb (NULL, NULL, NULL, info);
+        }
+      else
+        {
+         GtkFileSystemHandle *handle;
+
+          handle = gtk_file_system_get_folder (model->file_system,
+                                              paths->data, model->types,
+                                              ref_path_cb, info);
+         model->pending_handles = g_slist_append (model->pending_handles, handle);
+        }
+    }
 }
 
 /**
@@ -1277,15 +1344,8 @@ 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,
-                                                     node->path,
-                                                     model->types,
-                                                     NULL);  /* NULL-GError */
-       }
-#endif
+       g_assert_not_reached ();
     }
 
   return node->info;
@@ -1446,6 +1506,114 @@ file_model_node_child_unref (FileModelNode *parent)
     file_model_node_idle_clear (parent);
 }
 
+struct GetChildrenData
+{
+  GtkFileSystemModel *model;
+  FileModelNode *node;
+};
+
+static void
+get_children_get_folder_cb (GtkFileSystemHandle *handle,
+                           GtkFileFolder       *folder,
+                           const GError        *error,
+                           gpointer             callback_data)
+{
+  GSList *child_paths, *tmp_list;
+  gboolean has_children = FALSE;
+  gboolean cancelled = handle->cancelled;
+  struct GetChildrenData *data = callback_data;
+
+  tmp_list = g_slist_find (data->model->pending_handles, handle);
+
+  if (!tmp_list)
+    goto out;
+
+  data->model->pending_handles = g_slist_remove_link (data->model->pending_handles, tmp_list);
+
+  if (cancelled || !folder)
+    {
+      /* error, no folder, remove dummy child */
+      if (data->node->parent && data->node->parent->has_dummy)
+        {
+          data->node->parent->children = NULL;
+          data->node->parent->has_dummy = FALSE;
+       }
+
+      file_model_node_free (data->node);
+
+      goto out;
+    }
+
+  data->node->folder = folder;
+  data->node->load_pending = FALSE;
+
+  if (gtk_file_folder_list_children (folder, &child_paths, NULL)) /* NULL-GError */
+    {
+      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 (data->model, tmp_list->data);
+         gtk_file_path_free (tmp_list->data);
+         child_node->next = data->node->children;
+         child_node->parent = data->node;
+         child_node->depth = data->node->depth + 1;
+         child_node->is_visible = file_model_node_is_visible (data->model, child_node);
+
+         if (child_node->is_visible)
+           {
+             GtkTreeIter iter;
+             GtkTreePath *path;
+
+             has_children = TRUE;
+
+             iter.user_data = child_node;
+             path = gtk_tree_model_get_path (GTK_TREE_MODEL (data->model), &iter);
+             gtk_tree_model_row_inserted (GTK_TREE_MODEL (data->model), path, &iter);
+             gtk_tree_path_free (path);
+           }
+
+         data->node->children = child_node;
+       }
+      g_slist_free (child_paths);
+    }
+
+  data->node->children = (FileModelNode *)g_slist_reverse ((GSList *)data->node->children);
+
+  g_signal_connect (data->node->folder, "deleted",
+                   G_CALLBACK (deleted_callback), data->node);
+  g_signal_connect (data->node->folder, "files-added",
+                   G_CALLBACK (files_added_callback), data->node);
+  g_signal_connect (data->node->folder, "files-changed",
+                   G_CALLBACK (files_changed_callback), data->node);
+  g_signal_connect (data->node->folder, "files-removed",
+                   G_CALLBACK (files_removed_callback), data->node);
+
+  data->node->loaded = TRUE;
+
+  if (!has_children)
+    {
+      /* The hard case ... we claimed this folder had children, but actually
+       * it didn't. We have to add a dummy child, possibly to remove later.
+       */
+      FileModelNode *child_node = file_model_node_new (data->model, NULL);
+      child_node->is_visible = TRUE;
+      child_node->parent = data->node;
+      child_node->is_dummy = TRUE;
+
+      data->node->children = child_node;
+      data->node->has_dummy = TRUE;
+    }
+
+  g_object_set_data (G_OBJECT (data->node->folder), I_("model-node"), data->node);
+
+out:
+  g_object_unref (data->model);
+  g_free (data);
+
+  g_object_unref (handle);
+}
+
 static FileModelNode *
 file_model_node_get_children (GtkFileSystemModel *model,
                              FileModelNode      *node)
@@ -1453,7 +1621,7 @@ file_model_node_get_children (GtkFileSystemModel *model,
   if (node->ref_count == 0)
     return NULL;
 
-  if (!node->loaded)
+  if (!node->loaded && !node->load_pending)
     {
       const GtkFileInfo *info = file_model_node_get_info (model, node);
       gboolean has_children = FALSE;
@@ -1462,48 +1630,25 @@ file_model_node_get_children (GtkFileSystemModel *model,
       file_model_node_idle_clear_cancel (node);
 
       if (is_folder)
-       node->folder = gtk_file_system_get_folder (model->file_system,
-                                                  node->path,
-                                                  model->types,
-                                                  NULL);       /* NULL-GError */
-
-      if (node->folder)
-       {
-         GSList *child_paths, *tmp_list;
-         
-         if (gtk_file_folder_list_children (node->folder, &child_paths, NULL)) /* NULL-GError */
-           {
-             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 (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;
-                 child_node->is_visible = file_model_node_is_visible (model, child_node);
-                 if (child_node->is_visible)
-                   has_children = TRUE;
-                 node->children = child_node;
-               }
-             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);
+        {
+         struct GetChildrenData *data;
+         GtkFileSystemHandle *handle;
+
+         data = g_new (struct GetChildrenData, 1);
+         data->model = g_object_ref (model);
+         data->node = node;
+
+         handle =
+           gtk_file_system_get_folder (model->file_system,
+                                       node->path,
+                                       model->types,
+                                       get_children_get_folder_cb,
+                                       data);
+
+         model->pending_handles = g_slist_append (model->pending_handles, handle);
+         node->load_pending = TRUE;
        }
-      
+
       if (is_folder && !has_children)
        {
          /* The hard case ... we claimed this folder had children, but actually
@@ -1517,8 +1662,6 @@ file_model_node_get_children (GtkFileSystemModel *model,
          node->children = child_node;
          node->has_dummy = TRUE;
        }
-
-      node->loaded = TRUE;
     }
 
   return node->children;
@@ -1607,6 +1750,7 @@ do_files_added (GtkFileSystemModel *model,
          if (new->is_visible)
            {
              iter.user_data = new;
+             gtk_tree_path_free (path);
              path = gtk_tree_model_get_path (tree_model, &iter);
              gtk_tree_model_row_inserted (tree_model, path, &iter);
              
@@ -1649,7 +1793,6 @@ do_files_changed (GtkFileSystemModel *model,
 {
   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
   FileModelNode *children;
-  FileModelNode *prev = NULL;
   GtkTreeIter iter;
   GtkTreePath *path;
   GSList *sorted_paths;
@@ -1673,7 +1816,6 @@ do_files_changed (GtkFileSystemModel *model,
   
   if (parent_node && parent_node->has_dummy)
     {
-      prev = children;
       children = children->next;
       gtk_tree_path_next (path);
     }
@@ -1685,7 +1827,6 @@ do_files_changed (GtkFileSystemModel *model,
       while (children &&
             (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
        {
-         prev = children;
          if (children->is_visible)
            gtk_tree_path_next (path);
          
@@ -1821,105 +1962,6 @@ 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)
@@ -1979,3 +2021,6 @@ root_files_removed_callback (GtkFileFolder      *folder,
 {
   do_files_removed (model, NULL, paths);
 }
+
+#define __GTK_FILE_SYSTEM_MODEL_C__
+#include "gtkaliasdef.c"