[Xfce4-commits] <thunar:master> Make hiding items in the sidepane work.

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


Updating branch refs/heads/master
         to 90b4f222bc0a72f986907ca7184c1b59ce22495b (commit)
       from 919a5f205e7b699ec84bcfc858f15cfc7c744dfd (commit)

commit 90b4f222bc0a72f986907ca7184c1b59ce22495b
Author: Nick Schermer <nick at xfce.org>
Date:   Thu Oct 11 21:20:04 2012 +0200

    Make hiding items in the sidepane work.
    
    Make the model work on top of a filter model and implement
    hiding including related code and some small related
    improvements.

 thunar/thunar-device-monitor.c  |  204 +++++++++++-
 thunar/thunar-device-monitor.h  |    4 +
 thunar/thunar-device.c          |   65 ++++-
 thunar/thunar-device.h          |    4 +
 thunar/thunar-launcher-ui.xml   |    2 +
 thunar/thunar-launcher.c        |   31 ++-
 thunar/thunar-preferences.c     |   83 ++++-
 thunar/thunar-shortcuts-model.c |  700 ++++++++++++++++++++++++++-------------
 thunar/thunar-shortcuts-model.h |   69 +++--
 thunar/thunar-shortcuts-pane.c  |   10 +-
 thunar/thunar-shortcuts-view.c  |  278 +++++++++++++---
 11 files changed, 1104 insertions(+), 346 deletions(-)

diff --git a/thunar/thunar-device-monitor.c b/thunar/thunar-device-monitor.c
index c8dd712..a704bd0 100644
--- a/thunar/thunar-device-monitor.c
+++ b/thunar/thunar-device-monitor.c
@@ -29,6 +29,7 @@
 #include <thunar/thunar-device-monitor.h>
 #include <thunar/thunar-private.h>
 #include <thunar/thunar-marshal.h>
+#include <thunar/thunar-preferences.h>
 
 
 
@@ -42,9 +43,26 @@ enum
   LAST_SIGNAL
 };
 
+enum
+{
+  PROP_0,
+  PROP_HIDDEN_DEVICES
+};
+
 
 
 static void           thunar_device_monitor_finalize               (GObject                *object);
+static void           thunar_device_monitor_get_property           (GObject                *object,
+                                                                    guint                   prop_id,
+                                                                    GValue                 *value,
+                                                                    GParamSpec             *pspec);
+static void           thunar_device_monitor_set_property           (GObject                *object,
+                                                                    guint                   prop_id,
+                                                                    const GValue           *value,
+                                                                    GParamSpec             *pspec);
+static void           thunar_device_monitor_update_hidden          (gpointer                key,
+                                                                    gpointer                value,
+                                                                    gpointer                data);
 static void           thunar_device_monitor_volume_added           (GVolumeMonitor         *volume_monitor,
                                                                     GVolume                *volume,
                                                                     ThunarDeviceMonitor    *monitor);
@@ -89,12 +107,17 @@ struct _ThunarDeviceMonitor
 {
   GObject __parent__;
 
-  GVolumeMonitor *volume_monitor;
+  GVolumeMonitor     *volume_monitor;
 
   /* GVolume/GMount -> ThunarDevice */
-  GHashTable     *devices;
+  GHashTable         *devices;
+
+  /* GVolumes from GVolumeMonitor that are currently invisible */
+  GList              *hidden_volumes;
 
-  GList          *hidden_volumes;
+  /* user defined hidden volumes */
+  ThunarPreferences  *preferences;
+  gchar             **hidden_devices;
 };
 
 
@@ -114,6 +137,16 @@ thunar_device_monitor_class_init (ThunarDeviceMonitorClass *klass)
 
   gobject_class = G_OBJECT_CLASS (klass);
   gobject_class->finalize = thunar_device_monitor_finalize;
+  gobject_class->get_property = thunar_device_monitor_get_property;
+  gobject_class->set_property = thunar_device_monitor_set_property;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_HIDDEN_DEVICES,
+                                   g_param_spec_boxed ("hidden-devices",
+                                                       NULL,
+                                                       NULL,
+                                                       G_TYPE_STRV,
+                                                       EXO_PARAM_READWRITE));
 
   device_monitor_signals[DEVICE_ADDED] =
       g_signal_new (I_("device-added"),
@@ -161,6 +194,10 @@ thunar_device_monitor_init (ThunarDeviceMonitor *monitor)
   GList *list;
   GList *lp;
 
+  monitor->preferences = thunar_preferences_get ();
+  exo_binding_new (G_OBJECT (monitor->preferences), "hidden-devices",
+                   G_OBJECT (monitor), "hidden-devices");
+
   /* table for GVolume/GMount (key) -> ThunarDevice (value) */
   monitor->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, g_object_unref);
 
