volume:// uri support



Hey Alex,

I've attached some preliminary patches to introduce a new volume:// name
space. The thinking is that g_file_get_uri() will return a persistent
URI if a local file is on user-visible media.

(Emphasis on user-visible. As in the media is visible in the UI, e.g.
computer://, Nautilus's sidebar and the file chooser). Notably files on
your rootfs (which we hide in the UI) will still use file:// URI's.)

For example this snippet

 GFile *f;
 f = g_vfs_get_file_for_path ("/media/disk/path/to/file.txt");
 uri = g_file_get_uri (f);
 printf ("uri is %s", uri);

 f = g_vfs_get_file_uri ("volume://name=davidz%20data;uuid=1234/path/to/file.txt");
 uri = g_file_get_path (f);
 printf ("path is %s", uri);

will print

 volume://name=davidz%20data;uuid=1234/path/to/file.txt
 /media/disk/path/to/file.txt

There's a screenshot here showing integration into Nautilus

http://people.freedesktop.org/~david/gvfs-hal-2-persistent-volume-uris.png

The patch to Nautilus also includes a small patch to show the URI in the
Location file under preferences; this is just for testing. Also, note
that the GUnixVolumeMonitor doesn't implement this yet; only the hal one
does (finding file system uuid's requires root privileges) - I'll get
around to send the finished hal patch tomorrow...

(there's also a bugfix in the Nautilus patch in src/nautilus-pathbar.c)

Anyway, the thinking is that this can be used in

 - Things like Rhythmbox, Banshee, F-Spot etc. to make sure things Just
   Work(tm) when having collections on removable media / external drives

 - Indexers like Beagle, Tracker etc.

 - Recently used files

Implementation-wise there's little to no overhead except that

 - g_vfs_get_file_from_uri() may block the very first time it's
   called with a volume:// URI since it needs to construct a
   GVolumeMonitor object

 - ditto for g_file_get_uri()

I don't think these are real problems; we already block for GDaemonFile
all the time IIRC. Other notes:

 - We probably want g_volume_monitor_get() to keep around a ref to
   the singleton when it's called and release that ref, say, 5 seconds
   later via a timeout. This is to avoid constructing and tearing down 
   volume monitors all the time. Thoughts?

 - Obviously things don't work when e.g. doubleclicking the icon
   representing /media/disk/path/to/file.txt because gedit isn't
   yet using gio (on my system). This is because Nautilus passes
   a volume:// URI. 

About the latter; there's a couple of options

 1. Introduce g_file_get_stable_uri() and always make g_file_get_uri()
    return file:// URI's for local files

 2. Like 1. but reversed; introduce g_file_get_file_uri() (better name
    wanted). Then we'd patch the few applications passing URI's to
    other applications to use this on.

 3. Just screw it and port applications to gio already. Though there's
    the compatibility with other desktops to take into account too.

 4. Like 3. and backport volume:// support to gnome-vfs

Personally I'd just go for option 4. - there's nothing like a little
breakage to help nudge people into fixing their apps to use newer API.
Thoughts?

      David

Index: gunixmount.c
===================================================================
--- gunixmount.c	(revision 6105)
+++ gunixmount.c	(working copy)
@@ -167,6 +167,20 @@
   return g_object_ref (unix_mount->icon);
 }
 
+static const char *
+g_unix_mount_get_uuid (GMount *mount)
+{
+  return NULL;
+}
+
+static const char *
+__g_unix_mount_get_mount_path (GMount *mount)
+{
+  GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
+
+  return unix_mount->mount_path;
+}
+
 static char *
 g_unix_mount_get_name (GMount *mount)
 {
@@ -340,6 +354,8 @@
   iface->get_root = g_unix_mount_get_root;
   iface->get_name = g_unix_mount_get_name;
   iface->get_icon = g_unix_mount_get_icon;
+  iface->get_uuid = g_unix_mount_get_uuid;
+  iface->get_mount_path = __g_unix_mount_get_mount_path;
   iface->get_drive = g_unix_mount_get_drive;
   iface->get_volume = g_unix_mount_get_volume;
   iface->can_unmount = g_unix_mount_can_unmount;
Index: glocalvfs.c
===================================================================
--- glocalvfs.c	(revision 6105)
+++ glocalvfs.c	(working copy)
@@ -1,3 +1,5 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
 /* GIO - GLib Input, Output and Streaming Library
  * 
  * Copyright (C) 2006-2007 Red Hat, Inc.
@@ -23,6 +25,8 @@
 #include <config.h>
 #include "glocalvfs.h"
 #include "glocalfile.h"
+#include "gvolumemonitor.h"
+
 #include <gio/gdummyfile.h>
 #include <sys/types.h>
 #ifdef HAVE_PWD_H
@@ -77,29 +81,69 @@
   return _g_local_file_new (path);
 }
 
+#include <string.h>
+
 static GFile *
 g_local_vfs_get_file_for_uri (GVfs       *vfs,
                               const char *uri)
 {
+  char *uuid;
   char *path;
   GFile *file;
+  gsize path_offset;
+  char *local_path;
+  const char *mount_path;
+  GVolumeMonitor *volume_monitor;
+  GMount *mount;
 
-  path = g_filename_from_uri (uri, NULL, NULL);
+  if (g_str_has_prefix (uri, "volume://"))
+    {
 
-  if (path != NULL)
-    file = _g_local_file_new (path);
+      if (g_mount_reference_decode_uri (uri, NULL, &uuid, &path_offset))
+        {
+          volume_monitor = g_volume_monitor_get ();
+          mount = g_volume_monitor_get_mount_for_uuid (volume_monitor, uuid);
+          
+          if (mount == NULL)
+            file = _g_dummy_file_new (uri);
+          else
+            {
+              mount_path = g_mount_get_mount_path (mount);
+              local_path = g_strdup_printf ("%s/%s", mount_path, uri + path_offset);
+          
+              file = _g_local_file_new (local_path);
+          
+              g_object_unref (mount);
+              g_free (local_path);
+            }
+
+          g_free (uuid);
+          g_object_unref (volume_monitor);
+        }
+      else
+        {
+          file = _g_dummy_file_new (uri);
+        }
+    }
   else
-    file = _g_dummy_file_new (uri);
-
-  g_free (path);
-
+    {
+      path = g_filename_from_uri (uri, NULL, NULL);
+      
+      if (path != NULL)
+        file = _g_local_file_new (path);
+      else
+        file = _g_dummy_file_new (uri);
+      
+      g_free (path);
+    }
+      
   return file;
 }
 
 static const gchar * const *
 g_local_vfs_get_supported_uri_schemes (GVfs *vfs)
 {
-  static const gchar * uri_schemes[] = { "file", NULL };
+  static const gchar * uri_schemes[] = { "file", "volume", NULL };
 
   return uri_schemes;
 }
Index: gvfs.c
===================================================================
--- gvfs.c	(revision 6105)
+++ gvfs.c	(working copy)
@@ -113,7 +113,7 @@
 			const char *uri)
 {
   GVfsClass *class;
-  
+
   g_return_val_if_fail (G_IS_VFS (vfs), NULL);
   g_return_val_if_fail (uri != NULL, NULL);
 
@@ -158,7 +158,7 @@
 		  const char *parse_name)
 {
   GVfsClass *class;
-  
+
   g_return_val_if_fail (G_IS_VFS (vfs), NULL);
   g_return_val_if_fail (parse_name != NULL, NULL);
 
Index: gvolumemonitor.c
===================================================================
--- gvolumemonitor.c	(revision 6105)
+++ gvolumemonitor.c	(working copy)
@@ -290,5 +290,28 @@
   return class->get_mounts (volume_monitor);
 }
 
+/**
+ * g_volume_monitor_get_mount_for_uuid:
+ * @volume_monitor: a #GVolumeMonitor.
+ * 
+ * Finds a #GMount object by it's UUID (see g_mount_get_uuid())
+ * 
+ * Returns: a #GMount or %NULL if no such mount is available.
+ **/
+GMount *
+g_volume_monitor_get_mount_for_uuid   (GVolumeMonitor *volume_monitor, 
+                                       const char     *uuid)
+{
+  GVolumeMonitorClass *class;
+
+  g_return_val_if_fail (G_IS_VOLUME_MONITOR (volume_monitor), NULL);
+  g_return_val_if_fail (uuid != NULL, NULL);
+
+  class = G_VOLUME_MONITOR_GET_CLASS (volume_monitor);
+
+  return class->get_mount_for_uuid (volume_monitor, uuid);
+}
+
+
 #define __G_VOLUME_MONITOR_C__
 #include "gioaliasdef.c"
Index: gvolumemonitor.h
===================================================================
--- gvolumemonitor.h	(revision 6105)
+++ gvolumemonitor.h	(working copy)
@@ -88,6 +88,9 @@
   GList * (*get_volumes)            (GVolumeMonitor *volume_monitor);
   GList * (*get_mounts)             (GVolumeMonitor *volume_monitor);
 
+  GMount * (*get_mount_for_uuid)    (GVolumeMonitor  *volume_monitor, 
+                                     const char      *uuid);
+
   /*< private >*/
   /* Padding for future expansion */
   void (*_g_reserved1) (void);
@@ -102,10 +105,12 @@
 
 GType g_volume_monitor_get_type (void) G_GNUC_CONST;
 
-GVolumeMonitor *g_volume_monitor_get          (void);
-GList *         g_volume_monitor_get_connected_drives (GVolumeMonitor *volume_monitor);
-GList *         g_volume_monitor_get_volumes          (GVolumeMonitor *volume_monitor);
-GList *         g_volume_monitor_get_mounts           (GVolumeMonitor *volume_monitor);
+GVolumeMonitor *g_volume_monitor_get                     (void);
+GList *         g_volume_monitor_get_connected_drives    (GVolumeMonitor  *volume_monitor);
+GList *         g_volume_monitor_get_volumes             (GVolumeMonitor  *volume_monitor);
+GList *         g_volume_monitor_get_mounts              (GVolumeMonitor  *volume_monitor);
+GMount *        g_volume_monitor_get_mount_for_uuid      (GVolumeMonitor  *volume_monitor,
+                                                          const char      *uuid);
 
 G_END_DECLS
 
Index: gfile.c
===================================================================
--- gfile.c	(revision 6105)
+++ gfile.c	(working copy)
@@ -297,7 +297,7 @@
  * <programlisting>
  * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] 
  * </programlisting>
- * Common schemes include "file", "http", "ftp", etc. 
+ * Common schemes include "file", "volume", "http", "ftp", etc. 
  *
  * Returns: a string containing the URI scheme for the given 
  *     #GFile. The returned string should be freed with g_free() 
Index: gunixvolumemonitor.c
===================================================================
--- gunixvolumemonitor.c	(revision 6105)
+++ gunixvolumemonitor.c	(working copy)
@@ -120,6 +120,12 @@
 }
 
 static GMount *
+get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
+{
+  return NULL;
+}
+
+static GMount *
 get_mount_for_mount_path (const char *mount_path)
 {
   GUnixMountEntry *mount_entry;
@@ -145,6 +151,7 @@
   monitor_class->get_mounts = get_mounts;
   monitor_class->get_volumes = get_volumes;
   monitor_class->get_connected_drives = get_connected_drives;
+  monitor_class->get_mount_for_uuid = get_mount_for_uuid;
 
   native_class->priority = 0;
   native_class->get_mount_for_mount_path = get_mount_for_mount_path;
Index: gunionvolumemonitor.c
===================================================================
--- gunionvolumemonitor.c	(revision 6105)
+++ gunionvolumemonitor.c	(working copy)
@@ -163,6 +163,35 @@
   return res;
 }
 
+static GMount *
+get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
+{
+  GUnionVolumeMonitor *monitor;
+  GVolumeMonitor *child_monitor;
+  GMount *mount;
+  GList *l;
+  
+  monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
+
+  mount = NULL;
+  
+  G_LOCK (the_volume_monitor);
+
+  for (l = monitor->monitors; l != NULL; l = l->next)
+    {
+      child_monitor = l->data;
+
+      mount = g_volume_monitor_get_mount_for_uuid (child_monitor, uuid);
+      if (mount != NULL)
+        break;
+
+    }
+  
+  G_UNLOCK (the_volume_monitor);
+
+  return mount;
+}
+
 static void
 g_union_volume_monitor_class_init (GUnionVolumeMonitorClass *klass)
 {
@@ -175,6 +204,7 @@
   monitor_class->get_connected_drives = get_connected_drives;
   monitor_class->get_volumes = get_volumes;
   monitor_class->get_mounts = get_mounts;
+  monitor_class->get_mount_for_uuid = get_mount_for_uuid;
 }
 
 static void
Index: glocalfile.c
===================================================================
--- glocalfile.c	(revision 6105)
+++ glocalfile.c	(working copy)
@@ -1,3 +1,5 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
 /* GIO - GLib Input, Output and Streaming Library
  * 
  * Copyright (C) 2006-2007 Red Hat, Inc.
@@ -113,6 +115,10 @@
 
 static char * find_mountpoint_for (const char *file, dev_t dev);
 
+static GMount *g_local_file_find_enclosing_mount (GFile         *file,
+                                                  GCancellable  *cancellable,
+                                                  GError       **error);
+
 static void
 g_local_file_finalize (GObject *object)
 {
@@ -298,17 +304,38 @@
   return TRUE;
 }
 
+static char *
+g_local_file_get_uri_scheme (GFile *file)
+{
+  const char *uuid;
+  GMount *mount;
+
+  mount = g_local_file_find_enclosing_mount (file, NULL, NULL);
+  if (mount != NULL)
+    {
+      uuid = g_mount_get_uuid (mount);
+      if (uuid != NULL)
+        {
+          g_object_unref (mount);
+          return g_strdup ("volume");
+        }
+      g_object_unref (mount);
+    }
+
+  return g_strdup ("file");
+}
+
 static gboolean
 g_local_file_has_uri_scheme (GFile      *file,
 			     const char *uri_scheme)
 {
-  return g_ascii_strcasecmp (uri_scheme, "file") == 0;
-}
+  gboolean ret;
+  char *file_uri_scheme;
 
-static char *
-g_local_file_get_uri_scheme (GFile *file)
-{
-  return g_strdup ("file");
+  file_uri_scheme = g_local_file_get_uri_scheme (file);
+  ret = (g_ascii_strcasecmp (uri_scheme, file_uri_scheme) == 0);
+  g_free (file_uri_scheme);
+  return ret;
 }
 
 static char *
@@ -326,6 +353,54 @@
 static char *
 g_local_file_get_uri (GFile *file)
 {
+  GMount *mount;
+  const char *uuid;
+
+  /* if the file is on a mount that has uuid's, use that. Note that
+   * for rootfs files there normally isn't a #GMount object so these
+   * will default to file:///
+   */
+  mount = g_local_file_find_enclosing_mount (file, NULL, NULL);
+  if (mount != NULL)
+    {
+      uuid = g_mount_get_uuid (mount);
+      if (uuid != NULL)
+        {
+          char *uri;
+          char *name;
+          gsize filename_len;
+          gsize mount_path_len;
+          const char *mount_path;
+
+          name = g_mount_get_name (mount);
+          mount_path = g_mount_get_mount_path (mount);
+
+          filename_len = strlen (G_LOCAL_FILE (file)->filename);
+          mount_path_len = strlen (mount_path);
+
+          if (filename_len <= mount_path_len)
+            {
+              /* for the directory representing the mount point itself, create a file:/// API */
+              uri = g_filename_to_uri (G_LOCAL_FILE (file)->filename, NULL, NULL);
+            }
+          else
+            {
+              const char *path_on_mount;
+
+              /* create the volume:// uri */
+              path_on_mount = (G_LOCAL_FILE (file)->filename) + mount_path_len + 1;
+              uri = g_mount_reference_encode_uri (name, uuid, path_on_mount);
+            }
+
+          g_object_unref (mount);
+          g_free (name);
+          return uri;
+        } 
+
+      g_object_unref (mount);
+    }
+
+  /* returns a file:// uri */
   return g_filename_to_uri (G_LOCAL_FILE (file)->filename, NULL, NULL);
 }
 
