]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemmodel.c
aba2a1e85770e585fe765f58c645afb79a31f2fd
[~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     case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
261       return G_TYPE_STRING;
262    default:
263       g_assert_not_reached ();
264       return G_TYPE_NONE;
265     }
266 }
267
268 static gboolean
269 gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
270                                 GtkTreeIter  *iter,
271                                 GtkTreePath  *path)
272 {
273   GtkTreeIter parent;
274   gint *indices;
275   gint depth, i;
276
277   indices = gtk_tree_path_get_indices (path);
278   depth = gtk_tree_path_get_depth (path);
279
280   g_return_val_if_fail (depth > 0, FALSE);
281
282   if (!gtk_tree_model_iter_nth_child (tree_model, iter, NULL, indices[0]))
283     return FALSE;
284
285   for (i = 1; i < depth; i++)
286     {
287       parent = *iter;
288       if (!gtk_tree_model_iter_nth_child (tree_model, iter, &parent, indices[i]))
289         return FALSE;
290     }
291
292   return TRUE;
293 }
294
295 static GtkTreePath *
296 gtk_file_system_model_get_path (GtkTreeModel *tree_model,
297                                 GtkTreeIter  *iter)
298 {
299   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
300   FileModelNode *node = iter->user_data;
301
302   GtkTreePath *result = gtk_tree_path_new ();
303
304   while (node)
305     {
306       FileModelNode *parent = node->parent;
307       FileModelNode *children;
308       int n = 0;
309
310       if (parent)
311         children = parent->children;
312       else
313         children = model->roots;
314
315       while (children != node)
316         {
317           if (children->is_visible)
318             n++;
319           children = children->next;
320         }
321       
322       gtk_tree_path_prepend_index (result, n);
323
324       node = parent;
325     }
326
327   return result;
328 }
329
330 static void
331 gtk_file_system_model_get_value (GtkTreeModel *tree_model,
332                                  GtkTreeIter  *iter,
333                                  gint          column,
334                                  GValue       *value)
335 {
336   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
337   FileModelNode *node = iter->user_data;
338   
339   switch (column)
340     {
341     case GTK_FILE_SYSTEM_MODEL_URI:
342       g_value_init (value, G_TYPE_STRING);
343       g_value_set_string (value, node->uri);
344       break;
345     case GTK_FILE_SYSTEM_MODEL_INFO:
346       g_value_init (value, GTK_TYPE_FILE_INFO);
347       g_value_set_boxed (value, file_model_node_get_info (model, node));
348       break;
349     case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
350       {
351         const GtkFileInfo *info = file_model_node_get_info (model, node);
352         g_value_init (value, G_TYPE_STRING);
353         g_value_set_string (value, gtk_file_info_get_display_name (info));
354       }
355       break;
356     default:
357       g_assert_not_reached ();
358     }
359 }
360
361 static gboolean
362 gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
363                                  GtkTreeIter  *iter)
364 {
365   FileModelNode *node = iter->user_data;
366
367   node = node->next;
368   while (node && !node->is_visible)
369     node = node->next;
370   
371   iter->user_data = node;
372
373   return node != NULL;
374 }
375
376 static gboolean
377 gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
378                                      GtkTreeIter  *iter,
379                                      GtkTreeIter  *parent)
380 {
381   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
382   FileModelNode *children;
383
384   if (parent)
385     {
386       FileModelNode *parent_node = parent->user_data;
387       children = file_model_node_get_children (model, parent_node);
388     }
389   else
390     {
391       children = model->roots;
392     }
393
394   while (children && !children->is_visible)
395     children = children->next;
396
397   iter->user_data = children;
398
399   return children != NULL;
400 }
401
402 static gboolean
403 gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
404                                       GtkTreeIter  *iter)
405 {
406   FileModelNode *node = iter->user_data;
407
408   /* We never want to go into a directory just to
409    * find out if it has children
410    */
411   if (node->loaded)
412     return node->has_children;
413   else
414     {
415       GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
416
417       if (node->depth == model->max_depth)
418         return FALSE;
419       else
420         {
421           const GtkFileInfo *info = file_model_node_get_info (model, node);
422           return gtk_file_info_get_is_folder (info);
423         }
424     }
425 }
426
427 static gint
428 gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
429                                        GtkTreeIter  *iter)
430 {
431   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
432   FileModelNode *children;
433   gint n = 0;
434
435   if (iter)
436     {
437       FileModelNode *node = iter->user_data;
438       children = file_model_node_get_children (model, node);
439     }
440   else
441     {
442       children = model->roots;
443     }
444
445   while (children)
446     {
447       if (children->is_visible)
448         n++;
449       children = children->next;
450     }
451
452   return n;
453 }
454
455 static gboolean
456 gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
457                                       GtkTreeIter  *iter,
458                                       GtkTreeIter  *parent,
459                                       gint          n)
460 {
461   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
462   FileModelNode *children;
463
464   if (parent)
465     {
466       FileModelNode *parent_node = parent->user_data;
467       children = file_model_node_get_children (model, parent_node);
468     }
469   else
470     {
471       children = model->roots;
472     }
473
474   while (children && !children->is_visible)
475     children = children->next;
476
477   while (n && children)
478     {
479       n--;
480       children = children->next;
481       while (children && !children->is_visible)
482         children = children->next;
483     }
484
485   iter->user_data = children;
486
487   return children != NULL;
488 }
489
490 static gboolean
491 gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
492                                    GtkTreeIter  *iter,
493                                    GtkTreeIter  *child)
494 {
495   FileModelNode *node = child->user_data;
496   
497   node = node->parent;
498   iter->user_data = node;
499
500   return node != NULL;
501 }
502
503 static void
504 gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
505                                 GtkTreeIter  *iter)
506 {
507   file_model_node_ref (iter->user_data);
508 }
509
510 static void
511 gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
512                                   GtkTreeIter  *iter)
513 {
514   file_model_node_unref (GTK_FILE_SYSTEM_MODEL (tree_model),
515                          iter->user_data);
516 }
517
518 /**
519  * _gtk_file_system_model_new:
520  * @file_system: an object implementing #GtkFileSystem
521  * @root_uri: the URI of root of the file system to display,
522  *            or %NULL to display starting from the
523  *            root or roots of the fielsystem.
524  * @max_depth: the maximum depth from the children of @root_uri
525  *             or the roots of the file system to display in
526  *             the file selector). A depth of 0 displays
527  *             only the immediate children of @root_uri,
528  *             or the roots of the filesystem. -1 for no
529  *             maximum depth.
530  * @types: a bitmask indicating the types of information
531  *         that is desired about the files. This will
532  *         determine what information is returned by
533  *         _gtk_file_system_model_get_info().
534  *
535  * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
536  * object wraps a #GtkFileSystem interface as a #GtkTreeModel.
537  * Using the @root_uri and @max_depth parameters, the tree model
538  * can be restricted to a subportion of the entire file system.
539  * 
540  * Return value: the newly created #GtkFileSystemModel object.
541  **/
542 GtkFileSystemModel *
543 _gtk_file_system_model_new (GtkFileSystem  *file_system,
544                             const gchar    *root_uri,
545                             gint            max_depth,
546                             GtkFileInfoType types)
547 {
548   GtkFileSystemModel *model;
549   GSList *roots, *tmp_list;
550
551   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
552
553   model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
554   model->file_system = g_object_ref (file_system);
555   if (max_depth < 0)
556     model->max_depth = G_MAXUSHORT;
557   else
558     model->max_depth = MIN (max_depth, G_MAXUSHORT);
559   model->types = types | GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN;
560
561   if (root_uri)
562     {
563       GSList *child_uris;
564       
565       model->root_folder = gtk_file_system_get_folder (file_system, root_uri,
566                                                        model->types,
567                                                        NULL);   /* NULL-GError */
568
569       if (model->root_folder &&
570           gtk_file_folder_list_children (model->root_folder,
571                                          &child_uris,
572                                          NULL)) /* NULL-GError */
573         roots = child_uris;
574     }
575   else
576     roots = gtk_file_system_list_roots (file_system);
577
578   roots = g_slist_sort (roots, (GCompareFunc)strcmp);
579   
580   for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
581     {
582       FileModelNode *node = file_model_node_new (tmp_list->data);
583       g_free (tmp_list->data);
584       node->is_visible = file_model_node_is_visible (model, node);
585       node->next = model->roots;
586       node->depth = 0;
587       model->roots = node;
588     }
589   g_slist_free (roots);
590
591   model->roots = (FileModelNode *)g_slist_reverse ((GSList *)model->roots);
592   
593   return model;
594 }
595
596 static void
597 model_refilter_recurse (GtkFileSystemModel *model,
598                         FileModelNode      *parent,
599                         GtkTreePath        *path)
600 {
601   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
602   int i = 0;
603   FileModelNode *nodes;
604   gboolean has_children = FALSE;
605
606   if (parent && !parent->loaded)
607     return;
608
609   if (parent)
610     nodes = parent->children;
611   else
612     nodes = model->roots;
613
614   while (nodes)
615     {
616       FileModelNode *next = nodes->next;
617       gboolean is_visible;
618       
619       gtk_tree_path_append_index (path, i);
620
621       is_visible = file_model_node_is_visible (model, nodes);
622       
623       if (!is_visible && nodes->is_visible)
624         {
625           file_model_node_clear (model, nodes);
626           gtk_tree_model_row_deleted (tree_model, path);
627
628           nodes->is_visible = FALSE;
629         }
630       else if (is_visible && !nodes->is_visible)
631         {
632           GtkTreeIter iter;
633
634           iter.user_data = nodes;
635           gtk_tree_model_row_inserted (tree_model, path, &iter);
636
637           nodes->is_visible = TRUE;
638         }
639       else
640         model_refilter_recurse (model, nodes, path);
641
642       if (is_visible)
643         {
644           has_children = TRUE;
645           i++;
646         }
647       
648       gtk_tree_path_up (path);
649       
650       nodes = next;
651     }
652
653   if (parent && (has_children != parent->has_children))
654     {
655       GtkTreeIter iter;
656
657       parent->has_children = has_children;
658
659       iter.user_data = parent;
660       gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
661     }
662 }
663
664 /**
665  * _gtk_file_system_model_set_show_hidden:
666  * @model: a #GtkFileSystemModel
667  * @show_hidden: whether hidden files should be displayed
668  * 
669  * Sets whether hidden files should be included in the #GtkTreeModel
670  * for display.
671  **/
672 void
673 _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
674                                         gboolean            show_hidden)
675 {
676   show_hidden = show_hidden != FALSE;
677
678   if (show_hidden != model->show_hidden)
679     {
680       GtkTreePath *path;
681
682       model->show_hidden = show_hidden;
683
684       path = gtk_tree_path_new ();
685       model_refilter_recurse (model, NULL, path);
686       gtk_tree_path_free (path);
687     }
688 }
689
690 /**
691  * _gtk_file_system_model_set_show_folders:
692  * @model: a #GtkFileSystemModel
693  * @show_folders: whether folders should be displayed
694  * 
695  * Sets whether folders should be included in the #GtkTreeModel for
696  * display.
697  **/
698 void
699 _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
700                                          gboolean            show_folders)
701 {
702   show_folders = show_folders != FALSE;
703
704   if (show_folders != model->show_folders)
705     {
706       GtkTreePath *path;
707
708       model->show_folders = show_folders;
709
710       path = gtk_tree_path_new ();
711       model_refilter_recurse (model, NULL, path);
712       gtk_tree_path_free (path);
713     }
714 }
715
716 /**
717  * _gtk_file_system_model_set_show_files:
718  * @model: a #GtkFileSystemModel
719  * @show_files: whether files (as opposed to folders) should
720  *              be displayed.
721  * 
722  * Sets whether files (as opposed to folders) should be included
723  * in the #GtkTreeModel for display.
724  **/
725 void
726 _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
727                                        gboolean            show_files)
728 {
729   show_files = show_files != FALSE;
730
731   if (show_files != model->show_files)
732     {
733       GtkTreePath *path;
734
735       model->show_files = show_files;
736
737       path = gtk_tree_path_new ();
738       model_refilter_recurse (model, NULL, path);
739       gtk_tree_path_free (path);
740     }
741 }
742
743 /**
744  * _gtk_file_system_model_get_info:
745  * @model: a #GtkFileSystemModel
746  * @iter: a #GtkTreeIter pointing to a row of @model
747  * 
748  * Gets the #GtkFileInfo structure for a particular row
749  * of @model. The information included in this structure
750  * is determined by the @types parameter to
751  * _gtk_file_system_model_new().
752  * 
753  * Return value: a #GtkFileInfo structure. This structure
754  *   is owned by @model and must not be modified or freed.
755  *   If you want to save the information for later use,
756  *   you must make a copy, since the structure may be
757  *   freed on later changes to the file system.
758  **/
759 const GtkFileInfo *
760 _gtk_file_system_model_get_info (GtkFileSystemModel *model,
761                                  GtkTreeIter        *iter)
762 {
763   return file_model_node_get_info (model, iter->user_data);
764 }
765
766 /**
767  * _gtk_file_system_model_get_uri:
768  * @model: a #GtkFileSystemModel
769  * @iter: a #GtkTreeIter pointing to a row of @model
770  * 
771  * Gets the URI for a particular row in @model. 
772  *
773  * Return value: the URI. This string is owned by @model and
774  *   or freed. If you want to save the URI for later use,
775  *   you must make a copy, since the string may be freed
776  *   on later changes to the file system.
777  **/
778 const gchar *
779 _gtk_file_system_model_get_uri (GtkFileSystemModel *model,
780                                 GtkTreeIter        *iter)
781 {
782   FileModelNode *node = iter->user_data;
783
784   return node->uri;
785 }
786
787 static void
788 unref_node_and_parents (GtkFileSystemModel *model,
789                         FileModelNode      *node)
790 {
791   file_model_node_unref (model, node);
792   if (node->parent)
793     file_model_node_unref (model, node->parent);
794 }
795
796 static FileModelNode *
797 find_and_ref_uri (GtkFileSystemModel *model,
798                   const gchar        *uri)
799 {
800   gchar *parent_uri;
801   FileModelNode *parent_node;
802   FileModelNode *children;
803   
804   if (!gtk_file_system_get_parent (model->file_system, uri, &parent_uri, NULL))
805     return NULL;
806
807   if (parent_uri)
808     {
809       parent_node = find_and_ref_uri (model, parent_uri);
810       g_free (parent_uri);
811
812       if (!parent_node)
813         return NULL;
814     }
815   else
816     parent_node = NULL;
817
818   if (parent_node)
819     children = file_model_node_get_children (model, parent_node);
820   else
821     children = model->roots;
822
823   while (children)
824     {
825       if (children->is_visible &&
826           strcmp (children->uri, uri) == 0)
827         {
828           file_model_node_ref (children);
829           return children;
830         }
831
832       children = children->next;
833     }
834
835   if (parent_node)
836     unref_node_and_parents (model, parent_node);
837
838   return FALSE;
839 }
840
841 /**
842  * _gtk_file_system_model_uri_do:
843  * @model: a #GtkFileSystemModel
844  * @uri: a URI pointing to a file in the filesystem
845  *       for @model.
846  * @func: Function to call with the path and iter corresponding
847  *        to @uri.
848  * @user_data: data to pass to @func
849  * 
850  * Locates @uri within @model, referencing
851  * (gtk_tree_model_ref_node ()) all parent nodes,
852  * calls @func passing in the path and iter for @uri,
853  * then unrefs all the parent nodes.
854  *
855  * The reason for doing this operation as a callback
856  * is so that if the operation performed with the the
857  * path and iter results in referencing the the node
858  * and/or parent nodes, we don't load all the information
859  * about the nodes.
860  *
861  * This function is particularly useful for expanding
862  * a #GtkTreeView to a particular point in the file system.
863  * 
864  * Return value: %TRUE if the URI was successfully
865  *  found in @model and @func was called.
866  **/
867 gboolean
868 _gtk_file_system_model_uri_do (GtkFileSystemModel       *model,
869                                const gchar              *uri,
870                                GtkFileSystemModelURIFunc func,
871                                gpointer                  user_data)
872 {
873   FileModelNode *node = find_and_ref_uri (model, uri);
874
875   if (node)
876     {
877       GtkTreeIter iter;
878       GtkTreePath *path;
879
880       iter.user_data = node;
881       path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
882
883       (*func) (model, path, &iter, user_data);
884
885       gtk_tree_path_free (path);
886       unref_node_and_parents (model, node);
887
888       return TRUE;
889     }
890   else
891     return FALSE;
892 }
893
894 static gboolean
895 dummy_idle_callback (GtkFileSystemModel *model)
896 {
897   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
898   GSList *tmp_list;
899
900   for (tmp_list = model->dummy_idle_nodes; tmp_list; tmp_list = tmp_list->next)
901     {
902       GtkTreeIter iter;
903       GtkTreePath *path;
904       
905       FileModelNode *node = tmp_list->data;
906       g_assert (node->children && !node->children->next && !node->children->children);
907
908       iter.user_data = node->children;
909       path = gtk_tree_model_get_path (tree_model, &iter);
910       
911       if (node->children->ref_count)
912         gtk_tree_model_row_deleted (tree_model, path);
913
914       gtk_tree_path_up (path);
915       iter.user_data = node;
916       gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
917
918       gtk_tree_path_free (path);
919
920       file_model_node_free (node->children);
921       node->children = NULL;
922       node->has_children = FALSE;
923       node->has_dummy = FALSE;
924     }
925
926   model->dummy_idle_nodes = FALSE;
927   model->dummy_idle = NULL;
928
929   return FALSE;
930 }
931
932 static void
933 queue_dummy_idle (GtkFileSystemModel *model,
934                   FileModelNode      *node)
935 {
936   model->dummy_idle_nodes = g_slist_prepend (model->dummy_idle_nodes, node);
937
938   if (!model->dummy_idle)
939     {
940       model->dummy_idle = g_idle_source_new ();
941       g_source_set_priority (model->dummy_idle, G_PRIORITY_HIGH_IDLE);
942       g_source_set_closure (model->dummy_idle,
943                             g_cclosure_new_object (G_CALLBACK (dummy_idle_callback), G_OBJECT (model)));
944       g_source_attach (model->dummy_idle, NULL);
945     }
946 }
947
948 static void
949 unqueue_dummy_idle (GtkFileSystemModel *model,
950                     FileModelNode      *node)
951 {
952   model->dummy_idle_nodes = g_slist_remove (model->dummy_idle_nodes, node);
953   
954   if (!model->dummy_idle_nodes)
955     g_source_destroy (model->dummy_idle);
956 }
957
958 static FileModelNode *
959 file_model_node_new (const gchar *uri)
960 {
961   FileModelNode *node = g_new0 (FileModelNode, 1);
962
963   node->uri = g_strdup (uri);
964
965   return node;
966 }
967
968 static void
969 file_model_node_free (FileModelNode *node)
970 {
971   if (node->children)
972     {
973       FileModelNode *children;
974       
975       for (children = node->children; children; children = children->next)
976         file_model_node_free (children);
977     }
978   
979   if (node->uri)
980     g_free (node->uri);
981
982   if (node->info)
983     gtk_file_info_free (node->info);
984
985   if (node->folder)
986     g_object_unref (node->folder);
987
988   g_free (node);
989 }
990
991 static const GtkFileInfo *
992 file_model_node_get_info (GtkFileSystemModel *model,
993                           FileModelNode      *node)
994 {
995   if (!node->info)
996     {
997       if (node->parent && node->parent->has_dummy)
998         {
999           node->info = gtk_file_info_new ();
1000           gtk_file_info_set_display_name (node->info, "Loading...");
1001         }
1002       else if (node->parent || model->root_folder)
1003         {
1004           node->info = gtk_file_folder_get_info (node->parent ? node->parent->folder : model->root_folder,
1005                                                  node->uri,
1006                                                  NULL); /* NULL-GError */
1007         }
1008       else
1009         {
1010           node->info = gtk_file_system_get_root_info (model->file_system,
1011                                                       node->uri,
1012                                                       model->types,
1013                                                       NULL);  /* NULL-GError */
1014         }
1015     }
1016
1017   return node->info;
1018 }
1019
1020 static gboolean
1021 file_model_node_is_visible (GtkFileSystemModel *model,
1022                             FileModelNode      *node)
1023 {
1024   if (model->show_hidden && model->show_folders && model->show_files)
1025     return TRUE;
1026   else
1027     {
1028       const GtkFileInfo *info = file_model_node_get_info (model, node);
1029       gboolean is_folder = gtk_file_info_get_is_folder (info);
1030
1031       if (!model->show_folders && is_folder)
1032         return FALSE;
1033       if (!model->show_files && !is_folder)
1034         return FALSE;
1035       if (!model->show_hidden && gtk_file_info_get_is_hidden (info))
1036         return FALSE;
1037
1038       return TRUE;
1039     }
1040 }
1041
1042 static void
1043 file_model_node_clear (GtkFileSystemModel *model,
1044                        FileModelNode      *node)
1045 {
1046   FileModelNode *children;
1047   
1048   if (node->has_dummy)
1049     unqueue_dummy_idle (model, node);
1050   
1051   if (node->folder)
1052     {
1053       g_object_unref (node->folder);
1054       node->folder = NULL;
1055     }
1056   
1057   children = node->children;
1058   node->children = NULL;
1059   node->has_children = FALSE;
1060   node->loaded = FALSE;
1061   
1062   while (children)
1063     {
1064       FileModelNode *next = children->next;
1065       
1066       file_model_node_clear (model, children);
1067       file_model_node_free (children);
1068       
1069       children = next;
1070     }
1071
1072   node->ref_count = 0;
1073 }
1074
1075 static void
1076 file_model_node_ref (FileModelNode *node)
1077 {
1078   node->ref_count++;
1079 }
1080
1081 static void
1082 file_model_node_unref (GtkFileSystemModel *model,
1083                        FileModelNode       *node)
1084 {
1085   node->ref_count--;
1086   if (node->ref_count == 0)
1087     file_model_node_clear (model, node);
1088 }
1089
1090 static FileModelNode *
1091 file_model_node_get_children (GtkFileSystemModel *model,
1092                               FileModelNode      *node)
1093 {
1094   if (node->ref_count == 0)
1095     return NULL;
1096
1097   if (!node->loaded)
1098     {
1099       const GtkFileInfo *info = file_model_node_get_info (model, node);
1100       gboolean has_children = FALSE;
1101       gboolean is_folder = node->depth < model->max_depth && gtk_file_info_get_is_folder (info);
1102       
1103       if (is_folder)
1104         node->folder = gtk_file_system_get_folder (model->file_system,
1105                                                    node->uri,
1106                                                    model->types,
1107                                                    NULL);       /* NULL-GError */
1108
1109       if (node->folder)
1110         {
1111           GSList *child_uris, *tmp_list;
1112           
1113           if (gtk_file_folder_list_children (node->folder, &child_uris, NULL)) /* NULL-GError */
1114             {
1115               child_uris = g_slist_sort (child_uris, (GCompareFunc)strcmp);
1116
1117               for (tmp_list = child_uris; tmp_list; tmp_list = tmp_list->next)
1118                 {
1119                   FileModelNode *child_node = file_model_node_new (tmp_list->data);
1120                   g_free (tmp_list->data);
1121                   child_node->next = node->children;
1122                   child_node->parent = node;
1123                   child_node->depth = node->depth + 1;
1124                   child_node->is_visible = file_model_node_is_visible (model, child_node);
1125                   if (child_node->is_visible)
1126                     has_children = TRUE;
1127                   node->children = child_node;
1128                 }
1129               g_slist_free (child_uris);
1130             }
1131
1132           node->children = (FileModelNode *)g_slist_reverse ((GSList *)node->children);
1133         }
1134       
1135       node->has_children = has_children;
1136
1137       if (is_folder && !node->has_children)
1138         {
1139           /* The hard case ... we claimed this folder had children, but actually
1140            * it didn't. We have to add a dummy child, then remove it later
1141            */
1142           FileModelNode *child_node = file_model_node_new ("***dummy***");
1143           child_node->is_visible = TRUE;
1144           child_node->parent = node;
1145
1146           node->children = child_node;
1147           node->has_children = TRUE;
1148           node->has_dummy = TRUE;
1149
1150           queue_dummy_idle (model, node);
1151         }
1152
1153       node->loaded = TRUE;
1154     }
1155
1156   return node->children;
1157 }