]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemmodel.c
Add sorting, a size column.
[~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 "gtkfilesystemmodel.h"
22 #include "gtkfilesystem.h"
23 #include <gtk/gtktreemodel.h>
24 #include <string.h>
25
26 typedef struct _GtkFileSystemModelClass GtkFileSystemModelClass;
27 typedef struct _FileModelNode           FileModelNode;
28
29 #define GTK_FILE_SYSTEM_MODEL_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
30 #define GTK_IS_FILE_SYSTEM_MODEL_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_MODEL))
31 #define GTK_FILE_SYSTEM_MODEL_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
32
33 struct _GtkFileSystemModelClass
34 {
35   GObjectClass parent_class;
36 };
37
38 struct _GtkFileSystemModel
39 {
40   GObject parent_instance;
41
42   GtkFileSystem  *file_system;
43   GtkFileInfoType types;
44   FileModelNode  *roots;
45   GtkFileFolder  *root_folder;
46
47   GSource *dummy_idle;
48   GSList *dummy_idle_nodes;
49
50   gushort max_depth;
51   
52   guint show_hidden : 1;
53   guint show_folders : 1;
54   guint show_files : 1;
55   guint folders_only : 1;
56 };
57
58 struct _FileModelNode
59 {
60   gchar *uri;
61   FileModelNode *next;
62
63   GtkFileInfo *info;
64   GtkFileFolder *folder;
65   
66   FileModelNode *children;
67   FileModelNode *parent;
68
69   guint ref_count;
70
71   gushort depth;
72
73   guint loaded : 1;
74   guint has_dummy : 1;
75   guint is_visible : 1;
76   guint has_children : 1;
77 };
78
79 static void gtk_file_system_model_class_init   (GtkFileSystemModelClass *class);
80 static void gtk_file_system_model_iface_init   (GtkTreeModelIface       *iface);
81 static void gtk_file_system_model_init         (GtkFileSystemModel      *model);
82 static void gtk_file_system_model_finalize     (GObject                 *object);
83
84 static GtkTreeModelFlags gtk_file_system_model_get_flags       (GtkTreeModel *tree_model);
85 static gint              gtk_file_system_model_get_n_columns   (GtkTreeModel *tree_model);
86 static GType             gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
87                                                                 gint          index);
88 static gboolean          gtk_file_system_model_get_iter        (GtkTreeModel *tree_model,
89                                                                 GtkTreeIter  *iter,
90                                                                 GtkTreePath  *path);
91 static GtkTreePath *     gtk_file_system_model_get_path        (GtkTreeModel *tree_model,
92                                                                 GtkTreeIter  *iter);
93 static void              gtk_file_system_model_get_value       (GtkTreeModel *tree_model,
94                                                                 GtkTreeIter  *iter,
95                                                                 gint          column,
96                                                                 GValue       *value);
97 static gboolean          gtk_file_system_model_iter_next       (GtkTreeModel *tree_model,
98                                                                 GtkTreeIter  *iter);
99 static gboolean          gtk_file_system_model_iter_children   (GtkTreeModel *tree_model,
100                                                                 GtkTreeIter  *iter,
101                                                                 GtkTreeIter  *parent);
102 static gboolean          gtk_file_system_model_iter_has_child  (GtkTreeModel *tree_model,
103                                                                 GtkTreeIter  *iter);
104 static gint              gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
105                                                                 GtkTreeIter  *iter);
106 static gboolean          gtk_file_system_model_iter_nth_child  (GtkTreeModel *tree_model,
107                                                                 GtkTreeIter  *iter,
108                                                                 GtkTreeIter  *parent,
109                                                                 gint          n);
110 static gboolean          gtk_file_system_model_iter_parent     (GtkTreeModel *tree_model,
111                                                                 GtkTreeIter  *iter,
112                                                                 GtkTreeIter  *child);
113 static void              gtk_file_system_model_ref_node        (GtkTreeModel *tree_model,
114                                                                 GtkTreeIter  *iter);
115 static void              gtk_file_system_model_unref_node      (GtkTreeModel *tree_model,
116                                                                 GtkTreeIter  *iter);
117
118 static void queue_dummy_idle   (GtkFileSystemModel *model,
119                                 FileModelNode      *node);
120 static void unqueue_dummy_idle (GtkFileSystemModel *model,
121                                 FileModelNode      *node);
122
123 static FileModelNode *file_model_node_new        (const gchar        *uri);
124 static void           file_model_node_free       (FileModelNode      *node);
125 static void           file_model_node_ref        (FileModelNode      *node);
126 static void           file_model_node_unref      (GtkFileSystemModel *model,
127                                                   FileModelNode      *node);
128
129 static const GtkFileInfo *file_model_node_get_info     (GtkFileSystemModel *model,
130                                                         FileModelNode      *node);
131 static gboolean           file_model_node_is_visible   (GtkFileSystemModel *model,
132                                                         FileModelNode      *node);
133 static void               file_model_node_clear        (GtkFileSystemModel *model,
134                                                         FileModelNode      *node);
135 static FileModelNode *    file_model_node_get_children (GtkFileSystemModel *model,
136                                                         FileModelNode      *node);
137
138 GType
139 _gtk_file_system_model_get_type (void)
140 {
141   static GType file_system_model_type = 0;
142
143   if (!file_system_model_type)
144     {
145       static const GTypeInfo file_system_model_info =
146       {
147         sizeof (GtkFileSystemModelClass),
148         NULL,           /* base_init */
149         NULL,           /* base_finalize */
150         (GClassInitFunc) gtk_file_system_model_class_init,
151         NULL,           /* class_finalize */
152         NULL,           /* class_data */
153         sizeof (GtkFileSystemModel),
154         0,              /* n_preallocs */
155         (GInstanceInitFunc) gtk_file_system_model_init,
156       };
157       
158       static const GInterfaceInfo file_system_info =
159       {
160         (GInterfaceInitFunc) gtk_file_system_model_iface_init, /* interface_init */
161         NULL,                                                 /* interface_finalize */
162         NULL                                                  /* interface_data */
163       };
164
165       file_system_model_type = g_type_register_static (G_TYPE_OBJECT,
166                                                       "GtkFileSystemModel",
167                                                       &file_system_model_info, 0);
168       g_type_add_interface_static (file_system_model_type,
169                                    GTK_TYPE_TREE_MODEL,
170                                    &file_system_info);
171     }
172
173   return file_system_model_type;
174 }
175
176 static void
177 gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
178 {
179   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
180   
181   gobject_class->finalize = gtk_file_system_model_finalize;
182 }
183
184 static void
185 gtk_file_system_model_iface_init (GtkTreeModelIface *iface)
186 {
187   iface->get_flags =       gtk_file_system_model_get_flags;
188   iface->get_n_columns =   gtk_file_system_model_get_n_columns;
189   iface->get_column_type = gtk_file_system_model_get_column_type;
190   iface->get_iter =        gtk_file_system_model_get_iter;
191   iface->get_path =        gtk_file_system_model_get_path;
192   iface->get_value =       gtk_file_system_model_get_value;
193   iface->iter_next =       gtk_file_system_model_iter_next;
194   iface->iter_children =   gtk_file_system_model_iter_children;
195   iface->iter_has_child =  gtk_file_system_model_iter_has_child;
196   iface->iter_n_children = gtk_file_system_model_iter_n_children;
197   iface->iter_nth_child =  gtk_file_system_model_iter_nth_child;
198   iface->iter_parent =     gtk_file_system_model_iter_parent;
199   iface->ref_node =        gtk_file_system_model_ref_node;
200   iface->unref_node =      gtk_file_system_model_unref_node;
201 }
202
203 static void
204 gtk_file_system_model_init (GtkFileSystemModel *model)
205 {
206   model->show_files = TRUE;
207   model->show_folders = TRUE;
208   model->show_hidden = FALSE;
209 }
210
211 static void
212 gtk_file_system_model_finalize (GObject *object)
213 {
214   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
215   FileModelNode *children;
216
217   if (model->root_folder)
218     g_object_unref (model->root_folder);
219
220   children = model->roots;
221   while (children)
222     {
223       file_model_node_free (children);
224       children = children->next;
225     }
226 }
227
228 /*
229  * ******************** GtkTreeModel methods ********************
230  */
231
232 static GtkTreeModelFlags
233 gtk_file_system_model_get_flags (GtkTreeModel *tree_model)
234 {
235   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
236   GtkTreeModelFlags flags = GTK_TREE_MODEL_ITERS_PERSIST;
237
238   if (model->max_depth == 1)
239     flags |= GTK_TREE_MODEL_LIST_ONLY;
240
241   return flags;
242 }
243
244 static gint
245 gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model)
246 {
247   return GTK_FILE_SYSTEM_MODEL_N_COLUMNS;
248 }
249
250 static GType
251 gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
252                                        gint          index)
253 {
254   switch (index)
255     {
256     case GTK_FILE_SYSTEM_MODEL_URI:
257       return G_TYPE_STRING;
258     case GTK_FILE_SYSTEM_MODEL_INFO:
259       return GTK_TYPE_FILE_INFO;
260     default:
261       g_assert_not_reached ();
262       return G_TYPE_NONE;
263     }
264 }
265
266 static gboolean
267 gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
268                                 GtkTreeIter  *iter,
269                                 GtkTreePath  *path)
270 {
271   GtkTreeIter parent;
272   gint *indices;
273   gint depth, i;
274
275   indices = gtk_tree_path_get_indices (path);
276   depth = gtk_tree_path_get_depth (path);
277
278   g_return_val_if_fail (depth > 0, FALSE);
279
280   if (!gtk_tree_model_iter_nth_child (tree_model, iter, NULL, indices[0]))
281     return FALSE;
282
283   for (i = 1; i < depth; i++)
284     {
285       parent = *iter;
286       if (!gtk_tree_model_iter_nth_child (tree_model, iter, &parent, indices[i]))
287         return FALSE;
288     }
289
290   return TRUE;
291 }
292
293 static GtkTreePath *
294 gtk_file_system_model_get_path (GtkTreeModel *tree_model,
295                                 GtkTreeIter  *iter)
296 {
297   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
298   FileModelNode *node = iter->user_data;
299
300   GtkTreePath *result = gtk_tree_path_new ();
301
302   while (node)
303     {
304       FileModelNode *parent = node->parent;
305       FileModelNode *children;
306       int n = 0;
307
308       if (parent)
309         children = parent->children;
310       else
311         children = model->roots;
312
313       while (children != node)
314         {
315           if (children->is_visible)
316             n++;
317           children = children->next;
318         }
319       
320       gtk_tree_path_prepend_index (result, n);
321
322       node = parent;
323     }
324
325   return result;
326 }
327
328 static void
329 gtk_file_system_model_get_value (GtkTreeModel *tree_model,
330                                  GtkTreeIter  *iter,
331                                  gint          column,
332                                  GValue       *value)
333 {
334   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
335   FileModelNode *node = iter->user_data;
336   
337   switch (column)
338     {
339     case GTK_FILE_SYSTEM_MODEL_URI:
340       g_value_set_string (value, node->uri);
341       break;
342     case GTK_FILE_SYSTEM_MODEL_INFO:
343       g_value_set_boxed (value, file_model_node_get_info (model, node));
344       break;
345     default:
346       g_assert_not_reached ();
347     }
348 }
349
350 static gboolean
351 gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
352                                  GtkTreeIter  *iter)
353 {
354   FileModelNode *node = iter->user_data;
355
356   node = node->next;
357   while (node && !node->is_visible)
358     node = node->next;
359   
360   iter->user_data = node;
361
362   return node != NULL;
363 }
364
365 static gboolean
366 gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
367                                      GtkTreeIter  *iter,
368                                      GtkTreeIter  *parent)
369 {
370   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
371   FileModelNode *children;
372
373   if (parent)
374     {
375       FileModelNode *parent_node = parent->user_data;
376       children = file_model_node_get_children (model, parent_node);
377     }
378   else
379     {
380       children = model->roots;
381     }
382
383   while (children && !children->is_visible)
384     children = children->next;
385
386   iter->user_data = children;
387
388   return children != NULL;
389 }
390
391 static gboolean
392 gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
393                                       GtkTreeIter  *iter)
394 {
395   FileModelNode *node = iter->user_data;
396
397   /* We never want to go into a directory just to
398    * find out if it has children
399    */
400   if (node->loaded)
401     return node->has_children;
402   else
403     {
404       GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
405
406       if (node->depth == model->max_depth)
407         return FALSE;
408       else
409         {
410           const GtkFileInfo *info = file_model_node_get_info (model, node);
411           return gtk_file_info_get_is_folder (info);
412         }
413     }
414 }
415
416 static gint
417 gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
418                                        GtkTreeIter  *iter)
419 {
420   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
421   FileModelNode *children;
422   gint n = 0;
423
424   if (iter)
425     {
426       FileModelNode *node = iter->user_data;
427       children = file_model_node_get_children (model, node);
428     }
429   else
430     {
431       children = model->roots;
432     }
433
434   while (children)
435     {
436       if (children->is_visible)
437         n++;
438       children = children->next;
439     }
440
441   return n;
442 }
443
444 static gboolean
445 gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
446                                       GtkTreeIter  *iter,
447                                       GtkTreeIter  *parent,
448                                       gint          n)
449 {
450   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
451   FileModelNode *children;
452
453   if (parent)
454     {
455       FileModelNode *parent_node = parent->user_data;
456       children = file_model_node_get_children (model, parent_node);
457     }
458   else
459     {
460       children = model->roots;
461     }
462
463   while (children && !children->is_visible)
464     children = children->next;
465
466   while (n && children)
467     {
468       n--;
469       children = children->next;
470       while (children && !children->is_visible)
471         children = children->next;
472     }
473
474   iter->user_data = children;
475
476   return children != NULL;
477 }
478
479 static gboolean
480 gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
481                                    GtkTreeIter  *iter,
482                                    GtkTreeIter  *child)
483 {
484   FileModelNode *node = child->user_data;
485   
486   node = node->parent;
487   iter->user_data = node;
488
489   return node != NULL;
490 }
491
492 static void
493 gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
494                                 GtkTreeIter  *iter)
495 {
496   file_model_node_ref (iter->user_data);
497 }
498
499 static void
500 gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
501                                   GtkTreeIter  *iter)
502 {
503   file_model_node_unref (GTK_FILE_SYSTEM_MODEL (tree_model),
504                          iter->user_data);
505 }
506
507 /**
508  * _gtk_file_system_model_new:
509  * @file_system: an object implementing #GtkFileSystem
510  * @root_uri: the URI of root of the file system to display,
511  *            or %NULL to display starting from the
512  *            root or roots of the fielsystem.
513  * @max_depth: the maximum depth from the children of @root_uri
514  *             or the roots of the file system to display in
515  *             the file selector). A depth of 0 displays
516  *             only the immediate children of @root_uri,
517  *             or the roots of the filesystem. -1 for no
518  *             maximum depth.
519  * @types: a bitmask indicating the types of information
520  *         that is desired about the files. This will
521  *         determine what information is returned by
522  *         _gtk_file_system_model_get_info().
523  *
524  * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
525  * object wraps a #GtkFileSystem interface as a #GtkTreeModel.
526  * Using the @root_uri and @max_depth parameters, the tree model
527  * can be restricted to a subportion of the entire file system.
528  * 
529  * Return value: the newly created #GtkFileSystemModel object.
530  **/
531 GtkFileSystemModel *
532 _gtk_file_system_model_new (GtkFileSystem  *file_system,
533                             const gchar    *root_uri,
534                             gint            max_depth,
535                             GtkFileInfoType types)
536 {
537   GtkFileSystemModel *model;
538   GSList *roots, *tmp_list;
539
540   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
541
542   model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
543   model->file_system = g_object_ref (file_system);
544   if (max_depth < 0)
545     model->max_depth = G_MAXUSHORT;
546   else
547     model->max_depth = MIN (max_depth, G_MAXUSHORT);
548   model->types = types | GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN;
549
550   if (root_uri)
551     {
552       GSList *child_uris;
553       
554       model->root_folder = gtk_file_system_get_folder (file_system, root_uri,
555                                                        model->types,
556                                                        NULL);   /* NULL-GError */
557
558       if (model->root_folder &&
559           gtk_file_folder_list_children (model->root_folder,
560                                          &child_uris,
561                                          NULL)) /* NULL-GError */
562         roots = child_uris;
563     }
564   else
565     roots = gtk_file_system_list_roots (file_system);
566
567   roots = g_slist_sort (roots, (GCompareFunc)strcmp);
568   
569   for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
570     {
571       FileModelNode *node = file_model_node_new (tmp_list->data);
572       g_free (tmp_list->data);
573       node->is_visible = file_model_node_is_visible (model, node);
574       node->next = model->roots;
575       node->depth = 0;
576       model->roots = node;
577     }
578   g_slist_free (roots);
579
580   model->roots = (FileModelNode *)g_slist_reverse ((GSList *)model->roots);
581   
582   return model;
583 }
584
585 static void
586 model_refilter_recurse (GtkFileSystemModel *model,
587                         FileModelNode      *parent,
588                         GtkTreePath        *path)
589 {
590   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
591   int i = 0;
592   FileModelNode *nodes;
593   gboolean has_children = FALSE;
594
595   if (parent && !parent->loaded)
596     return;
597
598   if (parent)
599     nodes = parent->children;
600   else
601     nodes = model->roots;
602
603   while (nodes)
604     {
605       FileModelNode *next = nodes->next;
606       gboolean is_visible;
607       
608       gtk_tree_path_append_index (path, i);
609
610       is_visible = file_model_node_is_visible (model, nodes);
611       
612       if (!is_visible && nodes->is_visible)
613         {
614           file_model_node_clear (model, nodes);
615           gtk_tree_model_row_deleted (tree_model, path);
616
617           nodes->is_visible = FALSE;
618         }
619       else if (is_visible && !nodes->is_visible)
620         {
621           GtkTreeIter iter;
622
623           iter.user_data = nodes;
624           gtk_tree_model_row_inserted (tree_model, path, &iter);
625
626           nodes->is_visible = TRUE;
627         }
628       else
629         model_refilter_recurse (model, nodes, path);
630
631       if (is_visible)
632         {
633           has_children = TRUE;
634           i++;
635         }
636       
637       gtk_tree_path_up (path);
638       
639       nodes = next;
640     }
641
642   if (parent && (has_children != parent->has_children))
643     {
644       GtkTreeIter iter;
645
646       parent->has_children = has_children;
647
648       iter.user_data = parent;
649       gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
650     }
651 }
652
653 /**
654  * _gtk_file_system_model_set_show_hidden:
655  * @model: a #GtkFileSystemModel
656  * @show_hidden: whether hidden files should be displayed
657  * 
658  * Sets whether hidden files should be included in the #GtkTreeModel
659  * for display.
660  **/
661 void
662 _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
663                                         gboolean            show_hidden)
664 {
665   show_hidden = show_hidden != FALSE;
666
667   if (show_hidden != model->show_hidden)
668     {
669       GtkTreePath *path;
670
671       model->show_hidden = show_hidden;
672
673       path = gtk_tree_path_new ();
674       model_refilter_recurse (model, NULL, path);
675       gtk_tree_path_free (path);
676     }
677 }
678
679 /**
680  * _gtk_file_system_model_set_show_folders:
681  * @model: a #GtkFileSystemModel
682  * @show_folders: whether folders should be displayed
683  * 
684  * Sets whether folders should be included in the #GtkTreeModel for
685  * display.
686  **/
687 void
688 _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
689                                          gboolean            show_folders)
690 {
691   show_folders = show_folders != FALSE;
692
693   if (show_folders != model->show_folders)
694     {
695       GtkTreePath *path;
696
697       model->show_folders = show_folders;
698
699       path = gtk_tree_path_new ();
700       model_refilter_recurse (model, NULL, path);
701       gtk_tree_path_free (path);
702     }
703 }
704
705 /**
706  * _gtk_file_system_model_set_show_files:
707  * @model: a #GtkFileSystemModel
708  * @show_files: whether files (as opposed to folders) should
709  *              be displayed.
710  * 
711  * Sets whether files (as opposed to folders) should be included
712  * in the #GtkTreeModel for display.
713  **/
714 void
715 _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
716                                        gboolean            show_files)
717 {
718   show_files = show_files != FALSE;
719
720   if (show_files != model->show_files)
721     {
722       GtkTreePath *path;
723
724       model->show_files = show_files;
725
726       path = gtk_tree_path_new ();
727       model_refilter_recurse (model, NULL, path);
728       gtk_tree_path_free (path);
729     }
730 }
731
732 /**
733  * _gtk_file_system_model_get_info:
734  * @model: a #GtkFileSystemModel
735  * @iter: a #GtkTreeIter pointing to a row of @model
736  * 
737  * Gets the #GtkFileInfo structure for a particular row
738  * of @model. The information included in this structure
739  * is determined by the @types parameter to
740  * _gtk_file_system_model_new().
741  * 
742  * Return value: a #GtkFileInfo structure. This structure
743  *   is owned by @model and must not be modified or freed.
744  *   If you want to save the information for later use,
745  *   you must make a copy, since the structure may be
746  *   freed on later changes to the file system.
747  **/
748 const GtkFileInfo *
749 _gtk_file_system_model_get_info (GtkFileSystemModel *model,
750                                  GtkTreeIter        *iter)
751 {
752   return file_model_node_get_info (model, iter->user_data);
753 }
754
755 /**
756  * _gtk_file_system_model_get_uri:
757  * @model: a #GtkFileSystemModel
758  * @iter: a #GtkTreeIter pointing to a row of @model
759  * 
760  * Gets the URI for a particular row in @model. 
761  *
762  * Return value: the URI. This string is owned by @model and
763  *   or freed. If you want to save the URI for later use,
764  *   you must make a copy, since the string may be freed
765  *   on later changes to the file system.
766  **/
767 const gchar *
768 _gtk_file_system_model_get_uri (GtkFileSystemModel *model,
769                                 GtkTreeIter        *iter)
770 {
771   FileModelNode *node = iter->user_data;
772
773   return node->uri;
774 }
775
776 static void
777 unref_node_and_parents (GtkFileSystemModel *model,
778                         FileModelNode      *node)
779 {
780   file_model_node_unref (model, node);
781   if (node->parent)
782     file_model_node_unref (model, node->parent);
783 }
784
785 static FileModelNode *
786 find_and_ref_uri (GtkFileSystemModel *model,
787                   const gchar        *uri)
788 {
789   gchar *parent_uri;
790   FileModelNode *parent_node;
791   FileModelNode *children;
792   
793   if (!gtk_file_system_get_parent (model->file_system, uri, &parent_uri, NULL))
794     return NULL;
795
796   if (parent_uri)
797     {
798       parent_node = find_and_ref_uri (model, parent_uri);
799       g_free (parent_uri);
800
801       if (!parent_node)
802         return NULL;
803     }
804   else
805     parent_node = NULL;
806
807   if (parent_node)
808     children = file_model_node_get_children (model, parent_node);
809   else
810     children = model->roots;
811
812   while (children)
813     {
814       if (children->is_visible &&
815           strcmp (children->uri, uri) == 0)
816         {
817           file_model_node_ref (children);
818           return children;
819         }
820
821       children = children->next;
822     }
823
824   if (parent_node)
825     unref_node_and_parents (model, parent_node);
826
827   return FALSE;
828 }
829
830 /**
831  * _gtk_file_system_model_uri_do:
832  * @model: a #GtkFileSystemModel
833  * @uri: a URI pointing to a file in the filesystem
834  *       for @model.
835  * @func: Function to call with the path and iter corresponding
836  *        to @uri.
837  * @user_data: data to pass to @func
838  * 
839  * Locates @uri within @model, referencing
840  * (gtk_tree_model_ref_node ()) all parent nodes,
841  * calls @func passing in the path and iter for @uri,
842  * then unrefs all the parent nodes.
843  *
844  * The reason for doing this operation as a callback
845  * is so that if the operation performed with the the
846  * path and iter results in referencing the the node
847  * and/or parent nodes, we don't load all the information
848  * about the nodes.
849  *
850  * This function is particularly useful for expanding
851  * a #GtkTreeView to a particular point in the file system.
852  * 
853  * Return value: %TRUE if the URI was successfully
854  *  found in @model and @func was called.
855  **/
856 gboolean
857 _gtk_file_system_model_uri_do (GtkFileSystemModel       *model,
858                                const gchar              *uri,
859                                GtkFileSystemModelURIFunc func,
860                                gpointer                  user_data)
861 {
862   FileModelNode *node = find_and_ref_uri (model, uri);
863
864   if (node)
865     {
866       GtkTreeIter iter;
867       GtkTreePath *path;
868
869       iter.user_data = node;
870       path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
871
872       (*func) (model, path, &iter, user_data);
873
874       gtk_tree_path_free (path);
875       unref_node_and_parents (model, node);
876
877       return TRUE;
878     }
879   else
880     return FALSE;
881 }
882
883 static gboolean
884 dummy_idle_callback (GtkFileSystemModel *model)
885 {
886   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
887   GSList *tmp_list;
888
889   for (tmp_list = model->dummy_idle_nodes; tmp_list; tmp_list = tmp_list->next)
890     {
891       GtkTreeIter iter;
892       GtkTreePath *path;
893       
894       FileModelNode *node = tmp_list->data;
895       g_assert (node->children && !node->children->next && !node->children->children);
896
897       iter.user_data = node->children;
898       path = gtk_tree_model_get_path (tree_model, &iter);
899       
900       if (node->children->ref_count)
901         gtk_tree_model_row_deleted (tree_model, path);
902
903       gtk_tree_path_up (path);
904       iter.user_data = node;
905       gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
906
907       gtk_tree_path_free (path);
908
909       file_model_node_free (node->children);
910       node->children = NULL;
911       node->has_children = FALSE;
912       node->has_dummy = FALSE;
913     }
914
915   model->dummy_idle_nodes = FALSE;
916   model->dummy_idle = NULL;
917
918   return FALSE;
919 }
920
921 static void
922 queue_dummy_idle (GtkFileSystemModel *model,
923                   FileModelNode      *node)
924 {
925   model->dummy_idle_nodes = g_slist_prepend (model->dummy_idle_nodes, node);
926
927   if (!model->dummy_idle)
928     {
929       model->dummy_idle = g_idle_source_new ();
930       g_source_set_priority (model->dummy_idle, G_PRIORITY_HIGH_IDLE);
931       g_source_set_closure (model->dummy_idle,
932                             g_cclosure_new_object (G_CALLBACK (dummy_idle_callback), G_OBJECT (model)));
933       g_source_attach (model->dummy_idle, NULL);
934     }
935 }
936
937 static void
938 unqueue_dummy_idle (GtkFileSystemModel *model,
939                     FileModelNode      *node)
940 {
941   model->dummy_idle_nodes = g_slist_remove (model->dummy_idle_nodes, node);
942   
943   if (!model->dummy_idle_nodes)
944     g_source_destroy (model->dummy_idle);
945 }
946
947 static FileModelNode *
948 file_model_node_new (const gchar *uri)
949 {
950   FileModelNode *node = g_new0 (FileModelNode, 1);
951
952   node->uri = g_strdup (uri);
953
954   return node;
955 }
956
957 static void
958 file_model_node_free (FileModelNode *node)
959 {
960   if (node->children)
961     {
962       FileModelNode *children;
963       
964       for (children = node->children; children; children = children->next)
965         file_model_node_free (children);
966     }
967   
968   if (node->uri)
969     g_free (node->uri);
970
971   if (node->info)
972     gtk_file_info_free (node->info);
973
974   if (node->folder)
975     g_object_unref (node->folder);
976
977   g_free (node);
978 }
979
980 static const GtkFileInfo *
981 file_model_node_get_info (GtkFileSystemModel *model,
982                           FileModelNode      *node)
983 {
984   if (!node->info)
985     {
986       if (node->parent && node->parent->has_dummy)
987         {
988           node->info = gtk_file_info_new ();
989           gtk_file_info_set_display_name (node->info, "Loading...");
990         }
991       else if (node->parent || model->root_folder)
992         {
993           node->info = gtk_file_folder_get_info (node->parent ? node->parent->folder : model->root_folder,
994                                                  node->uri,
995                                                  NULL); /* NULL-GError */
996         }
997       else
998         {
999           node->info = gtk_file_system_get_root_info (model->file_system,
1000                                                       node->uri,
1001                                                       model->types,
1002                                                       NULL);  /* NULL-GError */
1003         }
1004     }
1005
1006   return node->info;
1007 }
1008
1009 static gboolean
1010 file_model_node_is_visible (GtkFileSystemModel *model,
1011                             FileModelNode      *node)
1012 {
1013   if (model->show_hidden && model->show_folders && model->show_files)
1014     return TRUE;
1015   else
1016     {
1017       const GtkFileInfo *info = file_model_node_get_info (model, node);
1018       gboolean is_folder = gtk_file_info_get_is_folder (info);
1019
1020       if (!model->show_folders && is_folder)
1021         return FALSE;
1022       if (!model->show_files && !is_folder)
1023         return FALSE;
1024       if (!model->show_hidden && gtk_file_info_get_is_hidden (info))
1025         return FALSE;
1026
1027       return TRUE;
1028     }
1029 }
1030
1031 static void
1032 file_model_node_clear (GtkFileSystemModel *model,
1033                        FileModelNode      *node)
1034 {
1035   FileModelNode *children;
1036   
1037   if (node->has_dummy)
1038     unqueue_dummy_idle (model, node);
1039   
1040   if (node->folder)
1041     {
1042       g_object_unref (node->folder);
1043       node->folder = NULL;
1044     }
1045   
1046   children = node->children;
1047   node->children = NULL;
1048   node->has_children = FALSE;
1049   node->loaded = FALSE;
1050   
1051   while (children)
1052     {
1053       FileModelNode *next = children->next;
1054       
1055       file_model_node_clear (model, children);
1056       file_model_node_free (children);
1057       
1058       children = next;
1059     }
1060
1061   node->ref_count = 0;
1062 }
1063
1064 static void
1065 file_model_node_ref (FileModelNode *node)
1066 {
1067   node->ref_count++;
1068 }
1069
1070 static void
1071 file_model_node_unref (GtkFileSystemModel *model,
1072                        FileModelNode       *node)
1073 {
1074   node->ref_count--;
1075   if (node->ref_count == 0)
1076     file_model_node_clear (model, node);
1077 }
1078
1079 static FileModelNode *
1080 file_model_node_get_children (GtkFileSystemModel *model,
1081                               FileModelNode      *node)
1082 {
1083   if (node->ref_count == 0)
1084     return NULL;
1085
1086   if (!node->loaded)
1087     {
1088       const GtkFileInfo *info = file_model_node_get_info (model, node);
1089       gboolean has_children = FALSE;
1090       gboolean is_folder = node->depth < model->max_depth && gtk_file_info_get_is_folder (info);
1091       
1092       if (is_folder)
1093         node->folder = gtk_file_system_get_folder (model->file_system,
1094                                                    node->uri,
1095                                                    model->types,
1096                                                    NULL);       /* NULL-GError */
1097
1098       if (node->folder)
1099         {
1100           GSList *child_uris, *tmp_list;
1101           
1102           if (gtk_file_folder_list_children (node->folder, &child_uris, NULL)) /* NULL-GError */
1103             {
1104               child_uris = g_slist_sort (child_uris, (GCompareFunc)strcmp);
1105
1106               for (tmp_list = child_uris; tmp_list; tmp_list = tmp_list->next)
1107                 {
1108                   FileModelNode *child_node = file_model_node_new (tmp_list->data);
1109                   g_free (tmp_list->data);
1110                   child_node->next = node->children;
1111                   child_node->parent = node;
1112                   child_node->depth = node->depth + 1;
1113                   child_node->is_visible = file_model_node_is_visible (model, child_node);
1114                   if (child_node->is_visible)
1115                     has_children = TRUE;
1116                   node->children = child_node;
1117                 }
1118               g_slist_free (child_uris);
1119             }
1120
1121           node->children = (FileModelNode *)g_slist_reverse ((GSList *)node->children);
1122         }
1123       
1124       node->has_children = has_children;
1125
1126       if (is_folder && !node->has_children)
1127         {
1128           /* The hard case ... we claimed this folder had children, but actually
1129            * it didn't. We have to add a dummy child, then remove it later
1130            */
1131           FileModelNode *child_node = file_model_node_new ("***dummy***");
1132           child_node->is_visible = TRUE;
1133           child_node->parent = node;
1134
1135           node->children = child_node;
1136           node->has_children = TRUE;
1137           node->has_dummy = TRUE;
1138
1139           queue_dummy_idle (model, node);
1140         }
1141
1142       node->loaded = TRUE;
1143     }
1144
1145   return node->children;
1146 }