1 /* gtkaspell - a spell-checking addon for GtkText
2 * Copyright (c) 2000 Evan Martin (original code for ispell).
3 * Copyright (c) 2002 Melvin Hadasht.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; If not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Stuphead: (C) 2000,2001 Grigroy Bakunov, Sergey Pinaev
21 * Adapted for Sylpheed (Claws) (c) 2001-2002 by Hiroyuki Yamamoto &
22 * The Sylpheed Claws Team.
23 * Adapted for pspell (c) 2001-2002 Melvin Hadasht
24 * Adapted for GNU/aspell (c) 2002 Melvin Hadasht
36 #include <sys/types.h>
48 #include <glib/gi18n.h>
52 #include <gtk/gtkoptionmenu.h>
53 #include <gtk/gtkmenu.h>
54 #include <gtk/gtkmenuitem.h>
55 #include <gdk/gdkkeysyms.h>
59 #include "gtk/gtktext.h"
62 #include "alertpanel.h"
63 #include "gtkaspell.h"
64 #define ASPELL_FASTMODE 1
65 #define ASPELL_NORMALMODE 2
66 #define ASPELL_BADSPELLERMODE 3
68 #define GTKASPELLWORDSIZE 1024
70 /* size of the text buffer used in various word-processing routines. */
73 /* number of suggestions to display on each menu. */
76 /* 'config' must be defined as a 'AspellConfig *' */
77 #define RETURN_FALSE_IF_CONFIG_ERROR() \
79 if (aspell_config_error_number(config) != 0) { \
80 gtkaspellcheckers->error_message = g_strdup(aspell_config_error_message(config)); \
85 #define CONFIG_REPLACE_RETURN_FALSE_IF_FAIL(option, value) { \
86 aspell_config_replace(config, option, value); \
87 RETURN_FALSE_IF_CONFIG_ERROR(); \
90 typedef struct _GtkAspellCheckers {
92 GSList *dictionary_list;
96 typedef struct _Dictionary {
102 typedef struct _GtkAspeller {
103 Dictionary *dictionary;
105 AspellConfig *config;
106 AspellSpeller *checker;
109 typedef void (*ContCheckFunc) (gpointer *gtkaspell);
113 GtkAspeller *gtkaspeller;
114 GtkAspeller *alternate_speller;
115 gchar *dictionary_path;
116 gchar theword[GTKASPELLWORDSIZE];
122 gboolean check_while_typing;
123 gboolean use_alternate;
125 ContCheckFunc continue_check;
127 GtkWidget *config_menu;
128 GtkWidget *popup_config_menu;
130 GtkWidget *replace_entry;
131 GtkWidget *parent_window;
133 gint default_sug_mode;
135 GList *suggestions_list;
137 GtkTextView *gtktext;
139 GtkAccelGroup *accel_group;
142 typedef AspellConfig GtkAspellConfig;
144 /******************************************************************************/
146 static GtkAspellCheckers *gtkaspellcheckers;
148 /* Error message storage */
149 static void gtkaspell_checkers_error_message (gchar *message);
152 static void entry_insert_cb (GtkTextBuffer *textbuf,
156 GtkAspell *gtkaspell);
157 static void entry_delete_cb (GtkTextBuffer *textbuf,
158 GtkTextIter *startiter,
159 GtkTextIter *enditer,
160 GtkAspell *gtkaspell);
161 static gint button_press_intercept_cb (GtkTextView *gtktext,
163 GtkAspell *gtkaspell);
165 /* Checker creation */
166 static GtkAspeller* gtkaspeller_new (Dictionary *dict);
167 static GtkAspeller* gtkaspeller_real_new (Dictionary *dict);
168 static GtkAspeller* gtkaspeller_delete (GtkAspeller *gtkaspeller);
169 static GtkAspeller* gtkaspeller_real_delete (GtkAspeller *gtkaspeller);
171 /* Checker configuration */
172 static gint set_dictionary (AspellConfig *config,
174 static void set_sug_mode_cb (GtkMenuItem *w,
175 GtkAspell *gtkaspell);
176 static void set_real_sug_mode (GtkAspell *gtkaspell,
177 const char *themode);
179 /* Checker actions */
180 static gboolean check_at (GtkAspell *gtkaspell,
182 static gboolean check_next_prev (GtkAspell *gtkaspell,
184 static GList* misspelled_suggest (GtkAspell *gtkaspell,
186 static void add_word_to_session_cb (GtkWidget *w,
188 static void add_word_to_personal_cb (GtkWidget *w,
190 static void replace_with_create_dialog_cb (GtkWidget *w,
192 static void replace_with_supplied_word_cb (GtkWidget *w,
193 GtkAspell *gtkaspell);
194 static void replace_word_cb (GtkWidget *w,
196 static void replace_real_word (GtkAspell *gtkaspell,
198 static void check_with_alternate_cb (GtkWidget *w,
200 static void use_alternate_dict (GtkAspell *gtkaspell);
201 static void toggle_check_while_typing_cb (GtkWidget *w,
205 static void popup_menu (GtkAspell *gtkaspell,
207 static GtkMenu* make_sug_menu (GtkAspell *gtkaspell);
208 static void populate_submenu (GtkAspell *gtkaspell,
210 static GtkMenu* make_config_menu (GtkAspell *gtkaspell);
211 static void set_menu_pos (GtkMenu *menu,
216 /* Other menu callbacks */
217 static gboolean cancel_menu_cb (GtkMenuShell *w,
219 static void change_dict_cb (GtkWidget *w,
220 GtkAspell *gtkaspell);
221 static void switch_to_alternate_cb (GtkWidget *w,
224 /* Misc. helper functions */
225 static void set_point_continue (GtkAspell *gtkaspell);
226 static void continue_check (gpointer *gtkaspell);
227 static gboolean iswordsep (unsigned char c);
228 static guchar get_text_index_whar (GtkAspell *gtkaspell,
230 static gboolean get_word_from_pos (GtkAspell *gtkaspell,
236 static void allocate_color (GtkAspell *gtkaspell,
238 static void change_color (GtkAspell *gtkaspell,
243 static guchar* convert_to_aspell_encoding (const guchar *encoding);
244 static gint compare_dict (Dictionary *a,
246 static void dictionary_delete (Dictionary *dict);
247 static Dictionary * dictionary_dup (const Dictionary *dict);
248 static void free_suggestions_list (GtkAspell *gtkaspell);
249 static void reset_theword_data (GtkAspell *gtkaspell);
250 static void free_checkers (gpointer elt,
252 static gint find_gtkaspeller (gconstpointer aa,
254 /* gtkspellconfig - only one config per session */
255 GtkAspellConfig * gtkaspellconfig;
256 static void destroy_menu(GtkWidget *widget, gpointer user_data);
258 /******************************************************************************/
259 static gint get_textview_buffer_charcount(GtkTextView *view);
260 static gint get_textview_buffer_position(GtkTextView *view);
262 static gint get_textview_buffer_charcount(GtkTextView *view)
264 GtkTextBuffer *buffer;
266 g_return_val_if_fail(view, 0);
268 buffer = gtk_text_view_get_buffer(view);
269 g_return_val_if_fail(buffer, 0);
271 return gtk_text_buffer_get_char_count(buffer);
273 static gint get_textview_buffer_offset(GtkTextView *view)
275 GtkTextBuffer * buffer;
279 g_return_val_if_fail(view, 0);
281 buffer = gtk_text_view_get_buffer(view);
282 g_return_val_if_fail(buffer, 0);
284 mark = gtk_text_buffer_get_insert(buffer);
285 g_return_val_if_fail(mark, 0);
287 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
289 return gtk_text_iter_get_offset(&iter);
291 static void set_textview_buffer_offset(GtkTextView *view, gint offset)
293 GtkTextBuffer *buffer;
296 g_return_if_fail(view);
298 buffer = gtk_text_view_get_buffer(view);
299 g_return_if_fail(buffer);
301 gtk_text_buffer_get_iter_at_offset(buffer, &iter, offset);
302 gtk_text_buffer_place_cursor(buffer, &iter);
304 /******************************************************************************/
306 void gtkaspell_checkers_init(void)
308 gtkaspellcheckers = g_new(GtkAspellCheckers, 1);
309 gtkaspellcheckers->checkers = NULL;
310 gtkaspellcheckers->dictionary_list = NULL;
311 gtkaspellcheckers->error_message = NULL;
314 void gtkaspell_checkers_quit(void)
319 if (gtkaspellcheckers == NULL)
322 if ((checkers = gtkaspellcheckers->checkers)) {
323 debug_print("Aspell: number of running checkers to delete %d\n",
324 g_slist_length(checkers));
326 g_slist_foreach(checkers, free_checkers, NULL);
327 g_slist_free(checkers);
330 if ((dict_list = gtkaspellcheckers->dictionary_list)) {
331 debug_print("Aspell: number of dictionaries to delete %d\n",
332 g_slist_length(dict_list));
334 gtkaspell_free_dictionary_list(dict_list);
335 gtkaspellcheckers->dictionary_list = NULL;
338 g_free(gtkaspellcheckers->error_message);
343 static void gtkaspell_checkers_error_message (gchar *message)
346 if (gtkaspellcheckers->error_message) {
347 tmp = g_strdup_printf("%s\n%s",
348 gtkaspellcheckers->error_message,
351 g_free(gtkaspellcheckers->error_message);
352 gtkaspellcheckers->error_message = tmp;
354 gtkaspellcheckers->error_message = message;
357 const char *gtkaspell_checkers_strerror(void)
359 g_return_val_if_fail(gtkaspellcheckers, "");
360 return gtkaspellcheckers->error_message;
363 void gtkaspell_checkers_reset_error(void)
365 g_return_if_fail(gtkaspellcheckers);
367 g_free(gtkaspellcheckers->error_message);
369 gtkaspellcheckers->error_message = NULL;
372 GtkAspell *gtkaspell_new(const gchar *dictionary_path,
373 const gchar *dictionary,
374 const gchar *encoding,
375 gint misspelled_color,
376 gboolean check_while_typing,
377 gboolean use_alternate,
378 GtkTextView *gtktext,
379 GtkWindow *parent_win)
382 GtkAspell *gtkaspell;
383 GtkAspeller *gtkaspeller;
384 GtkTextBuffer *buffer;
386 g_return_val_if_fail(gtktext, NULL);
387 buffer = gtk_text_view_get_buffer(gtktext);
389 dict = g_new0(Dictionary, 1);
390 dict->fullname = g_strdup(dictionary);
391 dict->encoding = g_strdup(encoding);
393 gtkaspeller = gtkaspeller_new(dict);
394 dictionary_delete(dict);
399 gtkaspell = g_new0(GtkAspell, 1);
401 gtkaspell->dictionary_path = g_strdup(dictionary_path);
403 gtkaspell->gtkaspeller = gtkaspeller;
404 gtkaspell->alternate_speller = NULL;
405 gtkaspell->theword[0] = 0x00;
406 gtkaspell->start_pos = 0;
407 gtkaspell->end_pos = 0;
408 gtkaspell->orig_pos = -1;
409 gtkaspell->end_check_pos = -1;
410 gtkaspell->misspelled = -1;
411 gtkaspell->check_while_typing = check_while_typing;
412 gtkaspell->continue_check = NULL;
413 gtkaspell->config_menu = NULL;
414 gtkaspell->popup_config_menu = NULL;
415 gtkaspell->sug_menu = NULL;
416 gtkaspell->replace_entry = NULL;
417 gtkaspell->gtktext = gtktext;
418 gtkaspell->default_sug_mode = ASPELL_FASTMODE;
419 gtkaspell->max_sug = -1;
420 gtkaspell->suggestions_list = NULL;
421 gtkaspell->use_alternate = use_alternate;
422 gtkaspell->parent_window = GTK_WIDGET(parent_win);
424 allocate_color(gtkaspell, misspelled_color);
426 g_signal_connect_after(G_OBJECT(buffer), "insert-text",
427 G_CALLBACK(entry_insert_cb), gtkaspell);
428 g_signal_connect_after(G_OBJECT(buffer), "delete-range",
429 G_CALLBACK(entry_delete_cb), gtkaspell);
430 g_signal_connect(G_OBJECT(gtktext), "button-press-event",
431 G_CALLBACK(button_press_intercept_cb),
434 debug_print("Aspell: created gtkaspell %0x\n", (guint) gtkaspell);
439 void gtkaspell_delete(GtkAspell * gtkaspell)
441 GtkTextView *gtktext = gtkaspell->gtktext;
443 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
444 G_CALLBACK(entry_insert_cb),
446 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
447 G_CALLBACK(entry_delete_cb),
449 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
450 G_CALLBACK(button_press_intercept_cb),
453 gtkaspell_uncheck_all(gtkaspell);
455 gtkaspeller_delete(gtkaspell->gtkaspeller);
457 if (gtkaspell->use_alternate && gtkaspell->alternate_speller)
458 gtkaspeller_delete(gtkaspell->alternate_speller);
460 if (gtkaspell->sug_menu)
461 gtk_widget_destroy(gtkaspell->sug_menu);
463 if (gtkaspell->popup_config_menu)
464 gtk_widget_destroy(gtkaspell->popup_config_menu);
466 if (gtkaspell->config_menu)
467 gtk_widget_destroy(gtkaspell->config_menu);
469 if (gtkaspell->suggestions_list)
470 free_suggestions_list(gtkaspell);
472 g_free((gchar *)gtkaspell->dictionary_path);
474 debug_print("Aspell: deleting gtkaspell %0x\n", (guint) gtkaspell);
481 static void entry_insert_cb(GtkTextBuffer *textbuf,
485 GtkAspell *gtkaspell)
490 g_return_if_fail(gtkaspell->gtkaspeller->checker);
492 if (!gtkaspell->check_while_typing)
495 pos = gtk_text_iter_get_offset(iter);
497 if (iswordsep(newtext[0])) {
498 /* did we just end a word? */
500 check_at(gtkaspell, pos - 2);
502 /* did we just split a word? */
503 if (pos < gtk_text_buffer_get_char_count(textbuf))
504 check_at(gtkaspell, pos + 1);
506 /* check as they type, *except* if they're typing at the end (the most
509 if (pos < gtk_text_buffer_get_char_count(textbuf) &&
510 !iswordsep(get_text_index_whar(gtkaspell, pos))) {
511 check_at(gtkaspell, pos - 1);
516 static void entry_delete_cb(GtkTextBuffer *textbuf,
517 GtkTextIter *startiter,
518 GtkTextIter *enditer,
519 GtkAspell *gtkaspell)
524 g_return_if_fail(gtkaspell->gtkaspeller->checker);
526 if (!gtkaspell->check_while_typing)
529 start = gtk_text_iter_get_offset(startiter);
530 end = gtk_text_iter_get_offset(enditer);
531 origpos = get_textview_buffer_offset(gtkaspell->gtktext);
533 check_at(gtkaspell, start - 1);
534 check_at(gtkaspell, start);
537 set_textview_buffer_offset(gtkaspell->gtktext, origpos);
538 /* this is to *UNDO* the selection, in case they were holding shift
539 * while hitting backspace. */
540 /* needed with textview ??? */
541 /* gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos); */
544 /* ok, this is pretty wacky:
545 * we need to let the right-mouse-click go through, so it moves the cursor,
546 * but we *can't* let it go through, because GtkText interprets rightclicks as
547 * weird selection modifiers.
549 * so what do we do? forge rightclicks as leftclicks, then popup the menu.
552 static gint button_press_intercept_cb(GtkTextView *gtktext,
554 GtkAspell *gtkaspell)
559 g_return_val_if_fail(gtkaspell->gtkaspeller->checker, FALSE);
561 if (e->type != GDK_BUTTON_PRESS)
563 eb = (GdkEventButton*) e;
568 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
569 G_CALLBACK(button_press_intercept_cb),
571 g_signal_emit_by_name(G_OBJECT(gtktext), "button-release-event",
574 /* forge the leftclick */
577 g_signal_emit_by_name(G_OBJECT(gtktext), "button-press-event",
579 g_signal_emit_by_name(G_OBJECT(gtktext), "button-release-event",
581 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
582 G_CALLBACK(button_press_intercept_cb),
584 g_signal_stop_emission_by_name(G_OBJECT(gtktext), "button-press-event");
586 /* now do the menu wackiness */
587 popup_menu(gtkaspell, eb);
588 gtk_grab_remove(GTK_WIDGET(gtktext));
592 /* Checker creation */
593 static GtkAspeller *gtkaspeller_new(Dictionary *dictionary)
596 GtkAspeller *gtkaspeller = NULL;
600 g_return_val_if_fail(gtkaspellcheckers, NULL);
602 g_return_val_if_fail(dictionary, NULL);
604 if (dictionary->fullname == NULL)
605 gtkaspell_checkers_error_message(
606 g_strdup(_("No dictionary selected.")));
608 g_return_val_if_fail(dictionary->fullname, NULL);
610 if (dictionary->dictname == NULL) {
613 tmp = strrchr(dictionary->fullname, G_DIR_SEPARATOR);
616 dictionary->dictname = dictionary->fullname;
618 dictionary->dictname = tmp + 1;
621 dict = dictionary_dup(dictionary);
623 tmp = g_new0(GtkAspeller, 1);
624 tmp->dictionary = dict;
626 exist = g_slist_find_custom(gtkaspellcheckers->checkers, tmp,
631 if ((gtkaspeller = gtkaspeller_real_new(dict)) != NULL) {
632 gtkaspellcheckers->checkers = g_slist_append(
633 gtkaspellcheckers->checkers,
636 debug_print("Aspell: Created a new gtkaspeller %0x\n",
639 dictionary_delete(dict);
641 debug_print("Aspell: Could not create spell checker.\n");
644 debug_print("Aspell: number of existing checkers %d\n",
645 g_slist_length(gtkaspellcheckers->checkers));
650 static GtkAspeller *gtkaspeller_real_new(Dictionary *dict)
652 GtkAspeller *gtkaspeller;
653 AspellConfig *config;
654 AspellCanHaveError *ret;
656 g_return_val_if_fail(gtkaspellcheckers, NULL);
657 g_return_val_if_fail(dict, NULL);
659 gtkaspeller = g_new(GtkAspeller, 1);
661 gtkaspeller->dictionary = dict;
662 gtkaspeller->sug_mode = ASPELL_FASTMODE;
664 config = new_aspell_config();
666 if (!set_dictionary(config, dict))
669 ret = new_aspell_speller(config);
670 delete_aspell_config(config);
672 if (aspell_error_number(ret) != 0) {
673 gtkaspellcheckers->error_message
674 = g_strdup(aspell_error_message(ret));
676 delete_aspell_can_have_error(ret);
681 gtkaspeller->checker = to_aspell_speller(ret);
682 gtkaspeller->config = aspell_speller_config(gtkaspeller->checker);
687 static GtkAspeller *gtkaspeller_delete(GtkAspeller *gtkaspeller)
689 g_return_val_if_fail(gtkaspellcheckers, NULL);
691 gtkaspellcheckers->checkers =
692 g_slist_remove(gtkaspellcheckers->checkers,
695 debug_print("Aspell: Deleting gtkaspeller %0x.\n",
698 gtkaspeller_real_delete(gtkaspeller);
700 debug_print("Aspell: number of existing checkers %d\n",
701 g_slist_length(gtkaspellcheckers->checkers));
706 static GtkAspeller *gtkaspeller_real_delete(GtkAspeller *gtkaspeller)
708 g_return_val_if_fail(gtkaspeller, NULL);
709 g_return_val_if_fail(gtkaspeller->checker, NULL);
711 aspell_speller_save_all_word_lists(gtkaspeller->checker);
713 delete_aspell_speller(gtkaspeller->checker);
715 dictionary_delete(gtkaspeller->dictionary);
717 debug_print("Aspell: gtkaspeller %0x deleted.\n",
725 /*****************************************************************************/
726 /* Checker configuration */
728 static gboolean set_dictionary(AspellConfig *config, Dictionary *dict)
730 gchar *language = NULL;
731 gchar *jargon = NULL;
735 g_return_val_if_fail(config, FALSE);
736 g_return_val_if_fail(dict, FALSE);
738 strncpy(buf, dict->fullname, BUFSIZE-1);
739 buf[BUFSIZE-1] = 0x00;
741 buf[dict->dictname - dict->fullname] = 0x00;
743 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("dict-dir", buf);
744 debug_print("Aspell: looking for dictionaries in path %s.\n", buf);
746 strncpy(buf, dict->dictname, BUFSIZE-1);
749 if ((size = strrchr(buf, '-')) && isdigit((int) size[1]))
754 if ((jargon = strchr(language, '-')) != NULL)
757 if (size != NULL && jargon == size)
760 debug_print("Aspell: language: %s, jargon: %s, size: %s\n",
761 language, jargon ? jargon : "",
765 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("lang", language);
767 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("jargon", jargon);
769 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("size", size);
770 if (dict->encoding) {
773 aspell_enc = convert_to_aspell_encoding (dict->encoding);
774 aspell_config_replace(config, "encoding",
775 (const char *) aspell_enc);
778 RETURN_FALSE_IF_CONFIG_ERROR();
784 guchar *gtkaspell_get_dict(GtkAspell *gtkaspell)
787 g_return_val_if_fail(gtkaspell->gtkaspeller->config, NULL);
788 g_return_val_if_fail(gtkaspell->gtkaspeller->dictionary, NULL);
790 return g_strdup(gtkaspell->gtkaspeller->dictionary->dictname);
793 guchar *gtkaspell_get_path(GtkAspell *gtkaspell)
798 g_return_val_if_fail(gtkaspell->gtkaspeller->config, NULL);
799 g_return_val_if_fail(gtkaspell->gtkaspeller->dictionary, NULL);
801 dict = gtkaspell->gtkaspeller->dictionary;
802 path = g_strndup(dict->fullname, dict->dictname - dict->fullname);
807 /* set_sug_mode_cb() - Menu callback: Set the suggestion mode */
808 static void set_sug_mode_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
812 gtk_label_get(GTK_LABEL(GTK_BIN(w)->child), (gchar **) &themode);
814 set_real_sug_mode(gtkaspell, themode);
816 if (gtkaspell->config_menu)
817 populate_submenu(gtkaspell, gtkaspell->config_menu);
820 static void set_real_sug_mode(GtkAspell *gtkaspell, const char *themode)
823 gint mode = ASPELL_FASTMODE;
824 g_return_if_fail(gtkaspell);
825 g_return_if_fail(gtkaspell->gtkaspeller);
826 g_return_if_fail(themode);
828 if (!strcmp(themode,_("Normal Mode")))
829 mode = ASPELL_NORMALMODE;
830 else if (!strcmp( themode,_("Bad Spellers Mode")))
831 mode = ASPELL_BADSPELLERMODE;
833 result = gtkaspell_set_sug_mode(gtkaspell, mode);
836 debug_print("Aspell: error while changing suggestion mode:%s\n",
837 gtkaspellcheckers->error_message);
838 gtkaspell_checkers_reset_error();
842 /* gtkaspell_set_sug_mode() - Set the suggestion mode */
843 gboolean gtkaspell_set_sug_mode(GtkAspell *gtkaspell, gint themode)
845 AspellConfig *config;
847 g_return_val_if_fail(gtkaspell, FALSE);
848 g_return_val_if_fail(gtkaspell->gtkaspeller, FALSE);
849 g_return_val_if_fail(gtkaspell->gtkaspeller->config, FALSE);
851 debug_print("Aspell: setting sug mode of gtkaspeller %0x to %d\n",
852 (guint) gtkaspell->gtkaspeller, themode);
854 config = gtkaspell->gtkaspeller->config;
857 case ASPELL_FASTMODE:
858 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "fast");
860 case ASPELL_NORMALMODE:
861 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "normal");
863 case ASPELL_BADSPELLERMODE:
864 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode",
868 gtkaspellcheckers->error_message =
869 g_strdup(_("Unknown suggestion mode."));
873 gtkaspell->gtkaspeller->sug_mode = themode;
874 gtkaspell->default_sug_mode = themode;
879 /* misspelled_suggest() - Create a suggestion list for word */
880 static GList *misspelled_suggest(GtkAspell *gtkaspell, guchar *word)
882 const guchar *newword;
884 const AspellWordList *suggestions;
885 AspellStringEnumeration *elements;
887 g_return_val_if_fail(word, NULL);
889 if (!aspell_speller_check(gtkaspell->gtkaspeller->checker, word, -1)) {
890 free_suggestions_list(gtkaspell);
892 suggestions = aspell_speller_suggest(
893 gtkaspell->gtkaspeller->checker,
894 (const char *)word, -1);
895 elements = aspell_word_list_elements(suggestions);
896 list = g_list_append(list, g_strdup(word));
898 while (newword = aspell_string_enumeration_next(elements))
899 list = g_list_append(list, g_strdup(newword));
901 gtkaspell->max_sug = g_list_length(list) - 1;
902 gtkaspell->suggestions_list = list;
907 free_suggestions_list(gtkaspell);
912 /* misspelled_test() - Just test if word is correctly spelled */
913 static int misspelled_test(GtkAspell *gtkaspell, unsigned char *word)
915 return aspell_speller_check(gtkaspell->gtkaspeller->checker, word, -1)
920 static gboolean iswordsep(unsigned char c)
922 return !isalpha(c) && c != '\'';
925 static guchar get_text_index_whar(GtkAspell *gtkaspell, int pos)
927 GtkTextView *view = gtkaspell->gtktext;
928 GtkTextBuffer *buffer = gtk_text_view_get_buffer(view);
929 GtkTextIter start, end;
930 const gchar *utf8chars;
933 gtk_text_buffer_get_iter_at_offset(buffer, &start, pos);
934 gtk_text_buffer_get_iter_at_offset(buffer, &end, pos+1);
936 utf8chars = gtk_text_iter_get_text(&start, &end);
937 if (is_ascii_str(utf8chars)) {
938 a = utf8chars ? utf8chars[0] : '\0' ;
940 gchar *tr = conv_iconv_strdup(utf8chars, CS_UTF_8,
941 gtkaspell->gtkaspeller->dictionary->encoding);
951 /* get_word_from_pos () - return the word pointed to. */
952 /* Handles correctly the quotes. */
953 static gboolean get_word_from_pos(GtkAspell *gtkaspell, gint pos,
954 unsigned char* buf, gint buflen,
955 gint *pstart, gint *pend)
958 /* TODO : when correcting a word into quotes, change the color of */
959 /* the quotes too, as may be they were highlighted before. To do */
960 /* this, we can use two others pointers that points to the whole */
961 /* word including quotes. */
967 GtkTextView *gtktext;
969 gtktext = gtkaspell->gtktext;
970 if (iswordsep(get_text_index_whar(gtkaspell, pos)))
973 /* The apostrophe character is somtimes used for quotes
974 * So include it in the word only if it is not surrounded
975 * by other characters.
978 for (start = pos; start >= 0; --start) {
979 c = get_text_index_whar(gtkaspell, start);
982 if (!isalpha(get_text_index_whar(gtkaspell,
984 /* start_quote = TRUE; */
989 /* start_quote = TRUE; */
993 else if (!isalpha(c))
999 for (end = pos; end < get_textview_buffer_charcount(gtktext); end++) {
1000 c = get_text_index_whar(gtkaspell, end);
1002 if (end < get_textview_buffer_charcount(gtktext)) {
1003 if (!isalpha(get_text_index_whar(gtkaspell,
1005 /* end_quote = TRUE; */
1010 /* end_quote = TRUE; */
1014 else if(!isalpha(c))
1024 if (end - start < buflen) {
1025 for (pos = start; pos < end; pos++) {
1027 get_text_index_whar(gtkaspell, pos);
1029 buf[pos - start] = 0;
1037 static gboolean check_at(GtkAspell *gtkaspell, gint from_pos)
1040 unsigned char buf[GTKASPELLWORDSIZE];
1041 GtkTextView *gtktext;
1043 g_return_val_if_fail(from_pos >= 0, FALSE);
1045 gtktext = gtkaspell->gtktext;
1047 if (!get_word_from_pos(gtkaspell, from_pos, buf, sizeof(buf),
1051 if (misspelled_test(gtkaspell, buf)) {
1052 strncpy(gtkaspell->theword, buf, GTKASPELLWORDSIZE - 1);
1053 gtkaspell->theword[GTKASPELLWORDSIZE - 1] = 0;
1054 gtkaspell->start_pos = start;
1055 gtkaspell->end_pos = end;
1056 free_suggestions_list(gtkaspell);
1058 change_color(gtkaspell, start, end, buf, &(gtkaspell->highlight));
1061 change_color(gtkaspell, start, end, buf, NULL);
1066 static gboolean check_next_prev(GtkAspell *gtkaspell, gboolean forward)
1072 gboolean misspelled;
1075 maxpos = gtkaspell->end_check_pos;
1083 pos = get_textview_buffer_offset(gtkaspell->gtktext);
1084 gtkaspell->orig_pos = pos;
1085 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1086 pos > minpos && pos <= maxpos)
1088 while (!(misspelled = check_at(gtkaspell, pos)) &&
1089 pos > minpos && pos <= maxpos) {
1091 while (!iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1092 pos > minpos && pos <= maxpos)
1095 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1096 pos > minpos && pos <= maxpos)
1100 GtkMenu *menu = NULL;
1101 misspelled_suggest(gtkaspell, gtkaspell->theword);
1104 gtkaspell->orig_pos = gtkaspell->end_pos;
1106 set_textview_buffer_offset(gtkaspell->gtktext,
1107 gtkaspell->end_pos);
1108 /* scroll line to window center */
1109 gtk_text_view_scroll_to_mark(gtkaspell->gtktext,
1110 gtk_text_buffer_get_insert(
1111 gtk_text_view_get_buffer(gtkaspell->gtktext)),
1112 0.0, TRUE, 0.0, 0.5);
1113 /* let textview recalculate coordinates (set_menu_pos) */
1114 while (gtk_events_pending ())
1115 gtk_main_iteration ();
1117 menu = make_sug_menu(gtkaspell);
1118 gtk_menu_popup(menu, NULL, NULL,
1119 set_menu_pos, gtkaspell, 0, GDK_CURRENT_TIME);
1120 g_signal_connect(G_OBJECT(menu), "deactivate",
1121 G_CALLBACK(destroy_menu),
1125 reset_theword_data(gtkaspell);
1127 alertpanel_notice(_("No misspelled word found."));
1128 set_textview_buffer_offset(gtkaspell->gtktext,
1129 gtkaspell->orig_pos);
1135 void gtkaspell_check_backwards(GtkAspell *gtkaspell)
1137 gtkaspell->continue_check = NULL;
1138 gtkaspell->end_check_pos =
1139 get_textview_buffer_charcount(gtkaspell->gtktext);
1140 check_next_prev(gtkaspell, FALSE);
1143 void gtkaspell_check_forwards_go(GtkAspell *gtkaspell)
1146 gtkaspell->continue_check = NULL;
1147 gtkaspell->end_check_pos =
1148 get_textview_buffer_charcount(gtkaspell->gtktext);
1149 check_next_prev(gtkaspell, TRUE);
1152 void gtkaspell_check_all(GtkAspell *gtkaspell)
1154 GtkTextView *gtktext;
1156 GtkTextBuffer *buffer;
1157 GtkTextIter startiter, enditer;
1159 g_return_if_fail(gtkaspell);
1160 g_return_if_fail(gtkaspell->gtktext);
1162 gtktext = gtkaspell->gtktext;
1163 buffer = gtk_text_view_get_buffer(gtktext);
1164 gtk_text_buffer_get_selection_bounds(buffer, &startiter, &enditer);
1165 start = gtk_text_iter_get_offset(&startiter);
1166 end = gtk_text_iter_get_offset(&enditer);
1170 end = gtk_text_buffer_get_char_count(buffer);
1171 } else if (start > end) {
1179 set_textview_buffer_offset(gtktext, start);
1181 gtkaspell->continue_check = continue_check;
1182 gtkaspell->end_check_pos = end;
1184 gtkaspell->misspelled = check_next_prev(gtkaspell, TRUE);
1187 static void continue_check(gpointer *data)
1189 GtkAspell *gtkaspell = (GtkAspell *) data;
1190 gint pos = get_textview_buffer_offset(gtkaspell->gtktext);
1191 if (pos < gtkaspell->end_check_pos && gtkaspell->misspelled)
1192 gtkaspell->misspelled = check_next_prev(gtkaspell, TRUE);
1194 gtkaspell->continue_check = NULL;
1197 void gtkaspell_highlight_all(GtkAspell *gtkaspell)
1202 GtkTextView *gtktext;
1204 g_return_if_fail(gtkaspell->gtkaspeller->checker);
1206 gtktext = gtkaspell->gtktext;
1208 len = get_textview_buffer_charcount(gtktext);
1210 origpos = get_textview_buffer_offset(gtktext);
1214 iswordsep(get_text_index_whar(gtkaspell, pos)))
1217 !iswordsep(get_text_index_whar(gtkaspell, pos)))
1220 check_at(gtkaspell, pos - 1);
1222 set_textview_buffer_offset(gtktext, origpos);
1225 static void replace_with_supplied_word_cb(GtkWidget *w, GtkAspell *gtkaspell)
1227 unsigned char *newword;
1228 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1230 newword = gtk_editable_get_chars(GTK_EDITABLE(gtkaspell->replace_entry),
1233 if (strcmp(newword, gtkaspell->theword)) {
1234 replace_real_word(gtkaspell, newword);
1236 if ((e->type == GDK_KEY_PRESS &&
1237 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK)) {
1238 aspell_speller_store_replacement(
1239 gtkaspell->gtkaspeller->checker,
1240 gtkaspell->theword, -1,
1243 gtkaspell->replace_entry = NULL;
1248 set_point_continue(gtkaspell);
1252 static void replace_word_cb(GtkWidget *w, gpointer data)
1254 unsigned char *newword;
1255 GtkAspell *gtkaspell = (GtkAspell *) data;
1256 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1258 gtk_label_get(GTK_LABEL(GTK_BIN(w)->child), (gchar**) &newword);
1260 replace_real_word(gtkaspell, newword);
1262 if ((e->type == GDK_KEY_PRESS &&
1263 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK) ||
1264 (e->type == GDK_BUTTON_RELEASE &&
1265 ((GdkEventButton *) e)->state & GDK_CONTROL_MASK)) {
1266 aspell_speller_store_replacement(
1267 gtkaspell->gtkaspeller->checker,
1268 gtkaspell->theword, -1,
1272 gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1274 set_point_continue(gtkaspell);
1277 static void replace_real_word(GtkAspell *gtkaspell, gchar *newword)
1279 int oldlen, newlen, wordlen;
1282 gint start = gtkaspell->start_pos;
1283 GtkTextView *gtktext;
1284 GtkTextBuffer *textbuf;
1285 GtkTextIter startiter, enditer;
1287 if (!newword) return;
1289 gtktext = gtkaspell->gtktext;
1290 textbuf = gtk_text_view_get_buffer(gtktext);
1292 origpos = gtkaspell->orig_pos;
1294 oldlen = gtkaspell->end_pos - gtkaspell->start_pos;
1295 wordlen = strlen(gtkaspell->theword);
1297 newlen = strlen(newword); /* FIXME: multybyte characters? */
1299 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1300 G_CALLBACK(entry_insert_cb),
1302 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1303 G_CALLBACK(entry_delete_cb),
1306 gtk_text_buffer_get_iter_at_offset(textbuf, &startiter,
1307 gtkaspell->start_pos);
1308 gtk_text_buffer_get_iter_at_offset(textbuf, &enditer,
1309 gtkaspell->end_pos);
1310 g_signal_emit_by_name(G_OBJECT(textbuf), "delete-range",
1311 &startiter, &enditer, gtkaspell);
1312 g_signal_emit_by_name(G_OBJECT(textbuf), "insert-text",
1313 &startiter, newword, newlen, gtkaspell);
1315 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1316 G_CALLBACK(entry_insert_cb),
1318 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1319 G_CALLBACK(entry_delete_cb),
1322 /* Put the point and the position where we clicked with the mouse
1323 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1324 * to let it update correctly the word insertion and then the
1325 * point & position position. If not, SEGV after the first replacement
1326 * If the new word ends before point, put the point at its end.
1329 if (origpos - gtkaspell->start_pos < oldlen &&
1330 origpos - gtkaspell->start_pos >= 0) {
1331 /* Original point was in the word.
1332 * Let it there unless point is going to be outside of the word
1334 if (origpos - gtkaspell->start_pos >= newlen) {
1335 pos = gtkaspell->start_pos + newlen;
1338 else if (origpos >= gtkaspell->end_pos) {
1339 /* move the position according to the change of length */
1340 pos = origpos + newlen - oldlen;
1343 gtkaspell->end_pos = gtkaspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1345 if (get_textview_buffer_charcount(gtktext) < pos)
1346 pos = get_textview_buffer_charcount(gtktext);
1347 gtkaspell->orig_pos = pos;
1349 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1352 /* Accept this word for this session */
1353 static void add_word_to_session_cb(GtkWidget *w, gpointer data)
1356 GtkTextView *gtktext;
1357 GtkAspell *gtkaspell = (GtkAspell *) data;
1358 gtktext = gtkaspell->gtktext;
1360 pos = get_textview_buffer_offset(gtktext);
1362 aspell_speller_add_to_session(gtkaspell->gtkaspeller->checker,
1364 strlen(gtkaspell->theword));
1366 check_at(gtkaspell, gtkaspell->start_pos);
1368 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1370 set_point_continue(gtkaspell);
1373 /* add_word_to_personal_cb() - add word to personal dict. */
1374 static void add_word_to_personal_cb(GtkWidget *w, gpointer data)
1376 GtkAspell *gtkaspell = (GtkAspell *) data;
1377 GtkTextView *gtktext = gtkaspell->gtktext;
1379 aspell_speller_add_to_personal(gtkaspell->gtkaspeller->checker,
1381 strlen(gtkaspell->theword));
1383 check_at(gtkaspell, gtkaspell->start_pos);
1385 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1386 set_point_continue(gtkaspell);
1389 static void check_with_alternate_cb(GtkWidget *w, gpointer data)
1391 GtkAspell *gtkaspell = (GtkAspell *) data;
1394 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1396 use_alternate_dict(gtkaspell);
1397 misspelled = check_at(gtkaspell, gtkaspell->start_pos);
1399 if (!gtkaspell->continue_check) {
1401 gtkaspell->misspelled = misspelled;
1403 if (gtkaspell->misspelled) {
1405 misspelled_suggest(gtkaspell, gtkaspell->theword);
1407 set_textview_buffer_offset(gtkaspell->gtktext,
1408 gtkaspell->end_pos);
1410 menu = make_sug_menu(gtkaspell);
1411 gtk_menu_popup(menu, NULL, NULL,
1412 set_menu_pos, gtkaspell, 0,
1414 g_signal_connect(G_OBJECT(menu), "deactivate",
1415 G_CALLBACK(destroy_menu),
1420 gtkaspell->orig_pos = gtkaspell->start_pos;
1422 set_point_continue(gtkaspell);
1425 static gboolean replace_key_pressed(GtkWidget *widget,
1427 GtkAspell *gtkaspell)
1429 if (event && event->keyval == GDK_Escape) {
1430 gtk_widget_destroy(widget);
1432 } else if (event && event->keyval == GDK_Return) {
1433 replace_with_supplied_word_cb(NULL, gtkaspell);
1434 gtk_widget_destroy(widget);
1440 static void replace_with_create_dialog_cb(GtkWidget *w, gpointer data)
1446 GtkWidget *ok_button;
1447 GtkWidget *cancel_button;
1450 GtkAspell *gtkaspell = (GtkAspell *) data;
1452 gdk_window_get_origin((GTK_WIDGET(w)->parent)->window, &xx, &yy);
1454 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1456 dialog = gtk_dialog_new();
1458 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1459 gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1460 gtk_widget_set_uposition(dialog, xx, yy);
1462 g_signal_connect_swapped(G_OBJECT(dialog), "destroy",
1463 G_CALLBACK(gtk_widget_destroy),
1466 hbox = gtk_hbox_new(FALSE, 0);
1467 gtk_container_set_border_width(GTK_CONTAINER(hbox), 8);
1469 thelabel = g_strdup_printf(_("Replace \"%s\" with: "),
1470 gtkaspell->theword);
1471 label = gtk_label_new(thelabel);
1473 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1475 entry = gtk_entry_new();
1476 gtkaspell->replace_entry = entry;
1477 gtk_entry_set_text(GTK_ENTRY(entry), gtkaspell->theword);
1478 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1479 g_signal_connect(G_OBJECT(dialog),
1481 G_CALLBACK(replace_key_pressed), gtkaspell);
1482 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
1484 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE,
1486 label = gtk_label_new(_("Holding down Control key while pressing "
1487 "Enter\nwill learn from mistake.\n"));
1488 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1489 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
1490 gtk_misc_set_padding(GTK_MISC(label), 8, 0);
1491 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label,
1494 hbox = gtk_hbox_new(TRUE, 0);
1496 ok_button = gtk_button_new_with_label(_("OK"));
1497 gtk_box_pack_start(GTK_BOX(hbox), ok_button, TRUE, TRUE, 8);
1498 g_signal_connect(G_OBJECT(ok_button), "clicked",
1499 G_CALLBACK(replace_with_supplied_word_cb),
1501 g_signal_connect_swapped(G_OBJECT(ok_button), "clicked",
1502 G_CALLBACK(gtk_widget_destroy),
1505 cancel_button = gtk_button_new_with_label(_("Cancel"));
1506 gtk_box_pack_start(GTK_BOX(hbox), cancel_button, TRUE, TRUE, 8);
1507 g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked",
1508 G_CALLBACK(gtk_widget_destroy),
1511 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), hbox);
1513 gtk_widget_grab_focus(entry);
1515 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1517 gtk_widget_show_all(dialog);
1520 void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1525 GtkTextView *gtktext;
1526 GtkTextBuffer *buffer;
1527 GtkTextIter startiter, enditer;
1529 gtktext = gtkaspell->gtktext;
1531 buffer = gtk_text_view_get_buffer(gtktext);
1532 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, 0);
1533 gtk_text_buffer_get_iter_at_offset(buffer, &enditer,
1534 get_textview_buffer_charcount(gtktext)-1);
1535 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
1536 &startiter, &enditer);
1539 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1541 GtkAspell *gtkaspell = (GtkAspell *) data;
1543 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1545 if (!gtkaspell->check_while_typing)
1546 gtkaspell_uncheck_all(gtkaspell);
1548 if (gtkaspell->config_menu)
1549 populate_submenu(gtkaspell, gtkaspell->config_menu);
1552 static GSList *create_empty_dictionary_list(void)
1554 GSList *list = NULL;
1557 dict = g_new0(Dictionary, 1);
1558 dict->fullname = g_strdup(_("None"));
1559 dict->dictname = dict->fullname;
1560 dict->encoding = NULL;
1562 return g_slist_append(list, dict);
1565 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1566 GSList *gtkaspell_get_dictionary_list(const gchar *aspell_path, gint refresh)
1570 AspellConfig *config;
1571 AspellDictInfoList *dlist;
1572 AspellDictInfoEnumeration *dels;
1573 const AspellDictInfo *entry;
1575 if (!gtkaspellcheckers)
1576 gtkaspell_checkers_init();
1578 if (gtkaspellcheckers->dictionary_list && !refresh)
1579 return gtkaspellcheckers->dictionary_list;
1581 gtkaspell_free_dictionary_list(
1582 gtkaspellcheckers->dictionary_list);
1585 config = new_aspell_config();
1587 aspell_config_replace(config, "rem-all-word-list-path", "");
1588 if (aspell_config_error_number(config) != 0) {
1589 gtkaspellcheckers->error_message = g_strdup(
1590 aspell_config_error_message(config));
1591 gtkaspellcheckers->dictionary_list =
1592 create_empty_dictionary_list();
1594 return gtkaspellcheckers->dictionary_list;
1597 aspell_config_replace(config, "dict-dir", aspell_path);
1598 if (aspell_config_error_number(config) != 0) {
1599 gtkaspellcheckers->error_message = g_strdup(
1600 aspell_config_error_message(config));
1601 gtkaspellcheckers->dictionary_list =
1602 create_empty_dictionary_list();
1604 return gtkaspellcheckers->dictionary_list;
1607 dlist = get_aspell_dict_info_list(config);
1608 delete_aspell_config(config);
1610 debug_print("Aspell: checking for dictionaries in %s\n", aspell_path);
1611 dels = aspell_dict_info_list_elements(dlist);
1612 while ( (entry = aspell_dict_info_enumeration_next(dels)) != 0)
1614 dict = g_new0(Dictionary, 1);
1615 dict->fullname = g_strdup_printf("%s%s", aspell_path,
1617 dict->dictname = dict->fullname + strlen(aspell_path);
1618 dict->encoding = g_strdup(entry->code);
1619 debug_print("Aspell: found dictionary %s %s\n", dict->fullname,
1621 list = g_slist_insert_sorted(list, dict,
1622 (GCompareFunc) compare_dict);
1625 delete_aspell_dict_info_enumeration(dels);
1629 debug_print("Aspell: error when searching for dictionaries: "
1630 "No dictionary found.\n");
1631 list = create_empty_dictionary_list();
1634 gtkaspellcheckers->dictionary_list = list;
1639 void gtkaspell_free_dictionary_list(GSList *list)
1643 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1645 dict = (Dictionary *) walk->data;
1646 dictionary_delete(dict);
1651 GtkWidget *gtkaspell_dictionary_option_menu_new(const gchar *aspell_path)
1653 GSList *dict_list, *tmp;
1658 dict_list = gtkaspell_get_dictionary_list(aspell_path, TRUE);
1659 g_return_val_if_fail(dict_list, NULL);
1661 menu = gtk_menu_new();
1663 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1664 dict = (Dictionary *) tmp->data;
1665 item = gtk_menu_item_new_with_label(dict->dictname);
1666 g_object_set_data(G_OBJECT(item), "dict_name",
1669 gtk_menu_append(GTK_MENU(menu), item);
1670 gtk_widget_show(item);
1673 gtk_widget_show(menu);
1678 gchar *gtkaspell_get_dictionary_menu_active_item(GtkWidget *menu)
1680 GtkWidget *menuitem;
1681 gchar *dict_fullname;
1684 g_return_val_if_fail(GTK_IS_MENU(menu), NULL);
1686 menuitem = gtk_menu_get_active(GTK_MENU(menu));
1687 dict_fullname = (gchar *) g_object_get_data(G_OBJECT(menuitem),
1689 g_return_val_if_fail(dict_fullname, NULL);
1691 label = g_strdup(dict_fullname);
1697 gint gtkaspell_set_dictionary_menu_active_item(GtkWidget *menu,
1698 const gchar *dictionary)
1703 g_return_val_if_fail(menu != NULL, 0);
1704 g_return_val_if_fail(dictionary != NULL, 0);
1705 g_return_val_if_fail(GTK_IS_OPTION_MENU(menu), 0);
1708 for (cur = GTK_MENU_SHELL(gtk_option_menu_get_menu(
1709 GTK_OPTION_MENU(menu)))->children;
1710 cur != NULL; cur = cur->next) {
1711 GtkWidget *menuitem;
1714 menuitem = GTK_WIDGET(cur->data);
1715 dict_name = g_object_get_data(G_OBJECT(menuitem),
1717 if ((dict_name != NULL) && !strcmp2(dict_name, dictionary)) {
1718 gtk_option_menu_set_history(GTK_OPTION_MENU(menu), n);
1728 GtkWidget *gtkaspell_sugmode_option_menu_new(gint sugmode)
1733 menu = gtk_menu_new();
1734 gtk_widget_show(menu);
1736 item = gtk_menu_item_new_with_label(_("Fast Mode"));
1737 gtk_widget_show(item);
1738 gtk_menu_append(GTK_MENU(menu), item);
1739 g_object_set_data(G_OBJECT(item), "sugmode",
1740 GINT_TO_POINTER(ASPELL_FASTMODE));
1742 item = gtk_menu_item_new_with_label(_("Normal Mode"));
1743 gtk_widget_show(item);
1744 gtk_menu_append(GTK_MENU(menu), item);
1745 g_object_set_data(G_OBJECT(item), "sugmode",
1746 GINT_TO_POINTER(ASPELL_NORMALMODE));
1748 item = gtk_menu_item_new_with_label(_("Bad Spellers Mode"));
1749 gtk_widget_show(item);
1750 gtk_menu_append(GTK_MENU(menu), item);
1751 g_object_set_data(G_OBJECT(item), "sugmode",
1752 GINT_TO_POINTER(ASPELL_BADSPELLERMODE));
1757 void gtkaspell_sugmode_option_menu_set(GtkOptionMenu *optmenu, gint sugmode)
1759 g_return_if_fail(GTK_IS_OPTION_MENU(optmenu));
1761 g_return_if_fail(sugmode == ASPELL_FASTMODE ||
1762 sugmode == ASPELL_NORMALMODE ||
1763 sugmode == ASPELL_BADSPELLERMODE);
1765 gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), sugmode - 1);
1768 gint gtkaspell_get_sugmode_from_option_menu(GtkOptionMenu *optmenu)
1773 g_return_val_if_fail(GTK_IS_OPTION_MENU(optmenu), -1);
1775 item = gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(optmenu)));
1777 sugmode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item),
1783 static void use_alternate_dict(GtkAspell *gtkaspell)
1787 tmp = gtkaspell->gtkaspeller;
1788 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1789 gtkaspell->alternate_speller = tmp;
1791 if (gtkaspell->config_menu)
1792 populate_submenu(gtkaspell, gtkaspell->config_menu);
1795 static void destroy_menu(GtkWidget *widget,
1796 gpointer user_data) {
1798 GtkAspell *gtkaspell = (GtkAspell *)user_data;
1800 if (gtkaspell->accel_group) {
1801 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1802 gtkaspell->accel_group);
1803 gtkaspell->accel_group = NULL;
1807 static void popup_menu(GtkAspell *gtkaspell, GdkEventButton *eb)
1809 GtkTextView * gtktext;
1810 GtkMenu *menu = NULL;
1812 gtktext = gtkaspell->gtktext;
1814 gtkaspell->orig_pos = get_textview_buffer_offset(gtktext);
1816 if (!(eb->state & GDK_SHIFT_MASK)) {
1817 if (check_at(gtkaspell, gtkaspell->orig_pos)) {
1819 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1821 if (misspelled_suggest(gtkaspell, gtkaspell->theword)) {
1822 menu = make_sug_menu(gtkaspell);
1823 gtk_menu_popup(menu,
1824 NULL, NULL, NULL, NULL,
1825 eb->button, eb->time);
1827 g_signal_connect(G_OBJECT(menu), "deactivate",
1828 G_CALLBACK(destroy_menu),
1833 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1835 menu = make_config_menu(gtkaspell);
1836 gtk_menu_popup(menu, NULL, NULL, NULL, NULL,
1837 eb->button, eb->time);
1839 g_signal_connect(G_OBJECT(menu), "deactivate",
1840 G_CALLBACK(destroy_menu),
1844 static gboolean aspell_key_pressed(GtkWidget *widget,
1846 GtkAspell *gtkaspell)
1848 if (event && (isascii(event->keyval) || event->keyval == GDK_Return)) {
1849 gtk_accel_groups_activate(
1850 G_OBJECT(gtkaspell->parent_window),
1851 event->keyval, event->state);
1852 } else if (event && event->keyval == GDK_Escape) {
1853 destroy_menu(NULL, gtkaspell);
1858 /* make_sug_menu() - Add menus to accept this word for this session
1859 * and to add it to personal dictionary
1861 static GtkMenu *make_sug_menu(GtkAspell *gtkaspell)
1863 GtkWidget *menu, *item;
1864 unsigned char *caption;
1865 GtkTextView *gtktext;
1866 GtkAccelGroup *accel;
1867 GList *l = gtkaspell->suggestions_list;
1870 gtktext = gtkaspell->gtktext;
1872 accel = gtk_accel_group_new();
1873 menu = gtk_menu_new();
1875 if (gtkaspell->sug_menu)
1876 gtk_widget_destroy(gtkaspell->sug_menu);
1878 if (gtkaspell->accel_group) {
1879 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1880 gtkaspell->accel_group);
1881 gtkaspell->accel_group = NULL;
1884 gtkaspell->sug_menu = menu;
1886 g_signal_connect(G_OBJECT(menu), "cancel",
1887 G_CALLBACK(cancel_menu_cb), gtkaspell);
1889 caption = g_strdup_printf(_("\"%s\" unknown in %s"),
1890 (unsigned char*) l->data,
1891 gtkaspell->gtkaspeller->dictionary->dictname);
1892 item = gtk_menu_item_new_with_label(caption);
1893 gtk_widget_show(item);
1894 gtk_menu_append(GTK_MENU(menu), item);
1895 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
1898 item = gtk_menu_item_new();
1899 gtk_widget_show(item);
1900 gtk_menu_append(GTK_MENU(menu), item);
1902 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1903 gtk_widget_show(item);
1904 gtk_menu_append(GTK_MENU(menu), item);
1905 g_signal_connect(G_OBJECT(item), "activate",
1906 G_CALLBACK(add_word_to_session_cb),
1908 gtk_widget_add_accelerator(item, "activate", accel, GDK_space,
1910 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1912 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1913 gtk_widget_show(item);
1914 gtk_menu_append(GTK_MENU(menu), item);
1915 g_signal_connect(G_OBJECT(item), "activate",
1916 G_CALLBACK(add_word_to_personal_cb),
1918 gtk_widget_add_accelerator(item, "activate", accel, GDK_Return,
1920 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1922 item = gtk_menu_item_new_with_label(_("Replace with..."));
1923 gtk_widget_show(item);
1924 gtk_menu_append(GTK_MENU(menu), item);
1925 g_signal_connect(G_OBJECT(item), "activate",
1926 G_CALLBACK(replace_with_create_dialog_cb),
1928 gtk_widget_add_accelerator(item, "activate", accel, GDK_R, 0,
1929 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1930 gtk_widget_add_accelerator(item, "activate", accel, GDK_R,
1934 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1935 caption = g_strdup_printf(_("Check with %s"),
1936 gtkaspell->alternate_speller->dictionary->dictname);
1937 item = gtk_menu_item_new_with_label(caption);
1939 gtk_widget_show(item);
1940 gtk_menu_append(GTK_MENU(menu), item);
1941 g_signal_connect(G_OBJECT(item), "activate",
1942 G_CALLBACK(check_with_alternate_cb),
1944 gtk_widget_add_accelerator(item, "activate", accel, GDK_X, 0,
1945 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1946 gtk_widget_add_accelerator(item, "activate", accel, GDK_X,
1951 item = gtk_menu_item_new();
1952 gtk_widget_show(item);
1953 gtk_menu_append(GTK_MENU(menu), item);
1957 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
1958 gtk_widget_show(item);
1959 gtk_menu_append(GTK_MENU(menu), item);
1961 GtkWidget *curmenu = menu;
1965 if (count == MENUCOUNT) {
1968 item = gtk_menu_item_new_with_label(_("More..."));
1969 gtk_widget_show(item);
1970 gtk_menu_append(GTK_MENU(curmenu), item);
1972 curmenu = gtk_menu_new();
1973 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1977 utf8buf = conv_codeset_strdup((unsigned char*)l->data,
1978 conv_get_locale_charset_str(),
1980 item = gtk_menu_item_new_with_label(utf8buf);
1981 gtk_widget_show(item);
1982 gtk_menu_append(GTK_MENU(curmenu), item);
1983 g_signal_connect(G_OBJECT(item), "activate",
1984 G_CALLBACK(replace_word_cb),
1987 if (curmenu == menu && count < MENUCOUNT) {
1988 gtk_widget_add_accelerator(item, "activate",
1993 gtk_widget_add_accelerator(item, "activate",
2002 } while ((l = l->next) != NULL);
2005 gtk_window_add_accel_group
2006 (GTK_WINDOW(gtkaspell->parent_window),
2008 gtkaspell->accel_group = accel;
2010 g_signal_connect(G_OBJECT(menu),
2012 G_CALLBACK(aspell_key_pressed), gtkaspell);
2014 return GTK_MENU(menu);
2017 static void populate_submenu(GtkAspell *gtkaspell, GtkWidget *menu)
2019 GtkWidget *item, *submenu;
2021 GtkAspeller *gtkaspeller = gtkaspell->gtkaspeller;
2023 if (GTK_MENU_SHELL(menu)->children) {
2024 GList *amenu, *alist;
2025 for (amenu = (GTK_MENU_SHELL(menu)->children); amenu; ) {
2026 alist = amenu->next;
2027 gtk_widget_destroy(GTK_WIDGET(amenu->data));
2032 dictname = g_strdup_printf(_("Dictionary: %s"),
2033 gtkaspeller->dictionary->dictname);
2034 item = gtk_menu_item_new_with_label(dictname);
2035 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
2037 gtk_widget_show(item);
2038 gtk_menu_append(GTK_MENU(menu), item);
2040 item = gtk_menu_item_new();
2041 gtk_widget_show(item);
2042 gtk_menu_append(GTK_MENU(menu), item);
2044 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
2045 dictname = g_strdup_printf(_("Use alternate (%s)"),
2046 gtkaspell->alternate_speller->dictionary->dictname);
2047 item = gtk_menu_item_new_with_label(dictname);
2049 g_signal_connect(G_OBJECT(item), "activate",
2050 G_CALLBACK(switch_to_alternate_cb),
2052 gtk_widget_show(item);
2053 gtk_menu_append(GTK_MENU(menu), item);
2056 item = gtk_check_menu_item_new_with_label(_("Fast Mode"));
2057 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_FASTMODE) {
2058 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),TRUE);
2059 gtk_widget_set_sensitive(GTK_WIDGET(item),FALSE);
2061 g_signal_connect(G_OBJECT(item), "activate",
2062 G_CALLBACK(set_sug_mode_cb),
2064 gtk_widget_show(item);
2065 gtk_menu_append(GTK_MENU(menu), item);
2067 item = gtk_check_menu_item_new_with_label(_("Normal Mode"));
2068 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_NORMALMODE) {
2069 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
2070 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2072 g_signal_connect(G_OBJECT(item), "activate",
2073 G_CALLBACK(set_sug_mode_cb),
2075 gtk_widget_show(item);
2076 gtk_menu_append(GTK_MENU(menu),item);
2078 item = gtk_check_menu_item_new_with_label(_("Bad Spellers Mode"));
2079 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_BADSPELLERMODE) {
2080 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
2081 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2083 g_signal_connect(G_OBJECT(item), "activate",
2084 G_CALLBACK(set_sug_mode_cb),
2086 gtk_widget_show(item);
2087 gtk_menu_append(GTK_MENU(menu), item);
2089 item = gtk_menu_item_new();
2090 gtk_widget_show(item);
2091 gtk_menu_append(GTK_MENU(menu), item);
2093 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
2094 if (gtkaspell->check_while_typing)
2095 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2097 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2098 g_signal_connect(G_OBJECT(item), "activate",
2099 G_CALLBACK(toggle_check_while_typing_cb),
2101 gtk_widget_show(item);
2102 gtk_menu_append(GTK_MENU(menu), item);
2104 item = gtk_menu_item_new();
2105 gtk_widget_show(item);
2106 gtk_menu_append(GTK_MENU(menu), item);
2108 submenu = gtk_menu_new();
2109 item = gtk_menu_item_new_with_label(_("Change dictionary"));
2110 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
2111 gtk_widget_show(item);
2112 gtk_menu_append(GTK_MENU(menu), item);
2115 if (gtkaspellcheckers->dictionary_list == NULL)
2116 gtkaspell_get_dictionary_list(gtkaspell->dictionary_path, FALSE);
2118 GtkWidget * curmenu = submenu;
2122 tmp = gtkaspellcheckers->dictionary_list;
2124 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
2125 tmp = g_slist_next(tmp)) {
2126 if (count == MENUCOUNT) {
2129 newmenu = gtk_menu_new();
2130 item = gtk_menu_item_new_with_label(_("More..."));
2131 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2134 gtk_menu_append(GTK_MENU(curmenu), item);
2135 gtk_widget_show(item);
2139 dict = (Dictionary *) tmp->data;
2140 item = gtk_check_menu_item_new_with_label(dict->dictname);
2141 g_object_set_data(G_OBJECT(item), "dict_name",
2143 if (strcmp2(dict->fullname,
2144 gtkaspell->gtkaspeller->dictionary->fullname))
2145 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2147 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2148 gtk_widget_set_sensitive(GTK_WIDGET(item),
2151 g_signal_connect(G_OBJECT(item), "activate",
2152 G_CALLBACK(change_dict_cb),
2154 gtk_widget_show(item);
2155 gtk_menu_append(GTK_MENU(curmenu), item);
2162 static GtkMenu *make_config_menu(GtkAspell *gtkaspell)
2164 if (!gtkaspell->popup_config_menu)
2165 gtkaspell->popup_config_menu = gtk_menu_new();
2167 debug_print("Aspell: creating/using popup_config_menu %0x\n",
2168 (guint) gtkaspell->popup_config_menu);
2169 populate_submenu(gtkaspell, gtkaspell->popup_config_menu);
2171 return GTK_MENU(gtkaspell->popup_config_menu);
2174 void gtkaspell_populate_submenu(GtkAspell *gtkaspell, GtkWidget *menuitem)
2178 menu = GTK_WIDGET(GTK_MENU_ITEM(menuitem)->submenu);
2180 debug_print("Aspell: using config menu %0x\n",
2181 (guint) gtkaspell->popup_config_menu);
2182 populate_submenu(gtkaspell, menu);
2184 gtkaspell->config_menu = menu;
2188 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y,
2189 gboolean *push_in, gpointer data)
2191 GtkAspell *gtkaspell = (GtkAspell *) data;
2192 gint xx = 0, yy = 0;
2195 GtkTextView *text = GTK_TEXT_VIEW(gtkaspell->gtktext);
2196 GtkTextBuffer *textbuf;
2201 textbuf = gtk_text_view_get_buffer(gtkaspell->gtktext);
2202 gtk_text_buffer_get_iter_at_mark(textbuf, &iter,
2203 gtk_text_buffer_get_insert(textbuf));
2204 gtk_text_view_get_iter_location(gtkaspell->gtktext, &iter, &rect);
2205 gtk_text_view_buffer_to_window_coords(text, GTK_TEXT_WINDOW_TEXT,
2209 gdk_window_get_origin(GTK_WIDGET(gtkaspell->gtktext)->window, &xx, &yy);
2211 sx = gdk_screen_width();
2212 sy = gdk_screen_height();
2214 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2220 gdk_char_width(gtk_style_get_font(GTK_WIDGET(text)->style), ' ');
2222 *y = rect.y + rect.height + yy;
2228 gdk_string_height(gtk_style_get_font(
2229 GTK_WIDGET(text)->style),
2230 gtkaspell->theword);
2233 /* Menu call backs */
2235 static gboolean cancel_menu_cb(GtkMenuShell *w, gpointer data)
2237 GtkAspell *gtkaspell = (GtkAspell *) data;
2239 gtkaspell->continue_check = NULL;
2240 set_point_continue(gtkaspell);
2245 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary)
2248 GtkAspeller *gtkaspeller;
2251 g_return_val_if_fail(gtkaspell, FALSE);
2252 g_return_val_if_fail(dictionary, FALSE);
2254 sug_mode = gtkaspell->default_sug_mode;
2256 dict = g_new0(Dictionary, 1);
2257 dict->fullname = g_strdup(dictionary);
2258 dict->encoding = g_strdup(gtkaspell->gtkaspeller->dictionary->encoding);
2260 if (gtkaspell->use_alternate && gtkaspell->alternate_speller &&
2261 dict == gtkaspell->alternate_speller->dictionary) {
2262 use_alternate_dict(gtkaspell);
2263 dictionary_delete(dict);
2267 gtkaspeller = gtkaspeller_new(dict);
2271 message = g_strdup_printf(_("The spell checker could not change dictionary.\n%s"),
2272 gtkaspellcheckers->error_message);
2274 alertpanel_warning(message);
2277 if (gtkaspell->use_alternate) {
2278 if (gtkaspell->alternate_speller)
2279 gtkaspeller_delete(gtkaspell->alternate_speller);
2280 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2282 gtkaspeller_delete(gtkaspell->gtkaspeller);
2284 gtkaspell->gtkaspeller = gtkaspeller;
2285 gtkaspell_set_sug_mode(gtkaspell, sug_mode);
2288 dictionary_delete(dict);
2290 if (gtkaspell->config_menu)
2291 populate_submenu(gtkaspell, gtkaspell->config_menu);
2296 /* change_dict_cb() - Menu callback : change dict */
2297 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2301 fullname = (gchar *) g_object_get_data(G_OBJECT(w), "dict_name");
2303 if (!strcmp2(fullname, _("None")))
2306 gtkaspell_change_dict(gtkaspell, fullname);
2309 static void switch_to_alternate_cb(GtkWidget *w,
2312 GtkAspell *gtkaspell = (GtkAspell *) data;
2313 use_alternate_dict(gtkaspell);
2316 /* Misc. helper functions */
2318 static void set_point_continue(GtkAspell *gtkaspell)
2320 GtkTextView *gtktext;
2322 gtktext = gtkaspell->gtktext;
2324 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
2326 if (gtkaspell->continue_check)
2327 gtkaspell->continue_check((gpointer *) gtkaspell);
2330 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2332 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtkaspell->gtktext);
2333 GdkColor *color = &(gtkaspell->highlight);
2335 /* Shameless copy from Sylpheed's gtkutils.c */
2337 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2339 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2341 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2344 gtk_text_buffer_create_tag(buffer, "misspelled",
2345 "foreground-gdk", color, NULL);
2348 static void change_color(GtkAspell * gtkaspell,
2349 gint start, gint end,
2353 GtkTextView *gtktext;
2354 GtkTextBuffer *buffer;
2355 GtkTextIter startiter, enditer;
2357 g_return_if_fail(start < end);
2359 gtktext = gtkaspell->gtktext;
2361 buffer = gtk_text_view_get_buffer(gtktext);
2362 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, start);
2363 gtk_text_buffer_get_iter_at_offset(buffer, &enditer, end);
2365 gtk_text_buffer_apply_tag_by_name(buffer, "misspelled",
2366 &startiter, &enditer);
2368 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
2369 &startiter, &enditer);
2372 /* convert_to_aspell_encoding () - converts ISO-8859-* strings to iso8859-*
2373 * as needed by aspell. Returns an allocated string.
2376 static guchar *convert_to_aspell_encoding (const guchar *encoding)
2378 guchar * aspell_encoding;
2380 if (strstr2(encoding, "ISO-8859-")) {
2381 aspell_encoding = g_strdup_printf("iso8859%s", encoding+8);
2384 if (!strcmp2(encoding, "US-ASCII"))
2385 aspell_encoding = g_strdup("iso8859-1");
2387 aspell_encoding = g_strdup(encoding);
2390 return aspell_encoding;
2393 /* compare_dict () - compare 2 dict names */
2394 static gint compare_dict(Dictionary *a, Dictionary *b)
2396 guint aparts = 0, bparts = 0;
2399 for (i=0; i < strlen(a->dictname); i++)
2400 if (a->dictname[i] == '-')
2402 for (i=0; i < strlen(b->dictname); i++)
2403 if (b->dictname[i] == '-')
2406 if (aparts != bparts)
2407 return (aparts < bparts) ? -1 : +1;
2410 compare = strcmp2(a->dictname, b->dictname);
2412 compare = strcmp2(a->fullname, b->fullname);
2417 static void dictionary_delete(Dictionary *dict)
2419 g_free(dict->fullname);
2420 g_free(dict->encoding);
2424 static Dictionary *dictionary_dup(const Dictionary *dict)
2428 dict2 = g_new(Dictionary, 1);
2430 dict2->fullname = g_strdup(dict->fullname);
2431 dict2->dictname = dict->dictname - dict->fullname + dict2->fullname;
2432 dict2->encoding = g_strdup(dict->encoding);
2437 static void free_suggestions_list(GtkAspell *gtkaspell)
2441 for (list = gtkaspell->suggestions_list; list != NULL;
2447 gtkaspell->max_sug = -1;
2448 gtkaspell->suggestions_list = NULL;
2451 static void reset_theword_data(GtkAspell *gtkaspell)
2453 gtkaspell->start_pos = 0;
2454 gtkaspell->end_pos = 0;
2455 gtkaspell->theword[0] = 0;
2456 gtkaspell->max_sug = -1;
2458 free_suggestions_list(gtkaspell);
2461 static void free_checkers(gpointer elt, gpointer data)
2463 GtkAspeller *gtkaspeller = elt;
2465 g_return_if_fail(gtkaspeller);
2467 gtkaspeller_real_delete(gtkaspeller);
2470 static gint find_gtkaspeller(gconstpointer aa, gconstpointer bb)
2472 Dictionary *a = ((GtkAspeller *) aa)->dictionary;
2473 Dictionary *b = ((GtkAspeller *) bb)->dictionary;
2475 if (a && b && a->fullname && b->fullname &&
2476 strcmp(a->fullname, b->fullname) == 0 &&
2477 a->encoding && b->encoding)
2478 return strcmp(a->encoding, b->encoding);