[evolution/gnome-3-24] Fix drag and drop in composer



commit 79c60b133ddd606005ad6d0c1cfa0deffaea699f
Author: Tomas Popela <tpopela redhat com>
Date:   Thu Mar 9 15:43:01 2017 +0100

    Fix drag and drop in composer
    
    Simplify how we handle the drag and drop. Now the drop is always handled
    by the EContentEditor first and then if the data are processed by the
    editor then it's handed to the EMsgComposer. Also the DOM operations
    are now handled on the web extension side to save some DBus calls.
    
    Correctly handle the history if the drop operation is copying the content
    and not moving it.
    
    Fix the history if we are dragging a content where there is a space
    before the selection start. WebKit will remove it unless there is a
    space after the selection end.
    
    Also there is a weird thing going on and I still don't know if it's caused
    by WebKit or Evolution. If dragging content around the editor sometimes the
    current selection is changed. The problem is that if moving the content,
    then WebKit is removing the currently selected content and at that point it
    could be a different one from the dragged one. So before the drop is
    performed we restore the selection to the state when the drag was initiated.

 src/composer/e-composer-private.c                  |    2 -
 src/composer/e-composer-private.h                  |    4 +-
 src/composer/e-msg-composer.c                      |  295 +++-------------
 src/e-util/e-util-enums.h                          |   24 ++
 src/modules/webkit-editor/e-webkit-editor.c        |  136 +++++++-
 .../web-extension/e-composer-dom-functions.c       |  244 -------------
 .../web-extension/e-composer-dom-functions.h       |    4 -
 .../web-extension/e-editor-dom-functions.c         |  384 ++++++++++++++++++--
 .../web-extension/e-editor-dom-functions.h         |    3 +-
 .../web-extension/e-editor-undo-redo-manager.c     |  106 ++++++-
 .../web-extension/e-editor-undo-redo-manager.h     |    9 +
 .../web-extension/e-editor-web-extension.c         |   57 +--
 12 files changed, 691 insertions(+), 577 deletions(-)
---
diff --git a/src/composer/e-composer-private.c b/src/composer/e-composer-private.c
index 91aef51..caeb272 100644
--- a/src/composer/e-composer-private.c
+++ b/src/composer/e-composer-private.c
@@ -132,8 +132,6 @@ e_composer_private_constructed (EMsgComposer *composer)
        priv->disable_signature = FALSE;
        priv->busy = FALSE;
        priv->saved_editable = FALSE;
-       priv->drop_occured = FALSE;
-       priv->dnd_is_uri = FALSE;
        priv->dnd_history_saved = FALSE;
        priv->check_if_signature_is_changed = FALSE;
        priv->ignore_next_signature_change = FALSE;
