Updated Slovenian translation
[nautilus.git] / libnautilus-private / nautilus-icon-dnd.c
blob46b92b5ecd5cc3f07435ed16cec679254c164e61
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>)
34 #include <config.h>
35 #include <math.h>
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>
54 #include <gdk/gdkx.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>
64 #include <stdio.h>
65 #include <string.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 */
82 /* Must be last: */
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,
98 GList *list)
100 EelCanvasGroup *group;
101 EelCanvas *canvas;
102 GdkBitmap *stipple;
103 int max_x, max_y;
104 int min_x, min_y;
105 GList *p;
107 if (list == NULL) {
108 return NULL;
111 /* if we're only dragging a single item, don't worry about the shadow */
112 if (list->next == NULL) {
113 return 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;
126 min_x = -max_x;
128 max_y = GTK_WIDGET (container)->allocation.height;
129 min_y = -max_y;
131 /* Create a group, so that it's easier to move all the items around at
132 once. */
133 group = EEL_CANVAS_GROUP
134 (eel_canvas_item_new (EEL_CANVAS_GROUP (canvas->root),
135 eel_canvas_group_get_type (),
136 NULL));
138 for (p = list; p != NULL; p = p->next) {
139 NautilusDragSelectionItem *item;
140 int x1, y1, x2, y2;
142 item = p->data;
144 if (!item->got_icon_position) {
145 continue;
148 x1 = item->icon_x;
149 y1 = item->icon_y;
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)
154 eel_canvas_item_new
155 (group,
156 eel_canvas_rect_get_type (),
157 "x1", (double) x1,
158 "y1", (double) y1,
159 "x2", (double) x2,
160 "y2", (double) y2,
161 "outline_color", "black",
162 "outline_stipple", stipple,
163 "width_pixels", 1,
164 NULL);
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.
173 static void
174 set_shadow_position (EelCanvasItem *shadow,
175 double x, double y)
177 eel_canvas_item_set (shadow,
178 "x", x, "y", y,
179 NULL);
183 /* Source-side handling of the drag. */
185 /* iteration glue struct */
186 typedef struct {
187 gpointer iterator_context;
188 NautilusDragEachSelectedItemDataGet iteratee;
189 gpointer iteratee_data;
190 } IconGetDataBinderContext;
192 static void
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)));
211 static void
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))),
219 world_x, world_y);
222 static gboolean
223 icon_get_data_binder (NautilusIcon *icon, gpointer data)
225 IconGetDataBinderContext *context;
226 EelDRect world_rect;
227 EelIRect widget_rect;
228 char *uri;
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);
242 if (uri == NULL) {
243 g_warning ("no URI for one of the iterated icons");
244 return TRUE;
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);
262 g_free (uri);
264 return TRUE;
267 /* Iterate over each selected icon in a NautilusIconContainer,
268 * calling each_function on each.
270 static void
271 nautilus_icon_container_each_selected_icon (NautilusIconContainer *container,
272 gboolean (*each_function) (NautilusIcon *, gpointer), gpointer data)
274 GList *p;
275 NautilusIcon *icon;
277 for (p = container->details->icons; p != NULL; p = p->next) {
278 icon = p->data;
279 if (!icon->is_selected) {
280 continue;
282 if (!each_function (icon, data)) {
283 return;
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
292 static void
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 */
309 static void
310 drag_data_get_callback (GtkWidget *widget,
311 GdkDragContext *context,
312 GtkSelectionData *selection_data,
313 guint info,
314 guint32 time,
315 gpointer 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. */
332 static void
333 nautilus_icon_container_position_shadow (NautilusIconContainer *container,
334 int x, int y)
336 EelCanvasItem *shadow;
337 double world_x, world_y;
339 shadow = container->details->dnd_info->shadow;
340 if (shadow == NULL) {
341 return;
344 canvas_widget_to_world (EEL_CANVAS (container), x, y,
345 &world_x, &world_y);
347 set_shadow_position (shadow, world_x, world_y);
348 eel_canvas_item_show (shadow);
351 static void
352 nautilus_icon_container_dropped_icon_feedback (GtkWidget *widget,
353 GtkSelectionData *data,
354 int x, int y)
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);
379 static char *
380 get_direct_save_filename (GdkDragContext *context)
382 guchar *prop_text;
383 gint prop_len;
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) {
388 return 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");
400 g_free (prop_text);
401 return NULL;
404 return prop_text;
407 static void
408 set_direct_save_uri (GtkWidget *widget, GdkDragContext *context, NautilusDragInfo *drag_info, int x, int y)
410 GFile *base, *child;
411 char *filename, *drop_target;
412 gchar *uri;
414 drag_info->got_drop_data_type = TRUE;
415 drag_info->data_type = NAUTILUS_ICON_DND_XDNDDIRECTSAVE;
417 uri = NULL;
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,
441 strlen (uri));
443 drag_info->direct_save_uri = uri;
446 g_free (filename);
447 g_free (drop_target);
450 /* FIXME bugzilla.gnome.org 47445: Needs to become a shared function */
451 static void
452 get_data_on_first_target_we_support (GtkWidget *widget, GdkDragContext *context, guint32 time, int x, int y)
454 GtkTargetList *list;
455 GdkAtom target;
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;
470 } else {
471 list = drop_types_list;
474 target = gtk_drag_dest_find_target (widget, context, list);
475 if (target != GDK_NONE) {
476 guint info;
477 NautilusDragInfo *drag_info;
478 gboolean found;
480 drag_info = &(NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->drag_info);
482 found = gtk_target_list_find (list, target, &info);
483 g_assert (found);
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;
493 } else {
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,
498 target, time);
503 static void
504 nautilus_icon_container_ensure_drag_data (NautilusIconContainer *container,
505 GdkDragContext *context,
506 guint32 time)
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);
517 static void
518 drag_end_callback (GtkWidget *widget,
519 GdkDragContext *context,
520 gpointer data)
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,
534 int x, int y)
536 GList *p;
537 int size;
538 EelDRect point;
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));
546 point.x0 = x;
547 point.y0 = y;
548 point.x1 = x + size;
549 point.y1 = y + size;
551 for (p = container->details->icons; p != NULL; p = p->next) {
552 NautilusIcon *icon;
553 icon = p->data;
555 eel_canvas_w2c (EEL_CANVAS (container),
556 point.x0,
557 point.y0,
558 &canvas_point.x0,
559 &canvas_point.y0);
560 eel_canvas_w2c (EEL_CANVAS (container),
561 point.x1,
562 point.y1,
563 &canvas_point.x1,
564 &canvas_point.y1);
565 if (nautilus_icon_canvas_item_hit_test_rectangle (icon->item, canvas_point)) {
566 return icon;
570 return NULL;
573 static char *
574 get_container_uri (NautilusIconContainer *container)
576 char *uri;
578 /* get the URI associated with the container */
579 uri = NULL;
580 g_signal_emit_by_name (container, "get_container_uri", &uri);
581 return uri;
584 static gboolean
585 nautilus_icon_container_selection_items_local (NautilusIconContainer *container,
586 GList *items)
588 char *container_uri_string;
589 gboolean result;
591 /* must have at least one item */
592 g_assert (items);
594 result = FALSE;
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);
601 } else {
602 result = nautilus_drag_items_local (container_uri_string, items);
604 g_free (container_uri_string);
606 return result;
609 static GdkDragAction
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);
627 return action;
630 static void
631 receive_dropped_color (NautilusIconContainer *container,
632 int x, int y,
633 GdkDragAction action,
634 GtkSelectionData *data)
636 action = get_background_drag_action (container, action);
638 if (action > 0) {
639 char *uri;
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);
644 g_free (uri);
646 eel_background_receive_dropped_color
647 (eel_get_widget_background (GTK_WIDGET (container)),
648 GTK_WIDGET (container),
649 action, x, y, data);
653 /* handle dropped tile images */
654 static void
655 receive_dropped_tile_image (NautilusIconContainer *container, GdkDragAction action, GtkSelectionData *data)
657 g_assert (data != NULL);
659 action = get_background_drag_action (container, action);
661 if (action > 0) {
662 char *uri;
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);
667 g_free (uri);
669 eel_background_receive_dropped_background_image
670 (eel_get_widget_background (GTK_WIDGET (container)),
671 action,
672 data->data);
676 /* handle dropped keywords */
677 static void
678 receive_dropped_keyword (NautilusIconContainer *container, const char *keyword, int x, int y)
680 char *uri;
681 double world_x, world_y;
683 NautilusIcon *drop_target_icon;
684 NautilusFile *file;
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) {
692 return;
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",
706 keyword, uri);
708 file = nautilus_file_get_by_uri (uri);
709 g_free (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 */
718 static void
719 receive_dropped_netscape_url (NautilusIconContainer *container, const char *encoded_url, GdkDragContext *context, int x, int y)
721 char *drop_target;
723 if (encoded_url == NULL) {
724 return;
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",
730 encoded_url,
731 drop_target,
732 context->action,
733 x, y);
735 g_free (drop_target);
738 /* handle dropped uri list */
739 static void
740 receive_dropped_uri_list (NautilusIconContainer *container, const char *uri_list, GdkDragContext *context, int x, int y)
742 char *drop_target;
744 if (uri_list == NULL) {
745 return;
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",
751 uri_list,
752 drop_target,
753 context->action,
754 x, y);
756 g_free (drop_target);
759 /* handle dropped text */
760 static void
761 receive_dropped_text (NautilusIconContainer *container, const char *text, GdkDragContext *context, int x, int y)
763 char *drop_target;
765 if (text == NULL) {
766 return;
769 drop_target = nautilus_icon_container_find_drop_target (container, context, x, y, NULL, TRUE);
771 g_signal_emit_by_name (container, "handle_text",
772 text,
773 drop_target,
774 context->action,
775 x, y);
777 g_free (drop_target);
780 static int
781 auto_scroll_timeout_callback (gpointer data)
783 NautilusIconContainer *container;
784 GtkWidget *widget;
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()) {
794 /* not yet */
795 return TRUE;
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) {
802 /* no work */
803 return TRUE;
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
813 return TRUE;
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
824 * area.
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,
849 exposed_area.x,
850 exposed_area.y,
851 exposed_area.width,
852 exposed_area.height);
854 return TRUE;
857 static void
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,
863 container);
866 static void
867 stop_auto_scroll (NautilusIconContainer *container)
869 nautilus_drag_autoscroll_stop (&container->details->dnd_info->drag_info);
872 static gboolean
873 confirm_switch_to_manual_layout (NautilusIconContainer *container)
875 #if 0
876 const char *message;
877 const char *detail;
878 GtkDialog *dialog;
879 int response;
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.");
891 } else {
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.");
896 } else {
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.");
900 } else {
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?"),
908 GTK_STOCK_CANCEL,
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;
915 #else
916 return FALSE;
917 #endif
920 static void
921 handle_local_move (NautilusIconContainer *container,
922 double world_x, double world_y)
924 GList *moved_icons, *p;
925 NautilusDragSelectionItem *item;
926 NautilusIcon *icon;
927 NautilusFile *file;
928 char screen_string[32];
929 GdkScreen *screen;
931 if (container->details->auto_layout) {
932 if (!confirm_switch_to_manual_layout (container)) {
933 return;
935 nautilus_icon_container_freeze_icon_positions (container);
939 /* Move and select the icons. */
940 moved_icons = NULL;
941 for (p = container->details->dnd_info->drag_info.selection_list; p != NULL; p = p->next) {
942 item = p->data;
944 icon = nautilus_icon_container_get_icon_by_uri
945 (container, item->uri);
947 if (icon == NULL) {
948 /* probably dragged from another screen. Add it to
949 * this screen
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),
963 FALSE);
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
971 (container, icon,
972 world_x + item->icon_x, world_y + item->icon_y,
973 icon->scale,
974 TRUE, TRUE, TRUE);
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);
985 static void
986 handle_nonlocal_move (NautilusIconContainer *container,
987 GdkDragContext *context,
988 int x, int y,
989 const char *target_uri,
990 gboolean icon_hit)
992 GList *source_uris, *p;
993 GArray *source_item_locations;
994 gboolean free_target_uri, is_rtl;
995 int index, item_x;
997 if (container->details->dnd_info->drag_info.selection_list == NULL) {
998 return;
1001 source_uris = 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));
1011 if (!icon_hit) {
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;
1021 if (is_rtl)
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;
1036 if (is_rtl) {
1037 x = CANVAS_WIDTH (container) - x;
1040 /* start the copy */
1041 g_signal_emit_by_name (container, "move_copy_items",
1042 source_uris,
1043 source_item_locations,
1044 target_uri,
1045 context->action,
1046 x, y);
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);
1056 static char *
1057 nautilus_icon_container_find_drop_target (NautilusIconContainer *container,
1058 GdkDragContext *context,
1059 int x, int y,
1060 gboolean *icon_hit,
1061 gboolean rewrite_desktop)
1063 NautilusIcon *drop_target_icon;
1064 double world_x, world_y;
1065 NautilusFile *file;
1066 char *icon_uri;
1067 char *container_uri;
1069 if (icon_hit) {
1070 *icon_hit = FALSE;
1073 if (!container->details->dnd_info->drag_info.got_drop_data_type) {
1074 return NULL;
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;
1101 g_free (icon_uri);
1102 nautilus_file_unref (file);
1106 if (drop_target_icon == NULL) {
1107 if (icon_hit) {
1108 *icon_hit = FALSE;
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;
1123 if (icon_hit) {
1124 *icon_hit = TRUE;
1126 return nautilus_icon_container_get_icon_drop_target_uri (container, drop_target_icon);
1129 static gboolean
1130 selection_is_image_file (GList *selection_list)
1132 const char *mime_type;
1133 NautilusDragSelectionItem *selected_item;
1134 gboolean result;
1135 GFile *location;
1136 GFileInfo *info;
1138 /* Make sure only one item is selected */
1139 if (selection_list == NULL ||
1140 selection_list->next != NULL) {
1141 return FALSE;
1144 selected_item = selection_list->data;
1146 mime_type = NULL;
1148 location = g_file_new_for_uri (selected_item->uri);
1149 info = g_file_query_info (location,
1150 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
1151 0, NULL, NULL);
1152 if (info) {
1153 mime_type = g_file_info_get_content_type (info);
1156 result = eel_istr_has_prefix (mime_type, "image/");
1158 if (info) {
1159 g_object_unref (info);
1161 g_object_unref (location);
1163 return result;
1167 static void
1168 nautilus_icon_container_receive_dropped_icons (NautilusIconContainer *container,
1169 GdkDragContext *context,
1170 int x, int y)
1172 char *drop_target;
1173 gboolean local_move_only;
1174 double world_x, world_y;
1175 gboolean icon_hit;
1176 GdkDragAction action;
1177 NautilusDragSelectionItem *selected_item;
1179 drop_target = NULL;
1181 if (container->details->dnd_info->drag_info.selection_list == NULL) {
1182 return;
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;
1191 } else {
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)),
1206 context->action,
1207 selected_item->uri);
1208 return;
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);
1231 } else {
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;
1241 static void
1242 nautilus_icon_container_get_drop_action (NautilusIconContainer *container,
1243 GdkDragContext *context,
1244 int x, int y,
1245 int *action)
1247 char *drop_target;
1248 gboolean icon_hit;
1249 NautilusIcon *icon;
1250 double world_x, world_y;
1252 icon_hit = FALSE;
1253 if (!container->details->dnd_info->drag_info.got_drop_data_type) {
1254 /* drag_data_received_callback didn't get called yet */
1255 return;
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);
1263 *action = 0;
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) {
1269 return;
1271 drop_target = nautilus_icon_container_find_drop_target (container,
1272 context, x, y, &icon_hit, FALSE);
1273 if (!drop_target) {
1274 return;
1276 nautilus_drag_default_drop_action_for_icons (context, drop_target,
1277 container->details->dnd_info->drag_info.selection_list,
1278 action);
1279 g_free (drop_target);
1280 break;
1282 /* handle emblems by setting the action if we're over an object */
1283 case NAUTILUS_ICON_DND_KEYWORD:
1284 if (icon != NULL) {
1285 *action = context->suggested_action;
1287 break;
1289 case NAUTILUS_ICON_DND_NETSCAPE_URL:
1290 *action = nautilus_drag_default_drop_action_for_netscape_url (context);
1291 break;
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;
1299 break;
1301 case NAUTILUS_ICON_DND_TEXT:
1302 case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
1303 *action = GDK_ACTION_COPY;
1304 break;
1308 static void
1309 set_drop_target (NautilusIconContainer *container,
1310 NautilusIcon *icon)
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) {
1319 return;
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);
1328 static void
1329 nautilus_icon_dnd_update_drop_target (NautilusIconContainer *container,
1330 GdkDragContext *context,
1331 int x, int y)
1333 NautilusIcon *icon;
1334 NautilusFile *file;
1335 double world_x, world_y;
1336 char *uri;
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);
1355 g_free (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)) {
1360 icon = NULL;
1363 nautilus_file_unref (file);
1366 set_drop_target (container, icon);
1369 static void
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;
1394 static void
1395 drag_leave_callback (GtkWidget *widget,
1396 GdkDragContext *context,
1397 guint32 time,
1398 gpointer data)
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));
1414 static void
1415 drag_begin_callback (GtkWidget *widget,
1416 GdkDragContext *context,
1417 gpointer data)
1419 NautilusIconContainer *container;
1420 GdkScreen *screen;
1421 GdkColormap *colormap;
1422 GdkPixmap *pixmap;
1423 GdkBitmap *mask;
1424 double x1, y1, x2, y2, winx, winy;
1425 int x_offset, y_offset;
1426 int start_x, start_y;
1427 gboolean use_mask;
1429 container = NAUTILUS_ICON_CONTAINER (widget);
1431 screen = gtk_widget_get_screen (widget);
1432 colormap = NULL;
1433 if (gdk_screen_is_composited (screen)) {
1434 colormap = gdk_screen_get_rgba_colormap (screen);
1435 if (colormap != NULL) {
1436 use_mask = FALSE;
1440 /* Fall back on using the same colormap as the widget */
1441 if (colormap == NULL) {
1442 colormap = gtk_widget_get_colormap (widget);
1443 use_mask = TRUE;
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;
1464 if (!use_mask) {
1465 cairo_t *cr;
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);
1471 cairo_paint (cr);
1472 cairo_destroy (cr);
1475 gtk_drag_set_icon_pixmap (context,
1476 colormap,
1477 pixmap, (use_mask ? mask : NULL),
1478 x_offset, y_offset);
1481 void
1482 nautilus_icon_dnd_begin_drag (NautilusIconContainer *container,
1483 GdkDragAction actions,
1484 int button,
1485 GdkEventMotion *event,
1486 int start_x,
1487 int start_y)
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,
1507 actions,
1508 button,
1509 (GdkEvent *) event);
1512 static gboolean
1513 drag_highlight_expose (GtkWidget *widget,
1514 GdkEventExpose *event,
1515 gpointer data)
1517 gint x, y, width, height;
1518 GdkWindow *window;
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,
1533 FALSE,
1534 x, y, width - 1, height - 1);
1536 return FALSE;
1539 /* Queue a redraw of the dnd highlight rect */
1540 static void
1541 dnd_highlight_queue_redraw (GtkWidget *widget)
1543 NautilusIconDndInfo *dnd_info;
1544 int width, height;
1546 dnd_info = NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info;
1548 if (!dnd_info->highlighted) {
1549 return;
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,
1559 0, 0,
1560 width, 10);
1561 gtk_widget_queue_draw_area (widget,
1562 0, 0,
1563 10, height);
1564 gtk_widget_queue_draw_area (widget,
1565 0, height - 10,
1566 width, 10);
1567 gtk_widget_queue_draw_area (widget,
1568 width - 10, 0,
1569 10, height);
1572 static void
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")) {
1583 return;
1586 if (!dnd_info->highlighted) {
1587 dnd_info->highlighted = TRUE;
1588 g_signal_connect_after (widget, "expose_event",
1589 G_CALLBACK (drag_highlight_expose),
1590 NULL);
1591 dnd_highlight_queue_redraw (widget);
1595 static void
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,
1605 NULL);
1606 dnd_highlight_queue_redraw (widget);
1607 dnd_info->highlighted = FALSE;
1611 static gboolean
1612 drag_motion_callback (GtkWidget *widget,
1613 GdkDragContext *context,
1614 int x, int y,
1615 guint32 time)
1617 int action;
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
1624 * the drop target.
1626 action = 0;
1627 nautilus_icon_container_get_drop_action (NAUTILUS_ICON_CONTAINER (widget), context, x, y,
1628 &action);
1629 if (action != 0) {
1630 start_dnd_highlight (widget);
1633 gdk_drag_status (context, action, time);
1635 return TRUE;
1638 static gboolean
1639 drag_drop_callback (GtkWidget *widget,
1640 GdkDragContext *context,
1641 int x,
1642 int y,
1643 guint32 time,
1644 gpointer data)
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);
1659 return FALSE;
1662 void
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);
1672 /* Do nothing.
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.
1685 static void
1686 drag_data_received_callback (GtkWidget *widget,
1687 GdkDragContext *context,
1688 int x,
1689 int y,
1690 GtkSelectionData *data,
1691 guint info,
1692 guint32 time,
1693 gpointer user_data)
1695 NautilusDragInfo *drag_info;
1696 EelBackground *background;
1697 char *tmp;
1698 gboolean success;
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;
1705 switch (info) {
1706 case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
1707 nautilus_icon_container_dropped_icon_feedback (widget, data, x, y);
1708 break;
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);
1721 break;
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);
1729 break;
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 */
1732 break;
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) {
1740 success = FALSE;
1741 switch (info) {
1742 case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
1743 nautilus_icon_container_receive_dropped_icons
1744 (NAUTILUS_ICON_CONTAINER (widget),
1745 context, x, y);
1746 break;
1747 case NAUTILUS_ICON_DND_COLOR:
1748 receive_dropped_color (NAUTILUS_ICON_CONTAINER (widget),
1749 x, y,
1750 context->action,
1751 data);
1752 success = TRUE;
1753 break;
1754 case NAUTILUS_ICON_DND_BGIMAGE:
1755 receive_dropped_tile_image
1756 (NAUTILUS_ICON_CONTAINER (widget),
1757 context->action,
1758 data);
1759 break;
1760 case NAUTILUS_ICON_DND_KEYWORD:
1761 receive_dropped_keyword
1762 (NAUTILUS_ICON_CONTAINER (widget),
1763 (char *) data->data, x, y);
1764 break;
1765 case NAUTILUS_ICON_DND_NETSCAPE_URL:
1766 receive_dropped_netscape_url
1767 (NAUTILUS_ICON_CONTAINER (widget),
1768 (char *) data->data, context, x, y);
1769 success = TRUE;
1770 break;
1771 case NAUTILUS_ICON_DND_URI_LIST:
1772 receive_dropped_uri_list
1773 (NAUTILUS_ICON_CONTAINER (widget),
1774 (char *) data->data, context, x, y);
1775 success = TRUE;
1776 break;
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);
1782 success = TRUE;
1783 g_free (tmp);
1784 break;
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);
1791 break;
1792 case NAUTILUS_ICON_DND_ROOTWINDOW_DROP:
1793 /* Do nothing, everything is done by the sender */
1794 break;
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) {
1808 GdkPoint p;
1809 GFile *location;
1811 location = g_file_new_for_uri (drag_info->direct_save_uri);
1813 nautilus_file_changes_queue_file_added (location);
1814 p.x = x; p.y = y;
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);
1821 success = TRUE;
1822 break;
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;
1836 void
1837 nautilus_icon_dnd_set_stipple (NautilusIconContainer *container,
1838 GdkBitmap *stipple)
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;
1851 void
1852 nautilus_icon_dnd_init (NautilusIconContainer *container,
1853 GdkBitmap *stipple)
1855 GtkTargetList *targets;
1856 int n_elements;
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 */
1873 n_elements -= 1;
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);
1907 void
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;