Index: gmount.c
===================================================================
--- gmount.c	(revision 6105)
+++ gmount.c	(working copy)
@@ -1,3 +1,5 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
 /* GIO - GLib Input, Output and Streaming Library
  * 
  * Copyright (C) 2006-2007 Red Hat, Inc.
@@ -22,6 +24,9 @@
  */
 
 #include <config.h>
+
+#include <string.h>
+
 #include "gmount.h"
 #include "gmountprivate.h"
 #include "gsimpleasyncresult.h"
@@ -175,6 +180,52 @@
 
   return (* iface->get_icon) (mount);
 }
+
+/**
+ * g_mount_get_uuid:
+ * @mount: a #GMount.
+ * 
+ * Gets the UUID for the #GMount. The reference is typically based on
+ * the file system UUID for the mount in question. Returns %NULL if
+ * there is no UUID available. See g_mount_reference_decode_uri() and
+ * g_mount_reference_encode_uri() for details.
+ * 
+ * Returns: the UUID for @mount or %NULL if no UUID can be computed.
+ **/
+const char *
+g_mount_get_uuid (GMount *mount)
+{
+  GMountIface *iface;
+
+  g_return_val_if_fail (G_IS_MOUNT (mount), NULL);
+
+  iface = G_MOUNT_GET_IFACE (mount);
+
+  return (* iface->get_uuid) (mount);
+}
+
+/**
+ * g_mount_get_mount_path:
+ * @mount: a #GMount.
+ * 
+ * Get a local path into the file system where @mount is
+ * mounted. Returns %NULL if the #GMount is not mounted into the local
+ * file system.
+ * 
+ * Returns: A local path into the file system where @mount is mounted
+ * or %NULL if it's not available in the local file system.
+ **/
+const char *
+g_mount_get_mount_path (GMount *mount)
+{
+  GMountIface *iface;
+
+  g_return_val_if_fail (G_IS_MOUNT (mount), NULL);
+
+  iface = G_MOUNT_GET_IFACE (mount);
+
+  return (* iface->get_mount_path) (mount);
+}
   
 /**
  * g_mount_get_volume:
@@ -308,5 +359,146 @@
   return (* iface->unmount_finish) (mount, result, error);
 }
 
+/**
+ * g_mount_reference_encode_uri:
+ * @name: User visible name to encode into URI
+ * @uuid: UUID of mount
+ * @path_on_mount: Path to file on @mount.
+ *
+ * See g_mount_reference_decode_uri() for a description of the
+ * <literal>volume://</literal> URI space.
+ *
+ * This function constructs a <literal>volume://</literal> URI given
+ * the @uuid of a #GMount (obtained using g_mount_get_uuid()) and
+ * @path_on_mount representing the path of the file relative to the
+ * mount root of the #GMount (obtained using
+ * g_mount_get_mount_root()). The @name parameter is used to store a
+ * user visible name in the URI that is supposed to be used for
+ * prompting the user to insert media with @name at later points in
+ * time where the media is not available.
+ *
+ * Note that this function is not supposed to be used by end user
+ * applications; it is meant only to be used for gio extensions loaded
+ * via #GIOModule.
+ *
+ * Returns: a <literal>volume://</literal> URI that points to
+ * @path_on_mount on media with @uuid mentioning @name.
+ **/
+char *
+g_mount_reference_encode_uri   (const char       *name, 
+                                const char       *uuid,
+                                const char       *path_on_mount)
+{
+  char *uri;
+  char *name_encoded;
+  char *uuid_encoded;
+
+  g_return_val_if_fail (name != NULL, NULL);
+  g_return_val_if_fail (uuid != NULL, NULL);
+
+  name_encoded = g_uri_escape_string (name, NULL, FALSE);
+  uuid_encoded = g_uri_escape_string (uuid, NULL, FALSE);
+  uri = g_strdup_printf ("volume://name=%s;uuid=%s/%s", name_encoded, uuid_encoded, path_on_mount);
+  g_free (name_encoded);
+  g_free (uuid_encoded);
+
+  return uri;
+}
+
+/**
+ * g_mount_reference_decode_uri:
+ * @uri: an URI
+ * @name: return location for decoded name or %NULL.
+ * @uuid: return location for #GMount uuid or %NULL.
+ * @path_offset: return location for offset of media path into @uri or %NULL.
+ *
+ * The <literal>volume://</literal> URI space is used for pointing to
+ * files on media with a known UUID (typically file system UUID's are
+ * used). Such URI's are useful for applications like music and photo
+ * applications and recently used files that keeps references to many
+ * files often kept on removable / hotpluggable media that are mounted
+ * in the local namespace.
+ *
+ * Note that the #GFile implementation for local files will
+ * automatically generate <literal>volume://</literal> URI's if the
+ * given file is on media with a UUID. By default, URI of files on the
+ * root file system (and other file systems considered to be
+ * implementation details of the OS; see
+ * g_unix_is_mount_path_system_internal()) will never generate
+ * <literal>volume://</literal> URI's.
+ * 
+ * This function can be used to detect whether @uri is of the type
+ * <literal>volume://</literal> and if so, return information about
+ * the @name, @uuid and relative path of the file with respect to the
+ * media (expressed via @path_offset as an offset into @uri where the
+ * local path name begins). Typically applications will use this
+ * function to if doing operations on a #GFile fails. If this function
+ * returns %TRUE, the application can then prompt the user to insert
+ * media called @name.
+ *
+ * Returns: %FALSE if uri isn't a <literal>volume://</literal> URI;
+ * %TRUE otherwise and @name, @uuid and @path_offset will be set. The
+ * @name and @uuid strings should be freed by the caller when no
+ * longer used.
+ **/
+gboolean
+g_mount_reference_decode_uri (const char  *uri,
+                              char       **name, 
+                              char       **uuid,
+                              gsize       *path_offset)
+{
+  gboolean ret;
+  const char *s;
+  char *p;
+  char **tokens;
+
+  g_return_val_if_fail (uri != NULL, FALSE);
+
+  if (name != NULL)
+    *name = NULL;
+  if (uuid != NULL)
+    *uuid = NULL;
+  if (path_offset != NULL)
+    *path_offset = 0;
+
+  ret = FALSE;
+  p = NULL;
+  tokens = NULL;
+
+  if (!g_str_has_prefix (uri, "volume://"))
+    goto out;
+
+  for (s = uri + 9; *s != '\0' && *s != '/'; s++)
+    ;
+  
+  p = g_strndup (uri + 9, s - uri - 9);
+  tokens = g_strsplit (p, ";", 0);
+  if (g_strv_length (tokens) != 2)
+    goto out;
+
+  if (strncmp (tokens[0], "name=", 5) != 0)
+    goto out;
+
+  if (strncmp (tokens[1], "uuid=", 5) != 0)
+    goto out;
+
+  if (name != NULL)
+    *name = g_uri_unescape_string (tokens[0] + 5, NULL);
+  if (uuid != NULL)
+    *uuid = g_uri_unescape_string (tokens[1] + 5, NULL);
+  if (path_offset != NULL)
+    *path_offset = (s - uri) + 1;
+
+  ret = TRUE;
+
+ out:
+  if (p != NULL)
+    g_free (p);
+  if (tokens != NULL)
+    g_strfreev (tokens);
+  return ret;
+}
+
+
 #define __G_MOUNT_C__
 #include "gioaliasdef.c"
