[Xfce4-commits] [xfce/thunar] 37/46: Revert "Replace our own shortcuts pane with GtkPlacesSidebar"

noreply at xfce.org noreply at xfce.org
Tue Aug 15 02:35:45 CEST 2017


This is an automated email from the git hooks/post-receive script.

a   n   d   r   e       p   u   s   h   e   d       a       c   o   m   m   i   t       t   o       b   r   a   n   c   h       m   a   s   t   e   r   
   in repository xfce/thunar.

commit 6a57746a7d1d9652b9ab4a3cd766a6c627765dbf
Author: Vladimir Chren <vlado.chren at gmail.com>
Date:   Mon Jun 12 16:57:41 2017 +0200

    Revert "Replace our own shortcuts pane with GtkPlacesSidebar"
    
    This reverts commit e114704622505fde83f1fc26afe66ccf2e436e1c.
    
    Since XDG dirs in GtkPlacesSidebar are not removable, and there
    are too many of them, we switch back to old custom sidebar.
---
 po/POTFILES.in                  |    2 +
 thunar/Makefile.am              |    4 +
 thunar/thunar-location-entry.c  |    1 +
 thunar/thunar-shortcuts-model.c | 2263 +++++++++++++++++++++++++++++++++++++++
 thunar/thunar-shortcuts-model.h |  129 +++
 thunar/thunar-shortcuts-pane.c  |  391 +------
 thunar/thunar-shortcuts-view.c  | 2137 ++++++++++++++++++++++++++++++++++++
 thunar/thunar-shortcuts-view.h  |   46 +
 8 files changed, 4630 insertions(+), 343 deletions(-)

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4bc7e19..01b151c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -64,7 +64,9 @@ thunar/thunar-renamer-progress.c
 thunar/thunar-sendto-model.c
 thunar/thunar-session-client.c
 thunar/thunar-shortcuts-icon-renderer.c
+thunar/thunar-shortcuts-model.c
 thunar/thunar-shortcuts-pane.c
+thunar/thunar-shortcuts-view.c
 thunar/thunar-side-pane.c
 thunar/thunar-simple-job.c
 thunar/thunar-size-label.c
diff --git a/thunar/Makefile.am b/thunar/Makefile.am
index 98bcbb6..40188ad 100644
--- a/thunar/Makefile.am
+++ b/thunar/Makefile.am
@@ -176,8 +176,12 @@ thunar_SOURCES =							\
 	thunar-session-client.h						\
 	thunar-shortcuts-icon-renderer.c				\
 	thunar-shortcuts-icon-renderer.h				\
+	thunar-shortcuts-model.c					\
+	thunar-shortcuts-model.h					\
 	thunar-shortcuts-pane.c						\
 	thunar-shortcuts-pane.h						\
+	thunar-shortcuts-view.c						\
+	thunar-shortcuts-view.h						\
 	thunar-side-pane.c						\
 	thunar-side-pane.h						\
 	thunar-simple-job.c						\
diff --git a/thunar/thunar-location-entry.c b/thunar/thunar-location-entry.c
index c5902bf..48ce464 100644
--- a/thunar/thunar-location-entry.c
+++ b/thunar/thunar-location-entry.c
@@ -34,6 +34,7 @@
 #include <thunar/thunar-marshal.h>
 #include <thunar/thunar-path-entry.h>
 #include <thunar/thunar-private.h>
+#include <thunar/thunar-shortcuts-model.h>
 
 
 