@@ -202,6 +239,10 @@ thunar_device_monitor_finalize (GObject *object)
 {
   ThunarDeviceMonitor *monitor = THUNAR_DEVICE_MONITOR (object);
 
+  /* release properties */
+  g_object_unref (monitor->preferences);
+  g_strfreev (monitor->hidden_devices);
+
   /* detatch from the monitor */
   g_signal_handlers_disconnect_matched (monitor->volume_monitor, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, monitor);
   g_object_unref (monitor->volume_monitor);
@@ -217,6 +258,99 @@ thunar_device_monitor_finalize (GObject *object)
 
 
 
+static void
+thunar_device_monitor_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  ThunarDeviceMonitor *monitor = THUNAR_DEVICE_MONITOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_HIDDEN_DEVICES:
+      g_value_set_boxed (value, monitor->hidden_devices);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static void
+thunar_device_monitor_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  ThunarDeviceMonitor *monitor = THUNAR_DEVICE_MONITOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_HIDDEN_DEVICES:
+      /* set */
+      g_strfreev (monitor->hidden_devices);
+      monitor->hidden_devices = g_value_dup_boxed (value);
+
+      /* update the devices */
+      if (monitor->devices != NULL)
+        g_hash_table_foreach (monitor->devices, thunar_device_monitor_update_hidden, monitor);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static gboolean
+thunar_device_monitor_id_is_hidden (ThunarDeviceMonitor *monitor,
+                                    const gchar         *id)
+{
+  guint n;
+
+  if (id == NULL || monitor->hidden_devices == NULL)
+    return FALSE;
+
+  /* check if the uuid is in the hidden list */
+  for (n = 0; monitor->hidden_devices[n] != NULL; n++)
+    if (g_strcmp0 (monitor->hidden_devices[n], id)== 0)
+      return TRUE;
+
+  return FALSE;
+}
+
+
+
+static void
+thunar_device_monitor_update_hidden (gpointer key,
+                                     gpointer value,
+                                     gpointer data)
+{
+  ThunarDeviceMonitor *monitor = THUNAR_DEVICE_MONITOR (data);
+  ThunarDevice        *device = THUNAR_DEVICE (value);
+  gchar               *id;
+  gboolean             hidden;
+
+  /* get state of the device */
+  id = thunar_device_get_identifier (device);
+  hidden = thunar_device_monitor_id_is_hidden (monitor, id);
+  g_free (id);
+
+  if (thunar_device_get_hidden (device) != hidden)
+    {g_message ("update device");
+      g_object_set (G_OBJECT (device), "hidden", hidden, NULL);
+      g_signal_emit (G_OBJECT (monitor), device_monitor_signals[DEVICE_CHANGED], 0, device);
+    }
+}
+
+
+
 #ifdef HAVE_GIO_UNIX
 static gboolean
 thunar_device_monitor_mount_is_internal (GMount *mount)
@@ -401,6 +535,7 @@ thunar_device_monitor_volume_changed (GVolumeMonitor      *volume_monitor,
 {
   GList        *lp;
   ThunarDevice *device;
+  gchar        *id;
 
   _thunar_return_if_fail (G_IS_VOLUME_MONITOR (volume_monitor));
   _thunar_return_if_fail (THUNAR_IS_DEVICE_MONITOR (monitor));
@@ -422,6 +557,13 @@ thunar_device_monitor_volume_changed (GVolumeMonitor      *volume_monitor,
                                  "kind", THUNAR_DEVICE_KIND_VOLUME,
                                  NULL);
 
+          /* set visibility */
+          id = thunar_device_get_identifier (device);
+          g_object_set (G_OBJECT (device),
+                        "hidden", thunar_device_monitor_id_is_hidden (monitor, id),
+                        NULL);
+          g_free (id);
+
           /* insert to list (takes ref from hidden list) */
           g_hash_table_insert (monitor->devices, volume, device);
 
@@ -472,6 +614,7 @@ thunar_device_monitor_mount_added (GVolumeMonitor      *volume_monitor,
   GFile            *location;
   ThunarDeviceKind  kind = THUNAR_DEVICE_KIND_MOUNT_LOCAL;
   GVolume          *volume;
+  gchar            *id;
 
   _thunar_return_if_fail (G_IS_VOLUME_MONITOR (volume_monitor));
   _thunar_return_if_fail (THUNAR_IS_DEVICE_MONITOR (monitor));
@@ -511,6 +654,13 @@ thunar_device_monitor_mount_added (GVolumeMonitor      *volume_monitor,
                              "kind", kind,
                              NULL);
 
+      /* set visibility */
+      id = thunar_device_get_identifier (device);
+      g_object_set (G_OBJECT (device),
+                    "hidden", thunar_device_monitor_id_is_hidden (monitor, id),
+                    NULL);
+      g_free (id);
+
       /* insert to list */
       g_hash_table_insert (monitor->devices, g_object_ref (mount), device);
 
@@ -688,3 +838,51 @@ thunar_device_monitor_get_devices (ThunarDeviceMonitor *monitor)
 
   return list;
 }
+
+
+
+void
+thunar_device_monitor_set_hidden (ThunarDeviceMonitor *monitor,
+                                  ThunarDevice        *device,
+                                  gboolean             hidden)
+{
+  gchar  *id;
+  gchar **devices;
+  guint   length;
+  guint   n;
+  guint   pos;
+
+  _thunar_return_if_fail (THUNAR_IS_DEVICE_MONITOR (monitor));
+  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
+
+  id = thunar_device_get_identifier (device);
+  if (id == NULL)
+    return;
+
+  /* update device */
+  g_object_set (G_OBJECT (device), "hidden", hidden, NULL);
+  g_signal_emit (G_OBJECT (monitor), device_monitor_signals[DEVICE_CHANGED], 0, device);
+
+  /* update the device list */
+  length = monitor->hidden_devices != NULL ? g_strv_length (monitor->hidden_devices) : 0;
+  devices = g_new0 (gchar *, length + 2);
+  pos = 0;
+
+  /* copy other identifiers in the new list */
+  if (monitor->hidden_devices != NULL)
+    {
+      for (n = 0; monitor->hidden_devices[n] != NULL; n++)
+        if (g_strcmp0 (monitor->hidden_devices[n], id) != 0)
+          devices[pos++] = g_strdup (monitor->hidden_devices[n]);
+    }
+
+  /* add the new identifiers if it should hide */
+  if (hidden)
+    devices[pos++] = id;
+  else
+    g_free (id);
+
+  /* store new list */
+  g_object_set (G_OBJECT (monitor->preferences), "hidden-devices", devices, NULL);
+  g_strfreev (devices);
+}
diff --git a/thunar/thunar-device-monitor.h b/thunar/thunar-device-monitor.h
index 4932c41..6c0c369 100644
--- a/thunar/thunar-device-monitor.h
+++ b/thunar/thunar-device-monitor.h
@@ -39,6 +39,10 @@ ThunarDeviceMonitor *thunar_device_monitor_get         (void);
 
 GList               *thunar_device_monitor_get_devices (ThunarDeviceMonitor *monitor);
 
+void                 thunar_device_monitor_set_hidden  (ThunarDeviceMonitor *monitor,
+                                                        ThunarDevice        *device,
+                                                        gboolean             hidden);
+
 G_END_DECLS
 
 #endif /* !__THUNAR_DEVICE_MONITOR_H__ */
diff --git a/thunar/thunar-device.c b/thunar/thunar-device.c
index 0047aa5..45d382e 100644
--- a/thunar/thunar-device.c
+++ b/thunar/thunar-device.c
@@ -32,6 +32,7 @@ enum
 {
   PROP_0,
   PROP_DEVICE,
+  PROP_HIDDEN,
   PROP_KIND
 };
 
@@ -62,6 +63,8 @@ struct _ThunarDevice
   gpointer          device;
 
   ThunarDeviceKind  kind;
+
+  guint             hidden : 1;
 };
 
 typedef struct
@@ -98,6 +101,14 @@ thunar_device_class_init (ThunarDeviceClass *klass)
                                                         | G_PARAM_CONSTRUCT_ONLY));
 
   g_object_class_install_property (gobject_class,
+                                   PROP_HIDDEN,
+                                   g_param_spec_boolean ("hidden",
+                                                         "hidden",
+                                                         "hidden",
+                                                         FALSE,
+                                                         EXO_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
                                    PROP_KIND,
                                    g_param_spec_uint ("kind",
                                                       "kind",
@@ -146,6 +157,10 @@ thunar_device_get_property (GObject    *object,
       g_value_set_object (value, device->device);
       break;
 
+    case PROP_HIDDEN:
+      g_value_set_boolean (value, device->hidden);
+      break;
+
     case PROP_KIND:
       g_value_set_uint (value, device->kind);
       break;
@@ -173,6 +188,10 @@ thunar_device_set_property (GObject      *object,
       _thunar_assert (G_IS_VOLUME (device->device) || G_IS_MOUNT (device->device));
       break;
 
+    case PROP_HIDDEN:
+      device->hidden = g_value_get_boolean (value);
+      break;
+
     case PROP_KIND:
       device->kind = g_value_get_uint (value);
       break;
@@ -290,7 +309,7 @@ thunar_device_volume_mount_finished (GObject      *object,
 
   _thunar_return_if_fail (G_IS_VOLUME (object));
   _thunar_return_if_fail (G_IS_ASYNC_RESULT (result));
-  
+
   /* finish the eject */
   if (!g_volume_eject_with_operation_finish (G_VOLUME (object), result, &error))
     {
@@ -389,6 +408,44 @@ thunar_device_get_kind (const ThunarDevice *device)
 
 
 
+gchar *
+thunar_device_get_identifier (const ThunarDevice *device)
+{
+  gchar *ident = NULL;
+
+  _thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), NULL);
+
+  if (G_IS_VOLUME (device->device))
+    {
+      ident = g_volume_get_uuid (device->device);
+      if (ident == NULL)
+        ident = g_volume_get_identifier (device->device, G_VOLUME_IDENTIFIER_KIND_UUID);
+      if (ident == NULL)
+        ident = g_volume_get_name (device->device);
+    }
+  else if (G_IS_MOUNT (device->device))
+    {
+      ident = g_mount_get_uuid (device->device);
+      if (ident == NULL)
+        ident = g_mount_get_name (device->device);
+    }
+  else
+    _thunar_assert_not_reached ();
+
+  return ident;
+}
+
+
+
+gboolean
+thunar_device_get_hidden (const ThunarDevice *device)
+{
+  _thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), FALSE);
+  return device->hidden;
+}
+
+
+
 /**
  * thunar_device_can_eject:
  *
@@ -509,7 +566,7 @@ thunar_device_is_mounted (const ThunarDevice *device)
   GMount   *volume_mount;
 
   _thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), FALSE);
-  
+
   if (G_IS_VOLUME (device->device))
     {
       /* a volume with a mount point is mounted */
@@ -590,12 +647,12 @@ thunar_device_mount (ThunarDevice         *device,
                      gpointer              user_data)
 {
   ThunarDeviceOperation *operation;
-  
+
   _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
   _thunar_return_if_fail (G_IS_MOUNT_OPERATION (mount_operation));
   _thunar_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
   _thunar_return_if_fail (callback != NULL);
-  
+
   if (G_IS_VOLUME (device->device))
     {
       operation = thunar_device_operation_new (device, callback, user_data);
diff --git a/thunar/thunar-device.h b/thunar/thunar-device.h
index f4de610..614fd51 100644
--- a/thunar/thunar-device.h
+++ b/thunar/thunar-device.h
@@ -53,6 +53,10 @@ GIcon               *thunar_device_get_icon         (const ThunarDevice   *devic
 
 ThunarDeviceKind     thunar_device_get_kind         (const ThunarDevice   *device) G_GNUC_PURE;
 
+gchar               *thunar_device_get_identifier   (const ThunarDevice   *device) G_GNUC_MALLOC;
+
+gboolean             thunar_device_get_hidden       (const ThunarDevice   *device);
+
 gboolean             thunar_device_can_eject        (const ThunarDevice   *device);
 
 gboolean             thunar_device_can_mount        (const ThunarDevice   *device);
diff --git a/thunar/thunar-launcher-ui.xml b/thunar/thunar-launcher-ui.xml
index ecf2432..2567ff8 100644
--- a/thunar/thunar-launcher-ui.xml
+++ b/thunar/thunar-launcher-ui.xml
@@ -28,6 +28,7 @@
       <menu action="sendto-menu">
         <placeholder name="placeholder-sendto-actions">
           <menuitem action="sendto-desktop" />
+          <separator />
         </placeholder>
       </menu>
     </menu>
@@ -50,6 +51,7 @@
     <menu action="sendto-menu">
       <placeholder name="placeholder-sendto-actions">
         <menuitem action="sendto-desktop" />
+        <separator />
       </placeholder>
     </menu>
   </popup>
diff --git a/thunar/thunar-launcher.c b/thunar/thunar-launcher.c
index 72cdf62..644b7b6 100644
--- a/thunar/thunar-launcher.c
+++ b/thunar/thunar-launcher.c
@@ -1462,6 +1462,9 @@ thunar_launcher_sendto_idle (gpointer user_data)
   gchar          *device_name;
   gint            n_selected_files;
   gint            n = 0;
+  gboolean        got_devices = FALSE;
+  const gchar    *file_menu_path;
+  const gchar    *context_menu_path;
 
   /* verify that we have an UI manager */
   if (launcher->ui_manager == NULL)
@@ -1505,6 +1508,11 @@ thunar_launcher_sendto_idle (gpointer user_data)
 
       /* determine the currently active devices */
       devices = thunar_device_monitor_get_devices (launcher->device_monitor);
+      got_devices = (devices != NULL);
+
+      /* paths in ui */
+      file_menu_path = "/main-menu/file-menu/sendto-menu/placeholder-sendto-actions";
+      context_menu_path = "/file-context-menu/sendto-menu/placeholder-sendto-actions";
 
       /* add removable (and writable) drives and media */
       for (lp = devices; lp != NULL; lp = lp->next, ++n)
@@ -1522,11 +1530,9 @@ thunar_launcher_sendto_idle (gpointer user_data)
           g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (thunar_launcher_action_sendto_device), launcher);
           gtk_action_group_add_action (launcher->action_group, action);
           gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
-                                 "/main-menu/file-menu/sendto-menu/placeholder-sendto-actions",
-                                 name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
+                                 file_menu_path, name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
           gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
-                                 "/file-context-menu/sendto-menu/placeholder-sendto-actions",
-                                 name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
+                                 context_menu_path, name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
           g_object_unref (action);
 
           icon = thunar_device_get_icon (lp->data);
@@ -1549,6 +1555,17 @@ thunar_launcher_sendto_idle (gpointer user_data)
       handlers = thunar_sendto_model_get_matching (launcher->sendto_model, launcher->selected_files);
       if (G_LIKELY (handlers != NULL))
         {
+          if (got_devices)
+            {
+              /* add separator between the devices and actions action */
+              gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
+                                     file_menu_path, "separator", NULL,
+                                     GTK_UI_MANAGER_SEPARATOR, FALSE);
+              gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
+                                     context_menu_path, "separator", NULL,
+                                     GTK_UI_MANAGER_SEPARATOR, FALSE);
+            }
+
           /* add all handlers to the user interface */
           for (lp = handlers; lp != NULL; lp = lp->next, ++n)
             {
@@ -1566,11 +1583,9 @@ thunar_launcher_sendto_idle (gpointer user_data)
               g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (thunar_launcher_action_open), launcher);
               gtk_action_group_add_action (launcher->action_group, action);
               gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
-                                     "/main-menu/file-menu/sendto-menu/placeholder-sendto-actions",
-                                     name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
+                                     file_menu_path, name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
               gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
-                                     "/file-context-menu/sendto-menu/placeholder-sendto-actions",
-                                     name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
+                                     context_menu_path, name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
               g_object_unref (G_OBJECT (action));
 
               /* cleanup */
diff --git a/thunar/thunar-preferences.c b/thunar/thunar-preferences.c
index ea5bdc8..4e1de11 100644
--- a/thunar/thunar-preferences.c
+++ b/thunar/thunar-preferences.c
@@ -50,6 +50,8 @@ enum
 {
   PROP_0,
   PROP_DEFAULT_VIEW,
+  PROP_HIDDEN_DEVICES,
+  PROP_HIDDEN_BOOKMARKS,
   PROP_LAST_COMPACT_VIEW_ZOOM_LEVEL,
   PROP_LAST_DETAILS_VIEW_COLUMN_ORDER,
   PROP_LAST_DETAILS_VIEW_COLUMN_WIDTHS,
@@ -118,6 +120,8 @@ struct _ThunarPreferences
   GObject __parent__;
 
   XfconfChannel *channel;
+
+  gulong         property_changed_id;
 };
 
 
@@ -152,6 +156,35 @@ thunar_preferences_class_init (ThunarPreferencesClass *klass)
                                                         EXO_PARAM_READWRITE));
 
   /**
+   * ThunarPreferences:hidden-bookmarks:
+   *
+   * List of URI's that are hidden in the bookmarks (obtained from ~/.gtk-bookmarks).
+   * If an URI is not in the bookmarks file it will be removed from this list.
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_HIDDEN_BOOKMARKS,
+                                   g_param_spec_boxed ("hidden-bookmarks",
+                                                       NULL,
+                                                       NULL,
+                                                       G_TYPE_STRV,
+                                                       EXO_PARAM_READWRITE));
+
+  /**
+   * ThunarPreferences:hidden-devices:
+   *
+   * List of hidden devices. The value could be an UUID or name.
+   * Visibility of the device can be obtained with
+   * thunar_device_get_hidden().
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_HIDDEN_DEVICES,
+                                   g_param_spec_boxed ("hidden-devices",
+                                                       NULL,
+                                                       NULL,
+                                                       G_TYPE_STRV,
+                                                       EXO_PARAM_READWRITE));
+
+  /**
    * ThunarPreferences:last-compact-view-zoom-level:
    *
    * The last selected #ThunarZoomLevel for the #ThunarCompactView.
@@ -666,7 +699,7 @@ thunar_preferences_init (ThunarPreferences *preferences)
   const gchar check_prop[] = "/last-view";
 
   /* load the channel */
-  preferences->channel = xfconf_channel_new ("thunar");
+  preferences->channel = xfconf_channel_get ("thunar");
 
   /* check one of the property to see if there are values */
   if (!xfconf_channel_has_property (preferences->channel, check_prop))
@@ -679,8 +712,9 @@ thunar_preferences_init (ThunarPreferences *preferences)
         xfconf_channel_set_string (preferences->channel, check_prop, "ThunarIconView");
     }
 
-  g_signal_connect (G_OBJECT (preferences->channel), "property-changed",
-                    G_CALLBACK (thunar_preferences_prop_changed), preferences);
+  preferences->property_changed_id =
+    g_signal_connect (G_OBJECT (preferences->channel), "property-changed",
+                      G_CALLBACK (thunar_preferences_prop_changed), preferences);
 }
 
 
