1 /* gtkaspell - a spell-checking addon for GtkText
2 * Copyright (c) 2000 Evan Martin (original code for ispell).
3 * Copyright (c) 2002 Melvin Hadasht.
4 * Copyright (C) 2001-2013 the Claws Mail Team
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 * Stuphead: (C) 2000,2001 Grigroy Bakunov, Sergey Pinaev
21 * Adapted for Sylpheed (Claws) (c) 2001-2002 by Hiroyuki Yamamoto &
22 * The Claws Mail Team.
23 * Adapted for pspell (c) 2001-2002 Melvin Hadasht
24 * Adapted for GNU/aspell (c) 2002 Melvin Hadasht
29 #include "claws-features.h"
37 #include <sys/types.h>
39 # include <sys/wait.h>
51 #include <glib/gi18n.h>
55 #include <gdk/gdkkeysyms.h>
58 #include "alertpanel.h"
59 #include "gtkaspell.h"
60 #include "gtk/gtkutils.h"
61 #include "gtk/combobox.h"
63 #define ASPELL_FASTMODE 1
64 #define ASPELL_NORMALMODE 2
65 #define ASPELL_BADSPELLERMODE 3
67 /* size of the text buffer used in various word-processing routines. */
70 /* number of suggestions to display on each menu. */
74 SET_GTKASPELL_NAME = 0,
75 SET_GTKASPELL_FULLNAME = 1,
79 typedef struct _GtkAspellCheckers {
81 GSList *dictionary_list;
85 /******************************************************************************/
87 static GtkAspellCheckers *gtkaspellcheckers;
89 /* Error message storage */
90 static void gtkaspell_checkers_error_message (gchar *message);
93 static gboolean key_press_cb (GtkWidget *text_view,
95 GtkAspell *gtkaspell);
96 static void entry_insert_cb (GtkTextBuffer *textbuf,
100 GtkAspell *gtkaspell);
101 static void entry_delete_cb (GtkTextBuffer *textbuf,
102 GtkTextIter *startiter,
103 GtkTextIter *enditer,
104 GtkAspell *gtkaspell);
105 /*static gint button_press_intercept_cb (GtkTextView *gtktext,
107 GtkAspell *gtkaspell);
109 static void button_press_intercept_cb(GtkTextView *gtktext,
110 GtkMenu *menu, GtkAspell *gtkaspell);
112 /* Checker creation */
113 static GtkAspeller* gtkaspeller_new (Dictionary *dict);
114 static GtkAspeller* gtkaspeller_real_new (Dictionary *dict);
115 static GtkAspeller* gtkaspeller_delete (GtkAspeller *gtkaspeller);
116 static GtkAspeller* gtkaspeller_real_delete (GtkAspeller *gtkaspeller);
118 /* Checker configuration */
119 static EnchantDict *set_dictionary (EnchantBroker *broker,
121 static void set_use_both_cb (GtkMenuItem *w,
122 GtkAspell *gtkaspell);
124 /* Checker actions */
125 static gboolean check_at (GtkAspell *gtkaspell,
127 static gboolean check_at_cb (gpointer data);
128 static GList* misspelled_suggest (GtkAspell *gtkaspell,
130 static gboolean find_misspelled_cb (gpointer data,
132 static void add_word_to_session_cb (GtkWidget *w,
134 static void add_word_to_personal_cb (GtkWidget *w,
136 static void replace_with_create_dialog_cb (GtkWidget *w,
138 static void replace_with_supplied_word_cb (GtkWidget *w,
139 GtkAspell *gtkaspell);
140 static void replace_word_cb (GtkWidget *w,
142 static void replace_real_word (GtkAspell *gtkaspell,
143 const gchar *newword);
144 static void replace_real_word_cb (gpointer data,
145 const gchar *newword);
146 static void check_with_alternate_cb (GtkWidget *w,
148 static void toggle_check_while_typing_cb (GtkWidget *w,
152 static GSList* make_sug_menu (GtkAspell *gtkaspell);
153 static GSList * populate_submenu (GtkAspell *gtkaspell);
154 GSList* gtkaspell_make_config_menu (GtkAspell *gtkaspell);
155 static void set_menu_pos (GtkMenu *menu,
160 /* Other menu callbacks */
161 static gboolean aspell_key_pressed (GtkWidget *widget,
163 GtkAspell *gtkaspell);
164 static void change_dict_cb (GtkWidget *w,
165 GtkAspell *gtkaspell);
166 static void switch_to_alternate_cb (GtkWidget *w,
169 /* Misc. helper functions */
170 static void set_point_continue (GtkAspell *gtkaspell);
171 static void continue_check (gpointer *gtkaspell);
172 static gboolean iswordsep (gunichar c);
173 static gunichar get_text_index_whar (GtkAspell *gtkaspell,
175 static gboolean get_word_from_pos (GtkAspell *gtkaspell,
181 static void allocate_color (GtkAspell *gtkaspell,
183 static void change_color (GtkAspell *gtkaspell,
188 static gint compare_dict (Dictionary *a,
190 static void dictionary_delete (Dictionary *dict);
191 static Dictionary * dictionary_dup (const Dictionary *dict);
192 static void reset_theword_data (GtkAspell *gtkaspell);
193 static void free_checkers (gpointer elt,
196 static void destroy_menu(GtkWidget *widget, gpointer user_data);
198 /******************************************************************************/
199 static gint get_textview_buffer_charcount(GtkTextView *view);
201 static void gtkaspell_free_dictionary_list (GSList *list);
202 static GSList* gtkaspell_get_dictionary_list (gint refresh);
204 static void gtkaspell_uncheck_all (GtkAspell *gtkaspell);
206 static gint get_textview_buffer_charcount(GtkTextView *view)
208 GtkTextBuffer *buffer;
210 cm_return_val_if_fail(view, 0);
212 buffer = gtk_text_view_get_buffer(view);
213 cm_return_val_if_fail(buffer, 0);
215 return gtk_text_buffer_get_char_count(buffer);
217 static gint get_textview_buffer_offset(GtkTextView *view)
219 GtkTextBuffer * buffer;
223 cm_return_val_if_fail(view, 0);
225 buffer = gtk_text_view_get_buffer(view);
226 cm_return_val_if_fail(buffer, 0);
228 mark = gtk_text_buffer_get_insert(buffer);
229 cm_return_val_if_fail(mark, 0);
231 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
233 return gtk_text_iter_get_offset(&iter);
235 static void set_textview_buffer_offset(GtkTextView *view, gint offset)
237 GtkTextBuffer *buffer;
240 cm_return_if_fail(view);
242 buffer = gtk_text_view_get_buffer(view);
243 cm_return_if_fail(buffer);
245 gtk_text_buffer_get_iter_at_offset(buffer, &iter, offset);
246 gtk_text_buffer_place_cursor(buffer, &iter);
248 /******************************************************************************/
250 void gtkaspell_checkers_init(void)
252 gtkaspellcheckers = g_new(GtkAspellCheckers, 1);
253 gtkaspellcheckers->checkers = NULL;
254 gtkaspellcheckers->dictionary_list = NULL;
255 gtkaspellcheckers->error_message = NULL;
258 void gtkaspell_checkers_quit(void)
263 if (gtkaspellcheckers == NULL)
266 if ((checkers = gtkaspellcheckers->checkers)) {
267 debug_print("Aspell: number of running checkers to delete %d\n",
268 g_slist_length(checkers));
270 g_slist_foreach(checkers, free_checkers, NULL);
271 g_slist_free(checkers);
272 gtkaspellcheckers->checkers = NULL;
275 if ((dict_list = gtkaspellcheckers->dictionary_list)) {
276 debug_print("Aspell: number of dictionaries to delete %d\n",
277 g_slist_length(dict_list));
279 gtkaspell_free_dictionary_list(dict_list);
280 gtkaspellcheckers->dictionary_list = NULL;
283 g_free(gtkaspellcheckers->error_message);
284 gtkaspellcheckers->error_message = NULL;
288 static void gtkaspell_checkers_error_message (gchar *message)
291 if (gtkaspellcheckers->error_message) {
292 tmp = g_strdup_printf("%s\n%s",
293 gtkaspellcheckers->error_message,
296 g_free(gtkaspellcheckers->error_message);
297 gtkaspellcheckers->error_message = tmp;
299 gtkaspellcheckers->error_message = message;
304 const char *gtkaspell_checkers_strerror(void)
306 cm_return_val_if_fail(gtkaspellcheckers, "");
307 return gtkaspellcheckers->error_message;
310 void gtkaspell_checkers_reset_error(void)
312 cm_return_if_fail(gtkaspellcheckers);
314 g_free(gtkaspellcheckers->error_message);
316 gtkaspellcheckers->error_message = NULL;
319 GtkAspell *gtkaspell_new(const gchar *dictionary,
320 const gchar *alt_dictionary,
321 const gchar *encoding, /* unused */
322 gint misspelled_color,
323 gboolean check_while_typing,
324 gboolean recheck_when_changing_dict,
325 gboolean use_alternate,
326 gboolean use_both_dicts,
327 GtkTextView *gtktext,
328 GtkWindow *parent_win,
329 void (dict_changed_cb)(void *data),
330 void (*spell_menu_cb)(void *data),
334 GtkAspell *gtkaspell;
335 GtkAspeller *gtkaspeller;
336 GtkTextBuffer *buffer;
338 cm_return_val_if_fail(gtktext, NULL);
339 if (!dictionary || !*dictionary) {
340 gtkaspell_checkers_error_message(
341 g_strdup(_("No dictionary selected.")));
345 buffer = gtk_text_view_get_buffer(gtktext);
347 dict = g_new0(Dictionary, 1);
348 if (strrchr(dictionary, '/')) {
349 dict->fullname = g_strdup(strrchr(dictionary, '/')+1);
350 dict->dictname = g_strdup(strrchr(dictionary, '/')+1);
352 dict->fullname = g_strdup(dictionary);
353 dict->dictname = g_strdup(dictionary);
356 if (strchr(dict->fullname, '-')) {
357 *(strchr(dict->fullname, '-')) = '\0';
358 *(strchr(dict->dictname, '-')) = '\0';
360 gtkaspeller = gtkaspeller_new(dict);
361 dictionary_delete(dict);
364 gtkaspell_checkers_error_message(
365 g_strdup_printf(_("Couldn't initialize %s speller."), dictionary));
369 gtkaspell = g_new0(GtkAspell, 1);
371 gtkaspell->gtkaspeller = gtkaspeller;
373 if (use_alternate && alt_dictionary && *alt_dictionary) {
374 Dictionary *alt_dict;
375 GtkAspeller *alt_gtkaspeller;
377 alt_dict = g_new0(Dictionary, 1);
378 if (strrchr(alt_dictionary, '/')) {
379 alt_dict->fullname = g_strdup(strrchr(alt_dictionary, '/')+1);
380 alt_dict->dictname = g_strdup(strrchr(alt_dictionary, '/')+1);
382 alt_dict->fullname = g_strdup(alt_dictionary);
383 alt_dict->dictname = g_strdup(alt_dictionary);
385 if (strchr(alt_dict->fullname, '-')) {
386 *(strchr(alt_dict->fullname, '-')) = '\0';
387 *(strchr(alt_dict->dictname, '-')) = '\0';
390 alt_gtkaspeller = gtkaspeller_new(alt_dict);
391 dictionary_delete(alt_dict);
393 if (!alt_gtkaspeller) {
394 gtkaspell_checkers_error_message(
395 g_strdup_printf(_("Couldn't initialize %s speller."), dictionary));
396 gtkaspeller_delete(gtkaspeller);
401 gtkaspell->alternate_speller = alt_gtkaspeller;
403 gtkaspell->alternate_speller = NULL;
406 gtkaspell->theword[0] = 0x00;
407 gtkaspell->start_pos = 0;
408 gtkaspell->end_pos = 0;
409 gtkaspell->orig_pos = -1;
410 gtkaspell->end_check_pos = -1;
411 gtkaspell->misspelled = -1;
412 gtkaspell->check_while_typing = check_while_typing;
413 gtkaspell->recheck_when_changing_dict = recheck_when_changing_dict;
414 gtkaspell->continue_check = NULL;
415 gtkaspell->replace_entry = NULL;
416 gtkaspell->gtktext = gtktext;
417 gtkaspell->max_sug = -1;
418 gtkaspell->suggestions_list = NULL;
419 gtkaspell->use_alternate = use_alternate;
420 gtkaspell->use_both_dicts = use_both_dicts;
421 gtkaspell->parent_window = GTK_WIDGET(parent_win);
422 gtkaspell->dict_changed_cb = dict_changed_cb;
423 gtkaspell->menu_changed_cb = spell_menu_cb;
424 gtkaspell->menu_changed_data = data;
426 allocate_color(gtkaspell, misspelled_color);
428 g_signal_connect(G_OBJECT(gtktext), "key_press_event",
429 G_CALLBACK(key_press_cb), gtkaspell);
430 g_signal_connect_after(G_OBJECT(buffer), "insert-text",
431 G_CALLBACK(entry_insert_cb), gtkaspell);
432 g_signal_connect_after(G_OBJECT(buffer), "delete-range",
433 G_CALLBACK(entry_delete_cb), gtkaspell);
434 /*g_signal_connect(G_OBJECT(gtktext), "button-press-event",
435 G_CALLBACK(button_press_intercept_cb),
437 g_signal_connect(G_OBJECT(gtktext), "populate-popup",
438 G_CALLBACK(button_press_intercept_cb), gtkaspell);
440 debug_print("Aspell: created gtkaspell %p\n", gtkaspell);
445 void gtkaspell_delete(GtkAspell *gtkaspell)
447 GtkTextView *gtktext = gtkaspell->gtktext;
449 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
450 G_CALLBACK(key_press_cb),
452 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
453 G_CALLBACK(entry_insert_cb),
455 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
456 G_CALLBACK(entry_delete_cb),
458 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
459 G_CALLBACK(button_press_intercept_cb),
462 gtkaspell_uncheck_all(gtkaspell);
464 gtkaspeller_delete(gtkaspell->gtkaspeller);
466 if (gtkaspell->alternate_speller)
467 gtkaspeller_delete(gtkaspell->alternate_speller);
469 if (gtkaspell->suggestions_list)
470 gtkaspell_free_suggestions_list(gtkaspell);
472 debug_print("Aspell: deleting gtkaspell %p\n", gtkaspell);
479 void gtkaspell_dict_changed(GtkAspell *gtkaspell)
481 if(!gtkaspell || !gtkaspell->dict_changed_cb ||
482 !gtkaspell->menu_changed_data)
485 gtkaspell->dict_changed_cb(gtkaspell->menu_changed_data);
488 static gboolean key_press_cb (GtkWidget *text_view,
490 GtkAspell *gtkaspell)
494 cm_return_val_if_fail(gtkaspell->gtkaspeller->speller, FALSE);
496 if (!gtkaspell->check_while_typing)
499 switch (event->keyval) {
505 case GDK_KEY_Page_Up:
506 case GDK_KEY_Page_Down:
509 pos = get_textview_buffer_offset(GTK_TEXT_VIEW(text_view));
511 check_at(gtkaspell, pos - 1);
513 check_at(gtkaspell, pos);
522 static void entry_insert_cb(GtkTextBuffer *textbuf,
526 GtkAspell *gtkaspell)
530 cm_return_if_fail(gtkaspell->gtkaspeller->speller);
532 if (!gtkaspell->check_while_typing)
535 pos = gtk_text_iter_get_offset(iter);
537 if (iswordsep(g_utf8_get_char(newtext))) {
538 /* did we just end a word? */
540 check_at(gtkaspell, pos - 2);
542 /* did we just split a word? */
543 if (pos < gtk_text_buffer_get_char_count(textbuf))
544 check_at(gtkaspell, pos + 1);
546 /* check as they type, *except* if they're typing at the end (the most
549 if (pos < gtk_text_buffer_get_char_count(textbuf) &&
550 !iswordsep(get_text_index_whar(gtkaspell, pos))) {
551 check_at(gtkaspell, pos - 1);
556 static void entry_delete_cb(GtkTextBuffer *textbuf,
557 GtkTextIter *startiter,
558 GtkTextIter *enditer,
559 GtkAspell *gtkaspell)
564 cm_return_if_fail(gtkaspell->gtkaspeller->speller);
566 if (!gtkaspell->check_while_typing)
569 start = gtk_text_iter_get_offset(startiter);
570 origpos = get_textview_buffer_offset(gtkaspell->gtktext);
572 check_at(gtkaspell, start - 1);
573 check_at(gtkaspell, start);
576 set_textview_buffer_offset(gtkaspell->gtktext, origpos);
577 /* this is to *UNDO* the selection, in case they were holding shift
578 * while hitting backspace. */
579 /* needed with textview ??? */
580 /* gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos); */
583 void gtkaspell_make_context_menu(GtkMenu *menu, GtkAspell *gtkaspell)
585 GtkMenuItem *menuitem;
586 GSList *spell_menu = NULL;
588 gboolean suggest = FALSE;
590 if (gtkaspell->misspelled &&
591 misspelled_suggest(gtkaspell, gtkaspell->theword)) {
592 spell_menu = make_sug_menu(gtkaspell);
596 spell_menu = gtkaspell_make_config_menu(gtkaspell);
598 menuitem = GTK_MENU_ITEM(gtk_separator_menu_item_new());
599 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
600 gtk_widget_show(GTK_WIDGET(menuitem));
602 spell_menu = g_slist_reverse(spell_menu);
603 for (items = spell_menu;
604 items; items = items->next) {
605 menuitem = GTK_MENU_ITEM(items->data);
606 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
607 gtk_widget_show(GTK_WIDGET(menuitem));
609 g_slist_free(spell_menu);
611 g_signal_connect(G_OBJECT(menu), "deactivate",
612 G_CALLBACK(destroy_menu),
615 g_signal_connect(G_OBJECT(menu),
617 G_CALLBACK(aspell_key_pressed),
621 static void set_position_cb(gpointer data, gint pos)
623 GtkAspell *gtkaspell = (GtkAspell *) data;
624 set_textview_buffer_offset(gtkaspell->gtktext, pos);
627 void gtkaspell_context_set(GtkAspell *gtkaspell)
629 gtkaspell->ctx.set_position = set_position_cb;
630 gtkaspell->ctx.set_menu_pos = set_menu_pos;
631 gtkaspell->ctx.find_misspelled = find_misspelled_cb;
632 gtkaspell->ctx.check_word = check_at_cb;
633 gtkaspell->ctx.replace_word = replace_real_word_cb;
634 gtkaspell->ctx.data = (gpointer) gtkaspell;
637 static void button_press_intercept_cb(GtkTextView *gtktext,
638 GtkMenu *menu, GtkAspell *gtkaspell)
640 gtktext = gtkaspell->gtktext;
641 gtkaspell->orig_pos = get_textview_buffer_offset(gtktext);
642 gtkaspell->misspelled = check_at(gtkaspell, gtkaspell->orig_pos);
644 gtkaspell_context_set(gtkaspell);
645 gtkaspell_make_context_menu(menu, gtkaspell);
647 /* Checker creation */
648 static GtkAspeller *gtkaspeller_new(Dictionary *dictionary)
650 GtkAspeller *gtkaspeller = NULL;
654 cm_return_val_if_fail(gtkaspellcheckers, NULL);
656 cm_return_val_if_fail(dictionary, NULL);
658 if (dictionary->dictname == NULL)
659 gtkaspell_checkers_error_message(
660 g_strdup(_("No dictionary selected.")));
662 cm_return_val_if_fail(dictionary->fullname, NULL);
664 dict = dictionary_dup(dictionary);
666 tmp = g_new0(GtkAspeller, 1);
667 tmp->dictionary = dict;
671 if ((gtkaspeller = gtkaspeller_real_new(dict)) != NULL) {
672 gtkaspellcheckers->checkers = g_slist_append(
673 gtkaspellcheckers->checkers,
676 debug_print("Aspell: Created a new gtkaspeller %p\n",
679 dictionary_delete(dict);
681 debug_print("Aspell: Could not create spell checker.\n");
684 debug_print("Aspell: number of existing checkers %d\n",
685 g_slist_length(gtkaspellcheckers->checkers));
690 static GtkAspeller *gtkaspeller_real_new(Dictionary *dict)
692 GtkAspeller *gtkaspeller;
693 EnchantBroker *broker;
694 EnchantDict *speller;
696 cm_return_val_if_fail(gtkaspellcheckers, NULL);
697 cm_return_val_if_fail(dict, NULL);
699 gtkaspeller = g_new(GtkAspeller, 1);
701 gtkaspeller->dictionary = dict;
703 broker = enchant_broker_init();
706 gtkaspell_checkers_error_message(
707 g_strdup(_("Couldn't initialize Enchant broker.")));
711 if ((speller = set_dictionary(broker, dict)) == NULL) {
712 gtkaspell_checkers_error_message(
713 g_strdup_printf(_("Couldn't initialize %s dictionary:"), dict->fullname));
714 gtkaspell_checkers_error_message(
715 g_strdup(enchant_broker_get_error(broker)));
719 gtkaspeller->speller = speller;
720 gtkaspeller->broker = broker;
725 static GtkAspeller *gtkaspeller_delete(GtkAspeller *gtkaspeller)
727 cm_return_val_if_fail(gtkaspellcheckers, NULL);
729 gtkaspellcheckers->checkers =
730 g_slist_remove(gtkaspellcheckers->checkers,
733 debug_print("Aspell: Deleting gtkaspeller %p.\n",
736 gtkaspeller_real_delete(gtkaspeller);
738 debug_print("Aspell: number of existing checkers %d\n",
739 g_slist_length(gtkaspellcheckers->checkers));
744 static GtkAspeller *gtkaspeller_real_delete(GtkAspeller *gtkaspeller)
746 cm_return_val_if_fail(gtkaspeller, NULL);
747 cm_return_val_if_fail(gtkaspeller->speller, NULL);
749 enchant_broker_free_dict(gtkaspeller->broker, gtkaspeller->speller);
750 enchant_broker_free(gtkaspeller->broker);
752 dictionary_delete(gtkaspeller->dictionary);
754 debug_print("Aspell: gtkaspeller %p deleted.\n",
762 /*****************************************************************************/
763 /* Checker configuration */
765 static EnchantDict *set_dictionary(EnchantBroker *broker, Dictionary *dict)
767 cm_return_val_if_fail(broker, FALSE);
768 cm_return_val_if_fail(dict, FALSE);
770 return enchant_broker_request_dict(broker, dict->dictname );
773 static void set_use_both_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
775 gtkaspell->use_both_dicts = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
776 gtkaspell_dict_changed(gtkaspell);
778 if (gtkaspell->menu_changed_cb)
779 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
782 /* misspelled_suggest() - Create a suggestion list for word */
783 static GList *misspelled_suggest(GtkAspell *gtkaspell, gchar *word)
788 cm_return_val_if_fail(word, NULL);
793 gtkaspell_free_suggestions_list(gtkaspell);
795 suggestions = enchant_dict_suggest(gtkaspell->gtkaspeller->speller, word, strlen(word), &num_sug);
796 list = g_list_append(list, g_strdup(word));
797 if (suggestions == NULL || num_sug == 0) {
798 gtkaspell->max_sug = -1;
799 gtkaspell->suggestions_list = list;
802 for (i = 0; i < num_sug; i++)
803 list = g_list_append(list, g_strdup((gchar *)suggestions[i]));
805 gtkaspell->max_sug = num_sug - 1;
806 gtkaspell->suggestions_list = list;
807 enchant_dict_free_string_list(gtkaspell->gtkaspeller->speller, suggestions);
811 /* misspelled_test() - Just test if word is correctly spelled */
812 int gtkaspell_misspelled_test(GtkAspell *gtkaspell, char *word)
815 cm_return_val_if_fail(word, 0);
820 result = enchant_dict_check(gtkaspell->gtkaspeller->speller, word, strlen(word));
822 if (result && gtkaspell->use_both_dicts && gtkaspell->alternate_speller) {
823 gtkaspell_use_alternate_dict(gtkaspell);
824 result = enchant_dict_check(gtkaspell->gtkaspeller->speller, word, strlen(word));
825 gtkaspell_use_alternate_dict(gtkaspell);
827 return (result && strcasecmp(word, "sylpheed") &&
828 strcasecmp(word, "claws-mail"));
832 static gboolean iswordsep(gunichar c)
834 return (g_unichar_isspace(c) || g_unichar_ispunct(c)) && c != (gunichar)'\'';
837 static gunichar get_text_index_whar(GtkAspell *gtkaspell, int pos)
839 GtkTextView *view = gtkaspell->gtktext;
840 GtkTextBuffer *buffer = gtk_text_view_get_buffer(view);
841 GtkTextIter start, end;
845 gtk_text_buffer_get_iter_at_offset(buffer, &start, pos);
846 gtk_text_buffer_get_iter_at_offset(buffer, &end, pos+1);
848 utf8chars = gtk_text_iter_get_text(&start, &end);
849 a = g_utf8_get_char(utf8chars);
855 /* get_word_from_pos () - return the word pointed to. */
856 /* Handles correctly the quotes. */
857 static gboolean get_word_from_pos(GtkAspell *gtkaspell, gint pos,
858 char* buf, gint buflen,
859 gint *pstart, gint *pend)
862 /* TODO : when correcting a word into quotes, change the color of */
863 /* the quotes too, as may be they were highlighted before. To do */
864 /* this, we can use two others pointers that points to the whole */
865 /* word including quotes. */
871 GtkTextView *gtktext;
873 gtktext = gtkaspell->gtktext;
874 if (iswordsep(get_text_index_whar(gtkaspell, pos)))
877 /* The apostrophe character is somtimes used for quotes
878 * So include it in the word only if it is not surrounded
879 * by other characters.
882 for (start = pos; start >= 0; --start) {
883 c = get_text_index_whar(gtkaspell, start);
884 if (c == (gunichar)'\'') {
886 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
888 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
890 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
892 /* start_quote = TRUE; */
897 /* start_quote = TRUE; */
901 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
907 for (end = pos; end < get_textview_buffer_charcount(gtktext); end++) {
908 c = get_text_index_whar(gtkaspell, end);
909 if (c == (gunichar)'\'') {
910 if (end < get_textview_buffer_charcount(gtktext)) {
911 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
913 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
915 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
917 /* end_quote = TRUE; */
922 /* end_quote = TRUE; */
926 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
936 if (end - start < buflen) {
937 GtkTextIter iterstart, iterend;
939 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtktext);
940 gtk_text_buffer_get_iter_at_offset(buffer, &iterstart, start);
941 gtk_text_buffer_get_iter_at_offset(buffer, &iterend, end);
942 tmp = gtk_text_buffer_get_text(buffer, &iterstart, &iterend, FALSE);
943 strncpy(buf, tmp, buflen-1);
953 static gboolean check_at(GtkAspell *gtkaspell, gint from_pos)
956 char buf[GTKASPELLWORDSIZE];
958 cm_return_val_if_fail(from_pos >= 0, FALSE);
960 if (!get_word_from_pos(gtkaspell, from_pos, buf, sizeof(buf),
964 if (gtkaspell_misspelled_test(gtkaspell, buf)) {
965 strncpy(gtkaspell->theword, (gchar *)buf, GTKASPELLWORDSIZE - 1);
966 gtkaspell->theword[GTKASPELLWORDSIZE - 1] = 0;
967 gtkaspell->start_pos = start;
968 gtkaspell->end_pos = end;
969 gtkaspell_free_suggestions_list(gtkaspell);
971 change_color(gtkaspell, start, end, (gchar *)buf, &(gtkaspell->highlight));
974 change_color(gtkaspell, start, end, (gchar *)buf, NULL);
979 static gboolean check_at_cb(gpointer data)
981 GtkAspell *gtkaspell = (GtkAspell *)data;
982 return check_at(gtkaspell, gtkaspell->start_pos);
985 static gboolean find_misspelled_cb(gpointer data, gboolean forward)
987 GtkAspell *gtkaspell = (GtkAspell *)data;
995 maxpos = gtkaspell->end_check_pos;
1003 pos = get_textview_buffer_offset(gtkaspell->gtktext);
1004 gtkaspell->orig_pos = pos;
1005 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1006 pos > minpos && pos <= maxpos)
1008 while (!(misspelled = check_at(gtkaspell, pos)) &&
1009 pos > minpos && pos <= maxpos) {
1011 while (!iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1012 pos > minpos && pos <= maxpos)
1015 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1016 pos > minpos && pos <= maxpos)
1023 gboolean gtkaspell_check_next_prev(GtkAspell *gtkaspell, gboolean forward)
1025 gboolean misspelled = gtkaspell->ctx.find_misspelled(gtkaspell->ctx.data,
1030 misspelled_suggest(gtkaspell, gtkaspell->theword);
1033 gtkaspell->orig_pos = gtkaspell->end_pos;
1035 gtkaspell->ctx.set_position(gtkaspell->ctx.data, gtkaspell->end_pos);
1037 /* only execute when in textview context */
1038 if (gtkaspell == (GtkAspell *)gtkaspell->ctx.data) {
1039 /* scroll line to window center */
1040 gtk_text_view_scroll_to_mark(gtkaspell->gtktext,
1041 gtk_text_buffer_get_insert(
1042 gtk_text_view_get_buffer(gtkaspell->gtktext)),
1043 0.0, TRUE, 0.0, 0.5);
1044 /* let textview recalculate coordinates (set_menu_pos) */
1045 while (gtk_events_pending ())
1046 gtk_main_iteration ();
1049 list = make_sug_menu(gtkaspell);
1050 menu = gtk_menu_new();
1051 for (cur = list; cur; cur = cur->next)
1052 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(cur->data));
1054 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1055 gtkaspell->ctx.set_menu_pos,
1056 gtkaspell->ctx.data,
1057 0, GDK_CURRENT_TIME);
1058 g_signal_connect(G_OBJECT(menu), "deactivate",
1059 G_CALLBACK(destroy_menu),
1061 g_signal_connect(G_OBJECT(menu),
1063 G_CALLBACK(aspell_key_pressed),
1068 reset_theword_data(gtkaspell);
1070 alertpanel_notice(_("No misspelled word found."));
1071 gtkaspell->ctx.set_position(gtkaspell->ctx.data,
1072 gtkaspell->orig_pos);
1078 void gtkaspell_check_backwards(GtkAspell *gtkaspell)
1080 gtkaspell->continue_check = NULL;
1081 gtkaspell->end_check_pos =
1082 get_textview_buffer_charcount(gtkaspell->gtktext);
1083 gtkaspell_context_set(gtkaspell);
1084 gtkaspell_check_next_prev(gtkaspell, FALSE);
1087 void gtkaspell_check_forwards_go(GtkAspell *gtkaspell)
1090 gtkaspell->continue_check = NULL;
1091 gtkaspell->end_check_pos =
1092 get_textview_buffer_charcount(gtkaspell->gtktext);
1093 gtkaspell_context_set(gtkaspell);
1094 gtkaspell_check_next_prev(gtkaspell, TRUE);
1097 void gtkaspell_check_all(GtkAspell *gtkaspell)
1099 GtkTextView *gtktext;
1101 GtkTextBuffer *buffer;
1102 GtkTextIter startiter, enditer;
1104 cm_return_if_fail(gtkaspell);
1105 cm_return_if_fail(gtkaspell->gtktext);
1107 gtktext = gtkaspell->gtktext;
1108 buffer = gtk_text_view_get_buffer(gtktext);
1109 gtk_text_buffer_get_selection_bounds(buffer, &startiter, &enditer);
1110 start = gtk_text_iter_get_offset(&startiter);
1111 end = gtk_text_iter_get_offset(&enditer);
1115 end = gtk_text_buffer_get_char_count(buffer);
1116 } else if (start > end) {
1124 set_textview_buffer_offset(gtktext, start);
1126 gtkaspell->continue_check = continue_check;
1127 gtkaspell->end_check_pos = end;
1129 gtkaspell_context_set(gtkaspell);
1130 gtkaspell->misspelled = gtkaspell_check_next_prev(gtkaspell, TRUE);
1133 static void continue_check(gpointer *data)
1135 GtkAspell *gtkaspell = (GtkAspell *) data;
1136 gint pos = get_textview_buffer_offset(gtkaspell->gtktext);
1137 if (pos < gtkaspell->end_check_pos && gtkaspell->misspelled)
1138 gtkaspell->misspelled = gtkaspell_check_next_prev(gtkaspell, TRUE);
1140 gtkaspell->continue_check = NULL;
1143 void gtkaspell_highlight_all(GtkAspell *gtkaspell)
1148 GtkTextView *gtktext;
1150 cm_return_if_fail(gtkaspell->gtkaspeller->speller);
1152 gtktext = gtkaspell->gtktext;
1154 len = get_textview_buffer_charcount(gtktext);
1156 origpos = get_textview_buffer_offset(gtktext);
1160 iswordsep(get_text_index_whar(gtkaspell, pos)))
1163 !iswordsep(get_text_index_whar(gtkaspell, pos)))
1166 check_at(gtkaspell, pos - 1);
1168 set_textview_buffer_offset(gtktext, origpos);
1171 static void replace_with_supplied_word_cb(GtkWidget *w, GtkAspell *gtkaspell)
1174 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1176 newword = gtk_editable_get_chars(GTK_EDITABLE(gtkaspell->replace_entry),
1179 if (strcmp(newword, gtkaspell->theword)) {
1180 gtkaspell->ctx.replace_word(gtkaspell->ctx.data, newword);
1182 if ((e->type == GDK_KEY_PRESS &&
1183 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK)) {
1184 enchant_dict_store_replacement(gtkaspell->gtkaspeller->speller,
1185 gtkaspell->theword, strlen(gtkaspell->theword),
1186 newword, strlen(newword));
1188 gtkaspell->replace_entry = NULL;
1193 if (w && GTK_IS_DIALOG(w)) {
1194 gtk_widget_destroy(w);
1197 set_point_continue(gtkaspell);
1201 static void replace_word_cb(GtkWidget *w, gpointer data)
1203 const char *newword;
1204 GtkAspell *gtkaspell = (GtkAspell *) data;
1205 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1207 newword = gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN((w)))));
1209 gtkaspell->ctx.replace_word(gtkaspell->ctx.data, newword);
1211 if ((e->type == GDK_KEY_PRESS &&
1212 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK) ||
1213 (e->type == GDK_BUTTON_RELEASE &&
1214 ((GdkEventButton *) e)->state & GDK_CONTROL_MASK)) {
1215 enchant_dict_store_replacement(gtkaspell->gtkaspeller->speller,
1216 gtkaspell->theword, strlen(gtkaspell->theword),
1217 newword, strlen(newword));
1220 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w)));
1222 set_point_continue(gtkaspell);
1225 void gtkaspell_block_check(GtkAspell *gtkaspell)
1227 GtkTextView *gtktext;
1229 if (gtkaspell == NULL)
1232 gtktext = gtkaspell->gtktext;
1233 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1234 G_CALLBACK(key_press_cb),
1236 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1237 G_CALLBACK(entry_insert_cb),
1239 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1240 G_CALLBACK(entry_delete_cb),
1244 void gtkaspell_unblock_check(GtkAspell *gtkaspell)
1246 GtkTextView *gtktext;
1248 if (gtkaspell == NULL)
1251 gtktext = gtkaspell->gtktext;
1252 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1253 G_CALLBACK(key_press_cb),
1255 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1256 G_CALLBACK(entry_insert_cb),
1258 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1259 G_CALLBACK(entry_delete_cb),
1263 static void replace_real_word(GtkAspell *gtkaspell, const gchar *newword)
1268 GtkTextView *gtktext;
1269 GtkTextBuffer *textbuf;
1270 GtkTextIter startiter, enditer;
1272 if (!newword) return;
1274 gtktext = gtkaspell->gtktext;
1275 textbuf = gtk_text_view_get_buffer(gtktext);
1277 origpos = gtkaspell->orig_pos;
1279 oldlen = gtkaspell->end_pos - gtkaspell->start_pos;
1281 newlen = strlen(newword); /* FIXME: multybyte characters? */
1283 gtkaspell_block_check(gtkaspell);
1285 gtk_text_buffer_get_iter_at_offset(textbuf, &startiter,
1286 gtkaspell->start_pos);
1287 gtk_text_buffer_get_iter_at_offset(textbuf, &enditer,
1288 gtkaspell->end_pos);
1289 g_signal_emit_by_name(G_OBJECT(textbuf), "delete-range",
1290 &startiter, &enditer, gtkaspell);
1291 g_signal_emit_by_name(G_OBJECT(textbuf), "insert-text",
1292 &startiter, newword, newlen, gtkaspell);
1294 gtkaspell_unblock_check(gtkaspell);
1296 /* Put the point and the position where we clicked with the mouse
1297 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1298 * to let it update correctly the word insertion and then the
1299 * point & position position. If not, SEGV after the first replacement
1300 * If the new word ends before point, put the point at its end.
1303 if (origpos - gtkaspell->start_pos < oldlen &&
1304 origpos - gtkaspell->start_pos >= 0) {
1305 /* Original point was in the word.
1306 * Let it there unless point is going to be outside of the word
1308 if (origpos - gtkaspell->start_pos >= newlen) {
1309 pos = gtkaspell->start_pos + newlen;
1312 else if (origpos >= gtkaspell->end_pos) {
1313 /* move the position according to the change of length */
1314 pos = origpos + newlen - oldlen;
1317 gtkaspell->end_pos = gtkaspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1319 if (get_textview_buffer_charcount(gtktext) < pos)
1320 pos = get_textview_buffer_charcount(gtktext);
1321 gtkaspell->orig_pos = pos;
1323 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1326 static void replace_real_word_cb(gpointer data, const gchar *newword)
1328 replace_real_word((GtkAspell *)data, newword);
1331 /* Accept this word for this session */
1332 static void add_word_to_session_cb(GtkWidget *w, gpointer data)
1334 GtkAspell *gtkaspell = (GtkAspell *) data;
1336 enchant_dict_add_to_session(gtkaspell->gtkaspeller->speller, gtkaspell->theword, strlen(gtkaspell->theword));
1338 gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1339 gtkaspell_dict_changed(gtkaspell);
1341 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w)));
1343 set_point_continue(gtkaspell);
1346 /* add_word_to_personal_cb() - add word to personal dict. */
1347 static void add_word_to_personal_cb(GtkWidget *w, gpointer data)
1349 GtkAspell *gtkaspell = (GtkAspell *) data;
1351 enchant_dict_add(gtkaspell->gtkaspeller->speller, gtkaspell->theword, strlen(gtkaspell->theword));
1353 gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1354 gtkaspell_dict_changed(gtkaspell);
1356 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w)));
1357 set_point_continue(gtkaspell);
1360 static void check_with_alternate_cb(GtkWidget *w, gpointer data)
1362 GtkAspell *gtkaspell = (GtkAspell *)data;
1365 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w)));
1367 gtkaspell_use_alternate_dict(gtkaspell);
1368 misspelled = gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1370 if (!gtkaspell->continue_check) {
1372 gtkaspell->misspelled = misspelled;
1374 if (gtkaspell->misspelled) {
1377 misspelled_suggest(gtkaspell, gtkaspell->theword);
1379 gtkaspell->ctx.set_position(gtkaspell->ctx.data,
1380 gtkaspell->end_pos);
1382 list = make_sug_menu(gtkaspell);
1383 menu = gtk_menu_new();
1384 for (cur = list; cur; cur = cur->next)
1385 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(cur->data));
1387 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1388 gtkaspell->ctx.set_menu_pos,
1389 gtkaspell->ctx.data, 0,
1391 g_signal_connect(G_OBJECT(menu), "deactivate",
1392 G_CALLBACK(destroy_menu),
1394 g_signal_connect(G_OBJECT(menu),
1396 G_CALLBACK(aspell_key_pressed),
1401 gtkaspell->orig_pos = gtkaspell->start_pos;
1403 set_point_continue(gtkaspell);
1406 static gboolean replace_key_pressed(GtkWidget *widget,
1408 GtkAspell *gtkaspell)
1410 if (event && event->keyval == GDK_KEY_Escape) {
1411 gtk_widget_destroy(widget);
1413 } else if (event && event->keyval == GDK_KEY_Return) {
1414 replace_with_supplied_word_cb(widget, gtkaspell);
1420 static void replace_with_create_dialog_cb(GtkWidget *w, gpointer data)
1422 static PangoFontDescription *font_desc;
1428 GtkWidget *ok_button;
1429 GtkWidget *cancel_button;
1430 GtkWidget *confirm_area;
1432 GtkWidget *parent_window;
1433 GtkWidget *content_area;
1434 GtkWidget *action_area;
1435 gchar *utf8buf, *thelabel;
1437 GtkAspell *gtkaspell = (GtkAspell *) data;
1439 cm_return_if_fail(w != NULL);
1440 parent_window = gtk_widget_get_parent(w);
1441 cm_return_if_fail(parent_window != NULL);
1442 gdk_window_get_origin(gtk_widget_get_window(parent_window), &xx, &yy);
1444 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w)));
1446 dialog = gtk_dialog_new();
1447 content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1448 action_area = gtk_dialog_get_action_area(GTK_DIALOG(dialog));
1450 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1451 gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1452 gtk_window_move(GTK_WINDOW(dialog), xx, yy);
1454 g_signal_connect_swapped(G_OBJECT(dialog), "destroy",
1455 G_CALLBACK(gtk_widget_destroy),
1458 gtk_box_set_spacing (GTK_BOX (content_area), 14);
1459 hbox = gtk_hbox_new (FALSE, 12);
1460 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1461 gtk_widget_show (hbox);
1462 gtk_box_pack_start (GTK_BOX (content_area), hbox, FALSE, FALSE, 0);
1464 utf8buf = g_strdup(gtkaspell->theword);
1466 thelabel = g_strdup_printf(g_strconcat("<span weight=\"bold\" size=\"larger\">",
1467 _("Replace \"%s\" with: "), "</span>", NULL),
1470 icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
1471 GTK_ICON_SIZE_DIALOG);
1472 gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
1473 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
1475 vbox = gtk_vbox_new (FALSE, 12);
1476 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1477 gtk_widget_show (vbox);
1479 label = gtk_label_new(thelabel);
1480 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1481 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1482 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1483 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1484 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1488 size = pango_font_description_get_size
1489 (gtk_widget_get_style(label)->font_desc);
1490 font_desc = pango_font_description_new();
1491 pango_font_description_set_weight
1492 (font_desc, PANGO_WEIGHT_BOLD);
1493 pango_font_description_set_size
1494 (font_desc, size * PANGO_SCALE_LARGE);
1497 gtk_widget_modify_font(label, font_desc);
1500 entry = gtk_entry_new();
1501 gtkaspell->replace_entry = entry;
1502 gtk_entry_set_text(GTK_ENTRY(entry), utf8buf);
1503 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1504 g_signal_connect(G_OBJECT(dialog),
1506 G_CALLBACK(replace_key_pressed), gtkaspell);
1507 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
1510 label = gtk_label_new(_("Holding down Control key while pressing "
1511 "Enter\nwill learn from mistake.\n"));
1512 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1513 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1514 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1515 gtk_widget_show(label);
1517 gtkut_stock_button_set_create(&confirm_area,
1518 &cancel_button, GTK_STOCK_CANCEL,
1519 &ok_button, GTK_STOCK_OK,
1522 gtk_box_pack_end(GTK_BOX(action_area), confirm_area, FALSE, FALSE, 0);
1523 gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
1525 g_signal_connect(G_OBJECT(ok_button), "clicked",
1526 G_CALLBACK(replace_with_supplied_word_cb),
1528 g_signal_connect_swapped(G_OBJECT(ok_button), "clicked",
1529 G_CALLBACK(gtk_widget_destroy),
1532 g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked",
1533 G_CALLBACK(gtk_widget_destroy),
1536 gtk_widget_grab_focus(entry);
1538 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1540 gtk_widget_show_all(dialog);
1543 static void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1545 GtkTextView *gtktext;
1546 GtkTextBuffer *buffer;
1547 GtkTextIter startiter, enditer;
1549 gtktext = gtkaspell->gtktext;
1551 buffer = gtk_text_view_get_buffer(gtktext);
1552 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, 0);
1553 gtk_text_buffer_get_iter_at_offset(buffer, &enditer,
1554 get_textview_buffer_charcount(gtktext)-1);
1555 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
1556 &startiter, &enditer);
1559 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1561 GtkAspell *gtkaspell = (GtkAspell *) data;
1563 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1565 if (!gtkaspell->check_while_typing)
1566 gtkaspell_uncheck_all(gtkaspell);
1567 if (gtkaspell->menu_changed_cb)
1568 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
1571 static GSList *create_empty_dictionary_list(void)
1573 GSList *list = NULL;
1576 dict = g_new0(Dictionary, 1);
1577 dict->fullname = g_strdup(_("None"));
1578 dict->dictname = NULL;
1580 return g_slist_append(list, dict);
1583 static void list_dict_cb(const char * const lang_tag,
1584 const char * const provider_name,
1585 const char * const provider_desc,
1586 const char * const provider_file,
1589 GSList **list = (GSList **)data;
1590 Dictionary *dict = g_new0(Dictionary, 1);
1591 dict->fullname = g_strdup(lang_tag);
1592 dict->dictname = g_strdup(lang_tag);
1594 if (g_slist_find_custom(*list, dict,
1595 (GCompareFunc) compare_dict) == NULL) {
1596 debug_print("Aspell: found dictionary %s %s\n", dict->fullname,
1598 *list = g_slist_insert_sorted(*list, dict,
1599 (GCompareFunc) compare_dict);
1601 dictionary_delete(dict);
1605 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1606 static GSList *gtkaspell_get_dictionary_list(gint refresh)
1609 EnchantBroker *broker;
1611 if (!gtkaspellcheckers)
1612 gtkaspell_checkers_init();
1614 if (gtkaspellcheckers->dictionary_list && !refresh)
1615 return gtkaspellcheckers->dictionary_list;
1617 gtkaspell_free_dictionary_list(
1618 gtkaspellcheckers->dictionary_list);
1621 broker = enchant_broker_init();
1623 enchant_broker_list_dicts(broker, list_dict_cb, &list);
1625 enchant_broker_free(broker);
1629 debug_print("Aspell: error when searching for dictionaries: "
1630 "No dictionary found.\n");
1631 list = create_empty_dictionary_list();
1634 gtkaspellcheckers->dictionary_list = list;
1639 static void gtkaspell_free_dictionary_list(GSList *list)
1643 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1645 dict = (Dictionary *) walk->data;
1646 dictionary_delete(dict);
1651 GtkTreeModel *gtkaspell_dictionary_store_new_with_refresh(gboolean refresh)
1653 GSList *dict_list, *tmp;
1654 GtkListStore *store;
1658 dict_list = gtkaspell_get_dictionary_list(refresh);
1659 cm_return_val_if_fail(dict_list, NULL);
1661 store = gtk_list_store_new(SET_GTKASPELL_SIZE,
1666 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1667 dict = (Dictionary *) tmp->data;
1669 gtk_list_store_append(store, &iter);
1670 gtk_list_store_set(store, &iter,
1671 SET_GTKASPELL_NAME, dict->dictname,
1672 SET_GTKASPELL_FULLNAME, dict->fullname,
1676 return GTK_TREE_MODEL(store);
1679 GtkTreeModel *gtkaspell_dictionary_store_new(void)
1681 return gtkaspell_dictionary_store_new_with_refresh
1685 GtkWidget *gtkaspell_dictionary_combo_new(const gboolean refresh)
1688 GtkCellRenderer *renderer;
1690 combo = gtk_combo_box_new_with_model(
1691 gtkaspell_dictionary_store_new_with_refresh(refresh));
1692 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1693 gtk_widget_show(combo);
1695 renderer = gtk_cell_renderer_text_new();
1696 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
1697 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),renderer,
1698 "text", SET_GTKASPELL_NAME, NULL);
1703 gchar *gtkaspell_get_dictionary_menu_active_item(GtkComboBox *combo)
1706 GtkTreeModel *model;
1707 gchar *dict_fullname = NULL;
1709 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo), NULL);
1710 cm_return_val_if_fail(gtk_combo_box_get_active_iter(combo, &iter), NULL);
1712 model = gtk_combo_box_get_model(combo);
1716 gtk_tree_model_get(model, &iter,
1717 SET_GTKASPELL_FULLNAME, &dict_fullname,
1720 return dict_fullname;
1723 gint gtkaspell_set_dictionary_menu_active_item(GtkComboBox *combo,
1724 const gchar *dictionary)
1726 GtkTreeModel *model;
1728 gchar *dict_name = NULL;
1730 cm_return_val_if_fail(combo != NULL, 0);
1731 cm_return_val_if_fail(dictionary != NULL, 0);
1732 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo), 0);
1734 if((model = gtk_combo_box_get_model(combo)) == NULL)
1736 if((gtk_tree_model_get_iter_first(model, &iter)) == FALSE)
1740 gtk_tree_model_get(model, &iter,
1741 SET_GTKASPELL_FULLNAME, &dict_name,
1744 if ((dict_name != NULL) && !g_strcmp0(dict_name, dictionary)) {
1745 gtk_combo_box_set_active_iter(combo, &iter);
1752 } while ((gtk_tree_model_iter_next(model, &iter)) == TRUE);
1757 void gtkaspell_use_alternate_dict(GtkAspell *gtkaspell)
1761 tmp = gtkaspell->gtkaspeller;
1762 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1763 gtkaspell->alternate_speller = tmp;
1766 static void destroy_menu(GtkWidget *widget,
1767 gpointer user_data) {
1768 GtkAspell *gtkaspell = (GtkAspell *)user_data;
1770 if (gtkaspell->accel_group) {
1771 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1772 gtkaspell->accel_group);
1773 gtkaspell->accel_group = NULL;
1777 static gboolean aspell_key_pressed(GtkWidget *widget,
1779 GtkAspell *gtkaspell)
1781 if (event && (isascii(event->keyval) || event->keyval == GDK_KEY_Return)) {
1782 gtk_accel_groups_activate(
1783 G_OBJECT(gtkaspell->parent_window),
1784 event->keyval, event->state);
1785 } else if (event && event->keyval == GDK_KEY_Escape) {
1786 destroy_menu(NULL, gtkaspell);
1791 /* Create a paged submenu with choice of available dictionaries */
1792 static GtkWidget *make_dictionary_list_submenu(GtkAspell *gtkaspell)
1794 GtkWidget *menu, *curmenu, *moremenu, *item;
1800 if (gtkaspellcheckers->dictionary_list == NULL)
1801 gtkaspell_get_dictionary_list(FALSE);
1803 menu = gtk_menu_new();
1806 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
1807 tmp = g_slist_next(tmp)) {
1808 if (count == MENUCOUNT) {
1810 moremenu = gtk_menu_new();
1811 item = gtk_menu_item_new_with_label(_("More..."));
1812 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1815 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1819 dict = (Dictionary *) tmp->data;
1820 item = gtk_check_menu_item_new_with_label(dict->fullname);
1821 g_object_set_data(G_OBJECT(item), "dict_name",
1823 if (g_strcmp0(dict->fullname,
1824 gtkaspell->gtkaspeller->dictionary->fullname))
1825 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1827 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1828 gtk_widget_set_sensitive(GTK_WIDGET(item),
1831 g_signal_connect(G_OBJECT(item), "activate",
1832 G_CALLBACK(change_dict_cb),
1834 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1839 gtk_widget_show_all(menu);
1843 /* make_sug_menu() - Add menus to accept this word for this session
1844 * and to add it to personal dictionary
1846 static GSList *make_sug_menu(GtkAspell *gtkaspell)
1848 GtkWidget *item, *submenu;
1850 GtkAccelGroup *accel;
1851 GList *l = gtkaspell->suggestions_list;
1853 GSList *list = NULL;
1858 accel = gtk_accel_group_new();
1860 if (gtkaspell->accel_group) {
1861 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1862 gtkaspell->accel_group);
1863 gtkaspell->accel_group = NULL;
1866 utf8buf = g_strdup(l->data);
1867 caption = g_strdup_printf(_("\"%s\" unknown in dictionary '%s'"),
1869 gtkaspell->gtkaspeller->dictionary->dictname);
1870 item = gtk_menu_item_new_with_label(caption);
1871 submenu = make_dictionary_list_submenu(gtkaspell);
1872 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
1874 gtk_widget_show(item);
1875 list = g_slist_append(list, item);
1876 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
1879 item = gtk_menu_item_new();
1880 gtk_widget_show(item);
1881 list = g_slist_append(list, item);
1883 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1884 gtk_widget_show(item);
1885 list = g_slist_append(list, item);
1886 g_signal_connect(G_OBJECT(item), "activate",
1887 G_CALLBACK(add_word_to_session_cb),
1889 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_space,
1891 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1893 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1894 gtk_widget_show(item);
1895 list = g_slist_append(list, item);
1896 g_signal_connect(G_OBJECT(item), "activate",
1897 G_CALLBACK(add_word_to_personal_cb),
1899 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_Return,
1901 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1903 item = gtk_menu_item_new_with_label(_("Replace with..."));
1904 gtk_widget_show(item);
1905 list = g_slist_append(list, item);
1906 g_signal_connect(G_OBJECT(item), "activate",
1907 G_CALLBACK(replace_with_create_dialog_cb),
1909 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_R, 0,
1910 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1911 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_R,
1915 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1916 caption = g_strdup_printf(_("Check with %s"),
1917 gtkaspell->alternate_speller->dictionary->dictname);
1918 item = gtk_menu_item_new_with_label(caption);
1920 gtk_widget_show(item);
1921 list = g_slist_append(list, item);
1922 g_signal_connect(G_OBJECT(item), "activate",
1923 G_CALLBACK(check_with_alternate_cb),
1925 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_X, 0,
1926 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1927 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_X,
1932 item = gtk_menu_item_new();
1933 gtk_widget_show(item);
1934 list = g_slist_append(list, item);
1938 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
1939 gtk_widget_show(item);
1940 list = g_slist_append(list, item);
1942 GtkWidget *curmenu = NULL;
1946 if (count == MENUCOUNT) {
1949 item = gtk_menu_item_new_with_label(_("More..."));
1950 gtk_widget_show(item);
1952 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1954 list = g_slist_append(list, item);
1956 curmenu = gtk_menu_new();
1957 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1961 utf8buf = g_strdup(l->data);
1963 item = gtk_menu_item_new_with_label(utf8buf);
1965 gtk_widget_show(item);
1966 if (curmenu == NULL) {
1967 list = g_slist_append(list, item);
1969 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1971 g_signal_connect(G_OBJECT(item), "activate",
1972 G_CALLBACK(replace_word_cb),
1975 if (curmenu == NULL && count < MENUCOUNT) {
1976 gtk_widget_add_accelerator(item, "activate",
1978 GDK_KEY_A + count, 0,
1981 gtk_widget_add_accelerator(item, "activate",
1990 } while ((l = l->next) != NULL);
1993 gtk_window_add_accel_group
1994 (GTK_WINDOW(gtkaspell->parent_window),
1996 gtkaspell->accel_group = accel;
2001 static GSList *populate_submenu(GtkAspell *gtkaspell)
2003 GtkWidget *item, *submenu, *both_dicts_item;
2005 GtkAspeller *gtkaspeller = NULL;
2006 GSList *list = NULL;
2011 gtkaspeller = gtkaspell->gtkaspeller;
2012 dictname = g_strdup_printf(_("Dictionary: %s"),
2013 gtkaspeller->dictionary->dictname);
2014 item = gtk_menu_item_new_with_label(dictname);
2015 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
2017 submenu = make_dictionary_list_submenu(gtkaspell);
2018 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
2019 gtk_widget_show(item);
2020 list = g_slist_append(list, item);
2022 item = gtk_menu_item_new();
2023 gtk_widget_show(item);
2024 list = g_slist_append(list, item);
2026 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
2027 dictname = g_strdup_printf(_("Use alternate (%s)"),
2028 gtkaspell->alternate_speller->dictionary->dictname);
2029 item = gtk_menu_item_new_with_label(dictname);
2031 g_signal_connect(G_OBJECT(item), "activate",
2032 G_CALLBACK(switch_to_alternate_cb),
2034 gtk_widget_show(item);
2035 list = g_slist_append(list, item);
2038 both_dicts_item = gtk_check_menu_item_new_with_label(_("Use both dictionaries"));
2039 if (gtkaspell->use_both_dicts && gtkaspell->use_alternate) {
2040 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(both_dicts_item), TRUE);
2042 gtk_widget_set_sensitive(both_dicts_item, gtkaspell->use_alternate);
2044 g_signal_connect(G_OBJECT(both_dicts_item), "activate",
2045 G_CALLBACK(set_use_both_cb),
2047 gtk_widget_show(both_dicts_item);
2048 list = g_slist_append(list, both_dicts_item);
2050 item = gtk_menu_item_new();
2051 gtk_widget_show(item);
2052 list = g_slist_append(list, item);
2054 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
2055 if (gtkaspell->check_while_typing)
2056 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2058 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2059 g_signal_connect(G_OBJECT(item), "activate",
2060 G_CALLBACK(toggle_check_while_typing_cb),
2062 gtk_widget_show(item);
2063 list = g_slist_append(list, item);
2068 GSList *gtkaspell_make_config_menu(GtkAspell *gtkaspell)
2070 return populate_submenu(gtkaspell);
2073 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y,
2074 gboolean *push_in, gpointer data)
2076 GtkAspell *gtkaspell = (GtkAspell *) data;
2077 gint xx = 0, yy = 0;
2080 GtkTextView *text = GTK_TEXT_VIEW(gtkaspell->gtktext);
2081 GtkTextBuffer *textbuf;
2086 textbuf = gtk_text_view_get_buffer(gtkaspell->gtktext);
2087 gtk_text_buffer_get_iter_at_mark(textbuf, &iter,
2088 gtk_text_buffer_get_insert(textbuf));
2089 gtk_text_view_get_iter_location(gtkaspell->gtktext, &iter, &rect);
2090 gtk_text_view_buffer_to_window_coords(text, GTK_TEXT_WINDOW_TEXT,
2094 gdk_window_get_origin(gtk_widget_get_window(GTK_WIDGET(gtkaspell->gtktext)),
2097 sx = gdk_screen_width();
2098 sy = gdk_screen_height();
2100 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2105 *x = rect.x + xx + 8;
2107 *y = rect.y + rect.height + yy;
2115 /* change the current dictionary of gtkaspell
2116 - if always_set_alt_dict is set, the alternate dict is unconditionally set to the former
2117 current dictionary (common use: from menu callbacks)
2118 - if always_set_alt_dict is NOT set, the alternate dict will be set to the former
2119 current dictionary only if there is no alternate dictionary already set
2120 (this is when we need to set the current dictionary then the alternate one
2121 when creating a compose window, from the account and folder settings)
2123 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary,
2124 gboolean always_set_alt_dict)
2127 GtkAspeller *gtkaspeller;
2129 cm_return_val_if_fail(gtkaspell, FALSE);
2130 cm_return_val_if_fail(dictionary, FALSE);
2132 dict = g_new0(Dictionary, 1);
2134 if (strrchr(dictionary, '/')) {
2135 dict->fullname = g_strdup(strrchr(dictionary, '/')+1);
2136 dict->dictname = g_strdup(strrchr(dictionary, '/')+1);
2138 dict->fullname = g_strdup(dictionary);
2139 dict->dictname = g_strdup(dictionary);
2142 if (dict->fullname && strchr(dict->fullname, '-')) {
2143 *(strchr(dict->fullname, '-')) = '\0';
2144 *(strchr(dict->dictname, '-')) = '\0';
2147 if (!dict->fullname || !(*dict->fullname)) {
2148 dictionary_delete(dict);
2151 gtkaspeller = gtkaspeller_new(dict);
2154 alertpanel_warning(_("The spell checker could not change dictionary.\n%s"),
2155 gtkaspellcheckers->error_message);
2157 if (gtkaspell->use_alternate) {
2158 if (gtkaspell->alternate_speller) {
2159 if (always_set_alt_dict) {
2160 gtkaspeller_delete(gtkaspell->alternate_speller);
2161 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2163 gtkaspeller_delete(gtkaspell->gtkaspeller);
2165 /* should never be reached as the dicts are always set
2166 to a default value */
2167 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2169 gtkaspeller_delete(gtkaspell->gtkaspeller);
2171 gtkaspell->gtkaspeller = gtkaspeller;
2174 dictionary_delete(dict);
2179 /* change the alternate dictionary of gtkaspell (doesn't affect the default dictionary) */
2180 gboolean gtkaspell_change_alt_dict(GtkAspell *gtkaspell, const gchar *alt_dictionary)
2183 GtkAspeller *gtkaspeller;
2185 cm_return_val_if_fail(gtkaspell, FALSE);
2186 cm_return_val_if_fail(alt_dictionary, FALSE);
2188 dict = g_new0(Dictionary, 1);
2189 if (strrchr(alt_dictionary, '/')) {
2190 dict->fullname = g_strdup(strrchr(alt_dictionary, '/')+1);
2191 dict->dictname = g_strdup(strrchr(alt_dictionary, '/')+1);
2193 dict->fullname = g_strdup(alt_dictionary);
2194 dict->dictname = g_strdup(alt_dictionary);
2197 if (dict->fullname && strchr(dict->fullname, '-')) {
2198 *(strchr(dict->fullname, '-')) = '\0';
2199 *(strchr(dict->dictname, '-')) = '\0';
2202 if (!dict->fullname || !(*dict->fullname)) {
2203 dictionary_delete(dict);
2207 gtkaspeller = gtkaspeller_new(dict);
2210 alertpanel_warning(_("The spell checker could not change the alternate dictionary.\n%s"),
2211 gtkaspellcheckers->error_message);
2213 if (gtkaspell->alternate_speller)
2214 gtkaspeller_delete(gtkaspell->alternate_speller);
2215 gtkaspell->alternate_speller = gtkaspeller;
2218 dictionary_delete(dict);
2223 /* Menu call backs */
2225 /* change_dict_cb() - Menu callback : change dict */
2226 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2230 fullname = (gchar *) g_object_get_data(G_OBJECT(w), "dict_name");
2232 if (!g_strcmp0(fullname, _("None")))
2235 gtkaspell_change_dict(gtkaspell, fullname, TRUE);
2236 gtkaspell_dict_changed(gtkaspell);
2238 if (gtkaspell->menu_changed_cb)
2239 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2242 static void switch_to_alternate_cb(GtkWidget *w,
2245 GtkAspell *gtkaspell = (GtkAspell *) data;
2246 gtkaspell_use_alternate_dict(gtkaspell);
2247 gtkaspell_dict_changed(gtkaspell);
2249 if (gtkaspell->menu_changed_cb)
2250 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2253 /* Misc. helper functions */
2255 static void set_point_continue(GtkAspell *gtkaspell)
2257 gtkaspell->ctx.set_position(gtkaspell->ctx.data, gtkaspell->orig_pos);
2259 if (gtkaspell->continue_check)
2260 gtkaspell->continue_check((gpointer *) gtkaspell->ctx.data);
2263 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2265 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtkaspell->gtktext);
2266 GdkColor *color = &(gtkaspell->highlight);
2268 /* Shameless copy from Sylpheed's gtkutils.c */
2270 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2272 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2274 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2278 gtk_text_buffer_create_tag(buffer, "misspelled",
2279 "foreground-gdk", color, NULL);
2281 gtk_text_buffer_create_tag(buffer, "misspelled",
2282 "underline", PANGO_UNDERLINE_ERROR, NULL);
2286 static void change_color(GtkAspell * gtkaspell,
2287 gint start, gint end,
2291 GtkTextView *gtktext;
2292 GtkTextBuffer *buffer;
2293 GtkTextIter startiter, enditer;
2298 gtktext = gtkaspell->gtktext;
2300 buffer = gtk_text_view_get_buffer(gtktext);
2301 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, start);
2302 gtk_text_buffer_get_iter_at_offset(buffer, &enditer, end);
2304 gtk_text_buffer_apply_tag_by_name(buffer, "misspelled",
2305 &startiter, &enditer);
2307 gtk_text_iter_forward_char(&enditer);
2308 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
2309 &startiter, &enditer);
2313 /* compare_dict () - compare 2 dict names */
2314 static gint compare_dict(Dictionary *a, Dictionary *b)
2316 guint aparts = 0, bparts = 0;
2319 for (i=0; i < strlen(a->dictname); i++)
2320 if (a->dictname[i] == '-')
2322 for (i=0; i < strlen(b->dictname); i++)
2323 if (b->dictname[i] == '-')
2326 if (aparts != bparts)
2327 return (aparts < bparts) ? -1 : +1;
2330 compare = g_strcmp0(a->dictname, b->dictname);
2332 compare = g_strcmp0(a->fullname, b->fullname);
2337 static void dictionary_delete(Dictionary *dict)
2339 g_free(dict->fullname);
2340 g_free(dict->dictname);
2344 static Dictionary *dictionary_dup(const Dictionary *dict)
2348 dict2 = g_new(Dictionary, 1);
2350 dict2->fullname = g_strdup(dict->fullname);
2351 dict2->dictname = g_strdup(dict->dictname);
2356 void gtkaspell_free_suggestions_list(GtkAspell *gtkaspell)
2360 for (list = gtkaspell->suggestions_list; list != NULL;
2364 g_list_free(gtkaspell->suggestions_list);
2366 gtkaspell->max_sug = -1;
2367 gtkaspell->suggestions_list = NULL;
2370 static void reset_theword_data(GtkAspell *gtkaspell)
2372 gtkaspell->start_pos = 0;
2373 gtkaspell->end_pos = 0;
2374 gtkaspell->theword[0] = 0;
2375 gtkaspell->max_sug = -1;
2377 gtkaspell_free_suggestions_list(gtkaspell);
2380 static void free_checkers(gpointer elt, gpointer data)
2382 GtkAspeller *gtkaspeller = elt;
2384 cm_return_if_fail(gtkaspeller);
2386 gtkaspeller_real_delete(gtkaspeller);
2389 gchar *gtkaspell_get_default_dictionary(GtkAspell *gtkaspell)
2391 if (gtkaspell && gtkaspell->gtkaspeller &&
2392 gtkaspell->gtkaspeller->dictionary)
2393 return gtkaspell->gtkaspeller->dictionary->dictname;