summaryrefslogtreecommitdiff
path: root/plugins/spell
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/spell')
-rwxr-xr-xplugins/spell/Makefile.am63
-rwxr-xr-xplugins/spell/gedit-automatic-spell-checker.c1015
-rwxr-xr-xplugins/spell/gedit-automatic-spell-checker.h67
-rwxr-xr-xplugins/spell/gedit-spell-checker-dialog.c722
-rwxr-xr-xplugins/spell/gedit-spell-checker-dialog.h92
-rwxr-xr-xplugins/spell/gedit-spell-checker-language.c439
-rwxr-xr-xplugins/spell/gedit-spell-checker-language.h51
-rwxr-xr-xplugins/spell/gedit-spell-checker.c520
-rwxr-xr-xplugins/spell/gedit-spell-checker.h109
-rwxr-xr-xplugins/spell/gedit-spell-language-dialog.c309
-rwxr-xr-xplugins/spell/gedit-spell-language-dialog.h67
-rwxr-xr-xplugins/spell/gedit-spell-marshal.list6
-rwxr-xr-xplugins/spell/gedit-spell-plugin.c1217
-rwxr-xr-xplugins/spell/gedit-spell-plugin.h75
-rwxr-xr-xplugins/spell/gedit-spell-utils.c94
-rwxr-xr-xplugins/spell/gedit-spell-utils.h37
-rwxr-xr-xplugins/spell/languages-dialog.ui145
-rwxr-xr-xplugins/spell/spell-checker.ui482
-rwxr-xr-xplugins/spell/spell.gedit-plugin.desktop.in9
19 files changed, 5519 insertions, 0 deletions
diff --git a/plugins/spell/Makefile.am b/plugins/spell/Makefile.am
new file mode 100755
index 00000000..9d332f95
--- /dev/null
+++ b/plugins/spell/Makefile.am
@@ -0,0 +1,63 @@
+# Spell checker plugin
+plugindir = $(GEDIT_PLUGINS_LIBS_DIR)
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ $(GEDIT_CFLAGS) \
+ $(ENCHANT_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(DISABLE_DEPRECATED_CFLAGS)
+
+BUILT_SOURCES = \
+ gedit-spell-marshal.c \
+ gedit-spell-marshal.h
+
+plugin_LTLIBRARIES = libspell.la
+
+libspell_la_SOURCES = \
+ gedit-spell-plugin.c \
+ gedit-spell-plugin.h \
+ gedit-spell-checker.c \
+ gedit-spell-checker.h \
+ gedit-spell-checker-dialog.c \
+ gedit-spell-checker-dialog.h \
+ gedit-spell-checker-language.c \
+ gedit-spell-checker-language.h \
+ gedit-spell-language-dialog.c \
+ gedit-spell-language-dialog.h \
+ gedit-automatic-spell-checker.c \
+ gedit-automatic-spell-checker.h \
+ gedit-spell-utils.c \
+ gedit-spell-utils.h \
+ $(BUILT_SOURCES)
+
+libspell_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+libspell_la_LIBADD = $(GEDIT_LIBS) $(ENCHANT_LIBS)
+
+uidir = $(GEDIT_PLUGINS_DATA_DIR)/spell
+ui_DATA = spell-checker.ui languages-dialog.ui
+
+gedit-spell-marshal.h: gedit-spell-marshal.list $(GLIB_GENMARSHAL)
+ $(AM_V_GEN) $(GLIB_GENMARSHAL) $< --header --prefix=gedit_marshal > $@
+
+gedit-spell-marshal.c: gedit-spell-marshal.list $(GLIB_GENMARSHAL)
+ $(AM_V_GEN) echo "#include \"gedit-spell-marshal.h\"" > $@ && \
+ $(GLIB_GENMARSHAL) $< --body --prefix=gedit_marshal >> $@
+
+plugin_in_files = spell.gedit-plugin.desktop.in
+
+%.gedit-plugin: %.gedit-plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+plugin_DATA = $(plugin_in_files:.gedit-plugin.desktop.in=.gedit-plugin)
+
+EXTRA_DIST = \
+ $(ui_DATA) \
+ $(plugin_in_files) \
+ gedit-spell-marshal.list
+
+CLEANFILES = $(BUILT_SOURCES) $(plugin_DATA)
+
+dist-hook:
+ cd $(distdir); rm -f $(BUILT_SOURCES)
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/spell/gedit-automatic-spell-checker.c b/plugins/spell/gedit-automatic-spell-checker.c
new file mode 100755
index 00000000..96b2fae9
--- /dev/null
+++ b/plugins/spell/gedit-automatic-spell-checker.c
@@ -0,0 +1,1015 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gedit-automatic-spell-checker.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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.
+ */
+
+/*
+ * Modified by the gedit Team, 2002. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ */
+
+/* This is a modified version of gtkspell 2.0.5 (gtkspell.sf.net) */
+/* gtkspell - a spell-checking addon for GTK's TextView widget
+ * Copyright (c) 2002 Evan Martin.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+#include "gedit-automatic-spell-checker.h"
+#include "gedit-spell-utils.h"
+
+struct _GeditAutomaticSpellChecker {
+ GeditDocument *doc;
+ GSList *views;
+
+ GtkTextMark *mark_insert_start;
+ GtkTextMark *mark_insert_end;
+ gboolean deferred_check;
+
+ GtkTextTag *tag_highlight;
+ GtkTextMark *mark_click;
+
+ GeditSpellChecker *spell_checker;
+};
+
+static GQuark automatic_spell_checker_id = 0;
+static GQuark suggestion_id = 0;
+
+static void gedit_automatic_spell_checker_free_internal (GeditAutomaticSpellChecker *spell);
+
+static void
+view_destroy (GeditView *view, GeditAutomaticSpellChecker *spell)
+{
+ gedit_automatic_spell_checker_detach_view (spell, view);
+}
+
+static void
+check_word (GeditAutomaticSpellChecker *spell, GtkTextIter *start, GtkTextIter *end)
+{
+ gchar *word;
+
+ word = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (spell->doc), start, end, FALSE);
+
+ /*
+ g_print ("Check word: %s [%d - %d]\n", word, gtk_text_iter_get_offset (start),
+ gtk_text_iter_get_offset (end));
+ */
+
+ if (!gedit_spell_checker_check_word (spell->spell_checker, word, -1))
+ {
+ /*
+ g_print ("Apply tag: [%d - %d]\n", gtk_text_iter_get_offset (start),
+ gtk_text_iter_get_offset (end));
+ */
+ gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (spell->doc),
+ spell->tag_highlight,
+ start,
+ end);
+ }
+
+ g_free (word);
+}
+
+static void
+check_range (GeditAutomaticSpellChecker *spell,
+ GtkTextIter start,
+ GtkTextIter end,
+ gboolean force_all)
+{
+ /* we need to "split" on word boundaries.
+ * luckily, Pango knows what "words" are
+ * so we don't have to figure it out. */
+
+ GtkTextIter wstart;
+ GtkTextIter wend;
+ GtkTextIter cursor;
+ GtkTextIter precursor;
+ gboolean highlight;
+
+ /*
+ g_print ("Check range: [%d - %d]\n", gtk_text_iter_get_offset (&start),
+ gtk_text_iter_get_offset (&end));
+ */
+
+ if (gtk_text_iter_inside_word (&end))
+ gtk_text_iter_forward_word_end (&end);
+
+ if (!gtk_text_iter_starts_word (&start))
+ {
+ if (gtk_text_iter_inside_word (&start) ||
+ gtk_text_iter_ends_word (&start))
+ {
+ gtk_text_iter_backward_word_start (&start);
+ }
+ else
+ {
+ /* if we're neither at the beginning nor inside a word,
+ * me must be in some spaces.
+ * skip forward to the beginning of the next word. */
+
+ if (gtk_text_iter_forward_word_end (&start))
+ gtk_text_iter_backward_word_start (&start);
+ }
+ }
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (spell->doc),
+ &cursor,
+ gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (spell->doc)));
+
+ precursor = cursor;
+ gtk_text_iter_backward_char (&precursor);
+
+ highlight = gtk_text_iter_has_tag (&cursor, spell->tag_highlight) ||
+ gtk_text_iter_has_tag (&precursor, spell->tag_highlight);
+
+ gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (spell->doc),
+ spell->tag_highlight,
+ &start,
+ &end);
+
+ /* Fix a corner case when replacement occurs at beginning of buffer:
+ * An iter at offset 0 seems to always be inside a word,
+ * even if it's not. Possibly a pango bug.
+ */
+ if (gtk_text_iter_get_offset (&start) == 0)
+ {
+ gtk_text_iter_forward_word_end(&start);
+ gtk_text_iter_backward_word_start(&start);
+ }
+
+ wstart = start;
+
+ while (gedit_spell_utils_skip_no_spell_check (&wstart, &end) &&
+ gtk_text_iter_compare (&wstart, &end) < 0)
+ {
+ gboolean inword;
+
+ /* move wend to the end of the current word. */
+ wend = wstart;
+
+ gtk_text_iter_forward_word_end (&wend);
+
+ inword = (gtk_text_iter_compare (&wstart, &cursor) < 0) &&
+ (gtk_text_iter_compare (&cursor, &wend) <= 0);
+
+ if (inword && !force_all)
+ {
+ /* this word is being actively edited,
+ * only check if it's already highligted,
+ * otherwise defer this check until later. */
+ if (highlight)
+ check_word (spell, &wstart, &wend);
+ else
+ spell->deferred_check = TRUE;
+ }
+ else
+ {
+ check_word (spell, &wstart, &wend);
+ spell->deferred_check = FALSE;
+ }
+
+ /* now move wend to the beginning of the next word, */
+ gtk_text_iter_forward_word_end (&wend);
+ gtk_text_iter_backward_word_start (&wend);
+
+ /* make sure we've actually advanced
+ * (we don't advance in some corner cases), */
+ if (gtk_text_iter_equal (&wstart, &wend))
+ break; /* we're done in these cases.. */
+
+ /* and then pick this as the new next word beginning. */
+ wstart = wend;
+ }
+}
+
+static void
+check_deferred_range (GeditAutomaticSpellChecker *spell,
+ gboolean force_all)
+{
+ GtkTextIter start, end;
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (spell->doc),
+ &start,
+ spell->mark_insert_start);
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (spell->doc),
+ &end,
+ spell->mark_insert_end);
+
+ check_range (spell, start, end, force_all);
+}
+
+/* insertion works like this:
+ * - before the text is inserted, we mark the position in the buffer.
+ * - after the text is inserted, we see where our mark is and use that and
+ * the current position to check the entire range of inserted text.
+ *
+ * this may be overkill for the common case (inserting one character). */
+
+static void
+insert_text_before (GtkTextBuffer *buffer, GtkTextIter *iter,
+ gchar *text, gint len, GeditAutomaticSpellChecker *spell)
+{
+ gtk_text_buffer_move_mark (buffer, spell->mark_insert_start, iter);
+}
+
+static void
+insert_text_after (GtkTextBuffer *buffer, GtkTextIter *iter,
+ gchar *text, gint len, GeditAutomaticSpellChecker *spell)
+{
+ GtkTextIter start;
+
+ /* we need to check a range of text. */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start, spell->mark_insert_start);
+
+ check_range (spell, start, *iter, FALSE);
+
+ gtk_text_buffer_move_mark (buffer, spell->mark_insert_end, iter);
+}
+
+/* deleting is more simple: we're given the range of deleted text.
+ * after deletion, the start and end iters should be at the same position
+ * (because all of the text between them was deleted!).
+ * this means we only really check the words immediately bounding the
+ * deletion.
+ */
+
+static void
+delete_range_after (GtkTextBuffer *buffer, GtkTextIter *start, GtkTextIter *end,
+ GeditAutomaticSpellChecker *spell)
+{
+ check_range (spell, *start, *end, FALSE);
+}
+
+static void
+mark_set (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ GtkTextMark *mark,
+ GeditAutomaticSpellChecker *spell)
+{
+ /* if the cursor has moved and there is a deferred check so handle it now */
+ if ((mark == gtk_text_buffer_get_insert (buffer)) && spell->deferred_check)
+ check_deferred_range (spell, FALSE);
+}
+
+static void
+get_word_extents_from_mark (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end,
+ GtkTextMark *mark)
+{
+ gtk_text_buffer_get_iter_at_mark(buffer, start, mark);
+
+ if (!gtk_text_iter_starts_word (start))
+ gtk_text_iter_backward_word_start (start);
+
+ *end = *start;
+
+ if (gtk_text_iter_inside_word (end))
+ gtk_text_iter_forward_word_end (end);
+}
+
+static void
+remove_tag_to_word (GeditAutomaticSpellChecker *spell, const gchar *word)
+{
+ GtkTextIter iter;
+ GtkTextIter match_start, match_end;
+
+ gboolean found;
+
+ gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (spell->doc), &iter, 0);
+
+ found = TRUE;
+
+ while (found)
+ {
+ found = gtk_text_iter_forward_search (&iter,
+ word,
+ GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY,
+ &match_start,
+ &match_end,
+ NULL);
+
+ if (found)
+ {
+ if (gtk_text_iter_starts_word (&match_start) &&
+ gtk_text_iter_ends_word (&match_end))
+ {
+ gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (spell->doc),
+ spell->tag_highlight,
+ &match_start,
+ &match_end);
+ }
+
+ iter = match_end;
+ }
+ }
+}
+
+static void
+add_to_dictionary (GtkWidget *menuitem, GeditAutomaticSpellChecker *spell)
+{
+ gchar *word;
+
+ GtkTextIter start, end;
+
+ get_word_extents_from_mark (GTK_TEXT_BUFFER (spell->doc), &start, &end, spell->mark_click);
+
+ word = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (spell->doc),
+ &start,
+ &end,
+ FALSE);
+
+ gedit_spell_checker_add_word_to_personal (spell->spell_checker, word, -1);
+
+ g_free (word);
+}
+
+static void
+ignore_all (GtkWidget *menuitem, GeditAutomaticSpellChecker *spell)
+{
+ gchar *word;
+
+ GtkTextIter start, end;
+
+ get_word_extents_from_mark (GTK_TEXT_BUFFER (spell->doc), &start, &end, spell->mark_click);
+
+ word = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (spell->doc),
+ &start,
+ &end,
+ FALSE);
+
+ gedit_spell_checker_add_word_to_session (spell->spell_checker, word, -1);
+
+ g_free (word);
+}
+
+static void
+replace_word (GtkWidget *menuitem, GeditAutomaticSpellChecker *spell)
+{
+ gchar *oldword;
+ const gchar *newword;
+
+ GtkTextIter start, end;
+
+ get_word_extents_from_mark (GTK_TEXT_BUFFER (spell->doc), &start, &end, spell->mark_click);
+
+ oldword = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (spell->doc), &start, &end, FALSE);
+
+ newword = g_object_get_qdata (G_OBJECT (menuitem), suggestion_id);
+ g_return_if_fail (newword != NULL);
+
+ gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (spell->doc));
+
+ gtk_text_buffer_delete (GTK_TEXT_BUFFER (spell->doc), &start, &end);
+ gtk_text_buffer_insert (GTK_TEXT_BUFFER (spell->doc), &start, newword, -1);
+
+ gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (spell->doc));
+
+ gedit_spell_checker_set_correction (spell->spell_checker,
+ oldword, strlen (oldword),
+ newword, strlen (newword));
+
+ g_free (oldword);
+}
+
+static GtkWidget *
+build_suggestion_menu (GeditAutomaticSpellChecker *spell, const gchar *word)
+{
+ GtkWidget *topmenu, *menu;
+ GtkWidget *mi;
+ GSList *suggestions;
+ GSList *list;
+ gchar *label_text;
+
+ topmenu = menu = gtk_menu_new();
+
+ suggestions = gedit_spell_checker_get_suggestions (spell->spell_checker, word, -1);
+
+ list = suggestions;
+
+ if (suggestions == NULL)
+ {
+ /* no suggestions. put something in the menu anyway... */
+ GtkWidget *label;
+ /* Translators: Displayed in the "Check Spelling" dialog if there are no suggestions for the current misspelled word */
+ label = gtk_label_new (_("(no suggested words)"));
+
+ mi = gtk_menu_item_new ();
+ gtk_widget_set_sensitive (mi, FALSE);
+ gtk_container_add (GTK_CONTAINER(mi), label);
+ gtk_widget_show_all (mi);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), mi);
+ }
+ else
+ {
+ gint count = 0;
+
+ /* build a set of menus with suggestions. */
+ while (suggestions != NULL)
+ {
+ GtkWidget *label;
+
+ if (count == 10)
+ {
+ /* Separator */
+ mi = gtk_menu_item_new ();
+ gtk_widget_show (mi);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
+
+ mi = gtk_menu_item_new_with_mnemonic (_("_More..."));
+ gtk_widget_show (mi);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
+
+ menu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), menu);
+ count = 0;
+ }
+
+ label_text = g_strdup_printf ("<b>%s</b>", (gchar*) suggestions->data);
+
+ label = gtk_label_new (label_text);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+ mi = gtk_menu_item_new ();
+ gtk_container_add (GTK_CONTAINER(mi), label);
+
+ gtk_widget_show_all (mi);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
+
+ g_object_set_qdata_full (G_OBJECT (mi),
+ suggestion_id,
+ g_strdup (suggestions->data),
+ (GDestroyNotify)g_free);
+
+ g_free (label_text);
+ g_signal_connect (mi,
+ "activate",
+ G_CALLBACK (replace_word),
+ spell);
+
+ count++;
+
+ suggestions = g_slist_next (suggestions);
+ }
+ }
+
+ /* free the suggestion list */
+ suggestions = list;
+
+ while (list)
+ {
+ g_free (list->data);
+ list = g_slist_next (list);
+ }
+
+ g_slist_free (suggestions);
+
+ /* Separator */
+ mi = gtk_menu_item_new ();
+ gtk_widget_show (mi);
+ gtk_menu_shell_append (GTK_MENU_SHELL (topmenu), mi);
+
+ /* Ignore all */
+ mi = gtk_image_menu_item_new_with_mnemonic (_("_Ignore All"));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi),
+ gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM,
+ GTK_ICON_SIZE_MENU));
+
+ g_signal_connect (mi,
+ "activate",
+ G_CALLBACK(ignore_all),
+ spell);
+
+ gtk_widget_show_all (mi);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (topmenu), mi);
+
+ /* + Add to Dictionary */
+ mi = gtk_image_menu_item_new_with_mnemonic (_("_Add"));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi),
+ gtk_image_new_from_stock (GTK_STOCK_ADD,
+ GTK_ICON_SIZE_MENU));
+
+ g_signal_connect (mi,
+ "activate",
+ G_CALLBACK (add_to_dictionary),
+ spell);
+
+ gtk_widget_show_all (mi);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (topmenu), mi);
+
+ return topmenu;
+}
+
+static void
+populate_popup (GtkTextView *textview, GtkMenu *menu, GeditAutomaticSpellChecker *spell)
+{
+ GtkWidget *img, *mi;
+ GtkTextIter start, end;
+ char *word;
+
+ /* we need to figure out if they picked a misspelled word. */
+ get_word_extents_from_mark (GTK_TEXT_BUFFER (spell->doc), &start, &end, spell->mark_click);
+
+ /* if our highlight algorithm ever messes up,
+ * this isn't correct, either. */
+ if (!gtk_text_iter_has_tag (&start, spell->tag_highlight))
+ return; /* word wasn't misspelled. */
+
+ /* menu separator comes first. */
+ mi = gtk_menu_item_new ();
+ gtk_widget_show (mi);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), mi);
+
+ /* then, on top of it, the suggestions menu. */
+ img = gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU);
+ mi = gtk_image_menu_item_new_with_mnemonic (_("_Spelling Suggestions..."));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), img);
+
+ word = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (spell->doc), &start, &end, FALSE);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi),
+ build_suggestion_menu (spell, word));
+ g_free(word);
+
+ gtk_widget_show_all (mi);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), mi);
+}
+
+void
+gedit_automatic_spell_checker_recheck_all (GeditAutomaticSpellChecker *spell)
+{
+ GtkTextIter start, end;
+
+ g_return_if_fail (spell != NULL);
+
+ gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (spell->doc), &start, &end);
+
+ check_range (spell, start, end, TRUE);
+}
+
+static void
+add_word_signal_cb (GeditSpellChecker *checker,
+ const gchar *word,
+ gint len,
+ GeditAutomaticSpellChecker *spell)
+{
+ gchar *w;
+
+ if (len < 0)
+ w = g_strdup (word);
+ else
+ w = g_strndup (word, len);
+
+ remove_tag_to_word (spell, w);
+
+ g_free (w);
+}
+
+static void
+set_language_cb (GeditSpellChecker *checker,
+ const GeditSpellCheckerLanguage *lang,
+ GeditAutomaticSpellChecker *spell)
+{
+ gedit_automatic_spell_checker_recheck_all (spell);
+}
+
+static void
+clear_session_cb (GeditSpellChecker *checker,
+ GeditAutomaticSpellChecker *spell)
+{
+ gedit_automatic_spell_checker_recheck_all (spell);
+}
+
+/* When the user right-clicks on a word, they want to check that word.
+ * Here, we do NOT move the cursor to the location of the clicked-upon word
+ * since that prevents the use of edit functions on the context menu.
+ */
+static gboolean
+button_press_event (GtkTextView *view,
+ GdkEventButton *event,
+ GeditAutomaticSpellChecker *spell)
+{
+ if (event->button == 3)
+ {
+ gint x, y;
+ GtkTextIter iter;
+
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer (view);
+
+ /* handle deferred check if it exists */
+ if (spell->deferred_check)
+ check_deferred_range (spell, TRUE);
+
+ gtk_text_view_window_to_buffer_coords (view,
+ GTK_TEXT_WINDOW_TEXT,
+ event->x, event->y,
+ &x, &y);
+
+ gtk_text_view_get_iter_at_location (view, &iter, x, y);
+
+ gtk_text_buffer_move_mark (buffer, spell->mark_click, &iter);
+ }
+
+ return FALSE; /* false: let gtk process this event, too.
+ we don't want to eat any events. */
+}
+
+/* Move the insert mark before popping up the menu, otherwise it
+ * will contain the wrong set of suggestions.
+ */
+static gboolean
+popup_menu_event (GtkTextView *view, GeditAutomaticSpellChecker *spell)
+{
+ GtkTextIter iter;
+ GtkTextBuffer *buffer;
+
+ buffer = gtk_text_view_get_buffer (view);
+
+ /* handle deferred check if it exists */
+ if (spell->deferred_check)
+ check_deferred_range (spell, TRUE);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter,
+ gtk_text_buffer_get_insert (buffer));
+ gtk_text_buffer_move_mark (buffer, spell->mark_click, &iter);
+
+ return FALSE;
+}
+
+static void
+tag_table_changed (GtkTextTagTable *table,
+ GeditAutomaticSpellChecker *spell)
+{
+ g_return_if_fail (spell->tag_highlight != NULL);
+
+ gtk_text_tag_set_priority (spell->tag_highlight,
+ gtk_text_tag_table_get_size (table) - 1);
+}
+
+static void
+tag_added_or_removed (GtkTextTagTable *table,
+ GtkTextTag *tag,
+ GeditAutomaticSpellChecker *spell)
+{
+ tag_table_changed (table, spell);
+}
+
+static void
+tag_changed (GtkTextTagTable *table,
+ GtkTextTag *tag,
+ gboolean size_changed,
+ GeditAutomaticSpellChecker *spell)
+{
+ tag_table_changed (table, spell);
+}
+
+static void
+highlight_updated (GtkSourceBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end,
+ GeditAutomaticSpellChecker *spell)
+{
+ check_range (spell, *start, *end, FALSE);
+}
+
+static void
+spell_tag_destroyed (GeditAutomaticSpellChecker *spell,
+ GObject *where_the_object_was)
+{
+ spell->tag_highlight = NULL;
+}
+
+GeditAutomaticSpellChecker *
+gedit_automatic_spell_checker_new (GeditDocument *doc,
+ GeditSpellChecker *checker)
+{
+ GeditAutomaticSpellChecker *spell;
+ GtkTextTagTable *tag_table;
+ GtkTextIter start, end;
+
+ g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL);
+ g_return_val_if_fail (GEDIT_IS_SPELL_CHECKER (checker), NULL);
+ g_return_val_if_fail ((spell = gedit_automatic_spell_checker_get_from_document (doc)) == NULL,
+ spell);
+
+ /* attach to the widget */
+ spell = g_new0 (GeditAutomaticSpellChecker, 1);
+
+ spell->doc = doc;
+ spell->spell_checker = g_object_ref (checker);
+
+ if (automatic_spell_checker_id == 0)
+ {
+ automatic_spell_checker_id =
+ g_quark_from_string ("GeditAutomaticSpellCheckerID");
+ }
+ if (suggestion_id == 0)
+ {
+ suggestion_id = g_quark_from_string ("GeditAutoSuggestionID");
+ }
+
+ g_object_set_qdata_full (G_OBJECT (doc),
+ automatic_spell_checker_id,
+ spell,
+ (GDestroyNotify)gedit_automatic_spell_checker_free_internal);
+
+ g_signal_connect (doc,
+ "insert-text",
+ G_CALLBACK (insert_text_before),
+ spell);
+ g_signal_connect_after (doc,
+ "insert-text",
+ G_CALLBACK (insert_text_after),
+ spell);
+ g_signal_connect_after (doc,
+ "delete-range",
+ G_CALLBACK (delete_range_after),
+ spell);
+ g_signal_connect (doc,
+ "mark-set",
+ G_CALLBACK (mark_set),
+ spell);
+
+ g_signal_connect (doc,
+ "highlight-updated",
+ G_CALLBACK (highlight_updated),
+ spell);
+
+ g_signal_connect (spell->spell_checker,
+ "add_word_to_session",
+ G_CALLBACK (add_word_signal_cb),
+ spell);
+ g_signal_connect (spell->spell_checker,
+ "add_word_to_personal",
+ G_CALLBACK (add_word_signal_cb),
+ spell);
+ g_signal_connect (spell->spell_checker,
+ "clear_session",
+ G_CALLBACK (clear_session_cb),
+ spell);
+ g_signal_connect (spell->spell_checker,
+ "set_language",
+ G_CALLBACK (set_language_cb),
+ spell);
+
+ spell->tag_highlight = gtk_text_buffer_create_tag (
+ GTK_TEXT_BUFFER (doc),
+ "gtkspell-misspelled",
+ "underline", PANGO_UNDERLINE_ERROR,
+ NULL);
+
+ g_object_weak_ref (G_OBJECT (spell->tag_highlight),
+ (GWeakNotify)spell_tag_destroyed,
+ spell);
+
+ tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (doc));
+
+ gtk_text_tag_set_priority (spell->tag_highlight,
+ gtk_text_tag_table_get_size (tag_table) - 1);
+
+ g_signal_connect (tag_table,
+ "tag-added",
+ G_CALLBACK (tag_added_or_removed),
+ spell);
+ g_signal_connect (tag_table,
+ "tag-removed",
+ G_CALLBACK (tag_added_or_removed),
+ spell);
+ g_signal_connect (tag_table,
+ "tag-changed",
+ G_CALLBACK (tag_changed),
+ spell);
+
+ /* we create the mark here, but we don't use it until text is
+ * inserted, so we don't really care where iter points. */
+ gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc), &start, &end);
+
+ spell->mark_insert_start = gtk_text_buffer_get_mark (GTK_TEXT_BUFFER (doc),
+ "gedit-automatic-spell-checker-insert-start");
+
+ if (spell->mark_insert_start == NULL)
+ {
+ spell->mark_insert_start =
+ gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (doc),
+ "gedit-automatic-spell-checker-insert-start",
+ &start,
+ TRUE);
+ }
+ else
+ {
+ gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
+ spell->mark_insert_start,
+ &start);
+ }
+
+ spell->mark_insert_end = gtk_text_buffer_get_mark (GTK_TEXT_BUFFER (doc),
+ "gedit-automatic-spell-checker-insert-end");
+
+ if (spell->mark_insert_end == NULL)
+ {
+ spell->mark_insert_end =
+ gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (doc),
+ "gedit-automatic-spell-checker-insert-end",
+ &start,
+ TRUE);
+ }
+ else
+ {
+ gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
+ spell->mark_insert_end,
+ &start);
+ }
+
+ spell->mark_click = gtk_text_buffer_get_mark (GTK_TEXT_BUFFER (doc),
+ "gedit-automatic-spell-checker-click");
+
+ if (spell->mark_click == NULL)
+ {
+ spell->mark_click =
+ gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (doc),
+ "gedit-automatic-spell-checker-click",
+ &start,
+ TRUE);
+ }
+ else
+ {
+ gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
+ spell->mark_click,
+ &start);
+ }
+
+ spell->deferred_check = FALSE;
+
+ return spell;
+}
+
+GeditAutomaticSpellChecker *
+gedit_automatic_spell_checker_get_from_document (const GeditDocument *doc)
+{
+ g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL);
+
+ if (automatic_spell_checker_id == 0)
+ return NULL;
+
+ return g_object_get_qdata (G_OBJECT (doc), automatic_spell_checker_id);
+}
+
+void
+gedit_automatic_spell_checker_free (GeditAutomaticSpellChecker *spell)
+{
+ g_return_if_fail (spell != NULL);
+ g_return_if_fail (gedit_automatic_spell_checker_get_from_document (spell->doc) == spell);
+
+ if (automatic_spell_checker_id == 0)
+ return;
+
+ g_object_set_qdata (G_OBJECT (spell->doc), automatic_spell_checker_id, NULL);
+}
+
+static void
+gedit_automatic_spell_checker_free_internal (GeditAutomaticSpellChecker *spell)
+{
+ GtkTextTagTable *table;
+ GtkTextIter start, end;
+ GSList *list;
+
+ g_return_if_fail (spell != NULL);
+
+ table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (spell->doc));
+
+ if (table != NULL && spell->tag_highlight != NULL)
+ {
+ gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (spell->doc),
+ &start,
+ &end);
+ gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (spell->doc),
+ spell->tag_highlight,
+ &start,
+ &end);
+
+ g_signal_handlers_disconnect_matched (G_OBJECT (table),
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL,
+ spell);
+
+ gtk_text_tag_table_remove (table, spell->tag_highlight);
+ }
+
+ g_signal_handlers_disconnect_matched (G_OBJECT (spell->doc),
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL,
+ spell);
+
+ g_signal_handlers_disconnect_matched (G_OBJECT (spell->spell_checker),
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL,
+ spell);
+
+ g_object_unref (spell->spell_checker);
+
+ list = spell->views;
+ while (list != NULL)
+ {
+ GeditView *view = GEDIT_VIEW (list->data);
+
+ g_signal_handlers_disconnect_matched (G_OBJECT (view),
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL,
+ spell);
+
+ g_signal_handlers_disconnect_matched (G_OBJECT (view),
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL,
+ spell);
+
+ list = g_slist_next (list);
+ }
+
+ g_slist_free (spell->views);
+
+ g_free (spell);
+}
+
+void
+gedit_automatic_spell_checker_attach_view (
+ GeditAutomaticSpellChecker *spell,
+ GeditView *view)
+{
+ g_return_if_fail (spell != NULL);
+ g_return_if_fail (GEDIT_IS_VIEW (view));
+
+ g_return_if_fail (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)) ==
+ GTK_TEXT_BUFFER (spell->doc));
+
+ g_signal_connect (view,
+ "button-press-event",
+ G_CALLBACK (button_press_event),
+ spell);
+ g_signal_connect (view,
+ "popup-menu",
+ G_CALLBACK (popup_menu_event),
+ spell);
+ g_signal_connect (view,
+ "populate-popup",
+ G_CALLBACK (populate_popup),
+ spell);
+ g_signal_connect (view,
+ "destroy",
+ G_CALLBACK (view_destroy),
+ spell);
+
+ spell->views = g_slist_prepend (spell->views, view);
+}
+
+void
+gedit_automatic_spell_checker_detach_view (
+ GeditAutomaticSpellChecker *spell,
+ GeditView *view)
+{
+ g_return_if_fail (spell != NULL);
+ g_return_if_fail (GEDIT_IS_VIEW (view));
+
+ g_return_if_fail (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)) ==
+ GTK_TEXT_BUFFER (spell->doc));
+ g_return_if_fail (spell->views != NULL);
+
+ g_signal_handlers_disconnect_matched (G_OBJECT (view),
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL,
+ spell);
+
+ g_signal_handlers_disconnect_matched (G_OBJECT (view),
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL,
+ spell);
+
+ spell->views = g_slist_remove (spell->views, view);
+}
+
diff --git a/plugins/spell/gedit-automatic-spell-checker.h b/plugins/spell/gedit-automatic-spell-checker.h
new file mode 100755
index 00000000..cc634424
--- /dev/null
+++ b/plugins/spell/gedit-automatic-spell-checker.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gedit-automatic-spell-checker.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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.
+ */
+
+/*
+ * Modified by the gedit Team, 2002. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ */
+
+/* This is a modified version of gtkspell 2.0.2 (gtkspell.sf.net) */
+/* gtkspell - a spell-checking addon for GTK's TextView widget
+ * Copyright (c) 2002 Evan Martin.
+ */
+
+#ifndef __GEDIT_AUTOMATIC_SPELL_CHECKER_H__
+#define __GEDIT_AUTOMATIC_SPELL_CHECKER_H__
+
+#include <gedit/gedit-document.h>
+#include <gedit/gedit-view.h>
+
+#include "gedit-spell-checker.h"
+
+typedef struct _GeditAutomaticSpellChecker GeditAutomaticSpellChecker;
+
+GeditAutomaticSpellChecker *gedit_automatic_spell_checker_new (
+ GeditDocument *doc,
+ GeditSpellChecker *checker);
+
+GeditAutomaticSpellChecker *gedit_automatic_spell_checker_get_from_document (
+ const GeditDocument *doc);
+
+void gedit_automatic_spell_checker_free (
+ GeditAutomaticSpellChecker *spell);
+
+void gedit_automatic_spell_checker_attach_view (
+ GeditAutomaticSpellChecker *spell,
+ GeditView *view);
+
+void gedit_automatic_spell_checker_detach_view (
+ GeditAutomaticSpellChecker *spell,
+ GeditView *view);
+
+void gedit_automatic_spell_checker_recheck_all (
+ GeditAutomaticSpellChecker *spell);
+
+#endif /* __GEDIT_AUTOMATIC_SPELL_CHECKER_H__ */
+
diff --git a/plugins/spell/gedit-spell-checker-dialog.c b/plugins/spell/gedit-spell-checker-dialog.c
new file mode 100755
index 00000000..0488d160
--- /dev/null
+++ b/plugins/spell/gedit-spell-checker-dialog.c
@@ -0,0 +1,722 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gedit-spell-checker-dialog.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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.
+ */
+
+/*
+ * Modified by the gedit Team, 2002. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gedit/gedit-utils.h>
+#include "gedit-spell-checker-dialog.h"
+#include "gedit-spell-marshal.h"
+
+struct _GeditSpellCheckerDialog
+{
+ GtkWindow parent_instance;
+
+ GeditSpellChecker *spell_checker;
+
+ gchar *misspelled_word;
+
+ GtkWidget *misspelled_word_label;
+ GtkWidget *word_entry;
+ GtkWidget *check_word_button;
+ GtkWidget *ignore_button;
+ GtkWidget *ignore_all_button;
+ GtkWidget *change_button;
+ GtkWidget *change_all_button;
+ GtkWidget *add_word_button;
+ GtkWidget *close_button;
+ GtkWidget *suggestions_list;
+ GtkWidget *language_label;
+
+ GtkTreeModel *suggestions_list_model;
+};
+
+enum
+{
+ IGNORE,
+ IGNORE_ALL,
+ CHANGE,
+ CHANGE_ALL,
+ ADD_WORD_TO_PERSONAL,
+ LAST_SIGNAL
+};
+
+enum
+{
+ COLUMN_SUGGESTIONS,
+ NUM_COLUMNS
+};
+
+static void update_suggestions_list_model (GeditSpellCheckerDialog *dlg,
+ GSList *suggestions);
+
+static void word_entry_changed_handler (GtkEditable *editable,
+ GeditSpellCheckerDialog *dlg);
+static void close_button_clicked_handler (GtkButton *button,
+ GeditSpellCheckerDialog *dlg);
+static void suggestions_list_selection_changed_handler (GtkTreeSelection *selection,
+ GeditSpellCheckerDialog *dlg);
+static void check_word_button_clicked_handler (GtkButton *button,
+ GeditSpellCheckerDialog *dlg);
+static void add_word_button_clicked_handler (GtkButton *button,
+ GeditSpellCheckerDialog *dlg);
+static void ignore_button_clicked_handler (GtkButton *button,
+ GeditSpellCheckerDialog *dlg);
+static void ignore_all_button_clicked_handler (GtkButton *button,
+ GeditSpellCheckerDialog *dlg);
+static void change_button_clicked_handler (GtkButton *button,
+ GeditSpellCheckerDialog *dlg);
+static void change_all_button_clicked_handler (GtkButton *button,
+ GeditSpellCheckerDialog *dlg);
+static void suggestions_list_row_activated_handler (GtkTreeView *view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ GeditSpellCheckerDialog *dlg);
+
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE(GeditSpellCheckerDialog, gedit_spell_checker_dialog, GTK_TYPE_WINDOW)
+
+static void
+gedit_spell_checker_dialog_destroy (GtkObject *object)
+{
+ GeditSpellCheckerDialog *dlg = GEDIT_SPELL_CHECKER_DIALOG (object);
+
+ if (dlg->spell_checker != NULL)
+ {
+ g_object_unref (dlg->spell_checker);
+ dlg->spell_checker = NULL;
+ }
+
+ if (dlg->misspelled_word != NULL)
+ {
+ g_free (dlg->misspelled_word);
+ dlg->misspelled_word = NULL;
+ }
+
+ GTK_OBJECT_CLASS (gedit_spell_checker_dialog_parent_class)->destroy (object);
+}
+
+static void
+gedit_spell_checker_dialog_class_init (GeditSpellCheckerDialogClass * klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+
+ GTK_OBJECT_CLASS (object_class)->destroy = gedit_spell_checker_dialog_destroy;
+
+ signals[IGNORE] =
+ g_signal_new ("ignore",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditSpellCheckerDialogClass, ignore),
+ NULL, NULL,
+ gedit_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+
+ signals[IGNORE_ALL] =
+ g_signal_new ("ignore_all",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditSpellCheckerDialogClass, ignore_all),
+ NULL, NULL,
+ gedit_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+
+ signals[CHANGE] =
+ g_signal_new ("change",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditSpellCheckerDialogClass, change),
+ NULL, NULL,
+ gedit_marshal_VOID__STRING_STRING,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ signals[CHANGE_ALL] =
+ g_signal_new ("change_all",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditSpellCheckerDialogClass, change_all),
+ NULL, NULL,
+ gedit_marshal_VOID__STRING_STRING,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ signals[ADD_WORD_TO_PERSONAL] =
+ g_signal_new ("add_word_to_personal",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditSpellCheckerDialogClass, add_word_to_personal),
+ NULL, NULL,
+ gedit_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+}
+
+static void
+create_dialog (GeditSpellCheckerDialog *dlg,
+ const gchar *data_dir)
+{
+ GtkWidget *error_widget;
+ GtkWidget *content;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *cell;
+ GtkTreeSelection *selection;
+ gchar *root_objects[] = {
+ "content",
+ "check_word_image",
+ "add_word_image",
+ "ignore_image",
+ "change_image",
+ "ignore_all_image",
+ "change_all_image",
+ NULL
+ };
+ gboolean ret;
+ gchar *ui_file;
+
+ g_return_if_fail (dlg != NULL);
+
+ dlg->spell_checker = NULL;
+ dlg->misspelled_word = NULL;
+
+ ui_file = g_build_filename (data_dir, "spell-checker.ui", NULL);
+ ret = gedit_utils_get_ui_objects (ui_file,
+ root_objects,
+ &error_widget,
+
+ "content", &content,
+ "misspelled_word_label", &dlg->misspelled_word_label,
+ "word_entry", &dlg->word_entry,
+ "check_word_button", &dlg->check_word_button,
+ "ignore_button", &dlg->ignore_button,
+ "ignore_all_button", &dlg->ignore_all_button,
+ "change_button", &dlg->change_button,
+ "change_all_button", &dlg->change_all_button,
+ "add_word_button", &dlg->add_word_button,
+ "close_button", &dlg->close_button,
+ "suggestions_list", &dlg->suggestions_list,
+ "language_label", &dlg->language_label,
+ NULL);
+ g_free (ui_file);
+
+ if (!ret)
+ {
+ gtk_widget_show (error_widget);
+
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ error_widget, TRUE, TRUE, 0);
+
+ return;
+ }
+
+ gtk_label_set_label (GTK_LABEL (dlg->misspelled_word_label), "");
+ gtk_widget_set_sensitive (dlg->word_entry, FALSE);
+ gtk_widget_set_sensitive (dlg->check_word_button, FALSE);
+ gtk_widget_set_sensitive (dlg->ignore_button, FALSE);
+ gtk_widget_set_sensitive (dlg->ignore_all_button, FALSE);
+ gtk_widget_set_sensitive (dlg->change_button, FALSE);
+ gtk_widget_set_sensitive (dlg->change_all_button, FALSE);
+ gtk_widget_set_sensitive (dlg->add_word_button, FALSE);
+
+ gtk_label_set_label (GTK_LABEL (dlg->language_label), "");
+
+ gtk_container_add (GTK_CONTAINER (dlg), content);
+ g_object_unref (content);
+
+ gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
+ gtk_window_set_title (GTK_WINDOW (dlg), _("Check Spelling"));
+
+ /* Suggestion list */
+ dlg->suggestions_list_model = GTK_TREE_MODEL (
+ gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING));
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (dlg->suggestions_list),
+ dlg->suggestions_list_model);
+
+ /* Add the suggestions column */
+ cell = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Suggestions"), cell,
+ "text", COLUMN_SUGGESTIONS, NULL);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (dlg->suggestions_list), column);
+
+ gtk_tree_view_set_search_column (GTK_TREE_VIEW (dlg->suggestions_list),
+ COLUMN_SUGGESTIONS);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dlg->suggestions_list));
+
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+ /* Set default button */
+ GTK_WIDGET_SET_FLAGS (dlg->change_button, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (dlg->change_button);
+
+ gtk_entry_set_activates_default (GTK_ENTRY (dlg->word_entry), TRUE);
+
+ /* Connect signals */
+ g_signal_connect (dlg->word_entry, "changed",
+ G_CALLBACK (word_entry_changed_handler), dlg);
+ g_signal_connect (dlg->close_button, "clicked",
+ G_CALLBACK (close_button_clicked_handler), dlg);
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (suggestions_list_selection_changed_handler),
+ dlg);
+ g_signal_connect (dlg->check_word_button, "clicked",
+ G_CALLBACK (check_word_button_clicked_handler), dlg);
+ g_signal_connect (dlg->add_word_button, "clicked",
+ G_CALLBACK (add_word_button_clicked_handler), dlg);
+ g_signal_connect (dlg->ignore_button, "clicked",
+ G_CALLBACK (ignore_button_clicked_handler), dlg);
+ g_signal_connect (dlg->ignore_all_button, "clicked",
+ G_CALLBACK (ignore_all_button_clicked_handler), dlg);
+ g_signal_connect (dlg->change_button, "clicked",
+ G_CALLBACK (change_button_clicked_handler), dlg);
+ g_signal_connect (dlg->change_all_button, "clicked",
+ G_CALLBACK (change_all_button_clicked_handler), dlg);
+ g_signal_connect (dlg->suggestions_list, "row-activated",
+ G_CALLBACK (suggestions_list_row_activated_handler), dlg);
+}
+
+static void
+gedit_spell_checker_dialog_init (GeditSpellCheckerDialog *dlg)
+{
+}
+
+GtkWidget *
+gedit_spell_checker_dialog_new (const gchar *data_dir)
+{
+ GeditSpellCheckerDialog *dlg;
+
+ dlg = GEDIT_SPELL_CHECKER_DIALOG (
+ g_object_new (GEDIT_TYPE_SPELL_CHECKER_DIALOG, NULL));
+
+ g_return_val_if_fail (dlg != NULL, NULL);
+
+ create_dialog (dlg, data_dir);
+
+ return GTK_WIDGET (dlg);
+}
+
+GtkWidget *
+gedit_spell_checker_dialog_new_from_spell_checker (GeditSpellChecker *spell,
+ const gchar *data_dir)
+{
+ GeditSpellCheckerDialog *dlg;
+
+ g_return_val_if_fail (spell != NULL, NULL);
+
+ dlg = GEDIT_SPELL_CHECKER_DIALOG (
+ g_object_new (GEDIT_TYPE_SPELL_CHECKER_DIALOG, NULL));
+
+ g_return_val_if_fail (dlg != NULL, NULL);
+
+ create_dialog (dlg, data_dir);
+
+ gedit_spell_checker_dialog_set_spell_checker (dlg, spell);
+
+ return GTK_WIDGET (dlg);
+}
+
+void
+gedit_spell_checker_dialog_set_spell_checker (GeditSpellCheckerDialog *dlg, GeditSpellChecker *spell)
+{
+ const GeditSpellCheckerLanguage* language;
+ const gchar *lang;
+ gchar *tmp;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+ g_return_if_fail (spell != NULL);
+
+ if (dlg->spell_checker != NULL)
+ g_object_unref (dlg->spell_checker);
+
+ dlg->spell_checker = spell;
+ g_object_ref (dlg->spell_checker);
+
+ language = gedit_spell_checker_get_language (dlg->spell_checker);
+
+ lang = gedit_spell_checker_language_to_string (language);
+ tmp = g_strdup_printf("<b>%s</b>", lang);
+
+ gtk_label_set_label (GTK_LABEL (dlg->language_label), tmp);
+ g_free (tmp);
+
+ if (dlg->misspelled_word != NULL)
+ gedit_spell_checker_dialog_set_misspelled_word (dlg, dlg->misspelled_word, -1);
+ else
+ gtk_list_store_clear (GTK_LIST_STORE (dlg->suggestions_list_model));
+
+ /* TODO: reset all widgets */
+}
+
+void
+gedit_spell_checker_dialog_set_misspelled_word (GeditSpellCheckerDialog *dlg,
+ const gchar *word,
+ gint len)
+{
+ gchar *tmp;
+ GSList *sug;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+ g_return_if_fail (word != NULL);
+
+ g_return_if_fail (dlg->spell_checker != NULL);
+ g_return_if_fail (!gedit_spell_checker_check_word (dlg->spell_checker, word, -1));
+
+ /* build_suggestions_list */
+ if (dlg->misspelled_word != NULL)
+ g_free (dlg->misspelled_word);
+
+ dlg->misspelled_word = g_strdup (word);
+
+ tmp = g_strdup_printf("<b>%s</b>", word);
+ gtk_label_set_label (GTK_LABEL (dlg->misspelled_word_label), tmp);
+ g_free (tmp);
+
+ sug = gedit_spell_checker_get_suggestions (dlg->spell_checker,
+ dlg->misspelled_word,
+ -1);
+
+ update_suggestions_list_model (dlg, sug);
+
+ /* free the suggestion list */
+ g_slist_foreach (sug, (GFunc)g_free, NULL);
+ g_slist_free (sug);
+
+ gtk_widget_set_sensitive (dlg->ignore_button, TRUE);
+ gtk_widget_set_sensitive (dlg->ignore_all_button, TRUE);
+ gtk_widget_set_sensitive (dlg->add_word_button, TRUE);
+}
+
+static void
+update_suggestions_list_model (GeditSpellCheckerDialog *dlg, GSList *suggestions)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeSelection *sel;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+ g_return_if_fail (GTK_IS_LIST_STORE (dlg->suggestions_list_model));
+
+ store = GTK_LIST_STORE (dlg->suggestions_list_model);
+ gtk_list_store_clear (store);
+
+ gtk_widget_set_sensitive (dlg->word_entry, TRUE);
+
+ if (suggestions == NULL)
+ {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ /* Translators: Displayed in the "Check Spelling" dialog if there are no suggestions
+ * for the current misspelled word */
+ COLUMN_SUGGESTIONS, _("(no suggested words)"),
+ -1);
+
+ gtk_entry_set_text (GTK_ENTRY (dlg->word_entry), "");
+
+ gtk_widget_set_sensitive (dlg->suggestions_list, FALSE);
+
+ return;
+ }
+
+ gtk_widget_set_sensitive (dlg->suggestions_list, TRUE);
+
+ gtk_entry_set_text (GTK_ENTRY (dlg->word_entry), (gchar*)suggestions->data);
+
+ while (suggestions != NULL)
+ {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COLUMN_SUGGESTIONS, (gchar*)suggestions->data,
+ -1);
+
+ suggestions = g_slist_next (suggestions);
+ }
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (dlg->suggestions_list));
+ gtk_tree_model_get_iter_first (dlg->suggestions_list_model, &iter);
+ gtk_tree_selection_select_iter (sel, &iter);
+}
+
+static void
+word_entry_changed_handler (GtkEditable *editable, GeditSpellCheckerDialog *dlg)
+{
+ const gchar *text;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+
+ text = gtk_entry_get_text (GTK_ENTRY (dlg->word_entry));
+
+ if (g_utf8_strlen (text, -1) > 0)
+ {
+ gtk_widget_set_sensitive (dlg->check_word_button, TRUE);
+ gtk_widget_set_sensitive (dlg->change_button, TRUE);
+ gtk_widget_set_sensitive (dlg->change_all_button, TRUE);
+ }
+ else
+ {
+ gtk_widget_set_sensitive (dlg->check_word_button, FALSE);
+ gtk_widget_set_sensitive (dlg->change_button, FALSE);
+ gtk_widget_set_sensitive (dlg->change_all_button, FALSE);
+ }
+}
+
+static void
+close_button_clicked_handler (GtkButton *button, GeditSpellCheckerDialog *dlg)
+{
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+
+ gtk_widget_destroy (GTK_WIDGET (dlg));
+}
+
+static void
+suggestions_list_selection_changed_handler (GtkTreeSelection *selection,
+ GeditSpellCheckerDialog *dlg)
+{
+ GtkTreeIter iter;
+ GValue value = {0, };
+ const gchar *text;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+
+ if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get_value (dlg->suggestions_list_model, &iter,
+ COLUMN_SUGGESTIONS,
+ &value);
+
+ text = g_value_get_string (&value);
+
+ gtk_entry_set_text (GTK_ENTRY (dlg->word_entry), text);
+
+ g_value_unset (&value);
+}
+
+static void
+check_word_button_clicked_handler (GtkButton *button, GeditSpellCheckerDialog *dlg)
+{
+ const gchar *word;
+ gssize len;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+
+ word = gtk_entry_get_text (GTK_ENTRY (dlg->word_entry));
+ len = strlen (word);
+ g_return_if_fail (len > 0);
+
+ if (gedit_spell_checker_check_word (dlg->spell_checker, word, len))
+ {
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ store = GTK_LIST_STORE (dlg->suggestions_list_model);
+ gtk_list_store_clear (store);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ /* Translators: Displayed in the "Check Spelling" dialog if the current word isn't misspelled */
+ COLUMN_SUGGESTIONS, _("(correct spelling)"),
+ -1);
+
+ gtk_widget_set_sensitive (dlg->suggestions_list, FALSE);
+ }
+ else
+ {
+ GSList *sug;
+
+ sug = gedit_spell_checker_get_suggestions (dlg->spell_checker,
+ word,
+ len);
+
+ update_suggestions_list_model (dlg, sug);
+
+ /* free the suggestion list */
+ g_slist_foreach (sug, (GFunc)g_free, NULL);
+ g_slist_free (sug);
+ }
+}
+
+static void
+add_word_button_clicked_handler (GtkButton *button, GeditSpellCheckerDialog *dlg)
+{
+ gchar *word;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+ g_return_if_fail (dlg->misspelled_word != NULL);
+
+ gedit_spell_checker_add_word_to_personal (dlg->spell_checker,
+ dlg->misspelled_word,
+ -1);
+
+ word = g_strdup (dlg->misspelled_word);
+
+ g_signal_emit (G_OBJECT (dlg), signals [ADD_WORD_TO_PERSONAL], 0, word);
+
+ g_free (word);
+}
+
+static void
+ignore_button_clicked_handler (GtkButton *button, GeditSpellCheckerDialog *dlg)
+{
+ gchar *word;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+ g_return_if_fail (dlg->misspelled_word != NULL);
+
+ word = g_strdup (dlg->misspelled_word);
+
+ g_signal_emit (G_OBJECT (dlg), signals [IGNORE], 0, word);
+
+ g_free (word);
+}
+
+static void
+ignore_all_button_clicked_handler (GtkButton *button, GeditSpellCheckerDialog *dlg)
+{
+ gchar *word;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+ g_return_if_fail (dlg->misspelled_word != NULL);
+
+ gedit_spell_checker_add_word_to_session (dlg->spell_checker,
+ dlg->misspelled_word,
+ -1);
+
+ word = g_strdup (dlg->misspelled_word);
+
+ g_signal_emit (G_OBJECT (dlg), signals [IGNORE_ALL], 0, word);
+
+ g_free (word);
+}
+
+static void
+change_button_clicked_handler (GtkButton *button, GeditSpellCheckerDialog *dlg)
+{
+ gchar *word;
+ gchar *change;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+ g_return_if_fail (dlg->misspelled_word != NULL);
+
+ change = g_strdup (gtk_entry_get_text (GTK_ENTRY (dlg->word_entry)));
+ g_return_if_fail (change != NULL);
+ g_return_if_fail (*change != '\0');
+
+ gedit_spell_checker_set_correction (dlg->spell_checker,
+ dlg->misspelled_word, -1,
+ change, -1);
+
+ word = g_strdup (dlg->misspelled_word);
+
+ g_signal_emit (G_OBJECT (dlg), signals [CHANGE], 0, word, change);
+
+ g_free (word);
+ g_free (change);
+}
+
+/* double click on one of the suggestions is like clicking on "change" */
+static void
+suggestions_list_row_activated_handler (GtkTreeView *view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ GeditSpellCheckerDialog *dlg)
+{
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+
+ change_button_clicked_handler (GTK_BUTTON (dlg->change_button), dlg);
+}
+
+static void
+change_all_button_clicked_handler (GtkButton *button, GeditSpellCheckerDialog *dlg)
+{
+ gchar *word;
+ gchar *change;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+ g_return_if_fail (dlg->misspelled_word != NULL);
+
+ change = g_strdup (gtk_entry_get_text (GTK_ENTRY (dlg->word_entry)));
+ g_return_if_fail (change != NULL);
+ g_return_if_fail (*change != '\0');
+
+ gedit_spell_checker_set_correction (dlg->spell_checker,
+ dlg->misspelled_word, -1,
+ change, -1);
+
+ word = g_strdup (dlg->misspelled_word);
+
+ g_signal_emit (G_OBJECT (dlg), signals [CHANGE_ALL], 0, word, change);
+
+ g_free (word);
+ g_free (change);
+}
+
+void
+gedit_spell_checker_dialog_set_completed (GeditSpellCheckerDialog *dlg)
+{
+ gchar *tmp;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER_DIALOG (dlg));
+
+ tmp = g_strdup_printf("<b>%s</b>", _("Completed spell checking"));
+ gtk_label_set_label (GTK_LABEL (dlg->misspelled_word_label),
+ tmp);
+ g_free (tmp);
+
+ gtk_list_store_clear (GTK_LIST_STORE (dlg->suggestions_list_model));
+ gtk_entry_set_text (GTK_ENTRY (dlg->word_entry), "");
+
+ gtk_widget_set_sensitive (dlg->word_entry, FALSE);
+ gtk_widget_set_sensitive (dlg->check_word_button, FALSE);
+ gtk_widget_set_sensitive (dlg->ignore_button, FALSE);
+ gtk_widget_set_sensitive (dlg->ignore_all_button, FALSE);
+ gtk_widget_set_sensitive (dlg->change_button, FALSE);
+ gtk_widget_set_sensitive (dlg->change_all_button, FALSE);
+ gtk_widget_set_sensitive (dlg->add_word_button, FALSE);
+ gtk_widget_set_sensitive (dlg->suggestions_list, FALSE);
+}
+
diff --git a/plugins/spell/gedit-spell-checker-dialog.h b/plugins/spell/gedit-spell-checker-dialog.h
new file mode 100755
index 00000000..257c2b75
--- /dev/null
+++ b/plugins/spell/gedit-spell-checker-dialog.h
@@ -0,0 +1,92 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gedit-spell-checker-dialog.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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.
+ */
+
+/*
+ * Modified by the gedit Team, 2002. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ */
+
+#ifndef __GEDIT_SPELL_CHECKER_DIALOG_H__
+#define __GEDIT_SPELL_CHECKER_DIALOG_H__
+
+#include <gtk/gtk.h>
+#include "gedit-spell-checker.h"
+
+G_BEGIN_DECLS
+
+#define GEDIT_TYPE_SPELL_CHECKER_DIALOG (gedit_spell_checker_dialog_get_type ())
+#define GEDIT_SPELL_CHECKER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_SPELL_CHECKER_DIALOG, GeditSpellCheckerDialog))
+#define GEDIT_SPELL_CHECKER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_SPELL_CHECKER_DIALOG, GeditSpellCheckerDialog))
+#define GEDIT_IS_SPELL_CHECKER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_SPELL_CHECKER_DIALOG))
+#define GEDIT_IS_SPELL_CHECKER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_SPELL_CHECKER_DIALOG))
+#define GEDIT_SPELL_CHECKER_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_SPELL_CHECKER_DIALOG, GeditSpellCheckerDialog))
+
+
+typedef struct _GeditSpellCheckerDialog GeditSpellCheckerDialog;
+
+typedef struct _GeditSpellCheckerDialogClass GeditSpellCheckerDialogClass;
+
+struct _GeditSpellCheckerDialogClass
+{
+ GtkWindowClass parent_class;
+
+ /* Signals */
+ void (*ignore) (GeditSpellCheckerDialog *dlg,
+ const gchar *word);
+ void (*ignore_all) (GeditSpellCheckerDialog *dlg,
+ const gchar *word);
+ void (*change) (GeditSpellCheckerDialog *dlg,
+ const gchar *word,
+ const gchar *change_to);
+ void (*change_all) (GeditSpellCheckerDialog *dlg,
+ const gchar *word,
+ const gchar *change_to);
+ void (*add_word_to_personal) (GeditSpellCheckerDialog *dlg,
+ const gchar *word);
+
+};
+
+GType gedit_spell_checker_dialog_get_type (void) G_GNUC_CONST;
+
+/* Constructors */
+GtkWidget *gedit_spell_checker_dialog_new (const gchar *data_dir);
+GtkWidget *gedit_spell_checker_dialog_new_from_spell_checker
+ (GeditSpellChecker *spell,
+ const gchar *data_dir);
+
+void gedit_spell_checker_dialog_set_spell_checker
+ (GeditSpellCheckerDialog *dlg,
+ GeditSpellChecker *spell);
+void gedit_spell_checker_dialog_set_misspelled_word
+ (GeditSpellCheckerDialog *dlg,
+ const gchar* word,
+ gint len);
+
+void gedit_spell_checker_dialog_set_completed
+ (GeditSpellCheckerDialog *dlg);
+
+G_END_DECLS
+
+#endif /* __GEDIT_SPELL_CHECKER_DIALOG_H__ */
+
diff --git a/plugins/spell/gedit-spell-checker-language.c b/plugins/spell/gedit-spell-checker-language.c
new file mode 100755
index 00000000..ae7e2422
--- /dev/null
+++ b/plugins/spell/gedit-spell-checker-language.c
@@ -0,0 +1,439 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gedit-spell-checker-language.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2006 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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.
+ */
+
+/*
+ * Modified by the gedit Team, 2006. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ */
+
+/* Part of the code taked from Epiphany.
+ *
+ * Copyright (C) 2003, 2004 Christian Persch
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <enchant.h>
+
+#include <glib/gi18n.h>
+#include <libxml/xmlreader.h>
+
+#include "gedit-spell-checker-language.h"
+
+#include <gedit/gedit-debug.h>
+
+#define ISO_639_DOMAIN "iso_639"
+#define ISO_3166_DOMAIN "iso_3166"
+
+#define ISOCODESLOCALEDIR ISO_CODES_PREFIX "/share/locale"
+
+struct _GeditSpellCheckerLanguage
+{
+ gchar *abrev;
+ gchar *name;
+};
+
+static gboolean available_languages_initialized = FALSE;
+static GSList *available_languages = NULL;
+
+static GHashTable *iso_639_table = NULL;
+static GHashTable *iso_3166_table = NULL;
+
+static void
+bind_iso_domains (void)
+{
+ static gboolean bound = FALSE;
+
+ if (bound == FALSE)
+ {
+ 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");
+
+ bound = TRUE;
+ }
+}
+
+static void
+read_iso_639_entry (xmlTextReaderPtr reader,
+ GHashTable *table)
+{
+ xmlChar *code, *name;
+
+ code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "iso_639_1_code");
+ name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name");
+
+ /* Get iso-639-2 code */
+ if (code == NULL || code[0] == '\0')
+ {
+ xmlFree (code);
+ /* FIXME: use the 2T or 2B code? */
+ code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "iso_639_2T_code");
+ }
+
+ if (code != NULL && code[0] != '\0' && name != NULL && name[0] != '\0')
+ {
+ g_hash_table_insert (table, code, name);
+ }
+ else
+ {
+ xmlFree (code);
+ xmlFree (name);
+ }
+}
+
+static void
+read_iso_3166_entry (xmlTextReaderPtr reader,
+ GHashTable *table)
+{
+ xmlChar *code, *name;
+
+ code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "alpha_2_code");
+ name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name");
+
+ if (code != NULL && code[0] != '\0' && name != NULL && name[0] != '\0')
+ {
+ char *lcode;
+
+ lcode = g_ascii_strdown ((char *) code, -1);
+ xmlFree (code);
+
+ /* g_print ("%s -> %s\n", lcode, name); */
+
+ g_hash_table_insert (table, lcode, name);
+ }
+ else
+ {
+ xmlFree (code);
+ xmlFree (name);
+ }
+}
+
+typedef enum
+{
+ STATE_START,
+ STATE_STOP,
+ STATE_ENTRIES,
+} ParserState;
+
+static void
+load_iso_entries (int iso,
+ GFunc read_entry_func,
+ gpointer user_data)
+{
+ xmlTextReaderPtr reader;
+ ParserState state = STATE_START;
+ xmlChar iso_entries[32], iso_entry[32];
+ char *filename;
+ int ret = -1;
+
+ gedit_debug_message (DEBUG_PLUGINS, "Loading ISO-%d codes", iso);
+
+ filename = g_strdup_printf (ISO_CODES_PREFIX "/share/xml/iso-codes/iso_%d.xml", iso);
+ reader = xmlNewTextReaderFilename (filename);
+ if (reader == NULL) goto out;
+
+ xmlStrPrintf (iso_entries, sizeof (iso_entries), (const xmlChar *)"iso_%d_entries", iso);
+ xmlStrPrintf (iso_entry, sizeof (iso_entry), (const xmlChar *)"iso_%d_entry", iso);
+
+ ret = xmlTextReaderRead (reader);
+
+ while (ret == 1)
+ {
+ const xmlChar *tag;
+ xmlReaderTypes type;
+
+ tag = xmlTextReaderConstName (reader);
+ type = xmlTextReaderNodeType (reader);
+
+ if (state == STATE_ENTRIES &&
+ type == XML_READER_TYPE_ELEMENT &&
+ xmlStrEqual (tag, iso_entry))
+ {
+ read_entry_func (reader, user_data);
+ }
+ else if (state == STATE_START &&
+ type == XML_READER_TYPE_ELEMENT &&
+ xmlStrEqual (tag, iso_entries))
+ {
+ state = STATE_ENTRIES;
+ }
+ else if (state == STATE_ENTRIES &&
+ type == XML_READER_TYPE_END_ELEMENT &&
+ xmlStrEqual (tag, iso_entries))
+ {
+ state = STATE_STOP;
+ }
+ else if (type == XML_READER_TYPE_SIGNIFICANT_WHITESPACE ||
+ type == XML_READER_TYPE_WHITESPACE ||
+ type == XML_READER_TYPE_TEXT ||
+ type == XML_READER_TYPE_COMMENT)
+ {
+ /* eat it */
+ }
+ else
+ {
+ /* ignore it */
+ }
+
+ ret = xmlTextReaderRead (reader);
+ }
+
+ xmlFreeTextReader (reader);
+
+out:
+ if (ret < 0 || state != STATE_STOP)
+ {
+ g_warning ("Failed to load ISO-%d codes from %s!\n",
+ iso, filename);
+ }
+
+ g_free (filename);
+}
+
+static GHashTable *
+create_iso_639_table (void)
+{
+ GHashTable *table;
+
+ bind_iso_domains ();
+ table = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) xmlFree,
+ (GDestroyNotify) xmlFree);
+
+ load_iso_entries (639, (GFunc) read_iso_639_entry, table);
+
+ return table;
+}
+
+static GHashTable *
+create_iso_3166_table (void)
+{
+ GHashTable *table;
+
+ bind_iso_domains ();
+ table = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) xmlFree);
+
+ load_iso_entries (3166, (GFunc) read_iso_3166_entry, table);
+
+ return table;
+}
+
+static char *
+create_name_for_language (const char *code)
+{
+ char **str;
+ char *name = NULL;
+ const char *langname, *localename;
+ int len;
+
+ g_return_val_if_fail (iso_639_table != NULL, NULL);
+ g_return_val_if_fail (iso_3166_table != NULL, NULL);
+
+ str = g_strsplit (code, "_", -1);
+ len = g_strv_length (str);
+ g_return_val_if_fail (len != 0, NULL);
+
+ langname = (const char *) g_hash_table_lookup (iso_639_table, str[0]);
+
+ if (len == 1 && langname != NULL)
+ {
+ name = g_strdup (dgettext (ISO_639_DOMAIN, langname));
+ }
+ else if (len == 2 && langname != NULL)
+ {
+ gchar *locale_code = g_ascii_strdown (str[1], -1);
+
+ localename = (const char *) g_hash_table_lookup (iso_3166_table, locale_code);
+ g_free (locale_code);
+
+ if (localename != NULL)
+ {
+ /* Translators: the first %s is the language name, and
+ * the second %s is the locale name. Example:
+ * "French (France)"
+ */
+ name = g_strdup_printf (C_("language", "%s (%s)"),
+ dgettext (ISO_639_DOMAIN, langname),
+ dgettext (ISO_3166_DOMAIN, localename));
+ }
+ else
+ {
+ name = g_strdup_printf (C_("language", "%s (%s)"),
+ dgettext (ISO_639_DOMAIN, langname), str[1]);
+ }
+ }
+ else
+ {
+ /* Translators: this refers to an unknown language code
+ * (one which isn't in our built-in list).
+ */
+ name = g_strdup_printf (C_("language", "Unknown (%s)"), code);
+ }
+
+ g_strfreev (str);
+
+ return name;
+}
+
+static void
+enumerate_dicts (const char * const lang_tag,
+ const char * const provider_name,
+ const char * const provider_desc,
+ const char * const provider_file,
+ void * user_data)
+{
+ gchar *lang_name;
+
+ GTree *dicts = (GTree *)user_data;
+
+ lang_name = create_name_for_language (lang_tag);
+ g_return_if_fail (lang_name != NULL);
+
+ /* g_print ("%s - %s\n", lang_tag, lang_name); */
+
+ g_tree_replace (dicts, g_strdup (lang_tag), lang_name);
+}
+
+static gint
+key_cmp (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ return strcmp (a, b);
+}
+
+static gint
+lang_cmp (const GeditSpellCheckerLanguage *a,
+ const GeditSpellCheckerLanguage *b)
+{
+ return g_utf8_collate (a->name, b->name);
+}
+
+static gboolean
+build_langs_list (const gchar *key,
+ const gchar *value,
+ gpointer data)
+{
+ GeditSpellCheckerLanguage *lang = g_new (GeditSpellCheckerLanguage, 1);
+
+ lang->abrev = g_strdup (key);
+ lang->name = g_strdup (value);
+
+ available_languages = g_slist_insert_sorted (available_languages,
+ lang,
+ (GCompareFunc)lang_cmp);
+
+ return FALSE;
+}
+
+const GSList *
+gedit_spell_checker_get_available_languages (void)
+{
+ EnchantBroker *broker;
+ GTree *dicts;
+
+ if (available_languages_initialized)
+ return available_languages;
+
+ g_return_val_if_fail (available_languages == NULL, NULL);
+
+ available_languages_initialized = TRUE;
+
+ broker = enchant_broker_init ();
+ g_return_val_if_fail (broker != NULL, NULL);
+
+ /* Use a GTree to efficiently remove duplicates while building the list */
+ dicts = g_tree_new_full (key_cmp,
+ NULL,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+
+ iso_639_table = create_iso_639_table ();
+ iso_3166_table = create_iso_3166_table ();
+
+ enchant_broker_list_dicts (broker, enumerate_dicts, dicts);
+
+ enchant_broker_free (broker);
+
+ g_hash_table_destroy (iso_639_table);
+ g_hash_table_destroy (iso_3166_table);
+
+ iso_639_table = NULL;
+ iso_3166_table = NULL;
+
+ g_tree_foreach (dicts, (GTraverseFunc)build_langs_list, NULL);
+
+ g_tree_destroy (dicts);
+
+ return available_languages;
+}
+
+const gchar *
+gedit_spell_checker_language_to_string (const GeditSpellCheckerLanguage *lang)
+{
+ if (lang == NULL)
+ /* Translators: this refers the Default language used by the
+ * spell checker
+ */
+ return C_("language", "Default");
+
+ return lang->name;
+}
+
+const gchar *
+gedit_spell_checker_language_to_key (const GeditSpellCheckerLanguage *lang)
+{
+ g_return_val_if_fail (lang != NULL, NULL);
+
+ return lang->abrev;
+}
+
+const GeditSpellCheckerLanguage *
+gedit_spell_checker_language_from_key (const gchar *key)
+{
+ const GSList *langs;
+
+ g_return_val_if_fail (key != NULL, NULL);
+
+ langs = gedit_spell_checker_get_available_languages ();
+
+ while (langs != NULL)
+ {
+ const GeditSpellCheckerLanguage *l = (const GeditSpellCheckerLanguage *)langs->data;
+
+ if (g_ascii_strcasecmp (key, l->abrev) == 0)
+ return l;
+
+ langs = g_slist_next (langs);
+ }
+
+ return NULL;
+}
diff --git a/plugins/spell/gedit-spell-checker-language.h b/plugins/spell/gedit-spell-checker-language.h
new file mode 100755
index 00000000..7e62de65
--- /dev/null
+++ b/plugins/spell/gedit-spell-checker-language.h
@@ -0,0 +1,51 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gedit-spell-checker-language.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2006 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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.
+ */
+
+/*
+ * Modified by the gedit Team, 2006. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ */
+
+#ifndef __GEDIT_SPELL_CHECKER_LANGUAGE_H__
+#define __GEDIT_SPELL_CHECKER_LANGUAGE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GeditSpellCheckerLanguage GeditSpellCheckerLanguage;
+
+const gchar *gedit_spell_checker_language_to_string (const GeditSpellCheckerLanguage *lang);
+
+const gchar *gedit_spell_checker_language_to_key (const GeditSpellCheckerLanguage *lang);
+
+const GeditSpellCheckerLanguage *gedit_spell_checker_language_from_key (const gchar *key);
+
+/* GSList contains "GeditSpellCheckerLanguage*" items */
+const GSList *gedit_spell_checker_get_available_languages
+ (void);
+
+G_END_DECLS
+
+#endif /* __GEDIT_SPELL_CHECKER_LANGUAGE_H__ */
diff --git a/plugins/spell/gedit-spell-checker.c b/plugins/spell/gedit-spell-checker.c
new file mode 100755
index 00000000..51b8d8a8
--- /dev/null
+++ b/plugins/spell/gedit-spell-checker.c
@@ -0,0 +1,520 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gedit-spell-checker.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002-2006 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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.
+ */
+
+/*
+ * Modified by the gedit Team, 2002-2006. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <enchant.h>
+
+#include <glib/gi18n.h>
+#include <glib.h>
+
+#include "gedit-spell-checker.h"
+#include "gedit-spell-utils.h"
+#include "gedit-spell-marshal.h"
+
+struct _GeditSpellChecker
+{
+ GObject parent_instance;
+
+ EnchantDict *dict;
+ EnchantBroker *broker;
+ const GeditSpellCheckerLanguage *active_lang;
+};
+
+/* GObject properties */
+enum {
+ PROP_0 = 0,
+ PROP_LANGUAGE,
+ LAST_PROP
+};
+
+/* Signals */
+enum {
+ ADD_WORD_TO_PERSONAL = 0,
+ ADD_WORD_TO_SESSION,
+ SET_LANGUAGE,
+ CLEAR_SESSION,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE(GeditSpellChecker, gedit_spell_checker, G_TYPE_OBJECT)
+
+static void
+gedit_spell_checker_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ /*
+ GeditSpellChecker *spell = GEDIT_SPELL_CHECKER (object);
+ */
+
+ switch (prop_id)
+ {
+ case PROP_LANGUAGE:
+ /* TODO */
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gedit_spell_checker_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ /*
+ GeditSpellChecker *spell = GEDIT_SPELL_CHECKER (object);
+ */
+
+ switch (prop_id)
+ {
+ case PROP_LANGUAGE:
+ /* TODO */
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gedit_spell_checker_finalize (GObject *object)
+{
+ GeditSpellChecker *spell_checker;
+
+ g_return_if_fail (GEDIT_IS_SPELL_CHECKER (object));
+
+ spell_checker = GEDIT_SPELL_CHECKER (object);
+
+ if (spell_checker->dict != NULL)
+ enchant_broker_free_dict (spell_checker->broker, spell_checker->dict);
+
+ if (spell_checker->broker != NULL)
+ enchant_broker_free (spell_checker->broker);
+
+ G_OBJECT_CLASS (gedit_spell_checker_parent_class)->finalize (object);
+}
+
+static void
+gedit_spell_checker_class_init (GeditSpellCheckerClass * klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = gedit_spell_checker_set_property;
+ object_class->get_property = gedit_spell_checker_get_property;
+
+ object_class->finalize = gedit_spell_checker_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_LANGUAGE,
+ g_param_spec_pointer ("language",
+ "Language",
+ "The language used by the spell checker",
+ G_PARAM_READWRITE));
+
+ signals[ADD_WORD_TO_PERSONAL] =
+ g_signal_new ("add_word_to_personal",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditSpellCheckerClass, add_word_to_personal),
+ NULL, NULL,
+ gedit_marshal_VOID__STRING_INT,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_INT);
+
+ signals[ADD_WORD_TO_SESSION] =
+ g_signal_new ("add_word_to_session",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditSpellCheckerClass, add_word_to_session),
+ NULL, NULL,
+ gedit_marshal_VOID__STRING_INT,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_INT);
+
+ signals[SET_LANGUAGE] =
+ g_signal_new ("set_language",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditSpellCheckerClass, set_language),
+ NULL, NULL,
+ gedit_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+
+ signals[CLEAR_SESSION] =
+ g_signal_new ("clear_session",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditSpellCheckerClass, clear_session),
+ NULL, NULL,
+ gedit_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}
+
+static void
+gedit_spell_checker_init (GeditSpellChecker *spell_checker)
+{
+ spell_checker->broker = enchant_broker_init ();
+ spell_checker->dict = NULL;
+ spell_checker->active_lang = NULL;
+}
+
+GeditSpellChecker *
+gedit_spell_checker_new (void)
+{
+ GeditSpellChecker *spell;
+
+ spell = GEDIT_SPELL_CHECKER (
+ g_object_new (GEDIT_TYPE_SPELL_CHECKER, NULL));
+
+ g_return_val_if_fail (spell != NULL, NULL);
+
+ return spell;
+}
+
+static gboolean
+lazy_init (GeditSpellChecker *spell,
+ const GeditSpellCheckerLanguage *language)
+{
+ if (spell->dict != NULL)
+ return TRUE;
+
+ g_return_val_if_fail (spell->broker != NULL, FALSE);
+
+ spell->active_lang = NULL;
+
+ if (language != NULL)
+ {
+ spell->active_lang = language;
+ }
+ else
+ {
+ /* First try to get a default language */
+ const GeditSpellCheckerLanguage *l;
+ gint i = 0;
+ const gchar * const *lang_tags = g_get_language_names ();
+
+ while (lang_tags [i])
+ {
+ l = gedit_spell_checker_language_from_key (lang_tags [i]);
+
+ if (l != NULL)
+ {
+ spell->active_lang = l;
+ break;
+ }
+
+ i++;
+ }
+ }
+
+ /* Second try to get a default language */
+ if (spell->active_lang == NULL)
+ spell->active_lang = gedit_spell_checker_language_from_key ("en_US");
+
+ /* Last try to get a default language */
+ if (spell->active_lang == NULL)
+ {
+ const GSList *langs;
+ langs = gedit_spell_checker_get_available_languages ();
+ if (langs != NULL)
+ spell->active_lang = (const GeditSpellCheckerLanguage *)langs->data;
+ }
+
+ if (spell->active_lang != NULL)
+ {
+ const gchar *key;
+
+ key = gedit_spell_checker_language_to_key (spell->active_lang);
+
+ spell->dict = enchant_broker_request_dict (spell->broker,
+ key);
+ }
+
+ if (spell->dict == NULL)
+ {
+ spell->active_lang = NULL;
+
+ if (language != NULL)
+ g_warning ("Spell checker plugin: cannot select a default language.");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gedit_spell_checker_set_language (GeditSpellChecker *spell,
+ const GeditSpellCheckerLanguage *language)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (GEDIT_IS_SPELL_CHECKER (spell), FALSE);
+
+ if (spell->dict != NULL)
+ {
+ enchant_broker_free_dict (spell->broker, spell->dict);
+ spell->dict = NULL;
+ }
+
+ ret = lazy_init (spell, language);
+
+ if (ret)
+ g_signal_emit (G_OBJECT (spell), signals[SET_LANGUAGE], 0, language);
+ else
+ g_warning ("Spell checker plugin: cannot use language %s.",
+ gedit_spell_checker_language_to_string (language));
+
+ return ret;
+}
+
+const GeditSpellCheckerLanguage *
+gedit_spell_checker_get_language (GeditSpellChecker *spell)
+{
+ g_return_val_if_fail (GEDIT_IS_SPELL_CHECKER (spell), NULL);
+
+ if (!lazy_init (spell, spell->active_lang))
+ return NULL;
+
+ return spell->active_lang;
+}
+
+gboolean
+gedit_spell_checker_check_word (GeditSpellChecker *spell,
+ const gchar *word,
+ gssize len)
+{
+ gint enchant_result;
+ gboolean res = FALSE;
+
+ g_return_val_if_fail (GEDIT_IS_SPELL_CHECKER (spell), FALSE);
+ g_return_val_if_fail (word != NULL, FALSE);
+
+ if (!lazy_init (spell, spell->active_lang))
+ return FALSE;
+
+ if (len < 0)
+ len = strlen (word);
+
+ if (strcmp (word, "gedit") == 0)
+ return TRUE;
+
+ if (gedit_spell_utils_is_digit (word, len))
+ return TRUE;
+
+ g_return_val_if_fail (spell->dict != NULL, FALSE);
+ enchant_result = enchant_dict_check (spell->dict, word, len);
+
+ switch (enchant_result)
+ {
+ case -1:
+ /* error */
+ res = FALSE;
+
+ g_warning ("Spell checker plugin: error checking word '%s' (%s).",
+ word, enchant_dict_get_error (spell->dict));
+
+ break;
+ case 1:
+ /* it is not in the directory */
+ res = FALSE;
+ break;
+ case 0:
+ /* is is in the directory */
+ res = TRUE;
+ break;
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ return res;
+}
+
+
+/* return NULL on error or if no suggestions are found */
+GSList *
+gedit_spell_checker_get_suggestions (GeditSpellChecker *spell,
+ const gchar *word,
+ gssize len)
+{
+ gchar **suggestions;
+ size_t n_suggestions = 0;
+ GSList *suggestions_list = NULL;
+ gint i;
+
+ g_return_val_if_fail (GEDIT_IS_SPELL_CHECKER (spell), NULL);
+ g_return_val_if_fail (word != NULL, NULL);
+
+ if (!lazy_init (spell, spell->active_lang))
+ return NULL;
+
+ g_return_val_if_fail (spell->dict != NULL, NULL);
+
+ if (len < 0)
+ len = strlen (word);
+
+ suggestions = enchant_dict_suggest (spell->dict, word, len, &n_suggestions);
+
+ if (n_suggestions == 0)
+ return NULL;
+
+ g_return_val_if_fail (suggestions != NULL, NULL);
+
+ for (i = 0; i < (gint)n_suggestions; i++)
+ {
+ suggestions_list = g_slist_prepend (suggestions_list,
+ suggestions[i]);
+ }
+
+ /* The single suggestions will be freed by the caller */
+ g_free (suggestions);
+
+ suggestions_list = g_slist_reverse (suggestions_list);
+
+ return suggestions_list;
+}
+
+gboolean
+gedit_spell_checker_add_word_to_personal (GeditSpellChecker *spell,
+ const gchar *word,
+ gssize len)
+{
+ g_return_val_if_fail (GEDIT_IS_SPELL_CHECKER (spell), FALSE);
+ g_return_val_if_fail (word != NULL, FALSE);
+
+ if (!lazy_init (spell, spell->active_lang))
+ return FALSE;
+
+ g_return_val_if_fail (spell->dict != NULL, FALSE);
+
+ if (len < 0)
+ len = strlen (word);
+
+ enchant_dict_add_to_pwl (spell->dict, word, len);
+
+ g_signal_emit (G_OBJECT (spell), signals[ADD_WORD_TO_PERSONAL], 0, word, len);
+
+ return TRUE;
+}
+
+gboolean
+gedit_spell_checker_add_word_to_session (GeditSpellChecker *spell,
+ const gchar *word,
+ gssize len)
+{
+ g_return_val_if_fail (GEDIT_IS_SPELL_CHECKER (spell), FALSE);
+ g_return_val_if_fail (word != NULL, FALSE);
+
+ if (!lazy_init (spell, spell->active_lang))
+ return FALSE;
+
+ g_return_val_if_fail (spell->dict != NULL, FALSE);
+
+ if (len < 0)
+ len = strlen (word);
+
+ enchant_dict_add_to_session (spell->dict, word, len);
+
+ g_signal_emit (G_OBJECT (spell), signals[ADD_WORD_TO_SESSION], 0, word, len);
+
+ return TRUE;
+}
+
+gboolean
+gedit_spell_checker_clear_session (GeditSpellChecker *spell)
+{
+ g_return_val_if_fail (GEDIT_IS_SPELL_CHECKER (spell), FALSE);
+
+ /* free and re-request dictionary */
+ if (spell->dict != NULL)
+ {
+ enchant_broker_free_dict (spell->broker, spell->dict);
+ spell->dict = NULL;
+ }
+
+ if (!lazy_init (spell, spell->active_lang))
+ return FALSE;
+
+ g_signal_emit (G_OBJECT (spell), signals[CLEAR_SESSION], 0);
+
+ return TRUE;
+}
+
+/*
+ * Informs dictionary, that word 'word' will be replaced/corrected by word
+ * 'replacement'
+ */
+gboolean
+gedit_spell_checker_set_correction (GeditSpellChecker *spell,
+ const gchar *word,
+ gssize w_len,
+ const gchar *replacement,
+ gssize r_len)
+{
+ g_return_val_if_fail (GEDIT_IS_SPELL_CHECKER (spell), FALSE);
+ g_return_val_if_fail (word != NULL, FALSE);
+ g_return_val_if_fail (replacement != NULL, FALSE);
+
+ if (!lazy_init (spell, spell->active_lang))
+ return FALSE;
+
+ g_return_val_if_fail (spell->dict != NULL, FALSE);
+
+ if (w_len < 0)
+ w_len = strlen (word);
+
+ if (r_len < 0)
+ r_len = strlen (replacement);
+
+ enchant_dict_store_replacement (spell->dict,
+ word,
+ w_len,
+ replacement,
+ r_len);
+
+ return TRUE;
+}
+
diff --git a/plugins/spell/gedit-spell-checker.h b/plugins/spell/gedit-spell-checker.h
new file mode 100755
index 00000000..0cc69d31
--- /dev/null
+++ b/plugins/spell/gedit-spell-checker.h
@@ -0,0 +1,109 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gedit-spell-checker.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002-2006 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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.
+ */
+
+/*
+ * Modified by the gedit Team, 2002. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ */
+
+#ifndef __GEDIT_SPELL_CHECKER_H__
+#define __GEDIT_SPELL_CHECKER_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gedit-spell-checker-language.h"
+
+G_BEGIN_DECLS
+
+#define GEDIT_TYPE_SPELL_CHECKER (gedit_spell_checker_get_type ())
+#define GEDIT_SPELL_CHECKER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_SPELL_CHECKER, GeditSpellChecker))
+#define GEDIT_SPELL_CHECKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_SPELL_CHECKER, GeditSpellChecker))
+#define GEDIT_IS_SPELL_CHECKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_SPELL_CHECKER))
+#define GEDIT_IS_SPELL_CHECKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_SPELL_CHECKER))
+#define GEDIT_SPELL_CHECKER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_SPELL_CHECKER, GeditSpellChecker))
+
+typedef struct _GeditSpellChecker GeditSpellChecker;
+
+typedef struct _GeditSpellCheckerClass GeditSpellCheckerClass;
+
+struct _GeditSpellCheckerClass
+{
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (*add_word_to_personal) (GeditSpellChecker *spell,
+ const gchar *word,
+ gint len);
+
+ void (*add_word_to_session) (GeditSpellChecker *spell,
+ const gchar *word,
+ gint len);
+
+ void (*set_language) (GeditSpellChecker *spell,
+ const GeditSpellCheckerLanguage *lang);
+
+ void (*clear_session) (GeditSpellChecker *spell);
+};
+
+
+GType gedit_spell_checker_get_type (void) G_GNUC_CONST;
+
+/* Constructors */
+GeditSpellChecker *gedit_spell_checker_new (void);
+
+gboolean gedit_spell_checker_set_language (GeditSpellChecker *spell,
+ const GeditSpellCheckerLanguage *lang);
+const GeditSpellCheckerLanguage
+ *gedit_spell_checker_get_language (GeditSpellChecker *spell);
+
+gboolean gedit_spell_checker_check_word (GeditSpellChecker *spell,
+ const gchar *word,
+ gssize len);
+
+GSList *gedit_spell_checker_get_suggestions (GeditSpellChecker *spell,
+ const gchar *word,
+ gssize len);
+
+gboolean gedit_spell_checker_add_word_to_personal
+ (GeditSpellChecker *spell,
+ const gchar *word,
+ gssize len);
+
+gboolean gedit_spell_checker_add_word_to_session
+ (GeditSpellChecker *spell,
+ const gchar *word,
+ gssize len);
+
+gboolean gedit_spell_checker_clear_session (GeditSpellChecker *spell);
+
+gboolean gedit_spell_checker_set_correction (GeditSpellChecker *spell,
+ const gchar *word,
+ gssize w_len,
+ const gchar *replacement,
+ gssize r_len);
+G_END_DECLS
+
+#endif /* __GEDIT_SPELL_CHECKER_H__ */
+
diff --git a/plugins/spell/gedit-spell-language-dialog.c b/plugins/spell/gedit-spell-language-dialog.c
new file mode 100755
index 00000000..1abba17f
--- /dev/null
+++ b/plugins/spell/gedit-spell-language-dialog.c
@@ -0,0 +1,309 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gedit-spell-language-dialog.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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.
+ */
+
+/*
+ * Modified by the gedit Team, 2002. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gedit/gedit-utils.h>
+#include <gedit/gedit-help.h>
+#include "gedit-spell-language-dialog.h"
+#include "gedit-spell-checker-language.h"
+
+
+enum
+{
+ COLUMN_LANGUAGE_NAME = 0,
+ COLUMN_LANGUAGE_POINTER,
+ ENCODING_NUM_COLS
+};
+
+
+struct _GeditSpellLanguageDialog
+{
+ GtkDialog dialog;
+
+ GtkWidget *languages_treeview;
+ GtkTreeModel *model;
+};
+
+G_DEFINE_TYPE(GeditSpellLanguageDialog, gedit_spell_language_dialog, GTK_TYPE_DIALOG)
+
+
+static void
+gedit_spell_language_dialog_class_init (GeditSpellLanguageDialogClass *klass)
+{
+ /* GObjectClass *object_class = G_OBJECT_CLASS (klass); */
+}
+
+static void
+dialog_response_handler (GtkDialog *dlg,
+ gint res_id)
+{
+ if (res_id == GTK_RESPONSE_HELP)
+ {
+ gedit_help_display (GTK_WINDOW (dlg),
+ NULL,
+ "gedit-spell-checker-plugin");
+
+ g_signal_stop_emission_by_name (dlg, "response");
+ }
+}
+
+static void
+scroll_to_selected (GtkTreeView *tree_view)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model (tree_view);
+ g_return_if_fail (model != NULL);
+
+ /* Scroll to selected */
+ selection = gtk_tree_view_get_selection (tree_view);
+ g_return_if_fail (selection != NULL);
+
+ if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+ {
+ GtkTreePath* path;
+
+ path = gtk_tree_model_get_path (model, &iter);
+ g_return_if_fail (path != NULL);
+
+ gtk_tree_view_scroll_to_cell (tree_view,
+ path, NULL, TRUE, 1.0, 0.0);
+ gtk_tree_path_free (path);
+ }
+}
+
+static void
+language_row_activated (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ GeditSpellLanguageDialog *dialog)
+{
+ gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+}
+
+static void
+create_dialog (GeditSpellLanguageDialog *dlg,
+ const gchar *data_dir)
+{
+ GtkWidget *error_widget;
+ GtkWidget *content;
+ gboolean ret;
+ GtkCellRenderer *cell;
+ GtkTreeViewColumn *column;
+ gchar *ui_file;
+ gchar *root_objects[] = {
+ "content",
+ NULL
+ };
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dlg),
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_OK,
+ GTK_STOCK_HELP,
+ GTK_RESPONSE_HELP,
+ NULL);
+
+ gtk_window_set_title (GTK_WINDOW (dlg), _("Set language"));
+ gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE);
+ gtk_window_set_modal (GTK_WINDOW (dlg), TRUE);
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (dlg), TRUE);
+
+ /* HIG defaults */
+ gtk_container_set_border_width (GTK_CONTAINER (dlg), 5);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ 2); /* 2 * 5 + 2 = 12 */
+ gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_action_area (GTK_DIALOG (dlg))),
+ 5);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_action_area (GTK_DIALOG (dlg))),
+ 6);
+
+ g_signal_connect (dlg,
+ "response",
+ G_CALLBACK (dialog_response_handler),
+ NULL);
+
+ ui_file = g_build_filename (data_dir, "languages-dialog.ui", NULL);
+ ret = gedit_utils_get_ui_objects (ui_file,
+ root_objects,
+ &error_widget,
+ "content", &content,
+ "languages_treeview", &dlg->languages_treeview,
+ NULL);
+ g_free (ui_file);
+
+ if (!ret)
+ {
+ gtk_widget_show (error_widget);
+
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ error_widget,
+ TRUE, TRUE, 0);
+
+ return;
+ }
+
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ content, TRUE, TRUE, 0);
+ g_object_unref (content);
+ gtk_container_set_border_width (GTK_CONTAINER (content), 5);
+
+ dlg->model = GTK_TREE_MODEL (gtk_list_store_new (ENCODING_NUM_COLS,
+ G_TYPE_STRING,
+ G_TYPE_POINTER));
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (dlg->languages_treeview),
+ dlg->model);
+
+ g_object_unref (dlg->model);
+
+ /* Add the encoding column */
+ cell = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Languages"),
+ cell,
+ "text",
+ COLUMN_LANGUAGE_NAME,
+ NULL);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (dlg->languages_treeview),
+ column);
+
+ gtk_tree_view_set_search_column (GTK_TREE_VIEW (dlg->languages_treeview),
+ COLUMN_LANGUAGE_NAME);
+
+ g_signal_connect (dlg->languages_treeview,
+ "realize",
+ G_CALLBACK (scroll_to_selected),
+ dlg);
+ g_signal_connect (dlg->languages_treeview,
+ "row-activated",
+ G_CALLBACK (language_row_activated),
+ dlg);
+}
+
+static void
+gedit_spell_language_dialog_init (GeditSpellLanguageDialog *dlg)
+{
+
+}
+
+static void
+populate_language_list (GeditSpellLanguageDialog *dlg,
+ const GeditSpellCheckerLanguage *cur_lang)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ const GSList* langs;
+
+ /* create list store */
+ store = GTK_LIST_STORE (dlg->model);
+
+ langs = gedit_spell_checker_get_available_languages ();
+
+ while (langs)
+ {
+ const gchar *name;
+
+ name = gedit_spell_checker_language_to_string ((const GeditSpellCheckerLanguage*)langs->data);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COLUMN_LANGUAGE_NAME, name,
+ COLUMN_LANGUAGE_POINTER, langs->data,
+ -1);
+
+ if (langs->data == cur_lang)
+ {
+ GtkTreeSelection *selection;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dlg->languages_treeview));
+ g_return_if_fail (selection != NULL);
+
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+
+ langs = g_slist_next (langs);
+ }
+}
+
+GtkWidget *
+gedit_spell_language_dialog_new (GtkWindow *parent,
+ const GeditSpellCheckerLanguage *cur_lang,
+ const gchar *data_dir)
+{
+ GeditSpellLanguageDialog *dlg;
+
+ g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
+
+ dlg = g_object_new (GEDIT_TYPE_SPELL_LANGUAGE_DIALOG, NULL);
+
+ create_dialog (dlg, data_dir);
+
+ populate_language_list (dlg, cur_lang);
+
+ gtk_window_set_transient_for (GTK_WINDOW (dlg), parent);
+ gtk_widget_grab_focus (dlg->languages_treeview);
+
+ return GTK_WIDGET (dlg);
+}
+
+const GeditSpellCheckerLanguage *
+gedit_spell_language_get_selected_language (GeditSpellLanguageDialog *dlg)
+{
+ GValue value = {0, };
+ const GeditSpellCheckerLanguage* lang;
+
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dlg->languages_treeview));
+ g_return_val_if_fail (selection != NULL, NULL);
+
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return NULL;
+
+ gtk_tree_model_get_value (dlg->model,
+ &iter,
+ COLUMN_LANGUAGE_POINTER,
+ &value);
+
+ lang = (const GeditSpellCheckerLanguage* ) g_value_get_pointer (&value);
+
+ return lang;
+}
+
diff --git a/plugins/spell/gedit-spell-language-dialog.h b/plugins/spell/gedit-spell-language-dialog.h
new file mode 100755
index 00000000..4ae9c97d
--- /dev/null
+++ b/plugins/spell/gedit-spell-language-dialog.h
@@ -0,0 +1,67 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gedit-spell-language-dialog.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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.
+ */
+
+/*
+ * Modified by the gedit Team, 2002. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ */
+
+#ifndef __GEDIT_SPELL_LANGUAGE_DIALOG_H__
+#define __GEDIT_SPELL_LANGUAGE_DIALOG_H__
+
+#include <gtk/gtk.h>
+#include "gedit-spell-checker-language.h"
+
+G_BEGIN_DECLS
+
+#define GEDIT_TYPE_SPELL_LANGUAGE_DIALOG (gedit_spell_language_dialog_get_type())
+#define GEDIT_SPELL_LANGUAGE_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_SPELL_LANGUAGE_DIALOG, GeditSpellLanguageDialog))
+#define GEDIT_SPELL_LANGUAGE_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_SPELL_LANGUAGE_DIALOG, GeditSpellLanguageDialogClass))
+#define GEDIT_IS_SPELL_LANGUAGE_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_SPELL_LANGUAGE_DIALOG))
+#define GEDIT_IS_SPELL_LANGUAGE_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_SPELL_LANGUAGE_DIALOG))
+#define GEDIT_SPELL_LANGUAGE_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_SPELL_LANGUAGE_DIALOG, GeditSpellLanguageDialogClass))
+
+
+typedef struct _GeditSpellLanguageDialog GeditSpellLanguageDialog;
+
+typedef struct _GeditSpellLanguageDialogClass GeditSpellLanguageDialogClass;
+
+struct _GeditSpellLanguageDialogClass
+{
+ GtkDialogClass parent_class;
+};
+
+GType gedit_spell_language_dialog_get_type (void) G_GNUC_CONST;
+
+GtkWidget *gedit_spell_language_dialog_new (GtkWindow *parent,
+ const GeditSpellCheckerLanguage *cur_lang,
+ const gchar *data_dir);
+
+const GeditSpellCheckerLanguage *
+ gedit_spell_language_get_selected_language (GeditSpellLanguageDialog *dlg);
+
+G_END_DECLS
+
+#endif /* __GEDIT_SPELL_LANGUAGE_DIALOG_H__ */
+
diff --git a/plugins/spell/gedit-spell-marshal.list b/plugins/spell/gedit-spell-marshal.list
new file mode 100755
index 00000000..007dcf7d
--- /dev/null
+++ b/plugins/spell/gedit-spell-marshal.list
@@ -0,0 +1,6 @@
+VOID:STRING
+VOID:STRING,STRING
+VOID:STRING,INT
+VOID:POINTER
+VOID:VOID
+
diff --git a/plugins/spell/gedit-spell-plugin.c b/plugins/spell/gedit-spell-plugin.c
new file mode 100755
index 00000000..6ef78e75
--- /dev/null
+++ b/plugins/spell/gedit-spell-plugin.c
@@ -0,0 +1,1217 @@
+/*
+ * gedit-spell-plugin.c
+ *
+ * Copyright (C) 2002-2005 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 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.
+ *
+ * $Id$
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gedit-spell-plugin.h"
+#include "gedit-spell-utils.h"
+
+#include <string.h> /* For strlen */
+
+#include <glib/gi18n.h>
+#include <gmodule.h>
+
+#include <gedit/gedit-debug.h>
+#include <gedit/gedit-prefs-manager.h>
+#include <gedit/gedit-statusbar.h>
+
+#include "gedit-spell-checker.h"
+#include "gedit-spell-checker-dialog.h"
+#include "gedit-spell-language-dialog.h"
+#include "gedit-automatic-spell-checker.h"
+
+#ifdef G_OS_WIN32
+#include <gedit/gedit-metadata-manager.h>
+#define GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE "spell-language"
+#define GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED "spell-enabled"
+#else
+#define GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE "metadata::gedit-spell-language"
+#define GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED "metadata::gedit-spell-enabled"
+#endif
+
+#define WINDOW_DATA_KEY "GeditSpellPluginWindowData"
+#define MENU_PATH "/MenuBar/ToolsMenu/ToolsOps_1"
+
+#define GEDIT_SPELL_PLUGIN_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \
+ GEDIT_TYPE_SPELL_PLUGIN, \
+ GeditSpellPluginPrivate))
+
+GEDIT_PLUGIN_REGISTER_TYPE(GeditSpellPlugin, gedit_spell_plugin)
+
+typedef struct
+{
+ GtkActionGroup *action_group;
+ guint ui_id;
+ guint message_cid;
+ gulong tab_added_id;
+ gulong tab_removed_id;
+} WindowData;
+
+typedef struct
+{
+ GeditPlugin *plugin;
+ GeditWindow *window;
+} ActionData;
+
+static void spell_cb (GtkAction *action, ActionData *action_data);
+static void set_language_cb (GtkAction *action, ActionData *action_data);
+static void auto_spell_cb (GtkAction *action, GeditWindow *window);
+
+/* UI actions. */
+static const GtkActionEntry action_entries[] =
+{
+ { "CheckSpell",
+ GTK_STOCK_SPELL_CHECK,
+ N_("_Check Spelling..."),
+ "<shift>F7",
+ N_("Check the current document for incorrect spelling"),
+ G_CALLBACK (spell_cb)
+ },
+
+ { "ConfigSpell",
+ NULL,
+ N_("Set _Language..."),
+ NULL,
+ N_("Set the language of the current document"),
+ G_CALLBACK (set_language_cb)
+ }
+};
+
+static const GtkToggleActionEntry toggle_action_entries[] =
+{
+ { "AutoSpell",
+ NULL,
+ N_("_Autocheck Spelling"),
+ NULL,
+ N_("Automatically spell-check the current document"),
+ G_CALLBACK (auto_spell_cb),
+ FALSE
+ }
+};
+
+typedef struct _CheckRange CheckRange;
+
+struct _CheckRange
+{
+ GtkTextMark *start_mark;
+ GtkTextMark *end_mark;
+
+ gint mw_start; /* misspelled word start */
+ gint mw_end; /* end */
+
+ GtkTextMark *current_mark;
+};
+
+static GQuark spell_checker_id = 0;
+static GQuark check_range_id = 0;
+
+static void
+gedit_spell_plugin_init (GeditSpellPlugin *plugin)
+{
+ gedit_debug_message (DEBUG_PLUGINS, "GeditSpellPlugin initializing");
+}
+
+static void
+gedit_spell_plugin_finalize (GObject *object)
+{
+ gedit_debug_message (DEBUG_PLUGINS, "GeditSpellPlugin finalizing");
+
+ G_OBJECT_CLASS (gedit_spell_plugin_parent_class)->finalize (object);
+}
+
+static void
+set_spell_language_cb (GeditSpellChecker *spell,
+ const GeditSpellCheckerLanguage *lang,
+ GeditDocument *doc)
+{
+ const gchar *key;
+
+ g_return_if_fail (GEDIT_IS_DOCUMENT (doc));
+ g_return_if_fail (lang != NULL);
+
+ key = gedit_spell_checker_language_to_key (lang);
+ g_return_if_fail (key != NULL);
+
+ gedit_document_set_metadata (doc, GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE,
+ key, NULL);
+}
+
+static void
+set_language_from_metadata (GeditSpellChecker *spell,
+ GeditDocument *doc)
+{
+ const GeditSpellCheckerLanguage *lang = NULL;
+ gchar *value = NULL;
+
+ value = gedit_document_get_metadata (doc, GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE);
+
+ if (value != NULL)
+ {
+ lang = gedit_spell_checker_language_from_key (value);
+ g_free (value);
+ }
+
+ if (lang != NULL)
+ {
+ g_signal_handlers_block_by_func (spell, set_spell_language_cb, doc);
+ gedit_spell_checker_set_language (spell, lang);
+ g_signal_handlers_unblock_by_func (spell, set_spell_language_cb, doc);
+ }
+}
+
+static GeditSpellChecker *
+get_spell_checker_from_document (GeditDocument *doc)
+{
+ GeditSpellChecker *spell;
+ gpointer data;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ g_return_val_if_fail (doc != NULL, NULL);
+
+ data = g_object_get_qdata (G_OBJECT (doc), spell_checker_id);
+
+ if (data == NULL)
+ {
+ spell = gedit_spell_checker_new ();
+
+ set_language_from_metadata (spell, doc);
+
+ g_object_set_qdata_full (G_OBJECT (doc),
+ spell_checker_id,
+ spell,
+ (GDestroyNotify) g_object_unref);
+
+ g_signal_connect (spell,
+ "set_language",
+ G_CALLBACK (set_spell_language_cb),
+ doc);
+ }
+ else
+ {
+ g_return_val_if_fail (GEDIT_IS_SPELL_CHECKER (data), NULL);
+ spell = GEDIT_SPELL_CHECKER (data);
+ }
+
+ return spell;
+}
+
+static CheckRange *
+get_check_range (GeditDocument *doc)
+{
+ CheckRange *range;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ g_return_val_if_fail (doc != NULL, NULL);
+
+ range = (CheckRange *) g_object_get_qdata (G_OBJECT (doc), check_range_id);
+
+ return range;
+}
+
+static void
+update_current (GeditDocument *doc,
+ gint current)
+{
+ CheckRange *range;
+ GtkTextIter iter;
+ GtkTextIter end_iter;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ g_return_if_fail (doc != NULL);
+ g_return_if_fail (current >= 0);
+
+ range = get_check_range (doc);
+ g_return_if_fail (range != NULL);
+
+ gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc),
+ &iter, current);
+
+ if (!gtk_text_iter_inside_word (&iter))
+ {
+ /* if we're not inside a word,
+ * we must be in some spaces.
+ * skip forward to the beginning of the next word. */
+ if (!gtk_text_iter_is_end (&iter))
+ {
+ gtk_text_iter_forward_word_end (&iter);
+ gtk_text_iter_backward_word_start (&iter);
+ }
+ }
+ else
+ {
+ if (!gtk_text_iter_starts_word (&iter))
+ gtk_text_iter_backward_word_start (&iter);
+ }
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (doc),
+ &end_iter,
+ range->end_mark);
+
+ if (gtk_text_iter_compare (&end_iter, &iter) < 0)
+ {
+ gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
+ range->current_mark,
+ &end_iter);
+ }
+ else
+ {
+ gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
+ range->current_mark,
+ &iter);
+ }
+}
+
+static void
+set_check_range (GeditDocument *doc,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ CheckRange *range;
+ GtkTextIter iter;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ range = get_check_range (doc);
+
+ if (range == NULL)
+ {
+ gedit_debug_message (DEBUG_PLUGINS, "There was not a previous check range");
+
+ gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &iter);
+
+ range = g_new0 (CheckRange, 1);
+
+ range->start_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (doc),
+ "check_range_start_mark", &iter, TRUE);
+
+ range->end_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (doc),
+ "check_range_end_mark", &iter, FALSE);
+
+ range->current_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (doc),
+ "check_range_current_mark", &iter, TRUE);
+
+ g_object_set_qdata_full (G_OBJECT (doc),
+ check_range_id,
+ range,
+ (GDestroyNotify)g_free);
+ }
+
+ if (gedit_spell_utils_skip_no_spell_check (start, end))
+ {
+ if (!gtk_text_iter_inside_word (end))
+ {
+ /* if we're neither inside a word,
+ * we must be in some spaces.
+ * skip backward to the end of the previous word. */
+ if (!gtk_text_iter_is_end (end))
+ {
+ gtk_text_iter_backward_word_start (end);
+ gtk_text_iter_forward_word_end (end);
+ }
+ }
+ else
+ {
+ if (!gtk_text_iter_ends_word (end))
+ gtk_text_iter_forward_word_end (end);
+ }
+ }
+ else
+ {
+ /* no spell checking in the specified range */
+ start = end;
+ }
+
+ gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
+ range->start_mark,
+ start);
+ gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
+ range->end_mark,
+ end);
+
+ range->mw_start = -1;
+ range->mw_end = -1;
+
+ update_current (doc, gtk_text_iter_get_offset (start));
+}
+
+static gchar *
+get_current_word (GeditDocument *doc, gint *start, gint *end)
+{
+ const CheckRange *range;
+ GtkTextIter end_iter;
+ GtkTextIter current_iter;
+ gint range_end;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ g_return_val_if_fail (doc != NULL, NULL);
+ g_return_val_if_fail (start != NULL, NULL);
+ g_return_val_if_fail (end != NULL, NULL);
+
+ range = get_check_range (doc);
+ g_return_val_if_fail (range != NULL, NULL);
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (doc),
+ &end_iter, range->end_mark);
+
+ range_end = gtk_text_iter_get_offset (&end_iter);
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (doc),
+ &current_iter, range->current_mark);
+
+ end_iter = current_iter;
+
+ if (!gtk_text_iter_is_end (&end_iter))
+ {
+ gedit_debug_message (DEBUG_PLUGINS, "Current is not end");
+
+ gtk_text_iter_forward_word_end (&end_iter);
+ }
+
+ *start = gtk_text_iter_get_offset (&current_iter);
+ *end = MIN (gtk_text_iter_get_offset (&end_iter), range_end);
+
+ gedit_debug_message (DEBUG_PLUGINS, "Current word extends [%d, %d]", *start, *end);
+
+ if (!(*start < *end))
+ return NULL;
+
+ return gtk_text_buffer_get_slice (GTK_TEXT_BUFFER (doc),
+ &current_iter,
+ &end_iter,
+ TRUE);
+}
+
+static gboolean
+goto_next_word (GeditDocument *doc)
+{
+ CheckRange *range;
+ GtkTextIter current_iter;
+ GtkTextIter old_current_iter;
+ GtkTextIter end_iter;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ g_return_val_if_fail (doc != NULL, FALSE);
+
+ range = get_check_range (doc);
+ g_return_val_if_fail (range != NULL, FALSE);
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (doc),
+ &current_iter,
+ range->current_mark);
+ gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &end_iter);
+
+ old_current_iter = current_iter;
+
+ gtk_text_iter_forward_word_ends (&current_iter, 2);
+ gtk_text_iter_backward_word_start (&current_iter);
+
+ if (gedit_spell_utils_skip_no_spell_check (&current_iter, &end_iter) &&
+ (gtk_text_iter_compare (&old_current_iter, &current_iter) < 0) &&
+ (gtk_text_iter_compare (&current_iter, &end_iter) < 0))
+ {
+ update_current (doc, gtk_text_iter_get_offset (&current_iter));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gchar *
+get_next_misspelled_word (GeditView *view)
+{
+ GeditDocument *doc;
+ CheckRange *range;
+ gint start, end;
+ gchar *word;
+ GeditSpellChecker *spell;
+
+ g_return_val_if_fail (view != NULL, NULL);
+
+ doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+ g_return_val_if_fail (doc != NULL, NULL);
+
+ range = get_check_range (doc);
+ g_return_val_if_fail (range != NULL, NULL);
+
+ spell = get_spell_checker_from_document (doc);
+ g_return_val_if_fail (spell != NULL, NULL);
+
+ word = get_current_word (doc, &start, &end);
+ if (word == NULL)
+ return NULL;
+
+ gedit_debug_message (DEBUG_PLUGINS, "Word to check: %s", word);
+
+ while (gedit_spell_checker_check_word (spell, word, -1))
+ {
+ g_free (word);
+
+ if (!goto_next_word (doc))
+ return NULL;
+
+ /* may return null if we reached the end of the selection */
+ word = get_current_word (doc, &start, &end);
+ if (word == NULL)
+ return NULL;
+
+ gedit_debug_message (DEBUG_PLUGINS, "Word to check: %s", word);
+ }
+
+ if (!goto_next_word (doc))
+ update_current (doc, gtk_text_buffer_get_char_count (GTK_TEXT_BUFFER (doc)));
+
+ if (word != NULL)
+ {
+ GtkTextIter s, e;
+
+ range->mw_start = start;
+ range->mw_end = end;
+
+ gedit_debug_message (DEBUG_PLUGINS, "Select [%d, %d]", start, end);
+
+ gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &s, start);
+ gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &e, end);
+
+ gtk_text_buffer_select_range (GTK_TEXT_BUFFER (doc), &s, &e);
+
+ gedit_view_scroll_to_cursor (view);
+ }
+ else
+ {
+ range->mw_start = -1;
+ range->mw_end = -1;
+ }
+
+ return word;
+}
+
+static void
+ignore_cb (GeditSpellCheckerDialog *dlg,
+ const gchar *w,
+ GeditView *view)
+{
+ gchar *word = NULL;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ g_return_if_fail (w != NULL);
+ g_return_if_fail (view != NULL);
+
+ word = get_next_misspelled_word (view);
+ if (word == NULL)
+ {
+ gedit_spell_checker_dialog_set_completed (dlg);
+
+ return;
+ }
+
+ gedit_spell_checker_dialog_set_misspelled_word (GEDIT_SPELL_CHECKER_DIALOG (dlg),
+ word,
+ -1);
+
+ g_free (word);
+}
+
+static void
+change_cb (GeditSpellCheckerDialog *dlg,
+ const gchar *word,
+ const gchar *change,
+ GeditView *view)
+{
+ GeditDocument *doc;
+ CheckRange *range;
+ gchar *w = NULL;
+ GtkTextIter start, end;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ g_return_if_fail (view != NULL);
+ g_return_if_fail (word != NULL);
+ g_return_if_fail (change != NULL);
+
+ doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+ g_return_if_fail (doc != NULL);
+
+ range = get_check_range (doc);
+ g_return_if_fail (range != NULL);
+
+ gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &start, range->mw_start);
+ if (range->mw_end < 0)
+ gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &end);
+ else
+ gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &end, range->mw_end);
+
+ w = gtk_text_buffer_get_slice (GTK_TEXT_BUFFER (doc), &start, &end, TRUE);
+ g_return_if_fail (w != NULL);
+
+ if (strcmp (w, word) != 0)
+ {
+ g_free (w);
+ return;
+ }
+
+ g_free (w);
+
+ gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER(doc));
+
+ gtk_text_buffer_delete (GTK_TEXT_BUFFER (doc), &start, &end);
+ gtk_text_buffer_insert (GTK_TEXT_BUFFER (doc), &start, change, -1);
+
+ gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER(doc));
+
+ update_current (doc, range->mw_start + g_utf8_strlen (change, -1));
+
+ /* go to next misspelled word */
+ ignore_cb (dlg, word, view);
+}
+
+static void
+change_all_cb (GeditSpellCheckerDialog *dlg,
+ const gchar *word,
+ const gchar *change,
+ GeditView *view)
+{
+ GeditDocument *doc;
+ CheckRange *range;
+ gchar *w = NULL;
+ GtkTextIter start, end;
+ gint flags = 0;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ g_return_if_fail (view != NULL);
+ g_return_if_fail (word != NULL);
+ g_return_if_fail (change != NULL);
+
+ doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+ g_return_if_fail (doc != NULL);
+
+ range = get_check_range (doc);
+ g_return_if_fail (range != NULL);
+
+ gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &start, range->mw_start);
+ if (range->mw_end < 0)
+ gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &end);
+ else
+ gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &end, range->mw_end);
+
+ w = gtk_text_buffer_get_slice (GTK_TEXT_BUFFER (doc), &start, &end, TRUE);
+ g_return_if_fail (w != NULL);
+
+ if (strcmp (w, word) != 0)
+ {
+ g_free (w);
+ return;
+ }
+
+ g_free (w);
+
+ GEDIT_SEARCH_SET_CASE_SENSITIVE (flags, TRUE);
+ GEDIT_SEARCH_SET_ENTIRE_WORD (flags, TRUE);
+
+ /* CHECK: currently this function does escaping etc */
+ gedit_document_replace_all (doc, word, change, flags);
+
+ update_current (doc, range->mw_start + g_utf8_strlen (change, -1));
+
+ /* go to next misspelled word */
+ ignore_cb (dlg, word, view);
+}
+
+static void
+add_word_cb (GeditSpellCheckerDialog *dlg,
+ const gchar *word,
+ GeditView *view)
+{
+ g_return_if_fail (view != NULL);
+ g_return_if_fail (word != NULL);
+
+ /* go to next misspelled word */
+ ignore_cb (dlg, word, view);
+}
+
+static void
+language_dialog_response (GtkDialog *dlg,
+ gint res_id,
+ GeditSpellChecker *spell)
+{
+ if (res_id == GTK_RESPONSE_OK)
+ {
+ const GeditSpellCheckerLanguage *lang;
+
+ lang = gedit_spell_language_get_selected_language (GEDIT_SPELL_LANGUAGE_DIALOG (dlg));
+ if (lang != NULL)
+ gedit_spell_checker_set_language (spell, lang);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dlg));
+}
+
+static void
+set_language_cb (GtkAction *action,
+ ActionData *action_data)
+{
+ GeditDocument *doc;
+ GeditSpellChecker *spell;
+ const GeditSpellCheckerLanguage *lang;
+ GtkWidget *dlg;
+ GtkWindowGroup *wg;
+ gchar *data_dir;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ doc = gedit_window_get_active_document (action_data->window);
+ g_return_if_fail (doc != NULL);
+
+ spell = get_spell_checker_from_document (doc);
+ g_return_if_fail (spell != NULL);
+
+ lang = gedit_spell_checker_get_language (spell);
+
+ data_dir = gedit_plugin_get_data_dir (action_data->plugin);
+ dlg = gedit_spell_language_dialog_new (GTK_WINDOW (action_data->window),
+ lang,
+ data_dir);
+ g_free (data_dir);
+
+ wg = gedit_window_get_group (action_data->window);
+
+ gtk_window_group_add_window (wg, GTK_WINDOW (dlg));
+
+ gtk_window_set_modal (GTK_WINDOW (dlg), TRUE);
+
+ g_signal_connect (dlg,
+ "response",
+ G_CALLBACK (language_dialog_response),
+ spell);
+
+ gtk_widget_show (dlg);
+}
+
+static void
+spell_cb (GtkAction *action,
+ ActionData *action_data)
+{
+ GeditView *view;
+ GeditDocument *doc;
+ GeditSpellChecker *spell;
+ GtkWidget *dlg;
+ GtkTextIter start, end;
+ gchar *word;
+ gchar *data_dir;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ view = gedit_window_get_active_view (action_data->window);
+ g_return_if_fail (view != NULL);
+
+ doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+ g_return_if_fail (doc != NULL);
+
+ spell = get_spell_checker_from_document (doc);
+ g_return_if_fail (spell != NULL);
+
+ if (gtk_text_buffer_get_char_count (GTK_TEXT_BUFFER (doc)) <= 0)
+ {
+ WindowData *data;
+ GtkWidget *statusbar;
+
+ data = (WindowData *) g_object_get_data (G_OBJECT (action_data->window),
+ WINDOW_DATA_KEY);
+ g_return_if_fail (data != NULL);
+
+ statusbar = gedit_window_get_statusbar (action_data->window);
+ gedit_statusbar_flash_message (GEDIT_STATUSBAR (statusbar),
+ data->message_cid,
+ _("The document is empty."));
+
+ return;
+ }
+
+ if (!gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc),
+ &start,
+ &end))
+ {
+ /* no selection, get the whole doc */
+ gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc),
+ &start,
+ &end);
+ }
+
+ set_check_range (doc, &start, &end);
+
+ word = get_next_misspelled_word (view);
+ if (word == NULL)
+ {
+ WindowData *data;
+ GtkWidget *statusbar;
+
+ data = (WindowData *) g_object_get_data (G_OBJECT (action_data->window),
+ WINDOW_DATA_KEY);
+ g_return_if_fail (data != NULL);
+
+ statusbar = gedit_window_get_statusbar (action_data->window);
+ gedit_statusbar_flash_message (GEDIT_STATUSBAR (statusbar),
+ data->message_cid,
+ _("No misspelled words"));
+
+ return;
+ }
+
+ data_dir = gedit_plugin_get_data_dir (action_data->plugin);
+ dlg = gedit_spell_checker_dialog_new_from_spell_checker (spell, data_dir);
+ g_free (data_dir);
+ gtk_window_set_modal (GTK_WINDOW (dlg), TRUE);
+ gtk_window_set_transient_for (GTK_WINDOW (dlg),
+ GTK_WINDOW (action_data->window));
+
+ g_signal_connect (dlg, "ignore", G_CALLBACK (ignore_cb), view);
+ g_signal_connect (dlg, "ignore_all", G_CALLBACK (ignore_cb), view);
+
+ g_signal_connect (dlg, "change", G_CALLBACK (change_cb), view);
+ g_signal_connect (dlg, "change_all", G_CALLBACK (change_all_cb), view);
+
+ g_signal_connect (dlg, "add_word_to_personal", G_CALLBACK (add_word_cb), view);
+
+ gedit_spell_checker_dialog_set_misspelled_word (GEDIT_SPELL_CHECKER_DIALOG (dlg),
+ word,
+ -1);
+
+ g_free (word);
+
+ gtk_widget_show (dlg);
+}
+
+static void
+set_auto_spell (GeditWindow *window,
+ GeditDocument *doc,
+ gboolean active)
+{
+ GeditAutomaticSpellChecker *autospell;
+ GeditSpellChecker *spell;
+
+ spell = get_spell_checker_from_document (doc);
+ g_return_if_fail (spell != NULL);
+
+ autospell = gedit_automatic_spell_checker_get_from_document (doc);
+
+ if (active)
+ {
+ if (autospell == NULL)
+ {
+ GeditView *active_view;
+
+ active_view = gedit_window_get_active_view (window);
+ g_return_if_fail (active_view != NULL);
+
+ autospell = gedit_automatic_spell_checker_new (doc, spell);
+ gedit_automatic_spell_checker_attach_view (autospell, active_view);
+ gedit_automatic_spell_checker_recheck_all (autospell);
+ }
+ }
+ else
+ {
+ if (autospell != NULL)
+ gedit_automatic_spell_checker_free (autospell);
+ }
+}
+
+static void
+auto_spell_cb (GtkAction *action,
+ GeditWindow *window)
+{
+
+ GeditDocument *doc;
+ gboolean active;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+
+ gedit_debug_message (DEBUG_PLUGINS, active ? "Auto Spell activated" : "Auto Spell deactivated");
+
+ doc = gedit_window_get_active_document (window);
+ if (doc == NULL)
+ return;
+
+ gedit_document_set_metadata (doc,
+ GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED,
+ active ? "1" : NULL, NULL);
+
+ set_auto_spell (window, doc, active);
+}
+
+static void
+free_window_data (WindowData *data)
+{
+ g_return_if_fail (data != NULL);
+
+ g_object_unref (data->action_group);
+ g_slice_free (WindowData, data);
+}
+
+static void
+free_action_data (gpointer data)
+{
+ g_return_if_fail (data != NULL);
+
+ g_slice_free (ActionData, data);
+}
+
+static void
+update_ui_real (GeditWindow *window,
+ WindowData *data)
+{
+ GeditDocument *doc;
+ GeditView *view;
+ gboolean autospell;
+ GtkAction *action;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ doc = gedit_window_get_active_document (window);
+ view = gedit_window_get_active_view (window);
+
+ autospell = (doc != NULL &&
+ gedit_automatic_spell_checker_get_from_document (doc) != NULL);
+
+ if (doc != NULL)
+ {
+ GeditTab *tab;
+ GeditTabState state;
+
+ tab = gedit_window_get_active_tab (window);
+ state = gedit_tab_get_state (tab);
+
+ /* If the document is loading we can't get the metadata so we
+ endup with an useless speller */
+ if (state == GEDIT_TAB_STATE_NORMAL)
+ {
+ action = gtk_action_group_get_action (data->action_group,
+ "AutoSpell");
+
+ g_signal_handlers_block_by_func (action, auto_spell_cb,
+ window);
+ set_auto_spell (window, doc, autospell);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ autospell);
+ g_signal_handlers_unblock_by_func (action, auto_spell_cb,
+ window);
+ }
+ }
+
+ gtk_action_group_set_sensitive (data->action_group,
+ (view != NULL) &&
+ gtk_text_view_get_editable (GTK_TEXT_VIEW (view)));
+}
+
+static void
+set_auto_spell_from_metadata (GeditWindow *window,
+ GeditDocument *doc,
+ GtkActionGroup *action_group)
+{
+ gboolean active = FALSE;
+ gchar *active_str;
+ GeditDocument *active_doc;
+
+ active_str = gedit_document_get_metadata (doc,
+ GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED);
+
+ if (active_str)
+ {
+ active = *active_str == '1';
+
+ g_free (active_str);
+ }
+
+ set_auto_spell (window, doc, active);
+
+ /* In case that the doc is the active one we mark the spell action */
+ active_doc = gedit_window_get_active_document (window);
+
+ if (active_doc == doc && action_group != NULL)
+ {
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (action_group,
+ "AutoSpell");
+
+ g_signal_handlers_block_by_func (action, auto_spell_cb,
+ window);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ active);
+ g_signal_handlers_unblock_by_func (action, auto_spell_cb,
+ window);
+ }
+}
+
+static void
+on_document_loaded (GeditDocument *doc,
+ const GError *error,
+ GeditWindow *window)
+{
+ if (error == NULL)
+ {
+ WindowData *data;
+ GeditSpellChecker *spell;
+
+ spell = GEDIT_SPELL_CHECKER (g_object_get_qdata (G_OBJECT (doc),
+ spell_checker_id));
+ if (spell != NULL)
+ {
+ set_language_from_metadata (spell, doc);
+ }
+
+ data = g_object_get_data (G_OBJECT (window),
+ WINDOW_DATA_KEY);
+
+ set_auto_spell_from_metadata (window, doc, data->action_group);
+ }
+}
+
+static void
+on_document_saved (GeditDocument *doc,
+ const GError *error,
+ GeditWindow *window)
+{
+ GeditAutomaticSpellChecker *autospell;
+ GeditSpellChecker *spell;
+ const gchar *key;
+
+ if (error != NULL)
+ {
+ return;
+ }
+
+ /* Make sure to save the metadata here too */
+ autospell = gedit_automatic_spell_checker_get_from_document (doc);
+ spell = GEDIT_SPELL_CHECKER (g_object_get_qdata (G_OBJECT (doc), spell_checker_id));
+
+ if (spell != NULL)
+ {
+ key = gedit_spell_checker_language_to_key (gedit_spell_checker_get_language (spell));
+ }
+ else
+ {
+ key = NULL;
+ }
+
+ gedit_document_set_metadata (doc,
+ GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED,
+ autospell != NULL ? "1" : NULL,
+ GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE,
+ key,
+ NULL);
+}
+
+static void
+tab_added_cb (GeditWindow *window,
+ GeditTab *tab,
+ gpointer useless)
+{
+ GeditDocument *doc;
+ GeditView *view;
+
+ doc = gedit_tab_get_document (tab);
+ view = gedit_tab_get_view (tab);
+
+ g_signal_connect (doc, "loaded",
+ G_CALLBACK (on_document_loaded),
+ window);
+
+ g_signal_connect (doc, "saved",
+ G_CALLBACK (on_document_saved),
+ window);
+}
+
+static void
+tab_removed_cb (GeditWindow *window,
+ GeditTab *tab,
+ gpointer useless)
+{
+ GeditDocument *doc;
+ GeditView *view;
+
+ doc = gedit_tab_get_document (tab);
+ view = gedit_tab_get_view (tab);
+
+ g_signal_handlers_disconnect_by_func (doc, on_document_loaded, window);
+ g_signal_handlers_disconnect_by_func (doc, on_document_saved, window);
+}
+
+static void
+impl_activate (GeditPlugin *plugin,
+ GeditWindow *window)
+{
+ GtkUIManager *manager;
+ WindowData *data;
+ ActionData *action_data;
+ GList *docs, *l;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ data = g_slice_new (WindowData);
+ action_data = g_slice_new (ActionData);
+ action_data->plugin = plugin;
+ action_data->window = window;
+
+ manager = gedit_window_get_ui_manager (window);
+
+ data->action_group = gtk_action_group_new ("GeditSpellPluginActions");
+ gtk_action_group_set_translation_domain (data->action_group,
+ GETTEXT_PACKAGE);
+ gtk_action_group_add_actions_full (data->action_group,
+ action_entries,
+ G_N_ELEMENTS (action_entries),
+ action_data,
+ (GDestroyNotify) free_action_data);
+ gtk_action_group_add_toggle_actions (data->action_group,
+ toggle_action_entries,
+ G_N_ELEMENTS (toggle_action_entries),
+ window);
+
+ gtk_ui_manager_insert_action_group (manager, data->action_group, -1);
+
+ data->ui_id = gtk_ui_manager_new_merge_id (manager);
+
+ data->message_cid = gtk_statusbar_get_context_id
+ (GTK_STATUSBAR (gedit_window_get_statusbar (window)),
+ "spell_plugin_message");
+
+ g_object_set_data_full (G_OBJECT (window),
+ WINDOW_DATA_KEY,
+ data,
+ (GDestroyNotify) free_window_data);
+
+ gtk_ui_manager_add_ui (manager,
+ data->ui_id,
+ MENU_PATH,
+ "CheckSpell",
+ "CheckSpell",
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ gtk_ui_manager_add_ui (manager,
+ data->ui_id,
+ MENU_PATH,
+ "AutoSpell",
+ "AutoSpell",
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ gtk_ui_manager_add_ui (manager,
+ data->ui_id,
+ MENU_PATH,
+ "ConfigSpell",
+ "ConfigSpell",
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ update_ui_real (window, data);
+
+ docs = gedit_window_get_documents (window);
+ for (l = docs; l != NULL; l = g_list_next (l))
+ {
+ GeditDocument *doc = GEDIT_DOCUMENT (l->data);
+
+ set_auto_spell_from_metadata (window, doc,
+ data->action_group);
+
+ g_signal_handlers_disconnect_by_func (doc,
+ on_document_loaded,
+ window);
+
+ g_signal_handlers_disconnect_by_func (doc,
+ on_document_saved,
+ window);
+ }
+
+ data->tab_added_id =
+ g_signal_connect (window, "tab-added",
+ G_CALLBACK (tab_added_cb), NULL);
+ data->tab_removed_id =
+ g_signal_connect (window, "tab-removed",
+ G_CALLBACK (tab_removed_cb), NULL);
+}
+
+static void
+impl_deactivate (GeditPlugin *plugin,
+ GeditWindow *window)
+{
+ GtkUIManager *manager;
+ WindowData *data;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ manager = gedit_window_get_ui_manager (window);
+
+ data = (WindowData *) g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY);
+ g_return_if_fail (data != NULL);
+
+ gtk_ui_manager_remove_ui (manager, data->ui_id);
+ gtk_ui_manager_remove_action_group (manager, data->action_group);
+
+ g_signal_handler_disconnect (window, data->tab_added_id);
+ g_signal_handler_disconnect (window, data->tab_removed_id);
+
+ g_object_set_data (G_OBJECT (window), WINDOW_DATA_KEY, NULL);
+}
+
+static void
+impl_update_ui (GeditPlugin *plugin,
+ GeditWindow *window)
+{
+ WindowData *data;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ data = (WindowData *) g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY);
+ g_return_if_fail (data != NULL);
+
+ update_ui_real (window, data);
+}
+
+static void
+gedit_spell_plugin_class_init (GeditSpellPluginClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeditPluginClass *plugin_class = GEDIT_PLUGIN_CLASS (klass);
+
+ object_class->finalize = gedit_spell_plugin_finalize;
+
+ plugin_class->activate = impl_activate;
+ plugin_class->deactivate = impl_deactivate;
+ plugin_class->update_ui = impl_update_ui;
+
+ if (spell_checker_id == 0)
+ spell_checker_id = g_quark_from_string ("GeditSpellCheckerID");
+
+ if (check_range_id == 0)
+ check_range_id = g_quark_from_string ("CheckRangeID");
+}
diff --git a/plugins/spell/gedit-spell-plugin.h b/plugins/spell/gedit-spell-plugin.h
new file mode 100755
index 00000000..7de5807a
--- /dev/null
+++ b/plugins/spell/gedit-spell-plugin.h
@@ -0,0 +1,75 @@
+/*
+ * gedit-spell-plugin.h
+ *
+ * Copyright (C) 2002-2005 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 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.
+ *
+ * $Id$
+ */
+
+#ifndef __GEDIT_SPELL_PLUGIN_H__
+#define __GEDIT_SPELL_PLUGIN_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gedit/gedit-plugin.h>
+
+G_BEGIN_DECLS
+
+/*
+ * Type checking and casting macros
+ */
+#define GEDIT_TYPE_SPELL_PLUGIN (gedit_spell_plugin_get_type ())
+#define GEDIT_SPELL_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GEDIT_TYPE_SPELL_PLUGIN, GeditSpellPlugin))
+#define GEDIT_SPELL_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GEDIT_TYPE_SPELL_PLUGIN, GeditSpellPluginClass))
+#define GEDIT_IS_SPELL_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GEDIT_TYPE_SPELL_PLUGIN))
+#define GEDIT_IS_SPELL_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GEDIT_TYPE_SPELL_PLUGIN))
+#define GEDIT_SPELL_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GEDIT_TYPE_SPELL_PLUGIN, GeditSpellPluginClass))
+
+/* Private structure type */
+typedef struct _GeditSpellPluginPrivate GeditSpellPluginPrivate;
+
+/*
+ * Main object structure
+ */
+typedef struct _GeditSpellPlugin GeditSpellPlugin;
+
+struct _GeditSpellPlugin
+{
+ GeditPlugin parent_instance;
+};
+
+/*
+ * Class definition
+ */
+typedef struct _GeditSpellPluginClass GeditSpellPluginClass;
+
+struct _GeditSpellPluginClass
+{
+ GeditPluginClass parent_class;
+};
+
+/*
+ * Public methods
+ */
+GType gedit_spell_plugin_get_type (void) G_GNUC_CONST;
+
+/* All the plugins must implement this function */
+G_MODULE_EXPORT GType register_gedit_plugin (GTypeModule *module);
+
+G_END_DECLS
+
+#endif /* __GEDIT_SPELL_PLUGIN_H__ */
diff --git a/plugins/spell/gedit-spell-utils.c b/plugins/spell/gedit-spell-utils.c
new file mode 100755
index 00000000..0782d37a
--- /dev/null
+++ b/plugins/spell/gedit-spell-utils.c
@@ -0,0 +1,94 @@
+/*
+ * gedit-spell-utils.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2010 - Jesse van den Kieboom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "gedit-spell-utils.h"
+#include <gtksourceview/gtksourcebuffer.h>
+
+gboolean
+gedit_spell_utils_is_digit (const char *text, gssize length)
+{
+ gunichar c;
+ const gchar *p;
+ const gchar *end;
+
+ g_return_val_if_fail (text != NULL, FALSE);
+
+ if (length < 0)
+ length = strlen (text);
+
+ p = text;
+ end = text + length;
+
+ while (p != end) {
+ const gchar *next;
+ next = g_utf8_next_char (p);
+
+ c = g_utf8_get_char (p);
+
+ if (!g_unichar_isdigit (c) && c != '.' && c != ',')
+ return FALSE;
+
+ p = next;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gedit_spell_utils_skip_no_spell_check (GtkTextIter *start,
+ GtkTextIter *end)
+{
+ GtkSourceBuffer *buffer = GTK_SOURCE_BUFFER (gtk_text_iter_get_buffer (start));
+
+ while (gtk_source_buffer_iter_has_context_class (buffer, start, "no-spell-check"))
+ {
+ GtkTextIter last = *start;
+
+ if (!gtk_source_buffer_iter_forward_to_context_class_toggle (buffer, start, "no-spell-check"))
+ {
+ return FALSE;
+ }
+
+ if (gtk_text_iter_compare (start, &last) <= 0)
+ {
+ return FALSE;
+ }
+
+ gtk_text_iter_forward_word_end (start);
+ gtk_text_iter_backward_word_start (start);
+
+ if (gtk_text_iter_compare (start, &last) <= 0)
+ {
+ return FALSE;
+ }
+
+ if (gtk_text_iter_compare (start, end) >= 0)
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
diff --git a/plugins/spell/gedit-spell-utils.h b/plugins/spell/gedit-spell-utils.h
new file mode 100755
index 00000000..fbfe4b1b
--- /dev/null
+++ b/plugins/spell/gedit-spell-utils.h
@@ -0,0 +1,37 @@
+/*
+ * gedit-spell-utils.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2010 - Jesse van den Kieboom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#ifndef __GEDIT_SPELL_UTILS_H__
+#define __GEDIT_SPELL_UTILS_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+gboolean gedit_spell_utils_is_digit (const char *text, gssize length);
+
+gboolean gedit_spell_utils_skip_no_spell_check (GtkTextIter *start, GtkTextIter *end);
+
+G_END_DECLS
+
+#endif /* __GEDIT_SPELL_UTILS_H__ */
+
diff --git a/plugins/spell/languages-dialog.ui b/plugins/spell/languages-dialog.ui
new file mode 100755
index 00000000..8ceb7056
--- /dev/null
+++ b/plugins/spell/languages-dialog.ui
@@ -0,0 +1,145 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkDialog" id="dialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Set language</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">True</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="helpbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-help</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="closebutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="button1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="content">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">11</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Select the _language of the current document.</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">True</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">languages_treeview</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+ <child>
+ <object class="GtkTreeView" id="languages_treeview">
+ <property name="height_request">180</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">helpbutton1</action-widget>
+ <action-widget response="-6">closebutton1</action-widget>
+ <action-widget response="-5">button1</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/plugins/spell/spell-checker.ui b/plugins/spell/spell-checker.ui
new file mode 100755
index 00000000..ea84290f
--- /dev/null
+++ b/plugins/spell/spell-checker.ui
@@ -0,0 +1,482 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkImage" id="check_word_image">
+ <property name="stock">gtk-spell-check</property>
+ <property name="icon_size">4</property>
+ </object>
+ <object class="GtkImage" id="add_word_image">
+ <property name="stock">gtk-add</property>
+ <property name="icon_size">4</property>
+ </object>
+ <object class="GtkImage" id="ignore_image">
+ <property name="stock">gtk-go-down</property>
+ <property name="icon_size">4</property>
+ </object>
+ <object class="GtkImage" id="change_image">
+ <property name="stock">gtk-convert</property>
+ <property name="icon_size">4</property>
+ </object>
+ <object class="GtkImage" id="ignore_all_image">
+ <property name="stock">gtk-goto-bottom</property>
+ <property name="icon_size">4</property>
+ </object>
+ <object class="GtkImage" id="change_all_image">
+ <property name="stock">gtk-convert</property>
+ <property name="icon_size">4</property>
+ </object>
+ <object class="GtkWindow" id="check_spelling_window">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Check spelling</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">False</property>
+ <property name="destroy_with_parent">False</property>
+ <child>
+ <object class="GtkVBox" id="content">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Misspelled word:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="misspelled_word_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">word</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <attributes>
+ <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Change _to:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">word_entry</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkEntry" id="word_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="activates_default">False</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="check_word_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="image">check_word_image</property>
+ <property name="label" translatable="yes">Check _Word</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table2">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Suggestions:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">suggestions_list</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+ <child>
+ <object class="GtkTreeView" id="suggestions_list">
+ <property name="width_request">200</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkTable" id="table3">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">True</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkButton" id="ignore_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="image">ignore_image</property>
+ <property name="label" translatable="yes">_Ignore</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options">expand</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="change_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="image">change_image</property>
+ <property name="label" translatable="yes">Cha_nge</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">expand</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ignore_all_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="image">ignore_all_image</property>
+ <property name="label" translatable="yes">Ignore _All</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options">expand</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="change_all_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="image">change_all_image</property>
+ <property name="label" translatable="yes">Change A_ll</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">expand</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">11</property>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">User dictionary:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">7.45058e-09</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="add_word_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="image">add_word_image</property>
+ <property name="label" translatable="yes">Add w_ord</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox32">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label44">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Language:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="language_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Language</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <attributes>
+ <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkButton" id="close_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/plugins/spell/spell.gedit-plugin.desktop.in b/plugins/spell/spell.gedit-plugin.desktop.in
new file mode 100755
index 00000000..c75c9eee
--- /dev/null
+++ b/plugins/spell/spell.gedit-plugin.desktop.in
@@ -0,0 +1,9 @@
+[Gedit Plugin]
+Module=spell
+IAge=2
+_Name=Spell Checker
+_Description=Checks the spelling of the current document.
+Icon=gtk-spell-check
+Authors=Paolo Maggi <[email protected]>
+Copyright=Copyright © 2002-2005 Paolo Maggi
+Website=http://www.gedit.org