Made spelling more friendly
[claws.git] / src / gtkspell.c
1 /* gtkpspell - a spell-checking addon for GtkText
2  * Copyright (c) 2000 Evan Martin.
3  * Copyright (c) 2002 Melvin Hadasht.
4  *
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.
9  *
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.
14  *
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
18  */
19 /*
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  */
25  
26 #if defined(HAVE_CONFIG_H)
27 #include "config.h"
28 #endif
29
30 #if USE_PSPELL
31 #include "intl.h"
32
33 #include <gtk/gtk.h>
34 #include <gdk/gdk.h>
35 #include <sys/types.h>
36 #include <sys/wait.h>
37 #include <unistd.h>
38 #include <stdio.h>
39 #include <signal.h>
40 #include <ctype.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <errno.h>
44 #include <stdio.h>
45 #include <sys/time.h>
46 #include <fcntl.h>
47 #include <time.h>
48 #include <prefs_common.h>
49 #include <utils.h>
50
51 #include <dirent.h>
52
53 #include <gtk/gtkoptionmenu.h>
54 #include <gtk/gtkmenu.h>
55 #include <gtk/gtkmenuitem.h>
56 #include <gdk/gdkkeysyms.h>
57
58 #include "gtkstext.h"
59
60 #include "gtkspell.h"
61
62 #include <pspell/pspell.h>
63 /* size of the text buffer used in various word-processing routines. */
64 #define BUFSIZE 1024
65
66 /* number of suggestions to display on each menu. */
67 #define MENUCOUNT 15
68
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)); \
73                 return FALSE; \
74         }
75
76 #define CONFIG_REPLACE_RETURN_FALSE_IF_FAIL(option, value) { \
77         pspell_config_replace(config, option, value);       \
78         RETURN_FALSE_IF_CONFIG_ERROR();                      \
79         }
80
81 /******************************************************************************/
82
83 GtkPspellCheckers *gtkpspellcheckers;
84
85 /* Error message storage */
86 static void             gtkpspell_checkers_error_message(gchar *message);
87
88 /* Callbacks */
89 static void             entry_insert_cb                 (GtkSText *gtktext, 
90                                                          gchar *newtext, 
91                                                          guint len, 
92                                                          guint *ppos, 
93                                                          GtkPspell *gtkpspell);
94 static void             entry_delete_cb                 (GtkSText *gtktext, 
95                                                          gint start, 
96                                                          gint end,
97                                                          GtkPspell *gtkpspell);
98 static gint             button_press_intercept_cb       (GtkSText *gtktext, 
99                                                          GdkEvent *e, 
100                                                          GtkPspell *gtkpspell);
101
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);
107
108 /* Checker configuration */
109 static gint             set_dictionary                  (PspellConfig *config, 
110                                                          Dictionary *dict);
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);
115
116 /* Checker actions */
117 static gboolean         check_at                        (GtkPspell *gtkpspell, 
118                                                          int from_pos);
119 static gboolean         check_next_prev                 (GtkPspell *gtkpspell, 
120                                                          gboolean forward);
121 static GList*           misspelled_suggest              (GtkPspell *gtkpspell, 
122                                                          guchar *word);
123 static void             add_word_to_session             (GtkWidget *w, 
124                                                          GtkPspell *d);
125 static void             add_word_to_personal            (GtkWidget *w, 
126                                                          GtkPspell *d);
127 static void             replace_word                    (GtkWidget *w, 
128                                                          GtkPspell *gtkpspell); 
129 static void             replace_real_word               (GtkPspell *gtkpspell, 
130                                                          gchar *newword);
131 static void             check_all_cb                    (GtkWidget *w, 
132                                                          GtkPspell *gtkpspell);
133 static void             toggle_check_while_typing       (GtkWidget *w, 
134                                                          gpointer data);
135
136 /* Menu creation */
137 static void             popup_menu                      (GtkPspell *gtkpspell, 
138                                                          GdkEventButton *eb);
139 static GtkMenu*         make_sug_menu                   (GtkPspell *gtkpspell);
140 static void             populate_submenu                (GtkPspell *gtkpspell, 
141                                                          GtkWidget *menu);
142 static GtkMenu*         make_config_menu                (GtkPspell *gtkpspell);
143 static void             set_menu_pos                    (GtkMenu *menu, 
144                                                          gint *x, 
145                                                          gint *y, 
146                                                          gpointer data);
147 /* Other menu callbacks */
148 static gboolean         deactivate_menu_cb              (GtkWidget *w, 
149                                                          gpointer *data);
150 static gboolean         cancel_menu_cb                  (GtkMenuShell *w,
151                                                          gpointer data);
152 static void             change_dict_cb                  (GtkWidget *w, 
153                                                          GtkPspell *gtkpspell);
154
155 /* Misc. helper functions */
156 static void continue_check(gpointer *gtkpspell);
157 static gboolean         iswordsep                       (unsigned char c);
158 static guchar           get_text_index_whar             (GtkPspell *gtkpspell, 
159                                                          int pos);
160 static gboolean         get_word_from_pos               (GtkPspell *gtkpspell, 
161                                                          int pos, 
162                                                          unsigned char* buf,
163                                                          int *pstart, int *pend);
164 static void             allocate_color                  (GtkPspell *gtkpspell);
165 static void             change_color                    (GtkPspell *gtkpspell, 
166                                                          int start, 
167                                                          int end, 
168                                                          GdkColor *color);
169 static guchar*          convert_to_pspell_encoding      (const guchar *encoding);
170 static gint             compare_dict                    (Dictionary *a, 
171                                                          Dictionary *b);
172 static void             dictionary_delete               (Dictionary *dict);
173 static Dictionary *     dictionary_dup                  (const Dictionary *dict);
174 static void             free_suggestions_list           (GtkPspell *gtkpspell);
175 static void             reset_theword_data              (GtkPspell *gtkpspell);
176 static void             free_checkers                   (gpointer elt, 
177                                                          gpointer data);
178 static gint             find_gtkpspeller                (gconstpointer aa, 
179                                                          gconstpointer bb);
180 static void             gtkpspell_alert_dialog          (gchar *message);
181 /* gtkspellconfig - only one config per session */
182 GtkPspellConfig * gtkpspellconfig;
183
184 /******************************************************************************/
185
186 GtkPspellCheckers * gtkpspell_checkers_new()
187 {
188         GtkPspellCheckers *gtkpspellcheckers;
189         
190         gtkpspellcheckers = g_new(GtkPspellCheckers, 1);
191         
192         gtkpspellcheckers->checkers        = NULL;
193
194         gtkpspellcheckers->dictionary_list = NULL;
195
196         gtkpspellcheckers->error_message   = NULL;
197         
198         return gtkpspellcheckers;
199 }
200         
201 GtkPspellCheckers *gtkpspell_checkers_delete()
202 {
203         GSList *checkers;
204         GSList *dict_list;
205
206         if (gtkpspellcheckers == NULL) return NULL;
207
208         if ((checkers  = gtkpspellcheckers->checkers)) {
209                 debug_print(_("Pspell: number of running checkers to delete %d\n"),
210                                 g_slist_length(checkers));
211
212                 g_slist_foreach(checkers, free_checkers, NULL);
213                 g_slist_free(checkers);
214         }
215
216         if ((dict_list = gtkpspellcheckers->dictionary_list)) {
217                 debug_print(_("Pspell: number of dictionaries to delete %d\n"),
218                                 g_slist_length(dict_list));
219
220                 gtkpspell_free_dictionary_list(dict_list);
221                 gtkpspellcheckers->dictionary_list = NULL;
222         }
223
224         g_free(gtkpspellcheckers->error_message);
225
226         return NULL;
227 }
228
229 static void gtkpspell_checkers_error_message (gchar *message)
230 {
231         gchar *tmp;
232         if (gtkpspellcheckers->error_message) {
233                 tmp = g_strdup_printf("%s\n%s", 
234                                       gtkpspellcheckers->error_message, message);
235                 g_free(message);
236                 g_free(gtkpspellcheckers->error_message);
237                 gtkpspellcheckers->error_message = tmp;
238         } else gtkpspellcheckers->error_message = message;
239 }
240
241 void gtkpspell_checkers_reset()
242 {
243         g_return_if_fail(gtkpspellcheckers);
244         
245         g_free(gtkpspellcheckers->error_message);
246         
247         gtkpspellcheckers->error_message = NULL;
248 }
249
250 GtkPspell *gtkpspell_new(const gchar *dictionary, 
251                          const gchar *encoding, 
252                          gboolean check_while_typing,
253                          GtkSText *gtktext)
254 {
255         Dictionary      *dict = g_new0(Dictionary, 1);
256         GtkPspell       *gtkpspell;
257         GtkPspeller     *gtkpspeller;
258
259         g_return_val_if_fail(gtktext, NULL);
260         
261         dict->fullname = g_strdup(dictionary);
262         dict->encoding = g_strdup(encoding);
263         gtkpspeller = gtkpspeller_new(dict); 
264         dictionary_delete(dict);
265
266         if (!gtkpspeller)
267                 return NULL;
268         
269         gtkpspell = g_new(GtkPspell, 1);
270
271         gtkpspell->gtkpspeller = gtkpspeller;
272         gtkpspell->theword[0]  = 0x00;
273         gtkpspell->start_pos   = 0;
274         gtkpspell->end_pos     = 0;
275         gtkpspell->orig_pos    = -1;
276         
277         gtkpspell->check_while_typing = check_while_typing;
278
279         gtkpspell->gtktext     = gtktext;
280
281         gtkpspell->default_sug_mode   = PSPELL_FASTMODE;
282         gtkpspell->max_sug            = -1;
283         gtkpspell->suggestions_list   = NULL;
284
285         gtk_signal_connect_after(GTK_OBJECT(gtktext), "insert-text",
286                                  GTK_SIGNAL_FUNC(entry_insert_cb), gtkpspell);
287
288         gtk_signal_connect_after(GTK_OBJECT(gtktext), "delete-text",
289                                  GTK_SIGNAL_FUNC(entry_delete_cb), gtkpspell);
290         
291         gtk_signal_connect(GTK_OBJECT(gtktext), "button-press-event",
292                            GTK_SIGNAL_FUNC(button_press_intercept_cb), gtkpspell);
293         
294         allocate_color(gtkpspell);
295         
296         return gtkpspell;
297 }
298
299 void gtkpspell_delete(GtkPspell * gtkpspell) 
300 {
301         GtkSText * gtktext;
302         
303         gtktext = gtkpspell->gtktext;
304
305         gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
306                                       GTK_SIGNAL_FUNC(entry_insert_cb), gtkpspell);
307         gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
308                                       GTK_SIGNAL_FUNC(entry_delete_cb), gtkpspell);
309         gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
310                                       GTK_SIGNAL_FUNC(button_press_intercept_cb), 
311                                       gtkpspell);
312
313         gtkpspell_uncheck_all(gtkpspell);
314         
315         gtkpspeller_delete(gtkpspell->gtkpspeller);
316
317         g_free(gtkpspell);
318         gtkpspell = NULL;
319 }
320
321 static void entry_insert_cb(GtkSText *gtktext, gchar *newtext, 
322                             guint len, guint *ppos, 
323                             GtkPspell * gtkpspell) 
324 {
325         g_return_if_fail(gtkpspell->gtkpspeller->checker);
326
327         if (!gtkpspell->check_while_typing)
328                 return;
329         
330         /* We must insert ourself the character to impose the */
331         /* color of the inserted character to be default */
332         /* Never mess with set_insertion when frozen */
333         gtk_stext_freeze(gtktext);
334         gtk_stext_backward_delete(GTK_STEXT(gtktext), len);
335         gtk_stext_insert(GTK_STEXT(gtktext), NULL, NULL, NULL, newtext, len);
336         *ppos = gtk_stext_get_point(GTK_STEXT(gtktext));
337                
338         if (iswordsep(newtext[0])) {
339                 /* did we just end a word? */
340                 if (*ppos >= 2) 
341                         check_at(gtkpspell, *ppos - 2);
342
343                 /* did we just split a word? */
344                 if (*ppos < gtk_stext_get_length(gtktext))
345                         check_at(gtkpspell, *ppos + 1);
346         } else {
347                 /* check as they type, *except* if they're typing at the end (the most
348                  * common case.
349                  */
350                 if (*ppos < gtk_stext_get_length(gtktext) 
351                 &&  !iswordsep(get_text_index_whar(gtkpspell, *ppos)))
352                         check_at(gtkpspell, *ppos - 1);
353                 }
354         gtk_stext_thaw(gtktext);
355         gtk_editable_set_position(GTK_EDITABLE(gtktext), *ppos);
356 }
357
358 static void entry_delete_cb(GtkSText *gtktext, gint start, gint end, 
359                             GtkPspell *gtkpspell) 
360 {
361         int origpos;
362     
363         g_return_if_fail(gtkpspell->gtkpspeller->checker);
364
365         if (!gtkpspell->check_while_typing)
366                 return;
367
368         origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
369         if (start) {
370                 check_at(gtkpspell, start - 1);
371                 check_at(gtkpspell, start);
372         }
373
374         gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
375         gtk_stext_set_point(gtktext, origpos);
376         gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos);
377         /* this is to *UNDO* the selection, in case they were holding shift
378          * while hitting backspace. */
379 }
380
381 /* ok, this is pretty wacky:
382  * we need to let the right-mouse-click go through, so it moves the cursor,
383  * but we *can't* let it go through, because GtkText interprets rightclicks as
384  * weird selection modifiers.
385  *
386  * so what do we do?  forge rightclicks as leftclicks, then popup the menu.
387  * HACK HACK HACK.
388  */
389 static gint button_press_intercept_cb(GtkSText *gtktext, GdkEvent *e, GtkPspell *gtkpspell) 
390 {
391         GdkEventButton *eb;
392         gboolean retval;
393
394         g_return_val_if_fail(gtkpspell->gtkpspeller->checker, FALSE);
395
396         if (e->type != GDK_BUTTON_PRESS) 
397                 return FALSE;
398         eb = (GdkEventButton*) e;
399
400         if (eb->button != 3) 
401                 return FALSE;
402
403         /* forge the leftclick */
404         eb->button = 1;
405
406         gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
407                                          GTK_SIGNAL_FUNC(button_press_intercept_cb), 
408                                          gtkpspell);
409         gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "button-press-event",
410                                 e, &retval);
411         gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
412                                            GTK_SIGNAL_FUNC(button_press_intercept_cb), 
413                                            gtkpspell);
414         gtk_signal_emit_stop_by_name(GTK_OBJECT(gtktext), "button-press-event");
415     
416         /* now do the menu wackiness */
417         popup_menu(gtkpspell, eb);
418         gtk_grab_remove(GTK_WIDGET(gtktext));
419         return TRUE;
420 }
421
422 /*****************************************************************************/
423 /* Checker creation */
424 static GtkPspeller *gtkpspeller_new(Dictionary *dictionary)
425 {
426         GSList          *checkers, *exist;
427         GtkPspeller     *gtkpspeller = NULL;
428         GtkPspeller     *g           = g_new(GtkPspeller, 1);
429         Dictionary      *dict;
430         gint            ispell;
431         gboolean        free_dict = TRUE;
432                 
433         g_return_val_if_fail(gtkpspellcheckers, NULL);
434
435         g_return_val_if_fail(dictionary, NULL);
436
437         if (dictionary->fullname == NULL)
438                 gtkpspell_checkers_error_message(g_strdup(_("No dictionary selected.")));
439         
440         g_return_val_if_fail(dictionary->fullname, NULL);
441         
442         if (dictionary->dictname == NULL) {
443                 gchar *tmp;
444
445                 tmp = strrchr(dictionary->fullname, G_DIR_SEPARATOR);
446
447                 if (tmp == NULL)
448                         dictionary->dictname = dictionary->fullname;
449                 else
450                         dictionary->dictname = tmp + 1;
451         }
452
453         dict = dictionary_dup(dictionary);
454
455         ispell = (strstr2(dict->dictname, "-ispell") != NULL);
456
457         g->dictionary = dict;
458
459         checkers = gtkpspellcheckers->checkers;
460
461         exist = g_slist_find_custom(checkers, g, find_gtkpspeller);
462         
463         g_free(g);
464
465         if (exist)
466                 gtkpspeller = (GtkPspeller *) exist->data;
467
468         if (exist && ispell)
469                 debug_print(_("Pspell: Using existing ispell checker %0x\n"),
470                             (gint) gtkpspeller);
471         else {
472                 if ((gtkpspeller = gtkpspeller_real_new(dict)) != NULL) {
473                         checkers = g_slist_append(checkers, gtkpspeller);
474                         
475                         debug_print(_("Pspell: Created a new gtkpspeller %0x\n"),
476                                     (gint) gtkpspeller);
477                         
478                         free_dict = FALSE;
479                 } else 
480                         debug_print(_("Pspell: Could not create spell checker.\n"));
481         }
482
483         debug_print(_("Pspell: number of existing checkers %d\n"), 
484                     g_slist_length(checkers));
485
486         if (free_dict)
487                 dictionary_delete(dict);
488
489         gtkpspellcheckers->checkers = checkers;
490
491         return gtkpspeller;
492 }
493
494 static GtkPspeller *gtkpspeller_real_new(Dictionary *dict)
495 {
496         GtkPspeller             *gtkpspeller;
497         PspellConfig            *config;
498         PspellCanHaveError      *ret;
499         
500         g_return_val_if_fail(gtkpspellcheckers, NULL);
501         g_return_val_if_fail(dict, NULL);
502
503         gtkpspeller = g_new(GtkPspeller, 1);
504         
505         gtkpspeller->dictionary = dict;
506         gtkpspeller->sug_mode   = PSPELL_FASTMODE;
507
508         config = new_pspell_config();
509
510         if (!set_dictionary(config, dict))
511                 return NULL;
512         
513         ret = new_pspell_manager(config);
514         delete_pspell_config(config);
515
516         if (pspell_error_number(ret) != 0) {
517                 gtkpspellcheckers->error_message = g_strdup(pspell_error_message(ret));
518                 
519                 delete_pspell_can_have_error(ret);
520                 
521                 return NULL;
522         }
523
524         gtkpspeller->checker = to_pspell_manager(ret);
525         gtkpspeller->config  = pspell_manager_config(gtkpspeller->checker);
526         gtkpspeller->ispell  = (strstr2(dict->fullname, "-ispell") != NULL);
527
528         return gtkpspeller;
529 }
530
531 static GtkPspeller *gtkpspeller_delete(GtkPspeller *gtkpspeller)
532 {
533         GSList *checkers;
534
535         g_return_val_if_fail(gtkpspellcheckers, NULL);
536         
537         checkers = gtkpspellcheckers->checkers;
538         
539         if (gtkpspeller->ispell) 
540                 debug_print(_("Pspell: Won't remove existing ispell checker %0x.\n"), 
541                             (gint) gtkpspeller);
542         else {
543                 checkers = g_slist_remove(checkers, gtkpspeller);
544
545                 gtkpspellcheckers->checkers = checkers;
546                 
547                 debug_print(_("Pspell: Deleting gtkpspeller %0x.\n"), 
548                             (gint) gtkpspeller);
549                 
550                 gtkpspeller_real_delete(gtkpspeller);
551         }
552
553         debug_print(_("Pspell: number of existing checkers %d\n"), 
554                     g_slist_length(checkers));
555
556         return gtkpspeller;
557 }
558
559 static GtkPspeller *gtkpspeller_real_delete(GtkPspeller *gtkpspeller)
560 {
561         g_return_val_if_fail(gtkpspeller,          NULL);
562         g_return_val_if_fail(gtkpspeller->checker, NULL);
563
564         pspell_manager_save_all_word_lists(gtkpspeller->checker);
565
566         delete_pspell_manager(gtkpspeller->checker);
567
568         dictionary_delete(gtkpspeller->dictionary);
569
570         g_free(gtkpspeller);
571
572         return NULL;
573 }
574
575 /*****************************************************************************/
576 /* Checker configuration */
577
578 static gboolean set_dictionary(PspellConfig *config, Dictionary *dict)
579 {
580         gchar *language = NULL;
581         gchar *spelling = NULL;
582         gchar *jargon   = NULL;
583         gchar *module   = NULL;
584         gchar *end      = NULL;
585         gchar buf[BUFSIZE];
586         
587         g_return_val_if_fail(config, FALSE);
588         g_return_val_if_fail(dict,   FALSE);
589
590         strncpy(buf, dict->fullname, BUFSIZE-1);
591         buf[BUFSIZE-1] = 0x00;
592
593         buf[dict->dictname - dict->fullname] = 0x00;
594
595         CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("rem-all-word-list-path", "");
596         debug_print(_("Pspell: removing all paths.\n"));
597
598         CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("add-word-list-path", buf);
599         debug_print(_("Pspell: adding path (%s).\n"), buf);
600
601         strncpy(buf, dict->dictname, BUFSIZE-1);
602         language = buf;
603         
604         if ((module = strrchr(buf, '-')) != NULL) {
605                 module++;
606         }
607         if ((spelling = strchr(language, '-')) != NULL) 
608                 *spelling++ = 0x00;
609         if (spelling != module) {
610                 if ((end = strchr(spelling, '-')) != NULL) {
611                         *end++ = 0x00;
612                         jargon = end;
613                         if (jargon != module)
614                                 if ((end = strchr(jargon, '-')) != NULL)
615                                         *end = 0x00;
616                                 else
617                                         jargon = NULL;
618                         else
619                                 jargon = NULL;
620                 }
621                 else
622                         spelling = NULL;
623         }
624         else
625                 spelling = NULL;
626
627         debug_print(_("Pspell: Language: %s, spelling: %s, jargon: %s, module: %s\n"),
628                     language, spelling, jargon, module);
629         
630         if (language)
631                 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("language-tag", language);
632         
633         if (spelling)
634                 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("spelling",     spelling);
635         
636         if (jargon)
637                 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("jargon",       jargon);
638         
639         if (module) {
640                 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("rem-all-module-search-order", "");
641                 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("add-module-search-order", module);
642         }
643         
644         if (dict->encoding) {
645                 gchar *pspell_enc;
646         
647                 pspell_enc = convert_to_pspell_encoding (dict->encoding);
648                 pspell_config_replace(config, "encoding", (const char *) pspell_enc);
649                 g_free(pspell_enc);
650
651                 RETURN_FALSE_IF_CONFIG_ERROR();
652         }
653         
654         return TRUE;
655 }
656
657 guchar *gtkpspell_get_dict(GtkPspell *gtkpspell)
658 {
659
660         g_return_val_if_fail(gtkpspell->gtkpspeller->config,     NULL);
661         g_return_val_if_fail(gtkpspell->gtkpspeller->dictionary, NULL);
662         
663         return g_strdup(gtkpspell->gtkpspeller->dictionary->dictname);
664 }
665   
666 guchar *gtkpspell_get_path(GtkPspell *gtkpspell)
667 {
668         guchar *path;
669         Dictionary *dict;
670
671         g_return_val_if_fail(gtkpspell->gtkpspeller->config, NULL);
672         g_return_val_if_fail(gtkpspell->gtkpspeller->dictionary, NULL);
673
674         dict = gtkpspell->gtkpspeller->dictionary;
675         path = g_strndup(dict->fullname, dict->dictname - dict->fullname);
676
677         return path;
678 }
679
680 /* set_sug_mode_cb() - Menu callback : Set the suggestion mode */
681 static void set_sug_mode_cb(GtkWidget *w, GtkPspell *gtkpspell)
682 {
683         unsigned char *themode;
684         
685         if (gtkpspell->gtkpspeller->ispell) return;
686
687         gtk_label_get(GTK_LABEL(GTK_BIN(w)->child), (gchar **) &themode);
688         
689         set_real_sug_mode(gtkpspell, themode);
690
691         if (gtkpspell->config_menu)
692                 populate_submenu(gtkpspell, gtkpspell->config_menu);
693 }
694 static void set_real_sug_mode(GtkPspell *gtkpspell, const char *themode)
695 {
696         gint result;
697         g_return_if_fail(gtkpspell);
698         g_return_if_fail(gtkpspell->gtkpspeller);
699         g_return_if_fail(themode);
700
701         if (!strcmp(themode, _("Fast Mode"))) {
702                 result = gtkpspell_set_sug_mode(gtkpspell, PSPELL_FASTMODE);
703         } else if (!strcmp(themode,_("Normal Mode"))) {
704                 result = gtkpspell_set_sug_mode(gtkpspell, PSPELL_NORMALMODE);
705         } else if (!strcmp( themode,_("Bad Spellers Mode"))) {
706                 result = gtkpspell_set_sug_mode(gtkpspell, PSPELL_BADSPELLERMODE);
707         }
708
709         if(!result) {
710                 debug_print(_("Pspell: error while changing suggestion mode:%s\n"),
711                             gtkpspellcheckers->error_message);
712                 gtkpspell_checkers_reset();
713         }
714 }
715   
716 /* gtkpspell_set_sug_mode() - Set the suggestion mode */
717 gboolean gtkpspell_set_sug_mode(GtkPspell *gtkpspell, gint themode)
718 {
719         PspellConfig *config;
720         g_return_val_if_fail(gtkpspell, FALSE);
721         g_return_val_if_fail(gtkpspell->gtkpspeller, FALSE);
722         g_return_val_if_fail(gtkpspell->gtkpspeller->config, FALSE);
723
724         config = gtkpspell->gtkpspeller->config;
725         switch (themode) {
726                 case PSPELL_FASTMODE: 
727                         CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "fast");
728                         break;
729                 case PSPELL_NORMALMODE: 
730                         CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "normal");
731                         break;
732                 case PSPELL_BADSPELLERMODE: 
733                         CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", 
734                                                             "bad-spellers");
735                         break;
736                 default: 
737                         gtkpspellcheckers->error_message = 
738                                 g_strdup(_("Unknown suggestion mode."));
739                         return FALSE;
740                 }
741
742         gtkpspell->gtkpspeller->sug_mode = themode;
743         gtkpspell->default_sug_mode      = themode;
744
745         return TRUE;
746 }
747
748 /* misspelled_suggest() - Create a suggestion list for  word  */
749 static GList *misspelled_suggest(GtkPspell *gtkpspell, guchar *word) 
750 {
751         const guchar          *newword;
752         GList                 *list = NULL;
753         const PspellWordList  *suggestions;
754         PspellStringEmulation *elements;
755
756         g_return_val_if_fail(word, NULL);
757
758         if (!pspell_manager_check(gtkpspell->gtkpspeller->checker, word, -1)) {
759                 free_suggestions_list(gtkpspell);
760
761                 suggestions = pspell_manager_suggest(gtkpspell->gtkpspeller->checker, 
762                                                      (const char *)word, -1);
763                 elements    = pspell_word_list_elements(suggestions);
764                 list        = g_list_append(list, g_strdup(word)); 
765                 
766                 while ((newword = pspell_string_emulation_next(elements)) != NULL)
767                         list = g_list_append(list, g_strdup(newword));
768
769                 gtkpspell->max_sug          = g_list_length(list) - 1;
770                 gtkpspell->suggestions_list = list;
771
772                 return list;
773         }
774
775         free_suggestions_list(gtkpspell);
776
777         return NULL;
778 }
779
780 /* misspelled_test() - Just test if word is correctly spelled */  
781 static int misspelled_test(GtkPspell *gtkpspell, unsigned char *word) 
782 {
783         return pspell_manager_check(gtkpspell->gtkpspeller->checker, word, -1) ? 0 : 1; 
784 }
785
786
787 static gboolean iswordsep(unsigned char c) 
788 {
789         return !isalpha(c) && c != '\'';
790 }
791
792 static guchar get_text_index_whar(GtkPspell *gtkpspell, int pos) 
793 {
794         guchar a;
795         gchar *text;
796         
797         text = gtk_editable_get_chars(GTK_EDITABLE(gtkpspell->gtktext), pos, pos + 1);
798         if (text == NULL) 
799                 return 0;
800         a = (guchar) *text;
801         g_free(text);
802         return a;
803 }
804
805 /* get_word_from_pos () - return the word pointed to. */
806 /* Handles correctly the quotes. */
807 static gboolean get_word_from_pos(GtkPspell *gtkpspell, int pos, 
808                                   unsigned char* buf,
809                                   int *pstart, int *pend) 
810 {
811
812         /* TODO : when correcting a word into quotes, change the color of */
813         /* the quotes too, as may be they were highlighted before. To do  */
814         /* this, we can use two others pointers that points to the whole    */
815         /* word including quotes. */
816
817         gint      start, 
818                   end;
819         guchar    c;
820         GtkSText *gtktext;
821         
822         gtktext = gtkpspell->gtktext;
823         if (iswordsep(get_text_index_whar(gtkpspell, pos))) 
824                 return FALSE;
825         
826         /* The apostrophe character is somtimes used for quotes 
827          * So include it in the word only if it is not surrounded 
828          * by other characters. 
829          */
830          
831         for (start = pos; start >= 0; --start) {
832                 c = get_text_index_whar(gtkpspell, start);
833                 if (c == '\'') {
834                         if (start > 0) {
835                                 if (!isalpha(get_text_index_whar(gtkpspell, start - 1))) {
836                                         /* start_quote = TRUE; */
837                                         break;
838                                 }
839                         }
840                         else {
841                                 /* start_quote = TRUE; */
842                                 break;
843                         }
844                 }
845                 else
846                         if (!isalpha(c))
847                                 break;
848         }
849         start++;
850
851         for (end = pos; end < gtk_stext_get_length(gtktext); end++) {
852                 c = get_text_index_whar(gtkpspell, end); 
853                 if (c == '\'') {
854                         if (end < gtk_stext_get_length(gtktext)) {
855                                 if (!isalpha(get_text_index_whar(gtkpspell, end + 1))) {
856                                         /* end_quote = TRUE; */
857                                         break;
858                                 }
859                         }
860                         else {
861                                 /* end_quote = TRUE; */
862                                 break;
863                         }
864                 }
865                 else
866                         if(!isalpha(c))
867                                 break;
868         }
869                                                 
870         if (buf) {
871                 for (pos = start; pos < end; pos++) 
872                         buf[pos - start] = get_text_index_whar(gtkpspell, pos);
873                 buf[pos - start] = 0;
874         }
875
876         if (pstart) 
877                 *pstart = start;
878         if (pend) 
879                 *pend = end;
880
881         return TRUE;
882 }
883
884 static gboolean check_at(GtkPspell *gtkpspell, int from_pos) 
885 {
886         int           start, end;
887         unsigned char buf[BUFSIZE];
888         GtkSText     *gtktext;
889
890         g_return_val_if_fail(from_pos >= 0, FALSE);
891     
892         gtktext = gtkpspell->gtktext;
893
894         if (!get_word_from_pos(gtkpspell, from_pos, buf, &start, &end))
895                 return FALSE;
896
897
898         if (misspelled_test(gtkpspell, buf)) {
899                 strncpy(gtkpspell->theword, buf, GTKPSPELLWORDSIZE - 1);
900                 gtkpspell->theword[GTKPSPELLWORDSIZE - 1] = 0;
901                 gtkpspell->start_pos  = start;
902                 gtkpspell->end_pos    = end;
903                 gtkpspell->newword[0] = 0;
904                 free_suggestions_list(gtkpspell);
905
906                 change_color(gtkpspell, start, end, &(gtkpspell->highlight));
907                 
908                 return TRUE;
909         } else {
910                 change_color(gtkpspell, start, end, NULL);
911                 return FALSE;
912         }
913 }
914
915 static void check_all_cb(GtkWidget *w, GtkPspell *gtkpspell)
916 {
917         gtkpspell_check_all(gtkpspell);
918 }
919 #if 0 /* Old code */
920 void gtkpspell_check_all(GtkPspell *gtkpspell) 
921 {
922         guint     origpos;
923         guint     pos = 0;
924         guint     len;
925         float     adj_value;
926         GtkSText *gtktext;
927
928         g_return_if_fail(gtkpspell->gtkpspeller->checker);      
929
930         gtktext = gtkpspell->gtktext;
931
932         len = gtk_stext_get_length(gtktext);
933
934         adj_value = gtktext->vadj->value;
935         gtk_stext_freeze(gtktext);
936         origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
937         gtk_editable_set_position(GTK_EDITABLE(gtktext),0);
938         while (pos < len) {
939                 while (pos < len && iswordsep(get_text_index_whar(gtkpspell, pos)))
940                         pos++;
941                 while (pos < len && !iswordsep(get_text_index_whar(gtkpspell, pos)))
942                         pos++;
943                 if (pos > 0)
944                         check_at(gtkpspell, pos - 1);
945         }
946         gtk_stext_thaw(gtktext);
947         gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
948         gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos);
949 }
950 #endif
951
952 static gboolean check_next_prev(GtkPspell *gtkpspell, gboolean forward)
953 {
954         gint pos;
955         gint minpos;
956         gint maxpos;
957         gint direc = -1;
958         gboolean misspelled;
959         GdkEvent *event= (GdkEvent *) gtk_get_current_event();
960         
961         minpos = 0;
962         maxpos = gtkpspell->last_check_pos;
963
964         if (forward) {
965                 minpos = -1;
966                 direc = -direc;
967                 maxpos--;
968         } 
969
970         pos = gtk_editable_get_position(GTK_EDITABLE(gtkpspell->gtktext));
971         gtkpspell->orig_pos = pos;
972         while (iswordsep(get_text_index_whar(gtkpspell, pos)) && pos > minpos && pos <= maxpos) 
973                 pos += direc;
974         while (!(misspelled = check_at(gtkpspell, pos)) && pos > minpos && pos <= maxpos)
975         {
976                 while (!iswordsep(get_text_index_whar(gtkpspell, pos)) && pos > minpos && pos <= maxpos)
977                         pos += direc;
978
979                 while (iswordsep(get_text_index_whar(gtkpspell, pos)) && pos > minpos && pos <= maxpos) 
980                         pos += direc;
981         }
982         if (misspelled) {
983                 misspelled_suggest(gtkpspell, gtkpspell->theword);
984                 if (forward)
985                         gtkpspell->orig_pos = gtkpspell->end_pos;
986                 gtk_stext_set_point(GTK_STEXT(gtkpspell->gtktext),
987                                 gtkpspell->end_pos);
988                 gtk_editable_set_position(GTK_EDITABLE(gtkpspell->gtktext),
989                                 gtkpspell->end_pos);
990                 gtk_menu_popup(make_sug_menu(gtkpspell), NULL, NULL, 
991                                 set_menu_pos, gtkpspell, 0, gdk_event_get_time(event));
992         } else {
993                 reset_theword_data(gtkpspell);
994
995                 gtkpspell_alert_dialog(_("No misspelled word found."));
996                 gtk_stext_set_point(GTK_STEXT(gtkpspell->gtktext), gtkpspell->orig_pos);
997                 gtk_editable_set_position(GTK_EDITABLE(gtkpspell->gtktext), gtkpspell->orig_pos);
998         }
999         return misspelled;
1000 }
1001
1002 void gtkpspell_check_backwards(GtkPspell *gtkpspell)
1003 {
1004         gtkpspell->continue_check = NULL;
1005         check_next_prev(gtkpspell, FALSE);
1006 }
1007
1008 void gtkpspell_check_forwards_go(GtkPspell *gtkpspell)
1009 {
1010
1011         gtkpspell->continue_check = NULL;
1012         gtkpspell->last_check_pos = gtk_stext_get_length(GTK_STEXT(gtkpspell->gtktext));
1013         check_next_prev(gtkpspell, TRUE);
1014 }
1015
1016 void gtkpspell_check_all(GtkPspell *gtkpspell)
1017 {       
1018         GtkWidget *gtktext;
1019         
1020         gint start, end, pos;
1021
1022         g_return_if_fail(gtkpspell);
1023         g_return_if_fail(gtkpspell->gtktext);
1024
1025         gtktext = (GtkWidget *) gtkpspell->gtktext;
1026
1027         start = 0;      
1028         end   = gtk_stext_get_length(GTK_STEXT(gtktext));
1029
1030         if (GTK_EDITABLE(gtktext)->has_selection) {
1031                 start = GTK_EDITABLE(gtktext)->selection_start_pos;
1032                 end   = GTK_EDITABLE(gtktext)->selection_end_pos;
1033         }
1034         
1035         gtk_editable_set_position(GTK_EDITABLE(gtktext), start);
1036         gtk_stext_set_point(GTK_STEXT(gtktext), start);
1037
1038         gtkpspell->continue_check = continue_check;
1039         gtkpspell->last_check_pos = end;
1040
1041         gtkpspell->misspelled = check_next_prev(gtkpspell, TRUE);
1042
1043 }       
1044
1045 static void continue_check(gpointer *data)
1046 {
1047         GtkPspell *gtkpspell = (GtkPspell *) data;
1048         gint pos = gtk_editable_get_position(GTK_EDITABLE(gtkpspell->gtktext));
1049         
1050         if (pos < gtkpspell->last_check_pos && gtkpspell->misspelled)
1051                 gtkpspell->misspelled = check_next_prev(gtkpspell, TRUE);
1052         else
1053                 gtkpspell->continue_check = NULL;
1054                 
1055 }
1056
1057 static void replace_with_cb(GtkWidget *w, GtkPspell *gtkpspell) 
1058 {
1059         unsigned char *newword;
1060         GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1061
1062         newword = gtk_editable_get_chars(GTK_EDITABLE(gtkpspell->replace_entry), 0, -1);
1063         if (strcmp(newword, gtkpspell->theword)) {
1064                 replace_real_word(gtkpspell, newword);
1065
1066                 if ((e->type == GDK_KEY_PRESS && 
1067                     ((GdkEventKey *) e)->state & GDK_MOD1_MASK)) {
1068                         pspell_manager_store_replacement(gtkpspell->gtkpspeller->checker, 
1069                                                          gtkpspell->theword, -1, 
1070                                                          newword, -1);
1071                 }
1072                 gtkpspell->newword[0] = 0x00;
1073                 gtkpspell->replace_entry = NULL;
1074         }
1075         g_free(newword);
1076 }
1077
1078
1079 static void replace_word(GtkWidget *w, GtkPspell *gtkpspell) 
1080 {
1081         unsigned char *newword;
1082         GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1083
1084         gtk_label_get(GTK_LABEL(GTK_BIN(w)->child), (gchar**) &newword);
1085         replace_real_word(gtkpspell, newword);
1086         if ((e->type == GDK_KEY_PRESS && ((GdkEventKey *) e)->state & GDK_MOD1_MASK) ||
1087             (e->type == GDK_BUTTON_RELEASE && ((GdkEventButton *) e)->state & GDK_MOD1_MASK)) {
1088                 pspell_manager_store_replacement(gtkpspell->gtkpspeller->checker, 
1089                                                  gtkpspell->theword, -1, 
1090                                                  newword, -1);
1091         }
1092         gtkpspell->newword[0] = 0x00;
1093         gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1094 }
1095
1096 static void replace_real_word(GtkPspell *gtkpspell, gchar *newword)
1097 {
1098         int             oldlen, newlen, wordlen;
1099         gint            origpos;
1100         gint            pos;
1101         gint            start = gtkpspell->start_pos;
1102         GtkSText       *gtktext;
1103     
1104         if (!newword) return;
1105
1106         gtktext = gtkpspell->gtktext;
1107
1108         gtk_stext_freeze(GTK_STEXT(gtktext));
1109         origpos = gtkpspell->orig_pos;
1110         pos     = origpos;
1111         oldlen  = gtkpspell->end_pos - gtkpspell->start_pos;
1112         wordlen = strlen(gtkpspell->theword);
1113
1114         strncpy(gtkpspell->newword, newword, GTKPSPELLWORDSIZE - 1);
1115         gtkpspell->newword[GTKPSPELLWORDSIZE-1] = 0x00;
1116         newlen = strlen(newword); /* FIXME: multybyte characters? */
1117
1118         gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
1119                         GTK_SIGNAL_FUNC(entry_insert_cb), 
1120                         gtkpspell);
1121         gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
1122                         GTK_SIGNAL_FUNC(entry_delete_cb), 
1123                         gtkpspell);
1124         gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "delete-text", 
1125                                 gtkpspell->start_pos, gtkpspell->end_pos);
1126         gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "insert-text", 
1127                                 newword, newlen, &start);
1128         
1129         gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
1130                         GTK_SIGNAL_FUNC(entry_insert_cb), 
1131                         gtkpspell);
1132         gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
1133                         GTK_SIGNAL_FUNC(entry_delete_cb), 
1134                         gtkpspell);
1135         
1136         /* Put the point and the position where we clicked with the mouse */
1137         /* It seems to be a hack, as I must thaw,freeze,thaw the widget   */
1138         /* to let it update correctly the word insertion and then the     */
1139         /* point & position position. If not, SEGV after the first replacement */
1140         /* If the new word ends before point, put the point at its end*/
1141     
1142         if (origpos - gtkpspell->start_pos < oldlen && origpos - gtkpspell->start_pos >= 0) {
1143                 /* Original point was in the word. */
1144                 /* Let it there unless point is going to be outside of the word */
1145                 if (origpos - gtkpspell->start_pos >= newlen) {
1146                         pos = gtkpspell->start_pos + newlen;
1147                 }
1148         }
1149         else if (origpos >= gtkpspell->end_pos) {
1150                 /* move the position according to the change of length */
1151                 pos = origpos + newlen - oldlen;
1152         }
1153         
1154         gtkpspell->end_pos = gtkpspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1155         
1156         gtk_stext_thaw(GTK_STEXT(gtktext));
1157         gtk_stext_freeze(GTK_STEXT(gtktext));
1158         if (GTK_STEXT(gtktext)->text_len < pos)
1159                 pos = gtk_stext_get_length(GTK_STEXT(gtktext));
1160         gtkpspell->orig_pos = pos;
1161         gtk_editable_set_position(GTK_EDITABLE(gtktext), gtkpspell->orig_pos);
1162         gtk_stext_set_point(GTK_STEXT(gtktext), 
1163                             gtk_editable_get_position(GTK_EDITABLE(gtktext)));
1164         gtk_stext_thaw(GTK_STEXT(gtktext));
1165 }
1166
1167 /* Accept this word for this session */
1168
1169 static void add_word_to_session(GtkWidget *w, GtkPspell *gtkpspell)
1170 {
1171         guint     pos;
1172         GtkSText *gtktext;
1173     
1174         gtktext = gtkpspell->gtktext;
1175
1176         gtk_stext_freeze(GTK_STEXT(gtktext));
1177
1178         pos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
1179     
1180         pspell_manager_add_to_session(gtkpspell->gtkpspeller->checker,gtkpspell->theword, strlen(gtkpspell->theword));
1181
1182         check_at(gtkpspell, gtkpspell->start_pos);
1183
1184         gtk_stext_thaw(gtkpspell->gtktext);
1185         gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1186 }
1187
1188 /* add_word_to_personal() - add word to personal dict. */
1189 static void add_word_to_personal(GtkWidget *w, GtkPspell *gtkpspell)
1190 {
1191         GtkSText *gtktext;
1192     
1193         gtktext = gtkpspell->gtktext;
1194
1195         gtk_stext_freeze(GTK_STEXT(gtktext));
1196     
1197         pspell_manager_add_to_personal(gtkpspell->gtkpspeller->checker,gtkpspell->theword, strlen(gtkpspell->theword));
1198     
1199         check_at(gtkpspell, gtkpspell->start_pos);
1200
1201         gtk_stext_thaw(gtkpspell->gtktext);
1202         gtk_menu_shell_deactivate(GTK_MENU_SHELL(w->parent));
1203 }
1204
1205 static void deactivate_menu(GtkWidget *w, gpointer data)
1206 {
1207         GtkMenuShell *menu_shell = (GtkMenuShell *) data;
1208         gtk_menu_shell_deactivate(menu_shell);
1209 }
1210
1211 static void replace_with(GtkWidget *w, GtkPspell *gtkpspell)
1212 {
1213         GtkWidget *dialog;
1214         GtkWidget *label;
1215         GtkWidget *hbox;
1216         GtkWidget *entry;
1217         GtkWidget *ok_button;
1218         GtkWidget *cancel_button;
1219         gchar *thelabel;
1220         gint xx, yy;
1221
1222         gdk_window_get_origin(GTK_WIDGET(w->parent)->window, &xx, &yy);
1223
1224         gtk_widget_hide(w->parent);
1225
1226         dialog = gtk_dialog_new();
1227         gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE);
1228         gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1229         gtk_widget_set_uposition(dialog, xx, yy);
1230
1231         gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
1232                           GTK_SIGNAL_FUNC(deactivate_menu), 
1233                           w->parent);
1234         gtk_signal_connect_object(GTK_OBJECT(dialog), "destroy",
1235                                   GTK_SIGNAL_FUNC(gtk_widget_destroy), 
1236                                   GTK_OBJECT(dialog));
1237
1238         hbox = gtk_hbox_new(FALSE, 0);
1239         gtk_container_set_border_width(GTK_CONTAINER(hbox), 8);
1240
1241         thelabel = g_strdup_printf(_("Replace \"%s\" with: "), 
1242                                    gtkpspell->theword);
1243         label = gtk_label_new(thelabel);
1244         g_free(thelabel);
1245         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1246
1247         entry = gtk_entry_new();
1248         gtkpspell->replace_entry = entry;
1249         gtk_entry_set_text(GTK_ENTRY(entry), gtkpspell->theword);
1250         gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1251         gtk_signal_connect(GTK_OBJECT(entry), "activate",
1252                            GTK_SIGNAL_FUNC(replace_with_cb), 
1253                            gtkpspell);
1254         gtk_signal_connect_object(GTK_OBJECT(entry), "activate",
1255                            GTK_SIGNAL_FUNC(gtk_widget_destroy), 
1256                            GTK_OBJECT(dialog));
1257         gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
1258
1259         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 0);
1260         if (!gtkpspell->gtkpspeller->ispell) {
1261                 label = gtk_label_new(_("Holding down MOD1 key while pressing Enter\nwill learn from mistake.\n"));
1262                 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1263                 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
1264                 gtk_misc_set_padding(GTK_MISC(label), 8, 0);
1265                 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
1266         }
1267
1268         hbox = gtk_hbox_new(TRUE, 0);
1269
1270         ok_button = gtk_button_new_with_label(_("OK"));
1271         gtk_box_pack_start(GTK_BOX(hbox), ok_button, TRUE, TRUE, 8);
1272         gtk_signal_connect(GTK_OBJECT(ok_button), "clicked",
1273                         GTK_SIGNAL_FUNC(replace_with_cb), 
1274                         gtkpspell);
1275         gtk_signal_connect_object(GTK_OBJECT(ok_button), "clicked",
1276                         GTK_SIGNAL_FUNC(gtk_widget_destroy), 
1277                         GTK_OBJECT(dialog));
1278
1279         cancel_button = gtk_button_new_with_label(_("Cancel"));
1280         gtk_box_pack_start(GTK_BOX(hbox), cancel_button, TRUE, TRUE, 8);
1281         gtk_signal_connect_object(GTK_OBJECT(cancel_button), "clicked",
1282                         GTK_SIGNAL_FUNC(gtk_widget_destroy), 
1283                         GTK_OBJECT(dialog));
1284
1285         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), hbox);
1286
1287         gtk_widget_grab_focus(entry);
1288
1289         gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1290
1291         gtk_widget_show_all(dialog);
1292 }
1293
1294 void gtkpspell_uncheck_all(GtkPspell * gtkpspell) 
1295 {
1296         int origpos;
1297         unsigned char *text;
1298         float adj_value;
1299         GtkSText *gtktext;
1300         
1301         gtktext=gtkpspell->gtktext;
1302
1303         adj_value = gtktext->vadj->value;
1304         gtk_stext_freeze(gtktext);
1305         origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
1306         text = gtk_editable_get_chars(GTK_EDITABLE(gtktext), 0, -1);
1307         gtk_stext_set_point(gtktext, 0);
1308         gtk_stext_forward_delete(gtktext, gtk_stext_get_length(gtktext));
1309         gtk_stext_insert(gtktext, NULL, NULL, NULL, text, strlen(text));
1310         gtk_stext_thaw(gtktext);
1311
1312         g_free(text);
1313
1314         gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
1315         gtk_adjustment_set_value(gtktext->vadj, adj_value);
1316 }
1317
1318 static void toggle_check_while_typing(GtkWidget *w, gpointer data)
1319 {
1320         GtkPspell *gtkpspell = (GtkPspell *) data;
1321
1322         gtkpspell->check_while_typing = gtkpspell->check_while_typing == FALSE;
1323         gtkpspell_uncheck_all(gtkpspell);
1324
1325         if (gtkpspell->config_menu)
1326                 populate_submenu(gtkpspell, gtkpspell->config_menu);
1327 }
1328
1329 static GSList *create_empty_dictionary_list(void)
1330 {
1331         GSList *list = NULL;
1332         Dictionary *dict;
1333
1334         dict = g_new0(Dictionary, 1);
1335         dict->fullname = g_strdup(_("None"));
1336         dict->dictname = dict->fullname;
1337         dict->encoding = NULL;
1338         return g_slist_append(list, dict);
1339 }
1340
1341 /* gtkpspell_get_dictionary_list() - returns list of dictionary names */
1342 GSList *gtkpspell_get_dictionary_list(const gchar *pspell_path, gint refresh)
1343 {
1344         GSList *list;
1345         gchar *dict_path, *tmp, *prevdir;
1346         gchar tmpname[BUFSIZE];
1347         Dictionary *dict;
1348         DIR *dir;
1349         struct dirent *ent;
1350
1351         if (!gtkpspellcheckers)
1352                 gtkpspellcheckers = gtkpspell_checkers_new();
1353
1354         if (gtkpspellcheckers->dictionary_list && !refresh)
1355                 return gtkpspellcheckers->dictionary_list;
1356         else
1357                 gtkpspell_free_dictionary_list(gtkpspellcheckers->dictionary_list);
1358
1359         list = NULL;
1360
1361 #ifdef USE_THREADS
1362 #warning TODO: no directory change
1363 #endif
1364         dict_path = g_strdup(pspell_path);
1365         prevdir = g_get_current_dir();
1366         if (chdir(dict_path) <0) {
1367                 debug_print(_("Error when searching for dictionaries:\n%s\n"),
1368                                           g_strerror(errno));
1369                 g_free(prevdir);
1370                 g_free(dict_path);
1371                 gtkpspellcheckers->dictionary_list = create_empty_dictionary_list();
1372                 return gtkpspellcheckers->dictionary_list; 
1373         }
1374
1375         debug_print(_("Checking for dictionaries in %s\n"), dict_path);
1376
1377         if (NULL != (dir = opendir("."))) {
1378                 while (NULL != (ent = readdir(dir))) {
1379                         /* search for pwli */
1380                         if ((NULL != (tmp = strstr2(ent->d_name, ".pwli"))) && (tmp[5] == 0x00)) {
1381                                 g_snprintf(tmpname, BUFSIZE, "%s%s", G_DIR_SEPARATOR_S, ent->d_name);
1382                                 tmpname[MIN(tmp - ent->d_name + 1, BUFSIZE-1)] = 0x00;
1383                                 dict = g_new0(Dictionary, 1);
1384                                 dict->fullname = g_strdup_printf("%s%s", dict_path, tmpname);
1385                                 dict->dictname = strrchr(dict->fullname, G_DIR_SEPARATOR) + 1;
1386                                 dict->encoding = NULL;
1387                                 debug_print(_("Found dictionary %s %s\n"), dict->fullname, dict->dictname);
1388                                 list = g_slist_insert_sorted(list, dict, (GCompareFunc) compare_dict);
1389                         }
1390                 }                       
1391                 closedir(dir);
1392         }
1393         else {
1394                 debug_print(_("Error when searching for dictionaries.\nNo dictionary found.\n(%s)"), 
1395                                           g_strerror(errno));
1396                 list = create_empty_dictionary_list();
1397         }
1398         if(list==NULL){
1399                 
1400                 debug_print(_("Error when searching for dictionaries.\nNo dictionary found."));
1401                 list = create_empty_dictionary_list();
1402         }
1403         chdir(prevdir);
1404         g_free(dict_path);
1405         g_free(prevdir);
1406         gtkpspellcheckers->dictionary_list = list;
1407         return list;
1408 }
1409
1410 void gtkpspell_free_dictionary_list(GSList *list)
1411 {
1412         Dictionary *dict;
1413         GSList *walk;
1414         for (walk = list; walk != NULL; walk = g_slist_next(walk))
1415                 if (walk->data) {
1416                         dict = (Dictionary *) walk->data;
1417                         dictionary_delete(dict);
1418                 }                               
1419         g_slist_free(list);
1420 }
1421
1422 #if 0
1423 static void dictionary_option_menu_item_data_destroy(gpointer data)
1424 {
1425         Dictionary *dict = (Dictionary *) data;
1426
1427         if (dict)
1428                 dictionary_delete(dict);
1429 }
1430 #endif
1431
1432 GtkWidget *gtkpspell_dictionary_option_menu_new(const gchar *pspell_path)
1433 {
1434         GSList *dict_list, *tmp;
1435         GtkWidget *item;
1436         GtkWidget *menu;
1437         Dictionary *dict;
1438
1439         dict_list = gtkpspell_get_dictionary_list(pspell_path, TRUE);
1440         g_return_val_if_fail(dict_list, NULL);
1441
1442         menu = gtk_menu_new();
1443         
1444         for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1445                 dict = (Dictionary *) tmp->data;
1446                 item = gtk_menu_item_new_with_label(dict->dictname);
1447                 gtk_object_set_data(GTK_OBJECT(item), "dict_name",
1448                                     dict->fullname); 
1449                                          
1450                 gtk_menu_append(GTK_MENU(menu), item);                                   
1451                 gtk_widget_show(item);
1452         }
1453
1454         gtk_widget_show(menu);
1455
1456         return menu;
1457 }
1458
1459 gchar *gtkpspell_get_dictionary_menu_active_item(GtkWidget *menu)
1460 {
1461         GtkWidget *menuitem;
1462         gchar *dict_fullname;
1463         gchar *label;
1464
1465         g_return_val_if_fail(GTK_IS_MENU(menu), NULL);
1466         menuitem = gtk_menu_get_active(GTK_MENU(menu));
1467         dict_fullname = (gchar *) gtk_object_get_data(GTK_OBJECT(menuitem), "dict_name");
1468         g_return_val_if_fail(dict_fullname, NULL);
1469         label = g_strdup(dict_fullname);
1470         return label;
1471   
1472 }
1473
1474 GtkWidget *gtkpspell_sugmode_option_menu_new(gint sugmode)
1475 {
1476         GtkWidget *menu;
1477         GtkWidget *item;
1478
1479
1480         menu = gtk_menu_new();
1481         gtk_widget_show(menu);
1482
1483         item = gtk_menu_item_new_with_label(_("Fast Mode"));
1484         gtk_widget_show(item);
1485         gtk_menu_append(GTK_MENU(menu), item);
1486         gtk_object_set_data(GTK_OBJECT(item), "sugmode", GINT_TO_POINTER(PSPELL_FASTMODE));
1487
1488         item = gtk_menu_item_new_with_label(_("Normal Mode"));
1489         gtk_widget_show(item);
1490         gtk_menu_append(GTK_MENU(menu), item);
1491         gtk_object_set_data(GTK_OBJECT(item), "sugmode", GINT_TO_POINTER(PSPELL_NORMALMODE));
1492         
1493         item = gtk_menu_item_new_with_label(_("Bad Spellers Mode"));
1494         gtk_widget_show(item);
1495         gtk_menu_append(GTK_MENU(menu), item);
1496         gtk_object_set_data(GTK_OBJECT(item), "sugmode", GINT_TO_POINTER(PSPELL_BADSPELLERMODE));
1497
1498         return menu;
1499 }
1500         
1501 void gtkpspell_sugmode_option_menu_set(GtkOptionMenu *optmenu, gint sugmode)
1502 {
1503         g_return_if_fail(GTK_IS_OPTION_MENU(optmenu));
1504
1505         g_return_if_fail(sugmode == PSPELL_FASTMODE ||
1506                          sugmode == PSPELL_NORMALMODE ||
1507                          sugmode == PSPELL_BADSPELLERMODE);
1508
1509         gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), sugmode - 1);
1510 }
1511
1512 gint gtkpspell_get_sugmode_from_option_menu(GtkOptionMenu *optmenu)
1513 {
1514         gint sugmode;
1515         GtkWidget *item;
1516         
1517         g_return_val_if_fail(GTK_IS_OPTION_MENU(optmenu), -1);
1518
1519         item = gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(optmenu)));
1520         
1521         sugmode = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item), "sugmode"));
1522
1523         return sugmode;
1524         
1525 }
1526
1527 void gtkpspell_set_primary_dict(GtkPspell *gtkpspell, gchar *dict, gchar *encoding)
1528 {
1529         g_return_if_fail(gtkpspell);
1530         if (gtkpspell->dict1)
1531                 dictionary_delete(gtkpspell->dict1);
1532         gtkpspell->dict1 = g_new(Dictionary, 1);
1533         gtkpspell->dict1->fullname = g_strdup(dict);
1534         gtkpspell->dict1->encoding = g_strdup(encoding);
1535 }
1536
1537 void gtkpspell_set_secondary_dict(GtkPspell *gtkpspell, gchar *dict, gchar *encoding)
1538 {
1539         g_return_if_fail(gtkpspell);
1540         if (gtkpspell->dict2)
1541                 dictionary_delete(gtkpspell->dict2);
1542         gtkpspell->dict2 = g_new(Dictionary, 1);
1543         gtkpspell->dict2->fullname = g_strdup(dict);
1544         gtkpspell->dict2->encoding = g_strdup(encoding);
1545 }
1546
1547 gboolean gtkpspell_use_dict(GtkPspell *gtkpspell, Dictionary *dict)
1548 {
1549         return TRUE;
1550 }
1551
1552 gboolean gtkpspell_use_dictionary(GtkPspell *gtkpspell, gchar *dictpath, gchar *encoding)
1553 {
1554         Dictionary *dict;
1555         gboolean retval;
1556         
1557         g_return_val_if_fail(gtkpspell, FALSE);
1558         g_return_val_if_fail(dict, FALSE);
1559         g_return_val_if_fail(encoding, FALSE);
1560
1561         dict = g_new(Dictionary, 1);
1562         
1563         dict->fullname = g_strdup(dictpath);
1564         dict->encoding = NULL; /* To be continued */
1565
1566         retval = gtkpspell_use_dict(gtkpspell, dict);
1567
1568         dictionary_delete(dict);
1569         
1570         return retval;
1571         
1572 }
1573
1574 gboolean gtkpspell_use_primary(GtkPspell *gtkpspell)
1575 {
1576         g_return_val_if_fail(gtkpspell, FALSE);
1577
1578         return gtkpspell_use_dict(gtkpspell, gtkpspell->dict1);
1579 }
1580
1581 gboolean gtkpspell_use_secondary(GtkPspell *gtkpspell)
1582 {
1583         g_return_val_if_fail(gtkpspell, FALSE);
1584
1585         return gtkpspell_use_dict(gtkpspell, gtkpspell->dict2);
1586 }
1587
1588 /*****************************************************************************/
1589
1590 static void popup_menu(GtkPspell *gtkpspell, GdkEventButton *eb) 
1591 {
1592         GtkSText * gtktext;
1593         
1594         gtktext = gtkpspell->gtktext;
1595         gtkpspell->orig_pos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
1596
1597         if (!(eb->state & GDK_SHIFT_MASK)) {
1598                 if (check_at(gtkpspell, gtkpspell->orig_pos)) {
1599
1600                         gtk_editable_set_position(GTK_EDITABLE(gtktext), 
1601                                                   gtkpspell->orig_pos);
1602                         gtk_stext_set_point(gtktext, gtkpspell->orig_pos);
1603
1604                         if (misspelled_suggest(gtkpspell, gtkpspell->theword)) {
1605                                 gtk_menu_popup(make_sug_menu(gtkpspell), 
1606                                                NULL, NULL, NULL, NULL,
1607                                                eb->button, eb->time);
1608                                 
1609                                 return;
1610                         }
1611                 } else {
1612                         gtk_editable_set_position(GTK_EDITABLE(gtktext), 
1613                                                   gtkpspell->orig_pos);
1614                         gtk_stext_set_point(gtktext, gtkpspell->orig_pos);
1615                 }
1616         }
1617
1618         gtk_menu_popup(make_config_menu(gtkpspell),NULL,NULL,NULL,NULL,
1619                        eb->button,eb->time);
1620 }
1621
1622 /* make_sug_menu() - Add menus to accept this word for this session and to add it to 
1623  * personal dictionary */
1624 static GtkMenu *make_sug_menu(GtkPspell *gtkpspell) 
1625 {
1626         GtkWidget       *menu, *item;
1627         unsigned char   *caption;
1628         GtkSText        *gtktext;
1629         GList           *l = gtkpspell->suggestions_list;
1630         GtkAccelGroup   *accel;
1631         
1632
1633         gtktext = gtkpspell->gtktext;
1634
1635         accel = gtk_accel_group_new();
1636         menu = gtk_menu_new(); 
1637         
1638         gtk_signal_connect(GTK_OBJECT(menu), "deactivate",
1639                 GTK_SIGNAL_FUNC(deactivate_menu_cb), gtkpspell);
1640         gtk_signal_connect(GTK_OBJECT(menu), "cancel",
1641                 GTK_SIGNAL_FUNC(cancel_menu_cb), gtkpspell);
1642
1643         caption = g_strdup_printf(_("Unknown word: \"%s\""), 
1644                                   (unsigned char*) l->data);
1645         item = gtk_menu_item_new_with_label(caption);
1646         gtk_widget_show(item);
1647         gtk_menu_append(GTK_MENU(menu), item);
1648         gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
1649         g_free(caption);
1650
1651         item = gtk_menu_item_new();
1652         gtk_widget_show(item);
1653         gtk_menu_append(GTK_MENU(menu), item);
1654
1655         item = gtk_menu_item_new_with_label(_("Accept in this session"));
1656         gtk_widget_show(item);
1657         gtk_menu_append(GTK_MENU(menu), item);
1658         gtk_signal_connect(GTK_OBJECT(item), "activate",
1659                            GTK_SIGNAL_FUNC(add_word_to_session), 
1660                            gtkpspell);
1661         gtk_accel_group_add(accel, GDK_space, GDK_MOD1_MASK, 
1662                             GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE, 
1663                             GTK_OBJECT(item), "activate");
1664
1665         item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1666         gtk_widget_show(item);
1667         gtk_menu_append(GTK_MENU(menu), item);
1668         gtk_signal_connect(GTK_OBJECT(item), "activate",
1669                            GTK_SIGNAL_FUNC(add_word_to_personal), 
1670                            gtkpspell);
1671         gtk_accel_group_add(accel, GDK_Return, GDK_MOD1_MASK, 
1672                             GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE, 
1673                             GTK_OBJECT(item), "activate");
1674         
1675         item = gtk_menu_item_new_with_label(_("Replace with..."));
1676         gtk_widget_show(item);
1677         gtk_menu_append(GTK_MENU(menu), item);
1678         gtk_signal_connect(GTK_OBJECT(item), "activate",
1679                            GTK_SIGNAL_FUNC(replace_with), 
1680                            gtkpspell);
1681         gtk_accel_group_add(accel, 'r', 0, 
1682                             GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE, 
1683                             GTK_OBJECT(item), "activate");
1684         
1685         item = gtk_menu_item_new();
1686         gtk_widget_show(item);
1687         gtk_menu_append(GTK_MENU(menu), item);
1688
1689         l = l->next;
1690         if (l == NULL) {
1691                 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
1692                 gtk_widget_show(item);
1693                 gtk_menu_append(GTK_MENU(menu), item);
1694         } else {
1695                 GtkWidget *curmenu = menu;
1696                 gint count = 0;
1697                 
1698                 do {
1699                         if (l->data == NULL && l->next != NULL) {
1700                                 count = 0;
1701                                 curmenu = gtk_menu_new();
1702                                 item = gtk_menu_item_new_with_label(_("Others..."));
1703                                 gtk_widget_show(item);
1704                                 gtk_menu_append(GTK_MENU(curmenu), item);
1705                                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), curmenu);
1706
1707                                 l = l->next;
1708                         } else if (count > MENUCOUNT) {
1709                                 count -= MENUCOUNT;
1710                                 item = gtk_menu_item_new_with_label(_("More..."));
1711                                 gtk_widget_show(item);
1712                                 gtk_menu_append(GTK_MENU(curmenu), item);
1713                                 curmenu = gtk_menu_new();
1714                                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), curmenu);
1715                         }
1716                         item = gtk_menu_item_new_with_label((unsigned char*)l->data);
1717                         gtk_widget_show(item);
1718                         gtk_menu_append(GTK_MENU(curmenu), item);
1719                         gtk_signal_connect(GTK_OBJECT(item), "activate",
1720                                            GTK_SIGNAL_FUNC(replace_word), gtkpspell);
1721                         if (count <= MENUCOUNT) {
1722                                 gtk_accel_group_add(accel, 'a' + count, 0, 
1723                                         GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE, 
1724                                         GTK_OBJECT(item), "activate");
1725                                 gtk_accel_group_add(accel, 'a' + count, 
1726                                         GDK_MOD1_MASK, GTK_ACCEL_LOCKED, 
1727                                         GTK_OBJECT(item), "activate");
1728                                 }
1729
1730
1731                         count++;
1732                 } while ((l = l->next) != NULL);
1733         }
1734         
1735         gtk_accel_group_attach(accel, GTK_OBJECT(menu));
1736         
1737         return GTK_MENU(menu);
1738 }
1739
1740 static void populate_submenu(GtkPspell *gtkpspell, GtkWidget *menu)
1741 {
1742         GtkWidget *item, *submenu;
1743         GtkPspeller *gtkpspeller = gtkpspell->gtkpspeller;
1744         gchar *dictname;
1745         gint ispell = gtkpspeller->ispell;
1746
1747         if (GTK_MENU_SHELL(menu)->children) {
1748                 GList *amenu, *alist;
1749                 for (amenu = (GTK_MENU_SHELL(menu)->children); amenu; ) {
1750                         alist = amenu->next;
1751                         item = GTK_WIDGET(amenu->data);
1752                         gtk_widget_destroy(item);
1753                         amenu = alist;
1754                 }
1755         }
1756         
1757         dictname = g_strdup_printf(_("Dictionary: %s"), gtkpspeller->dictionary->dictname);
1758         item = gtk_menu_item_new_with_label(dictname);
1759         gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
1760         g_free(dictname);
1761         gtk_widget_show(item);
1762         gtk_menu_append(GTK_MENU(menu), item);
1763
1764         item = gtk_menu_item_new();
1765         gtk_widget_show(item);
1766         gtk_menu_append(GTK_MENU(menu), item);
1767                 
1768         item = gtk_check_menu_item_new_with_label(_("Fast Mode"));
1769         if (ispell || gtkpspell->gtkpspeller->sug_mode == PSPELL_FASTMODE)
1770                 gtk_widget_set_sensitive(GTK_WIDGET(item),FALSE);
1771         if (!ispell && gtkpspell->gtkpspeller->sug_mode == PSPELL_FASTMODE)
1772                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),TRUE);
1773         else
1774                 gtk_signal_connect(GTK_OBJECT(item), "activate",
1775                                 GTK_SIGNAL_FUNC(set_sug_mode_cb),
1776                                 gtkpspell);
1777         gtk_widget_show(item);
1778         gtk_menu_append(GTK_MENU(menu), item);
1779
1780         item = gtk_check_menu_item_new_with_label(_("Normal Mode"));
1781         if (ispell || gtkpspell->gtkpspeller->sug_mode == PSPELL_NORMALMODE)
1782                 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
1783         if (!ispell && gtkpspell->gtkpspeller->sug_mode == PSPELL_NORMALMODE) 
1784                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1785         else
1786                 gtk_signal_connect(GTK_OBJECT(item), "activate",
1787                                 GTK_SIGNAL_FUNC(set_sug_mode_cb),
1788                                 gtkpspell);
1789         gtk_widget_show(item);
1790         gtk_menu_append(GTK_MENU(menu),item);
1791
1792         item = gtk_check_menu_item_new_with_label(_("Bad Spellers Mode"));
1793         if (ispell || gtkpspell->gtkpspeller->sug_mode == PSPELL_BADSPELLERMODE)
1794                 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
1795         if (!ispell && gtkpspell->gtkpspeller->sug_mode == PSPELL_BADSPELLERMODE)
1796                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1797         else
1798                 gtk_signal_connect(GTK_OBJECT(item), "activate",
1799                                 GTK_SIGNAL_FUNC(set_sug_mode_cb),
1800                                 gtkpspell);
1801         gtk_widget_show(item);
1802         gtk_menu_append(GTK_MENU(menu), item);
1803         
1804         item = gtk_menu_item_new();
1805         gtk_widget_show(item);
1806         gtk_menu_append(GTK_MENU(menu), item);
1807         
1808         item = gtk_check_menu_item_new_with_label(_("Check while typing"));
1809         if (gtkpspell->check_while_typing)
1810                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1811         else    
1812                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1813         gtk_signal_connect(GTK_OBJECT(item), "activate",
1814                            GTK_SIGNAL_FUNC(toggle_check_while_typing),
1815                            gtkpspell);
1816         gtk_widget_show(item);
1817         gtk_menu_append(GTK_MENU(menu), item);
1818
1819         item = gtk_menu_item_new();
1820         gtk_widget_show(item);
1821         gtk_menu_append(GTK_MENU(menu), item);
1822
1823         submenu = gtk_menu_new();
1824         item = gtk_menu_item_new_with_label(_("Change dictionary"));
1825         gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
1826         gtk_widget_show(item);
1827         gtk_menu_append(GTK_MENU(menu), item);
1828
1829         /* Dict list */
1830         if (gtkpspellcheckers->dictionary_list == NULL)
1831                 gtkpspell_get_dictionary_list(prefs_common.pspell_path, FALSE);
1832         {
1833                 GtkWidget * curmenu = submenu;
1834                 int count = 0;
1835                 Dictionary *dict;
1836                 GSList *tmp;
1837                 tmp = gtkpspellcheckers->dictionary_list;
1838                 
1839                 for (tmp = gtkpspellcheckers->dictionary_list; tmp != NULL; 
1840                                 tmp = g_slist_next(tmp)) {
1841                         dict = (Dictionary *) tmp->data;
1842                         item = gtk_check_menu_item_new_with_label(dict->dictname);
1843                         gtk_object_set_data(GTK_OBJECT(item), "dict_name", dict->fullname); 
1844                         if (strcmp2(dict->fullname, gtkpspell->gtkpspeller->dictionary->fullname))
1845                                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1846                         else {
1847                                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1848                                 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
1849                         }
1850                         gtk_signal_connect(GTK_OBJECT(item), "activate",
1851                                            GTK_SIGNAL_FUNC(change_dict_cb),
1852                                            gtkpspell);
1853                         gtk_widget_show(item);
1854                         gtk_menu_append(GTK_MENU(curmenu), item);
1855                         
1856                         count++;
1857                         
1858                         if (count == MENUCOUNT) {
1859                                 GtkWidget *newmenu;
1860                                 
1861                                 newmenu = gtk_menu_new();
1862                                 item = gtk_menu_item_new_with_label(_("More..."));
1863                                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), 
1864                                                           newmenu);
1865                                 
1866                                 gtk_menu_append(GTK_MENU(curmenu), item);
1867                                 gtk_widget_show(item);
1868                                 curmenu = newmenu;
1869                                 count = 0;
1870                         }
1871                 }
1872         }  
1873
1874         
1875         
1876
1877 }
1878
1879 static GtkMenu *make_config_menu(GtkPspell *gtkpspell)
1880 {
1881         GtkWidget *menu, *item, *submenu;
1882         gint ispell = gtkpspell->gtkpspeller->ispell;
1883   
1884         menu = gtk_menu_new();
1885         
1886         populate_submenu(gtkpspell, menu);
1887
1888         return GTK_MENU(menu);
1889 }
1890
1891 void gtkpspell_populate_submenu(GtkPspell *gtkpspell, GtkWidget *menuitem)
1892 {
1893         GtkWidget *menu;
1894         menu = GTK_WIDGET(GTK_MENU_ITEM(menuitem)->submenu);
1895         populate_submenu(gtkpspell, menu);
1896         gtkpspell->config_menu = menu;
1897         
1898 }
1899
1900 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y, gpointer data)
1901 {
1902         GtkPspell       *gtkpspell = (GtkPspell *) data;
1903         gint             xx = 0, yy = 0;
1904         gint             sx,     sy;
1905         gint             wx,     wy;
1906         GtkSText        *text = GTK_STEXT(gtkpspell->gtktext);
1907         GtkRequisition   r;
1908
1909         gdk_window_get_origin(GTK_WIDGET(gtkpspell->gtktext)->window, &xx, &yy);
1910         
1911         sx = gdk_screen_width();
1912         sy = gdk_screen_height();
1913         
1914         gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
1915         
1916         wx =  r.width;
1917         wy =  r.height;
1918         
1919         *x = gtkpspell->gtktext->cursor_pos_x + xx +
1920              gdk_char_width(GTK_WIDGET(text)->style->font, ' ');
1921         *y = gtkpspell->gtktext->cursor_pos_y + yy;
1922
1923         if (*x + wx > sx)
1924                 *x = sx - wx;
1925         if (*y + wy > sy)
1926                 *y = *y - wy - 
1927                      gdk_string_height((GTK_WIDGET(gtkpspell->gtktext))->style->font, 
1928                                        gtkpspell->theword);
1929
1930 }
1931
1932 /*************************************************************************/
1933 /* Menu call backs */
1934
1935 static gboolean deactivate_menu_cb(GtkWidget *w, gpointer *data)
1936 {
1937         GtkPspell *gtkpspell = (GtkPspell *) data;
1938         GtkSText *gtktext;
1939         gtktext = gtkpspell->gtktext;
1940
1941         gtk_stext_freeze(gtktext);
1942         gtk_editable_set_position(GTK_EDITABLE(gtktext),gtkpspell->orig_pos);
1943         gtk_stext_set_point(gtktext, gtkpspell->orig_pos);
1944         gtk_stext_thaw(gtktext);
1945
1946         if (gtkpspell->continue_check)
1947                 gtkpspell->continue_check((gpointer *) gtkpspell);
1948
1949         return FALSE;
1950 }
1951
1952 static gboolean cancel_menu_cb(GtkMenuShell *w, gpointer data)
1953 {
1954         GtkPspell *gtkpspell = (GtkPspell *) data;
1955         gtkpspell->continue_check = NULL;
1956         return FALSE;
1957         
1958 }
1959 /* change_dict_cb() - Menu callback : change dict */
1960 static void change_dict_cb(GtkWidget *w, GtkPspell *gtkpspell)
1961 {
1962         Dictionary      *dict;       
1963         gchar           *fullname;
1964         GtkPspeller     *gtkpspeller;
1965         gint             sug_mode;
1966   
1967         fullname = (gchar *) gtk_object_get_data(GTK_OBJECT(w), "dict_name");
1968         
1969         if (!strcmp2(fullname, _("None")))
1970                         return;
1971
1972         sug_mode  = gtkpspell->default_sug_mode;
1973
1974         dict = g_new0(Dictionary, 1);
1975         dict->fullname = g_strdup(fullname);
1976         dict->encoding = g_strdup(gtkpspell->gtkpspeller->dictionary->encoding);
1977         
1978         gtkpspeller = gtkpspeller_new(dict);
1979
1980         if (!gtkpspeller) {
1981                 gchar *message;
1982                 message = g_strdup_printf(_("The spell checker could not change dictionary.\n%s"), 
1983                                           gtkpspellcheckers->error_message);
1984
1985                 gtkpspell_alert_dialog(message); 
1986                 g_free(message);
1987         } else {
1988                 gtkpspeller_delete(gtkpspell->gtkpspeller);
1989                 gtkpspell->gtkpspeller = gtkpspeller;
1990                 gtkpspell_set_sug_mode(gtkpspell, sug_mode);
1991         }
1992         
1993         dictionary_delete(dict);
1994
1995         if (gtkpspell->config_menu)
1996                 populate_submenu(gtkpspell, gtkpspell->config_menu);
1997
1998 }
1999
2000 /******************************************************************************/
2001 /* Misc. helper functions */
2002
2003 static void allocate_color(GtkPspell *gtkpspell)
2004 {
2005         GdkColormap *gc;
2006         GdkColor *color = &(gtkpspell->highlight);
2007         gint rgbvalue   = prefs_common.misspelled_col;
2008
2009         gc = gtk_widget_get_colormap(GTK_WIDGET(gtkpspell->gtktext));
2010
2011         if (gtkpspell->highlight.pixel)
2012                 gdk_colormap_free_colors(gc, &(gtkpspell->highlight), 1);
2013
2014         /* Shameless copy from Sylpheed gtkutils.c */
2015         color->pixel = 0L;
2016         color->red   = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0) * 65535.0);
2017         color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >>  8) / 255.0) * 65535.0);
2018         color->blue  = (int) (((gdouble) (rgbvalue & 0x0000ff)        / 255.0) * 65535.0);
2019
2020         gdk_colormap_alloc_color(gc, &(gtkpspell->highlight), FALSE, TRUE);
2021 }
2022
2023 static void change_color(GtkPspell * gtkpspell, 
2024                          int start, int end, 
2025                          GdkColor *color) 
2026 {
2027         char     *newtext;
2028         GtkSText *gtktext;
2029
2030         g_return_if_fail(start < end);
2031     
2032         gtktext = gtkpspell->gtktext;
2033     
2034         gtk_stext_freeze(gtktext);
2035         newtext = gtk_editable_get_chars(GTK_EDITABLE(gtktext), start, end);
2036         if (newtext) {
2037                 gtk_stext_set_point(gtktext, start);
2038                 gtk_stext_forward_delete(gtktext, end - start);
2039
2040                 gtk_stext_insert(gtktext, NULL, color, NULL, newtext, end - start);
2041                 g_free(newtext);
2042         }
2043         gtk_stext_thaw(gtktext);
2044 }
2045
2046 /* convert_to_pspell_encoding () - converts ISO-8859-* strings to iso8859-* 
2047  * as needed by pspell. Returns an allocated string.
2048  */
2049
2050 static guchar *convert_to_pspell_encoding (const guchar *encoding)
2051 {
2052         guchar * pspell_encoding;
2053
2054         if (strstr2(encoding, "ISO-8859-")) {
2055                 pspell_encoding = g_strdup_printf("iso8859%s", encoding+8);
2056         }
2057         else
2058                 if (!strcmp2(encoding, "US-ASCII"))
2059                         pspell_encoding = g_strdup("iso8859-1");
2060                 else
2061                         pspell_encoding = g_strdup(encoding);
2062
2063         return pspell_encoding;
2064 }
2065
2066 /* compare_dict () - compare 2 dict names */
2067 static gint compare_dict(Dictionary *a, Dictionary *b)
2068 {
2069         guint   aparts = 0,  bparts = 0;
2070         guint   i;
2071
2072         for (i=0; i < strlen(a->dictname); i++)
2073                 if (a->dictname[i] == '-')
2074                         aparts++;
2075         for (i=0; i < strlen(b->dictname); i++)
2076                 if (b->dictname[i] == '-')
2077                         bparts++;
2078
2079         if (aparts != bparts) 
2080                 return (aparts < bparts) ? -1 : +1;
2081         else {
2082                 gint compare;
2083                 compare = strcmp2(a->dictname, b->dictname);
2084                 if (!compare)
2085                         compare = strcmp2(a->fullname, b->fullname);
2086                 return compare;
2087         }
2088 }
2089
2090
2091 static void dictionary_delete(Dictionary *dict)
2092 {
2093         g_free(dict->fullname);
2094         g_free(dict->encoding);
2095         g_free(dict);
2096 }
2097
2098 static Dictionary *dictionary_dup(const Dictionary *dict)
2099 {
2100         Dictionary *dict2;
2101
2102         dict2 = g_new(Dictionary, 1); 
2103
2104         dict2->fullname = g_strdup(dict->fullname);
2105         dict2->dictname = dict->dictname - dict->fullname + dict2->fullname;
2106         dict2->encoding = g_strdup(dict->encoding);
2107
2108         return dict2;
2109 }
2110
2111 static void free_suggestions_list(GtkPspell *gtkpspell)
2112 {
2113         GList *list;
2114
2115         for (list = gtkpspell->suggestions_list; list != NULL; list = list->next)
2116                 g_free(list->data);
2117         g_list_free(list);
2118         
2119         gtkpspell->max_sug          = -1;
2120         gtkpspell->suggestions_list = NULL;
2121  
2122 }
2123
2124 static void reset_theword_data(GtkPspell *gtkpspell)
2125 {
2126         gtkpspell->start_pos     =  0;
2127         gtkpspell->end_pos       =  0;
2128         gtkpspell->theword[0]    =  0;
2129         gtkpspell->max_sug       = -1;
2130         gtkpspell->newword[0]    =  0;
2131
2132         free_suggestions_list(gtkpspell);
2133 }
2134
2135 static void free_checkers(gpointer elt, gpointer data)
2136 {
2137         GtkPspeller *gtkpspeller = elt;
2138
2139         g_return_if_fail(gtkpspeller);
2140
2141         gtkpspeller_real_delete(gtkpspeller);
2142 }
2143
2144 static gint find_gtkpspeller(gconstpointer aa, gconstpointer bb)
2145 {
2146         Dictionary *a = ((GtkPspeller *) aa)->dictionary;
2147         Dictionary *b = ((GtkPspeller *) bb)->dictionary;
2148
2149         if (!a || !b)
2150                 return 1;
2151
2152         if (strcmp(a->fullname, b->fullname) == 0
2153             && a->encoding && b->encoding)
2154                 return strcmp(a->encoding, b->encoding);
2155
2156         return 1;
2157 }
2158
2159 static void gtkpspell_alert_dialog(gchar *message)
2160 {
2161         GtkWidget *dialog;
2162         GtkWidget *hbox;
2163         GtkWidget *label;
2164         GtkWidget *ok_button;
2165
2166         dialog = gtk_dialog_new();
2167         gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE);
2168         gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_MOUSE);
2169         gtk_signal_connect_object(GTK_OBJECT(dialog), "destroy",
2170                                    GTK_SIGNAL_FUNC(gtk_widget_destroy), 
2171                                    GTK_OBJECT(dialog));
2172
2173         label  = gtk_label_new(message);
2174         gtk_misc_set_padding(GTK_MISC(label), 8, 8);
2175
2176         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
2177         
2178         hbox = gtk_hbox_new(FALSE, 0);
2179
2180         ok_button = gtk_button_new_with_label(_("OK"));
2181         GTK_WIDGET_SET_FLAGS(ok_button, GTK_CAN_DEFAULT);
2182         gtk_box_pack_start(GTK_BOX(hbox), ok_button, TRUE, TRUE, 8);    
2183
2184         gtk_signal_connect_object(GTK_OBJECT(ok_button), "clicked",
2185                                    GTK_SIGNAL_FUNC(gtk_widget_destroy), 
2186                                    GTK_OBJECT(dialog));
2187         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), hbox);
2188                         
2189         gtk_widget_grab_default(ok_button);
2190         gtk_widget_grab_focus(ok_button);
2191         gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
2192
2193         gtk_widget_show_all(dialog);
2194 }
2195 #endif