diff --git a/src/composer/e-composer-private.h b/src/composer/e-composer-private.h
index b2c50df..72b4d07 100644
--- a/src/composer/e-composer-private.h
+++ b/src/composer/e-composer-private.h
@@ -100,8 +100,6 @@ struct _EMsgComposerPrivate {
         * This is used to restore the previous editable state. */
        gboolean saved_editable;
        gboolean set_signature_from_message;
-       gboolean drop_occured;
-       gboolean dnd_is_uri;
        gboolean is_sending_message;
        gboolean dnd_history_saved;
        gboolean check_if_signature_is_changed;
@@ -119,6 +117,8 @@ struct _EMsgComposerPrivate {
        gulong notify_signature_uid_handler;
        gulong notify_subject_handler;
        gulong notify_subject_changed_handler;
+
+       gulong drag_data_received_handler_id;
 };
 
 void           e_composer_private_constructed  (EMsgComposer *composer);
diff --git a/src/composer/e-msg-composer.c b/src/composer/e-msg-composer.c
index 1268fe1..f7f1e4b 100644
--- a/src/composer/e-msg-composer.c
+++ b/src/composer/e-msg-composer.c
@@ -103,24 +103,14 @@ enum {
        LAST_SIGNAL
 };
 
-enum DndTargetType {
-       DND_TARGET_TYPE_TEXT_URI_LIST,
-       DND_TARGET_TYPE_MOZILLA_URL,
-       DND_TARGET_TYPE_TEXT_HTML,
-       DND_TARGET_TYPE_UTF8_STRING,
-       DND_TARGET_TYPE_TEXT_PLAIN,
-       DND_TARGET_TYPE_STRING,
-       DND_TARGET_TYPE_TEXT_PLAIN_UTF8
-};
-
 static GtkTargetEntry drag_dest_targets[] = {
-       { (gchar *) "text/uri-list", 0, DND_TARGET_TYPE_TEXT_URI_LIST },
-       { (gchar *) "_NETSCAPE_URL", 0, DND_TARGET_TYPE_MOZILLA_URL },
-       { (gchar *) "text/html", 0, DND_TARGET_TYPE_TEXT_HTML },
-       { (gchar *) "UTF8_STRING", 0, DND_TARGET_TYPE_UTF8_STRING },
-       { (gchar *) "text/plain", 0, DND_TARGET_TYPE_TEXT_PLAIN },
-       { (gchar *) "STRING", 0, DND_TARGET_TYPE_STRING },
-       { (gchar *) "text/plain;charset=utf-8", 0, DND_TARGET_TYPE_TEXT_PLAIN_UTF8 },
+       { (gchar *) "text/uri-list", 0, E_DND_TARGET_TYPE_TEXT_URI_LIST },
+       { (gchar *) "_NETSCAPE_URL", 0, E_DND_TARGET_TYPE_MOZILLA_URL },
+       { (gchar *) "text/html", 0, E_DND_TARGET_TYPE_TEXT_HTML },
+       { (gchar *) "UTF8_STRING", 0, E_DND_TARGET_TYPE_UTF8_STRING },
+       { (gchar *) "text/plain", 0, E_DND_TARGET_TYPE_TEXT_PLAIN },
+       { (gchar *) "STRING", 0, E_DND_TARGET_TYPE_STRING },
+       { (gchar *) "text/plain;charset=utf-8", 0, E_DND_TARGET_TYPE_TEXT_PLAIN_UTF8 },
 };
 
 static guint signals[LAST_SIGNAL];
@@ -1783,135 +1773,6 @@ msg_composer_paste_clipboard_cb (EContentEditor *cnt_editor,
 
        return TRUE;
 }
-#if 0 /* FIXME WK2 */
-static gboolean
-msg_composer_drag_motion_cb (GtkWidget *widget,
-                             GdkDragContext *context,
-                             gint x,
-                             gint y,
-                             guint time,
-                             EMsgComposer *composer)
-{
-       GtkWidget *source_widget;
-       EHTMLEditor *editor = e_msg_composer_get_editor (composer);
-       EHTMLEditorView *editor_view = e_html_editor_get_view (editor);
-
-       source_widget = gtk_drag_get_source_widget (context);
-       /* When we are doind DnD just inside the web view, the DnD is supposed
-        * to move things around. */
-       if (E_IS_HTML_EDITOR_VIEW (source_widget)) {
-               if ((gpointer) editor_view == (gpointer) source_widget) {
-                       gdk_drag_status (context, GDK_ACTION_MOVE, time);
-
-                       return FALSE;
-               }
-       }
-
-       gdk_drag_status (context, GDK_ACTION_COPY, time);
-
-       return FALSE;
-}
-
-static gboolean
-msg_composer_drag_drop_cb (GtkWidget *widget,
-                           GdkDragContext *context,
-                           gint x,
-                           gint y,
-                           guint time,
-                           EMsgComposer *composer)
-{
-       GdkAtom target;
-       GtkWidget *source_widget;
-
-       /* When we are doing DnD just inside the web view, the DnD is supposed
-        * to move things around. */
-       source_widget = gtk_drag_get_source_widget (context);
-       if (E_IS_HTML_EDITOR_VIEW (source_widget)) {
-               EHTMLEditor *editor = e_msg_composer_get_editor (composer);
-               EHTMLEditorView *editor_view = e_html_editor_get_view (editor);
-
-               if ((gpointer) editor_view == (gpointer) source_widget) {
-                       GDBusProxy *web_extension;
-
-                       web_extension = e_html_editor_view_get_web_extension_proxy (editor_view);
-                       if (web_extension) {
-                               e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-                                       web_extension,
-                                       "DOMSaveDragAndDropHistory",
-                                       g_variant_new (
-                                               "(t)",
-                                               webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (editor_view))),
-                                       NULL);
-                       }
-                       return FALSE;
-               }
-       }
-
-       target = gtk_drag_dest_find_target (widget, context, NULL);
-       if (target == GDK_NONE)
-               gdk_drag_status (context, 0, time);
-       else {
-               /* Prevent WebKit from pasting the URI of file into the view. Also
-                * prevent it from inserting the text/plain or text/html content as we
-                * want to insert it ourselves. */
-               if (composer->priv->dnd_is_uri || !E_IS_HTML_EDITOR_VIEW (source_widget))
-                       g_signal_stop_emission_by_name (widget, "drag-drop");
-
-               composer->priv->dnd_is_uri = FALSE;
-
-               if (E_IS_HTML_EDITOR_VIEW (source_widget))
-                       gdk_drag_status (context, GDK_ACTION_MOVE, time);
-               else
-                       gdk_drag_status (context, GDK_ACTION_COPY, time);
-
-               composer->priv->drop_occured = TRUE;
-               gtk_drag_get_data (widget, context, target, time);
-
-               return TRUE;
-       }
-
-       return FALSE;
-}
-
-static void
-msg_composer_drag_data_received_after_cb (GtkWidget *widget,
-                                          GdkDragContext *context,
-                                          gint x,
-                                          gint y,
-                                          GtkSelectionData *selection,
-                                          guint info,
-                                          guint time,
-                                          EMsgComposer *composer)
-{
-       EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       GDBusProxy *web_extension;
-
-       if (!composer->priv->drop_occured)
-               goto out;
-
-       /* Save just history for events handled by WebKit. */
-       if (composer->priv->dnd_history_saved)
-               goto out;
-
-       editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       web_extension = e_html_editor_view_get_web_extension_proxy (view);
-       if (!web_extension)
-               goto out;
-
-       e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               web_extension,
-               "DOMCleanAfterDragAndDrop",
-               g_variant_new (
-                       "(t)",
-                       webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (view))),
-               NULL);
-
- out:
-       composer->priv->drop_occured = FALSE;
-       composer->priv->dnd_history_saved = FALSE;
-}
 
 static gchar *
 next_uri (guchar **uri_list,
@@ -1950,90 +1811,16 @@ msg_composer_drag_data_received_cb (GtkWidget *widget,
 {
        EHTMLEditor *editor;
        EContentEditor *cnt_editor;
-       gboolean html_mode, same_widget = FALSE;
-       GtkWidget *source_widget;
+       gboolean html_mode, is_move;
 
        editor = e_msg_composer_get_editor (composer);
        cnt_editor = e_html_editor_get_content_editor (editor);
        html_mode = e_content_editor_get_html_mode (cnt_editor);
 
-       composer->priv->dnd_history_saved = TRUE;
+       g_signal_handler_disconnect (cnt_editor, composer->priv->drag_data_received_handler_id);
+       composer->priv->drag_data_received_handler_id = 0;
 
-       /* When we are doing DnD just inside the web view, the DnD is supposed
-        * to move things around. */
-       source_widget = gtk_drag_get_source_widget (context);
-       if (E_IS_CONTENT_EDITOR (source_widget) &&
-           ((gpointer) cnt_editor == (gpointer) source_widget))
-               same_widget = TRUE;
-
-       /* Leave DnD inside the view on WebKit. */
-       if (composer->priv->drop_occured && same_widget) {
-               gdk_drag_status (context, 0, time);
-               return;
-       }
-
-       if (!composer->priv->drop_occured) {
-               if (!same_widget) {
-                       /* Check if we are DnD'ing some URI, if so WebKit will
-                        * insert the URI into the view and we have to prevent it
-                        * from doing that. */
-                       if (info == DND_TARGET_TYPE_TEXT_URI_LIST) {
-                               gchar **uris;
-
-                               uris = gtk_selection_data_get_uris (selection);
-                               /* I don't know what regressed outside of Evo, but
-                                * this is called twice. Firstly with uris set
-                                * following by one with uris not set. */
-                               if (!composer->priv->dnd_is_uri)
-                                       composer->priv->dnd_is_uri = uris != NULL;
-                               g_strfreev (uris);
-                       }
-               }
-               return;
-       }
-
-       composer->priv->dnd_is_uri = FALSE;
-
-       /* Leave the text on WebKit to handle it. */
-       if (info == DND_TARGET_TYPE_UTF8_STRING ||
-           info == DND_TARGET_TYPE_STRING ||
-           info == DND_TARGET_TYPE_TEXT_PLAIN ||
-           info == DND_TARGET_TYPE_TEXT_PLAIN_UTF8) {
-               composer->priv->dnd_history_saved = FALSE;
-               gdk_drag_status (context, 0, time);
-               return;
-       }
-
-       if (info == DND_TARGET_TYPE_TEXT_HTML) {
-               const guchar *data;
-               gint length;
-               gint list_len, len;
-               gchar *text;
-
-               data = gtk_selection_data_get_data (selection);
-               length = gtk_selection_data_get_length (selection);
-
-               if (!data || length < 0) {
-                       gtk_drag_finish (context, FALSE, FALSE, time);
-                       return;
-               }
-
-               e_content_editor_move_caret_on_coordinates (cnt_editor, x, y, FALSE);
-
-               list_len = length;
-               do {
-                       text = next_uri ((guchar **) &data, &len, &list_len);
-                       e_content_editor_insert_content (
-                               cnt_editor,
-                               text,
-                               E_CONTENT_EDITOR_INSERT_TEXT_HTML);
-                       g_free (text);
-               } while (list_len);
-
-               gtk_drag_finish (context, TRUE, FALSE, time);
-
-               return;
-       }
+       is_move = gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE;
 
        /* HTML mode has a few special cases for drops... */
        /* If we're receiving URIs and -all- the URIs point to
@@ -2064,7 +1851,7 @@ msg_composer_drag_data_received_cb (GtkWidget *widget,
                        g_free (uri);
                } while (list_len);
 
-               gtk_drag_finish (context, TRUE, FALSE, time);
+               gtk_drag_finish (context, TRUE, is_move, time);
        } else {
                EAttachmentView *attachment_view =
                        e_msg_composer_get_attachment_view (composer);
@@ -2077,7 +1864,43 @@ msg_composer_drag_data_received_cb (GtkWidget *widget,
                        context, x, y, selection, info, time);
        }
 }
-#endif
+
+static gboolean
+msg_composer_drag_drop_cb (GtkWidget *widget,
+                           GdkDragContext *context,
+                           gint x,
+                           gint y,
+                           guint time,
+                           EMsgComposer *composer)
+{
+       GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
+
+       if (target == GDK_NONE) {
+               gdk_drag_status (context, 0, time);
+       } else {
+               composer->priv->drag_data_received_handler_id = g_signal_connect (
+                       E_CONTENT_EDITOR (widget), "drag-data-received",
+                       G_CALLBACK (msg_composer_drag_data_received_cb), composer);
+
+               gtk_drag_get_data (widget, context, target, time);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+msg_composer_drag_begin_cb (GtkWidget *widget,
+                            GdkDragContext *context,
+                            EMsgComposer *composer)
+{
+       if (composer->priv->drag_data_received_handler_id != 0) {
+               g_signal_handler_disconnect (E_CONTENT_EDITOR( widget), 
composer->priv->drag_data_received_handler_id);
+               composer->priv->drag_data_received_handler_id = 0;
+       }
+}
+
 static void
 msg_composer_notify_header_cb (EMsgComposer *composer)
 {
@@ -2466,28 +2289,14 @@ msg_composer_constructed (GObject *object)
                G_CALLBACK (msg_composer_paste_primary_clipboard_cb), composer);
 
        /* Drag-and-Drop Support */
-#if 0 /* FIXME WK2 */
-       EHTMLEditorView *view;
-
-       view = e_html_editor_get_view (editor);
-
        g_signal_connect (
-               view, "drag-motion",
-               G_CALLBACK (msg_composer_drag_motion_cb), composer);
-
-        g_signal_connect (
-               view, "drag-drop",
+               cnt_editor, "drag-drop",
                G_CALLBACK (msg_composer_drag_drop_cb), composer);
 
        g_signal_connect (
-               view, "drag-data-received",
-               G_CALLBACK (msg_composer_drag_data_received_cb), composer);
+               cnt_editor, "drag-begin",
+               G_CALLBACK (msg_composer_drag_begin_cb), composer);
 
-       /* Used for fixing various stuff after WebKit processed the DnD data. */
-       g_signal_connect_after (
-               view, "drag-data-received",
-               G_CALLBACK (msg_composer_drag_data_received_after_cb), composer);
-#endif
        g_signal_connect (
                composer->priv->gallery_icon_view, "drag-data-get",
                G_CALLBACK (msg_composer_gallery_drag_data_get), NULL);
diff --git a/src/e-util/e-util-enums.h b/src/e-util/e-util-enums.h
index 6078cb9..75eeeee 100644
--- a/src/e-util/e-util-enums.h
+++ b/src/e-util/e-util-enums.h
@@ -548,6 +548,30 @@ typedef enum {
        E_CLIPBOARD_CAN_PASTE   = 1 << 2 */
 } EClipboardFlags;
 
+/**
+ * EDnDTargetType:
+ * DND_TARGET_TYPE_TEXT_URI_LIST: text/uri-list
+ * DND_TARGET_TYPE_MOZILLA_URL: _NETSCAPE_URL
+ * DND_TARGET_TYPE_TEXT_HTML: text/html
+ * DND_TARGET_TYPE_UTF8_STRING: UTF8_STRING
+ * DND_TARGET_TYPE_TEXT_PLAIN: text/plain
+ * DND_TARGET_TYPE_STRING: STRING
+ * DND_TARGET_TYPE_TEXT_PLAIN_UTF8: text/plain;charser=utf-8
+ *
+ * Drag and drop targets supported by EContentEditor.
+ *
+ * Since: 3.26
+ **/
+typedef enum {
+       E_DND_TARGET_TYPE_TEXT_URI_LIST = 0,
+       E_DND_TARGET_TYPE_MOZILLA_URL,
+       E_DND_TARGET_TYPE_TEXT_HTML,
+       E_DND_TARGET_TYPE_UTF8_STRING,
+       E_DND_TARGET_TYPE_TEXT_PLAIN,
+       E_DND_TARGET_TYPE_STRING,
+       E_DND_TARGET_TYPE_TEXT_PLAIN_UTF8
+} EDnDTargetType;
+
 G_END_DECLS
 
 #endif /* E_UTIL_ENUMS_H */
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index e93c89c..170d47c 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -132,6 +132,9 @@ struct _EWebKitEditorPrivate {
        gulong found_text_handler_id;
        gulong failed_to_find_text_handler_id;
 
+       gboolean performing_drag;
+       gulong drag_data_received_handler_id;
+
        gchar *last_hover_uri;
 };
 
@@ -5770,10 +5773,129 @@ webkit_editor_context_menu_cb (EWebKitEditor *wk_editor,
 }
 
 static void
+webkit_editor_drag_begin_cb (EWebKitEditor *wk_editor,
+                             GdkDragContext *context)
+{
+       wk_editor->priv->performing_drag = TRUE;
+}
+
+static void
+webkit_editor_drag_failed_cb (EWebKitEditor *wk_editor,
+                              GdkDragContext *context,
+                              GtkDragResult result)
+{
+       wk_editor->priv->performing_drag = FALSE;
+}
+
+static void
 webkit_editor_drag_end_cb (EWebKitEditor *wk_editor,
                            GdkDragContext *context)
 {
-       webkit_editor_call_simple_extension_function (wk_editor, "DOMDragAndDropEnd");
+       wk_editor->priv->performing_drag = FALSE;
+}
+
+static gchar *
+next_uri (guchar **uri_list,
+          gint *len,
+          gint *list_len)
+{
+       guchar *uri, *begin;
+
+       begin = *uri_list;
+       *len = 0;
+       while (**uri_list && **uri_list != '\n' && **uri_list != '\r' && *list_len) {
+               (*uri_list) ++;
+               (*len) ++;
+               (*list_len) --;
+       }
+
+       uri = (guchar *) g_strndup ((gchar *) begin, *len);
+
+       while ((!**uri_list || **uri_list == '\n' || **uri_list == '\r') && *list_len) {
+               (*uri_list) ++;
+               (*list_len) --;
+       }
+
+       return (gchar *) uri;
+}
+
+static void
+webkit_editor_drag_data_received_cb (GtkWidget *widget,
+                                     GdkDragContext *context,
+                                     gint x,
+                                     gint y,
+                                     GtkSelectionData *selection,
+                                     guint info,
+                                     guint time)
+{
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (widget);
+       gboolean is_move = FALSE;
+
+       g_signal_handler_disconnect (wk_editor, wk_editor->priv->drag_data_received_handler_id);
+       wk_editor->priv->drag_data_received_handler_id = 0;
+
+       is_move = gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE;
+
+       /* Leave DnD inside the view on WebKit */
+       /* Leave the text on WebKit to handle it. */
+       if (wk_editor->priv->performing_drag ||
+           info == E_DND_TARGET_TYPE_UTF8_STRING || info == E_DND_TARGET_TYPE_STRING ||
+           info == E_DND_TARGET_TYPE_TEXT_PLAIN || info == E_DND_TARGET_TYPE_TEXT_PLAIN_UTF8) {
+               gdk_drag_status (context, gdk_drag_context_get_selected_action(context), time);
+               GTK_WIDGET_CLASS (e_webkit_editor_parent_class)->drag_drop (widget, context, x, y, time);
+               g_signal_stop_emission_by_name (widget, "drag-data-received");
+               if (!is_move)
+                       webkit_editor_call_simple_extension_function (wk_editor, 
"DOMLastDropOperationDidCopy");
+               return;
+       }
+
+       if (info == E_DND_TARGET_TYPE_TEXT_HTML) {
+               const guchar *data;
+               gint length;
+               gint list_len, len;
+               gchar *text;
+
+               data = gtk_selection_data_get_data (selection);
+               length = gtk_selection_data_get_length (selection);
+
+               if (!data || length < 0) {
+                       gtk_drag_finish (context, FALSE, is_move, time);
+                       g_signal_stop_emission_by_name (widget, "drag-data-received");
+                       return;
+               }
+
+               webkit_editor_move_caret_on_coordinates (E_CONTENT_EDITOR (widget), x, y, FALSE);
+
+               list_len = length;
+               do {
+                       text = next_uri ((guchar **) &data, &len, &list_len);
+                       webkit_editor_insert_content (
+                               E_CONTENT_EDITOR (wk_editor),
+                               text,
+                               E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+                       g_free (text);
+               } while (list_len);
+
+               gtk_drag_finish (context, TRUE, is_move, time);
+               g_signal_stop_emission_by_name (widget, "drag-data-received");
+               return;
+       }
+}
+
+static gboolean
+webkit_editor_drag_drop_cb (EWebKitEditor *wk_editor,
+                            GdkDragContext *context,
+                            gint x,
+                            gint y,
+                            guint time)
+{
+       wk_editor->priv->drag_data_received_handler_id = g_signal_connect (
+               wk_editor, "drag-data-received",
+               G_CALLBACK (webkit_editor_drag_data_received_cb), NULL);
+
+       webkit_editor_set_changed (wk_editor, TRUE);
+
+       return FALSE;
 }
 
 static void
@@ -6077,10 +6199,22 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
                G_CALLBACK (webkit_editor_mouse_target_changed_cb), NULL);
 
        g_signal_connect (
+               wk_editor, "drag-begin",
+               G_CALLBACK (webkit_editor_drag_begin_cb), NULL);
+
+       g_signal_connect (
+               wk_editor, "drag-failed",
+               G_CALLBACK (webkit_editor_drag_failed_cb), NULL);
+
+       g_signal_connect (
                wk_editor, "drag-end",
                G_CALLBACK (webkit_editor_drag_end_cb), NULL);
 
        g_signal_connect (
+               wk_editor, "drag-drop",
+               G_CALLBACK (webkit_editor_drag_drop_cb), NULL);
+
+       g_signal_connect (
                wk_editor, "web-process-crashed",
                G_CALLBACK (webkit_editor_web_process_crashed_cb), NULL);
 
diff --git a/src/modules/webkit-editor/web-extension/e-composer-dom-functions.c 
b/src/modules/webkit-editor/web-extension/e-composer-dom-functions.c
index a200680..1544081 100644
--- a/src/modules/webkit-editor/web-extension/e-composer-dom-functions.c
+++ b/src/modules/webkit-editor/web-extension/e-composer-dom-functions.c
@@ -589,247 +589,3 @@ e_composer_dom_get_raw_body_content (EEditorPage *editor_page)
 
        return  webkit_dom_html_element_get_inner_text (body);
 }
-
-static void
-insert_nbsp_history_event (WebKitDOMDocument *document,
-                           EEditorUndoRedoManager *manager,
-                           gboolean delete,
-                           guint x,
-                           guint y)
-{
-       EEditorHistoryEvent *event;
-       WebKitDOMDocumentFragment *fragment;
-
-       event = g_new0 (EEditorHistoryEvent, 1);
-       event->type = HISTORY_AND;
-       e_editor_undo_redo_manager_insert_history_event (manager, event);
-
-       fragment = webkit_dom_document_create_document_fragment (document);
-       webkit_dom_node_append_child (
-               WEBKIT_DOM_NODE (fragment),
-               WEBKIT_DOM_NODE (
-                       webkit_dom_document_create_text_node (document, UNICODE_NBSP)),
-               NULL);
-
-       event = g_new0 (EEditorHistoryEvent, 1);
-       event->type = HISTORY_DELETE;
-
-       if (delete)
-               g_object_set_data (G_OBJECT (fragment), "history-delete-key", GINT_TO_POINTER (1));
-
-       event->data.fragment = fragment;
-
-       event->before.start.x = x;
-       event->before.start.y = y;
-       event->before.end.x = x;
-       event->before.end.y = y;
-
-       event->after.start.x = x;
-       event->after.start.y = y;
-       event->after.end.x = x;
-       event->after.end.y = y;
-
-       e_editor_undo_redo_manager_insert_history_event (manager, event);
-}
-
-void
-e_composer_dom_save_drag_and_drop_history (EEditorPage *editor_page)
-{
-       WebKitDOMDocument *document;
-       WebKitDOMDocumentFragment *fragment;
-       WebKitDOMDOMSelection *dom_selection = NULL;
-       WebKitDOMDOMWindow *dom_window = NULL;
-       WebKitDOMRange *beginning_of_line = NULL;
-       WebKitDOMRange *range = NULL, *range_clone = NULL;
-       EEditorHistoryEvent *event;
-       EEditorUndoRedoManager *manager;
-       gboolean start_to_start, end_to_end;
-       gchar *range_text;
-       guint x, y;
-
-       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
-
-       document = e_editor_page_get_document (editor_page);
-       manager = e_editor_page_get_undo_redo_manager (editor_page);
-
-       if (!(dom_window = webkit_dom_document_get_default_view (document)))
-               return;
-
-       if (!(dom_selection = webkit_dom_dom_window_get_selection (dom_window))) {
-               g_clear_object (&dom_window);
-               return;
-       }
-
-       g_clear_object (&dom_window);
-
-       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
-               g_clear_object (&dom_selection);
-               return;
-       }
-
-       /* Obtain the dragged content. */
-       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-       range_clone = webkit_dom_range_clone_range (range, NULL);
-
-       /* Create the history event for the content that will
-        * be removed by DnD. */
-       event = g_new0 (EEditorHistoryEvent, 1);
-       event->type = HISTORY_DELETE;
-
-       e_editor_dom_selection_get_coordinates (editor_page,
-               &event->before.start.x,
-               &event->before.start.y,
-               &event->before.end.x,
-               &event->before.end.y);
-
-       x = event->before.start.x;
-       y = event->before.start.y;
-
-       event->after.start.x = x;
-       event->after.start.y = y;
-       event->after.end.x = x;
-       event->after.end.y = y;
-
-       /* Save the content that will be removed. */
-       fragment = webkit_dom_range_clone_contents (range_clone, NULL);
-
-       /* Extend the cloned range to point one character after
-        * the selection ends to later check if there is a whitespace
-        * after it. */
-       webkit_dom_range_set_end (
-               range_clone,
-               webkit_dom_range_get_end_container (range_clone, NULL),
-               webkit_dom_range_get_end_offset (range_clone, NULL) + 1,
-               NULL);
-       range_text = webkit_dom_range_get_text (range_clone);
-
-       /* Check if the current selection starts on the beginning
-        * of line. */
-       webkit_dom_dom_selection_modify (
-               dom_selection, "extend", "left", "lineboundary");
-       beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-       start_to_start = webkit_dom_range_compare_boundary_points (
-               beginning_of_line, 0 /* START_TO_START */, range, NULL) == 0;
-
-       /* Restore the selection to state before the check. */
-       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
-       webkit_dom_dom_selection_add_range (dom_selection, range);
-       g_clear_object (&beginning_of_line);
-
-       /* Check if the current selection end on the end of the line. */
-       webkit_dom_dom_selection_modify (
-               dom_selection, "extend", "right", "lineboundary");
-       beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-       end_to_end = webkit_dom_range_compare_boundary_points (
-               beginning_of_line, 2 /* END_TO_END */, range, NULL) == 0;
-
-       /* Dragging the whole line. */
-       if (start_to_start && end_to_end) {
-               WebKitDOMNode *container, *actual_block, *tmp_block;
-
-               /* Select the whole line (to the beginning of the next
-                * one so we can reuse the undo code while undoing this.
-                * Because of this we need to special mark the event
-                * with history-drag-and-drop to correct the selection
-                * after undoing it (otherwise the beginning of the next
-                * line will be selected as well. */
-               webkit_dom_dom_selection_modify (
-                       dom_selection, "extend", "right", "character");
-               g_clear_object (&beginning_of_line);
-               beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-
-               container = webkit_dom_range_get_end_container (range, NULL);
-               actual_block = e_editor_dom_get_parent_block_node_from_child (container);
-
-               tmp_block = webkit_dom_range_get_end_container (beginning_of_line, NULL);
-               if ((tmp_block = e_editor_dom_get_parent_block_node_from_child (tmp_block))) {
-                       e_editor_dom_selection_get_coordinates (editor_page,
-                               &event->before.start.x,
-                               &event->before.start.y,
-                               &event->before.end.x,
-                               &event->before.end.y);
-
-                       /* Create the right content for the history event. */
-                       fragment = webkit_dom_document_create_document_fragment (document);
-                       /* The removed line. */
-                       webkit_dom_node_append_child (
-                               WEBKIT_DOM_NODE (fragment),
-                               webkit_dom_node_clone_node_with_error (actual_block, TRUE, NULL),
-                               NULL);
-                       /* The following block, but empty. */
-                       webkit_dom_node_append_child (
-                               WEBKIT_DOM_NODE (fragment),
-                               webkit_dom_node_clone_node_with_error (tmp_block, FALSE, NULL),
-                               NULL);
-                       g_object_set_data (
-                               G_OBJECT (fragment),
-                               "history-drag-and-drop",
-                               GINT_TO_POINTER (1));
-                       /* It should act as a Delete key press. */
-                       g_object_set_data (
-                               G_OBJECT (fragment),
-                               "history-delete-key",
-                               GINT_TO_POINTER (1));
-               }
-       }
-
-       event->data.fragment = fragment;
-       e_editor_undo_redo_manager_insert_history_event (manager, event);
-
-       /* Selection is ending on the end of the line, check if
-        * there is a space before the selection start. If so, it
-        * will be removed and we need create the history event
-        * for it. */
-       if (end_to_end) {
-               gchar *range_text_start;
-               glong start_offset;
-
-               start_offset = webkit_dom_range_get_start_offset (range_clone, NULL);
-               webkit_dom_range_set_start (
-                       range_clone,
-                       webkit_dom_range_get_start_container (range_clone, NULL),
-                       start_offset > 0 ? start_offset - 1 : 0,
-                       NULL);
-
-               range_text_start = webkit_dom_range_get_text (range_clone);
-               if (g_str_has_prefix (range_text_start, " ") ||
-                   g_str_has_prefix (range_text_start, UNICODE_NBSP))
-                       insert_nbsp_history_event (document, manager, FALSE, x, y);
-
-               g_free (range_text_start);
-       }
-
-       /* WebKit removes the space (if presented) after selection and
-        * we need to create a new history event for it. */
-       if (g_str_has_suffix (range_text, " ") ||
-           g_str_has_suffix (range_text, UNICODE_NBSP))
-               insert_nbsp_history_event (document, manager, TRUE, x, y);
-
-       g_free (range_text);
-
-       /* Restore the selection to original state. */
-       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
-       webkit_dom_dom_selection_add_range (dom_selection, range);
-       g_clear_object (&beginning_of_line);
-
-       /* All the things above were about removing the content,
-        * create an AND event to continue later with inserting
-        * the dropped content. */
-       event = g_new0 (EEditorHistoryEvent, 1);
-       event->type = HISTORY_AND;
-       e_editor_undo_redo_manager_insert_history_event (manager, event);
-
-       g_clear_object (&dom_selection);
-
-       g_clear_object (&range);
-       g_clear_object (&range_clone);
-}
-
-void
-e_composer_dom_clean_after_drag_and_drop (EEditorPage *editor_page)
-{
-       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
-
-       e_editor_dom_save_history_for_drop (editor_page);
-       e_editor_dom_check_magic_links (editor_page, FALSE);
-}
diff --git a/src/modules/webkit-editor/web-extension/e-composer-dom-functions.h 
b/src/modules/webkit-editor/web-extension/e-composer-dom-functions.h
index 8e0f934..a204346 100644
--- a/src/modules/webkit-editor/web-extension/e-composer-dom-functions.h
+++ b/src/modules/webkit-editor/web-extension/e-composer-dom-functions.h
@@ -38,10 +38,6 @@ gchar *              e_composer_dom_get_raw_body_content_without_signature
                                                (EEditorPage *editor_page);
 gchar *                e_composer_dom_get_raw_body_content
                                                (EEditorPage *editor_page);
