1 /* gtkpspell - a spell-checking addon for GtkText
2 * Copyright (c) 2000 Evan Martin.
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
26 #if defined(HAVE_CONFIG_H)
35 #include <sys/types.h>
48 #include <prefs_common.h>
53 #include <gtk/gtkoptionmenu.h>
54 #include <gtk/gtkmenu.h>
55 #include <gtk/gtkmenuitem.h>
56 #include <gdk/gdkkeysyms.h>
62 #include <pspell/pspell.h>
63 /* size of the text buffer used in various word-processing routines. */
66 /* number of suggestions to display on each menu. */
69 /* 'config' must be defined as a 'PspellConfig *' */
70 #define RETURN_FALSE_IF_CONFIG_ERROR() \
71 if (pspell_config_error_number(config) != 0) { \
72 gtkpspellcheckers->error_message = g_strdup(pspell_config_error_message(config)); \
76 #define CONFIG_REPLACE_RETURN_FALSE_IF_FAIL(option, value) { \
77 pspell_config_replace(config, option, value); \
78 RETURN_FALSE_IF_CONFIG_ERROR(); \
81 /******************************************************************************/
83 GtkPspellCheckers *gtkpspellcheckers;
85 /* Error message storage */
86 static void gtkpspell_checkers_error_message(gchar *message);
89 static void entry_insert_cb (GtkSText *gtktext,
93 GtkPspell *gtkpspell);
94 static void entry_delete_cb (GtkSText *gtktext,
97 GtkPspell *gtkpspell);
98 static gint button_press_intercept_cb (GtkSText *gtktext,
100 GtkPspell *gtkpspell);
102 /* Checker creation */
103 static GtkPspeller * gtkpspeller_new (Dictionary *dict);
104 static GtkPspeller * gtkpspeller_real_new (Dictionary *dict);
105 static GtkPspeller * gtkpspeller_delete (GtkPspeller *gtkpspeller);
106 static GtkPspeller * gtkpspeller_real_delete (GtkPspeller *gtkpspeller);
108 /* Checker configuration */
109 static gint set_dictionary (PspellConfig *config,
111 static void set_sug_mode_cb (GtkWidget *w,
112 GtkPspell *gtkpspell);
113 static void set_real_sug_mode (GtkPspell *gtkpspell,
114 const char *themode);
116 /* Checker actions */
117 static gboolean check_at (GtkPspell *gtkpspell,
119 static void check_next_prev (GtkPspell *gtkpspell,
122 static GList* misspelled_suggest (GtkPspell *gtkpspell,
124 static void add_word_to_session (GtkWidget *w,
126 static void add_word_to_personal (GtkWidget *w,
128 static void replace_word (GtkWidget *w,
129 GtkPspell *gtkpspell);
130 static void replace_real_word (GtkPspell *gtkpspell,
132 static void check_all_cb (GtkWidget *w,
133 GtkPspell *gtkpspell);
136 static void popup_menu (GtkPspell *gtkpspell,
138 static GtkMenu* make_sug_menu (GtkPspell *gtkpspell);
139 static GtkMenu* make_config_menu (GtkPspell *gtkpspell);
140 static void set_menu_pos (GtkMenu *menu,
144 /* Other menu callbacks */
145 static gboolean deactivate_menu_cb (GtkWidget *w,
147 static void change_dict_cb (GtkWidget *w,
148 GtkPspell *gtkpspell);
150 /* Misc. helper functions */
151 static gboolean iswordsep (unsigned char c);
152 static guchar get_text_index_whar (GtkPspell *gtkpspell,
154 static gboolean get_word_from_pos (GtkPspell *gtkpspell,
157 int *pstart, int *pend);
158 static void allocate_color (GtkPspell *gtkpspell);
159 static void change_color (GtkPspell *gtkpspell,
163 static guchar* convert_to_pspell_encoding (const guchar *encoding);
164 static gint compare_dict (Dictionary *a,
166 static void dictionary_delete (Dictionary *dict);
167 static Dictionary * dictionary_dup (const Dictionary *dict);
168 static void free_suggestions_list (GtkPspell *gtkpspell);
169 static void reset_theword_data (GtkPspell *gtkpspell);
170 static void free_checkers (gpointer elt,
172 static gint find_gtkpspeller (gconstpointer aa,
174 static void gtkpspell_alert_dialog (gchar *message);
175 /* gtkspellconfig - only one config per session */
176 GtkPspellConfig * gtkpspellconfig;
178 /******************************************************************************/
180 GtkPspellCheckers * gtkpspell_checkers_new()
182 GtkPspellCheckers *gtkpspellcheckers;
184 gtkpspellcheckers = g_new(GtkPspellCheckers, 1);
186 gtkpspellcheckers->checkers = NULL;
188 gtkpspellcheckers->dictionary_list = NULL;
190 gtkpspellcheckers->error_message = NULL;
192 return gtkpspellcheckers;
195 GtkPspellCheckers *gtkpspell_checkers_delete()
200 if (gtkpspellcheckers == NULL) return NULL;
202 if ((checkers = gtkpspellcheckers->checkers)) {
203 debug_print(_("Pspell: number of running checkers to delete %d\n"),
204 g_slist_length(checkers));
206 g_slist_foreach(checkers, free_checkers, NULL);
207 g_slist_free(checkers);
210 if ((dict_list = gtkpspellcheckers->dictionary_list)) {
211 debug_print(_("Pspell: number of dictionaries to delete %d\n"),
212 g_slist_length(dict_list));
214 gtkpspell_free_dictionary_list(dict_list);
215 gtkpspellcheckers->dictionary_list = NULL;
218 g_free(gtkpspellcheckers->error_message);
223 static void gtkpspell_checkers_error_message (gchar *message)
226 if (gtkpspellcheckers->error_message) {
227 tmp = g_strdup_printf("%s\n%s",
228 gtkpspellcheckers->error_message, message);
230 g_free(gtkpspellcheckers->error_message);
231 gtkpspellcheckers->error_message = tmp;
232 } else gtkpspellcheckers->error_message = message;
235 void gtkpspell_checkers_reset()
237 g_return_if_fail(gtkpspellcheckers);
239 g_free(gtkpspellcheckers->error_message);
241 gtkpspellcheckers->error_message = NULL;
244 GtkPspell *gtkpspell_new(const gchar *dictionary,
245 const gchar *encoding,
248 Dictionary *dict = g_new0(Dictionary, 1);
249 GtkPspell *gtkpspell;
250 GtkPspeller *gtkpspeller;
252 g_return_val_if_fail(gtktext, NULL);
254 dict->fullname = g_strdup(dictionary);
255 dict->encoding = g_strdup(encoding);
256 gtkpspeller = gtkpspeller_new(dict);
257 dictionary_delete(dict);
262 gtkpspell = g_new(GtkPspell, 1);
264 gtkpspell->gtkpspeller = gtkpspeller;
265 gtkpspell->theword[0] = 0x00;
266 gtkpspell->start_pos = 0;
267 gtkpspell->end_pos = 0;
268 gtkpspell->orig_pos = -1;
270 gtkpspell->gtktext = gtktext;
272 gtkpspell->default_sug_mode = PSPELL_FASTMODE;
273 gtkpspell->max_sug = -1;
274 gtkpspell->suggestions_list = NULL;
276 gtk_signal_connect_after(GTK_OBJECT(gtktext), "insert-text",
277 GTK_SIGNAL_FUNC(entry_insert_cb), gtkpspell);
279 gtk_signal_connect_after(GTK_OBJECT(gtktext), "delete-text",
280 GTK_SIGNAL_FUNC(entry_delete_cb), gtkpspell);
282 gtk_signal_connect(GTK_OBJECT(gtktext), "button-press-event",
283 GTK_SIGNAL_FUNC(button_press_intercept_cb), gtkpspell);
285 allocate_color(gtkpspell);
290 void gtkpspell_delete(GtkPspell * gtkpspell)
294 gtktext = gtkpspell->gtktext;
296 gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
297 GTK_SIGNAL_FUNC(entry_insert_cb), gtkpspell);
298 gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
299 GTK_SIGNAL_FUNC(entry_delete_cb), gtkpspell);
300 gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
301 GTK_SIGNAL_FUNC(button_press_intercept_cb),
304 gtkpspell_uncheck_all(gtkpspell);
306 gtkpspeller_delete(gtkpspell->gtkpspeller);
312 static void entry_insert_cb(GtkSText *gtktext, gchar *newtext,
313 guint len, guint *ppos,
314 GtkPspell * gtkpspell)
316 g_return_if_fail(gtkpspell->gtkpspeller->checker);
318 /* We must insert ourself the character to impose the */
319 /* color of the inserted character to be default */
320 /* Never mess with set_insertion when frozen */
321 gtk_stext_freeze(gtktext);
322 gtk_stext_backward_delete(GTK_STEXT(gtktext), len);
323 gtk_stext_insert(GTK_STEXT(gtktext), NULL, NULL, NULL, newtext, len);
324 *ppos = gtk_stext_get_point(GTK_STEXT(gtktext));
326 if (iswordsep(newtext[0])) {
327 /* did we just end a word? */
329 check_at(gtkpspell, *ppos - 2);
331 /* did we just split a word? */
332 if (*ppos < gtk_stext_get_length(gtktext))
333 check_at(gtkpspell, *ppos + 1);
335 /* check as they type, *except* if they're typing at the end (the most
338 if (*ppos < gtk_stext_get_length(gtktext)
339 && !iswordsep(get_text_index_whar(gtkpspell, *ppos)))
340 check_at(gtkpspell, *ppos - 1);
342 gtk_stext_thaw(gtktext);
343 gtk_editable_set_position(GTK_EDITABLE(gtktext), *ppos);
346 static void entry_delete_cb(GtkSText *gtktext, gint start, gint end,
347 GtkPspell *gtkpspell)
351 g_return_if_fail(gtkpspell->gtkpspeller->checker);
353 origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
355 check_at(gtkpspell, start - 1);
356 check_at(gtkpspell, start);
359 gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
360 gtk_stext_set_point(gtktext, origpos);
361 gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos);
362 /* this is to *UNDO* the selection, in case they were holding shift
363 * while hitting backspace. */
366 /* ok, this is pretty wacky:
367 * we need to let the right-mouse-click go through, so it moves the cursor,
368 * but we *can't* let it go through, because GtkText interprets rightclicks as
369 * weird selection modifiers.
371 * so what do we do? forge rightclicks as leftclicks, then popup the menu.
374 static gint button_press_intercept_cb(GtkSText *gtktext, GdkEvent *e, GtkPspell *gtkpspell)
379 g_return_val_if_fail(gtkpspell->gtkpspeller->checker, FALSE);
381 if (e->type != GDK_BUTTON_PRESS)
383 eb = (GdkEventButton*) e;
388 /* forge the leftclick */
391 gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
392 GTK_SIGNAL_FUNC(button_press_intercept_cb),
394 gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "button-press-event",
396 gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
397 GTK_SIGNAL_FUNC(button_press_intercept_cb),
399 gtk_signal_emit_stop_by_name(GTK_OBJECT(gtktext), "button-press-event");
401 /* now do the menu wackiness */
402 popup_menu(gtkpspell, eb);
403 gtk_grab_remove(GTK_WIDGET(gtktext));
407 /*****************************************************************************/
408 /* Checker creation */
409 static GtkPspeller *gtkpspeller_new(Dictionary *dictionary)
411 GSList *checkers, *exist;
412 GtkPspeller *gtkpspeller = NULL;
413 GtkPspeller *g = g_new(GtkPspeller, 1);
416 gboolean free_dict = TRUE;
418 g_return_val_if_fail(gtkpspellcheckers, NULL);
420 g_return_val_if_fail(dictionary, NULL);
422 if (dictionary->fullname == NULL)
423 gtkpspell_checkers_error_message(g_strdup(_("No dictionary selected.")));
425 g_return_val_if_fail(dictionary->fullname, NULL);
427 if (dictionary->dictname == NULL) {
430 tmp = strrchr(dictionary->fullname, G_DIR_SEPARATOR);
433 dictionary->dictname = dictionary->fullname;
435 dictionary->dictname = tmp + 1;
438 dict = dictionary_dup(dictionary);
440 ispell = (strstr2(dict->dictname, "-ispell") != NULL);
442 g->dictionary = dict;
444 checkers = gtkpspellcheckers->checkers;
446 exist = g_slist_find_custom(checkers, g, find_gtkpspeller);
451 gtkpspeller = (GtkPspeller *) exist->data;
454 debug_print(_("Pspell: Using existing ispell checker %0x\n"),
457 if ((gtkpspeller = gtkpspeller_real_new(dict)) != NULL) {
458 checkers = g_slist_append(checkers, gtkpspeller);
460 debug_print(_("Pspell: Created a new gtkpspeller %0x\n"),
465 debug_print(_("Pspell: Could not create spell checker.\n"));
468 debug_print(_("Pspell: number of existing checkers %d\n"),
469 g_slist_length(checkers));
472 dictionary_delete(dict);
474 gtkpspellcheckers->checkers = checkers;
479 static GtkPspeller *gtkpspeller_real_new(Dictionary *dict)
481 GtkPspeller *gtkpspeller;
482 PspellConfig *config;
483 PspellCanHaveError *ret;
485 g_return_val_if_fail(gtkpspellcheckers, NULL);
486 g_return_val_if_fail(dict, NULL);
488 gtkpspeller = g_new(GtkPspeller, 1);
490 gtkpspeller->dictionary = dict;
491 gtkpspeller->sug_mode = PSPELL_FASTMODE;
493 config = new_pspell_config();
495 if (!set_dictionary(config, dict))
498 ret = new_pspell_manager(config);
499 delete_pspell_config(config);
501 if (pspell_error_number(ret) != 0) {
502 gtkpspellcheckers->error_message = g_strdup(pspell_error_message(ret));
504 delete_pspell_can_have_error(ret);
509 gtkpspeller->checker = to_pspell_manager(ret);
510 gtkpspeller->config = pspell_manager_config(gtkpspeller->checker);
511 gtkpspeller->ispell = (strstr2(dict->fullname, "-ispell") != NULL);
516 static GtkPspeller *gtkpspeller_delete(GtkPspeller *gtkpspeller)
520 g_return_val_if_fail(gtkpspellcheckers, NULL);
522 checkers = gtkpspellcheckers->checkers;
524 if (gtkpspeller->ispell)
525 debug_print(_("Pspell: Won't remove existing ispell checker %0x.\n"),
528 checkers = g_slist_remove(checkers, gtkpspeller);
530 gtkpspellcheckers->checkers = checkers;
532 debug_print(_("Pspell: Deleting gtkpspeller %0x.\n"),
535 gtkpspeller_real_delete(gtkpspeller);
538 debug_print(_("Pspell: number of existing checkers %d\n"),
539 g_slist_length(checkers));
544 static GtkPspeller *gtkpspeller_real_delete(GtkPspeller *gtkpspeller)
546 g_return_val_if_fail(gtkpspeller, NULL);
547 g_return_val_if_fail(gtkpspeller->checker, NULL);
549 pspell_manager_save_all_word_lists(gtkpspeller->checker);
551 delete_pspell_manager(gtkpspeller->checker);
553 dictionary_delete(gtkpspeller->dictionary);
560 /*****************************************************************************/
561 /* Checker configuration */
563 static gboolean set_dictionary(PspellConfig *config, Dictionary *dict)
565 gchar *language = NULL;
566 gchar *spelling = NULL;
567 gchar *jargon = NULL;
568 gchar *module = NULL;
572 g_return_val_if_fail(config, FALSE);
573 g_return_val_if_fail(dict, FALSE);
575 strncpy(buf, dict->fullname, BUFSIZE-1);
576 buf[BUFSIZE-1] = 0x00;
578 buf[dict->dictname - dict->fullname] = 0x00;
580 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("rem-all-word-list-path", "");
581 debug_print(_("Pspell: removing all paths.\n"));
583 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("add-word-list-path", buf);
584 debug_print(_("Pspell: adding path (%s).\n"), buf);
586 strncpy(buf, dict->dictname, BUFSIZE-1);
589 if ((module = strrchr(buf, '-')) != NULL) {
592 if ((spelling = strchr(language, '-')) != NULL)
594 if (spelling != module) {
595 if ((end = strchr(spelling, '-')) != NULL) {
598 if (jargon != module)
599 if ((end = strchr(jargon, '-')) != NULL)
612 debug_print(_("Pspell: Language: %s, spelling: %s, jargon: %s, module: %s\n"),
613 language, spelling, jargon, module);
616 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("language-tag", language);
619 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("spelling", spelling);
622 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("jargon", jargon);
625 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("rem-all-module-search-order", "");
626 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("add-module-search-order", module);
629 if (dict->encoding) {
632 pspell_enc = convert_to_pspell_encoding (dict->encoding);
633 pspell_config_replace(config, "encoding", (const char *) pspell_enc);
636 RETURN_FALSE_IF_CONFIG_ERROR();
642 guchar *gtkpspell_get_dict(GtkPspell *gtkpspell)
645 g_return_val_if_fail(gtkpspell->gtkpspeller->config, NULL);
646 g_return_val_if_fail(gtkpspell->gtkpspeller->dictionary, NULL);
648 return g_strdup(gtkpspell->gtkpspeller->dictionary->dictname);
651 guchar *gtkpspell_get_path(GtkPspell *gtkpspell)
656 g_return_val_if_fail(gtkpspell->gtkpspeller->config, NULL);
657 g_return_val_if_fail(gtkpspell->gtkpspeller->dictionary, NULL);
659 dict = gtkpspell->gtkpspeller->dictionary;
660 path = g_strndup(dict->fullname, dict->dictname - dict->fullname);
665 /* set_sug_mode_cb() - Menu callback : Set the suggestion mode */
666 static void set_sug_mode_cb(GtkWidget *w, GtkPspell *gtkpspell)
668 unsigned char *themode;
670 if (gtkpspell->gtkpspeller->ispell) return;
672 gtk_label_get(GTK_LABEL(GTK_BIN(w)->child), (gchar **) &themode);
674 set_real_sug_mode(gtkpspell, themode);
677 static void set_real_sug_mode(GtkPspell *gtkpspell, const char *themode)
680 g_return_if_fail(gtkpspell);
681 g_return_if_fail(gtkpspell->gtkpspeller);
682 g_return_if_fail(themode);
684 if (!strcmp(themode, _("Fast Mode"))) {
685 result = gtkpspell_set_sug_mode(gtkpspell, PSPELL_FASTMODE);
686 } else if (!strcmp(themode,_("Normal Mode"))) {
687 result = gtkpspell_set_sug_mode(gtkpspell, PSPELL_NORMALMODE);
688 } else if (!strcmp( themode,_("Bad Spellers Mode"))) {
689 result = gtkpspell_set_sug_mode(gtkpspell, PSPELL_BADSPELLERMODE);
693 debug_print(_("Pspell: error while changing suggestion mode:%s\n"),
694 gtkpspellcheckers->error_message);
695 gtkpspell_checkers_reset();
699 /* gtkpspell_set_sug_mode() - Set the suggestion mode */
700 gboolean gtkpspell_set_sug_mode(GtkPspell *gtkpspell, gint themode)
702 PspellConfig *config;
703 g_return_val_if_fail(gtkpspell, FALSE);
704 g_return_val_if_fail(gtkpspell->gtkpspeller, FALSE);
705 g_return_val_if_fail(gtkpspell->gtkpspeller->config, FALSE);
707 config = gtkpspell->gtkpspeller->config;
709 case PSPELL_FASTMODE:
710 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "fast");
712 case PSPELL_NORMALMODE:
713 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "normal");
715 case PSPELL_BADSPELLERMODE:
716 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode",
720 gtkpspellcheckers->error_message =
721 g_strdup(_("Unknown suggestion mode."));
725 gtkpspell->gtkpspeller->sug_mode = themode;
726 gtkpspell->default_sug_mode = themode;
731 /* misspelled_suggest() - Create a suggestion list for word */
732 static GList *misspelled_suggest(GtkPspell *gtkpspell, guchar *word)
734 const guchar *newword;
736 const PspellWordList *suggestions;
737 PspellStringEmulation *elements;
739 g_return_val_if_fail(word, NULL);
741 if (!pspell_manager_check(gtkpspell->gtkpspeller->checker, word, -1)) {
742 free_suggestions_list(gtkpspell);
744 suggestions = pspell_manager_suggest(gtkpspell->gtkpspeller->checker,
745 (const char *)word, -1);
746 elements = pspell_word_list_elements(suggestions);
747 list = g_list_append(list, g_strdup(word));
749 while ((newword = pspell_string_emulation_next(elements)) != NULL)
750 list = g_list_append(list, g_strdup(newword));
752 gtkpspell->max_sug = g_list_length(list) - 1;
753 gtkpspell->suggestions_list = list;
758 free_suggestions_list(gtkpspell);
763 /* misspelled_test() - Just test if word is correctly spelled */
764 static int misspelled_test(GtkPspell *gtkpspell, unsigned char *word)
766 return pspell_manager_check(gtkpspell->gtkpspeller->checker, word, -1) ? 0 : 1;
770 static gboolean iswordsep(unsigned char c)
772 return !isalpha(c) && c != '\'';
775 static guchar get_text_index_whar(GtkPspell *gtkpspell, int pos)
780 text = gtk_editable_get_chars(GTK_EDITABLE(gtkpspell->gtktext), pos, pos + 1);
788 /* get_word_from_pos () - return the word pointed to. */
789 /* Handles correctly the quotes. */
790 static gboolean get_word_from_pos(GtkPspell *gtkpspell, int pos,
792 int *pstart, int *pend)
795 /* TODO : when correcting a word into quotes, change the color of */
796 /* the quotes too, as may be they were highlighted before. To do */
797 /* this, we can use two others pointers that points to the whole */
798 /* word including quotes. */
805 gtktext = gtkpspell->gtktext;
806 if (iswordsep(get_text_index_whar(gtkpspell, pos)))
809 /* The apostrophe character is somtimes used for quotes
810 * So include it in the word only if it is not surrounded
811 * by other characters.
814 for (start = pos; start >= 0; --start) {
815 c = get_text_index_whar(gtkpspell, start);
818 if (!isalpha(get_text_index_whar(gtkpspell, start - 1))) {
819 /* start_quote = TRUE; */
824 /* start_quote = TRUE; */
834 for (end = pos; end < gtk_stext_get_length(gtktext); end++) {
835 c = get_text_index_whar(gtkpspell, end);
837 if (end < gtk_stext_get_length(gtktext)) {
838 if (!isalpha(get_text_index_whar(gtkpspell, end + 1))) {
839 /* end_quote = TRUE; */
844 /* end_quote = TRUE; */
854 for (pos = start; pos < end; pos++)
855 buf[pos - start] = get_text_index_whar(gtkpspell, pos);
856 buf[pos - start] = 0;
867 static gboolean check_at(GtkPspell *gtkpspell, int from_pos)
870 unsigned char buf[BUFSIZE];
873 g_return_val_if_fail(from_pos >= 0, FALSE);
875 gtktext = gtkpspell->gtktext;
877 if (!get_word_from_pos(gtkpspell, from_pos, buf, &start, &end))
881 if (misspelled_test(gtkpspell, buf)) {
882 strncpy(gtkpspell->theword, buf, GTKPSPELLWORDSIZE - 1);
883 gtkpspell->theword[GTKPSPELLWORDSIZE - 1] = 0;
884 gtkpspell->start_pos = start;
885 gtkpspell->end_pos = end;
886 gtkpspell->newword[0] = 0;
887 free_suggestions_list(gtkpspell);
889 change_color(gtkpspell, start, end, &(gtkpspell->highlight));
893 change_color(gtkpspell, start, end, NULL);
898 static void check_all_cb(GtkWidget *w, GtkPspell *gtkpspell)
900 gtkpspell_check_all(gtkpspell);
903 void gtkpspell_check_all(GtkPspell *gtkpspell)
911 g_return_if_fail(gtkpspell->gtkpspeller->checker);
913 gtktext = gtkpspell->gtktext;
915 len = gtk_stext_get_length(gtktext);
917 adj_value = gtktext->vadj->value;
918 gtk_stext_freeze(gtktext);
919 origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
920 gtk_editable_set_position(GTK_EDITABLE(gtktext),0);
922 while (pos < len && iswordsep(get_text_index_whar(gtkpspell, pos)))
924 while (pos < len && !iswordsep(get_text_index_whar(gtkpspell, pos)))
927 check_at(gtkpspell, pos - 1);
929 gtk_stext_thaw(gtktext);
930 gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
931 gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos);
934 static void check_next_prev(GtkPspell *gtkpspell, gboolean forward)
941 GdkEvent *event= (GdkEvent *) gtk_get_current_event();
944 maxpos = gtk_stext_get_length(gtkpspell->gtktext);
953 pos = gtk_editable_get_position(GTK_EDITABLE(gtkpspell->gtktext));
954 gtkpspell->orig_pos = pos;
955 while (iswordsep(get_text_index_whar(gtkpspell, pos)) && pos > minpos && pos <= maxpos)
957 while (!(misspelled = check_at(gtkpspell, pos)) && pos > minpos && pos <= maxpos)
959 while (!iswordsep(get_text_index_whar(gtkpspell, pos)) && pos > minpos && pos <= maxpos)
962 while (iswordsep(get_text_index_whar(gtkpspell, pos)) && pos > minpos && pos <= maxpos)
966 misspelled_suggest(gtkpspell, gtkpspell->theword);
968 gtkpspell->orig_pos = gtkpspell->end_pos;
969 gtk_stext_set_point(GTK_STEXT(gtkpspell->gtktext),
971 gtk_editable_set_position(GTK_EDITABLE(gtkpspell->gtktext),
973 gtk_menu_popup(make_sug_menu(gtkpspell), NULL, NULL,
974 set_menu_pos, gtkpspell, 0, gdk_event_get_time(event));
976 reset_theword_data(gtkpspell);
978 gtk_stext_set_point(GTK_STEXT(gtkpspell->gtktext), gtkpspell->orig_pos);
979 gtk_editable_set_position(GTK_EDITABLE(gtkpspell->gtktext), gtkpspell->orig_pos);
983 void gtkpspell_check_backwards(GtkPspell *gtkpspell)
985 check_next_prev(gtkpspell, FALSE);
988 void gtkpspell_check_forwards_go(GtkPspell *gtkpspell)
990 check_next_prev(gtkpspell, TRUE);
994 static void replace_word(GtkWidget *w, GtkPspell *gtkpspell)
996 unsigned char *newword;
997 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
999 gtk_label_get(GTK_LABEL(GTK_BIN(w)->child), (gchar**) &newword);
1000 replace_real_word(gtkpspell, newword);
1001 if ((e->type == GDK_KEY_PRESS && ((GdkEventKey *) e)->state & GDK_MOD1_MASK) ||
1002 (e->type == GDK_BUTTON_RELEASE && ((GdkEventButton *) e)->state & GDK_MOD1_MASK)) {
1003 pspell_manager_store_replacement(gtkpspell->gtkpspeller->checker,
1004 gtkpspell->theword, -1,
1007 gtkpspell->newword[0] = 0x00;
1008 gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1011 static void replace_real_word(GtkPspell *gtkpspell, gchar *newword)
1013 int oldlen, newlen, wordlen;
1016 gint start = gtkpspell->start_pos;
1019 if (!newword) return;
1021 gtktext = gtkpspell->gtktext;
1023 gtk_stext_freeze(GTK_STEXT(gtktext));
1024 origpos = gtkpspell->orig_pos;
1026 oldlen = gtkpspell->end_pos - gtkpspell->start_pos;
1027 wordlen = strlen(gtkpspell->theword);
1029 strncpy(gtkpspell->newword, newword, GTKPSPELLWORDSIZE - 1);
1030 gtkpspell->newword[GTKPSPELLWORDSIZE-1] = 0x00;
1031 newlen = strlen(newword); /* FIXME: multybyte characters? */
1033 gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
1034 GTK_SIGNAL_FUNC(entry_insert_cb),
1036 gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
1037 GTK_SIGNAL_FUNC(entry_delete_cb),
1039 gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "delete-text",
1040 gtkpspell->start_pos, gtkpspell->end_pos);
1041 gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "insert-text",
1042 newword, newlen, &start);
1044 gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
1045 GTK_SIGNAL_FUNC(entry_insert_cb),
1047 gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
1048 GTK_SIGNAL_FUNC(entry_delete_cb),
1051 /* Put the point and the position where we clicked with the mouse */
1052 /* It seems to be a hack, as I must thaw,freeze,thaw the widget */
1053 /* to let it update correctly the word insertion and then the */
1054 /* point & position position. If not, SEGV after the first replacement */
1055 /* If the new word ends before point, put the point at its end*/
1057 if (origpos - gtkpspell->start_pos < oldlen && origpos - gtkpspell->start_pos >= 0) {
1058 /* Original point was in the word. */
1059 /* Let it there unless point is going to be outside of the word */
1060 if (origpos - gtkpspell->start_pos >= newlen) {
1061 pos = gtkpspell->start_pos + newlen;
1064 else if (origpos >= gtkpspell->end_pos) {
1065 /* move the position according to the change of length */
1066 pos = origpos + newlen - oldlen;
1069 gtkpspell->end_pos = gtkpspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1071 gtk_stext_thaw(GTK_STEXT(gtktext));
1072 gtk_stext_freeze(GTK_STEXT(gtktext));
1073 if (GTK_STEXT(gtktext)->text_len < pos)
1074 pos = gtk_stext_get_length(GTK_STEXT(gtktext));
1075 gtkpspell->orig_pos = pos;
1076 gtk_editable_set_position(GTK_EDITABLE(gtktext), gtkpspell->orig_pos);
1077 gtk_stext_set_point(GTK_STEXT(gtktext),
1078 gtk_editable_get_position(GTK_EDITABLE(gtktext)));
1079 gtk_stext_thaw(GTK_STEXT(gtktext));
1082 /* Accept this word for this session */
1084 static void add_word_to_session(GtkWidget *w, GtkPspell *gtkpspell)
1089 gtktext = gtkpspell->gtktext;
1091 gtk_stext_freeze(GTK_STEXT(gtktext));
1093 pos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
1095 pspell_manager_add_to_session(gtkpspell->gtkpspeller->checker,gtkpspell->theword, strlen(gtkpspell->theword));
1097 check_at(gtkpspell, gtkpspell->start_pos);
1099 gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1102 /* add_word_to_personal() - add word to personal dict. */
1103 static void add_word_to_personal(GtkWidget *w, GtkPspell *gtkpspell)
1107 gtktext = gtkpspell->gtktext;
1109 gtk_stext_freeze(GTK_STEXT(gtktext));
1111 pspell_manager_add_to_personal(gtkpspell->gtkpspeller->checker,gtkpspell->theword, strlen(gtkpspell->theword));
1113 check_at(gtkpspell, gtkpspell->start_pos);
1115 gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1118 void gtkpspell_uncheck_all(GtkPspell * gtkpspell)
1121 unsigned char *text;
1125 gtktext=gtkpspell->gtktext;
1127 adj_value = gtktext->vadj->value;
1128 gtk_stext_freeze(gtktext);
1129 origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
1130 text = gtk_editable_get_chars(GTK_EDITABLE(gtktext), 0, -1);
1131 gtk_stext_set_point(gtktext, 0);
1132 gtk_stext_forward_delete(gtktext, gtk_stext_get_length(gtktext));
1133 gtk_stext_insert(gtktext, NULL, NULL, NULL, text, strlen(text));
1134 gtk_stext_thaw(gtktext);
1138 gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
1139 gtk_adjustment_set_value(gtktext->vadj, adj_value);
1142 static GSList *create_empty_dictionary_list(void)
1144 GSList *list = NULL;
1147 dict = g_new0(Dictionary, 1);/*printf("N %08x dictionary\n", dict);*/
1148 dict->fullname = g_strdup(_("None"));/*printf("N %08x fullname\n", dict->fullname);*/
1149 dict->dictname = dict->fullname;
1150 dict->encoding = NULL;
1151 return g_slist_append(list, dict);
1154 /* gtkpspell_get_dictionary_list() - returns list of dictionary names */
1155 GSList *gtkpspell_get_dictionary_list(const gchar *pspell_path, gint refresh)
1158 gchar *dict_path, *tmp, *prevdir;
1159 gchar tmpname[BUFSIZE];
1164 if (!gtkpspellcheckers)
1165 gtkpspellcheckers = gtkpspell_checkers_new();
1167 if (gtkpspellcheckers->dictionary_list && !refresh)
1168 return gtkpspellcheckers->dictionary_list;
1170 gtkpspell_free_dictionary_list(gtkpspellcheckers->dictionary_list);
1175 #warning TODO: no directory change
1177 dict_path = g_strdup(pspell_path);
1178 prevdir = g_get_current_dir();
1179 if (chdir(dict_path) <0) {
1180 debug_print(_("Error when searching for dictionaries:\n%s\n"),
1184 gtkpspellcheckers->dictionary_list = create_empty_dictionary_list();
1185 return gtkpspellcheckers->dictionary_list;
1188 debug_print(_("Checking for dictionaries in %s\n"), dict_path);
1190 if (NULL != (dir = opendir("."))) {
1191 while (NULL != (ent = readdir(dir))) {
1192 /* search for pwli */
1193 if ((NULL != (tmp = strstr2(ent->d_name, ".pwli"))) && (tmp[5] == 0x00)) {
1194 g_snprintf(tmpname, BUFSIZE, "%s%s", G_DIR_SEPARATOR_S, ent->d_name);
1195 tmpname[MIN(tmp - ent->d_name + 1, BUFSIZE-1)] = 0x00;
1196 dict = g_new0(Dictionary, 1);/*printf("N %08x dictionary\n", dict);*/
1197 dict->fullname = g_strdup_printf("%s%s", dict_path, tmpname);/*printf("N %08x fullname\n", dict->fullname);*/
1198 dict->dictname = strrchr(dict->fullname, G_DIR_SEPARATOR) + 1;
1199 dict->encoding = NULL;
1200 debug_print(_("Found dictionary %s %s\n"), dict->fullname, dict->dictname);
1201 list = g_slist_insert_sorted(list, dict, (GCompareFunc) compare_dict);
1207 debug_print(_("Error when searching for dictionaries.\nNo dictionary found.\n(%s)"),
1209 list = create_empty_dictionary_list();
1213 debug_print(_("Error when searching for dictionaries.\nNo dictionary found."));
1214 list = create_empty_dictionary_list();
1219 gtkpspellcheckers->dictionary_list = list;
1223 void gtkpspell_free_dictionary_list(GSList *list)
1227 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1229 dict = (Dictionary *) walk->data;
1230 dictionary_delete(dict);
1236 static void dictionary_option_menu_item_data_destroy(gpointer data)
1238 Dictionary *dict = (Dictionary *) data;
1241 dictionary_delete(dict);
1245 GtkWidget *gtkpspell_dictionary_option_menu_new(const gchar *pspell_path)
1247 GSList *dict_list, *tmp;
1252 dict_list = gtkpspell_get_dictionary_list(pspell_path, TRUE);
1253 g_return_val_if_fail(dict_list, NULL);
1255 menu = gtk_menu_new();
1257 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1258 dict = (Dictionary *) tmp->data;
1259 item = gtk_menu_item_new_with_label(dict->dictname);
1261 gtk_object_set_data(GTK_OBJECT(item), "dict_name",
1264 gtk_menu_append(GTK_MENU(menu), item);
1265 gtk_widget_show(item);
1268 gtk_widget_show(menu);
1273 gchar *gtkpspell_get_dictionary_menu_active_item(GtkWidget *menu)
1275 GtkWidget *menuitem;
1279 g_return_val_if_fail(GTK_IS_MENU(menu), NULL);
1280 menuitem = gtk_menu_get_active(GTK_MENU(menu));
1281 result = (Dictionary *) gtk_object_get_data(GTK_OBJECT(menuitem), "dict_name");
1282 g_return_val_if_fail(result->fullname, NULL);
1283 label = g_strdup(result->fullname);
1288 GtkWidget *gtkpspell_sugmode_option_menu_new(gint sugmode)
1294 menu = gtk_menu_new();
1295 gtk_widget_show(menu);
1297 item = gtk_menu_item_new_with_label(_("Fast Mode"));
1298 gtk_widget_show(item);
1299 gtk_menu_append(GTK_MENU(menu), item);
1300 gtk_object_set_data(GTK_OBJECT(item), "sugmode", GINT_TO_POINTER(PSPELL_FASTMODE));
1302 item = gtk_menu_item_new_with_label(_("Normal Mode"));
1303 gtk_widget_show(item);
1304 gtk_menu_append(GTK_MENU(menu), item);
1305 gtk_object_set_data(GTK_OBJECT(item), "sugmode", GINT_TO_POINTER(PSPELL_NORMALMODE));
1307 item = gtk_menu_item_new_with_label(_("Bad Spellers Mode"));
1308 gtk_widget_show(item);
1309 gtk_menu_append(GTK_MENU(menu), item);
1310 gtk_object_set_data(GTK_OBJECT(item), "sugmode", GINT_TO_POINTER(PSPELL_BADSPELLERMODE));
1315 void gtkpspell_sugmode_option_menu_set(GtkOptionMenu *optmenu, gint sugmode)
1317 g_return_if_fail(GTK_IS_OPTION_MENU(optmenu));
1319 g_return_if_fail(sugmode == PSPELL_FASTMODE ||
1320 sugmode == PSPELL_NORMALMODE ||
1321 sugmode == PSPELL_BADSPELLERMODE);
1323 gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), sugmode - 1);
1326 gint gtkpspell_get_sugmode_from_option_menu(GtkOptionMenu *optmenu)
1331 g_return_val_if_fail(GTK_IS_OPTION_MENU(optmenu), -1);
1333 item = gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(optmenu)));
1335 sugmode = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item), "sugmode"));
1341 void gtkpspell_set_primary_dict(GtkPspell *gtkpspell, gchar *dict, gchar *encoding)
1343 g_return_if_fail(gtkpspell);
1344 if (gtkpspell->dict1)
1345 dictionary_delete(gtkpspell->dict1);
1346 gtkpspell->dict1 = g_new(Dictionary, 1);
1347 gtkpspell->dict1->fullname = g_strdup(dict);
1348 gtkpspell->dict1->encoding = g_strdup(encoding);
1351 void gtkpspell_set_secondary_dict(GtkPspell *gtkpspell, gchar *dict, gchar *encoding)
1353 g_return_if_fail(gtkpspell);
1354 if (gtkpspell->dict2)
1355 dictionary_delete(gtkpspell->dict2);
1356 gtkpspell->dict2 = g_new(Dictionary, 1);
1357 gtkpspell->dict2->fullname = g_strdup(dict);
1358 gtkpspell->dict2->encoding = g_strdup(encoding);
1361 gboolean gtkpspell_use_dict(GtkPspell *gtkpspell, Dictionary *dict)
1366 gboolean gtkpspell_use_dictionary(GtkPspell *gtkpspell, gchar *dictpath, gchar *encoding)
1371 g_return_val_if_fail(gtkpspell, FALSE);
1372 g_return_val_if_fail(dict, FALSE);
1373 g_return_val_if_fail(encoding, FALSE);
1375 dict = g_new(Dictionary, 1);
1377 dict->fullname = g_strdup(dictpath);
1378 dict->encoding = NULL; /* To be continued */
1380 retval = gtkpspell_use_dict(gtkpspell, dict);
1382 dictionary_delete(dict);
1388 gboolean gtkpspell_use_primary(GtkPspell *gtkpspell)
1390 g_return_val_if_fail(gtkpspell, FALSE);
1392 return gtkpspell_use_dict(gtkpspell, gtkpspell->dict1);
1395 gboolean gtkpspell_use_secondary(GtkPspell *gtkpspell)
1397 g_return_val_if_fail(gtkpspell, FALSE);
1399 return gtkpspell_use_dict(gtkpspell, gtkpspell->dict2);
1402 /*****************************************************************************/
1404 static void popup_menu(GtkPspell *gtkpspell, GdkEventButton *eb)
1408 gtktext = gtkpspell->gtktext;
1409 gtkpspell->orig_pos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
1411 if (!(eb->state & GDK_SHIFT_MASK)) {
1412 if (check_at(gtkpspell, gtkpspell->orig_pos)) {
1414 gtk_editable_set_position(GTK_EDITABLE(gtktext),
1415 gtkpspell->orig_pos);
1416 gtk_stext_set_point(gtktext, gtkpspell->orig_pos);
1418 if (misspelled_suggest(gtkpspell, gtkpspell->theword)) {
1419 gtk_menu_popup(make_sug_menu(gtkpspell),
1420 NULL, NULL, NULL, NULL,
1421 eb->button, eb->time);
1426 gtk_editable_set_position(GTK_EDITABLE(gtktext),
1427 gtkpspell->orig_pos);
1428 gtk_stext_set_point(gtktext, gtkpspell->orig_pos);
1432 gtk_menu_popup(make_config_menu(gtkpspell),NULL,NULL,NULL,NULL,
1433 eb->button,eb->time);
1436 /* make_sug_menu() - Add menus to accept this word for this session and to add it to
1437 * personal dictionary */
1438 static GtkMenu *make_sug_menu(GtkPspell *gtkpspell)
1440 GtkWidget *menu, *item;
1441 unsigned char *caption;
1443 GList *l = gtkpspell->suggestions_list;
1444 GtkAccelGroup *accel;
1447 gtktext = gtkpspell->gtktext;
1449 accel = gtk_accel_group_new();
1450 menu = gtk_menu_new();
1452 gtk_signal_connect(GTK_OBJECT(menu), "deactivate",
1453 GTK_SIGNAL_FUNC(deactivate_menu_cb), gtkpspell);
1455 caption = g_strdup_printf(_("Unknown word: \"%s\""),
1456 (unsigned char*) l->data);
1457 item = gtk_menu_item_new_with_label(caption);
1458 gtk_widget_show(item);
1459 gtk_menu_append(GTK_MENU(menu), item);
1460 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
1463 item = gtk_menu_item_new();
1464 gtk_widget_show(item);
1465 gtk_menu_append(GTK_MENU(menu), item);
1467 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1468 gtk_widget_show(item);
1469 gtk_menu_append(GTK_MENU(menu), item);
1470 gtk_signal_connect(GTK_OBJECT(item), "activate",
1471 GTK_SIGNAL_FUNC(add_word_to_session),
1473 gtk_accel_group_add(accel, GDK_space, GDK_MOD1_MASK,
1474 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE,
1475 GTK_OBJECT(item), "activate");
1477 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1478 gtk_widget_show(item);
1479 gtk_menu_append(GTK_MENU(menu), item);
1480 gtk_signal_connect(GTK_OBJECT(item), "activate",
1481 GTK_SIGNAL_FUNC(add_word_to_personal),
1483 gtk_accel_group_add(accel, GDK_Return, GDK_MOD1_MASK,
1484 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE,
1485 GTK_OBJECT(item), "activate");
1487 item = gtk_menu_item_new();
1488 gtk_widget_show(item);
1489 gtk_menu_append(GTK_MENU(menu), item);
1493 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
1494 gtk_widget_show(item);
1495 gtk_menu_append(GTK_MENU(menu), item);
1497 GtkWidget *curmenu = menu;
1501 if (l->data == NULL && l->next != NULL) {
1503 curmenu = gtk_menu_new();
1504 item = gtk_menu_item_new_with_label(_("Others..."));
1505 gtk_widget_show(item);
1506 gtk_menu_append(GTK_MENU(curmenu), item);
1507 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), curmenu);
1510 } else if (count > MENUCOUNT) {
1512 item = gtk_menu_item_new_with_label(_("More..."));
1513 gtk_widget_show(item);
1514 gtk_menu_append(GTK_MENU(curmenu), item);
1515 curmenu = gtk_menu_new();
1516 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), curmenu);
1518 item = gtk_menu_item_new_with_label((unsigned char*)l->data);
1519 gtk_widget_show(item);
1520 gtk_menu_append(GTK_MENU(curmenu), item);
1521 gtk_signal_connect(GTK_OBJECT(item), "activate",
1522 GTK_SIGNAL_FUNC(replace_word), gtkpspell);
1523 if (count <= MENUCOUNT) {
1524 gtk_accel_group_add(accel, 'a' + count, 0,
1525 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE,
1526 GTK_OBJECT(item), "activate");
1527 gtk_accel_group_add(accel, 'a' + count,
1528 GDK_MOD1_MASK, GTK_ACCEL_LOCKED,
1529 GTK_OBJECT(item), "activate");
1534 } while ((l = l->next) != NULL);
1537 gtk_accel_group_attach(accel, GTK_OBJECT(menu));
1539 return GTK_MENU(menu);
1542 static GtkMenu *make_config_menu(GtkPspell *gtkpspell)
1544 GtkWidget *menu, *item, *submenu;
1545 gint ispell = gtkpspell->gtkpspeller->ispell;
1547 menu = gtk_menu_new();
1549 item = gtk_menu_item_new_with_label(_("Spell check all"));
1550 gtk_signal_connect(GTK_OBJECT(item),"activate",
1551 GTK_SIGNAL_FUNC(check_all_cb),
1553 gtk_widget_show(item);
1554 gtk_menu_append(GTK_MENU(menu), item);
1557 item = gtk_menu_item_new();
1558 gtk_widget_show(item);
1559 gtk_menu_append(GTK_MENU(menu),item);
1561 submenu = gtk_menu_new();
1562 item = gtk_menu_item_new_with_label(_("Change dictionary"));
1563 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
1564 gtk_widget_show(item);
1565 gtk_menu_append(GTK_MENU(menu), item);
1568 if (gtkpspellcheckers->dictionary_list == NULL)
1569 gtkpspell_get_dictionary_list(prefs_common.pspell_path, FALSE);
1571 GtkWidget * curmenu = submenu;
1575 tmp = gtkpspellcheckers->dictionary_list;
1577 for (tmp = gtkpspellcheckers->dictionary_list; tmp != NULL;
1578 tmp = g_slist_next(tmp)) {
1579 dict = (Dictionary *) tmp->data;
1580 item = gtk_check_menu_item_new_with_label(dict->dictname);
1581 gtk_object_set_data(GTK_OBJECT(item), "dict_name", dict);
1582 if (strcmp2(dict->fullname, gtkpspell->gtkpspeller->dictionary->fullname))
1583 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1585 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1586 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
1588 gtk_signal_connect(GTK_OBJECT(item), "activate",
1589 GTK_SIGNAL_FUNC(change_dict_cb),
1591 gtk_widget_show(item);
1592 gtk_menu_append(GTK_MENU(curmenu), item);
1596 if (count == MENUCOUNT) {
1599 newmenu = gtk_menu_new();
1600 item = gtk_menu_item_new_with_label(_("More..."));
1601 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1604 gtk_menu_append(GTK_MENU(curmenu), item);
1605 gtk_widget_show(item);
1612 item = gtk_menu_item_new();
1613 gtk_widget_show(item);
1614 gtk_menu_append(GTK_MENU(menu), item);
1616 item = gtk_check_menu_item_new_with_label(_("Fast Mode"));
1617 if (ispell || gtkpspell->gtkpspeller->sug_mode == PSPELL_FASTMODE)
1618 gtk_widget_set_sensitive(GTK_WIDGET(item),FALSE);
1619 if (!ispell && gtkpspell->gtkpspeller->sug_mode == PSPELL_FASTMODE)
1620 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),TRUE);
1622 gtk_signal_connect(GTK_OBJECT(item), "activate",
1623 GTK_SIGNAL_FUNC(set_sug_mode_cb),
1625 gtk_widget_show(item);
1626 gtk_menu_append(GTK_MENU(menu), item);
1628 item = gtk_check_menu_item_new_with_label(_("Normal Mode"));
1629 if (ispell || gtkpspell->gtkpspeller->sug_mode == PSPELL_NORMALMODE)
1630 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
1631 if (!ispell && gtkpspell->gtkpspeller->sug_mode == PSPELL_NORMALMODE)
1632 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1634 gtk_signal_connect(GTK_OBJECT(item), "activate",
1635 GTK_SIGNAL_FUNC(set_sug_mode_cb),
1637 gtk_widget_show(item);
1638 gtk_menu_append(GTK_MENU(menu),item);
1640 item = gtk_check_menu_item_new_with_label(_("Bad Spellers Mode"));
1641 if (ispell || gtkpspell->gtkpspeller->sug_mode == PSPELL_BADSPELLERMODE)
1642 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
1643 if (!ispell && gtkpspell->gtkpspeller->sug_mode == PSPELL_BADSPELLERMODE)
1644 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1646 gtk_signal_connect(GTK_OBJECT(item), "activate",
1647 GTK_SIGNAL_FUNC(set_sug_mode_cb),
1649 gtk_widget_show(item);
1650 gtk_menu_append(GTK_MENU(menu), item);
1652 return GTK_MENU(menu);
1655 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y, gpointer data)
1657 GtkPspell *gtkpspell = (GtkPspell *) data;
1658 gint xx = 0, yy = 0;
1661 GtkSText *text = GTK_STEXT(gtkpspell->gtktext);
1664 gdk_window_get_origin(GTK_WIDGET(gtkpspell->gtktext)->window, &xx, &yy);
1666 sx = gdk_screen_width();
1667 sy = gdk_screen_height();
1669 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
1674 *x = gtkpspell->gtktext->cursor_pos_x + xx +
1675 gdk_char_width(GTK_WIDGET(text)->style->font, ' ');
1676 *y = gtkpspell->gtktext->cursor_pos_y + yy;
1682 gdk_string_height((GTK_WIDGET(gtkpspell->gtktext))->style->font,
1683 gtkpspell->theword);
1687 /*************************************************************************/
1688 /* Menu call backs */
1690 static gboolean deactivate_menu_cb(GtkWidget *w, gpointer *data)
1692 GtkPspell *gtkpspell = (GtkPspell *) data;
1694 gtktext = gtkpspell->gtktext;
1696 gtk_stext_thaw(gtktext);
1697 gtk_stext_freeze(gtktext);
1698 gtk_editable_set_position(GTK_EDITABLE(gtktext),gtkpspell->orig_pos);
1699 gtk_stext_set_point(gtktext, gtkpspell->orig_pos);
1700 gtk_stext_thaw(gtktext);
1705 /* change_dict_cb() - Menu callback : change dict */
1706 static void change_dict_cb(GtkWidget *w, GtkPspell *gtkpspell)
1708 Dictionary *dict, *dict2;
1709 GtkPspeller *gtkpspeller;
1712 /* Dict is simply the menu label */
1714 dict = (Dictionary *) gtk_object_get_data(GTK_OBJECT(w), "dict_name");
1716 if (!strcmp2(dict->fullname, _("None")))
1719 sug_mode = gtkpspell->default_sug_mode;
1721 dict2 = dictionary_dup(dict);
1722 if (dict2->encoding)
1723 g_free(dict2->encoding);
1724 dict2->encoding = g_strdup(gtkpspell->gtkpspeller->dictionary->encoding);
1726 gtkpspeller = gtkpspeller_new(dict2);
1730 message = g_strdup_printf(_("The spell checker could not change dictionary.\n%s\n"),
1731 gtkpspellcheckers->error_message);
1733 gtkpspell_alert_dialog(message);
1736 gtkpspeller_delete(gtkpspell->gtkpspeller);
1737 gtkpspell->gtkpspeller = gtkpspeller;
1738 gtkpspell_set_sug_mode(gtkpspell, sug_mode);
1741 dictionary_delete(dict2);
1744 /******************************************************************************/
1745 /* Misc. helper functions */
1747 static void allocate_color(GtkPspell *gtkpspell)
1750 GdkColor *color = &(gtkpspell->highlight);
1751 gint rgbvalue = prefs_common.misspelled_col;
1753 gc = gtk_widget_get_colormap(GTK_WIDGET(gtkpspell->gtktext));
1755 if (gtkpspell->highlight.pixel)
1756 gdk_colormap_free_colors(gc, &(gtkpspell->highlight), 1);
1758 /* Shameless copy from Sylpheed gtkutils.c */
1760 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0) * 65535.0);
1761 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0) * 65535.0);
1762 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0) * 65535.0);
1764 gdk_colormap_alloc_color(gc, &(gtkpspell->highlight), FALSE, TRUE);
1767 static void change_color(GtkPspell * gtkpspell,
1774 g_return_if_fail(start < end);
1776 gtktext = gtkpspell->gtktext;
1778 gtk_stext_freeze(gtktext);
1779 newtext = gtk_editable_get_chars(GTK_EDITABLE(gtktext), start, end);
1781 gtk_stext_set_point(gtktext, start);
1782 gtk_stext_forward_delete(gtktext, end - start);
1784 gtk_stext_insert(gtktext, NULL, color, NULL, newtext, end - start);
1787 gtk_stext_thaw(gtktext);
1790 /* convert_to_pspell_encoding () - converts ISO-8859-* strings to iso8859-*
1791 * as needed by pspell. Returns an allocated string.
1794 static guchar *convert_to_pspell_encoding (const guchar *encoding)
1796 guchar * pspell_encoding;
1798 if (strstr2(encoding, "ISO-8859-")) {
1799 pspell_encoding = g_strdup_printf("iso8859%s", encoding+8);
1802 if (!strcmp2(encoding, "US-ASCII"))
1803 pspell_encoding = g_strdup("iso8859-1");
1805 pspell_encoding = g_strdup(encoding);
1807 return pspell_encoding;
1810 /* compare_dict () - compare 2 dict names */
1811 static gint compare_dict(Dictionary *a, Dictionary *b)
1813 guint aparts = 0, bparts = 0;
1816 for (i=0; i < strlen(a->dictname); i++)
1817 if (a->dictname[i] == '-')
1819 for (i=0; i < strlen(b->dictname); i++)
1820 if (b->dictname[i] == '-')
1823 if (aparts != bparts)
1824 return (aparts < bparts) ? -1 : +1;
1827 compare = strcmp2(a->dictname, b->dictname);
1829 compare = strcmp2(a->fullname, b->fullname);
1835 static void dictionary_delete(Dictionary *dict)
1837 g_free(dict->fullname);
1838 g_free(dict->encoding);
1842 static Dictionary *dictionary_dup(const Dictionary *dict)
1846 dict2 = g_new(Dictionary, 1);
1848 dict2->fullname = g_strdup(dict->fullname);
1849 dict2->dictname = dict->dictname - dict->fullname + dict2->fullname;
1850 dict2->encoding = g_strdup(dict->encoding);
1855 static void free_suggestions_list(GtkPspell *gtkpspell)
1859 for (list = gtkpspell->suggestions_list; list != NULL; list = list->next)
1863 gtkpspell->max_sug = -1;
1864 gtkpspell->suggestions_list = NULL;
1868 static void reset_theword_data(GtkPspell *gtkpspell)
1870 gtkpspell->start_pos = 0;
1871 gtkpspell->end_pos = 0;
1872 gtkpspell->theword[0] = 0;
1873 gtkpspell->max_sug = -1;
1874 gtkpspell->newword[0] = 0;
1876 free_suggestions_list(gtkpspell);
1879 static void free_checkers(gpointer elt, gpointer data)
1881 GtkPspeller *gtkpspeller = elt;
1883 g_return_if_fail(gtkpspeller);
1885 gtkpspeller_delete(gtkpspeller);
1888 static gint find_gtkpspeller(gconstpointer aa, gconstpointer bb)
1890 Dictionary *a = ((GtkPspeller *) aa)->dictionary;
1891 Dictionary *b = ((GtkPspeller *) bb)->dictionary;
1896 if (strcmp(a->fullname, b->fullname) == 0
1897 && a->encoding && b->encoding)
1898 return strcmp(a->encoding, b->encoding);
1903 static void gtkpspell_alert_dialog(gchar *message)
1907 GtkWidget *ok_button;
1909 dialog = gtk_dialog_new();
1910 label = gtk_label_new(message);
1911 ok_button = gtk_button_new_with_label(_("OK"));
1913 gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE);
1914 gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_MOUSE);
1915 GTK_WIDGET_SET_FLAGS(ok_button, GTK_CAN_DEFAULT);
1916 gtk_signal_connect_object(GTK_OBJECT(ok_button), "clicked",
1917 GTK_SIGNAL_FUNC(gtk_widget_destroy),
1918 GTK_OBJECT(dialog));
1920 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), ok_button);
1922 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
1923 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 8);
1924 gtk_widget_grab_default(ok_button);
1925 gtk_widget_grab_focus(ok_button);
1926 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1927 gtk_widget_show_all(dialog);