[evolution/wip/tpopela/freeze-fixes: 7/7] EHTMLEditorView - Introduce function that performs spell check just in current viewport



commit 3e56adabe8cccf021aec86c9fe9599ae71d64a43
Author: Tomas Popela <tpopela redhat com>
Date:   Wed Mar 18 14:31:36 2015 +0100

    EHTMLEditorView - Introduce function that performs spell check just in current viewport
    
    The e_html_editor_view_force_spell_check is heavy for most cases. On
    some big mails composer can spent there even 10 seconds (on bigger
    mails probably even more). With this commit we introduced the
    e_html_editor_view_force_spell_check_in_viewport that really performs
    the spell check on elements that are currently in viewport. The
    downside of this approach is that when we scroll the viewport we the
    newly visible elements won't be spell checked. As a solution we need to
    perform the spell check when the scroll occurred (this information we
    get from HTML scroll event). The problem was that the scrolling was
    choppy as the e_html_editor_view_force_spell_check_in_viewport function
    still need some time to perform the spell check thus we are performing
    the spell check one second after last scroll event occurred.

 composer/e-composer-private.c         |    2 +-
 composer/e-msg-composer.c             |    8 +-
 e-util/e-html-editor-actions.c        |    6 +-
 e-util/e-html-editor-replace-dialog.c |    2 +-
 e-util/e-html-editor-selection.c      |    5 +-
 e-util/e-html-editor-view.c           |  174 +++++++++++++++++++++++++++------
 e-util/e-html-editor-view.h           |    2 +
 7 files changed, 154 insertions(+), 45 deletions(-)
---
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index 6a177bb..94ef230 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -1051,7 +1051,7 @@ composer_move_caret (EMsgComposer *composer)
                        view, "size-allocate",
                        G_CALLBACK (composer_size_allocate_cb), NULL);
 
