gnome-icon-container-dnd.c 19.7 KB
Newer Older
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1 2 3 4
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* gnome-icon-container-dnd.c - Drag & drop handling for the icon container
   widget.

Elliot Lee's avatar
Elliot Lee committed
5
   Copyright (C) 1999, 2000 Free Software Foundation
6 7
   Copyright (C) 2000 Eazel, Inc.
   
Ettore Perazzoli's avatar
Ettore Perazzoli committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   The Gnome Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The Gnome Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Author: Ettore Perazzoli <ettore@gnu.org>
*/

26 27
#include <config.h>
#include "gnome-icon-container-dnd.h"
Ettore Perazzoli's avatar
Ettore Perazzoli committed
28

29
#include <math.h>
30 31
#include <string.h>
#include <stdio.h>
32
#include "nautilus-glib-extensions.h"
33
#include <gtk/gtksignal.h>
34 35 36 37
#include "nautilus-gtk-extensions.h"
#include "nautilus-background.h"
#include "gnome-icon-container-private.h"
#include <libgnomeui/gnome-canvas-rect-ellipse.h>
38

39
typedef struct {
40
	char *uri;
41 42 43 44
	gboolean got_icon_position;
	int icon_x, icon_y;
	int icon_width, icon_height;
} DndSelectionItem;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
45 46 47 48

static GtkTargetEntry drag_types [] = {
	{ GNOME_ICON_CONTAINER_DND_GNOME_ICON_LIST_TYPE, 0, GNOME_ICON_CONTAINER_DND_GNOME_ICON_LIST },
	{ GNOME_ICON_CONTAINER_DND_URI_LIST_TYPE, 0, GNOME_ICON_CONTAINER_DND_URI_LIST },
49
	{ GNOME_ICON_CONTAINER_DND_URL_TYPE, 0, GNOME_ICON_CONTAINER_DND_URL }
Ettore Perazzoli's avatar
Ettore Perazzoli committed
50 51 52 53 54
};

static GtkTargetEntry drop_types [] = {
	{ GNOME_ICON_CONTAINER_DND_GNOME_ICON_LIST_TYPE, 0, GNOME_ICON_CONTAINER_DND_GNOME_ICON_LIST },
	{ GNOME_ICON_CONTAINER_DND_URI_LIST_TYPE, 0, GNOME_ICON_CONTAINER_DND_URI_LIST },
55 56
	{ GNOME_ICON_CONTAINER_DND_URL_TYPE, 0, GNOME_ICON_CONTAINER_DND_URL },
	{ GNOME_ICON_CONTAINER_DND_COLOR_TYPE, 0, GNOME_ICON_CONTAINER_DND_COLOR }
Ettore Perazzoli's avatar
Ettore Perazzoli committed
57 58 59 60 61 62 63 64 65
};

