1 /* -*- Mode: C; indent-tabs-mode: f; c-basic-offset: 4; tab-width: 4 -*- */
2
3 /* nemo-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., 51 Franklin Street - Suite 500,
21 Boston, MA 02110-1335, USA.
22
23 Authors: Pavel Cisler <pavel@eazel.com>,
24 Ettore Perazzoli <ettore@gnu.org>
25 */
26
27 #include <config.h>
28 #include "nemo-dnd.h"
29
30 #include "nemo-program-choosing.h"
31 #include "nemo-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 <libnemo-private/nemo-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
nemo_drag_init(NemoDragInfo * drag_info,const GtkTargetEntry * drag_types,int drag_type_count,gboolean add_text_targets)62 nemo_drag_init (NemoDragInfo *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 NEMO_ICON_DND_TEXT);
73 }
74
75 drag_info->drop_occured = FALSE;
76 drag_info->need_to_destroy = FALSE;
77 drag_info->source_fs = NULL;
78 drag_info->can_delete_source = FALSE;
79 }
80
81 void
nemo_drag_finalize(NemoDragInfo * drag_info)82 nemo_drag_finalize (NemoDragInfo *drag_info)
83 {
84 gtk_target_list_unref (drag_info->target_list);
85 nemo_drag_destroy_selection_list (drag_info->selection_list);
86 g_free (drag_info->source_fs);
87 drag_info->can_delete_source = FALSE;
88
89 g_free (drag_info);
90 }
91
92
93 /* Functions to deal with NemoDragSelectionItems. */
94
95 NemoDragSelectionItem *
nemo_drag_selection_item_new(void)96 nemo_drag_selection_item_new (void)
97 {
98 return g_new0 (NemoDragSelectionItem, 1);
99 }
100
101 static void
drag_selection_item_destroy(NemoDragSelectionItem * item)102 drag_selection_item_destroy (NemoDragSelectionItem *item)
103 {
104 g_free (item->uri);
105 g_free (item);
106 }
107
108 void
nemo_drag_destroy_selection_list(GList * list)109 nemo_drag_destroy_selection_list (GList *list)
110 {
111 GList *p;
112
113 if (list == NULL)
114 return;
115
116 for (p = list; p != NULL; p = p->next)
117 drag_selection_item_destroy (p->data);
118
119 g_list_free (list);
120 }
121
122 char **
nemo_drag_uri_array_from_selection_list(const GList * selection_list)123 nemo_drag_uri_array_from_selection_list (const GList *selection_list)
124 {
125 GList *uri_list;
126 char **uris;
127
128 uri_list = nemo_drag_uri_list_from_selection_list (selection_list);
129 uris = nemo_drag_uri_array_from_list (uri_list);
130 g_list_free_full (uri_list, g_free);
131
132 return uris;
133 }
134
135 GList *
nemo_drag_uri_list_from_selection_list(const GList * selection_list)136 nemo_drag_uri_list_from_selection_list (const GList *selection_list)
137 {
138 NemoDragSelectionItem *selection_item;
139 GList *uri_list;
140 const GList *l;
141
142 uri_list = NULL;
143 for (l = selection_list; l != NULL; l = l->next) {
144 selection_item = (NemoDragSelectionItem *) l->data;
145 if (selection_item->uri != NULL) {
146 uri_list = g_list_prepend (uri_list, g_strdup (selection_item->uri));
147 }
148 }
149
150 return g_list_reverse (uri_list);
151 }
152
153 char **
nemo_drag_uri_array_from_list(const GList * uri_list)154 nemo_drag_uri_array_from_list (const GList *uri_list)
155 {
156 const GList *l;
157 char **uris;
158 int i;
159
160 if (uri_list == NULL) {
161 return NULL;
162 }
163
164 uris = g_new0 (char *, g_list_length ((GList *) uri_list));
165 for (i = 0, l = uri_list; l != NULL; l = l->next) {
166 uris[i++] = g_strdup ((char *) l->data);
167 }
168 uris[i] = NULL;
169
170 return uris;
171 }
172
173 GList *
nemo_drag_uri_list_from_array(const char ** uris)174 nemo_drag_uri_list_from_array (const char **uris)
175 {
176 GList *uri_list;
177 int i;
178
179 if (uris == NULL) {
180 return NULL;
181 }
182
183 uri_list = NULL;
184
185 for (i = 0; uris[i] != NULL; i++) {
186 uri_list = g_list_prepend (uri_list, g_strdup (uris[i]));
187 }
188
189 return g_list_reverse (uri_list);
190 }
191
192 GList *
nemo_drag_build_selection_list(GtkSelectionData * data)193 nemo_drag_build_selection_list (GtkSelectionData *data)
194 {
195 GList *result;
196 const guchar *p, *oldp;
197 int size;
198
199 result = NULL;
200 oldp = gtk_selection_data_get_data (data);
201 size = gtk_selection_data_get_length (data);
202
203 while (size > 0) {
204 NemoDragSelectionItem *item;
205 guint len;
206
207 /* The list is in the form:
208
209 name\rx:y:width:height\r\n
210
211 The geometry information after the first \r is optional. */
212
213 /* 1: Decode name. */
214
215 p = memchr (oldp, '\r', size);
216 if (p == NULL) {
217 break;
218 }
219
220 item = nemo_drag_selection_item_new ();
221
222 len = p - oldp;
223
224 item->uri = g_malloc (len + 1);
225 memcpy (item->uri, oldp, len);
226 item->uri[len] = 0;
227
228 p++;
229 if (*p == '\n' || *p == '\0') {
230 result = g_list_prepend (result, item);
231 if (p == 0) {
232 g_warning ("Invalid x-special/gnome-icon-list data received: "
233 "missing newline character.");
234 break;
235 } else {
236 oldp = p + 1;
237 continue;
238 }
239 }
240
241 size -= p - oldp;
242 oldp = p;
243
244 /* 2: Decode geometry information. */
245
246 item->got_icon_position = sscanf ((const gchar *) p, "%d:%d:%d:%d%*s",
247 &item->icon_x, &item->icon_y,
248 &item->icon_width, &item->icon_height) == 4;
249 if (!item->got_icon_position) {
250 g_warning ("Invalid x-special/gnome-icon-list data received: "
251 "invalid icon position specification.");
252 }
253
254 result = g_list_prepend (result, item);
255
256 p = memchr (p, '\r', size);
257 if (p == NULL || p[1] != '\n') {
258 g_warning ("Invalid x-special/gnome-icon-list data received: "
259 "missing newline character.");
260 if (p == NULL) {
261 break;
262 }
263 } else {
264 p += 2;
265 }
266
267 size -= p - oldp;
268 oldp = p;
269 }
270
271 return g_list_reverse (result);
272 }
273
274 static gboolean
nemo_drag_file_local_internal(const char * target_uri_string,const char * first_source_uri)275 nemo_drag_file_local_internal (const char *target_uri_string,
276 const char *first_source_uri)
277 {
278 /* check if the first item on the list has target_uri_string as a parent
279 * FIXME:
280 * we should really test each item but that would be slow for large selections
281 * and currently dropped items can only be from the same container
282 */
283 GFile *target, *item, *parent;
284 gboolean result;
285
286 result = FALSE;
287
288 target = g_file_new_for_uri (target_uri_string);
289
290 /* get the parent URI of the first item in the selection */
291 item = g_file_new_for_uri (first_source_uri);
292 parent = g_file_get_parent (item);
293 g_object_unref (item);
294
295 if (parent != NULL) {
296 result = g_file_equal (parent, target);
297 g_object_unref (parent);
298 }
299
300 g_object_unref (target);
301
302 return result;
303 }
304
305 gboolean
nemo_drag_uris_local(const char * target_uri,const GList * source_uri_list)306 nemo_drag_uris_local (const char *target_uri,
307 const GList *source_uri_list)
308 {
309 /* must have at least one item */
310 g_assert (source_uri_list);
311
312 return nemo_drag_file_local_internal (target_uri, source_uri_list->data);
313 }
314
315 gboolean
nemo_drag_items_local(const char * target_uri_string,const GList * selection_list)316 nemo_drag_items_local (const char *target_uri_string,
317 const GList *selection_list)
318 {
319 /* must have at least one item */
320 g_assert (selection_list);
321
322 return nemo_drag_file_local_internal (target_uri_string,
323 ((NemoDragSelectionItem *)selection_list->data)->uri);
324 }
325
326 gboolean
nemo_drag_items_in_trash(const GList * selection_list)327 nemo_drag_items_in_trash (const GList *selection_list)
328 {
329 /* check if the first item on the list is in trash.
330 * FIXME:
331 * we should really test each item but that would be slow for large selections
332 * and currently dropped items can only be from the same container
333 */
334 return eel_uri_is_trash (((NemoDragSelectionItem *)selection_list->data)->uri);
335 }
336
337 gboolean
nemo_drag_items_on_desktop(const GList * selection_list)338 nemo_drag_items_on_desktop (const GList *selection_list)
339 {
340 char *uri;
341 GFile *desktop, *item, *parent;
342 gboolean result;
343
344 /* check if the first item on the list is in trash.
345 * FIXME:
346 * we should really test each item but that would be slow for large selections
347 * and currently dropped items can only be from the same container
348 */
349 uri = ((NemoDragSelectionItem *)selection_list->data)->uri;
350 if (eel_uri_is_desktop (uri)) {
351 return TRUE;
352 }
353
354 desktop = nemo_get_desktop_location ();
355
356 item = g_file_new_for_uri (uri);
357 parent = g_file_get_parent (item);
358 g_object_unref (item);
359
360 result = FALSE;
361
362 if (parent) {
363 result = g_file_equal (desktop, parent);
364 g_object_unref (parent);
365 }
366 g_object_unref (desktop);
367
368 return result;
369
370 }
371
372 GdkDragAction
nemo_drag_default_drop_action_for_netscape_url(GdkDragContext * context)373 nemo_drag_default_drop_action_for_netscape_url (GdkDragContext *context)
374 {
375 /* Mozilla defaults to copy, but unless thats the
376 only allowed thing (enforced by ctrl) we want to LINK */
377 if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY &&
378 gdk_drag_context_get_actions (context) != GDK_ACTION_COPY) {
379 return GDK_ACTION_LINK;
380 } else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_MOVE) {
381 /* Don't support move */
382 return GDK_ACTION_COPY;
383 }
384
385 return gdk_drag_context_get_suggested_action (context);
386 }
387
388 static gboolean
check_same_fs(NemoFile * target_file,NemoFile * source_file,const gchar * source_fs_for_desktop)389 check_same_fs (NemoFile *target_file,
390 NemoFile *source_file,
391 const gchar *source_fs_for_desktop)
392 {
393 char *target_id, *source_id;
394 gboolean result;
395
396 result = FALSE;
397
398 if (target_file != NULL && source_file != NULL) {
399 source_id = nemo_file_get_filesystem_id (source_file);
400 target_id = nemo_file_get_filesystem_id (target_file);
401
402 if (source_id != NULL && target_id != NULL) {
403 result = (strcmp (source_id, target_id) == 0);
404 }
405
406 g_free (source_id);
407 g_free (target_id);
408 } else if (target_file != NULL && source_fs_for_desktop != NULL) {
409 target_id = nemo_file_get_filesystem_id (target_file);
410
411 if (target_id != NULL) {
412 result = (strcmp (source_fs_for_desktop, target_id) == 0);
413 }
414
415 g_free (target_id);
416 }
417
418 return result;
419 }
420
421 static gboolean
source_is_deletable(GFile * file)422 source_is_deletable (GFile *file)
423 {
424 NemoFile *naut_file;
425 gboolean ret;
426
427 /* if there's no a cached NemoFile, it returns NULL */
428 naut_file = nemo_file_get_existing (file);
429 if (naut_file == NULL) {
430 return FALSE;
431 }
432
433 ret = nemo_file_can_delete (naut_file);
434 nemo_file_unref (naut_file);
435
436 return ret;
437 }
438
439 static gboolean
uri_contains_desktop(const gchar * uri)440 uri_contains_desktop (const gchar *uri)
441 {
442 gchar *real_desktop_uri;
443
444 if (eel_uri_is_desktop (uri)) {
445 return TRUE;
446 }
447
448 real_desktop_uri = nemo_get_desktop_directory_uri ();
449
450 if (g_str_has_prefix (uri, real_desktop_uri)) {
451 g_free (real_desktop_uri);
452 return TRUE;
453 }
454
455 return FALSE;
456 }
457
458 void
nemo_drag_default_drop_action_for_icons(GdkDragContext * context,const char * target_uri_string,const GList * items,int * action,gchar ** source_fs,gboolean * can_delete_source)459 nemo_drag_default_drop_action_for_icons (GdkDragContext *context,
460 const char *target_uri_string,
461 const GList *items,
462 int *action,
463 gchar **source_fs,
464 gboolean *can_delete_source)
465 {
466 gboolean same_fs;
467 gboolean target_is_source_parent;
468 gboolean source_deletable;
469 const char *dropped_uri;
470 GFile *target, *dropped, *dropped_directory;
471 GdkDragAction actions;
472 NemoFile *dropped_file, *target_file;
473 gboolean fav_target, fav_item;
474
475 if (target_uri_string == NULL) {
476 *action = 0;
477 return;
478 }
479
480 dropped_uri = ((NemoDragSelectionItem *)items->data)->uri;
481
482 fav_target = eel_uri_is_favorite (target_uri_string);
483 fav_item = eel_uri_is_favorite (dropped_uri);
484
485 if (fav_item && fav_target) {
486 *action = 0;
487 return;
488 }
489
490 if (fav_item != fav_target) {
491 *action = GDK_ACTION_COPY;
492 return;
493 }
494
495 actions = gdk_drag_context_get_actions (context) & (GDK_ACTION_MOVE | GDK_ACTION_COPY);
496 if (actions == 0) {
497 /* We can't use copy or move, just go with the suggested action. */
498 *action = gdk_drag_context_get_suggested_action (context);
499 return;
500 }
501
502 if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_ASK) {
503 /* Don't override ask */
504 *action = gdk_drag_context_get_suggested_action (context);
505 return;
506 }
507
508 dropped_file = nemo_file_get_existing_by_uri (dropped_uri);
509
510 /* To/from desktop preparation - since we are separate processes, we don't have the full filesystem
511 * info on the source and destination - we only have the destination info. Creating a NemoFile for it
512 * is an async operation, so here we'll grab the source filesystem type and store it for the duration of
513 * the drag. We can use it to compare below with the destination type to ensure a proper move action -
514 * (we default to copy when we can't confirm source and destination fs types are the same.)
515 */
516 if (dropped_file == NULL && (uri_contains_desktop (target_uri_string) || uri_contains_desktop (dropped_uri))) {
517 if (*source_fs == NULL) {
518 GFile *source_file;
519
520 source_file = g_file_new_for_uri (dropped_uri);
521
522 if (g_file_is_native (source_file)) {
523 GFileInfo *fs_info;
524 GError *error = NULL;
525
526 fs_info = g_file_query_info (source_file,
527 G_FILE_ATTRIBUTE_ID_FILESYSTEM "," G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
528 G_FILE_QUERY_INFO_NONE,
529 NULL,
530 &error);
531
532 if (error != NULL) {
533 g_warning ("Cannot fetch filesystem type for drag involving the desktop: %s", error->message);
534 g_clear_error (&error);
535 } else {
536 if (g_file_info_has_attribute (fs_info, G_FILE_ATTRIBUTE_ID_FILESYSTEM)) {
537 *source_fs = g_strdup (g_file_info_get_attribute_string (fs_info,
538 G_FILE_ATTRIBUTE_ID_FILESYSTEM));
539 } else {
540 *source_fs = NULL;
541 }
542
543 if (g_file_info_has_attribute (fs_info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE)) {
544 *can_delete_source = g_file_info_get_attribute_boolean (fs_info,
545 G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE);
546 } else {
547 *can_delete_source = FALSE;
548 }
549 }
550
551 g_object_unref (fs_info);
552 }
553
554 g_object_unref (source_file);
555 }
556 }
557
558 target_file = nemo_file_get_existing_by_uri (target_uri_string);
559
560 /*
561 * Check for trash URI. We do a find_directory for any Trash directory.
562 * Passing 0 permissions as gnome-vfs would override the permissions
563 * passed with 700 while creating .Trash directory
564 */
565 if (eel_uri_is_trash (target_uri_string)) {
566 /* Only move to Trash */
567 if (actions & GDK_ACTION_MOVE) {
568 *action = GDK_ACTION_MOVE;
569 }
570
571 nemo_file_unref (dropped_file);
572 nemo_file_unref (target_file);
573 return;
574
575 } else if (dropped_file != NULL && nemo_file_is_launcher (dropped_file)) {
576 if (actions & GDK_ACTION_MOVE) {
577 *action = GDK_ACTION_MOVE;
578 }
579 nemo_file_unref (dropped_file);
580 nemo_file_unref (target_file);
581 return;
582 } else if (eel_uri_is_desktop (target_uri_string)) {
583 target = nemo_get_desktop_location ();
584
585 nemo_file_unref (target_file);
586 target_file = nemo_file_get (target);
587
588 if (eel_uri_is_desktop (dropped_uri)) {
589 /* Only move to Desktop icons */
590 if (actions & GDK_ACTION_MOVE) {
591 *action = GDK_ACTION_MOVE;
592 }
593
594 g_object_unref (target);
595 nemo_file_unref (dropped_file);
596 nemo_file_unref (target_file);
597 return;
598 }
599 } else if (target_file != NULL && nemo_file_is_archive (target_file)) {
600 *action = GDK_ACTION_COPY;
601
602 nemo_file_unref (dropped_file);
603 nemo_file_unref (target_file);
604 return;
605 } else {
606 target = g_file_new_for_uri (target_uri_string);
607 }
608
609 same_fs = check_same_fs (target_file, dropped_file, *source_fs);
610
611 nemo_file_unref (dropped_file);
612 nemo_file_unref (target_file);
613
614 /* Compare the first dropped uri with the target uri for same fs match. */
615 dropped = g_file_new_for_uri (dropped_uri);
616 dropped_directory = g_file_get_parent (dropped);
617 target_is_source_parent = FALSE;
618 if (dropped_directory != NULL) {
619 /* If the dropped file is already in the same directory but
620 is in another filesystem we still want to move, not copy
621 as this is then just a move of a mountpoint to another
622 position in the dir */
623 target_is_source_parent = g_file_equal (dropped_directory, target);
624 g_object_unref (dropped_directory);
625 }
626 source_deletable = source_is_deletable (dropped);
627
628 if ((same_fs && (source_deletable || *can_delete_source)) || target_is_source_parent ||
629 g_file_has_uri_scheme (dropped, "trash")) {
630 if (actions & GDK_ACTION_MOVE) {
631 *action = GDK_ACTION_MOVE;
632 } else {
633 *action = gdk_drag_context_get_suggested_action (context);
634 }
635 } else {
636 if (actions & GDK_ACTION_COPY) {
637 *action = GDK_ACTION_COPY;
638 } else {
639 *action = gdk_drag_context_get_suggested_action (context);
640 }
641 }
642
643 g_object_unref (target);
644 g_object_unref (dropped);
645
646 }
647
648 GdkDragAction
nemo_drag_default_drop_action_for_uri_list(GdkDragContext * context,const char * target_uri_string)649 nemo_drag_default_drop_action_for_uri_list (GdkDragContext *context,
650 const char *target_uri_string)
651 {
652 if (eel_uri_is_trash (target_uri_string) && (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE)) {
653 /* Only move to Trash */
654 return GDK_ACTION_MOVE;
655 } else {
656 return gdk_drag_context_get_suggested_action (context);
657 }
658 }
659
660 /* Encode a "x-special/gnome-icon-list" selection.
661 Along with the URIs of the dragged files, this encodes
662 the location and size of each icon relative to the cursor.
663 */
664 static void
add_one_gnome_icon(const char * uri,int x,int y,int w,int h,gpointer data)665 add_one_gnome_icon (const char *uri, int x, int y, int w, int h,
666 gpointer data)
667 {
668 GString *result;
669
670 result = (GString *) data;
671 g_string_append_printf (result, "%s\r%d:%d:%hu:%hu\r\n",
672 uri, x, y, w, h);
673 }
674
675 /*
676 * Cf. #48423
677 */
678 #ifdef THIS_WAS_REALLY_BROKEN
679 static gboolean
is_path_that_gnome_uri_list_extract_filenames_can_parse(const char * path)680 is_path_that_gnome_uri_list_extract_filenames_can_parse (const char *path)
681 {
682 if (path == NULL || path [0] == '\0') {
683 return FALSE;
684 }
685
686 /* It strips leading and trailing spaces. So it can't handle
687 * file names with leading and trailing spaces.
688 */
689 if (g_ascii_isspace (path [0])) {
690 return FALSE;
691 }
692 if (g_ascii_isspace (path [strlen (path) - 1])) {
693 return FALSE;
694 }
695
696 /* # works as a comment delimiter, and \r and \n are used to
697 * separate the lines, so it can't handle file names with any
698 * of these.
699 */
700 if (strchr (path, '#') != NULL
701 || strchr (path, '\r') != NULL
702 || strchr (path, '\n') != NULL) {
703 return FALSE;
704 }
705
706 return TRUE;
707 }
708
709 /* Encode a "text/plain" selection; this is a broken URL -- just
710 * "file:" with a path after it (no escaping or anything). We are
711 * trying to make the old gnome_uri_list_extract_filenames function
712 * happy, so this is coded to its idiosyncrasises.
713 */
714 static void
add_one_compatible_uri(const char * uri,int x,int y,int w,int h,gpointer data)715 add_one_compatible_uri (const char *uri, int x, int y, int w, int h, gpointer data)
716 {
717 GString *result;
718 char *local_path;
719
720 result = (GString *) data;
721
722 /* For URLs that do not have a file: scheme, there's no harm
723 * in passing the real URL. But for URLs that do have a file:
724 * scheme, we have to send a URL that will work with the old
725 * gnome-libs function or nothing will be able to understand
726 * it.
727 */
728 if (!g_str_has_prefix (uri, "file:")) {
729 g_string_append (result, uri);
730 g_string_append (result, "\r\n");
731 } else {
732 local_path = g_filename_from_uri (uri, NULL, NULL);
733
734 /* Check for characters that confuse the old
735 * gnome_uri_list_extract_filenames implementation, and just leave
736 * out any paths with those in them.
737 */
738 if (is_path_that_gnome_uri_list_extract_filenames_can_parse (local_path)) {
739 g_string_append (result, "file:");
740 g_string_append (result, local_path);
741 g_string_append (result, "\r\n");
742 }
743
744 g_free (local_path);
745 }
746 }
747 #endif
748
749 static void
add_one_uri(const char * uri,int x,int y,int w,int h,gpointer data)750 add_one_uri (const char *uri, int x, int y, int w, int h, gpointer data)
751 {
752 GString *result;
753
754 result = (GString *) data;
755
756 g_string_append (result, uri);
757 g_string_append (result, "\r\n");
758 }
759
760 /* Common function for drag_data_get_callback calls.
761 * Returns FALSE if it doesn't handle drag data */
762 gboolean
nemo_drag_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint32 time,gpointer container_context,NemoDragEachSelectedItemIterator each_selected_item_iterator)763 nemo_drag_drag_data_get (GtkWidget *widget,
764 GdkDragContext *context,
765 GtkSelectionData *selection_data,
766 guint info,
767 guint32 time,
768 gpointer container_context,
769 NemoDragEachSelectedItemIterator each_selected_item_iterator)
770 {
771 GString *result;
772
773 switch (info) {
774 case NEMO_ICON_DND_GNOME_ICON_LIST:
775 result = g_string_new (NULL);
776 (* each_selected_item_iterator) (add_one_gnome_icon, container_context, result);
777 break;
778
779 case NEMO_ICON_DND_URI_LIST:
780 case NEMO_ICON_DND_TEXT:
781 result = g_string_new (NULL);
782 (* each_selected_item_iterator) (add_one_uri, container_context, result);
783 break;
784
785 default:
786 return FALSE;
787 }
788
789 gtk_selection_data_set (selection_data,
790 gtk_selection_data_get_target (selection_data),
791 8, (guchar *) result->str, result->len);
792 g_string_free (result, TRUE);
793
794 return TRUE;
795 }
796
797 typedef struct
798 {
799 GMainLoop *loop;
800 GdkDragAction chosen;
801 } DropActionMenuData;
802
803 static void
menu_deactivate_callback(GtkWidget * menu,gpointer data)804 menu_deactivate_callback (GtkWidget *menu,
805 gpointer data)
806 {
807 DropActionMenuData *damd;
808
809 damd = data;
810
811 if (g_main_loop_is_running (damd->loop))
812 g_main_loop_quit (damd->loop);
813 }
814
815 static void
drop_action_activated_callback(GtkWidget * menu_item,gpointer data)816 drop_action_activated_callback (GtkWidget *menu_item,
817 gpointer data)
818 {
819 DropActionMenuData *damd;
820
821 damd = data;
822
823 damd->chosen = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item),
824 "action"));
825
826 if (g_main_loop_is_running (damd->loop))
827 g_main_loop_quit (damd->loop);
828 }
829
830 static void
append_drop_action_menu_item(GtkWidget * menu,const char * text,GdkDragAction action,gboolean sensitive,DropActionMenuData * damd)831 append_drop_action_menu_item (GtkWidget *menu,
832 const char *text,
833 GdkDragAction action,
834 gboolean sensitive,
835 DropActionMenuData *damd)
836 {
837 GtkWidget *menu_item;
838
839 menu_item = gtk_menu_item_new_with_mnemonic (text);
840 gtk_widget_set_sensitive (menu_item, sensitive);
841 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
842
843 g_object_set_data (G_OBJECT (menu_item),
844 "action",
845 GINT_TO_POINTER (action));
846
847 g_signal_connect (menu_item, "activate",
848 G_CALLBACK (drop_action_activated_callback),
849 damd);
850
851 gtk_widget_show (menu_item);
852 }
853
854 /* Pops up a menu of actions to perform on dropped files */
855 GdkDragAction
nemo_drag_drop_action_ask(GtkWidget * widget,GdkDragAction actions)856 nemo_drag_drop_action_ask (GtkWidget *widget,
857 GdkDragAction actions)
858 {
859 GtkWidget *menu;
860 GtkWidget *menu_item;
861 DropActionMenuData damd;
862
863 /* Create the menu and set the sensitivity of the items based on the
864 * allowed actions.
865 */
866 menu = gtk_menu_new ();
867 gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
868
869 append_drop_action_menu_item (menu, _("_Move Here"),
870 GDK_ACTION_MOVE,
871 (actions & GDK_ACTION_MOVE) != 0,
872 &damd);
873
874 append_drop_action_menu_item (menu, _("_Copy Here"),
875 GDK_ACTION_COPY,
876 (actions & GDK_ACTION_COPY) != 0,
877 &damd);
878
879 append_drop_action_menu_item (menu, _("_Link Here"),
880 GDK_ACTION_LINK,
881 (actions & GDK_ACTION_LINK) != 0,
882 &damd);
883
884 eel_gtk_menu_append_separator (GTK_MENU (menu));
885
886 menu_item = gtk_menu_item_new_with_mnemonic (_("Cancel"));
887 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
888 gtk_widget_show (menu_item);
889
890 damd.chosen = 0;
891 damd.loop = g_main_loop_new (NULL, FALSE);
892
893 g_signal_connect (menu, "deactivate",
894 G_CALLBACK (menu_deactivate_callback),
895 &damd);
896
897 gtk_grab_add (menu);
898
899 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
900 NULL, NULL, 0, GDK_CURRENT_TIME);
901
902 g_main_loop_run (damd.loop);
903
904 gtk_grab_remove (menu);
905
906 g_main_loop_unref (damd.loop);
907
908 g_object_ref_sink (menu);
909 g_object_unref (menu);
910
911 return damd.chosen;
912 }
913
914 gboolean
nemo_drag_autoscroll_in_scroll_region(GtkWidget * widget)915 nemo_drag_autoscroll_in_scroll_region (GtkWidget *widget)
916 {
917 float x_scroll_delta, y_scroll_delta;
918
919 nemo_drag_autoscroll_calculate_delta (widget, &x_scroll_delta, &y_scroll_delta);
920
921 return x_scroll_delta != 0 || y_scroll_delta != 0;
922 }
923
924
925 void
nemo_drag_autoscroll_calculate_delta(GtkWidget * widget,float * x_scroll_delta,float * y_scroll_delta)926 nemo_drag_autoscroll_calculate_delta (GtkWidget *widget, float *x_scroll_delta, float *y_scroll_delta)
927 {
928 GtkAllocation allocation;
929 GdkDeviceManager *manager;
930 GdkDevice *pointer;
931 int x, y;
932
933 g_assert (GTK_IS_WIDGET (widget));
934
935 manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
936 pointer = gdk_device_manager_get_client_pointer (manager);
937 gdk_window_get_device_position (gtk_widget_get_window (widget), pointer,
938 &x, &y, NULL);
939
940 /* Find out if we are anywhere close to the tree view edges
941 * to see if we need to autoscroll.
942 */
943 *x_scroll_delta = 0;
944 *y_scroll_delta = 0;
945
946 if (x < AUTO_SCROLL_MARGIN) {
947 *x_scroll_delta = (float)(x - AUTO_SCROLL_MARGIN);
948 }
949
950 gtk_widget_get_allocation (widget, &allocation);
951 if (x > allocation.width - AUTO_SCROLL_MARGIN) {
952 if (*x_scroll_delta != 0) {
953 /* Already trying to scroll because of being too close to
954 * the top edge -- must be the window is really short,
955 * don't autoscroll.
956 */
957 return;
958 }
959 *x_scroll_delta = (float)(x - (allocation.width - AUTO_SCROLL_MARGIN));
960 }
961
962 if (y < AUTO_SCROLL_MARGIN) {
963 *y_scroll_delta = (float)(y - AUTO_SCROLL_MARGIN);
964 }
965
966 if (y > allocation.height - AUTO_SCROLL_MARGIN) {
967 if (*y_scroll_delta != 0) {
968 /* Already trying to scroll because of being too close to
969 * the top edge -- must be the window is really narrow,
970 * don't autoscroll.
971 */
972 return;
973 }
974 *y_scroll_delta = (float)(y - (allocation.height - AUTO_SCROLL_MARGIN));
975 }
976
977 if (*x_scroll_delta == 0 && *y_scroll_delta == 0) {
978 /* no work */
979 return;
980 }
981
982 /* Adjust the scroll delta to the proper acceleration values depending on how far
983 * into the sroll margins we are.
984 * FIXME bugzilla.eazel.com 2486:
985 * we could use an exponential acceleration factor here for better feel
986 */
987 if (*x_scroll_delta != 0) {
988 *x_scroll_delta /= AUTO_SCROLL_MARGIN;
989 *x_scroll_delta *= (MAX_AUTOSCROLL_DELTA - MIN_AUTOSCROLL_DELTA);
990 *x_scroll_delta += MIN_AUTOSCROLL_DELTA;
991 }
992
993 if (*y_scroll_delta != 0) {
994 *y_scroll_delta /= AUTO_SCROLL_MARGIN;
995 *y_scroll_delta *= (MAX_AUTOSCROLL_DELTA - MIN_AUTOSCROLL_DELTA);
996 *y_scroll_delta += MIN_AUTOSCROLL_DELTA;
997 }
998
999 }
1000
1001
1002
1003 void
nemo_drag_autoscroll_start(NemoDragInfo * drag_info,GtkWidget * widget,GSourceFunc callback,gpointer user_data)1004 nemo_drag_autoscroll_start (NemoDragInfo *drag_info,
1005 GtkWidget *widget,
1006 GSourceFunc callback,
1007 gpointer user_data)
1008 {
1009 if (nemo_drag_autoscroll_in_scroll_region (widget)) {
1010 if (drag_info->auto_scroll_timeout_id == 0) {
1011 drag_info->waiting_to_autoscroll = TRUE;
1012 drag_info->start_auto_scroll_in = g_get_monotonic_time ()
1013 + AUTOSCROLL_INITIAL_DELAY;
1014
1015 drag_info->auto_scroll_timeout_id = g_timeout_add
1016 (AUTOSCROLL_TIMEOUT_INTERVAL,
1017 callback,
1018 user_data);
1019 }
1020 } else {
1021 if (drag_info->auto_scroll_timeout_id != 0) {
1022 g_source_remove (drag_info->auto_scroll_timeout_id);
1023 drag_info->auto_scroll_timeout_id = 0;
1024 }
1025 }
1026 }
1027
1028 void
nemo_drag_autoscroll_stop(NemoDragInfo * drag_info)1029 nemo_drag_autoscroll_stop (NemoDragInfo *drag_info)
1030 {
1031 if (drag_info->auto_scroll_timeout_id != 0) {
1032 g_source_remove (drag_info->auto_scroll_timeout_id);
1033 drag_info->auto_scroll_timeout_id = 0;
1034 }
1035 }
1036
1037 gboolean
nemo_drag_selection_includes_special_link(GList * selection_list)1038 nemo_drag_selection_includes_special_link (GList *selection_list)
1039 {
1040 GList *node;
1041 char *uri;
1042
1043 for (node = selection_list; node != NULL; node = node->next) {
1044 uri = ((NemoDragSelectionItem *) node->data)->uri;
1045
1046 if (eel_uri_is_desktop (uri)) {
1047 return TRUE;
1048 }
1049 }
1050
1051 return FALSE;
1052 }
1053