-       e_html_editor_view_force_spell_check (view);
+       e_html_editor_view_force_spell_check_in_viewport (view);
 
        e_html_editor_selection_unblock_selection_changed (editor_selection);
 }
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
index c74eaff..29c2474 100644
--- a/composer/e-msg-composer.c
+++ b/composer/e-msg-composer.c
@@ -1258,7 +1258,7 @@ composer_build_message (EMsgComposer *composer,
 
                e_html_editor_view_remove_embed_styles (view);
                e_html_editor_selection_restore (selection);
-               e_html_editor_view_force_spell_check (view);
+               e_html_editor_view_force_spell_check_in_viewport (view);
 
                g_byte_array_append (data, (guint8 *) text, strlen (text));
 
@@ -1847,7 +1847,7 @@ msg_composer_drag_data_received_after_cb (GtkWidget *widget,
 
        e_html_editor_view_check_magic_links (view, FALSE);
        /* Also force spell check on view. */
-       e_html_editor_view_force_spell_check (view);
+       e_html_editor_view_force_spell_check_in_viewport (view);
 }
 
 static gchar *
@@ -1963,7 +1963,7 @@ msg_composer_drag_data_received_cb (GtkWidget *widget,
                } while (list_len);
 
                e_html_editor_view_check_magic_links (html_editor_view, FALSE);
-               e_html_editor_view_force_spell_check (html_editor_view);
+               e_html_editor_view_force_spell_check_in_viewport (html_editor_view);
 
                e_html_editor_selection_scroll_to_caret (editor_selection);
 
@@ -5369,7 +5369,7 @@ e_msg_composer_restore_focus_on_composer (EMsgComposer *composer)
        if (E_IS_HTML_EDITOR_VIEW (widget)) {
                EHTMLEditorSelection *selection;
 
-               e_html_editor_view_force_spell_check (E_HTML_EDITOR_VIEW (widget));
+               e_html_editor_view_force_spell_check_in_viewport (E_HTML_EDITOR_VIEW (widget));
 
                selection = e_html_editor_view_get_selection (
                        E_HTML_EDITOR_VIEW (widget));
diff --git a/e-util/e-html-editor-actions.c b/e-util/e-html-editor-actions.c
index 192d077..49d0c60 100644
--- a/e-util/e-html-editor-actions.c
+++ b/e-util/e-html-editor-actions.c
@@ -832,7 +832,7 @@ action_paste_cb (GtkAction *action,
        EHTMLEditorView *view = e_html_editor_get_view (editor);
 
        webkit_web_view_paste_clipboard (WEBKIT_WEB_VIEW (view));
-       e_html_editor_view_force_spell_check (view);
+       e_html_editor_view_force_spell_check_in_viewport (view);
 }
 
 static void
@@ -845,7 +845,7 @@ action_paste_as_text_cb (GtkAction *action,
                gtk_widget_grab_focus (GTK_WIDGET (view));
 
        e_html_editor_view_paste_as_text (view);
-       e_html_editor_view_force_spell_check (view);
+       e_html_editor_view_force_spell_check_in_viewport (view);
 }
 
 static void
@@ -858,7 +858,7 @@ action_paste_quote_cb (GtkAction *action,
                gtk_widget_grab_focus (GTK_WIDGET (view));
 
        e_html_editor_view_paste_clipboard_quoted (view);
-       e_html_editor_view_force_spell_check (view);
+       e_html_editor_view_force_spell_check_in_viewport (view);
 }
 
 static void
diff --git a/e-util/e-html-editor-replace-dialog.c b/e-util/e-html-editor-replace-dialog.c
index 6c594c2..cb700c0 100644
--- a/e-util/e-html-editor-replace-dialog.c
+++ b/e-util/e-html-editor-replace-dialog.c
@@ -157,7 +157,7 @@ html_editor_replace_dialog_replace_all_cb (EHTMLEditorReplaceDialog *dialog)
 
                        e_html_editor_view_insert_new_history_event (view, ev);
                }
-               e_html_editor_view_force_spell_check (view);
+               e_html_editor_view_force_spell_check_in_viewport (view);
        }
 
        result = g_strdup_printf (ngettext("%d occurence replaced",
diff --git a/e-util/e-html-editor-selection.c b/e-util/e-html-editor-selection.c
index 0d99e78..3fbbe89 100644
--- a/e-util/e-html-editor-selection.c
+++ b/e-util/e-html-editor-selection.c
@@ -4449,9 +4449,6 @@ e_html_editor_selection_set_monospaced (EHTMLEditorSelection *selection,
 
                        webkit_dom_dom_selection_modify (
                                window_selection, "move", "right", "character");
-
-                       e_html_editor_view_force_spell_check_for_current_paragraph (
-                               view);
                }
 
                /* Re-set formatting */
@@ -5028,7 +5025,7 @@ e_html_editor_selection_insert_html (EHTMLEditorSelection *selection,
                if (strstr (html_text, "id=\"-x-evo-selection-start-marker\""))
                        e_html_editor_selection_restore (selection);
                e_html_editor_view_check_magic_links (view, FALSE);
-               e_html_editor_view_force_spell_check (view);
+               e_html_editor_view_force_spell_check_in_viewport (view);
 
                e_html_editor_selection_scroll_to_caret (selection);
        } else
diff --git a/e-util/e-html-editor-view.c b/e-util/e-html-editor-view.c
index 0353d8e..4ff3ee9 100644
--- a/e-util/e-html-editor-view.c
+++ b/e-util/e-html-editor-view.c
@@ -115,6 +115,7 @@ struct _EHTMLEditorViewPrivate {
        GHashTable *old_settings;
 
        GQueue *post_reload_operations;
+       guint spell_check_on_scroll_event_source_id;
 
        GList *history;
        guint history_size;
@@ -466,13 +467,31 @@ get_parent_block_element (WebKitDOMNode *node)
        return parent;
 }
 
+static void
+perform_spell_check (WebKitDOMDOMSelection *dom_selection,
+                     WebKitDOMRange *start_range,
+                     WebKitDOMRange *end_range)
+{
+       WebKitDOMRange *actual = start_range;
+
+       /* Go through all words to spellcheck them. To avoid this we have to wait for
+        * http://www.w3.org/html/wg/drafts/html/master/editing.html#dom-forcespellcheck */
+       /* We are moving forward word by word until we hit the text on the end. */
+       while (actual && webkit_dom_range_compare_boundary_points (end_range, 2, actual, NULL) != 0) {
+               webkit_dom_dom_selection_modify (
+                       dom_selection, "move", "forward", "word");
+               actual = webkit_dom_dom_selection_get_range_at (
+                       dom_selection, 0, NULL);
+       }
+}
+
 void
 e_html_editor_view_force_spell_check_for_current_paragraph (EHTMLEditorView *view)
 {
        EHTMLEditorSelection *selection;
        WebKitDOMDocument *document;
        WebKitDOMDOMSelection *dom_selection;
-       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMWindow *dom_window;
        WebKitDOMElement *selection_start_marker, *selection_end_marker;
        WebKitDOMElement *parent, *element;
        WebKitDOMRange *end_range, *actual;
@@ -482,8 +501,8 @@ e_html_editor_view_force_spell_check_for_current_paragraph (EHTMLEditorView *vie
                return;
 
        document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       window = webkit_dom_document_get_default_view (document);
-       dom_selection = webkit_dom_dom_window_get_selection (window);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
 
        element = webkit_dom_document_query_selector (
                document, "body[spellcheck=true]", NULL);
@@ -535,17 +554,8 @@ e_html_editor_view_force_spell_check_for_current_paragraph (EHTMLEditorView *vie
        webkit_dom_dom_selection_remove_all_ranges (dom_selection);
        webkit_dom_dom_selection_add_range (dom_selection, actual);
 
-       /* Go through all words to spellcheck them. To avoid this we have to wait for
-        * http://www.w3.org/html/wg/drafts/html/master/editing.html#dom-forcespellcheck */
        actual = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-       /* We are moving forward word by word until we hit the text on the end of
-        * the paragraph that we previously inserted there */
-       while (actual && webkit_dom_range_compare_boundary_points (end_range, 2, actual, NULL) != 0) {
-               webkit_dom_dom_selection_modify (
-                       dom_selection, "move", "forward", "word");
-               actual = webkit_dom_dom_selection_get_range_at (
-                       dom_selection, 0, NULL);
-       }
+       perform_spell_check (dom_selection, actual, end_range);
 
        /* Remove the text that we inserted on the end of the paragraph */
        remove_node (WEBKIT_DOM_NODE (text));
@@ -709,17 +719,8 @@ refresh_spell_check (EHTMLEditorView *view,
        webkit_dom_dom_selection_modify (
                dom_selection, "move", "backward", "documentboundary");
 
-       /* Go through all words to spellcheck them. To avoid this we have to wait for
-        * http://www.w3.org/html/wg/drafts/html/master/editing.html#dom-forcespellcheck */
        actual = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-       /* We are moving forward word by word until we hit the text on the end of
-        * the body that we previously inserted there */
-       while (actual && webkit_dom_range_compare_boundary_points (end_range, 2, actual, NULL) != 0) {
-               webkit_dom_dom_selection_modify (
-                       dom_selection, "move", "forward", "word");
-               actual = webkit_dom_dom_selection_get_range_at (
-                       dom_selection, 0, NULL);
-       }
+       perform_spell_check (dom_selection, actual, end_range);
 
        /* Remove the text that we inserted on the end of the body */
        remove_node (WEBKIT_DOM_NODE (text));
@@ -737,6 +738,83 @@ e_html_editor_view_turn_spell_check_off (EHTMLEditorView *view)
 }
 
 void
+e_html_editor_view_force_spell_check_in_viewport (EHTMLEditorView *view)
+{
+       EHTMLEditorSelection *selection;
+       glong viewport_height;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMElement *last_element;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMRange *end_range, *actual;
+       WebKitDOMText *text;
+
+       if (!view->priv->inline_spelling)
+               return;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       body = WEBKIT_DOM_HTML_ELEMENT (webkit_dom_document_query_selector (
+               document, "body[spellcheck=true]", NULL));
+
+       if (!body) {
+               body = webkit_dom_document_get_body (document);
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (body), "spellcheck", "true", NULL);
+       }
+
+       if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)))
+               return;
+
+       selection = e_html_editor_view_get_selection (view);
+       e_html_editor_selection_save (selection);
+
+       /* Block callbacks of selection-changed signal as we don't want to
+        * recount all the block format things in EHTMLEditorSelection and here as well
+        * when we are moving with caret */
+       block_selection_changed_callbacks (view);
+
+       /* We have to add 10 px offset as otherwise just the HTML element will be returned */
+       actual = webkit_dom_document_caret_range_from_point (document, 10, 10);
+
+       /* Append some text on the end of the body */
+       text = webkit_dom_document_create_text_node (document, "-x-evo-end");
+
+       /* We have to add 10 px offset as otherwise just the HTML element will be returned */
+       viewport_height = webkit_dom_dom_window_get_inner_height (dom_window);
+       last_element = webkit_dom_document_element_from_point (document, 10, viewport_height - 10);
+       if (last_element && !WEBKIT_DOM_IS_HTML_HTML_ELEMENT (last_element)) {
+               WebKitDOMElement *parent;
+
+               parent = get_parent_block_element (WEBKIT_DOM_NODE (last_element));
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (parent), WEBKIT_DOM_NODE (text), NULL);
+       } else
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (text), NULL);
+
+       /* Create range that's pointing on the end of viewport */
+       end_range = webkit_dom_document_create_range (document);
+       webkit_dom_range_select_node_contents (
+               end_range, WEBKIT_DOM_NODE (text), NULL);
+       webkit_dom_range_collapse (end_range, FALSE, NULL);
+
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, actual);
+       perform_spell_check (dom_selection, actual, end_range);
+
+       /* Remove the text that we inserted on the end of the body */
+       remove_node (WEBKIT_DOM_NODE (text));
+
+       /* Unblock the callbacks */
+       unblock_selection_changed_callbacks (view);
+
+       e_html_editor_selection_restore (selection);
+}
+
+void
 e_html_editor_view_force_spell_check (EHTMLEditorView *view)
 {
        if (view->priv->inline_spelling)
@@ -1056,7 +1134,7 @@ insert_new_line_into_citation (EHTMLEditorView *view,
                                        document, WEBKIT_DOM_ELEMENT (node), citation_level);
                        }
 
