[evolution/wip/webkit-composer: 556/966] Port spell-checking



commit e4f0adf34f5c12016d355509de57c2432b52324a
Author: Dan Vrátil <dvratil redhat com>
Date:   Thu Aug 16 15:14:58 2012 +0200

    Port spell-checking
    
    We use our own WebKitSpellChecker, which is in general the
    same as the WebKit's default, but it does not work with
    language codes, but directly with EnchantDicts. This allows
    us to remove ESpellDictionary and call enchant_ functions
    directly from EEditor* classes.
    
    FIXME: The spellchecking probably needs some polishing and lots
    of testing...

 e-util/Makefile.am                   |    4 +
 e-util/e-editor-actions.c            |  162 +++----
 e-util/e-editor-private.h            |    5 +
 e-util/e-editor-selection.c          |   79 +++
 e-util/e-editor-selection.h          |    8 +
 e-util/e-editor-spell-check-dialog.c |  336 +++++++++++
 e-util/e-editor-spell-check-dialog.h |   19 +
 e-util/e-editor-spell-checker.c      |  704 +++++++++++++++++++++++
 e-util/e-editor-spell-checker.h      |   85 +++
 e-util/e-editor-widget.c             |    7 +
 e-util/e-editor.c                    |  442 +++++++++++----
 e-util/e-editor.h                    |   47 +--
 e-util/e-spell-dialog.c              |  755 ------------------------
 e-util/e-spell-dialog.h              |   83 ---
 e-util/e-spell-dictionary.c          | 1042 ----------------------------------
 e-util/e-spell-dictionary.h          |  132 -----
 e-util/e-util.h                      |    4 +-
 17 files changed, 1667 insertions(+), 2247 deletions(-)
---
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 0c116de..75cdaf0 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -189,6 +189,8 @@ evolution_util_include_HEADERS =  \
        e-editor-paragraph-dialog.h \
        e-editor-replace-dialog.h \
        e-editor-selection.h \
+       e-editor-spell-check-dialog.h \
+       e-editor-spell-checker.h \
        e-editor-table-dialog.h \
        e-editor-text-dialog.h \
        e-editor-utils.h \
@@ -461,6 +463,8 @@ libevolution_util_la_SOURCES = \
        e-editor-private.h \
        e-editor-replace-dialog.c \
        e-editor-selection.c \
+       e-editor-spell-check-dialog.c \
+       e-editor-spell-checker.c \
        e-editor-table-dialog.c \
        e-editor-text-dialog.c \
        e-editor-utils.c \
diff --git a/e-util/e-editor-actions.c b/e-util/e-editor-actions.c
index cb216d0..4b147fe 100644
--- a/e-util/e-editor-actions.c
+++ b/e-util/e-editor-actions.c
@@ -15,8 +15,6 @@
  * Boston, MA 02111-1307, USA.
  */
 
-//#include "e-editor-private.h"
-
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
@@ -26,8 +24,9 @@
 #include <string.h>
 
 #include "e-editor.h"
-#include "e-editor-actions.h"
 #include "e-editor-private.h"
+#include "e-editor-actions.h"
+#include "e-editor-spell-checker.h"
 #include "e-editor-widgets.h"
 #include "e-editor-utils.h"
 #include "e-emoticon-action.h"
@@ -369,54 +368,34 @@ static void
 action_context_spell_add_cb (GtkAction *action,
                              EEditor *editor)
 {
-       WebKitDOMDocument *document;
-       WebKitDOMDOMWindow *window;
-       WebKitDOMDOMSelection *selection;
-       WebKitDOMRange *range;
        WebKitSpellChecker *spell_checker;
+       EEditorSelection *selection;
        gchar *word;
 
-       document = webkit_web_view_get_dom_document (
-                       WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
-       window = webkit_dom_document_get_default_view (document);
-       selection = webkit_dom_dom_window_get_selection (window);
-       if (webkit_dom_dom_selection_get_range_count (selection) < 1)
-               return;
-
-       range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
-       word = webkit_dom_range_get_text (range);
-
        spell_checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
-       webkit_spell_checker_learn_word (spell_checker, word);
+       selection = e_editor_widget_get_selection (editor->priv->editor_widget);
 
-       g_free (word);
+       word = e_editor_selection_get_caret_word (selection);
+       if (word && *word) {
+               webkit_spell_checker_learn_word (spell_checker, word);
+       }
 }
 
 static void
 action_context_spell_ignore_cb (GtkAction *action,
                                 EEditor *editor)
 {
-       WebKitDOMDocument *document;
-       WebKitDOMDOMWindow *window;
-       WebKitDOMDOMSelection *selection;
-       WebKitDOMRange *range;
        WebKitSpellChecker *spell_checker;
+       EEditorSelection *selection;
        gchar *word;
 
-       document = webkit_web_view_get_dom_document (
-                       WEBKIT_WEB_VIEW (e_editor_get_editor_widget (editor)));
-       window = webkit_dom_document_get_default_view (document);
-       selection = webkit_dom_dom_window_get_selection (window);
-       if (webkit_dom_dom_selection_get_range_count (selection) < 1)
-               return;
-
-       range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
-       word = webkit_dom_range_get_text (range);
-
        spell_checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
-       webkit_spell_checker_ignore_word (spell_checker, word);
+       selection = e_editor_widget_get_selection (editor->priv->editor_widget);
 
-       g_free (word);
+       word = e_editor_selection_get_caret_word (selection);
+       if (word && *word) {
+               webkit_spell_checker_ignore_word (spell_checker, word);
+       }
 }
 
 static void
@@ -596,46 +575,40 @@ static void
 action_language_cb (GtkToggleAction *action,
                     EEditor *editor)
 {
-       /* FIXME WEBKIT */
-       /*
-       const GtkhtmlSpellLanguage *language;
-       GtkhtmlSpellChecker *checker;
+       EEditorSpellChecker *checker;
+       EnchantDict *dictionary;
        const gchar *language_code;
        GtkAction *add_action;
-       GtkHTML *html;
        GList *list;
        guint length;
        gchar *action_name;
        gboolean active;
 
+       checker = E_EDITOR_SPELL_CHECKER (webkit_get_text_checker ());
        active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
        language_code = gtk_action_get_name (GTK_ACTION (action));
-       language = gtkhtml_spell_language_lookup (language_code);
-
-       checker = g_hash_table_lookup (
-               editor->priv->available_spell_checkers, language);
-       g_return_if_fail (checker != NULL);
+       dictionary = e_editor_spell_checker_lookup_dict (checker, language_code);
 
-       //Update the list of active spell checkers.
-       list = editor->priv->active_spell_checkers;
+       /* Update the list of active dictionaries */
+       list = editor->priv->active_dictionaries;
        if (active)
                list = g_list_insert_sorted (
-                       list, g_object_ref (checker),
-                       (GCompareFunc) gtkhtml_spell_checker_compare);
+                       list, (EnchantDict *) dictionary,
+                       (GCompareFunc) e_editor_spell_checker_dict_compare);
        else {
                GList *link;
 
-               link = g_list_find (list, checker);
+               link = g_list_find (list, dictionary);
                g_return_if_fail (link != NULL);
+               e_editor_spell_checker_free_dict (checker, link->data);
                list = g_list_delete_link (list, link);
-               g_object_unref (checker);
        }
-       editor->priv->active_spell_checkers = list;
+       editor->priv->active_dictionaries = list;
        length = g_list_length (list);
 
-       // Update "Add Word To" context menu item visibility.
+       /* Update "Add Word To" context menu item visibility. */
        action_name = g_strdup_printf ("context-spell-add-%s", language_code);
-       add_action = gtkhtml_editor_get_action (editor, action_name);
+       add_action = e_editor_get_action (editor, action_name);
        gtk_action_set_visible (add_action, active);
        g_free (action_name);
 
@@ -645,11 +618,7 @@ action_language_cb (GtkToggleAction *action,
 
        gtk_action_set_sensitive (ACTION (SPELL_CHECK), length > 0);
 
-       html = gtkhtml_editor_get_html (editor);
-       html_engine_spell_check (html->engine);
-
-       gtkthtml_editor_emit_spell_languages_changed (editor);
-       */
+       e_editor_emit_spell_languages_changed (editor);
 }
 
 struct _ModeChanged {
@@ -935,9 +904,16 @@ static void
 action_spell_check_cb (GtkAction *action,
                        EEditor *editor)
 {
-       /* FIXME WEBKIT
-       e_editor_widget_spell_check (editor);
-       */
+       if (editor->priv->spell_check_dialog == NULL) {
+               editor->priv->spell_check_dialog =
+                       e_editor_spell_check_dialog_new (editor);
+
+               e_editor_spell_check_dialog_set_dictionaries (
+                       E_EDITOR_SPELL_CHECK_DIALOG (editor->priv->spell_check_dialog),
+                       editor->priv->active_dictionaries);
+       }
+
+       gtk_window_present (GTK_WINDOW (editor->priv->spell_check_dialog));
 }
 
 static void
@@ -1708,31 +1684,25 @@ static GtkActionEntry spell_context_entries[] = {
 static void
 editor_actions_setup_languages_menu (EEditor *editor)
 {
-       /* FIXME WEBKIT
+       EEditorSpellChecker *checker;
        GtkUIManager *manager;
        GtkActionGroup *action_group;
-       const GList *available_languages;
+       const GList *available_dicts;
        guint merge_id;
 
        manager = editor->priv->manager;
        action_group = editor->priv->language_actions;
-       available_languages = gtkhtml_spell_language_get_available ();
+       checker = E_EDITOR_SPELL_CHECKER (webkit_get_text_checker ());
+       available_dicts = e_editor_spell_checker_get_available_dicts (checker);
        merge_id = gtk_ui_manager_new_merge_id (manager);
 
-       while (available_languages != NULL) {
-               GtkhtmlSpellLanguage *language = available_languages->data;
-               GtkhtmlSpellChecker *checker;
+       while (available_dicts != NULL) {
+               EnchantDict *dictionary = available_dicts->data;
                GtkToggleAction *action;
 
-               checker = gtkhtml_spell_checker_new (language);
-
-               g_hash_table_insert (
-                       editor->priv->available_spell_checkers,
-                       language, checker);
-
                action = gtk_toggle_action_new (
-                       gtkhtml_spell_language_get_code (language),
-                       gtkhtml_spell_language_get_name (language),
+                       e_editor_spell_checker_get_dict_code (dictionary),
+                       e_editor_spell_checker_get_dict_name (dictionary),
                        NULL, NULL);
 
                g_signal_connect (
@@ -1747,42 +1717,41 @@ editor_actions_setup_languages_menu (EEditor *editor)
                gtk_ui_manager_add_ui (
                        manager, merge_id,
                        "/main-menu/edit-menu/language-menu",
-                       gtkhtml_spell_language_get_code (language),
-                       gtkhtml_spell_language_get_code (language),
+                       e_editor_spell_checker_get_dict_code (dictionary),
+                       e_editor_spell_checker_get_dict_code (dictionary),
                        GTK_UI_MANAGER_AUTO, FALSE);
 
-               available_languages = g_list_next (available_languages);
+               available_dicts = g_list_next (available_dicts);
        }
-       */
 }
 
 static void
 editor_actions_setup_spell_check_menu (EEditor *editor)
 {
-       /*
+       EEditorSpellChecker *checker;
        GtkUIManager *manager;
        GtkActionGroup *action_group;
-       const GList *available_languages;
+       const GList *available_dicts;
        guint merge_id;
 
        manager = editor->priv->manager;
        action_group = editor->priv->spell_check_actions;;
-       available_languages = gtkhtml_spell_language_get_available ();
+       checker = E_EDITOR_SPELL_CHECKER (webkit_get_text_checker ());
+       available_dicts = e_editor_spell_checker_get_available_dicts (checker);
        merge_id = gtk_ui_manager_new_merge_id (manager);
 
-       while (available_languages != NULL) {
-               GtkhtmlSpellLanguage *language = available_languages->data;
+       while (available_dicts != NULL) {
+               EnchantDict *dictionary = available_dicts->data;
                GtkAction *action;
                const gchar *code;
                const gchar *name;
                gchar *action_label;
                gchar *action_name;
 
-               code = gtkhtml_spell_language_get_code (language);
-               name = gtkhtml_spell_language_get_name (language);
-
-               // Add a suggestion menu. 
+               code = e_editor_spell_checker_get_dict_code (dictionary);
+               name = e_editor_spell_checker_get_dict_name (dictionary);
 
+               /* Add a suggestion menu. */
                action_name = g_strdup_printf (
                        "context-spell-suggest-%s-menu", code);
 
@@ -1798,11 +1767,11 @@ editor_actions_setup_spell_check_menu (EEditor *editor)
 
                g_free (action_name);
 
-               // Add an item to the "Add Word To" menu.
-
+               /* Add an item to the "Add Word To" menu. */
                action_name = g_strdup_printf ("context-spell-add-%s", code);
-               // Translators: %s will be replaced with the actual dictionary name,
-               //where a user can add a word to. This is part of an "Add Word To" submenu.
+               /* Translators: %s will be replaced with the actual dictionary
+                * name, where a user can add a word to. This is part of an
+                * "Add Word To" submenu. */
                action_label = g_strdup_printf (_("%s Dictionary"), name);
 
                action = gtk_action_new (
@@ -1812,8 +1781,8 @@ editor_actions_setup_spell_check_menu (EEditor *editor)
                        action, "activate",
                        G_CALLBACK (action_context_spell_add_cb), editor);
 
-               // Visibility is dependent on whether the
-               //corresponding language action is active.
+               /* Visibility is dependent on whether the
+                  corresponding language action is active. */
                gtk_action_set_visible (action, FALSE);
 
                gtk_action_group_add_action (action_group, action);
@@ -1829,9 +1798,8 @@ editor_actions_setup_spell_check_menu (EEditor *editor)
                g_free (action_label);
                g_free (action_name);
 
-               available_languages = g_list_next (available_languages);
+               available_dicts = g_list_next (available_dicts);
        }
-       */
 }
 
 void
diff --git a/e-util/e-editor-private.h b/e-util/e-editor-private.h
index 6a47fdb..df0d166 100644
--- a/e-util/e-editor-private.h
+++ b/e-util/e-editor-private.h
@@ -34,6 +34,7 @@
 #include <e-editor-text-dialog.h>
 #include <e-editor-paragraph-dialog.h>
 #include <e-editor-cell-dialog.h>
+#include <e-editor-spell-check-dialog.h>
 
 #ifdef HAVE_XFREE
 #include <X11/XF86keysym.h>
@@ -72,6 +73,7 @@ struct _EEditorPrivate {
        GtkWidget *text_dialog;
        GtkWidget *paragraph_dialog;
        GtkWidget *cell_dialog;
+       GtkWidget *spell_check_dialog;
 
        GtkWidget *color_combo_box;
        GtkWidget *mode_combo_box;
@@ -84,6 +86,9 @@ struct _EEditorPrivate {
 
        gchar *filename;
 
+       guint spell_suggestions_merge_id;
+       GList *active_dictionaries;
+
        WebKitDOMNode *image;
        WebKitDOMNode *table_cell;
 };
diff --git a/e-util/e-editor-selection.c b/e-util/e-editor-selection.c
index b10d56d..b0c504c 100644
--- a/e-util/e-editor-selection.c
+++ b/e-util/e-editor-selection.c
@@ -575,6 +575,85 @@ e_editor_selection_new (WebKitWebView *parent_view)
                        "webview", parent_view, NULL);
 }
 
+gboolean
+e_editor_selection_has_text (EEditorSelection *selection)
+{
+       WebKitDOMRange *range;
+       WebKitDOMNode *node;
+
+       g_return_val_if_fail (E_IS_EDITOR_SELECTION (selection), FALSE);
+
+       range = editor_selection_get_current_range (selection);
+
+       node = webkit_dom_range_get_start_container (range, NULL);
+       if (webkit_dom_node_get_node_type (node) == 3) {
+               return TRUE;
+       }
+
+       node = webkit_dom_range_get_end_container (range, NULL);
+       if (webkit_dom_node_get_node_type (node) == 3) {
+               return TRUE;
+       }
+
+       node = WEBKIT_DOM_NODE (webkit_dom_range_clone_contents (range, NULL));
+       while (node) {
+               if (webkit_dom_node_get_node_type (node) == 3) {
+                       return TRUE;
+               }
+
+               if (webkit_dom_node_has_child_nodes (node)) {
+                       node = webkit_dom_node_get_first_child (node);
+               } else if (webkit_dom_node_get_next_sibling (node)) {
+                       node = webkit_dom_node_get_next_sibling (node);
+               } else {
+                       node = webkit_dom_node_get_parent_node (node);
+                       if (node) {
+                               node = webkit_dom_node_get_next_sibling (node);
+                       }
+               }
+       }
+
+       return FALSE;
+}
+
+gchar *
+e_editor_selection_get_caret_word (EEditorSelection *selection)
+{
+       WebKitDOMRange *range;
+
+       g_return_val_if_fail (E_IS_EDITOR_SELECTION (selection), NULL);
+
+       range = editor_selection_get_current_range (selection);
+
+       /* Don't operate on the visible selection */
+       range = webkit_dom_range_clone_range (range, NULL);
+       webkit_dom_range_expand (range, "word", NULL);
+
+       return webkit_dom_range_to_string (range, NULL);
+}
+
+void
+e_editor_selection_replace_caret_word (EEditorSelection *selection,
+                                      const gchar *replacement)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+
+       g_return_if_fail (E_IS_EDITOR_SELECTION (selection));
+       g_return_if_fail (replacement);
+
+       range = editor_selection_get_current_range (selection);
+       document = webkit_web_view_get_dom_document (selection->priv->webview);
+       window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (window);
+
+       webkit_dom_range_expand (range, "word", NULL);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+       e_editor_selection_insert_html (selection, replacement);
+}
 
 const gchar *
 e_editor_selection_get_string(EEditorSelection *selection)
