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