[gedit] dnd: Implement XDS dnd support for GeditView and GeditWindow



commit c1c3b568ec6294f49d1ce7e90ae64109c4dc3a93
Author: Nelson Benítez León <nbenitezl+gnome gmail com>
Date:   Sat Oct 26 18:56:07 2013 +0200

    dnd: Implement XDS dnd support for GeditView and GeditWindow
    
    So we can open files directly when dropped from file-roller.
    
    Fixes bug 430798

 gedit/gedit-utils.c          |   70 ++++++++++++++++++
 gedit/gedit-utils.h          |    2 +
 gedit/gedit-view.c           |  163 +++++++++++++++++++++++++++++-------------
 gedit/gedit-window-private.h |    2 +
 gedit/gedit-window.c         |  110 ++++++++++++++++++++++++++---
 5 files changed, 287 insertions(+), 60 deletions(-)
---
diff --git a/gedit/gedit-utils.c b/gedit/gedit-utils.c
index 0a587ac..a638b27 100644
--- a/gedit/gedit-utils.c
+++ b/gedit/gedit-utils.c
@@ -43,6 +43,7 @@
 
 #include <glib.h>
 #include <glib/gi18n.h>
+#include <glib/gstdio.h>
 #include <gio/gio.h>
 
 #include "gedit-utils.h"
@@ -1455,4 +1456,73 @@ gedit_utils_get_compression_type_from_content_type (const gchar *content_type)
        return GEDIT_DOCUMENT_COMPRESSION_TYPE_NONE;
 }
 
+/* Copied from nautilus */
+static gchar *
+get_direct_save_filename (GdkDragContext *context)
+{
+       guchar *prop_text;
+       gint prop_len;
+
+       if (!gdk_property_get (gdk_drag_context_get_source_window  (context), gdk_atom_intern 
("XdndDirectSave0", FALSE),
+                              gdk_atom_intern ("text/plain", FALSE), 0, 1024, FALSE, NULL, NULL,
+                              &prop_len, &prop_text) && prop_text != NULL) {
+               return NULL;
+       }
+
+       /* Zero-terminate the string */
+       prop_text = g_realloc (prop_text, prop_len + 1);
+       prop_text[prop_len] = '\0';
+
+       /* Verify that the file name provided by the source is valid */
+       if (*prop_text == '\0' ||
+           strchr ((const gchar *) prop_text, G_DIR_SEPARATOR) != NULL) {
+               gedit_debug_message (DEBUG_UTILS, "Invalid filename provided by XDS drag site");
+               g_free (prop_text);
+               return NULL;
+       }
+
+       return (gchar *)prop_text;
+}
+
+gchar *
+gedit_utils_set_direct_save_filename (GdkDragContext *context)
+{
+       gchar *uri;
+       gchar *filename;
+
+       uri = NULL;
+       filename = get_direct_save_filename (context);
+
+       if (filename != NULL)
+       {
+               gchar *tempdir;
+               gchar *path;
+
+               tempdir = g_dir_make_tmp ("gedit-drop-XXXXXX", NULL);
+               if (tempdir == NULL)
+               {
+                       tempdir = g_strdup (g_get_tmp_dir ());
+               }
+
+               path = g_build_filename (tempdir,
+                                       filename,
+                                       NULL);
+
+               uri = g_filename_to_uri (path, NULL, NULL);
+
+               /* Change the property */
+               gdk_property_change (gdk_drag_context_get_source_window (context),
+                                    gdk_atom_intern ("XdndDirectSave0", FALSE),
+                                    gdk_atom_intern ("text/plain", FALSE), 8,
+                                    GDK_PROP_MODE_REPLACE, (const guchar *) uri,
+                                    strlen (uri));
+
+               g_free (tempdir);
+               g_free (path);
+               g_free (filename);
+       }
+
+       return uri;
+}
+
 /* ex:set ts=8 noet: */
diff --git a/gedit/gedit-utils.h b/gedit/gedit-utils.h
index f568be6..5c185cb 100644
--- a/gedit/gedit-utils.h
+++ b/gedit/gedit-utils.h
@@ -152,6 +152,8 @@ GeditDocumentCompressionType
                 gedit_utils_get_compression_type_from_content_type
                                                        (const gchar      *content_type);
 
+gchar           *gedit_utils_set_direct_save_filename  (GdkDragContext *context);
+
 G_END_DECLS
 
 #endif /* __GEDIT_UTILS_H__ */
diff --git a/gedit/gedit-view.c b/gedit/gedit-view.c
index d191410..17ae078 100644
--- a/gedit/gedit-view.c
+++ b/gedit/gedit-view.c
@@ -57,6 +57,7 @@
 enum
 {
        TARGET_URI_LIST = 100,
+       TARGET_XDNDDIRECTSAVE,
        TARGET_TAB
 };
 
