[evolution/wip/mcrha/webkit-jsc-api] Cover spell checking dialog



commit b93ec2cc89743de7708968b505feb45d6fa93d43
Author: Milan Crha <mcrha redhat com>
Date:   Wed Jan 15 19:03:53 2020 +0100

    Cover spell checking dialog

 data/webkit/e-editor.js                            | 80 ++++++++++++++++++
 src/e-util/e-spell-checker.c                       |  4 +
 src/modules/webkit-editor/e-webkit-editor.c        | 84 +++++++++----------
 .../webkit-editor/web-extension/CMakeLists.txt     | 13 ++-
 .../web-extension/e-editor-web-extension.c         | 95 ++++++++++++++++++++--
 5 files changed, 226 insertions(+), 50 deletions(-)
---
diff --git a/data/webkit/e-editor.js b/data/webkit/e-editor.js
index 78b7353bff..761a0cb557 100644
--- a/data/webkit/e-editor.js
+++ b/data/webkit/e-editor.js
@@ -3336,6 +3336,86 @@ EvoEditor.GetCaretWord = function()
        return range.toString();
 }
 
+EvoEditor.SpellCheckContinue = function(fromCaret, directionNext)
+{
+       var selection, storedSelection = null;
+
+       selection = document.getSelection();
+
+       if (fromCaret) {
+               storedSelection = EvoSelection.Store(document);
+       } else {
+               if (directionNext) {
+                       selection.modify("move", "left", "documentboundary");
+               } else {
+                       selection.modify ("move", "right", "documentboundary");
+                       selection.modify ("extend", "backward", "word");
+               }
+       }
+
+       var selectWord = function(selection, directionNext) {
+               var anchorNode, anchorOffset;
+
+               anchorNode = selection.anchorNode;
+               anchorOffset = selection.anchorOffset;
+
+               if (directionNext) {
+                       var focusNode, focusOffset;
+
+                       focusNode = selection.focusNode;
+                       focusOffset = selection.focusOffset;
+
+                       /* Jump _behind_ next word */
+                       selection.modify("move", "forward", "word");
+                       /* Jump before the word */
+                       selection.modify("move", "backward", "word");
+                       /* Select it */
+                       selection.modify("extend", "forward", "word");
+
+                       /* If the selection didn't change, then we have most probably reached the end of the 
document. */
+                       return !((anchorNode === selection.anchorNode) &&
+                                (anchorOffset == selection.anchorOffset) &&
+                                (focusNode === selection.focusNode) &&
+                                (focusOffset == selection.focusOffset));
+               } else {
+                       /* Jump on the beginning of current word */
+                       selection.modify("move", "backward", "word");
+                       /* Jump before previous word */
+                       selection.modify("move", "backward", "word");
+                       /* Select it */
+                       selection.modify("extend", "forward", "word");
+
+                       /* If the selection start didn't change, then we have most probably reached the 
beginning of the document. */
+                       return (!(anchorNode === selection.anchorNode)) ||
+                               (anchorOffset != selection.anchorOffset);
+               }
+       };
+
+       while (selectWord(selection, directionNext)) {
+               if (selection.rangeCount < 1)
+                       break;
+
+               var range = selection.getRangeAt(0);
+
+               if (!range)
+                       break;
+
+               var word = range.toString();
+
+               if (!EvoEditor.SpellCheckWord(word)) {
+                       /* Found misspelled word */
+                       return word;
+               }
+       }
+
+       /* Restore the selection to contain the last misspelled word. This is
+        * reached only when we reach the beginning/end of the document */
+       if (storedSelection)
+               EvoSelection.Restore(document, storedSelection);
+
+       return null;
+}
+
 EvoEditor.onContextMenu = function(event)
 {
        var node = event.target;
diff --git a/src/e-util/e-spell-checker.c b/src/e-util/e-spell-checker.c
index 3e121de430..5c72859beb 100644
--- a/src/e-util/e-spell-checker.c
+++ b/src/e-util/e-spell-checker.c
@@ -312,7 +312,9 @@ e_spell_checker_list_available_dicts (ESpellChecker *checker)
 
        if (g_hash_table_size (checker->priv->dictionaries_cache) == 0) {
                e_spell_checker_init_global_memory ();
+               G_LOCK (global_memory);
                g_hash_table_foreach (global_language_tags, copy_enchant_dicts, checker);
+               G_UNLOCK (global_memory);
        }
 
        list = g_hash_table_get_values (checker->priv->dictionaries_cache);
@@ -333,7 +335,9 @@ e_spell_checker_count_available_dicts (ESpellChecker *checker)
 
        if (g_hash_table_size (checker->priv->dictionaries_cache) == 0) {
                e_spell_checker_init_global_memory ();
+               G_LOCK (global_memory);
                g_hash_table_foreach (global_language_tags, copy_enchant_dicts, checker);
+               G_UNLOCK (global_memory);
        }
 
        return g_hash_table_size (checker->priv->dictionaries_cache);
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index 46b3439778..9efdb6b8ce 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -3072,6 +3072,43 @@ webkit_editor_on_dialog_open (EContentEditor *editor,
 
        e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
                "EvoEditor.OnDialogOpen(%s);", name);
+
+       if (g_strcmp0 (name, E_CONTENT_EDITOR_DIALOG_SPELLCHECK) == 0) {
+               gchar **strv;
+
+               strv = e_spell_checker_list_active_languages (wk_editor->priv->spell_checker, NULL);
+
+               if (strv) {
+                       gint ii, len = 0;
+                       gchar *langs, *ptr;
+
+                       for (ii = 0; strv[ii]; ii++) {
+                               len += strlen (strv[ii]) + 1;
+                       }
+
+                       len++;
+
+                       langs = g_slice_alloc0 (len);
+                       ptr = langs;
+
+                       for (ii = 0; strv[ii]; ii++) {
+                               strcpy (ptr, strv[ii]);
+                               ptr += strlen (strv[ii]);
+                               if (strv[ii + 1]) {
+                                       *ptr = '|';
+                                       ptr++;
+                               }
+                       }
+
+                       *ptr = '\0';
+
+                       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                               "EvoEditor.SetSpellCheckLanguages(%s);", langs);
+
+                       g_slice_free1 (len, langs);
+                       g_strfreev (strv);
+               }
+       }
 }
 
 static void
