Fixed compile-time warnings
[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", (guint) 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", (guint) 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                         (guint) 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 check_with_alternate_cb(GtkWidget *w,
1267                                     gpointer data)
1268 {
1269         GtkPspell *gtkpspell = (GtkPspell *) data;
1270         gint misspelled;
1271         gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1272
1273         use_alternate_dict(gtkpspell);
1274         misspelled = check_at(gtkpspell, gtkpspell->start_pos);
1275
1276         if (!gtkpspell->continue_check) {
1277                 gtkpspell->misspelled = misspelled;
1278                 if (gtkpspell->misspelled) {
1279                         misspelled_suggest(gtkpspell, gtkpspell->theword);
1280                         gtk_stext_set_point(GTK_STEXT(gtkpspell->gtktext),
1281                                             gtkpspell->end_pos);
1282                         gtk_editable_set_position(GTK_EDITABLE(gtkpspell->gtktext),
1283                                                   gtkpspell->end_pos);
1284                         gtk_menu_popup(make_sug_menu(gtkpspell), NULL, NULL, 
1285                                        set_menu_pos, gtkpspell, 0, 
1286                                        GDK_CURRENT_TIME);
1287                         return;
1288                 }
1289         } else
1290                 gtkpspell->orig_pos = gtkpspell->start_pos;
1291
1292         set_point_continue(gtkpspell);
1293 }
1294         
1295 static void replace_with_create_dialog_cb(GtkWidget *w, gpointer data)
1296 {
1297         GtkWidget *dialog;
1298         GtkWidget *label;
1299         GtkWidget *hbox;
1300         GtkWidget *entry;
1301         GtkWidget *ok_button;
1302         GtkWidget *cancel_button;
1303         gchar *thelabel;
1304         gint xx, yy;
1305         GtkPspell *gtkpspell = (GtkPspell *) data;
1306
1307         gdk_window_get_origin((GTK_WIDGET(w)->parent)->window, &xx, &yy);
1308
1309         gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w)->parent));
1310
1311         dialog = gtk_dialog_new();
1312         gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE);
1313         gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1314         gtk_widget_set_uposition(dialog, xx, yy);
1315
1316         gtk_signal_connect_object(GTK_OBJECT(dialog), "destroy",
1317                                   GTK_SIGNAL_FUNC(gtk_widget_destroy), 
1318                                   GTK_OBJECT(dialog));
1319
1320         hbox = gtk_hbox_new(FALSE, 0);
1321         gtk_container_set_border_width(GTK_CONTAINER(hbox), 8);
1322
1323         thelabel = g_strdup_printf(_("Replace \"%s\" with: "), 
1324                                    gtkpspell->theword);
1325         label = gtk_label_new(thelabel);
1326         g_free(thelabel);
1327         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1328
1329         entry = gtk_entry_new();
1330         gtkpspell->replace_entry = entry;
1331         gtk_entry_set_text(GTK_ENTRY(entry), gtkpspell->theword);
1332         gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1333         gtk_signal_connect(GTK_OBJECT(entry), "activate",
1334                            GTK_SIGNAL_FUNC(replace_with_supplied_word_cb), 
1335                            gtkpspell);
1336         gtk_signal_connect_object(GTK_OBJECT(entry), "activate",
1337                            GTK_SIGNAL_FUNC(gtk_widget_destroy), 
1338                            GTK_OBJECT(dialog));
1339         gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
1340
1341         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, 
1342                            TRUE, 0);
1343         if (!gtkpspell->gtkpspeller->ispell) {
1344                 label = gtk_label_new(_("Holding down MOD1 key while pressing Enter\nwill learn from mistake.\n"));
1345                 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1346                 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
1347                 gtk_misc_set_padding(GTK_MISC(label), 8, 0);
1348                 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, 
1349                                    TRUE, TRUE, 0);
1350         }
1351
1352         hbox = gtk_hbox_new(TRUE, 0);
1353
1354         ok_button = gtk_button_new_with_label(_("OK"));
1355         gtk_box_pack_start(GTK_BOX(hbox), ok_button, TRUE, TRUE, 8);
1356         gtk_signal_connect(GTK_OBJECT(ok_button), "clicked",
1357                            GTK_SIGNAL_FUNC(replace_with_supplied_word_cb), 
1358                            gtkpspell);
1359         gtk_signal_connect_object(GTK_OBJECT(ok_button), "clicked",
1360                                   GTK_SIGNAL_FUNC(gtk_widget_destroy), 
1361                                   GTK_OBJECT(dialog));
1362
1363         cancel_button = gtk_button_new_with_label(_("Cancel"));
1364         gtk_box_pack_start(GTK_BOX(hbox), cancel_button, TRUE, TRUE, 8);
1365         gtk_signal_connect_object(GTK_OBJECT(cancel_button), "clicked",
1366                                   GTK_SIGNAL_FUNC(gtk_widget_destroy), 
1367                                   GTK_OBJECT(dialog));
1368
1369         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), hbox);
1370
1371         gtk_widget_grab_focus(entry);
1372
1373         gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1374
1375         gtk_widget_show_all(dialog);
1376 }
1377
1378 void gtkpspell_uncheck_all(GtkPspell * gtkpspell) 
1379 {
1380         gint      origpos;
1381         gchar    *text;
1382         gfloat    adj_value;
1383         GtkSText *gtktext;
1384         
1385         gtktext = gtkpspell->gtktext;
1386
1387         adj_value = gtktext->vadj->value;
1388
1389         gtk_stext_freeze(gtktext);
1390
1391         origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
1392
1393         text = gtk_editable_get_chars(GTK_EDITABLE(gtktext), 0, -1);
1394
1395         gtk_stext_set_point(gtktext, 0);
1396         gtk_stext_forward_delete(gtktext, gtk_stext_get_length(gtktext));
1397         gtk_stext_insert(gtktext, NULL, NULL, NULL, text, strlen(text));
1398
1399         gtk_stext_thaw(gtktext);
1400
1401         gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
1402         gtk_stext_set_point(gtktext, origpos);
1403         gtk_adjustment_set_value(gtktext->vadj, adj_value);
1404
1405         g_free(text);
1406
1407 }
1408
1409 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1410 {
1411         GtkPspell *gtkpspell = (GtkPspell *) data;
1412
1413         gtkpspell->check_while_typing = gtkpspell->check_while_typing == FALSE;
1414         if (!gtkpspell->check_while_typing)
1415                 gtkpspell_uncheck_all(gtkpspell);
1416
1417         if (gtkpspell->config_menu)
1418                 populate_submenu(gtkpspell, gtkpspell->config_menu);
1419 }
1420
1421 static GSList *create_empty_dictionary_list(void)
1422 {
1423         GSList *list = NULL;
1424         Dictionary *dict;
1425
1426         dict = g_new0(Dictionary, 1);
1427         dict->fullname = g_strdup(_("None"));
1428         dict->dictname = dict->fullname;
1429         dict->encoding = NULL;
1430         return g_slist_append(list, dict);
1431 }
1432
1433 /* gtkpspell_get_dictionary_list() - returns list of dictionary names */
1434 GSList *gtkpspell_get_dictionary_list(const gchar *pspell_path, gint refresh)
1435 {
1436         GSList *list;
1437         gchar *dict_path, *tmp, *prevdir;
1438         gchar tmpname[BUFSIZE];
1439         Dictionary *dict;
1440         DIR *dir;
1441         struct dirent *ent;
1442
1443         if (!gtkpspellcheckers)
1444                 gtkpspellcheckers = gtkpspell_checkers_new();
1445
1446         if (gtkpspellcheckers->dictionary_list && !refresh)
1447                 return gtkpspellcheckers->dictionary_list;
1448         else
1449                 gtkpspell_free_dictionary_list(gtkpspellcheckers->dictionary_list);
1450
1451         list = NULL;
1452
1453 #ifdef USE_THREADS
1454 #warning TODO: no directory change
1455 #endif
1456         dict_path = g_strdup(pspell_path);
1457         prevdir   = g_get_current_dir();
1458         if (chdir(dict_path) <0) {
1459                 debug_print(_("Pspell: error when searching for dictionaries:\n%s\n"),
1460                             g_strerror(errno));
1461                 g_free(prevdir);
1462                 g_free(dict_path);
1463                 gtkpspellcheckers->dictionary_list = create_empty_dictionary_list();
1464                 return gtkpspellcheckers->dictionary_list; 
1465         }
1466
1467         debug_print(_("Pspell: checking for dictionaries in %s\n"), dict_path);
1468
1469         if (NULL != (dir = opendir("."))) {
1470                 while (NULL != (ent = readdir(dir))) {
1471                         /* search for pwli */
1472                         if ((NULL != (tmp = strstr2(ent->d_name, ".pwli"))) && 
1473                             (tmp[5] == 0x00)) {
1474                                 g_snprintf(tmpname, BUFSIZE, "%s%s", 
1475                                            G_DIR_SEPARATOR_S, ent->d_name);
1476                                 tmpname[MIN(tmp - ent->d_name + 1, BUFSIZE-1)] = 0x00;
1477                                 dict = g_new0(Dictionary, 1);
1478                                 dict->fullname = g_strdup_printf("%s%s", 
1479                                                                  dict_path, 
1480                                                                  tmpname);
1481                                 dict->dictname = strrchr(dict->fullname, 
1482                                                          G_DIR_SEPARATOR) + 1;
1483                                 dict->encoding = NULL;
1484                                 debug_print(_("Pspell: found dictionary %s %s\n"),
1485                                             dict->fullname, dict->dictname);
1486                                 list = g_slist_insert_sorted(list, dict,
1487                                                 (GCompareFunc) compare_dict);
1488                         }
1489                 }                       
1490                 closedir(dir);
1491         }
1492         else {
1493                 debug_print(_("Pspell: error when searching for dictionaries.\nNo dictionary found.\n(%s)"), 
1494                             g_strerror(errno));
1495                 list = create_empty_dictionary_list();
1496         }
1497         if(list==NULL){
1498                 
1499                 debug_print(_("Pspell: error when searching for dictionaries.\nNo dictionary found."));
1500                 list = create_empty_dictionary_list();
1501         }
1502         chdir(prevdir);
1503         g_free(dict_path);
1504         g_free(prevdir);
1505         gtkpspellcheckers->dictionary_list = list;
1506         return list;
1507 }
1508
1509 void gtkpspell_free_dictionary_list(GSList *list)
1510 {
1511         Dictionary *dict;
1512         GSList *walk;
1513         for (walk = list; walk != NULL; walk = g_slist_next(walk))
1514                 if (walk->data) {
1515                         dict = (Dictionary *) walk->data;
1516                         dictionary_delete(dict);
1517                 }                               
1518         g_slist_free(list);
1519 }
1520
1521 #if 0
1522 static void dictionary_option_menu_item_data_destroy(gpointer data)
1523 {
1524         Dictionary *dict = (Dictionary *) data;
1525
1526         if (dict)
1527                 dictionary_delete(dict);
1528 }
1529 #endif
1530
1531 GtkWidget *gtkpspell_dictionary_option_menu_new(const gchar *pspell_path)
1532 {
1533         GSList *dict_list, *tmp;
1534         GtkWidget *item;
1535         GtkWidget *menu;
1536         Dictionary *dict;
1537
1538         dict_list = gtkpspell_get_dictionary_list(pspell_path, TRUE);
1539         g_return_val_if_fail(dict_list, NULL);
1540
1541         menu = gtk_menu_new();
1542         
1543         for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1544                 dict = (Dictionary *) tmp->data;
1545                 item = gtk_menu_item_new_with_label(dict->dictname);
1546                 gtk_object_set_data(GTK_OBJECT(item), "dict_name",
1547                                     dict->fullname); 
1548                                          
1549                 gtk_menu_append(GTK_MENU(menu), item);                                   
1550                 gtk_widget_show(item);
1551         }
1552
1553         gtk_widget_show(menu);
1554
1555         return menu;
1556 }
1557
1558 gchar *gtkpspell_get_dictionary_menu_active_item(GtkWidget *menu)
1559 {
1560         GtkWidget *menuitem;
1561         gchar *dict_fullname;
1562         gchar *label;
1563
1564         g_return_val_if_fail(GTK_IS_MENU(menu), NULL);
1565         menuitem = gtk_menu_get_active(GTK_MENU(menu));
1566         dict_fullname = (gchar *) gtk_object_get_data(GTK_OBJECT(menuitem), "dict_name");
1567         g_return_val_if_fail(dict_fullname, NULL);
1568         label = g_strdup(dict_fullname);
1569         return label;
1570   
1571 }
1572
1573 GtkWidget *gtkpspell_sugmode_option_menu_new(gint sugmode)
1574 {
1575         GtkWidget *menu;
1576         GtkWidget *item;
1577
1578
1579         menu = gtk_menu_new();
1580         gtk_widget_show(menu);
1581
1582         item = gtk_menu_item_new_with_label(_("Fast Mode"));
1583         gtk_widget_show(item);
1584         gtk_menu_append(GTK_MENU(menu), item);
1585         gtk_object_set_data(GTK_OBJECT(item), "sugmode", GINT_TO_POINTER(PSPELL_FASTMODE));
1586
1587         item = gtk_menu_item_new_with_label(_("Normal Mode"));
1588         gtk_widget_show(item);
1589         gtk_menu_append(GTK_MENU(menu), item);
1590         gtk_object_set_data(GTK_OBJECT(item), "sugmode", GINT_TO_POINTER(PSPELL_NORMALMODE));
1591         
1592         item = gtk_menu_item_new_with_label(_("Bad Spellers Mode"));
1593         gtk_widget_show(item);
1594         gtk_menu_append(GTK_MENU(menu), item);
1595         gtk_object_set_data(GTK_OBJECT(item), "sugmode", GINT_TO_POINTER(PSPELL_BADSPELLERMODE));
1596
1597         return menu;
1598 }
1599         
1600 void gtkpspell_sugmode_option_menu_set(GtkOptionMenu *optmenu, gint sugmode)
1601 {
1602         g_return_if_fail(GTK_IS_OPTION_MENU(optmenu));
1603
1604         g_return_if_fail(sugmode == PSPELL_FASTMODE ||
1605                          sugmode == PSPELL_NORMALMODE ||
1606                          sugmode == PSPELL_BADSPELLERMODE);
1607
1608         gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), sugmode - 1);
1609 }
1610
1611 gint gtkpspell_get_sugmode_from_option_menu(GtkOptionMenu *optmenu)
1612 {
1613         gint sugmode;
1614         GtkWidget *item;
1615         
1616         g_return_val_if_fail(GTK_IS_OPTION_MENU(optmenu), -1);
1617
1618         item = gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(optmenu)));
1619         
1620         sugmode = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item), "sugmode"));
1621
1622         return sugmode;
1623         
1624 }
1625
1626 static void use_alternate_dict(GtkPspell *gtkpspell)
1627 {
1628         GtkPspeller *tmp;
1629
1630         tmp = gtkpspell->gtkpspeller;
1631         gtkpspell->gtkpspeller = gtkpspell->alternate_speller;
1632         gtkpspell->alternate_speller = tmp;
1633
1634         if (gtkpspell->config_menu)
1635                 populate_submenu(gtkpspell, gtkpspell->config_menu);
1636 }
1637
1638
1639 #if 0 /* Experimenal */
1640 void gtkpspell_set_primary_dict(GtkPspell *gtkpspell, gchar *dict, gchar *encoding)
1641 {
1642         g_return_if_fail(gtkpspell);
1643         if (gtkpspell->dict1)
1644                 dictionary_delete(gtkpspell->dict1);
1645         gtkpspell->dict1 = g_new(Dictionary, 1);
1646         gtkpspell->dict1->fullname = g_strdup(dict);
1647         gtkpspell->dict1->encoding = g_strdup(encoding);
1648 }
1649
1650 void gtkpspell_set_secondary_dict(GtkPspell *gtkpspell, gchar *dict, gchar *encoding)
1651 {
1652         g_return_if_fail(gtkpspell);
1653         if (gtkpspell->dict2)
1654                 dictionary_delete(gtkpspell->dict2);
1655         gtkpspell->dict2 = g_new(Dictionary, 1);
1656         gtkpspell->dict2->fullname = g_strdup(dict);
1657         gtkpspell->dict2->encoding = g_strdup(encoding);
1658 }
1659
1660 gboolean gtkpspell_use_dict(GtkPspell *gtkpspell, Dictionary *dict)
1661 {
1662         return TRUE;
1663 }
1664
1665 gboolean gtkpspell_use_dictionary(GtkPspell *gtkpspell, gchar *dictpath, gchar *encoding)
1666 {
1667         Dictionary *dict;
1668         gboolean retval;
1669         
1670         g_return_val_if_fail(gtkpspell, FALSE);
1671         g_return_val_if_fail(dict, FALSE);
1672         g_return_val_if_fail(encoding, FALSE);
1673
1674         dict = g_new(Dictionary, 1);
1675         
1676         dict->fullname = g_strdup(dictpath);
1677         dict->encoding = NULL; /* To be continued */
1678
1679         retval = gtkpspell_use_dict(gtkpspell, dict);
1680
1681         dictionary_delete(dict);
1682         
1683         return retval;
1684         
1685 }
1686
1687 gboolean gtkpspell_use_primary(GtkPspell *gtkpspell)
1688 {
1689         g_return_val_if_fail(gtkpspell, FALSE);
1690
1691         return gtkpspell_use_dict(gtkpspell, gtkpspell->dict1);
1692 }
1693
1694 gboolean gtkpspell_use_secondary(GtkPspell *gtkpspell)
1695 {
1696         g_return_val_if_fail(gtkpspell, FALSE);
1697
1698         return gtkpspell_use_dict(gtkpspell, gtkpspell->dict2);
1699 }
1700 #endif
1701
1702 /*****************************************************************************/
1703
1704 static void popup_menu(GtkPspell *gtkpspell, GdkEventButton *eb) 
1705 {
1706         GtkSText * gtktext;
1707         
1708         gtktext = gtkpspell->gtktext;
1709         gtkpspell->orig_pos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
1710
1711         if (!(eb->state & GDK_SHIFT_MASK)) {
1712                 if (check_at(gtkpspell, gtkpspell->orig_pos)) {
1713
1714                         gtk_editable_set_position(GTK_EDITABLE(gtktext), 
1715                                                   gtkpspell->orig_pos);
1716                         gtk_stext_set_point(gtktext, gtkpspell->orig_pos);
1717
1718                         if (misspelled_suggest(gtkpspell, gtkpspell->theword)) {
1719                                 gtk_menu_popup(make_sug_menu(gtkpspell), 
1720                                                NULL, NULL, NULL, NULL,
1721                                                eb->button, GDK_CURRENT_TIME);
1722                                 
1723                                 return;
1724                         }
1725                 } else {
1726                         gtk_editable_set_position(GTK_EDITABLE(gtktext), 
1727                                                   gtkpspell->orig_pos);
1728                         gtk_stext_set_point(gtktext, gtkpspell->orig_pos);
1729                 }
1730         }
1731
1732         gtk_menu_popup(make_config_menu(gtkpspell),NULL,NULL,NULL,NULL,
1733                        eb->button, GDK_CURRENT_TIME);
1734 }
1735
1736 /* make_sug_menu() - Add menus to accept this word for this session and to add it to 
1737  * personal dictionary */
1738 static GtkMenu *make_sug_menu(GtkPspell *gtkpspell) 
1739 {
1740         GtkWidget       *menu, *item;
1741         unsigned char   *caption;
1742         GtkSText        *gtktext;
1743         GList           *l = gtkpspell->suggestions_list;
1744         GtkAccelGroup   *accel;
1745         
1746
1747         gtktext = gtkpspell->gtktext;
1748
1749         accel = gtk_accel_group_new();
1750         menu = gtk_menu_new(); 
1751
1752         if (gtkpspell->sug_menu)
1753                 gtk_widget_destroy(gtkpspell->sug_menu);
1754
1755         gtkpspell->sug_menu = menu;     
1756
1757         gtk_signal_connect(GTK_OBJECT(menu), "cancel",
1758                 GTK_SIGNAL_FUNC(cancel_menu_cb), gtkpspell);
1759
1760         caption = g_strdup_printf(_("\"%s\" unknown in %s"), 
1761                                   (unsigned char*) l->data, 
1762                                   gtkpspell->gtkpspeller->dictionary->dictname);
1763         item = gtk_menu_item_new_with_label(caption);
1764         gtk_widget_show(item);
1765         gtk_menu_append(GTK_MENU(menu), item);
1766         gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
1767         g_free(caption);
1768
1769         item = gtk_menu_item_new();
1770         gtk_widget_show(item);
1771         gtk_menu_append(GTK_MENU(menu), item);
1772
1773         item = gtk_menu_item_new_with_label(_("Accept in this session"));
1774         gtk_widget_show(item);
1775         gtk_menu_append(GTK_MENU(menu), item);
1776         gtk_signal_connect(GTK_OBJECT(item), "activate",
1777                            GTK_SIGNAL_FUNC(add_word_to_session_cb), 
1778                            gtkpspell);
1779         gtk_widget_add_accelerator(item, "activate", accel, GDK_space, GDK_MOD1_MASK,
1780                                    GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1781
1782         item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1783         gtk_widget_show(item);
1784         gtk_menu_append(GTK_MENU(menu), item);
1785         gtk_signal_connect(GTK_OBJECT(item), "activate",
1786                            GTK_SIGNAL_FUNC(add_word_to_personal_cb), 
1787                            gtkpspell);
1788         gtk_widget_add_accelerator(item, "activate", accel, GDK_Return, GDK_MOD1_MASK,
1789                                    GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1790
1791         item = gtk_menu_item_new_with_label(_("Replace with..."));
1792         gtk_widget_show(item);
1793         gtk_menu_append(GTK_MENU(menu), item);
1794         gtk_signal_connect(GTK_OBJECT(item), "activate",
1795                            GTK_SIGNAL_FUNC(replace_with_create_dialog_cb), 
1796                            gtkpspell);
1797         gtk_widget_add_accelerator(item, "activate", accel, GDK_R, 0,
1798                                    GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1799
1800         if (gtkpspell->use_alternate && gtkpspell->alternate_speller) {
1801                 caption = g_strdup_printf(_("Check with %s"), 
1802                         gtkpspell->alternate_speller->dictionary->dictname);
1803                 item = gtk_menu_item_new_with_label(caption);
1804                 g_free(caption);
1805                 gtk_widget_show(item);
1806                 gtk_menu_append(GTK_MENU(menu), item);
1807                 gtk_signal_connect(GTK_OBJECT(item), "activate",
1808                                 GTK_SIGNAL_FUNC(check_with_alternate_cb),
1809                                 gtkpspell);
1810                 gtk_widget_add_accelerator(item, "activate", accel, GDK_X, 0,
1811                                            GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1812         }
1813         item = gtk_menu_item_new();
1814         gtk_widget_show(item);
1815         gtk_menu_append(GTK_MENU(menu), item);
1816
1817         l = l->next;
1818         if (l == NULL) {
1819                 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
1820                 gtk_widget_show(item);
1821                 gtk_menu_append(GTK_MENU(menu), item);
1822         } else {
1823                 GtkWidget *curmenu = menu;
1824                 gint count = 0;
1825                 
1826                 do {
1827                         if (l->data == NULL && l->next != NULL) {
1828                                 count = 0;
1829                                 curmenu = gtk_menu_new();
1830                                 item = gtk_menu_item_new_with_label(_("Others..."));
1831                                 gtk_widget_show(item);
1832                                 gtk_menu_append(GTK_MENU(curmenu), item);
1833                                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), curmenu);
1834
1835                                 l = l->next;
1836                         } else if (count > MENUCOUNT) {
1837                                 count -= MENUCOUNT;
1838
1839                                 item = gtk_menu_item_new_with_label(_("More..."));
1840                                 gtk_widget_show(item);
1841                                 gtk_menu_append(GTK_MENU(curmenu), item);
1842
1843                                 curmenu = gtk_menu_new();
1844                                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), curmenu);
1845                         }
1846
1847                         item = gtk_menu_item_new_with_label((unsigned char*)l->data);
1848                         gtk_widget_show(item);
1849                         gtk_menu_append(GTK_MENU(curmenu), item);
1850                         gtk_signal_connect(GTK_OBJECT(item), "activate",
1851                                            GTK_SIGNAL_FUNC(replace_word_cb), gtkpspell);
1852
1853                         if (curmenu == menu && count < MENUCOUNT) {
1854                                 gtk_widget_add_accelerator(item, "activate",
1855                                                            accel,
1856                                                            GDK_A + count, 0,
1857                                                            GTK_ACCEL_LOCKED | 
1858                                                            GTK_ACCEL_VISIBLE);
1859                                 gtk_widget_add_accelerator(item, "activate", 
1860                                                            accel,
1861                                                            GDK_A + count, 
1862                                                            GDK_MOD1_MASK,
1863                                                            GTK_ACCEL_LOCKED);
1864                                 }
1865
1866                         count++;
1867
1868                 } while ((l = l->next) != NULL);
1869         }
1870
1871         gtk_accel_group_attach(accel, GTK_OBJECT(menu));
1872         gtk_accel_group_unref(accel);
1873         
1874         return GTK_MENU(menu);
1875 }
1876
1877 static void populate_submenu(GtkPspell *gtkpspell, GtkWidget *menu)
1878 {
1879         GtkWidget *item, *submenu;
1880         GtkPspeller *gtkpspeller = gtkpspell->gtkpspeller;
1881         gchar *dictname;
1882         gint ispell = gtkpspeller->ispell;
1883
1884         if (GTK_MENU_SHELL(menu)->children) {
1885                 GList *amenu, *alist;
1886                 for (amenu = (GTK_MENU_SHELL(menu)->children); amenu; ) {
1887                         alist = amenu->next;
1888                         gtk_widget_destroy(GTK_WIDGET(amenu->data));
1889                         amenu = alist;
1890                 }
1891         }
1892         
1893         dictname = g_strdup_printf(_("Dictionary: %s"), gtkpspeller->dictionary->dictname);
1894         item = gtk_menu_item_new_with_label(dictname);
1895         gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item)->child), 0.5, 0.5);
1896         g_free(dictname);
1897         gtk_widget_show(item);
1898         gtk_menu_append(GTK_MENU(menu), item);
1899
1900         item = gtk_menu_item_new();
1901         gtk_widget_show(item);
1902         gtk_menu_append(GTK_MENU(menu), item);
1903
1904         if (gtkpspell->use_alternate && gtkpspell->alternate_speller) {
1905                 dictname = g_strdup_printf(_("Use alternate (%s)"), 
1906                                 gtkpspell->alternate_speller->dictionary->dictname);
1907                 item = gtk_menu_item_new_with_label(dictname);
1908                 g_free(dictname);
1909                 gtk_signal_connect(GTK_OBJECT(item), "activate",
1910                                    GTK_SIGNAL_FUNC(switch_to_alternate_cb),
1911                                    gtkpspell);
1912                 gtk_widget_show(item);
1913                 gtk_menu_append(GTK_MENU(menu), item);
1914         }
1915
1916         item = gtk_check_menu_item_new_with_label(_("Fast Mode"));
1917         if (ispell || gtkpspell->gtkpspeller->sug_mode == PSPELL_FASTMODE)
1918                 gtk_widget_set_sensitive(GTK_WIDGET(item),FALSE);
1919         if (!ispell && gtkpspell->gtkpspeller->sug_mode == PSPELL_FASTMODE)
1920                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),TRUE);
1921         else
1922                 gtk_signal_connect(GTK_OBJECT(item), "activate",
1923                                    GTK_SIGNAL_FUNC(set_sug_mode_cb),
1924                                    gtkpspell);
1925         gtk_widget_show(item);
1926         gtk_menu_append(GTK_MENU(menu), item);
1927
1928         item = gtk_check_menu_item_new_with_label(_("Normal Mode"));
1929         if (ispell || gtkpspell->gtkpspeller->sug_mode == PSPELL_NORMALMODE)
1930                 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
1931         if (!ispell && gtkpspell->gtkpspeller->sug_mode == PSPELL_NORMALMODE) 
1932                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1933         else
1934                 gtk_signal_connect(GTK_OBJECT(item), "activate",
1935                                    GTK_SIGNAL_FUNC(set_sug_mode_cb),
1936                                    gtkpspell);
1937         gtk_widget_show(item);
1938         gtk_menu_append(GTK_MENU(menu),item);
1939
1940         item = gtk_check_menu_item_new_with_label(_("Bad Spellers Mode"));
1941         if (ispell || gtkpspell->gtkpspeller->sug_mode == PSPELL_BADSPELLERMODE)
1942                 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
1943         if (!ispell && gtkpspell->gtkpspeller->sug_mode == PSPELL_BADSPELLERMODE)
1944                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1945         else
1946                 gtk_signal_connect(GTK_OBJECT(item), "activate",
1947                                    GTK_SIGNAL_FUNC(set_sug_mode_cb),
1948                                    gtkpspell);
1949         gtk_widget_show(item);
1950         gtk_menu_append(GTK_MENU(menu), item);
1951         
1952         item = gtk_menu_item_new();
1953         gtk_widget_show(item);
1954         gtk_menu_append(GTK_MENU(menu), item);
1955         
1956         item = gtk_check_menu_item_new_with_label(_("Check while typing"));
1957         if (gtkpspell->check_while_typing)
1958                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1959         else    
1960                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1961         gtk_signal_connect(GTK_OBJECT(item), "activate",
1962                            GTK_SIGNAL_FUNC(toggle_check_while_typing_cb),
1963                            gtkpspell);
1964         gtk_widget_show(item);
1965         gtk_menu_append(GTK_MENU(menu), item);
1966
1967         item = gtk_menu_item_new();
1968         gtk_widget_show(item);
1969         gtk_menu_append(GTK_MENU(menu), item);
1970
1971         submenu = gtk_menu_new();
1972         item = gtk_menu_item_new_with_label(_("Change dictionary"));
1973         gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
1974         gtk_widget_show(item);
1975         gtk_menu_append(GTK_MENU(menu), item);
1976
1977         /* Dict list */
1978         if (gtkpspellcheckers->dictionary_list == NULL)
1979                 gtkpspell_get_dictionary_list(prefs_common.pspell_path, FALSE);
1980         {
1981                 GtkWidget * curmenu = submenu;
1982                 int count = 0;
1983                 Dictionary *dict;
1984                 GSList *tmp;
1985                 tmp = gtkpspellcheckers->dictionary_list;
1986                 
1987                 for (tmp = gtkpspellcheckers->dictionary_list; tmp != NULL; 
1988                                 tmp = g_slist_next(tmp)) {
1989                         dict = (Dictionary *) tmp->data;
1990                         item = gtk_check_menu_item_new_with_label(dict->dictname);
1991                         gtk_object_set_data(GTK_OBJECT(item), "dict_name", dict->fullname); 
1992                         if (strcmp2(dict->fullname, gtkpspell->gtkpspeller->dictionary->fullname))
1993                                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1994                         else {
1995                                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1996                                 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
1997                         }
1998                         gtk_signal_connect(GTK_OBJECT(item), "activate",
1999                                            GTK_SIGNAL_FUNC(change_dict_cb),
2000                                            gtkpspell);
2001                         gtk_widget_show(item);
2002                         gtk_menu_append(GTK_MENU(curmenu), item);
2003                         
2004                         count++;
2005                         
2006                         if (count == MENUCOUNT) {
2007                                 GtkWidget *newmenu;
2008                                 
2009                                 newmenu = gtk_menu_new();
2010                                 item = gtk_menu_item_new_with_label(_("More..."));
2011                                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), 
2012                                                           newmenu);
2013                                 
2014                                 gtk_menu_append(GTK_MENU(curmenu), item);
2015                                 gtk_widget_show(item);
2016                                 curmenu = newmenu;
2017                                 count = 0;
2018                         }
2019                 }
2020         }  
2021
2022         
2023         
2024
2025 }
2026
2027 static GtkMenu *make_config_menu(GtkPspell *gtkpspell)
2028 {
2029         if (!gtkpspell->popup_config_menu)
2030                 gtkpspell->popup_config_menu = gtk_menu_new();
2031
2032         debug_print("Pspell: creating/using popup_config_menu %0x\n", 
2033                         (guint) gtkpspell->popup_config_menu);
2034         populate_submenu(gtkpspell, gtkpspell->popup_config_menu);
2035
2036         return GTK_MENU(gtkpspell->popup_config_menu);
2037 }
2038
2039 void gtkpspell_populate_submenu(GtkPspell *gtkpspell, GtkWidget *menuitem)
2040 {
2041         GtkWidget *menu;
2042
2043         menu = GTK_WIDGET(GTK_MENU_ITEM(menuitem)->submenu);
2044         
2045         debug_print("Pspell: using config menu %0x\n", 
2046                         (guint) gtkpspell->popup_config_menu);
2047         populate_submenu(gtkpspell, menu);
2048         
2049         gtkpspell->config_menu = menu;
2050         
2051 }
2052
2053 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y, gpointer data)
2054 {
2055         GtkPspell       *gtkpspell = (GtkPspell *) data;
2056         gint             xx = 0, yy = 0;
2057         gint             sx,     sy;
2058         gint             wx,     wy;
2059         GtkSText        *text = GTK_STEXT(gtkpspell->gtktext);
2060         GtkRequisition   r;
2061
2062         gdk_window_get_origin(GTK_WIDGET(gtkpspell->gtktext)->window, &xx, &yy);
2063         
2064         sx = gdk_screen_width();
2065         sy = gdk_screen_height();
2066         
2067         gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2068         
2069         wx =  r.width;
2070         wy =  r.height;
2071         
2072         *x = gtkpspell->gtktext->cursor_pos_x + xx +
2073              gdk_char_width(GTK_WIDGET(text)->style->font, ' ');
2074         *y = gtkpspell->gtktext->cursor_pos_y + yy;
2075
2076         if (*x + wx > sx)
2077                 *x = sx - wx;
2078         if (*y + wy > sy)
2079                 *y = *y - wy - 
2080                      gdk_string_height((GTK_WIDGET(gtkpspell->gtktext))->style->font, 
2081                                        gtkpspell->theword);
2082
2083 }
2084
2085 /*************************************************************************/
2086 /* Menu call backs */
2087
2088 static gboolean cancel_menu_cb(GtkMenuShell *w, gpointer data)
2089 {
2090         GtkPspell *gtkpspell = (GtkPspell *) data;
2091         gtkpspell->continue_check = NULL;
2092         set_point_continue(gtkpspell);
2093         return FALSE;
2094         
2095 }
2096 /* change_dict_cb() - Menu callback : change dict */
2097 static void change_dict_cb(GtkWidget *w, GtkPspell *gtkpspell)
2098 {
2099         Dictionary      *dict;       
2100         gchar           *fullname;
2101         GtkPspeller     *gtkpspeller;
2102         gint             sug_mode;
2103   
2104         fullname = (gchar *) gtk_object_get_data(GTK_OBJECT(w), "dict_name");
2105         
2106         if (!strcmp2(fullname, _("None")))
2107                         return;
2108
2109         sug_mode  = gtkpspell->default_sug_mode;
2110
2111         dict = g_new0(Dictionary, 1);
2112         dict->fullname = g_strdup(fullname);
2113         dict->encoding = g_strdup(gtkpspell->gtkpspeller->dictionary->encoding);
2114
2115         if (gtkpspell->use_alternate && gtkpspell->alternate_speller &&
2116             dict == gtkpspell->alternate_speller->dictionary) {
2117                 use_alternate_dict(gtkpspell);
2118                 dictionary_delete(dict);
2119                 return;
2120         }
2121         
2122         gtkpspeller = gtkpspeller_new(dict);
2123
2124         if (!gtkpspeller) {
2125                 gchar *message;
2126                 message = g_strdup_printf(_("The spell checker could not change dictionary.\n%s"), 
2127                                           gtkpspellcheckers->error_message);
2128
2129                 gtkpspell_alert_dialog(message); 
2130                 g_free(message);
2131         } else {
2132                 if (gtkpspell->use_alternate) {
2133                         if (gtkpspell->alternate_speller)
2134                                 gtkpspeller_delete(gtkpspell->alternate_speller);
2135                         gtkpspell->alternate_speller = gtkpspell->gtkpspeller;
2136                 } else
2137                         gtkpspeller_delete(gtkpspell->gtkpspeller);
2138
2139                 gtkpspell->gtkpspeller = gtkpspeller;
2140                 gtkpspell_set_sug_mode(gtkpspell, sug_mode);
2141         }
2142         
2143         dictionary_delete(dict);
2144
2145         if (gtkpspell->config_menu)
2146                 populate_submenu(gtkpspell, gtkpspell->config_menu);
2147
2148 }
2149
2150 static void switch_to_alternate_cb(GtkWidget *w,
2151                                    gpointer data)
2152 {
2153         GtkPspell *gtkpspell = (GtkPspell *) data;
2154         use_alternate_dict(gtkpspell);
2155 }
2156
2157 /******************************************************************************/
2158 /* Misc. helper functions */
2159
2160 static void set_point_continue(GtkPspell *gtkpspell)
2161 {
2162         GtkSText  *gtktext;
2163
2164         gtktext = gtkpspell->gtktext;
2165
2166         gtk_stext_freeze(gtktext);
2167         gtk_editable_set_position(GTK_EDITABLE(gtktext),gtkpspell->orig_pos);
2168         gtk_stext_set_point(gtktext, gtkpspell->orig_pos);
2169         gtk_stext_thaw(gtktext);
2170
2171         if (gtkpspell->continue_check)
2172                 gtkpspell->continue_check((gpointer *) gtkpspell);
2173 }
2174
2175 static void allocate_color(GtkPspell *gtkpspell, gint rgbvalue)
2176 {
2177         GdkColormap *gc;
2178         GdkColor *color = &(gtkpspell->highlight);
2179
2180         gc = gtk_widget_get_colormap(GTK_WIDGET(gtkpspell->gtktext));
2181
2182         if (gtkpspell->highlight.pixel)
2183                 gdk_colormap_free_colors(gc, &(gtkpspell->highlight), 1);
2184
2185         /* Shameless copy from Sylpheed's gtkutils.c */
2186         color->pixel = 0L;
2187         color->red   = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0) * 65535.0);
2188         color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >>  8) / 255.0) * 65535.0);
2189         color->blue  = (int) (((gdouble) (rgbvalue & 0x0000ff)        / 255.0) * 65535.0);
2190
2191         gdk_colormap_alloc_color(gc, &(gtkpspell->highlight), FALSE, TRUE);
2192 }
2193
2194 static void change_color(GtkPspell * gtkpspell, 
2195                          gint start, gint end,
2196                          gchar *newtext,
2197                          GdkColor *color) 
2198 {
2199         GtkSText *gtktext;
2200
2201         g_return_if_fail(start < end);
2202     
2203         gtktext = gtkpspell->gtktext;
2204     
2205         gtk_stext_freeze(gtktext);
2206         if (newtext) {
2207                 gtk_stext_set_point(gtktext, start);
2208                 gtk_stext_forward_delete(gtktext, end - start);
2209                 gtk_stext_insert(gtktext, NULL, color, NULL, newtext, end - start);
2210         }
2211         gtk_stext_thaw(gtktext);
2212 }
2213
2214 /* convert_to_pspell_encoding () - converts ISO-8859-* strings to iso8859-* 
2215  * as needed by pspell. Returns an allocated string.
2216  */
2217
2218 static guchar *convert_to_pspell_encoding (const guchar *encoding)
2219 {
2220         guchar * pspell_encoding;
2221
2222         if (strstr2(encoding, "ISO-8859-")) {
2223                 pspell_encoding = g_strdup_printf("iso8859%s", encoding+8);
2224         }
2225         else
2226                 if (!strcmp2(encoding, "US-ASCII"))
2227                         pspell_encoding = g_strdup("iso8859-1");
2228                 else
2229                         pspell_encoding = g_strdup(encoding);
2230
2231         return pspell_encoding;
2232 }
2233
2234 /* compare_dict () - compare 2 dict names */
2235 static gint compare_dict(Dictionary *a, Dictionary *b)
2236 {
2237         guint   aparts = 0,  bparts = 0;
2238         guint   i;
2239
2240         for (i=0; i < strlen(a->dictname); i++)
2241                 if (a->dictname[i] == '-')
2242                         aparts++;
2243         for (i=0; i < strlen(b->dictname); i++)
2244                 if (b->dictname[i] == '-')
2245                         bparts++;
2246
2247         if (aparts != bparts) 
2248                 return (aparts < bparts) ? -1 : +1;
2249         else {
2250                 gint compare;
2251                 compare = strcmp2(a->dictname, b->dictname);
2252                 if (!compare)
2253                         compare = strcmp2(a->fullname, b->fullname);
2254                 return compare;
2255         }
2256 }
2257
2258
2259 static void dictionary_delete(Dictionary *dict)
2260 {
2261         g_free(dict->fullname);
2262         g_free(dict->encoding);
2263         g_free(dict);
2264 }
2265
2266 static Dictionary *dictionary_dup(const Dictionary *dict)
2267 {
2268         Dictionary *dict2;
2269
2270         dict2 = g_new(Dictionary, 1); 
2271
2272         dict2->fullname = g_strdup(dict->fullname);
2273         dict2->dictname = dict->dictname - dict->fullname + dict2->fullname;
2274         dict2->encoding = g_strdup(dict->encoding);
2275
2276         return dict2;
2277 }
2278
2279 static void free_suggestions_list(GtkPspell *gtkpspell)
2280 {
2281         GList *list;
2282
2283         for (list = gtkpspell->suggestions_list; list != NULL; list = list->next)
2284                 g_free(list->data);
2285         g_list_free(list);
2286         
2287         gtkpspell->max_sug          = -1;
2288         gtkpspell->suggestions_list = NULL;
2289  
2290 }
2291
2292 static void reset_theword_data(GtkPspell *gtkpspell)
2293 {
2294         gtkpspell->start_pos     =  0;
2295         gtkpspell->end_pos       =  0;
2296         gtkpspell->theword[0]    =  0;
2297         gtkpspell->max_sug       = -1;
2298
2299         free_suggestions_list(gtkpspell);
2300 }
2301
2302 static void free_checkers(gpointer elt, gpointer data)
2303 {
2304         GtkPspeller *gtkpspeller = elt;
2305
2306         g_return_if_fail(gtkpspeller);
2307
2308         gtkpspeller_real_delete(gtkpspeller);
2309 }
2310
2311 static gint find_gtkpspeller(gconstpointer aa, gconstpointer bb)
2312 {
2313         Dictionary *a = ((GtkPspeller *) aa)->dictionary;
2314         Dictionary *b = ((GtkPspeller *) bb)->dictionary;
2315
2316         if (a && b && a->fullname && b->fullname  &&
2317             strcmp(a->fullname, b->fullname) == 0 &&
2318             a->encoding && b->encoding)
2319                 return strcmp(a->encoding, b->encoding);
2320
2321         return 1;
2322 }
2323
2324 static void gtkpspell_alert_dialog(gchar *message)
2325 {
2326         GtkWidget *dialog;
2327         GtkWidget *hbox;
2328         GtkWidget *label;
2329         GtkWidget *ok_button;
2330
2331         dialog = gtk_dialog_new();
2332         gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE);
2333         gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_MOUSE);
2334         gtk_signal_connect_object(GTK_OBJECT(dialog), "destroy",
2335                                    GTK_SIGNAL_FUNC(gtk_widget_destroy), 
2336                                    GTK_OBJECT(dialog));
2337
2338         label  = gtk_label_new(message);
2339         gtk_misc_set_padding(GTK_MISC(label), 8, 8);
2340
2341         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
2342         
2343         hbox = gtk_hbox_new(FALSE, 0);
2344
2345         ok_button = gtk_button_new_with_label(_("OK"));
2346         GTK_WIDGET_SET_FLAGS(ok_button, GTK_CAN_DEFAULT);
2347         gtk_box_pack_start(GTK_BOX(hbox), ok_button, TRUE, TRUE, 8);    
2348
2349         gtk_signal_connect_object(GTK_OBJECT(ok_button), "clicked",
2350                                    GTK_SIGNAL_FUNC(gtk_widget_destroy), 
2351                                    GTK_OBJECT(dialog));
2352         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), hbox);
2353                         
2354         gtk_widget_grab_default(ok_button);
2355         gtk_widget_grab_focus(ok_button);
2356         gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
2357
2358         gtk_widget_show_all(dialog);
2359 }
2360 #endif