-void           e_composer_dom_save_drag_and_drop_history
-                                               (EEditorPage *editor_page);
-void           e_composer_dom_clean_after_drag_and_drop
-                                               (EEditorPage *editor_page);
 
 G_END_DECLS
 
diff --git a/src/modules/webkit-editor/web-extension/e-editor-dom-functions.c 
b/src/modules/webkit-editor/web-extension/e-editor-dom-functions.c
index 8594213..e9e6df3 100644
--- a/src/modules/webkit-editor/web-extension/e-editor-dom-functions.c
+++ b/src/modules/webkit-editor/web-extension/e-editor-dom-functions.c
@@ -5749,6 +5749,94 @@ body_compositionend_event_cb (WebKitDOMElement *element,
 }
 
 static void
+body_drop_event_cb (WebKitDOMElement *element,
+                    WebKitDOMUIEvent *event,
+                    EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_page_is_pasting_content_from_itself (editor_page)) {
+               EEditorUndoRedoManager *manager;
+               EEditorHistoryEvent *and_event, *event;
+
+               /* There is a weird thing going on and I still don't know if it's
+                * caused by WebKit or Evolution. If dragging content around the
+                * editor sometimes the current selection is changed. The problem
+                * is that if moving the content, then WebKit is removing the
+                * currently selected content and at that point it could be a
+                * different one from the dragged one. So before the drop is
+                * performed we restore the selection to the state when the
+                * drag was initiated. */
+               manager = e_editor_page_get_undo_redo_manager (editor_page);
+               and_event = e_editor_undo_redo_manager_get_current_history_event (manager);
+               while (and_event && and_event->type == HISTORY_AND) {
+                       event = e_editor_undo_redo_manager_get_next_history_event_for (manager, and_event);
+                       and_event = e_editor_undo_redo_manager_get_next_history_event_for (manager, event);
+               }
+
+               if (event)
+                       e_editor_dom_selection_restore_to_history_event_state (editor_page, event->before);
+
+               e_editor_dom_save_history_for_drop (editor_page);
+       }
+}
+
+static void
+body_dragstart_event_cb (WebKitDOMElement *element,
+                         WebKitDOMUIEvent *event,
+                         EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       e_editor_dom_remove_input_event_listener_from_body (editor_page);
+       e_editor_page_set_pasting_content_from_itself (editor_page, TRUE);
+       e_editor_dom_save_history_for_drag (editor_page);
+}
+
+static void
+body_dragend_event_cb (WebKitDOMElement *element,
+                       WebKitDOMUIEvent *event,
+                       EEditorPage *editor_page)
+{
+       EEditorHistoryEvent *ev;
+       EEditorUndoRedoManager *manager;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (e_editor_page_is_pasting_content_from_itself (editor_page) &&
+          (ev = e_editor_undo_redo_manager_get_current_history_event (manager))) {
+               if (ev->type == HISTORY_INSERT_HTML &&
+                   ev->after.start.x == 0 && ev->after.start.y == 0 &&
+                   ev->after.end.x == 0 && ev->after.end.y == 0) {
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &ev->after.start.x,
+                               &ev->after.start.y,
+                               &ev->after.end.x,
+                               &ev->after.end.y);
+                       ev->before.start.x = ev->after.start.x;
+                       ev->before.start.y = ev->after.start.y;
+                       ev->before.end.x = ev->after.start.x;
+                       ev->before.end.y = ev->after.start.y;
+                       e_editor_dom_force_spell_check_in_viewport (editor_page);
+               } else {
+                       /* Drag and Drop was cancelled */
+                       while (ev && ev->type == HISTORY_AND) {
+                               e_editor_undo_redo_manager_remove_current_history_event (manager);
+                               ev = e_editor_undo_redo_manager_get_current_history_event (manager);
+                               /* Basically the same as in body_drop_event_cb().  See the comment there. */
+                               e_editor_dom_selection_restore_to_history_event_state (editor_page, 
ev->before);
+                               e_editor_undo_redo_manager_remove_current_history_event (manager);
+                               ev = e_editor_undo_redo_manager_get_current_history_event (manager);
+                       }
+               }
+       }
+
+       e_editor_page_set_pasting_content_from_itself (editor_page, FALSE);
+       e_editor_dom_register_input_event_listener_on_body (editor_page);
+}
+
+static void
 register_html_events_handlers (EEditorPage *editor_page,
                                WebKitDOMHTMLElement *body)
 {
@@ -5788,6 +5876,27 @@ register_html_events_handlers (EEditorPage *editor_page,
                G_CALLBACK (body_compositionend_event_cb),
                FALSE,
                editor_page);
+
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (body),
+               "drop",
+               G_CALLBACK (body_drop_event_cb),
+               FALSE,
+               editor_page);
+
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (body),
+               "dragstart",
+               G_CALLBACK (body_dragstart_event_cb),
+               FALSE,
+               editor_page);
+
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (body),
+               "dragend",
+               G_CALLBACK (body_dragend_event_cb),
+               FALSE,
+               editor_page);
 }
 
 void
