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 void gtkaspell_block_check(GtkAspell *gtkaspell)
1193 GtkTextView *gtktext = gtkaspell->gtktext;
1194 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1195 G_CALLBACK(entry_insert_cb),
1197 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1198 G_CALLBACK(entry_delete_cb),
1202 void gtkaspell_unblock_check(GtkAspell *gtkaspell)
1204 GtkTextView *gtktext = gtkaspell->gtktext;
1205 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1206 G_CALLBACK(entry_insert_cb),
1208 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1209 G_CALLBACK(entry_delete_cb),
1213 static void replace_real_word(GtkAspell *gtkaspell, const gchar *newword)
1215 int oldlen, newlen, wordlen;
1218 GtkTextView *gtktext;
1219 GtkTextBuffer *textbuf;
1220 GtkTextIter startiter, enditer;
1222 if (!newword) return;
1224 gtktext = gtkaspell->gtktext;
1225 textbuf = gtk_text_view_get_buffer(gtktext);
1227 origpos = gtkaspell->orig_pos;
1229 oldlen = gtkaspell->end_pos - gtkaspell->start_pos;
1230 wordlen = strlen(gtkaspell->theword);
1232 newlen = strlen(newword); /* FIXME: multybyte characters? */
1234 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1235 G_CALLBACK(entry_insert_cb),
1237 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1238 G_CALLBACK(entry_delete_cb),
1241 gtk_text_buffer_get_iter_at_offset(textbuf, &startiter,
1242 gtkaspell->start_pos);
1243 gtk_text_buffer_get_iter_at_offset(textbuf, &enditer,
1244 gtkaspell->end_pos);
1245 g_signal_emit_by_name(G_OBJECT(textbuf), "delete-range",
1246 &startiter, &enditer, gtkaspell);
1247 g_signal_emit_by_name(G_OBJECT(textbuf), "insert-text",
1248 &startiter, newword, newlen, gtkaspell);
1250 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1251 G_CALLBACK(entry_insert_cb),
1253 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1254 G_CALLBACK(entry_delete_cb),
1257 /* Put the point and the position where we clicked with the mouse
1258 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1259 * to let it update correctly the word insertion and then the
1260 * point & position position. If not, SEGV after the first replacement
1261 * If the new word ends before point, put the point at its end.
1264 if (origpos - gtkaspell->start_pos < oldlen &&
1265 origpos - gtkaspell->start_pos >= 0) {
1266 /* Original point was in the word.
1267 * Let it there unless point is going to be outside of the word
1269 if (origpos - gtkaspell->start_pos >= newlen) {
1270 pos = gtkaspell->start_pos + newlen;
1273 else if (origpos >= gtkaspell->end_pos) {
1274 /* move the position according to the change of length */
1275 pos = origpos + newlen - oldlen;
1278 gtkaspell->end_pos = gtkaspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1280 if (get_textview_buffer_charcount(gtktext) < pos)
1281 pos = get_textview_buffer_charcount(gtktext);
1282 gtkaspell->orig_pos = pos;
1284 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1287 static void replace_real_word_cb(gpointer data, const gchar *newword)
1289 replace_real_word((GtkAspell *)data, newword);
1292 /* Accept this word for this session */
1293 static void add_word_to_session_cb(GtkWidget *w, gpointer data)
1295 GtkTextView *gtktext;
1296 GtkAspell *gtkaspell = (GtkAspell *) data;
1297 gtktext = gtkaspell->gtktext;
1300 enchant_dict_add_to_session(gtkaspell->gtkaspeller->speller, gtkaspell->theword, strlen(gtkaspell->theword));
1302 gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1303 gtkaspell_dict_changed(gtkaspell);
1305 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1307 set_point_continue(gtkaspell);
1310 /* add_word_to_personal_cb() - add word to personal dict. */
1311 static void add_word_to_personal_cb(GtkWidget *w, gpointer data)
1313 GtkAspell *gtkaspell = (GtkAspell *) data;
1315 enchant_dict_add_to_pwl(gtkaspell->gtkaspeller->speller, gtkaspell->theword, strlen(gtkaspell->theword));
1317 gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1318 gtkaspell_dict_changed(gtkaspell);
1320 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1321 set_point_continue(gtkaspell);
1324 static void check_with_alternate_cb(GtkWidget *w, gpointer data)
1326 GtkAspell *gtkaspell = (GtkAspell *)data;
1329 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1331 gtkaspell_use_alternate_dict(gtkaspell);
1332 misspelled = gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1334 if (!gtkaspell->continue_check) {
1336 gtkaspell->misspelled = misspelled;
1338 if (gtkaspell->misspelled) {
1341 misspelled_suggest(gtkaspell, gtkaspell->theword);
1343 gtkaspell->ctx.set_position(gtkaspell->ctx.data,
1344 gtkaspell->end_pos);
1346 list = make_sug_menu(gtkaspell);
1347 menu = gtk_menu_new();
1348 for (cur = list; cur; cur = cur->next)
1349 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(cur->data));
1351 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1352 gtkaspell->ctx.set_menu_pos,
1353 gtkaspell->ctx.data, 0,
1355 g_signal_connect(G_OBJECT(menu), "deactivate",
1356 G_CALLBACK(destroy_menu),
1358 g_signal_connect(G_OBJECT(menu),
1360 G_CALLBACK(aspell_key_pressed),
1365 gtkaspell->orig_pos = gtkaspell->start_pos;
1367 set_point_continue(gtkaspell);
1370 static gboolean replace_key_pressed(GtkWidget *widget,
1372 GtkAspell *gtkaspell)
1374 if (event && event->keyval == GDK_KEY_Escape) {
1375 gtk_widget_destroy(widget);
1377 } else if (event && event->keyval == GDK_KEY_Return) {
1378 replace_with_supplied_word_cb(widget, gtkaspell);
1384 static void replace_with_create_dialog_cb(GtkWidget *w, gpointer data)
1386 static PangoFontDescription *font_desc;
1393 GtkWidget *ok_button;
1394 GtkWidget *cancel_button;
1395 GtkWidget *confirm_area;
1397 gchar *utf8buf, *thelabel;
1399 GtkAspell *gtkaspell = (GtkAspell *) data;
1401 gdk_window_get_origin((GTK_WIDGET(w)->parent)->window, &xx, &yy);
1403 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1405 dialog = gtk_dialog_new();
1407 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1408 gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1409 gtk_window_move(GTK_WINDOW(dialog), xx, yy);
1411 g_signal_connect_swapped(G_OBJECT(dialog), "destroy",
1412 G_CALLBACK(gtk_widget_destroy),
1415 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
1416 hbox = gtk_hbox_new (FALSE, 12);
1417 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1418 gtk_widget_show (hbox);
1419 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
1422 utf8buf = g_strdup(gtkaspell->theword);
1424 thelabel = g_strdup_printf(_("<span weight=\"bold\" "
1425 "size=\"larger\">Replace \"%s\" with: </span>"),
1427 /* for title label */
1428 w_hbox = gtk_hbox_new(FALSE, 0);
1430 icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
1431 GTK_ICON_SIZE_DIALOG);
1432 gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
1433 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
1435 vbox = gtk_vbox_new (FALSE, 12);
1436 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1437 gtk_widget_show (vbox);
1439 label = gtk_label_new(thelabel);
1440 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1441 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1442 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1443 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1444 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1448 size = pango_font_description_get_size
1449 (label->style->font_desc);
1450 font_desc = pango_font_description_new();
1451 pango_font_description_set_weight
1452 (font_desc, PANGO_WEIGHT_BOLD);
1453 pango_font_description_set_size
1454 (font_desc, size * PANGO_SCALE_LARGE);
1457 gtk_widget_modify_font(label, font_desc);
1460 entry = gtk_entry_new();
1461 gtkaspell->replace_entry = entry;
1462 gtk_entry_set_text(GTK_ENTRY(entry), utf8buf);
1463 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1464 g_signal_connect(G_OBJECT(dialog),
1466 G_CALLBACK(replace_key_pressed), gtkaspell);
1467 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
1470 label = gtk_label_new(_("Holding down Control key while pressing "
1471 "Enter\nwill learn from mistake.\n"));
1472 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1473 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1474 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1475 gtk_widget_show(label);
1477 hbox = gtk_hbox_new(TRUE, 0);
1479 gtkut_stock_button_set_create(&confirm_area,
1480 &cancel_button, GTK_STOCK_CANCEL,
1481 &ok_button, GTK_STOCK_OK,
1484 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1485 confirm_area, FALSE, FALSE, 0);
1486 gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
1488 g_signal_connect(G_OBJECT(ok_button), "clicked",
1489 G_CALLBACK(replace_with_supplied_word_cb),
1491 g_signal_connect_swapped(G_OBJECT(ok_button), "clicked",
1492 G_CALLBACK(gtk_widget_destroy),
1495 g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked",
1496 G_CALLBACK(gtk_widget_destroy),
1499 gtk_widget_grab_focus(entry);
1501 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1503 gtk_widget_show_all(dialog);
1506 static void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1508 GtkTextView *gtktext;
1509 GtkTextBuffer *buffer;
1510 GtkTextIter startiter, enditer;
1512 gtktext = gtkaspell->gtktext;
1514 buffer = gtk_text_view_get_buffer(gtktext);
1515 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, 0);
1516 gtk_text_buffer_get_iter_at_offset(buffer, &enditer,
1517 get_textview_buffer_charcount(gtktext)-1);
1518 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
1519 &startiter, &enditer);
1522 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1524 GtkAspell *gtkaspell = (GtkAspell *) data;
1526 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1528 if (!gtkaspell->check_while_typing)
1529 gtkaspell_uncheck_all(gtkaspell);
1530 if (gtkaspell->menu_changed_cb)
1531 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
1534 static GSList *create_empty_dictionary_list(void)
1536 GSList *list = NULL;
1539 dict = g_new0(Dictionary, 1);
1540 dict->fullname = g_strdup(_("None"));
1541 dict->dictname = NULL;
1543 return g_slist_append(list, dict);
1546 static void list_dict_cb(const char * const lang_tag,
1547 const char * const provider_name,
1548 const char * const provider_desc,
1549 const char * const provider_file,
1552 GSList **list = (GSList **)data;
1553 Dictionary *dict = g_new0(Dictionary, 1);
1554 dict->fullname = g_strdup(lang_tag);
1555 dict->dictname = g_strdup(lang_tag);
1557 if (g_slist_find_custom(*list, dict,
1558 (GCompareFunc) compare_dict) == NULL) {
1559 debug_print("Aspell: found dictionary %s %s\n", dict->fullname,
1561 *list = g_slist_insert_sorted(*list, dict,
1562 (GCompareFunc) compare_dict);
1564 dictionary_delete(dict);
1568 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1569 static GSList *gtkaspell_get_dictionary_list(gint refresh)
1572 EnchantBroker *broker;
1574 if (!gtkaspellcheckers)
1575 gtkaspell_checkers_init();
1577 if (gtkaspellcheckers->dictionary_list && !refresh)
1578 return gtkaspellcheckers->dictionary_list;
1580 gtkaspell_free_dictionary_list(
1581 gtkaspellcheckers->dictionary_list);
1584 broker = enchant_broker_init();
1586 enchant_broker_list_dicts(broker, list_dict_cb, &list);
1588 enchant_broker_free(broker);
1592 debug_print("Aspell: error when searching for dictionaries: "
1593 "No dictionary found.\n");
1594 list = create_empty_dictionary_list();
1597 gtkaspellcheckers->dictionary_list = list;
1602 static void gtkaspell_free_dictionary_list(GSList *list)
1606 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1608 dict = (Dictionary *) walk->data;
1609 dictionary_delete(dict);
1614 GtkTreeModel *gtkaspell_dictionary_store_new_with_refresh(gboolean refresh)
1616 GSList *dict_list, *tmp;
1617 GtkListStore *store;
1621 dict_list = gtkaspell_get_dictionary_list(refresh);
1622 cm_return_val_if_fail(dict_list, NULL);
1624 store = gtk_list_store_new(SET_GTKASPELL_SIZE,
1629 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1630 dict = (Dictionary *) tmp->data;
1632 gtk_list_store_append(store, &iter);
1633 gtk_list_store_set(store, &iter,
1634 SET_GTKASPELL_NAME, dict->dictname,
1635 SET_GTKASPELL_FULLNAME, dict->fullname,
1639 return GTK_TREE_MODEL(store);
1642 GtkTreeModel *gtkaspell_dictionary_store_new(void)
1644 return gtkaspell_dictionary_store_new_with_refresh
1648 GtkWidget *gtkaspell_dictionary_combo_new(const gboolean refresh)
1651 GtkCellRenderer *renderer;
1653 combo = gtk_combo_box_new_with_model(
1654 gtkaspell_dictionary_store_new_with_refresh(refresh));
1655 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1656 gtk_widget_show(combo);
1658 renderer = gtk_cell_renderer_text_new();
1659 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
1660 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),renderer,
1661 "text", SET_GTKASPELL_NAME, NULL);
1666 gchar *gtkaspell_get_dictionary_menu_active_item(GtkComboBox *combo)
1669 GtkTreeModel *model;
1670 gchar *dict_fullname = NULL;
1672 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo), NULL);
1673 cm_return_val_if_fail(gtk_combo_box_get_active_iter(combo, &iter), NULL);
1675 model = gtk_combo_box_get_model(combo);
1679 gtk_tree_model_get(model, &iter,
1680 SET_GTKASPELL_FULLNAME, &dict_fullname,
1683 return dict_fullname;
1686 gint gtkaspell_set_dictionary_menu_active_item(GtkComboBox *combo,
1687 const gchar *dictionary)
1689 GtkTreeModel *model;
1691 gchar *dict_name = NULL;
1693 cm_return_val_if_fail(combo != NULL, 0);
1694 cm_return_val_if_fail(dictionary != NULL, 0);
1695 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo), 0);
1697 if((model = gtk_combo_box_get_model(combo)) == NULL)
1699 if((gtk_tree_model_get_iter_first(model, &iter)) == FALSE)
1703 gtk_tree_model_get(model, &iter,
1704 SET_GTKASPELL_FULLNAME, &dict_name,
1707 if ((dict_name != NULL) && !strcmp2(dict_name, dictionary)) {
1708 gtk_combo_box_set_active_iter(combo, &iter);
1715 } while ((gtk_tree_model_iter_next(model, &iter)) == TRUE);
1720 void gtkaspell_use_alternate_dict(GtkAspell *gtkaspell)
1724 tmp = gtkaspell->gtkaspeller;
1725 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1726 gtkaspell->alternate_speller = tmp;
1729 static void destroy_menu(GtkWidget *widget,
1730 gpointer user_data) {
1731 GtkAspell *gtkaspell = (GtkAspell *)user_data;
1733 if (gtkaspell->accel_group) {
1734 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1735 gtkaspell->accel_group);
1736 gtkaspell->accel_group = NULL;
1740 static gboolean aspell_key_pressed(GtkWidget *widget,
1742 GtkAspell *gtkaspell)
1744 if (event && (isascii(event->keyval) || event->keyval == GDK_KEY_Return)) {
1745 gtk_accel_groups_activate(
1746 G_OBJECT(gtkaspell->parent_window),
1747 event->keyval, event->state);
1748 } else if (event && event->keyval == GDK_KEY_Escape) {
1749 destroy_menu(NULL, gtkaspell);
1754 /* make_sug_menu() - Add menus to accept this word for this session
1755 * and to add it to personal dictionary
1757 static GSList *make_sug_menu(GtkAspell *gtkaspell)
1761 GtkTextView *gtktext;
1762 GtkAccelGroup *accel;
1763 GList *l = gtkaspell->suggestions_list;
1765 GSList *list = NULL;
1766 gtktext = gtkaspell->gtktext;
1771 accel = gtk_accel_group_new();
1773 if (gtkaspell->accel_group) {
1774 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1775 gtkaspell->accel_group);
1776 gtkaspell->accel_group = NULL;
1779 utf8buf = g_strdup(l->data);
1780 caption = g_strdup_printf(_("\"%s\" unknown in %s"),
1782 gtkaspell->gtkaspeller->dictionary->dictname);
1783 item = gtk_menu_item_new_with_label(caption);
1785 gtk_widget_show(item);
1786 list = g_slist_append(list, item);
1787 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
1790 item = gtk_menu_item_new();
1791 gtk_widget_show(item);
1792 list = g_slist_append(list, item);
1794 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1795 gtk_widget_show(item);
1796 list = g_slist_append(list, item);
1797 g_signal_connect(G_OBJECT(item), "activate",
1798 G_CALLBACK(add_word_to_session_cb),
1800 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_space,
1802 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1804 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1805 gtk_widget_show(item);
1806 list = g_slist_append(list, item);
1807 g_signal_connect(G_OBJECT(item), "activate",
1808 G_CALLBACK(add_word_to_personal_cb),
1810 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_Return,
1812 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1814 item = gtk_menu_item_new_with_label(_("Replace with..."));
1815 gtk_widget_show(item);
1816 list = g_slist_append(list, item);
1817 g_signal_connect(G_OBJECT(item), "activate",
1818 G_CALLBACK(replace_with_create_dialog_cb),
1820 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_R, 0,
1821 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1822 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_R,
1826 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1827 caption = g_strdup_printf(_("Check with %s"),
1828 gtkaspell->alternate_speller->dictionary->dictname);
1829 item = gtk_menu_item_new_with_label(caption);
1831 gtk_widget_show(item);
1832 list = g_slist_append(list, item);
1833 g_signal_connect(G_OBJECT(item), "activate",
1834 G_CALLBACK(check_with_alternate_cb),
1836 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_X, 0,
1837 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1838 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_X,
1843 item = gtk_menu_item_new();
1844 gtk_widget_show(item);
1845 list = g_slist_append(list, item);
1849 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
1850 gtk_widget_show(item);
1851 list = g_slist_append(list, item);
1853 GtkWidget *curmenu = NULL;
1857 if (count == MENUCOUNT) {
1860 item = gtk_menu_item_new_with_label(_("More..."));
1861 gtk_widget_show(item);
1863 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1865 list = g_slist_append(list, item);
1867 curmenu = gtk_menu_new();
1868 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1872 utf8buf = g_strdup(l->data);
1874 item = gtk_menu_item_new_with_label(utf8buf);
1876 gtk_widget_show(item);
1877 if (curmenu == NULL) {
1878 list = g_slist_append(list, item);
1880 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1882 g_signal_connect(G_OBJECT(item), "activate",
1883 G_CALLBACK(replace_word_cb),
1886 if (curmenu == NULL && count < MENUCOUNT) {
1887 gtk_widget_add_accelerator(item, "activate",
1889 GDK_KEY_A + count, 0,
1892 gtk_widget_add_accelerator(item, "activate",
1901 } while ((l = l->next) != NULL);
1904 gtk_window_add_accel_group
1905 (GTK_WINDOW(gtkaspell->parent_window),
1907 gtkaspell->accel_group = accel;
1912 static GSList *populate_submenu(GtkAspell *gtkaspell)
1914 GtkWidget *item, *submenu;
1916 GtkAspeller *gtkaspeller = NULL;
1917 GSList *list = NULL;
1922 gtkaspeller = gtkaspell->gtkaspeller;
1923 dictname = g_strdup_printf(_("Dictionary: %s"),
1924 gtkaspeller->dictionary->dictname);
1925 item = gtk_menu_item_new_with_label(dictname);
1926 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
1928 gtk_widget_show(item);
1929 list = g_slist_append(list, item);
1931 item = gtk_menu_item_new();
1932 gtk_widget_show(item);
1933 list = g_slist_append(list, item);
1935 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1936 dictname = g_strdup_printf(_("Use alternate (%s)"),
1937 gtkaspell->alternate_speller->dictionary->dictname);
1938 item = gtk_menu_item_new_with_label(dictname);
1940 g_signal_connect(G_OBJECT(item), "activate",
1941 G_CALLBACK(switch_to_alternate_cb),
1943 gtk_widget_show(item);
1944 list = g_slist_append(list, item);
1947 item = gtk_check_menu_item_new_with_label(_("Use both dictionaries"));
1948 if (gtkaspell->use_both_dicts) {
1949 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1951 g_signal_connect(G_OBJECT(item), "activate",
1952 G_CALLBACK(set_use_both_cb),
1954 gtk_widget_show(item);
1955 list = g_slist_append(list, item);
1957 item = gtk_menu_item_new();
1958 gtk_widget_show(item);
1959 list = g_slist_append(list, item);
1961 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
1962 if (gtkaspell->check_while_typing)
1963 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1965 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1966 g_signal_connect(G_OBJECT(item), "activate",
1967 G_CALLBACK(toggle_check_while_typing_cb),
1969 gtk_widget_show(item);
1970 list = g_slist_append(list, item);
1972 item = gtk_menu_item_new();
1973 gtk_widget_show(item);
1974 list = g_slist_append(list, item);
1976 submenu = gtk_menu_new();
1977 item = gtk_menu_item_new_with_label(_("Change dictionary"));
1978 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
1979 gtk_widget_show(item);
1980 list = g_slist_append(list, item);
1983 if (gtkaspellcheckers->dictionary_list == NULL)
1984 gtkaspell_get_dictionary_list(FALSE);
1986 GtkWidget * curmenu = submenu;
1990 tmp = gtkaspellcheckers->dictionary_list;
1992 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
1993 tmp = g_slist_next(tmp)) {
1994 if (count == MENUCOUNT) {
1997 newmenu = gtk_menu_new();
1998 item = gtk_menu_item_new_with_label(_("More..."));
1999 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2002 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
2003 gtk_widget_show(item);
2007 dict = (Dictionary *) tmp->data;
2008 item = gtk_check_menu_item_new_with_label(dict->dictname);
2009 g_object_set_data(G_OBJECT(item), "dict_name",
2011 if (strcmp2(dict->fullname,
2012 gtkaspell->gtkaspeller->dictionary->fullname))
2013 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2015 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2016 gtk_widget_set_sensitive(GTK_WIDGET(item),
2019 g_signal_connect(G_OBJECT(item), "activate",
2020 G_CALLBACK(change_dict_cb),
2022 gtk_widget_show(item);
2023 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
2031 GSList *gtkaspell_make_config_menu(GtkAspell *gtkaspell)
2033 return populate_submenu(gtkaspell);
2036 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y,
2037 gboolean *push_in, gpointer data)
2039 GtkAspell *gtkaspell = (GtkAspell *) data;
2040 gint xx = 0, yy = 0;
2043 GtkTextView *text = GTK_TEXT_VIEW(gtkaspell->gtktext);
2044 GtkTextBuffer *textbuf;
2049 textbuf = gtk_text_view_get_buffer(gtkaspell->gtktext);
2050 gtk_text_buffer_get_iter_at_mark(textbuf, &iter,
2051 gtk_text_buffer_get_insert(textbuf));
2052 gtk_text_view_get_iter_location(gtkaspell->gtktext, &iter, &rect);
2053 gtk_text_view_buffer_to_window_coords(text, GTK_TEXT_WINDOW_TEXT,
2057 gdk_window_get_origin(GTK_WIDGET(gtkaspell->gtktext)->window, &xx, &yy);
2059 sx = gdk_screen_width();
2060 sy = gdk_screen_height();
2062 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2067 *x = rect.x + xx + 8;
2069 *y = rect.y + rect.height + yy;
2077 /* change the current dictionary of gtkaspell
2078 - if always_set_alt_dict is set, the alternate dict is unconditionally set to the former
2079 current dictionary (common use: from menu callbacks)
2080 - if always_set_alt_dict is NOT set, the alternate dict will be set to the former
2081 current dictionary only if there is no alternate dictionary already set
2082 (this is when we need to set the current dictionary then the alternate one
2083 when creating a compose window, from the account and folder settings)
2085 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary,
2086 gboolean always_set_alt_dict)
2089 GtkAspeller *gtkaspeller;
2091 cm_return_val_if_fail(gtkaspell, FALSE);
2092 cm_return_val_if_fail(dictionary, FALSE);
2094 dict = g_new0(Dictionary, 1);
2096 if (strrchr(dictionary, '/')) {
2097 dict->fullname = g_strdup(strrchr(dictionary, '/')+1);
2098 dict->dictname = g_strdup(strrchr(dictionary, '/')+1);
2100 dict->fullname = g_strdup(dictionary);
2101 dict->dictname = g_strdup(dictionary);
2104 if (dict->fullname && strchr(dict->fullname, '-')) {
2105 *(strchr(dict->fullname, '-')) = '\0';
2106 *(strchr(dict->dictname, '-')) = '\0';
2109 if (!dict->fullname || !(*dict->fullname)) {
2110 dictionary_delete(dict);
2113 gtkaspeller = gtkaspeller_new(dict);
2116 alertpanel_warning(_("The spell checker could not change dictionary.\n%s"),
2117 gtkaspellcheckers->error_message);
2119 if (gtkaspell->use_alternate) {
2120 if (gtkaspell->alternate_speller) {
2121 if (always_set_alt_dict) {
2122 gtkaspeller_delete(gtkaspell->alternate_speller);
2123 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2125 gtkaspeller_delete(gtkaspell->gtkaspeller);
2127 /* should never be reached as the dicts are always set
2128 to a default value */
2129 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2131 gtkaspeller_delete(gtkaspell->gtkaspeller);
2133 gtkaspell->gtkaspeller = gtkaspeller;
2136 dictionary_delete(dict);
2141 /* change the alternate dictionary of gtkaspell (doesn't affect the default dictionary) */
2142 gboolean gtkaspell_change_alt_dict(GtkAspell *gtkaspell, const gchar *alt_dictionary)
2145 GtkAspeller *gtkaspeller;
2147 cm_return_val_if_fail(gtkaspell, FALSE);
2148 cm_return_val_if_fail(alt_dictionary, FALSE);
2150 dict = g_new0(Dictionary, 1);
2151 if (strrchr(alt_dictionary, '/')) {
2152 dict->fullname = g_strdup(strrchr(alt_dictionary, '/')+1);
2153 dict->dictname = g_strdup(strrchr(alt_dictionary, '/')+1);
2155 dict->fullname = g_strdup(alt_dictionary);
2156 dict->dictname = g_strdup(alt_dictionary);
2159 if (dict->fullname && strchr(dict->fullname, '-')) {
2160 *(strchr(dict->fullname, '-')) = '\0';
2161 *(strchr(dict->dictname, '-')) = '\0';
2164 if (!dict->fullname || !(*dict->fullname)) {
2165 dictionary_delete(dict);
2169 gtkaspeller = gtkaspeller_new(dict);
2172 alertpanel_warning(_("The spell checker could not change the alternate dictionary.\n%s"),
2173 gtkaspellcheckers->error_message);
2175 if (gtkaspell->alternate_speller)
2176 gtkaspeller_delete(gtkaspell->alternate_speller);
2177 gtkaspell->alternate_speller = gtkaspeller;
2180 dictionary_delete(dict);
2185 /* Menu call backs */
2187 /* change_dict_cb() - Menu callback : change dict */
2188 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2192 fullname = (gchar *) g_object_get_data(G_OBJECT(w), "dict_name");
2194 if (!strcmp2(fullname, _("None")))
2197 gtkaspell_change_dict(gtkaspell, fullname, TRUE);
2198 gtkaspell_dict_changed(gtkaspell);
2200 if (gtkaspell->menu_changed_cb)
2201 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2204 static void switch_to_alternate_cb(GtkWidget *w,
2207 GtkAspell *gtkaspell = (GtkAspell *) data;
2208 gtkaspell_use_alternate_dict(gtkaspell);
2209 gtkaspell_dict_changed(gtkaspell);
2211 if (gtkaspell->menu_changed_cb)
2212 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2215 /* Misc. helper functions */
2217 static void set_point_continue(GtkAspell *gtkaspell)
2219 GtkTextView *gtktext;
2221 gtktext = gtkaspell->gtktext;
2223 gtkaspell->ctx.set_position(gtkaspell->ctx.data, gtkaspell->orig_pos);
2225 if (gtkaspell->continue_check)
2226 gtkaspell->continue_check((gpointer *) gtkaspell->ctx.data);
2229 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2231 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtkaspell->gtktext);
2232 GdkColor *color = &(gtkaspell->highlight);
2234 /* Shameless copy from Sylpheed's gtkutils.c */
2236 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2238 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2240 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2244 gtk_text_buffer_create_tag(buffer, "misspelled",
2245 "foreground-gdk", color, NULL);
2247 gtk_text_buffer_create_tag(buffer, "misspelled",
2248 "underline", PANGO_UNDERLINE_ERROR, NULL);
2252 static void change_color(GtkAspell * gtkaspell,
2253 gint start, gint end,
2257 GtkTextView *gtktext;
2258 GtkTextBuffer *buffer;
2259 GtkTextIter startiter, enditer;
2264 gtktext = gtkaspell->gtktext;
2266 buffer = gtk_text_view_get_buffer(gtktext);
2267 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, start);
2268 gtk_text_buffer_get_iter_at_offset(buffer, &enditer, end);
2270 gtk_text_buffer_apply_tag_by_name(buffer, "misspelled",
2271 &startiter, &enditer);
2273 gtk_text_iter_forward_char(&enditer);
2274 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
2275 &startiter, &enditer);
2279 /* compare_dict () - compare 2 dict names */
2280 static gint compare_dict(Dictionary *a, Dictionary *b)
2282 guint aparts = 0, bparts = 0;
2285 for (i=0; i < strlen(a->dictname); i++)
2286 if (a->dictname[i] == '-')
2288 for (i=0; i < strlen(b->dictname); i++)
2289 if (b->dictname[i] == '-')
2292 if (aparts != bparts)
2293 return (aparts < bparts) ? -1 : +1;
2296 compare = strcmp2(a->dictname, b->dictname);
2298 compare = strcmp2(a->fullname, b->fullname);
2303 static void dictionary_delete(Dictionary *dict)
2305 g_free(dict->fullname);
2306 g_free(dict->dictname);
2310 static Dictionary *dictionary_dup(const Dictionary *dict)
2314 dict2 = g_new(Dictionary, 1);
2316 dict2->fullname = g_strdup(dict->fullname);
2317 dict2->dictname = g_strdup(dict->dictname);
2322 void gtkaspell_free_suggestions_list(GtkAspell *gtkaspell)
2326 for (list = gtkaspell->suggestions_list; list != NULL;
2330 g_list_free(gtkaspell->suggestions_list);
2332 gtkaspell->max_sug = -1;
2333 gtkaspell->suggestions_list = NULL;
2336 static void reset_theword_data(GtkAspell *gtkaspell)
2338 gtkaspell->start_pos = 0;
2339 gtkaspell->end_pos = 0;
2340 gtkaspell->theword[0] = 0;
2341 gtkaspell->max_sug = -1;
2343 gtkaspell_free_suggestions_list(gtkaspell);
2346 static void free_checkers(gpointer elt, gpointer data)
2348 GtkAspeller *gtkaspeller = elt;
2350 cm_return_if_fail(gtkaspeller);
2352 gtkaspeller_real_delete(gtkaspeller);
2355 static gint find_gtkaspeller(gconstpointer aa, gconstpointer bb)
2357 Dictionary *a = ((GtkAspeller *) aa)->dictionary;
2358 Dictionary *b = ((GtkAspeller *) bb)->dictionary;
2360 if (a && b && a->fullname && b->fullname)
2361 return strcmp(a->fullname, b->fullname);
2366 gchar *gtkaspell_get_default_dictionary(GtkAspell *gtkaspell)
2368 if (gtkaspell && gtkaspell->gtkaspeller &&
2369 gtkaspell->gtkaspeller->dictionary)
2370 return gtkaspell->gtkaspeller->dictionary->dictname;