@@ -3960,57 +3997,22 @@ webkit_editor_table_set_background_image_uri (EContentEditor *editor,
        webkit_editor_replace_image_src (E_WEBKIT_EDITOR (editor), NULL, uri);
 }
 
-static gchar *
-move_to_another_word (EContentEditor *editor,
-                      const gchar *word,
-                      const gchar *dom_function)
-{
-       EWebKitEditor *wk_editor;
-       gchar **active_languages;
-       gchar *another_word = NULL;
-       GVariant *result;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
-
-       if (!wk_editor->priv->web_extension_proxy) {
-               printf ("EHTMLEditorWebExtension not ready at %s!\n", G_STRFUNC);
-               return NULL;
-       }
-
-       active_languages = e_spell_checker_list_active_languages (
-               wk_editor->priv->spell_checker, NULL);
-       if (!active_languages)
-               return NULL;
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               dom_function,
-               g_variant_new (
-                       "(ts^as)", current_page_id (wk_editor), word ? word : "", active_languages),
-               NULL);
-
-       g_strfreev (active_languages);
-
-       if (result) {
-               g_variant_get (result, "(s)", &another_word);
-               g_variant_unref (result);
-       }
-
-       return another_word;
-}
-
 static gchar *
 webkit_editor_spell_check_next_word (EContentEditor *editor,
                                      const gchar *word)
 {
-       return move_to_another_word (editor, word, "EEditorSpellCheckDialogNext");
+       return webkit_editor_extract_and_free_jsc_string (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), 
"EvoEditor.SpellCheckContinue(%x,%x);", word && *word, TRUE),
+               NULL);
 }
 
 static gchar *
 webkit_editor_spell_check_prev_word (EContentEditor *editor,
                                      const gchar *word)
 {
-       return move_to_another_word (editor, word, "EEditorSpellCheckDialogPrev");
+       return webkit_editor_extract_and_free_jsc_string (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), 
"EvoEditor.SpellCheckContinue(%x,%x);", word && *word, FALSE),
+               NULL);
 }
 
 static void
