Package: nautilus
Version: 2.26.2-5
Tags: patch

Background:
This bugreport concerns nautilus' support for XDS. For more information
about what XDS is and the standard for implementing it, see
http://www.newplanetsoftware.com/xds/

Short version:
Nautilus doesn't implement the "F" fallback in the XDS standard.

Long version:
Nautilus has support for XDS, but it is not complete. In particular, it
does not support the "F" fallback. The XDS standard outlines two ways
for the file to be saved: (1) The source application does all the work
of saving the file, with the file manager only providing the full URI
where the file should be saved. (2) If the source application can't or
won't save the file, the file manager should request the raw data (type
application/octet-stream) from the source and then save the file itself.

Number (2) is important because the source application may be running on
a remote machine and have no way to do the work of saving the file.
However, nautilus only implements only (1). The end result is that
nautilus does not accept XDS drops from an application that can't or
won't do all the work of saving the file.

Proposed solution:
One of my projects needed to have a file manager that fully implements
XDS. The file manager rox-filer worked fine, but I really wanted to use
nautilus. So I created a patch to make nautilus fully implement the XDS
standard. The file is attached, and it worked fine for me when added to
the quilt series in debian/patches.

Here are the main features of the patch: When handling an XDS drop, if
the source sends the "F" fallback, then nautilus immediately requests
the raw data in application/octet-stream format. Of course, nautilus
then needs to be able to handle drops of raw data, so the patch adds
that support as well. And since the raw data likely contains embedded
nul bytes, a GString is used to hold the data instead of a char *.

A simple test program written in Python is attached. Dragging the top
button to nautilus should create a file named 'tar' in the drop location
which should be identical to the file '/bin/tar'. This tests the full
implementation of the XDS standard. Dragging the bottom button should
create a file named 'dropped data' in the drop location which also
should be identical to the file '/bin/tar'. This does *not* use XDS; it
just tests nautilus' acceptance of raw data (type
application/octet-stream). BTW, nothing special about /bin/tar; it's
just a hard-coded example that I supposed everyone would have.

Let me know if you have any questions, or if there's anything else I can
do to help.

Thanks,
James Dietrich
-- 
  
  [email protected]

From 4ab85ebba84d2bd931f9fe34609d8337878c868a Mon Sep 17 00:00:00 2001
From: James Dietrich <[email protected]>
Date: Thu, 11 Jun 2009 08:43:33 -0400
Subject: [PATCH] Complete XDS support

---
 libnautilus-private/nautilus-dnd.h                 |    2 +
 libnautilus-private/nautilus-file-dnd.c            |    1 +
 libnautilus-private/nautilus-file-operations.c     |   16 +++--
 libnautilus-private/nautilus-file-operations.h     |    2 +-
 libnautilus-private/nautilus-icon-container.c      |   16 ++++
 libnautilus-private/nautilus-icon-container.h      |    7 ++
 libnautilus-private/nautilus-icon-dnd.c            |   69 ++++++++++++++----
 libnautilus-private/nautilus-marshal.list          |    1 +
 libnautilus-private/nautilus-tree-view-drag-dest.c |   77 +++++++++++++++++---
 libnautilus-private/nautilus-tree-view-drag-dest.h |    7 ++
 src/file-manager/fm-directory-view.c               |   51 +++++++++++++-
 src/file-manager/fm-directory-view.h               |    7 ++
 src/file-manager/fm-icon-view.c                    |   11 +++
 src/file-manager/fm-list-view.c                    |   11 +++
 14 files changed, 241 insertions(+), 37 deletions(-)

diff --git nautilus-2.26.2.orig/libnautilus-private/nautilus-dnd.h nautilus-2.26.2/libnautilus-private/nautilus-dnd.h
index 353a1f8..633054c 100644
--- nautilus-2.26.2.orig/libnautilus-private/nautilus-dnd.h
+++ nautilus-2.26.2/libnautilus-private/nautilus-dnd.h
@@ -40,6 +40,7 @@
 #define NAUTILUS_ICON_DND_RESET_BACKGROUND_TYPE "x-special/gnome-reset-background"
 #define NAUTILUS_ICON_DND_ROOTWINDOW_DROP_TYPE	"application/x-rootwindow-drop"
 #define NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE	"XdndDirectSave0" /* XDS Protocol Type */