Index: gmount.h
===================================================================
--- gmount.h	(revision 6105)
+++ gmount.h	(working copy)
@@ -58,6 +58,8 @@
  * @get_root: Gets a #GFile to the root directory of the #GMount.
  * @get_name: Gets a string containing the name of the #GMount.
  * @get_icon: Gets a #GIcon for the #GMount.
+ * @get_uuid: Gets the UUID for the #GMount. The reference is typically based on the file system UUID for the mount in question. Returns %NULL if there is no UUID available. See g_mount_reference_decode_uri() and g_mount_reference_encode_uri() for details.
+ * @get_mount_path: Get a local path into the file system where the #GMount is mounted. Returns %NULL if the #GMount is not mounted into the local file system.
  * @get_volume: Gets a #GVolume the mount is located on. Returns %NULL if the #GMount is not associated with a #GVolume.
  * @get_drive: Gets a #GDrive the volume of the mount is located on. Returns %NULL if the #GMount is not associated with a #GDrive or a #GVolume. This is convenience method for getting the #GVolume and using that to get the #GDrive.
  * @can_unmount: Checks if a #GMount can be unmounted.
@@ -79,6 +81,8 @@
   GFile *            (*get_root)             (GMount         *mount);
   char *             (*get_name)             (GMount         *mount);
   GIcon *            (*get_icon)             (GMount         *mount);
