From 2a91e49505a8090ef36f8bdc9384f5343e072866 Mon Sep 17 00:00:00 2001
From: Benedikt Meurer <benny@xfce.org>
Date: Thu, 1 Sep 2005 23:40:33 +0000
Subject: [PATCH] 2005-09-01	Benedikt Meurer <benny@xfce.org>

	* thunar-vfs/thunar-vfs-listdir-job.c: Fix the pre-sorting of files, so
	  upper layers always receive the info list sorted by name.
	* thunar/thunar-details-view.c(thunar_details_view_button_press_event),
	  thunar/thunar-icon-view.c(thunar_icon_view_button_press_event): Don't
	  popup the context menu immediately on right-clicks, but schedule the
	  menu popup using thunar_standard_view_queue_popup().
	* thunar/thunar-standard-view.{c,h}: Add the ability to start a drag
	  operation using the right mouse button.
	* thunar/thunar-file.{c,h}: Add virtual method accepts_uri_drop() and
	  method thunar_file_accepts_uri_drop(), which are used to determine
	  whether it is possible to drop a certain list of ThunarVfsURIs on
	  a given ThunarFile (using a set of actions specified by the drag
	  source).
	* thunar/thunar-local-file.c: Implement the accepts_uri_drop() method
	  for local file handling.
	* thunar/thunar-progress-dialog.c(thunar_progress_dialog_ask),
	  (thunar_progress_dialog_error): Be sure to display the progress dialog
	  prior to opening an error or question dialog.
	* thunar-vfs/thunar-vfs-info.c(thunar_vfs_info_rename): Fix gcc4
	  warning.
	* thunar-vfs/thunar-vfs.symbols: Add missing thunar_vfs_rename symbol.
	* thunar/thunar-favourites-model.c(thunar_favourites_model_get_value):
	  Work-around a compiler bug with newer gcc versions.
	* thunar/thunar-standard-view.{c,h}: Turn ThunarStandardView into a
	  valid drop site with support for text/uri-list drops.
	* thunar/Makefile.am, thunar/thunar-dnd.{c,h}: Add DnD helper functions,
	  which can be used by other modules as well (e.g. for the desktop
	  view).




(Old svn revision: 17266)
---
 ChangeLog                           |  31 ++
 thunar-vfs/thunar-vfs-info.c        |   2 +-
 thunar-vfs/thunar-vfs-listdir-job.c |  13 +-
 thunar-vfs/thunar-vfs.symbols       |   1 +
 thunar/Makefile.am                  |   2 +
 thunar/thunar-details-view.c        |  98 +++--
 thunar/thunar-dnd.c                 | 210 ++++++++++
 thunar/thunar-dnd.h                 |  38 ++
 thunar/thunar-favourites-model.c    |   5 +-
 thunar/thunar-file.c                |  67 ++++
 thunar/thunar-file.h                |   8 +
 thunar/thunar-icon-view.c           | 104 +++--
 thunar/thunar-local-file.c          |  28 ++
 thunar/thunar-progress-dialog.c     |   6 +
 thunar/thunar-standard-view.c       | 603 +++++++++++++++++++++++++---
 thunar/thunar-standard-view.h       |  45 ++-
 16 files changed, 1117 insertions(+), 144 deletions(-)
 create mode 100644 thunar/thunar-dnd.c
 create mode 100644 thunar/thunar-dnd.h

diff --git a/ChangeLog b/ChangeLog
index 4dea705b3..54d1f02a4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2005-09-01	Benedikt Meurer <benny@xfce.org>
+
+	* thunar-vfs/thunar-vfs-listdir-job.c: Fix the pre-sorting of files, so
+	  upper layers always receive the info list sorted by name.
+	* thunar/thunar-details-view.c(thunar_details_view_button_press_event),
+	  thunar/thunar-icon-view.c(thunar_icon_view_button_press_event): Don't
+	  popup the context menu immediately on right-clicks, but schedule the
+	  menu popup using thunar_standard_view_queue_popup().
+	* thunar/thunar-standard-view.{c,h}: Add the ability to start a drag
+	  operation using the right mouse button.
+	* thunar/thunar-file.{c,h}: Add virtual method accepts_uri_drop() and
+	  method thunar_file_accepts_uri_drop(), which are used to determine
+	  whether it is possible to drop a certain list of ThunarVfsURIs on
+	  a given ThunarFile (using a set of actions specified by the drag
+	  source).
+	* thunar/thunar-local-file.c: Implement the accepts_uri_drop() method
+	  for local file handling.
+	* thunar/thunar-progress-dialog.c(thunar_progress_dialog_ask),
+	  (thunar_progress_dialog_error): Be sure to display the progress dialog
+	  prior to opening an error or question dialog.
+	* thunar-vfs/thunar-vfs-info.c(thunar_vfs_info_rename): Fix gcc4
+	  warning.
+	* thunar-vfs/thunar-vfs.symbols: Add missing thunar_vfs_rename symbol.
+	* thunar/thunar-favourites-model.c(thunar_favourites_model_get_value):
+	  Work-around a compiler bug with newer gcc versions.
+	* thunar/thunar-standard-view.{c,h}: Turn ThunarStandardView into a
+	  valid drop site with support for text/uri-list drops.
+	* thunar/Makefile.am, thunar/thunar-dnd.{c,h}: Add DnD helper functions,
+	  which can be used by other modules as well (e.g. for the desktop
+	  view).
+
 2005-08-30	Benedikt Meurer <benny@xfce.org>
 
 	* thunar/thunar-favourites-model.c: Drop the icon caching from the