@@ -10838,6 +10947,240 @@ e_editor_dom_get_caret_position (EEditorPage *editor_page)
        return ret_val;
 }
 
+static void
+insert_nbsp_history_event (WebKitDOMDocument *document,
+                           EEditorUndoRedoManager *manager,
+                           gboolean delete,
+                           guint x,
+                           guint y)
+{
+       EEditorHistoryEvent *event;
+       WebKitDOMDocumentFragment *fragment;
+
+       event = g_new0 (EEditorHistoryEvent, 1);
+       event->type = HISTORY_AND;
+       e_editor_undo_redo_manager_insert_history_event (manager, event);
+
+       fragment = webkit_dom_document_create_document_fragment (document);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (fragment),
+               WEBKIT_DOM_NODE (
+                       webkit_dom_document_create_text_node (document, UNICODE_NBSP)),
+               NULL);
+
+       event = g_new0 (EEditorHistoryEvent, 1);
+       event->type = HISTORY_DELETE;
+
+       if (delete)
+               g_object_set_data (G_OBJECT (fragment), "history-delete-key", GINT_TO_POINTER (1));
+
+       event->data.fragment = fragment;
+
+       event->before.start.x = x;
+       event->before.start.y = y;
+       event->before.end.x = x;
+       event->before.end.y = y;
+
+       event->after.start.x = x;
+       event->after.start.y = y;
+       event->after.end.x = x;
+       event->after.end.y = y;
+
+       e_editor_undo_redo_manager_insert_history_event (manager, event);
+}
+void
+e_editor_dom_save_history_for_drag (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMRange *beginning_of_line = NULL;
+       WebKitDOMRange *range = NULL, *range_clone = NULL;
+       EEditorHistoryEvent *event;
+       EEditorUndoRedoManager *manager;
+       gboolean start_to_start = FALSE, end_to_end = FALSE;
+       gchar *range_text;
+       guint x, y;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       if (!(dom_window = webkit_dom_document_get_default_view (document)))
+               return;
+
+       if (!(dom_selection = webkit_dom_dom_window_get_selection (dom_window))) {
+               g_clear_object (&dom_window);
+               return;
+       }
+
+       g_clear_object (&dom_window);
+
+       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
+               g_clear_object (&dom_selection);
+               return;
+       }
+
+       /* Obtain the dragged content. */
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       range_clone = webkit_dom_range_clone_range (range, NULL);
+
+       /* Create the history event for the content that will
+        * be removed by DnD. */
+       event = g_new0 (EEditorHistoryEvent, 1);
+       event->type = HISTORY_DELETE;
+
+       e_editor_dom_selection_get_coordinates (editor_page,
+               &event->before.start.x,
+               &event->before.start.y,
+               &event->before.end.x,
+               &event->before.end.y);
+
+       x = event->before.start.x;
+       y = event->before.start.y;
+
+       event->after.start.x = x;
+       event->after.start.y = y;
+       event->after.end.x = x;
+       event->after.end.y = y;
+
+       /* Save the content that will be removed. */
+       fragment = webkit_dom_range_clone_contents (range_clone, NULL);
+
+       /* Extend the cloned range to point one character after
+        * the selection ends to later check if there is a whitespace
+        * after it. */
+       webkit_dom_range_set_end (
+               range_clone,
+               webkit_dom_range_get_end_container (range_clone, NULL),
+               webkit_dom_range_get_end_offset (range_clone, NULL) + 1,
+               NULL);
+       range_text = webkit_dom_range_get_text (range_clone);
+
+       /* Check if the current selection starts on the beginning of line. */
+       webkit_dom_dom_selection_modify (
+               dom_selection, "extend", "left", "lineboundary");
+       beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       start_to_start = webkit_dom_range_compare_boundary_points (
+               beginning_of_line, WEBKIT_DOM_RANGE_START_TO_START, range, NULL) == 0;
+
+       /* Restore the selection to state before the check. */
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+       g_clear_object (&beginning_of_line);
+
+       /* Check if the current selection end on the end of the line. */
+       webkit_dom_dom_selection_modify (
+               dom_selection, "extend", "right", "lineboundary");
+       beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       end_to_end = webkit_dom_range_compare_boundary_points (
+               beginning_of_line, WEBKIT_DOM_RANGE_END_TO_END, range, NULL) == 0;
+
+       /* Dragging the whole line. */
+       if (start_to_start && end_to_end) {
+               WebKitDOMNode *container, *actual_block, *tmp_block;
+
+               /* Select the whole line (to the beginning of the next
+                * one so we can reuse the undo code while undoing this.
+                * Because of this we need to special mark the event
+                * with history-drag-and-drop to correct the selection
+                * after undoing it (otherwise the beginning of the next
+                * line will be selected as well. */
+               webkit_dom_dom_selection_modify (
+                       dom_selection, "extend", "right", "character");
+               g_clear_object (&beginning_of_line);
+               beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+               container = webkit_dom_range_get_end_container (range, NULL);
+               actual_block = e_editor_dom_get_parent_block_node_from_child (container);
+
+               tmp_block = webkit_dom_range_get_end_container (beginning_of_line, NULL);
+               if ((tmp_block = e_editor_dom_get_parent_block_node_from_child (tmp_block))) {
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &event->before.start.x,
+                               &event->before.start.y,
+                               &event->before.end.x,
+                               &event->before.end.y);
+
+                       /* Create the right content for the history event. */
+                       fragment = webkit_dom_document_create_document_fragment (document);
+                       /* The removed line. */
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               webkit_dom_node_clone_node_with_error (actual_block, TRUE, NULL),
+                               NULL);
+                       /* The following block, but empty. */
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               webkit_dom_node_clone_node_with_error (tmp_block, FALSE, NULL),
+                               NULL);
+                       g_object_set_data (
+                               G_OBJECT (fragment),
+                               "history-drag-and-drop",
+                               GINT_TO_POINTER (1));
+               }
+       }
+       /* It should act as a Delete key press. */
+       g_object_set_data (G_OBJECT (fragment), "history-delete-key", GINT_TO_POINTER (1));
+
+       event->data.fragment = fragment;
+       e_editor_undo_redo_manager_insert_history_event (manager, event);
+
+       /* WebKit removes the space (if presented) after selection and
+        * we need to create a new history event for it. */
+       if (g_str_has_suffix (range_text, " ") ||
+           g_str_has_suffix (range_text, UNICODE_NBSP))
+               insert_nbsp_history_event (document, manager, TRUE, x, y);
+       else {
+               /* If there is a space before the selection WebKit will remove
+                * it as well unless there is a space after the selection. */
+               gchar *range_text_start;
+               glong start_offset;
+
+               start_offset = webkit_dom_range_get_start_offset (range_clone, NULL);
+               webkit_dom_range_set_start (
+                       range_clone,
+                       webkit_dom_range_get_start_container (range_clone, NULL),
+                       start_offset > 0 ? start_offset - 1 : 0,
+                       NULL);
+
+               range_text_start = webkit_dom_range_get_text (range_clone);
+               if (g_str_has_prefix (range_text_start, " ") ||
+                   g_str_has_prefix (range_text_start, UNICODE_NBSP)) {
+                       if (!end_to_end) {
+                               webkit_dom_dom_selection_collapse_to_start (dom_selection, NULL);
+                               webkit_dom_dom_selection_modify (
+                                       dom_selection, "move", "backward", "character");
+                               e_editor_dom_selection_get_coordinates (editor_page, &x, &y, &x, &y);
+                       }
+                       insert_nbsp_history_event (document, manager, TRUE, x, y);
+               }
+
+               g_free (range_text_start);
+       }
+
+       g_free (range_text);
+
+       /* Restore the selection to original state. */
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+       g_clear_object (&beginning_of_line);
+
+       /* All the things above were about removing the content,
+        * create an AND event to continue later with inserting
+        * the dropped content. */
+       event = g_new0 (EEditorHistoryEvent, 1);
+       event->type = HISTORY_AND;
+       e_editor_undo_redo_manager_insert_history_event (manager, event);
+
+       g_clear_object (&dom_selection);
+
+       g_clear_object (&range);
+       g_clear_object (&range_clone);
+}
+
 void
 e_editor_dom_save_history_for_drop (EEditorPage *editor_page)
 {
@@ -10885,14 +11228,6 @@ e_editor_dom_save_history_for_drop (EEditorPage *editor_page)
 
        range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
 
-       /* Remove the last inserted history event as this one was inserted in
-        * body_input_event_cb and is wrong as its type is HISTORY_INPUT. */
-       /* FIXME we could probably disable the HTML input event callback while
-        * doing DnD within the view */
-       event = e_editor_undo_redo_manager_get_current_history_event (manager);
-       if (event && event->type == HISTORY_INPUT)
-               e_editor_undo_redo_manager_remove_current_history_event (manager);
-
        event = g_new0 (EEditorHistoryEvent, 1);
        event->type = HISTORY_INSERT_HTML;
 
@@ -10902,28 +11237,8 @@ e_editor_dom_save_history_for_drop (EEditorPage *editor_page)
        /* Get the HTML content of the dropped content. */
        event->data.string.to = dom_get_node_inner_html (WEBKIT_DOM_NODE (fragment));
 
-       e_editor_dom_selection_get_coordinates (editor_page,
-               &event->before.start.x,
-               &event->before.start.y,
-               &event->before.end.x,
-               &event->before.end.y);
-
-       event->before.end.x = event->before.start.x;
-       event->before.end.y = event->before.start.y;
-
-       if (length > 0)
-               webkit_dom_dom_selection_collapse_to_start (dom_selection, NULL);
-       else
-               webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
-
-       e_editor_dom_selection_get_coordinates (editor_page,
-               &event->after.start.x,
-               &event->after.start.y,
-               &event->after.end.x,
-               &event->after.end.y);
-
        e_editor_undo_redo_manager_insert_history_event (manager, event);
-
+#if 0 /* FIXME Not exactly sure if it is still needed */
        if (!e_editor_page_get_html_mode (editor_page)) {
                list = webkit_dom_document_query_selector_all (
                        document, "span[style^=font-family]", NULL);
@@ -10949,21 +11264,12 @@ e_editor_dom_save_history_for_drop (EEditorPage *editor_page)
                if (length > 0)
                        e_editor_dom_selection_restore (editor_page);
        }
-
-       e_editor_dom_force_spell_check_in_viewport (editor_page);
+#endif
 
        g_clear_object (&range);
        g_clear_object (&dom_selection);
 }
 
