nautilus-3.6.3/libnautilus-private/nautilus-tree-view-drag-dest.c

No issues found

   1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
   2 
   3 /*
   4  * Nautilus
   5  *
   6  * Copyright (C) 2002 Sun Microsystems, Inc.
   7  *
   8  * Nautilus is free software; you can redistribute it and/or
   9  * modify it under the terms of the GNU General Public License as
  10  * published by the Free Software Foundation; either version 2 of the
  11  * License, or (at your option) any later version.
  12  *
  13  * Nautilus is distributed in the hope that it will be useful, but
  14  * WITHOUT ANY WARRANTY; without even the implied warranty of
  15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16  * General Public License for more details.
  17  *
  18  * You should have received a copy of the GNU General Public
  19  * License along with this program; if not, write to the
  20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  21  * Boston, MA 02111-1307, USA.
  22  * 
  23  * Author: Dave Camp <dave@ximian.com>
  24  * XDS support: Benedikt Meurer <benny@xfce.org> (adapted by Amos Brocco <amos.brocco@unifr.ch>)
  25  */
  26 
  27 /* nautilus-tree-view-drag-dest.c: Handles drag and drop for treeviews which 
  28  *                                 contain a hierarchy of files
  29  */
  30 
  31 #include <config.h>
  32 
  33 #include "nautilus-tree-view-drag-dest.h"
  34 
  35 #include "nautilus-file-dnd.h"
  36 #include "nautilus-file-changes-queue.h"
  37 #include "nautilus-link.h"
  38 
  39 #include <gtk/gtk.h>
  40 
  41 #include <stdio.h>
  42 #include <string.h>
  43 
  44 #define DEBUG_FLAG NAUTILUS_DEBUG_LIST_VIEW
  45 #include "nautilus-debug.h"
  46 
  47 #define AUTO_SCROLL_MARGIN 20
  48 
  49 struct _NautilusTreeViewDragDestDetails {
  50 	GtkTreeView *tree_view;
  51 
  52 	gboolean drop_occurred;
  53 
  54 	gboolean have_drag_data;
  55 	guint drag_type;
  56 	GtkSelectionData *drag_data;
  57 	GList *drag_list;
  58 
  59 	guint highlight_id;
  60 	guint scroll_id;
  61 
  62 	char *direct_save_uri;
  63 };
  64 
  65 enum {
  66 	GET_ROOT_URI,
  67 	GET_FILE_FOR_PATH,
  68 	MOVE_COPY_ITEMS,
  69 	HANDLE_NETSCAPE_URL,
  70 	HANDLE_URI_LIST,
  71 	HANDLE_TEXT,
  72 	HANDLE_RAW,
  73 	LAST_SIGNAL
  74 };
  75 
  76 static guint signals[LAST_SIGNAL];
  77 
  78 G_DEFINE_TYPE (NautilusTreeViewDragDest, nautilus_tree_view_drag_dest,
  79 	       G_TYPE_OBJECT);
  80 
  81 static const GtkTargetEntry drag_types [] = {
  82 	{ NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
  83 	/* prefer "_NETSCAPE_URL" over "text/uri-list" to satisfy web browsers. */
  84 	{ NAUTILUS_ICON_DND_NETSCAPE_URL_TYPE, 0, NAUTILUS_ICON_DND_NETSCAPE_URL },
  85 	{ NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
  86 	{ NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, 0, NAUTILUS_ICON_DND_XDNDDIRECTSAVE }, /* XDS Protocol Type */
  87 	{ NAUTILUS_ICON_DND_RAW_TYPE, 0, NAUTILUS_ICON_DND_RAW }
  88 };
  89 
  90 
  91 static void
  92 gtk_tree_view_vertical_autoscroll (GtkTreeView *tree_view)
  93 {
  94 	GdkRectangle visible_rect;
  95 	GtkAdjustment *vadjustment;
  96 	GdkDeviceManager *manager;
  97 	GdkDevice *pointer;
  98 	GdkWindow *window;
  99 	int y;
 100 	int offset;
 101 	float value;
 102 	
 103 	window = gtk_tree_view_get_bin_window (tree_view);
 104 	vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (tree_view));
 105 
 106 	manager = gdk_display_get_device_manager (gtk_widget_get_display (GTK_WIDGET (tree_view)));
 107 	pointer = gdk_device_manager_get_client_pointer (manager);
 108 	gdk_window_get_device_position (window, pointer,
 109 					NULL, &y, NULL);
 110 	
 111 	y += gtk_adjustment_get_value (vadjustment);
 112 
 113 	gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
 114 	
 115 	offset = y - (visible_rect.y + 2 * AUTO_SCROLL_MARGIN);
 116 	if (offset > 0) {
 117 		offset = y - (visible_rect.y + visible_rect.height - 2 * AUTO_SCROLL_MARGIN);
 118 		if (offset < 0) {
 119 			return;
 120 		}
 121 	}
 122 
 123 	value = CLAMP (gtk_adjustment_get_value (vadjustment) + offset, 0.0,
 124 		       gtk_adjustment_get_upper (vadjustment) - gtk_adjustment_get_page_size (vadjustment));
 125 	gtk_adjustment_set_value (vadjustment, value);
 126 }
 127 
 128 static int
 129 scroll_timeout (gpointer data)
 130 {
 131 	GtkTreeView *tree_view = GTK_TREE_VIEW (data);
 132 	
 133 	gtk_tree_view_vertical_autoscroll (tree_view);
 134 
 135 	return TRUE;
 136 }
 137 
 138 static void
 139 remove_scroll_timeout (NautilusTreeViewDragDest *dest)
 140 {
 141 	if (dest->details->scroll_id) {
 142 		g_source_remove (dest->details->scroll_id);
 143 		dest->details->scroll_id = 0;
 144 	}
 145 }
 146 
 147 static gboolean
 148 highlight_draw (GtkWidget *widget,
 149 		cairo_t   *cr,
 150                 gpointer data)
 151 {
 152 	GdkWindow *bin_window;
 153 	int width;
 154 	int height;
 155 	GtkStyleContext *style;
 156 
 157         /* FIXMEchpe: is bin window right here??? */
 158         bin_window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget));
 159 
 160         width = gdk_window_get_width (bin_window);
 161         height = gdk_window_get_height (bin_window);
 162 
 163 	style = gtk_widget_get_style_context (widget);
 164 
 165 	gtk_style_context_save (style);
 166 	gtk_style_context_add_class (style, "treeview-drop-indicator");
 167 
 168         gtk_render_focus (style,
 169 			  cr,
 170 			  0, 0, width, height);
 171 
 172 	gtk_style_context_restore (style);
 173 
 174 	return FALSE;
 175 }
 176 
 177 static void
 178 set_widget_highlight (NautilusTreeViewDragDest *dest, gboolean highlight)
 179 {
 180 	if (!highlight && dest->details->highlight_id) {
 181 		g_signal_handler_disconnect (dest->details->tree_view,
 182 					     dest->details->highlight_id);
 183 		dest->details->highlight_id = 0;
 184 		gtk_widget_queue_draw (GTK_WIDGET (dest->details->tree_view));
 185 	}
 186 	
 187 	if (highlight && !dest->details->highlight_id) {
 188 		dest->details->highlight_id = 
 189 			g_signal_connect_object (dest->details->tree_view,
 190 						 "draw",
 191 						 G_CALLBACK (highlight_draw),
 192 						 dest, 0);
 193 		gtk_widget_queue_draw (GTK_WIDGET (dest->details->tree_view));
 194 	}
 195 }
 196 
 197 static void
 198 set_drag_dest_row (NautilusTreeViewDragDest *dest,
 199 		   GtkTreePath *path)
 200 {
 201 	if (path) {
 202 		set_widget_highlight (dest, FALSE);
 203 		gtk_tree_view_set_drag_dest_row
 204 			(dest->details->tree_view,
 205 			 path,
 206 			 GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
 207 	} else {
 208 		set_widget_highlight (dest, TRUE);
 209 		gtk_tree_view_set_drag_dest_row (dest->details->tree_view, 
 210 						 NULL, 
 211 						 0);
 212 	}
 213 }
 214 
 215 static void
 216 clear_drag_dest_row (NautilusTreeViewDragDest *dest)
 217 {
 218 	gtk_tree_view_set_drag_dest_row (dest->details->tree_view, NULL, 0);
 219 	set_widget_highlight (dest, FALSE);
 220 }
 221 
 222 static gboolean
 223 get_drag_data (NautilusTreeViewDragDest *dest,
 224 	       GdkDragContext *context, 
 225 	       guint32 time)
 226 {
 227 	GdkAtom target;
 228 	
 229 	target = gtk_drag_dest_find_target (GTK_WIDGET (dest->details->tree_view), 
 230 					    context, 
 231 					    NULL);
 232 
 233 	if (target == GDK_NONE) {
 234 		return FALSE;
 235 	}
 236 
 237 	if (target == gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE) &&
 238 	    !dest->details->drop_occurred) {
 239 		dest->details->drag_type = NAUTILUS_ICON_DND_XDNDDIRECTSAVE;
 240 		dest->details->have_drag_data = TRUE;
 241 		return TRUE;
 242 	}
 243 
 244 	gtk_drag_get_data (GTK_WIDGET (dest->details->tree_view),
 245 			   context, target, time);
 246 
 247 	return TRUE;
 248 }
 249 
 250 static void
 251 free_drag_data (NautilusTreeViewDragDest *dest)
 252 {
 253 	dest->details->have_drag_data = FALSE;
 254 
 255 	if (dest->details->drag_data) {
 256 		gtk_selection_data_free (dest->details->drag_data);
 257 		dest->details->drag_data = NULL;
 258 	}
 259 
 260 	if (dest->details->drag_list) {
 261 		nautilus_drag_destroy_selection_list (dest->details->drag_list);
 262 		dest->details->drag_list = NULL;
 263 	}
 264 
 265 	g_free (dest->details->direct_save_uri);
 266 	dest->details->direct_save_uri = NULL;
 267 }
 268 
 269 static char *
 270 get_root_uri (NautilusTreeViewDragDest *dest)
 271 {
 272 	char *uri;
 273 	
 274 	g_signal_emit (dest, signals[GET_ROOT_URI], 0, &uri);
 275 	
 276 	return uri;
 277 }
 278 
 279 static NautilusFile *
 280 file_for_path (NautilusTreeViewDragDest *dest, GtkTreePath *path)
 281 {
 282 	NautilusFile *file;
 283 	char *uri;
 284 	
 285 	if (path) {
 286 		g_signal_emit (dest, signals[GET_FILE_FOR_PATH], 0, path, &file);
 287 	} else {
 288 		uri = get_root_uri (dest);
 289 
 290 		file = NULL;
 291 		if (uri != NULL) {
 292 			file = nautilus_file_get_by_uri (uri);
 293 		}
 294 		
 295 		g_free (uri);
 296 	}
 297 	
 298 	return file;
 299 }
 300 
 301 static GtkTreePath *
 302 get_drop_path (NautilusTreeViewDragDest *dest,
 303 	       GtkTreePath *path)
 304 {
 305 	NautilusFile *file;
 306 	GtkTreePath *ret;
 307 	
 308 	if (!path || !dest->details->have_drag_data) {
 309 		return NULL;
 310 	}
 311 
 312 	ret = gtk_tree_path_copy (path);
 313 	file = file_for_path (dest, ret);
 314 
 315 	/* Go up the tree until we find a file that can accept a drop */
 316 	while (file == NULL /* dummy row */ ||
 317 	       !nautilus_drag_can_accept_info (file,
 318 		       			       dest->details->drag_type,
 319 					       dest->details->drag_list)) {
 320 		if (gtk_tree_path_get_depth (ret) == 1) {
 321 			gtk_tree_path_free (ret);
 322 			ret = NULL;
 323 			break;
 324 		} else {
 325 			gtk_tree_path_up (ret);
 326 
 327 			nautilus_file_unref (file);
 328 			file = file_for_path (dest, ret);
 329 		}
 330 	}
 331 	nautilus_file_unref (file);
 332 	
 333 	return ret;
 334 }
 335 
 336 static char *
 337 get_drop_target_uri_for_path (NautilusTreeViewDragDest *dest,
 338 			      GtkTreePath *path)
 339 {
 340 	NautilusFile *file;
 341 	char *target = NULL;
 342 	gboolean can;
 343 
 344 	file = file_for_path (dest, path);
 345 	if (file == NULL) {
 346 		return NULL;
 347 	}
 348 	can = nautilus_drag_can_accept_info (file,
 349 					     dest->details->drag_type,
 350 					     dest->details->drag_list);
 351 	if (can) {
 352 		target = nautilus_file_get_drop_target_uri (file);
 353 	}
 354 	nautilus_file_unref (file);
 355 	
 356 	return target;
 357 }
 358 
 359 static guint
 360 get_drop_action (NautilusTreeViewDragDest *dest, 
 361 		 GdkDragContext *context,
 362 		 GtkTreePath *path)
 363 {
 364 	char *drop_target;
 365 	int action;
 366 	
 367 	if (!dest->details->have_drag_data ||
 368 	    (dest->details->drag_type == NAUTILUS_ICON_DND_GNOME_ICON_LIST &&
 369 	     dest->details->drag_list == NULL)) {
 370 		return 0;
 371 	}
 372 
 373 	drop_target = get_drop_target_uri_for_path (dest, path);
 374 	if (drop_target == NULL) {
 375 		return 0;
 376 	}
 377 
 378 	action = 0;
 379 	switch (dest->details->drag_type) {
 380 	case NAUTILUS_ICON_DND_GNOME_ICON_LIST :
 381 		nautilus_drag_default_drop_action_for_icons
 382 			(context,
 383 			 drop_target,
 384 			 dest->details->drag_list,
 385 			 &action);
 386 		break;
 387 	case NAUTILUS_ICON_DND_NETSCAPE_URL:
 388 		action = nautilus_drag_default_drop_action_for_netscape_url (context);
 389 		break;
 390 	case NAUTILUS_ICON_DND_URI_LIST :
 391 		action = gdk_drag_context_get_suggested_action (context);
 392 		break;
 393 	case NAUTILUS_ICON_DND_TEXT:
 394 	case NAUTILUS_ICON_DND_RAW:
 395 	case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
 396 		action = GDK_ACTION_COPY;
 397 		break;
 398 	}
 399 
 400 	g_free (drop_target);
 401 
 402 	return action;
 403 }
 404 
 405 static gboolean
 406 drag_motion_callback (GtkWidget *widget,
 407 		      GdkDragContext *context,
 408 		      int x,
 409 		      int y,
 410 		      guint32 time,
 411 		      gpointer data)
 412 {
 413 	NautilusTreeViewDragDest *dest;
 414 	GtkTreePath *path;
 415 	GtkTreePath *drop_path, *old_drop_path;
 416 	GtkTreeViewDropPosition pos;
 417 	GdkWindow *bin_window;
 418 	guint action;
 419 	gboolean res = TRUE;
 420 
 421 	dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data);
 422 
 423 	gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
 424 					   x, y, &path, &pos);
 425 	if (pos == GTK_TREE_VIEW_DROP_BEFORE ||
 426 	    pos == GTK_TREE_VIEW_DROP_AFTER) {
 427 		gtk_tree_path_free (path);
 428 		path = NULL;
 429 	}
 430 
 431 	if (!dest->details->have_drag_data) {
 432 		res = get_drag_data (dest, context, time);
 433 	}
 434 
 435 	if (!res) {
 436 		return FALSE;
 437 	}
 438 
 439 	drop_path = get_drop_path (dest, path);
 440 	
 441 	action = 0;
 442 	bin_window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget));
 443 	if (bin_window != NULL) {
 444 		int bin_x, bin_y;
 445 		gdk_window_get_position (bin_window, &bin_x, &bin_y);
 446 		if (bin_y <= y) {
 447 			/* ignore drags on the header */
 448 			action = get_drop_action (dest, context, drop_path);
 449 		}
 450 	}
 451 
 452 	gtk_tree_view_get_drag_dest_row (GTK_TREE_VIEW (widget), &old_drop_path,
 453 					 NULL);
 454 	
 455 	if (action) {
 456 		set_drag_dest_row (dest, drop_path);
 457 	} else {
 458 		clear_drag_dest_row (dest);
 459 	}
 460 	
 461 	if (path) {
 462 		gtk_tree_path_free (path);
 463 	}
 464 	
 465 	if (drop_path) {
 466 		gtk_tree_path_free (drop_path);
 467 	}
 468 	
 469 	if (old_drop_path) {
 470 		gtk_tree_path_free (old_drop_path);
 471 	}
 472 	
 473 	if (dest->details->scroll_id == 0) {
 474 		dest->details->scroll_id = 
 475 			g_timeout_add (150, 
 476 				       scroll_timeout, 
 477 				       dest->details->tree_view);
 478 	}
 479 
 480 	gdk_drag_status (context, action, time);
 481 
 482 	return TRUE;
 483 }
 484 
 485 static void
 486 drag_leave_callback (GtkWidget *widget,
 487 		     GdkDragContext *context,
 488 		     guint32 time,
 489 		     gpointer data)
 490 {
 491 	NautilusTreeViewDragDest *dest;
 492 
 493 	dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data);
 494 
 495 	clear_drag_dest_row (dest);
 496 
 497 	free_drag_data (dest);
 498 
 499 	remove_scroll_timeout (dest);
 500 }
 501 
 502 static char *
 503 get_drop_target_uri_at_pos (NautilusTreeViewDragDest *dest, int x, int y)
 504 {
 505 	char *drop_target = NULL;
 506 	GtkTreePath *path;
 507 	GtkTreePath *drop_path;
 508 	GtkTreeViewDropPosition pos;
 509 
 510 	gtk_tree_view_get_dest_row_at_pos (dest->details->tree_view, x, y,
 511 					   &path, &pos);
 512 	if (pos == GTK_TREE_VIEW_DROP_BEFORE ||
 513 	    pos == GTK_TREE_VIEW_DROP_AFTER) {
 514 		gtk_tree_path_free (path);
 515 		path = NULL;
 516 	}
 517 
 518 	drop_path = get_drop_path (dest, path);
 519 
 520 	drop_target = get_drop_target_uri_for_path (dest, drop_path);
 521 
 522 	if (path != NULL) {
 523 		gtk_tree_path_free (path);
 524 	}
 525 
 526 	if (drop_path != NULL) {
 527 		gtk_tree_path_free (drop_path);
 528 	}
 529 
 530 	return drop_target;
 531 }
 532 
 533 static void
 534 receive_uris (NautilusTreeViewDragDest *dest,
 535 	      GdkDragContext *context,
 536 	      GList *source_uris,
 537 	      int x, int y)
 538 {
 539 	char *drop_target;
 540 	GdkDragAction action, real_action;
 541 
 542 	drop_target = get_drop_target_uri_at_pos (dest, x, y);
 543 	g_assert (drop_target != NULL);
 544 
 545 	real_action = gdk_drag_context_get_selected_action (context);
 546 
 547 	if (real_action == GDK_ACTION_ASK) {
 548 		if (nautilus_drag_selection_includes_special_link (dest->details->drag_list)) {
 549 			/* We only want to move the trash */
 550 			action = GDK_ACTION_MOVE;
 551 		} else {
 552 			action = GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK;
 553 		}
 554 		real_action = nautilus_drag_drop_action_ask
 555 			(GTK_WIDGET (dest->details->tree_view), action);
 556 	}
 557 
 558 	/* We only want to copy external uris */
 559 	if (dest->details->drag_type == NAUTILUS_ICON_DND_URI_LIST) {
 560 		real_action = GDK_ACTION_COPY;
 561 	}
 562 
 563 	if (real_action > 0) {
 564 		if (!nautilus_drag_uris_local (drop_target, source_uris)
 565 			|| real_action != GDK_ACTION_MOVE) {
 566 			g_signal_emit (dest, signals[MOVE_COPY_ITEMS], 0,
 567 				       source_uris, 
 568 				       drop_target,
 569 				       real_action,
 570 				       x, y);
 571 		}
 572 	}
 573 
 574 	g_free (drop_target);
 575 }
 576 
 577 static void
 578 receive_dropped_icons (NautilusTreeViewDragDest *dest,
 579 		       GdkDragContext *context,
 580 		       int x, int y)
 581 {
 582 	GList *source_uris;
 583 	GList *l;
 584 
 585 	/* FIXME: ignore local only moves */
 586 
 587 	if (!dest->details->drag_list) {
 588 		return;
 589 	}
 590 	
 591 	source_uris = NULL;
 592 	for (l = dest->details->drag_list; l != NULL; l = l->next) {
 593 		source_uris = g_list_prepend (source_uris,
 594 					      ((NautilusDragSelectionItem *)l->data)->uri);
 595 	}
 596 
 597 	source_uris = g_list_reverse (source_uris);
 598 
 599 	receive_uris (dest, context, source_uris, x, y);
 600 	
 601 	g_list_free (source_uris);
 602 }
 603 
 604 static void
 605 receive_dropped_uri_list (NautilusTreeViewDragDest *dest,
 606 			  GdkDragContext *context,
 607 			  int x, int y)
 608 {
 609 	char *drop_target;
 610 
 611 	if (!dest->details->drag_data) {
 612 		return;
 613 	}
 614 
 615 	drop_target = get_drop_target_uri_at_pos (dest, x, y);
 616 	g_assert (drop_target != NULL);
 617 
 618 	g_signal_emit (dest, signals[HANDLE_URI_LIST], 0,
 619 		       (char*) gtk_selection_data_get_data (dest->details->drag_data),
 620 		       drop_target,
 621 		       gdk_drag_context_get_selected_action (context),
 622 		       x, y);
 623 
 624 	g_free (drop_target);
 625 }
 626 
 627 static void
 628 receive_dropped_text (NautilusTreeViewDragDest *dest,
 629 		      GdkDragContext *context,
 630 		      int x, int y)
 631 {
 632 	char *drop_target;
 633 	guchar *text;
 634 
 635 	if (!dest->details->drag_data) {
 636 		return;
 637 	}
 638 
 639 	drop_target = get_drop_target_uri_at_pos (dest, x, y);
 640 	g_assert (drop_target != NULL);
 641 
 642 	text = gtk_selection_data_get_text (dest->details->drag_data);
 643 	g_signal_emit (dest, signals[HANDLE_TEXT], 0,
 644 		       (char *) text, drop_target,
 645 		       gdk_drag_context_get_selected_action (context),
 646 		       x, y);
 647 
 648 	g_free (text);
 649 	g_free (drop_target);
 650 }
 651 
 652 static void
 653 receive_dropped_raw (NautilusTreeViewDragDest *dest,
 654 		      const char *raw_data, int length,
 655 		      GdkDragContext *context,
 656 		      int x, int y)
 657 {
 658 	char *drop_target;
 659 
 660 	if (!dest->details->drag_data) {
 661 		return;
 662 	}
 663 
 664 	drop_target = get_drop_target_uri_at_pos (dest, x, y);
 665 	g_assert (drop_target != NULL);
 666 
 667 	g_signal_emit (dest, signals[HANDLE_RAW], 0,
 668 		       raw_data, length, drop_target,
 669 		       dest->details->direct_save_uri,
 670 		       gdk_drag_context_get_selected_action (context),
 671 		       x, y);
 672 
 673 	g_free (drop_target);
 674 }
 675 
 676 static void
 677 receive_dropped_netscape_url (NautilusTreeViewDragDest *dest,
 678 			      GdkDragContext *context,
 679 			      int x, int y)
 680 {
 681 	char *drop_target;
 682 
 683 	if (!dest->details->drag_data) {
 684 		return;
 685 	}
 686 
 687 	drop_target = get_drop_target_uri_at_pos (dest, x, y);
 688 	g_assert (drop_target != NULL);
 689 
 690 	g_signal_emit (dest, signals[HANDLE_NETSCAPE_URL], 0,
 691 		       (char*) gtk_selection_data_get_data (dest->details->drag_data),
 692 		       drop_target,
 693 		       gdk_drag_context_get_selected_action (context),
 694 		       x, y);
 695 
 696 	g_free (drop_target);
 697 }
 698 
 699 static gboolean
 700 receive_xds (NautilusTreeViewDragDest *dest,
 701 	     GtkWidget *widget,
 702 	     guint32 time,
 703 	     GdkDragContext *context,
 704 	     int x, int y)
 705 {
 706 	GFile *location;
 707 	const guchar *selection_data;
 708 	gint selection_format;
 709 	gint selection_length;
 710 
 711 	selection_data = gtk_selection_data_get_data (dest->details->drag_data);
 712 	selection_format = gtk_selection_data_get_format (dest->details->drag_data);
 713 	selection_length = gtk_selection_data_get_length (dest->details->drag_data);
 714 
 715 	if (selection_format == 8 
 716 	    && selection_length == 1 
 717 	    && selection_data[0] == 'F') {
 718 		gtk_drag_get_data (widget, context,
 719 		                  gdk_atom_intern (NAUTILUS_ICON_DND_RAW_TYPE,
 720 		                                  FALSE),
 721 		                  time);
 722 		return FALSE;
 723 	} else if (selection_format == 8 
 724 		   && selection_length == 1 
 725 		   && selection_data[0] == 'S') {
 726 		g_assert (dest->details->direct_save_uri != NULL);
 727 		location = g_file_new_for_uri (dest->details->direct_save_uri);
 728 
 729 		nautilus_file_changes_queue_file_added (location);
 730 		nautilus_file_changes_consume_changes (TRUE);
 731 
 732 		g_object_unref (location);
 733 	}
 734 	return TRUE;
 735 }
 736 
 737 
 738 static gboolean
 739 drag_data_received_callback (GtkWidget *widget,
 740 			     GdkDragContext *context,
 741 			     int x,
 742 			     int y,
 743 			     GtkSelectionData *selection_data,
 744 			     guint info,
 745 			     guint32 time,
 746 			     gpointer data)
 747 {
 748 	NautilusTreeViewDragDest *dest;
 749 	const gchar *tmp;
 750 	int length;
 751 	gboolean success, finished;
 752 	
 753 	dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data);
 754 
 755 	if (!dest->details->have_drag_data) {
 756 		dest->details->have_drag_data = TRUE;
 757 		dest->details->drag_type = info;
 758 		dest->details->drag_data = 
 759 			gtk_selection_data_copy (selection_data);
 760 		if (info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) {
 761 			dest->details->drag_list = 
 762 				nautilus_drag_build_selection_list (selection_data);
 763 		}
 764 	}
 765 
 766 	if (dest->details->drop_occurred) {
 767 		success = FALSE;
 768 		finished = TRUE;
 769 		switch (info) {
 770 		case NAUTILUS_ICON_DND_GNOME_ICON_LIST :
 771 			receive_dropped_icons (dest, context, x, y);
 772 			success = TRUE;
 773 			break;
 774 		case NAUTILUS_ICON_DND_NETSCAPE_URL :
 775 			receive_dropped_netscape_url (dest, context, x, y);
 776 			success = TRUE;
 777 			break;
 778 		case NAUTILUS_ICON_DND_URI_LIST :
 779 			receive_dropped_uri_list (dest, context, x, y);
 780 			success = TRUE;
 781 			break;
 782 		case NAUTILUS_ICON_DND_TEXT:
 783 			receive_dropped_text (dest, context, x, y);
 784 			success = TRUE;
 785 			break;
 786 		case NAUTILUS_ICON_DND_RAW:
 787 			length = gtk_selection_data_get_length (selection_data);
 788 			tmp = (const gchar *) gtk_selection_data_get_data (selection_data);
 789 			receive_dropped_raw (dest, tmp, length, context, x, y);
 790 			success = TRUE;
 791 			break;
 792 		case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
 793 			finished = receive_xds (dest, widget, time, context, x, y);
 794 			success = TRUE;
 795 			break;
 796 		}
 797 
 798 		if (finished) {
 799 			dest->details->drop_occurred = FALSE;
 800 			free_drag_data (dest);
 801 			gtk_drag_finish (context, success, FALSE, time);
 802 		}
 803 	}
 804 
 805 	/* appease GtkTreeView by preventing its drag_data_receive
 806 	 * from being called */
 807 	g_signal_stop_emission_by_name (dest->details->tree_view,
 808 					"drag_data_received");
 809 
 810 	return TRUE;
 811 }
 812 
 813 static char *
 814 get_direct_save_filename (GdkDragContext *context)
 815 {
 816 	guchar *prop_text;
 817 	gint prop_len;
 818 
 819 	if (!gdk_property_get (gdk_drag_context_get_source_window (context), gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE),
 820 			       gdk_atom_intern ("text/plain", FALSE), 0, 1024, FALSE, NULL, NULL,
 821 			       &prop_len, &prop_text)) {
 822 		return NULL;
 823 	}
 824 
 825 	/* Zero-terminate the string */
 826 	prop_text = g_realloc (prop_text, prop_len + 1);
 827 	prop_text[prop_len] = '\0';
 828 
 829 	/* Verify that the file name provided by the source is valid */
 830 	if (*prop_text == '\0' ||
 831 	    strchr ((const gchar *) prop_text, G_DIR_SEPARATOR) != NULL) {
 832 		DEBUG ("Invalid filename provided by XDS drag site");
 833 		g_free (prop_text);
 834 		return NULL;
 835 	}
 836 
 837 	return (gchar *) prop_text;
 838 }
 839 
 840 static gboolean
 841 set_direct_save_uri (NautilusTreeViewDragDest *dest,
 842 		     GdkDragContext *context,
 843 		     int x, int y)
 844 {
 845 	GFile *base, *child;
 846 	char *drop_uri;
 847 	char *filename, *uri;
 848 
 849 	g_assert (dest->details->direct_save_uri == NULL);
 850 
 851 	uri = NULL;
 852 
 853 	drop_uri = get_drop_target_uri_at_pos (dest, x, y);
 854 	if (drop_uri != NULL) {
 855 		filename = get_direct_save_filename (context);
 856 		if (filename != NULL) {
 857 			/* Resolve relative path */
 858 			base = g_file_new_for_uri (drop_uri);
 859 			child = g_file_get_child (base, filename);
 860 			uri = g_file_get_uri (child);
 861 
 862 			g_object_unref (base);
 863 			g_object_unref (child);
 864 
 865 			/* Change the property */
 866 			gdk_property_change (gdk_drag_context_get_source_window (context),
 867 					     gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE),
 868 					     gdk_atom_intern ("text/plain", FALSE), 8,
 869 					     GDK_PROP_MODE_REPLACE, (const guchar *) uri,
 870 					     strlen (uri));
 871 
 872 			dest->details->direct_save_uri = uri;
 873 		} else {
 874 			DEBUG ("Invalid filename provided by XDS drag site");
 875 		}
 876 	} else {
 877 		DEBUG ("Could not retrieve XDS drop destination");
 878 	}
 879 
 880 	return uri != NULL;
 881 }
 882 
 883 static gboolean
 884 drag_drop_callback (GtkWidget *widget,
 885 		    GdkDragContext *context,
 886 		    int x, 
 887 		    int y,
 888 		    guint32 time,
 889 		    gpointer data)
 890 {
 891 	NautilusTreeViewDragDest *dest;
 892 	guint info;
 893 	GdkAtom target;
 894 
 895 	dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data);
 896 	
 897 	target = gtk_drag_dest_find_target (GTK_WIDGET (dest->details->tree_view), 
 898 					    context, 
 899 					    NULL);
 900 	if (target == GDK_NONE) {
 901 		return FALSE;
 902 	}
 903 
 904 	info = dest->details->drag_type;
 905 
 906 	if (info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE) {
 907 		/* We need to set this or get_drop_path will fail, and it
 908 		   was unset by drag_leave_callback */
 909 		dest->details->have_drag_data = TRUE;
 910 		if (!set_direct_save_uri (dest, context, x, y)) {
 911 			return FALSE;
 912 		}
 913 		dest->details->have_drag_data = FALSE;
 914 	}
 915 
 916 	dest->details->drop_occurred = TRUE;
 917 
 918 	get_drag_data (dest, context, time);
 919 	remove_scroll_timeout (dest);
 920 	clear_drag_dest_row (dest);
 921 	
 922 	return TRUE;
 923 }
 924 
 925 static void
 926 tree_view_weak_notify (gpointer user_data,
 927 		       GObject *object)
 928 {
 929 	NautilusTreeViewDragDest *dest;
 930 
 931 	dest = NAUTILUS_TREE_VIEW_DRAG_DEST (user_data);
 932 	
 933 	remove_scroll_timeout (dest);
 934 
 935 	dest->details->tree_view = NULL;
 936 }
 937 
 938 static void
 939 nautilus_tree_view_drag_dest_dispose (GObject *object)
 940 {
 941 	NautilusTreeViewDragDest *dest;
 942 	
 943 	dest = NAUTILUS_TREE_VIEW_DRAG_DEST (object);
 944 
 945 	if (dest->details->tree_view) {
 946 		g_object_weak_unref (G_OBJECT (dest->details->tree_view),
 947 				     tree_view_weak_notify,
 948 				     dest);
 949 	}
 950 	
 951 	remove_scroll_timeout (dest);
 952 
 953 	G_OBJECT_CLASS (nautilus_tree_view_drag_dest_parent_class)->dispose (object);
 954 }
 955 
 956 static void
 957 nautilus_tree_view_drag_dest_finalize (GObject *object)
 958 {
 959 	NautilusTreeViewDragDest *dest;
 960 	
 961 	dest = NAUTILUS_TREE_VIEW_DRAG_DEST (object);
 962 	free_drag_data (dest);
 963 
 964 	G_OBJECT_CLASS (nautilus_tree_view_drag_dest_parent_class)->finalize (object);
 965 }
 966 
 967 static void
 968 nautilus_tree_view_drag_dest_init (NautilusTreeViewDragDest *dest)
 969 {
 970 	dest->details = G_TYPE_INSTANCE_GET_PRIVATE (dest, NAUTILUS_TYPE_TREE_VIEW_DRAG_DEST,
 971 						     NautilusTreeViewDragDestDetails);
 972 }
 973 
 974 static void
 975 nautilus_tree_view_drag_dest_class_init (NautilusTreeViewDragDestClass *class)
 976 {
 977 	GObjectClass *gobject_class;
 978 
 979 	gobject_class = G_OBJECT_CLASS (class);
 980 	
 981 	gobject_class->dispose = nautilus_tree_view_drag_dest_dispose;
 982 	gobject_class->finalize = nautilus_tree_view_drag_dest_finalize;
 983 
 984 	g_type_class_add_private (class, sizeof (NautilusTreeViewDragDestDetails));
 985 
 986 	signals[GET_ROOT_URI] = 
 987 		g_signal_new ("get_root_uri",
 988 			      G_TYPE_FROM_CLASS (class),
 989 			      G_SIGNAL_RUN_LAST,
 990 			      G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
 991 					       get_root_uri),
 992 			      NULL, NULL,
 993 			      g_cclosure_marshal_generic,
 994 			      G_TYPE_STRING, 0);
 995 	signals[GET_FILE_FOR_PATH] = 
 996 		g_signal_new ("get_file_for_path",
 997 			      G_TYPE_FROM_CLASS (class),
 998 			      G_SIGNAL_RUN_LAST,
 999 			      G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
1000 					       get_file_for_path),
1001 			      NULL, NULL,
1002 			      g_cclosure_marshal_generic,
1003 			      NAUTILUS_TYPE_FILE, 1,
1004 			      GTK_TYPE_TREE_PATH);
1005 	signals[MOVE_COPY_ITEMS] =
1006 		g_signal_new ("move_copy_items",
1007 			      G_TYPE_FROM_CLASS (class),
1008 			      G_SIGNAL_RUN_LAST,
1009 			      G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
1010 					       move_copy_items),
1011 			      NULL, NULL,
1012 			      
1013 			      g_cclosure_marshal_generic,
1014 			      G_TYPE_NONE, 5,
1015 			      G_TYPE_POINTER,
1016 			      G_TYPE_STRING,
1017 			      GDK_TYPE_DRAG_ACTION,
1018 			      G_TYPE_INT,
1019 			      G_TYPE_INT);
1020 	signals[HANDLE_NETSCAPE_URL] =
1021 		g_signal_new ("handle_netscape_url",
1022 			      G_TYPE_FROM_CLASS (class),
1023 			      G_SIGNAL_RUN_LAST,
1024 			      G_STRUCT_OFFSET (NautilusTreeViewDragDestClass, 
1025 					       handle_netscape_url),
1026 			      NULL, NULL,
1027 			      g_cclosure_marshal_generic,
1028 			      G_TYPE_NONE, 5,
1029 			      G_TYPE_STRING,
1030 			      G_TYPE_STRING,
1031 			      GDK_TYPE_DRAG_ACTION,
1032 			      G_TYPE_INT,
1033 			      G_TYPE_INT);
1034 	signals[HANDLE_URI_LIST] =
1035 		g_signal_new ("handle_uri_list",
1036 			      G_TYPE_FROM_CLASS (class),
1037 			      G_SIGNAL_RUN_LAST,
1038 			      G_STRUCT_OFFSET (NautilusTreeViewDragDestClass, 
1039 					       handle_uri_list),
1040 			      NULL, NULL,
1041 			      g_cclosure_marshal_generic,
1042 			      G_TYPE_NONE, 5,
1043 			      G_TYPE_STRING,
1044 			      G_TYPE_STRING,
1045 			      GDK_TYPE_DRAG_ACTION,
1046 			      G_TYPE_INT,
1047 			      G_TYPE_INT);
1048 	signals[HANDLE_TEXT] =
1049 		g_signal_new ("handle_text",
1050 			      G_TYPE_FROM_CLASS (class),
1051 			      G_SIGNAL_RUN_LAST,
1052 			      G_STRUCT_OFFSET (NautilusTreeViewDragDestClass, 
1053 					       handle_text),
1054 			      NULL, NULL,
1055 			      g_cclosure_marshal_generic,
1056 			      G_TYPE_NONE, 5,
1057 			      G_TYPE_STRING,
1058 			      G_TYPE_STRING,
1059 			      GDK_TYPE_DRAG_ACTION,
1060 			      G_TYPE_INT,
1061 			      G_TYPE_INT);
1062 	signals[HANDLE_RAW] =
1063 		g_signal_new ("handle_raw",
1064 			      G_TYPE_FROM_CLASS (class),
1065 			      G_SIGNAL_RUN_LAST,
1066 			      G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
1067 					       handle_raw),
1068 			      NULL, NULL,
1069 			      g_cclosure_marshal_generic,
1070 			      G_TYPE_NONE, 7,
1071 			      G_TYPE_POINTER,
1072 			      G_TYPE_INT,
1073 			      G_TYPE_STRING,
1074 			      G_TYPE_STRING,
1075 			      GDK_TYPE_DRAG_ACTION,
1076 			      G_TYPE_INT,
1077 			      G_TYPE_INT);
1078 }
1079 
1080 
1081 
1082 NautilusTreeViewDragDest *
1083 nautilus_tree_view_drag_dest_new (GtkTreeView *tree_view)
1084 {
1085 	NautilusTreeViewDragDest *dest;
1086 	GtkTargetList *targets;
1087 	
1088 	dest = g_object_new (NAUTILUS_TYPE_TREE_VIEW_DRAG_DEST, NULL);
1089 
1090 	dest->details->tree_view = tree_view;
1091 	g_object_weak_ref (G_OBJECT (dest->details->tree_view),
1092 			   tree_view_weak_notify, dest);
1093 	
1094 	gtk_drag_dest_set (GTK_WIDGET (tree_view),
1095 			   0, drag_types, G_N_ELEMENTS (drag_types),
1096 			   GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK);
1097 
1098 	targets = gtk_drag_dest_get_target_list (GTK_WIDGET (tree_view));
1099 	gtk_target_list_add_text_targets (targets, NAUTILUS_ICON_DND_TEXT);
1100 
1101 	g_signal_connect_object (tree_view,
1102 				 "drag_motion",
1103 				 G_CALLBACK (drag_motion_callback),
1104 				 dest, 0);
1105 	g_signal_connect_object (tree_view,
1106 				 "drag_leave",
1107 				 G_CALLBACK (drag_leave_callback),
1108 				 dest, 0);
1109 	g_signal_connect_object (tree_view,
1110 				 "drag_drop",
1111 				 G_CALLBACK (drag_drop_callback),
1112 				 dest, 0);
1113 	g_signal_connect_object (tree_view, 
1114 				 "drag_data_received",
1115 				 G_CALLBACK (drag_data_received_callback),
1116 				 dest, 0);
1117 	
1118 	return dest;
1119 }