[nautilus/wip/corey/rework-clipboard] files-view: Allow pasting GDK_TYPE_PIXBUF




commit a5b37ad1082abc39a9feb7edbdb1b057589ed241
Author: Corey Berla <corey berla me>
Date:   Tue Sep 20 15:18:13 2022 -0700

    files-view: Allow pasting GDK_TYPE_PIXBUF
    
    In my testing, copying images was captured as GDK_TYPE_PIXBUF.
    Allow pasting those files into Nautilus.  Since we don't have
    any information regarding filename, popup a dialog that asks for a
    filename and allows choosing a supported filetype.

 src/meson.build                                 |   2 +
 src/nautilus-file-operations.c                  | 101 ++++++++++++++
 src/nautilus-file-operations.h                  |   7 +
 src/nautilus-files-view.c                       |  56 +++++++-
 src/nautilus-paste-image-dialog-controller.c    | 166 ++++++++++++++++++++++++
 src/nautilus-paste-image-dialog-controller.h    |  36 +++++
 src/resources/nautilus.gresource.xml            |   1 +
 src/resources/ui/nautilus-paste-image-dialog.ui |  86 ++++++++++++
 8 files changed, 454 insertions(+), 1 deletion(-)
---
diff --git a/src/meson.build b/src/meson.build
index 383920c3d..5c022c4fb 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -103,6 +103,8 @@ libnautilus_sources = [
   'nautilus-mime-actions.h',
   'nautilus-name-cell.c',
   'nautilus-name-cell.h',
+  'nautilus-paste-image-dialog-controller.c',
+  'nautilus-paste-image-dialog-controller.h',
   'nautilus-pathbar.c',
   'nautilus-pathbar.h',
   'nautilus-places-view.c',
diff --git a/src/nautilus-file-operations.c b/src/nautilus-file-operations.c
index aa8bd29f3..4d1b8c2f2 100644
--- a/src/nautilus-file-operations.c
+++ b/src/nautilus-file-operations.c
@@ -8026,6 +8026,107 @@ nautilus_file_operations_new_folder (GtkWidget                      *parent_view
     g_task_run_in_thread (task, create_task_thread_func);
 }
 
+static void
+paste_image_save_callback (GObject* source_object,
+                           GAsyncResult* res,
+                           gpointer user_data)
+{
+    CreateJob *job = user_data;
+
+    gdk_pixbuf_save_to_stream_finish (res, NULL);
+    nautilus_progress_info_set_progress (job->common.progress, 1, 1);
+    finalize_common ((CommonJob *) job);
+    g_object_unref (source_object);
+}
+
+static void
+paste_image_stream_ready (GObject* source_object,
+                          GAsyncResult* res,
+                          gpointer user_data)
+{
+    CreateJob *job = user_data;
+    g_autoptr (GError) error = NULL;
+
+    GFileOutputStream *stream = g_file_create_finish (G_FILE (source_object), res, &error);
+    if (error != NULL)
+    {
+        g_warning ("%s hello", error->message);
+    }
+
+    gdk_pixbuf_save_to_stream_async (job->done_callback_data,
+                                     G_OUTPUT_STREAM (stream),
+                                     "png",
+                                     job->common.cancellable,
+                                     paste_image_save_callback, job, NULL);
+}
+
+static void
+paste_image_received_callback (GObject* source_object,
+                               GAsyncResult* res,
+                               gpointer user_data)
+{
+    GdkClipboard *clipboard;
+    const GValue *value;
+    CreateJob *job = user_data;
+
+    clipboard = GDK_CLIPBOARD (source_object);
+
+    if (job_aborted ((CommonJob *) job))
+    {
+        return;
+    }
+
+    value = gdk_clipboard_read_value_finish (clipboard, res, NULL);
+
+    if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
+    {
+        GdkPixbuf *pixbuf = g_value_get_object (value);
+        g_autoptr (GFile) location = g_file_new_for_path ("/home/corey/Music/testthis.png");
+
+        job->done_callback_data = gdk_pixbuf_copy (pixbuf);
+        g_file_create_async (location, 0, 0, job->common.cancellable, paste_image_stream_ready, job);
+    }
+    else
+    {
+        finalize_common ((CommonJob *) job);
+    }
+}
+
+void
+nautilus_file_operations_paste_image_from_clipboard (GtkWidget                      *parent_view,
+                                                     NautilusFileOperationsDBusData *dbus_data,
+                                                     const char                     *parent_dir_uri,
+                                                     NautilusCreateCallback          done_callback,
+                                                     gpointer                        done_callback_data)
+{
+    g_autoptr (GTask) task = NULL;
+    CreateJob *job;
+    GtkWindow *parent_window;
+    GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (parent_view));
+
+
+    if (parent_view)
+    {
+        parent_window = (GtkWindow *) gtk_widget_get_ancestor (parent_view, GTK_TYPE_WINDOW);
+    }
+
+    job = op_job_new (CreateJob, parent_window, dbus_data);
+
+    nautilus_progress_info_start (job->common.progress);
+    nautilus_progress_info_set_details (job->common.progress,
+                                        _("Retrieving clipboard data"));
+    gdk_clipboard_read_value_async (clipboard, GDK_TYPE_PIXBUF, 0, job->common.cancellable, 
paste_image_received_callback, job);
+
+    if (!nautilus_file_undo_manager_is_operating ())
+    {
+        g_autoptr (GFile) target_dir = g_file_new_for_uri (parent_dir_uri);
+
+        /* job->common.undo_info = nautilus_file_undo_info_ext_new (NAUTILUS_FILE_UNDO_OP_COPY, */
+        /*                                                          1, */
+        /*                                                          NULL, target_dir); */
+    }
+}
+
 void
 nautilus_file_operations_new_file_from_template (GtkWidget              *parent_view,
                                                  const char             *parent_dir,
diff --git a/src/nautilus-file-operations.h b/src/nautilus-file-operations.h
index 14d664f80..37f6ab4ac 100644
--- a/src/nautilus-file-operations.h
+++ b/src/nautilus-file-operations.h
@@ -164,3 +164,10 @@ void nautilus_file_operations_compress (GList                          *files,
                                         NautilusFileOperationsDBusData *dbus_data,
                                         NautilusCreateCallback          done_callback,
                                         gpointer                        done_callback_data);
+
+void
+nautilus_file_operations_paste_image_from_clipboard (GtkWidget                      *parent_view,
+                                                     NautilusFileOperationsDBusData *dbus_data,
+                                                     const char                     *parent_dir_uri,
+                                                     NautilusCreateCallback          done_callback,
+                                                     gpointer                        done_callback_data);
diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c
index c78523f3a..0858f7694 100644
--- a/src/nautilus-files-view.c
+++ b/src/nautilus-files-view.c
@@ -76,6 +76,7 @@
 #include "nautilus-mime-actions.h"
 #include "nautilus-module.h"
 #include "nautilus-new-folder-dialog-controller.h"
+#include "nautilus-paste-image-dialog-controller.h"
 #include "nautilus-previewer.h"
 #include "nautilus-profile.h"
 #include "nautilus-program-choosing.h"
@@ -2092,6 +2093,21 @@ nautilus_files_view_new_folder_dialog_new (NautilusFilesView *view,
                       view);
 }
 