-void
-e_editor_dom_drag_and_drop_end (EEditorPage *editor_page)
-{
-       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
-
-       e_editor_dom_save_history_for_drop (editor_page);
-}
-
 static void
 dom_set_link_color_in_document (EEditorPage *editor_page,
                                 const gchar *color,
diff --git a/src/modules/webkit-editor/web-extension/e-editor-dom-functions.h 
b/src/modules/webkit-editor/web-extension/e-editor-dom-functions.h
index ff38f73..226cde1 100644
--- a/src/modules/webkit-editor/web-extension/e-editor-dom-functions.h
+++ b/src/modules/webkit-editor/web-extension/e-editor-dom-functions.h
@@ -159,7 +159,6 @@ void                e_editor_dom_process_content_after_mode_change
                                                (EEditorPage *editor_page);
 guint          e_editor_dom_get_caret_offset   (EEditorPage *editor_page);
 guint          e_editor_dom_get_caret_position (EEditorPage *editor_page);
-void           e_editor_dom_drag_and_drop_end  (EEditorPage *editor_page);
 void           e_editor_dom_set_link_color     (EEditorPage *editor_page,
                                                 const gchar *color);
 void           e_editor_dom_set_visited_link_color
@@ -209,6 +208,8 @@ WebKitDOMElement *
                                                 WebKitDOMElement *element);
 gint           e_editor_dom_get_citation_level (WebKitDOMNode *node,
                                                 gboolean set_plaintext_quoted);
