[Xfce4-commits] <thunar:master> Work on a fancy-pancy sidepane in GtkTreeView.

Nick Schermer noreply at xfce.org
Sat Oct 13 16:12:07 CEST 2012


Updating branch refs/heads/master
         to e486a39069ea5ac99e59db74064f70037417053c (commit)
       from 5413440250858decf1c0ee0bb34ed2282dfcc967 (commit)

commit e486a39069ea5ac99e59db74064f70037417053c
Author: Nick Schermer <nick at xfce.org>
Date:   Fri Oct 5 23:02:00 2012 +0200

    Work on a fancy-pancy sidepane in GtkTreeView.

 thunar/thunar-file.c                    |   10 +-
 thunar/thunar-shortcuts-icon-renderer.c |   66 +++-
 thunar/thunar-shortcuts-model.c         |  685 ++++++++++++++++++++++---------
 thunar/thunar-shortcuts-model.h         |   20 +-
 thunar/thunar-shortcuts-view.c          |  570 +++++++++++++++++++-------
 5 files changed, 973 insertions(+), 378 deletions(-)

diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c
index 52d223d..cb93e57 100644
--- a/thunar/thunar-file.c
+++ b/thunar/thunar-file.c
@@ -3398,10 +3398,12 @@ thunar_file_compare_by_name (const ThunarFile *file_a,
   if (result == 0)
     result = strcmp (file_a->collate_key, file_b->collate_key);
 
-#ifdef G_ENABLE_DEBUG
-  /* check final output */
-  _thunar_return_val_if_fail (result != 0, 0);
- #endif
+  /* this happens in the trash */
+  if (result == 0)
+    {
+      result = g_strcmp0 (thunar_file_get_original_path (file_a),
+                          thunar_file_get_original_path (file_b));
+    }
 
   return result;
 }
diff --git a/thunar/thunar-shortcuts-icon-renderer.c b/thunar/thunar-shortcuts-icon-renderer.c
index ff949c8..a31f553 100644
--- a/thunar/thunar-shortcuts-icon-renderer.c
+++ b/thunar/thunar-shortcuts-icon-renderer.c
@@ -36,6 +36,7 @@ enum
 {
   PROP_0,
   PROP_VOLUME,
+  PROP_MOUNT,
   PROP_GICON,
 };
 
@@ -70,6 +71,7 @@ struct _ThunarShortcutsIconRenderer
   ThunarIconRenderer __parent__;
 
   GVolume           *volume;
+  GMount            *mount;
   GIcon             *gicon;
 };
 
@@ -106,6 +108,18 @@ thunar_shortcuts_icon_renderer_class_init (ThunarShortcutsIconRendererClass *kla
                                                         EXO_PARAM_READWRITE));
 
   /**
+   * ThunarShortcutsIconRenderer:mount:
+   *
+   * The #GMount for which to render an icon or %NULL to fallback
+   * to the default icon renderering (see #ThunarIconRenderer).
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_MOUNT,
+                                   g_param_spec_object ("mount", "mount", "mount",
+                                                        G_TYPE_MOUNT,
+                                                        EXO_PARAM_READWRITE));
+
+  /**
    * ThunarIconRenderer:gicon:
    *
    * The GIcon to render, this property has preference over the the icon returned
@@ -133,15 +147,16 @@ thunar_shortcuts_icon_renderer_init (ThunarShortcutsIconRenderer *shortcuts_icon
 static void
 thunar_shortcuts_icon_renderer_finalize (GObject *object)
 {
-  ThunarShortcutsIconRenderer *shortcuts_icon_renderer = THUNAR_SHORTCUTS_ICON_RENDERER (object);
+  ThunarShortcutsIconRenderer *renderer = THUNAR_SHORTCUTS_ICON_RENDERER (object);
 
-  /* release the volume (if any) */
-  if (G_UNLIKELY (shortcuts_icon_renderer->volume != NULL))
-    g_object_unref (shortcuts_icon_renderer->volume);
+  if (G_UNLIKELY (renderer->volume != NULL))
+    g_object_unref (renderer->volume);
 
-  /* release the icon */
-  if (G_UNLIKELY (shortcuts_icon_renderer->gicon != NULL))
-    g_object_unref (shortcuts_icon_renderer->gicon);
+  if (G_UNLIKELY (renderer->mount != NULL))
+    g_object_unref (renderer->mount);
+
+  if (G_UNLIKELY (renderer->gicon != NULL))
+    g_object_unref (renderer->gicon);
 
   (*G_OBJECT_CLASS (thunar_shortcuts_icon_renderer_parent_class)->finalize) (object);
 }
