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