[evolution/webkit-composer: 73/231] Fix ESpellEntry crash



commit 01ea780f6c1d903a5e4535c23ab48296cf4b4334
Author: Dan Vrátil <dvratil redhat com>
Date:   Wed Aug 29 15:43:02 2012 +0200

    Fix ESpellEntry crash

 composer/e-composer-private.c       |   19 ++-
 e-util/e-editor-widget.c            |   23 ++--
 e-util/e-editor-window.c            |    4 +-
 e-util/e-spell-dictionary.c         |    3 +
 e-util/e-spell-entry.c              |   61 ++++----
 modules/mail/e-mail-shell-backend.c |    2 +-
 widgets/editor/selection-style.c    |  288 +++++++++++++++++++++++++++++++++++
 7 files changed, 346 insertions(+), 54 deletions(-)
---
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index 438d1c7..75151dd 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -131,8 +131,10 @@ composer_update_gallery_visibility (EMsgComposer *composer)
 
 static void
 composer_spell_languages_changed (EMsgComposer *composer,
-                                  GList *languages)
+                                 GParamSpec *pspec,
+                                 EEditorWidget *editor_widget)
 {
+       GList *languages;
        EComposerHeader *header;
        EComposerHeaderTable *table;
 
@@ -140,8 +142,11 @@ composer_spell_languages_changed (EMsgComposer *composer,
        header = e_composer_header_table_get_header (
                table, E_COMPOSER_HEADER_SUBJECT);
 
-       e_composer_spell_header_set_languages (
-               E_COMPOSER_SPELL_HEADER (header), languages);
+       languages = e_editor_widget_get_spell_languages (editor_widget);
+       header = e_composer_header_table_get_header (table, E_COMPOSER_HEADER_SUBJECT);
+       e_composer_spell_header_set_languages (E_COMPOSER_SPELL_HEADER (header), languages);
+
+       g_list_free (languages);
 }
 
 void
@@ -263,9 +268,9 @@ e_composer_private_constructed (EMsgComposer *composer)
        priv->header_table = g_object_ref (widget);
        gtk_widget_show (widget);
 
-       g_signal_connect (
-               G_OBJECT (composer), "spell-languages-changed",
-               G_CALLBACK (composer_spell_languages_changed), NULL);
+       g_signal_connect_swapped (
+               editor_widget, "notify::spell-languages",
+               G_CALLBACK (composer_spell_languages_changed), composer);
 
        /* Construct the attachment paned. */
 
@@ -939,7 +944,7 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
                "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
                "    key=\"signature\" value=\"1\">-->"
                "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
-               "    key=\"signature_name\" value=\"uid:%s\">-->",
+               "    key=\"signature_name\" value=\"uid:%s\"-->",
                (encoded_uid != NULL) ? encoded_uid : "");
 
        g_string_append (
diff --git a/e-util/e-editor-widget.c b/e-util/e-editor-widget.c
index 23f8143..83f43a3 100644
--- a/e-util/e-editor-widget.c
+++ b/e-util/e-editor-widget.c
@@ -806,6 +806,15 @@ e_editor_widget_class_init (EEditorWidgetClass *klass)
                        FALSE,
                        G_PARAM_READABLE));
 