+  const char *       (*get_uuid)             (GMount         *mount);
+  const char *       (*get_mount_path)       (GMount         *mount);
   GVolume *          (*get_volume)           (GMount         *mount);
   GDrive *           (*get_drive)            (GMount         *mount);
   gboolean           (*can_unmount)          (GMount         *mount);
@@ -96,17 +100,28 @@
 GFile *            g_mount_get_root             (GMount              *mount);
 char *             g_mount_get_name             (GMount              *mount);
 GIcon *            g_mount_get_icon             (GMount              *mount);
+const char *       g_mount_get_uuid             (GMount              *mount);
+const char *       g_mount_get_mount_path       (GMount              *mount);
 GVolume *          g_mount_get_volume           (GMount              *mount);
 GDrive *           g_mount_get_drive            (GMount              *mount);
 gboolean           g_mount_can_unmount          (GMount              *mount);
 void               g_mount_unmount              (GMount              *mount,
-                                                 GCancellable         *cancellable,
-                                                 GAsyncReadyCallback   callback,
-                                                 gpointer              user_data);
+                                                 GCancellable        *cancellable,
+                                                 GAsyncReadyCallback  callback,
+                                                 gpointer             user_data);
 gboolean           g_mount_unmount_finish       (GMount              *mount,
-                                                 GAsyncResult         *result,
-                                                 GError              **error);
+                                                 GAsyncResult        *result,
+                                                 GError             **error);
 