@@ -690,8 +724,8 @@ thunar_preferences_finalize (GObject *object)
 {
   ThunarPreferences *preferences = THUNAR_PREFERENCES (object);
 
-  /* release the channel */
-  g_object_unref (G_OBJECT (preferences->channel));
+  /* disconnect from the updates */
+  g_signal_handler_disconnect (preferences->channel, preferences->property_changed_id);
 
   (*G_OBJECT_CLASS (thunar_preferences_parent_class)->finalize) (object);
 }
@@ -704,19 +738,26 @@ thunar_preferences_get_property (GObject    *object,
                                  GValue     *value,
                                  GParamSpec *pspec)
 {
-  ThunarPreferences *preferences = THUNAR_PREFERENCES (object);
-  GValue             src = { 0, };
-  gchar              prop_name[64];
+  ThunarPreferences  *preferences = THUNAR_PREFERENCES (object);
+  GValue              src = { 0, };
+  gchar               prop_name[64];
+  gchar             **array;
 
   /* build property name */
   g_snprintf (prop_name, sizeof (prop_name), "/%s", g_param_spec_get_name (pspec));
 
-  if (xfconf_channel_get_property (preferences->channel, prop_name, &src))
+  if (G_VALUE_TYPE (value) == G_TYPE_STRV)
+    {
+      /* handle arrays directly since we cannot transform those */
+      array = xfconf_channel_get_string_list (preferences->channel, prop_name);
+      g_value_take_boxed (value, array);
+    }
+  else if (xfconf_channel_get_property (preferences->channel, prop_name, &src))
     {
       if (G_VALUE_TYPE (value) == G_VALUE_TYPE (&src))
         g_value_copy (&src, value);
       else if (!g_value_transform (&src, value))
-        g_printerr ("Thunar: Failed to transform property %s", prop_name);
+        g_printerr ("Thunar: Failed to transform property %s\n", prop_name);
       g_value_unset (&src);
     }
   else
@@ -734,13 +775,17 @@ thunar_preferences_set_property (GObject      *object,
                                  const GValue *value,
                                  GParamSpec   *pspec)
 {
-  ThunarPreferences *preferences = THUNAR_PREFERENCES (object);
-  GValue             dst = { 0, };
-  gchar              prop_name[64];
+  ThunarPreferences  *preferences = THUNAR_PREFERENCES (object);
+  GValue              dst = { 0, };
+  gchar               prop_name[64];
+  gchar             **array;
 
   /* build property name */
   g_snprintf (prop_name, sizeof (prop_name), "/%s", g_param_spec_get_name (pspec));
 
+  /* freeze */
+  g_signal_handler_block (preferences->channel, preferences->property_changed_id);
+
   if (G_VALUE_HOLDS_ENUM (value))
     {
       /* convert into a string */
@@ -749,11 +794,23 @@ thunar_preferences_set_property (GObject      *object,
         xfconf_channel_set_property (preferences->channel, prop_name, &dst);
       g_value_unset (&dst);
     }
+  else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
+    {
+      /* convert to a GValue GPtrArray in xfconf */
+      array = g_value_get_boxed (value);
+      if (array != NULL && *array != NULL)
+        xfconf_channel_set_string_list (preferences->channel, prop_name, (const gchar * const *) array);
+      else
+        xfconf_channel_reset_property (preferences->channel, prop_name, FALSE);
+    }
   else
     {
       /* other types we support directly */
       xfconf_channel_set_property (preferences->channel, prop_name, value);
     }
+
+  /* thaw */
+  g_signal_handler_unblock (preferences->channel, preferences->property_changed_id);
 }
 
 
diff --git a/thunar/thunar-shortcuts-model.c b/thunar/thunar-shortcuts-model.c
index 84fdc30..15c0cb5 100644
--- a/thunar/thunar-shortcuts-model.c
+++ b/thunar/thunar-shortcuts-model.c
@@ -2,6 +2,7 @@
 /*-
  * 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
@@ -41,6 +42,7 @@
 #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-private.h>
 
 #define SPINNER_CYCLE_DURATION 1000
@@ -56,8 +58,24 @@ typedef struct _ThunarShortcut ThunarShortcut;
 
 
 
+enum
+{
+  PROP_0,
+  PROP_HIDDEN_BOOKMARKS
+};
+
+
+
 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);
@@ -95,6 +113,11 @@ static gboolean           thunar_shortcuts_model_drag_data_get      (GtkTreeDrag
                                                                      GtkSelectionData          *selection_data);
 static gboolean           thunar_shortcuts_model_drag_data_delete   (GtkTreeDragSource         *source,
                                                                      GtkTreePath               *path);
+static void               thunar_shortcuts_model_shortcut_places    (ThunarShortcutsModel      *model);
+static void               thunar_shortcuts_model_shortcut_network   (ThunarShortcutsModel      *model);
+static gboolean           thunar_shortcuts_model_devices_load       (gpointer                   data);
+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,
@@ -139,26 +162,29 @@ struct _ThunarShortcutsModel
    * generated by another model.
    */
 #ifndef NDEBUG
-  gint                 stamp;
+  gint                  stamp;
 #endif
 
-  GList               *shortcuts;
+  GList                *shortcuts;
+
+  ThunarPreferences    *preferences;
+  gchar               **hidden_bookmarks;
 
-  ThunarDeviceMonitor *device_monitor;
-  guint                devices_monitor_idle_id;
+  ThunarDeviceMonitor  *device_monitor;
+  guint                 devices_monitor_idle_id;
 
-  GFileMonitor        *monitor;
-  guint                load_idle_id;
+  gint64                bookmarks_time;
+  GFile                *bookmarks_file;
+  GFileMonitor         *bookmarks_monitor;
+  guint                 bookmarks_idle_id;
 
-  guint busy_timeout_id;
+  guint                 busy_timeout_id;
 };
 
 struct _ThunarShortcut
 {
   ThunarShortcutGroup  group;
 
-  guint                is_header : 1;
-
   gchar               *name;
   GIcon               *gicon;
   gint                 sort_id;
@@ -169,6 +195,8 @@ struct _ThunarShortcut
   GFile               *location;
   ThunarFile          *file;
   ThunarDevice        *device;
+
+  guint                hidden : 1;
 };
 
 
@@ -186,6 +214,16 @@ thunar_shortcuts_model_class_init (ThunarShortcutsModelClass *klass)
 
   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));
 }
 
 
