1 /* gtkaspell - a spell-checking addon for GtkText
2 * Copyright (c) 2000 Evan Martin (original code for ispell).
3 * Copyright (c) 2002 Melvin Hadasht.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 3 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 * Stuphead: (C) 2000,2001 Grigroy Bakunov, Sergey Pinaev
20 * Adapted for Sylpheed (Claws) (c) 2001-2002 by Hiroyuki Yamamoto &
21 * The Claws Mail Team.
22 * Adapted for pspell (c) 2001-2002 Melvin Hadasht
23 * Adapted for GNU/aspell (c) 2002 Melvin Hadasht
35 #include <sys/types.h>
47 #include <glib/gi18n.h>
51 #include <gdk/gdkkeysyms.h>
57 #include "alertpanel.h"
58 #include "gtkaspell.h"
59 #include "gtk/gtkutils.h"
60 #include "gtk/combobox.h"
62 #define ASPELL_FASTMODE 1
63 #define ASPELL_NORMALMODE 2
64 #define ASPELL_BADSPELLERMODE 3
66 #define GTKASPELLWORDSIZE 1024
68 /* size of the text buffer used in various word-processing routines. */
71 /* number of suggestions to display on each menu. */
74 /* 'config' must be defined as a 'AspellConfig *' */
75 #define RETURN_FALSE_IF_CONFIG_ERROR() \
77 if (aspell_config_error_number(config) != 0) { \
78 gtkaspellcheckers->error_message = g_strdup(aspell_config_error_message(config)); \
83 #define CONFIG_REPLACE_RETURN_FALSE_IF_FAIL(option, value) { \
84 aspell_config_replace(config, option, value); \
85 RETURN_FALSE_IF_CONFIG_ERROR(); \
89 SET_GTKASPELL_NAME = 0,
90 SET_GTKASPELL_FULLNAME = 1,
94 typedef struct _GtkAspellCheckers {
96 GSList *dictionary_list;
100 typedef struct _Dictionary {
106 typedef struct _GtkAspeller {
107 Dictionary *dictionary;
109 AspellConfig *config;
110 AspellSpeller *checker;
113 typedef void (*ContCheckFunc) (gpointer *gtkaspell);
117 GtkAspeller *gtkaspeller;
118 GtkAspeller *alternate_speller;
119 gchar *dictionary_path;
120 gchar theword[GTKASPELLWORDSIZE];
126 gboolean check_while_typing;
127 gboolean recheck_when_changing_dict;
128 gboolean use_alternate;
129 gboolean use_both_dicts;
131 ContCheckFunc continue_check;
133 GtkWidget *replace_entry;
134 GtkWidget *parent_window;
136 gint default_sug_mode;
138 GList *suggestions_list;
140 GtkTextView *gtktext;
142 GtkAccelGroup *accel_group;
143 void (*menu_changed_cb)(void *data);
144 void *menu_changed_data;
147 typedef AspellConfig GtkAspellConfig;
149 /******************************************************************************/
151 static GtkAspellCheckers *gtkaspellcheckers;
153 /* Error message storage */
154 static void gtkaspell_checkers_error_message (gchar *message);
157 static void entry_insert_cb (GtkTextBuffer *textbuf,
161 GtkAspell *gtkaspell);
162 static void entry_delete_cb (GtkTextBuffer *textbuf,
163 GtkTextIter *startiter,
164 GtkTextIter *enditer,
165 GtkAspell *gtkaspell);
166 /*static gint button_press_intercept_cb (GtkTextView *gtktext,
168 GtkAspell *gtkaspell);
170 static void button_press_intercept_cb(GtkTextView *gtktext,
171 GtkMenu *menu, GtkAspell *gtkaspell);
173 /* Checker creation */
174 static GtkAspeller* gtkaspeller_new (Dictionary *dict);
175 static GtkAspeller* gtkaspeller_real_new (Dictionary *dict);
176 static GtkAspeller* gtkaspeller_delete (GtkAspeller *gtkaspeller);
177 static GtkAspeller* gtkaspeller_real_delete (GtkAspeller *gtkaspeller);
179 /* Checker configuration */
180 static gint set_dictionary (AspellConfig *config,
182 static void set_sug_mode_cb (GtkMenuItem *w,
183 GtkAspell *gtkaspell);
184 static void set_use_both_cb (GtkMenuItem *w,
185 GtkAspell *gtkaspell);
186 static void set_real_sug_mode (GtkAspell *gtkaspell,
187 const char *themode);
189 /* Checker actions */
190 static gboolean check_at (GtkAspell *gtkaspell,
192 static gboolean check_next_prev (GtkAspell *gtkaspell,
194 static GList* misspelled_suggest (GtkAspell *gtkaspell,
196 static void add_word_to_session_cb (GtkWidget *w,
198 static void add_word_to_personal_cb (GtkWidget *w,
200 static void replace_with_create_dialog_cb (GtkWidget *w,
202 static void replace_with_supplied_word_cb (GtkWidget *w,
203 GtkAspell *gtkaspell);
204 static void replace_word_cb (GtkWidget *w,
206 static void replace_real_word (GtkAspell *gtkaspell,
208 static void check_with_alternate_cb (GtkWidget *w,
210 static void use_alternate_dict (GtkAspell *gtkaspell);
211 static void toggle_check_while_typing_cb (GtkWidget *w,
215 static GSList* make_sug_menu (GtkAspell *gtkaspell);
216 static GSList * populate_submenu (GtkAspell *gtkaspell);
217 GSList* gtkaspell_make_config_menu (GtkAspell *gtkaspell);
218 static void set_menu_pos (GtkMenu *menu,
223 /* Other menu callbacks */
224 static gboolean aspell_key_pressed (GtkWidget *widget,
226 GtkAspell *gtkaspell);
227 static void change_dict_cb (GtkWidget *w,
228 GtkAspell *gtkaspell);
229 static void switch_to_alternate_cb (GtkWidget *w,
232 /* Misc. helper functions */
233 static void set_point_continue (GtkAspell *gtkaspell);
234 static void continue_check (gpointer *gtkaspell);
235 static gboolean iswordsep (gunichar c);
236 static gunichar get_text_index_whar (GtkAspell *gtkaspell,
238 static gboolean get_word_from_pos (GtkAspell *gtkaspell,
244 static void allocate_color (GtkAspell *gtkaspell,
246 static void change_color (GtkAspell *gtkaspell,
251 static gchar* convert_to_aspell_encoding (const gchar *encoding);
252 static gint compare_dict (Dictionary *a,
254 static void dictionary_delete (Dictionary *dict);
255 static Dictionary * dictionary_dup (const Dictionary *dict);
256 static void free_suggestions_list (GtkAspell *gtkaspell);
257 static void reset_theword_data (GtkAspell *gtkaspell);
258 static void free_checkers (gpointer elt,
260 static gint find_gtkaspeller (gconstpointer aa,
262 /* gtkspellconfig - only one config per session */
263 GtkAspellConfig * gtkaspellconfig;
264 static void destroy_menu(GtkWidget *widget, gpointer user_data);
266 /******************************************************************************/
267 static gint get_textview_buffer_charcount(GtkTextView *view);
269 static void gtkaspell_free_dictionary_list (GSList *list);
270 static GSList* gtkaspell_get_dictionary_list (const char *aspell_path,
273 static void gtkaspell_uncheck_all (GtkAspell *gtkaspell);
275 static gint get_textview_buffer_charcount(GtkTextView *view)
277 GtkTextBuffer *buffer;
279 g_return_val_if_fail(view, 0);
281 buffer = gtk_text_view_get_buffer(view);
282 g_return_val_if_fail(buffer, 0);
284 return gtk_text_buffer_get_char_count(buffer);
286 static gint get_textview_buffer_offset(GtkTextView *view)
288 GtkTextBuffer * buffer;
292 g_return_val_if_fail(view, 0);
294 buffer = gtk_text_view_get_buffer(view);
295 g_return_val_if_fail(buffer, 0);
297 mark = gtk_text_buffer_get_insert(buffer);
298 g_return_val_if_fail(mark, 0);
300 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
302 return gtk_text_iter_get_offset(&iter);
304 static void set_textview_buffer_offset(GtkTextView *view, gint offset)
306 GtkTextBuffer *buffer;
309 g_return_if_fail(view);
311 buffer = gtk_text_view_get_buffer(view);
312 g_return_if_fail(buffer);
314 gtk_text_buffer_get_iter_at_offset(buffer, &iter, offset);
315 gtk_text_buffer_place_cursor(buffer, &iter);
317 /******************************************************************************/
319 void gtkaspell_checkers_init(void)
321 gtkaspellcheckers = g_new(GtkAspellCheckers, 1);
322 gtkaspellcheckers->checkers = NULL;
323 gtkaspellcheckers->dictionary_list = NULL;
324 gtkaspellcheckers->error_message = NULL;
327 void gtkaspell_checkers_quit(void)
332 if (gtkaspellcheckers == NULL)
335 if ((checkers = gtkaspellcheckers->checkers)) {
336 debug_print("Aspell: number of running checkers to delete %d\n",
337 g_slist_length(checkers));
339 g_slist_foreach(checkers, free_checkers, NULL);
340 g_slist_free(checkers);
341 gtkaspellcheckers->checkers = NULL;
344 if ((dict_list = gtkaspellcheckers->dictionary_list)) {
345 debug_print("Aspell: number of dictionaries to delete %d\n",
346 g_slist_length(dict_list));
348 gtkaspell_free_dictionary_list(dict_list);
349 gtkaspellcheckers->dictionary_list = NULL;
352 g_free(gtkaspellcheckers->error_message);
353 gtkaspellcheckers->error_message = NULL;
357 static void gtkaspell_checkers_error_message (gchar *message)
360 if (gtkaspellcheckers->error_message) {
361 tmp = g_strdup_printf("%s\n%s",
362 gtkaspellcheckers->error_message,
365 g_free(gtkaspellcheckers->error_message);
366 gtkaspellcheckers->error_message = tmp;
368 gtkaspellcheckers->error_message = message;
371 const char *gtkaspell_checkers_strerror(void)
373 g_return_val_if_fail(gtkaspellcheckers, "");
374 return gtkaspellcheckers->error_message;
377 void gtkaspell_checkers_reset_error(void)
379 g_return_if_fail(gtkaspellcheckers);
381 g_free(gtkaspellcheckers->error_message);
383 gtkaspellcheckers->error_message = NULL;
386 GtkAspell *gtkaspell_new(const gchar *dictionary_path,
387 const gchar *dictionary,
388 const gchar *alt_dictionary,
389 const gchar *encoding,
390 gint misspelled_color,
391 gboolean check_while_typing,
392 gboolean recheck_when_changing_dict,
393 gboolean use_alternate,
394 gboolean use_both_dicts,
395 GtkTextView *gtktext,
396 GtkWindow *parent_win,
397 void (*spell_menu_cb)(void *data),
401 GtkAspell *gtkaspell;
402 GtkAspeller *gtkaspeller;
403 GtkTextBuffer *buffer;
405 g_return_val_if_fail(gtktext, NULL);
406 g_return_val_if_fail(dictionary && strlen(dictionary) > 0,
409 g_return_val_if_fail(dictionary_path && strlen(dictionary_path) > 0,
412 buffer = gtk_text_view_get_buffer(gtktext);
414 dict = g_new0(Dictionary, 1);
415 dict->fullname = g_strdup(dictionary);
416 dict->encoding = g_strdup(encoding);
418 gtkaspeller = gtkaspeller_new(dict);
419 dictionary_delete(dict);
424 gtkaspell = g_new0(GtkAspell, 1);
426 gtkaspell->dictionary_path = g_strdup(dictionary_path);
428 gtkaspell->gtkaspeller = gtkaspeller;
430 if (use_alternate && alt_dictionary && *alt_dictionary) {
431 Dictionary *alt_dict;
432 GtkAspeller *alt_gtkaspeller;
434 alt_dict = g_new0(Dictionary, 1);
435 alt_dict->fullname = g_strdup(alt_dictionary);
436 alt_dict->encoding = g_strdup(encoding);
438 alt_gtkaspeller = gtkaspeller_new(alt_dict);
439 dictionary_delete(alt_dict);
441 if (!alt_gtkaspeller)
444 gtkaspell->alternate_speller = alt_gtkaspeller;
446 gtkaspell->alternate_speller = NULL;
449 gtkaspell->theword[0] = 0x00;
450 gtkaspell->start_pos = 0;
451 gtkaspell->end_pos = 0;
452 gtkaspell->orig_pos = -1;
453 gtkaspell->end_check_pos = -1;
454 gtkaspell->misspelled = -1;
455 gtkaspell->check_while_typing = check_while_typing;
456 gtkaspell->recheck_when_changing_dict = recheck_when_changing_dict;
457 gtkaspell->continue_check = NULL;
458 gtkaspell->replace_entry = NULL;
459 gtkaspell->gtktext = gtktext;
460 gtkaspell->default_sug_mode = ASPELL_FASTMODE;
461 gtkaspell->max_sug = -1;
462 gtkaspell->suggestions_list = NULL;
463 gtkaspell->use_alternate = use_alternate;
464 gtkaspell->use_both_dicts = use_both_dicts;
465 gtkaspell->parent_window = GTK_WIDGET(parent_win);
466 gtkaspell->menu_changed_cb = spell_menu_cb;
467 gtkaspell->menu_changed_data = data;
469 allocate_color(gtkaspell, misspelled_color);
471 g_signal_connect_after(G_OBJECT(buffer), "insert-text",
472 G_CALLBACK(entry_insert_cb), gtkaspell);
473 g_signal_connect_after(G_OBJECT(buffer), "delete-range",
474 G_CALLBACK(entry_delete_cb), gtkaspell);
475 /*g_signal_connect(G_OBJECT(gtktext), "button-press-event",
476 G_CALLBACK(button_press_intercept_cb),
478 g_signal_connect(G_OBJECT(gtktext), "populate-popup",
479 G_CALLBACK(button_press_intercept_cb), gtkaspell);
481 debug_print("Aspell: created gtkaspell %p\n", gtkaspell);
486 void gtkaspell_delete(GtkAspell *gtkaspell)
488 GtkTextView *gtktext = gtkaspell->gtktext;
490 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
491 G_CALLBACK(entry_insert_cb),
493 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
494 G_CALLBACK(entry_delete_cb),
496 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
497 G_CALLBACK(button_press_intercept_cb),
500 gtkaspell_uncheck_all(gtkaspell);
502 gtkaspeller_delete(gtkaspell->gtkaspeller);
504 if (gtkaspell->alternate_speller)
505 gtkaspeller_delete(gtkaspell->alternate_speller);
507 if (gtkaspell->suggestions_list)
508 free_suggestions_list(gtkaspell);
510 g_free((gchar *)gtkaspell->dictionary_path);
511 gtkaspell->dictionary_path = NULL;
513 debug_print("Aspell: deleting gtkaspell %p\n", gtkaspell);
520 static void entry_insert_cb(GtkTextBuffer *textbuf,
524 GtkAspell *gtkaspell)
528 g_return_if_fail(gtkaspell->gtkaspeller->checker);
530 if (!gtkaspell->check_while_typing)
533 pos = gtk_text_iter_get_offset(iter);
535 if (iswordsep(g_utf8_get_char(newtext))) {
536 /* did we just end a word? */
538 check_at(gtkaspell, pos - 2);
540 /* did we just split a word? */
541 if (pos < gtk_text_buffer_get_char_count(textbuf))
542 check_at(gtkaspell, pos + 1);
544 /* check as they type, *except* if they're typing at the end (the most
547 if (pos < gtk_text_buffer_get_char_count(textbuf) &&
548 !iswordsep(get_text_index_whar(gtkaspell, pos))) {
549 check_at(gtkaspell, pos - 1);
554 static void entry_delete_cb(GtkTextBuffer *textbuf,
555 GtkTextIter *startiter,
556 GtkTextIter *enditer,
557 GtkAspell *gtkaspell)
562 g_return_if_fail(gtkaspell->gtkaspeller->checker);
564 if (!gtkaspell->check_while_typing)
567 start = gtk_text_iter_get_offset(startiter);
568 end = gtk_text_iter_get_offset(enditer);
569 origpos = get_textview_buffer_offset(gtkaspell->gtktext);
571 check_at(gtkaspell, start - 1);
572 check_at(gtkaspell, start);
575 set_textview_buffer_offset(gtkaspell->gtktext, origpos);
576 /* this is to *UNDO* the selection, in case they were holding shift
577 * while hitting backspace. */
578 /* needed with textview ??? */
579 /* gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos); */
582 static void button_press_intercept_cb(GtkTextView *gtktext,
583 GtkMenu *menu, GtkAspell *gtkaspell)
585 GtkMenuItem *menuitem;
586 GSList *spell_menu = NULL;
588 gboolean suggest = FALSE;
590 menuitem = GTK_MENU_ITEM(gtk_separator_menu_item_new());
591 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
592 gtk_widget_show(GTK_WIDGET(menuitem));
594 gtktext = gtkaspell->gtktext;
596 gtkaspell->orig_pos = get_textview_buffer_offset(gtktext);
598 if (check_at(gtkaspell, gtkaspell->orig_pos)) {
600 if (misspelled_suggest(gtkaspell, gtkaspell->theword)) {
601 spell_menu = make_sug_menu(gtkaspell);
606 spell_menu = gtkaspell_make_config_menu(gtkaspell);
608 spell_menu = g_slist_reverse(spell_menu);
609 for (items = spell_menu;
610 items; items = items->next) {
611 menuitem = GTK_MENU_ITEM(items->data);
612 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
613 gtk_widget_show(GTK_WIDGET(menuitem));
615 g_slist_free(spell_menu);
617 g_signal_connect(G_OBJECT(menu), "deactivate",
618 G_CALLBACK(destroy_menu),
621 g_signal_connect(G_OBJECT(menu),
623 G_CALLBACK(aspell_key_pressed), gtkaspell);
627 /* Checker creation */
628 static GtkAspeller *gtkaspeller_new(Dictionary *dictionary)
631 GtkAspeller *gtkaspeller = NULL;
635 g_return_val_if_fail(gtkaspellcheckers, NULL);
637 g_return_val_if_fail(dictionary, NULL);
639 if (dictionary->fullname == NULL)
640 gtkaspell_checkers_error_message(
641 g_strdup(_("No dictionary selected.")));
643 g_return_val_if_fail(dictionary->fullname, NULL);
645 if (dictionary->dictname == NULL) {
648 tmp = strrchr(dictionary->fullname, G_DIR_SEPARATOR);
651 dictionary->dictname = dictionary->fullname;
653 dictionary->dictname = tmp + 1;
656 dict = dictionary_dup(dictionary);
658 tmp = g_new0(GtkAspeller, 1);
659 tmp->dictionary = dict;
661 exist = g_slist_find_custom(gtkaspellcheckers->checkers, tmp,
666 if ((gtkaspeller = gtkaspeller_real_new(dict)) != NULL) {
667 gtkaspellcheckers->checkers = g_slist_append(
668 gtkaspellcheckers->checkers,
671 debug_print("Aspell: Created a new gtkaspeller %p\n",
674 dictionary_delete(dict);
676 debug_print("Aspell: Could not create spell checker.\n");
679 debug_print("Aspell: number of existing checkers %d\n",
680 g_slist_length(gtkaspellcheckers->checkers));
685 static GtkAspeller *gtkaspeller_real_new(Dictionary *dict)
687 GtkAspeller *gtkaspeller;
688 AspellConfig *config;
689 AspellCanHaveError *ret;
691 g_return_val_if_fail(gtkaspellcheckers, NULL);
692 g_return_val_if_fail(dict, NULL);
694 gtkaspeller = g_new(GtkAspeller, 1);
696 gtkaspeller->dictionary = dict;
697 gtkaspeller->sug_mode = ASPELL_FASTMODE;
699 config = new_aspell_config();
701 if (!set_dictionary(config, dict))
704 ret = new_aspell_speller(config);
705 delete_aspell_config(config);
707 if (aspell_error_number(ret) != 0) {
708 gtkaspellcheckers->error_message
709 = g_strdup(aspell_error_message(ret));
711 delete_aspell_can_have_error(ret);
716 gtkaspeller->checker = to_aspell_speller(ret);
717 gtkaspeller->config = aspell_speller_config(gtkaspeller->checker);
722 static GtkAspeller *gtkaspeller_delete(GtkAspeller *gtkaspeller)
724 g_return_val_if_fail(gtkaspellcheckers, NULL);
726 gtkaspellcheckers->checkers =
727 g_slist_remove(gtkaspellcheckers->checkers,
730 debug_print("Aspell: Deleting gtkaspeller %p.\n",
733 gtkaspeller_real_delete(gtkaspeller);
735 debug_print("Aspell: number of existing checkers %d\n",
736 g_slist_length(gtkaspellcheckers->checkers));
741 static GtkAspeller *gtkaspeller_real_delete(GtkAspeller *gtkaspeller)
743 g_return_val_if_fail(gtkaspeller, NULL);
744 g_return_val_if_fail(gtkaspeller->checker, NULL);
746 aspell_speller_save_all_word_lists(gtkaspeller->checker);
748 delete_aspell_speller(gtkaspeller->checker);
750 dictionary_delete(gtkaspeller->dictionary);
752 debug_print("Aspell: gtkaspeller %p deleted.\n",
760 /*****************************************************************************/
761 /* Checker configuration */
763 static gboolean set_dictionary(AspellConfig *config, Dictionary *dict)
765 gchar *language = NULL;
766 gchar *jargon = NULL;
770 g_return_val_if_fail(config, FALSE);
771 g_return_val_if_fail(dict, FALSE);
773 strncpy(buf, dict->fullname, BUFSIZE-1);
774 buf[BUFSIZE-1] = 0x00;
776 buf[dict->dictname - dict->fullname] = 0x00;
778 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("dict-dir", buf);
779 debug_print("Aspell: looking for dictionaries in path %s.\n", buf);
781 strncpy(buf, dict->dictname, BUFSIZE-1);
784 if ((size = strrchr(buf, '-')) && isdigit((int) size[1]))
789 if ((jargon = strchr(language, '-')) != NULL)
792 if (size != NULL && jargon == size)
795 debug_print("Aspell: language: %s, jargon: %s, size: %s\n",
796 language, jargon ? jargon : "",
800 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("lang", language);
802 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("jargon", jargon);
804 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("size", size);
805 if (dict->encoding) {
808 aspell_enc = convert_to_aspell_encoding (dict->encoding);
809 aspell_config_replace(config, "encoding",
810 (const char *) aspell_enc);
813 RETURN_FALSE_IF_CONFIG_ERROR();
819 /* set_sug_mode_cb() - Menu callback: Set the suggestion mode */
820 static void set_sug_mode_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
824 themode = (char *) gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN((w)))));
825 themode = g_strdup(themode);
827 set_real_sug_mode(gtkaspell, themode);
829 if (gtkaspell->menu_changed_cb)
830 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
833 /* set_sug_mode_cb() - Menu callback: Set the suggestion mode */
834 static void set_use_both_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
836 gtkaspell->use_both_dicts = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
838 if (gtkaspell->recheck_when_changing_dict) {
839 gtkaspell_highlight_all(gtkaspell);
842 if (gtkaspell->menu_changed_cb)
843 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
846 static void set_real_sug_mode(GtkAspell *gtkaspell, const char *themode)
849 gint mode = ASPELL_FASTMODE;
850 g_return_if_fail(gtkaspell);
851 g_return_if_fail(gtkaspell->gtkaspeller);
852 g_return_if_fail(themode);
854 if (!strcmp(themode,_("Normal Mode")))
855 mode = ASPELL_NORMALMODE;
856 else if (!strcmp( themode,_("Bad Spellers Mode")))
857 mode = ASPELL_BADSPELLERMODE;
859 result = gtkaspell_set_sug_mode(gtkaspell, mode);
862 debug_print("Aspell: error while changing suggestion mode:%s\n",
863 gtkaspellcheckers->error_message);
864 gtkaspell_checkers_reset_error();
868 /* gtkaspell_set_sug_mode() - Set the suggestion mode */
869 gboolean gtkaspell_set_sug_mode(GtkAspell *gtkaspell, gint themode)
871 AspellConfig *config;
873 g_return_val_if_fail(gtkaspell, FALSE);
874 g_return_val_if_fail(gtkaspell->gtkaspeller, FALSE);
875 g_return_val_if_fail(gtkaspell->gtkaspeller->config, FALSE);
877 debug_print("Aspell: setting sug mode of gtkaspeller %p to %d\n",
878 gtkaspell->gtkaspeller, themode);
880 config = gtkaspell->gtkaspeller->config;
883 case ASPELL_FASTMODE:
884 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "fast");
886 case ASPELL_NORMALMODE:
887 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "normal");
889 case ASPELL_BADSPELLERMODE:
890 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode",
894 gtkaspellcheckers->error_message =
895 g_strdup(_("Unknown suggestion mode."));
899 gtkaspell->gtkaspeller->sug_mode = themode;
900 gtkaspell->default_sug_mode = themode;
905 /* misspelled_suggest() - Create a suggestion list for word */
906 static GList *misspelled_suggest(GtkAspell *gtkaspell, gchar *word)
908 const guchar *newword;
910 const AspellWordList *suggestions;
911 AspellStringEnumeration *elements;
913 g_return_val_if_fail(word, NULL);
915 if (!aspell_speller_check(gtkaspell->gtkaspeller->checker, word, -1)) {
916 free_suggestions_list(gtkaspell);
918 suggestions = aspell_speller_suggest(
919 gtkaspell->gtkaspeller->checker,
920 (const char *)word, -1);
921 elements = aspell_word_list_elements(suggestions);
922 list = g_list_append(list, g_strdup(word));
924 while ((newword = (guchar *)aspell_string_enumeration_next(elements)) != NULL)
925 list = g_list_append(list, g_strdup((gchar *)newword));
927 gtkaspell->max_sug = g_list_length(list) - 1;
928 gtkaspell->suggestions_list = list;
933 free_suggestions_list(gtkaspell);
938 /* misspelled_test() - Just test if word is correctly spelled */
939 static int misspelled_test(GtkAspell *gtkaspell, unsigned char *word)
941 gint result = aspell_speller_check(gtkaspell->gtkaspeller->checker, (char *)word, -1)
943 if (result && gtkaspell->use_both_dicts && gtkaspell->alternate_speller) {
944 use_alternate_dict(gtkaspell);
945 result = aspell_speller_check(gtkaspell->gtkaspeller->checker, (char *)word, -1)
947 use_alternate_dict(gtkaspell);
953 static gboolean iswordsep(gunichar c)
955 return (g_unichar_isspace(c) || g_unichar_ispunct(c)) && c != (gunichar)'\'';
958 static gunichar get_text_index_whar(GtkAspell *gtkaspell, int pos)
960 GtkTextView *view = gtkaspell->gtktext;
961 GtkTextBuffer *buffer = gtk_text_view_get_buffer(view);
962 GtkTextIter start, end;
966 gtk_text_buffer_get_iter_at_offset(buffer, &start, pos);
967 gtk_text_buffer_get_iter_at_offset(buffer, &end, pos+1);
969 utf8chars = gtk_text_iter_get_text(&start, &end);
970 if (is_ascii_str(utf8chars)) {
971 a = utf8chars ? (gunichar)utf8chars[0] : '\0' ;
973 gchar *tr = conv_iconv_strdup(utf8chars, CS_UTF_8,
974 gtkaspell->gtkaspeller->dictionary->encoding);
976 a = g_utf8_get_char(tr);
985 /* get_word_from_pos () - return the word pointed to. */
986 /* Handles correctly the quotes. */
987 static gboolean get_word_from_pos(GtkAspell *gtkaspell, gint pos,
988 unsigned char* buf, gint buflen,
989 gint *pstart, gint *pend)
992 /* TODO : when correcting a word into quotes, change the color of */
993 /* the quotes too, as may be they were highlighted before. To do */
994 /* this, we can use two others pointers that points to the whole */
995 /* word including quotes. */
1001 GtkTextView *gtktext;
1003 gtktext = gtkaspell->gtktext;
1004 if (iswordsep(get_text_index_whar(gtkaspell, pos)))
1007 /* The apostrophe character is somtimes used for quotes
1008 * So include it in the word only if it is not surrounded
1009 * by other characters.
1012 for (start = pos; start >= 0; --start) {
1013 c = get_text_index_whar(gtkaspell, start);
1014 if (c == (gunichar)'\'') {
1016 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
1018 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
1020 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
1022 /* start_quote = TRUE; */
1027 /* start_quote = TRUE; */
1031 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
1037 for (end = pos; end < get_textview_buffer_charcount(gtktext); end++) {
1038 c = get_text_index_whar(gtkaspell, end);
1039 if (c == (gunichar)'\'') {
1040 if (end < get_textview_buffer_charcount(gtktext)) {
1041 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
1043 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
1045 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
1047 /* end_quote = TRUE; */
1052 /* end_quote = TRUE; */
1056 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
1066 if (end - start < buflen) {
1067 GtkTextIter iterstart, iterend;
1069 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtktext);
1070 gtk_text_buffer_get_iter_at_offset(buffer, &iterstart, start);
1071 gtk_text_buffer_get_iter_at_offset(buffer, &iterend, end);
1072 tmp = gtk_text_buffer_get_text(buffer, &iterstart, &iterend, FALSE);
1073 conv = conv_iconv_strdup(tmp, CS_UTF_8,
1074 gtkaspell->gtkaspeller->dictionary->encoding);
1076 strncpy((char *)buf, conv, buflen-1);
1086 static gboolean check_at(GtkAspell *gtkaspell, gint from_pos)
1089 unsigned char buf[GTKASPELLWORDSIZE];
1090 GtkTextView *gtktext;
1092 g_return_val_if_fail(from_pos >= 0, FALSE);
1094 gtktext = gtkaspell->gtktext;
1096 if (!get_word_from_pos(gtkaspell, from_pos, buf, sizeof(buf),
1100 if (misspelled_test(gtkaspell, buf)
1101 && strcasecmp((char *)buf, "sylpheed") && strcasecmp((char *)buf, "claws-mail")) {
1102 strncpy(gtkaspell->theword, (gchar *)buf, GTKASPELLWORDSIZE - 1);
1103 gtkaspell->theword[GTKASPELLWORDSIZE - 1] = 0;
1104 gtkaspell->start_pos = start;
1105 gtkaspell->end_pos = end;
1106 free_suggestions_list(gtkaspell);
1108 change_color(gtkaspell, start, end, (gchar *)buf, &(gtkaspell->highlight));
1111 change_color(gtkaspell, start, end, (gchar *)buf, NULL);
1116 static gboolean check_next_prev(GtkAspell *gtkaspell, gboolean forward)
1122 gboolean misspelled;
1125 maxpos = gtkaspell->end_check_pos;
1133 pos = get_textview_buffer_offset(gtkaspell->gtktext);
1134 gtkaspell->orig_pos = pos;
1135 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1136 pos > minpos && pos <= maxpos)
1138 while (!(misspelled = check_at(gtkaspell, pos)) &&
1139 pos > minpos && pos <= maxpos) {
1141 while (!iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1142 pos > minpos && pos <= maxpos)
1145 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1146 pos > minpos && pos <= maxpos)
1152 misspelled_suggest(gtkaspell, gtkaspell->theword);
1155 gtkaspell->orig_pos = gtkaspell->end_pos;
1157 set_textview_buffer_offset(gtkaspell->gtktext,
1158 gtkaspell->end_pos);
1159 /* scroll line to window center */
1160 gtk_text_view_scroll_to_mark(gtkaspell->gtktext,
1161 gtk_text_buffer_get_insert(
1162 gtk_text_view_get_buffer(gtkaspell->gtktext)),
1163 0.0, TRUE, 0.0, 0.5);
1164 /* let textview recalculate coordinates (set_menu_pos) */
1165 while (gtk_events_pending ())
1166 gtk_main_iteration ();
1168 list = make_sug_menu(gtkaspell);
1169 menu = gtk_menu_new();
1170 for (cur = list; cur; cur = cur->next)
1171 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(cur->data));
1173 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1174 set_menu_pos, gtkaspell, 0, GDK_CURRENT_TIME);
1175 g_signal_connect(G_OBJECT(menu), "deactivate",
1176 G_CALLBACK(destroy_menu),
1178 g_signal_connect(G_OBJECT(menu),
1180 G_CALLBACK(aspell_key_pressed), gtkaspell);
1184 reset_theword_data(gtkaspell);
1186 alertpanel_notice(_("No misspelled word found."));
1187 set_textview_buffer_offset(gtkaspell->gtktext,
1188 gtkaspell->orig_pos);
1194 void gtkaspell_check_backwards(GtkAspell *gtkaspell)
1196 gtkaspell->continue_check = NULL;
1197 gtkaspell->end_check_pos =
1198 get_textview_buffer_charcount(gtkaspell->gtktext);
1199 check_next_prev(gtkaspell, FALSE);
1202 void gtkaspell_check_forwards_go(GtkAspell *gtkaspell)
1205 gtkaspell->continue_check = NULL;
1206 gtkaspell->end_check_pos =
1207 get_textview_buffer_charcount(gtkaspell->gtktext);
1208 check_next_prev(gtkaspell, TRUE);
1211 void gtkaspell_check_all(GtkAspell *gtkaspell)
1213 GtkTextView *gtktext;
1215 GtkTextBuffer *buffer;
1216 GtkTextIter startiter, enditer;
1218 g_return_if_fail(gtkaspell);
1219 g_return_if_fail(gtkaspell->gtktext);
1221 gtktext = gtkaspell->gtktext;
1222 buffer = gtk_text_view_get_buffer(gtktext);
1223 gtk_text_buffer_get_selection_bounds(buffer, &startiter, &enditer);
1224 start = gtk_text_iter_get_offset(&startiter);
1225 end = gtk_text_iter_get_offset(&enditer);
1229 end = gtk_text_buffer_get_char_count(buffer);
1230 } else if (start > end) {
1238 set_textview_buffer_offset(gtktext, start);
1240 gtkaspell->continue_check = continue_check;
1241 gtkaspell->end_check_pos = end;
1243 gtkaspell->misspelled = check_next_prev(gtkaspell, TRUE);
1246 static void continue_check(gpointer *data)
1248 GtkAspell *gtkaspell = (GtkAspell *) data;
1249 gint pos = get_textview_buffer_offset(gtkaspell->gtktext);
1250 if (pos < gtkaspell->end_check_pos && gtkaspell->misspelled)
1251 gtkaspell->misspelled = check_next_prev(gtkaspell, TRUE);
1253 gtkaspell->continue_check = NULL;
1256 void gtkaspell_highlight_all(GtkAspell *gtkaspell)
1261 GtkTextView *gtktext;
1263 g_return_if_fail(gtkaspell->gtkaspeller->checker);
1265 gtktext = gtkaspell->gtktext;
1267 len = get_textview_buffer_charcount(gtktext);
1269 origpos = get_textview_buffer_offset(gtktext);
1273 iswordsep(get_text_index_whar(gtkaspell, pos)))
1276 !iswordsep(get_text_index_whar(gtkaspell, pos)))
1279 check_at(gtkaspell, pos - 1);
1281 set_textview_buffer_offset(gtktext, origpos);
1284 static void replace_with_supplied_word_cb(GtkWidget *w, GtkAspell *gtkaspell)
1286 unsigned char *newword;
1287 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1289 newword = (unsigned char *)gtk_editable_get_chars(GTK_EDITABLE(gtkaspell->replace_entry),
1292 if (strcmp((char *)newword, gtkaspell->theword)) {
1293 replace_real_word(gtkaspell, (char *)newword);
1295 if ((e->type == GDK_KEY_PRESS &&
1296 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK)) {
1297 aspell_speller_store_replacement(
1298 gtkaspell->gtkaspeller->checker,
1299 gtkaspell->theword, -1,
1300 (char *)newword, -1);
1302 gtkaspell->replace_entry = NULL;
1307 if (w && GTK_IS_DIALOG(w)) {
1308 gtk_widget_destroy(w);
1311 set_point_continue(gtkaspell);
1315 static void replace_word_cb(GtkWidget *w, gpointer data)
1317 unsigned char *newword;
1318 GtkAspell *gtkaspell = (GtkAspell *) data;
1319 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1321 newword = (unsigned char *) gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN((w)))));
1322 newword = (unsigned char *)g_strdup((char *)newword);
1324 replace_real_word(gtkaspell, (char *)newword);
1326 if ((e->type == GDK_KEY_PRESS &&
1327 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK) ||
1328 (e->type == GDK_BUTTON_RELEASE &&
1329 ((GdkEventButton *) e)->state & GDK_CONTROL_MASK)) {
1330 aspell_speller_store_replacement(
1331 gtkaspell->gtkaspeller->checker,
1332 gtkaspell->theword, -1,
1333 (char *)newword, -1);
1336 gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1338 set_point_continue(gtkaspell);
1342 static void replace_real_word(GtkAspell *gtkaspell, gchar *newword)
1344 int oldlen, newlen, wordlen;
1347 GtkTextView *gtktext;
1348 GtkTextBuffer *textbuf;
1349 GtkTextIter startiter, enditer;
1351 if (!newword) return;
1353 gtktext = gtkaspell->gtktext;
1354 textbuf = gtk_text_view_get_buffer(gtktext);
1356 origpos = gtkaspell->orig_pos;
1358 oldlen = gtkaspell->end_pos - gtkaspell->start_pos;
1359 wordlen = strlen(gtkaspell->theword);
1361 newlen = strlen(newword); /* FIXME: multybyte characters? */
1363 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1364 G_CALLBACK(entry_insert_cb),
1366 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1367 G_CALLBACK(entry_delete_cb),
1370 gtk_text_buffer_get_iter_at_offset(textbuf, &startiter,
1371 gtkaspell->start_pos);
1372 gtk_text_buffer_get_iter_at_offset(textbuf, &enditer,
1373 gtkaspell->end_pos);
1374 g_signal_emit_by_name(G_OBJECT(textbuf), "delete-range",
1375 &startiter, &enditer, gtkaspell);
1376 g_signal_emit_by_name(G_OBJECT(textbuf), "insert-text",
1377 &startiter, newword, newlen, gtkaspell);
1379 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1380 G_CALLBACK(entry_insert_cb),
1382 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1383 G_CALLBACK(entry_delete_cb),
1386 /* Put the point and the position where we clicked with the mouse
1387 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1388 * to let it update correctly the word insertion and then the
1389 * point & position position. If not, SEGV after the first replacement
1390 * If the new word ends before point, put the point at its end.
1393 if (origpos - gtkaspell->start_pos < oldlen &&
1394 origpos - gtkaspell->start_pos >= 0) {
1395 /* Original point was in the word.
1396 * Let it there unless point is going to be outside of the word
1398 if (origpos - gtkaspell->start_pos >= newlen) {
1399 pos = gtkaspell->start_pos + newlen;
1402 else if (origpos >= gtkaspell->end_pos) {
1403 /* move the position according to the change of length */
1404 pos = origpos + newlen - oldlen;
1407 gtkaspell->end_pos = gtkaspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1409 if (get_textview_buffer_charcount(gtktext) < pos)
1410 pos = get_textview_buffer_charcount(gtktext);
1411 gtkaspell->orig_pos = pos;
1413 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1416 /* Accept this word for this session */
1417 static void add_word_to_session_cb(GtkWidget *w, gpointer data)
1420 GtkTextView *gtktext;
1421 GtkAspell *gtkaspell = (GtkAspell *) data;
1422 gtktext = gtkaspell->gtktext;
1424 pos = get_textview_buffer_offset(gtktext);
1426 aspell_speller_add_to_session(gtkaspell->gtkaspeller->checker,
1428 strlen(gtkaspell->theword));
1430 check_at(gtkaspell, gtkaspell->start_pos);
1432 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1434 set_point_continue(gtkaspell);
1437 /* add_word_to_personal_cb() - add word to personal dict. */
1438 static void add_word_to_personal_cb(GtkWidget *w, gpointer data)
1440 GtkAspell *gtkaspell = (GtkAspell *) data;
1442 aspell_speller_add_to_personal(gtkaspell->gtkaspeller->checker,
1444 strlen(gtkaspell->theword));
1446 check_at(gtkaspell, gtkaspell->start_pos);
1448 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1449 set_point_continue(gtkaspell);
1452 static void check_with_alternate_cb(GtkWidget *w, gpointer data)
1454 GtkAspell *gtkaspell = (GtkAspell *) data;
1457 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1459 use_alternate_dict(gtkaspell);
1460 misspelled = check_at(gtkaspell, gtkaspell->start_pos);
1462 if (!gtkaspell->continue_check) {
1464 gtkaspell->misspelled = misspelled;
1466 if (gtkaspell->misspelled) {
1469 misspelled_suggest(gtkaspell, gtkaspell->theword);
1471 set_textview_buffer_offset(gtkaspell->gtktext,
1472 gtkaspell->end_pos);
1474 list = make_sug_menu(gtkaspell);
1475 menu = gtk_menu_new();
1476 for (cur = list; cur; cur = cur->next)
1477 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(cur->data));
1479 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1480 set_menu_pos, gtkaspell, 0,
1482 g_signal_connect(G_OBJECT(menu), "deactivate",
1483 G_CALLBACK(destroy_menu),
1485 g_signal_connect(G_OBJECT(menu),
1487 G_CALLBACK(aspell_key_pressed), gtkaspell);
1491 gtkaspell->orig_pos = gtkaspell->start_pos;
1493 set_point_continue(gtkaspell);
1496 static gboolean replace_key_pressed(GtkWidget *widget,
1498 GtkAspell *gtkaspell)
1500 if (event && event->keyval == GDK_Escape) {
1501 gtk_widget_destroy(widget);
1503 } else if (event && event->keyval == GDK_Return) {
1504 replace_with_supplied_word_cb(widget, gtkaspell);
1510 static void replace_with_create_dialog_cb(GtkWidget *w, gpointer data)
1512 static PangoFontDescription *font_desc;
1519 GtkWidget *ok_button;
1520 GtkWidget *cancel_button;
1521 GtkWidget *confirm_area;
1523 gchar *utf8buf, *thelabel;
1525 GtkAspell *gtkaspell = (GtkAspell *) data;
1527 gdk_window_get_origin((GTK_WIDGET(w)->parent)->window, &xx, &yy);
1529 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1531 dialog = gtk_dialog_new();
1533 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1534 gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1535 gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1536 gtk_window_move(GTK_WINDOW(dialog), xx, yy);
1538 g_signal_connect_swapped(G_OBJECT(dialog), "destroy",
1539 G_CALLBACK(gtk_widget_destroy),
1542 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
1543 hbox = gtk_hbox_new (FALSE, 12);
1544 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1545 gtk_widget_show (hbox);
1546 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
1549 utf8buf = conv_codeset_strdup(gtkaspell->theword,
1550 conv_get_locale_charset_str(),
1553 thelabel = g_strdup_printf(_("<span weight=\"bold\" "
1554 "size=\"larger\">Replace \"%s\" with: </span>"),
1556 /* for title label */
1557 w_hbox = gtk_hbox_new(FALSE, 0);
1559 icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
1560 GTK_ICON_SIZE_DIALOG);
1561 gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
1562 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
1564 vbox = gtk_vbox_new (FALSE, 12);
1565 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1566 gtk_widget_show (vbox);
1568 label = gtk_label_new(thelabel);
1569 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1570 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1571 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1572 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1573 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1577 size = pango_font_description_get_size
1578 (label->style->font_desc);
1579 font_desc = pango_font_description_new();
1580 pango_font_description_set_weight
1581 (font_desc, PANGO_WEIGHT_BOLD);
1582 pango_font_description_set_size
1583 (font_desc, size * PANGO_SCALE_LARGE);
1586 gtk_widget_modify_font(label, font_desc);
1589 entry = gtk_entry_new();
1590 gtkaspell->replace_entry = entry;
1591 gtk_entry_set_text(GTK_ENTRY(entry), utf8buf);
1592 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1593 g_signal_connect(G_OBJECT(dialog),
1595 G_CALLBACK(replace_key_pressed), gtkaspell);
1596 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
1599 label = gtk_label_new(_("Holding down Control key while pressing "
1600 "Enter\nwill learn from mistake.\n"));
1601 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1602 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1603 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1604 gtk_widget_show(label);
1606 hbox = gtk_hbox_new(TRUE, 0);
1608 gtkut_stock_button_set_create(&confirm_area,
1609 &cancel_button, GTK_STOCK_CANCEL,
1610 &ok_button, GTK_STOCK_OK,
1613 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1614 confirm_area, FALSE, FALSE, 0);
1615 gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
1617 g_signal_connect(G_OBJECT(ok_button), "clicked",
1618 G_CALLBACK(replace_with_supplied_word_cb),
1620 g_signal_connect_swapped(G_OBJECT(ok_button), "clicked",
1621 G_CALLBACK(gtk_widget_destroy),
1624 g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked",
1625 G_CALLBACK(gtk_widget_destroy),
1628 gtk_widget_grab_focus(entry);
1630 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1632 gtk_widget_show_all(dialog);
1635 static void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1637 GtkTextView *gtktext;
1638 GtkTextBuffer *buffer;
1639 GtkTextIter startiter, enditer;
1641 gtktext = gtkaspell->gtktext;
1643 buffer = gtk_text_view_get_buffer(gtktext);
1644 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, 0);
1645 gtk_text_buffer_get_iter_at_offset(buffer, &enditer,
1646 get_textview_buffer_charcount(gtktext)-1);
1647 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
1648 &startiter, &enditer);
1651 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1653 GtkAspell *gtkaspell = (GtkAspell *) data;
1655 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1657 if (!gtkaspell->check_while_typing)
1658 gtkaspell_uncheck_all(gtkaspell);
1659 if (gtkaspell->menu_changed_cb)
1660 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
1663 static GSList *create_empty_dictionary_list(void)
1665 GSList *list = NULL;
1668 dict = g_new0(Dictionary, 1);
1669 dict->fullname = g_strdup(_("None"));
1670 dict->dictname = dict->fullname;
1671 dict->encoding = NULL;
1673 return g_slist_append(list, dict);
1676 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1677 static GSList *gtkaspell_get_dictionary_list(const gchar *aspell_path, gint refresh)
1681 AspellConfig *config;
1682 AspellDictInfoList *dlist;
1683 AspellDictInfoEnumeration *dels;
1684 const AspellDictInfo *entry;
1686 if (!gtkaspellcheckers)
1687 gtkaspell_checkers_init();
1689 if (gtkaspellcheckers->dictionary_list && !refresh)
1690 return gtkaspellcheckers->dictionary_list;
1692 gtkaspell_free_dictionary_list(
1693 gtkaspellcheckers->dictionary_list);
1696 config = new_aspell_config();
1698 aspell_config_replace(config, "dict-dir", aspell_path);
1699 if (aspell_config_error_number(config) != 0) {
1700 gtkaspellcheckers->error_message = g_strdup(
1701 aspell_config_error_message(config));
1702 gtkaspellcheckers->dictionary_list =
1703 create_empty_dictionary_list();
1705 return gtkaspellcheckers->dictionary_list;
1708 dlist = get_aspell_dict_info_list(config);
1709 delete_aspell_config(config);
1711 debug_print("Aspell: checking for dictionaries in %s\n", aspell_path?aspell_path:"(null)");
1712 dels = aspell_dict_info_list_elements(dlist);
1713 while ( (entry = aspell_dict_info_enumeration_next(dels)) != 0)
1715 dict = g_new0(Dictionary, 1);
1716 dict->fullname = g_strdup_printf("%s%s", aspell_path,
1718 dict->dictname = dict->fullname + strlen(aspell_path);
1719 dict->encoding = g_strdup(entry->code);
1721 if (g_slist_find_custom(list, dict,
1722 (GCompareFunc) compare_dict) != NULL) {
1723 dictionary_delete(dict);
1727 debug_print("Aspell: found dictionary %s %s %s\n", dict->fullname,
1728 dict->dictname, dict->encoding);
1729 list = g_slist_insert_sorted(list, dict,
1730 (GCompareFunc) compare_dict);
1733 delete_aspell_dict_info_enumeration(dels);
1737 debug_print("Aspell: error when searching for dictionaries: "
1738 "No dictionary found.\n");
1739 list = create_empty_dictionary_list();
1742 gtkaspellcheckers->dictionary_list = list;
1747 static void gtkaspell_free_dictionary_list(GSList *list)
1751 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1753 dict = (Dictionary *) walk->data;
1754 dictionary_delete(dict);
1759 GtkTreeModel *gtkaspell_dictionary_store_new_with_refresh(const gchar *aspell_path,
1762 GSList *dict_list, *tmp;
1763 GtkListStore *store;
1767 dict_list = gtkaspell_get_dictionary_list(aspell_path, refresh);
1768 g_return_val_if_fail(dict_list, NULL);
1770 store = gtk_list_store_new(SET_GTKASPELL_SIZE,
1775 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1776 dict = (Dictionary *) tmp->data;
1778 gtk_list_store_append(store, &iter);
1779 gtk_list_store_set(store, &iter,
1780 SET_GTKASPELL_NAME, dict->dictname,
1781 SET_GTKASPELL_FULLNAME, dict->fullname,
1785 return GTK_TREE_MODEL(store);
1788 GtkTreeModel *gtkaspell_dictionary_store_new(const gchar *aspell_path)
1790 return gtkaspell_dictionary_store_new_with_refresh
1791 (aspell_path, TRUE);
1794 GtkWidget *gtkaspell_dictionary_combo_new(const gchar *aspell_path,
1795 const gboolean refresh)
1798 GtkCellRenderer *renderer;
1800 combo = gtk_combo_box_new_with_model(
1801 gtkaspell_dictionary_store_new_with_refresh(aspell_path, refresh));
1802 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1803 gtk_widget_show(combo);
1805 renderer = gtk_cell_renderer_text_new();
1806 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
1807 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),renderer,
1808 "text", SET_GTKASPELL_NAME, NULL);
1813 gchar *gtkaspell_get_dictionary_menu_active_item(GtkComboBox *combo)
1816 GtkTreeModel *model;
1817 gchar *dict_fullname = NULL;
1819 g_return_val_if_fail(GTK_IS_COMBO_BOX(combo), NULL);
1820 g_return_val_if_fail(gtk_combo_box_get_active_iter(combo, &iter), NULL);
1822 model = gtk_combo_box_get_model(combo);
1826 gtk_tree_model_get(model, &iter,
1827 SET_GTKASPELL_FULLNAME, &dict_fullname,
1830 return dict_fullname;
1833 gint gtkaspell_set_dictionary_menu_active_item(GtkComboBox *combo,
1834 const gchar *dictionary)
1836 GtkTreeModel *model;
1838 gchar *dict_name = NULL;
1840 g_return_val_if_fail(combo != NULL, 0);
1841 g_return_val_if_fail(dictionary != NULL, 0);
1842 g_return_val_if_fail(GTK_IS_COMBO_BOX(combo), 0);
1844 if((model = gtk_combo_box_get_model(combo)) == NULL)
1846 if((gtk_tree_model_get_iter_first(model, &iter)) == FALSE)
1850 gtk_tree_model_get(model, &iter,
1851 SET_GTKASPELL_FULLNAME, &dict_name,
1854 if ((dict_name != NULL) && !strcmp2(dict_name, dictionary)) {
1855 gtk_combo_box_set_active_iter(combo, &iter);
1862 } while ((gtk_tree_model_iter_next(model, &iter)) == TRUE);
1867 GtkWidget *gtkaspell_sugmode_combo_new(gint sugmode)
1869 GtkWidget *combo = gtkut_sc_combobox_create(NULL, FALSE);
1870 GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(
1871 GTK_COMBO_BOX(combo)));
1874 g_return_val_if_fail(store != NULL, NULL);
1876 COMBOBOX_ADD(store, _("Fast Mode"), ASPELL_FASTMODE);
1877 COMBOBOX_ADD(store, _("Normal Mode"), ASPELL_NORMALMODE);
1878 COMBOBOX_ADD(store, _("Bad Spellers Mode"), ASPELL_BADSPELLERMODE);
1880 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), sugmode - 1);
1881 gtk_widget_show(combo);
1886 static void use_alternate_dict(GtkAspell *gtkaspell)
1890 tmp = gtkaspell->gtkaspeller;
1891 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1892 gtkaspell->alternate_speller = tmp;
1895 static void destroy_menu(GtkWidget *widget,
1896 gpointer user_data) {
1898 GtkAspell *gtkaspell = (GtkAspell *)user_data;
1900 if (gtkaspell->accel_group) {
1901 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1902 gtkaspell->accel_group);
1903 gtkaspell->accel_group = NULL;
1907 static gboolean aspell_key_pressed(GtkWidget *widget,
1909 GtkAspell *gtkaspell)
1911 if (event && (isascii(event->keyval) || event->keyval == GDK_Return)) {
1912 gtk_accel_groups_activate(
1913 G_OBJECT(gtkaspell->parent_window),
1914 event->keyval, event->state);
1915 } else if (event && event->keyval == GDK_Escape) {
1916 destroy_menu(NULL, gtkaspell);
1921 /* make_sug_menu() - Add menus to accept this word for this session
1922 * and to add it to personal dictionary
1924 static GSList *make_sug_menu(GtkAspell *gtkaspell)
1928 GtkTextView *gtktext;
1929 GtkAccelGroup *accel;
1930 GList *l = gtkaspell->suggestions_list;
1932 GSList *list = NULL;
1933 gtktext = gtkaspell->gtktext;
1935 accel = gtk_accel_group_new();
1937 if (gtkaspell->accel_group) {
1938 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1939 gtkaspell->accel_group);
1940 gtkaspell->accel_group = NULL;
1943 utf8buf = conv_codeset_strdup((char*)l->data,
1944 conv_get_locale_charset_str(),
1946 caption = g_strdup_printf(_("\"%s\" unknown in %s"),
1948 gtkaspell->gtkaspeller->dictionary->dictname);
1949 item = gtk_menu_item_new_with_label(caption);
1951 gtk_widget_show(item);
1952 list = g_slist_append(list, item);
1953 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
1956 item = gtk_menu_item_new();
1957 gtk_widget_show(item);
1958 list = g_slist_append(list, item);
1960 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1961 gtk_widget_show(item);
1962 list = g_slist_append(list, item);
1963 g_signal_connect(G_OBJECT(item), "activate",
1964 G_CALLBACK(add_word_to_session_cb),
1966 gtk_widget_add_accelerator(item, "activate", accel, GDK_space,
1968 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1970 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1971 gtk_widget_show(item);
1972 list = g_slist_append(list, item);
1973 g_signal_connect(G_OBJECT(item), "activate",
1974 G_CALLBACK(add_word_to_personal_cb),
1976 gtk_widget_add_accelerator(item, "activate", accel, GDK_Return,
1978 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1980 item = gtk_menu_item_new_with_label(_("Replace with..."));
1981 gtk_widget_show(item);
1982 list = g_slist_append(list, item);
1983 g_signal_connect(G_OBJECT(item), "activate",
1984 G_CALLBACK(replace_with_create_dialog_cb),
1986 gtk_widget_add_accelerator(item, "activate", accel, GDK_R, 0,
1987 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1988 gtk_widget_add_accelerator(item, "activate", accel, GDK_R,
1992 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1993 caption = g_strdup_printf(_("Check with %s"),
1994 gtkaspell->alternate_speller->dictionary->dictname);
1995 item = gtk_menu_item_new_with_label(caption);
1997 gtk_widget_show(item);
1998 list = g_slist_append(list, item);
1999 g_signal_connect(G_OBJECT(item), "activate",
2000 G_CALLBACK(check_with_alternate_cb),
2002 gtk_widget_add_accelerator(item, "activate", accel, GDK_X, 0,
2003 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
2004 gtk_widget_add_accelerator(item, "activate", accel, GDK_X,
2009 item = gtk_menu_item_new();
2010 gtk_widget_show(item);
2011 list = g_slist_append(list, item);
2015 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
2016 gtk_widget_show(item);
2017 list = g_slist_append(list, item);
2019 GtkWidget *curmenu = NULL;
2023 if (count == MENUCOUNT) {
2026 item = gtk_menu_item_new_with_label(_("More..."));
2027 gtk_widget_show(item);
2029 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
2031 list = g_slist_append(list, item);
2033 curmenu = gtk_menu_new();
2034 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2038 utf8buf = conv_codeset_strdup((char*)l->data,
2039 conv_get_locale_charset_str(),
2041 item = gtk_menu_item_new_with_label(utf8buf);
2043 gtk_widget_show(item);
2044 if (curmenu == NULL) {
2045 list = g_slist_append(list, item);
2047 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
2049 g_signal_connect(G_OBJECT(item), "activate",
2050 G_CALLBACK(replace_word_cb),
2053 if (curmenu == NULL && count < MENUCOUNT) {
2054 gtk_widget_add_accelerator(item, "activate",
2059 gtk_widget_add_accelerator(item, "activate",
2068 } while ((l = l->next) != NULL);
2071 gtk_window_add_accel_group
2072 (GTK_WINDOW(gtkaspell->parent_window),
2074 gtkaspell->accel_group = accel;
2079 static GSList *populate_submenu(GtkAspell *gtkaspell)
2081 GtkWidget *item, *submenu;
2083 GtkAspeller *gtkaspeller = NULL;
2084 GSList *list = NULL;
2089 gtkaspeller = gtkaspell->gtkaspeller;
2090 dictname = g_strdup_printf(_("Dictionary: %s"),
2091 gtkaspeller->dictionary->dictname);
2092 item = gtk_menu_item_new_with_label(dictname);
2093 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
2095 gtk_widget_show(item);
2096 list = g_slist_append(list, item);
2098 item = gtk_menu_item_new();
2099 gtk_widget_show(item);
2100 list = g_slist_append(list, item);
2102 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
2103 dictname = g_strdup_printf(_("Use alternate (%s)"),
2104 gtkaspell->alternate_speller->dictionary->dictname);
2105 item = gtk_menu_item_new_with_label(dictname);
2107 g_signal_connect(G_OBJECT(item), "activate",
2108 G_CALLBACK(switch_to_alternate_cb),
2110 gtk_widget_show(item);
2111 list = g_slist_append(list, item);
2114 item = gtk_check_menu_item_new_with_label(_("Use both dictionaries"));
2115 if (gtkaspell->use_both_dicts) {
2116 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2118 g_signal_connect(G_OBJECT(item), "activate",
2119 G_CALLBACK(set_use_both_cb),
2121 gtk_widget_show(item);
2122 list = g_slist_append(list, item);
2124 item = gtk_menu_item_new();
2125 gtk_widget_show(item);
2126 list = g_slist_append(list, item);
2128 item = gtk_check_menu_item_new_with_label(_("Fast Mode"));
2129 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
2130 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_FASTMODE) {
2131 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),TRUE);
2132 gtk_widget_set_sensitive(GTK_WIDGET(item),FALSE);
2134 g_signal_connect(G_OBJECT(item), "activate",
2135 G_CALLBACK(set_sug_mode_cb),
2137 gtk_widget_show(item);
2138 list = g_slist_append(list, item);
2140 item = gtk_check_menu_item_new_with_label(_("Normal Mode"));
2141 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
2142 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_NORMALMODE) {
2143 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
2144 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2146 g_signal_connect(G_OBJECT(item), "activate",
2147 G_CALLBACK(set_sug_mode_cb),
2149 gtk_widget_show(item);
2150 list = g_slist_append(list, item);
2152 item = gtk_check_menu_item_new_with_label(_("Bad Spellers Mode"));
2153 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
2154 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_BADSPELLERMODE) {
2155 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
2156 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2158 g_signal_connect(G_OBJECT(item), "activate",
2159 G_CALLBACK(set_sug_mode_cb),
2161 gtk_widget_show(item);
2162 list = g_slist_append(list, item);
2164 item = gtk_menu_item_new();
2165 gtk_widget_show(item);
2166 list = g_slist_append(list, item);
2168 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
2169 if (gtkaspell->check_while_typing)
2170 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2172 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2173 g_signal_connect(G_OBJECT(item), "activate",
2174 G_CALLBACK(toggle_check_while_typing_cb),
2176 gtk_widget_show(item);
2177 list = g_slist_append(list, item);
2179 item = gtk_menu_item_new();
2180 gtk_widget_show(item);
2181 list = g_slist_append(list, item);
2183 submenu = gtk_menu_new();
2184 item = gtk_menu_item_new_with_label(_("Change dictionary"));
2185 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
2186 gtk_widget_show(item);
2187 list = g_slist_append(list, item);
2190 if (gtkaspellcheckers->dictionary_list == NULL)
2191 gtkaspell_get_dictionary_list(gtkaspell->dictionary_path, FALSE);
2193 GtkWidget * curmenu = submenu;
2197 tmp = gtkaspellcheckers->dictionary_list;
2199 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
2200 tmp = g_slist_next(tmp)) {
2201 if (count == MENUCOUNT) {
2204 newmenu = gtk_menu_new();
2205 item = gtk_menu_item_new_with_label(_("More..."));
2206 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2209 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
2210 gtk_widget_show(item);
2214 dict = (Dictionary *) tmp->data;
2215 item = gtk_check_menu_item_new_with_label(dict->dictname);
2216 g_object_set_data(G_OBJECT(item), "dict_name",
2218 if (strcmp2(dict->fullname,
2219 gtkaspell->gtkaspeller->dictionary->fullname))
2220 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2222 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2223 gtk_widget_set_sensitive(GTK_WIDGET(item),
2226 g_signal_connect(G_OBJECT(item), "activate",
2227 G_CALLBACK(change_dict_cb),
2229 gtk_widget_show(item);
2230 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
2238 GSList *gtkaspell_make_config_menu(GtkAspell *gtkaspell)
2240 return populate_submenu(gtkaspell);
2243 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y,
2244 gboolean *push_in, gpointer data)
2246 GtkAspell *gtkaspell = (GtkAspell *) data;
2247 gint xx = 0, yy = 0;
2250 GtkTextView *text = GTK_TEXT_VIEW(gtkaspell->gtktext);
2251 GtkTextBuffer *textbuf;
2256 textbuf = gtk_text_view_get_buffer(gtkaspell->gtktext);
2257 gtk_text_buffer_get_iter_at_mark(textbuf, &iter,
2258 gtk_text_buffer_get_insert(textbuf));
2259 gtk_text_view_get_iter_location(gtkaspell->gtktext, &iter, &rect);
2260 gtk_text_view_buffer_to_window_coords(text, GTK_TEXT_WINDOW_TEXT,
2264 gdk_window_get_origin(GTK_WIDGET(gtkaspell->gtktext)->window, &xx, &yy);
2266 sx = gdk_screen_width();
2267 sy = gdk_screen_height();
2269 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2274 *x = rect.x + xx + 8;
2276 *y = rect.y + rect.height + yy;
2284 /* change the current dictionary of gtkaspell
2285 - if always_set_alt_dict is set, the alternate dict is unconditionally set to the former
2286 current dictionary (common use: from menu callbacks)
2287 - if always_set_alt_dict is NOT set, the alternate dict will be set to the former
2288 current dictionary only if there is no alternate dictionary already set
2289 (this is when we need to set the current dictionary then the alternate one
2290 when creating a compose window, from the account and folder settings)
2292 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary,
2293 gboolean always_set_alt_dict)
2296 GtkAspeller *gtkaspeller;
2299 g_return_val_if_fail(gtkaspell, FALSE);
2300 g_return_val_if_fail(dictionary, FALSE);
2302 sug_mode = gtkaspell->default_sug_mode;
2304 dict = g_new0(Dictionary, 1);
2305 dict->fullname = g_strdup(dictionary);
2306 dict->encoding = g_strdup(gtkaspell->gtkaspeller->dictionary->encoding);
2308 gtkaspeller = gtkaspeller_new(dict);
2311 alertpanel_warning(_("The spell checker could not change dictionary.\n%s"),
2312 gtkaspellcheckers->error_message);
2314 if (gtkaspell->use_alternate) {
2315 if (gtkaspell->alternate_speller) {
2316 if (always_set_alt_dict) {
2317 gtkaspeller_delete(gtkaspell->alternate_speller);
2318 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2320 gtkaspeller_delete(gtkaspell->gtkaspeller);
2322 /* should never be reached as the dicts are always set
2323 to a default value */
2324 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2326 gtkaspeller_delete(gtkaspell->gtkaspeller);
2328 gtkaspell->gtkaspeller = gtkaspeller;
2329 gtkaspell_set_sug_mode(gtkaspell, sug_mode);
2332 dictionary_delete(dict);
2337 /* change the alternate dictionary of gtkaspell (doesn't affect the default dictionary) */
2338 gboolean gtkaspell_change_alt_dict(GtkAspell *gtkaspell, const gchar *alt_dictionary)
2341 GtkAspeller *gtkaspeller;
2343 g_return_val_if_fail(gtkaspell, FALSE);
2344 g_return_val_if_fail(alt_dictionary, FALSE);
2346 dict = g_new0(Dictionary, 1);
2347 dict->fullname = g_strdup(alt_dictionary);
2348 dict->encoding = g_strdup(gtkaspell->gtkaspeller->dictionary->encoding);
2350 gtkaspeller = gtkaspeller_new(dict);
2353 alertpanel_warning(_("The spell checker could not change the alternate dictionary.\n%s"),
2354 gtkaspellcheckers->error_message);
2356 if (gtkaspell->alternate_speller)
2357 gtkaspeller_delete(gtkaspell->alternate_speller);
2358 gtkaspell->alternate_speller = gtkaspeller;
2361 dictionary_delete(dict);
2366 /* Menu call backs */
2368 /* change_dict_cb() - Menu callback : change dict */
2369 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2373 fullname = (gchar *) g_object_get_data(G_OBJECT(w), "dict_name");
2375 if (!strcmp2(fullname, _("None")))
2378 gtkaspell_change_dict(gtkaspell, fullname, TRUE);
2379 if (gtkaspell->recheck_when_changing_dict) {
2380 gtkaspell_highlight_all(gtkaspell);
2382 if (gtkaspell->menu_changed_cb)
2383 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2386 static void switch_to_alternate_cb(GtkWidget *w,
2389 GtkAspell *gtkaspell = (GtkAspell *) data;
2390 use_alternate_dict(gtkaspell);
2391 if (gtkaspell->recheck_when_changing_dict) {
2392 gtkaspell_highlight_all(gtkaspell);
2394 if (gtkaspell->menu_changed_cb)
2395 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2398 /* Misc. helper functions */
2400 static void set_point_continue(GtkAspell *gtkaspell)
2402 GtkTextView *gtktext;
2404 gtktext = gtkaspell->gtktext;
2406 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
2408 if (gtkaspell->continue_check)
2409 gtkaspell->continue_check((gpointer *) gtkaspell);
2412 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2414 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtkaspell->gtktext);
2415 GdkColor *color = &(gtkaspell->highlight);
2417 /* Shameless copy from Sylpheed's gtkutils.c */
2419 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2421 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2423 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2427 gtk_text_buffer_create_tag(buffer, "misspelled",
2428 "foreground-gdk", color, NULL);
2430 gtk_text_buffer_create_tag(buffer, "misspelled",
2431 "underline", PANGO_UNDERLINE_ERROR, NULL);
2435 static void change_color(GtkAspell * gtkaspell,
2436 gint start, gint end,
2440 GtkTextView *gtktext;
2441 GtkTextBuffer *buffer;
2442 GtkTextIter startiter, enditer;
2447 gtktext = gtkaspell->gtktext;
2449 buffer = gtk_text_view_get_buffer(gtktext);
2450 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, start);
2451 gtk_text_buffer_get_iter_at_offset(buffer, &enditer, end);
2453 gtk_text_buffer_apply_tag_by_name(buffer, "misspelled",
2454 &startiter, &enditer);
2456 gtk_text_iter_forward_char(&enditer);
2457 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
2458 &startiter, &enditer);
2462 /* convert_to_aspell_encoding () - converts ISO-8859-* strings to iso8859-*
2463 * as needed by aspell. Returns an allocated string.
2466 static gchar *convert_to_aspell_encoding (const gchar *encoding)
2468 gchar * aspell_encoding;
2470 if (strstr2(encoding, "ISO-8859-")) {
2471 aspell_encoding = g_strdup_printf("iso8859%s", encoding+8);
2474 if (!strcmp2(encoding, "US-ASCII"))
2475 aspell_encoding = g_strdup("iso8859-1");
2477 aspell_encoding = g_strdup(encoding);
2480 return aspell_encoding;
2483 /* compare_dict () - compare 2 dict names */
2484 static gint compare_dict(Dictionary *a, Dictionary *b)
2486 guint aparts = 0, bparts = 0;
2489 for (i=0; i < strlen(a->dictname); i++)
2490 if (a->dictname[i] == '-')
2492 for (i=0; i < strlen(b->dictname); i++)
2493 if (b->dictname[i] == '-')
2496 if (aparts != bparts)
2497 return (aparts < bparts) ? -1 : +1;
2500 compare = strcmp2(a->dictname, b->dictname);
2502 compare = strcmp2(a->fullname, b->fullname);
2507 static void dictionary_delete(Dictionary *dict)
2509 g_free(dict->fullname);
2510 g_free(dict->encoding);
2514 static Dictionary *dictionary_dup(const Dictionary *dict)
2518 dict2 = g_new(Dictionary, 1);
2520 dict2->fullname = g_strdup(dict->fullname);
2521 dict2->dictname = dict->dictname - dict->fullname + dict2->fullname;
2522 dict2->encoding = g_strdup(dict->encoding);
2527 static void free_suggestions_list(GtkAspell *gtkaspell)
2531 for (list = gtkaspell->suggestions_list; list != NULL;
2535 g_list_free(gtkaspell->suggestions_list);
2537 gtkaspell->max_sug = -1;
2538 gtkaspell->suggestions_list = NULL;
2541 static void reset_theword_data(GtkAspell *gtkaspell)
2543 gtkaspell->start_pos = 0;
2544 gtkaspell->end_pos = 0;
2545 gtkaspell->theword[0] = 0;
2546 gtkaspell->max_sug = -1;
2548 free_suggestions_list(gtkaspell);
2551 static void free_checkers(gpointer elt, gpointer data)
2553 GtkAspeller *gtkaspeller = elt;
2555 g_return_if_fail(gtkaspeller);
2557 gtkaspeller_real_delete(gtkaspeller);
2560 static gint find_gtkaspeller(gconstpointer aa, gconstpointer bb)
2562 Dictionary *a = ((GtkAspeller *) aa)->dictionary;
2563 Dictionary *b = ((GtkAspeller *) bb)->dictionary;
2565 if (a && b && a->fullname && b->fullname &&
2566 strcmp(a->fullname, b->fullname) == 0 &&
2567 a->encoding && b->encoding)
2568 return strcmp(a->encoding, b->encoding);
2573 gchar *gtkaspell_get_default_dictionary(GtkAspell *gtkaspell)
2575 if (gtkaspell && gtkaspell->gtkaspeller &&
2576 gtkaspell->gtkaspeller->dictionary)
2577 return gtkaspell->gtkaspeller->dictionary->dictname;