[nautilus/wip/corey/rework-clipboard] files-view: Allow pasting GDK_TYPE_PIXBUF
- From: Corey Berla <coreyberla src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [nautilus/wip/corey/rework-clipboard] files-view: Allow pasting GDK_TYPE_PIXBUF
- Date: Fri, 23 Sep 2022 03:50:34 +0000 (UTC)
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]