static GnomeCanvasItem *
create_selection_shadow (GnomeIconContainer *container,
			 GList *list)
{
	GnomeCanvasGroup *group;
	GnomeCanvas *canvas;
	GdkBitmap *stipple;
66 67
	int max_x, max_y;
	int min_x, min_y;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
68
	GList *p;
69
	double pixels_per_unit;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
70 71

	if (list == NULL)
72
	    return NULL;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
73

74
	stipple = container->details->dnd_info->stipple;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
75 76 77 78 79 80 81 82
	g_return_val_if_fail (stipple != NULL, NULL);

	canvas = GNOME_CANVAS (container);

	/* Creating a big set of rectangles in the canvas can be expensive, so
           we try to be smart and only create the maximum number of rectangles
           that we will need, in the vertical/horizontal directions.  */

83
	/* FIXME: Does this work properly if the window is scrolled? */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
84 85 86 87 88 89 90 91 92 93 94 95 96
	max_x = GTK_WIDGET (container)->allocation.width;
	min_x = -max_x;

	max_y = GTK_WIDGET (container)->allocation.height;
	min_y = -max_y;

	/* Create a group, so that it's easier to move all the items around at
           once.  */
	group = GNOME_CANVAS_GROUP
		(gnome_canvas_item_new (GNOME_CANVAS_GROUP (canvas->root),
					gnome_canvas_group_get_type (),
					NULL));
	
97
	pixels_per_unit = canvas->pixels_per_unit;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
98 99
	for (p = list; p != NULL; p = p->next) {
		DndSelectionItem *item;
100
		int x1, y1, x2, y2;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
101 102 103

		item = p->data;

104 105 106 107 108 109 110
		if (!item->got_icon_position)
			continue;

		x1 = item->icon_x;
		y1 = item->icon_y;
		x2 = x1 + item->icon_width;
		y2 = y1 + item->icon_height;
111
			
112 113
		if (x2 >= min_x && x1 <= max_x && y2 >= min_y && y1 <= max_y)
			gnome_canvas_item_new
Ettore Perazzoli's avatar
Ettore Perazzoli committed
114 115
				(group,
				 gnome_canvas_rect_get_type (),
116 117 118 119
				 "x1", (double) x1 / pixels_per_unit,
				 "y1", (double) y1 / pixels_per_unit,
				 "x2", (double) x2 / pixels_per_unit,
				 "y2", (double) y2 / pixels_per_unit,
Ettore Perazzoli's avatar
Ettore Perazzoli committed
120 121 122 123 124 125 126 127 128
				 "outline_color", "black",
				 "outline_stipple", stipple,
				 "width_pixels", 1,
				 NULL);
	}

	return GNOME_CANVAS_ITEM (group);
}

129 130 131
/* Set the affine instead of the x and y position.
 * Simple, and setting x and y was broken at one point.
 */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
132 133
static void
set_shadow_position (GnomeCanvasItem *shadow,
134
		     double x, double y)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
135 136 137 138 139 140 141 142 143 144 145 146 147 148
{
	double affine[6];

	affine[0] = 1.0;
	affine[1] = 0.0;
	affine[2] = 0.0;
	affine[3] = 1.0;
	affine[4] = x;
	affine[5] = y;

	gnome_canvas_item_affine_absolute (shadow, affine);
}


149

Ettore Perazzoli's avatar
Ettore Perazzoli committed
150 151 152 153 154 155 156
/* Functions to deal with DndSelectionItems.  */

static DndSelectionItem *
dnd_selection_item_new (void)
{
	DndSelectionItem *new;

157
	new = g_new0 (DndSelectionItem, 1);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
	
	return new;
}

static void
dnd_selection_item_destroy (DndSelectionItem *item)
{
	g_free (item->uri);
	g_free (item);
}

static void
destroy_selection_list (GList *list)
{
	GList *p;

	if (list == NULL)
		return;

	for (p = list; p != NULL; p = p->next)
178
		dnd_selection_item_destroy (p->data);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
179 180

	g_list_free (list);
181 182
}

Ettore Perazzoli's avatar
Ettore Perazzoli committed
183 184
/* Source-side handling of the drag.  */

185 186 187 188
/* Encode a "special/x-gnome-icon-list" selection.
   Along with the URIs of the dragged files, this encodes
   the location and size of each icon relative to the cursor.
*/
Ettore Perazzoli's avatar
Ettore Perazzoli committed
189 190 191 192
static void
set_gnome_icon_list_selection (GnomeIconContainer *container,
			       GtkSelectionData *selection_data)
{
193
	GnomeIconContainerDetails *details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
194 195 196
	GList *p;
	GString *data;

197
	details = container->details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
198 199

	data = g_string_new (NULL);
200
	for (p = details->icons; p != NULL; p = p->next) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
201
		GnomeIconContainerIcon *icon;
202
		ArtIRect rect;
203 204
		char *uri;
		char *s;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
205 206

		icon = p->data;
207
		if (!icon->is_selected)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
208 209
			continue;

210 211 212 213 214 215 216 217 218 219 220
		nautilus_icons_view_icon_item_get_icon_window_rectangle
			(icon->item, &rect);
		uri = nautilus_icons_controller_get_icon_uri
			(details->controller, icon->data);

		s = g_strdup_printf ("%s\r%d:%d:%hu:%hu\r\n",
				     uri,
				     (int) (rect.x0 - details->dnd_info->start_x),
				     (int) (rect.y0 - details->dnd_info->start_y),
				     rect.x1 - rect.x0,
				     rect.y1 - rect.y0);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
221

222
		g_free (uri);
223

Ettore Perazzoli's avatar
Ettore Perazzoli committed
224 225 226 227 228 229
		g_string_append (data, s);
		g_free (s);
	}

	gtk_selection_data_set (selection_data,
				selection_data->target,
230
				8, data->str, data->len);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