diff --git a/src/modules/webkit-editor/web-extension/CMakeLists.txt 
b/src/modules/webkit-editor/web-extension/CMakeLists.txt
index 7852e44834..7ec514acc4 100644
--- a/src/modules/webkit-editor/web-extension/CMakeLists.txt
+++ b/src/modules/webkit-editor/web-extension/CMakeLists.txt
@@ -23,6 +23,7 @@ macro(add_webextension_editor_module _name _sourcesvar _depsvar _defsvar _cflags
 endmacro(add_webextension_editor_module)
 
 set(extra_deps
+       evolution-util
        evolution-mail
 )
 set(sources
@@ -35,9 +36,15 @@ set(extra_defines
        -DEVOLUTION_SOURCE_WEBKITDATADIR=\"${CMAKE_SOURCE_DIR}/data/webkit\"
 )
 
-set(extra_cflags)
-set(extra_incdirs)
-set(extra_ldflags)
+set(extra_cflags
+       ${EVOLUTION_DATA_SERVER_CFLAGS}
+)
+set(extra_incdirs
+       ${EVOLUTION_DATA_SERVER_INCLUDE_DIRS}
+)
+set(extra_ldflags
+       ${EVOLUTION_DATA_SERVER_LDFLAGS}
+)
 
 add_webextension_editor_module(module-webkit-editor-webextension
        sources
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 497e53ee01..765f8ffb63 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
@@ -20,10 +20,17 @@
 
 #include <webkit2/webkit-web-extension.h>
 
+#include <libedataserver/libedataserver.h>
+
+#define E_UTIL_INCLUDE_WITHOUT_WEBKIT 1
+#include "e-util/e-util.h"
+#undef E_UTIL_INCLUDE_WITHOUT_WEBKIT
+
 #include "e-editor-web-extension.h"
 
 struct _EEditorWebExtensionPrivate {
        WebKitWebExtension *wk_extension;
+       ESpellChecker *spell_checker;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (EEditorWebExtension, e_editor_web_extension, G_TYPE_OBJECT)
@@ -34,6 +41,7 @@ e_editor_web_extension_dispose (GObject *object)
        EEditorWebExtension *extension = E_EDITOR_WEB_EXTENSION (object);
 
        g_clear_object (&extension->priv->wk_extension);
+       g_clear_object (&extension->priv->spell_checker);
 
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_editor_web_extension_parent_class)->dispose (object);
@@ -51,6 +59,7 @@ static void
 e_editor_web_extension_init (EEditorWebExtension *extension)
 {
        extension->priv = e_editor_web_extension_get_instance_private (extension);
+       extension->priv->spell_checker = e_spell_checker_new ();
 }
 
 static gpointer
@@ -206,15 +215,66 @@ evo_editor_jsc_find_pattern (const gchar *text,
        return object ? object : jsc_value_new_null (jsc_context);
 }
 
+static void
+evo_editor_jsc_set_spell_check_languages (const gchar *langs,
+                                         GWeakRef *wkrf_extension)
+{
+       EEditorWebExtension *extension;
+       gchar **strv;
+
+       g_return_if_fail (wkrf_extension != NULL);
+
+       extension = g_weak_ref_get (wkrf_extension);
+
+       if (!extension)
+               return;
+
+       if (langs && *langs)
+               strv = g_strsplit (langs, "|", -1);
+       else
+               strv = NULL;
+
+       e_spell_checker_set_active_languages (extension->priv->spell_checker, (const gchar * const *) strv);
+
+       g_object_unref (extension);
+       g_strfreev (strv);
+}
+
+/* Returns whether the 'word' is a properly spelled word. It checks
+   with languages previously set by EvoEditor.SetSpellCheckLanguages(). */
+static gboolean
+evo_editor_jsc_spell_check_word (const gchar *word,
+                                GWeakRef *wkrf_extension)
+{
+       EEditorWebExtension *extension;
+       gboolean is_correct;
+
+       g_return_val_if_fail (wkrf_extension != NULL, FALSE);
+
+       extension = g_weak_ref_get (wkrf_extension);
+
+       if (!extension)
+               return TRUE;
+
+       is_correct = e_spell_checker_check_word (extension->priv->spell_checker, word, -1);
+
+       g_object_unref (extension);
+
+       return is_correct;
+}
+
 static void
 window_object_cleared_cb (WebKitScriptWorld *world,
                          WebKitWebPage *page,
                          WebKitFrame *frame,
                          gpointer user_data)
 {
+       EEditorWebExtension *extension = user_data;
        JSCContext *jsc_context;
        JSCValue *jsc_editor;
 
+       g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension));
+
        /* Load the javascript files only to the main frame, not to the subframes */
        if (!webkit_frame_is_main_frame (frame))
                return;
@@ -231,12 +291,35 @@ window_object_cleared_cb (WebKitScriptWorld *world,
 
        if (jsc_editor) {
                JSCValue *jsc_function;
+               const gchar *func_name;
 
-               jsc_function = jsc_value_new_function (jsc_context, "findPattern",
+               /* EvoEditor.findPattern(text, pattern) */
+               func_name = "findPattern";
+               jsc_function = jsc_value_new_function (jsc_context, func_name,
                        G_CALLBACK (evo_editor_jsc_find_pattern), g_object_ref (jsc_context), g_object_unref,
                        JSC_TYPE_VALUE, 2, G_TYPE_STRING, G_TYPE_STRING);
 
-               jsc_value_object_set_property (jsc_editor, "findPattern", jsc_function);
+               jsc_value_object_set_property (jsc_editor, func_name, jsc_function);
+
+               g_clear_object (&jsc_function);
+
+               /* EvoEditor.SetSpellCheckLanguages(langs) */
+               func_name = "SetSpellCheckLanguages";
+               jsc_function = jsc_value_new_function (jsc_context, func_name,
+                       G_CALLBACK (evo_editor_jsc_set_spell_check_languages), e_weak_ref_new (extension), 
(GDestroyNotify) e_weak_ref_free,
+                       G_TYPE_NONE, 1, G_TYPE_STRING);
+
+               jsc_value_object_set_property (jsc_editor, func_name, jsc_function);
+
+               g_clear_object (&jsc_function);
+
+               /* EvoEditor.SpellCheckWord(word) */
+               func_name = "SpellCheckWord";
+               jsc_function = jsc_value_new_function (jsc_context, func_name,
+                       G_CALLBACK (evo_editor_jsc_spell_check_word), e_weak_ref_new (extension), 
(GDestroyNotify) e_weak_ref_free,
+                       G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
+
+               jsc_value_object_set_property (jsc_editor, func_name, jsc_function);
 
                g_clear_object (&jsc_function);
                g_clear_object (&jsc_editor);
@@ -251,7 +334,7 @@ web_page_document_loaded_cb (WebKitWebPage *web_page,
 {
        g_return_if_fail (WEBKIT_IS_WEB_PAGE (web_page));
 
-       window_object_cleared_cb (NULL, web_page, webkit_web_page_get_main_frame (web_page), NULL);
+       window_object_cleared_cb (NULL, web_page, webkit_web_page_get_main_frame (web_page), user_data);
 }
 
 static void
@@ -262,7 +345,7 @@ web_page_created_cb (WebKitWebExtension *wk_extension,
        g_return_if_fail (WEBKIT_IS_WEB_PAGE (web_page));
        g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension));
 
-       window_object_cleared_cb (NULL, web_page, webkit_web_page_get_main_frame (web_page), NULL);
+       window_object_cleared_cb (NULL, web_page, webkit_web_page_get_main_frame (web_page), extension);
 
        g_signal_connect (
                web_page, "send-request",
@@ -270,7 +353,7 @@ web_page_created_cb (WebKitWebExtension *wk_extension,
 
        g_signal_connect (
                web_page, "document-loaded",
-               G_CALLBACK (web_page_document_loaded_cb), NULL);
+               G_CALLBACK (web_page_document_loaded_cb), extension);
 }
 
 void
@@ -290,5 +373,5 @@ e_editor_web_extension_initialize (EEditorWebExtension *extension,
        script_world = webkit_script_world_get_default ();
 
        g_signal_connect (script_world, "window-object-cleared",
-               G_CALLBACK (window_object_cleared_cb), NULL);
+               G_CALLBACK (window_object_cleared_cb), extension);
 }


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