diff --git a/thunar-vfs/thunar-vfs-info.c b/thunar-vfs/thunar-vfs-info.c
index 691f5d040..605b84e8f 100644
--- a/thunar-vfs/thunar-vfs-info.c
+++ b/thunar-vfs/thunar-vfs-info.c
@@ -458,7 +458,7 @@ thunar_vfs_info_rename (ThunarVfsInfo *info,
   const gchar * const   *locale;
   const gchar           *src_path;
   GKeyFile              *key_file;
-  gssize                 data_length;
+  gsize                  data_length;
   gchar                 *data;
   gchar                 *key;
   gchar                 *dir_name;
diff --git a/thunar-vfs/thunar-vfs-listdir-job.c b/thunar-vfs/thunar-vfs-listdir-job.c
index 4d2db0264..338230c02 100644
--- a/thunar-vfs/thunar-vfs-listdir-job.c
+++ b/thunar-vfs/thunar-vfs-listdir-job.c
@@ -159,15 +159,6 @@ thunar_vfs_listdir_job_finalize (ExoObject *object)
 
 
 
-static gint
-namecmp (gconstpointer name_a,
-         gconstpointer name_b)
-{
-  return -1 * strcmp (name_a, name_b);
-}
-
-
-
 static void
 thunar_vfs_listdir_job_execute (ThunarVfsJob *job)
 {
@@ -198,7 +189,7 @@ thunar_vfs_listdir_job_execute (ThunarVfsJob *job)
        * disk seeking.
        */
       while (_thunar_vfs_sysdep_readdir (dp, &d_buffer, &d, &error) && d != NULL)
-        names = g_slist_insert_sorted (names, g_string_chunk_insert (names_chunk, d->d_name), namecmp);
+        names = g_slist_insert_sorted (names, g_string_chunk_insert (names_chunk, d->d_name), (GCompareFunc) strcmp);
 
       closedir (dp);
 
@@ -216,7 +207,7 @@ thunar_vfs_listdir_job_execute (ThunarVfsJob *job)
           file_uri = thunar_vfs_uri_relative (THUNAR_VFS_LISTDIR_JOB (job)->uri, lp->data);
           info = thunar_vfs_info_new_for_uri (file_uri, NULL);
           if (G_LIKELY (info != NULL))
-            infos = g_slist_prepend (infos, info);
+            infos = g_slist_append (infos, info);
           thunar_vfs_uri_unref (file_uri);
 
           current_time = time (NULL);
diff --git a/thunar-vfs/thunar-vfs.symbols b/thunar-vfs/thunar-vfs.symbols
index 7d54dd836..d30f36d64 100644
--- a/thunar-vfs/thunar-vfs.symbols
+++ b/thunar-vfs/thunar-vfs.symbols
@@ -53,6 +53,7 @@ thunar_vfs_info_new_for_uri
 thunar_vfs_info_ref
 thunar_vfs_info_unref
 thunar_vfs_info_execute
+thunar_vfs_info_rename
 thunar_vfs_info_get_hint
 thunar_vfs_info_matches
 thunar_vfs_info_list_free
diff --git a/thunar/Makefile.am b/thunar/Makefile.am
index 320ad26b0..6999f2296 100644
--- a/thunar/Makefile.am
+++ b/thunar/Makefile.am
@@ -31,6 +31,8 @@ Thunar_SOURCES =							\
 	thunar-desktop-window.h						\
 	thunar-details-view.c						\
 	thunar-details-view.h						\
+	thunar-dnd.c							\
+	thunar-dnd.h							\
 	thunar-fallback-icon.c						\
 	thunar-fallback-icon.h						\
 	thunar-favourites-model.c					\
diff --git a/thunar/thunar-details-view.c b/thunar/thunar-details-view.c
index 39aa85b5e..d13fd6b47 100644
--- a/thunar/thunar-details-view.c
+++ b/thunar/thunar-details-view.c
@@ -27,33 +27,37 @@
 #include <thunar/thunar-text-renderer.h>
 
 
-
-static void       thunar_details_view_class_init          (ThunarDetailsViewClass *klass);
-static void       thunar_details_view_init                (ThunarDetailsView      *details_view);
-static AtkObject *thunar_details_view_get_accessible      (GtkWidget              *widget);
-static GList     *thunar_details_view_get_selected_items  (ThunarStandardView     *standard_view);
-static void       thunar_details_view_select_all          (ThunarStandardView     *standard_view);
-static void       thunar_details_view_unselect_all        (ThunarStandardView     *standard_view);
-static void       thunar_details_view_select_path         (ThunarStandardView     *standard_view,
-                                                           GtkTreePath            *path);
-static void       thunar_details_view_set_cursor          (ThunarStandardView     *standard_view,
-                                                           GtkTreePath            *path,
-                                                           gboolean                start_editing);
-static void       thunar_details_view_scroll_to_path      (ThunarStandardView     *standard_view,
-                                                           GtkTreePath            *path);
-static void       thunar_details_view_notify_model        (GtkTreeView            *tree_view,
-                                                           GParamSpec             *pspec,
-                                                           ThunarDetailsView      *details_view);
-static gboolean   thunar_details_view_button_press_event  (GtkTreeView            *tree_view,
-                                                           GdkEventButton         *event,
-                                                           ThunarDetailsView      *details_view);
-static gboolean   thunar_details_view_key_press_event     (GtkTreeView            *tree_view,
-                                                           GdkEventKey            *event,
-                                                           ThunarDetailsView      *details_view);
-static void       thunar_details_view_row_activated       (GtkTreeView            *tree_view,
-                                                           GtkTreePath            *path,
-                                                           GtkTreeViewColumn      *column,
-                                                           ThunarDetailsView      *details_view);
+static void         thunar_details_view_class_init          (ThunarDetailsViewClass *klass);
+static void         thunar_details_view_init                (ThunarDetailsView      *details_view);
+static AtkObject   *thunar_details_view_get_accessible      (GtkWidget              *widget);
+static GList       *thunar_details_view_get_selected_items  (ThunarStandardView     *standard_view);
+static void         thunar_details_view_select_all          (ThunarStandardView     *standard_view);
+static void         thunar_details_view_unselect_all        (ThunarStandardView     *standard_view);
+static void         thunar_details_view_select_path         (ThunarStandardView     *standard_view,
+                                                             GtkTreePath            *path);
+static void         thunar_details_view_set_cursor          (ThunarStandardView     *standard_view,
+                                                             GtkTreePath            *path,
+                                                             gboolean                start_editing);
+static void         thunar_details_view_scroll_to_path      (ThunarStandardView     *standard_view,
+                                                             GtkTreePath            *path);
+static GtkTreePath *thunar_details_view_get_path_at_pos     (ThunarStandardView     *standard_view,
+                                                             gint                    x,
+                                                             gint                    y);
+static void         thunar_details_view_highlight_path      (ThunarStandardView     *standard_view,
+                                                             GtkTreePath            *path);
+static void         thunar_details_view_notify_model        (GtkTreeView            *tree_view,
+                                                             GParamSpec             *pspec,
+                                                             ThunarDetailsView      *details_view);
+static gboolean     thunar_details_view_button_press_event  (GtkTreeView            *tree_view,
+                                                             GdkEventButton         *event,
+                                                             ThunarDetailsView      *details_view);
+static gboolean     thunar_details_view_key_press_event     (GtkTreeView            *tree_view,
+                                                             GdkEventKey            *event,
+                                                             ThunarDetailsView      *details_view);
+static void         thunar_details_view_row_activated       (GtkTreeView            *tree_view,
+                                                             GtkTreePath            *path,
+                                                             GtkTreeViewColumn      *column,
+                                                             ThunarDetailsView      *details_view);
 
 
 
@@ -89,6 +93,8 @@ thunar_details_view_class_init (ThunarDetailsViewClass *klass)
   thunarstandard_view_class->select_path = thunar_details_view_select_path;
   thunarstandard_view_class->set_cursor = thunar_details_view_set_cursor;
   thunarstandard_view_class->scroll_to_path = thunar_details_view_scroll_to_path;
+  thunarstandard_view_class->get_path_at_pos = thunar_details_view_get_path_at_pos;
+  thunarstandard_view_class->highlight_path = thunar_details_view_highlight_path;
 }
 
 
@@ -315,6 +321,33 @@ thunar_details_view_scroll_to_path (ThunarStandardView *standard_view,
 
 
 
+static GtkTreePath*
+thunar_details_view_get_path_at_pos (ThunarStandardView *standard_view,
+                                     gint                x,
+                                     gint                y)
+{
+  GtkTreePath *path;
+
+  g_return_val_if_fail (THUNAR_IS_DETAILS_VIEW (standard_view), NULL);
+
+  if (gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (GTK_BIN (standard_view)->child), x, y, &path, NULL))
+    return path;
+
+  return NULL;
+}
+
+
+
+static void
+thunar_details_view_highlight_path (ThunarStandardView *standard_view,
+                                    GtkTreePath        *path)
+{
+  g_return_if_fail (THUNAR_IS_DETAILS_VIEW (standard_view));
+  gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (GTK_BIN (standard_view)->child), path, GTK_TREE_VIEW_DROP_INTO_OR_AFTER);
+}
+
+
+
 static void
 thunar_details_view_notify_model (GtkTreeView       *tree_view,
                                   GParamSpec        *pspec,
@@ -361,10 +394,15 @@ thunar_details_view_button_press_event (GtkTreeView       *tree_view,
               gtk_tree_selection_select_path (selection, path);
             }
           gtk_tree_path_free (path);
-        }
 
-      /* open the context menu */
-      thunar_standard_view_context_menu (THUNAR_STANDARD_VIEW (details_view), event->button, event->time);
+          /* queue the menu popup */
+          thunar_standard_view_queue_popup (THUNAR_STANDARD_VIEW (details_view), event);
+        }
+      else
+        {
+          /* open the context menu */
+          thunar_standard_view_context_menu (THUNAR_STANDARD_VIEW (details_view), event->button, event->time);
+        }
 
       return TRUE;
     }