diff --git a/thunar/thunar-shortcuts-model.c b/thunar/thunar-shortcuts-model.c
new file mode 100644
index 0000000..ef2a5d2
--- /dev/null
+++ b/thunar/thunar-shortcuts-model.c
@@ -0,0 +1,2263 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2005-2006 Benedikt Meurer <benny at xfce.org>
+ * Copyright (c) 2009 Jannis Pohlmann <jannis at xfce.org>
+ * Copyright (c) 2012      Nick Schermer <nick at xfce.org>
+ *
+ * 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 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 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 Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <thunar/thunar-file.h>
+#include <thunar/thunar-shortcuts-model.h>
+#include <thunar/thunar-device-monitor.h>
+#include <thunar/thunar-preferences.h>
+#include <thunar/thunar-util.h>
+#include <thunar/thunar-private.h>
+
+#define SPINNER_CYCLE_DURATION 1000
+#define SPINNER_NUM_STEPS      12
+
+
+
+#define THUNAR_SHORTCUT(obj) ((ThunarShortcut *) (obj))
+
+
+
+typedef struct _ThunarShortcut ThunarShortcut;
+
+
+
+enum
+{
+  PROP_0,
+  PROP_HIDDEN_BOOKMARKS,
+  PROP_FILE_SIZE_BINARY
+};
+
+
+
+static void               thunar_shortcuts_model_tree_model_init    (GtkTreeModelIface         *iface);
+static void               thunar_shortcuts_model_drag_source_init   (GtkTreeDragSourceIface    *iface);
+static void               thunar_shortcuts_model_get_property       (GObject                   *object,
+                                                                     guint                      prop_id,
+                                                                     GValue                    *value,
+                                                                     GParamSpec                *pspec);
+static void               thunar_shortcuts_model_set_property       (GObject                   *object,
+                                                                     guint                      prop_id,
+                                                                     const GValue              *value,
+                                                                     GParamSpec                *pspec);
+static void               thunar_shortcuts_model_finalize           (GObject                   *object);
+static GtkTreeModelFlags  thunar_shortcuts_model_get_flags          (GtkTreeModel              *tree_model);
+static gint               thunar_shortcuts_model_get_n_columns      (GtkTreeModel              *tree_model);
+static GType              thunar_shortcuts_model_get_column_type    (GtkTreeModel              *tree_model,
+                                                                     gint                       idx);
+static gboolean           thunar_shortcuts_model_get_iter           (GtkTreeModel              *tree_model,
+                                                                     GtkTreeIter               *iter,
+                                                                     GtkTreePath               *path);
+static GtkTreePath       *thunar_shortcuts_model_get_path           (GtkTreeModel              *tree_model,
+                                                                     GtkTreeIter               *iter);
+static void               thunar_shortcuts_model_get_value          (GtkTreeModel              *tree_model,
+                                                                     GtkTreeIter               *iter,
+                                                                     gint                       column,
+                                                                     GValue                    *value);
+static gboolean           thunar_shortcuts_model_iter_next          (GtkTreeModel              *tree_model,
+                                                                     GtkTreeIter               *iter);
+static gboolean           thunar_shortcuts_model_iter_children      (GtkTreeModel              *tree_model,
+                                                                     GtkTreeIter               *iter,
+                                                                     GtkTreeIter               *parent);
+static gboolean           thunar_shortcuts_model_iter_has_child     (GtkTreeModel              *tree_model,
+                                                                     GtkTreeIter               *iter);
+static gint               thunar_shortcuts_model_iter_n_children    (GtkTreeModel              *tree_model,
+                                                                     GtkTreeIter               *iter);
+static gboolean           thunar_shortcuts_model_iter_nth_child     (GtkTreeModel              *tree_model,
+                                                                     GtkTreeIter               *iter,
+                                                                     GtkTreeIter               *parent,
+                                                                     gint                       n);
+static gboolean           thunar_shortcuts_model_iter_parent        (GtkTreeModel              *tree_model,
+                                                                     GtkTreeIter               *iter,
+                                                                     GtkTreeIter               *child);
+static gboolean           thunar_shortcuts_model_row_draggable      (GtkTreeDragSource         *source,
+                                                                     GtkTreePath               *path);
+static gboolean           thunar_shortcuts_model_drag_data_get      (GtkTreeDragSource         *source,
+                                                                     GtkTreePath               *path,
+                                                                     GtkSelectionData          *selection_data);
+static gboolean           thunar_shortcuts_model_drag_data_delete   (GtkTreeDragSource         *source,
+                                                                     GtkTreePath               *path);
+static void               thunar_shortcuts_model_header_visibility  (ThunarShortcutsModel      *model);
+static void               thunar_shortcuts_model_shortcut_devices   (ThunarShortcutsModel      *model);
+static void               thunar_shortcuts_model_shortcut_places    (ThunarShortcutsModel      *model);
+static void               thunar_shortcuts_model_shortcut_network   (ThunarShortcutsModel      *model);
+static gboolean           thunar_shortcuts_model_get_hidden         (ThunarShortcutsModel      *model,
+                                                                     ThunarShortcut            *shortcut);
+static void               thunar_shortcuts_model_add_shortcut       (ThunarShortcutsModel      *model,
+                                                                     ThunarShortcut            *shortcut);
+static void               thunar_shortcuts_model_remove_shortcut    (ThunarShortcutsModel      *model,
+                                                                     ThunarShortcut            *shortcut);
+static gboolean           thunar_shortcuts_model_load               (gpointer                   data);
+static void               thunar_shortcuts_model_save               (ThunarShortcutsModel      *model);
+static void               thunar_shortcuts_model_monitor            (GFileMonitor              *monitor,
+                                                                     GFile                     *file,
+                                                                     GFile                     *other_file,
+                                                                     GFileMonitorEvent          event_type,
+                                                                     gpointer                   user_data);
+static void               thunar_shortcuts_model_file_changed       (ThunarFile                *file,
+                                                                     ThunarShortcutsModel      *model);
+static void               thunar_shortcuts_model_file_destroy       (ThunarFile                *file,
+                                                                     ThunarShortcutsModel      *model);
+static void               thunar_shortcuts_model_device_added       (ThunarDeviceMonitor       *device_monitor,
+                                                                     ThunarDevice              *device,
+                                                                     ThunarShortcutsModel      *model);
+static void               thunar_shortcuts_model_device_removed     (ThunarDeviceMonitor       *device_monitor,
+                                                                     ThunarDevice              *device,
+                                                                     ThunarShortcutsModel      *model);
+static void               thunar_shortcuts_model_device_changed     (ThunarDeviceMonitor       *device_monitor,
+                                                                     ThunarDevice              *device,
+                                                                     ThunarShortcutsModel      *model);
+
+static void               thunar_shortcut_free                      (ThunarShortcut            *shortcut,
+                                                                     ThunarShortcutsModel      *model);
+
+
+
+struct _ThunarShortcutsModelClass
+{
+  GObjectClass __parent__;
+};
+
+struct _ThunarShortcutsModel
+{
+  GObject         __parent__;
+
+  /* the model stamp is only used when debugging is
+   * enabled, to make sure we don't accept iterators
+   * generated by another model.
+   */
+#ifndef NDEBUG
+  gint                  stamp;
+#endif
+
+  GList                *shortcuts;
+
+  ThunarPreferences    *preferences;
+  gchar               **hidden_bookmarks;
+  gboolean              file_size_binary;
+
+  ThunarDeviceMonitor  *device_monitor;
+
+  gint64                bookmarks_time;
+  GFile                *bookmarks_file;
+  GFileMonitor         *bookmarks_monitor;
+  guint                 bookmarks_idle_id;
+
+  guint                 busy_timeout_id;
+};
+
+struct _ThunarShortcut
+{
+  ThunarShortcutGroup  group;
+
+  gchar               *name;
+  GIcon               *gicon;
+  gchar               *tooltip;
+  gint                 sort_id;
+
+  guint                busy : 1;
+  guint                busy_pulse;
+
+  GFile               *location;
+  ThunarFile          *file;
+  ThunarDevice        *device;
+
+  guint                hidden : 1;
+};
+
+
+
+G_DEFINE_TYPE_WITH_CODE (ThunarShortcutsModel, thunar_shortcuts_model, G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, thunar_shortcuts_model_tree_model_init)
+    G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, thunar_shortcuts_model_drag_source_init))
+
+
+
+static void
+thunar_shortcuts_model_class_init (ThunarShortcutsModelClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = thunar_shortcuts_model_finalize;
+  gobject_class->get_property = thunar_shortcuts_model_get_property;
+  gobject_class->set_property = thunar_shortcuts_model_set_property;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_HIDDEN_BOOKMARKS,
+                                   g_param_spec_boxed ("hidden-bookmarks",
+                                                       NULL,
+                                                       NULL,
+                                                       G_TYPE_STRV,
+                                                       EXO_PARAM_READWRITE));
+
+  /**
+   * ThunarPropertiesDialog:file_size_binary:
+   *
+   * Whether the file size should be shown in binary or decimal.
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_FILE_SIZE_BINARY,
+                                   g_param_spec_boolean ("file-size-binary",
+                                                         "FileSizeBinary",
+                                                         NULL,
+                                                         FALSE,
+                                                         EXO_PARAM_READWRITE));
+}
+
+
+
+static void
+thunar_shortcuts_model_tree_model_init (GtkTreeModelIface *iface)
+{
+  iface->get_flags = thunar_shortcuts_model_get_flags;
+  iface->get_n_columns = thunar_shortcuts_model_get_n_columns;
+  iface->get_column_type = thunar_shortcuts_model_get_column_type;
+  iface->get_iter = thunar_shortcuts_model_get_iter;
+  iface->get_path = thunar_shortcuts_model_get_path;
+  iface->get_value = thunar_shortcuts_model_get_value;
+  iface->iter_next = thunar_shortcuts_model_iter_next;
+  iface->iter_children = thunar_shortcuts_model_iter_children;
+  iface->iter_has_child = thunar_shortcuts_model_iter_has_child;
+  iface->iter_n_children = thunar_shortcuts_model_iter_n_children;
+  iface->iter_nth_child = thunar_shortcuts_model_iter_nth_child;
+  iface->iter_parent = thunar_shortcuts_model_iter_parent;
+}
+
+
+
+static void
+thunar_shortcuts_model_drag_source_init (GtkTreeDragSourceIface *iface)
+{
+  iface->row_draggable = thunar_shortcuts_model_row_draggable;
+  iface->drag_data_get = thunar_shortcuts_model_drag_data_get;
+  iface->drag_data_delete = thunar_shortcuts_model_drag_data_delete;
+}
+
+
+
+static void
+thunar_shortcuts_model_init (ThunarShortcutsModel *model)
+{
+#ifndef NDEBUG
+  model->stamp = g_random_int ();
+#endif
+
+  /* hidden bookmarks */
+  model->preferences = thunar_preferences_get ();
+  exo_binding_new (G_OBJECT (model->preferences), "hidden-bookmarks",
+                   G_OBJECT (model), "hidden-bookmarks");
+
+  /* binary file size */
+  exo_binding_new (G_OBJECT (model->preferences), "misc-file-size-binary",
+                   G_OBJECT (model), "file-size-binary");
+
+  /* load volumes */
+  thunar_shortcuts_model_shortcut_devices (model);
+
+  /* add network */
+  thunar_shortcuts_model_shortcut_network (model);
+
+  /* add bookmarks */
+  thunar_shortcuts_model_shortcut_places (model);
+}
+
+
+
+static void
+thunar_shortcuts_model_finalize (GObject *object)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (object);
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+
+  /* stop the busy timeout */
+  if (model->busy_timeout_id != 0)
+    g_source_remove (model->busy_timeout_id);
+
+  /* stop bookmark load idle */
+  if (model->bookmarks_idle_id != 0)
+    g_source_remove (model->bookmarks_idle_id);
+
+  /* free all shortcuts */
+  g_list_foreach (model->shortcuts, (GFunc) thunar_shortcut_free, model);
+  g_list_free (model->shortcuts);
+
+  /* disconnect from the preferences */
+  g_object_unref (model->preferences);
+
+  /* free hidden list */
+  g_strfreev (model->hidden_bookmarks);
+
+  /* detach from the file monitor */
+  if (model->bookmarks_monitor != NULL)
+    {
+      g_file_monitor_cancel (model->bookmarks_monitor);
+      g_object_unref (model->bookmarks_monitor);
+    }
+
+  if (model->bookmarks_file != NULL)
+    g_object_unref (model->bookmarks_file);
+
+  /* unlink from the device monitor */
+  g_signal_handlers_disconnect_matched (model->device_monitor, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, model);
+  g_object_unref (model->device_monitor);
+
+  (*G_OBJECT_CLASS (thunar_shortcuts_model_parent_class)->finalize) (object);
+}
+
+
+
+static void
+thunar_shortcuts_model_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (object);
+
+  switch (prop_id)
+    {
+    case PROP_HIDDEN_BOOKMARKS:
+      g_value_set_boxed (value, model->hidden_bookmarks);
+      break;
+
+    case PROP_FILE_SIZE_BINARY:
+      g_value_set_boolean (value, model->file_size_binary);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static void
+thunar_shortcuts_model_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (object);
+  GList                *lp;
+  ThunarShortcut       *shortcut;
+  gboolean              hidden;
+  GtkTreeIter           iter;
+  GtkTreePath          *path;
+  guint                 idx;
+
+  switch (prop_id)
+    {
+    case PROP_HIDDEN_BOOKMARKS:
+      g_strfreev (model->hidden_bookmarks);
+      model->hidden_bookmarks = g_value_dup_boxed (value);
+
+      /* update shortcuts */
+      for (lp = model->shortcuts, idx = 0; lp != NULL; lp = lp->next, idx++)
+        {
+          shortcut = lp->data;
+
+          /* skip devices and headers*/
+          if (shortcut->device != NULL
+              || (shortcut->file == NULL && shortcut->location == NULL))
+            continue;
+
+          /* update state if required */
+          hidden = thunar_shortcuts_model_get_hidden (model, shortcut);
+          if (shortcut->hidden != hidden)
+            {
+              shortcut->hidden = hidden;
+
+              GTK_TREE_ITER_INIT (iter, model->stamp, lp);
+
+              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);
+            }
+        }
+
+      /* update header visibility */
+      thunar_shortcuts_model_header_visibility (model);
+      break;
+
+    case PROP_FILE_SIZE_BINARY:
+      model->file_size_binary = g_value_get_boolean (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static GtkTreeModelFlags
+thunar_shortcuts_model_get_flags (GtkTreeModel *tree_model)
+{
+  return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
+}
+
+
+
+static gint
+thunar_shortcuts_model_get_n_columns (GtkTreeModel *tree_model)
+{
+  return THUNAR_SHORTCUTS_MODEL_N_COLUMNS;
+}
+
+
+
+static GType
+thunar_shortcuts_model_get_column_type (GtkTreeModel *tree_model,
+                                        gint          idx)
+{
+  switch (idx)
+    {
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_IS_HEADER:
+      return G_TYPE_BOOLEAN;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_IS_ITEM:
+      return G_TYPE_BOOLEAN;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_VISIBLE:
+      return G_TYPE_BOOLEAN;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_NAME:
+      return G_TYPE_STRING;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_TOOLTIP:
+      return G_TYPE_STRING;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_FILE:
+      return THUNAR_TYPE_FILE;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_LOCATION:
+      return G_TYPE_FILE;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_GICON:
+      return G_TYPE_ICON;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE:
+      return THUNAR_TYPE_DEVICE;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_MUTABLE:
+      return G_TYPE_BOOLEAN;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_CAN_EJECT:
+      return G_TYPE_BOOLEAN;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_GROUP:
+      return G_TYPE_UINT;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY:
+      return G_TYPE_BOOLEAN;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY_PULSE:
+      return G_TYPE_UINT;
+    }
+
+  _thunar_assert_not_reached ();
+  return G_TYPE_INVALID;
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_get_iter (GtkTreeModel *tree_model,
+                                 GtkTreeIter  *iter,
+                                 GtkTreePath  *path)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (tree_model);
+  GList                 *lp;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
+  _thunar_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
+
+  /* determine the list item for the path */
+  lp = g_list_nth (model->shortcuts, gtk_tree_path_get_indices (path)[0]);
+  if (G_LIKELY (lp != NULL))
+    {
+      GTK_TREE_ITER_INIT (*iter, model->stamp, lp);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+
+static GtkTreePath*
+thunar_shortcuts_model_get_path (GtkTreeModel *tree_model,
+                                 GtkTreeIter  *iter)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (tree_model);
+  gint                  idx;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), NULL);
+  _thunar_return_val_if_fail (iter->stamp == model->stamp, NULL);
+
+  /* lookup the list item in the shortcuts list */
+  idx = g_list_position (model->shortcuts, iter->user_data);
+  if (G_LIKELY (idx >= 0))
+    return gtk_tree_path_new_from_indices (idx, -1);
+
+  return NULL;
+}
+
+
+
+static void
+thunar_shortcuts_model_get_value (GtkTreeModel *tree_model,
+                                  GtkTreeIter  *iter,
+                                  gint          column,
+                                  GValue       *value)
+{
+  ThunarShortcut *shortcut;
+  gboolean        can_eject;
+  GFile          *file;
+  gchar          *disk_usage;
+  guint32         trash_items;
+  gchar          *trash_string;
+  gchar          *parse_name;
+  gboolean        file_size_binary;
+
+  _thunar_return_if_fail (iter->stamp == THUNAR_SHORTCUTS_MODEL (tree_model)->stamp);
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (tree_model));
+
+  /* determine the shortcut for the list item */
+  shortcut = THUNAR_SHORTCUT (((GList *) iter->user_data)->data);
+  if (shortcut == NULL)
+    return;
+
+  switch (column)
+    {
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_IS_HEADER:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value, (shortcut->group & THUNAR_SHORTCUT_GROUP_HEADER) != 0);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_IS_ITEM:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value, (shortcut->group & THUNAR_SHORTCUT_GROUP_HEADER) == 0);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_VISIBLE:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value, !shortcut->hidden);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_NAME:
+      g_value_init (value, G_TYPE_STRING);
+      if (G_UNLIKELY (shortcut->device != NULL))
+        g_value_take_string (value, thunar_device_get_name (shortcut->device));
+      else if (shortcut->name != NULL)
+        g_value_set_static_string (value, shortcut->name);
+      else if (shortcut->file != NULL)
+        g_value_set_static_string (value, thunar_file_get_display_name (shortcut->file));
+      else if (shortcut->location != NULL)
+        g_value_take_string (value, thunar_g_file_get_display_name_remote (shortcut->location));
+      else
+        g_value_set_static_string (value, "");
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_TOOLTIP:
+      g_value_init (value, G_TYPE_STRING);
+      if ((shortcut->group & THUNAR_SHORTCUT_GROUP_DEVICES) != 0)
+        {
+          if (shortcut->device != NULL)
+            file = thunar_device_get_root (shortcut->device);
+          else if (shortcut->file != NULL)
+            file = g_object_ref (thunar_file_get_file (shortcut->file));
+          else
+            file = NULL;
+
+          if (file != NULL)
+            {
+              file_size_binary = THUNAR_SHORTCUTS_MODEL (tree_model)->file_size_binary;
+              disk_usage = thunar_g_file_get_free_space_string (file, file_size_binary);
+              g_object_unref (file);
+              g_value_take_string (value, disk_usage);
+            }
+          break;
+        }
+      else if ((shortcut->group & THUNAR_SHORTCUT_GROUP_PLACES_TRASH) != 0)
+        {
+          trash_items = thunar_file_get_item_count (shortcut->file);
+          if (trash_items == 0)
+            {
+              g_value_set_static_string (value, _("Trash is empty"));
+            }
+          else
+            {
+              trash_string = g_strdup_printf (ngettext ("Trash contains %d file",
+                                                        "Trash contains %d files",
+                                                        trash_items), trash_items);
+              g_value_take_string (value, trash_string);
+            }
+          break;
+        }
+      else if (shortcut->tooltip == NULL)
+        {
+          if (shortcut->file != NULL)
+            file = thunar_file_get_file (shortcut->file);
+          else if (shortcut->location != NULL)
+            file = shortcut->location;
+          else
+            file = NULL;
+
+          if (G_LIKELY (file != NULL))
+            {
+              parse_name = g_file_get_parse_name (file);
+              shortcut->tooltip = g_markup_escape_text (parse_name, -1);
+              g_free (parse_name);
+            }
+        }
+
+      g_value_set_static_string (value, shortcut->tooltip);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_FILE:
+      g_value_init (value, THUNAR_TYPE_FILE);
+      g_value_set_object (value, shortcut->file);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_GICON:
+      g_value_init (value, G_TYPE_ICON);
+      g_value_set_object (value, shortcut->gicon);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE:
+      g_value_init (value, THUNAR_TYPE_DEVICE);
+      g_value_set_object (value, shortcut->device);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_LOCATION:
+      g_value_init (value, G_TYPE_FILE);
+      if (shortcut->location != NULL)
+        g_value_set_object (value, shortcut->location);
+      else if (shortcut->file != NULL)
+        g_value_set_object (value, thunar_file_get_file (shortcut->file));
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_MUTABLE:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value,
+                           shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_CAN_EJECT:
+      if (shortcut->device != NULL)
+        can_eject = thunar_device_can_eject (shortcut->device);
+      else
+        can_eject = FALSE;
+
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value, can_eject);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_GROUP:
+      g_value_init (value, G_TYPE_UINT);
+      g_value_set_uint (value, shortcut->group);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value, shortcut->busy);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY_PULSE:
+      g_value_init (value, G_TYPE_UINT);
+      g_value_set_uint (value, shortcut->busy_pulse);
+      break;
+
+    default:
+      _thunar_assert_not_reached ();
+    }
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_iter_next (GtkTreeModel *tree_model,
+                                  GtkTreeIter  *iter)
+{
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (tree_model), FALSE);
+  _thunar_return_val_if_fail (iter->stamp == THUNAR_SHORTCUTS_MODEL (tree_model)->stamp, FALSE);
+
+  iter->user_data = g_list_next (iter->user_data);
+  return (iter->user_data != NULL);
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_iter_children (GtkTreeModel *tree_model,
+                                      GtkTreeIter  *iter,
+                                      GtkTreeIter  *parent)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (tree_model);
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
+
+  if (G_LIKELY (parent == NULL && model->shortcuts != NULL))
+    {
+      GTK_TREE_ITER_INIT (*iter, model->stamp, model->shortcuts);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_iter_has_child (GtkTreeModel *tree_model,
+                                       GtkTreeIter  *iter)
+{
+  return FALSE;
+}
+
+
+
+static gint
+thunar_shortcuts_model_iter_n_children (GtkTreeModel *tree_model,
+                                        GtkTreeIter  *iter)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (tree_model);
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), 0);
+
+  return (iter == NULL) ? g_list_length (model->shortcuts) : 0;
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_iter_nth_child (GtkTreeModel *tree_model,
+                                       GtkTreeIter  *iter,
+                                       GtkTreeIter  *parent,
+                                       gint          n)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (tree_model);
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
+
+  if (G_LIKELY (parent == NULL))
+    {
+      GTK_TREE_ITER_INIT (*iter, model->stamp, g_list_nth (model->shortcuts, n));
+      return (iter->user_data != NULL);
+    }
+
+  return FALSE;
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_iter_parent (GtkTreeModel *tree_model,
+                                    GtkTreeIter  *iter,
+                                    GtkTreeIter  *child)
+{
+  return FALSE;
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_row_draggable (GtkTreeDragSource *source,
+                                      GtkTreePath       *path)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (source);
+  ThunarShortcut       *shortcut;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
+  _thunar_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
+
+  /* lookup the ThunarShortcut for the path */
+  shortcut = g_list_nth_data (model->shortcuts, gtk_tree_path_get_indices (path)[0]);
+
+  /* special shortcuts cannot be reordered */
+  return (shortcut != NULL && shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS);
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_drag_data_get (GtkTreeDragSource *source,
+                                      GtkTreePath       *path,
+                                      GtkSelectionData  *selection_data)
+{
+  /* we simply return FALSE here, as the drag handling is done in
+   * the ThunarShortcutsView class.
+   */
+  return FALSE;
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_drag_data_delete (GtkTreeDragSource *source,
+                                         GtkTreePath       *path)
+{
+  /* we simply return FALSE here, as this function can only be
+   * called if the user is re-arranging shortcuts within the
+   * model, which will be handle by the exchange method.
+   */
+  return FALSE;
+}
+
+
+
+static void
+thunar_shortcuts_model_header_visibility (ThunarShortcutsModel *model)
+{
+  GList          *lp;
+  ThunarShortcut *shortcut;
+  ThunarShortcut *header;
+  guint           n_children = 0;
+  GList          *lp_header = NULL;
+  gint            idx_header = 0;
+  gint            i;
+  GtkTreePath    *path;
+  GtkTreeIter     iter;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+
+  for (lp = model->shortcuts, i = 0; lp != NULL; lp = lp->next, i++)
+    {
+      shortcut = lp->data;
+
+      if ((shortcut->group & THUNAR_SHORTCUT_GROUP_HEADER) != 0)
+        {
+          if (lp_header != NULL)
+            {
+              update_header:
+
+              header = lp_header->data;
+              if (header->hidden != (n_children == 0))
+                {
+                  /* set new visibility */
+                  header->hidden = (n_children == 0);
+
+                  /* notify view */
+                  path = gtk_tree_path_new_from_indices (idx_header, -1);
+                  GTK_TREE_ITER_INIT (iter, model->stamp, lp_header);
+                  gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
+                  gtk_tree_path_free (path);
+                }
+
+              if (lp == NULL)
+                return;
+            }
+
+          /* reset for new */
+          idx_header = i;
+          lp_header = lp;
+          n_children = 0;
+        }
+      else if (!shortcut->hidden)
+        {
+          n_children++;
+        }
+    }
+
+  _thunar_assert (lp == NULL);
+  if (lp_header != NULL)
+    goto update_header;
+}
+
+
+
+static void
+thunar_shortcuts_model_shortcut_devices (ThunarShortcutsModel *model)
+{
+  ThunarShortcut *shortcut;
+  GList          *devices;
+  GList          *lp;
+
+  /* add the devices heading */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->group = THUNAR_SHORTCUT_GROUP_DEVICES_HEADER;
+  shortcut->name = g_strdup (_("DEVICES"));
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
+
+  /* the filesystem entry */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->group = THUNAR_SHORTCUT_GROUP_DEVICES_FILESYSTEM;
+  shortcut->name = g_strdup (_("File System"));
+  shortcut->file = thunar_file_get_for_uri ("file:///", NULL);
+  shortcut->gicon = g_themed_icon_new ("drive-harddisk");
+  shortcut->hidden = thunar_shortcuts_model_get_hidden (model, shortcut);
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
+
+  /* connect to the device monitor */
+  model->device_monitor = thunar_device_monitor_get ();
+
+  /* get a list of all devices available */
+  devices = thunar_device_monitor_get_devices (model->device_monitor);
+  for (lp = devices; lp != NULL; lp = lp->next)
+    {
+      thunar_shortcuts_model_device_added (NULL, lp->data, model);
+      g_object_unref (G_OBJECT (lp->data));
+    }
+  g_list_free (devices);
+
+  /* monitor for changes */
+  g_signal_connect (model->device_monitor, "device-added", G_CALLBACK (thunar_shortcuts_model_device_added), model);
+  g_signal_connect (model->device_monitor, "device-removed", G_CALLBACK (thunar_shortcuts_model_device_removed), model);
+  g_signal_connect (model->device_monitor, "device-changed", G_CALLBACK (thunar_shortcuts_model_device_changed), model);
+
+  thunar_shortcuts_model_header_visibility (model);
+}
+
+
+
+static void
+thunar_shortcuts_model_shortcut_network (ThunarShortcutsModel *model)
+{
+  ThunarShortcut *shortcut;
+
+  /* add the network heading */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->group = THUNAR_SHORTCUT_GROUP_NETWORK_HEADER;
+  shortcut->name = g_strdup (_("NETWORK"));
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
+
+  /* the browse network entry */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->group = THUNAR_SHORTCUT_GROUP_NETWORK_DEFAULT;
+  shortcut->name = g_strdup (_("Browse Network"));
+  shortcut->location = g_file_new_for_uri ("network://");
+  shortcut->gicon = g_themed_icon_new ("network-workgroup");
+  shortcut->hidden = thunar_shortcuts_model_get_hidden (model, shortcut);
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
+}
+
+
+
+static void
+thunar_shortcuts_model_shortcut_places (ThunarShortcutsModel *model)
+{
+  ThunarShortcut *shortcut;
+  GFile          *home;
+  GFile          *desktop;
+  GFile          *trash;
+  ThunarFile     *file;
+
+  /* add the places heading */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES_HEADER;
+  shortcut->name = g_strdup (_("PLACES"));
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
+
+  /* get home path */
+  home = thunar_g_file_new_for_home ();
+
+  /* add home entry */
+  file = thunar_file_get (home, NULL);
+  if (file != NULL)
+    {
+      shortcut = g_slice_new0 (ThunarShortcut);
+      shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES_DEFAULT;
+      shortcut->file = file;
+      shortcut->gicon = g_themed_icon_new ("go-home");
+      shortcut->sort_id = 0;
+      shortcut->hidden = thunar_shortcuts_model_get_hidden (model, shortcut);
+      thunar_shortcuts_model_add_shortcut (model, shortcut);
+    }
+
+  /* add desktop entry */
+  desktop = thunar_g_file_new_for_desktop ();
+  if (!g_file_equal (desktop, home))
+    {
+      file = thunar_file_get (desktop, NULL);
+      if (file != NULL)
+        {
+          shortcut = g_slice_new0 (ThunarShortcut);
+          shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES_DEFAULT;
+          shortcut->file = file;
+          shortcut->sort_id =  1;
+          shortcut->hidden = thunar_shortcuts_model_get_hidden (model, shortcut);
+          thunar_shortcuts_model_add_shortcut (model, shortcut);
+        }
+    }
+  g_object_unref (desktop);
+  g_object_unref (home);
+
+  /* append the trash icon if the trash is supported */
+  if (thunar_g_vfs_is_uri_scheme_supported ("trash"))
+    {
+      trash = thunar_g_file_new_for_trash ();
+      file = thunar_file_get (trash, NULL);
+      g_object_unref (trash);
+
+      if (file != NULL)
+        {
+          shortcut = g_slice_new0 (ThunarShortcut);
+          shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES_TRASH;
+          shortcut->file = file;
+          shortcut->hidden = thunar_shortcuts_model_get_hidden (model, shortcut);
+          thunar_shortcuts_model_add_shortcut (model, shortcut);
+        }
+    }
+
+  /* determine the URI to the Gtk+ bookmarks file */
+  model->bookmarks_file = thunar_g_file_new_for_bookmarks ();
+
+  /* register with the alteration monitor for the bookmarks file */
+  model->bookmarks_monitor = g_file_monitor_file (model->bookmarks_file, G_FILE_MONITOR_NONE, NULL, NULL);
+  if (G_LIKELY (model->bookmarks_monitor != NULL))
+    {
+      g_signal_connect (model->bookmarks_monitor, "changed",
+        G_CALLBACK (thunar_shortcuts_model_monitor), model);
+    }
+
+  /* read the Gtk+ bookmarks file */
+  model->bookmarks_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT, thunar_shortcuts_model_load, model, NULL);
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_get_hidden (ThunarShortcutsModel *model,
+                                   ThunarShortcut       *shortcut)
+{
+  gchar    *uri;
+  guint     n;
+  gboolean  hidden = FALSE;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
+  _thunar_return_val_if_fail (shortcut->device == NULL, FALSE);
+
+  if (model->hidden_bookmarks == NULL)
+    return FALSE;
+
+  /* get the uri */
+  if (shortcut->file != NULL)
+    uri = thunar_file_dup_uri (shortcut->file);
+  else if (shortcut->location != NULL)
+    uri = g_file_get_uri (shortcut->location);
+  else
+    _thunar_assert_not_reached ();
+
+  if (uri == NULL)
+    return FALSE;
+
+  for (n = 0; !hidden && model->hidden_bookmarks[n] != NULL; n++)
+    hidden = (g_strcmp0 (model->hidden_bookmarks[n], uri) == 0);
+
+  g_free (uri);
+
+  return hidden;
+}
+
+
+
+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;
+
+  /* use sort order */
+  if (a->sort_id != b->sort_id)
+    return a->sort_id > b->sort_id ? 1 : -1;
+
+  /* properly sort devices by timestamp */
+  if (a->device != NULL && b->device != NULL)
+    return thunar_device_sort (a->device, b->device);
+
+  return g_strcmp0 (a->name, b->name);
+}
+
+
+
+static void
+thunar_shortcuts_model_add_shortcut_with_path (ThunarShortcutsModel *model,
+                                               ThunarShortcut       *shortcut,
+                                               GtkTreePath          *path)
+{
+  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));
+
+  /* we want to stay informed about changes to the file */
+  if (G_LIKELY (shortcut->file != NULL))
+    {
+      /* watch the file for changes */
+      thunar_file_watch (shortcut->file);
+
+      /* connect appropriate signals */
+      g_signal_connect (G_OBJECT (shortcut->file), "changed",
+                        G_CALLBACK (thunar_shortcuts_model_file_changed), model);
+      g_signal_connect (G_OBJECT (shortcut->file), "destroy",
+                        G_CALLBACK (thunar_shortcuts_model_file_destroy), model);
+    }
+
+  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);
+}
+
+
+
+static void
+thunar_shortcuts_model_remove_shortcut (ThunarShortcutsModel *model,
+                                        ThunarShortcut       *shortcut)
+{
+  GtkTreePath *path;
+  gint         idx;
+  gboolean     needs_save;
+
+  /* determine the index of the shortcut */
+  idx = g_list_index (model->shortcuts, shortcut);
+  if (G_LIKELY (idx >= 0))
+    {
+      /* 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);
+      gtk_tree_path_free (path);
+
+      /* check if we need to save */
+      needs_save = (shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS);
+
+      /* actually free the shortcut */
+      thunar_shortcut_free (shortcut, model);
+
+      /* the shortcuts list was changed, so write the gtk bookmarks file */
+      if (needs_save)
+        thunar_shortcuts_model_save (model);
+
+      /* update header visibility */
+      thunar_shortcuts_model_header_visibility (model);
+    }
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_local_file (GFile *gfile)
+{
+  _thunar_return_val_if_fail (G_IS_FILE (gfile), FALSE);
+
+  /* schemes we'd like to have as ThunarFiles in the model */
+  if (g_file_has_uri_scheme (gfile, "file")
+      || g_file_has_uri_scheme (gfile, "computer")
+      || g_file_has_uri_scheme (gfile, "recent"))
+    return TRUE;
+
+  return FALSE;
+}
+
+
+
+static void
+thunar_shortcuts_model_load_line (GFile       *file_path,
+                                  const gchar *name,
+                                  gint         row_num,
+                                  gpointer     user_data)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (user_data);
+  ThunarShortcut       *shortcut;
+  ThunarFile           *file;
+
+  _thunar_return_if_fail (G_IS_FILE (file_path));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+  _thunar_return_if_fail (name == NULL || g_utf8_validate (name, -1, NULL));
+
+  /* handle local and remove files differently */
+  if (thunar_shortcuts_model_local_file (file_path))
+    {
+      /* try to open the file corresponding to the uri */
+      file = thunar_file_get (file_path, NULL);
+      if (G_UNLIKELY (file == NULL))
+        return;
+
+      /* 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_PLACES_BOOKMARKS;
+          shortcut->file = file;
+          shortcut->sort_id = row_num;
+          shortcut->hidden = thunar_shortcuts_model_get_hidden (model, shortcut);
+          shortcut->name = g_strdup (name);
+
+          /* 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->group = THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS;
+      shortcut->gicon = g_themed_icon_new ("folder-remote");
+      shortcut->location = g_object_ref (file_path);
+      shortcut->sort_id = row_num;
+      shortcut->hidden = thunar_shortcuts_model_get_hidden (model, shortcut);
+      shortcut->name = g_strdup (name);
+
+      /* append the shortcut to the list */
+      thunar_shortcuts_model_add_shortcut (model, shortcut);
+    }
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_load (gpointer data)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (data);
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
+
+  GDK_THREADS_ENTER ();
+
+  /* parse the bookmarks */
+  thunar_util_load_bookmarks (model->bookmarks_file,
+                              thunar_shortcuts_model_load_line,
+                              model);
+
+  /* update the visibility */
+  thunar_shortcuts_model_header_visibility (model);
+
+  GDK_THREADS_LEAVE ();
+
+  model->bookmarks_idle_id = 0;
+
+  return FALSE;
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_reload (gpointer data)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (data);
+  ThunarShortcut       *shortcut;
+  GtkTreePath          *path;
+  GList                *lp;
+  gint                  idx;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
+
+  GDK_THREADS_ENTER ();
+
+  /* drop all existing user-defined shortcuts from the model */
+  for (idx = 0, lp = model->shortcuts; lp != NULL; )
+    {
+      /* grab the shortcut */
+      shortcut = THUNAR_SHORTCUT (lp->data);
+
+      /* advance to the next list item */
+      lp = g_list_next (lp);
+
+      /* drop the shortcut if it is user-defined */
+      if (shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_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);
+          gtk_tree_path_free (path);
+
+          /* actually free the shortcut */
+          thunar_shortcut_free (shortcut, model);
+        }
+      else
+        {
+          ++idx;
+        }
+    }
+
+  GDK_THREADS_LEAVE ();
+
+  /* load new bookmarks */
+  return thunar_shortcuts_model_load (data);
+}
+
+
+
+static void
+thunar_shortcuts_model_monitor (GFileMonitor     *monitor,
+                                GFile            *file,
+                                GFile            *other_file,
+                                GFileMonitorEvent event_type,
+                                gpointer          user_data)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (user_data);
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+  _thunar_return_if_fail (model->bookmarks_monitor == monitor);
+
+  /* leave if we saved less than 2 seconds ago */
+  if (model->bookmarks_time + 2 * G_USEC_PER_SEC > g_get_real_time ())
+    return;
+
+  /* reload the shortcuts model */
+  if (model->bookmarks_idle_id == 0)
+    model->bookmarks_idle_id = g_idle_add (thunar_shortcuts_model_reload, model);
+}
+
+
+
+static void
+thunar_shortcuts_model_save (ThunarShortcutsModel *model)
+{
+  GString        *contents;
+  ThunarShortcut *shortcut;
+  gchar          *bookmarks_path;
+  gchar          *uri;
+  GList          *lp;
+  GError         *err = NULL;
+  GFile          *parent = NULL;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+
+  contents = g_string_new (NULL);
+
+  for (lp = model->shortcuts; lp != NULL; lp = lp->next)
+    {
+      shortcut = THUNAR_SHORTCUT (lp->data);
+      if (shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS)
+        {
+          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))
+            g_string_append_printf (contents, "%s %s\n", uri, shortcut->name);
+          else
+            g_string_append_printf (contents, "%s\n", uri);
+          g_free (uri);
+        }
+    }
+
+  /* create folder if it does not exist */
+  parent = g_file_get_parent (model->bookmarks_file);
+  if (!g_file_make_directory_with_parents (parent, NULL, &err))
+    {
+       if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_EXISTS))
+         {
+           g_clear_error (&err);
+         }
+       else
+         {
+           g_warning ("Failed to create bookmarks folder: %s", err->message);
+           g_error_free (err);
+         }
+    }
+  g_clear_object (&parent);
+
+  /* write data to the disk */
+  bookmarks_path = g_file_get_path (model->bookmarks_file);
+  if (!g_file_set_contents (bookmarks_path, contents->str, contents->len, &err))
+    {
+      g_warning ("Failed to write \"%s\": %s", bookmarks_path, err->message);
+      g_error_free (err);
+    }
+  g_free (bookmarks_path);
+  g_string_free (contents, TRUE);
+
+  /* store the save time */
+  model->bookmarks_time = g_get_real_time ();
+}
+
+
+
+static void
+thunar_shortcuts_model_file_changed (ThunarFile           *file,
+                                     ThunarShortcutsModel *model)
+{
+  ThunarShortcut *shortcut;
+  GtkTreePath    *path;
+  GtkTreeIter     iter;
+  GList          *lp;
+  gint            idx;
+
+  _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
+   * it like the file was destroyed (and thereby removed) */
+
+  if (G_UNLIKELY (!thunar_file_is_directory (file)))
+    {
+      thunar_shortcuts_model_file_destroy (file, model);
+      return;
+    }
+
+  for (idx = 0, lp = model->shortcuts; lp != NULL; ++idx, lp = lp->next)
+    {
+      shortcut = THUNAR_SHORTCUT (lp->data);
+      if (shortcut->file == file)
+        {
+          GTK_TREE_ITER_INIT (iter, model->stamp, lp);
+
+          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);
+          break;
+        }
+    }
+}
+
+
+
+static void
+thunar_shortcuts_model_file_destroy (ThunarFile           *file,
+                                     ThunarShortcutsModel *model)
+{
+  ThunarShortcut *shortcut = NULL;
+  GList          *lp;
+
+  _thunar_return_if_fail (THUNAR_IS_FILE (file));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+
+  /* lookup the shortcut matching the file */
+  for (lp = model->shortcuts; lp != NULL; lp = lp->next)
+    {
+      shortcut = THUNAR_SHORTCUT (lp->data);
+      if (shortcut->file == file)
+        break;
+    }
+
+  /* verify that we actually found a shortcut */
+  _thunar_assert (lp != NULL);
+  _thunar_assert (THUNAR_IS_FILE (shortcut->file));
+
+  /* drop the shortcut from the model */
+  thunar_shortcuts_model_remove_shortcut (model, shortcut);
+}
+
+
+
+static void
+thunar_shortcuts_model_device_added (ThunarDeviceMonitor  *device_monitor,
+                                     ThunarDevice         *device,
+                                     ThunarShortcutsModel *model)
+{
+  ThunarShortcut *shortcut;
+
+  _thunar_return_if_fail (device_monitor == NULL || THUNAR_DEVICE_MONITOR (device_monitor));
+  _thunar_return_if_fail (device_monitor == NULL || model->device_monitor == device_monitor);
+  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+
+  /* allocate a new shortcut */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->device = g_object_ref (device);
+  shortcut->hidden = thunar_device_get_hidden (device);
+
+  switch (thunar_device_get_kind (device))
+    {
+    case THUNAR_DEVICE_KIND_VOLUME:
+      shortcut->group = THUNAR_SHORTCUT_GROUP_DEVICES_VOLUMES;
+      break;
+
+    case THUNAR_DEVICE_KIND_MOUNT_LOCAL:
+      shortcut->group = THUNAR_SHORTCUT_GROUP_DEVICES_MOUNTS;
+      break;
+
+    case THUNAR_DEVICE_KIND_MOUNT_REMOTE:
+      shortcut->group = THUNAR_SHORTCUT_GROUP_NETWORK_MOUNTS;
+      break;
+    }
+
+  /* insert in the model */
+  thunar_shortcuts_model_add_shortcut (model, shortcut);
+
+  /* header visibility if call is from monitor */
+  if (device_monitor != NULL
+      && !shortcut->hidden)
+    thunar_shortcuts_model_header_visibility (model);
+}
+
+
+
+static void
+thunar_shortcuts_model_device_removed (ThunarDeviceMonitor  *device_monitor,
+                                       ThunarDevice         *device,
+                                       ThunarShortcutsModel *model)
+{
+  GList *lp;
+
+  _thunar_return_if_fail (THUNAR_DEVICE_MONITOR (device_monitor));
+  _thunar_return_if_fail (model->device_monitor == device_monitor);
+  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+
+  /* find the shortcut */
+  for (lp = model->shortcuts; lp != NULL; lp = lp->next)
+    if (THUNAR_SHORTCUT (lp->data)->device == device)
+      break;
+
+  /* something is broken if we don't have a shortcut here */
+  _thunar_assert (lp != NULL);
+  _thunar_assert (THUNAR_SHORTCUT (lp->data)->device == device);
+
+  /* drop the shortcut from the model */
+  if (G_LIKELY (lp != NULL))
+    thunar_shortcuts_model_remove_shortcut (model, lp->data);
+}
+
+
+
+static void
+thunar_shortcuts_model_device_changed (ThunarDeviceMonitor  *device_monitor,
+                                       ThunarDevice         *device,
+                                       ThunarShortcutsModel *model)
+{
+  GtkTreeIter     iter;
+  GList          *lp;
+  gint            idx;
+  GtkTreePath    *path;
+  ThunarShortcut *shortcut;
+  gboolean        update_header = FALSE;
+
+  _thunar_return_if_fail (THUNAR_DEVICE_MONITOR (device_monitor));
+  _thunar_return_if_fail (model->device_monitor == device_monitor);
+  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+
+    /* find the shortcut */
+  for (lp = model->shortcuts, idx = 0; lp != NULL; lp = lp->next, idx++)
+    if (THUNAR_SHORTCUT (lp->data)->device == device)
+      break;
+
+  /* something is broken if we don't have a shortcut here */
+  _thunar_assert (lp != NULL);
+  _thunar_assert (THUNAR_SHORTCUT (lp->data)->device == device);
+
+  if (G_LIKELY (lp != NULL))
+    {
+      shortcut = lp->data;
+
+      /* cleanup tooltip */
+      g_free (shortcut->tooltip);
+      shortcut->tooltip = NULL;
+
+      /* hidden state */
+      if (shortcut->hidden != thunar_device_get_hidden (device))
+        {
+          shortcut->hidden = thunar_device_get_hidden (device);
+          update_header = TRUE;
+        }
+
+      /* 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);
+    }
+
+  /* header visibility */
+  if (update_header)
+    thunar_shortcuts_model_header_visibility (model);
+}
+
+
+
+static void
+thunar_shortcut_free (ThunarShortcut       *shortcut,
+                      ThunarShortcutsModel *model)
+{
+  if (G_LIKELY (shortcut->file != NULL))
+    {
+      /* drop the file watch */
+      thunar_file_unwatch (shortcut->file);
+
+      /* unregister from the file */
+      g_signal_handlers_disconnect_matched (shortcut->file,
+                                            G_SIGNAL_MATCH_DATA, 0,
+                                            0, NULL, NULL, model);
+      g_object_unref (shortcut->file);
+    }
+
+  if (G_LIKELY (shortcut->device != NULL))
+    g_object_unref (shortcut->device);
+
+  if (G_LIKELY (shortcut->gicon != NULL))
+    g_object_unref (shortcut->gicon);
+
+  if (G_LIKELY (shortcut->location != NULL))
+    g_object_unref (shortcut->location);
+
+  g_free (shortcut->name);
+  g_free (shortcut->tooltip);
+
+  /* release the shortcut itself */
+  g_slice_free (ThunarShortcut, shortcut);
+}
+
+
+
+static gboolean
+thunar_shortcuts_model_busy_timeout (gpointer data)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (data);
+  gboolean              keep_running = FALSE;
+  ThunarShortcut       *shortcut;
+  GtkTreePath          *path;
+  guint                 idx;
+  GtkTreeIter           iter;
+  GList                *lp;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (data), FALSE);
+
+  for (lp = model->shortcuts, idx = 0; lp != NULL; lp = lp->next, idx++)
+    {
+      shortcut = lp->data;
+      if (!shortcut->busy)
+        continue;
+
+      /* loop the pulse of the shortcut */
+      shortcut->busy_pulse++;
+      if (shortcut->busy_pulse >= SPINNER_NUM_STEPS)
+        shortcut->busy_pulse = 0;
+
+      /* generate an iterator for the path */
+      GTK_TREE_ITER_INIT (iter, model->stamp, lp);
+
+      /* notify the views about the change */
+      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);
+
+      /* keep the timeout running */
+      keep_running = TRUE;
+    }
+
+  return keep_running;
+}
+
+
+
+static void
+thunar_shortcuts_model_busy_timeout_destroyed (gpointer data)
+{
+  THUNAR_SHORTCUTS_MODEL (data)->busy_timeout_id = 0;
+}
+
+
+
+/**
+ * thunar_shortcuts_model_get_default:
+ *
+ * Returns the default #ThunarShortcutsModel instance shared by
+ * all #ThunarShortcutsView instances.
+ *
+ * Call #g_object_unref() on the returned object when you
+ * don't need it any longer.
+ *
+ * Return value: the default #ThunarShortcutsModel instance.
+ **/
+ThunarShortcutsModel*
+thunar_shortcuts_model_get_default (void)
+{
+  static ThunarShortcutsModel *model = NULL;
+
+  if (G_UNLIKELY (model == NULL))
+    {
+      model = g_object_new (THUNAR_TYPE_SHORTCUTS_MODEL, NULL);
+      g_object_add_weak_pointer (G_OBJECT (model), (gpointer) &model);
+    }
+  else
+    {
+      g_object_ref (G_OBJECT (model));
+    }
+
+  return model;
+}
+
+
+
+/**
+ * thunar_shortcuts_model_has_bookmark:
+ * @model : a #ThunarShortcutsModel instance.
+ * @file  : a #ThuanrFile instance.
+ *
+ * Returns %TRUE if there is a bookmark (not a mount or volume) with
+ * @file as destination.
+ *
+ * Return value: %TRUE if @file was found, else %FALSE.
+ **/
+gboolean
+thunar_shortcuts_model_has_bookmark (ThunarShortcutsModel *model,
+                                     GFile                *file)
+{
+  GList          *lp;
+  ThunarShortcut *shortcut;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
+  _thunar_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+  for (lp = model->shortcuts; lp != NULL; lp = lp->next)
+    {
+      shortcut = lp->data;
+
+      /* only check bookmarks */
+      if (shortcut->group != THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS)
+        continue;
+
+      if (shortcut->file != NULL
+          && g_file_equal (thunar_file_get_file (shortcut->file), file))
+        return TRUE;
+
+      if (shortcut->location != NULL
+          && g_file_equal (shortcut->location, file))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+
+/**
+ * thunar_shortcuts_model_iter_for_file:
+ * @model : a #ThunarShortcutsModel instance.
+ * @file  : a #ThuanrFile instance.
+ * @iter  : pointer to a #GtkTreeIter.
+ *
+ * Tries to lookup the #GtkTreeIter, that belongs to a shortcut, which
+ * refers to @file and stores it to @iter. If no such #GtkTreeIter was
+ * found, %FALSE will be returned and @iter won't be changed. Else
+ * %TRUE will be returned and @iter will be set appropriately.
+ *
+ * Return value: %TRUE if @file was found, else %FALSE.
+ **/
+gboolean
+thunar_shortcuts_model_iter_for_file (ThunarShortcutsModel *model,
+                                      ThunarFile           *file,
+                                      GtkTreeIter          *iter)
+{
+  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);
+
+  for (lp = model->shortcuts; lp != NULL; lp = lp->next)
+    {
+      shortcut = lp->data;
+
+      /* check if we have a location that matches */
+      if (shortcut->file == file)
+        {
+          GTK_TREE_ITER_INIT (*iter, model->stamp, lp);
+          return TRUE;
+        }
+
+      if (shortcut->location != NULL
+          && g_file_equal (shortcut->location, thunar_file_get_file (file)))
+        {
+          GTK_TREE_ITER_INIT (*iter, model->stamp, lp);
+          return TRUE;
+        }
+
+      /* but maybe we have a mounted(!) volume with a matching mount point */
+      if (shortcut->device != NULL
+          && !thunar_device_get_hidden (shortcut->device))
+        {
+          mount_point = thunar_device_get_root (shortcut->device);
+          if (mount_point != NULL)
+            {
+              if (g_file_equal (mount_point, thunar_file_get_file (file)))
+                {
+                  GTK_TREE_ITER_INIT (*iter, model->stamp, lp);
+                  g_object_unref (mount_point);
+                  return TRUE;
+                }
+
+              g_object_unref (mount_point);
+            }
+        }
+    }
+
+  return FALSE;
+}
+
+
+
+/**
+ * thunar_shortcuts_model_drop_possible:
+ * @model : a #ThunarShortcutstModel.
+ * @path  : a #GtkTreePath.
+ *
+ * Determines whether a drop is possible before the given @path, at the same depth
+ * as @path. I.e., can we drop data at that location. @path does not have to exist;
+ * the return value will almost certainly be FALSE if the parent of @path doesn't
+ * exist, though.
+ *
+ * Return value: %TRUE if it's possible to drop data before @path, else %FALSE.
+ **/
+gboolean
+thunar_shortcuts_model_drop_possible (ThunarShortcutsModel *model,
+                                      GtkTreePath          *path)
+{
+  ThunarShortcut *shortcut;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
+  _thunar_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
+
+  /* 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 not possible */
+  if (G_LIKELY (shortcut == NULL))
+    return FALSE;
+
+  /* cannot drop before special shortcuts! */
+  if (shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS)
+    return TRUE;
+
+  /* we can drop at the end of the bookmarks (before network header) */
+  if (shortcut->group == THUNAR_SHORTCUT_GROUP_NETWORK_HEADER)
+    return TRUE;
+
+  return FALSE;
+}
+
+
+
+/**
+ * thunar_shortcuts_model_add:
+ * @model    : a #ThunarShortcutsModel.
+ * @dst_path : the destination path.
+ * @file     : the #ThunarFile that should be added to the shortcuts list.
+ *
+ * Adds the shortcut @file to the @model at @dst_path, unless @file is
+ * already present in @model in which case no action is performed.
+ **/
+void
+thunar_shortcuts_model_add (ThunarShortcutsModel *model,
+                            GtkTreePath          *dst_path,
+                            gpointer              file)
+{
+  ThunarShortcut *shortcut;
+  GFile          *location;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+  _thunar_return_if_fail (dst_path == NULL || gtk_tree_path_get_depth (dst_path) > 0);
+  _thunar_return_if_fail (dst_path == NULL || gtk_tree_path_get_indices (dst_path)[0] >= 0);
+  _thunar_return_if_fail (dst_path == NULL || gtk_tree_path_get_indices (dst_path)[0] <= (gint) g_list_length (model->shortcuts));
+  _thunar_return_if_fail (THUNAR_IS_FILE (file) || G_IS_FILE (file));
+
+  location = G_IS_FILE (file) ? file : thunar_file_get_file (file);
+
+  /* verify that the file is not already in use as shortcut */
+  if (thunar_shortcuts_model_has_bookmark (model, location))
+    return;
+
+  /* create the new shortcut that will be inserted */
+  shortcut = g_slice_new0 (ThunarShortcut);
+  shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS;
+
+  if (thunar_shortcuts_model_local_file (location))
+    {
+      shortcut->file = thunar_file_get (location, NULL);
+    }
+  else
+    {
+      shortcut->location = g_object_ref (G_OBJECT (location));
+      shortcut->gicon = g_themed_icon_new ("folder-remote");
+    }
+
+  /* add the shortcut to the list at the given position */
+  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);
+}
+
+
+
+/**
+ * thunar_shortcuts_model_move:
+ * @model    : a #ThunarShortcutsModel.
+ * @src_path : the source path.
+ * @dst_path : the destination path.
+ *
+ * Moves the shortcut at @src_path to @dst_path, adjusting other
+ * shortcut's positions as required.
+ **/
+void
+thunar_shortcuts_model_move (ThunarShortcutsModel *model,
+                             GtkTreePath          *src_path,
+                             GtkTreePath          *dst_path)
+{
+  ThunarShortcut *shortcut;
+  GtkTreePath    *path;
+  GList          *lp;
+  gint           *order;
+  gint            index_src;
+  gint            index_dst;
+  gint            idx;
+  gint            n_shortcuts;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+  _thunar_return_if_fail (gtk_tree_path_get_depth (src_path) > 0);
+  _thunar_return_if_fail (gtk_tree_path_get_depth (dst_path) > 0);
+  _thunar_return_if_fail (gtk_tree_path_get_indices (src_path)[0] >= 0);
+  _thunar_return_if_fail (gtk_tree_path_get_indices (src_path)[0] < (gint) g_list_length (model->shortcuts));
+  _thunar_return_if_fail (gtk_tree_path_get_indices (dst_path)[0] > 0);
+
+  index_src = gtk_tree_path_get_indices (src_path)[0];
+  index_dst = gtk_tree_path_get_indices (dst_path)[0];
+
+  if (G_UNLIKELY (index_src == index_dst))
+    return;
+
+  /* generate the order for the rows prior the dst/src rows */
+  n_shortcuts = g_list_length (model->shortcuts);
+  order = g_newa (gint, n_shortcuts);
+  for (idx = 0, lp = model->shortcuts; idx < index_src && idx < index_dst; ++idx, lp = lp->next)
+    order[idx] = idx;
+
+  if (idx == index_src)
+    {
+      shortcut = THUNAR_SHORTCUT (lp->data);
+
+      for (; idx < index_dst; ++idx, lp = lp->next)
+        {
+          if (lp->next == NULL)
+            break;
+          lp->data = lp->next->data;
+          order[idx] = idx + 1;
+        }
+
+      lp->data = shortcut;
+      order[idx++] = index_src;
+    }
+  else
+    {
+      for (; idx < index_src; ++idx, lp = lp->next)
+        ;
+
+      _thunar_assert (idx == index_src);
+
+      shortcut = THUNAR_SHORTCUT (lp->data);
+
+      for (; idx > index_dst; --idx, lp = lp->prev)
+        {
+          lp->data = lp->prev->data;
+          order[idx] = idx - 1;
+        }
+
+      _thunar_assert (idx == index_dst);
+
+      lp->data = shortcut;
+      order[idx] = index_src;
+      idx = index_src + 1;
+    }
+
+  /* generate the remaining order */
+  for (; idx < n_shortcuts; ++idx)
+    order[idx] = idx;
+
+  /* tell all listeners about the reordering just performed */
+  path = gtk_tree_path_new_first ();
+  gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model), path, NULL, order);
+  gtk_tree_path_free (path);
+
+  /* the shortcuts list was changed, so write the gtk bookmarks file */
+  thunar_shortcuts_model_save (model);
+}
+
+
+
+/**
+ * thunar_shortcuts_model_remove:
+ * @model : a #ThunarShortcutsModel.
+ * @path  : the #GtkTreePath of the shortcut to remove.
+ *
+ * Removes the shortcut at @path from the @model and syncs to
+ * on-disk storage. @path must refer to a valid, user-defined
+ * shortcut, as you cannot remove system-defined entities (they
+ * are managed internally).
+ **/
+void
+thunar_shortcuts_model_remove (ThunarShortcutsModel *model,
+                               GtkTreePath          *path)
+{
+  ThunarShortcut *shortcut;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+  _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));
+
+  /* lookup the shortcut for the given path */
+  shortcut = g_list_nth_data (model->shortcuts, gtk_tree_path_get_indices (path)[0]);
+
+  /* verify that the shortcut is removable */
+  _thunar_assert (shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS);
+
+  /* remove the shortcut (using the file destroy handler) */
+  thunar_shortcuts_model_remove_shortcut (model, shortcut);
+}
+
+
+
+/**
+ * thunar_shortcuts_model_rename:
+ * @model : a #ThunarShortcutsModel.
+ * @iter  : the #GtkTreeIter which refers to the shortcut that
+ *          should be renamed to @name.
+ * @name  : the new name for the shortcut at @path or %NULL to
+ *          return to the default name.
+ *
+ * Renames the shortcut at @iter to the new @name in @model.
+ *
+ * @name may be %NULL or an empty to reset the shortcut to
+ * its default name.
+ **/
+void
+thunar_shortcuts_model_rename (ThunarShortcutsModel *model,
+                               GtkTreeIter          *iter,
+                               const gchar          *name)
+{
+  ThunarShortcut *shortcut;
+  GtkTreePath    *path;
+
+  _thunar_return_if_fail (name == NULL || g_utf8_validate (name, -1, NULL));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+  _thunar_return_if_fail (iter->stamp == model->stamp);
+  _thunar_return_if_fail (iter->user_data != NULL);
+
+  /* lookup the shortcut for the given path */
+  shortcut = THUNAR_SHORTCUT (((GList *) iter->user_data)->data);
+
+  /* verify the shortcut */
+  _thunar_assert (shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS);
+
+  /* perform the rename */
+  if (G_UNLIKELY (shortcut->name != NULL))
+    g_free (shortcut->name);
+  if (G_UNLIKELY (name == NULL || *name == '\0'))
+    shortcut->name = NULL;
+  else
+    shortcut->name = g_strdup (name);
+
+  /* notify the views about the change */
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
+  gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, iter);
+  gtk_tree_path_free (path);
+
+  /* save the changes to the model */
+  thunar_shortcuts_model_save (model);
+}
+
+
+
+void
+thunar_shortcuts_model_set_busy (ThunarShortcutsModel *model,
+                                 ThunarDevice         *device,
+                                 gboolean              busy)
+{
+  ThunarShortcut *shortcut;
+  GList          *lp;
+  guint           idx;
+  GtkTreeIter     iter;
+  GtkTreePath    *path;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
+
+  /* get the device */
+  for (lp = model->shortcuts, idx = 0; lp != NULL; lp = lp->next, idx++)
+    if (THUNAR_SHORTCUT (lp->data)->device == device)
+      break;
+
+  if (lp == NULL)
+    return;
+
+  shortcut = lp->data;
+  _thunar_assert (shortcut->device == device);
+
+  if (G_LIKELY (shortcut->busy != busy))
+    {
+      shortcut->busy = busy;
+
+      if (busy && model->busy_timeout_id == 0)
+        {
+          /* start the global cycle timeout */
+          model->busy_timeout_id =
+            gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
+                                          SPINNER_CYCLE_DURATION / SPINNER_NUM_STEPS,
+                                          thunar_shortcuts_model_busy_timeout, model,
+                                          thunar_shortcuts_model_busy_timeout_destroyed);
+        }
+      else if (!busy)
+        {
+          /* generate an iterator for the path */
+          GTK_TREE_ITER_INIT (iter, model->stamp, lp);
+
+          /* notify the views about the change */
+          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);
+        }
+    }
+}
+
+
+
+
+void
+thunar_shortcuts_model_set_hidden (ThunarShortcutsModel *model,
+                                   GtkTreePath          *path,
+                                   gboolean              hidden)
+{
+  ThunarShortcut  *shortcut;
+  guint            length;
+  gchar          **bookmarks;
+  guint            pos;
+  gchar           *uri;
+  guint            n;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+  _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));
+
+  /* lookup the shortcut for the given path */
+  shortcut = g_list_nth_data (model->shortcuts, gtk_tree_path_get_indices (path)[0]);
+  _thunar_return_if_fail (shortcut != NULL);
+
+  if (shortcut->device != NULL)
+    {
+      /* if this is a device, store in the device monitor */
+      thunar_device_monitor_set_hidden (model->device_monitor, shortcut->device, hidden);
+      return;
+    }
+
+  /* get the uri */
+  if (shortcut->file != NULL)
+    uri = thunar_file_dup_uri (shortcut->file);
+  else if (shortcut->location != NULL)
+    uri = g_file_get_uri (shortcut->location);
+  else
+    _thunar_assert_not_reached ();
+
+  /* prepare array */
+  length = model->hidden_bookmarks != NULL ? g_strv_length (model->hidden_bookmarks) : 0;
+  bookmarks = g_new0 (gchar *, length + 2);
+  pos = 0;
+
+  /* copy other uuid in the new list */
+  if (model->hidden_bookmarks != NULL)
+    {
+      for (n = 0; model->hidden_bookmarks[n] != NULL; n++)
+        if (g_strcmp0 (model->hidden_bookmarks[n], uri) != 0)
+          bookmarks[pos++] = g_strdup (model->hidden_bookmarks[n]);
+    }
+
+  /* add the new uri if it should hide */
+  if (hidden)
+    bookmarks[pos++] = uri;
+  else
+    g_free (uri);
+
+  /* store new list */
+  g_object_set (G_OBJECT (model->preferences), "hidden-bookmarks", bookmarks, NULL);
+  g_strfreev (bookmarks);
+
+  /* header visibility */
+  thunar_shortcuts_model_header_visibility (model);
+}
diff --git a/thunar/thunar-shortcuts-model.h b/thunar/thunar-shortcuts-model.h
new file mode 100644
index 0000000..cb87a1f
--- /dev/null
+++ b/thunar/thunar-shortcuts-model.h
@@ -0,0 +1,129 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2005-2006 Benedikt Meurer <benny at xfce.org>
+ *
+ * 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 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 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 Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __THUNAR_SHORTCUTS_MODEL_H__
+#define __THUNAR_SHORTCUTS_MODEL_H__
+
+#include <thunar/thunar-file.h>
+#include <thunar/thunar-device.h>
+
+G_BEGIN_DECLS;
+
+typedef struct _ThunarShortcutsModelClass ThunarShortcutsModelClass;
+typedef struct _ThunarShortcutsModel      ThunarShortcutsModel;
+typedef enum   _ThunarShortcutGroup       ThunarShortcutGroup;
+
+#define THUNAR_TYPE_SHORTCUTS_MODEL            (thunar_shortcuts_model_get_type ())
+#define THUNAR_SHORTCUTS_MODEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_SHORTCUTS_MODEL, ThunarShortcutsModel))
+#define THUNAR_SHORTCUTS_MODEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_SHORTCUTS_MODEL, ThunarShortcutsModelClass))
+#define THUNAR_IS_SHORTCUTS_MODEL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_SHORTCUTS_MODEL))
+#define THUNAR_IS_SHORTCUTS_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_SHORTCUTS_MODEL))
+#define THUNAR_SHORTCUTS_MODEL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_MODEL_SHORTCUTS_MODEL, ThunarShortcutsModelClass))
+
+typedef enum
+{
+  THUNAR_SHORTCUTS_MODEL_COLUMN_IS_HEADER,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_IS_ITEM,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_VISIBLE,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_NAME,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_TOOLTIP,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_FILE,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_LOCATION,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_GICON,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_MUTABLE,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_CAN_EJECT,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_GROUP,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY_PULSE,
+  THUNAR_SHORTCUTS_MODEL_N_COLUMNS,
+} ThunarShortcutsModelColumn;
+
+#define THUNAR_SHORTCUT_GROUP_DEVICES (THUNAR_SHORTCUT_GROUP_DEVICES_HEADER \
+                                       | THUNAR_SHORTCUT_GROUP_DEVICES_FILESYSTEM \
+                                       | THUNAR_SHORTCUT_GROUP_DEVICES_VOLUMES \
+                                       | THUNAR_SHORTCUT_GROUP_DEVICES_MOUNTS)
+#define THUNAR_SHORTCUT_GROUP_PLACES  (THUNAR_SHORTCUT_GROUP_PLACES_HEADER \
+                                       | THUNAR_SHORTCUT_GROUP_PLACES_DEFAULT \
+                                       | THUNAR_SHORTCUT_GROUP_PLACES_TRASH \
+                                       | THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS)
+#define THUNAR_SHORTCUT_GROUP_NETWORK (THUNAR_SHORTCUT_GROUP_NETWORK_HEADER \
+                                       | THUNAR_SHORTCUT_GROUP_NETWORK_DEFAULT \
+                                       | THUNAR_SHORTCUT_GROUP_NETWORK_MOUNTS)
+#define THUNAR_SHORTCUT_GROUP_HEADER  (THUNAR_SHORTCUT_GROUP_DEVICES_HEADER \
+                                       | THUNAR_SHORTCUT_GROUP_PLACES_HEADER \
+                                       | THUNAR_SHORTCUT_GROUP_NETWORK_HEADER)
+
+enum _ThunarShortcutGroup
+{
+  /* THUNAR_SHORTCUT_GROUP_DEVICES */
+  THUNAR_SHORTCUT_GROUP_DEVICES_HEADER     = (1 << 0),  /* devices header */
+  THUNAR_SHORTCUT_GROUP_DEVICES_FILESYSTEM = (1 << 1),  /* local filesystem */
+  THUNAR_SHORTCUT_GROUP_DEVICES_VOLUMES    = (1 << 2),  /* local ThunarDevices */
+  THUNAR_SHORTCUT_GROUP_DEVICES_MOUNTS     = (1 << 3),  /* local mounts, like cameras and archives */
+
+  /* THUNAR_SHORTCUT_GROUP_PLACES */
+  THUNAR_SHORTCUT_GROUP_PLACES_HEADER      = (1 << 4),  /* places header */
+  THUNAR_SHORTCUT_GROUP_PLACES_DEFAULT     = (1 << 5),  /* home and desktop */
+  THUNAR_SHORTCUT_GROUP_PLACES_TRASH       = (1 << 6),  /* trash */
+  THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS   = (1 << 7),  /* gtk-bookmarks */
+
+  /* THUNAR_SHORTCUT_GROUP_NETWORK */
+  THUNAR_SHORTCUT_GROUP_NETWORK_HEADER     = (1 << 8),  /* network header */
+  THUNAR_SHORTCUT_GROUP_NETWORK_DEFAULT    = (1 << 9),  /* browse network */
+  THUNAR_SHORTCUT_GROUP_NETWORK_MOUNTS     = (1 << 10), /* remote ThunarDevices */
+};
+
+
+
+GType                  thunar_shortcuts_model_get_type      (void) G_GNUC_CONST;
+
+ThunarShortcutsModel  *thunar_shortcuts_model_get_default   (void);
+
+gboolean               thunar_shortcuts_model_has_bookmark  (ThunarShortcutsModel *model,
+                                                             GFile                *file);
+
+gboolean               thunar_shortcuts_model_iter_for_file (ThunarShortcutsModel *model,
+                                                             ThunarFile           *file,
+                                                             GtkTreeIter          *iter);
+
+gboolean               thunar_shortcuts_model_drop_possible (ThunarShortcutsModel *model,
+                                                             GtkTreePath          *path);
+
+void                   thunar_shortcuts_model_add           (ThunarShortcutsModel *model,
+                                                             GtkTreePath          *dst_path,
+                                                             gpointer              file);
+void                   thunar_shortcuts_model_move          (ThunarShortcutsModel *model,
+                                                             GtkTreePath          *src_path,
+                                                             GtkTreePath          *dst_path);
+void                   thunar_shortcuts_model_remove        (ThunarShortcutsModel *model,
+                                                             GtkTreePath          *path);
+void                   thunar_shortcuts_model_rename        (ThunarShortcutsModel *model,
+                                                             GtkTreeIter          *iter,
+                                                             const gchar          *name);
+void                   thunar_shortcuts_model_set_busy      (ThunarShortcutsModel *model,
+                                                             ThunarDevice         *device,
+                                                             gboolean              busy);
+void                   thunar_shortcuts_model_set_hidden    (ThunarShortcutsModel *model,
+                                                             GtkTreePath          *path,
+                                                             gboolean              hidden);
+
+G_END_DECLS;
+
+#endif /* !__THUNAR_SHORTCUTS_MODEL_H__ */
diff --git a/thunar/thunar-shortcuts-pane.c b/thunar/thunar-shortcuts-pane.c
index fa646b4..590a92c 100644
--- a/thunar/thunar-shortcuts-pane.c
+++ b/thunar/thunar-shortcuts-pane.c
@@ -1,7 +1,6 @@
 /* vi:set et ai sw=2 sts=2 ts=2: */
 /*-
  * Copyright (c) 2005-2006 Benedikt Meurer <benny at xfce.org>
- * Copyright (c) 2015 Jonas Kümmerlin <rgcjonas at gmail.com>
  *
  * 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
@@ -24,17 +23,12 @@
 
 #include <thunar/thunar-gobject-extensions.h>
 #include <thunar/thunar-private.h>
+#include <thunar/thunar-shortcuts-model.h>
 #include <thunar/thunar-shortcuts-pane.h>
 #include <thunar/thunar-shortcuts-pane-ui.h>
+#include <thunar/thunar-shortcuts-view.h>
 #include <thunar/thunar-side-pane.h>
 #include <thunar/thunar-stock.h>
-#include <thunar/thunar-browser.h>
-#include <thunar/thunar-dialogs.h>
-#include <thunar/thunar-application.h>
-#include <thunar/thunar-dnd.h>
-#include <thunar/thunar-gtk-extensions.h>
-#include <thunar/thunar-gio-extensions.h>
-#include <thunar/thunar-util.h>
 
 
 
@@ -73,35 +67,17 @@ static void          thunar_shortcuts_pane_set_ui_manager        (ThunarComponen
                                                                   GtkUIManager             *ui_manager);
 static void          thunar_shortcuts_pane_action_shortcuts_add  (GtkAction                *action,
                                                                   ThunarShortcutsPane      *shortcuts_pane);
-static void          thunar_shortcuts_pane_open_location         (GtkPlacesSidebar         *sidebar,
-                                                                  GFile                    *location,
-                                                                  GtkPlacesOpenFlags        flags,
-                                                                  ThunarShortcutsPane      *shortcuts_pane);
-static gint          thunar_shortcuts_pane_drag_action_requested (GtkPlacesSidebar         *sidebar,
-                                                                  GdkDragContext           *context,
-                                                                  GFile                    *dest_file,
-                                                                  GList                    *source_file_list,
-                                                                  ThunarShortcutsPane      *shortcuts_pane);
-static void          thunar_shortcuts_pane_drag_perform_drop     (GtkPlacesSidebar         *sidebar,
-                                                                  GFile                    *dest_file,
-                                                                  GList                    *source_file_list,
-                                                                  GdkDragAction             action,
-                                                                  ThunarShortcutsPane      *shortcuts_pane);
-static gint          thunar_shortcuts_pane_drag_action_ask       (GtkPlacesSidebar         *sidebar,
-                                                                  GdkDragAction             actions,
-                                                                  ThunarShortcutsPane      *shortcuts_pane);
-
 
 
 
 struct _ThunarShortcutsPaneClass
 {
-  GtkBinClass __parent__;
+  GtkScrolledWindowClass __parent__;
 };
 
 struct _ThunarShortcutsPane
 {
-  GtkBin __parent__;
+  GtkScrolledWindow __parent__;
 
   ThunarFile       *current_directory;
   GList            *selected_files;
@@ -110,7 +86,7 @@ struct _ThunarShortcutsPane
   GtkUIManager     *ui_manager;
   guint             ui_merge_id;
 
-  GtkWidget        *places;
+  GtkWidget        *view;
 
   guint             idle_select_directory;
 };
@@ -124,11 +100,10 @@ static const GtkActionEntry action_entries[] =
 
 
 
-G_DEFINE_TYPE_WITH_CODE (ThunarShortcutsPane, thunar_shortcuts_pane, GTK_TYPE_BIN,
+G_DEFINE_TYPE_WITH_CODE (ThunarShortcutsPane, thunar_shortcuts_pane, GTK_TYPE_SCROLLED_WINDOW,
     G_IMPLEMENT_INTERFACE (THUNAR_TYPE_NAVIGATOR, thunar_shortcuts_pane_navigator_init)
     G_IMPLEMENT_INTERFACE (THUNAR_TYPE_COMPONENT, thunar_shortcuts_pane_component_init)
-    G_IMPLEMENT_INTERFACE (THUNAR_TYPE_SIDE_PANE, thunar_shortcuts_pane_side_pane_init)
-    G_IMPLEMENT_INTERFACE (THUNAR_TYPE_BROWSER, NULL))
+    G_IMPLEMENT_INTERFACE (THUNAR_TYPE_SIDE_PANE, thunar_shortcuts_pane_side_pane_init))
 
 
 
@@ -193,17 +168,20 @@ thunar_shortcuts_pane_init (ThunarShortcutsPane *shortcuts_pane)
   gtk_action_group_set_translation_domain (shortcuts_pane->action_group, GETTEXT_PACKAGE);
   gtk_action_group_add_actions (shortcuts_pane->action_group, action_entries, G_N_ELEMENTS (action_entries), shortcuts_pane);
 
-  /* configure the places sidebar */
-  shortcuts_pane->places = gtk_places_sidebar_new ();
-  gtk_places_sidebar_set_open_flags (GTK_PLACES_SIDEBAR (shortcuts_pane->places), GTK_PLACES_OPEN_NORMAL|GTK_PLACES_OPEN_NEW_TAB|GTK_PLACES_OPEN_NEW_WINDOW);
+  /* configure the GtkScrolledWindow */
+  gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (shortcuts_pane), NULL);
+  gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (shortcuts_pane), NULL);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (shortcuts_pane), GTK_SHADOW_IN);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (shortcuts_pane), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
 
