[evolution/wip/webkit-composer: 3/262] Import classes for spell checking



commit b9924e98f7414fccd755557b8a458ceb23832cbf
Author: Dan Vrátil <dvratil redhat com>
Date:   Mon Jul 23 11:39:44 2012 +0200

    Import classes for spell checking
    
    GtkhtmlSpellDialog => ESpellDialog
    GtkhtmlSpellLanguage => ESpellDictionary
    
    Other GTKHtml spell-checking classes are not needed now

 configure.ac                |   10 +
 e-util/e-spell-dialog.c     |  755 +++++++++++++++++++++++++++++++++++++++++++
 e-util/e-spell-dialog.h     |   83 +++++
 e-util/e-spell-dictionary.c |  472 +++++++++++++++++++++++++++
 e-util/e-spell-dictionary.h |   53 +++
 e-util/e-util.h             |    2 +
 6 files changed, 1375 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index bf23f0c..ed3fdf3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -45,6 +45,7 @@ m4_define([soup_minimum_version], [2.42])
 m4_define([soup_encoded_version], [SOUP_VERSION_2_42])
 
 m4_define([eds_minimum_version], [evo_version])
+m4_define([enchant_minimum_version], [1.1.7])
 m4_define([gtkhtml_minimum_version], [4.5.2])
 m4_define([gdk_pixbuf_minimum_version], [2.24.0])
 m4_define([gcr_minimum_version], [3.4])
