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 <gtk/gtkoptionmenu.h>
52 #include <gtk/gtkmenu.h>
53 #include <gtk/gtkmenuitem.h>
54 #include <gdk/gdkkeysyms.h>
60 #include "alertpanel.h"
61 #include "gtkaspell.h"
62 #include "gtk/gtkutils.h"
63 #include "gtk/combobox.h"
65 #define ASPELL_FASTMODE 1
66 #define ASPELL_NORMALMODE 2
67 #define ASPELL_BADSPELLERMODE 3
69 #define GTKASPELLWORDSIZE 1024
71 /* size of the text buffer used in various word-processing routines. */
74 /* number of suggestions to display on each menu. */
77 /* 'config' must be defined as a 'AspellConfig *' */
78 #define RETURN_FALSE_IF_CONFIG_ERROR() \
80 if (aspell_config_error_number(config) != 0) { \
81 gtkaspellcheckers->error_message = g_strdup(aspell_config_error_message(config)); \
86 #define CONFIG_REPLACE_RETURN_FALSE_IF_FAIL(option, value) { \
87 aspell_config_replace(config, option, value); \
88 RETURN_FALSE_IF_CONFIG_ERROR(); \
92 SET_GTKASPELL_NAME = 0,
93 SET_GTKASPELL_FULLNAME = 1,
97 typedef struct _GtkAspellCheckers {
99 GSList *dictionary_list;
100 gchar *error_message;
103 typedef struct _Dictionary {
109 typedef struct _GtkAspeller {
110 Dictionary *dictionary;
112 AspellConfig *config;
113 AspellSpeller *checker;
116 typedef void (*ContCheckFunc) (gpointer *gtkaspell);
120 GtkAspeller *gtkaspeller;
121 GtkAspeller *alternate_speller;
122 gchar *dictionary_path;
123 gchar theword[GTKASPELLWORDSIZE];
129 gboolean check_while_typing;
130 gboolean recheck_when_changing_dict;
131 gboolean use_alternate;
132 gboolean use_both_dicts;
134 ContCheckFunc continue_check;
136 GtkWidget *replace_entry;
137 GtkWidget *parent_window;
139 gint default_sug_mode;
141 GList *suggestions_list;
143 GtkTextView *gtktext;
145 GtkAccelGroup *accel_group;
146 void (*menu_changed_cb)(void *data);
147 void *menu_changed_data;
150 typedef AspellConfig GtkAspellConfig;
152 /******************************************************************************/
154 static GtkAspellCheckers *gtkaspellcheckers;
156 /* Error message storage */
157 static void gtkaspell_checkers_error_message (gchar *message);
160 static void entry_insert_cb (GtkTextBuffer *textbuf,
164 GtkAspell *gtkaspell);
165 static void entry_delete_cb (GtkTextBuffer *textbuf,
166 GtkTextIter *startiter,
167 GtkTextIter *enditer,
168 GtkAspell *gtkaspell);
169 /*static gint button_press_intercept_cb (GtkTextView *gtktext,
171 GtkAspell *gtkaspell);
173 static void button_press_intercept_cb(GtkTextView *gtktext,
174 GtkMenu *menu, GtkAspell *gtkaspell);
176 /* Checker creation */
177 static GtkAspeller* gtkaspeller_new (Dictionary *dict);
178 static GtkAspeller* gtkaspeller_real_new (Dictionary *dict);
179 static GtkAspeller* gtkaspeller_delete (GtkAspeller *gtkaspeller);
180 static GtkAspeller* gtkaspeller_real_delete (GtkAspeller *gtkaspeller);
182 /* Checker configuration */
183 static gint set_dictionary (AspellConfig *config,
185 static void set_sug_mode_cb (GtkMenuItem *w,
186 GtkAspell *gtkaspell);
187 static void set_use_both_cb (GtkMenuItem *w,
188 GtkAspell *gtkaspell);
189 static void set_real_sug_mode (GtkAspell *gtkaspell,
190 const char *themode);
192 /* Checker actions */
193 static gboolean check_at (GtkAspell *gtkaspell,
195 static gboolean check_next_prev (GtkAspell *gtkaspell,
197 static GList* misspelled_suggest (GtkAspell *gtkaspell,
199 static void add_word_to_session_cb (GtkWidget *w,
201 static void add_word_to_personal_cb (GtkWidget *w,
203 static void replace_with_create_dialog_cb (GtkWidget *w,
205 static void replace_with_supplied_word_cb (GtkWidget *w,
206 GtkAspell *gtkaspell);
207 static void replace_word_cb (GtkWidget *w,
209 static void replace_real_word (GtkAspell *gtkaspell,
211 static void check_with_alternate_cb (GtkWidget *w,
213 static void use_alternate_dict (GtkAspell *gtkaspell);
214 static void toggle_check_while_typing_cb (GtkWidget *w,
218 static GSList* make_sug_menu (GtkAspell *gtkaspell);
219 static GSList * populate_submenu (GtkAspell *gtkaspell);
220 GSList* gtkaspell_make_config_menu (GtkAspell *gtkaspell);
221 static void set_menu_pos (GtkMenu *menu,
226 /* Other menu callbacks */
227 static gboolean aspell_key_pressed (GtkWidget *widget,
229 GtkAspell *gtkaspell);
230 static void change_dict_cb (GtkWidget *w,
231 GtkAspell *gtkaspell);
232 static void switch_to_alternate_cb (GtkWidget *w,
235 /* Misc. helper functions */
236 static void set_point_continue (GtkAspell *gtkaspell);
237 static void continue_check (gpointer *gtkaspell);
238 static gboolean iswordsep (gunichar c);
239 static gunichar get_text_index_whar (GtkAspell *gtkaspell,
241 static gboolean get_word_from_pos (GtkAspell *gtkaspell,
247 static void allocate_color (GtkAspell *gtkaspell,
249 static void change_color (GtkAspell *gtkaspell,
254 static gchar* convert_to_aspell_encoding (const gchar *encoding);
255 static gint compare_dict (Dictionary *a,
257 static void dictionary_delete (Dictionary *dict);
258 static Dictionary * dictionary_dup (const Dictionary *dict);
259 static void free_suggestions_list (GtkAspell *gtkaspell);
260 static void reset_theword_data (GtkAspell *gtkaspell);
261 static void free_checkers (gpointer elt,
263 static gint find_gtkaspeller (gconstpointer aa,
265 /* gtkspellconfig - only one config per session */
266 GtkAspellConfig * gtkaspellconfig;
267 static void destroy_menu(GtkWidget *widget, gpointer user_data);
269 /******************************************************************************/
270 static gint get_textview_buffer_charcount(GtkTextView *view);
272 static void gtkaspell_free_dictionary_list (GSList *list);
273 static GSList* gtkaspell_get_dictionary_list (const char *aspell_path,
276 static void gtkaspell_uncheck_all (GtkAspell *gtkaspell);
278 static gint get_textview_buffer_charcount(GtkTextView *view)
280 GtkTextBuffer *buffer;
282 g_return_val_if_fail(view, 0);
284 buffer = gtk_text_view_get_buffer(view);
285 g_return_val_if_fail(buffer, 0);
287 return gtk_text_buffer_get_char_count(buffer);
289 static gint get_textview_buffer_offset(GtkTextView *view)
291 GtkTextBuffer * buffer;
295 g_return_val_if_fail(view, 0);
297 buffer = gtk_text_view_get_buffer(view);
298 g_return_val_if_fail(buffer, 0);
300 mark = gtk_text_buffer_get_insert(buffer);
301 g_return_val_if_fail(mark, 0);
303 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
305 return gtk_text_iter_get_offset(&iter);
307 static void set_textview_buffer_offset(GtkTextView *view, gint offset)
309 GtkTextBuffer *buffer;
312 g_return_if_fail(view);
314 buffer = gtk_text_view_get_buffer(view);
315 g_return_if_fail(buffer);
317 gtk_text_buffer_get_iter_at_offset(buffer, &iter, offset);
318 gtk_text_buffer_place_cursor(buffer, &iter);
320 /******************************************************************************/
322 void gtkaspell_checkers_init(void)
324 gtkaspellcheckers = g_new(GtkAspellCheckers, 1);
325 gtkaspellcheckers->checkers = NULL;
326 gtkaspellcheckers->dictionary_list = NULL;
327 gtkaspellcheckers->error_message = NULL;
330 void gtkaspell_checkers_quit(void)
335 if (gtkaspellcheckers == NULL)
338 if ((checkers = gtkaspellcheckers->checkers)) {
339 debug_print("Aspell: number of running checkers to delete %d\n",
340 g_slist_length(checkers));
342 g_slist_foreach(checkers, free_checkers, NULL);
343 g_slist_free(checkers);
344 gtkaspellcheckers->checkers = NULL;
347 if ((dict_list = gtkaspellcheckers->dictionary_list)) {
348 debug_print("Aspell: number of dictionaries to delete %d\n",
349 g_slist_length(dict_list));
351 gtkaspell_free_dictionary_list(dict_list);
352 gtkaspellcheckers->dictionary_list = NULL;
355 g_free(gtkaspellcheckers->error_message);
356 gtkaspellcheckers->error_message = NULL;
360 static void gtkaspell_checkers_error_message (gchar *message)
363 if (gtkaspellcheckers->error_message) {
364 tmp = g_strdup_printf("%s\n%s",
365 gtkaspellcheckers->error_message,
368 g_free(gtkaspellcheckers->error_message);
369 gtkaspellcheckers->error_message = tmp;
371 gtkaspellcheckers->error_message = message;
374 const char *gtkaspell_checkers_strerror(void)
376 g_return_val_if_fail(gtkaspellcheckers, "");
377 return gtkaspellcheckers->error_message;
380 void gtkaspell_checkers_reset_error(void)
382 g_return_if_fail(gtkaspellcheckers);
384 g_free(gtkaspellcheckers->error_message);
386 gtkaspellcheckers->error_message = NULL;
389 GtkAspell *gtkaspell_new(const gchar *dictionary_path,
390 const gchar *dictionary,
391 const gchar *alt_dictionary,
392 const gchar *encoding,
393 gint misspelled_color,
394 gboolean check_while_typing,
395 gboolean recheck_when_changing_dict,
396 gboolean use_alternate,
397 gboolean use_both_dicts,
398 GtkTextView *gtktext,
399 GtkWindow *parent_win,
400 void (*spell_menu_cb)(void *data),
404 GtkAspell *gtkaspell;
405 GtkAspeller *gtkaspeller;
406 GtkTextBuffer *buffer;
408 g_return_val_if_fail(gtktext, NULL);
409 g_return_val_if_fail(dictionary && strlen(dictionary) > 0,
412 g_return_val_if_fail(dictionary_path && strlen(dictionary_path) > 0,
415 buffer = gtk_text_view_get_buffer(gtktext);
417 dict = g_new0(Dictionary, 1);
418 dict->fullname = g_strdup(dictionary);
419 dict->encoding = g_strdup(encoding);
421 gtkaspeller = gtkaspeller_new(dict);
422 dictionary_delete(dict);
427 gtkaspell = g_new0(GtkAspell, 1);
429 gtkaspell->dictionary_path = g_strdup(dictionary_path);
431 gtkaspell->gtkaspeller = gtkaspeller;
433 if (use_alternate && alt_dictionary && *alt_dictionary) {
434 Dictionary *alt_dict;
435 GtkAspeller *alt_gtkaspeller;
437 alt_dict = g_new0(Dictionary, 1);
438 alt_dict->fullname = g_strdup(alt_dictionary);
439 alt_dict->encoding = g_strdup(encoding);
441 alt_gtkaspeller = gtkaspeller_new(alt_dict);
442 dictionary_delete(alt_dict);
444 if (!alt_gtkaspeller)
447 gtkaspell->alternate_speller = alt_gtkaspeller;
449 gtkaspell->alternate_speller = NULL;
452 gtkaspell->theword[0] = 0x00;
453 gtkaspell->start_pos = 0;
454 gtkaspell->end_pos = 0;
455 gtkaspell->orig_pos = -1;
456 gtkaspell->end_check_pos = -1;
457 gtkaspell->misspelled = -1;
458 gtkaspell->check_while_typing = check_while_typing;
459 gtkaspell->recheck_when_changing_dict = recheck_when_changing_dict;
460 gtkaspell->continue_check = NULL;
461 gtkaspell->replace_entry = NULL;
462 gtkaspell->gtktext = gtktext;
463 gtkaspell->default_sug_mode = ASPELL_FASTMODE;
464 gtkaspell->max_sug = -1;
465 gtkaspell->suggestions_list = NULL;
466 gtkaspell->use_alternate = use_alternate;
467 gtkaspell->use_both_dicts = use_both_dicts;
468 gtkaspell->parent_window = GTK_WIDGET(parent_win);
469 gtkaspell->menu_changed_cb = spell_menu_cb;
470 gtkaspell->menu_changed_data = data;
472 allocate_color(gtkaspell, misspelled_color);
474 g_signal_connect_after(G_OBJECT(buffer), "insert-text",
475 G_CALLBACK(entry_insert_cb), gtkaspell);
476 g_signal_connect_after(G_OBJECT(buffer), "delete-range",
477 G_CALLBACK(entry_delete_cb), gtkaspell);
478 /*g_signal_connect(G_OBJECT(gtktext), "button-press-event",
479 G_CALLBACK(button_press_intercept_cb),
481 g_signal_connect(G_OBJECT(gtktext), "populate-popup",
482 G_CALLBACK(button_press_intercept_cb), gtkaspell);
484 debug_print("Aspell: created gtkaspell %p\n", gtkaspell);
489 void gtkaspell_delete(GtkAspell *gtkaspell)
491 GtkTextView *gtktext = gtkaspell->gtktext;
493 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
494 G_CALLBACK(entry_insert_cb),
496 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
497 G_CALLBACK(entry_delete_cb),
499 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
500 G_CALLBACK(button_press_intercept_cb),
503 gtkaspell_uncheck_all(gtkaspell);
505 gtkaspeller_delete(gtkaspell->gtkaspeller);
507 if (gtkaspell->alternate_speller)
508 gtkaspeller_delete(gtkaspell->alternate_speller);
510 if (gtkaspell->suggestions_list)
511 free_suggestions_list(gtkaspell);
513 g_free((gchar *)gtkaspell->dictionary_path);
514 gtkaspell->dictionary_path = NULL;
516 debug_print("Aspell: deleting gtkaspell %p\n", gtkaspell);
523 static void entry_insert_cb(GtkTextBuffer *textbuf,
527 GtkAspell *gtkaspell)
531 g_return_if_fail(gtkaspell->gtkaspeller->checker);
533 if (!gtkaspell->check_while_typing)
536 pos = gtk_text_iter_get_offset(iter);
538 if (iswordsep(g_utf8_get_char(newtext))) {
539 /* did we just end a word? */
541 check_at(gtkaspell, pos - 2);
543 /* did we just split a word? */
544 if (pos < gtk_text_buffer_get_char_count(textbuf))
545 check_at(gtkaspell, pos + 1);
547 /* check as they type, *except* if they're typing at the end (the most
550 if (pos < gtk_text_buffer_get_char_count(textbuf) &&
551 !iswordsep(get_text_index_whar(gtkaspell, pos))) {
552 check_at(gtkaspell, pos - 1);
557 static void entry_delete_cb(GtkTextBuffer *textbuf,
558 GtkTextIter *startiter,
559 GtkTextIter *enditer,
560 GtkAspell *gtkaspell)
565 g_return_if_fail(gtkaspell->gtkaspeller->checker);
567 if (!gtkaspell->check_while_typing)
570 start = gtk_text_iter_get_offset(startiter);
571 end = gtk_text_iter_get_offset(enditer);
572 origpos = get_textview_buffer_offset(gtkaspell->gtktext);
574 check_at(gtkaspell, start - 1);
575 check_at(gtkaspell, start);
578 set_textview_buffer_offset(gtkaspell->gtktext, origpos);
579 /* this is to *UNDO* the selection, in case they were holding shift
580 * while hitting backspace. */
581 /* needed with textview ??? */
582 /* gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos); */
585 static void button_press_intercept_cb(GtkTextView *gtktext,
586 GtkMenu *menu, GtkAspell *gtkaspell)
588 GtkMenuItem *menuitem;
589 GSList *spell_menu = NULL;
591 gboolean suggest = FALSE;
593 menuitem = GTK_MENU_ITEM(gtk_separator_menu_item_new());
594 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
595 gtk_widget_show(GTK_WIDGET(menuitem));
597 gtktext = gtkaspell->gtktext;
599 gtkaspell->orig_pos = get_textview_buffer_offset(gtktext);
601 if (check_at(gtkaspell, gtkaspell->orig_pos)) {
603 if (misspelled_suggest(gtkaspell, gtkaspell->theword)) {
604 spell_menu = make_sug_menu(gtkaspell);
609 spell_menu = gtkaspell_make_config_menu(gtkaspell);
611 spell_menu = g_slist_reverse(spell_menu);
612 for (items = spell_menu;
613 items; items = items->next) {
614 menuitem = GTK_MENU_ITEM(items->data);
615 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
616 gtk_widget_show(GTK_WIDGET(menuitem));
618 g_slist_free(spell_menu);
620 g_signal_connect(G_OBJECT(menu), "deactivate",
621 G_CALLBACK(destroy_menu),
624 g_signal_connect(G_OBJECT(menu),
626 G_CALLBACK(aspell_key_pressed), gtkaspell);
630 /* Checker creation */
631 static GtkAspeller *gtkaspeller_new(Dictionary *dictionary)
634 GtkAspeller *gtkaspeller = NULL;
638 g_return_val_if_fail(gtkaspellcheckers, NULL);
640 g_return_val_if_fail(dictionary, NULL);
642 if (dictionary->fullname == NULL)
643 gtkaspell_checkers_error_message(
644 g_strdup(_("No dictionary selected.")));
646 g_return_val_if_fail(dictionary->fullname, NULL);
648 if (dictionary->dictname == NULL) {
651 tmp = strrchr(dictionary->fullname, G_DIR_SEPARATOR);
654 dictionary->dictname = dictionary->fullname;
656 dictionary->dictname = tmp + 1;
659 dict = dictionary_dup(dictionary);
661 tmp = g_new0(GtkAspeller, 1);
662 tmp->dictionary = dict;
664 exist = g_slist_find_custom(gtkaspellcheckers->checkers, tmp,
669 if ((gtkaspeller = gtkaspeller_real_new(dict)) != NULL) {
670 gtkaspellcheckers->checkers = g_slist_append(
671 gtkaspellcheckers->checkers,
674 debug_print("Aspell: Created a new gtkaspeller %p\n",
677 dictionary_delete(dict);
679 debug_print("Aspell: Could not create spell checker.\n");
682 debug_print("Aspell: number of existing checkers %d\n",
683 g_slist_length(gtkaspellcheckers->checkers));
688 static GtkAspeller *gtkaspeller_real_new(Dictionary *dict)
690 GtkAspeller *gtkaspeller;
691 AspellConfig *config;
692 AspellCanHaveError *ret;
694 g_return_val_if_fail(gtkaspellcheckers, NULL);
695 g_return_val_if_fail(dict, NULL);
697 gtkaspeller = g_new(GtkAspeller, 1);
699 gtkaspeller->dictionary = dict;
700 gtkaspeller->sug_mode = ASPELL_FASTMODE;
702 config = new_aspell_config();
704 if (!set_dictionary(config, dict))
707 ret = new_aspell_speller(config);
708 delete_aspell_config(config);
710 if (aspell_error_number(ret) != 0) {
711 gtkaspellcheckers->error_message
712 = g_strdup(aspell_error_message(ret));
714 delete_aspell_can_have_error(ret);
719 gtkaspeller->checker = to_aspell_speller(ret);
720 gtkaspeller->config = aspell_speller_config(gtkaspeller->checker);
725 static GtkAspeller *gtkaspeller_delete(GtkAspeller *gtkaspeller)
727 g_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 g_return_val_if_fail(gtkaspeller, NULL);
747 g_return_val_if_fail(gtkaspeller->checker, NULL);
749 aspell_speller_save_all_word_lists(gtkaspeller->checker);
751 delete_aspell_speller(gtkaspeller->checker);
753 dictionary_delete(gtkaspeller->dictionary);
755 debug_print("Aspell: gtkaspeller %p deleted.\n",
763 /*****************************************************************************/
764 /* Checker configuration */
766 static gboolean set_dictionary(AspellConfig *config, Dictionary *dict)
768 gchar *language = NULL;
769 gchar *jargon = NULL;
773 g_return_val_if_fail(config, FALSE);
774 g_return_val_if_fail(dict, FALSE);
776 strncpy(buf, dict->fullname, BUFSIZE-1);
777 buf[BUFSIZE-1] = 0x00;
779 buf[dict->dictname - dict->fullname] = 0x00;
781 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("dict-dir", buf);
782 debug_print("Aspell: looking for dictionaries in path %s.\n", buf);
784 strncpy(buf, dict->dictname, BUFSIZE-1);
787 if ((size = strrchr(buf, '-')) && isdigit((int) size[1]))
792 if ((jargon = strchr(language, '-')) != NULL)
795 if (size != NULL && jargon == size)
798 debug_print("Aspell: language: %s, jargon: %s, size: %s\n",
799 language, jargon ? jargon : "",
803 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("lang", language);
805 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("jargon", jargon);
807 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("size", size);
808 if (dict->encoding) {
811 aspell_enc = convert_to_aspell_encoding (dict->encoding);
812 aspell_config_replace(config, "encoding",
813 (const char *) aspell_enc);
816 RETURN_FALSE_IF_CONFIG_ERROR();
822 /* set_sug_mode_cb() - Menu callback: Set the suggestion mode */
823 static void set_sug_mode_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
827 themode = (char *) gtk_label_get_text(GTK_LABEL(GTK_BIN(w)->child));
828 themode = g_strdup(themode);
830 set_real_sug_mode(gtkaspell, themode);
832 if (gtkaspell->menu_changed_cb)
833 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
836 /* set_sug_mode_cb() - Menu callback: Set the suggestion mode */
837 static void set_use_both_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
839 gtkaspell->use_both_dicts = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
841 if (gtkaspell->recheck_when_changing_dict) {
842 gtkaspell_highlight_all(gtkaspell);
845 if (gtkaspell->menu_changed_cb)
846 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
849 static void set_real_sug_mode(GtkAspell *gtkaspell, const char *themode)
852 gint mode = ASPELL_FASTMODE;
853 g_return_if_fail(gtkaspell);
854 g_return_if_fail(gtkaspell->gtkaspeller);
855 g_return_if_fail(themode);
857 if (!strcmp(themode,_("Normal Mode")))
858 mode = ASPELL_NORMALMODE;
859 else if (!strcmp( themode,_("Bad Spellers Mode")))
860 mode = ASPELL_BADSPELLERMODE;
862 result = gtkaspell_set_sug_mode(gtkaspell, mode);
865 debug_print("Aspell: error while changing suggestion mode:%s\n",
866 gtkaspellcheckers->error_message);
867 gtkaspell_checkers_reset_error();
871 /* gtkaspell_set_sug_mode() - Set the suggestion mode */
872 gboolean gtkaspell_set_sug_mode(GtkAspell *gtkaspell, gint themode)
874 AspellConfig *config;
876 g_return_val_if_fail(gtkaspell, FALSE);
877 g_return_val_if_fail(gtkaspell->gtkaspeller, FALSE);
878 g_return_val_if_fail(gtkaspell->gtkaspeller->config, FALSE);
880 debug_print("Aspell: setting sug mode of gtkaspeller %p to %d\n",
881 gtkaspell->gtkaspeller, themode);
883 config = gtkaspell->gtkaspeller->config;
886 case ASPELL_FASTMODE:
887 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "fast");
889 case ASPELL_NORMALMODE:
890 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "normal");
892 case ASPELL_BADSPELLERMODE:
893 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode",
897 gtkaspellcheckers->error_message =
898 g_strdup(_("Unknown suggestion mode."));
902 gtkaspell->gtkaspeller->sug_mode = themode;
903 gtkaspell->default_sug_mode = themode;
908 /* misspelled_suggest() - Create a suggestion list for word */
909 static GList *misspelled_suggest(GtkAspell *gtkaspell, gchar *word)
911 const guchar *newword;
913 const AspellWordList *suggestions;
914 AspellStringEnumeration *elements;
916 g_return_val_if_fail(word, NULL);
918 if (!aspell_speller_check(gtkaspell->gtkaspeller->checker, word, -1)) {
919 free_suggestions_list(gtkaspell);
921 suggestions = aspell_speller_suggest(
922 gtkaspell->gtkaspeller->checker,
923 (const char *)word, -1);
924 elements = aspell_word_list_elements(suggestions);
925 list = g_list_append(list, g_strdup(word));
927 while ((newword = (guchar *)aspell_string_enumeration_next(elements)) != NULL)
928 list = g_list_append(list, g_strdup((gchar *)newword));
930 gtkaspell->max_sug = g_list_length(list) - 1;
931 gtkaspell->suggestions_list = list;
936 free_suggestions_list(gtkaspell);
941 /* misspelled_test() - Just test if word is correctly spelled */
942 static int misspelled_test(GtkAspell *gtkaspell, unsigned char *word)
944 gint result = aspell_speller_check(gtkaspell->gtkaspeller->checker, (char *)word, -1)
946 if (result && gtkaspell->use_both_dicts && gtkaspell->alternate_speller) {
947 use_alternate_dict(gtkaspell);
948 result = aspell_speller_check(gtkaspell->gtkaspeller->checker, (char *)word, -1)
950 use_alternate_dict(gtkaspell);
956 static gboolean iswordsep(gunichar c)
958 return (g_unichar_isspace(c) || g_unichar_ispunct(c)) && c != (gunichar)'\'';
961 static gunichar get_text_index_whar(GtkAspell *gtkaspell, int pos)
963 GtkTextView *view = gtkaspell->gtktext;
964 GtkTextBuffer *buffer = gtk_text_view_get_buffer(view);
965 GtkTextIter start, end;
969 gtk_text_buffer_get_iter_at_offset(buffer, &start, pos);
970 gtk_text_buffer_get_iter_at_offset(buffer, &end, pos+1);
972 utf8chars = gtk_text_iter_get_text(&start, &end);
973 if (is_ascii_str(utf8chars)) {
974 a = utf8chars ? (gunichar)utf8chars[0] : '\0' ;
976 gchar *tr = conv_iconv_strdup(utf8chars, CS_UTF_8,
977 gtkaspell->gtkaspeller->dictionary->encoding);
979 a = g_utf8_get_char(tr);
988 /* get_word_from_pos () - return the word pointed to. */
989 /* Handles correctly the quotes. */
990 static gboolean get_word_from_pos(GtkAspell *gtkaspell, gint pos,
991 unsigned char* buf, gint buflen,
992 gint *pstart, gint *pend)
995 /* TODO : when correcting a word into quotes, change the color of */
996 /* the quotes too, as may be they were highlighted before. To do */
997 /* this, we can use two others pointers that points to the whole */
998 /* word including quotes. */
1004 GtkTextView *gtktext;
1006 gtktext = gtkaspell->gtktext;
1007 if (iswordsep(get_text_index_whar(gtkaspell, pos)))
1010 /* The apostrophe character is somtimes used for quotes
1011 * So include it in the word only if it is not surrounded
1012 * by other characters.
1015 for (start = pos; start >= 0; --start) {
1016 c = get_text_index_whar(gtkaspell, start);
1017 if (c == (gunichar)'\'') {
1019 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
1021 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
1023 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
1025 /* start_quote = TRUE; */
1030 /* start_quote = TRUE; */
1034 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
1040 for (end = pos; end < get_textview_buffer_charcount(gtktext); end++) {
1041 c = get_text_index_whar(gtkaspell, end);
1042 if (c == (gunichar)'\'') {
1043 if (end < get_textview_buffer_charcount(gtktext)) {
1044 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
1046 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
1048 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
1050 /* end_quote = TRUE; */
1055 /* end_quote = TRUE; */
1059 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
1069 if (end - start < buflen) {
1070 GtkTextIter iterstart, iterend;
1072 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtktext);
1073 gtk_text_buffer_get_iter_at_offset(buffer, &iterstart, start);
1074 gtk_text_buffer_get_iter_at_offset(buffer, &iterend, end);
1075 tmp = gtk_text_buffer_get_text(buffer, &iterstart, &iterend, FALSE);
1076 conv = conv_iconv_strdup(tmp, CS_UTF_8,
1077 gtkaspell->gtkaspeller->dictionary->encoding);
1079 strncpy((char *)buf, conv, buflen-1);
1089 static gboolean check_at(GtkAspell *gtkaspell, gint from_pos)
1092 unsigned char buf[GTKASPELLWORDSIZE];
1093 GtkTextView *gtktext;
1095 g_return_val_if_fail(from_pos >= 0, FALSE);
1097 gtktext = gtkaspell->gtktext;
1099 if (!get_word_from_pos(gtkaspell, from_pos, buf, sizeof(buf),
1103 if (misspelled_test(gtkaspell, buf)
1104 && strcasecmp((char *)buf, "sylpheed") && strcasecmp((char *)buf, "claws-mail")) {
1105 strncpy(gtkaspell->theword, (gchar *)buf, GTKASPELLWORDSIZE - 1);
1106 gtkaspell->theword[GTKASPELLWORDSIZE - 1] = 0;
1107 gtkaspell->start_pos = start;
1108 gtkaspell->end_pos = end;
1109 free_suggestions_list(gtkaspell);
1111 change_color(gtkaspell, start, end, (gchar *)buf, &(gtkaspell->highlight));
1114 change_color(gtkaspell, start, end, (gchar *)buf, NULL);
1119 static gboolean check_next_prev(GtkAspell *gtkaspell, gboolean forward)
1125 gboolean misspelled;
1128 maxpos = gtkaspell->end_check_pos;
1136 pos = get_textview_buffer_offset(gtkaspell->gtktext);
1137 gtkaspell->orig_pos = pos;
1138 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1139 pos > minpos && pos <= maxpos)
1141 while (!(misspelled = check_at(gtkaspell, pos)) &&
1142 pos > minpos && pos <= maxpos) {
1144 while (!iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1145 pos > minpos && pos <= maxpos)
1148 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1149 pos > minpos && pos <= maxpos)
1155 misspelled_suggest(gtkaspell, gtkaspell->theword);
1158 gtkaspell->orig_pos = gtkaspell->end_pos;
1160 set_textview_buffer_offset(gtkaspell->gtktext,
1161 gtkaspell->end_pos);
1162 /* scroll line to window center */
1163 gtk_text_view_scroll_to_mark(gtkaspell->gtktext,
1164 gtk_text_buffer_get_insert(
1165 gtk_text_view_get_buffer(gtkaspell->gtktext)),
1166 0.0, TRUE, 0.0, 0.5);
1167 /* let textview recalculate coordinates (set_menu_pos) */
1168 while (gtk_events_pending ())
1169 gtk_main_iteration ();
1171 list = make_sug_menu(gtkaspell);
1172 menu = gtk_menu_new();
1173 for (cur = list; cur; cur = cur->next)
1174 gtk_menu_append(menu, GTK_WIDGET(cur->data));
1176 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1177 set_menu_pos, gtkaspell, 0, GDK_CURRENT_TIME);
1178 g_signal_connect(G_OBJECT(menu), "deactivate",
1179 G_CALLBACK(destroy_menu),
1181 g_signal_connect(G_OBJECT(menu),
1183 G_CALLBACK(aspell_key_pressed), gtkaspell);
1187 reset_theword_data(gtkaspell);
1189 alertpanel_notice(_("No misspelled word found."));
1190 set_textview_buffer_offset(gtkaspell->gtktext,
1191 gtkaspell->orig_pos);
1197 void gtkaspell_check_backwards(GtkAspell *gtkaspell)
1199 gtkaspell->continue_check = NULL;
1200 gtkaspell->end_check_pos =
1201 get_textview_buffer_charcount(gtkaspell->gtktext);
1202 check_next_prev(gtkaspell, FALSE);
1205 void gtkaspell_check_forwards_go(GtkAspell *gtkaspell)
1208 gtkaspell->continue_check = NULL;
1209 gtkaspell->end_check_pos =
1210 get_textview_buffer_charcount(gtkaspell->gtktext);
1211 check_next_prev(gtkaspell, TRUE);
1214 void gtkaspell_check_all(GtkAspell *gtkaspell)
1216 GtkTextView *gtktext;
1218 GtkTextBuffer *buffer;
1219 GtkTextIter startiter, enditer;
1221 g_return_if_fail(gtkaspell);
1222 g_return_if_fail(gtkaspell->gtktext);
1224 gtktext = gtkaspell->gtktext;
1225 buffer = gtk_text_view_get_buffer(gtktext);
1226 gtk_text_buffer_get_selection_bounds(buffer, &startiter, &enditer);
1227 start = gtk_text_iter_get_offset(&startiter);
1228 end = gtk_text_iter_get_offset(&enditer);
1232 end = gtk_text_buffer_get_char_count(buffer);
1233 } else if (start > end) {
1241 set_textview_buffer_offset(gtktext, start);
1243 gtkaspell->continue_check = continue_check;
1244 gtkaspell->end_check_pos = end;
1246 gtkaspell->misspelled = check_next_prev(gtkaspell, TRUE);
1249 static void continue_check(gpointer *data)
1251 GtkAspell *gtkaspell = (GtkAspell *) data;
1252 gint pos = get_textview_buffer_offset(gtkaspell->gtktext);
1253 if (pos < gtkaspell->end_check_pos && gtkaspell->misspelled)
1254 gtkaspell->misspelled = check_next_prev(gtkaspell, TRUE);
1256 gtkaspell->continue_check = NULL;
1259 void gtkaspell_highlight_all(GtkAspell *gtkaspell)
1264 GtkTextView *gtktext;
1266 g_return_if_fail(gtkaspell->gtkaspeller->checker);
1268 gtktext = gtkaspell->gtktext;
1270 len = get_textview_buffer_charcount(gtktext);
1272 origpos = get_textview_buffer_offset(gtktext);
1276 iswordsep(get_text_index_whar(gtkaspell, pos)))
1279 !iswordsep(get_text_index_whar(gtkaspell, pos)))
1282 check_at(gtkaspell, pos - 1);
1284 set_textview_buffer_offset(gtktext, origpos);
1287 static void replace_with_supplied_word_cb(GtkWidget *w, GtkAspell *gtkaspell)
1289 unsigned char *newword;
1290 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1292 newword = (unsigned char *)gtk_editable_get_chars(GTK_EDITABLE(gtkaspell->replace_entry),
1295 if (strcmp((char *)newword, gtkaspell->theword)) {
1296 replace_real_word(gtkaspell, (char *)newword);
1298 if ((e->type == GDK_KEY_PRESS &&
1299 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK)) {
1300 aspell_speller_store_replacement(
1301 gtkaspell->gtkaspeller->checker,
1302 gtkaspell->theword, -1,
1303 (char *)newword, -1);
1305 gtkaspell->replace_entry = NULL;
1310 if (w && GTK_IS_DIALOG(w)) {
1311 gtk_widget_destroy(w);
1314 set_point_continue(gtkaspell);
1318 static void replace_word_cb(GtkWidget *w, gpointer data)
1320 unsigned char *newword;
1321 GtkAspell *gtkaspell = (GtkAspell *) data;
1322 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1324 newword = (unsigned char *) gtk_label_get_text(GTK_LABEL(GTK_BIN(w)->child));
1325 newword = (unsigned char *)g_strdup((char *)newword);
1327 replace_real_word(gtkaspell, (char *)newword);
1329 if ((e->type == GDK_KEY_PRESS &&
1330 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK) ||
1331 (e->type == GDK_BUTTON_RELEASE &&
1332 ((GdkEventButton *) e)->state & GDK_CONTROL_MASK)) {
1333 aspell_speller_store_replacement(
1334 gtkaspell->gtkaspeller->checker,
1335 gtkaspell->theword, -1,
1336 (char *)newword, -1);
1339 gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1341 set_point_continue(gtkaspell);
1345 static void replace_real_word(GtkAspell *gtkaspell, gchar *newword)
1347 int oldlen, newlen, wordlen;
1350 GtkTextView *gtktext;
1351 GtkTextBuffer *textbuf;
1352 GtkTextIter startiter, enditer;
1354 if (!newword) return;
1356 gtktext = gtkaspell->gtktext;
1357 textbuf = gtk_text_view_get_buffer(gtktext);
1359 origpos = gtkaspell->orig_pos;
1361 oldlen = gtkaspell->end_pos - gtkaspell->start_pos;
1362 wordlen = strlen(gtkaspell->theword);
1364 newlen = strlen(newword); /* FIXME: multybyte characters? */
1366 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1367 G_CALLBACK(entry_insert_cb),
1369 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1370 G_CALLBACK(entry_delete_cb),
1373 gtk_text_buffer_get_iter_at_offset(textbuf, &startiter,
1374 gtkaspell->start_pos);
1375 gtk_text_buffer_get_iter_at_offset(textbuf, &enditer,
1376 gtkaspell->end_pos);
1377 g_signal_emit_by_name(G_OBJECT(textbuf), "delete-range",
1378 &startiter, &enditer, gtkaspell);
1379 g_signal_emit_by_name(G_OBJECT(textbuf), "insert-text",
1380 &startiter, newword, newlen, gtkaspell);
1382 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1383 G_CALLBACK(entry_insert_cb),
1385 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1386 G_CALLBACK(entry_delete_cb),
1389 /* Put the point and the position where we clicked with the mouse
1390 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1391 * to let it update correctly the word insertion and then the
1392 * point & position position. If not, SEGV after the first replacement
1393 * If the new word ends before point, put the point at its end.
1396 if (origpos - gtkaspell->start_pos < oldlen &&
1397 origpos - gtkaspell->start_pos >= 0) {
1398 /* Original point was in the word.
1399 * Let it there unless point is going to be outside of the word
1401 if (origpos - gtkaspell->start_pos >= newlen) {
1402 pos = gtkaspell->start_pos + newlen;
1405 else if (origpos >= gtkaspell->end_pos) {
1406 /* move the position according to the change of length */
1407 pos = origpos + newlen - oldlen;
1410 gtkaspell->end_pos = gtkaspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1412 if (get_textview_buffer_charcount(gtktext) < pos)
1413 pos = get_textview_buffer_charcount(gtktext);
1414 gtkaspell->orig_pos = pos;
1416 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1419 /* Accept this word for this session */
1420 static void add_word_to_session_cb(GtkWidget *w, gpointer data)
1423 GtkTextView *gtktext;
1424 GtkAspell *gtkaspell = (GtkAspell *) data;
1425 gtktext = gtkaspell->gtktext;
1427 pos = get_textview_buffer_offset(gtktext);
1429 aspell_speller_add_to_session(gtkaspell->gtkaspeller->checker,
1431 strlen(gtkaspell->theword));
1433 check_at(gtkaspell, gtkaspell->start_pos);
1435 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1437 set_point_continue(gtkaspell);
1440 /* add_word_to_personal_cb() - add word to personal dict. */
1441 static void add_word_to_personal_cb(GtkWidget *w, gpointer data)
1443 GtkAspell *gtkaspell = (GtkAspell *) data;
1445 aspell_speller_add_to_personal(gtkaspell->gtkaspeller->checker,
1447 strlen(gtkaspell->theword));
1449 check_at(gtkaspell, gtkaspell->start_pos);
1451 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1452 set_point_continue(gtkaspell);
1455 static void check_with_alternate_cb(GtkWidget *w, gpointer data)
1457 GtkAspell *gtkaspell = (GtkAspell *) data;
1460 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1462 use_alternate_dict(gtkaspell);
1463 misspelled = check_at(gtkaspell, gtkaspell->start_pos);
1465 if (!gtkaspell->continue_check) {
1467 gtkaspell->misspelled = misspelled;
1469 if (gtkaspell->misspelled) {
1472 misspelled_suggest(gtkaspell, gtkaspell->theword);
1474 set_textview_buffer_offset(gtkaspell->gtktext,
1475 gtkaspell->end_pos);
1477 list = make_sug_menu(gtkaspell);
1478 menu = gtk_menu_new();
1479 for (cur = list; cur; cur = cur->next)
1480 gtk_menu_append(menu, GTK_WIDGET(cur->data));
1482 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1483 set_menu_pos, gtkaspell, 0,
1485 g_signal_connect(G_OBJECT(menu), "deactivate",
1486 G_CALLBACK(destroy_menu),
1488 g_signal_connect(G_OBJECT(menu),
1490 G_CALLBACK(aspell_key_pressed), gtkaspell);
1494 gtkaspell->orig_pos = gtkaspell->start_pos;
1496 set_point_continue(gtkaspell);
1499 static gboolean replace_key_pressed(GtkWidget *widget,
1501 GtkAspell *gtkaspell)
1503 if (event && event->keyval == GDK_Escape) {
1504 gtk_widget_destroy(widget);
1506 } else if (event && event->keyval == GDK_Return) {
1507 replace_with_supplied_word_cb(widget, gtkaspell);
1513 static void replace_with_create_dialog_cb(GtkWidget *w, gpointer data)
1515 static PangoFontDescription *font_desc;
1522 GtkWidget *ok_button;
1523 GtkWidget *cancel_button;
1524 GtkWidget *confirm_area;
1528 GtkAspell *gtkaspell = (GtkAspell *) data;
1530 gdk_window_get_origin((GTK_WIDGET(w)->parent)->window, &xx, &yy);
1532 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1534 dialog = gtk_dialog_new();
1536 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1537 gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1538 gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1539 gtk_widget_set_uposition(dialog, xx, yy);
1541 g_signal_connect_swapped(G_OBJECT(dialog), "destroy",
1542 G_CALLBACK(gtk_widget_destroy),
1545 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
1546 hbox = gtk_hbox_new (FALSE, 12);
1547 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1548 gtk_widget_show (hbox);
1549 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
1552 thelabel = g_strdup_printf(_("<span weight=\"bold\" "
1553 "size=\"larger\">Replace \"%s\" with: </span>"),
1554 gtkaspell->theword);
1555 /* for title label */
1556 w_hbox = gtk_hbox_new(FALSE, 0);
1558 icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
1559 GTK_ICON_SIZE_DIALOG);
1560 gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
1561 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
1563 vbox = gtk_vbox_new (FALSE, 12);
1564 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1565 gtk_widget_show (vbox);
1567 label = gtk_label_new(thelabel);
1568 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1569 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1570 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1571 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1572 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1576 size = pango_font_description_get_size
1577 (label->style->font_desc);
1578 font_desc = pango_font_description_new();
1579 pango_font_description_set_weight
1580 (font_desc, PANGO_WEIGHT_BOLD);
1581 pango_font_description_set_size
1582 (font_desc, size * PANGO_SCALE_LARGE);
1585 gtk_widget_modify_font(label, font_desc);
1588 entry = gtk_entry_new();
1589 gtkaspell->replace_entry = entry;
1590 gtk_entry_set_text(GTK_ENTRY(entry), gtkaspell->theword);
1591 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1592 g_signal_connect(G_OBJECT(dialog),
1594 G_CALLBACK(replace_key_pressed), gtkaspell);
1595 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
1597 label = gtk_label_new(_("Holding down Control key while pressing "
1598 "Enter\nwill learn from mistake.\n"));
1599 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1600 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1601 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1602 gtk_widget_show(label);
1604 hbox = gtk_hbox_new(TRUE, 0);
1606 gtkut_stock_button_set_create(&confirm_area,
1607 &cancel_button, GTK_STOCK_CANCEL,
1608 &ok_button, GTK_STOCK_OK,
1611 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1612 confirm_area, FALSE, FALSE, 0);
1613 gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
1615 g_signal_connect(G_OBJECT(ok_button), "clicked",
1616 G_CALLBACK(replace_with_supplied_word_cb),
1618 g_signal_connect_swapped(G_OBJECT(ok_button), "clicked",
1619 G_CALLBACK(gtk_widget_destroy),
1622 g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked",
1623 G_CALLBACK(gtk_widget_destroy),
1626 gtk_widget_grab_focus(entry);
1628 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1630 gtk_widget_show_all(dialog);
1633 static void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1635 GtkTextView *gtktext;
1636 GtkTextBuffer *buffer;
1637 GtkTextIter startiter, enditer;
1639 gtktext = gtkaspell->gtktext;
1641 buffer = gtk_text_view_get_buffer(gtktext);
1642 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, 0);
1643 gtk_text_buffer_get_iter_at_offset(buffer, &enditer,
1644 get_textview_buffer_charcount(gtktext)-1);
1645 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
1646 &startiter, &enditer);
1649 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1651 GtkAspell *gtkaspell = (GtkAspell *) data;
1653 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1655 if (!gtkaspell->check_while_typing)
1656 gtkaspell_uncheck_all(gtkaspell);
1657 if (gtkaspell->menu_changed_cb)
1658 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
1661 static GSList *create_empty_dictionary_list(void)
1663 GSList *list = NULL;
1666 dict = g_new0(Dictionary, 1);
1667 dict->fullname = g_strdup(_("None"));
1668 dict->dictname = dict->fullname;
1669 dict->encoding = NULL;
1671 return g_slist_append(list, dict);
1674 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1675 static GSList *gtkaspell_get_dictionary_list(const gchar *aspell_path, gint refresh)
1679 AspellConfig *config;
1680 AspellDictInfoList *dlist;
1681 AspellDictInfoEnumeration *dels;
1682 const AspellDictInfo *entry;
1684 if (!gtkaspellcheckers)
1685 gtkaspell_checkers_init();
1687 if (gtkaspellcheckers->dictionary_list && !refresh)
1688 return gtkaspellcheckers->dictionary_list;
1690 gtkaspell_free_dictionary_list(
1691 gtkaspellcheckers->dictionary_list);
1694 config = new_aspell_config();
1696 aspell_config_replace(config, "dict-dir", aspell_path);
1697 if (aspell_config_error_number(config) != 0) {
1698 gtkaspellcheckers->error_message = g_strdup(
1699 aspell_config_error_message(config));
1700 gtkaspellcheckers->dictionary_list =
1701 create_empty_dictionary_list();
1703 return gtkaspellcheckers->dictionary_list;
1706 dlist = get_aspell_dict_info_list(config);
1707 delete_aspell_config(config);
1709 debug_print("Aspell: checking for dictionaries in %s\n", aspell_path?aspell_path:"(null)");
1710 dels = aspell_dict_info_list_elements(dlist);
1711 while ( (entry = aspell_dict_info_enumeration_next(dels)) != 0)
1713 dict = g_new0(Dictionary, 1);
1714 dict->fullname = g_strdup_printf("%s%s", aspell_path,
1716 dict->dictname = dict->fullname + strlen(aspell_path);
1717 dict->encoding = g_strdup(entry->code);
1719 if (g_slist_find_custom(list, dict,
1720 (GCompareFunc) compare_dict) != NULL) {
1721 dictionary_delete(dict);
1725 debug_print("Aspell: found dictionary %s %s %s\n", dict->fullname,
1726 dict->dictname, dict->encoding);
1727 list = g_slist_insert_sorted(list, dict,
1728 (GCompareFunc) compare_dict);
1731 delete_aspell_dict_info_enumeration(dels);
1735 debug_print("Aspell: error when searching for dictionaries: "
1736 "No dictionary found.\n");
1737 list = create_empty_dictionary_list();
1740 gtkaspellcheckers->dictionary_list = list;
1745 static void gtkaspell_free_dictionary_list(GSList *list)
1749 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1751 dict = (Dictionary *) walk->data;
1752 dictionary_delete(dict);
1757 GtkTreeModel *gtkaspell_dictionary_store_new_with_refresh(const gchar *aspell_path,
1760 GSList *dict_list, *tmp;
1761 GtkListStore *store;
1765 dict_list = gtkaspell_get_dictionary_list(aspell_path, refresh);
1766 g_return_val_if_fail(dict_list, NULL);
1768 store = gtk_list_store_new(SET_GTKASPELL_SIZE,
1773 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1774 dict = (Dictionary *) tmp->data;
1776 gtk_list_store_append(store, &iter);
1777 gtk_list_store_set(store, &iter,
1778 SET_GTKASPELL_NAME, dict->dictname,
1779 SET_GTKASPELL_FULLNAME, dict->fullname,
1783 return GTK_TREE_MODEL(store);
1786 GtkTreeModel *gtkaspell_dictionary_store_new(const gchar *aspell_path)
1788 return gtkaspell_dictionary_store_new_with_refresh
1789 (aspell_path, TRUE);
1792 GtkWidget *gtkaspell_dictionary_combo_new(const gchar *aspell_path,
1793 const gboolean refresh)
1796 GtkCellRenderer *renderer;
1798 combo = gtk_combo_box_new_with_model(
1799 gtkaspell_dictionary_store_new_with_refresh(aspell_path, refresh));
1800 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1801 gtk_widget_show(combo);
1803 renderer = gtk_cell_renderer_text_new();
1804 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
1805 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),renderer,
1806 "text", SET_GTKASPELL_NAME, NULL);
1811 gchar *gtkaspell_get_dictionary_menu_active_item(GtkComboBox *combo)
1814 GtkTreeModel *model;
1815 gchar *dict_fullname = NULL;
1817 g_return_val_if_fail(GTK_IS_COMBO_BOX(combo), NULL);
1818 g_return_val_if_fail(gtk_combo_box_get_active_iter(combo, &iter), NULL);
1820 model = gtk_combo_box_get_model(combo);
1824 gtk_tree_model_get(model, &iter,
1825 SET_GTKASPELL_FULLNAME, &dict_fullname,
1828 return dict_fullname;
1831 gint gtkaspell_set_dictionary_menu_active_item(GtkComboBox *combo,
1832 const gchar *dictionary)
1834 GtkTreeModel *model;
1836 gchar *dict_name = NULL;
1838 g_return_val_if_fail(combo != NULL, 0);
1839 g_return_val_if_fail(dictionary != NULL, 0);
1840 g_return_val_if_fail(GTK_IS_COMBO_BOX(combo), 0);
1842 if((model = gtk_combo_box_get_model(combo)) == NULL)
1844 if((gtk_tree_model_get_iter_first(model, &iter)) == FALSE)
1848 gtk_tree_model_get(model, &iter,
1849 SET_GTKASPELL_FULLNAME, &dict_name,
1852 if ((dict_name != NULL) && !strcmp2(dict_name, dictionary)) {
1853 gtk_combo_box_set_active_iter(combo, &iter);
1860 } while ((gtk_tree_model_iter_next(model, &iter)) == TRUE);
1865 GtkWidget *gtkaspell_sugmode_combo_new(gint sugmode)
1867 GtkWidget *combo = gtkut_sc_combobox_create(NULL, FALSE);
1868 GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(
1869 GTK_COMBO_BOX(combo)));
1872 g_return_val_if_fail(store != NULL, NULL);
1874 COMBOBOX_ADD(store, _("Fast Mode"), ASPELL_FASTMODE);
1875 COMBOBOX_ADD(store, _("Normal Mode"), ASPELL_NORMALMODE);
1876 COMBOBOX_ADD(store, _("Bad Spellers Mode"), ASPELL_BADSPELLERMODE);
1878 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), sugmode - 1);
1879 gtk_widget_show(combo);
1884 static void use_alternate_dict(GtkAspell *gtkaspell)
1888 tmp = gtkaspell->gtkaspeller;
1889 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1890 gtkaspell->alternate_speller = tmp;
1893 static void destroy_menu(GtkWidget *widget,
1894 gpointer user_data) {
1896 GtkAspell *gtkaspell = (GtkAspell *)user_data;
1898 if (gtkaspell->accel_group) {
1899 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1900 gtkaspell->accel_group);
1901 gtkaspell->accel_group = NULL;
1905 static gboolean aspell_key_pressed(GtkWidget *widget,
1907 GtkAspell *gtkaspell)
1909 if (event && (isascii(event->keyval) || event->keyval == GDK_Return)) {
1910 gtk_accel_groups_activate(
1911 G_OBJECT(gtkaspell->parent_window),
1912 event->keyval, event->state);
1913 } else if (event && event->keyval == GDK_Escape) {
1914 destroy_menu(NULL, gtkaspell);
1919 /* make_sug_menu() - Add menus to accept this word for this session
1920 * and to add it to personal dictionary
1922 static GSList *make_sug_menu(GtkAspell *gtkaspell)
1926 GtkTextView *gtktext;
1927 GtkAccelGroup *accel;
1928 GList *l = gtkaspell->suggestions_list;
1930 GSList *list = NULL;
1931 gtktext = gtkaspell->gtktext;
1933 accel = gtk_accel_group_new();
1935 if (gtkaspell->accel_group) {
1936 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1937 gtkaspell->accel_group);
1938 gtkaspell->accel_group = NULL;
1941 utf8buf = conv_codeset_strdup((char*)l->data,
1942 conv_get_locale_charset_str(),
1944 caption = g_strdup_printf(_("\"%s\" unknown in %s"),
1946 gtkaspell->gtkaspeller->dictionary->dictname);
1947 item = gtk_menu_item_new_with_label(caption);
1949 gtk_widget_show(item);
1950 list = g_slist_append(list, item);
1951 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
1954 item = gtk_menu_item_new();
1955 gtk_widget_show(item);
1956 list = g_slist_append(list, item);
1958 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1959 gtk_widget_show(item);
1960 list = g_slist_append(list, item);
1961 g_signal_connect(G_OBJECT(item), "activate",
1962 G_CALLBACK(add_word_to_session_cb),
1964 gtk_widget_add_accelerator(item, "activate", accel, GDK_space,
1966 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1968 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1969 gtk_widget_show(item);
1970 list = g_slist_append(list, item);
1971 g_signal_connect(G_OBJECT(item), "activate",
1972 G_CALLBACK(add_word_to_personal_cb),
1974 gtk_widget_add_accelerator(item, "activate", accel, GDK_Return,
1976 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1978 item = gtk_menu_item_new_with_label(_("Replace with..."));
1979 gtk_widget_show(item);
1980 list = g_slist_append(list, item);
1981 g_signal_connect(G_OBJECT(item), "activate",
1982 G_CALLBACK(replace_with_create_dialog_cb),
1984 gtk_widget_add_accelerator(item, "activate", accel, GDK_R, 0,
1985 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1986 gtk_widget_add_accelerator(item, "activate", accel, GDK_R,
1990 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1991 caption = g_strdup_printf(_("Check with %s"),
1992 gtkaspell->alternate_speller->dictionary->dictname);
1993 item = gtk_menu_item_new_with_label(caption);
1995 gtk_widget_show(item);
1996 list = g_slist_append(list, item);
1997 g_signal_connect(G_OBJECT(item), "activate",
1998 G_CALLBACK(check_with_alternate_cb),
2000 gtk_widget_add_accelerator(item, "activate", accel, GDK_X, 0,
2001 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
2002 gtk_widget_add_accelerator(item, "activate", accel, GDK_X,
2007 item = gtk_menu_item_new();
2008 gtk_widget_show(item);
2009 list = g_slist_append(list, item);
2013 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
2014 gtk_widget_show(item);
2015 list = g_slist_append(list, item);
2017 GtkWidget *curmenu = NULL;
2021 if (count == MENUCOUNT) {
2024 item = gtk_menu_item_new_with_label(_("More..."));
2025 gtk_widget_show(item);
2027 gtk_menu_append(GTK_MENU(curmenu), item);
2029 list = g_slist_append(list, item);
2031 curmenu = gtk_menu_new();
2032 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2036 utf8buf = conv_codeset_strdup((char*)l->data,
2037 conv_get_locale_charset_str(),
2039 item = gtk_menu_item_new_with_label(utf8buf);
2041 gtk_widget_show(item);
2042 if (curmenu == NULL) {
2043 list = g_slist_append(list, item);
2045 gtk_menu_append(GTK_MENU(curmenu), item);
2047 g_signal_connect(G_OBJECT(item), "activate",
2048 G_CALLBACK(replace_word_cb),
2051 if (curmenu == NULL && count < MENUCOUNT) {
2052 gtk_widget_add_accelerator(item, "activate",
2057 gtk_widget_add_accelerator(item, "activate",
2066 } while ((l = l->next) != NULL);
2069 gtk_window_add_accel_group
2070 (GTK_WINDOW(gtkaspell->parent_window),
2072 gtkaspell->accel_group = accel;
2077 static GSList *populate_submenu(GtkAspell *gtkaspell)
2079 GtkWidget *item, *submenu;
2081 GtkAspeller *gtkaspeller = NULL;
2082 GSList *list = NULL;
2087 gtkaspeller = gtkaspell->gtkaspeller;
2088 dictname = g_strdup_printf(_("Dictionary: %s"),
2089 gtkaspeller->dictionary->dictname);
2090 item = gtk_menu_item_new_with_label(dictname);
2091 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
2093 gtk_widget_show(item);
2094 list = g_slist_append(list, item);
2096 item = gtk_menu_item_new();
2097 gtk_widget_show(item);
2098 list = g_slist_append(list, item);
2100 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
2101 dictname = g_strdup_printf(_("Use alternate (%s)"),
2102 gtkaspell->alternate_speller->dictionary->dictname);
2103 item = gtk_menu_item_new_with_label(dictname);
2105 g_signal_connect(G_OBJECT(item), "activate",
2106 G_CALLBACK(switch_to_alternate_cb),
2108 gtk_widget_show(item);
2109 list = g_slist_append(list, item);
2112 item = gtk_check_menu_item_new_with_label(_("Use both dictionaries"));
2113 if (gtkaspell->use_both_dicts) {
2114 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2116 g_signal_connect(G_OBJECT(item), "activate",
2117 G_CALLBACK(set_use_both_cb),
2119 gtk_widget_show(item);
2120 list = g_slist_append(list, item);
2122 item = gtk_menu_item_new();
2123 gtk_widget_show(item);
2124 list = g_slist_append(list, item);
2126 item = gtk_check_menu_item_new_with_label(_("Fast Mode"));
2127 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
2128 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_FASTMODE) {
2129 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),TRUE);
2130 gtk_widget_set_sensitive(GTK_WIDGET(item),FALSE);
2132 g_signal_connect(G_OBJECT(item), "activate",
2133 G_CALLBACK(set_sug_mode_cb),
2135 gtk_widget_show(item);
2136 list = g_slist_append(list, item);
2138 item = gtk_check_menu_item_new_with_label(_("Normal Mode"));
2139 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
2140 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_NORMALMODE) {
2141 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
2142 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2144 g_signal_connect(G_OBJECT(item), "activate",
2145 G_CALLBACK(set_sug_mode_cb),
2147 gtk_widget_show(item);
2148 list = g_slist_append(list, item);
2150 item = gtk_check_menu_item_new_with_label(_("Bad Spellers Mode"));
2151 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
2152 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_BADSPELLERMODE) {
2153 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
2154 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2156 g_signal_connect(G_OBJECT(item), "activate",
2157 G_CALLBACK(set_sug_mode_cb),
2159 gtk_widget_show(item);
2160 list = g_slist_append(list, item);
2162 item = gtk_menu_item_new();
2163 gtk_widget_show(item);
2164 list = g_slist_append(list, item);
2166 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
2167 if (gtkaspell->check_while_typing)
2168 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2170 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2171 g_signal_connect(G_OBJECT(item), "activate",
2172 G_CALLBACK(toggle_check_while_typing_cb),
2174 gtk_widget_show(item);
2175 list = g_slist_append(list, item);
2177 item = gtk_menu_item_new();
2178 gtk_widget_show(item);
2179 list = g_slist_append(list, item);
2181 submenu = gtk_menu_new();
2182 item = gtk_menu_item_new_with_label(_("Change dictionary"));
2183 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
2184 gtk_widget_show(item);
2185 list = g_slist_append(list, item);
2188 if (gtkaspellcheckers->dictionary_list == NULL)
2189 gtkaspell_get_dictionary_list(gtkaspell->dictionary_path, FALSE);
2191 GtkWidget * curmenu = submenu;
2195 tmp = gtkaspellcheckers->dictionary_list;
2197 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
2198 tmp = g_slist_next(tmp)) {
2199 if (count == MENUCOUNT) {
2202 newmenu = gtk_menu_new();
2203 item = gtk_menu_item_new_with_label(_("More..."));
2204 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2207 gtk_menu_append(GTK_MENU(curmenu), item);
2208 gtk_widget_show(item);
2212 dict = (Dictionary *) tmp->data;
2213 item = gtk_check_menu_item_new_with_label(dict->dictname);
2214 g_object_set_data(G_OBJECT(item), "dict_name",
2216 if (strcmp2(dict->fullname,
2217 gtkaspell->gtkaspeller->dictionary->fullname))
2218 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2220 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2221 gtk_widget_set_sensitive(GTK_WIDGET(item),
2224 g_signal_connect(G_OBJECT(item), "activate",
2225 G_CALLBACK(change_dict_cb),
2227 gtk_widget_show(item);
2228 gtk_menu_append(GTK_MENU(curmenu), item);
2236 GSList *gtkaspell_make_config_menu(GtkAspell *gtkaspell)
2238 return populate_submenu(gtkaspell);
2241 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y,
2242 gboolean *push_in, gpointer data)
2244 GtkAspell *gtkaspell = (GtkAspell *) data;
2245 gint xx = 0, yy = 0;
2248 GtkTextView *text = GTK_TEXT_VIEW(gtkaspell->gtktext);
2249 GtkTextBuffer *textbuf;
2254 textbuf = gtk_text_view_get_buffer(gtkaspell->gtktext);
2255 gtk_text_buffer_get_iter_at_mark(textbuf, &iter,
2256 gtk_text_buffer_get_insert(textbuf));
2257 gtk_text_view_get_iter_location(gtkaspell->gtktext, &iter, &rect);
2258 gtk_text_view_buffer_to_window_coords(text, GTK_TEXT_WINDOW_TEXT,
2262 gdk_window_get_origin(GTK_WIDGET(gtkaspell->gtktext)->window, &xx, &yy);
2264 sx = gdk_screen_width();
2265 sy = gdk_screen_height();
2267 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2273 gdk_char_width(gtk_style_get_font(GTK_WIDGET(text)->style), ' ');
2275 *y = rect.y + rect.height + yy;
2281 gdk_string_height(gtk_style_get_font(
2282 GTK_WIDGET(text)->style),
2283 gtkaspell->theword);
2286 /* change the current dictionary of gtkaspell
2287 - if always_set_alt_dict is set, the alternate dict is unconditionally set to the former
2288 current dictionary (common use: from menu callbacks)
2289 - if always_set_alt_dict is NOT set, the alternate dict will be set to the former
2290 current dictionary only if there is no alternate dictionary already set
2291 (this is when we need to set the current dictionary then the alternate one
2292 when creating a compose window, from the account and folder settings)
2294 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary,
2295 gboolean always_set_alt_dict)
2298 GtkAspeller *gtkaspeller;
2301 g_return_val_if_fail(gtkaspell, FALSE);
2302 g_return_val_if_fail(dictionary, FALSE);
2304 sug_mode = gtkaspell->default_sug_mode;
2306 dict = g_new0(Dictionary, 1);
2307 dict->fullname = g_strdup(dictionary);
2308 dict->encoding = g_strdup(gtkaspell->gtkaspeller->dictionary->encoding);
2310 gtkaspeller = gtkaspeller_new(dict);
2313 alertpanel_warning(_("The spell checker could not change dictionary.\n%s"),
2314 gtkaspellcheckers->error_message);
2316 if (gtkaspell->use_alternate) {
2317 if (gtkaspell->alternate_speller) {
2318 if (always_set_alt_dict) {
2319 gtkaspeller_delete(gtkaspell->alternate_speller);
2320 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2322 gtkaspeller_delete(gtkaspell->gtkaspeller);
2324 /* should never be reached as the dicts are always set
2325 to a default value */
2326 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2328 gtkaspeller_delete(gtkaspell->gtkaspeller);
2330 gtkaspell->gtkaspeller = gtkaspeller;
2331 gtkaspell_set_sug_mode(gtkaspell, sug_mode);
2334 dictionary_delete(dict);
2339 /* change the alternate dictionary of gtkaspell (doesn't affect the default dictionary) */
2340 gboolean gtkaspell_change_alt_dict(GtkAspell *gtkaspell, const gchar *alt_dictionary)
2343 GtkAspeller *gtkaspeller;
2345 g_return_val_if_fail(gtkaspell, FALSE);
2346 g_return_val_if_fail(alt_dictionary, FALSE);
2348 dict = g_new0(Dictionary, 1);
2349 dict->fullname = g_strdup(alt_dictionary);
2350 dict->encoding = g_strdup(gtkaspell->gtkaspeller->dictionary->encoding);
2352 gtkaspeller = gtkaspeller_new(dict);
2355 alertpanel_warning(_("The spell checker could not change the alternate dictionary.\n%s"),
2356 gtkaspellcheckers->error_message);
2358 if (gtkaspell->alternate_speller)
2359 gtkaspeller_delete(gtkaspell->alternate_speller);
2360 gtkaspell->alternate_speller = gtkaspeller;
2363 dictionary_delete(dict);
2368 /* Menu call backs */
2370 /* change_dict_cb() - Menu callback : change dict */
2371 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2375 fullname = (gchar *) g_object_get_data(G_OBJECT(w), "dict_name");
2377 if (!strcmp2(fullname, _("None")))
2380 gtkaspell_change_dict(gtkaspell, fullname, TRUE);
2381 if (gtkaspell->recheck_when_changing_dict) {
2382 gtkaspell_highlight_all(gtkaspell);
2384 if (gtkaspell->menu_changed_cb)
2385 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2388 static void switch_to_alternate_cb(GtkWidget *w,
2391 GtkAspell *gtkaspell = (GtkAspell *) data;
2392 use_alternate_dict(gtkaspell);
2393 if (gtkaspell->recheck_when_changing_dict) {
2394 gtkaspell_highlight_all(gtkaspell);
2396 if (gtkaspell->menu_changed_cb)
2397 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2400 /* Misc. helper functions */
2402 static void set_point_continue(GtkAspell *gtkaspell)
2404 GtkTextView *gtktext;
2406 gtktext = gtkaspell->gtktext;
2408 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
2410 if (gtkaspell->continue_check)
2411 gtkaspell->continue_check((gpointer *) gtkaspell);
2414 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2416 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtkaspell->gtktext);
2417 GdkColor *color = &(gtkaspell->highlight);
2419 /* Shameless copy from Sylpheed's gtkutils.c */
2421 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2423 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2425 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2429 gtk_text_buffer_create_tag(buffer, "misspelled",
2430 "foreground-gdk", color, NULL);
2432 gtk_text_buffer_create_tag(buffer, "misspelled",
2433 "underline", PANGO_UNDERLINE_ERROR, NULL);
2437 static void change_color(GtkAspell * gtkaspell,
2438 gint start, gint end,
2442 GtkTextView *gtktext;
2443 GtkTextBuffer *buffer;
2444 GtkTextIter startiter, enditer;
2449 gtktext = gtkaspell->gtktext;
2451 buffer = gtk_text_view_get_buffer(gtktext);
2452 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, start);
2453 gtk_text_buffer_get_iter_at_offset(buffer, &enditer, end);
2455 gtk_text_buffer_apply_tag_by_name(buffer, "misspelled",
2456 &startiter, &enditer);
2458 gtk_text_iter_forward_char(&enditer);
2459 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
2460 &startiter, &enditer);
2464 /* convert_to_aspell_encoding () - converts ISO-8859-* strings to iso8859-*
2465 * as needed by aspell. Returns an allocated string.
2468 static gchar *convert_to_aspell_encoding (const gchar *encoding)
2470 gchar * aspell_encoding;
2472 if (strstr2(encoding, "ISO-8859-")) {
2473 aspell_encoding = g_strdup_printf("iso8859%s", encoding+8);
2476 if (!strcmp2(encoding, "US-ASCII"))
2477 aspell_encoding = g_strdup("iso8859-1");
2479 aspell_encoding = g_strdup(encoding);
2482 return aspell_encoding;
2485 /* compare_dict () - compare 2 dict names */
2486 static gint compare_dict(Dictionary *a, Dictionary *b)
2488 guint aparts = 0, bparts = 0;
2491 for (i=0; i < strlen(a->dictname); i++)
2492 if (a->dictname[i] == '-')
2494 for (i=0; i < strlen(b->dictname); i++)
2495 if (b->dictname[i] == '-')
2498 if (aparts != bparts)
2499 return (aparts < bparts) ? -1 : +1;
2502 compare = strcmp2(a->dictname, b->dictname);
2504 compare = strcmp2(a->fullname, b->fullname);
2509 static void dictionary_delete(Dictionary *dict)
2511 g_free(dict->fullname);
2512 g_free(dict->encoding);
2516 static Dictionary *dictionary_dup(const Dictionary *dict)
2520 dict2 = g_new(Dictionary, 1);
2522 dict2->fullname = g_strdup(dict->fullname);
2523 dict2->dictname = dict->dictname - dict->fullname + dict2->fullname;
2524 dict2->encoding = g_strdup(dict->encoding);
2529 static void free_suggestions_list(GtkAspell *gtkaspell)
2533 for (list = gtkaspell->suggestions_list; list != NULL;
2537 g_list_free(gtkaspell->suggestions_list);
2539 gtkaspell->max_sug = -1;
2540 gtkaspell->suggestions_list = NULL;
2543 static void reset_theword_data(GtkAspell *gtkaspell)
2545 gtkaspell->start_pos = 0;
2546 gtkaspell->end_pos = 0;
2547 gtkaspell->theword[0] = 0;
2548 gtkaspell->max_sug = -1;
2550 free_suggestions_list(gtkaspell);
2553 static void free_checkers(gpointer elt, gpointer data)
2555 GtkAspeller *gtkaspeller = elt;
2557 g_return_if_fail(gtkaspeller);
2559 gtkaspeller_real_delete(gtkaspeller);
2562 static gint find_gtkaspeller(gconstpointer aa, gconstpointer bb)
2564 Dictionary *a = ((GtkAspeller *) aa)->dictionary;
2565 Dictionary *b = ((GtkAspeller *) bb)->dictionary;
2567 if (a && b && a->fullname && b->fullname &&
2568 strcmp(a->fullname, b->fullname) == 0 &&
2569 a->encoding && b->encoding)
2570 return strcmp(a->encoding, b->encoding);
2575 gchar *gtkaspell_get_default_dictionary(GtkAspell *gtkaspell)
2577 if (gtkaspell && gtkaspell->gtkaspeller &&
2578 gtkaspell->gtkaspeller->dictionary)
2579 return gtkaspell->gtkaspeller->dictionary->dictname;