]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilesystem.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkfilesystem.c
index 200a3185c271dab3d22ba1f04918a78af13dcae6..2fdfa3df9dd448a1d14a08bb376e80c055e8a856 100644 (file)
 /* GTK - The GIMP Toolkit
- * gtkfilesystem.c: Abstract file system interfaces
+ * gtkfilesystem.c: Filesystem abstraction functions.
  * Copyright (C) 2003, Red Hat, Inc.
+ * Copyright (C) 2007-2008 Carlos Garnacho
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Carlos Garnacho <carlos@imendio.com>
  */
 
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
 
+#include "gtkfilechooser.h"
 #include "gtkfilesystem.h"
 #include "gtkicontheme.h"
+#include "gtkprivate.h"
 
-#include <string.h>
+/* #define DEBUG_MODE */
+#ifdef DEBUG_MODE
+#define DEBUG(x) g_debug (x);
+#else
+#define DEBUG(x)
+#endif
 
-struct _GtkFileInfo
-{
-  GtkFileTime modification_time;
-  gint64 size;
-  gchar *display_name;
-  gchar *display_key;
-  gchar *mime_type;
-  GtkFileIconType icon_type : 4;
-  guint is_folder : 1;
-  guint is_hidden : 1;
+#define FILES_PER_QUERY 100
+
+/* The pointers we return for a GtkFileSystemVolume are opaque tokens; they are
+ * really pointers to GDrive, GVolume or GMount objects.  We need an extra
+ * token for the fake "File System" volume.  So, we'll return a pointer to
+ * this particular string.
+ */
+static const gchar *root_volume_token = N_("File System");
+#define IS_ROOT_VOLUME(volume) ((gpointer) (volume) == (gpointer) root_volume_token)
+
+enum {
+  PROP_0,
+  PROP_FILE,
+  PROP_ENUMERATOR,
+  PROP_ATTRIBUTES
 };
 
-static void gtk_file_system_base_init (gpointer g_class);
-static void gtk_file_folder_base_init (gpointer g_class);
+enum {
+  BOOKMARKS_CHANGED,
+  VOLUMES_CHANGED,
+  FS_LAST_SIGNAL
+};
 
-GQuark
-gtk_file_system_error_quark (void)
-{
-  static GQuark quark = 0;
-  if (quark == 0)
-    quark = g_quark_from_static_string ("gtk-file-system-error-quark");
-  return quark;
-}
+enum {
+  FILES_ADDED,
+  FILES_REMOVED,
+  FILES_CHANGED,
+  FINISHED_LOADING,
+  DELETED,
+  FOLDER_LAST_SIGNAL
+};
+
+static guint fs_signals [FS_LAST_SIGNAL] = { 0, };
+
+typedef struct AsyncFuncData AsyncFuncData;
 
-/*****************************************
- *             GtkFileInfo               *
- *****************************************/
-GType
-gtk_file_info_get_type (void)
+struct GtkFileSystemPrivate
 {
-  static GType our_type = 0;
-  
-  if (our_type == 0)
-    our_type = g_boxed_type_register_static ("GtkFileInfo",
-                                            (GBoxedCopyFunc) gtk_file_info_copy,
-                                            (GBoxedFreeFunc) gtk_file_info_free);
+  GVolumeMonitor *volume_monitor;
 
-  return our_type;
-}
+  /* This list contains elements that can be
+   * of type GDrive, GVolume and GMount
+   */
+  GSList *volumes;
 
-GtkFileInfo *
-gtk_file_info_new  (void)
+  /* This list contains GtkFileSystemBookmark structs */
+  GSList *bookmarks;
+
+  GFileMonitor *bookmarks_monitor;
+};
+
+struct AsyncFuncData
 {
-  GtkFileInfo *info;
-  
-  info = g_new0 (GtkFileInfo, 1);
+  GtkFileSystem *file_system;
+  GFile *file;
+  GCancellable *cancellable;
+  gchar *attributes;
 
-  return info;
-}
+  gpointer callback;
+  gpointer data;
+};
 
-GtkFileInfo *
-gtk_file_info_copy (GtkFileInfo *info)
+struct GtkFileSystemBookmark
 {
-  GtkFileInfo *new_info;
+  GFile *file;
+  gchar *label;
+};
 
-  g_return_val_if_fail (info != NULL, NULL);
+G_DEFINE_TYPE (GtkFileSystem, _gtk_file_system, G_TYPE_OBJECT)
 
-  new_info = g_memdup (info, sizeof (GtkFileInfo));
-  if (new_info->display_name)
-    new_info->display_name = g_strdup (new_info->display_name);
-  if (new_info->mime_type)
-    new_info->mime_type = g_strdup (new_info->mime_type);
 
-  return new_info;
+/* GtkFileSystemBookmark methods */
+void
+_gtk_file_system_bookmark_free (GtkFileSystemBookmark *bookmark)
+{
+  g_object_unref (bookmark->file);
+  g_free (bookmark->label);
+  g_slice_free (GtkFileSystemBookmark, bookmark);
 }
 
-void
-gtk_file_info_free (GtkFileInfo *info)
+/* GtkFileSystem methods */
+static void
+volumes_changed (GVolumeMonitor *volume_monitor,
+                gpointer        volume,
+                gpointer        user_data)
 {
-  g_return_if_fail (info != NULL);
+  GtkFileSystem *file_system;
 
-  if (info->display_name)
-    g_free (info->display_name);
-  if (info->mime_type)
-    g_free (info->mime_type);
-  if (info->display_key)
-    g_free (info->display_key);
+  gdk_threads_enter ();
 
-  g_free (info);
+  file_system = GTK_FILE_SYSTEM (user_data);
+  g_signal_emit (file_system, fs_signals[VOLUMES_CHANGED], 0, volume);
+  gdk_threads_leave ();
 }
 
-G_CONST_RETURN gchar *
-gtk_file_info_get_display_name (const GtkFileInfo *info)
+static void
+gtk_file_system_dispose (GObject *object)
 {
-  g_return_val_if_fail (info != NULL, NULL);
-  
-  return info->display_name;
-}
-
-/**
- * gtk_file_info_get_display_key:
- * @info: a #GtkFileInfo
- * 
- * Returns results of g_utf8_collate_key() on the display name
- * for @info. This is useful when sorting a bunch of #GtkFileInfo
- * structures since the collate key will be only computed once.
- * 
- * Return value: The collate key for the display name, or %NULL
- *   if the display name hasn't been set.
- **/
-G_CONST_RETURN gchar *
-gtk_file_info_get_display_key (const GtkFileInfo *info)
-{
-  g_return_val_if_fail (info != NULL, NULL);
-
-  if (!info->display_key && info->display_name)
+  GtkFileSystem *file_system = GTK_FILE_SYSTEM (object);
+  GtkFileSystemPrivate *priv = file_system->priv;
+
+  DEBUG ("dispose");
+
+  if (priv->volumes)
     {
-      /* Since info->display_key is only a cache, we cast off the const
-       */
-      ((GtkFileInfo *)info)->display_key = g_utf8_collate_key (info->display_name, -1);
+      g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
+      g_slist_free (priv->volumes);
+      priv->volumes = NULL;
     }
-       
-  return info->display_key;
+
+  if (priv->volume_monitor)
+    {
+      g_signal_handlers_disconnect_by_func (priv->volume_monitor, volumes_changed, object);
+      g_object_unref (priv->volume_monitor);
+      priv->volume_monitor = NULL;
+    }
+
+  G_OBJECT_CLASS (_gtk_file_system_parent_class)->dispose (object);
 }
 
