[nautilus/wip/antoniof/gtk4-restore-clipboard: 4/7] clipboard: Reintroduce non-local clipboard handling




commit a44f478f168de6d8f12c5df37be6f44c05bbffc6
Author: António Fernandes <antoniof gnome org>
Date:   Tue Jan 4 01:34:03 2022 +0000

    clipboard: Reintroduce non-local clipboard handling
    
    The old implementation supported providing 3 clipboard targers:
      - the "x-special/gnome-copied-files" MIME Type
      - the "text/uri-list" MIME Type
      - plain text clipboard
    
    We would build slightly different strings for each target.
    
    For pasting, only the first target was supported, but the provider
    could be any other program, not necessarily ourselves.
    
    In order to restore this non-local clipboard functionality, we need to
    serialize content into these target types or deserialize into a GType.
    
      - For the first one, we register our own custom [de]serializers,
        which replicate the old implementation format.
      - For the other two, we have GDK do it from GTK_TYPE_FILE_LIST.

 src/nautilus-application.c |   3 +
 src/nautilus-clipboard.c   | 247 +++++++++++++++++++++++++--------------------
 src/nautilus-clipboard.h   |   2 +
 3 files changed, 143 insertions(+), 109 deletions(-)
---
diff --git a/src/nautilus-application.c b/src/nautilus-application.c
index d7854dcec..06255587d 100644
--- a/src/nautilus-application.c
+++ b/src/nautilus-application.c
@@ -42,6 +42,7 @@
 #include "nautilus-debug.h"
 
 #include "nautilus-bookmark-list.h"
+#include "nautilus-clipboard.h"
 #include "nautilus-dbus-manager.h"
 #include "nautilus-directory-private.h"
 #include "nautilus-file.h"
@@ -1110,6 +1111,8 @@ nautilus_application_init (NautilusApplication *self)
 
     nautilus_ensure_extension_points ();
     nautilus_ensure_extension_builtins ();
+
+    nautilus_clipboard_register ();
 }
 
 static void
diff --git a/src/nautilus-clipboard.c b/src/nautilus-clipboard.c
index c1a71f4c8..63183641d 100644
--- a/src/nautilus-clipboard.c
+++ b/src/nautilus-clipboard.c
@@ -44,129 +44,54 @@ struct _NautilusClipboard
 G_DEFINE_BOXED_TYPE (NautilusClipboard, nautilus_clipboard,
                      nautilus_clipboard_copy, nautilus_clipboard_free)
 
-#if 0 && NAUTILUS_CLIPBOARD_NEEDS_GTK4_REIMPLEMENTATION
-static GList *
-convert_lines_to_str_list (char **lines)
-{
-    int i;
-    GList *result;
-
-    if (lines[0] == NULL)
-    {
-        return NULL;
-    }
-
-    result = NULL;
-    for (i = 0; lines[i] != NULL; i++)
-    {
-        result = g_list_prepend (result, g_strdup (lines[i]));
-    }
-    return g_list_reverse (result);
-}
-
 static char *
-convert_file_list_to_string (ClipboardInfo *info,
-                             gboolean       format_for_text,
-                             gsize         *len)
+nautilus_clipboard_to_string (NautilusClipboard *clip)
 {
     GString *uris;
-    char *uri, *tmp;
-    GFile *f;
+    char *uri;
     guint i;
     GList *l;
 
-    if (format_for_text)
-    {
-        uris = g_string_new (NULL);
-    }
-    else
-    {
-        uris = g_string_new (info->cut ? "cut" : "copy");
-    }
+    uris = g_string_new (clip->cut ? "cut" : "copy");
 
-    for (i = 0, l = info->files; l != NULL; l = l->next, i++)
+    for (i = 0, l = clip->files; l != NULL; l = l->next, i++)
     {
         uri = nautilus_file_get_uri (l->data);
 
-        if (format_for_text)
-        {
-            f = g_file_new_for_uri (uri);
-            tmp = g_file_get_parse_name (f);
-            g_object_unref (f);
-
-            if (tmp != NULL)
-            {
-                g_string_append (uris, tmp);
-                g_free (tmp);
-            }
-            else
-            {
-                g_string_append (uris, uri);
-            }
-
-            /* skip newline for last element */
-            if (i + 1 < g_list_length (info->files))
-            {
-                g_string_append_c (uris, '\n');
-            }
-        }
-        else
-        {
-            g_string_append_c (uris, '\n');
-            g_string_append (uris, uri);
-        }
+        g_string_append_c (uris, '\n');
+        g_string_append (uris, uri);
 
         g_free (uri);
     }
 
-    *len = uris->len;
     return g_string_free (uris, FALSE);
 }
 
