faq, man, manual moved to doc/; fix automake warnings
[claws.git] / src / gtk / gtkaspell.c
blob8fa21c85e22d07ca271d136ee5546c42b87bb37c
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
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #ifdef USE_ASPELL
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <sys/types.h>
37 #include <sys/wait.h>
38 #include <signal.h>
39 #include <ctype.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <sys/time.h>
43 #include <fcntl.h>
44 #include <time.h>
45 #include <dirent.h>
47 #include <glib.h>
49 #include <gtk/gtk.h>
50 #include <gdk/gdk.h>
51 #include <gtk/gtkoptionmenu.h>
52 #include <gtk/gtkmenu.h>
53 #include <gtk/gtkmenuitem.h>
54 #include <gdk/gdkkeysyms.h>
56 #include <aspell.h>
58 #include "intl.h"
59 #include "gtkstext.h"
60 #include "utils.h"
62 #include "gtkaspell.h"
63 #define ASPELL_FASTMODE 1
64 #define ASPELL_NORMALMODE 2
65 #define ASPELL_BADSPELLERMODE 3
67 #define GTKASPELLWORDSIZE 1024
69 /* size of the text buffer used in various word-processing routines. */
70 #define BUFSIZE 1024
72 /* number of suggestions to display on each menu. */
73 #define MENUCOUNT 15
75 /* 'config' must be defined as a 'AspellConfig *' */
76 #define RETURN_FALSE_IF_CONFIG_ERROR() \
77 { \
78 if (aspell_config_error_number(config) != 0) { \
79 gtkaspellcheckers->error_message = g_strdup(aspell_config_error_message(config)); \
80 return FALSE; \
81 } \
84 #define CONFIG_REPLACE_RETURN_FALSE_IF_FAIL(option, value) { \
85 aspell_config_replace(config, option, value); \
86 RETURN_FALSE_IF_CONFIG_ERROR(); \
89 typedef struct _GtkAspellCheckers {
90 GSList *checkers;
91 GSList *dictionary_list;
92 gchar *error_message;
93 } GtkAspellCheckers;
95 typedef struct _Dictionary {
96 gchar *fullname;
97 gchar *dictname;
98 gchar *encoding;
99 } Dictionary;
101 typedef struct _GtkAspeller {
102 Dictionary *dictionary;
103 gint sug_mode;
104 AspellConfig *config;
105 AspellSpeller *checker;
106 } GtkAspeller;
108 typedef void (*ContCheckFunc) (gpointer *gtkaspell);
110 struct _GtkAspell
112 GtkAspeller *gtkaspeller;
113 GtkAspeller *alternate_speller;
114 gchar *dictionary_path;
115 gchar theword[GTKASPELLWORDSIZE];
116 gint start_pos;
117 gint end_pos;
118 gint orig_pos;
119 gint end_check_pos;
120 gboolean misspelled;
121 gboolean check_while_typing;
122 gboolean use_alternate;
124 ContCheckFunc continue_check;
126 GtkWidget *config_menu;
127 GtkWidget *popup_config_menu;
128 GtkWidget *sug_menu;
129 GtkWidget *replace_entry;
131 gint default_sug_mode;
132 gint max_sug;
133 GList *suggestions_list;
135 GtkSText *gtktext;
136 GdkColor highlight;
139 typedef AspellConfig GtkAspellConfig;
141 /******************************************************************************/
143 static GtkAspellCheckers *gtkaspellcheckers;
145 /* Error message storage */
146 static void gtkaspell_checkers_error_message (gchar *message);
148 /* Callbacks */
149 static void entry_insert_cb (GtkSText *gtktext,
150 gchar *newtext,
151 guint len,
152 guint *ppos,
153 GtkAspell *gtkaspell);
154 static void entry_delete_cb (GtkSText *gtktext,
155 gint start,
156 gint end,
157 GtkAspell *gtkaspell);
158 static gint button_press_intercept_cb (GtkSText *gtktext,
159 GdkEvent *e,
160 GtkAspell *gtkaspell);
162 /* Checker creation */
163 static GtkAspeller* gtkaspeller_new (Dictionary *dict);
164 static GtkAspeller* gtkaspeller_real_new (Dictionary *dict);
165 static GtkAspeller* gtkaspeller_delete (GtkAspeller *gtkaspeller);
166 static GtkAspeller* gtkaspeller_real_delete (GtkAspeller *gtkaspeller);
168 /* Checker configuration */
169 static gint set_dictionary (AspellConfig *config,
170 Dictionary *dict);
171 static void set_sug_mode_cb (GtkMenuItem *w,
172 GtkAspell *gtkaspell);
173 static void set_real_sug_mode (GtkAspell *gtkaspell,
174 const char *themode);
176 /* Checker actions */
177 static gboolean check_at (GtkAspell *gtkaspell,
178 int from_pos);
179 static gboolean check_next_prev (GtkAspell *gtkaspell,
180 gboolean forward);
181 static GList* misspelled_suggest (GtkAspell *gtkaspell,
182 guchar *word);
183 static void add_word_to_session_cb (GtkWidget *w,
184 gpointer data);
185 static void add_word_to_personal_cb (GtkWidget *w,
186 gpointer data);
187 static void replace_with_create_dialog_cb (GtkWidget *w,
188 gpointer data);
189 static void replace_with_supplied_word_cb (GtkWidget *w,
190 GtkAspell *gtkaspell);
191 static void replace_word_cb (GtkWidget *w,
192 gpointer data);
193 static void replace_real_word (GtkAspell *gtkaspell,
194 gchar *newword);
195 static void check_with_alternate_cb (GtkWidget *w,
196 gpointer data);
197 static void use_alternate_dict (GtkAspell *gtkaspell);
198 static void toggle_check_while_typing_cb (GtkWidget *w,
199 gpointer data);
201 /* Menu creation */
202 static void popup_menu (GtkAspell *gtkaspell,
203 GdkEventButton *eb);
204 static GtkMenu* make_sug_menu (GtkAspell *gtkaspell);
205 static void populate_submenu (GtkAspell *gtkaspell,
206 GtkWidget *menu);
207 static GtkMenu* make_config_menu (GtkAspell *gtkaspell);
208 static void set_menu_pos (GtkMenu *menu,
209 gint *x,
210 gint *y,
211 gpointer data);
212 /* Other menu callbacks */
213 static gboolean cancel_menu_cb (GtkMenuShell *w,
214 gpointer data);
215 static void change_dict_cb (GtkWidget *w,
216 GtkAspell *gtkaspell);
217 static void switch_to_alternate_cb (GtkWidget *w,
218 gpointer data);
220 /* Misc. helper functions */
221 static void set_point_continue (GtkAspell *gtkaspell);
222 static void continue_check (gpointer *gtkaspell);
223 static gboolean iswordsep (unsigned char c);
224 static guchar get_text_index_whar (GtkAspell *gtkaspell,
225 int pos);
226 static gboolean get_word_from_pos (GtkAspell *gtkaspell,
227 gint pos,
228 unsigned char* buf,
229 gint buflen,
230 gint *pstart,
231 gint *pend);
232 static void allocate_color (GtkAspell *gtkaspell,
233 gint rgbvalue);
234 static void change_color (GtkAspell *gtkaspell,
235 gint start,
236 gint end,
237 gchar *newtext,
238 GdkColor *color);
239 static guchar* convert_to_aspell_encoding (const guchar *encoding);
240 static gint compare_dict (Dictionary *a,
241 Dictionary *b);
242 static void dictionary_delete (Dictionary *dict);
243 static Dictionary * dictionary_dup (const Dictionary *dict);
244 static void free_suggestions_list (GtkAspell *gtkaspell);
245 static void reset_theword_data (GtkAspell *gtkaspell);
246 static void free_checkers (gpointer elt,
247 gpointer data);
248 static gint find_gtkaspeller (gconstpointer aa,
249 gconstpointer bb);
250 static void gtkaspell_alert_dialog (gchar *message);
251 /* gtkspellconfig - only one config per session */
252 GtkAspellConfig * gtkaspellconfig;
254 /******************************************************************************/
256 void gtkaspell_checkers_init(void)
258 gtkaspellcheckers = g_new(GtkAspellCheckers, 1);
259 gtkaspellcheckers->checkers = NULL;
260 gtkaspellcheckers->dictionary_list = NULL;
261 gtkaspellcheckers->error_message = NULL;
264 void gtkaspell_checkers_quit(void)
266 GSList *checkers;
267 GSList *dict_list;
269 if (gtkaspellcheckers == NULL)
270 return;
272 if ((checkers = gtkaspellcheckers->checkers)) {
273 debug_print("Aspell: number of running checkers to delete %d\n",
274 g_slist_length(checkers));
276 g_slist_foreach(checkers, free_checkers, NULL);
277 g_slist_free(checkers);
280 if ((dict_list = gtkaspellcheckers->dictionary_list)) {
281 debug_print("Aspell: number of dictionaries to delete %d\n",
282 g_slist_length(dict_list));
284 gtkaspell_free_dictionary_list(dict_list);
285 gtkaspellcheckers->dictionary_list = NULL;
288 g_free(gtkaspellcheckers->error_message);
290 return;
293 static void gtkaspell_checkers_error_message (gchar *message)
295 gchar *tmp;
296 if (gtkaspellcheckers->error_message) {
297 tmp = g_strdup_printf("%s\n%s",
298 gtkaspellcheckers->error_message, message);
299 g_free(message);
300 g_free(gtkaspellcheckers->error_message);
301 gtkaspellcheckers->error_message = tmp;
302 } else
303 gtkaspellcheckers->error_message = message;
306 const char *gtkaspell_checkers_strerror(void)
308 g_return_val_if_fail(gtkaspellcheckers, "");
309 return gtkaspellcheckers->error_message;
312 void gtkaspell_checkers_reset_error(void)
314 g_return_if_fail(gtkaspellcheckers);
316 g_free(gtkaspellcheckers->error_message);
318 gtkaspellcheckers->error_message = NULL;
321 GtkAspell *gtkaspell_new(const gchar *dictionary_path,
322 const gchar *dictionary,
323 const gchar *encoding,
324 gint misspelled_color,
325 gboolean check_while_typing,
326 gboolean use_alternate,
327 GtkSText *gtktext)
329 Dictionary *dict;
330 GtkAspell *gtkaspell;
331 GtkAspeller *gtkaspeller;
333 g_return_val_if_fail(gtktext, NULL);
335 dict = g_new0(Dictionary, 1);
336 dict->fullname = g_strdup(dictionary);
337 dict->encoding = g_strdup(encoding);
339 gtkaspeller = gtkaspeller_new(dict);
340 dictionary_delete(dict);
342 if (!gtkaspeller)
343 return NULL;
345 gtkaspell = g_new0(GtkAspell, 1);
347 gtkaspell->dictionary_path = g_strdup(dictionary_path);
349 gtkaspell->gtkaspeller = gtkaspeller;
350 gtkaspell->alternate_speller = NULL;
351 gtkaspell->theword[0] = 0x00;
352 gtkaspell->start_pos = 0;
353 gtkaspell->end_pos = 0;
354 gtkaspell->orig_pos = -1;
355 gtkaspell->end_check_pos = -1;
356 gtkaspell->misspelled = -1;
357 gtkaspell->check_while_typing = check_while_typing;
358 gtkaspell->continue_check = NULL;
359 gtkaspell->config_menu = NULL;
360 gtkaspell->popup_config_menu = NULL;
361 gtkaspell->sug_menu = NULL;
362 gtkaspell->replace_entry = NULL;
363 gtkaspell->gtktext = gtktext;
364 gtkaspell->default_sug_mode = ASPELL_FASTMODE;
365 gtkaspell->max_sug = -1;
366 gtkaspell->suggestions_list = NULL;
367 gtkaspell->use_alternate = use_alternate;
369 allocate_color(gtkaspell, misspelled_color);
371 gtk_signal_connect_after(GTK_OBJECT(gtktext), "insert-text",
372 GTK_SIGNAL_FUNC(entry_insert_cb), gtkaspell);
373 gtk_signal_connect_after(GTK_OBJECT(gtktext), "delete-text",
374 GTK_SIGNAL_FUNC(entry_delete_cb), gtkaspell);
375 gtk_signal_connect(GTK_OBJECT(gtktext), "button-press-event",
376 GTK_SIGNAL_FUNC(button_press_intercept_cb), gtkaspell);
378 debug_print("Aspell: created gtkaspell %0x\n", (guint) gtkaspell);
380 return gtkaspell;
383 void gtkaspell_delete(GtkAspell * gtkaspell)
385 GtkSText *gtktext = gtkaspell->gtktext;
387 gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
388 GTK_SIGNAL_FUNC(entry_insert_cb),
389 gtkaspell);
390 gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
391 GTK_SIGNAL_FUNC(entry_delete_cb),
392 gtkaspell);
393 gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
394 GTK_SIGNAL_FUNC(button_press_intercept_cb),
395 gtkaspell);
397 gtkaspell_uncheck_all(gtkaspell);
399 gtkaspeller_delete(gtkaspell->gtkaspeller);
401 if (gtkaspell->use_alternate && gtkaspell->alternate_speller)
402 gtkaspeller_delete(gtkaspell->alternate_speller);
404 if (gtkaspell->sug_menu)
405 gtk_widget_destroy(gtkaspell->sug_menu);
407 if (gtkaspell->popup_config_menu)
408 gtk_widget_destroy(gtkaspell->popup_config_menu);
410 if (gtkaspell->config_menu)
411 gtk_widget_destroy(gtkaspell->config_menu);
413 if (gtkaspell->suggestions_list)
414 free_suggestions_list(gtkaspell);
416 g_free((gchar *)gtkaspell->dictionary_path);
418 debug_print("Aspell: deleting gtkaspell %0x\n", (guint) gtkaspell);
420 g_free(gtkaspell);
422 gtkaspell = NULL;
425 static void entry_insert_cb(GtkSText *gtktext,
426 gchar *newtext,
427 guint len,
428 guint *ppos,
429 GtkAspell *gtkaspell)
431 size_t wlen;
433 g_return_if_fail(gtkaspell->gtkaspeller->checker);
435 if (!gtkaspell->check_while_typing)
436 return;
438 /* We must insert ourselves the character so the
439 * color of the inserted character is the default color.
440 * Never mess with set_insertion when frozen.
443 gtk_stext_freeze(gtktext);
444 if (MB_CUR_MAX > 1) {
445 gchar *str;
446 Xstrndup_a(str, newtext, len, return);
447 wlen = mbstowcs(NULL, str, 0);
448 if (wlen < 0)
449 return;
450 } else
451 wlen = len;
453 gtk_stext_backward_delete(GTK_STEXT(gtktext), wlen);
454 gtk_stext_insert(GTK_STEXT(gtktext), NULL, NULL, NULL, newtext, len);
455 *ppos = gtk_stext_get_point(GTK_STEXT(gtktext));
457 if (iswordsep(newtext[0])) {
458 /* did we just end a word? */
459 if (*ppos >= 2)
460 check_at(gtkaspell, *ppos - 2);
462 /* did we just split a word? */
463 if (*ppos < gtk_stext_get_length(gtktext))
464 check_at(gtkaspell, *ppos + 1);
465 } else {
466 /* check as they type, *except* if they're typing at the end (the most
467 * common case).
469 if (*ppos < gtk_stext_get_length(gtktext) &&
470 !iswordsep(get_text_index_whar(gtkaspell, *ppos))) {
471 check_at(gtkaspell, *ppos - 1);
475 gtk_stext_thaw(gtktext);
476 gtk_editable_set_position(GTK_EDITABLE(gtktext), *ppos);
479 static void entry_delete_cb(GtkSText *gtktext,
480 gint start,
481 gint end,
482 GtkAspell *gtkaspell)
484 int origpos;
486 g_return_if_fail(gtkaspell->gtkaspeller->checker);
488 if (!gtkaspell->check_while_typing)
489 return;
491 origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
492 if (start) {
493 check_at(gtkaspell, start - 1);
494 check_at(gtkaspell, start);
497 gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
498 gtk_stext_set_point(gtktext, origpos);
499 /* this is to *UNDO* the selection, in case they were holding shift
500 * while hitting backspace. */
501 gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos);
504 /* ok, this is pretty wacky:
505 * we need to let the right-mouse-click go through, so it moves the cursor,
506 * but we *can't* let it go through, because GtkText interprets rightclicks as
507 * weird selection modifiers.
509 * so what do we do? forge rightclicks as leftclicks, then popup the menu.
510 * HACK HACK HACK.
512 static gint button_press_intercept_cb(GtkSText *gtktext, GdkEvent *e, GtkAspell *gtkaspell)
514 GdkEventButton *eb;
515 gboolean retval;
517 g_return_val_if_fail(gtkaspell->gtkaspeller->checker, FALSE);
519 if (e->type != GDK_BUTTON_PRESS)
520 return FALSE;
521 eb = (GdkEventButton*) e;
523 if (eb->button != 3)
524 return FALSE;
526 /* forge the leftclick */
527 eb->button = 1;
529 gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
530 GTK_SIGNAL_FUNC(button_press_intercept_cb),
531 gtkaspell);
532 gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "button-press-event",
533 e, &retval);
534 gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
535 GTK_SIGNAL_FUNC(button_press_intercept_cb),
536 gtkaspell);
537 gtk_signal_emit_stop_by_name(GTK_OBJECT(gtktext), "button-press-event");
539 /* now do the menu wackiness */
540 popup_menu(gtkaspell, eb);
541 gtk_grab_remove(GTK_WIDGET(gtktext));
542 return TRUE;
545 /* Checker creation */
546 static GtkAspeller *gtkaspeller_new(Dictionary *dictionary)
548 GSList *exist;
549 GtkAspeller *gtkaspeller = NULL;
550 GtkAspeller *tmp;
551 Dictionary *dict;
553 g_return_val_if_fail(gtkaspellcheckers, NULL);
555 g_return_val_if_fail(dictionary, NULL);
557 if (dictionary->fullname == NULL)
558 gtkaspell_checkers_error_message(g_strdup(_("No dictionary selected.")));
560 g_return_val_if_fail(dictionary->fullname, NULL);
562 if (dictionary->dictname == NULL) {
563 gchar *tmp;
565 tmp = strrchr(dictionary->fullname, G_DIR_SEPARATOR);
567 if (tmp == NULL)
568 dictionary->dictname = dictionary->fullname;
569 else
570 dictionary->dictname = tmp + 1;
573 dict = dictionary_dup(dictionary);
575 tmp = g_new0(GtkAspeller, 1);
576 tmp->dictionary = dict;
578 exist = g_slist_find_custom(gtkaspellcheckers->checkers, tmp,
579 find_gtkaspeller);
581 g_free(tmp);
583 if ((gtkaspeller = gtkaspeller_real_new(dict)) != NULL) {
584 gtkaspellcheckers->checkers = g_slist_append(
585 gtkaspellcheckers->checkers,
586 gtkaspeller);
588 debug_print("Aspell: Created a new gtkaspeller %0x\n",
589 (gint) gtkaspeller);
590 } else {
591 dictionary_delete(dict);
593 debug_print("Aspell: Could not create spell checker.\n");
596 debug_print("Aspell: number of existing checkers %d\n",
597 g_slist_length(gtkaspellcheckers->checkers));
599 return gtkaspeller;
602 static GtkAspeller *gtkaspeller_real_new(Dictionary *dict)
604 GtkAspeller *gtkaspeller;
605 AspellConfig *config;
606 AspellCanHaveError *ret;
608 g_return_val_if_fail(gtkaspellcheckers, NULL);
609 g_return_val_if_fail(dict, NULL);
611 gtkaspeller = g_new(GtkAspeller, 1);
613 gtkaspeller->dictionary = dict;
614 gtkaspeller->sug_mode = ASPELL_FASTMODE;
616 config = new_aspell_config();
618 if (!set_dictionary(config, dict))
619 return NULL;
621 ret = new_aspell_speller(config);
622 delete_aspell_config(config);
624 if (aspell_error_number(ret) != 0) {
625 gtkaspellcheckers->error_message = g_strdup(aspell_error_message(ret));
627 delete_aspell_can_have_error(ret);
629 return NULL;
632 gtkaspeller->checker = to_aspell_speller(ret);
633 gtkaspeller->config = aspell_speller_config(gtkaspeller->checker);
635 return gtkaspeller;
638 static GtkAspeller *gtkaspeller_delete(GtkAspeller *gtkaspeller)
640 g_return_val_if_fail(gtkaspellcheckers, NULL);
642 gtkaspellcheckers->checkers =
643 g_slist_remove(gtkaspellcheckers->checkers,
644 gtkaspeller);
646 debug_print("Aspell: Deleting gtkaspeller %0x.\n",
647 (gint) gtkaspeller);
649 gtkaspeller_real_delete(gtkaspeller);
651 debug_print("Aspell: number of existing checkers %d\n",
652 g_slist_length(gtkaspellcheckers->checkers));
654 return gtkaspeller;
657 static GtkAspeller *gtkaspeller_real_delete(GtkAspeller *gtkaspeller)
659 g_return_val_if_fail(gtkaspeller, NULL);
660 g_return_val_if_fail(gtkaspeller->checker, NULL);
662 aspell_speller_save_all_word_lists(gtkaspeller->checker);
664 delete_aspell_speller(gtkaspeller->checker);
666 dictionary_delete(gtkaspeller->dictionary);
668 debug_print("Aspell: gtkaspeller %0x deleted.\n",
669 (gint) gtkaspeller);
671 g_free(gtkaspeller);
673 return NULL;
676 /*****************************************************************************/
677 /* Checker configuration */
679 static gboolean set_dictionary(AspellConfig *config, Dictionary *dict)
681 gchar *language = NULL;
682 gchar *jargon = NULL;
683 gchar *size = NULL;
684 gchar buf[BUFSIZE];
686 g_return_val_if_fail(config, FALSE);
687 g_return_val_if_fail(dict, FALSE);
689 strncpy(buf, dict->fullname, BUFSIZE-1);
690 buf[BUFSIZE-1] = 0x00;
692 buf[dict->dictname - dict->fullname] = 0x00;
694 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("dict-dir", buf);
695 debug_print("Aspell: looking for dictionaries in path %s.\n", buf);
697 strncpy(buf, dict->dictname, BUFSIZE-1);
698 language = buf;
700 if ((size = strrchr(buf, '-')) && isdigit((int) size[1]))
701 *size++ = 0x00;
702 else
703 size = NULL;
705 if ((jargon = strchr(language, '-')) != NULL)
706 *jargon++ = 0x00;
708 if (size != NULL && jargon == size)
709 jargon = NULL;
711 debug_print("Aspell: language: %s, jargon: %s, size: %s\n",
712 language, jargon, size);
714 if (language)
715 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("lang", language);
716 if (jargon)
717 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("jargon", jargon);
718 if (size)
719 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("size", size);
720 if (dict->encoding) {
721 gchar *aspell_enc;
723 aspell_enc = convert_to_aspell_encoding (dict->encoding);
724 aspell_config_replace(config, "encoding", (const char *) aspell_enc);
725 g_free(aspell_enc);
727 RETURN_FALSE_IF_CONFIG_ERROR();
730 return TRUE;
733 guchar *gtkaspell_get_dict(GtkAspell *gtkaspell)
736 g_return_val_if_fail(gtkaspell->gtkaspeller->config, NULL);
737 g_return_val_if_fail(gtkaspell->gtkaspeller->dictionary, NULL);
739 return g_strdup(gtkaspell->gtkaspeller->dictionary->dictname);
742 guchar *gtkaspell_get_path(GtkAspell *gtkaspell)
744 guchar *path;
745 Dictionary *dict;
747 g_return_val_if_fail(gtkaspell->gtkaspeller->config, NULL);
748 g_return_val_if_fail(gtkaspell->gtkaspeller->dictionary, NULL);
750 dict = gtkaspell->gtkaspeller->dictionary;
751 path = g_strndup(dict->fullname, dict->dictname - dict->fullname);
753 return path;
756 /* set_sug_mode_cb() - Menu callback: Set the suggestion mode */
757 static void set_sug_mode_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
759 char *themode;
761 gtk_label_get(GTK_LABEL(GTK_BIN(w)->child), (gchar **) &themode);
763 set_real_sug_mode(gtkaspell, themode);
765 if (gtkaspell->config_menu)
766 populate_submenu(gtkaspell, gtkaspell->config_menu);
769 static void set_real_sug_mode(GtkAspell *gtkaspell, const char *themode)
771 gint result;
772 gint mode = ASPELL_FASTMODE;
773 g_return_if_fail(gtkaspell);
774 g_return_if_fail(gtkaspell->gtkaspeller);
775 g_return_if_fail(themode);
777 if (!strcmp(themode,_("Normal Mode")))
778 mode = ASPELL_NORMALMODE;
779 else if (!strcmp( themode,_("Bad Spellers Mode")))
780 mode = ASPELL_BADSPELLERMODE;
782 result = gtkaspell_set_sug_mode(gtkaspell, mode);
784 if(!result) {
785 debug_print("Aspell: error while changing suggestion mode:%s\n",
786 gtkaspellcheckers->error_message);
787 gtkaspell_checkers_reset_error();
791 /* gtkaspell_set_sug_mode() - Set the suggestion mode */
792 gboolean gtkaspell_set_sug_mode(GtkAspell *gtkaspell, gint themode)
794 AspellConfig *config;
796 g_return_val_if_fail(gtkaspell, FALSE);
797 g_return_val_if_fail(gtkaspell->gtkaspeller, FALSE);
798 g_return_val_if_fail(gtkaspell->gtkaspeller->config, FALSE);
800 debug_print("Aspell: setting sug mode of gtkaspeller %0x to %d\n",
801 (guint) gtkaspell->gtkaspeller, themode);
803 config = gtkaspell->gtkaspeller->config;
805 switch (themode) {
806 case ASPELL_FASTMODE:
807 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "fast");
808 break;
809 case ASPELL_NORMALMODE:
810 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "normal");
811 break;
812 case ASPELL_BADSPELLERMODE:
813 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode",
814 "bad-spellers");
815 break;
816 default:
817 gtkaspellcheckers->error_message =
818 g_strdup(_("Unknown suggestion mode."));
819 return FALSE;
822 gtkaspell->gtkaspeller->sug_mode = themode;
823 gtkaspell->default_sug_mode = themode;
825 return TRUE;
828 /* misspelled_suggest() - Create a suggestion list for word */
829 static GList *misspelled_suggest(GtkAspell *gtkaspell, guchar *word)
831 const guchar *newword;
832 GList *list = NULL;
833 const AspellWordList *suggestions;
834 AspellStringEnumeration *elements;
836 g_return_val_if_fail(word, NULL);
838 if (!aspell_speller_check(gtkaspell->gtkaspeller->checker, word, -1)) {
839 free_suggestions_list(gtkaspell);
841 suggestions = aspell_speller_suggest(gtkaspell->gtkaspeller->checker,
842 (const char *)word, -1);
843 elements = aspell_word_list_elements(suggestions);
844 list = g_list_append(list, g_strdup(word));
846 while ((newword = aspell_string_enumeration_next(elements)) != NULL)
847 list = g_list_append(list, g_strdup(newword));
849 gtkaspell->max_sug = g_list_length(list) - 1;
850 gtkaspell->suggestions_list = list;
852 return list;
855 free_suggestions_list(gtkaspell);
857 return NULL;
860 /* misspelled_test() - Just test if word is correctly spelled */
861 static int misspelled_test(GtkAspell *gtkaspell, unsigned char *word)
863 return aspell_speller_check(gtkaspell->gtkaspeller->checker, word, -1) ? 0 : 1;
867 static gboolean iswordsep(unsigned char c)
869 return !isalpha(c) && c != '\'';
872 static guchar get_text_index_whar(GtkAspell *gtkaspell, int pos)
874 guchar a;
875 gchar *text;
877 text = gtk_editable_get_chars(GTK_EDITABLE(gtkaspell->gtktext), pos,
878 pos + 1);
879 if (text == NULL)
880 return 0;
882 a = (guchar) *text;
884 g_free(text);
886 return a;
889 /* get_word_from_pos () - return the word pointed to. */
890 /* Handles correctly the quotes. */
891 static gboolean get_word_from_pos(GtkAspell *gtkaspell, gint pos,
892 unsigned char* buf, gint buflen,
893 gint *pstart, gint *pend)
896 /* TODO : when correcting a word into quotes, change the color of */
897 /* the quotes too, as may be they were highlighted before. To do */
898 /* this, we can use two others pointers that points to the whole */
899 /* word including quotes. */
901 gint start;
902 gint end;
904 guchar c;
905 GtkSText *gtktext;
907 gtktext = gtkaspell->gtktext;
908 if (iswordsep(get_text_index_whar(gtkaspell, pos)))
909 return FALSE;
911 /* The apostrophe character is somtimes used for quotes
912 * So include it in the word only if it is not surrounded
913 * by other characters.
916 for (start = pos; start >= 0; --start) {
917 c = get_text_index_whar(gtkaspell, start);
918 if (c == '\'') {
919 if (start > 0) {
920 if (!isalpha(get_text_index_whar(gtkaspell,
921 start - 1))) {
922 /* start_quote = TRUE; */
923 break;
926 else {
927 /* start_quote = TRUE; */
928 break;
931 else if (!isalpha(c))
932 break;
935 start++;
937 for (end = pos; end < gtk_stext_get_length(gtktext); end++) {
938 c = get_text_index_whar(gtkaspell, end);
939 if (c == '\'') {
940 if (end < gtk_stext_get_length(gtktext)) {
941 if (!isalpha(get_text_index_whar(gtkaspell,
942 end + 1))) {
943 /* end_quote = TRUE; */
944 break;
947 else {
948 /* end_quote = TRUE; */
949 break;
952 else if(!isalpha(c))
953 break;
956 if (pstart)
957 *pstart = start;
958 if (pend)
959 *pend = end;
961 if (buf) {
962 if (end - start < buflen) {
963 for (pos = start; pos < end; pos++)
964 buf[pos - start] =
965 get_text_index_whar(gtkaspell, pos);
966 buf[pos - start] = 0;
967 } else
968 return FALSE;
971 return TRUE;
974 static gboolean check_at(GtkAspell *gtkaspell, gint from_pos)
976 gint start, end;
977 unsigned char buf[GTKASPELLWORDSIZE];
978 GtkSText *gtktext;
980 g_return_val_if_fail(from_pos >= 0, FALSE);
982 gtktext = gtkaspell->gtktext;
984 if (!get_word_from_pos(gtkaspell, from_pos, buf, sizeof(buf),
985 &start, &end))
986 return FALSE;
988 if (misspelled_test(gtkaspell, buf)) {
989 strncpy(gtkaspell->theword, buf, GTKASPELLWORDSIZE - 1);
990 gtkaspell->theword[GTKASPELLWORDSIZE - 1] = 0;
991 gtkaspell->start_pos = start;
992 gtkaspell->end_pos = end;
993 free_suggestions_list(gtkaspell);
995 change_color(gtkaspell, start, end, buf, &(gtkaspell->highlight));
996 return TRUE;
997 } else {
998 change_color(gtkaspell, start, end, buf, NULL);
999 return FALSE;
1003 static gboolean check_next_prev(GtkAspell *gtkaspell, gboolean forward)
1005 gint pos;
1006 gint minpos;
1007 gint maxpos;
1008 gint direc = -1;
1009 gboolean misspelled;
1011 minpos = 0;
1012 maxpos = gtkaspell->end_check_pos;
1014 if (forward) {
1015 minpos = -1;
1016 direc = 1;
1017 maxpos--;
1020 pos = gtk_editable_get_position(GTK_EDITABLE(gtkaspell->gtktext));
1021 gtkaspell->orig_pos = pos;
1022 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1023 pos > minpos && pos <= maxpos)
1024 pos += direc;
1025 while (!(misspelled = check_at(gtkaspell, pos)) &&
1026 pos > minpos && pos <= maxpos) {
1028 while (!iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1029 pos > minpos && pos <= maxpos)
1030 pos += direc;
1032 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1033 pos > minpos && pos <= maxpos)
1034 pos += direc;
1036 if (misspelled) {
1037 misspelled_suggest(gtkaspell, gtkaspell->theword);
1039 if (forward)
1040 gtkaspell->orig_pos = gtkaspell->end_pos;
1042 gtk_stext_set_point(GTK_STEXT(gtkaspell->gtktext),
1043 gtkaspell->end_pos);
1044 gtk_editable_set_position(GTK_EDITABLE(gtkaspell->gtktext),
1045 gtkaspell->end_pos);
1046 gtk_menu_popup(make_sug_menu(gtkaspell), NULL, NULL,
1047 set_menu_pos, gtkaspell, 0, GDK_CURRENT_TIME);
1048 } else {
1049 reset_theword_data(gtkaspell);
1051 gtkaspell_alert_dialog(_("No misspelled word found."));
1052 gtk_stext_set_point(GTK_STEXT(gtkaspell->gtktext),
1053 gtkaspell->orig_pos);
1054 gtk_editable_set_position(GTK_EDITABLE(gtkaspell->gtktext),
1055 gtkaspell->orig_pos);
1059 return misspelled;
1062 void gtkaspell_check_backwards(GtkAspell *gtkaspell)
1064 gtkaspell->continue_check = NULL;
1065 gtkaspell->end_check_pos =
1066 gtk_stext_get_length(GTK_STEXT(gtkaspell->gtktext));
1067 check_next_prev(gtkaspell, FALSE);
1070 void gtkaspell_check_forwards_go(GtkAspell *gtkaspell)
1073 gtkaspell->continue_check = NULL;
1074 gtkaspell->end_check_pos
1075 = gtk_stext_get_length(GTK_STEXT(gtkaspell->gtktext));
1076 check_next_prev(gtkaspell, TRUE);
1079 void gtkaspell_check_all(GtkAspell *gtkaspell)
1081 GtkWidget *gtktext;
1082 gint start, end;
1084 g_return_if_fail(gtkaspell);
1085 g_return_if_fail(gtkaspell->gtktext);
1087 gtktext = (GtkWidget *) gtkaspell->gtktext;
1089 start = 0;
1090 end = gtk_stext_get_length(GTK_STEXT(gtktext));
1092 if (GTK_EDITABLE(gtktext)->has_selection) {
1093 start = GTK_EDITABLE(gtktext)->selection_start_pos;
1094 end = GTK_EDITABLE(gtktext)->selection_end_pos;
1097 if (start > end) {
1098 gint tmp;
1100 tmp = start;
1101 start = end;
1102 end = tmp;
1106 gtk_editable_set_position(GTK_EDITABLE(gtktext), start);
1107 gtk_stext_set_point(GTK_STEXT(gtktext), start);
1109 gtkaspell->continue_check = continue_check;
1110 gtkaspell->end_check_pos = end;
1112 gtkaspell->misspelled = check_next_prev(gtkaspell, TRUE);
1116 static void continue_check(gpointer *data)
1118 GtkAspell *gtkaspell = (GtkAspell *) data;
1119 gint pos = gtk_editable_get_position(GTK_EDITABLE(gtkaspell->gtktext));
1120 if (pos < gtkaspell->end_check_pos && gtkaspell->misspelled)
1121 gtkaspell->misspelled = check_next_prev(gtkaspell, TRUE);
1122 else
1123 gtkaspell->continue_check = NULL;
1127 void gtkaspell_highlight_all(GtkAspell *gtkaspell)
1129 guint origpos;
1130 guint pos = 0;
1131 guint len;
1132 GtkSText *gtktext;
1133 gfloat adj_value;
1135 g_return_if_fail(gtkaspell->gtkaspeller->checker);
1137 gtktext = gtkaspell->gtktext;
1139 adj_value = gtktext->vadj->value;
1141 len = gtk_stext_get_length(gtktext);
1143 origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
1145 while (pos < len) {
1146 while (pos < len &&
1147 iswordsep(get_text_index_whar(gtkaspell, pos)))
1148 pos++;
1149 while (pos < len &&
1150 !iswordsep(get_text_index_whar(gtkaspell, pos)))
1151 pos++;
1152 if (pos > 0)
1153 check_at(gtkaspell, pos - 1);
1155 gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
1156 gtk_stext_set_point(GTK_STEXT(gtktext), origpos);
1157 gtk_adjustment_set_value(gtktext->vadj, adj_value);
1160 static void replace_with_supplied_word_cb(GtkWidget *w, GtkAspell *gtkaspell)
1162 unsigned char *newword;
1163 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1165 newword = gtk_editable_get_chars(GTK_EDITABLE(gtkaspell->replace_entry),
1166 0, -1);
1168 if (strcmp(newword, gtkaspell->theword)) {
1169 replace_real_word(gtkaspell, newword);
1171 if ((e->type == GDK_KEY_PRESS &&
1172 ((GdkEventKey *) e)->state & GDK_MOD1_MASK)) {
1173 aspell_speller_store_replacement(gtkaspell->gtkaspeller->checker,
1174 gtkaspell->theword, -1,
1175 newword, -1);
1177 gtkaspell->replace_entry = NULL;
1180 g_free(newword);
1182 set_point_continue(gtkaspell);
1186 static void replace_word_cb(GtkWidget *w, gpointer data)
1188 unsigned char *newword;
1189 GtkAspell *gtkaspell = (GtkAspell *) data;
1190 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1192 gtk_label_get(GTK_LABEL(GTK_BIN(w)->child), (gchar**) &newword);
1194 replace_real_word(gtkaspell, newword);
1196 if ((e->type == GDK_KEY_PRESS &&
1197 ((GdkEventKey *) e)->state & GDK_MOD1_MASK) ||
1198 (e->type == GDK_BUTTON_RELEASE &&
1199 ((GdkEventButton *) e)->state & GDK_MOD1_MASK)) {
1200 aspell_speller_store_replacement(gtkaspell->gtkaspeller->checker,
1201 gtkaspell->theword, -1,
1202 newword, -1);
1205 gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1207 set_point_continue(gtkaspell);
1210 static void replace_real_word(GtkAspell *gtkaspell, gchar *newword)
1212 int oldlen, newlen, wordlen;
1213 gint origpos;
1214 gint pos;
1215 gint start = gtkaspell->start_pos;
1216 GtkSText *gtktext;
1218 if (!newword) return;
1220 gtktext = gtkaspell->gtktext;
1222 gtk_stext_freeze(GTK_STEXT(gtktext));
1223 origpos = gtkaspell->orig_pos;
1224 pos = origpos;
1225 oldlen = gtkaspell->end_pos - gtkaspell->start_pos;
1226 wordlen = strlen(gtkaspell->theword);
1228 newlen = strlen(newword); /* FIXME: multybyte characters? */
1230 gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
1231 GTK_SIGNAL_FUNC(entry_insert_cb),
1232 gtkaspell);
1233 gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
1234 GTK_SIGNAL_FUNC(entry_delete_cb),
1235 gtkaspell);
1237 gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "delete-text",
1238 gtkaspell->start_pos, gtkaspell->end_pos);
1239 gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "insert-text",
1240 newword, newlen, &start);
1242 gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
1243 GTK_SIGNAL_FUNC(entry_insert_cb),
1244 gtkaspell);
1245 gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
1246 GTK_SIGNAL_FUNC(entry_delete_cb),
1247 gtkaspell);
1249 /* Put the point and the position where we clicked with the mouse
1250 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1251 * to let it update correctly the word insertion and then the
1252 * point & position position. If not, SEGV after the first replacement
1253 * If the new word ends before point, put the point at its end.
1256 if (origpos - gtkaspell->start_pos < oldlen &&
1257 origpos - gtkaspell->start_pos >= 0) {
1258 /* Original point was in the word.
1259 * Let it there unless point is going to be outside of the word
1261 if (origpos - gtkaspell->start_pos >= newlen) {
1262 pos = gtkaspell->start_pos + newlen;
1265 else if (origpos >= gtkaspell->end_pos) {
1266 /* move the position according to the change of length */
1267 pos = origpos + newlen - oldlen;
1270 gtkaspell->end_pos = gtkaspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1272 gtk_stext_thaw(GTK_STEXT(gtktext));
1273 gtk_stext_freeze(GTK_STEXT(gtktext));
1275 if (GTK_STEXT(gtktext)->text_len < pos)
1276 pos = gtk_stext_get_length(GTK_STEXT(gtktext));
1278 gtkaspell->orig_pos = pos;
1280 gtk_editable_set_position(GTK_EDITABLE(gtktext), gtkaspell->orig_pos);
1281 gtk_stext_set_point(GTK_STEXT(gtktext),
1282 gtk_editable_get_position(GTK_EDITABLE(gtktext)));
1284 gtk_stext_thaw(GTK_STEXT(gtktext));
1287 /* Accept this word for this session */
1288 static void add_word_to_session_cb(GtkWidget *w, gpointer data)
1290 guint pos;
1291 GtkSText *gtktext;
1292 GtkAspell *gtkaspell = (GtkAspell *) data;
1293 gtktext = gtkaspell->gtktext;
1295 gtk_stext_freeze(GTK_STEXT(gtktext));
1297 pos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
1299 aspell_speller_add_to_session(gtkaspell->gtkaspeller->checker,
1300 gtkaspell->theword,
1301 strlen(gtkaspell->theword));
1303 check_at(gtkaspell, gtkaspell->start_pos);
1305 gtk_stext_thaw(gtkaspell->gtktext);
1307 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1309 set_point_continue(gtkaspell);
1312 /* add_word_to_personal_cb() - add word to personal dict. */
1313 static void add_word_to_personal_cb(GtkWidget *w, gpointer data)
1315 GtkAspell *gtkaspell = (GtkAspell *) data;
1316 GtkSText *gtktext = gtkaspell->gtktext;
1318 gtk_stext_freeze(GTK_STEXT(gtktext));
1320 aspell_speller_add_to_personal(gtkaspell->gtkaspeller->checker,
1321 gtkaspell->theword,
1322 strlen(gtkaspell->theword));
1324 check_at(gtkaspell, gtkaspell->start_pos);
1326 gtk_stext_thaw(gtkaspell->gtktext);
1328 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1329 set_point_continue(gtkaspell);
1332 static void check_with_alternate_cb(GtkWidget *w, gpointer data)
1334 GtkAspell *gtkaspell = (GtkAspell *) data;
1335 gint misspelled;
1337 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1339 use_alternate_dict(gtkaspell);
1340 misspelled = check_at(gtkaspell, gtkaspell->start_pos);
1342 if (!gtkaspell->continue_check) {
1344 gtkaspell->misspelled = misspelled;
1346 if (gtkaspell->misspelled) {
1348 misspelled_suggest(gtkaspell, gtkaspell->theword);
1350 gtk_stext_set_point(GTK_STEXT(gtkaspell->gtktext),
1351 gtkaspell->end_pos);
1352 gtk_editable_set_position(GTK_EDITABLE(gtkaspell->gtktext),
1353 gtkaspell->end_pos);
1355 gtk_menu_popup(make_sug_menu(gtkaspell), NULL, NULL,
1356 set_menu_pos, gtkaspell, 0,
1357 GDK_CURRENT_TIME);
1358 return;
1360 } else
1361 gtkaspell->orig_pos = gtkaspell->start_pos;
1363 set_point_continue(gtkaspell);
1366 static void replace_with_create_dialog_cb(GtkWidget *w, gpointer data)
1368 GtkWidget *dialog;
1369 GtkWidget *label;
1370 GtkWidget *hbox;
1371 GtkWidget *entry;
1372 GtkWidget *ok_button;
1373 GtkWidget *cancel_button;
1374 gchar *thelabel;
1375 gint xx, yy;
1376 GtkAspell *gtkaspell = (GtkAspell *) data;
1378 gdk_window_get_origin((GTK_WIDGET(w)->parent)->window, &xx, &yy);
1380 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1382 dialog = gtk_dialog_new();
1384 gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE);
1385 gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1386 gtk_widget_set_uposition(dialog, xx, yy);
1388 gtk_signal_connect_object(GTK_OBJECT(dialog), "destroy",
1389 GTK_SIGNAL_FUNC(gtk_widget_destroy),
1390 GTK_OBJECT(dialog));
1392 hbox = gtk_hbox_new(FALSE, 0);
1393 gtk_container_set_border_width(GTK_CONTAINER(hbox), 8);
1395 thelabel = g_strdup_printf(_("Replace \"%s\" with: "),
1396 gtkaspell->theword);
1397 label = gtk_label_new(thelabel);
1398 g_free(thelabel);
1399 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1401 entry = gtk_entry_new();
1402 gtkaspell->replace_entry = entry;
1403 gtk_entry_set_text(GTK_ENTRY(entry), gtkaspell->theword);
1404 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1405 gtk_signal_connect(GTK_OBJECT(entry), "activate",
1406 GTK_SIGNAL_FUNC(replace_with_supplied_word_cb),
1407 gtkaspell);
1408 gtk_signal_connect_object(GTK_OBJECT(entry), "activate",
1409 GTK_SIGNAL_FUNC(gtk_widget_destroy),
1410 GTK_OBJECT(dialog));
1411 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
1413 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE,
1414 TRUE, 0);
1415 label = gtk_label_new(_("Holding down MOD1 key while pressing "
1416 "Enter\nwill learn from mistake.\n"));
1417 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1418 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
1419 gtk_misc_set_padding(GTK_MISC(label), 8, 0);
1420 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label,
1421 TRUE, TRUE, 0);
1423 hbox = gtk_hbox_new(TRUE, 0);
1425 ok_button = gtk_button_new_with_label(_("OK"));
1426 gtk_box_pack_start(GTK_BOX(hbox), ok_button, TRUE, TRUE, 8);
1427 gtk_signal_connect(GTK_OBJECT(ok_button), "clicked",
1428 GTK_SIGNAL_FUNC(replace_with_supplied_word_cb),
1429 gtkaspell);
1430 gtk_signal_connect_object(GTK_OBJECT(ok_button), "clicked",
1431 GTK_SIGNAL_FUNC(gtk_widget_destroy),
1432 GTK_OBJECT(dialog));
1434 cancel_button = gtk_button_new_with_label(_("Cancel"));
1435 gtk_box_pack_start(GTK_BOX(hbox), cancel_button, TRUE, TRUE, 8);
1436 gtk_signal_connect_object(GTK_OBJECT(cancel_button), "clicked",
1437 GTK_SIGNAL_FUNC(gtk_widget_destroy),
1438 GTK_OBJECT(dialog));
1440 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), hbox);
1442 gtk_widget_grab_focus(entry);
1444 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1446 gtk_widget_show_all(dialog);
1449 void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1451 gint origpos;
1452 gchar *text;
1453 gfloat adj_value;
1454 GtkSText *gtktext;
1456 gtktext = gtkaspell->gtktext;
1458 adj_value = gtktext->vadj->value;
1460 gtk_stext_freeze(gtktext);
1462 origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
1464 text = gtk_editable_get_chars(GTK_EDITABLE(gtktext), 0, -1);
1466 gtk_stext_set_point(gtktext, 0);
1467 gtk_stext_forward_delete(gtktext, gtk_stext_get_length(gtktext));
1468 gtk_stext_insert(gtktext, NULL, NULL, NULL, text, strlen(text));
1470 gtk_stext_thaw(gtktext);
1472 gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
1473 gtk_stext_set_point(gtktext, origpos);
1474 gtk_adjustment_set_value(gtktext->vadj, adj_value);
1476 g_free(text);
1480 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1482 GtkAspell *gtkaspell = (GtkAspell *) data;
1484 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1486 if (!gtkaspell->check_while_typing)
1487 gtkaspell_uncheck_all(gtkaspell);
1489 if (gtkaspell->config_menu)
1490 populate_submenu(gtkaspell, gtkaspell->config_menu);
1493 static GSList *create_empty_dictionary_list(void)
1495 GSList *list = NULL;
1496 Dictionary *dict;
1498 dict = g_new0(Dictionary, 1);
1499 dict->fullname = g_strdup(_("None"));
1500 dict->dictname = dict->fullname;
1501 dict->encoding = NULL;
1503 return g_slist_append(list, dict);
1506 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1507 GSList *gtkaspell_get_dictionary_list(const gchar *aspell_path, gint refresh)
1509 GSList *list;
1510 Dictionary *dict;
1511 AspellConfig *config;
1512 AspellDictInfoList *dlist;
1513 AspellDictInfoEnumeration *dels;
1514 const AspellDictInfo *entry;
1516 if (!gtkaspellcheckers)
1517 gtkaspell_checkers_init();
1519 if (gtkaspellcheckers->dictionary_list && !refresh)
1520 return gtkaspellcheckers->dictionary_list;
1521 else
1522 gtkaspell_free_dictionary_list(gtkaspellcheckers->dictionary_list);
1523 list = NULL;
1525 config = new_aspell_config();
1526 #if 0
1527 aspell_config_replace(config, "rem-all-word-list-path", "");
1528 if (aspell_config_error_number(config) != 0) {
1529 gtkaspellcheckers->error_message = g_strdup(
1530 aspell_config_error_message(config));
1531 gtkaspellcheckers->dictionary_list =
1532 create_empty_dictionary_list();
1534 return gtkaspellcheckers->dictionary_list;
1536 #endif
1537 aspell_config_replace(config, "dict-dir", aspell_path);
1538 if (aspell_config_error_number(config) != 0) {
1539 gtkaspellcheckers->error_message = g_strdup(
1540 aspell_config_error_message(config));
1541 gtkaspellcheckers->dictionary_list =
1542 create_empty_dictionary_list();
1544 return gtkaspellcheckers->dictionary_list;
1547 dlist = get_aspell_dict_info_list(config);
1548 delete_aspell_config(config);
1550 debug_print("Aspell: checking for dictionaries in %s\n", aspell_path);
1551 dels = aspell_dict_info_list_elements(dlist);
1552 while ( (entry = aspell_dict_info_enumeration_next(dels)) != 0)
1554 dict = g_new0(Dictionary, 1);
1555 dict->fullname = g_strdup_printf("%s%s", aspell_path,
1556 entry->name);
1557 dict->dictname = dict->fullname + strlen(aspell_path);
1558 dict->encoding = g_strdup(entry->code);
1559 debug_print("Aspell: found dictionary %s %s\n", dict->fullname,
1560 dict->dictname);
1561 list = g_slist_insert_sorted(list, dict,
1562 (GCompareFunc) compare_dict);
1565 delete_aspell_dict_info_enumeration(dels);
1567 if(list==NULL){
1569 debug_print("Aspell: error when searching for dictionaries: "
1570 "No dictionary found.\n");
1571 list = create_empty_dictionary_list();
1574 gtkaspellcheckers->dictionary_list = list;
1576 return list;
1579 void gtkaspell_free_dictionary_list(GSList *list)
1581 Dictionary *dict;
1582 GSList *walk;
1583 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1584 if (walk->data) {
1585 dict = (Dictionary *) walk->data;
1586 dictionary_delete(dict);
1588 g_slist_free(list);
1591 GtkWidget *gtkaspell_dictionary_option_menu_new(const gchar *aspell_path)
1593 GSList *dict_list, *tmp;
1594 GtkWidget *item;
1595 GtkWidget *menu;
1596 Dictionary *dict;
1598 dict_list = gtkaspell_get_dictionary_list(aspell_path, TRUE);
1599 g_return_val_if_fail(dict_list, NULL);
1601 menu = gtk_menu_new();
1603 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1604 dict = (Dictionary *) tmp->data;
1605 item = gtk_menu_item_new_with_label(dict->dictname);
1606 gtk_object_set_data(GTK_OBJECT(item), "dict_name",
1607 dict->fullname);
1609 gtk_menu_append(GTK_MENU(menu), item);
1610 gtk_widget_show(item);
1613 gtk_widget_show(menu);
1615 return menu;
1618 gchar *gtkaspell_get_dictionary_menu_active_item(GtkWidget *menu)
1620 GtkWidget *menuitem;
1621 gchar *dict_fullname;
1622 gchar *label;
1624 g_return_val_if_fail(GTK_IS_MENU(menu), NULL);
1626 menuitem = gtk_menu_get_active(GTK_MENU(menu));
1627 dict_fullname = (gchar *) gtk_object_get_data(GTK_OBJECT(menuitem),
1628 "dict_name");
1629 g_return_val_if_fail(dict_fullname, NULL);
1631 label = g_strdup(dict_fullname);
1633 return label;
1637 gint gtkaspell_set_dictionary_menu_active_item(GtkWidget *menu, const gchar *dictionary)
1639 GList *cur;
1640 gint n;
1642 g_return_val_if_fail(menu != NULL, 0);
1643 g_return_val_if_fail(dictionary != NULL, 0);
1644 g_return_val_if_fail(GTK_IS_OPTION_MENU(menu), 0);
1646 n = 0;
1647 for (cur = GTK_MENU_SHELL(gtk_option_menu_get_menu(GTK_OPTION_MENU(menu)))->children;
1648 cur != NULL; cur = cur->next) {
1649 GtkWidget *menuitem;
1650 gchar *dict_name;
1652 menuitem = GTK_WIDGET(cur->data);
1653 dict_name = gtk_object_get_data(GTK_OBJECT(menuitem),
1654 "dict_name");
1655 if ((dict_name != NULL) && !strcmp2(dict_name, dictionary)) {
1656 gtk_option_menu_set_history(GTK_OPTION_MENU(menu), n);
1658 return 1;
1660 n++;
1663 return 0;
1666 GtkWidget *gtkaspell_sugmode_option_menu_new(gint sugmode)
1668 GtkWidget *menu;
1669 GtkWidget *item;
1671 menu = gtk_menu_new();
1672 gtk_widget_show(menu);
1674 item = gtk_menu_item_new_with_label(_("Fast Mode"));
1675 gtk_widget_show(item);
1676 gtk_menu_append(GTK_MENU(menu), item);
1677 gtk_object_set_data(GTK_OBJECT(item), "sugmode", GINT_TO_POINTER(ASPELL_FASTMODE));
1679 item = gtk_menu_item_new_with_label(_("Normal Mode"));
1680 gtk_widget_show(item);
1681 gtk_menu_append(GTK_MENU(menu), item);
1682 gtk_object_set_data(GTK_OBJECT(item), "sugmode", GINT_TO_POINTER(ASPELL_NORMALMODE));
1684 item = gtk_menu_item_new_with_label(_("Bad Spellers Mode"));
1685 gtk_widget_show(item);
1686 gtk_menu_append(GTK_MENU(menu), item);
1687 gtk_object_set_data(GTK_OBJECT(item), "sugmode", GINT_TO_POINTER(ASPELL_BADSPELLERMODE));
1689 return menu;
1692 void gtkaspell_sugmode_option_menu_set(GtkOptionMenu *optmenu, gint sugmode)
1694 g_return_if_fail(GTK_IS_OPTION_MENU(optmenu));
1696 g_return_if_fail(sugmode == ASPELL_FASTMODE ||
1697 sugmode == ASPELL_NORMALMODE ||
1698 sugmode == ASPELL_BADSPELLERMODE);
1700 gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), sugmode - 1);
1703 gint gtkaspell_get_sugmode_from_option_menu(GtkOptionMenu *optmenu)
1705 gint sugmode;
1706 GtkWidget *item;
1708 g_return_val_if_fail(GTK_IS_OPTION_MENU(optmenu), -1);
1710 item = gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(optmenu)));
1712 sugmode = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),
1713 "sugmode"));
1715 return sugmode;
1718 static void use_alternate_dict(GtkAspell *gtkaspell)
1720 GtkAspeller *tmp;
1722 tmp = gtkaspell->gtkaspeller;
1723 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1724 gtkaspell->alternate_speller = tmp;
1726 if (gtkaspell->config_menu)
1727 populate_submenu(gtkaspell, gtkaspell->config_menu);
1730 static void popup_menu(GtkAspell *gtkaspell, GdkEventButton *eb)
1732 GtkSText * gtktext;
1734 gtktext = gtkaspell->gtktext;
1735 gtkaspell->orig_pos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
1737 if (!(eb->state & GDK_SHIFT_MASK)) {
1738 if (check_at(gtkaspell, gtkaspell->orig_pos)) {
1740 gtk_editable_set_position(GTK_EDITABLE(gtktext),
1741 gtkaspell->orig_pos);
1742 gtk_stext_set_point(gtktext, gtkaspell->orig_pos);
1744 if (misspelled_suggest(gtkaspell, gtkaspell->theword)) {
1745 gtk_menu_popup(make_sug_menu(gtkaspell),
1746 NULL, NULL, NULL, NULL,
1747 eb->button, GDK_CURRENT_TIME);
1749 return;
1751 } else {
1752 gtk_editable_set_position(GTK_EDITABLE(gtktext),
1753 gtkaspell->orig_pos);
1754 gtk_stext_set_point(gtktext, gtkaspell->orig_pos);
1758 gtk_menu_popup(make_config_menu(gtkaspell), NULL, NULL, NULL, NULL,
1759 eb->button, GDK_CURRENT_TIME);
1762 /* make_sug_menu() - Add menus to accept this word for this session
1763 * and to add it to personal dictionary
1765 static GtkMenu *make_sug_menu(GtkAspell *gtkaspell)
1767 GtkWidget *menu, *item;
1768 unsigned char *caption;
1769 GtkSText *gtktext;
1770 GtkAccelGroup *accel;
1771 GList *l = gtkaspell->suggestions_list;
1773 gtktext = gtkaspell->gtktext;
1775 accel = gtk_accel_group_new();
1776 menu = gtk_menu_new();
1778 if (gtkaspell->sug_menu)
1779 gtk_widget_destroy(gtkaspell->sug_menu);
1781 gtkaspell->sug_menu = menu;
1783 gtk_signal_connect(GTK_OBJECT(menu), "cancel",
1784 GTK_SIGNAL_FUNC(cancel_menu_cb), gtkaspell);
1786 caption = g_strdup_printf(_("\"%s\" unknown in %s"),
1787 (unsigned char*) l->data,
1788 gtkaspell->gtkaspeller->dictionary->dictname);
1789 item = gtk_menu_item_new_with_label(caption);
1790 gtk_widget_show(item);
1791 gtk_menu_append(GTK_MENU(menu), item);
1792 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
1793 g_free(caption);
1795 item = gtk_menu_item_new();
1796 gtk_widget_show(item);
1797 gtk_menu_append(GTK_MENU(menu), item);
1799 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1800 gtk_widget_show(item);
1801 gtk_menu_append(GTK_MENU(menu), item);
1802 gtk_signal_connect(GTK_OBJECT(item), "activate",
1803 GTK_SIGNAL_FUNC(add_word_to_session_cb),
1804 gtkaspell);
1805 gtk_widget_add_accelerator(item, "activate", accel, GDK_space,
1806 GDK_MOD1_MASK,
1807 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1809 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1810 gtk_widget_show(item);
1811 gtk_menu_append(GTK_MENU(menu), item);
1812 gtk_signal_connect(GTK_OBJECT(item), "activate",
1813 GTK_SIGNAL_FUNC(add_word_to_personal_cb),
1814 gtkaspell);
1815 gtk_widget_add_accelerator(item, "activate", accel, GDK_Return,
1816 GDK_MOD1_MASK,
1817 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1819 item = gtk_menu_item_new_with_label(_("Replace with..."));
1820 gtk_widget_show(item);
1821 gtk_menu_append(GTK_MENU(menu), item);
1822 gtk_signal_connect(GTK_OBJECT(item), "activate",
1823 GTK_SIGNAL_FUNC(replace_with_create_dialog_cb),
1824 gtkaspell);
1825 gtk_widget_add_accelerator(item, "activate", accel, GDK_R, 0,
1826 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1828 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1829 caption = g_strdup_printf(_("Check with %s"),
1830 gtkaspell->alternate_speller->dictionary->dictname);
1831 item = gtk_menu_item_new_with_label(caption);
1832 g_free(caption);
1833 gtk_widget_show(item);
1834 gtk_menu_append(GTK_MENU(menu), item);
1835 gtk_signal_connect(GTK_OBJECT(item), "activate",
1836 GTK_SIGNAL_FUNC(check_with_alternate_cb),
1837 gtkaspell);
1838 gtk_widget_add_accelerator(item, "activate", accel, GDK_X, 0,
1839 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1842 item = gtk_menu_item_new();
1843 gtk_widget_show(item);
1844 gtk_menu_append(GTK_MENU(menu), item);
1846 l = l->next;
1847 if (l == NULL) {
1848 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
1849 gtk_widget_show(item);
1850 gtk_menu_append(GTK_MENU(menu), item);
1851 } else {
1852 GtkWidget *curmenu = menu;
1853 gint count = 0;
1855 do {
1856 if (count == MENUCOUNT) {
1857 count -= MENUCOUNT;
1859 item = gtk_menu_item_new_with_label(_("More..."));
1860 gtk_widget_show(item);
1861 gtk_menu_append(GTK_MENU(curmenu), item);
1863 curmenu = gtk_menu_new();
1864 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1865 curmenu);
1868 item = gtk_menu_item_new_with_label((unsigned char*)l->data);
1869 gtk_widget_show(item);
1870 gtk_menu_append(GTK_MENU(curmenu), item);
1871 gtk_signal_connect(GTK_OBJECT(item), "activate",
1872 GTK_SIGNAL_FUNC(replace_word_cb),
1873 gtkaspell);
1875 if (curmenu == menu && count < MENUCOUNT) {
1876 gtk_widget_add_accelerator(item, "activate",
1877 accel,
1878 GDK_A + count, 0,
1879 GTK_ACCEL_LOCKED |
1880 GTK_ACCEL_VISIBLE);
1881 gtk_widget_add_accelerator(item, "activate",
1882 accel,
1883 GDK_A + count,
1884 GDK_MOD1_MASK,
1885 GTK_ACCEL_LOCKED);
1888 count++;
1890 } while ((l = l->next) != NULL);
1893 gtk_accel_group_attach(accel, GTK_OBJECT(menu));
1894 gtk_accel_group_unref(accel);
1896 return GTK_MENU(menu);
1899 static void populate_submenu(GtkAspell *gtkaspell, GtkWidget *menu)
1901 GtkWidget *item, *submenu;
1902 gchar *dictname;
1903 GtkAspeller *gtkaspeller = gtkaspell->gtkaspeller;
1905 if (GTK_MENU_SHELL(menu)->children) {
1906 GList *amenu, *alist;
1907 for (amenu = (GTK_MENU_SHELL(menu)->children); amenu; ) {
1908 alist = amenu->next;
1909 gtk_widget_destroy(GTK_WIDGET(amenu->data));
1910 amenu = alist;
1914 dictname = g_strdup_printf(_("Dictionary: %s"),
1915 gtkaspeller->dictionary->dictname);
1916 item = gtk_menu_item_new_with_label(dictname);
1917 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
1918 g_free(dictname);
1919 gtk_widget_show(item);
1920 gtk_menu_append(GTK_MENU(menu), item);
1922 item = gtk_menu_item_new();
1923 gtk_widget_show(item);
1924 gtk_menu_append(GTK_MENU(menu), item);
1926 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1927 dictname = g_strdup_printf(_("Use alternate (%s)"),
1928 gtkaspell->alternate_speller->dictionary->dictname);
1929 item = gtk_menu_item_new_with_label(dictname);
1930 g_free(dictname);
1931 gtk_signal_connect(GTK_OBJECT(item), "activate",
1932 GTK_SIGNAL_FUNC(switch_to_alternate_cb),
1933 gtkaspell);
1934 gtk_widget_show(item);
1935 gtk_menu_append(GTK_MENU(menu), item);
1938 item = gtk_check_menu_item_new_with_label(_("Fast Mode"));
1939 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_FASTMODE) {
1940 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),TRUE);
1941 gtk_widget_set_sensitive(GTK_WIDGET(item),FALSE);
1942 } else
1943 gtk_signal_connect(GTK_OBJECT(item), "activate",
1944 GTK_SIGNAL_FUNC(set_sug_mode_cb),
1945 gtkaspell);
1946 gtk_widget_show(item);
1947 gtk_menu_append(GTK_MENU(menu), item);
1949 item = gtk_check_menu_item_new_with_label(_("Normal Mode"));
1950 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_NORMALMODE) {
1951 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
1952 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1953 } else
1954 gtk_signal_connect(GTK_OBJECT(item), "activate",
1955 GTK_SIGNAL_FUNC(set_sug_mode_cb),
1956 gtkaspell);
1957 gtk_widget_show(item);
1958 gtk_menu_append(GTK_MENU(menu),item);
1960 item = gtk_check_menu_item_new_with_label(_("Bad Spellers Mode"));
1961 if (gtkaspell->gtkaspeller->sug_mode == ASPELL_BADSPELLERMODE) {
1962 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
1963 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1964 } else
1965 gtk_signal_connect(GTK_OBJECT(item), "activate",
1966 GTK_SIGNAL_FUNC(set_sug_mode_cb),
1967 gtkaspell);
1968 gtk_widget_show(item);
1969 gtk_menu_append(GTK_MENU(menu), item);
1971 item = gtk_menu_item_new();
1972 gtk_widget_show(item);
1973 gtk_menu_append(GTK_MENU(menu), item);
1975 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
1976 if (gtkaspell->check_while_typing)
1977 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1978 else
1979 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1980 gtk_signal_connect(GTK_OBJECT(item), "activate",
1981 GTK_SIGNAL_FUNC(toggle_check_while_typing_cb),
1982 gtkaspell);
1983 gtk_widget_show(item);
1984 gtk_menu_append(GTK_MENU(menu), item);
1986 item = gtk_menu_item_new();
1987 gtk_widget_show(item);
1988 gtk_menu_append(GTK_MENU(menu), item);
1990 submenu = gtk_menu_new();
1991 item = gtk_menu_item_new_with_label(_("Change dictionary"));
1992 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
1993 gtk_widget_show(item);
1994 gtk_menu_append(GTK_MENU(menu), item);
1996 /* Dict list */
1997 if (gtkaspellcheckers->dictionary_list == NULL)
1998 gtkaspell_get_dictionary_list(gtkaspell->dictionary_path, FALSE);
2000 GtkWidget * curmenu = submenu;
2001 int count = 0;
2002 Dictionary *dict;
2003 GSList *tmp;
2004 tmp = gtkaspellcheckers->dictionary_list;
2006 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
2007 tmp = g_slist_next(tmp)) {
2008 if (count == MENUCOUNT) {
2009 GtkWidget *newmenu;
2011 newmenu = gtk_menu_new();
2012 item = gtk_menu_item_new_with_label(_("More..."));
2013 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
2014 newmenu);
2016 gtk_menu_append(GTK_MENU(curmenu), item);
2017 gtk_widget_show(item);
2018 curmenu = newmenu;
2019 count = 0;
2021 dict = (Dictionary *) tmp->data;
2022 item = gtk_check_menu_item_new_with_label(dict->dictname);
2023 gtk_object_set_data(GTK_OBJECT(item), "dict_name",
2024 dict->fullname);
2025 if (strcmp2(dict->fullname,
2026 gtkaspell->gtkaspeller->dictionary->fullname))
2027 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2028 else {
2029 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2030 gtk_widget_set_sensitive(GTK_WIDGET(item),
2031 FALSE);
2033 gtk_signal_connect(GTK_OBJECT(item), "activate",
2034 GTK_SIGNAL_FUNC(change_dict_cb),
2035 gtkaspell);
2036 gtk_widget_show(item);
2037 gtk_menu_append(GTK_MENU(curmenu), item);
2039 count++;
2044 static GtkMenu *make_config_menu(GtkAspell *gtkaspell)
2046 if (!gtkaspell->popup_config_menu)
2047 gtkaspell->popup_config_menu = gtk_menu_new();
2049 debug_print("Aspell: creating/using popup_config_menu %0x\n",
2050 (guint) gtkaspell->popup_config_menu);
2051 populate_submenu(gtkaspell, gtkaspell->popup_config_menu);
2053 return GTK_MENU(gtkaspell->popup_config_menu);
2056 void gtkaspell_populate_submenu(GtkAspell *gtkaspell, GtkWidget *menuitem)
2058 GtkWidget *menu;
2060 menu = GTK_WIDGET(GTK_MENU_ITEM(menuitem)->submenu);
2062 debug_print("Aspell: using config menu %0x\n",
2063 (guint) gtkaspell->popup_config_menu);
2064 populate_submenu(gtkaspell, menu);
2066 gtkaspell->config_menu = menu;
2070 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y, gpointer data)
2072 GtkAspell *gtkaspell = (GtkAspell *) data;
2073 gint xx = 0, yy = 0;
2074 gint sx, sy;
2075 gint wx, wy;
2076 GtkSText *text = GTK_STEXT(gtkaspell->gtktext);
2077 GtkRequisition r;
2079 gdk_window_get_origin(GTK_WIDGET(gtkaspell->gtktext)->window, &xx, &yy);
2081 sx = gdk_screen_width();
2082 sy = gdk_screen_height();
2084 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2086 wx = r.width;
2087 wy = r.height;
2089 *x = gtkaspell->gtktext->cursor_pos_x + xx +
2090 gdk_char_width(GTK_WIDGET(text)->style->font, ' ');
2091 *y = gtkaspell->gtktext->cursor_pos_y + yy;
2093 if (*x + wx > sx)
2094 *x = sx - wx;
2095 if (*y + wy > sy)
2096 *y = *y - wy -
2097 gdk_string_height((GTK_WIDGET(gtkaspell->gtktext))->style->font,
2098 gtkaspell->theword);
2102 /* Menu call backs */
2104 static gboolean cancel_menu_cb(GtkMenuShell *w, gpointer data)
2106 GtkAspell *gtkaspell = (GtkAspell *) data;
2108 gtkaspell->continue_check = NULL;
2109 set_point_continue(gtkaspell);
2111 return FALSE;
2115 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary)
2117 Dictionary *dict;
2118 GtkAspeller *gtkaspeller;
2119 gint sug_mode;
2121 g_return_val_if_fail(gtkaspell, FALSE);
2122 g_return_val_if_fail(dictionary, FALSE);
2124 sug_mode = gtkaspell->default_sug_mode;
2126 dict = g_new0(Dictionary, 1);
2127 dict->fullname = g_strdup(dictionary);
2128 dict->encoding = g_strdup(gtkaspell->gtkaspeller->dictionary->encoding);
2130 if (gtkaspell->use_alternate && gtkaspell->alternate_speller &&
2131 dict == gtkaspell->alternate_speller->dictionary) {
2132 use_alternate_dict(gtkaspell);
2133 dictionary_delete(dict);
2134 return TRUE;
2137 gtkaspeller = gtkaspeller_new(dict);
2139 if (!gtkaspeller) {
2140 gchar *message;
2141 message = g_strdup_printf(_("The spell checker could not change dictionary.\n%s"),
2142 gtkaspellcheckers->error_message);
2144 gtkaspell_alert_dialog(message);
2145 g_free(message);
2146 } else {
2147 if (gtkaspell->use_alternate) {
2148 if (gtkaspell->alternate_speller)
2149 gtkaspeller_delete(gtkaspell->alternate_speller);
2150 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2151 } else
2152 gtkaspeller_delete(gtkaspell->gtkaspeller);
2154 gtkaspell->gtkaspeller = gtkaspeller;
2155 gtkaspell_set_sug_mode(gtkaspell, sug_mode);
2158 dictionary_delete(dict);
2160 if (gtkaspell->config_menu)
2161 populate_submenu(gtkaspell, gtkaspell->config_menu);
2163 return TRUE;
2166 /* change_dict_cb() - Menu callback : change dict */
2167 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2169 gchar *fullname;
2171 fullname = (gchar *) gtk_object_get_data(GTK_OBJECT(w), "dict_name");
2173 if (!strcmp2(fullname, _("None")))
2174 return;
2176 gtkaspell_change_dict(gtkaspell, fullname);
2179 static void switch_to_alternate_cb(GtkWidget *w,
2180 gpointer data)
2182 GtkAspell *gtkaspell = (GtkAspell *) data;
2183 use_alternate_dict(gtkaspell);
2186 /* Misc. helper functions */
2188 static void set_point_continue(GtkAspell *gtkaspell)
2190 GtkSText *gtktext;
2192 gtktext = gtkaspell->gtktext;
2194 gtk_stext_freeze(gtktext);
2195 gtk_editable_set_position(GTK_EDITABLE(gtktext),gtkaspell->orig_pos);
2196 gtk_stext_set_point(gtktext, gtkaspell->orig_pos);
2197 gtk_stext_thaw(gtktext);
2199 if (gtkaspell->continue_check)
2200 gtkaspell->continue_check((gpointer *) gtkaspell);
2203 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2205 GdkColormap *gc;
2206 GdkColor *color = &(gtkaspell->highlight);
2208 gc = gtk_widget_get_colormap(GTK_WIDGET(gtkaspell->gtktext));
2210 if (gtkaspell->highlight.pixel)
2211 gdk_colormap_free_colors(gc, &(gtkaspell->highlight), 1);
2213 /* Shameless copy from Sylpheed's gtkutils.c */
2214 color->pixel = 0L;
2215 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2216 * 65535.0);
2217 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2218 * 65535.0);
2219 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2220 * 65535.0);
2222 gdk_colormap_alloc_color(gc, &(gtkaspell->highlight), FALSE, TRUE);
2225 static void change_color(GtkAspell * gtkaspell,
2226 gint start, gint end,
2227 gchar *newtext,
2228 GdkColor *color)
2230 GtkSText *gtktext;
2232 g_return_if_fail(start < end);
2234 gtktext = gtkaspell->gtktext;
2236 gtk_stext_freeze(gtktext);
2237 if (newtext) {
2238 gtk_stext_set_point(gtktext, start);
2239 gtk_stext_forward_delete(gtktext, end - start);
2240 gtk_stext_insert(gtktext, NULL, color, NULL, newtext,
2241 end - start);
2243 gtk_stext_thaw(gtktext);
2246 /* convert_to_aspell_encoding () - converts ISO-8859-* strings to iso8859-*
2247 * as needed by aspell. Returns an allocated string.
2250 static guchar *convert_to_aspell_encoding (const guchar *encoding)
2252 guchar * aspell_encoding;
2254 if (strstr2(encoding, "ISO-8859-")) {
2255 aspell_encoding = g_strdup_printf("iso8859%s", encoding+8);
2257 else {
2258 if (!strcmp2(encoding, "US-ASCII"))
2259 aspell_encoding = g_strdup("iso8859-1");
2260 else
2261 aspell_encoding = g_strdup(encoding);
2264 return aspell_encoding;
2267 /* compare_dict () - compare 2 dict names */
2268 static gint compare_dict(Dictionary *a, Dictionary *b)
2270 guint aparts = 0, bparts = 0;
2271 guint i;
2273 for (i=0; i < strlen(a->dictname); i++)
2274 if (a->dictname[i] == '-')
2275 aparts++;
2276 for (i=0; i < strlen(b->dictname); i++)
2277 if (b->dictname[i] == '-')
2278 bparts++;
2280 if (aparts != bparts)
2281 return (aparts < bparts) ? -1 : +1;
2282 else {
2283 gint compare;
2284 compare = strcmp2(a->dictname, b->dictname);
2285 if (!compare)
2286 compare = strcmp2(a->fullname, b->fullname);
2287 return compare;
2291 static void dictionary_delete(Dictionary *dict)
2293 g_free(dict->fullname);
2294 g_free(dict->encoding);
2295 g_free(dict);
2298 static Dictionary *dictionary_dup(const Dictionary *dict)
2300 Dictionary *dict2;
2302 dict2 = g_new(Dictionary, 1);
2304 dict2->fullname = g_strdup(dict->fullname);
2305 dict2->dictname = dict->dictname - dict->fullname + dict2->fullname;
2306 dict2->encoding = g_strdup(dict->encoding);
2308 return dict2;
2311 static void free_suggestions_list(GtkAspell *gtkaspell)
2313 GList *list;
2315 for (list = gtkaspell->suggestions_list; list != NULL;
2316 list = list->next)
2317 g_free(list->data);
2319 g_list_free(list);
2321 gtkaspell->max_sug = -1;
2322 gtkaspell->suggestions_list = NULL;
2325 static void reset_theword_data(GtkAspell *gtkaspell)
2327 gtkaspell->start_pos = 0;
2328 gtkaspell->end_pos = 0;
2329 gtkaspell->theword[0] = 0;
2330 gtkaspell->max_sug = -1;
2332 free_suggestions_list(gtkaspell);
2335 static void free_checkers(gpointer elt, gpointer data)
2337 GtkAspeller *gtkaspeller = elt;
2339 g_return_if_fail(gtkaspeller);
2341 gtkaspeller_real_delete(gtkaspeller);
2344 static gint find_gtkaspeller(gconstpointer aa, gconstpointer bb)
2346 Dictionary *a = ((GtkAspeller *) aa)->dictionary;
2347 Dictionary *b = ((GtkAspeller *) bb)->dictionary;
2349 if (a && b && a->fullname && b->fullname &&
2350 strcmp(a->fullname, b->fullname) == 0 &&
2351 a->encoding && b->encoding)
2352 return strcmp(a->encoding, b->encoding);
2354 return 1;
2357 static void gtkaspell_alert_dialog(gchar *message)
2359 GtkWidget *dialog;
2360 GtkWidget *hbox;
2361 GtkWidget *label;
2362 GtkWidget *ok_button;
2364 dialog = gtk_dialog_new();
2365 gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE);
2366 gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_MOUSE);
2367 gtk_signal_connect_object(GTK_OBJECT(dialog), "destroy",
2368 GTK_SIGNAL_FUNC(gtk_widget_destroy),
2369 GTK_OBJECT(dialog));
2371 label = gtk_label_new(message);
2372 gtk_misc_set_padding(GTK_MISC(label), 8, 8);
2374 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
2376 hbox = gtk_hbox_new(FALSE, 0);
2378 ok_button = gtk_button_new_with_label(_("OK"));
2379 GTK_WIDGET_SET_FLAGS(ok_button, GTK_CAN_DEFAULT);
2380 gtk_box_pack_start(GTK_BOX(hbox), ok_button, TRUE, TRUE, 8);
2382 gtk_signal_connect_object(GTK_OBJECT(ok_button), "clicked",
2383 GTK_SIGNAL_FUNC(gtk_widget_destroy),
2384 GTK_OBJECT(dialog));
2385 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), hbox);
2387 gtk_widget_grab_default(ok_button);
2388 gtk_widget_grab_focus(ok_button);
2389 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
2391 gtk_widget_show_all(dialog);
2393 #endif