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 if (suggestions == NULL || num_sug == 0) {
754 gtkaspell->max_sug = - 1;
755 gtkaspell->suggestions_list = NULL;
758 list = g_list_append(list, g_strdup(word));
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_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1382 gtk_window_move(GTK_WINDOW(dialog), xx, yy);
1384 g_signal_connect_swapped(G_OBJECT(dialog), "destroy",
1385 G_CALLBACK(gtk_widget_destroy),
1388 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
1389 hbox = gtk_hbox_new (FALSE, 12);
1390 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1391 gtk_widget_show (hbox);
1392 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
1395 utf8buf = g_strdup(gtkaspell->theword);
1397 thelabel = g_strdup_printf(_("<span weight=\"bold\" "
1398 "size=\"larger\">Replace \"%s\" with: </span>"),
1400 /* for title label */
1401 w_hbox = gtk_hbox_new(FALSE, 0);
1403 icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
1404 GTK_ICON_SIZE_DIALOG);
1405 gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
1406 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
1408 vbox = gtk_vbox_new (FALSE, 12);
1409 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1410 gtk_widget_show (vbox);
1412 label = gtk_label_new(thelabel);
1413 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1414 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1415 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1416 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1417 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1421 size = pango_font_description_get_size
1422 (label->style->font_desc);
1423 font_desc = pango_font_description_new();
1424 pango_font_description_set_weight
1425 (font_desc, PANGO_WEIGHT_BOLD);
1426 pango_font_description_set_size
1427 (font_desc, size * PANGO_SCALE_LARGE);
1430 gtk_widget_modify_font(label, font_desc);
1433 entry = gtk_entry_new();
1434 gtkaspell->replace_entry = entry;
1435 gtk_entry_set_text(GTK_ENTRY(entry), utf8buf);
1436 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1437 g_signal_connect(G_OBJECT(dialog),
1439 G_CALLBACK(replace_key_pressed), gtkaspell);
1440 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
1443 label = gtk_label_new(_("Holding down Control key while pressing "
1444 "Enter\nwill learn from mistake.\n"));
1445 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1446 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1447 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1448 gtk_widget_show(label);
1450 hbox = gtk_hbox_new(TRUE, 0);
1452 gtkut_stock_button_set_create(&confirm_area,
1453 &cancel_button, GTK_STOCK_CANCEL,
1454 &ok_button, GTK_STOCK_OK,
1457 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1458 confirm_area, FALSE, FALSE, 0);
1459 gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
1461 g_signal_connect(G_OBJECT(ok_button), "clicked",
1462 G_CALLBACK(replace_with_supplied_word_cb),
1464 g_signal_connect_swapped(G_OBJECT(ok_button), "clicked",
1465 G_CALLBACK(gtk_widget_destroy),
1468 g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked",
1469 G_CALLBACK(gtk_widget_destroy),
1472 gtk_widget_grab_focus(entry);
1474 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1476 gtk_widget_show_all(dialog);
1479 static void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1481 GtkTextView *gtktext;
1482 GtkTextBuffer *buffer;
1483 GtkTextIter startiter, enditer;
1485 gtktext = gtkaspell->gtktext;
1487 buffer = gtk_text_view_get_buffer(gtktext);
1488 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, 0);
1489 gtk_text_buffer_get_iter_at_offset(buffer, &enditer,
1490 get_textview_buffer_charcount(gtktext)-1);
1491 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
1492 &startiter, &enditer);
1495 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1497 GtkAspell *gtkaspell = (GtkAspell *) data;
1499 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1501 if (!gtkaspell->check_while_typing)
1502 gtkaspell_uncheck_all(gtkaspell);
1503 if (gtkaspell->menu_changed_cb)
1504 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
1507 static GSList *create_empty_dictionary_list(void)
1509 GSList *list = NULL;
1512 dict = g_new0(Dictionary, 1);
1513 dict->fullname = g_strdup(_("None"));
1514 dict->dictname = NULL;
1516 return g_slist_append(list, dict);
1519 static void list_dict_cb(const char * const lang_tag,
1520 const char * const provider_name,
1521 const char * const provider_desc,
1522 const char * const provider_file,
1525 GSList **list = (GSList **)data;
1526 Dictionary *dict = g_new0(Dictionary, 1);
1527 dict->fullname = g_strdup(lang_tag);
1528 dict->dictname = g_strdup(lang_tag);
1530 if (g_slist_find_custom(*list, dict,
1531 (GCompareFunc) compare_dict) == NULL) {
1532 debug_print("Aspell: found dictionary %s %s\n", dict->fullname,
1534 *list = g_slist_insert_sorted(*list, dict,
1535 (GCompareFunc) compare_dict);
1537 dictionary_delete(dict);
1541 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1542 static GSList *gtkaspell_get_dictionary_list(gint refresh)
1545 EnchantBroker *broker;
1547 if (!gtkaspellcheckers)
1548 gtkaspell_checkers_init();
1550 if (gtkaspellcheckers->dictionary_list && !refresh)
1551 return gtkaspellcheckers->dictionary_list;
1553 gtkaspell_free_dictionary_list(
1554 gtkaspellcheckers->dictionary_list);
1557 broker = enchant_broker_init();
1559 enchant_broker_list_dicts(broker, list_dict_cb, &list);
1561 enchant_broker_free(broker);
1565 debug_print("Aspell: error when searching for dictionaries: "
1566 "No dictionary found.\n");
1567 list = create_empty_dictionary_list();
1570 gtkaspellcheckers->dictionary_list = list;
1575 static void gtkaspell_free_dictionary_list(GSList *list)
1579 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1581 dict = (Dictionary *) walk->data;
1582 dictionary_delete(dict);
1587 GtkTreeModel *gtkaspell_dictionary_store_new_with_refresh(gboolean refresh)
1589 GSList *dict_list, *tmp;
1590 GtkListStore *store;
1594 dict_list = gtkaspell_get_dictionary_list(refresh);
1595 cm_return_val_if_fail(dict_list, NULL);
1597 store = gtk_list_store_new(SET_GTKASPELL_SIZE,
1602 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1603 dict = (Dictionary *) tmp->data;
1605 gtk_list_store_append(store, &iter);
1606 gtk_list_store_set(store, &iter,
1607 SET_GTKASPELL_NAME, dict->dictname,
1608 SET_GTKASPELL_FULLNAME, dict->fullname,
1612 return GTK_TREE_MODEL(store);
1615 GtkTreeModel *gtkaspell_dictionary_store_new(void)
1617 return gtkaspell_dictionary_store_new_with_refresh
1621 GtkWidget *gtkaspell_dictionary_combo_new(const gboolean refresh)
1624 GtkCellRenderer *renderer;
1626 combo = gtk_combo_box_new_with_model(
1627 gtkaspell_dictionary_store_new_with_refresh(refresh));
1628 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1629 gtk_widget_show(combo);
1631 renderer = gtk_cell_renderer_text_new();
1632 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
1633 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),renderer,
1634 "text", SET_GTKASPELL_NAME, NULL);
1639 gchar *gtkaspell_get_dictionary_menu_active_item(GtkComboBox *combo)
1642 GtkTreeModel *model;
1643 gchar *dict_fullname = NULL;
1645 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo), NULL);
1646 cm_return_val_if_fail(gtk_combo_box_get_active_iter(combo, &iter), NULL);
1648 model = gtk_combo_box_get_model(combo);
1652 gtk_tree_model_get(model, &iter,
1653 SET_GTKASPELL_FULLNAME, &dict_fullname,
1656 return dict_fullname;
1659 gint gtkaspell_set_dictionary_menu_active_item(GtkComboBox *combo,
1660 const gchar *dictionary)
1662 GtkTreeModel *model;
1664 gchar *dict_name = NULL;
1666 cm_return_val_if_fail(combo != NULL, 0);
1667 cm_return_val_if_fail(dictionary != NULL, 0);
1668 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo), 0);
1670 if((model = gtk_combo_box_get_model(combo)) == NULL)
1672 if((gtk_tree_model_get_iter_first(model, &iter)) == FALSE)
1676 gtk_tree_model_get(model, &iter,
1677 SET_GTKASPELL_FULLNAME, &dict_name,
1680 if ((dict_name != NULL) && !strcmp2(dict_name, dictionary)) {
1681 gtk_combo_box_set_active_iter(combo, &iter);
1688 } while ((gtk_tree_model_iter_next(model, &iter)) == TRUE);
1693 void gtkaspell_use_alternate_dict(GtkAspell *gtkaspell)
1697 tmp = gtkaspell->gtkaspeller;
1698 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1699 gtkaspell->alternate_speller = tmp;
1702 static void destroy_menu(GtkWidget *widget,
1703 gpointer user_data) {
1704 GtkAspell *gtkaspell = (GtkAspell *)user_data;
1706 if (gtkaspell->accel_group) {
1707 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1708 gtkaspell->accel_group);
1709 gtkaspell->accel_group = NULL;
1713 static gboolean aspell_key_pressed(GtkWidget *widget,
1715 GtkAspell *gtkaspell)
1717 if (event && (isascii(event->keyval) || event->keyval == GDK_Return)) {
1718 gtk_accel_groups_activate(
1719 G_OBJECT(gtkaspell->parent_window),
1720 event->keyval, event->state);
1721 } else if (event && event->keyval == GDK_Escape) {
1722 destroy_menu(NULL, gtkaspell);
1727 /* make_sug_menu() - Add menus to accept this word for this session
1728 * and to add it to personal dictionary
1730 static GSList *make_sug_menu(GtkAspell *gtkaspell)
1734 GtkTextView *gtktext;
1735 GtkAccelGroup *accel;
1736 GList *l = gtkaspell->suggestions_list;
1738 GSList *list = NULL;
1739 gtktext = gtkaspell->gtktext;
1744 accel = gtk_accel_group_new();
1746 if (gtkaspell->accel_group) {
1747 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1748 gtkaspell->accel_group);
1749 gtkaspell->accel_group = NULL;
1752 utf8buf = g_strdup(l->data);
1753 caption = g_strdup_printf(_("\"%s\" unknown in %s"),
1755 gtkaspell->gtkaspeller->dictionary->dictname);
1756 item = gtk_menu_item_new_with_label(caption);
1758 gtk_widget_show(item);
1759 list = g_slist_append(list, item);
1760 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
1763 item = gtk_menu_item_new();
1764 gtk_widget_show(item);
1765 list = g_slist_append(list, item);
1767 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1768 gtk_widget_show(item);
1769 list = g_slist_append(list, item);
1770 g_signal_connect(G_OBJECT(item), "activate",
1771 G_CALLBACK(add_word_to_session_cb),
1773 gtk_widget_add_accelerator(item, "activate", accel, GDK_space,
1775 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1777 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1778 gtk_widget_show(item);
1779 list = g_slist_append(list, item);
1780 g_signal_connect(G_OBJECT(item), "activate",
1781 G_CALLBACK(add_word_to_personal_cb),
1783 gtk_widget_add_accelerator(item, "activate", accel, GDK_Return,
1785 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1787 item = gtk_menu_item_new_with_label(_("Replace with..."));
1788 gtk_widget_show(item);
1789 list = g_slist_append(list, item);
1790 g_signal_connect(G_OBJECT(item), "activate",
1791 G_CALLBACK(replace_with_create_dialog_cb),
1793 gtk_widget_add_accelerator(item, "activate", accel, GDK_R, 0,
1794 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1795 gtk_widget_add_accelerator(item, "activate", accel, GDK_R,
1799 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1800 caption = g_strdup_printf(_("Check with %s"),
1801 gtkaspell->alternate_speller->dictionary->dictname);
1802 item = gtk_menu_item_new_with_label(caption);
1804 gtk_widget_show(item);
1805 list = g_slist_append(list, item);
1806 g_signal_connect(G_OBJECT(item), "activate",
1807 G_CALLBACK(check_with_alternate_cb),
1809 gtk_widget_add_accelerator(item, "activate", accel, GDK_X, 0,
1810 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1811 gtk_widget_add_accelerator(item, "activate", accel, GDK_X,
1816 item = gtk_menu_item_new();
1817 gtk_widget_show(item);
1818 list = g_slist_append(list, item);
1822 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
1823 gtk_widget_show(item);
1824 list = g_slist_append(list, item);
1826 GtkWidget *curmenu = NULL;
1830 if (count == MENUCOUNT) {
1833 item = gtk_menu_item_new_with_label(_("More..."));
1834 gtk_widget_show(item);
1836 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1838 list = g_slist_append(list, item);
1840 curmenu = gtk_menu_new();
1841 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1845 utf8buf = g_strdup(l->data);
1847 item = gtk_menu_item_new_with_label(utf8buf);
1849 gtk_widget_show(item);
1850 if (curmenu == NULL) {
1851 list = g_slist_append(list, item);
1853 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1855 g_signal_connect(G_OBJECT(item), "activate",
1856 G_CALLBACK(replace_word_cb),
1859 if (curmenu == NULL && count < MENUCOUNT) {
1860 gtk_widget_add_accelerator(item, "activate",
1865 gtk_widget_add_accelerator(item, "activate",
1874 } while ((l = l->next) != NULL);
1877 gtk_window_add_accel_group
1878 (GTK_WINDOW(gtkaspell->parent_window),
1880 gtkaspell->accel_group = accel;
1885 static GSList *populate_submenu(GtkAspell *gtkaspell)
1887 GtkWidget *item, *submenu;
1889 GtkAspeller *gtkaspeller = NULL;
1890 GSList *list = NULL;
1895 gtkaspeller = gtkaspell->gtkaspeller;
1896 dictname = g_strdup_printf(_("Dictionary: %s"),
1897 gtkaspeller->dictionary->dictname);
1898 item = gtk_menu_item_new_with_label(dictname);
1899 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
1901 gtk_widget_show(item);
1902 list = g_slist_append(list, item);
1904 item = gtk_menu_item_new();
1905 gtk_widget_show(item);
1906 list = g_slist_append(list, item);
1908 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1909 dictname = g_strdup_printf(_("Use alternate (%s)"),
1910 gtkaspell->alternate_speller->dictionary->dictname);
1911 item = gtk_menu_item_new_with_label(dictname);
1913 g_signal_connect(G_OBJECT(item), "activate",
1914 G_CALLBACK(switch_to_alternate_cb),
1916 gtk_widget_show(item);
1917 list = g_slist_append(list, item);
1920 item = gtk_check_menu_item_new_with_label(_("Use both dictionaries"));
1921 if (gtkaspell->use_both_dicts) {
1922 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1924 g_signal_connect(G_OBJECT(item), "activate",
1925 G_CALLBACK(set_use_both_cb),
1927 gtk_widget_show(item);
1928 list = g_slist_append(list, item);
1930 item = gtk_menu_item_new();
1931 gtk_widget_show(item);
1932 list = g_slist_append(list, item);
1934 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
1935 if (gtkaspell->check_while_typing)
1936 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1938 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1939 g_signal_connect(G_OBJECT(item), "activate",
1940 G_CALLBACK(toggle_check_while_typing_cb),
1942 gtk_widget_show(item);
1943 list = g_slist_append(list, item);
1945 item = gtk_menu_item_new();
1946 gtk_widget_show(item);
1947 list = g_slist_append(list, item);
1949 submenu = gtk_menu_new();
1950 item = gtk_menu_item_new_with_label(_("Change dictionary"));
1951 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
1952 gtk_widget_show(item);
1953 list = g_slist_append(list, item);
1956 if (gtkaspellcheckers->dictionary_list == NULL)
1957 gtkaspell_get_dictionary_list(FALSE);
1959 GtkWidget * curmenu = submenu;
1963 tmp = gtkaspellcheckers->dictionary_list;
1965 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
1966 tmp = g_slist_next(tmp)) {
1967 if (count == MENUCOUNT) {
1970 newmenu = gtk_menu_new();
1971 item = gtk_menu_item_new_with_label(_("More..."));
1972 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1975 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1976 gtk_widget_show(item);
1980 dict = (Dictionary *) tmp->data;
1981 item = gtk_check_menu_item_new_with_label(dict->dictname);
1982 g_object_set_data(G_OBJECT(item), "dict_name",
1984 if (strcmp2(dict->fullname,
1985 gtkaspell->gtkaspeller->dictionary->fullname))
1986 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1988 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1989 gtk_widget_set_sensitive(GTK_WIDGET(item),
1992 g_signal_connect(G_OBJECT(item), "activate",
1993 G_CALLBACK(change_dict_cb),
1995 gtk_widget_show(item);
1996 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
2004 GSList *gtkaspell_make_config_menu(GtkAspell *gtkaspell)
2006 return populate_submenu(gtkaspell);
2009 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y,
2010 gboolean *push_in, gpointer data)
2012 GtkAspell *gtkaspell = (GtkAspell *) data;
2013 gint xx = 0, yy = 0;
2016 GtkTextView *text = GTK_TEXT_VIEW(gtkaspell->gtktext);
2017 GtkTextBuffer *textbuf;
2022 textbuf = gtk_text_view_get_buffer(gtkaspell->gtktext);
2023 gtk_text_buffer_get_iter_at_mark(textbuf, &iter,
2024 gtk_text_buffer_get_insert(textbuf));
2025 gtk_text_view_get_iter_location(gtkaspell->gtktext, &iter, &rect);
2026 gtk_text_view_buffer_to_window_coords(text, GTK_TEXT_WINDOW_TEXT,
2030 gdk_window_get_origin(GTK_WIDGET(gtkaspell->gtktext)->window, &xx, &yy);
2032 sx = gdk_screen_width();
2033 sy = gdk_screen_height();
2035 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2040 *x = rect.x + xx + 8;
2042 *y = rect.y + rect.height + yy;
2050 /* change the current dictionary of gtkaspell
2051 - if always_set_alt_dict is set, the alternate dict is unconditionally set to the former
2052 current dictionary (common use: from menu callbacks)
2053 - if always_set_alt_dict is NOT set, the alternate dict will be set to the former
2054 current dictionary only if there is no alternate dictionary already set
2055 (this is when we need to set the current dictionary then the alternate one
2056 when creating a compose window, from the account and folder settings)
2058 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary,
2059 gboolean always_set_alt_dict)
2062 GtkAspeller *gtkaspeller;
2064 cm_return_val_if_fail(gtkaspell, FALSE);
2065 cm_return_val_if_fail(dictionary, FALSE);
2067 dict = g_new0(Dictionary, 1);
2069 if (strrchr(dictionary, '/')) {
2070 dict->fullname = g_strdup(strrchr(dictionary, '/')+1);
2071 dict->dictname = g_strdup(strrchr(dictionary, '/')+1);
2073 dict->fullname = g_strdup(dictionary);
2074 dict->dictname = g_strdup(dictionary);
2077 if (dict->fullname && strchr(dict->fullname, '-')) {
2078 *(strchr(dict->fullname, '-')) = '\0';
2079 *(strchr(dict->dictname, '-')) = '\0';
2082 if (!dict->fullname || !(*dict->fullname)) {
2083 dictionary_delete(dict);
2086 gtkaspeller = gtkaspeller_new(dict);
2089 alertpanel_warning(_("The spell checker could not change dictionary.\n%s"),
2090 gtkaspellcheckers->error_message);
2092 if (gtkaspell->use_alternate) {
2093 if (gtkaspell->alternate_speller) {
2094 if (always_set_alt_dict) {
2095 gtkaspeller_delete(gtkaspell->alternate_speller);
2096 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2098 gtkaspeller_delete(gtkaspell->gtkaspeller);
2100 /* should never be reached as the dicts are always set
2101 to a default value */
2102 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2104 gtkaspeller_delete(gtkaspell->gtkaspeller);
2106 gtkaspell->gtkaspeller = gtkaspeller;
2109 dictionary_delete(dict);
2114 /* change the alternate dictionary of gtkaspell (doesn't affect the default dictionary) */
2115 gboolean gtkaspell_change_alt_dict(GtkAspell *gtkaspell, const gchar *alt_dictionary)
2118 GtkAspeller *gtkaspeller;
2120 cm_return_val_if_fail(gtkaspell, FALSE);
2121 cm_return_val_if_fail(alt_dictionary, FALSE);
2123 dict = g_new0(Dictionary, 1);
2124 if (strrchr(alt_dictionary, '/')) {
2125 dict->fullname = g_strdup(strrchr(alt_dictionary, '/')+1);
2126 dict->dictname = g_strdup(strrchr(alt_dictionary, '/')+1);
2128 dict->fullname = g_strdup(alt_dictionary);
2129 dict->dictname = g_strdup(alt_dictionary);
2132 if (dict->fullname && strchr(dict->fullname, '-')) {
2133 *(strchr(dict->fullname, '-')) = '\0';
2134 *(strchr(dict->dictname, '-')) = '\0';
2137 if (!dict->fullname || !(*dict->fullname)) {
2138 dictionary_delete(dict);
2142 gtkaspeller = gtkaspeller_new(dict);
2145 alertpanel_warning(_("The spell checker could not change the alternate dictionary.\n%s"),
2146 gtkaspellcheckers->error_message);
2148 if (gtkaspell->alternate_speller)
2149 gtkaspeller_delete(gtkaspell->alternate_speller);
2150 gtkaspell->alternate_speller = gtkaspeller;
2153 dictionary_delete(dict);
2158 /* Menu call backs */
2160 /* change_dict_cb() - Menu callback : change dict */
2161 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2165 fullname = (gchar *) g_object_get_data(G_OBJECT(w), "dict_name");
2167 if (!strcmp2(fullname, _("None")))
2170 gtkaspell_change_dict(gtkaspell, fullname, TRUE);
2171 gtkaspell_dict_changed(gtkaspell);
2173 if (gtkaspell->menu_changed_cb)
2174 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2177 static void switch_to_alternate_cb(GtkWidget *w,
2180 GtkAspell *gtkaspell = (GtkAspell *) data;
2181 gtkaspell_use_alternate_dict(gtkaspell);
2182 gtkaspell_dict_changed(gtkaspell);
2184 if (gtkaspell->menu_changed_cb)
2185 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2188 /* Misc. helper functions */
2190 static void set_point_continue(GtkAspell *gtkaspell)
2192 GtkTextView *gtktext;
2194 gtktext = gtkaspell->gtktext;
2196 gtkaspell->ctx.set_position(gtkaspell->ctx.data, gtkaspell->orig_pos);
2198 if (gtkaspell->continue_check)
2199 gtkaspell->continue_check((gpointer *) gtkaspell->ctx.data);
2202 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2204 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtkaspell->gtktext);
2205 GdkColor *color = &(gtkaspell->highlight);
2207 /* Shameless copy from Sylpheed's gtkutils.c */
2209 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2211 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2213 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2217 gtk_text_buffer_create_tag(buffer, "misspelled",
2218 "foreground-gdk", color, NULL);
2220 gtk_text_buffer_create_tag(buffer, "misspelled",
2221 "underline", PANGO_UNDERLINE_ERROR, NULL);
2225 static void change_color(GtkAspell * gtkaspell,
2226 gint start, gint end,
2230 GtkTextView *gtktext;
2231 GtkTextBuffer *buffer;
2232 GtkTextIter startiter, enditer;
2237 gtktext = gtkaspell->gtktext;
2239 buffer = gtk_text_view_get_buffer(gtktext);
2240 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, start);
2241 gtk_text_buffer_get_iter_at_offset(buffer, &enditer, end);
2243 gtk_text_buffer_apply_tag_by_name(buffer, "misspelled",
2244 &startiter, &enditer);
2246 gtk_text_iter_forward_char(&enditer);
2247 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
2248 &startiter, &enditer);
2252 /* compare_dict () - compare 2 dict names */
2253 static gint compare_dict(Dictionary *a, Dictionary *b)
2255 guint aparts = 0, bparts = 0;
2258 for (i=0; i < strlen(a->dictname); i++)
2259 if (a->dictname[i] == '-')
2261 for (i=0; i < strlen(b->dictname); i++)
2262 if (b->dictname[i] == '-')
2265 if (aparts != bparts)
2266 return (aparts < bparts) ? -1 : +1;
2269 compare = strcmp2(a->dictname, b->dictname);
2271 compare = strcmp2(a->fullname, b->fullname);
2276 static void dictionary_delete(Dictionary *dict)
2278 g_free(dict->fullname);
2279 g_free(dict->dictname);
2283 static Dictionary *dictionary_dup(const Dictionary *dict)
2287 dict2 = g_new(Dictionary, 1);
2289 dict2->fullname = g_strdup(dict->fullname);
2290 dict2->dictname = g_strdup(dict->dictname);
2295 void gtkaspell_free_suggestions_list(GtkAspell *gtkaspell)
2299 for (list = gtkaspell->suggestions_list; list != NULL;
2303 g_list_free(gtkaspell->suggestions_list);
2305 gtkaspell->max_sug = -1;
2306 gtkaspell->suggestions_list = NULL;
2309 static void reset_theword_data(GtkAspell *gtkaspell)
2311 gtkaspell->start_pos = 0;
2312 gtkaspell->end_pos = 0;
2313 gtkaspell->theword[0] = 0;
2314 gtkaspell->max_sug = -1;
2316 gtkaspell_free_suggestions_list(gtkaspell);
2319 static void free_checkers(gpointer elt, gpointer data)
2321 GtkAspeller *gtkaspeller = elt;
2323 cm_return_if_fail(gtkaspeller);
2325 gtkaspeller_real_delete(gtkaspeller);
2328 static gint find_gtkaspeller(gconstpointer aa, gconstpointer bb)
2330 Dictionary *a = ((GtkAspeller *) aa)->dictionary;
2331 Dictionary *b = ((GtkAspeller *) bb)->dictionary;
2333 if (a && b && a->fullname && b->fullname)
2334 return strcmp(a->fullname, b->fullname);
2339 gchar *gtkaspell_get_default_dictionary(GtkAspell *gtkaspell)
2341 if (gtkaspell && gtkaspell->gtkaspeller &&
2342 gtkaspell->gtkaspeller->dictionary)
2343 return gtkaspell->gtkaspeller->dictionary->dictname;