+#define NAUTILUS_ICON_DND_RAW_TYPE	"application/octet-stream"
 
 /* Item of the drag selection list */
 typedef struct {
@@ -60,6 +61,7 @@ typedef enum {
 	NAUTILUS_ICON_DND_TEXT,
 	NAUTILUS_ICON_DND_RESET_BACKGROUND,
 	NAUTILUS_ICON_DND_XDNDDIRECTSAVE,
+	NAUTILUS_ICON_DND_RAW,
 	NAUTILUS_ICON_DND_ROOTWINDOW_DROP
 } NautilusIconDndTargetType;
 
diff --git nautilus-2.26.2.orig/libnautilus-private/nautilus-file-dnd.c nautilus-2.26.2/libnautilus-private/nautilus-file-dnd.c
index 5502857..6901b83 100644
--- nautilus-2.26.2.orig/libnautilus-private/nautilus-file-dnd.c
+++ nautilus-2.26.2/libnautilus-private/nautilus-file-dnd.c
@@ -122,6 +122,7 @@ nautilus_drag_can_accept_info (NautilusFile *drop_target_item,
 			return nautilus_drag_can_accept_files (drop_target_item);
 
 		case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
+		case NAUTILUS_ICON_DND_RAW:
 			return nautilus_drag_can_accept_files (drop_target_item); /* Check if we can accept files at this location */
 
 		case NAUTILUS_ICON_DND_KEYWORD:
diff --git nautilus-2.26.2.orig/libnautilus-private/nautilus-file-operations.c nautilus-2.26.2/libnautilus-private/nautilus-file-operations.c
index d1a92f1..94557cb 100644
--- nautilus-2.26.2.orig/libnautilus-private/nautilus-file-operations.c
+++ nautilus-2.26.2/libnautilus-private/nautilus-file-operations.c
@@ -115,7 +115,7 @@ typedef struct {
 	char *filename;
 	gboolean make_dir;
 	GFile *src;
-	char *src_data;
+	GString *src_data;
 	GdkPoint position;
 	gboolean has_position;
 	GFile *created_file;
@@ -5582,7 +5582,7 @@ create_job_done (gpointer user_data)
 	if (job->src) {
 		g_object_unref (job->src);
 	}
-	g_free (job->src_data);
+	g_string_free (job->src_data, TRUE);
 	g_free (job->filename);
 	if (job->created_file) {
 		g_object_unref (job->created_file);
@@ -5610,7 +5610,7 @@ create_job (GIOSchedulerJob *io_job,
 	gboolean filename_is_utf8;
 	char *primary, *secondary, *details;
 	int response;
-	char *data;
+	GString *data;
 	GFileOutputStream *out;
 	gboolean handled_invalid_filename;
 	int max_length;
@@ -5683,9 +5683,10 @@ create_job (GIOSchedulerJob *io_job,
 					   NULL, NULL,
 					   &error);
 		} else {
-			data = "";
 			if (job->src_data) {
 				data = job->src_data;
+			} else {
+				data = g_string_new("");
 			}
 
 			out = g_file_create (dest,
@@ -5694,7 +5695,7 @@ create_job (GIOSchedulerJob *io_job,
 					     &error);
 			if (out) {
 				res = g_output_stream_write_all (G_OUTPUT_STREAM (out),
-								 data, strlen (data),
+								 data->str, data->len,
 								 NULL,
 								 common->cancellable,
 								 &error);
@@ -5916,7 +5917,7 @@ nautilus_file_operations_new_file (GtkWidget *parent_view,
 				   GdkPoint *target_point,
 				   const char *parent_dir,
 				   const char *target_filename,
-				   const char *initial_contents,
+				   GString *initial_contents,
 				   NautilusCreateCallback done_callback,
 				   gpointer done_callback_data)
 {
@@ -5936,7 +5937,8 @@ nautilus_file_operations_new_file (GtkWidget *parent_view,
 		job->position = *target_point;
 		job->has_position = TRUE;
 	}
-	job->src_data = g_strdup (initial_contents);
+	job->src_data = g_string_new_len(initial_contents->str,
+	                                 initial_contents->len);
 	job->filename = g_strdup (target_filename);
 
 	g_io_scheduler_push_job (create_job,
diff --git nautilus-2.26.2.orig/libnautilus-private/nautilus-file-operations.h nautilus-2.26.2/libnautilus-private/nautilus-file-operations.h
index 9d1410e..29b1579 100644
--- nautilus-2.26.2.orig/libnautilus-private/nautilus-file-operations.h
+++ nautilus-2.26.2/libnautilus-private/nautilus-file-operations.h
@@ -60,7 +60,7 @@ void nautilus_file_operations_new_file    (GtkWidget                 *parent_vie
 					   GdkPoint                  *target_point,
 					   const char                *parent_dir,
 					   const char                *target_filename,
-					   const char                *initial_contents,
+					   GString                   *initial_contents,
 					   NautilusCreateCallback     done_callback,
 					   gpointer                   data);
 void nautilus_file_operations_new_file_from_template (GtkWidget               *parent_view,
diff --git nautilus-2.26.2.orig/libnautilus-private/nautilus-icon-container.c nautilus-2.26.2/libnautilus-private/nautilus-icon-container.c
index c66c511..95f9705 100644
--- nautilus-2.26.2.orig/libnautilus-private/nautilus-icon-container.c
+++ nautilus-2.26.2/libnautilus-private/nautilus-icon-container.c
@@ -269,6 +269,7 @@ enum {
 	HANDLE_NETSCAPE_URL,
 	HANDLE_URI_LIST,
 	HANDLE_TEXT,
+	HANDLE_RAW,
 	PREVIEW,
 	SELECTION_CHANGED,
 	ICON_ADDED,
@@ -5836,6 +5837,21 @@ nautilus_icon_container_class_init (NautilusIconContainerClass *class)
 				GDK_TYPE_DRAG_ACTION,
 				G_TYPE_INT,
 				G_TYPE_INT);
+	signals[HANDLE_RAW]
+		= g_signal_new ("handle_raw",
+		                G_TYPE_FROM_CLASS (class),
+		                G_SIGNAL_RUN_LAST,
+		                G_STRUCT_OFFSET (NautilusIconContainerClass,
+						 handle_raw),
+		                NULL, NULL,
+		                nautilus_marshal_VOID__POINTER_STRING_STRING_ENUM_INT_INT,
+		                G_TYPE_NONE, 6,
+				G_TYPE_GSTRING,
+				G_TYPE_STRING,
+				G_TYPE_STRING,
+				GDK_TYPE_DRAG_ACTION,
+				G_TYPE_INT,
+				G_TYPE_INT);
 	signals[GET_CONTAINER_URI] 
 		= g_signal_new ("get_container_uri",
 		                G_TYPE_FROM_CLASS (class),
diff --git nautilus-2.26.2.orig/libnautilus-private/nautilus-icon-container.h nautilus-2.26.2/libnautilus-private/nautilus-icon-container.h
index 55b5a28..6745e00 100644
--- nautilus-2.26.2.orig/libnautilus-private/nautilus-icon-container.h
+++ nautilus-2.26.2/libnautilus-private/nautilus-icon-container.h
@@ -120,6 +120,13 @@ typedef struct {
 						   GdkDragAction action,
 						   int x,
 						   int y);
+	void	     (* handle_raw)		  (NautilusIconContainer *container,
+						   GString *raw_data,
+						   const char *target_uri,
+						   const char *direct_save_uri,
+						   GdkDragAction action,
+						   int x,
+						   int y);
 
 	/* Queries on the container for subclass/client.
 	 * These must be implemented. The default "do nothing" is not good enough.
diff --git nautilus-2.26.2.orig/libnautilus-private/nautilus-icon-dnd.c nautilus-2.26.2/libnautilus-private/nautilus-icon-dnd.c
index e2ad56c..0bab4ba 100644
--- nautilus-2.26.2.orig/libnautilus-private/nautilus-icon-dnd.c
+++ nautilus-2.26.2/libnautilus-private/nautilus-icon-dnd.c
@@ -75,6 +75,7 @@ static const GtkTargetEntry drop_types [] = {
 	{ NAUTILUS_ICON_DND_KEYWORD_TYPE, 0, NAUTILUS_ICON_DND_KEYWORD },
 	{ NAUTILUS_ICON_DND_RESET_BACKGROUND_TYPE,  0, NAUTILUS_ICON_DND_RESET_BACKGROUND },
 	{ NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, 0, NAUTILUS_ICON_DND_XDNDDIRECTSAVE }, /* XDS Protocol Type */
+	{ NAUTILUS_ICON_DND_RAW_TYPE, 0, NAUTILUS_ICON_DND_RAW },
 	/* Must be last: */
 	{ NAUTILUS_ICON_DND_ROOTWINDOW_DROP_TYPE,  0, NAUTILUS_ICON_DND_ROOTWINDOW_DROP }
 };
@@ -383,7 +384,7 @@ get_direct_save_filename (GdkDragContext *context)
 			       &prop_len, &prop_text) && prop_text != NULL) {
 		return NULL;
 	}
-	
+
 	/* Zero-terminate the string */
 	prop_text = g_realloc (prop_text, prop_len + 1);
 	prop_text[prop_len] = '\0';
@@ -773,6 +774,28 @@ receive_dropped_text (NautilusIconContainer *container, const char *text, GdkDra
 	g_free (drop_target);
 }
 
+/* handle dropped raw data */
+static void
+receive_dropped_raw (NautilusIconContainer *container, GString *raw_data, const char *direct_save_uri, GdkDragContext *context, int x, int y)
+{
+	char *drop_target;
+
+	if (raw_data == NULL) {
+		return;
+	}
+
+	drop_target = nautilus_icon_container_find_drop_target (container, context, x, y, NULL, TRUE);
+
+	g_signal_emit_by_name (container, "handle_raw",
+			       raw_data,
+			       drop_target,
+			       direct_save_uri,
+			       context->action,
+			       x, y);
+
+	g_free (drop_target);
+}
+
 static int
 auto_scroll_timeout_callback (gpointer data)
 {
@@ -1304,6 +1327,7 @@ nautilus_icon_container_get_drop_action (NautilusIconContainer *container,
 
 	case NAUTILUS_ICON_DND_TEXT:
 	case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
+	case NAUTILUS_ICON_DND_RAW:
 		*action = GDK_ACTION_COPY;
 		break;
 	}
@@ -1699,6 +1723,7 @@ drag_data_received_callback (GtkWidget *widget,
     	NautilusDragInfo *drag_info;
 	EelBackground *background;
 	char *tmp;
+	GString *raw_data;
 	gboolean success;
 
 	drag_info = &(NAUTILUS_ICON_CONTAINER (widget)->details->dnd_info->drag_info);
@@ -1717,6 +1742,7 @@ drag_data_received_callback (GtkWidget *widget,
 	case NAUTILUS_ICON_DND_TEXT:
 	case NAUTILUS_ICON_DND_RESET_BACKGROUND:
 	case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
+	case NAUTILUS_ICON_DND_RAW:
 		/* Save the data so we can do the actual work on drop. */
 		if (drag_info->selection_data != NULL) {
 			gtk_selection_data_free (drag_info->selection_data);
@@ -1786,6 +1812,15 @@ drag_data_received_callback (GtkWidget *widget,
 			success = TRUE;
 			g_free (tmp);
 			break;
+		case NAUTILUS_ICON_DND_RAW:
+			raw_data = g_string_new_len(data->data, data->length);
+			receive_dropped_raw
+				(NAUTILUS_ICON_CONTAINER (widget),
+				 raw_data, drag_info->direct_save_uri,
+				 context, x, y);
+			success = TRUE;
+			g_string_free(raw_data, TRUE);
+			break;
 		case NAUTILUS_ICON_DND_RESET_BACKGROUND:
 			background = eel_get_widget_background (widget);
 			if (background != NULL) {
@@ -1797,18 +1832,18 @@ drag_data_received_callback (GtkWidget *widget,
 			/* Do nothing, everything is done by the sender */
 			break;
 		case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
-			/* Indicate that we don't provide "F" fallback */
-          		if (drag_info->selection_data->format == 8 &&
+			if (drag_info->selection_data->format == 8 &&
 			    drag_info->selection_data->length == 1 &&
 			    drag_info->selection_data->data[0] == 'F') {
-	              		gdk_property_change (GDK_DRAWABLE (context->source_window),
-						     gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE),
-						     gdk_atom_intern ("text/plain", FALSE), 8,
-						     GDK_PROP_MODE_REPLACE, (const guchar *) "", 0);
-            		} else if (drag_info->selection_data->format == 8 &&
-				   drag_info->selection_data->length == 1 &&
-				   drag_info->selection_data->data[0] == 'S' &&
-				   drag_info->direct_save_uri != NULL) {
+				gtk_drag_get_data(widget, context,
+				                  gdk_atom_intern(NAUTILUS_ICON_DND_RAW_TYPE,
+				                                  FALSE),
+				                  time);
+				return;
+			} else if (drag_info->selection_data->format == 8 &&
+			           drag_info->selection_data->length == 1 &&
+			           drag_info->selection_data->data[0] == 'S' &&
+			           drag_info->direct_save_uri != NULL) {
 				GdkPoint p;
 				GFile *location;
 
@@ -1816,13 +1851,15 @@ drag_data_received_callback (GtkWidget *widget,
 
 				nautilus_file_changes_queue_file_added (location);
 				p.x = x; p.y = y;
-				nautilus_file_changes_queue_schedule_position_set (location,
-										   p,
-										   gdk_screen_get_number (gtk_widget_get_screen (widget)));
+				nautilus_file_changes_queue_schedule_position_set (
+				                 location,
+				                 p,
+				                 gdk_screen_get_number (
+				                             gtk_widget_get_screen (widget)));
 				g_object_unref (location);
 				nautilus_file_changes_consume_changes (TRUE);
-            		}
-		        success = TRUE;
+				success = TRUE;
+			}
 			break;
 		}
 		gtk_drag_finish (context, success, FALSE, time);
diff --git nautilus-2.26.2.orig/libnautilus-private/nautilus-marshal.list nautilus-2.26.2/libnautilus-private/nautilus-marshal.list
index 0279047..55dd139 100644
--- nautilus-2.26.2.orig/libnautilus-private/nautilus-marshal.list
+++ nautilus-2.26.2/libnautilus-private/nautilus-marshal.list
@@ -19,3 +19,4 @@ VOID:POINTER,STRING,ENUM,INT,INT
 VOID:STRING,STRING,ENUM,INT,INT
 VOID:STRING,ENUM,INT,INT
 VOID:STRING,STRING
+VOID:POINTER,STRING,STRING,ENUM,INT,INT
diff --git nautilus-2.26.2.orig/libnautilus-private/nautilus-tree-view-drag-dest.c nautilus-2.26.2/libnautilus-private/nautilus-tree-view-drag-dest.c
index e747268..c068f00 100644
--- nautilus-2.26.2.orig/libnautilus-private/nautilus-tree-view-drag-dest.c
+++ nautilus-2.26.2/libnautilus-private/nautilus-tree-view-drag-dest.c
@@ -70,6 +70,7 @@ enum {
 	HANDLE_NETSCAPE_URL,
 	HANDLE_URI_LIST,
 	HANDLE_TEXT,
+	HANDLE_RAW,
 	LAST_SIGNAL
 };
 
@@ -89,6 +90,7 @@ static const GtkTargetEntry drag_types [] = {
 	{ NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
 	{ NAUTILUS_ICON_DND_KEYWORD_TYPE, 0, NAUTILUS_ICON_DND_KEYWORD },
 	{ NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, 0, NAUTILUS_ICON_DND_XDNDDIRECTSAVE }, /* XDS Protocol Type */
+	{ NAUTILUS_ICON_DND_RAW_TYPE, 0, NAUTILUS_ICON_DND_RAW }
 };
 
 
@@ -424,6 +426,7 @@ get_drop_action (NautilusTreeViewDragDest *dest,
 		return context->suggested_action;
 
 	case NAUTILUS_ICON_DND_TEXT:
+	case NAUTILUS_ICON_DND_RAW:
 	case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
 		return GDK_ACTION_COPY;
 
@@ -685,6 +688,29 @@ receive_dropped_text (NautilusTreeViewDragDest *dest,
 	g_free (drop_target);
 }
 
+static void
+receive_dropped_raw (NautilusTreeViewDragDest *dest,
+		      GString *raw_data,
+		      GdkDragContext *context,
+		      int x, int y)
+{
+	char *drop_target;
+
+	if (!dest->details->drag_data) {
+		return;
+	}
+
+	drop_target = get_drop_target_uri_at_pos (dest, x, y);
+	g_assert (drop_target != NULL);
+
+	g_signal_emit (dest, signals[HANDLE_RAW], 0,
+		       raw_data, drop_target,
+		       dest->details->direct_save_uri,
+		       context->action,
+		       x, y);
+
+	g_free (drop_target);
+}
 
 static void
 receive_dropped_netscape_url (NautilusTreeViewDragDest *dest,
@@ -735,21 +761,23 @@ receive_dropped_keyword (NautilusTreeViewDragDest *dest,
 	g_free (drop_target_uri);
 }
 
-static void
+static gboolean
 receive_xds (NautilusTreeViewDragDest *dest,
+	     GtkWidget *widget,
+	     guint32 time,
 	     GdkDragContext *context,
 	     int x, int y)
 {
 	GFile *location;
 
-	/* Indicate that we don't provide "F" fallback */
 	if (dest->details->drag_data->format == 8 
 	    && dest->details->drag_data->length == 1 
 	    && dest->details->drag_data->data[0] == 'F') {
-		gdk_property_change (GDK_DRAWABLE (context->source_window),
-				     gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE),
-				     gdk_atom_intern ("text/plain", FALSE), 8,
-				     GDK_PROP_MODE_REPLACE, (const guchar *) "", 0);
+		gtk_drag_get_data(widget, context,
+		                  gdk_atom_intern(NAUTILUS_ICON_DND_RAW_TYPE,
+		                                  FALSE),
+		                  time);
+		return FALSE;
 	} else if (dest->details->drag_data->format == 8 
 		   && dest->details->drag_data->length == 1 
 		   && dest->details->drag_data->data[0] == 'S') {
@@ -760,6 +788,7 @@ receive_xds (NautilusTreeViewDragDest *dest,
 		nautilus_file_changes_consume_changes (TRUE);
 
 		g_object_unref (location);
+		return TRUE;
 	}
 }
 
@@ -775,7 +804,8 @@ drag_data_received_callback (GtkWidget *widget,
 			     gpointer data)
 {
 	NautilusTreeViewDragDest *dest;
-	gboolean success;
+	gboolean success, finished;
+	GString *raw_data;
 	
 	dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data);
 
@@ -792,6 +822,7 @@ drag_data_received_callback (GtkWidget *widget,
 
 	if (dest->details->drop_occurred) {
 		success = FALSE;
+		finished = TRUE;
 		switch (info) {
 		case NAUTILUS_ICON_DND_GNOME_ICON_LIST :
 			receive_dropped_icons (dest, context, x, y);
@@ -809,19 +840,28 @@ drag_data_received_callback (GtkWidget *widget,
 			receive_dropped_text (dest, context, x, y);
 			success = TRUE;
 			break;
+		case NAUTILUS_ICON_DND_RAW:
+			raw_data = g_string_new_len(selection_data->data,
+			                            selection_data->length);
+			receive_dropped_raw (dest, raw_data, context, x, y);
+			success = TRUE;
+			g_string_free(raw_data, TRUE);
+			break;
 		case NAUTILUS_ICON_DND_KEYWORD:
 			receive_dropped_keyword (dest, context, x, y);
 			success = TRUE;
 			break;
 		case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
-			receive_xds (dest, context, x, y);
+			finished = receive_xds (dest, widget, time, context, x, y);
 			success = TRUE;
 			break;
 		}
 
-		dest->details->drop_occurred = FALSE;
-		free_drag_data (dest);
-		gtk_drag_finish (context, success, FALSE, time);
+		if (finished) {
+			dest->details->drop_occurred = FALSE;
+			free_drag_data (dest);
+			gtk_drag_finish (context, success, FALSE, time);
+		}
 	}
 
 	/* appease GtkTreeView by preventing its drag_data_receive
@@ -1087,6 +1127,21 @@ nautilus_tree_view_drag_dest_class_init (NautilusTreeViewDragDestClass *class)
 			      GDK_TYPE_DRAG_ACTION,
 			      G_TYPE_INT,
 			      G_TYPE_INT);
+	signals[HANDLE_RAW] =
+		g_signal_new ("handle_raw",
+			      G_TYPE_FROM_CLASS (class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (NautilusTreeViewDragDestClass,
+					       handle_raw),
+			      NULL, NULL,
+			      nautilus_marshal_VOID__POINTER_STRING_STRING_ENUM_INT_INT,
+			      G_TYPE_NONE, 6,
+			      G_TYPE_GSTRING,
+			      G_TYPE_STRING,
+			      G_TYPE_STRING,
+			      GDK_TYPE_DRAG_ACTION,
+			      G_TYPE_INT,
+			      G_TYPE_INT);
 }
 
 
diff --git nautilus-2.26.2.orig/libnautilus-private/nautilus-tree-view-drag-dest.h nautilus-2.26.2/libnautilus-private/nautilus-tree-view-drag-dest.h
index 71944fc..a8cdab4 100644
--- nautilus-2.26.2.orig/libnautilus-private/nautilus-tree-view-drag-dest.h
+++ nautilus-2.26.2/libnautilus-private/nautilus-tree-view-drag-dest.h
@@ -82,6 +82,13 @@ struct _NautilusTreeViewDragDestClass {
 				  GdkDragAction action,
 				  int x,
 				  int y);
+	void (* handle_raw)    (NautilusTreeViewDragDest *dest,
+				  GString *raw_data,
+				  const char *target_uri,
+				  const char *direct_save_uri,
+				  GdkDragAction action,
+				  int x,
+				  int y);
 };
 
 GType                     nautilus_tree_view_drag_dest_get_type (void);
diff --git nautilus-2.26.2.orig/src/file-manager/fm-directory-view.c nautilus-2.26.2/src/file-manager/fm-directory-view.c
index 21171cc..1f464b0 100644
--- nautilus-2.26.2.orig/src/file-manager/fm-directory-view.c
+++ nautilus-2.26.2/src/file-manager/fm-directory-view.c
@@ -4010,7 +4010,7 @@ static void
 fm_directory_view_new_file_with_initial_contents (FMDirectoryView *directory_view,
 						  const char *parent_uri,
 						  const char *filename,
-						  const char *initial_contents)
+						  GString *initial_contents)
 {
 	GdkPoint *pos;
 	NewFolderData *data;
@@ -9584,6 +9584,7 @@ fm_directory_view_handle_text_drop (FMDirectoryView  *view,
 				    int               y)
 {
 	char *container_uri;
+	GString *initial_text;
 
 	if (text == NULL) {
 		return;
@@ -9597,11 +9598,57 @@ fm_directory_view_handle_text_drop (FMDirectoryView  *view,
 		g_assert (container_uri != NULL);
 	}
 
+	initial_text = g_string_new(text);
+
 	fm_directory_view_new_file_with_initial_contents (
 		view, target_uri != NULL ? target_uri : container_uri,
 		/* Translator: This is the filename used for when you dnd text to a directory */
 		_("dropped text.txt"),
-		text);
+		initial_text);
+
+	g_free (container_uri);
+	g_string_free(initial_text, TRUE);
+}
+
+void
+fm_directory_view_handle_raw_drop (FMDirectoryView  *view,
+				    GString          *raw_data,
+				    const char       *target_uri,
+				    const char       *direct_save_uri,
+				    GdkDragAction     action,
+				    int               x,
+				    int               y)
+{
+	char *container_uri, *filename;
+	GFile *direct_save_full;
+
+	if (raw_data == NULL) {
+		return;
+	}
+
+	g_return_if_fail (action == GDK_ACTION_COPY);
+
+	container_uri = NULL;
+	if (target_uri == NULL) {
+		container_uri = fm_directory_view_get_backing_uri (view);
+		g_assert (container_uri != NULL);
+	}
+
+	filename = NULL;
+	if (direct_save_uri != NULL) {
+		direct_save_full = g_file_new_for_uri(direct_save_uri);
+		filename = g_file_get_basename(direct_save_full);
+	}
+	if (filename == NULL) {
+		/* Translator: This is the filename used for when you dnd raw
+		 * data to a directory, if the source didn't supply a name.
+		 */
+		filename = _("dropped data");
+	}
+
+	fm_directory_view_new_file_with_initial_contents (
+		view, target_uri != NULL ? target_uri : container_uri,
+		filename, raw_data);
 
 	g_free (container_uri);
 }
diff --git nautilus-2.26.2.orig/src/file-manager/fm-directory-view.h nautilus-2.26.2/src/file-manager/fm-directory-view.h
index 3dcba25..b37de3f 100644
--- nautilus-2.26.2.orig/src/file-manager/fm-directory-view.h
+++ nautilus-2.26.2/src/file-manager/fm-directory-view.h
@@ -459,6 +459,13 @@ void                fm_directory_view_handle_text_drop                 (FMDirect
 									GdkDragAction     action,
 									int               x,
 									int               y);
+void                fm_directory_view_handle_raw_drop                 (FMDirectoryView  *view,
+									GString          *raw_data,
+									const char       *target_uri,
+									const char       *direct_save_uri,
+									GdkDragAction     action,
+									int               x,
+									int               y);
 void                fm_directory_view_freeze_updates                   (FMDirectoryView  *view);
 void                fm_directory_view_unfreeze_updates                 (FMDirectoryView  *view);
 void                fm_directory_view_add_subdirectory                (FMDirectoryView  *view,
diff --git nautilus-2.26.2.orig/src/file-manager/fm-icon-view.c nautilus-2.26.2/src/file-manager/fm-icon-view.c
index ee2d99e..96a4e10 100644
--- nautilus-2.26.2.orig/src/file-manager/fm-icon-view.c
+++ nautilus-2.26.2/src/file-manager/fm-icon-view.c
@@ -2749,6 +2749,15 @@ icon_view_handle_text (NautilusIconContainer *container, const char *text,
 					    text, target_uri, action, x, y);
 }
 
+static void
+icon_view_handle_raw (NautilusIconContainer *container, GString *raw_data,
+		       const char *target_uri, const char *direct_save_uri,
+		       GdkDragAction action, int x, int y, FMIconView *view)
+{
+	fm_directory_view_handle_raw_drop (FM_DIRECTORY_VIEW (view),
+					    raw_data, target_uri, direct_save_uri, action, x, y);
+}
+
 static char *
 icon_view_get_first_visible_file (NautilusView *view)
 {
@@ -2981,6 +2990,8 @@ fm_icon_view_init (FMIconView *icon_view)
 				 G_CALLBACK (icon_view_handle_uri_list), icon_view, 0);
 	g_signal_connect_object (get_icon_container (icon_view), "handle_text",
 				 G_CALLBACK (icon_view_handle_text), icon_view, 0);
+	g_signal_connect_object (get_icon_container (icon_view), "handle_raw",
+				 G_CALLBACK (icon_view_handle_raw), icon_view, 0);
 }
 
 static NautilusView *
diff --git nautilus-2.26.2.orig/src/file-manager/fm-list-view.c nautilus-2.26.2/src/file-manager/fm-list-view.c
index 5b3bd68..c2c3e23 100644
--- nautilus-2.26.2.orig/src/file-manager/fm-list-view.c
+++ nautilus-2.26.2/src/file-manager/fm-list-view.c
@@ -1190,6 +1190,15 @@ list_view_handle_text (NautilusTreeViewDragDest *dest, const char *text,
 }
 
 static void
+list_view_handle_raw (NautilusTreeViewDragDest *dest, GString *raw_data,
+		       const char *target_uri, const char *direct_save_uri,
+		       GdkDragAction action, int x, int y, FMListView *view)
+{
+	fm_directory_view_handle_raw_drop (FM_DIRECTORY_VIEW (view),
+					    raw_data, target_uri, direct_save_uri, action, x, y);
+}
+
+static void
 move_copy_items_callback (NautilusTreeViewDragDest *dest,
 			  const GList *item_uris,
 			  const char *target_uri,
@@ -1371,6 +1380,8 @@ create_and_set_up_tree_view (FMListView *view)
 				 G_CALLBACK (list_view_handle_uri_list), view, 0);
 	g_signal_connect_object (view->details->drag_dest, "handle_text",
 				 G_CALLBACK (list_view_handle_text), view, 0);
+	g_signal_connect_object (view->details->drag_dest, "handle_raw",
+				 G_CALLBACK (list_view_handle_raw), view, 0);
 
 	g_signal_connect_object (gtk_tree_view_get_selection (view->details->tree_view),
 				 "changed",
-- 
1.6.3.1

#!/usr/bin/env python

import sys
import gtk
import gtk.gdk

class DNDTest:
    TARGET_TYPE_XDS = 42
    TARGET_TYPE_OCTET = 43
    XDS_ATOM = gtk.gdk.atom_intern("XdndDirectSave0")
    TEXT_ATOM = gtk.gdk.atom_intern("text/plain")
    XDS_FAILURE = "F"

    def __init__(self):
        # Main window
        window = gtk.Window()
        window.connect("destroy", self.quit)

        # VBox to pack the two buttons into
        vbox = gtk.VBox(False, 5)
        vbox.set_border_width(5)
        window.add(vbox)
        vbox.show()

        # First button
        # When this button is dragged and dropped on a file manager,
        # a file named 'tar' should appear in the drop location, and
        # it should be identical to the file '/bin/tar' on the source.
        dnd_xds = gtk.Button("Drag Me to Test XDS")
        vbox.pack_start(dnd_xds, False, False, 0)
        dnd_xds_targets = [("XdndDirectSave0", 0, self.TARGET_TYPE_XDS),
                   ("application/octet-stream", 0, self.TARGET_TYPE_OCTET)]
        dnd_xds.drag_source_set(gtk.gdk.BUTTON1_MASK,
                                dnd_xds_targets,
                                gtk.gdk.ACTION_COPY)
        dnd_xds.connect("drag-begin", self.on_drag_begin)
        dnd_xds.connect("drag-data-get", self.on_drag_data_get)
        dnd_xds.connect("drag-end", self.on_drag_end)
        dnd_xds.show()

        # Second button
        # When this button is dragged and dropped on a file manager,
        # a file should appear in the drop location, and it should be
        # identical to the file '/bin/tar' on the source. The file
        # manager will have to choose a name, since none was supplied.
        dnd_raw = gtk.Button("Drag Me to Test XDND of Raw Data")
        vbox.pack_start(dnd_raw, False, False, 0)
        dnd_raw_targets = [("application/octet-stream", 0, self.TARGET_TYPE_OCTET)]
        dnd_raw.drag_source_set(gtk.gdk.BUTTON1_MASK,
                                dnd_raw_targets,
                                gtk.gdk.ACTION_COPY)
        dnd_raw.connect("drag-data-get", self.on_drag_data_get)
        dnd_raw.show()

        window.show()

    def on_drag_begin(self, widget, context):
        # When doing an XDS DND, this sets the filename to use
        # in the destination.
        # I have hard-coded it to be 'tar' for testing purposes.
        filename = "tar"
        context.source_window.property_change(self.XDS_ATOM, self.TEXT_ATOM, 8,
                                              gtk.gdk.PROP_MODE_REPLACE,
                                              filename)

    def on_drag_data_get(self, widget, context, selection, info, time):
        if info == self.TARGET_TYPE_XDS:
            # The destination has requested that we save the file ourselves.
            # We won't even try to do this, since this program might be
            # running on a different machine than the one running the file
            # manager. When the file manager gets this response, it should
            # request the raw data from us--type application/octet-stream--
            # and it will take care of saving the file for us.
            selection.set(selection.target, 8, self.XDS_FAILURE)
        elif info == self.TARGET_TYPE_OCTET:
            # This is the raw data to send to the destination. It could be
            # an image or a binary executable or anything. It could contain
            # embedded nul bytes. The destination must be prepared to deal
            # with this binary data.
            # I have hard-coded '/bin/tar' as the file to send, since I
            # suppose that everyone has that file.
            f = open('/bin/tar', 'r')
            selection.set(selection.target, 8, f.read())

    def on_drag_end(self, widget, context):
        # Clear the XDS property.
        context.source_window.property_delete(self.XDS_ATOM)

    def quit(self, unused):
        sys.exit(0)

if __name__ == '__main__':
    dnd = DNDTest()
    gtk.main()

Reply via email to