nautilus-icon-dnd.c 36.5 KB
Newer Older
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3

/* nautilus-icon-dnd.c - Drag & drop handling for the icon container widget.
Ettore Perazzoli's avatar
Ettore Perazzoli committed
4

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
   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.

23
24
25
   Authors: Ettore Perazzoli <ettore@gnu.org>,
            Darin Adler <darin@eazel.com>,
	    Andy Hertzfeld <andy@eazel.com>
26
	    Pavel Cisler <pavel@eazel.com>
Ettore Perazzoli's avatar
Ettore Perazzoli committed
27
28
*/

29

30
#include <config.h>
31
#include "nautilus-icon-dnd.h"
Ettore Perazzoli's avatar
Ettore Perazzoli committed
32

33
#include <math.h>
34
35
#include <string.h>
#include <stdio.h>
36
37
#include <gdk/gdkx.h>
#include <gdk/gdkkeysyms.h>
38
#include <gtk/gtksignal.h>
Pavel Cisler's avatar
Pavel Cisler committed
39
#include <gtk/gtkmain.h>
Pavel Cisler's avatar
Pavel Cisler committed
40
#include <libgnomevfs/gnome-vfs-uri.h>
41
42
43
#include <libgnome/gnome-i18n.h>
#include <libgnomeui/gnome-stock.h>
#include <libgnomeui/gnome-canvas-rect-ellipse.h>
44
#include "nautilus-glib-extensions.h"
45
#include "nautilus-gtk-extensions.h"
46
#include "nautilus-gtk-macros.h"
47
#include "nautilus-gnome-extensions.h"
48
#include "nautilus-background.h"
49
#include "nautilus-graphic-effects.h"
50
#include "nautilus-stock-dialogs.h"
51

52
#include "nautilus-icon-private.h"
53

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
static gboolean drag_drop_callback                   (GtkWidget             *widget,
						      GdkDragContext        *context,
						      int                    x,
						      int                    y,
						      guint32                time,
						      gpointer               data);
static void     nautilus_icon_dnd_update_drop_target (NautilusIconContainer *container,
						      GdkDragContext        *context,
						      int                    x,
						      int                    y);
static gboolean drag_motion_callback                 (GtkWidget             *widget,
						      GdkDragContext        *context,
						      int                    x,
						      int                    y,
						      guint32                time);
69

