[evince] shell: Fix DnD support for attachments
- From: Germán Poo-Caamaño <gpoo src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evince] shell: Fix DnD support for attachments
- Date: Fri, 18 May 2018 23:25:56 +0000 (UTC)
commit 469732c1191ff5fad8104a62a21d672306c20cbd
Author: Germán Poo-Caamaño <gpoo gnome org>
Date: Wed Sep 27 15:12:57 2017 -0300
shell: Fix DnD support for attachments
Follows XDS if available. Otherwise, uses temporary files as
fallback.
This patch only enables drag and drop of one file. If several
files are selected, only the first one will be saved. The limitation
relies on the XDS specification, which does not support more than
one file, as was also found in Gedit: See Bug 710546#c4.
Partially fixes https://bugzilla.gnome.org/show_bug.cgi?id=683316
shell/ev-sidebar-attachments.c | 294 +++++++++++++++++++++++++++++++++-------
shell/ev-sidebar-attachments.h | 7 +-
shell/ev-window.c | 129 ++++++++++++------
3 files changed, 338 insertions(+), 92 deletions(-)
---
diff --git a/shell/ev-sidebar-attachments.c b/shell/ev-sidebar-attachments.c
index 04ca30e..24576fc 100644
--- a/shell/ev-sidebar-attachments.c
+++ b/shell/ev-sidebar-attachments.c
@@ -54,11 +54,23 @@ enum {
enum {
SIGNAL_POPUP_MENU,
+ SIGNAL_SAVE_ATTACHMENT,
N_SIGNALS
};
+enum {
+ EV_DND_TARGET_XDS,
+ EV_DND_TARGET_TEXT_URI_LIST
+};
static guint signals[N_SIGNALS];
+#define XDS_ATOM gdk_atom_intern_static_string ("XdndDirectSave0")
+#define TEXT_ATOM gdk_atom_intern_static_string ("text/plain")
+#define STRING_ATOM gdk_atom_intern_static_string ("STRING")
+#define MAX_XDS_ATOM_VAL_LEN 4096
+#define XDS_ERROR 'E'
+#define XDS_SUCCESS 'S'
+
struct _EvSidebarAttachmentsPrivate {
GtkWidget *icon_view;
GtkListStore *model;
@@ -387,69 +399,239 @@ ev_sidebar_attachments_screen_changed (GtkWidget *widget,
}
}
+
+static gchar *
+read_xds_property (GdkDragContext *context)
+{
+ guchar *prop_text;
+ gint length;
+ gchar *retval = NULL;
+
+ g_assert (context != NULL);
+
+ if (gdk_property_get (gdk_drag_context_get_source_window (context), XDS_ATOM, TEXT_ATOM,
+ 0, MAX_XDS_ATOM_VAL_LEN, FALSE,
+ NULL, NULL, &length, &prop_text)
+ && prop_text) {
+
+ /* g_strndup will null terminate the string */
+ retval = g_strndup ((const gchar *) prop_text, length);
+ g_free (prop_text);
+ }
+
+ return retval;
+}
+
static void
-ev_sidebar_attachments_drag_data_get (GtkWidget *widget,
- GdkDragContext *drag_context,
- GtkSelectionData *data,
- guint info,
- guint time,
- gpointer user_data)
+write_xds_property (GdkDragContext *context,
+ const gchar *value)
{
- EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (user_data);
- GList *selected = NULL, *l;
- GPtrArray *uris;
- char **uri_list;
+ g_assert (context != NULL);
+
+ if (value)
+ gdk_property_change (gdk_drag_context_get_source_window (context), XDS_ATOM,
+ TEXT_ATOM, 8, GDK_PROP_MODE_REPLACE,
+ (const guchar *) value, strlen (value));
+ else
+ gdk_property_delete (gdk_drag_context_get_source_window (context), XDS_ATOM);
+}
+
+/*
+ * Copied from add_custom_button_to_dialog () in gtk+, from file
+ * gtkfilechooserwidget.c
+ */
+static void
+ev_add_custom_button_to_dialog (GtkDialog *dialog,
+ const gchar *mnemonic_label,
+ const gchar *stock_id,
+ gint response_id)
+{
+ GtkWidget *button;
+
+ button = gtk_button_new_with_mnemonic (mnemonic_label);
+ gtk_widget_set_can_default (button, TRUE);
+ gtk_button_set_image (GTK_BUTTON (button),
+ gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON));
+ gtk_widget_show (button);
+
+ gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, response_id);
+}
+
+/* Presents an overwrite confirmation dialog; returns whether the file
+ * should be overwritten.
+ * Taken from confirm_dialog_should_accept_filename () in gtk+, from file
+ * gtkfilechooserwidget.c
+ */
+static gboolean
+ev_sidebar_attachments_confirm_overwrite (EvSidebarAttachments *attachbar,
+ const gchar *uri)
+{
+ GtkWidget *toplevel, *dialog;
+ int response;
+ gchar *filename, *basename;
+ GFile *file;
+
+ filename = g_filename_from_uri (uri, NULL, NULL);
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ g_free (filename);
+ return TRUE;
+ }
+ g_free (filename);
+
+ file = g_file_new_for_uri (uri);
+ basename = g_file_get_basename (file);
+ g_object_unref (file);
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (attachbar));
+ dialog = gtk_message_dialog_new (gtk_widget_is_toplevel (toplevel) ? GTK_WINDOW (toplevel) : NULL,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ _("A file named \"%s\" already exists. Do you want to "
+ "replace it?"),
+ basename);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("The file \"%s\" already exists. Replacing"
+ " it will overwrite its contents."),
+ uri);
+
+ gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ ev_add_custom_button_to_dialog (GTK_DIALOG (dialog), _("_Replace"),
+ GTK_STOCK_SAVE_AS, GTK_RESPONSE_ACCEPT);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_ACCEPT,
+ GTK_RESPONSE_CANCEL,
+ -1);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+
+ if (gtk_window_has_group (GTK_WINDOW (toplevel)))
+ gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
+ GTK_WINDOW (dialog));
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+
+ return (response == GTK_RESPONSE_ACCEPT);
+}
+
+static EvAttachment *
+ev_sidebar_attachments_get_selected_attachment (EvSidebarAttachments *ev_attachbar)
+{
+ EvAttachment *attachment;
+ GList *selected = NULL;
+ GtkTreeIter iter;
+ GtkTreePath *path;
selected = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (ev_attachbar->priv->icon_view));
+
if (!selected)
- return;
+ return NULL;
- uris = g_ptr_array_new ();
-
- for (l = selected; l && l->data; l = g_list_next (l)) {
- EvAttachment *attachment;
- GtkTreePath *path;
- GtkTreeIter iter;
- GFile *file;
- gchar *template;
- GError *error = NULL;
-
- path = (GtkTreePath *) l->data;
+ if (!selected->data) {
+ g_list_free (selected);
+ return NULL;
+ }
- gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
- &iter, path);
- gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
- COLUMN_ATTACHMENT, &attachment,
- -1);
+ path = (GtkTreePath *) selected->data;
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
+ &iter, path);
+ gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
+ COLUMN_ATTACHMENT, &attachment,
+ -1);
+
+ gtk_tree_path_free (path);
+ g_list_free (selected);
+
+ return attachment;
+}
+
+static void
+ev_sidebar_attachments_drag_begin (GtkWidget *widget,
+ GdkDragContext *drag_context,
+ EvSidebarAttachments *ev_attachbar)
+{
+ EvAttachment *attachment;
+ gchar *filename;
+
+ attachment = ev_sidebar_attachments_get_selected_attachment (ev_attachbar);
+
+ if (!attachment)
+ return;
+
+ filename = g_build_filename (ev_attachment_get_name (attachment), NULL);
+ write_xds_property (drag_context, filename);
+
+ g_free (filename);
+ g_object_unref (attachment);
+}
+
+static void
+ev_sidebar_attachments_drag_data_get (GtkWidget *widget,
+ GdkDragContext *drag_context,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ EvSidebarAttachments *ev_attachbar)
+{
+ EvAttachment *attachment;
+
+ attachment = ev_sidebar_attachments_get_selected_attachment (ev_attachbar);
+
+ if (!attachment)
+ return;
+
+ if (info == EV_DND_TARGET_XDS) {
+ guchar to_send = XDS_ERROR;
+ gchar *uri;
+
+ uri = read_xds_property (drag_context);
+ if (!uri) {
+ g_object_unref (attachment);
+ return;
+ }
+
+ if (ev_sidebar_attachments_confirm_overwrite (ev_attachbar, uri)) {
+ gboolean success;
+
+ g_signal_emit (ev_attachbar,
+ signals[SIGNAL_SAVE_ATTACHMENT],
+ 0,
+ attachment,
+ uri,
+ &success);
+
+ if (success)
+ to_send = XDS_SUCCESS;
+ }
+ g_free (uri);
+ gtk_selection_data_set (data, STRING_ATOM, 8, &to_send, sizeof (to_send));
+ } else {
+ GError *error = NULL;
+ GFile *file;
+ gchar *uri_list[2];
+ gchar *template;
/* FIXMEchpe: convert to filename encoding first! */
template = g_strdup_printf ("%s.XXXXXX", ev_attachment_get_name (attachment));
file = ev_mkstemp_file (template, &error);
g_free (template);
-
- if (file != NULL && ev_attachment_save (attachment, file, &error)) {
- gchar *uri;
- uri = g_file_get_uri (file);
- g_ptr_array_add (uris, uri);
+ if (file != NULL && ev_attachment_save (attachment, file, &error)) {
+ uri_list[0] = g_file_get_uri (file);
+ uri_list[1] = NULL; /* NULL-terminate */
+ g_object_unref (file);
}
-
+
if (error) {
g_warning ("%s", error->message);
g_error_free (error);
}
-
- gtk_tree_path_free (path);
- g_object_unref (file);
- g_object_unref (attachment);
+ gtk_selection_data_set_uris (data, uri_list);
+ g_free (uri_list[0]);
}
-
- g_ptr_array_add (uris, NULL); /* NULL-terminate */
- uri_list = (char **) g_ptr_array_free (uris, FALSE);
- gtk_selection_data_set_uris (data, uri_list);
- g_strfreev (uri_list);
-
- g_list_free (selected);
+ g_object_unref (attachment);
}
static void
@@ -525,6 +707,17 @@ ev_sidebar_attachments_class_init (EvSidebarAttachmentsClass *ev_attachbar_class
G_TYPE_NONE, 1,
G_TYPE_POINTER);
+ signals[SIGNAL_SAVE_ATTACHMENT] =
+ g_signal_new ("save-attachment",
+ G_TYPE_FROM_CLASS (g_object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EvSidebarAttachmentsClass, save_attachment),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_BOOLEAN, 2,
+ G_TYPE_OBJECT,
+ G_TYPE_STRING);
+
g_object_class_override_property (g_object_class,
PROP_WIDGET,
"main-widget");
@@ -535,6 +728,9 @@ ev_sidebar_attachments_init (EvSidebarAttachments *ev_attachbar)
{
GtkWidget *swindow;
+ static const GtkTargetEntry targets[] = { {"text/uri-list", 0, EV_DND_TARGET_TEXT_URI_LIST},
+ {"XdndDirectSave0", 0, EV_DND_TARGET_XDS}};
+
ev_attachbar->priv = EV_SIDEBAR_ATTACHMENTS_GET_PRIVATE (ev_attachbar);
swindow = gtk_scrolled_window_new (NULL, NULL);
@@ -582,14 +778,18 @@ ev_sidebar_attachments_init (EvSidebarAttachments *ev_attachbar)
gtk_icon_view_enable_model_drag_source (
GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
GDK_BUTTON1_MASK,
- NULL, 0,
- GDK_ACTION_COPY);
- gtk_drag_source_add_uri_targets (ev_attachbar->priv->icon_view);
+ targets, G_N_ELEMENTS (targets),
+ GDK_ACTION_MOVE);
g_signal_connect (ev_attachbar->priv->icon_view,
"drag-data-get",
G_CALLBACK (ev_sidebar_attachments_drag_data_get),
(gpointer) ev_attachbar);
+
+ g_signal_connect (ev_attachbar->priv->icon_view,
+ "drag-begin",
+ G_CALLBACK (ev_sidebar_attachments_drag_begin),
+ (gpointer) ev_attachbar);
}
GtkWidget *
diff --git a/shell/ev-sidebar-attachments.h b/shell/ev-sidebar-attachments.h
index b7ed9b0..43d7333 100644
--- a/shell/ev-sidebar-attachments.h
+++ b/shell/ev-sidebar-attachments.h
@@ -50,8 +50,11 @@ struct _EvSidebarAttachmentsClass {
GtkBoxClass base_class;
/* Signals */
- void (*popup_menu) (EvSidebarAttachments *ev_attachbar,
- EvAttachment *attachment);
+ void (*popup_menu) (EvSidebarAttachments *ev_attachbar,
+ EvAttachment *attachment);
+ void (*save_attachment) (EvSidebarAttachments *ev_attachbar,
+ EvAttachment *attachment,
+ const char *uri);
};
GType ev_sidebar_attachments_get_type (void) G_GNUC_CONST;
diff --git a/shell/ev-window.c b/shell/ev-window.c
index 519865d..8549231 100644
--- a/shell/ev-window.c
+++ b/shell/ev-window.c
@@ -5202,6 +5202,83 @@ attachment_bar_menu_popup_cb (EvSidebarAttachments *attachbar,
return TRUE;
}
+static gboolean
+save_attachment_to_target_file (EvAttachment *attachment,
+ GFile *target_file,
+ gboolean is_dir,
+ gboolean is_native,
+ EvWindow *ev_window)
+{
+ GFile *save_to = NULL;
+ GError *error = NULL;
+
+ if (is_native) {
+ if (is_dir) {
+ save_to = g_file_get_child (target_file,
+ /* FIXMEchpe: file name encoding! */
+ ev_attachment_get_name (attachment));
+ } else {
+ save_to = g_object_ref (target_file);
+ }
+ } else {
+ save_to = ev_mkstemp_file ("saveattachment.XXXXXX", &error);
+ }
+
+ if (save_to)
+ ev_attachment_save (attachment, save_to, &error);
+
+ if (error) {
+ ev_window_error_message (ev_window, error,
+ "%s", _("The attachment could not be saved."));
+ g_error_free (error);
+ g_object_unref (save_to);
+
+ return FALSE;
+ }
+
+ if (!is_native) {
+ GFile *dest_file;
+
+ if (is_dir) {
+ dest_file = g_file_get_child (target_file,
+ ev_attachment_get_name (attachment));
+ } else {
+ dest_file = g_object_ref (target_file);
+ }
+
+ ev_window_save_remote (ev_window, EV_SAVE_ATTACHMENT,
+ save_to, dest_file);
+
+ g_object_unref (dest_file);
+ }
+
+ g_object_unref (save_to);
+ return TRUE;
+}
+
+static gboolean
+attachment_bar_save_attachment_cb (EvSidebarAttachments *attachbar,
+ EvAttachment *attachment,
+ const char *uri,
+ EvWindow *ev_window)
+{
+ GFile *target_file;
+ gboolean is_native;
+ gboolean success;
+
+ target_file = g_file_new_for_uri (uri);
+ is_native = g_file_is_native (target_file);
+
+ success = save_attachment_to_target_file (attachment,
+ target_file,
+ FALSE,
+ is_native,
+ ev_window);
+
+ g_object_unref (target_file);
+ return success;
+}
+
static void
find_sidebar_result_activated_cb (EvFindSidebar *find_sidebar,
gint page,
@@ -6555,52 +6632,14 @@ attachment_save_dialog_response_cb (GtkWidget *fc,
for (l = ev_window->priv->attach_list; l && l->data; l = g_list_next (l)) {
EvAttachment *attachment;
- GFile *save_to = NULL;
- GError *error = NULL;
attachment = (EvAttachment *) l->data;
- if (is_native) {
- if (is_dir) {
- save_to = g_file_get_child (target_file,
- /* FIXMEchpe: file name encoding! */
- ev_attachment_get_name (attachment));
- } else {
- save_to = g_object_ref (target_file);
- }
- } else {
- save_to = ev_mkstemp_file ("saveattachment.XXXXXX", &error);
- }
-
- if (save_to)
- ev_attachment_save (attachment, save_to, &error);
-
- if (error) {
- ev_window_error_message (ev_window, error,
- "%s", _("The attachment could not be saved."));
- g_error_free (error);
- g_object_unref (save_to);
-
- continue;
- }
-
- if (!is_native) {
- GFile *dest_file;
-
- if (is_dir) {
- dest_file = g_file_get_child (target_file,
- ev_attachment_get_name (attachment));
- } else {
- dest_file = g_object_ref (target_file);
- }
-
- ev_window_save_remote (ev_window, EV_SAVE_ATTACHMENT,
- save_to, dest_file);
-
- g_object_unref (dest_file);
- }
-
- g_object_unref (save_to);
+ save_attachment_to_target_file (attachment,
+ target_file,
+ is_dir,
+ is_native,
+ ev_window);
}
g_free (uri);
@@ -7020,6 +7059,10 @@ ev_window_init (EvWindow *ev_window)
"popup",
G_CALLBACK (attachment_bar_menu_popup_cb),
ev_window, 0);
+ g_signal_connect_object (sidebar_widget,
+ "save-attachment",
+ G_CALLBACK (attachment_bar_save_attachment_cb),
+ ev_window, 0);
gtk_widget_show (sidebar_widget);
ev_sidebar_add_page (EV_SIDEBAR (ev_window->priv->sidebar),
sidebar_widget);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]