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