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