From c1f55cdca5b9c83483eb4f299f16e78c02b18a44 Mon Sep 17 00:00:00 2001
From: Benedikt Meurer <benny@xfce.org>
Date: Thu, 9 Jun 2005 22:42:58 +0000
Subject: [PATCH] 2005-06-10	Benedikt Meurer <benny@xfce.org>

	* thunar/thunar-favourites-model.{c,h},
	  thunar/thunar-favourites-view.c: Add initial drag&drop-support for
	  the favourites view. This is currently limited to rearranging
	  favourites in the list. Support for adding new favourites by
	  dropping text/uri-list's will be added soon. Also, the
	  ThunarFavouritesModel also saves changes made by the user back to
	  the .gtk-bookmarks file now.
	* TODO: Add note about missing text/uri-list support in
	  ThunarFavouritesView, and missing .gtk-bookmarks monitor support.




(Old svn revision: 16327)
---
 ChangeLog                        |  12 ++
 TODO                             |   6 +
 thunar/thunar-favourites-model.c | 299 ++++++++++++++++++++++++++++---
 thunar/thunar-favourites-model.h |   8 +-
 thunar/thunar-favourites-view.c  | 225 ++++++++++++++++++++++-
 5 files changed, 515 insertions(+), 35 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 124bf60e8..2eefacfd6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2005-06-10	Benedikt Meurer <benny@xfce.org>
+
+	* thunar/thunar-favourites-model.{c,h},
+	  thunar/thunar-favourites-view.c: Add initial drag&drop-support for
+	  the favourites view. This is currently limited to rearranging
+	  favourites in the list. Support for adding new favourites by
+	  dropping text/uri-list's will be added soon. Also, the
+	  ThunarFavouritesModel also saves changes made by the user back to
+	  the .gtk-bookmarks file now.
+	* TODO: Add note about missing text/uri-list support in
+	  ThunarFavouritesView, and missing .gtk-bookmarks monitor support.
+
 2005-06-08	Benedikt Meurer <benny@xfce.org>
 
 	* thunar/thunar-favourites-model.c: Use a signed gint for the number
diff --git a/TODO b/TODO
index 59e837587..0e961404b 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,12 @@
 Important for Thunar 1.0
 ========================
 
+ - Handle text/uri-list drops on the ThunarFavouritesView.
+
+ - ThunarFavouritesModel should watch the .gtk-bookmarks file for changes and
+   reload it on-demand (take care of not reloading if the change was caused by
+   thunar_favourites_model_save!).
+
  - The layouting code for ThunarLocationButtons is still buggy. Problem shows
    with paths that include a very long directory; you cannot scroll to the
    last path component then.
diff --git a/thunar/thunar-favourites-model.c b/thunar/thunar-favourites-model.c
index e3d2ee576..01e8d7f7c 100644
--- a/thunar/thunar-favourites-model.c
+++ b/thunar/thunar-favourites-model.c
@@ -21,7 +21,16 @@
 #include <config.h>
 #endif
 
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
 #include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 
 #include <thunar/thunar-favourites-model.h>
 #include <thunar/thunar-file.h>
@@ -35,7 +44,7 @@ typedef struct _ThunarFavourite ThunarFavourite;
 static void               thunar_favourites_model_class_init          (ThunarFavouritesModelClass *klass);
 static void               thunar_favourites_model_init                (ThunarFavouritesModel      *model);
 static void               thunar_favourites_model_tree_model_init     (GtkTreeModelIface          *iface);
-static void               thunar_favourites_model_drag_dest_init      (GtkTreeDragDestIface       *iface);
+static void               thunar_favourites_model_drag_source_init    (GtkTreeDragSourceIface     *iface);
 static void               thunar_favourites_model_finalize            (GObject                    *object);
 static GtkTreeModelFlags  thunar_favourites_model_get_flags           (GtkTreeModel               *tree_model);
 static gint               thunar_favourites_model_get_n_columns       (GtkTreeModel               *tree_model);