@@ -154,16 +169,20 @@ thunar_shortcuts_icon_renderer_get_property (GObject    *object,
                                              GValue     *value,
                                              GParamSpec *pspec)
 {
-  ThunarShortcutsIconRenderer *shortcuts_icon_renderer = THUNAR_SHORTCUTS_ICON_RENDERER (object);
+  ThunarShortcutsIconRenderer *renderer = THUNAR_SHORTCUTS_ICON_RENDERER (object);
 
   switch (prop_id)
     {
     case PROP_VOLUME:
-      g_value_set_object (value, shortcuts_icon_renderer->volume);
+      g_value_set_object (value, renderer->volume);
+      break;
+
+    case PROP_MOUNT:
+      g_value_set_object (value, renderer->mount);
       break;
 
     case PROP_GICON:
-      g_value_set_object (value, shortcuts_icon_renderer->gicon);
+      g_value_set_object (value, renderer->gicon);
       break;
 
     default:
@@ -180,20 +199,26 @@ thunar_shortcuts_icon_renderer_set_property (GObject      *object,
                                              const GValue *value,
                                              GParamSpec   *pspec)
 {
-  ThunarShortcutsIconRenderer *shortcuts_icon_renderer = THUNAR_SHORTCUTS_ICON_RENDERER (object);
+  ThunarShortcutsIconRenderer *renderer = THUNAR_SHORTCUTS_ICON_RENDERER (object);
 
   switch (prop_id)
     {
     case PROP_VOLUME:
-      if (G_UNLIKELY (shortcuts_icon_renderer->volume != NULL))
-        g_object_unref (shortcuts_icon_renderer->volume);
-      shortcuts_icon_renderer->volume = g_value_dup_object (value);
+      if (G_UNLIKELY (renderer->volume != NULL))
+        g_object_unref (renderer->volume);
+      renderer->volume = g_value_dup_object (value);
+      break;
+
+    case PROP_MOUNT:
+      if (G_UNLIKELY (renderer->mount != NULL))
+        g_object_unref (renderer->mount);
+      renderer->mount = g_value_dup_object (value);
       break;
 
     case PROP_GICON:
-      if (G_UNLIKELY (shortcuts_icon_renderer->gicon != NULL))
-        g_object_unref (shortcuts_icon_renderer->gicon);
-      shortcuts_icon_renderer->gicon = g_value_dup_object (value);
+      if (G_UNLIKELY (renderer->gicon != NULL))
+        g_object_unref (renderer->gicon);
+      renderer->gicon = g_value_dup_object (value);
       break;
 
     default:
@@ -224,7 +249,8 @@ thunar_shortcuts_icon_renderer_render (GtkCellRenderer     *renderer,
 
   /* check if we have a volume set */
   if (G_UNLIKELY (shortcuts_icon_renderer->volume != NULL
-      || shortcuts_icon_renderer->gicon != NULL))
+      || shortcuts_icon_renderer->gicon != NULL
+      ||  shortcuts_icon_renderer->mount != NULL))
     {
       /* load the volume icon */
       icon_theme = gtk_icon_theme_get_for_screen (gdk_drawable_get_screen (window));
@@ -232,8 +258,10 @@ thunar_shortcuts_icon_renderer_render (GtkCellRenderer     *renderer,
       /* look up the icon info */
       if (shortcuts_icon_renderer->gicon != NULL)
         gicon = g_object_ref (shortcuts_icon_renderer->gicon);
-      else
+      else if (shortcuts_icon_renderer->volume != NULL)
         gicon = g_volume_get_icon (shortcuts_icon_renderer->volume);
+      else
+        gicon = g_mount_get_icon (shortcuts_icon_renderer->mount);
 
       icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme, gicon, cell_area->width, 
                                                   GTK_ICON_LOOKUP_USE_BUILTIN);
diff --git a/thunar/thunar-shortcuts-model.c b/thunar/thunar-shortcuts-model.c
index bf6d67e..c16988c 100644
--- a/thunar/thunar-shortcuts-model.c
+++ b/thunar/thunar-shortcuts-model.c
@@ -57,14 +57,6 @@ const gchar *_thunar_user_directory_names[9] = {
 
 typedef struct _ThunarShortcut ThunarShortcut;
 
-typedef enum
-{
-  THUNAR_SHORTCUT_SEPARATOR,
-  THUNAR_SHORTCUT_SYSTEM_DEFINED,
-  THUNAR_SHORTCUT_REMOVABLE_MEDIA,
-  THUNAR_SHORTCUT_USER_DEFINED,
-} ThunarShortcutType;
-
 
 
 static void               thunar_shortcuts_model_tree_model_init    (GtkTreeModelIface         *iface);
@@ -107,8 +99,7 @@ static gboolean           thunar_shortcuts_model_drag_data_get      (GtkTreeDrag
 static gboolean           thunar_shortcuts_model_drag_data_delete   (GtkTreeDragSource         *source,
                                                                      GtkTreePath               *path);
 static void               thunar_shortcuts_model_add_shortcut       (ThunarShortcutsModel      *model,
-                                                                     ThunarShortcut            *shortcut,
-                                                                     GtkTreePath               *path);
+                                                                     ThunarShortcut            *shortcut);
 static void               thunar_shortcuts_model_remove_shortcut    (ThunarShortcutsModel      *model,
                                                                      ThunarShortcut            *shortcut);
 static void               thunar_shortcuts_model_load               (ThunarShortcutsModel      *model);
@@ -131,6 +122,16 @@ static void               thunar_shortcuts_model_volume_removed     (GVolumeMoni
 static void               thunar_shortcuts_model_volume_changed     (GVolumeMonitor            *monitor,
                                                                      GVolume                   *volume,
                                                                      ThunarShortcutsModel      *model);
+static void               thunar_shortcuts_model_mount_added        (GVolumeMonitor            *volume_monitor,
+                                                                     GMount                    *mount,
+                                                                     ThunarShortcutsModel      *model);
+static void               thunar_shortcuts_model_mount_removed      (GVolumeMonitor            *volume_monitor,
+                                                                     GMount                    *mount,
+                                                                     ThunarShortcutsModel      *model);
+static void               thunar_shortcuts_model_mount_changed      (GVolumeMonitor            *monitor,
+                                                                     GMount                    *mount,
+                                                                     ThunarShortcutsModel      *model);
+
 static void               thunar_shortcut_free                      (ThunarShortcut            *shortcut,
                                                                      ThunarShortcutsModel      *model);
 
@@ -162,13 +163,18 @@ struct _ThunarShortcutsModel
 
 struct _ThunarShortcut
 {
-  ThunarShortcutType type;
+  ThunarShortcutGroup  group;
+
+  guint                is_header : 1;
 
-  GFile              *location;
-  gchar              *name;
-  GIcon              *gicon;
-  ThunarFile         *file;
-  GVolume            *volume;
+  gchar               *name;
+  GIcon               *gicon;
+  gint                 sort_id;
+
+  GFile               *location;
+  ThunarFile          *file;
+  GVolume             *volume;
+  GMount              *mount;
 };
 
 
@@ -178,7 +184,7 @@ G_DEFINE_TYPE_WITH_CODE (ThunarShortcutsModel, thunar_shortcuts_model, G_TYPE_OB
     G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, thunar_shortcuts_model_drag_source_init))
 
 
-    
+
 static void
 thunar_shortcuts_model_class_init (ThunarShortcutsModelClass *klass)
 {
@@ -218,124 +224,138 @@ thunar_shortcuts_model_drag_source_init (GtkTreeDragSourceIface *iface)
 }
 
 
+
 static void
-thunar_shortcuts_model_add_shortcut_system (ThunarShortcutsModel *model,
-                                            GFile                *location,
-                                            const gchar          *name,
-                                            const gchar          *icon_name,
-                                            GtkTreePath          *path)
+thunar_shortcuts_model_volumes_load (ThunarShortcutsModel *model)
 {
   ThunarShortcut *shortcut;
+  GList          *volumes;
+  GList          *lp;
+  GList          *mounts;
 
+  /* add the devices heading */
   shortcut = g_slice_new0 (ThunarShortcut);
-  shortcut->type = THUNAR_SHORTCUT_SYSTEM_DEFINED;
-  shortcut->location = g_object_ref (location);
-  shortcut->name = g_strdup (name);
-  shortcut->gicon = g_themed_icon_new (icon_name);
-  thunar_shortcuts_model_add_shortcut (model, shortcut, path);
-}
+  shortcut->group = THUNAR_SHORTCUT_GROUP_DEVICES;
+  shortcut->name = g_strdup (_("DEVICES"));
+  shortcut->is_header = TRUE;
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
 
-
-static void
-thunar_shortcuts_model_init (ThunarShortcutsModel *model)
-{
-  ThunarShortcut  *shortcut;
-  GtkTreePath     *path;
-  GVolume         *volume;
-  GFile           *bookmarks;
-  GFile           *home;
-  GFile           *file;
-  GList           *volumes;
-  GList           *lp;
-
-#ifndef NDEBUG
-  model->stamp = g_random_int ();
-#endif
+  /* the filesystem entry */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->group = THUNAR_SHORTCUT_GROUP_DEVICES;
+  shortcut->name = g_strdup (_("File System"));
+  shortcut->file = thunar_file_get_for_uri ("file:///", NULL);
+  shortcut->gicon = g_themed_icon_new (GTK_STOCK_HARDDISK);
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
 
   /* connect to the volume monitor */
   model->volume_monitor = g_volume_monitor_get ();
+
+  /* get a list of all volumes available */
+  volumes = g_volume_monitor_get_volumes (model->volume_monitor);
+  for (lp = volumes; lp != NULL; lp = lp->next)
+    {
+      thunar_shortcuts_model_volume_added (model->volume_monitor, lp->data, model);
+      g_object_unref (G_OBJECT (lp->data));
+    }
+  g_list_free (volumes);
+
+  /* get a list of all mounts available */
+  mounts = g_volume_monitor_get_mounts (model->volume_monitor);
+  for (lp = mounts; lp != NULL; lp = lp->next)
+    {
+      thunar_shortcuts_model_mount_added (model->volume_monitor, lp->data, model);
+      g_object_unref (G_OBJECT (lp->data));
+    }
+  g_list_free (mounts);
+
+  /* monitor for changes */
   g_signal_connect (model->volume_monitor, "volume-added", G_CALLBACK (thunar_shortcuts_model_volume_added), model);
   g_signal_connect (model->volume_monitor, "volume-removed", G_CALLBACK (thunar_shortcuts_model_volume_removed), model);
   g_signal_connect (model->volume_monitor, "volume-changed", G_CALLBACK (thunar_shortcuts_model_volume_changed), model);
+  g_signal_connect (model->volume_monitor, "mount-added", G_CALLBACK (thunar_shortcuts_model_mount_added), model);
+  g_signal_connect (model->volume_monitor, "mount-removed", G_CALLBACK (thunar_shortcuts_model_mount_removed), model);
+  g_signal_connect (model->volume_monitor, "mount-changed", G_CALLBACK (thunar_shortcuts_model_mount_changed), model);
+}
 
-  /* get home path */
-  home = thunar_g_file_new_for_home ();
 
-  /* will be used to append the shortcuts to the list */
-  path = gtk_tree_path_new_from_indices (0, -1);
 
-  /* add the home folder to the system paths */
-  thunar_shortcuts_model_add_shortcut_system (model, home, NULL, "user-home", path);
-  gtk_tree_path_next (path);
+static void
+thunar_shortcuts_model_shortcut_network (ThunarShortcutsModel *model)
+{
+  ThunarShortcut *shortcut;
 
-  /* append the user's desktop folder */
-  file = thunar_g_file_new_for_desktop ();
-  if (!g_file_equal (file, home))
-    {
-      thunar_shortcuts_model_add_shortcut_system (model, file, NULL, "user-desktop", path);
-      gtk_tree_path_next (path);
-    }
-  g_object_unref (file);
+  /* add the network heading */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->group = THUNAR_SHORTCUT_GROUP_NETWORK;
+  shortcut->name = g_strdup (_("NETWORK"));
+  shortcut->is_header = TRUE;
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
 
-  /* append the trash icon if the trash is supported */
-  if (thunar_g_vfs_is_uri_scheme_supported ("trash"))
-    {
-      file = thunar_g_file_new_for_trash ();
-      thunar_shortcuts_model_add_shortcut_system (model, file, _("Trash"), "user-trash", path);
-      g_object_unref (file);
-      gtk_tree_path_next (path);
-    }
+  /* the browse network entry */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->group = THUNAR_SHORTCUT_GROUP_NETWORK;
+  shortcut->name = g_strdup (_("Browse Network"));
+  shortcut->location = g_file_new_for_uri ("network://");
+  shortcut->gicon = g_themed_icon_new (GTK_STOCK_NETWORK);
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
+}
 
-  /* append the trash icon if the trash is supported */
-  if (thunar_g_vfs_is_uri_scheme_supported ("network"))
+
+static void
+thunar_shortcuts_model_shortcut_places (ThunarShortcutsModel *model)
+{
+  ThunarShortcut *shortcut;
+  GFile          *home;
+  GFile          *bookmarks;
+  GFile          *desktop;
+  GFile          *trash;
+
+  /* add the places heading */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES;
+  shortcut->name = g_strdup (_("PLACES"));
+  shortcut->is_header = TRUE;
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
+
+  /* get home path */
+  home = thunar_g_file_new_for_home ();
+
+  /* add home entry */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES;
+  shortcut->file = thunar_file_get (home, NULL);
+  shortcut->sort_id = 0;
+  shortcut->gicon = g_themed_icon_new ("user-home");
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
+
+  /* add desktop entry */
+  desktop = thunar_g_file_new_for_desktop ();
+  if (!g_file_equal (desktop, home))
     {
-      file = g_file_new_for_uri ("network://");
-      thunar_shortcuts_model_add_shortcut_system (model, file, _("Browse Network"), GTK_STOCK_NETWORK, path);
-      g_object_unref (file);
-      gtk_tree_path_next (path);
+      shortcut = g_slice_new0 (ThunarShortcut);
+      shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES;
+      shortcut->file = thunar_file_get (desktop, NULL);
+      shortcut->gicon = g_themed_icon_new ("user-desktop");
+      shortcut->sort_id =  1;
+      thunar_shortcuts_model_add_shortcut (model, shortcut);
     }
+  g_object_unref (desktop);
 
-  /* append the root file system */
-  file = thunar_g_file_new_for_root ();
-  thunar_shortcuts_model_add_shortcut_system (model, file, _("File System"), GTK_STOCK_HARDDISK, path);
-  g_object_unref (file);
-  gtk_tree_path_next (path);
-
-  /* prepend the removable media volumes */
-  volumes = g_volume_monitor_get_volumes (model->volume_monitor);
-  for (lp = volumes; lp != NULL; lp = lp->next)
+  /* append the trash icon if the trash is supported */
+  if (thunar_g_vfs_is_uri_scheme_supported ("trash"))
     {
-      /* monitor the volume for changes */
-      volume = G_VOLUME (lp->data);
+      trash = thunar_g_file_new_for_trash ();
 
-      /* we list only present, removable devices here */
-      if (thunar_g_volume_is_removable (volume) && thunar_g_volume_is_present (volume))
-        {
-          /* generate the shortcut (w/o a file, else we might
-           * prevent the volume from being unmounted)
-           */
-          shortcut = g_slice_new0 (ThunarShortcut);
-          shortcut->type = THUNAR_SHORTCUT_REMOVABLE_MEDIA;
-          shortcut->volume = volume;
+      shortcut = g_slice_new0 (ThunarShortcut);
+      shortcut->group = THUNAR_SHORTCUT_GROUP_TRASH;
+      shortcut->file = thunar_file_get (trash, NULL);
+      shortcut->gicon = g_themed_icon_new ("user-trash");
+      shortcut->name = g_strdup (_("Trash"));
+      thunar_shortcuts_model_add_shortcut (model, shortcut);
 
-          /* link the shortcut to the list */
-          thunar_shortcuts_model_add_shortcut (model, shortcut, path);
-          gtk_tree_path_next (path);
-        }
-      else
-        {
-          /* schedule the volume for later checking, not removable or 
-           * there's no medium present */
-          model->hidden_volumes = g_list_prepend (model->hidden_volumes, volume);
-        }
+      g_object_unref (trash);
     }
-  g_list_free (volumes);
-
-  /* prepend the row separator */
-  shortcut = g_slice_new0 (ThunarShortcut);
-  shortcut->type = THUNAR_SHORTCUT_SEPARATOR;
-  thunar_shortcuts_model_add_shortcut (model, shortcut, path);
-  gtk_tree_path_next (path);
 
   /* determine the URI to the Gtk+ bookmarks file */
   bookmarks = g_file_resolve_relative_path (home, ".gtk-bookmarks");
@@ -348,10 +368,28 @@ thunar_shortcuts_model_init (ThunarShortcutsModel *model)
   /* read the Gtk+ bookmarks file */
   thunar_shortcuts_model_load (model);
 
-  /* cleanup */
-  g_object_unref (bookmarks);
   g_object_unref (home);
-  gtk_tree_path_free (path);
+  g_object_unref (bookmarks);
+}
+
+
+
+
+static void
+thunar_shortcuts_model_init (ThunarShortcutsModel *model)
+{
+#ifndef NDEBUG
+  model->stamp = g_random_int ();
+#endif
+
+  /* load volumes */
+  thunar_shortcuts_model_volumes_load (model);
+
+  /* add network */
+  thunar_shortcuts_model_shortcut_network (model);
+
+  /* add bookmarks */
+  thunar_shortcuts_model_shortcut_places (model);
 }
 
 
@@ -408,6 +446,10 @@ thunar_shortcuts_model_get_column_type (GtkTreeModel *tree_model,
 {
   switch (idx)
     {
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_HEADER:
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_NOT_HEADER:
+      return G_TYPE_BOOLEAN;
+
     case THUNAR_SHORTCUTS_MODEL_COLUMN_NAME:
       return G_TYPE_STRING;
 
@@ -423,14 +465,17 @@ thunar_shortcuts_model_get_column_type (GtkTreeModel *tree_model,
     case THUNAR_SHORTCUTS_MODEL_COLUMN_VOLUME:
       return G_TYPE_VOLUME;
 
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_MOUNT:
+      return G_TYPE_MOUNT;
+
     case THUNAR_SHORTCUTS_MODEL_COLUMN_MUTABLE:
       return G_TYPE_BOOLEAN;
 
     case THUNAR_SHORTCUTS_MODEL_COLUMN_EJECT:
       return G_TYPE_STRING;
 
-    case THUNAR_SHORTCUTS_MODEL_COLUMN_SEPARATOR:
-      return G_TYPE_BOOLEAN;
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_GROUP:
+      return G_TYPE_UINT;
     }
 
   _thunar_assert_not_reached ();
@@ -502,10 +547,22 @@ thunar_shortcuts_model_get_value (GtkTreeModel *tree_model,
 
   switch (column)
     {
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_HEADER:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value, shortcut->is_header);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_NOT_HEADER:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value, !shortcut->is_header);
+      break;
+
     case THUNAR_SHORTCUTS_MODEL_COLUMN_NAME:
       g_value_init (value, G_TYPE_STRING);
       if (G_UNLIKELY (shortcut->volume != NULL))
         g_value_take_string (value, g_volume_get_name (shortcut->volume));
+      else if (shortcut->mount != NULL)
+        g_value_take_string (value, g_mount_get_name (shortcut->mount));
       else if (shortcut->name != NULL)
         g_value_set_static_string (value, shortcut->name);
       else if (shortcut->file != NULL)
@@ -518,11 +575,20 @@ thunar_shortcuts_model_get_value (GtkTreeModel *tree_model,
 
     case THUNAR_SHORTCUTS_MODEL_COLUMN_FILE:
       g_value_init (value, THUNAR_TYPE_FILE);
-      if (shortcut->volume != NULL && shortcut->file == NULL)
+
+      if (shortcut->file != NULL)
+        {
+          g_value_set_object (value, shortcut->file);
+        }
+      if (shortcut->volume != NULL
+          || shortcut->mount != NULL)
         {
           /* determine the mount of the volume */
-          mount = g_volume_get_mount (shortcut->volume);
-          
+          if (shortcut->volume != NULL)
+            mount = g_volume_get_mount (shortcut->volume);
+          else
+            mount = g_object_ref (shortcut->mount);
+
           if (G_LIKELY (mount != NULL))
             {
               /* the volume is mounted, get the mount point */
@@ -537,10 +603,6 @@ thunar_shortcuts_model_get_value (GtkTreeModel *tree_model,
               g_object_unref (mount);
             }
         }
-      else
-        {
-          g_value_set_object (value, shortcut->file);
-        }
       break;
 
     case THUNAR_SHORTCUTS_MODEL_COLUMN_GICON:
@@ -553,6 +615,11 @@ thunar_shortcuts_model_get_value (GtkTreeModel *tree_model,
       g_value_set_object (value, shortcut->volume);
       break;
 
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_MOUNT:
+      g_value_init (value, G_TYPE_MOUNT);
+      g_value_set_object (value, shortcut->mount);
+      break;
+
     case THUNAR_SHORTCUTS_MODEL_COLUMN_LOCATION:
       g_value_init (value, G_TYPE_FILE);
       if (shortcut->location != NULL)
@@ -563,14 +630,14 @@ thunar_shortcuts_model_get_value (GtkTreeModel *tree_model,
 
     case THUNAR_SHORTCUTS_MODEL_COLUMN_MUTABLE:
       g_value_init (value, G_TYPE_BOOLEAN);
-      g_value_set_boolean (value, shortcut->type == THUNAR_SHORTCUT_USER_DEFINED);
+      g_value_set_boolean (value, shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS);
       break;
 
     case THUNAR_SHORTCUTS_MODEL_COLUMN_EJECT:
       g_value_init (value, G_TYPE_STRING);
       if (shortcut->volume != NULL)
         {
-          if (thunar_g_volume_is_removable (shortcut->volume) 
+          if (thunar_g_volume_is_removable (shortcut->volume)
               && thunar_g_volume_is_present (shortcut->volume))
             {
               g_value_set_static_string (value, "media-eject");
@@ -586,9 +653,9 @@ thunar_shortcuts_model_get_value (GtkTreeModel *tree_model,
         }
       break;
 
-    case THUNAR_SHORTCUTS_MODEL_COLUMN_SEPARATOR:
-      g_value_init (value, G_TYPE_BOOLEAN);
-      g_value_set_boolean (value, shortcut->type == THUNAR_SHORTCUT_SEPARATOR);
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_GROUP:
+      g_value_init (value, G_TYPE_UINT);
+      g_value_set_uint (value, shortcut->group);
       break;
 
     default:
@@ -698,7 +765,7 @@ thunar_shortcuts_model_row_draggable (GtkTreeDragSource *source,
   shortcut = g_list_nth_data (model->shortcuts, gtk_tree_path_get_indices (path)[0]);
 
   /* special shortcuts cannot be reordered */
-  return (shortcut != NULL && shortcut->type == THUNAR_SHORTCUT_USER_DEFINED);
+  return (shortcut != NULL && shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS);
 }
 
 
@@ -729,18 +796,50 @@ thunar_shortcuts_model_drag_data_delete (GtkTreeDragSource *source,
 
 
 
+static gint
+thunar_shortcuts_model_sort_func (gconstpointer shortcut_a,
+                                  gconstpointer shortcut_b)
+{
+  const ThunarShortcut *a = shortcut_a;
+  const ThunarShortcut *b = shortcut_b;
+
+  /* sort groups */
+  if (a->group != b->group)
+    return a->group - b->group;
+
+  /* sort header at the top of a group */
+  if (a->is_header != b->is_header)
+    return b->is_header ? 1 : -1;
+
+  /* use sort order */
+  if (a->sort_id != b->sort_id)
+    return a->sort_id > b->sort_id ? 1 : -1;
+
+  /* properly sort volumes by timestamp */
+  if (a->volume != NULL && b->volume != NULL)
+    return -g_strcmp0 (g_volume_get_sort_key (a->volume),
+                       g_volume_get_sort_key (b->volume));
+
+  /* properly sort mounts by timestamp */
+  if (a->mount != NULL && b->mount != NULL)
+    return -g_strcmp0 (g_mount_get_sort_key (a->mount),
+                       g_mount_get_sort_key (b->mount));
+
+  return g_strcmp0 (a->name, b->name);
+}
+
+
+
 static void
-thunar_shortcuts_model_add_shortcut (ThunarShortcutsModel *model,
-                                     ThunarShortcut       *shortcut,
-                                     GtkTreePath          *path)
+thunar_shortcuts_model_add_shortcut_with_path (ThunarShortcutsModel *model,
+                                               ThunarShortcut       *shortcut,
+                                               GtkTreePath          *path)
 {
-  GtkTreeIter iter;
+  GtkTreeIter  iter;
+  GtkTreePath *sorted_path = NULL;
 
   _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
   _thunar_return_if_fail (shortcut->file == NULL || THUNAR_IS_FILE (shortcut->file));
-  _thunar_return_if_fail (gtk_tree_path_get_depth (path) > 0);
-  _thunar_return_if_fail (gtk_tree_path_get_indices (path)[0] >= 0);
-  _thunar_return_if_fail (gtk_tree_path_get_indices (path)[0] <= (gint) g_list_length (model->shortcuts));
 
   /* we want to stay informed about changes to the file */
   if (G_LIKELY (shortcut->file != NULL))
@@ -755,12 +854,36 @@ thunar_shortcuts_model_add_shortcut (ThunarShortcutsModel *model,
                         G_CALLBACK (thunar_shortcuts_model_file_destroy), model);
     }
 
-  /* insert the new shortcut to the shortcuts list */
-  model->shortcuts = g_list_insert (model->shortcuts, shortcut, gtk_tree_path_get_indices (path)[0]);
+  if (path == NULL)
+    {
+      /* insert the new shortcut to the shortcuts list */
+      model->shortcuts = g_list_insert_sorted (model->shortcuts, shortcut, thunar_shortcuts_model_sort_func);
+      sorted_path = gtk_tree_path_new_from_indices (g_list_index (model->shortcuts, shortcut), -1);
+      path = sorted_path;
+    }
+  else
+    {
+      model->shortcuts = g_list_insert (model->shortcuts, shortcut, gtk_tree_path_get_indices (path)[0]);
+    }
 
   /* tell everybody that we have a new shortcut */
   gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
   gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
+
+  if (sorted_path)
+    gtk_tree_path_free (sorted_path);
+}
+
+
+
+static void
+thunar_shortcuts_model_add_shortcut (ThunarShortcutsModel *model,
+                                     ThunarShortcut       *shortcut)
+{
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+  _thunar_return_if_fail (shortcut->file == NULL || THUNAR_IS_FILE (shortcut->file));
+
+  thunar_shortcuts_model_add_shortcut_with_path (model, shortcut, NULL);
 }
 
 
@@ -825,7 +948,6 @@ static void
 thunar_shortcuts_model_load (ThunarShortcutsModel *model)
 {
   ThunarShortcut *shortcut;
-  GtkTreePath     *path;
   const gchar     *user_special_dir = NULL;
   ThunarFile      *file;
   GFile           *file_path;
@@ -835,6 +957,7 @@ thunar_shortcuts_model_load (ThunarShortcutsModel *model)
   gchar           *name;
   FILE            *fp;
   gint             i;
+  gint             sort_id;
 
   home = thunar_g_file_new_for_home ();
 
@@ -845,8 +968,7 @@ thunar_shortcuts_model_load (ThunarShortcutsModel *model)
   fp = fopen (bookmarks_path, "r");
   if (G_LIKELY (fp != NULL))
     {
-      /* allocate a tree path for appending to the model */
-      path = gtk_tree_path_new_from_indices (g_list_length (model->shortcuts), -1);
+      sort_id = 0;
 
       while (fgets (line, sizeof (line), fp) != NULL)
         {
@@ -867,34 +989,51 @@ thunar_shortcuts_model_load (ThunarShortcutsModel *model)
           /* parse the URI */
           file_path = g_file_new_for_uri (line);
 
-          /* try to open the file corresponding to the uri */
-          file = thunar_file_get (file_path, NULL);
-          g_object_unref (file_path);
+          /* handle local and remove files differently */
+          if (g_file_has_uri_scheme (file_path, "file"))
+            {
+              /* try to open the file corresponding to the uri */
+              file = thunar_file_get (file_path, NULL);
+              g_object_unref (file_path);
 
-          if (G_UNLIKELY (file == NULL))
-            continue;
+              if (G_UNLIKELY (file == NULL))
+                continue;
 
-          /* make sure the file refers to a directory */
-          if (G_UNLIKELY (thunar_file_is_directory (file)))
+              /* make sure the file refers to a directory */
+              if (G_UNLIKELY (thunar_file_is_directory (file)))
+                {
+                  /* create the shortcut entry */
+                  shortcut = g_slice_new0 (ThunarShortcut);
+                  shortcut->group = THUNAR_SHORTCUT_GROUP_BOOKMARKS;
+                  shortcut->file = file;
+                  shortcut->sort_id = ++sort_id;
+                  shortcut->name = (*name != '\0') ? g_strdup (name) : NULL;
+
+                  /* append the shortcut to the list */
+                  thunar_shortcuts_model_add_shortcut (model, shortcut);
+                }
+              else
+                {
+                  g_object_unref (file);
+                }
+            }
+          else
             {
               /* create the shortcut entry */
               shortcut = g_slice_new0 (ThunarShortcut);
-              shortcut->type = THUNAR_SHORTCUT_USER_DEFINED;
-              shortcut->file = file;
+              shortcut->group = THUNAR_SHORTCUT_GROUP_BOOKMARKS;
+              shortcut->gicon = g_themed_icon_new ("folder-remote");
+              shortcut->location = file_path;
+              shortcut->sort_id = ++sort_id;
               shortcut->name = (*name != '\0') ? g_strdup (name) : NULL;
 
               /* append the shortcut to the list */
-              thunar_shortcuts_model_add_shortcut (model, shortcut, path);
-              gtk_tree_path_next (path);
-            }
-          else
-            {
-              g_object_unref (file);
+              thunar_shortcuts_model_add_shortcut (model, shortcut);
             }
+
         }
 
       /* clean up */
-      gtk_tree_path_free (path);
       fclose (fp);
     }
   else
@@ -917,7 +1056,6 @@ thunar_shortcuts_model_load (ThunarShortcutsModel *model)
       setlocale (LC_MESSAGES, locale);
       g_free (locale);
 
-      path = gtk_tree_path_new_from_indices (g_list_length (model->shortcuts), -1);
       for (i = G_USER_DIRECTORY_DESKTOP;
            i < G_USER_N_DIRECTORIES && _thunar_user_directory_names[i] != NULL;
            ++i)
@@ -958,20 +1096,18 @@ thunar_shortcuts_model_load (ThunarShortcutsModel *model)
 
           /* create the shortcut entry */
           shortcut = g_slice_new0 (ThunarShortcut);
-          shortcut->type = THUNAR_SHORTCUT_USER_DEFINED;
+          shortcut->group = THUNAR_SHORTCUT_GROUP_BOOKMARKS;
           shortcut->file = file;
-          shortcut->name = g_strdup (dgettext (XDG_USER_DIRS_PACKAGE, 
+          shortcut->name = g_strdup (dgettext (XDG_USER_DIRS_PACKAGE,
                                                (gchar *) _thunar_user_directory_names[i]));
 
           /* append the shortcut to the list */
-          thunar_shortcuts_model_add_shortcut (model, shortcut, path);
-          gtk_tree_path_next (path);
+          thunar_shortcuts_model_add_shortcut (model, shortcut);
         }
 
       /* restore the old locale */
       setlocale (LC_MESSAGES, old_locale);
       g_free(old_locale);
-      gtk_tree_path_free (path);
 
       /* we try to save the obtained new model */
       thunar_shortcuts_model_save (model);
@@ -1010,11 +1146,11 @@ thunar_shortcuts_model_monitor (GFileMonitor     *monitor,
       lp = g_list_next (lp);
 
       /* drop the shortcut if it is user-defined */
-      if (shortcut->type == THUNAR_SHORTCUT_USER_DEFINED)
+      if (shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS)
         {
           /* unlink the shortcut from the model */
           model->shortcuts = g_list_remove (model->shortcuts, shortcut);
-          
+
           /* tell everybody that we have lost a shortcut */
           path = gtk_tree_path_new_from_indices (idx, -1);
           gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
@@ -1064,9 +1200,15 @@ thunar_shortcuts_model_save (ThunarShortcutsModel *model)
   for (lp = model->shortcuts; lp != NULL; lp = lp->next)
     {
       shortcut = THUNAR_SHORTCUT (lp->data);
-      if (shortcut->type == THUNAR_SHORTCUT_USER_DEFINED)
+      if (shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS)
         {
-          uri = thunar_file_dup_uri (shortcut->file);
+          if (shortcut->file != NULL)
+            uri = thunar_file_dup_uri (shortcut->file);
+          else if (shortcut->location != NULL)
+            uri = g_file_get_uri (shortcut->location);
+          else
+            continue;
+
           if (G_LIKELY (shortcut->name != NULL))
             fprintf (fp, "%s %s\n", uri, shortcut->name);
           else
@@ -1106,9 +1248,9 @@ thunar_shortcuts_model_file_changed (ThunarFile           *file,
 
   _thunar_return_if_fail (THUNAR_IS_FILE (file));
   _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
- 
-  /* check if the file still refers to a directory or a not mounted URI, 
-   * otherwise we cannot keep it on the shortcuts list, and so we'll treat 
+
+  /* check if the file still refers to a directory or a not mounted URI,
+   * otherwise we cannot keep it on the shortcuts list, and so we'll treat
    * it like the file was destroyed (and thereby removed) */
 
   if (G_UNLIKELY (!thunar_file_is_directory (file)))
@@ -1168,10 +1310,10 @@ thunar_shortcuts_model_volume_changed (GVolumeMonitor       *volume_monitor,
                                        ThunarShortcutsModel *model)
 {
   ThunarShortcut *shortcut = NULL;
-  GtkTreePath    *path;
   GtkTreeIter     iter;
   GList          *lp;
   gint            idx;
+  GtkTreePath    *path;
 
   _thunar_return_if_fail (G_IS_VOLUME_MONITOR (volume_monitor));
   _thunar_return_if_fail (model->volume_monitor == volume_monitor);
@@ -1183,36 +1325,25 @@ thunar_shortcuts_model_volume_changed (GVolumeMonitor       *volume_monitor,
   if (lp != NULL)
     {
       /* check if we need to display the volume now */
-      if (thunar_g_volume_is_removable (volume) && thunar_g_volume_is_present (volume))
+      if (thunar_g_volume_is_removable (volume))
         {
           /* remove the volume from the list of hidden volumes */
           model->hidden_volumes = g_list_delete_link (model->hidden_volumes, lp);
 
-          /* find the insert position */
-          for (idx = 0, lp = model->shortcuts; lp != NULL; ++idx, lp = lp->next)
-            {
-              shortcut = THUNAR_SHORTCUT (lp->data);
-              if (shortcut->type == THUNAR_SHORTCUT_SEPARATOR
-                  || shortcut->type == THUNAR_SHORTCUT_USER_DEFINED)
-                break;
-            }
-
           /* allocate a new shortcut */
           shortcut = g_slice_new0 (ThunarShortcut);
-          shortcut->type = THUNAR_SHORTCUT_REMOVABLE_MEDIA;
+          shortcut->group = THUNAR_SHORTCUT_GROUP_VOLUMES;
           shortcut->volume = volume;
 
           /* the volume is present now, so we have to display it */
-          path = gtk_tree_path_new_from_indices (idx, -1);
-          thunar_shortcuts_model_add_shortcut (model, shortcut, path);
-          gtk_tree_path_free (path);
+          thunar_shortcuts_model_add_shortcut (model, shortcut);
         }
     }
   else
     {
       /* lookup the shortcut that contains the given volume */
-      for (idx = 0, lp = model->shortcuts; 
-           shortcut == NULL && lp != NULL; 
+      for (idx = 0, lp = model->shortcuts;
+           shortcut == NULL && lp != NULL;
            ++idx, lp = lp->next)
         {
           if (THUNAR_SHORTCUT (lp->data)->volume == volume)
@@ -1224,10 +1355,10 @@ thunar_shortcuts_model_volume_changed (GVolumeMonitor       *volume_monitor,
       _thunar_assert (shortcut->volume == volume);
 
       /* check if we need to hide the volume now */
-      if (!thunar_g_volume_is_removable (volume) || !thunar_g_volume_is_present (volume))
+      if (!thunar_g_volume_is_removable (volume))
         {
           /* move the volume to the hidden list */
-          model->hidden_volumes = g_list_prepend (model->hidden_volumes, 
+          model->hidden_volumes = g_list_prepend (model->hidden_volumes,
                                                   g_object_ref (volume));
 
           /* remove the shortcut from the user interface */
@@ -1278,7 +1409,7 @@ thunar_shortcuts_model_volume_removed (GVolumeMonitor       *volume_monitor,
   _thunar_return_if_fail (model->volume_monitor == volume_monitor);
   _thunar_return_if_fail (G_IS_VOLUME (volume));
   _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
-  
+
   lp = g_list_find (model->hidden_volumes, volume);
   if (G_LIKELY (lp != NULL))
     {
@@ -1304,6 +1435,154 @@ thunar_shortcuts_model_volume_removed (GVolumeMonitor       *volume_monitor,
 
 
 
+static gboolean
+thunar_shortcuts_model_has_location (ThunarShortcutsModel *model,
+                                     GFile                *location)
+{
+  GList          *lp;
+  ThunarShortcut *shortcut;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
+  _thunar_return_val_if_fail (G_IS_FILE (location), FALSE);
+
+  for (lp = model->shortcuts; lp != NULL; lp = lp->next)
+    {
+      shortcut = lp->data;
+
+      /* check if we have a location that matches */
+      if (shortcut->location != NULL
+          && g_file_equal (shortcut->location, location))
+        return TRUE;
+
+      /* check if we have a file that matches */
+      if (shortcut->file != NULL
+          && g_file_equal (thunar_file_get_file (shortcut->file), location))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+
+static void
+thunar_shortcuts_model_mount_changed (GVolumeMonitor       *volume_monitor,
+                                      GMount               *mount,
+                                      ThunarShortcutsModel *model)
+{
+  GList       *lp;
+  guint        idx;
+  GtkTreeIter  iter;
+  GtkTreePath *path;
+
+  _thunar_return_if_fail (G_IS_VOLUME_MONITOR (volume_monitor));
+  _thunar_return_if_fail (model->volume_monitor == volume_monitor);
+  _thunar_return_if_fail (G_IS_MOUNT (mount));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+
+  /* find mount */
+  for (lp = model->shortcuts, idx = 0; lp != NULL; lp = lp->next, idx++)
+    if (THUNAR_SHORTCUT (lp->data)->mount == mount)
+      break;
+
+  /* something is broken if we don't have a shortcut here */
+  _thunar_assert (lp != NULL);
+  _thunar_assert (THUNAR_SHORTCUT (lp->data)->mount == mount);
+
+  /* generate an iterator for the path */
+  GTK_TREE_ITER_INIT (iter, model->stamp, lp);
+
+  /* tell the view that the volume has changed in some way */
+  path = gtk_tree_path_new_from_indices (idx, -1);
+  gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
+  gtk_tree_path_free (path);
+}
+
+
+
+static void
+thunar_shortcuts_model_mount_added (GVolumeMonitor       *volume_monitor,
+                                    GMount               *mount,
+                                    ThunarShortcutsModel *model)
+{
+  GVolume             *volume;
+  GFile               *location;
+  ThunarShortcutGroup  group;
+  ThunarShortcut      *shortcut;
+
+  _thunar_return_if_fail (G_IS_VOLUME_MONITOR (volume_monitor));
+  _thunar_return_if_fail (model->volume_monitor == volume_monitor);
+  _thunar_return_if_fail (G_IS_MOUNT (mount));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+
+  /* never show shadowed mounts */
+  if (g_mount_is_shadowed (mount))
+    return;
+
+  /* skip mounts with a volume */
+  volume = g_mount_get_volume (mount);
+  if (volume != NULL)
+    {
+      g_object_unref (volume);
+      return;
+    }
+
+  location = g_mount_get_root (mount);
+
+  /* skip ghoto locations, since those also have a mount
+   * and igore locations already in the model */
+  if (g_file_has_uri_scheme (location, "gphoto2")
+      || thunar_shortcuts_model_has_location (model, location))
+    {
+      g_object_unref (location);
+      return;
+    }
+
+  if (g_file_has_uri_scheme (location, "file")
+      || g_file_has_uri_scheme (location, "archive"))
+    group = THUNAR_SHORTCUT_GROUP_MOUNTS;
+  else
+    group = THUNAR_SHORTCUT_GROUP_NETWORK_MOUNTS;
+
+  /* allocate a new shortcut */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->group = group;
+  shortcut->location = location;
+  shortcut->mount = g_object_ref (mount);
+
+  /* the mount is present now, so we have to display it */
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
+}
+
+
+
+static void
+thunar_shortcuts_model_mount_removed (GVolumeMonitor       *volume_monitor,
+                                      GMount               *mount,
+                                      ThunarShortcutsModel *model)
+{
+  GList *lp;
+
+  _thunar_return_if_fail (G_IS_VOLUME_MONITOR (volume_monitor));
+  _thunar_return_if_fail (model->volume_monitor == volume_monitor);
+  _thunar_return_if_fail (G_IS_MOUNT (mount));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+
+  /* find mount */
+  for (lp = model->shortcuts; lp != NULL; lp = lp->next)
+    if (THUNAR_SHORTCUT (lp->data)->mount == mount)
+      break;
+
+  /* something is broken if we don't have a shortcut here */
+  _thunar_assert (lp != NULL);
+  _thunar_assert (THUNAR_SHORTCUT (lp->data)->mount == mount);
+
+  /* drop the shortcut from the model */
+  thunar_shortcuts_model_remove_shortcut (model, lp->data);
+}
+
+
+
 static void
 thunar_shortcut_free (ThunarShortcut       *shortcut,
                       ThunarShortcutsModel *model)
@@ -1396,7 +1675,7 @@ thunar_shortcuts_model_iter_for_file (ThunarShortcutsModel *model,
   GFile          *mount_point;
   GList          *lp;
   ThunarShortcut *shortcut;
-  
+
   _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
   _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
   _thunar_return_val_if_fail (iter != NULL, FALSE);
@@ -1508,12 +1787,19 @@ thunar_shortcuts_model_drop_possible (ThunarShortcutsModel *model,
   /* determine the list item for the path */
   shortcut = g_list_nth_data (model->shortcuts, gtk_tree_path_get_indices (path)[0]);
 
-  /* append to the list is always possible */
+  /* append to the list is not possible */
   if (G_LIKELY (shortcut == NULL))
-    return TRUE;
+    return FALSE;
 
   /* cannot drop before special shortcuts! */
-  return (shortcut->type == THUNAR_SHORTCUT_USER_DEFINED);
+  if (shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS)
+    return TRUE;
+
+  /* we can drop at the end of the bookmarks (before network header) */
+  if (shortcut->group == THUNAR_SHORTCUT_GROUP_NETWORK && shortcut->is_header)
+    return TRUE;
+
+  return FALSE;
 }
 
 
@@ -1548,11 +1834,11 @@ thunar_shortcuts_model_add (ThunarShortcutsModel *model,
 
   /* create the new shortcut that will be inserted */
   shortcut = g_slice_new0 (ThunarShortcut);
-  shortcut->type = THUNAR_SHORTCUT_USER_DEFINED;
+  shortcut->group = THUNAR_SHORTCUT_GROUP_BOOKMARKS;
   shortcut->file = g_object_ref (G_OBJECT (file));
 
   /* add the shortcut to the list at the given position */
-  thunar_shortcuts_model_add_shortcut (model, shortcut, dst_path);
+  thunar_shortcuts_model_add_shortcut_with_path (model, shortcut, dst_path);
 
   /* the shortcuts list was changed, so write the gtk bookmarks file */
   thunar_shortcuts_model_save (model);
@@ -1606,6 +1892,8 @@ thunar_shortcuts_model_move (ThunarShortcutsModel *model,
 
       for (; idx < index_dst; ++idx, lp = lp->next)
         {
+          if (lp->next == NULL)
+            break;
           lp->data = lp->next->data;
           order[idx] = idx + 1;
         }
@@ -1675,11 +1963,10 @@ thunar_shortcuts_model_remove (ThunarShortcutsModel *model,
   shortcut = g_list_nth_data (model->shortcuts, gtk_tree_path_get_indices (path)[0]);
 
   /* verify that the shortcut is removable */
-  _thunar_assert (shortcut->type == THUNAR_SHORTCUT_USER_DEFINED);
-  _thunar_assert (THUNAR_IS_FILE (shortcut->file));
+  _thunar_assert (shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS);
 
   /* remove the shortcut (using the file destroy handler) */
-  thunar_shortcuts_model_file_destroy (shortcut->file, model);
+  thunar_shortcuts_model_remove_shortcut (model, shortcut);
 }
 
 
@@ -1714,7 +2001,7 @@ thunar_shortcuts_model_rename (ThunarShortcutsModel *model,
   shortcut = THUNAR_SHORTCUT (((GList *) iter->user_data)->data);
 
   /* verify the shortcut */
-  _thunar_assert (shortcut->type == THUNAR_SHORTCUT_USER_DEFINED);
+  _thunar_assert (shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS);
   _thunar_assert (THUNAR_IS_FILE (shortcut->file));
 
   /* perform the rename */
diff --git a/thunar/thunar-shortcuts-model.h b/thunar/thunar-shortcuts-model.h
index 71e897c..7be3034 100644
--- a/thunar/thunar-shortcuts-model.h
+++ b/thunar/thunar-shortcuts-model.h
@@ -36,6 +36,7 @@ typedef struct _ThunarShortcutsModel      ThunarShortcutsModel;
 
 /**
  * ThunarShortcutsModelColumn:
+ * @THUNAR_SHORTCUTS_MODEL_COLUMN_TYPE      : #ThunarShortcutType.
  * @THUNAR_SHORTCUTS_MODEL_COLUMN_NAME      : the index of the name column.
  * @THUNAR_SHORTCUTS_MODEL_COLUMN_FILE      : the index of the file column.
  * @THUNAR_SHORTCUTS_MODEL_COLUMN_LOCATION  : file of the location.
@@ -50,17 +51,34 @@ typedef struct _ThunarShortcutsModel      ThunarShortcutsModel;
  **/
 typedef enum
 {
+  THUNAR_SHORTCUTS_MODEL_COLUMN_HEADER,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_NOT_HEADER,
   THUNAR_SHORTCUTS_MODEL_COLUMN_NAME,
   THUNAR_SHORTCUTS_MODEL_COLUMN_FILE,
   THUNAR_SHORTCUTS_MODEL_COLUMN_LOCATION,
   THUNAR_SHORTCUTS_MODEL_COLUMN_GICON,
   THUNAR_SHORTCUTS_MODEL_COLUMN_VOLUME,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_MOUNT,
   THUNAR_SHORTCUTS_MODEL_COLUMN_MUTABLE,
   THUNAR_SHORTCUTS_MODEL_COLUMN_EJECT,
-  THUNAR_SHORTCUTS_MODEL_COLUMN_SEPARATOR,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_GROUP,
   THUNAR_SHORTCUTS_MODEL_N_COLUMNS,
 } ThunarShortcutsModelColumn;
 
+typedef enum
+{
+  THUNAR_SHORTCUT_GROUP_DEVICES,
+  THUNAR_SHORTCUT_GROUP_VOLUMES,
+  THUNAR_SHORTCUT_GROUP_MOUNTS,
+  THUNAR_SHORTCUT_GROUP_PLACES,
+  THUNAR_SHORTCUT_GROUP_TRASH,
+  THUNAR_SHORTCUT_GROUP_BOOKMARKS,
+  THUNAR_SHORTCUT_GROUP_NETWORK,
+  THUNAR_SHORTCUT_GROUP_NETWORK_MOUNTS
+} ThunarShortcutGroup;
+
+
+
 GType                  thunar_shortcuts_model_get_type      (void) G_GNUC_CONST;
 
 ThunarShortcutsModel *thunar_shortcuts_model_get_default    (void);
diff --git a/thunar/thunar-shortcuts-view.c b/thunar/thunar-shortcuts-view.c
index 52103a1..afdb213 100644
--- a/thunar/thunar-shortcuts-view.c
+++ b/thunar/thunar-shortcuts-view.c
@@ -3,18 +3,18 @@
  * Copyright (c) 2005-2007 Benedikt Meurer <benny at xfce.org>
  * Copyright (c) 2009-2011 Jannis Pohlmann <jannis at xfce.org>
  *
- * This program is free software; you can redistribute it and/or 
+ * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of 
+ * published by the Free Software Foundation; either version 2 of
  * the License, or (at your option) any later version.
  *
  * This program 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 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public 
- * License along with this program; if not, write to the Free 
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */
@@ -128,11 +128,9 @@ static void           thunar_shortcuts_view_open                         (Thunar
                                                                           gboolean                  new_window);
 static void           thunar_shortcuts_view_open_in_new_window_clicked   (ThunarShortcutsView      *view);
 static void           thunar_shortcuts_view_empty_trash                  (ThunarShortcutsView      *view);
-static void           thunar_shortcuts_view_eject                        (ThunarShortcutsView      *view);
+static void           thunar_shortcuts_view_disconnect                   (ThunarShortcutsView      *view);
 static void           thunar_shortcuts_view_mount                        (ThunarShortcutsView      *view);
-static gboolean       thunar_shortcuts_view_separator_func               (GtkTreeModel             *model,
-                                                                          GtkTreeIter              *iter,
-                                                                          gpointer                  user_data);
+static void           thunar_shortcuts_view_unmount                      (ThunarShortcutsView      *view);
 
 
 
@@ -146,7 +144,7 @@ struct _ThunarShortcutsView
   GtkTreeView        __parent__;
   ThunarPreferences *preferences;
   GtkCellRenderer   *icon_renderer;
-  
+
   ThunarxProviderFactory *provider_factory;
 
   /* the currently pressed mouse button, set in the
@@ -230,17 +228,34 @@ thunar_shortcuts_view_class_init (ThunarShortcutsViewClass *klass)
 }
 
 
+static gboolean
+thunar_shortcuts_view_selection_func (GtkTreeSelection *selection,
+                                      GtkTreeModel *model,
+                                      GtkTreePath *path,
+                                      gboolean path_currently_selected,
+                                      gpointer user_data)
+{
+  GtkTreeIter iter;
+  gboolean    is_header;
+
+  /* don't allow selecting headers */
+  gtk_tree_model_get_iter (model, &iter, path);
+  gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_HEADER, &is_header, -1);
+  return !is_header;
+}
+
 
 static void
 thunar_shortcuts_view_init (ThunarShortcutsView *view)
 {
   GtkTreeViewColumn *column;
   GtkCellRenderer   *renderer;
+  GtkTreeSelection  *selection;
 
   /* configure the tree view */
   gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), FALSE);
   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
-  
+
   /* grab a reference on the provider factory */
   view->provider_factory = thunarx_provider_factory_get_default ();
 
@@ -263,6 +278,27 @@ thunar_shortcuts_view_init (ThunarShortcutsView *view)
   view->queue_resize_signal_id = g_signal_connect_swapped (G_OBJECT (view->preferences), "notify::shortcuts-icon-size",
                                                            G_CALLBACK (gtk_tree_view_column_queue_resize), column);
 
+  /* header */
+  renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
+                           "weight", PANGO_WEIGHT_BOLD,
+                           "weight-set", TRUE,
+                           "xpad", 6,
+                           "ypad", 6,
+                           NULL);
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_attributes (column, renderer,
+                                       "text", THUNAR_SHORTCUTS_MODEL_COLUMN_NAME,
+                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_HEADER,
+                                       NULL);
+
+  /* separator for indent */
+  renderer = gtk_cell_renderer_text_new ();
+  g_object_set (G_OBJECT (renderer), "xpad", 6, NULL);
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_attributes (column, renderer,
+                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_NOT_HEADER,
+                                       NULL);
+
   /* allocate the special icon renderer */
   view->icon_renderer = thunar_shortcuts_icon_renderer_new ();
   gtk_tree_view_column_pack_start (column, view->icon_renderer, FALSE);
@@ -270,8 +306,12 @@ thunar_shortcuts_view_init (ThunarShortcutsView *view)
                                        "gicon", THUNAR_SHORTCUTS_MODEL_COLUMN_GICON,
                                        "file", THUNAR_SHORTCUTS_MODEL_COLUMN_FILE,
                                        "volume", THUNAR_SHORTCUTS_MODEL_COLUMN_VOLUME,
+                                       "mount", THUNAR_SHORTCUTS_MODEL_COLUMN_MOUNT,
+                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_NOT_HEADER,
                                        NULL);
 
+
+
   /* sync the "emblems" property of the icon renderer with the "shortcuts-icon-emblems" preference
    * and the "size" property of the renderer with the "shortcuts-icon-size" preference.
    */
@@ -288,6 +328,7 @@ thunar_shortcuts_view_init (ThunarShortcutsView *view)
   gtk_tree_view_column_pack_start (column, renderer, TRUE);
   gtk_tree_view_column_set_attributes (column, renderer,
                                        "text", THUNAR_SHORTCUTS_MODEL_COLUMN_NAME,
+                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_NOT_HEADER,
                                        NULL);
 
   /* allocate icon renderer for the eject symbol */
@@ -296,6 +337,7 @@ thunar_shortcuts_view_init (ThunarShortcutsView *view)
   gtk_tree_view_column_pack_start (column, renderer, FALSE);
   gtk_tree_view_column_set_attributes (column, renderer,
                                        "icon-name", THUNAR_SHORTCUTS_MODEL_COLUMN_EJECT,
+                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_NOT_HEADER,
                                        NULL);
 
 
@@ -309,8 +351,8 @@ thunar_shortcuts_view_init (ThunarShortcutsView *view)
   gtk_drag_dest_set (GTK_WIDGET (view), 0, drop_targets, G_N_ELEMENTS (drop_targets),
                      GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE);
 
-  /* setup a row separator function to tell GtkTreeView about the separator */
-  gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (view), thunar_shortcuts_view_separator_func, NULL, NULL);
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+  gtk_tree_selection_set_select_function (selection, thunar_shortcuts_view_selection_func, NULL, NULL);
 }
 
 
@@ -398,7 +440,7 @@ thunar_shortcuts_view_button_press_event (GtkWidget      *widget,
             }
 
           /*
-          g_debug("thunar_shortcuts_view_button_press_event(): x: %f, y: %f; my width: %i, eject width: %i, eject: %i", 
+          g_debug("thunar_shortcuts_view_button_press_event(): x: %f, y: %f; my width: %i, eject width: %i, eject: %i",
                   event->x, event->y, column_width, icon_width, view->pressed_eject_button);
           */
 
@@ -427,7 +469,7 @@ thunar_shortcuts_view_button_release_event (GtkWidget      *widget,
   if (G_LIKELY (view->pressed_button == (gint) event->button))
     {
       if (view->pressed_eject_button)
-        thunar_shortcuts_view_eject (view);
+        thunar_shortcuts_view_disconnect (view);
 
       /* check if we should simply open or open in new window */
       if (G_LIKELY (event->button == 1))
@@ -712,6 +754,8 @@ thunar_shortcuts_view_drag_motion (GtkWidget      *widget,
 
       /* compute the drop position for the coordinates */
       path = thunar_shortcuts_view_compute_drop_position (view, x, y);
+      if (path == NULL)
+        return FALSE;
 
       /* check if path is about to append to the model */
       model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
@@ -828,16 +872,19 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
                                     GtkTreeModel        *model,
                                     GtkTreeIter         *iter)
 {
-  GtkTreePath *path;
-  ThunarFile  *file;
-  GtkWidget   *image;
-  GtkWidget   *menu;
-  GtkWidget   *item;
-  GtkWidget   *window;
-  gboolean     mutable;
-  GVolume     *volume;
-  GList       *providers, *lp;
-  GList       *actions = NULL, *tmp;
+  GtkTreePath         *path;
+  ThunarFile          *file;
+  GtkWidget           *image;
+  GtkWidget           *menu;
+  GtkWidget           *item;
+  GtkWidget           *window;
+  gboolean             mutable;
+  GVolume             *volume;
+  GMount              *mount;
+  GMount              *volume_mount;
+  GList               *providers, *lp;
+  GList               *actions = NULL, *tmp;
+  ThunarShortcutGroup  group;
 
   /* determine the tree path for the given iter */
   path = gtk_tree_model_get_path (model, iter);
@@ -848,7 +895,9 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
   gtk_tree_model_get (model, iter,
                       THUNAR_SHORTCUTS_MODEL_COLUMN_FILE, &file,
                       THUNAR_SHORTCUTS_MODEL_COLUMN_VOLUME, &volume,
-                      THUNAR_SHORTCUTS_MODEL_COLUMN_MUTABLE, &mutable, 
+                      THUNAR_SHORTCUTS_MODEL_COLUMN_MOUNT, &mount,
+                      THUNAR_SHORTCUTS_MODEL_COLUMN_MUTABLE, &mutable,
+                      THUNAR_SHORTCUTS_MODEL_COLUMN_GROUP, &group,
                       -1);
 
   /* prepare the popup menu */
@@ -870,49 +919,70 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
   gtk_widget_show (item);
 
-  /* append a menu separator */
-  item = gtk_separator_menu_item_new ();
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-  gtk_widget_show (item);
-
   /* check if we have a volume here */
-  if (G_UNLIKELY (volume != NULL))
+  switch (group)
     {
-      /* append the "Mount Volume" menu action */
-      item = gtk_image_menu_item_new_with_mnemonic (_("_Mount Volume"));
-      gtk_widget_set_sensitive (item, !thunar_g_volume_is_mounted (volume));
-      g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_mount), view);
-      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-      gtk_widget_show (item);
+      case THUNAR_SHORTCUT_GROUP_VOLUMES:
+        /* append a menu separator */
+        item = gtk_separator_menu_item_new ();
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+        gtk_widget_show (item);
+
+        volume_mount = g_volume_get_mount (volume);
+
+        /* append the "Mount" item */
+        item = gtk_image_menu_item_new_with_mnemonic (_("_Mount"));
+        gtk_widget_set_visible (item, volume_mount == NULL && g_volume_can_mount (volume));
+        g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_mount), view);
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+        /* append the "Unmount" item */
+        item = gtk_image_menu_item_new_with_mnemonic (_("_Unmount"));
+        gtk_widget_set_visible (item, volume_mount != NULL && g_mount_can_unmount (volume_mount));
+        g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_unmount), view);
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+        /* append the "Disconnect" (eject + safely remove drive) item */
+        item = gtk_image_menu_item_new_with_mnemonic (_("Disconn_ect"));
+        gtk_widget_set_visible (item, (volume_mount != NULL && g_mount_can_eject (volume_mount)) || g_volume_can_eject (volume));
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+        g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_disconnect), view);
+
+        if (volume_mount != NULL)
+          g_object_unref (volume_mount);
+        break;
 
-      /* check if the volume is present and can be ejected */
-      if (thunar_g_volume_is_present (volume) && thunar_g_volume_is_removable (volume))
-        {
-          /* append the "Eject Volume" menu action */
-          item = gtk_image_menu_item_new_with_mnemonic (_("E_ject Volume"));
-          g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_eject), view);
-          gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-          gtk_widget_show (item);
-        }
+      case THUNAR_SHORTCUT_GROUP_MOUNTS:
+      case THUNAR_SHORTCUT_GROUP_NETWORK_MOUNTS:
+        /* append a menu separator */
+        item = gtk_separator_menu_item_new ();
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+        gtk_widget_show (item);
+
+        /* append the "Disconnect" item */
+        item = gtk_image_menu_item_new_with_mnemonic (_("Disconn_ect"));
+        g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_disconnect), view);
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+        gtk_widget_show (item);
+        break;
 
-      /* append a menu separator */
-      item = gtk_separator_menu_item_new ();
-      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-      gtk_widget_show (item);
-    }
-  else if (G_UNLIKELY (file != NULL && thunar_file_is_trashed (file) && thunar_file_is_root (file)))
-    {
-      /* append the "Empty Trash" menu action */
-      item = gtk_image_menu_item_new_with_mnemonic (_("_Empty Trash"));
-      gtk_widget_set_sensitive (item, (thunar_file_get_item_count (file) > 0));
-      g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_empty_trash), view);
-      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-      gtk_widget_show (item);
 
-      /* append a menu separator */
-      item = gtk_separator_menu_item_new ();
-      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-      gtk_widget_show (item);
+      case THUNAR_SHORTCUT_GROUP_TRASH:
+        /* append a menu separator */
+        item = gtk_separator_menu_item_new ();
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+        gtk_widget_show (item);
+
+        /* append the "Empty Trash" menu action */
+        item = gtk_image_menu_item_new_with_mnemonic (_("_Empty Trash"));
+        gtk_widget_set_sensitive (item, (thunar_file_get_item_count (file) > 0));
+        g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_empty_trash), view);
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+        gtk_widget_show (item);
+        break;
+
+      default:
+        break;
     }
 
   /* create provider menu items if there is a non-trashed file */
@@ -934,6 +1004,14 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
             }
           g_list_free (providers);
 
+          if (actions != NULL)
+            {
+              /* append a menu separator */
+              item = gtk_separator_menu_item_new ();
+              gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+              gtk_widget_show (item);
+            }
+
           /* add the actions to the menu */
           for (lp = actions; lp != NULL; lp = lp->next)
             {
@@ -945,43 +1023,43 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
               g_object_unref (G_OBJECT (lp->data));
             }
 
-          /* add a separator to the end of the menu */
-          if (G_LIKELY (lp != actions))
-            {
-              /* append a menu separator */
-              item = gtk_separator_menu_item_new ();
-              gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-              gtk_widget_show (item);
-            }
-
           /* cleanup */
           g_list_free (actions);
         }
     }
 
   /* append the remove menu item */
-  item = gtk_image_menu_item_new_with_mnemonic (_("_Remove Shortcut"));
-  g_object_set_data_full (G_OBJECT (item), I_("thunar-shortcuts-row"),
-                          gtk_tree_row_reference_new (model, path),
-                          (GDestroyNotify) gtk_tree_row_reference_free);
-  g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_remove_activated), view);
-  gtk_widget_set_sensitive (item, mutable);
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-  gtk_widget_show (item);
+  if (group == THUNAR_SHORTCUT_GROUP_BOOKMARKS)
+    {
+      /* append a menu separator */
+      item = gtk_separator_menu_item_new ();
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+      gtk_widget_show (item);
 
-  /* set the remove stock icon */
-  image = gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU);
-  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+      item = gtk_image_menu_item_new_with_mnemonic (_("_Remove Shortcut"));
+      g_object_set_data_full (G_OBJECT (item), I_("thunar-shortcuts-row"),
+                              gtk_tree_row_reference_new (model, path),
+                              (GDestroyNotify) gtk_tree_row_reference_free);
+      g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_remove_activated), view);
+      gtk_widget_set_sensitive (item, mutable);
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+      gtk_widget_show (item);
 
-  /* append the rename menu item */
-  item = gtk_image_menu_item_new_with_mnemonic (_("Re_name Shortcut"));
-  g_object_set_data_full (G_OBJECT (item), I_("thunar-shortcuts-row"),
-                          gtk_tree_row_reference_new (model, path),
-                          (GDestroyNotify) gtk_tree_row_reference_free);
-  g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_rename_activated), view);
-  gtk_widget_set_sensitive (item, mutable);
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-  gtk_widget_show (item);
+      /* set the remove stock icon */
+      image = gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU);
+      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+
+      /* append the rename menu item */
+      item = gtk_image_menu_item_new_with_mnemonic (_("Re_name Shortcut"));
+      g_object_set_data_full (G_OBJECT (item), I_("thunar-shortcuts-row"),
+                              gtk_tree_row_reference_new (model, path),
+                              (GDestroyNotify) gtk_tree_row_reference_free);
+      g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_rename_activated), view);
+      gtk_widget_set_sensitive (item, mutable);
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+      gtk_widget_show (item);
+    }
 
   /* run the menu on the view's screen (taking over the floating reference on menu) */
   thunar_gtk_menu_run (GTK_MENU (menu), GTK_WIDGET (view), NULL, NULL, (event != NULL) ? event->button : 0,
@@ -992,6 +1070,8 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
     g_object_unref (G_OBJECT (file));
   if (G_UNLIKELY (volume != NULL))
     g_object_unref (G_OBJECT (volume));
+  if (G_UNLIKELY (mount != NULL))
+    g_object_unref (G_OBJECT (mount));
   gtk_tree_path_free (path);
 }
 
@@ -1034,7 +1114,7 @@ thunar_shortcuts_view_rename_activated (GtkWidget           *item,
       /* determine the text renderer */
       column = gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0);
       renderers = gtk_cell_layout_get_cells  (GTK_CELL_LAYOUT (column));
-      renderer = g_list_nth_data (renderers, 1);
+      renderer = g_list_nth_data (renderers, 3);
 
       /* make sure the text renderer is editable */
       g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
@@ -1142,6 +1222,9 @@ thunar_shortcuts_view_compute_drop_actions (ThunarShortcutsView     *view,
       /* but maybe we can add as shortcut */
       path = thunar_shortcuts_view_compute_drop_position (view, x, y);
 
+      if (path == NULL)
+        return 0;
+
       /* check if path is about to append to the model */
       if (gtk_tree_path_get_indices (path)[0] >= gtk_tree_model_iter_n_children (model, NULL))
         {
@@ -1176,7 +1259,7 @@ thunar_shortcuts_view_compute_drop_position (ThunarShortcutsView *view,
   GtkTreeViewColumn *column;
   GtkTreeModel      *model;
   GdkRectangle       area;
-  GtkTreePath       *path;
+  GtkTreePath       *path = NULL;
   gint               n_rows;
 
   _thunar_return_val_if_fail (gtk_tree_view_get_model (GTK_TREE_VIEW (view)) != NULL, NULL);
@@ -1189,7 +1272,7 @@ thunar_shortcuts_view_compute_drop_position (ThunarShortcutsView *view,
   if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view), x, y,
                                      &path, &column, &x, &y))
     {
-      /* determine the exact path of the row the user is trying to drop 
+      /* determine the exact path of the row the user is trying to drop
        * (taking into account the relative y position)
        */
       gtk_tree_view_get_background_area (GTK_TREE_VIEW (view), path, column, &area);
@@ -1200,14 +1283,11 @@ thunar_shortcuts_view_compute_drop_position (ThunarShortcutsView *view,
       for (; gtk_tree_path_get_indices (path)[0] < n_rows; gtk_tree_path_next (path))
         if (thunar_shortcuts_model_drop_possible (THUNAR_SHORTCUTS_MODEL (model), path))
           return path;
-    }
-  else
-    {
-      /* we'll append to the shortcuts list */
-      path = gtk_tree_path_new_from_indices (n_rows, -1);
+
+      gtk_tree_path_free (path);
     }
 
-  return path;
+  return NULL;
 }
 
 
@@ -1284,7 +1364,7 @@ thunar_shortcuts_view_poke_file_finish (ThunarBrowser *browser,
   gboolean           new_window = GPOINTER_TO_UINT (user_data);
 
   _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (browser));
-  _thunar_return_if_fail (THUNAR_IS_FILE (file));
+  _thunar_return_if_fail (THUNAR_IS_FILE (target_file));
 
   if (error == NULL)
     {
@@ -1292,7 +1372,7 @@ thunar_shortcuts_view_poke_file_finish (ThunarBrowser *browser,
         {
           /* open a new window for the target folder */
           application = thunar_application_get ();
-          thunar_application_open_window (application, target_file, 
+          thunar_application_open_window (application, target_file,
                                           gtk_widget_get_screen (GTK_WIDGET (browser)), NULL);
           g_object_unref (application);
         }
@@ -1321,15 +1401,25 @@ thunar_shortcuts_view_poke_location_finish (ThunarBrowser *browser,
 {
   ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (browser);
   GtkTreeModel        *model;
+  gchar               *name;
 
   _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (browser));
-  _thunar_return_if_fail (THUNAR_IS_FILE (file));
+  _thunar_return_if_fail (G_IS_FILE (location));
 
   /* sotre the new file in the shortcuts model */
-  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
-  thunar_shortcuts_model_set_file (THUNAR_SHORTCUTS_MODEL (model), location, file);
+  if (error == NULL)
+    {
+      model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+      thunar_shortcuts_model_set_file (THUNAR_SHORTCUTS_MODEL (model), location, file);
 
-  thunar_shortcuts_view_poke_file_finish (browser, file, target_file, error, user_data);
+      thunar_shortcuts_view_poke_file_finish (browser, file, target_file, error, user_data);
+    }
+  else
+    {
+      name = thunar_g_file_get_display_name (location);
+      thunar_dialogs_show_error (GTK_WIDGET (browser), error, _("Failed to open \"%s\""), name);
+      g_free (name);
+    }
 }
 
 
@@ -1356,7 +1446,7 @@ thunar_shortcuts_view_poke_volume_finish (ThunarBrowser *browser,
   else
     {
       volume_name = g_volume_get_name (volume);
-      thunar_dialogs_show_error (GTK_WIDGET (browser), error, 
+      thunar_dialogs_show_error (GTK_WIDGET (browser), error,
                                  _("Failed to mount \"%s\""), volume_name);
       g_free (volume_name);
     }
@@ -1387,7 +1477,7 @@ thunar_shortcuts_view_open (ThunarShortcutsView *view,
   if (gtk_tree_selection_get_selected (selection, &model, &iter))
     {
       /* determine the file for the shortcut at the given tree iterator */
-      gtk_tree_model_get (model, &iter, 
+      gtk_tree_model_get (model, &iter,
                           THUNAR_SHORTCUTS_MODEL_COLUMN_FILE, &file,
                           THUNAR_SHORTCUTS_MODEL_COLUMN_VOLUME, &volume,
                           THUNAR_SHORTCUTS_MODEL_COLUMN_LOCATION, &location,
@@ -1399,7 +1489,7 @@ thunar_shortcuts_view_open (ThunarShortcutsView *view,
                                       thunar_shortcuts_view_poke_volume_finish,
                                       GUINT_TO_POINTER (new_window));
         }
-      else if (file != NULL) 
+      else if (file != NULL)
         {
           thunar_browser_poke_file (THUNAR_BROWSER (view), file, view,
                                     thunar_shortcuts_view_poke_file_finish,
@@ -1450,9 +1540,9 @@ thunar_shortcuts_view_empty_trash (ThunarShortcutsView *view)
 
 
 static void
-thunar_shortcuts_view_eject_finish (GObject      *object,
-                                    GAsyncResult *result,
-                                    gpointer      user_data)
+thunar_shortcuts_view_eject_volume_finish (GObject      *object,
+                                           GAsyncResult *result,
+                                           gpointer      user_data)
 {
   ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (user_data);
   GtkWidget           *window;
@@ -1491,6 +1581,47 @@ thunar_shortcuts_view_eject_finish (GObject      *object,
 
 
 static void
+thunar_shortcuts_view_eject_mount_finish (GObject      *object,
+                                          GAsyncResult *result,
+                                          gpointer      user_data)
+{
+  ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (user_data);
+  GtkWidget           *window;
+  GMount              *mount = G_MOUNT (object);
+  GError              *error = NULL;
+  gchar               *mount_name;
+
+  _thunar_return_if_fail (G_IS_MOUNT (object));
+  _thunar_return_if_fail (G_IS_ASYNC_RESULT (result));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
+
+  /* check if there was an error */
+  if (!g_mount_eject_with_operation_finish (mount, result, &error))
+    {
+      /* ignore GIO errors already handled */
+      if (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_FAILED_HANDLED)
+        {
+          window = gtk_widget_get_toplevel (GTK_WIDGET (view));
+
+          /* display an error dialog to inform the user */
+          mount_name = g_mount_get_name (mount);
+          thunar_dialogs_show_error (window, error, _("Failed to eject \"%s\""), mount_name);
+          g_free (mount_name);
+
+          g_error_free (error);
+        }
+    }
+
+#ifdef HAVE_LIBNOTIFY
+  thunar_notify_unmount_finish (mount);
+#endif
+
+  g_object_unref (view);
+}
+
+
+
+static void
 thunar_shortcuts_view_unmount_finish (GObject      *object,
                                       GAsyncResult *result,
                                       gpointer      user_data)
@@ -1532,13 +1663,14 @@ thunar_shortcuts_view_unmount_finish (GObject      *object,
 
 
 static void
-thunar_shortcuts_view_eject (ThunarShortcutsView *view)
+thunar_shortcuts_view_disconnect (ThunarShortcutsView *view)
 {
   GtkTreeSelection *selection;
   GtkTreeModel     *model;
   GtkTreeIter       iter;
   GVolume          *volume;
   GMount           *mount;
+  GMount           *volume_mount;
   GMountOperation  *mount_operation;
   GtkWidget        *window;
 
@@ -1546,52 +1678,109 @@ thunar_shortcuts_view_eject (ThunarShortcutsView *view)
 
   /* determine the selected item */
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
-  if (gtk_tree_selection_get_selected (selection, &model, &iter))
+  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+    return;
+
+  /* determine the volume/mount for the shortcut at the given tree iterator */
+  gtk_tree_model_get (model, &iter,
+                      THUNAR_SHORTCUTS_MODEL_COLUMN_VOLUME, &volume,
+                      THUNAR_SHORTCUTS_MODEL_COLUMN_MOUNT, &mount, -1);
+
+  _thunar_return_if_fail (volume == NULL || G_IS_VOLUME (volume));
+  _thunar_return_if_fail (mount == NULL || G_IS_MOUNT (mount));
+
+  /* prepare a mount operation */
+  window = gtk_widget_get_toplevel (GTK_WIDGET (view));
+  mount_operation = gtk_mount_operation_new (GTK_WINDOW (window));
+
+  if (mount != NULL)
     {
-      /* determine the volume for the shortcut at the given tree iterator */
-      gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_VOLUME, &volume, -1);
-      if (G_UNLIKELY (volume != NULL))
+      /* distinguish between ejectable and unmountable mounts */
+      if (g_mount_can_eject (mount))
         {
-          /* prepare a mount operation */
-          window = gtk_widget_get_toplevel (GTK_WIDGET (view));
-          mount_operation = gtk_mount_operation_new (GTK_WINDOW (window));
+#ifdef HAVE_LIBNOTIFY
+          thunar_notify_unmount (mount);
+#endif
 
-          /* determine what the appropriate method is: eject or unmount */
-          if (g_volume_can_eject (volume))
-            {
+          /* try ejecting the mount */
+          g_mount_eject_with_operation (mount,
+                                        G_MOUNT_UNMOUNT_NONE,
+                                        mount_operation,
+                                        NULL,
+                                        thunar_shortcuts_view_eject_mount_finish,
+                                        g_object_ref (view));
+        }
+      else if (g_mount_can_unmount (mount))
+        {
 #ifdef HAVE_LIBNOTIFY
-              thunar_notify_eject (volume);
+          thunar_notify_unmount (mount);
 #endif
 
-              /* try to to eject the volume asynchronously */
-              g_volume_eject_with_operation (volume, G_MOUNT_UNMOUNT_NONE, mount_operation, NULL,
-                                             thunar_shortcuts_view_eject_finish,
-                                             g_object_ref (view));
-            }
-          else
+          /* try unmounting the mount */
+          g_mount_unmount_with_operation (mount,
+                                          G_MOUNT_UNMOUNT_NONE,
+                                          mount_operation,
+                                          NULL,
+                                          thunar_shortcuts_view_unmount_finish,
+                                          g_object_ref (view));
+        }
+
+      g_object_unref (mount);
+    }
+  else if (volume != NULL)
+    {
+      if (g_volume_can_eject (volume))
+        {
+#ifdef HAVE_LIBNOTIFY
+          thunar_notify_eject (volume);
+#endif
+          /* try ejecting the volume */
+          g_volume_eject_with_operation (volume,
+                                         G_MOUNT_UNMOUNT_NONE,
+                                         mount_operation,
+                                         NULL,
+                                         thunar_shortcuts_view_eject_volume_finish,
+                                         g_object_ref (view));
+        }
+      else
+        {
+          volume_mount = g_volume_get_mount (volume);
+          if (volume_mount != NULL)
             {
-              /* determine the mount of the volume */
-              mount = g_volume_get_mount (volume);
-              if (G_LIKELY (mount != NULL))
+              /* distinguish between ejectable and unmountable mounts */
+              if (g_mount_can_eject (volume_mount))
+                {
+#ifdef HAVE_LIBNOTIFY
+                  thunar_notify_unmount (volume_mount);
+#endif
+
+                  g_mount_eject_with_operation (mount,
+                                                G_MOUNT_UNMOUNT_NONE,
+                                                mount_operation,
+                                                NULL,
+                                                thunar_shortcuts_view_eject_mount_finish,
+                                                g_object_ref (view));
+                }
+              else if (g_mount_can_unmount (volume_mount))
                 {
 #ifdef HAVE_LIBNOTIFY
-                  thunar_notify_unmount (mount);
+                 thunar_notify_unmount (volume_mount);
 #endif
 
-                  /* the volume is mounted, try to unmount the mount */
-                  g_mount_unmount_with_operation (mount, G_MOUNT_UNMOUNT_NONE, mount_operation, NULL,
+                  /* try unmounting the mount */
+                  g_mount_unmount_with_operation (mount,
+                                                  G_MOUNT_UNMOUNT_NONE,
+                                                  mount_operation,
+                                                  NULL,
                                                   thunar_shortcuts_view_unmount_finish,
                                                   g_object_ref (view));
-
-                  /* release the mount */
-                  g_object_unref (mount);
                 }
-            }
 
-          /* cleanup */
-          g_object_unref (volume);
-          g_object_unref (mount_operation);
+              g_object_unref (volume_mount);
+            }
         }
+
+      g_object_unref (volume);
     }
 }
 
@@ -1612,7 +1801,7 @@ thunar_shortcuts_view_poke_volume_mount_finish (ThunarBrowser *browser,
   if (error != NULL)
     {
       volume_name = g_volume_get_name (volume);
-      thunar_dialogs_show_error (GTK_WIDGET (browser), error, 
+      thunar_dialogs_show_error (GTK_WIDGET (browser), error,
                                  _("Failed to mount \"%s\""), volume_name);
       g_free (volume_name);
     }
@@ -1641,8 +1830,8 @@ thunar_shortcuts_view_mount (ThunarShortcutsView *view)
   if (gtk_tree_selection_get_selected (selection, &model, &iter))
     {
       /* determine the file for the shortcut at the given tree iterator */
-      gtk_tree_model_get (model, &iter, 
-                          THUNAR_SHORTCUTS_MODEL_COLUMN_VOLUME, &volume, 
+      gtk_tree_model_get (model, &iter,
+                          THUNAR_SHORTCUTS_MODEL_COLUMN_VOLUME, &volume,
                           -1);
 
       if (G_LIKELY (volume != NULL))
@@ -1657,14 +1846,85 @@ thunar_shortcuts_view_mount (ThunarShortcutsView *view)
 
 
 
-static gboolean
-thunar_shortcuts_view_separator_func (GtkTreeModel *model,
-                                      GtkTreeIter  *iter,
-                                      gpointer      user_data)
+static void
+thunar_shortcuts_view_unmount (ThunarShortcutsView *view)
 {
-  gboolean separator;
-  gtk_tree_model_get (model, iter, THUNAR_SHORTCUTS_MODEL_COLUMN_SEPARATOR, &separator, -1);
-  return separator;
+  GtkTreeSelection *selection;
+  GtkTreeModel     *model;
+  GtkTreeIter       iter;
+  GVolume          *volume;
+  GMount           *mount;
+  GMount           *volume_mount;
+  GMountOperation  *mount_operation;
+  GtkWidget        *window;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
+
+  /* determine the selected item */
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+  if (gtk_tree_selection_get_selected (selection, &model, &iter))
+    {
+      /* determine the volume/mount for the shortcut at the given tree iterator */
+      gtk_tree_model_get (model, &iter,
+                          THUNAR_SHORTCUTS_MODEL_COLUMN_VOLUME, &volume,
+                          THUNAR_SHORTCUTS_MODEL_COLUMN_MOUNT, &mount, -1);
+
+      _thunar_return_if_fail (volume == NULL || G_IS_VOLUME (volume));
+      _thunar_return_if_fail (mount == NULL || G_IS_MOUNT (mount));
+
+      /* prepare a mount operation */
+      window = gtk_widget_get_toplevel (GTK_WIDGET (view));
+      mount_operation = gtk_mount_operation_new (GTK_WINDOW (window));
+
+      if (mount != NULL)
+        {
+          /* only handle mounts that can be unmounted here */
+          if (g_mount_can_unmount (mount))
+            {
+#ifdef HAVE_LIBNOTIFY
+              thunar_notify_unmount (mount);
+#endif
+
+              /* try unmounting the mount */
+              g_mount_unmount_with_operation (mount,
+                                              G_MOUNT_UNMOUNT_NONE,
+                                              mount_operation,
+                                              NULL,
+                                              thunar_shortcuts_view_unmount_finish,
+                                              g_object_ref (view));
+            }
+
+          g_object_unref (mount);
+        }
+      else if (volume != NULL)
+        {
+          volume_mount = g_volume_get_mount (volume);
+          if (volume_mount != NULL)
+            {
+              /* only handle mounts that can be unmounted here */
+              if (g_mount_can_unmount (volume_mount))
+                {
+#ifdef HAVE_LIBNOTIFY
+                  thunar_notify_unmount (volume_mount);
+#endif
+
+                  /* try unmounting the mount */
+                  g_mount_unmount_with_operation (volume_mount,
+                                                  G_MOUNT_UNMOUNT_NONE,
+                                                  mount_operation,
+                                                  NULL,
+                                                  thunar_shortcuts_view_unmount_finish,
+                                                  g_object_ref (view));
+                }
+
+              g_object_unref (volume_mount);
+            }
+
+          g_object_unref (volume);
+        }
+
+      g_object_unref (mount_operation);
+    }
 }
 
 


More information about the Xfce4-commits mailing list