]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemmodel.c
754d0cc36bc8e4f7e45d8bedc2795b4795088562
[~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_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 *uri;
570   char *uris;
571
572   model = GTK_FILE_SYSTEM_MODEL (drag_source);
573
574   if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
575     return FALSE;
576
577   file_path = _gtk_file_system_model_get_path (model, &iter);
578   g_assert (file_path != NULL);
579
580   uri = gtk_file_system_path_to_uri (model->file_system, file_path);
581   uris = g_strconcat (uri, "\r\n", NULL);
582
583   gtk_selection_data_set (selection_data,
584                           gdk_atom_intern_static_string ("text/uri-list"),
585                           8,
586                           uris,
587                           strlen (uris) + 1);
588
589   g_free (uri);
590   g_free (uris);
591
592   return TRUE;
593 }
594
595 /* Callback used when the root folder finished loading */
596 static void
597 root_folder_finished_loading_cb (GtkFileFolder      *folder,
598                                  GtkFileSystemModel *model)
599 {
600   g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
601 }
602
603 static void
604 got_root_folder_cb (GtkFileSystemHandle *handle,
605                     GtkFileFolder       *folder,
606                     const GError        *error,
607                     gpointer             data)
608 {
609   GSList *roots = NULL;
610   GSList *tmp_list;
611   gboolean cancelled = handle->cancelled;
612   GtkFileSystemModel *model = data;
613
614   tmp_list = g_slist_find (model->pending_handles, handle);
615   if (!tmp_list)
616     goto out;
617
618   model->pending_handles = g_slist_remove_link (model->pending_handles,
619                                                 tmp_list);
620
621   if (cancelled || !folder)
622     goto out;
623
624   model->root_folder = folder;
625
626   if (gtk_file_folder_is_finished_loading (model->root_folder))
627     g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
628   else
629     g_signal_connect_object (model->root_folder, "finished-loading",
630                              G_CALLBACK (root_folder_finished_loading_cb), model, 0);
631
632   gtk_file_folder_list_children (model->root_folder, &roots, NULL);
633
634   g_signal_connect_object (model->root_folder, "deleted",
635                            G_CALLBACK (root_deleted_callback), model, 0);
636   g_signal_connect_object (model->root_folder, "files-added",
637                            G_CALLBACK (root_files_added_callback), model, 0);
638   g_signal_connect_object (model->root_folder, "files-changed",
639                            G_CALLBACK (root_files_changed_callback), model, 0);
640   g_signal_connect_object (model->root_folder, "files-removed",
641                            G_CALLBACK (root_files_removed_callback), model, 0);
642
643   roots = gtk_file_paths_sort (roots);
644   
645   for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
646     {
647       FileModelNode *node = file_model_node_new (model, tmp_list->data);
648       gtk_file_path_free (tmp_list->data);
649       node->is_visible = file_model_node_is_visible (model, node);
650       node->next = model->roots;
651       node->depth = 0;
652       model->roots = node;
653
654       if (node->is_visible)
655         {
656           GtkTreeIter iter;
657           GtkTreePath *path;
658
659           iter.user_data = node;
660           path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
661           gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
662           gtk_tree_path_free (path);
663         }
664     }
665   g_slist_free (roots);
666
667   model->roots = (FileModelNode *) g_slist_reverse ((GSList *)model->roots);
668
669 out:
670   g_object_unref (model);
671   g_object_unref (handle);
672 }
673
674 /**
675  * _gtk_file_system_model_new:
676  * @file_system: an object implementing #GtkFileSystem
677  * @root_path: the path of root of the file system to display
678  * @max_depth: the maximum depth from the children of @root_path
679  *             or the roots of the file system to display in
680  *             the file selector). A depth of 0 displays
681  *             only the immediate children of @root_path,
682  *             or the roots of the filesystem. -1 for no
683  *             maximum depth.
684  * @types: a bitmask indicating the types of information
685  *         that is desired about the files. This will
686  *         determine what information is returned by
687  *         _gtk_file_system_model_get_info().
688  * @error: location to store error, or %NULL.
689  *
690  * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
691  * object wraps a #GtkFileSystem interface as a #GtkTreeModel.
692  * Using the @root_path and @max_depth parameters, the tree model
693  * can be restricted to a subportion of the entire file system.
694  * 
695  * Return value: the newly created #GtkFileSystemModel object, or NULL if there
696  * was an error.
697  **/
698 GtkFileSystemModel *
699 _gtk_file_system_model_new (GtkFileSystem     *file_system,
700                             const GtkFilePath *root_path,
701                             gint               max_depth,
702                             GtkFileInfoType    types,
703                             GError           **error)
704 {
705   GtkFileSystemModel *model;
706   GtkFileSystemHandle *handle;
707
708   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
709   g_return_val_if_fail (root_path != NULL, NULL);
710   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
711
712   /* First, start loading the root folder */
713
714   types |= GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN;
715
716   /* Then, actually create the model and the root nodes */
717
718   model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
719   model->file_system = g_object_ref (file_system);
720   if (max_depth < 0)
721     model->max_depth = G_MAXUSHORT;
722   else
723     model->max_depth = MIN (max_depth, G_MAXUSHORT);
724
725   model->types = types;
726   model->root_folder = NULL;
727   model->root_path = gtk_file_path_copy (root_path);
728
729   model->roots = NULL;
730
731   handle = gtk_file_system_get_folder (file_system, root_path, types,
732                                        got_root_folder_cb,
733                                        g_object_ref (model));
734   if (!handle)
735     {
736       /* In this case got_root_folder_cb() will never be called, so we
737        * need to unref model twice.
738        */
739       g_object_unref (model);
740       g_object_unref (model);
741
742       g_set_error (error,
743                    GTK_FILE_CHOOSER_ERROR,
744                    GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
745                    _("Could not obtain root folder"));
746
747       return NULL;
748     }
749
750   model->pending_handles = g_slist_append (model->pending_handles, handle);
751
752   return model;
753 }
754
755 static void
756 model_refilter_recurse (GtkFileSystemModel *model,
757                         FileModelNode      *parent,
758                         GtkTreePath        *path)
759 {
760   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
761   int i = 0;
762   FileModelNode *nodes;
763   gboolean has_children = FALSE;
764
765   if (parent && !parent->loaded)
766     return;
767
768   if (parent)
769     nodes = parent->children;
770   else
771     nodes = model->roots;
772
773   while (nodes)
774     {
775       FileModelNode *next = nodes->next;
776       gboolean is_visible;
777       
778       gtk_tree_path_append_index (path, i);
779
780       is_visible = file_model_node_is_visible (model, nodes);
781       
782       if (!is_visible && nodes->is_visible)
783         {
784           file_model_node_clear (model, nodes);
785           gtk_tree_model_row_deleted (tree_model, path);
786
787           nodes->is_visible = FALSE;
788         }
789       else if (is_visible && !nodes->is_visible)
790         {
791           GtkTreeIter iter;
792
793           iter.user_data = nodes;
794           nodes->is_visible = TRUE;
795           gtk_tree_model_row_inserted (tree_model, path, &iter);
796         }
797       else
798         model_refilter_recurse (model, nodes, path);
799
800       if (is_visible)
801         {
802           has_children = TRUE;
803           i++;
804         }
805       
806       gtk_tree_path_up (path);
807       
808       nodes = next;
809     }
810
811   if (parent && !has_children)
812     {
813       /* Fixme - need to insert dummy node here */
814     }
815 }
816
817 static void
818 model_refilter_all (GtkFileSystemModel *model)
819 {
820   GtkTreePath *path;
821
822   path = gtk_tree_path_new ();
823   model_refilter_recurse (model, NULL, path);
824   gtk_tree_path_free (path);
825 }
826
827 /**
828  * _gtk_file_system_model_set_show_hidden:
829  * @model: a #GtkFileSystemModel
830  * @show_hidden: whether hidden files should be displayed
831  * 
832  * Sets whether hidden files should be included in the #GtkTreeModel
833  * for display.
834  **/
835 void
836 _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
837                                         gboolean            show_hidden)
838 {
839   show_hidden = show_hidden != FALSE;
840
841   if (show_hidden != model->show_hidden)
842     {
843       model->show_hidden = show_hidden;
844       model_refilter_all (model);
845     }
846 }
847
848 /**
849  * _gtk_file_system_model_set_show_folders:
850  * @model: a #GtkFileSystemModel
851  * @show_folders: whether folders should be displayed
852  * 
853  * Sets whether folders should be included in the #GtkTreeModel for
854  * display.
855  **/
856 void
857 _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
858                                          gboolean            show_folders)
859 {
860   show_folders = show_folders != FALSE;
861
862   if (show_folders != model->show_folders)
863     {
864       model->show_folders = show_folders;
865       model_refilter_all (model);
866     }
867 }
868
869 /**
870  * _gtk_file_system_model_set_show_files:
871  * @model: a #GtkFileSystemModel
872  * @show_files: whether files (as opposed to folders) should
873  *              be displayed.
874  * 
875  * Sets whether files (as opposed to folders) should be included
876  * in the #GtkTreeModel for display.
877  **/
878 void
879 _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
880                                        gboolean            show_files)
881 {
882   show_files = show_files != FALSE;
883
884   if (show_files != model->show_files)
885     {
886       model->show_files = show_files;
887       model_refilter_all (model);
888     }
889 }
890
891 /**
892  * _gtk_file_system_model_get_info:
893  * @model: a #GtkFileSystemModel
894  * @iter: a #GtkTreeIter pointing to a row of @model
895  * 
896  * Gets the #GtkFileInfo structure for a particular row
897  * of @model. The information included in this structure
898  * is determined by the @types parameter to
899  * _gtk_file_system_model_new().
900  * 
901  * Return value: a #GtkFileInfo structure. This structure
902  *   is owned by @model and must not be modified or freed.
903  *   If you want to save the information for later use,
904  *   you must make a copy, since the structure may be
905  *   freed on later changes to the file system.  If you have
906  *   called _gtk_file_system_model_add_editable() and the @iter
907  *   corresponds to the row that this function returned, the
908  *   return value will be NULL.
909  **/
910 const GtkFileInfo *
911 _gtk_file_system_model_get_info (GtkFileSystemModel *model,
912                                  GtkTreeIter        *iter)
913 {
914   FileModelNode *node;
915
916   node = iter->user_data;
917   if (model->has_editable && node == model->roots)
918     return NULL;
919   else
920     return file_model_node_get_info (model, node);
921 }
922
923 /**
924  * _gtk_file_system_model_get_path:
925  * @model: a #GtkFileSystemModel
926  * @iter: a #GtkTreeIter pointing to a row of @model
927  * 
928  * Gets the path for a particular row in @model. 
929  *
930  * Return value: the path. This string is owned by @model and
931  *   or freed. If you want to save the path for later use,
932  *   you must make a copy, since the string may be freed
933  *   on later changes to the file system.
934  **/
935 const GtkFilePath *
936 _gtk_file_system_model_get_path (GtkFileSystemModel *model,
937                                  GtkTreeIter        *iter)
938 {
939   FileModelNode *node = iter->user_data;
940
941   if (model->has_editable && node == model->roots)
942     return NULL;
943
944   if (node->is_dummy)
945     return node->parent->path;
946   else
947     return node->path;
948 }
949
950 static void
951 unref_node_and_parents (GtkFileSystemModel *model,
952                         FileModelNode      *node)
953 {
954   file_model_node_unref (model, node);
955   if (node->parent)
956     file_model_node_unref (model, node->parent);
957 }
958
959 static FileModelNode *
960 find_child_node (GtkFileSystemModel *model,
961                  FileModelNode      *parent_node,
962                  const GtkFilePath  *path)
963 {
964   FileModelNode *children;
965   
966   if (parent_node)
967     children = file_model_node_get_children (model, parent_node);
968   else
969     children = model->roots;
970
971   while (children)
972     {
973       if (children->is_visible &&
974           children->path &&
975           gtk_file_path_compare (children->path, path) == 0)
976         return children;
977
978       children = children->next;
979     }
980
981   return NULL;
982 }
983
984 /**
985  * _gtk_file_system_model_set_filter:
986  * @mode: a #GtkFileSystemModel
987  * @filter: function to be called for each file
988  * @user_data: data to pass to @filter
989  * 
990  * Sets a callback called for each file/directory to see whether
991  * it should be included in model. If this function was made
992  * public, we'd want to include a GDestroyNotify as well.
993  **/
994 void
995 _gtk_file_system_model_set_filter (GtkFileSystemModel      *model,
996                                    GtkFileSystemModelFilter filter,
997                                    gpointer                 user_data)
998 {
999   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1000   
1001   model->filter_func = filter;
1002   model->filter_data = user_data;
1003
1004   model_refilter_all (model);
1005 }
1006
1007
1008 struct RefPathData
1009 {
1010   GtkFileSystemModel *model;
1011   FileModelNode *node;
1012   FileModelNode *parent_node;
1013   GSList *paths;
1014   GSList *cleanups;
1015   GtkFileSystemModelPathFunc func;
1016   gpointer user_data;
1017 };
1018
1019 /* FIXME: maybe we have to wait on finished-loading? */
1020 static void
1021 ref_path_cb (GtkFileSystemHandle *handle,
1022              GtkFileFolder       *folder,
1023              const GError        *error,
1024              gpointer             data)
1025 {
1026   struct RefPathData *info = data;
1027   gboolean cancelled = handle->cancelled;
1028
1029   if (!g_slist_find (info->model->pending_handles, handle))
1030     goto out;
1031
1032   info->model->pending_handles = g_slist_remove (info->model->pending_handles, handle);
1033
1034   /* Note that !folder means that the child node was already
1035    * found, without using get_folder.
1036    */
1037   if (cancelled || error)
1038     goto out;
1039
1040   if (folder)
1041     info->cleanups = g_slist_prepend (info->cleanups, folder);
1042   else if (g_slist_length (info->paths) == 1
1043            && gtk_file_path_compare (info->node->path, info->paths->data) == 0)
1044     {
1045       /* Done, now call the function */
1046       if (info->node)
1047         {
1048           GtkTreeIter iter;
1049           GtkTreePath *path;
1050
1051           iter.user_data = info->node;
1052           path = gtk_tree_model_get_path (GTK_TREE_MODEL (info->model), &iter);
1053
1054           (* info->func) (info->model, path, &iter, info->user_data);
1055
1056           gtk_tree_path_free (path);
1057         }
1058
1059       goto out;
1060     }
1061
1062   info->node = find_child_node (info->model, info->parent_node, info->paths->data);
1063   if (info->node)
1064     file_model_node_ref (info->node);
1065   else
1066     {
1067       goto out;
1068     }
1069
1070   gtk_file_path_free (info->paths->data);
1071   info->paths = g_slist_remove (info->paths, info->paths->data);
1072
1073   if (g_slist_length (info->paths) < 1)
1074     {
1075       /* Done, now call the function */
1076       if (info->node)
1077         {
1078           GtkTreeIter iter;
1079           GtkTreePath *path;
1080
1081           iter.user_data = info->node;
1082           path = gtk_tree_model_get_path (GTK_TREE_MODEL (info->model), &iter);
1083
1084           (* info->func) (info->model, path, &iter, info->user_data);
1085
1086           gtk_tree_path_free (path);
1087         }
1088
1089       goto out;
1090     }
1091   else
1092     {
1093       info->parent_node = info->node;
1094
1095       if (info->parent_node->loaded)
1096         {
1097           info->node = find_child_node (info->model, info->parent_node, info->paths->data);
1098           ref_path_cb (NULL, NULL, NULL, info);
1099         }
1100       else
1101         {
1102           GtkFileSystemHandle *handle;
1103
1104           handle = gtk_file_system_get_folder (info->model->file_system,
1105                                                info->paths->data,
1106                                                info->model->types,
1107                                                ref_path_cb, data);
1108           info->model->pending_handles =
1109             g_slist_append (info->model->pending_handles, handle);
1110         }
1111
1112       return;
1113     }
1114
1115 out:
1116   if (info->node)
1117     unref_node_and_parents (info->model, info->node);
1118   gtk_file_paths_free (info->paths);
1119   g_slist_foreach (info->cleanups, (GFunc)g_object_unref, NULL);
1120   g_slist_free (info->cleanups);
1121   g_object_unref (info->model);
1122   g_free (info);
1123
1124   g_object_unref (handle);
1125 }
1126
1127 /**
1128  * _gtk_file_system_model_path_do:
1129  * @model: a #GtkFileSystemModel
1130  * @path: a path pointing to a file in the filesystem
1131  *       for @model.
1132  * @func: Function to call with the path and iter corresponding
1133  *        to @path.
1134  * @user_data: data to pass to @func
1135  * 
1136  * Locates @path within @model, referencing
1137  * (gtk_tree_model_ref_node ()) all parent nodes,
1138  * calls @func passing in the path and iter for @path,
1139  * then unrefs all the parent nodes.
1140  *
1141  * The reason for doing this operation as a callback
1142  * is so that if the operation performed with the
1143  * path and iter results in referencing the node
1144  * and/or parent nodes, we don't load all the information
1145  * about the nodes.
1146  *
1147  * This function is particularly useful for expanding
1148  * a #GtkTreeView to a particular point in the file system.
1149  * 
1150  * Return value: %TRUE if the path was successfully
1151  *  found in @model and @func was called.
1152  **/
1153 void
1154 _gtk_file_system_model_path_do (GtkFileSystemModel        *model,
1155                                 const GtkFilePath         *path,
1156                                 GtkFileSystemModelPathFunc func,
1157                                 gpointer                   user_data)
1158 {
1159   GtkFilePath *parent_path;
1160   GSList *paths = NULL;
1161   FileModelNode *node;
1162   struct RefPathData *info;
1163
1164   if (gtk_file_path_compare (path, model->root_path) == 0
1165       || !gtk_file_system_get_parent (model->file_system, path, &parent_path, NULL))
1166     return;
1167
1168   paths = g_slist_prepend (paths, gtk_file_path_copy (path));
1169   while (gtk_file_path_compare (parent_path, model->root_path) != 0)
1170     {
1171       paths = g_slist_prepend (paths, parent_path);
1172       if (!gtk_file_system_get_parent (model->file_system, parent_path, &parent_path, NULL))
1173         {
1174           gtk_file_paths_free (paths);
1175           return;
1176         }
1177     }
1178
1179   if (g_slist_length (paths) < 1)
1180     return;
1181
1182   /* Now we have all paths, except the root path */
1183   node = find_child_node (model, NULL, paths->data);
1184   if (!node)
1185     {
1186       gtk_file_paths_free (paths);
1187       return;
1188     }
1189
1190   file_model_node_ref (node);
1191
1192   gtk_file_path_free (paths->data);
1193   paths = g_slist_remove (paths, paths->data);
1194
1195   if (g_slist_length (paths) < 1)
1196     {
1197       /* Done, now call the function */
1198       if (node)
1199         {
1200           GtkTreeIter iter;
1201           GtkTreePath *path;
1202
1203           iter.user_data = node;
1204           path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1205
1206           (* func) (model, path, &iter, user_data);
1207
1208           gtk_tree_path_free (path);
1209           unref_node_and_parents (model, node);
1210         }
1211     }
1212   else
1213     {
1214       info = g_new0 (struct RefPathData, 1);
1215       info->paths = paths;
1216       info->model = g_object_ref (model);
1217       info->func = func;
1218       info->user_data = user_data;
1219       info->node = node;
1220
1221       if (info->node->loaded)
1222         {
1223           info->parent_node = info->node;
1224           info->node = find_child_node (model, info->parent_node, info->paths->data);
1225           ref_path_cb (NULL, NULL, NULL, info);
1226         }
1227       else
1228         {
1229           GtkFileSystemHandle *handle;
1230
1231           handle = gtk_file_system_get_folder (model->file_system,
1232                                                paths->data, model->types,
1233                                                ref_path_cb, info);
1234           model->pending_handles = g_slist_append (model->pending_handles, handle);
1235         }
1236     }
1237 }
1238
1239 /**
1240  * _gtk_file_system_model_add_editable:
1241  * @model: a #GtkFileSystemModel
1242  * @iter: Location to return the iter corresponding to the editable row
1243  * 
1244  * Adds an "empty" row at the beginning of the model.  This does not refer to
1245  * any file, but is a temporary placeholder for a file name that the user will
1246  * type when a corresponding cell is made editable.  When your code is done
1247  * using this temporary row, call _gtk_file_system_model_remove_editable().
1248  **/
1249 void
1250 _gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *iter)
1251 {
1252   FileModelNode *node;
1253   GtkTreePath *path;
1254
1255   g_return_if_fail (!model->has_editable);
1256
1257   model->has_editable = TRUE;
1258
1259   node = file_model_node_new (model, NULL);
1260   node->is_visible = TRUE;
1261
1262   node->next = model->roots;
1263   model->roots = node;
1264
1265   path = gtk_tree_path_new ();
1266   gtk_tree_path_append_index (path, 0);
1267   iter->user_data = node;
1268
1269   gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, iter);
1270
1271   gtk_tree_path_free (path);
1272 }
1273
1274 /**
1275  * _gtk_file_system_model_remove_editable:
1276  * @model: a #GtkFileSystemModel
1277  * 
1278  * Removes the "empty" row at the beginning of the model that was
1279  * created with _gtk_file_system_model_add_editable().  You should call
1280  * this function when your code is finished editing this temporary row.
1281  **/
1282 void
1283 _gtk_file_system_model_remove_editable (GtkFileSystemModel *model)
1284 {
1285   GtkTreePath *path;
1286   FileModelNode *node;
1287
1288   g_return_if_fail (model->has_editable);
1289
1290   model->has_editable = FALSE;
1291
1292   node = model->roots;
1293   model->roots = model->roots->next;
1294   file_model_node_free (node);
1295
1296   path = gtk_tree_path_new ();
1297   gtk_tree_path_append_index (path, 0);
1298
1299   gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
1300
1301   gtk_tree_path_free (path);
1302 }
1303
1304 static FileModelNode *
1305 file_model_node_new (GtkFileSystemModel *model,
1306                      const GtkFilePath  *path)
1307 {
1308   FileModelNode *node = g_new0 (FileModelNode, 1);
1309
1310   node->model = model;
1311   node->path = path ? gtk_file_path_copy (path) : NULL;
1312
1313   return node;
1314 }
1315
1316 static void
1317 file_model_node_free (FileModelNode *node)
1318 {
1319   file_model_node_clear (node->model, node);
1320   
1321   if (node->path)
1322     gtk_file_path_free (node->path);
1323
1324   if (node->info)
1325     gtk_file_info_free (node->info);
1326
1327   g_free (node);
1328 }
1329
1330 static const GtkFileInfo *
1331 file_model_node_get_info (GtkFileSystemModel *model,
1332                           FileModelNode      *node)
1333 {
1334   if (!node->info)
1335     {
1336       if (node->is_dummy)
1337         {
1338           node->info = gtk_file_info_new ();
1339           gtk_file_info_set_display_name (node->info, _("(Empty)"));
1340         }
1341       else if (node->parent || model->root_folder)
1342         {
1343           node->info = gtk_file_folder_get_info (node->parent ? node->parent->folder : model->root_folder,
1344                                                  node->path,
1345                                                  NULL); /* NULL-GError */
1346         }
1347       else
1348         g_assert_not_reached ();
1349     }
1350
1351   return node->info;
1352 }
1353
1354 static gboolean
1355 file_model_node_is_visible (GtkFileSystemModel *model,
1356                             FileModelNode      *node)
1357 {
1358   if (model->show_folders != model->show_files ||
1359       !model->show_hidden ||
1360       model->filter_func)
1361     {
1362       const GtkFileInfo *info = file_model_node_get_info (model, node);
1363
1364       if (!info)
1365         {
1366           /* File probably disappeared underneath us or resides in a
1367              directory where we have only partial access rights.  */
1368           return FALSE;
1369         }
1370
1371       if (model->show_folders != model->show_files &&
1372           model->show_folders != gtk_file_info_get_is_folder (info))
1373         return FALSE;
1374
1375       if (!model->show_hidden && gtk_file_info_get_is_hidden (info))
1376         return FALSE;
1377
1378       if (model->filter_func &&
1379           !model->filter_func (model, node->path, info, model->filter_data))
1380         return FALSE;
1381     }
1382
1383   return TRUE;
1384 }
1385
1386 static void
1387 file_model_node_clear (GtkFileSystemModel *model,
1388                        FileModelNode      *node)
1389 {
1390   FileModelNode *children;
1391   
1392   file_model_node_idle_clear_cancel (node);
1393   
1394   children = node->children;
1395   node->children = NULL;
1396   node->loaded = FALSE;
1397   
1398   while (children)
1399     {
1400       FileModelNode *next = children->next;
1401       
1402       file_model_node_free (children);
1403       
1404       children = next;
1405     }
1406
1407   if (node->folder)
1408     {
1409       /* Unreffing node->folder may cause roots_changed,
1410        * so we need to be careful about ordering.
1411        */
1412       GtkFileFolder *folder = node->folder;
1413       node->folder = NULL;
1414
1415       g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (deleted_callback), node);
1416       g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_added_callback), node);
1417       g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_changed_callback), node);
1418       g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_removed_callback), node);
1419       
1420       g_object_unref (folder);
1421     }
1422 }
1423
1424 static void
1425 file_model_node_ref (FileModelNode *node)
1426 {
1427   node->ref_count++;
1428   if (node->ref_count == 1 && node->parent)
1429     node->parent->n_referenced_children++;
1430 }
1431
1432 static gboolean
1433 idle_clear_callback (GtkFileSystemModel *model)
1434 {
1435   while (model->idle_clears)
1436     {
1437       FileModelNode *node = model->idle_clears->data;
1438       model->idle_clears = g_slist_delete_link (model->idle_clears, model->idle_clears);
1439
1440       node->idle_clear = FALSE;
1441       file_model_node_clear (node->model, node);
1442     }
1443
1444   return FALSE;
1445 }
1446  
1447 static void
1448 file_model_node_idle_clear (FileModelNode *node)
1449 {
1450   if (!node->idle_clear)
1451     {
1452       GtkFileSystemModel *model = node->model;
1453
1454       node->idle_clear = TRUE;
1455       if (!model->idle_clears)
1456         {
1457           model->idle_clear_source = g_idle_source_new ();
1458           g_source_set_priority (model->idle_clear_source, G_PRIORITY_HIGH);
1459           g_source_set_closure (model->idle_clear_source,
1460                                 g_cclosure_new_object (G_CALLBACK (idle_clear_callback),
1461                                                        G_OBJECT (model)));
1462           g_source_attach (model->idle_clear_source, NULL);
1463         }
1464
1465       model->idle_clears = g_slist_prepend (model->idle_clears, node);
1466       node->idle_clear = TRUE;
1467     }
1468 }
1469
1470 static void
1471 file_model_node_idle_clear_cancel (FileModelNode *node)
1472 {
1473   if (node->idle_clear)
1474     {
1475       GtkFileSystemModel *model = node->model;
1476
1477       model->idle_clears = g_slist_remove (model->idle_clears, node);
1478       if (!model->idle_clears)
1479         {
1480           g_source_destroy (model->idle_clear_source);
1481           model->idle_clear_source = NULL;
1482         }
1483       
1484       node->idle_clear = FALSE;
1485     }
1486 }
1487
1488 static void
1489 file_model_node_unref (GtkFileSystemModel *model,
1490                        FileModelNode       *node)
1491 {
1492   node->ref_count--;
1493   if (node->ref_count == 0)
1494     {
1495       file_model_node_clear (model, node);
1496       if (node->parent)
1497         file_model_node_child_unref (node->parent);
1498     }
1499 }
1500
1501 static void
1502 file_model_node_child_unref (FileModelNode *parent)
1503 {
1504   parent->n_referenced_children--;
1505   if (parent->n_referenced_children == 0)
1506     file_model_node_idle_clear (parent);
1507 }
1508
1509 struct GetChildrenData
1510 {
1511   GtkFileSystemModel *model;
1512   FileModelNode *node;
1513 };
1514
1515 static void
1516 get_children_get_folder_cb (GtkFileSystemHandle *handle,
1517                             GtkFileFolder       *folder,
1518                             const GError        *error,
1519                             gpointer             callback_data)
1520 {
1521   GSList *child_paths, *tmp_list;
1522   gboolean has_children = FALSE;
1523   gboolean cancelled = handle->cancelled;
1524   struct GetChildrenData *data = callback_data;
1525
1526   tmp_list = g_slist_find (data->model->pending_handles, handle);
1527
1528   if (!tmp_list)
1529     goto out;
1530
1531   data->model->pending_handles = g_slist_remove_link (data->model->pending_handles, tmp_list);
1532
1533   if (cancelled || !folder)
1534     {
1535       /* error, no folder, remove dummy child */
1536       if (data->node->parent && data->node->parent->has_dummy)
1537         {
1538           data->node->parent->children = NULL;
1539           data->node->parent->has_dummy = FALSE;
1540         }
1541
1542       file_model_node_free (data->node);
1543
1544       goto out;
1545     }
1546
1547   data->node->folder = folder;
1548   data->node->load_pending = FALSE;
1549
1550   if (gtk_file_folder_list_children (folder, &child_paths, NULL)) /* NULL-GError */
1551     {
1552       child_paths = gtk_file_paths_sort (child_paths);
1553
1554       for (tmp_list = child_paths; tmp_list; tmp_list = tmp_list->next)
1555         {
1556           FileModelNode *child_node = file_model_node_new (data->model, tmp_list->data);
1557           gtk_file_path_free (tmp_list->data);
1558           child_node->next = data->node->children;
1559           child_node->parent = data->node;
1560           child_node->depth = data->node->depth + 1;
1561           child_node->is_visible = file_model_node_is_visible (data->model, child_node);
1562
1563           if (child_node->is_visible)
1564             {
1565               GtkTreeIter iter;
1566               GtkTreePath *path;
1567
1568               has_children = TRUE;
1569
1570               iter.user_data = child_node;
1571               path = gtk_tree_model_get_path (GTK_TREE_MODEL (data->model), &iter);
1572               gtk_tree_model_row_inserted (GTK_TREE_MODEL (data->model), path, &iter);
1573               gtk_tree_path_free (path);
1574             }
1575
1576           data->node->children = child_node;
1577         }
1578       g_slist_free (child_paths);
1579     }
1580
1581   data->node->children = (FileModelNode *)g_slist_reverse ((GSList *)data->node->children);
1582
1583   g_signal_connect (data->node->folder, "deleted",
1584                     G_CALLBACK (deleted_callback), data->node);
1585   g_signal_connect (data->node->folder, "files-added",
1586                     G_CALLBACK (files_added_callback), data->node);
1587   g_signal_connect (data->node->folder, "files-changed",
1588                     G_CALLBACK (files_changed_callback), data->node);
1589   g_signal_connect (data->node->folder, "files-removed",
1590                     G_CALLBACK (files_removed_callback), data->node);
1591
1592   data->node->loaded = TRUE;
1593
1594   if (!has_children)
1595     {
1596       /* The hard case ... we claimed this folder had children, but actually
1597        * it didn't. We have to add a dummy child, possibly to remove later.
1598        */
1599       FileModelNode *child_node = file_model_node_new (data->model, NULL);
1600       child_node->is_visible = TRUE;
1601       child_node->parent = data->node;
1602       child_node->is_dummy = TRUE;
1603
1604       data->node->children = child_node;
1605       data->node->has_dummy = TRUE;
1606     }
1607
1608   g_object_set_data (G_OBJECT (data->node->folder), I_("model-node"), data->node);
1609
1610 out:
1611   g_object_unref (data->model);
1612   g_free (data);
1613
1614   g_object_unref (handle);
1615 }
1616
1617 static FileModelNode *
1618 file_model_node_get_children (GtkFileSystemModel *model,
1619                               FileModelNode      *node)
1620 {
1621   if (node->ref_count == 0)
1622     return NULL;
1623
1624   if (!node->loaded && !node->load_pending)
1625     {
1626       const GtkFileInfo *info = file_model_node_get_info (model, node);
1627       gboolean has_children = FALSE;
1628       gboolean is_folder = node->depth < model->max_depth && gtk_file_info_get_is_folder (info);
1629
1630       file_model_node_idle_clear_cancel (node);
1631
1632       if (is_folder)
1633         {
1634           struct GetChildrenData *data;
1635           GtkFileSystemHandle *handle;
1636
1637           data = g_new (struct GetChildrenData, 1);
1638           data->model = g_object_ref (model);
1639           data->node = node;
1640
1641           handle =
1642             gtk_file_system_get_folder (model->file_system,
1643                                         node->path,
1644                                         model->types,
1645                                         get_children_get_folder_cb,
1646                                         data);
1647
1648           model->pending_handles = g_slist_append (model->pending_handles, handle);
1649           node->load_pending = TRUE;
1650         }
1651
1652       if (is_folder && !has_children)
1653         {
1654           /* The hard case ... we claimed this folder had children, but actually
1655            * it didn't. We have to add a dummy child, possibly to remove later.
1656            */
1657           FileModelNode *child_node = file_model_node_new (model, NULL);
1658           child_node->is_visible = TRUE;
1659           child_node->parent = node;
1660           child_node->is_dummy = TRUE;
1661
1662           node->children = child_node;
1663           node->has_dummy = TRUE;
1664         }
1665     }
1666
1667   return node->children;
1668 }
1669
1670 static void
1671 do_files_added (GtkFileSystemModel *model,
1672                 FileModelNode      *parent_node,
1673                 GSList             *paths)
1674 {
1675   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1676   FileModelNode *children;
1677   FileModelNode *prev = NULL;
1678   GtkTreeIter iter;
1679   GtkTreePath *path;
1680   GSList *sorted_paths;
1681   GSList *tmp_list;
1682
1683   sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
1684   
1685   if (parent_node)
1686     {
1687       iter.user_data = parent_node;
1688       path = gtk_tree_model_get_path (tree_model, &iter);
1689       children = parent_node->children;
1690     }
1691   else
1692     {
1693       path = gtk_tree_path_new ();
1694       children = model->roots;
1695     }
1696
1697   gtk_tree_path_down (path);
1698   
1699   if (parent_node && parent_node->has_dummy)
1700     {
1701       prev = children;
1702       children = children->next;
1703       gtk_tree_path_next (path);
1704     }
1705
1706   for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
1707     {
1708       const GtkFilePath *file_path = tmp_list->data;
1709       
1710       while (children &&
1711              (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
1712         {
1713           prev = children;
1714           if (children->is_visible)
1715             gtk_tree_path_next (path);
1716           
1717           children = children->next;
1718         }
1719   
1720       if (children &&
1721           children->path && gtk_file_path_compare (children->path, file_path) == 0)
1722         {
1723           /* Shouldn't happen */
1724         }
1725       else
1726         {
1727           FileModelNode *new;
1728           
1729           new = file_model_node_new (model, file_path);
1730           
1731           if (children)
1732             new->next = children;
1733           if (prev)
1734             prev->next = new;
1735           else if (parent_node)
1736             parent_node->children = new;
1737           else
1738             model->roots = new;
1739
1740           prev = new;
1741           
1742           if (parent_node)
1743             {
1744               new->parent = parent_node;
1745               new->depth = parent_node->depth + 1;
1746             }
1747           
1748           new->is_visible = file_model_node_is_visible (model, new);
1749           
1750           if (new->is_visible)
1751             {
1752               iter.user_data = new;
1753               gtk_tree_path_free (path);
1754               path = gtk_tree_model_get_path (tree_model, &iter);
1755               gtk_tree_model_row_inserted (tree_model, path, &iter);
1756               
1757               if (gtk_file_system_model_iter_has_child (tree_model, &iter))
1758                 gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
1759               
1760               if (parent_node && parent_node->has_dummy)
1761                 {
1762                   FileModelNode *dummy = parent_node->children;
1763                   GtkTreePath *dummy_path;
1764                   
1765                   parent_node->children = parent_node->children->next;
1766                   parent_node->has_dummy = FALSE;
1767
1768                   dummy_path = gtk_tree_path_copy (path);
1769                   gtk_tree_path_up (dummy_path);
1770                   gtk_tree_path_down (dummy_path);
1771                   
1772                   gtk_tree_model_row_deleted (tree_model, dummy_path);
1773                   gtk_tree_path_free (dummy_path);
1774
1775                   if (dummy->ref_count)
1776                     file_model_node_child_unref (parent_node);
1777                   file_model_node_free (dummy);
1778                 }
1779               
1780               gtk_tree_path_next (path);
1781             }
1782         }
1783     }
1784
1785   gtk_tree_path_free (path);
1786   g_slist_free (sorted_paths);
1787 }
1788
1789 static void
1790 do_files_changed (GtkFileSystemModel *model,
1791                   FileModelNode      *parent_node,
1792                   GSList             *paths)
1793 {
1794   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1795   FileModelNode *children;
1796   GtkTreeIter iter;
1797   GtkTreePath *path;
1798   GSList *sorted_paths;
1799   GSList *tmp_list;
1800
1801   sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
1802   
1803   if (parent_node)
1804     {
1805       iter.user_data = parent_node;
1806       path = gtk_tree_model_get_path (tree_model, &iter);
1807       children = parent_node->children;
1808     }
1809   else
1810     {
1811       path = gtk_tree_path_new ();
1812       children = model->roots;
1813     }
1814
1815   gtk_tree_path_down (path);
1816   
1817   if (parent_node && parent_node->has_dummy)
1818     {
1819       children = children->next;
1820       gtk_tree_path_next (path);
1821     }
1822
1823   for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
1824     {
1825       const GtkFilePath *file_path = tmp_list->data;
1826       
1827       while (children &&
1828              (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
1829         {
1830           if (children->is_visible)
1831             gtk_tree_path_next (path);
1832           
1833           children = children->next;
1834         }
1835   
1836       if (children &&
1837           children->path && gtk_file_path_compare (children->path, file_path) == 0)
1838         {
1839           gtk_tree_model_row_changed (tree_model, path, &iter);
1840         }
1841       else
1842         {
1843           /* Shouldn't happen */
1844         }
1845     }
1846
1847   gtk_tree_path_free (path);
1848   g_slist_free (sorted_paths);
1849 }
1850
1851 static void
1852 do_files_removed (GtkFileSystemModel *model,
1853                   FileModelNode      *parent_node,
1854                   GSList             *paths)
1855 {
1856   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1857   FileModelNode *children;
1858   FileModelNode *prev = NULL;
1859   GtkTreeIter iter;
1860   GtkTreePath *path;
1861   GSList *sorted_paths;
1862   GSList *tmp_list;
1863   FileModelNode *tmp_child;
1864   gint n_visible;
1865
1866   sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
1867   
1868   if (parent_node)
1869     {
1870       iter.user_data = parent_node;
1871       path = gtk_tree_model_get_path (tree_model, &iter);
1872       children = parent_node->children;
1873     }
1874   else
1875     {
1876       path = gtk_tree_path_new ();
1877       children = model->roots;
1878     }
1879
1880   /* Count the number of currently visible children, so that
1881    * can catch when we need to insert a dummy node.
1882    */
1883   n_visible = 0;
1884   for (tmp_child = children; tmp_child; tmp_child = tmp_child->next)
1885     {
1886       if (tmp_child->is_visible)
1887         n_visible++;
1888     }
1889
1890   gtk_tree_path_down (path);
1891   
1892   if (parent_node && parent_node->has_dummy)
1893     {
1894       prev = children;
1895       children = children->next;
1896       gtk_tree_path_next (path);
1897     }
1898
1899   for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
1900     {
1901       const GtkFilePath *file_path = tmp_list->data;
1902       
1903       while (children &&
1904              (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
1905         {
1906           prev = children;
1907           if (children->is_visible)
1908             gtk_tree_path_next (path);
1909           
1910           children = children->next;
1911         }
1912   
1913       if (children &&
1914           children->path && gtk_file_path_compare (children->path, file_path) == 0)
1915         {
1916           FileModelNode *next = children->next;
1917
1918           if (children->is_visible)
1919             n_visible--;
1920           
1921           if (parent_node && n_visible == 0)
1922             {
1923               FileModelNode *dummy = file_model_node_new (model, NULL);
1924               dummy->is_visible = TRUE;
1925               dummy->parent = parent_node;
1926               dummy->is_dummy = TRUE;
1927
1928               parent_node->children = dummy;
1929               parent_node->has_dummy = TRUE;
1930
1931               iter.user_data = dummy;
1932               gtk_tree_model_row_inserted (tree_model, path, &iter);
1933               gtk_tree_path_next (path);
1934
1935               prev = dummy;
1936             }
1937           
1938           if (prev)
1939             prev->next = next;
1940           else if (parent_node)
1941             parent_node->children = next;
1942           else
1943             model->roots = next;
1944
1945           if (parent_node && children->ref_count)
1946             file_model_node_child_unref (parent_node);
1947               
1948           if (children->is_visible)
1949             gtk_tree_model_row_deleted (tree_model, path);
1950
1951           file_model_node_free (children);
1952
1953           children = next;
1954         }
1955       else
1956         {
1957           /* Shouldn't happen */
1958         }
1959     }
1960
1961   gtk_tree_path_free (path);
1962   g_slist_free (sorted_paths);
1963 }
1964
1965 static void
1966 deleted_callback (GtkFileFolder      *folder,
1967                   FileModelNode      *node)
1968 {
1969 }
1970
1971 static void
1972 files_added_callback (GtkFileFolder      *folder,
1973                       GSList             *paths,
1974                       FileModelNode      *node)
1975 {
1976   do_files_added (node->model, node, paths);
1977 }
1978
1979 static void
1980 files_changed_callback (GtkFileFolder      *folder,
1981                         GSList             *paths,
1982                         FileModelNode      *node)
1983 {
1984   do_files_changed (node->model, node, paths);
1985 }
1986
1987 static void
1988 files_removed_callback (GtkFileFolder      *folder,
1989                         GSList             *paths,
1990                         FileModelNode      *node)
1991 {
1992   do_files_removed (node->model, node, paths);
1993 }
1994
1995 static void
1996 root_deleted_callback (GtkFileFolder      *folder,
1997                        GtkFileSystemModel *model)
1998 {
1999 }
2000
2001 static void
2002 root_files_added_callback (GtkFileFolder      *folder,
2003                            GSList             *paths,
2004                            GtkFileSystemModel *model)
2005 {
2006   do_files_added (model, NULL, paths);
2007 }
2008
2009 static void
2010 root_files_changed_callback (GtkFileFolder      *folder,
2011                              GSList             *paths,
2012                              GtkFileSystemModel *model)
2013 {
2014   do_files_changed (model, NULL, paths);
2015 }
2016
2017 static void
2018 root_files_removed_callback (GtkFileFolder      *folder,
2019                              GSList             *paths,
2020                              GtkFileSystemModel *model)
2021 {
2022   do_files_removed (model, NULL, paths);
2023 }