@@ -66,12 +75,14 @@ static gboolean           thunar_favourites_model_iter_nth_child      (GtkTreeMo
 static gboolean           thunar_favourites_model_iter_parent         (GtkTreeModel               *tree_model,
                                                                        GtkTreeIter                *iter,
                                                                        GtkTreeIter                *child);
-static gboolean           thunar_favourites_model_drag_data_received  (GtkTreeDragDest            *dest,
-                                                                       GtkTreePath                *path,
-                                                                       GtkSelectionData           *data);
-static gboolean           thunar_favourites_model_row_drop_possible   (GtkTreeDragDest            *dest,
+static gboolean           thunar_favourites_model_row_draggable       (GtkTreeDragSource          *source,
+                                                                       GtkTreePath                *path);
+static gboolean           thunar_favourites_model_drag_data_get       (GtkTreeDragSource          *source,
                                                                        GtkTreePath                *path,
-                                                                       GtkSelectionData           *data);
+                                                                       GtkSelectionData           *selection_data);
+static gboolean           thunar_favourites_model_drag_data_delete    (GtkTreeDragSource          *source,
+                                                                       GtkTreePath                *path);
+static void               thunar_favourites_model_save                (ThunarFavouritesModel      *model);
 static void               thunar_favourites_model_file_changed        (ThunarFile                 *file,
                                                                        ThunarFavouritesModel      *model);
 static void               thunar_favourites_model_file_destroy        (ThunarFile                 *file,
@@ -96,8 +107,9 @@ struct _ThunarFavouritesModel
 struct _ThunarFavourite
 {
   ThunarFile      *file;
-  const gchar     *special_name;
+  const gchar     *name;
   ThunarFavourite *next;
+  ThunarFavourite *prev;
 };
 
 
@@ -107,8 +119,8 @@ G_DEFINE_TYPE_WITH_CODE (ThunarFavouritesModel,
                          G_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
                                                 thunar_favourites_model_tree_model_init)
-                         G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST,
-                                                thunar_favourites_model_drag_dest_init));
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
+                                                thunar_favourites_model_drag_source_init));
 
 
     
@@ -172,6 +184,7 @@ thunar_favourites_model_init (ThunarFavouritesModel *model)
               for (current = model->favourites;; current = current->next)
                 if (current->next == NULL)
                   break;
+              favourite->prev = current;
               current->next = favourite;
             }
           ++model->n_favourites;
@@ -189,6 +202,8 @@ thunar_favourites_model_init (ThunarFavouritesModel *model)
   /* prepend the row separator (only supported with Gtk+ 2.6) */
 #if GTK_CHECK_VERSION(2,6,0)
   favourite = g_new0 (ThunarFavourite, 1);
+  if (G_LIKELY (model->favourites != NULL))
+    model->favourites->prev = favourite;
   favourite->next = model->favourites;
   model->favourites = favourite;
   ++model->n_favourites;
