]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemmodel.c
ca967f9c7cfcba3095113cb216d0e4199bbe0f17
[~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   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 /* iter setup:
115  * @user_data: the model
116  * @user_data2: GUINT_TO_POINTER of array index of current entry
117  *
118  * All other fields are unused. Note that the array index does not corrspond
119  * 1:1 with the path index as entries might not be visible.
120  */
121 #define ITER_INDEX(iter) GPOINTER_TO_UINT((iter)->user_data2)
122 #define ITER_IS_VALID(model, iter) ((model) == (iter)->user_data)
123 #define ITER_INIT_FROM_INDEX(model, _iter, _index) G_STMT_START {\
124   g_assert (_index < (model)->files->len); \
125   (_iter)->user_data = (model); \
126   (_iter)->user_data2 = GUINT_TO_POINTER (_index); \
127 }G_STMT_END
128
129 /*** FileModelNode ***/
130
131 #define get_node(_model, _index) ((FileModelNode *) ((_model)->files->data + (_index) * (_model)->node_size))
132 #define node_index(_model, _node) (((gchar *) (_node) - (_model)->files->data) / (_model)->node_size)
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                       model->node_size,
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                          model->node_size,
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 (model->dir_thaw_source != 0)
972         {
973           g_source_remove (model->dir_thaw_source);
974           model->dir_thaw_source = 0;
975           _gtk_file_system_model_thaw_updates (model);
976         }
977
978       if (error)
979         g_error_free (error);
980       else
981         g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, NULL);
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   g_assert (n_columns > 0);
1094
1095   model->n_columns = n_columns;
1096   model->column_types = g_slice_alloc (sizeof (GType) * n_columns);
1097
1098   model->node_size = sizeof (FileModelNode) + sizeof (GValue) * (n_columns - 1); /* minus 1 because FileModelNode.values[] has a default size of 1 */
1099
1100   for (i = 0; i < (guint) n_columns; i++)
1101     {
1102       GType type = va_arg (args, GType);
1103       if (! _gtk_tree_data_list_check_type (type))
1104         {
1105           g_error ("%s: type %s cannot be a column type for GtkFileSystemModel\n", G_STRLOC, g_type_name (type));
1106           return; /* not reached */
1107         }
1108
1109       model->column_types[i] = type;
1110     }
1111
1112   model->sort_list = _gtk_tree_data_list_header_new (n_columns, model->column_types);
1113
1114   model->files = g_array_sized_new (FALSE, FALSE, model->node_size, FILES_PER_QUERY);
1115   /* add editable node at start */
1116   g_array_set_size (model->files, 1);
1117   memset (get_node (model, 0), 0, model->node_size);
1118 }
1119
1120 static void
1121 gtk_file_system_model_set_directory (GtkFileSystemModel *model,
1122                                      GFile *             dir,
1123                                      const gchar *       attributes)
1124 {
1125   g_assert (G_IS_FILE (dir));
1126
1127   model->dir = g_object_ref (dir);
1128   model->attributes = g_strdup (attributes);
1129
1130   g_object_ref (model);
1131   g_file_enumerate_children_async (model->dir,
1132                                    attributes,
1133                                    0,
1134                                    IO_PRIORITY,
1135                                    model->cancellable,
1136                                    gtk_file_system_model_got_enumerator,
1137                                    model);
1138
1139 }
1140
1141 static GtkFileSystemModel *
1142 _gtk_file_system_model_new_valist (GtkFileSystemModelGetValue get_func,
1143                                    gpointer            get_data,
1144                                    guint               n_columns,
1145                                    va_list             args)
1146 {
1147   GtkFileSystemModel *model;
1148
1149   model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
1150   model->get_func = get_func;
1151   model->get_data = get_data;
1152
1153   gtk_file_system_model_set_n_columns (model, n_columns, args);
1154
1155   return model;
1156 }
1157
1158 /**
1159  * _gtk_file_system_model_new:
1160  * @get_func: function to call for getting a value
1161  * @get_data: user data argument passed to @get_func
1162  * @n_columns: number of columns
1163  * @...: @n_columns #GType types for the columns
1164  *
1165  * Creates a new #GtkFileSystemModel object. You need to add files
1166  * to the list using _gtk_file_system_model_add_file().
1167  *
1168  * Return value: the newly created #GtkFileSystemModel
1169  **/
1170 GtkFileSystemModel *
1171 _gtk_file_system_model_new (GtkFileSystemModelGetValue get_func,
1172                             gpointer            get_data,
1173                             guint               n_columns,
1174                             ...)
1175 {
1176   GtkFileSystemModel *model;
1177   va_list args;
1178
1179   g_return_val_if_fail (get_func != NULL, NULL);
1180   g_return_val_if_fail (n_columns > 0, NULL);
1181
1182   va_start (args, n_columns);
1183   model = _gtk_file_system_model_new_valist (get_func, get_data, n_columns, args);
1184   va_end (args);
1185
1186   return model;
1187 }
1188
1189 /**
1190  * _gtk_file_system_model_new_for_directory:
1191  * @directory: the directory to show.
1192  * @attributes: attributes to immediately load or %NULL for all
1193  * @get_func: function that the model should call to query data about a file
1194  * @get_data: user data to pass to the @get_func
1195  * @n_columns: number of columns
1196  * @...: @n_columns #GType types for the columns
1197  *
1198  * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
1199  * object wraps the given @directory as a #GtkTreeModel.
1200  * The model will query the given directory with the given @attributes
1201  * and add all files inside the directory automatically. If supported,
1202  * it will also monitor the drectory and update the model's
1203  * contents to reflect changes, if the @directory supports monitoring.
1204  * 
1205  * Return value: the newly created #GtkFileSystemModel
1206  **/
1207 GtkFileSystemModel *
1208 _gtk_file_system_model_new_for_directory (GFile *                    dir,
1209                                           const gchar *              attributes,
1210                                           GtkFileSystemModelGetValue get_func,
1211                                           gpointer                   get_data,
1212                                           guint                      n_columns,
1213                                           ...)
1214 {
1215   GtkFileSystemModel *model;
1216   va_list args;
1217
1218   g_return_val_if_fail (G_IS_FILE (dir), NULL);
1219   g_return_val_if_fail (get_func != NULL, NULL);
1220   g_return_val_if_fail (n_columns > 0, NULL);
1221
1222   va_start (args, n_columns);
1223   model = _gtk_file_system_model_new_valist (get_func, get_data, n_columns, args);
1224   va_end (args);
1225
1226   gtk_file_system_model_set_directory (model, dir, attributes);
1227
1228   return model;
1229 }
1230
1231 static void
1232 gtk_file_system_model_refilter_all (GtkFileSystemModel *model)
1233 {
1234   guint i;
1235
1236   if (model->frozen)
1237     {
1238       model->filter_on_thaw = TRUE;
1239       return;
1240     }
1241
1242   _gtk_file_system_model_freeze_updates (model);
1243
1244   /* start at index 1, don't change the editable */
1245   for (i = 1; i < model->files->len; i++)
1246     {
1247       node_set_visible (model, i, node_should_be_visible (model, i));
1248     }
1249
1250   model->filter_on_thaw = FALSE;
1251   _gtk_file_system_model_thaw_updates (model);
1252 }
1253
1254 /**
1255  * _gtk_file_system_model_set_show_hidden:
1256  * @model: a #GtkFileSystemModel
1257  * @show_hidden: whether hidden files should be displayed
1258  * 
1259  * Sets whether hidden files should be included in the #GtkTreeModel
1260  * for display.
1261  **/
1262 void
1263 _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
1264                                         gboolean            show_hidden)
1265 {
1266   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1267
1268   show_hidden = show_hidden != FALSE;
1269
1270   if (show_hidden != model->show_hidden)
1271     {
1272       model->show_hidden = show_hidden;
1273       gtk_file_system_model_refilter_all (model);
1274     }
1275 }
1276
1277 /**
1278  * _gtk_file_system_model_set_show_folders:
1279  * @model: a #GtkFileSystemModel
1280  * @show_folders: whether folders should be displayed
1281  * 
1282  * Sets whether folders should be included in the #GtkTreeModel for
1283  * display.
1284  **/
1285 void
1286 _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
1287                                          gboolean            show_folders)
1288 {
1289   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1290
1291   show_folders = show_folders != FALSE;
1292
1293   if (show_folders != model->show_folders)
1294     {
1295       model->show_folders = show_folders;
1296       gtk_file_system_model_refilter_all (model);
1297     }
1298 }
1299
1300 /**
1301  * _gtk_file_system_model_set_show_files:
1302  * @model: a #GtkFileSystemModel
1303  * @show_files: whether files (as opposed to folders) should
1304  *              be displayed.
1305  * 
1306  * Sets whether files (as opposed to folders) should be included
1307  * in the #GtkTreeModel for display.
1308  **/
1309 void
1310 _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
1311                                        gboolean            show_files)
1312 {
1313   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1314
1315   show_files = show_files != FALSE;
1316
1317   if (show_files != model->show_files)
1318     {
1319       model->show_files = show_files;
1320       gtk_file_system_model_refilter_all (model);
1321     }
1322 }
1323
1324 /**
1325  * _gtk_file_system_model_get_cancellable:
1326  * @model: the model
1327  *
1328  * Gets the cancellable used by the @model. This is the cancellable used
1329  * internally by the @model that will be cancelled when @model is 
1330  * disposed. So you can use it for operations that should be cancelled
1331  * when the model goes away.
1332  *
1333  * Returns: The cancellable used by @model
1334  **/
1335 GCancellable *
1336 _gtk_file_system_model_get_cancellable (GtkFileSystemModel *model)
1337 {
1338   g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL);
1339
1340   return model->cancellable;
1341 }
1342
1343 /**
1344  * _gtk_file_system_model_get_is_visible:
1345  * @model: the model
1346  * @iter: a valid iterator
1347  *
1348  * Checks if the iterator is visible. A visible iterator references
1349  * a row that is currently exposed using the #GtkTreeModel API. If
1350  * the iterator is invisible, it references a file that is not shown
1351  * for some reason, such as being filtered by the current filter or
1352  * being a hidden file.
1353  *
1354  * Returns: %TRUE if the iterator is visible
1355  **/
1356 gboolean
1357 _gtk_file_system_model_get_is_visible (GtkFileSystemModel *model,
1358                                        GtkTreeIter        *iter)
1359 {
1360   FileModelNode *node;
1361
1362   g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), FALSE);
1363   g_return_val_if_fail (iter != NULL, FALSE);
1364
1365   node = get_node (model, ITER_INDEX (iter));
1366   return node->visible;
1367 }
1368
1369 /**
1370  * _gtk_file_system_model_get_info:
1371  * @model: a #GtkFileSystemModel
1372  * @iter: a #GtkTreeIter pointing to a row of @model
1373  * 
1374  * Gets the #GFileInfo structure for a particular row
1375  * of @model.
1376  * 
1377  * Return value: a #GFileInfo structure. This structure
1378  *   is owned by @model and must not be modified or freed.
1379  *   If you want to keep the information for later use,
1380  *   you must take a reference, since the structure may be
1381  *   freed on later changes to the file system.  If you have
1382  *   called _gtk_file_system_model_add_editable() and the @iter
1383  *   corresponds to the row that this function returned, the
1384  *   return value will be NULL.
1385  **/
1386 GFileInfo *
1387 _gtk_file_system_model_get_info (GtkFileSystemModel *model,
1388                                  GtkTreeIter        *iter)
1389 {
1390   FileModelNode *node;
1391
1392   g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL);
1393   g_return_val_if_fail (iter != NULL, NULL);
1394
1395   node = get_node (model, ITER_INDEX (iter));
1396   g_assert (node->info == NULL || G_IS_FILE_INFO (node->info));
1397   return node->info;
1398 }
1399
1400 /**
1401  * _gtk_file_system_model_get_file:
1402  * @model: a #GtkFileSystemModel
1403  * @iter: a #GtkTreeIter pointing to a row of @model
1404  * 
1405  * Gets the file for a particular row in @model. 
1406  *
1407  * Return value: the file. This object is owned by @model and
1408  *   or freed. If you want to save the path for later use,
1409  *   you must take a ref, since the object may be freed
1410  *   on later changes to the file system.
1411  **/
1412 GFile *
1413 _gtk_file_system_model_get_file (GtkFileSystemModel *model,
1414                                  GtkTreeIter        *iter)
1415 {
1416   FileModelNode *node;
1417
1418   g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL);
1419
1420   node = get_node (model, ITER_INDEX (iter));
1421   return node->file;
1422 }
1423
1424 /**
1425  * _gtk_file_system_model_get_value:
1426  * @model: a #GtkFileSystemModel
1427  * @iter: a #GtkTreeIter pointing to a row of @model
1428  * @column: the column to get the value for
1429  *
1430  * Gets the value associated with the given row @iter and @column.
1431  * If no value is available yet and the default value should be used,
1432  * %NULL is returned.
1433  * This is a performance optimization for the calls 
1434  * gtk_tree_model_get() or gtk_tree_model_get_value(), which copy 
1435  * the value and spend a considerable amount of time in iterator 
1436  * lookups. Both of which are slow.
1437  *
1438  * Returns: a pointer to the actual value as stored in @model or %NULL
1439  *          if no value available yet.
1440  **/
1441 const GValue *
1442 _gtk_file_system_model_get_value (GtkFileSystemModel *model,
1443                                   GtkTreeIter *       iter,
1444                                   int                 column)
1445 {
1446   FileModelNode *node;
1447
1448   g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL);
1449   g_return_val_if_fail (column >= 0 && (guint) column < model->n_columns, NULL);
1450
1451   node = get_node (model, ITER_INDEX (iter));
1452     
1453   if (!G_VALUE_TYPE (&node->values[column]))
1454     {
1455       g_value_init (&node->values[column], model->column_types[column]);
1456       if (!model->get_func (model, 
1457                             node->file, 
1458                             node->info, 
1459                             column, 
1460                             &node->values[column],
1461                             model->get_data))
1462         {
1463           g_value_unset (&node->values[column]);
1464           return NULL;
1465         }
1466     }
1467   
1468   return &node->values[column];
1469 }
1470
1471 static guint
1472 node_get_for_file (GtkFileSystemModel *model,
1473                    GFile *             file)
1474 {
1475   guint i;
1476
1477   i = GPOINTER_TO_UINT (g_hash_table_lookup (model->file_lookup, file));
1478   if (i != 0)
1479     return i;
1480
1481   /* node 0 is the editable row and has no associated file or entry in the table */
1482   for (i = g_hash_table_size (model->file_lookup) + 1; i < model->files->len; i++)
1483     {
1484       FileModelNode *node = get_node (model, i);
1485
1486       g_hash_table_insert (model->file_lookup, node->file, GUINT_TO_POINTER (i));
1487       if (g_file_equal (node->file, file))
1488         return i;
1489     }
1490
1491   return 0;
1492 }
1493
1494 /**
1495  * _gtk_file_system_model_get_iter_for_file:
1496  * @model: the model
1497  * @iter: the iterator to be initialized
1498  * @file: the file to look up
1499  *
1500  * Initializes @iter to point to the row used for @file, if @file is part 
1501  * of the model. Note that upon successful return, @iter may point to an 
1502  * invisible row in the @model. Use 
1503  * _gtk_file_system_model_get_is_visible() to make sure it is visible to
1504  * the tree view.
1505  *
1506  * Returns: %TRUE if file is part of the model and @iter was initialized
1507  **/
1508 gboolean
1509 _gtk_file_system_model_get_iter_for_file (GtkFileSystemModel *model,
1510                                           GtkTreeIter        *iter,
1511                                           GFile *             file)
1512 {
1513   guint i;
1514
1515   g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), FALSE);
1516   g_return_val_if_fail (iter != NULL, FALSE);
1517   g_return_val_if_fail (G_IS_FILE (file), FALSE);
1518
1519   i = node_get_for_file (model, file);
1520
1521   if (i == 0)
1522     return FALSE;
1523
1524   ITER_INIT_FROM_INDEX (model, iter, i);
1525   return TRUE;
1526 }
1527
1528 /**
1529  * _gtk_file_system_model_add_file:
1530  * @model: the model
1531  * @file: the file to add
1532  * @info: the information to associate with the file
1533  *
1534  * Adds the given @file with its associated @info to the @model. 
1535  * If the model is frozen, the file will only show up after it is thawn.
1536  **/
1537 void
1538 _gtk_file_system_model_add_file (GtkFileSystemModel *model,
1539                                  GFile              *file,
1540                                  GFileInfo          *info)
1541 {
1542   FileModelNode *node;
1543   
1544   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1545   g_return_if_fail (G_IS_FILE (file));
1546   g_return_if_fail (G_IS_FILE_INFO (info));
1547
1548   node = g_slice_alloc0 (model->node_size);
1549   node->file = g_object_ref (file);
1550   if (info)
1551     node->info = g_object_ref (info);
1552   node->frozen_add = model->frozen ? TRUE : FALSE;
1553
1554   g_array_append_vals (model->files, node, 1);
1555   g_slice_free1 (model->node_size, node);
1556
1557   if (!model->frozen)
1558     node_set_visible (model, model->files->len -1,
1559                       node_should_be_visible (model, model->files->len - 1));
1560   gtk_file_system_model_sort_node (model, model->files->len -1);
1561 }
1562
1563 /**
1564  * _gtk_file_system_model_remove_file:
1565  * @model: the model
1566  * @file: file to remove from the model. The file must have been 
1567  *        added to the model previously
1568  *
1569  * Removes the given file from the model. If the file is not part of 
1570  * @model, this function does nothing.
1571  **/
1572 void
1573 _gtk_file_system_model_remove_file (GtkFileSystemModel *model,
1574                                    GFile              *file)
1575 {
1576   FileModelNode *node;
1577   guint id;
1578
1579   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1580   g_return_if_fail (G_IS_FILE (file));
1581
1582   id = node_get_for_file (model, file);
1583   if (id == 0)
1584     return;
1585
1586   node = get_node (model, id);
1587   node_set_visible (model, id, FALSE);
1588   g_object_unref (node->file);
1589   if (node->info)
1590     g_object_unref (node->info);
1591   g_array_remove_index (model->files, id);
1592 }
1593
1594 /**
1595  * _gtk_file_system_model_update_file:
1596  * @model: the model
1597  * @file: the file
1598  * @info: the new file info
1599  * @requires_resort: FIXME: get rid of this argument
1600  *
1601  * Tells the file system model that the file changed and that the 
1602  * new @info should be used for it now.  If the file is not part of 
1603  * @model, it will get added automatically.
1604  **/
1605 void
1606 _gtk_file_system_model_update_file (GtkFileSystemModel *model,
1607                                     GFile              *file,
1608                                     GFileInfo          *info,
1609                                     gboolean            requires_resort)
1610 {
1611   FileModelNode *node;
1612   guint i, id;
1613
1614   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1615   g_return_if_fail (G_IS_FILE (file));
1616   g_return_if_fail (G_IS_FILE_INFO (info));
1617
1618   id = node_get_for_file (model, file);
1619   if (id == 0)
1620     _gtk_file_system_model_add_file (model, file, info);
1621
1622   node = get_node (model, id);
1623   if (node->info)
1624     g_object_unref (node->info);
1625   node->info = g_object_ref (info);
1626   for (i = 0; i < model->n_columns; i++)
1627     {
1628       if (G_VALUE_TYPE (&node->values[i]))
1629         g_value_unset (&node->values[i]);
1630     }
1631
1632   if (node->visible)
1633     {
1634       GtkTreePath *path;
1635       GtkTreeIter iter;
1636       
1637       path = gtk_tree_path_new_from_node (model, id);
1638       ITER_INIT_FROM_INDEX (model, &iter, id);
1639       gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
1640       gtk_tree_path_free (path);
1641     }
1642
1643   if (requires_resort)
1644     gtk_file_system_model_sort_node (model, id);
1645 }
1646
1647 /**
1648  * _gtk_file_system_model_set_filter:
1649  * @mode: a #GtkFileSystemModel
1650  * @filter: %NULL or filter to use
1651  * 
1652  * Sets a filter to be used for deciding if a row should be visible or not.
1653  * Directories are always visible.
1654  **/
1655 void
1656 _gtk_file_system_model_set_filter (GtkFileSystemModel      *model,
1657                                    GtkFileFilter *          filter)
1658 {
1659   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1660   g_return_if_fail (filter == NULL || GTK_IS_FILE_FILTER (filter));
1661   
1662   if (filter)
1663     g_object_ref (filter);
1664   if (model->filter)
1665     g_object_unref (model->filter);
1666   model->filter = filter;
1667
1668   gtk_file_system_model_refilter_all (model);
1669 }
1670
1671 /**
1672  * _gtk_file_system_model_add_editable:
1673  * @model: a #GtkFileSystemModel
1674  * @iter: Location to return the iter corresponding to the editable row
1675  * 
1676  * Adds an "empty" row at the beginning of the model.  This does not refer to
1677  * any file, but is a temporary placeholder for a file name that the user will
1678  * type when a corresponding cell is made editable.  When your code is done
1679  * using this temporary row, call _gtk_file_system_model_remove_editable().
1680  **/
1681 void
1682 _gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *iter)
1683 {
1684   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1685   g_return_if_fail (!get_node (model, 0)->visible);
1686
1687   node_set_visible (model, 0, TRUE);
1688   ITER_INIT_FROM_INDEX (model, iter, 0);
1689 }
1690
1691 /**
1692  * _gtk_file_system_model_remove_editable:
1693  * @model: a #GtkFileSystemModel
1694  * 
1695  * Removes the "empty" row at the beginning of the model that was
1696  * created with _gtk_file_system_model_add_editable().  You should call
1697  * this function when your code is finished editing this temporary row.
1698  **/
1699 void
1700 _gtk_file_system_model_remove_editable (GtkFileSystemModel *model)
1701 {
1702   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1703   g_return_if_fail (get_node (model, 0)->visible);
1704
1705   node_set_visible (model, 0, FALSE);
1706 }
1707
1708 /**
1709  * _gtk_file_system_model_freeze_updates:
1710  * @model: a #GtkFileSystemModel
1711  *
1712  * Freezes most updates on the model, so that performing multiple 
1713  * operations on the files in the model do not cause any events.
1714  * Use _gtk_file_system_model_thaw_updates() to resume proper 
1715  * operations. It is fine to call this function multiple times as
1716  * long as freeze and thaw calls are balanced.
1717  **/
1718 void
1719 _gtk_file_system_model_freeze_updates (GtkFileSystemModel *model)
1720 {
1721   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1722
1723   model->frozen++;
1724 }
1725
1726 /**
1727  * _gtk_file_system_model_thaw_updates:
1728  * @model: a #GtkFileSystemModel
1729  *
1730  * Undoes the effect of a previous call to
1731  * _gtk_file_system_model_freeze_updates() 
1732  **/
1733 void
1734 _gtk_file_system_model_thaw_updates (GtkFileSystemModel *model)
1735 {
1736   gboolean stuff_added;
1737
1738   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1739   g_return_if_fail (model->frozen > 0);
1740
1741   model->frozen--;
1742   if (model->frozen > 0)
1743     return;
1744
1745   stuff_added = get_node (model, model->files->len - 1)->frozen_add;
1746
1747   if (model->filter_on_thaw)
1748     gtk_file_system_model_refilter_all (model);
1749   if (model->sort_on_thaw)
1750     gtk_file_system_model_sort (model);
1751   if (stuff_added)
1752     {
1753       guint i;
1754
1755       for (i = 0; i < model->files->len; i++)
1756         {
1757           FileModelNode *node = get_node (model, i);
1758
1759           if (!node->frozen_add)
1760             continue;
1761           node->frozen_add = FALSE;
1762           node_set_visible (model, i, node_should_be_visible (model, i));
1763         }
1764     }
1765 }
1766
1767 /**
1768  * _gtk_file_system_model_clear_cached_values:
1769  * @model: a #GtkFileSystemModel
1770  * @column: the column to clear or -1 for all columns
1771  *
1772  * Clears the cached values in the model for the given @column. Use 
1773  * this function whenever your get_value function would return different
1774  * values for a column.
1775  * The file chooser uses this for example when the icon theme changes to 
1776  * invalidate the cached pixbufs.
1777  **/
1778 void
1779 _gtk_file_system_model_clear_cache (GtkFileSystemModel *model,
1780                                     int                 column)
1781 {
1782   guint i;
1783   int start, end;
1784   gboolean changed;
1785
1786   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1787   g_return_if_fail (column >= -1 && (guint) column < model->n_columns);
1788
1789   if (column)
1790     {
1791       start = column;
1792       end = column + 1;
1793     }
1794   else
1795     {
1796       start = 0;
1797       end = model->n_columns;
1798     }
1799
1800   for (i = 0; i < model->files->len; i++)
1801     {
1802       FileModelNode *node = get_node (model, i);
1803       changed = FALSE;
1804       for (column = start; column < end; column++)
1805         {
1806           if (!G_VALUE_TYPE (&node->values[column]))
1807             continue;
1808           
1809           g_value_unset (&node->values[column]);
1810           changed = TRUE;
1811         }
1812
1813       if (changed && node->visible)
1814         {
1815           GtkTreePath *path;
1816           GtkTreeIter iter;
1817           
1818           path = gtk_tree_path_new_from_node (model, i);
1819           ITER_INIT_FROM_INDEX (model, &iter, i);
1820           gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
1821           gtk_tree_path_free (path);
1822         }
1823     }
1824
1825   /* FIXME: resort? */
1826 }
1827
1828 /**
1829  * _gtk_file_system_model_add_and_query_file:
1830  * @model: a #GtkFileSystemModel
1831  * @file: the file to add
1832  * @attributes: attributes to query before adding the file
1833  *
1834  * This is a conenience function that calls g_file_query_info_async() on 
1835  * the given file, and when successful, adds it to the model with
1836  * _gtk_file_system_model_add_file(). Upon failure, the @file is discarded.
1837  **/
1838 void
1839 _gtk_file_system_model_add_and_query_file (GtkFileSystemModel *model,
1840                                            GFile *             file,
1841                                            const char *        attributes)
1842 {
1843   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1844   g_return_if_fail (G_IS_FILE (file));
1845   g_return_if_fail (attributes != NULL);
1846
1847   g_file_query_info_async (file,
1848                            attributes,
1849                            0,
1850                            IO_PRIORITY,
1851                            model->cancellable,
1852                            gtk_file_system_model_query_done,
1853                            model);
1854 }