@@ -65,6 +66,7 @@ struct _GeditViewPrivate
        GSettings *editor_settings;
        GtkTextBuffer *current_buffer;
        PeasExtensionSet *extensions;
+       gchar *direct_save_uri;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GeditView, gedit_view, GTK_SOURCE_TYPE_VIEW)
@@ -138,10 +140,15 @@ gedit_view_init (GeditView *view)
        view->priv->editor_settings = g_settings_new ("org.gnome.gedit.preferences.editor");
 
        /* Drag and drop support */
+       view->priv->direct_save_uri = NULL;
        tl = gtk_drag_dest_get_target_list (GTK_WIDGET (view));
 
        if (tl != NULL)
        {
+               gtk_target_list_add (tl,
+                                    gdk_atom_intern ("XdndDirectSave0", FALSE),
+                                    0,
+                                    TARGET_XDNDDIRECTSAVE);
                gtk_target_list_add_uri_targets (tl, TARGET_URI_LIST);
                gtk_target_list_add (tl,
                                     gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"),
@@ -370,58 +377,98 @@ gedit_view_drag_data_received (GtkWidget        *widget,
                               guint             timestamp)
 {
        /* If this is an URL emit DROP_URIS, otherwise chain up the signal */
-       if (info == TARGET_URI_LIST)
+       switch (info)
        {
-               gchar **uri_list;
+               case TARGET_URI_LIST:
+                       gchar **uri_list;
 
-               uri_list = gedit_utils_drop_get_uris (selection_data);
+                       uri_list = gedit_utils_drop_get_uris (selection_data);
 
-               if (uri_list != NULL)
-               {
-                       g_signal_emit (widget, view_signals[DROP_URIS], 0, uri_list);
-                       g_strfreev (uri_list);
+                       if (uri_list != NULL)
+                       {
+                               g_signal_emit (widget, view_signals[DROP_URIS], 0, uri_list);
+                               g_strfreev (uri_list);
 
-                       gtk_drag_finish (context, TRUE, FALSE, timestamp);
-               }
-       }
-       else if (info == TARGET_TAB)
-       {
-               GtkWidget *notebook;
-               GtkWidget *new_notebook;
-               GtkWidget *page;
+                               gtk_drag_finish (context, TRUE, FALSE, timestamp);
+                       }
 
-               notebook = gtk_drag_get_source_widget (context);
+                       break;
 
-               if (!GTK_IS_WIDGET (notebook))
-               {
-                       return;
-               }
+               case TARGET_TAB:
+                       GtkWidget *notebook;
+                       GtkWidget *new_notebook;
+                       GtkWidget *page;
 
-               page = *(GtkWidget **) gtk_selection_data_get_data (selection_data);
-               g_return_if_fail (page != NULL);
+                       notebook = gtk_drag_get_source_widget (context);
 
-               /* We need to iterate and get the notebook of the target view
-                  because we can have several notebooks per window */
-               new_notebook = get_notebook_from_view (widget);
+                       if (!GTK_IS_WIDGET (notebook))
+                       {
+                               return;
+                       }
 
-               if (notebook != new_notebook)
-               {
-                       gedit_notebook_move_tab (GEDIT_NOTEBOOK (notebook),
-                                                GEDIT_NOTEBOOK (new_notebook),
-                                                GEDIT_TAB (page),
-                                                0);
-               }
+                       page = *(GtkWidget **) gtk_selection_data_get_data (selection_data);
+                       g_return_if_fail (page != NULL);
 
-               gtk_drag_finish (context, TRUE, TRUE, timestamp);
-       }
-       else
-       {
-               GTK_WIDGET_CLASS (gedit_view_parent_class)->drag_data_received (widget,
-                                                                               context,
-                                                                               x, y,
-                                                                               selection_data,
-                                                                               info,
-                                                                               timestamp);
+                       /* We need to iterate and get the notebook of the target view
+                          because we can have several notebooks per window */
+                       new_notebook = get_notebook_from_view (widget);
+
+                       if (notebook != new_notebook)
+                       {
+                               gedit_notebook_move_tab (GEDIT_NOTEBOOK (notebook),
+                                                        GEDIT_NOTEBOOK (new_notebook),
+                                                        GEDIT_TAB (page),
+                                                        0);
+                       }
+
+                       gtk_drag_finish (context, TRUE, TRUE, timestamp);
+
+                       break;
+
+               case TARGET_XDNDDIRECTSAVE:
+                       GeditView *view;
+
+                       view = GEDIT_VIEW (widget);
+
+                       /* Indicate that we don't provide "F" fallback */
+                       if (gtk_selection_data_get_format (selection_data) == 8 &&
+                           gtk_selection_data_get_length (selection_data) == 1 &&
+                           gtk_selection_data_get_data (selection_data)[0] == 'F')
+                       {
+                               gdk_property_change (gdk_drag_context_get_source_window (context),
+                                                    gdk_atom_intern ("XdndDirectSave0", FALSE),
+                                                    gdk_atom_intern ("text/plain", FALSE), 8,
+                                                    GDK_PROP_MODE_REPLACE, (const guchar *) "", 0);
+                       }
+                       else if (gtk_selection_data_get_format (selection_data) == 8 &&
+                                gtk_selection_data_get_length (selection_data) == 1 &&
+                                gtk_selection_data_get_data (selection_data)[0] == 'S' &&
+                                view->priv->direct_save_uri != NULL)
+                       {
+                               gchar **uris;
+
+                               uris = g_new (gchar *, 2);
+                               uris[0] = view->priv->direct_save_uri;
+                               uris[1] = NULL;
+                               g_signal_emit (widget, view_signals[DROP_URIS], 0, uris);
+                               g_free (uris);
+                       }
+
+                       g_free (view->priv->direct_save_uri);
+                       view->priv->direct_save_uri = NULL;
+
+                       gtk_drag_finish (context, TRUE, FALSE, timestamp);
+
+                       break;
+
+               default:
+                       GTK_WIDGET_CLASS (gedit_view_parent_class)->drag_data_received (widget,
+                                                                                       context,
+                                                                                       x, y,
+                                                                                       selection_data,
+                                                                                       info,
+                                                                                       timestamp);
+                       break;
        }
 }
 
@@ -434,12 +481,28 @@ gedit_view_drag_drop (GtkWidget      *widget,
 {
        gboolean result;
        GdkAtom target;
+       guint info;
+       gboolean found;
+       GtkTargetList *target_list;
 
-       /* If this is a URL, just get the drag data */
-       target = drag_get_uri_target (widget, context);
+       target_list = gtk_drag_dest_get_target_list (widget);
+       target = gtk_drag_dest_find_target (widget, context, target_list);
+       found = gtk_target_list_find (target_list, target, &info);
 
-       if (target != GDK_NONE)
+       if (found && (info == TARGET_URI_LIST || info == TARGET_XDNDDIRECTSAVE))
        {
+               if (info == TARGET_XDNDDIRECTSAVE)
+               {
+                       gchar *uri;
+                       uri = gedit_utils_set_direct_save_filename (context);
+
+                       if (uri != NULL)
+                       {
+                               g_free (GEDIT_VIEW (widget)->priv->direct_save_uri);
+                               GEDIT_VIEW (widget)->priv->direct_save_uri = uri;
+                       }
+               }
+
                gtk_drag_get_data (widget, context, target, timestamp);
                result = TRUE;
        }
diff --git a/gedit/gedit-window-private.h b/gedit/gedit-window-private.h
index ccd2bdf..a56c915 100644
--- a/gedit/gedit-window-private.h
+++ b/gedit/gedit-window-private.h
@@ -119,6 +119,8 @@ struct _GeditWindowPrivate
 
        GFile          *default_location;
 
+       gchar          *direct_save_uri;
+
 #ifdef OS_OSX
        GtkOSXApplicationMenuGroup *mac_menu_group;
 #endif
diff --git a/gedit/gedit-window.c b/gedit/gedit-window.c
index 7b20183..49d93bc 100644
--- a/gedit/gedit-window.c
+++ b/gedit/gedit-window.c
@@ -90,7 +90,13 @@ enum
 
 enum
 {
-       TARGET_URI_LIST = 100
+       TARGET_URI_LIST = 100,
+       TARGET_XDNDDIRECTSAVE
+};
+
+static const GtkTargetEntry drop_types [] = {
+       { "XdndDirectSave0", 0, TARGET_XDNDDIRECTSAVE }, /* XDS Protocol Type */
+       { "text/uri-list", 0, TARGET_URI_LIST}
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GeditWindow, gedit_window, GTK_TYPE_APPLICATION_WINDOW)
@@ -2642,11 +2648,91 @@ drag_data_received_cb (GtkWidget        *widget,
        if (window == NULL)
                return;
 
-       if (info == TARGET_URI_LIST)
+       switch (info)
        {
-               uri_list = gedit_utils_drop_get_uris (selection_data);
-               load_uris_from_drop (window, uri_list);
-               g_strfreev (uri_list);
+               case TARGET_URI_LIST:
+                       uri_list = gedit_utils_drop_get_uris(selection_data);
+                       load_uris_from_drop (window, uri_list);
+                       g_strfreev (uri_list);
+
+                       gtk_drag_finish (context, TRUE, FALSE, timestamp);
+
+                       break;
+
+               case TARGET_XDNDDIRECTSAVE:
+                       /* Indicate that we don't provide "F" fallback */
+                       if (gtk_selection_data_get_format (selection_data) == 8 &&
+                           gtk_selection_data_get_length (selection_data) == 1 &&
+                           gtk_selection_data_get_data (selection_data)[0] == 'F')
+                       {
+                               gdk_property_change (gdk_drag_context_get_source_window (context),
+                                                    gdk_atom_intern ("XdndDirectSave0", FALSE),
+                                                    gdk_atom_intern ("text/plain", FALSE), 8,
+                                                    GDK_PROP_MODE_REPLACE, (const guchar *) "", 0);
+                       }
+                       else if (gtk_selection_data_get_format (selection_data) == 8 &&
+                                gtk_selection_data_get_length (selection_data) == 1 &&
+                                gtk_selection_data_get_data (selection_data)[0] == 'S' &&
+                                window->priv->direct_save_uri != NULL)
+                       {
+                               gchar **uris;
+
+                               uris = g_new (gchar *, 2);
+                               uris[0] = window->priv->direct_save_uri;
+                               uris[1] = NULL;
+
+                               load_uris_from_drop (window, uris);
+                               g_free (uris);
+                       }
+
+                       g_free (window->priv->direct_save_uri);
+                       window->priv->direct_save_uri = NULL;
+
+                       gtk_drag_finish (context, TRUE, FALSE, timestamp);
+
+                       break;
+       }
+}
+
+static void
+drag_drop_cb (GtkWidget      *widget,
+             GdkDragContext *context,
+             gint            x,
+             gint            y,
+             guint           time,
+             gpointer        user_data)
+{
+       GeditWindow *window;
+       GtkTargetList *target_list;
+       GdkAtom target;
+
+       window = get_drop_window (widget);
+
+       target_list = gtk_drag_dest_get_target_list (widget);
+       target = gtk_drag_dest_find_target (widget, context, target_list);
+
+       if (target != GDK_NONE)
+       {
+               guint info;
+               gboolean found;
+
+               found = gtk_target_list_find (target_list, target, &info);
+               g_assert (found);
+
+               if (info == TARGET_XDNDDIRECTSAVE)
+               {
+                       gchar *uri;
+                       uri = gedit_utils_set_direct_save_filename (context);
+
+                       if (uri != NULL)
+                       {
+                               g_free (window->priv->direct_save_uri);
+                               window->priv->direct_save_uri = uri;
+                       }
+               }
+
+               gtk_drag_get_data (GTK_WIDGET (widget), context,
+                                  target, time);
        }
 }
 
@@ -3697,6 +3783,7 @@ gedit_window_init (GeditWindow *window)
        window->priv->dispose_has_run = FALSE;
        window->priv->fullscreen_controls = NULL;
        window->priv->fullscreen_animation_timeout_id = 0;
+       window->priv->direct_save_uri = NULL;
        window->priv->editor_settings = g_settings_new ("org.gnome.gedit.preferences.editor");
        window->priv->ui_settings = g_settings_new ("org.gnome.gedit.preferences.ui");
 
@@ -3810,14 +3897,13 @@ gedit_window_init (GeditWindow *window)
        gtk_widget_show (window->priv->hpaned);
        gtk_widget_show (window->priv->vpaned);
 
-       /* Drag and drop support, set targets to NULL because we add the
-          default uri_targets below */
+       /* Drag and drop support */
        gtk_drag_dest_set (GTK_WIDGET (window),
                           GTK_DEST_DEFAULT_MOTION |
                           GTK_DEST_DEFAULT_HIGHLIGHT |
                           GTK_DEST_DEFAULT_DROP,
-                          NULL,
-                          0,
+                          drop_types,
+                          G_N_ELEMENTS (drop_types),
                           GDK_ACTION_COPY);
 
        /* Add uri targets */
@@ -3825,7 +3911,7 @@ gedit_window_init (GeditWindow *window)
 
        if (tl == NULL)
        {
-               tl = gtk_target_list_new (NULL, 0);
+               tl = gtk_target_list_new (drop_types, G_N_ELEMENTS (drop_types));
                gtk_drag_dest_set_target_list (GTK_WIDGET (window), tl);
                gtk_target_list_unref (tl);
        }
@@ -3838,6 +3924,10 @@ gedit_window_init (GeditWindow *window)
                          "drag_data_received",
                          G_CALLBACK (drag_data_received_cb),
                          NULL);
+       g_signal_connect (window,
+                         "drag_drop",
+                         G_CALLBACK (drag_drop_cb),
+                         NULL);
 
        /* we can get the clipboard only after the widget
         * is realized */


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