@@ -201,7 +216,11 @@ thunar_favourites_model_init (ThunarFavouritesModel *model)
     {
       favourite = g_new (ThunarFavourite, 1);
       favourite->file = file;
-      favourite->special_name = _("Filesystem");
+      favourite->name = _("Filesystem");
+
+      /* link the favourites to the list */
+      if (G_LIKELY (model->favourites != NULL))
+        model->favourites->prev = favourite;
       favourite->next = model->favourites;
       model->favourites = favourite;
       ++model->n_favourites;
@@ -220,7 +239,11 @@ thunar_favourites_model_init (ThunarFavouritesModel *model)
     {
       favourite = g_new (ThunarFavourite, 1);
       favourite->file = file;
-      favourite->special_name = _("Home");
+      favourite->name = _("Home");
+
+      /* link the favourites to the list */
+      if (G_LIKELY (model->favourites != NULL))
+        model->favourites->prev = favourite;
       favourite->next = model->favourites;
       model->favourites = favourite;
       ++model->n_favourites;
@@ -255,10 +278,11 @@ thunar_favourites_model_tree_model_init (GtkTreeModelIface *iface)
 
 
 static void
-thunar_favourites_model_drag_dest_init (GtkTreeDragDestIface *iface)
+thunar_favourites_model_drag_source_init (GtkTreeDragSourceIface *iface)
 {
-  iface->drag_data_received = thunar_favourites_model_drag_data_received;
-  iface->row_drop_possible = thunar_favourites_model_row_drop_possible;
+  iface->row_draggable = thunar_favourites_model_row_draggable;
+  iface->drag_data_get = thunar_favourites_model_drag_data_get;
+  iface->drag_data_delete = thunar_favourites_model_drag_data_delete;
 }
 
 
@@ -403,8 +427,8 @@ thunar_favourites_model_get_value (GtkTreeModel *tree_model,
     {
     case THUNAR_FAVOURITES_MODEL_COLUMN_NAME:
       g_value_init (value, G_TYPE_STRING);
-      if (favourite->special_name != NULL)
-        name = favourite->special_name;
+      if (favourite->name != NULL)
+        name = favourite->name;
       else if (favourite->file != NULL)
         name = thunar_file_get_display_name (favourite->file);
       else
@@ -533,27 +557,107 @@ thunar_favourites_model_iter_parent (GtkTreeModel *tree_model,
 
 
 static gboolean
-thunar_favourites_model_drag_data_received (GtkTreeDragDest  *dest,
-                                            GtkTreePath      *path,
-                                            GtkSelectionData *data)
+thunar_favourites_model_row_draggable (GtkTreeDragSource *source,
+                                       GtkTreePath       *path)
 {
-  // TODO: Implement this.
+  ThunarFavouritesModel *model = THUNAR_FAVOURITES_MODEL (source);
+  ThunarFavourite       *favourite;
+  gint                   n;
+
+  g_return_val_if_fail (THUNAR_IS_FAVOURITES_MODEL (model), FALSE);
+  g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
+
+  /* verify that the index of the path is valid */
+  n = gtk_tree_path_get_indices (path)[0];
+  if (G_UNLIKELY (n < 0 || n >= model->n_favourites))
+    return FALSE;
+
+  /* lookup the ThunarFavourite for the path */
+  for (favourite = model->favourites; --n >= 0; favourite = favourite->next)
+    ;
+
+  /* special favourites cannot be reordered */
+  return (favourite->name == NULL);
+}
+
+
+
+static gboolean
+thunar_favourites_model_drag_data_get (GtkTreeDragSource *source,
+                                       GtkTreePath       *path,
+                                       GtkSelectionData  *selection_data)
+{
+  // FIXME
   return FALSE;
 }
 
 
 
 static gboolean
-thunar_favourites_model_row_drop_possible (GtkTreeDragDest  *dest,
-                                           GtkTreePath      *path,
-                                           GtkSelectionData *data)
+thunar_favourites_model_drag_data_delete (GtkTreeDragSource *source,
+                                          GtkTreePath       *path)
 {
-  // TODO: Implement this.
+  // we simply return FALSE here, as this function can only be
+  // called if the user is re-arranging favourites within the
+  // model, which will be handle by the exchange method
   return FALSE;
 }
 
 
 
+static void
+thunar_favourites_model_save (ThunarFavouritesModel *model)
+{
+  ThunarFavourite *favourite;
+  gchar           *dst_path;
+  gchar           *tmp_path;
+  gchar           *uri;
+  FILE            *fp = NULL;
+
+  g_return_if_fail (THUNAR_IS_FAVOURITES_MODEL (model));
+
+  /* open a temporary file for writing */
+  tmp_path = g_strdup_printf ("%s%c.gtk-bookmarks.tmp-%d",
+                              xfce_get_homedir (),
+                              G_DIR_SEPARATOR,
+                              (gint) getpid ());
+  fp = fopen (tmp_path, "w");
+  if (G_UNLIKELY (fp == NULL))
+    {
+      g_warning ("Failed to open `%s' for writing: %s",
+                 tmp_path, g_strerror (errno));
+      g_free (tmp_path);
+      return;
+    }
+
+  /* write the uris of user customizable favourites */
+  for (favourite = model->favourites; favourite != NULL; favourite = favourite->next)
+    if (favourite->name == NULL && favourite->file != NULL)
+      {
+        uri = thunar_vfs_uri_to_string (thunar_file_get_uri (favourite->file));
+        fprintf (fp, "%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) */
+  dst_path = xfce_get_homefile (".gtk-bookmarks", NULL);
+  if (rename (tmp_path, dst_path) < 0)
+    {
+      g_warning ("Failed to write `%s': %s",
+                 dst_path, g_strerror (errno));
+      unlink (tmp_path);
+    }
+
+  /* cleanup */
+  g_free (dst_path);
+  g_free (tmp_path);
+}
+
+
+
 static void
 thunar_favourites_model_file_changed (ThunarFile            *file,
                                       ThunarFavouritesModel *model)
@@ -572,8 +676,7 @@ thunar_favourites_model_file_changed (ThunarFile            *file,
         iter.stamp = model->stamp;
         iter.user_data = favourite;
 
-        path = gtk_tree_path_new ();
-        gtk_tree_path_append_index (path, n);
+        path = gtk_tree_path_new_from_indices (n, -1);
         gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
         gtk_tree_path_free (path);
       }
@@ -678,3 +781,147 @@ thunar_favourites_model_file_for_iter (ThunarFavouritesModel *model,
 }
 
 
+
+/**
+ * thunar_favourites_model_drop_possible:
+ * @model : a #ThunarFavouritestModel.
+ * @path  : a #GtkTreePath.
+ *
+ * Determines whether a drop is possible before the given @path, at the same depth
+ * as @path. I.e., can we drop data at that location. @path does not have to exist;
+ * the return value will almost certainly be FALSE if the parent of @path doesn't
+ * exist, though.
+ *
+ * Return value: %TRUE if it's possible to drop data before @path, else %FALSE.
+ **/
+gboolean
+thunar_favourites_model_drop_possible (ThunarFavouritesModel *model,
+                                       GtkTreePath           *path)
+{
+  ThunarFavourite *favourite;
+  gint             n;
+
+  g_return_val_if_fail (THUNAR_IS_FAVOURITES_MODEL (model), FALSE);
+  g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
+
+  /* append to the list is always possible */
+  n = gtk_tree_path_get_indices (path)[0];
+  if (n >= model->n_favourites)
+    return TRUE;
+
+  /* lookup the ThunarFavourite for the path (remember, we are
+   * checking whether we can insert BEFORE path)
+   */
+  for (favourite = model->favourites; --n >= 0; favourite = favourite->next)
+    ;
+
+  /* cannot drop before special favourites! */
+  return (favourite->name == NULL
+       && favourite->file != NULL);
+}
+
+
+
+/**
+ * thunar_favourites_model_move:
+ * @model    : a #ThunarFavouritesModel.
+ * @src_path : the source path.
+ * @dst_path : the destination path.
+ *
+ * Moves the favourite at @src_path to @dst_path, adjusting other
+ * favourite's positions as required.
+ **/
+void
+thunar_favourites_model_move (ThunarFavouritesModel *model,
+                              GtkTreePath           *src_path,
+                              GtkTreePath           *dst_path)
+{
+  ThunarFavourite *favourite;
+  GtkTreePath     *path;
+  const gchar     *name;
+  ThunarFile      *file;
+  gint            *order;
+  gint             index_src;
+  gint             index_dst;
+  gint             index;
+
+  g_return_if_fail (THUNAR_IS_FAVOURITES_MODEL (model));
+  g_return_if_fail (gtk_tree_path_get_depth (src_path) > 0);
+  g_return_if_fail (gtk_tree_path_get_depth (dst_path) > 0);
+  g_return_if_fail (gtk_tree_path_get_indices (src_path)[0] >= 0);
+  g_return_if_fail (gtk_tree_path_get_indices (src_path)[0] < model->n_favourites);
+  g_return_if_fail (gtk_tree_path_get_indices (dst_path)[0] > 0);
+
+  index_src = gtk_tree_path_get_indices (src_path)[0];
+  index_dst = gtk_tree_path_get_indices (dst_path)[0];
+
+  if (G_UNLIKELY (index_src == index_dst))
+    return;
+
+  /* generate the order for the rows prior the dst/src rows */
+  order = g_new (gint, model->n_favourites);
+  for (favourite = model->favourites, index = 0;
+       index < index_src && index < index_dst;
+       favourite = favourite->next, ++index)
+    {
+      order[index] = index;
+    }
+
+  if (index == index_src)
+    {
+      file = favourite->file;
+      name = favourite->name;
+
+      for (; index < index_dst; favourite = favourite->next, ++index)
+        {
+          favourite->file = favourite->next->file;
+          favourite->name = favourite->next->name;
+          order[index] = index + 1;
+        }
+
+      favourite->file = file;
+      favourite->name = name;
+      order[index++] = index_src;
+    }
+  else
+    {
+      for (; index < index_src; favourite = favourite->next, ++index)
+        ;
+
+      g_assert (index == index_src);
+
+      file = favourite->file;
+      name = favourite->name;
+
+      for (; index > index_dst; favourite = favourite->prev, --index)
+        {
+          favourite->file = favourite->prev->file;
+          favourite->name = favourite->prev->name;
+          order[index] = index - 1;
+        }
+
+      g_assert (index == index_dst);
+
+      favourite->file = file;
+      favourite->name = name;
+      order[index] = index_src;
+      index = index_src + 1;
+    }
+
+  /* generate the remaining order */
+  for (; index < model->n_favourites; ++index)
+    order[index] = index;
+
+  /* tell all listeners about the reordering just performed */
+  path = gtk_tree_path_new ();
+  gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model), path, NULL, order);
+  gtk_tree_path_free (path);
+
+  /* the favourites list was changed, so write the gtk bookmarks file */
+  thunar_favourites_model_save (model);
+
+  /* cleanup */
+  g_free (order);
+}
+
+
diff --git a/thunar/thunar-favourites-model.h b/thunar/thunar-favourites-model.h
index badc3d9fe..285fd26c7 100644
--- a/thunar/thunar-favourites-model.h
+++ b/thunar/thunar-favourites-model.h
@@ -55,10 +55,16 @@ ThunarFavouritesModel *thunar_favourites_model_get_default    (void);
 gboolean               thunar_favourites_model_iter_for_file  (ThunarFavouritesModel *model,
                                                                ThunarFile            *file,
                                                                GtkTreeIter           *iter);
-
 ThunarFile            *thunar_favourites_model_file_for_iter  (ThunarFavouritesModel *model,
                                                                GtkTreeIter           *iter);
 
+gboolean               thunar_favourites_model_drop_possible  (ThunarFavouritesModel *model,
+                                                               GtkTreePath           *path);
+
+void                   thunar_favourites_model_move           (ThunarFavouritesModel *model,
+                                                               GtkTreePath           *src_path,
+                                                               GtkTreePath           *dst_path);
+
 G_END_DECLS;
 
 #endif /* !__THUNAR_FAVOURITES_MODEL_H__ */
diff --git a/thunar/thunar-favourites-view.c b/thunar/thunar-favourites-view.c
index 37da7d66f..d1e0991cc 100644
--- a/thunar/thunar-favourites-view.c
+++ b/thunar/thunar-favourites-view.c
@@ -26,23 +26,51 @@
 
 
 
+/* Identifiers for signals */
 enum
 {
   FAVOURITE_ACTIVATED,
   LAST_SIGNAL,
 };
 
+/* Identifiers for DnD target types */
+enum
+{
+  GTK_TREE_MODEL_ROW,
+  TEXT_URI_LIST,
+};
 
 
-static void     thunar_favourites_view_class_init     (ThunarFavouritesViewClass *klass);
-static void     thunar_favourites_view_init           (ThunarFavouritesView      *view);
-static void     thunar_favourites_view_row_activated  (GtkTreeView               *tree_view,
-                                                       GtkTreePath               *path,
-                                                       GtkTreeViewColumn         *column);
+
+static void         thunar_favourites_view_class_init             (ThunarFavouritesViewClass *klass);
+static void         thunar_favourites_view_init                   (ThunarFavouritesView      *view);
+static void         thunar_favourites_view_row_activated          (GtkTreeView               *tree_view,
+                                                                   GtkTreePath               *path,
+                                                                   GtkTreeViewColumn         *column);
+static void         thunar_favourites_view_drag_data_received     (GtkWidget                 *widget,
+                                                                   GdkDragContext            *context,
+                                                                   gint                       x,
+                                                                   gint                       y,
+                                                                   GtkSelectionData          *selection_data,
+                                                                   guint                      info,
+                                                                   guint                      time);
+static gboolean     thunar_favourites_view_drag_drop              (GtkWidget                 *widget,
+                                                                   GdkDragContext            *context,
+                                                                   gint                       x,
+                                                                   gint                       y,
+                                                                   guint                      time);
+static gboolean     thunar_favourites_view_drag_motion            (GtkWidget                 *widget,
+                                                                   GdkDragContext            *context,
+                                                                   gint                       x,
+                                                                   gint                       y,
+                                                                   guint                      time);
+static GtkTreePath *thunar_favourites_view_compute_drop_position  (ThunarFavouritesView      *view,
+                                                                   gint                       x,
+                                                                   gint                       y);
 #if GTK_CHECK_VERSION(2,6,0)
-static gboolean thunar_favourites_view_separator_func (GtkTreeModel              *model,
-                                                       GtkTreeIter               *iter,
-                                                       gpointer                   user_data);
+static gboolean     thunar_favourites_view_separator_func         (GtkTreeModel              *model,
+                                                                   GtkTreeIter               *iter,
+                                                                   gpointer                   user_data);
 #endif
 
 
@@ -65,6 +93,17 @@ struct _ThunarFavouritesView
 
 static guint view_signals[LAST_SIGNAL];
 
+/* Target types for dragging from the favourites view */
+static const GtkTargetEntry drag_targets[] = {
+  { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
+};
+
+/* Target types for dropping into the favourites view */
+static const GtkTargetEntry drop_targets[] = {
+  { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
+  { "text/uri-list", 0, TEXT_URI_LIST },
+};
+
 
 
 G_DEFINE_TYPE (ThunarFavouritesView, thunar_favourites_view, GTK_TYPE_TREE_VIEW);
@@ -75,6 +114,12 @@ static void
 thunar_favourites_view_class_init (ThunarFavouritesViewClass *klass)
 {
   GtkTreeViewClass *gtktree_view_class;
+  GtkWidgetClass   *gtkwidget_class;
+
+  gtkwidget_class = GTK_WIDGET_CLASS (klass);
+  gtkwidget_class->drag_data_received = thunar_favourites_view_drag_data_received;
+  gtkwidget_class->drag_drop = thunar_favourites_view_drag_drop;
+  gtkwidget_class->drag_motion = thunar_favourites_view_drag_motion;
 
   gtktree_view_class = GTK_TREE_VIEW_CLASS (klass);
   gtktree_view_class->row_activated = thunar_favourites_view_row_activated;
@@ -124,6 +169,17 @@ thunar_favourites_view_init (ThunarFavouritesView *view)
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
   gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
 
+  /* enable drag support for the favourites view (actually used to support reordering) */
+  gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (view), GDK_BUTTON1_MASK, drag_targets,
+                                          G_N_ELEMENTS (drag_targets), GDK_ACTION_MOVE);
+
+  /* enable drop support for the favourites view (both internal reordering
+   * and adding new favourites from other widgets)
+   */
+  gtk_drag_dest_set (GTK_WIDGET (view), GTK_DEST_DEFAULT_ALL,
+                     drop_targets, G_N_ELEMENTS (drop_targets),
+                     GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
 #if GTK_CHECK_VERSION(2,6,0)
   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (view), thunar_favourites_view_separator_func, NULL, NULL);
 #endif
@@ -159,6 +215,159 @@ thunar_favourites_view_row_activated (GtkTreeView       *tree_view,
 
 
 
+static void
+thunar_favourites_view_drag_data_received (GtkWidget        *widget,
+                                           GdkDragContext   *context,
+                                           gint              x,
+                                           gint              y,
+                                           GtkSelectionData *selection_data,
+                                           guint             info,
+                                           guint             time)
+{
+  ThunarFavouritesView *view = THUNAR_FAVOURITES_VIEW (widget);
+  GtkTreeSelection     *selection;
+  GtkTreeModel         *model;
+  GtkTreePath          *dst_path;
+  GtkTreePath          *src_path;
+  GtkTreeIter           iter;
+
+  g_return_if_fail (THUNAR_IS_FAVOURITES_VIEW (view));
+
+  /* compute the drop position */
+  dst_path = thunar_favourites_view_compute_drop_position (view, x, y);
+
+  if (selection_data->target == gdk_atom_intern ("text/uri-list", FALSE))
+    {
+      g_error ("text/uri-list not handled yet");
+    }
+  else if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
+    {
+      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_favourites_model_drop_possible (THUNAR_FAVOURITES_MODEL (model), dst_path))
+            gtk_tree_path_next (dst_path);
+
+          /* perform the move */
+          src_path = gtk_tree_model_get_path (model, &iter);
+          thunar_favourites_model_move (THUNAR_FAVOURITES_MODEL (model), src_path, dst_path);
+          gtk_tree_path_free (src_path);
+        }
+    }
+
+  gtk_tree_path_free (dst_path);
+}
+
+
+
+static gboolean
+thunar_favourites_view_drag_drop (GtkWidget      *widget,
+                                  GdkDragContext *context,
+                                  gint            x,
+                                  gint            y,
+                                  guint           time)
+{
+  g_return_val_if_fail (THUNAR_IS_FAVOURITES_VIEW (widget), FALSE);
+  return TRUE;
+}
+
+
+
+static gboolean
+thunar_favourites_view_drag_motion (GtkWidget      *widget,
+                                    GdkDragContext *context,
+                                    gint            x,
+                                    gint            y,
+                                    guint           time)
+{
+  GtkTreeViewDropPosition position = GTK_TREE_VIEW_DROP_BEFORE;
+  ThunarFavouritesView   *view = THUNAR_FAVOURITES_VIEW (widget);
+  GdkDragAction           action;
+  GtkTreeModel           *model;
+  GtkTreePath            *path;
+
+  /* check the action that should be performed */
+  if (context->suggested_action == GDK_ACTION_COPY || (context->actions & GDK_ACTION_COPY) != 0)
+    action = GDK_ACTION_COPY;
+  else if (context->suggested_action == GDK_ACTION_MOVE || (context->actions & GDK_ACTION_MOVE) != 0)
+    action = GDK_ACTION_MOVE;
+  else
+    return FALSE;
+
+  /* compute the drop position for the coordinates */
+  path = thunar_favourites_view_compute_drop_position (view, x, y);
+
+  /* check if path is about to append to the model */
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+  if (gtk_tree_path_get_indices (path)[0] >= gtk_tree_model_iter_n_children (model, NULL))
+    {
+      /* set the position to "after" and move the path to
+       * point to the previous row instead; required to
+       * get the highlighting in GtkTreeView correct.
+       */
+      position = GTK_TREE_VIEW_DROP_AFTER;
+      gtk_tree_path_prev (path);
+    }
+
+  /* highlight the appropriate row */
+  gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (view), path, position);
+  gtk_tree_path_free (path);
+
+  gdk_drag_status (context, action, time);
+  return TRUE;
+}
+
+
+
+static GtkTreePath*
+thunar_favourites_view_compute_drop_position (ThunarFavouritesView *view,
+                                              gint                  x,
+                                              gint                  y)
+{
+  GtkTreeViewColumn *column;
+  GtkTreeModel      *model;
+  GdkRectangle       area;
+  GtkTreePath       *path;
+  gint               n_rows;
+
+  g_return_val_if_fail (gtk_tree_view_get_model (GTK_TREE_VIEW (view)) != NULL, NULL);
+  g_return_val_if_fail (THUNAR_IS_FAVOURITES_VIEW (view), NULL);
+
+  /* query the number of rows in the model */
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+  n_rows = gtk_tree_model_iter_n_children (model, NULL);
+
+  if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view), x, y,
+                                     &path, &column, &x, &y))
+    {
+      /* determine the exact path of the row the user is trying to drop 
+       * (taking into account the relative y position)
+       */
+      gtk_tree_view_get_background_area (GTK_TREE_VIEW (view), path, column, &area);
+      if (y >= area.height / 2)
+        gtk_tree_path_next (path);
+
+      /* find a suitable drop path (we cannot drop into the default favourites list) */
+      for (; gtk_tree_path_get_indices (path)[0] < n_rows; gtk_tree_path_next (path))
+        if (thunar_favourites_model_drop_possible (THUNAR_FAVOURITES_MODEL (model), path))
+          return path;
+    }
+  else
+    {
+      /* we'll append to the favourites list */
+      path = gtk_tree_path_new_from_indices (n_rows, -1);
+    }
+
+  return path;
+}
+
+
+
 #if GTK_CHECK_VERSION(2,6,0)
 static gboolean
 thunar_favourites_view_separator_func (GtkTreeModel *model,
-- 
GitLab