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;
1526 gchar *utf8buf, *thelabel;
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 utf8buf = conv_codeset_strdup(gtkaspell->theword,
1553 conv_get_locale_charset_str(),
1556 thelabel = g_strdup_printf(_("<span weight=\"bold\" "
1557 "size=\"larger\">Replace \"%s\" with: </span>"),
1559 /* for title label */
1560 w_hbox = gtk_hbox_new(FALSE, 0);
1562 icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
1563 GTK_ICON_SIZE_DIALOG);
1564 gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
1565 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
1567 vbox = gtk_vbox_new (FALSE, 12);
1568 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1569 gtk_widget_show (vbox);
1571 label = gtk_label_new(thelabel);
1572 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1573 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1574 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1575 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1576 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1580 size = pango_font_description_get_size
1581 (label->style->font_desc);
1582 font_desc = pango_font_description_new();
1583 pango_font_description_set_weight
1584 (font_desc, PANGO_WEIGHT_BOLD);
1585 pango_font_description_set_size
1586 (font_desc, size * PANGO_SCALE_LARGE);
1589 gtk_widget_modify_font(label, font_desc);
1592 entry = gtk_entry_new();
1593 gtkaspell->replace_entry = entry;
1594 gtk_entry_set_text(GTK_ENTRY(entry), utf8buf);
1595 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1596 g_signal_connect(G_OBJECT(dialog),
1598 G_CALLBACK(replace_key_pressed), gtkaspell);
1599 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
1602 label = gtk_label_new(_("Holding down Control key while pressing "
1603 "Enter\nwill learn from mistake.\n"));
1604 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1605 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1606 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1607 gtk_widget_show(label);
1609 hbox = gtk_hbox_new(TRUE, 0);
1611 gtkut_stock_button_set_create(&confirm_area,
1612 &cancel_button, GTK_STOCK_CANCEL,
1613 &ok_button, GTK_STOCK_OK,
1616 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1617 confirm_area, FALSE, FALSE, 0);
1618 gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
1620 g_signal_connect(G_OBJECT(ok_button), "clicked",
1621 G_CALLBACK(replace_with_supplied_word_cb),
1623 g_signal_connect_swapped(G_OBJECT(ok_button), "clicked",
1624 G_CALLBACK(gtk_widget_destroy),
1627 g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked",
1628 G_CALLBACK(gtk_widget_destroy),
1631 gtk_widget_grab_focus(entry);
1633 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1635 gtk_widget_show_all(dialog);
1638 static void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1640 GtkTextView *gtktext;
1641 GtkTextBuffer *buffer;
1642 GtkTextIter startiter, enditer;
1644 gtktext = gtkaspell->gtktext;
1646 buffer = gtk_text_view_get_buffer(gtktext);
1647 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, 0);
1648 gtk_text_buffer_get_iter_at_offset(buffer, &enditer,
1649 get_textview_buffer_charcount(gtktext)-1);
1650 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
1651 &startiter, &enditer);
1654 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1656 GtkAspell *gtkaspell = (GtkAspell *) data;
1658 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1660 if (!gtkaspell->check_while_typing)
1661 gtkaspell_uncheck_all(gtkaspell);
1662 if (gtkaspell->menu_changed_cb)
1663 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
1666 static GSList *create_empty_dictionary_list(void)
1668 GSList *list = NULL;
1671 dict = g_new0(Dictionary, 1);
1672 dict->fullname = g_strdup(_("None"));
1673 dict->dictname = dict->fullname;
1674 dict->encoding = NULL;
1676 return g_slist_append(list, dict);
1679 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1680 static GSList *gtkaspell_get_dictionary_list(const gchar *aspell_path, gint refresh)
1684 AspellConfig *config;
1685 AspellDictInfoList *dlist;
1686 AspellDictInfoEnumeration *dels;
1687 const AspellDictInfo *entry;
1689 if (!gtkaspellcheckers)
1690 gtkaspell_checkers_init();
1692 if (gtkaspellcheckers->dictionary_list && !refresh)
1693 return gtkaspellcheckers->dictionary_list;
1695 gtkaspell_free_dictionary_list(
1696 gtkaspellcheckers->dictionary_list);
1699 config = new_aspell_config();
1701 aspell_config_replace(config, "dict-dir", aspell_path);
1702 if (aspell_config_error_number(config) != 0) {
1703 gtkaspellcheckers->error_message = g_strdup(
1704 aspell_config_error_message(config));
1705 gtkaspellcheckers->dictionary_list =
1706 create_empty_dictionary_list();
1708 return gtkaspellcheckers->dictionary_list;
1711 dlist = get_aspell_dict_info_list(config);
1712 delete_aspell_config(config);
1714 debug_print("Aspell: checking for dictionaries in %s\n", aspell_path?aspell_path:"(null)");
1715 dels = aspell_dict_info_list_elements(dlist);
1716 while ( (entry = aspell_dict_info_enumeration_next(dels)) != 0)
1718 dict = g_new0(Dictionary, 1);
1719 dict->fullname = g_strdup_printf("%s%s", aspell_path,
1721 dict->dictname = dict->fullname + strlen(aspell_path);
1722 dict->encoding = g_strdup(entry->code);
1724 if (g_slist_find_custom(list, dict,
1725 (GCompareFunc) compare_dict) != NULL) {
1726 dictionary_delete(dict);
1730 debug_print("Aspell: found dictionary %s %s %s\n", dict->fullname,
1731 dict->dictname, dict->encoding);
1732 list = g_slist_insert_sorted(list, dict,
1733 (GCompareFunc) compare_dict);
1736 delete_aspell_dict_info_enumeration(dels);
1740 debug_print("Aspell: error when searching for dictionaries: "
1741 "No dictionary found.\n");
1742 list = create_empty_dictionary_list();
1745 gtkaspellcheckers->dictionary_list = list;
1750 static void gtkaspell_free_dictionary_list(GSList *list)
1754 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1756 dict = (Dictionary *) walk->data;
1757 dictionary_delete(dict);
1762 GtkTreeModel *gtkaspell_dictionary_store_new_with_refresh(const gchar *aspell_path,
1765 GSList *dict_list, *tmp;
1766 GtkListStore *store;
1770 dict_list = gtkaspell_get_dictionary_list(aspell_path, refresh);
1771 g_return_val_if_fail(dict_list, NULL);
1773 store = gtk_list_store_new(SET_GTKASPELL_SIZE,
1778 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1779 dict = (Dictionary *) tmp->data;
1781 gtk_list_store_append(store, &iter);
1782 gtk_list_store_set(store, &iter,
1783 SET_GTKASPELL_NAME, dict->dictname,
1784 SET_GTKASPELL_FULLNAME, dict->fullname,
1788 return GTK_TREE_MODEL(store);
1791 GtkTreeModel *gtkaspell_dictionary_store_new(const gchar *aspell_path)
1793 return gtkaspell_dictionary_store_new_with_refresh
1794 (aspell_path, TRUE);
1797 GtkWidget *gtkaspell_dictionary_combo_new(const gchar *aspell_path,
1798 const gboolean refresh)
1801 GtkCellRenderer *renderer;
1803 combo = gtk_combo_box_new_with_model(
1804 gtkaspell_dictionary_store_new_with_refresh(aspell_path, refresh));
1805 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1806 gtk_widget_show(combo);
1808 renderer = gtk_cell_renderer_text_new();
1809 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
1810 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),renderer,
1811 "text", SET_GTKASPELL_NAME, NULL);
1816 gchar *gtkaspell_get_dictionary_menu_active_item(GtkComboBox *combo)
1819 GtkTreeModel *model;
1820 gchar *dict_fullname = NULL;
1822 g_return_val_if_fail(GTK_IS_COMBO_BOX(combo), NULL);
1823 g_return_val_if_fail(gtk_combo_box_get_active_iter(combo, &iter), NULL);
1825 model = gtk_combo_box_get_model(combo);
1829 gtk_tree_model_get(model, &iter,
1830 SET_GTKASPELL_FULLNAME, &dict_fullname,
1833 return dict_fullname;
1836 gint gtkaspell_set_dictionary_menu_active_item(GtkComboBox *combo,
1837 const gchar *dictionary)
1839 GtkTreeModel *model;
1841 gchar *dict_name = NULL;
1843 g_return_val_if_fail(combo != NULL, 0);
1844 g_return_val_if_fail(dictionary != NULL, 0);
1845 g_return_val_if_fail(GTK_IS_COMBO_BOX(combo), 0);
1847 if((model = gtk_combo_box_get_model(combo)) == NULL)
1849 if((gtk_tree_model_get_iter_first(model, &iter)) == FALSE)
1853 gtk_tree_model_get(model, &iter,
1854 SET_GTKASPELL_FULLNAME, &dict_name,
1857 if ((dict_name != NULL) && !strcmp2(dict_name, dictionary)) {
1858 gtk_combo_box_set_active_iter(combo, &iter);
1865 } while ((gtk_tree_model_iter_next(model, &iter)) == TRUE);
1870 GtkWidget *gtkaspell_sugmode_combo_new(gint sugmode)
1872 GtkWidget *combo = gtkut_sc_combobox_create(NULL, FALSE);
1873 GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(
1874 GTK_COMBO_BOX(combo)));
1877 g_return_val_if_fail(store != NULL, NULL);
1879 COMBOBOX_ADD(store, _("Fast Mode"), ASPELL_FASTMODE);
1880 COMBOBOX_ADD(store, _("Normal Mode"), ASPELL_NORMALMODE);
1881 COMBOBOX_ADD(store, _("Bad Spellers Mode"), ASPELL_BADSPELLERMODE);
1883 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), sugmode - 1);
1884 gtk_widget_show(combo);
1889 static void use_alternate_dict(GtkAspell *gtkaspell)
1893 tmp = gtkaspell->gtkaspeller;
1894 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1895 gtkaspell->alternate_speller = tmp;
1898 static void destroy_menu(GtkWidget *widget,
1899 gpointer user_data) {
1901 GtkAspell *gtkaspell = (GtkAspell *)user_data;
1903 if (gtkaspell->accel_group) {
1904 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1905 gtkaspell->accel_group);
1906 gtkaspell->accel_group = NULL;
1910 static gboolean aspell_key_pressed(GtkWidget *widget,
1912 GtkAspell *gtkaspell)
1914 if (event && (isascii(event->keyval) || event->keyval == GDK_Return)) {
1915 gtk_accel_groups_activate(
1916 G_OBJECT(gtkaspell->parent_window),
1917 event->keyval, event->state);
1918 } else if (event && event->keyval == GDK_Escape) {
1919 destroy_menu(NULL, gtkaspell);
1924 /* make_sug_menu() - Add menus to accept this word for this session
1925 * and to add it to personal dictionary
1927 static GSList *make_sug_menu(GtkAspell *gtkaspell)
1931 GtkTextView *gtktext;
1932 GtkAccelGroup *accel;
1933 GList *l = gtkaspell->suggestions_list;
1935 GSList *list = NULL;
1936 gtktext = gtkaspell->gtktext;
1938 accel = gtk_accel_group_new();
1940 if (gtkaspell->accel_group) {
1941 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1942 gtkaspell->accel_group);
1943 gtkaspell->accel_group = NULL;
1946 utf8buf = conv_codeset_strdup((char*)l->data,
1947 conv_get_locale_charset_str(),
1949 caption = g_strdup_printf(_("\"%s\" unknown in %s"),
1951 gtkaspell->gtkaspeller->dictionary->dictname);
1952 item = gtk_menu_item_new_with_label(caption);
1954 gtk_widget_show(item);
1955 list = g_slist_append(list, item);
1956 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
1959 item = gtk_menu_item_new();
1960 gtk_widget_show(item);
1961 list = g_slist_append(list, item);
1963 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1964 gtk_widget_show(item);
1965 list = g_slist_append(list, item);
1966 g_signal_connect(G_OBJECT(item), "activate",
1967 G_CALLBACK(add_word_to_session_cb),
1969 gtk_widget_add_accelerator(item, "activate", accel, GDK_space,
1971 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1973 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1974 gtk_widget_show(item);
1975 list = g_slist_append(list, item);
1976 g_signal_connect(G_OBJECT(item), "activate",
1977 G_CALLBACK(add_word_to_personal_cb),
1979 gtk_widget_add_accelerator(item, "activate", accel, GDK_Return,
1981 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1983 item = gtk_menu_item_new_with_label(_("Replace with..."));
1984 gtk_widget_show(item);
1985 list = g_slist_append(list, item);
1986 g_signal_connect(G_OBJECT(item), "activate",
1987 G_CALLBACK(replace_with_create_dialog_cb),
1989 gtk_widget_add_accelerator(item, "activate", accel, GDK_R, 0,
1990 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1991 gtk_widget_add_accelerator(item, "activate", accel, GDK_R,
1995 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1996 caption = g_strdup_printf(_("Check with %s"),
1997 gtkaspell->alternate_speller->dictionary->dictname);
1998 item = gtk_menu_item_new_with_label(caption);
2000 gtk_widget_show(item);
2001 list = g_slist_append(list, item);
2002 g_signal_connect(G_OBJECT(item), "activate",
2003 G_CALLBACK(check_with_alternate_cb),
2005 gtk_widget_add_accelerator(item, "activate", accel, GDK_X, 0,
2006 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
2007 gtk_widget_add_accelerator(item, "activate", accel, GDK_X,
2012 item = gtk_menu_item_new();
2013 gtk_widget_show(item);
2014 list = g_slist_append(list, item);
2018 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
2019 gtk_widget_show(item);
2020 list = g_slist_append(list, item);
2022 GtkWidget *curmenu = NULL;
2026 if (count == MENUCOUNT) {
2029 item = gtk_menu_item_new_with_label(_("More..."));
2030 gtk_widget_show(item);
2032 gtk_menu_append(GTK_MENU(curmenu), item);
2034 list = g_slist_append(list, item);
2036 curmenu = gtk_menu_new();
2037 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2041 utf8buf = conv_codeset_strdup((char*)l->data,
2042 conv_get_locale_charset_str(),
2044 item = gtk_menu_item_new_with_label(utf8buf);
2046 gtk_widget_show(item);
2047 if (curmenu == NULL) {
2048 list = g_slist_append(list, item);
2050 gtk_menu_append(GTK_MENU(curmenu), item);
2052 g_signal_connect(G_OBJECT(item), "activate",
2053 G_CALLBACK(replace_word_cb),
2056 if (curmenu == NULL && count < MENUCOUNT) {
2057 gtk_widget_add_accelerator(item, "activate",
2062 gtk_widget_add_accelerator(item, "activate",
2071 } while ((l = l->next) != NULL);
2074 gtk_window_add_accel_group
2075 (GTK_WINDOW(gtkaspell->parent_window),
2077 gtkaspell->accel_group = accel;
2082 static GSList *populate_submenu(GtkAspell *gtkaspell)
2084 GtkWidget *item, *submenu;
2086 GtkAspeller *gtkaspeller = NULL;
2087 GSList *list = NULL;
2092 gtkaspeller = gtkaspell->gtkaspeller;
2093 dictname = g_strdup_printf(_("Dictionary: %s"),
2094 gtkaspeller->dictionary->dictname);
2095 item = gtk_menu_item_new_with_label(dictname);
2096 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
2098 gtk_widget_show(item);
2099 list = g_slist_append(list, item);
2101 item = gtk_menu_item_new();
2102 gtk_widget_show(item);
2103 list = g_slist_append(list, item);
2105 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
2106 dictname = g_strdup_printf(_("Use alternate (%s)"),
2107 gtkaspell->alternate_speller->dictionary->dictname);
2108 item = gtk_menu_item_new_with_label(dictname);
2110 g_signal_connect(G_OBJECT(item), "activate",
2111 G_CALLBACK(switch_to_alternate_cb),
2113 gtk_widget_show(item);
2114 list = g_slist_append(list, item);
2117 item = gtk_check_menu_item_new_with_label(_("Use both dictionaries"));
2118 if (gtkaspell->use_both_dicts) {
2119 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2121 g_signal_connect(G_OBJECT(item), "activate",
2122 G_CALLBACK(set_use_both_cb),
2124 gtk_widget_show(item);
2125 list = g_slist_append(list, item);
2127 item = gtk_menu_item_new();
2128 gtk_widget_show(item);
2129 list = g_slist_append(list, item);
2131 item = gtk_check_menu_item_new_with_label(_("Fast Mode"));
2132 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
2133 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_FASTMODE) {
2134 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),TRUE);
2135 gtk_widget_set_sensitive(GTK_WIDGET(item),FALSE);
2137 g_signal_connect(G_OBJECT(item), "activate",
2138 G_CALLBACK(set_sug_mode_cb),
2140 gtk_widget_show(item);
2141 list = g_slist_append(list, item);
2143 item = gtk_check_menu_item_new_with_label(_("Normal Mode"));
2144 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
2145 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_NORMALMODE) {
2146 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
2147 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2149 g_signal_connect(G_OBJECT(item), "activate",
2150 G_CALLBACK(set_sug_mode_cb),
2152 gtk_widget_show(item);
2153 list = g_slist_append(list, item);
2155 item = gtk_check_menu_item_new_with_label(_("Bad Spellers Mode"));
2156 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
2157 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_BADSPELLERMODE) {
2158 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
2159 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2161 g_signal_connect(G_OBJECT(item), "activate",
2162 G_CALLBACK(set_sug_mode_cb),
2164 gtk_widget_show(item);
2165 list = g_slist_append(list, item);
2167 item = gtk_menu_item_new();
2168 gtk_widget_show(item);
2169 list = g_slist_append(list, item);
2171 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
2172 if (gtkaspell->check_while_typing)
2173 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2175 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2176 g_signal_connect(G_OBJECT(item), "activate",
2177 G_CALLBACK(toggle_check_while_typing_cb),
2179 gtk_widget_show(item);
2180 list = g_slist_append(list, item);
2182 item = gtk_menu_item_new();
2183 gtk_widget_show(item);
2184 list = g_slist_append(list, item);
2186 submenu = gtk_menu_new();
2187 item = gtk_menu_item_new_with_label(_("Change dictionary"));
2188 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
2189 gtk_widget_show(item);
2190 list = g_slist_append(list, item);
2193 if (gtkaspellcheckers->dictionary_list == NULL)
2194 gtkaspell_get_dictionary_list(gtkaspell->dictionary_path, FALSE);
2196 GtkWidget * curmenu = submenu;
2200 tmp = gtkaspellcheckers->dictionary_list;
2202 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
2203 tmp = g_slist_next(tmp)) {
2204 if (count == MENUCOUNT) {
2207 newmenu = gtk_menu_new();
2208 item = gtk_menu_item_new_with_label(_("More..."));
2209 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2212 gtk_menu_append(GTK_MENU(curmenu), item);
2213 gtk_widget_show(item);
2217 dict = (Dictionary *) tmp->data;
2218 item = gtk_check_menu_item_new_with_label(dict->dictname);
2219 g_object_set_data(G_OBJECT(item), "dict_name",
2221 if (strcmp2(dict->fullname,
2222 gtkaspell->gtkaspeller->dictionary->fullname))
2223 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2225 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2226 gtk_widget_set_sensitive(GTK_WIDGET(item),
2229 g_signal_connect(G_OBJECT(item), "activate",
2230 G_CALLBACK(change_dict_cb),
2232 gtk_widget_show(item);
2233 gtk_menu_append(GTK_MENU(curmenu), item);
2241 GSList *gtkaspell_make_config_menu(GtkAspell *gtkaspell)
2243 return populate_submenu(gtkaspell);
2246 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y,
2247 gboolean *push_in, gpointer data)
2249 GtkAspell *gtkaspell = (GtkAspell *) data;
2250 gint xx = 0, yy = 0;
2253 GtkTextView *text = GTK_TEXT_VIEW(gtkaspell->gtktext);
2254 GtkTextBuffer *textbuf;
2259 textbuf = gtk_text_view_get_buffer(gtkaspell->gtktext);
2260 gtk_text_buffer_get_iter_at_mark(textbuf, &iter,
2261 gtk_text_buffer_get_insert(textbuf));
2262 gtk_text_view_get_iter_location(gtkaspell->gtktext, &iter, &rect);
2263 gtk_text_view_buffer_to_window_coords(text, GTK_TEXT_WINDOW_TEXT,
2267 gdk_window_get_origin(GTK_WIDGET(gtkaspell->gtktext)->window, &xx, &yy);
2269 sx = gdk_screen_width();
2270 sy = gdk_screen_height();
2272 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2278 gdk_char_width(gtk_style_get_font(GTK_WIDGET(text)->style), ' ');
2280 *y = rect.y + rect.height + yy;
2286 gdk_string_height(gtk_style_get_font(
2287 GTK_WIDGET(text)->style),
2288 gtkaspell->theword);
2291 /* change the current dictionary of gtkaspell
2292 - if always_set_alt_dict is set, the alternate dict is unconditionally set to the former
2293 current dictionary (common use: from menu callbacks)
2294 - if always_set_alt_dict is NOT set, the alternate dict will be set to the former
2295 current dictionary only if there is no alternate dictionary already set
2296 (this is when we need to set the current dictionary then the alternate one
2297 when creating a compose window, from the account and folder settings)
2299 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary,
2300 gboolean always_set_alt_dict)
2303 GtkAspeller *gtkaspeller;
2306 g_return_val_if_fail(gtkaspell, FALSE);
2307 g_return_val_if_fail(dictionary, FALSE);
2309 sug_mode = gtkaspell->default_sug_mode;
2311 dict = g_new0(Dictionary, 1);
2312 dict->fullname = g_strdup(dictionary);
2313 dict->encoding = g_strdup(gtkaspell->gtkaspeller->dictionary->encoding);
2315 gtkaspeller = gtkaspeller_new(dict);
2318 alertpanel_warning(_("The spell checker could not change dictionary.\n%s"),
2319 gtkaspellcheckers->error_message);
2321 if (gtkaspell->use_alternate) {
2322 if (gtkaspell->alternate_speller) {
2323 if (always_set_alt_dict) {
2324 gtkaspeller_delete(gtkaspell->alternate_speller);
2325 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2327 gtkaspeller_delete(gtkaspell->gtkaspeller);
2329 /* should never be reached as the dicts are always set
2330 to a default value */
2331 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2333 gtkaspeller_delete(gtkaspell->gtkaspeller);
2335 gtkaspell->gtkaspeller = gtkaspeller;
2336 gtkaspell_set_sug_mode(gtkaspell, sug_mode);
2339 dictionary_delete(dict);
2344 /* change the alternate dictionary of gtkaspell (doesn't affect the default dictionary) */
2345 gboolean gtkaspell_change_alt_dict(GtkAspell *gtkaspell, const gchar *alt_dictionary)
2348 GtkAspeller *gtkaspeller;
2350 g_return_val_if_fail(gtkaspell, FALSE);
2351 g_return_val_if_fail(alt_dictionary, FALSE);
2353 dict = g_new0(Dictionary, 1);
2354 dict->fullname = g_strdup(alt_dictionary);
2355 dict->encoding = g_strdup(gtkaspell->gtkaspeller->dictionary->encoding);
2357 gtkaspeller = gtkaspeller_new(dict);
2360 alertpanel_warning(_("The spell checker could not change the alternate dictionary.\n%s"),
2361 gtkaspellcheckers->error_message);
2363 if (gtkaspell->alternate_speller)
2364 gtkaspeller_delete(gtkaspell->alternate_speller);
2365 gtkaspell->alternate_speller = gtkaspeller;
2368 dictionary_delete(dict);
2373 /* Menu call backs */
2375 /* change_dict_cb() - Menu callback : change dict */
2376 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2380 fullname = (gchar *) g_object_get_data(G_OBJECT(w), "dict_name");
2382 if (!strcmp2(fullname, _("None")))
2385 gtkaspell_change_dict(gtkaspell, fullname, TRUE);
2386 if (gtkaspell->recheck_when_changing_dict) {
2387 gtkaspell_highlight_all(gtkaspell);
2389 if (gtkaspell->menu_changed_cb)
2390 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2393 static void switch_to_alternate_cb(GtkWidget *w,
2396 GtkAspell *gtkaspell = (GtkAspell *) data;
2397 use_alternate_dict(gtkaspell);
2398 if (gtkaspell->recheck_when_changing_dict) {
2399 gtkaspell_highlight_all(gtkaspell);
2401 if (gtkaspell->menu_changed_cb)
2402 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2405 /* Misc. helper functions */
2407 static void set_point_continue(GtkAspell *gtkaspell)
2409 GtkTextView *gtktext;
2411 gtktext = gtkaspell->gtktext;
2413 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
2415 if (gtkaspell->continue_check)
2416 gtkaspell->continue_check((gpointer *) gtkaspell);
2419 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2421 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtkaspell->gtktext);
2422 GdkColor *color = &(gtkaspell->highlight);
2424 /* Shameless copy from Sylpheed's gtkutils.c */
2426 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2428 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2430 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2434 gtk_text_buffer_create_tag(buffer, "misspelled",
2435 "foreground-gdk", color, NULL);
2437 gtk_text_buffer_create_tag(buffer, "misspelled",
2438 "underline", PANGO_UNDERLINE_ERROR, NULL);
2442 static void change_color(GtkAspell * gtkaspell,
2443 gint start, gint end,
2447 GtkTextView *gtktext;
2448 GtkTextBuffer *buffer;
2449 GtkTextIter startiter, enditer;
2454 gtktext = gtkaspell->gtktext;
2456 buffer = gtk_text_view_get_buffer(gtktext);
2457 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, start);
2458 gtk_text_buffer_get_iter_at_offset(buffer, &enditer, end);
2460 gtk_text_buffer_apply_tag_by_name(buffer, "misspelled",
2461 &startiter, &enditer);
2463 gtk_text_iter_forward_char(&enditer);
2464 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
2465 &startiter, &enditer);
2469 /* convert_to_aspell_encoding () - converts ISO-8859-* strings to iso8859-*
2470 * as needed by aspell. Returns an allocated string.
2473 static gchar *convert_to_aspell_encoding (const gchar *encoding)
2475 gchar * aspell_encoding;
2477 if (strstr2(encoding, "ISO-8859-")) {
2478 aspell_encoding = g_strdup_printf("iso8859%s", encoding+8);
2481 if (!strcmp2(encoding, "US-ASCII"))
2482 aspell_encoding = g_strdup("iso8859-1");
2484 aspell_encoding = g_strdup(encoding);
2487 return aspell_encoding;
2490 /* compare_dict () - compare 2 dict names */
2491 static gint compare_dict(Dictionary *a, Dictionary *b)
2493 guint aparts = 0, bparts = 0;
2496 for (i=0; i < strlen(a->dictname); i++)
2497 if (a->dictname[i] == '-')
2499 for (i=0; i < strlen(b->dictname); i++)
2500 if (b->dictname[i] == '-')
2503 if (aparts != bparts)
2504 return (aparts < bparts) ? -1 : +1;
2507 compare = strcmp2(a->dictname, b->dictname);
2509 compare = strcmp2(a->fullname, b->fullname);
2514 static void dictionary_delete(Dictionary *dict)
2516 g_free(dict->fullname);
2517 g_free(dict->encoding);
2521 static Dictionary *dictionary_dup(const Dictionary *dict)
2525 dict2 = g_new(Dictionary, 1);
2527 dict2->fullname = g_strdup(dict->fullname);
2528 dict2->dictname = dict->dictname - dict->fullname + dict2->fullname;
2529 dict2->encoding = g_strdup(dict->encoding);
2534 static void free_suggestions_list(GtkAspell *gtkaspell)
2538 for (list = gtkaspell->suggestions_list; list != NULL;
2542 g_list_free(gtkaspell->suggestions_list);
2544 gtkaspell->max_sug = -1;
2545 gtkaspell->suggestions_list = NULL;
2548 static void reset_theword_data(GtkAspell *gtkaspell)
2550 gtkaspell->start_pos = 0;
2551 gtkaspell->end_pos = 0;
2552 gtkaspell->theword[0] = 0;
2553 gtkaspell->max_sug = -1;
2555 free_suggestions_list(gtkaspell);
2558 static void free_checkers(gpointer elt, gpointer data)
2560 GtkAspeller *gtkaspeller = elt;
2562 g_return_if_fail(gtkaspeller);
2564 gtkaspeller_real_delete(gtkaspeller);
2567 static gint find_gtkaspeller(gconstpointer aa, gconstpointer bb)
2569 Dictionary *a = ((GtkAspeller *) aa)->dictionary;
2570 Dictionary *b = ((GtkAspeller *) bb)->dictionary;
2572 if (a && b && a->fullname && b->fullname &&
2573 strcmp(a->fullname, b->fullname) == 0 &&
2574 a->encoding && b->encoding)
2575 return strcmp(a->encoding, b->encoding);
2580 gchar *gtkaspell_get_default_dictionary(GtkAspell *gtkaspell)
2582 if (gtkaspell && gtkaspell->gtkaspeller &&
2583 gtkaspell->gtkaspeller->dictionary)
2584 return gtkaspell->gtkaspeller->dictionary->dictname;