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 2 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, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Stuphead: (C) 2000,2001 Grigroy Bakunov, Sergey Pinaev
21 * Adapted for Sylpheed (Claws) (c) 2001-2002 by Hiroyuki Yamamoto &
22 * The Claws Mail Team.
23 * Adapted for pspell (c) 2001-2002 Melvin Hadasht
24 * Adapted for GNU/aspell (c) 2002 Melvin Hadasht
36 #include <sys/types.h>
48 #include <glib/gi18n.h>
52 #include <gtk/gtkoptionmenu.h>
53 #include <gtk/gtkmenu.h>
54 #include <gtk/gtkmenuitem.h>
55 #include <gdk/gdkkeysyms.h>
61 #include "alertpanel.h"
62 #include "gtkaspell.h"
63 #include "gtk/gtkutils.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(); \
91 typedef struct _GtkAspellCheckers {
93 GSList *dictionary_list;
97 typedef struct _Dictionary {
103 typedef struct _GtkAspeller {
104 Dictionary *dictionary;
106 AspellConfig *config;
107 AspellSpeller *checker;
110 typedef void (*ContCheckFunc) (gpointer *gtkaspell);
114 GtkAspeller *gtkaspeller;
115 GtkAspeller *alternate_speller;
116 gchar *dictionary_path;
117 gchar theword[GTKASPELLWORDSIZE];
123 gboolean check_while_typing;
124 gboolean recheck_when_changing_dict;
125 gboolean use_alternate;
126 gboolean use_both_dicts;
128 ContCheckFunc continue_check;
130 GtkWidget *replace_entry;
131 GtkWidget *parent_window;
133 gint default_sug_mode;
135 GList *suggestions_list;
137 GtkTextView *gtktext;
139 GtkAccelGroup *accel_group;
140 void (*menu_changed_cb)(void *data);
141 void *menu_changed_data;
144 typedef AspellConfig GtkAspellConfig;
146 /******************************************************************************/
148 static GtkAspellCheckers *gtkaspellcheckers;
150 /* Error message storage */
151 static void gtkaspell_checkers_error_message (gchar *message);
154 static void entry_insert_cb (GtkTextBuffer *textbuf,
158 GtkAspell *gtkaspell);
159 static void entry_delete_cb (GtkTextBuffer *textbuf,
160 GtkTextIter *startiter,
161 GtkTextIter *enditer,
162 GtkAspell *gtkaspell);
163 /*static gint button_press_intercept_cb (GtkTextView *gtktext,
165 GtkAspell *gtkaspell);
167 static void button_press_intercept_cb(GtkTextView *gtktext,
168 GtkMenu *menu, GtkAspell *gtkaspell);
170 /* Checker creation */
171 static GtkAspeller* gtkaspeller_new (Dictionary *dict);
172 static GtkAspeller* gtkaspeller_real_new (Dictionary *dict);
173 static GtkAspeller* gtkaspeller_delete (GtkAspeller *gtkaspeller);
174 static GtkAspeller* gtkaspeller_real_delete (GtkAspeller *gtkaspeller);
176 /* Checker configuration */
177 static gint set_dictionary (AspellConfig *config,
179 static void set_sug_mode_cb (GtkMenuItem *w,
180 GtkAspell *gtkaspell);
181 static void set_use_both_cb (GtkMenuItem *w,
182 GtkAspell *gtkaspell);
183 static void set_real_sug_mode (GtkAspell *gtkaspell,
184 const char *themode);
186 /* Checker actions */
187 static gboolean check_at (GtkAspell *gtkaspell,
189 static gboolean check_next_prev (GtkAspell *gtkaspell,
191 static GList* misspelled_suggest (GtkAspell *gtkaspell,
193 static void add_word_to_session_cb (GtkWidget *w,
195 static void add_word_to_personal_cb (GtkWidget *w,
197 static void replace_with_create_dialog_cb (GtkWidget *w,
199 static void replace_with_supplied_word_cb (GtkWidget *w,
200 GtkAspell *gtkaspell);
201 static void replace_word_cb (GtkWidget *w,
203 static void replace_real_word (GtkAspell *gtkaspell,
205 static void check_with_alternate_cb (GtkWidget *w,
207 static void use_alternate_dict (GtkAspell *gtkaspell);
208 static void toggle_check_while_typing_cb (GtkWidget *w,
212 static GSList* make_sug_menu (GtkAspell *gtkaspell);
213 static GSList * populate_submenu (GtkAspell *gtkaspell);
214 GSList* gtkaspell_make_config_menu (GtkAspell *gtkaspell);
215 static void set_menu_pos (GtkMenu *menu,
220 /* Other menu callbacks */
221 static gboolean aspell_key_pressed (GtkWidget *widget,
223 GtkAspell *gtkaspell);
224 static void change_dict_cb (GtkWidget *w,
225 GtkAspell *gtkaspell);
226 static void switch_to_alternate_cb (GtkWidget *w,
229 /* Misc. helper functions */
230 static void set_point_continue (GtkAspell *gtkaspell);
231 static void continue_check (gpointer *gtkaspell);
232 static gboolean iswordsep (gunichar c);
233 static gunichar get_text_index_whar (GtkAspell *gtkaspell,
235 static gboolean get_word_from_pos (GtkAspell *gtkaspell,
241 static void allocate_color (GtkAspell *gtkaspell,
243 static void change_color (GtkAspell *gtkaspell,
248 static gchar* convert_to_aspell_encoding (const gchar *encoding);
249 static gint compare_dict (Dictionary *a,
251 static void dictionary_delete (Dictionary *dict);
252 static Dictionary * dictionary_dup (const Dictionary *dict);
253 static void free_suggestions_list (GtkAspell *gtkaspell);
254 static void reset_theword_data (GtkAspell *gtkaspell);
255 static void free_checkers (gpointer elt,
257 static gint find_gtkaspeller (gconstpointer aa,
259 /* gtkspellconfig - only one config per session */
260 GtkAspellConfig * gtkaspellconfig;
261 static void destroy_menu(GtkWidget *widget, gpointer user_data);
263 /******************************************************************************/
264 static gint get_textview_buffer_charcount(GtkTextView *view);
266 static gint get_textview_buffer_charcount(GtkTextView *view)
268 GtkTextBuffer *buffer;
270 g_return_val_if_fail(view, 0);
272 buffer = gtk_text_view_get_buffer(view);
273 g_return_val_if_fail(buffer, 0);
275 return gtk_text_buffer_get_char_count(buffer);
277 static gint get_textview_buffer_offset(GtkTextView *view)
279 GtkTextBuffer * buffer;
283 g_return_val_if_fail(view, 0);
285 buffer = gtk_text_view_get_buffer(view);
286 g_return_val_if_fail(buffer, 0);
288 mark = gtk_text_buffer_get_insert(buffer);
289 g_return_val_if_fail(mark, 0);
291 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
293 return gtk_text_iter_get_offset(&iter);
295 static void set_textview_buffer_offset(GtkTextView *view, gint offset)
297 GtkTextBuffer *buffer;
300 g_return_if_fail(view);
302 buffer = gtk_text_view_get_buffer(view);
303 g_return_if_fail(buffer);
305 gtk_text_buffer_get_iter_at_offset(buffer, &iter, offset);
306 gtk_text_buffer_place_cursor(buffer, &iter);
308 /******************************************************************************/
310 void gtkaspell_checkers_init(void)
312 gtkaspellcheckers = g_new(GtkAspellCheckers, 1);
313 gtkaspellcheckers->checkers = NULL;
314 gtkaspellcheckers->dictionary_list = NULL;
315 gtkaspellcheckers->error_message = NULL;
318 void gtkaspell_checkers_quit(void)
323 if (gtkaspellcheckers == NULL)
326 if ((checkers = gtkaspellcheckers->checkers)) {
327 debug_print("Aspell: number of running checkers to delete %d\n",
328 g_slist_length(checkers));
330 g_slist_foreach(checkers, free_checkers, NULL);
331 g_slist_free(checkers);
332 gtkaspellcheckers->checkers = NULL;
335 if ((dict_list = gtkaspellcheckers->dictionary_list)) {
336 debug_print("Aspell: number of dictionaries to delete %d\n",
337 g_slist_length(dict_list));
339 gtkaspell_free_dictionary_list(dict_list);
340 gtkaspellcheckers->dictionary_list = NULL;
343 g_free(gtkaspellcheckers->error_message);
344 gtkaspellcheckers->error_message = NULL;
348 static void gtkaspell_checkers_error_message (gchar *message)
351 if (gtkaspellcheckers->error_message) {
352 tmp = g_strdup_printf("%s\n%s",
353 gtkaspellcheckers->error_message,
356 g_free(gtkaspellcheckers->error_message);
357 gtkaspellcheckers->error_message = tmp;
359 gtkaspellcheckers->error_message = message;
362 const char *gtkaspell_checkers_strerror(void)
364 g_return_val_if_fail(gtkaspellcheckers, "");
365 return gtkaspellcheckers->error_message;
368 void gtkaspell_checkers_reset_error(void)
370 g_return_if_fail(gtkaspellcheckers);
372 g_free(gtkaspellcheckers->error_message);
374 gtkaspellcheckers->error_message = NULL;
377 GtkAspell *gtkaspell_new(const gchar *dictionary_path,
378 const gchar *dictionary,
379 const gchar *alt_dictionary,
380 const gchar *encoding,
381 gint misspelled_color,
382 gboolean check_while_typing,
383 gboolean recheck_when_changing_dict,
384 gboolean use_alternate,
385 gboolean use_both_dicts,
386 GtkTextView *gtktext,
387 GtkWindow *parent_win,
388 void (*spell_menu_cb)(void *data),
392 GtkAspell *gtkaspell;
393 GtkAspeller *gtkaspeller;
394 GtkTextBuffer *buffer;
396 g_return_val_if_fail(gtktext, NULL);
397 g_return_val_if_fail(dictionary && strlen(dictionary) > 0,
400 g_return_val_if_fail(dictionary_path && strlen(dictionary_path) > 0,
403 buffer = gtk_text_view_get_buffer(gtktext);
405 dict = g_new0(Dictionary, 1);
406 dict->fullname = g_strdup(dictionary);
407 dict->encoding = g_strdup(encoding);
409 gtkaspeller = gtkaspeller_new(dict);
410 dictionary_delete(dict);
415 gtkaspell = g_new0(GtkAspell, 1);
417 gtkaspell->dictionary_path = g_strdup(dictionary_path);
419 gtkaspell->gtkaspeller = gtkaspeller;
421 if (use_alternate && alt_dictionary && *alt_dictionary) {
422 Dictionary *alt_dict;
423 GtkAspeller *alt_gtkaspeller;
425 alt_dict = g_new0(Dictionary, 1);
426 alt_dict->fullname = g_strdup(alt_dictionary);
427 alt_dict->encoding = g_strdup(encoding);
429 alt_gtkaspeller = gtkaspeller_new(alt_dict);
430 dictionary_delete(alt_dict);
432 if (!alt_gtkaspeller)
435 gtkaspell->alternate_speller = alt_gtkaspeller;
437 gtkaspell->alternate_speller = NULL;
440 gtkaspell->theword[0] = 0x00;
441 gtkaspell->start_pos = 0;
442 gtkaspell->end_pos = 0;
443 gtkaspell->orig_pos = -1;
444 gtkaspell->end_check_pos = -1;
445 gtkaspell->misspelled = -1;
446 gtkaspell->check_while_typing = check_while_typing;
447 gtkaspell->recheck_when_changing_dict = recheck_when_changing_dict;
448 gtkaspell->continue_check = NULL;
449 gtkaspell->replace_entry = NULL;
450 gtkaspell->gtktext = gtktext;
451 gtkaspell->default_sug_mode = ASPELL_FASTMODE;
452 gtkaspell->max_sug = -1;
453 gtkaspell->suggestions_list = NULL;
454 gtkaspell->use_alternate = use_alternate;
455 gtkaspell->use_both_dicts = use_both_dicts;
456 gtkaspell->parent_window = GTK_WIDGET(parent_win);
457 gtkaspell->menu_changed_cb = spell_menu_cb;
458 gtkaspell->menu_changed_data = data;
460 allocate_color(gtkaspell, misspelled_color);
462 g_signal_connect_after(G_OBJECT(buffer), "insert-text",
463 G_CALLBACK(entry_insert_cb), gtkaspell);
464 g_signal_connect_after(G_OBJECT(buffer), "delete-range",
465 G_CALLBACK(entry_delete_cb), gtkaspell);
466 /*g_signal_connect(G_OBJECT(gtktext), "button-press-event",
467 G_CALLBACK(button_press_intercept_cb),
469 g_signal_connect(G_OBJECT(gtktext), "populate-popup",
470 G_CALLBACK(button_press_intercept_cb), gtkaspell);
472 debug_print("Aspell: created gtkaspell %p\n", gtkaspell);
477 void gtkaspell_delete(GtkAspell *gtkaspell)
479 GtkTextView *gtktext = gtkaspell->gtktext;
481 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
482 G_CALLBACK(entry_insert_cb),
484 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
485 G_CALLBACK(entry_delete_cb),
487 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
488 G_CALLBACK(button_press_intercept_cb),
491 gtkaspell_uncheck_all(gtkaspell);
493 gtkaspeller_delete(gtkaspell->gtkaspeller);
495 if (gtkaspell->alternate_speller)
496 gtkaspeller_delete(gtkaspell->alternate_speller);
498 if (gtkaspell->suggestions_list)
499 free_suggestions_list(gtkaspell);
501 g_free((gchar *)gtkaspell->dictionary_path);
502 gtkaspell->dictionary_path = NULL;
504 debug_print("Aspell: deleting gtkaspell %p\n", gtkaspell);
511 static void entry_insert_cb(GtkTextBuffer *textbuf,
515 GtkAspell *gtkaspell)
519 g_return_if_fail(gtkaspell->gtkaspeller->checker);
521 if (!gtkaspell->check_while_typing)
524 pos = gtk_text_iter_get_offset(iter);
526 if (iswordsep(g_utf8_get_char(newtext))) {
527 /* did we just end a word? */
529 check_at(gtkaspell, pos - 2);
531 /* did we just split a word? */
532 if (pos < gtk_text_buffer_get_char_count(textbuf))
533 check_at(gtkaspell, pos + 1);
535 /* check as they type, *except* if they're typing at the end (the most
538 if (pos < gtk_text_buffer_get_char_count(textbuf) &&
539 !iswordsep(get_text_index_whar(gtkaspell, pos))) {
540 check_at(gtkaspell, pos - 1);
545 static void entry_delete_cb(GtkTextBuffer *textbuf,
546 GtkTextIter *startiter,
547 GtkTextIter *enditer,
548 GtkAspell *gtkaspell)
553 g_return_if_fail(gtkaspell->gtkaspeller->checker);
555 if (!gtkaspell->check_while_typing)
558 start = gtk_text_iter_get_offset(startiter);
559 end = gtk_text_iter_get_offset(enditer);
560 origpos = get_textview_buffer_offset(gtkaspell->gtktext);
562 check_at(gtkaspell, start - 1);
563 check_at(gtkaspell, start);
566 set_textview_buffer_offset(gtkaspell->gtktext, origpos);
567 /* this is to *UNDO* the selection, in case they were holding shift
568 * while hitting backspace. */
569 /* needed with textview ??? */
570 /* gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos); */
573 static void button_press_intercept_cb(GtkTextView *gtktext,
574 GtkMenu *menu, GtkAspell *gtkaspell)
576 GtkMenuItem *menuitem;
577 GSList *spell_menu = NULL;
579 gboolean suggest = FALSE;
581 menuitem = GTK_MENU_ITEM(gtk_separator_menu_item_new());
582 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
583 gtk_widget_show(GTK_WIDGET(menuitem));
585 gtktext = gtkaspell->gtktext;
587 gtkaspell->orig_pos = get_textview_buffer_offset(gtktext);
589 if (check_at(gtkaspell, gtkaspell->orig_pos)) {
591 if (misspelled_suggest(gtkaspell, gtkaspell->theword)) {
592 spell_menu = make_sug_menu(gtkaspell);
597 spell_menu = gtkaspell_make_config_menu(gtkaspell);
599 spell_menu = g_slist_reverse(spell_menu);
600 for (items = spell_menu;
601 items; items = items->next) {
602 menuitem = GTK_MENU_ITEM(items->data);
603 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
604 gtk_widget_show(GTK_WIDGET(menuitem));
606 g_slist_free(spell_menu);
608 g_signal_connect(G_OBJECT(menu), "deactivate",
609 G_CALLBACK(destroy_menu),
612 g_signal_connect(G_OBJECT(menu),
614 G_CALLBACK(aspell_key_pressed), gtkaspell);
618 /* Checker creation */
619 static GtkAspeller *gtkaspeller_new(Dictionary *dictionary)
622 GtkAspeller *gtkaspeller = NULL;
626 g_return_val_if_fail(gtkaspellcheckers, NULL);
628 g_return_val_if_fail(dictionary, NULL);
630 if (dictionary->fullname == NULL)
631 gtkaspell_checkers_error_message(
632 g_strdup(_("No dictionary selected.")));
634 g_return_val_if_fail(dictionary->fullname, NULL);
636 if (dictionary->dictname == NULL) {
639 tmp = strrchr(dictionary->fullname, G_DIR_SEPARATOR);
642 dictionary->dictname = dictionary->fullname;
644 dictionary->dictname = tmp + 1;
647 dict = dictionary_dup(dictionary);
649 tmp = g_new0(GtkAspeller, 1);
650 tmp->dictionary = dict;
652 exist = g_slist_find_custom(gtkaspellcheckers->checkers, tmp,
657 if ((gtkaspeller = gtkaspeller_real_new(dict)) != NULL) {
658 gtkaspellcheckers->checkers = g_slist_append(
659 gtkaspellcheckers->checkers,
662 debug_print("Aspell: Created a new gtkaspeller %p\n",
665 dictionary_delete(dict);
667 debug_print("Aspell: Could not create spell checker.\n");
670 debug_print("Aspell: number of existing checkers %d\n",
671 g_slist_length(gtkaspellcheckers->checkers));
676 static GtkAspeller *gtkaspeller_real_new(Dictionary *dict)
678 GtkAspeller *gtkaspeller;
679 AspellConfig *config;
680 AspellCanHaveError *ret;
682 g_return_val_if_fail(gtkaspellcheckers, NULL);
683 g_return_val_if_fail(dict, NULL);
685 gtkaspeller = g_new(GtkAspeller, 1);
687 gtkaspeller->dictionary = dict;
688 gtkaspeller->sug_mode = ASPELL_FASTMODE;
690 config = new_aspell_config();
692 if (!set_dictionary(config, dict))
695 ret = new_aspell_speller(config);
696 delete_aspell_config(config);
698 if (aspell_error_number(ret) != 0) {
699 gtkaspellcheckers->error_message
700 = g_strdup(aspell_error_message(ret));
702 delete_aspell_can_have_error(ret);
707 gtkaspeller->checker = to_aspell_speller(ret);
708 gtkaspeller->config = aspell_speller_config(gtkaspeller->checker);
713 static GtkAspeller *gtkaspeller_delete(GtkAspeller *gtkaspeller)
715 g_return_val_if_fail(gtkaspellcheckers, NULL);
717 gtkaspellcheckers->checkers =
718 g_slist_remove(gtkaspellcheckers->checkers,
721 debug_print("Aspell: Deleting gtkaspeller %p.\n",
724 gtkaspeller_real_delete(gtkaspeller);
726 debug_print("Aspell: number of existing checkers %d\n",
727 g_slist_length(gtkaspellcheckers->checkers));
732 static GtkAspeller *gtkaspeller_real_delete(GtkAspeller *gtkaspeller)
734 g_return_val_if_fail(gtkaspeller, NULL);
735 g_return_val_if_fail(gtkaspeller->checker, NULL);
737 aspell_speller_save_all_word_lists(gtkaspeller->checker);
739 delete_aspell_speller(gtkaspeller->checker);
741 dictionary_delete(gtkaspeller->dictionary);
743 debug_print("Aspell: gtkaspeller %p deleted.\n",
751 /*****************************************************************************/
752 /* Checker configuration */
754 static gboolean set_dictionary(AspellConfig *config, Dictionary *dict)
756 gchar *language = NULL;
757 gchar *jargon = NULL;
761 g_return_val_if_fail(config, FALSE);
762 g_return_val_if_fail(dict, FALSE);
764 strncpy(buf, dict->fullname, BUFSIZE-1);
765 buf[BUFSIZE-1] = 0x00;
767 buf[dict->dictname - dict->fullname] = 0x00;
769 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("dict-dir", buf);
770 debug_print("Aspell: looking for dictionaries in path %s.\n", buf);
772 strncpy(buf, dict->dictname, BUFSIZE-1);
775 if ((size = strrchr(buf, '-')) && isdigit((int) size[1]))
780 if ((jargon = strchr(language, '-')) != NULL)
783 if (size != NULL && jargon == size)
786 debug_print("Aspell: language: %s, jargon: %s, size: %s\n",
787 language, jargon ? jargon : "",
791 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("lang", language);
793 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("jargon", jargon);
795 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("size", size);
796 if (dict->encoding) {
799 aspell_enc = convert_to_aspell_encoding (dict->encoding);
800 aspell_config_replace(config, "encoding",
801 (const char *) aspell_enc);
804 RETURN_FALSE_IF_CONFIG_ERROR();
810 guchar *gtkaspell_get_dict(GtkAspell *gtkaspell)
813 g_return_val_if_fail(gtkaspell->gtkaspeller->config, NULL);
814 g_return_val_if_fail(gtkaspell->gtkaspeller->dictionary, NULL);
816 return (guchar *)g_strdup(gtkaspell->gtkaspeller->dictionary->dictname);
819 guchar *gtkaspell_get_path(GtkAspell *gtkaspell)
824 g_return_val_if_fail(gtkaspell->gtkaspeller->config, NULL);
825 g_return_val_if_fail(gtkaspell->gtkaspeller->dictionary, NULL);
827 dict = gtkaspell->gtkaspeller->dictionary;
828 path = (guchar *)g_strndup(dict->fullname, dict->dictname - dict->fullname);
833 /* set_sug_mode_cb() - Menu callback: Set the suggestion mode */
834 static void set_sug_mode_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
838 themode = (char *) gtk_label_get_text(GTK_LABEL(GTK_BIN(w)->child));
839 themode = g_strdup(themode);
841 set_real_sug_mode(gtkaspell, themode);
843 if (gtkaspell->menu_changed_cb)
844 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
847 /* set_sug_mode_cb() - Menu callback: Set the suggestion mode */
848 static void set_use_both_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
850 gtkaspell->use_both_dicts = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
852 if (gtkaspell->recheck_when_changing_dict) {
853 gtkaspell_highlight_all(gtkaspell);
856 if (gtkaspell->menu_changed_cb)
857 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
860 static void set_real_sug_mode(GtkAspell *gtkaspell, const char *themode)
863 gint mode = ASPELL_FASTMODE;
864 g_return_if_fail(gtkaspell);
865 g_return_if_fail(gtkaspell->gtkaspeller);
866 g_return_if_fail(themode);
868 if (!strcmp(themode,_("Normal Mode")))
869 mode = ASPELL_NORMALMODE;
870 else if (!strcmp( themode,_("Bad Spellers Mode")))
871 mode = ASPELL_BADSPELLERMODE;
873 result = gtkaspell_set_sug_mode(gtkaspell, mode);
876 debug_print("Aspell: error while changing suggestion mode:%s\n",
877 gtkaspellcheckers->error_message);
878 gtkaspell_checkers_reset_error();
882 /* gtkaspell_set_sug_mode() - Set the suggestion mode */
883 gboolean gtkaspell_set_sug_mode(GtkAspell *gtkaspell, gint themode)
885 AspellConfig *config;
887 g_return_val_if_fail(gtkaspell, FALSE);
888 g_return_val_if_fail(gtkaspell->gtkaspeller, FALSE);
889 g_return_val_if_fail(gtkaspell->gtkaspeller->config, FALSE);
891 debug_print("Aspell: setting sug mode of gtkaspeller %p to %d\n",
892 gtkaspell->gtkaspeller, themode);
894 config = gtkaspell->gtkaspeller->config;
897 case ASPELL_FASTMODE:
898 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "fast");
900 case ASPELL_NORMALMODE:
901 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "normal");
903 case ASPELL_BADSPELLERMODE:
904 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode",
908 gtkaspellcheckers->error_message =
909 g_strdup(_("Unknown suggestion mode."));
913 gtkaspell->gtkaspeller->sug_mode = themode;
914 gtkaspell->default_sug_mode = themode;
919 /* misspelled_suggest() - Create a suggestion list for word */
920 static GList *misspelled_suggest(GtkAspell *gtkaspell, gchar *word)
922 const guchar *newword;
924 const AspellWordList *suggestions;
925 AspellStringEnumeration *elements;
927 g_return_val_if_fail(word, NULL);
929 if (!aspell_speller_check(gtkaspell->gtkaspeller->checker, word, -1)) {
930 free_suggestions_list(gtkaspell);
932 suggestions = aspell_speller_suggest(
933 gtkaspell->gtkaspeller->checker,
934 (const char *)word, -1);
935 elements = aspell_word_list_elements(suggestions);
936 list = g_list_append(list, g_strdup(word));
938 while ((newword = (guchar *)aspell_string_enumeration_next(elements)) != NULL)
939 list = g_list_append(list, g_strdup((gchar *)newword));
941 gtkaspell->max_sug = g_list_length(list) - 1;
942 gtkaspell->suggestions_list = list;
947 free_suggestions_list(gtkaspell);
952 /* misspelled_test() - Just test if word is correctly spelled */
953 static int misspelled_test(GtkAspell *gtkaspell, unsigned char *word)
955 gint result = aspell_speller_check(gtkaspell->gtkaspeller->checker, (char *)word, -1)
957 if (result && gtkaspell->use_both_dicts && gtkaspell->alternate_speller) {
958 use_alternate_dict(gtkaspell);
959 result = aspell_speller_check(gtkaspell->gtkaspeller->checker, (char *)word, -1)
961 use_alternate_dict(gtkaspell);
967 static gboolean iswordsep(gunichar c)
969 return (g_unichar_isspace(c) || g_unichar_ispunct(c)) && c != (gunichar)'\'';
972 static gunichar get_text_index_whar(GtkAspell *gtkaspell, int pos)
974 GtkTextView *view = gtkaspell->gtktext;
975 GtkTextBuffer *buffer = gtk_text_view_get_buffer(view);
976 GtkTextIter start, end;
980 gtk_text_buffer_get_iter_at_offset(buffer, &start, pos);
981 gtk_text_buffer_get_iter_at_offset(buffer, &end, pos+1);
983 utf8chars = gtk_text_iter_get_text(&start, &end);
984 if (is_ascii_str(utf8chars)) {
985 a = utf8chars ? (gunichar)utf8chars[0] : '\0' ;
987 gchar *tr = conv_iconv_strdup(utf8chars, CS_UTF_8,
988 gtkaspell->gtkaspeller->dictionary->encoding);
990 a = g_utf8_get_char(tr);
999 /* get_word_from_pos () - return the word pointed to. */
1000 /* Handles correctly the quotes. */
1001 static gboolean get_word_from_pos(GtkAspell *gtkaspell, gint pos,
1002 unsigned char* buf, gint buflen,
1003 gint *pstart, gint *pend)
1006 /* TODO : when correcting a word into quotes, change the color of */
1007 /* the quotes too, as may be they were highlighted before. To do */
1008 /* this, we can use two others pointers that points to the whole */
1009 /* word including quotes. */
1015 GtkTextView *gtktext;
1017 gtktext = gtkaspell->gtktext;
1018 if (iswordsep(get_text_index_whar(gtkaspell, pos)))
1021 /* The apostrophe character is somtimes used for quotes
1022 * So include it in the word only if it is not surrounded
1023 * by other characters.
1026 for (start = pos; start >= 0; --start) {
1027 c = get_text_index_whar(gtkaspell, start);
1028 if (c == (gunichar)'\'') {
1030 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
1032 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
1034 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
1036 /* start_quote = TRUE; */
1041 /* start_quote = TRUE; */
1045 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
1051 for (end = pos; end < get_textview_buffer_charcount(gtktext); end++) {
1052 c = get_text_index_whar(gtkaspell, end);
1053 if (c == (gunichar)'\'') {
1054 if (end < get_textview_buffer_charcount(gtktext)) {
1055 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
1057 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
1059 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
1061 /* end_quote = TRUE; */
1066 /* end_quote = TRUE; */
1070 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
1080 if (end - start < buflen) {
1081 GtkTextIter iterstart, iterend;
1083 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtktext);
1084 gtk_text_buffer_get_iter_at_offset(buffer, &iterstart, start);
1085 gtk_text_buffer_get_iter_at_offset(buffer, &iterend, end);
1086 tmp = gtk_text_buffer_get_text(buffer, &iterstart, &iterend, FALSE);
1087 conv = conv_iconv_strdup(tmp, CS_UTF_8,
1088 gtkaspell->gtkaspeller->dictionary->encoding);
1090 strncpy((char *)buf, conv, buflen-1);
1100 static gboolean check_at(GtkAspell *gtkaspell, gint from_pos)
1103 unsigned char buf[GTKASPELLWORDSIZE];
1104 GtkTextView *gtktext;
1106 g_return_val_if_fail(from_pos >= 0, FALSE);
1108 gtktext = gtkaspell->gtktext;
1110 if (!get_word_from_pos(gtkaspell, from_pos, buf, sizeof(buf),
1114 if (misspelled_test(gtkaspell, buf)
1115 && strcasecmp((char *)buf, "sylpheed") && strcasecmp((char *)buf, "claws-mail")) {
1116 strncpy(gtkaspell->theword, (gchar *)buf, GTKASPELLWORDSIZE - 1);
1117 gtkaspell->theword[GTKASPELLWORDSIZE - 1] = 0;
1118 gtkaspell->start_pos = start;
1119 gtkaspell->end_pos = end;
1120 free_suggestions_list(gtkaspell);
1122 change_color(gtkaspell, start, end, (gchar *)buf, &(gtkaspell->highlight));
1125 change_color(gtkaspell, start, end, (gchar *)buf, NULL);
1130 static gboolean check_next_prev(GtkAspell *gtkaspell, gboolean forward)
1136 gboolean misspelled;
1139 maxpos = gtkaspell->end_check_pos;
1147 pos = get_textview_buffer_offset(gtkaspell->gtktext);
1148 gtkaspell->orig_pos = pos;
1149 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1150 pos > minpos && pos <= maxpos)
1152 while (!(misspelled = check_at(gtkaspell, pos)) &&
1153 pos > minpos && pos <= maxpos) {
1155 while (!iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1156 pos > minpos && pos <= maxpos)
1159 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1160 pos > minpos && pos <= maxpos)
1166 misspelled_suggest(gtkaspell, gtkaspell->theword);
1169 gtkaspell->orig_pos = gtkaspell->end_pos;
1171 set_textview_buffer_offset(gtkaspell->gtktext,
1172 gtkaspell->end_pos);
1173 /* scroll line to window center */
1174 gtk_text_view_scroll_to_mark(gtkaspell->gtktext,
1175 gtk_text_buffer_get_insert(
1176 gtk_text_view_get_buffer(gtkaspell->gtktext)),
1177 0.0, TRUE, 0.0, 0.5);
1178 /* let textview recalculate coordinates (set_menu_pos) */
1179 while (gtk_events_pending ())
1180 gtk_main_iteration ();
1182 list = make_sug_menu(gtkaspell);
1183 menu = gtk_menu_new();
1184 for (cur = list; cur; cur = cur->next)
1185 gtk_menu_append(menu, GTK_WIDGET(cur->data));
1187 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1188 set_menu_pos, gtkaspell, 0, GDK_CURRENT_TIME);
1189 g_signal_connect(G_OBJECT(menu), "deactivate",
1190 G_CALLBACK(destroy_menu),
1192 g_signal_connect(G_OBJECT(menu),
1194 G_CALLBACK(aspell_key_pressed), gtkaspell);
1198 reset_theword_data(gtkaspell);
1200 alertpanel_notice(_("No misspelled word found."));
1201 set_textview_buffer_offset(gtkaspell->gtktext,
1202 gtkaspell->orig_pos);
1208 void gtkaspell_check_backwards(GtkAspell *gtkaspell)
1210 gtkaspell->continue_check = NULL;
1211 gtkaspell->end_check_pos =
1212 get_textview_buffer_charcount(gtkaspell->gtktext);
1213 check_next_prev(gtkaspell, FALSE);
1216 void gtkaspell_check_forwards_go(GtkAspell *gtkaspell)
1219 gtkaspell->continue_check = NULL;
1220 gtkaspell->end_check_pos =
1221 get_textview_buffer_charcount(gtkaspell->gtktext);
1222 check_next_prev(gtkaspell, TRUE);
1225 void gtkaspell_check_all(GtkAspell *gtkaspell)
1227 GtkTextView *gtktext;
1229 GtkTextBuffer *buffer;
1230 GtkTextIter startiter, enditer;
1232 g_return_if_fail(gtkaspell);
1233 g_return_if_fail(gtkaspell->gtktext);
1235 gtktext = gtkaspell->gtktext;
1236 buffer = gtk_text_view_get_buffer(gtktext);
1237 gtk_text_buffer_get_selection_bounds(buffer, &startiter, &enditer);
1238 start = gtk_text_iter_get_offset(&startiter);
1239 end = gtk_text_iter_get_offset(&enditer);
1243 end = gtk_text_buffer_get_char_count(buffer);
1244 } else if (start > end) {
1252 set_textview_buffer_offset(gtktext, start);
1254 gtkaspell->continue_check = continue_check;
1255 gtkaspell->end_check_pos = end;
1257 gtkaspell->misspelled = check_next_prev(gtkaspell, TRUE);
1260 static void continue_check(gpointer *data)
1262 GtkAspell *gtkaspell = (GtkAspell *) data;
1263 gint pos = get_textview_buffer_offset(gtkaspell->gtktext);
1264 if (pos < gtkaspell->end_check_pos && gtkaspell->misspelled)
1265 gtkaspell->misspelled = check_next_prev(gtkaspell, TRUE);
1267 gtkaspell->continue_check = NULL;
1270 void gtkaspell_highlight_all(GtkAspell *gtkaspell)
1275 GtkTextView *gtktext;
1277 g_return_if_fail(gtkaspell->gtkaspeller->checker);
1279 gtktext = gtkaspell->gtktext;
1281 len = get_textview_buffer_charcount(gtktext);
1283 origpos = get_textview_buffer_offset(gtktext);
1287 iswordsep(get_text_index_whar(gtkaspell, pos)))
1290 !iswordsep(get_text_index_whar(gtkaspell, pos)))
1293 check_at(gtkaspell, pos - 1);
1295 set_textview_buffer_offset(gtktext, origpos);
1298 static void replace_with_supplied_word_cb(GtkWidget *w, GtkAspell *gtkaspell)
1300 unsigned char *newword;
1301 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1303 newword = (unsigned char *)gtk_editable_get_chars(GTK_EDITABLE(gtkaspell->replace_entry),
1306 if (strcmp((char *)newword, gtkaspell->theword)) {
1307 replace_real_word(gtkaspell, (char *)newword);
1309 if ((e->type == GDK_KEY_PRESS &&
1310 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK)) {
1311 aspell_speller_store_replacement(
1312 gtkaspell->gtkaspeller->checker,
1313 gtkaspell->theword, -1,
1314 (char *)newword, -1);
1316 gtkaspell->replace_entry = NULL;
1321 if (w && GTK_IS_DIALOG(w)) {
1322 gtk_widget_destroy(w);
1325 set_point_continue(gtkaspell);
1329 static void replace_word_cb(GtkWidget *w, gpointer data)
1331 unsigned char *newword;
1332 GtkAspell *gtkaspell = (GtkAspell *) data;
1333 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1335 newword = (unsigned char *) gtk_label_get_text(GTK_LABEL(GTK_BIN(w)->child));
1336 newword = (unsigned char *)g_strdup((char *)newword);
1338 replace_real_word(gtkaspell, (char *)newword);
1340 if ((e->type == GDK_KEY_PRESS &&
1341 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK) ||
1342 (e->type == GDK_BUTTON_RELEASE &&
1343 ((GdkEventButton *) e)->state & GDK_CONTROL_MASK)) {
1344 aspell_speller_store_replacement(
1345 gtkaspell->gtkaspeller->checker,
1346 gtkaspell->theword, -1,
1347 (char *)newword, -1);
1350 gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1352 set_point_continue(gtkaspell);
1356 static void replace_real_word(GtkAspell *gtkaspell, gchar *newword)
1358 int oldlen, newlen, wordlen;
1361 GtkTextView *gtktext;
1362 GtkTextBuffer *textbuf;
1363 GtkTextIter startiter, enditer;
1365 if (!newword) return;
1367 gtktext = gtkaspell->gtktext;
1368 textbuf = gtk_text_view_get_buffer(gtktext);
1370 origpos = gtkaspell->orig_pos;
1372 oldlen = gtkaspell->end_pos - gtkaspell->start_pos;
1373 wordlen = strlen(gtkaspell->theword);
1375 newlen = strlen(newword); /* FIXME: multybyte characters? */
1377 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1378 G_CALLBACK(entry_insert_cb),
1380 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1381 G_CALLBACK(entry_delete_cb),
1384 gtk_text_buffer_get_iter_at_offset(textbuf, &startiter,
1385 gtkaspell->start_pos);
1386 gtk_text_buffer_get_iter_at_offset(textbuf, &enditer,
1387 gtkaspell->end_pos);
1388 g_signal_emit_by_name(G_OBJECT(textbuf), "delete-range",
1389 &startiter, &enditer, gtkaspell);
1390 g_signal_emit_by_name(G_OBJECT(textbuf), "insert-text",
1391 &startiter, newword, newlen, gtkaspell);
1393 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1394 G_CALLBACK(entry_insert_cb),
1396 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1397 G_CALLBACK(entry_delete_cb),
1400 /* Put the point and the position where we clicked with the mouse
1401 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1402 * to let it update correctly the word insertion and then the
1403 * point & position position. If not, SEGV after the first replacement
1404 * If the new word ends before point, put the point at its end.
1407 if (origpos - gtkaspell->start_pos < oldlen &&
1408 origpos - gtkaspell->start_pos >= 0) {
1409 /* Original point was in the word.
1410 * Let it there unless point is going to be outside of the word
1412 if (origpos - gtkaspell->start_pos >= newlen) {
1413 pos = gtkaspell->start_pos + newlen;
1416 else if (origpos >= gtkaspell->end_pos) {
1417 /* move the position according to the change of length */
1418 pos = origpos + newlen - oldlen;
1421 gtkaspell->end_pos = gtkaspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1423 if (get_textview_buffer_charcount(gtktext) < pos)
1424 pos = get_textview_buffer_charcount(gtktext);
1425 gtkaspell->orig_pos = pos;
1427 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1430 /* Accept this word for this session */
1431 static void add_word_to_session_cb(GtkWidget *w, gpointer data)
1434 GtkTextView *gtktext;
1435 GtkAspell *gtkaspell = (GtkAspell *) data;
1436 gtktext = gtkaspell->gtktext;
1438 pos = get_textview_buffer_offset(gtktext);
1440 aspell_speller_add_to_session(gtkaspell->gtkaspeller->checker,
1442 strlen(gtkaspell->theword));
1444 check_at(gtkaspell, gtkaspell->start_pos);
1446 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1448 set_point_continue(gtkaspell);
1451 /* add_word_to_personal_cb() - add word to personal dict. */
1452 static void add_word_to_personal_cb(GtkWidget *w, gpointer data)
1454 GtkAspell *gtkaspell = (GtkAspell *) data;
1456 aspell_speller_add_to_personal(gtkaspell->gtkaspeller->checker,
1458 strlen(gtkaspell->theword));
1460 check_at(gtkaspell, gtkaspell->start_pos);
1462 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1463 set_point_continue(gtkaspell);
1466 static void check_with_alternate_cb(GtkWidget *w, gpointer data)
1468 GtkAspell *gtkaspell = (GtkAspell *) data;
1471 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1473 use_alternate_dict(gtkaspell);
1474 misspelled = check_at(gtkaspell, gtkaspell->start_pos);
1476 if (!gtkaspell->continue_check) {
1478 gtkaspell->misspelled = misspelled;
1480 if (gtkaspell->misspelled) {
1483 misspelled_suggest(gtkaspell, gtkaspell->theword);
1485 set_textview_buffer_offset(gtkaspell->gtktext,
1486 gtkaspell->end_pos);
1488 list = make_sug_menu(gtkaspell);
1489 menu = gtk_menu_new();
1490 for (cur = list; cur; cur = cur->next)
1491 gtk_menu_append(menu, GTK_WIDGET(cur->data));
1493 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1494 set_menu_pos, gtkaspell, 0,
1496 g_signal_connect(G_OBJECT(menu), "deactivate",
1497 G_CALLBACK(destroy_menu),
1499 g_signal_connect(G_OBJECT(menu),
1501 G_CALLBACK(aspell_key_pressed), gtkaspell);
1505 gtkaspell->orig_pos = gtkaspell->start_pos;
1507 set_point_continue(gtkaspell);
1510 static gboolean replace_key_pressed(GtkWidget *widget,
1512 GtkAspell *gtkaspell)
1514 if (event && event->keyval == GDK_Escape) {
1515 gtk_widget_destroy(widget);
1517 } else if (event && event->keyval == GDK_Return) {
1518 replace_with_supplied_word_cb(widget, gtkaspell);
1524 static void replace_with_create_dialog_cb(GtkWidget *w, gpointer data)
1526 static PangoFontDescription *font_desc;
1533 GtkWidget *ok_button;
1534 GtkWidget *cancel_button;
1535 GtkWidget *confirm_area;
1539 GtkAspell *gtkaspell = (GtkAspell *) data;
1541 gdk_window_get_origin((GTK_WIDGET(w)->parent)->window, &xx, &yy);
1543 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1545 dialog = gtk_dialog_new();
1547 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1548 gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1549 gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1550 gtk_widget_set_uposition(dialog, xx, yy);
1552 g_signal_connect_swapped(G_OBJECT(dialog), "destroy",
1553 G_CALLBACK(gtk_widget_destroy),
1556 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
1557 hbox = gtk_hbox_new (FALSE, 12);
1558 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1559 gtk_widget_show (hbox);
1560 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
1563 thelabel = g_strdup_printf(_("<span weight=\"bold\" "
1564 "size=\"larger\">Replace \"%s\" with: </span>"),
1565 gtkaspell->theword);
1566 /* for title label */
1567 w_hbox = gtk_hbox_new(FALSE, 0);
1569 icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
1570 GTK_ICON_SIZE_DIALOG);
1571 gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
1572 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
1574 vbox = gtk_vbox_new (FALSE, 12);
1575 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1576 gtk_widget_show (vbox);
1578 label = gtk_label_new(thelabel);
1579 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1580 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1581 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1582 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1583 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1587 size = pango_font_description_get_size
1588 (label->style->font_desc);
1589 font_desc = pango_font_description_new();
1590 pango_font_description_set_weight
1591 (font_desc, PANGO_WEIGHT_BOLD);
1592 pango_font_description_set_size
1593 (font_desc, size * PANGO_SCALE_LARGE);
1596 gtk_widget_modify_font(label, font_desc);
1599 entry = gtk_entry_new();
1600 gtkaspell->replace_entry = entry;
1601 gtk_entry_set_text(GTK_ENTRY(entry), gtkaspell->theword);
1602 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1603 g_signal_connect(G_OBJECT(dialog),
1605 G_CALLBACK(replace_key_pressed), gtkaspell);
1606 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
1608 label = gtk_label_new(_("Holding down Control key while pressing "
1609 "Enter\nwill learn from mistake.\n"));
1610 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1611 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1612 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1613 gtk_widget_show(label);
1615 hbox = gtk_hbox_new(TRUE, 0);
1617 gtkut_stock_button_set_create(&confirm_area,
1618 &cancel_button, GTK_STOCK_CANCEL,
1619 &ok_button, GTK_STOCK_OK,
1622 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1623 confirm_area, FALSE, FALSE, 0);
1624 gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
1626 g_signal_connect(G_OBJECT(ok_button), "clicked",
1627 G_CALLBACK(replace_with_supplied_word_cb),
1629 g_signal_connect_swapped(G_OBJECT(ok_button), "clicked",
1630 G_CALLBACK(gtk_widget_destroy),
1633 g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked",
1634 G_CALLBACK(gtk_widget_destroy),
1637 gtk_widget_grab_focus(entry);
1639 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1641 gtk_widget_show_all(dialog);
1644 void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1646 GtkTextView *gtktext;
1647 GtkTextBuffer *buffer;
1648 GtkTextIter startiter, enditer;
1650 gtktext = gtkaspell->gtktext;
1652 buffer = gtk_text_view_get_buffer(gtktext);
1653 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, 0);
1654 gtk_text_buffer_get_iter_at_offset(buffer, &enditer,
1655 get_textview_buffer_charcount(gtktext)-1);
1656 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
1657 &startiter, &enditer);
1660 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1662 GtkAspell *gtkaspell = (GtkAspell *) data;
1664 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1666 if (!gtkaspell->check_while_typing)
1667 gtkaspell_uncheck_all(gtkaspell);
1668 if (gtkaspell->menu_changed_cb)
1669 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
1672 static GSList *create_empty_dictionary_list(void)
1674 GSList *list = NULL;
1677 dict = g_new0(Dictionary, 1);
1678 dict->fullname = g_strdup(_("None"));
1679 dict->dictname = dict->fullname;
1680 dict->encoding = NULL;
1682 return g_slist_append(list, dict);
1685 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1686 GSList *gtkaspell_get_dictionary_list(const gchar *aspell_path, gint refresh)
1690 AspellConfig *config;
1691 AspellDictInfoList *dlist;
1692 AspellDictInfoEnumeration *dels;
1693 const AspellDictInfo *entry;
1695 if (!gtkaspellcheckers)
1696 gtkaspell_checkers_init();
1698 if (gtkaspellcheckers->dictionary_list && !refresh)
1699 return gtkaspellcheckers->dictionary_list;
1701 gtkaspell_free_dictionary_list(
1702 gtkaspellcheckers->dictionary_list);
1705 config = new_aspell_config();
1707 aspell_config_replace(config, "dict-dir", aspell_path);
1708 if (aspell_config_error_number(config) != 0) {
1709 gtkaspellcheckers->error_message = g_strdup(
1710 aspell_config_error_message(config));
1711 gtkaspellcheckers->dictionary_list =
1712 create_empty_dictionary_list();
1714 return gtkaspellcheckers->dictionary_list;
1717 dlist = get_aspell_dict_info_list(config);
1718 delete_aspell_config(config);
1720 debug_print("Aspell: checking for dictionaries in %s\n", aspell_path);
1721 dels = aspell_dict_info_list_elements(dlist);
1722 while ( (entry = aspell_dict_info_enumeration_next(dels)) != 0)
1724 dict = g_new0(Dictionary, 1);
1725 dict->fullname = g_strdup_printf("%s%s", aspell_path,
1727 dict->dictname = dict->fullname + strlen(aspell_path);
1728 dict->encoding = g_strdup(entry->code);
1730 if (g_slist_find_custom(list, dict,
1731 (GCompareFunc) compare_dict) != NULL) {
1732 dictionary_delete(dict);
1736 debug_print("Aspell: found dictionary %s %s %s\n", dict->fullname,
1737 dict->dictname, dict->encoding);
1738 list = g_slist_insert_sorted(list, dict,
1739 (GCompareFunc) compare_dict);
1742 delete_aspell_dict_info_enumeration(dels);
1746 debug_print("Aspell: error when searching for dictionaries: "
1747 "No dictionary found.\n");
1748 list = create_empty_dictionary_list();
1751 gtkaspellcheckers->dictionary_list = list;
1756 void gtkaspell_free_dictionary_list(GSList *list)
1760 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1762 dict = (Dictionary *) walk->data;
1763 dictionary_delete(dict);
1769 GtkWidget *gtkaspell_dictionary_option_menu_new_with_refresh(const gchar *aspell_path,
1772 GSList *dict_list, *tmp;
1777 dict_list = gtkaspell_get_dictionary_list(aspell_path, refresh);
1778 g_return_val_if_fail(dict_list, NULL);
1780 menu = gtk_menu_new();
1782 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1783 dict = (Dictionary *) tmp->data;
1784 item = gtk_menu_item_new_with_label(dict->dictname);
1785 g_object_set_data(G_OBJECT(item), "dict_name",
1788 gtk_menu_append(GTK_MENU(menu), item);
1789 gtk_widget_show(item);
1792 gtk_widget_show(menu);
1797 GtkWidget *gtkaspell_dictionary_option_menu_new(const gchar *aspell_path)
1799 return gtkaspell_dictionary_option_menu_new_with_refresh
1800 (aspell_path, TRUE);
1804 gchar *gtkaspell_get_dictionary_menu_active_item(GtkWidget *menu)
1806 GtkWidget *menuitem;
1807 gchar *dict_fullname;
1810 g_return_val_if_fail(GTK_IS_MENU(menu), NULL);
1812 menuitem = gtk_menu_get_active(GTK_MENU(menu));
1813 dict_fullname = (gchar *) g_object_get_data(G_OBJECT(menuitem),
1815 g_return_val_if_fail(dict_fullname, NULL);
1817 label = g_strdup(dict_fullname);
1823 gint gtkaspell_set_dictionary_menu_active_item(GtkWidget *menu,
1824 const gchar *dictionary)
1829 g_return_val_if_fail(menu != NULL, 0);
1830 g_return_val_if_fail(dictionary != NULL, 0);
1831 g_return_val_if_fail(GTK_IS_OPTION_MENU(menu), 0);
1834 for (cur = GTK_MENU_SHELL(gtk_option_menu_get_menu(
1835 GTK_OPTION_MENU(menu)))->children;
1836 cur != NULL; cur = cur->next) {
1837 GtkWidget *menuitem;
1840 menuitem = GTK_WIDGET(cur->data);
1841 dict_name = g_object_get_data(G_OBJECT(menuitem),
1843 if ((dict_name != NULL) && !strcmp2(dict_name, dictionary)) {
1844 gtk_option_menu_set_history(GTK_OPTION_MENU(menu), n);
1855 GtkWidget *gtkaspell_sugmode_option_menu_new(gint sugmode)
1860 menu = gtk_menu_new();
1861 gtk_widget_show(menu);
1863 item = gtk_menu_item_new_with_label(_("Fast Mode"));
1864 gtk_widget_show(item);
1865 gtk_menu_append(GTK_MENU(menu), item);
1866 g_object_set_data(G_OBJECT(item), "sugmode",
1867 GINT_TO_POINTER(ASPELL_FASTMODE));
1869 item = gtk_menu_item_new_with_label(_("Normal Mode"));
1870 gtk_widget_show(item);
1871 gtk_menu_append(GTK_MENU(menu), item);
1872 g_object_set_data(G_OBJECT(item), "sugmode",
1873 GINT_TO_POINTER(ASPELL_NORMALMODE));
1875 item = gtk_menu_item_new_with_label(_("Bad Spellers Mode"));
1876 gtk_widget_show(item);
1877 gtk_menu_append(GTK_MENU(menu), item);
1878 g_object_set_data(G_OBJECT(item), "sugmode",
1879 GINT_TO_POINTER(ASPELL_BADSPELLERMODE));
1884 void gtkaspell_sugmode_option_menu_set(GtkOptionMenu *optmenu, gint sugmode)
1886 g_return_if_fail(GTK_IS_OPTION_MENU(optmenu));
1888 g_return_if_fail(sugmode == ASPELL_FASTMODE ||
1889 sugmode == ASPELL_NORMALMODE ||
1890 sugmode == ASPELL_BADSPELLERMODE);
1892 gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), sugmode - 1);
1895 gint gtkaspell_get_sugmode_from_option_menu(GtkOptionMenu *optmenu)
1900 g_return_val_if_fail(GTK_IS_OPTION_MENU(optmenu), -1);
1902 item = gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(optmenu)));
1904 sugmode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item),
1910 static void use_alternate_dict(GtkAspell *gtkaspell)
1914 tmp = gtkaspell->gtkaspeller;
1915 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1916 gtkaspell->alternate_speller = tmp;
1919 static void destroy_menu(GtkWidget *widget,
1920 gpointer user_data) {
1922 GtkAspell *gtkaspell = (GtkAspell *)user_data;
1924 if (gtkaspell->accel_group) {
1925 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1926 gtkaspell->accel_group);
1927 gtkaspell->accel_group = NULL;
1931 static gboolean aspell_key_pressed(GtkWidget *widget,
1933 GtkAspell *gtkaspell)
1935 if (event && (isascii(event->keyval) || event->keyval == GDK_Return)) {
1936 gtk_accel_groups_activate(
1937 G_OBJECT(gtkaspell->parent_window),
1938 event->keyval, event->state);
1939 } else if (event && event->keyval == GDK_Escape) {
1940 destroy_menu(NULL, gtkaspell);
1945 /* make_sug_menu() - Add menus to accept this word for this session
1946 * and to add it to personal dictionary
1948 static GSList *make_sug_menu(GtkAspell *gtkaspell)
1952 GtkTextView *gtktext;
1953 GtkAccelGroup *accel;
1954 GList *l = gtkaspell->suggestions_list;
1956 GSList *list = NULL;
1957 gtktext = gtkaspell->gtktext;
1959 accel = gtk_accel_group_new();
1961 if (gtkaspell->accel_group) {
1962 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1963 gtkaspell->accel_group);
1964 gtkaspell->accel_group = NULL;
1967 utf8buf = conv_codeset_strdup((char*)l->data,
1968 conv_get_locale_charset_str(),
1970 caption = g_strdup_printf(_("\"%s\" unknown in %s"),
1972 gtkaspell->gtkaspeller->dictionary->dictname);
1973 item = gtk_menu_item_new_with_label(caption);
1975 gtk_widget_show(item);
1976 list = g_slist_append(list, item);
1977 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
1980 item = gtk_menu_item_new();
1981 gtk_widget_show(item);
1982 list = g_slist_append(list, item);
1984 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1985 gtk_widget_show(item);
1986 list = g_slist_append(list, item);
1987 g_signal_connect(G_OBJECT(item), "activate",
1988 G_CALLBACK(add_word_to_session_cb),
1990 gtk_widget_add_accelerator(item, "activate", accel, GDK_space,
1992 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1994 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1995 gtk_widget_show(item);
1996 list = g_slist_append(list, item);
1997 g_signal_connect(G_OBJECT(item), "activate",
1998 G_CALLBACK(add_word_to_personal_cb),
2000 gtk_widget_add_accelerator(item, "activate", accel, GDK_Return,
2002 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
2004 item = gtk_menu_item_new_with_label(_("Replace with..."));
2005 gtk_widget_show(item);
2006 list = g_slist_append(list, item);
2007 g_signal_connect(G_OBJECT(item), "activate",
2008 G_CALLBACK(replace_with_create_dialog_cb),
2010 gtk_widget_add_accelerator(item, "activate", accel, GDK_R, 0,
2011 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
2012 gtk_widget_add_accelerator(item, "activate", accel, GDK_R,
2016 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
2017 caption = g_strdup_printf(_("Check with %s"),
2018 gtkaspell->alternate_speller->dictionary->dictname);
2019 item = gtk_menu_item_new_with_label(caption);
2021 gtk_widget_show(item);
2022 list = g_slist_append(list, item);
2023 g_signal_connect(G_OBJECT(item), "activate",
2024 G_CALLBACK(check_with_alternate_cb),
2026 gtk_widget_add_accelerator(item, "activate", accel, GDK_X, 0,
2027 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
2028 gtk_widget_add_accelerator(item, "activate", accel, GDK_X,
2033 item = gtk_menu_item_new();
2034 gtk_widget_show(item);
2035 list = g_slist_append(list, item);
2039 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
2040 gtk_widget_show(item);
2041 list = g_slist_append(list, item);
2043 GtkWidget *curmenu = NULL;
2047 if (count == MENUCOUNT) {
2050 item = gtk_menu_item_new_with_label(_("More..."));
2051 gtk_widget_show(item);
2053 gtk_menu_append(GTK_MENU(curmenu), item);
2055 list = g_slist_append(list, item);
2057 curmenu = gtk_menu_new();
2058 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2062 utf8buf = conv_codeset_strdup((char*)l->data,
2063 conv_get_locale_charset_str(),
2065 item = gtk_menu_item_new_with_label(utf8buf);
2067 gtk_widget_show(item);
2068 if (curmenu == NULL) {
2069 list = g_slist_append(list, item);
2071 gtk_menu_append(GTK_MENU(curmenu), item);
2073 g_signal_connect(G_OBJECT(item), "activate",
2074 G_CALLBACK(replace_word_cb),
2077 if (curmenu == NULL && count < MENUCOUNT) {
2078 gtk_widget_add_accelerator(item, "activate",
2083 gtk_widget_add_accelerator(item, "activate",
2092 } while ((l = l->next) != NULL);
2095 gtk_window_add_accel_group
2096 (GTK_WINDOW(gtkaspell->parent_window),
2098 gtkaspell->accel_group = accel;
2103 static GSList *populate_submenu(GtkAspell *gtkaspell)
2105 GtkWidget *item, *submenu;
2107 GtkAspeller *gtkaspeller = NULL;
2108 GSList *list = NULL;
2113 gtkaspeller = gtkaspell->gtkaspeller;
2114 dictname = g_strdup_printf(_("Dictionary: %s"),
2115 gtkaspeller->dictionary->dictname);
2116 item = gtk_menu_item_new_with_label(dictname);
2117 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
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 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
2127 dictname = g_strdup_printf(_("Use alternate (%s)"),
2128 gtkaspell->alternate_speller->dictionary->dictname);
2129 item = gtk_menu_item_new_with_label(dictname);
2131 g_signal_connect(G_OBJECT(item), "activate",
2132 G_CALLBACK(switch_to_alternate_cb),
2134 gtk_widget_show(item);
2135 list = g_slist_append(list, item);
2138 item = gtk_check_menu_item_new_with_label(_("Use both dictionaries"));
2139 if (gtkaspell->use_both_dicts) {
2140 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2142 g_signal_connect(G_OBJECT(item), "activate",
2143 G_CALLBACK(set_use_both_cb),
2145 gtk_widget_show(item);
2146 list = g_slist_append(list, item);
2148 item = gtk_menu_item_new();
2149 gtk_widget_show(item);
2150 list = g_slist_append(list, item);
2152 item = gtk_check_menu_item_new_with_label(_("Fast Mode"));
2153 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
2154 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_FASTMODE) {
2155 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),TRUE);
2156 gtk_widget_set_sensitive(GTK_WIDGET(item),FALSE);
2158 g_signal_connect(G_OBJECT(item), "activate",
2159 G_CALLBACK(set_sug_mode_cb),
2161 gtk_widget_show(item);
2162 list = g_slist_append(list, item);
2164 item = gtk_check_menu_item_new_with_label(_("Normal Mode"));
2165 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
2166 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_NORMALMODE) {
2167 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
2168 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2170 g_signal_connect(G_OBJECT(item), "activate",
2171 G_CALLBACK(set_sug_mode_cb),
2173 gtk_widget_show(item);
2174 list = g_slist_append(list, item);
2176 item = gtk_check_menu_item_new_with_label(_("Bad Spellers Mode"));
2177 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
2178 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_BADSPELLERMODE) {
2179 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
2180 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2182 g_signal_connect(G_OBJECT(item), "activate",
2183 G_CALLBACK(set_sug_mode_cb),
2185 gtk_widget_show(item);
2186 list = g_slist_append(list, item);
2188 item = gtk_menu_item_new();
2189 gtk_widget_show(item);
2190 list = g_slist_append(list, item);
2192 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
2193 if (gtkaspell->check_while_typing)
2194 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2196 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2197 g_signal_connect(G_OBJECT(item), "activate",
2198 G_CALLBACK(toggle_check_while_typing_cb),
2200 gtk_widget_show(item);
2201 list = g_slist_append(list, item);
2203 item = gtk_menu_item_new();
2204 gtk_widget_show(item);
2205 list = g_slist_append(list, item);
2207 submenu = gtk_menu_new();
2208 item = gtk_menu_item_new_with_label(_("Change dictionary"));
2209 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
2210 gtk_widget_show(item);
2211 list = g_slist_append(list, item);
2214 if (gtkaspellcheckers->dictionary_list == NULL)
2215 gtkaspell_get_dictionary_list(gtkaspell->dictionary_path, FALSE);
2217 GtkWidget * curmenu = submenu;
2221 tmp = gtkaspellcheckers->dictionary_list;
2223 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
2224 tmp = g_slist_next(tmp)) {
2225 if (count == MENUCOUNT) {
2228 newmenu = gtk_menu_new();
2229 item = gtk_menu_item_new_with_label(_("More..."));
2230 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2233 gtk_menu_append(GTK_MENU(curmenu), item);
2234 gtk_widget_show(item);
2238 dict = (Dictionary *) tmp->data;
2239 item = gtk_check_menu_item_new_with_label(dict->dictname);
2240 g_object_set_data(G_OBJECT(item), "dict_name",
2242 if (strcmp2(dict->fullname,
2243 gtkaspell->gtkaspeller->dictionary->fullname))
2244 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2246 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2247 gtk_widget_set_sensitive(GTK_WIDGET(item),
2250 g_signal_connect(G_OBJECT(item), "activate",
2251 G_CALLBACK(change_dict_cb),
2253 gtk_widget_show(item);
2254 gtk_menu_append(GTK_MENU(curmenu), item);
2262 GSList *gtkaspell_make_config_menu(GtkAspell *gtkaspell)
2264 return populate_submenu(gtkaspell);
2267 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y,
2268 gboolean *push_in, gpointer data)
2270 GtkAspell *gtkaspell = (GtkAspell *) data;
2271 gint xx = 0, yy = 0;
2274 GtkTextView *text = GTK_TEXT_VIEW(gtkaspell->gtktext);
2275 GtkTextBuffer *textbuf;
2280 textbuf = gtk_text_view_get_buffer(gtkaspell->gtktext);
2281 gtk_text_buffer_get_iter_at_mark(textbuf, &iter,
2282 gtk_text_buffer_get_insert(textbuf));
2283 gtk_text_view_get_iter_location(gtkaspell->gtktext, &iter, &rect);
2284 gtk_text_view_buffer_to_window_coords(text, GTK_TEXT_WINDOW_TEXT,
2288 gdk_window_get_origin(GTK_WIDGET(gtkaspell->gtktext)->window, &xx, &yy);
2290 sx = gdk_screen_width();
2291 sy = gdk_screen_height();
2293 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2299 gdk_char_width(gtk_style_get_font(GTK_WIDGET(text)->style), ' ');
2301 *y = rect.y + rect.height + yy;
2307 gdk_string_height(gtk_style_get_font(
2308 GTK_WIDGET(text)->style),
2309 gtkaspell->theword);
2312 /* change the current dictionary of gtkaspell
2313 - if always_set_alt_dict is set, the alternate dict is unconditionally set to the former
2314 current dictionary (common use: from menu callbacks)
2315 - if always_set_alt_dict is NOT set, the alternate dict will be set to the former
2316 current dictionary only if there is no alternate dictionary already set
2317 (this is when we need to set the current dictionary then the alternate one
2318 when creating a compose window, from the account and folder settings)
2320 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary,
2321 gboolean always_set_alt_dict)
2324 GtkAspeller *gtkaspeller;
2327 g_return_val_if_fail(gtkaspell, FALSE);
2328 g_return_val_if_fail(dictionary, FALSE);
2330 sug_mode = gtkaspell->default_sug_mode;
2332 dict = g_new0(Dictionary, 1);
2333 dict->fullname = g_strdup(dictionary);
2334 dict->encoding = g_strdup(gtkaspell->gtkaspeller->dictionary->encoding);
2336 gtkaspeller = gtkaspeller_new(dict);
2340 message = g_strdup_printf(_("The spell checker could not change dictionary.\n%s"),
2341 gtkaspellcheckers->error_message);
2343 alertpanel_warning(message);
2346 if (gtkaspell->use_alternate) {
2347 if (gtkaspell->alternate_speller) {
2348 if (always_set_alt_dict) {
2349 gtkaspeller_delete(gtkaspell->alternate_speller);
2350 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2352 gtkaspeller_delete(gtkaspell->gtkaspeller);
2354 /* should never be reached as the dicts are always set
2355 to a default value */
2356 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2358 gtkaspeller_delete(gtkaspell->gtkaspeller);
2360 gtkaspell->gtkaspeller = gtkaspeller;
2361 gtkaspell_set_sug_mode(gtkaspell, sug_mode);
2364 dictionary_delete(dict);
2369 /* change the alternate dictionary of gtkaspell (doesn't affect the default dictionary) */
2370 gboolean gtkaspell_change_alt_dict(GtkAspell *gtkaspell, const gchar *alt_dictionary)
2373 GtkAspeller *gtkaspeller;
2375 g_return_val_if_fail(gtkaspell, FALSE);
2376 g_return_val_if_fail(alt_dictionary, FALSE);
2378 dict = g_new0(Dictionary, 1);
2379 dict->fullname = g_strdup(alt_dictionary);
2380 dict->encoding = g_strdup(gtkaspell->gtkaspeller->dictionary->encoding);
2382 gtkaspeller = gtkaspeller_new(dict);
2386 message = g_strdup_printf(_("The spell checker could not change the alternate dictionary.\n%s"),
2387 gtkaspellcheckers->error_message);
2389 alertpanel_warning(message);
2392 if (gtkaspell->alternate_speller)
2393 gtkaspeller_delete(gtkaspell->alternate_speller);
2394 gtkaspell->alternate_speller = gtkaspeller;
2397 dictionary_delete(dict);
2402 /* Menu call backs */
2404 /* change_dict_cb() - Menu callback : change dict */
2405 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2409 fullname = (gchar *) g_object_get_data(G_OBJECT(w), "dict_name");
2411 if (!strcmp2(fullname, _("None")))
2414 gtkaspell_change_dict(gtkaspell, fullname, TRUE);
2415 if (gtkaspell->recheck_when_changing_dict) {
2416 gtkaspell_highlight_all(gtkaspell);
2418 if (gtkaspell->menu_changed_cb)
2419 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2422 static void switch_to_alternate_cb(GtkWidget *w,
2425 GtkAspell *gtkaspell = (GtkAspell *) data;
2426 use_alternate_dict(gtkaspell);
2427 if (gtkaspell->recheck_when_changing_dict) {
2428 gtkaspell_highlight_all(gtkaspell);
2430 if (gtkaspell->menu_changed_cb)
2431 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2434 /* Misc. helper functions */
2436 static void set_point_continue(GtkAspell *gtkaspell)
2438 GtkTextView *gtktext;
2440 gtktext = gtkaspell->gtktext;
2442 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
2444 if (gtkaspell->continue_check)
2445 gtkaspell->continue_check((gpointer *) gtkaspell);
2448 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2450 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtkaspell->gtktext);
2451 GdkColor *color = &(gtkaspell->highlight);
2453 /* Shameless copy from Sylpheed's gtkutils.c */
2455 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2457 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2459 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2463 gtk_text_buffer_create_tag(buffer, "misspelled",
2464 "foreground-gdk", color, NULL);
2466 gtk_text_buffer_create_tag(buffer, "misspelled",
2467 "underline", PANGO_UNDERLINE_ERROR, NULL);
2471 static void change_color(GtkAspell * gtkaspell,
2472 gint start, gint end,
2476 GtkTextView *gtktext;
2477 GtkTextBuffer *buffer;
2478 GtkTextIter startiter, enditer;
2483 gtktext = gtkaspell->gtktext;
2485 buffer = gtk_text_view_get_buffer(gtktext);
2486 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, start);
2487 gtk_text_buffer_get_iter_at_offset(buffer, &enditer, end);
2489 gtk_text_buffer_apply_tag_by_name(buffer, "misspelled",
2490 &startiter, &enditer);
2492 gtk_text_iter_forward_char(&enditer);
2493 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
2494 &startiter, &enditer);
2498 /* convert_to_aspell_encoding () - converts ISO-8859-* strings to iso8859-*
2499 * as needed by aspell. Returns an allocated string.
2502 static gchar *convert_to_aspell_encoding (const gchar *encoding)
2504 gchar * aspell_encoding;
2506 if (strstr2(encoding, "ISO-8859-")) {
2507 aspell_encoding = g_strdup_printf("iso8859%s", encoding+8);
2510 if (!strcmp2(encoding, "US-ASCII"))
2511 aspell_encoding = g_strdup("iso8859-1");
2513 aspell_encoding = g_strdup(encoding);
2516 return aspell_encoding;
2519 /* compare_dict () - compare 2 dict names */
2520 static gint compare_dict(Dictionary *a, Dictionary *b)
2522 guint aparts = 0, bparts = 0;
2525 for (i=0; i < strlen(a->dictname); i++)
2526 if (a->dictname[i] == '-')
2528 for (i=0; i < strlen(b->dictname); i++)
2529 if (b->dictname[i] == '-')
2532 if (aparts != bparts)
2533 return (aparts < bparts) ? -1 : +1;
2536 compare = strcmp2(a->dictname, b->dictname);
2538 compare = strcmp2(a->fullname, b->fullname);
2543 static void dictionary_delete(Dictionary *dict)
2545 g_free(dict->fullname);
2546 g_free(dict->encoding);
2550 static Dictionary *dictionary_dup(const Dictionary *dict)
2554 dict2 = g_new(Dictionary, 1);
2556 dict2->fullname = g_strdup(dict->fullname);
2557 dict2->dictname = dict->dictname - dict->fullname + dict2->fullname;
2558 dict2->encoding = g_strdup(dict->encoding);
2563 static void free_suggestions_list(GtkAspell *gtkaspell)
2567 for (list = gtkaspell->suggestions_list; list != NULL;
2571 g_list_free(gtkaspell->suggestions_list);
2573 gtkaspell->max_sug = -1;
2574 gtkaspell->suggestions_list = NULL;
2577 static void reset_theword_data(GtkAspell *gtkaspell)
2579 gtkaspell->start_pos = 0;
2580 gtkaspell->end_pos = 0;
2581 gtkaspell->theword[0] = 0;
2582 gtkaspell->max_sug = -1;
2584 free_suggestions_list(gtkaspell);
2587 static void free_checkers(gpointer elt, gpointer data)
2589 GtkAspeller *gtkaspeller = elt;
2591 g_return_if_fail(gtkaspeller);
2593 gtkaspeller_real_delete(gtkaspeller);
2596 static gint find_gtkaspeller(gconstpointer aa, gconstpointer bb)
2598 Dictionary *a = ((GtkAspeller *) aa)->dictionary;
2599 Dictionary *b = ((GtkAspeller *) bb)->dictionary;
2601 if (a && b && a->fullname && b->fullname &&
2602 strcmp(a->fullname, b->fullname) == 0 &&
2603 a->encoding && b->encoding)
2604 return strcmp(a->encoding, b->encoding);