Ettore Perazzoli's avatar
Ettore Perazzoli committed
70
static GtkTargetEntry drag_types [] = {
71
	{ NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
72
73
	{ NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
	{ NAUTILUS_ICON_DND_URL_TYPE, 0, NAUTILUS_ICON_DND_URL }
Ettore Perazzoli's avatar
Ettore Perazzoli committed
74
75
76
};

static GtkTargetEntry drop_types [] = {
77
	{ NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
78
79
	{ NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
	{ NAUTILUS_ICON_DND_URL_TYPE, 0, NAUTILUS_ICON_DND_URL },
80
	{ NAUTILUS_ICON_DND_COLOR_TYPE, 0, NAUTILUS_ICON_DND_COLOR },
81
82
	{ NAUTILUS_ICON_DND_BGIMAGE_TYPE, 0, NAUTILUS_ICON_DND_BGIMAGE },
	{ NAUTILUS_ICON_DND_KEYWORD_TYPE, 0, NAUTILUS_ICON_DND_KEYWORD }
Ettore Perazzoli's avatar
Ettore Perazzoli committed
83
84
};

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#define AUTOSCROLL_TIMEOUT_INTERVAL 100
	/* in milliseconds */

#define AUTOSCROLL_INITIAL_DELAY 600000
	/* in microseconds */

#define AUTO_SCROLL_MARGIN 20
	/* drag this close to the view edge to start auto scroll*/

#define MIN_AUTOSCROLL_DELTA 5
	/* the smallest amount of auto scroll used when we just enter the autoscroll
	 * margin
	 */
	 
#define MAX_AUTOSCROLL_DELTA 50
	/* the largest amount of auto scroll used when we are right over the view
	 * edge
	 */

Ettore Perazzoli's avatar
Ettore Perazzoli committed
104
static GnomeCanvasItem *
105
create_selection_shadow (NautilusIconContainer *container,
Ettore Perazzoli's avatar
Ettore Perazzoli committed
106
107
108
109
110
			 GList *list)
{
	GnomeCanvasGroup *group;
	GnomeCanvas *canvas;
	GdkBitmap *stipple;
111
112
	int max_x, max_y;
	int min_x, min_y;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
113
	GList *p;
114
	double pixels_per_unit;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
115

116
117
118
	if (list == NULL) {
		return NULL;
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
119

120
	/* if we're only dragging a single item, don't worry about the shadow */
121
	if (list->next == NULL) {
122
		return NULL;
123
	}
124
		
125
	stipple = container->details->dnd_info->drag_info.stipple;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
126
127
128
129
130
131
132
133
	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.  */

John Sullivan's avatar
John Sullivan committed
134
135
136
	/* FIXME bugzilla.eazel.com 624: 
	 * Does this work properly if the window is scrolled? 
	 */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
137
138
139
140
141
142
143
144
145
146
147
148
149
	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));
	
150
	pixels_per_unit = canvas->pixels_per_unit;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
151
	for (p = list; p != NULL; p = p->next) {
152
		DragSelectionItem *item;
153
		int x1, y1, x2, y2;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
154
155
156

		item = p->data;

157
		if (!item->got_icon_position) {
158
			continue;
159
		}
160
161
162
163
164

		x1 = item->icon_x;
		y1 = item->icon_y;
		x2 = x1 + item->icon_width;
		y2 = y1 + item->icon_height;
165
			
166
167
		if (x2 >= min_x && x1 <= max_x && y2 >= min_y && y1 <= max_y)
			gnome_canvas_item_new
Ettore Perazzoli's avatar
Ettore Perazzoli committed
168
169
				(group,
				 gnome_canvas_rect_get_type (),
170
171
172
173
				 "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
174
175
176
177
178
179
180
181
182
				 "outline_color", "black",
				 "outline_stipple", stipple,
				 "width_pixels", 1,
				 NULL);
	}

	return GNOME_CANVAS_ITEM (group);
}

183
184
185
/* 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
186
187
static void
set_shadow_position (GnomeCanvasItem *shadow,
188
		     double x, double y)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
189
190
191
192
193
194
195
196
197
198
199
200
201
{
	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);
}

202

203
/* Source-side handling of the drag. */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
204

205
206
207
208
209
210
211
212
213
/* iteration glue struct */
typedef struct {
	gpointer iterator_context;
	NautilusDragEachSelectedItemDataGet iteratee;
	gpointer iteratee_data;
} IconGetDataBinderContext;

static gboolean
icon_get_data_binder (NautilusIcon *icon, gpointer data)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
214
{
215
216
217
218
219
	IconGetDataBinderContext *context;
	ArtDRect world_rect;
	ArtIRect window_rect;
	char *uri;
	NautilusIconContainer *container;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
220

221
	context = (IconGetDataBinderContext *)data;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
222

223
	g_assert (NAUTILUS_IS_ICON_CONTAINER (context->iterator_context));
Ettore Perazzoli's avatar
Ettore Perazzoli committed
224

225
	container = NAUTILUS_ICON_CONTAINER (context->iterator_context);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
226

227
228
229
230
231
232
233
234
235
	nautilus_icon_canvas_item_get_icon_rectangle
		(icon->item, &world_rect);
	nautilus_gnome_canvas_world_to_window_rectangle
		(GNOME_CANVAS (container), &world_rect, &window_rect);

	uri = nautilus_icon_container_get_icon_uri (container, icon);
	if (uri == NULL) {
		g_warning ("no URI for one of the iterated icons");
		return TRUE;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
236
237
	}

238
239
240
241
242
243
244
	/* pass the uri, mouse-relative x/y and icon width/height */
	context->iteratee (uri, 
			   (int) (window_rect.x0 - container->details->dnd_info->drag_info.start_x),
			   (int) (window_rect.y0 - container->details->dnd_info->drag_info.start_y),
			   window_rect.x1 - window_rect.x0,
			   window_rect.y1 - window_rect.y0,
			   context->iteratee_data);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
245

246
247
248
	g_free (uri);

	return TRUE;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
249
250
}

251
252
253
/* Iterate over each selected icon in a NautilusIconContainer,
 * calling each_function on each.
 */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
254
static void
255
256
nautilus_icon_container_each_selected_icon (NautilusIconContainer *container,
	gboolean (*each_function) (NautilusIcon *, gpointer), gpointer data)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
257
258
{
	GList *p;
259
	NautilusIcon *icon;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
260

261
	for (p = container->details->icons; p != NULL; p = p->next) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
262
		icon = p->data;
263
		if (!icon->is_selected) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
264
			continue;
265
266
267
268
		}
		if (!each_function (icon, data)) {
			return;
		}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
269
	}
270
271
272
273
274
275
276
277
278
279
280
281
}

/* Adaptor function used with nautilus_icon_container_each_selected_icon
 * to help iterate over all selected items, passing uris, x,y,w and h
 * values to the iteratee
 */
static void
each_icon_get_data_binder (NautilusDragEachSelectedItemDataGet iteratee, 
	gpointer iterator_context, gpointer data)
{
	IconGetDataBinderContext context;
	NautilusIconContainer *container;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
282

283
284
	g_assert (NAUTILUS_IS_ICON_CONTAINER (iterator_context));
	container = NAUTILUS_ICON_CONTAINER (iterator_context);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
285

286
287
288
289
	context.iterator_context = iterator_context;
	context.iteratee = iteratee;
	context.iteratee_data = data;
	nautilus_icon_container_each_selected_icon (container, icon_get_data_binder, &context);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
290
291
}

292
/* Called when the data for drag&drop is needed */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
293
static void
294
295
296
297
298
299
drag_data_get_callback (GtkWidget *widget,
			GdkDragContext *context,
			GtkSelectionData *selection_data,
			guint info,
			guint32 time,
			gpointer data)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
300
{
301
302
	g_assert (widget != NULL);
	g_assert (NAUTILUS_IS_ICON_CONTAINER (widget));
Ettore Perazzoli's avatar
Ettore Perazzoli committed
303
304
	g_return_if_fail (context != NULL);

305
306
307
308
309
310
	/* Call common function from nautilus-drag that set's up
	 * the selection data in the right format. Pass it means to
	 * iterate all the selected icons.
	 */
	nautilus_drag_drag_data_get (widget, context, selection_data,
		info, time, widget, each_icon_get_data_binder);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
311
312
}

313

Ettore Perazzoli's avatar
Ettore Perazzoli committed
314
315
316
/* Target-side handling of the drag.  */

static void
317
318
nautilus_icon_container_position_shadow (NautilusIconContainer *container,
					 int x, int y)
319
320
321
322
{
	GnomeCanvasItem *shadow;
	double world_x, world_y;

323
	shadow = container->details->dnd_info->shadow;
324
	if (shadow == NULL) {
325
		return;
326
	}
327
328
329
330

	gnome_canvas_window_to_world (GNOME_CANVAS (container),
				      x, y, &world_x, &world_y);
	set_shadow_position (shadow, world_x, world_y);
331
	gnome_canvas_item_show (shadow);
332
333
334
}

static void
335
336
337
nautilus_icon_container_dropped_icon_feedback (GtkWidget *widget,
					       GtkSelectionData *data,
					       int x, int y)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
338
{
339
340
	NautilusIconContainer *container;
	NautilusIconDndInfo *dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
341

342
	container = NAUTILUS_ICON_CONTAINER (widget);
343
	dnd_info = container->details->dnd_info;
344

345
	/* Delete old selection list. */
346
347
	nautilus_drag_destroy_selection_list (dnd_info->drag_info.selection_list);
	dnd_info->drag_info.selection_list = NULL;
348

349
	/* Delete old shadow if any. */
350
	if (dnd_info->shadow != NULL) {
351
		/* FIXME: Is a destroy really sufficient here? Who does the unref? */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
352
		gtk_object_destroy (GTK_OBJECT (dnd_info->shadow));
353
	}
354
355

	/* Build the selection list and the shadow. */
356
357
	dnd_info->drag_info.selection_list = nautilus_drag_build_selection_list (data);
	dnd_info->shadow = create_selection_shadow (container, dnd_info->drag_info.selection_list);
358
	nautilus_icon_container_position_shadow (container, x, y);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
359
360
}

361
static void
362
363
364
365
366
367
368
369
drag_data_received_callback (GtkWidget *widget,
			     GdkDragContext *context,
			     int x,
			     int y,
			     GtkSelectionData *data,
			     guint info,
			     guint32 time,
			     gpointer user_data)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
370
{
371
    	NautilusIconDndInfo *dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
372

373
	dnd_info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
374

375
376
	dnd_info->drag_info.got_drop_data_type = TRUE;
	dnd_info->drag_info.data_type = info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
377

378
	switch (info) {
379
	case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
380
		nautilus_icon_container_dropped_icon_feedback (widget, data, x, y);
381
		break;
382
	case NAUTILUS_ICON_DND_COLOR:
383
	case NAUTILUS_ICON_DND_BGIMAGE:	
384
	case NAUTILUS_ICON_DND_KEYWORD:	
385
		/* Save the data so we can do the actual work on drop. */
386
387
		g_assert (dnd_info->drag_info.selection_data == NULL);
		dnd_info->drag_info.selection_data = nautilus_gtk_selection_data_copy_deep (data);
388
389
		break;
	default:
390
		break;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
391
	}
392
393
394
}

static void
395
396
397
nautilus_icon_container_ensure_drag_data (NautilusIconContainer *container,
					  GdkDragContext *context,
					  guint32 time)
398
{
399
	NautilusIconDndInfo *dnd_info;
400

401
	dnd_info = container->details->dnd_info;
402

403
	if (!dnd_info->drag_info.got_drop_data_type) {
404
405
406
		gtk_drag_get_data (GTK_WIDGET (container), context,
				   GPOINTER_TO_INT (context->targets->data),
				   time);
407
	}
408
409
}

Ettore Perazzoli's avatar
Ettore Perazzoli committed
410
static void
411
412
413
drag_end_callback (GtkWidget *widget,
		   GdkDragContext *context,
		   gpointer data)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
414
{
415
416
	NautilusIconContainer *container;
	NautilusIconDndInfo *dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
417

418
	container = NAUTILUS_ICON_CONTAINER (widget);
419
	dnd_info = container->details->dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
420

421
422
	nautilus_drag_destroy_selection_list (dnd_info->drag_info.selection_list);
	dnd_info->drag_info.selection_list = NULL;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
423
424
}

Pavel Cisler's avatar
Pavel Cisler committed
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
static NautilusIcon *
nautilus_icon_container_item_at (NautilusIconContainer *container,
				 int x, int y)
{
	GList *p;
	ArtDRect point;

	/* hit test a single pixel rectangle */
	point.x0 = x;
	point.y0 = y;
	point.x1 = x + 1;
	point.y1 = y + 1;

	for (p = container->details->icons; p != NULL; p = p->next) {
		NautilusIcon *icon;
		icon = p->data;
		if (nautilus_icon_canvas_item_hit_test_rectangle
			(icon->item, &point)) {
			return icon;
		}
	}

	return NULL;
}

static char *
get_container_uri (const NautilusIconContainer *container)
{
	char *uri;

	/* get the URI associated with the container */
	uri = NULL;
	gtk_signal_emit_by_name (GTK_OBJECT (container),
			 "get_container_uri",
			 &uri);
	return uri;
}

static gboolean
nautilus_icon_container_selection_items_local (const NautilusIconContainer *container,
					       const GList *items)
{
	char *container_uri_string;
	gboolean result;

	/* must have at least one item */
	g_assert (items);

	result = FALSE;

	/* get the URI associated with the container */
	container_uri_string = get_container_uri (container);
477
	result = nautilus_drag_items_local (container_uri_string, items);
Pavel Cisler's avatar
Pavel Cisler committed
478
479
480
481
482
	g_free (container_uri_string);
	
	return result;
}

483
/* handle dropped tile images */
484
static void
485
receive_dropped_tile_image (NautilusIconContainer *container, gpointer data)
486
{
487
488
	g_assert (data != NULL);
	nautilus_background_receive_dropped_background_image
489
		(nautilus_get_widget_background (GTK_WIDGET (container)), data);
490
491
}

492
493
494
495
496
497
498
499
500
501
502
/* handle dropped keywords */
static void
receive_dropped_keyword (NautilusIconContainer *container, char* keyword, int x, int y)
{
	GList *keywords, *word;
	char *uri;
	double world_x, world_y;

	NautilusIcon *drop_target_icon;
	NautilusFile *file;
	
503
	g_assert (keyword != NULL);
504
505
506
507

	/* find the item we hit with our drop, if any */
  	gnome_canvas_window_to_world (GNOME_CANVAS (container), x, y, &world_x, &world_y);
	drop_target_icon = nautilus_icon_container_item_at (container, world_x, world_y);
508
	if (drop_target_icon == NULL) {
509
		return;
510
511
512
513
514
515
516
517
	}

	/* FIXME: This does not belong in the icon code.
	 * It has to be in the file manager.
	 * The icon code has no right to deal with the file directly.
	 * But luckily there's no issue of not getting a file object,
	 * so we don't have to worry about async. issues here.
	 */
518
	uri = nautilus_icon_container_get_icon_uri (container, drop_target_icon);
519
520
	file = nautilus_file_get (uri);
	g_free (uri);
521
522
523
	
	keywords = nautilus_file_get_keywords (file);
	word = g_list_find_custom (keywords, keyword, (GCompareFunc) strcmp);
524
	if (word == NULL) {
525
		keywords = g_list_append (keywords, g_strdup (keyword));
526
	} else {
527
		keywords = g_list_remove_link (keywords, word);
528
529
530
		g_free (word->data);
		g_list_free (word);
	}
531
532

	nautilus_file_set_keywords (file, keywords);
533
	nautilus_file_unref (file);
534
535
536
537
	nautilus_icon_container_update_icon (container, drop_target_icon);

}

Pavel Cisler's avatar
Pavel Cisler committed
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
static int
auto_scroll_timeout_callback (gpointer data)
{
	NautilusIconContainer *container;
	GtkWidget *widget;
	int x, y;
	float x_scroll_delta, y_scroll_delta;
	GdkRectangle exposed_area;

	g_assert (NAUTILUS_IS_ICON_CONTAINER (data));
	widget = GTK_WIDGET (data);
	container = NAUTILUS_ICON_CONTAINER (widget);

	if (container->details->waiting_to_autoscroll
		&& container->details->start_auto_scroll_in < nautilus_get_system_time()) {
		/* not yet */
		return TRUE;
	}

	container->details->waiting_to_autoscroll = FALSE;

	gdk_window_get_pointer (widget->window, &x, &y, NULL);

	/* Find out if we are anywhere close to the container view edges
	 * to see if we need to autoscroll.
	 */
	x_scroll_delta = 0;
	y_scroll_delta = 0;
	
	if (x < AUTO_SCROLL_MARGIN) {
		x_scroll_delta = (float)(x - AUTO_SCROLL_MARGIN);
	}

	if (x > widget->allocation.width - AUTO_SCROLL_MARGIN) {
		if (x_scroll_delta != 0) {
			/* Already trying to scroll because of being too close to 
			 * the top edge -- must be the window is really short,
			 * don't autoscroll.
			 */
			return TRUE;
		}
		x_scroll_delta = (float)(x - (widget->allocation.width - AUTO_SCROLL_MARGIN));
	}

	if (y < AUTO_SCROLL_MARGIN) {
		y_scroll_delta = (float)(y - AUTO_SCROLL_MARGIN);
	}

	if (y > widget->allocation.height - AUTO_SCROLL_MARGIN) {
		if (y_scroll_delta != 0) {
			/* Already trying to scroll because of being too close to 
			 * the top edge -- must be the window is really narrow,
			 * don't autoscroll.
			 */
			return TRUE;
		}
		y_scroll_delta = (float)(y - (widget->allocation.height - AUTO_SCROLL_MARGIN));
	}

	if (x_scroll_delta == 0 && y_scroll_delta == 0) {
		/* no work */
		return TRUE;
	}

	/* Adjust the scroll delta to the proper acceleration values depending on how far
	 * into the sroll margins we are.
	 * FIXME:
	 * we could use an exponential acceleration factor here for better feel
	 */
	if (x_scroll_delta != 0) {
		x_scroll_delta /= AUTO_SCROLL_MARGIN;
		x_scroll_delta *= (MAX_AUTOSCROLL_DELTA - MIN_AUTOSCROLL_DELTA);
		x_scroll_delta += MIN_AUTOSCROLL_DELTA;
	}
	
	if (y_scroll_delta != 0) {
		y_scroll_delta /= AUTO_SCROLL_MARGIN;
		y_scroll_delta *= (MAX_AUTOSCROLL_DELTA - MIN_AUTOSCROLL_DELTA);
		y_scroll_delta += MIN_AUTOSCROLL_DELTA;
	}

	nautilus_icon_container_scroll (container, (int)x_scroll_delta, (int)y_scroll_delta);

	/* update cached drag start offsets */
	container->details->dnd_info->drag_info.start_x -= x_scroll_delta;
	container->details->dnd_info->drag_info.start_y -= y_scroll_delta;

	/* Due to a glitch in GtkLayout, whe need to do an explicit draw of the exposed
	 * area. 
	 * Calculate the size of the area we need to draw
	 */
	exposed_area.x = widget->allocation.x;
	exposed_area.y = widget->allocation.y;
	exposed_area.width = widget->allocation.width;
	exposed_area.height = widget->allocation.height;

	if (x_scroll_delta > 0) {
		exposed_area.x = exposed_area.width - x_scroll_delta;
	} else if (x_scroll_delta < 0) {
		exposed_area.width = -x_scroll_delta;
	}

	if (y_scroll_delta > 0) {
		exposed_area.y = exposed_area.height - y_scroll_delta;
	} else if (y_scroll_delta < 0) {
		exposed_area.height = -y_scroll_delta;
	}

	/* offset it to 0, 0 */
	exposed_area.x -= widget->allocation.x;
	exposed_area.y -= widget->allocation.y;

650
	gtk_widget_draw (widget, &exposed_area);
Pavel Cisler's avatar
Pavel Cisler committed
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677

	return TRUE;
}

static void
set_up_auto_scroll_if_needed (NautilusIconContainer *container)
{
	if (container->details->auto_scroll_timeout_id == 0) {
		container->details->waiting_to_autoscroll = TRUE;
		container->details->start_auto_scroll_in = nautilus_get_system_time() 
			+ AUTOSCROLL_INITIAL_DELAY;
		container->details->auto_scroll_timeout_id = gtk_timeout_add
				(AUTOSCROLL_TIMEOUT_INTERVAL,
				 auto_scroll_timeout_callback,
			 	 container);
	}
}

static void
stop_auto_scroll (NautilusIconContainer *container)
{
	if (container->details->auto_scroll_timeout_id) {
		gtk_timeout_remove (container->details->auto_scroll_timeout_id);
		container->details->auto_scroll_timeout_id = 0;
	}
}

678
679
680
681
682
static gboolean
confirm_switch_to_manual_layout (NautilusIconContainer *container)
{
	const char *message;

Darin Adler's avatar
Darin Adler committed
683
684
685
686
	/* FIXME bugzilla.eazel.com 915: Use of the word "directory"
	 * makes this FMIconView specific. Move these messages into
	 * FMIconView so NautilusIconContainer can be used for things
	 * that are not directories?
687
688
	 */
	if (nautilus_icon_container_has_stored_icon_positions (container)) {
689
		if (nautilus_g_list_exactly_one_item (container->details->dnd_info->drag_info.selection_list)) {
690
691
692
			message = _("This directory uses automatic layout. "
			"Do you want to switch to manual layout and leave this item where you dropped it? "
			"This will clobber the stored manual layout.");
693
		} else {
694
695
696
			message = _("This directory uses automatic layout. "
			"Do you want to switch to manual layout and leave these items where you dropped them? "
			"This will clobber the stored manual layout.");
697
698
		}
	} else {
699
		if (nautilus_g_list_exactly_one_item (container->details->dnd_info->drag_info.selection_list)) {
700
701
			message = _("This directory uses automatic layout. "
			"Do you want to switch to manual layout and leave this item where you dropped it?");
702
		} else {
703
704
			message = _("This directory uses automatic layout. "
			"Do you want to switch to manual layout and leave these items where you dropped them?");
705
706
707
708
709
710
711
712
713
		}
	}

	return nautilus_simple_dialog
		(GTK_WIDGET (container), message,
		 _("Switch to Manual Layout?"),
		 _("Switch"), GNOME_STOCK_BUTTON_CANCEL, NULL) == 0;
}

714
715
716
717
718
static void
handle_local_move (NautilusIconContainer *container,
		   double world_x, double world_y)
{
	GList *moved_icons, *p;
719
	DragSelectionItem *item;
720
721
722
	NautilusIcon *icon;

	if (container->details->auto_layout) {
723
724
725
726
		if (!confirm_switch_to_manual_layout (container)) {
			return;
		}
		nautilus_icon_container_freeze_icon_positions (container);
727
728
	}

729
	/* Move and select the icons. */
730
	moved_icons = NULL;
731
	for (p = container->details->dnd_info->drag_info.selection_list; p != NULL; p = p->next) {
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
		item = p->data;
		
		icon = nautilus_icon_container_get_icon_by_uri
			(container, item->uri);
		if (item->got_icon_position) {
			nautilus_icon_container_move_icon
				(container, icon,
				 world_x + item->icon_x, world_y + item->icon_y,
				 icon->scale_x, icon->scale_y,
				 TRUE);
		}
		moved_icons = g_list_prepend (moved_icons, icon);
	}		
	nautilus_icon_container_select_list_unselect_others
		(container, moved_icons);
747
748
	/* Might have been moved in a way that requires adjusting scroll region */
	nautilus_icon_container_update_scroll_region (container);
749
750
751
752
753
754
755
	g_list_free (moved_icons);
}

static void
handle_nonlocal_move (NautilusIconContainer *container,
		      GdkDragContext *context,
		      int x, int y,
756
757
		      const char *target_uri,
		      gboolean icon_hit)
758
759
760
761
762
{
	GList *source_uris, *p;
	GdkPoint *source_item_locations;
	int i;

763
	if (container->details->dnd_info->drag_info.selection_list == NULL) {
764
765
766
767
		return;
	}
	
	source_uris = NULL;
768
	for (p = container->details->dnd_info->drag_info.selection_list; p != NULL; p = p->next) {
769
		/* do a shallow copy of all the uri strings of the copied files */
770
		source_uris = g_list_prepend (source_uris, ((DragSelectionItem *)p->data)->uri);
771
772
773
774
	}
	source_uris = g_list_reverse (source_uris);
	
	source_item_locations = NULL;
775
	if (!icon_hit) {
776
777
778
779
		/* Drop onto a container. Pass along the item points to allow placing
		 * the items in their same relative positions in the new container.
		 */
		source_item_locations = g_new (GdkPoint, g_list_length (source_uris));
780
		for (i = 0, p = container->details->dnd_info->drag_info.selection_list;
781
782
783
784
		     p != NULL; i++, p = p->next) {
			/* FIXME bugzilla.eazel.com 626:
			 * subtract the original click coordinates from each point here
			 */
785
786
			source_item_locations[i].x = ((DragSelectionItem *)p->data)->icon_x;
			source_item_locations[i].y = ((DragSelectionItem *)p->data)->icon_y;
787
788
		}
	}
789
		
790
791
792
793
794
795
796
797
798
799
800
	/* start the copy */
	gtk_signal_emit_by_name (GTK_OBJECT (container), "move_copy_items",
				 source_uris,
				 source_item_locations,
				 target_uri,
				 context->action,
				 x, y);
	g_list_free (source_uris);
	g_free (source_item_locations);
}

801
802
803
804
805
static char *
nautilus_icon_container_find_drop_target (NautilusIconContainer *container,
					  GdkDragContext *context,
					  int x, int y,
					  gboolean *icon_hit)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
806
{
Pavel Cisler's avatar
Pavel Cisler committed
807
808
	NautilusIcon *drop_target_icon;
	double world_x, world_y;
809

810
	if (container->details->dnd_info->drag_info.selection_list == NULL) {
811
		return NULL;
812
	}
813

814
  	gnome_canvas_window_to_world (GNOME_CANVAS (container),
Pavel Cisler's avatar
Pavel Cisler committed
815
816
				      x, y, &world_x, &world_y);

817
818
819
820
821
	/* FIXME: These "can_accept_items" tests need to be done by
	 * the icon view, not here. This file is not supposed to know
	 * that the target is a file.
	 */

822
	/* Find the item we hit with our drop, if any */
Pavel Cisler's avatar
Pavel Cisler committed
823
	drop_target_icon = nautilus_icon_container_item_at (container, world_x, world_y);
824
825
826
827
828
	if (drop_target_icon != NULL 
		&& !nautilus_drag_can_accept_items 
			(nautilus_file_get (
				nautilus_icon_container_get_icon_uri 
					(container, drop_target_icon)), 
829
			container->details->dnd_info->drag_info.selection_list)) {
Pavel Cisler's avatar
Pavel Cisler committed
830
831
832
833
834
835
		/* the item we dropped our selection on cannot accept the items,
		 * do the same thing as if we just dropped the items on the canvas
		 */
		drop_target_icon = NULL;
	}

836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
	if (!drop_target_icon) {
		*icon_hit = FALSE;
		return get_container_uri (container);
	}

	
	*icon_hit = TRUE;
	return nautilus_icon_container_get_icon_uri (container, drop_target_icon);
}

static void
nautilus_icon_container_receive_dropped_icons (NautilusIconContainer *container,
					       GdkDragContext *context,
					       int x, int y)
{
	char *drop_target;
	gboolean local_move_only;
	double world_x, world_y;
	gboolean icon_hit;
855
856
857

	drop_target = NULL;

858
859
860
861
	if (container->details->dnd_info->drag_info.selection_list == NULL) {
		return;
	}

862
863
864
865
	if (context->action == GDK_ACTION_ASK) {
		context->action = nautilus_drag_drop_action_ask 
			(GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
	}
866

867
	if (context->action > 0) {
868
869
	  	gnome_canvas_window_to_world (GNOME_CANVAS (container),
					      x, y, &world_x, &world_y);
870

871
872
		drop_target = nautilus_icon_container_find_drop_target (container, 
			context, x, y, &icon_hit);
Pavel Cisler's avatar
Pavel Cisler committed
873

874
875
876
877
878
879
880
881
882
883
884
885
886
887
		local_move_only = FALSE;
		if (!icon_hit && context->action == GDK_ACTION_MOVE) {
			/* we can just move the icon positions if the move ended up in
			 * the item's parent container
			 */
			local_move_only = nautilus_icon_container_selection_items_local
				(container, container->details->dnd_info->drag_info.selection_list);
		}

		if (local_move_only) {
			handle_local_move (container, world_x, world_y);
		} else {
			handle_nonlocal_move (container, context, x, y, drop_target, icon_hit);
		}
888
	}
Pavel Cisler's avatar
Pavel Cisler committed
889

890
	g_free (drop_target);
891
892
	nautilus_drag_destroy_selection_list (container->details->dnd_info->drag_info.selection_list);
	container->details->dnd_info->drag_info.selection_list = NULL;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
893
894
}

895
896
897
898
899
900
901
902
903
904
static void
nautilus_icon_container_get_drop_action (NautilusIconContainer *container,
					 GdkDragContext *context,
					 int x, int y,
					 int *default_action,
					 int *non_default_action)
{
	char *drop_target;
	gboolean icon_hit;

905
906
907
908
	if (!container->details->dnd_info->drag_info.got_drop_data_type) {
		/* drag_data_received_callback didn't get called yet */
		return;
	}
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923

	switch (container->details->dnd_info->drag_info.data_type) {
	case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
		if (container->details->dnd_info->drag_info.selection_list == NULL) {
			*default_action = 0;
			*non_default_action = 0;
			return;
		}
		drop_target = nautilus_icon_container_find_drop_target (container,
			context, x, y, &icon_hit);
		if (!drop_target) {
			*default_action = 0;
			*non_default_action = 0;
			return;
		}
924
		nautilus_drag_default_drop_action_for_icons (context, drop_target, 
925
926
927
			container->details->dnd_info->drag_info.selection_list, 
			default_action, non_default_action);
		break;
928

929
930
931
	case NAUTILUS_ICON_DND_COLOR:
	case NAUTILUS_ICON_DND_BGIMAGE:
	case NAUTILUS_ICON_DND_KEYWORD:
932
		*default_action = context->suggested_action;
933
		*non_default_action = context->suggested_action;
934
		break;
935

936
	default:
937
	}
938

939
940
}

941
static void
942
943
944
945
946
set_drop_target (NautilusIconContainer *container,
		 NautilusIcon *icon)
{
	NautilusIcon *old_icon;

947
948
949
	/* Check if current drop target changed, update icon drop
	 * higlight if needed.
	 */
950
951
952
953
954
955
956
957
958
959
960
961
962
	old_icon = container->details->drop_target;
	if (icon == old_icon) {
		return;
	}

	/* Remember the new drop target for the next round. */
	container->details->drop_target = icon;
	nautilus_icon_container_update_icon (container, old_icon);
	nautilus_icon_container_update_icon (container, icon);
}

static void
nautilus_icon_dnd_update_drop_target (NautilusIconContainer *container,
963
964
965
				      GdkDragContext *context,
				      int x, int y)
{
966
	NautilusIcon *icon;
967
968
969
	double world_x, world_y;
	
	g_assert (NAUTILUS_IS_ICON_CONTAINER (container));
970
971
	if ((container->details->dnd_info->drag_info.selection_list == NULL) 
	   && (container->details->dnd_info->drag_info.data_type != NAUTILUS_ICON_DND_KEYWORD)) {
972
973
974
975
976
977
978
		return;
	}

  	gnome_canvas_window_to_world (GNOME_CANVAS (container),
				      x, y, &world_x, &world_y);

	/* Find the item we hit with our drop, if any. */
979
	icon = nautilus_icon_container_item_at (container, world_x, world_y);
980

981
982
983
984
985
	/* FIXME: These "can_accept_items" tests need to be done by
	 * the icon view, not here. This file is not supposed to know
	 * that the target is a file.
	 */

986
	/* Find if target icon accepts our drop. */
987
	if (icon != NULL 
988
		&& (container->details->dnd_info->drag_info.data_type != NAUTILUS_ICON_DND_KEYWORD) 
989
990
991
		&& !nautilus_drag_can_accept_items 
			(nautilus_file_get (
				nautilus_icon_container_get_icon_uri (container, icon)), 
992
			container->details->dnd_info->drag_info.selection_list)) {
993
		icon = NULL;
994
995
	}

996
	set_drop_target (container, icon);
997
998
}

Ettore Perazzoli's avatar
Ettore Perazzoli committed
999
static void
1000
nautilus_icon_container_free_drag_data (NautilusIconContainer *container)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1001
{
1002
	NautilusIconDndInfo *dnd_info;
1003
	
1004
	dnd_info = container->details->dnd_info;
1005
	
1006
	dnd_info->drag_info.got_drop_data_type = FALSE;
1007
	
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1008
	if (dnd_info->shadow != NULL) {
1009
		gtk_object_destroy (GTK_OBJECT (dnd_info->shadow));
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1010
1011
1012
		dnd_info->shadow = NULL;
	}

1013
1014
1015
	if (dnd_info->drag_info.selection_data != NULL) {
		nautilus_gtk_selection_data_free_deep (dnd_info->drag_info.selection_data);
		dnd_info->drag_info.selection_data = NULL;
1016
1017
1018
1019
	}
}

static void
1020
1021
1022
1023
drag_leave_callback (GtkWidget *widget,
		     GdkDragContext *context,
		     guint32 time,
		     gpointer data)
1024
{
1025
	NautilusIconDndInfo *dnd_info;
1026

1027
	dnd_info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info;
1028
	
1029
1030
	if (dnd_info->shadow != NULL)
		gnome_canvas_item_hide (dnd_info->shadow);
1031
	
Pavel Cisler's avatar
Pavel Cisler committed
1032
	stop_auto_scroll (NAUTILUS_ICON_CONTAINER (widget));
1033
	nautilus_icon_container_free_drag_data(NAUTILUS_ICON_CONTAINER (widget));
1034
}
1035

Ettore Perazzoli's avatar
Ettore Perazzoli committed
1036
void
1037
1038
nautilus_icon_dnd_init (NautilusIconContainer *container,
			GdkBitmap *stipple)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1039
1040
{
	g_return_if_fail (container != NULL);
1041
	g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1042
1043


1044
1045
1046
	container->details->dnd_info = g_new0 (NautilusIconDndInfo, 1);
	nautilus_drag_init (&container->details->dnd_info->drag_info,
		drag_types, NAUTILUS_N_ELEMENTS (drag_types), stipple);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1047

1048
1049
1050
1051
	/* 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.)
	 */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1052
1053
	gtk_drag_dest_set  (GTK_WIDGET (container),
			    0,
1054
			    drop_types, NAUTILUS_N_ELEMENTS (drop_types),
1055
1056
			    GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK
			    | GDK_ACTION_ASK);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1057

1058
	/* Messages for outgoing drag. */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1059
	gtk_signal_connect (GTK_OBJECT (container), "drag_data_get",
1060
			    GTK_SIGNAL_FUNC (drag_data_get_callback), NULL);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1061
	gtk_signal_connect (GTK_OBJECT (container), "drag_end",
1062
			    GTK_SIGNAL_FUNC (drag_end_callback), NULL);
1063
1064

	/* Messages for incoming drag. */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1065
	gtk_signal_connect (GTK_OBJECT (container), "drag_data_received",
1066
			    GTK_SIGNAL_FUNC (drag_data_received_callback), NULL);
1067
	gtk_signal_connect (GTK_OBJECT (container), "drag_motion",
1068
			    GTK_SIGNAL_FUNC (drag_motion_callback), NULL);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1069
	gtk_signal_connect (GTK_OBJECT (container), "drag_drop",
1070
			    GTK_SIGNAL_FUNC (drag_drop_callback), NULL);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1071
	gtk_signal_connect (GTK_OBJECT (container), "drag_leave",
1072
			    GTK_SIGNAL_FUNC (drag_leave_callback), NULL);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1073
1074
1075
1076

}

void
1077
nautilus_icon_dnd_fini (NautilusIconContainer *container)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1078
1079
{
	g_return_if_fail (container != NULL);
1080
	g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
1081
	g_return_if_fail (container->details->dnd_info != NULL);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1082

Pavel Cisler's avatar
Pavel Cisler committed
1083
	stop_auto_scroll (container);
1084
1085
	if (container->details->dnd_info->shadow != NULL) {
		/* FIXME: Is a destroy really sufficient here? Who does the unref? */
1086
		gtk_object_destroy (GTK_OBJECT (container->details->dnd_info->shadow));
1087
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1088

1089
	nautilus_drag_finalize (&container->details->dnd_info->drag_info);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1090
1091
1092
}

void
1093
1094
1095
1096
nautilus_icon_dnd_begin_drag (NautilusIconContainer *container,
			      GdkDragAction actions,
			      int button,
			      GdkEventMotion *event)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1097
{
1098
	NautilusIconDndInfo *dnd_info;
1099
	GnomeCanvas *canvas;
1100
	GdkDragContext *context;
1101
	GdkPixbuf *pixbuf, *transparent_pixbuf;
1102
1103
	GdkPixmap *pixmap_for_dragged_file;
	GdkBitmap *mask_for_dragged_file;
1104
	int x_offset, y_offset;
1105
1106
	ArtDRect world_rect;
	ArtIRect window_rect;
1107
	
1108
	g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1109
1110
	g_return_if_fail (event != NULL);

1111
	dnd_info = container->details->dnd_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1112
	g_return_if_fail (dnd_info != NULL);
1113
	
1114
1115
1116
1117
1118
1119
	/* 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,
1120
				      &dnd_info->drag_info.start_x, &dnd_info->drag_info.start_y);
1121
	
1122
	/* start the drag */
1123
	context = gtk_drag_begin (GTK_WIDGET (container),
1124
				  dnd_info->drag_info.target_list,
1125
1126
1127
				  actions,
				  button,
				  (GdkEvent *) event);