[evolution] I#966 - Composer: Correct caret placement around signature wrapper



commit 34e28ff1494a90e1713ab213de6b4dbd272a9da7
Author: Milan Crha <mcrha redhat com>
Date:   Thu Jun 4 17:49:22 2020 +0200

    I#966 - Composer: Correct caret placement around signature wrapper
    
    Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/966

 data/webkit/e-editor.js                     | 96 +++++++++++++++++------------
 src/composer/e-composer-private.c           | 54 +++++++++++++---
 src/composer/e-composer-private.h           |  2 +
 src/e-util/e-content-editor.c               |  2 +
 src/e-util/e-content-editor.h               |  2 +
 src/e-util/e-mail-signature-combo-box.c     |  6 ++
 src/e-util/test-html-editor-units-bugs.c    |  1 +
 src/modules/webkit-editor/e-webkit-editor.c | 14 ++---
 8 files changed, 121 insertions(+), 56 deletions(-)
---
diff --git a/data/webkit/e-editor.js b/data/webkit/e-editor.js
index 0451093a54..61dba68c94 100644
--- a/data/webkit/e-editor.js
+++ b/data/webkit/e-editor.js
@@ -79,7 +79,6 @@ var EvoEditor = {
        UNICODE_SMILEYS : false,
        WRAP_QUOTED_TEXT_IN_REPLIES : true,
        START_BOTTOM : false,
-       TOP_SIGNATURE : false,
 
        FORCE_NO : 0,
        FORCE_YES : 1,
@@ -4719,6 +4718,31 @@ EvoEditor.GetCurrentSignatureUid = function()
        return "";
 }
 
+EvoEditor.insertEmptyParagraphBefore = function(beforeNode)
+{
+       var node = document.createElement("DIV");
+
+       node.appendChild(document.createElement("BR"));
+       document.body.insertBefore(node, beforeNode);
+       EvoEditor.maybeUpdateParagraphWidth(node);
+
+       return node;
+}
+
+EvoEditor.scrollIntoSelection = function()
+{
+       var node = document.getSelection().focusNode;
+
+       if (node) {
+               if (node.nodeType != node.ELEMENT_NODE)
+                       node = node.parentElement;
+
+               if (node && node.scrollIntoView != undefined) {
+                       node.scrollIntoView();
+               }
+       }
+}
+
 EvoEditor.removeUnwantedTags = function(parent)
 {
        if (!parent)
@@ -4735,12 +4759,9 @@ EvoEditor.removeUnwantedTags = function(parent)
        }
 }
 