+       g_object_class_install_property (
+               object_class,
+               PROP_SPELL_LANGUAGES,
+               g_param_spec_pointer (
+                       "spell-languages",
+                       "Active spell checking languages",
+                       NULL,
+                       G_PARAM_READWRITE));
+
        signals[POPUP_EVENT] = g_signal_new (
                "popup-event",
                G_TYPE_FROM_CLASS (klass),
@@ -1064,20 +1073,12 @@ void
 e_editor_widget_set_spell_languages (EEditorWidget *widget,
                                     GList *spell_languages)
 {
-       GList *iter;
-
        g_return_if_fail (E_IS_EDITOR_WIDGET (widget));
        g_return_if_fail (spell_languages);
 
-       g_list_free_full (widget->priv->spelling_langs, g_free);
-
-       widget->priv->spelling_langs = NULL;
-       for (iter = spell_languages; iter; iter = g_list_next (iter)) {
-               widget->priv->spelling_langs =
-                       g_list_append (
-                               widget->priv->spelling_langs,
-                               g_strdup (iter->data));
-       }
+       g_list_free_full (widget->priv->spelling_langs, g_object_unref);
+       widget->priv->spelling_langs = g_list_copy (spell_languages);
+       g_list_foreach (widget->priv->spelling_langs, (GFunc) g_object_ref, NULL);
 
        g_object_notify (G_OBJECT (widget), "spell-languages");
 }
diff --git a/e-util/e-editor-window.c b/e-util/e-editor-window.c
index fd55563..30a4286 100644
--- a/e-util/e-editor-window.c
+++ b/e-util/e-editor-window.c
@@ -147,8 +147,10 @@ e_editor_window_pack_above (EEditorWindow *window,
        g_return_if_fail (E_IS_EDITOR_WINDOW (window));
        g_return_if_fail (GTK_IS_WIDGET (child));
 
-       gtk_grid_insert_row (window->priv->main_layout, window->priv->editor_row);
+       gtk_grid_insert_row (
+               window->priv->main_layout, window->priv->editor_row);
        window->priv->editor_row++;
+
        gtk_grid_attach_next_to (
                window->priv->main_layout, child,
                GTK_WIDGET (window->priv->editor),
diff --git a/e-util/e-spell-dictionary.c b/e-util/e-spell-dictionary.c
index e5ed02a..002a5ac 100644
--- a/e-util/e-spell-dictionary.c
+++ b/e-util/e-spell-dictionary.c
@@ -647,5 +647,8 @@ gint
 e_spell_dictionary_compare (ESpellDictionary *dict1,
                            ESpellDictionary *dict2)
 {
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dict1), 0);
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dict2), 0);
+
        return strcmp (dict1->priv->collate_key, dict2->priv->collate_key);
 }