@@ -290,6 +291,15 @@ PKG_CHECK_MODULES([EVOLUTION_DATA_SERVER],
 AC_SUBST(EVOLUTION_DATA_SERVER_CFLAGS)
 AC_SUBST(EVOLUTION_DATA_SERVER_LIBS)
 
+
+dnl ****************
+dnl Enchant Library
+dnl ****************
+PKG_CHECK_MODULES([ENCHANT],
+       [enchant >= enchant_minimum_version])
+AC_SUBST(ENCHANT_CFLAGS)
+AC_SUBST(ENCHANT_LIBS)
+
 dnl ******************************
 dnl Canberra / Canberra-GTK Sound
 dnl ******************************
diff --git a/e-util/e-spell-dialog.c b/e-util/e-spell-dialog.c
new file mode 100644
index 0000000..957d28a
--- /dev/null
+++ b/e-util/e-spell-dialog.c
@@ -0,0 +1,755 @@
+/* e-editor-spell-dialog.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-dialog.h"
+#include "e-spell-dictionary.h"
+#include "e-editor-widget.h"
+
+#include <glib/gi18n-lib.h>
+
+G_DEFINE_TYPE (
+       ESpellDialog,
+       e_spell_dialog,
+       GTK_TYPE_DIALOG
+);
+
+enum {
+       COMBO_COLUMN_DICTIONARY,        /* E_TYPE_SPELL_DICTIONARY */
+       COMBO_COLUMN_TEXT               /* G_TYPE_STRING */
+};
+
+enum {
+       PROP_0,
+       PROP_WORD
+};
+
+enum {
+       ADDED,
+       IGNORED,
+       NEXT_WORD,
+       PREV_WORD,
+       REPLACE,
+       REPLACE_ALL,
+       LAST_SIGNAL
+};
+
+struct _ESpellDialogPrivate {
+
+       /* widgets */
+       GtkWidget *add_word_button;
+       GtkWidget *back_button;
+       GtkWidget *dictionary_combo;
+       GtkWidget *ignore_button;
+       GtkWidget *replace_button;
+       GtkWidget *replace_all_button;
+       GtkWidget *skip_button;
+       GtkWidget *suggestion_label;
+       GtkWidget *tree_view;
+
+       GList *dictionaries;
+       gchar *word;
+
+       EEditorWidget *editor;
+};
+
+static guint signals[LAST_SIGNAL];
+
+static void
+spell_dialog_render_dictionary (GtkComboBox *combo_box,
+                               GtkCellRenderer *renderer,
+                               GtkTreeModel *model,
+                               GtkTreeIter *iter)
+{
+       ESpellDictionary *dictionary;
+       const gchar *name;
+
+       gtk_tree_model_get (model, iter, 0, &dictionary, -1);
+       name = e_spell_dictionary_get_name (dictionary);
+
+       g_object_set (renderer, "text", name, NULL);
+}
+
+static void
+spell_dialog_update_buttons (ESpellDialog *dialog)
+{
+       gboolean sensitive;
+
+       /* Update "Add Word" and "Ignore" button sensitivity. */
+       sensitive = (e_spell_dialog_get_word (dialog) != NULL);
+       gtk_widget_set_sensitive (dialog->priv->add_word_button, sensitive);
+       gtk_widget_set_sensitive (dialog->priv->ignore_button, sensitive);
+}
+
+static void
+spell_dialog_update_suggestion_label (ESpellDialog *dialog)
+{
+       GtkLabel *label;
+       const gchar *word;
+       gchar *markup;
+       gchar *text;
+
+       label = GTK_LABEL (dialog->priv->suggestion_label);
+       word = e_spell_dialog_get_word (dialog);
+
+       /* Handle the simple case and get out. */
+       if (word == NULL) {
+               gtk_label_set_markup (label, NULL);
+               return;
+       }
+
+       text = g_strdup_printf (_("Suggestions for \"%s\""), word);
+       markup = g_strdup_printf ("<b>%s</b>", text);
+
+       gtk_label_set_markup (label, markup);
+
+       g_free (markup);
+       g_free (text);
+}
+
+static void
+spell_dialog_update_tree_view (ESpellDialog *dialog)
+{
+       WebKitSpellChecker *checker;
+       GtkTreeSelection *selection;
+       GtkTreeView *tree_view;
+       GtkListStore *store;
+       GtkTreePath *path;
+       const gchar *word;
+       gchar** list;
+       gint i;
+
+       tree_view = GTK_TREE_VIEW (dialog->priv->tree_view);
+       selection = gtk_tree_view_get_selection (tree_view);
+       word = e_spell_dialog_get_word (dialog);
+
+       store = gtk_list_store_new (1, G_TYPE_STRING);
+
+       checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+       if (checker != NULL && word != NULL) {
+               gchar *suggestions;
+               suggestions = webkit_spell_checker_get_autocorrect_suggestions_for_misspelled_word (
+                       checker, word);
+
+               list = g_strsplit (suggestions, ",", 0);
+               g_free (suggestions);
+       }
+
+       for (i = 0; list && list[i]; i++) {
+               const gchar *suggestion = list[i];
+               GtkTreeIter iter;
+
+               gtk_list_store_append (store, &iter);
+               gtk_list_store_set (store, &iter, 0, suggestion, -1);
+       }
+       g_strfreev (list);
+
+       gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (store));
+
+       /* Select the first item. */
+       path = gtk_tree_path_new_first ();
+       gtk_tree_selection_select_path (selection, path);
+       gtk_tree_path_free (path);
+}
+
+static void
+spell_dialog_add_word_cb (ESpellDialog *dialog)
+{
+       WebKitSpellChecker *checker;
+       const gchar *word;
+
+       checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+       word = e_spell_dialog_get_word (dialog);
+
+       webkit_spell_checker_learn_word (checker, word);
+       g_signal_emit (dialog, signals[ADDED], 0);
+
+       e_spell_dialog_next_word (dialog);
+}
+
+static void
+spell_dialog_ignore_cb (ESpellDialog *dialog)
+{
+       WebKitSpellChecker *checker;
+       const gchar *word;
+
+       checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
+       word = e_spell_dialog_get_word (dialog);
+
+       webkit_spell_checker_ignore_word (checker, word);
+       g_signal_emit (dialog, signals[IGNORED], 0);
+
+       e_spell_dialog_next_word (dialog);
+}
+
+static void
+spell_dialog_selection_changed_cb (ESpellDialog *dialog)
+{
+       GtkTreeSelection *selection;
+       GtkTreeView *tree_view;
+       gboolean selected;
+
+       tree_view = GTK_TREE_VIEW (dialog->priv->tree_view);
+       selection = gtk_tree_view_get_selection (tree_view);
+
+       /* Update "Replace" and "Replace All" button sensitivity. */
+       selected = gtk_tree_selection_get_selected (selection, NULL, NULL);
+       gtk_widget_set_sensitive (dialog->priv->replace_button, selected);
+       gtk_widget_set_sensitive (dialog->priv->replace_all_button, selected);
+}
+
+static void
+spell_dialog_replace_cb (ESpellDialog *dialog)
+{
+       gchar *word;
+
+       word = e_spell_dialog_get_active_suggestion (dialog);
+       g_return_if_fail (word != NULL);
+
+       g_signal_emit (dialog, signals[REPLACE], 0, word);
+
+       g_free (word);
+}
+
+static void
+spell_dialog_replace_all_cb (ESpellDialog *dialog)
+{
+       gchar *word;
+
+       word = e_spell_dialog_get_active_suggestion (dialog);
+       g_return_if_fail (word != NULL);
+
+       g_signal_emit (dialog, signals[REPLACE_ALL], 0, word);
+
+       g_free (word);
+}
+
+static void
+spell_dialog_set_property (GObject *object,
+                           guint property_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_WORD:
+                       e_spell_dialog_set_word (
+                               E_SPELL_DIALOG (object),
+                               g_value_get_string (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spell_dialog_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_WORD:
+                       g_value_set_string (
+                               value, e_spell_dialog_get_word (
+                               E_SPELL_DIALOG (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spell_dialog_dispose (GObject *object)
+{
+       ESpellDialogPrivate *priv;
+
+       priv = E_SPELL_DIALOG (object)->priv;
+
+       g_clear_object (&priv->add_word_button);
+       g_clear_object (&priv->back_button);
+       g_clear_object (&priv->dictionary_combo);
+       g_clear_object (&priv->ignore_button);
+       g_clear_object (&priv->replace_button);
+       g_clear_object (&priv->replace_all_button);
+       g_clear_object (&priv->skip_button);
+       g_clear_object (&priv->tree_view);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_spell_dialog_parent_class)->dispose (object);
+}
+
+static void
+spell_dialog_finalize (GObject *object)
+{
+       ESpellDialogPrivate *priv;
+
+       priv = E_SPELL_DIALOG (object)->priv;
+
+       g_free (priv->word);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_spell_dialog_parent_class)->finalize (object);
+}
+
+static void
+e_spell_dialog_class_init (ESpellDialogClass *klass)
+{
+       GObjectClass *object_class;
+
+       e_spell_dialog_parent_class = g_type_class_peek_parent (klass);
+       g_type_class_add_private (klass, sizeof (ESpellDialogPrivate));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = spell_dialog_set_property;
+       object_class->get_property = spell_dialog_get_property;
+       object_class->dispose = spell_dialog_dispose;
+       object_class->finalize = spell_dialog_finalize;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_WORD,
+               g_param_spec_string (
+                       "word",
+                       "Misspelled Word",
+                       "The current misspelled word",
+                       NULL,
+                       G_PARAM_READWRITE));
+
+       signals[ADDED] = g_signal_new (
+               "added",
+               G_OBJECT_CLASS_TYPE (object_class),
+               G_SIGNAL_RUN_LAST,
+               0, NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+
+       signals[IGNORED] = g_signal_new (
+               "ignored",
+               G_OBJECT_CLASS_TYPE (object_class),
+               G_SIGNAL_RUN_LAST,
+               0, NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+
+       signals[NEXT_WORD] = g_signal_new (
+               "next-word",
+               G_OBJECT_CLASS_TYPE (object_class),
+               G_SIGNAL_RUN_LAST,
+               0, NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+
+       signals[PREV_WORD] = g_signal_new (
+               "prev-word",
+               G_OBJECT_CLASS_TYPE (object_class),
+               G_SIGNAL_RUN_LAST,
+               0, NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+
+       signals[REPLACE] = g_signal_new (
+               "replace",
+               G_OBJECT_CLASS_TYPE (object_class),
+               G_SIGNAL_RUN_LAST,
+               0, NULL, NULL,
+               g_cclosure_marshal_VOID__STRING,
+               G_TYPE_NONE, 1,
+               G_TYPE_STRING);
+
+       signals[REPLACE_ALL] = g_signal_new (
+               "replace-all",
+               G_OBJECT_CLASS_TYPE (object_class),
+               G_SIGNAL_RUN_LAST,
+               0, NULL, NULL,
+               g_cclosure_marshal_VOID__STRING,
+               G_TYPE_NONE, 1,
+               G_TYPE_STRING);
+}
+
+static void
+e_spell_dialog_init (ESpellDialog *dialog)
+{
+       GtkTreeSelection *selection;
+       GtkTreeViewColumn *column;
+       GtkCellRenderer *renderer;
+       GtkWidget *container;
+       GtkWidget *content_area;
+       GtkWidget *table;
+       GtkWidget *widget;
+       gchar *markup;
+
+       dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+               dialog, E_TYPE_SPELL_DIALOG, ESpellDialogPrivate);
+
+       g_signal_connect (
+               dialog, "notify::word", G_CALLBACK (
+               spell_dialog_update_buttons), NULL);
+
+       g_signal_connect (
+               dialog, "notify::word", G_CALLBACK (
+               spell_dialog_update_suggestion_label), NULL);
+
+       g_signal_connect (
+               dialog, "notify::word", G_CALLBACK (
+               spell_dialog_update_tree_view), NULL);
+
+       /* Build the widgets. */
+
+       content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+       gtk_dialog_add_button (
+               GTK_DIALOG (dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
+       gtk_window_set_title (GTK_WINDOW (dialog), _("Spell Checker"));
+       gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+
+       gtk_box_set_spacing (GTK_BOX (content_area), 2);
+
+       /* Table */
+       widget = gtk_table_new (4, 2, FALSE);
+       gtk_container_set_border_width (GTK_CONTAINER (widget), 5);
+       gtk_table_set_row_spacings (GTK_TABLE (widget), 6);
+       gtk_table_set_col_spacings (GTK_TABLE (widget), 6);
+       gtk_table_set_row_spacing (GTK_TABLE (widget), 1, 12);
+       gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
+       gtk_widget_show (widget);
+       table = widget;
+
+       /* Suggestion Label */
+       widget = gtk_label_new (NULL);
+       gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
+       gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+       gtk_table_attach (
+               GTK_TABLE (table), widget, 0, 2, 0, 1,
+               GTK_EXPAND | GTK_FILL, 0, 0, 0);
+       dialog->priv->suggestion_label = g_object_ref (widget);
+       gtk_widget_show (widget);
+
+       /* Scrolled Window */
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (
+               GTK_SCROLLED_WINDOW (widget),
+               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_shadow_type (
+               GTK_SCROLLED_WINDOW (widget),
+               GTK_SHADOW_ETCHED_IN);
+       gtk_table_attach (
+               GTK_TABLE (table), widget, 0, 1, 1, 2,
+               GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+       gtk_widget_show (widget);
+       container = widget;
+
+       /* Tree View */
+       widget = gtk_tree_view_new ();
+       column = gtk_tree_view_column_new ();
+       renderer = gtk_cell_renderer_text_new ();
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+       g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+       gtk_tree_view_column_pack_start (column, renderer, TRUE);
+       gtk_tree_view_column_add_attribute (column, renderer, "text", 0);
+       gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
+       gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget), FALSE);
+       gtk_label_set_mnemonic_widget (
+               GTK_LABEL (dialog->priv->suggestion_label), widget);
+       g_signal_connect_swapped (
+               widget, "row-activated",
+               G_CALLBACK (spell_dialog_replace_cb), dialog);
+       g_signal_connect_swapped (
+               selection, "changed",
+               G_CALLBACK (spell_dialog_selection_changed_cb), dialog);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       dialog->priv->tree_view = g_object_ref (widget);
+       gtk_widget_show (widget);
+
+       /* Vertical Button Box */
+       widget = gtk_vbutton_box_new ();
+       gtk_button_box_set_layout (
+               GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_START);
+       gtk_box_set_spacing (GTK_BOX (widget), 6);
+       gtk_table_attach (
+               GTK_TABLE (table), widget, 1, 2, 1, 2,
+               0, GTK_EXPAND | GTK_FILL, 0, 0);
+       gtk_widget_show (widget);
+       container = widget;
+
+       /* Replace Button */
+       widget = gtk_button_new_with_mnemonic (_("_Replace"));
+       gtk_button_set_image (
+               GTK_BUTTON (widget),
+               gtk_image_new_from_stock (
+               GTK_STOCK_CONVERT, GTK_ICON_SIZE_BUTTON));
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (spell_dialog_replace_cb), dialog);
+       gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+       dialog->priv->replace_button = g_object_ref (widget);
+       gtk_widget_set_sensitive (widget, FALSE);
+       gtk_widget_show (widget);
+
+       /* Replace All Button */
+       widget = gtk_button_new_with_mnemonic (_("R_eplace All"));
+       gtk_button_set_image (
+               GTK_BUTTON (widget),
+               gtk_image_new_from_stock (
+               GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON));
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (spell_dialog_replace_all_cb), dialog);
+       gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+       dialog->priv->replace_all_button = g_object_ref (widget);
+       gtk_widget_set_sensitive (widget, FALSE);
+       gtk_widget_show (widget);
+
+       /* Ignore Button */
+       widget = gtk_button_new_with_mnemonic (_("_Ignore"));
+       gtk_button_set_image (
+               GTK_BUTTON (widget),
+               gtk_image_new_from_stock (
+               GTK_STOCK_CLEAR, GTK_ICON_SIZE_BUTTON));
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (spell_dialog_ignore_cb), dialog);
+       gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+       dialog->priv->ignore_button = g_object_ref (widget);
+       gtk_widget_set_sensitive (widget, FALSE);
+       gtk_widget_show (widget);
+
+       /* Skip Button */
+       widget = gtk_button_new_with_mnemonic (_("_Skip"));
+       gtk_button_set_image (
+               GTK_BUTTON (widget),
+               gtk_image_new_from_stock (
+               GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_BUTTON));
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (e_spell_dialog_next_word), dialog);
+       gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+       dialog->priv->skip_button = g_object_ref (widget);
+       gtk_widget_show (widget);
+
+       /* Back Button */
+       widget = gtk_button_new_with_mnemonic (_("_Back"));
+       gtk_button_set_image (
+               GTK_BUTTON (widget),
+               gtk_image_new_from_stock (
+               GTK_STOCK_GO_BACK, GTK_ICON_SIZE_BUTTON));
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (e_spell_dialog_prev_word), dialog);
+       gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+       dialog->priv->back_button = g_object_ref (widget);
+       gtk_widget_show (widget);
+
+       /* Dictionary Label */
+       markup = g_markup_printf_escaped ("<b>%s</b>", _("Dictionary"));
+       widget = gtk_label_new (markup);
+       gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+       gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+       gtk_table_attach (
+               GTK_TABLE (table), widget, 0, 2, 2, 3,
+               GTK_EXPAND | GTK_FILL, 0, 0, 0);
+       gtk_widget_show (widget);
+       g_free (markup);
+
+       /* Dictionary Combo Box */
+       widget = gtk_combo_box_new ();
+       renderer = gtk_cell_renderer_text_new ();
+       gtk_cell_layout_pack_start (
+               GTK_CELL_LAYOUT (widget), renderer, TRUE);
+       gtk_cell_layout_set_cell_data_func (
+               GTK_CELL_LAYOUT (widget), renderer,
+               (GtkCellLayoutDataFunc) spell_dialog_render_dictionary,
+               NULL, NULL);
+       g_signal_connect_swapped (
+               widget, "changed",
+               G_CALLBACK (spell_dialog_update_tree_view), dialog);
+       gtk_table_attach (
+               GTK_TABLE (table), widget, 0, 1, 3, 4,
+               GTK_EXPAND | GTK_FILL, 0, 0, 0);
+       dialog->priv->dictionary_combo = g_object_ref (widget);
+       gtk_widget_show (widget);
+
+       /* Add Word Button */
+       widget = gtk_button_new_with_mnemonic (_("_Add Word"));
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (spell_dialog_add_word_cb), dialog);
+       gtk_button_set_image (
+               GTK_BUTTON (widget),
+               gtk_image_new_from_stock (
+               GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON));
+       gtk_table_attach (
+               GTK_TABLE (table), widget, 1, 2, 3, 4,
+               GTK_FILL, 0, 0, 0);
+       dialog->priv->add_word_button = g_object_ref (widget);
+       gtk_widget_show (widget);
+}
+
+GtkWidget *
+e_spell_dialog_new (GtkWindow *parent)
+{
+       return g_object_new (
+               E_TYPE_SPELL_DIALOG,
+               "transient-for", parent, NULL);
+}
+
+void
+e_spell_dialog_close (ESpellDialog *dialog)
+{
+       g_return_if_fail (E_IS_SPELL_DIALOG (dialog));
+
+       gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
+}
+
+const gchar *
+e_spell_dialog_get_word (ESpellDialog *dialog)
+{
+       g_return_val_if_fail (E_IS_SPELL_DIALOG (dialog), NULL);
+
+       return dialog->priv->word;
+}
+
+void
+e_spell_dialog_set_word (ESpellDialog *dialog,
+                        const gchar *word)
+{
+       g_return_if_fail (E_IS_SPELL_DIALOG (dialog));
+
+       /* Do not emit signals if the word is unchanged. */
+       if (word != NULL && dialog->priv->word != NULL)
+               if (g_str_equal (word, dialog->priv->word))
+                       return;
+
+       g_free (dialog->priv->word);
+       dialog->priv->word = g_strdup (word);
+
+       g_object_notify (G_OBJECT (dialog), "word");
+}
+
+void
+e_spell_dialog_next_word (ESpellDialog *dialog)
+{
+       g_signal_emit (dialog, signals[NEXT_WORD], 0);
+}
+
+void
+e_spell_dialog_prev_word (ESpellDialog *dialog)
+{
+       g_signal_emit (dialog, signals[PREV_WORD], 0);
+}
+
+GList *
+e_spell_dialog_get_dictionaries (ESpellDialog *dialog)
+{
+       g_return_val_if_fail (E_IS_SPELL_DIALOG (dialog), NULL);
+
+       return g_list_copy (dialog->priv->dictionaries);
+}
+
+void
+e_spell_dialog_set_dictionaries (ESpellDialog *dialog,
+                                  GList *dictionaries)
+{
+       GtkComboBox *combo_box;
+       GtkListStore *store;
+       GList *list;
+
+       g_return_if_fail (E_IS_SPELL_DIALOG (dialog));
+
+       combo_box = GTK_COMBO_BOX (dialog->priv->dictionary_combo);
+
+       /* Free the old list of spell checkers. */
+       list = dialog->priv->dictionaries;
+       g_list_foreach (list, (GFunc) g_object_unref, NULL);
+       g_list_free (list);
+
+       /* Copy and sort the new list of spell checkers. */
+       list = g_list_sort (
+               g_list_copy (dictionaries),
+               (GCompareFunc) e_spell_dictionary_compare);
+       g_list_foreach (list, (GFunc) g_object_ref, NULL);
+       dialog->priv->dictionaries = list;
+
+       /* Populate a list store for the combo box. */
+
+       store = gtk_list_store_new (1, E_TYPE_SPELL_DICTIONARY);
+
+       while (list != NULL) {
+               ESpellDictionary *dictionary = list->data;
+               GtkTreeIter iter;
+
+               gtk_list_store_append (store, &iter);
+               gtk_list_store_set (store, &iter, 0, dictionary, -1);
+
+               list = g_list_next (list);
+       }
+
+       /* FIXME Try to preserve the previously selected language. */
+       gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (store));
+       gtk_combo_box_set_active (combo_box, 0);
+
+       g_object_unref (store);
+
+       /* XXX notify property? */
+}
+
+ESpellDictionary *
+e_spell_dialog_get_active_dictionary (ESpellDialog *dialog)
+{
+       ESpellDictionary *dict;
+       GtkComboBox *combo_box;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+
+       g_return_val_if_fail (E_IS_SPELL_DIALOG (dialog), NULL);
+
+       combo_box = GTK_COMBO_BOX (dialog->priv->dictionary_combo);
+       model = gtk_combo_box_get_model (combo_box);
+
+       if (!gtk_combo_box_get_active_iter (combo_box, &iter))
+               return NULL;
+
+       gtk_tree_model_get (model, &iter, COMBO_COLUMN_DICTIONARY, &dict, -1);
+
+       return dict;
+}
+
+gchar *
+e_spell_dialog_get_active_suggestion (ESpellDialog *dialog)
+{
+       GtkTreeSelection *selection;
+       GtkTreeView *tree_view;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       gchar *word;
+
+       g_return_val_if_fail (E_IS_SPELL_DIALOG (dialog), NULL);
+
+       tree_view = GTK_TREE_VIEW (dialog->priv->tree_view);
+       selection = gtk_tree_view_get_selection (tree_view);
+
+       /* If nothing is selected, return NULL. */
+       if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+               return NULL;
+
+       gtk_tree_model_get (model, &iter, 0, &word, -1);
+       g_return_val_if_fail (word != NULL, NULL);
+
+       return word;
+}
diff --git a/e-util/e-spell-dialog.h b/e-util/e-spell-dialog.h
new file mode 100644
index 0000000..832cf55
--- /dev/null
+++ b/e-util/e-spell-dialog.h
@@ -0,0 +1,83 @@
+/* e-editor-spell-dialog.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.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_SPELL_DIALOG_H
+#define E_SPELL_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-spell-dictionary.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SPELL_DIALOG \
+       (e_spell_dialog_get_type ())
+#define E_SPELL_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SPELL_DIALOG, ESpellDialog))
+#define E_SPELL_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_SPELL_DIALOG, ESpellDialogClass))
+#define E_IS_SPELL_DIALOG(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_SPELL_DIALOG))
+#define E_IS_SPELL_DIALOG_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_SPELL_DIALOG))
+#define E_SPELL_DIALOG_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_SPELL_DIALOG, ESpellDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESpellDialog ESpellDialog;
+typedef struct _ESpellDialogClass ESpellDialogClass;
+typedef struct _ESpellDialogPrivate ESpellDialogPrivate;
+
+struct _ESpellDialog {
+       GtkDialog parent;
+       ESpellDialogPrivate *priv;
+};
+
+struct _ESpellDialogClass {
+       GtkDialogClass parent_class;
+};
+
+GType          e_spell_dialog_get_type         (void);
+GtkWidget *    e_spell_dialog_new              (GtkWindow *parent);
+void           e_spell_dialog_close            (ESpellDialog *dialog);
+const gchar *  e_spell_dialog_get_word         (ESpellDialog *dialog);
+void           e_spell_dialog_set_word         (ESpellDialog *dialog,
+                                                const gchar *word);
+void           e_spell_dialog_next_word        (ESpellDialog *dialog);
+void           e_spell_dialog_prev_word        (ESpellDialog *dialog);
+GList *                e_spell_dialog_get_dictionaries
+                                               (ESpellDialog *dialog);
+void           e_spell_dialog_set_dictionaries
+                                               (ESpellDialog *dialog,
+                                                GList *dictionaries);
+ESpellDictionary *
+               e_spell_dialog_get_active_dictionary
+                                               (ESpellDialog *dialog);
+gchar *                e_spell_dialog_get_active_suggestion
+                                               (ESpellDialog *dialog);
+
+G_END_DECLS
+
+#endif /* E_SPELL_DIALOG_H */
diff --git a/e-util/e-spell-dictionary.c b/e-util/e-spell-dictionary.c
new file mode 100644
index 0000000..d170c62
--- /dev/null
+++ b/e-util/e-spell-dictionary.c
@@ -0,0 +1,472 @@
+/* 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 <string.h>
+#include <enchant.h>
+#include <glib/gi18n-lib.h>
+
+#define ISO_639_DOMAIN "iso_639"
+#define ISO_3166_DOMAIN        "iso_3166"
+
+struct _ESpellDictionary {
+       gchar *code;
+       gchar *name;
+       gchar *ckey;
+};
+
+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 (strcmp (element_name, "iso_639_entry") != 0)
+               return;
+
+       for (ii = 0; attribute_names[ii] != NULL; ii++) {
+               if (strcmp (attribute_names[ii], "name") == 0)
+                       name = attribute_values[ii];
+               else if (strcmp (attribute_names[ii], "iso_639_1_code") == 0)
+                       iso_639_1_code = attribute_values[ii];
+               else if (strcmp (attribute_names[ii], "iso_639_2T_code") == 0)
+                       iso_639_2_code = attribute_values[ii];
+       }
+
+       code = (iso_639_1_code != NULL) ? iso_639_1_code : iso_639_2_code;
+
+       if (code != NULL && *code != '\0' && name != NULL && *name != '\0')
+               g_hash_table_insert (
+                       hash_table, g_strdup (code),
+                       g_strdup (dgettext (ISO_639_DOMAIN, name)));
+}
+
+static void
+iso_3166_start_element (GMarkupParseContext *context,
+                        const gchar *element_name,
+                        const gchar **attribute_names,
+                        const gchar **attribute_values,
+                        gpointer data,
+                        GError **error)
+{
+       GHashTable *hash_table = data;
+       const gchar *name = NULL;
+       const gchar *code = NULL;
+       gint ii;
+
+       if (strcmp (element_name, "iso_3166_entry") != 0)
+               return;
+
+       for (ii = 0; attribute_names[ii] != NULL; ii++) {
+               if (strcmp (attribute_names[ii], "name") == 0)
+                       name = attribute_values[ii];
+               else if (strcmp (attribute_names[ii], "alpha_2_code") == 0)
+                       code = attribute_values[ii];
+       }
+
+       if (code != NULL && *code != '\0' && name != NULL && *name != '\0')
+               g_hash_table_insert (
+                       hash_table, g_ascii_strdown (code, -1),
+                       g_strdup (dgettext (ISO_3166_DOMAIN, name)));
+}
+
+static GMarkupParser iso_639_parser = {
+       iso_639_start_element,
+       NULL, NULL, NULL, NULL
+};
+
+static GMarkupParser iso_3166_parser = {
+       iso_3166_start_element,
+       NULL, NULL, NULL, NULL
+};
+
+static void
+iso_codes_parse (const GMarkupParser *parser,
+                 const gchar *basename,
+                 GHashTable *hash_table)
+{
+       GMappedFile *mapped_file;
+       gchar *filename;
+       GError *error = NULL;
+
+       filename = g_build_filename (
+               ISO_CODES_PREFIX, "share", "xml",
+               "iso-codes", basename, NULL);
+       mapped_file = g_mapped_file_new (filename, FALSE, &error);
+       g_free (filename);
+
+       if (mapped_file != NULL) {
+               GMarkupParseContext *context;
+               const gchar *contents;
+               gsize length;
+
+               context = g_markup_parse_context_new (
+                       parser, 0, hash_table, NULL);
+               contents = g_mapped_file_get_contents (mapped_file);
+               length = g_mapped_file_get_length (mapped_file);
+               g_markup_parse_context_parse (
+                       context, contents, length, &error);
+               g_markup_parse_context_free (context);
+#if GLIB_CHECK_VERSION(2,21,3)
+               g_mapped_file_unref (mapped_file);
+#else
+               g_mapped_file_free (mapped_file);
+#endif
+       }
+
+       if (error != NULL) {
+               g_warning ("%s: %s", basename, error->message);
+               g_error_free (error);
+       }
+}
+
+#endif /* HAVE_ISO_CODES */
+
+static void
+spell_dictionary_describe_cb (const gchar * const language_code,
+                             const gchar * const provider_name,
+                             const gchar * const provider_desc,
+                             const gchar * const provider_file,
+                             GTree *tree)
+{
+       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_code, -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_code);
+               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);
+
+       g_tree_replace (tree, g_strdup (language_code), language_name);
+}
+
+static const ESpellDictionary *
+spell_dictionary_copy (const ESpellDictionary *language)
+{
+       return language;
+}
+
+static void
+spell_dictionary_free (const ESpellDictionary *language)
+{
+       /* do nothing */
+}
+
+static const ESpellDictionary *
+spell_dictionary_lookup (const gchar *language_code)
+{
+       const ESpellDictionary *closest_match = NULL;
+       const GList *available_dicts;
+
+       available_dicts = e_spell_dictionary_get_available ();
+
+       while (available_dicts != NULL && language_code != NULL) {
+               ESpellDictionary *dict = available_dicts->data;
+               const gchar *code = dict->code;
+               gsize length = strlen (code);
+
+               if (g_ascii_strcasecmp (language_code, code) == 0)
+                       return dict;
+
+               if (g_ascii_strncasecmp (language_code, code, length) == 0)
+                       closest_match = dict;
+
+               available_dicts = g_list_next (available_dicts);
+       }
+
+       return closest_match;
+}
+
+static gboolean
+spell_dictionary_traverse_cb (const gchar *code,
+                            const gchar *name,
+                            GList **available_languages)
+{
+       ESpellDictionary *dict;
+
+       dict = g_slice_new (ESpellDictionary);
+       dict->code = g_strdup (code);
+       dict->name = g_strdup (name);
+       dict->ckey = g_utf8_collate_key (name, -1);
+
+       *available_languages = g_list_insert_sorted (
+               *available_languages, dict,
+               (GCompareFunc) e_spell_dictionary_compare);
+
+       return FALSE;
+}
+
+GType
+e_spell_dictionary_get_type (void)
+{
+       static GType type = 0;
+
+       if (G_UNLIKELY (type == 0))
+               type = g_boxed_type_register_static (
+                       "ESpellDictionary",
+                       (GBoxedCopyFunc) spell_dictionary_copy,
+                       (GBoxedFreeFunc) spell_dictionary_free);
+
+       return type;
+}
+
+const GList *
+e_spell_dictionary_get_available (void)
+{
+       static gboolean initialized = FALSE;
+       static GList *available_dicts = NULL;
+       EnchantBroker *broker;
+       GTree *tree;
+
+       if (initialized)
+               return available_dicts;
+
+       initialized = TRUE;
+
+#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
+
+       tree = g_tree_new_full (
+               (GCompareDataFunc) strcmp, NULL,
+               (GDestroyNotify) g_free,
+               (GDestroyNotify) g_free);
+
+       broker = enchant_broker_init ();
+       enchant_broker_list_dicts (
+               broker, (EnchantDictDescribeFn)
+               spell_dictionary_describe_cb, tree);
+       enchant_broker_free (broker);
+
+       g_tree_foreach (
+               tree, (GTraverseFunc)
+               spell_dictionary_traverse_cb,
+               &available_dicts);
+
+       g_tree_destroy (tree);
+
+       return available_dicts;
+}
+
+static const ESpellDictionary *
+spell_dictionary_pick_default (void)
+{
+       const ESpellDictionary *dictionary = NULL;
+       const gchar * const *language_names;
+       const GList *available_dicts;
+       gint ii;
+
+       language_names = g_get_language_names ();
+       available_dicts = e_spell_dictionary_get_available ();
+
+       for (ii = 0; dictionary == NULL && language_names[ii] != NULL; ii++)
+               dictionary = spell_dictionary_lookup (language_names[ii]);
+
+       if (dictionary == NULL)
+               dictionary = spell_dictionary_lookup ("en_US");
+
+       if (dictionary == NULL && available_dicts != NULL)
+               dictionary = available_dicts->data;
+
+       return dictionary;
+}
+
+const ESpellDictionary *
+e_spell_dictionary_lookup (const gchar *language_code)
+{
+       const ESpellDictionary *dictionary = NULL;
+
+       dictionary = spell_dictionary_lookup (language_code);
+
+       if (dictionary == NULL)
+               dictionary = spell_dictionary_pick_default ();
+
+       return dictionary;
+}
+
+const gchar *
+e_spell_dictionary_get_language_code (const ESpellDictionary *dictionary)
+{
+       g_return_val_if_fail (dictionary != NULL, NULL);
+
+       return dictionary->code;
+}
+
+const gchar *
+e_spell_dictionary_get_name (const ESpellDictionary *dictionary)
+{
+       if (dictionary == NULL)
+                /* Translators: This refers to the default language used
+                * by the spell checker. */
+               return C_("language", "Default");
+
+       return dictionary->name;
+}
+
+gint
+e_spell_dictionary_compare (const ESpellDictionary *dict_a,
+                           const ESpellDictionary *dict_b)
+{
+       return strcmp (dict_a->ckey, dict_b->ckey);
+}
diff --git a/e-util/e-spell-dictionary.h b/e-util/e-spell-dictionary.h
new file mode 100644
index 0000000..9c3b15e
--- /dev/null
+++ b/e-util/e-spell-dictionary.h
@@ -0,0 +1,53 @@
+/* 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.
+ */
+
+/* Based on Marco Barisione's GSpellLanguage. */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_SPELL_DICTIONARY_H
+#define E_SPELL_DICTIONARY_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ESpellDictionary ESpellDictionary;
+
+#define E_TYPE_SPELL_DICTIONARY \
+       (e_spell_dictionary_get_type ())
+
+
+GType          e_spell_dictionary_get_type             (void);
+const GList *  e_spell_dictionary_get_available        (void);
+
+const ESpellDictionary *
+               e_spell_dictionary_lookup
+                                       (const gchar *language_code);
+const gchar *  e_spell_dictionary_get_language_code
+                                       (const ESpellDictionary *dictionary);
+const gchar *  e_spell_dictionary_get_name
+                                       (const ESpellDictionary *dictionary);
+gint           e_spell_dictionary_compare
+                                       (const ESpellDictionary *dict_a,
+                                        const ESpellDictionary *dict_b);
+
+G_END_DECLS
+
+#endif /* E_SPELL_DICTIONARY_H */
diff --git a/e-util/e-util.h b/e-util/e-util.h
index e5ea0ee..e1eb29c 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -169,6 +169,8 @@
 #include <e-util/e-source-selector-dialog.h>
 #include <e-util/e-source-selector.h>
 #include <e-util/e-source-util.h>
+#include <e-util/e-spell-dialog.h>
+#include <e-util/e-spell-dictionary.h>
 #include <e-util/e-spell-entry.h>
 #include <e-util/e-spell-text-view.h>
 #include <e-util/e-stock-request.h>



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