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 Sylpheed Claws 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 use_alternate;
126 ContCheckFunc continue_check;
128 GtkWidget *config_menu;
129 GtkWidget *popup_config_menu;
131 GtkWidget *replace_entry;
132 GtkWidget *parent_window;
134 gint default_sug_mode;
136 GList *suggestions_list;
138 GtkTextView *gtktext;
140 GtkAccelGroup *accel_group;
143 typedef AspellConfig GtkAspellConfig;
145 /******************************************************************************/
147 static GtkAspellCheckers *gtkaspellcheckers;
149 /* Error message storage */
150 static void gtkaspell_checkers_error_message (gchar *message);
153 static void entry_insert_cb (GtkTextBuffer *textbuf,
157 GtkAspell *gtkaspell);
158 static void entry_delete_cb (GtkTextBuffer *textbuf,
159 GtkTextIter *startiter,
160 GtkTextIter *enditer,
161 GtkAspell *gtkaspell);
162 static gint button_press_intercept_cb (GtkTextView *gtktext,
164 GtkAspell *gtkaspell);
166 /* Checker creation */
167 static GtkAspeller* gtkaspeller_new (Dictionary *dict);
168 static GtkAspeller* gtkaspeller_real_new (Dictionary *dict);
169 static GtkAspeller* gtkaspeller_delete (GtkAspeller *gtkaspeller);
170 static GtkAspeller* gtkaspeller_real_delete (GtkAspeller *gtkaspeller);
172 /* Checker configuration */
173 static gint set_dictionary (AspellConfig *config,
175 static void set_sug_mode_cb (GtkMenuItem *w,
176 GtkAspell *gtkaspell);
177 static void set_real_sug_mode (GtkAspell *gtkaspell,
178 const char *themode);
180 /* Checker actions */
181 static gboolean check_at (GtkAspell *gtkaspell,
183 static gboolean check_next_prev (GtkAspell *gtkaspell,
185 static GList* misspelled_suggest (GtkAspell *gtkaspell,
187 static void add_word_to_session_cb (GtkWidget *w,
189 static void add_word_to_personal_cb (GtkWidget *w,
191 static void replace_with_create_dialog_cb (GtkWidget *w,
193 static void replace_with_supplied_word_cb (GtkWidget *w,
194 GtkAspell *gtkaspell);
195 static void replace_word_cb (GtkWidget *w,
197 static void replace_real_word (GtkAspell *gtkaspell,
199 static void check_with_alternate_cb (GtkWidget *w,
201 static void use_alternate_dict (GtkAspell *gtkaspell);
202 static void toggle_check_while_typing_cb (GtkWidget *w,
206 static void popup_menu (GtkAspell *gtkaspell,
208 static GtkMenu* make_sug_menu (GtkAspell *gtkaspell);
209 static void populate_submenu (GtkAspell *gtkaspell,
211 static GtkMenu* make_config_menu (GtkAspell *gtkaspell);
212 static void set_menu_pos (GtkMenu *menu,
217 /* Other menu callbacks */
218 static gboolean cancel_menu_cb (GtkMenuShell *w,
220 static void change_dict_cb (GtkWidget *w,
221 GtkAspell *gtkaspell);
222 static void switch_to_alternate_cb (GtkWidget *w,
225 /* Misc. helper functions */
226 static void set_point_continue (GtkAspell *gtkaspell);
227 static void continue_check (gpointer *gtkaspell);
228 static gboolean iswordsep (unsigned char c);
229 static guchar get_text_index_whar (GtkAspell *gtkaspell,
231 static gboolean get_word_from_pos (GtkAspell *gtkaspell,
237 static void allocate_color (GtkAspell *gtkaspell,
239 static void change_color (GtkAspell *gtkaspell,
244 static guchar* convert_to_aspell_encoding (const guchar *encoding);
245 static gint compare_dict (Dictionary *a,
247 static void dictionary_delete (Dictionary *dict);
248 static Dictionary * dictionary_dup (const Dictionary *dict);
249 static void free_suggestions_list (GtkAspell *gtkaspell);
250 static void reset_theword_data (GtkAspell *gtkaspell);
251 static void free_checkers (gpointer elt,
253 static gint find_gtkaspeller (gconstpointer aa,
255 /* gtkspellconfig - only one config per session */
256 GtkAspellConfig * gtkaspellconfig;
257 static void destroy_menu(GtkWidget *widget, gpointer user_data);
259 /******************************************************************************/
260 static gint get_textview_buffer_charcount(GtkTextView *view);
262 static gint get_textview_buffer_charcount(GtkTextView *view)
264 GtkTextBuffer *buffer;
266 g_return_val_if_fail(view, 0);
268 buffer = gtk_text_view_get_buffer(view);
269 g_return_val_if_fail(buffer, 0);
271 return gtk_text_buffer_get_char_count(buffer);
273 static gint get_textview_buffer_offset(GtkTextView *view)
275 GtkTextBuffer * buffer;
279 g_return_val_if_fail(view, 0);
281 buffer = gtk_text_view_get_buffer(view);
282 g_return_val_if_fail(buffer, 0);
284 mark = gtk_text_buffer_get_insert(buffer);
285 g_return_val_if_fail(mark, 0);
287 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
289 return gtk_text_iter_get_offset(&iter);
291 static void set_textview_buffer_offset(GtkTextView *view, gint offset)
293 GtkTextBuffer *buffer;
296 g_return_if_fail(view);
298 buffer = gtk_text_view_get_buffer(view);
299 g_return_if_fail(buffer);
301 gtk_text_buffer_get_iter_at_offset(buffer, &iter, offset);
302 gtk_text_buffer_place_cursor(buffer, &iter);
304 /******************************************************************************/
306 void gtkaspell_checkers_init(void)
308 gtkaspellcheckers = g_new(GtkAspellCheckers, 1);
309 gtkaspellcheckers->checkers = NULL;
310 gtkaspellcheckers->dictionary_list = NULL;
311 gtkaspellcheckers->error_message = NULL;
314 void gtkaspell_checkers_quit(void)
319 if (gtkaspellcheckers == NULL)
322 if ((checkers = gtkaspellcheckers->checkers)) {
323 debug_print("Aspell: number of running checkers to delete %d\n",
324 g_slist_length(checkers));
326 g_slist_foreach(checkers, free_checkers, NULL);
327 g_slist_free(checkers);
330 if ((dict_list = gtkaspellcheckers->dictionary_list)) {
331 debug_print("Aspell: number of dictionaries to delete %d\n",
332 g_slist_length(dict_list));
334 gtkaspell_free_dictionary_list(dict_list);
335 gtkaspellcheckers->dictionary_list = NULL;
338 g_free(gtkaspellcheckers->error_message);
343 static void gtkaspell_checkers_error_message (gchar *message)
346 if (gtkaspellcheckers->error_message) {
347 tmp = g_strdup_printf("%s\n%s",
348 gtkaspellcheckers->error_message,
351 g_free(gtkaspellcheckers->error_message);
352 gtkaspellcheckers->error_message = tmp;
354 gtkaspellcheckers->error_message = message;
357 const char *gtkaspell_checkers_strerror(void)
359 g_return_val_if_fail(gtkaspellcheckers, "");
360 return gtkaspellcheckers->error_message;
363 void gtkaspell_checkers_reset_error(void)
365 g_return_if_fail(gtkaspellcheckers);
367 g_free(gtkaspellcheckers->error_message);
369 gtkaspellcheckers->error_message = NULL;
372 GtkAspell *gtkaspell_new(const gchar *dictionary_path,
373 const gchar *dictionary,
374 const gchar *encoding,
375 gint misspelled_color,
376 gboolean check_while_typing,
377 gboolean use_alternate,
378 GtkTextView *gtktext,
379 GtkWindow *parent_win)
382 GtkAspell *gtkaspell;
383 GtkAspeller *gtkaspeller;
384 GtkTextBuffer *buffer;
386 g_return_val_if_fail(gtktext, NULL);
387 buffer = gtk_text_view_get_buffer(gtktext);
389 dict = g_new0(Dictionary, 1);
390 dict->fullname = g_strdup(dictionary);
391 dict->encoding = g_strdup(encoding);
393 gtkaspeller = gtkaspeller_new(dict);
394 dictionary_delete(dict);
399 gtkaspell = g_new0(GtkAspell, 1);
401 gtkaspell->dictionary_path = g_strdup(dictionary_path);
403 gtkaspell->gtkaspeller = gtkaspeller;
404 gtkaspell->alternate_speller = NULL;
405 gtkaspell->theword[0] = 0x00;
406 gtkaspell->start_pos = 0;
407 gtkaspell->end_pos = 0;
408 gtkaspell->orig_pos = -1;
409 gtkaspell->end_check_pos = -1;
410 gtkaspell->misspelled = -1;
411 gtkaspell->check_while_typing = check_while_typing;
412 gtkaspell->continue_check = NULL;
413 gtkaspell->config_menu = NULL;
414 gtkaspell->popup_config_menu = NULL;
415 gtkaspell->sug_menu = NULL;
416 gtkaspell->replace_entry = NULL;
417 gtkaspell->gtktext = gtktext;
418 gtkaspell->default_sug_mode = ASPELL_FASTMODE;
419 gtkaspell->max_sug = -1;
420 gtkaspell->suggestions_list = NULL;
421 gtkaspell->use_alternate = use_alternate;
422 gtkaspell->parent_window = GTK_WIDGET(parent_win);
424 allocate_color(gtkaspell, misspelled_color);
426 g_signal_connect_after(G_OBJECT(buffer), "insert-text",
427 G_CALLBACK(entry_insert_cb), gtkaspell);
428 g_signal_connect_after(G_OBJECT(buffer), "delete-range",
429 G_CALLBACK(entry_delete_cb), gtkaspell);
430 g_signal_connect(G_OBJECT(gtktext), "button-press-event",
431 G_CALLBACK(button_press_intercept_cb),
434 debug_print("Aspell: created gtkaspell %0x\n", (guint) gtkaspell);
439 void gtkaspell_delete(GtkAspell * gtkaspell)
441 GtkTextView *gtktext = gtkaspell->gtktext;
443 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
444 G_CALLBACK(entry_insert_cb),
446 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
447 G_CALLBACK(entry_delete_cb),
449 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
450 G_CALLBACK(button_press_intercept_cb),
453 gtkaspell_uncheck_all(gtkaspell);
455 gtkaspeller_delete(gtkaspell->gtkaspeller);
457 if (gtkaspell->use_alternate && gtkaspell->alternate_speller)
458 gtkaspeller_delete(gtkaspell->alternate_speller);
460 if (gtkaspell->sug_menu)
461 gtk_widget_destroy(gtkaspell->sug_menu);
463 if (gtkaspell->popup_config_menu)
464 gtk_widget_destroy(gtkaspell->popup_config_menu);
466 if (gtkaspell->config_menu)
467 gtk_widget_destroy(gtkaspell->config_menu);
469 if (gtkaspell->suggestions_list)
470 free_suggestions_list(gtkaspell);
472 g_free((gchar *)gtkaspell->dictionary_path);
474 debug_print("Aspell: deleting gtkaspell %0x\n", (guint) gtkaspell);
481 static void entry_insert_cb(GtkTextBuffer *textbuf,
485 GtkAspell *gtkaspell)
489 g_return_if_fail(gtkaspell->gtkaspeller->checker);
491 if (!gtkaspell->check_while_typing)
494 pos = gtk_text_iter_get_offset(iter);
496 if (iswordsep(newtext[0])) {
497 /* did we just end a word? */
499 check_at(gtkaspell, pos - 2);
501 /* did we just split a word? */
502 if (pos < gtk_text_buffer_get_char_count(textbuf))
503 check_at(gtkaspell, pos + 1);
505 /* check as they type, *except* if they're typing at the end (the most
508 if (pos < gtk_text_buffer_get_char_count(textbuf) &&
509 !iswordsep(get_text_index_whar(gtkaspell, pos))) {
510 check_at(gtkaspell, pos - 1);
515 static void entry_delete_cb(GtkTextBuffer *textbuf,
516 GtkTextIter *startiter,
517 GtkTextIter *enditer,
518 GtkAspell *gtkaspell)
523 g_return_if_fail(gtkaspell->gtkaspeller->checker);
525 if (!gtkaspell->check_while_typing)
528 start = gtk_text_iter_get_offset(startiter);
529 end = gtk_text_iter_get_offset(enditer);
530 origpos = get_textview_buffer_offset(gtkaspell->gtktext);
532 check_at(gtkaspell, start - 1);
533 check_at(gtkaspell, start);
536 set_textview_buffer_offset(gtkaspell->gtktext, origpos);
537 /* this is to *UNDO* the selection, in case they were holding shift
538 * while hitting backspace. */
539 /* needed with textview ??? */
540 /* gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos); */
543 /* ok, this is pretty wacky:
544 * we need to let the right-mouse-click go through, so it moves the cursor,
545 * but we *can't* let it go through, because GtkText interprets rightclicks as
546 * weird selection modifiers.
548 * so what do we do? forge rightclicks as leftclicks, then popup the menu.
551 static gint button_press_intercept_cb(GtkTextView *gtktext,
553 GtkAspell *gtkaspell)
558 g_return_val_if_fail(gtkaspell->gtkaspeller->checker, FALSE);
560 if (e->type != GDK_BUTTON_PRESS)
562 eb = (GdkEventButton*) e;
567 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
568 G_CALLBACK(button_press_intercept_cb),
570 g_signal_emit_by_name(G_OBJECT(gtktext), "button-release-event",
573 /* forge the leftclick */
576 g_signal_emit_by_name(G_OBJECT(gtktext), "button-press-event",
578 g_signal_emit_by_name(G_OBJECT(gtktext), "button-release-event",
580 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
581 G_CALLBACK(button_press_intercept_cb),
583 g_signal_stop_emission_by_name(G_OBJECT(gtktext), "button-press-event");
585 /* now do the menu wackiness */
586 popup_menu(gtkaspell, eb);
587 gtk_grab_remove(GTK_WIDGET(gtktext));
591 /* Checker creation */
592 static GtkAspeller *gtkaspeller_new(Dictionary *dictionary)
595 GtkAspeller *gtkaspeller = NULL;
599 g_return_val_if_fail(gtkaspellcheckers, NULL);
601 g_return_val_if_fail(dictionary, NULL);
603 if (dictionary->fullname == NULL)
604 gtkaspell_checkers_error_message(
605 g_strdup(_("No dictionary selected.")));
607 g_return_val_if_fail(dictionary->fullname, NULL);
609 if (dictionary->dictname == NULL) {
612 tmp = strrchr(dictionary->fullname, G_DIR_SEPARATOR);
615 dictionary->dictname = dictionary->fullname;
617 dictionary->dictname = tmp + 1;
620 dict = dictionary_dup(dictionary);
622 tmp = g_new0(GtkAspeller, 1);
623 tmp->dictionary = dict;
625 exist = g_slist_find_custom(gtkaspellcheckers->checkers, tmp,
630 if ((gtkaspeller = gtkaspeller_real_new(dict)) != NULL) {
631 gtkaspellcheckers->checkers = g_slist_append(
632 gtkaspellcheckers->checkers,
635 debug_print("Aspell: Created a new gtkaspeller %0x\n",
638 dictionary_delete(dict);
640 debug_print("Aspell: Could not create spell checker.\n");
643 debug_print("Aspell: number of existing checkers %d\n",
644 g_slist_length(gtkaspellcheckers->checkers));
649 static GtkAspeller *gtkaspeller_real_new(Dictionary *dict)
651 GtkAspeller *gtkaspeller;
652 AspellConfig *config;
653 AspellCanHaveError *ret;
655 g_return_val_if_fail(gtkaspellcheckers, NULL);
656 g_return_val_if_fail(dict, NULL);
658 gtkaspeller = g_new(GtkAspeller, 1);
660 gtkaspeller->dictionary = dict;
661 gtkaspeller->sug_mode = ASPELL_FASTMODE;
663 config = new_aspell_config();
665 if (!set_dictionary(config, dict))
668 ret = new_aspell_speller(config);
669 delete_aspell_config(config);
671 if (aspell_error_number(ret) != 0) {
672 gtkaspellcheckers->error_message
673 = g_strdup(aspell_error_message(ret));
675 delete_aspell_can_have_error(ret);
680 gtkaspeller->checker = to_aspell_speller(ret);
681 gtkaspeller->config = aspell_speller_config(gtkaspeller->checker);
686 static GtkAspeller *gtkaspeller_delete(GtkAspeller *gtkaspeller)
688 g_return_val_if_fail(gtkaspellcheckers, NULL);
690 gtkaspellcheckers->checkers =
691 g_slist_remove(gtkaspellcheckers->checkers,
694 debug_print("Aspell: Deleting gtkaspeller %0x.\n",
697 gtkaspeller_real_delete(gtkaspeller);
699 debug_print("Aspell: number of existing checkers %d\n",
700 g_slist_length(gtkaspellcheckers->checkers));
705 static GtkAspeller *gtkaspeller_real_delete(GtkAspeller *gtkaspeller)
707 g_return_val_if_fail(gtkaspeller, NULL);
708 g_return_val_if_fail(gtkaspeller->checker, NULL);
710 aspell_speller_save_all_word_lists(gtkaspeller->checker);
712 delete_aspell_speller(gtkaspeller->checker);
714 dictionary_delete(gtkaspeller->dictionary);
716 debug_print("Aspell: gtkaspeller %0x deleted.\n",
724 /*****************************************************************************/
725 /* Checker configuration */
727 static gboolean set_dictionary(AspellConfig *config, Dictionary *dict)
729 gchar *language = NULL;
730 gchar *jargon = NULL;
734 g_return_val_if_fail(config, FALSE);
735 g_return_val_if_fail(dict, FALSE);
737 strncpy(buf, dict->fullname, BUFSIZE-1);
738 buf[BUFSIZE-1] = 0x00;
740 buf[dict->dictname - dict->fullname] = 0x00;
742 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("dict-dir", buf);
743 debug_print("Aspell: looking for dictionaries in path %s.\n", buf);
745 strncpy(buf, dict->dictname, BUFSIZE-1);
748 if ((size = strrchr(buf, '-')) && isdigit((int) size[1]))
753 if ((jargon = strchr(language, '-')) != NULL)
756 if (size != NULL && jargon == size)
759 debug_print("Aspell: language: %s, jargon: %s, size: %s\n",
760 language, jargon ? jargon : "",
764 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("lang", language);
766 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("jargon", jargon);
768 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("size", size);
769 if (dict->encoding) {
772 aspell_enc = convert_to_aspell_encoding (dict->encoding);
773 aspell_config_replace(config, "encoding",
774 (const char *) aspell_enc);
777 RETURN_FALSE_IF_CONFIG_ERROR();
783 guchar *gtkaspell_get_dict(GtkAspell *gtkaspell)
786 g_return_val_if_fail(gtkaspell->gtkaspeller->config, NULL);
787 g_return_val_if_fail(gtkaspell->gtkaspeller->dictionary, NULL);
789 return g_strdup(gtkaspell->gtkaspeller->dictionary->dictname);
792 guchar *gtkaspell_get_path(GtkAspell *gtkaspell)
797 g_return_val_if_fail(gtkaspell->gtkaspeller->config, NULL);
798 g_return_val_if_fail(gtkaspell->gtkaspeller->dictionary, NULL);
800 dict = gtkaspell->gtkaspeller->dictionary;
801 path = g_strndup(dict->fullname, dict->dictname - dict->fullname);
806 /* set_sug_mode_cb() - Menu callback: Set the suggestion mode */
807 static void set_sug_mode_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
811 themode = (char *) gtk_label_get_text(GTK_LABEL(GTK_BIN(w)->child));
812 themode = g_strdup(themode);
814 set_real_sug_mode(gtkaspell, themode);
817 if (gtkaspell->config_menu)
818 populate_submenu(gtkaspell, gtkaspell->config_menu);
821 static void set_real_sug_mode(GtkAspell *gtkaspell, const char *themode)
824 gint mode = ASPELL_FASTMODE;
825 g_return_if_fail(gtkaspell);
826 g_return_if_fail(gtkaspell->gtkaspeller);
827 g_return_if_fail(themode);
829 if (!strcmp(themode,_("Normal Mode")))
830 mode = ASPELL_NORMALMODE;
831 else if (!strcmp( themode,_("Bad Spellers Mode")))
832 mode = ASPELL_BADSPELLERMODE;
834 result = gtkaspell_set_sug_mode(gtkaspell, mode);
837 debug_print("Aspell: error while changing suggestion mode:%s\n",
838 gtkaspellcheckers->error_message);
839 gtkaspell_checkers_reset_error();
843 /* gtkaspell_set_sug_mode() - Set the suggestion mode */
844 gboolean gtkaspell_set_sug_mode(GtkAspell *gtkaspell, gint themode)
846 AspellConfig *config;
848 g_return_val_if_fail(gtkaspell, FALSE);
849 g_return_val_if_fail(gtkaspell->gtkaspeller, FALSE);
850 g_return_val_if_fail(gtkaspell->gtkaspeller->config, FALSE);
852 debug_print("Aspell: setting sug mode of gtkaspeller %0x to %d\n",
853 (guint) gtkaspell->gtkaspeller, themode);
855 config = gtkaspell->gtkaspeller->config;
858 case ASPELL_FASTMODE:
859 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "fast");
861 case ASPELL_NORMALMODE:
862 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "normal");
864 case ASPELL_BADSPELLERMODE:
865 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode",
869 gtkaspellcheckers->error_message =
870 g_strdup(_("Unknown suggestion mode."));
874 gtkaspell->gtkaspeller->sug_mode = themode;
875 gtkaspell->default_sug_mode = themode;
880 /* misspelled_suggest() - Create a suggestion list for word */
881 static GList *misspelled_suggest(GtkAspell *gtkaspell, guchar *word)
883 const guchar *newword;
885 const AspellWordList *suggestions;
886 AspellStringEnumeration *elements;
888 g_return_val_if_fail(word, NULL);
890 if (!aspell_speller_check(gtkaspell->gtkaspeller->checker, word, -1)) {
891 free_suggestions_list(gtkaspell);
893 suggestions = aspell_speller_suggest(
894 gtkaspell->gtkaspeller->checker,
895 (const char *)word, -1);
896 elements = aspell_word_list_elements(suggestions);
897 list = g_list_append(list, g_strdup(word));
899 while (newword = aspell_string_enumeration_next(elements))
900 list = g_list_append(list, g_strdup(newword));
902 gtkaspell->max_sug = g_list_length(list) - 1;
903 gtkaspell->suggestions_list = list;
908 free_suggestions_list(gtkaspell);
913 /* misspelled_test() - Just test if word is correctly spelled */
914 static int misspelled_test(GtkAspell *gtkaspell, unsigned char *word)
916 return aspell_speller_check(gtkaspell->gtkaspeller->checker, word, -1)
921 static gboolean iswordsep(unsigned char c)
923 return !isalpha(c) && c != '\'';
926 static guchar get_text_index_whar(GtkAspell *gtkaspell, int pos)
928 GtkTextView *view = gtkaspell->gtktext;
929 GtkTextBuffer *buffer = gtk_text_view_get_buffer(view);
930 GtkTextIter start, end;
931 const gchar *utf8chars;
934 gtk_text_buffer_get_iter_at_offset(buffer, &start, pos);
935 gtk_text_buffer_get_iter_at_offset(buffer, &end, pos+1);
937 utf8chars = gtk_text_iter_get_text(&start, &end);
938 if (is_ascii_str(utf8chars)) {
939 a = utf8chars ? utf8chars[0] : '\0' ;
941 gchar *tr = conv_iconv_strdup(utf8chars, CS_UTF_8,
942 gtkaspell->gtkaspeller->dictionary->encoding);
952 /* get_word_from_pos () - return the word pointed to. */
953 /* Handles correctly the quotes. */
954 static gboolean get_word_from_pos(GtkAspell *gtkaspell, gint pos,
955 unsigned char* buf, gint buflen,
956 gint *pstart, gint *pend)
959 /* TODO : when correcting a word into quotes, change the color of */
960 /* the quotes too, as may be they were highlighted before. To do */
961 /* this, we can use two others pointers that points to the whole */
962 /* word including quotes. */
968 GtkTextView *gtktext;
970 gtktext = gtkaspell->gtktext;
971 if (iswordsep(get_text_index_whar(gtkaspell, pos)))
974 /* The apostrophe character is somtimes used for quotes
975 * So include it in the word only if it is not surrounded
976 * by other characters.
979 for (start = pos; start >= 0; --start) {
980 c = get_text_index_whar(gtkaspell, start);
983 if (!isalpha(get_text_index_whar(gtkaspell,
985 /* start_quote = TRUE; */
990 /* start_quote = TRUE; */
994 else if (!isalpha(c))
1000 for (end = pos; end < get_textview_buffer_charcount(gtktext); end++) {
1001 c = get_text_index_whar(gtkaspell, end);
1003 if (end < get_textview_buffer_charcount(gtktext)) {
1004 if (!isalpha(get_text_index_whar(gtkaspell,
1006 /* end_quote = TRUE; */
1011 /* end_quote = TRUE; */
1015 else if(!isalpha(c))
1025 if (end - start < buflen) {
1026 for (pos = start; pos < end; pos++) {
1028 get_text_index_whar(gtkaspell, pos);
1030 buf[pos - start] = 0;
1038 static gboolean check_at(GtkAspell *gtkaspell, gint from_pos)
1041 unsigned char buf[GTKASPELLWORDSIZE];
1042 GtkTextView *gtktext;
1044 g_return_val_if_fail(from_pos >= 0, FALSE);
1046 gtktext = gtkaspell->gtktext;
1048 if (!get_word_from_pos(gtkaspell, from_pos, buf, sizeof(buf),
1052 if (misspelled_test(gtkaspell, buf)) {
1053 strncpy(gtkaspell->theword, buf, GTKASPELLWORDSIZE - 1);
1054 gtkaspell->theword[GTKASPELLWORDSIZE - 1] = 0;
1055 gtkaspell->start_pos = start;
1056 gtkaspell->end_pos = end;
1057 free_suggestions_list(gtkaspell);
1059 change_color(gtkaspell, start, end, buf, &(gtkaspell->highlight));
1062 change_color(gtkaspell, start, end, buf, NULL);
1067 static gboolean check_next_prev(GtkAspell *gtkaspell, gboolean forward)
1073 gboolean misspelled;
1076 maxpos = gtkaspell->end_check_pos;
1084 pos = get_textview_buffer_offset(gtkaspell->gtktext);
1085 gtkaspell->orig_pos = pos;
1086 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1087 pos > minpos && pos <= maxpos)
1089 while (!(misspelled = check_at(gtkaspell, pos)) &&
1090 pos > minpos && pos <= maxpos) {
1092 while (!iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1093 pos > minpos && pos <= maxpos)
1096 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1097 pos > minpos && pos <= maxpos)
1101 GtkMenu *menu = NULL;
1102 misspelled_suggest(gtkaspell, gtkaspell->theword);
1105 gtkaspell->orig_pos = gtkaspell->end_pos;
1107 set_textview_buffer_offset(gtkaspell->gtktext,
1108 gtkaspell->end_pos);
1109 /* scroll line to window center */
1110 gtk_text_view_scroll_to_mark(gtkaspell->gtktext,
1111 gtk_text_buffer_get_insert(
1112 gtk_text_view_get_buffer(gtkaspell->gtktext)),
1113 0.0, TRUE, 0.0, 0.5);
1114 /* let textview recalculate coordinates (set_menu_pos) */
1115 while (gtk_events_pending ())
1116 gtk_main_iteration ();
1118 menu = make_sug_menu(gtkaspell);
1119 gtk_menu_popup(menu, NULL, NULL,
1120 set_menu_pos, gtkaspell, 0, GDK_CURRENT_TIME);
1121 g_signal_connect(G_OBJECT(menu), "deactivate",
1122 G_CALLBACK(destroy_menu),
1126 reset_theword_data(gtkaspell);
1128 alertpanel_notice(_("No misspelled word found."));
1129 set_textview_buffer_offset(gtkaspell->gtktext,
1130 gtkaspell->orig_pos);
1136 void gtkaspell_check_backwards(GtkAspell *gtkaspell)
1138 gtkaspell->continue_check = NULL;
1139 gtkaspell->end_check_pos =
1140 get_textview_buffer_charcount(gtkaspell->gtktext);
1141 check_next_prev(gtkaspell, FALSE);
1144 void gtkaspell_check_forwards_go(GtkAspell *gtkaspell)
1147 gtkaspell->continue_check = NULL;
1148 gtkaspell->end_check_pos =
1149 get_textview_buffer_charcount(gtkaspell->gtktext);
1150 check_next_prev(gtkaspell, TRUE);
1153 void gtkaspell_check_all(GtkAspell *gtkaspell)
1155 GtkTextView *gtktext;
1157 GtkTextBuffer *buffer;
1158 GtkTextIter startiter, enditer;
1160 g_return_if_fail(gtkaspell);
1161 g_return_if_fail(gtkaspell->gtktext);
1163 gtktext = gtkaspell->gtktext;
1164 buffer = gtk_text_view_get_buffer(gtktext);
1165 gtk_text_buffer_get_selection_bounds(buffer, &startiter, &enditer);
1166 start = gtk_text_iter_get_offset(&startiter);
1167 end = gtk_text_iter_get_offset(&enditer);
1171 end = gtk_text_buffer_get_char_count(buffer);
1172 } else if (start > end) {
1180 set_textview_buffer_offset(gtktext, start);
1182 gtkaspell->continue_check = continue_check;
1183 gtkaspell->end_check_pos = end;
1185 gtkaspell->misspelled = check_next_prev(gtkaspell, TRUE);
1188 static void continue_check(gpointer *data)
1190 GtkAspell *gtkaspell = (GtkAspell *) data;
1191 gint pos = get_textview_buffer_offset(gtkaspell->gtktext);
1192 if (pos < gtkaspell->end_check_pos && gtkaspell->misspelled)
1193 gtkaspell->misspelled = check_next_prev(gtkaspell, TRUE);
1195 gtkaspell->continue_check = NULL;
1198 void gtkaspell_highlight_all(GtkAspell *gtkaspell)
1203 GtkTextView *gtktext;
1205 g_return_if_fail(gtkaspell->gtkaspeller->checker);
1207 gtktext = gtkaspell->gtktext;
1209 len = get_textview_buffer_charcount(gtktext);
1211 origpos = get_textview_buffer_offset(gtktext);
1215 iswordsep(get_text_index_whar(gtkaspell, pos)))
1218 !iswordsep(get_text_index_whar(gtkaspell, pos)))
1221 check_at(gtkaspell, pos - 1);
1223 set_textview_buffer_offset(gtktext, origpos);
1226 static void replace_with_supplied_word_cb(GtkWidget *w, GtkAspell *gtkaspell)
1228 unsigned char *newword;
1229 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1231 newword = gtk_editable_get_chars(GTK_EDITABLE(gtkaspell->replace_entry),
1234 if (strcmp(newword, gtkaspell->theword)) {
1235 replace_real_word(gtkaspell, newword);
1237 if ((e->type == GDK_KEY_PRESS &&
1238 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK)) {
1239 aspell_speller_store_replacement(
1240 gtkaspell->gtkaspeller->checker,
1241 gtkaspell->theword, -1,
1244 gtkaspell->replace_entry = NULL;
1249 if (w && GTK_IS_DIALOG(w)) {
1250 gtk_widget_destroy(w);
1253 set_point_continue(gtkaspell);
1257 static void replace_word_cb(GtkWidget *w, gpointer data)
1259 unsigned char *newword;
1260 GtkAspell *gtkaspell = (GtkAspell *) data;
1261 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1263 newword = (unsigned char *) gtk_label_get_text(GTK_LABEL(GTK_BIN(w)->child));
1264 newword = g_strdup(newword);
1266 replace_real_word(gtkaspell, newword);
1268 if ((e->type == GDK_KEY_PRESS &&
1269 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK) ||
1270 (e->type == GDK_BUTTON_RELEASE &&
1271 ((GdkEventButton *) e)->state & GDK_CONTROL_MASK)) {
1272 aspell_speller_store_replacement(
1273 gtkaspell->gtkaspeller->checker,
1274 gtkaspell->theword, -1,
1278 gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1280 set_point_continue(gtkaspell);
1284 static void replace_real_word(GtkAspell *gtkaspell, gchar *newword)
1286 int oldlen, newlen, wordlen;
1289 GtkTextView *gtktext;
1290 GtkTextBuffer *textbuf;
1291 GtkTextIter startiter, enditer;
1293 if (!newword) return;
1295 gtktext = gtkaspell->gtktext;
1296 textbuf = gtk_text_view_get_buffer(gtktext);
1298 origpos = gtkaspell->orig_pos;
1300 oldlen = gtkaspell->end_pos - gtkaspell->start_pos;
1301 wordlen = strlen(gtkaspell->theword);
1303 newlen = strlen(newword); /* FIXME: multybyte characters? */
1305 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1306 G_CALLBACK(entry_insert_cb),
1308 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1309 G_CALLBACK(entry_delete_cb),
1312 gtk_text_buffer_get_iter_at_offset(textbuf, &startiter,
1313 gtkaspell->start_pos);
1314 gtk_text_buffer_get_iter_at_offset(textbuf, &enditer,
1315 gtkaspell->end_pos);
1316 g_signal_emit_by_name(G_OBJECT(textbuf), "delete-range",
1317 &startiter, &enditer, gtkaspell);
1318 g_signal_emit_by_name(G_OBJECT(textbuf), "insert-text",
1319 &startiter, newword, newlen, gtkaspell);
1321 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1322 G_CALLBACK(entry_insert_cb),
1324 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1325 G_CALLBACK(entry_delete_cb),
1328 /* Put the point and the position where we clicked with the mouse
1329 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1330 * to let it update correctly the word insertion and then the
1331 * point & position position. If not, SEGV after the first replacement
1332 * If the new word ends before point, put the point at its end.
1335 if (origpos - gtkaspell->start_pos < oldlen &&
1336 origpos - gtkaspell->start_pos >= 0) {
1337 /* Original point was in the word.
1338 * Let it there unless point is going to be outside of the word
1340 if (origpos - gtkaspell->start_pos >= newlen) {
1341 pos = gtkaspell->start_pos + newlen;
1344 else if (origpos >= gtkaspell->end_pos) {
1345 /* move the position according to the change of length */
1346 pos = origpos + newlen - oldlen;
1349 gtkaspell->end_pos = gtkaspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1351 if (get_textview_buffer_charcount(gtktext) < pos)
1352 pos = get_textview_buffer_charcount(gtktext);
1353 gtkaspell->orig_pos = pos;
1355 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1358 /* Accept this word for this session */
1359 static void add_word_to_session_cb(GtkWidget *w, gpointer data)
1362 GtkTextView *gtktext;
1363 GtkAspell *gtkaspell = (GtkAspell *) data;
1364 gtktext = gtkaspell->gtktext;
1366 pos = get_textview_buffer_offset(gtktext);
1368 aspell_speller_add_to_session(gtkaspell->gtkaspeller->checker,
1370 strlen(gtkaspell->theword));
1372 check_at(gtkaspell, gtkaspell->start_pos);
1374 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1376 set_point_continue(gtkaspell);
1379 /* add_word_to_personal_cb() - add word to personal dict. */
1380 static void add_word_to_personal_cb(GtkWidget *w, gpointer data)
1382 GtkAspell *gtkaspell = (GtkAspell *) data;
1384 aspell_speller_add_to_personal(gtkaspell->gtkaspeller->checker,
1386 strlen(gtkaspell->theword));
1388 check_at(gtkaspell, gtkaspell->start_pos);
1390 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1391 set_point_continue(gtkaspell);
1394 static void check_with_alternate_cb(GtkWidget *w, gpointer data)
1396 GtkAspell *gtkaspell = (GtkAspell *) data;
1399 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1401 use_alternate_dict(gtkaspell);
1402 misspelled = check_at(gtkaspell, gtkaspell->start_pos);
1404 if (!gtkaspell->continue_check) {
1406 gtkaspell->misspelled = misspelled;
1408 if (gtkaspell->misspelled) {
1410 misspelled_suggest(gtkaspell, gtkaspell->theword);
1412 set_textview_buffer_offset(gtkaspell->gtktext,
1413 gtkaspell->end_pos);
1415 menu = make_sug_menu(gtkaspell);
1416 gtk_menu_popup(menu, NULL, NULL,
1417 set_menu_pos, gtkaspell, 0,
1419 g_signal_connect(G_OBJECT(menu), "deactivate",
1420 G_CALLBACK(destroy_menu),
1425 gtkaspell->orig_pos = gtkaspell->start_pos;
1427 set_point_continue(gtkaspell);
1430 static gboolean replace_key_pressed(GtkWidget *widget,
1432 GtkAspell *gtkaspell)
1434 if (event && event->keyval == GDK_Escape) {
1435 gtk_widget_destroy(widget);
1437 } else if (event && event->keyval == GDK_Return) {
1438 replace_with_supplied_word_cb(widget, gtkaspell);
1444 static void replace_with_create_dialog_cb(GtkWidget *w, gpointer data)
1446 static PangoFontDescription *font_desc;
1453 GtkWidget *ok_button;
1454 GtkWidget *cancel_button;
1455 GtkWidget *confirm_area;
1459 GtkAspell *gtkaspell = (GtkAspell *) data;
1461 gdk_window_get_origin((GTK_WIDGET(w)->parent)->window, &xx, &yy);
1463 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1465 dialog = gtk_dialog_new();
1467 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1468 gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1469 gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1470 gtk_widget_set_uposition(dialog, xx, yy);
1472 g_signal_connect_swapped(G_OBJECT(dialog), "destroy",
1473 G_CALLBACK(gtk_widget_destroy),
1476 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
1477 hbox = gtk_hbox_new (FALSE, 12);
1478 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1479 gtk_widget_show (hbox);
1480 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
1483 thelabel = g_strdup_printf(_("<span weight=\"bold\" "
1484 "size=\"larger\">Replace \"%s\" with: </span>"),
1485 gtkaspell->theword);
1486 /* for title label */
1487 w_hbox = gtk_hbox_new(FALSE, 0);
1489 icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
1490 GTK_ICON_SIZE_DIALOG);
1491 gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
1492 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
1494 vbox = gtk_vbox_new (FALSE, 12);
1495 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1496 gtk_widget_show (vbox);
1498 label = gtk_label_new(thelabel);
1499 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1500 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1501 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1502 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1503 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1507 size = pango_font_description_get_size
1508 (label->style->font_desc);
1509 font_desc = pango_font_description_new();
1510 pango_font_description_set_weight
1511 (font_desc, PANGO_WEIGHT_BOLD);
1512 pango_font_description_set_size
1513 (font_desc, size * PANGO_SCALE_LARGE);
1516 gtk_widget_modify_font(label, font_desc);
1519 entry = gtk_entry_new();
1520 gtkaspell->replace_entry = entry;
1521 gtk_entry_set_text(GTK_ENTRY(entry), gtkaspell->theword);
1522 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1523 g_signal_connect(G_OBJECT(dialog),
1525 G_CALLBACK(replace_key_pressed), gtkaspell);
1526 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
1528 label = gtk_label_new(_("Holding down Control key while pressing "
1529 "Enter\nwill learn from mistake.\n"));
1530 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1531 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1532 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1533 gtk_widget_show(label);
1535 hbox = gtk_hbox_new(TRUE, 0);
1537 gtkut_stock_button_set_create(&confirm_area,
1538 &ok_button, GTK_STOCK_OK,
1539 &cancel_button, GTK_STOCK_CANCEL,
1542 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1543 confirm_area, FALSE, FALSE, 0);
1544 gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
1546 g_signal_connect(G_OBJECT(ok_button), "clicked",
1547 G_CALLBACK(replace_with_supplied_word_cb),
1549 g_signal_connect_swapped(G_OBJECT(ok_button), "clicked",
1550 G_CALLBACK(gtk_widget_destroy),
1553 g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked",
1554 G_CALLBACK(gtk_widget_destroy),
1557 gtk_widget_grab_focus(entry);
1559 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1561 gtk_widget_show_all(dialog);
1564 void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1566 GtkTextView *gtktext;
1567 GtkTextBuffer *buffer;
1568 GtkTextIter startiter, enditer;
1570 gtktext = gtkaspell->gtktext;
1572 buffer = gtk_text_view_get_buffer(gtktext);
1573 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, 0);
1574 gtk_text_buffer_get_iter_at_offset(buffer, &enditer,
1575 get_textview_buffer_charcount(gtktext)-1);
1576 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
1577 &startiter, &enditer);
1580 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1582 GtkAspell *gtkaspell = (GtkAspell *) data;
1584 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1586 if (!gtkaspell->check_while_typing)
1587 gtkaspell_uncheck_all(gtkaspell);
1589 if (gtkaspell->config_menu)
1590 populate_submenu(gtkaspell, gtkaspell->config_menu);
1593 static GSList *create_empty_dictionary_list(void)
1595 GSList *list = NULL;
1598 dict = g_new0(Dictionary, 1);
1599 dict->fullname = g_strdup(_("None"));
1600 dict->dictname = dict->fullname;
1601 dict->encoding = NULL;
1603 return g_slist_append(list, dict);
1606 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1607 GSList *gtkaspell_get_dictionary_list(const gchar *aspell_path, gint refresh)
1611 AspellConfig *config;
1612 AspellDictInfoList *dlist;
1613 AspellDictInfoEnumeration *dels;
1614 const AspellDictInfo *entry;
1616 if (!gtkaspellcheckers)
1617 gtkaspell_checkers_init();
1619 if (gtkaspellcheckers->dictionary_list && !refresh)
1620 return gtkaspellcheckers->dictionary_list;
1622 gtkaspell_free_dictionary_list(
1623 gtkaspellcheckers->dictionary_list);
1626 config = new_aspell_config();
1628 aspell_config_replace(config, "dict-dir", aspell_path);
1629 if (aspell_config_error_number(config) != 0) {
1630 gtkaspellcheckers->error_message = g_strdup(
1631 aspell_config_error_message(config));
1632 gtkaspellcheckers->dictionary_list =
1633 create_empty_dictionary_list();
1635 return gtkaspellcheckers->dictionary_list;
1638 dlist = get_aspell_dict_info_list(config);
1639 delete_aspell_config(config);
1641 debug_print("Aspell: checking for dictionaries in %s\n", aspell_path);
1642 dels = aspell_dict_info_list_elements(dlist);
1643 while ( (entry = aspell_dict_info_enumeration_next(dels)) != 0)
1645 dict = g_new0(Dictionary, 1);
1646 dict->fullname = g_strdup_printf("%s%s", aspell_path,
1648 dict->dictname = dict->fullname + strlen(aspell_path);
1649 dict->encoding = g_strdup(entry->code);
1651 if (g_slist_find_custom(list, dict,
1652 (GCompareFunc) compare_dict) != NULL) {
1653 dictionary_delete(dict);
1657 debug_print("Aspell: found dictionary %s %s %s\n", dict->fullname,
1658 dict->dictname, dict->encoding);
1659 list = g_slist_insert_sorted(list, dict,
1660 (GCompareFunc) compare_dict);
1663 delete_aspell_dict_info_enumeration(dels);
1667 debug_print("Aspell: error when searching for dictionaries: "
1668 "No dictionary found.\n");
1669 list = create_empty_dictionary_list();
1672 gtkaspellcheckers->dictionary_list = list;
1677 void gtkaspell_free_dictionary_list(GSList *list)
1681 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1683 dict = (Dictionary *) walk->data;
1684 dictionary_delete(dict);
1689 GtkWidget *gtkaspell_dictionary_option_menu_new(const gchar *aspell_path)
1691 GSList *dict_list, *tmp;
1696 dict_list = gtkaspell_get_dictionary_list(aspell_path, TRUE);
1697 g_return_val_if_fail(dict_list, NULL);
1699 menu = gtk_menu_new();
1701 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1702 dict = (Dictionary *) tmp->data;
1703 item = gtk_menu_item_new_with_label(dict->dictname);
1704 g_object_set_data(G_OBJECT(item), "dict_name",
1707 gtk_menu_append(GTK_MENU(menu), item);
1708 gtk_widget_show(item);
1711 gtk_widget_show(menu);
1716 gchar *gtkaspell_get_dictionary_menu_active_item(GtkWidget *menu)
1718 GtkWidget *menuitem;
1719 gchar *dict_fullname;
1722 g_return_val_if_fail(GTK_IS_MENU(menu), NULL);
1724 menuitem = gtk_menu_get_active(GTK_MENU(menu));
1725 dict_fullname = (gchar *) g_object_get_data(G_OBJECT(menuitem),
1727 g_return_val_if_fail(dict_fullname, NULL);
1729 label = g_strdup(dict_fullname);
1735 gint gtkaspell_set_dictionary_menu_active_item(GtkWidget *menu,
1736 const gchar *dictionary)
1741 g_return_val_if_fail(menu != NULL, 0);
1742 g_return_val_if_fail(dictionary != NULL, 0);
1743 g_return_val_if_fail(GTK_IS_OPTION_MENU(menu), 0);
1746 for (cur = GTK_MENU_SHELL(gtk_option_menu_get_menu(
1747 GTK_OPTION_MENU(menu)))->children;
1748 cur != NULL; cur = cur->next) {
1749 GtkWidget *menuitem;
1752 menuitem = GTK_WIDGET(cur->data);
1753 dict_name = g_object_get_data(G_OBJECT(menuitem),
1755 if ((dict_name != NULL) && !strcmp2(dict_name, dictionary)) {
1756 gtk_option_menu_set_history(GTK_OPTION_MENU(menu), n);
1766 GtkWidget *gtkaspell_sugmode_option_menu_new(gint sugmode)
1771 menu = gtk_menu_new();
1772 gtk_widget_show(menu);
1774 item = gtk_menu_item_new_with_label(_("Fast Mode"));
1775 gtk_widget_show(item);
1776 gtk_menu_append(GTK_MENU(menu), item);
1777 g_object_set_data(G_OBJECT(item), "sugmode",
1778 GINT_TO_POINTER(ASPELL_FASTMODE));
1780 item = gtk_menu_item_new_with_label(_("Normal Mode"));
1781 gtk_widget_show(item);
1782 gtk_menu_append(GTK_MENU(menu), item);
1783 g_object_set_data(G_OBJECT(item), "sugmode",
1784 GINT_TO_POINTER(ASPELL_NORMALMODE));
1786 item = gtk_menu_item_new_with_label(_("Bad Spellers Mode"));
1787 gtk_widget_show(item);
1788 gtk_menu_append(GTK_MENU(menu), item);
1789 g_object_set_data(G_OBJECT(item), "sugmode",
1790 GINT_TO_POINTER(ASPELL_BADSPELLERMODE));
1795 void gtkaspell_sugmode_option_menu_set(GtkOptionMenu *optmenu, gint sugmode)
1797 g_return_if_fail(GTK_IS_OPTION_MENU(optmenu));
1799 g_return_if_fail(sugmode == ASPELL_FASTMODE ||
1800 sugmode == ASPELL_NORMALMODE ||
1801 sugmode == ASPELL_BADSPELLERMODE);
1803 gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), sugmode - 1);
1806 gint gtkaspell_get_sugmode_from_option_menu(GtkOptionMenu *optmenu)
1811 g_return_val_if_fail(GTK_IS_OPTION_MENU(optmenu), -1);
1813 item = gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(optmenu)));
1815 sugmode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item),
1821 static void use_alternate_dict(GtkAspell *gtkaspell)
1825 tmp = gtkaspell->gtkaspeller;
1826 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1827 gtkaspell->alternate_speller = tmp;
1829 if (gtkaspell->config_menu)
1830 populate_submenu(gtkaspell, gtkaspell->config_menu);
1833 static void destroy_menu(GtkWidget *widget,
1834 gpointer user_data) {
1836 GtkAspell *gtkaspell = (GtkAspell *)user_data;
1838 if (gtkaspell->accel_group) {
1839 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1840 gtkaspell->accel_group);
1841 gtkaspell->accel_group = NULL;
1845 static void popup_menu(GtkAspell *gtkaspell, GdkEventButton *eb)
1847 GtkTextView * gtktext;
1848 GtkMenu *menu = NULL;
1850 gtktext = gtkaspell->gtktext;
1852 gtkaspell->orig_pos = get_textview_buffer_offset(gtktext);
1854 if (!(eb->state & GDK_SHIFT_MASK)) {
1855 if (check_at(gtkaspell, gtkaspell->orig_pos)) {
1857 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1859 if (misspelled_suggest(gtkaspell, gtkaspell->theword)) {
1860 menu = make_sug_menu(gtkaspell);
1861 gtk_menu_popup(menu,
1862 NULL, NULL, NULL, NULL,
1863 eb->button, eb->time);
1865 g_signal_connect(G_OBJECT(menu), "deactivate",
1866 G_CALLBACK(destroy_menu),
1871 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1873 menu = make_config_menu(gtkaspell);
1874 gtk_menu_popup(menu, NULL, NULL, NULL, NULL,
1875 eb->button, eb->time);
1877 g_signal_connect(G_OBJECT(menu), "deactivate",
1878 G_CALLBACK(destroy_menu),
1882 static gboolean aspell_key_pressed(GtkWidget *widget,
1884 GtkAspell *gtkaspell)
1886 if (event && (isascii(event->keyval) || event->keyval == GDK_Return)) {
1887 gtk_accel_groups_activate(
1888 G_OBJECT(gtkaspell->parent_window),
1889 event->keyval, event->state);
1890 } else if (event && event->keyval == GDK_Escape) {
1891 destroy_menu(NULL, gtkaspell);
1896 /* make_sug_menu() - Add menus to accept this word for this session
1897 * and to add it to personal dictionary
1899 static GtkMenu *make_sug_menu(GtkAspell *gtkaspell)
1901 GtkWidget *menu, *item;
1902 unsigned char *caption;
1903 GtkTextView *gtktext;
1904 GtkAccelGroup *accel;
1905 GList *l = gtkaspell->suggestions_list;
1908 gtktext = gtkaspell->gtktext;
1910 accel = gtk_accel_group_new();
1911 menu = gtk_menu_new();
1913 if (gtkaspell->sug_menu)
1914 gtk_widget_destroy(gtkaspell->sug_menu);
1916 if (gtkaspell->accel_group) {
1917 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1918 gtkaspell->accel_group);
1919 gtkaspell->accel_group = NULL;
1922 gtkaspell->sug_menu = menu;
1924 g_signal_connect(G_OBJECT(menu), "cancel",
1925 G_CALLBACK(cancel_menu_cb), gtkaspell);
1927 caption = g_strdup_printf(_("\"%s\" unknown in %s"),
1928 (unsigned char*) l->data,
1929 gtkaspell->gtkaspeller->dictionary->dictname);
1930 item = gtk_menu_item_new_with_label(caption);
1931 gtk_widget_show(item);
1932 gtk_menu_append(GTK_MENU(menu), item);
1933 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
1936 item = gtk_menu_item_new();
1937 gtk_widget_show(item);
1938 gtk_menu_append(GTK_MENU(menu), item);
1940 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1941 gtk_widget_show(item);
1942 gtk_menu_append(GTK_MENU(menu), item);
1943 g_signal_connect(G_OBJECT(item), "activate",
1944 G_CALLBACK(add_word_to_session_cb),
1946 gtk_widget_add_accelerator(item, "activate", accel, GDK_space,
1948 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1950 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1951 gtk_widget_show(item);
1952 gtk_menu_append(GTK_MENU(menu), item);
1953 g_signal_connect(G_OBJECT(item), "activate",
1954 G_CALLBACK(add_word_to_personal_cb),
1956 gtk_widget_add_accelerator(item, "activate", accel, GDK_Return,
1958 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1960 item = gtk_menu_item_new_with_label(_("Replace with..."));
1961 gtk_widget_show(item);
1962 gtk_menu_append(GTK_MENU(menu), item);
1963 g_signal_connect(G_OBJECT(item), "activate",
1964 G_CALLBACK(replace_with_create_dialog_cb),
1966 gtk_widget_add_accelerator(item, "activate", accel, GDK_R, 0,
1967 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1968 gtk_widget_add_accelerator(item, "activate", accel, GDK_R,
1972 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1973 caption = g_strdup_printf(_("Check with %s"),
1974 gtkaspell->alternate_speller->dictionary->dictname);
1975 item = gtk_menu_item_new_with_label(caption);
1977 gtk_widget_show(item);
1978 gtk_menu_append(GTK_MENU(menu), item);
1979 g_signal_connect(G_OBJECT(item), "activate",
1980 G_CALLBACK(check_with_alternate_cb),
1982 gtk_widget_add_accelerator(item, "activate", accel, GDK_X, 0,
1983 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1984 gtk_widget_add_accelerator(item, "activate", accel, GDK_X,
1989 item = gtk_menu_item_new();
1990 gtk_widget_show(item);
1991 gtk_menu_append(GTK_MENU(menu), item);
1995 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
1996 gtk_widget_show(item);
1997 gtk_menu_append(GTK_MENU(menu), item);
1999 GtkWidget *curmenu = menu;
2003 if (count == MENUCOUNT) {
2006 item = gtk_menu_item_new_with_label(_("More..."));
2007 gtk_widget_show(item);
2008 gtk_menu_append(GTK_MENU(curmenu), item);
2010 curmenu = gtk_menu_new();
2011 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2015 utf8buf = conv_codeset_strdup((unsigned char*)l->data,
2016 conv_get_locale_charset_str(),
2018 item = gtk_menu_item_new_with_label(utf8buf);
2019 gtk_widget_show(item);
2020 gtk_menu_append(GTK_MENU(curmenu), item);
2021 g_signal_connect(G_OBJECT(item), "activate",
2022 G_CALLBACK(replace_word_cb),
2025 if (curmenu == menu && count < MENUCOUNT) {
2026 gtk_widget_add_accelerator(item, "activate",
2031 gtk_widget_add_accelerator(item, "activate",
2040 } while ((l = l->next) != NULL);
2043 gtk_window_add_accel_group
2044 (GTK_WINDOW(gtkaspell->parent_window),
2046 gtkaspell->accel_group = accel;
2048 g_signal_connect(G_OBJECT(menu),
2050 G_CALLBACK(aspell_key_pressed), gtkaspell);
2052 return GTK_MENU(menu);
2055 static void populate_submenu(GtkAspell *gtkaspell, GtkWidget *menu)
2057 GtkWidget *item, *submenu;
2059 GtkAspeller *gtkaspeller = gtkaspell->gtkaspeller;
2061 if (GTK_MENU_SHELL(menu)->children) {
2062 GList *amenu, *alist;
2063 for (amenu = (GTK_MENU_SHELL(menu)->children); amenu; ) {
2064 alist = amenu->next;
2065 gtk_widget_destroy(GTK_WIDGET(amenu->data));
2070 dictname = g_strdup_printf(_("Dictionary: %s"),
2071 gtkaspeller->dictionary->dictname);
2072 item = gtk_menu_item_new_with_label(dictname);
2073 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
2075 gtk_widget_show(item);
2076 gtk_menu_append(GTK_MENU(menu), item);
2078 item = gtk_menu_item_new();
2079 gtk_widget_show(item);
2080 gtk_menu_append(GTK_MENU(menu), item);
2082 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
2083 dictname = g_strdup_printf(_("Use alternate (%s)"),
2084 gtkaspell->alternate_speller->dictionary->dictname);
2085 item = gtk_menu_item_new_with_label(dictname);
2087 g_signal_connect(G_OBJECT(item), "activate",
2088 G_CALLBACK(switch_to_alternate_cb),
2090 gtk_widget_show(item);
2091 gtk_menu_append(GTK_MENU(menu), item);
2094 item = gtk_check_menu_item_new_with_label(_("Fast Mode"));
2095 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_FASTMODE) {
2096 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),TRUE);
2097 gtk_widget_set_sensitive(GTK_WIDGET(item),FALSE);
2099 g_signal_connect(G_OBJECT(item), "activate",
2100 G_CALLBACK(set_sug_mode_cb),
2102 gtk_widget_show(item);
2103 gtk_menu_append(GTK_MENU(menu), item);
2105 item = gtk_check_menu_item_new_with_label(_("Normal Mode"));
2106 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_NORMALMODE) {
2107 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
2108 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2110 g_signal_connect(G_OBJECT(item), "activate",
2111 G_CALLBACK(set_sug_mode_cb),
2113 gtk_widget_show(item);
2114 gtk_menu_append(GTK_MENU(menu),item);
2116 item = gtk_check_menu_item_new_with_label(_("Bad Spellers Mode"));
2117 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_BADSPELLERMODE) {
2118 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
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_sug_mode_cb),
2124 gtk_widget_show(item);
2125 gtk_menu_append(GTK_MENU(menu), item);
2127 item = gtk_menu_item_new();
2128 gtk_widget_show(item);
2129 gtk_menu_append(GTK_MENU(menu), item);
2131 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
2132 if (gtkaspell->check_while_typing)
2133 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2135 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2136 g_signal_connect(G_OBJECT(item), "activate",
2137 G_CALLBACK(toggle_check_while_typing_cb),
2139 gtk_widget_show(item);
2140 gtk_menu_append(GTK_MENU(menu), item);
2142 item = gtk_menu_item_new();
2143 gtk_widget_show(item);
2144 gtk_menu_append(GTK_MENU(menu), item);
2146 submenu = gtk_menu_new();
2147 item = gtk_menu_item_new_with_label(_("Change dictionary"));
2148 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
2149 gtk_widget_show(item);
2150 gtk_menu_append(GTK_MENU(menu), item);
2153 if (gtkaspellcheckers->dictionary_list == NULL)
2154 gtkaspell_get_dictionary_list(gtkaspell->dictionary_path, FALSE);
2156 GtkWidget * curmenu = submenu;
2160 tmp = gtkaspellcheckers->dictionary_list;
2162 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
2163 tmp = g_slist_next(tmp)) {
2164 if (count == MENUCOUNT) {
2167 newmenu = gtk_menu_new();
2168 item = gtk_menu_item_new_with_label(_("More..."));
2169 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2172 gtk_menu_append(GTK_MENU(curmenu), item);
2173 gtk_widget_show(item);
2177 dict = (Dictionary *) tmp->data;
2178 item = gtk_check_menu_item_new_with_label(dict->dictname);
2179 g_object_set_data(G_OBJECT(item), "dict_name",
2181 if (strcmp2(dict->fullname,
2182 gtkaspell->gtkaspeller->dictionary->fullname))
2183 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2185 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2186 gtk_widget_set_sensitive(GTK_WIDGET(item),
2189 g_signal_connect(G_OBJECT(item), "activate",
2190 G_CALLBACK(change_dict_cb),
2192 gtk_widget_show(item);
2193 gtk_menu_append(GTK_MENU(curmenu), item);
2200 static GtkMenu *make_config_menu(GtkAspell *gtkaspell)
2202 if (!gtkaspell->popup_config_menu)
2203 gtkaspell->popup_config_menu = gtk_menu_new();
2205 debug_print("Aspell: creating/using popup_config_menu %0x\n",
2206 (guint) gtkaspell->popup_config_menu);
2207 populate_submenu(gtkaspell, gtkaspell->popup_config_menu);
2209 return GTK_MENU(gtkaspell->popup_config_menu);
2212 void gtkaspell_populate_submenu(GtkAspell *gtkaspell, GtkWidget *menuitem)
2216 menu = GTK_WIDGET(GTK_MENU_ITEM(menuitem)->submenu);
2218 debug_print("Aspell: using config menu %0x\n",
2219 (guint) gtkaspell->popup_config_menu);
2220 populate_submenu(gtkaspell, menu);
2222 gtkaspell->config_menu = menu;
2226 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y,
2227 gboolean *push_in, gpointer data)
2229 GtkAspell *gtkaspell = (GtkAspell *) data;
2230 gint xx = 0, yy = 0;
2233 GtkTextView *text = GTK_TEXT_VIEW(gtkaspell->gtktext);
2234 GtkTextBuffer *textbuf;
2239 textbuf = gtk_text_view_get_buffer(gtkaspell->gtktext);
2240 gtk_text_buffer_get_iter_at_mark(textbuf, &iter,
2241 gtk_text_buffer_get_insert(textbuf));
2242 gtk_text_view_get_iter_location(gtkaspell->gtktext, &iter, &rect);
2243 gtk_text_view_buffer_to_window_coords(text, GTK_TEXT_WINDOW_TEXT,
2247 gdk_window_get_origin(GTK_WIDGET(gtkaspell->gtktext)->window, &xx, &yy);
2249 sx = gdk_screen_width();
2250 sy = gdk_screen_height();
2252 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2258 gdk_char_width(gtk_style_get_font(GTK_WIDGET(text)->style), ' ');
2260 *y = rect.y + rect.height + yy;
2266 gdk_string_height(gtk_style_get_font(
2267 GTK_WIDGET(text)->style),
2268 gtkaspell->theword);
2271 /* Menu call backs */
2273 static gboolean cancel_menu_cb(GtkMenuShell *w, gpointer data)
2275 GtkAspell *gtkaspell = (GtkAspell *) data;
2277 gtkaspell->continue_check = NULL;
2278 set_point_continue(gtkaspell);
2283 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary)
2286 GtkAspeller *gtkaspeller;
2289 g_return_val_if_fail(gtkaspell, FALSE);
2290 g_return_val_if_fail(dictionary, FALSE);
2292 sug_mode = gtkaspell->default_sug_mode;
2294 dict = g_new0(Dictionary, 1);
2295 dict->fullname = g_strdup(dictionary);
2296 dict->encoding = g_strdup(gtkaspell->gtkaspeller->dictionary->encoding);
2298 if (gtkaspell->use_alternate && gtkaspell->alternate_speller &&
2299 dict == gtkaspell->alternate_speller->dictionary) {
2300 use_alternate_dict(gtkaspell);
2301 dictionary_delete(dict);
2305 gtkaspeller = gtkaspeller_new(dict);
2309 message = g_strdup_printf(_("The spell checker could not change dictionary.\n%s"),
2310 gtkaspellcheckers->error_message);
2312 alertpanel_warning(message);
2315 if (gtkaspell->use_alternate) {
2316 if (gtkaspell->alternate_speller)
2317 gtkaspeller_delete(gtkaspell->alternate_speller);
2318 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2320 gtkaspeller_delete(gtkaspell->gtkaspeller);
2322 gtkaspell->gtkaspeller = gtkaspeller;
2323 gtkaspell_set_sug_mode(gtkaspell, sug_mode);
2326 dictionary_delete(dict);
2328 if (gtkaspell->config_menu)
2329 populate_submenu(gtkaspell, gtkaspell->config_menu);
2334 /* change_dict_cb() - Menu callback : change dict */
2335 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2339 fullname = (gchar *) g_object_get_data(G_OBJECT(w), "dict_name");
2341 if (!strcmp2(fullname, _("None")))
2344 gtkaspell_change_dict(gtkaspell, fullname);
2347 static void switch_to_alternate_cb(GtkWidget *w,
2350 GtkAspell *gtkaspell = (GtkAspell *) data;
2351 use_alternate_dict(gtkaspell);
2354 /* Misc. helper functions */
2356 static void set_point_continue(GtkAspell *gtkaspell)
2358 GtkTextView *gtktext;
2360 gtktext = gtkaspell->gtktext;
2362 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
2364 if (gtkaspell->continue_check)
2365 gtkaspell->continue_check((gpointer *) gtkaspell);
2368 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2370 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtkaspell->gtktext);
2371 GdkColor *color = &(gtkaspell->highlight);
2373 /* Shameless copy from Sylpheed's gtkutils.c */
2375 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2377 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2379 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2382 gtk_text_buffer_create_tag(buffer, "misspelled",
2383 "foreground-gdk", color, NULL);
2386 static void change_color(GtkAspell * gtkaspell,
2387 gint start, gint end,
2391 GtkTextView *gtktext;
2392 GtkTextBuffer *buffer;
2393 GtkTextIter startiter, enditer;
2395 g_return_if_fail(start < end);
2397 gtktext = gtkaspell->gtktext;
2399 buffer = gtk_text_view_get_buffer(gtktext);
2400 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, start);
2401 gtk_text_buffer_get_iter_at_offset(buffer, &enditer, end);
2403 gtk_text_buffer_apply_tag_by_name(buffer, "misspelled",
2404 &startiter, &enditer);
2406 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
2407 &startiter, &enditer);
2410 /* convert_to_aspell_encoding () - converts ISO-8859-* strings to iso8859-*
2411 * as needed by aspell. Returns an allocated string.
2414 static guchar *convert_to_aspell_encoding (const guchar *encoding)
2416 guchar * aspell_encoding;
2418 if (strstr2(encoding, "ISO-8859-")) {
2419 aspell_encoding = g_strdup_printf("iso8859%s", encoding+8);
2422 if (!strcmp2(encoding, "US-ASCII"))
2423 aspell_encoding = g_strdup("iso8859-1");
2425 aspell_encoding = g_strdup(encoding);
2428 return aspell_encoding;
2431 /* compare_dict () - compare 2 dict names */
2432 static gint compare_dict(Dictionary *a, Dictionary *b)
2434 guint aparts = 0, bparts = 0;
2437 for (i=0; i < strlen(a->dictname); i++)
2438 if (a->dictname[i] == '-')
2440 for (i=0; i < strlen(b->dictname); i++)
2441 if (b->dictname[i] == '-')
2444 if (aparts != bparts)
2445 return (aparts < bparts) ? -1 : +1;
2448 compare = strcmp2(a->dictname, b->dictname);
2450 compare = strcmp2(a->fullname, b->fullname);
2455 static void dictionary_delete(Dictionary *dict)
2457 g_free(dict->fullname);
2458 g_free(dict->encoding);
2462 static Dictionary *dictionary_dup(const Dictionary *dict)
2466 dict2 = g_new(Dictionary, 1);
2468 dict2->fullname = g_strdup(dict->fullname);
2469 dict2->dictname = dict->dictname - dict->fullname + dict2->fullname;
2470 dict2->encoding = g_strdup(dict->encoding);
2475 static void free_suggestions_list(GtkAspell *gtkaspell)
2479 for (list = gtkaspell->suggestions_list; list != NULL;
2485 gtkaspell->max_sug = -1;
2486 gtkaspell->suggestions_list = NULL;
2489 static void reset_theword_data(GtkAspell *gtkaspell)
2491 gtkaspell->start_pos = 0;
2492 gtkaspell->end_pos = 0;
2493 gtkaspell->theword[0] = 0;
2494 gtkaspell->max_sug = -1;
2496 free_suggestions_list(gtkaspell);
2499 static void free_checkers(gpointer elt, gpointer data)
2501 GtkAspeller *gtkaspeller = elt;
2503 g_return_if_fail(gtkaspeller);
2505 gtkaspeller_real_delete(gtkaspeller);
2508 static gint find_gtkaspeller(gconstpointer aa, gconstpointer bb)
2510 Dictionary *a = ((GtkAspeller *) aa)->dictionary;
2511 Dictionary *b = ((GtkAspeller *) bb)->dictionary;
2513 if (a && b && a->fullname && b->fullname &&
2514 strcmp(a->fullname, b->fullname) == 0 &&
2515 a->encoding && b->encoding)
2516 return strcmp(a->encoding, b->encoding);