diff --git a/shell/rb-shell-clipboard.c b/shell/rb-shell-clipboard.c
index 20201e6..7cb4d32 100644
--- a/shell/rb-shell-clipboard.c
+++ b/shell/rb-shell-clipboard.c
@@ -88,6 +88,13 @@ static void rb_shell_clipboard_cmd_move_to_trash (GtkAction *action,
 						  RBShellClipboard *clipboard);
 static void rb_shell_clipboard_set (RBShellClipboard *clipboard,
 			            GList *nodes);
+static void rb_shell_gtk_clipboard_set (RBShellClipboard *clipboard);
+static void rb_shell_get_clipboard_callback (GtkClipboard *clipboard,
+						GtkSelectionData *selection_data,
+						guint    info,
+						gpointer user_data);
+static void rb_shell_clear_clipboard_callback (GtkClipboard  *clipboard,
+						gpointer          user_data);
 static void rb_shell_clipboard_playlist_added_cb (RBPlaylistManager *mgr,
 						  RBPlaylistSource *source,
 						  RBShellClipboard *clipboard);
@@ -131,6 +138,43 @@ struct RBShellClipboardPrivate
 	guint idle_sync_id, idle_playlist_id;
 
 	GList *entries;
+
+	GtkTargetEntry *target_table;
+	gint n_targets;
+
+        /* 
+         * When GTK's clipboard changes, rhythmbox's "clipboard clear" callback
+         * will be called, so that we can do any cleanup of our clipboard data.
+         * What we will do is empty the "entries" list.  If we did not clear
+         * that list, then even though the GTK clipboard changed, the internal
+         * clipboard will not change, and an attempt to paste to a playlist
+         * will be successful.  This is not what we would expect if we changed
+         * clipboard contents.
+         * So, we clear the GList *entries when we get the "clear" callback.
+         *
+         * Now, consider this situation:  Rhythmbox data was already on the
+         * clipboard, and we are copying different rhythmbox data onto the
+         * clipboard.
+         * In this case, our cmd_copy will run, replacing GList *entries with
+         * the newly-desired data.  It is only AFTER this that GTK will call
+         * the clipboard clear callback.
+         * Since our clipboard-clear behavior is to blank GList *entries, this
+         * will have the effect that the clipboard will be empty at the end,
+         * even though we just copied new data on there.
+         *
+         * The problem is that what we want to clear is the "old version" of
+         * the clipboard.  We have already replaced this with the "new
+         * version", and don't need to clear anything.
+         *
+         * To get around this problem, we introduce a "serial number" to the
+         * clipboard.  When we put new data onto the clipboard, we increment
+         * the serial number.  We register the clear callback using this
+         * number.  Therefore, when the clear callback comes, we will know what
+         * "version" we are being asked to clear.  If we are being asked to
+         * clear an "old version" of the data, we can ignore this request,
+         * since the data has already been replaced.
+         */
+	guint serial;
 };
 
 #define RB_SHELL_CLIPBOARD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SHELL_CLIPBOARD, RBShellClipboardPrivate))
@@ -207,6 +251,60 @@ static guint num_playlist_menu_paths = G_N_ELEMENTS (playlist_menu_paths);
 
 G_DEFINE_TYPE (RBShellClipboard, rb_shell_clipboard, G_TYPE_OBJECT)
 