231 232 233 234 235 236 237 238 239

	g_string_free (data, TRUE);
}

/* Encode a "text/uri-list" selection.  */
static void
set_uri_list_selection (GnomeIconContainer *container,
			GtkSelectionData *selection_data)
{
240
	GnomeIconContainerDetails *details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
241
	GList *p;
242
	char *uri;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
243 244
	GString *data;

245
	details = container->details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
246 247 248

	data = g_string_new (NULL);

249
	for (p = details->icons; p != NULL; p = p->next) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
250 251 252
		GnomeIconContainerIcon *icon;

		icon = p->data;
253
		if (!icon->is_selected)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
254 255
			continue;

256 257 258 259
		uri = nautilus_icons_controller_get_icon_uri (details->controller, icon->data);
		g_string_append (data, uri);
		g_free (uri);

260
		g_string_append (data, "\r\n");
Ettore Perazzoli's avatar
Ettore Perazzoli committed
261 262 263 264
	}

	gtk_selection_data_set (selection_data,
				selection_data->target,
265
				8, data->str, data->len);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306

	g_string_free (data, TRUE);
}

static void
drag_data_get_cb (GtkWidget *widget,
		  GdkDragContext *context,
		  GtkSelectionData *selection_data,
		  guint info,
		  guint32 time,
		  gpointer data)
{
	GnomeIconContainer *container;

	g_return_if_fail (widget != NULL);
	g_return_if_fail (GNOME_IS_ICON_CONTAINER (widget));
	g_return_if_fail (context != NULL);

	container = GNOME_ICON_CONTAINER (widget);

	switch (info) {
	case GNOME_ICON_CONTAINER_DND_GNOME_ICON_LIST:
		set_gnome_icon_list_selection (container, selection_data);
		break;
	case GNOME_ICON_CONTAINER_DND_URI_LIST:
		set_uri_list_selection (container, selection_data);
		break;
	default:
		g_assert_not_reached ();
	}
}


/* Target-side handling of the drag.  */

static void
get_gnome_icon_list_selection (GnomeIconContainer *container,
			       GtkSelectionData *data)
{
	GnomeIconContainerDndInfo *dnd_info;
	const guchar *p, *oldp;
307
	int size;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
308

309
	dnd_info = container->details->dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357

	oldp = data->data;
	size = data->length;

	while (1) {
		DndSelectionItem *item;
		guint len;

		/* The list is in the form:

		   name\rx:y:width:height\r\n

		   The geometry information after the first \r is optional.  */

		/* 1: Decode name. */

		p = memchr (oldp, '\r', size);
		if (p == NULL)
			break;

		item = dnd_selection_item_new ();

		len = p - oldp;

		item->uri = g_malloc (len + 1);
		memcpy (item->uri, oldp, len);
		item->uri[len] = 0;

		p++;
		if (*p == '\n' || *p == '\0') {
			dnd_info->selection_list
				= g_list_prepend (dnd_info->selection_list,
						  item);
			if (p == 0) {
				g_warning ("Invalid special/x-gnome-icon-list data received: "
					   "missing newline character.");
				break;
			} else {
				oldp = p + 1;
				continue;
			}
		}

		size -= p - oldp;
		oldp = p;

		/* 2: Decode geometry information.  */

358 359
		item->got_icon_position = sscanf (p, "%d:%d:%d:%d%*s",
						  &item->icon_x, &item->icon_y,
360
						  &item->icon_width, &item->icon_height) == 4;
361
		if (!item->got_icon_position)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
362
			g_warning ("Invalid special/x-gnome-icon-list data received: "
363
				   "invalid icon position specification.");
Ettore Perazzoli's avatar
Ettore Perazzoli committed
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383

		dnd_info->selection_list
			= g_list_prepend (dnd_info->selection_list, item);

		p = memchr (p, '\r', size);
		if (p == NULL || p[1] != '\n') {
			g_warning ("Invalid special/x-gnome-icon-list data received: "
				   "missing newline character.");
			if (p == NULL)
				break;
		} else {
			p += 2;
		}

		size -= p - oldp;
		oldp = p;
	}
}