-void
-gtk_file_info_set_display_name (GtkFileInfo *info,
-                               const gchar *display_name)
+static void
+gtk_file_system_finalize (GObject *object)
 {
-  g_return_if_fail (info != NULL);
+  GtkFileSystem *file_system = GTK_FILE_SYSTEM (object);
+  GtkFileSystemPrivate *priv = file_system->priv;
 
-  if (info->display_name)
-    g_free (info->display_name);
-  if (info->display_key)
+  DEBUG ("finalize");
+
+  if (priv->bookmarks_monitor)
+    g_object_unref (priv->bookmarks_monitor);
+
+  if (priv->bookmarks)
     {
-      g_free (info->display_key);
-      info->display_key = NULL;
+      g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL);
+      g_slist_free (priv->bookmarks);
     }
 
-  info->display_name = g_strdup (display_name);
+  G_OBJECT_CLASS (_gtk_file_system_parent_class)->finalize (object);
 }
 
-gboolean
-gtk_file_info_get_is_folder (const GtkFileInfo *info)
+static void
+_gtk_file_system_class_init (GtkFileSystemClass *class)
 {
-  g_return_val_if_fail (info != NULL, FALSE);
-  
-  return info->is_folder;
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->dispose = gtk_file_system_dispose;
+  object_class->finalize = gtk_file_system_finalize;
+
+  fs_signals[BOOKMARKS_CHANGED] =
+    g_signal_new ("bookmarks-changed",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GtkFileSystemClass, bookmarks_changed),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+
+  fs_signals[VOLUMES_CHANGED] =
+    g_signal_new ("volumes-changed",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GtkFileSystemClass, volumes_changed),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+
+  g_type_class_add_private (object_class, sizeof (GtkFileSystemPrivate));
 }
 
-void
-gtk_file_info_set_is_folder (GtkFileInfo *info,
-                            gboolean     is_folder)
+static GFile *
+get_legacy_bookmarks_file (void)
 {
-  g_return_if_fail (info != NULL);
+  GFile *file;
+  gchar *filename;
+
+  filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
+  file = g_file_new_for_path (filename);
+  g_free (filename);
 
-  info->is_folder = is_folder != FALSE;
+  return file;
 }
 
-gboolean
-gtk_file_info_get_is_hidden (const GtkFileInfo *info)
+static GFile *
+get_bookmarks_file (void)
 {
-  g_return_val_if_fail (info != NULL, FALSE);
-  
-  return info->is_hidden;
+  GFile *file;
+  gchar *filename;
+
+  filename = g_build_filename (g_get_user_config_dir (), "gtk-3.0", "bookmarks", NULL);
+  file = g_file_new_for_path (filename);
+  g_free (filename);
+
+  return file;
 }
 
-void
-gtk_file_info_set_is_hidden (GtkFileInfo *info,
-                            gboolean     is_hidden)
+static GSList *
+read_bookmarks (GFile *file)
 {
-  g_return_if_fail (info != NULL);
+  gchar *contents;
+  gchar **lines, *space;
+  GSList *bookmarks = NULL;
+  gint i;
+
+  if (!g_file_load_contents (file, NULL, &contents,
+                            NULL, NULL, NULL))
+    return NULL;
+
+  lines = g_strsplit (contents, "\n", -1);
+
+  for (i = 0; lines[i]; i++)
+    {
+      GtkFileSystemBookmark *bookmark;
+
+      if (!*lines[i])
+       continue;
+
+      if (!g_utf8_validate (lines[i], -1, NULL))
+       continue;
 
-  info->is_hidden = is_hidden != FALSE;
+      bookmark = g_slice_new0 (GtkFileSystemBookmark);
+
+      if ((space = strchr (lines[i], ' ')) != NULL)
+       {
+         space[0] = '\0';
+         bookmark->label = g_strdup (space + 1);
+       }
+
+      bookmark->file = g_file_new_for_uri (lines[i]);
+      bookmarks = g_slist_prepend (bookmarks, bookmark);
+    }
+
+  bookmarks = g_slist_reverse (bookmarks);
+  g_strfreev (lines);
+  g_free (contents);
+
+  return bookmarks;
 }
 
-G_CONST_RETURN gchar *
-gtk_file_info_get_mime_type (const GtkFileInfo *info)
+static void
+save_bookmarks (GFile  *bookmarks_file,
+               GSList *bookmarks)
 {
-  g_return_val_if_fail (info != NULL, NULL);
-  
-  return info->mime_type;
+  GError *error = NULL;
+  GString *contents;
+  GSList *l;
+  GFile *parent_file;
+  gchar *path;
+
+  contents = g_string_new ("");
+
+  for (l = bookmarks; l; l = l->next)
+    {
+      GtkFileSystemBookmark *bookmark = l->data;
+      gchar *uri;
+
+      uri = g_file_get_uri (bookmark->file);
+      if (!uri)
+       continue;
+
+      g_string_append (contents, uri);
+
+      if (bookmark->label)
+       g_string_append_printf (contents, " %s", bookmark->label);
+
+      g_string_append_c (contents, '\n');
+      g_free (uri);
+    }
+
+  parent_file = g_file_get_parent (bookmarks_file);
+  path = g_file_get_path (parent_file);
+  if (g_mkdir_with_parents (path, 0700) == 0)
+    {
+      if (!g_file_replace_contents (bookmarks_file,
+                                    contents->str,
+                                    strlen (contents->str),
+                                    NULL, FALSE, 0, NULL,
+                                    NULL, &error))
+        {
+          g_critical ("%s", error->message);
+          g_error_free (error);
+        }
+    }
+  g_free (path);
+  g_object_unref (parent_file);
+  g_string_free (contents, TRUE);
 }
 
-void
-gtk_file_info_set_mime_type (GtkFileInfo *info,
-                            const gchar *mime_type)
+static void
+bookmarks_file_changed (GFileMonitor      *monitor,
+                       GFile             *file,
+                       GFile             *other_file,
+                       GFileMonitorEvent  event,
+                       gpointer           data)
 {
-  g_return_if_fail (info != NULL);
-  
-  if (info->mime_type)
-    g_free (info->mime_type);
+  GtkFileSystem *file_system = GTK_FILE_SYSTEM (data);
+  GtkFileSystemPrivate *priv = file_system->priv;
 
-  info->mime_type = g_strdup (mime_type);
+  switch (event)
+    {
+    case G_FILE_MONITOR_EVENT_CHANGED:
+    case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+    case G_FILE_MONITOR_EVENT_CREATED:
+    case G_FILE_MONITOR_EVENT_DELETED:
+      g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL);
+      g_slist_free (priv->bookmarks);
+
+      priv->bookmarks = read_bookmarks (file);
+
+      gdk_threads_enter ();
+      g_signal_emit (data, fs_signals[BOOKMARKS_CHANGED], 0);
+      gdk_threads_leave ();
+      break;
+    default:
+      /* ignore at the moment */
+      break;
+    }
 }
 
-GtkFileTime
-gtk_file_info_get_modification_time (const GtkFileInfo *info)
+static gboolean
+mount_referenced_by_volume_activation_root (GList *volumes, GMount *mount)
 {
-  g_return_val_if_fail (info != NULL, 0);
+  GList *l;
+  GFile *mount_root;
+  gboolean ret;
+
+  ret = FALSE;
+
+  mount_root = g_mount_get_root (mount);
+
+  for (l = volumes; l != NULL; l = l->next)
+    {
+      GVolume *volume = G_VOLUME (l->data);
+      GFile *volume_activation_root;
+
+      volume_activation_root = g_volume_get_activation_root (volume);
+      if (volume_activation_root != NULL)
+        {
+          if (g_file_has_prefix (volume_activation_root, mount_root))
+            {
+              ret = TRUE;
+              g_object_unref (volume_activation_root);
+              break;
+            }
+          g_object_unref (volume_activation_root);
+        }
+    }
 
-  return info->modification_time;
+  g_object_unref (mount_root);
+  return ret;
 }
 
