[evolution/wip/webkit-composer: 62/262] Move spell-checking parts to e-util



commit dc212c7dd2d9bb9ee44f9cf36999d75ef8fa6a3e
Author: Dan Vrátil <dvratil redhat com>
Date:   Mon Aug 27 11:46:46 2012 +0200

    Move spell-checking parts to e-util

 e-util/Makefile.am                   |   10 +-
 e-util/e-editor-actions.c            |   61 ++--
 e-util/e-editor-spell-check-dialog.c |    9 +-
 e-util/e-editor-spell-checker.c      |  708 ----------------------------------
 e-util/e-editor-spell-checker.h      |   85 ----
 e-util/e-editor-widget.c             |   12 +-
 e-util/e-editor-widget.h             |    4 +
 e-util/e-editor.c                    |   45 +--
 e-util/e-spell-checker.c             |  521 +++++++++++++++++++++++++
 e-util/e-spell-checker.h             |   86 ++++
 e-util/e-spell-dictionary.c          |  651 +++++++++++++++++++++++++++++++
 e-util/e-spell-dictionary.h          |  104 +++++
 e-util/e-util.h                      |    1 -
 13 files changed, 1433 insertions(+), 864 deletions(-)
---
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 1facc83..2ce4f09 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -102,7 +102,7 @@ libevolution_util_la_CPPFLAGS = \
        $(GNOME_PLATFORM_CFLAGS) \
        $(GEO_CFLAGS) \
        $(GTKHTML_CFLAGS) \
-       $(GTKSPELL_CFLAGS) \
+       $(ENCHANT_CFLAGS) \
        $(NULL)
 
 evolution_util_include_HEADERS =  \
@@ -188,7 +188,6 @@ evolution_util_include_HEADERS =  \
        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 \
@@ -279,6 +278,8 @@ evolution_util_include_HEADERS =  \
        e-source-selector-dialog.h \
        e-source-selector.h \
        e-source-util.h \
+       e-spell-checker.h \
+       e-spell-dictionary.h \
        e-spell-entry.h \
        e-spell-text-view.h \
        e-stock-request.h \
@@ -460,7 +461,6 @@ libevolution_util_la_SOURCES = \
        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 \
@@ -551,6 +551,8 @@ libevolution_util_la_SOURCES = \
        e-source-selector-dialog.c \
        e-source-selector.c \
        e-source-util.c \
+       e-spell-checker.c \
+       e-spell-dictionary.c \
        e-spell-entry.c \
        e-spell-text-view.c \
        e-stock-request.c \
@@ -645,7 +647,7 @@ libevolution_util_la_LIBADD =  \
        $(GNOME_PLATFORM_LIBS) \
        $(GEO_LIBS) \
        $(GTKHTML_LIBS) \
-       $(GTKSPELL_LIBS) \
+       $(ENCHANT_LIBS) \
        $(INTLLIBS) \
        $(MATH_LIB) \
        $(NULL)
diff --git a/e-util/e-editor-actions.c b/e-util/e-editor-actions.c
index c8b239d..8121c59 100644
--- a/e-util/e-editor-actions.c
+++ b/e-util/e-editor-actions.c
@@ -22,15 +22,16 @@
 #include <gio/gio.h>
 #include <glib/gi18n-lib.h>
 #include <string.h>
+#include <enchant/enchant.h>
 
 #include "e-editor.h"
 #include "e-editor-private.h"
 #include "e-editor-actions.h"
-#include "e-editor-spell-checker.h"
 #include "e-editor-utils.h"
 #include "e-emoticon-action.h"
 #include "e-emoticon-chooser.h"
 #include "e-image-chooser-dialog.h"