static void
384 385 386 387 388 389
gnome_icon_container_position_shadow (GnomeIconContainer *container,
				      int x, int y)
{
	GnomeCanvasItem *shadow;
	double world_x, world_y;

390
	shadow = container->details->dnd_info->shadow;
391 392 393 394 395 396
	if (shadow == NULL)
		return;

	gnome_canvas_window_to_world (GNOME_CANVAS (container),
				      x, y, &world_x, &world_y);
	set_shadow_position (shadow, world_x, world_y);
397
	gnome_canvas_item_show (shadow);
398 399 400 401 402 403
}

static void
gnome_icon_container_dropped_icon_feedback (GtkWidget *widget,
					    GtkSelectionData *data,
					    int x, int y)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
404 405 406 407 408
{
	GnomeIconContainer *container;
	GnomeIconContainerDndInfo *dnd_info;

	container = GNOME_ICON_CONTAINER (widget);
409
	dnd_info = container->details->dnd_info;
410

411
	/* Delete old selection list if any. */
412 413 414 415 416
	if (dnd_info->selection_list != NULL) {
		destroy_selection_list (dnd_info->selection_list);
		dnd_info->selection_list = NULL;
	}

417
	/* Delete old shadow if any. */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
418 419
	if (dnd_info->shadow != NULL)
		gtk_object_destroy (GTK_OBJECT (dnd_info->shadow));
420 421 422 423 424

	/* Build the selection list and the shadow. */
	get_gnome_icon_list_selection (container, data);		
	dnd_info->shadow = create_selection_shadow (container, dnd_info->selection_list);
	gnome_icon_container_position_shadow (container, x, y);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
425 426
}

427 428 429
static void
drag_data_received_cb (GtkWidget *widget,
		       GdkDragContext *context,
430 431
		       int x,
		       int y,
432 433 434 435
		       GtkSelectionData *data,
		       guint info,
		       guint32 time,
		       gpointer user_data)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
436
{
437
    	GnomeIconContainerDndInfo *dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
438

439
	dnd_info = GNOME_ICON_CONTAINER (widget)->details->dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
440

441 442
	dnd_info->got_data_type = TRUE;
	dnd_info->data_type = info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
443

444 445 446 447 448 449 450 451 452 453
	switch (info) {
	case GNOME_ICON_CONTAINER_DND_GNOME_ICON_LIST:
		gnome_icon_container_dropped_icon_feedback (widget, data, x, y);
		break;
	case GNOME_ICON_CONTAINER_DND_COLOR:
		/* Save the data so we can do the actual work on drop. */
		dnd_info->selection_data = nautilus_gtk_selection_data_copy_deep (data);
		break;
	default:
		g_warning ("drag_data_received_cb received data but could not handle it");
Ettore Perazzoli's avatar
Ettore Perazzoli committed
454
	}
455 456 457 458 459 460 461 462 463
}

static void
gnome_icon_container_ensure_drag_data (GnomeIconContainer *container,
				       GdkDragContext *context,
				       guint32 time)
{
	GnomeIconContainerDndInfo *dnd_info;

464
	dnd_info = container->details->dnd_info;
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479

	if (!dnd_info->got_data_type)
		gtk_drag_get_data (GTK_WIDGET (container), context,
				   GPOINTER_TO_INT (context->targets->data),
				   time);
}