+gboolean g_mount_reference_decode_uri   (const char  *uri,
+                                         char       **name, 
+                                         char       **uuid,
+                                         gsize       *path_offset);
+
+char *   g_mount_reference_encode_uri   (const char  *name, 
+                                         const char  *uuid,
+                                         const char  *path_on_mount);
+
 G_END_DECLS
 
 #endif /* __G_MOUNT_H__ */
Index: gio.symbols
===================================================================
--- gio.symbols	(revision 6105)
+++ gio.symbols	(working copy)
@@ -699,11 +699,15 @@
 g_mount_get_root 
 g_mount_get_name 
 g_mount_get_icon 
+g_mount_get_uuid 
+g_mount_get_mount_path 
 g_mount_get_volume
 g_mount_get_drive 
 g_mount_can_unmount 
 g_mount_unmount 
 g_mount_unmount_finish 
+g_mount_reference_decode_uri
+g_mount_reference_encode_uri
 #endif
 #endif
 
@@ -726,6 +730,7 @@
 g_volume_monitor_get_connected_drives 
 g_volume_monitor_get_volumes 
 g_volume_monitor_get_mounts
+g_volume_monitor_get_mount_for_uuid
 #endif
 #if IN_FILE(__G_UNION_VOLUME_MONITOR_C__)
 g_volume_monitor_get 