-void
-gtk_file_info_set_modification_time (GtkFileInfo *info,
-                                    GtkFileTime  modification_time)
+static void
+get_volumes_list (GtkFileSystem *file_system)
 {
-  g_return_if_fail (info != NULL);
+  GtkFileSystemPrivate *priv = file_system->priv;
+  GList *l, *ll;
+  GList *drives;
+  GList *volumes;
+  GList *mounts;
+  GDrive *drive;
+  GVolume *volume;
+  GMount *mount;
+
+  if (priv->volumes)
+    {
+      g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
+      g_slist_free (priv->volumes);
+      priv->volumes = NULL;
+    }
+
+  /* first go through all connected drives */
+  drives = g_volume_monitor_get_connected_drives (priv->volume_monitor);
+
+  for (l = drives; l != NULL; l = l->next)
+    {
+      drive = l->data;
+      volumes = g_drive_get_volumes (drive);
+
+      if (volumes)
+        {
+          for (ll = volumes; ll != NULL; ll = ll->next)
+            {
+              volume = ll->data;
+              mount = g_volume_get_mount (volume);
+
+              if (mount)
+                {
+                  /* Show mounted volume */
+                  priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
+                  g_object_unref (mount);
+                }
+              else
+                {
+                  /* Do show the unmounted volumes in the sidebar;
+                   * this is so the user can mount it (in case automounting
+                   * is off).
+                   *
+                   * Also, even if automounting is enabled, this gives a visual
+                   * cue that the user should remember to yank out the media if
+                   * he just unmounted it.
+                   */
+                  priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
+                }
+
+             g_object_unref (volume);
+            }
   
-  info->modification_time = modification_time;
+           g_list_free (volumes);
+        }
+      else if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive))
+       {
+         /* If the drive has no mountable volumes and we cannot detect media change.. we
+          * display the drive in the sidebar so the user can manually poll the drive by
+          * right clicking and selecting "Rescan..."
+          *
+          * This is mainly for drives like floppies where media detection doesn't
+          * work.. but it's also for human beings who like to turn off media detection
+          * in the OS to save battery juice.
+          */
+
+         priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (drive));
+       }
+
+      g_object_unref (drive);
+    }
+
+  g_list_free (drives);
+
+  /* add all volumes that is not associated with a drive */
+  volumes = g_volume_monitor_get_volumes (priv->volume_monitor);
+
+  for (l = volumes; l != NULL; l = l->next)
+    {
+      volume = l->data;
+      drive = g_volume_get_drive (volume);
+
+      if (drive)
+        {
+          g_object_unref (drive);
+          continue;
+        }
+
+      mount = g_volume_get_mount (volume);
+
+      if (mount)
+        {
+          /* show this mount */
+          priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
+          g_object_unref (mount);
+        }
+      else
+        {
+          /* see comment above in why we add an icon for a volume */
+          priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
+        }
+
+      g_object_unref (volume);
+    }
+
+  /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
+  mounts = g_volume_monitor_get_mounts (priv->volume_monitor);
+
+  for (l = mounts; l != NULL; l = l->next)
+    {
+      mount = l->data;
+      volume = g_mount_get_volume (mount);
+
+      if (volume)
+        {
+          g_object_unref (volume);
+          continue;
+        }
+
+      /* if there's exists one or more volumes with an activation root inside the mount,
+       * don't display the mount
+       */
+      if (mount_referenced_by_volume_activation_root (volumes, mount))
+        {
+          g_object_unref (mount);
+          continue;
+        }
+
+      /* show this mount */
+      priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
+      g_object_unref (mount);
+    }
+
+  g_list_free (volumes);
+
+  g_list_free (mounts);
 }
 
-gint64
-gtk_file_info_get_size (const GtkFileInfo *info)
+static void
+_gtk_file_system_init (GtkFileSystem *file_system)
 {
-  g_return_val_if_fail (info != NULL, 0);
-  
-  return info->size;
+  GtkFileSystemPrivate *priv;
+  GFile *bookmarks_file;
+  GError *error = NULL;
+
+  DEBUG ("init");
+
+  file_system->priv = G_TYPE_INSTANCE_GET_PRIVATE (file_system,
+                                                   GTK_TYPE_FILE_SYSTEM,
+                                                   GtkFileSystemPrivate);
+  priv = file_system->priv;
+
+  /* Volumes */
+  priv->volume_monitor = g_volume_monitor_get ();
+
+  g_signal_connect (priv->volume_monitor, "mount-added",
+                   G_CALLBACK (volumes_changed), file_system);
+  g_signal_connect (priv->volume_monitor, "mount-removed",
+                   G_CALLBACK (volumes_changed), file_system);
+  g_signal_connect (priv->volume_monitor, "mount-changed",
+                   G_CALLBACK (volumes_changed), file_system);
+  g_signal_connect (priv->volume_monitor, "volume-added",
+                   G_CALLBACK (volumes_changed), file_system);
+  g_signal_connect (priv->volume_monitor, "volume-removed",
+                   G_CALLBACK (volumes_changed), file_system);
+  g_signal_connect (priv->volume_monitor, "volume-changed",
+                   G_CALLBACK (volumes_changed), file_system);
+  g_signal_connect (priv->volume_monitor, "drive-connected",
+                   G_CALLBACK (volumes_changed), file_system);
+  g_signal_connect (priv->volume_monitor, "drive-disconnected",
+                   G_CALLBACK (volumes_changed), file_system);
+  g_signal_connect (priv->volume_monitor, "drive-changed",
+                   G_CALLBACK (volumes_changed), file_system);
+
+  /* Bookmarks */
+  bookmarks_file = get_bookmarks_file ();
+  priv->bookmarks = read_bookmarks (bookmarks_file);
+  if (!priv->bookmarks)
+    {
+      GFile *legacy_bookmarks_file;
+
+      /* Read the legacy one and write it to the new one */
+      legacy_bookmarks_file = get_legacy_bookmarks_file ();
+      priv->bookmarks = read_bookmarks (legacy_bookmarks_file);
+      save_bookmarks (bookmarks_file, priv->bookmarks);
+
+      g_object_unref (legacy_bookmarks_file);
+    }
+
+  priv->bookmarks_monitor = g_file_monitor_file (bookmarks_file,
+                                                G_FILE_MONITOR_NONE,
+                                                NULL, &error);
+  if (error)
+    {
+      g_warning ("%s", error->message);
+      g_error_free (error);
+    }
+  else
+    g_signal_connect (priv->bookmarks_monitor, "changed",
+                     G_CALLBACK (bookmarks_file_changed), file_system);
+
+  g_object_unref (bookmarks_file);
 }
 
-void
-gtk_file_info_set_size (GtkFileInfo *info,
-                       gint64       size)
+/* GtkFileSystem public methods */
+GtkFileSystem *
+_gtk_file_system_new (void)
 {
-  g_return_if_fail (info != NULL);
-  g_return_if_fail (size >= 0);
-  
-  info->size = size;
+  return g_object_new (GTK_TYPE_FILE_SYSTEM, NULL);
 }
 