diff --git a/e-util/e-editor-selection.h b/e-util/e-editor-selection.h
index 5614e72..043656e 100644
--- a/e-util/e-editor-selection.h
+++ b/e-util/e-editor-selection.h
@@ -98,6 +98,14 @@ GType                        e_editor_selection_get_type     (void);
 
 EEditorSelection *     e_editor_selection_new          (WebKitWebView *parent_view);
 
+gboolean               e_editor_selection_has_text     (EEditorSelection *selection);
+
+gchar *                        e_editor_selection_get_caret_word
+                                                       (EEditorSelection *selection);
+void                   e_editor_selection_replace_caret_word
+                                                       (EEditorSelection *selection,
+                                                        const gchar *replacement);
+
 void                   e_editor_selection_set_alignment
                                                        (EEditorSelection *selection,
                                                         EEditorSelectionAlignment alignment);
diff --git a/e-util/e-editor-spell-check-dialog.c b/e-util/e-editor-spell-check-dialog.c
index 75b0122..2a65e22 100644
--- a/e-util/e-editor-spell-check-dialog.c
+++ b/e-util/e-editor-spell-check-dialog.c
@@ -1,7 +1,11 @@
+<<<<<<< HEAD
 /*
  * e-editor-spell-dialog.c
  *
  * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+=======
+/* e-editor-spell-dialog.c
+>>>>>>> Port spell-checking
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU Lesser General Public
@@ -27,6 +31,7 @@
 #include <glib/gi18n-lib.h>
 #include <enchant/enchant.h>
 
+<<<<<<< HEAD
 #include "e-editor-widget.h"
 #include "e-spell-checker.h"
 #include "e-spell-dictionary.h"
@@ -34,6 +39,21 @@
 #define E_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
        ((obj), E_TYPE_EDITOR_SPELL_CHECK_DIALOG, EEditorSpellCheckDialogPrivate))
+=======
+#include "e-editor-spell-checker.h"
+#include "e-editor-widget.h"
+
+G_DEFINE_TYPE (
+       EEditorSpellCheckDialog,
+       e_editor_spell_check_dialog,
+       E_TYPE_EDITOR_DIALOG
+);
+
+enum {
+       COMBO_COLUMN_DICTIONARY,        /* E_TYPE_SPELL_DICTIONARY */
+       COMBO_COLUMN_TEXT               /* G_TYPE_STRING */
+};
+>>>>>>> Port spell-checking
 
 struct _EEditorSpellCheckDialogPrivate {
        GtkWidget *add_word_button;
@@ -45,6 +65,7 @@ struct _EEditorSpellCheckDialogPrivate {
        GtkWidget *skip_button;
        GtkWidget *suggestion_label;
        GtkWidget *tree_view;
+<<<<<<< HEAD
 
        WebKitDOMDOMSelection *selection;
 
@@ -76,6 +97,29 @@ editor_spell_check_dialog_set_word (EEditorSpellCheckDialog *dialog,
 
        if (word == NULL)
                return;
+=======
+       GtkWidget *close_button;
+
+       GList *dictionaries;
+       WebKitDOMDOMSelection *selection;
+
+       gchar *word;
+       EnchantDict *current_dict;
+};
+
+static void
+editor_spell_check_dialog_set_word (EEditorSpellCheckDialog *dialog,
+                                   const gchar *word)
+{
+       GtkListStore *store;
+       gchar *markup;
+       gchar **suggestions;
+       gint ii;
+
+       if (word == NULL) {
+               return;
+       }
+>>>>>>> Port spell-checking
 
        if (dialog->priv->word != word) {
                g_free (dialog->priv->word);
@@ -87,6 +131,7 @@ editor_spell_check_dialog_set_word (EEditorSpellCheckDialog *dialog,
                GTK_LABEL (dialog->priv->suggestion_label), markup);
        g_free (markup);
 
+<<<<<<< HEAD
        tree_view = GTK_TREE_VIEW (dialog->priv->tree_view);
        store = GTK_LIST_STORE (gtk_tree_view_get_model (tree_view));
        gtk_list_store_clear (store);
@@ -97,11 +142,24 @@ editor_spell_check_dialog_set_word (EEditorSpellCheckDialog *dialog,
        for (link = list; link != NULL; link = g_list_next (link)) {
                GtkTreeIter iter;
                gchar *suggestion = link->data;
+=======
+       store = GTK_LIST_STORE (
+                       gtk_tree_view_get_model (
+                               GTK_TREE_VIEW (dialog->priv->tree_view)));
+       gtk_list_store_clear (store);
+
+       suggestions = enchant_dict_suggest (
+                       dialog->priv->current_dict, word, -1, NULL);
+       for (ii = 0; suggestions && suggestions[ii]; ii++) {
+               GtkTreeIter iter;
+               gchar *suggestion = suggestions[ii];
+>>>>>>> Port spell-checking
 
                gtk_list_store_append (store, &iter);
                gtk_list_store_set (store, &iter, 0, suggestion, -1);
        }
 
+<<<<<<< HEAD
        g_list_free_full (list, (GDestroyNotify) g_free);
 
        /* We give focus to WebKit so that the currently selected word
@@ -112,20 +170,31 @@ editor_spell_check_dialog_set_word (EEditorSpellCheckDialog *dialog,
        editor = e_editor_dialog_get_editor (E_EDITOR_DIALOG (dialog));
        editor_widget = e_editor_get_editor_widget (editor);
        gtk_widget_grab_focus (GTK_WIDGET (editor_widget));
+=======
+       g_strfreev (suggestions);
+>>>>>>> Port spell-checking
 }
 
 static gboolean
 select_next_word (EEditorSpellCheckDialog *dialog)
 {
+<<<<<<< HEAD
        WebKitDOMNode *anchor, *focus;
        gulong anchor_offset, focus_offset;
+=======
+       WebKitDOMNode *anchor;
+       gulong anchor_offset;
+>>>>>>> Port spell-checking
 
        anchor = webkit_dom_dom_selection_get_anchor_node (dialog->priv->selection);
        anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dialog->priv->selection);
 
+<<<<<<< HEAD
        focus = webkit_dom_dom_selection_get_focus_node (dialog->priv->selection);
        focus_offset = webkit_dom_dom_selection_get_focus_offset (dialog->priv->selection);
 
+=======
+>>>>>>> Port spell-checking
        /* Jump _behind_ next word */
        webkit_dom_dom_selection_modify (
                dialog->priv->selection, "move", "forward", "word");
@@ -136,6 +205,7 @@ select_next_word (EEditorSpellCheckDialog *dialog)
        webkit_dom_dom_selection_modify (
                dialog->priv->selection, "extend", "forward", "word");
 
+<<<<<<< HEAD
        /* If the selection didn't change, then we have most probably
         * reached the end of document - return FALSE */
        return !((anchor == webkit_dom_dom_selection_get_anchor_node (
@@ -149,6 +219,18 @@ select_next_word (EEditorSpellCheckDialog *dialog)
 }
 
 static gboolean
+=======
+       /* If the selection start didn't change, then we have most probably
+        * reached the end of document - return FALSE */
+       return ((anchor != webkit_dom_dom_selection_get_anchor_node (
+                               dialog->priv->selection)) ||
+               (anchor_offset != webkit_dom_dom_selection_get_anchor_offset (
+                               dialog->priv->selection)));
+}
+
+
+static void
+>>>>>>> Port spell-checking
 editor_spell_check_dialog_next (EEditorSpellCheckDialog *dialog)
 {
        WebKitDOMNode *start = NULL, *end = NULL;
@@ -160,6 +242,7 @@ editor_spell_check_dialog_next (EEditorSpellCheckDialog *dialog)
        } else {
                /* Remember last selected word */
                start = webkit_dom_dom_selection_get_anchor_node (
+<<<<<<< HEAD
                        dialog->priv->selection);
                end = webkit_dom_dom_selection_get_focus_node (
                        dialog->priv->selection);
@@ -167,6 +250,15 @@ editor_spell_check_dialog_next (EEditorSpellCheckDialog *dialog)
                        dialog->priv->selection);
                end_offset = webkit_dom_dom_selection_get_focus_offset (
                        dialog->priv->selection);
+=======
+                                       dialog->priv->selection);
+               end = webkit_dom_dom_selection_get_focus_node (
+                                       dialog->priv->selection);
+               start_offset = webkit_dom_dom_selection_get_anchor_offset (
+                                       dialog->priv->selection);
+               end_offset = webkit_dom_dom_selection_get_focus_offset (
+                                       dialog->priv->selection);
+>>>>>>> Port spell-checking
        }
 
        while (select_next_word (dialog)) {
@@ -175,8 +267,12 @@ editor_spell_check_dialog_next (EEditorSpellCheckDialog *dialog)
                gint loc, len;
                gchar *word;
 
+<<<<<<< HEAD
                range = webkit_dom_dom_selection_get_range_at (
                        dialog->priv->selection, 0, NULL);
+=======
+               range = webkit_dom_dom_selection_get_range_at (dialog->priv->selection, 0, NULL);
+>>>>>>> Port spell-checking
                word = webkit_dom_range_get_text (range);
 
                checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
@@ -187,7 +283,11 @@ editor_spell_check_dialog_next (EEditorSpellCheckDialog *dialog)
                if (loc != -1) {
                        editor_spell_check_dialog_set_word (dialog, word);
                        g_free (word);
+<<<<<<< HEAD
                        return TRUE;
+=======
+                       return;
+>>>>>>> Port spell-checking
                }
 
                g_free (word);
@@ -202,13 +302,18 @@ editor_spell_check_dialog_next (EEditorSpellCheckDialog *dialog)
        }
 
        /* Close the dialog */
+<<<<<<< HEAD
        gtk_widget_hide (GTK_WIDGET (dialog));
        return FALSE;
+=======
+       gtk_widget_hide (GTK_WIDGET (dialog));
+>>>>>>> Port spell-checking
 }
 
 static gboolean
 select_previous_word (EEditorSpellCheckDialog *dialog)
 {
+<<<<<<< HEAD
        WebKitDOMNode *old_anchor_node;
        WebKitDOMNode *new_anchor_node;
        gulong old_anchor_offset;
@@ -218,6 +323,13 @@ select_previous_word (EEditorSpellCheckDialog *dialog)
                dialog->priv->selection);
        old_anchor_offset = webkit_dom_dom_selection_get_anchor_offset (
                dialog->priv->selection);
+=======
+       WebKitDOMNode *anchor;
+       gulong anchor_offset;
+
+       anchor = webkit_dom_dom_selection_get_anchor_node (dialog->priv->selection);
+       anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dialog->priv->selection);
+>>>>>>> Port spell-checking
 
        /* Jump on the beginning of current word */
        webkit_dom_dom_selection_modify (
@@ -231,6 +343,7 @@ select_previous_word (EEditorSpellCheckDialog *dialog)
 
        /* If the selection start didn't change, then we have most probably
         * reached the beginnig of document. Return FALSE */
+<<<<<<< HEAD
 
        new_anchor_node = webkit_dom_dom_selection_get_anchor_node (
                dialog->priv->selection);
@@ -264,6 +377,35 @@ editor_spell_check_dialog_prev (EEditorSpellCheckDialog *dialog)
                        dialog->priv->selection);
                end_offset = webkit_dom_dom_selection_get_focus_offset (
                        dialog->priv->selection);
+=======
+       return ((anchor != webkit_dom_dom_selection_get_anchor_node (
+                               dialog->priv->selection)) ||
+               (anchor_offset != webkit_dom_dom_selection_get_anchor_offset (
+                               dialog->priv->selection)));
+}
+
+static void
+editor_spell_check_dialog_prev (EEditorSpellCheckDialog *dialog)
+{
+       WebKitDOMNode *start = NULL, *end = NULL;
+       gulong start_offset, end_offset;        
+
+       if (dialog->priv->word == NULL) {
+               webkit_dom_dom_selection_modify (
+                       dialog->priv->selection, "move", "right", "documentboundary");
+               webkit_dom_dom_selection_modify (
+                       dialog->priv->selection, "extend", "backward", "word");
+       } else {
+               /* Remember last selected word */
+               start = webkit_dom_dom_selection_get_anchor_node (
+                                       dialog->priv->selection);
+               end = webkit_dom_dom_selection_get_focus_node (
+                                       dialog->priv->selection);
+               start_offset = webkit_dom_dom_selection_get_anchor_offset (
+                                       dialog->priv->selection);
+               end_offset = webkit_dom_dom_selection_get_focus_offset (
+                                       dialog->priv->selection);
+>>>>>>> Port spell-checking
        }
 
        while (select_previous_word (dialog)) {
@@ -272,19 +414,31 @@ editor_spell_check_dialog_prev (EEditorSpellCheckDialog *dialog)
                gint loc, len;
                gchar *word;
 
+<<<<<<< HEAD
                range = webkit_dom_dom_selection_get_range_at (
                        dialog->priv->selection, 0, NULL);
+=======
+               range = webkit_dom_dom_selection_get_range_at (dialog->priv->selection, 0, NULL);
+>>>>>>> Port spell-checking
                word = webkit_dom_range_get_text (range);
 
                checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
                webkit_spell_checker_check_spelling_of_string (
+<<<<<<< HEAD
                        checker, word, &loc, &len);
+=======
+                               checker, word, &loc, &len);
+>>>>>>> Port spell-checking
 
                /* Found misspelled word! */
                if (loc != -1) {
                        editor_spell_check_dialog_set_word (dialog, word);
                        g_free (word);
+<<<<<<< HEAD
                        return TRUE;
+=======
+                       return;
+>>>>>>> Port spell-checking
                }
 
                g_free (word);
@@ -300,7 +454,10 @@ editor_spell_check_dialog_prev (EEditorSpellCheckDialog *dialog)
 
        /* Close the dialog */
        gtk_widget_hide (GTK_WIDGET (dialog));
+<<<<<<< HEAD
        return FALSE;
+=======
+>>>>>>> Port spell-checking
 }
 
 static void
@@ -319,7 +476,11 @@ editor_spell_check_dialog_replace (EEditorSpellCheckDialog *dialog)
        editor_selection = e_editor_widget_get_selection (widget);
 
        selection = gtk_tree_view_get_selection (
+<<<<<<< HEAD
                GTK_TREE_VIEW (dialog->priv->tree_view));
+=======
+                       GTK_TREE_VIEW (dialog->priv->tree_view));
+>>>>>>> Port spell-checking
        gtk_tree_selection_get_selected (selection, &model, &iter);
        gtk_tree_model_get (model, &iter, 0, &replacement, -1);
 
@@ -346,7 +507,11 @@ editor_spell_check_dialog_replace_all (EEditorSpellCheckDialog *dialog)
        editor_selection = e_editor_widget_get_selection (widget);
 
        selection = gtk_tree_view_get_selection (
+<<<<<<< HEAD
                GTK_TREE_VIEW (dialog->priv->tree_view));
+=======
+                       GTK_TREE_VIEW (dialog->priv->tree_view));
+>>>>>>> Port spell-checking
        gtk_tree_selection_get_selected (selection, &model, &iter);
        gtk_tree_model_get (model, &iter, 0, &replacement, -1);
 
