summaryrefslogtreecommitdiff
path: root/src/nautilus-tree-view-drag-dest.c
diff options
context:
space:
mode:
authorCarlos Soriano <csoriano@gnome.org>2016-04-22 17:02:38 +0200
committerCarlos Soriano <csoriano@gnome.org>2016-04-25 16:31:42 +0200
commit7e24f1b2a2b3b7860ee29820d69f5a1511fee994 (patch)
treea2ee175c5f9cd05ab518e768a073ef4a31fe8376 /src/nautilus-tree-view-drag-dest.c
parent2774b8552dcc89ae744700af5832dbf76c138a9e (diff)
downloadnautilus-7e24f1b2a2b3b7860ee29820d69f5a1511fee994.tar.gz
general: merge libnautilus-private to srcwip/csoriano/private-to-src
And fix make distcheck. Although libnautilus-private seem self contained, it was actually depending on the files on src/ for dnd. Not only that, but files in libnautilus-private also were depending on dnd files, which you can guess it's wrong. Before the desktop split, this was working because the files were distributed, but now was a problem since we reestructured the code, and now nautilus being a library make distcheck stop working. First solution was try to fix this inter dependency of files, but at some point I realized that there was no real point on splitting some of those files, because for example, is perfectly fine for dnd to need to access the window functions, and it's perfectly fine for the widgets in the private library to need to access to all dnd functions. So seems to me the private library of nautilus is somehow an artificial split, which provides more problems than solutions. We needed libnautilus-private to have a private library that we could isolate from extensions, but I don't think it worth given the problems it provides, and also, this not so good logical split. Right now, since with the desktop split we created a libnautilus to be used by the desktop part of nautilus, extensions have access to all the API of nautilus. We will think in future how this can be handled if we want. So for now, merge the libnautilus-private into src, and let's rethink a better logic to split the code and the private parts of nautilus than what we had. Thanks a lot to Rafael Fonseca for helping in get this done. https://bugzilla.gnome.org/show_bug.cgi?id=765543
Diffstat (limited to 'src/nautilus-tree-view-drag-dest.c')
-rw-r--r--src/nautilus-tree-view-drag-dest.c1265
1 files changed, 1265 insertions, 0 deletions
diff --git a/src/nautilus-tree-view-drag-dest.c b/src/nautilus-tree-view-drag-dest.c
new file mode 100644
index 000000000..97257dd94
--- /dev/null
+++ b/src/nautilus-tree-view-drag-dest.c
@@ -0,0 +1,1265 @@
+
+/*
+ * Nautilus
+ *
+ * Copyright (C) 2002 Sun Microsystems, Inc.
+ *
+ * Nautilus 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.
+ *
+ * Nautilus 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Dave Camp <dave@ximian.com>
+ * XDS support: Benedikt Meurer <benny@xfce.org> (adapted by Amos Brocco <amos.brocco@unifr.ch>)
+ */
+
+/* nautilus-tree-view-drag-dest.c: Handles drag and drop for treeviews which
+ * contain a hierarchy of files
+ */
+
+#include <config.h>
+
+#include "nautilus-tree-view-drag-dest.h"
+
+#include "nautilus-dnd.h"
+#include "nautilus-file-changes-queue.h"
+#include "nautilus-global-preferences.h"
+#include "nautilus-link.h"
+
+#include <gtk/gtk.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#define DEBUG_FLAG NAUTILUS_DEBUG_LIST_VIEW
+#include "nautilus-debug.h"
+
+#define AUTO_SCROLL_MARGIN 20
+#define HOVER_EXPAND_TIMEOUT 1
+
+struct _NautilusTreeViewDragDestDetails {
+ GtkTreeView *tree_view;
+
+ gboolean drop_occurred;
+
+ gboolean have_drag_data;
+ guint drag_type;
+ GtkSelectionData *drag_data;
+ GList *drag_list;
+
+ guint hover_id;
+ guint highlight_id;
+ guint scroll_id;
+ guint expand_id;
+
+ char *direct_save_uri;
+ char *target_uri;
+};
+
+enum {
+ GET_ROOT_URI,
+ GET_FILE_FOR_PATH,
+ MOVE_COPY_ITEMS,
+ HANDLE_NETSCAPE_URL,
+ HANDLE_URI_LIST,
+ HANDLE_TEXT,
+ HANDLE_RAW,
+ HANDLE_HOVER,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (NautilusTreeViewDragDest, nautilus_tree_view_drag_dest,
+ G_TYPE_OBJECT);
+
+static const GtkTargetEntry drag_types [] = {
+ { NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
+ /* prefer "_NETSCAPE_URL" over "text/uri-list" to satisfy web browsers. */
+ { NAUTILUS_ICON_DND_NETSCAPE_URL_TYPE, 0, NAUTILUS_ICON_DND_NETSCAPE_URL },
+ { NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
+ { NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, 0, NAUTILUS_ICON_DND_XDNDDIRECTSAVE }, /* XDS Protocol Type */
+ { NAUTILUS_ICON_DND_RAW_TYPE, 0, NAUTILUS_ICON_DND_RAW }
+};
+
+
+static void
+gtk_tree_view_vertical_autoscroll (GtkTreeView *tree_view)
+{
+ GdkRectangle visible_rect;
+ GtkAdjustment *vadjustment;
+ GdkDisplay *display;
+ GdkSeat *seat;
+ GdkDevice *pointer;
+ GdkWindow *window;
+ int y;
+ int offset;
+ float value;
+
+ window = gtk_tree_view_get_bin_window (tree_view);
+ vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (tree_view));
+
+ display = gtk_widget_get_display (GTK_WIDGET (tree_view));
+ seat = gdk_display_get_default_seat (display);
+ pointer = gdk_seat_get_pointer (seat);
+ gdk_window_get_device_position (window, pointer,
+ NULL, &y, NULL);
+
+ y += gtk_adjustment_get_value (vadjustment);
+
+ gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
+
+ offset = y - (visible_rect.y + 2 * AUTO_SCROLL_MARGIN);
+ if (offset > 0) {
+ offset = y - (visible_rect.y + visible_rect.height - 2 * AUTO_SCROLL_MARGIN);
+ if (offset < 0) {
+ return;
+ }
+ }
+
+ value = CLAMP (gtk_adjustment_get_value (vadjustment) + offset, 0.0,
+ gtk_adjustment_get_upper (vadjustment) - gtk_adjustment_get_page_size (vadjustment));
+ gtk_adjustment_set_value (vadjustment, value);
+}
+
+static int
+scroll_timeout (gpointer data)
+{
+ GtkTreeView *tree_view = GTK_TREE_VIEW (data);
+
+ gtk_tree_view_vertical_autoscroll (tree_view);
+
+ return TRUE;
+}
+
+static void
+remove_scroll_timeout (NautilusTreeViewDragDest *dest)
+{
+ if (dest->details->scroll_id) {
+ g_source_remove (dest->details->scroll_id);
+ dest->details->scroll_id = 0;
+ }
+}
+
+static int
+expand_timeout (gpointer data)
+{
+ GtkTreeView *tree_view;
+ GtkTreePath *drop_path;
+
+ tree_view = GTK_TREE_VIEW (data);
+
+ gtk_tree_view_get_drag_dest_row (tree_view, &drop_path, NULL);
+
+ if (drop_path) {
+ gtk_tree_view_expand_row (tree_view, drop_path, FALSE);
+ gtk_tree_path_free (drop_path);
+ }
+
+ return FALSE;
+}
+
+static void
+remove_expand_timer (NautilusTreeViewDragDest *dest)
+{
+ if (dest->details->expand_id) {
+ g_source_remove (dest->details->expand_id);
+ dest->details->expand_id = 0;
+ }
+}
+
+static gboolean
+highlight_draw (GtkWidget *widget,
+ cairo_t *cr,
+ gpointer data)
+{
+ GdkWindow *bin_window;
+ int width;
+ int height;
+ GtkStyleContext *style;
+
+ /* FIXMEchpe: is bin window right here??? */
+ bin_window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget));
+
+ width = gdk_window_get_width (bin_window);
+ height = gdk_window_get_height (bin_window);
+
+ style = gtk_widget_get_style_context (widget);
+
+ gtk_style_context_save (style);
+ gtk_style_context_add_class (style, "treeview-drop-indicator");
+
+ gtk_render_focus (style,
+ cr,
+ 0, 0, width, height);
+
+ gtk_style_context_restore (style);
+
+ return FALSE;
+}
+
+static void
+set_widget_highlight (NautilusTreeViewDragDest *dest, gboolean highlight)
+{
+ if (!highlight && dest->details->highlight_id) {
+ g_signal_handler_disconnect (dest->details->tree_view,
+ dest->details->highlight_id);
+ dest->details->highlight_id = 0;
+ gtk_widget_queue_draw (GTK_WIDGET (dest->details->tree_view));
+ }
+
+ if (highlight && !dest->details->highlight_id) {
+ dest->details->highlight_id =
+ g_signal_connect_object (dest->details->tree_view,
+ "draw",
+ G_CALLBACK (highlight_draw),
+ dest, 0);
+ gtk_widget_queue_draw (GTK_WIDGET (dest->details->tree_view));
+ }
+}
+
+static void
+set_drag_dest_row (NautilusTreeViewDragDest *dest,
+ GtkTreePath *path)
+{
+ if (path) {
+ set_widget_highlight (dest, FALSE);
+ gtk_tree_view_set_drag_dest_row
+ (dest->details->tree_view,
+ path,
+ GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
+ } else {
+ set_widget_highlight (dest, TRUE);
+ gtk_tree_view_set_drag_dest_row (dest->details->tree_view,
+ NULL,
+ 0);
+ }
+}
+
+static void
+clear_drag_dest_row (NautilusTreeViewDragDest *dest)
+{
+ gtk_tree_view_set_drag_dest_row (dest->details->tree_view, NULL, 0);
+ set_widget_highlight (dest, FALSE);
+}
+
+static gboolean
+get_drag_data (NautilusTreeViewDragDest *dest,
+ GdkDragContext *context,
+ guint32 time)
+{
+ GdkAtom target;
+
+ target = gtk_drag_dest_find_target (GTK_WIDGET (dest->details->tree_view),
+ context,
+ NULL);
+
+ if (target == GDK_NONE) {
+ return FALSE;
+ }
+
+ if (target == gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE) &&
+ !dest->details->drop_occurred) {
+ dest->details->drag_type = NAUTILUS_ICON_DND_XDNDDIRECTSAVE;
+ dest->details->have_drag_data = TRUE;
+ return TRUE;
+ }
+
+ gtk_drag_get_data (GTK_WIDGET (dest->details->tree_view),
+ context, target, time);
+
+ return TRUE;
+}
+
+static void
+remove_hover_timer (NautilusTreeViewDragDest *dest)
+{
+ if (dest->details->hover_id != 0) {
+ g_source_remove (dest->details->hover_id);
+ dest->details->hover_id = 0;
+ }
+}
+
+static void
+free_drag_data (NautilusTreeViewDragDest *dest)
+{
+ dest->details->have_drag_data = FALSE;
+
+ if (dest->details->drag_data) {
+ gtk_selection_data_free (dest->details->drag_data);
+ dest->details->drag_data = NULL;
+ }
+
+ if (dest->details->drag_list) {
+ nautilus_drag_destroy_selection_list (dest->details->drag_list);
+ dest->details->drag_list = NULL;
+ }
+
+ g_free (dest->details->direct_save_uri);
+ dest->details->direct_save_uri = NULL;
+
+ g_free (dest->details->target_uri);
+ dest->details->target_uri = NULL;
+
+ remove_hover_timer (dest);
+ remove_expand_timer (dest);
+}
+
+static gboolean
+hover_timer (gpointer user_data)
+{
+ NautilusTreeViewDragDest *dest = user_data;
+
+ dest->details->hover_id = 0;
+
+ g_signal_emit (dest, signals[HANDLE_HOVER], 0, dest->details->target_uri);
+
+ return FALSE;
+}
+
+static void
+check_hover_timer (NautilusTreeViewDragDest *dest,
+ const char *uri)
+{
+ GtkSettings *settings;
+ guint timeout;
+
+ if (g_strcmp0 (uri, dest->details->target_uri) == 0) {
+ return;
+ }
+ remove_hover_timer (dest);
+
+ settings = gtk_widget_get_settings (GTK_WIDGET (dest->details->tree_view));
+ g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
+
+ g_free (dest->details->target_uri);
+ dest->details->target_uri = NULL;
+
+ if (uri != NULL) {
+ dest->details->target_uri = g_strdup (uri);
+ dest->details->hover_id =
+ gdk_threads_add_timeout (timeout,
+ hover_timer,
+ dest);
+ }
+}
+
+static void
+check_expand_timer (NautilusTreeViewDragDest *dest,
+ GtkTreePath *drop_path,
+ GtkTreePath *old_drop_path)
+{
+ GtkTreeModel *model;
+ GtkTreeIter drop_iter;
+
+ model = gtk_tree_view_get_model (dest->details->tree_view);
+
+ if (drop_path == NULL ||
+ (old_drop_path != NULL && gtk_tree_path_compare (old_drop_path, drop_path) != 0)) {
+ remove_expand_timer (dest);
+ }
+
+ if (dest->details->expand_id == 0 &&
+ drop_path != NULL) {
+ gtk_tree_model_get_iter (model, &drop_iter, drop_path);
+ if (gtk_tree_model_iter_has_child (model, &drop_iter)) {
+ dest->details->expand_id =
+ g_timeout_add_seconds (HOVER_EXPAND_TIMEOUT,
+ expand_timeout,
+ dest->details->tree_view);
+ }
+ }
+}
+
+static char *
+get_root_uri (NautilusTreeViewDragDest *dest)
+{
+ char *uri;
+
+ g_signal_emit (dest, signals[GET_ROOT_URI], 0, &uri);
+
+ return uri;
+}
+
+static NautilusFile *
+file_for_path (NautilusTreeViewDragDest *dest, GtkTreePath *path)
+{
+ NautilusFile *file;
+ char *uri;
+
+ if (path) {
+ g_signal_emit (dest, signals[GET_FILE_FOR_PATH], 0, path, &file);
+ } else {
+ uri = get_root_uri (dest);
+
+ file = NULL;
+ if (uri != NULL) {
+ file = nautilus_file_get_by_uri (uri);
+ }
+
+ g_free (uri);
+ }
+
+ return file;
+}
+
+static char *
+get_drop_target_uri_for_path (NautilusTreeViewDragDest *dest,
+ GtkTreePath *path)
+{
+ NautilusFile *file;
+ char *target = NULL;
+ gboolean can;
+
+ file = file_for_path (dest, path);
+ if (file == NULL) {
+ return NULL;
+ }
+ can = nautilus_drag_can_accept_info (file,
+ dest->details->drag_type,
+ dest->details->drag_list);
+ if (can) {
+ target = nautilus_file_get_target_uri (file);
+ }
+ nautilus_file_unref (file);
+
+ return target;
+}
+
+static void
+check_hover_expand_timer (NautilusTreeViewDragDest *dest,
+ GtkTreePath *path,
+ GtkTreePath *drop_path,
+ GtkTreePath *old_drop_path)
+{
+ gboolean use_tree = g_settings_get_boolean (nautilus_list_view_preferences,
+ NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE);
+
+ if (use_tree) {
+ check_expand_timer (dest, drop_path, old_drop_path);
+ } else {
+ char *uri;
+ uri = get_drop_target_uri_for_path (dest, path);
+ check_hover_timer (dest, uri);
+ g_free (uri);
+ }
+}
+
+static GtkTreePath *
+get_drop_path (NautilusTreeViewDragDest *dest,
+ GtkTreePath *path)
+{
+ NautilusFile *file;
+ GtkTreePath *ret;
+
+ if (!path || !dest->details->have_drag_data) {
+ return NULL;
+ }
+
+ ret = gtk_tree_path_copy (path);
+ file = file_for_path (dest, ret);
+
+ /* Go up the tree until we find a file that can accept a drop */
+ while (file == NULL /* dummy row */ ||
+ !nautilus_drag_can_accept_info (file,
+ dest->details->drag_type,
+ dest->details->drag_list)) {
+ if (gtk_tree_path_get_depth (ret) == 1) {
+ gtk_tree_path_free (ret);
+ ret = NULL;
+ break;
+ } else {
+ gtk_tree_path_up (ret);
+
+ nautilus_file_unref (file);
+ file = file_for_path (dest, ret);
+ }
+ }
+ nautilus_file_unref (file);
+
+ return ret;
+}
+
+static guint
+get_drop_action (NautilusTreeViewDragDest *dest,
+ GdkDragContext *context,
+ GtkTreePath *path)
+{
+ char *drop_target;
+ int action;
+
+ if (!dest->details->have_drag_data ||
+ (dest->details->drag_type == NAUTILUS_ICON_DND_GNOME_ICON_LIST &&
+ dest->details->drag_list == NULL)) {
+ return 0;
+ }
+
+ drop_target = get_drop_target_uri_for_path (dest, path);
+ if (drop_target == NULL) {
+ return 0;
+ }
+
+ action = 0;
+ switch (dest->details->drag_type) {
+ case NAUTILUS_ICON_DND_GNOME_ICON_LIST :
+ nautilus_drag_default_drop_action_for_icons
+ (context,
+ drop_target,
+ dest->details->drag_list,
+ 0,
+ &action);
+ break;
+ case NAUTILUS_ICON_DND_NETSCAPE_URL:
+ action = nautilus_drag_default_drop_action_for_netscape_url (context);
+ break;
+ case NAUTILUS_ICON_DND_URI_LIST :
+ action = gdk_drag_context_get_suggested_action (context);
+ break;
+ case NAUTILUS_ICON_DND_TEXT:
+ case NAUTILUS_ICON_DND_RAW:
+ case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
+ action = GDK_ACTION_COPY;
+ break;
+ }
+
+ g_free (drop_target);
+
+ return action;
+}
+
+static gboolean
+drag_motion_callback (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ guint32 time,
+ gpointer data)
+{
+ NautilusTreeViewDragDest *dest;
+ GtkTreePath *path;
+ GtkTreePath *drop_path, *old_drop_path;
+ GtkTreeViewDropPosition pos;
+ GdkWindow *bin_window;
+ guint action;
+ gboolean res = TRUE;
+
+ dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data);
+
+ gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
+ x, y, &path, &pos);
+ if (pos == GTK_TREE_VIEW_DROP_BEFORE ||
+ pos == GTK_TREE_VIEW_DROP_AFTER) {
+ gtk_tree_path_free (path);
+ path = NULL;
+ }
+
+ if (!dest->details->have_drag_data) {
+ res = get_drag_data (dest, context, time);
+ }
+
+ if (!res) {
+ return FALSE;
+ }
+
+ drop_path = get_drop_path (dest, path);
+
+ action = 0;
+ bin_window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget));
+ if (bin_window != NULL) {
+ int bin_x, bin_y;
+ gdk_window_get_position (bin_window, &bin_x, &bin_y);
+ if (bin_y <= y) {
+ /* ignore drags on the header */
+ action = get_drop_action (dest, context, drop_path);
+ }
+ }
+
+ gtk_tree_view_get_drag_dest_row (GTK_TREE_VIEW (widget), &old_drop_path,
+ NULL);
+
+ if (action) {
+ set_drag_dest_row (dest, drop_path);
+ check_hover_expand_timer (dest, path, drop_path, old_drop_path);
+ } else {
+ clear_drag_dest_row (dest);
+ remove_hover_timer (dest);
+ remove_expand_timer (dest);
+ }
+
+ if (path) {
+ gtk_tree_path_free (path);
+ }
+
+ if (drop_path) {
+ gtk_tree_path_free (drop_path);
+ }
+
+ if (old_drop_path) {
+ gtk_tree_path_free (old_drop_path);
+ }
+
+ if (dest->details->scroll_id == 0) {
+ dest->details->scroll_id =
+ g_timeout_add (150,
+ scroll_timeout,
+ dest->details->tree_view);
+ }
+
+ gdk_drag_status (context, action, time);
+
+ return TRUE;
+}
+
+static void
+drag_leave_callback (GtkWidget *widget,
+ GdkDragContext *context,
+ guint32 time,
+ gpointer data)
+{
+ NautilusTreeViewDragDest *dest;
+
+ dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data);
+
+ clear_drag_dest_row (dest);
+
+ free_drag_data (dest);
+
+ remove_scroll_timeout (dest);
+}
+
+static char *
+get_drop_target_uri_at_pos (NautilusTreeViewDragDest *dest, int x, int y)
+{
+ char *drop_target = NULL;
+ GtkTreePath *path;
+ GtkTreePath *drop_path;
+ GtkTreeViewDropPosition pos;
+
+ gtk_tree_view_get_dest_row_at_pos (dest->details->tree_view, x, y,
+ &path, &pos);
+ if (pos == GTK_TREE_VIEW_DROP_BEFORE ||
+ pos == GTK_TREE_VIEW_DROP_AFTER) {
+ gtk_tree_path_free (path);
+ path = NULL;
+ }
+
+ drop_path = get_drop_path (dest, path);
+
+ drop_target = get_drop_target_uri_for_path (dest, drop_path);
+
+ if (path != NULL) {
+ gtk_tree_path_free (path);
+ }
+
+ if (drop_path != NULL) {
+ gtk_tree_path_free (drop_path);
+ }
+
+ return drop_target;
+}
+
+static void
+receive_uris (NautilusTreeViewDragDest *dest,
+ GdkDragContext *context,
+ GList *source_uris,
+ int x, int y)
+{
+ char *drop_target;
+ GdkDragAction action, real_action;
+
+ drop_target = get_drop_target_uri_at_pos (dest, x, y);
+ g_assert (drop_target != NULL);
+
+ real_action = gdk_drag_context_get_selected_action (context);
+
+ if (real_action == GDK_ACTION_ASK) {
+ if (nautilus_drag_selection_includes_special_link (dest->details->drag_list)) {
+ /* We only want to move the trash */
+ action = GDK_ACTION_MOVE;
+ } else {
+ action = GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK;
+ }
+ real_action = nautilus_drag_drop_action_ask
+ (GTK_WIDGET (dest->details->tree_view), action);
+ }
+
+ /* We only want to copy external uris */
+ if (dest->details->drag_type == NAUTILUS_ICON_DND_URI_LIST) {
+ real_action = GDK_ACTION_COPY;
+ }
+
+ if (real_action > 0) {
+ if (!nautilus_drag_uris_local (drop_target, source_uris)
+ || real_action != GDK_ACTION_MOVE) {
+ g_signal_emit (dest, signals[MOVE_COPY_ITEMS], 0,
+ source_uris,
+ drop_target,
+ real_action,
+ x, y);
+ }
+ }
+
+ g_free (drop_target);
+}
+
+static void
+receive_dropped_icons (NautilusTreeViewDragDest *dest,
+ GdkDragContext *context,
+ int x, int y)
+{
+ GList *source_uris;
+ GList *l;
+
+ /* FIXME: ignore local only moves */
+
+ if (!dest->details->drag_list) {
+ return;
+ }
+
+ source_uris = NULL;
+ for (l = dest->details->drag_list; l != NULL; l = l->next) {
+ source_uris = g_list_prepend (source_uris,
+ ((NautilusDragSelectionItem *)l->data)->uri);
+ }
+
+ source_uris = g_list_reverse (source_uris);
+
+ receive_uris (dest, context, source_uris, x, y);
+
+ g_list_free (source_uris);
+}
+
+static void
+receive_dropped_uri_list (NautilusTreeViewDragDest *dest,
+ GdkDragContext *context,
+ int x, int y)
+{
+ char *drop_target;
+
+ if (!dest->details->drag_data) {
+ return;
+ }
+
+ drop_target = get_drop_target_uri_at_pos (dest, x, y);
+ g_assert (drop_target != NULL);
+
+ g_signal_emit (dest, signals[HANDLE_URI_LIST], 0,
+ (char*) gtk_selection_data_get_data (dest->details->drag_data),
+ drop_target,
+ gdk_drag_context_get_selected_action (context),
+ x, y);
+
+ g_free (drop_target);
+}
+
+static void
+receive_dropped_text (NautilusTreeViewDragDest *dest,
+ GdkDragContext *context,
+ int x, int y)
+{
+ char *drop_target;
+ guchar *text;
+
+ if (!dest->details->drag_data) {
+ return;
+ }
+
+ drop_target = get_drop_target_uri_at_pos (dest, x, y);
+ g_assert (drop_target != NULL);
+
+ text = gtk_selection_data_get_text (dest->details->drag_data);
+ g_signal_emit (dest, signals[HANDLE_TEXT], 0,
+ (char *) text, drop_target,
+ gdk_drag_context_get_selected_action (context),
+ x, y);
+
+ g_free (text);
+ g_free (drop_target);
+}
+
+static void
+receive_dropped_raw (NautilusTreeViewDragDest *dest,
+ const char *raw_data, int length,
+ GdkDragContext *context,
+ int x, int y)
+{
+ char *drop_target;
+
+ if (!dest->details->drag_data) {
+ return;
+ }
+
+ drop_target = get_drop_target_uri_at_pos (dest, x, y);
+ g_assert (drop_target != NULL);
+
+ g_signal_emit (dest, signals[HANDLE_RAW], 0,
+ raw_data, length, drop_target,
+ dest->details->direct_save_uri,
+ gdk_drag_context_get_selected_action (context),
+ x, y);
+
+ g_free (drop_target);
+}
+
+static void
+receive_dropped_netscape_url (NautilusTreeViewDragDest *dest,
+ GdkDragContext *context,
+ int x, int y)
+{
+ char *drop_target;
+
+ if (!dest->details->drag_data) {
+ return;
+ }
+
+ drop_target = get_drop_target_uri_at_pos (dest, x, y);
+ g_assert (drop_target != NULL);
+
+ g_signal_emit (dest, signals[HANDLE_NETSCAPE_URL], 0,
+ (char*) gtk_selection_data_get_data (dest->details->drag_data),
+ drop_target,
+ gdk_drag_context_get_selected_action (context),
+ x, y);
+
+ g_free (drop_target);
+}
+
+static gboolean
+receive_xds (NautilusTreeViewDragDest *dest,
+ GtkWidget *widget,
+ guint32 time,
+ GdkDragContext *context,
+ int x, int y)
+{
+ GFile *location;
+ const guchar *selection_data;
+ gint selection_format;
+ gint selection_length;
+
+ selection_data = gtk_selection_data_get_data (dest->details->drag_data);
+ selection_format = gtk_selection_data_get_format (dest->details->drag_data);
+ selection_length = gtk_selection_data_get_length (dest->details->drag_data);
+
+ if (selection_format == 8
+ && selection_length == 1
+ && selection_data[0] == 'F') {
+ gtk_drag_get_data (widget, context,
+ gdk_atom_intern (NAUTILUS_ICON_DND_RAW_TYPE,
+ FALSE),
+ time);
+ return FALSE;
+ } else if (selection_format == 8
+ && selection_length == 1
+ && selection_data[0] == 'S') {
+ g_assert (dest->details->direct_save_uri != NULL);
+ location = g_file_new_for_uri (dest->details->direct_save_uri);
+
+ nautilus_file_changes_queue_file_added (location);
+ nautilus_file_changes_consume_changes (TRUE);
+
+ g_object_unref (location);
+ }
+ return TRUE;
+}
+
+
+static gboolean
+drag_data_received_callback (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint32 time,
+ gpointer data)
+{
+ NautilusTreeViewDragDest *dest;
+ const gchar *tmp;
+ int length;
+ gboolean success, finished;
+
+ dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data);
+
+ if (!dest->details->have_drag_data) {
+ dest->details->have_drag_data = TRUE;
+ dest->details->drag_type = info;
+ dest->details->drag_data =
+ gtk_selection_data_copy (selection_data);
+ if (info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) {
+ dest->details->drag_list =
+ nautilus_drag_build_selection_list (selection_data);
+ }
+ }
+
+ if (dest->details->drop_occurred) {
+ success = FALSE;
+ finished = TRUE;
+ switch (info) {
+ case NAUTILUS_ICON_DND_GNOME_ICON_LIST :
+ receive_dropped_icons (dest, context, x, y);
+ success = TRUE;
+ break;
+ case NAUTILUS_ICON_DND_NETSCAPE_URL :
+ receive_dropped_netscape_url (dest, context, x, y);
+ success = TRUE;
+ break;
+ case NAUTILUS_ICON_DND_URI_LIST :
+ receive_dropped_uri_list (dest, context, x, y);
+ success = TRUE;
+ break;
+ case NAUTILUS_ICON_DND_TEXT:
+ receive_dropped_text (dest, context, x, y);
+ success = TRUE;
+ break;
+ case NAUTILUS_ICON_DND_RAW:
+ length = gtk_selection_data_get_length (selection_data);
+ tmp = (const gchar *) gtk_selection_data_get_data (selection_data);
+ receive_dropped_raw (dest, tmp, length, context, x, y);
+ success = TRUE;
+ break;
+ case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
+ finished = receive_xds (dest, widget, time, context, x, y);
+ success = TRUE;
+ break;
+ }
+
+ if (finished) {
+ dest->details->drop_occurred = FALSE;
+ free_drag_data (dest);
+ gtk_drag_finish (context, success, FALSE, time);
+ }
+ }
+
+ /* appease GtkTreeView by preventing its drag_data_receive
+ * from being called */
+ g_signal_stop_emission_by_name (dest->details->tree_view,
+ "drag-data-received");
+
+ return TRUE;
+}
+
+static char *
+get_direct_save_filename (GdkDragContext *context)
+{
+ guchar *prop_text;
+ gint prop_len;
+
+ if (!gdk_property_get (gdk_drag_context_get_source_window (context), gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE),
+ gdk_atom_intern ("text/plain", FALSE), 0, 1024, FALSE, NULL, NULL,
+ &prop_len, &prop_text)) {
+ return NULL;
+ }
+
+ /* Zero-terminate the string */
+ prop_text = g_realloc (prop_text, prop_len + 1);
+ prop_text[prop_len] = '\0';
+
+ /* Verify that the file name provided by the source is valid */
+ if (*prop_text == '\0' ||
+ strchr ((const gchar *) prop_text, G_DIR_SEPARATOR) != NULL) {
+ DEBUG ("Invalid filename provided by XDS drag site");
+ g_free (prop_text);
+ return NULL;
+ }
+
+ return (gchar *) prop_text;
+}
+
+static gboolean
+set_direct_save_uri (NautilusTreeViewDragDest *dest,
+ GdkDragContext *context,
+ int x, int y)
+{
+ GFile *base, *child;
+ char *drop_uri;
+ char *filename, *uri;
+
+ g_assert (dest->details->direct_save_uri == NULL);
+
+ uri = NULL;
+
+ drop_uri = get_drop_target_uri_at_pos (dest, x, y);
+ if (drop_uri != NULL) {
+ filename = get_direct_save_filename (context);
+ if (filename != NULL) {
+ /* Resolve relative path */
+ base = g_file_new_for_uri (drop_uri);
+ child = g_file_get_child (base, filename);
+ uri = g_file_get_uri (child);
+
+ g_object_unref (base);
+ g_object_unref (child);
+
+ /* Change the property */
+ gdk_property_change (gdk_drag_context_get_source_window (context),
+ gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE),
+ gdk_atom_intern ("text/plain", FALSE), 8,
+ GDK_PROP_MODE_REPLACE, (const guchar *) uri,
+ strlen (uri));
+
+ dest->details->direct_save_uri = uri;
+ } else {
+ DEBUG ("Invalid filename provided by XDS drag site");
+ }
+ } else {
+ DEBUG ("Could not retrieve XDS drop destination");
+ }
+
+ return uri != NULL;
+}
+
+static gboolean
+drag_drop_callback (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ guint32 time,
+ gpointer data)
+{
+ NautilusTreeViewDragDest *dest;
+ guint info;
+ GdkAtom target;
+
+ dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data);
+
+ target = gtk_drag_dest_find_target (GTK_WIDGET (dest->details->tree_view),
+ context,
+ NULL);
+ if (target == GDK_NONE) {
+ return FALSE;
+ }
+
+ info = dest->details->drag_type;
+
+ if (info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE) {
+ /* We need to set this or get_drop_path will fail, and it
+ was unset by drag_leave_callback */
+ dest->details->have_drag_data = TRUE;
+ if (!set_direct_save_uri (dest, context, x, y)) {
+ return FALSE;
+ }
+ dest->details->have_drag_data = FALSE;
+ }
+
+ dest->details->drop_occurred = TRUE;
+
+ get_drag_data (dest, context, time);
+ remove_scroll_timeout (dest);
+ clear_drag_dest_row (dest);
+
+ return TRUE;
+}
+
+static void
+tree_view_weak_notify (gpointer user_data,
+ GObject *object)
+{
+ NautilusTreeViewDragDest *dest;
+
+ dest = NAUTILUS_TREE_VIEW_DRAG_DEST (user_data);
+
+ remove_scroll_timeout (dest);
+
+ dest->details->tree_view = NULL;
+}
+
+static void
+nautilus_tree_view_drag_dest_dispose (GObject *object)
+{
+ NautilusTreeViewDragDest *dest;
+
+ dest = NAUTILUS_TREE_VIEW_DRAG_DEST (object);
+
+ if (dest->details->tree_view) {
+ g_object_weak_unref (G_OBJECT (dest->details->tree_view),
+ tree_view_weak_notify,
+ dest);
+ }
+
+ remove_scroll_timeout (dest);
+
+ G_OBJECT_CLASS (nautilus_tree_view_drag_dest_parent_class)->dispose (object);
+}
+
+static void
+nautilus_tree_view_drag_dest_finalize (GObject *object)
+{
+ NautilusTreeViewDragDest *dest;
+
+ dest = NAUTILUS_TREE_VIEW_DRAG_DEST (object);
+ free_drag_data (dest);
+
+ G_OBJECT_CLASS (nautilus_tree_view_drag_dest_parent_class)->finalize (object);
+}
+
+static void
+nautilus_tree_view_drag_dest_init (NautilusTreeViewDragDest *dest)
+{
+ dest->details = G_TYPE_INSTANCE_GET_PRIVATE (dest, NAUTILUS_TYPE_TREE_VIEW_DRAG_DEST,
+ NautilusTreeViewDragDestDetails);
+}
+
+static void
+nautilus_tree_view_drag_dest_class_init (NautilusTreeViewDragDestClass *class)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->dispose = nautilus_tree_view_drag_dest_dispose;
+ gobject_class->finalize = nautilus_tree_view_drag_dest_finalize;
+
+ g_type_class_add_private (class, sizeof (NautilusTreeViewDragDestDetails));
+
+ signals[GET_ROOT_URI] =
+ g_signal_new ("get-root-uri",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
+ get_root_uri),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_STRING, 0);
+ signals[GET_FILE_FOR_PATH] =
+ g_signal_new ("get-file-for-path",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
+ get_file_for_path),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ NAUTILUS_TYPE_FILE, 1,
+ GTK_TYPE_TREE_PATH);
+ signals[MOVE_COPY_ITEMS] =
+ g_signal_new ("move-copy-items",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
+ move_copy_items),
+ NULL, NULL,
+
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 5,
+ G_TYPE_POINTER,
+ G_TYPE_STRING,
+ GDK_TYPE_DRAG_ACTION,
+ G_TYPE_INT,
+ G_TYPE_INT);
+ signals[HANDLE_NETSCAPE_URL] =
+ g_signal_new ("handle-netscape-url",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
+ handle_netscape_url),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 5,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ GDK_TYPE_DRAG_ACTION,
+ G_TYPE_INT,
+ G_TYPE_INT);
+ signals[HANDLE_URI_LIST] =
+ g_signal_new ("handle-uri-list",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
+ handle_uri_list),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 5,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ GDK_TYPE_DRAG_ACTION,
+ G_TYPE_INT,
+ G_TYPE_INT);
+ signals[HANDLE_TEXT] =
+ g_signal_new ("handle-text",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
+ handle_text),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 5,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ GDK_TYPE_DRAG_ACTION,
+ G_TYPE_INT,
+ G_TYPE_INT);
+ signals[HANDLE_RAW] =
+ g_signal_new ("handle-raw",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
+ handle_raw),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 7,
+ G_TYPE_POINTER,
+ G_TYPE_INT,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ GDK_TYPE_DRAG_ACTION,
+ G_TYPE_INT,
+ G_TYPE_INT);
+ signals[HANDLE_HOVER] =
+ g_signal_new ("handle-hover",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
+ handle_hover),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+}
+
+
+
+NautilusTreeViewDragDest *
+nautilus_tree_view_drag_dest_new (GtkTreeView *tree_view)
+{
+ NautilusTreeViewDragDest *dest;
+ GtkTargetList *targets;
+
+ dest = g_object_new (NAUTILUS_TYPE_TREE_VIEW_DRAG_DEST, NULL);
+
+ dest->details->tree_view = tree_view;
+ g_object_weak_ref (G_OBJECT (dest->details->tree_view),
+ tree_view_weak_notify, dest);
+
+ gtk_drag_dest_set (GTK_WIDGET (tree_view),
+ 0, drag_types, G_N_ELEMENTS (drag_types),
+ GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK);
+
+ targets = gtk_drag_dest_get_target_list (GTK_WIDGET (tree_view));
+ gtk_target_list_add_text_targets (targets, NAUTILUS_ICON_DND_TEXT);
+
+ g_signal_connect_object (tree_view,
+ "drag-motion",
+ G_CALLBACK (drag_motion_callback),
+ dest, 0);
+ g_signal_connect_object (tree_view,
+ "drag-leave",
+ G_CALLBACK (drag_leave_callback),
+ dest, 0);
+ g_signal_connect_object (tree_view,
+ "drag-drop",
+ G_CALLBACK (drag_drop_callback),
+ dest, 0);
+ g_signal_connect_object (tree_view,
+ "drag-data-received",
+ G_CALLBACK (drag_data_received_callback),
+ dest, 0);
+
+ return dest;
+}