1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /* nautilus-icon-dnd.c - Drag & drop handling for the icon container widget.
5 Copyright (C) 1999, 2000 Free Software Foundation
6 Copyright (C) 2000 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: Ettore Perazzoli <ettore@gnu.org>,
24 Darin Adler <darin@bentspoon.com>,
25 Andy Hertzfeld <andy@eazel.com>
26 Pavel Cisler <pavel@eazel.com>
29 XDS support: Benedikt Meurer <benny@xfce.org> (adapted by Amos Brocco <amos.brocco@unifr.ch>)
36 #include "nautilus-icon-dnd.h"
38 #include "nautilus-debug-log.h"
39 #include "nautilus-file-dnd.h"
40 #include "nautilus-icon-private.h"
41 #include "nautilus-link.h"
42 #include "nautilus-metadata.h"
43 #include <eel/eel-background.h>
44 #include <eel/eel-gdk-pixbuf-extensions.h>
45 #include <eel/eel-glib-extensions.h>
46 #include <eel/eel-gnome-extensions.h>
47 #include <eel/eel-graphic-effects.h>
48 #include <eel/eel-gtk-extensions.h>
49 #include <eel/eel-gtk-macros.h>
50 #include <eel/eel-stock-dialogs.h>
51 #include <eel/eel-string.h>
52 #include <eel/eel-vfs-extensions.h>
53 #include <gdk/gdkkeysyms.h>
55 #include <gtk/gtkmain.h>
56 #include <gtk/gtksignal.h>
57 #include <gtk/gtkstock.h>
58 #include <glib/gi18n.h>
59 #include <eel/eel-canvas-rect-ellipse.h>
60 #include <libgnomeui/gnome-stock-icons.h>
61 #include <libgnomeui/gnome-uidefs.h>
62 #include <libnautilus-private/nautilus-file-utilities.h>
63 #include <libnautilus-private/nautilus-file-changes-queue.h>
67 static const GtkTargetEntry drag_types
[] = {
68 { NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE
, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST
},
69 { NAUTILUS_ICON_DND_URI_LIST_TYPE
, 0, NAUTILUS_ICON_DND_URI_LIST
},
72 static const GtkTargetEntry drop_types
[] = {
73 { NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE
, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST
},
74 /* prefer "_NETSCAPE_URL" over "text/uri-list" to satisfy web browsers. */
75 { NAUTILUS_ICON_DND_NETSCAPE_URL_TYPE
, 0, NAUTILUS_ICON_DND_NETSCAPE_URL
},
76 { NAUTILUS_ICON_DND_URI_LIST_TYPE
, 0, NAUTILUS_ICON_DND_URI_LIST
},
77 { NAUTILUS_ICON_DND_COLOR_TYPE
, 0, NAUTILUS_ICON_DND_COLOR
},
78 { NAUTILUS_ICON_DND_BGIMAGE_TYPE
, 0, NAUTILUS_ICON_DND_BGIMAGE
},
79 { NAUTILUS_ICON_DND_KEYWORD_TYPE
, 0, NAUTILUS_ICON_DND_KEYWORD
},
80 { NAUTILUS_ICON_DND_RESET_BACKGROUND_TYPE
, 0, NAUTILUS_ICON_DND_RESET_BACKGROUND
},
81 { NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE
, 0, NAUTILUS_ICON_DND_XDNDDIRECTSAVE
}, /* XDS Protocol Type */
83 { NAUTILUS_ICON_DND_ROOTWINDOW_DROP_TYPE
, 0, NAUTILUS_ICON_DND_ROOTWINDOW_DROP
}
85 static void stop_dnd_highlight (GtkWidget
*widget
);
86 static void dnd_highlight_queue_redraw (GtkWidget
*widget
);
88 static GtkTargetList
*drop_types_list
= NULL
;
89 static GtkTargetList
*drop_types_list_root
= NULL
;
91 static char * nautilus_icon_container_find_drop_target (NautilusIconContainer
*container
,
92 GdkDragContext
*context
,
93 int x
, int y
, gboolean
*icon_hit
,
94 gboolean rewrite_desktop
);
96 static EelCanvasItem
*
97 create_selection_shadow (NautilusIconContainer
*container
,
100 EelCanvasGroup
*group
;
111 /* if we're only dragging a single item, don't worry about the shadow */
112 if (list
->next
== NULL
) {
116 stipple
= container
->details
->dnd_info
->stipple
;
117 g_return_val_if_fail (stipple
!= NULL
, NULL
);
119 canvas
= EEL_CANVAS (container
);
121 /* Creating a big set of rectangles in the canvas can be expensive, so
122 we try to be smart and only create the maximum number of rectangles
123 that we will need, in the vertical/horizontal directions. */
125 max_x
= GTK_WIDGET (container
)->allocation
.width
;
128 max_y
= GTK_WIDGET (container
)->allocation
.height
;
131 /* Create a group, so that it's easier to move all the items around at
133 group
= EEL_CANVAS_GROUP
134 (eel_canvas_item_new (EEL_CANVAS_GROUP (canvas
->root
),
135 eel_canvas_group_get_type (),
138 for (p
= list
; p
!= NULL
; p
= p
->next
) {
139 NautilusDragSelectionItem
*item
;
144 if (!item
->got_icon_position
) {
150 x2
= x1
+ item
->icon_width
;
151 y2
= y1
+ item
->icon_height
;
153 if (x2
>= min_x
&& x1
<= max_x
&& y2
>= min_y
&& y1
<= max_y
)
156 eel_canvas_rect_get_type (),
161 "outline_color", "black",
162 "outline_stipple", stipple
,
167 return EEL_CANVAS_ITEM (group
);
170 /* Set the affine instead of the x and y position.
171 * Simple, and setting x and y was broken at one point.
174 set_shadow_position (EelCanvasItem
*shadow
,
177 eel_canvas_item_set (shadow
,
183 /* Source-side handling of the drag. */
185 /* iteration glue struct */
187 gpointer iterator_context
;
188 NautilusDragEachSelectedItemDataGet iteratee
;
189 gpointer iteratee_data
;
190 } IconGetDataBinderContext
;
193 canvas_rect_world_to_widget (EelCanvas
*canvas
,
194 EelDRect
*world_rect
,
195 EelIRect
*widget_rect
)
197 EelDRect window_rect
;
199 eel_canvas_world_to_window (canvas
,
200 world_rect
->x0
, world_rect
->y0
,
201 &window_rect
.x0
, &window_rect
.y0
);
202 eel_canvas_world_to_window (canvas
,
203 world_rect
->x1
, world_rect
->y1
,
204 &window_rect
.x1
, &window_rect
.y1
);
205 widget_rect
->x0
= (int) window_rect
.x0
- gtk_adjustment_get_value (gtk_layout_get_hadjustment (GTK_LAYOUT (canvas
)));
206 widget_rect
->y0
= (int) window_rect
.y0
- gtk_adjustment_get_value (gtk_layout_get_vadjustment (GTK_LAYOUT (canvas
)));
207 widget_rect
->x1
= (int) window_rect
.x1
- gtk_adjustment_get_value (gtk_layout_get_hadjustment (GTK_LAYOUT (canvas
)));
208 widget_rect
->y1
= (int) window_rect
.y1
- gtk_adjustment_get_value (gtk_layout_get_vadjustment (GTK_LAYOUT (canvas
)));
212 canvas_widget_to_world (EelCanvas
*canvas
,
213 double widget_x
, double widget_y
,
214 double *world_x
, double *world_y
)
216 eel_canvas_window_to_world (canvas
,
217 widget_x
+ gtk_adjustment_get_value (gtk_layout_get_hadjustment (GTK_LAYOUT (canvas
))),
218 widget_y
+ gtk_adjustment_get_value (gtk_layout_get_vadjustment (GTK_LAYOUT (canvas
))),
223 icon_get_data_binder (NautilusIcon
*icon
, gpointer data
)
225 IconGetDataBinderContext
*context
;
227 EelIRect widget_rect
;
229 NautilusIconContainer
*container
;
231 context
= (IconGetDataBinderContext
*)data
;
233 g_assert (NAUTILUS_IS_ICON_CONTAINER (context
->iterator_context
));
235 container
= NAUTILUS_ICON_CONTAINER (context
->iterator_context
);
237 world_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon
->item
);
239 canvas_rect_world_to_widget (EEL_CANVAS (container
), &world_rect
, &widget_rect
);
241 uri
= nautilus_icon_container_get_icon_uri (container
, icon
);
243 g_warning ("no URI for one of the iterated icons");
247 widget_rect
= eel_irect_offset_by (widget_rect
,
248 - container
->details
->dnd_info
->drag_info
.start_x
,
249 - container
->details
->dnd_info
->drag_info
.start_y
);
251 widget_rect
= eel_irect_scale_by (widget_rect
,
252 1 / EEL_CANVAS (container
)->pixels_per_unit
);
254 /* pass the uri, mouse-relative x/y and icon width/height */
255 context
->iteratee (uri
,
256 (int) widget_rect
.x0
,
257 (int) widget_rect
.y0
,
258 widget_rect
.x1
- widget_rect
.x0
,
259 widget_rect
.y1
- widget_rect
.y0
,
260 context
->iteratee_data
);
267 /* Iterate over each selected icon in a NautilusIconContainer,
268 * calling each_function on each.
271 nautilus_icon_container_each_selected_icon (NautilusIconContainer
*container
,
272 gboolean (*each_function
) (NautilusIcon
*, gpointer
), gpointer data
)
277 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
279 if (!icon
->is_selected
) {
282 if (!each_function (icon
, data
)) {
288 /* Adaptor function used with nautilus_icon_container_each_selected_icon
289 * to help iterate over all selected items, passing uris, x, y, w and h
290 * values to the iteratee
293 each_icon_get_data_binder (NautilusDragEachSelectedItemDataGet iteratee
,
294 gpointer iterator_context
, gpointer data
)
296 IconGetDataBinderContext context
;
297 NautilusIconContainer
*container
;
299 g_assert (NAUTILUS_IS_ICON_CONTAINER (iterator_context
));
300 container
= NAUTILUS_ICON_CONTAINER (iterator_context
);
302 context
.iterator_context
= iterator_context
;
303 context
.iteratee
= iteratee
;
304 context
.iteratee_data
= data
;
305 nautilus_icon_container_each_selected_icon (container
, icon_get_data_binder
, &context
);
308 /* Called when the data for drag&drop is needed */
310 drag_data_get_callback (GtkWidget
*widget
,
311 GdkDragContext
*context
,
312 GtkSelectionData
*selection_data
,
317 g_assert (widget
!= NULL
);
318 g_assert (NAUTILUS_IS_ICON_CONTAINER (widget
));
319 g_return_if_fail (context
!= NULL
);
321 /* Call common function from nautilus-drag that set's up
322 * the selection data in the right format. Pass it means to
323 * iterate all the selected icons.
325 nautilus_drag_drag_data_get (widget
, context
, selection_data
,
326 info
, time
, widget
, each_icon_get_data_binder
);
330 /* Target-side handling of the drag. */
333 nautilus_icon_container_position_shadow (NautilusIconContainer
*container
,
336 EelCanvasItem
*shadow
;
337 double world_x
, world_y
;
339 shadow
= container
->details
->dnd_info
->shadow
;
340 if (shadow
== NULL
) {
344 canvas_widget_to_world (EEL_CANVAS (container
), x
, y
,
347 set_shadow_position (shadow
, world_x
, world_y
);
348 eel_canvas_item_show (shadow
);
352 nautilus_icon_container_dropped_icon_feedback (GtkWidget
*widget
,
353 GtkSelectionData
*data
,
356 NautilusIconContainer
*container
;
357 NautilusIconDndInfo
*dnd_info
;
359 container
= NAUTILUS_ICON_CONTAINER (widget
);
360 dnd_info
= container
->details
->dnd_info
;
362 /* Delete old selection list. */
363 nautilus_drag_destroy_selection_list (dnd_info
->drag_info
.selection_list
);
364 dnd_info
->drag_info
.selection_list
= NULL
;
366 /* Delete old shadow if any. */
367 if (dnd_info
->shadow
!= NULL
) {
368 /* FIXME bugzilla.gnome.org 42484:
369 * Is a destroy really sufficient here? Who does the unref? */
370 gtk_object_destroy (GTK_OBJECT (dnd_info
->shadow
));
373 /* Build the selection list and the shadow. */
374 dnd_info
->drag_info
.selection_list
= nautilus_drag_build_selection_list (data
);
375 dnd_info
->shadow
= create_selection_shadow (container
, dnd_info
->drag_info
.selection_list
);
376 nautilus_icon_container_position_shadow (container
, x
, y
);
380 get_direct_save_filename (GdkDragContext
*context
)
385 if (!gdk_property_get (context
->source_window
, gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE
, FALSE
),
386 gdk_atom_intern ("text/plain", FALSE
), 0, 1024, FALSE
, NULL
, NULL
,
387 &prop_len
, &prop_text
) && prop_text
!= NULL
) {
391 /* Zero-terminate the string */
392 prop_text
= g_realloc (prop_text
, prop_len
+ 1);
393 prop_text
[prop_len
] = '\0';
395 /* Verify that the file name provided by the source is valid */
396 if (*prop_text
== '\0' ||
397 strchr ((const gchar
*) prop_text
, G_DIR_SEPARATOR
) != NULL
) {
398 nautilus_debug_log (FALSE
, NAUTILUS_DEBUG_LOG_DOMAIN_USER
,
399 "Invalid filename provided by XDS drag site");
408 set_direct_save_uri (GtkWidget
*widget
, GdkDragContext
*context
, NautilusDragInfo
*drag_info
, int x
, int y
)
411 char *filename
, *drop_target
;
414 drag_info
->got_drop_data_type
= TRUE
;
415 drag_info
->data_type
= NAUTILUS_ICON_DND_XDNDDIRECTSAVE
;
419 filename
= get_direct_save_filename (context
);
420 drop_target
= nautilus_icon_container_find_drop_target (NAUTILUS_ICON_CONTAINER (widget
),
421 context
, x
, y
, NULL
, TRUE
);
423 if (drop_target
&& eel_uri_is_trash (drop_target
)) {
424 g_free (drop_target
);
425 drop_target
= NULL
; /* Cannot save to trash ...*/
428 if (filename
!= NULL
&& drop_target
!= NULL
) {
429 /* Resolve relative path */
430 base
= g_file_new_for_uri (drop_target
);
431 child
= g_file_get_child (base
, filename
);
432 uri
= g_file_get_uri (child
);
433 g_object_unref (base
);
434 g_object_unref (child
);
436 /* Change the uri property */
437 gdk_property_change (GDK_DRAWABLE (context
->source_window
),
438 gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE
, FALSE
),
439 gdk_atom_intern ("text/plain", FALSE
), 8,
440 GDK_PROP_MODE_REPLACE
, (const guchar
*) uri
,
443 drag_info
->direct_save_uri
= uri
;
447 g_free (drop_target
);
450 /* FIXME bugzilla.gnome.org 47445: Needs to become a shared function */
452 get_data_on_first_target_we_support (GtkWidget
*widget
, GdkDragContext
*context
, guint32 time
, int x
, int y
)
457 if (drop_types_list
== NULL
) {
458 drop_types_list
= gtk_target_list_new (drop_types
,
459 G_N_ELEMENTS (drop_types
) - 1);
460 gtk_target_list_add_text_targets (drop_types_list
, NAUTILUS_ICON_DND_TEXT
);
462 if (drop_types_list_root
== NULL
) {
463 drop_types_list_root
= gtk_target_list_new (drop_types
,
464 G_N_ELEMENTS (drop_types
));
465 gtk_target_list_add_text_targets (drop_types_list_root
, NAUTILUS_ICON_DND_TEXT
);
468 if (nautilus_icon_container_get_is_desktop (NAUTILUS_ICON_CONTAINER (widget
))) {
469 list
= drop_types_list_root
;
471 list
= drop_types_list
;
474 target
= gtk_drag_dest_find_target (widget
, context
, list
);
475 if (target
!= GDK_NONE
) {
477 NautilusDragInfo
*drag_info
;
480 drag_info
= &(NAUTILUS_ICON_CONTAINER (widget
)->details
->dnd_info
->drag_info
);
482 found
= gtk_target_list_find (list
, target
, &info
);
485 /* Don't get_data for destructive ops */
486 if ((info
== NAUTILUS_ICON_DND_ROOTWINDOW_DROP
||
487 info
== NAUTILUS_ICON_DND_XDNDDIRECTSAVE
) &&
488 !drag_info
->drop_occured
) {
489 /* We can't call get_data here, because that would
490 make the source execute the rootwin action or the direct save */
491 drag_info
->got_drop_data_type
= TRUE
;
492 drag_info
->data_type
= info
;
494 if (info
== NAUTILUS_ICON_DND_XDNDDIRECTSAVE
) {
495 set_direct_save_uri (widget
, context
, drag_info
, x
, y
);
497 gtk_drag_get_data (GTK_WIDGET (widget
), context
,
504 nautilus_icon_container_ensure_drag_data (NautilusIconContainer
*container
,
505 GdkDragContext
*context
,
508 NautilusIconDndInfo
*dnd_info
;
510 dnd_info
= container
->details
->dnd_info
;
512 if (!dnd_info
->drag_info
.got_drop_data_type
) {
513 get_data_on_first_target_we_support (GTK_WIDGET (container
), context
, time
, 0, 0);
518 drag_end_callback (GtkWidget
*widget
,
519 GdkDragContext
*context
,
522 NautilusIconContainer
*container
;
523 NautilusIconDndInfo
*dnd_info
;
525 container
= NAUTILUS_ICON_CONTAINER (widget
);
526 dnd_info
= container
->details
->dnd_info
;
528 nautilus_drag_destroy_selection_list (dnd_info
->drag_info
.selection_list
);
529 dnd_info
->drag_info
.selection_list
= NULL
;
532 static NautilusIcon
*
533 nautilus_icon_container_item_at (NautilusIconContainer
*container
,
539 EelIRect canvas_point
;
541 /* build the hit-test rectangle. Base the size on the scale factor to ensure that it is
542 * non-empty even at the smallest scale factor
545 size
= MAX (1, 1 + (1 / EEL_CANVAS (container
)->pixels_per_unit
));
551 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
555 eel_canvas_w2c (EEL_CANVAS (container
),
560 eel_canvas_w2c (EEL_CANVAS (container
),
565 if (nautilus_icon_canvas_item_hit_test_rectangle (icon
->item
, canvas_point
)) {
574 get_container_uri (NautilusIconContainer
*container
)
578 /* get the URI associated with the container */
580 g_signal_emit_by_name (container
, "get_container_uri", &uri
);
585 nautilus_icon_container_selection_items_local (NautilusIconContainer
*container
,
588 char *container_uri_string
;
591 /* must have at least one item */
596 /* get the URI associated with the container */
597 container_uri_string
= get_container_uri (container
);
599 if (eel_uri_is_desktop (container_uri_string
)) {
600 result
= nautilus_drag_items_on_desktop (items
);
602 result
= nautilus_drag_items_local (container_uri_string
, items
);
604 g_free (container_uri_string
);
610 get_background_drag_action (NautilusIconContainer
*container
,
611 GdkDragAction action
)
613 /* FIXME: This function is very FMDirectoryView specific, and
614 * should be moved out of nautilus-icon-dnd.c */
615 GdkDragAction valid_actions
;
617 if (action
== GDK_ACTION_ASK
) {
618 valid_actions
= NAUTILUS_DND_ACTION_SET_AS_FOLDER_BACKGROUND
;
619 if (!eel_background_is_desktop (eel_get_widget_background (GTK_WIDGET (container
)))) {
620 valid_actions
|= NAUTILUS_DND_ACTION_SET_AS_GLOBAL_BACKGROUND
;
623 action
= nautilus_drag_drop_background_ask
624 (GTK_WIDGET (container
), valid_actions
);
631 receive_dropped_color (NautilusIconContainer
*container
,
633 GdkDragAction action
,
634 GtkSelectionData
*data
)
636 action
= get_background_drag_action (container
, action
);
641 uri
= get_container_uri (container
);
642 nautilus_debug_log (FALSE
, NAUTILUS_DEBUG_LOG_DOMAIN_USER
,
643 "dropped color on icon container displaying %s", uri
);
646 eel_background_receive_dropped_color
647 (eel_get_widget_background (GTK_WIDGET (container
)),
648 GTK_WIDGET (container
),
653 /* handle dropped tile images */
655 receive_dropped_tile_image (NautilusIconContainer
*container
, GdkDragAction action
, GtkSelectionData
*data
)
657 g_assert (data
!= NULL
);
659 action
= get_background_drag_action (container
, action
);
664 uri
= get_container_uri (container
);
665 nautilus_debug_log (FALSE
, NAUTILUS_DEBUG_LOG_DOMAIN_USER
,
666 "dropped tile image on icon container displaying %s", uri
);
669 eel_background_receive_dropped_background_image
670 (eel_get_widget_background (GTK_WIDGET (container
)),
676 /* handle dropped keywords */
678 receive_dropped_keyword (NautilusIconContainer
*container
, const char *keyword
, int x
, int y
)
681 double world_x
, world_y
;
683 NautilusIcon
*drop_target_icon
;
686 g_assert (keyword
!= NULL
);
688 /* find the item we hit with our drop, if any */
689 canvas_widget_to_world (EEL_CANVAS (container
), x
, y
, &world_x
, &world_y
);
690 drop_target_icon
= nautilus_icon_container_item_at (container
, world_x
, world_y
);
691 if (drop_target_icon
== NULL
) {
695 /* FIXME bugzilla.gnome.org 42485:
696 * This does not belong in the icon code.
697 * It has to be in the file manager.
698 * The icon code has no right to deal with the file directly.
699 * But luckily there's no issue of not getting a file object,
700 * so we don't have to worry about async. issues here.
702 uri
= nautilus_icon_container_get_icon_uri (container
, drop_target_icon
);
704 nautilus_debug_log (FALSE
, NAUTILUS_DEBUG_LOG_DOMAIN_USER
,
705 "dropped emblem '%s' on icon container URI: %s",
708 file
= nautilus_file_get_by_uri (uri
);
711 nautilus_drag_file_receive_dropped_keyword (file
, keyword
);
713 nautilus_file_unref (file
);
714 nautilus_icon_container_update_icon (container
, drop_target_icon
);
717 /* handle dropped url */
719 receive_dropped_netscape_url (NautilusIconContainer
*container
, const char *encoded_url
, GdkDragContext
*context
, int x
, int y
)
723 if (encoded_url
== NULL
) {
727 drop_target
= nautilus_icon_container_find_drop_target (container
, context
, x
, y
, NULL
, TRUE
);
729 g_signal_emit_by_name (container
, "handle_netscape_url",
735 g_free (drop_target
);
738 /* handle dropped uri list */
740 receive_dropped_uri_list (NautilusIconContainer
*container
, const char *uri_list
, GdkDragContext
*context
, int x
, int y
)
744 if (uri_list
== NULL
) {
748 drop_target
= nautilus_icon_container_find_drop_target (container
, context
, x
, y
, NULL
, TRUE
);
750 g_signal_emit_by_name (container
, "handle_uri_list",
756 g_free (drop_target
);
759 /* handle dropped text */
761 receive_dropped_text (NautilusIconContainer
*container
, const char *text
, GdkDragContext
*context
, int x
, int y
)
769 drop_target
= nautilus_icon_container_find_drop_target (container
, context
, x
, y
, NULL
, TRUE
);
771 g_signal_emit_by_name (container
, "handle_text",
777 g_free (drop_target
);
781 auto_scroll_timeout_callback (gpointer data
)
783 NautilusIconContainer
*container
;
785 float x_scroll_delta
, y_scroll_delta
;
786 GdkRectangle exposed_area
;
788 g_assert (NAUTILUS_IS_ICON_CONTAINER (data
));
789 widget
= GTK_WIDGET (data
);
790 container
= NAUTILUS_ICON_CONTAINER (widget
);
792 if (container
->details
->dnd_info
->drag_info
.waiting_to_autoscroll
793 && container
->details
->dnd_info
->drag_info
.start_auto_scroll_in
> eel_get_system_time()) {
798 container
->details
->dnd_info
->drag_info
.waiting_to_autoscroll
= FALSE
;
800 nautilus_drag_autoscroll_calculate_delta (widget
, &x_scroll_delta
, &y_scroll_delta
);
801 if (x_scroll_delta
== 0 && y_scroll_delta
== 0) {
806 /* Clear the old dnd highlight frame */
807 dnd_highlight_queue_redraw (widget
);
809 if (!nautilus_icon_container_scroll (container
, (int)x_scroll_delta
, (int)y_scroll_delta
)) {
810 /* the scroll value got pinned to a min or max adjustment value,
811 * we ended up not scrolling
816 /* Make sure the dnd highlight frame is redrawn */
817 dnd_highlight_queue_redraw (widget
);
819 /* update cached drag start offsets */
820 container
->details
->dnd_info
->drag_info
.start_x
-= x_scroll_delta
;
821 container
->details
->dnd_info
->drag_info
.start_y
-= y_scroll_delta
;
823 /* Due to a glitch in GtkLayout, whe need to do an explicit draw of the exposed
825 * Calculate the size of the area we need to draw
827 exposed_area
.x
= widget
->allocation
.x
;
828 exposed_area
.y
= widget
->allocation
.y
;
829 exposed_area
.width
= widget
->allocation
.width
;
830 exposed_area
.height
= widget
->allocation
.height
;
832 if (x_scroll_delta
> 0) {
833 exposed_area
.x
= exposed_area
.width
- x_scroll_delta
;
834 } else if (x_scroll_delta
< 0) {
835 exposed_area
.width
= -x_scroll_delta
;
838 if (y_scroll_delta
> 0) {
839 exposed_area
.y
= exposed_area
.height
- y_scroll_delta
;
840 } else if (y_scroll_delta
< 0) {
841 exposed_area
.height
= -y_scroll_delta
;
844 /* offset it to 0, 0 */
845 exposed_area
.x
-= widget
->allocation
.x
;
846 exposed_area
.y
-= widget
->allocation
.y
;
848 gtk_widget_queue_draw_area (widget
,
852 exposed_area
.height
);
858 set_up_auto_scroll_if_needed (NautilusIconContainer
*container
)
860 nautilus_drag_autoscroll_start (&container
->details
->dnd_info
->drag_info
,
861 GTK_WIDGET (container
),
862 auto_scroll_timeout_callback
,
867 stop_auto_scroll (NautilusIconContainer
*container
)
869 nautilus_drag_autoscroll_stop (&container
->details
->dnd_info
->drag_info
);
873 confirm_switch_to_manual_layout (NautilusIconContainer
*container
)
881 /* FIXME bugzilla.gnome.org 40915: Use of the word "directory"
882 * makes this FMIconView specific. Move these messages into
883 * FMIconView so NautilusIconContainer can be used for things
884 * that are not directories?
886 if (nautilus_icon_container_has_stored_icon_positions (container
)) {
887 if (eel_g_list_exactly_one_item (container
->details
->dnd_info
->drag_info
.selection_list
)) {
888 message
= no_translate("Do you want to switch to manual layout and leave this item where you dropped it? "
889 "This will clobber the stored manual layout.");
890 detail
= no_translate("This folder uses automatic layout.");
892 message
= no_translate("Do you want to switch to manual layout and leave these items where you dropped them? "
893 "This will clobber the stored manual layout.");
894 detail
= no_translate("This folder uses automatic layout.");
897 if (eel_g_list_exactly_one_item (container
->details
->dnd_info
->drag_info
.selection_list
)) {
898 message
= no_translate("Do you want to switch to manual layout and leave this item where you dropped it?");
899 detail
= no_translate("This folder uses automatic layout.");
901 message
= no_translate("Do you want to switch to manual layout and leave these items where you dropped them?");
902 detail
= no_translate("This folder uses automatic layout.");
907 dialog
= eel_show_yes_no_dialog (message
, detail
, _("Switch to Manual Layout?"),
909 GTK_WINDOW (gtk_widget_get_toplevel(GTK_WIDGET(container
))));
911 response
= gtk_dialog_run (dialog
);
912 gtk_object_destroy (GTK_OBJECT (dialog
));
914 return response
== GTK_RESPONSE_YES
;
921 handle_local_move (NautilusIconContainer
*container
,
922 double world_x
, double world_y
)
924 GList
*moved_icons
, *p
;
925 NautilusDragSelectionItem
*item
;
928 char screen_string
[32];
931 if (container
->details
->auto_layout
) {
932 if (!confirm_switch_to_manual_layout (container
)) {
935 nautilus_icon_container_freeze_icon_positions (container
);
939 /* Move and select the icons. */
941 for (p
= container
->details
->dnd_info
->drag_info
.selection_list
; p
!= NULL
; p
= p
->next
) {
944 icon
= nautilus_icon_container_get_icon_by_uri
945 (container
, item
->uri
);
948 /* probably dragged from another screen. Add it to
952 file
= nautilus_file_get_by_uri (item
->uri
);
954 screen
= gtk_widget_get_screen (GTK_WIDGET (container
));
955 g_snprintf (screen_string
, sizeof (screen_string
), "%d",
956 gdk_screen_get_number (screen
));
957 nautilus_file_set_metadata (file
,
958 NAUTILUS_METADATA_KEY_SCREEN
,
959 NULL
, screen_string
);
961 nautilus_icon_container_add (container
,
962 NAUTILUS_ICON_CONTAINER_ICON_DATA (file
),
965 icon
= nautilus_icon_container_get_icon_by_uri
966 (container
, item
->uri
);
969 if (item
->got_icon_position
) {
970 nautilus_icon_container_move_icon
972 world_x
+ item
->icon_x
, world_y
+ item
->icon_y
,
976 moved_icons
= g_list_prepend (moved_icons
, icon
);
978 nautilus_icon_container_select_list_unselect_others
979 (container
, moved_icons
);
980 /* Might have been moved in a way that requires adjusting scroll region. */
981 nautilus_icon_container_update_scroll_region (container
);
982 g_list_free (moved_icons
);
986 handle_nonlocal_move (NautilusIconContainer
*container
,
987 GdkDragContext
*context
,
989 const char *target_uri
,
992 GList
*source_uris
, *p
;
993 GArray
*source_item_locations
;
994 gboolean free_target_uri
, is_rtl
;
997 if (container
->details
->dnd_info
->drag_info
.selection_list
== NULL
) {
1002 for (p
= container
->details
->dnd_info
->drag_info
.selection_list
; p
!= NULL
; p
= p
->next
) {
1003 /* do a shallow copy of all the uri strings of the copied files */
1004 source_uris
= g_list_prepend (source_uris
, ((NautilusDragSelectionItem
*)p
->data
)->uri
);
1006 source_uris
= g_list_reverse (source_uris
);
1008 is_rtl
= nautilus_icon_container_is_layout_rtl (container
);
1010 source_item_locations
= g_array_new (FALSE
, TRUE
, sizeof (GdkPoint
));
1012 /* Drop onto a container. Pass along the item points to allow placing
1013 * the items in their same relative positions in the new container.
1015 source_item_locations
= g_array_set_size (source_item_locations
,
1016 g_list_length (container
->details
->dnd_info
->drag_info
.selection_list
));
1018 for (index
= 0, p
= container
->details
->dnd_info
->drag_info
.selection_list
;
1019 p
!= NULL
; index
++, p
= p
->next
) {
1020 item_x
= ((NautilusDragSelectionItem
*)p
->data
)->icon_x
;
1022 item_x
= -item_x
- ((NautilusDragSelectionItem
*)p
->data
)->icon_width
;
1023 g_array_index (source_item_locations
, GdkPoint
, index
).x
= item_x
;
1024 g_array_index (source_item_locations
, GdkPoint
, index
).y
=
1025 ((NautilusDragSelectionItem
*)p
->data
)->icon_y
;
1029 free_target_uri
= FALSE
;
1030 /* Rewrite internal desktop URIs to the normal target uri */
1031 if (eel_uri_is_desktop (target_uri
)) {
1032 target_uri
= nautilus_get_desktop_directory_uri ();
1033 free_target_uri
= TRUE
;
1037 x
= CANVAS_WIDTH (container
) - x
;
1040 /* start the copy */
1041 g_signal_emit_by_name (container
, "move_copy_items",
1043 source_item_locations
,
1048 if (free_target_uri
) {
1049 g_free ((char *)target_uri
);
1052 g_list_free (source_uris
);
1053 g_array_free (source_item_locations
, TRUE
);
1057 nautilus_icon_container_find_drop_target (NautilusIconContainer
*container
,
1058 GdkDragContext
*context
,
1061 gboolean rewrite_desktop
)
1063 NautilusIcon
*drop_target_icon
;
1064 double world_x
, world_y
;
1067 char *container_uri
;
1073 if (!container
->details
->dnd_info
->drag_info
.got_drop_data_type
) {
1077 canvas_widget_to_world (EEL_CANVAS (container
), x
, y
, &world_x
, &world_y
);
1079 /* FIXME bugzilla.gnome.org 42485:
1080 * These "can_accept_items" tests need to be done by
1081 * the icon view, not here. This file is not supposed to know
1082 * that the target is a file.
1085 /* Find the item we hit with our drop, if any */
1086 drop_target_icon
= nautilus_icon_container_item_at (container
, world_x
, world_y
);
1087 if (drop_target_icon
!= NULL
) {
1088 icon_uri
= nautilus_icon_container_get_icon_uri (container
, drop_target_icon
);
1089 if (icon_uri
!= NULL
) {
1090 file
= nautilus_file_get_by_uri (icon_uri
);
1092 if (!nautilus_drag_can_accept_info (file
,
1093 container
->details
->dnd_info
->drag_info
.data_type
,
1094 container
->details
->dnd_info
->drag_info
.selection_list
)) {
1095 /* the item we dropped our selection on cannot accept the items,
1096 * do the same thing as if we just dropped the items on the canvas
1098 drop_target_icon
= NULL
;
1102 nautilus_file_unref (file
);
1106 if (drop_target_icon
== NULL
) {
1111 container_uri
= get_container_uri (container
);
1113 if (rewrite_desktop
&&
1114 container_uri
!= NULL
&&
1115 eel_uri_is_desktop (container_uri
)) {
1116 g_free (container_uri
);
1117 container_uri
= nautilus_get_desktop_directory_uri ();
1120 return container_uri
;
1126 return nautilus_icon_container_get_icon_drop_target_uri (container
, drop_target_icon
);
1130 selection_is_image_file (GList
*selection_list
)
1132 const char *mime_type
;
1133 NautilusDragSelectionItem
*selected_item
;
1138 /* Make sure only one item is selected */
1139 if (selection_list
== NULL
||
1140 selection_list
->next
!= NULL
) {
1144 selected_item
= selection_list
->data
;
1148 location
= g_file_new_for_uri (selected_item
->uri
);
1149 info
= g_file_query_info (location
,
1150 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
,
1153 mime_type
= g_file_info_get_content_type (info
);
1156 result
= eel_istr_has_prefix (mime_type
, "image/");
1159 g_object_unref (info
);
1161 g_object_unref (location
);
1168 nautilus_icon_container_receive_dropped_icons (NautilusIconContainer
*container
,
1169 GdkDragContext
*context
,
1173 gboolean local_move_only
;
1174 double world_x
, world_y
;
1176 GdkDragAction action
;
1177 NautilusDragSelectionItem
*selected_item
;
1181 if (container
->details
->dnd_info
->drag_info
.selection_list
== NULL
) {
1185 if (context
->action
== GDK_ACTION_ASK
) {
1186 /* FIXME bugzilla.gnome.org 42485: This belongs in FMDirectoryView, not here. */
1187 /* Check for special case items in selection list */
1188 if (nautilus_drag_selection_includes_special_link (container
->details
->dnd_info
->drag_info
.selection_list
)) {
1189 /* We only want to move the trash */
1190 action
= GDK_ACTION_MOVE
;
1192 action
= GDK_ACTION_MOVE
| GDK_ACTION_COPY
| GDK_ACTION_LINK
;
1194 if (selection_is_image_file (container
->details
->dnd_info
->drag_info
.selection_list
)) {
1195 action
|= NAUTILUS_DND_ACTION_SET_AS_BACKGROUND
;
1198 context
->action
= nautilus_drag_drop_action_ask
1199 (GTK_WIDGET (container
), action
);
1202 if (context
->action
== NAUTILUS_DND_ACTION_SET_AS_BACKGROUND
) {
1203 selected_item
= container
->details
->dnd_info
->drag_info
.selection_list
->data
;
1204 eel_background_receive_dropped_background_image
1205 (eel_get_widget_background (GTK_WIDGET (container
)),
1207 selected_item
->uri
);
1211 if (context
->action
> 0) {
1212 eel_canvas_window_to_world (EEL_CANVAS (container
),
1213 x
+ gtk_adjustment_get_value (gtk_layout_get_hadjustment (GTK_LAYOUT (container
))),
1214 y
+ gtk_adjustment_get_value (gtk_layout_get_vadjustment (GTK_LAYOUT (container
))),
1215 &world_x
, &world_y
);
1217 drop_target
= nautilus_icon_container_find_drop_target (container
,
1218 context
, x
, y
, &icon_hit
, FALSE
);
1220 local_move_only
= FALSE
;
1221 if (!icon_hit
&& context
->action
== GDK_ACTION_MOVE
) {
1222 /* we can just move the icon positions if the move ended up in
1223 * the item's parent container
1225 local_move_only
= nautilus_icon_container_selection_items_local
1226 (container
, container
->details
->dnd_info
->drag_info
.selection_list
);
1229 if (local_move_only
) {
1230 handle_local_move (container
, world_x
, world_y
);
1232 handle_nonlocal_move (container
, context
, world_x
, world_y
, drop_target
, icon_hit
);
1236 g_free (drop_target
);
1237 nautilus_drag_destroy_selection_list (container
->details
->dnd_info
->drag_info
.selection_list
);
1238 container
->details
->dnd_info
->drag_info
.selection_list
= NULL
;
1242 nautilus_icon_container_get_drop_action (NautilusIconContainer
*container
,
1243 GdkDragContext
*context
,
1250 double world_x
, world_y
;
1253 if (!container
->details
->dnd_info
->drag_info
.got_drop_data_type
) {
1254 /* drag_data_received_callback didn't get called yet */
1258 /* find out if we're over an icon */
1259 canvas_widget_to_world (EEL_CANVAS (container
), x
, y
, &world_x
, &world_y
);
1261 icon
= nautilus_icon_container_item_at (container
, world_x
, world_y
);
1265 /* case out on the type of object being dragged */
1266 switch (container
->details
->dnd_info
->drag_info
.data_type
) {
1267 case NAUTILUS_ICON_DND_GNOME_ICON_LIST
:
1268 if (container
->details
->dnd_info
->drag_info
.selection_list
== NULL
) {
1271 drop_target
= nautilus_icon_container_find_drop_target (container
,
1272 context
, x
, y
, &icon_hit
, FALSE
);
1276 nautilus_drag_default_drop_action_for_icons (context
, drop_target
,
1277 container
->details
->dnd_info
->drag_info
.selection_list
,
1279 g_free (drop_target
);
1282 /* handle emblems by setting the action if we're over an object */
1283 case NAUTILUS_ICON_DND_KEYWORD
:
1285 *action
= context
->suggested_action
;
1289 case NAUTILUS_ICON_DND_NETSCAPE_URL
:
1290 *action
= nautilus_drag_default_drop_action_for_netscape_url (context
);
1293 case NAUTILUS_ICON_DND_COLOR
:
1294 case NAUTILUS_ICON_DND_BGIMAGE
:
1295 case NAUTILUS_ICON_DND_RESET_BACKGROUND
:
1296 case NAUTILUS_ICON_DND_URI_LIST
:
1297 case NAUTILUS_ICON_DND_ROOTWINDOW_DROP
:
1298 *action
= context
->suggested_action
;
1301 case NAUTILUS_ICON_DND_TEXT
:
1302 case NAUTILUS_ICON_DND_XDNDDIRECTSAVE
:
1303 *action
= GDK_ACTION_COPY
;
1309 set_drop_target (NautilusIconContainer
*container
,
1312 NautilusIcon
*old_icon
;
1314 /* Check if current drop target changed, update icon drop
1315 * higlight if needed.
1317 old_icon
= container
->details
->drop_target
;
1318 if (icon
== old_icon
) {
1322 /* Remember the new drop target for the next round. */
1323 container
->details
->drop_target
= icon
;
1324 nautilus_icon_container_update_icon (container
, old_icon
);
1325 nautilus_icon_container_update_icon (container
, icon
);
1329 nautilus_icon_dnd_update_drop_target (NautilusIconContainer
*container
,
1330 GdkDragContext
*context
,
1335 double world_x
, world_y
;
1338 g_assert (NAUTILUS_IS_ICON_CONTAINER (container
));
1340 canvas_widget_to_world (EEL_CANVAS (container
), x
, y
, &world_x
, &world_y
);
1342 /* Find the item we hit with our drop, if any. */
1343 icon
= nautilus_icon_container_item_at (container
, world_x
, world_y
);
1345 /* FIXME bugzilla.gnome.org 42485:
1346 * These "can_accept_items" tests need to be done by
1347 * the icon view, not here. This file is not supposed to know
1348 * that the target is a file.
1351 /* Find if target icon accepts our drop. */
1352 if (icon
!= NULL
&& (container
->details
->dnd_info
->drag_info
.data_type
!= NAUTILUS_ICON_DND_KEYWORD
)) {
1353 uri
= nautilus_icon_container_get_icon_uri (container
, icon
);
1354 file
= nautilus_file_get_by_uri (uri
);
1357 if (!nautilus_drag_can_accept_info (file
,
1358 container
->details
->dnd_info
->drag_info
.data_type
,
1359 container
->details
->dnd_info
->drag_info
.selection_list
)) {
1363 nautilus_file_unref (file
);
1366 set_drop_target (container
, icon
);
1370 nautilus_icon_container_free_drag_data (NautilusIconContainer
*container
)
1372 NautilusIconDndInfo
*dnd_info
;
1374 dnd_info
= container
->details
->dnd_info
;
1376 dnd_info
->drag_info
.got_drop_data_type
= FALSE
;
1378 if (dnd_info
->shadow
!= NULL
) {
1379 gtk_object_destroy (GTK_OBJECT (dnd_info
->shadow
));
1380 dnd_info
->shadow
= NULL
;
1383 if (dnd_info
->drag_info
.selection_data
!= NULL
) {
1384 gtk_selection_data_free (dnd_info
->drag_info
.selection_data
);
1385 dnd_info
->drag_info
.selection_data
= NULL
;
1388 if (dnd_info
->drag_info
.direct_save_uri
!= NULL
) {
1389 g_free (dnd_info
->drag_info
.direct_save_uri
);
1390 dnd_info
->drag_info
.direct_save_uri
= NULL
;
1395 drag_leave_callback (GtkWidget
*widget
,
1396 GdkDragContext
*context
,
1400 NautilusIconDndInfo
*dnd_info
;
1402 dnd_info
= NAUTILUS_ICON_CONTAINER (widget
)->details
->dnd_info
;
1404 if (dnd_info
->shadow
!= NULL
)
1405 eel_canvas_item_hide (dnd_info
->shadow
);
1407 stop_dnd_highlight (widget
);
1409 set_drop_target (NAUTILUS_ICON_CONTAINER (widget
), NULL
);
1410 stop_auto_scroll (NAUTILUS_ICON_CONTAINER (widget
));
1411 nautilus_icon_container_free_drag_data(NAUTILUS_ICON_CONTAINER (widget
));
1415 drag_begin_callback (GtkWidget
*widget
,
1416 GdkDragContext
*context
,
1419 NautilusIconContainer
*container
;
1421 GdkColormap
*colormap
;
1424 double x1
, y1
, x2
, y2
, winx
, winy
;
1425 int x_offset
, y_offset
;
1426 int start_x
, start_y
;
1429 container
= NAUTILUS_ICON_CONTAINER (widget
);
1431 screen
= gtk_widget_get_screen (widget
);
1433 if (gdk_screen_is_composited (screen
)) {
1434 colormap
= gdk_screen_get_rgba_colormap (screen
);
1435 if (colormap
!= NULL
) {
1440 /* Fall back on using the same colormap as the widget */
1441 if (colormap
== NULL
) {
1442 colormap
= gtk_widget_get_colormap (widget
);
1446 start_x
= container
->details
->dnd_info
->drag_info
.start_x
+ gtk_adjustment_get_value (gtk_layout_get_hadjustment (GTK_LAYOUT (container
)));
1447 start_y
= container
->details
->dnd_info
->drag_info
.start_y
+ gtk_adjustment_get_value (gtk_layout_get_vadjustment (GTK_LAYOUT (container
)));
1449 /* create a pixmap and mask to drag with */
1450 pixmap
= nautilus_icon_canvas_item_get_image (container
->details
->drag_icon
->item
, &mask
, colormap
);
1452 /* we want to drag semi-transparent pixbufs, but X is too slow dealing with
1453 stippled masks, so we had to remove the code; this comment is left as a memorial
1454 to it, with the hope that we get it back someday as X Windows improves */
1456 /* compute the image's offset */
1457 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (container
->details
->drag_icon
->item
),
1458 &x1
, &y1
, &x2
, &y2
);
1459 eel_canvas_world_to_window (EEL_CANVAS (container
),
1460 x1
, y1
, &winx
, &winy
);
1461 x_offset
= start_x
- winx
;
1462 y_offset
= start_y
- winy
;
1467 /* If composite works, make the icons partially transparent */
1468 cr
= gdk_cairo_create (pixmap
);
1469 cairo_set_operator (cr
, CAIRO_OPERATOR_DEST_OUT
);
1470 cairo_set_source_rgba(cr
, 1,0,0,0.35);
1475 gtk_drag_set_icon_pixmap (context
,
1477 pixmap
, (use_mask
? mask
: NULL
),
1478 x_offset
, y_offset
);
1482 nautilus_icon_dnd_begin_drag (NautilusIconContainer
*container
,
1483 GdkDragAction actions
,
1485 GdkEventMotion
*event
,
1489 NautilusIconDndInfo
*dnd_info
;
1490 GdkDragContext
*context
;
1492 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
1493 g_return_if_fail (event
!= NULL
);
1495 dnd_info
= container
->details
->dnd_info
;
1496 g_return_if_fail (dnd_info
!= NULL
);
1498 /* Notice that the event is in bin_window coordinates, because of
1499 the way the canvas handles events.
1501 dnd_info
->drag_info
.start_x
= start_x
- gtk_adjustment_get_value (gtk_layout_get_hadjustment (GTK_LAYOUT (container
)));
1502 dnd_info
->drag_info
.start_y
= start_y
- gtk_adjustment_get_value (gtk_layout_get_vadjustment (GTK_LAYOUT (container
)));
1504 /* start the drag */
1505 context
= gtk_drag_begin (GTK_WIDGET (container
),
1506 dnd_info
->drag_info
.target_list
,
1509 (GdkEvent
*) event
);
1513 drag_highlight_expose (GtkWidget
*widget
,
1514 GdkEventExpose
*event
,
1517 gint x
, y
, width
, height
;
1520 x
= gtk_adjustment_get_value (gtk_layout_get_hadjustment (GTK_LAYOUT (widget
)));
1521 y
= gtk_adjustment_get_value (gtk_layout_get_vadjustment (GTK_LAYOUT (widget
)));
1522 gdk_drawable_get_size (widget
->window
, &width
, &height
);
1524 window
= GTK_LAYOUT (widget
)->bin_window
;
1526 gtk_paint_shadow (widget
->style
, window
,
1527 GTK_STATE_NORMAL
, GTK_SHADOW_OUT
,
1528 NULL
, widget
, "dnd",
1529 x
, y
, width
, height
);
1531 gdk_draw_rectangle (window
,
1532 widget
->style
->black_gc
,
1534 x
, y
, width
- 1, height
- 1);
1539 /* Queue a redraw of the dnd highlight rect */
1541 dnd_highlight_queue_redraw (GtkWidget
*widget
)
1543 NautilusIconDndInfo
*dnd_info
;
1546 dnd_info
= NAUTILUS_ICON_CONTAINER (widget
)->details
->dnd_info
;
1548 if (!dnd_info
->highlighted
) {
1552 width
= widget
->allocation
.width
;
1553 height
= widget
->allocation
.height
;
1555 /* we don't know how wide the shadow is exactly,
1556 * so we expose a 10-pixel wide border
1558 gtk_widget_queue_draw_area (widget
,
1561 gtk_widget_queue_draw_area (widget
,
1564 gtk_widget_queue_draw_area (widget
,
1567 gtk_widget_queue_draw_area (widget
,
1573 start_dnd_highlight (GtkWidget
*widget
)
1575 NautilusIconDndInfo
*dnd_info
;
1576 GtkWidget
*toplevel
;
1578 dnd_info
= NAUTILUS_ICON_CONTAINER (widget
)->details
->dnd_info
;
1580 toplevel
= gtk_widget_get_toplevel (widget
);
1581 if (toplevel
!= NULL
&&
1582 g_object_get_data (G_OBJECT (toplevel
), "is_desktop_window")) {
1586 if (!dnd_info
->highlighted
) {
1587 dnd_info
->highlighted
= TRUE
;
1588 g_signal_connect_after (widget
, "expose_event",
1589 G_CALLBACK (drag_highlight_expose
),
1591 dnd_highlight_queue_redraw (widget
);
1596 stop_dnd_highlight (GtkWidget
*widget
)
1598 NautilusIconDndInfo
*dnd_info
;
1600 dnd_info
= NAUTILUS_ICON_CONTAINER (widget
)->details
->dnd_info
;
1602 if (dnd_info
->highlighted
) {
1603 g_signal_handlers_disconnect_by_func (widget
,
1604 drag_highlight_expose
,
1606 dnd_highlight_queue_redraw (widget
);
1607 dnd_info
->highlighted
= FALSE
;
1612 drag_motion_callback (GtkWidget
*widget
,
1613 GdkDragContext
*context
,
1619 nautilus_icon_container_ensure_drag_data (NAUTILUS_ICON_CONTAINER (widget
), context
, time
);
1620 nautilus_icon_container_position_shadow (NAUTILUS_ICON_CONTAINER (widget
), x
, y
);
1621 nautilus_icon_dnd_update_drop_target (NAUTILUS_ICON_CONTAINER (widget
), context
, x
, y
);
1622 set_up_auto_scroll_if_needed (NAUTILUS_ICON_CONTAINER (widget
));
1623 /* Find out what the drop actions are based on our drag selection and
1627 nautilus_icon_container_get_drop_action (NAUTILUS_ICON_CONTAINER (widget
), context
, x
, y
,
1630 start_dnd_highlight (widget
);
1633 gdk_drag_status (context
, action
, time
);
1639 drag_drop_callback (GtkWidget
*widget
,
1640 GdkDragContext
*context
,
1646 NautilusIconDndInfo
*dnd_info
;
1648 dnd_info
= NAUTILUS_ICON_CONTAINER (widget
)->details
->dnd_info
;
1650 /* tell the drag_data_received callback that
1651 the drop occured and that it can actually
1652 process the actions.
1653 make sure it is going to be called at least once.
1655 dnd_info
->drag_info
.drop_occured
= TRUE
;
1657 get_data_on_first_target_we_support (widget
, context
, time
, x
, y
);
1663 nautilus_icon_dnd_end_drag (NautilusIconContainer
*container
)
1665 NautilusIconDndInfo
*dnd_info
;
1667 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
1669 dnd_info
= container
->details
->dnd_info
;
1670 g_return_if_fail (dnd_info
!= NULL
);
1671 stop_auto_scroll (container
);
1673 * Can that possibly be right?
1677 /** this callback is called in 2 cases.
1678 It is called upon drag_motion events to get the actual data
1679 In that case, it just makes sure it gets the data.
1680 It is called upon drop_drop events to execute the actual
1681 actions on the received action. In that case, it actually first makes sure
1682 that we have got the data then processes it.
1686 drag_data_received_callback (GtkWidget
*widget
,
1687 GdkDragContext
*context
,
1690 GtkSelectionData
*data
,
1695 NautilusDragInfo
*drag_info
;
1696 EelBackground
*background
;
1700 drag_info
= &(NAUTILUS_ICON_CONTAINER (widget
)->details
->dnd_info
->drag_info
);
1702 drag_info
->got_drop_data_type
= TRUE
;
1703 drag_info
->data_type
= info
;
1706 case NAUTILUS_ICON_DND_GNOME_ICON_LIST
:
1707 nautilus_icon_container_dropped_icon_feedback (widget
, data
, x
, y
);
1709 case NAUTILUS_ICON_DND_COLOR
:
1710 case NAUTILUS_ICON_DND_BGIMAGE
:
1711 case NAUTILUS_ICON_DND_KEYWORD
:
1712 case NAUTILUS_ICON_DND_URI_LIST
:
1713 case NAUTILUS_ICON_DND_TEXT
:
1714 case NAUTILUS_ICON_DND_RESET_BACKGROUND
:
1715 case NAUTILUS_ICON_DND_XDNDDIRECTSAVE
:
1716 /* Save the data so we can do the actual work on drop. */
1717 if (drag_info
->selection_data
!= NULL
) {
1718 gtk_selection_data_free (drag_info
->selection_data
);
1720 drag_info
->selection_data
= gtk_selection_data_copy (data
);
1723 /* Netscape keeps sending us the data, even though we accept the first drag */
1724 case NAUTILUS_ICON_DND_NETSCAPE_URL
:
1725 if (drag_info
->selection_data
!= NULL
) {
1726 gtk_selection_data_free (drag_info
->selection_data
);
1727 drag_info
->selection_data
= gtk_selection_data_copy (data
);
1730 case NAUTILUS_ICON_DND_ROOTWINDOW_DROP
:
1731 /* Do nothing, this won't even happen, since we don't want to call get_data twice */
1735 /* this is the second use case of this callback.
1736 * we have to do the actual work for the drop.
1738 if (drag_info
->drop_occured
) {
1742 case NAUTILUS_ICON_DND_GNOME_ICON_LIST
:
1743 nautilus_icon_container_receive_dropped_icons
1744 (NAUTILUS_ICON_CONTAINER (widget
),
1747 case NAUTILUS_ICON_DND_COLOR
:
1748 receive_dropped_color (NAUTILUS_ICON_CONTAINER (widget
),
1754 case NAUTILUS_ICON_DND_BGIMAGE
:
1755 receive_dropped_tile_image
1756 (NAUTILUS_ICON_CONTAINER (widget
),
1760 case NAUTILUS_ICON_DND_KEYWORD
:
1761 receive_dropped_keyword
1762 (NAUTILUS_ICON_CONTAINER (widget
),
1763 (char *) data
->data
, x
, y
);
1765 case NAUTILUS_ICON_DND_NETSCAPE_URL
:
1766 receive_dropped_netscape_url
1767 (NAUTILUS_ICON_CONTAINER (widget
),
1768 (char *) data
->data
, context
, x
, y
);
1771 case NAUTILUS_ICON_DND_URI_LIST
:
1772 receive_dropped_uri_list
1773 (NAUTILUS_ICON_CONTAINER (widget
),
1774 (char *) data
->data
, context
, x
, y
);
1777 case NAUTILUS_ICON_DND_TEXT
:
1778 tmp
= gtk_selection_data_get_text (data
);
1779 receive_dropped_text
1780 (NAUTILUS_ICON_CONTAINER (widget
),
1781 (char *) tmp
, context
, x
, y
);
1785 case NAUTILUS_ICON_DND_RESET_BACKGROUND
:
1786 background
= eel_get_widget_background (widget
);
1787 if (background
!= NULL
) {
1788 eel_background_reset (background
);
1790 gtk_drag_finish (context
, FALSE
, FALSE
, time
);
1792 case NAUTILUS_ICON_DND_ROOTWINDOW_DROP
:
1793 /* Do nothing, everything is done by the sender */
1795 case NAUTILUS_ICON_DND_XDNDDIRECTSAVE
:
1796 /* Indicate that we don't provide "F" fallback */
1797 if (drag_info
->selection_data
->format
== 8 &&
1798 drag_info
->selection_data
->length
== 1 &&
1799 drag_info
->selection_data
->data
[0] == 'F') {
1800 gdk_property_change (GDK_DRAWABLE (context
->source_window
),
1801 gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE
, FALSE
),
1802 gdk_atom_intern ("text/plain", FALSE
), 8,
1803 GDK_PROP_MODE_REPLACE
, (const guchar
*) "", 0);
1804 } else if (drag_info
->selection_data
->format
== 8 &&
1805 drag_info
->selection_data
->length
== 1 &&
1806 drag_info
->selection_data
->data
[0] == 'S' &&
1807 drag_info
->direct_save_uri
!= NULL
) {
1811 location
= g_file_new_for_uri (drag_info
->direct_save_uri
);
1813 nautilus_file_changes_queue_file_added (location
);
1815 nautilus_file_changes_queue_schedule_position_set (location
,
1817 gdk_screen_get_number (gtk_widget_get_screen (widget
)));
1818 g_object_unref (location
);
1819 nautilus_file_changes_consume_changes (TRUE
);
1824 gtk_drag_finish (context
, success
, FALSE
, time
);
1826 nautilus_icon_container_free_drag_data (NAUTILUS_ICON_CONTAINER (widget
));
1828 set_drop_target (NAUTILUS_ICON_CONTAINER (widget
), NULL
);
1830 /* reinitialise it for the next dnd */
1831 drag_info
->drop_occured
= FALSE
;
1837 nautilus_icon_dnd_set_stipple (NautilusIconContainer
*container
,
1840 if (stipple
!= NULL
) {
1841 g_object_ref (stipple
);
1844 if (container
->details
->dnd_info
->stipple
!= NULL
) {
1845 g_object_unref (container
->details
->dnd_info
->stipple
);
1848 container
->details
->dnd_info
->stipple
= stipple
;
1852 nautilus_icon_dnd_init (NautilusIconContainer
*container
,
1855 GtkTargetList
*targets
;
1858 g_return_if_fail (container
!= NULL
);
1859 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
1862 container
->details
->dnd_info
= g_new0 (NautilusIconDndInfo
, 1);
1863 nautilus_drag_init (&container
->details
->dnd_info
->drag_info
,
1864 drag_types
, G_N_ELEMENTS (drag_types
), TRUE
);
1866 /* Set up the widget as a drag destination.
1867 * (But not a source, as drags starting from this widget will be
1868 * implemented by dealing with events manually.)
1870 n_elements
= G_N_ELEMENTS (drop_types
);
1871 if (!nautilus_icon_container_get_is_desktop (container
)) {
1872 /* Don't set up rootwindow drop */
1875 gtk_drag_dest_set (GTK_WIDGET (container
),
1877 drop_types
, n_elements
,
1878 GDK_ACTION_COPY
| GDK_ACTION_MOVE
| GDK_ACTION_LINK
| GDK_ACTION_ASK
);
1880 targets
= gtk_drag_dest_get_target_list (GTK_WIDGET (container
));
1881 gtk_target_list_add_text_targets (targets
, NAUTILUS_ICON_DND_TEXT
);
1884 /* Messages for outgoing drag. */
1885 g_signal_connect (container
, "drag_begin",
1886 G_CALLBACK (drag_begin_callback
), NULL
);
1887 g_signal_connect (container
, "drag_data_get",
1888 G_CALLBACK (drag_data_get_callback
), NULL
);
1889 g_signal_connect (container
, "drag_end",
1890 G_CALLBACK (drag_end_callback
), NULL
);
1892 /* Messages for incoming drag. */
1893 g_signal_connect (container
, "drag_data_received",
1894 G_CALLBACK (drag_data_received_callback
), NULL
);
1895 g_signal_connect (container
, "drag_motion",
1896 G_CALLBACK (drag_motion_callback
), NULL
);
1897 g_signal_connect (container
, "drag_drop",
1898 G_CALLBACK (drag_drop_callback
), NULL
);
1899 g_signal_connect (container
, "drag_leave",
1900 G_CALLBACK (drag_leave_callback
), NULL
);
1902 if (stipple
!= NULL
) {
1903 container
->details
->dnd_info
->stipple
= g_object_ref (stipple
);
1908 nautilus_icon_dnd_fini (NautilusIconContainer
*container
)
1910 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
1912 if (container
->details
->dnd_info
!= NULL
) {
1913 stop_auto_scroll (container
);
1915 if (container
->details
->dnd_info
->stipple
!= NULL
) {
1916 g_object_unref (container
->details
->dnd_info
->stipple
);
1919 nautilus_drag_finalize (&container
->details
->dnd_info
->drag_info
);
1920 container
->details
->dnd_info
= NULL
;