-  g_signal_connect (shortcuts_pane->places, "open-location", G_CALLBACK (thunar_shortcuts_pane_open_location), shortcuts_pane);
-  g_signal_connect (shortcuts_pane->places, "drag-action-requested", G_CALLBACK (thunar_shortcuts_pane_drag_action_requested), shortcuts_pane);
-  g_signal_connect (shortcuts_pane->places, "drag-perform-drop", G_CALLBACK (thunar_shortcuts_pane_drag_perform_drop), shortcuts_pane);
-  g_signal_connect (shortcuts_pane->places, "drag-action-ask", G_CALLBACK (thunar_shortcuts_pane_drag_action_ask), shortcuts_pane);
+  /* allocate the shortcuts view */
+  shortcuts_pane->view = thunar_shortcuts_view_new ();
+  gtk_container_add (GTK_CONTAINER (shortcuts_pane), shortcuts_pane->view);
+  gtk_widget_show (shortcuts_pane->view);
 
-  gtk_container_add (GTK_CONTAINER (shortcuts_pane), shortcuts_pane->places);
-  gtk_widget_show (shortcuts_pane->places);
+  /* connect the "shortcut-activated" signal */
+  g_signal_connect_swapped (G_OBJECT (shortcuts_pane->view), "shortcut-activated", G_CALLBACK (thunar_navigator_change_directory), shortcuts_pane);
+  g_signal_connect_swapped (G_OBJECT (shortcuts_pane->view), "shortcut-activated-tab", G_CALLBACK (thunar_navigator_open_new_tab), shortcuts_pane);
 }
 
 