static gboolean
drag_motion_cb (GtkWidget *widget,
		GdkDragContext *context,
		int x, int y,
		guint32 time)
{
	gnome_icon_container_ensure_drag_data (GNOME_ICON_CONTAINER (widget), context, time);
	gnome_icon_container_position_shadow (GNOME_ICON_CONTAINER (widget), x, y);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
480 481

	gdk_drag_status (context, context->suggested_action, time);
482

Ettore Perazzoli's avatar
Ettore Perazzoli committed
483 484 485 486 487 488 489 490 491 492 493 494
	return TRUE;
}

static void
drag_end_cb (GtkWidget *widget,
	     GdkDragContext *context,
	     gpointer data)
{
	GnomeIconContainer *container;
	GnomeIconContainerDndInfo *dnd_info;

	container = GNOME_ICON_CONTAINER (widget);
495
	dnd_info = container->details->dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
496 497 498 499 500

	destroy_selection_list (dnd_info->selection_list);
	dnd_info->selection_list = NULL;
}

501 502 503
/* Utility routine to extract the directory from an item_uri
   (which may have geometry info attached).
*/
504

505 506
static void
gnome_icon_container_receive_dropped_icons (GnomeIconContainer *container,
507 508
					    GdkDragContext *context,
					    int x, int y)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
509 510
{
	GnomeIconContainerDndInfo *dnd_info;
511
	GList *p;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
512

513
	dnd_info = container->details->dnd_info;
514 515 516
	if (dnd_info->selection_list == NULL)
		return;

517 518 519 520 521
	/* Move files. */
	if (context->action != GDK_ACTION_MOVE) {
		/* FIXME: We want to copy files here, I think. */
		g_warning ("non-move action not implemented yet");
	} else {
522
		GList *icons_to_select;
523
		
524 525 526 527
		icons_to_select = NULL;
		for (p = dnd_info->selection_list; p != NULL; p = p->next) {
			DndSelectionItem *item;
			GnomeIconContainerIcon *icon;
528
			
529 530 531
			item = p->data;
			icon = gnome_icon_container_get_icon_by_uri
				(container, item->uri);
532
			
533
			if (icon == NULL) {
534 535 536
				/* FIXME: Do we ever get a MOVE between windows?
				 * If so, we need to move files here.
				 */
537 538 539
				g_warning ("drag between containers not implemented yet");
				continue;
			}
540
			
541
			if (item->got_icon_position) {
542
				double world_x, world_y;
543
				
544 545 546 547
				gnome_canvas_window_to_world (GNOME_CANVAS (container),
							      x + item->icon_x,
							      y + item->icon_y,
							      &world_x, &world_y);
548
				gnome_icon_container_move_icon
549
					(container, icon, world_x, world_y, TRUE);
550
			}
551
			
552 553
			icons_to_select = g_list_prepend (icons_to_select, icon);
		}
554
		
555 556 557
		gnome_icon_container_select_list_unselect_others (container, icons_to_select);

		g_list_free (icons_to_select);
558 559 560 561
	}
	
	destroy_selection_list (dnd_info->selection_list);
	dnd_info->selection_list = NULL;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
562 563 564
}

static void
565
gnome_icon_container_free_drag_data (GnomeIconContainer *container)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
566 567
{
	GnomeIconContainerDndInfo *dnd_info;
568
	
569
	dnd_info = container->details->dnd_info;
570 571 572
	
	dnd_info->got_data_type = FALSE;
	
Ettore Perazzoli's avatar
Ettore Perazzoli committed
573 574 575 576 577
	if (dnd_info->shadow != NULL) {
		gtk_object_destroy (GTK_OBJECT (dnd_info->shadow));
		dnd_info->shadow = NULL;
	}

578 579 580 581 582 583 584 585 586
	if (dnd_info->selection_data != NULL) {
		nautilus_gtk_selection_data_free_deep (dnd_info->selection_data);
		dnd_info->selection_data = NULL;
	}
}