+static void
+paste_image_dialog_controller_on_name_accepted (NautilusPasteImageDialogController *controller,
+                                                gpointer                            user_data)
+{
+    nautilus_paste_image_dialog_controller_save_pixbuf (controller);
+    g_object_unref (controller);
+}
+
+static void
+paste_image_dialog_controller_on_cancelled (NautilusPasteImageDialogController *controller,
+                                            gpointer                            user_data)
+{
+    g_object_unref (controller);
+}
+
 typedef struct
 {
     NautilusFilesView *view;
@@ -2765,6 +2781,26 @@ paste_value_received_callback (GObject      *source_object,
     paste_callback_data_free (data);
 }
 
+static void
+paste_image_received_callback (GObject      *source_object,
+                               GAsyncResult *result,
+                               gpointer      user_data)
+{
+    GdkClipboard *clipboard;
+    const GValue *value;
+    NautilusPasteImageDialogController *controller = user_data;
+
+    clipboard = GDK_CLIPBOARD (source_object);
+
+    value = gdk_clipboard_read_value_finish (clipboard, result, NULL);
+
+    if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
+    {
+        GdkPixbuf *pixbuf = g_value_get_object (value);
+        nautilus_paste_image_dialog_controller_set_pixbuf (controller, pixbuf);
+    }
+}
+
 static void
 paste_files (NautilusFilesView *view,
              PasteCallbackData *data)
@@ -2795,6 +2831,23 @@ paste_files (NautilusFilesView *view,
     {
         gdk_clipboard_read_value_async (clipboard, G_TYPE_FILE, 0, priv->clipboard_cancellable, 
paste_value_received_callback, data);
     }
+    else if (gdk_content_formats_contain_gtype (formats, GDK_TYPE_PIXBUF))
+    {
+        /* g_autoptr (NautilusDirectory) destination_directory = nautilus_directory_get_by_uri 
(data->dest_uri); */
+
+        /* NautilusPasteImageDialogController *controller = nautilus_paste_image_dialog_controller_new 
(nautilus_files_view_get_containing_window (data->view), */
+        /*                                                                                              
destination_directory); */
+
+        nautilus_file_operations_paste_image_from_clipboard (GTK_WIDGET (view),
+                                                             NULL,
+                                                             data->dest_uri,
+                                                             NULL,
+                                                             NULL);
+        /* g_signal_connect (controller, "name-accepted", G_CALLBACK 
(paste_image_dialog_controller_on_name_accepted), NULL); */
+        /* g_signal_connect (controller, "cancelled", G_CALLBACK 
(paste_image_dialog_controller_on_cancelled), NULL); */
+        /* gdk_clipboard_read_value_async (clipboard, GDK_TYPE_PIXBUF, 0, priv->clipboard_cancellable, 
paste_image_received_callback, controller); */
+        paste_callback_data_free (data);
+    }
 }
 
 static void
@@ -7211,7 +7264,8 @@ update_actions_state_for_clipboard_targets (NautilusFilesView *view)
     clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view));
     formats = gdk_clipboard_get_formats (clipboard);
     is_data_copied = gdk_content_formats_contain_gtype (formats, NAUTILUS_TYPE_CLIPBOARD) ||
