]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemmodel.c
07f3685bf04a38ab8a6ccae7d5055d1b60ba0609
[~andy/gtk] / gtk / gtkfilesystemmodel.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilesystemmodel.c: GtkTreeModel wrapping a GtkFileSystem
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include "gtkfilesystemmodel.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "gtkfilesystem.h"
29 #include "gtkintl.h"
30 #include "gtkmarshalers.h"
31 #include "gtktreedatalist.h"
32 #include "gtktreednd.h"
33 #include "gtktreemodel.h"
34 #include "gtkalias.h"
35
36 /*** DEFINES ***/
37
38 /* priority used for all async callbacks in the main loop
39  * This should be higher than redraw priorities so multiple callbacks
40  * firing can be handled without intermediate redraws */
41 #define IO_PRIORITY G_PRIORITY_DEFAULT
42
43 /* random number that everyone else seems to use, too */
44 #define FILES_PER_QUERY 100
45
46 typedef struct _FileModelNode           FileModelNode;
47 typedef struct _GtkFileSystemModelClass GtkFileSystemModelClass;
48
49 struct _FileModelNode
50 {
51   GFile *               file;           /* file represented by this node or NULL for editable */
52   GFileInfo *           info;           /* info for this file or NULL if unknown */
53
54   guint                 index;          /* if valid, index in path - aka visible nodes before this one */
55
56   guint                 visible :1;     /* if the file is currently visible */
57   guint                 frozen_add :1;  /* true if the model was frozen and the entry has not been added yet */
58
59   GValue                values[1];      /* actually n_columns values */
60 };
61
62 struct _GtkFileSystemModel
63 {
64   GObject               parent_instance;
65
66   GFile *               dir;            /* directory that's displayed */
67   guint                 dir_thaw_source;/* GSource id for unfreezing the model */
68   char *                attributes;     /* attributes the file info must contain, or NULL for all attributes */
69   GFileMonitor *        dir_monitor;    /* directory that is monitored, or NULL if monitoring was not supported */
70
71   GCancellable *        cancellable;    /* cancellable in use for all operations - cancelled on dispose */
72   GArray *              files;          /* array of FileModelNode containing all our files */
73   GSize                 node_size;      /* Size of a FileModelNode structure once its ->values field has n_columns */
74   guint                 n_indexes_valid;/* count of valid indexes */
75   GHashTable *          file_lookup;    /* file => array index table */
76
77   guint                 n_columns;      /* number of columns */
78   GType *               column_types;   /* types of each column */
79   GtkFileSystemModelGetValue get_func;  /* function to call to fill in values in columns */
80   gpointer              get_data;       /* data to pass to get_func */
81
82   GtkFileFilter *       filter;         /* filter to use for deciding which nodes are visible */
83
84   int                   sort_column_id; /* current sorting column */
85   GtkSortType           sort_order;     /* current sorting order */
86   GList *               sort_list;      /* list of sorting functions */
87   GtkTreeIterCompareFunc default_sort_func; /* default sort function */
88   gpointer              default_sort_data; /* data to pass to default sort func */
89   GDestroyNotify        default_sort_destroy; /* function to call to destroy default_sort_data */
90
91   guint                 frozen;         /* number of times we're frozen */
92
93   gboolean              filter_on_thaw :1;/* set when filtering needs to happen upon thawing */
94   gboolean              sort_on_thaw :1;/* set when sorting needs to happen upon thawing */
95
96   guint                 show_hidden :1; /* whether to show hidden files */
97   guint                 show_folders :1;/* whether to show folders */
98   guint                 show_files :1;  /* whether to show files */
99 };
100
101 #define GTK_FILE_SYSTEM_MODEL_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
102 #define GTK_IS_FILE_SYSTEM_MODEL_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_MODEL))
103 #define GTK_FILE_SYSTEM_MODEL_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
104
105 struct _GtkFileSystemModelClass
106 {
107   GObjectClass parent_class;
108
109   /* Signals */
110
111   void (*finished_loading) (GtkFileSystemModel *model, GError *error);
112 };
113
114 static void add_file (GtkFileSystemModel *model,
115                       GFile              *file,
116                       GFileInfo          *info);
117 static void remove_file (GtkFileSystemModel *model,
118                          GFile              *file);
119
120 /* iter setup:
121  * @user_data: the model
122  * @user_data2: GUINT_TO_POINTER of array index of current entry
123  *
124  * All other fields are unused. Note that the array index does not corrspond
125  * 1:1 with the path index as entries might not be visible.
126  */
127 #define ITER_INDEX(iter) GPOINTER_TO_UINT((iter)->user_data2)
128 #define ITER_IS_VALID(model, iter) ((model) == (iter)->user_data)
129 #define ITER_INIT_FROM_INDEX(model, _iter, _index) G_STMT_START {\
130   g_assert (_index < (model)->files->len); \
131   (_iter)->user_data = (model); \
132   (_iter)->user_data2 = GUINT_TO_POINTER (_index); \
133 }G_STMT_END
134
135 /*** FileModelNode ***/
136
137 #define get_node(_model, _index) ((FileModelNode *) ((_model)->files->data + (_index) * (_model)->node_size))
138 #define node_index(_model, _node) (((gchar *) (_node) - (_model)->files->data) / (_model)->node_size)
139
140 static void
141 node_validate_indexes (GtkFileSystemModel *model, guint min_index, guint min_visible)
142 {
143   guint validate, current;
144
145   if (model->files->len == 0)
146     return;
147   min_index = MIN (min_index, model->files->len - 1);
148   validate = model->n_indexes_valid;
149   if (validate)
150     current = get_node (model, validate - 1)->index;
151   else
152     current = 0;
153   while (validate <= min_index && current <= min_visible)
154     {
155       FileModelNode *node = get_node (model, validate);
156       if (node->visible)
157         current++;
158       node->index = current;
159       validate++;
160     }
161   model->n_indexes_valid = validate;
162 }
163
164 static guint
165 node_get_index (GtkFileSystemModel *model, guint id)
166 {
167   if (model->n_indexes_valid <= id)
168     node_validate_indexes (model, id, G_MAXUINT);
169
170   return get_node (model, id)->index - 1;
171 }
172
173 static void 
174 node_invalidate_index (GtkFileSystemModel *model, guint id)
175 {
176   model->n_indexes_valid = MIN (model->n_indexes_valid, id);
177 }
178
179 static GtkTreePath *
180 gtk_tree_path_new_from_node (GtkFileSystemModel *model, guint id)
181 {
182   guint i = node_get_index (model, id);
183
184   g_assert (i < model->files->len);
185
186   return gtk_tree_path_new_from_indices (i, -1);
187 }
188
189 static void
190 node_set_visible (GtkFileSystemModel *model, guint id, gboolean visible)
191 {
192   FileModelNode *node = get_node (model, id);
193   GtkTreePath *path;
194   GtkTreeIter iter;
195
196   if (node->visible == visible ||
197       node->frozen_add)
198     return;
199
200   if (visible)
201     {
202       node->visible = TRUE;
203       node_invalidate_index (model, id);
204       path = gtk_tree_path_new_from_node (model, id);
205       ITER_INIT_FROM_INDEX (model, &iter, id);
206       gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
207       gtk_tree_path_free (path);
208     }
209   else
210     {
211       path = gtk_tree_path_new_from_node (model, id);
212       node->visible = FALSE;
213       node_invalidate_index (model, id);
214       gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
215       gtk_tree_path_free (path);
216     }
217 }
218
219 static gboolean
220 node_should_be_visible (GtkFileSystemModel *model, guint id)
221 {
222   FileModelNode *node = get_node (model, id);
223   GtkFileFilterInfo filter_info = { 0, };
224   GtkFileFilterFlags required;
225   gboolean is_folder, result;
226   char *mime_type = NULL;
227   char *filename = NULL;
228   char *uri = NULL;
229
230   if (node->info == NULL)
231     return FALSE;
232
233   if (!model->show_hidden &&
234       (g_file_info_get_is_hidden (node->info) || g_file_info_get_is_backup (node->info)))
235     return FALSE;
236
237   is_folder = _gtk_file_info_consider_as_directory (node->info);
238   
239   /* wtf? */
240   if (model->show_folders != model->show_files &&
241       model->show_folders != is_folder)
242     return FALSE;
243
244   if (is_folder)
245     return TRUE;
246
247   if (model->filter == NULL)
248     return TRUE;
249
250   /* fill info */
251   required = gtk_file_filter_get_needed (model->filter);
252
253   filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME;
254   filter_info.display_name = g_file_info_get_display_name (node->info);
255
256   if (required & GTK_FILE_FILTER_MIME_TYPE)
257     {
258       filter_info.mime_type = g_file_info_get_attribute_string (node->info, "filechooser::mime-type");
259       if (filter_info.mime_type != NULL)
260         filter_info.contains |= GTK_FILE_FILTER_MIME_TYPE;
261       else
262         {
263           const char *s = g_file_info_get_content_type (node->info);
264           if (s)
265             {
266               mime_type = g_content_type_get_mime_type (s);
267               if (mime_type)
268                 {
269                   filter_info.mime_type = mime_type;
270                   filter_info.contains |= GTK_FILE_FILTER_MIME_TYPE;
271                 }
272             }
273         }
274     }
275
276   if (required & GTK_FILE_FILTER_FILENAME)
277     {
278       filename = g_file_get_path (node->file);
279       if (filter_info.filename)
280         {
281           filter_info.filename = filename;
282           filter_info.contains |= GTK_FILE_FILTER_FILENAME;
283         }
284     }
285
286   if (required & GTK_FILE_FILTER_URI)
287     {
288       uri = g_file_get_uri (node->file);
289       if (uri)
290         {
291           filter_info.uri = uri;
292           filter_info.contains |= GTK_FILE_FILTER_URI;
293         }
294     }
295
296   result = gtk_file_filter_filter (model->filter, &filter_info);
297
298   g_free (mime_type);
299   g_free (filename);
300   g_free (uri);
301
302   return result;
303 }
304
305 /*** GtkTreeModel ***/
306
307 static GtkTreeModelFlags
308 gtk_file_system_model_get_flags (GtkTreeModel *tree_model)
309 {
310   /* GTK_TREE_MODEL_ITERS_PERSIST doesn't work with arrays :( */
311   return GTK_TREE_MODEL_LIST_ONLY;
312 }
313
314 static gint
315 gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model)
316 {
317   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
318   
319   return model->n_columns;
320 }
321
322 static GType
323 gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
324                                        gint          i)
325 {
326   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
327   
328   g_return_val_if_fail (i >= 0 && (guint) i < model->n_columns, G_TYPE_NONE);
329
330   return model->column_types[i];
331 }
332
333 static int
334 compare_indices (gconstpointer key, gconstpointer _node)
335 {
336   const FileModelNode *node = _node;
337
338   return GPOINTER_TO_UINT (key) - node->index;
339 }
340
341 static gboolean
342 gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
343                                       GtkTreeIter  *iter,
344                                       GtkTreeIter  *parent,
345                                       gint          n)
346 {
347   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
348   char *node;
349   guint id, find;
350
351   g_return_val_if_fail (n >= 0, FALSE);
352
353   if (parent != NULL)
354     return FALSE;
355
356   find = n + 1;
357
358   if (model->n_indexes_valid > 0 &&
359       get_node (model, model->n_indexes_valid - 1)->index >= find)
360     {
361       /* fast path */
362       node = bsearch (GUINT_TO_POINTER (find), 
363                       model->files->data,
364                       model->n_indexes_valid,
365                       model->node_size,
366                       compare_indices);
367       if (node == NULL)
368         return FALSE;
369
370       id = node_index (model, node);
371       while (!get_node (model, id)->visible)
372         id--;
373     }
374   else
375     {
376       node_validate_indexes (model, G_MAXUINT, n);
377       id = model->n_indexes_valid - 1;
378       if (model->n_indexes_valid == 0 || get_node (model, id)->index != find)
379         return FALSE;
380     }
381
382   ITER_INIT_FROM_INDEX (model, iter, id);
383   return TRUE;
384 }
385
386 static gboolean
387 gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
388                                 GtkTreeIter  *iter,
389                                 GtkTreePath  *path)
390 {
391   g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
392
393   return gtk_file_system_model_iter_nth_child (tree_model, 
394                                                iter,
395                                                NULL, 
396                                                gtk_tree_path_get_indices (path)[0]);
397 }
398
399 static GtkTreePath *
400 gtk_file_system_model_get_path (GtkTreeModel *tree_model,
401                                 GtkTreeIter  *iter)
402 {
403   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
404       
405   g_return_val_if_fail (ITER_IS_VALID (model, iter), NULL);
406
407   return gtk_tree_path_new_from_node (model, ITER_INDEX (iter));
408 }
409
410 static void
411 gtk_file_system_model_get_value (GtkTreeModel *tree_model,
412                                  GtkTreeIter  *iter,
413                                  gint          column,
414                                  GValue       *value)
415 {
416   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
417   const GValue *original;
418   
419   g_return_if_fail ((guint) column < model->n_columns);
420   g_return_if_fail (ITER_IS_VALID (model, iter));
421
422   original = _gtk_file_system_model_get_value (model, iter, column);
423   if (original)
424     {
425       g_value_init (value, G_VALUE_TYPE (original));
426       g_value_copy (original, value);
427     }
428   else
429     g_value_init (value, model->column_types[column]);
430 }
431
432 static gboolean
433 gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
434                                  GtkTreeIter  *iter)
435 {
436   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
437   guint i;
438
439   g_return_val_if_fail (ITER_IS_VALID (model, iter), FALSE);
440
441   for (i = ITER_INDEX (iter) + 1; i < model->files->len; i++) 
442     {
443       FileModelNode *node = get_node (model, i);
444
445       if (node->visible)
446         {
447           ITER_INIT_FROM_INDEX (model, iter, i);
448           return TRUE;
449         }
450     }
451       
452   return FALSE;
453 }
454
455 static gboolean
456 gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
457                                      GtkTreeIter  *iter,
458                                      GtkTreeIter  *parent)
459 {
460   return FALSE;
461 }
462
463 static gboolean
464 gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
465                                       GtkTreeIter  *iter)
466 {
467   return FALSE;
468 }
469
470 static gint
471 gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
472                                        GtkTreeIter  *iter)
473 {
474   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
475
476   if (iter)
477     return 0;
478
479   return node_get_index (model, model->files->len - 1) + 1;
480 }
481
482 static gboolean
483 gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
484                                    GtkTreeIter  *iter,
485                                    GtkTreeIter  *child)
486 {
487   return FALSE;
488 }
489
490 static void
491 gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
492                                 GtkTreeIter  *iter)
493 {
494   /* FIXME: implement */
495 }
496
497 static void
498 gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
499                                   GtkTreeIter  *iter)
500 {
501   /* FIXME: implement */
502 }
503
504 static void
505 gtk_file_system_model_iface_init (GtkTreeModelIface *iface)
506 {
507   iface->get_flags =       gtk_file_system_model_get_flags;
508   iface->get_n_columns =   gtk_file_system_model_get_n_columns;
509   iface->get_column_type = gtk_file_system_model_get_column_type;
510   iface->get_iter =        gtk_file_system_model_get_iter;
511   iface->get_path =        gtk_file_system_model_get_path;
512   iface->get_value =       gtk_file_system_model_get_value;
513   iface->iter_next =       gtk_file_system_model_iter_next;
514   iface->iter_children =   gtk_file_system_model_iter_children;
515   iface->iter_has_child =  gtk_file_system_model_iter_has_child;
516   iface->iter_n_children = gtk_file_system_model_iter_n_children;
517   iface->iter_nth_child =  gtk_file_system_model_iter_nth_child;
518   iface->iter_parent =     gtk_file_system_model_iter_parent;
519   iface->ref_node =        gtk_file_system_model_ref_node;
520   iface->unref_node =      gtk_file_system_model_unref_node;
521 }
522
523 /*** GtkTreeSortable ***/
524
525 typedef struct _SortData SortData;
526 struct _SortData {
527   GtkFileSystemModel *    model;
528   GtkTreeIterCompareFunc  func;
529   gpointer                data;
530   int                     order;        /* -1 to invert sort order or 1 to keep it */
531 };
532
533 /* returns FALSE if no sort necessary */
534 static gboolean
535 sort_data_init (SortData *data, GtkFileSystemModel *model)
536 {
537   GtkTreeDataSortHeader *header;
538
539   if (model->files->len <= 2)
540     return FALSE;
541
542   switch (model->sort_column_id)
543     {
544     case GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID:
545       if (!model->default_sort_func)
546         return FALSE;
547       data->func = model->default_sort_func;
548       data->data = model->default_sort_data;
549       break;
550     case GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID:
551       return FALSE;
552     default:
553       header = _gtk_tree_data_list_get_header (model->sort_list, model->sort_column_id);
554       if (header == NULL)
555         return FALSE;
556       data->func = header->func;
557       data->data = header->data;
558       break;
559     }
560
561   data->order = model->sort_order == GTK_SORT_DESCENDING ? -1 : 1;
562   data->model = model;
563   return TRUE;
564 }
565
566 static int
567 compare_array_element (gconstpointer a, gconstpointer b, gpointer user_data)
568 {
569   SortData *data = user_data;
570   GtkTreeIter itera, iterb;
571
572   ITER_INIT_FROM_INDEX (data->model, &itera, node_index (data->model, a));
573   ITER_INIT_FROM_INDEX (data->model, &iterb, node_index (data->model, b));
574   return data->func (GTK_TREE_MODEL (data->model), &itera, &iterb, data->data) * data->order;
575 }
576
577 static void
578 gtk_file_system_model_sort (GtkFileSystemModel *model)
579 {
580   SortData data;
581
582   if (model->frozen)
583     {
584       model->sort_on_thaw = TRUE;
585       return;
586     }
587
588   if (sort_data_init (&data, model))
589     {
590       GtkTreePath *path;
591       guint i, j, n_elements;
592
593       node_validate_indexes (model, G_MAXUINT, G_MAXUINT);
594       n_elements = node_get_index (model, model->files->len - 1) + 1;
595       model->n_indexes_valid = 0;
596       g_hash_table_remove_all (model->file_lookup);
597       g_qsort_with_data (get_node (model, 1),
598                          model->files->len - 1,
599                          model->node_size,
600                          compare_array_element,
601                          &data);
602       g_assert (model->n_indexes_valid == 0);
603       g_assert (g_hash_table_size (model->file_lookup) == 0);
604       if (n_elements)
605         {
606           int *new_order = g_new (int, n_elements);
607         
608           j = 0;
609           for (i = 0; i < model->files->len; i++)
610             {
611               FileModelNode *node = get_node (model, i);
612               if (!node->visible)
613                 {
614                   node->index = j;
615                   continue;
616                 }
617
618               new_order[j] = node->index;
619               j++;
620               node->index = j;
621             }
622           g_assert (j == n_elements);
623           path = gtk_tree_path_new ();
624           gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
625                                          path,
626                                          NULL,
627                                          new_order);
628           gtk_tree_path_free (path);
629           g_free (new_order);
630         }
631     }
632
633   model->sort_on_thaw = FALSE;
634 }
635
636 static void
637 gtk_file_system_model_sort_node (GtkFileSystemModel *model, guint node)
638 {
639   /* FIXME: improve */
640   gtk_file_system_model_sort (model);
641 }
642
643 static gboolean
644 gtk_file_system_model_get_sort_column_id (GtkTreeSortable  *sortable,
645                                           gint             *sort_column_id,
646                                           GtkSortType      *order)
647 {
648   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
649
650   if (sort_column_id)
651     *sort_column_id = model->sort_column_id;
652   if (order)
653     *order = model->sort_order;
654
655   if (model->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID ||
656       model->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
657     return FALSE;
658
659   return TRUE;
660 }
661
662 static void
663 gtk_file_system_model_set_sort_column_id (GtkTreeSortable  *sortable,
664                                           gint              sort_column_id,
665                                           GtkSortType       order)
666 {
667   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
668
669   if ((model->sort_column_id == sort_column_id) &&
670       (model->sort_order == order))
671     return;
672
673   if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
674     {
675       if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
676         {
677           GtkTreeDataSortHeader *header = NULL;
678
679           header = _gtk_tree_data_list_get_header (model->sort_list, 
680                                                    sort_column_id);
681
682           /* We want to make sure that we have a function */
683           g_return_if_fail (header != NULL);
684           g_return_if_fail (header->func != NULL);
685         }
686       else
687         {
688           g_return_if_fail (model->default_sort_func != NULL);
689         }
690     }
691
692
693   model->sort_column_id = sort_column_id;
694   model->sort_order = order;
695
696   gtk_tree_sortable_sort_column_changed (sortable);
697
698   gtk_file_system_model_sort (model);
699 }
700
701 static void
702 gtk_file_system_model_set_sort_func (GtkTreeSortable        *sortable,
703                                      gint                    sort_column_id,
704                                      GtkTreeIterCompareFunc  func,
705                                      gpointer                data,
706                                      GDestroyNotify          destroy)
707 {
708   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
709
710   model->sort_list = _gtk_tree_data_list_set_header (model->sort_list, 
711                                                      sort_column_id, 
712                                                      func, data, destroy);
713
714   if (model->sort_column_id == sort_column_id)
715     gtk_file_system_model_sort (model);
716 }
717
718 static void
719 gtk_file_system_model_set_default_sort_func (GtkTreeSortable        *sortable,
720                                              GtkTreeIterCompareFunc  func,
721                                              gpointer                data,
722                                              GDestroyNotify          destroy)
723 {
724   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
725
726   if (model->default_sort_destroy)
727     {
728       GDestroyNotify d = model->default_sort_destroy;
729
730       model->default_sort_destroy = NULL;
731       d (model->default_sort_data);
732     }
733
734   model->default_sort_func = func;
735   model->default_sort_data = data;
736   model->default_sort_destroy = destroy;
737
738   if (model->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
739     gtk_file_system_model_sort (model);
740 }
741
742 static gboolean
743 gtk_file_system_model_has_default_sort_func (GtkTreeSortable *sortable)
744 {
745   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
746
747   return (model->default_sort_func != NULL);
748 }
749
750 static void
751 gtk_file_system_model_sortable_init (GtkTreeSortableIface *iface)
752 {
753   iface->get_sort_column_id = gtk_file_system_model_get_sort_column_id;
754   iface->set_sort_column_id = gtk_file_system_model_set_sort_column_id;
755   iface->set_sort_func = gtk_file_system_model_set_sort_func;
756   iface->set_default_sort_func = gtk_file_system_model_set_default_sort_func;
757   iface->has_default_sort_func = gtk_file_system_model_has_default_sort_func;
758 }
759
760 /*** GtkTreeDragSource ***/
761
762 static gboolean
763 drag_source_row_draggable (GtkTreeDragSource *drag_source,
764                            GtkTreePath       *path)
765 {
766   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (drag_source);
767   GtkTreeIter iter;
768
769   if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
770     return FALSE;
771
772   return ITER_INDEX (&iter) != 0;
773 }
774
775 static gboolean
776 drag_source_drag_data_get (GtkTreeDragSource *drag_source,
777                            GtkTreePath       *path,
778                            GtkSelectionData  *selection_data)
779 {
780   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (drag_source);
781   FileModelNode *node;
782   GtkTreeIter iter;
783   char *uris[2]; 
784
785   if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
786     return FALSE;
787
788   node = get_node (model, ITER_INDEX (&iter));
789   if (node->file == NULL)
790     return FALSE;
791
792   uris[0] = g_file_get_uri (node->file);
793   uris[1] = NULL;
794   gtk_selection_data_set_uris (selection_data, uris);
795   g_free (uris[0]);
796
797   return TRUE;
798 }
799
800 static void
801 drag_source_iface_init (GtkTreeDragSourceIface *iface)
802 {
803   iface->row_draggable = drag_source_row_draggable;
804   iface->drag_data_get = drag_source_drag_data_get;
805   iface->drag_data_delete = NULL;
806 }
807
808 /*** GtkFileSystemModel ***/
809
810 /* Signal IDs */
811 enum {
812   FINISHED_LOADING,
813   LAST_SIGNAL
814 };
815
816 static guint file_system_model_signals[LAST_SIGNAL] = { 0 };
817
818 \f
819
820 G_DEFINE_TYPE_WITH_CODE (GtkFileSystemModel, _gtk_file_system_model, G_TYPE_OBJECT,
821                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
822                                                 gtk_file_system_model_iface_init)
823                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
824                                                 gtk_file_system_model_sortable_init)
825                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
826                                                 drag_source_iface_init))
827
828 static void
829 gtk_file_system_model_dispose (GObject *object)
830 {
831   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
832
833   g_cancellable_cancel (model->cancellable);
834   if (model->dir_monitor)
835     g_file_monitor_cancel (model->dir_monitor);
836
837   G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->dispose (object);
838 }
839
840
841 static void
842 gtk_file_system_model_finalize (GObject *object)
843 {
844   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
845   guint i;
846
847   for (i = 0; i < model->files->len; i++)
848     {
849       FileModelNode *node = get_node (model, i);
850       if (node->file)
851         g_object_unref (node->file);
852       if (node->info)
853         g_object_unref (node->info);
854     }
855   g_array_free (model->files, TRUE);
856
857   g_object_unref (model->cancellable);
858   g_free (model->attributes);
859   if (model->dir)
860     g_object_unref (model->dir);
861   if (model->dir_monitor)
862     g_object_unref (model->dir_monitor);
863   g_hash_table_destroy (model->file_lookup);
864   if (model->filter)
865     g_object_unref (model->filter);
866
867   _gtk_tree_data_list_header_free (model->sort_list);
868   if (model->default_sort_destroy)
869     model->default_sort_destroy (model->default_sort_data);
870
871   G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->finalize (object);
872 }
873
874 static void
875 _gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
876 {
877   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
878
879   gobject_class->finalize = gtk_file_system_model_finalize;
880   gobject_class->dispose = gtk_file_system_model_dispose;
881
882   file_system_model_signals[FINISHED_LOADING] =
883     g_signal_new (I_("finished-loading"),
884                   G_OBJECT_CLASS_TYPE (gobject_class),
885                   G_SIGNAL_RUN_LAST,
886                   G_STRUCT_OFFSET (GtkFileSystemModelClass, finished_loading),
887                   NULL, NULL,
888                   _gtk_marshal_VOID__POINTER,
889                   G_TYPE_NONE, 1, G_TYPE_POINTER);
890 }
891
892 static void
893 _gtk_file_system_model_init (GtkFileSystemModel *model)
894 {
895   model->show_files = TRUE;
896   model->show_folders = TRUE;
897   model->show_hidden = FALSE;
898
899   model->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
900
901   model->file_lookup = g_hash_table_new (g_file_hash, (GEqualFunc) g_file_equal);
902   model->cancellable = g_cancellable_new ();
903 }
904
905 /*** API ***/
906
907 static void
908 gtk_file_system_model_closed_enumerator (GObject *object, GAsyncResult *res, gpointer data)
909 {
910   g_file_enumerator_close_finish (G_FILE_ENUMERATOR (object), res, NULL);
911 }
912
913 static gboolean
914 thaw_func (gpointer data)
915 {
916   GtkFileSystemModel *model = data;
917
918   _gtk_file_system_model_thaw_updates (model);
919   model->dir_thaw_source = 0;
920
921   return FALSE;
922 }
923
924 static void
925 gtk_file_system_model_got_files (GObject *object, GAsyncResult *res, gpointer data)
926 {
927   GFileEnumerator *enumerator = G_FILE_ENUMERATOR (object);
928   GtkFileSystemModel *model = data;
929   GList *walk, *files;
930   GError *error = NULL;
931
932   gdk_threads_enter ();
933
934   files = g_file_enumerator_next_files_finish (enumerator, res, &error);
935
936   if (files)
937     {
938       if (model->dir_thaw_source == 0)
939         {
940           _gtk_file_system_model_freeze_updates (model);
941           model->dir_thaw_source = gdk_threads_add_timeout_full (IO_PRIORITY + 1,
942                                                                  50,
943                                                                  thaw_func,
944                                                                  model,
945                                                                  NULL);
946         }
947
948       for (walk = files; walk; walk = walk->next)
949         {
950           const char *name;
951           GFileInfo *info;
952           GFile *file;
953           
954           info = walk->data;
955           name = g_file_info_get_name (info);
956           if (name == NULL)
957             {
958               /* Shouldn't happen, but the APIs allow it */
959               g_object_unref (info);
960               continue;
961             }
962           file = g_file_get_child (model->dir, name);
963           add_file (model, file, info);
964           g_object_unref (file);
965           g_object_unref (info);
966         }
967       g_list_free (files);
968     }
969
970   if (files == NULL)
971     {
972       g_file_enumerator_close_async (enumerator, 
973                                      IO_PRIORITY,
974                                      model->cancellable,
975                                      gtk_file_system_model_closed_enumerator,
976                                      NULL);
977       if (model->dir_thaw_source != 0)
978         {
979           g_source_remove (model->dir_thaw_source);
980           model->dir_thaw_source = 0;
981           _gtk_file_system_model_thaw_updates (model);
982         }
983
984       if (error)
985         g_error_free (error);
986       else
987         g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, NULL);
988
989       g_object_unref (model);
990     }
991   else
992     g_file_enumerator_next_files_async (enumerator,
993                                         g_file_is_native (model->dir) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY,
994                                         IO_PRIORITY,
995                                         model->cancellable,
996                                         gtk_file_system_model_got_files,
997                                         model);
998
999   gdk_threads_leave ();
1000 }
1001
1002 static void
1003 gtk_file_system_model_query_done (GObject *     object,
1004                                   GAsyncResult *res,
1005                                   gpointer      data)
1006 {
1007   GtkFileSystemModel *model = data; /* only a valid pointer if not cancelled */
1008   GFile *file = G_FILE (object);
1009   GFileInfo *info;
1010
1011   info = g_file_query_info_finish (file, res, NULL);
1012   if (info == NULL)
1013     return;
1014
1015   _gtk_file_system_model_update_file (model, file, info, TRUE);
1016 }
1017
1018 static void
1019 gtk_file_system_model_monitor_change (GFileMonitor *      monitor,
1020                                       GFile *             file,
1021                                       GFile *             other_file,
1022                                       GFileMonitorEvent   type,
1023                                       GtkFileSystemModel *model)
1024 {
1025   switch (type)
1026     {
1027       case G_FILE_MONITOR_EVENT_CREATED:
1028       case G_FILE_MONITOR_EVENT_CHANGED:
1029       case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
1030         /* We can treat all of these the same way */
1031         g_file_query_info_async (file,
1032                                  model->attributes,
1033                                  0,
1034                                  IO_PRIORITY,
1035                                  model->cancellable,
1036                                  gtk_file_system_model_query_done,
1037                                  model);
1038         break;
1039       case G_FILE_MONITOR_EVENT_DELETED:
1040         remove_file (model, file);
1041         break;
1042       case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
1043         /* FIXME: use freeze/thaw with this somehow? */
1044       case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
1045       case G_FILE_MONITOR_EVENT_UNMOUNTED:
1046       default:
1047         /* ignore these */
1048         break;
1049     }
1050 }
1051
1052 static void
1053 gtk_file_system_model_got_enumerator (GObject *dir, GAsyncResult *res, gpointer data)
1054 {
1055   GtkFileSystemModel *model = data;
1056   GFileEnumerator *enumerator;
1057   GError *error = NULL;
1058
1059   gdk_threads_enter ();
1060
1061   enumerator = g_file_enumerate_children_finish (G_FILE (dir), res, &error);
1062   if (enumerator == NULL)
1063     {
1064       g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error);
1065       g_object_unref (model);
1066       g_error_free (error);
1067     }
1068   else
1069     {
1070       g_file_enumerator_next_files_async (enumerator,
1071                                           g_file_is_native (model->dir) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY,
1072                                           IO_PRIORITY,
1073                                           model->cancellable,
1074                                           gtk_file_system_model_got_files,
1075                                           model);
1076       g_object_unref (enumerator);
1077       model->dir_monitor = g_file_monitor_directory (model->dir,
1078                                                      G_FILE_MONITOR_NONE,
1079                                                      model->cancellable,
1080                                                      NULL); /* we don't mind if directory monitoring isn't supported, so the GError is NULL here */
1081       if (model->dir_monitor)
1082         g_signal_connect (model->dir_monitor,
1083                           "changed",
1084                           G_CALLBACK (gtk_file_system_model_monitor_change),
1085                           model);
1086     }
1087
1088   gdk_threads_leave ();
1089 }
1090
1091 static void
1092 gtk_file_system_model_set_n_columns (GtkFileSystemModel *model,
1093                                      gint                n_columns,
1094                                      va_list             args)
1095 {
1096   guint i;
1097
1098   g_assert (model->files == NULL);
1099   g_assert (n_columns > 0);
1100
1101   model->n_columns = n_columns;
1102   model->column_types = g_slice_alloc (sizeof (GType) * n_columns);
1103
1104   model->node_size = sizeof (FileModelNode) + sizeof (GValue) * (n_columns - 1); /* minus 1 because FileModelNode.values[] has a default size of 1 */
1105
1106   for (i = 0; i < (guint) n_columns; i++)
1107     {
1108       GType type = va_arg (args, GType);
1109       if (! _gtk_tree_data_list_check_type (type))
1110         {
1111           g_error ("%s: type %s cannot be a column type for GtkFileSystemModel\n", G_STRLOC, g_type_name (type));
1112           return; /* not reached */
1113         }
1114
1115       model->column_types[i] = type;
1116     }
1117
1118   model->sort_list = _gtk_tree_data_list_header_new (n_columns, model->column_types);
1119
1120   model->files = g_array_sized_new (FALSE, FALSE, model->node_size, FILES_PER_QUERY);
1121   /* add editable node at start */
1122   g_array_set_size (model->files, 1);
1123   memset (get_node (model, 0), 0, model->node_size);
1124 }
1125
1126 static void
1127 gtk_file_system_model_set_directory (GtkFileSystemModel *model,
1128                                      GFile *             dir,
1129                                      const gchar *       attributes)
1130 {
1131   g_assert (G_IS_FILE (dir));
1132
1133   model->dir = g_object_ref (dir);
1134   model->attributes = g_strdup (attributes);
1135
1136   g_object_ref (model);
1137   g_file_enumerate_children_async (model->dir,
1138                                    attributes,
1139                                    G_FILE_QUERY_INFO_NONE,
1140                                    IO_PRIORITY,
1141                                    model->cancellable,
1142                                    gtk_file_system_model_got_enumerator,
1143                                    model);
1144
1145 }
1146
1147 static GtkFileSystemModel *
1148 _gtk_file_system_model_new_valist (GtkFileSystemModelGetValue get_func,
1149                                    gpointer            get_data,
1150                                    guint               n_columns,
1151                                    va_list             args)
1152 {
1153   GtkFileSystemModel *model;
1154
1155   model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
1156   model->get_func = get_func;
1157   model->get_data = get_data;
1158
1159   gtk_file_system_model_set_n_columns (model, n_columns, args);
1160
1161   return model;
1162 }
1163
1164 /**
1165  * _gtk_file_system_model_new:
1166  * @get_func: function to call for getting a value
1167  * @get_data: user data argument passed to @get_func
1168  * @n_columns: number of columns
1169  * @...: @n_columns #GType types for the columns
1170  *
1171  * Creates a new #GtkFileSystemModel object. You need to add files
1172  * to the list using _gtk_file_system_model_add_and_query_file()
1173  * or _gtk_file_system_model_update_file().
1174  *
1175  * Return value: the newly created #GtkFileSystemModel
1176  **/
1177 GtkFileSystemModel *
1178 _gtk_file_system_model_new (GtkFileSystemModelGetValue get_func,
1179                             gpointer            get_data,
1180                             guint               n_columns,
1181                             ...)
1182 {
1183   GtkFileSystemModel *model;
1184   va_list args;
1185
1186   g_return_val_if_fail (get_func != NULL, NULL);
1187   g_return_val_if_fail (n_columns > 0, NULL);
1188
1189   va_start (args, n_columns);
1190   model = _gtk_file_system_model_new_valist (get_func, get_data, n_columns, args);
1191   va_end (args);
1192
1193   return model;
1194 }
1195
1196 /**
1197  * _gtk_file_system_model_new_for_directory:
1198  * @directory: the directory to show.
1199  * @attributes: attributes to immediately load or %NULL for all
1200  * @get_func: function that the model should call to query data about a file
1201  * @get_data: user data to pass to the @get_func
1202  * @n_columns: number of columns
1203  * @...: @n_columns #GType types for the columns
1204  *
1205  * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
1206  * object wraps the given @directory as a #GtkTreeModel.
1207  * The model will query the given directory with the given @attributes
1208  * and add all files inside the directory automatically. If supported,
1209  * it will also monitor the drectory and update the model's
1210  * contents to reflect changes, if the @directory supports monitoring.
1211  * 
1212  * Return value: the newly created #GtkFileSystemModel
1213  **/
1214 GtkFileSystemModel *
1215 _gtk_file_system_model_new_for_directory (GFile *                    dir,
1216                                           const gchar *              attributes,
1217                                           GtkFileSystemModelGetValue get_func,
1218                                           gpointer                   get_data,
1219                                           guint                      n_columns,
1220                                           ...)
1221 {
1222   GtkFileSystemModel *model;
1223   va_list args;
1224
1225   g_return_val_if_fail (G_IS_FILE (dir), NULL);
1226   g_return_val_if_fail (get_func != NULL, NULL);
1227   g_return_val_if_fail (n_columns > 0, NULL);
1228
1229   va_start (args, n_columns);
1230   model = _gtk_file_system_model_new_valist (get_func, get_data, n_columns, args);
1231   va_end (args);
1232
1233   gtk_file_system_model_set_directory (model, dir, attributes);
1234
1235   return model;
1236 }
1237
1238 static void
1239 gtk_file_system_model_refilter_all (GtkFileSystemModel *model)
1240 {
1241   guint i;
1242
1243   if (model->frozen)
1244     {
1245       model->filter_on_thaw = TRUE;
1246       return;
1247     }
1248
1249   _gtk_file_system_model_freeze_updates (model);
1250
1251   /* start at index 1, don't change the editable */
1252   for (i = 1; i < model->files->len; i++)
1253     {
1254       node_set_visible (model, i, node_should_be_visible (model, i));
1255     }
1256
1257   model->filter_on_thaw = FALSE;
1258   _gtk_file_system_model_thaw_updates (model);
1259 }
1260
1261 /**
1262  * _gtk_file_system_model_set_show_hidden:
1263  * @model: a #GtkFileSystemModel
1264  * @show_hidden: whether hidden files should be displayed
1265  * 
1266  * Sets whether hidden files should be included in the #GtkTreeModel
1267  * for display.
1268  **/
1269 void
1270 _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
1271                                         gboolean            show_hidden)
1272 {
1273   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1274
1275   show_hidden = show_hidden != FALSE;
1276
1277   if (show_hidden != model->show_hidden)
1278     {
1279       model->show_hidden = show_hidden;
1280       gtk_file_system_model_refilter_all (model);
1281     }
1282 }
1283
1284 /**
1285  * _gtk_file_system_model_set_show_folders:
1286  * @model: a #GtkFileSystemModel
1287  * @show_folders: whether folders should be displayed
1288  * 
1289  * Sets whether folders should be included in the #GtkTreeModel for
1290  * display.
1291  **/
1292 void
1293 _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
1294                                          gboolean            show_folders)
1295 {
1296   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1297
1298   show_folders = show_folders != FALSE;
1299
1300   if (show_folders != model->show_folders)
1301     {
1302       model->show_folders = show_folders;
1303       gtk_file_system_model_refilter_all (model);
1304     }
1305 }
1306
1307 /**
1308  * _gtk_file_system_model_set_show_files:
1309  * @model: a #GtkFileSystemModel
1310  * @show_files: whether files (as opposed to folders) should
1311  *              be displayed.
1312  * 
1313  * Sets whether files (as opposed to folders) should be included
1314  * in the #GtkTreeModel for display.
1315  **/
1316 void
1317 _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
1318                                        gboolean            show_files)
1319 {
1320   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1321
1322   show_files = show_files != FALSE;
1323
1324   if (show_files != model->show_files)
1325     {
1326       model->show_files = show_files;
1327       gtk_file_system_model_refilter_all (model);
1328     }
1329 }
1330
1331 /**
1332  * _gtk_file_system_model_get_cancellable:
1333  * @model: the model
1334  *
1335  * Gets the cancellable used by the @model. This is the cancellable used
1336  * internally by the @model that will be cancelled when @model is 
1337  * disposed. So you can use it for operations that should be cancelled
1338  * when the model goes away.
1339  *
1340  * Returns: The cancellable used by @model
1341  **/
1342 GCancellable *
1343 _gtk_file_system_model_get_cancellable (GtkFileSystemModel *model)
1344 {
1345   g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL);
1346
1347   return model->cancellable;
1348 }
1349
1350 /**
1351  * _gtk_file_system_model_get_is_visible:
1352  * @model: the model
1353  * @iter: a valid iterator
1354  *
1355  * Checks if the iterator is visible. A visible iterator references
1356  * a row that is currently exposed using the #GtkTreeModel API. If
1357  * the iterator is invisible, it references a file that is not shown
1358  * for some reason, such as being filtered by the current filter or
1359  * being a hidden file.
1360  *
1361  * Returns: %TRUE if the iterator is visible
1362  **/
1363 gboolean
1364 _gtk_file_system_model_get_is_visible (GtkFileSystemModel *model,
1365                                        GtkTreeIter        *iter)
1366 {
1367   FileModelNode *node;
1368
1369   g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), FALSE);
1370   g_return_val_if_fail (iter != NULL, FALSE);
1371
1372   node = get_node (model, ITER_INDEX (iter));
1373   return node->visible;
1374 }
1375
1376 /**
1377  * _gtk_file_system_model_get_info:
1378  * @model: a #GtkFileSystemModel
1379  * @iter: a #GtkTreeIter pointing to a row of @model
1380  * 
1381  * Gets the #GFileInfo structure for a particular row
1382  * of @model.
1383  * 
1384  * Return value: a #GFileInfo structure. This structure
1385  *   is owned by @model and must not be modified or freed.
1386  *   If you want to keep the information for later use,
1387  *   you must take a reference, since the structure may be
1388  *   freed on later changes to the file system.  If you have
1389  *   called _gtk_file_system_model_add_editable() and the @iter
1390  *   corresponds to the row that this function returned, the
1391  *   return value will be NULL.
1392  **/
1393 GFileInfo *
1394 _gtk_file_system_model_get_info (GtkFileSystemModel *model,
1395                                  GtkTreeIter        *iter)
1396 {
1397   FileModelNode *node;
1398
1399   g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL);
1400   g_return_val_if_fail (iter != NULL, NULL);
1401
1402   node = get_node (model, ITER_INDEX (iter));
1403   g_assert (node->info == NULL || G_IS_FILE_INFO (node->info));
1404   return node->info;
1405 }
1406
1407 /**
1408  * _gtk_file_system_model_get_file:
1409  * @model: a #GtkFileSystemModel
1410  * @iter: a #GtkTreeIter pointing to a row of @model
1411  * 
1412  * Gets the file for a particular row in @model. 
1413  *
1414  * Return value: the file. This object is owned by @model and
1415  *   or freed. If you want to save the path for later use,
1416  *   you must take a ref, since the object may be freed
1417  *   on later changes to the file system.
1418  **/
1419 GFile *
1420 _gtk_file_system_model_get_file (GtkFileSystemModel *model,
1421                                  GtkTreeIter        *iter)
1422 {
1423   FileModelNode *node;
1424
1425   g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL);
1426
1427   node = get_node (model, ITER_INDEX (iter));
1428   return node->file;
1429 }
1430
1431 /**
1432  * _gtk_file_system_model_get_value:
1433  * @model: a #GtkFileSystemModel
1434  * @iter: a #GtkTreeIter pointing to a row of @model
1435  * @column: the column to get the value for
1436  *
1437  * Gets the value associated with the given row @iter and @column.
1438  * If no value is available yet and the default value should be used,
1439  * %NULL is returned.
1440  * This is a performance optimization for the calls 
1441  * gtk_tree_model_get() or gtk_tree_model_get_value(), which copy 
1442  * the value and spend a considerable amount of time in iterator 
1443  * lookups. Both of which are slow.
1444  *
1445  * Returns: a pointer to the actual value as stored in @model or %NULL
1446  *          if no value available yet.
1447  **/
1448 const GValue *
1449 _gtk_file_system_model_get_value (GtkFileSystemModel *model,
1450                                   GtkTreeIter *       iter,
1451                                   int                 column)
1452 {
1453   FileModelNode *node;
1454
1455   g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL);
1456   g_return_val_if_fail (column >= 0 && (guint) column < model->n_columns, NULL);
1457
1458   node = get_node (model, ITER_INDEX (iter));
1459     
1460   if (!G_VALUE_TYPE (&node->values[column]))
1461     {
1462       g_value_init (&node->values[column], model->column_types[column]);
1463       if (!model->get_func (model, 
1464                             node->file, 
1465                             node->info, 
1466                             column, 
1467                             &node->values[column],
1468                             model->get_data))
1469         {
1470           g_value_unset (&node->values[column]);
1471           return NULL;
1472         }
1473     }
1474   
1475   return &node->values[column];
1476 }
1477
1478 static guint
1479 node_get_for_file (GtkFileSystemModel *model,
1480                    GFile *             file)
1481 {
1482   guint i;
1483
1484   i = GPOINTER_TO_UINT (g_hash_table_lookup (model->file_lookup, file));
1485   if (i != 0)
1486     return i;
1487
1488   /* node 0 is the editable row and has no associated file or entry in the table */
1489   for (i = g_hash_table_size (model->file_lookup) + 1; i < model->files->len; i++)
1490     {
1491       FileModelNode *node = get_node (model, i);
1492
1493       g_hash_table_insert (model->file_lookup, node->file, GUINT_TO_POINTER (i));
1494       if (g_file_equal (node->file, file))
1495         return i;
1496     }
1497
1498   return 0;
1499 }
1500
1501 /**
1502  * _gtk_file_system_model_get_iter_for_file:
1503  * @model: the model
1504  * @iter: the iterator to be initialized
1505  * @file: the file to look up
1506  *
1507  * Initializes @iter to point to the row used for @file, if @file is part 
1508  * of the model. Note that upon successful return, @iter may point to an 
1509  * invisible row in the @model. Use 
1510  * _gtk_file_system_model_get_is_visible() to make sure it is visible to
1511  * the tree view.
1512  *
1513  * Returns: %TRUE if file is part of the model and @iter was initialized
1514  **/
1515 gboolean
1516 _gtk_file_system_model_get_iter_for_file (GtkFileSystemModel *model,
1517                                           GtkTreeIter        *iter,
1518                                           GFile *             file)
1519 {
1520   guint i;
1521
1522   g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), FALSE);
1523   g_return_val_if_fail (iter != NULL, FALSE);
1524   g_return_val_if_fail (G_IS_FILE (file), FALSE);
1525
1526   i = node_get_for_file (model, file);
1527
1528   if (i == 0)
1529     return FALSE;
1530
1531   ITER_INIT_FROM_INDEX (model, iter, i);
1532   return TRUE;
1533 }
1534
1535 /**
1536  * add_file:
1537  * @model: the model
1538  * @file: the file to add
1539  * @info: the information to associate with the file
1540  *
1541  * Adds the given @file with its associated @info to the @model. 
1542  * If the model is frozen, the file will only show up after it is thawn.
1543  **/
1544 static void
1545 add_file (GtkFileSystemModel *model,
1546           GFile              *file,
1547           GFileInfo          *info)
1548 {
1549   FileModelNode *node;
1550   
1551   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1552   g_return_if_fail (G_IS_FILE (file));
1553   g_return_if_fail (G_IS_FILE_INFO (info));
1554
1555   node = g_slice_alloc0 (model->node_size);
1556   node->file = g_object_ref (file);
1557   if (info)
1558     node->info = g_object_ref (info);
1559   node->frozen_add = model->frozen ? TRUE : FALSE;
1560
1561   g_array_append_vals (model->files, node, 1);
1562   g_slice_free1 (model->node_size, node);
1563
1564   if (!model->frozen)
1565     node_set_visible (model, model->files->len -1,
1566                       node_should_be_visible (model, model->files->len - 1));
1567   gtk_file_system_model_sort_node (model, model->files->len -1);
1568 }
1569
1570 /**
1571  * remove_file:
1572  * @model: the model
1573  * @file: file to remove from the model. The file must have been 
1574  *        added to the model previously
1575  *
1576  * Removes the given file from the model. If the file is not part of 
1577  * @model, this function does nothing.
1578  **/
1579 static void
1580 remove_file (GtkFileSystemModel *model,
1581              GFile              *file)
1582 {
1583   FileModelNode *node;
1584   guint id;
1585
1586   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1587   g_return_if_fail (G_IS_FILE (file));
1588
1589   id = node_get_for_file (model, file);
1590   if (id == 0)
1591     return;
1592
1593   node = get_node (model, id);
1594   node_set_visible (model, id, FALSE);
1595   g_object_unref (node->file);
1596   if (node->info)
1597     g_object_unref (node->info);
1598   g_array_remove_index (model->files, id);
1599 }
1600
1601 /**
1602  * _gtk_file_system_model_update_file:
1603  * @model: the model
1604  * @file: the file
1605  * @info: the new file info
1606  * @requires_resort: FIXME: get rid of this argument
1607  *
1608  * Tells the file system model that the file changed and that the 
1609  * new @info should be used for it now.  If the file is not part of 
1610  * @model, it will get added automatically.
1611  **/
1612 void
1613 _gtk_file_system_model_update_file (GtkFileSystemModel *model,
1614                                     GFile              *file,
1615                                     GFileInfo          *info,
1616                                     gboolean            requires_resort)
1617 {
1618   FileModelNode *node;
1619   guint i, id;
1620
1621   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1622   g_return_if_fail (G_IS_FILE (file));
1623   g_return_if_fail (G_IS_FILE_INFO (info));
1624
1625   id = node_get_for_file (model, file);
1626   if (id == 0)
1627     add_file (model, file, info);
1628
1629   node = get_node (model, id);
1630   if (node->info)
1631     g_object_unref (node->info);
1632   node->info = g_object_ref (info);
1633   for (i = 0; i < model->n_columns; i++)
1634     {
1635       if (G_VALUE_TYPE (&node->values[i]))
1636         g_value_unset (&node->values[i]);
1637     }
1638
1639   if (node->visible)
1640     {
1641       GtkTreePath *path;
1642       GtkTreeIter iter;
1643       
1644       path = gtk_tree_path_new_from_node (model, id);
1645       ITER_INIT_FROM_INDEX (model, &iter, id);
1646       gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
1647       gtk_tree_path_free (path);
1648     }
1649
1650   if (requires_resort)
1651     gtk_file_system_model_sort_node (model, id);
1652 }
1653
1654 /**
1655  * _gtk_file_system_model_set_filter:
1656  * @mode: a #GtkFileSystemModel
1657  * @filter: %NULL or filter to use
1658  * 
1659  * Sets a filter to be used for deciding if a row should be visible or not.
1660  * Directories are always visible.
1661  **/
1662 void
1663 _gtk_file_system_model_set_filter (GtkFileSystemModel      *model,
1664                                    GtkFileFilter *          filter)
1665 {
1666   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1667   g_return_if_fail (filter == NULL || GTK_IS_FILE_FILTER (filter));
1668   
1669   if (filter)
1670     g_object_ref (filter);
1671   if (model->filter)
1672     g_object_unref (model->filter);
1673   model->filter = filter;
1674
1675   gtk_file_system_model_refilter_all (model);
1676 }
1677
1678 /**
1679  * _gtk_file_system_model_add_editable:
1680  * @model: a #GtkFileSystemModel
1681  * @iter: Location to return the iter corresponding to the editable row
1682  * 
1683  * Adds an "empty" row at the beginning of the model.  This does not refer to
1684  * any file, but is a temporary placeholder for a file name that the user will
1685  * type when a corresponding cell is made editable.  When your code is done
1686  * using this temporary row, call _gtk_file_system_model_remove_editable().
1687  **/
1688 void
1689 _gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *iter)
1690 {
1691   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1692   g_return_if_fail (!get_node (model, 0)->visible);
1693
1694   node_set_visible (model, 0, TRUE);
1695   ITER_INIT_FROM_INDEX (model, iter, 0);
1696 }
1697
1698 /**
1699  * _gtk_file_system_model_remove_editable:
1700  * @model: a #GtkFileSystemModel
1701  * 
1702  * Removes the "empty" row at the beginning of the model that was
1703  * created with _gtk_file_system_model_add_editable().  You should call
1704  * this function when your code is finished editing this temporary row.
1705  **/
1706 void
1707 _gtk_file_system_model_remove_editable (GtkFileSystemModel *model)
1708 {
1709   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1710   g_return_if_fail (get_node (model, 0)->visible);
1711
1712   node_set_visible (model, 0, FALSE);
1713 }
1714
1715 /**
1716  * _gtk_file_system_model_freeze_updates:
1717  * @model: a #GtkFileSystemModel
1718  *
1719  * Freezes most updates on the model, so that performing multiple 
1720  * operations on the files in the model do not cause any events.
1721  * Use _gtk_file_system_model_thaw_updates() to resume proper 
1722  * operations. It is fine to call this function multiple times as
1723  * long as freeze and thaw calls are balanced.
1724  **/
1725 void
1726 _gtk_file_system_model_freeze_updates (GtkFileSystemModel *model)
1727 {
1728   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1729
1730   model->frozen++;
1731 }
1732
1733 /**
1734  * _gtk_file_system_model_thaw_updates:
1735  * @model: a #GtkFileSystemModel
1736  *
1737  * Undoes the effect of a previous call to
1738  * _gtk_file_system_model_freeze_updates() 
1739  **/
1740 void
1741 _gtk_file_system_model_thaw_updates (GtkFileSystemModel *model)
1742 {
1743   gboolean stuff_added;
1744
1745   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1746   g_return_if_fail (model->frozen > 0);
1747
1748   model->frozen--;
1749   if (model->frozen > 0)
1750     return;
1751
1752   stuff_added = get_node (model, model->files->len - 1)->frozen_add;
1753
1754   if (model->filter_on_thaw)
1755     gtk_file_system_model_refilter_all (model);
1756   if (model->sort_on_thaw)
1757     gtk_file_system_model_sort (model);
1758   if (stuff_added)
1759     {
1760       guint i;
1761
1762       for (i = 0; i < model->files->len; i++)
1763         {
1764           FileModelNode *node = get_node (model, i);
1765
1766           if (!node->frozen_add)
1767             continue;
1768           node->frozen_add = FALSE;
1769           node_set_visible (model, i, node_should_be_visible (model, i));
1770         }
1771     }
1772 }
1773
1774 /**
1775  * _gtk_file_system_model_clear_cached_values:
1776  * @model: a #GtkFileSystemModel
1777  * @column: the column to clear or -1 for all columns
1778  *
1779  * Clears the cached values in the model for the given @column. Use 
1780  * this function whenever your get_value function would return different
1781  * values for a column.
1782  * The file chooser uses this for example when the icon theme changes to 
1783  * invalidate the cached pixbufs.
1784  **/
1785 void
1786 _gtk_file_system_model_clear_cache (GtkFileSystemModel *model,
1787                                     int                 column)
1788 {
1789   guint i;
1790   int start, end;
1791   gboolean changed;
1792
1793   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1794   g_return_if_fail (column >= -1 && (guint) column < model->n_columns);
1795
1796   if (column)
1797     {
1798       start = column;
1799       end = column + 1;
1800     }
1801   else
1802     {
1803       start = 0;
1804       end = model->n_columns;
1805     }
1806
1807   for (i = 0; i < model->files->len; i++)
1808     {
1809       FileModelNode *node = get_node (model, i);
1810       changed = FALSE;
1811       for (column = start; column < end; column++)
1812         {
1813           if (!G_VALUE_TYPE (&node->values[column]))
1814             continue;
1815           
1816           g_value_unset (&node->values[column]);
1817           changed = TRUE;
1818         }
1819
1820       if (changed && node->visible)
1821         {
1822           GtkTreePath *path;
1823           GtkTreeIter iter;
1824           
1825           path = gtk_tree_path_new_from_node (model, i);
1826           ITER_INIT_FROM_INDEX (model, &iter, i);
1827           gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
1828           gtk_tree_path_free (path);
1829         }
1830     }
1831
1832   /* FIXME: resort? */
1833 }
1834
1835 /**
1836  * _gtk_file_system_model_add_and_query_file:
1837  * @model: a #GtkFileSystemModel
1838  * @file: the file to add
1839  * @attributes: attributes to query before adding the file
1840  *
1841  * This is a conenience function that calls g_file_query_info_async() on 
1842  * the given file, and when successful, adds it to the model.
1843  * Upon failure, the @file is discarded.
1844  **/
1845 void
1846 _gtk_file_system_model_add_and_query_file (GtkFileSystemModel *model,
1847                                            GFile *             file,
1848                                            const char *        attributes)
1849 {
1850   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1851   g_return_if_fail (G_IS_FILE (file));
1852   g_return_if_fail (attributes != NULL);
1853
1854   g_file_query_info_async (file,
1855                            attributes,
1856                            0,
1857                            IO_PRIORITY,
1858                            model->cancellable,
1859                            gtk_file_system_model_query_done,
1860                            model);
1861 }