-                       e_html_editor_view_force_spell_check (view);
+                       e_html_editor_view_force_spell_check_in_viewport (view);
                }
        }
 
@@ -2542,6 +2620,27 @@ save_history_for_input (EHTMLEditorView *view)
        e_html_editor_view_insert_new_history_event (view, ev);
 }
 
+static gboolean
+force_spell_check_on_timeout (EHTMLEditorView *view)
+{
+       e_html_editor_view_force_spell_check_in_viewport (view);
+       view->priv->spell_check_on_scroll_event_source_id = 0;
+       return FALSE;
+}
+
+static void
+body_scroll_event_cb (WebKitDOMElement *element,
+                        WebKitDOMEvent *event,
+                        EHTMLEditorView *view)
+{
+       if (view->priv->spell_check_on_scroll_event_source_id > 0) {
+               g_source_remove (view->priv->spell_check_on_scroll_event_source_id);
+       }
+
+       view->priv->spell_check_on_scroll_event_source_id =
+               g_timeout_add (1000, (GSourceFunc)force_spell_check_on_timeout, view);
+}
+
 static void
 body_input_event_cb (WebKitDOMElement *element,
                      WebKitDOMEvent *event,
@@ -6232,6 +6331,7 @@ html_editor_convert_view_content (EHTMLEditorView *view,
        gint ii, length;
        GSettings *settings;
        WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window;
        WebKitDOMElement *paragraph, *content_wrapper, *top_signature;
        WebKitDOMElement *cite_body, *signature, *wrapper;
        WebKitDOMHTMLElement *body;
@@ -6243,6 +6343,7 @@ html_editor_convert_view_content (EHTMLEditorView *view,
        g_object_unref (settings);
 
        document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+       dom_window = webkit_dom_document_get_default_view (document);
        body = webkit_dom_document_get_body (document);
        /* Wrapper that will represent the new body. */
        wrapper = webkit_dom_document_create_element (document, "div", NULL);
@@ -6474,7 +6575,7 @@ html_editor_convert_view_content (EHTMLEditorView *view,
        clear_attributes (document);
 
        e_html_editor_selection_restore (selection);
-       e_html_editor_view_force_spell_check (view);
+       e_html_editor_view_force_spell_check_in_viewport (view);
 
        /* Register on input event that is called when the content (body) is modified */
        webkit_dom_event_target_add_event_listener (
@@ -6484,6 +6585,13 @@ html_editor_convert_view_content (EHTMLEditorView *view,
                FALSE,
                view);
 
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (dom_window),
+               "scroll",
+               G_CALLBACK (body_scroll_event_cb),
+               FALSE,
+               view);
+
        register_html_events_handlers (view, body);
 
        g_free (inner_html);
@@ -6886,7 +6994,7 @@ html_editor_view_insert_converted_html_into_selection (EHTMLEditorView *view,
 
        e_html_editor_selection_restore (selection);
  out:
-       e_html_editor_view_force_spell_check (view);
+       e_html_editor_view_force_spell_check_in_viewport (view);
        e_html_editor_selection_scroll_to_caret (selection);
 
        register_input_event_listener_on_body (view);
@@ -8653,7 +8761,7 @@ convert_when_changing_composer_mode (EHTMLEditorView *view)
        /* Update fonts - in plain text we only want monospace */
        e_html_editor_view_update_fonts (view);
 
-       e_html_editor_view_force_spell_check (view);
+       e_html_editor_view_force_spell_check_in_viewport (view);
 }
 
 void
@@ -9063,7 +9171,7 @@ e_html_editor_view_set_html_mode (EHTMLEditorView *view,
                                plain,
                                NULL);
                        e_html_editor_selection_restore (selection);
-                       e_html_editor_view_force_spell_check (view);
+                       e_html_editor_view_force_spell_check_in_viewport (view);
                }
 
                g_free (plain);
@@ -9118,7 +9226,7 @@ html_editor_view_drag_end_cb (EHTMLEditorView *view,
        else
                webkit_dom_dom_selection_collapse_to_end (selection, NULL);
 
-       e_html_editor_view_force_spell_check (view);
+       e_html_editor_view_force_spell_check_in_viewport (view);
 }
 
 static void
@@ -9268,6 +9376,8 @@ e_html_editor_view_init (EHTMLEditorView *view)
        view->priv->undo_redo_in_progress = FALSE;
        view->priv->dont_save_history_in_body_input = FALSE;
 
+       view->priv->spell_check_on_scroll_event_source_id = 0;
+
        /* Make WebKit think we are displaying a local file, so that it
         * does not block loading resources from file:// protocol */
        webkit_web_view_load_string (
@@ -10640,7 +10750,7 @@ undo_delete (EHTMLEditorView *view,
                        webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
                        NULL);
                e_html_editor_selection_restore (selection);
-               e_html_editor_view_force_spell_check (view);
+               e_html_editor_view_force_spell_check_in_viewport (view);
 
                return;
        }
@@ -10720,7 +10830,7 @@ undo_delete (EHTMLEditorView *view,
                }
 
                e_html_editor_selection_restore (selection);
-               e_html_editor_view_force_spell_check (view);
+               e_html_editor_view_force_spell_check_in_viewport (view);
        } else {
                WebKitDOMNode *nd;
 
@@ -11682,7 +11792,7 @@ undo_redo_citation_split (EHTMLEditorView *view,
 
                restore_selection_to_history_event_state (view, event->before);
 
-               e_html_editor_view_force_spell_check (view);
+               e_html_editor_view_force_spell_check_in_viewport (view);
        } else {
                insert_new_line_into_citation (view, "");
        }
diff --git a/e-util/e-html-editor-view.h b/e-util/e-html-editor-view.h
index f0d719e..5ba9118 100644
--- a/e-util/e-html-editor-view.h
+++ b/e-util/e-html-editor-view.h
@@ -235,6 +235,8 @@ void                e_html_editor_view_force_spell_check_for_current_paragraph
                                                (EHTMLEditorView *view);
 void           e_html_editor_view_force_spell_check
                                                (EHTMLEditorView *view);
+void           e_html_editor_view_force_spell_check_in_viewport
+                                               (EHTMLEditorView *view);
 void           e_html_editor_view_quote_plain_text_element_after_wrapping
                                                (WebKitDOMDocument *document,
                                                 WebKitDOMElement *element,


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