-                     gdk_content_formats_contain_gtype (formats, G_TYPE_FILE);
+                     gdk_content_formats_contain_gtype (formats, G_TYPE_FILE) ||
+                     gdk_content_formats_contain_gtype (formats, GDK_TYPE_PIXBUF);
 
     action = g_action_map_lookup_action (G_ACTION_MAP (priv->view_action_group),
                                          "paste");
diff --git a/src/nautilus-paste-image-dialog-controller.c b/src/nautilus-paste-image-dialog-controller.c
new file mode 100644
index 000000000..89fcd5245
--- /dev/null
+++ b/src/nautilus-paste-image-dialog-controller.c
@@ -0,0 +1,166 @@
+/* nautilus-new-folder-dialog-controller.c
+ *
+ * Copyright (C) 2016 the Nautilus developers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <glib/gi18n.h>
+
+#include "nautilus-paste-image-dialog-controller.h"
+
+
+struct _NautilusPasteImageDialogController
+{
+    NautilusFileNameWidgetController parent_instance;
+
+    GdkPixbuf *pixbuf;
+    gchar *dest_uri;
+    GtkWidget *combo;
+    GtkWidget *entry;
+    GtkWidget *dialog;
+    GtkWidget *preview;
+    GtkWidget *spinner;
+    GtkWidget *activate_button;
+
+    gulong response_handler_id;
+};
+
+G_DEFINE_TYPE (NautilusPasteImageDialogController, nautilus_paste_image_dialog_controller, 
NAUTILUS_TYPE_FILE_NAME_WIDGET_CONTROLLER)
+
+static void
+paste_image_dialog_controller_on_response (GtkDialog *dialog,
+                                           gint       response_id,
+                                           gpointer   user_data)
+{
+    NautilusPasteImageDialogController *self = user_data;
+
+    if (response_id != GTK_RESPONSE_OK)
+    {
+        g_signal_emit_by_name (self, "cancelled");
+    }
+}
+
+static gchar *
+real_get_new_name (NautilusFileNameWidgetController *self)
+{
+    NautilusPasteImageDialogController *controller = NAUTILUS_PASTE_IMAGE_DIALOG_CONTROLLER (self);
+    const char *extension = gtk_combo_box_get_active_id (GTK_COMBO_BOX (controller->combo));
+
+    return g_strstrip (g_strdup_printf ("%s.%s", gtk_editable_get_text (GTK_EDITABLE (controller->entry)), 
extension));
+}
+
+void
+nautilus_paste_image_dialog_controller_save_pixbuf (NautilusPasteImageDialogController *self)
+{
+    g_autoptr (GFile) parent = g_file_new_for_uri (self->dest_uri);
+    g_autofree gchar *filename = real_get_new_name (NAUTILUS_FILE_NAME_WIDGET_CONTROLLER (self));
+    g_autoptr (GFile) location = g_file_get_child (parent, filename);
+    g_autofree gchar *path = g_file_get_path (location);
+    const char *extension = gtk_combo_box_get_active_id (GTK_COMBO_BOX (self->combo));
+
+    gdk_pixbuf_save (self->pixbuf, path, extension, NULL, NULL);
+}
+
+void nautilus_paste_image_dialog_controller_set_pixbuf (NautilusPasteImageDialogController *self,
+                                                        GdkPixbuf                          *pixbuf)
+{
+    self->pixbuf = gdk_pixbuf_copy (pixbuf);
+    gtk_widget_set_visible (self->spinner, FALSE);
+    gtk_widget_set_visible (self->activate_button, TRUE);
+    gtk_picture_set_pixbuf (GTK_PICTURE (self->preview), self->pixbuf);
+}
+
+NautilusPasteImageDialogController *
+nautilus_paste_image_dialog_controller_new (GtkWindow         *parent_window,
+                                            NautilusDirectory *destination_directory)
+{
+    NautilusPasteImageDialogController *self;
+    g_autoptr (GtkBuilder) builder = NULL;
+    GtkWidget *paste_image_dialog;
+    GtkWidget *error_revealer;
+    GtkWidget *error_label;
+    GtkWidget *name_entry;
+    GtkWidget *activate_button;
+
+    builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/ui/nautilus-paste-image-dialog.ui");
+    paste_image_dialog = GTK_WIDGET (gtk_builder_get_object (builder, "paste_image_dialog"));
+    error_revealer = GTK_WIDGET (gtk_builder_get_object (builder, "error_revealer"));
+    error_label = GTK_WIDGET (gtk_builder_get_object (builder, "error_label"));
+    name_entry = GTK_WIDGET (gtk_builder_get_object (builder, "name_entry"));
+    activate_button = GTK_WIDGET (gtk_builder_get_object (builder, "ok_button"));
+
+    gtk_window_set_transient_for (GTK_WINDOW (paste_image_dialog),
+                                  parent_window);
+
+    self = g_object_new (NAUTILUS_TYPE_PASTE_IMAGE_DIALOG_CONTROLLER,
+                         "error-revealer", error_revealer,
+                         "error-label", error_label,
+                         "name-entry", name_entry,
+                         "activate-button", activate_button,
+                         "containing-directory", destination_directory, NULL);
+
+    self->combo = GTK_WIDGET (gtk_builder_get_object (builder, "combo"));
+    self->entry = name_entry;
+    self->activate_button = activate_button;
+    self->dest_uri = nautilus_directory_get_uri (destination_directory);
+    self->dialog = paste_image_dialog;
+    self->preview = GTK_WIDGET (gtk_builder_get_object (builder, "picture_preview"));
+    self->spinner = GTK_WIDGET (gtk_builder_get_object (builder, "spinner"));
+
+    gtk_combo_box_set_active_id (GTK_COMBO_BOX (self->combo), "png");
+
+    g_signal_connect (paste_image_dialog, "response", G_CALLBACK 
(paste_image_dialog_controller_on_response), self);
+    gtk_widget_show (paste_image_dialog);
+
+    return self;
+}
+
+static void
+nautilus_paste_image_dialog_controller_init (NautilusPasteImageDialogController *self)
+{
+}
+
+static void
+nautilus_paste_image_dialog_controller_finalize (GObject *object)
+{
+    NautilusPasteImageDialogController *self;
+
+    self = NAUTILUS_PASTE_IMAGE_DIALOG_CONTROLLER (object);
+    g_clear_object (&self->pixbuf);
+    g_free (self->dest_uri);
+    self->dest_uri = NULL;
+
+    if (self->dialog != NULL)
+    {
+        g_signal_handlers_disconnect_by_func (self->dialog, paste_image_dialog_controller_on_response, self);
+
+        gtk_window_destroy (GTK_WINDOW (self->dialog));
+        self->dialog = NULL;
+    }
+
+    G_OBJECT_CLASS (nautilus_paste_image_dialog_controller_parent_class)->finalize (object);
+}
+
+static void
+nautilus_paste_image_dialog_controller_class_init (NautilusPasteImageDialogControllerClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    NautilusFileNameWidgetControllerClass *parent_class = NAUTILUS_FILE_NAME_WIDGET_CONTROLLER_CLASS (klass);
+
+    object_class->finalize = nautilus_paste_image_dialog_controller_finalize;
+
+    parent_class->get_new_name = real_get_new_name;
+}
diff --git a/src/nautilus-paste-image-dialog-controller.h b/src/nautilus-paste-image-dialog-controller.h
new file mode 100644
index 000000000..5a6b010a8
--- /dev/null
+++ b/src/nautilus-paste-image-dialog-controller.h
@@ -0,0 +1,36 @@
+/* nautilus-rename-file-popover-controller.h
+ *
+ * Copyright (C) 2016 the Nautilus developers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "nautilus-file-name-widget-controller.h"
+
+#define NAUTILUS_TYPE_PASTE_IMAGE_DIALOG_CONTROLLER nautilus_paste_image_dialog_controller_get_type ()
+G_DECLARE_FINAL_TYPE (NautilusPasteImageDialogController, nautilus_paste_image_dialog_controller, NAUTILUS, 
PASTE_IMAGE_DIALOG_CONTROLLER, NautilusFileNameWidgetController)
+
+NautilusPasteImageDialogController * nautilus_paste_image_dialog_controller_new (GtkWindow         
*parent_window,
+                                                                                 NautilusDirectory 
*destination_directory);
+
+void nautilus_paste_image_dialog_controller_save_pixbuf (NautilusPasteImageDialogController *self);
+
+void nautilus_paste_image_dialog_controller_set_pixbuf (NautilusPasteImageDialogController *self,
+                                                        GdkPixbuf                          *pixbuf);
diff --git a/src/resources/nautilus.gresource.xml b/src/resources/nautilus.gresource.xml
index bfe8b2bd3..e1550d5d6 100644
--- a/src/resources/nautilus.gresource.xml
+++ b/src/resources/nautilus.gresource.xml
@@ -28,6 +28,7 @@
     <file>ui/nautilus-operations-ui-manager-request-passphrase.ui</file>
     <file>ui/nautilus-grid-cell.ui</file>
     <file>ui/nautilus-name-cell.ui</file>
+    <file>ui/nautilus-paste-image-dialog.ui</file>
     <file alias="gtk/ui/nautilusgtksidebarrow.ui">../gtk/nautilusgtksidebarrow.ui</file>
     <file alias="gtk/ui/nautilusgtkplacesview.ui">../gtk/nautilusgtkplacesview.ui</file>
     <file alias="gtk/ui/nautilusgtkplacesviewrow.ui">../gtk/nautilusgtkplacesviewrow.ui</file>
diff --git a/src/resources/ui/nautilus-paste-image-dialog.ui b/src/resources/ui/nautilus-paste-image-dialog.ui
new file mode 100644
index 000000000..f82f3f9e7
--- /dev/null
+++ b/src/resources/ui/nautilus-paste-image-dialog.ui
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk" version="4.0"/>
+  <object class="GtkDialog" id="paste_image_dialog">
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="use-header-bar">1</property>
+    <property name="width_request">450</property>
+    <property name="title" translatable="yes">New Image from Clipboard</property>
+    <child internal-child="content_area">
+      <object class="GtkBox" id="content_area">
+        <property name="orientation">vertical</property>
+        <property name="margin_top">18</property>
+        <property name="margin_bottom">12</property>
+        <property name="margin_start">18</property>
+        <property name="margin_end">18</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkSpinner" id="spinner">
+            <property name="spinning">true</property>
+          </object>
+        </child>
+        <child>
+          <object class="AdwClamp">
+            <property name="maximum-size">320</property>
+            <property name="child">
+              <object class="GtkPicture" id="picture_preview">
+                <property name="content-fit">GTK_CONTENT_FIT_SCALE_DOWN</property>
+              </object>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">horizontal</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkEntry" id="name_entry">
+                <property name="placeholder-text" translatable="yes">Enter desired filename for pasted 
image</property>
+                <property name="activates-default">true</property>
+                <property name="hexpand">true</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkComboBoxText" id="combo">
+                <items>
+                  <item id="png">png</item>
+                  <item id="jpeg">jpeg</item>
+                  <item id="bmp">bmp</item>
+                </items>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkRevealer" id="error_revealer">
+            <property name="child">
+              <object class="GtkLabel" id="error_label">
+                <property name="margin_top">4</property>
+                <property name="margin_bottom">4</property>
+                <property name="xalign">0</property>
+              </object>
+            </property>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child type="action">
+      <object class="GtkButton" id="cancel_button">
+        <property name="label" translatable="yes">Cancel</property>
+      </object>
+    </child>
+    <child type="action">
+      <object class="GtkButton" id="ok_button">
+        <property name="label" translatable="yes">Save</property>
+        <property name="sensitive">False</property>
+        <property name="visible">False</property>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="ok" default="true">ok_button</action-widget>
+      <action-widget response="cancel">cancel_button</action-widget>
+    </action-widgets>
+  </object>
+</interface>


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