Index: src/nautilus-pathbar.c
===================================================================
--- src/nautilus-pathbar.c	(revision 13525)
+++ src/nautilus-pathbar.c	(working copy)
@@ -269,7 +269,7 @@
 		  G_SIGNAL_RUN_FIRST,
 		  G_STRUCT_OFFSET (NautilusPathBarClass, path_clicked),
 		  NULL, NULL,
-		  g_cclosure_marshal_VOID__STRING,
+		  g_cclosure_marshal_VOID__OBJECT,
 		  G_TYPE_NONE, 1,
 		  G_TYPE_FILE);
 }
Index: src/file-manager/fm-properties-window.c
===================================================================
--- src/file-manager/fm-properties-window.c	(revision 13525)
+++ src/file-manager/fm-properties-window.c	(working copy)
@@ -2963,7 +2963,7 @@
 	}
 
 	append_title_and_ellipsizing_value (window, table, _("Location:"), 
-					    "where",
+					    "uri", //TODO DZE: "where",
 					    _("--"),
 					    TRUE);
 	
Index: src/nautilus-places-sidebar.c
===================================================================
--- src/nautilus-places-sidebar.c	(revision 13525)
+++ src/nautilus-places-sidebar.c	(working copy)
@@ -1269,6 +1269,7 @@
 		 GAsyncResult *res,
 		 gpointer user_data)
 {
+	GMountOperation *mount_op = user_data;
 	GError *error;
 	char *primary;
 	char *name;
@@ -1284,6 +1285,8 @@
 		g_free (primary);
 		g_error_free (error);
 	}