@@ -314,8 +292,8 @@ thunar_shortcuts_pane_set_current_directory_idle (gpointer data)
 
   if (shortcuts_pane->current_directory != NULL)
     {
-      gtk_places_sidebar_set_location (GTK_PLACES_SIDEBAR (shortcuts_pane->places),
-                                       thunar_file_get_file (shortcuts_pane->current_directory));
+      thunar_shortcuts_view_select_by_file (THUNAR_SHORTCUTS_VIEW (shortcuts_pane->view),
+                                            shortcuts_pane->current_directory);
     }
 
   /* unset id */
@@ -400,12 +378,20 @@ thunar_shortcuts_pane_set_selected_files (ThunarComponent *component,
   if (lp == NULL && selected_files != NULL)
     {
       /* check if atleast one of the selected folders is not already present in the model */
-      /* TODO */
+      model = gtk_tree_view_get_model (GTK_TREE_VIEW (shortcuts_pane->view));
+      if (G_LIKELY (model != NULL))
+        {
+          /* check all selected folders */
+          child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+          for (lp = selected_files; lp != NULL; lp = lp->next)
+            if (!thunar_shortcuts_model_has_bookmark (THUNAR_SHORTCUTS_MODEL (child_model), thunar_file_get_file (lp->data)))
+              break;
+        }
 
       /* display the action and change the label appropriately */
       g_object_set (G_OBJECT (action),
                     "label", ngettext ("Side Pane (Create Shortcut)", "Side Pane (Create Shortcuts)", n),
-                    "sensitive", /*(lp != NULL)*/ TRUE,
+                    "sensitive", (lp != NULL),
                     "tooltip", ngettext ("Add the selected folder to the shortcuts side pane",
                                          "Add the selected folders to the shortcuts side pane", n),
                     "visible", TRUE,
@@ -476,316 +462,35 @@ thunar_shortcuts_pane_set_ui_manager (ThunarComponent *component,
   g_object_notify (G_OBJECT (shortcuts_pane), "ui-manager");
 }
 
-struct bookmark
-{
-  struct bookmark *next;
-  GFile           *file;
-  gchar           *name;
-};
-
-static void store_bookmark (GFile       *file,
-                            const gchar *name,
-                            gint         row_num,
-                            gpointer     user_data)
-{
-  struct bookmark **plist = user_data;
-  struct bookmark *bm   = g_slice_new0 (struct bookmark);
-
-  _thunar_return_if_fail (plist != NULL);
-
-  bm->next = *plist;
-  bm->file = g_object_ref (file);
-  bm->name = g_strdup (name);
-
-  *plist = bm;
-}
-
 
 
 static void
 thunar_shortcuts_pane_action_shortcuts_add (GtkAction           *action,
                                             ThunarShortcutsPane *shortcuts_pane)
 {
-  GFile           *bookmark_file = NULL, *parent = NULL;
-  gchar           *uri = NULL;
-  GString         *contents = NULL;
-  struct bookmark *bm_list = NULL, *bm_i = NULL;
-  GList           *lp = NULL;
-  GError          *error = NULL;
+  GtkTreeModel *model;
+  GtkTreeModel *child_model;
+  GList        *lp;
 
   _thunar_return_if_fail (GTK_IS_ACTION (action));
   _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_PANE (shortcuts_pane));
 
-  bookmark_file = thunar_g_file_new_for_bookmarks ();
-
-  thunar_util_load_bookmarks (bookmark_file, store_bookmark, &bm_list);
-
-  /* add all selected folders to the list - unless they are already contained in there */
-  /* FIXME: complexity */
-  for (lp = shortcuts_pane->selected_files; lp != NULL; lp = lp->next)
-    if (G_LIKELY (thunar_file_is_directory (lp->data)))
-      {
-        uri = thunar_file_dup_uri (lp->data);
-
-        for (bm_i = bm_list; bm_i != NULL; bm_i = bm_i->next)
-          {
-            if (g_file_equal(thunar_file_get_file (lp->data), bm_i->file))
-              break;
-          }
-
-        if (bm_i == NULL)
+  /* determine the shortcuts model for the view */
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (shortcuts_pane->view));
+  if (G_LIKELY (model != NULL))
+    {
+      /* add all selected folders to the model */
+      child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+      for (lp = shortcuts_pane->selected_files; lp != NULL; lp = lp->next)
+        if (G_LIKELY (thunar_file_is_directory (lp->data)))
           {
-            bm_i = g_slice_new0 (struct bookmark);
-            bm_i->next = bm_list;
-            bm_i->file = g_object_ref (thunar_file_get_file (lp->data));
-            bm_i->name = NULL;
-
-            bm_list = bm_i;
+            /* append the folder to the shortcuts model */
+            thunar_shortcuts_model_add (THUNAR_SHORTCUTS_MODEL (child_model), NULL, lp->data);
           }
-      }
-
-  /* create the new contents of the file */
-  contents = g_string_new ("");
-
-  for (bm_i = bm_list; bm_i != NULL; bm_i = bm_i->next)
-    {
-      uri = g_file_get_uri (bm_i->file);
-      if (!uri)
-        continue;
-
-      /* Since we built the list in reverse, we will prepend instead of append
-       * to the string. That way, it evens out. */
-      g_string_prepend_c (contents, '\n');
 
-      if (bm_i->name)
-        {
-          g_string_prepend (contents, bm_i->name);
-          g_string_prepend_c (contents, ' ');
-        }
-
-      g_string_prepend (contents, uri);
-
-      g_free (uri);
+      /* update the user interface to reflect the new action state */
+      lp = thunar_g_file_list_copy (shortcuts_pane->selected_files);
+      thunar_component_set_selected_files (THUNAR_COMPONENT (shortcuts_pane), lp);
+      thunar_g_file_list_free (lp);
     }
-
-  /* save the contents back into the file */
-  parent = g_file_get_parent (bookmark_file);
-  if (!g_file_make_directory_with_parents (parent, NULL, &error))
-    {
-      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
-        g_clear_error (&error);
-      else
-        goto out;
-    }
-
-  if (!g_file_replace_contents (bookmark_file,
-                                contents->str,
-                                contents->len,
-                                NULL, FALSE, 0, NULL,
-                                NULL, &error))
-
-  /* update the user interface to reflect the new action state */
-  lp = thunar_g_file_list_copy (shortcuts_pane->selected_files);
-  thunar_component_set_selected_files (THUNAR_COMPONENT (shortcuts_pane), lp);
-  thunar_g_file_list_free (lp);
-
-out:
-  if (error)
-    {
-      g_critical ("%s", error->message);
-      g_error_free (error);
-    }
-  g_string_free (contents, TRUE);
-  g_object_unref (parent);
-  g_object_unref (bookmark_file);
-
-  for (bm_i = bm_list; bm_i != NULL; bm_i = bm_list)
-    {
-      g_object_unref (bm_i->file);
-      g_free (bm_i->name);
-
-      bm_list = bm_i->next;
-      g_slice_free (struct bookmark, bm_i);
-    }
-}
-
-
-
-static void
-thunar_shortcuts_pane_poke_location_finish (ThunarBrowser *browser,
-                                            GFile         *location,
-                                            ThunarFile    *file,
-                                            ThunarFile    *target_file,
-                                            GError        *error,
-                                            gpointer       user_data)
-{
-  gchar *name;
-  GtkPlacesOpenFlags open_in;
-  ThunarApplication *application;
-
-  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_PANE (browser));
-  _thunar_return_if_fail (G_IS_FILE (location));
-
-  open_in = (GtkPlacesOpenFlags)GPOINTER_TO_INT (user_data);
-
-  /* sotre the new file in the shortcuts model */
-  if (error == NULL)
-    {
-      if (open_in == GTK_PLACES_OPEN_NEW_WINDOW)
-        {
-          /* open a new window for the target folder */
-          application = thunar_application_get ();
-          thunar_application_open_window (application, target_file,
-                                          gtk_widget_get_screen (GTK_WIDGET (browser)), NULL);
-          g_object_unref (application);
-        }
-      else if (open_in == GTK_PLACES_OPEN_NEW_TAB)
-        {
-          thunar_navigator_open_new_tab (THUNAR_NAVIGATOR (browser), target_file);
-        }
-      else if (thunar_file_check_loaded (target_file))
-        {
-          thunar_navigator_change_directory (THUNAR_NAVIGATOR (browser), target_file);
-        }
-      else
-        {
-          g_warning ("WTF: File to be activated but not loaded!");
-        }
-    }
-  else
-    {
-      name = thunar_g_file_get_display_name_remote (location);
-      thunar_dialogs_show_error (GTK_WIDGET (browser), error, _("Failed to open \"%s\""), name);
-      g_free (name);
-    }
-}
-
-
-
-static void
-thunar_shortcuts_pane_open_location (GtkPlacesSidebar    *sidebar,
-                                     GFile               *location,
-                                     GtkPlacesOpenFlags   flags,
-                                     ThunarShortcutsPane *shortcuts_pane)
-{
-  thunar_browser_poke_location (THUNAR_BROWSER (shortcuts_pane), location,
-                                GTK_WIDGET (shortcuts_pane),
-                                thunar_shortcuts_pane_poke_location_finish,
-                                GINT_TO_POINTER (flags));
-}
-
-
-
-static gint thunar_shortcuts_pane_drag_action_requested (GtkPlacesSidebar    *sidebar,
-                                                         GdkDragContext      *context,
-                                                         GFile               *dest_file,
-                                                         GList               *source_file_list,
-                                                         ThunarShortcutsPane *shortcuts_pane)
-{
-  ThunarFile *th_dest_file;
-  GdkDragAction suggested_action;
-
-  /* create ThunarFile* instances for the given file */
-  th_dest_file = thunar_file_get (dest_file, NULL);
-
-  _thunar_return_val_if_fail (th_dest_file != NULL, 0);
-
-  /* check for allowed drop actions */
-  thunar_file_accepts_drop (th_dest_file, source_file_list, context, &suggested_action);
-
-  /* cleanup */
-  g_object_unref (th_dest_file);
-
-  return suggested_action;
-}
-
-
-
-void thunar_shortcuts_pane_drag_perform_drop (GtkPlacesSidebar    *sidebar,
-                                              GFile               *dest_file,
-                                              GList               *source_file_list,
-                                              GdkDragAction        action,
-                                              ThunarShortcutsPane *shortcuts_pane)
-{
-  ThunarFile *th_dest_file;
-
-  if (G_UNLIKELY ((action & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)) == 0))
-    return;
-
-  /* get the thunar file */
-  th_dest_file = thunar_file_get (dest_file, NULL);
-
-  _thunar_return_if_fail (th_dest_file != NULL);
-
-  /* perform the drop */
-  thunar_dnd_perform (GTK_WIDGET (shortcuts_pane), th_dest_file, source_file_list, action, NULL);
-
-  /* cleanup */
-  g_object_unref (th_dest_file);
-}
-
-static void drag_ask_menu_copy_activated (GtkMenuItem   *menuitem,
-                                          GdkDragAction *out_action)
-{
-  *out_action = GDK_ACTION_COPY;
-}
-static void drag_ask_menu_link_activated (GtkMenuItem   *menuitem,
-                                          GdkDragAction *out_action)
-{
-  *out_action = GDK_ACTION_LINK;
-}
-static void drag_ask_menu_move_activated (GtkMenuItem   *menuitem,
-                                          GdkDragAction *out_action)
-{
-  *out_action = GDK_ACTION_MOVE;
-}
-
-static gint thunar_shortcuts_pane_drag_action_ask (GtkPlacesSidebar    *sidebar,
-                                                   GdkDragAction        actions,
-                                                   ThunarShortcutsPane *shortcuts_pane)
-{
-  GtkWidget *menu, *item;
-  GdkDragAction chosen_action = 0;
-
-  /* prepare the popup menu */
-  menu = gtk_menu_new ();
-
-  /* append copy */
-  if (actions & GDK_ACTION_COPY)
-    {
-      item = gtk_menu_item_new_with_mnemonic (_ ("_Copy here"));
-      g_signal_connect (item, "activate", G_CALLBACK (drag_ask_menu_copy_activated), &chosen_action);
-      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-      gtk_widget_show (item);
-    }
-  if (actions & GDK_ACTION_MOVE)
-    {
-      item = gtk_menu_item_new_with_mnemonic (_ ("_Move here"));
-      g_signal_connect (item, "activate", G_CALLBACK (drag_ask_menu_move_activated), &chosen_action);
-      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-      gtk_widget_show (item);
-    }
-  if (actions & GDK_ACTION_LINK)
-    {
-      item = gtk_menu_item_new_with_mnemonic (_ ("_Link here"));
-      g_signal_connect (item, "activate", G_CALLBACK (drag_ask_menu_link_activated), &chosen_action);
-      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-      gtk_widget_show (item);
-    }
-
-  /* append the separator */
-  item = gtk_separator_menu_item_new ();
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-  gtk_widget_show (item);
-
-
-  /* append the cancel item */
-  item = gtk_menu_item_new_with_mnemonic (_ ("_Cancel"));
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-  gtk_widget_show (item);
-
-  /* run the menu on the widget's screen (takes over the floating reference of menu) */
-  thunar_gtk_menu_run (GTK_MENU (menu), sidebar, NULL, NULL, 3, 0);
-
-  return chosen_action;
 }