+void           e_editor_dom_save_history_for_drag
+                                               (EEditorPage *editor_page);
 void           e_editor_dom_save_history_for_drop
                                                (EEditorPage *editor_page);
 void           e_editor_dom_fix_file_uri_images
diff --git a/src/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.c 
b/src/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.c
index 9bd1fa0..541ff49 100644
--- a/src/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.c
+++ b/src/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.c
@@ -1476,7 +1476,9 @@ undo_redo_paste (EEditorPage *editor_page,
                                NULL);
 
                        e_editor_dom_selection_restore (editor_page);
-               } else {
+               } else if (event->after.start.x == event->after.end.x &&
+                          event->after.start.y == event->after.end.y) {
+                       /* Selection was collapsed after the event */
                        WebKitDOMDOMWindow *dom_window = NULL;
                        WebKitDOMDOMSelection *dom_selection = NULL;
                        WebKitDOMElement *element, *tmp;
@@ -1525,6 +1527,12 @@ undo_redo_paste (EEditorPage *editor_page,
                        e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DELETE, NULL);
 
                        e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+               } else {
+                       e_editor_dom_selection_restore_to_history_event_state (editor_page, event->after);
+
+                       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DELETE, NULL);
+
+                       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
                }
        } else {
                e_editor_dom_selection_restore_to_history_event_state (editor_page, event->before);
@@ -1538,6 +1546,8 @@ undo_redo_paste (EEditorPage *editor_page,
                else
                        e_editor_dom_convert_and_insert_html_into_selection (editor_page, 
event->data.string.to, FALSE);
                        /* e_editor_selection_insert_as_text (selection, event->data.string.to); */
+
+               e_editor_dom_selection_restore_to_history_event_state (editor_page, event->after);
        }
 }
 