+
+	g_object_unref (mount_op);
 }
 
 static void
@@ -1332,7 +1335,9 @@
 		GVolume *volume;
 		gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_VOLUME, &volume, -1);
 		if (volume != NULL) {
-			g_volume_mount (volume, NULL, NULL, volume_mount_cb, NULL);
+			GMountOperation *mount_op;
+			mount_op = g_mount_operation_new ();
+			g_volume_mount (volume, mount_op, NULL, volume_mount_cb, mount_op);
 			g_object_unref (volume);
 		}
 	}
@@ -1448,10 +1453,8 @@
 
 	if (volume != NULL) {
 		GMountOperation *mount_op;
-
-		/* TODO: do we leak this? */
 		mount_op = g_mount_operation_new ();
-		g_volume_mount (volume, mount_op, NULL, volume_mount_cb, NULL);
+		g_volume_mount (volume, mount_op, NULL, volume_mount_cb, mount_op);
 		g_object_unref (volume);
 	}
 }
Index: libnautilus-private/nautilus-mime-actions.c
===================================================================
--- libnautilus-private/nautilus-mime-actions.c	(revision 13525)
+++ libnautilus-private/nautilus-mime-actions.c	(working copy)
@@ -30,6 +30,7 @@
 #include <eel/eel-string.h>
 #include <eel/eel-mount-operation.h>
 #include <glib/gi18n.h>
+#include <gio/gvolumemonitor.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -1164,12 +1165,12 @@
 		char *full_uri_for_display;
 		char *uri_for_display;
 		char *error_message;
+		char *error_message_sec;
 		
 		file = NAUTILUS_FILE (l->data);
 
 		location = nautilus_file_get_location (file);
 		full_uri_for_display = g_file_get_parse_name (location);
-		g_object_unref (location);
 
 		/* Truncate the URI so it doesn't get insanely wide. Note that even
 		 * though the dialog uses wrapped text, if the URI doesn't contain
@@ -1178,16 +1179,52 @@
 		uri_for_display = eel_str_middle_truncate
 			(full_uri_for_display, MAX_URI_IN_DIALOG_LENGTH);
 		g_free (full_uri_for_display);
-	
+
+		char *uri;
+		char *media_uuid;
+		char *media_name;
+		gsize media_path_offset;
+
+		uri = g_file_get_uri (location);
+		if (g_mount_reference_decode_uri (uri, &media_name, &media_uuid, &media_path_offset)) {
+			GMount *mount;
+			GVolumeMonitor *volume_monitor;
+
+			volume_monitor = g_volume_monitor_get ();
+			mount = g_volume_monitor_get_mount_for_uuid (volume_monitor, media_uuid);
+			if (mount == NULL) {
+				error_message = g_strdup_printf (_("Please insert \"%s\"."), media_name);
+				error_message_sec = g_strdup_printf (
+					_("The file \"%s\" is located on media that is not currently mounted."), 
+					uri + media_path_offset);
+				
+				eel_show_error_dialog (error_message,
+						       error_message_sec,
+						       parameters->parent_window);
+				g_free (error_message);
+				g_free (error_message_sec);
+				g_object_unref (volume_monitor);
+				g_free (media_name);
+				g_free (media_uuid);
+				goto dialog_shown;
+			}
+			g_object_unref (volume_monitor);
+			g_free (media_name);
+			g_free (media_uuid);
+		}
+
 		error_message = g_strdup_printf (_("Couldn't display \"%s\"."),
-						 uri_for_display);
-		
-		g_free (uri_for_display);
-
+						 uri_for_display);		
 		eel_show_error_dialog (error_message,
 				       _("There is no application installed for this file type"),
 				       parameters->parent_window);
 		g_free (error_message);
+
+	dialog_shown:
+
+		g_free (uri_for_display);
+		g_free (uri);
+		g_object_unref (location);
 	}
 
 	if (open_in_app_parameters != NULL ||
Index: libnautilus-private/nautilus-file-operations.c
===================================================================
--- libnautilus-private/nautilus-file-operations.c	(revision 13525)
+++ libnautilus-private/nautilus-file-operations.c	(working copy)
@@ -63,6 +63,7 @@
 #include <libgnomevfs/gnome-vfs-volume.h>
 #include <libgnomevfs/gnome-vfs-volume-monitor.h>
 #include <gio/gfile.h>
+#include <gio/gdrive.h>
 #include <glib/gurifuncs.h>
 #include <gio/gioscheduler.h>
 #include "nautilus-file-changes-queue.h"
@@ -1711,8 +1712,8 @@
 
 static void
 unmount_mount_callback (GObject *source_object,
-			 GAsyncResult *res,
-			 gpointer user_data)
+			GAsyncResult *res,
+			gpointer user_data)
 {
 	UnmountData *data = user_data;
 	GError *error;
@@ -1744,24 +1745,20 @@
 do_unmount (UnmountData *data)
 {
 	if (data->eject) {
-#if 0
-/* TODO */
 		GDrive *drive;
