]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemmodel.c
11ee1d2fd3f3c139589fb5dc4f9929e4cfd51b23
[~andy/gtk] / gtk / gtkfilesystemmodel.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilesystemmodel.c: GtkTreeModel wrapping a GtkFileSystem
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22 #include <string.h>
23
24 #include "gtkfilechooserprivate.h"
25 #include "gtkfilesystemmodel.h"
26 #include "gtkfilesystem.h"
27 #include "gtkintl.h"
28 #include "gtkmarshalers.h"
29 #include "gtktreednd.h"
30 #include "gtktreemodel.h"
31 #include "gtkalias.h"
32
33 typedef struct _GtkFileSystemModelClass GtkFileSystemModelClass;
34
35 #define GTK_FILE_SYSTEM_MODEL_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
36 #define GTK_IS_FILE_SYSTEM_MODEL_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_MODEL))
37 #define GTK_FILE_SYSTEM_MODEL_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
38
39 struct _GtkFileSystemModelClass
40 {
41   GObjectClass parent_class;
42
43   /* Signals */
44
45   void (*finished_loading) (GtkFileSystemModel *model);
46 };
47
48
49 static void gtk_file_system_model_iface_init   (GtkTreeModelIface       *iface);
50 static void gtk_file_system_model_finalize     (GObject                 *object);
51 static void gtk_file_system_model_dispose      (GObject                 *object);
52
53 static void drag_source_iface_init (GtkTreeDragSourceIface *iface);
54
55 static GtkTreeModelFlags gtk_file_system_model_get_flags       (GtkTreeModel *tree_model);
56 static gint              gtk_file_system_model_get_n_columns   (GtkTreeModel *tree_model);
57 static GType             gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
58                                                                 gint          index);
59 static gboolean          gtk_file_system_model_get_iter        (GtkTreeModel *tree_model,
60                                                                 GtkTreeIter  *iter,
61                                                                 GtkTreePath  *path);
62 static GtkTreePath *     gtk_file_system_model_get_path        (GtkTreeModel *tree_model,
63                                                                 GtkTreeIter  *iter);
64 static void              gtk_file_system_model_get_value       (GtkTreeModel *tree_model,
65                                                                 GtkTreeIter  *iter,
66                                                                 gint          column,
67                                                                 GValue       *value);
68 static gboolean          gtk_file_system_model_iter_next       (GtkTreeModel *tree_model,
69                                                                 GtkTreeIter  *iter);
70 static gboolean          gtk_file_system_model_iter_children   (GtkTreeModel *tree_model,
71                                                                 GtkTreeIter  *iter,
72                                                                 GtkTreeIter  *parent);
73 static gboolean          gtk_file_system_model_iter_has_child  (GtkTreeModel *tree_model,
74                                                                 GtkTreeIter  *iter);
75 static gint              gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
76                                                                 GtkTreeIter  *iter);
77 static gboolean          gtk_file_system_model_iter_nth_child  (GtkTreeModel *tree_model,
78                                                                 GtkTreeIter  *iter,
79                                                                 GtkTreeIter  *parent,
80                                                                 gint          n);
81 static gboolean          gtk_file_system_model_iter_parent     (GtkTreeModel *tree_model,
82                                                                 GtkTreeIter  *iter,
83                                                                 GtkTreeIter  *child);
84 static void              gtk_file_system_model_ref_node        (GtkTreeModel *tree_model,
85                                                                 GtkTreeIter  *iter);
86 static void              gtk_file_system_model_unref_node      (GtkTreeModel *tree_model,
87                                                                 GtkTreeIter  *iter);
88
89 static gboolean drag_source_row_draggable (GtkTreeDragSource   *drag_source,
90                                            GtkTreePath         *path);
91 static gboolean drag_source_drag_data_get (GtkTreeDragSource   *drag_source,
92                                            GtkTreePath         *path,
93                                            GtkSelectionData    *selection_data);
94
95 static FileModelNode *file_model_node_new        (GtkFileSystemModel *model,
96                                                   const GtkFilePath  *path);
97 static void           file_model_node_free       (FileModelNode      *node);
98 static void           file_model_node_ref        (FileModelNode      *node);
99 static void           file_model_node_unref      (GtkFileSystemModel *model,
100                                                   FileModelNode      *node);
101
102 static void file_model_node_idle_clear        (FileModelNode *node);
103 static void file_model_node_idle_clear_cancel (FileModelNode *node);
104 static void file_model_node_child_unref       (FileModelNode *parent);
105
106 static const GtkFileInfo *file_model_node_get_info     (GtkFileSystemModel *model,
107                                                         FileModelNode      *node);
108 static gboolean           file_model_node_is_visible   (GtkFileSystemModel *model,
109                                                         FileModelNode      *node);
110 static void               file_model_node_clear        (GtkFileSystemModel *model,
111                                                         FileModelNode      *node);
112 static FileModelNode *    file_model_node_get_children (GtkFileSystemModel *model,
113                                                         FileModelNode      *node);
114
115 static void deleted_callback       (GtkFileFolder *folder,
116                                     FileModelNode *node);
117 static void files_added_callback   (GtkFileFolder *folder,
118                                     GSList        *paths,
119                                     FileModelNode *node);
120 static void files_changed_callback (GtkFileFolder *folder,
121                                     GSList        *paths,
122                                     FileModelNode *node);
123 static void files_removed_callback (GtkFileFolder *folder,
124                                     GSList        *paths,
125                                     FileModelNode *node);
126
127 static void root_deleted_callback       (GtkFileFolder      *folder,
128                                          GtkFileSystemModel *model);
129 static void root_files_added_callback   (GtkFileFolder      *folder,
130                                          GSList             *paths,
131                                          GtkFileSystemModel *model);
132 static void root_files_changed_callback (GtkFileFolder      *folder,
133                                          GSList             *paths,
134                                          GtkFileSystemModel *model);
135 static void root_files_removed_callback (GtkFileFolder      *folder,
136                                          GSList             *paths,
137                                          GtkFileSystemModel *model);
138
139 /* Signal IDs */
140 enum {
141   FINISHED_LOADING,
142   LAST_SIGNAL
143 };
144
145 static guint file_system_model_signals[LAST_SIGNAL] = { 0 };
146
147 \f
148
149 G_DEFINE_TYPE_WITH_CODE (GtkFileSystemModel, _gtk_file_system_model, G_TYPE_OBJECT,
150                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
151                                                 gtk_file_system_model_iface_init)
152                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
153                                                 drag_source_iface_init))
154
155 static void
156 _gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
157 {
158   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
159
160   gobject_class->finalize = gtk_file_system_model_finalize;
161   gobject_class->dispose = gtk_file_system_model_dispose;
162
163   file_system_model_signals[FINISHED_LOADING] =
164     g_signal_new (I_("finished-loading"),
165                   G_OBJECT_CLASS_TYPE (gobject_class),
166                   G_SIGNAL_RUN_LAST,
167                   G_STRUCT_OFFSET (GtkFileSystemModelClass, finished_loading),
168                   NULL, NULL,
169                   _gtk_marshal_VOID__VOID,
170                   G_TYPE_NONE, 0);
171 }
172
173 static void
174 gtk_file_system_model_iface_init (GtkTreeModelIface *iface)
175 {
176   iface->get_flags =       gtk_file_system_model_get_flags;
177   iface->get_n_columns =   gtk_file_system_model_get_n_columns;
178   iface->get_column_type = gtk_file_system_model_get_column_type;
179   iface->get_iter =        gtk_file_system_model_get_iter;
180   iface->get_path =        gtk_file_system_model_get_path;
181   iface->get_value =       gtk_file_system_model_get_value;
182   iface->iter_next =       gtk_file_system_model_iter_next;
183   iface->iter_children =   gtk_file_system_model_iter_children;
184   iface->iter_has_child =  gtk_file_system_model_iter_has_child;
185   iface->iter_n_children = gtk_file_system_model_iter_n_children;
186   iface->iter_nth_child =  gtk_file_system_model_iter_nth_child;
187   iface->iter_parent =     gtk_file_system_model_iter_parent;
188   iface->ref_node =        gtk_file_system_model_ref_node;
189   iface->unref_node =      gtk_file_system_model_unref_node;
190 }
191
192 static void
193 _gtk_file_system_model_init (GtkFileSystemModel *model)
194 {
195   model->show_files = TRUE;
196   model->show_folders = TRUE;
197   model->show_hidden = FALSE;
198 }
199
200 static void
201 gtk_file_system_model_finalize (GObject *object)
202 {
203   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
204   FileModelNode *children, *next;
205
206   if (model->root_folder)
207     g_object_unref (model->root_folder);
208
209   if (model->root_path)
210     gtk_file_path_free (model->root_path);
211
212   if (model->file_system)
213     g_object_unref (model->file_system);
214
215   children = model->roots;
216   while (children)
217     {
218       next = children->next;
219       file_model_node_free (children);
220       children = next;
221     }
222
223   G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->finalize (object);
224 }
225
226
227 static void
228 gtk_file_system_model_dispose (GObject *object)
229 {
230   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
231
232   if (model->pending_handles)
233     {
234       GSList *l;
235
236       for (l = model->pending_handles; l; l = l->next)
237         gtk_file_system_cancel_operation (l->data);
238       g_slist_free (model->pending_handles);
239       model->pending_handles = NULL;
240     }
241
242   G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->dispose (object);
243 }
244
245 static void
246 drag_source_iface_init (GtkTreeDragSourceIface *iface)
247 {
248   iface->row_draggable = drag_source_row_draggable;
249   iface->drag_data_get = drag_source_drag_data_get;
250   iface->drag_data_delete = NULL;
251 }
252
253 /*
254  * ******************** GtkTreeModel methods ********************
255  */
256
257 static GtkTreeModelFlags
258 gtk_file_system_model_get_flags (GtkTreeModel *tree_model)
259 {
260   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
261   GtkTreeModelFlags flags = GTK_TREE_MODEL_ITERS_PERSIST;
262
263   if (model->max_depth == 0)
264     flags |= GTK_TREE_MODEL_LIST_ONLY;
265
266   return flags;
267 }
268
269 static gint
270 gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model)
271 {
272   return GTK_FILE_SYSTEM_MODEL_N_COLUMNS;
273 }
274
275 static GType
276 gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
277                                        gint          index)
278 {
279   switch (index)
280     {
281     case GTK_FILE_SYSTEM_MODEL_INFO:
282       return GTK_TYPE_FILE_INFO; 
283     case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
284       return G_TYPE_STRING;
285    default:
286       g_assert_not_reached ();
287       return G_TYPE_NONE;
288     }
289 }
290
291 static gboolean
292 gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
293                                 GtkTreeIter  *iter,
294                                 GtkTreePath  *path)
295 {
296   GtkTreeIter parent;
297   gint *indices;
298   gint depth, i;
299
300   indices = gtk_tree_path_get_indices (path);
301   depth = gtk_tree_path_get_depth (path);
302
303   g_return_val_if_fail (depth > 0, FALSE);
304
305   if (!gtk_tree_model_iter_nth_child (tree_model, iter, NULL, indices[0]))
306     return FALSE;
307
308   for (i = 1; i < depth; i++)
309     {
310       parent = *iter;
311       if (!gtk_tree_model_iter_nth_child (tree_model, iter, &parent, indices[i]))
312         return FALSE;
313     }
314
315   return TRUE;
316 }
317
318 static GtkTreePath *
319 gtk_file_system_model_get_path (GtkTreeModel *tree_model,
320                                 GtkTreeIter  *iter)
321 {
322   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
323   FileModelNode *node = iter->user_data;
324
325   GtkTreePath *result = gtk_tree_path_new ();
326
327   while (node)
328     {
329       FileModelNode *parent = node->parent;
330       FileModelNode *children;
331       int n = 0;
332
333       if (parent)
334         children = parent->children;
335       else
336         children = model->roots;
337
338       while (children != node)
339         {
340           if (children->is_visible)
341             n++;
342           children = children->next;
343         }
344       
345       gtk_tree_path_prepend_index (result, n);
346
347       node = parent;
348     }
349
350   return result;
351 }
352
353 static void
354 gtk_file_system_model_get_value (GtkTreeModel *tree_model,
355                                  GtkTreeIter  *iter,
356                                  gint          column,
357                                  GValue       *value)
358 {
359   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
360   FileModelNode *node = iter->user_data;
361   const GtkFileInfo *info;
362   
363   switch (column)
364     {
365     case GTK_FILE_SYSTEM_MODEL_INFO:
366       if (model->has_editable && node == model->roots)
367         info = NULL;
368       else
369         info = file_model_node_get_info (model, node);
370
371       g_value_init (value, GTK_TYPE_FILE_INFO);
372       g_value_set_boxed (value, info);
373       break;
374     case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
375       {
376         g_value_init (value, G_TYPE_STRING);
377
378         if (model->has_editable && node == model->roots)
379           g_value_set_static_string (value, "");
380         else
381           {
382             const GtkFileInfo *info = file_model_node_get_info (model, node);
383
384             g_value_set_string (value, gtk_file_info_get_display_name (info));
385           }
386       }
387       break;
388     default:
389       g_assert_not_reached ();
390     }
391 }
392
393 static gboolean
394 gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
395                                  GtkTreeIter  *iter)
396 {
397   FileModelNode *node = iter->user_data;
398
399   node = node->next;
400   while (node && !node->is_visible)
401     node = node->next;
402   
403   iter->user_data = node;
404
405   return node != NULL;
406 }
407
408 static gboolean
409 gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
410                                      GtkTreeIter  *iter,
411                                      GtkTreeIter  *parent)
412 {
413   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
414   FileModelNode *children;
415
416   if (parent)
417     {
418       FileModelNode *parent_node = parent->user_data;
419       children = file_model_node_get_children (model, parent_node);
420     }
421   else
422     {
423       children = model->roots;
424     }
425
426   while (children && !children->is_visible)
427     children = children->next;
428
429   iter->user_data = children;
430
431   return children != NULL;
432 }
433
434 static gboolean
435 gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
436                                       GtkTreeIter  *iter)
437 {
438   FileModelNode *node = iter->user_data;
439   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
440
441   if (node->depth == model->max_depth)
442     return FALSE;
443   else
444     {
445       const GtkFileInfo *info = file_model_node_get_info (model, node);
446       return gtk_file_info_get_is_folder (info);
447     }
448 }
449
450 static gint
451 gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
452                                        GtkTreeIter  *iter)
453 {
454   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
455   FileModelNode *children;
456   gint n = 0;
457
458   if (iter)
459     {
460       FileModelNode *node = iter->user_data;
461       children = file_model_node_get_children (model, node);
462     }
463   else
464     {
465       children = model->roots;
466     }
467
468   while (children)
469     {
470       if (children->is_visible)
471         n++;
472       children = children->next;
473     }
474
475   return n;
476 }
477
478 static gboolean
479 gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
480                                       GtkTreeIter  *iter,
481                                       GtkTreeIter  *parent,
482                                       gint          n)
483 {
484   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
485   FileModelNode *children;
486
487   if (parent)
488     {
489       FileModelNode *parent_node = parent->user_data;
490       children = file_model_node_get_children (model, parent_node);
491     }
492   else
493     {
494       children = model->roots;
495     }
496
497   while (children && !children->is_visible)
498     children = children->next;
499
500   while (n && children)
501     {
502       n--;
503       children = children->next;
504       while (children && !children->is_visible)
505         children = children->next;
506     }
507
508   iter->user_data = children;
509
510   return children != NULL;
511 }
512
513 static gboolean
514 gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
515                                    GtkTreeIter  *iter,
516                                    GtkTreeIter  *child)
517 {
518   FileModelNode *node = child->user_data;
519   
520   node = node->parent;
521   iter->user_data = node;
522
523   return node != NULL;
524 }
525
526 static void
527 gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
528                                 GtkTreeIter  *iter)
529 {
530   file_model_node_ref (iter->user_data);
531 }
532
533 static void
534 gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
535                                   GtkTreeIter  *iter)
536 {
537   file_model_node_unref (GTK_FILE_SYSTEM_MODEL (tree_model),
538                          iter->user_data);
539 }
540
541 static gboolean
542 drag_source_row_draggable (GtkTreeDragSource *drag_source,
543                            GtkTreePath       *path)
544 {
545   GtkFileSystemModel *model;
546   GtkTreeIter iter;
547   FileModelNode *node;
548
549   model = GTK_FILE_SYSTEM_MODEL (drag_source);
550
551   if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
552     return FALSE;
553
554   if (!model->has_editable)
555     return TRUE;
556
557   node = iter.user_data;
558   return (node != model->roots);
559 }
560
561 static gboolean
562 drag_source_drag_data_get (GtkTreeDragSource *drag_source,
563                            GtkTreePath       *path,
564                            GtkSelectionData  *selection_data)
565 {
566   GtkFileSystemModel *model;
567   GtkTreeIter iter;
568   const GtkFilePath *file_path;
569   char *uris[2]; 
570
571   model = GTK_FILE_SYSTEM_MODEL (drag_source);
572
573   if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
574     return FALSE;
575
576   file_path = _gtk_file_system_model_get_path (model, &iter);
577   g_assert (file_path != NULL);
578
579   uris[0] = gtk_file_system_path_to_uri (model->file_system, file_path);
580   uris[1] = NULL;
581
582   gtk_selection_data_set_uris (selection_data, uris);
583
584   g_free (uris[0]);
585
586   return TRUE;
587 }
588
589 /* Callback used when the root folder finished loading */
590 static void
591 root_folder_finished_loading_cb (GtkFileFolder      *folder,
592                                  GtkFileSystemModel *model)
593 {
594   g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
595 }
596
597 static void
598 got_root_folder_cb (GtkFileSystemHandle *handle,
599                     GtkFileFolder       *folder,
600                     const GError        *error,
601                     gpointer             data)
602 {
603   GSList *roots = NULL;
604   GSList *tmp_list;
605   gboolean cancelled = handle->cancelled;
606   GtkFileSystemModel *model = data;
607
608   tmp_list = g_slist_find (model->pending_handles, handle);
609   if (!tmp_list)
610     goto out;
611
612   model->pending_handles = g_slist_delete_link (model->pending_handles,
613                                                 tmp_list);
614
615   if (cancelled || !folder)
616     goto out;
617
618   model->root_folder = folder;
619
620   if (gtk_file_folder_is_finished_loading (model->root_folder))
621     g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
622   else
623     g_signal_connect_object (model->root_folder, "finished-loading",
624                              G_CALLBACK (root_folder_finished_loading_cb), model, 0);
625
626   gtk_file_folder_list_children (model->root_folder, &roots, NULL);
627
628   g_signal_connect_object (model->root_folder, "deleted",
629                            G_CALLBACK (root_deleted_callback), model, 0);
630   g_signal_connect_object (model->root_folder, "files-added",
631                            G_CALLBACK (root_files_added_callback), model, 0);
632   g_signal_connect_object (model->root_folder, "files-changed",
633                            G_CALLBACK (root_files_changed_callback), model, 0);
634   g_signal_connect_object (model->root_folder, "files-removed",
635                            G_CALLBACK (root_files_removed_callback), model, 0);
636
637   roots = gtk_file_paths_sort (roots);
638   
639   for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
640     {
641       FileModelNode *node = file_model_node_new (model, tmp_list->data);
642       gtk_file_path_free (tmp_list->data);
643       node->is_visible = file_model_node_is_visible (model, node);
644       node->next = model->roots;
645       node->depth = 0;
646       model->roots = node;
647
648       if (node->is_visible)
649         {
650           GtkTreeIter iter;
651           GtkTreePath *path;
652
653           iter.user_data = node;
654           path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
655           gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
656           gtk_tree_path_free (path);
657         }
658     }
659   g_slist_free (roots);
660
661 out:
662   g_object_unref (model);
663   g_object_unref (handle);
664 }
665
666 /**
667  * _gtk_file_system_model_new:
668  * @file_system: an object implementing #GtkFileSystem
669  * @root_path: the path of root of the file system to display
670  * @max_depth: the maximum depth from the children of @root_path
671  *             or the roots of the file system to display in
672  *             the file selector). A depth of 0 displays
673  *             only the immediate children of @root_path,
674  *             or the roots of the filesystem. -1 for no
675  *             maximum depth.
676  * @types: a bitmask indicating the types of information
677  *         that is desired about the files. This will
678  *         determine what information is returned by
679  *         _gtk_file_system_model_get_info().
680  * @error: location to store error, or %NULL.
681  *
682  * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
683  * object wraps a #GtkFileSystem interface as a #GtkTreeModel.
684  * Using the @root_path and @max_depth parameters, the tree model
685  * can be restricted to a subportion of the entire file system.
686  * 
687  * Return value: the newly created #GtkFileSystemModel object, or NULL if there
688  * was an error.
689  **/
690 GtkFileSystemModel *
691 _gtk_file_system_model_new (GtkFileSystem     *file_system,
692                             const GtkFilePath *root_path,
693                             gint               max_depth,
694                             GtkFileInfoType    types,
695                             GError           **error)
696 {
697   GtkFileSystemModel *model;
698   GtkFileSystemHandle *handle;
699
700   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
701   g_return_val_if_fail (root_path != NULL, NULL);
702   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
703
704   /* First, start loading the root folder */
705
706   types |= GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN;
707
708   /* Then, actually create the model and the root nodes */
709
710   model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
711   model->file_system = g_object_ref (file_system);
712   if (max_depth < 0)
713     model->max_depth = G_MAXUSHORT;
714   else
715     model->max_depth = MIN (max_depth, G_MAXUSHORT);
716
717   model->types = types;
718   model->root_folder = NULL;
719   model->root_path = gtk_file_path_copy (root_path);
720
721   model->roots = NULL;
722
723   handle = gtk_file_system_get_folder (file_system, root_path, types,
724                                        got_root_folder_cb,
725                                        g_object_ref (model));
726   if (!handle)
727     {
728       /* In this case got_root_folder_cb() will never be called, so we
729        * need to unref model twice.
730        */
731       g_object_unref (model);
732       g_object_unref (model);
733
734       g_set_error (error,
735                    GTK_FILE_CHOOSER_ERROR,
736                    GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
737                    _("Could not obtain root folder"));
738
739       return NULL;
740     }
741
742   model->pending_handles = g_slist_append (model->pending_handles, handle);
743
744   return model;
745 }
746
747 static void
748 model_refilter_recurse (GtkFileSystemModel *model,
749                         FileModelNode      *parent,
750                         GtkTreePath        *path)
751 {
752   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
753   int i = 0;
754   FileModelNode *nodes;
755   gboolean has_children = FALSE;
756
757   if (parent && !parent->loaded)
758     return;
759
760   if (parent)
761     nodes = parent->children;
762   else
763     nodes = model->roots;
764
765   while (nodes)
766     {
767       FileModelNode *next = nodes->next;
768       gboolean is_visible;
769       
770       gtk_tree_path_append_index (path, i);
771
772       is_visible = file_model_node_is_visible (model, nodes);
773       
774       if (!is_visible && nodes->is_visible)
775         {
776           file_model_node_clear (model, nodes);
777           gtk_tree_model_row_deleted (tree_model, path);
778
779           nodes->is_visible = FALSE;
780         }
781       else if (is_visible && !nodes->is_visible)
782         {
783           GtkTreeIter iter;
784
785           iter.user_data = nodes;
786           nodes->is_visible = TRUE;
787           gtk_tree_model_row_inserted (tree_model, path, &iter);
788         }
789       else
790         model_refilter_recurse (model, nodes, path);
791
792       if (is_visible)
793         {
794           has_children = TRUE;
795           i++;
796         }
797       
798       gtk_tree_path_up (path);
799       
800       nodes = next;
801     }
802
803   if (parent && !has_children)
804     {
805       /* Fixme - need to insert dummy node here */
806     }
807 }
808
809 static void
810 model_refilter_all (GtkFileSystemModel *model)
811 {
812   GtkTreePath *path;
813
814   path = gtk_tree_path_new ();
815   model_refilter_recurse (model, NULL, path);
816   gtk_tree_path_free (path);
817 }
818
819 /**
820  * _gtk_file_system_model_set_show_hidden:
821  * @model: a #GtkFileSystemModel
822  * @show_hidden: whether hidden files should be displayed
823  * 
824  * Sets whether hidden files should be included in the #GtkTreeModel
825  * for display.
826  **/
827 void
828 _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
829                                         gboolean            show_hidden)
830 {
831   show_hidden = show_hidden != FALSE;
832
833   if (show_hidden != model->show_hidden)
834     {
835       model->show_hidden = show_hidden;
836       model_refilter_all (model);
837     }
838 }
839
840 /**
841  * _gtk_file_system_model_set_show_folders:
842  * @model: a #GtkFileSystemModel
843  * @show_folders: whether folders should be displayed
844  * 
845  * Sets whether folders should be included in the #GtkTreeModel for
846  * display.
847  **/
848 void
849 _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
850                                          gboolean            show_folders)
851 {
852   show_folders = show_folders != FALSE;
853
854   if (show_folders != model->show_folders)
855     {
856       model->show_folders = show_folders;
857       model_refilter_all (model);
858     }
859 }
860
861 /**
862  * _gtk_file_system_model_set_show_files:
863  * @model: a #GtkFileSystemModel
864  * @show_files: whether files (as opposed to folders) should
865  *              be displayed.
866  * 
867  * Sets whether files (as opposed to folders) should be included
868  * in the #GtkTreeModel for display.
869  **/
870 void
871 _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
872                                        gboolean            show_files)
873 {
874   show_files = show_files != FALSE;
875
876   if (show_files != model->show_files)
877     {
878       model->show_files = show_files;
879       model_refilter_all (model);
880     }
881 }
882
883 /**
884  * _gtk_file_system_model_get_info:
885  * @model: a #GtkFileSystemModel
886  * @iter: a #GtkTreeIter pointing to a row of @model
887  * 
888  * Gets the #GtkFileInfo structure for a particular row
889  * of @model. The information included in this structure
890  * is determined by the @types parameter to
891  * _gtk_file_system_model_new().
892  * 
893  * Return value: a #GtkFileInfo structure. This structure
894  *   is owned by @model and must not be modified or freed.
895  *   If you want to save the information for later use,
896  *   you must make a copy, since the structure may be
897  *   freed on later changes to the file system.  If you have
898  *   called _gtk_file_system_model_add_editable() and the @iter
899  *   corresponds to the row that this function returned, the
900  *   return value will be NULL.
901  **/
902 const GtkFileInfo *
903 _gtk_file_system_model_get_info (GtkFileSystemModel *model,
904                                  GtkTreeIter        *iter)
905 {
906   FileModelNode *node;
907
908   node = iter->user_data;
909   if (model->has_editable && node == model->roots)
910     return NULL;
911   else
912     return file_model_node_get_info (model, node);
913 }
914
915 /**
916  * _gtk_file_system_model_get_path:
917  * @model: a #GtkFileSystemModel
918  * @iter: a #GtkTreeIter pointing to a row of @model
919  * 
920  * Gets the path for a particular row in @model. 
921  *
922  * Return value: the path. This string is owned by @model and
923  *   or freed. If you want to save the path for later use,
924  *   you must make a copy, since the string may be freed
925  *   on later changes to the file system.
926  **/
927 const GtkFilePath *
928 _gtk_file_system_model_get_path (GtkFileSystemModel *model,
929                                  GtkTreeIter        *iter)
930 {
931   FileModelNode *node = iter->user_data;
932
933   if (model->has_editable && node == model->roots)
934     return NULL;
935
936   if (node->is_dummy)
937     return node->parent->path;
938   else
939     return node->path;
940 }
941
942 static void
943 unref_node_and_parents (GtkFileSystemModel *model,
944                         FileModelNode      *node)
945 {
946   file_model_node_unref (model, node);
947   if (node->parent)
948     file_model_node_unref (model, node->parent);
949 }
950
951 static FileModelNode *
952 find_child_node (GtkFileSystemModel *model,
953                  FileModelNode      *parent_node,
954                  const GtkFilePath  *path)
955 {
956   FileModelNode *children;
957   
958   if (parent_node)
959     children = file_model_node_get_children (model, parent_node);
960   else
961     children = model->roots;
962
963   while (children)
964     {
965       if (children->is_visible &&
966           children->path &&
967           gtk_file_path_compare (children->path, path) == 0)
968         return children;
969
970       children = children->next;
971     }
972
973   return NULL;
974 }
975
976 /**
977  * _gtk_file_system_model_set_filter:
978  * @mode: a #GtkFileSystemModel
979  * @filter: function to be called for each file
980  * @user_data: data to pass to @filter
981  * 
982  * Sets a callback called for each file/directory to see whether
983  * it should be included in model. If this function was made
984  * public, we'd want to include a GDestroyNotify as well.
985  **/
986 void
987 _gtk_file_system_model_set_filter (GtkFileSystemModel      *model,
988                                    GtkFileSystemModelFilter filter,
989                                    gpointer                 user_data)
990 {
991   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
992   
993   model->filter_func = filter;
994   model->filter_data = user_data;
995
996   model_refilter_all (model);
997 }
998
999
1000 struct RefPathData
1001 {
1002   GtkFileSystemModel *model;
1003   FileModelNode *node;
1004   FileModelNode *parent_node;
1005   GSList *paths;
1006   GSList *cleanups;
1007   GtkFileSystemModelPathFunc func;
1008   gpointer user_data;
1009 };
1010
1011 /* FIXME: maybe we have to wait on finished-loading? */
1012 static void
1013 ref_path_cb (GtkFileSystemHandle *handle,
1014              GtkFileFolder       *folder,
1015              const GError        *error,
1016              gpointer             data)
1017 {
1018   struct RefPathData *info = data;
1019   gboolean cancelled = handle->cancelled;
1020
1021   if (!g_slist_find (info->model->pending_handles, handle))
1022     goto out;
1023
1024   info->model->pending_handles = g_slist_remove (info->model->pending_handles, handle);
1025
1026   /* Note that !folder means that the child node was already
1027    * found, without using get_folder.
1028    */
1029   if (cancelled || error)
1030     goto out;
1031
1032   if (folder)
1033     info->cleanups = g_slist_prepend (info->cleanups, folder);
1034   else if (g_slist_length (info->paths) == 1
1035            && gtk_file_path_compare (info->node->path, info->paths->data) == 0)
1036     {
1037       /* Done, now call the function */
1038       if (info->node)
1039         {
1040           GtkTreeIter iter;
1041           GtkTreePath *path;
1042
1043           iter.user_data = info->node;
1044           path = gtk_tree_model_get_path (GTK_TREE_MODEL (info->model), &iter);
1045
1046           (* info->func) (info->model, path, &iter, info->user_data);
1047
1048           gtk_tree_path_free (path);
1049         }
1050
1051       goto out;
1052     }
1053
1054   info->node = find_child_node (info->model, info->parent_node, info->paths->data);
1055   if (info->node)
1056     file_model_node_ref (info->node);
1057   else
1058     {
1059       goto out;
1060     }
1061
1062   gtk_file_path_free (info->paths->data);
1063   info->paths = g_slist_remove (info->paths, info->paths->data);
1064
1065   if (g_slist_length (info->paths) < 1)
1066     {
1067       /* Done, now call the function */
1068       if (info->node)
1069         {
1070           GtkTreeIter iter;
1071           GtkTreePath *path;
1072
1073           iter.user_data = info->node;
1074           path = gtk_tree_model_get_path (GTK_TREE_MODEL (info->model), &iter);
1075
1076           (* info->func) (info->model, path, &iter, info->user_data);
1077
1078           gtk_tree_path_free (path);
1079         }
1080
1081       goto out;
1082     }
1083   else
1084     {
1085       info->parent_node = info->node;
1086
1087       if (info->parent_node->loaded)
1088         {
1089           info->node = find_child_node (info->model, info->parent_node, info->paths->data);
1090           ref_path_cb (NULL, NULL, NULL, info);
1091         }
1092       else
1093         {
1094           GtkFileSystemHandle *handle;
1095
1096           handle = gtk_file_system_get_folder (info->model->file_system,
1097                                                info->paths->data,
1098                                                info->model->types,
1099                                                ref_path_cb, data);
1100           info->model->pending_handles =
1101             g_slist_append (info->model->pending_handles, handle);
1102         }
1103
1104       return;
1105     }
1106
1107 out:
1108   if (info->node)
1109     unref_node_and_parents (info->model, info->node);
1110   gtk_file_paths_free (info->paths);
1111   g_slist_foreach (info->cleanups, (GFunc)g_object_unref, NULL);
1112   g_slist_free (info->cleanups);
1113   g_object_unref (info->model);
1114   g_free (info);
1115
1116   g_object_unref (handle);
1117 }
1118
1119 /**
1120  * _gtk_file_system_model_path_do:
1121  * @model: a #GtkFileSystemModel
1122  * @path: a path pointing to a file in the filesystem
1123  *       for @model.
1124  * @func: Function to call with the path and iter corresponding
1125  *        to @path.
1126  * @user_data: data to pass to @func
1127  * 
1128  * Locates @path within @model, referencing
1129  * (gtk_tree_model_ref_node()) all parent nodes,
1130  * calls @func passing in the path and iter for @path,
1131  * then unrefs all the parent nodes.
1132  *
1133  * The reason for doing this operation as a callback
1134  * is so that if the operation performed with the
1135  * path and iter results in referencing the node
1136  * and/or parent nodes, we don't load all the information
1137  * about the nodes.
1138  *
1139  * This function is particularly useful for expanding
1140  * a #GtkTreeView to a particular point in the file system.
1141  */
1142 void
1143 _gtk_file_system_model_path_do (GtkFileSystemModel        *model,
1144                                 const GtkFilePath         *path,
1145                                 GtkFileSystemModelPathFunc func,
1146                                 gpointer                   user_data)
1147 {
1148   GtkFilePath *parent_path;
1149   GSList *paths = NULL;
1150   FileModelNode *node;
1151   struct RefPathData *info;
1152
1153   if (gtk_file_path_compare (path, model->root_path) == 0 ||
1154       !gtk_file_system_get_parent (model->file_system, path, &parent_path, NULL) ||
1155       parent_path == NULL)
1156     return;
1157
1158   paths = g_slist_prepend (paths, gtk_file_path_copy (path));
1159   while (gtk_file_path_compare (parent_path, model->root_path) != 0)
1160     {
1161       paths = g_slist_prepend (paths, parent_path);
1162       if (!gtk_file_system_get_parent (model->file_system, parent_path, &parent_path, NULL) || 
1163           parent_path == NULL)
1164         {
1165           gtk_file_paths_free (paths);
1166           return;
1167         }
1168     }
1169
1170   if (g_slist_length (paths) < 1)
1171     return;
1172
1173   /* Now we have all paths, except the root path */
1174   node = find_child_node (model, NULL, paths->data);
1175   if (!node)
1176     {
1177       gtk_file_paths_free (paths);
1178       return;
1179     }
1180
1181   file_model_node_ref (node);
1182
1183   gtk_file_path_free (paths->data);
1184   paths = g_slist_remove (paths, paths->data);
1185
1186   if (g_slist_length (paths) < 1)
1187     {
1188       /* Done, now call the function */
1189       if (node)
1190         {
1191           GtkTreeIter iter;
1192           GtkTreePath *path;
1193
1194           iter.user_data = node;
1195           path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1196
1197           (* func) (model, path, &iter, user_data);
1198
1199           gtk_tree_path_free (path);
1200           unref_node_and_parents (model, node);
1201         }
1202     }
1203   else
1204     {
1205       info = g_new0 (struct RefPathData, 1);
1206       info->paths = paths;
1207       info->model = g_object_ref (model);
1208       info->func = func;
1209       info->user_data = user_data;
1210       info->node = node;
1211
1212       if (info->node->loaded)
1213         {
1214           info->parent_node = info->node;
1215           info->node = find_child_node (model, info->parent_node, info->paths->data);
1216           ref_path_cb (NULL, NULL, NULL, info);
1217         }
1218       else
1219         {
1220           GtkFileSystemHandle *handle;
1221
1222           handle = gtk_file_system_get_folder (model->file_system,
1223                                                paths->data, model->types,
1224                                                ref_path_cb, info);
1225           model->pending_handles = g_slist_append (model->pending_handles, handle);
1226         }
1227     }
1228 }
1229
1230 /**
1231  * _gtk_file_system_model_add_editable:
1232  * @model: a #GtkFileSystemModel
1233  * @iter: Location to return the iter corresponding to the editable row
1234  * 
1235  * Adds an "empty" row at the beginning of the model.  This does not refer to
1236  * any file, but is a temporary placeholder for a file name that the user will
1237  * type when a corresponding cell is made editable.  When your code is done
1238  * using this temporary row, call _gtk_file_system_model_remove_editable().
1239  **/
1240 void
1241 _gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *iter)
1242 {
1243   FileModelNode *node;
1244   GtkTreePath *path;
1245
1246   g_return_if_fail (!model->has_editable);
1247
1248   model->has_editable = TRUE;
1249
1250   node = file_model_node_new (model, NULL);
1251   node->is_visible = TRUE;
1252
1253   node->next = model->roots;
1254   model->roots = node;
1255
1256   path = gtk_tree_path_new ();
1257   gtk_tree_path_append_index (path, 0);
1258   iter->user_data = node;
1259
1260   gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, iter);
1261
1262   gtk_tree_path_free (path);
1263 }
1264
1265 /**
1266  * _gtk_file_system_model_remove_editable:
1267  * @model: a #GtkFileSystemModel
1268  * 
1269  * Removes the "empty" row at the beginning of the model that was
1270  * created with _gtk_file_system_model_add_editable().  You should call
1271  * this function when your code is finished editing this temporary row.
1272  **/
1273 void
1274 _gtk_file_system_model_remove_editable (GtkFileSystemModel *model)
1275 {
1276   GtkTreePath *path;
1277   FileModelNode *node;
1278
1279   g_return_if_fail (model->has_editable);
1280
1281   model->has_editable = FALSE;
1282
1283   node = model->roots;
1284   model->roots = model->roots->next;
1285   file_model_node_free (node);
1286
1287   path = gtk_tree_path_new ();
1288   gtk_tree_path_append_index (path, 0);
1289
1290   gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
1291
1292   gtk_tree_path_free (path);
1293 }
1294
1295 static FileModelNode *
1296 file_model_node_new (GtkFileSystemModel *model,
1297                      const GtkFilePath  *path)
1298 {
1299   FileModelNode *node = g_new0 (FileModelNode, 1);
1300
1301   node->model = model;
1302   node->path = path ? gtk_file_path_copy (path) : NULL;
1303
1304   return node;
1305 }
1306
1307 static void
1308 file_model_node_free (FileModelNode *node)
1309 {
1310   file_model_node_clear (node->model, node);
1311   
1312   if (node->path)
1313     gtk_file_path_free (node->path);
1314
1315   if (node->info)
1316     gtk_file_info_free (node->info);
1317
1318   g_free (node);
1319 }
1320
1321 static const GtkFileInfo *
1322 file_model_node_get_info (GtkFileSystemModel *model,
1323                           FileModelNode      *node)
1324 {
1325   if (!node->info)
1326     {
1327       if (node->is_dummy)
1328         {
1329           node->info = gtk_file_info_new ();
1330           gtk_file_info_set_display_name (node->info, _("(Empty)"));
1331         }
1332       else if (node->parent || model->root_folder)
1333         {
1334           node->info = gtk_file_folder_get_info (node->parent ? node->parent->folder : model->root_folder,
1335                                                  node->path,
1336                                                  NULL); /* NULL-GError */
1337         }
1338       else
1339         g_assert_not_reached ();
1340     }
1341
1342   return node->info;
1343 }
1344
1345 static gboolean
1346 file_model_node_is_visible (GtkFileSystemModel *model,
1347                             FileModelNode      *node)
1348 {
1349   if (model->show_folders != model->show_files ||
1350       !model->show_hidden ||
1351       model->filter_func)
1352     {
1353       const GtkFileInfo *info = file_model_node_get_info (model, node);
1354
1355       if (!info)
1356         {
1357           /* File probably disappeared underneath us or resides in a
1358              directory where we have only partial access rights.  */
1359           return FALSE;
1360         }
1361
1362       if (model->show_folders != model->show_files &&
1363           model->show_folders != gtk_file_info_get_is_folder (info))
1364         return FALSE;
1365
1366       if (!model->show_hidden && gtk_file_info_get_is_hidden (info))
1367         return FALSE;
1368
1369       if (model->filter_func &&
1370           !model->filter_func (model, node->path, info, model->filter_data))
1371         return FALSE;
1372     }
1373
1374   return TRUE;
1375 }
1376
1377 static void
1378 file_model_node_clear (GtkFileSystemModel *model,
1379                        FileModelNode      *node)
1380 {
1381   FileModelNode *children;
1382   
1383   file_model_node_idle_clear_cancel (node);
1384   
1385   children = node->children;
1386   node->children = NULL;
1387   node->loaded = FALSE;
1388   
1389   while (children)
1390     {
1391       FileModelNode *next = children->next;
1392       
1393       file_model_node_free (children);
1394       
1395       children = next;
1396     }
1397
1398   if (node->folder)
1399     {
1400       /* Unreffing node->folder may cause roots_changed,
1401        * so we need to be careful about ordering.
1402        */
1403       GtkFileFolder *folder = node->folder;
1404       node->folder = NULL;
1405
1406       g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (deleted_callback), node);
1407       g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_added_callback), node);
1408       g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_changed_callback), node);
1409       g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_removed_callback), node);
1410       
1411       g_object_unref (folder);
1412     }
1413 }
1414
1415 static void
1416 file_model_node_ref (FileModelNode *node)
1417 {
1418   node->ref_count++;
1419   if (node->ref_count == 1 && node->parent)
1420     node->parent->n_referenced_children++;
1421 }
1422
1423 static gboolean
1424 idle_clear_callback (GtkFileSystemModel *model)
1425 {
1426   while (model->idle_clears)
1427     {
1428       FileModelNode *node = model->idle_clears->data;
1429       model->idle_clears = g_slist_delete_link (model->idle_clears, model->idle_clears);
1430
1431       node->idle_clear = FALSE;
1432       file_model_node_clear (node->model, node);
1433     }
1434
1435   return FALSE;
1436 }
1437  
1438 static void
1439 file_model_node_idle_clear (FileModelNode *node)
1440 {
1441   if (!node->idle_clear)
1442     {
1443       GtkFileSystemModel *model = node->model;
1444
1445       node->idle_clear = TRUE;
1446       if (!model->idle_clears)
1447         {
1448           model->idle_clear_source = g_idle_source_new ();
1449           g_source_set_priority (model->idle_clear_source, G_PRIORITY_HIGH);
1450           g_source_set_closure (model->idle_clear_source,
1451                                 g_cclosure_new_object (G_CALLBACK (idle_clear_callback),
1452                                                        G_OBJECT (model)));
1453           g_source_attach (model->idle_clear_source, NULL);
1454         }
1455
1456       model->idle_clears = g_slist_prepend (model->idle_clears, node);
1457       node->idle_clear = TRUE;
1458     }
1459 }
1460
1461 static void
1462 file_model_node_idle_clear_cancel (FileModelNode *node)
1463 {
1464   if (node->idle_clear)
1465     {
1466       GtkFileSystemModel *model = node->model;
1467
1468       model->idle_clears = g_slist_remove (model->idle_clears, node);
1469       if (!model->idle_clears)
1470         {
1471           g_source_destroy (model->idle_clear_source);
1472           model->idle_clear_source = NULL;
1473         }
1474       
1475       node->idle_clear = FALSE;
1476     }
1477 }
1478
1479 static void
1480 file_model_node_unref (GtkFileSystemModel *model,
1481                        FileModelNode       *node)
1482 {
1483   node->ref_count--;
1484   if (node->ref_count == 0)
1485     {
1486       file_model_node_clear (model, node);
1487       if (node->parent)
1488         file_model_node_child_unref (node->parent);
1489     }
1490 }
1491
1492 static void
1493 file_model_node_child_unref (FileModelNode *parent)
1494 {
1495   parent->n_referenced_children--;
1496   if (parent->n_referenced_children == 0)
1497     file_model_node_idle_clear (parent);
1498 }
1499
1500 struct GetChildrenData
1501 {
1502   GtkFileSystemModel *model;
1503   FileModelNode *node;
1504 };
1505
1506 static void
1507 get_children_get_folder_cb (GtkFileSystemHandle *handle,
1508                             GtkFileFolder       *folder,
1509                             const GError        *error,
1510                             gpointer             callback_data)
1511 {
1512   GSList *child_paths, *tmp_list;
1513   gboolean has_children = FALSE;
1514   gboolean cancelled = handle->cancelled;
1515   struct GetChildrenData *data = callback_data;
1516
1517   tmp_list = g_slist_find (data->model->pending_handles, handle);
1518
1519   if (!tmp_list)
1520     goto out;
1521
1522   data->model->pending_handles = g_slist_delete_link (data->model->pending_handles, tmp_list);
1523
1524   if (cancelled || !folder)
1525     {
1526       /* error, no folder, remove dummy child */
1527       if (data->node->parent && data->node->parent->has_dummy)
1528         {
1529           data->node->parent->children = NULL;
1530           data->node->parent->has_dummy = FALSE;
1531         }
1532
1533       file_model_node_free (data->node);
1534
1535       goto out;
1536     }
1537
1538   data->node->folder = folder;
1539   data->node->load_pending = FALSE;
1540
1541   if (gtk_file_folder_list_children (folder, &child_paths, NULL)) /* NULL-GError */
1542     {
1543       child_paths = gtk_file_paths_sort (child_paths);
1544
1545       for (tmp_list = child_paths; tmp_list; tmp_list = tmp_list->next)
1546         {
1547           FileModelNode *child_node = file_model_node_new (data->model, tmp_list->data);
1548           gtk_file_path_free (tmp_list->data);
1549           child_node->next = data->node->children;
1550           child_node->parent = data->node;
1551           child_node->depth = data->node->depth + 1;
1552           child_node->is_visible = file_model_node_is_visible (data->model, child_node);
1553
1554           if (child_node->is_visible)
1555             {
1556               GtkTreeIter iter;
1557               GtkTreePath *path;
1558
1559               has_children = TRUE;
1560
1561               iter.user_data = child_node;
1562               path = gtk_tree_model_get_path (GTK_TREE_MODEL (data->model), &iter);
1563               gtk_tree_model_row_inserted (GTK_TREE_MODEL (data->model), path, &iter);
1564               gtk_tree_path_free (path);
1565             }
1566
1567           data->node->children = child_node;
1568         }
1569       g_slist_free (child_paths);
1570     }
1571
1572   g_signal_connect (data->node->folder, "deleted",
1573                     G_CALLBACK (deleted_callback), data->node);
1574   g_signal_connect (data->node->folder, "files-added",
1575                     G_CALLBACK (files_added_callback), data->node);
1576   g_signal_connect (data->node->folder, "files-changed",
1577                     G_CALLBACK (files_changed_callback), data->node);
1578   g_signal_connect (data->node->folder, "files-removed",
1579                     G_CALLBACK (files_removed_callback), data->node);
1580
1581   data->node->loaded = TRUE;
1582
1583   if (!has_children)
1584     {
1585       /* The hard case ... we claimed this folder had children, but actually
1586        * it didn't. We have to add a dummy child, possibly to remove later.
1587        */
1588       FileModelNode *child_node = file_model_node_new (data->model, NULL);
1589       child_node->is_visible = TRUE;
1590       child_node->parent = data->node;
1591       child_node->is_dummy = TRUE;
1592
1593       data->node->children = child_node;
1594       data->node->has_dummy = TRUE;
1595     }
1596
1597   g_object_set_data (G_OBJECT (data->node->folder), I_("model-node"), data->node);
1598
1599 out:
1600   g_object_unref (data->model);
1601   g_free (data);
1602
1603   g_object_unref (handle);
1604 }
1605
1606 static FileModelNode *
1607 file_model_node_get_children (GtkFileSystemModel *model,
1608                               FileModelNode      *node)
1609 {
1610   if (node->ref_count == 0)
1611     return NULL;
1612
1613   if (!node->loaded && !node->load_pending)
1614     {
1615       const GtkFileInfo *info = file_model_node_get_info (model, node);
1616       gboolean has_children = FALSE;
1617       gboolean is_folder = node->depth < model->max_depth && gtk_file_info_get_is_folder (info);
1618
1619       file_model_node_idle_clear_cancel (node);
1620
1621       if (is_folder)
1622         {
1623           struct GetChildrenData *data;
1624           GtkFileSystemHandle *handle;
1625
1626           data = g_new (struct GetChildrenData, 1);
1627           data->model = g_object_ref (model);
1628           data->node = node;
1629
1630           handle =
1631             gtk_file_system_get_folder (model->file_system,
1632                                         node->path,
1633                                         model->types,
1634                                         get_children_get_folder_cb,
1635                                         data);
1636
1637           model->pending_handles = g_slist_append (model->pending_handles, handle);
1638           node->load_pending = TRUE;
1639         }
1640
1641       if (is_folder && !has_children)
1642         {
1643           /* The hard case ... we claimed this folder had children, but actually
1644            * it didn't. We have to add a dummy child, possibly to remove later.
1645            */
1646           FileModelNode *child_node = file_model_node_new (model, NULL);
1647           child_node->is_visible = TRUE;
1648           child_node->parent = node;
1649           child_node->is_dummy = TRUE;
1650
1651           node->children = child_node;
1652           node->has_dummy = TRUE;
1653         }
1654     }
1655
1656   return node->children;
1657 }
1658
1659 static void
1660 do_files_added (GtkFileSystemModel *model,
1661                 FileModelNode      *parent_node,
1662                 GSList             *paths)
1663 {
1664   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1665   FileModelNode *children;
1666   FileModelNode *prev = NULL;
1667   GtkTreeIter iter;
1668   GtkTreePath *path;
1669   GSList *sorted_paths;
1670   GSList *tmp_list;
1671
1672   sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
1673   
1674   if (parent_node)
1675     {
1676       iter.user_data = parent_node;
1677       path = gtk_tree_model_get_path (tree_model, &iter);
1678       children = parent_node->children;
1679     }
1680   else
1681     {
1682       path = gtk_tree_path_new ();
1683       children = model->roots;
1684     }
1685
1686   gtk_tree_path_down (path);
1687   
1688   if (parent_node && parent_node->has_dummy)
1689     {
1690       prev = children;
1691       children = children->next;
1692       gtk_tree_path_next (path);
1693     }
1694
1695   for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
1696     {
1697       const GtkFilePath *file_path = tmp_list->data;
1698       
1699       while (children &&
1700              (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
1701         {
1702           prev = children;
1703           if (children->is_visible)
1704             gtk_tree_path_next (path);
1705           
1706           children = children->next;
1707         }
1708   
1709       if (children &&
1710           children->path && gtk_file_path_compare (children->path, file_path) == 0)
1711         {
1712           /* Shouldn't happen */
1713         }
1714       else
1715         {
1716           FileModelNode *new;
1717           
1718           new = file_model_node_new (model, file_path);
1719           
1720           if (children)
1721             new->next = children;
1722           if (prev)
1723             prev->next = new;
1724           else if (parent_node)
1725             parent_node->children = new;
1726           else
1727             model->roots = new;
1728
1729           prev = new;
1730           
1731           if (parent_node)
1732             {
1733               new->parent = parent_node;
1734               new->depth = parent_node->depth + 1;
1735             }
1736           
1737           new->is_visible = file_model_node_is_visible (model, new);
1738           
1739           if (new->is_visible)
1740             {
1741               iter.user_data = new;
1742               gtk_tree_path_free (path);
1743               path = gtk_tree_model_get_path (tree_model, &iter);
1744               gtk_tree_model_row_inserted (tree_model, path, &iter);
1745               
1746               if (gtk_file_system_model_iter_has_child (tree_model, &iter))
1747                 gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
1748               
1749               if (parent_node && parent_node->has_dummy)
1750                 {
1751                   FileModelNode *dummy = parent_node->children;
1752                   GtkTreePath *dummy_path;
1753                   
1754                   parent_node->children = parent_node->children->next;
1755                   parent_node->has_dummy = FALSE;
1756
1757                   dummy_path = gtk_tree_path_copy (path);
1758                   gtk_tree_path_up (dummy_path);
1759                   gtk_tree_path_down (dummy_path);
1760                   
1761                   gtk_tree_model_row_deleted (tree_model, dummy_path);
1762                   gtk_tree_path_free (dummy_path);
1763
1764                   if (dummy->ref_count)
1765                     file_model_node_child_unref (parent_node);
1766                   file_model_node_free (dummy);
1767                 }
1768               
1769               gtk_tree_path_next (path);
1770             }
1771         }
1772     }
1773
1774   gtk_tree_path_free (path);
1775   g_slist_free (sorted_paths);
1776 }
1777
1778 static void
1779 do_files_changed (GtkFileSystemModel *model,
1780                   FileModelNode      *parent_node,
1781                   GSList             *paths)
1782 {
1783   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1784   FileModelNode *children;
1785   GtkTreeIter iter;
1786   GtkTreePath *path;
1787   GSList *sorted_paths;
1788   GSList *tmp_list;
1789
1790   sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
1791   
1792   if (parent_node)
1793     {
1794       iter.user_data = parent_node;
1795       path = gtk_tree_model_get_path (tree_model, &iter);
1796       children = parent_node->children;
1797     }
1798   else
1799     {
1800       path = gtk_tree_path_new ();
1801       children = model->roots;
1802     }
1803
1804   gtk_tree_path_down (path);
1805   
1806   if (parent_node && parent_node->has_dummy)
1807     {
1808       children = children->next;
1809       gtk_tree_path_next (path);
1810     }
1811
1812   for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
1813     {
1814       const GtkFilePath *file_path = tmp_list->data;
1815       
1816       while (children &&
1817              (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
1818         {
1819           if (children->is_visible)
1820             gtk_tree_path_next (path);
1821           
1822           children = children->next;
1823         }
1824   
1825       if (children &&
1826           children->path && gtk_file_path_compare (children->path, file_path) == 0)
1827         {
1828           gtk_tree_model_row_changed (tree_model, path, &iter);
1829         }
1830       else
1831         {
1832           /* Shouldn't happen */
1833         }
1834     }
1835
1836   gtk_tree_path_free (path);
1837   g_slist_free (sorted_paths);
1838 }
1839
1840 static void
1841 do_files_removed (GtkFileSystemModel *model,
1842                   FileModelNode      *parent_node,
1843                   GSList             *paths)
1844 {
1845   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1846   FileModelNode *children;
1847   FileModelNode *prev = NULL;
1848   GtkTreeIter iter;
1849   GtkTreePath *path;
1850   GSList *sorted_paths;
1851   GSList *tmp_list;
1852   FileModelNode *tmp_child;
1853   gint n_visible;
1854
1855   sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
1856   
1857   if (parent_node)
1858     {
1859       iter.user_data = parent_node;
1860       path = gtk_tree_model_get_path (tree_model, &iter);
1861       children = parent_node->children;
1862     }
1863   else
1864     {
1865       path = gtk_tree_path_new ();
1866       children = model->roots;
1867     }
1868
1869   /* Count the number of currently visible children, so that
1870    * can catch when we need to insert a dummy node.
1871    */
1872   n_visible = 0;
1873   for (tmp_child = children; tmp_child; tmp_child = tmp_child->next)
1874     {
1875       if (tmp_child->is_visible)
1876         n_visible++;
1877     }
1878
1879   gtk_tree_path_down (path);
1880   
1881   if (parent_node && parent_node->has_dummy)
1882     {
1883       prev = children;
1884       children = children->next;
1885       gtk_tree_path_next (path);
1886     }
1887
1888   for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
1889     {
1890       const GtkFilePath *file_path = tmp_list->data;
1891       
1892       while (children &&
1893              (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
1894         {
1895           prev = children;
1896           if (children->is_visible)
1897             gtk_tree_path_next (path);
1898           
1899           children = children->next;
1900         }
1901   
1902       if (children &&
1903           children->path && gtk_file_path_compare (children->path, file_path) == 0)
1904         {
1905           FileModelNode *next = children->next;
1906
1907           if (children->is_visible)
1908             n_visible--;
1909           
1910           if (parent_node && n_visible == 0)
1911             {
1912               FileModelNode *dummy = file_model_node_new (model, NULL);
1913               dummy->is_visible = TRUE;
1914               dummy->parent = parent_node;
1915               dummy->is_dummy = TRUE;
1916
1917               parent_node->children = dummy;
1918               parent_node->has_dummy = TRUE;
1919
1920               iter.user_data = dummy;
1921               gtk_tree_model_row_inserted (tree_model, path, &iter);
1922               gtk_tree_path_next (path);
1923
1924               prev = dummy;
1925             }
1926           
1927           if (prev)
1928             prev->next = next;
1929           else if (parent_node)
1930             parent_node->children = next;
1931           else
1932             model->roots = next;
1933
1934           if (parent_node && children->ref_count)
1935             file_model_node_child_unref (parent_node);
1936               
1937           if (children->is_visible)
1938             gtk_tree_model_row_deleted (tree_model, path);
1939
1940           file_model_node_free (children);
1941
1942           children = next;
1943         }
1944       else
1945         {
1946           /* Shouldn't happen */
1947         }
1948     }
1949
1950   gtk_tree_path_free (path);
1951   g_slist_free (sorted_paths);
1952 }
1953
1954 static void
1955 deleted_callback (GtkFileFolder      *folder,
1956                   FileModelNode      *node)
1957 {
1958 }
1959
1960 static void
1961 files_added_callback (GtkFileFolder      *folder,
1962                       GSList             *paths,
1963                       FileModelNode      *node)
1964 {
1965   do_files_added (node->model, node, paths);
1966 }
1967
1968 static void
1969 files_changed_callback (GtkFileFolder      *folder,
1970                         GSList             *paths,
1971                         FileModelNode      *node)
1972 {
1973   do_files_changed (node->model, node, paths);
1974 }
1975
1976 static void
1977 files_removed_callback (GtkFileFolder      *folder,
1978                         GSList             *paths,
1979                         FileModelNode      *node)
1980 {
1981   do_files_removed (node->model, node, paths);
1982 }
1983
1984 static void
1985 root_deleted_callback (GtkFileFolder      *folder,
1986                        GtkFileSystemModel *model)
1987 {
1988 }
1989
1990 static void
1991 root_files_added_callback (GtkFileFolder      *folder,
1992                            GSList             *paths,
1993                            GtkFileSystemModel *model)
1994 {
1995   do_files_added (model, NULL, paths);
1996 }
1997
1998 static void
1999 root_files_changed_callback (GtkFileFolder      *folder,
2000                              GSList             *paths,
2001                              GtkFileSystemModel *model)
2002 {
2003   do_files_changed (model, NULL, paths);
2004 }
2005
2006 static void
2007 root_files_removed_callback (GtkFileFolder      *folder,
2008                              GSList             *paths,
2009                              GtkFileSystemModel *model)
2010 {
2011   do_files_removed (model, NULL, paths);
2012 }