-void
-gtk_file_info_set_icon_type  (GtkFileInfo      *info,
-                             GtkFileIconType   icon_type)
+GSList *
+_gtk_file_system_list_volumes (GtkFileSystem *file_system)
 {
-  g_return_if_fail (info != NULL);
+  GtkFileSystemPrivate *priv = file_system->priv;
+  GSList *list;
+
+  DEBUG ("list_volumes");
 
-  info->icon_type = icon_type;
+  get_volumes_list (file_system);
+
+  list = g_slist_copy (priv->volumes);
+
+#ifndef G_OS_WIN32
+  /* Prepend root volume */
+  list = g_slist_prepend (list, (gpointer) root_volume_token);
+#endif
+
+  return list;
 }
 
-GtkFileIconType
-gtk_file_info_get_icon_type (const GtkFileInfo *info)
+GSList *
+_gtk_file_system_list_bookmarks (GtkFileSystem *file_system)
 {
-  g_return_val_if_fail (info != NULL, GTK_FILE_ICON_REGULAR);
+  GtkFileSystemPrivate *priv = file_system->priv;
+  GSList *bookmarks, *files = NULL;
 
-  return info->icon_type;
-}
+  DEBUG ("list_bookmarks");
 
-typedef struct _IconCacheElement IconCacheElement;
+  bookmarks = priv->bookmarks;
 
-struct _IconCacheElement
-{
-  gint size;
-  GdkPixbuf *pixbuf;
-};
+  while (bookmarks)
+    {
+      GtkFileSystemBookmark *bookmark;
 
-static void
-icon_cache_element_free (IconCacheElement *element)
-{
-  if (element->pixbuf)
-    g_object_unref (element->pixbuf);
-  g_free (element);
+      bookmark = bookmarks->data;
+      bookmarks = bookmarks->next;
+
+      files = g_slist_prepend (files, g_object_ref (bookmark->file));
+    }
+
+  return g_slist_reverse (files);
 }
 
 static void
-icon_theme_changed (GtkIconTheme *icon_theme)
+free_async_data (AsyncFuncData *async_data)
 {
-  GHashTable *cache;
-  
-  /* Difference from the initial creation is that we don't
-   * reconnect the signal
-   */
-  cache = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                (GDestroyNotify)g_free,
-                                (GDestroyNotify)icon_cache_element_free);
-  g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
-                         cache, (GDestroyNotify)g_hash_table_destroy);
+  g_object_unref (async_data->file_system);
+  g_object_unref (async_data->file);
+  g_object_unref (async_data->cancellable);
+
+  g_free (async_data->attributes);
+  g_free (async_data);
 }
 
-static GdkPixbuf *
-get_cached_icon (GtkWidget   *widget,
-                const gchar *name,
-                gint         pixel_size)
+static void
+query_info_callback (GObject      *source_object,
+                    GAsyncResult *result,
+                    gpointer      user_data)
 {
-  GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
-  GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
-  IconCacheElement *element;
-  
-  if (!cache)
-    {
-      cache = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                    (GDestroyNotify)g_free,
-                                    (GDestroyNotify)icon_cache_element_free);
-      
-      g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
-                             cache, (GDestroyNotify)g_hash_table_destroy);
-      g_signal_connect (icon_theme, "changed",
-                       G_CALLBACK (icon_theme_changed), NULL);
-    }
+  AsyncFuncData *async_data;
+  GError *error = NULL;
+  GFileInfo *file_info;
+  GFile *file;
 
-  element = g_hash_table_lookup (cache, name);
-  if (!element)
-    {
-      element = g_new0 (IconCacheElement, 1);
-      g_hash_table_insert (cache, g_strdup (name), element);
-    }
+  DEBUG ("query_info_callback");
+
+  file = G_FILE (source_object);
+  async_data = (AsyncFuncData *) user_data;
+  file_info = g_file_query_info_finish (file, result, &error);
 
-  if (element->size != pixel_size)
+  if (async_data->callback)
     {
-      if (element->pixbuf)
-       g_object_unref (element->pixbuf);
-      element->size = pixel_size;
-      element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
-                                                 pixel_size, 0, NULL);
+      gdk_threads_enter ();
+      ((GtkFileSystemGetInfoCallback) async_data->callback) (async_data->cancellable,
+                                                            file_info, error, async_data->data);
+      gdk_threads_leave ();
     }
 
-  return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
+  if (file_info)
+    g_object_unref (file_info);
+
+  if (error)
+    g_error_free (error);
+
+  free_async_data (async_data);
 }
-                
 
-GdkPixbuf *
-gtk_file_info_render_icon (const GtkFileInfo *info,
-                          GtkWidget         *widget,
-                          gint               pixel_size)
+GCancellable *
+_gtk_file_system_get_info (GtkFileSystem                *file_system,
+                          GFile                        *file,
+                          const gchar                  *attributes,
+                          GtkFileSystemGetInfoCallback  callback,
+                          gpointer                      data)
 {
-  const gchar *separator;
-  GdkPixbuf *pixbuf;
-  GString *icon_name;
-
-  g_return_val_if_fail (info != NULL, NULL);
-  g_return_val_if_fail (widget != NULL, NULL);
-  g_return_val_if_fail (pixel_size > 0, NULL);
+  GCancellable *cancellable;
+  AsyncFuncData *async_data;
 
-  if (info->icon_type != GTK_FILE_ICON_REGULAR)
-    {
-      const char *name = NULL; /* Quiet gcc */
-      
-      switch (info->icon_type)
-       {
-       case GTK_FILE_ICON_BLOCK_DEVICE:
-          name ="gnome-fs-blockdev";
-         break;
-       case GTK_FILE_ICON_BROKEN_SYMBOLIC_LINK:
-         name = "gnome-fs-symlink";
-         break;
-       case GTK_FILE_ICON_CHARACTER_DEVICE:
-         name = "gnome-fs-chardev";
-         break;
-       case GTK_FILE_ICON_DIRECTORY:
-         name = "gnome-fs-directory";
-         break;
-       case GTK_FILE_ICON_EXECUTABLE:
-         name ="gnome-fs-executable";
-         break;
-       case GTK_FILE_ICON_FIFO:
-         name = "gnome-fs-fifo";
-         break;
-       case GTK_FILE_ICON_SOCKET:
-         name = "gnome-fs-socket";
-         break;
-       case GTK_FILE_ICON_REGULAR:
-         g_assert_not_reached ();
-       }
+  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
 
-      return get_cached_icon (widget, name, pixel_size);
-    }
-  
-  if (!info->mime_type)
-    return NULL;
+  cancellable = g_cancellable_new ();
 
-  separator = strchr (info->mime_type, '/');
-  if (!separator)
-    return NULL;
+  async_data = g_new0 (AsyncFuncData, 1);
+  async_data->file_system = g_object_ref (file_system);
+  async_data->file = g_object_ref (file);
+  async_data->cancellable = g_object_ref (cancellable);
 
-  icon_name = g_string_new ("gnome-mime-");
-  g_string_append_len (icon_name, info->mime_type, separator - info->mime_type);
-  g_string_append_c (icon_name, '-');
-  g_string_append (icon_name, separator + 1);
-  pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
-  g_string_free (icon_name, TRUE);
-  if (pixbuf)
-    return pixbuf;
+  async_data->callback = callback;
+  async_data->data = data;
 
-  icon_name = g_string_new ("gnome-mime-");
-  g_string_append_len (icon_name, info->mime_type, separator - info->mime_type);
-  pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
-  g_string_free (icon_name, TRUE);
-  if (pixbuf)
-    return pixbuf;
+  g_file_query_info_async (file,
+                          attributes,
+                          G_FILE_QUERY_INFO_NONE,
+                          G_PRIORITY_DEFAULT,
+                          cancellable,
+                          query_info_callback,
+                          async_data);
 
-  return get_cached_icon (widget, "gnome-fs-regular", pixel_size);
+  return cancellable;
 }
 