static gboolean
drag_drop_cb (GtkWidget *widget,
	      GdkDragContext *context,
587 588
	      int x,
	      int y,
589 590 591 592 593
	      guint32 time,
	      gpointer data)
{
	GnomeIconContainerDndInfo *dnd_info;

594
	dnd_info = GNOME_ICON_CONTAINER (widget)->details->dnd_info;
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617

	gnome_icon_container_ensure_drag_data (GNOME_ICON_CONTAINER (widget), context, time);

	g_assert (dnd_info->got_data_type);
	switch (dnd_info->data_type) {
	case GNOME_ICON_CONTAINER_DND_GNOME_ICON_LIST:
		gnome_icon_container_receive_dropped_icons
			(GNOME_ICON_CONTAINER (widget),
			 context, x, y);
		break;
	case GNOME_ICON_CONTAINER_DND_COLOR:
		nautilus_background_receive_dropped_color
			(nautilus_get_widget_background (widget),
			 widget, x, y, dnd_info->selection_data);
		gtk_drag_finish (context, TRUE, FALSE, time);
		break;
	default:
		g_warning ("drag_drop_cb received data but could not handle it");
	}

	gnome_icon_container_free_drag_data (GNOME_ICON_CONTAINER (widget));

	return FALSE;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
618 619
}

620 621 622 623 624 625
static void
drag_leave_cb (GtkWidget *widget,
	       GdkDragContext *context,
	       guint32 time,
	       gpointer data)
{
626 627 628 629 630 631
	GnomeIconContainerDndInfo *dnd_info;

	dnd_info = GNOME_ICON_CONTAINER (widget)->details->dnd_info;

	if (dnd_info->shadow != NULL)
		gnome_canvas_item_hide (dnd_info->shadow);
632
}
633

Ettore Perazzoli's avatar
Ettore Perazzoli committed
634 635 636 637 638 639 640 641 642
void
gnome_icon_container_dnd_init (GnomeIconContainer *container,
			       GdkBitmap *stipple)
{
	GnomeIconContainerDndInfo *dnd_info;

	g_return_if_fail (container != NULL);
	g_return_if_fail (GNOME_IS_ICON_CONTAINER (container));

643
	dnd_info = g_new0 (GnomeIconContainerDndInfo, 1);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
644 645

	dnd_info->target_list = gtk_target_list_new (drag_types,
646
						     NAUTILUS_N_ELEMENTS (drag_types));
Ettore Perazzoli's avatar
Ettore Perazzoli committed
647 648 649 650 651 652 653 654 655 656

	dnd_info->stipple = gdk_bitmap_ref (stipple);


	/* Set up the widget as a drag destination.  */
	/* (But not a source, as drags starting from this widget will be
           implemented by dealing with events manually.)  */

	gtk_drag_dest_set  (GTK_WIDGET (container),
			    0,
657
			    drop_types, NAUTILUS_N_ELEMENTS (drop_types),
Ettore Perazzoli's avatar
Ettore Perazzoli committed
658 659
			    GDK_ACTION_COPY | GDK_ACTION_MOVE);

660 661
	/* Messages for outgoing drag. */

Ettore Perazzoli's avatar
Ettore Perazzoli committed
662 663 664 665
	gtk_signal_connect (GTK_OBJECT (container), "drag_data_get",
			    GTK_SIGNAL_FUNC (drag_data_get_cb), NULL);
	gtk_signal_connect (GTK_OBJECT (container), "drag_end",
			    GTK_SIGNAL_FUNC (drag_end_cb), NULL);
666 667 668

	/* Messages for incoming drag. */

Ettore Perazzoli's avatar
Ettore Perazzoli committed
669 670
	gtk_signal_connect (GTK_OBJECT (container), "drag_data_received",
			    GTK_SIGNAL_FUNC (drag_data_received_cb), NULL);
671 672
	gtk_signal_connect (GTK_OBJECT (container), "drag_motion",
			    GTK_SIGNAL_FUNC (drag_motion_cb), NULL);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
673 674 675 676 677
	gtk_signal_connect (GTK_OBJECT (container), "drag_drop",
			    GTK_SIGNAL_FUNC (drag_drop_cb), NULL);
	gtk_signal_connect (GTK_OBJECT (container), "drag_leave",
			    GTK_SIGNAL_FUNC (drag_leave_cb), NULL);

678
	container->details->dnd_info = dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
679 680 681 682 683 684 685 686 687 688
}

