diff options
Diffstat (limited to 'plugins/spell')
-rwxr-xr-x | plugins/spell/Makefile.am | 63 | ||||
-rwxr-xr-x | plugins/spell/gedit-automatic-spell-checker.c | 1015 | ||||
-rwxr-xr-x | plugins/spell/gedit-automatic-spell-checker.h | 67 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-checker-dialog.c | 722 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-checker-dialog.h | 92 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-checker-language.c | 439 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-checker-language.h | 51 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-checker.c | 520 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-checker.h | 109 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-language-dialog.c | 309 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-language-dialog.h | 67 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-marshal.list | 6 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-plugin.c | 1217 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-plugin.h | 75 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-utils.c | 94 | ||||
-rwxr-xr-x | plugins/spell/gedit-spell-utils.h | 37 | ||||
-rwxr-xr-x | plugins/spell/languages-dialog.ui | 145 | ||||
-rwxr-xr-x | plugins/spell/spell-checker.ui | 482 | ||||
-rwxr-xr-x | plugins/spell/spell.gedit-plugin.desktop.in | 9 |
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), + ¤t_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 (¤t_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), + ¤t_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), + ¤t_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 (¤t_iter, 2); + gtk_text_iter_backward_word_start (¤t_iter); + + if (gedit_spell_utils_skip_no_spell_check (¤t_iter, &end_iter) && + (gtk_text_iter_compare (&old_current_iter, ¤t_iter) < 0) && + (gtk_text_iter_compare (¤t_iter, &end_iter) < 0)) + { + update_current (doc, gtk_text_iter_get_offset (¤t_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 |