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