-/*****************************************
- *             GtkFileSystem             *
- *****************************************/
-GType
-gtk_file_system_get_type (void)
+static void
+drive_poll_for_media_cb (GObject      *source_object,
+                         GAsyncResult *result,
+                         gpointer      user_data)
 {
-  static GType file_system_type = 0;
+  AsyncFuncData *async_data;
+  GError *error = NULL;
 
-  if (!file_system_type)
-    {
-      static const GTypeInfo file_system_info =
-      {
-       sizeof (GtkFileSystemIface),  /* class_size */
-       gtk_file_system_base_init,    /* base_init */
-       NULL,                         /* base_finalize */
-      };
-
-      file_system_type = g_type_register_static (G_TYPE_INTERFACE,
-                                                "GtkFileSystem",
-                                                &file_system_info, 0);
-
-      g_type_interface_add_prerequisite (file_system_type, G_TYPE_OBJECT);
-    }
+  g_drive_poll_for_media_finish (G_DRIVE (source_object), result, &error);
+  async_data = (AsyncFuncData *) user_data;
+
+  gdk_threads_enter ();
+  ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
+                                                            (GtkFileSystemVolume *) source_object,
+                                                            error, async_data->data);
+  gdk_threads_leave ();
 
-  return file_system_type;
+  if (error)
+    g_error_free (error);
 }
 
 static void
-gtk_file_system_base_init (gpointer g_class)
+volume_mount_cb (GObject      *source_object,
+                GAsyncResult *result,
+                gpointer      user_data)
 {
-  static gboolean initialized = FALSE;
-  
-  if (!initialized)
-    {
-      GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
-
-      g_signal_new ("roots-changed",
-                   iface_type,
-                   G_SIGNAL_RUN_LAST,
-                   G_STRUCT_OFFSET (GtkFileSystemIface, roots_changed),
-                   NULL, NULL,
-                   g_cclosure_marshal_VOID__VOID,
-                   G_TYPE_NONE, 0);
-      g_signal_new ("bookmarks-changed",
-                   iface_type,
-                   G_SIGNAL_RUN_LAST,
-                   G_STRUCT_OFFSET (GtkFileSystemIface, bookmarks_changed),
-                   NULL, NULL,
-                   g_cclosure_marshal_VOID__VOID,
-                   G_TYPE_NONE, 0);
-
-      initialized = TRUE;
-    }
+  AsyncFuncData *async_data;
+  GError *error = NULL;
+
+  g_volume_mount_finish (G_VOLUME (source_object), result, &error);
+  async_data = (AsyncFuncData *) user_data;
+
+  gdk_threads_enter ();
+  ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
+                                                            (GtkFileSystemVolume *) source_object,
+                                                            error, async_data->data);
+  gdk_threads_leave ();
+
+  if (error)
+    g_error_free (error);
 }
 
-GSList *
-gtk_file_system_list_roots (GtkFileSystem  *file_system)
+GCancellable *
+_gtk_file_system_mount_volume (GtkFileSystem                    *file_system,
+                              GtkFileSystemVolume              *volume,
+                              GMountOperation                  *mount_operation,
+                              GtkFileSystemVolumeMountCallback  callback,
+                              gpointer                          data)
 {
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+  GCancellable *cancellable;
+  AsyncFuncData *async_data;
+  gboolean handled = FALSE;
+
+  DEBUG ("volume_mount");
+
+  cancellable = g_cancellable_new ();
+
+  async_data = g_new0 (AsyncFuncData, 1);
+  async_data->file_system = g_object_ref (file_system);
+  async_data->cancellable = g_object_ref (cancellable);
+
+  async_data->callback = callback;
+  async_data->data = data;
+
+  if (G_IS_DRIVE (volume))
+    {
+      /* this path happens for drives that are not polled by the OS and where the last media
+       * check indicated that no media was available. So the thing to do here is to
+       * invoke poll_for_media() on the drive
+       */
+      g_drive_poll_for_media (G_DRIVE (volume), cancellable, drive_poll_for_media_cb, async_data);
+      handled = TRUE;
+    }
+  else if (G_IS_VOLUME (volume))
+    {
+      g_volume_mount (G_VOLUME (volume), G_MOUNT_MOUNT_NONE, mount_operation, cancellable, volume_mount_cb, async_data);
+      handled = TRUE;
+    }
+
+  if (!handled)
+    free_async_data (async_data);
 
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_roots (file_system);
+  return cancellable;
 }
 
-GtkFileInfo *
-gtk_file_system_get_root_info  (GtkFileSystem     *file_system,
-                               const GtkFilePath *path,
-                               GtkFileInfoType    types,
-                               GError           **error)
+static void
+enclosing_volume_mount_cb (GObject      *source_object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
 {
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
-  g_return_val_if_fail (path != NULL, NULL);
-  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+  GtkFileSystemVolume *volume;
+  AsyncFuncData *async_data;
+  GError *error = NULL;
+
+  async_data = (AsyncFuncData *) user_data;
+  g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error);
+  volume = _gtk_file_system_get_volume_for_file (async_data->file_system, G_FILE (source_object));
 
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_root_info (file_system, path, types, error);
+  /* Silently drop G_IO_ERROR_ALREADY_MOUNTED error for gvfs backends without visible mounts. */
+  /* Better than doing query_info with additional I/O every time. */
+  if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED))
+    g_clear_error (&error);
+
+  gdk_threads_enter ();
+  ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable, volume,
+                                                            error, async_data->data);
+  gdk_threads_leave ();
+
+  if (error)
+    g_error_free (error);
+
+  _gtk_file_system_volume_unref (volume);
 }
 
-GtkFileFolder *
-gtk_file_system_get_folder (GtkFileSystem     *file_system,
-                           const GtkFilePath *path,
-                           GtkFileInfoType    types,
-                           GError           **error)
+GCancellable *
+_gtk_file_system_mount_enclosing_volume (GtkFileSystem                     *file_system,
+                                        GFile                             *file,
+                                        GMountOperation                   *mount_operation,
+                                        GtkFileSystemVolumeMountCallback   callback,
+                                        gpointer                           data)
 {
+  GCancellable *cancellable;
+  AsyncFuncData *async_data;
+
   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
-  g_return_val_if_fail (path != NULL, NULL);
-  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  DEBUG ("mount_enclosing_volume");
 
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_folder (file_system, path, types, error);
+  cancellable = g_cancellable_new ();
+
+  async_data = g_new0 (AsyncFuncData, 1);
+  async_data->file_system = g_object_ref (file_system);
+  async_data->file = g_object_ref (file);
+  async_data->cancellable = g_object_ref (cancellable);
+
+  async_data->callback = callback;
+  async_data->data = data;
+
+  g_file_mount_enclosing_volume (file,
+                                G_MOUNT_MOUNT_NONE,
+                                mount_operation,
+                                cancellable,
+                                enclosing_volume_mount_cb,
+                                async_data);
+  return cancellable;
 }
 
 gboolean