@@ -219,161 +257,6 @@ thunar_shortcuts_model_drag_source_init (GtkTreeDragSourceIface *iface)
 
 
 
-static gboolean
-thunar_shortcuts_model_devices_load (gpointer data)
-{
-  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (data);
-  ThunarShortcut       *shortcut;
-  GList                *devices;
-  GList                *lp;
-
-  GDK_THREADS_ENTER ();
-
-  /* add the devices heading */
-  shortcut = g_slice_new0 (ThunarShortcut);
-  shortcut->group = THUNAR_SHORTCUT_GROUP_DEVICES;
-  shortcut->name = g_strdup (_("DEVICES"));
-  shortcut->is_header = TRUE;
-  thunar_shortcuts_model_add_shortcut (model, shortcut);
-
-  /* the filesystem entry */
-  shortcut = g_slice_new0 (ThunarShortcut);
-  shortcut->group = THUNAR_SHORTCUT_GROUP_DEVICES;
-  shortcut->name = g_strdup (_("File System"));
-  shortcut->file = thunar_file_get_for_uri ("file:///", NULL);
-  shortcut->gicon = g_themed_icon_new (GTK_STOCK_HARDDISK);
-  thunar_shortcuts_model_add_shortcut (model, shortcut);
-
-  /* connect to the 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 (model->device_monitor, lp->data, model);
-      g_object_unref (G_OBJECT (lp->data));
-    }
-  g_list_free (devices);
-
-  GDK_THREADS_LEAVE ();
-
-  /* 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);
-
-  model->devices_monitor_idle_id = 0;
-
-  return FALSE;
-}
-
-
-
-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;
-  shortcut->name = g_strdup (_("NETWORK"));
-  shortcut->is_header = TRUE;
-  thunar_shortcuts_model_add_shortcut (model, shortcut);
-
-  /* the browse network entry */
-  shortcut = g_slice_new0 (ThunarShortcut);
-  shortcut->group = THUNAR_SHORTCUT_GROUP_NETWORK;
-  shortcut->name = g_strdup (_("Browse Network"));
-  shortcut->location = g_file_new_for_uri ("network://");
-  shortcut->gicon = g_themed_icon_new (GTK_STOCK_NETWORK);
-  thunar_shortcuts_model_add_shortcut (model, shortcut);
-}
-
-
-
-static void
-thunar_shortcuts_model_shortcut_places (ThunarShortcutsModel *model)
-{
-  ThunarShortcut *shortcut;
-  GFile          *home;
-  GFile          *bookmarks;
-  GFile          *desktop;
-  GFile          *trash;
-  ThunarFile     *file;
-
-  /* add the places heading */
-  shortcut = g_slice_new0 (ThunarShortcut);
-  shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES;
-  shortcut->name = g_strdup (_("PLACES"));
-  shortcut->is_header = TRUE;
-  thunar_shortcuts_model_add_shortcut (model, shortcut);
-
-  /* get home path */
-  home = thunar_g_file_new_for_home ();
-
-  /* add home entry */
-  file = thunar_file_get (home, NULL);
-  if (file != NULL)
-    {
-      shortcut = g_slice_new0 (ThunarShortcut);
-      shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES;
-      shortcut->file = file;
-      shortcut->sort_id = 0;
-      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;
-          shortcut->file = file;
-          shortcut->sort_id =  1;
-          thunar_shortcuts_model_add_shortcut (model, shortcut);
-        }
-    }
-  g_object_unref (desktop);
-
-  /* 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_TRASH;
-          shortcut->file = file;
-          thunar_shortcuts_model_add_shortcut (model, shortcut);
-        }
-    }
-
-  /* determine the URI to the Gtk+ bookmarks file */
-  bookmarks = g_file_resolve_relative_path (home, ".gtk-bookmarks");
-
-  /* register with the alteration monitor for the bookmarks file */
-  model->monitor = g_file_monitor_file (bookmarks, G_FILE_MONITOR_NONE, NULL, NULL);
-  if (G_LIKELY (model->monitor != NULL))
-    g_signal_connect (model->monitor, "changed", G_CALLBACK (thunar_shortcuts_model_monitor), model);
-
-  /* read the Gtk+ bookmarks file */
-  model->load_idle_id = g_idle_add (thunar_shortcuts_model_load, model);
-
-  g_object_unref (home);
-  g_object_unref (bookmarks);
-}
-
-
-
-
 static void
 thunar_shortcuts_model_init (ThunarShortcutsModel *model)
 {
@@ -381,8 +264,13 @@ thunar_shortcuts_model_init (ThunarShortcutsModel *model)
   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");
+
   /* load volumes */
-  model->devices_monitor_idle_id = g_idle_add_full (G_PRIORITY_LOW, thunar_shortcuts_model_devices_load, model, NULL);
+  model->devices_monitor_idle_id = g_idle_add (thunar_shortcuts_model_devices_load, model);
 
   /* add network */
   thunar_shortcuts_model_shortcut_network (model);
@@ -405,8 +293,8 @@ thunar_shortcuts_model_finalize (GObject *object)
     g_source_remove (model->busy_timeout_id);
 
   /* stop bookmark load idle */
-  if (model->load_idle_id != 0)
-    g_source_remove (model->load_idle_id);
+  if (model->bookmarks_idle_id != 0)
+    g_source_remove (model->bookmarks_idle_id);
 
   /* stop device monitor loading */
   if (model->devices_monitor_idle_id != 0)
@@ -416,13 +304,19 @@ thunar_shortcuts_model_finalize (GObject *object)
   g_list_foreach (model->shortcuts, (GFunc) thunar_shortcut_free, model);
   g_list_free (model->shortcuts);
 
+  /* free hidden list */
+  g_strfreev (model->hidden_bookmarks);
+
   /* detach from the file monitor */
-  if (model->monitor != NULL)
+  if (model->bookmarks_monitor != NULL)
     {
-      g_file_monitor_cancel (model->monitor);
-      g_object_unref (model->monitor);
+      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);
@@ -432,6 +326,81 @@ thunar_shortcuts_model_finalize (GObject *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;
+
+    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);
+            }
+        }
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
 static GtkTreeModelFlags
 thunar_shortcuts_model_get_flags (GtkTreeModel *tree_model)
 {
@@ -455,7 +424,12 @@ thunar_shortcuts_model_get_column_type (GtkTreeModel *tree_model,
   switch (idx)
     {
     case THUNAR_SHORTCUTS_MODEL_COLUMN_HEADER:
-    case THUNAR_SHORTCUTS_MODEL_COLUMN_NOT_HEADER:
+      return G_TYPE_BOOLEAN;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_ITEM:
+      return G_TYPE_BOOLEAN;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_VISIBLE:
       return G_TYPE_BOOLEAN;
 
     case THUNAR_SHORTCUTS_MODEL_COLUMN_NAME:
@@ -553,17 +527,27 @@ thunar_shortcuts_model_get_value (GtkTreeModel *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_HEADER:
       g_value_init (value, G_TYPE_BOOLEAN);
-      g_value_set_boolean (value, shortcut->is_header);
+      g_value_set_boolean (value, (shortcut->group & THUNAR_SHORTCUT_GROUP_HEADER) != 0);
+      break;
+
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_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_NOT_HEADER:
+    case THUNAR_SHORTCUTS_MODEL_COLUMN_VISIBLE:
       g_value_init (value, G_TYPE_BOOLEAN);
-      g_value_set_boolean (value, !shortcut->is_header);
+      if (shortcut->device == NULL)
+        g_value_set_boolean (value, !shortcut->hidden);
+      else
+        g_value_set_boolean (value, !thunar_device_get_hidden (shortcut->device));
       break;
 
     case THUNAR_SHORTCUTS_MODEL_COLUMN_NAME:
@@ -605,7 +589,7 @@ thunar_shortcuts_model_get_value (GtkTreeModel *tree_model,
 
     case THUNAR_SHORTCUTS_MODEL_COLUMN_MUTABLE:
       g_value_init (value, G_TYPE_BOOLEAN);
-      g_value_set_boolean (value, shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS);
+      g_value_set_boolean (value, shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS);
       break;
 
     case THUNAR_SHORTCUTS_MODEL_COLUMN_CAN_EJECT:
@@ -740,7 +724,7 @@ thunar_shortcuts_model_row_draggable (GtkTreeDragSource *source,
   shortcut = g_list_nth_data (model->shortcuts, gtk_tree_path_get_indices (path)[0]);
 
   /* special shortcuts cannot be reordered */
-  return (shortcut != NULL && shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS);
+  return (shortcut != NULL && shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS);
 }
 
 
@@ -771,6 +755,198 @@ thunar_shortcuts_model_drag_data_delete (GtkTreeDragSource *source,
 
 
 
+static gboolean
+thunar_shortcuts_model_devices_load (gpointer data)
+{
+  ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (data);
+  ThunarShortcut       *shortcut;
+  GList                *devices;
+  GList                *lp;
+
+  GDK_THREADS_ENTER ();
+
+  /* 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 (GTK_STOCK_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 (model->device_monitor, lp->data, model);
+      g_object_unref (G_OBJECT (lp->data));
+    }
+  g_list_free (devices);
+
+  GDK_THREADS_LEAVE ();
+
+  /* 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);
+
+  model->devices_monitor_idle_id = 0;
+
+  return FALSE;
+}
+
+
+
+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 (GTK_STOCK_NETWORK);
+  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->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);
+
+  /* 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 = g_file_resolve_relative_path (home, ".gtk-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 (thunar_shortcuts_model_load, model);
+
+  g_object_unref (home);
+}
+
+
+
+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)
@@ -782,10 +958,6 @@ thunar_shortcuts_model_sort_func (gconstpointer shortcut_a,
   if (a->group != b->group)
     return a->group - b->group;
 
-  /* sort header at the top of a group */
-  if (a->is_header != b->is_header)
-    return b->is_header ? 1 : -1;
-
   /* use sort order */
   if (a->sort_id != b->sort_id)
     return a->sort_id > b->sort_id ? 1 : -1;
@@ -894,7 +1066,6 @@ thunar_shortcuts_model_load (gpointer data)
   ThunarShortcut       *shortcut;
   ThunarFile           *file;
   GFile                *file_path;
-  GFile                *home;
   gchar                *bookmarks_path;
   gchar                 line[2048];
   gchar                *name;
@@ -903,10 +1074,8 @@ thunar_shortcuts_model_load (gpointer data)
 
   _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
 
-  home = thunar_g_file_new_for_home ();
-
   /* determine the path to the GTK+ bookmarks file */
-  bookmarks_path = xfce_get_homefile (".gtk-bookmarks", NULL);
+  bookmarks_path = g_file_get_path (model->bookmarks_file);
 
   /* append the GTK+ bookmarks (if any) */
   fp = fopen (bookmarks_path, "r");
@@ -950,9 +1119,10 @@ thunar_shortcuts_model_load (gpointer data)
                 {
                   /* create the shortcut entry */
                   shortcut = g_slice_new0 (ThunarShortcut);
-                  shortcut->group = THUNAR_SHORTCUT_GROUP_BOOKMARKS;
+                  shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS;
                   shortcut->file = file;
                   shortcut->sort_id = ++sort_id;
+                  shortcut->hidden = thunar_shortcuts_model_get_hidden (model, shortcut);
                   shortcut->name = (*name != '\0') ? g_strdup (name) : NULL;
 
                   /* append the shortcut to the list */
@@ -967,7 +1137,7 @@ thunar_shortcuts_model_load (gpointer data)
             {
               /* create the shortcut entry */
               shortcut = g_slice_new0 (ThunarShortcut);
-              shortcut->group = THUNAR_SHORTCUT_GROUP_BOOKMARKS;
+              shortcut->group = THUNAR_SHORTCUT_GROUP_NETWORK_BOOKMARKS;
               shortcut->gicon = g_themed_icon_new ("folder-remote");
               shortcut->location = file_path;
               shortcut->sort_id = ++sort_id;
@@ -985,10 +1155,9 @@ thunar_shortcuts_model_load (gpointer data)
     }
 
   /* clean up */
-  g_object_unref (home);
   g_free (bookmarks_path);
 
-  model->load_idle_id = 0;
+  model->bookmarks_idle_id = 0;
 
   return FALSE;
 }
@@ -1018,7 +1187,7 @@ thunar_shortcuts_model_reload (gpointer data)
       lp = g_list_next (lp);
 
       /* drop the shortcut if it is user-defined */
-      if (shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS)
+      if (shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS)
         {
           /* unlink the shortcut from the model */
           model->shortcuts = g_list_remove (model->shortcuts, shortcut);
@@ -1055,11 +1224,15 @@ thunar_shortcuts_model_monitor (GFileMonitor     *monitor,
   ThunarShortcutsModel *model = THUNAR_SHORTCUTS_MODEL (user_data);
 
   _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
-  _thunar_return_if_fail (model->monitor == monitor);
+  _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->load_idle_id == 0)
-    model->load_idle_id = g_idle_add (thunar_shortcuts_model_reload, model);
+  if (model->bookmarks_idle_id == 0)
+    model->bookmarks_idle_id = g_idle_add (thunar_shortcuts_model_reload, model);
 }
 
 
@@ -1067,33 +1240,21 @@ thunar_shortcuts_model_monitor (GFileMonitor     *monitor,
 static void
 thunar_shortcuts_model_save (ThunarShortcutsModel *model)
 {
+  GString        *contents;
   ThunarShortcut *shortcut;
   gchar          *bookmarks_path;
-  gchar          *tmp_path;
   gchar          *uri;
   GList          *lp;
-  FILE           *fp;
-  gint            fd;
+  GError         *err = NULL;
 
   _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
 
-  /* open a temporary file for writing */
-  tmp_path = xfce_get_homefile (".gtk-bookmarks.XXXXXX", NULL);
-  fd = g_mkstemp (tmp_path);
-  if (G_UNLIKELY (fd < 0))
-    {
-      g_warning ("Failed to open `%s' for writing: %s",
-                 tmp_path, g_strerror (errno));
-      g_free (tmp_path);
-      return;
-    }
+  contents = g_string_new (NULL);
 
-  /* write the uris of user customizable shortcuts */
-  fp = fdopen (fd, "w");
   for (lp = model->shortcuts; lp != NULL; lp = lp->next)
     {
       shortcut = THUNAR_SHORTCUT (lp->data);
-      if (shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS)
+      if (shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS)
         {
           if (shortcut->file != NULL)
             uri = thunar_file_dup_uri (shortcut->file);
@@ -1103,28 +1264,25 @@ thunar_shortcuts_model_save (ThunarShortcutsModel *model)
             continue;
 
           if (G_LIKELY (shortcut->name != NULL))
-            fprintf (fp, "%s %s\n", uri, shortcut->name);
+            g_string_append_printf (contents, "%s %s\n", uri, shortcut->name);
           else
-            fprintf (fp, "%s\n", uri);
+            g_string_append_printf (contents, "%s\n", uri);
           g_free (uri);
         }
     }
 
-  /* we're done writing the temporary file */
-  fclose (fp);
-
-  /* move the temporary file to it's final location (atomic writing) */
-  bookmarks_path = xfce_get_homefile (".gtk-bookmarks", NULL);
-  if (rename (tmp_path, bookmarks_path) < 0)
+  /* 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, g_strerror (errno));
-      g_unlink (tmp_path);
+      g_warning ("Failed to write \"%s\": %s", bookmarks_path, err->message);
+      g_error_free (err);
     }
-
-  /* cleanup */
   g_free (bookmarks_path);
-  g_free (tmp_path);
+  g_string_free (contents, TRUE);
+
+  /* store the save time */
+  model->bookmarks_time = g_get_real_time ();
 }
 
 
@@ -1212,15 +1370,16 @@ thunar_shortcuts_model_device_added (ThunarDeviceMonitor  *device_monitor,
   /* 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_VOLUMES;
+      shortcut->group = THUNAR_SHORTCUT_GROUP_DEVICES_VOLUMES;
       break;
 
     case THUNAR_DEVICE_KIND_MOUNT_LOCAL:
-      shortcut->group = THUNAR_SHORTCUT_GROUP_MOUNTS;
+      shortcut->group = THUNAR_SHORTCUT_GROUP_DEVICES_MOUNTS;
       break;
 
     case THUNAR_DEVICE_KIND_MOUNT_REMOTE:
@@ -1547,11 +1706,11 @@ thunar_shortcuts_model_drop_possible (ThunarShortcutsModel *model,
     return FALSE;
 
   /* cannot drop before special shortcuts! */
-  if (shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS)
+  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 && shortcut->is_header)
+  if (shortcut->group == THUNAR_SHORTCUT_GROUP_NETWORK_HEADER)
     return TRUE;
 
   return FALSE;
@@ -1589,7 +1748,7 @@ thunar_shortcuts_model_add (ThunarShortcutsModel *model,
 
   /* create the new shortcut that will be inserted */
   shortcut = g_slice_new0 (ThunarShortcut);
-  shortcut->group = THUNAR_SHORTCUT_GROUP_BOOKMARKS;
+  shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS;
   shortcut->file = g_object_ref (G_OBJECT (file));
 
   /* add the shortcut to the list at the given position */
@@ -1616,12 +1775,13 @@ thunar_shortcuts_model_move (ThunarShortcutsModel *model,
                              GtkTreePath          *dst_path)
 {
   ThunarShortcut *shortcut;
-  GtkTreePath     *path;
-  GList           *lp;
-  gint            *order;
-  gint             index_src;
-  gint             index_dst;
-  gint             idx;
+  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);
@@ -1637,7 +1797,8 @@ thunar_shortcuts_model_move (ThunarShortcutsModel *model,
     return;
 
   /* generate the order for the rows prior the dst/src rows */
-  order = g_newa (gint, g_list_length (model->shortcuts));
+  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;
 
@@ -1679,11 +1840,11 @@ thunar_shortcuts_model_move (ThunarShortcutsModel *model,
     }
 
   /* generate the remaining order */
-  for (; idx < (gint) g_list_length (model->shortcuts); ++idx)
+  for (; idx < n_shortcuts; ++idx)
     order[idx] = idx;
 
   /* tell all listeners about the reordering just performed */
-  path = gtk_tree_path_new ();
+  path = gtk_tree_path_new_first ();
   gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model), path, NULL, order);
   gtk_tree_path_free (path);
 
@@ -1718,7 +1879,7 @@ thunar_shortcuts_model_remove (ThunarShortcutsModel *model,
   shortcut = g_list_nth_data (model->shortcuts, gtk_tree_path_get_indices (path)[0]);
 
   /* verify that the shortcut is removable */
-  _thunar_assert (shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS);
+  _thunar_assert (shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS);
 
   /* remove the shortcut (using the file destroy handler) */
   thunar_shortcuts_model_remove_shortcut (model, shortcut);
@@ -1756,7 +1917,7 @@ thunar_shortcuts_model_rename (ThunarShortcutsModel *model,
   shortcut = THUNAR_SHORTCUT (((GList *) iter->user_data)->data);
 
   /* verify the shortcut */
-  _thunar_assert (shortcut->group == THUNAR_SHORTCUT_GROUP_BOOKMARKS);
+  _thunar_assert (shortcut->group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS);
   _thunar_assert (THUNAR_IS_FILE (shortcut->file));
 
   /* perform the rename */
@@ -1828,3 +1989,66 @@ thunar_shortcuts_model_set_busy (ThunarShortcutsModel *model,
         }
     }
 }
+
+
+
+
+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);
+}
diff --git a/thunar/thunar-shortcuts-model.h b/thunar/thunar-shortcuts-model.h
index 5932ad9..f09cbec 100644
--- a/thunar/thunar-shortcuts-model.h
+++ b/thunar/thunar-shortcuts-model.h
@@ -27,6 +27,7 @@ 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))
@@ -35,25 +36,11 @@ typedef struct _ThunarShortcutsModel      ThunarShortcutsModel;
 #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))
 
-/**
- * ThunarShortcutsModelColumn:
- * @THUNAR_SHORTCUTS_MODEL_COLUMN_TYPE      : #ThunarShortcutType.
- * @THUNAR_SHORTCUTS_MODEL_COLUMN_NAME      : the index of the name column.
- * @THUNAR_SHORTCUTS_MODEL_COLUMN_FILE      : the index of the file column.
- * @THUNAR_SHORTCUTS_MODEL_COLUMN_LOCATION  : file of the location.
- * @THUNAR_SHORTCUTS_MODEL_COLUMN_GICON     : custom image.
- * @THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE    : the index of the device column.
- * @THUNAR_SHORTCUTS_MODEL_COLUMN_MUTABLE   : tells whether a row is mutable.
- * @THUNAR_SHORTCUTS_MODEL_COLUMN_EJECT     : stock icon name for eject symbol
- * @THUNAR_SHORTCUTS_MODEL_COLUMN_SEPARATOR : tells whether a row is a separator.
- *
- * Columns exported by #ThunarShortcutsModel using the
- * #GtkTreeModel interface.
- **/
 typedef enum
 {
   THUNAR_SHORTCUTS_MODEL_COLUMN_HEADER,
-  THUNAR_SHORTCUTS_MODEL_COLUMN_NOT_HEADER,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_ITEM,
+  THUNAR_SHORTCUTS_MODEL_COLUMN_VISIBLE,
   THUNAR_SHORTCUTS_MODEL_COLUMN_NAME,
   THUNAR_SHORTCUTS_MODEL_COLUMN_FILE,
   THUNAR_SHORTCUTS_MODEL_COLUMN_LOCATION,
@@ -67,23 +54,48 @@ typedef enum
   THUNAR_SHORTCUTS_MODEL_N_COLUMNS,
 } ThunarShortcutsModelColumn;
 
-typedef enum
+#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 \
+                                       | THUNAR_SHORTCUT_GROUP_NETWORK_BOOKMARKS)
+#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_VOLUMES,
-  THUNAR_SHORTCUT_GROUP_MOUNTS,
-  THUNAR_SHORTCUT_GROUP_PLACES,
-  THUNAR_SHORTCUT_GROUP_TRASH,
-  THUNAR_SHORTCUT_GROUP_BOOKMARKS,
-  THUNAR_SHORTCUT_GROUP_NETWORK,
-  THUNAR_SHORTCUT_GROUP_NETWORK_MOUNTS
-} ThunarShortcutGroup;
+  /* 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),  /* local 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 */
+  THUNAR_SHORTCUT_GROUP_NETWORK_BOOKMARKS  = (1 << 11), /* remote bookmarks */
+};
 
 
 
 GType                  thunar_shortcuts_model_get_type      (void) G_GNUC_CONST;
 
-ThunarShortcutsModel *thunar_shortcuts_model_get_default    (void);
+ThunarShortcutsModel  *thunar_shortcuts_model_get_default   (void);
 
 gboolean               thunar_shortcuts_model_iter_for_file (ThunarShortcutsModel *model,
                                                              ThunarFile           *file,
@@ -110,6 +122,9 @@ void                   thunar_shortcuts_model_rename        (ThunarShortcutsMode
 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;
 
diff --git a/thunar/thunar-shortcuts-pane.c b/thunar/thunar-shortcuts-pane.c
index 6cbb72f..ca32dba 100644
--- a/thunar/thunar-shortcuts-pane.c
+++ b/thunar/thunar-shortcuts-pane.c
@@ -325,6 +325,7 @@ thunar_shortcuts_pane_set_selected_files (ThunarComponent *component,
 {
   ThunarShortcutsPane *shortcuts_pane = THUNAR_SHORTCUTS_PANE (component);
   GtkTreeModel        *model;
+  GtkTreeModel        *child_model;
   GtkTreeIter          iter;
   GtkAction           *action;
   GList               *lp;
@@ -350,8 +351,9 @@ thunar_shortcuts_pane_set_selected_files (ThunarComponent *component,
       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_iter_for_file (THUNAR_SHORTCUTS_MODEL (model), lp->data, &iter))
+            if (!thunar_shortcuts_model_iter_for_file (THUNAR_SHORTCUTS_MODEL (child_model), lp->data, &iter))
               break;
         }
 
@@ -436,6 +438,7 @@ thunar_shortcuts_pane_action_shortcuts_add (GtkAction           *action,
                                             ThunarShortcutsPane *shortcuts_pane)
 {
   GtkTreeModel *model;
+  GtkTreeModel *child_model;
   GtkTreePath  *path;
   GList        *lp;
 
@@ -447,12 +450,13 @@ thunar_shortcuts_pane_action_shortcuts_add (GtkAction           *action,
   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)))
           {
             /* append the folder to the shortcuts model */
-            path = gtk_tree_path_new_from_indices (gtk_tree_model_iter_n_children (model, NULL), -1);
-            thunar_shortcuts_model_add (THUNAR_SHORTCUTS_MODEL (model), path, lp->data);
+            path = gtk_tree_path_new_from_indices (gtk_tree_model_iter_n_children (child_model, NULL), -1);
+            thunar_shortcuts_model_add (THUNAR_SHORTCUTS_MODEL (child_model), path, lp->data);
             gtk_tree_path_free (path);
           }
 
diff --git a/thunar/thunar-shortcuts-view.c b/thunar/thunar-shortcuts-view.c
index b8d19a3..831ccb5 100644
--- a/thunar/thunar-shortcuts-view.c
+++ b/thunar/thunar-shortcuts-view.c
@@ -2,6 +2,7 @@
 /*-
  * 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
@@ -96,12 +97,17 @@ static gboolean       thunar_shortcuts_view_popup_menu                   (GtkWid
 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                 (ThunarShortcutsView      *view,
                                                                           GdkEventButton           *event,
                                                                           GtkTreeModel             *model,
                                                                           GtkTreeIter              *iter);
 static void           thunar_shortcuts_view_remove_activated             (GtkWidget                *item,
-                                                                          ThunarShortcutsView      *view);
+                                                                          GtkTreeModel             *model);
 static void           thunar_shortcuts_view_rename_activated             (GtkWidget                *item,
                                                                           ThunarShortcutsView      *view);
 static void           thunar_shortcuts_view_renamed                      (GtkCellRenderer          *renderer,
@@ -226,22 +232,6 @@ thunar_shortcuts_view_class_init (ThunarShortcutsViewClass *klass)
 }
 
 
-static gboolean
-thunar_shortcuts_view_selection_func (GtkTreeSelection *selection,
-                                      GtkTreeModel *model,
-                                      GtkTreePath *path,
-                                      gboolean path_currently_selected,
-                                      gpointer user_data)
-{
-  GtkTreeIter iter;
-  gboolean    is_header;
-
-  /* don't allow selecting headers */
-  gtk_tree_model_get_iter (model, &iter, path);
-  gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_HEADER, &is_header, -1);
-  return !is_header;
-}
-
 
 static void
 thunar_shortcuts_view_init (ThunarShortcutsView *view)
@@ -294,7 +284,7 @@ thunar_shortcuts_view_init (ThunarShortcutsView *view)
   g_object_set (G_OBJECT (renderer), "xpad", 6, NULL);
   gtk_tree_view_column_pack_start (column, renderer, FALSE);
   gtk_tree_view_column_set_attributes (column, renderer,
-                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_NOT_HEADER,
+                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_ITEM,
                                        NULL);
 
   /* allocate the special icon renderer */
@@ -304,7 +294,7 @@ thunar_shortcuts_view_init (ThunarShortcutsView *view)
                                        "gicon", THUNAR_SHORTCUTS_MODEL_COLUMN_GICON,
                                        "file", THUNAR_SHORTCUTS_MODEL_COLUMN_FILE,
                                        "device", THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE,
-                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_NOT_HEADER,
+                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_ITEM,
                                        NULL);
 
   /* sync the "emblems" property of the icon renderer with the "shortcuts-icon-emblems" preference
@@ -322,7 +312,7 @@ thunar_shortcuts_view_init (ThunarShortcutsView *view)
   gtk_tree_view_column_pack_start (column, renderer, TRUE);
   gtk_tree_view_column_set_attributes (column, renderer,
                                        "text", THUNAR_SHORTCUTS_MODEL_COLUMN_NAME,
-                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_NOT_HEADER,
+                                       "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_ITEM,
                                        NULL);
 
   /* spinner to indicate (un)mount/eject delay */
