summaryrefslogtreecommitdiffstats
path: root/src/nautilus-clipboard.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/nautilus-clipboard.c349
1 files changed, 349 insertions, 0 deletions
diff --git a/src/nautilus-clipboard.c b/src/nautilus-clipboard.c
new file mode 100644
index 0000000..99dc02d
--- /dev/null
+++ b/src/nautilus-clipboard.c
@@ -0,0 +1,349 @@
+/* nautilus-clipboard.c
+ *
+ * Nautilus Clipboard support. For now, routines to support component cut
+ * and paste.
+ *
+ * Copyright (C) 1999, 2000 Free Software Foundaton
+ * Copyright (C) 2000, 2001 Eazel, Inc.
+ * Copyright (C) 2016 Carlos Soriano <csoriano@gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Rebecca Schulman <rebecka@eazel.com>,
+ * Darin Adler <darin@bentspoon.com>
+ */
+
+#include <config.h>
+#include "nautilus-clipboard.h"
+#include "nautilus-file-utilities.h"
+#include "nautilus-file.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+/* The .files member contains elements of type NautilusFile. */
+struct _NautilusClipboard
+{
+ gboolean cut;
+ GList *files;
+};
+
+/* Boxed type used to wrap this struct in a clipboard GValue. */
+G_DEFINE_BOXED_TYPE (NautilusClipboard, nautilus_clipboard,
+ nautilus_clipboard_copy, nautilus_clipboard_free)
+
+static char *
+nautilus_clipboard_to_string (NautilusClipboard *clip)
+{
+ GString *uris;
+ char *uri;
+ guint i;
+ GList *l;
+
+ uris = g_string_new (clip->cut ? "cut" : "copy");
+
+ for (i = 0, l = clip->files; l != NULL; l = l->next, i++)
+ {
+ uri = nautilus_file_get_uri (l->data);
+
+ g_string_append_c (uris, '\n');
+ g_string_append (uris, uri);
+
+ g_free (uri);
+ }
+
+ return g_string_free (uris, FALSE);
+}
+
+static NautilusClipboard *
+nautilus_clipboard_from_string (char *string,
+ GError **error)
+{
+ NautilusClipboard *clip;
+ g_auto (GStrv) lines = NULL;
+ g_autolist (NautilusFile) files = NULL;
+
+ if (string == NULL)
+ {
+ *error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Clipboard string cannot be NULL.");
+ return NULL;
+ }
+
+ lines = g_strsplit (string, "\n", 0);
+
+ if (g_strcmp0 (lines[0], "cut") != 0 && g_strcmp0 (lines[0], "copy") != 0)
+ {
+ /* Translators: Do not translate 'cut' and 'copy'. These are literal keywords. */
+ *error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Nautilus Clipboard must begin with “cut” or “copy”.");
+ return NULL;
+ }
+
+ /* Line 0 is "cut" or "copy", so uris start at line 1. */
+ for (int i = 1; lines[i] != NULL; i++)
+ {
+ if (g_strcmp0 (lines[i], "") == 0)
+ {
+ *error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Nautilus Clipboard must not have empty lines.");
+ return NULL;
+ }
+ else if (!g_uri_is_valid (lines[i], G_URI_FLAGS_NONE, error))
+ {
+ return NULL;
+ }
+ files = g_list_prepend (files, nautilus_file_get_by_uri (lines[i]));
+ }
+
+ clip = g_new0 (NautilusClipboard, 1);
+ files = g_list_reverse (files);
+ clip->files = g_steal_pointer (&files);
+ clip->cut = g_str_equal (lines[0], "cut");
+
+ return clip;
+}
+
+#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION
+void
+nautilus_clipboard_clear_if_colliding_uris (GtkWidget *widget,
+ const GList *item_uris)
+{
+ GtkSelectionData *data;
+ GList *clipboard_item_uris, *l;
+ gboolean collision;
+
+ collision = FALSE;
+ data = gtk_clipboard_wait_for_contents (gtk_widget_get_clipboard (widget),
+ copied_files_atom);
+ if (data == NULL)
+ {
+ return;
+ }
+
+ clipboard_item_uris = nautilus_clipboard_get_uri_list_from_selection_data (data);
+
+ for (l = (GList *) item_uris; l; l = l->next)
+ {
+ if (g_list_find_custom ((GList *) clipboard_item_uris, l->data,
+ (GCompareFunc) g_strcmp0))
+ {
+ collision = TRUE;
+ break;
+ }
+ }
+
+ if (collision)
+ {
+ gtk_clipboard_clear (gtk_widget_get_clipboard (widget));
+ }
+
+ if (clipboard_item_uris)
+ {
+ g_list_free_full (clipboard_item_uris, g_free);
+ }
+}
+#endif
+
+/*
+ * This asumes the implementation of GTK_TYPE_FILE_LIST is a GSList<GFile>.
+ * As of writing this, the API docs don't provide for this assumption.
+ */
+static GSList *
+convert_file_list_to_gdk_file_list (NautilusClipboard *clip)
+{
+ GSList *file_list = NULL;
+ for (GList *l = clip->files; l != NULL; l = l->next)
+ {
+ file_list = g_slist_prepend (file_list,
+ nautilus_file_get_location (l->data));
+ }
+ return g_slist_reverse (file_list);
+}
+
+static void
+nautilus_clipboard_serialize (GdkContentSerializer *serializer)
+{
+ NautilusClipboard *clip;
+ g_autofree gchar *str = NULL;
+ g_autoptr (GError) error = NULL;
+
+ clip = g_value_get_boxed (gdk_content_serializer_get_value (serializer));
+
+ str = nautilus_clipboard_to_string (clip);
+
+ if (g_output_stream_printf (gdk_content_serializer_get_output_stream (serializer),
+ NULL,
+ gdk_content_serializer_get_cancellable (serializer),
+ &error,
+ "%s", str))
+ {
+ gdk_content_serializer_return_success (serializer);
+ }
+ else
+ {
+ gdk_content_serializer_return_error (serializer, error);
+ }
+}
+
+static void
+nautilus_clipboard_deserialize_finish (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GdkContentDeserializer *deserializer = user_data;
+ GOutputStream *output = G_OUTPUT_STREAM (source);
+ GError *error = NULL;
+ g_autofree gchar *string = NULL;
+ g_autoptr (NautilusClipboard) clip = NULL;
+
+ if (g_output_stream_splice_finish (output, result, &error) < 0)
+ {
+ gdk_content_deserializer_return_error (deserializer, error);
+ return;
+ }
+
+ /* write terminating NULL */
+ if (g_output_stream_write (output, "", 1, NULL, &error) < 0 ||
+ !g_output_stream_close (output, NULL, &error))
+ {
+ gdk_content_deserializer_return_error (deserializer, error);
+ return;
+ }
+
+ string = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (output));
+
+ clip = nautilus_clipboard_from_string (string, &error);
+
+ if (clip == NULL)
+ {
+ gdk_content_deserializer_return_error (deserializer, error);
+ return;
+ }
+
+ g_value_set_boxed (gdk_content_deserializer_get_value (deserializer), clip);
+ gdk_content_deserializer_return_success (deserializer);
+}
+
+static void
+nautilus_clipboard_deserialize (GdkContentDeserializer *deserializer)
+{
+ g_autoptr (GOutputStream) output = NULL;
+
+ output = g_memory_output_stream_new_resizable ();
+ g_output_stream_splice_async (output,
+ gdk_content_deserializer_get_input_stream (deserializer),
+ G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
+ gdk_content_deserializer_get_priority (deserializer),
+ gdk_content_deserializer_get_cancellable (deserializer),
+ nautilus_clipboard_deserialize_finish,
+ deserializer);
+}
+
+/**
+ * nautilus_clipboard_peek_files:
+ * @clip: The current local clipboard value.
+ *
+ * Returns: (transfer none): The internal GList of GFile objects.
+ */
+GList *
+nautilus_clipboard_peek_files (NautilusClipboard *clip)
+{
+ return clip->files;
+}
+
+/**
+ * nautilus_clipboard_get_uri_list:
+ * @clip: The current local clipboard value.
+ *
+ * Returns: (transfer full): A GList of URI strings.
+ */
+GList *
+nautilus_clipboard_get_uri_list (NautilusClipboard *clip)
+{
+ GList *uris = NULL;
+
+ for (GList *l = clip->files; l != NULL; l = l->next)
+ {
+ uris = g_list_prepend (uris, nautilus_file_get_uri (l->data));
+ }
+
+ return g_list_reverse (uris);
+}
+
+gboolean
+nautilus_clipboard_is_cut (NautilusClipboard *clip)
+{
+ return clip->cut;
+}
+
+NautilusClipboard *
+nautilus_clipboard_copy (NautilusClipboard *clip)
+{
+ NautilusClipboard *new_clip = g_new0 (NautilusClipboard, 1);
+
+ new_clip->cut = clip->cut;
+ new_clip->files = nautilus_file_list_copy (clip->files);
+
+ return new_clip;
+}
+
+void
+nautilus_clipboard_free (NautilusClipboard *clip)
+{
+ nautilus_file_list_free (clip->files);
+ g_free (clip);
+}
+
+void
+nautilus_clipboard_prepare_for_files (GdkClipboard *clipboard,
+ GList *files,
+ gboolean cut)
+{
+ g_autoptr (NautilusClipboard) clip = NULL;
+ g_autoslist (GFile) file_list = NULL;
+ GdkContentProvider *providers[2];
+ g_autoptr (GdkContentProvider) provider = NULL;
+
+ clip = g_new (NautilusClipboard, 1);
+ clip->cut = cut;
+ clip->files = nautilus_file_list_copy (files);
+
+ file_list = convert_file_list_to_gdk_file_list (clip);
+
+ providers[0] = gdk_content_provider_new_typed (NAUTILUS_TYPE_CLIPBOARD, clip);
+ providers[1] = gdk_content_provider_new_typed (GDK_TYPE_FILE_LIST, file_list);
+
+ provider = gdk_content_provider_new_union (providers, 2);
+ gdk_clipboard_set_content (clipboard, provider);
+}
+
+void
+nautilus_clipboard_register (void)
+{
+ /*
+ * While it'is not a public API and the format is not documented, some apps
+ * have come to use this atom/mime type to integrate with our clipboard.
+ */
+ const gchar *nautilus_clipboard_mime_type = "x-special/gnome-copied-files";
+
+ gdk_content_register_serializer (NAUTILUS_TYPE_CLIPBOARD,
+ nautilus_clipboard_mime_type,
+ nautilus_clipboard_serialize,
+ NULL,
+ NULL);
+ gdk_content_register_deserializer (nautilus_clipboard_mime_type,
+ NAUTILUS_TYPE_CLIPBOARD,
+ nautilus_clipboard_deserialize,
+ NULL,
+ NULL);
+}