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