]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystem.c
bgo#514843 - [filechooser] Deal with corrupted .gtk-bookmarks gracefully
[~andy/gtk] / gtk / gtkfilesystem.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilesystem.c: Filesystem abstraction functions.
3  * Copyright (C) 2003, Red Hat, Inc.
4  * Copyright (C) 2007-2008 Carlos Garnacho
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Authors: Carlos Garnacho <carlos@imendio.com>
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include <glib/gi18n-lib.h>
28
29 #include "gtkfilechooser.h"
30 #include "gtkfilesystem.h"
31 #include "gtkicontheme.h"
32 #include "gtkprivate.h"
33
34 /* #define DEBUG_MODE */
35 #ifdef DEBUG_MODE
36 #define DEBUG(x) g_debug (x);
37 #else
38 #define DEBUG(x)
39 #endif
40
41 #define FILES_PER_QUERY 100
42
43 /* The pointers we return for a GtkFileSystemVolume are opaque tokens; they are
44  * really pointers to GDrive, GVolume or GMount objects.  We need an extra
45  * token for the fake "File System" volume.  So, we'll return a pointer to
46  * this particular string.
47  */
48 static const gchar *root_volume_token = N_("File System");
49 #define IS_ROOT_VOLUME(volume) ((gpointer) (volume) == (gpointer) root_volume_token)
50
51 enum {
52   PROP_0,
53   PROP_FILE,
54   PROP_ENUMERATOR,
55   PROP_ATTRIBUTES
56 };
57
58 enum {
59   BOOKMARKS_CHANGED,
60   VOLUMES_CHANGED,
61   FS_LAST_SIGNAL
62 };
63
64 enum {
65   FILES_ADDED,
66   FILES_REMOVED,
67   FILES_CHANGED,
68   FINISHED_LOADING,
69   DELETED,
70   FOLDER_LAST_SIGNAL
71 };
72
73 static guint fs_signals [FS_LAST_SIGNAL] = { 0, };
74 static guint folder_signals [FOLDER_LAST_SIGNAL] = { 0, };
75
76 typedef struct AsyncFuncData AsyncFuncData;
77
78 struct GtkFileSystemPrivate
79 {
80   GVolumeMonitor *volume_monitor;
81
82   /* This list contains elements that can be
83    * of type GDrive, GVolume and GMount
84    */
85   GSList *volumes;
86
87   /* This list contains GtkFileSystemBookmark structs */
88   GSList *bookmarks;
89
90   GFileMonitor *bookmarks_monitor;
91 };
92
93 struct GtkFolderPrivate
94 {
95   GFile *folder_file;
96   GHashTable *children;
97   GFileMonitor *directory_monitor;
98   GFileEnumerator *enumerator;
99   GCancellable *cancellable;
100   gchar *attributes;
101
102   guint finished_loading : 1;
103 };
104
105 struct AsyncFuncData
106 {
107   GtkFileSystem *file_system;
108   GFile *file;
109   GtkFolder *folder;
110   GCancellable *cancellable;
111   gchar *attributes;
112
113   gpointer callback;
114   gpointer data;
115 };
116
117 struct GtkFileSystemBookmark
118 {
119   GFile *file;
120   gchar *label;
121 };
122
123 G_DEFINE_TYPE (GtkFileSystem, _gtk_file_system, G_TYPE_OBJECT)
124
125 G_DEFINE_TYPE (GtkFolder, _gtk_folder, G_TYPE_OBJECT)
126
127
128 static void gtk_folder_set_finished_loading (GtkFolder *folder,
129                                              gboolean   finished_loading);
130 static void gtk_folder_add_file             (GtkFolder *folder,
131                                              GFile     *file,
132                                              GFileInfo *info);
133
134
135 /* GtkFileSystemBookmark methods */
136 void
137 _gtk_file_system_bookmark_free (GtkFileSystemBookmark *bookmark)
138 {
139   g_object_unref (bookmark->file);
140   g_free (bookmark->label);
141   g_slice_free (GtkFileSystemBookmark, bookmark);
142 }
143
144 /* GtkFileSystem methods */
145 static void
146 volumes_changed (GVolumeMonitor *volume_monitor,
147                  gpointer        volume,
148                  gpointer        user_data)
149 {
150   GtkFileSystem *file_system;
151
152   gdk_threads_enter ();
153
154   file_system = GTK_FILE_SYSTEM (user_data);
155   g_signal_emit (file_system, fs_signals[VOLUMES_CHANGED], 0, volume);
156   gdk_threads_leave ();
157 }
158
159 static void
160 gtk_file_system_dispose (GObject *object)
161 {
162   GtkFileSystem *file_system = GTK_FILE_SYSTEM (object);
163   GtkFileSystemPrivate *priv = file_system->priv;
164
165   DEBUG ("dispose");
166
167   if (priv->volumes)
168     {
169       g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
170       g_slist_free (priv->volumes);
171       priv->volumes = NULL;
172     }
173
174   if (priv->volume_monitor)
175     {
176       g_signal_handlers_disconnect_by_func (priv->volume_monitor, volumes_changed, object);
177       g_object_unref (priv->volume_monitor);
178       priv->volume_monitor = NULL;
179     }
180
181   G_OBJECT_CLASS (_gtk_file_system_parent_class)->dispose (object);
182 }
183
184 static void
185 gtk_file_system_finalize (GObject *object)
186 {
187   GtkFileSystem *file_system = GTK_FILE_SYSTEM (object);
188   GtkFileSystemPrivate *priv = file_system->priv;
189
190   DEBUG ("finalize");
191
192   if (priv->bookmarks_monitor)
193     g_object_unref (priv->bookmarks_monitor);
194
195   if (priv->bookmarks)
196     {
197       g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL);
198       g_slist_free (priv->bookmarks);
199     }
200
201   G_OBJECT_CLASS (_gtk_file_system_parent_class)->finalize (object);
202 }
203
204 static void
205 _gtk_file_system_class_init (GtkFileSystemClass *class)
206 {
207   GObjectClass *object_class = G_OBJECT_CLASS (class);
208
209   object_class->dispose = gtk_file_system_dispose;
210   object_class->finalize = gtk_file_system_finalize;
211
212   fs_signals[BOOKMARKS_CHANGED] =
213     g_signal_new ("bookmarks-changed",
214                   G_TYPE_FROM_CLASS (object_class),
215                   G_SIGNAL_RUN_LAST,
216                   G_STRUCT_OFFSET (GtkFileSystemClass, bookmarks_changed),
217                   NULL, NULL,
218                   g_cclosure_marshal_VOID__VOID,
219                   G_TYPE_NONE, 0);
220
221   fs_signals[VOLUMES_CHANGED] =
222     g_signal_new ("volumes-changed",
223                   G_TYPE_FROM_CLASS (object_class),
224                   G_SIGNAL_RUN_LAST,
225                   G_STRUCT_OFFSET (GtkFileSystemClass, volumes_changed),
226                   NULL, NULL,
227                   g_cclosure_marshal_VOID__VOID,
228                   G_TYPE_NONE, 0);
229
230   g_type_class_add_private (object_class, sizeof (GtkFileSystemPrivate));
231 }
232
233 static GFile *
234 get_bookmarks_file (void)
235 {
236   GFile *file;
237   gchar *filename;
238
239   filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
240   file = g_file_new_for_path (filename);
241   g_free (filename);
242
243   return file;
244 }
245
246 static GSList *
247 read_bookmarks (GFile *file)
248 {
249   gchar *contents;
250   gchar **lines, *space;
251   GSList *bookmarks = NULL;
252   gint i;
253
254   if (!g_file_load_contents (file, NULL, &contents,
255                              NULL, NULL, NULL))
256     return NULL;
257
258   lines = g_strsplit (contents, "\n", -1);
259
260   for (i = 0; lines[i]; i++)
261     {
262       GtkFileSystemBookmark *bookmark;
263
264       if (!*lines[i])
265         continue;
266
267       if (!g_utf8_validate (lines[i], -1, NULL))
268         continue;
269
270       bookmark = g_slice_new0 (GtkFileSystemBookmark);
271
272       if ((space = strchr (lines[i], ' ')) != NULL)
273         {
274           space[0] = '\0';
275           bookmark->label = g_strdup (space + 1);
276         }
277
278       bookmark->file = g_file_new_for_uri (lines[i]);
279       bookmarks = g_slist_prepend (bookmarks, bookmark);
280     }
281
282   bookmarks = g_slist_reverse (bookmarks);
283   g_strfreev (lines);
284   g_free (contents);
285
286   return bookmarks;
287 }
288
289 static void
290 save_bookmarks (GFile  *bookmarks_file,
291                 GSList *bookmarks)
292 {
293   GError *error = NULL;
294   GString *contents;
295   GSList *l;
296
297   contents = g_string_new ("");
298
299   for (l = bookmarks; l; l = l->next)
300     {
301       GtkFileSystemBookmark *bookmark = l->data;
302       gchar *uri;
303
304       uri = g_file_get_uri (bookmark->file);
305       if (!uri)
306         continue;
307
308       g_string_append (contents, uri);
309
310       if (bookmark->label)
311         g_string_append_printf (contents, " %s", bookmark->label);
312
313       g_string_append_c (contents, '\n');
314       g_free (uri);
315     }
316
317   if (!g_file_replace_contents (bookmarks_file,
318                                 contents->str,
319                                 strlen (contents->str),
320                                 NULL, FALSE, 0, NULL,
321                                 NULL, &error))
322     {
323       g_critical ("%s", error->message);
324       g_error_free (error);
325     }
326
327   g_string_free (contents, TRUE);
328 }
329
330 static void
331 bookmarks_file_changed (GFileMonitor      *monitor,
332                         GFile             *file,
333                         GFile             *other_file,
334                         GFileMonitorEvent  event,
335                         gpointer           data)
336 {
337   GtkFileSystem *file_system = GTK_FILE_SYSTEM (data);
338   GtkFileSystemPrivate *priv = file_system->priv;
339
340   switch (event)
341     {
342     case G_FILE_MONITOR_EVENT_CHANGED:
343     case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
344     case G_FILE_MONITOR_EVENT_CREATED:
345     case G_FILE_MONITOR_EVENT_DELETED:
346       g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL);
347       g_slist_free (priv->bookmarks);
348
349       priv->bookmarks = read_bookmarks (file);
350
351       gdk_threads_enter ();
352       g_signal_emit (data, fs_signals[BOOKMARKS_CHANGED], 0);
353       gdk_threads_leave ();
354       break;
355     default:
356       /* ignore at the moment */
357       break;
358     }
359 }
360
361 static gboolean
362 mount_referenced_by_volume_activation_root (GList *volumes, GMount *mount)
363 {
364   GList *l;
365   GFile *mount_root;
366   gboolean ret;
367
368   ret = FALSE;
369
370   mount_root = g_mount_get_root (mount);
371
372   for (l = volumes; l != NULL; l = l->next)
373     {
374       GVolume *volume = G_VOLUME (l->data);
375       GFile *volume_activation_root;
376
377       volume_activation_root = g_volume_get_activation_root (volume);
378       if (volume_activation_root != NULL)
379         {
380           if (g_file_has_prefix (volume_activation_root, mount_root))
381             {
382               ret = TRUE;
383               g_object_unref (volume_activation_root);
384               break;
385             }
386           g_object_unref (volume_activation_root);
387         }
388     }
389
390   g_object_unref (mount_root);
391   return ret;
392 }
393
394 static void
395 get_volumes_list (GtkFileSystem *file_system)
396 {
397   GtkFileSystemPrivate *priv = file_system->priv;
398   GList *l, *ll;
399   GList *drives;
400   GList *volumes;
401   GList *mounts;
402   GDrive *drive;
403   GVolume *volume;
404   GMount *mount;
405
406   if (priv->volumes)
407     {
408       g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
409       g_slist_free (priv->volumes);
410       priv->volumes = NULL;
411     }
412
413   /* first go through all connected drives */
414   drives = g_volume_monitor_get_connected_drives (priv->volume_monitor);
415
416   for (l = drives; l != NULL; l = l->next)
417     {
418       drive = l->data;
419       volumes = g_drive_get_volumes (drive);
420
421       if (volumes)
422         {
423           for (ll = volumes; ll != NULL; ll = ll->next)
424             {
425               volume = ll->data;
426               mount = g_volume_get_mount (volume);
427
428               if (mount)
429                 {
430                   /* Show mounted volume */
431                   priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
432                   g_object_unref (mount);
433                 }
434               else
435                 {
436                   /* Do show the unmounted volumes in the sidebar;
437                    * this is so the user can mount it (in case automounting
438                    * is off).
439                    *
440                    * Also, even if automounting is enabled, this gives a visual
441                    * cue that the user should remember to yank out the media if
442                    * he just unmounted it.
443                    */
444                   priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
445                 }
446
447               g_object_unref (volume);
448             }
449   
450            g_list_free (volumes);
451         }
452       else if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive))
453         {
454           /* If the drive has no mountable volumes and we cannot detect media change.. we
455            * display the drive in the sidebar so the user can manually poll the drive by
456            * right clicking and selecting "Rescan..."
457            *
458            * This is mainly for drives like floppies where media detection doesn't
459            * work.. but it's also for human beings who like to turn off media detection
460            * in the OS to save battery juice.
461            */
462
463           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (drive));
464         }
465
466       g_object_unref (drive);
467     }
468
469   g_list_free (drives);
470
471   /* add all volumes that is not associated with a drive */
472   volumes = g_volume_monitor_get_volumes (priv->volume_monitor);
473
474   for (l = volumes; l != NULL; l = l->next)
475     {
476       volume = l->data;
477       drive = g_volume_get_drive (volume);
478
479       if (drive)
480         {
481           g_object_unref (drive);
482           continue;
483         }
484
485       mount = g_volume_get_mount (volume);
486
487       if (mount)
488         {
489           /* show this mount */
490           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
491           g_object_unref (mount);
492         }
493       else
494         {
495           /* see comment above in why we add an icon for a volume */
496           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
497         }
498
499       g_object_unref (volume);
500     }
501
502   /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
503   mounts = g_volume_monitor_get_mounts (priv->volume_monitor);
504
505   for (l = mounts; l != NULL; l = l->next)
506     {
507       mount = l->data;
508       volume = g_mount_get_volume (mount);
509
510       if (volume)
511         {
512           g_object_unref (volume);
513           continue;
514         }
515
516       /* if there's exists one or more volumes with an activation root inside the mount,
517        * don't display the mount
518        */
519       if (mount_referenced_by_volume_activation_root (volumes, mount))
520         {
521           g_object_unref (mount);
522           continue;
523         }
524
525       /* show this mount */
526       priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
527       g_object_unref (mount);
528     }
529
530   g_list_free (volumes);
531
532   g_list_free (mounts);
533 }
534
535 static void
536 _gtk_file_system_init (GtkFileSystem *file_system)
537 {
538   GtkFileSystemPrivate *priv;
539   GFile *bookmarks_file;
540   GError *error = NULL;
541
542   DEBUG ("init");
543
544   file_system->priv = G_TYPE_INSTANCE_GET_PRIVATE (file_system,
545                                                    GTK_TYPE_FILE_SYSTEM,
546                                                    GtkFileSystemPrivate);
547   priv = file_system->priv;
548
549   /* Volumes */
550   priv->volume_monitor = g_volume_monitor_get ();
551
552   g_signal_connect (priv->volume_monitor, "mount-added",
553                     G_CALLBACK (volumes_changed), file_system);
554   g_signal_connect (priv->volume_monitor, "mount-removed",
555                     G_CALLBACK (volumes_changed), file_system);
556   g_signal_connect (priv->volume_monitor, "mount-changed",
557                     G_CALLBACK (volumes_changed), file_system);
558   g_signal_connect (priv->volume_monitor, "volume-added",
559                     G_CALLBACK (volumes_changed), file_system);
560   g_signal_connect (priv->volume_monitor, "volume-removed",
561                     G_CALLBACK (volumes_changed), file_system);
562   g_signal_connect (priv->volume_monitor, "volume-changed",
563                     G_CALLBACK (volumes_changed), file_system);
564   g_signal_connect (priv->volume_monitor, "drive-connected",
565                     G_CALLBACK (volumes_changed), file_system);
566   g_signal_connect (priv->volume_monitor, "drive-disconnected",
567                     G_CALLBACK (volumes_changed), file_system);
568   g_signal_connect (priv->volume_monitor, "drive-changed",
569                     G_CALLBACK (volumes_changed), file_system);
570
571   /* Bookmarks */
572   bookmarks_file = get_bookmarks_file ();
573   priv->bookmarks = read_bookmarks (bookmarks_file);
574   priv->bookmarks_monitor = g_file_monitor_file (bookmarks_file,
575                                                  G_FILE_MONITOR_NONE,
576                                                  NULL, &error);
577   if (error)
578     {
579       g_warning ("%s", error->message);
580       g_error_free (error);
581     }
582   else
583     g_signal_connect (priv->bookmarks_monitor, "changed",
584                       G_CALLBACK (bookmarks_file_changed), file_system);
585
586   g_object_unref (bookmarks_file);
587 }
588
589 /* GtkFileSystem public methods */
590 GtkFileSystem *
591 _gtk_file_system_new (void)
592 {
593   return g_object_new (GTK_TYPE_FILE_SYSTEM, NULL);
594 }
595
596 GSList *
597 _gtk_file_system_list_volumes (GtkFileSystem *file_system)
598 {
599   GtkFileSystemPrivate *priv = file_system->priv;
600   GSList *list;
601
602   DEBUG ("list_volumes");
603
604   get_volumes_list (file_system);
605
606   list = g_slist_copy (priv->volumes);
607
608 #ifndef G_OS_WIN32
609   /* Prepend root volume */
610   list = g_slist_prepend (list, (gpointer) root_volume_token);
611 #endif
612
613   return list;
614 }
615
616 GSList *
617 _gtk_file_system_list_bookmarks (GtkFileSystem *file_system)
618 {
619   GtkFileSystemPrivate *priv = file_system->priv;
620   GSList *bookmarks, *files = NULL;
621
622   DEBUG ("list_bookmarks");
623
624   bookmarks = priv->bookmarks;
625
626   while (bookmarks)
627     {
628       GtkFileSystemBookmark *bookmark;
629
630       bookmark = bookmarks->data;
631       bookmarks = bookmarks->next;
632
633       files = g_slist_prepend (files, g_object_ref (bookmark->file));
634     }
635
636   return g_slist_reverse (files);
637 }
638
639 static gboolean
640 is_valid_scheme_character (char c)
641 {
642   return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.';
643 }
644
645 static gboolean
646 has_uri_scheme (const char *str)
647 {
648   const char *p;
649
650   p = str;
651
652   if (!is_valid_scheme_character (*p))
653     return FALSE;
654
655   do
656     p++;
657   while (is_valid_scheme_character (*p));
658
659   return (strncmp (p, "://", 3) == 0);
660 }
661
662 gboolean
663 _gtk_file_system_parse (GtkFileSystem     *file_system,
664                         GFile             *base_file,
665                         const gchar       *str,
666                         GFile            **folder,
667                         gchar            **file_part,
668                         GError           **error)
669 {
670   GFile *file;
671   gboolean result = FALSE;
672   gboolean is_dir = FALSE;
673   gchar *last_slash = NULL;
674   gboolean is_uri;
675
676   DEBUG ("parse");
677
678   if (str && *str)
679     is_dir = (str [strlen (str) - 1] == G_DIR_SEPARATOR);
680
681   last_slash = strrchr (str, G_DIR_SEPARATOR);
682
683   is_uri = has_uri_scheme (str);
684
685   if (is_uri)
686     {
687       const char *colon;
688       const char *slash_after_hostname;
689
690       colon = strchr (str, ':');
691       g_assert (colon != NULL);
692       g_assert (strncmp (colon, "://", 3) == 0);
693
694       slash_after_hostname = strchr (colon + 3, '/');
695
696       if (slash_after_hostname == NULL)
697         {
698           /* We don't have a full hostname yet.  So, don't switch the folder
699            * until we have seen a full hostname.  Otherwise, completion will
700            * happen for every character the user types for the hostname.
701            */
702
703           *folder = NULL;
704           *file_part = NULL;
705           g_set_error (error,
706                        GTK_FILE_CHOOSER_ERROR,
707                        GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME,
708                        "Incomplete hostname");
709           return FALSE;
710         }
711     }
712
713   if (str[0] == '~' || g_path_is_absolute (str) || is_uri)
714     file = g_file_parse_name (str);
715   else
716     {
717       if (base_file)
718         file = g_file_resolve_relative_path (base_file, str);
719       else
720         {
721           *folder = NULL;
722           *file_part = NULL;
723           g_set_error (error,
724                        GTK_FILE_CHOOSER_ERROR,
725                        GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
726                        _("Invalid path"));
727           return FALSE;
728         }
729     }
730
731   if (base_file && g_file_equal (base_file, file))
732     {
733       /* this is when user types '.', could be the
734        * beginning of a hidden file, ./ or ../
735        */
736       *folder = g_object_ref (file);
737       *file_part = g_strdup (str);
738       result = TRUE;
739     }
740   else if (is_dir)
741     {
742       /* it's a dir, or at least it ends with the dir separator */
743       *folder = g_object_ref (file);
744       *file_part = g_strdup ("");
745       result = TRUE;
746     }
747   else
748     {
749       GFile *parent_file;
750
751       parent_file = g_file_get_parent (file);
752
753       if (!parent_file)
754         {
755           g_set_error (error,
756                        GTK_FILE_CHOOSER_ERROR,
757                        GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
758                        "Could not get parent file");
759           *folder = NULL;
760           *file_part = NULL;
761         }
762       else
763         {
764           *folder = parent_file;
765           result = TRUE;
766
767           if (last_slash)
768             *file_part = g_strdup (last_slash + 1);
769           else
770             *file_part = g_strdup (str);
771         }
772     }
773
774   g_object_unref (file);
775
776   return result;
777 }
778
779 static void
780 free_async_data (AsyncFuncData *async_data)
781 {
782   g_object_unref (async_data->file_system);
783   g_object_unref (async_data->file);
784   g_object_unref (async_data->cancellable);
785
786   if (async_data->folder)
787     g_object_unref (async_data->folder);
788
789   g_free (async_data->attributes);
790   g_free (async_data);
791 }
792
793 static void
794 enumerate_children_callback (GObject      *source_object,
795                              GAsyncResult *result,
796                              gpointer      user_data)
797 {
798   GFileEnumerator *enumerator;
799   AsyncFuncData *async_data;
800   GtkFolder *folder = NULL;
801   GFile *file;
802   GError *error = NULL;
803
804   file = G_FILE (source_object);
805   async_data = (AsyncFuncData *) user_data;
806   enumerator = g_file_enumerate_children_finish (file, result, &error);
807
808   if (enumerator)
809     {
810       folder = g_object_new (GTK_TYPE_FOLDER,
811                              "file", source_object,
812                              "enumerator", enumerator,
813                              "attributes", async_data->attributes,
814                              NULL);
815       g_object_unref (enumerator);
816     }
817
818   gdk_threads_enter ();
819   ((GtkFileSystemGetFolderCallback) async_data->callback) (async_data->cancellable,
820                                                            folder, error, async_data->data);
821   gdk_threads_leave ();
822
823   free_async_data (async_data);
824
825   if (error)
826     g_error_free (error);
827 }
828
829 GCancellable *
830 _gtk_file_system_get_folder (GtkFileSystem                  *file_system,
831                              GFile                          *file,
832                              const gchar                    *attributes,
833                              GtkFileSystemGetFolderCallback  callback,
834                              gpointer                        data)
835 {
836   GCancellable *cancellable;
837   AsyncFuncData *async_data;
838
839   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
840   g_return_val_if_fail (G_IS_FILE (file), NULL);
841
842   cancellable = g_cancellable_new ();
843
844   async_data = g_new0 (AsyncFuncData, 1);
845   async_data->file_system = g_object_ref (file_system);
846   async_data->file = g_object_ref (file);
847   async_data->cancellable = g_object_ref (cancellable);
848   async_data->attributes = g_strdup (attributes);
849
850   async_data->callback = callback;
851   async_data->data = data;
852
853   g_file_enumerate_children_async (file,
854                                    attributes,
855                                    G_FILE_QUERY_INFO_NONE,
856                                    G_PRIORITY_DEFAULT,
857                                    cancellable,
858                                    enumerate_children_callback,
859                                    async_data);
860   return cancellable;
861 }
862
863 static void
864 query_info_callback (GObject      *source_object,
865                      GAsyncResult *result,
866                      gpointer      user_data)
867 {
868   AsyncFuncData *async_data;
869   GError *error = NULL;
870   GFileInfo *file_info;
871   GFile *file;
872
873   DEBUG ("query_info_callback");
874
875   file = G_FILE (source_object);
876   async_data = (AsyncFuncData *) user_data;
877   file_info = g_file_query_info_finish (file, result, &error);
878
879   if (async_data->callback)
880     {
881       gdk_threads_enter ();
882       ((GtkFileSystemGetInfoCallback) async_data->callback) (async_data->cancellable,
883                                                              file_info, error, async_data->data);
884       gdk_threads_leave ();
885     }
886
887   if (file_info)
888     g_object_unref (file_info);
889
890   if (error)
891     g_error_free (error);
892
893   free_async_data (async_data);
894 }
895
896 GCancellable *
897 _gtk_file_system_get_info (GtkFileSystem                *file_system,
898                            GFile                        *file,
899                            const gchar                  *attributes,
900                            GtkFileSystemGetInfoCallback  callback,
901                            gpointer                      data)
902 {
903   GCancellable *cancellable;
904   AsyncFuncData *async_data;
905
906   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
907   g_return_val_if_fail (G_IS_FILE (file), NULL);
908
909   cancellable = g_cancellable_new ();
910
911   async_data = g_new0 (AsyncFuncData, 1);
912   async_data->file_system = g_object_ref (file_system);
913   async_data->file = g_object_ref (file);
914   async_data->cancellable = g_object_ref (cancellable);
915
916   async_data->callback = callback;
917   async_data->data = data;
918
919   g_file_query_info_async (file,
920                            attributes,
921                            G_FILE_QUERY_INFO_NONE,
922                            G_PRIORITY_DEFAULT,
923                            cancellable,
924                            query_info_callback,
925                            async_data);
926
927   return cancellable;
928 }
929
930 static void
931 drive_poll_for_media_cb (GObject      *source_object,
932                          GAsyncResult *result,
933                          gpointer      user_data)
934 {
935   AsyncFuncData *async_data;
936   GError *error = NULL;
937
938   g_drive_poll_for_media_finish (G_DRIVE (source_object), result, &error);
939   async_data = (AsyncFuncData *) user_data;
940
941   gdk_threads_enter ();
942   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
943                                                              (GtkFileSystemVolume *) source_object,
944                                                              error, async_data->data);
945   gdk_threads_leave ();
946
947   if (error)
948     g_error_free (error);
949 }
950
951 static void
952 volume_mount_cb (GObject      *source_object,
953                  GAsyncResult *result,
954                  gpointer      user_data)
955 {
956   AsyncFuncData *async_data;
957   GError *error = NULL;
958
959   g_volume_mount_finish (G_VOLUME (source_object), result, &error);
960   async_data = (AsyncFuncData *) user_data;
961
962   gdk_threads_enter ();
963   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
964                                                              (GtkFileSystemVolume *) source_object,
965                                                              error, async_data->data);
966   gdk_threads_leave ();
967
968   if (error)
969     g_error_free (error);
970 }
971
972 GCancellable *
973 _gtk_file_system_mount_volume (GtkFileSystem                    *file_system,
974                                GtkFileSystemVolume              *volume,
975                                GMountOperation                  *mount_operation,
976                                GtkFileSystemVolumeMountCallback  callback,
977                                gpointer                          data)
978 {
979   GCancellable *cancellable;
980   AsyncFuncData *async_data;
981   gboolean handled = FALSE;
982
983   DEBUG ("volume_mount");
984
985   cancellable = g_cancellable_new ();
986
987   async_data = g_new0 (AsyncFuncData, 1);
988   async_data->file_system = g_object_ref (file_system);
989   async_data->cancellable = g_object_ref (cancellable);
990
991   async_data->callback = callback;
992   async_data->data = data;
993
994   if (G_IS_DRIVE (volume))
995     {
996       /* this path happens for drives that are not polled by the OS and where the last media
997        * check indicated that no media was available. So the thing to do here is to
998        * invoke poll_for_media() on the drive
999        */
1000       g_drive_poll_for_media (G_DRIVE (volume), cancellable, drive_poll_for_media_cb, async_data);
1001       handled = TRUE;
1002     }
1003   else if (G_IS_VOLUME (volume))
1004     {
1005       g_volume_mount (G_VOLUME (volume), G_MOUNT_MOUNT_NONE, mount_operation, cancellable, volume_mount_cb, async_data);
1006       handled = TRUE;
1007     }
1008
1009   if (!handled)
1010     free_async_data (async_data);
1011
1012   return cancellable;
1013 }
1014
1015 static void
1016 enclosing_volume_mount_cb (GObject      *source_object,
1017                            GAsyncResult *result,
1018                            gpointer      user_data)
1019 {
1020   GtkFileSystemVolume *volume;
1021   AsyncFuncData *async_data;
1022   GError *error = NULL;
1023
1024   async_data = (AsyncFuncData *) user_data;
1025   g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error);
1026   volume = _gtk_file_system_get_volume_for_file (async_data->file_system, G_FILE (source_object));
1027
1028   /* Silently drop G_IO_ERROR_ALREADY_MOUNTED error for gvfs backends without visible mounts. */
1029   /* Better than doing query_info with additional I/O every time. */
1030   if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED))
1031     g_clear_error (&error);
1032
1033   gdk_threads_enter ();
1034   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable, volume,
1035                                                              error, async_data->data);
1036   gdk_threads_leave ();
1037
1038   if (error)
1039     g_error_free (error);
1040
1041   _gtk_file_system_volume_unref (volume);
1042 }
1043
1044 GCancellable *
1045 _gtk_file_system_mount_enclosing_volume (GtkFileSystem                     *file_system,
1046                                          GFile                             *file,
1047                                          GMountOperation                   *mount_operation,
1048                                          GtkFileSystemVolumeMountCallback   callback,
1049                                          gpointer                           data)
1050 {
1051   GCancellable *cancellable;
1052   AsyncFuncData *async_data;
1053
1054   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
1055   g_return_val_if_fail (G_IS_FILE (file), NULL);
1056
1057   DEBUG ("mount_enclosing_volume");
1058
1059   cancellable = g_cancellable_new ();
1060
1061   async_data = g_new0 (AsyncFuncData, 1);
1062   async_data->file_system = g_object_ref (file_system);
1063   async_data->file = g_object_ref (file);
1064   async_data->cancellable = g_object_ref (cancellable);
1065
1066   async_data->callback = callback;
1067   async_data->data = data;
1068
1069   g_file_mount_enclosing_volume (file,
1070                                  G_MOUNT_MOUNT_NONE,
1071                                  mount_operation,
1072                                  cancellable,
1073                                  enclosing_volume_mount_cb,
1074                                  async_data);
1075   return cancellable;
1076 }
1077
1078 gboolean
1079 _gtk_file_system_insert_bookmark (GtkFileSystem  *file_system,
1080                                   GFile          *file,
1081                                   gint            position,
1082                                   GError        **error)
1083 {
1084   GtkFileSystemPrivate *priv = file_system->priv;
1085   GSList *bookmarks;
1086   GtkFileSystemBookmark *bookmark;
1087   gboolean result = TRUE;
1088   GFile *bookmarks_file;
1089
1090   bookmarks = priv->bookmarks;
1091
1092   while (bookmarks)
1093     {
1094       bookmark = bookmarks->data;
1095       bookmarks = bookmarks->next;
1096
1097       if (g_file_equal (bookmark->file, file))
1098         {
1099           /* File is already in bookmarks */
1100           result = FALSE;
1101           break;
1102         }
1103     }
1104
1105   if (!result)
1106     {
1107       gchar *uri = g_file_get_uri (file);
1108
1109       g_set_error (error,
1110                    GTK_FILE_CHOOSER_ERROR,
1111                    GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
1112                    "%s already exists in the bookmarks list",
1113                    uri);
1114
1115       g_free (uri);
1116
1117       return FALSE;
1118     }
1119
1120   bookmark = g_slice_new0 (GtkFileSystemBookmark);
1121   bookmark->file = g_object_ref (file);
1122
1123   priv->bookmarks = g_slist_insert (priv->bookmarks, bookmark, position);
1124
1125   bookmarks_file = get_bookmarks_file ();
1126   save_bookmarks (bookmarks_file, priv->bookmarks);
1127   g_object_unref (bookmarks_file);
1128
1129   g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
1130
1131   return TRUE;
1132 }
1133
1134 gboolean
1135 _gtk_file_system_remove_bookmark (GtkFileSystem  *file_system,
1136                                   GFile          *file,
1137                                   GError        **error)
1138 {
1139   GtkFileSystemPrivate *priv = file_system->priv;
1140   GtkFileSystemBookmark *bookmark;
1141   GSList *bookmarks;
1142   gboolean result = FALSE;
1143   GFile *bookmarks_file;
1144
1145   if (!priv->bookmarks)
1146     return FALSE;
1147
1148   bookmarks = priv->bookmarks;
1149
1150   while (bookmarks)
1151     {
1152       bookmark = bookmarks->data;
1153
1154       if (g_file_equal (bookmark->file, file))
1155         {
1156           result = TRUE;
1157           priv->bookmarks = g_slist_remove_link (priv->bookmarks, bookmarks);
1158           _gtk_file_system_bookmark_free (bookmark);
1159           g_slist_free_1 (bookmarks);
1160           break;
1161         }
1162
1163       bookmarks = bookmarks->next;
1164     }
1165
1166   if (!result)
1167     {
1168       gchar *uri = g_file_get_uri (file);
1169
1170       g_set_error (error,
1171                    GTK_FILE_CHOOSER_ERROR,
1172                    GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
1173                    "%s does not exist in the bookmarks list",
1174                    uri);
1175
1176       g_free (uri);
1177
1178       return FALSE;
1179     }
1180
1181   bookmarks_file = get_bookmarks_file ();
1182   save_bookmarks (bookmarks_file, priv->bookmarks);
1183   g_object_unref (bookmarks_file);
1184
1185   g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
1186
1187   return TRUE;
1188 }
1189
1190 gchar *
1191 _gtk_file_system_get_bookmark_label (GtkFileSystem *file_system,
1192                                      GFile         *file)
1193 {
1194   GtkFileSystemPrivate *priv = file_system->priv;
1195   GSList *bookmarks;
1196   gchar *label = NULL;
1197
1198   DEBUG ("get_bookmark_label");
1199
1200   bookmarks = priv->bookmarks;
1201
1202   while (bookmarks)
1203     {
1204       GtkFileSystemBookmark *bookmark;
1205
1206       bookmark = bookmarks->data;
1207       bookmarks = bookmarks->next;
1208
1209       if (g_file_equal (file, bookmark->file))
1210         {
1211           label = g_strdup (bookmark->label);
1212           break;
1213         }
1214     }
1215
1216   return label;
1217 }
1218
1219 void
1220 _gtk_file_system_set_bookmark_label (GtkFileSystem *file_system,
1221                                      GFile         *file,
1222                                      const gchar   *label)
1223 {
1224   GtkFileSystemPrivate *priv = file_system->priv;
1225   gboolean changed = FALSE;
1226   GFile *bookmarks_file;
1227   GSList *bookmarks;
1228
1229   DEBUG ("set_bookmark_label");
1230
1231   bookmarks = priv->bookmarks;
1232
1233   while (bookmarks)
1234     {
1235       GtkFileSystemBookmark *bookmark;
1236
1237       bookmark = bookmarks->data;
1238       bookmarks = bookmarks->next;
1239
1240       if (g_file_equal (file, bookmark->file))
1241         {
1242           g_free (bookmark->label);
1243           bookmark->label = g_strdup (label);
1244           changed = TRUE;
1245           break;
1246         }
1247     }
1248
1249   bookmarks_file = get_bookmarks_file ();
1250   save_bookmarks (bookmarks_file, priv->bookmarks);
1251   g_object_unref (bookmarks_file);
1252
1253   if (changed)
1254     g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1255 }
1256
1257 GtkFileSystemVolume *
1258 _gtk_file_system_get_volume_for_file (GtkFileSystem *file_system,
1259                                       GFile         *file)
1260 {
1261   GMount *mount;
1262
1263   DEBUG ("get_volume_for_file");
1264
1265   mount = g_file_find_enclosing_mount (file, NULL, NULL);
1266
1267   if (!mount && g_file_is_native (file))
1268     return (GtkFileSystemVolume *) root_volume_token;
1269
1270   return (GtkFileSystemVolume *) mount;
1271 }
1272
1273 /* GtkFolder methods */
1274 static void
1275 gtk_folder_set_property (GObject      *object,
1276                          guint         prop_id,
1277                          const GValue *value,
1278                          GParamSpec   *pspec)
1279 {
1280   GtkFolder *folder = GTK_FOLDER (object);
1281   GtkFolderPrivate *priv = folder->priv;
1282
1283   switch (prop_id)
1284     {
1285     case PROP_FILE:
1286       priv->folder_file = g_value_dup_object (value);
1287       break;
1288     case PROP_ENUMERATOR:
1289       priv->enumerator = g_value_dup_object (value);
1290       break;
1291     case PROP_ATTRIBUTES:
1292       priv->attributes = g_value_dup_string (value);
1293       break;
1294     default:
1295       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1296       break;
1297     }
1298 }
1299
1300 static void
1301 gtk_folder_get_property (GObject    *object,
1302                          guint       prop_id,
1303                          GValue     *value,
1304                          GParamSpec *pspec)
1305 {
1306   GtkFolder *folder = GTK_FOLDER (object);
1307   GtkFolderPrivate *priv = folder->priv;
1308
1309   switch (prop_id)
1310     {
1311     case PROP_FILE:
1312       g_value_set_object (value, priv->folder_file);
1313       break;
1314     case PROP_ENUMERATOR:
1315       g_value_set_object (value, priv->enumerator);
1316       break;
1317     case PROP_ATTRIBUTES:
1318       g_value_set_string (value, priv->attributes);
1319       break;
1320     default:
1321       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1322       break;
1323     }
1324 }
1325
1326 static void
1327 query_created_file_info_callback (GObject      *source_object,
1328                                   GAsyncResult *result,
1329                                   gpointer      user_data)
1330 {
1331   GFile *file = G_FILE (source_object);
1332   GError *error = NULL;
1333   GFileInfo *info;
1334   GtkFolder *folder;
1335   GSList *files;
1336
1337   info = g_file_query_info_finish (file, result, &error);
1338
1339   if (error)
1340     {
1341       g_error_free (error);
1342       return;
1343     }
1344
1345   gdk_threads_enter ();
1346
1347   folder = GTK_FOLDER (user_data);
1348   gtk_folder_add_file (folder, file, info);
1349
1350   files = g_slist_prepend (NULL, file);
1351   g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files);
1352   g_slist_free (files);
1353
1354   g_object_unref (info);
1355   gdk_threads_leave ();
1356 }
1357
1358 static void
1359 directory_monitor_changed (GFileMonitor      *monitor,
1360                            GFile             *file,
1361                            GFile             *other_file,
1362                            GFileMonitorEvent  event,
1363                            gpointer           data)
1364 {
1365   GtkFolder *folder = GTK_FOLDER (data);
1366   GtkFolderPrivate *priv = folder->priv;
1367   GSList *files;
1368
1369   files = g_slist_prepend (NULL, file);
1370
1371   gdk_threads_enter ();
1372
1373   switch (event)
1374     {
1375     case G_FILE_MONITOR_EVENT_CREATED:
1376       g_file_query_info_async (file,
1377                                priv->attributes,
1378                                G_FILE_QUERY_INFO_NONE,
1379                                G_PRIORITY_DEFAULT,
1380                                priv->cancellable,
1381                                query_created_file_info_callback,
1382                                folder);
1383       break;
1384     case G_FILE_MONITOR_EVENT_DELETED:
1385       if (g_file_equal (file, priv->folder_file))
1386         g_signal_emit (folder, folder_signals[DELETED], 0);
1387       else
1388         g_signal_emit (folder, folder_signals[FILES_REMOVED], 0, files);
1389       break;
1390     default:
1391       break;
1392     }
1393
1394   gdk_threads_leave ();
1395
1396   g_slist_free (files);
1397 }
1398
1399 static void
1400 enumerator_files_callback (GObject      *source_object,
1401                            GAsyncResult *result,
1402                            gpointer      user_data)
1403 {
1404   GtkFolder *folder = GTK_FOLDER (user_data);
1405   GtkFolderPrivate *priv = folder->priv;
1406   GFileEnumerator *enumerator;
1407   GError *error = NULL;
1408   GSList *files = NULL;
1409   GList *file_infos, *f;
1410
1411   enumerator = G_FILE_ENUMERATOR (source_object);
1412   file_infos = g_file_enumerator_next_files_finish (enumerator, result, &error);
1413
1414   if (error)
1415     {
1416       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1417         g_warning ("%s", error->message);
1418
1419       g_error_free (error);
1420       return;
1421     }
1422
1423   if (!file_infos)
1424     {
1425       g_file_enumerator_close_async (enumerator,
1426                                      G_PRIORITY_DEFAULT,
1427                                      NULL, NULL, NULL);
1428
1429       gtk_folder_set_finished_loading (folder, TRUE);
1430       return;
1431     }
1432
1433   g_file_enumerator_next_files_async (enumerator, FILES_PER_QUERY,
1434                                       G_PRIORITY_DEFAULT,
1435                                       priv->cancellable,
1436                                       enumerator_files_callback,
1437                                       folder);
1438
1439   for (f = file_infos; f; f = f->next)
1440     {
1441       GFileInfo *info;
1442       GFile *child_file;
1443
1444       info = f->data;
1445       child_file = g_file_get_child (priv->folder_file, g_file_info_get_name (info));
1446       gtk_folder_add_file (folder, child_file, info);
1447       files = g_slist_prepend (files, child_file);
1448     }
1449
1450   gdk_threads_enter ();
1451   g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files);
1452   gdk_threads_leave ();
1453
1454   g_list_foreach (file_infos, (GFunc) g_object_unref, NULL);
1455   g_list_free (file_infos);
1456
1457   g_slist_foreach (files, (GFunc) g_object_unref, NULL);
1458   g_slist_free (files);
1459 }
1460
1461 static void
1462 gtk_folder_constructed (GObject *object)
1463 {
1464   GtkFolder *folder = GTK_FOLDER (object);
1465   GtkFolderPrivate *priv = folder->priv;
1466   GError *error = NULL;
1467
1468   priv->directory_monitor = g_file_monitor_directory (priv->folder_file, G_FILE_MONITOR_NONE, NULL, &error);
1469
1470   if (error)
1471     {
1472       g_warning ("%s", error->message);
1473       g_error_free (error);
1474     }
1475   else
1476     g_signal_connect (priv->directory_monitor, "changed",
1477                       G_CALLBACK (directory_monitor_changed), object);
1478
1479   g_file_enumerator_next_files_async (priv->enumerator,
1480                                       FILES_PER_QUERY,
1481                                       G_PRIORITY_DEFAULT,
1482                                       priv->cancellable,
1483                                       enumerator_files_callback,
1484                                       object);
1485   /* This isn't needed anymore */
1486   g_object_unref (priv->enumerator);
1487   priv->enumerator = NULL;
1488 }
1489
1490 static void
1491 gtk_folder_finalize (GObject *object)
1492 {
1493   GtkFolder *folder = GTK_FOLDER (object);
1494   GtkFolderPrivate *priv = folder->priv;
1495
1496   g_hash_table_unref (priv->children);
1497
1498   if (priv->folder_file)
1499     g_object_unref (priv->folder_file);
1500
1501   if (priv->directory_monitor)
1502     g_object_unref (priv->directory_monitor);
1503
1504   g_cancellable_cancel (priv->cancellable);
1505   g_object_unref (priv->cancellable);
1506   g_free (priv->attributes);
1507
1508   G_OBJECT_CLASS (_gtk_folder_parent_class)->finalize (object);
1509 }
1510
1511 static void
1512 _gtk_folder_class_init (GtkFolderClass *class)
1513 {
1514   GObjectClass *object_class = G_OBJECT_CLASS (class);
1515
1516   object_class->set_property = gtk_folder_set_property;
1517   object_class->get_property = gtk_folder_get_property;
1518   object_class->constructed = gtk_folder_constructed;
1519   object_class->finalize = gtk_folder_finalize;
1520
1521   g_object_class_install_property (object_class,
1522                                    PROP_FILE,
1523                                    g_param_spec_object ("file",
1524                                                         "File",
1525                                                         "GFile for the folder",
1526                                                         G_TYPE_FILE,
1527                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1528   g_object_class_install_property (object_class,
1529                                    PROP_ENUMERATOR,
1530                                    g_param_spec_object ("enumerator",
1531                                                         "Enumerator",
1532                                                         "GFileEnumerator to list files",
1533                                                         G_TYPE_FILE_ENUMERATOR,
1534                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1535   g_object_class_install_property (object_class,
1536                                    PROP_ATTRIBUTES,
1537                                    g_param_spec_string ("attributes",
1538                                                         "Attributes",
1539                                                         "Attributes to query for",
1540                                                         NULL,
1541                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1542   folder_signals[FILES_ADDED] =
1543     g_signal_new ("files-added",
1544                   G_TYPE_FROM_CLASS (object_class),
1545                   G_SIGNAL_RUN_LAST,
1546                   G_STRUCT_OFFSET (GtkFolderClass, files_added),
1547                   NULL, NULL,
1548                   g_cclosure_marshal_VOID__POINTER,
1549                   G_TYPE_NONE, 1, G_TYPE_POINTER);
1550   folder_signals[FILES_REMOVED] =
1551     g_signal_new ("files-removed",
1552                   G_TYPE_FROM_CLASS (object_class),
1553                   G_SIGNAL_RUN_LAST,
1554                   G_STRUCT_OFFSET (GtkFolderClass, files_removed),
1555                   NULL, NULL,
1556                   g_cclosure_marshal_VOID__POINTER,
1557                   G_TYPE_NONE, 1, G_TYPE_POINTER);
1558   folder_signals[FILES_CHANGED] =
1559     g_signal_new ("files-changed",
1560                   G_TYPE_FROM_CLASS (object_class),
1561                   G_SIGNAL_RUN_LAST,
1562                   G_STRUCT_OFFSET (GtkFolderClass, files_changed),
1563                   NULL, NULL,
1564                   g_cclosure_marshal_VOID__POINTER,
1565                   G_TYPE_NONE, 1, G_TYPE_POINTER);
1566   folder_signals[FINISHED_LOADING] =
1567     g_signal_new ("finished-loading",
1568                   G_TYPE_FROM_CLASS (object_class),
1569                   G_SIGNAL_RUN_LAST,
1570                   G_STRUCT_OFFSET (GtkFolderClass, finished_loading),
1571                   NULL, NULL,
1572                   g_cclosure_marshal_VOID__VOID,
1573                   G_TYPE_NONE, 0);
1574   folder_signals[DELETED] =
1575     g_signal_new ("deleted",
1576                   G_TYPE_FROM_CLASS (object_class),
1577                   G_SIGNAL_RUN_LAST,
1578                   G_STRUCT_OFFSET (GtkFolderClass, deleted),
1579                   NULL, NULL,
1580                   g_cclosure_marshal_VOID__VOID,
1581                   G_TYPE_NONE, 0);
1582
1583   g_type_class_add_private (object_class, sizeof (GtkFolderPrivate));
1584 }
1585
1586 static void
1587 _gtk_folder_init (GtkFolder *folder)
1588 {
1589   GtkFolderPrivate *priv;
1590
1591   folder->priv = G_TYPE_INSTANCE_GET_PRIVATE (folder,
1592                                               GTK_TYPE_FOLDER,
1593                                               GtkFolderPrivate);
1594   priv = folder->priv;
1595   priv->children = g_hash_table_new_full (g_file_hash,
1596                                           (GEqualFunc) g_file_equal,
1597                                           (GDestroyNotify) g_object_unref,
1598                                           (GDestroyNotify) g_object_unref);
1599   priv->cancellable = g_cancellable_new ();
1600 }
1601
1602 static void
1603 gtk_folder_set_finished_loading (GtkFolder *folder,
1604                                  gboolean   finished_loading)
1605 {
1606   GtkFolderPrivate *priv = folder->priv;
1607
1608   priv->finished_loading = (finished_loading == TRUE);
1609
1610   gdk_threads_enter ();
1611   g_signal_emit (folder, folder_signals[FINISHED_LOADING], 0);
1612   gdk_threads_leave ();
1613 }
1614
1615 static void
1616 gtk_folder_add_file (GtkFolder *folder,
1617                      GFile     *file,
1618                      GFileInfo *info)
1619 {
1620   GtkFolderPrivate *priv = folder->priv;
1621
1622   g_hash_table_insert (priv->children,
1623                        g_object_ref (file),
1624                        g_object_ref (info));
1625 }
1626
1627 GSList *
1628 _gtk_folder_list_children (GtkFolder *folder)
1629 {
1630   GtkFolderPrivate *priv = folder->priv;
1631   GList *files, *elem;
1632   GSList *children = NULL;
1633
1634   files = g_hash_table_get_keys (priv->children);
1635   children = NULL;
1636
1637   for (elem = files; elem; elem = elem->next)
1638     children = g_slist_prepend (children, g_object_ref (elem->data));
1639
1640   g_list_free (files);
1641
1642   return children;
1643 }
1644
1645 GFileInfo *
1646 _gtk_folder_get_info (GtkFolder  *folder,
1647                       GFile      *file)
1648 {
1649   GtkFolderPrivate *priv = folder->priv;
1650   GFileInfo *info;
1651
1652   info = g_hash_table_lookup (priv->children, file);
1653
1654   if (!info)
1655     return NULL;
1656
1657   return g_object_ref (info);
1658 }
1659
1660 gboolean
1661 _gtk_folder_is_finished_loading (GtkFolder *folder)
1662 {
1663   return folder->priv->finished_loading;
1664 }
1665
1666 /* GtkFileSystemVolume public methods */
1667 gchar *
1668 _gtk_file_system_volume_get_display_name (GtkFileSystemVolume *volume)
1669 {
1670   DEBUG ("volume_get_display_name");
1671
1672   if (IS_ROOT_VOLUME (volume))
1673     return g_strdup (_(root_volume_token));
1674   if (G_IS_DRIVE (volume))
1675     return g_drive_get_name (G_DRIVE (volume));
1676   else if (G_IS_MOUNT (volume))
1677     return g_mount_get_name (G_MOUNT (volume));
1678   else if (G_IS_VOLUME (volume))
1679     return g_volume_get_name (G_VOLUME (volume));
1680
1681   return NULL;
1682 }
1683
1684 gboolean
1685 _gtk_file_system_volume_is_mounted (GtkFileSystemVolume *volume)
1686 {
1687   gboolean mounted;
1688
1689   DEBUG ("volume_is_mounted");
1690
1691   if (IS_ROOT_VOLUME (volume))
1692     return TRUE;
1693
1694   mounted = FALSE;
1695
1696   if (G_IS_MOUNT (volume))
1697     mounted = TRUE;
1698   else if (G_IS_VOLUME (volume))
1699     {
1700       GMount *mount;
1701
1702       mount = g_volume_get_mount (G_VOLUME (volume));
1703
1704       if (mount)
1705         {
1706           mounted = TRUE;
1707           g_object_unref (mount);
1708         }
1709     }
1710
1711   return mounted;
1712 }
1713
1714 GFile *
1715 _gtk_file_system_volume_get_root (GtkFileSystemVolume *volume)
1716 {
1717   GFile *file = NULL;
1718
1719   DEBUG ("volume_get_base");
1720
1721   if (IS_ROOT_VOLUME (volume))
1722     return g_file_new_for_uri ("file:///");
1723
1724   if (G_IS_MOUNT (volume))
1725     file = g_mount_get_root (G_MOUNT (volume));
1726   else if (G_IS_VOLUME (volume))
1727     {
1728       GMount *mount;
1729
1730       mount = g_volume_get_mount (G_VOLUME (volume));
1731
1732       if (mount)
1733         {
1734           file = g_mount_get_root (mount);
1735           g_object_unref (mount);
1736         }
1737     }
1738
1739   return file;
1740 }
1741
1742 static GdkPixbuf *
1743 get_pixbuf_from_gicon (GIcon      *icon,
1744                        GtkWidget  *widget,
1745                        gint        icon_size,
1746                        GError    **error)
1747 {
1748   GdkScreen *screen;
1749   GtkIconTheme *icon_theme;
1750   GtkIconInfo *icon_info;
1751   GdkPixbuf *pixbuf;
1752
1753   screen = gtk_widget_get_screen (GTK_WIDGET (widget));
1754   icon_theme = gtk_icon_theme_get_for_screen (screen);
1755
1756   icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme,
1757                                               icon,
1758                                               icon_size,
1759                                               GTK_ICON_LOOKUP_USE_BUILTIN);
1760
1761   if (!icon_info)
1762     return NULL;
1763
1764   pixbuf = gtk_icon_info_load_icon (icon_info, error);
1765   gtk_icon_info_free (icon_info);
1766
1767   return pixbuf;
1768 }
1769
1770 GdkPixbuf *
1771 _gtk_file_system_volume_render_icon (GtkFileSystemVolume  *volume,
1772                                      GtkWidget            *widget,
1773                                      gint                  icon_size,
1774                                      GError              **error)
1775 {
1776   GIcon *icon = NULL;
1777   GdkPixbuf *pixbuf;
1778
1779   DEBUG ("volume_get_icon_name");
1780
1781   if (IS_ROOT_VOLUME (volume))
1782     icon = g_themed_icon_new ("drive-harddisk");
1783   else if (G_IS_DRIVE (volume))
1784     icon = g_drive_get_icon (G_DRIVE (volume));
1785   else if (G_IS_VOLUME (volume))
1786     icon = g_volume_get_icon (G_VOLUME (volume));
1787   else if (G_IS_MOUNT (volume))
1788     icon = g_mount_get_icon (G_MOUNT (volume));
1789
1790   if (!icon)
1791     return NULL;
1792
1793   pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, error);
1794
1795   g_object_unref (icon);
1796
1797   return pixbuf;
1798 }
1799
1800 GtkFileSystemVolume *
1801 _gtk_file_system_volume_ref (GtkFileSystemVolume *volume)
1802 {
1803   if (IS_ROOT_VOLUME (volume))
1804     return volume;
1805
1806   if (G_IS_MOUNT (volume)  ||
1807       G_IS_VOLUME (volume) ||
1808       G_IS_DRIVE (volume))
1809     g_object_ref (volume);
1810
1811   return volume;
1812 }
1813
1814 void
1815 _gtk_file_system_volume_unref (GtkFileSystemVolume *volume)
1816 {
1817   /* Root volume doesn't need to be freed */
1818   if (IS_ROOT_VOLUME (volume))
1819     return;
1820
1821   if (G_IS_MOUNT (volume)  ||
1822       G_IS_VOLUME (volume) ||
1823       G_IS_DRIVE (volume))
1824     g_object_unref (volume);
1825 }
1826
1827 /* GFileInfo helper functions */
1828 GdkPixbuf *
1829 _gtk_file_info_render_icon (GFileInfo *info,
1830                            GtkWidget *widget,
1831                            gint       icon_size)
1832 {
1833   GIcon *icon;
1834   GdkPixbuf *pixbuf = NULL;
1835   const gchar *thumbnail_path;
1836
1837   thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
1838
1839   if (thumbnail_path)
1840     pixbuf = gdk_pixbuf_new_from_file_at_size (thumbnail_path,
1841                                                icon_size, icon_size,
1842                                                NULL);
1843
1844   if (!pixbuf)
1845     {
1846       icon = g_file_info_get_icon (info);
1847
1848       if (icon)
1849         pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
1850
1851       if (!pixbuf)
1852         {
1853            /* Use general fallback for all files without icon */
1854           icon = g_themed_icon_new ("text-x-generic");
1855           pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
1856           g_object_unref (icon);
1857         }
1858     }
1859
1860   return pixbuf;
1861 }
1862
1863 gboolean
1864 _gtk_file_info_consider_as_directory (GFileInfo *info)
1865 {
1866   GFileType type = g_file_info_get_file_type (info);
1867   
1868   return (type == G_FILE_TYPE_DIRECTORY ||
1869           type == G_FILE_TYPE_MOUNTABLE ||
1870           type == G_FILE_TYPE_SHORTCUT);
1871 }
1872