@@ -359,6 +524,10 @@ editor_spell_check_dialog_replace_all (EEditorSpellCheckDialog *dialog)
 
                e_editor_selection_insert_html (
                        editor_selection, replacement);
+<<<<<<< HEAD
+=======
+
+>>>>>>> Port spell-checking
        }
 
        g_free (replacement);
@@ -368,10 +537,18 @@ editor_spell_check_dialog_replace_all (EEditorSpellCheckDialog *dialog)
 static void
 editor_spell_check_dialog_ignore (EEditorSpellCheckDialog *dialog)
 {
+<<<<<<< HEAD
        if (dialog->priv->word == NULL)
                return;
 
        e_spell_dictionary_ignore_word (
+=======
+       if (dialog->priv->word == NULL) {
+               return;
+       }
+
+       enchant_dict_add_to_session (
+>>>>>>> Port spell-checking
                dialog->priv->current_dict, dialog->priv->word, -1);
 
        editor_spell_check_dialog_next (dialog);
@@ -380,10 +557,18 @@ editor_spell_check_dialog_ignore (EEditorSpellCheckDialog *dialog)
 static void
 editor_spell_check_dialog_learn (EEditorSpellCheckDialog *dialog)
 {
+<<<<<<< HEAD
        if (dialog->priv->word == NULL)
                return;
 
        e_spell_dictionary_learn_word (
+=======
+       if (dialog->priv->word == NULL) {
+               return;
+       }
+
+       enchant_dict_add_to_personal (
+>>>>>>> Port spell-checking
                dialog->priv->current_dict, dialog->priv->word, -1);
 
        editor_spell_check_dialog_next (dialog);
@@ -392,6 +577,7 @@ editor_spell_check_dialog_learn (EEditorSpellCheckDialog *dialog)
 static void
 editor_spell_check_dialog_set_dictionary (EEditorSpellCheckDialog *dialog)
 {
+<<<<<<< HEAD
        GtkComboBox *combo_box;
        GtkTreeModel *model;
        GtkTreeIter iter;
@@ -400,6 +586,16 @@ editor_spell_check_dialog_set_dictionary (EEditorSpellCheckDialog *dialog)
        combo_box = GTK_COMBO_BOX (dialog->priv->dictionary_combo);
        gtk_combo_box_get_active_iter (combo_box, &iter);
        model = gtk_combo_box_get_model (combo_box);
+=======
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       EnchantDict *dictionary;
+
+       gtk_combo_box_get_active_iter (
+                       GTK_COMBO_BOX (dialog->priv->dictionary_combo), &iter);
+       model = gtk_combo_box_get_model (
+                       GTK_COMBO_BOX (dialog->priv->dictionary_combo));
+>>>>>>> Port spell-checking
 
        gtk_tree_model_get (model, &iter, 1, &dictionary, -1);
 
@@ -410,6 +606,7 @@ editor_spell_check_dialog_set_dictionary (EEditorSpellCheckDialog *dialog)
 }
 
 static void
+<<<<<<< HEAD
 editor_spell_check_dialog_show (GtkWidget *widget)
 {
        EEditorSpellCheckDialog *dialog;
@@ -417,6 +614,19 @@ editor_spell_check_dialog_show (GtkWidget *widget)
        WebKitDOMDOMWindow *window;
 
        dialog = E_EDITOR_SPELL_CHECK_DIALOG (widget);
+=======
+editor_spell_check_dialog_show (GtkWidget *gtk_widget)
+{
+       EEditorSpellCheckDialog *dialog;
+       EEditor *editor;
+       EEditorWidget *widget;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+
+       dialog = E_EDITOR_SPELL_CHECK_DIALOG (gtk_widget);
+       editor = e_editor_dialog_get_editor (E_EDITOR_DIALOG (dialog));
+       widget = e_editor_get_editor_widget (editor);
+>>>>>>> Port spell-checking
 
        g_free (dialog->priv->word);
        dialog->priv->word = NULL;
@@ -425,6 +635,7 @@ editor_spell_check_dialog_show (GtkWidget *widget)
        window = webkit_dom_document_get_default_view (document);
        dialog->priv->selection = webkit_dom_dom_window_get_selection (window);
 
+<<<<<<< HEAD
        /* Select the first word or quit */
        if (editor_spell_check_dialog_next (dialog)) {
                GTK_WIDGET_CLASS (e_editor_spell_check_dialog_parent_class)->
@@ -461,10 +672,32 @@ editor_spell_check_dialog_constructed (GObject *object)
 
 static void
 e_editor_spell_check_dialog_class_init (EEditorSpellCheckDialogClass *class)
+=======
+       /* Select the first word */
+       editor_spell_check_dialog_next (dialog);
+
+       GTK_WIDGET_CLASS (e_editor_spell_check_dialog_parent_class)->show (gtk_widget);
+}
+
+static void
+editor_spell_check_dialog_finalize (GObject *object)
+{
+       EEditorSpellCheckDialog *dialog;
+
+       dialog = E_EDITOR_SPELL_CHECK_DIALOG (object);
+
+       g_free (dialog->priv->word);
+       dialog->priv->word = NULL;
+}
+
+static void
+e_editor_spell_check_dialog_class_init (EEditorSpellCheckDialogClass *klass)
+>>>>>>> Port spell-checking
 {
        GtkWidgetClass *widget_class;
        GObjectClass *object_class;
 
+<<<<<<< HEAD
        g_type_class_add_private (
                class, sizeof (EEditorSpellCheckDialogPrivate));
 
@@ -473,6 +706,15 @@ e_editor_spell_check_dialog_class_init (EEditorSpellCheckDialogClass *class)
        object_class->constructed = editor_spell_check_dialog_constructed;
 
        widget_class = GTK_WIDGET_CLASS (class);
+=======
+       e_editor_spell_check_dialog_parent_class = g_type_class_peek_parent (klass);
+       g_type_class_add_private (klass, sizeof (EEditorSpellCheckDialogPrivate));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->finalize = editor_spell_check_dialog_finalize;
+
+       widget_class = GTK_WIDGET_CLASS (klass);
+>>>>>>> Port spell-checking
        widget_class->show = editor_spell_check_dialog_show;
 }
 
@@ -480,14 +722,29 @@ static void
 e_editor_spell_check_dialog_init (EEditorSpellCheckDialog *dialog)
 {
        GtkWidget *widget;
+<<<<<<< HEAD
        GtkGrid *main_layout;
+=======
+       GtkGrid *main_layout;
+>>>>>>> Port spell-checking
        GtkListStore *store;
        GtkTreeViewColumn *column;
        GtkCellRenderer *renderer;
 
+<<<<<<< HEAD
        dialog->priv = E_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE (dialog);
 
        main_layout = e_editor_dialog_get_container (E_EDITOR_DIALOG (dialog));
+=======
+       dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+               dialog, E_TYPE_EDITOR_SPELL_CHECK_DIALOG, EEditorSpellCheckDialogPrivate);
+
+       main_layout = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (main_layout, 10);
+       gtk_grid_set_column_spacing (main_layout, 10);
+       gtk_container_add (GTK_CONTAINER (dialog), GTK_WIDGET (main_layout));
+       gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
+>>>>>>> Port spell-checking
 
        /* == Suggestions == */
        widget = gtk_label_new ("");
@@ -505,7 +762,11 @@ e_editor_spell_check_dialog_init (EEditorSpellCheckDialog *dialog)
 
        /* Column */
        column = gtk_tree_view_column_new_with_attributes (
+<<<<<<< HEAD
                "", gtk_cell_renderer_text_new (), "text", 0, NULL);
+=======
+                       "", gtk_cell_renderer_text_new (), "text", 0, NULL);
+>>>>>>> Port spell-checking
        gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
 
        /* Store */
@@ -623,12 +884,29 @@ e_editor_spell_check_dialog_init (EEditorSpellCheckDialog *dialog)
                widget, "clicked",
                G_CALLBACK (editor_spell_check_dialog_learn), dialog);
 
+<<<<<<< HEAD
+=======
+       /* Close Button */
+       widget = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
+       dialog->priv->close_button = widget;
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (gtk_widget_hide), dialog);
+
+       /* Button box */
+       widget = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+       gtk_button_box_set_layout (GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_END);
+       gtk_grid_attach (main_layout, widget, 0, 8, 2, 1);
+       gtk_box_pack_start (GTK_BOX (widget), dialog->priv->close_button, FALSE, FALSE, 5);
+
+>>>>>>> Port spell-checking
        gtk_widget_show_all (GTK_WIDGET (main_layout));
 }
 
 GtkWidget *
 e_editor_spell_check_dialog_new (EEditor *editor)
 {
+<<<<<<< HEAD
        return g_object_new (
                E_TYPE_EDITOR_SPELL_CHECK_DIALOG,
                "editor", editor,
@@ -684,10 +962,56 @@ e_editor_spell_check_dialog_update_dictionaries (EEditorSpellCheckDialog *dialog
 
                dictionary = g_queue_pop_head (&queue);
                name = e_spell_dictionary_get_name (dictionary);
+=======
+       return GTK_WIDGET (
+               g_object_new (
+                       E_TYPE_EDITOR_SPELL_CHECK_DIALOG,
+                       "editor", editor,
+                       "title", N_("Spell Checking"),
+                       NULL));
+}
+
+GList *
+e_editor_spell_check_dialog_get_dictionaries (EEditorSpellCheckDialog *dialog)
+{
+       g_return_val_if_fail (E_IS_EDITOR_SPELL_CHECK_DIALOG (dialog), NULL);
+
+       return g_list_copy (dialog->priv->dictionaries);
+}
+
+void
+e_editor_spell_check_dialog_set_dictionaries (EEditorSpellCheckDialog *dialog,
+                                             GList *dictionaries)
+{
+       GtkComboBox *combo_box;
+       GtkListStore *store;
+       GList *list;
+
+       g_return_if_fail (E_IS_EDITOR_SPELL_CHECK_DIALOG (dialog));
+
+       combo_box = GTK_COMBO_BOX (dialog->priv->dictionary_combo);
+
+       /* Free the old list of spell checkers. */
+       g_list_free (dialog->priv->dictionaries);
+
+       /* Copy and sort the new list of spell checkers. */
+       list = g_list_sort (
+               g_list_copy (dictionaries),
+               (GCompareFunc) e_editor_spell_checker_dict_compare);
+       dialog->priv->dictionaries = list;
+
+       /* Populate a list store for the combo box. */
+       store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
+
+       while (list != NULL) {
+               EnchantDict *dictionary = list->data;
+               GtkTreeIter iter;
+>>>>>>> Port spell-checking
 
                gtk_list_store_append (store, &iter);
                gtk_list_store_set (
                        store, &iter,
+<<<<<<< HEAD
                        COLUMN_NAME, name,
                        COLUMN_DICTIONARY, dictionary,
                        -1);
@@ -697,9 +1021,21 @@ e_editor_spell_check_dialog_update_dictionaries (EEditorSpellCheckDialog *dialog
 
        /* FIXME Try to restore selection. */
        combo_box = GTK_COMBO_BOX (dialog->priv->dictionary_combo);
+=======
+                       0, e_editor_spell_checker_get_dict_name (dictionary),
+                       1, dictionary, -1);
+
+               list = g_list_next (list);
+       }
+
+       /* FIXME: Try to restore selection */
+>>>>>>> Port spell-checking
        gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (store));
        gtk_combo_box_set_active (combo_box, 0);
 
        g_object_unref (store);
 }
+<<<<<<< HEAD
 
+=======
+>>>>>>> Port spell-checking
diff --git a/e-util/e-editor-spell-check-dialog.h b/e-util/e-editor-spell-check-dialog.h
index c368ec6..a1a52cf 100644
--- a/e-util/e-editor-spell-check-dialog.h
+++ b/e-util/e-editor-spell-check-dialog.h
@@ -1,7 +1,11 @@
+<<<<<<< HEAD
 /*
  * e-editor-spell-check-dialog.h
  *
  * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
+=======
+/* e-editor-spell-check-dialog.h
+>>>>>>> Port spell-checking
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU Lesser General Public
@@ -54,6 +58,10 @@ typedef struct _EEditorSpellCheckDialogPrivate EEditorSpellCheckDialogPrivate;
 
 struct _EEditorSpellCheckDialog {
        EEditorDialog parent;
+<<<<<<< HEAD
+=======
+
+>>>>>>> Port spell-checking
        EEditorSpellCheckDialogPrivate *priv;
 };
 
@@ -61,12 +69,23 @@ struct _EEditorSpellCheckDialogClass {
        EEditorDialogClass parent_class;
 };
 
+<<<<<<< HEAD
 GType          e_editor_spell_check_dialog_get_type
                                        (void) G_GNUC_CONST;
 GtkWidget *    e_editor_spell_check_dialog_new
                                        (EEditor *editor);
 void           e_editor_spell_check_dialog_update_dictionaries
                                        (EEditorSpellCheckDialog *dialog);
+=======
+GType          e_editor_spell_check_dialog_get_type    (void);
+GtkWidget *    e_editor_spell_check_dialog_new         (EEditor *editor);
+
+GList *                e_editor_spell_check_dialog_get_dictionaries
+                                                       (EEditorSpellCheckDialog *dialog);
+void           e_editor_spell_check_dialog_set_dictionaries
+                                                       (EEditorSpellCheckDialog *dialog,
+                                                        GList *dictionaries);
+>>>>>>> Port spell-checking
 
 G_END_DECLS
 
diff --git a/e-util/e-editor-spell-checker.c b/e-util/e-editor-spell-checker.c
new file mode 100644
index 0000000..b7c100f
--- /dev/null
+++ b/e-util/e-editor-spell-checker.c
@@ -0,0 +1,704 @@
+ /*
+ * e-editor-spell-checker.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+ /* Based on webkitspellcheckerenchant.cpp */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-editor-spell-checker.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <pango/pango.h>
+#include <enchant/enchant.h>
+#include <webkit/webkitspellchecker.h>
+
+#include "e-editor.h"
+
+static void e_editor_spell_checker_interface_init (WebKitSpellCheckerInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (
+       EEditorSpellChecker,
+       e_editor_spell_checker,
+       G_TYPE_OBJECT,
+       0,
+       G_IMPLEMENT_INTERFACE (
+               WEBKIT_TYPE_SPELL_CHECKER,
+               e_editor_spell_checker_interface_init));
+
+#define ISO_639_DOMAIN "iso_639"
+#define ISO_3166_DOMAIN        "iso_3166"
+
+struct _EEditorSpellCheckerPrivate {
+       EnchantBroker *broker;
+
+       GList *dicts;
+
+       /* Dictionary to which to write new words */
+       EnchantDict *write_dictionary;
+};
+
+struct _available_dictionaries_data {
+       EEditorSpellChecker *checker;
+       GList *dicts;
+};
+
+struct _enchant_dict_description_data {
+       const gchar *language_tag;
+       gchar *dict_name;
+};
+
+static GHashTable *iso_639_table = NULL;
+static GHashTable *iso_3166_table = NULL;
+
+#ifdef HAVE_ISO_CODES
+
+#define ISOCODESLOCALEDIR ISO_CODES_PREFIX "/share/locale"
+
+#ifdef G_OS_WIN32
+#ifdef DATADIR
+#undef DATADIR
+#endif
+#include <shlobj.h>
+static HMODULE hmodule;
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+         DWORD fdwReason,
+         LPVOID lpvReserved);
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+         DWORD fdwReason,
+         LPVOID lpvReserved)
+{
+       switch (fdwReason)
+    {
+    case DLL_PROCESS_ATTACH:
+               hmodule = hinstDLL;
+               break;
+    }
+
+       return TRUE;
+}
+
+static gchar *
+_get_iso_codes_prefix (void)
+{
+       static gchar retval[1000];
+       static gint beenhere = 0;
+       gchar *temp_dir = 0;
+
+       if (beenhere)
+               return retval;
+
+       if (!(temp_dir = g_win32_get_package_installation_directory_of_module ((gpointer) hmodule))) {
+               strcpy (retval, ISO_CODES_PREFIX);
+               return retval;
+       }
+
+       strcpy (retval, temp_dir);
+       g_free (temp_dir);
+       beenhere = 1;
+       return retval;
+}
+
+static gchar *
+_get_isocodeslocaledir (void)
+{
+       static gchar retval[1000];
+       static gint beenhere = 0;
+
+       if (beenhere)
+               return retval;
+
+       strcpy (retval, _get_iso_codes_prefix ());
+       strcat (retval, "\\share\\locale" );
+       beenhere = 1;
+       return retval;
+}
+
+#undef ISO_CODES_PREFIX
+#define ISO_CODES_PREFIX _get_iso_codes_prefix ()
+
+#undef ISOCODESLOCALEDIR
+#define ISOCODESLOCALEDIR _get_isocodeslocaledir ()
+
+#endif
+
+static void
+iso_639_start_element (GMarkupParseContext *context,
+                       const gchar *element_name,
+                       const gchar **attribute_names,
+                       const gchar **attribute_values,
+                       gpointer data,
+                       GError **error)
+{
+       GHashTable *hash_table = data;
+       const gchar *iso_639_1_code = NULL;
+       const gchar *iso_639_2_code = NULL;
+       const gchar *name = NULL;
+       const gchar *code = NULL;
+       gint ii;
+
+       if (g_strcmp0 (element_name, "iso_639_entry") != 0)
+               return;
+
+       for (ii = 0; attribute_names[ii] != NULL; ii++) {
+               if (strcmp (attribute_names[ii], "name") == 0)
+                       name = attribute_values[ii];
+               else if (strcmp (attribute_names[ii], "iso_639_1_code") == 0)
+                       iso_639_1_code = attribute_values[ii];
+               else if (strcmp (attribute_names[ii], "iso_639_2T_code") == 0)
+                       iso_639_2_code = attribute_values[ii];
+       }
+
+       code = (iso_639_1_code != NULL) ? iso_639_1_code : iso_639_2_code;
+
+       if (code != NULL && *code != '\0' && name != NULL && *name != '\0')
+               g_hash_table_insert (
+                       hash_table, g_strdup (code),
+                       g_strdup (dgettext (ISO_639_DOMAIN, name)));
+}
+
+static void
+iso_3166_start_element (GMarkupParseContext *context,
+                        const gchar *element_name,
+                        const gchar **attribute_names,
+                        const gchar **attribute_values,
+                        gpointer data,
+                        GError **error)
+{
+       GHashTable *hash_table = data;
+       const gchar *name = NULL;
+       const gchar *code = NULL;
+       gint ii;
+
+       if (strcmp (element_name, "iso_3166_entry") != 0)
+               return;
+
+       for (ii = 0; attribute_names[ii] != NULL; ii++) {
+               if (strcmp (attribute_names[ii], "name") == 0)
+                       name = attribute_values[ii];
+               else if (strcmp (attribute_names[ii], "alpha_2_code") == 0)
+                       code = attribute_values[ii];
+       }
+
+       if (code != NULL && *code != '\0' && name != NULL && *name != '\0')
+               g_hash_table_insert (
+                       hash_table, g_ascii_strdown (code, -1),
+                       g_strdup (dgettext (ISO_3166_DOMAIN, name)));
+}
+
+static GMarkupParser iso_639_parser = {
+       iso_639_start_element,
+       NULL, NULL, NULL, NULL
+};
+
+static GMarkupParser iso_3166_parser = {
+       iso_3166_start_element,
+       NULL, NULL, NULL, NULL
+};
+
+static void
+iso_codes_parse (const GMarkupParser *parser,
+                 const gchar *basename,
+                 GHashTable *hash_table)
+{
+       GMappedFile *mapped_file;
+       gchar *filename;
+       GError *error = NULL;
+
+       filename = g_build_filename (
+               ISO_CODES_PREFIX, "share", "xml",
+               "iso-codes", basename, NULL);
+       mapped_file = g_mapped_file_new (filename, FALSE, &error);
+       g_free (filename);
+
+       if (mapped_file != NULL) {
+               GMarkupParseContext *context;
+               const gchar *contents;
+               gsize length;
+
+               context = g_markup_parse_context_new (
+                       parser, 0, hash_table, NULL);
+               contents = g_mapped_file_get_contents (mapped_file);
+               length = g_mapped_file_get_length (mapped_file);
+               g_markup_parse_context_parse (
+                       context, contents, length, &error);
+               g_markup_parse_context_free (context);
+#if GLIB_CHECK_VERSION(2,21,3)
+               g_mapped_file_unref (mapped_file);
+#else
+               g_mapped_file_free (mapped_file);
+#endif
+       }
+
+       if (error != NULL) {
+               g_warning ("%s: %s", basename, error->message);
+               g_error_free (error);
+       }
+}
+
+#endif /* HAVE_ISO_CODES */
+
+
+static void
+get_available_dictionaries (const char *language_tag,
+                           const char *provider_name,
+                           const char *provider_desc,
+                           const char *provider_file,
+                           gpointer user_data)
+{
+       struct _available_dictionaries_data *data = user_data;
+       EnchantDict *dict;
+
+       dict = enchant_broker_request_dict (
+                       data->checker->priv->broker, language_tag);
+       if (dict) {
+               data->dicts = g_list_append (data->dicts, dict);
+       }
+}
+
+static void
+describe_dictionary (const gchar *language_tag,
+                    const gchar *provider_name,
+                    const gchar *provider_desc,
+                    const gchar *provider_file,
+                    gpointer user_data)
+{
+       struct _enchant_dict_description_data *data = user_data;
+       const gchar *iso_639_name;
+       const gchar *iso_3166_name;
+       gchar *language_name;
+       gchar *lowercase;
+       gchar **tokens;
+
+       /* Split language code into lowercase tokens. */
+       lowercase = g_ascii_strdown (language_tag, -1);
+       tokens = g_strsplit (lowercase, "_", -1);
+       g_free (lowercase);
+
+       g_return_if_fail (tokens != NULL);
+
+       iso_639_name = g_hash_table_lookup (iso_639_table, tokens[0]);
+
+       if (iso_639_name == NULL) {
+               language_name = g_strdup_printf (
+               /* Translators: %s is the language ISO code. */
+                       C_("language", "Unknown (%s)"), language_tag);
+               goto exit;
+       }
+
+       if (g_strv_length (tokens) < 2) {
+               language_name = g_strdup (iso_639_name);
+               goto exit;
+       }
+
+       iso_3166_name = g_hash_table_lookup (iso_3166_table, tokens[1]);
+
+       if (iso_3166_name != NULL)
+               language_name = g_strdup_printf (
+                /* Translators: The first %s is the language name, and the
+                * second is the country name. Example: "French (France)" */
+                       C_("language", "%s (%s)"), iso_639_name, iso_3166_name);
+       else
+               language_name = g_strdup_printf (
+                /* Translators: The first %s is the language name, and the
+                * second is the country name. Example: "French (France)" */
+                       C_("language", "%s (%s)"), iso_639_name, tokens[1]);
+
+exit:
+       g_strfreev (tokens);
+
+       data->language_tag = language_tag;
+       data->dict_name = language_name;
+}
+
+static void
+free_dictionary (gpointer data,
+                gpointer user_data)
+{
+       EEditorSpellChecker *checker = user_data;
+
+       enchant_broker_free_dict (checker->priv->broker, data);
+}
+
+static void
+update_spell_checking_languages (WebKitSpellChecker *ichecker,
+                                const char *languages)
+{
+       GList* spell_dictionaries = 0;
+       EEditorSpellChecker *checker = E_EDITOR_SPELL_CHECKER (ichecker);
+
+       if (languages) {
+               gchar **langs;
+               gint ii;
+
+               langs = g_strsplit (languages, ",", -1);
+               for (ii = 0; langs[ii]; ii++) {
+                       if (enchant_broker_dict_exists (
+                                       checker->priv->broker, langs[ii])) {
+
+                               EnchantDict* dict =
+                                       enchant_broker_request_dict (
+                                               checker->priv->broker, langs[ii]);
+                               spell_dictionaries = g_list_append (
+                                               spell_dictionaries, dict);
+                       }
+               }
+               g_strfreev (langs);
+       } else {
+               const char* language;
+
+               language = pango_language_to_string (gtk_get_default_language ());
+               if (enchant_broker_dict_exists (checker->priv->broker, language)) {
+                       EnchantDict* dict;
+
+                       dict = enchant_broker_request_dict (
+                               checker->priv->broker, language);
+                       spell_dictionaries =
+                               g_list_append (spell_dictionaries, dict);
+               } else {
+                       spell_dictionaries =
+                               e_editor_spell_checker_get_available_dicts (checker);
+               }
+       }
+
+       g_list_foreach (checker->priv->dicts, free_dictionary, checker);
+       g_list_free (checker->priv->dicts);
+       checker->priv->dicts = spell_dictionaries;
+}
+
+
+static void
+check_spelling_of_string (WebKitSpellChecker *webkit_checker,
+                         const gchar *word,
+                         gint *misspelling_location,
+                         gint *misspelling_length)
+{
+       EEditorSpellChecker *checker = E_EDITOR_SPELL_CHECKER (webkit_checker);
+       PangoLanguage *language;
+       PangoLogAttr *attrs;
+       GList *dicts;
+       gint length, ii;
+
+       dicts = checker->priv->dicts;
+       if (!dicts)
+               return;
+
+       length = g_utf8_strlen (word, -1);
+
+       language = pango_language_get_default ();
+       attrs = g_new (PangoLogAttr, length + 1);
+
+       pango_get_log_attrs (word, -1, -1, language, attrs, length + 1);
+
+       for (ii = 0; ii < length + 1; ii++) {
+               /* We go through each character until we find an is_word_start,
+                * then we get into an inner loop to find the is_word_end
+                * corresponding */
+               if (attrs[ii].is_word_start) {
+                       int start = ii;
+                       int end = ii;
+                       int word_length;
+                       gchar *cstart;
+                       gint bytes;
+                       gchar *new_word;
+                       GList *iter;
+
+                       while (attrs[end].is_word_end < 1) {
+                               end++;
+                       }
+
+                       word_length = end - start;
+                       /* Set the iterator to be at the current word end, so we don't
+                        * check characters twice. */
+                       ii = end;
+
+                       cstart = g_utf8_offset_to_pointer (word, start);
+                       bytes = g_utf8_offset_to_pointer (word, end) - cstart;
+                       new_word = g_new0 (gchar, bytes + 1);
+
+                       g_utf8_strncpy (new_word, cstart, word_length);
+
+                       for (iter = dicts; iter; iter = iter->next) {
+                               EnchantDict* dict = iter->data;
+
+                               if (enchant_dict_check (dict, new_word, word_length)) {
+                                       if (misspelling_location)
+                                               *misspelling_location = start;
+                                       if (misspelling_length)
+                                               *misspelling_length = word_length;
+                               } else {
+                                       /* Stop checking, this word is ok in at
+                                        * least one dict. */
+                                       if (misspelling_location)
+                                               *misspelling_location = -1;
+                                       if (misspelling_length)
+                                               *misspelling_length = 0;
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+
+static gchar **
+get_guesses_for_word (WebKitSpellChecker *webkit_checker,
+                     const gchar *word,
+                     const gchar *context)
+{
+       EEditorSpellChecker *checker = E_EDITOR_SPELL_CHECKER (webkit_checker);
+       GList *dicts;
+       char** guesses = 0;
+
+       g_return_val_if_fail (E_IS_EDITOR_SPELL_CHECKER (checker), NULL);
+
+       for (dicts = checker->priv->dicts; dicts; dicts = dicts->next) {
+               EnchantDict *dict;
+               gchar **suggestions;
+               size_t suggestions_count;
+               size_t ii;
+
+               dict = dicts->data;
+               suggestions = enchant_dict_suggest (dict, word, -1, &suggestions_count);
+
+               if (suggestions_count > 0) {
+                       if (suggestions_count > 10) {
+                               suggestions_count = 10;
+                       }
+
+                       guesses = g_malloc0 ((suggestions_count + 1) * sizeof (char *));
+                       for (ii = 0; ii < suggestions_count && ii < 10; ii++) {
+                               guesses[ii] = g_strdup (suggestions[ii]);
+                       }
+
+                       guesses[ii] = 0;
+
+                       enchant_dict_free_suggestions (dict, suggestions);
+               }
+       }
+
+       return guesses;
+}
+
+static void
+ignore_word (WebKitSpellChecker *checker,
+            const gchar *word)
+{
+       EEditorSpellChecker *editor_spellchecker;
+
+       editor_spellchecker = E_EDITOR_SPELL_CHECKER (checker);
+       enchant_dict_add_to_session (
+               editor_spellchecker->priv->write_dictionary, word, -1);
+}
+
+static void
+learn_word (WebKitSpellChecker *checker,
+           const gchar *word)
+{
+       EEditorSpellChecker *editor_spellchecker;
+
+       editor_spellchecker = E_EDITOR_SPELL_CHECKER (checker);
+       enchant_dict_add_to_personal (
+               editor_spellchecker->priv->write_dictionary, word, -1);
+}
+
+static gchar *
+get_autocorrect_suggestions (WebKitSpellChecker *checker,
+                            const gchar *word)
+{
+       /* Not implemented, not needed */
+       return 0;
+}
+
+
+static void
+editor_spell_checker_finalize (GObject *object)
+{
+       EEditorSpellCheckerPrivate *priv = E_EDITOR_SPELL_CHECKER (object)->priv;
+
+       if (priv->broker) {
+               enchant_broker_free (priv->broker);
+               priv->broker = NULL;
+       }
+
+       if (priv->dicts) {
+               g_list_free (priv->dicts);
+               priv->dicts = NULL;
+       }
+
+       /* Chain up to parent implementation */
+       G_OBJECT_CLASS (e_editor_spell_checker_parent_class)->finalize (object);
+}
+
+static void
+e_editor_spell_checker_class_init (EEditorSpellCheckerClass *klass)
+{
+       GObjectClass *object_class;
+
+       e_editor_spell_checker_parent_class = g_type_class_peek_parent (klass);
+       g_type_class_add_private (klass, sizeof (EEditorSpellCheckerPrivate));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->finalize = editor_spell_checker_finalize;
+}
+
+static void
+e_editor_spell_checker_interface_init (WebKitSpellCheckerInterface *iface)
+{
+       iface->check_spelling_of_string = check_spelling_of_string;
+       iface->get_autocorrect_suggestions_for_misspelled_word = get_autocorrect_suggestions;
+       iface->get_guesses_for_word = get_guesses_for_word;
+       iface->update_spell_checking_languages = update_spell_checking_languages;
+       iface->ignore_word = ignore_word;
+       iface->learn_word = learn_word;
+}
+
+static void
+e_editor_spell_checker_init (EEditorSpellChecker *checker)
+{
+       checker->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+               checker, E_TYPE_EDITOR_SPELL_CHECKER, EEditorSpellCheckerPrivate);
+
+       checker->priv->broker = enchant_broker_init ();
+
+#if defined (ENABLE_NLS) && defined (HAVE_ISO_CODES)
+       bindtextdomain (ISO_639_DOMAIN, ISOCODESLOCALEDIR);
+       bind_textdomain_codeset (ISO_639_DOMAIN, "UTF-8");
+
+       bindtextdomain (ISO_3166_DOMAIN, ISOCODESLOCALEDIR);
+       bind_textdomain_codeset (ISO_3166_DOMAIN, "UTF-8");
+#endif
+
+       iso_639_table = g_hash_table_new_full (
+               g_str_hash, g_str_equal,
+               (GDestroyNotify) g_free,
+               (GDestroyNotify) g_free);
+
+       iso_3166_table = g_hash_table_new_full (
+               g_str_hash, g_str_equal,
+               (GDestroyNotify) g_free,
+               (GDestroyNotify) g_free);
+
+#ifdef HAVE_ISO_CODES
+       iso_codes_parse (&iso_639_parser, "iso_639.xml", iso_639_table);
+       iso_codes_parse (&iso_3166_parser, "iso_3166.xml", iso_3166_table);
+#endif
+
+}
+
+void
+e_editor_spell_checker_set_dictionaries (EEditorSpellChecker *checker,
+                                        GList *dictionaries)
+{
+       g_return_if_fail (E_IS_EDITOR_SPELL_CHECKER (checker));
+
+       g_list_foreach (checker->priv->dicts, free_dictionary, checker);
+       g_list_free (checker->priv->dicts);
+
+       checker->priv->dicts = g_list_copy (dictionaries);
+}
+
+EnchantDict *
+e_editor_spell_checker_lookup_dict (EEditorSpellChecker *checker,
+                                   const gchar *language_code)
+{
+       g_return_val_if_fail (E_IS_EDITOR_SPELL_CHECKER (checker), NULL);
+       g_return_val_if_fail (language_code != NULL, NULL);
+
+       if (!enchant_broker_dict_exists (checker->priv->broker, language_code)) {
+               return NULL;
+       }
+
+       return enchant_broker_request_dict (checker->priv->broker, language_code);
+}
+
+void
+e_editor_spell_checker_free_dict (EEditorSpellChecker *checker,
+                                 EnchantDict *dict)
+{
+       g_return_if_fail (E_IS_EDITOR_SPELL_CHECKER (checker));
+       g_return_if_fail (dict != NULL);
+
+       enchant_broker_free_dict (checker->priv->broker, dict);
+}
+
+
+gint
+e_editor_spell_checker_dict_compare (const EnchantDict *dict_a,
+                                    const EnchantDict *dict_b)
+{
+       const gchar *dict_a_name, *dict_b_name;
+       gchar *dict_a_ckey, *dict_b_ckey;
+       gint result;
+
+       dict_a_name = e_editor_spell_checker_get_dict_name (dict_a);
+       dict_b_name = e_editor_spell_checker_get_dict_name (dict_b);
+
+       dict_a_ckey = g_utf8_collate_key (dict_a_name, -1);
+       dict_b_ckey = g_utf8_collate_key (dict_b_name, -1);
+
+       result = g_strcmp0 (dict_a_ckey, dict_b_ckey);
+
+       g_free (dict_a_ckey);
+       g_free (dict_b_ckey);
+
+       return result;
+}
+
+GList *
+e_editor_spell_checker_get_available_dicts (EEditorSpellChecker *checker)
+{
+       struct _available_dictionaries_data data;
+
+       g_return_val_if_fail (E_IS_EDITOR_SPELL_CHECKER (checker), NULL);
+
+       data.checker = checker;
+       data.dicts = NULL;
+
+       enchant_broker_list_dicts (
+               checker->priv->broker, get_available_dictionaries, &data);
+
+       return data.dicts;
+}
+
+const gchar *
+e_editor_spell_checker_get_dict_name (const EnchantDict *dictionary)
+{
+       struct _enchant_dict_description_data data;
+
+       enchant_dict_describe (
+               (EnchantDict *) dictionary, describe_dictionary, &data);
+
+       return data.dict_name;
+}
+
+const gchar *
+e_editor_spell_checker_get_dict_code (const EnchantDict *dictionary)
+{
+       struct _enchant_dict_description_data data;
+
+       enchant_dict_describe (
+               (EnchantDict *) dictionary, describe_dictionary, &data);
+
+       return data.language_tag;
+}
diff --git a/e-util/e-editor-spell-checker.h b/e-util/e-editor-spell-checker.h
new file mode 100644
index 0000000..16bb54e
--- /dev/null
+++ b/e-util/e-editor-spell-checker.h
@@ -0,0 +1,85 @@
+/*
+ * e-editor-spell-checker.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EDITOR_SPELL_CHECKER_H
+#define E_EDITOR_SPELL_CHECKER_H
+
+#include <glib-object.h>
+#include <enchant/enchant.h>
+
+#define E_TYPE_EDITOR_SPELL_CHECKER \
+       (e_editor_spell_checker_get_type ())
+#define E_EDITOR_SPELL_CHECKER(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_EDITOR_SPELL_CHECKER, EEditorSpellChecker))
+#define E_EDITOR_SPELL_CHECKER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_EDITOR_SPELL_CHECKER, EEditorSpellCheckerClass))
+#define E_IS_EDITOR_SPELL_CHECKER(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_EDITOR_SPELL_CHECKER))
+#define E_IS_EDITOR_SPELL_CHECKER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_EDITOR_SPELL_CHECKER))
+#define E_EDITOR_SPELL_CHECKER_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_EDITOR_SPELL_CHECKER, EEditorSpellCheckerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EEditorSpellChecker EEditorSpellChecker;
+typedef struct _EEditorSpellCheckerClass EEditorSpellCheckerClass;
+typedef struct _EEditorSpellCheckerPrivate EEditorSpellCheckerPrivate;
+
+struct _EEditorSpellChecker {
+       GObject parent;
+
+       EEditorSpellCheckerPrivate *priv;
+};
+
+struct _EEditorSpellCheckerClass {
+       GObjectClass parent_class;
+};
+
+GType          e_editor_spell_checker_get_type         (void);
+
+
+void           e_editor_spell_checker_set_dictionaries (EEditorSpellChecker *checker,
+                                                        GList *dictionaries);
+
+EnchantDict *  e_editor_spell_checker_lookup_dict      (EEditorSpellChecker *checker,
+                                                        const gchar *language_code);
+void           e_editor_spell_checker_free_dict        (EEditorSpellChecker *checker,
+                                                        EnchantDict *dict);
+
+gint           e_editor_spell_checker_dict_compare     (const EnchantDict *dict_a,
+                                                        const EnchantDict *dict_b);
+
+GList *                e_editor_spell_checker_get_available_dicts
+                                                       (EEditorSpellChecker *checker);
+
+const gchar *  e_editor_spell_checker_get_dict_name    (const EnchantDict *dictionary);
+const gchar *  e_editor_spell_checker_get_dict_code    (const EnchantDict *dictionary);
+
+G_END_DECLS
+
+#endif /* E_EDITOR_SPELL_CHECKER_H */
diff --git a/e-util/e-editor-widget.c b/e-util/e-editor-widget.c
index 313f56a..605e5bc 100644
--- a/e-util/e-editor-widget.c
+++ b/e-util/e-editor-widget.c
@@ -23,6 +23,7 @@
 #include "e-editor-widget.h"
 #include "e-editor.h"
 #include "e-emoticon-chooser.h"
+#include "e-editor-spell-checker.h"
 
 #include <e-util/e-util.h>
 #include <e-util/e-marshal.h>
@@ -642,6 +643,7 @@ e_editor_widget_init (EEditorWidget *editor)
        WebKitDOMDocument *document;
        GSettings *g_settings;
        GSettingsSchema *settings_schema;
+       EEditorSpellChecker *checker;
 
        editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (
                editor, E_TYPE_EDITOR_WIDGET, EEditorWidgetPrivate);
@@ -656,10 +658,15 @@ e_editor_widget_init (EEditorWidget *editor)
                "enable-file-access-from-file-uris", TRUE,
                "enable-plugins", FALSE,
                "enable-scripts", FALSE,
+               "enable-spell-checking", TRUE,
                NULL);
 
        webkit_web_view_set_settings (WEBKIT_WEB_VIEW (editor), settings);
 
+       /* Override the spell-checker, use our own */
+       checker = g_object_new (E_TYPE_EDITOR_SPELL_CHECKER, NULL);
+       webkit_set_text_checker (G_OBJECT (checker));
+
        /* Don't use CSS when possible to preserve compatibility with older
         * versions of Evolution or other MUAs */
        document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (editor));
diff --git a/e-util/e-editor.c b/e-util/e-editor.c
index 246512f..a9377e9 100644
--- a/e-util/e-editor.c
+++ b/e-util/e-editor.c
@@ -22,8 +22,11 @@
 #include "e-editor.h"
 #include "e-editor-private.h"
 #include "e-editor-utils.h"
+#include "e-editor-selection.h"
+#include "e-editor-spell-checker.h"
 
 #include <glib/gi18n-lib.h>
+#include <enchant/enchant.h>
 
 G_DEFINE_TYPE (
        EEditor,
@@ -37,25 +40,260 @@ enum {
 
 enum {
        UPDATE_ACTIONS,
+       SPELL_LANGUAGES_CHANGED,
        LAST_SIGNAL
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
+
+/* This controls how spelling suggestions are divided between the primary
+ * context menu and a secondary menu.  The idea is to prevent the primary
+ * menu from growing too long.
+ *
+ * The constants below are used as follows:
+ *
+ * if TOTAL_SUGGESTIONS <= MAX_LEVEL1_SUGGETIONS:
+ *     LEVEL1_SUGGESTIONS = TOTAL_SUGGESTIONS
+ * elif TOTAL_SUGGESTIONS - MAX_LEVEL1_SUGGESTIONS < MIN_LEVEL2_SUGGESTIONS:
+ *     LEVEL1_SUGGESTIONS = TOTAL_SUGGESTIONS
+ * else
+ *     LEVEL1_SUGGESTIONS = MAX_LEVEL1_SUGGETIONS
+ *
+ * LEVEL2_SUGGESTIONS = TOTAL_SUGGESTIONS - LEVEL1_SUGGESTIONS
+ *
+ * Note that MAX_LEVEL1_SUGGETIONS is not a hard maximum.
+ */
+#define MAX_LEVEL1_SUGGESTIONS 4
+#define MIN_LEVEL2_SUGGESTIONS 3
+
+/* Action callback for context menu spelling suggestions.
+ * XXX This should really be in e-editor-actions.c */
+static void
+action_context_spell_suggest_cb (GtkAction *action,
+                                 EEditor *editor)
+{
+       EEditorWidget *widget;
+       EEditorSelection *selection;
+       const gchar *word;
+
+       word = g_object_get_data (G_OBJECT (action), "word");
+       g_return_if_fail (word != NULL);
+
+       widget = e_editor_get_editor_widget (editor);
+       selection = e_editor_widget_get_selection (widget);
+
+       e_editor_selection_replace_caret_word (selection, word);
+}
+
+static void
+editor_inline_spelling_suggestions (EEditor *editor,
+                                    EnchantDict *dictionary)
+{
+       EEditorWidget *widget;
+       EEditorSelection *selection;
+       WebKitSpellChecker *checker;
+       GtkActionGroup *action_group;
+       GtkUIManager *manager;
+       gchar **suggestions;
+       const gchar *path;
+       gchar *word;
+       guint count = 0;
+       guint length;
+       guint merge_id;
+       guint threshold;
+       gint ii;
+
+       widget = e_editor_get_editor_widget (editor);
+       selection = e_editor_widget_get_selection (widget);
+       checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+
+       word = e_editor_selection_get_caret_word (selection);
+       if (!word || !*word) {
+               return;
+       }
+
+       suggestions = webkit_spell_checker_get_guesses_for_word (checker, word, NULL);
+
+       path = "/context-menu/context-spell-suggest/";
+       manager = e_editor_get_ui_manager (editor);
+       action_group = editor->priv->suggestion_actions;
+       merge_id = editor->priv->spell_suggestions_merge_id;
+
+       length = 0;
+       while (suggestions && suggestions[length]) {
+               length++;
+       }
+
+       /* Calculate how many suggestions to put directly in the
+        * context menu.  The rest will go in a secondary menu. */
+       if (length <= MAX_LEVEL1_SUGGESTIONS) {
+               threshold = length;
+       } else if (length - MAX_LEVEL1_SUGGESTIONS < MIN_LEVEL2_SUGGESTIONS) {
+               threshold = length;
+       } else {
+               threshold = MAX_LEVEL1_SUGGESTIONS;
+       }
+
+       ii = 0;
+       for (ii = 0; suggestions && suggestions[ii]; ii++) {
+               gchar *suggestion = suggestions[ii];
+               gchar *action_name;
+               gchar *action_label;
+               GtkAction *action;
+               GtkWidget *child;
+               GSList *proxies;
+
+               /* Once we reach the threshold, put all subsequent
+                * spelling suggestions in a secondary menu. */
+               if (count == threshold)
+                       path = "/context-menu/context-more-suggestions-menu/";
+
+               /* Action name just needs to be unique. */
+               action_name = g_strdup_printf ("suggest-%d", count++);
+
+               action_label = g_markup_printf_escaped (
+                       "<b>%s</b>", suggestion);
+
+               action = gtk_action_new (
+                       action_name, action_label, NULL, NULL);
+
+               g_object_set_data_full (
+                       G_OBJECT (action), "word",
+                       g_strdup (suggestion), g_free);
+
+               g_signal_connect (
+                       action, "activate", G_CALLBACK (
+                       action_context_spell_suggest_cb), editor);
+
+               gtk_action_group_add_action (action_group, action);
+
+               gtk_ui_manager_add_ui (
+                       manager, merge_id, path,
+                       action_name, action_name,
+                       GTK_UI_MANAGER_AUTO, FALSE);
+
+               /* XXX GtkAction offers no support for Pango markup,
+                *     so we have to manually set "use-markup" on the
+                *     child of the proxy widget. */
+               gtk_ui_manager_ensure_update (manager);
+               proxies = gtk_action_get_proxies (action);
+               child = gtk_bin_get_child (proxies->data);
+               g_object_set (child, "use-markup", TRUE, NULL);
+
+               g_free (action_name);
+               g_free (action_label);
+       }
+
+       g_free (word);
+       g_strfreev (suggestions);
+}
+
+/* Helper for editor_update_actions() */
+static void
+editor_spell_checkers_foreach (EnchantDict *dictionary,
+                               EEditor *editor)
+{
+       EEditorSpellChecker *checker;
+       EEditorWidget *widget;
+       EEditorSelection *selection;
+       const gchar *language_code;
+       GtkActionGroup *action_group;
+       GtkUIManager *manager;
+       gchar **suggestions;
+       gchar *path;
+       gchar *word;
+       gint ii = 0;
+       guint merge_id;
+
+       language_code = e_editor_spell_checker_get_dict_code (dictionary);
+
+       widget = e_editor_get_editor_widget (editor);
+       selection = e_editor_widget_get_selection (widget);
+       checker = E_EDITOR_SPELL_CHECKER (webkit_get_text_checker ());
+       word = e_editor_selection_get_caret_word (selection);
+       if (!word || !*word) {
+               return;
+       }
+
+       suggestions = webkit_spell_checker_get_guesses_for_word (
+                       WEBKIT_SPELL_CHECKER (checker), word, NULL);
+
+       manager = e_editor_get_ui_manager (editor);
+       action_group = editor->priv->suggestion_actions;
+       merge_id = editor->priv->spell_suggestions_merge_id;
+
+       path = g_strdup_printf (
+               "/context-menu/context-spell-suggest/"
+               "context-spell-suggest-%s-menu", language_code);
+
+       for (ii = 0; suggestions && suggestions[ii]; ii++) {
+               gchar *suggestion = suggestions[ii];
+               gchar *action_name;
+               gchar *action_label;
+               GtkAction *action;
+               GtkWidget *child;
+               GSList *proxies;
+
+               /* Action name just needs to be unique. */
+               action_name = g_strdup_printf (
+                       "suggest-%s-%d", language_code, ii);
+
+               action_label = g_markup_printf_escaped (
+                       "<b>%s</b>", suggestion);
+
+               action = gtk_action_new (
+                       action_name, action_label, NULL, NULL);
+
+               g_object_set_data_full (
+                       G_OBJECT (action), "word",
+                       g_strdup (suggestion), g_free);
+
+               g_signal_connect (
+                       action, "activate", G_CALLBACK (
+                       action_context_spell_suggest_cb), editor);
+
+               gtk_action_group_add_action (action_group, action);
+
+               gtk_ui_manager_add_ui (
+                       manager, merge_id, path,
+                       action_name, action_name,
+                       GTK_UI_MANAGER_AUTO, FALSE);
+
+               /* XXX GtkAction offers no supports for Pango markup,
+                *     so we have to manually set "use-markup" on the
+                *     child of the proxy widget. */
+               gtk_ui_manager_ensure_update (manager);
+               proxies = gtk_action_get_proxies (action);
+               child = gtk_bin_get_child (proxies->data);
+               g_object_set (child, "use-markup", TRUE, NULL);
+
+               g_free (action_name);
+               g_free (action_label);
+       }
+
+       g_free (path);
+       g_free (word);
+       g_strfreev (suggestions);
+}
+
 static void
 editor_update_actions (EEditor *editor,
                       GdkEventButton *event)
 {
        WebKitWebView *webview;
+       WebKitSpellChecker *checker;
        WebKitHitTestResult *hit_test;
        WebKitHitTestResultContext context;
        WebKitDOMNode *node;
        EEditorWidget *widget;
+       EEditorSelection *selection;
        GtkUIManager *manager;
        GtkActionGroup *action_group;
        GList *list;
        gboolean visible;
        guint merge_id;
+       gint loc, len;
 
        widget = e_editor_get_editor_widget (editor);
        webview = WEBKIT_WEB_VIEW (widget);
@@ -126,18 +364,16 @@ editor_update_actions (EEditor *editor,
 
        /********************** Spell Check Suggestions **********************/
 
-       /* FIXME WEBKIT No spellcheching for now
-       object = html->engine->cursor->object;
        action_group = editor->priv->suggestion_actions;
 
-       // Remove the old content from the context menu.
+       /* Remove the old content from the context menu. */
        merge_id = editor->priv->spell_suggestions_merge_id;
        if (merge_id > 0) {
                gtk_ui_manager_remove_ui (manager, merge_id);
                editor->priv->spell_suggestions_merge_id = 0;
        }
 
-       // Clear the action group for spelling suggestions.
+       /* Clear the action group for spelling suggestions. */
        list = gtk_action_group_list_actions (action_group);
        while (list != NULL) {
                GtkAction *action = list->data;
@@ -146,31 +382,88 @@ editor_update_actions (EEditor *editor,
                list = g_list_delete_link (list, list);
        }
 
-       // Decide if we should show spell checking items.
-       visible =
-               !html_engine_is_selection_active (html->engine) &&
-               object != NULL && html_object_is_text (object) &&
-               !html_engine_spell_word_is_valid (html->engine);
+       /* Decide if we should show spell checking items. */
+       checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+       selection = e_editor_widget_get_selection (widget);
+       visible = FALSE;
+       if ((g_list_length (editor->priv->active_dictionaries) > 0) &&
+           e_editor_selection_has_text (selection)) {
+               gchar *word = e_editor_selection_get_caret_word (selection);
+               if (word && *word) {
+                       webkit_spell_checker_check_spelling_of_string (
+                               checker, word, &loc, &len);
+                       visible = (loc > -1);
+               } else {
+                       visible = FALSE;
+               }
+               g_free (word);
+       }
+
        action_group = editor->priv->spell_check_actions;
        gtk_action_group_set_visible (action_group, visible);
 
-       // Exit early if spell checking items are invisible.
-       if (!visible)
+       /* Exit early if spell checking items are invisible. */
+       if (!visible) {
                return;
+       }
 
-       list = editor->priv->active_spell_checkers;
+       list = editor->priv->active_dictionaries;
        merge_id = gtk_ui_manager_new_merge_id (manager);
        editor->priv->spell_suggestions_merge_id = merge_id;
 
-       // Handle a single active language as a special case.
+       /* Handle a single active language as a special case. */
        if (g_list_length (list) == 1) {
                editor_inline_spelling_suggestions (editor, list->data);
                return;
        }
 
-       // Add actions and context menu content for active languages
+       /* Add actions and context menu content for active languages */
        g_list_foreach (list, (GFunc) editor_spell_checkers_foreach, editor);
-       */
+}
+
+static void
+editor_spell_languages_changed (EEditor *editor,
+                               GList *dictionaries)
+{
+       WebKitSpellChecker *checker;
+       WebKitWebSettings *settings;
+       GString *languages;
+       const GList *iter;
+
+       languages = g_string_new ("");
+
+       /* Join the languages codes to comma-separated list */
+       for (iter = dictionaries; iter; iter = iter->next) {
+               EnchantDict *dictionary = iter->data;
+
+               if (iter != dictionaries) {
+                       g_string_append (languages, ",");
+               }
+
+               g_string_append (
+                       languages,
+                       e_editor_spell_checker_get_dict_code (dictionary));
+       }
+
+       /* Set the languages for spell-checker to use for suggestions etc. */
+       checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker());
+       webkit_spell_checker_update_spell_checking_languages (checker, languages->str);
+
+       /* Set the languages for webview to highlight misspelled words */
+       settings = webkit_web_view_get_settings (
+                       WEBKIT_WEB_VIEW (editor->priv->editor_widget));
+       g_object_set (
+               G_OBJECT (settings),
+               "spell-checking-languages", languages->str,
+               NULL);
+
+       if (editor->priv->spell_check_dialog != NULL) {
+               e_editor_spell_check_dialog_set_dictionaries (
+                       E_EDITOR_SPELL_CHECK_DIALOG (editor->priv->spell_check_dialog),
+                       dictionaries);
+       }
+
+       g_string_free (languages, TRUE);
 }
 
 static gboolean
@@ -261,6 +554,7 @@ editor_constructed (GObject *object)
 {
        EEditor *editor = E_EDITOR (object);
        EEditorPrivate *priv = editor->priv;
+       GtkIMMulticontext *im_context;
 
        GtkWidget *widget;
        GtkToolbar *toolbar;
@@ -360,69 +654,15 @@ editor_constructed (GObject *object)
        priv->size_combo_box = g_object_ref (widget);
        gtk_widget_show_all (GTK_WIDGET (tool_item));
 
-       /* Initialize painters (requires "edit_area"). */
-
-       /* FIXME WEBKIT
-       html = e_editor_widget_get_html (E_EDITOR_WIDGET (editor));
-       gtk_widget_ensure_style (GTK_WIDGET (html));
-       priv->html_painter = g_object_ref (html->engine->painter);
-       priv->plain_painter = html_plain_painter_new (priv->edit_area, TRUE);
-       */
-
        /* Add input methods to the context menu. */
-
-       /* FIXME WEBKIT
        widget = e_editor_get_managed_widget (
                editor, "/context-menu/context-input-methods-menu");
        widget = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+       g_object_get (
+               G_OBJECT (priv->editor_widget), "im-context", &im_context, NULL);
        gtk_im_multicontext_append_menuitems (
-               GTK_IM_MULTICONTEXT (html->priv->im_context),
+               GTK_IM_MULTICONTEXT (im_context),
                GTK_MENU_SHELL (widget));
-       */
-
-       /* Configure color stuff. */
-
-       /* FIXME WEBKIT
-       priv->palette = gtkhtml_color_palette_new ();
-       priv->text_color = gtkhtml_color_state_new ();
-
-       gtkhtml_color_state_set_default_label (
-               priv->text_color, _("Automatic"));
-       gtkhtml_color_state_set_palette (
-               priv->text_color, priv->palette);
-       */
-
-       /* Text color widgets share state. */
-
-       /* FIXME WEBKIT
-       widget = priv->color_combo_box;
-       gtkhtml_color_combo_set_state (
-               GTKHTML_COLOR_COMBO (widget), priv->text_color);
-
-       widget = WIDGET (TEXT_PROPERTIES_COLOR_COMBO);
-       gtkhtml_color_combo_set_state (
-               GTKHTML_COLOR_COMBO (widget), priv->text_color);
-       */
-
-       /* These color widgets share a custom color palette. */
-
-       /* FIXME WEBKIT
-       widget = WIDGET (CELL_PROPERTIES_COLOR_COMBO);
-       gtkhtml_color_combo_set_palette (
-               GTKHTML_COLOR_COMBO (widget), priv->palette);
-
-       widget = WIDGET (PAGE_PROPERTIES_BACKGROUND_COLOR_COMBO);
-       gtkhtml_color_combo_set_palette (
-               GTKHTML_COLOR_COMBO (widget), priv->palette);
-
-       widget = WIDGET (PAGE_PROPERTIES_LINK_COLOR_COMBO);
-       gtkhtml_color_combo_set_palette (
-               GTKHTML_COLOR_COMBO (widget), priv->palette);
-
-       widget = WIDGET (TABLE_PROPERTIES_COLOR_COMBO);
-       gtkhtml_color_combo_set_palette (
-               GTKHTML_COLOR_COMBO (widget), priv->palette);
-               */
 }
 
 static void
@@ -431,16 +671,6 @@ editor_dispose (GObject *object)
        EEditor *editor = E_EDITOR (object);
        EEditorPrivate *priv = editor->priv;
 
-       /* Disconnect signal handlers from the color
-        * state object since it may live on. */
-       /* FIXME WEBKIT
-       if (priv->text_color != NULL) {
-               g_signal_handlers_disconnect_matched (
-                       priv->text_color, G_SIGNAL_MATCH_DATA,
-                       0, 0, NULL, NULL, editor);
-       }
-       */
-
        g_clear_object (&priv->manager);
        g_clear_object (&priv->manager);
        g_clear_object (&priv->core_actions);
@@ -452,15 +682,8 @@ editor_dispose (GObject *object)
        g_clear_object (&priv->suggestion_actions);
        g_clear_object (&priv->builder);
 
-       /* FIXME WEBKIT
-       g_hash_table_remove_all (priv->available_spell_checkers);
-
-       g_list_foreach (
-               priv->active_spell_checkers,
-               (GFunc) g_object_unref, NULL);
-       g_list_free (priv->active_spell_checkers);
-       priv->active_spell_checkers = NULL;
-       */
+       g_list_free (priv->active_dictionaries);
+       priv->active_dictionaries = NULL;
 
        g_clear_object (&priv->main_menu);
        g_clear_object (&priv->main_toolbar);
@@ -473,11 +696,6 @@ editor_dispose (GObject *object)
        g_clear_object (&priv->size_combo_box);
        g_clear_object (&priv->style_combo_box);
        g_clear_object (&priv->scrolled_window);
-
-       /* FIXME WEBKIT
-       DISPOSE (priv->palette);
-       DISPOSE (priv->text_color);
-       */
 }
 
 static void
@@ -494,6 +712,7 @@ e_editor_class_init (EEditorClass *klass)
        object_class->dispose = editor_dispose;
 
        klass->update_actions = editor_update_actions;
+       klass->spell_languages_changed = editor_spell_languages_changed;
 
        g_object_class_install_property (
                object_class,
@@ -514,6 +733,16 @@ e_editor_class_init (EEditorClass *klass)
                g_cclosure_marshal_VOID__BOXED,
                G_TYPE_NONE, 1,
                GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+       signals[SPELL_LANGUAGES_CHANGED] = g_signal_new (
+               "spell-languages-changed",
+               G_OBJECT_CLASS_TYPE (klass),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EEditorClass, spell_languages_changed),
+               NULL, NULL,
+               g_cclosure_marshal_VOID__POINTER,
+               G_TYPE_NONE, 1,
+               G_TYPE_POINTER);
 }
 
 static void
@@ -751,3 +980,24 @@ e_editor_save (EEditor *editor,
 
        return TRUE;
 }
+
+void
+e_editor_emit_spell_languages_changed (EEditor *editor)
+{
+       GList *dictionaries, *iter;
+
+       g_return_if_fail (editor != NULL);
+
+       dictionaries = NULL;
+       for (iter = editor->priv->active_dictionaries; iter; iter = g_list_next (iter)) {
+               EnchantDict *dictionary = iter->data;
+
+               dictionaries = g_list_prepend (dictionaries, dictionary);
+       }
+
+       dictionaries = g_list_reverse (dictionaries);
+
+       g_signal_emit (editor, signals[SPELL_LANGUAGES_CHANGED], 0, dictionaries);
+
+       g_list_free (dictionaries);
+}
diff --git a/e-util/e-editor.h b/e-util/e-editor.h
index 1bb4a32..1dcfdd0 100644
--- a/e-util/e-editor.h
+++ b/e-util/e-editor.h
@@ -1,11 +1,4 @@
-<<<<<<< HEAD
-/*
- * e-editor.h
- *
- * Copyright (C) 2012 Dan Vrátil <dvratil redhat com>
-=======
 /* e-editor.h
->>>>>>> Initial import of GtkhtmlEditor class
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU Lesser General Public
@@ -30,10 +23,6 @@
 #define E_EDITOR_H
 
 #include <gtk/gtk.h>
-<<<<<<< HEAD
-#include <e-util/e-activity.h>
-=======
->>>>>>> Initial import of GtkhtmlEditor class
 #include <e-util/e-editor-widget.h>
 
 /* Standard GObject macros */
@@ -62,34 +51,20 @@ typedef struct _EEditorClass EEditorClass;
 typedef struct _EEditorPrivate EEditorPrivate;
 
 struct _EEditor {
-<<<<<<< HEAD
-       GtkGrid parent;
-=======
        GtkBox parent;
 
->>>>>>> Initial import of GtkhtmlEditor class
        EEditorPrivate *priv;
 };
 
 struct _EEditorClass {
-<<<<<<< HEAD
-       GtkGridClass parent_class;
-
-       void            (*update_actions)       (EEditor *editor,
-                                                GdkEventButton *event);
-       void            (*spell_languages_changed)
-                                               (EEditor *editor);
-};
-
-GType          e_editor_get_type               (void) G_GNUC_CONST;
-GtkWidget *    e_editor_new                    (void);
-gboolean       e_editor_is_busy                (EEditor *editor);
-EEditorWidget *        e_editor_get_editor_widget      (EEditor *editor);
-=======
        GtkBoxClass parent_class;
 
        void            (*update_actions)       (EEditor *editor,
                                                 GdkEventButton *event);
+
+       void            (*spell_languages_changed)
+                                               (EEditor *editor,
+                                                GList *dictionaries);
 };
 
 GType          e_editor_get_type               (void);
@@ -97,7 +72,6 @@ GtkWidget *   e_editor_new                    (void);
 
 EEditorWidget* e_editor_get_editor_widget      (EEditor *editor);
 
->>>>>>> Initial import of GtkhtmlEditor class
 GtkBuilder *   e_editor_get_builder            (EEditor *editor);
 GtkUIManager * e_editor_get_ui_manager         (EEditor *editor);
 GtkAction *    e_editor_get_action             (EEditor *editor,
@@ -106,23 +80,16 @@ GtkActionGroup *e_editor_get_action_group  (EEditor *editor,
                                                 const gchar *group_name);
 GtkWidget *    e_editor_get_widget             (EEditor *editor,
                                                 const gchar *widget_name);
-<<<<<<< HEAD
-GtkWidget *    e_editor_get_managed_widget     (EEditor *editor,
-=======
 GtkWidget *    e_editor_get_managed_widget
                                                (EEditor *editor,
->>>>>>> Initial import of GtkhtmlEditor class
                                                 const gchar *widget_path);
 const gchar *  e_editor_get_filename           (EEditor *editor);
 void           e_editor_set_filename           (EEditor *editor,
                                                 const gchar *filename);
-<<<<<<< HEAD
-EActivity *    e_editor_new_activity           (EEditor *editor);
-void           e_editor_pack_above             (EEditor *editor,
-                                                GtkWidget *child);
-=======
 
->>>>>>> Initial import of GtkhtmlEditor class
+void           e_editor_emit_spell_languages_changed
+                                               (EEditor *editor);
+
 
 /*****************************************************************************
  * High-Level Editing Interface
diff --git a/e-util/e-util.h b/e-util/e-util.h
index 4d6f080..2d77f85 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -102,6 +102,8 @@
 #include <e-util/e-editor-paragraph-dialog.h>
 #include <e-util/e-editor-replace-dialog.h>
 #include <e-util/e-editor-selection.h>
+#include <e-util/e-editor-spell-check-dialog.h>
+#include <e-util/e-editor-spell-checker.h>
 #include <e-util/e-editor-table-dialog.h>
 #include <e-util/e-editor-text-dialog.h>
 #include <e-util/e-editor-utils.h>
@@ -192,8 +194,6 @@
 #include <e-util/e-source-selector-dialog.h>
 #include <e-util/e-source-selector.h>
 #include <e-util/e-source-util.h>
-#include <e-util/e-spell-dialog.h>
-#include <e-util/e-spell-dictionary.h>
 #include <e-util/e-spell-entry.h>
 #include <e-util/e-spell-text-view.h>
 #include <e-util/e-stock-request.h>


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