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