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