@@ -649,6 +639,9 @@ thunar_shortcuts_view_drag_drop (GtkWidget      *widget,
   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);
 
@@ -673,18 +666,29 @@ thunar_shortcuts_view_drag_drop (GtkWidget      *widget,
       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
       if (gtk_tree_selection_get_selected (selection, &model, &iter))
         {
-          /* we need to adjust the destination path here, because the path returned by
-           * the drop position computation effectively points after the insert position,
-           * which can led to unexpected results.
-           */
-          gtk_tree_path_prev (dst_path);
-          if (!thunar_shortcuts_model_drop_possible (THUNAR_SHORTCUTS_MODEL (model), dst_path))
-            gtk_tree_path_next (dst_path);
+          /* get the source path */
+          src_path = gtk_tree_model_get_path (model, &iter);
 
-          /* perform the move */
+          /* 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);
-          thunar_shortcuts_model_move (THUNAR_SHORTCUTS_MODEL (model), src_path, dst_path);
+          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 */
@@ -866,6 +870,134 @@ thunar_shortcuts_view_row_activated (GtkTreeView       *tree_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_HEADER, &is_header, -1);
+  return !is_header;
+}
+
+
+
+static void
+thunar_shortcuts_view_context_menu_header_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_header (ThunarShortcutsView *view,
+                                           GdkEventButton      *event,
+                                           GtkTreeModel        *model,
+                                           GtkTreeIter         *header_iter)
+{
+  GtkTreeIter          iter;
+  guint                mask = 0;
+  ThunarShortcutGroup  group;
+  GtkWidget           *menu;
+  GtkWidget           *mi;
+  gchar               *label;
+  GtkTreeSelection    *selection;
+  gboolean             visible;
+  GtkTreePath         *path;
+  GtkTreeModel        *child_model;
+
+  /* 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 = gtk_menu_new ();
+
+  /* process all items below the header */
+  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), &iter, header_iter);
+  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 (mask == 0 && (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_set_sensitive (mi, FALSE);
+          gtk_widget_show (mi);
+
+          mi = gtk_separator_menu_item_new ();
+          gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
+          gtk_widget_show (mi);
+        }
+      else if ((group & mask) != 0)
+        {
+          mi = gtk_check_menu_item_new_with_label (label);
+          gtk_menu_shell_append (GTK_MENU_SHELL (menu), 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_header_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,
@@ -883,6 +1015,15 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
   GList               *providers, *lp;
   GList               *actions = NULL, *tmp;
   ThunarShortcutGroup  group;
+  gboolean             is_header;
+
+  /* check if this is an item menu or a header menu */
+  gtk_tree_model_get (model, iter, THUNAR_SHORTCUTS_MODEL_COLUMN_HEADER, &is_header, -1);
+  if (is_header)
+    {
+      thunar_shortcuts_view_context_menu_header (view, event, model, iter);
+      return;
+    }
 
   /* determine the tree path for the given iter */
   path = gtk_tree_model_get_path (model, iter);
@@ -918,7 +1059,7 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
 
   switch (group)
     {
-      case THUNAR_SHORTCUT_GROUP_VOLUMES:
+      case THUNAR_SHORTCUT_GROUP_DEVICES_VOLUMES:
         /* append a menu separator */
         item = gtk_separator_menu_item_new ();
         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
@@ -943,7 +1084,7 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
         g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_eject), view);
         break;
 
-      case THUNAR_SHORTCUT_GROUP_MOUNTS:
+      case THUNAR_SHORTCUT_GROUP_DEVICES_MOUNTS:
       case THUNAR_SHORTCUT_GROUP_NETWORK_MOUNTS:
         /* append a menu separator */
         item = gtk_separator_menu_item_new ();
@@ -958,7 +1099,7 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
         gtk_widget_show (item);
         break;
 
-      case THUNAR_SHORTCUT_GROUP_TRASH:
+      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);
@@ -1020,7 +1161,7 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
     }
 
   /* append the remove menu item */
