2008-02-29 Cosimo Cecchi <cosimoc@gnome.org>
[nautilus.git] / libnautilus-private / nautilus-dnd.c
blob97a502ff98a305e21a21604769627a6d7c281d7e
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /* nautilus-dnd.c - Common Drag & drop handling code shared by the icon container
4 and the list view.
6 Copyright (C) 2000, 2001 Eazel, Inc.
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.
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.
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.
23 Authors: Pavel Cisler <pavel@eazel.com>,
24 Ettore Perazzoli <ettore@gnu.org>
27 /* FIXME: This should really be back in Nautilus, not here in Eel. */
29 #include <config.h>
30 #include "nautilus-dnd.h"
32 #include "nautilus-program-choosing.h"
33 #include "nautilus-link.h"
34 #include <eel/eel-glib-extensions.h>
35 #include <eel/eel-gtk-extensions.h>
36 #include <eel/eel-string.h>
37 #include <eel/eel-vfs-extensions.h>
38 #include <gtk/gtkmain.h>
39 #include <gtk/gtkmenu.h>
40 #include <gtk/gtkseparatormenuitem.h>
41 #include <glib/gi18n.h>
42 #include <libgnomeui/gnome-uidefs.h>
43 #include <libnautilus-private/nautilus-file-utilities.h>
44 #include <stdio.h>
45 #include <string.h>
47 /* a set of defines stolen from the eel-icon-dnd.c file.
48 * These are in microseconds.
50 #define AUTOSCROLL_TIMEOUT_INTERVAL 100
51 #define AUTOSCROLL_INITIAL_DELAY 100000
53 /* drag this close to the view edge to start auto scroll*/
54 #define AUTO_SCROLL_MARGIN 30
56 /* the smallest amount of auto scroll used when we just enter the autoscroll
57 * margin
59 #define MIN_AUTOSCROLL_DELTA 5
61 /* the largest amount of auto scroll used when we are right over the view
62 * edge
64 #define MAX_AUTOSCROLL_DELTA 50
66 void
67 nautilus_drag_init (NautilusDragInfo *drag_info,
68 const GtkTargetEntry *drag_types,
69 int drag_type_count,
70 gboolean add_text_targets)
72 drag_info->target_list = gtk_target_list_new (drag_types,
73 drag_type_count);
75 if (add_text_targets) {
76 gtk_target_list_add_text_targets (drag_info->target_list,
77 NAUTILUS_ICON_DND_TEXT);
80 drag_info->drop_occured = FALSE;
81 drag_info->need_to_destroy = FALSE;
84 void
85 nautilus_drag_finalize (NautilusDragInfo *drag_info)
87 gtk_target_list_unref (drag_info->target_list);
88 nautilus_drag_destroy_selection_list (drag_info->selection_list);
90 g_free (drag_info);
94 /* Functions to deal with NautilusDragSelectionItems. */
96 NautilusDragSelectionItem *
97 nautilus_drag_selection_item_new (void)
99 return g_new0 (NautilusDragSelectionItem, 1);
102 static void
103 drag_selection_item_destroy (NautilusDragSelectionItem *item)
105 g_free (item->uri);
106 g_free (item);
109 void
110 nautilus_drag_destroy_selection_list (GList *list)
112 GList *p;
114 if (list == NULL)
115 return;
117 for (p = list; p != NULL; p = p->next)
118 drag_selection_item_destroy (p->data);
120 g_list_free (list);
123 GList *
124 nautilus_drag_build_selection_list (GtkSelectionData *data)
126 GList *result;
127 const guchar *p, *oldp;
128 int size;
130 result = NULL;
131 oldp = data->data;
132 size = data->length;
134 while (size > 0) {
135 NautilusDragSelectionItem *item;
136 guint len;
138 /* The list is in the form:
140 name\rx:y:width:height\r\n
142 The geometry information after the first \r is optional. */
144 /* 1: Decode name. */
146 p = memchr (oldp, '\r', size);
147 if (p == NULL) {
148 break;
151 item = nautilus_drag_selection_item_new ();
153 len = p - oldp;
155 item->uri = g_malloc (len + 1);
156 memcpy (item->uri, oldp, len);
157 item->uri[len] = 0;
159 p++;
160 if (*p == '\n' || *p == '\0') {
161 result = g_list_prepend (result, item);
162 if (p == 0) {
163 g_warning ("Invalid x-special/gnome-icon-list data received: "
164 "missing newline character.");
165 break;
166 } else {
167 oldp = p + 1;
168 continue;
172 size -= p - oldp;
173 oldp = p;
175 /* 2: Decode geometry information. */
177 item->got_icon_position = sscanf (p, "%d:%d:%d:%d%*s",
178 &item->icon_x, &item->icon_y,
179 &item->icon_width, &item->icon_height) == 4;
180 if (!item->got_icon_position) {
181 g_warning ("Invalid x-special/gnome-icon-list data received: "
182 "invalid icon position specification.");
185 result = g_list_prepend (result, item);
187 p = memchr (p, '\r', size);
188 if (p == NULL || p[1] != '\n') {
189 g_warning ("Invalid x-special/gnome-icon-list data received: "
190 "missing newline character.");
191 if (p == NULL) {
192 break;
194 } else {
195 p += 2;
198 size -= p - oldp;
199 oldp = p;
202 return g_list_reverse (result);
205 static gboolean
206 nautilus_drag_file_local_internal (const char *target_uri_string,
207 const char *first_source_uri)
209 /* check if the first item on the list has target_uri_string as a parent
210 * FIXME:
211 * we should really test each item but that would be slow for large selections
212 * and currently dropped items can only be from the same container
214 GFile *target, *item, *parent;
215 gboolean result;
217 result = FALSE;
219 target = g_file_new_for_uri (target_uri_string);
221 /* get the parent URI of the first item in the selection */
222 item = g_file_new_for_uri (first_source_uri);
223 parent = g_file_get_parent (item);
224 g_object_unref (item);
226 if (parent != NULL) {
227 result = g_file_equal (parent, target);
228 g_object_unref (parent);
231 return result;
234 gboolean
235 nautilus_drag_uris_local (const char *target_uri,
236 const GList *source_uri_list)
238 /* must have at least one item */
239 g_assert (source_uri_list);
241 return nautilus_drag_file_local_internal (target_uri, source_uri_list->data);
244 gboolean
245 nautilus_drag_items_local (const char *target_uri_string,
246 const GList *selection_list)
248 /* must have at least one item */
249 g_assert (selection_list);
251 return nautilus_drag_file_local_internal (target_uri_string,
252 ((NautilusDragSelectionItem *)selection_list->data)->uri);
255 gboolean
256 nautilus_drag_items_in_trash (const GList *selection_list)
258 /* check if the first item on the list is in trash.
259 * FIXME:
260 * we should really test each item but that would be slow for large selections
261 * and currently dropped items can only be from the same container
263 return eel_uri_is_trash (((NautilusDragSelectionItem *)selection_list->data)->uri);
266 gboolean
267 nautilus_drag_items_on_desktop (const GList *selection_list)
269 char *uri;
270 GFile *desktop, *item, *parent;
271 gboolean result;
273 /* check if the first item on the list is in trash.
274 * FIXME:
275 * we should really test each item but that would be slow for large selections
276 * and currently dropped items can only be from the same container
278 uri = ((NautilusDragSelectionItem *)selection_list->data)->uri;
279 if (eel_uri_is_desktop (uri)) {
280 return TRUE;
283 desktop = nautilus_get_desktop_location ();
285 item = g_file_new_for_uri (uri);
286 parent = g_file_get_parent (item);
287 g_object_unref (item);
289 result = FALSE;
291 if (parent) {
292 result = g_file_equal (desktop, parent);
293 g_object_unref (parent);
295 g_object_unref (desktop);
297 return result;
301 GdkDragAction
302 nautilus_drag_default_drop_action_for_netscape_url (GdkDragContext *context)
304 /* Mozilla defaults to copy, but unless thats the
305 only allowed thing (enforced by ctrl) we want to ASK */
306 if (context->suggested_action == GDK_ACTION_COPY &&
307 context->actions != GDK_ACTION_COPY) {
308 return GDK_ACTION_ASK;
309 } else if (context->suggested_action == GDK_ACTION_MOVE) {
310 /* Don't support move */
311 return GDK_ACTION_COPY;
314 return context->suggested_action;
317 static gboolean
318 check_same_fs (GFile *file1, GFile *file2)
320 GFileInfo *info1, *info2;
321 const char *id1, *id2;
322 gboolean res;
324 info1 = g_file_query_info (file1,
325 G_FILE_ATTRIBUTE_ID_FILESYSTEM,
326 0, NULL, NULL);
328 if (info1 == NULL) {
329 return FALSE;
332 id1 = g_file_info_get_attribute_string (info1, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
333 if (id1 == NULL) {
334 g_object_unref (info1);
335 return FALSE;
338 info2 = g_file_query_info (file2,
339 G_FILE_ATTRIBUTE_ID_FILESYSTEM,
340 0, NULL, NULL);
341 if (info2 == NULL) {
342 g_object_unref (info1);
343 return FALSE;
346 id2 = g_file_info_get_attribute_string (info2, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
347 if (id2 == NULL) {
348 g_object_unref (info1);
349 g_object_unref (info2);
350 return FALSE;
353 res = strcmp (id1, id2) == 0;
355 g_object_unref (info1);
356 g_object_unref (info2);
358 return res;
361 void
362 nautilus_drag_default_drop_action_for_icons (GdkDragContext *context,
363 const char *target_uri_string, const GList *items,
364 int *action)
366 gboolean same_fs;
367 gboolean target_is_source_parent;
368 const char *dropped_uri;
369 GFile *target, *dropped;
370 GdkDragAction actions;
371 NautilusFile *target_file;
373 if (target_uri_string == NULL) {
374 *action = 0;
375 return;
378 actions = context->actions & (GDK_ACTION_MOVE | GDK_ACTION_COPY);
379 if (actions == 0) {
380 /* We can't use copy or move, just go with the suggested action. */
381 *action = context->suggested_action;
382 return;
385 if (context->suggested_action == GDK_ACTION_ASK) {
386 /* Don't override ask */
387 *action = context->suggested_action;
388 return;
391 dropped_uri = ((NautilusDragSelectionItem *)items->data)->uri;
392 target_file = nautilus_file_get_existing_by_uri (dropped_uri);
395 * Check for trash URI. We do a find_directory for any Trash directory.
396 * Passing 0 permissions as gnome-vfs would override the permissions
397 * passed with 700 while creating .Trash directory
399 if (eel_uri_is_trash (target_uri_string)) {
400 /* Only move to Trash */
401 if (actions & GDK_ACTION_MOVE) {
402 *action = GDK_ACTION_MOVE;
405 nautilus_file_unref (target_file);
406 return;
408 } else if (target_file != NULL && nautilus_file_is_launcher (target_file)) {
409 if (actions & GDK_ACTION_MOVE) {
410 *action = GDK_ACTION_MOVE;
412 nautilus_file_unref (target_file);
413 return;
414 } else if (eel_uri_is_desktop (target_uri_string)) {
415 target = nautilus_get_desktop_location ();
416 if (eel_uri_is_desktop (dropped_uri)) {
417 /* Only move to Desktop icons */
418 if (actions & GDK_ACTION_MOVE) {
419 *action = GDK_ACTION_MOVE;
422 nautilus_file_unref (target_file);
423 return;
425 } else {
426 target = g_file_new_for_uri (target_uri_string);
429 nautilus_file_unref (target_file);
431 /* Compare the first dropped uri with the target uri for same fs match. */
432 dropped = g_file_new_for_uri (dropped_uri);
433 same_fs = check_same_fs (target, dropped);
434 target_is_source_parent = g_file_has_prefix (dropped, target);
436 if (same_fs || target_is_source_parent ||
437 g_file_has_uri_scheme (dropped, "trash")) {
438 if (actions & GDK_ACTION_MOVE) {
439 *action = GDK_ACTION_MOVE;
440 } else {
441 *action = context->suggested_action;
443 } else {
444 if (actions & GDK_ACTION_COPY) {
445 *action = GDK_ACTION_COPY;
446 } else {
447 *action = context->suggested_action;
451 g_object_unref (target);
452 g_object_unref (dropped);
456 /* Encode a "x-special/gnome-icon-list" selection.
457 Along with the URIs of the dragged files, this encodes
458 the location and size of each icon relative to the cursor.
460 static void
461 add_one_gnome_icon (const char *uri, int x, int y, int w, int h,
462 gpointer data)
464 GString *result;
466 result = (GString *) data;
468 g_string_append_printf (result, "%s\r%d:%d:%hu:%hu\r\n",
469 uri, x, y, w, h);
473 * Cf. #48423
475 #ifdef THIS_WAS_REALLY_BROKEN
476 static gboolean
477 is_path_that_gnome_uri_list_extract_filenames_can_parse (const char *path)
479 if (path == NULL || path [0] == '\0') {
480 return FALSE;
483 /* It strips leading and trailing spaces. So it can't handle
484 * file names with leading and trailing spaces.
486 if (g_ascii_isspace (path [0])) {
487 return FALSE;
489 if (g_ascii_isspace (path [strlen (path) - 1])) {
490 return FALSE;
493 /* # works as a comment delimiter, and \r and \n are used to
494 * separate the lines, so it can't handle file names with any
495 * of these.
497 if (strchr (path, '#') != NULL
498 || strchr (path, '\r') != NULL
499 || strchr (path, '\n') != NULL) {
500 return FALSE;
503 return TRUE;
506 /* Encode a "text/plain" selection; this is a broken URL -- just
507 * "file:" with a path after it (no escaping or anything). We are
508 * trying to make the old gnome_uri_list_extract_filenames function
509 * happy, so this is coded to its idiosyncrasises.
511 static void
512 add_one_compatible_uri (const char *uri, int x, int y, int w, int h, gpointer data)
514 GString *result;
515 char *local_path;
517 result = (GString *) data;
519 /* For URLs that do not have a file: scheme, there's no harm
520 * in passing the real URL. But for URLs that do have a file:
521 * scheme, we have to send a URL that will work with the old
522 * gnome-libs function or nothing will be able to understand
523 * it.
525 if (!eel_istr_has_prefix (uri, "file:")) {
526 g_string_append (result, uri);
527 g_string_append (result, "\r\n");
528 } else {
529 local_path = g_filename_from_uri (uri, NULL, NULL);
531 /* Check for characters that confuse the old
532 * gnome_uri_list_extract_filenames implementation, and just leave
533 * out any paths with those in them.
535 if (is_path_that_gnome_uri_list_extract_filenames_can_parse (local_path)) {
536 g_string_append (result, "file:");
537 g_string_append (result, local_path);
538 g_string_append (result, "\r\n");
541 g_free (local_path);
544 #endif
546 static void
547 add_one_uri (const char *uri, int x, int y, int w, int h, gpointer data)
549 GString *result;
551 result = (GString *) data;
553 g_string_append (result, uri);
554 g_string_append (result, "\r\n");
557 /* Common function for drag_data_get_callback calls.
558 * Returns FALSE if it doesn't handle drag data */
559 gboolean
560 nautilus_drag_drag_data_get (GtkWidget *widget,
561 GdkDragContext *context,
562 GtkSelectionData *selection_data,
563 guint info,
564 guint32 time,
565 gpointer container_context,
566 NautilusDragEachSelectedItemIterator each_selected_item_iterator)
568 GString *result;
570 switch (info) {
571 case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
572 result = g_string_new (NULL);
573 (* each_selected_item_iterator) (add_one_gnome_icon, container_context, result);
574 break;
576 case NAUTILUS_ICON_DND_URI_LIST:
577 case NAUTILUS_ICON_DND_TEXT:
578 result = g_string_new (NULL);
579 (* each_selected_item_iterator) (add_one_uri, container_context, result);
580 break;
582 default:
583 return FALSE;
586 gtk_selection_data_set (selection_data,
587 selection_data->target,
588 8, result->str, result->len);
590 return TRUE;
593 typedef struct
595 GMainLoop *loop;
596 GdkDragAction chosen;
597 } DropActionMenuData;
599 static void
600 menu_deactivate_callback (GtkWidget *menu,
601 gpointer data)
603 DropActionMenuData *damd;
605 damd = data;
607 if (g_main_loop_is_running (damd->loop))
608 g_main_loop_quit (damd->loop);
611 static void
612 drop_action_activated_callback (GtkWidget *menu_item,
613 gpointer data)
615 DropActionMenuData *damd;
617 damd = data;
619 damd->chosen = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item),
620 "action"));
622 if (g_main_loop_is_running (damd->loop))
623 g_main_loop_quit (damd->loop);
626 static void
627 append_drop_action_menu_item (GtkWidget *menu,
628 const char *text,
629 GdkDragAction action,
630 gboolean sensitive,
631 DropActionMenuData *damd)
633 GtkWidget *menu_item;
635 menu_item = gtk_menu_item_new_with_mnemonic (text);
636 gtk_widget_set_sensitive (menu_item, sensitive);
637 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
639 g_object_set_data (G_OBJECT (menu_item),
640 "action",
641 GINT_TO_POINTER (action));
643 g_signal_connect (menu_item, "activate",
644 G_CALLBACK (drop_action_activated_callback),
645 damd);
647 gtk_widget_show (menu_item);
650 /* Pops up a menu of actions to perform on dropped files */
651 GdkDragAction
652 nautilus_drag_drop_action_ask (GtkWidget *widget,
653 GdkDragAction actions)
655 GtkWidget *menu;
656 GtkWidget *menu_item;
657 DropActionMenuData damd;
659 /* Create the menu and set the sensitivity of the items based on the
660 * allowed actions.
662 menu = gtk_menu_new ();
663 gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
665 append_drop_action_menu_item (menu, _("_Move Here"),
666 GDK_ACTION_MOVE,
667 (actions & GDK_ACTION_MOVE) != 0,
668 &damd);
670 append_drop_action_menu_item (menu, _("_Copy Here"),
671 GDK_ACTION_COPY,
672 (actions & GDK_ACTION_COPY) != 0,
673 &damd);
675 append_drop_action_menu_item (menu, _("_Link Here"),
676 GDK_ACTION_LINK,
677 (actions & GDK_ACTION_LINK) != 0,
678 &damd);
680 append_drop_action_menu_item (menu, _("Set as _Background"),
681 NAUTILUS_DND_ACTION_SET_AS_BACKGROUND,
682 (actions & NAUTILUS_DND_ACTION_SET_AS_BACKGROUND) != 0,
683 &damd);
685 eel_gtk_menu_append_separator (GTK_MENU (menu));
687 menu_item = gtk_menu_item_new_with_mnemonic (_("Cancel"));
688 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
689 gtk_widget_show (menu_item);
691 damd.chosen = 0;
692 damd.loop = g_main_loop_new (NULL, FALSE);
694 g_signal_connect (menu, "deactivate",
695 G_CALLBACK (menu_deactivate_callback),
696 &damd);
698 gtk_grab_add (menu);
700 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
701 NULL, NULL, 0, GDK_CURRENT_TIME);
703 g_main_loop_run (damd.loop);
705 gtk_grab_remove (menu);
707 g_main_loop_unref (damd.loop);
709 gtk_object_sink (GTK_OBJECT (menu));
711 return damd.chosen;
714 GdkDragAction
715 nautilus_drag_drop_background_ask (GtkWidget *widget,
716 GdkDragAction actions)
718 GtkWidget *menu;
719 GtkWidget *menu_item;
720 DropActionMenuData damd;
722 /* Create the menu and set the sensitivity of the items based on the
723 * allowed actions.
725 menu = gtk_menu_new ();
726 gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
728 append_drop_action_menu_item (menu, _("Set as background for _all folders"),
729 NAUTILUS_DND_ACTION_SET_AS_GLOBAL_BACKGROUND,
730 (actions & NAUTILUS_DND_ACTION_SET_AS_GLOBAL_BACKGROUND) != 0,
731 &damd);
733 append_drop_action_menu_item (menu, _("Set as background for _this folder"),
734 NAUTILUS_DND_ACTION_SET_AS_FOLDER_BACKGROUND,
735 (actions & NAUTILUS_DND_ACTION_SET_AS_FOLDER_BACKGROUND) != 0,
736 &damd);
738 eel_gtk_menu_append_separator (GTK_MENU (menu));
740 menu_item = gtk_menu_item_new_with_mnemonic (_("Cancel"));
741 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
742 gtk_widget_show (menu_item);
744 damd.chosen = 0;
745 damd.loop = g_main_loop_new (NULL, FALSE);
747 g_signal_connect (menu, "deactivate",
748 G_CALLBACK (menu_deactivate_callback),
749 &damd);
751 gtk_grab_add (menu);
753 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
754 NULL, NULL, 0, GDK_CURRENT_TIME);
756 g_main_loop_run (damd.loop);
758 gtk_grab_remove (menu);
760 g_main_loop_unref (damd.loop);
762 gtk_object_sink (GTK_OBJECT (menu));
764 return damd.chosen;
767 gboolean
768 nautilus_drag_autoscroll_in_scroll_region (GtkWidget *widget)
770 float x_scroll_delta, y_scroll_delta;
772 nautilus_drag_autoscroll_calculate_delta (widget, &x_scroll_delta, &y_scroll_delta);
774 return x_scroll_delta != 0 || y_scroll_delta != 0;
778 void
779 nautilus_drag_autoscroll_calculate_delta (GtkWidget *widget, float *x_scroll_delta, float *y_scroll_delta)
781 int x, y;
783 g_assert (GTK_IS_WIDGET (widget));
785 gdk_window_get_pointer (widget->window, &x, &y, NULL);
787 /* Find out if we are anywhere close to the tree view edges
788 * to see if we need to autoscroll.
790 *x_scroll_delta = 0;
791 *y_scroll_delta = 0;
793 if (x < AUTO_SCROLL_MARGIN) {
794 *x_scroll_delta = (float)(x - AUTO_SCROLL_MARGIN);
797 if (x > widget->allocation.width - AUTO_SCROLL_MARGIN) {
798 if (*x_scroll_delta != 0) {
799 /* Already trying to scroll because of being too close to
800 * the top edge -- must be the window is really short,
801 * don't autoscroll.
803 return;
805 *x_scroll_delta = (float)(x - (widget->allocation.width - AUTO_SCROLL_MARGIN));
808 if (y < AUTO_SCROLL_MARGIN) {
809 *y_scroll_delta = (float)(y - AUTO_SCROLL_MARGIN);
812 if (y > widget->allocation.height - AUTO_SCROLL_MARGIN) {
813 if (*y_scroll_delta != 0) {
814 /* Already trying to scroll because of being too close to
815 * the top edge -- must be the window is really narrow,
816 * don't autoscroll.
818 return;
820 *y_scroll_delta = (float)(y - (widget->allocation.height - AUTO_SCROLL_MARGIN));
823 if (*x_scroll_delta == 0 && *y_scroll_delta == 0) {
824 /* no work */
825 return;
828 /* Adjust the scroll delta to the proper acceleration values depending on how far
829 * into the sroll margins we are.
830 * FIXME bugzilla.eazel.com 2486:
831 * we could use an exponential acceleration factor here for better feel
833 if (*x_scroll_delta != 0) {
834 *x_scroll_delta /= AUTO_SCROLL_MARGIN;
835 *x_scroll_delta *= (MAX_AUTOSCROLL_DELTA - MIN_AUTOSCROLL_DELTA);
836 *x_scroll_delta += MIN_AUTOSCROLL_DELTA;
839 if (*y_scroll_delta != 0) {
840 *y_scroll_delta /= AUTO_SCROLL_MARGIN;
841 *y_scroll_delta *= (MAX_AUTOSCROLL_DELTA - MIN_AUTOSCROLL_DELTA);
842 *y_scroll_delta += MIN_AUTOSCROLL_DELTA;
849 void
850 nautilus_drag_autoscroll_start (NautilusDragInfo *drag_info,
851 GtkWidget *widget,
852 GtkFunction callback,
853 gpointer user_data)
855 if (nautilus_drag_autoscroll_in_scroll_region (widget)) {
856 if (drag_info->auto_scroll_timeout_id == 0) {
857 drag_info->waiting_to_autoscroll = TRUE;
858 drag_info->start_auto_scroll_in = eel_get_system_time()
859 + AUTOSCROLL_INITIAL_DELAY;
861 drag_info->auto_scroll_timeout_id = g_timeout_add
862 (AUTOSCROLL_TIMEOUT_INTERVAL,
863 callback,
864 user_data);
866 } else {
867 if (drag_info->auto_scroll_timeout_id != 0) {
868 g_source_remove (drag_info->auto_scroll_timeout_id);
869 drag_info->auto_scroll_timeout_id = 0;
874 void
875 nautilus_drag_autoscroll_stop (NautilusDragInfo *drag_info)
877 if (drag_info->auto_scroll_timeout_id != 0) {
878 g_source_remove (drag_info->auto_scroll_timeout_id);
879 drag_info->auto_scroll_timeout_id = 0;
883 gboolean
884 nautilus_drag_selection_includes_special_link (GList *selection_list)
886 GList *node;
887 char *uri;
889 for (node = selection_list; node != NULL; node = node->next) {
890 uri = ((NautilusDragSelectionItem *) node->data)->uri;
892 if (eel_uri_is_desktop (uri)) {
893 return TRUE;
897 return FALSE;