-gtk_file_system_create_folder(GtkFileSystem     *file_system,
-                             const GtkFilePath *path,
-                             GError           **error)
-{
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
-  g_return_val_if_fail (path != NULL, FALSE);
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->create_folder (file_system, path, error);
-}
-
-/**
- * gtk_file_system_get_parent:
- * @file_system: a #GtkFileSystem
- * @path: base path name
- * @parent: location to store parent path name
- * @error: location to store error, or %NULL
- * 
- * Gets the name of the parent folder of a file.
- * 
- * Return value: TRUE if the operation was successful; note that in this case @parent
- * can be returned as %NULL if the base @path has no parent folder (i.e. if it is
- * already a file system root).  If the operation fails, this function returns FALSE
- * and sets the @error value if it is specified.
- **/
-gboolean
-gtk_file_system_get_parent (GtkFileSystem     *file_system,
-                           const GtkFilePath *path,
-                           GtkFilePath      **parent,
-                           GError           **error)
+_gtk_file_system_insert_bookmark (GtkFileSystem  *file_system,
+                                 GFile          *file,
+                                 gint            position,
+                                 GError        **error)
 {
-  GtkFilePath *tmp_parent = NULL;
-  gboolean result;
-  
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
-  g_return_val_if_fail (path != NULL, FALSE);
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  GtkFileSystemPrivate *priv = file_system->priv;
+  GSList *bookmarks;
+  GtkFileSystemBookmark *bookmark;
+  gboolean result = TRUE;
+  GFile *bookmarks_file;
 
-  result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_parent (file_system, path, &tmp_parent, error);
-  g_assert (result || tmp_parent == NULL);
+  bookmarks = priv->bookmarks;
 
-  if (parent)
-    *parent = tmp_parent;
-  else
-    gtk_file_path_free (tmp_parent);
-  
-  return result;
+  while (bookmarks)
+    {
+      bookmark = bookmarks->data;
+      bookmarks = bookmarks->next;
+
+      if (g_file_equal (bookmark->file, file))
+       {
+         /* File is already in bookmarks */
+         result = FALSE;
+         break;
+       }
+    }
+
+  if (!result)
+    {
+      gchar *uri = g_file_get_uri (file);
+
+      g_set_error (error,
+                  GTK_FILE_CHOOSER_ERROR,
+                  GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
+                  "%s already exists in the bookmarks list",
+                  uri);
+
+      g_free (uri);
+
+      return FALSE;
+    }
+
+  bookmark = g_slice_new0 (GtkFileSystemBookmark);
+  bookmark->file = g_object_ref (file);
+
+  priv->bookmarks = g_slist_insert (priv->bookmarks, bookmark, position);
+
+  bookmarks_file = get_bookmarks_file ();
+  save_bookmarks (bookmarks_file, priv->bookmarks);
+  g_object_unref (bookmarks_file);
+
+  g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
+
+  return TRUE;
 }
 
-GtkFilePath *
-gtk_file_system_make_path (GtkFileSystem    *file_system,
-                         const GtkFilePath *base_path,
-                         const gchar       *display_name,
-                         GError           **error)
-{
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
-  g_return_val_if_fail (base_path != NULL, NULL);
-  g_return_val_if_fail (display_name != NULL, NULL);
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->make_path (file_system, base_path, display_name, error);
-}
-
-/**
- * gtk_file_system_parse:
- * @file_system: a #GtkFileSystem
- * @base_path: reference folder with respect to which relative
- *            paths should be interpreted.
- * @str: the string to parse
- * @folder: location to store folder portion of result, or %NULL
- * @file_part: location to store file portion of result, or %NULL
- * @error: location to store error, or %NULL
- * 
- * Given a string entered by a user, parse it (possibly using
- * heuristics) into a folder path and a UTF-8 encoded
- * filename part. (Suitable for passing to gtk_file_system_make_path())
- *
- * Note that the returned filename point may point to a subfolder
- * of the returned folder. Adding a trailing path separator is needed
- * to enforce the interpretation as a folder name.
- *
- * If parsing fails because the syntax of @str is not understood,
- * and error of type GTK_FILE_SYSTEM_ERROR_BAD_FILENAME will
- * be set in @error and %FALSE returned.
- *
- * If parsing fails because a path was encountered that doesn't
- * exist on the filesystem, then an error of type
- * %GTK_FILE_SYSTEM_ERROR_NONEXISTENT will be set in @error
- * and %FALSE returned. (This only applies to parsing relative paths,
- * not to interpretation of @file_part. No check is made as
- * to whether @file_part exists.)
- *
- * Return value: %TRUE if the parsing succeeds, otherwise, %FALSE.
- **/
 gboolean
-gtk_file_system_parse (GtkFileSystem     *file_system,
-                      const GtkFilePath *base_path,
-                      const gchar       *str,
-                      GtkFilePath      **folder,
-                      gchar            **file_part,
-                      GError           **error)
+_gtk_file_system_remove_bookmark (GtkFileSystem  *file_system,
+                                 GFile          *file,
+                                 GError        **error)
 {
-  GtkFilePath *tmp_folder = NULL;
-  gchar *tmp_file_part = NULL;
-  gboolean result;
+  GtkFileSystemPrivate *priv = file_system->priv;
+  GtkFileSystemBookmark *bookmark;
+  GSList *bookmarks;
+  gboolean result = FALSE;
+  GFile *bookmarks_file;
 
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
-  g_return_val_if_fail (base_path != NULL, FALSE);
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  if (!priv->bookmarks)
+    return FALSE;
 
+  bookmarks = priv->bookmarks;
 
-  result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->parse (file_system, base_path, str,
-                                                          &tmp_folder, &tmp_file_part,
-                                                          error);
-  g_assert (result || (tmp_folder == NULL && tmp_file_part == NULL));
+  while (bookmarks)
+    {
+      bookmark = bookmarks->data;
 
-  if (folder)
-    *folder = tmp_folder;
-  else
-    gtk_file_path_free (tmp_folder);
+      if (g_file_equal (bookmark->file, file))
+       {
+         result = TRUE;
+         priv->bookmarks = g_slist_remove_link (priv->bookmarks, bookmarks);
+         _gtk_file_system_bookmark_free (bookmark);
+         g_slist_free_1 (bookmarks);
+         break;
+       }
 
-  if (file_part)
-    *file_part = tmp_file_part;
-  else
-    g_free (tmp_file_part);
+      bookmarks = bookmarks->next;
+    }
 
-  return result;
-}
+  if (!result)
+    {
+      gchar *uri = g_file_get_uri (file);
 
+      g_set_error (error,
+                  GTK_FILE_CHOOSER_ERROR,
+                  GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
+                  "%s does not exist in the bookmarks list",
+                  uri);
 
-gchar *
-gtk_file_system_path_to_uri (GtkFileSystem     *file_system,
-                            const GtkFilePath *path)
-{
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
-  g_return_val_if_fail (path != NULL, NULL);
-  
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->path_to_uri (file_system, path);
+      g_free (uri);
+
+      return FALSE;
+    }
+
+  bookmarks_file = get_bookmarks_file ();
+  save_bookmarks (bookmarks_file, priv->bookmarks);
+  g_object_unref (bookmarks_file);
+
+  g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
+
+  return TRUE;
 }
 
 gchar *
-gtk_file_system_path_to_filename (GtkFileSystem     *file_system,
-                                 const GtkFilePath *path)
+_gtk_file_system_get_bookmark_label (GtkFileSystem *file_system,
+                                    GFile         *file)
 {
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
-  g_return_val_if_fail (path != NULL, NULL);
-  
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->path_to_filename (file_system, path);
+  GtkFileSystemPrivate *priv = file_system->priv;
+  GSList *bookmarks;
+  gchar *label = NULL;
+
+  DEBUG ("get_bookmark_label");
+
+  bookmarks = priv->bookmarks;
+
+  while (bookmarks)
+    {
+      GtkFileSystemBookmark *bookmark;
+
+      bookmark = bookmarks->data;
+      bookmarks = bookmarks->next;
+
+      if (g_file_equal (file, bookmark->file))
+       {
+         label = g_strdup (bookmark->label);
+         break;
+       }
+    }
+
+  return label;
 }
 