-  if (group == THUNAR_SHORTCUT_GROUP_BOOKMARKS)
+  if (group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS)
     {
       /* append a menu separator */
       item = gtk_separator_menu_item_new ();
@@ -1031,7 +1172,7 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
       g_object_set_data_full (G_OBJECT (item), I_("thunar-shortcuts-row"),
                               gtk_tree_row_reference_new (model, path),
                               (GDestroyNotify) gtk_tree_row_reference_free);
-      g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (thunar_shortcuts_view_remove_activated), view);
+      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);
@@ -1067,20 +1208,24 @@ thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
 
 
 static void
-thunar_shortcuts_view_remove_activated (GtkWidget           *item,
-                                        ThunarShortcutsView *view)
+thunar_shortcuts_view_remove_activated (GtkWidget    *item,
+                                        GtkTreeModel *model)
 {
   GtkTreeRowReference *row;
-  GtkTreeModel        *model;
+  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))
     {
-      model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
-      thunar_shortcuts_model_remove (THUNAR_SHORTCUTS_MODEL (model), path);
+      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);
     }
 }
 
@@ -1127,6 +1272,8 @@ thunar_shortcuts_view_renamed (GtkCellRenderer     *renderer,
 {
   GtkTreeModel *model;
   GtkTreeIter   iter;
+  GtkTreeModel *child_model;
+  GtkTreeIter   child_iter;
 
   /* reset the editable flag */
   g_object_set (G_OBJECT (renderer), "editable", FALSE, NULL);
@@ -1134,7 +1281,11 @@ thunar_shortcuts_view_renamed (GtkCellRenderer     *renderer,
   /* 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))
-    thunar_shortcuts_model_rename (THUNAR_SHORTCUTS_MODEL (model), &iter, text);
+    {
+      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);
+    }
 }
 
 
@@ -1248,8 +1399,11 @@ thunar_shortcuts_view_compute_drop_position (ThunarShortcutsView *view,
   GtkTreeViewColumn *column;
   GtkTreeModel      *model;
   GdkRectangle       area;
-  GtkTreePath       *path = NULL;
+  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);
@@ -1268,10 +1422,17 @@ thunar_shortcuts_view_compute_drop_position (ThunarShortcutsView *view,
       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))
-        if (thunar_shortcuts_model_drop_possible (THUNAR_SHORTCUTS_MODEL (model), path))
-          return 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);
     }
@@ -1291,12 +1452,15 @@ thunar_shortcuts_view_drop_uri_list (ThunarShortcutsView *view,
   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);
@@ -1313,7 +1477,10 @@ thunar_shortcuts_view_drop_uri_list (ThunarShortcutsView *view,
           break;
         }
 
-      thunar_shortcuts_model_add (THUNAR_SHORTCUTS_MODEL (model), path, file);
+      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);
     }
@@ -1755,12 +1922,17 @@ GtkWidget*
 thunar_shortcuts_view_new (void)
 {
   ThunarShortcutsModel *model;
-  GtkWidget             *view;
+  GtkWidget            *view;
+  GtkTreeModel         *filter_model;
 
   model = thunar_shortcuts_model_get_default ();
-  view = g_object_new (THUNAR_TYPE_SHORTCUTS_VIEW, "model", model, NULL);
+  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;
 }
 
@@ -1782,18 +1954,24 @@ thunar_shortcuts_view_select_by_file (ThunarShortcutsView *view,
   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));
 
-  /* clear the selection */
+  /* get the selection */
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
-  gtk_tree_selection_unselect_all (selection);
 
-  /* try to lookup a tree iter for the given file */
   model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
-  if (thunar_shortcuts_model_iter_for_file (THUNAR_SHORTCUTS_MODEL (model), file, &iter))
+  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);
 }
 
 


More information about the Xfce4-commits mailing list