-
-		drive = g_mount_get_drive (drive);
+		drive = g_mount_get_drive (data->mount);
 		if (drive != NULL) {
 			g_drive_eject (drive, 
-				NULL,
-				unmount_mount_callback,
-				data);
+				       NULL,
+				       unmount_mount_callback,
+				       data);
 			g_object_unref (drive);
 		}
-#endif
 	} else {
 		g_mount_unmount (data->mount,
-				  NULL,
-				  unmount_mount_callback,
-				  data);
+				 NULL,
+				 unmount_mount_callback,
+				 data);
 	}
 }
 
Index: client/gdaemonvolumemonitor.c
===================================================================
--- client/gdaemonvolumemonitor.c	(revision 1037)
+++ client/gdaemonvolumemonitor.c	(working copy)
@@ -67,6 +67,12 @@
   return NULL;
 }
 
+static GMount *
+get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
+{
+  return NULL;
+}
+
 static GDaemonMount *
 find_mount_by_mount_info (GDaemonVolumeMonitor *daemon_monitor, GMountInfo *mount_info)
 {
@@ -193,6 +199,7 @@
   monitor_class->get_mounts = get_mounts;
   monitor_class->get_volumes = get_volumes;
   monitor_class->get_connected_drives = get_connected_drives;
+  monitor_class->get_mount_for_uuid = get_mount_for_uuid;
 }
 
 GVolumeMonitor *
Index: client/gdaemonvfs.c
===================================================================
--- client/gdaemonvfs.c	(revision 1037)
+++ client/gdaemonvfs.c	(working copy)
@@ -258,7 +258,8 @@
 
   daemon_vfs = G_DAEMON_VFS (vfs);
 
-  if (g_str_has_prefix (uri, "file:"))
+  /* TODO: it's not nice to assume that what uri scheme that wrapped_vfs supports */
+  if (g_str_has_prefix (uri, "file:") || g_str_has_prefix (uri, "volume:"))
     {
       path = g_filename_from_uri (uri, NULL, NULL);
 
Index: client/gdaemonmount.c
===================================================================
--- client/gdaemonmount.c	(revision 1037)
+++ client/gdaemonmount.c	(working copy)
@@ -114,6 +114,19 @@
   return g_strdup (daemon_mount->mount_info->display_name);
 }
 
+static char *
+g_daemon_mount_get_uuid (GMount *mount)
+{
+  return NULL;
+}
+
+static char *
+g_daemon_mount_get_mount_path (GMount *mount)
+{
+  /* TODO: could return the FUSE mount path? */
+  return NULL;
+}
+
 static GVolume *
 g_daemon_mount_get_volume (GMount *mount)
 {
@@ -192,6 +205,8 @@
   iface->get_root = g_daemon_mount_get_root;
   iface->get_name = g_daemon_mount_get_name;
   iface->get_icon = g_daemon_mount_get_icon;
+  iface->get_uuid = g_daemon_mount_get_uuid;
+  iface->get_mount_path = g_daemon_mount_get_mount_path;
   iface->get_volume = g_daemon_mount_get_volume;
   iface->get_drive = g_daemon_mount_get_drive;
   iface->can_unmount = g_daemon_mount_can_unmount;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]