void
gnome_icon_container_dnd_fini (GnomeIconContainer *container)
{
	GnomeIconContainerDndInfo *dnd_info;

	g_return_if_fail (container != NULL);
	g_return_if_fail (GNOME_IS_ICON_CONTAINER (container));

689
	dnd_info = container->details->dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
	g_return_if_fail (dnd_info != NULL);

	gtk_target_list_unref (dnd_info->target_list);
	destroy_selection_list (dnd_info->selection_list);

	if (dnd_info->shadow != NULL)
		gtk_object_destroy (GTK_OBJECT (dnd_info->shadow));

	gdk_bitmap_unref (dnd_info->stipple);

	g_free (dnd_info);
}

void
gnome_icon_container_dnd_begin_drag (GnomeIconContainer *container,
				     GdkDragAction actions,
706
				     int button,
Ettore Perazzoli's avatar
Ettore Perazzoli committed
707 708 709
				     GdkEventMotion *event)
{
	GnomeIconContainerDndInfo *dnd_info;
710
	GnomeCanvas *canvas;
711 712 713 714
	GdkDragContext *context;
	GtkArg pixbuf_arg;
	GnomeCanvasItem *item;
	GdkPixbuf *pixbuf;
715 716
	GdkPixmap *pixmap_for_dragged_file;
	GdkBitmap *mask_for_dragged_file;
717 718
	int x_offset, y_offset;
	ArtIRect rect;
719
	
Ettore Perazzoli's avatar
Ettore Perazzoli committed
720 721 722
	g_return_if_fail (GNOME_IS_ICON_CONTAINER (container));
	g_return_if_fail (event != NULL);

723
	dnd_info = container->details->dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
724
	g_return_if_fail (dnd_info != NULL);
725
	
726 727 728 729 730 731 732
	/* Notice that the event is in world coordinates, because of
           the way the canvas handles events.
	*/
	canvas = GNOME_CANVAS (container);
	gnome_canvas_world_to_window (canvas,
				      event->x, event->y,
				      &dnd_info->start_x, &dnd_info->start_y);
733
	
734
	/* start the drag */
735
	context = gtk_drag_begin (GTK_WIDGET (container),
736 737 738 739
				  dnd_info->target_list,
				  actions,
				  button,
				  (GdkEvent *) event);
740
	  
741
        /* create a pixmap and mask to drag with */
742 743 744 745 746 747 748 749 750
        item = GNOME_CANVAS_ITEM (container->details->drag_icon->item);
        pixbuf_arg.name = "NautilusIconsViewIconItem::pixbuf";
        gtk_object_getv (GTK_OBJECT (item), 1, &pixbuf_arg);
        pixbuf = GTK_VALUE_BOXED (pixbuf_arg);
        gdk_pixbuf_render_pixmap_and_mask (pixbuf,
					   &pixmap_for_dragged_file,
					   &mask_for_dragged_file,
					   128);
	
751
        /* compute the image's offset */
752 753 754 755
	nautilus_icons_view_icon_item_get_icon_window_rectangle
		(container->details->drag_icon->item, &rect);
        x_offset = dnd_info->start_x - rect.x0;
        y_offset = dnd_info->start_y - rect.y0;
756
        
757
        /* set the pixmap and mask for dragging */
758 759
        gtk_drag_set_icon_pixmap (context, gtk_widget_get_colormap (GTK_WIDGET (container)),
				  pixmap_for_dragged_file, mask_for_dragged_file,
760
				  x_offset, y_offset);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
761 762 763 764 765 766 767 768 769
}

void
gnome_icon_container_dnd_end_drag (GnomeIconContainer *container)
{
	GnomeIconContainerDndInfo *dnd_info;

	g_return_if_fail (GNOME_IS_ICON_CONTAINER (container));

770
	dnd_info = container->details->dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
771 772
	g_return_if_fail (dnd_info != NULL);
}