+static GdkAtom copied_files_atom;
+
+/*
+ * MIME-types that we will support on the clipboard.
+ */
+#define RB_CLIPBOARD_COPYFILES_TYPE  "x-special/gnome-copied-files"
+
+/*
+ * Internal-use numbers that we will use to refer to the types we will support
+ * on the clipboard.
+ *
+ * RB_CLIPBOARD_COPYFILES is the x-special/gnome-copied-files data that
+ * Nautilus and other file managers expect.  The format of this data is:
+ *   copy\n
+ *   file:///url\n
+ *   file:///url\n
+ *   [etc...]
+ * The first line is either "copy" or "cut", depending on what we want the file
+ * manager to do (copy or move the files).
+ *
+ * (note:  I inferred this format by reading nautilus's function
+ * convert_file_list_to_string, in this file:
+ * http://git.gnome.org/browse/nautilus/tree/libnautilus-private/nautilus-clipboard-monitor.c )
+ *
+ * RB_CLIPBOARD_URI_LIST will be a list of uris.  Essentially the same as
+ * RB_CLIPBOARD_COPYFILES, but it doesn't have the "copy/cut" line.
+ * Note, we don't put this into the fileselection_types array by hand.
+ * Instead, we make a GList* out of that array and then call
+ * gtk_target_list_add_uri_targets.
+ *
+ * RB_CLIPBOARD_TEXT will be a list of filenames rather than URIs, separated by
+ * \n.  Note that, like RB_CLIPBOARD_URI_LIST, we don't put this into the
+ * fileselection_types array, but instead add it with
+ * gtk_target_list_add_text_targets.  This is because there are many different
+ * MIME-types that GTK considers to be text-type, and should all work the same.
+ * Let the GTK library maintain that list of MIME-types.
+ */
+typedef enum {
+	RB_CLIPBOARD_COPYFILES,
+	RB_CLIPBOARD_URI_LIST,
+	RB_CLIPBOARD_TEXT,
+} RBClipboardTypes;
+
+static const GtkTargetEntry fileselection_types [] = {
+	{RB_CLIPBOARD_COPYFILES_TYPE, 0, RB_CLIPBOARD_COPYFILES},
+};
+
+typedef struct _RBShellClipboardCallbackArgs
+{
+	RBShellClipboard *rb_clipboard;
+	guint serial;
+} RBShellClipboardCallbackArgs;
+
+
 static void
 rb_shell_clipboard_class_init (RBShellClipboardClass *klass)
 {
@@ -262,17 +360,31 @@ rb_shell_clipboard_class_init (RBShellClipboardClass *klass)
 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
 	g_type_class_add_private (klass, sizeof (RBShellClipboardPrivate));
+
+	copied_files_atom = gdk_atom_intern (RB_CLIPBOARD_COPYFILES_TYPE, FALSE);
 }
 
 static void
 rb_shell_clipboard_init (RBShellClipboard *shell_clipboard)
 {
+	GtkTargetList *target_list = NULL;
+
 	shell_clipboard->priv = RB_SHELL_CLIPBOARD_GET_PRIVATE (shell_clipboard);
 
 	shell_clipboard->priv->signal_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
 								    NULL, g_free);
 
 	shell_clipboard->priv->deleted_queue = g_async_queue_new ();
+
+	/*
+	 * Add uri and text-type clipboard targets.
+	 */
+	target_list = gtk_target_list_new(fileselection_types, G_N_ELEMENTS(fileselection_types));
+	gtk_target_list_add_text_targets(target_list, RB_CLIPBOARD_TEXT);
+	gtk_target_list_add_uri_targets(target_list, RB_CLIPBOARD_URI_LIST);
+	shell_clipboard->priv->target_table = gtk_target_table_new_from_list(target_list, &(shell_clipboard->priv->n_targets));
+
+	gtk_target_list_unref(target_list);
 }
 
 static void
@@ -334,6 +446,8 @@ rb_shell_clipboard_finalize (GObject *object)
 
 	g_return_if_fail (shell_clipboard->priv != NULL);
 
+	gtk_target_table_free(shell_clipboard->priv->target_table, shell_clipboard->priv->n_targets);
+
 	g_hash_table_destroy (shell_clipboard->priv->signal_hash);
 
 	g_list_foreach (shell_clipboard->priv->entries, (GFunc)rhythmdb_entry_unref, NULL);
@@ -735,6 +849,7 @@ rb_shell_clipboard_cmd_copy (GtkAction *action,
 	rb_debug ("copy");
 	rb_shell_clipboard_set (clipboard,
 				rb_source_copy (clipboard->priv->source));
+	rb_shell_gtk_clipboard_set (clipboard);
 }
 
 static void
@@ -779,9 +894,162 @@ rb_shell_clipboard_set (RBShellClipboard *clipboard,
 		g_list_free (clipboard->priv->entries);
 	}
 
+	clipboard->priv->serial++;
 	clipboard->priv->entries = entries;
 }
 