@@ -2269,6 +2279,21 @@ e_editor_undo_redo_manager_get_current_history_event (EEditorUndoRedoManager *ma
        return NULL;
 }
 
+EEditorHistoryEvent *
+e_editor_undo_redo_manager_get_next_history_event_for (EEditorUndoRedoManager *manager,
+                                                       EEditorHistoryEvent *event)
+{
+       g_return_val_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager), NULL);
+
+       if (manager->priv->history) {
+               GList *item = g_list_find (manager->priv->history, event);
+               if (item && item->next)
+                       return item->next->data;
+       }
+
+       return NULL;
+}
+
 void
 e_editor_undo_redo_manager_remove_current_history_event (EEditorUndoRedoManager *manager)
 {
@@ -2363,6 +2388,85 @@ e_editor_undo_redo_manager_insert_dash_history_event (EEditorUndoRedoManager *ma
        g_object_unref (editor_page);
 }
 
+static void
+copy_event_coordinates_to_event (EEditorHistoryEvent *source,
+                                 EEditorHistoryEvent *target)
+{
+       target->before.start.x = source->before.start.x;
+       target->before.start.y = source->before.start.y;
+       target->before.end.x = source->before.end.x;
+       target->before.end.y = source->before.end.y;
+       target->after.start.x = source->after.start.x;
+       target->after.start.y = source->after.start.y;
+       target->after.end.x = source->after.end.x;
+       target->after.end.y = source->after.end.y;
+}
+
+void
+e_editor_undo_redo_manager_last_drop_operation_did_copy (EEditorUndoRedoManager *manager)
+{
+       EEditorPage *editor_page;
+       GList *history;
+
+       g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager));
+
+       editor_page = editor_undo_redo_manager_ref_editor_page (manager);
+       g_return_if_fail (editor_page != NULL);
+
+       history = manager->priv->history;
+       if (history) {
+               GList *history_and, *history_delete;
+               EEditorHistoryEvent *original_insert, *item;
+               WebKitDOMDocumentFragment *fragment;
+
+               /* When a drag operation within an editor is performed, we save
+                * the history for it. We always assume that the drop will move
+                * the content (the default action) and not copy it, thus the
+                * history contains one delete item and one 'and' item. If the
+                * action is changed to copy (by holding the Control key during
+                * drop) there is not content deleted, but we saved it to the
+                * history. What this function does is that it changes the
+                * history to not delete any content, but to only insert the
+                * dropped one. */
+               original_insert = history->data;
+               if (original_insert->type != HISTORY_INSERT_HTML || !history->next) {
+                       g_object_unref (editor_page);
+                       return;
+               }
+
+               history_and = history->next;
+               item = history_and->data;
+               if (item->type != HISTORY_AND || !history_and->next) {
+                       g_object_unref (editor_page);
+                       return;
+               }
+
+               history_delete = history_and->next;
+               item = history_delete->data;
+               if (item->type != HISTORY_DELETE) {
+                       g_object_unref (editor_page);
+                       return;
+               }
+
+               /* Change the history type from 'Delete' to 'Insert' */
+               item->type = HISTORY_INSERT_HTML;
+               copy_event_coordinates_to_event (original_insert, item);
+
+               /* Copy the content */
+               fragment = item->data.fragment;
+               item->data.fragment = NULL;
+               item->data.string.to = dom_get_node_inner_html (WEBKIT_DOM_NODE (fragment));
+               g_clear_object (&fragment);
+
+               /* Remove the old insert event */
+               remove_history_event (manager, manager->priv->history);
+               /* And the 'AND' event */
+               remove_history_event (manager, manager->priv->history);
+       }
+
+       g_object_unref (editor_page);
+}
+
 gboolean
 e_editor_undo_redo_manager_can_undo (EEditorUndoRedoManager *manager)
 {
diff --git a/src/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.h 
b/src/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.h
index 60dabaf..0ed08d7 100644
--- a/src/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.h
+++ b/src/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.h
@@ -151,12 +151,21 @@ void              e_editor_undo_redo_manager_insert_history_event
 EEditorHistoryEvent *
                e_editor_undo_redo_manager_get_current_history_event
                                                (EEditorUndoRedoManager *manager);
+
+EEditorHistoryEvent *
+               e_editor_undo_redo_manager_get_next_history_event_for
+                                               (EEditorUndoRedoManager *manager,
+                                                EEditorHistoryEvent *event);
+
 void           e_editor_undo_redo_manager_remove_current_history_event
                                                (EEditorUndoRedoManager *manager);
 
 void           e_editor_undo_redo_manager_insert_dash_history_event
                                                (EEditorUndoRedoManager *manager);
 
+void           e_editor_undo_redo_manager_last_drop_operation_did_copy
+                                               (EEditorUndoRedoManager *manager);
+
 gboolean       e_editor_undo_redo_manager_can_undo
                                                (EEditorUndoRedoManager *manager);
 
diff --git a/src/modules/webkit-editor/web-extension/e-editor-web-extension.c 
b/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
index 15db265..8f27eee 100644
--- a/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
+++ b/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
@@ -478,9 +478,6 @@ static const gchar *introspection_xml =
 "      <arg type='s' name='selector' direction='in'/>"
 "      <arg type='s' name='uri' direction='in'/>"
 "    </method>"
-"    <method name='DOMDragAndDropEnd'>"
-"      <arg type='t' name='page_id' direction='in'/>"
-"    </method>"
 "    <method name='DOMMoveSelectionOnPoint'>"
 "      <arg type='t' name='page_id' direction='in'/>"
 "      <arg type='i' name='x' direction='in'/>"
@@ -491,6 +488,9 @@ static const gchar *introspection_xml =
 "      <arg type='t' name='page_id' direction='in'/>"
 "      <arg type='s' name='smiley_name' direction='in'/>"
 "    </method>"
+"    <method name='DOMLastDropOperationDidCopy'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
 "<!-- ********************************************************* -->"
 "<!--     Functions that are used in EEditorSelection       -->"
 "<!-- ********************************************************* -->"
@@ -579,12 +579,6 @@ static const gchar *introspection_xml =
 "      <arg type='b' name='out_check_if_signature_is_changed' direction='out'/>"
 "      <arg type='b' name='out_ignore_next_signature_change' direction='out'/>"
 "    </method>"
-"    <method name='DOMSaveDragAndDropHistory'>"
-"      <arg type='t' name='page_id' direction='in'/>"
-"    </method>"
-"    <method name='DOMCleanAfterDragAndDrop'>"
-"      <arg type='t' name='page_id' direction='in'/>"
-"    </method>"
 "    <method name='DOMGetActiveSignatureUid'>"
 "      <arg type='t' name='page_id' direction='in'/>"
 "      <arg type='s' name='uid' direction='out'/>"
@@ -1857,15 +1851,6 @@ handle_method_call (GDBusConnection *connection,
                e_editor_dom_replace_image_src (editor_page, selector, uri);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
-       } else if (g_strcmp0 (method_name, "DOMDragAndDropEnd") == 0) {
-               g_variant_get (parameters, "(t)", &page_id);
-
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
-
-               e_editor_dom_drag_and_drop_end (editor_page);
-               g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "DOMInsertSmiley") == 0) {
                const gchar *smiley_name;
 
@@ -2153,28 +2138,6 @@ handle_method_call (GDBusConnection *connection,
                                ignore_next_signature_change));
 
                g_free (new_signature_id);
-       } else if (g_strcmp0 (method_name, "DOMSaveDragAndDropHistory") == 0) {
-               g_variant_get (
-                       parameters, "(t)", &page_id);
-
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
-
-               e_composer_dom_save_drag_and_drop_history (editor_page);
-
-               g_dbus_method_invocation_return_value (invocation, NULL);
-       } else if (g_strcmp0 (method_name, "DOMCleanAfterDragAndDrop") == 0) {
-               g_variant_get (
-                       parameters, "(t)", &page_id);
-
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
-
-               e_composer_dom_clean_after_drag_and_drop (editor_page);
-
-               g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "DOMGetActiveSignatureUid") == 0) {
                gchar *value;
 
@@ -2192,6 +2155,20 @@ handle_method_call (GDBusConnection *connection,
                                "(@s)",
                                g_variant_new_take_string (
                                        value ? value : g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "DOMLastDropOperationDidCopy") == 0) {
+               EEditorUndoRedoManager *manager;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               manager = e_editor_page_get_undo_redo_manager (editor_page);
+               if (manager)
+                       e_editor_undo_redo_manager_last_drop_operation_did_copy (manager);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "DOMGetCaretPosition") == 0) {
                guint32 value;
 


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