diff --git a/thunar/thunar-shortcuts-view.c b/thunar/thunar-shortcuts-view.c
new file mode 100644
index 0000000..45dee59
--- /dev/null
+++ b/thunar/thunar-shortcuts-view.c
@@ -0,0 +1,2137 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2005-2007 Benedikt Meurer <benny at xfce.org>
+ * Copyright (c) 2009-2011 Jannis Pohlmann <jannis at xfce.org>
+ * Copyright (c) 2012      Nick Schermer <nick at xfce.org>
+ *
+ * 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
+ * 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
+ * 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
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <gdk/gdkkeysyms.h>
+
+#include <thunar/thunar-application.h>
+#include <thunar/thunar-browser.h>
+#include <thunar/thunar-dialogs.h>
+#include <thunar/thunar-dnd.h>
+#include <thunar/thunar-gio-extensions.h>
+#include <thunar/thunar-gtk-extensions.h>
+#include <thunar/thunar-preferences.h>
+#include <thunar/thunar-private.h>
+#include <thunar/thunar-shortcuts-icon-renderer.h>
+#include <thunar/thunar-shortcuts-model.h>
+#include <thunar/thunar-shortcuts-view.h>
+#include <thunar/thunar-device-monitor.h>
+#include <thunar/thunar-stock.h>
+
+
+
+/* Identifiers for signals */
+enum
+{
+  SHORTCUT_ACTIVATED,
+  SHORTCUT_ACTIVATED_TAB,
+  LAST_SIGNAL,
+};
+
+/* Identifiers for DnD target types */
+enum
+{
+  GTK_TREE_MODEL_ROW,
+  TEXT_URI_LIST,
+};
+
+/* Target for open action */
+typedef enum
+{
+  OPEN_IN_VIEW,
+  OPEN_IN_WINDOW,
+  OPEN_IN_TAB
+}
+OpenTarget;
+
+
+
+static void           thunar_shortcuts_view_finalize                     (GObject                  *object);
+static gboolean       thunar_shortcuts_view_button_press_event           (GtkWidget                *widget,
+                                                                          GdkEventButton           *event);
+static gboolean       thunar_shortcuts_view_button_release_event         (GtkWidget                *widget,
+                                                                          GdkEventButton           *event);
+static gboolean       thunar_shortcuts_view_key_release_event            (GtkWidget                *widget,
+                                                                          GdkEventKey              *event);
+static void           thunar_shortcuts_view_drag_begin                   (GtkWidget                *widget,
+                                                                          GdkDragContext           *context);
+static void           thunar_shortcuts_view_drag_data_received           (GtkWidget                *widget,
+                                                                          GdkDragContext           *context,
+                                                                          gint                      x,
+                                                                          gint                      y,
+                                                                          GtkSelectionData         *selection_data,
+                                                                          guint                     info,
+                                                                          guint                     timestamp);
+static gboolean       thunar_shortcuts_view_drag_drop                    (GtkWidget                *widget,
+                                                                          GdkDragContext           *context,
+                                                                          gint                      x,
+                                                                          gint                      y,
+                                                                          guint                     timestamp);
+static gboolean       thunar_shortcuts_view_drag_motion                  (GtkWidget                *widget,
+                                                                          GdkDragContext           *context,
+                                                                          gint                      x,
+                                                                          gint                      y,
+                                                                          guint                     timestamp);
+static void           thunar_shortcuts_view_drag_leave                   (GtkWidget                *widget,
+                                                                          GdkDragContext           *context,
+                                                                          guint                     timestamp);
+static gboolean       thunar_shortcuts_view_popup_menu                   (GtkWidget                *widget);
+static void           thunar_shortcuts_view_row_activated                (GtkTreeView              *tree_view,
+                                                                          GtkTreePath              *path,
+                                                                          GtkTreeViewColumn        *column);
+static gboolean       thunar_shortcuts_view_selection_func               (GtkTreeSelection         *selection,
+                                                                          GtkTreeModel             *model,
+                                                                          GtkTreePath              *path,
+                                                                          gboolean                  path_currently_selected,
+                                                                          gpointer                  user_data);
+static void           thunar_shortcuts_view_context_menu_visibility      (ThunarShortcutsView      *view,
+                                                                          GdkEventButton           *event,
+                                                                          GtkTreeModel             *model);
+static void           thunar_shortcuts_view_context_menu                 (ThunarShortcutsView      *view,
+                                                                          GdkEventButton           *event,
+                                                                          GtkTreeModel             *model,
+                                                                          GtkTreeIter              *iter);
+static void           thunar_shortcuts_view_remove_activated             (GtkWidget                *item,
+                                                                          GtkTreeModel             *model);
+static void           thunar_shortcuts_view_editing_canceled             (GtkCellRenderer          *renderer,
+                                                                          ThunarShortcutsView      *view);
+static void           thunar_shortcuts_view_rename_activated             (GtkWidget                *item,
+                                                                          ThunarShortcutsView      *view);
+static void           thunar_shortcuts_view_renamed                      (GtkCellRenderer          *renderer,
+                                                                          const gchar              *path_string,
+                                                                          const gchar              *text,
+                                                                          ThunarShortcutsView      *view);
+static GdkDragAction  thunar_shortcuts_view_compute_drop_actions         (ThunarShortcutsView      *view,
+                                                                          GdkDragContext           *context,
+                                                                          gint                      x,
+                                                                          gint                      y,
+                                                                          GtkTreePath             **path_return,
+                                                                          GdkDragAction            *action_return,
+                                                                          GtkTreeViewDropPosition  *position_return);
+static GtkTreePath   *thunar_shortcuts_view_compute_drop_position        (ThunarShortcutsView      *view,
+                                                                          gint                      x,
+                                                                          gint                      y);
+static void           thunar_shortcuts_view_drop_uri_list                (ThunarShortcutsView      *view,
+                                                                          GList                    *path_list,
+                                                                          GtkTreePath              *dst_path);
+static void           thunar_shortcuts_view_open_clicked                 (ThunarShortcutsView      *view);
+static void           thunar_shortcuts_view_open                         (ThunarShortcutsView      *view,
+                                                                          OpenTarget                open_in);
+static void           thunar_shortcuts_view_open_in_new_window_clicked   (ThunarShortcutsView      *view);
+static void           thunar_shortcuts_view_open_in_new_tab_clicked      (ThunarShortcutsView      *view);
+static void           thunar_shortcuts_view_empty_trash                  (ThunarShortcutsView      *view);
+static void           thunar_shortcuts_view_create_shortcut              (ThunarShortcutsView      *view);
+static void           thunar_shortcuts_view_eject                        (ThunarShortcutsView      *view);
+static void           thunar_shortcuts_view_mount                        (ThunarShortcutsView      *view);
+static void           thunar_shortcuts_view_unmount                      (ThunarShortcutsView      *view);
+
+
+
+struct _ThunarShortcutsViewClass
+{
+  GtkTreeViewClass __parent__;
+};
+
+struct _ThunarShortcutsView
+{
+  GtkTreeView        __parent__;
+
+
+  ThunarPreferences      *preferences;
+  GtkCellRenderer        *icon_renderer;
+
+  ThunarxProviderFactory *provider_factory;
+
+  /* the currently pressed mouse button, set in the
+   * button-press-event handler if the associated
+   * button-release-event should activate.
+   */
+  gint  pressed_button;
+  guint pressed_eject_button : 1;
+
+  /* drop site support */
+  guint  drop_data_ready : 1; /* whether the drop data was received already */
+  guint  drop_occurred : 1;
+  GList *drop_file_list;      /* the list of URIs that are contained in the drop data */
+
+  /* id of the signal used to queue a resize on the
+   * column whenever the shortcuts icon size is changed.
+   */
+  gulong queue_resize_signal_id;
+};
+
+
+
+/* Target types for dragging from the shortcuts view */
+static const GtkTargetEntry drag_targets[] = {
+  { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
+};
+
+/* Target types for dropping into the shortcuts view */
+static const GtkTargetEntry drop_targets[] = {
+  { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
+  { "text/uri-list", 0, TEXT_URI_LIST },
+};
+
+
+
+static guint view_signals[LAST_SIGNAL];
+
+
+
+G_DEFINE_TYPE_WITH_CODE (ThunarShortcutsView, thunar_shortcuts_view, GTK_TYPE_TREE_VIEW,
+                         G_IMPLEMENT_INTERFACE (THUNAR_TYPE_BROWSER, NULL))
+
+
+
+static void
+thunar_shortcuts_view_class_init (ThunarShortcutsViewClass *klass)
+{
+  GtkTreeViewClass *gtktree_view_class;
+  GtkWidgetClass   *gtkwidget_class;
+  GObjectClass     *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = thunar_shortcuts_view_finalize;
+
+  gtkwidget_class = GTK_WIDGET_CLASS (klass);
+  gtkwidget_class->button_press_event = thunar_shortcuts_view_button_press_event;
+  gtkwidget_class->button_release_event = thunar_shortcuts_view_button_release_event;
+  gtkwidget_class->key_release_event = thunar_shortcuts_view_key_release_event;
+  gtkwidget_class->drag_begin = thunar_shortcuts_view_drag_begin;
+  gtkwidget_class->drag_data_received = thunar_shortcuts_view_drag_data_received;
+  gtkwidget_class->drag_drop = thunar_shortcuts_view_drag_drop;
+  gtkwidget_class->drag_motion = thunar_shortcuts_view_drag_motion;
+  gtkwidget_class->drag_leave = thunar_shortcuts_view_drag_leave;
+  gtkwidget_class->popup_menu = thunar_shortcuts_view_popup_menu;
+
+  gtktree_view_class = GTK_TREE_VIEW_CLASS (klass);
+  gtktree_view_class->row_activated = thunar_shortcuts_view_row_activated;
+
+  /**
+   * ThunarShortcutsView:shortcut-activated:
+   *
+   * Invoked whenever a shortcut is activated by the user.
+   **/
+  view_signals[SHORTCUT_ACTIVATED] =
+    g_signal_new (I_("shortcut-activated"),
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1, THUNAR_TYPE_FILE);
+
+  /**
+   * ThunarShortcutsView:shortcut-activated-tab:
+   *
+   * Invoked whenever a shortcut is activated by the user and should be opened in a new tab,
+   **/
+  view_signals[SHORTCUT_ACTIVATED_TAB] =
+    g_signal_new (I_("shortcut-activated-tab"),
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1, THUNAR_TYPE_FILE);
+}
+
+
+
+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);
+  gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (view), THUNAR_SHORTCUTS_MODEL_COLUMN_TOOLTIP);
+
+  /* grab a reference on the provider factory */
+  view->provider_factory = thunarx_provider_factory_get_default ();
+
+  /* grab a reference on the preferences; be sure to redraw the view
+   * whenever the "shortcuts-icon-emblems" preference changes.
+   */
+  view->preferences = thunar_preferences_get ();
+  g_signal_connect_swapped (G_OBJECT (view->preferences), "notify::shortcuts-icon-emblems", G_CALLBACK (gtk_widget_queue_draw), view);
+
+  /* allocate a single column for our renderers */
+  column = g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
+                         "reorderable", FALSE,
+                         "resizable", FALSE,
+                         "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
+                         "spacing", 2,
+                         NULL);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+  /* queue a resize on the column whenever the icon size is changed */
+  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,
+                           "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_IS_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_IS_ITEM,
+                                       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);
+  gtk_tree_view_column_set_attributes (column, view->icon_renderer,
+                                       "gicon", THUNAR_SHORTCUTS_MODEL_COLUMN_GICON,
+                                       "file", THUNAR_SHORTCUTS_MODEL_COLUMN_FILE,
+                                       "device", THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE,
+                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_IS_ITEM,
+                                       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.
+   */
+  exo_binding_new (G_OBJECT (view->preferences), "shortcuts-icon-size", G_OBJECT (view->icon_renderer), "size");
+  exo_binding_new (G_OBJECT (view->preferences), "shortcuts-icon-emblems", G_OBJECT (view->icon_renderer), "emblems");
+
+  /* allocate the text renderer (ellipsizing as required, but "File System" must fit) */
+  renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
+                           "ellipsize", PANGO_ELLIPSIZE_END,
+                           NULL);
+  g_signal_connect (G_OBJECT (renderer), "edited", G_CALLBACK (thunar_shortcuts_view_renamed), 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_IS_ITEM,
+                                       NULL);
+
+  /* spinner to indicate (un)mount/eject delay */
+  renderer = gtk_cell_renderer_spinner_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_attributes (column, renderer,
+                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY,
+                                       "active", THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY,
+                                       "pulse", THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY_PULSE,
+                                       NULL);
+
+  /* allocate icon renderer for the eject symbol */
+  renderer = gtk_cell_renderer_pixbuf_new ();
+  g_object_set (renderer, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, "icon-name", "media-eject", NULL);
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_attributes (column, renderer,
+                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_CAN_EJECT,
+                                       NULL);
+
+  /* enable drag support for the shortcuts view (actually used to support reordering) */
+  gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (view), GDK_BUTTON1_MASK, drag_targets,
+                                          G_N_ELEMENTS (drag_targets), GDK_ACTION_MOVE);
+
+  /* enable drop support for the shortcuts view (both internal reordering
+   * and adding new shortcuts from other widgets)
+   */
+  gtk_drag_dest_set (GTK_WIDGET (view), 0, drop_targets, G_N_ELEMENTS (drop_targets),
+                     GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE);
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+  gtk_tree_selection_set_select_function (selection, thunar_shortcuts_view_selection_func, NULL, NULL);
+}
+
+
+
+static void
+thunar_shortcuts_view_finalize (GObject *object)
+{
+  ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (object);
+
+  /* release drop path list (if drag_leave wasn't called) */
+  thunar_g_file_list_free (view->drop_file_list);
+
+  /* release the provider factory */
+  g_object_unref (G_OBJECT (view->provider_factory));
+
+  /* disconnect the queue resize signal handler */
+  g_signal_handler_disconnect (G_OBJECT (view->preferences), view->queue_resize_signal_id);
+
+  /* disconnect from the preferences object */
+  g_signal_handlers_disconnect_matched (G_OBJECT (view->preferences), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, view);
+  g_object_unref (G_OBJECT (view->preferences));
+
+  (*G_OBJECT_CLASS (thunar_shortcuts_view_parent_class)->finalize) (object);
+}
+
+
+
+static gboolean
+thunar_shortcuts_view_button_press_event (GtkWidget      *widget,
+                                          GdkEventButton *event)
+{
+  ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
+  GtkTreeModel        *model;
+  GtkTreePath         *path;
+  GtkTreeIter          iter;
+  gboolean             result;
+  gboolean             can_eject;
+  gint                 icon_width, icon_height, column_width;
+
+  /* reset the pressed button state */
+  view->pressed_button = -1;
+  view->pressed_eject_button = 0;
+
+  /* completely ignore double click events */
+  if (event->type == GDK_2BUTTON_PRESS)
+    return TRUE;
+
+  /* let the widget process the event first (handles focussing and scrolling) */
+  result = (*GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->button_press_event) (widget, event);
+
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+
+  /* resolve the path at the cursor position */
+  if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), event->x, event->y, &path, NULL, NULL, NULL))
+    {
+      /* check if we should popup the context menu */
+      if (G_UNLIKELY (event->button == 3))
+        {
+          /* determine the iterator for the path */
+          if (gtk_tree_model_get_iter (model, &iter, path))
+            {
+              /* popup the context menu */
+              thunar_shortcuts_view_context_menu (view, event, model, &iter);
+
+              /* we effectively handled the event */
+              result = TRUE;
+            }
+        }
+      else if (event->button == 1 || event->button == 2)
+        {
+          /* check if we clicked the eject button area */
+          column_width = gtk_tree_view_column_get_width (gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0));
+          gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height);
+          if (event->button == 1 && event->x >= column_width - icon_width - 3)
+            {
+              /* check if that shortcut actually has an eject button */
+              model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+              if (gtk_tree_model_get_iter (model, &iter, path))
+                {
+                  gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_CAN_EJECT, &can_eject, -1);
+                  if (can_eject)
+                    view->pressed_eject_button = 1;
+                }
+            }
+          else
+            gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0), FALSE);
+
+          /* remember the button as pressed and handle it in the release handler */
+          view->pressed_button = event->button;
+        }
+
+      /* release the path */
+      gtk_tree_path_free (path);
+    }
+  else if (event->button == 3)
+    {
+      thunar_shortcuts_view_context_menu_visibility (view, event, model);
+      result = TRUE;
+    }
+
+  return result;
+}
+
+
+
+static gboolean
+thunar_shortcuts_view_button_release_event (GtkWidget      *widget,
+                                            GdkEventButton *event)
+{
+  ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
+  gboolean             in_tab;
+
+  /* check if we have an event matching the pressed button state */
+  if (G_LIKELY (view->pressed_button == (gint) event->button))
+    {
+      /* check if the eject button has been pressed */
+      if (view->pressed_eject_button)
+        {
+          thunar_shortcuts_view_eject (view);
+        }
+      else if (G_LIKELY (event->button == 1))
+        {
+          /* button 1 opens in the same window */
+          thunar_shortcuts_view_open (view, OPEN_IN_VIEW);
+        }
+      else if (G_UNLIKELY (event->button == 2))
+        {
+          /* button 2 opens in a new window or tab */
+          g_object_get (view->preferences, "misc-middle-click-in-tab", &in_tab, NULL);
+
+          /* holding ctrl inverts the action */
+          if ((event->state & GDK_CONTROL_MASK) != 0)
+            in_tab = !in_tab;
+
+          thunar_shortcuts_view_open (view, in_tab ? OPEN_IN_TAB : OPEN_IN_WINDOW);
+        }
+    }
+
+  /* reset the pressed button state */
+  view->pressed_button = -1;
+  view->pressed_eject_button = 0;
+
+  /* call the parent's release event handler */
+  return (*GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->button_release_event) (widget, event);
+}
+
+
+
+static gboolean
+thunar_shortcuts_view_key_release_event (GtkWidget   *widget,
+                                         GdkEventKey *event)
+{
+  ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
+
+  /* work nicer with keyboard navigation */
+  switch (event->keyval)
+    {
+    case GDK_KEY_Up:
+    case GDK_KEY_Down:
+    case GDK_KEY_KP_Up:
+    case GDK_KEY_KP_Down:
+      thunar_shortcuts_view_open (view, OPEN_IN_VIEW);
+
+      /* keep focus on us */
+      gtk_widget_grab_focus (widget);
+      break;
+    }
+
+  /* call the parent's release event handler */
+  return (*GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->key_release_event) (widget, event);
+}
+
+
+
+static void
+thunar_shortcuts_view_drag_begin (GtkWidget      *widget,
+                                  GdkDragContext *context)
+{
+  ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
+
+  /* reset the pressed button state */
+  view->pressed_button = -1;
+
+  /* call the parent's drag_begin() handler */
+  if (GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->drag_begin != NULL)
+    (*GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->drag_begin) (widget, context);
+}
+
+
+
+static void
+thunar_shortcuts_view_drag_data_received (GtkWidget        *widget,
+                                          GdkDragContext   *context,
+                                          gint              x,
+                                          gint              y,
+                                          GtkSelectionData *selection_data,
+                                          guint             info,
+                                          guint             timestamp)
+{
+  GtkTreeViewDropPosition position = GTK_TREE_VIEW_DROP_BEFORE;
+  ThunarShortcutsView    *view = THUNAR_SHORTCUTS_VIEW (widget);
+  GdkDragAction           actions;
+  GdkDragAction           action;
+  GtkTreeModel           *model;
+  GtkTreePath            *path;
+  GtkTreeIter             iter;
+  ThunarFile             *file;
+  gboolean                succeed = FALSE;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
+
+  /* check if we don't already know the drop data */
+  if (G_LIKELY (!view->drop_data_ready))
+    {
+      /* extract the URI list from the selection data (if valid) */
+      if (info == TEXT_URI_LIST && gtk_selection_data_get_format (selection_data) == 8 && gtk_selection_data_get_length (selection_data) > 0)
+        view->drop_file_list = thunar_g_file_list_new_from_string ((const gchar *) gtk_selection_data_get_data (selection_data));
+
+      /* reset the state */
+      view->drop_data_ready = TRUE;
+    }
+
+  /* check if the data was droppped */
+  if (G_UNLIKELY (view->drop_occurred))
+    {
+      /* reset the state */
+      view->drop_occurred = FALSE;
+
+      /* verify that we only handle text/uri-list here */
+      if (G_LIKELY (info == TEXT_URI_LIST))
+        {
+          /* determine the drop actions */
+          actions = thunar_shortcuts_view_compute_drop_actions (view, context, x, y, &path, &action, &position);
+          if (G_LIKELY (actions != 0))
+            {
+              /* check if we should add a shortcut */
+              if (position == GTK_TREE_VIEW_DROP_BEFORE || position == GTK_TREE_VIEW_DROP_AFTER)
+                {
+                  /* if position is "after", we need to advance the path,
+                   * as the drop_uri_list() will insert "at" the path.
+                   */
+                  if (position == GTK_TREE_VIEW_DROP_AFTER)
+                    gtk_tree_path_next (path);
+
+                  /* just add the required shortcuts then */
+                  thunar_shortcuts_view_drop_uri_list (view, view->drop_file_list, path);
+                }
+              else if (G_LIKELY ((actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)) != 0))
+                {
+                  /* get the shortcuts model */
+                  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+
+                  /* determine the iterator for the path */
+                  if (gtk_tree_model_get_iter (model, &iter, path))
+                    {
+                      /* determine the file for the iter */
+                      gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_FILE, &file, -1);
+                      if (G_LIKELY (file != NULL))
+                        {
+                          /* ask the user what to do with the drop data */
+                          if (G_UNLIKELY (action == GDK_ACTION_ASK))
+                            action = thunar_dnd_ask (widget, file, view->drop_file_list, timestamp, actions);
+
+                          /* perform the requested action */
+                          if (G_LIKELY (action != 0))
+                            {
+                              /* really perform the drop :-) */
+                              succeed = thunar_dnd_perform (widget, file, view->drop_file_list, action, NULL);
+                            }
+
+                          /* release the file */
+                          g_object_unref (G_OBJECT (file));
+                        }
+                    }
+                }
+
+              /* release the tree path */
+              gtk_tree_path_free (path);
+            }
+        }
+
+      /* disable the drop highlighting */
+      thunar_shortcuts_view_drag_leave (widget, context, timestamp);
+
+      /* tell the peer that we handled the drop */
+      gtk_drag_finish (context, succeed, FALSE, timestamp);
+    }
+  else
+    {
+      gdk_drag_status (context, 0, timestamp);
+    }
+}
+
+
+
+static gboolean
+thunar_shortcuts_view_drag_drop (GtkWidget      *widget,
+                                 GdkDragContext *context,
+                                 gint            x,
+                                 gint            y,
+                                 guint           timestamp)
+{
+  ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
+  GtkTreeSelection    *selection;
+  GtkTreeModel        *model;
+  GtkTreePath         *dst_path;
+  GtkTreePath         *src_path;
+  GtkTreeIter          iter;
+  GdkAtom              target;
+  GtkTreeModel        *child_model;
+  GtkTreePath         *child_dst_path;
+  GtkTreePath         *child_src_path;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view), FALSE);
+
+  /* determine the drop target */
+  target = gtk_drag_dest_find_target (widget, context, NULL);
+  if (G_LIKELY (target == gdk_atom_intern_static_string ("text/uri-list")))
+    {
+      /* set state so the drag-data-received handler
+       * knows that this is really a drop this time.
+       */
+      view->drop_occurred = TRUE;
+
+      /* request the drag data from the source. */
+      gtk_drag_get_data (widget, context, target, timestamp);
+    }
+  else if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
+    {
+      /* compute the drop position */
+      dst_path = thunar_shortcuts_view_compute_drop_position (view, x, y);
+
+      /* determine the source path */
+      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+      if (gtk_tree_selection_get_selected (selection, &model, &iter))
+        {
+          /* get the source path */
+          src_path = gtk_tree_model_get_path (model, &iter);
+
+          /* if we drop downwards, correct the drop destination */
+          if (gtk_tree_path_compare (src_path, dst_path) < 0)
+            gtk_tree_path_prev (dst_path);
+
+          /* convert iters */
+          src_path = gtk_tree_model_get_path (model, &iter);
+          child_src_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model), src_path);
+          child_dst_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model), dst_path);
+          gtk_tree_path_free (src_path);
+
+          child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+
+          /* perform the move */
+          thunar_shortcuts_model_move (THUNAR_SHORTCUTS_MODEL (child_model), child_src_path, child_dst_path);
+
+          gtk_tree_path_free (child_src_path);
+          gtk_tree_path_free (child_dst_path);
+
+          /* make sure the new position is selectde */
+          gtk_tree_selection_select_path (selection, dst_path);
+        }
+
+      /* release the dst path */
+      gtk_tree_path_free (dst_path);
+
+      /* finish the dnd operation */
+      gtk_drag_finish (context, TRUE, FALSE, timestamp);
+    }
+  else
+    {
+      /* we cannot handle the drop */
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+
+
+static gboolean
+thunar_shortcuts_view_drag_motion (GtkWidget      *widget,
+                                   GdkDragContext *context,
+                                   gint            x,
+                                   gint            y,
+                                   guint           timestamp)
+{
+  GtkTreeViewDropPosition position = GTK_TREE_VIEW_DROP_BEFORE;
+  ThunarShortcutsView    *view = THUNAR_SHORTCUTS_VIEW (widget);
+  GdkDragAction           action = 0;
+  GtkTreeModel           *model;
+  GtkTreePath            *path = NULL;
+  GdkAtom                 target;
+
+  /* reset the "drop-file" of the icon renderer */
+  g_object_set (G_OBJECT (view->icon_renderer), "drop-file", NULL, NULL);
+
+  /* determine the drag target */
+  target = gtk_drag_dest_find_target (widget, context, NULL);
+  if (target == gdk_atom_intern_static_string ("text/uri-list"))
+    {
+      /* request the drop data on-demand (if we don't have it already) */
+      if (G_UNLIKELY (!view->drop_data_ready))
+        {
+          /* request the drag data from the source */
+          gtk_drag_get_data (widget, context, target, timestamp);
+
+          /* gdk_drag_status() will be called by drag_data_received */
+          return TRUE;
+        }
+      else
+        {
+          /* compute the drop position */
+          thunar_shortcuts_view_compute_drop_actions (view, context, x, y, &path, &action, &position);
+        }
+    }
+  else if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
+    {
+      /* check the action that should be performed */
+      if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_LINK || (gdk_drag_context_get_actions (context) & GDK_ACTION_LINK) != 0)
+        action = GDK_ACTION_LINK;
+      else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY || (gdk_drag_context_get_actions (context) & GDK_ACTION_COPY) != 0)
+        action = GDK_ACTION_COPY;
+      else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_MOVE || (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
+        action = GDK_ACTION_MOVE;
+      else
+        return FALSE;
+
+      /* 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));
+      if (gtk_tree_path_get_indices (path)[0] >= gtk_tree_model_iter_n_children (model, NULL))
+        {
+          /* set the position to "after" and move the path to
+           * point to the previous row instead; required to
+           * get the highlighting in GtkTreeView correct.
+           */
+          position = GTK_TREE_VIEW_DROP_AFTER;
+          gtk_tree_path_prev (path);
+        }
+    }
+  else
+    {
+      /* we cannot handle the drop */
+      return FALSE;
+    }
+
+  /* check if we have a drop path */
+  if (G_LIKELY (path != NULL))
+    {
+      /* highlight the appropriate row */
+      gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (view), path, position);
+      gtk_tree_path_free (path);
+    }
+
+  /* tell Gdk whether we can drop here */
+  gdk_drag_status (context, action, timestamp);
+
+  return TRUE;
+}
+
+
+
+static void
+thunar_shortcuts_view_drag_leave (GtkWidget      *widget,
+                                  GdkDragContext *context,
+                                  guint           timestamp)
+{
+  ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
+
+  /* reset the "drop-file" of the icon renderer */
+  g_object_set (G_OBJECT (view->icon_renderer), "drop-file", NULL, NULL);
+
+  /* reset the "drop data ready" status and free the URI list */
+  if (G_LIKELY (view->drop_data_ready))
+    {
+      thunar_g_file_list_free (view->drop_file_list);
+      view->drop_data_ready = FALSE;
+      view->drop_file_list = NULL;
+    }
+
+  /* schedule a repaint to make sure the special drop icon for the target row
+   * is reset to its default (http://bugzilla.xfce.org/show_bug.cgi?id=2498).
+   */
+  gtk_widget_queue_draw (widget);
+
+  /* call the parent's handler */
+  (*GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->drag_leave) (widget, context, timestamp);
+}
+
+
+
+static gboolean
+thunar_shortcuts_view_popup_menu (GtkWidget *widget)
+{
+  ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
+  GtkTreeSelection    *selection;
+  GtkTreeModel        *model;
+  GtkTreeIter          iter;
+
+  /* determine the selection for the tree view */
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+  /* determine the selected row */
+  if (gtk_tree_selection_get_selected (selection, &model, &iter))
+    {
+      /* popup the context menu */
+      thunar_shortcuts_view_context_menu (view, NULL, model, &iter);
+      return TRUE;
+    }
+  else if (GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->popup_menu != NULL)
+    {
+      /* call the parent's "popup-menu" handler */
+      return (*GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->popup_menu) (widget);
+    }
+
+  return FALSE;
+}
+
+
+
+static void
+thunar_shortcuts_view_row_activated (GtkTreeView       *tree_view,
+                                     GtkTreePath       *path,
+                                     GtkTreeViewColumn *column)
+{
+  ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (tree_view);
+
+  /* call the parent's "row-activated" handler */
+  if (GTK_TREE_VIEW_CLASS (thunar_shortcuts_view_parent_class)->row_activated != NULL)
+    (*GTK_TREE_VIEW_CLASS (thunar_shortcuts_view_parent_class)->row_activated) (tree_view, path, column);
+
+  /* open the selected shortcut */
+  thunar_shortcuts_view_open (view, OPEN_IN_VIEW);
+}
+
+
+
+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_IS_HEADER, &is_header, -1);
+  return !is_header;
+}
+
+
+
+static void
+thunar_shortcuts_view_context_menu_visibility_toggled (GtkCheckMenuItem *item,
+                                                       GtkTreeModel     *model)
+{
+  gboolean             hidden;
+  GtkTreePath         *path;
+  GtkTreeRowReference *row;
+
+  _thunar_return_if_fail (GTK_IS_CHECK_MENU_ITEM (item));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
+
+  row = g_object_get_data (G_OBJECT (item), I_("thunar-shortcuts-row"));
+  path = gtk_tree_row_reference_get_path (row);
+  if (G_LIKELY (path != NULL))
+    {
+      hidden = !gtk_check_menu_item_get_active (item);
+      thunar_shortcuts_model_set_hidden (THUNAR_SHORTCUTS_MODEL (model), path, hidden);
+      gtk_tree_path_free (path);
+    }
+}
+
+
+
+static void
+thunar_shortcuts_view_context_menu_visibility (ThunarShortcutsView *view,
+                                               GdkEventButton      *event,
+                                               GtkTreeModel        *model)
+{
+  GtkTreeIter          iter;
+  guint                mask = 0;
+  ThunarShortcutGroup  group;
+  GtkWidget           *menu;
+  GtkWidget           *mi;
+  gchar               *label;
+  GtkTreeSelection    *selection;
+  gboolean             visible;
+  GtkTreePath         *path;
+  GtkTreeModel        *child_model;
+  GtkWidget           *submenu;
+
+  /* unselect the items */
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+  gtk_tree_selection_unselect_all (GTK_TREE_SELECTION (selection));
+
+  /* prepare the popup menu */
+  menu = submenu = gtk_menu_new ();
+
+  /* process all items below the header */
+  child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+  if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (child_model), &iter))
+      return;
+  path = gtk_tree_model_get_path (child_model, &iter);
+  do
+    {
+      /* get all the info */
+      gtk_tree_model_get (GTK_TREE_MODEL (child_model), &iter,
+                          THUNAR_SHORTCUTS_MODEL_COLUMN_GROUP, &group,
+                          THUNAR_SHORTCUTS_MODEL_COLUMN_NAME, &label,
+                          THUNAR_SHORTCUTS_MODEL_COLUMN_VISIBLE, &visible,
+                          -1);
+
+      if ((group & THUNAR_SHORTCUT_GROUP_HEADER) != 0)
+        {
+          /* get the mask of the group */
+          if ((group & THUNAR_SHORTCUT_GROUP_DEVICES) != 0)
+            mask = THUNAR_SHORTCUT_GROUP_DEVICES;
+          else if ((group & THUNAR_SHORTCUT_GROUP_PLACES) != 0)
+            mask = THUNAR_SHORTCUT_GROUP_PLACES;
+          else if ((group & THUNAR_SHORTCUT_GROUP_NETWORK) != 0)
+            mask = THUNAR_SHORTCUT_GROUP_NETWORK;
+          else
+            _thunar_assert_not_reached ();
+
+          /* create menu items */
+          mi = gtk_menu_item_new_with_label (label);
+          gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
+          gtk_widget_show (mi);
+
+          submenu = gtk_menu_new ();
+          gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), submenu);
+        }
+      else if ((group & mask) != 0)
+        {
+          mi = gtk_check_menu_item_new_with_label (label);
+          gtk_menu_shell_append (GTK_MENU_SHELL (submenu), mi);
+          gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi), visible);
+          g_object_set_data_full (G_OBJECT (mi), I_("thunar-shortcuts-row"),
+                                  gtk_tree_row_reference_new (child_model, path),
+                                  (GDestroyNotify) gtk_tree_row_reference_free);
+          g_signal_connect (G_OBJECT (mi), "toggled",
+            G_CALLBACK (thunar_shortcuts_view_context_menu_visibility_toggled), child_model);
+          gtk_widget_show (mi);
+        }
+
+      g_free (label);
+      gtk_tree_path_next (path);
+    }
+  while (gtk_tree_model_iter_next (child_model, &iter));
+
+  gtk_tree_path_free (path);
+
+  /* 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,
+                       (event != NULL) ? event->time : gtk_get_current_event_time ());
+}
+
+
+
+static void
+thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
+                                    GdkEventButton      *event,
+                                    GtkTreeModel        *model,
+                                    GtkTreeIter         *iter)
+{
+  GtkTreePath         *path;
+  ThunarFile          *file;
+  GtkWidget           *image;
+  GtkWidget           *menu;
+  GtkWidget           *item;
+  GtkWidget           *window;
+  gboolean             mutable;
+  ThunarDevice        *device;
+  GList               *providers, *lp;
+  GList               *actions = NULL, *tmp;
+  ThunarShortcutGroup  group;
+  gboolean             is_header;
+  GFile               *mount_point;
+  GtkTreeModel        *shortcuts_model;
+  gboolean             can_mount;
+  gboolean             can_unmount;
+  gboolean             can_eject;
+
+  /* check if this is an item menu or a header menu */
+  gtk_tree_model_get (model, iter, THUNAR_SHORTCUTS_MODEL_COLUMN_IS_HEADER, &is_header, -1);
+  if (is_header)
+    {
+      thunar_shortcuts_view_context_menu_visibility (view, event, model);
+      return;
+    }
+
+  /* determine the tree path for the given iter */
+  path = gtk_tree_model_get_path (model, iter);
+  if (G_UNLIKELY (path == NULL))
+    return;
+
+  /* check whether the shortcut at the given path is mutable */
+  gtk_tree_model_get (model, iter,
+                      THUNAR_SHORTCUTS_MODEL_COLUMN_FILE, &file,
+                      THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE, &device,
+                      THUNAR_SHORTCUTS_MODEL_COLUMN_MUTABLE, &mutable,
+                      THUNAR_SHORTCUTS_MODEL_COLUMN_GROUP, &group,
+                      -1);
+
+  /* prepare the popup menu */
+  menu = gtk_menu_new ();
+
+  /* append the "Open" menu action */
+  item = gtk_image_menu_item_new_with_mnemonic (_("_Open"));
+  g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_open_clicked), view);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+  gtk_widget_show (item);
+
+  /* set the stock icon */
+  image = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU);
+  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+
+  /* append the "Open in New Tab" menu action */
+  item = gtk_image_menu_item_new_with_mnemonic (_("Open in New Tab"));
+  g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_open_in_new_tab_clicked), view);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+  gtk_widget_show (item);
+
+  /* append the "Open in New Window" menu action */
+  item = gtk_image_menu_item_new_with_mnemonic (_("Open in New Window"));
+  g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_open_in_new_window_clicked), view);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+  gtk_widget_show (item);
+
+  switch (group)
+    {
+      case THUNAR_SHORTCUT_GROUP_DEVICES_VOLUMES:
+        can_mount = thunar_device_can_mount (device);
+        can_unmount = thunar_device_can_unmount (device);
+        can_eject = thunar_device_can_eject (device);
+
+        if (!can_mount && !can_unmount && !can_eject)
+          break;
+
+        /* 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 "Mount" item */
+        item = gtk_image_menu_item_new_with_mnemonic (_("_Mount"));
+        gtk_widget_set_visible (item, can_mount);
+        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, can_unmount);
+        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 (_("_Eject"));
+        gtk_widget_set_visible (item, can_eject);
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+        g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_eject), view);
+        break;
+
+      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);
+
+        /* get the mount point */
+        mount_point = thunar_device_get_root (device);
+        shortcuts_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+
+        /* append the "Disconnect" item */
+        item = gtk_image_menu_item_new_with_mnemonic (_("Create _Shortcut"));
+        gtk_widget_set_sensitive (item, mount_point != NULL && !thunar_shortcuts_model_has_bookmark (THUNAR_SHORTCUTS_MODEL (shortcuts_model), mount_point));
+        g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_create_shortcut), view);
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+        gtk_widget_show (item);
+
+        image = gtk_image_new_from_stock (THUNAR_STOCK_SHORTCUTS, GTK_ICON_SIZE_MENU);
+        gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+
+        if (mount_point != NULL)
+          g_object_unref (mount_point);
+
+        /* fall-through */
+
+      case THUNAR_SHORTCUT_GROUP_DEVICES_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"));
+        gtk_widget_set_sensitive (item, thunar_device_can_eject (device));
+        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);
+
+        image = gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU);
+        gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+        break;
+
+      case THUNAR_SHORTCUT_GROUP_PLACES_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 */
+  if (G_LIKELY (file != NULL && !thunar_file_is_trashed (file)))
+    {
+      /* load the menu providers from the provider factory */
+      providers = thunarx_provider_factory_list_providers (view->provider_factory, THUNARX_TYPE_MENU_PROVIDER);
+      if (G_LIKELY (providers != NULL))
+        {
+          /* determine the toplevel window we belong to */
+          window = gtk_widget_get_toplevel (GTK_WIDGET (view));
+
+          /* load the actions offered by the menu providers */
+          for (lp = providers; lp != NULL; lp = lp->next)
+            {
+              tmp = thunarx_menu_provider_get_folder_actions (lp->data, window, THUNARX_FILE_INFO (file));
+              actions = g_list_concat (actions, tmp);
+              g_object_unref (G_OBJECT (lp->data));
+            }
+          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)
+            {
+              item = gtk_action_create_menu_item (GTK_ACTION (lp->data));
+              gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+              gtk_widget_show (item);
+
+              /* release the reference on the action */
+              g_object_unref (G_OBJECT (lp->data));
+            }
+
+          /* cleanup */
+          g_list_free (actions);
+        }
+    }
+
+  /* append the remove menu item */
+  if (group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS)
+    {
+      /* append a menu separator */
+      item = gtk_separator_menu_item_new ();
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+      gtk_widget_show (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), model);
+      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);
+    }
+
+  /* clean up */
+  if (G_LIKELY (file != NULL))
+    g_object_unref (G_OBJECT (file));
+  if (G_UNLIKELY (device != NULL))
+    g_object_unref (G_OBJECT (device));
+  gtk_tree_path_free (path);
+
+  /* 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,
+                       (event != NULL) ? event->time : gtk_get_current_event_time ());
+}
+
+
+
+static void
+thunar_shortcuts_view_remove_activated (GtkWidget    *item,
+                                        GtkTreeModel *model)
+{
+  GtkTreeRowReference *row;
+  GtkTreeModel        *child_model;
+  GtkTreePath         *path;
+  GtkTreePath         *child_path;
+
+  row = g_object_get_data (G_OBJECT (item), I_("thunar-shortcuts-row"));
+  path = gtk_tree_row_reference_get_path (row);
+  if (G_LIKELY (path != NULL))
+    {
+      child_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model), path);
+      gtk_tree_path_free (path);
+
+      child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+      thunar_shortcuts_model_remove (THUNAR_SHORTCUTS_MODEL (child_model), child_path);
+      gtk_tree_path_free (child_path);
+    }
+}
+
+
+
+static void
+thunar_shortcuts_view_editing_canceled (GtkCellRenderer     *renderer,
+                                        ThunarShortcutsView *view)
+{
+  g_object_set (G_OBJECT (renderer), "editable", FALSE, NULL);
+
+  g_signal_handlers_disconnect_by_func (G_OBJECT (renderer),
+                                       G_CALLBACK (thunar_shortcuts_view_editing_canceled),
+                                       view);
+}
+
+
+
+static void
+thunar_shortcuts_view_rename_activated (GtkWidget           *item,
+                                        ThunarShortcutsView *view)
+{
+  GtkTreeRowReference *row;
+  GtkTreeViewColumn   *column;
+  GtkCellRenderer     *renderer;
+  GtkTreePath         *path;
+  GList               *renderers;
+
+  row = g_object_get_data (G_OBJECT (item), I_("thunar-shortcuts-row"));
+  path = gtk_tree_row_reference_get_path (row);
+  if (G_LIKELY (path != NULL))
+    {
+      /* 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, 3);
+
+      /* make sure the text renderer is editable */
+      g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
+
+      /* set up signals for the cell renderer */
+      g_signal_connect (G_OBJECT (renderer), "editing-canceled",
+                        G_CALLBACK (thunar_shortcuts_view_editing_canceled), view);
+
+      /* tell the tree view to start editing the given row */
+      gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (view), path, column, renderer, TRUE);
+
+      /* cleanup */
+      gtk_tree_path_free (path);
+      g_list_free (renderers);
+    }
+}
+
+
+
+static void
+thunar_shortcuts_view_renamed (GtkCellRenderer     *renderer,
+                               const gchar         *path_string,
+                               const gchar         *text,
+                               ThunarShortcutsView *view)
+{
+  GtkTreeModel *model;
+  GtkTreeIter   iter;
+  GtkTreeModel *child_model;
+  GtkTreeIter   child_iter;
+
+  /* reset the editable flag */
+  g_object_set (G_OBJECT (renderer), "editable", FALSE, NULL);
+
+  /* perform the rename */
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+  if (gtk_tree_model_get_iter_from_string (model, &iter, path_string))
+    {
+      child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+      gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter);
+      thunar_shortcuts_model_rename (THUNAR_SHORTCUTS_MODEL (child_model), &child_iter, text);
+    }
+}
+
+
+
+static GdkDragAction
+thunar_shortcuts_view_compute_drop_actions (ThunarShortcutsView     *view,
+                                            GdkDragContext          *context,
+                                            gint                     x,
+                                            gint                     y,
+                                            GtkTreePath            **path_return,
+                                            GdkDragAction           *action_return,
+                                            GtkTreeViewDropPosition *position_return)
+{
+  GtkTreeViewColumn *column;
+  GdkDragAction      actions = 0;
+  GdkRectangle       cell_area;
+  GtkTreeModel      *model;
+  GtkTreePath       *path;
+  GtkTreeIter        iter;
+  ThunarFile        *file;
+  gint               cell_y;
+
+  /* determine the shortcuts model */
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+
+  /* determine the path for x/y */
+  if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view), x, y, &path, &column, NULL, &cell_y))
+    {
+      /* determine the background area of the column */
+      gtk_tree_view_get_background_area (GTK_TREE_VIEW (view), path, column, &cell_area);
+
+      /* check if 1/6th inside the cell and the path is within the model */
+      if ((cell_y > (cell_area.height / 6) && cell_y < (cell_area.height - cell_area.height / 6))
+          && gtk_tree_model_get_iter (model, &iter, path))
+        {
+          /* determine the file for the iterator */
+          gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_FILE, &file, -1);
+          if (G_LIKELY (file != NULL))
+            {
+              /* check if the file accepts the drop */
+              actions = thunar_file_accepts_drop (file, view->drop_file_list, context, action_return);
+              if (G_LIKELY (actions != 0))
+                {
+                  /* we can drop into this location */
+                  *position_return = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
+                  *path_return = gtk_tree_path_copy (path);
+
+                  /* display a drop icon for the file */
+                  g_object_set (G_OBJECT (view->icon_renderer), "drop-file", file, NULL);
+                }
+
+              /* release the file */
+              g_object_unref (G_OBJECT (file));
+            }
+        }
+
+      /* release the path */
+      gtk_tree_path_free (path);
+    }
+
+  /* check if we cannot drop into the shortcut */
+  if (G_UNLIKELY (actions == 0))
+    {
+      /* check the action that should be performed */
+      if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_LINK || (gdk_drag_context_get_actions (context) & GDK_ACTION_LINK) != 0)
+        actions = GDK_ACTION_LINK;
+      else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY || (gdk_drag_context_get_actions (context) & GDK_ACTION_COPY) != 0)
+        actions = GDK_ACTION_COPY;
+      else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_MOVE || (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
+        actions = GDK_ACTION_MOVE;
+      else
+        return 0;
+
+      /* 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))
+        {
+          /* set the position to "after" and move the path to
+           * point to the previous row instead; required to
+           * get the highlighting in GtkTreeView correct.
+           */
+          *position_return = GTK_TREE_VIEW_DROP_AFTER;
+          gtk_tree_path_prev (path);
+        }
+      else
+        {
+          /* drop before the path */
+          *position_return = GTK_TREE_VIEW_DROP_BEFORE;
+        }
+
+      /* got it */
+      *action_return = actions;
+      *path_return = path;
+    }
+
+  return actions;
+}
+
+
+
+static GtkTreePath*
+thunar_shortcuts_view_compute_drop_position (ThunarShortcutsView *view,
+                                             gint                 x,
+                                             gint                 y)
+{
+  GtkTreeViewColumn *column;
+  GtkTreeModel      *model;
+  GdkRectangle       area;
+  GtkTreePath       *path;
+  gint               n_rows;
+  gboolean           result;
+  GtkTreePath       *child_path;
+  GtkTreeModel      *child_model;
+
+  _thunar_return_val_if_fail (gtk_tree_view_get_model (GTK_TREE_VIEW (view)) != NULL, NULL);
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view), NULL);
+
+  /* query the number of rows in the model */
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+  n_rows = gtk_tree_model_iter_n_children (model, NULL);
+
+  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
+       * (taking into account the relative y position)
+       */
+      gtk_tree_view_get_background_area (GTK_TREE_VIEW (view), path, column, &area);
+      if (y >= area.height / 2)
+        gtk_tree_path_next (path);
+
+      child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+
+      /* find a suitable drop path (we cannot drop into the default shortcuts list) */
+      for (; gtk_tree_path_get_indices (path)[0] < n_rows; gtk_tree_path_next (path))
+        {
+          child_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model), path);
+          result = thunar_shortcuts_model_drop_possible (THUNAR_SHORTCUTS_MODEL (child_model), child_path);
+          gtk_tree_path_free (child_path);
+          if (result)
+            return path;
+        }
+
+      gtk_tree_path_free (path);
+    }
+
+  return NULL;
+}
+
+
+
+static void
+thunar_shortcuts_view_drop_uri_list (ThunarShortcutsView *view,
+                                     GList               *path_list,
+                                     GtkTreePath         *dst_path)
+{
+  GtkTreeModel *model;
+  GtkTreePath  *path;
+  ThunarFile   *file;
+  GError       *error = NULL;
+  GList        *lp;
+  GtkTreeModel *child_model;
+  GtkTreePath  *child_path;
+
+  /* take a copy of the destination path */
+  path = gtk_tree_path_copy (dst_path);
+
+  /* process the URIs one-by-one and stop on error */
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+  child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+  for (lp = path_list; lp != NULL; lp = lp->next)
+    {
+      file = thunar_file_get (lp->data, &error);
+      if (G_UNLIKELY (file == NULL))
+        break;
+
+      /* make sure, that only directories gets added to the shortcuts list */
+      if (G_UNLIKELY (!thunar_file_is_directory (file)))
+        {
+          g_set_error (&error, G_FILE_ERROR, G_FILE_ERROR_NOTDIR,
+                       _("The path \"%s\" does not refer to a directory"),
+                       thunar_file_get_display_name (file));
+          g_object_unref (file);
+          break;
+        }
+
+      child_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model), path);
+      thunar_shortcuts_model_add (THUNAR_SHORTCUTS_MODEL (child_model), child_path, file);
+      gtk_tree_path_free (child_path);
+
+      g_object_unref (file);
+      gtk_tree_path_next (path);
+    }
+
+  /* release the tree path copy */
+  gtk_tree_path_free (path);
+
+  if (G_UNLIKELY (error != NULL))
+    {
+      /* display an error message to the user */
+      thunar_dialogs_show_error (GTK_WIDGET (view), error, _("Failed to add new shortcut"));
+
+      /* release the error */
+      g_error_free (error);
+    }
+}
+
+
+
+static void
+thunar_shortcuts_view_open_clicked (ThunarShortcutsView *view)
+{
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
+  thunar_shortcuts_view_open (view, OPEN_IN_VIEW);
+}
+
+
+
+static void
+thunar_shortcuts_view_poke_file_finish (ThunarBrowser *browser,
+                                        ThunarFile    *file,
+                                        ThunarFile    *target_file,
+                                        GError        *error,
+                                        gpointer       user_data)
+{
+  ThunarApplication *application;
+  OpenTarget         open_in = GPOINTER_TO_UINT (user_data);
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (browser));
+  _thunar_return_if_fail (THUNAR_IS_FILE (target_file));
+
+  if (error == NULL)
+    {
+      if (open_in == OPEN_IN_WINDOW)
+        {
+          /* open a new window for the target folder */
+          application = thunar_application_get ();
+          thunar_application_open_window (application, target_file,
+                                          gtk_widget_get_screen (GTK_WIDGET (browser)), NULL);
+          g_object_unref (application);
+        }
+      else if (open_in == OPEN_IN_TAB)
+        {
+          /* invoke the signal to change to open folder in a new tab */
+          g_signal_emit (browser, view_signals[SHORTCUT_ACTIVATED_TAB], 0, target_file);
+        }
+      else if (thunar_file_check_loaded (target_file))
+        {
+          /* invoke the signal to change to that folder */
+          g_signal_emit (browser, view_signals[SHORTCUT_ACTIVATED], 0, target_file);
+        }
+    }
+  else
+    {
+      thunar_dialogs_show_error (GTK_WIDGET (browser), error, _("Failed to open \"%s\""),
+                                 thunar_file_get_display_name (file));
+    }
+}
+
+
+
+static void
+thunar_shortcuts_view_poke_location_finish (ThunarBrowser *browser,
+                                            GFile         *location,
+                                            ThunarFile    *file,
+                                            ThunarFile    *target_file,
+                                            GError        *error,
+                                            gpointer       user_data)
+{
+  gchar *name;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (browser));
+  _thunar_return_if_fail (G_IS_FILE (location));
+
+  /* sotre the new file in the shortcuts model */
+  if (error == NULL)
+    {
+      thunar_shortcuts_view_poke_file_finish (browser, file, target_file, error, user_data);
+    }
+  else
+    {
+      name = thunar_g_file_get_display_name_remote (location);
+      thunar_dialogs_show_error (GTK_WIDGET (browser), error, _("Failed to open \"%s\""), name);
+      g_free (name);
+    }
+}
+
+
+
+static void
+thunar_shortcuts_view_poke_device_finish (ThunarBrowser *browser,
+                                          ThunarDevice  *device,
+                                          ThunarFile    *mount_point,
+                                          GError        *error,
+                                          gpointer       user_data)
+{
+  gchar        *device_name;
+  GtkTreeModel *model;
+  GtkTreeModel *child_model;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (browser));
+  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
+
+  if (error == NULL)
+    {
+      thunar_browser_poke_file (browser, mount_point, GTK_WIDGET (browser),
+                                thunar_shortcuts_view_poke_file_finish,
+                                user_data);
+    }
+  else
+    {
+      device_name = thunar_device_get_name (device);
+      thunar_dialogs_show_error (GTK_WIDGET (browser), error,
+                                 _("Failed to mount \"%s\""), device_name);
+      g_free (device_name);
+    }
+
+  /* stop the spinner */
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (browser));
+  child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+  thunar_shortcuts_model_set_busy (THUNAR_SHORTCUTS_MODEL (child_model), device, FALSE);
+}
+
+
+
+static void
+thunar_shortcuts_view_open (ThunarShortcutsView *view,
+                            OpenTarget           open_in)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel     *model;
+  GtkTreeIter       iter;
+  ThunarFile       *file;
+  ThunarDevice     *device;
+  GFile            *location;
+  GtkTreeModel     *child_model;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
+
+  /* determine the selected item */
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+  /* avoid dealing with invalid selections */
+  if (!GTK_IS_TREE_SELECTION (selection))
+    return;
+
+  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_FILE, &file,
+                          THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE, &device,
+                          THUNAR_SHORTCUTS_MODEL_COLUMN_LOCATION, &location,
+                          -1);
+
+      if (G_LIKELY (device != NULL))
+        {
+          /* start the spinner */
+          child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+          thunar_shortcuts_model_set_busy (THUNAR_SHORTCUTS_MODEL (child_model), device, TRUE);
+
+          thunar_browser_poke_device (THUNAR_BROWSER (view), device, view,
+                                      thunar_shortcuts_view_poke_device_finish,
+                                      GUINT_TO_POINTER (open_in));
+        }
+      else if (file != NULL)
+        {
+          thunar_browser_poke_file (THUNAR_BROWSER (view), file, view,
+                                    thunar_shortcuts_view_poke_file_finish,
+                                    GUINT_TO_POINTER (open_in));
+        }
+      else if (location != NULL)
+        {
+          thunar_browser_poke_location (THUNAR_BROWSER (view), location, view,
+                                        thunar_shortcuts_view_poke_location_finish,
+                                        GUINT_TO_POINTER (open_in));
+        }
+
+      if (file != NULL)
+        g_object_unref (file);
+
+      if (device != NULL)
+        g_object_unref (device);
+
+      if (location != NULL)
+        g_object_unref (location);
+    }
+}
+
+
+
+static void
+thunar_shortcuts_view_open_in_new_window_clicked (ThunarShortcutsView *view)
+{
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
+  thunar_shortcuts_view_open (view, OPEN_IN_WINDOW);
+}
+
+
+
+static void
+thunar_shortcuts_view_open_in_new_tab_clicked (ThunarShortcutsView *view)
+{
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
+  thunar_shortcuts_view_open (view, OPEN_IN_TAB);
+}
+
+
+
+static void
+thunar_shortcuts_view_empty_trash (ThunarShortcutsView *view)
+{
+  ThunarApplication *application;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
+
+  /* empty the trash bin (asking the user first) */
+  application = thunar_application_get ();
+  thunar_application_empty_trash (application, GTK_WIDGET (view), NULL);
+  g_object_unref (G_OBJECT (application));
+}
+
+
+
+static void
+thunar_shortcuts_view_create_shortcut (ThunarShortcutsView *view)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel     *model;
+  GtkTreeIter       iter;
+  ThunarDevice     *device;
+  GFile            *mount_point;
+  GtkTreeModel     *shortcuts_model;
+
+  _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 device/mount for the shortcut at the given tree iterator */
+      gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE, &device, -1);
+      _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
+
+      /* add the mount point to the model */
+      mount_point = thunar_device_get_root (device);
+      if (mount_point != NULL)
+        {
+          shortcuts_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+          thunar_shortcuts_model_add (THUNAR_SHORTCUTS_MODEL (shortcuts_model), NULL, mount_point);
+          g_object_unref (mount_point);
+        }
+
+      g_object_unref (G_OBJECT (device));
+    }
+}
+
+
+
+static void
+thunar_shortcuts_view_eject_finish (ThunarDevice *device,
+                                    const GError *error,
+                                    gpointer      user_data)
+{
+  ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (user_data);
+  gchar               *device_name;
+  GtkTreeModel        *model;
+  GtkTreeModel        *child_model;
+
+  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
+
+  /* check if there was an error */
+  if (error != NULL)
+    {
+      /* display an error dialog to inform the user */
+      device_name = thunar_device_get_name (device);
+      thunar_dialogs_show_error (GTK_WIDGET (view), error, _("Failed to eject \"%s\""), device_name);
+      g_free (device_name);
+    }
+
+  /* stop the spinner */
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+  child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+  thunar_shortcuts_model_set_busy (THUNAR_SHORTCUTS_MODEL (child_model), device, FALSE);
+
+  g_object_unref (view);
+}
+
+
+
+static void
+thunar_shortcuts_view_eject (ThunarShortcutsView *view)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel     *model;
+  GtkTreeIter       iter;
+  ThunarDevice     *device;
+  GMountOperation  *mount_operation;
+  GtkTreeModel     *child_model;
+
+  _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 device/mount for the shortcut at the given tree iterator */
+      gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE, &device, -1);
+      _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
+
+      /* prepare a mount operation */
+      mount_operation = thunar_gtk_mount_operation_new (GTK_WIDGET (view));
+
+      /* start the spinner */
+      child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+      thunar_shortcuts_model_set_busy (THUNAR_SHORTCUTS_MODEL (child_model), device, TRUE);
+
+      /* try to unmount */
+      thunar_device_eject (device,
+                           mount_operation,
+                           NULL,
+                           thunar_shortcuts_view_eject_finish,
+                           g_object_ref (view));
+
+      g_object_unref (G_OBJECT (device));
+      g_object_unref (G_OBJECT (mount_operation));
+    }
+}
+
+
+
+static void
+thunar_shortcuts_view_poke_device_mount_finish (ThunarBrowser *browser,
+                                                ThunarDevice  *device,
+                                                ThunarFile    *mount_point,
+                                                GError        *error,
+                                                gpointer       ignored)
+{
+  gchar        *device_name;
+  GtkTreeModel *model;
+  GtkTreeModel *child_model;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (browser));
+  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
+
+  if (error != NULL)
+    {
+      device_name = thunar_device_get_name (device);
+      thunar_dialogs_show_error (GTK_WIDGET (browser), error,
+                                 _("Failed to mount \"%s\""), device_name);
+      g_free (device_name);
+    }
+
+  /* stop the spinner */
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (browser));
+  child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+  thunar_shortcuts_model_set_busy (THUNAR_SHORTCUTS_MODEL (child_model), device, FALSE);
+}
+
+
+
+static void
+thunar_shortcuts_view_mount (ThunarShortcutsView *view)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel     *model;
+  GtkTreeIter       iter;
+  ThunarDevice     *device;
+  GtkTreeModel     *child_model;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
+
+  /* determine the selected item */
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+  /* avoid dealing with invalid selections */
+  if (!GTK_IS_TREE_SELECTION (selection))
+    return;
+
+  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_DEVICE, &device, -1);
+
+      if (G_LIKELY (device != NULL))
+        {
+          /* start the spinner */
+          child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+          thunar_shortcuts_model_set_busy (THUNAR_SHORTCUTS_MODEL (child_model), device, TRUE);
+
+          thunar_browser_poke_device (THUNAR_BROWSER (view), device, view,
+                                      thunar_shortcuts_view_poke_device_mount_finish,
+                                      NULL);
+          g_object_unref (device);
+        }
+    }
+}
+
+
+
+static void
+thunar_shortcuts_view_unmount_finish (ThunarDevice *device,
+                                      const GError *error,
+                                      gpointer      user_data)
+{
+  ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (user_data);
+  gchar               *device_name;
+  GtkTreeModel        *model;
+  GtkTreeModel        *child_model;
+
+  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
+
+  /* check if there was an error */
+  if (error != NULL)
+    {
+      /* display an error dialog to inform the user */
+      device_name = thunar_device_get_name (device);
+      thunar_dialogs_show_error (GTK_WIDGET (view), error, _("Failed to unmount \"%s\""), device_name);
+      g_free (device_name);
+    }
+
+  /* stop the spinner */
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+  child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+  thunar_shortcuts_model_set_busy (THUNAR_SHORTCUTS_MODEL (child_model), device, FALSE);
+
+  g_object_unref (view);
+}
+
+
+
+static void
+thunar_shortcuts_view_unmount (ThunarShortcutsView *view)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel     *model;
+  GtkTreeIter       iter;
+  ThunarDevice     *device;
+  GMountOperation  *mount_operation;
+  GtkTreeModel     *child_model;
+
+  _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 device/mount for the shortcut at the given tree iterator */
+      gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE, &device, -1);
+      _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
+
+      /* prepare a mount operation */
+      mount_operation = thunar_gtk_mount_operation_new (GTK_WIDGET (view));
+
+      /* start the spinner */
+      child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+      thunar_shortcuts_model_set_busy (THUNAR_SHORTCUTS_MODEL (child_model), device, TRUE);
+
+      /* try to unmount */
+      thunar_device_unmount (device,
+                             mount_operation,
+                             NULL,
+                             thunar_shortcuts_view_unmount_finish,
+                             g_object_ref (view));
+
+      g_object_unref (G_OBJECT (device));
+      g_object_unref (G_OBJECT (mount_operation));
+    }
+}
+
+
+
+/**
+ * thunar_shortcuts_view_new:
+ *
+ * Allocates a new #ThunarShortcutsView instance and associates
+ * it with the default #ThunarShortcutsModel instance.
+ *
+ * Return value: the newly allocated #ThunarShortcutsView instance.
+ **/
+GtkWidget*
+thunar_shortcuts_view_new (void)
+{
+  ThunarShortcutsModel *model;
+  GtkWidget            *view;
+  GtkTreeModel         *filter_model;
+
+  model = thunar_shortcuts_model_get_default ();
+  filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (model), NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), THUNAR_SHORTCUTS_MODEL_COLUMN_VISIBLE);
+  g_object_unref (G_OBJECT (model));
+
+  view = g_object_new (THUNAR_TYPE_SHORTCUTS_VIEW, "model", filter_model, NULL);
+  g_object_unref (G_OBJECT (filter_model));
+
+  return view;
+}
+
+
+
+/**
+ * thunar_shortcuts_view_select_by_file:
+ * @view : a #ThunarShortcutsView instance.
+ * @file : a #ThunarFile instance.
+ *
+ * Looks up the first shortcut that refers to @file in @view and selects it.
+ * If @file is not present in the underlying #ThunarShortcutsModel, no
+ * shortcut will be selected afterwards.
+ **/
+void
+thunar_shortcuts_view_select_by_file (ThunarShortcutsView *view,
+                                      ThunarFile          *file)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel     *model;
+  GtkTreeIter       iter;
+  GtkTreeModel     *child_model;
+  GtkTreeIter       child_iter;
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
+  _thunar_return_if_fail (THUNAR_IS_FILE (file));
+
+  /* get the selection */
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+  child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+
+  /* try to lookup a tree iter for the given file */
+  if (thunar_shortcuts_model_iter_for_file (THUNAR_SHORTCUTS_MODEL (child_model), file, &child_iter)
+      && gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (model), &iter, &child_iter))
+    gtk_tree_selection_select_iter (selection, &iter);
+  else
+     gtk_tree_selection_unselect_all (selection);
+}
diff --git a/thunar/thunar-shortcuts-view.h b/thunar/thunar-shortcuts-view.h
new file mode 100644
index 0000000..5f81f0b
--- /dev/null
+++ b/thunar/thunar-shortcuts-view.h
@@ -0,0 +1,46 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny at xfce.org>
+ *
+ * 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 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 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 Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __THUNAR_SHORTCUTS_VIEW_H__
+#define __THUNAR_SHORTCUTS_VIEW_H__
+
+#include <thunar/thunar-file.h>
+
+G_BEGIN_DECLS;
+
+typedef struct _ThunarShortcutsViewClass ThunarShortcutsViewClass;
+typedef struct _ThunarShortcutsView      ThunarShortcutsView;
+
+#define THUNAR_TYPE_SHORTCUTS_VIEW             (thunar_shortcuts_view_get_type ())
+#define THUNAR_SHORTCUTS_VIEW(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_SHORTCUTS_VIEW, ThunarShortcutsView))
+#define THUNAR_SHORTCUTS_VIEW_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_SHORTCUTS_VIEW, ThunarShortcutsViewClass))
+#define THUNAR_IS_SHORTCUTS_VIEW(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_SHORTCUTS_VIEW))
+#define THUNAR_IS_SHORTCUTS_VIEW_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_SHORTCUTS_VIEW))
+#define THUNAR_SHORTCUTS_VIEW_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_SHORTCUTS_VIEW, ThunarShortcutsViewClass))
+
+GType      thunar_shortcuts_view_get_type        (void) G_GNUC_CONST;
+
+GtkWidget *thunar_shortcuts_view_new             (void) G_GNUC_MALLOC;
+
+void       thunar_shortcuts_view_select_by_file  (ThunarShortcutsView *view,
+                                                  ThunarFile           *file);
+
+G_END_DECLS;
+
+#endif /* !__THUNAR_SHORTCUTS_VIEW_H__ */

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the Xfce4-commits mailing list