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 3 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, see <http://www.gnu.org/licenses/>.
19 * Stuphead: (C) 2000,2001 Grigroy Bakunov, Sergey Pinaev
20 * Adapted for Sylpheed (Claws) (c) 2001-2002 by Hiroyuki Yamamoto &
21 * The Claws Mail Team.
22 * Adapted for pspell (c) 2001-2002 Melvin Hadasht
23 * Adapted for GNU/aspell (c) 2002 Melvin Hadasht
35 #include <sys/types.h>
47 #include <glib/gi18n.h>
51 #include <gdk/gdkkeysyms.h>
54 #include "alertpanel.h"
55 #include "gtkaspell.h"
56 #include "gtk/gtkutils.h"
57 #include "gtk/combobox.h"
59 #define ASPELL_FASTMODE 1
60 #define ASPELL_NORMALMODE 2
61 #define ASPELL_BADSPELLERMODE 3
63 /* size of the text buffer used in various word-processing routines. */
66 /* number of suggestions to display on each menu. */
70 SET_GTKASPELL_NAME = 0,
71 SET_GTKASPELL_FULLNAME = 1,
75 typedef struct _GtkAspellCheckers {
77 GSList *dictionary_list;
81 /******************************************************************************/
83 static GtkAspellCheckers *gtkaspellcheckers;
85 /* Error message storage */
86 static void gtkaspell_checkers_error_message (gchar *message);
89 static void entry_insert_cb (GtkTextBuffer *textbuf,
93 GtkAspell *gtkaspell);
94 static void entry_delete_cb (GtkTextBuffer *textbuf,
95 GtkTextIter *startiter,
97 GtkAspell *gtkaspell);
98 /*static gint button_press_intercept_cb (GtkTextView *gtktext,
100 GtkAspell *gtkaspell);
102 static void button_press_intercept_cb(GtkTextView *gtktext,
103 GtkMenu *menu, GtkAspell *gtkaspell);
105 /* Checker creation */
106 static GtkAspeller* gtkaspeller_new (Dictionary *dict);
107 static GtkAspeller* gtkaspeller_real_new (Dictionary *dict);
108 static GtkAspeller* gtkaspeller_delete (GtkAspeller *gtkaspeller);
109 static GtkAspeller* gtkaspeller_real_delete (GtkAspeller *gtkaspeller);
111 /* Checker configuration */
112 static EnchantDict *set_dictionary (EnchantBroker *broker,
114 static void set_use_both_cb (GtkMenuItem *w,
115 GtkAspell *gtkaspell);
117 /* Checker actions */
118 static gboolean check_at (GtkAspell *gtkaspell,
120 static gboolean check_at_cb (gpointer data);
121 static GList* misspelled_suggest (GtkAspell *gtkaspell,
123 static gboolean find_misspelled_cb (gpointer data,
125 static void add_word_to_session_cb (GtkWidget *w,
127 static void add_word_to_personal_cb (GtkWidget *w,
129 static void replace_with_create_dialog_cb (GtkWidget *w,
131 static void replace_with_supplied_word_cb (GtkWidget *w,
132 GtkAspell *gtkaspell);
133 static void replace_word_cb (GtkWidget *w,
135 static void replace_real_word (GtkAspell *gtkaspell,
136 const gchar *newword);
137 static void replace_real_word_cb (gpointer data,
138 const gchar *newword);
139 static void check_with_alternate_cb (GtkWidget *w,
141 static void toggle_check_while_typing_cb (GtkWidget *w,
145 static GSList* make_sug_menu (GtkAspell *gtkaspell);
146 static GSList * populate_submenu (GtkAspell *gtkaspell);
147 GSList* gtkaspell_make_config_menu (GtkAspell *gtkaspell);
148 static void set_menu_pos (GtkMenu *menu,
153 /* Other menu callbacks */
154 static gboolean aspell_key_pressed (GtkWidget *widget,
156 GtkAspell *gtkaspell);
157 static void change_dict_cb (GtkWidget *w,
158 GtkAspell *gtkaspell);
159 static void switch_to_alternate_cb (GtkWidget *w,
162 /* Misc. helper functions */
163 static void set_point_continue (GtkAspell *gtkaspell);
164 static void continue_check (gpointer *gtkaspell);
165 static gboolean iswordsep (gunichar c);
166 static gunichar get_text_index_whar (GtkAspell *gtkaspell,
168 static gboolean get_word_from_pos (GtkAspell *gtkaspell,
174 static void allocate_color (GtkAspell *gtkaspell,
176 static void change_color (GtkAspell *gtkaspell,
181 static gint compare_dict (Dictionary *a,
183 static void dictionary_delete (Dictionary *dict);
184 static Dictionary * dictionary_dup (const Dictionary *dict);
185 static void reset_theword_data (GtkAspell *gtkaspell);
186 static void free_checkers (gpointer elt,
188 static gint find_gtkaspeller (gconstpointer aa,
191 static void destroy_menu(GtkWidget *widget, gpointer user_data);
193 /******************************************************************************/
194 static gint get_textview_buffer_charcount(GtkTextView *view);
196 static void gtkaspell_free_dictionary_list (GSList *list);
197 static GSList* gtkaspell_get_dictionary_list (gint refresh);
199 static void gtkaspell_uncheck_all (GtkAspell *gtkaspell);
201 static gint get_textview_buffer_charcount(GtkTextView *view)
203 GtkTextBuffer *buffer;
205 cm_return_val_if_fail(view, 0);
207 buffer = gtk_text_view_get_buffer(view);
208 cm_return_val_if_fail(buffer, 0);
210 return gtk_text_buffer_get_char_count(buffer);
212 static gint get_textview_buffer_offset(GtkTextView *view)
214 GtkTextBuffer * buffer;
218 cm_return_val_if_fail(view, 0);
220 buffer = gtk_text_view_get_buffer(view);
221 cm_return_val_if_fail(buffer, 0);
223 mark = gtk_text_buffer_get_insert(buffer);
224 cm_return_val_if_fail(mark, 0);
226 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
228 return gtk_text_iter_get_offset(&iter);
230 static void set_textview_buffer_offset(GtkTextView *view, gint offset)
232 GtkTextBuffer *buffer;
235 cm_return_if_fail(view);
237 buffer = gtk_text_view_get_buffer(view);
238 cm_return_if_fail(buffer);
240 gtk_text_buffer_get_iter_at_offset(buffer, &iter, offset);
241 gtk_text_buffer_place_cursor(buffer, &iter);
243 /******************************************************************************/
245 void gtkaspell_checkers_init(void)
247 gtkaspellcheckers = g_new(GtkAspellCheckers, 1);
248 gtkaspellcheckers->checkers = NULL;
249 gtkaspellcheckers->dictionary_list = NULL;
250 gtkaspellcheckers->error_message = NULL;
253 void gtkaspell_checkers_quit(void)
258 if (gtkaspellcheckers == NULL)
261 if ((checkers = gtkaspellcheckers->checkers)) {
262 debug_print("Aspell: number of running checkers to delete %d\n",
263 g_slist_length(checkers));
265 g_slist_foreach(checkers, free_checkers, NULL);
266 g_slist_free(checkers);
267 gtkaspellcheckers->checkers = NULL;
270 if ((dict_list = gtkaspellcheckers->dictionary_list)) {
271 debug_print("Aspell: number of dictionaries to delete %d\n",
272 g_slist_length(dict_list));
274 gtkaspell_free_dictionary_list(dict_list);
275 gtkaspellcheckers->dictionary_list = NULL;
278 g_free(gtkaspellcheckers->error_message);
279 gtkaspellcheckers->error_message = NULL;
283 static void gtkaspell_checkers_error_message (gchar *message)
286 if (gtkaspellcheckers->error_message) {
287 tmp = g_strdup_printf("%s\n%s",
288 gtkaspellcheckers->error_message,
291 g_free(gtkaspellcheckers->error_message);
292 gtkaspellcheckers->error_message = tmp;
294 gtkaspellcheckers->error_message = message;
299 const char *gtkaspell_checkers_strerror(void)
301 cm_return_val_if_fail(gtkaspellcheckers, "");
302 return gtkaspellcheckers->error_message;
305 void gtkaspell_checkers_reset_error(void)
307 cm_return_if_fail(gtkaspellcheckers);
309 g_free(gtkaspellcheckers->error_message);
311 gtkaspellcheckers->error_message = NULL;
314 GtkAspell *gtkaspell_new(const gchar *dictionary,
315 const gchar *alt_dictionary,
316 const gchar *encoding, /* unused */
317 gint misspelled_color,
318 gboolean check_while_typing,
319 gboolean recheck_when_changing_dict,
320 gboolean use_alternate,
321 gboolean use_both_dicts,
322 GtkTextView *gtktext,
323 GtkWindow *parent_win,
324 void (dict_changed_cb)(void *data),
325 void (*spell_menu_cb)(void *data),
329 GtkAspell *gtkaspell;
330 GtkAspeller *gtkaspeller;
331 GtkTextBuffer *buffer;
333 cm_return_val_if_fail(gtktext, NULL);
334 if (!dictionary || !*dictionary) {
335 gtkaspell_checkers_error_message(
336 g_strdup(_("No dictionary selected.")));
340 buffer = gtk_text_view_get_buffer(gtktext);
342 dict = g_new0(Dictionary, 1);
343 if (strrchr(dictionary, '/')) {
344 dict->fullname = g_strdup(strrchr(dictionary, '/')+1);
345 dict->dictname = g_strdup(strrchr(dictionary, '/')+1);
347 dict->fullname = g_strdup(dictionary);
348 dict->dictname = g_strdup(dictionary);
351 if (strchr(dict->fullname, '-')) {
352 *(strchr(dict->fullname, '-')) = '\0';
353 *(strchr(dict->dictname, '-')) = '\0';
355 gtkaspeller = gtkaspeller_new(dict);
356 dictionary_delete(dict);
359 gtkaspell_checkers_error_message(
360 g_strdup_printf(_("Couldn't initialize %s speller."), dictionary));
364 gtkaspell = g_new0(GtkAspell, 1);
366 gtkaspell->gtkaspeller = gtkaspeller;
368 if (use_alternate && alt_dictionary && *alt_dictionary) {
369 Dictionary *alt_dict;
370 GtkAspeller *alt_gtkaspeller;
372 alt_dict = g_new0(Dictionary, 1);
373 if (strrchr(alt_dictionary, '/')) {
374 alt_dict->fullname = g_strdup(strrchr(alt_dictionary, '/')+1);
375 alt_dict->dictname = g_strdup(strrchr(alt_dictionary, '/')+1);
377 alt_dict->fullname = g_strdup(alt_dictionary);
378 alt_dict->dictname = g_strdup(alt_dictionary);
380 if (strchr(alt_dict->fullname, '-')) {
381 *(strchr(alt_dict->fullname, '-')) = '\0';
382 *(strchr(alt_dict->dictname, '-')) = '\0';
385 alt_gtkaspeller = gtkaspeller_new(alt_dict);
386 dictionary_delete(alt_dict);
388 if (!alt_gtkaspeller) {
389 gtkaspell_checkers_error_message(
390 g_strdup_printf(_("Couldn't initialize %s speller."), dictionary));
394 gtkaspell->alternate_speller = alt_gtkaspeller;
396 gtkaspell->alternate_speller = NULL;
399 gtkaspell->theword[0] = 0x00;
400 gtkaspell->start_pos = 0;
401 gtkaspell->end_pos = 0;
402 gtkaspell->orig_pos = -1;
403 gtkaspell->end_check_pos = -1;
404 gtkaspell->misspelled = -1;
405 gtkaspell->check_while_typing = check_while_typing;
406 gtkaspell->recheck_when_changing_dict = recheck_when_changing_dict;
407 gtkaspell->continue_check = NULL;
408 gtkaspell->replace_entry = NULL;
409 gtkaspell->gtktext = gtktext;
410 gtkaspell->max_sug = -1;
411 gtkaspell->suggestions_list = NULL;
412 gtkaspell->use_alternate = use_alternate;
413 gtkaspell->use_both_dicts = use_both_dicts;
414 gtkaspell->parent_window = GTK_WIDGET(parent_win);
415 gtkaspell->dict_changed_cb = dict_changed_cb;
416 gtkaspell->menu_changed_cb = spell_menu_cb;
417 gtkaspell->menu_changed_data = data;
419 allocate_color(gtkaspell, misspelled_color);
421 g_signal_connect_after(G_OBJECT(buffer), "insert-text",
422 G_CALLBACK(entry_insert_cb), gtkaspell);
423 g_signal_connect_after(G_OBJECT(buffer), "delete-range",
424 G_CALLBACK(entry_delete_cb), gtkaspell);
425 /*g_signal_connect(G_OBJECT(gtktext), "button-press-event",
426 G_CALLBACK(button_press_intercept_cb),
428 g_signal_connect(G_OBJECT(gtktext), "populate-popup",
429 G_CALLBACK(button_press_intercept_cb), gtkaspell);
431 debug_print("Aspell: created gtkaspell %p\n", gtkaspell);
436 void gtkaspell_delete(GtkAspell *gtkaspell)
438 GtkTextView *gtktext = gtkaspell->gtktext;
440 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
441 G_CALLBACK(entry_insert_cb),
443 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
444 G_CALLBACK(entry_delete_cb),
446 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
447 G_CALLBACK(button_press_intercept_cb),
450 gtkaspell_uncheck_all(gtkaspell);
452 gtkaspeller_delete(gtkaspell->gtkaspeller);
454 if (gtkaspell->alternate_speller)
455 gtkaspeller_delete(gtkaspell->alternate_speller);
457 if (gtkaspell->suggestions_list)
458 gtkaspell_free_suggestions_list(gtkaspell);
460 debug_print("Aspell: deleting gtkaspell %p\n", gtkaspell);
467 void gtkaspell_dict_changed(GtkAspell *gtkaspell)
469 if(!gtkaspell || !gtkaspell->dict_changed_cb ||
470 !gtkaspell->menu_changed_data)
473 gtkaspell->dict_changed_cb(gtkaspell->menu_changed_data);
476 static void entry_insert_cb(GtkTextBuffer *textbuf,
480 GtkAspell *gtkaspell)
484 cm_return_if_fail(gtkaspell->gtkaspeller->speller);
486 if (!gtkaspell->check_while_typing)
489 pos = gtk_text_iter_get_offset(iter);
491 if (iswordsep(g_utf8_get_char(newtext))) {
492 /* did we just end a word? */
494 check_at(gtkaspell, pos - 2);
496 /* did we just split a word? */
497 if (pos < gtk_text_buffer_get_char_count(textbuf))
498 check_at(gtkaspell, pos + 1);
500 /* check as they type, *except* if they're typing at the end (the most
503 if (pos < gtk_text_buffer_get_char_count(textbuf) &&
504 !iswordsep(get_text_index_whar(gtkaspell, pos))) {
505 check_at(gtkaspell, pos - 1);
510 static void entry_delete_cb(GtkTextBuffer *textbuf,
511 GtkTextIter *startiter,
512 GtkTextIter *enditer,
513 GtkAspell *gtkaspell)
518 cm_return_if_fail(gtkaspell->gtkaspeller->speller);
520 if (!gtkaspell->check_while_typing)
523 start = gtk_text_iter_get_offset(startiter);
524 end = gtk_text_iter_get_offset(enditer);
525 origpos = get_textview_buffer_offset(gtkaspell->gtktext);
527 check_at(gtkaspell, start - 1);
528 check_at(gtkaspell, start);
531 set_textview_buffer_offset(gtkaspell->gtktext, origpos);
532 /* this is to *UNDO* the selection, in case they were holding shift
533 * while hitting backspace. */
534 /* needed with textview ??? */
535 /* gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos); */
538 void gtkaspell_make_context_menu(GtkMenu *menu, GtkAspell *gtkaspell)
540 GtkMenuItem *menuitem;
541 GSList *spell_menu = NULL;
543 gboolean suggest = FALSE;
545 if (gtkaspell->misspelled &&
546 misspelled_suggest(gtkaspell, gtkaspell->theword)) {
547 spell_menu = make_sug_menu(gtkaspell);
551 spell_menu = gtkaspell_make_config_menu(gtkaspell);
553 menuitem = GTK_MENU_ITEM(gtk_separator_menu_item_new());
554 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
555 gtk_widget_show(GTK_WIDGET(menuitem));
557 spell_menu = g_slist_reverse(spell_menu);
558 for (items = spell_menu;
559 items; items = items->next) {
560 menuitem = GTK_MENU_ITEM(items->data);
561 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
562 gtk_widget_show(GTK_WIDGET(menuitem));
564 g_slist_free(spell_menu);
566 g_signal_connect(G_OBJECT(menu), "deactivate",
567 G_CALLBACK(destroy_menu),
570 g_signal_connect(G_OBJECT(menu),
572 G_CALLBACK(aspell_key_pressed),
576 static void set_position_cb(gpointer data, gint pos)
578 GtkAspell *gtkaspell = (GtkAspell *) data;
579 set_textview_buffer_offset(gtkaspell->gtktext, pos);
582 void gtkaspell_context_set(GtkAspell *gtkaspell)
584 gtkaspell->ctx.set_position = set_position_cb;
585 gtkaspell->ctx.set_menu_pos = set_menu_pos;
586 gtkaspell->ctx.find_misspelled = find_misspelled_cb;
587 gtkaspell->ctx.check_word = check_at_cb;
588 gtkaspell->ctx.replace_word = replace_real_word_cb;
589 gtkaspell->ctx.data = (gpointer) gtkaspell;
592 static void button_press_intercept_cb(GtkTextView *gtktext,
593 GtkMenu *menu, GtkAspell *gtkaspell)
595 gtktext = gtkaspell->gtktext;
596 gtkaspell->orig_pos = get_textview_buffer_offset(gtktext);
597 gtkaspell->misspelled = check_at(gtkaspell, gtkaspell->orig_pos);
599 gtkaspell_context_set(gtkaspell);
600 gtkaspell_make_context_menu(menu, gtkaspell);
602 /* Checker creation */
603 static GtkAspeller *gtkaspeller_new(Dictionary *dictionary)
606 GtkAspeller *gtkaspeller = NULL;
610 cm_return_val_if_fail(gtkaspellcheckers, NULL);
612 cm_return_val_if_fail(dictionary, NULL);
614 if (dictionary->dictname == NULL)
615 gtkaspell_checkers_error_message(
616 g_strdup(_("No dictionary selected.")));
618 cm_return_val_if_fail(dictionary->fullname, NULL);
620 dict = dictionary_dup(dictionary);
622 tmp = g_new0(GtkAspeller, 1);
623 tmp->dictionary = dict;
625 exist = g_slist_find_custom(gtkaspellcheckers->checkers, tmp,
630 if ((gtkaspeller = gtkaspeller_real_new(dict)) != NULL) {
631 gtkaspellcheckers->checkers = g_slist_append(
632 gtkaspellcheckers->checkers,
635 debug_print("Aspell: Created a new gtkaspeller %p\n",
638 dictionary_delete(dict);
640 debug_print("Aspell: Could not create spell checker.\n");
643 debug_print("Aspell: number of existing checkers %d\n",
644 g_slist_length(gtkaspellcheckers->checkers));
649 static GtkAspeller *gtkaspeller_real_new(Dictionary *dict)
651 GtkAspeller *gtkaspeller;
652 EnchantBroker *broker;
653 EnchantDict *speller;
655 cm_return_val_if_fail(gtkaspellcheckers, NULL);
656 cm_return_val_if_fail(dict, NULL);
658 gtkaspeller = g_new(GtkAspeller, 1);
660 gtkaspeller->dictionary = dict;
662 broker = enchant_broker_init();
665 gtkaspell_checkers_error_message(
666 g_strdup(_("Couldn't initialize Enchant broker.")));
669 if ((speller = set_dictionary(broker, dict)) == NULL) {
670 gtkaspell_checkers_error_message(
671 g_strdup_printf(_("Couldn't initialize %s dictionary:"), dict->fullname));
672 gtkaspell_checkers_error_message(
673 g_strdup(enchant_broker_get_error(broker)));
676 gtkaspeller->speller = speller;
677 gtkaspeller->broker = broker;
682 static GtkAspeller *gtkaspeller_delete(GtkAspeller *gtkaspeller)
684 cm_return_val_if_fail(gtkaspellcheckers, NULL);
686 gtkaspellcheckers->checkers =
687 g_slist_remove(gtkaspellcheckers->checkers,
690 debug_print("Aspell: Deleting gtkaspeller %p.\n",
693 gtkaspeller_real_delete(gtkaspeller);
695 debug_print("Aspell: number of existing checkers %d\n",
696 g_slist_length(gtkaspellcheckers->checkers));
701 static GtkAspeller *gtkaspeller_real_delete(GtkAspeller *gtkaspeller)
703 cm_return_val_if_fail(gtkaspeller, NULL);
704 cm_return_val_if_fail(gtkaspeller->speller, NULL);
706 enchant_broker_free_dict(gtkaspeller->broker, gtkaspeller->speller);
707 enchant_broker_free(gtkaspeller->broker);
709 dictionary_delete(gtkaspeller->dictionary);
711 debug_print("Aspell: gtkaspeller %p deleted.\n",
719 /*****************************************************************************/
720 /* Checker configuration */
722 static EnchantDict *set_dictionary(EnchantBroker *broker, Dictionary *dict)
724 cm_return_val_if_fail(broker, FALSE);
725 cm_return_val_if_fail(dict, FALSE);
727 return enchant_broker_request_dict(broker, dict->dictname );
730 static void set_use_both_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
732 gtkaspell->use_both_dicts = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
733 gtkaspell_dict_changed(gtkaspell);
735 if (gtkaspell->menu_changed_cb)
736 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
739 /* misspelled_suggest() - Create a suggestion list for word */
740 static GList *misspelled_suggest(GtkAspell *gtkaspell, gchar *word)
745 cm_return_val_if_fail(word, NULL);
750 gtkaspell_free_suggestions_list(gtkaspell);
752 suggestions = enchant_dict_suggest(gtkaspell->gtkaspeller->speller, word, strlen(word), &num_sug);
753 list = g_list_append(list, g_strdup(word));
754 if (suggestions == NULL || num_sug == 0) {
755 gtkaspell->max_sug = -1;
756 gtkaspell->suggestions_list = list;
759 for (i = 0; i < num_sug; i++)
760 list = g_list_append(list, g_strdup((gchar *)suggestions[i]));
762 gtkaspell->max_sug = num_sug - 1;
763 gtkaspell->suggestions_list = list;
764 enchant_dict_free_string_list(gtkaspell->gtkaspeller->speller, suggestions);
768 /* misspelled_test() - Just test if word is correctly spelled */
769 int gtkaspell_misspelled_test(GtkAspell *gtkaspell, char *word)
772 cm_return_val_if_fail(word, 0);
777 result = enchant_dict_check(gtkaspell->gtkaspeller->speller, word, strlen(word));
779 if (result && gtkaspell->use_both_dicts && gtkaspell->alternate_speller) {
780 gtkaspell_use_alternate_dict(gtkaspell);
781 result = enchant_dict_check(gtkaspell->gtkaspeller->speller, word, strlen(word));
782 gtkaspell_use_alternate_dict(gtkaspell);
784 return (result && strcasecmp(word, "sylpheed") &&
785 strcasecmp(word, "claws-mail"));
789 static gboolean iswordsep(gunichar c)
791 return (g_unichar_isspace(c) || g_unichar_ispunct(c)) && c != (gunichar)'\'';
794 static gunichar get_text_index_whar(GtkAspell *gtkaspell, int pos)
796 GtkTextView *view = gtkaspell->gtktext;
797 GtkTextBuffer *buffer = gtk_text_view_get_buffer(view);
798 GtkTextIter start, end;
802 gtk_text_buffer_get_iter_at_offset(buffer, &start, pos);
803 gtk_text_buffer_get_iter_at_offset(buffer, &end, pos+1);
805 utf8chars = gtk_text_iter_get_text(&start, &end);
806 a = g_utf8_get_char(utf8chars);
812 /* get_word_from_pos () - return the word pointed to. */
813 /* Handles correctly the quotes. */
814 static gboolean get_word_from_pos(GtkAspell *gtkaspell, gint pos,
815 char* buf, gint buflen,
816 gint *pstart, gint *pend)
819 /* TODO : when correcting a word into quotes, change the color of */
820 /* the quotes too, as may be they were highlighted before. To do */
821 /* this, we can use two others pointers that points to the whole */
822 /* word including quotes. */
828 GtkTextView *gtktext;
830 gtktext = gtkaspell->gtktext;
831 if (iswordsep(get_text_index_whar(gtkaspell, pos)))
834 /* The apostrophe character is somtimes used for quotes
835 * So include it in the word only if it is not surrounded
836 * by other characters.
839 for (start = pos; start >= 0; --start) {
840 c = get_text_index_whar(gtkaspell, start);
841 if (c == (gunichar)'\'') {
843 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
845 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
847 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
849 /* start_quote = TRUE; */
854 /* start_quote = TRUE; */
858 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
864 for (end = pos; end < get_textview_buffer_charcount(gtktext); end++) {
865 c = get_text_index_whar(gtkaspell, end);
866 if (c == (gunichar)'\'') {
867 if (end < get_textview_buffer_charcount(gtktext)) {
868 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
870 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
872 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
874 /* end_quote = TRUE; */
879 /* end_quote = TRUE; */
883 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
893 if (end - start < buflen) {
894 GtkTextIter iterstart, iterend;
896 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtktext);
897 gtk_text_buffer_get_iter_at_offset(buffer, &iterstart, start);
898 gtk_text_buffer_get_iter_at_offset(buffer, &iterend, end);
899 tmp = gtk_text_buffer_get_text(buffer, &iterstart, &iterend, FALSE);
900 strncpy(buf, tmp, buflen-1);
910 static gboolean check_at(GtkAspell *gtkaspell, gint from_pos)
913 char buf[GTKASPELLWORDSIZE];
914 GtkTextView *gtktext;
916 cm_return_val_if_fail(from_pos >= 0, FALSE);
918 gtktext = gtkaspell->gtktext;
920 if (!get_word_from_pos(gtkaspell, from_pos, buf, sizeof(buf),
924 if (gtkaspell_misspelled_test(gtkaspell, buf)) {
925 strncpy(gtkaspell->theword, (gchar *)buf, GTKASPELLWORDSIZE - 1);
926 gtkaspell->theword[GTKASPELLWORDSIZE - 1] = 0;
927 gtkaspell->start_pos = start;
928 gtkaspell->end_pos = end;
929 gtkaspell_free_suggestions_list(gtkaspell);
931 change_color(gtkaspell, start, end, (gchar *)buf, &(gtkaspell->highlight));
934 change_color(gtkaspell, start, end, (gchar *)buf, NULL);
939 static gboolean check_at_cb(gpointer data)
941 GtkAspell *gtkaspell = (GtkAspell *)data;
942 return check_at(gtkaspell, gtkaspell->start_pos);
945 static gboolean find_misspelled_cb(gpointer data, gboolean forward)
947 GtkAspell *gtkaspell = (GtkAspell *)data;
955 maxpos = gtkaspell->end_check_pos;
963 pos = get_textview_buffer_offset(gtkaspell->gtktext);
964 gtkaspell->orig_pos = pos;
965 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
966 pos > minpos && pos <= maxpos)
968 while (!(misspelled = check_at(gtkaspell, pos)) &&
969 pos > minpos && pos <= maxpos) {
971 while (!iswordsep(get_text_index_whar(gtkaspell, pos)) &&
972 pos > minpos && pos <= maxpos)
975 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
976 pos > minpos && pos <= maxpos)
983 gboolean gtkaspell_check_next_prev(GtkAspell *gtkaspell, gboolean forward)
985 gboolean misspelled = gtkaspell->ctx.find_misspelled(gtkaspell->ctx.data,
990 misspelled_suggest(gtkaspell, gtkaspell->theword);
993 gtkaspell->orig_pos = gtkaspell->end_pos;
995 gtkaspell->ctx.set_position(gtkaspell->ctx.data, gtkaspell->end_pos);
997 /* only execute when in textview context */
998 if (gtkaspell == (GtkAspell *)gtkaspell->ctx.data) {
999 /* scroll line to window center */
1000 gtk_text_view_scroll_to_mark(gtkaspell->gtktext,
1001 gtk_text_buffer_get_insert(
1002 gtk_text_view_get_buffer(gtkaspell->gtktext)),
1003 0.0, TRUE, 0.0, 0.5);
1004 /* let textview recalculate coordinates (set_menu_pos) */
1005 while (gtk_events_pending ())
1006 gtk_main_iteration ();
1009 list = make_sug_menu(gtkaspell);
1010 menu = gtk_menu_new();
1011 for (cur = list; cur; cur = cur->next)
1012 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(cur->data));
1014 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1015 gtkaspell->ctx.set_menu_pos,
1016 gtkaspell->ctx.data,
1017 0, GDK_CURRENT_TIME);
1018 g_signal_connect(G_OBJECT(menu), "deactivate",
1019 G_CALLBACK(destroy_menu),
1021 g_signal_connect(G_OBJECT(menu),
1023 G_CALLBACK(aspell_key_pressed),
1028 reset_theword_data(gtkaspell);
1030 alertpanel_notice(_("No misspelled word found."));
1031 gtkaspell->ctx.set_position(gtkaspell->ctx.data,
1032 gtkaspell->orig_pos);
1038 void gtkaspell_check_backwards(GtkAspell *gtkaspell)
1040 gtkaspell->continue_check = NULL;
1041 gtkaspell->end_check_pos =
1042 get_textview_buffer_charcount(gtkaspell->gtktext);
1043 gtkaspell_context_set(gtkaspell);
1044 gtkaspell_check_next_prev(gtkaspell, FALSE);
1047 void gtkaspell_check_forwards_go(GtkAspell *gtkaspell)
1050 gtkaspell->continue_check = NULL;
1051 gtkaspell->end_check_pos =
1052 get_textview_buffer_charcount(gtkaspell->gtktext);
1053 gtkaspell_context_set(gtkaspell);
1054 gtkaspell_check_next_prev(gtkaspell, TRUE);
1057 void gtkaspell_check_all(GtkAspell *gtkaspell)
1059 GtkTextView *gtktext;
1061 GtkTextBuffer *buffer;
1062 GtkTextIter startiter, enditer;
1064 cm_return_if_fail(gtkaspell);
1065 cm_return_if_fail(gtkaspell->gtktext);
1067 gtktext = gtkaspell->gtktext;
1068 buffer = gtk_text_view_get_buffer(gtktext);
1069 gtk_text_buffer_get_selection_bounds(buffer, &startiter, &enditer);
1070 start = gtk_text_iter_get_offset(&startiter);
1071 end = gtk_text_iter_get_offset(&enditer);
1075 end = gtk_text_buffer_get_char_count(buffer);
1076 } else if (start > end) {
1084 set_textview_buffer_offset(gtktext, start);
1086 gtkaspell->continue_check = continue_check;
1087 gtkaspell->end_check_pos = end;
1089 gtkaspell_context_set(gtkaspell);
1090 gtkaspell->misspelled = gtkaspell_check_next_prev(gtkaspell, TRUE);
1093 static void continue_check(gpointer *data)
1095 GtkAspell *gtkaspell = (GtkAspell *) data;
1096 gint pos = get_textview_buffer_offset(gtkaspell->gtktext);
1097 if (pos < gtkaspell->end_check_pos && gtkaspell->misspelled)
1098 gtkaspell->misspelled = gtkaspell_check_next_prev(gtkaspell, TRUE);
1100 gtkaspell->continue_check = NULL;
1103 void gtkaspell_highlight_all(GtkAspell *gtkaspell)
1108 GtkTextView *gtktext;
1110 cm_return_if_fail(gtkaspell->gtkaspeller->speller);
1112 gtktext = gtkaspell->gtktext;
1114 len = get_textview_buffer_charcount(gtktext);
1116 origpos = get_textview_buffer_offset(gtktext);
1120 iswordsep(get_text_index_whar(gtkaspell, pos)))
1123 !iswordsep(get_text_index_whar(gtkaspell, pos)))
1126 check_at(gtkaspell, pos - 1);
1128 set_textview_buffer_offset(gtktext, origpos);
1131 static void replace_with_supplied_word_cb(GtkWidget *w, GtkAspell *gtkaspell)
1134 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1136 newword = gtk_editable_get_chars(GTK_EDITABLE(gtkaspell->replace_entry),
1139 if (strcmp(newword, gtkaspell->theword)) {
1140 gtkaspell->ctx.replace_word(gtkaspell->ctx.data, newword);
1142 if ((e->type == GDK_KEY_PRESS &&
1143 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK)) {
1144 enchant_dict_store_replacement(gtkaspell->gtkaspeller->speller,
1145 gtkaspell->theword, strlen(gtkaspell->theword),
1146 newword, strlen(newword));
1148 gtkaspell->replace_entry = NULL;
1153 if (w && GTK_IS_DIALOG(w)) {
1154 gtk_widget_destroy(w);
1157 set_point_continue(gtkaspell);
1161 static void replace_word_cb(GtkWidget *w, gpointer data)
1163 const char *newword;
1164 GtkAspell *gtkaspell = (GtkAspell *) data;
1165 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1167 newword = gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN((w)))));
1169 gtkaspell->ctx.replace_word(gtkaspell->ctx.data, newword);
1171 if ((e->type == GDK_KEY_PRESS &&
1172 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK) ||
1173 (e->type == GDK_BUTTON_RELEASE &&
1174 ((GdkEventButton *) e)->state & GDK_CONTROL_MASK)) {
1175 enchant_dict_store_replacement(gtkaspell->gtkaspeller->speller,
1176 gtkaspell->theword, strlen(gtkaspell->theword),
1177 newword, strlen(newword));
1180 gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1182 set_point_continue(gtkaspell);
1185 static void replace_real_word(GtkAspell *gtkaspell, const gchar *newword)
1187 int oldlen, newlen, wordlen;
1190 GtkTextView *gtktext;
1191 GtkTextBuffer *textbuf;
1192 GtkTextIter startiter, enditer;
1194 if (!newword) return;
1196 gtktext = gtkaspell->gtktext;
1197 textbuf = gtk_text_view_get_buffer(gtktext);
1199 origpos = gtkaspell->orig_pos;
1201 oldlen = gtkaspell->end_pos - gtkaspell->start_pos;
1202 wordlen = strlen(gtkaspell->theword);
1204 newlen = strlen(newword); /* FIXME: multybyte characters? */
1206 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1207 G_CALLBACK(entry_insert_cb),
1209 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1210 G_CALLBACK(entry_delete_cb),
1213 gtk_text_buffer_get_iter_at_offset(textbuf, &startiter,
1214 gtkaspell->start_pos);
1215 gtk_text_buffer_get_iter_at_offset(textbuf, &enditer,
1216 gtkaspell->end_pos);
1217 g_signal_emit_by_name(G_OBJECT(textbuf), "delete-range",
1218 &startiter, &enditer, gtkaspell);
1219 g_signal_emit_by_name(G_OBJECT(textbuf), "insert-text",
1220 &startiter, newword, newlen, gtkaspell);
1222 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1223 G_CALLBACK(entry_insert_cb),
1225 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1226 G_CALLBACK(entry_delete_cb),
1229 /* Put the point and the position where we clicked with the mouse
1230 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1231 * to let it update correctly the word insertion and then the
1232 * point & position position. If not, SEGV after the first replacement
1233 * If the new word ends before point, put the point at its end.
1236 if (origpos - gtkaspell->start_pos < oldlen &&
1237 origpos - gtkaspell->start_pos >= 0) {
1238 /* Original point was in the word.
1239 * Let it there unless point is going to be outside of the word
1241 if (origpos - gtkaspell->start_pos >= newlen) {
1242 pos = gtkaspell->start_pos + newlen;
1245 else if (origpos >= gtkaspell->end_pos) {
1246 /* move the position according to the change of length */
1247 pos = origpos + newlen - oldlen;
1250 gtkaspell->end_pos = gtkaspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1252 if (get_textview_buffer_charcount(gtktext) < pos)
1253 pos = get_textview_buffer_charcount(gtktext);
1254 gtkaspell->orig_pos = pos;
1256 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1259 static void replace_real_word_cb(gpointer data, const gchar *newword)
1261 replace_real_word((GtkAspell *)data, newword);
1264 /* Accept this word for this session */
1265 static void add_word_to_session_cb(GtkWidget *w, gpointer data)
1267 GtkTextView *gtktext;
1268 GtkAspell *gtkaspell = (GtkAspell *) data;
1269 gtktext = gtkaspell->gtktext;
1272 enchant_dict_add_to_session(gtkaspell->gtkaspeller->speller, gtkaspell->theword, strlen(gtkaspell->theword));
1274 gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1275 gtkaspell_dict_changed(gtkaspell);
1277 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1279 set_point_continue(gtkaspell);
1282 /* add_word_to_personal_cb() - add word to personal dict. */
1283 static void add_word_to_personal_cb(GtkWidget *w, gpointer data)
1285 GtkAspell *gtkaspell = (GtkAspell *) data;
1287 enchant_dict_add_to_pwl(gtkaspell->gtkaspeller->speller, gtkaspell->theword, strlen(gtkaspell->theword));
1289 gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1290 gtkaspell_dict_changed(gtkaspell);
1292 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1293 set_point_continue(gtkaspell);
1296 static void check_with_alternate_cb(GtkWidget *w, gpointer data)
1298 GtkAspell *gtkaspell = (GtkAspell *)data;
1301 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1303 gtkaspell_use_alternate_dict(gtkaspell);
1304 misspelled = gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1306 if (!gtkaspell->continue_check) {
1308 gtkaspell->misspelled = misspelled;
1310 if (gtkaspell->misspelled) {
1313 misspelled_suggest(gtkaspell, gtkaspell->theword);
1315 gtkaspell->ctx.set_position(gtkaspell->ctx.data,
1316 gtkaspell->end_pos);
1318 list = make_sug_menu(gtkaspell);
1319 menu = gtk_menu_new();
1320 for (cur = list; cur; cur = cur->next)
1321 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(cur->data));
1323 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1324 gtkaspell->ctx.set_menu_pos,
1325 gtkaspell->ctx.data, 0,
1327 g_signal_connect(G_OBJECT(menu), "deactivate",
1328 G_CALLBACK(destroy_menu),
1330 g_signal_connect(G_OBJECT(menu),
1332 G_CALLBACK(aspell_key_pressed),
1337 gtkaspell->orig_pos = gtkaspell->start_pos;
1339 set_point_continue(gtkaspell);
1342 static gboolean replace_key_pressed(GtkWidget *widget,
1344 GtkAspell *gtkaspell)
1346 if (event && event->keyval == GDK_Escape) {
1347 gtk_widget_destroy(widget);
1349 } else if (event && event->keyval == GDK_Return) {
1350 replace_with_supplied_word_cb(widget, gtkaspell);
1356 static void replace_with_create_dialog_cb(GtkWidget *w, gpointer data)
1358 static PangoFontDescription *font_desc;
1365 GtkWidget *ok_button;
1366 GtkWidget *cancel_button;
1367 GtkWidget *confirm_area;
1369 gchar *utf8buf, *thelabel;
1371 GtkAspell *gtkaspell = (GtkAspell *) data;
1373 gdk_window_get_origin((GTK_WIDGET(w)->parent)->window, &xx, &yy);
1375 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1377 dialog = gtk_dialog_new();
1379 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1380 gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1381 gtk_window_move(GTK_WINDOW(dialog), xx, yy);
1383 g_signal_connect_swapped(G_OBJECT(dialog), "destroy",
1384 G_CALLBACK(gtk_widget_destroy),
1387 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
1388 hbox = gtk_hbox_new (FALSE, 12);
1389 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1390 gtk_widget_show (hbox);
1391 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
1394 utf8buf = g_strdup(gtkaspell->theword);
1396 thelabel = g_strdup_printf(_("<span weight=\"bold\" "
1397 "size=\"larger\">Replace \"%s\" with: </span>"),
1399 /* for title label */
1400 w_hbox = gtk_hbox_new(FALSE, 0);
1402 icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
1403 GTK_ICON_SIZE_DIALOG);
1404 gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
1405 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
1407 vbox = gtk_vbox_new (FALSE, 12);
1408 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1409 gtk_widget_show (vbox);
1411 label = gtk_label_new(thelabel);
1412 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1413 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1414 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1415 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1416 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1420 size = pango_font_description_get_size
1421 (label->style->font_desc);
1422 font_desc = pango_font_description_new();
1423 pango_font_description_set_weight
1424 (font_desc, PANGO_WEIGHT_BOLD);
1425 pango_font_description_set_size
1426 (font_desc, size * PANGO_SCALE_LARGE);
1429 gtk_widget_modify_font(label, font_desc);
1432 entry = gtk_entry_new();
1433 gtkaspell->replace_entry = entry;
1434 gtk_entry_set_text(GTK_ENTRY(entry), utf8buf);
1435 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1436 g_signal_connect(G_OBJECT(dialog),
1438 G_CALLBACK(replace_key_pressed), gtkaspell);
1439 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
1442 label = gtk_label_new(_("Holding down Control key while pressing "
1443 "Enter\nwill learn from mistake.\n"));
1444 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1445 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1446 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1447 gtk_widget_show(label);
1449 hbox = gtk_hbox_new(TRUE, 0);
1451 gtkut_stock_button_set_create(&confirm_area,
1452 &cancel_button, GTK_STOCK_CANCEL,
1453 &ok_button, GTK_STOCK_OK,
1456 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1457 confirm_area, FALSE, FALSE, 0);
1458 gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
1460 g_signal_connect(G_OBJECT(ok_button), "clicked",
1461 G_CALLBACK(replace_with_supplied_word_cb),
1463 g_signal_connect_swapped(G_OBJECT(ok_button), "clicked",
1464 G_CALLBACK(gtk_widget_destroy),
1467 g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked",
1468 G_CALLBACK(gtk_widget_destroy),
1471 gtk_widget_grab_focus(entry);
1473 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1475 gtk_widget_show_all(dialog);
1478 static void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1480 GtkTextView *gtktext;
1481 GtkTextBuffer *buffer;
1482 GtkTextIter startiter, enditer;
1484 gtktext = gtkaspell->gtktext;
1486 buffer = gtk_text_view_get_buffer(gtktext);
1487 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, 0);
1488 gtk_text_buffer_get_iter_at_offset(buffer, &enditer,
1489 get_textview_buffer_charcount(gtktext)-1);
1490 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
1491 &startiter, &enditer);
1494 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1496 GtkAspell *gtkaspell = (GtkAspell *) data;
1498 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1500 if (!gtkaspell->check_while_typing)
1501 gtkaspell_uncheck_all(gtkaspell);
1502 if (gtkaspell->menu_changed_cb)
1503 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
1506 static GSList *create_empty_dictionary_list(void)
1508 GSList *list = NULL;
1511 dict = g_new0(Dictionary, 1);
1512 dict->fullname = g_strdup(_("None"));
1513 dict->dictname = NULL;
1515 return g_slist_append(list, dict);
1518 static void list_dict_cb(const char * const lang_tag,
1519 const char * const provider_name,
1520 const char * const provider_desc,
1521 const char * const provider_file,
1524 GSList **list = (GSList **)data;
1525 Dictionary *dict = g_new0(Dictionary, 1);
1526 dict->fullname = g_strdup(lang_tag);
1527 dict->dictname = g_strdup(lang_tag);
1529 if (g_slist_find_custom(*list, dict,
1530 (GCompareFunc) compare_dict) == NULL) {
1531 debug_print("Aspell: found dictionary %s %s\n", dict->fullname,
1533 *list = g_slist_insert_sorted(*list, dict,
1534 (GCompareFunc) compare_dict);
1536 dictionary_delete(dict);
1540 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1541 static GSList *gtkaspell_get_dictionary_list(gint refresh)
1544 EnchantBroker *broker;
1546 if (!gtkaspellcheckers)
1547 gtkaspell_checkers_init();
1549 if (gtkaspellcheckers->dictionary_list && !refresh)
1550 return gtkaspellcheckers->dictionary_list;
1552 gtkaspell_free_dictionary_list(
1553 gtkaspellcheckers->dictionary_list);
1556 broker = enchant_broker_init();
1558 enchant_broker_list_dicts(broker, list_dict_cb, &list);
1560 enchant_broker_free(broker);
1564 debug_print("Aspell: error when searching for dictionaries: "
1565 "No dictionary found.\n");
1566 list = create_empty_dictionary_list();
1569 gtkaspellcheckers->dictionary_list = list;
1574 static void gtkaspell_free_dictionary_list(GSList *list)
1578 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1580 dict = (Dictionary *) walk->data;
1581 dictionary_delete(dict);
1586 GtkTreeModel *gtkaspell_dictionary_store_new_with_refresh(gboolean refresh)
1588 GSList *dict_list, *tmp;
1589 GtkListStore *store;
1593 dict_list = gtkaspell_get_dictionary_list(refresh);
1594 cm_return_val_if_fail(dict_list, NULL);
1596 store = gtk_list_store_new(SET_GTKASPELL_SIZE,
1601 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1602 dict = (Dictionary *) tmp->data;
1604 gtk_list_store_append(store, &iter);
1605 gtk_list_store_set(store, &iter,
1606 SET_GTKASPELL_NAME, dict->dictname,
1607 SET_GTKASPELL_FULLNAME, dict->fullname,
1611 return GTK_TREE_MODEL(store);
1614 GtkTreeModel *gtkaspell_dictionary_store_new(void)
1616 return gtkaspell_dictionary_store_new_with_refresh
1620 GtkWidget *gtkaspell_dictionary_combo_new(const gboolean refresh)
1623 GtkCellRenderer *renderer;
1625 combo = gtk_combo_box_new_with_model(
1626 gtkaspell_dictionary_store_new_with_refresh(refresh));
1627 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1628 gtk_widget_show(combo);
1630 renderer = gtk_cell_renderer_text_new();
1631 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
1632 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),renderer,
1633 "text", SET_GTKASPELL_NAME, NULL);
1638 gchar *gtkaspell_get_dictionary_menu_active_item(GtkComboBox *combo)
1641 GtkTreeModel *model;
1642 gchar *dict_fullname = NULL;
1644 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo), NULL);
1645 cm_return_val_if_fail(gtk_combo_box_get_active_iter(combo, &iter), NULL);
1647 model = gtk_combo_box_get_model(combo);
1651 gtk_tree_model_get(model, &iter,
1652 SET_GTKASPELL_FULLNAME, &dict_fullname,
1655 return dict_fullname;
1658 gint gtkaspell_set_dictionary_menu_active_item(GtkComboBox *combo,
1659 const gchar *dictionary)
1661 GtkTreeModel *model;
1663 gchar *dict_name = NULL;
1665 cm_return_val_if_fail(combo != NULL, 0);
1666 cm_return_val_if_fail(dictionary != NULL, 0);
1667 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo), 0);
1669 if((model = gtk_combo_box_get_model(combo)) == NULL)
1671 if((gtk_tree_model_get_iter_first(model, &iter)) == FALSE)
1675 gtk_tree_model_get(model, &iter,
1676 SET_GTKASPELL_FULLNAME, &dict_name,
1679 if ((dict_name != NULL) && !strcmp2(dict_name, dictionary)) {
1680 gtk_combo_box_set_active_iter(combo, &iter);
1687 } while ((gtk_tree_model_iter_next(model, &iter)) == TRUE);
1692 void gtkaspell_use_alternate_dict(GtkAspell *gtkaspell)
1696 tmp = gtkaspell->gtkaspeller;
1697 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1698 gtkaspell->alternate_speller = tmp;
1701 static void destroy_menu(GtkWidget *widget,
1702 gpointer user_data) {
1703 GtkAspell *gtkaspell = (GtkAspell *)user_data;
1705 if (gtkaspell->accel_group) {
1706 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1707 gtkaspell->accel_group);
1708 gtkaspell->accel_group = NULL;
1712 static gboolean aspell_key_pressed(GtkWidget *widget,
1714 GtkAspell *gtkaspell)
1716 if (event && (isascii(event->keyval) || event->keyval == GDK_Return)) {
1717 gtk_accel_groups_activate(
1718 G_OBJECT(gtkaspell->parent_window),
1719 event->keyval, event->state);
1720 } else if (event && event->keyval == GDK_Escape) {
1721 destroy_menu(NULL, gtkaspell);
1726 /* make_sug_menu() - Add menus to accept this word for this session
1727 * and to add it to personal dictionary
1729 static GSList *make_sug_menu(GtkAspell *gtkaspell)
1733 GtkTextView *gtktext;
1734 GtkAccelGroup *accel;
1735 GList *l = gtkaspell->suggestions_list;
1737 GSList *list = NULL;
1738 gtktext = gtkaspell->gtktext;
1743 accel = gtk_accel_group_new();
1745 if (gtkaspell->accel_group) {
1746 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1747 gtkaspell->accel_group);
1748 gtkaspell->accel_group = NULL;
1751 utf8buf = g_strdup(l->data);
1752 caption = g_strdup_printf(_("\"%s\" unknown in %s"),
1754 gtkaspell->gtkaspeller->dictionary->dictname);
1755 item = gtk_menu_item_new_with_label(caption);
1757 gtk_widget_show(item);
1758 list = g_slist_append(list, item);
1759 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
1762 item = gtk_menu_item_new();
1763 gtk_widget_show(item);
1764 list = g_slist_append(list, item);
1766 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1767 gtk_widget_show(item);
1768 list = g_slist_append(list, item);
1769 g_signal_connect(G_OBJECT(item), "activate",
1770 G_CALLBACK(add_word_to_session_cb),
1772 gtk_widget_add_accelerator(item, "activate", accel, GDK_space,
1774 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1776 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1777 gtk_widget_show(item);
1778 list = g_slist_append(list, item);
1779 g_signal_connect(G_OBJECT(item), "activate",
1780 G_CALLBACK(add_word_to_personal_cb),
1782 gtk_widget_add_accelerator(item, "activate", accel, GDK_Return,
1784 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1786 item = gtk_menu_item_new_with_label(_("Replace with..."));
1787 gtk_widget_show(item);
1788 list = g_slist_append(list, item);
1789 g_signal_connect(G_OBJECT(item), "activate",
1790 G_CALLBACK(replace_with_create_dialog_cb),
1792 gtk_widget_add_accelerator(item, "activate", accel, GDK_R, 0,
1793 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1794 gtk_widget_add_accelerator(item, "activate", accel, GDK_R,
1798 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1799 caption = g_strdup_printf(_("Check with %s"),
1800 gtkaspell->alternate_speller->dictionary->dictname);
1801 item = gtk_menu_item_new_with_label(caption);
1803 gtk_widget_show(item);
1804 list = g_slist_append(list, item);
1805 g_signal_connect(G_OBJECT(item), "activate",
1806 G_CALLBACK(check_with_alternate_cb),
1808 gtk_widget_add_accelerator(item, "activate", accel, GDK_X, 0,
1809 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1810 gtk_widget_add_accelerator(item, "activate", accel, GDK_X,
1815 item = gtk_menu_item_new();
1816 gtk_widget_show(item);
1817 list = g_slist_append(list, item);
1821 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
1822 gtk_widget_show(item);
1823 list = g_slist_append(list, item);
1825 GtkWidget *curmenu = NULL;
1829 if (count == MENUCOUNT) {
1832 item = gtk_menu_item_new_with_label(_("More..."));
1833 gtk_widget_show(item);
1835 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1837 list = g_slist_append(list, item);
1839 curmenu = gtk_menu_new();
1840 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1844 utf8buf = g_strdup(l->data);
1846 item = gtk_menu_item_new_with_label(utf8buf);
1848 gtk_widget_show(item);
1849 if (curmenu == NULL) {
1850 list = g_slist_append(list, item);
1852 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1854 g_signal_connect(G_OBJECT(item), "activate",
1855 G_CALLBACK(replace_word_cb),
1858 if (curmenu == NULL && count < MENUCOUNT) {
1859 gtk_widget_add_accelerator(item, "activate",
1864 gtk_widget_add_accelerator(item, "activate",
1873 } while ((l = l->next) != NULL);
1876 gtk_window_add_accel_group
1877 (GTK_WINDOW(gtkaspell->parent_window),
1879 gtkaspell->accel_group = accel;
1884 static GSList *populate_submenu(GtkAspell *gtkaspell)
1886 GtkWidget *item, *submenu;
1888 GtkAspeller *gtkaspeller = NULL;
1889 GSList *list = NULL;
1894 gtkaspeller = gtkaspell->gtkaspeller;
1895 dictname = g_strdup_printf(_("Dictionary: %s"),
1896 gtkaspeller->dictionary->dictname);
1897 item = gtk_menu_item_new_with_label(dictname);
1898 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
1900 gtk_widget_show(item);
1901 list = g_slist_append(list, item);
1903 item = gtk_menu_item_new();
1904 gtk_widget_show(item);
1905 list = g_slist_append(list, item);
1907 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1908 dictname = g_strdup_printf(_("Use alternate (%s)"),
1909 gtkaspell->alternate_speller->dictionary->dictname);
1910 item = gtk_menu_item_new_with_label(dictname);
1912 g_signal_connect(G_OBJECT(item), "activate",
1913 G_CALLBACK(switch_to_alternate_cb),
1915 gtk_widget_show(item);
1916 list = g_slist_append(list, item);
1919 item = gtk_check_menu_item_new_with_label(_("Use both dictionaries"));
1920 if (gtkaspell->use_both_dicts) {
1921 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1923 g_signal_connect(G_OBJECT(item), "activate",
1924 G_CALLBACK(set_use_both_cb),
1926 gtk_widget_show(item);
1927 list = g_slist_append(list, item);
1929 item = gtk_menu_item_new();
1930 gtk_widget_show(item);
1931 list = g_slist_append(list, item);
1933 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
1934 if (gtkaspell->check_while_typing)
1935 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1937 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1938 g_signal_connect(G_OBJECT(item), "activate",
1939 G_CALLBACK(toggle_check_while_typing_cb),
1941 gtk_widget_show(item);
1942 list = g_slist_append(list, item);
1944 item = gtk_menu_item_new();
1945 gtk_widget_show(item);
1946 list = g_slist_append(list, item);
1948 submenu = gtk_menu_new();
1949 item = gtk_menu_item_new_with_label(_("Change dictionary"));
1950 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
1951 gtk_widget_show(item);
1952 list = g_slist_append(list, item);
1955 if (gtkaspellcheckers->dictionary_list == NULL)
1956 gtkaspell_get_dictionary_list(FALSE);
1958 GtkWidget * curmenu = submenu;
1962 tmp = gtkaspellcheckers->dictionary_list;
1964 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
1965 tmp = g_slist_next(tmp)) {
1966 if (count == MENUCOUNT) {
1969 newmenu = gtk_menu_new();
1970 item = gtk_menu_item_new_with_label(_("More..."));
1971 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1974 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1975 gtk_widget_show(item);
1979 dict = (Dictionary *) tmp->data;
1980 item = gtk_check_menu_item_new_with_label(dict->dictname);
1981 g_object_set_data(G_OBJECT(item), "dict_name",
1983 if (strcmp2(dict->fullname,
1984 gtkaspell->gtkaspeller->dictionary->fullname))
1985 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1987 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1988 gtk_widget_set_sensitive(GTK_WIDGET(item),
1991 g_signal_connect(G_OBJECT(item), "activate",
1992 G_CALLBACK(change_dict_cb),
1994 gtk_widget_show(item);
1995 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
2003 GSList *gtkaspell_make_config_menu(GtkAspell *gtkaspell)
2005 return populate_submenu(gtkaspell);
2008 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y,
2009 gboolean *push_in, gpointer data)
2011 GtkAspell *gtkaspell = (GtkAspell *) data;
2012 gint xx = 0, yy = 0;
2015 GtkTextView *text = GTK_TEXT_VIEW(gtkaspell->gtktext);
2016 GtkTextBuffer *textbuf;
2021 textbuf = gtk_text_view_get_buffer(gtkaspell->gtktext);
2022 gtk_text_buffer_get_iter_at_mark(textbuf, &iter,
2023 gtk_text_buffer_get_insert(textbuf));
2024 gtk_text_view_get_iter_location(gtkaspell->gtktext, &iter, &rect);
2025 gtk_text_view_buffer_to_window_coords(text, GTK_TEXT_WINDOW_TEXT,
2029 gdk_window_get_origin(GTK_WIDGET(gtkaspell->gtktext)->window, &xx, &yy);
2031 sx = gdk_screen_width();
2032 sy = gdk_screen_height();
2034 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2039 *x = rect.x + xx + 8;
2041 *y = rect.y + rect.height + yy;
2049 /* change the current dictionary of gtkaspell
2050 - if always_set_alt_dict is set, the alternate dict is unconditionally set to the former
2051 current dictionary (common use: from menu callbacks)
2052 - if always_set_alt_dict is NOT set, the alternate dict will be set to the former
2053 current dictionary only if there is no alternate dictionary already set
2054 (this is when we need to set the current dictionary then the alternate one
2055 when creating a compose window, from the account and folder settings)
2057 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary,
2058 gboolean always_set_alt_dict)
2061 GtkAspeller *gtkaspeller;
2063 cm_return_val_if_fail(gtkaspell, FALSE);
2064 cm_return_val_if_fail(dictionary, FALSE);
2066 dict = g_new0(Dictionary, 1);
2068 if (strrchr(dictionary, '/')) {
2069 dict->fullname = g_strdup(strrchr(dictionary, '/')+1);
2070 dict->dictname = g_strdup(strrchr(dictionary, '/')+1);
2072 dict->fullname = g_strdup(dictionary);
2073 dict->dictname = g_strdup(dictionary);
2076 if (dict->fullname && strchr(dict->fullname, '-')) {
2077 *(strchr(dict->fullname, '-')) = '\0';
2078 *(strchr(dict->dictname, '-')) = '\0';
2081 if (!dict->fullname || !(*dict->fullname)) {
2082 dictionary_delete(dict);
2085 gtkaspeller = gtkaspeller_new(dict);
2088 alertpanel_warning(_("The spell checker could not change dictionary.\n%s"),
2089 gtkaspellcheckers->error_message);
2091 if (gtkaspell->use_alternate) {
2092 if (gtkaspell->alternate_speller) {
2093 if (always_set_alt_dict) {
2094 gtkaspeller_delete(gtkaspell->alternate_speller);
2095 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2097 gtkaspeller_delete(gtkaspell->gtkaspeller);
2099 /* should never be reached as the dicts are always set
2100 to a default value */
2101 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2103 gtkaspeller_delete(gtkaspell->gtkaspeller);
2105 gtkaspell->gtkaspeller = gtkaspeller;
2108 dictionary_delete(dict);
2113 /* change the alternate dictionary of gtkaspell (doesn't affect the default dictionary) */
2114 gboolean gtkaspell_change_alt_dict(GtkAspell *gtkaspell, const gchar *alt_dictionary)
2117 GtkAspeller *gtkaspeller;
2119 cm_return_val_if_fail(gtkaspell, FALSE);
2120 cm_return_val_if_fail(alt_dictionary, FALSE);
2122 dict = g_new0(Dictionary, 1);
2123 if (strrchr(alt_dictionary, '/')) {
2124 dict->fullname = g_strdup(strrchr(alt_dictionary, '/')+1);
2125 dict->dictname = g_strdup(strrchr(alt_dictionary, '/')+1);
2127 dict->fullname = g_strdup(alt_dictionary);
2128 dict->dictname = g_strdup(alt_dictionary);
2131 if (dict->fullname && strchr(dict->fullname, '-')) {
2132 *(strchr(dict->fullname, '-')) = '\0';
2133 *(strchr(dict->dictname, '-')) = '\0';
2136 if (!dict->fullname || !(*dict->fullname)) {
2137 dictionary_delete(dict);
2141 gtkaspeller = gtkaspeller_new(dict);
2144 alertpanel_warning(_("The spell checker could not change the alternate dictionary.\n%s"),
2145 gtkaspellcheckers->error_message);
2147 if (gtkaspell->alternate_speller)
2148 gtkaspeller_delete(gtkaspell->alternate_speller);
2149 gtkaspell->alternate_speller = gtkaspeller;
2152 dictionary_delete(dict);
2157 /* Menu call backs */
2159 /* change_dict_cb() - Menu callback : change dict */
2160 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2164 fullname = (gchar *) g_object_get_data(G_OBJECT(w), "dict_name");
2166 if (!strcmp2(fullname, _("None")))
2169 gtkaspell_change_dict(gtkaspell, fullname, TRUE);
2170 gtkaspell_dict_changed(gtkaspell);
2172 if (gtkaspell->menu_changed_cb)
2173 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2176 static void switch_to_alternate_cb(GtkWidget *w,
2179 GtkAspell *gtkaspell = (GtkAspell *) data;
2180 gtkaspell_use_alternate_dict(gtkaspell);
2181 gtkaspell_dict_changed(gtkaspell);
2183 if (gtkaspell->menu_changed_cb)
2184 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2187 /* Misc. helper functions */
2189 static void set_point_continue(GtkAspell *gtkaspell)
2191 GtkTextView *gtktext;
2193 gtktext = gtkaspell->gtktext;
2195 gtkaspell->ctx.set_position(gtkaspell->ctx.data, gtkaspell->orig_pos);
2197 if (gtkaspell->continue_check)
2198 gtkaspell->continue_check((gpointer *) gtkaspell->ctx.data);
2201 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2203 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtkaspell->gtktext);
2204 GdkColor *color = &(gtkaspell->highlight);
2206 /* Shameless copy from Sylpheed's gtkutils.c */
2208 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2210 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2212 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2216 gtk_text_buffer_create_tag(buffer, "misspelled",
2217 "foreground-gdk", color, NULL);
2219 gtk_text_buffer_create_tag(buffer, "misspelled",
2220 "underline", PANGO_UNDERLINE_ERROR, NULL);
2224 static void change_color(GtkAspell * gtkaspell,
2225 gint start, gint end,
2229 GtkTextView *gtktext;
2230 GtkTextBuffer *buffer;
2231 GtkTextIter startiter, enditer;
2236 gtktext = gtkaspell->gtktext;
2238 buffer = gtk_text_view_get_buffer(gtktext);
2239 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, start);
2240 gtk_text_buffer_get_iter_at_offset(buffer, &enditer, end);
2242 gtk_text_buffer_apply_tag_by_name(buffer, "misspelled",
2243 &startiter, &enditer);
2245 gtk_text_iter_forward_char(&enditer);
2246 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
2247 &startiter, &enditer);
2251 /* compare_dict () - compare 2 dict names */
2252 static gint compare_dict(Dictionary *a, Dictionary *b)
2254 guint aparts = 0, bparts = 0;
2257 for (i=0; i < strlen(a->dictname); i++)
2258 if (a->dictname[i] == '-')
2260 for (i=0; i < strlen(b->dictname); i++)
2261 if (b->dictname[i] == '-')
2264 if (aparts != bparts)
2265 return (aparts < bparts) ? -1 : +1;
2268 compare = strcmp2(a->dictname, b->dictname);
2270 compare = strcmp2(a->fullname, b->fullname);
2275 static void dictionary_delete(Dictionary *dict)
2277 g_free(dict->fullname);
2278 g_free(dict->dictname);
2282 static Dictionary *dictionary_dup(const Dictionary *dict)
2286 dict2 = g_new(Dictionary, 1);
2288 dict2->fullname = g_strdup(dict->fullname);
2289 dict2->dictname = g_strdup(dict->dictname);
2294 void gtkaspell_free_suggestions_list(GtkAspell *gtkaspell)
2298 for (list = gtkaspell->suggestions_list; list != NULL;
2302 g_list_free(gtkaspell->suggestions_list);
2304 gtkaspell->max_sug = -1;
2305 gtkaspell->suggestions_list = NULL;
2308 static void reset_theword_data(GtkAspell *gtkaspell)
2310 gtkaspell->start_pos = 0;
2311 gtkaspell->end_pos = 0;
2312 gtkaspell->theword[0] = 0;
2313 gtkaspell->max_sug = -1;
2315 gtkaspell_free_suggestions_list(gtkaspell);
2318 static void free_checkers(gpointer elt, gpointer data)
2320 GtkAspeller *gtkaspeller = elt;
2322 cm_return_if_fail(gtkaspeller);
2324 gtkaspeller_real_delete(gtkaspeller);
2327 static gint find_gtkaspeller(gconstpointer aa, gconstpointer bb)
2329 Dictionary *a = ((GtkAspeller *) aa)->dictionary;
2330 Dictionary *b = ((GtkAspeller *) bb)->dictionary;
2332 if (a && b && a->fullname && b->fullname)
2333 return strcmp(a->fullname, b->fullname);
2338 gchar *gtkaspell_get_default_dictionary(GtkAspell *gtkaspell)
2340 if (gtkaspell && gtkaspell->gtkaspeller &&
2341 gtkaspell->gtkaspeller->dictionary)
2342 return gtkaspell->gtkaspeller->dictionary->dictname;