-static GList *
-get_item_list_from_selection_data (GtkSelectionData *selection_data)
+static NautilusClipboard *
+nautilus_clipboard_from_string (char *string)
 {
-    GList *items;
-    char **lines;
+    NautilusClipboard *clip;
+    g_auto (GStrv) lines = NULL;
 
-    if (gtk_selection_data_get_data_type (selection_data) != copied_files_atom
-        || gtk_selection_data_get_length (selection_data) <= 0)
-    {
-        items = NULL;
-    }
-    else
-    {
-        gchar *data;
-        /* Not sure why it's legal to assume there's an extra byte
-         * past the end of the selection data that it's safe to write
-         * to. But gtk_editable_selection_received does this, so I
-         * think it is OK.
-         */
-        data = (gchar *) gtk_selection_data_get_data (selection_data);
-        data[gtk_selection_data_get_length (selection_data)] = '\0';
-        lines = g_strsplit (data, "\n", 0);
-        items = convert_lines_to_str_list (lines);
-        g_strfreev (lines);
-    }
-
-    return items;
-}
-
-GList *
-nautilus_clipboard_get_uri_list_from_selection_data (GtkSelectionData *selection_data)
-{
-    GList *items;
+    clip = g_new0 (NautilusClipboard, 1);
 
-    items = get_item_list_from_selection_data (selection_data);
-    if (items)
+    if (string != NULL)
     {
+        lines = g_strsplit (string, "\n", 0);
+
         /* Line 0 is "cut" or "copy", so uris start at line 1. */
-        items = g_list_remove (items, items->data);
+        clip->cut = g_str_equal (lines[0], "cut");
+        for (int i = 1; lines[i] != NULL; i++)
+        {
+            clip->files = g_list_prepend (clip->files, nautilus_file_get_by_uri (lines[i]));
+        }
+        clip->files = g_list_reverse (clip->files);
     }
 
-    return items;
+    return clip;
 }
 
+#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION
 void
 nautilus_clipboard_clear_if_colliding_uris (GtkWidget   *widget,
                                             const GList *item_uris)
@@ -207,6 +132,95 @@ nautilus_clipboard_clear_if_colliding_uris (GtkWidget   *widget,
 }
 #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;
+    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);
+
+    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_get_uri_list:
  * @clip: The current local clipboard value.
@@ -256,25 +270,40 @@ nautilus_clipboard_prepare_for_files (GdkClipboard *clipboard,
                                       gboolean      cut)
 {
     g_autoptr (NautilusClipboard) clip = NULL;
-    GdkContentProvider *provider;
+    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);
 
-    provider = gdk_content_provider_new_typed (NAUTILUS_TYPE_CLIPBOARD, clip);
+    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);
 }
 
-#if 0 && NAUTILUS_CLIPBOARD_NEEDS_GTK4_REIMPLEMENTATION
-GdkAtom
-nautilus_clipboard_get_atom (void)
+void
+nautilus_clipboard_register (void)
 {
-    if (!copied_files_atom)
-    {
-        copied_files_atom = gdk_atom_intern_static_string ("x-special/gnome-copied-files");
-    }
-
-    return copied_files_atom;
+    /*
+     * 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);
 }
-#endif
diff --git a/src/nautilus-clipboard.h b/src/nautilus-clipboard.h
index 13bb8b3ac..137a14e69 100644
--- a/src/nautilus-clipboard.h
+++ b/src/nautilus-clipboard.h
@@ -42,3 +42,5 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (NautilusClipboard, nautilus_clipboard_free)
 void nautilus_clipboard_prepare_for_files (GdkClipboard *clipboard,
                                            GList        *files,
                                            gboolean      cut);
+
+void               nautilus_clipboard_register     (void);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]