-EvoEditor.InsertSignature = function(content, isHTML, uid, fromMessage, checkChanged, ignoreNextChange, 
startBottom, topSignature, addDelimiter)
+EvoEditor.InsertSignature = function(content, isHTML, canRepositionCaret, uid, fromMessage, checkChanged, 
ignoreNextChange, startBottom, topSignature, addDelimiter)
 {
-       var sigSpan, node, scrollX, scrollY;
-
-       scrollX = window.scrollX;
-       scrollY = window.scrollY;
+       var sigSpan, node;
 
        sigSpan = document.createElement("SPAN");
        sigSpan.className = "-x-evo-signature";
@@ -4819,8 +4840,9 @@ EvoEditor.InsertSignature = function(content, isHTML, uid, fromMessage, checkCha
                                if (checkChanged) {
                                        /* Normalize the signature that we want to insert as the one in the
                                         * message already is normalized. */
-                                       webkit_dom_node_normalize (WEBKIT_DOM_NODE (signature_to_insert));
-                                       if (!webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE 
(signature_to_insert), signature)) {
+                                       signature.normalize();
+
+                                       if (signature.firstElementChild && 
!signature.firstElementChild.isEqualNode(sigSpan)) {
                                                /* Signature in the body is different than the one with the
                                                 * same id, so set the active signature to None and leave
                                                 * the signature that is in the body. */
@@ -4835,7 +4857,7 @@ EvoEditor.InsertSignature = function(content, isHTML, uid, fromMessage, checkCha
                                        if (signature.hasAttribute("name")) {
                                                id = signature.getAttribute("name");
                                                signature.id = id;
-                                               signature.removeAttribute(name);
+                                               signature.removeAttribute("name");
                                        } else {
                                                id = signature.id;
                                        }
@@ -4902,7 +4924,9 @@ EvoEditor.InsertSignature = function(content, isHTML, uid, fromMessage, checkCha
 
                                EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertSignature::new-changes", document.body, document.body, EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
                                try {
-                                       if (topSignature) {
+                                       var emptyDocument = !document.body.firstElementChild || 
!document.body.firstElementChild.nextElementSibling;
+
+                                       if (topSignature && !emptyDocument) {
                                                document.body.insertBefore(useWrapper, 
document.body.firstChild);
 
                                                node = document.createElement("DIV");
@@ -4910,7 +4934,15 @@ EvoEditor.InsertSignature = function(content, isHTML, uid, fromMessage, checkCha
                                                node.className = "-x-evo-top-signature-spacer";
 
                                                document.body.insertBefore(node, useWrapper.nextSibling);
+
+                                               // Insert empty paragraph before the signature
+                                               
EvoEditor.insertEmptyParagraphBefore(document.body.firstChild);
                                        } else {
+                                               if (!startBottom && !emptyDocument) {
+                                                       // Insert empty paragraph before the signature
+                                                       EvoEditor.insertEmptyParagraphBefore(null);
+                                               }
+
                                                document.body.appendChild(useWrapper);
                                        }
                                } finally {
@@ -4920,36 +4952,27 @@ EvoEditor.InsertSignature = function(content, isHTML, uid, fromMessage, checkCha
 
                        fromMessage = false;
 
-                       // Position the caret and scroll to it
-                       if (startBottom) {
-                               if (topSignature) {
-                                       document.getSelection().setPosition(document.body.lastChild, 0);
-                               } else if (useWrapper.previousSibling) {
-                                       document.getSelection().setPosition(useWrapper.previousSibling, 0);
+                       if (canRepositionCaret) {
+                               // Position the caret and scroll to it
+                               if (startBottom) {
+                                       if (topSignature) {
+                                               document.getSelection().setPosition(document.body.lastChild, 
0);
+                                       } else if (useWrapper.previousSibling) {
+                                               
document.getSelection().setPosition(useWrapper.previousSibling, 0);
+                                       } else {
+                                               document.getSelection().setPosition(useWrapper, 0);
+                                       }
                                } else {
-                                       document.getSelection().setPosition(useWrapper, 0);
+                                       document.getSelection().setPosition(document.body.firstChild, 0);
                                }
-                       } else {
-                               document.getSelection().setPosition(document.body.firstChild, 0);
-                       }
-
-                       node = document.getSelection().anchorNode;
 
-                       if (node) {
-                               if (node.nodeType != node.ELEMENT_NODE)
-                                       node = node.parentElement;
-
-                               if (node && node.scrollIntoViewIfNeeded != undefined)
-                                       node.scrollIntoViewIfNeeded();
+                               EvoEditor.scrollIntoSelection();
                        }
                }
        } finally {
                EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, "InsertSignature");
        }
 
-       // the above changes can cause change of the scroll offset, thus restore it
-       window.scrollTo(scrollX, scrollY);
-
        var res = [];
 
        res["fromMessage"] = fromMessage;
@@ -5716,15 +5739,12 @@ EvoEditor.processLoadedContent = function()
                node.scrollIntoView();
        }
 
-       if (EvoEditor.START_BOTTOM) {
-               var node = document.createElement("DIV");
-
-               node.appendChild(document.createElement("BR"));
-               document.body.appendChild(node);
-               EvoEditor.maybeUpdateParagraphWidth(node);
-
+       if (EvoEditor.START_BOTTOM && document.body.firstElementChild && 
document.body.firstElementChild.nextElementSibling) {
+               node = EvoEditor.insertEmptyParagraphBefore(null);
                document.getSelection().setPosition(node, 0);
                node.scrollIntoView();
+       } else {
+               EvoEditor.scrollIntoSelection();
        }
 }
 
diff --git a/src/composer/e-composer-private.c b/src/composer/e-composer-private.c
index e4a1c17924..77d1ca7918 100644
--- a/src/composer/e-composer-private.c
+++ b/src/composer/e-composer-private.c
@@ -548,6 +548,8 @@ e_composer_private_finalize (EMsgComposer *composer)
        g_ptr_array_foreach (array, (GFunc) g_free, NULL);
        g_ptr_array_free (array, TRUE);
 
+       g_clear_object (&composer->priv->load_signature_cancellable);
+
        g_free (composer->priv->charset);
        g_free (composer->priv->mime_type);
        g_free (composer->priv->mime_body);
@@ -769,11 +771,29 @@ e_composer_selection_is_image_uris (EMsgComposer *composer,
        return all_image_uris;
 }
 
+typedef struct _UpdateSignatureData {
+       EMsgComposer *composer;
+       gboolean can_reposition_caret;
+} UpdateSignatureData;
+
+static void
+update_signature_data_free (gpointer ptr)
+{
+       UpdateSignatureData *usd = ptr;
+
+       if (usd) {
+               g_clear_object (&usd->composer);
+               g_slice_free (UpdateSignatureData, usd);
+       }
+}
+
 static void
 composer_load_signature_cb (EMailSignatureComboBox *combo_box,
-                            GAsyncResult *result,
-                            EMsgComposer *composer)
+                           GAsyncResult *result,
+                           gpointer user_data)
 {
+       UpdateSignatureData *usd = user_data;
+       EMsgComposer *composer = usd->composer;
        gchar *contents = NULL, *new_signature_id;
        gsize length = 0;
        gboolean is_html;
@@ -786,15 +806,18 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
 
        /* FIXME Use an EAlert here. */
        if (error != NULL) {
-               g_warning ("%s: %s", G_STRFUNC, error->message);
+               if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+                       g_warning ("%s: %s", G_STRFUNC, error->message);
                g_error_free (error);
-               g_object_unref (composer);
+               update_signature_data_free (usd);
                return;
        }
 
+       g_clear_object (&composer->priv->load_signature_cancellable);
+
        if (composer->priv->ignore_next_signature_change) {
                composer->priv->ignore_next_signature_change = FALSE;
-               g_object_unref (composer);
+               update_signature_data_free (usd);
                return;
        }
 
@@ -805,6 +828,7 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
                cnt_editor,
                contents,
                is_html,
+               usd->can_reposition_caret,
                gtk_combo_box_get_active_id (GTK_COMBO_BOX (combo_box)),
                &composer->priv->set_signature_from_message,
                &composer->priv->check_if_signature_is_changed,
@@ -833,7 +857,7 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
 
        g_free (new_signature_id);
        g_free (contents);
-       g_object_unref (composer);
+       update_signature_data_free (usd);
 }
 
 static void
@@ -853,9 +877,15 @@ e_composer_update_signature (EMsgComposer *composer)
        EMailSignatureComboBox *combo_box;
        EHTMLEditor *editor;
        EContentEditor *cnt_editor;
+       UpdateSignatureData *usd;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
+       if (composer->priv->load_signature_cancellable) {
+               g_cancellable_cancel (composer->priv->load_signature_cancellable);
+               g_clear_object (&composer->priv->load_signature_cancellable);
+       }
+
        /* Do nothing if we're redirecting a message or we disabled
         * the signature on purpose */
        if (composer->priv->redirect || composer->priv->disable_signature)
@@ -874,13 +904,19 @@ e_composer_update_signature (EMsgComposer *composer)
                return;
        }
 
+       composer->priv->load_signature_cancellable = g_cancellable_new ();
+
+       usd = g_slice_new (UpdateSignatureData);
+       usd->composer = g_object_ref (composer);
+       usd->can_reposition_caret = e_msg_composer_get_is_reply_or_forward (composer) &&
+               !gtk_widget_get_realized (GTK_WIDGET (composer));
+
        /* XXX Signature files should be local and therefore load quickly,
         *     so while we do load them asynchronously we don't allow for
         *     user cancellation and we keep the composer alive until the
         *     asynchronous loading is complete. */
        e_mail_signature_combo_box_load_selected (
-               combo_box, G_PRIORITY_DEFAULT, NULL,
+               combo_box, G_PRIORITY_DEFAULT, composer->priv->load_signature_cancellable,
                (GAsyncReadyCallback) composer_load_signature_cb,
-               g_object_ref (composer));
+               usd);
 }
-
diff --git a/src/composer/e-composer-private.h b/src/composer/e-composer-private.h
index c415974cbc..19c1aefb64 100644
--- a/src/composer/e-composer-private.h
+++ b/src/composer/e-composer-private.h
@@ -124,6 +124,8 @@ struct _EMsgComposerPrivate {
 
        guint content_hash_ref_count; /* when reaches 0, the content_hash is freed; to be able to reuse it */
        EContentEditorContentHash *content_hash;
+
+       GCancellable *load_signature_cancellable;
 };
 
 void           e_composer_private_constructed  (EMsgComposer *composer);
diff --git a/src/e-util/e-content-editor.c b/src/e-util/e-content-editor.c
index 9f69638c39..ed07be45b2 100644
--- a/src/e-util/e-content-editor.c
+++ b/src/e-util/e-content-editor.c
@@ -2400,6 +2400,7 @@ gchar *
 e_content_editor_insert_signature (EContentEditor *editor,
                                    const gchar *content,
                                    gboolean is_html,
+                                  gboolean can_reposition_caret,
                                    const gchar *signature_id,
                                    gboolean *set_signature_from_message,
                                    gboolean *check_if_signature_is_changed,
@@ -2417,6 +2418,7 @@ e_content_editor_insert_signature (EContentEditor *editor,
                editor,
                content,
                is_html,
+               can_reposition_caret,
                signature_id,
                set_signature_from_message,
                check_if_signature_is_changed,
diff --git a/src/e-util/e-content-editor.h b/src/e-util/e-content-editor.h
index ae00b147f4..eaa7aab44d 100644
--- a/src/e-util/e-content-editor.h
+++ b/src/e-util/e-content-editor.h
@@ -145,6 +145,7 @@ struct _EContentEditorInterface {
        gchar *         (*insert_signature)             (EContentEditor *editor,
                                                         const gchar *content,
                                                         gboolean is_html,
+                                                        gboolean can_reposition_caret,
                                                         const gchar *signature_id,
                                                         gboolean *set_signature_from_message,
                                                         gboolean *check_if_signature_is_changed,
@@ -644,6 +645,7 @@ gchar *             e_content_editor_insert_signature
                                                (EContentEditor *editor,
                                                 const gchar *content,
                                                 gboolean is_html,
+                                                gboolean can_reposition_caret,
                                                 const gchar *signature_id,
                                                 gboolean *set_signature_from_message,
                                                 gboolean *check_if_signature_is_changed,
diff --git a/src/e-util/e-mail-signature-combo-box.c b/src/e-util/e-mail-signature-combo-box.c
index 8ba4428135..7dac48bc1e 100644
--- a/src/e-util/e-mail-signature-combo-box.c
+++ b/src/e-util/e-mail-signature-combo-box.c
@@ -615,6 +615,7 @@ e_mail_signature_combo_box_set_identity (EMailSignatureComboBox *combo_box,
 typedef struct _LoadContext LoadContext;
 
 struct _LoadContext {
+       GCancellable *cancellable;
        gchar *contents;
        gsize length;
        gboolean is_html;
@@ -623,6 +624,7 @@ struct _LoadContext {
 static void
 load_context_free (LoadContext *context)
 {
+       g_clear_object (&context->cancellable);
        g_free (context->contents);
        g_slice_free (LoadContext, context);
 }
@@ -761,6 +763,7 @@ e_mail_signature_combo_box_load_selected (EMailSignatureComboBox *combo_box,
        g_return_if_fail (E_IS_MAIL_SIGNATURE_COMBO_BOX (combo_box));
 
        context = g_slice_new0 (LoadContext);
+       context->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
 
        simple = g_simple_async_result_new (
                G_OBJECT (combo_box), callback, user_data,
@@ -824,6 +827,9 @@ e_mail_signature_combo_box_load_selected_finish (EMailSignatureComboBox *combo_b
        if (g_simple_async_result_propagate_error (simple, error))
                return FALSE;
 
+       if (g_cancellable_set_error_if_cancelled (context->cancellable, error))
+               return FALSE;
+
        if (contents != NULL) {
                *contents = context->contents;
                context->contents = NULL;
diff --git a/src/e-util/test-html-editor-units-bugs.c b/src/e-util/test-html-editor-units-bugs.c
index a67b8a478d..8ca3e532d5 100644
--- a/src/e-util/test-html-editor-units-bugs.c
+++ b/src/e-util/test-html-editor-units-bugs.c
@@ -712,6 +712,7 @@ test_bug_772513 (TestFixture *fixture)
                cnt_editor,
                "",
                FALSE,
+               FALSE,
                "none",
                &set_signature_from_message,
                &check_if_signature_is_changed,
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index 5b782a7328..4bd12bdccd 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -2470,10 +2470,6 @@ webkit_editor_set_top_signature (EWebKitEditor *wk_editor,
 
        wk_editor->priv->top_signature = value;
 
-       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
-               "EvoEditor.TOP_SIGNATURE = %x;",
-               e_webkit_editor_three_state_to_bool (value, "composer-top-signature"));
-
        g_object_notify (G_OBJECT (wk_editor), "top-signature");
 }
 
@@ -2579,6 +2575,7 @@ static gchar *
 webkit_editor_insert_signature (EContentEditor *editor,
                                 const gchar *content,
                                 gboolean is_html,
+                               gboolean can_reposition_caret,
                                 const gchar *signature_id,
                                 gboolean *set_signature_from_message,
                                 gboolean *check_if_signature_is_changed,
@@ -2597,9 +2594,10 @@ webkit_editor_insert_signature (EContentEditor *editor,
        }
 
        jsc_value = webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor),
-               "EvoEditor.InsertSignature(%s, %x, %s, %x, %x, %x, %x, %x, %x);",
+               "EvoEditor.InsertSignature(%s, %x, %x, %s, %x, %x, %x, %x, %x, %x);",
                content ? content : "",
                is_html,
+               can_reposition_caret,
                signature_id,
                *set_signature_from_message,
                *check_if_signature_is_changed,
@@ -4798,10 +4796,8 @@ webkit_editor_load_changed_cb (EWebKitEditor *wk_editor,
                return;
 
        e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
-               "EvoEditor.START_BOTTOM = %x;\n"
-               "EvoEditor.TOP_SIGNATURE = %x;",
-               e_webkit_editor_three_state_to_bool (wk_editor->priv->start_bottom, 
"composer-reply-start-bottom"),
-               e_webkit_editor_three_state_to_bool (wk_editor->priv->top_signature, 
"composer-top-signature"));
+               "EvoEditor.START_BOTTOM = %x;",
+               e_webkit_editor_three_state_to_bool (wk_editor->priv->start_bottom, 
"composer-reply-start-bottom"));
 
        /* Dispatch queued operations - as we are using this just for load
         * operations load just the latest request and throw away the rest. */


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