+#include "e-spell-checker.h"
 
 static void
 insert_html_file_ready_cb (GFile *file,
@@ -367,16 +368,16 @@ static void
 action_context_spell_add_cb (GtkAction *action,
                              EEditor *editor)
 {
-       WebKitSpellChecker *spell_checker;
+       ESpellChecker *spell_checker;
        EEditorSelection *selection;
        gchar *word;
 
-       spell_checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+       spell_checker = e_editor_widget_get_spell_checker (editor->priv->editor_widget);
        selection = e_editor_widget_get_selection (editor->priv->editor_widget);
 
        word = e_editor_selection_get_caret_word (selection);
        if (word && *word) {
-               webkit_spell_checker_learn_word (spell_checker, word);
+               e_spell_checker_learn_word (spell_checker, word);
        }
 }
 
@@ -384,16 +385,16 @@ static void
 action_context_spell_ignore_cb (GtkAction *action,
                                 EEditor *editor)
 {
-       WebKitSpellChecker *spell_checker;
+       ESpellChecker *spell_checker;
        EEditorSelection *selection;
        gchar *word;
 
-       spell_checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+       spell_checker = e_editor_widget_get_spell_checker (editor->priv->editor_widget);
        selection = e_editor_widget_get_selection (editor->priv->editor_widget);
 
        word = e_editor_selection_get_caret_word (selection);
        if (word && *word) {
-               webkit_spell_checker_ignore_word (spell_checker, word);
+               e_spell_checker_ignore_word (spell_checker, word);
        }
 }
 
@@ -574,8 +575,8 @@ static void
 action_language_cb (GtkToggleAction *action,
                     EEditor *editor)
 {
-       EEditorSpellChecker *checker;
-       EnchantDict *dictionary;
+       ESpellChecker *checker;
+       ESpellDictionary *dictionary;
        const gchar *language_code;
        GtkAction *add_action;
        GList *list;
@@ -583,23 +584,23 @@ action_language_cb (GtkToggleAction *action,
        gchar *action_name;
        gboolean active;
 
-       checker = E_EDITOR_SPELL_CHECKER (webkit_get_text_checker ());
+       checker = e_editor_widget_get_spell_checker (editor->priv->editor_widget);
        active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
        language_code = gtk_action_get_name (GTK_ACTION (action));
-       dictionary = e_editor_spell_checker_lookup_dict (checker, language_code);
+       dictionary = e_spell_checker_lookup_dictionary (checker, language_code);
 
        /* Update the list of active dictionaries */
        list = editor->priv->active_dictionaries;
        if (active)
                list = g_list_insert_sorted (
                        list, (EnchantDict *) dictionary,
-                       (GCompareFunc) e_editor_spell_checker_dict_compare);
+                       (GCompareFunc) e_spell_dictionary_compare);
        else {
                GList *link;
 
                link = g_list_find (list, dictionary);
                g_return_if_fail (link != NULL);
-               e_editor_spell_checker_free_dict (checker, link->data);
+               g_object_unref (link->data);
                list = g_list_delete_link (list, link);
        }
        editor->priv->active_dictionaries = list;
@@ -619,7 +620,7 @@ action_language_cb (GtkToggleAction *action,
 
        e_editor_emit_spell_languages_changed (editor);
 
-       e_editor_spell_checker_free_dict (checker, dictionary);
+       g_object_unref (dictionary);
 }
 
 struct _ModeChanged {
@@ -1677,7 +1678,7 @@ static GtkActionEntry spell_context_entries[] = {
 static void
 editor_actions_setup_languages_menu (EEditor *editor)
 {
-       EEditorSpellChecker *checker;
+       ESpellChecker *checker;
        GtkUIManager *manager;
        GtkActionGroup *action_group;
        GList *available_dicts, *iter;
@@ -1685,17 +1686,17 @@ editor_actions_setup_languages_menu (EEditor *editor)
 
        manager = editor->priv->manager;
        action_group = editor->priv->language_actions;
-       checker = E_EDITOR_SPELL_CHECKER (webkit_get_text_checker ());
-       available_dicts = e_editor_spell_checker_get_available_dicts (checker);
+       checker = e_editor_widget_get_spell_checker (editor->priv->editor_widget);
+       available_dicts = e_spell_checker_list_available_dicts (checker);
        merge_id = gtk_ui_manager_new_merge_id (manager);
 
        for (iter = available_dicts; iter; iter = iter->next) {
-               EnchantDict *dictionary = iter->data;
+               ESpellDictionary *dictionary = iter->data;
                GtkToggleAction *action;
 
                action = gtk_toggle_action_new (
-                       e_editor_spell_checker_get_dict_code (dictionary),
-                       e_editor_spell_checker_get_dict_name (dictionary),
+                       e_spell_dictionary_get_code (dictionary),
+                       e_spell_dictionary_get_name (dictionary),
                        NULL, NULL);
 
                g_signal_connect (
@@ -1710,11 +1711,11 @@ editor_actions_setup_languages_menu (EEditor *editor)
                gtk_ui_manager_add_ui (
                        manager, merge_id,
                        "/main-menu/edit-menu/language-menu",
-                       e_editor_spell_checker_get_dict_code (dictionary),
-                       e_editor_spell_checker_get_dict_code (dictionary),
+                       e_spell_dictionary_get_code (dictionary),
+                       e_spell_dictionary_get_code (dictionary),
                        GTK_UI_MANAGER_AUTO, FALSE);
 
-               e_editor_spell_checker_free_dict (checker, dictionary);
+               g_object_unref (dictionary);
        }
 
        g_list_free (available_dicts);
@@ -1723,7 +1724,7 @@ editor_actions_setup_languages_menu (EEditor *editor)
 static void
 editor_actions_setup_spell_check_menu (EEditor *editor)
 {
-       EEditorSpellChecker *checker;
+       ESpellChecker *checker;
        GtkUIManager *manager;
        GtkActionGroup *action_group;
        GList *available_dicts, *iter;
@@ -1731,20 +1732,20 @@ editor_actions_setup_spell_check_menu (EEditor *editor)
 
        manager = editor->priv->manager;
        action_group = editor->priv->spell_check_actions;;
-       checker = E_EDITOR_SPELL_CHECKER (webkit_get_text_checker ());
-       available_dicts = e_editor_spell_checker_get_available_dicts (checker);
+       checker = e_editor_widget_get_spell_checker (editor->priv->editor_widget);
+       available_dicts = e_spell_checker_list_available_dicts (checker);
        merge_id = gtk_ui_manager_new_merge_id (manager);
 
        for (iter = available_dicts; iter; iter = iter->next) {
-               EnchantDict *dictionary = iter->data;
+               ESpellDictionary *dictionary = iter->data;
                GtkAction *action;
                const gchar *code;
                const gchar *name;
                gchar *action_label;
                gchar *action_name;
 
-               code = e_editor_spell_checker_get_dict_code (dictionary);
-               name = e_editor_spell_checker_get_dict_name (dictionary);
+               code = e_spell_dictionary_get_code (dictionary);
+               name = e_spell_dictionary_get_name (dictionary);
 
                /* Add a suggestion menu. */
                action_name = g_strdup_printf (
@@ -1790,7 +1791,7 @@ editor_actions_setup_spell_check_menu (EEditor *editor)
                        action_name, action_name,
                        GTK_UI_MANAGER_AUTO, FALSE);
 
-               e_editor_spell_checker_free_dict (checker, dictionary);
+               g_object_unref (dictionary);
                g_free (action_label);
                g_free (action_name);
        }
diff --git a/e-util/e-editor-spell-check-dialog.c b/e-util/e-editor-spell-check-dialog.c
index 0b2594a..cf1dc22 100644
--- a/e-util/e-editor-spell-check-dialog.c
+++ b/e-util/e-editor-spell-check-dialog.c
@@ -24,8 +24,9 @@
 #include <glib/gi18n-lib.h>
 #include <enchant/enchant.h>
 
-#include "e-editor-spell-checker.h"
 #include "e-editor-widget.h"
+#include "e-spell-checker.h"
+#include "e-spell-dictionary.h"
 
 G_DEFINE_TYPE (
        EEditorSpellCheckDialog,
@@ -636,20 +637,20 @@ e_editor_spell_check_dialog_set_dictionaries (EEditorSpellCheckDialog *dialog,
        /* Copy and sort the new list of spell checkers. */
        list = g_list_sort (
                g_list_copy (dictionaries),
-               (GCompareFunc) e_editor_spell_checker_dict_compare);
+               (GCompareFunc) e_spell_dictionary_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;
+               ESpellDictionary *dictionary = list->data;
                GtkTreeIter iter;
 
                gtk_list_store_append (store, &iter);
                gtk_list_store_set (
                        store, &iter,
-                       0, e_editor_spell_checker_get_dict_name (dictionary),
+                       0, e_spell_dictionary_get_name (dictionary),
                        1, dictionary, -1);
 
                list = g_list_next (list);
diff --git a/e-util/e-editor-widget.c b/e-util/e-editor-widget.c
index cc9acfd..23f8143 100644
--- a/e-util/e-editor-widget.c
+++ b/e-util/e-editor-widget.c
@@ -23,7 +23,6 @@
 #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>
@@ -825,7 +824,7 @@ e_editor_widget_init (EEditorWidget *editor)
        WebKitDOMDocument *document;
        GSettings *g_settings;
        GSettingsSchema *settings_schema;
-       EEditorSpellChecker *checker;
+       ESpellChecker *checker;
 
        editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (
                editor, E_TYPE_EDITOR_WIDGET, EEditorWidgetPrivate);
@@ -846,7 +845,7 @@ e_editor_widget_init (EEditorWidget *editor)
        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);
+       checker = g_object_new (E_TYPE_SPELL_CHECKER, NULL);
        webkit_set_text_checker (G_OBJECT (checker));
        g_object_unref (checker);
 
@@ -1083,6 +1082,13 @@ e_editor_widget_set_spell_languages (EEditorWidget *widget,
        g_object_notify (G_OBJECT (widget), "spell-languages");
 }
 
+ESpellChecker *
+e_editor_widget_get_spell_checker (EEditorWidget *widget)
+{
+       return E_SPELL_CHECKER (webkit_get_text_checker ());
+}
+
+
 gchar *
 e_editor_widget_get_text_html (EEditorWidget *widget)
 {
diff --git a/e-util/e-editor-widget.h b/e-util/e-editor-widget.h
index dcdf549..59a5b9e 100644
--- a/e-util/e-editor-widget.h
+++ b/e-util/e-editor-widget.h
@@ -26,6 +26,7 @@
 #include <webkit/webkit.h>
 
 #include <e-util/e-editor-selection.h>
+#include <e-util/e-spell-checker.h>
 
 /* Standard GObject macros */
 #define E_TYPE_EDITOR_WIDGET \
@@ -116,6 +117,9 @@ void                e_editor_widget_set_spell_languages
                                                (EEditorWidget *widget,
                                                 GList *spell_languages);
 
+ESpellChecker *        e_editor_widget_get_spell_checker
+                                               (EEditorWidget *widget);
+
 gchar *                e_editor_widget_get_text_html   (EEditorWidget *widget);
 gchar *                e_editor_widget_get_text_plain  (EEditorWidget *widget);
 void           e_editor_widget_set_text_html   (EEditorWidget *widget,
diff --git a/e-util/e-editor.c b/e-util/e-editor.c
index 92916ad..fcf6d8e 100644
--- a/e-util/e-editor.c
+++ b/e-util/e-editor.c
@@ -23,7 +23,6 @@
 #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>
@@ -191,33 +190,30 @@ editor_inline_spelling_suggestions (EEditor *editor,
 
 /* Helper for editor_update_actions() */
 static void
-editor_spell_checkers_foreach (EnchantDict *dictionary,
+editor_spell_checkers_foreach (ESpellDictionary *dictionary,
                                EEditor *editor)
 {
-       EEditorSpellChecker *checker;
        EEditorWidget *widget;
        EEditorSelection *selection;
        const gchar *language_code;
        GtkActionGroup *action_group;
        GtkUIManager *manager;
-       gchar **suggestions;
+       GList *suggestions, *iter;
        gchar *path;
        gchar *word;
        gint ii = 0;
        guint merge_id;
 
-       language_code = e_editor_spell_checker_get_dict_code (dictionary);
+       language_code = e_spell_dictionary_get_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);
+       suggestions = e_spell_dictionary_get_suggestions (dictionary, word, -1);
 
        manager = e_editor_get_ui_manager (editor);
        action_group = editor->priv->suggestion_actions;
@@ -227,8 +223,8 @@ editor_spell_checkers_foreach (EnchantDict *dictionary,
                "/context-menu/context-spell-suggest/"
                "context-spell-suggest-%s-menu", language_code);
 
-       for (ii = 0; suggestions && suggestions[ii]; ii++) {
-               gchar *suggestion = suggestions[ii];
+       for (iter = suggestions; iter; iter = iter->next) {
+               gchar *suggestion = iter->data;
                gchar *action_name;
                gchar *action_label;
                GtkAction *action;
@@ -272,9 +268,10 @@ editor_spell_checkers_foreach (EnchantDict *dictionary,
                g_free (action_label);
        }
 
+       e_spell_dictionary_free_suggestions (suggestions);
+
        g_free (path);
        g_free (word);
-       g_strfreev (suggestions);
 }
 
 static void
@@ -426,16 +423,20 @@ static void
 editor_spell_languages_changed (EEditor *editor,
                                GList *dictionaries)
 {
-       WebKitSpellChecker *checker;
+       ESpellChecker *checker;
        WebKitWebSettings *settings;
        GString *languages;
        const GList *iter;
 
+       /* Set the languages for spell-checker to use for suggestions etc. */
+       checker = e_editor_widget_get_spell_checker (editor->priv->editor_widget);
+       e_spell_checker_set_active_dictionaries (checker, dictionaries);
+
        languages = g_string_new ("");
 
        /* Join the languages codes to comma-separated list */
        for (iter = dictionaries; iter; iter = iter->next) {
-               EnchantDict *dictionary = iter->data;
+               ESpellDictionary *dictionary = iter->data;
 
                if (iter != dictionaries) {
                        g_string_append (languages, ",");
@@ -443,13 +444,9 @@ editor_spell_languages_changed (EEditor *editor,
 
                g_string_append (
                        languages,
-                       e_editor_spell_checker_get_dict_code (dictionary));
+                       e_spell_dictionary_get_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));
@@ -687,13 +684,6 @@ editor_constructed (GObject *object)
 }
 
 static void
-free_dict (gpointer dict,
-          gpointer checker)
-{
-       e_editor_spell_checker_free_dict (checker, dict);
-}
-
-static void
 editor_dispose (GObject *object)
 {
        EEditor *editor = E_EDITOR (object);
@@ -708,10 +698,7 @@ editor_dispose (GObject *object)
        g_clear_object (&priv->spell_check_actions);
        g_clear_object (&priv->suggestion_actions);
 
-       g_list_foreach (
-               priv->active_dictionaries, free_dict,
-               webkit_get_text_checker());
-       g_list_free (priv->active_dictionaries);
+       g_list_free_full (priv->active_dictionaries, g_object_unref);
        priv->active_dictionaries = NULL;
 
        g_clear_object (&priv->main_menu);
diff --git a/e-util/e-spell-checker.c b/e-util/e-spell-checker.c
new file mode 100644
index 0000000..4fd6849
--- /dev/null
+++ b/e-util/e-spell-checker.c
@@ -0,0 +1,521 @@
+/*
+ * e-spell-checker.c
+ *
+ * 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
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-spell-checker.h"
+#include "e-spell-dictionary.h"
+
+#include <webkit/webkitspellchecker.h>
+#include <pango/pango.h>
+#include <gtk/gtk.h>
+
+static void e_spell_checker_init_webkit_checker (WebKitSpellCheckerInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (
+       ESpellChecker,
+       e_spell_checker,
+       G_TYPE_OBJECT,
+       0,
+       G_IMPLEMENT_INTERFACE (
+               WEBKIT_TYPE_SPELL_CHECKER,
+               e_spell_checker_init_webkit_checker))
+
+struct _ESpellCheckerPrivate {
+       GList *active;
+
+       EnchantBroker *broker;
+};
+
+enum {
+       PROP_ACTIVE_DICTIONARIES
+};
+
+/**
+ * ESpellChecker:
+ *
+ * #ESpellChecker represents a spellchecker in Evolution. It can be used as a
+ * provider for dictionaries. It also implements #WebKitSpellCheckerInterface,
+ * so it can be set as a default spell-checker to WebKit editors
+ */
+
+static void
+wksc_check_spelling (WebKitSpellChecker *webkit_checker,
+                    const char         *word,
+                    int                *misspelling_location,
+                    int                *misspelling_length)
+{
+       ESpellChecker *checker = E_SPELL_CHECKER (webkit_checker);
+       PangoLanguage *language;
+       PangoLogAttr *attrs;
+       GList *dicts;
+       gint length, ii;
+
+       dicts = checker->priv->active;
+       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) {
+                               ESpellDictionary *dict = iter->data;
+
+                               if (e_spell_dictionary_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;
+                               }
+                       }
+
+                       g_free (new_word);
+               }
+       }
+
+       g_free (attrs);
+}
+
+static gchar **
+wksc_get_guesses (WebKitSpellChecker *webkit_checker,
+                 const gchar *word,
+                 const gchar *context)
+{
+       ESpellChecker *checker = E_SPELL_CHECKER (webkit_checker);
+       GList *dicts;
+       char** guesses;
+       gint ii;
+
+       guesses = g_malloc0_n (sizeof (gchar *), 11);
+       ii = 0;
+       for (dicts = checker->priv->active; dicts && ii < 10; dicts = dicts->next) {
+               ESpellDictionary *dict;
+               GList *suggestions, *iter;
+               gint suggestions_count;
+
+               dict = dicts->data;
+               suggestions = e_spell_dictionary_get_suggestions (dict, word, -1);
+
+               suggestions_count = g_list_length (suggestions);
+               if (suggestions_count > 0) {
+                       if (suggestions_count > 10) {
+                               suggestions_count = 10;
+                       }
+
+                       for (iter = suggestions; iter && ii < 10; iter = iter->next, ii++) {
+                               guesses[ii] = g_strdup (iter->data);
+                       }
+
+                       guesses[ii] = 0;
+
+                       e_spell_dictionary_free_suggestions (suggestions);
+               }
+       }
+
+       return guesses;
+}
+
+static gchar *
+wksc_get_autocorrect_suggestions (WebKitSpellChecker *webkit_checker,
+                                 const gchar *word)
+{
+       /* Not supported/needed */
+       return NULL;
+}
+
+static void
+spell_checker_learn_word (WebKitSpellChecker *webkit_checker,
+                         const gchar *word)
+{
+       /* Carefully, this will add the word to all active dictionaries! */
+
+       ESpellChecker *checker;
+       GList *iter;
+
+       checker = E_SPELL_CHECKER (webkit_checker);
+       for (iter = checker->priv->active; iter; iter = iter->next) {
+               ESpellDictionary *dict = iter->data;
+
+               e_spell_dictionary_learn_word (dict, word, -1);
+       }
+}
+
+static void
+spell_checker_ignore_word (WebKitSpellChecker *webkit_checker,
+                          const gchar *word)
+{
+       /* Carefully, this will add the word to all active dictionaries */
+
+       ESpellChecker *checker;
+       GList *iter;
+
+       checker = E_SPELL_CHECKER (webkit_checker);
+       for (iter = checker->priv->active; iter; iter = iter->next) {
+               ESpellDictionary *dict = iter->data;
+
+               e_spell_dictionary_ignore_word (dict, word, -1);
+       }
+}
+
+static void
+wksc_update_languages (WebKitSpellChecker *webkit_checker,
+                      const char *languages)
+{
+       ESpellChecker *checker;
+       GList *dictionaries = NULL;
+       gchar **langs;
+       gint ii;
+
+       checker = E_SPELL_CHECKER (webkit_checker);
+       if (languages) {
+               langs = g_strsplit (languages, ",", -1);
+               for (ii = 0; langs[ii] != NULL; ii++) {
+                       ESpellDictionary *dict;
+
+                       dict = e_spell_checker_lookup_dictionary (checker, langs[ii]);
+                       dictionaries = g_list_append (dictionaries, dict);
+               }
+               g_strfreev (langs);
+       } else {
+               const gchar *language;
+               ESpellDictionary *dict;
+
+               language = pango_language_to_string (gtk_get_default_language ());
+               dict = e_spell_checker_lookup_dictionary (checker, language);
+               if (dict) {
+                       dictionaries = g_list_append (dictionaries, dict);
+               } else {
+                       dictionaries = e_spell_checker_list_available_dicts (checker);
+               }
+       }
+
+       e_spell_checker_set_active_dictionaries (checker, dictionaries);
+       g_list_free_full (dictionaries, g_object_unref);
+}
+
+
+static void
+spell_checker_set_property (GObject *object,
+                           guint property_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_ACTIVE_DICTIONARIES:
+                       e_spell_checker_set_active_dictionaries (
+                               E_SPELL_CHECKER (object),
+                               g_value_get_pointer (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spell_checker_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_ACTIVE_DICTIONARIES:
+                       g_value_set_pointer (
+                               value,
+                               e_spell_checker_get_active_dictionaries (
+                                       E_SPELL_CHECKER (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spell_checker_dispose (GObject *object)
+{
+       ESpellCheckerPrivate *priv = E_SPELL_CHECKER (object)->priv;
+
+       g_list_free_full (priv->active, g_object_unref);
+       priv->active = NULL;
+
+       enchant_broker_free (priv->broker);
+       priv->broker = NULL;
+
+       /* Chain up to parent implementation */
+       G_OBJECT_CLASS (e_spell_checker_parent_class)->dispose (object);
+}
+
+static void
+e_spell_checker_class_init (ESpellCheckerClass *klass)
+{
+       GObjectClass *object_class;
+
+       e_spell_checker_parent_class = g_type_class_peek_parent (klass);
+       g_type_class_add_private (klass, sizeof (ESpellCheckerPrivate));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = spell_checker_set_property;
+       object_class->get_property = spell_checker_get_property;
+       object_class->dispose = spell_checker_dispose;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_ACTIVE_DICTIONARIES,
+               g_param_spec_pointer (
+                       "active-dictionaries",
+                       NULL,
+                       "List of active dictionaries to use for spell-checking",
+                       G_PARAM_READWRITE));
+}
+
+static void
+e_spell_checker_init_webkit_checker (WebKitSpellCheckerInterface *iface)
+{
+       iface->check_spelling_of_string = wksc_check_spelling;
+       iface->get_autocorrect_suggestions_for_misspelled_word = wksc_get_autocorrect_suggestions;
+       iface->get_guesses_for_word = wksc_get_guesses;
+       iface->ignore_word = spell_checker_ignore_word;
+       iface->learn_word = spell_checker_learn_word;
+       iface->update_spell_checking_languages = wksc_update_languages;
+}
+
+
+static void
+e_spell_checker_init (ESpellChecker *checker)
+{
+       checker->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+               checker, E_TYPE_SPELL_CHECKER, ESpellCheckerPrivate);
+}
+
+typedef struct  {
+       ESpellChecker *checker;
+       GList *dicts;
+} ListAvailDictsData;
+
+static void
+list_enchant_dicts (const char * const lang_tag,
+                   const char * const provider_name,
+                   const char * const provider_desc,
+                   const char * const provider_file,
+                   void * user_data)
+{
+       ListAvailDictsData *data = user_data;
+       EnchantDict *dict;
+
+       dict = enchant_broker_request_dict (data->checker->priv->broker, lang_tag);
+       if (dict) {
+               ESpellDictionary *e_dict;
+
+               e_dict = e_spell_dictionary_new (data->checker, dict);
+
+               data->dicts = g_list_prepend (data->dicts, e_dict);
+       }
+}
+
+
+/**
+ * e_spell_checker_list_available_dicts:
+ * @checker: An #ESpellChecker
+ *
+ * Returns list of all dictionaries available to the actual
+ * spell-checking backend.
+ *
+ * Return value: a #GList of #ESpellDictionary. Free the list using g_list_free()
+ *             when not needed anymore.
+ */
+GList *
+e_spell_checker_list_available_dicts (ESpellChecker *checker)
+{
+       ESpellChecker *e_checker;
+       ListAvailDictsData data;
+
+       e_checker = E_SPELL_CHECKER (checker);
+
+       data.checker = e_checker;
+       enchant_broker_list_dicts (
+               e_checker->priv->broker, list_enchant_dicts, &data);
+
+       return g_list_reverse (data.dicts);
+}
+
+/**
+ * e_spell_checker_lookup_dictionary:
+ * @checker: an #ESpellChecker
+ * @language_code: (allow-none) language code for which to lookup the dictionary
+ *
+ * Tries to find an #ESpellDictionary for given @language_code. When @language_code
+ * is #NULL, this function will return a default #ESpellDictionary.
+ *
+ * Return value: an #ESpellDictionary for @language_code
+ */
+ESpellDictionary *
+e_spell_checker_lookup_dictionary (ESpellChecker *checker,
+                                  const gchar *language_code)
+{
+       ESpellChecker *e_checker;
+       ESpellDictionary *e_dict;
+
+       e_checker = E_SPELL_CHECKER (checker);
+
+       e_dict = NULL;
+
+       if (!language_code) {
+               GList *dicts = e_spell_checker_list_available_dicts (checker);
+
+               if (dicts) {
+                       e_dict = g_object_ref (dicts->data);
+                       g_list_free_full (dicts, g_object_unref);
+               }
+       } else {
+               EnchantDict *dict;
+               dict = enchant_broker_request_dict (
+                       e_checker->priv->broker, language_code);
+               if (dict) {
+                       e_dict = e_spell_dictionary_new (checker, dict);
+               }
+       }
+
+       return e_dict;
+}
+
+/**
+ * e_spell_checker_get_active_dictionaries:
+ * @checker: an #ESpellChecker
+ *
+ * Returns a list of #ESpellDictionary that are to be used for spell-checking.
+ *
+ * Return value: a #GList of #ESpellDictionary. Free the list using g_list_fre()
+ *              when no longer needed.
+ */
+GList *
+e_spell_checker_get_active_dictionaries (ESpellChecker *checker)
+{
+       g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), NULL);
+
+       return g_list_copy (checker->priv->active);
+}
+
+/**
+ * e_spell_checker_set_active_dictionaries:
+ * @checker: an #ESpellChecker
+ * @active_dict: a #GList of #ESpellDictionary to use for spell-checking
+ *
+ * Set dictionaries to be actively used for spell-checking.
+ */
+void
+e_spell_checker_set_active_dictionaries (ESpellChecker *checker,
+                                        GList *active_dicts)
+{
+       g_return_if_fail (E_IS_SPELL_CHECKER (checker));
+
+       g_list_free_full (checker->priv->active, (GDestroyNotify) g_object_unref);
+
+       checker->priv->active = g_list_copy (active_dicts);
+       g_list_foreach (checker->priv->active, (GFunc) g_object_ref, NULL);
+}
+
+void
+e_spell_checker_free_dict (ESpellChecker *checker,
+                          EnchantDict *dict)
+{
+       g_return_if_fail (E_IS_SPELL_CHECKER (checker));
+       g_return_if_fail (dict != NULL);
+
+       enchant_broker_free_dict (checker->priv->broker, dict);
+}
+
+/**
+ * e_spell_checker_ignore_word:
+ * @checker: an #ESpellChecker
+ * @word: word to ignore for the rest of session
+ *
+ * Calls #e_spell_dictionary_ignore_word() on all active dictionaries in
+ * the @checker.
+ */
+void
+e_spell_checker_ignore_word (ESpellChecker *checker,
+                            const gchar *word)
+{
+       WebKitSpellCheckerInterface *webkit_checker_iface;
+
+       g_return_if_fail (E_IS_SPELL_CHECKER (checker));
+
+       webkit_checker_iface = WEBKIT_SPELL_CHECKER_GET_IFACE (checker);
+       webkit_checker_iface->ignore_word (WEBKIT_SPELL_CHECKER (checker), word);
+}
+
+/**
+ * e_spell_checker_learn_word:
+ * @checker: an #ESpellChecker
+ * @word: word to learn
+ *
+ * Calls #e_spell_dictionary_learn_word() on all active dictionaries in
+ * the @checker.
+ */
+void
+e_spell_checker_learn_word (ESpellChecker *checker,
+                           const gchar *word)
+{
+       WebKitSpellCheckerInterface *webkit_checker_iface;
+
+       g_return_if_fail (E_IS_SPELL_CHECKER (checker));
+
+       webkit_checker_iface = WEBKIT_SPELL_CHECKER_GET_IFACE (checker);
+       webkit_checker_iface->learn_word (WEBKIT_SPELL_CHECKER (checker), word);
+}
diff --git a/e-util/e-spell-checker.h b/e-util/e-spell-checker.h
new file mode 100644
index 0000000..c403ea1
--- /dev/null
+++ b/e-util/e-spell-checker.h
@@ -0,0 +1,86 @@
+/*
+ * e-spell-checker.h
+ *
+ * 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
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef E_SPELL_CHECKER_H
+#define E_SPELL_CHECKER_H
+
+#include <glib-object.h>
+#include <e-util/e-spell-dictionary.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SPELL_CHECKER \
+       (e_spell_checker_get_type ())
+#define E_SPELL_CHECKER(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SPELL_CHECKER, ESpellChecker))
+#define E_SPELL_CHECKER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_SPELL_CHECKER, ESpellCheckerClass))
+#define E_IS_SPELL_CHECKER(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_SPELL_CHECKER))
+#define E_IS_SPELL_CHECKER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_SPELL_CHECKER))
+#define E_SPELL_CHECKER_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_SPELL_CHECKER, ESpellCheckerClass))
+
+
+G_BEGIN_DECLS
+
+typedef struct _ESpellChecker ESpellChecker;
+typedef struct _ESpellCheckerPrivate ESpellCheckerPrivate;
+typedef struct _ESpellCheckerClass ESpellCheckerClass;
+
+struct _ESpellChecker {
+       GObject parent;
+
+       ESpellCheckerPrivate *priv;
+};
+
+struct _ESpellCheckerClass {
+       GObjectClass parent_class;
+};
+
+GType                  e_spell_checker_get_type        (void);
+
+GList *                        e_spell_checker_list_available_dicts
+                                                       (ESpellChecker *checker);
+ESpellDictionary *     e_spell_checker_lookup_dictionary
+                                                       (ESpellChecker *checker,
+                                                        const gchar *language_code);
+
+void                   e_spell_checker_set_active_dictionaries
+                                                       (ESpellChecker *checker,
+                                                        GList *active_dicts);
+GList *                        e_spell_checker_get_active_dictionaries
+                                                       (ESpellChecker *checker);
+
+void                   e_spell_checker_free_dict       (ESpellChecker *checker,
+                                                        EnchantDict *dict);
+
+void                   e_spell_checker_learn_word      (ESpellChecker *checker,
+                                                        const gchar *word);
+void                   e_spell_checker_ignore_word     (ESpellChecker *checker,
+                                                        const gchar *word);
+
+G_END_DECLS
+
+
+#endif /* E_SPELL_CHECKER_H */
\ No newline at end of file
diff --git a/e-util/e-spell-dictionary.c b/e-util/e-spell-dictionary.c
new file mode 100644
index 0000000..aed60ce
--- /dev/null
+++ b/e-util/e-spell-dictionary.c
@@ -0,0 +1,651 @@
+/*
+ * e-spell-dictionary.c
+ *
+ * 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
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-spell-dictionary.h"
+#include "e-spell-checker.h"
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+
+G_DEFINE_TYPE (
+       ESpellDictionary,
+       e_spell_dictionary,
+       G_TYPE_OBJECT);
+
+/**
+ * ESpellDictionary:
+ *
+ * The #ESpellDictionary is a wrapper around #EnchantDict.
+ */
+
+
+enum {
+       PROP_SPELLCHECKER
+};
+
+struct _ESpellDictionaryPrivate {
+       ESpellChecker *spell_checker;
+       EnchantDict *dict;
+
+       gchar *name;
+       gchar *code;
+       gchar *collate_key;
+};
+
+#define ISO_639_DOMAIN "iso_639"
+#define ISO_3166_DOMAIN        "iso_3166"
+
+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 */
+
+struct _enchant_dict_description_data {
+       gchar *language_tag;
+       gchar *dict_name;
+};
+
+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 = g_strdup (language_tag);
+       data->dict_name = language_name;
+}
+
+static void
+spell_dictionary_set_enchant_dict (ESpellDictionary *dictionary,
+                                  EnchantDict *enchant_dict)
+{
+       struct _enchant_dict_description_data data;
+
+       enchant_dict_describe (enchant_dict, describe_dictionary, &data);
+
+       dictionary->priv->code = data.language_tag;
+       dictionary->priv->name = data.dict_name;
+       dictionary->priv->collate_key = g_utf8_collate_key (data.dict_name, -1);
+}
+
+static void
+spell_dictionary_set_property (GObject *object,
+                              guint property_id,
+                              const GValue *value,
+                              GParamSpec *pspec)
+{
+       ESpellDictionary *dict = E_SPELL_DICTIONARY (object);
+
+       switch (property_id) {
+               case PROP_SPELLCHECKER:
+                       dict->priv->spell_checker =
+                               g_object_ref (g_value_get_object (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spell_dictionary_get_property (GObject *object,
+                              guint property_id,
+                              GValue *value,
+                              GParamSpec *pspec)
+{
+       ESpellDictionary *dict = E_SPELL_DICTIONARY (object);
+
+       switch (property_id) {
+               case PROP_SPELLCHECKER:
+                       g_value_set_object (
+                               value,
+                               e_spell_dictionary_get_parent_checker (dict));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spell_dictionary_dispose (GObject *object)
+{
+       ESpellDictionaryPrivate *priv = E_SPELL_DICTIONARY (object)->priv;
+
+       e_spell_checker_free_dict (
+               e_spell_dictionary_get_parent_checker (E_SPELL_DICTIONARY (object)),
+               priv->dict);
+       priv->dict = NULL;
+
+       g_free (priv->name);
+       priv->name = NULL;
+
+       g_free (priv->code);
+       priv->code = NULL;
+
+       g_free (priv->collate_key);
+       priv->collate_key = NULL;
+
+       g_clear_object (&priv->spell_checker);
+
+       /* Chain up to parent implementation */
+       G_OBJECT_CLASS (e_spell_dictionary_parent_class)->dispose (object);
+}
+
+static void
+e_spell_dictionary_class_init (ESpellDictionaryClass *klass)
+{
+       GObjectClass *object_class;
+
+       e_spell_dictionary_parent_class = g_type_class_peek_parent (klass);
+       g_type_class_add_private (klass, sizeof (ESpellDictionaryPrivate));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = spell_dictionary_set_property;
+       object_class->get_property = spell_dictionary_get_property;
+       object_class->dispose = spell_dictionary_dispose;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_SPELLCHECKER,
+               g_param_spec_object (
+                       "spell-checker",
+                       NULL,
+                       "Parent spell checker",
+                       E_TYPE_SPELL_CHECKER,
+                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+e_spell_dictionary_init (ESpellDictionary *dict)
+{
+       dict->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+               dict, E_TYPE_SPELL_DICTIONARY, ESpellDictionaryPrivate);
+
+       if (!iso_639_table && !iso_3166_table) {
+               #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
+       }
+}
+
+ESpellDictionary *
+e_spell_dictionary_new (ESpellChecker *parent_checker,
+                       EnchantDict *dict)
+{
+       ESpellDictionary *e_dict;
+
+       e_dict = g_object_new (E_TYPE_SPELL_DICTIONARY,
+                              "spell-checker", parent_checker, NULL);
+
+       spell_dictionary_set_enchant_dict (e_dict, dict);
+
+       return e_dict;
+}
+
+
+/**
+ * e_spell_dictionary_get_name:
+ * @dictionary: an #ESpellDictionary
+ *
+ * Provides a user-friendly name of the dictionary (for example
+ * "English (British)")
+ *
+ * Return value: a name of the @dictionary
+ */
+const gchar *
+e_spell_dictionary_get_name (ESpellDictionary *dictionary)
+{
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+
+       return dictionary->priv->name;
+}
+
+/**
+ * e_spell_dictionary_get_code:
+ * @dictionary: an #ESpellDictionary
+ *
+ * Provides an ISO code of spell-checking language of the
+ * @dictionary (for example "en_US").
+ *
+ * Return value: language code of the @dictionary
+ */
+const gchar *
+e_spell_dictionary_get_code (ESpellDictionary *dictionary)
+{
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+
+       return dictionary->priv->code;
+}
+
+/**
+ * e_spell_dictionary_check:
+ * @dictionary: an #ESpellDictionary
+ * @word: a word whose spelling to checking
+ * @len: size of @word in bytes or -1 when NULL-terminated
+ *
+ * Tries to lookup the @word in the @dictionary to check whether
+ * it's spelled correctly or not.
+ *
+ * Return value: #TRUE when the word is spelled correctly, FALSE otherwise
+ */
+gboolean
+e_spell_dictionary_check (ESpellDictionary *dictionary,
+                         const gchar *word,
+                         gsize len)
+{
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), TRUE);
+       g_return_val_if_fail (word && *word, TRUE);
+
+       return enchant_dict_check (dictionary->priv->dict, word, len);
+}
+
+/**
+ * e_spell_dictionary_learn_word:
+ * @dictionary: an #ESpellDictionary
+ * @word: a word to add to @dictionary
+ * @len: size of @word in bytes or -1 when NULL-terminated
+ *
+ * Permanently adds @word to @dictionary so that next time calling
+ * e_spell_dictionary_check() on the @word will return #TRUE.
+ */
+void
+e_spell_dictionary_learn_word (ESpellDictionary *dictionary,
+                              const gchar *word,
+                              gsize len)
+{
+       g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary));
+       g_return_if_fail (word && *word);
+
+       enchant_dict_add_to_personal (dictionary->priv->dict, word, len);
+}
+
+/**
+ * e_spell_dictionary_ignore_word:
+ * @dictionary: an #ESpellDictionary
+ * @word: a word to add to ignore list
+ * @len: size of @word in bytes or -1 when NULL-terminated
+ *
+ * Adds @word to temporary ignore list of the @dictionary, so that
+ * e_spell_dictionary_check() on the @word will return #TRUE. The
+ * list is cleared when the dictionary is free'd.
+ */
+void
+e_spell_dictionary_ignore_word (ESpellDictionary *dictionary,
+                               const gchar *word,
+                               gsize len)
+{
+       g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary));
+       g_return_if_fail (word && *word);
+
+       enchant_dict_add_to_session (dictionary->priv->dict, word, len);
+}
+
+/**
+ * e_spell_dictionary_get_suggestions:
+ * @dictionary: an #ESpellDictionary
+ * @word: a word to which to find suggestions
+ * @len: size of @word in bytes or -1 when NULL-terminated
+ *
+ * Provides list of alternative spellings of @word.
+ *
+ * Return value: a list of strings that has to be free'd by
+ * e_spell_dictionary_free_suggestions().
+ */
+GList *
+e_spell_dictionary_get_suggestions (ESpellDictionary *dictionary,
+                                   const gchar *word,
+                                   gsize len)
+{
+       gchar **s;
+       GList *list;
+       gsize ii, cnt;
+
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+       g_return_val_if_fail (word && *word, NULL);
+
+       s = enchant_dict_suggest (dictionary->priv->dict, word, len, &cnt);
+       list = NULL;
+       for (ii = 0; ii < len; ii++) {
+               list = g_list_prepend (list, g_strdup (s[ii]));
+       }
+       enchant_dict_free_suggestions (dictionary->priv->dict, s);
+
+       return g_list_reverse (list);
+}
+
+/**
+ * e_spell_dictionary_free_suggestions:
+ * @suggestions: (allow-none) a list of suggestions obtained from
+ *              e_spell_dictionary_get_suggestions()
+ *
+ * Frees the @suggestions list.
+ */
+void
+e_spell_dictionary_free_suggestions (GList *suggestions)
+{
+       g_list_foreach (suggestions, (GFunc) g_free, NULL);
+}
+
+/**
+ * e_spell_dictionary_add_correction
+ * @dictionary: an #ESpellDictionary
+ * @misspelled: a word to which add a correction
+ * @misspelled_len: size of @misspelled (in bytes) or -1 when string NULL-terminated
+ * @correction: a correct word
+ * @correction_len: size of @correction (in bytes) or -1 when string NULL-terminated
+ *
+ * Learns a new @correction of @misspelled word
+ */
+void
+e_spell_dictionary_store_correction (ESpellDictionary *dictionary,
+                                    const gchar *misspelled,
+                                    gsize misspelled_len,
+                                    const gchar *correction,
+                                    gsize correction_len)
+{
+       g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary));
+       g_return_if_fail (misspelled && *misspelled);
+       g_return_if_fail (correction && *correction);
+
+       enchant_dict_store_replacement (
+               dictionary->priv->dict, misspelled, misspelled_len,
+               correction, correction_len);
+}
+
+/**
+ * e_spell_dictionary_get_parent_checker:
+ * @dictionary: an #ESpellDictionary
+ *
+ * Returns an #ESpellChecker which is parent (and the original owner) of
+ * the @dictionary.
+ *
+ * Return value: an #ESpellChecker
+ */
+ESpellChecker *
+e_spell_dictionary_get_parent_checker (ESpellDictionary *dictionary)
+{
+       g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+
+       return dictionary->priv->spell_checker;
+}
+
+gint
+e_spell_dictionary_compare (ESpellDictionary *dict1,
+                           ESpellDictionary *dict2)
+{
+       return strcmp (dict1->priv->collate_key, dict2->priv->collate_key);
+}
diff --git a/e-util/e-spell-dictionary.h b/e-util/e-spell-dictionary.h
new file mode 100644
index 0000000..fd4e307
--- /dev/null
+++ b/e-util/e-spell-dictionary.h
@@ -0,0 +1,104 @@
+/*
+ * e-spell-dictionary.h
+ *
+ * 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
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef E_SPELL_DICTIONARY_H
+#define E_SPELL_DICTIONARY_H
+
+#include <glib-object.h>
+#include <enchant/enchant.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SPELL_DICTIONARY \
+       (e_spell_dictionary_get_type ())
+#define E_SPELL_DICTIONARY(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SPELL_DICTIONARY, ESpellDictionary))
+#define E_SPELL_DICTIONARY_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_SPELL_DICTIONARY, ESpellDictionaryClass))
+#define E_IS_SPELL_DICTIONARY(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_SPELL_DICTIONARY))
+#define E_IS_SPELL_DICTIONARY_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_SPELL_DICTIONARY))
+#define E_SPELL_DICTIONARY_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_SPELL_DICTIONARY, ESpellDictionaryClass))
+
+
+G_BEGIN_DECLS
+
+typedef struct _ESpellDictionary ESpellDictionary;
+typedef struct _ESpellDictionaryPrivate ESpellDictionaryPrivate;
+typedef struct _ESpellDictionaryClass ESpellDictionaryClass;
+typedef struct _ESpellChecker ESpellChecker;
+
+struct _ESpellDictionary {
+       GObject parent;
+
+       ESpellDictionaryPrivate *priv;
+};
+
+struct _ESpellDictionaryClass {
+       GObjectClass parent_class;
+
+};
+
+GType                  e_spell_dictionary_get_type     (void);
+
+ESpellDictionary *     e_spell_dictionary_new          (ESpellChecker *parent_checker,
+                                                        EnchantDict *dict);
+
+const gchar *          e_spell_dictionary_get_name     (ESpellDictionary *dict);
+const gchar *          e_spell_dictionary_get_code     (ESpellDictionary *dict);
+
+
+gboolean               e_spell_dictionary_check        (ESpellDictionary *dict,
+                                                        const gchar *word,
+                                                        gsize len);
+
+void                   e_spell_dictionary_learn_word   (ESpellDictionary *dict,
+                                                        const gchar *word,
+                                                        gsize len);
+void                   e_spell_dictionary_ignore_word  (ESpellDictionary *dict,
+                                                        const gchar *word,
+                                                        gsize len);
+GList *                        e_spell_dictionary_get_suggestions
+                                                       (ESpellDictionary *dict,
+                                                        const gchar *word,
+                                                        gsize len);
+void                   e_spell_dictionary_free_suggestions
+                                                       (GList *suggestions);
+void                   e_spell_dictionary_store_correction
+                                                       (ESpellDictionary *dict,
+                                                        const gchar *misspelled,
+                                                        gsize misspelled_len,
+                                                        const gchar *correction,
+                                                        gsize correction_len);
+
+ESpellChecker *                e_spell_dictionary_get_parent_checker
+                                                       (ESpellDictionary *dict);
+
+gint                   e_spell_dictionary_compare      (ESpellDictionary *dict1,
+                                                        ESpellDictionary *dict2);
+
+G_END_DECLS
+
+
+#endif /* E_SPELL_DICTIONARY_H */
\ No newline at end of file
diff --git a/e-util/e-util.h b/e-util/e-util.h
index 05f9709..1633702 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -103,7 +103,6 @@
 #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>


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