diff --git a/thunar/thunar-dnd.c b/thunar/thunar-dnd.c
new file mode 100644
index 000000000..118c58fa1
--- /dev/null
+++ b/thunar/thunar-dnd.c
@@ -0,0 +1,210 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <thunar/thunar-application.h>
+#include <thunar/thunar-dnd.h>
+
+
+
+static void
+action_selected (GtkWidget     *item,
+                 GdkDragAction *action)
+{
+  *action = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (item), "action"));
+}
+
+
+
+/**
+ * thunar_dnd_ask:
+ * @widget  : the widget on which the drop was performed.
+ * @time    : the time of the drop event.
+ * @actions : the list of actions supported for the drop.
+ *
+ * Pops up a menu that asks the user to choose one of the
+ * @actions or to cancel the drop. If the user chooses a
+ * valid #GdkDragAction from @actions, then this action is
+ * returned. Else if the user cancels the drop, 0 will be
+ * returned.
+ *
+ * This method can be used to implement a response to the
+ * #GDK_ACTION_ASK action on drops.
+ *
+ * Return value: the selected #GdkDragAction or 0 to cancel.
+ **/
+GdkDragAction
+thunar_dnd_ask (GtkWidget    *widget,
+                guint         time,
+                GdkDragAction actions)
+{
+  static const GdkDragAction action_items[] = { GDK_ACTION_COPY, GDK_ACTION_MOVE, GDK_ACTION_LINK };
+  static const gchar        *action_names[] = { N_ ("_Copy here"), N_ ("_Move here"), N_ ("_Link here") };
+
+  GdkDragAction action = 0;
+  GtkWidget    *menu;
+  GtkWidget    *item;
+  GMainLoop    *loop;
+  guint         n;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
+
+  /* prepare the internal loop */
+  loop = g_main_loop_new (NULL, FALSE);
+
+  /* prepare the popup menu */
+  menu = gtk_menu_new ();
+  gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
+  g_signal_connect_swapped (G_OBJECT (menu), "deactivate", G_CALLBACK (g_main_loop_quit), loop);
+
+  /* append the various items */
+  for (n = 0; n < G_N_ELEMENTS (action_items); ++n)
+    if (G_LIKELY ((actions & action_items[n]) != 0))
+      {
+        item = gtk_image_menu_item_new_with_mnemonic (_(action_names[n]));
+        g_object_set_data (G_OBJECT (item), "action", GUINT_TO_POINTER (action_items[n]));
+        g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (action_selected), &action);
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+        gtk_widget_show (item);
+      }
+
+  /* append the separator */
+  item = gtk_separator_menu_item_new ();
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+  gtk_widget_show (item);
+
+  /* append the cancel item */
+  item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CANCEL, NULL);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+  gtk_widget_show (item);
+
+  /* run the internal loop */
+  gtk_grab_add (menu);
+  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 3, time);
+  g_main_loop_run (loop);
+  gtk_grab_remove (menu);
+
+  /* clean up */
+  gtk_object_sink (GTK_OBJECT (menu));
+  g_main_loop_unref (loop);
+
+  return action;
+}
+
+
+
+/**
+ * thunar_dnd_perform:
+ * @widget   : the #GtkWidget on which the drop was done.
+ * @file     : the #ThunarFile on which the @uri_list was dropped.
+ * @uri_list : the list of #ThunarVfsURI<!---->s that was dropped.
+ * @action   : the #GdkDragAction that was performed.
+ *
+ * Performs the drop of @uri_list on @file in @widget, as given in
+ * @action and returns %TRUE if the drop was started successfully
+ * (or even completed successfully), else %FALSE.
+ *
+ * Return value: %TRUE if the DnD operation was started
+ *               successfully, else %FALSE.
+ **/
+gboolean
+thunar_dnd_perform (GtkWidget    *widget,
+                    ThunarFile   *file,
+                    GList        *uri_list,
+                    GdkDragAction action)
+{
+  ThunarApplication *application;
+  GtkWidget         *message;
+  GtkWidget         *window;
+  gboolean           succeed = TRUE;
+  GError            *error = NULL;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+  g_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
+
+  /* query a reference on the application object */
+  application = thunar_application_get ();
+
+  /* determine the toplevel window for the widget */
+  window = gtk_widget_get_toplevel (widget);
+
+  /* check if the file is a directory */
+  if (thunar_file_is_directory (file))
+    {
+      /* perform the given directory operation */
+      switch (action)
+        {
+        case GDK_ACTION_COPY:
+          thunar_application_copy_uris (application, GTK_WINDOW (window), uri_list, thunar_file_get_uri (file));
+          break;
+
+        case GDK_ACTION_MOVE:
+          thunar_application_move_uris (application, GTK_WINDOW (window), uri_list, thunar_file_get_uri (file));
+          break;
+
+        case GDK_ACTION_LINK:
+          // FIXME
+          message = gtk_message_dialog_new (GTK_WINDOW (window),
+                                            GTK_DIALOG_MODAL
+                                            | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                            GTK_MESSAGE_ERROR,
+                                            GTK_BUTTONS_OK,
+                                            _("Creating links is not yet supported. This will be fixed soon!"));
+          gtk_dialog_run (GTK_DIALOG (message));
+          gtk_widget_destroy (message);
+          break;
+
+        default:
+          succeed = FALSE;
+        }
+    }
+  else if (thunar_file_is_executable (file))
+    {
+      succeed = thunar_file_execute (file, gtk_widget_get_screen (widget), uri_list, &error);
+      if (G_UNLIKELY (!succeed))
+        {
+          message = gtk_message_dialog_new (GTK_WINDOW (window),
+                                            GTK_DIALOG_MODAL
+                                            | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                            GTK_MESSAGE_ERROR,
+                                            GTK_BUTTONS_OK,
+                                            _("Unable to execute file \"%s\"."),
+                                            thunar_file_get_display_name (file));
+          gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (message), "%s.", error->message);
+          gtk_dialog_run (GTK_DIALOG (message));
+          gtk_widget_destroy (message);
+          g_error_free (error);
+        }
+    }
+  else
+    {
+      succeed = FALSE;
+    }
+
+  /* release the application reference */
+  g_object_unref (G_OBJECT (application));
+
+  return succeed;
+}
+
+
+
diff --git a/thunar/thunar-dnd.h b/thunar/thunar-dnd.h
new file mode 100644
index 000000000..930a112ba
--- /dev/null
+++ b/thunar/thunar-dnd.h
@@ -0,0 +1,38 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __THUNAR_DND_H__
+#define __THUNAR_DND_H__
+
+#include <thunar/thunar-file.h>
+
+G_BEGIN_DECLS;
+
+GdkDragAction thunar_dnd_ask     (GtkWidget    *widget,
+                                  guint         time,
+                                  GdkDragAction actions);
+
+gboolean      thunar_dnd_perform (GtkWidget    *widget,
+                                  ThunarFile   *file,
+                                  GList        *uri_list,
+                                  GdkDragAction action);
+
+G_END_DECLS;
+
+#endif /* !__THUNAR_DND_H__ */
diff --git a/thunar/thunar-favourites-model.c b/thunar/thunar-favourites-model.c
index ddf3890aa..c1ed2a434 100644
--- a/thunar/thunar-favourites-model.c
+++ b/thunar/thunar-favourites-model.c
@@ -454,7 +454,7 @@ thunar_favourites_model_get_value (GtkTreeModel *tree_model,
   ThunarFavourite       *favourite;
   GtkIconTheme          *icon_theme;
   const gchar           *icon_name;
-  GdkPixbuf             *icon;
+  GdkPixbuf             *icon = NULL;
 
   g_return_if_fail (THUNAR_IS_FAVOURITES_MODEL (model));
   g_return_if_fail (iter->stamp == model->stamp);
@@ -489,7 +489,8 @@ thunar_favourites_model_get_value (GtkTreeModel *tree_model,
         {
           icon = thunar_file_load_icon (favourite->file, icon_factory, 32);
         }
-      g_value_take_object (value, icon);
+      if (G_LIKELY (icon != NULL))
+        g_value_take_object (value, icon);
       break;
 
     case THUNAR_FAVOURITES_MODEL_COLUMN_MUTABLE:
diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c
index a58e1689d..e147b722c 100644
--- a/thunar/thunar-file.c
+++ b/thunar/thunar-file.c
@@ -163,6 +163,7 @@ thunar_file_class_init (ThunarFileClass *klass)
   klass->execute = thunar_file_real_execute;
   klass->rename = thunar_file_real_rename;
   klass->open_as_folder = thunar_file_real_open_as_folder;
+  klass->accepts_uri_drop = (gpointer) exo_noop_zero;
   klass->get_mime_info = (gpointer) exo_noop_null;
   klass->get_special_name = thunar_file_real_get_special_name;
   klass->get_date = (gpointer) exo_noop_false;
@@ -688,6 +689,72 @@ thunar_file_open_as_folder (ThunarFile *file,
 
 
 
+/**
+ * thunar_file_accepts_uri_drop:
+ * @file     : a #ThunarFile instance.
+ * @uri_list : the list of #ThunarVfsURI<!---->s that will be droppped.
+ * @actions  : the #GdkDragAction<!---->s provided by the drag source.
+ *
+ * Checks whether @file can accept @uri_list for the given @actions and
+ * returns the #GdkDragAction<!---->s that can be used or 0 if no
+ * actions apply.
+ *
+ * Return value: the #GdkDragAction<!---->s supported for the drop or
+ *               0 if no drop is possible.
+ **/
+GdkDragAction
+thunar_file_accepts_uri_drop (ThunarFile   *file,
+                              GList        *uri_list,
+                              GdkDragAction actions)
+{
+  GdkDragAction action;
+  ThunarVfsURI *parent_uri;
+  ThunarVfsURI *uri;
+  GList        *lp;
+  guint         n;
+
+  g_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
+
+  /* we can never drop an empty list */
+  if (G_UNLIKELY (uri_list == NULL))
+    return 0;
+
+  /* determine the uri of the file */
+  uri = thunar_file_get_uri (file);
+
+  /* check up to 500 of the URIs (just in case somebody tries to
+   * drag around his music collection with 5000 files).
+   */
+  for (lp = uri_list, n = 0; actions != 0 && lp != NULL && n < 500; lp = lp->next, ++n)
+    {
+      /* we cannot drop a file on itself */
+      if (G_UNLIKELY (thunar_vfs_uri_equal (uri, lp->data)))
+        return 0;
+
+      /* check whether source and destination are the same */
+      parent_uri = thunar_vfs_uri_parent (lp->data);
+      if (G_LIKELY (parent_uri != NULL))
+        {
+          if (thunar_vfs_uri_equal (uri, parent_uri))
+            actions = 0;
+          thunar_vfs_uri_unref (parent_uri);
+        }
+
+      /* check if this URI is supported */
+      action = (*THUNAR_FILE_GET_CLASS (file)->accepts_uri_drop) (file, lp->data, action);
+      if (G_UNLIKELY (action == 0))
+        return 0;
+
+      /* drop actions not supported for this URI from the actions list */
+      actions &= action;
+    }
+
+  /* check if we can drop */
+  return actions;
+}
+
+
+
 /**
  * thunar_file_get_uri:
  * @file  : a #ThunarFile instance.
diff --git a/thunar/thunar-file.h b/thunar/thunar-file.h
index 1e8f37a87..fd19ac3d6 100644
--- a/thunar/thunar-file.h
+++ b/thunar/thunar-file.h
@@ -83,6 +83,10 @@ struct _ThunarFileClass
   ThunarFolder        *(*open_as_folder)      (ThunarFile             *file,
                                                GError                **error);
 
+  GdkDragAction        (*accepts_uri_drop)    (ThunarFile             *file,
+                                               const ThunarVfsURI     *uri,
+                                               GdkDragAction           actions);
+
   ThunarVfsURI        *(*get_uri)             (ThunarFile             *file);
 
   ThunarVfsMimeInfo   *(*get_mime_info)       (ThunarFile             *file);
@@ -159,6 +163,10 @@ gboolean           thunar_file_rename           (ThunarFile             *file,
 ThunarFolder      *thunar_file_open_as_folder   (ThunarFile             *file,
                                                  GError                **error);
 
+GdkDragAction      thunar_file_accepts_uri_drop (ThunarFile             *file,
+                                                 GList                  *uri_list,
+                                                 GdkDragAction           actions);
+
 ThunarVfsURI      *thunar_file_get_uri          (ThunarFile             *file);
 
 ThunarVfsMimeInfo *thunar_file_get_mime_info    (ThunarFile             *file);
diff --git a/thunar/thunar-icon-view.c b/thunar/thunar-icon-view.c
index bacb66564..4217051d1 100644
--- a/thunar/thunar-icon-view.c
+++ b/thunar/thunar-icon-view.c
@@ -28,37 +28,42 @@
 
 
 
-static void       thunar_icon_view_class_init             (ThunarIconViewClass *klass);
-static void       thunar_icon_view_init                   (ThunarIconView      *icon_view);
-static AtkObject *thunar_icon_view_get_accessible         (GtkWidget           *widget);
-static void       thunar_icon_view_connect_ui_manager     (ThunarStandardView  *standard_view,
-                                                           GtkUIManager        *ui_manager);
-static void       thunar_icon_view_disconnect_ui_manager  (ThunarStandardView  *standard_view,
-                                                           GtkUIManager        *ui_manager);
-static GList     *thunar_icon_view_get_selected_items     (ThunarStandardView  *standard_view);
-static void       thunar_icon_view_select_all             (ThunarStandardView  *standard_view);
-static void       thunar_icon_view_unselect_all           (ThunarStandardView  *standard_view);
-static void       thunar_icon_view_select_path            (ThunarStandardView  *standard_view,
-                                                           GtkTreePath         *path);
-static void       thunar_icon_view_set_cursor             (ThunarStandardView  *standard_view,
-                                                           GtkTreePath         *path,
-                                                           gboolean             start_editing);
-static void       thunar_icon_view_scroll_to_path         (ThunarStandardView  *standard_view,
-                                                           GtkTreePath         *path);
-static void       thunar_icon_view_action_sort            (GtkAction           *action,
-                                                           GtkAction           *current,
-                                                           ThunarStandardView  *standard_view);
-static gboolean   thunar_icon_view_button_press_event     (ExoIconView         *view,
-                                                           GdkEventButton      *event,
-                                                           ThunarIconView      *icon_view);
-static gboolean   thunar_icon_view_key_press_event        (ExoIconView         *view,
-                                                           GdkEventKey         *event,
-                                                           ThunarIconView      *icon_view);
-static void       thunar_icon_view_item_activated         (ExoIconView         *view,
-                                                           GtkTreePath         *path,
-                                                           ThunarIconView      *icon_view);
-static void       thunar_icon_view_sort_column_changed    (GtkTreeSortable     *sortable,
-                                                           ThunarIconView      *icon_view);
+static void         thunar_icon_view_class_init             (ThunarIconViewClass *klass);
+static void         thunar_icon_view_init                   (ThunarIconView      *icon_view);
+static AtkObject   *thunar_icon_view_get_accessible         (GtkWidget           *widget);
+static void         thunar_icon_view_connect_ui_manager     (ThunarStandardView  *standard_view,
+                                                             GtkUIManager        *ui_manager);
+static void         thunar_icon_view_disconnect_ui_manager  (ThunarStandardView  *standard_view,
+                                                             GtkUIManager        *ui_manager);
+static GList       *thunar_icon_view_get_selected_items     (ThunarStandardView  *standard_view);
+static void         thunar_icon_view_select_all             (ThunarStandardView  *standard_view);
+static void         thunar_icon_view_unselect_all           (ThunarStandardView  *standard_view);
+static void         thunar_icon_view_select_path            (ThunarStandardView  *standard_view,
+                                                             GtkTreePath         *path);
+static void         thunar_icon_view_set_cursor             (ThunarStandardView  *standard_view,
+                                                             GtkTreePath         *path,
+                                                             gboolean             start_editing);
+static void         thunar_icon_view_scroll_to_path         (ThunarStandardView  *standard_view,
+                                                             GtkTreePath         *path);
+static GtkTreePath *thunar_icon_view_get_path_at_pos        (ThunarStandardView  *standard_view,
+                                                             gint                 x,
+                                                             gint                 y);
+static void         thunar_icon_view_highlight_path         (ThunarStandardView  *standard_view,
+                                                             GtkTreePath         *path);
+static void         thunar_icon_view_action_sort            (GtkAction           *action,
+                                                             GtkAction           *current,
+                                                             ThunarStandardView  *standard_view);
+static gboolean     thunar_icon_view_button_press_event     (ExoIconView         *view,
+                                                             GdkEventButton      *event,
+                                                             ThunarIconView      *icon_view);
+static gboolean     thunar_icon_view_key_press_event        (ExoIconView         *view,
+                                                             GdkEventKey         *event,
+                                                             ThunarIconView      *icon_view);
+static void         thunar_icon_view_item_activated         (ExoIconView         *view,
+                                                             GtkTreePath         *path,
+                                                             ThunarIconView      *icon_view);
+static void         thunar_icon_view_sort_column_changed    (GtkTreeSortable     *sortable,
+                                                             ThunarIconView      *icon_view);
 
 
 
@@ -119,6 +124,8 @@ thunar_icon_view_class_init (ThunarIconViewClass *klass)
   thunarstandard_view_class->select_path = thunar_icon_view_select_path;
   thunarstandard_view_class->set_cursor = thunar_icon_view_set_cursor;
   thunarstandard_view_class->scroll_to_path = thunar_icon_view_scroll_to_path;
+  thunarstandard_view_class->get_path_at_pos = thunar_icon_view_get_path_at_pos;
+  thunarstandard_view_class->highlight_path = thunar_icon_view_highlight_path;
 }
 
 
@@ -295,6 +302,32 @@ thunar_icon_view_scroll_to_path (ThunarStandardView *standard_view,
 
 
 
+static GtkTreePath*
+thunar_icon_view_get_path_at_pos (ThunarStandardView *standard_view,
+                                  gint                x,
+                                  gint                y)
+{
+  g_return_val_if_fail (THUNAR_IS_ICON_VIEW (standard_view), NULL);
+
+  /* translate the widget coordinates to icon window coordinates */
+  exo_icon_view_widget_to_icon_coords (EXO_ICON_VIEW (GTK_BIN (standard_view)->child), x, y, &x, &y);
+
+  /* determine the path at the position */
+  return exo_icon_view_get_path_at_pos (EXO_ICON_VIEW (GTK_BIN (standard_view)->child), x, y);
+}
+
+
+
+static void
+thunar_icon_view_highlight_path (ThunarStandardView *standard_view,
+                                 GtkTreePath        *path)
+{
+  g_return_if_fail (THUNAR_IS_ICON_VIEW (standard_view));
+  exo_icon_view_set_drag_dest_item (EXO_ICON_VIEW (GTK_BIN (standard_view)->child), path, EXO_ICON_VIEW_DROP_INTO);
+}
+
+
+
 static void
 thunar_icon_view_action_sort (GtkAction          *action,
                               GtkAction          *current,
@@ -338,6 +371,9 @@ thunar_icon_view_button_press_event (ExoIconView    *view,
               exo_icon_view_select_path (view, path);
             }
           gtk_tree_path_free (path);
+
+          /* queue the menu popup */
+          thunar_standard_view_queue_popup (THUNAR_STANDARD_VIEW (icon_view), event);
         }
       else if ((event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
         {
@@ -345,11 +381,11 @@ thunar_icon_view_button_press_event (ExoIconView    *view,
            * to make sure that the folder context menu is opened.
            */
           exo_icon_view_unselect_all (view);
+      
+          /* open the context menu */
+          thunar_standard_view_context_menu (THUNAR_STANDARD_VIEW (icon_view), event->button, event->time);
         }
 
-      /* open the context menu */
-      thunar_standard_view_context_menu (THUNAR_STANDARD_VIEW (icon_view), event->button, event->time);
-
       return TRUE;
     }
 
diff --git a/thunar/thunar-local-file.c b/thunar/thunar-local-file.c
index feab8653a..2da5ee736 100644
--- a/thunar/thunar-local-file.c
+++ b/thunar/thunar-local-file.c
@@ -39,6 +39,9 @@ static gboolean           thunar_local_file_rename            (ThunarFile
                                                                GError                **error);
 static ThunarFolder      *thunar_local_file_open_as_folder    (ThunarFile             *file,
                                                                GError                **error);
+static GdkDragAction      thunar_local_file_accepts_uri_drop  (ThunarFile             *file,
+                                                               const ThunarVfsURI     *uri,
+                                                               GdkDragAction           actions);
 static ThunarVfsURI      *thunar_local_file_get_uri           (ThunarFile             *file);
 static ThunarVfsMimeInfo *thunar_local_file_get_mime_info     (ThunarFile             *file);
 static const gchar       *thunar_local_file_get_display_name  (ThunarFile             *file);
@@ -145,6 +148,7 @@ thunar_local_file_class_init (ThunarLocalFileClass *klass)
   thunarfile_class->execute = thunar_local_file_execute;
   thunarfile_class->rename = thunar_local_file_rename;
   thunarfile_class->open_as_folder = thunar_local_file_open_as_folder;
+  thunarfile_class->accepts_uri_drop = thunar_local_file_accepts_uri_drop;
   thunarfile_class->get_uri = thunar_local_file_get_uri;
   thunarfile_class->get_mime_info = thunar_local_file_get_mime_info;
   thunarfile_class->get_display_name = thunar_local_file_get_display_name;
@@ -271,6 +275,30 @@ thunar_local_file_open_as_folder (ThunarFile *file,
 
 
 
+static GdkDragAction
+thunar_local_file_accepts_uri_drop (ThunarFile         *file,
+                                    const ThunarVfsURI *uri,
+                                    GdkDragAction       actions)
+{
+  ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (file);
+
+  /* we can only drop local files here (for now) */
+  if (G_LIKELY (thunar_vfs_uri_get_scheme (uri) == THUNAR_VFS_URI_SCHEME_FILE))
+    {
+      /* check if we have a writable directory here */
+      if (G_LIKELY (local_file->info->type == THUNAR_VFS_FILE_TYPE_DIRECTORY))
+        return GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK;
+
+      /* check if we can execute the file */
+      if ((local_file->info->flags & THUNAR_VFS_FILE_FLAGS_EXECUTABLE) != 0)
+        return GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_PRIVATE;
+    }
+
+  return 0;
+}
+
+
+
 static ThunarVfsURI*
 thunar_local_file_get_uri (ThunarFile *file)
 {
diff --git a/thunar/thunar-progress-dialog.c b/thunar/thunar-progress-dialog.c
index e9cc3ab9d..b9f16c1af 100644
--- a/thunar/thunar-progress-dialog.c
+++ b/thunar/thunar-progress-dialog.c
@@ -258,6 +258,9 @@ thunar_progress_dialog_ask (ThunarProgressDialog           *dialog,
   g_return_val_if_fail (THUNAR_VFS_IS_INTERACTIVE_JOB (job), THUNAR_VFS_INTERACTIVE_JOB_RESPONSE_CANCEL);
   g_return_val_if_fail (dialog->job == job, THUNAR_VFS_INTERACTIVE_JOB_RESPONSE_CANCEL);
 
+  /* be sure to display the progress dialog prior to opening the question dialog */
+  gtk_widget_show_now (GTK_WIDGET (dialog));
+
   question = g_object_new (GTK_TYPE_DIALOG,
                            "has-separator", FALSE,
                            "resizable", FALSE,
@@ -340,6 +343,9 @@ thunar_progress_dialog_error (ThunarProgressDialog *dialog,
   g_return_if_fail (THUNAR_VFS_IS_INTERACTIVE_JOB (job));
   g_return_if_fail (dialog->job == job);
 
+  /* be sure to display the progress dialog prior to opening the error dialog */
+  gtk_widget_show_now (GTK_WIDGET (dialog));
+
   message = gtk_message_dialog_new (GTK_WINDOW (dialog),
                                     GTK_DIALOG_MODAL |
                                     GTK_DIALOG_DESTROY_WITH_PARENT,
diff --git a/thunar/thunar-standard-view.c b/thunar/thunar-standard-view.c
index a1e9c6c70..65c345cc7 100644
--- a/thunar/thunar-standard-view.c
+++ b/thunar/thunar-standard-view.c
@@ -30,6 +30,7 @@
 
 #include <thunarx/thunarx-gtk-extensions.h>
 
+#include <thunar/thunar-dnd.h>
 #include <thunar/thunar-icon-renderer.h>
 #include <thunar/thunar-launcher.h>
 #include <thunar/thunar-properties-dialog.h>
@@ -81,6 +82,8 @@ static void          thunar_standard_view_set_property              (GObject
 static void          thunar_standard_view_realize                   (GtkWidget                *widget);
 static void          thunar_standard_view_unrealize                 (GtkWidget                *widget);
 static void          thunar_standard_view_grab_focus                (GtkWidget                *widget);
+static gboolean      thunar_standard_view_expose_event              (GtkWidget                *widget,
+                                                                     GdkEventExpose           *event);
 static ThunarFile   *thunar_standard_view_get_current_directory     (ThunarNavigator          *navigator);
 static void          thunar_standard_view_set_current_directory     (ThunarNavigator          *navigator,
                                                                      ThunarFile               *current_directory);
@@ -89,6 +92,12 @@ static const gchar  *thunar_standard_view_get_statusbar_text        (ThunarView
 static GtkUIManager *thunar_standard_view_get_ui_manager            (ThunarView               *view);
 static void          thunar_standard_view_set_ui_manager            (ThunarView               *view,
                                                                      GtkUIManager             *ui_manager);
+static GdkDragAction thunar_standard_view_get_dest_actions          (ThunarStandardView       *standard_view,
+                                                                     GdkDragContext           *context,
+                                                                     gint                      x,
+                                                                     gint                      y,
+                                                                     guint                     time,
+                                                                     ThunarFile              **file_return);
 static GList        *thunar_standard_view_get_selected_files        (ThunarStandardView       *standard_view);
 static GList        *thunar_standard_view_get_selected_uris         (ThunarStandardView       *standard_view);
 static void          thunar_standard_view_action_properties         (GtkAction                *action,
@@ -109,20 +118,55 @@ static void          thunar_standard_view_action_rename             (GtkAction
                                                                      ThunarStandardView       *standard_view);
 static void          thunar_standard_view_action_show_hidden_files  (GtkToggleAction          *toggle_action,
                                                                      ThunarStandardView       *standard_view);
-static void          thunar_standard_view_drag_begin                (GtkWidget                *widget,
+static gboolean      thunar_standard_view_button_release_event      (GtkWidget                *view,
+                                                                     GdkEventButton           *event,
+                                                                     ThunarStandardView       *standard_view);
+static gboolean      thunar_standard_view_motion_notify_event       (GtkWidget                *view,
+                                                                     GdkEventMotion           *event,
+                                                                     ThunarStandardView       *standard_view);
+static gboolean      thunar_standard_view_drag_drop                 (GtkWidget                *view,
+                                                                     GdkDragContext           *context,
+                                                                     gint                      x,
+                                                                     gint                      y,
+                                                                     guint                     time,
+                                                                     ThunarStandardView       *standard_view);
+static void          thunar_standard_view_drag_data_received        (GtkWidget                *view,
+                                                                     GdkDragContext           *context,
+                                                                     gint                      x,
+                                                                     gint                      y,
+                                                                     GtkSelectionData         *selection_data,
+                                                                     guint                     info,
+                                                                     guint                     time,
+                                                                     ThunarStandardView       *standard_view);
+static void          thunar_standard_view_drag_leave                (GtkWidget                *view,
+                                                                     GdkDragContext           *context,
+                                                                     guint                     time,
+                                                                     ThunarStandardView       *standard_view);
+static gboolean      thunar_standard_view_drag_motion               (GtkWidget                *view,
+                                                                     GdkDragContext           *context,
+                                                                     gint                      x,
+                                                                     gint                      y,
+                                                                     guint                     time,
+                                                                     ThunarStandardView       *standard_view);
+static void          thunar_standard_view_drag_begin                (GtkWidget                *view,
                                                                      GdkDragContext           *context,
                                                                      ThunarStandardView       *standard_view);
-static void          thunar_standard_view_drag_data_get             (GtkWidget                *widget,
+static void          thunar_standard_view_drag_data_get             (GtkWidget                *view,
                                                                      GdkDragContext           *context,
                                                                      GtkSelectionData         *selection_data,
                                                                      guint                     info,
                                                                      guint                     time,
                                                                      ThunarStandardView       *standard_view);
+static void          thunar_standard_view_drag_end                  (GtkWidget                *view,
+                                                                     GdkDragContext           *context,
+                                                                     ThunarStandardView       *standard_view);
 static void          thunar_standard_view_renamed                   (ThunarTextRenderer       *text_renderer,
                                                                      const gchar              *path_string,
                                                                      const gchar              *text,
                                                                      ThunarStandardView       *standard_view);
 static void          thunar_standard_view_loading_unbound           (gpointer                  user_data);
+static gboolean      thunar_standard_view_drag_timer                (gpointer                  user_data);
+static void          thunar_standard_view_drag_timer_destroy        (gpointer                  user_data);
 
 
 
@@ -138,6 +182,18 @@ struct _ThunarStandardViewPrivate
   GtkAction      *action_select_by_pattern;
   GtkAction      *action_rename;
   GtkAction      *action_show_hidden_files;
+
+  /* right-click drag/popup support */
+  GList          *drag_uri_list;
+  gint            drag_timer_id;
+  gint            drag_x;
+  gint            drag_y;
+
+  /* drop site support */
+  guint           drop_data_ready : 1; /* whether the drop data was received already */
+  guint           drop_highlight : 1;
+  guint           drop_occurred : 1;   /* whether the data was dropped */
+  GList          *drop_uri_list;       /* the list of URIs that are contained in the drop data */
 };
 
 
@@ -167,6 +223,14 @@ static const GtkTargetEntry drag_targets[] =
   { "text/uri-list", 0, TEXT_URI_LIST, },
 };
 
+/* Target types for dropping to the view */
+static const GtkTargetEntry drop_targets[] =
+{
+  { "text/uri-list", 0, TEXT_URI_LIST, },
+};
+
+
+
 static GObjectClass *thunar_standard_view_parent_class;
 
 
@@ -240,6 +304,7 @@ thunar_standard_view_class_init (ThunarStandardViewClass *klass)
   gtkwidget_class->realize = thunar_standard_view_realize;
   gtkwidget_class->unrealize = thunar_standard_view_unrealize;
   gtkwidget_class->grab_focus = thunar_standard_view_grab_focus;
+  gtkwidget_class->expose_event = thunar_standard_view_expose_event;
 
   klass->connect_ui_manager = (gpointer) exo_noop;
   klass->disconnect_ui_manager = (gpointer) exo_noop;
@@ -291,6 +356,7 @@ static void
 thunar_standard_view_init (ThunarStandardView *standard_view)
 {
   standard_view->priv = THUNAR_STANDARD_VIEW_GET_PRIVATE (standard_view);
+  standard_view->priv->drag_timer_id = -1;
 
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (standard_view),
                                   GTK_POLICY_AUTOMATIC,
@@ -354,7 +420,7 @@ thunar_standard_view_constructor (GType                  type,
                                   guint                  n_construct_properties,
                                   GObjectConstructParam *construct_properties)
 {
-  GtkWidget *widget;
+  GtkWidget *view;
   GObject   *object;
 
   /* let the GObject constructor create the instance */
@@ -362,23 +428,28 @@ thunar_standard_view_constructor (GType                  type,
                                                                             n_construct_properties,
                                                                             construct_properties);
 
+  /* determine the real view widget (treeview or iconview) */
+  view = GTK_BIN (object)->child;
+
   /* apply our list model to the real view (the child of the scrolled window),
    * we therefore assume that all real views have the "model" property.
    */
-  g_object_set (G_OBJECT (GTK_BIN (object)->child),
-                "model", THUNAR_STANDARD_VIEW (object)->model,
-                NULL);
+  g_object_set (G_OBJECT (view), "model", THUNAR_STANDARD_VIEW (object)->model, NULL);
 
-  /* determine the real view widget (treeview or iconview) */
-  widget = GTK_BIN (object)->child;
+  /* setup the real view as drop site */
+  gtk_drag_dest_set (view, 0, drop_targets, G_N_ELEMENTS (drop_targets), GDK_ACTION_ASK | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE);
+  g_signal_connect (G_OBJECT (view), "drag-drop", G_CALLBACK (thunar_standard_view_drag_drop), object);
+  g_signal_connect (G_OBJECT (view), "drag-data-received", G_CALLBACK (thunar_standard_view_drag_data_received), object);
+  g_signal_connect (G_OBJECT (view), "drag-leave", G_CALLBACK (thunar_standard_view_drag_leave), object);
+  g_signal_connect (G_OBJECT (view), "drag-motion", G_CALLBACK (thunar_standard_view_drag_motion), object);
 
   /* setup the real view as drag source */
-  gtk_drag_source_set (widget, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
-                       drag_targets, G_N_ELEMENTS (drag_targets),
-                       GDK_ACTION_COPY | GDK_ACTION_MOVE
-                       | GDK_ACTION_LINK | GDK_ACTION_ASK);
-  g_signal_connect (G_OBJECT (widget), "drag-begin", G_CALLBACK (thunar_standard_view_drag_begin), object);
-  g_signal_connect (G_OBJECT (widget), "drag-data-get", G_CALLBACK (thunar_standard_view_drag_data_get), object);
+  gtk_drag_source_set (view, GDK_BUTTON1_MASK, drag_targets,
+                       G_N_ELEMENTS (drag_targets), GDK_ACTION_COPY
+                       | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+  g_signal_connect (G_OBJECT (view), "drag-begin", G_CALLBACK (thunar_standard_view_drag_begin), object);
+  g_signal_connect (G_OBJECT (view), "drag-data-get", G_CALLBACK (thunar_standard_view_drag_data_get), object);
+  g_signal_connect (G_OBJECT (view), "drag-end", G_CALLBACK (thunar_standard_view_drag_end), object);
 
   /* done, we have a working object */
   return object;
@@ -395,13 +466,17 @@ thunar_standard_view_dispose (GObject *object)
   if (G_UNLIKELY (standard_view->loading_binding != NULL))
     exo_binding_unbind (standard_view->loading_binding);
 
+  /* be sure to cancel any pending drag timer */
+  if (G_UNLIKELY (standard_view->priv->drag_timer_id >= 0))
+    g_source_remove (standard_view->priv->drag_timer_id);
+
   /* reset the UI manager property */
   thunar_view_set_ui_manager (THUNAR_VIEW (standard_view), NULL);
 
   /* disconnect the widget from the launcher support */
   thunar_launcher_set_widget (standard_view->priv->launcher, NULL);
 
-  G_OBJECT_CLASS (thunar_standard_view_parent_class)->dispose (object);
+  (*G_OBJECT_CLASS (thunar_standard_view_parent_class)->dispose) (object);
 }
 
 
@@ -417,6 +492,12 @@ thunar_standard_view_finalize (GObject *object)
   g_assert (standard_view->ui_manager == NULL);
   g_assert (standard_view->clipboard == NULL);
 
+  /* release the drag URI list (just in case the drag-end wasn't fired before) */
+  thunar_vfs_uri_list_free (standard_view->priv->drag_uri_list);
+
+  /* release the drop URI list (just in case the drag-leave wasn't fired before) */
+  thunar_vfs_uri_list_free (standard_view->priv->drop_uri_list);
+
   /* release the reference on the name renderer */
   g_object_unref (G_OBJECT (standard_view->name_renderer));
 
@@ -436,7 +517,7 @@ thunar_standard_view_finalize (GObject *object)
   /* free the statusbar text (if any) */
   g_free (standard_view->statusbar_text);
 
-  G_OBJECT_CLASS (thunar_standard_view_parent_class)->finalize (object);
+  (*G_OBJECT_CLASS (thunar_standard_view_parent_class)->finalize) (object);
 }
 
 
@@ -567,6 +648,51 @@ thunar_standard_view_grab_focus (GtkWidget *widget)
 
 
 
+static gboolean
+thunar_standard_view_expose_event (GtkWidget      *widget,
+                                   GdkEventExpose *event)
+{
+  gboolean result = FALSE;
+#if GTK_CHECK_VERSION(2,7,2)
+  cairo_t *cr;
+#endif
+  gint     x, y, width, height;
+
+  /* let the scrolled window do it's work */
+  result = (*GTK_WIDGET_CLASS (thunar_standard_view_parent_class)->expose_event) (widget, event);
+
+  /* render the folder drop shadow */
+  if (G_UNLIKELY (THUNAR_STANDARD_VIEW (widget)->priv->drop_highlight))
+    {
+      x = widget->allocation.x;
+      y = widget->allocation.y;
+      width = widget->allocation.width;
+      height = widget->allocation.height;
+
+      gtk_paint_shadow (widget->style, widget->window,
+                        GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+                        NULL, widget, "dnd",
+                        x, y, width, height);
+
+#if GTK_CHECK_VERSION(2,7,2)
+      /* the cairo version looks better here, so we use it if possible */
+      cr = gdk_cairo_create (widget->window);
+      cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+      cairo_set_line_width (cr, 1.0);
+      cairo_rectangle (cr, x + 0.5, y + 0.5, width - 1, height - 1);
+      cairo_stroke (cr);
+      cairo_destroy (cr);
+#else
+      gdk_draw_rectangle (widget->window, widget->style->black_gc,
+                          FALSE, x, y, width - 1, height - 1);
+#endif
+    }
+
+  return result;
+}
+
+
+
 static ThunarFile*
 thunar_standard_view_get_current_directory (ThunarNavigator *navigator)
 {
@@ -763,6 +889,94 @@ thunar_standard_view_set_ui_manager (ThunarView   *view,
 
 
 
+static GdkDragAction
+thunar_standard_view_get_dest_actions (ThunarStandardView *standard_view,
+                                       GdkDragContext     *context,
+                                       gint                x,
+                                       gint                y,
+                                       guint               time,
+                                       ThunarFile        **file_return)
+{
+  GdkDragAction actions = 0;
+  GdkDragAction action = 0;
+  GtkTreePath  *path;
+  GtkTreeIter   iter;
+  ThunarFile   *file;
+
+  /* determine the path for the given coordinates */
+  path = (*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->get_path_at_pos) (standard_view, x, y);
+  if (G_LIKELY (path != NULL))
+    {
+      /* determine the file for the path */
+      gtk_tree_model_get_iter (GTK_TREE_MODEL (standard_view->model), &iter, path);
+      file = thunar_list_model_get_file (standard_view->model, &iter);
+    }
+  else
+    {
+      /* determine the current directory */
+      file = thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (standard_view));
+      if (G_LIKELY (file != NULL))
+        g_object_ref (G_OBJECT (file));
+    }
+
+  /* check if we can drop there */
+  if (G_LIKELY (file != NULL))
+    {
+      actions = thunar_file_accepts_uri_drop (file, standard_view->priv->drop_uri_list, context->actions);
+      if (G_LIKELY (actions != 0))
+        {
+          /* determine a working action */
+          if (G_LIKELY ((context->suggested_action & actions) != 0))
+            action = context->suggested_action;
+          else if ((actions & GDK_ACTION_ASK) != 0)
+            action = GDK_ACTION_ASK;
+          else if ((actions & GDK_ACTION_COPY) != 0)
+            action = GDK_ACTION_COPY;
+          else if ((actions & GDK_ACTION_LINK) != 0)
+            action = GDK_ACTION_LINK;
+          else if ((actions & GDK_ACTION_MOVE) != 0)
+            action = GDK_ACTION_MOVE;
+          else
+            action = GDK_ACTION_PRIVATE;
+
+          /* tell the caller about the file (if it's interested) */
+          if (G_UNLIKELY (file_return != NULL))
+            *file_return = g_object_ref (G_OBJECT (file));
+        }
+
+      /* release the file reference */
+      g_object_unref (G_OBJECT (file));
+    }
+
+  /* reset path if we cannot drop */
+  if (G_UNLIKELY (action == 0 && path != NULL))
+    {
+      gtk_tree_path_free (path);
+      path = NULL;
+    }
+
+  /* do the view highlighting */
+  if (standard_view->priv->drop_highlight != (path == NULL && action != 0))
+    {
+      standard_view->priv->drop_highlight = (path == NULL && action != 0);
+      gtk_widget_queue_draw (GTK_WIDGET (standard_view));
+    }
+
+  /* do the item highlighting */
+  (*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->highlight_path) (standard_view, path);
+
+  /* tell Gdk whether we can drop here */
+  gdk_drag_status (context, action, time);
+
+  /* clean up */
+  if (G_LIKELY (path != NULL))
+    gtk_tree_path_free (path);
+
+  return actions;
+}
+
+
+
 static GList*
 thunar_standard_view_get_selected_files (ThunarStandardView *standard_view)
 {
@@ -1034,49 +1248,249 @@ thunar_standard_view_action_show_hidden_files (GtkToggleAction    *toggle_action
 
 
 
+static gboolean
+thunar_standard_view_button_release_event (GtkWidget          *view,
+                                           GdkEventButton     *event,
+                                           ThunarStandardView *standard_view)
+{
+  g_return_val_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view), FALSE);
+  g_return_val_if_fail (standard_view->priv->drag_timer_id >= 0, FALSE);
+
+  /* cancel the pending drag timer */
+  g_source_remove (standard_view->priv->drag_timer_id);
+
+  /* fire up the context menu */
+  thunar_standard_view_context_menu (standard_view, event->button, event->time);
+
+  return TRUE;
+}
+
+
+
+static gboolean
+thunar_standard_view_motion_notify_event (GtkWidget          *view,
+                                          GdkEventMotion     *event,
+                                          ThunarStandardView *standard_view)
+{
+  GdkDragContext *context;
+  GtkTargetList  *target_list;
+
+  g_return_val_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view), FALSE);
+  g_return_val_if_fail (standard_view->priv->drag_timer_id >= 0, FALSE);
+
+  /* check if we passed the DnD threshold */
+  if (gtk_drag_check_threshold (view, standard_view->priv->drag_x, standard_view->priv->drag_y, event->x, event->y))
+    {
+      /* cancel the drag timer, as we won't popup the menu anymore */
+      g_source_remove (standard_view->priv->drag_timer_id);
+
+      /* allocate the drag context (preferred action is to ask the user) */
+      target_list = gtk_target_list_new (drag_targets, G_N_ELEMENTS (drag_targets));
+      context = gtk_drag_begin (view, target_list, GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK, 3, (GdkEvent *) event);
+      context->suggested_action = GDK_ACTION_ASK;
+      gtk_target_list_unref (target_list);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+
+static gboolean
+thunar_standard_view_drag_drop (GtkWidget          *view,
+                                GdkDragContext     *context,
+                                gint                x,
+                                gint                y,
+                                guint               time,
+                                ThunarStandardView *standard_view)
+{
+  GdkAtom target;
+
+  target = gtk_drag_dest_find_target (view, context, NULL);
+  if (G_LIKELY (target != GDK_NONE))
+    {
+      /* set state so the drag-data-received knows that
+       * this is really a drop this time.
+       */
+      standard_view->priv->drop_occurred = TRUE;
+
+      /* request the drag data from the source */
+      gtk_drag_get_data (view, context, target, time);
+  
+      /* we'll call gtk_drag_finish() later */
+      return TRUE;
+    }
+  else
+    {
+      /* we cannot handle the drag data */
+      return FALSE;
+    }
+}
+
+
+
 static void
-thunar_standard_view_drag_begin (GtkWidget          *widget,
+thunar_standard_view_drag_data_received (GtkWidget          *view,
+                                         GdkDragContext     *context,
+                                         gint                x,
+                                         gint                y,
+                                         GtkSelectionData   *selection_data,
+                                         guint               info,
+                                         guint               time,
+                                         ThunarStandardView *standard_view)
+{
+  GdkDragAction actions;
+  GdkDragAction action;
+  ThunarFile   *file = NULL;
+  gboolean      succeed = FALSE;
+
+  /* check if we don't already know the drop data */
+  if (G_LIKELY (!standard_view->priv->drop_data_ready))
+    {
+      /* extract the URI list from the selection data (if valid) */
+      if (info == TEXT_URI_LIST && selection_data->format == 8 && selection_data->length > 0)
+        standard_view->priv->drop_uri_list = thunar_vfs_uri_list_from_string ((gchar *) selection_data->data, NULL);
+
+      /* reset the state */
+      standard_view->priv->drop_data_ready = TRUE;
+    }
+
+  /* check if the data was dropped */
+  if (G_UNLIKELY (standard_view->priv->drop_occurred))
+    {
+      /* reset the state */
+      standard_view->priv->drop_occurred = FALSE;
+
+      /* determine the drop position */
+      actions = thunar_standard_view_get_dest_actions (standard_view, context, x, y, time, &file);
+      if (G_LIKELY ((actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)) != 0))
+        {
+          /* ask the user what to do with the drop data */
+          action = (context->action == GDK_ACTION_ASK) ? thunar_dnd_ask (GTK_WIDGET (standard_view), time, actions) : context->action;
+
+          /* perform the requested action */
+          if (G_LIKELY (action != 0))
+            succeed = thunar_dnd_perform (GTK_WIDGET (standard_view), file, standard_view->priv->drop_uri_list, action);
+        }
+
+      /* release the file reference */
+      if (G_LIKELY (file != NULL))
+        g_object_unref (G_OBJECT (file));
+
+      /* disable the highlighting and release the drag data */
+      thunar_standard_view_drag_leave (view, context, time, standard_view);
+
+      /* tell the peer that we handled the drop */
+      gtk_drag_finish (context, succeed, FALSE, time);
+    }
+}
+
+
+
+static void
+thunar_standard_view_drag_leave (GtkWidget          *widget,
+                                 GdkDragContext     *context,
+                                 guint               time,
+                                 ThunarStandardView *standard_view)
+{
+  /* disable the drop highlighting around the view */
+  if (G_LIKELY (standard_view->priv->drop_highlight))
+    {
+      standard_view->priv->drop_highlight = FALSE;
+      gtk_widget_queue_draw (GTK_WIDGET (standard_view));
+    }
+
+  /* reset the "drop data ready" status and free the URI list */
+  if (G_LIKELY (standard_view->priv->drop_data_ready))
+    {
+      thunar_vfs_uri_list_free (standard_view->priv->drop_uri_list);
+      standard_view->priv->drop_uri_list = NULL;
+      standard_view->priv->drop_data_ready = FALSE;
+    }
+
+  /* disable the highlighting of the items in the view */
+  (*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->highlight_path) (standard_view, NULL);
+}
+
+
+
+static gboolean
+thunar_standard_view_drag_motion (GtkWidget          *view,
+                                  GdkDragContext     *context,
+                                  gint                x,
+                                  gint                y,
+                                  guint               time,
+                                  ThunarStandardView *standard_view)
+{
+  GdkAtom target;
+
+  /* request the drop data on-demand (if we don't have it already) */
+  if (G_UNLIKELY (!standard_view->priv->drop_data_ready))
+    {
+      /* check if we can handle that drag data (yet?) */
+      target = gtk_drag_dest_find_target (view, context, NULL);
+      if (G_UNLIKELY (target == GDK_NONE))
+        {
+          /* we cannot handle the drag data */
+          gdk_drag_status (context, 0, time);
+        }
+      else
+        {
+          /* request the drag data from the source */
+          gtk_drag_get_data (view, context, target, time);
+
+          /* and deny the drop so far */
+          gdk_drag_status (context, 0, time);
+        }
+    }
+  else
+    {
+      /* check whether we can drop at (x,y) */
+      thunar_standard_view_get_dest_actions (standard_view, context, x, y, time, NULL);
+    }
+
+  return TRUE;
+}
+
+
+
+static void
+thunar_standard_view_drag_begin (GtkWidget          *view,
                                  GdkDragContext     *context,
                                  ThunarStandardView *standard_view)
 {
-  GtkTreeIter iter;
   ThunarFile *file;
   GdkPixbuf  *icon;
-  GList      *selected_items;
   gint        size;
 
-  /* query the list of selected items */
-  selected_items = (*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->get_selected_items) (standard_view);
-  if (G_LIKELY (selected_items != NULL))
+  g_return_if_fail (standard_view->priv->drag_uri_list == NULL);
+
+  /* query the list of selected URIs */
+  standard_view->priv->drag_uri_list = thunar_standard_view_get_selected_uris (standard_view);
+  if (G_LIKELY (standard_view->priv->drag_uri_list != NULL))
     {
-      /* grab the tree iterator for the first selected item */
-      if (gtk_tree_model_get_iter (GTK_TREE_MODEL (standard_view->model), &iter, selected_items->data))
+      /* determine the first selected file */
+      file = thunar_file_get_for_uri (standard_view->priv->drag_uri_list->data, NULL);
+      if (G_LIKELY (file != NULL))
         {
-          /* determine the first selected file */
-          file = thunar_list_model_get_file (THUNAR_LIST_MODEL (standard_view->model), &iter);
-          if (G_LIKELY (file != NULL))
-            {
-              /* generate an icon based on that file */
-              g_object_get (G_OBJECT (standard_view->icon_renderer), "size", &size, NULL);
-              icon = thunar_file_load_icon (file, standard_view->icon_factory, size);
-              gtk_drag_set_icon_pixbuf (context, icon, 0, 0);
-              g_object_unref (G_OBJECT (icon));
-
-              /* release the file */
-              g_object_unref (G_OBJECT (file));
-            }
+          /* generate an icon based on that file */
+          g_object_get (G_OBJECT (standard_view->icon_renderer), "size", &size, NULL);
+          icon = thunar_file_load_icon (file, standard_view->icon_factory, size);
+          gtk_drag_set_icon_pixbuf (context, icon, 0, 0);
+          g_object_unref (G_OBJECT (icon));
+
+          /* release the file */
+          g_object_unref (G_OBJECT (file));
         }
-
-      /* release the selected items */
-      g_list_foreach (selected_items, (GFunc) gtk_tree_path_free, NULL);
-      g_list_free (selected_items);
     }
 }
 
 
 
 static void
-thunar_standard_view_drag_data_get (GtkWidget          *widget,
+thunar_standard_view_drag_data_get (GtkWidget          *view,
                                     GdkDragContext     *context,
                                     GtkSelectionData   *selection_data,
                                     guint               info,
@@ -1084,23 +1498,27 @@ thunar_standard_view_drag_data_get (GtkWidget          *widget,
                                     ThunarStandardView *standard_view)
 {
   gchar *uri_string;
-  GList *uri_list;
-
-  /* transform the list of selected URIs to a string */
-  uri_list = thunar_standard_view_get_selected_uris (standard_view);
-  uri_string = thunar_vfs_uri_list_to_string (uri_list, 0);
-  thunar_vfs_uri_list_free (uri_list);
 
   /* set the URI list for the drag selection */
-  gtk_selection_data_set (selection_data, selection_data->target, 8,
-                          (guchar *) uri_string, strlen (uri_string));
-
-  /* clean up */
+  uri_string = thunar_vfs_uri_list_to_string (standard_view->priv->drag_uri_list, 0);
+  gtk_selection_data_set (selection_data, selection_data->target, 8, (guchar *) uri_string, strlen (uri_string));
   g_free (uri_string);
 }
 
 
 
+static void
+thunar_standard_view_drag_end (GtkWidget          *view,
+                               GdkDragContext     *context,
+                               ThunarStandardView *standard_view)
+{
+  /* release the list of dragged URIs */
+  thunar_vfs_uri_list_free (standard_view->priv->drag_uri_list);
+  standard_view->priv->drag_uri_list = NULL;
+}
+
+
+
 static void
 thunar_standard_view_renamed (ThunarTextRenderer *text_renderer,
                               const gchar        *path_string,
@@ -1202,6 +1620,34 @@ thunar_standard_view_loading_unbound (gpointer user_data)
 
 
 
+static gboolean
+thunar_standard_view_drag_timer (gpointer user_data)
+{
+  ThunarStandardView *standard_view = THUNAR_STANDARD_VIEW (user_data);
+
+  /* fire up the context menu */
+  GDK_THREADS_ENTER ();
+  thunar_standard_view_context_menu (standard_view, 3, gtk_get_current_event_time ());
+  GDK_THREADS_LEAVE ();
+
+  return FALSE;
+}
+
+
+
+static void
+thunar_standard_view_drag_timer_destroy (gpointer user_data)
+{
+  /* unregister the motion notify and button release event handlers (thread-safe) */
+  g_signal_handlers_disconnect_by_func (GTK_BIN (user_data)->child, thunar_standard_view_button_release_event, user_data);
+  g_signal_handlers_disconnect_by_func (GTK_BIN (user_data)->child, thunar_standard_view_motion_notify_event, user_data);
+
+  /* reset the drag timer source id */
+  THUNAR_STANDARD_VIEW (user_data)->priv->drag_timer_id = -1;
+}
+
+
+
 /**
  * thunar_standard_view_context_menu:
  * @standard_view : a #ThunarStandardView instance.
@@ -1268,6 +1714,59 @@ thunar_standard_view_context_menu (ThunarStandardView *standard_view,
 
 
 
+/**
+ * thunar_standard_view_queue_popup:
+ * @standard_view : a #ThunarStandardView.
+ * @event         : the right click event.
+ *
+ * Schedules a context menu popup in response to
+ * a right-click button event. Right-click events
+ * need to be handled in a special way, as the
+ * user may also start a drag using the right
+ * mouse button and therefore this function
+ * schedules a timer, which - once expired -
+ * opens the context menu. If the user moves
+ * the mouse prior to expiration, a right-click
+ * drag (with #GDK_ACTION_ASK) will be started
+ * instead.
+ **/
+void
+thunar_standard_view_queue_popup (ThunarStandardView *standard_view,
+                                  GdkEventButton     *event)
+{
+  GtkSettings *settings;
+  GtkWidget   *view;
+  gint         delay;
+
+  g_return_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view));
+  g_return_if_fail (event != NULL);
+
+  /* check if we have already scheduled a drag timer */
+  if (G_LIKELY (standard_view->priv->drag_timer_id < 0))
+    {
+      /* remember the new coordinates */
+      standard_view->priv->drag_x = event->x;
+      standard_view->priv->drag_y = event->y;
+
+      /* figure out the real view */
+      view = GTK_BIN (standard_view)->child;
+
+      /* we use the menu popup delay here, which should give us good values */
+      settings = gtk_settings_get_for_screen (gtk_widget_get_screen (view));
+      g_object_get (G_OBJECT (settings), "gtk-menu-popup-delay", &delay, NULL);
+
+      /* schedule the timer */
+      standard_view->priv->drag_timer_id = g_timeout_add_full (G_PRIORITY_LOW, delay, thunar_standard_view_drag_timer,
+                                                               standard_view, thunar_standard_view_drag_timer_destroy);
+
+      /* register the motion notify and the button release events on the real view */
+      g_signal_connect (G_OBJECT (view), "button-release-event", G_CALLBACK (thunar_standard_view_button_release_event), standard_view);
+      g_signal_connect (G_OBJECT (view), "motion-notify-event", G_CALLBACK (thunar_standard_view_motion_notify_event), standard_view);
+    }
+}
+
+
+
 /**
  * thunar_standard_view_selection_changed:
  * @standard_view : a #ThunarStandardView instance.
diff --git a/thunar/thunar-standard-view.h b/thunar/thunar-standard-view.h
index 8fa549f4c..cdb3efaee 100644
--- a/thunar/thunar-standard-view.h
+++ b/thunar/thunar-standard-view.h
@@ -44,39 +44,52 @@ struct _ThunarStandardViewClass
   /* Called by the ThunarStandardView class to let derived classes
    * connect to and disconnect from the UI manager.
    */
-  void   (*connect_ui_manager)    (ThunarStandardView *standard_view,
-                                   GtkUIManager       *ui_manager);
-  void   (*disconnect_ui_manager) (ThunarStandardView *standard_view,
-                                   GtkUIManager       *ui_manager);
+  void       (*connect_ui_manager)    (ThunarStandardView *standard_view,
+                                       GtkUIManager       *ui_manager);
+  void       (*disconnect_ui_manager) (ThunarStandardView *standard_view,
+                                       GtkUIManager       *ui_manager);
 
   /* Returns the list of currently selected GtkTreePath's, where
    * both the list and the items are owned by the caller. */
-  GList *(*get_selected_items)    (ThunarStandardView *standard_view);
+  GList       *(*get_selected_items)    (ThunarStandardView *standard_view);
 
   /* Selects all items in the view */
-  void   (*select_all)            (ThunarStandardView *standard_view);
+  void         (*select_all)            (ThunarStandardView *standard_view);
 
   /* Unselects all items in the view */
-  void   (*unselect_all)          (ThunarStandardView *standard_view);
+  void         (*unselect_all)          (ThunarStandardView *standard_view);
 
   /* Selects the given item */
-  void   (*select_path)           (ThunarStandardView *standard_view,
-                                   GtkTreePath        *path);
+  void         (*select_path)           (ThunarStandardView *standard_view,
+                                         GtkTreePath        *path);
 
   /* Called by the ThunarStandardView class to let derived class
    * place the cursor on the item/row referred to by path. If
    * start_editing is TRUE, the derived class should also start
    * editing that item/row.
    */
-  void   (*set_cursor)            (ThunarStandardView *standard_view,
-                                   GtkTreePath        *path,
-                                   gboolean            start_editing);
+  void         (*set_cursor)            (ThunarStandardView *standard_view,
+                                         GtkTreePath        *path,
+                                         gboolean            start_editing);
 
   /* Called by the ThunarStandardView class to let derived class
    * scroll the view to the given path.
    */
-  void   (*scroll_to_path)        (ThunarStandardView *standard_view,
-                                   GtkTreePath        *path);
+  void         (*scroll_to_path)        (ThunarStandardView *standard_view,
+                                         GtkTreePath        *path);
+
+  /* Returns the path at the given position or NULL if no item/row
+   * is located at that coordinates. The path is freed by the caller.
+   */
+  GtkTreePath *(*get_path_at_pos)       (ThunarStandardView *standard_view,
+                                         gint                x,
+                                         gint                y);
+
+  /* Sets the item/row that is highlighted for feedback. NULL is
+   * passed for path to disable the highlighting.
+   */
+  void         (*highlight_path)       (ThunarStandardView  *standard_view,
+                                        GtkTreePath         *path);
 };
 
 struct _ThunarStandardView
@@ -106,6 +119,10 @@ GType thunar_standard_view_get_type           (void) G_GNUC_CONST;
 void  thunar_standard_view_context_menu       (ThunarStandardView *standard_view,
                                                guint               button,
                                                guint32             time);
+
+void  thunar_standard_view_queue_popup        (ThunarStandardView *standard_view,
+                                               GdkEventButton     *event);
+
 void  thunar_standard_view_selection_changed  (ThunarStandardView *standard_view);
 
 G_END_DECLS;
-- 
GitLab