]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemmodel.c
Finish monitoring, add a TODO, fill in details in README, fix some missing finalizati...
[~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   gushort max_depth;
48   
49   guint show_hidden : 1;
50   guint show_folders : 1;
51   guint show_files : 1;
52   guint folders_only : 1;
53 };
54
55 struct _FileModelNode
56 {
57   gchar *uri;
58   FileModelNode *next;
59
60   GtkFileInfo *info;
61   GtkFileFolder *folder;
62   
63   FileModelNode *children;
64   FileModelNode *parent;
65   GtkFileSystemModel *model;
66
67   guint ref_count;
68
69   gushort depth;
70
71   guint has_dummy : 1;
72   guint is_dummy : 1;
73   guint is_visible : 1;
74   guint loaded : 1;
75 };
76
77 static void gtk_file_system_model_class_init   (GtkFileSystemModelClass *class);
78 static void gtk_file_system_model_iface_init   (GtkTreeModelIface       *iface);
79 static void gtk_file_system_model_init         (GtkFileSystemModel      *model);
80 static void gtk_file_system_model_finalize     (GObject                 *object);
81
82 static GtkTreeModelFlags gtk_file_system_model_get_flags       (GtkTreeModel *tree_model);
83 static gint              gtk_file_system_model_get_n_columns   (GtkTreeModel *tree_model);
84 static GType             gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
85                                                                 gint          index);
86 static gboolean          gtk_file_system_model_get_iter        (GtkTreeModel *tree_model,
87                                                                 GtkTreeIter  *iter,
88                                                                 GtkTreePath  *path);
89 static GtkTreePath *     gtk_file_system_model_get_path        (GtkTreeModel *tree_model,
90                                                                 GtkTreeIter  *iter);
91 static void              gtk_file_system_model_get_value       (GtkTreeModel *tree_model,
92                                                                 GtkTreeIter  *iter,
93                                                                 gint          column,
94                                                                 GValue       *value);
95 static gboolean          gtk_file_system_model_iter_next       (GtkTreeModel *tree_model,
96                                                                 GtkTreeIter  *iter);
97 static gboolean          gtk_file_system_model_iter_children   (GtkTreeModel *tree_model,
98                                                                 GtkTreeIter  *iter,
99                                                                 GtkTreeIter  *parent);
100 static gboolean          gtk_file_system_model_iter_has_child  (GtkTreeModel *tree_model,
101                                                                 GtkTreeIter  *iter);
102 static gint              gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
103                                                                 GtkTreeIter  *iter);
104 static gboolean          gtk_file_system_model_iter_nth_child  (GtkTreeModel *tree_model,
105                                                                 GtkTreeIter  *iter,
106                                                                 GtkTreeIter  *parent,
107                                                                 gint          n);
108 static gboolean          gtk_file_system_model_iter_parent     (GtkTreeModel *tree_model,
109                                                                 GtkTreeIter  *iter,
110                                                                 GtkTreeIter  *child);
111 static void              gtk_file_system_model_ref_node        (GtkTreeModel *tree_model,
112                                                                 GtkTreeIter  *iter);
113 static void              gtk_file_system_model_unref_node      (GtkTreeModel *tree_model,
114                                                                 GtkTreeIter  *iter);
115
116 static FileModelNode *file_model_node_new        (GtkFileSystemModel *model,
117                                                   const gchar        *uri);
118 static void           file_model_node_free       (FileModelNode      *node);
119 static void           file_model_node_ref        (FileModelNode      *node);
120 static void           file_model_node_unref      (GtkFileSystemModel *model,
121                                                   FileModelNode      *node);
122
123 static const GtkFileInfo *file_model_node_get_info     (GtkFileSystemModel *model,
124                                                         FileModelNode      *node);
125 static gboolean           file_model_node_is_visible   (GtkFileSystemModel *model,
126                                                         FileModelNode      *node);
127 static void               file_model_node_clear        (GtkFileSystemModel *model,
128                                                         FileModelNode      *node);
129 static FileModelNode *    file_model_node_get_children (GtkFileSystemModel *model,
130                                                         FileModelNode      *node);
131
132 static void deleted_callback       (GtkFileFolder *folder,
133                                     FileModelNode *node);
134 static void files_added_callback   (GtkFileFolder *folder,
135                                     GSList        *uris,
136                                     FileModelNode *node);
137 static void files_changed_callback (GtkFileFolder *folder,
138                                     GSList        *uris,
139                                     FileModelNode *node);
140 static void files_removed_callback (GtkFileFolder *folder,
141                                     GSList        *uris,
142                                     FileModelNode *node);
143
144 static void root_deleted_callback       (GtkFileFolder      *folder,
145                                          GtkFileSystemModel *model);
146 static void root_files_added_callback   (GtkFileFolder      *folder,
147                                          GSList             *uris,
148                                          GtkFileSystemModel *model);
149 static void root_files_changed_callback (GtkFileFolder      *folder,
150                                          GSList             *uris,
151                                          GtkFileSystemModel *model);
152 static void root_files_removed_callback (GtkFileFolder      *folder,
153                                          GSList             *uris,
154                                          GtkFileSystemModel *model);
155
156 GType
157 _gtk_file_system_model_get_type (void)
158 {
159   static GType file_system_model_type = 0;
160
161   if (!file_system_model_type)
162     {
163       static const GTypeInfo file_system_model_info =
164       {
165         sizeof (GtkFileSystemModelClass),
166         NULL,           /* base_init */
167         NULL,           /* base_finalize */
168         (GClassInitFunc) gtk_file_system_model_class_init,
169         NULL,           /* class_finalize */
170         NULL,           /* class_data */
171         sizeof (GtkFileSystemModel),
172         0,              /* n_preallocs */
173         (GInstanceInitFunc) gtk_file_system_model_init,
174       };
175       
176       static const GInterfaceInfo file_system_info =
177       {
178         (GInterfaceInitFunc) gtk_file_system_model_iface_init, /* interface_init */
179         NULL,                                                 /* interface_finalize */
180         NULL                                                  /* interface_data */
181       };
182
183       file_system_model_type = g_type_register_static (G_TYPE_OBJECT,
184                                                       "GtkFileSystemModel",
185                                                       &file_system_model_info, 0);
186       g_type_add_interface_static (file_system_model_type,
187                                    GTK_TYPE_TREE_MODEL,
188                                    &file_system_info);
189     }
190
191   return file_system_model_type;
192 }
193
194 static void
195 gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
196 {
197   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
198   
199   gobject_class->finalize = gtk_file_system_model_finalize;
200 }
201
202 static void
203 gtk_file_system_model_iface_init (GtkTreeModelIface *iface)
204 {
205   iface->get_flags =       gtk_file_system_model_get_flags;
206   iface->get_n_columns =   gtk_file_system_model_get_n_columns;
207   iface->get_column_type = gtk_file_system_model_get_column_type;
208   iface->get_iter =        gtk_file_system_model_get_iter;
209   iface->get_path =        gtk_file_system_model_get_path;
210   iface->get_value =       gtk_file_system_model_get_value;
211   iface->iter_next =       gtk_file_system_model_iter_next;
212   iface->iter_children =   gtk_file_system_model_iter_children;
213   iface->iter_has_child =  gtk_file_system_model_iter_has_child;
214   iface->iter_n_children = gtk_file_system_model_iter_n_children;
215   iface->iter_nth_child =  gtk_file_system_model_iter_nth_child;
216   iface->iter_parent =     gtk_file_system_model_iter_parent;
217   iface->ref_node =        gtk_file_system_model_ref_node;
218   iface->unref_node =      gtk_file_system_model_unref_node;
219 }
220
221 static void
222 gtk_file_system_model_init (GtkFileSystemModel *model)
223 {
224   model->show_files = TRUE;
225   model->show_folders = TRUE;
226   model->show_hidden = FALSE;
227 }
228
229 static void
230 gtk_file_system_model_finalize (GObject *object)
231 {
232   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
233   FileModelNode *children;
234
235   if (model->root_folder)
236     g_object_unref (model->root_folder);
237
238   children = model->roots;
239   while (children)
240     {
241       file_model_node_free (children);
242       children = children->next;
243     }
244 }
245
246 /*
247  * ******************** GtkTreeModel methods ********************
248  */
249
250 static GtkTreeModelFlags
251 gtk_file_system_model_get_flags (GtkTreeModel *tree_model)
252 {
253   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
254   GtkTreeModelFlags flags = GTK_TREE_MODEL_ITERS_PERSIST;
255
256   if (model->max_depth == 1)
257     flags |= GTK_TREE_MODEL_LIST_ONLY;
258
259   return flags;
260 }
261
262 static gint
263 gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model)
264 {
265   return GTK_FILE_SYSTEM_MODEL_N_COLUMNS;
266 }
267
268 static GType
269 gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
270                                        gint          index)
271 {
272   switch (index)
273     {
274     case GTK_FILE_SYSTEM_MODEL_URI:
275       return G_TYPE_STRING;
276     case GTK_FILE_SYSTEM_MODEL_INFO:
277       return GTK_TYPE_FILE_INFO; 
278     case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
279       return G_TYPE_STRING;
280    default:
281       g_assert_not_reached ();
282       return G_TYPE_NONE;
283     }
284 }
285
286 static gboolean
287 gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
288                                 GtkTreeIter  *iter,
289                                 GtkTreePath  *path)
290 {
291   GtkTreeIter parent;
292   gint *indices;
293   gint depth, i;
294
295   indices = gtk_tree_path_get_indices (path);
296   depth = gtk_tree_path_get_depth (path);
297
298   g_return_val_if_fail (depth > 0, FALSE);
299
300   if (!gtk_tree_model_iter_nth_child (tree_model, iter, NULL, indices[0]))
301     return FALSE;
302
303   for (i = 1; i < depth; i++)
304     {
305       parent = *iter;
306       if (!gtk_tree_model_iter_nth_child (tree_model, iter, &parent, indices[i]))
307         return FALSE;
308     }
309
310   return TRUE;
311 }
312
313 static GtkTreePath *
314 gtk_file_system_model_get_path (GtkTreeModel *tree_model,
315                                 GtkTreeIter  *iter)
316 {
317   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
318   FileModelNode *node = iter->user_data;
319
320   GtkTreePath *result = gtk_tree_path_new ();
321
322   while (node)
323     {
324       FileModelNode *parent = node->parent;
325       FileModelNode *children;
326       int n = 0;
327
328       if (parent)
329         children = parent->children;
330       else
331         children = model->roots;
332
333       while (children != node)
334         {
335           if (children->is_visible)
336             n++;
337           children = children->next;
338         }
339       
340       gtk_tree_path_prepend_index (result, n);
341
342       node = parent;
343     }
344
345   return result;
346 }
347
348 static void
349 gtk_file_system_model_get_value (GtkTreeModel *tree_model,
350                                  GtkTreeIter  *iter,
351                                  gint          column,
352                                  GValue       *value)
353 {
354   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
355   FileModelNode *node = iter->user_data;
356   
357   switch (column)
358     {
359     case GTK_FILE_SYSTEM_MODEL_URI:
360       g_value_init (value, G_TYPE_STRING);
361       g_value_set_string (value, node->uri);
362       break;
363     case GTK_FILE_SYSTEM_MODEL_INFO:
364       g_value_init (value, GTK_TYPE_FILE_INFO);
365       g_value_set_boxed (value, file_model_node_get_info (model, node));
366       break;
367     case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
368       {
369         const GtkFileInfo *info = file_model_node_get_info (model, node);
370         g_value_init (value, G_TYPE_STRING);
371         g_value_set_string (value, gtk_file_info_get_display_name (info));
372       }
373       break;
374     default:
375       g_assert_not_reached ();
376     }
377 }
378
379 static gboolean
380 gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
381                                  GtkTreeIter  *iter)
382 {
383   FileModelNode *node = iter->user_data;
384
385   node = node->next;
386   while (node && !node->is_visible)
387     node = node->next;
388   
389   iter->user_data = node;
390
391   return node != NULL;
392 }
393
394 static gboolean
395 gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
396                                      GtkTreeIter  *iter,
397                                      GtkTreeIter  *parent)
398 {
399   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
400   FileModelNode *children;
401
402   if (parent)
403     {
404       FileModelNode *parent_node = parent->user_data;
405       children = file_model_node_get_children (model, parent_node);
406     }
407   else
408     {
409       children = model->roots;
410     }
411
412   while (children && !children->is_visible)
413     children = children->next;
414
415   iter->user_data = children;
416
417   return children != NULL;
418 }
419
420 static gboolean
421 gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
422                                       GtkTreeIter  *iter)
423 {
424   FileModelNode *node = iter->user_data;
425   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
426
427   if (node->depth == model->max_depth)
428     return FALSE;
429   else
430     {
431       const GtkFileInfo *info = file_model_node_get_info (model, node);
432       return gtk_file_info_get_is_folder (info);
433     }
434 }
435
436 static gint
437 gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
438                                        GtkTreeIter  *iter)
439 {
440   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
441   FileModelNode *children;
442   gint n = 0;
443
444   if (iter)
445     {
446       FileModelNode *node = iter->user_data;
447       children = file_model_node_get_children (model, node);
448     }
449   else
450     {
451       children = model->roots;
452     }
453
454   while (children)
455     {
456       if (children->is_visible)
457         n++;
458       children = children->next;
459     }
460
461   return n;
462 }
463
464 static gboolean
465 gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
466                                       GtkTreeIter  *iter,
467                                       GtkTreeIter  *parent,
468                                       gint          n)
469 {
470   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
471   FileModelNode *children;
472
473   if (parent)
474     {
475       FileModelNode *parent_node = parent->user_data;
476       children = file_model_node_get_children (model, parent_node);
477     }
478   else
479     {
480       children = model->roots;
481     }
482
483   while (children && !children->is_visible)
484     children = children->next;
485
486   while (n && children)
487     {
488       n--;
489       children = children->next;
490       while (children && !children->is_visible)
491         children = children->next;
492     }
493
494   iter->user_data = children;
495
496   return children != NULL;
497 }
498
499 static gboolean
500 gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
501                                    GtkTreeIter  *iter,
502                                    GtkTreeIter  *child)
503 {
504   FileModelNode *node = child->user_data;
505   
506   node = node->parent;
507   iter->user_data = node;
508
509   return node != NULL;
510 }
511
512 static void
513 gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
514                                 GtkTreeIter  *iter)
515 {
516   file_model_node_ref (iter->user_data);
517 }
518
519 static void
520 gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
521                                   GtkTreeIter  *iter)
522 {
523   file_model_node_unref (GTK_FILE_SYSTEM_MODEL (tree_model),
524                          iter->user_data);
525 }
526
527 /**
528  * _gtk_file_system_model_new:
529  * @file_system: an object implementing #GtkFileSystem
530  * @root_uri: the URI of root of the file system to display,
531  *            or %NULL to display starting from the
532  *            root or roots of the fielsystem.
533  * @max_depth: the maximum depth from the children of @root_uri
534  *             or the roots of the file system to display in
535  *             the file selector). A depth of 0 displays
536  *             only the immediate children of @root_uri,
537  *             or the roots of the filesystem. -1 for no
538  *             maximum depth.
539  * @types: a bitmask indicating the types of information
540  *         that is desired about the files. This will
541  *         determine what information is returned by
542  *         _gtk_file_system_model_get_info().
543  *
544  * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
545  * object wraps a #GtkFileSystem interface as a #GtkTreeModel.
546  * Using the @root_uri and @max_depth parameters, the tree model
547  * can be restricted to a subportion of the entire file system.
548  * 
549  * Return value: the newly created #GtkFileSystemModel object.
550  **/
551 GtkFileSystemModel *
552 _gtk_file_system_model_new (GtkFileSystem  *file_system,
553                             const gchar    *root_uri,
554                             gint            max_depth,
555                             GtkFileInfoType types)
556 {
557   GtkFileSystemModel *model;
558   GSList *roots, *tmp_list;
559
560   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
561
562   model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
563   model->file_system = g_object_ref (file_system);
564   if (max_depth < 0)
565     model->max_depth = G_MAXUSHORT;
566   else
567     model->max_depth = MIN (max_depth, G_MAXUSHORT);
568   model->types = types | GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN;
569
570   if (root_uri)
571     {
572       GSList *child_uris;
573       
574       model->root_folder = gtk_file_system_get_folder (file_system, root_uri,
575                                                        model->types,
576                                                        NULL);   /* NULL-GError */
577
578       if (model->root_folder &&
579           gtk_file_folder_list_children (model->root_folder,
580                                          &child_uris,
581                                          NULL)) /* NULL-GError */
582         {
583           roots = child_uris;
584           
585           g_signal_connect (model->root_folder, "deleted",
586                             G_CALLBACK (root_deleted_callback), model);
587           g_signal_connect (model->root_folder, "files_added",
588                             G_CALLBACK (root_files_added_callback), model);
589           g_signal_connect (model->root_folder, "files_changed",
590                             G_CALLBACK (root_files_changed_callback), model);
591           g_signal_connect (model->root_folder, "files_removed",
592                             G_CALLBACK (root_files_removed_callback), model);
593         }
594     }
595   else
596     roots = gtk_file_system_list_roots (file_system);
597
598   roots = g_slist_sort (roots, (GCompareFunc)strcmp);
599   
600   for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
601     {
602       FileModelNode *node = file_model_node_new (model, tmp_list->data);
603       g_free (tmp_list->data);
604       node->is_visible = file_model_node_is_visible (model, node);
605       node->next = model->roots;
606       node->depth = 0;
607       model->roots = node;
608     }
609   g_slist_free (roots);
610
611   model->roots = (FileModelNode *)g_slist_reverse ((GSList *)model->roots);
612   
613   return model;
614 }
615
616 static void
617 model_refilter_recurse (GtkFileSystemModel *model,
618                         FileModelNode      *parent,
619                         GtkTreePath        *path)
620 {
621   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
622   int i = 0;
623   FileModelNode *nodes;
624   gboolean has_children = FALSE;
625
626   if (parent && !parent->loaded)
627     return;
628
629   if (parent)
630     nodes = parent->children;
631   else
632     nodes = model->roots;
633
634   while (nodes)
635     {
636       FileModelNode *next = nodes->next;
637       gboolean is_visible;
638       
639       gtk_tree_path_append_index (path, i);
640
641       is_visible = file_model_node_is_visible (model, nodes);
642       
643       if (!is_visible && nodes->is_visible)
644         {
645           file_model_node_clear (model, nodes);
646           gtk_tree_model_row_deleted (tree_model, path);
647
648           nodes->is_visible = FALSE;
649         }
650       else if (is_visible && !nodes->is_visible)
651         {
652           GtkTreeIter iter;
653
654           iter.user_data = nodes;
655           gtk_tree_model_row_inserted (tree_model, path, &iter);
656
657           nodes->is_visible = TRUE;
658         }
659       else
660         model_refilter_recurse (model, nodes, path);
661
662       if (is_visible)
663         {
664           has_children = TRUE;
665           i++;
666         }
667       
668       gtk_tree_path_up (path);
669       
670       nodes = next;
671     }
672
673   if (parent && !has_children)
674     {
675       /* Fixme - need to insert dummy node here */
676     }
677 }
678
679 /**
680  * _gtk_file_system_model_set_show_hidden:
681  * @model: a #GtkFileSystemModel
682  * @show_hidden: whether hidden files should be displayed
683  * 
684  * Sets whether hidden files should be included in the #GtkTreeModel
685  * for display.
686  **/
687 void
688 _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
689                                         gboolean            show_hidden)
690 {
691   show_hidden = show_hidden != FALSE;
692
693   if (show_hidden != model->show_hidden)
694     {
695       GtkTreePath *path;
696
697       model->show_hidden = show_hidden;
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_folders:
707  * @model: a #GtkFileSystemModel
708  * @show_folders: whether folders should be displayed
709  * 
710  * Sets whether folders should be included in the #GtkTreeModel for
711  * display.
712  **/
713 void
714 _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
715                                          gboolean            show_folders)
716 {
717   show_folders = show_folders != FALSE;
718
719   if (show_folders != model->show_folders)
720     {
721       GtkTreePath *path;
722
723       model->show_folders = show_folders;
724
725       path = gtk_tree_path_new ();
726       model_refilter_recurse (model, NULL, path);
727       gtk_tree_path_free (path);
728     }
729 }
730
731 /**
732  * _gtk_file_system_model_set_show_files:
733  * @model: a #GtkFileSystemModel
734  * @show_files: whether files (as opposed to folders) should
735  *              be displayed.
736  * 
737  * Sets whether files (as opposed to folders) should be included
738  * in the #GtkTreeModel for display.
739  **/
740 void
741 _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
742                                        gboolean            show_files)
743 {
744   show_files = show_files != FALSE;
745
746   if (show_files != model->show_files)
747     {
748       GtkTreePath *path;
749
750       model->show_files = show_files;
751
752       path = gtk_tree_path_new ();
753       model_refilter_recurse (model, NULL, path);
754       gtk_tree_path_free (path);
755     }
756 }
757
758 /**
759  * _gtk_file_system_model_get_info:
760  * @model: a #GtkFileSystemModel
761  * @iter: a #GtkTreeIter pointing to a row of @model
762  * 
763  * Gets the #GtkFileInfo structure for a particular row
764  * of @model. The information included in this structure
765  * is determined by the @types parameter to
766  * _gtk_file_system_model_new().
767  * 
768  * Return value: a #GtkFileInfo structure. This structure
769  *   is owned by @model and must not be modified or freed.
770  *   If you want to save the information for later use,
771  *   you must make a copy, since the structure may be
772  *   freed on later changes to the file system.
773  **/
774 const GtkFileInfo *
775 _gtk_file_system_model_get_info (GtkFileSystemModel *model,
776                                  GtkTreeIter        *iter)
777 {
778   return file_model_node_get_info (model, iter->user_data);
779 }
780
781 /**
782  * _gtk_file_system_model_get_uri:
783  * @model: a #GtkFileSystemModel
784  * @iter: a #GtkTreeIter pointing to a row of @model
785  * 
786  * Gets the URI for a particular row in @model. 
787  *
788  * Return value: the URI. This string is owned by @model and
789  *   or freed. If you want to save the URI for later use,
790  *   you must make a copy, since the string may be freed
791  *   on later changes to the file system.
792  **/
793 const gchar *
794 _gtk_file_system_model_get_uri (GtkFileSystemModel *model,
795                                 GtkTreeIter        *iter)
796 {
797   FileModelNode *node = iter->user_data;
798
799   return node->uri;
800 }
801
802 static void
803 unref_node_and_parents (GtkFileSystemModel *model,
804                         FileModelNode      *node)
805 {
806   file_model_node_unref (model, node);
807   if (node->parent)
808     file_model_node_unref (model, node->parent);
809 }
810
811 static FileModelNode *
812 find_child_node (GtkFileSystemModel *model,
813                  FileModelNode      *parent_node,
814                  const gchar        *uri)
815 {
816   FileModelNode *children;
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         return children;
828
829       children = children->next;
830     }
831
832   return NULL;
833 }
834                  
835
836 static FileModelNode *
837 find_and_ref_uri (GtkFileSystemModel *model,
838                   const gchar        *uri)
839 {
840   gchar *parent_uri;
841   FileModelNode *parent_node;
842   FileModelNode *child_node;
843   GtkFileFolder *folder;
844
845   if (!gtk_file_system_get_parent (model->file_system, uri, &parent_uri, NULL))
846     return NULL;
847
848   if (parent_uri)
849     {
850       parent_node = find_and_ref_uri (model, parent_uri);
851       g_free (parent_uri);
852
853       if (!parent_node)
854         return NULL;
855     }
856   else
857     parent_node = NULL;
858
859   child_node = find_child_node (model, parent_node, uri);
860   if (child_node)
861     {
862       file_model_node_ref (child_node);
863       return child_node;
864     }
865
866   folder = gtk_file_system_get_folder (model->file_system,
867                                        uri,
868                                        model->types,
869                                        NULL);   /* NULL-GError */
870
871   child_node = find_child_node (model, parent_node, uri);
872   if (child_node)
873     {
874       file_model_node_ref (child_node);
875       return child_node;
876     }
877
878   if (parent_node)
879     unref_node_and_parents (model, parent_node);
880
881   return FALSE;
882 }
883
884 /**
885  * _gtk_file_system_model_uri_do:
886  * @model: a #GtkFileSystemModel
887  * @uri: a URI pointing to a file in the filesystem
888  *       for @model.
889  * @func: Function to call with the path and iter corresponding
890  *        to @uri.
891  * @user_data: data to pass to @func
892  * 
893  * Locates @uri within @model, referencing
894  * (gtk_tree_model_ref_node ()) all parent nodes,
895  * calls @func passing in the path and iter for @uri,
896  * then unrefs all the parent nodes.
897  *
898  * The reason for doing this operation as a callback
899  * is so that if the operation performed with the the
900  * path and iter results in referencing the the node
901  * and/or parent nodes, we don't load all the information
902  * about the nodes.
903  *
904  * This function is particularly useful for expanding
905  * a #GtkTreeView to a particular point in the file system.
906  * 
907  * Return value: %TRUE if the URI was successfully
908  *  found in @model and @func was called.
909  **/
910 gboolean
911 _gtk_file_system_model_uri_do (GtkFileSystemModel       *model,
912                                const gchar              *uri,
913                                GtkFileSystemModelURIFunc func,
914                                gpointer                  user_data)
915 {
916   FileModelNode *node = find_and_ref_uri (model, uri);
917
918   if (node)
919     {
920       GtkTreeIter iter;
921       GtkTreePath *path;
922
923       iter.user_data = node;
924       path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
925
926       (*func) (model, path, &iter, user_data);
927
928       gtk_tree_path_free (path);
929       unref_node_and_parents (model, node);
930
931       return TRUE;
932     }
933   else
934     return FALSE;
935 }
936
937 static FileModelNode *
938 file_model_node_new (GtkFileSystemModel *model,
939                      const gchar        *uri)
940 {
941   FileModelNode *node = g_new0 (FileModelNode, 1);
942
943   node->model = model;
944   node->uri = g_strdup (uri);
945
946   return node;
947 }
948
949 static void
950 file_model_node_free (FileModelNode *node)
951 {
952   if (node->children)
953     {
954       FileModelNode *children;
955       
956       for (children = node->children; children; children = children->next)
957         file_model_node_free (children);
958     }
959   
960   if (node->uri)
961     g_free (node->uri);
962
963   if (node->info)
964     gtk_file_info_free (node->info);
965
966   if (node->folder)
967     g_object_unref (node->folder);
968
969   g_free (node);
970 }
971
972 static const GtkFileInfo *
973 file_model_node_get_info (GtkFileSystemModel *model,
974                           FileModelNode      *node)
975 {
976   if (!node->info)
977     {
978       if (node->is_dummy)
979         {
980           node->info = gtk_file_info_new ();
981           gtk_file_info_set_display_name (node->info, "(Empty)");
982         }
983       else if (node->parent || model->root_folder)
984         {
985           node->info = gtk_file_folder_get_info (node->parent ? node->parent->folder : model->root_folder,
986                                                  node->uri,
987                                                  NULL); /* NULL-GError */
988         }
989       else
990         {
991           node->info = gtk_file_system_get_root_info (model->file_system,
992                                                       node->uri,
993                                                       model->types,
994                                                       NULL);  /* NULL-GError */
995         }
996     }
997
998   return node->info;
999 }
1000
1001 static gboolean
1002 file_model_node_is_visible (GtkFileSystemModel *model,
1003                             FileModelNode      *node)
1004 {
1005   if (model->show_hidden && model->show_folders && model->show_files)
1006     return TRUE;
1007   else
1008     {
1009       const GtkFileInfo *info = file_model_node_get_info (model, node);
1010       gboolean is_folder = gtk_file_info_get_is_folder (info);
1011
1012       if (!model->show_folders && is_folder)
1013         return FALSE;
1014       if (!model->show_files && !is_folder)
1015         return FALSE;
1016       if (!model->show_hidden && gtk_file_info_get_is_hidden (info))
1017         return FALSE;
1018
1019       return TRUE;
1020     }
1021 }
1022
1023 static void
1024 file_model_node_clear (GtkFileSystemModel *model,
1025                        FileModelNode      *node)
1026 {
1027   FileModelNode *children;
1028   
1029   if (node->folder)
1030     {
1031       g_object_unref (node->folder);
1032       node->folder = NULL;
1033     }
1034   
1035   children = node->children;
1036   node->children = NULL;
1037   node->loaded = FALSE;
1038   
1039   while (children)
1040     {
1041       FileModelNode *next = children->next;
1042       
1043       file_model_node_clear (model, children);
1044       file_model_node_free (children);
1045       
1046       children = next;
1047     }
1048
1049   node->ref_count = 0;
1050 }
1051
1052 static void
1053 file_model_node_ref (FileModelNode *node)
1054 {
1055   node->ref_count++;
1056 }
1057
1058 static void
1059 file_model_node_unref (GtkFileSystemModel *model,
1060                        FileModelNode       *node)
1061 {
1062   node->ref_count--;
1063   if (node->ref_count == 0)
1064     file_model_node_clear (model, node);
1065 }
1066
1067 static FileModelNode *
1068 file_model_node_get_children (GtkFileSystemModel *model,
1069                               FileModelNode      *node)
1070 {
1071   if (node->ref_count == 0)
1072     return NULL;
1073
1074   if (!node->loaded)
1075     {
1076       const GtkFileInfo *info = file_model_node_get_info (model, node);
1077       gboolean has_children = FALSE;
1078       gboolean is_folder = node->depth < model->max_depth && gtk_file_info_get_is_folder (info);
1079
1080       if (is_folder)
1081         node->folder = gtk_file_system_get_folder (model->file_system,
1082                                                    node->uri,
1083                                                    model->types,
1084                                                    NULL);       /* NULL-GError */
1085
1086       if (node->folder)
1087         {
1088           GSList *child_uris, *tmp_list;
1089           
1090           if (gtk_file_folder_list_children (node->folder, &child_uris, NULL)) /* NULL-GError */
1091             {
1092               child_uris = g_slist_sort (child_uris, (GCompareFunc)strcmp);
1093
1094               for (tmp_list = child_uris; tmp_list; tmp_list = tmp_list->next)
1095                 {
1096                   FileModelNode *child_node = file_model_node_new (model, tmp_list->data);
1097                   g_free (tmp_list->data);
1098                   child_node->next = node->children;
1099                   child_node->parent = node;
1100                   child_node->depth = node->depth + 1;
1101                   child_node->is_visible = file_model_node_is_visible (model, child_node);
1102                   if (child_node->is_visible)
1103                     has_children = TRUE;
1104                   node->children = child_node;
1105                 }
1106               g_slist_free (child_uris);
1107             }
1108
1109           node->children = (FileModelNode *)g_slist_reverse ((GSList *)node->children);
1110
1111           g_signal_connect (node->folder, "deleted",
1112                             G_CALLBACK (deleted_callback), node);
1113           g_signal_connect (node->folder, "files_added",
1114                             G_CALLBACK (files_added_callback), node);
1115           g_signal_connect (node->folder, "files_changed",
1116                             G_CALLBACK (files_changed_callback), node);
1117           g_signal_connect (node->folder, "files_removed",
1118                             G_CALLBACK (files_removed_callback), node);
1119
1120           g_object_set_data (G_OBJECT (node->folder), "model-node", node);
1121         }
1122       
1123       if (is_folder && !has_children)
1124         {
1125           /* The hard case ... we claimed this folder had children, but actually
1126            * it didn't. We have to add a dummy child, possibly to remove later.
1127            */
1128           FileModelNode *child_node = file_model_node_new (model, "***dummy***");
1129           child_node->is_visible = TRUE;
1130           child_node->parent = node;
1131           child_node->is_dummy = TRUE;
1132
1133           node->children = child_node;
1134           node->has_dummy = TRUE;
1135         }
1136
1137       node->loaded = TRUE;
1138     }
1139
1140   return node->children;
1141 }
1142
1143 static void
1144 do_files_added (GtkFileSystemModel *model,
1145                 FileModelNode      *parent_node,
1146                 GSList             *uris)
1147 {
1148   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1149   FileModelNode *children;
1150   FileModelNode *prev = NULL;
1151   GtkTreeIter iter;
1152   GtkTreePath *path;
1153   GSList *sorted_uris;
1154   GSList *tmp_list;
1155
1156   sorted_uris = g_slist_copy (uris);
1157   sorted_uris = g_slist_sort (sorted_uris, (GCompareFunc)strcmp);
1158   
1159   if (parent_node)
1160     {
1161       iter.user_data = parent_node;
1162       path = gtk_tree_model_get_path (tree_model, &iter);
1163       children = parent_node->children;
1164     }
1165   else
1166     {
1167       path = gtk_tree_path_new ();
1168       children = model->roots;
1169     }
1170
1171   gtk_tree_path_down (path);
1172   
1173   if (parent_node && parent_node->has_dummy)
1174     {
1175       prev = children;
1176       children = children->next;
1177       gtk_tree_path_next (path);
1178     }
1179
1180   for (tmp_list = sorted_uris; tmp_list; tmp_list = tmp_list->next)
1181     {
1182       const gchar *uri = tmp_list->data;
1183       
1184       while (children && strcmp (children->uri, uri) < 0)
1185         {
1186           prev = children;
1187           if (children->is_visible)
1188             gtk_tree_path_next (path);
1189           
1190           children = children->next;
1191         }
1192   
1193       if (children && strcmp (children->uri, uri) == 0)
1194         {
1195           /* Shouldn't happen */
1196         }
1197       else
1198         {
1199           FileModelNode *new;
1200           
1201           new = file_model_node_new (model, uri);
1202           
1203           if (children)
1204             new->next = children;
1205           if (prev)
1206             prev->next = new;
1207           else if (parent_node)
1208             parent_node->children = new;
1209           else
1210             model->roots = new;
1211
1212           prev = new;
1213           
1214           if (parent_node)
1215             {
1216               new->parent = parent_node;
1217               new->depth = parent_node->depth + 1;
1218             }
1219           
1220           new->is_visible = file_model_node_is_visible (model, new);
1221           
1222           if (new->is_visible)
1223             {
1224               iter.user_data = new;
1225               path = gtk_tree_model_get_path (tree_model, &iter);
1226               gtk_tree_model_row_inserted (tree_model, path, &iter);
1227               
1228               if (gtk_file_system_model_iter_has_child (tree_model, &iter))
1229                 gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
1230               
1231               if (parent_node && parent_node->has_dummy)
1232                 {
1233                   FileModelNode *dummy = parent_node->children;
1234                   GtkTreePath *dummy_path;
1235                   
1236                   parent_node->children = parent_node->children->next;
1237                   parent_node->has_dummy = FALSE;
1238                   file_model_node_free (dummy);
1239
1240                   dummy_path = gtk_tree_path_copy (path);
1241                   gtk_tree_path_up (dummy_path);
1242                   gtk_tree_path_down (dummy_path);
1243                   
1244                   gtk_tree_model_row_deleted (tree_model, dummy_path);
1245                   gtk_tree_path_free (dummy_path);
1246                 }
1247               
1248               gtk_tree_path_next (path);
1249             }
1250         }
1251     }
1252
1253   gtk_tree_path_free (path);
1254   g_slist_free (sorted_uris);
1255 }
1256
1257 static void
1258 do_files_changed (GtkFileSystemModel *model,
1259                   FileModelNode      *parent_node,
1260                   GSList             *uris)
1261 {
1262   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1263   FileModelNode *children;
1264   FileModelNode *prev = NULL;
1265   GtkTreeIter iter;
1266   GtkTreePath *path;
1267   GSList *sorted_uris;
1268   GSList *tmp_list;
1269
1270   sorted_uris = g_slist_copy (uris);
1271   sorted_uris = g_slist_sort (sorted_uris, (GCompareFunc)strcmp);
1272   
1273   if (parent_node)
1274     {
1275       iter.user_data = parent_node;
1276       path = gtk_tree_model_get_path (tree_model, &iter);
1277       children = parent_node->children;
1278     }
1279   else
1280     {
1281       path = gtk_tree_path_new ();
1282       children = model->roots;
1283     }
1284
1285   gtk_tree_path_down (path);
1286   
1287   if (parent_node && parent_node->has_dummy)
1288     {
1289       prev = children;
1290       children = children->next;
1291       gtk_tree_path_next (path);
1292     }
1293
1294   for (tmp_list = sorted_uris; tmp_list; tmp_list = tmp_list->next)
1295     {
1296       const gchar *uri = tmp_list->data;
1297       
1298       while (children && strcmp (children->uri, uri) < 0)
1299         {
1300           prev = children;
1301           if (children->is_visible)
1302             gtk_tree_path_next (path);
1303           
1304           children = children->next;
1305         }
1306   
1307       if (children && strcmp (children->uri, uri) == 0)
1308         {
1309           gtk_tree_model_row_changed (tree_model, path, &iter);
1310         }
1311       else
1312         {
1313           /* Shouldn't happen */
1314         }
1315     }
1316
1317   gtk_tree_path_free (path);
1318   g_slist_free (sorted_uris);
1319 }
1320
1321 static void
1322 do_files_removed (GtkFileSystemModel *model,
1323                   FileModelNode      *parent_node,
1324                   GSList             *uris)
1325 {
1326   GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1327   FileModelNode *children;
1328   FileModelNode *prev = NULL;
1329   GtkTreeIter iter;
1330   GtkTreePath *path;
1331   GSList *sorted_uris;
1332   GSList *tmp_list;
1333   FileModelNode *tmp_child;
1334   gint n_visible;
1335
1336   sorted_uris = g_slist_copy (uris);
1337   sorted_uris = g_slist_sort (sorted_uris, (GCompareFunc)strcmp);
1338   
1339   if (parent_node)
1340     {
1341       iter.user_data = parent_node;
1342       path = gtk_tree_model_get_path (tree_model, &iter);
1343       children = parent_node->children;
1344     }
1345   else
1346     {
1347       path = gtk_tree_path_new ();
1348       children = model->roots;
1349     }
1350
1351   /* Count the number of currently visible children, so that
1352    * can catch when we need to insert a dummy node.
1353    */
1354   n_visible = 0;
1355   for (tmp_child = children; tmp_child; tmp_child = tmp_child->next)
1356     {
1357       if (tmp_child->is_visible)
1358         n_visible++;
1359     }
1360
1361   gtk_tree_path_down (path);
1362   
1363   if (parent_node && parent_node->has_dummy)
1364     {
1365       prev = children;
1366       children = children->next;
1367       gtk_tree_path_next (path);
1368     }
1369
1370   for (tmp_list = sorted_uris; tmp_list; tmp_list = tmp_list->next)
1371     {
1372       const gchar *uri = tmp_list->data;
1373       
1374       while (children && strcmp (children->uri, uri) < 0)
1375         {
1376           prev = children;
1377           if (children->is_visible)
1378             gtk_tree_path_next (path);
1379           
1380           children = children->next;
1381         }
1382   
1383       if (children && strcmp (children->uri, uri) == 0)
1384         {
1385           FileModelNode *next = children->next;
1386
1387           if (children->is_visible)
1388             n_visible--;
1389           
1390           if (n_visible == 0)
1391             {
1392               FileModelNode *dummy = file_model_node_new (model, "***dummy***");
1393               dummy->is_visible = TRUE;
1394               dummy->parent = parent_node;
1395               dummy->is_dummy = TRUE;
1396               
1397               parent_node->children = dummy;
1398               parent_node->has_dummy = TRUE;
1399
1400               iter.user_data = dummy;
1401               gtk_tree_model_row_inserted (tree_model, path, &iter);
1402               gtk_tree_path_next (path);
1403
1404               prev = dummy;
1405             }
1406           
1407           if (prev)
1408             prev->next = next;
1409           else if (parent_node)
1410             parent_node->children = next;
1411           else
1412             model->roots = next;
1413
1414           if (children->is_visible)
1415             gtk_tree_model_row_deleted (tree_model, path);
1416
1417           file_model_node_free (children);
1418
1419           children = next;
1420         }
1421       else
1422         {
1423           /* Shouldn't happen */
1424         }
1425     }
1426
1427   gtk_tree_path_free (path);
1428   g_slist_free (sorted_uris);
1429 }
1430
1431 static void
1432 deleted_callback (GtkFileFolder      *folder,
1433                   FileModelNode      *node)
1434 {
1435 }
1436
1437 static void
1438 files_added_callback (GtkFileFolder      *folder,
1439                       GSList             *uris,
1440                       FileModelNode      *node)
1441 {
1442   do_files_added (node->model, node, uris);
1443 }
1444
1445 static void
1446 files_changed_callback (GtkFileFolder      *folder,
1447                         GSList             *uris,
1448                         FileModelNode      *node)
1449 {
1450   do_files_changed (node->model, node, uris);
1451 }
1452
1453 static void
1454 files_removed_callback (GtkFileFolder      *folder,
1455                         GSList             *uris,
1456                         FileModelNode      *node)
1457 {
1458   do_files_removed (node->model, node, uris);
1459 }
1460
1461 static void
1462 root_deleted_callback (GtkFileFolder      *folder,
1463                        GtkFileSystemModel *model)
1464 {
1465 }
1466
1467 static void
1468 root_files_added_callback (GtkFileFolder      *folder,
1469                            GSList             *uris,
1470                            GtkFileSystemModel *model)
1471 {
1472   do_files_added (model, NULL, uris);
1473 }
1474
1475 static void
1476 root_files_changed_callback (GtkFileFolder      *folder,
1477                              GSList             *uris,
1478                              GtkFileSystemModel *model)
1479 {
1480   do_files_changed (model, NULL, uris);
1481 }
1482
1483 static void
1484 root_files_removed_callback (GtkFileFolder      *folder,
1485                              GSList             *uris,
1486                              GtkFileSystemModel *model)
1487 {
1488   do_files_removed (model, NULL, uris);
1489 }
1490
1491