[evolution/webkit-composer: 63/111] Move spell-checking parts to e-util



commit da647dd1ac12e5b4dfb428b6362eb468dd5f6da7
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 |    7 +-
 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, 1432 insertions(+), 863 deletions(-)
---
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index b4f012d..3a403e2 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -98,7 +98,7 @@ libeutil_la_CPPFLAGS = \
 	$(EVOLUTION_DATA_SERVER_CFLAGS) \
 	$(GNOME_PLATFORM_CFLAGS) \
 	$(GEO_CFLAGS) \
-	$(GTKHTML_CFLAGS) \
+	$(ENCHANT_CFLAGS) \
 	$(NULL)
 
 eutilinclude_HEADERS =  \
@@ -182,7 +182,6 @@ eutilinclude_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 \
@@ -266,6 +265,8 @@ eutilinclude_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-stock-request.h \
 	e-table-click-to-add.h \
@@ -454,7 +455,6 @@ libeutil_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 \
@@ -538,6 +538,8 @@ libeutil_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-stock-request.c \
 	e-table-click-to-add.c \
@@ -640,7 +642,7 @@ libeutil_la_LIBADD =  \
 	$(EVOLUTION_DATA_SERVER_LIBS) \
 	$(GNOME_PLATFORM_LIBS) \
 	$(GEO_LIBS) \
-	$(GTKHTML_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..d91c6cb 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,7 +637,7 @@ 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. */
@@ -649,7 +650,7 @@ e_editor_spell_check_dialog_set_dictionaries (EEditorSpellCheckDialog *dialog,
 		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 fe2c973..65593cb 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -102,7 +102,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]