nautilus-3.6.3/libnautilus-private/nautilus-canvas-dnd.c

No issues found

   1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
   2 
   3 /* nautilus-canvas-dnd.c - Drag & drop handling for the canvas container widget.
   4 
   5    Copyright (C) 1999, 2000 Free Software Foundation
   6    Copyright (C) 2000 Eazel, Inc.
   7    
   8    The Gnome Library is free software; you can redistribute it and/or
   9    modify it under the terms of the GNU Library 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    The Gnome Library is distributed in the hope that it will be useful,
  14    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16    Library General Public License for more details.
  17 
  18    You should have received a copy of the GNU Library General Public
  19    License along with the Gnome Library; see the file COPYING.LIB.  If not,
  20    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  21    Boston, MA 02111-1307, USA.
  22 
  23    Authors: Ettore Perazzoli <ettore@gnu.org>,
  24             Darin Adler <darin@bentspoon.com>,
  25 	    Andy Hertzfeld <andy@eazel.com>
  26 	    Pavel Cisler <pavel@eazel.com>
  27 	    
  28 
  29    XDS support: Benedikt Meurer <benny@xfce.org> (adapted by Amos Brocco <amos.brocco@unifr.ch>)
  30 				
  31 */
  32 
  33 
  34 #include <config.h>
  35 #include <math.h>
  36 #include "nautilus-canvas-dnd.h"
  37 
  38 #include "nautilus-file-dnd.h"
  39 #include "nautilus-canvas-private.h"
  40 #include "nautilus-link.h"
  41 #include "nautilus-metadata.h"
  42 #include "nautilus-selection-canvas-item.h"
  43 #include <eel/eel-glib-extensions.h>
  44 #include <eel/eel-gnome-extensions.h>
  45 #include <eel/eel-graphic-effects.h>
  46 #include <eel/eel-gtk-extensions.h>
  47 #include <eel/eel-stock-dialogs.h>
  48 #include <eel/eel-string.h>
  49 #include <eel/eel-vfs-extensions.h>
  50 #include <gdk/gdkkeysyms.h>
  51 #include <gdk/gdkx.h>
  52 #include <gtk/gtk.h>
  53 #include <glib/gi18n.h>
  54 
  55 #include <libnautilus-private/nautilus-desktop-background.h>
  56 #include <libnautilus-private/nautilus-file-utilities.h>
  57 #include <libnautilus-private/nautilus-file-changes-queue.h>
  58 #include <stdio.h>
  59 #include <string.h>
  60 
  61 #define DEBUG_FLAG NAUTILUS_DEBUG_CANVAS_CONTAINER
  62 #include "nautilus-debug.h"
  63 
  64 static const GtkTargetEntry drag_types [] = {
  65 	{ NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
  66 	{ NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
  67 };
  68 
  69 static const GtkTargetEntry drop_types [] = {
  70 	{ NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
  71 	/* prefer "_NETSCAPE_URL" over "text/uri-list" to satisfy web browsers. */
  72 	{ NAUTILUS_ICON_DND_NETSCAPE_URL_TYPE, 0, NAUTILUS_ICON_DND_NETSCAPE_URL },
  73 	{ NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
  74 	{ NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, 0, NAUTILUS_ICON_DND_XDNDDIRECTSAVE }, /* XDS Protocol Type */
  75 	{ NAUTILUS_ICON_DND_RAW_TYPE, 0, NAUTILUS_ICON_DND_RAW },
  76 	/* Must be last: */
  77 	{ NAUTILUS_ICON_DND_ROOTWINDOW_DROP_TYPE,  0, NAUTILUS_ICON_DND_ROOTWINDOW_DROP }
  78 };
  79 static void     stop_dnd_highlight         (GtkWidget      *widget);
  80 static void     dnd_highlight_queue_redraw (GtkWidget      *widget);
  81 
  82 static GtkTargetList *drop_types_list = NULL;
  83 static GtkTargetList *drop_types_list_root = NULL;
  84 
  85 static char * nautilus_canvas_container_find_drop_target (NautilusCanvasContainer *container,
  86 							GdkDragContext *context,
  87 							int x, int y, gboolean *icon_hit,
  88 							gboolean rewrite_desktop);
  89 
  90 static EelCanvasItem *
  91 create_selection_shadow (NautilusCanvasContainer *container,
  92 			 GList *list)
  93 {
  94 	EelCanvasGroup *group;
  95 	EelCanvas *canvas;
  96 	int max_x, max_y;
  97 	int min_x, min_y;
  98 	GList *p;
  99 	GtkAllocation allocation;
 100 
 101 	if (list == NULL) {
 102 		return NULL;
 103 	}
 104 
 105 	/* if we're only dragging a single item, don't worry about the shadow */
 106 	if (list->next == NULL) {
 107 		return NULL;
 108 	}
 109 		
 110 	canvas = EEL_CANVAS (container);
 111 	gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 112 
 113 	/* Creating a big set of rectangles in the canvas can be expensive, so
 114            we try to be smart and only create the maximum number of rectangles
 115            that we will need, in the vertical/horizontal directions.  */
 116 
 117 	max_x = allocation.width;
 118 	min_x = -max_x;
 119 
 120 	max_y = allocation.height;
 121 	min_y = -max_y;
 122 
 123 	/* Create a group, so that it's easier to move all the items around at
 124            once.  */
 125 	group = EEL_CANVAS_GROUP
 126 		(eel_canvas_item_new (EEL_CANVAS_GROUP (canvas->root),
 127 					eel_canvas_group_get_type (),
 128 					NULL));
 129 	
 130 	for (p = list; p != NULL; p = p->next) {
 131 		NautilusDragSelectionItem *item;
 132 		int x1, y1, x2, y2;
 133 		GdkRGBA black = { 0, 0, 0, 1 };
 134 
 135 		item = p->data;
 136 
 137 		if (!item->got_icon_position) {
 138 			continue;
 139 		}
 140 
 141 		x1 = item->icon_x;
 142 		y1 = item->icon_y;
 143 		x2 = x1 + item->icon_width;
 144 		y2 = y1 + item->icon_height;
 145 			
 146 		if (x2 >= min_x && x1 <= max_x && y2 >= min_y && y1 <= max_y)
 147 			eel_canvas_item_new
 148 				(group,
 149 				 NAUTILUS_TYPE_SELECTION_CANVAS_ITEM,
 150 				 "x1", (double) x1,
 151 				 "y1", (double) y1,
 152 				 "x2", (double) x2,
 153 				 "y2", (double) y2,
 154 				 "outline-color-rgba", &black,
 155 				 "outline-stippling", TRUE,
 156 				 "width_pixels", 1,
 157 				 NULL);
 158 	}
 159 
 160 	return EEL_CANVAS_ITEM (group);
 161 }
 162 
 163 /* Set the affine instead of the x and y position.
 164  * Simple, and setting x and y was broken at one point.
 165  */
 166 static void
 167 set_shadow_position (EelCanvasItem *shadow,
 168 		     double x, double y)
 169 {
 170 	eel_canvas_item_set (shadow,
 171 			     "x", x, "y", y,
 172 			     NULL);
 173 }
 174 
 175 
 176 /* Source-side handling of the drag. */
 177 
 178 /* iteration glue struct */
 179 typedef struct {
 180 	gpointer iterator_context;
 181 	NautilusDragEachSelectedItemDataGet iteratee;
 182 	gpointer iteratee_data;
 183 } CanvasGetDataBinderContext;
 184 
 185 static void
 186 canvas_rect_world_to_widget (EelCanvas *canvas,
 187 			     EelDRect *world_rect,
 188 			     EelIRect *widget_rect)
 189 {
 190 	EelDRect window_rect;
 191 	GtkAdjustment *hadj, *vadj;
 192 
 193 	hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
 194 	vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
 195 	
 196 	eel_canvas_world_to_window (canvas,
 197 				    world_rect->x0, world_rect->y0,
 198 				    &window_rect.x0, &window_rect.y0);
 199 	eel_canvas_world_to_window (canvas,
 200 				    world_rect->x1, world_rect->y1,
 201 				    &window_rect.x1, &window_rect.y1);
 202 	widget_rect->x0 = (int) window_rect.x0 - gtk_adjustment_get_value (hadj);
 203 	widget_rect->y0 = (int) window_rect.y0 - gtk_adjustment_get_value (vadj);
 204 	widget_rect->x1 = (int) window_rect.x1 - gtk_adjustment_get_value (hadj);
 205 	widget_rect->y1 = (int) window_rect.y1 - gtk_adjustment_get_value (vadj);
 206 }
 207 
 208 static void
 209 canvas_widget_to_world (EelCanvas *canvas,
 210 			double widget_x, double widget_y,
 211 			double *world_x, double *world_y)
 212 {
 213 	eel_canvas_window_to_world (canvas,
 214 				    widget_x + gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas))),
 215 				    widget_y + gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas))),
 216 				    world_x, world_y);
 217 }
 218 
 219 static gboolean
 220 icon_get_data_binder (NautilusCanvasIcon *icon, gpointer data)
 221 {
 222 	CanvasGetDataBinderContext *context;
 223 	EelDRect world_rect;
 224 	EelIRect widget_rect;
 225 	char *uri;
 226 	NautilusCanvasContainer *container;
 227 
 228 	context = (CanvasGetDataBinderContext *)data;
 229 
 230 	g_assert (NAUTILUS_IS_CANVAS_CONTAINER (context->iterator_context));
 231 
 232 	container = NAUTILUS_CANVAS_CONTAINER (context->iterator_context);
 233 
 234 	world_rect = nautilus_canvas_item_get_icon_rectangle (icon->item);
 235 
 236 	canvas_rect_world_to_widget (EEL_CANVAS (container), &world_rect, &widget_rect);
 237 
 238 	uri = nautilus_canvas_container_get_icon_uri (container, icon);
 239 	if (uri == NULL) {
 240 		g_warning ("no URI for one of the iterated icons");
 241 		return TRUE;
 242 	}
 243 
 244 	widget_rect = eel_irect_offset_by (widget_rect, 
 245 		- container->details->dnd_info->drag_info.start_x,
 246 		- container->details->dnd_info->drag_info.start_y);
 247 
 248 	widget_rect = eel_irect_scale_by (widget_rect, 
 249 		1 / EEL_CANVAS (container)->pixels_per_unit);
 250 	
 251 	/* pass the uri, mouse-relative x/y and icon width/height */
 252 	context->iteratee (uri, 
 253 			   (int) widget_rect.x0,
 254 			   (int) widget_rect.y0,
 255 			   widget_rect.x1 - widget_rect.x0,
 256 			   widget_rect.y1 - widget_rect.y0,
 257 			   context->iteratee_data);
 258 
 259 	g_free (uri);
 260 
 261 	return TRUE;
 262 }
 263 
 264 /* Iterate over each selected icon in a NautilusCanvasContainer,
 265  * calling each_function on each.
 266  */
 267 static void
 268 nautilus_canvas_container_each_selected_icon (NautilusCanvasContainer *container,
 269 	gboolean (*each_function) (NautilusCanvasIcon *, gpointer), gpointer data)
 270 {
 271 	GList *p;
 272 	NautilusCanvasIcon *icon;
 273 
 274 	for (p = container->details->icons; p != NULL; p = p->next) {
 275 		icon = p->data;
 276 		if (!icon->is_selected) {
 277 			continue;
 278 		}
 279 		if (!each_function (icon, data)) {
 280 			return;
 281 		}
 282 	}
 283 }
 284 
 285 /* Adaptor function used with nautilus_canvas_container_each_selected_icon
 286  * to help iterate over all selected items, passing uris, x, y, w and h
 287  * values to the iteratee
 288  */
 289 static void
 290 each_icon_get_data_binder (NautilusDragEachSelectedItemDataGet iteratee, 
 291 	gpointer iterator_context, gpointer data)
 292 {
 293 	CanvasGetDataBinderContext context;
 294 	NautilusCanvasContainer *container;
 295 
 296 	g_assert (NAUTILUS_IS_CANVAS_CONTAINER (iterator_context));
 297 	container = NAUTILUS_CANVAS_CONTAINER (iterator_context);
 298 
 299 	context.iterator_context = iterator_context;
 300 	context.iteratee = iteratee;
 301 	context.iteratee_data = data;
 302 	nautilus_canvas_container_each_selected_icon (container, icon_get_data_binder, &context);
 303 }
 304 
 305 /* Called when the data for drag&drop is needed */
 306 static void
 307 drag_data_get_callback (GtkWidget *widget,
 308 			GdkDragContext *context,
 309 			GtkSelectionData *selection_data,
 310 			guint info,
 311 			guint32 time,
 312 			gpointer data)
 313 {
 314 	g_assert (widget != NULL);
 315 	g_assert (NAUTILUS_IS_CANVAS_CONTAINER (widget));
 316 	g_return_if_fail (context != NULL);
 317 
 318 	/* Call common function from nautilus-drag that set's up
 319 	 * the selection data in the right format. Pass it means to
 320 	 * iterate all the selected icons.
 321 	 */
 322 	nautilus_drag_drag_data_get (widget, context, selection_data,
 323 		info, time, widget, each_icon_get_data_binder);
 324 }
 325 
 326 
 327 /* Target-side handling of the drag.  */
 328 
 329 static void
 330 nautilus_canvas_container_position_shadow (NautilusCanvasContainer *container,
 331 					 int x, int y)
 332 {
 333 	EelCanvasItem *shadow;
 334 	double world_x, world_y;
 335 
 336 	shadow = container->details->dnd_info->shadow;
 337 	if (shadow == NULL) {
 338 		return;
 339 	}
 340 
 341 	canvas_widget_to_world (EEL_CANVAS (container), x, y,
 342 				&world_x, &world_y);
 343 
 344 	set_shadow_position (shadow, world_x, world_y);
 345 	eel_canvas_item_show (shadow);
 346 }
 347 
 348 static void
 349 nautilus_canvas_container_dropped_canvas_feedback (GtkWidget *widget,
 350 					       GtkSelectionData *data,
 351 					       int x, int y)
 352 {
 353 	NautilusCanvasContainer *container;
 354 	NautilusCanvasDndInfo *dnd_info;
 355 
 356 	container = NAUTILUS_CANVAS_CONTAINER (widget);
 357 	dnd_info = container->details->dnd_info;
 358 	
 359 	/* Delete old selection list. */
 360 	nautilus_drag_destroy_selection_list (dnd_info->drag_info.selection_list);
 361 	dnd_info->drag_info.selection_list = NULL;
 362 
 363 	/* Delete old shadow if any. */
 364 	if (dnd_info->shadow != NULL) {
 365 		/* FIXME bugzilla.gnome.org 42484: 
 366 		 * Is a destroy really sufficient here? Who does the unref? */
 367 		eel_canvas_item_destroy (dnd_info->shadow);
 368 	}
 369 
 370 	/* Build the selection list and the shadow. */
 371 	dnd_info->drag_info.selection_list = nautilus_drag_build_selection_list (data);
 372 	dnd_info->shadow = create_selection_shadow (container, dnd_info->drag_info.selection_list);
 373 	nautilus_canvas_container_position_shadow (container, x, y);
 374 }
 375 
 376 static char *
 377 get_direct_save_filename (GdkDragContext *context)
 378 {
 379 	guchar *prop_text;
 380 	gint prop_len;
 381 
 382 	if (!gdk_property_get (gdk_drag_context_get_source_window (context), gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE),
 383 			       gdk_atom_intern ("text/plain", FALSE), 0, 1024, FALSE, NULL, NULL,
 384 			       &prop_len, &prop_text)) {
 385 		return NULL;
 386 	}
 387 
 388 	/* Zero-terminate the string */
 389 	prop_text = g_realloc (prop_text, prop_len + 1);
 390 	prop_text[prop_len] = '\0';
 391 	
 392 	/* Verify that the file name provided by the source is valid */
 393 	if (*prop_text == '\0' ||
 394 	    strchr ((const gchar *) prop_text, G_DIR_SEPARATOR) != NULL) {
 395 		DEBUG ("Invalid filename provided by XDS drag site");
 396 		g_free (prop_text);
 397 		return NULL;
 398 	}
 399 	
 400 	return (gchar *) prop_text;
 401 }
 402 
 403 static void
 404 set_direct_save_uri (GtkWidget *widget, GdkDragContext *context, NautilusDragInfo *drag_info, int x, int y)
 405 {
 406 	GFile *base, *child;
 407 	char *filename, *drop_target;
 408 	gchar *uri;
 409 	
 410 	drag_info->got_drop_data_type = TRUE;
 411 	drag_info->data_type = NAUTILUS_ICON_DND_XDNDDIRECTSAVE;
 412 	
 413 	uri = NULL;
 414 	
 415 	filename = get_direct_save_filename (context);
 416 	drop_target = nautilus_canvas_container_find_drop_target (NAUTILUS_CANVAS_CONTAINER (widget), 
 417 								context, x, y, NULL, TRUE);
 418 	
 419 	if (drop_target && eel_uri_is_trash (drop_target)) {
 420 		g_free (drop_target);
 421 		drop_target = NULL; /* Cannot save to trash ...*/
 422 	}
 423 	
 424 	if (filename != NULL && drop_target != NULL) {
 425 		/* Resolve relative path */
 426 		base = g_file_new_for_uri (drop_target);
 427 		child = g_file_get_child (base, filename);
 428 		uri = g_file_get_uri (child);
 429 		g_object_unref (base);
 430 		g_object_unref (child);
 431 		
 432 		/* Change the uri property */
 433 		gdk_property_change (gdk_drag_context_get_source_window (context),
 434 				     gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE),
 435 				     gdk_atom_intern ("text/plain", FALSE), 8,
 436 				     GDK_PROP_MODE_REPLACE, (const guchar *) uri,
 437 				     strlen (uri));
 438 		
 439 		drag_info->direct_save_uri = uri;
 440 	} 
 441 	
 442 	g_free (filename);
 443 	g_free (drop_target);
 444 }
 445 
 446 /* FIXME bugzilla.gnome.org 47445: Needs to become a shared function */
 447 static void
 448 get_data_on_first_target_we_support (GtkWidget *widget, GdkDragContext *context, guint32 time, int x, int y)
 449 {
 450 	GtkTargetList *list;
 451 	GdkAtom target;
 452 
 453 	if (drop_types_list == NULL) {
 454 		drop_types_list = gtk_target_list_new (drop_types,
 455 						       G_N_ELEMENTS (drop_types) - 1);
 456 		gtk_target_list_add_text_targets (drop_types_list, NAUTILUS_ICON_DND_TEXT);
 457 	}
 458 	if (drop_types_list_root == NULL) {
 459 		drop_types_list_root = gtk_target_list_new (drop_types,
 460 							    G_N_ELEMENTS (drop_types));
 461 		gtk_target_list_add_text_targets (drop_types_list_root, NAUTILUS_ICON_DND_TEXT);
 462 	}
 463 	
 464 	if (nautilus_canvas_container_get_is_desktop (NAUTILUS_CANVAS_CONTAINER (widget))) {
 465 		list = drop_types_list_root;
 466 	} else {
 467 		list = drop_types_list;
 468 	}
 469 
 470 	target = gtk_drag_dest_find_target (widget, context, list);
 471 	if (target != GDK_NONE) {
 472 		guint info;
 473 		NautilusDragInfo *drag_info;
 474 		gboolean found;
 475 
 476 		drag_info = &(NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info->drag_info);
 477 
 478 		found = gtk_target_list_find (list, target, &info);
 479 		g_assert (found);
 480 
 481 		/* Don't get_data for destructive ops */
 482 		if ((info == NAUTILUS_ICON_DND_ROOTWINDOW_DROP ||
 483 		     info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE) &&
 484 		    !drag_info->drop_occured) {
 485 			/* We can't call get_data here, because that would
 486 			   make the source execute the rootwin action or the direct save */
 487 			drag_info->got_drop_data_type = TRUE;
 488 			drag_info->data_type = info;
 489 		} else {
 490 			if (info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE) {
 491 				set_direct_save_uri (widget, context, drag_info, x, y);
 492 			}
 493 			gtk_drag_get_data (GTK_WIDGET (widget), context,
 494 					   target, time);
 495 		}
 496 	}
 497 }
 498 
 499 static void
 500 nautilus_canvas_container_ensure_drag_data (NautilusCanvasContainer *container,
 501 					  GdkDragContext *context,
 502 					  guint32 time)
 503 {
 504 	NautilusCanvasDndInfo *dnd_info;
 505 
 506 	dnd_info = container->details->dnd_info;
 507 
 508 	if (!dnd_info->drag_info.got_drop_data_type) {
 509 		get_data_on_first_target_we_support (GTK_WIDGET (container), context, time, 0, 0);
 510 	}
 511 }
 512 
 513 static void
 514 drag_end_callback (GtkWidget *widget,
 515 		   GdkDragContext *context,
 516 		   gpointer data)
 517 {
 518 	NautilusCanvasContainer *container;
 519 	NautilusCanvasDndInfo *dnd_info;
 520 
 521 	container = NAUTILUS_CANVAS_CONTAINER (widget);
 522 	dnd_info = container->details->dnd_info;
 523 
 524 	nautilus_drag_destroy_selection_list (dnd_info->drag_info.selection_list);
 525 	dnd_info->drag_info.selection_list = NULL;
 526 }
 527 
 528 static NautilusCanvasIcon *
 529 nautilus_canvas_container_item_at (NautilusCanvasContainer *container,
 530 				 int x, int y)
 531 {
 532 	GList *p;
 533 	int size;
 534 	EelDRect point;
 535 	EelIRect canvas_point;
 536 
 537 	/* build the hit-test rectangle. Base the size on the scale factor to ensure that it is
 538 	 * non-empty even at the smallest scale factor
 539 	 */
 540 	
 541 	size = MAX (1, 1 + (1 / EEL_CANVAS (container)->pixels_per_unit));
 542 	point.x0 = x;
 543 	point.y0 = y;
 544 	point.x1 = x + size;
 545 	point.y1 = y + size;
 546 
 547 	for (p = container->details->icons; p != NULL; p = p->next) {
 548 		NautilusCanvasIcon *icon;
 549 		icon = p->data;
 550 		
 551 		eel_canvas_w2c (EEL_CANVAS (container),
 552 				point.x0,
 553 				point.y0,
 554 				&canvas_point.x0,
 555 				&canvas_point.y0);
 556 		eel_canvas_w2c (EEL_CANVAS (container),
 557 				point.x1,
 558 				point.y1,
 559 				&canvas_point.x1,
 560 				&canvas_point.y1);
 561 		if (nautilus_canvas_item_hit_test_rectangle (icon->item, canvas_point)) {
 562 			return icon;
 563 		}
 564 	}
 565 	
 566 	return NULL;
 567 }
 568 
 569 static char *
 570 get_container_uri (NautilusCanvasContainer *container)
 571 {
 572 	char *uri;
 573 
 574 	/* get the URI associated with the container */
 575 	uri = NULL;
 576 	g_signal_emit_by_name (container, "get_container_uri", &uri);
 577 	return uri;
 578 }
 579 
 580 static gboolean
 581 nautilus_canvas_container_selection_items_local (NautilusCanvasContainer *container,
 582 					       GList *items)
 583 {
 584 	char *container_uri_string;
 585 	gboolean result;
 586 
 587 	/* must have at least one item */
 588 	g_assert (items);
 589 
 590 	/* get the URI associated with the container */
 591 	container_uri_string = get_container_uri (container);
 592 	
 593 	if (eel_uri_is_desktop (container_uri_string)) {
 594 		result = nautilus_drag_items_on_desktop (items);
 595 	} else {
 596 		result = nautilus_drag_items_local (container_uri_string, items);
 597 	}
 598 	g_free (container_uri_string);
 599 	
 600 	return result;
 601 }
 602 
 603 /* handle dropped url */
 604 static void
 605 receive_dropped_netscape_url (NautilusCanvasContainer *container, const char *encoded_url, GdkDragContext *context, int x, int y)
 606 {
 607 	char *drop_target;
 608 
 609 	if (encoded_url == NULL) {
 610 		return;
 611 	}
 612 
 613 	drop_target = nautilus_canvas_container_find_drop_target (container, context, x, y, NULL, TRUE);
 614 
 615 	g_signal_emit_by_name (container, "handle_netscape_url",
 616 			       encoded_url,
 617 			       drop_target,
 618 			       gdk_drag_context_get_selected_action (context),
 619 			       x, y);
 620 
 621 	g_free (drop_target);
 622 }
 623 
 624 /* handle dropped uri list */
 625 static void
 626 receive_dropped_uri_list (NautilusCanvasContainer *container, const char *uri_list, GdkDragContext *context, int x, int y)
 627 {	
 628 	char *drop_target;
 629 
 630 	if (uri_list == NULL) {
 631 		return;
 632 	}
 633 
 634 	drop_target = nautilus_canvas_container_find_drop_target (container, context, x, y, NULL, TRUE);
 635 
 636 	g_signal_emit_by_name (container, "handle_uri_list",
 637 				 uri_list,
 638 				 drop_target,
 639 				 gdk_drag_context_get_selected_action (context),
 640 				 x, y);
 641 
 642 	g_free (drop_target);
 643 }
 644 
 645 /* handle dropped text */
 646 static void
 647 receive_dropped_text (NautilusCanvasContainer *container, const char *text, GdkDragContext *context, int x, int y)
 648 {	
 649 	char *drop_target;
 650 
 651 	if (text == NULL) {
 652 		return;
 653 	}
 654 
 655 	drop_target = nautilus_canvas_container_find_drop_target (container, context, x, y, NULL, TRUE);
 656 	
 657 	g_signal_emit_by_name (container, "handle_text",
 658 			       text,
 659 			       drop_target,
 660 			       gdk_drag_context_get_selected_action (context),
 661 			       x, y);
 662 
 663 	g_free (drop_target);
 664 }
 665 
 666 /* handle dropped raw data */
 667 static void
 668 receive_dropped_raw (NautilusCanvasContainer *container, const char *raw_data, int length, const char *direct_save_uri, GdkDragContext *context, int x, int y)
 669 {
 670 	char *drop_target;
 671 
 672 	if (raw_data == NULL) {
 673 		return;
 674 	}
 675 
 676 	drop_target = nautilus_canvas_container_find_drop_target (container, context, x, y, NULL, TRUE);
 677 
 678 	g_signal_emit_by_name (container, "handle_raw",
 679 			       raw_data,
 680 			       length,
 681 			       drop_target,
 682 			       direct_save_uri,
 683 			       gdk_drag_context_get_selected_action (context),
 684 			       x, y);
 685 
 686 	g_free (drop_target);
 687 }
 688 
 689 static int
 690 auto_scroll_timeout_callback (gpointer data)
 691 {
 692 	NautilusCanvasContainer *container;
 693 	GtkWidget *widget;
 694 	float x_scroll_delta, y_scroll_delta;
 695 	GdkRectangle exposed_area;
 696 	GtkAllocation allocation;
 697 
 698 	g_assert (NAUTILUS_IS_CANVAS_CONTAINER (data));
 699 	widget = GTK_WIDGET (data);
 700 	container = NAUTILUS_CANVAS_CONTAINER (widget);
 701 
 702 	if (container->details->dnd_info->drag_info.waiting_to_autoscroll
 703 	    && container->details->dnd_info->drag_info.start_auto_scroll_in > g_get_monotonic_time ()) {
 704 		/* not yet */
 705 		return TRUE;
 706 	}
 707 
 708 	container->details->dnd_info->drag_info.waiting_to_autoscroll = FALSE;
 709 
 710 	nautilus_drag_autoscroll_calculate_delta (widget, &x_scroll_delta, &y_scroll_delta);
 711 	if (x_scroll_delta == 0 && y_scroll_delta == 0) {
 712 		/* no work */
 713 		return TRUE;
 714 	}
 715 
 716 	/* Clear the old dnd highlight frame */
 717 	dnd_highlight_queue_redraw (widget);
 718 	
 719 	if (!nautilus_canvas_container_scroll (container, (int)x_scroll_delta, (int)y_scroll_delta)) {
 720 		/* the scroll value got pinned to a min or max adjustment value,
 721 		 * we ended up not scrolling
 722 		 */
 723 		return TRUE;
 724 	}
 725 
 726 	/* Make sure the dnd highlight frame is redrawn */
 727 	dnd_highlight_queue_redraw (widget);
 728 	
 729 	/* update cached drag start offsets */
 730 	container->details->dnd_info->drag_info.start_x -= x_scroll_delta;
 731 	container->details->dnd_info->drag_info.start_y -= y_scroll_delta;
 732 
 733 	/* Due to a glitch in GtkLayout, whe need to do an explicit draw of the exposed
 734 	 * area. 
 735 	 * Calculate the size of the area we need to draw
 736 	 */
 737 	gtk_widget_get_allocation (widget, &allocation);
 738 	exposed_area.x = allocation.x;
 739 	exposed_area.y = allocation.y;
 740 	exposed_area.width = allocation.width;
 741 	exposed_area.height = allocation.height;
 742 
 743 	if (x_scroll_delta > 0) {
 744 		exposed_area.x = exposed_area.width - x_scroll_delta;
 745 	} else if (x_scroll_delta < 0) {
 746 		exposed_area.width = -x_scroll_delta;
 747 	}
 748 
 749 	if (y_scroll_delta > 0) {
 750 		exposed_area.y = exposed_area.height - y_scroll_delta;
 751 	} else if (y_scroll_delta < 0) {
 752 		exposed_area.height = -y_scroll_delta;
 753 	}
 754 
 755 	/* offset it to 0, 0 */
 756 	exposed_area.x -= allocation.x;
 757 	exposed_area.y -= allocation.y;
 758 
 759 	gtk_widget_queue_draw_area (widget,
 760 				    exposed_area.x,
 761 				    exposed_area.y,
 762 				    exposed_area.width,
 763 				    exposed_area.height);
 764 
 765 	return TRUE;
 766 }
 767 
 768 static void
 769 set_up_auto_scroll_if_needed (NautilusCanvasContainer *container)
 770 {
 771 	nautilus_drag_autoscroll_start (&container->details->dnd_info->drag_info,
 772 					GTK_WIDGET (container),
 773 					auto_scroll_timeout_callback,
 774 					container);
 775 }
 776 
 777 static void
 778 stop_auto_scroll (NautilusCanvasContainer *container)
 779 {
 780 	nautilus_drag_autoscroll_stop (&container->details->dnd_info->drag_info);
 781 }
 782 
 783 static void
 784 handle_local_move (NautilusCanvasContainer *container,
 785 		   double world_x, double world_y)
 786 {
 787 	GList *moved_icons, *p;
 788 	NautilusDragSelectionItem *item;
 789 	NautilusCanvasIcon *icon;
 790 	NautilusFile *file;
 791 	char screen_string[32];
 792 	GdkScreen *screen;
 793 	time_t now;
 794 
 795 	if (container->details->auto_layout) {
 796 		return;
 797 	}
 798 
 799 	time (&now);
 800 
 801 	/* Move and select the icons. */
 802 	moved_icons = NULL;
 803 	for (p = container->details->dnd_info->drag_info.selection_list; p != NULL; p = p->next) {
 804 		item = p->data;
 805 		
 806 		icon = nautilus_canvas_container_get_icon_by_uri
 807 			(container, item->uri);
 808 
 809 		if (icon == NULL) {
 810 			/* probably dragged from another screen.  Add it to
 811 			 * this screen
 812 			 */
 813 
 814 			file = nautilus_file_get_by_uri (item->uri);
 815 
 816 			screen = gtk_widget_get_screen (GTK_WIDGET (container));
 817 			g_snprintf (screen_string, sizeof (screen_string), "%d",
 818 				    gdk_screen_get_number (screen));
 819 			nautilus_file_set_metadata (file,
 820 					NAUTILUS_METADATA_KEY_SCREEN,
 821 					NULL, screen_string);
 822 			nautilus_file_set_time_metadata (file,
 823 							 NAUTILUS_METADATA_KEY_ICON_POSITION_TIMESTAMP, now);
 824 
 825 			nautilus_canvas_container_add (container, NAUTILUS_CANVAS_ICON_DATA (file));
 826 			
 827 			icon = nautilus_canvas_container_get_icon_by_uri
 828 				(container, item->uri);
 829 		}
 830 
 831 		if (item->got_icon_position) {
 832 			nautilus_canvas_container_move_icon
 833 				(container, icon,
 834 				 world_x + item->icon_x, world_y + item->icon_y,
 835 				 icon->scale,
 836 				 TRUE, TRUE, TRUE);
 837 		}
 838 		moved_icons = g_list_prepend (moved_icons, icon);
 839 	}		
 840 	nautilus_canvas_container_select_list_unselect_others
 841 		(container, moved_icons);
 842 	/* Might have been moved in a way that requires adjusting scroll region. */
 843 	nautilus_canvas_container_update_scroll_region (container);
 844 	g_list_free (moved_icons);
 845 }
 846 
 847 static void
 848 handle_nonlocal_move (NautilusCanvasContainer *container,
 849 		      GdkDragAction action,
 850 		      int x, int y,
 851 		      const char *target_uri,
 852 		      gboolean icon_hit)
 853 {
 854 	GList *source_uris, *p;
 855 	GArray *source_item_locations;
 856 	gboolean free_target_uri, is_rtl;
 857 	int index, item_x;
 858 	GtkAllocation allocation;
 859 
 860 	if (container->details->dnd_info->drag_info.selection_list == NULL) {
 861 		return;
 862 	}
 863 
 864 	source_uris = NULL;
 865 	for (p = container->details->dnd_info->drag_info.selection_list; p != NULL; p = p->next) {
 866 		/* do a shallow copy of all the uri strings of the copied files */
 867 		source_uris = g_list_prepend (source_uris, ((NautilusDragSelectionItem *)p->data)->uri);
 868 	}
 869 	source_uris = g_list_reverse (source_uris);
 870 	
 871 	is_rtl = nautilus_canvas_container_is_layout_rtl (container);
 872 
 873 	source_item_locations = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
 874 	if (!icon_hit) {
 875 		/* Drop onto a container. Pass along the item points to allow placing
 876 		 * the items in their same relative positions in the new container.
 877 		 */
 878 		source_item_locations = g_array_set_size (source_item_locations,
 879 			g_list_length (container->details->dnd_info->drag_info.selection_list));
 880 			
 881 		for (index = 0, p = container->details->dnd_info->drag_info.selection_list;
 882 			p != NULL; index++, p = p->next) {
 883 		    	item_x = ((NautilusDragSelectionItem *)p->data)->icon_x;
 884 			if (is_rtl)
 885 				item_x = -item_x - ((NautilusDragSelectionItem *)p->data)->icon_width;
 886 		     	g_array_index (source_item_locations, GdkPoint, index).x = item_x;
 887 		     	g_array_index (source_item_locations, GdkPoint, index).y =
 888 				((NautilusDragSelectionItem *)p->data)->icon_y;
 889 		}
 890 	}
 891 
 892 	free_target_uri = FALSE;
 893  	/* Rewrite internal desktop URIs to the normal target uri */
 894 	if (eel_uri_is_desktop (target_uri)) {
 895 		target_uri = nautilus_get_desktop_directory_uri ();
 896 		free_target_uri = TRUE;
 897 	}
 898 
 899 	if (is_rtl) {
 900 		gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 901 		x = CANVAS_WIDTH (container, allocation) - x;
 902 	}
 903 
 904 	/* start the copy */
 905 	g_signal_emit_by_name (container, "move_copy_items",
 906 			       source_uris,
 907 			       source_item_locations,
 908 			       target_uri,
 909 			       action,
 910 			       x, y);
 911 
 912 	if (free_target_uri) {
 913 		g_free ((char *)target_uri);
 914 	}
 915 
 916 	g_list_free (source_uris);
 917 	g_array_free (source_item_locations, TRUE);
 918 }
 919 
 920 static char *
 921 nautilus_canvas_container_find_drop_target (NautilusCanvasContainer *container,
 922 					  GdkDragContext *context,
 923 					  int x, int y,
 924 					  gboolean *icon_hit,
 925 					  gboolean rewrite_desktop)
 926 {
 927 	NautilusCanvasIcon *drop_target_icon;
 928 	double world_x, world_y;
 929 	NautilusFile *file;
 930 	char *icon_uri;
 931 	char *container_uri;
 932 	
 933 	if (icon_hit) {
 934 		*icon_hit = FALSE;
 935 	}
 936 
 937 	if (!container->details->dnd_info->drag_info.got_drop_data_type) {
 938 		return NULL;
 939 	}
 940 
 941 	canvas_widget_to_world (EEL_CANVAS (container), x, y, &world_x, &world_y);
 942 	
 943 	/* FIXME bugzilla.gnome.org 42485: 
 944 	 * These "can_accept_items" tests need to be done by
 945 	 * the canvas view, not here. This file is not supposed to know
 946 	 * that the target is a file.
 947 	 */
 948 
 949 	/* Find the item we hit with our drop, if any */	
 950 	drop_target_icon = nautilus_canvas_container_item_at (container, world_x, world_y);
 951 	if (drop_target_icon != NULL) {
 952 		icon_uri = nautilus_canvas_container_get_icon_uri (container, drop_target_icon);
 953 		if (icon_uri != NULL) {
 954 			file = nautilus_file_get_by_uri (icon_uri);
 955 
 956 			if (!nautilus_drag_can_accept_info (file,
 957 							    container->details->dnd_info->drag_info.data_type,
 958 							    container->details->dnd_info->drag_info.selection_list)) {
 959 			 	/* the item we dropped our selection on cannot accept the items,
 960 			 	 * do the same thing as if we just dropped the items on the canvas
 961 				 */
 962 				drop_target_icon = NULL;
 963 			}
 964 			
 965 			g_free (icon_uri);
 966 			nautilus_file_unref (file);
 967 		}
 968 	}
 969 
 970 	if (drop_target_icon == NULL) {
 971 		if (icon_hit) {
 972 			*icon_hit = FALSE;
 973 		}
 974 
 975 		container_uri = get_container_uri (container);
 976 
 977 		if (container_uri != NULL) {
 978 			if (rewrite_desktop && eel_uri_is_desktop (container_uri)) {
 979 				g_free (container_uri);
 980 				container_uri = nautilus_get_desktop_directory_uri ();
 981 			} else {
 982 				gboolean can;
 983 				file = nautilus_file_get_by_uri (container_uri);
 984 				can = nautilus_drag_can_accept_info (file,
 985 								     container->details->dnd_info->drag_info.data_type,
 986 								     container->details->dnd_info->drag_info.selection_list);
 987 				g_object_unref (file);
 988 				if (!can) {
 989 					g_free (container_uri);
 990 					container_uri = NULL;
 991 				}
 992 			}
 993 		}
 994 		
 995 		return container_uri;
 996 	}
 997 	
 998 	if (icon_hit) {
 999 		*icon_hit = TRUE;
1000 	}
1001 	return nautilus_canvas_container_get_icon_drop_target_uri (container, drop_target_icon);
1002 }
1003 
1004 static gboolean
1005 selection_is_image_file (GList *selection_list)
1006 {
1007 	const char *mime_type;
1008 	NautilusDragSelectionItem *selected_item;
1009 	gboolean result;
1010 	GFile *location;
1011 	GFileInfo *info;
1012 
1013 	/* Make sure only one item is selected */
1014 	if (selection_list == NULL ||
1015 	    selection_list->next != NULL) {
1016 		return FALSE;
1017 	}
1018 
1019 	selected_item = selection_list->data;
1020 
1021 	mime_type = NULL;
1022 	
1023 	location = g_file_new_for_uri (selected_item->uri);
1024 	info = g_file_query_info (location,
1025 				  G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
1026 				  0, NULL, NULL);
1027 	if (info) {
1028 		mime_type = g_file_info_get_content_type (info);
1029 	}
1030 
1031 	result = g_str_has_prefix (mime_type, "image/");
1032 
1033 	if (info) {
1034 		g_object_unref (info);
1035 	}
1036 	g_object_unref (location);
1037 	
1038 	return result;
1039 }
1040 
1041 
1042 static void
1043 nautilus_canvas_container_receive_dropped_icons (NautilusCanvasContainer *container,
1044 					       GdkDragContext *context,
1045 					       int x, int y)
1046 {
1047 	char *drop_target, *container_uri;
1048 	gboolean local_move_only;
1049 	double world_x, world_y;
1050 	gboolean icon_hit;
1051 	GdkDragAction action, real_action;
1052 	NautilusDragSelectionItem *selected_item;
1053 
1054 	drop_target = NULL;
1055 
1056 	if (container->details->dnd_info->drag_info.selection_list == NULL) {
1057 		return;
1058 	}
1059 
1060 	real_action = gdk_drag_context_get_selected_action (context);
1061 
1062 	if (real_action == GDK_ACTION_ASK) {
1063 		/* FIXME bugzilla.gnome.org 42485: This belongs in FMDirectoryView, not here. */
1064 		/* Check for special case items in selection list */
1065 		if (nautilus_drag_selection_includes_special_link (container->details->dnd_info->drag_info.selection_list)) {
1066 			/* We only want to move the trash */
1067 			action = GDK_ACTION_MOVE;
1068 		} else {
1069 			action = GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK;
1070 			container_uri = get_container_uri (container);
1071 			
1072 			if (eel_uri_is_desktop (container_uri) &&
1073 			    selection_is_image_file (container->details->dnd_info->drag_info.selection_list)) {
1074 				action |= NAUTILUS_DND_ACTION_SET_AS_BACKGROUND;
1075 			}
1076 
1077 			g_free (container_uri);
1078 		}
1079 		real_action = nautilus_drag_drop_action_ask
1080 			(GTK_WIDGET (container), action);
1081 	}
1082 	
1083 	if (real_action == (GdkDragAction) NAUTILUS_DND_ACTION_SET_AS_BACKGROUND) {
1084 		NautilusDesktopBackground *background;
1085 
1086 		background = nautilus_desktop_background_new (container);
1087 		selected_item = container->details->dnd_info->drag_info.selection_list->data;
1088 
1089 		nautilus_desktop_background_receive_dropped_background_image (background,
1090 									      selected_item->uri);
1091 
1092 		g_object_unref (background);
1093 
1094 		return;
1095 	}
1096 		
1097 	if (real_action > 0) {
1098 		eel_canvas_window_to_world (EEL_CANVAS (container),
1099 					    x + gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container))),
1100 					    y + gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container))),
1101 					    &world_x, &world_y);
1102 
1103 		drop_target = nautilus_canvas_container_find_drop_target (container, 
1104 									context, x, y, &icon_hit, FALSE);
1105 
1106 		local_move_only = FALSE;
1107 		if (!icon_hit && real_action == GDK_ACTION_MOVE) {
1108 			/* we can just move the canvas positions if the move ended up in
1109 			 * the item's parent container
1110 			 */
1111 			local_move_only = nautilus_canvas_container_selection_items_local
1112 				(container, container->details->dnd_info->drag_info.selection_list);
1113 		}
1114 
1115 		if (local_move_only) {
1116 			handle_local_move (container, world_x, world_y);
1117 		} else {
1118 			handle_nonlocal_move (container, real_action, world_x, world_y, drop_target, icon_hit);
1119 		}
1120 	}
1121 
1122 	g_free (drop_target);
1123 	nautilus_drag_destroy_selection_list (container->details->dnd_info->drag_info.selection_list);
1124 	container->details->dnd_info->drag_info.selection_list = NULL;
1125 }
1126 
1127 static void
1128 nautilus_canvas_container_get_drop_action (NautilusCanvasContainer *container,
1129 					 GdkDragContext *context,
1130 					 int x, int y,
1131 					 int *action)
1132 {
1133 	char *drop_target;
1134 	gboolean icon_hit;
1135 	double world_x, world_y;
1136 
1137 	icon_hit = FALSE;
1138 	if (!container->details->dnd_info->drag_info.got_drop_data_type) {
1139 		/* drag_data_received_callback didn't get called yet */
1140 		return;
1141 	}
1142 
1143 	/* find out if we're over an canvas */
1144 	canvas_widget_to_world (EEL_CANVAS (container), x, y, &world_x, &world_y);
1145 	*action = 0;
1146 
1147 	drop_target = nautilus_canvas_container_find_drop_target (container,
1148 								  context, x, y, &icon_hit, FALSE);
1149 	if (drop_target == NULL) {
1150 		return;
1151 	}
1152 
1153 	/* case out on the type of object being dragged */
1154 	switch (container->details->dnd_info->drag_info.data_type) {
1155 	case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
1156 		if (container->details->dnd_info->drag_info.selection_list != NULL) {
1157 			nautilus_drag_default_drop_action_for_icons (context, drop_target, 
1158 								     container->details->dnd_info->drag_info.selection_list, 
1159 								     action);
1160 		}
1161 		break;
1162 	case NAUTILUS_ICON_DND_URI_LIST:
1163 		*action = nautilus_drag_default_drop_action_for_uri_list (context, drop_target);
1164 		break;
1165 
1166 	case NAUTILUS_ICON_DND_NETSCAPE_URL:
1167 		*action = nautilus_drag_default_drop_action_for_netscape_url (context);
1168 		break;
1169 
1170 	case NAUTILUS_ICON_DND_ROOTWINDOW_DROP:
1171 		*action = gdk_drag_context_get_suggested_action (context);
1172 		break;
1173 
1174 	case NAUTILUS_ICON_DND_TEXT:
1175 	case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
1176 	case NAUTILUS_ICON_DND_RAW:
1177 		*action = GDK_ACTION_COPY;
1178 		break;
1179 	}
1180 
1181 	g_free (drop_target);
1182 }
1183 
1184 static void
1185 set_drop_target (NautilusCanvasContainer *container,
1186 		 NautilusCanvasIcon *icon)
1187 {
1188 	NautilusCanvasIcon *old_icon;
1189 
1190 	/* Check if current drop target changed, update icon drop
1191 	 * higlight if needed.
1192 	 */
1193 	old_icon = container->details->drop_target;
1194 	if (icon == old_icon) {
1195 		return;
1196 	}
1197 
1198 	/* Remember the new drop target for the next round. */
1199 	container->details->drop_target = icon;
1200 	nautilus_canvas_container_update_icon (container, old_icon);
1201 	nautilus_canvas_container_update_icon (container, icon);
1202 }
1203 
1204 static void
1205 nautilus_canvas_dnd_update_drop_target (NautilusCanvasContainer *container,
1206 				      GdkDragContext *context,
1207 				      int x, int y)
1208 {
1209 	NautilusCanvasIcon *icon;
1210 	NautilusFile *file;
1211 	double world_x, world_y;
1212 	char *uri;
1213 	
1214 	g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
1215 
1216 	canvas_widget_to_world (EEL_CANVAS (container), x, y, &world_x, &world_y);
1217 
1218 	/* Find the item we hit with our drop, if any. */
1219 	icon = nautilus_canvas_container_item_at (container, world_x, world_y);
1220 
1221 	/* FIXME bugzilla.gnome.org 42485: 
1222 	 * These "can_accept_items" tests need to be done by
1223 	 * the canvas view, not here. This file is not supposed to know
1224 	 * that the target is a file.
1225 	 */
1226 
1227 	/* Find if target canvas accepts our drop. */
1228 	if (icon != NULL) {
1229 		    uri = nautilus_canvas_container_get_icon_uri (container, icon);
1230 		    file = nautilus_file_get_by_uri (uri);
1231 		    g_free (uri);
1232 		
1233 		    if (!nautilus_drag_can_accept_info (file,
1234 					    		container->details->dnd_info->drag_info.data_type,
1235 							container->details->dnd_info->drag_info.selection_list)) {
1236 			    icon = NULL;
1237 		    }
1238 
1239 		    nautilus_file_unref (file);
1240 	}
1241 
1242 	set_drop_target (container, icon);
1243 }
1244 
1245 static void
1246 nautilus_canvas_container_free_drag_data (NautilusCanvasContainer *container)
1247 {
1248 	NautilusCanvasDndInfo *dnd_info;
1249 
1250 	dnd_info = container->details->dnd_info;
1251 	
1252 	dnd_info->drag_info.got_drop_data_type = FALSE;
1253 
1254 	if (dnd_info->shadow != NULL) {
1255 		eel_canvas_item_destroy (dnd_info->shadow);
1256 		dnd_info->shadow = NULL;
1257 	}
1258 
1259 	if (dnd_info->drag_info.selection_data != NULL) {
1260 		gtk_selection_data_free (dnd_info->drag_info.selection_data);
1261 		dnd_info->drag_info.selection_data = NULL;
1262 	}
1263 
1264 	if (dnd_info->drag_info.direct_save_uri != NULL) {
1265 		g_free (dnd_info->drag_info.direct_save_uri);
1266 		dnd_info->drag_info.direct_save_uri = NULL;
1267 	}
1268 }
1269 
1270 static void
1271 drag_leave_callback (GtkWidget *widget,
1272 		     GdkDragContext *context,
1273 		     guint32 time,
1274 		     gpointer data)
1275 {
1276 	NautilusCanvasDndInfo *dnd_info;
1277 
1278 	dnd_info = NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info;
1279 	
1280 	if (dnd_info->shadow != NULL)
1281 		eel_canvas_item_hide (dnd_info->shadow);
1282 
1283 	stop_dnd_highlight (widget);
1284 	
1285 	set_drop_target (NAUTILUS_CANVAS_CONTAINER (widget), NULL);
1286 	stop_auto_scroll (NAUTILUS_CANVAS_CONTAINER (widget));
1287 	nautilus_canvas_container_free_drag_data(NAUTILUS_CANVAS_CONTAINER (widget));
1288 }
1289 
1290 static void
1291 drag_begin_callback (GtkWidget      *widget,
1292 		     GdkDragContext *context,
1293 		     gpointer        data)
1294 {
1295 	NautilusCanvasContainer *container;
1296 	cairo_surface_t *surface;
1297 	double x1, y1, x2, y2, winx, winy;
1298 	int x_offset, y_offset;
1299 	int start_x, start_y;
1300 
1301 	container = NAUTILUS_CANVAS_CONTAINER (widget);
1302 
1303 	start_x = container->details->dnd_info->drag_info.start_x +
1304 		gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
1305 	start_y = container->details->dnd_info->drag_info.start_y +
1306 		gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)));
1307 
1308         /* create a pixmap and mask to drag with */
1309         surface = nautilus_canvas_item_get_drag_surface (container->details->drag_icon->item);
1310 
1311         /* compute the image's offset */
1312 	eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (container->details->drag_icon->item),
1313 				    &x1, &y1, &x2, &y2);
1314 	eel_canvas_world_to_window (EEL_CANVAS (container), 
1315 				    x1, y1,  &winx, &winy);
1316         x_offset = start_x - winx;
1317         y_offset = start_y - winy;
1318 
1319         cairo_surface_set_device_offset (surface, -x_offset, -y_offset);
1320         gtk_drag_set_icon_surface (context, surface);
1321         cairo_surface_destroy (surface);
1322 }
1323 
1324 void
1325 nautilus_canvas_dnd_begin_drag (NautilusCanvasContainer *container,
1326 			      GdkDragAction actions,
1327 			      int button,
1328 			      GdkEventMotion *event,
1329 			      int                    start_x,
1330 			      int                    start_y)
1331 {
1332 	NautilusCanvasDndInfo *dnd_info;
1333 	
1334 	g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
1335 	g_return_if_fail (event != NULL);
1336 
1337 	dnd_info = container->details->dnd_info;
1338 	g_return_if_fail (dnd_info != NULL);
1339 	
1340 	/* Notice that the event is in bin_window coordinates, because of
1341            the way the canvas handles events.
1342 	*/
1343 	dnd_info->drag_info.start_x = start_x -
1344 		gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
1345 	dnd_info->drag_info.start_y = start_y -
1346 		gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)));	
1347 
1348 	/* start the drag */
1349 	gtk_drag_begin (GTK_WIDGET (container),
1350 			dnd_info->drag_info.target_list,
1351 			actions,
1352 			button,
1353 			(GdkEvent *) event);
1354 }
1355 
1356 static gboolean
1357 drag_highlight_draw (GtkWidget *widget,
1358                      cairo_t   *cr,
1359                      gpointer   user_data)
1360 {
1361 	gint width, height;
1362 	GdkWindow *window;
1363 	GtkStyleContext *style;
1364 	
1365         window = gtk_widget_get_window (widget);
1366         width = gdk_window_get_width (window);
1367         height = gdk_window_get_height (window);
1368 
1369 	style = gtk_widget_get_style_context (widget);
1370 
1371 	gtk_style_context_save (style);
1372 	gtk_style_context_add_class (style, GTK_STYLE_CLASS_DND);
1373 	gtk_style_context_set_state (style, GTK_STATE_FLAG_FOCUSED);
1374 
1375 	gtk_render_frame (style,
1376 			  cr,
1377 			  0, 0, width, height);
1378 
1379 	gtk_style_context_restore (style);
1380 
1381 	return FALSE;
1382 }
1383 
1384 /* Queue a redraw of the dnd highlight rect */
1385 static void
1386 dnd_highlight_queue_redraw (GtkWidget *widget)
1387 {
1388 	NautilusCanvasDndInfo *dnd_info;
1389 	int width, height;
1390 	GtkAllocation allocation;
1391 	
1392 	dnd_info = NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info;
1393 	
1394 	if (!dnd_info->highlighted) {
1395 		return;
1396 	}
1397 
1398 	gtk_widget_get_allocation (widget, &allocation);
1399 	width = allocation.width;
1400 	height = allocation.height;
1401 
1402 	/* we don't know how wide the shadow is exactly,
1403 	 * so we expose a 10-pixel wide border
1404 	 */
1405 	gtk_widget_queue_draw_area (widget,
1406 				    0, 0,
1407 				    width, 10);
1408 	gtk_widget_queue_draw_area (widget,
1409 				    0, 0,
1410 				    10, height);
1411 	gtk_widget_queue_draw_area (widget,
1412 				    0, height - 10,
1413 				    width, 10);
1414 	gtk_widget_queue_draw_area (widget,
1415 				    width - 10, 0,
1416 				    10, height);
1417 }
1418 
1419 static void
1420 start_dnd_highlight (GtkWidget *widget)
1421 {
1422 	NautilusCanvasDndInfo *dnd_info;
1423 	GtkWidget *toplevel;
1424 	
1425 	dnd_info = NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info;
1426 
1427 	toplevel = gtk_widget_get_toplevel (widget);
1428 	if (toplevel != NULL &&
1429 	    g_object_get_data (G_OBJECT (toplevel), "is_desktop_window")) {
1430 		return;
1431 	}
1432 
1433 	if (!dnd_info->highlighted) {
1434 		dnd_info->highlighted = TRUE;
1435 		g_signal_connect_after (widget, "draw",
1436 					G_CALLBACK (drag_highlight_draw),
1437 					NULL);
1438 		dnd_highlight_queue_redraw (widget);
1439 	}
1440 }
1441 
1442 static void
1443 stop_dnd_highlight (GtkWidget *widget)
1444 {
1445 	NautilusCanvasDndInfo *dnd_info;
1446 	
1447 	dnd_info = NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info;
1448 
1449 	if (dnd_info->highlighted) {
1450 		g_signal_handlers_disconnect_by_func (widget,
1451 						      drag_highlight_draw,
1452 						      NULL);
1453 		dnd_highlight_queue_redraw (widget);
1454 		dnd_info->highlighted = FALSE;
1455 	}
1456 }
1457 
1458 static gboolean
1459 drag_motion_callback (GtkWidget *widget,
1460 		      GdkDragContext *context,
1461 		      int x, int y,
1462 		      guint32 time)
1463 {
1464 	int action;
1465 
1466 	nautilus_canvas_container_ensure_drag_data (NAUTILUS_CANVAS_CONTAINER (widget), context, time);
1467 	nautilus_canvas_container_position_shadow (NAUTILUS_CANVAS_CONTAINER (widget), x, y);
1468 	nautilus_canvas_dnd_update_drop_target (NAUTILUS_CANVAS_CONTAINER (widget), context, x, y);
1469 	set_up_auto_scroll_if_needed (NAUTILUS_CANVAS_CONTAINER (widget));
1470 	/* Find out what the drop actions are based on our drag selection and
1471 	 * the drop target.
1472 	 */
1473 	action = 0;
1474 	nautilus_canvas_container_get_drop_action (NAUTILUS_CANVAS_CONTAINER (widget), context, x, y,
1475 						 &action);
1476 	if (action != 0) {
1477 		start_dnd_highlight (widget);
1478 	}
1479 	  
1480 	gdk_drag_status (context, action, time);
1481 
1482 	return TRUE;
1483 }
1484 
1485 static gboolean
1486 drag_drop_callback (GtkWidget *widget,
1487 		    GdkDragContext *context,
1488 		    int x,
1489 		    int y,
1490 		    guint32 time,
1491 		    gpointer data)
1492 {
1493 	NautilusCanvasDndInfo *dnd_info;
1494 
1495 	dnd_info = NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info;
1496 
1497 	/* tell the drag_data_received callback that
1498 	   the drop occured and that it can actually
1499 	   process the actions.
1500 	   make sure it is going to be called at least once.
1501 	*/
1502 	dnd_info->drag_info.drop_occured = TRUE;
1503 
1504 	get_data_on_first_target_we_support (widget, context, time, x, y);
1505 	
1506 	return TRUE;
1507 }
1508 
1509 void
1510 nautilus_canvas_dnd_end_drag (NautilusCanvasContainer *container)
1511 {
1512 	NautilusCanvasDndInfo *dnd_info;
1513 
1514 	g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
1515 		
1516 	dnd_info = container->details->dnd_info;
1517 	g_return_if_fail (dnd_info != NULL);
1518 	stop_auto_scroll (container);
1519 	/* Do nothing.
1520 	 * Can that possibly be right?
1521 	 */
1522 }
1523 
1524 /** this callback is called in 2 cases.
1525     It is called upon drag_motion events to get the actual data 
1526     In that case, it just makes sure it gets the data.
1527     It is called upon drop_drop events to execute the actual 
1528     actions on the received action. In that case, it actually first makes sure
1529     that we have got the data then processes it.
1530 */
1531 
1532 static void
1533 drag_data_received_callback (GtkWidget *widget,
1534 			     GdkDragContext *context,
1535 			     int x,
1536 			     int y,
1537 			     GtkSelectionData *data,
1538 			     guint info,
1539 			     guint32 time,
1540 			     gpointer user_data)
1541 {
1542     	NautilusDragInfo *drag_info;
1543 	guchar *tmp;
1544 	const guchar *tmp_raw;
1545 	int length;
1546 	gboolean success;
1547 
1548 	drag_info = &(NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info->drag_info);
1549 
1550 	drag_info->got_drop_data_type = TRUE;
1551 	drag_info->data_type = info;
1552 
1553 	switch (info) {
1554 	case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
1555 		nautilus_canvas_container_dropped_canvas_feedback (widget, data, x, y);
1556 		break;
1557 	case NAUTILUS_ICON_DND_URI_LIST:
1558 	case NAUTILUS_ICON_DND_TEXT:
1559 	case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
1560 	case NAUTILUS_ICON_DND_RAW:
1561 		/* Save the data so we can do the actual work on drop. */
1562 		if (drag_info->selection_data != NULL) {
1563 			gtk_selection_data_free (drag_info->selection_data);
1564 		}
1565 		drag_info->selection_data = gtk_selection_data_copy (data);
1566 		break;
1567 
1568 	/* Netscape keeps sending us the data, even though we accept the first drag */
1569 	case NAUTILUS_ICON_DND_NETSCAPE_URL:
1570 		if (drag_info->selection_data != NULL) {
1571 			gtk_selection_data_free (drag_info->selection_data);
1572 			drag_info->selection_data = gtk_selection_data_copy (data);
1573 		}
1574 		break;
1575 	case NAUTILUS_ICON_DND_ROOTWINDOW_DROP:
1576 		/* Do nothing, this won't even happen, since we don't want to call get_data twice */
1577 		break;
1578 	}
1579 
1580 	/* this is the second use case of this callback.
1581 	 * we have to do the actual work for the drop.
1582 	 */
1583 	if (drag_info->drop_occured) {
1584 
1585 		success = FALSE;
1586 		switch (info) {
1587 		case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
1588 			nautilus_canvas_container_receive_dropped_icons
1589 				(NAUTILUS_CANVAS_CONTAINER (widget),
1590 				 context, x, y);
1591 			break;
1592 		case NAUTILUS_ICON_DND_NETSCAPE_URL:
1593 			receive_dropped_netscape_url
1594 				(NAUTILUS_CANVAS_CONTAINER (widget),
1595 				 (char *) gtk_selection_data_get_data (data), context, x, y);
1596 			success = TRUE;
1597 			break;
1598 		case NAUTILUS_ICON_DND_URI_LIST:
1599 			receive_dropped_uri_list
1600 				(NAUTILUS_CANVAS_CONTAINER (widget),
1601 				 (char *) gtk_selection_data_get_data (data), context, x, y);
1602 			success = TRUE;
1603 			break;
1604 		case NAUTILUS_ICON_DND_TEXT:
1605 			tmp = gtk_selection_data_get_text (data);
1606 			receive_dropped_text
1607 				(NAUTILUS_CANVAS_CONTAINER (widget),
1608 				 (char *) tmp, context, x, y);
1609 			success = TRUE;
1610 			g_free (tmp);
1611 			break;
1612 		case NAUTILUS_ICON_DND_RAW:
1613 			length = gtk_selection_data_get_length (data);
1614 			tmp_raw = gtk_selection_data_get_data (data);
1615 			receive_dropped_raw
1616 				(NAUTILUS_CANVAS_CONTAINER (widget),
1617 				 (const gchar *) tmp_raw, length, drag_info->direct_save_uri,
1618 				 context, x, y);
1619 			success = TRUE;
1620 			break;
1621 		case NAUTILUS_ICON_DND_ROOTWINDOW_DROP:
1622 			/* Do nothing, everything is done by the sender */
1623 			break;
1624 		case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
1625 		{
1626 			const guchar *selection_data;
1627 			gint selection_length;
1628 			gint selection_format;
1629 
1630 			selection_data = gtk_selection_data_get_data (drag_info->selection_data);
1631 			selection_length = gtk_selection_data_get_length (drag_info->selection_data);
1632 			selection_format = gtk_selection_data_get_format (drag_info->selection_data);
1633 
1634 			if (selection_format == 8 &&
1635 			    selection_length == 1 &&
1636 			    selection_data[0] == 'F') {
1637 				gtk_drag_get_data (widget, context,
1638 				                  gdk_atom_intern (NAUTILUS_ICON_DND_RAW_TYPE,
1639 				                                   FALSE),
1640 				                  time);
1641 				return;
1642 			} else if (selection_format == 8 &&
1643 				   selection_length == 1 &&
1644 				   selection_data[0] == 'F' &&
1645 			           drag_info->direct_save_uri != NULL) {
1646 				GdkPoint p;
1647 				GFile *location;
1648 
1649 				location = g_file_new_for_uri (drag_info->direct_save_uri);
1650 
1651 				nautilus_file_changes_queue_file_added (location);
1652 				p.x = x; p.y = y;
1653 				nautilus_file_changes_queue_schedule_position_set (
1654 				                 location,
1655 				                 p,
1656 				                 gdk_screen_get_number (
1657 				                             gtk_widget_get_screen (widget)));
1658 				g_object_unref (location);
1659 				nautilus_file_changes_consume_changes (TRUE);
1660 				success = TRUE;
1661 			}
1662 			break;
1663 		} /* NAUTILUS_ICON_DND_XDNDDIRECTSAVE */
1664 		}
1665 		gtk_drag_finish (context, success, FALSE, time);
1666 		
1667 		nautilus_canvas_container_free_drag_data (NAUTILUS_CANVAS_CONTAINER (widget));
1668 		
1669 		set_drop_target (NAUTILUS_CANVAS_CONTAINER (widget), NULL);
1670 
1671 		/* reinitialise it for the next dnd */
1672 		drag_info->drop_occured = FALSE;
1673 	}
1674 
1675 }
1676 
1677 void
1678 nautilus_canvas_dnd_init (NautilusCanvasContainer *container)
1679 {
1680 	GtkTargetList *targets;
1681 	int n_elements;
1682 	
1683 	g_return_if_fail (container != NULL);
1684 	g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
1685 
1686 
1687 	container->details->dnd_info = g_new0 (NautilusCanvasDndInfo, 1);
1688 	nautilus_drag_init (&container->details->dnd_info->drag_info,
1689 		drag_types, G_N_ELEMENTS (drag_types), TRUE);
1690 
1691 	/* Set up the widget as a drag destination.
1692 	 * (But not a source, as drags starting from this widget will be
1693          * implemented by dealing with events manually.)
1694 	 */
1695 	n_elements = G_N_ELEMENTS (drop_types);
1696 	if (!nautilus_canvas_container_get_is_desktop (container)) {
1697 		/* Don't set up rootwindow drop */
1698 		n_elements -= 1;
1699 	}
1700 	gtk_drag_dest_set (GTK_WIDGET (container),
1701 			   0,
1702 			   drop_types, n_elements,
1703 			   GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK);
1704 
1705 	targets = gtk_drag_dest_get_target_list (GTK_WIDGET (container));
1706 	gtk_target_list_add_text_targets (targets, NAUTILUS_ICON_DND_TEXT);
1707 
1708 	
1709 	/* Messages for outgoing drag. */
1710 	g_signal_connect (container, "drag_begin", 
1711 			  G_CALLBACK (drag_begin_callback), NULL);
1712 	g_signal_connect (container, "drag_data_get",
1713 			  G_CALLBACK (drag_data_get_callback), NULL);
1714 	g_signal_connect (container, "drag_end",
1715 			  G_CALLBACK (drag_end_callback), NULL);
1716 	
1717 	/* Messages for incoming drag. */
1718 	g_signal_connect (container, "drag_data_received",
1719 			  G_CALLBACK (drag_data_received_callback), NULL);
1720 	g_signal_connect (container, "drag_motion",
1721 			  G_CALLBACK (drag_motion_callback), NULL);
1722 	g_signal_connect (container, "drag_drop",
1723 			  G_CALLBACK (drag_drop_callback), NULL);
1724 	g_signal_connect (container, "drag_leave",
1725 			  G_CALLBACK (drag_leave_callback), NULL);
1726 }
1727 
1728 void
1729 nautilus_canvas_dnd_fini (NautilusCanvasContainer *container)
1730 {
1731 	g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
1732 
1733 	if (container->details->dnd_info != NULL) {
1734 		stop_auto_scroll (container);
1735 
1736 		nautilus_drag_finalize (&container->details->dnd_info->drag_info);
1737 		container->details->dnd_info = NULL;
1738 	}
1739 }