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 }