-GtkFilePath *
-gtk_file_system_uri_to_path (GtkFileSystem *file_system,
-                            const gchar    *uri)
+void
+_gtk_file_system_set_bookmark_label (GtkFileSystem *file_system,
+                                    GFile         *file,
+                                    const gchar   *label)
 {
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
-  g_return_val_if_fail (uri != NULL, NULL);
-  
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->uri_to_path (file_system, uri);
+  GtkFileSystemPrivate *priv = file_system->priv;
+  gboolean changed = FALSE;
+  GFile *bookmarks_file;
+  GSList *bookmarks;
+
+  DEBUG ("set_bookmark_label");
+
+  bookmarks = priv->bookmarks;
+
+  while (bookmarks)
+    {
+      GtkFileSystemBookmark *bookmark;
+
+      bookmark = bookmarks->data;
+      bookmarks = bookmarks->next;
+
+      if (g_file_equal (file, bookmark->file))
+       {
+          g_free (bookmark->label);
+         bookmark->label = g_strdup (label);
+         changed = TRUE;
+         break;
+       }
+    }
+
+  bookmarks_file = get_bookmarks_file ();
+  save_bookmarks (bookmarks_file, priv->bookmarks);
+  g_object_unref (bookmarks_file);
+
+  if (changed)
+    g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
 }
 
-GtkFilePath *
-gtk_file_system_filename_to_path (GtkFileSystem *file_system,
-                                 const gchar   *filename)
+GtkFileSystemVolume *
+_gtk_file_system_get_volume_for_file (GtkFileSystem *file_system,
+                                     GFile         *file)
 {
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
-  g_return_val_if_fail (filename != NULL, NULL);
-
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->filename_to_path (file_system, filename);
-}
-
-/**
- * gtk_file_system_add_bookmark:
- * @file_system: a #GtkFileSystem
- * @bookmark: path of the bookmark to add
- * @error: location to store error, or %NULL
- * 
- * Adds a bookmark folder to the user's bookmarks list.  If the operation succeeds,
- * the "bookmarks_changed" signal will be emitted.
- * 
- * Return value: TRUE if the operation succeeds, FALSE otherwise.  In the latter case,
- * the @error value will be set.
- **/
-gboolean
-gtk_file_system_add_bookmark (GtkFileSystem     *file_system,
-                             const GtkFilePath *path,
-                             GError           **error)
-{
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
-  g_return_val_if_fail (path != NULL, FALSE);
-
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->add_bookmark (file_system, path, error);
-}
-
-/**
- * gtk_file_system_remove_bookmark:
- * @file_system: a #GtkFileSystem
- * @bookmark: path of the bookmark to remove
- * @error: location to store error, or %NULL
- * 
- * Removes a bookmark folder from the user's bookmarks list.  If the operation
- * succeeds, the "bookmarks_changed" signal will be emitted.
- * 
- * Return value: TRUE if the operation succeeds, FALSE otherwise.  In the latter
- * case, the @error value will be set.
- **/
-gboolean
-gtk_file_system_remove_bookmark (GtkFileSystem     *file_system,
-                                const GtkFilePath *path,
-                                GError           **error)
-{
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
-  g_return_val_if_fail (path != NULL, FALSE);
+  GMount *mount;
+
+  DEBUG ("get_volume_for_file");
+
+  mount = g_file_find_enclosing_mount (file, NULL, NULL);
 
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->remove_bookmark (file_system, path, error);
+  if (!mount && g_file_is_native (file))
+    return (GtkFileSystemVolume *) root_volume_token;
+
+  return (GtkFileSystemVolume *) mount;
 }
 
-/**
- * gtk_file_system_list_bookmarks:
- * @file_system: a #GtkFileSystem
- * 
- * Queries the list of bookmarks in the file system.
- * 
- * Return value: A list of #GtkFilePath, or NULL if there are no configured
- * bookmarks.  You should use gtk_file_paths_free() to free this list.
- *
- * See also: gtk_file_system_get_supports_bookmarks()
- **/
-GSList *
-gtk_file_system_list_bookmarks (GtkFileSystem *file_system)
+/* GtkFileSystemVolume public methods */
+gchar *
+_gtk_file_system_volume_get_display_name (GtkFileSystemVolume *volume)
 {
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
-
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_bookmarks (file_system);
+  DEBUG ("volume_get_display_name");
+
+  if (IS_ROOT_VOLUME (volume))
+    return g_strdup (_(root_volume_token));
+  if (G_IS_DRIVE (volume))
+    return g_drive_get_name (G_DRIVE (volume));
+  else if (G_IS_MOUNT (volume))
+    return g_mount_get_name (G_MOUNT (volume));
+  else if (G_IS_VOLUME (volume))
+    return g_volume_get_name (G_VOLUME (volume));
+
+  return NULL;
 }
 
-/*****************************************
- *             GtkFileFolder             *
- *****************************************/
-GType
-gtk_file_folder_get_type (void)
+gboolean
+_gtk_file_system_volume_is_mounted (GtkFileSystemVolume *volume)
 {
-  static GType file_folder_type = 0;
+  gboolean mounted;
+
+  DEBUG ("volume_is_mounted");
 
-  if (!file_folder_type)
+  if (IS_ROOT_VOLUME (volume))
+    return TRUE;
+
+  mounted = FALSE;
+
+  if (G_IS_MOUNT (volume))
+    mounted = TRUE;
+  else if (G_IS_VOLUME (volume))
     {
-      static const GTypeInfo file_folder_info =
-      {
-       sizeof (GtkFileFolderIface),  /* class_size */
-       gtk_file_folder_base_init,    /* base_init */
-       NULL,                         /* base_finalize */
-      };
-
-      file_folder_type = g_type_register_static (G_TYPE_INTERFACE,
-                                                "GtkFileFolder",
-                                                &file_folder_info, 0);
-      
-      g_type_interface_add_prerequisite (file_folder_type, G_TYPE_OBJECT);
+      GMount *mount;
+
+      mount = g_volume_get_mount (G_VOLUME (volume));
+
+      if (mount)
+        {
+          mounted = TRUE;
+          g_object_unref (mount);
+        }
     }
 
-  return file_folder_type;
+  return mounted;
 }
 
