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

No issues found

  1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
  2 
  3 /* nautilus-dnd.c - Common Drag & drop handling code shared by the icon container
  4    and the list view.
  5 
  6    Copyright (C) 2000, 2001 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: Pavel Cisler <pavel@eazel.com>,
 24    	    Ettore Perazzoli <ettore@gnu.org>
 25 */
 26 
 27 #include <config.h>
 28 #include "nautilus-dnd.h"
 29 
 30 #include "nautilus-program-choosing.h"
 31 #include "nautilus-link.h"
 32 #include <eel/eel-glib-extensions.h>
 33 #include <eel/eel-gtk-extensions.h>
 34 #include <eel/eel-string.h>
 35 #include <eel/eel-vfs-extensions.h>
 36 #include <gtk/gtk.h>
 37 #include <glib/gi18n.h>
 38 #include <libnautilus-private/nautilus-file-utilities.h>
 39 #include <stdio.h>
 40 #include <string.h>
 41 
 42 /* a set of defines stolen from the eel-icon-dnd.c file.
 43  * These are in microseconds.
 44  */
 45 #define AUTOSCROLL_TIMEOUT_INTERVAL 100
 46 #define AUTOSCROLL_INITIAL_DELAY 100000
 47 
 48 /* drag this close to the view edge to start auto scroll*/
 49 #define AUTO_SCROLL_MARGIN 30
 50 
 51 /* the smallest amount of auto scroll used when we just enter the autoscroll
 52  * margin
 53  */
 54 #define MIN_AUTOSCROLL_DELTA 5
 55 	 
 56 /* the largest amount of auto scroll used when we are right over the view
 57  * edge
 58  */
 59 #define MAX_AUTOSCROLL_DELTA 50
 60 
 61 void
 62 nautilus_drag_init (NautilusDragInfo     *drag_info,
 63 		    const GtkTargetEntry *drag_types,
 64 		    int                   drag_type_count,
 65 		    gboolean              add_text_targets)
 66 {
 67 	drag_info->target_list = gtk_target_list_new (drag_types,
 68 						   drag_type_count);
 69 
 70 	if (add_text_targets) {
 71 		gtk_target_list_add_text_targets (drag_info->target_list,
 72 						  NAUTILUS_ICON_DND_TEXT);
 73 	}
 74 
 75 	drag_info->drop_occured = FALSE;
 76 	drag_info->need_to_destroy = FALSE;
 77 }
 78 
 79 void
 80 nautilus_drag_finalize (NautilusDragInfo *drag_info)
 81 {
 82 	gtk_target_list_unref (drag_info->target_list);
 83 	nautilus_drag_destroy_selection_list (drag_info->selection_list);
 84 
 85 	g_free (drag_info);
 86 }
 87 
 88 
 89 /* Functions to deal with NautilusDragSelectionItems.  */
 90 
 91 NautilusDragSelectionItem *
 92 nautilus_drag_selection_item_new (void)
 93 {
 94 	return g_new0 (NautilusDragSelectionItem, 1);
 95 }
 96 
 97 static void
 98 drag_selection_item_destroy (NautilusDragSelectionItem *item)
 99 {
100 	g_free (item->uri);
101 	g_free (item);
102 }
103 
104 void
105 nautilus_drag_destroy_selection_list (GList *list)
106 {
107 	GList *p;
108 
109 	if (list == NULL)
110 		return;
111 
112 	for (p = list; p != NULL; p = p->next)
113 		drag_selection_item_destroy (p->data);
114 
115 	g_list_free (list);
116 }
117 
118 char **
119 nautilus_drag_uri_array_from_selection_list (const GList *selection_list)
120 {
121 	GList *uri_list;
122 	char **uris;
123 
124 	uri_list = nautilus_drag_uri_list_from_selection_list (selection_list);
125 	uris = nautilus_drag_uri_array_from_list (uri_list);
126 	g_list_free_full (uri_list, g_free);
127 
128 	return uris;
129 }
130 
131 GList *
132 nautilus_drag_uri_list_from_selection_list (const GList *selection_list)
133 {
134 	NautilusDragSelectionItem *selection_item;
135 	GList *uri_list;
136 	const GList *l;
137 
138 	uri_list = NULL;
139 	for (l = selection_list; l != NULL; l = l->next) {
140 		selection_item = (NautilusDragSelectionItem *) l->data;
141 		if (selection_item->uri != NULL) {
142 			uri_list = g_list_prepend (uri_list, g_strdup (selection_item->uri));
143 		}
144 	}
145 
146 	return g_list_reverse (uri_list);
147 }
148 
149 char **
150 nautilus_drag_uri_array_from_list (const GList *uri_list)
151 {
152 	const GList *l;
153 	char **uris;
154 	int i;
155 
156 	if (uri_list == NULL) {
157 		return NULL;
158 	}
159 
160 	uris = g_new0 (char *, g_list_length ((GList *) uri_list));
161 	for (i = 0, l = uri_list; l != NULL; l = l->next) {
162 		uris[i++] = g_strdup ((char *) l->data);
163 	}
164 	uris[i] = NULL;
165 
166 	return uris;
167 }
168 
169 GList *
170 nautilus_drag_uri_list_from_array (const char **uris)
171 {
172 	GList *uri_list;
173 	int i;
174 
175 	if (uris == NULL) {
176 		return NULL;
177 	}
178 
179 	uri_list = NULL;
180 
181 	for (i = 0; uris[i] != NULL; i++) {
182 		uri_list = g_list_prepend (uri_list, g_strdup (uris[i]));
183 	}
184 
185 	return g_list_reverse (uri_list);
186 }
187 
188 GList *
189 nautilus_drag_build_selection_list (GtkSelectionData *data)
190 {
191 	GList *result;
192 	const guchar *p, *oldp;
193 	int size;
194 
195 	result = NULL;
196 	oldp = gtk_selection_data_get_data (data);
197 	size = gtk_selection_data_get_length (data);
198 
199 	while (size > 0) {
200 		NautilusDragSelectionItem *item;
201 		guint len;
202 
203 		/* The list is in the form:
204 
205 		   name\rx:y:width:height\r\n
206 
207 		   The geometry information after the first \r is optional.  */
208 
209 		/* 1: Decode name. */
210 
211 		p = memchr (oldp, '\r', size);
212 		if (p == NULL) {
213 			break;
214 		}
215 
216 		item = nautilus_drag_selection_item_new ();
217 
218 		len = p - oldp;
219 
220 		item->uri = g_malloc (len + 1);
221 		memcpy (item->uri, oldp, len);
222 		item->uri[len] = 0;
223 
224 		p++;
225 		if (*p == '\n' || *p == '\0') {
226 			result = g_list_prepend (result, item);
227 			if (p == 0) {
228 				g_warning ("Invalid x-special/gnome-icon-list data received: "
229 					   "missing newline character.");
230 				break;
231 			} else {
232 				oldp = p + 1;
233 				continue;
234 			}
235 		}
236 
237 		size -= p - oldp;
238 		oldp = p;
239 
240 		/* 2: Decode geometry information.  */
241 
242 		item->got_icon_position = sscanf ((const gchar *) p, "%d:%d:%d:%d%*s",
243 						  &item->icon_x, &item->icon_y,
244 						  &item->icon_width, &item->icon_height) == 4;
245 		if (!item->got_icon_position) {
246 			g_warning ("Invalid x-special/gnome-icon-list data received: "
247 				   "invalid icon position specification.");
248 		}
249 
250 		result = g_list_prepend (result, item);
251 
252 		p = memchr (p, '\r', size);
253 		if (p == NULL || p[1] != '\n') {
254 			g_warning ("Invalid x-special/gnome-icon-list data received: "
255 				   "missing newline character.");
256 			if (p == NULL) {
257 				break;
258 			}
259 		} else {
260 			p += 2;
261 		}
262 
263 		size -= p - oldp;
264 		oldp = p;
265 	}
266 
267 	return g_list_reverse (result);
268 }
269 
270 static gboolean
271 nautilus_drag_file_local_internal (const char *target_uri_string,
272 				   const char *first_source_uri)
273 {
274 	/* check if the first item on the list has target_uri_string as a parent
275 	 * FIXME:
276 	 * we should really test each item but that would be slow for large selections
277 	 * and currently dropped items can only be from the same container
278 	 */
279 	GFile *target, *item, *parent;
280 	gboolean result;
281 
282 	result = FALSE;
283 
284 	target = g_file_new_for_uri (target_uri_string);
285 
286 	/* get the parent URI of the first item in the selection */
287 	item = g_file_new_for_uri (first_source_uri);
288 	parent = g_file_get_parent (item);
289 	g_object_unref (item);
290 	
291 	if (parent != NULL) {
292 		result = g_file_equal (parent, target);
293 		g_object_unref (parent);
294 	}
295 	
296 	g_object_unref (target);
297 	
298 	return result;
299 }	
300 
301 gboolean
302 nautilus_drag_uris_local (const char *target_uri,
303 			  const GList *source_uri_list)
304 {
305 	/* must have at least one item */
306 	g_assert (source_uri_list);
307 	
308 	return nautilus_drag_file_local_internal (target_uri, source_uri_list->data);
309 }
310 
311 gboolean
312 nautilus_drag_items_local (const char *target_uri_string,
313 			   const GList *selection_list)
314 {
315 	/* must have at least one item */
316 	g_assert (selection_list);
317 
318 	return nautilus_drag_file_local_internal (target_uri_string,
319 						  ((NautilusDragSelectionItem *)selection_list->data)->uri);
320 }
321 
322 gboolean
323 nautilus_drag_items_in_trash (const GList *selection_list)
324 {
325 	/* check if the first item on the list is in trash.
326 	 * FIXME:
327 	 * we should really test each item but that would be slow for large selections
328 	 * and currently dropped items can only be from the same container
329 	 */
330 	return eel_uri_is_trash (((NautilusDragSelectionItem *)selection_list->data)->uri);
331 }
332 
333 gboolean
334 nautilus_drag_items_on_desktop (const GList *selection_list)
335 {
336 	char *uri;
337 	GFile *desktop, *item, *parent;
338 	gboolean result;
339 	
340 	/* check if the first item on the list is in trash.
341 	 * FIXME:
342 	 * we should really test each item but that would be slow for large selections
343 	 * and currently dropped items can only be from the same container
344 	 */
345 	uri = ((NautilusDragSelectionItem *)selection_list->data)->uri;
346 	if (eel_uri_is_desktop (uri)) {
347 		return TRUE;
348 	}
349 	
350 	desktop = nautilus_get_desktop_location ();
351 	
352 	item = g_file_new_for_uri (uri);
353 	parent = g_file_get_parent (item);
354 	g_object_unref (item);
355 
356 	result = FALSE;
357 	
358 	if (parent) {
359 		result = g_file_equal (desktop, parent);
360 		g_object_unref (parent);
361 	}
362 	g_object_unref (desktop);
363 	
364 	return result;
365 	
366 }
367 
368 GdkDragAction
369 nautilus_drag_default_drop_action_for_netscape_url (GdkDragContext *context)
370 {
371 	/* Mozilla defaults to copy, but unless thats the
372 	   only allowed thing (enforced by ctrl) we want to LINK */
373 	if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY &&
374 	    gdk_drag_context_get_actions (context) != GDK_ACTION_COPY) {
375 		return GDK_ACTION_LINK;
376 	} else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_MOVE) {
377 		/* Don't support move */
378 		return GDK_ACTION_COPY;
379 	}
380 	
381 	return gdk_drag_context_get_suggested_action (context);
382 }
383 
384 static gboolean
385 check_same_fs (NautilusFile *file1,
386 	       NautilusFile *file2)
387 {
388 	char *id1, *id2;
389 	gboolean result;
390 
391 	result = FALSE;
392 
393 	if (file1 != NULL && file2 != NULL) {
394 		id1 = nautilus_file_get_filesystem_id (file1);
395 		id2 = nautilus_file_get_filesystem_id (file2);
396 
397 		if (id1 != NULL && id2 != NULL) {
398 			result = (strcmp (id1, id2) == 0);
399 		}
400 
401 		g_free (id1);
402 		g_free (id2);
403 	}
404 
405 	return result;
406 }
407 
408 static gboolean
409 source_is_deletable (GFile *file)
410 {
411 	NautilusFile *naut_file;
412 	gboolean ret;
413 
414 	/* if there's no a cached NautilusFile, it returns NULL */
415 	naut_file = nautilus_file_get_existing (file);
416 	if (naut_file == NULL) {
417 		return FALSE;
418 	}
419 	
420 	ret = nautilus_file_can_delete (naut_file);
421 	nautilus_file_unref (naut_file);
422 
423 	return ret;
424 }
425 
426 void
427 nautilus_drag_default_drop_action_for_icons (GdkDragContext *context,
428 					     const char *target_uri_string, const GList *items,
429 					     int *action)
430 {
431 	gboolean same_fs;
432 	gboolean target_is_source_parent;
433 	gboolean source_deletable;
434 	const char *dropped_uri;
435 	GFile *target, *dropped, *dropped_directory;
436 	GdkDragAction actions;
437 	NautilusFile *dropped_file, *target_file;
438 
439 	if (target_uri_string == NULL) {
440 		*action = 0;
441 		return;
442 	}
443 
444 	actions = gdk_drag_context_get_actions (context) & (GDK_ACTION_MOVE | GDK_ACTION_COPY);
445 	if (actions == 0) {
446 		 /* We can't use copy or move, just go with the suggested action. */
447 		*action = gdk_drag_context_get_suggested_action (context);
448 		return;
449 	}
450 
451 	if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_ASK) {
452 		/* Don't override ask */
453 		*action = gdk_drag_context_get_suggested_action (context);
454 		return;
455 	}
456 	
457 	dropped_uri = ((NautilusDragSelectionItem *)items->data)->uri;
458 	dropped_file = nautilus_file_get_existing_by_uri (dropped_uri);
459 	target_file = nautilus_file_get_existing_by_uri (target_uri_string);
460 	
461 	/*
462 	 * Check for trash URI.  We do a find_directory for any Trash directory.
463 	 * Passing 0 permissions as gnome-vfs would override the permissions
464 	 * passed with 700 while creating .Trash directory
465 	 */
466 	if (eel_uri_is_trash (target_uri_string)) {
467 		/* Only move to Trash */
468 		if (actions & GDK_ACTION_MOVE) {
469 			*action = GDK_ACTION_MOVE;
470 		}
471 
472 		nautilus_file_unref (dropped_file);
473 		nautilus_file_unref (target_file);
474 		return;
475 
476 	} else if (dropped_file != NULL && nautilus_file_is_launcher (dropped_file)) {
477 		if (actions & GDK_ACTION_MOVE) {
478 			*action = GDK_ACTION_MOVE;
479 		}
480 		nautilus_file_unref (dropped_file);
481 		nautilus_file_unref (target_file);
482 		return;
483 	} else if (eel_uri_is_desktop (target_uri_string)) {
484 		target = nautilus_get_desktop_location ();
485 
486 		nautilus_file_unref (target_file);
487 		target_file = nautilus_file_get (target);
488 
489 		if (eel_uri_is_desktop (dropped_uri)) {
490 			/* Only move to Desktop icons */
491 			if (actions & GDK_ACTION_MOVE) {
492 				*action = GDK_ACTION_MOVE;
493 			}
494 			
495 			g_object_unref (target);
496 			nautilus_file_unref (dropped_file);
497 			nautilus_file_unref (target_file);
498 			return;
499 		}
500 	} else if (target_file != NULL && nautilus_file_is_archive (target_file)) {
501 		*action = GDK_ACTION_COPY;
502 
503 		nautilus_file_unref (dropped_file);
504 		nautilus_file_unref (target_file);
505 		return;
506 	} else {
507 		target = g_file_new_for_uri (target_uri_string);
508 	}
509 
510 	same_fs = check_same_fs (target_file, dropped_file);
511 
512 	nautilus_file_unref (dropped_file);
513 	nautilus_file_unref (target_file);
514 	
515 	/* Compare the first dropped uri with the target uri for same fs match. */
516 	dropped = g_file_new_for_uri (dropped_uri);
517 	dropped_directory = g_file_get_parent (dropped);
518 	target_is_source_parent = FALSE;
519 	if (dropped_directory != NULL) {
520 		/* If the dropped file is already in the same directory but
521 		   is in another filesystem we still want to move, not copy
522 		   as this is then just a move of a mountpoint to another
523 		   position in the dir */
524 		target_is_source_parent = g_file_equal (dropped_directory, target);
525 		g_object_unref (dropped_directory);
526 	}
527 	source_deletable = source_is_deletable (dropped);
528 
529 	if ((same_fs && source_deletable) || target_is_source_parent ||
530 	    g_file_has_uri_scheme (dropped, "trash")) {
531 		if (actions & GDK_ACTION_MOVE) {
532 			*action = GDK_ACTION_MOVE;
533 		} else {
534 			*action = gdk_drag_context_get_suggested_action (context);
535 		}
536 	} else {
537 		if (actions & GDK_ACTION_COPY) {
538 			*action = GDK_ACTION_COPY;
539 		} else {
540 			*action = gdk_drag_context_get_suggested_action (context);
541 		}
542 	}
543 
544 	g_object_unref (target);
545 	g_object_unref (dropped);
546 	
547 }
548 
549 GdkDragAction
550 nautilus_drag_default_drop_action_for_uri_list (GdkDragContext *context,
551 						const char *target_uri_string)
552 {
553 	if (eel_uri_is_trash (target_uri_string) && (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE)) {
554 		/* Only move to Trash */
555 		return GDK_ACTION_MOVE;
556 	} else {
557 		return gdk_drag_context_get_suggested_action (context);
558 	}
559 }
560 
561 /* Encode a "x-special/gnome-icon-list" selection.
562    Along with the URIs of the dragged files, this encodes
563    the location and size of each icon relative to the cursor.
564 */
565 static void
566 add_one_gnome_icon (const char *uri, int x, int y, int w, int h, 
567 		    gpointer data)
568 {
569 	GString *result;
570 
571 	result = (GString *) data;
572 
573 	g_string_append_printf (result, "%s\r%d:%d:%hu:%hu\r\n",
574 				uri, x, y, w, h);
575 }
576 
577 static void
578 add_one_uri (const char *uri, int x, int y, int w, int h, gpointer data)
579 {
580 	GString *result;
581 	
582 	result = (GString *) data;
583 
584 	g_string_append (result, uri);
585 	g_string_append (result, "\r\n");
586 }
587 
588 /* Common function for drag_data_get_callback calls.
589  * Returns FALSE if it doesn't handle drag data */
590 gboolean
591 nautilus_drag_drag_data_get (GtkWidget *widget,
592 			GdkDragContext *context,
593 			GtkSelectionData *selection_data,
594 			guint info,
595 			guint32 time,
596 			gpointer container_context,
597 			NautilusDragEachSelectedItemIterator each_selected_item_iterator)
598 {
599 	GString *result;
600 		
601 	switch (info) {
602 	case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
603 		result = g_string_new (NULL);
604 		(* each_selected_item_iterator) (add_one_gnome_icon, container_context, result);
605 		break;
606 		
607 	case NAUTILUS_ICON_DND_URI_LIST:
608 	case NAUTILUS_ICON_DND_TEXT:
609 		result = g_string_new (NULL);
610 		(* each_selected_item_iterator) (add_one_uri, container_context, result);
611 		break;
612 
613 	default:
614 		return FALSE;
615 	}
616 	
617 	gtk_selection_data_set (selection_data,
618 				gtk_selection_data_get_target (selection_data),
619 				8, (guchar *) result->str, result->len);
620 	g_string_free (result, TRUE);
621 
622 	return TRUE;
623 }
624 
625 typedef struct
626 {
627 	GMainLoop *loop;
628 	GdkDragAction chosen;
629 } DropActionMenuData;
630 
631 static void
632 menu_deactivate_callback (GtkWidget *menu,
633 			  gpointer   data)
634 {
635 	DropActionMenuData *damd;
636 	
637 	damd = data;
638 
639 	if (g_main_loop_is_running (damd->loop))
640 		g_main_loop_quit (damd->loop);
641 }
642 
643 static void
644 drop_action_activated_callback (GtkWidget  *menu_item,
645 				gpointer    data)
646 {
647 	DropActionMenuData *damd;
648 	
649 	damd = data;
650 
651 	damd->chosen = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item),
652 							   "action"));
653 
654 	if (g_main_loop_is_running (damd->loop))
655 		g_main_loop_quit (damd->loop);
656 }
657 
658 static void
659 append_drop_action_menu_item (GtkWidget          *menu,
660 			      const char         *text,
661 			      GdkDragAction       action,
662 			      gboolean            sensitive,
663 			      DropActionMenuData *damd)
664 {
665 	GtkWidget *menu_item;
666 
667 	menu_item = gtk_menu_item_new_with_mnemonic (text);
668 	gtk_widget_set_sensitive (menu_item, sensitive);
669 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
670 
671 	g_object_set_data (G_OBJECT (menu_item),
672 			   "action",
673 			   GINT_TO_POINTER (action));
674 	
675 	g_signal_connect (menu_item, "activate",
676 			  G_CALLBACK (drop_action_activated_callback),
677 			  damd);
678 
679 	gtk_widget_show (menu_item);
680 }
681 
682 /* Pops up a menu of actions to perform on dropped files */
683 GdkDragAction
684 nautilus_drag_drop_action_ask (GtkWidget *widget,
685 			       GdkDragAction actions)
686 {
687 	GtkWidget *menu;
688 	GtkWidget *menu_item;
689 	DropActionMenuData damd;
690 	
691 	/* Create the menu and set the sensitivity of the items based on the
692 	 * allowed actions.
693 	 */
694 	menu = gtk_menu_new ();
695 	gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
696 	
697 	append_drop_action_menu_item (menu, _("_Move Here"),
698 				      GDK_ACTION_MOVE,
699 				      (actions & GDK_ACTION_MOVE) != 0,
700 				      &damd);
701 
702 	append_drop_action_menu_item (menu, _("_Copy Here"),
703 				      GDK_ACTION_COPY,
704 				      (actions & GDK_ACTION_COPY) != 0,
705 				      &damd);
706 	
707 	append_drop_action_menu_item (menu, _("_Link Here"),
708 				      GDK_ACTION_LINK,
709 				      (actions & GDK_ACTION_LINK) != 0,
710 				      &damd);
711 
712 	append_drop_action_menu_item (menu, _("Set as _Background"),
713 				      NAUTILUS_DND_ACTION_SET_AS_BACKGROUND,
714 				      (actions & NAUTILUS_DND_ACTION_SET_AS_BACKGROUND) != 0,
715 				      &damd);
716 
717 	eel_gtk_menu_append_separator (GTK_MENU (menu));
718 	
719 	menu_item = gtk_menu_item_new_with_mnemonic (_("Cancel"));
720 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
721 	gtk_widget_show (menu_item);
722 	
723 	damd.chosen = 0;
724 	damd.loop = g_main_loop_new (NULL, FALSE);
725 
726 	g_signal_connect (menu, "deactivate",
727 			  G_CALLBACK (menu_deactivate_callback),
728 			  &damd);
729 	
730 	gtk_grab_add (menu);
731 	
732 	gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
733 			NULL, NULL, 0, GDK_CURRENT_TIME);
734 	
735 	g_main_loop_run (damd.loop);
736 
737 	gtk_grab_remove (menu);
738 	
739 	g_main_loop_unref (damd.loop);
740 
741 	g_object_ref_sink (menu);
742 	g_object_unref (menu);
743 
744 	return damd.chosen;
745 }
746 
747 gboolean
748 nautilus_drag_autoscroll_in_scroll_region (GtkWidget *widget)
749 {
750 	float x_scroll_delta, y_scroll_delta;
751 
752 	nautilus_drag_autoscroll_calculate_delta (widget, &x_scroll_delta, &y_scroll_delta);
753 
754 	return x_scroll_delta != 0 || y_scroll_delta != 0;
755 }
756 
757 
758 void
759 nautilus_drag_autoscroll_calculate_delta (GtkWidget *widget, float *x_scroll_delta, float *y_scroll_delta)
760 {
761 	GtkAllocation allocation;
762 	GdkDeviceManager *manager;
763 	GdkDevice *pointer;
764 	int x, y;
765 
766 	g_assert (GTK_IS_WIDGET (widget));
767 
768 	manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
769 	pointer = gdk_device_manager_get_client_pointer (manager);
770 	gdk_window_get_device_position (gtk_widget_get_window (widget), pointer,
771 					&x, &y, NULL);
772 
773 	/* Find out if we are anywhere close to the tree view edges
774 	 * to see if we need to autoscroll.
775 	 */
776 	*x_scroll_delta = 0;
777 	*y_scroll_delta = 0;
778 	
779 	if (x < AUTO_SCROLL_MARGIN) {
780 		*x_scroll_delta = (float)(x - AUTO_SCROLL_MARGIN);
781 	}
782 
783 	gtk_widget_get_allocation (widget, &allocation);
784 	if (x > allocation.width - AUTO_SCROLL_MARGIN) {
785 		if (*x_scroll_delta != 0) {
786 			/* Already trying to scroll because of being too close to 
787 			 * the top edge -- must be the window is really short,
788 			 * don't autoscroll.
789 			 */
790 			return;
791 		}
792 		*x_scroll_delta = (float)(x - (allocation.width - AUTO_SCROLL_MARGIN));
793 	}
794 
795 	if (y < AUTO_SCROLL_MARGIN) {
796 		*y_scroll_delta = (float)(y - AUTO_SCROLL_MARGIN);
797 	}
798 
799 	if (y > allocation.height - AUTO_SCROLL_MARGIN) {
800 		if (*y_scroll_delta != 0) {
801 			/* Already trying to scroll because of being too close to 
802 			 * the top edge -- must be the window is really narrow,
803 			 * don't autoscroll.
804 			 */
805 			return;
806 		}
807 		*y_scroll_delta = (float)(y - (allocation.height - AUTO_SCROLL_MARGIN));
808 	}
809 
810 	if (*x_scroll_delta == 0 && *y_scroll_delta == 0) {
811 		/* no work */
812 		return;
813 	}
814 
815 	/* Adjust the scroll delta to the proper acceleration values depending on how far
816 	 * into the sroll margins we are.
817 	 * FIXME bugzilla.eazel.com 2486:
818 	 * we could use an exponential acceleration factor here for better feel
819 	 */
820 	if (*x_scroll_delta != 0) {
821 		*x_scroll_delta /= AUTO_SCROLL_MARGIN;
822 		*x_scroll_delta *= (MAX_AUTOSCROLL_DELTA - MIN_AUTOSCROLL_DELTA);
823 		*x_scroll_delta += MIN_AUTOSCROLL_DELTA;
824 	}
825 	
826 	if (*y_scroll_delta != 0) {
827 		*y_scroll_delta /= AUTO_SCROLL_MARGIN;
828 		*y_scroll_delta *= (MAX_AUTOSCROLL_DELTA - MIN_AUTOSCROLL_DELTA);
829 		*y_scroll_delta += MIN_AUTOSCROLL_DELTA;
830 	}
831 
832 }
833 
834 
835 
836 void
837 nautilus_drag_autoscroll_start (NautilusDragInfo *drag_info,
838 				GtkWidget        *widget,
839 				GSourceFunc       callback,
840 				gpointer          user_data)
841 {
842 	if (nautilus_drag_autoscroll_in_scroll_region (widget)) {
843 		if (drag_info->auto_scroll_timeout_id == 0) {
844 			drag_info->waiting_to_autoscroll = TRUE;
845 			drag_info->start_auto_scroll_in = g_get_monotonic_time () 
846 				+ AUTOSCROLL_INITIAL_DELAY;
847 			
848 			drag_info->auto_scroll_timeout_id = g_timeout_add
849 				(AUTOSCROLL_TIMEOUT_INTERVAL,
850 				 callback,
851 			 	 user_data);
852 		}
853 	} else {
854 		if (drag_info->auto_scroll_timeout_id != 0) {
855 			g_source_remove (drag_info->auto_scroll_timeout_id);
856 			drag_info->auto_scroll_timeout_id = 0;
857 		}
858 	}
859 }
860 
861 void
862 nautilus_drag_autoscroll_stop (NautilusDragInfo *drag_info)
863 {
864 	if (drag_info->auto_scroll_timeout_id != 0) {
865 		g_source_remove (drag_info->auto_scroll_timeout_id);
866 		drag_info->auto_scroll_timeout_id = 0;
867 	}
868 }
869 
870 gboolean
871 nautilus_drag_selection_includes_special_link (GList *selection_list)
872 {
873 	GList *node;
874 	char *uri;
875 
876 	for (node = selection_list; node != NULL; node = node->next) {
877 		uri = ((NautilusDragSelectionItem *) node->data)->uri;
878 
879 		if (eel_uri_is_desktop (uri)) {
880 			return TRUE;
881 		}
882 	}
883 	
884 	return FALSE;
885 }