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>
37 # include <sys/wait.h>
49 #include <glib/gi18n.h>
53 #include <gdk/gdkkeysyms.h>
56 #include "alertpanel.h"
57 #include "gtkaspell.h"
58 #include "gtk/gtkutils.h"
59 #include "gtk/combobox.h"
61 #define ASPELL_FASTMODE 1
62 #define ASPELL_NORMALMODE 2
63 #define ASPELL_BADSPELLERMODE 3
65 /* size of the text buffer used in various word-processing routines. */
68 /* number of suggestions to display on each menu. */
72 SET_GTKASPELL_NAME = 0,
73 SET_GTKASPELL_FULLNAME = 1,
77 typedef struct _GtkAspellCheckers {
79 GSList *dictionary_list;
83 /******************************************************************************/
85 static GtkAspellCheckers *gtkaspellcheckers;
87 /* Error message storage */
88 static void gtkaspell_checkers_error_message (gchar *message);
91 static void entry_insert_cb (GtkTextBuffer *textbuf,
95 GtkAspell *gtkaspell);
96 static void entry_delete_cb (GtkTextBuffer *textbuf,
97 GtkTextIter *startiter,
99 GtkAspell *gtkaspell);
100 /*static gint button_press_intercept_cb (GtkTextView *gtktext,
102 GtkAspell *gtkaspell);
104 static void button_press_intercept_cb(GtkTextView *gtktext,
105 GtkMenu *menu, GtkAspell *gtkaspell);
107 /* Checker creation */
108 static GtkAspeller* gtkaspeller_new (Dictionary *dict);
109 static GtkAspeller* gtkaspeller_real_new (Dictionary *dict);
110 static GtkAspeller* gtkaspeller_delete (GtkAspeller *gtkaspeller);
111 static GtkAspeller* gtkaspeller_real_delete (GtkAspeller *gtkaspeller);
113 /* Checker configuration */
114 static EnchantDict *set_dictionary (EnchantBroker *broker,
116 static void set_use_both_cb (GtkMenuItem *w,
117 GtkAspell *gtkaspell);
119 /* Checker actions */
120 static gboolean check_at (GtkAspell *gtkaspell,
122 static gboolean check_at_cb (gpointer data);
123 static GList* misspelled_suggest (GtkAspell *gtkaspell,
125 static gboolean find_misspelled_cb (gpointer data,
127 static void add_word_to_session_cb (GtkWidget *w,
129 static void add_word_to_personal_cb (GtkWidget *w,
131 static void replace_with_create_dialog_cb (GtkWidget *w,
133 static void replace_with_supplied_word_cb (GtkWidget *w,
134 GtkAspell *gtkaspell);
135 static void replace_word_cb (GtkWidget *w,
137 static void replace_real_word (GtkAspell *gtkaspell,
138 const gchar *newword);
139 static void replace_real_word_cb (gpointer data,
140 const gchar *newword);
141 static void check_with_alternate_cb (GtkWidget *w,
143 static void toggle_check_while_typing_cb (GtkWidget *w,
147 static GSList* make_sug_menu (GtkAspell *gtkaspell);
148 static GSList * populate_submenu (GtkAspell *gtkaspell);
149 GSList* gtkaspell_make_config_menu (GtkAspell *gtkaspell);
150 static void set_menu_pos (GtkMenu *menu,
155 /* Other menu callbacks */
156 static gboolean aspell_key_pressed (GtkWidget *widget,
158 GtkAspell *gtkaspell);
159 static void change_dict_cb (GtkWidget *w,
160 GtkAspell *gtkaspell);
161 static void switch_to_alternate_cb (GtkWidget *w,
164 /* Misc. helper functions */
165 static void set_point_continue (GtkAspell *gtkaspell);
166 static void continue_check (gpointer *gtkaspell);
167 static gboolean iswordsep (gunichar c);
168 static gunichar get_text_index_whar (GtkAspell *gtkaspell,
170 static gboolean get_word_from_pos (GtkAspell *gtkaspell,
176 static void allocate_color (GtkAspell *gtkaspell,
178 static void change_color (GtkAspell *gtkaspell,
183 static gint compare_dict (Dictionary *a,
185 static void dictionary_delete (Dictionary *dict);
186 static Dictionary * dictionary_dup (const Dictionary *dict);
187 static void reset_theword_data (GtkAspell *gtkaspell);
188 static void free_checkers (gpointer elt,
190 static gint find_gtkaspeller (gconstpointer aa,
193 static void destroy_menu(GtkWidget *widget, gpointer user_data);
195 /******************************************************************************/
196 static gint get_textview_buffer_charcount(GtkTextView *view);
198 static void gtkaspell_free_dictionary_list (GSList *list);
199 static GSList* gtkaspell_get_dictionary_list (gint refresh);
201 static void gtkaspell_uncheck_all (GtkAspell *gtkaspell);
203 static gint get_textview_buffer_charcount(GtkTextView *view)
205 GtkTextBuffer *buffer;
207 cm_return_val_if_fail(view, 0);
209 buffer = gtk_text_view_get_buffer(view);
210 cm_return_val_if_fail(buffer, 0);
212 return gtk_text_buffer_get_char_count(buffer);
214 static gint get_textview_buffer_offset(GtkTextView *view)
216 GtkTextBuffer * buffer;
220 cm_return_val_if_fail(view, 0);
222 buffer = gtk_text_view_get_buffer(view);
223 cm_return_val_if_fail(buffer, 0);
225 mark = gtk_text_buffer_get_insert(buffer);
226 cm_return_val_if_fail(mark, 0);
228 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
230 return gtk_text_iter_get_offset(&iter);
232 static void set_textview_buffer_offset(GtkTextView *view, gint offset)
234 GtkTextBuffer *buffer;
237 cm_return_if_fail(view);
239 buffer = gtk_text_view_get_buffer(view);
240 cm_return_if_fail(buffer);
242 gtk_text_buffer_get_iter_at_offset(buffer, &iter, offset);
243 gtk_text_buffer_place_cursor(buffer, &iter);
245 /******************************************************************************/
247 void gtkaspell_checkers_init(void)
249 gtkaspellcheckers = g_new(GtkAspellCheckers, 1);
250 gtkaspellcheckers->checkers = NULL;
251 gtkaspellcheckers->dictionary_list = NULL;
252 gtkaspellcheckers->error_message = NULL;
255 void gtkaspell_checkers_quit(void)
260 if (gtkaspellcheckers == NULL)
263 if ((checkers = gtkaspellcheckers->checkers)) {
264 debug_print("Aspell: number of running checkers to delete %d\n",
265 g_slist_length(checkers));
267 g_slist_foreach(checkers, free_checkers, NULL);
268 g_slist_free(checkers);
269 gtkaspellcheckers->checkers = NULL;
272 if ((dict_list = gtkaspellcheckers->dictionary_list)) {
273 debug_print("Aspell: number of dictionaries to delete %d\n",
274 g_slist_length(dict_list));
276 gtkaspell_free_dictionary_list(dict_list);
277 gtkaspellcheckers->dictionary_list = NULL;
280 g_free(gtkaspellcheckers->error_message);
281 gtkaspellcheckers->error_message = NULL;
285 static void gtkaspell_checkers_error_message (gchar *message)
288 if (gtkaspellcheckers->error_message) {
289 tmp = g_strdup_printf("%s\n%s",
290 gtkaspellcheckers->error_message,
293 g_free(gtkaspellcheckers->error_message);
294 gtkaspellcheckers->error_message = tmp;
296 gtkaspellcheckers->error_message = message;
301 const char *gtkaspell_checkers_strerror(void)
303 cm_return_val_if_fail(gtkaspellcheckers, "");
304 return gtkaspellcheckers->error_message;
307 void gtkaspell_checkers_reset_error(void)
309 cm_return_if_fail(gtkaspellcheckers);
311 g_free(gtkaspellcheckers->error_message);
313 gtkaspellcheckers->error_message = NULL;
316 GtkAspell *gtkaspell_new(const gchar *dictionary,
317 const gchar *alt_dictionary,
318 const gchar *encoding, /* unused */
319 gint misspelled_color,
320 gboolean check_while_typing,
321 gboolean recheck_when_changing_dict,
322 gboolean use_alternate,
323 gboolean use_both_dicts,
324 GtkTextView *gtktext,
325 GtkWindow *parent_win,
326 void (dict_changed_cb)(void *data),
327 void (*spell_menu_cb)(void *data),
331 GtkAspell *gtkaspell;
332 GtkAspeller *gtkaspeller;
333 GtkTextBuffer *buffer;
335 cm_return_val_if_fail(gtktext, NULL);
336 if (!dictionary || !*dictionary) {
337 gtkaspell_checkers_error_message(
338 g_strdup(_("No dictionary selected.")));
342 buffer = gtk_text_view_get_buffer(gtktext);
344 dict = g_new0(Dictionary, 1);
345 if (strrchr(dictionary, '/')) {
346 dict->fullname = g_strdup(strrchr(dictionary, '/')+1);
347 dict->dictname = g_strdup(strrchr(dictionary, '/')+1);
349 dict->fullname = g_strdup(dictionary);
350 dict->dictname = g_strdup(dictionary);
353 if (strchr(dict->fullname, '-')) {
354 *(strchr(dict->fullname, '-')) = '\0';
355 *(strchr(dict->dictname, '-')) = '\0';
357 gtkaspeller = gtkaspeller_new(dict);
358 dictionary_delete(dict);
361 gtkaspell_checkers_error_message(
362 g_strdup_printf(_("Couldn't initialize %s speller."), dictionary));
366 gtkaspell = g_new0(GtkAspell, 1);
368 gtkaspell->gtkaspeller = gtkaspeller;
370 if (use_alternate && alt_dictionary && *alt_dictionary) {
371 Dictionary *alt_dict;
372 GtkAspeller *alt_gtkaspeller;
374 alt_dict = g_new0(Dictionary, 1);
375 if (strrchr(alt_dictionary, '/')) {
376 alt_dict->fullname = g_strdup(strrchr(alt_dictionary, '/')+1);
377 alt_dict->dictname = g_strdup(strrchr(alt_dictionary, '/')+1);
379 alt_dict->fullname = g_strdup(alt_dictionary);
380 alt_dict->dictname = g_strdup(alt_dictionary);
382 if (strchr(alt_dict->fullname, '-')) {
383 *(strchr(alt_dict->fullname, '-')) = '\0';
384 *(strchr(alt_dict->dictname, '-')) = '\0';
387 alt_gtkaspeller = gtkaspeller_new(alt_dict);
388 dictionary_delete(alt_dict);
390 if (!alt_gtkaspeller) {
391 gtkaspell_checkers_error_message(
392 g_strdup_printf(_("Couldn't initialize %s speller."), dictionary));
393 gtkaspeller_delete(gtkaspeller);
398 gtkaspell->alternate_speller = alt_gtkaspeller;
400 gtkaspell->alternate_speller = NULL;
403 gtkaspell->theword[0] = 0x00;
404 gtkaspell->start_pos = 0;
405 gtkaspell->end_pos = 0;
406 gtkaspell->orig_pos = -1;
407 gtkaspell->end_check_pos = -1;
408 gtkaspell->misspelled = -1;
409 gtkaspell->check_while_typing = check_while_typing;
410 gtkaspell->recheck_when_changing_dict = recheck_when_changing_dict;
411 gtkaspell->continue_check = NULL;
412 gtkaspell->replace_entry = NULL;
413 gtkaspell->gtktext = gtktext;
414 gtkaspell->max_sug = -1;
415 gtkaspell->suggestions_list = NULL;
416 gtkaspell->use_alternate = use_alternate;
417 gtkaspell->use_both_dicts = use_both_dicts;
418 gtkaspell->parent_window = GTK_WIDGET(parent_win);
419 gtkaspell->dict_changed_cb = dict_changed_cb;
420 gtkaspell->menu_changed_cb = spell_menu_cb;
421 gtkaspell->menu_changed_data = data;
423 allocate_color(gtkaspell, misspelled_color);
425 g_signal_connect_after(G_OBJECT(buffer), "insert-text",
426 G_CALLBACK(entry_insert_cb), gtkaspell);
427 g_signal_connect_after(G_OBJECT(buffer), "delete-range",
428 G_CALLBACK(entry_delete_cb), gtkaspell);
429 /*g_signal_connect(G_OBJECT(gtktext), "button-press-event",
430 G_CALLBACK(button_press_intercept_cb),
432 g_signal_connect(G_OBJECT(gtktext), "populate-popup",
433 G_CALLBACK(button_press_intercept_cb), gtkaspell);
435 debug_print("Aspell: created gtkaspell %p\n", gtkaspell);
440 void gtkaspell_delete(GtkAspell *gtkaspell)
442 GtkTextView *gtktext = gtkaspell->gtktext;
444 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
445 G_CALLBACK(entry_insert_cb),
447 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
448 G_CALLBACK(entry_delete_cb),
450 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
451 G_CALLBACK(button_press_intercept_cb),
454 gtkaspell_uncheck_all(gtkaspell);
456 gtkaspeller_delete(gtkaspell->gtkaspeller);
458 if (gtkaspell->alternate_speller)
459 gtkaspeller_delete(gtkaspell->alternate_speller);
461 if (gtkaspell->suggestions_list)
462 gtkaspell_free_suggestions_list(gtkaspell);
464 debug_print("Aspell: deleting gtkaspell %p\n", gtkaspell);
471 void gtkaspell_dict_changed(GtkAspell *gtkaspell)
473 if(!gtkaspell || !gtkaspell->dict_changed_cb ||
474 !gtkaspell->menu_changed_data)
477 gtkaspell->dict_changed_cb(gtkaspell->menu_changed_data);
480 static void entry_insert_cb(GtkTextBuffer *textbuf,
484 GtkAspell *gtkaspell)
488 cm_return_if_fail(gtkaspell->gtkaspeller->speller);
490 if (!gtkaspell->check_while_typing)
493 pos = gtk_text_iter_get_offset(iter);
495 if (iswordsep(g_utf8_get_char(newtext))) {
496 /* did we just end a word? */
498 check_at(gtkaspell, pos - 2);
500 /* did we just split a word? */
501 if (pos < gtk_text_buffer_get_char_count(textbuf))
502 check_at(gtkaspell, pos + 1);
504 /* check as they type, *except* if they're typing at the end (the most
507 if (pos < gtk_text_buffer_get_char_count(textbuf) &&
508 !iswordsep(get_text_index_whar(gtkaspell, pos))) {
509 check_at(gtkaspell, pos - 1);
514 static void entry_delete_cb(GtkTextBuffer *textbuf,
515 GtkTextIter *startiter,
516 GtkTextIter *enditer,
517 GtkAspell *gtkaspell)
522 cm_return_if_fail(gtkaspell->gtkaspeller->speller);
524 if (!gtkaspell->check_while_typing)
527 start = gtk_text_iter_get_offset(startiter);
528 end = gtk_text_iter_get_offset(enditer);
529 origpos = get_textview_buffer_offset(gtkaspell->gtktext);
531 check_at(gtkaspell, start - 1);
532 check_at(gtkaspell, start);
535 set_textview_buffer_offset(gtkaspell->gtktext, origpos);
536 /* this is to *UNDO* the selection, in case they were holding shift
537 * while hitting backspace. */
538 /* needed with textview ??? */
539 /* gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos); */
542 void gtkaspell_make_context_menu(GtkMenu *menu, GtkAspell *gtkaspell)
544 GtkMenuItem *menuitem;
545 GSList *spell_menu = NULL;
547 gboolean suggest = FALSE;
549 if (gtkaspell->misspelled &&
550 misspelled_suggest(gtkaspell, gtkaspell->theword)) {
551 spell_menu = make_sug_menu(gtkaspell);
555 spell_menu = gtkaspell_make_config_menu(gtkaspell);
557 menuitem = GTK_MENU_ITEM(gtk_separator_menu_item_new());
558 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
559 gtk_widget_show(GTK_WIDGET(menuitem));
561 spell_menu = g_slist_reverse(spell_menu);
562 for (items = spell_menu;
563 items; items = items->next) {
564 menuitem = GTK_MENU_ITEM(items->data);
565 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
566 gtk_widget_show(GTK_WIDGET(menuitem));
568 g_slist_free(spell_menu);
570 g_signal_connect(G_OBJECT(menu), "deactivate",
571 G_CALLBACK(destroy_menu),
574 g_signal_connect(G_OBJECT(menu),
576 G_CALLBACK(aspell_key_pressed),
580 static void set_position_cb(gpointer data, gint pos)
582 GtkAspell *gtkaspell = (GtkAspell *) data;
583 set_textview_buffer_offset(gtkaspell->gtktext, pos);
586 void gtkaspell_context_set(GtkAspell *gtkaspell)
588 gtkaspell->ctx.set_position = set_position_cb;
589 gtkaspell->ctx.set_menu_pos = set_menu_pos;
590 gtkaspell->ctx.find_misspelled = find_misspelled_cb;
591 gtkaspell->ctx.check_word = check_at_cb;
592 gtkaspell->ctx.replace_word = replace_real_word_cb;
593 gtkaspell->ctx.data = (gpointer) gtkaspell;
596 static void button_press_intercept_cb(GtkTextView *gtktext,
597 GtkMenu *menu, GtkAspell *gtkaspell)
599 gtktext = gtkaspell->gtktext;
600 gtkaspell->orig_pos = get_textview_buffer_offset(gtktext);
601 gtkaspell->misspelled = check_at(gtkaspell, gtkaspell->orig_pos);
603 gtkaspell_context_set(gtkaspell);
604 gtkaspell_make_context_menu(menu, gtkaspell);
606 /* Checker creation */
607 static GtkAspeller *gtkaspeller_new(Dictionary *dictionary)
610 GtkAspeller *gtkaspeller = NULL;
614 cm_return_val_if_fail(gtkaspellcheckers, NULL);
616 cm_return_val_if_fail(dictionary, NULL);
618 if (dictionary->dictname == NULL)
619 gtkaspell_checkers_error_message(
620 g_strdup(_("No dictionary selected.")));
622 cm_return_val_if_fail(dictionary->fullname, NULL);
624 dict = dictionary_dup(dictionary);
626 tmp = g_new0(GtkAspeller, 1);
627 tmp->dictionary = dict;
629 exist = g_slist_find_custom(gtkaspellcheckers->checkers, tmp,
634 if ((gtkaspeller = gtkaspeller_real_new(dict)) != NULL) {
635 gtkaspellcheckers->checkers = g_slist_append(
636 gtkaspellcheckers->checkers,
639 debug_print("Aspell: Created a new gtkaspeller %p\n",
642 dictionary_delete(dict);
644 debug_print("Aspell: Could not create spell checker.\n");
647 debug_print("Aspell: number of existing checkers %d\n",
648 g_slist_length(gtkaspellcheckers->checkers));
653 static GtkAspeller *gtkaspeller_real_new(Dictionary *dict)
655 GtkAspeller *gtkaspeller;
656 EnchantBroker *broker;
657 EnchantDict *speller;
659 cm_return_val_if_fail(gtkaspellcheckers, NULL);
660 cm_return_val_if_fail(dict, NULL);
662 gtkaspeller = g_new(GtkAspeller, 1);
664 gtkaspeller->dictionary = dict;
666 broker = enchant_broker_init();
669 gtkaspell_checkers_error_message(
670 g_strdup(_("Couldn't initialize Enchant broker.")));
674 if ((speller = set_dictionary(broker, dict)) == NULL) {
675 gtkaspell_checkers_error_message(
676 g_strdup_printf(_("Couldn't initialize %s dictionary:"), dict->fullname));
677 gtkaspell_checkers_error_message(
678 g_strdup(enchant_broker_get_error(broker)));
682 gtkaspeller->speller = speller;
683 gtkaspeller->broker = broker;
688 static GtkAspeller *gtkaspeller_delete(GtkAspeller *gtkaspeller)
690 cm_return_val_if_fail(gtkaspellcheckers, NULL);
692 gtkaspellcheckers->checkers =
693 g_slist_remove(gtkaspellcheckers->checkers,
696 debug_print("Aspell: Deleting gtkaspeller %p.\n",
699 gtkaspeller_real_delete(gtkaspeller);
701 debug_print("Aspell: number of existing checkers %d\n",
702 g_slist_length(gtkaspellcheckers->checkers));
707 static GtkAspeller *gtkaspeller_real_delete(GtkAspeller *gtkaspeller)
709 cm_return_val_if_fail(gtkaspeller, NULL);
710 cm_return_val_if_fail(gtkaspeller->speller, NULL);
712 enchant_broker_free_dict(gtkaspeller->broker, gtkaspeller->speller);
713 enchant_broker_free(gtkaspeller->broker);
715 dictionary_delete(gtkaspeller->dictionary);
717 debug_print("Aspell: gtkaspeller %p deleted.\n",
725 /*****************************************************************************/
726 /* Checker configuration */
728 static EnchantDict *set_dictionary(EnchantBroker *broker, Dictionary *dict)
730 cm_return_val_if_fail(broker, FALSE);
731 cm_return_val_if_fail(dict, FALSE);
733 return enchant_broker_request_dict(broker, dict->dictname );
736 static void set_use_both_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
738 gtkaspell->use_both_dicts = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
739 gtkaspell_dict_changed(gtkaspell);
741 if (gtkaspell->menu_changed_cb)
742 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
745 /* misspelled_suggest() - Create a suggestion list for word */
746 static GList *misspelled_suggest(GtkAspell *gtkaspell, gchar *word)
751 cm_return_val_if_fail(word, NULL);
756 gtkaspell_free_suggestions_list(gtkaspell);
758 suggestions = enchant_dict_suggest(gtkaspell->gtkaspeller->speller, word, strlen(word), &num_sug);
759 list = g_list_append(list, g_strdup(word));
760 if (suggestions == NULL || num_sug == 0) {
761 gtkaspell->max_sug = -1;
762 gtkaspell->suggestions_list = list;
765 for (i = 0; i < num_sug; i++)
766 list = g_list_append(list, g_strdup((gchar *)suggestions[i]));
768 gtkaspell->max_sug = num_sug - 1;
769 gtkaspell->suggestions_list = list;
770 enchant_dict_free_string_list(gtkaspell->gtkaspeller->speller, suggestions);
774 /* misspelled_test() - Just test if word is correctly spelled */
775 int gtkaspell_misspelled_test(GtkAspell *gtkaspell, char *word)
778 cm_return_val_if_fail(word, 0);
783 result = enchant_dict_check(gtkaspell->gtkaspeller->speller, word, strlen(word));
785 if (result && gtkaspell->use_both_dicts && gtkaspell->alternate_speller) {
786 gtkaspell_use_alternate_dict(gtkaspell);
787 result = enchant_dict_check(gtkaspell->gtkaspeller->speller, word, strlen(word));
788 gtkaspell_use_alternate_dict(gtkaspell);
790 return (result && strcasecmp(word, "sylpheed") &&
791 strcasecmp(word, "claws-mail"));
795 static gboolean iswordsep(gunichar c)
797 return (g_unichar_isspace(c) || g_unichar_ispunct(c)) && c != (gunichar)'\'';
800 static gunichar get_text_index_whar(GtkAspell *gtkaspell, int pos)
802 GtkTextView *view = gtkaspell->gtktext;
803 GtkTextBuffer *buffer = gtk_text_view_get_buffer(view);
804 GtkTextIter start, end;
808 gtk_text_buffer_get_iter_at_offset(buffer, &start, pos);
809 gtk_text_buffer_get_iter_at_offset(buffer, &end, pos+1);
811 utf8chars = gtk_text_iter_get_text(&start, &end);
812 a = g_utf8_get_char(utf8chars);
818 /* get_word_from_pos () - return the word pointed to. */
819 /* Handles correctly the quotes. */
820 static gboolean get_word_from_pos(GtkAspell *gtkaspell, gint pos,
821 char* buf, gint buflen,
822 gint *pstart, gint *pend)
825 /* TODO : when correcting a word into quotes, change the color of */
826 /* the quotes too, as may be they were highlighted before. To do */
827 /* this, we can use two others pointers that points to the whole */
828 /* word including quotes. */
834 GtkTextView *gtktext;
836 gtktext = gtkaspell->gtktext;
837 if (iswordsep(get_text_index_whar(gtkaspell, pos)))
840 /* The apostrophe character is somtimes used for quotes
841 * So include it in the word only if it is not surrounded
842 * by other characters.
845 for (start = pos; start >= 0; --start) {
846 c = get_text_index_whar(gtkaspell, start);
847 if (c == (gunichar)'\'') {
849 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
851 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
853 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
855 /* start_quote = TRUE; */
860 /* start_quote = TRUE; */
864 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
870 for (end = pos; end < get_textview_buffer_charcount(gtktext); end++) {
871 c = get_text_index_whar(gtkaspell, end);
872 if (c == (gunichar)'\'') {
873 if (end < get_textview_buffer_charcount(gtktext)) {
874 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
876 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
878 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
880 /* end_quote = TRUE; */
885 /* end_quote = TRUE; */
889 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
899 if (end - start < buflen) {
900 GtkTextIter iterstart, iterend;
902 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtktext);
903 gtk_text_buffer_get_iter_at_offset(buffer, &iterstart, start);
904 gtk_text_buffer_get_iter_at_offset(buffer, &iterend, end);
905 tmp = gtk_text_buffer_get_text(buffer, &iterstart, &iterend, FALSE);
906 strncpy(buf, tmp, buflen-1);
916 static gboolean check_at(GtkAspell *gtkaspell, gint from_pos)
919 char buf[GTKASPELLWORDSIZE];
920 GtkTextView *gtktext;
922 cm_return_val_if_fail(from_pos >= 0, FALSE);
924 gtktext = gtkaspell->gtktext;
926 if (!get_word_from_pos(gtkaspell, from_pos, buf, sizeof(buf),
930 if (gtkaspell_misspelled_test(gtkaspell, buf)) {
931 strncpy(gtkaspell->theword, (gchar *)buf, GTKASPELLWORDSIZE - 1);
932 gtkaspell->theword[GTKASPELLWORDSIZE - 1] = 0;
933 gtkaspell->start_pos = start;
934 gtkaspell->end_pos = end;
935 gtkaspell_free_suggestions_list(gtkaspell);
937 change_color(gtkaspell, start, end, (gchar *)buf, &(gtkaspell->highlight));
940 change_color(gtkaspell, start, end, (gchar *)buf, NULL);
945 static gboolean check_at_cb(gpointer data)
947 GtkAspell *gtkaspell = (GtkAspell *)data;
948 return check_at(gtkaspell, gtkaspell->start_pos);
951 static gboolean find_misspelled_cb(gpointer data, gboolean forward)
953 GtkAspell *gtkaspell = (GtkAspell *)data;
961 maxpos = gtkaspell->end_check_pos;
969 pos = get_textview_buffer_offset(gtkaspell->gtktext);
970 gtkaspell->orig_pos = pos;
971 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
972 pos > minpos && pos <= maxpos)
974 while (!(misspelled = check_at(gtkaspell, pos)) &&
975 pos > minpos && pos <= maxpos) {
977 while (!iswordsep(get_text_index_whar(gtkaspell, pos)) &&
978 pos > minpos && pos <= maxpos)
981 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
982 pos > minpos && pos <= maxpos)
989 gboolean gtkaspell_check_next_prev(GtkAspell *gtkaspell, gboolean forward)
991 gboolean misspelled = gtkaspell->ctx.find_misspelled(gtkaspell->ctx.data,
996 misspelled_suggest(gtkaspell, gtkaspell->theword);
999 gtkaspell->orig_pos = gtkaspell->end_pos;
1001 gtkaspell->ctx.set_position(gtkaspell->ctx.data, gtkaspell->end_pos);
1003 /* only execute when in textview context */
1004 if (gtkaspell == (GtkAspell *)gtkaspell->ctx.data) {
1005 /* scroll line to window center */
1006 gtk_text_view_scroll_to_mark(gtkaspell->gtktext,
1007 gtk_text_buffer_get_insert(
1008 gtk_text_view_get_buffer(gtkaspell->gtktext)),
1009 0.0, TRUE, 0.0, 0.5);
1010 /* let textview recalculate coordinates (set_menu_pos) */
1011 while (gtk_events_pending ())
1012 gtk_main_iteration ();
1015 list = make_sug_menu(gtkaspell);
1016 menu = gtk_menu_new();
1017 for (cur = list; cur; cur = cur->next)
1018 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(cur->data));
1020 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1021 gtkaspell->ctx.set_menu_pos,
1022 gtkaspell->ctx.data,
1023 0, GDK_CURRENT_TIME);
1024 g_signal_connect(G_OBJECT(menu), "deactivate",
1025 G_CALLBACK(destroy_menu),
1027 g_signal_connect(G_OBJECT(menu),
1029 G_CALLBACK(aspell_key_pressed),
1034 reset_theword_data(gtkaspell);
1036 alertpanel_notice(_("No misspelled word found."));
1037 gtkaspell->ctx.set_position(gtkaspell->ctx.data,
1038 gtkaspell->orig_pos);
1044 void gtkaspell_check_backwards(GtkAspell *gtkaspell)
1046 gtkaspell->continue_check = NULL;
1047 gtkaspell->end_check_pos =
1048 get_textview_buffer_charcount(gtkaspell->gtktext);
1049 gtkaspell_context_set(gtkaspell);
1050 gtkaspell_check_next_prev(gtkaspell, FALSE);
1053 void gtkaspell_check_forwards_go(GtkAspell *gtkaspell)
1056 gtkaspell->continue_check = NULL;
1057 gtkaspell->end_check_pos =
1058 get_textview_buffer_charcount(gtkaspell->gtktext);
1059 gtkaspell_context_set(gtkaspell);
1060 gtkaspell_check_next_prev(gtkaspell, TRUE);
1063 void gtkaspell_check_all(GtkAspell *gtkaspell)
1065 GtkTextView *gtktext;
1067 GtkTextBuffer *buffer;
1068 GtkTextIter startiter, enditer;
1070 cm_return_if_fail(gtkaspell);
1071 cm_return_if_fail(gtkaspell->gtktext);
1073 gtktext = gtkaspell->gtktext;
1074 buffer = gtk_text_view_get_buffer(gtktext);
1075 gtk_text_buffer_get_selection_bounds(buffer, &startiter, &enditer);
1076 start = gtk_text_iter_get_offset(&startiter);
1077 end = gtk_text_iter_get_offset(&enditer);
1081 end = gtk_text_buffer_get_char_count(buffer);
1082 } else if (start > end) {
1090 set_textview_buffer_offset(gtktext, start);
1092 gtkaspell->continue_check = continue_check;
1093 gtkaspell->end_check_pos = end;
1095 gtkaspell_context_set(gtkaspell);
1096 gtkaspell->misspelled = gtkaspell_check_next_prev(gtkaspell, TRUE);
1099 static void continue_check(gpointer *data)
1101 GtkAspell *gtkaspell = (GtkAspell *) data;
1102 gint pos = get_textview_buffer_offset(gtkaspell->gtktext);
1103 if (pos < gtkaspell->end_check_pos && gtkaspell->misspelled)
1104 gtkaspell->misspelled = gtkaspell_check_next_prev(gtkaspell, TRUE);
1106 gtkaspell->continue_check = NULL;
1109 void gtkaspell_highlight_all(GtkAspell *gtkaspell)
1114 GtkTextView *gtktext;
1116 cm_return_if_fail(gtkaspell->gtkaspeller->speller);
1118 gtktext = gtkaspell->gtktext;
1120 len = get_textview_buffer_charcount(gtktext);
1122 origpos = get_textview_buffer_offset(gtktext);
1126 iswordsep(get_text_index_whar(gtkaspell, pos)))
1129 !iswordsep(get_text_index_whar(gtkaspell, pos)))
1132 check_at(gtkaspell, pos - 1);
1134 set_textview_buffer_offset(gtktext, origpos);
1137 static void replace_with_supplied_word_cb(GtkWidget *w, GtkAspell *gtkaspell)
1140 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1142 newword = gtk_editable_get_chars(GTK_EDITABLE(gtkaspell->replace_entry),
1145 if (strcmp(newword, gtkaspell->theword)) {
1146 gtkaspell->ctx.replace_word(gtkaspell->ctx.data, newword);
1148 if ((e->type == GDK_KEY_PRESS &&
1149 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK)) {
1150 enchant_dict_store_replacement(gtkaspell->gtkaspeller->speller,
1151 gtkaspell->theword, strlen(gtkaspell->theword),
1152 newword, strlen(newword));
1154 gtkaspell->replace_entry = NULL;
1159 if (w && GTK_IS_DIALOG(w)) {
1160 gtk_widget_destroy(w);
1163 set_point_continue(gtkaspell);
1167 static void replace_word_cb(GtkWidget *w, gpointer data)
1169 const char *newword;
1170 GtkAspell *gtkaspell = (GtkAspell *) data;
1171 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1173 newword = gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN((w)))));
1175 gtkaspell->ctx.replace_word(gtkaspell->ctx.data, newword);
1177 if ((e->type == GDK_KEY_PRESS &&
1178 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK) ||
1179 (e->type == GDK_BUTTON_RELEASE &&
1180 ((GdkEventButton *) e)->state & GDK_CONTROL_MASK)) {
1181 enchant_dict_store_replacement(gtkaspell->gtkaspeller->speller,
1182 gtkaspell->theword, strlen(gtkaspell->theword),
1183 newword, strlen(newword));
1186 gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1188 set_point_continue(gtkaspell);
1191 static void replace_real_word(GtkAspell *gtkaspell, const gchar *newword)
1193 int oldlen, newlen, wordlen;
1196 GtkTextView *gtktext;
1197 GtkTextBuffer *textbuf;
1198 GtkTextIter startiter, enditer;
1200 if (!newword) return;
1202 gtktext = gtkaspell->gtktext;
1203 textbuf = gtk_text_view_get_buffer(gtktext);
1205 origpos = gtkaspell->orig_pos;
1207 oldlen = gtkaspell->end_pos - gtkaspell->start_pos;
1208 wordlen = strlen(gtkaspell->theword);
1210 newlen = strlen(newword); /* FIXME: multybyte characters? */
1212 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1213 G_CALLBACK(entry_insert_cb),
1215 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1216 G_CALLBACK(entry_delete_cb),
1219 gtk_text_buffer_get_iter_at_offset(textbuf, &startiter,
1220 gtkaspell->start_pos);
1221 gtk_text_buffer_get_iter_at_offset(textbuf, &enditer,
1222 gtkaspell->end_pos);
1223 g_signal_emit_by_name(G_OBJECT(textbuf), "delete-range",
1224 &startiter, &enditer, gtkaspell);
1225 g_signal_emit_by_name(G_OBJECT(textbuf), "insert-text",
1226 &startiter, newword, newlen, gtkaspell);
1228 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1229 G_CALLBACK(entry_insert_cb),
1231 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1232 G_CALLBACK(entry_delete_cb),
1235 /* Put the point and the position where we clicked with the mouse
1236 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1237 * to let it update correctly the word insertion and then the
1238 * point & position position. If not, SEGV after the first replacement
1239 * If the new word ends before point, put the point at its end.
1242 if (origpos - gtkaspell->start_pos < oldlen &&
1243 origpos - gtkaspell->start_pos >= 0) {
1244 /* Original point was in the word.
1245 * Let it there unless point is going to be outside of the word
1247 if (origpos - gtkaspell->start_pos >= newlen) {
1248 pos = gtkaspell->start_pos + newlen;
1251 else if (origpos >= gtkaspell->end_pos) {
1252 /* move the position according to the change of length */
1253 pos = origpos + newlen - oldlen;
1256 gtkaspell->end_pos = gtkaspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1258 if (get_textview_buffer_charcount(gtktext) < pos)
1259 pos = get_textview_buffer_charcount(gtktext);
1260 gtkaspell->orig_pos = pos;
1262 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1265 static void replace_real_word_cb(gpointer data, const gchar *newword)
1267 replace_real_word((GtkAspell *)data, newword);
1270 /* Accept this word for this session */
1271 static void add_word_to_session_cb(GtkWidget *w, gpointer data)
1273 GtkTextView *gtktext;
1274 GtkAspell *gtkaspell = (GtkAspell *) data;
1275 gtktext = gtkaspell->gtktext;
1278 enchant_dict_add_to_session(gtkaspell->gtkaspeller->speller, gtkaspell->theword, strlen(gtkaspell->theword));
1280 gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1281 gtkaspell_dict_changed(gtkaspell);
1283 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1285 set_point_continue(gtkaspell);
1288 /* add_word_to_personal_cb() - add word to personal dict. */
1289 static void add_word_to_personal_cb(GtkWidget *w, gpointer data)
1291 GtkAspell *gtkaspell = (GtkAspell *) data;
1293 enchant_dict_add_to_pwl(gtkaspell->gtkaspeller->speller, gtkaspell->theword, strlen(gtkaspell->theword));
1295 gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1296 gtkaspell_dict_changed(gtkaspell);
1298 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1299 set_point_continue(gtkaspell);
1302 static void check_with_alternate_cb(GtkWidget *w, gpointer data)
1304 GtkAspell *gtkaspell = (GtkAspell *)data;
1307 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1309 gtkaspell_use_alternate_dict(gtkaspell);
1310 misspelled = gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1312 if (!gtkaspell->continue_check) {
1314 gtkaspell->misspelled = misspelled;
1316 if (gtkaspell->misspelled) {
1319 misspelled_suggest(gtkaspell, gtkaspell->theword);
1321 gtkaspell->ctx.set_position(gtkaspell->ctx.data,
1322 gtkaspell->end_pos);
1324 list = make_sug_menu(gtkaspell);
1325 menu = gtk_menu_new();
1326 for (cur = list; cur; cur = cur->next)
1327 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(cur->data));
1329 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1330 gtkaspell->ctx.set_menu_pos,
1331 gtkaspell->ctx.data, 0,
1333 g_signal_connect(G_OBJECT(menu), "deactivate",
1334 G_CALLBACK(destroy_menu),
1336 g_signal_connect(G_OBJECT(menu),
1338 G_CALLBACK(aspell_key_pressed),
1343 gtkaspell->orig_pos = gtkaspell->start_pos;
1345 set_point_continue(gtkaspell);
1348 static gboolean replace_key_pressed(GtkWidget *widget,
1350 GtkAspell *gtkaspell)
1352 if (event && event->keyval == GDK_KEY_Escape) {
1353 gtk_widget_destroy(widget);
1355 } else if (event && event->keyval == GDK_KEY_Return) {
1356 replace_with_supplied_word_cb(widget, gtkaspell);
1362 static void replace_with_create_dialog_cb(GtkWidget *w, gpointer data)
1364 static PangoFontDescription *font_desc;
1371 GtkWidget *ok_button;
1372 GtkWidget *cancel_button;
1373 GtkWidget *confirm_area;
1375 gchar *utf8buf, *thelabel;
1377 GtkAspell *gtkaspell = (GtkAspell *) data;
1379 gdk_window_get_origin((GTK_WIDGET(w)->parent)->window, &xx, &yy);
1381 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1383 dialog = gtk_dialog_new();
1385 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1386 gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1387 gtk_window_move(GTK_WINDOW(dialog), xx, yy);
1389 g_signal_connect_swapped(G_OBJECT(dialog), "destroy",
1390 G_CALLBACK(gtk_widget_destroy),
1393 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
1394 hbox = gtk_hbox_new (FALSE, 12);
1395 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1396 gtk_widget_show (hbox);
1397 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
1400 utf8buf = g_strdup(gtkaspell->theword);
1402 thelabel = g_strdup_printf(_("<span weight=\"bold\" "
1403 "size=\"larger\">Replace \"%s\" with: </span>"),
1405 /* for title label */
1406 w_hbox = gtk_hbox_new(FALSE, 0);
1408 icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
1409 GTK_ICON_SIZE_DIALOG);
1410 gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
1411 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
1413 vbox = gtk_vbox_new (FALSE, 12);
1414 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1415 gtk_widget_show (vbox);
1417 label = gtk_label_new(thelabel);
1418 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1419 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1420 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1421 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1422 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1426 size = pango_font_description_get_size
1427 (label->style->font_desc);
1428 font_desc = pango_font_description_new();
1429 pango_font_description_set_weight
1430 (font_desc, PANGO_WEIGHT_BOLD);
1431 pango_font_description_set_size
1432 (font_desc, size * PANGO_SCALE_LARGE);
1435 gtk_widget_modify_font(label, font_desc);
1438 entry = gtk_entry_new();
1439 gtkaspell->replace_entry = entry;
1440 gtk_entry_set_text(GTK_ENTRY(entry), utf8buf);
1441 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1442 g_signal_connect(G_OBJECT(dialog),
1444 G_CALLBACK(replace_key_pressed), gtkaspell);
1445 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
1448 label = gtk_label_new(_("Holding down Control key while pressing "
1449 "Enter\nwill learn from mistake.\n"));
1450 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1451 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1452 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1453 gtk_widget_show(label);
1455 hbox = gtk_hbox_new(TRUE, 0);
1457 gtkut_stock_button_set_create(&confirm_area,
1458 &cancel_button, GTK_STOCK_CANCEL,
1459 &ok_button, GTK_STOCK_OK,
1462 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1463 confirm_area, FALSE, FALSE, 0);
1464 gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
1466 g_signal_connect(G_OBJECT(ok_button), "clicked",
1467 G_CALLBACK(replace_with_supplied_word_cb),
1469 g_signal_connect_swapped(G_OBJECT(ok_button), "clicked",
1470 G_CALLBACK(gtk_widget_destroy),
1473 g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked",
1474 G_CALLBACK(gtk_widget_destroy),
1477 gtk_widget_grab_focus(entry);
1479 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1481 gtk_widget_show_all(dialog);
1484 static void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1486 GtkTextView *gtktext;
1487 GtkTextBuffer *buffer;
1488 GtkTextIter startiter, enditer;
1490 gtktext = gtkaspell->gtktext;
1492 buffer = gtk_text_view_get_buffer(gtktext);
1493 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, 0);
1494 gtk_text_buffer_get_iter_at_offset(buffer, &enditer,
1495 get_textview_buffer_charcount(gtktext)-1);
1496 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
1497 &startiter, &enditer);
1500 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1502 GtkAspell *gtkaspell = (GtkAspell *) data;
1504 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1506 if (!gtkaspell->check_while_typing)
1507 gtkaspell_uncheck_all(gtkaspell);
1508 if (gtkaspell->menu_changed_cb)
1509 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
1512 static GSList *create_empty_dictionary_list(void)
1514 GSList *list = NULL;
1517 dict = g_new0(Dictionary, 1);
1518 dict->fullname = g_strdup(_("None"));
1519 dict->dictname = NULL;
1521 return g_slist_append(list, dict);
1524 static void list_dict_cb(const char * const lang_tag,
1525 const char * const provider_name,
1526 const char * const provider_desc,
1527 const char * const provider_file,
1530 GSList **list = (GSList **)data;
1531 Dictionary *dict = g_new0(Dictionary, 1);
1532 dict->fullname = g_strdup(lang_tag);
1533 dict->dictname = g_strdup(lang_tag);
1535 if (g_slist_find_custom(*list, dict,
1536 (GCompareFunc) compare_dict) == NULL) {
1537 debug_print("Aspell: found dictionary %s %s\n", dict->fullname,
1539 *list = g_slist_insert_sorted(*list, dict,
1540 (GCompareFunc) compare_dict);
1542 dictionary_delete(dict);
1546 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1547 static GSList *gtkaspell_get_dictionary_list(gint refresh)
1550 EnchantBroker *broker;
1552 if (!gtkaspellcheckers)
1553 gtkaspell_checkers_init();
1555 if (gtkaspellcheckers->dictionary_list && !refresh)
1556 return gtkaspellcheckers->dictionary_list;
1558 gtkaspell_free_dictionary_list(
1559 gtkaspellcheckers->dictionary_list);
1562 broker = enchant_broker_init();
1564 enchant_broker_list_dicts(broker, list_dict_cb, &list);
1566 enchant_broker_free(broker);
1570 debug_print("Aspell: error when searching for dictionaries: "
1571 "No dictionary found.\n");
1572 list = create_empty_dictionary_list();
1575 gtkaspellcheckers->dictionary_list = list;
1580 static void gtkaspell_free_dictionary_list(GSList *list)
1584 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1586 dict = (Dictionary *) walk->data;
1587 dictionary_delete(dict);
1592 GtkTreeModel *gtkaspell_dictionary_store_new_with_refresh(gboolean refresh)
1594 GSList *dict_list, *tmp;
1595 GtkListStore *store;
1599 dict_list = gtkaspell_get_dictionary_list(refresh);
1600 cm_return_val_if_fail(dict_list, NULL);
1602 store = gtk_list_store_new(SET_GTKASPELL_SIZE,
1607 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1608 dict = (Dictionary *) tmp->data;
1610 gtk_list_store_append(store, &iter);
1611 gtk_list_store_set(store, &iter,
1612 SET_GTKASPELL_NAME, dict->dictname,
1613 SET_GTKASPELL_FULLNAME, dict->fullname,
1617 return GTK_TREE_MODEL(store);
1620 GtkTreeModel *gtkaspell_dictionary_store_new(void)
1622 return gtkaspell_dictionary_store_new_with_refresh
1626 GtkWidget *gtkaspell_dictionary_combo_new(const gboolean refresh)
1629 GtkCellRenderer *renderer;
1631 combo = gtk_combo_box_new_with_model(
1632 gtkaspell_dictionary_store_new_with_refresh(refresh));
1633 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1634 gtk_widget_show(combo);
1636 renderer = gtk_cell_renderer_text_new();
1637 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
1638 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),renderer,
1639 "text", SET_GTKASPELL_NAME, NULL);
1644 gchar *gtkaspell_get_dictionary_menu_active_item(GtkComboBox *combo)
1647 GtkTreeModel *model;
1648 gchar *dict_fullname = NULL;
1650 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo), NULL);
1651 cm_return_val_if_fail(gtk_combo_box_get_active_iter(combo, &iter), NULL);
1653 model = gtk_combo_box_get_model(combo);
1657 gtk_tree_model_get(model, &iter,
1658 SET_GTKASPELL_FULLNAME, &dict_fullname,
1661 return dict_fullname;
1664 gint gtkaspell_set_dictionary_menu_active_item(GtkComboBox *combo,
1665 const gchar *dictionary)
1667 GtkTreeModel *model;
1669 gchar *dict_name = NULL;
1671 cm_return_val_if_fail(combo != NULL, 0);
1672 cm_return_val_if_fail(dictionary != NULL, 0);
1673 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo), 0);
1675 if((model = gtk_combo_box_get_model(combo)) == NULL)
1677 if((gtk_tree_model_get_iter_first(model, &iter)) == FALSE)
1681 gtk_tree_model_get(model, &iter,
1682 SET_GTKASPELL_FULLNAME, &dict_name,
1685 if ((dict_name != NULL) && !strcmp2(dict_name, dictionary)) {
1686 gtk_combo_box_set_active_iter(combo, &iter);
1693 } while ((gtk_tree_model_iter_next(model, &iter)) == TRUE);
1698 void gtkaspell_use_alternate_dict(GtkAspell *gtkaspell)
1702 tmp = gtkaspell->gtkaspeller;
1703 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1704 gtkaspell->alternate_speller = tmp;
1707 static void destroy_menu(GtkWidget *widget,
1708 gpointer user_data) {
1709 GtkAspell *gtkaspell = (GtkAspell *)user_data;
1711 if (gtkaspell->accel_group) {
1712 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1713 gtkaspell->accel_group);
1714 gtkaspell->accel_group = NULL;
1718 static gboolean aspell_key_pressed(GtkWidget *widget,
1720 GtkAspell *gtkaspell)
1722 if (event && (isascii(event->keyval) || event->keyval == GDK_KEY_Return)) {
1723 gtk_accel_groups_activate(
1724 G_OBJECT(gtkaspell->parent_window),
1725 event->keyval, event->state);
1726 } else if (event && event->keyval == GDK_KEY_Escape) {
1727 destroy_menu(NULL, gtkaspell);
1732 /* make_sug_menu() - Add menus to accept this word for this session
1733 * and to add it to personal dictionary
1735 static GSList *make_sug_menu(GtkAspell *gtkaspell)
1739 GtkTextView *gtktext;
1740 GtkAccelGroup *accel;
1741 GList *l = gtkaspell->suggestions_list;
1743 GSList *list = NULL;
1744 gtktext = gtkaspell->gtktext;
1749 accel = gtk_accel_group_new();
1751 if (gtkaspell->accel_group) {
1752 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1753 gtkaspell->accel_group);
1754 gtkaspell->accel_group = NULL;
1757 utf8buf = g_strdup(l->data);
1758 caption = g_strdup_printf(_("\"%s\" unknown in %s"),
1760 gtkaspell->gtkaspeller->dictionary->dictname);
1761 item = gtk_menu_item_new_with_label(caption);
1763 gtk_widget_show(item);
1764 list = g_slist_append(list, item);
1765 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
1768 item = gtk_menu_item_new();
1769 gtk_widget_show(item);
1770 list = g_slist_append(list, item);
1772 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1773 gtk_widget_show(item);
1774 list = g_slist_append(list, item);
1775 g_signal_connect(G_OBJECT(item), "activate",
1776 G_CALLBACK(add_word_to_session_cb),
1778 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_space,
1780 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1782 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1783 gtk_widget_show(item);
1784 list = g_slist_append(list, item);
1785 g_signal_connect(G_OBJECT(item), "activate",
1786 G_CALLBACK(add_word_to_personal_cb),
1788 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_Return,
1790 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1792 item = gtk_menu_item_new_with_label(_("Replace with..."));
1793 gtk_widget_show(item);
1794 list = g_slist_append(list, item);
1795 g_signal_connect(G_OBJECT(item), "activate",
1796 G_CALLBACK(replace_with_create_dialog_cb),
1798 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_R, 0,
1799 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1800 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_R,
1804 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1805 caption = g_strdup_printf(_("Check with %s"),
1806 gtkaspell->alternate_speller->dictionary->dictname);
1807 item = gtk_menu_item_new_with_label(caption);
1809 gtk_widget_show(item);
1810 list = g_slist_append(list, item);
1811 g_signal_connect(G_OBJECT(item), "activate",
1812 G_CALLBACK(check_with_alternate_cb),
1814 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_X, 0,
1815 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1816 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_X,
1821 item = gtk_menu_item_new();
1822 gtk_widget_show(item);
1823 list = g_slist_append(list, item);
1827 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
1828 gtk_widget_show(item);
1829 list = g_slist_append(list, item);
1831 GtkWidget *curmenu = NULL;
1835 if (count == MENUCOUNT) {
1838 item = gtk_menu_item_new_with_label(_("More..."));
1839 gtk_widget_show(item);
1841 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1843 list = g_slist_append(list, item);
1845 curmenu = gtk_menu_new();
1846 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1850 utf8buf = g_strdup(l->data);
1852 item = gtk_menu_item_new_with_label(utf8buf);
1854 gtk_widget_show(item);
1855 if (curmenu == NULL) {
1856 list = g_slist_append(list, item);
1858 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1860 g_signal_connect(G_OBJECT(item), "activate",
1861 G_CALLBACK(replace_word_cb),
1864 if (curmenu == NULL && count < MENUCOUNT) {
1865 gtk_widget_add_accelerator(item, "activate",
1867 GDK_KEY_A + count, 0,
1870 gtk_widget_add_accelerator(item, "activate",
1879 } while ((l = l->next) != NULL);
1882 gtk_window_add_accel_group
1883 (GTK_WINDOW(gtkaspell->parent_window),
1885 gtkaspell->accel_group = accel;
1890 static GSList *populate_submenu(GtkAspell *gtkaspell)
1892 GtkWidget *item, *submenu;
1894 GtkAspeller *gtkaspeller = NULL;
1895 GSList *list = NULL;
1900 gtkaspeller = gtkaspell->gtkaspeller;
1901 dictname = g_strdup_printf(_("Dictionary: %s"),
1902 gtkaspeller->dictionary->dictname);
1903 item = gtk_menu_item_new_with_label(dictname);
1904 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
1906 gtk_widget_show(item);
1907 list = g_slist_append(list, item);
1909 item = gtk_menu_item_new();
1910 gtk_widget_show(item);
1911 list = g_slist_append(list, item);
1913 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1914 dictname = g_strdup_printf(_("Use alternate (%s)"),
1915 gtkaspell->alternate_speller->dictionary->dictname);
1916 item = gtk_menu_item_new_with_label(dictname);
1918 g_signal_connect(G_OBJECT(item), "activate",
1919 G_CALLBACK(switch_to_alternate_cb),
1921 gtk_widget_show(item);
1922 list = g_slist_append(list, item);
1925 item = gtk_check_menu_item_new_with_label(_("Use both dictionaries"));
1926 if (gtkaspell->use_both_dicts) {
1927 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1929 g_signal_connect(G_OBJECT(item), "activate",
1930 G_CALLBACK(set_use_both_cb),
1932 gtk_widget_show(item);
1933 list = g_slist_append(list, item);
1935 item = gtk_menu_item_new();
1936 gtk_widget_show(item);
1937 list = g_slist_append(list, item);
1939 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
1940 if (gtkaspell->check_while_typing)
1941 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1943 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1944 g_signal_connect(G_OBJECT(item), "activate",
1945 G_CALLBACK(toggle_check_while_typing_cb),
1947 gtk_widget_show(item);
1948 list = g_slist_append(list, item);
1950 item = gtk_menu_item_new();
1951 gtk_widget_show(item);
1952 list = g_slist_append(list, item);
1954 submenu = gtk_menu_new();
1955 item = gtk_menu_item_new_with_label(_("Change dictionary"));
1956 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
1957 gtk_widget_show(item);
1958 list = g_slist_append(list, item);
1961 if (gtkaspellcheckers->dictionary_list == NULL)
1962 gtkaspell_get_dictionary_list(FALSE);
1964 GtkWidget * curmenu = submenu;
1968 tmp = gtkaspellcheckers->dictionary_list;
1970 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
1971 tmp = g_slist_next(tmp)) {
1972 if (count == MENUCOUNT) {
1975 newmenu = gtk_menu_new();
1976 item = gtk_menu_item_new_with_label(_("More..."));
1977 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1980 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1981 gtk_widget_show(item);
1985 dict = (Dictionary *) tmp->data;
1986 item = gtk_check_menu_item_new_with_label(dict->dictname);
1987 g_object_set_data(G_OBJECT(item), "dict_name",
1989 if (strcmp2(dict->fullname,
1990 gtkaspell->gtkaspeller->dictionary->fullname))
1991 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1993 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1994 gtk_widget_set_sensitive(GTK_WIDGET(item),
1997 g_signal_connect(G_OBJECT(item), "activate",
1998 G_CALLBACK(change_dict_cb),
2000 gtk_widget_show(item);
2001 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
2009 GSList *gtkaspell_make_config_menu(GtkAspell *gtkaspell)
2011 return populate_submenu(gtkaspell);
2014 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y,
2015 gboolean *push_in, gpointer data)
2017 GtkAspell *gtkaspell = (GtkAspell *) data;
2018 gint xx = 0, yy = 0;
2021 GtkTextView *text = GTK_TEXT_VIEW(gtkaspell->gtktext);
2022 GtkTextBuffer *textbuf;
2027 textbuf = gtk_text_view_get_buffer(gtkaspell->gtktext);
2028 gtk_text_buffer_get_iter_at_mark(textbuf, &iter,
2029 gtk_text_buffer_get_insert(textbuf));
2030 gtk_text_view_get_iter_location(gtkaspell->gtktext, &iter, &rect);
2031 gtk_text_view_buffer_to_window_coords(text, GTK_TEXT_WINDOW_TEXT,
2035 gdk_window_get_origin(GTK_WIDGET(gtkaspell->gtktext)->window, &xx, &yy);
2037 sx = gdk_screen_width();
2038 sy = gdk_screen_height();
2040 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2045 *x = rect.x + xx + 8;
2047 *y = rect.y + rect.height + yy;
2055 /* change the current dictionary of gtkaspell
2056 - if always_set_alt_dict is set, the alternate dict is unconditionally set to the former
2057 current dictionary (common use: from menu callbacks)
2058 - if always_set_alt_dict is NOT set, the alternate dict will be set to the former
2059 current dictionary only if there is no alternate dictionary already set
2060 (this is when we need to set the current dictionary then the alternate one
2061 when creating a compose window, from the account and folder settings)
2063 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary,
2064 gboolean always_set_alt_dict)
2067 GtkAspeller *gtkaspeller;
2069 cm_return_val_if_fail(gtkaspell, FALSE);
2070 cm_return_val_if_fail(dictionary, FALSE);
2072 dict = g_new0(Dictionary, 1);
2074 if (strrchr(dictionary, '/')) {
2075 dict->fullname = g_strdup(strrchr(dictionary, '/')+1);
2076 dict->dictname = g_strdup(strrchr(dictionary, '/')+1);
2078 dict->fullname = g_strdup(dictionary);
2079 dict->dictname = g_strdup(dictionary);
2082 if (dict->fullname && strchr(dict->fullname, '-')) {
2083 *(strchr(dict->fullname, '-')) = '\0';
2084 *(strchr(dict->dictname, '-')) = '\0';
2087 if (!dict->fullname || !(*dict->fullname)) {
2088 dictionary_delete(dict);
2091 gtkaspeller = gtkaspeller_new(dict);
2094 alertpanel_warning(_("The spell checker could not change dictionary.\n%s"),
2095 gtkaspellcheckers->error_message);
2097 if (gtkaspell->use_alternate) {
2098 if (gtkaspell->alternate_speller) {
2099 if (always_set_alt_dict) {
2100 gtkaspeller_delete(gtkaspell->alternate_speller);
2101 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2103 gtkaspeller_delete(gtkaspell->gtkaspeller);
2105 /* should never be reached as the dicts are always set
2106 to a default value */
2107 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2109 gtkaspeller_delete(gtkaspell->gtkaspeller);
2111 gtkaspell->gtkaspeller = gtkaspeller;
2114 dictionary_delete(dict);
2119 /* change the alternate dictionary of gtkaspell (doesn't affect the default dictionary) */
2120 gboolean gtkaspell_change_alt_dict(GtkAspell *gtkaspell, const gchar *alt_dictionary)
2123 GtkAspeller *gtkaspeller;
2125 cm_return_val_if_fail(gtkaspell, FALSE);
2126 cm_return_val_if_fail(alt_dictionary, FALSE);
2128 dict = g_new0(Dictionary, 1);
2129 if (strrchr(alt_dictionary, '/')) {
2130 dict->fullname = g_strdup(strrchr(alt_dictionary, '/')+1);
2131 dict->dictname = g_strdup(strrchr(alt_dictionary, '/')+1);
2133 dict->fullname = g_strdup(alt_dictionary);
2134 dict->dictname = g_strdup(alt_dictionary);
2137 if (dict->fullname && strchr(dict->fullname, '-')) {
2138 *(strchr(dict->fullname, '-')) = '\0';
2139 *(strchr(dict->dictname, '-')) = '\0';
2142 if (!dict->fullname || !(*dict->fullname)) {
2143 dictionary_delete(dict);
2147 gtkaspeller = gtkaspeller_new(dict);
2150 alertpanel_warning(_("The spell checker could not change the alternate dictionary.\n%s"),
2151 gtkaspellcheckers->error_message);
2153 if (gtkaspell->alternate_speller)
2154 gtkaspeller_delete(gtkaspell->alternate_speller);
2155 gtkaspell->alternate_speller = gtkaspeller;
2158 dictionary_delete(dict);
2163 /* Menu call backs */
2165 /* change_dict_cb() - Menu callback : change dict */
2166 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2170 fullname = (gchar *) g_object_get_data(G_OBJECT(w), "dict_name");
2172 if (!strcmp2(fullname, _("None")))
2175 gtkaspell_change_dict(gtkaspell, fullname, TRUE);
2176 gtkaspell_dict_changed(gtkaspell);
2178 if (gtkaspell->menu_changed_cb)
2179 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2182 static void switch_to_alternate_cb(GtkWidget *w,
2185 GtkAspell *gtkaspell = (GtkAspell *) data;
2186 gtkaspell_use_alternate_dict(gtkaspell);
2187 gtkaspell_dict_changed(gtkaspell);
2189 if (gtkaspell->menu_changed_cb)
2190 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2193 /* Misc. helper functions */
2195 static void set_point_continue(GtkAspell *gtkaspell)
2197 GtkTextView *gtktext;
2199 gtktext = gtkaspell->gtktext;
2201 gtkaspell->ctx.set_position(gtkaspell->ctx.data, gtkaspell->orig_pos);
2203 if (gtkaspell->continue_check)
2204 gtkaspell->continue_check((gpointer *) gtkaspell->ctx.data);
2207 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2209 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtkaspell->gtktext);
2210 GdkColor *color = &(gtkaspell->highlight);
2212 /* Shameless copy from Sylpheed's gtkutils.c */
2214 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2216 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2218 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2222 gtk_text_buffer_create_tag(buffer, "misspelled",
2223 "foreground-gdk", color, NULL);
2225 gtk_text_buffer_create_tag(buffer, "misspelled",
2226 "underline", PANGO_UNDERLINE_ERROR, NULL);
2230 static void change_color(GtkAspell * gtkaspell,
2231 gint start, gint end,
2235 GtkTextView *gtktext;
2236 GtkTextBuffer *buffer;
2237 GtkTextIter startiter, enditer;
2242 gtktext = gtkaspell->gtktext;
2244 buffer = gtk_text_view_get_buffer(gtktext);
2245 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, start);
2246 gtk_text_buffer_get_iter_at_offset(buffer, &enditer, end);
2248 gtk_text_buffer_apply_tag_by_name(buffer, "misspelled",
2249 &startiter, &enditer);
2251 gtk_text_iter_forward_char(&enditer);
2252 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
2253 &startiter, &enditer);
2257 /* compare_dict () - compare 2 dict names */
2258 static gint compare_dict(Dictionary *a, Dictionary *b)
2260 guint aparts = 0, bparts = 0;
2263 for (i=0; i < strlen(a->dictname); i++)
2264 if (a->dictname[i] == '-')
2266 for (i=0; i < strlen(b->dictname); i++)
2267 if (b->dictname[i] == '-')
2270 if (aparts != bparts)
2271 return (aparts < bparts) ? -1 : +1;
2274 compare = strcmp2(a->dictname, b->dictname);
2276 compare = strcmp2(a->fullname, b->fullname);
2281 static void dictionary_delete(Dictionary *dict)
2283 g_free(dict->fullname);
2284 g_free(dict->dictname);
2288 static Dictionary *dictionary_dup(const Dictionary *dict)
2292 dict2 = g_new(Dictionary, 1);
2294 dict2->fullname = g_strdup(dict->fullname);
2295 dict2->dictname = g_strdup(dict->dictname);
2300 void gtkaspell_free_suggestions_list(GtkAspell *gtkaspell)
2304 for (list = gtkaspell->suggestions_list; list != NULL;
2308 g_list_free(gtkaspell->suggestions_list);
2310 gtkaspell->max_sug = -1;
2311 gtkaspell->suggestions_list = NULL;
2314 static void reset_theword_data(GtkAspell *gtkaspell)
2316 gtkaspell->start_pos = 0;
2317 gtkaspell->end_pos = 0;
2318 gtkaspell->theword[0] = 0;
2319 gtkaspell->max_sug = -1;
2321 gtkaspell_free_suggestions_list(gtkaspell);
2324 static void free_checkers(gpointer elt, gpointer data)
2326 GtkAspeller *gtkaspeller = elt;
2328 cm_return_if_fail(gtkaspeller);
2330 gtkaspeller_real_delete(gtkaspeller);
2333 static gint find_gtkaspeller(gconstpointer aa, gconstpointer bb)
2335 Dictionary *a = ((GtkAspeller *) aa)->dictionary;
2336 Dictionary *b = ((GtkAspeller *) bb)->dictionary;
2338 if (a && b && a->fullname && b->fullname)
2339 return strcmp(a->fullname, b->fullname);
2344 gchar *gtkaspell_get_default_dictionary(GtkAspell *gtkaspell)
2346 if (gtkaspell && gtkaspell->gtkaspeller &&
2347 gtkaspell->gtkaspeller->dictionary)
2348 return gtkaspell->gtkaspeller->dictionary->dictname;