+/**
+ * Set the GtkClipboard up so that these entries can be pasted into
+ * nautilus, or the urls can be pasted as text.
+ */
+static void
+rb_shell_gtk_clipboard_set (RBShellClipboard *clipboard)
+{
+	RBShellClipboardCallbackArgs *callback_data;
+	RBShellClipboardCallbackArgs *callback_data_primary;
+
+	clipboard->priv->serial++;
+
+	callback_data = g_slice_new(RBShellClipboardCallbackArgs);
+	callback_data->rb_clipboard = clipboard;
+	callback_data->serial = clipboard->priv->serial;
+
+	callback_data_primary = g_slice_new(RBShellClipboardCallbackArgs);
+	callback_data_primary->rb_clipboard = clipboard;
+	callback_data_primary->serial = clipboard->priv->serial;
+
+	gtk_clipboard_set_with_data(
+		gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
+		clipboard->priv->target_table,
+		clipboard->priv->n_targets,
+		rb_shell_get_clipboard_callback,
+		rb_shell_clear_clipboard_callback,
+		(gpointer) callback_data
+	);
+
+	gtk_clipboard_set_with_data(
+		gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE)),
+		clipboard->priv->target_table,
+		clipboard->priv->n_targets,
+		rb_shell_get_clipboard_callback,
+		rb_shell_clear_clipboard_callback,
+		(gpointer) callback_data_primary
+	);
+}
+
+/**
+ * Hand off clipboard data to the requestor.  We will support nautilus's
+ * copy-files type.
+ */
+static void 
+rb_shell_get_clipboard_callback (GtkClipboard     *clipboard,
+			GtkSelectionData *selection_data,
+			guint             info,
+			gpointer          user_data)
+{
+	RBShellClipboardCallbackArgs *callback_data = (RBShellClipboardCallbackArgs *)user_data;
+
+	RBShellClipboard *rb_clipboard = callback_data->rb_clipboard;
+	GList *it;
+	GString *str;
+
+	char **uris;
+	int i=0;
+
+	if (!rb_clipboard->priv || !rb_clipboard->priv->entries) {
+		return;
+	}
+
+	switch (info) {
+	case RB_CLIPBOARD_COPYFILES:
+	case RB_CLIPBOARD_TEXT:
+		if (info == RB_CLIPBOARD_COPYFILES) {
+			str = g_string_new("copy");
+		} else {
+			str = g_string_new("");
+		}
+		for (it=rb_clipboard->priv->entries; it; it=g_list_next(it), i++) {
+			const gchar *uri;
+			RhythmDBEntry *entry = (RhythmDBEntry *)it->data;
+
+			uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
+			if (i!=0 || info==RB_CLIPBOARD_COPYFILES) {
+				g_string_append_c (str, '\n');
+			}
+
+			if (info == RB_CLIPBOARD_TEXT) {
+				GFile *file;
+				char *file_path;
+				file = g_file_new_for_uri(uri);
+				file_path = g_file_get_path(file);
+				if (file_path) {
+					g_string_append (str, file_path);
+					g_free(file_path);
+				} else {
+					g_string_append (str, uri);
+				}
+				g_object_unref(file);
+			} else {
+				g_string_append (str, uri);
+			}
+		}
+
+		if (info == RB_CLIPBOARD_COPYFILES) {
+			gtk_selection_data_set (selection_data, copied_files_atom, 8, (guchar *)str->str, str->len);
+		} else if (info == RB_CLIPBOARD_TEXT) {
+			gtk_selection_data_set_text (selection_data, str->str, str->len);
+		}
+
+		g_string_free (str, TRUE);
+
+		break;
+	case RB_CLIPBOARD_URI_LIST:
+		uris = (char **)g_malloc(
+			(g_list_length(rb_clipboard->priv->entries)+1)
+			* sizeof(char *));
+		for (it=rb_clipboard->priv->entries; it; it=g_list_next(it), i++) {
+			const gchar *uri;
+			RhythmDBEntry *entry = (RhythmDBEntry *)it->data;
+
+			uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
+			uris[i] = g_strdup(uri);
+		}
+		uris[i] = NULL;
+		gtk_selection_data_set_uris (selection_data, uris);
+		g_strfreev (uris);
+		break;
+	}
+}
+
+static void 
+rb_shell_clear_clipboard_callback (GtkClipboard  *clipboard,
+			gpointer          user_data)
+{
+	RBShellClipboardCallbackArgs *callback_data = (RBShellClipboardCallbackArgs *)user_data;
+	
+	RBShellClipboard *rb_clipboard = callback_data->rb_clipboard;
+	guint old_serial = callback_data->serial;
+
+	/*
+	 * If it's just the X PRIMARY clipboard, we don't need to clear our
+	 * internal data structure.  We want paste to still keep working until
+	 * the gtk clipboard is replaced.
+	 */
+	if (clipboard != gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE))) {
+		/*
+		 * If we've changed our internal clipboard before the clear
+		 * callback is called, then let's not clear our clipboard
+		 * again.
+		 */
+		if (old_serial == rb_clipboard->priv->serial) {
+			rb_shell_clipboard_set (rb_clipboard, NULL);
+		}
+	}
+
+	g_slice_free(RBShellClipboardCallbackArgs, callback_data);
+}
+
+
 static void
 rb_shell_clipboard_entry_deleted_cb (RhythmDB *db,
 				     RhythmDBEntry *entry,