-static void
-gtk_file_folder_base_init (gpointer g_class)
+GFile *
+_gtk_file_system_volume_get_root (GtkFileSystemVolume *volume)
 {
-  static gboolean initialized = FALSE;
-  
-  if (!initialized)
+  GFile *file = NULL;
+
+  DEBUG ("volume_get_base");
+
+  if (IS_ROOT_VOLUME (volume))
+    return g_file_new_for_uri ("file:///");
+
+  if (G_IS_MOUNT (volume))
+    file = g_mount_get_root (G_MOUNT (volume));
+  else if (G_IS_VOLUME (volume))
     {
-      GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
-
-      g_signal_new ("deleted",
-                   iface_type,
-                   G_SIGNAL_RUN_LAST,
-                   G_STRUCT_OFFSET (GtkFileFolderIface, deleted),
-                   NULL, NULL,
-                   g_cclosure_marshal_VOID__VOID,
-                   G_TYPE_NONE, 0);
-      g_signal_new ("files-added",
-                   iface_type,
-                   G_SIGNAL_RUN_LAST,
-                   G_STRUCT_OFFSET (GtkFileFolderIface, files_added),
-                   NULL, NULL,
-                   g_cclosure_marshal_VOID__POINTER,
-                   G_TYPE_NONE, 1,
-                   G_TYPE_POINTER);
-      g_signal_new ("files-changed",
-                   iface_type,
-                   G_SIGNAL_RUN_LAST,
-                   G_STRUCT_OFFSET (GtkFileFolderIface, files_changed),
-                   NULL, NULL,
-                   g_cclosure_marshal_VOID__POINTER,
-                   G_TYPE_NONE, 1,
-                   G_TYPE_POINTER);
-      g_signal_new ("files-removed",
-                   iface_type,
-                   G_SIGNAL_RUN_LAST,
-                   G_STRUCT_OFFSET (GtkFileFolderIface, files_removed),
-                   NULL, NULL,
-                   g_cclosure_marshal_VOID__POINTER,
-                   G_TYPE_NONE, 1,
-                   G_TYPE_POINTER);
-
-      initialized = TRUE;
+      GMount *mount;
+
+      mount = g_volume_get_mount (G_VOLUME (volume));
+
+      if (mount)
+       {
+         file = g_mount_get_root (mount);
+         g_object_unref (mount);
+       }
     }
+
+  return file;
 }
 
-gboolean
-gtk_file_folder_list_children (GtkFileFolder    *folder,
-                              GSList          **children,
-                              GError          **error)
+static GdkPixbuf *
+get_pixbuf_from_gicon (GIcon      *icon,
+                      GtkWidget  *widget,
+                      gint        icon_size,
+                      GError    **error)
 {
-  gboolean result;
-  GSList *tmp_children = NULL;
-  
-  g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), FALSE);
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  GdkScreen *screen;
+  GtkIconTheme *icon_theme;
+  GtkIconInfo *icon_info;
+  GdkPixbuf *pixbuf;
 
-  result = GTK_FILE_FOLDER_GET_IFACE (folder)->list_children (folder, &tmp_children, error);
-  g_assert (result || tmp_children == NULL);
+  screen = gtk_widget_get_screen (GTK_WIDGET (widget));
+  icon_theme = gtk_icon_theme_get_for_screen (screen);
 
-  if (children)
-    *children = tmp_children;
-  else
-    gtk_file_paths_free (tmp_children);
+  icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme,
+                                             icon,
+                                             icon_size,
+                                             GTK_ICON_LOOKUP_USE_BUILTIN);
 
-  return result;
+  if (!icon_info)
+    return NULL;
+
+  pixbuf = gtk_icon_info_load_icon (icon_info, error);
+  g_object_unref (icon_info);
+
+  return pixbuf;
 }
 
-GtkFileInfo *
-gtk_file_folder_get_info (GtkFileFolder     *folder,
-                         const GtkFilePath *path,
-                         GError           **error)
+GdkPixbuf *
+_gtk_file_system_volume_render_icon (GtkFileSystemVolume  *volume,
+                                    GtkWidget            *widget,
+                                    gint                  icon_size,
+                                    GError              **error)
 {
-  g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), NULL);
-  g_return_val_if_fail (path != NULL, NULL);
-  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+  GIcon *icon = NULL;
+  GdkPixbuf *pixbuf;
+
+  DEBUG ("volume_get_icon_name");
+
+  if (IS_ROOT_VOLUME (volume))
+    icon = g_themed_icon_new ("drive-harddisk");
+  else if (G_IS_DRIVE (volume))
+    icon = g_drive_get_icon (G_DRIVE (volume));
+  else if (G_IS_VOLUME (volume))
+    icon = g_volume_get_icon (G_VOLUME (volume));
+  else if (G_IS_MOUNT (volume))
+    icon = g_mount_get_icon (G_MOUNT (volume));
+
+  if (!icon)
+    return NULL;
+
+  pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, error);
+
+  g_object_unref (icon);
 
-  return GTK_FILE_FOLDER_GET_IFACE (folder)->get_info (folder, path, error);
+  return pixbuf;
 }
 
-GSList *
-gtk_file_paths_sort (GSList *paths)
+GtkFileSystemVolume *
+_gtk_file_system_volume_ref (GtkFileSystemVolume *volume)
 {
-  return g_slist_sort (paths, (GCompareFunc)strcmp);
+  if (IS_ROOT_VOLUME (volume))
+    return volume;
+
+  if (G_IS_MOUNT (volume)  ||
+      G_IS_VOLUME (volume) ||
+      G_IS_DRIVE (volume))
+    g_object_ref (volume);
+
+  return volume;
 }
 
-/**
- * gtk_file_paths_copy:
- * @paths: A #GSList of 3GtkFilePath structures.
- * 
- * Copies a list of #GtkFilePath structures.
- * 
- * Return value: A copy of @paths.  Since the contents of the list are copied as
- * well, you should use gtk_file_paths_free() to free the result.
- **/
-GSList *
-gtk_file_paths_copy (GSList *paths)
+void
+_gtk_file_system_volume_unref (GtkFileSystemVolume *volume)
 {
-  GSList *head, *tail, *l;
+  /* Root volume doesn't need to be freed */
+  if (IS_ROOT_VOLUME (volume))
+    return;
+
+  if (G_IS_MOUNT (volume)  ||
+      G_IS_VOLUME (volume) ||
+      G_IS_DRIVE (volume))
+    g_object_unref (volume);
+}
 
-  head = tail = NULL;
+/* GFileInfo helper functions */
+GdkPixbuf *
+_gtk_file_info_render_icon (GFileInfo *info,
+                          GtkWidget *widget,
+                          gint       icon_size)
+{
+  GIcon *icon;
+  GdkPixbuf *pixbuf = NULL;
+  const gchar *thumbnail_path;
 
-  for (l = paths; l; l = l->next)
-    {
-      GtkFilePath *path;
-      GSList *node;
+  thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
 
-      path = l->data;
-      node = g_slist_alloc ();
+  if (thumbnail_path)
+    pixbuf = gdk_pixbuf_new_from_file_at_size (thumbnail_path,
+                                              icon_size, icon_size,
+                                              NULL);
 
-      if (tail)
-       tail->next = node;
-      else
-       head = node;
+  if (!pixbuf)
+    {
+      icon = g_file_info_get_icon (info);
 
-      node->data = gtk_file_path_copy (path);
-      tail = node;
+      if (icon)
+       pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
+
+      if (!pixbuf)
+       {
+          /* Use general fallback for all files without icon */
+         icon = g_themed_icon_new ("text-x-generic");
+         pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
+         g_object_unref (icon);
+       }
     }
 
-  return head;
+  return pixbuf;
 }
 
-void
-gtk_file_paths_free (GSList *paths)
+gboolean
+_gtk_file_info_consider_as_directory (GFileInfo *info)
+{
+  GFileType type = g_file_info_get_file_type (info);
+  
+  return (type == G_FILE_TYPE_DIRECTORY ||
+          type == G_FILE_TYPE_MOUNTABLE ||
+          type == G_FILE_TYPE_SHORTCUT);
+}
+
+gboolean
+_gtk_file_has_native_path (GFile *file)
 {
-  GSList *tmp_list;
+  char *local_file_path;
+  gboolean has_native_path;
 
-  for (tmp_list = paths; tmp_list; tmp_list = tmp_list->next)
-    gtk_file_path_free (tmp_list->data);
+  /* Don't use g_file_is_native(), as we want to support FUSE paths if available */
+  local_file_path = g_file_get_path (file);
+  has_native_path = (local_file_path != NULL);
+  g_free (local_file_path);
 
-  g_slist_free (paths);
+  return has_native_path;
 }