]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystem.c
4a6791e9aa5da4d1ec273edeb1115c494b336a6c
[~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
75 typedef struct AsyncFuncData AsyncFuncData;
76
77 struct GtkFileSystemPrivate
78 {
79   GVolumeMonitor *volume_monitor;
80
81   /* This list contains elements that can be
82    * of type GDrive, GVolume and GMount
83    */
84   GSList *volumes;
85
86   /* This list contains GtkFileSystemBookmark structs */
87   GSList *bookmarks;
88
89   GFileMonitor *bookmarks_monitor;
90 };
91
92 struct AsyncFuncData
93 {
94   GtkFileSystem *file_system;
95   GFile *file;
96   GCancellable *cancellable;
97   gchar *attributes;
98
99   gpointer callback;
100   gpointer data;
101 };
102
103 struct GtkFileSystemBookmark
104 {
105   GFile *file;
106   gchar *label;
107 };
108
109 G_DEFINE_TYPE (GtkFileSystem, _gtk_file_system, G_TYPE_OBJECT)
110
111
112 /* GtkFileSystemBookmark methods */
113 void
114 _gtk_file_system_bookmark_free (GtkFileSystemBookmark *bookmark)
115 {
116   g_object_unref (bookmark->file);
117   g_free (bookmark->label);
118   g_slice_free (GtkFileSystemBookmark, bookmark);
119 }
120
121 /* GtkFileSystem methods */
122 static void
123 volumes_changed (GVolumeMonitor *volume_monitor,
124                  gpointer        volume,
125                  gpointer        user_data)
126 {
127   GtkFileSystem *file_system;
128
129   gdk_threads_enter ();
130
131   file_system = GTK_FILE_SYSTEM (user_data);
132   g_signal_emit (file_system, fs_signals[VOLUMES_CHANGED], 0, volume);
133   gdk_threads_leave ();
134 }
135
136 static void
137 gtk_file_system_dispose (GObject *object)
138 {
139   GtkFileSystem *file_system = GTK_FILE_SYSTEM (object);
140   GtkFileSystemPrivate *priv = file_system->priv;
141
142   DEBUG ("dispose");
143
144   if (priv->volumes)
145     {
146       g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
147       g_slist_free (priv->volumes);
148       priv->volumes = NULL;
149     }
150
151   if (priv->volume_monitor)
152     {
153       g_signal_handlers_disconnect_by_func (priv->volume_monitor, volumes_changed, object);
154       g_object_unref (priv->volume_monitor);
155       priv->volume_monitor = NULL;
156     }
157
158   G_OBJECT_CLASS (_gtk_file_system_parent_class)->dispose (object);
159 }
160
161 static void
162 gtk_file_system_finalize (GObject *object)
163 {
164   GtkFileSystem *file_system = GTK_FILE_SYSTEM (object);
165   GtkFileSystemPrivate *priv = file_system->priv;
166
167   DEBUG ("finalize");
168
169   if (priv->bookmarks_monitor)
170     g_object_unref (priv->bookmarks_monitor);
171
172   if (priv->bookmarks)
173     {
174       g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL);
175       g_slist_free (priv->bookmarks);
176     }
177
178   G_OBJECT_CLASS (_gtk_file_system_parent_class)->finalize (object);
179 }
180
181 static void
182 _gtk_file_system_class_init (GtkFileSystemClass *class)
183 {
184   GObjectClass *object_class = G_OBJECT_CLASS (class);
185
186   object_class->dispose = gtk_file_system_dispose;
187   object_class->finalize = gtk_file_system_finalize;
188
189   fs_signals[BOOKMARKS_CHANGED] =
190     g_signal_new ("bookmarks-changed",
191                   G_TYPE_FROM_CLASS (object_class),
192                   G_SIGNAL_RUN_LAST,
193                   G_STRUCT_OFFSET (GtkFileSystemClass, bookmarks_changed),
194                   NULL, NULL,
195                   g_cclosure_marshal_VOID__VOID,
196                   G_TYPE_NONE, 0);
197
198   fs_signals[VOLUMES_CHANGED] =
199     g_signal_new ("volumes-changed",
200                   G_TYPE_FROM_CLASS (object_class),
201                   G_SIGNAL_RUN_LAST,
202                   G_STRUCT_OFFSET (GtkFileSystemClass, volumes_changed),
203                   NULL, NULL,
204                   g_cclosure_marshal_VOID__VOID,
205                   G_TYPE_NONE, 0);
206
207   g_type_class_add_private (object_class, sizeof (GtkFileSystemPrivate));
208 }
209
210 static GFile *
211 get_bookmarks_file (void)
212 {
213   GFile *file;
214   gchar *filename;
215
216   filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
217   file = g_file_new_for_path (filename);
218   g_free (filename);
219
220   return file;
221 }
222
223 static GSList *
224 read_bookmarks (GFile *file)
225 {
226   gchar *contents;
227   gchar **lines, *space;
228   GSList *bookmarks = NULL;
229   gint i;
230
231   if (!g_file_load_contents (file, NULL, &contents,
232                              NULL, NULL, NULL))
233     return NULL;
234
235   lines = g_strsplit (contents, "\n", -1);
236
237   for (i = 0; lines[i]; i++)
238     {
239       GtkFileSystemBookmark *bookmark;
240
241       if (!*lines[i])
242         continue;
243
244       if (!g_utf8_validate (lines[i], -1, NULL))
245         continue;
246
247       bookmark = g_slice_new0 (GtkFileSystemBookmark);
248
249       if ((space = strchr (lines[i], ' ')) != NULL)
250         {
251           space[0] = '\0';
252           bookmark->label = g_strdup (space + 1);
253         }
254
255       bookmark->file = g_file_new_for_uri (lines[i]);
256       bookmarks = g_slist_prepend (bookmarks, bookmark);
257     }
258
259   bookmarks = g_slist_reverse (bookmarks);
260   g_strfreev (lines);
261   g_free (contents);
262
263   return bookmarks;
264 }
265
266 static void
267 save_bookmarks (GFile  *bookmarks_file,
268                 GSList *bookmarks)
269 {
270   GError *error = NULL;
271   GString *contents;
272   GSList *l;
273
274   contents = g_string_new ("");
275
276   for (l = bookmarks; l; l = l->next)
277     {
278       GtkFileSystemBookmark *bookmark = l->data;
279       gchar *uri;
280
281       uri = g_file_get_uri (bookmark->file);
282       if (!uri)
283         continue;
284
285       g_string_append (contents, uri);
286
287       if (bookmark->label)
288         g_string_append_printf (contents, " %s", bookmark->label);
289
290       g_string_append_c (contents, '\n');
291       g_free (uri);
292     }
293
294   if (!g_file_replace_contents (bookmarks_file,
295                                 contents->str,
296                                 strlen (contents->str),
297                                 NULL, FALSE, 0, NULL,
298                                 NULL, &error))
299     {
300       g_critical ("%s", error->message);
301       g_error_free (error);
302     }
303
304   g_string_free (contents, TRUE);
305 }
306
307 static void
308 bookmarks_file_changed (GFileMonitor      *monitor,
309                         GFile             *file,
310                         GFile             *other_file,
311                         GFileMonitorEvent  event,
312                         gpointer           data)
313 {
314   GtkFileSystem *file_system = GTK_FILE_SYSTEM (data);
315   GtkFileSystemPrivate *priv = file_system->priv;
316
317   switch (event)
318     {
319     case G_FILE_MONITOR_EVENT_CHANGED:
320     case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
321     case G_FILE_MONITOR_EVENT_CREATED:
322     case G_FILE_MONITOR_EVENT_DELETED:
323       g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL);
324       g_slist_free (priv->bookmarks);
325
326       priv->bookmarks = read_bookmarks (file);
327
328       gdk_threads_enter ();
329       g_signal_emit (data, fs_signals[BOOKMARKS_CHANGED], 0);
330       gdk_threads_leave ();
331       break;
332     default:
333       /* ignore at the moment */
334       break;
335     }
336 }
337
338 static gboolean
339 mount_referenced_by_volume_activation_root (GList *volumes, GMount *mount)
340 {
341   GList *l;
342   GFile *mount_root;
343   gboolean ret;
344
345   ret = FALSE;
346
347   mount_root = g_mount_get_root (mount);
348
349   for (l = volumes; l != NULL; l = l->next)
350     {
351       GVolume *volume = G_VOLUME (l->data);
352       GFile *volume_activation_root;
353
354       volume_activation_root = g_volume_get_activation_root (volume);
355       if (volume_activation_root != NULL)
356         {
357           if (g_file_has_prefix (volume_activation_root, mount_root))
358             {
359               ret = TRUE;
360               g_object_unref (volume_activation_root);
361               break;
362             }
363           g_object_unref (volume_activation_root);
364         }
365     }
366
367   g_object_unref (mount_root);
368   return ret;
369 }
370
371 static void
372 get_volumes_list (GtkFileSystem *file_system)
373 {
374   GtkFileSystemPrivate *priv = file_system->priv;
375   GList *l, *ll;
376   GList *drives;
377   GList *volumes;
378   GList *mounts;
379   GDrive *drive;
380   GVolume *volume;
381   GMount *mount;
382
383   if (priv->volumes)
384     {
385       g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
386       g_slist_free (priv->volumes);
387       priv->volumes = NULL;
388     }
389
390   /* first go through all connected drives */
391   drives = g_volume_monitor_get_connected_drives (priv->volume_monitor);
392
393   for (l = drives; l != NULL; l = l->next)
394     {
395       drive = l->data;
396       volumes = g_drive_get_volumes (drive);
397
398       if (volumes)
399         {
400           for (ll = volumes; ll != NULL; ll = ll->next)
401             {
402               volume = ll->data;
403               mount = g_volume_get_mount (volume);
404
405               if (mount)
406                 {
407                   /* Show mounted volume */
408                   priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
409                   g_object_unref (mount);
410                 }
411               else
412                 {
413                   /* Do show the unmounted volumes in the sidebar;
414                    * this is so the user can mount it (in case automounting
415                    * is off).
416                    *
417                    * Also, even if automounting is enabled, this gives a visual
418                    * cue that the user should remember to yank out the media if
419                    * he just unmounted it.
420                    */
421                   priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
422                 }
423
424               g_object_unref (volume);
425             }
426   
427            g_list_free (volumes);
428         }
429       else if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive))
430         {
431           /* If the drive has no mountable volumes and we cannot detect media change.. we
432            * display the drive in the sidebar so the user can manually poll the drive by
433            * right clicking and selecting "Rescan..."
434            *
435            * This is mainly for drives like floppies where media detection doesn't
436            * work.. but it's also for human beings who like to turn off media detection
437            * in the OS to save battery juice.
438            */
439
440           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (drive));
441         }
442
443       g_object_unref (drive);
444     }
445
446   g_list_free (drives);
447
448   /* add all volumes that is not associated with a drive */
449   volumes = g_volume_monitor_get_volumes (priv->volume_monitor);
450
451   for (l = volumes; l != NULL; l = l->next)
452     {
453       volume = l->data;
454       drive = g_volume_get_drive (volume);
455
456       if (drive)
457         {
458           g_object_unref (drive);
459           continue;
460         }
461
462       mount = g_volume_get_mount (volume);
463
464       if (mount)
465         {
466           /* show this mount */
467           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
468           g_object_unref (mount);
469         }
470       else
471         {
472           /* see comment above in why we add an icon for a volume */
473           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
474         }
475
476       g_object_unref (volume);
477     }
478
479   /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
480   mounts = g_volume_monitor_get_mounts (priv->volume_monitor);
481
482   for (l = mounts; l != NULL; l = l->next)
483     {
484       mount = l->data;
485       volume = g_mount_get_volume (mount);
486
487       if (volume)
488         {
489           g_object_unref (volume);
490           continue;
491         }
492
493       /* if there's exists one or more volumes with an activation root inside the mount,
494        * don't display the mount
495        */
496       if (mount_referenced_by_volume_activation_root (volumes, mount))
497         {
498           g_object_unref (mount);
499           continue;
500         }
501
502       /* show this mount */
503       priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
504       g_object_unref (mount);
505     }
506
507   g_list_free (volumes);
508
509   g_list_free (mounts);
510 }
511
512 static void
513 _gtk_file_system_init (GtkFileSystem *file_system)
514 {
515   GtkFileSystemPrivate *priv;
516   GFile *bookmarks_file;
517   GError *error = NULL;
518
519   DEBUG ("init");
520
521   file_system->priv = G_TYPE_INSTANCE_GET_PRIVATE (file_system,
522                                                    GTK_TYPE_FILE_SYSTEM,
523                                                    GtkFileSystemPrivate);
524   priv = file_system->priv;
525
526   /* Volumes */
527   priv->volume_monitor = g_volume_monitor_get ();
528
529   g_signal_connect (priv->volume_monitor, "mount-added",
530                     G_CALLBACK (volumes_changed), file_system);
531   g_signal_connect (priv->volume_monitor, "mount-removed",
532                     G_CALLBACK (volumes_changed), file_system);
533   g_signal_connect (priv->volume_monitor, "mount-changed",
534                     G_CALLBACK (volumes_changed), file_system);
535   g_signal_connect (priv->volume_monitor, "volume-added",
536                     G_CALLBACK (volumes_changed), file_system);
537   g_signal_connect (priv->volume_monitor, "volume-removed",
538                     G_CALLBACK (volumes_changed), file_system);
539   g_signal_connect (priv->volume_monitor, "volume-changed",
540                     G_CALLBACK (volumes_changed), file_system);
541   g_signal_connect (priv->volume_monitor, "drive-connected",
542                     G_CALLBACK (volumes_changed), file_system);
543   g_signal_connect (priv->volume_monitor, "drive-disconnected",
544                     G_CALLBACK (volumes_changed), file_system);
545   g_signal_connect (priv->volume_monitor, "drive-changed",
546                     G_CALLBACK (volumes_changed), file_system);
547
548   /* Bookmarks */
549   bookmarks_file = get_bookmarks_file ();
550   priv->bookmarks = read_bookmarks (bookmarks_file);
551   priv->bookmarks_monitor = g_file_monitor_file (bookmarks_file,
552                                                  G_FILE_MONITOR_NONE,
553                                                  NULL, &error);
554   if (error)
555     {
556       g_warning ("%s", error->message);
557       g_error_free (error);
558     }
559   else
560     g_signal_connect (priv->bookmarks_monitor, "changed",
561                       G_CALLBACK (bookmarks_file_changed), file_system);
562
563   g_object_unref (bookmarks_file);
564 }
565
566 /* GtkFileSystem public methods */
567 GtkFileSystem *
568 _gtk_file_system_new (void)
569 {
570   return g_object_new (GTK_TYPE_FILE_SYSTEM, NULL);
571 }
572
573 GSList *
574 _gtk_file_system_list_volumes (GtkFileSystem *file_system)
575 {
576   GtkFileSystemPrivate *priv = file_system->priv;
577   GSList *list;
578
579   DEBUG ("list_volumes");
580
581   get_volumes_list (file_system);
582
583   list = g_slist_copy (priv->volumes);
584
585 #ifndef G_OS_WIN32
586   /* Prepend root volume */
587   list = g_slist_prepend (list, (gpointer) root_volume_token);
588 #endif
589
590   return list;
591 }
592
593 GSList *
594 _gtk_file_system_list_bookmarks (GtkFileSystem *file_system)
595 {
596   GtkFileSystemPrivate *priv = file_system->priv;
597   GSList *bookmarks, *files = NULL;
598
599   DEBUG ("list_bookmarks");
600
601   bookmarks = priv->bookmarks;
602
603   while (bookmarks)
604     {
605       GtkFileSystemBookmark *bookmark;
606
607       bookmark = bookmarks->data;
608       bookmarks = bookmarks->next;
609
610       files = g_slist_prepend (files, g_object_ref (bookmark->file));
611     }
612
613   return g_slist_reverse (files);
614 }
615
616 static void
617 free_async_data (AsyncFuncData *async_data)
618 {
619   g_object_unref (async_data->file_system);
620   g_object_unref (async_data->file);
621   g_object_unref (async_data->cancellable);
622
623   g_free (async_data->attributes);
624   g_free (async_data);
625 }
626
627 static void
628 query_info_callback (GObject      *source_object,
629                      GAsyncResult *result,
630                      gpointer      user_data)
631 {
632   AsyncFuncData *async_data;
633   GError *error = NULL;
634   GFileInfo *file_info;
635   GFile *file;
636
637   DEBUG ("query_info_callback");
638
639   file = G_FILE (source_object);
640   async_data = (AsyncFuncData *) user_data;
641   file_info = g_file_query_info_finish (file, result, &error);
642
643   if (async_data->callback)
644     {
645       gdk_threads_enter ();
646       ((GtkFileSystemGetInfoCallback) async_data->callback) (async_data->cancellable,
647                                                              file_info, error, async_data->data);
648       gdk_threads_leave ();
649     }
650
651   if (file_info)
652     g_object_unref (file_info);
653
654   if (error)
655     g_error_free (error);
656
657   free_async_data (async_data);
658 }
659
660 GCancellable *
661 _gtk_file_system_get_info (GtkFileSystem                *file_system,
662                            GFile                        *file,
663                            const gchar                  *attributes,
664                            GtkFileSystemGetInfoCallback  callback,
665                            gpointer                      data)
666 {
667   GCancellable *cancellable;
668   AsyncFuncData *async_data;
669
670   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
671   g_return_val_if_fail (G_IS_FILE (file), NULL);
672
673   cancellable = g_cancellable_new ();
674
675   async_data = g_new0 (AsyncFuncData, 1);
676   async_data->file_system = g_object_ref (file_system);
677   async_data->file = g_object_ref (file);
678   async_data->cancellable = g_object_ref (cancellable);
679
680   async_data->callback = callback;
681   async_data->data = data;
682
683   g_file_query_info_async (file,
684                            attributes,
685                            G_FILE_QUERY_INFO_NONE,
686                            G_PRIORITY_DEFAULT,
687                            cancellable,
688                            query_info_callback,
689                            async_data);
690
691   return cancellable;
692 }
693
694 static void
695 drive_poll_for_media_cb (GObject      *source_object,
696                          GAsyncResult *result,
697                          gpointer      user_data)
698 {
699   AsyncFuncData *async_data;
700   GError *error = NULL;
701
702   g_drive_poll_for_media_finish (G_DRIVE (source_object), result, &error);
703   async_data = (AsyncFuncData *) user_data;
704
705   gdk_threads_enter ();
706   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
707                                                              (GtkFileSystemVolume *) source_object,
708                                                              error, async_data->data);
709   gdk_threads_leave ();
710
711   if (error)
712     g_error_free (error);
713 }
714
715 static void
716 volume_mount_cb (GObject      *source_object,
717                  GAsyncResult *result,
718                  gpointer      user_data)
719 {
720   AsyncFuncData *async_data;
721   GError *error = NULL;
722
723   g_volume_mount_finish (G_VOLUME (source_object), result, &error);
724   async_data = (AsyncFuncData *) user_data;
725
726   gdk_threads_enter ();
727   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
728                                                              (GtkFileSystemVolume *) source_object,
729                                                              error, async_data->data);
730   gdk_threads_leave ();
731
732   if (error)
733     g_error_free (error);
734 }
735
736 GCancellable *
737 _gtk_file_system_mount_volume (GtkFileSystem                    *file_system,
738                                GtkFileSystemVolume              *volume,
739                                GMountOperation                  *mount_operation,
740                                GtkFileSystemVolumeMountCallback  callback,
741                                gpointer                          data)
742 {
743   GCancellable *cancellable;
744   AsyncFuncData *async_data;
745   gboolean handled = FALSE;
746
747   DEBUG ("volume_mount");
748
749   cancellable = g_cancellable_new ();
750
751   async_data = g_new0 (AsyncFuncData, 1);
752   async_data->file_system = g_object_ref (file_system);
753   async_data->cancellable = g_object_ref (cancellable);
754
755   async_data->callback = callback;
756   async_data->data = data;
757
758   if (G_IS_DRIVE (volume))
759     {
760       /* this path happens for drives that are not polled by the OS and where the last media
761        * check indicated that no media was available. So the thing to do here is to
762        * invoke poll_for_media() on the drive
763        */
764       g_drive_poll_for_media (G_DRIVE (volume), cancellable, drive_poll_for_media_cb, async_data);
765       handled = TRUE;
766     }
767   else if (G_IS_VOLUME (volume))
768     {
769       g_volume_mount (G_VOLUME (volume), G_MOUNT_MOUNT_NONE, mount_operation, cancellable, volume_mount_cb, async_data);
770       handled = TRUE;
771     }
772
773   if (!handled)
774     free_async_data (async_data);
775
776   return cancellable;
777 }
778
779 static void
780 enclosing_volume_mount_cb (GObject      *source_object,
781                            GAsyncResult *result,
782                            gpointer      user_data)
783 {
784   GtkFileSystemVolume *volume;
785   AsyncFuncData *async_data;
786   GError *error = NULL;
787
788   async_data = (AsyncFuncData *) user_data;
789   g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error);
790   volume = _gtk_file_system_get_volume_for_file (async_data->file_system, G_FILE (source_object));
791
792   /* Silently drop G_IO_ERROR_ALREADY_MOUNTED error for gvfs backends without visible mounts. */
793   /* Better than doing query_info with additional I/O every time. */
794   if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED))
795     g_clear_error (&error);
796
797   gdk_threads_enter ();
798   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable, volume,
799                                                              error, async_data->data);
800   gdk_threads_leave ();
801
802   if (error)
803     g_error_free (error);
804
805   _gtk_file_system_volume_unref (volume);
806 }
807
808 GCancellable *
809 _gtk_file_system_mount_enclosing_volume (GtkFileSystem                     *file_system,
810                                          GFile                             *file,
811                                          GMountOperation                   *mount_operation,
812                                          GtkFileSystemVolumeMountCallback   callback,
813                                          gpointer                           data)
814 {
815   GCancellable *cancellable;
816   AsyncFuncData *async_data;
817
818   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
819   g_return_val_if_fail (G_IS_FILE (file), NULL);
820
821   DEBUG ("mount_enclosing_volume");
822
823   cancellable = g_cancellable_new ();
824
825   async_data = g_new0 (AsyncFuncData, 1);
826   async_data->file_system = g_object_ref (file_system);
827   async_data->file = g_object_ref (file);
828   async_data->cancellable = g_object_ref (cancellable);
829
830   async_data->callback = callback;
831   async_data->data = data;
832
833   g_file_mount_enclosing_volume (file,
834                                  G_MOUNT_MOUNT_NONE,
835                                  mount_operation,
836                                  cancellable,
837                                  enclosing_volume_mount_cb,
838                                  async_data);
839   return cancellable;
840 }
841
842 gboolean
843 _gtk_file_system_insert_bookmark (GtkFileSystem  *file_system,
844                                   GFile          *file,
845                                   gint            position,
846                                   GError        **error)
847 {
848   GtkFileSystemPrivate *priv = file_system->priv;
849   GSList *bookmarks;
850   GtkFileSystemBookmark *bookmark;
851   gboolean result = TRUE;
852   GFile *bookmarks_file;
853
854   bookmarks = priv->bookmarks;
855
856   while (bookmarks)
857     {
858       bookmark = bookmarks->data;
859       bookmarks = bookmarks->next;
860
861       if (g_file_equal (bookmark->file, file))
862         {
863           /* File is already in bookmarks */
864           result = FALSE;
865           break;
866         }
867     }
868
869   if (!result)
870     {
871       gchar *uri = g_file_get_uri (file);
872
873       g_set_error (error,
874                    GTK_FILE_CHOOSER_ERROR,
875                    GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
876                    "%s already exists in the bookmarks list",
877                    uri);
878
879       g_free (uri);
880
881       return FALSE;
882     }
883
884   bookmark = g_slice_new0 (GtkFileSystemBookmark);
885   bookmark->file = g_object_ref (file);
886
887   priv->bookmarks = g_slist_insert (priv->bookmarks, bookmark, position);
888
889   bookmarks_file = get_bookmarks_file ();
890   save_bookmarks (bookmarks_file, priv->bookmarks);
891   g_object_unref (bookmarks_file);
892
893   g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
894
895   return TRUE;
896 }
897
898 gboolean
899 _gtk_file_system_remove_bookmark (GtkFileSystem  *file_system,
900                                   GFile          *file,
901                                   GError        **error)
902 {
903   GtkFileSystemPrivate *priv = file_system->priv;
904   GtkFileSystemBookmark *bookmark;
905   GSList *bookmarks;
906   gboolean result = FALSE;
907   GFile *bookmarks_file;
908
909   if (!priv->bookmarks)
910     return FALSE;
911
912   bookmarks = priv->bookmarks;
913
914   while (bookmarks)
915     {
916       bookmark = bookmarks->data;
917
918       if (g_file_equal (bookmark->file, file))
919         {
920           result = TRUE;
921           priv->bookmarks = g_slist_remove_link (priv->bookmarks, bookmarks);
922           _gtk_file_system_bookmark_free (bookmark);
923           g_slist_free_1 (bookmarks);
924           break;
925         }
926
927       bookmarks = bookmarks->next;
928     }
929
930   if (!result)
931     {
932       gchar *uri = g_file_get_uri (file);
933
934       g_set_error (error,
935                    GTK_FILE_CHOOSER_ERROR,
936                    GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
937                    "%s does not exist in the bookmarks list",
938                    uri);
939
940       g_free (uri);
941
942       return FALSE;
943     }
944
945   bookmarks_file = get_bookmarks_file ();
946   save_bookmarks (bookmarks_file, priv->bookmarks);
947   g_object_unref (bookmarks_file);
948
949   g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
950
951   return TRUE;
952 }
953
954 gchar *
955 _gtk_file_system_get_bookmark_label (GtkFileSystem *file_system,
956                                      GFile         *file)
957 {
958   GtkFileSystemPrivate *priv = file_system->priv;
959   GSList *bookmarks;
960   gchar *label = NULL;
961
962   DEBUG ("get_bookmark_label");
963
964   bookmarks = priv->bookmarks;
965
966   while (bookmarks)
967     {
968       GtkFileSystemBookmark *bookmark;
969
970       bookmark = bookmarks->data;
971       bookmarks = bookmarks->next;
972
973       if (g_file_equal (file, bookmark->file))
974         {
975           label = g_strdup (bookmark->label);
976           break;
977         }
978     }
979
980   return label;
981 }
982
983 void
984 _gtk_file_system_set_bookmark_label (GtkFileSystem *file_system,
985                                      GFile         *file,
986                                      const gchar   *label)
987 {
988   GtkFileSystemPrivate *priv = file_system->priv;
989   gboolean changed = FALSE;
990   GFile *bookmarks_file;
991   GSList *bookmarks;
992
993   DEBUG ("set_bookmark_label");
994
995   bookmarks = priv->bookmarks;
996
997   while (bookmarks)
998     {
999       GtkFileSystemBookmark *bookmark;
1000
1001       bookmark = bookmarks->data;
1002       bookmarks = bookmarks->next;
1003
1004       if (g_file_equal (file, bookmark->file))
1005         {
1006           g_free (bookmark->label);
1007           bookmark->label = g_strdup (label);
1008           changed = TRUE;
1009           break;
1010         }
1011     }
1012
1013   bookmarks_file = get_bookmarks_file ();
1014   save_bookmarks (bookmarks_file, priv->bookmarks);
1015   g_object_unref (bookmarks_file);
1016
1017   if (changed)
1018     g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1019 }
1020
1021 GtkFileSystemVolume *
1022 _gtk_file_system_get_volume_for_file (GtkFileSystem *file_system,
1023                                       GFile         *file)
1024 {
1025   GMount *mount;
1026
1027   DEBUG ("get_volume_for_file");
1028
1029   mount = g_file_find_enclosing_mount (file, NULL, NULL);
1030
1031   if (!mount && g_file_is_native (file))
1032     return (GtkFileSystemVolume *) root_volume_token;
1033
1034   return (GtkFileSystemVolume *) mount;
1035 }
1036
1037 /* GtkFileSystemVolume public methods */
1038 gchar *
1039 _gtk_file_system_volume_get_display_name (GtkFileSystemVolume *volume)
1040 {
1041   DEBUG ("volume_get_display_name");
1042
1043   if (IS_ROOT_VOLUME (volume))
1044     return g_strdup (_(root_volume_token));
1045   if (G_IS_DRIVE (volume))
1046     return g_drive_get_name (G_DRIVE (volume));
1047   else if (G_IS_MOUNT (volume))
1048     return g_mount_get_name (G_MOUNT (volume));
1049   else if (G_IS_VOLUME (volume))
1050     return g_volume_get_name (G_VOLUME (volume));
1051
1052   return NULL;
1053 }
1054
1055 gboolean
1056 _gtk_file_system_volume_is_mounted (GtkFileSystemVolume *volume)
1057 {
1058   gboolean mounted;
1059
1060   DEBUG ("volume_is_mounted");
1061
1062   if (IS_ROOT_VOLUME (volume))
1063     return TRUE;
1064
1065   mounted = FALSE;
1066
1067   if (G_IS_MOUNT (volume))
1068     mounted = TRUE;
1069   else if (G_IS_VOLUME (volume))
1070     {
1071       GMount *mount;
1072
1073       mount = g_volume_get_mount (G_VOLUME (volume));
1074
1075       if (mount)
1076         {
1077           mounted = TRUE;
1078           g_object_unref (mount);
1079         }
1080     }
1081
1082   return mounted;
1083 }
1084
1085 GFile *
1086 _gtk_file_system_volume_get_root (GtkFileSystemVolume *volume)
1087 {
1088   GFile *file = NULL;
1089
1090   DEBUG ("volume_get_base");
1091
1092   if (IS_ROOT_VOLUME (volume))
1093     return g_file_new_for_uri ("file:///");
1094
1095   if (G_IS_MOUNT (volume))
1096     file = g_mount_get_root (G_MOUNT (volume));
1097   else if (G_IS_VOLUME (volume))
1098     {
1099       GMount *mount;
1100
1101       mount = g_volume_get_mount (G_VOLUME (volume));
1102
1103       if (mount)
1104         {
1105           file = g_mount_get_root (mount);
1106           g_object_unref (mount);
1107         }
1108     }
1109
1110   return file;
1111 }
1112
1113 static GdkPixbuf *
1114 get_pixbuf_from_gicon (GIcon      *icon,
1115                        GtkWidget  *widget,
1116                        gint        icon_size,
1117                        GError    **error)
1118 {
1119   GdkScreen *screen;
1120   GtkIconTheme *icon_theme;
1121   GtkIconInfo *icon_info;
1122   GdkPixbuf *pixbuf;
1123
1124   screen = gtk_widget_get_screen (GTK_WIDGET (widget));
1125   icon_theme = gtk_icon_theme_get_for_screen (screen);
1126
1127   icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme,
1128                                               icon,
1129                                               icon_size,
1130                                               GTK_ICON_LOOKUP_USE_BUILTIN);
1131
1132   if (!icon_info)
1133     return NULL;
1134
1135   pixbuf = gtk_icon_info_load_icon (icon_info, error);
1136   gtk_icon_info_free (icon_info);
1137
1138   return pixbuf;
1139 }
1140
1141 GdkPixbuf *
1142 _gtk_file_system_volume_render_icon (GtkFileSystemVolume  *volume,
1143                                      GtkWidget            *widget,
1144                                      gint                  icon_size,
1145                                      GError              **error)
1146 {
1147   GIcon *icon = NULL;
1148   GdkPixbuf *pixbuf;
1149
1150   DEBUG ("volume_get_icon_name");
1151
1152   if (IS_ROOT_VOLUME (volume))
1153     icon = g_themed_icon_new ("drive-harddisk");
1154   else if (G_IS_DRIVE (volume))
1155     icon = g_drive_get_icon (G_DRIVE (volume));
1156   else if (G_IS_VOLUME (volume))
1157     icon = g_volume_get_icon (G_VOLUME (volume));
1158   else if (G_IS_MOUNT (volume))
1159     icon = g_mount_get_icon (G_MOUNT (volume));
1160
1161   if (!icon)
1162     return NULL;
1163
1164   pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, error);
1165
1166   g_object_unref (icon);
1167
1168   return pixbuf;
1169 }
1170
1171 GtkFileSystemVolume *
1172 _gtk_file_system_volume_ref (GtkFileSystemVolume *volume)
1173 {
1174   if (IS_ROOT_VOLUME (volume))
1175     return volume;
1176
1177   if (G_IS_MOUNT (volume)  ||
1178       G_IS_VOLUME (volume) ||
1179       G_IS_DRIVE (volume))
1180     g_object_ref (volume);
1181
1182   return volume;
1183 }
1184
1185 void
1186 _gtk_file_system_volume_unref (GtkFileSystemVolume *volume)
1187 {
1188   /* Root volume doesn't need to be freed */
1189   if (IS_ROOT_VOLUME (volume))
1190     return;
1191
1192   if (G_IS_MOUNT (volume)  ||
1193       G_IS_VOLUME (volume) ||
1194       G_IS_DRIVE (volume))
1195     g_object_unref (volume);
1196 }
1197
1198 /* GFileInfo helper functions */
1199 GdkPixbuf *
1200 _gtk_file_info_render_icon (GFileInfo *info,
1201                            GtkWidget *widget,
1202                            gint       icon_size)
1203 {
1204   GIcon *icon;
1205   GdkPixbuf *pixbuf = NULL;
1206   const gchar *thumbnail_path;
1207
1208   thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
1209
1210   if (thumbnail_path)
1211     pixbuf = gdk_pixbuf_new_from_file_at_size (thumbnail_path,
1212                                                icon_size, icon_size,
1213                                                NULL);
1214
1215   if (!pixbuf)
1216     {
1217       icon = g_file_info_get_icon (info);
1218
1219       if (icon)
1220         pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
1221
1222       if (!pixbuf)
1223         {
1224            /* Use general fallback for all files without icon */
1225           icon = g_themed_icon_new ("text-x-generic");
1226           pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
1227           g_object_unref (icon);
1228         }
1229     }
1230
1231   return pixbuf;
1232 }
1233
1234 gboolean
1235 _gtk_file_info_consider_as_directory (GFileInfo *info)
1236 {
1237   GFileType type = g_file_info_get_file_type (info);
1238   
1239   return (type == G_FILE_TYPE_DIRECTORY ||
1240           type == G_FILE_TYPE_MOUNTABLE ||
1241           type == G_FILE_TYPE_SHORTCUT);
1242 }
1243