diff --git a/e-util/e-spell-entry.c b/e-util/e-spell-entry.c
index 7cfbff2..005976b 100644
--- a/e-util/e-spell-entry.c
+++ b/e-util/e-spell-entry.c
@@ -39,7 +39,7 @@ struct _ESpellEntryPrivate {
        GSettings *settings;
        gboolean custom_checkers;
        gboolean checking_enabled;
-       GSList *dictionaries;
+       GList *dictionaries;
        gchar **words;
        gint *word_starts;
        gint *word_ends;
@@ -77,12 +77,12 @@ word_misspelled (ESpellEntry *entry,
        g_strlcpy (word, text + start, end - start + 1);
 
        if (g_unichar_isalpha (*word)) {
-               GSList *li;
+               GList *li;
                gssize wlen = strlen (word);
 
-               for (li = entry->priv->dictionaries; li; li = g_slist_next (li)) {
-                       EnchantDict *dict = li->data;
-                       if (enchant_dict_check (dict, word, wlen)) {
+               for (li = entry->priv->dictionaries; li; li = g_list_next (li)) {
+                       ESpellDictionary *dict = li->data;
+                       if (e_spell_dictionary_check (dict, word, wlen)) {
                                result = FALSE;
                                break;
                        }
@@ -270,7 +270,7 @@ add_to_dictionary (GtkWidget *menuitem,
 {
        gchar *word;
        gint start, end;
-       EnchantDict *dict;
+       ESpellDictionary *dict;
 
        get_word_extents_from_position (
                entry, &start, &end, entry->priv->mark_character);
@@ -278,7 +278,7 @@ add_to_dictionary (GtkWidget *menuitem,
 
        dict = g_object_get_data (G_OBJECT (menuitem), "spell-entry-checker");
        if (dict != NULL)
-               enchant_dict_add_to_personal (dict, word, -1);
+               e_spell_dictionary_learn_word (dict, word, -1);
 
        g_free (word);
 
@@ -303,15 +303,15 @@ ignore_all (GtkWidget *menuitem,
 {
        gchar *word;
        gint start, end;
-       GSList *li;
+       GList *li;
 
        get_word_extents_from_position (
                entry, &start, &end, entry->priv->mark_character);
        word = gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
 
-       for (li = entry->priv->dictionaries; li; li = g_slist_next (li)) {
-               EnchantDict *dict = li->data;
-               enchant_dict_add_to_session (dict, word, -1);
+       for (li = entry->priv->dictionaries; li; li = g_list_next (li)) {
+               ESpellDictionary *dict = li->data;
+               e_spell_dictionary_ignore_word (dict, word, -1);
        }
 
        g_free (word);
@@ -339,7 +339,7 @@ replace_word (GtkWidget *menuitem,
        const gchar *newword;
        gint start, end;
        gint cursor;
-       EnchantDict *dict;
+       ESpellDictionary *dict;
 
        get_word_extents_from_position (
                entry, &start, &end, entry->priv->mark_character);
@@ -363,7 +363,7 @@ replace_word (GtkWidget *menuitem,
        dict = g_object_get_data (G_OBJECT (menuitem), "spell-entry-checker");
 
        if (dict != NULL)
-               enchant_dict_store_replacement (
+               e_spell_dictionary_store_correction (
                        dict, oldword, -1, newword, -1);
 
        g_free (oldword);
@@ -446,11 +446,11 @@ build_spelling_menu (ESpellEntry *entry,
                dict = entry->priv->dictionaries->data;
                build_suggestion_menu (entry, topmenu, dict, word);
        } else {
-               GSList *li;
+               GList *li;
                GtkWidget *menu;
                const gchar *lang_name;
 
-               for (li = entry->priv->dictionaries; li; li = g_slist_next (li)) {
+               for (li = entry->priv->dictionaries; li; li = g_list_next (li)) {
                        dict = li->data;
 
                        lang_name = e_spell_dictionary_get_name (dict);
@@ -490,14 +490,14 @@ build_spelling_menu (ESpellEntry *entry,
                        mi, "activate",
                        G_CALLBACK (add_to_dictionary), entry);
        } else {
-               GSList *li;
+               GList *li;
                GtkWidget *menu, *submi;
                const gchar *lang_name;
 
                menu = gtk_menu_new ();
                gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), menu);
 
-               for (li = entry->priv->dictionaries; li; li = g_slist_next (li)) {
+               for (li = entry->priv->dictionaries; li; li = g_list_next (li)) {
                        dict = li->data;
 
                        lang_name = e_spell_dictionary_get_name (dict);
@@ -763,7 +763,7 @@ spell_entry_dispose (GObject *object)
        g_clear_object (&priv->settings);
        g_clear_object (&priv->spell_checker);
 
-       g_slist_free_full (
+       g_list_free_full (
                priv->dictionaries, (GDestroyNotify) g_object_unref);
        priv->dictionaries = NULL;
 
@@ -901,30 +901,23 @@ e_spell_entry_new (void)
 
 void
 e_spell_entry_set_languages (ESpellEntry *spell_entry,
-                             GList *languages)
+                             GList *dictionaries)
 {
-       GList *iter;
-
-       g_return_if_fail (spell_entry != NULL);
+       g_return_if_fail (E_IS_SPELL_ENTRY (spell_entry));
 
        spell_entry->priv->custom_checkers = TRUE;
 
-       if (spell_entry->priv->dictionaries)
-               g_slist_free_full (spell_entry->priv->dictionaries, g_object_unref);
-       spell_entry->priv->dictionaries = NULL;
-
-       for (iter = languages; iter; iter = g_list_next (iter)) {
-               ESpellDictionary *dict = iter->data;
-
-               if (dict)
-                       spell_entry->priv->dictionaries =
-                               g_slist_prepend (spell_entry->priv->dictionaries, dict);
+       if (spell_entry->priv->dictionaries) {
+               g_list_free_full (spell_entry->priv->dictionaries, g_object_unref);
        }
+       spell_entry->priv->dictionaries = NULL;
 
-       spell_entry->priv->dictionaries = g_slist_reverse (spell_entry->priv->dictionaries);
+       spell_entry->priv->dictionaries = g_list_copy (dictionaries);
+       g_list_foreach (spell_entry->priv->dictionaries, (GFunc) g_object_ref, NULL);
 
-       if (gtk_widget_get_realized (GTK_WIDGET (spell_entry)))
+       if (gtk_widget_get_realized (GTK_WIDGET (spell_entry))) {
                spell_entry_recheck_all (spell_entry);
+       }
 }
 
 gboolean
diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c
index 09d44f0..086b166 100644
--- a/modules/mail/e-mail-shell-backend.c
+++ b/modules/mail/e-mail-shell-backend.c
@@ -382,7 +382,7 @@ mail_shell_backend_window_added_cb (GtkApplication *application,
                spell_languages = e_load_spell_languages (spell_checker);
 
                e_editor_widget_set_spell_languages (editor_widget, spell_languages);
-               g_list_free (spell_languages);
+               g_list_free_full (spell_languages, g_object_unref);
 
                settings = g_settings_new ("org.gnome.evolution.mail");
 
diff --git a/widgets/editor/selection-style.c b/widgets/editor/selection-style.c
new file mode 100644
index 0000000..d75f1f0
--- /dev/null
+++ b/widgets/editor/selection-style.c
@@ -0,0 +1,288 @@
+
+
+/*
+ * DO NOT replace type casting of WebKit types by GLib macros unless
+ * you know what you are doing (I do).
+ *
+ * Probably due to bugs in WebKitGtk+ DOM bindings these macros will
+ * produce runtime warnings, but the objects and class hierarchy ARE VALID.
+ *
+ * This mostly affects only WebKitDOMText, which is subclass
+ * of WebKitDOMNode, but the text nodes are rarely created as instances of
+ * WebKitDOMText. To make sure that you really can cast WebKitDOMNode to
+ * WebKitDOMText, check whether webkit_dom_node_get_node_type() == 3
+ * (3 is "text" node type). WebKitDOMNode is just a thin wrapper around
+ * WebKit's internal WebCore objects. Using get_node_type() is evaluated
+ * against properties of these internal object.
+ */
+
+
+
+static void
+normalize (WebKitDOMNode *node)
+{
+       WebKitDOMNodeList *children;
+       gulong ii;
+
+       /* Standard normalization */
+       webkit_dom_node_normalize (node);
+
+       children = webkit_dom_node_get_child_nodes (node);
+
+       ii = 0;
+       while (ii < webkit_dom_node_list_get_length (children)) {
+               WebKitDOMNode *child, *sibling;
+               gchar *tag_name, *sibling_tag_name;
+
+               child = webkit_dom_node_list_item (children, ii);
+
+               /* We are interested only in nodes representing HTML
+                * elements */
+               if (webkit_dom_node_get_node_type (child) != 1) {
+                       ii++;
+                       continue;
+               }
+
+               sibling = webkit_dom_node_get_next_sibling (child);
+
+               /* If sibling node is not an element, then skip the current
+                * element and the sibling node as well */
+               if (webkit_dom_node_get_node_type (sibling) != 1) {
+                       ii += 2;
+                       continue;
+               }
+
+               /* Recursively normalize the child element */
+               normalize (child);
+
+               tag_name = webkit_dom_element_get_tag_name (
+                               WEBKIT_DOM_ELEMENT (child));
+               sibling_tag_name = webkit_dom_element_get_tag_name (
+                               WEBKIT_DOM_ELEMENT (sibling));
+
+               if (g_strcmp0 (tag_name, sibling_tag_name) == 0) {
+                       gchar *str1, *str2, *inner_html;
+
+                       str1 = webkit_dom_html_element_get_inner_html (
+                               WEBKIT_DOM_HTML_ELEMENT (child));
+                       str2 = webkit_dom_html_element_get_inner_html (
+                               WEBKIT_DOM_HTML_ELEMENT (sibling));
+                       inner_html = g_strconcat (str1, str2, NULL);
+                       webkit_dom_html_element_set_inner_html (
+                               WEBKIT_DOM_HTML_ELEMENT (child), inner_html, NULL);
+
+                       g_free (str1);
+                       g_free (str2);
+                       g_free (inner_html);
+
+                       webkit_dom_node_remove_child (
+                               webkit_dom_node_get_parent_node (sibling),
+                               sibling, NULL);
+               }
+
+               ii++;
+       }
+}
+
+static void
+remove_format (EEditorSelection *selection,
+              const gchar *format_tag)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMRange *range;
+       WebKitDOMNode *start_node, *end_node;
+       WebKitDOMElement *common_ancestor;
+
+       document = webkit_web_view_get_dom_document (selection->priv->webview);
+       range = editor_selection_get_current_range (selection);
+
+       start_node = webkit_dom_range_get_start_container (range, NULL);
+       end_node = webkit_dom_range_get_end_container (range, NULL);
+
+       common_ancestor = webkit_dom_node_get_parent_element (
+                               webkit_dom_node_get_parent_node (
+                                       webkit_dom_range_get_common_ancestor_container (
+                                               range, NULL)));
+
+       /* Cool! The selection is all within one node */
+       if (start_node == end_node) {
+               WebKitDOMElement *element;
+               WebKitDOMNode *node = start_node;
+               WebKitDOMNodeList *children;
+               gchar *wrapper_tag_name;
+
+               if (webkit_dom_node_get_node_type (start_node) != 3) {
+                       /* XXX Is it possible for selection to start somewhere
+                        * else then in a text node? If yes, what should we
+                        * do about it? */
+                       return;
+               }
+
+               /* Split <b>|blabla SELECTED TEXT bla|</b> to
+                * <b>|blabla |SELECTED TEXT| bla|</b> (| indicates node) */
+               node = (WebKitDOMNode *) webkit_dom_text_split_text (
+                               (WebKitDOMText *) (node),
+                       webkit_dom_range_get_start_offset (range, NULL), NULL);
+               webkit_dom_text_split_text  (
+                               (WebKitDOMText *) node,
+                               webkit_dom_range_get_end_offset (range, NULL),
+                               NULL);
+
+               element = webkit_dom_node_get_parent_element (node);
+               children = webkit_dom_node_get_child_nodes (WEBKIT_DOM_NODE (element));
+               wrapper_tag_name = webkit_dom_element_get_tag_name (element);
+
+               while (webkit_dom_node_list_get_length (children) > 0) {
+                       WebKitDOMNode *child;
+
+                       child = webkit_dom_node_list_item (children, 0);
+
+                       if (child != node) {
+                               WebKitDOMElement *wrapper;
+                               wrapper = webkit_dom_document_create_element (
+                                       document, wrapper_tag_name, NULL);
+
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (wrapper), child, NULL);
+
+                               child = WEBKIT_DOM_NODE (wrapper);
+                       }
+
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (element)),
+                               child, WEBKIT_DOM_NODE (element), NULL);
+               }
+
+               /* Remove the now empty container */
+               /*
+               webkit_dom_node_remove_child (
+                       webkit_dom_node_get_parent_node (
+                               WEBKIT_DOM_NODE (element)),
+                       WEBKIT_DOM_NODE (element), NULL);
+               */
+
+               g_free (wrapper_tag_name);
+       }
+
+       normalize (WEBKIT_DOM_NODE (common_ancestor));
+}
+
+static void
+apply_format (EEditorSelection *editor_selection,
+             const gchar *format_tag)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMRange *range;
+       WebKitDOMNode *selection, *node;
+       WebKitDOMNode *new_parent;
+       gint format_tag_len = strlen (format_tag);
+       gboolean prev_sibling_match, next_sibling_match;
+
+       prev_sibling_match = FALSE;
+       next_sibling_match = FALSE;
+
+       document = webkit_web_view_get_dom_document (editor_selection->priv->webview);
+       range = editor_selection_get_current_range (editor_selection);
+
+       if (webkit_dom_range_get_start_offset (range, NULL) != 0) {
+               node = webkit_dom_range_get_start_container (range, NULL);
+               selection = (WebKitDOMNode*) webkit_dom_text_split_text (
+                       (WebKitDOMText *) node,
+                       webkit_dom_range_get_start_offset (range, NULL), NULL);
+       } else {
+               selection = webkit_dom_range_get_start_container (range, NULL);
+       }
+
+       webkit_dom_text_split_text ((WebKitDOMText *) selection,
+                       webkit_dom_range_get_end_offset (range, NULL), NULL);
+
+       /* The split above might have produced an empty text node
+        * (for example splitting "TEXT" on offset 4 will produce
+        * "TEXT" and "" nodes), so remove it */
+       node = webkit_dom_node_get_next_sibling (selection);
+       if (webkit_dom_node_get_node_type (node) == 3) {
+               gchar *content;
+
+               content = webkit_dom_node_get_text_content (node);
+               if (!content || (strlen (content) == 0)) {
+                       webkit_dom_node_remove_child (
+                               webkit_dom_node_get_parent_node (node),
+                               node, NULL);
+               }
+
+               g_free (content);
+       }
+
+       /* Check whether previous sibling is an element and whether it is <format_tag> */
+       node = webkit_dom_node_get_previous_sibling (selection);
+       if (node && (webkit_dom_node_get_node_type (node) == 1)) {
+               gchar *tag_name;
+
+               tag_name = webkit_dom_element_get_tag_name (
+                               (WebKitDOMElement *) node);
+               prev_sibling_match = ((format_tag_len == strlen (tag_name)) &&
+                                     (g_ascii_strncasecmp (
+                                               format_tag, tag_name,
+                                               format_tag_len) == 0));
+               g_free (tag_name);
+       }
+
+       /* Check whether next sibling is an element and whether it is <format_tag> */
+       node = webkit_dom_node_get_next_sibling (selection);
+       if (node && (webkit_dom_node_get_node_type (node) == 1)) {
+               gchar *tag_name;
+
+               tag_name = webkit_dom_element_get_tag_name (
+                               (WebKitDOMElement *) node);
+               next_sibling_match = ((format_tag_len == strlen (tag_name)) &&
+                                     (g_ascii_strncasecmp (
+                                               format_tag, tag_name,
+                                               format_tag_len) == 0));
+               g_free (tag_name);
+       }
+
+       /* Merge selection and next sibling to the orevious sibling */
+       if (prev_sibling_match && next_sibling_match) {
+               WebKitDOMNode *next_sibling, *child;
+
+               new_parent = webkit_dom_node_get_previous_sibling (selection);
+               next_sibling = webkit_dom_node_get_next_sibling (selection);
+
+               /* Append selection to the new parent */
+               webkit_dom_node_append_child (new_parent, selection, NULL);
+
+               /* Append all children of next sibling to the new parent */
+               while ((child = webkit_dom_node_get_first_child (next_sibling)) != NULL) {
+                       webkit_dom_node_append_child (new_parent, child, NULL);
+               }
+               webkit_dom_node_remove_child (
+                       webkit_dom_node_get_parent_node (next_sibling),
+                       next_sibling, NULL);
+
+       /* Merge selection to the previous sibling */
+       } else if (prev_sibling_match && !next_sibling_match) {
+               new_parent = webkit_dom_node_get_previous_sibling (selection);
+               webkit_dom_node_append_child (new_parent, selection, NULL);
+
+       /* Merge selection to the next sibling */
+       } else if (!prev_sibling_match && next_sibling_match) {
+               new_parent = webkit_dom_node_get_next_sibling (selection);
+               webkit_dom_node_insert_before (
+                       new_parent, selection,
+                       webkit_dom_node_get_first_child (new_parent), NULL);
+
+       /* Just wrap the selection to <tag_name> */
+       } else {
+               new_parent = (WebKitDOMNode *)
+                               webkit_dom_document_create_element (
+                                       document, format_tag, NULL);
+               webkit_dom_range_surround_contents (range, new_parent, NULL);
+       }
+
+       webkit_dom_node_normalize (
+               (WebKitDOMNode *) webkit_dom_node_get_parent_element (new_parent));
+}
+
+
+ 


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