]> git.lyx.org Git - lyx.git/blob - src/spellchecker.C
b239790c9209227a667cbda3d6b2218809744bdf
[lyx.git] / src / spellchecker.C
1 /*
2  *This file is part of
3  * ====================================================== 
4  * 
5  *           LyX, The Document Processor
6  *       
7  *          Copyright 1995 Matthias Ettrich
8  *          Copyright 1995-1998 The LyX Team
9  *
10  * ====================================================== 
11  */
12
13 #include <config.h>
14
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <cstdlib>
18 #include <cstring>
19 #include <csignal>
20 #include <sys/wait.h>
21 #include <sys/types.h>
22 #include <cctype>
23 #include FORMS_H_LOCATION
24
25 #if TIME_WITH_SYS_TIME
26 # include <sys/time.h>
27 # include <ctime>
28 #else
29 # if HAVE_SYS_TIME_H
30 #  include <sys/time.h>
31 # else
32 #  include <ctime>
33 # endif
34 #endif
35
36 #ifdef HAVE_SYS_SELECT_H
37 #include <sys/select.h>
38 #endif
39
40 #include "LString.h"
41 #include "sp_form.h"
42 #include "spellchecker.h"
43 #include "lyx_main.h"
44 #include "buffer.h"
45 #include "lyxrc.h"
46 #include "BufferView.h"
47 #include "gettext.h"
48 #include "lyx_gui_misc.h"
49 #include "debug.h"
50 #include "support/lstrings.h"
51
52 extern LyXRC *lyxrc;
53 extern BufferView *current_view;
54
55 // Spellchecker status
56 enum {
57         ISP_OK = 1,
58         ISP_ROOT,
59         ISP_COMPOUNDWORD,
60         ISP_UNKNOWN,
61         ISP_MISSED,
62         ISP_IGNORE
63 };
64
65 static bool RunSpellChecker(string const &);
66
67 static FILE *in, *out;  /* streams to communicate with ispell */
68 pid_t isp_pid = -1; // pid for the `ispell' process. Also used (RO) in
69                     // lyx_cb.C
70
71 // the true spell checker program being used
72 enum ActualSpellChecker {ASC_ISPELL, ASC_ASPELL};
73 static ActualSpellChecker actual_spell_checker;
74
75 static int isp_fd;
76
77 static FD_form_spell_options *fd_form_spell_options = 0;
78 FD_form_spell_check *fd_form_spell_check = 0;
79
80 //void sigchldhandler(int sig);
81 void sigchldhandler(pid_t pid, int *status);
82
83 //extern void sigchldchecker(int sig);
84 extern void sigchldchecker(pid_t pid, int *status);
85
86 struct isp_result {
87         int flag;
88         int count;
89         string str;
90         char **misses;
91         isp_result() {
92                 flag = ISP_UNKNOWN;
93                 count = 0;
94                 misses = static_cast<char**>(0);
95         }
96         ~isp_result() {
97                 if (misses) // DEL LINE
98                         delete[] misses;
99         }
100 };
101
102
103 /***** Spellchecker options *****/
104
105 // Rewritten to use ordinary LyX xforms loop and OK, Apply and Cancel set,
106 // now also string. Amazing, eh? (Asger)
107
108 // Set (sane) values in form to current spellchecker options
109 void SpellOptionsUpdate() 
110 {
111         // Alternate language
112         if (lyxrc->isp_alt_lang.empty()) {
113                 lyxrc->isp_use_alt_lang = false;
114         } else {
115                 fl_set_input(fd_form_spell_options->altlang_input,
116                              lyxrc->isp_alt_lang.c_str());
117         }
118         if (lyxrc->isp_use_alt_lang) {
119                 fl_set_button(fd_form_spell_options->buflang, 0);
120                 fl_set_button(fd_form_spell_options->altlang, 1);
121         } else {
122                 fl_set_button(fd_form_spell_options->buflang, 1);
123                 fl_set_button(fd_form_spell_options->altlang, 0);
124         }  
125
126         // Personal dictionary
127         if (lyxrc->isp_pers_dict.empty()) {
128                 lyxrc->isp_use_pers_dict = false;
129         } else {
130                 fl_set_input(fd_form_spell_options->perdict_input,
131                              lyxrc->isp_pers_dict.c_str());
132         }
133         fl_set_button(fd_form_spell_options->perdict,
134                       lyxrc->isp_use_pers_dict ? 1:0);
135
136         // Escape chars
137         if (lyxrc->isp_esc_chars.empty()) {
138                 lyxrc->isp_use_esc_chars = false;
139         } else {
140                 fl_set_input(fd_form_spell_options->esc_chars_input,
141                              lyxrc->isp_esc_chars.c_str());
142         }
143         fl_set_button(fd_form_spell_options->esc_chars,
144                       lyxrc->isp_use_esc_chars ? 1:0);
145
146         // Options
147         fl_set_button(fd_form_spell_options->compounds,
148                       lyxrc->isp_accept_compound ? 1 : 0);
149         fl_set_button(fd_form_spell_options->inpenc,
150                       lyxrc->isp_use_input_encoding ? 1 : 0);
151 }
152
153 // Update spellchecker options
154 void SpellOptionsApplyCB(FL_OBJECT *, long)
155 {
156         // Build new status from form data
157         lyxrc->isp_use_alt_lang = 
158                 fl_get_button(fd_form_spell_options->altlang);
159         lyxrc->isp_use_pers_dict = 
160                 fl_get_button(fd_form_spell_options->perdict);
161         lyxrc->isp_accept_compound = 
162                 fl_get_button(fd_form_spell_options->compounds);
163         lyxrc->isp_use_esc_chars = 
164                 fl_get_button(fd_form_spell_options->esc_chars);
165         lyxrc->isp_use_input_encoding = 
166                 fl_get_button(fd_form_spell_options->inpenc);
167
168         // Update strings with data from input fields
169         lyxrc->isp_alt_lang = 
170                 fl_get_input(fd_form_spell_options->altlang_input);
171         lyxrc->isp_pers_dict = 
172                 fl_get_input(fd_form_spell_options->perdict_input);
173         lyxrc->isp_esc_chars = 
174                 fl_get_input(fd_form_spell_options->esc_chars_input);
175
176         // Update form
177         SpellOptionsUpdate();
178 }
179
180
181 void SpellOptionsCancelCB(FL_OBJECT *, long)
182 {
183         fl_hide_form(fd_form_spell_options->form_spell_options);
184 }
185
186
187 void SpellOptionsOKCB(FL_OBJECT * ob, long data)
188 {
189         SpellOptionsApplyCB(ob, data);
190         SpellOptionsCancelCB(ob, data);
191 }
192
193
194 // Show spellchecker options form
195 void SpellCheckerOptions()
196 {
197         // Create form if nescessary
198         if (fd_form_spell_options ==  0) {
199                 fd_form_spell_options = create_form_form_spell_options();
200                 // Make sure pressing the close box does not kill LyX. (RvdK)
201                 fl_set_form_atclose(fd_form_spell_options->form_spell_options, 
202                                     CancelCloseBoxCB, 0);
203         }
204
205         // Update form to current options
206         SpellOptionsUpdate();
207
208         // Focus in alternate language field
209         fl_set_focus_object(fd_form_spell_options->form_spell_options,
210                             fd_form_spell_options->altlang_input);
211
212         // Show form
213         if (fd_form_spell_options->form_spell_options->visible) {
214                 fl_raise_form(fd_form_spell_options->form_spell_options);
215         } else {
216                 fl_show_form(fd_form_spell_options->form_spell_options,
217                              FL_PLACE_MOUSE, FL_FULLBORDER,
218                              _("Spellchecker Options"));
219         }
220 }
221
222
223 /***** Spellchecker *****/
224
225 // Could also use a clean up. (Asger Alstrup)
226
227 static
228 void create_ispell_pipe(string const & lang)
229 {
230         static char o_buf[BUFSIZ];  // jc: it could be smaller
231         int pipein[2], pipeout[2];
232         char * argv[14];
233         int argc;
234
235         isp_pid = -1;
236
237         if(pipe(pipein) == -1 || pipe(pipeout) == -1) {
238                 lyxerr << "LyX: Can't create pipe for spellchecker!" << endl;
239                 return;
240         }
241
242         if ((out = fdopen(pipein[1], "w")) == 0) {
243                 lyxerr << "LyX: Can't create stream for pipe for spellchecker!"
244                        << endl;
245                 return;
246         }
247
248         if ((in = fdopen(pipeout[0], "r")) == 0) {
249                 lyxerr <<"LyX: Can't create stream for pipe for spellchecker!"
250                        << endl;
251                 return;
252         }
253
254         setvbuf(out, o_buf, _IOLBF, BUFSIZ);
255
256         isp_fd = pipeout[0];
257
258         isp_pid = fork();
259
260         if(isp_pid == -1) {
261                 lyxerr << "LyX: Can't create child process for spellchecker!"
262                        << endl;
263                 return;
264         }
265
266         if(isp_pid == 0) {        
267                 /* child process */
268                 dup2(pipein[0], STDIN_FILENO);
269                 dup2(pipeout[1], STDOUT_FILENO);
270                 close(pipein[0]);
271                 close(pipein[1]);
272                 close(pipeout[0]);
273                 close(pipeout[1]);
274
275                 argc = 0;
276                 char * tmp = new char[lyxrc->isp_command.length() + 1];
277                 lyxrc->isp_command.copy(tmp, lyxrc->isp_command.length());
278                 tmp[lyxrc->isp_command.length()] = '\0';
279                 argv[argc++] = tmp;
280                 tmp = new char[3];
281                 string("-a").copy(tmp, 2); tmp[2] = '\0'; // pipe mode
282                 argv[argc++] = tmp;
283
284                 if (lang != "default") {
285                         tmp = new char[3];
286                         string("-d").copy(tmp, 2); tmp[2] = '\0'; // Dictionary file
287                         argv[argc++] = tmp;
288                         tmp = new char[lang.length() + 1];
289                         lang.copy(tmp, lang.length()); tmp[lang.length()] = '\0';
290                         argv[argc++] = tmp;
291                 }
292
293                 if (lyxrc->isp_accept_compound) {
294                         // Consider run-together words as legal compounds
295                         tmp = new char[3];
296                         string("-C").copy(tmp, 2); tmp[2] = '\0';
297                         argv[argc++] = tmp;
298                 } else {
299                         // Report run-together words with
300                         // missing blanks as errors
301                         tmp = new char[3]; 
302                         string("-B").copy(tmp, 2); tmp[2] = '\0';
303                         argv[argc++] = tmp; 
304                 }
305                 if (lyxrc->isp_use_esc_chars) {
306                         // Specify additional characters that
307                         // can be part of a word
308                         tmp = new char[3];
309                         string("-w").copy(tmp, 2); tmp[2] = '\0';
310                         argv[argc++] = tmp; 
311                         // Put the escape chars in ""s
312                         string tms = "\"" + lyxrc->isp_esc_chars + "\"";
313                         tmp = new char[tms.length() + 1];
314                         tms.copy(tmp, tms.length()); tmp[tms.length()] = '\0';
315                         argv[argc++] = tmp;
316                 }
317                 if (lyxrc->isp_use_pers_dict) {
318                         // Specify an alternate personal dictionary
319                         tmp = new char[3];
320                         string("-p").copy(tmp, 2);
321                         tmp[2]= '\0';
322                         argv[argc++] = tmp;
323                         tmp = new char[lyxrc->isp_pers_dict.length() + 1];
324                         lyxrc->isp_pers_dict.copy(tmp, lyxrc->isp_pers_dict.length());
325                         tmp[lyxrc->isp_pers_dict.length()] = '\0';
326                         argv[argc++] = tmp;
327                 }
328                 if (lyxrc->isp_use_input_encoding &&
329                     current_view->buffer()->params.inputenc != "default") {
330                         tmp = new char[3];
331                         string("-T").copy(tmp, 2); tmp[2] = '\0';
332                         argv[argc++] = tmp; // Input encoding
333                         tmp = new char[current_view->buffer()->params.inputenc.length() + 1];
334                         current_view->buffer()->params.inputenc.copy(tmp, current_view->buffer()->params.inputenc.length());
335                         tmp[current_view->buffer()->params.inputenc.length()] = '\0';
336                         argv[argc++] = tmp;
337                 }
338
339                 argv[argc++] = 0;
340
341                 execvp("ispell", const_cast<char * const *>(argv));
342
343                 // free the memory used by string::copy in the
344                 // setup of argv
345                 for (int i= 0; i < argc -1; ++i)
346                         delete[] argv[i];
347                 
348                 lyxerr << "LyX: Failed to start ispell!" << endl;
349                 _exit(0);
350         }
351
352         /* Parent process: Read ispells identification message */
353         // Hmm...what are we using this id msg for? Nothing? (Lgb)
354         // Actually I used it to tell if it's truly Ispell or if it's
355         // aspell -- (kevinatk@home.com)
356         char buf[2048];
357         fd_set infds;
358         struct timeval tv;
359         int retval = 0;
360         FD_ZERO(&infds);
361         FD_SET(pipeout[0], &infds);
362         tv.tv_sec = 15; // fifteen second timeout. Probably too much,
363                         // but it can't really hurt.
364         tv.tv_usec = 0;
365
366         // Configure provides us with macros which are supposed to do
367         // the right typecast.
368         retval = select(SELECT_TYPE_ARG1 (pipeout[0]+1), 
369                         SELECT_TYPE_ARG234 (&infds), 
370                         0, 
371                         0, 
372                         SELECT_TYPE_ARG5 (&tv));
373
374         if (retval > 0) {
375                 // Ok, do the reading. We don't have to FD_ISSET since
376                 // there is only one fd in infds.
377                 fgets(buf, 2048, in);
378                 
379                 // determine if the spell checker is really Aspell
380                 if (strstr(buf, "Aspell"))
381                   actual_spell_checker = ASC_ASPELL;
382                 else
383                   actual_spell_checker = ASC_ISPELL;
384                 
385         } else if (retval == 0) {
386                 // timeout. Give nice message to user.
387                 lyxerr << "Ispell read timed out, what now?" << endl;
388                 // This probably works but could need some thought
389                 isp_pid = -1;
390                 close(pipeout[0]); close(pipeout[1]);
391                 close(pipein[0]); close(pipein[1]);
392                 isp_fd = -1;
393         } else {
394                 // Select returned error
395                 lyxerr << "Select on ispell returned error, what now?" << endl;
396         }
397 }
398
399
400 // Send word to ispell and get reply
401 static
402 isp_result *ispell_check_word(char *word)
403 {
404         //Please rewrite to use string.
405         isp_result *result;
406         char buf[1024], *p;
407         int count, i;
408
409         fputs(word, out); 
410         fputc('\n', out);
411   
412         fgets(buf, 1024, in); 
413   
414         /* I think we have to check if ispell is still alive here because
415            the signal-handler could have disabled blocking on the fd */
416         if (isp_pid == -1) return 0;
417
418         result = new isp_result;
419   
420         switch (*buf) {
421         case '*': // Word found
422                 result->flag = ISP_OK;
423                 break;
424         case '+': // Word found through affix removal
425                 result->flag = ISP_ROOT;
426                 break;
427         case '-': // Word found through compound formation
428                 result->flag = ISP_COMPOUNDWORD;
429                 break;
430         case '\n': // Number or when in terse mode: no problems
431                 result->flag = ISP_IGNORE;
432                 break;
433         case '#': // Not found, no near misses and guesses
434                 result->flag = ISP_UNKNOWN;
435                 break;
436         case '?': // Not found, and no near misses, but guesses (guesses are ignored)
437         case '&': // Not found, but we have near misses
438         {
439                 result->flag = ISP_MISSED;
440                 result->str = buf;
441                 // nb is leaked! where should it be freed? I have to
442                 // admit I do not understand the intent of the code :(
443                 // (JMarc) 
444                 char * nb = new char[result->str.length() + 1];
445                 result->str.copy(nb, result->str.length());
446                 nb[result->str.length()]= '\0';
447                 p = strpbrk(nb+2, " ");
448                 sscanf(p, "%d", &count); // Get near misses count
449                 result->count = count;
450                 if (count) result->misses = new char*[count];
451                 p = strpbrk(nb, ":");
452                 p += 2;
453                 for (i = 0; i < count; ++i) {
454                         result->misses[i] = p;
455                         p = strpbrk(p, ",\n");
456                         *p = 0;
457                         p += 2;
458                 }
459                 break;
460         }
461         default: // This shouldn't happend, but you know Murphy
462                 result->flag = ISP_UNKNOWN;
463         }
464
465         *buf = 0;
466         if (result->flag!= ISP_IGNORE) {
467                 while (*buf!= '\n') fgets(buf, 255, in); /* wait for ispell to finish */
468         }
469         return result;
470 }
471
472
473 static
474 inline void ispell_terminate()
475 {
476         // Note: If you decide to optimize this out when it is not 
477         // needed please note that when Aspell is used this command 
478         // is also needed to save the replacement dictionary.
479         // -- Kevin Atkinson (kevinatk@home.com)
480
481         fputs("#\n", out); // Save personal dictionary
482
483         fflush(out);
484         fclose(out);
485 }
486
487
488 static
489 inline void ispell_terse_mode()
490 {
491         fputs("!\n", out); // Set terse mode (silently accept correct words)
492 }
493
494
495 static
496 inline void ispell_insert_word(char const *word)
497 {
498         fputc('*', out); // Insert word in personal dictionary
499         fputs(word, out);
500         fputc('\n', out);
501 }
502
503
504 static
505 inline void ispell_accept_word(char const *word) 
506 {
507         fputc('@', out); // Accept in this session
508         fputs(word, out);
509         fputc('\n', out);
510 }
511
512 static
513 inline void ispell_store_replacement(char const *mis, string const & cor) {
514         if(actual_spell_checker == ASC_ASPELL) {
515                 fputs("$$ra ", out);
516                 fputs(mis, out);
517                 fputc(',', out);
518                 fputs(cor.c_str(), out);
519                 fputc('\n', out);
520         }
521 }
522
523
524 void ShowSpellChecker()
525 {
526         FL_OBJECT *obj;
527         int ret;
528
529         // Exit if we don't have a document open
530         if (!current_view->getScreen())
531                 return;
532
533         if (fd_form_spell_check == 0) {
534                 fd_form_spell_check = create_form_form_spell_check();
535                 // Make sure pressing the close box does not kill LyX. (RvdK)
536                 fl_set_form_atclose(fd_form_spell_check->form_spell_check, IgnoreCloseBoxCB, 0);
537         }
538
539         // Clear form
540         fl_set_slider_value(fd_form_spell_check->slider, 0);
541         fl_set_slider_bounds(fd_form_spell_check->slider, 0, 100);
542         fl_set_object_label(fd_form_spell_check->text, "");
543         fl_set_input(fd_form_spell_check->input, "");
544         fl_clear_browser(fd_form_spell_check->browser);
545
546         // Show form
547         if (fd_form_spell_check->form_spell_check->visible) {
548                 fl_raise_form(fd_form_spell_check->form_spell_check);
549         } else {
550                 fl_show_form(fd_form_spell_check->form_spell_check,
551                              FL_PLACE_MOUSE, FL_FULLBORDER,
552                              _("Spellchecker"));
553         }
554         fl_deactivate_object(fd_form_spell_check->slider); 
555
556         // deactivate insert, accept, replace, and stop
557         fl_deactivate_object(fd_form_spell_check->insert);
558         fl_deactivate_object(fd_form_spell_check->accept);
559         fl_deactivate_object(fd_form_spell_check->ignore);
560         fl_deactivate_object(fd_form_spell_check->replace);
561         fl_deactivate_object(fd_form_spell_check->stop);
562         fl_deactivate_object(fd_form_spell_check->input);
563         fl_deactivate_object(fd_form_spell_check->browser);
564         fl_set_object_lcol(fd_form_spell_check->insert, FL_INACTIVE);
565         fl_set_object_lcol(fd_form_spell_check->accept, FL_INACTIVE);
566         fl_set_object_lcol(fd_form_spell_check->ignore, FL_INACTIVE);
567         fl_set_object_lcol(fd_form_spell_check->replace, FL_INACTIVE);
568         fl_set_object_lcol(fd_form_spell_check->stop, FL_INACTIVE);
569         fl_set_object_lcol(fd_form_spell_check->input, FL_INACTIVE);
570         fl_set_object_lcol(fd_form_spell_check->browser, FL_INACTIVE);
571
572         while (true){
573                 obj = fl_do_forms();
574                 if (obj == fd_form_spell_check->options){
575                         SpellCheckerOptions();
576                 }
577                 if (obj == fd_form_spell_check->start){
578                         // activate insert, accept, and stop
579                         fl_activate_object(fd_form_spell_check->insert);
580                         fl_activate_object(fd_form_spell_check->accept);
581                         fl_activate_object(fd_form_spell_check->ignore);
582                         fl_activate_object(fd_form_spell_check->stop);
583                         fl_activate_object(fd_form_spell_check->input);
584                         fl_activate_object(fd_form_spell_check->browser);
585                         fl_set_object_lcol(fd_form_spell_check->insert, FL_BLACK);
586                         fl_set_object_lcol(fd_form_spell_check->accept, FL_BLACK);
587                         fl_set_object_lcol(fd_form_spell_check->ignore, FL_BLACK);
588                         fl_set_object_lcol(fd_form_spell_check->stop, FL_BLACK);
589                         fl_set_object_lcol(fd_form_spell_check->input, FL_BLACK);
590                         fl_set_object_lcol(fd_form_spell_check->browser, FL_BLACK);
591                         // activate replace only if the file is not read-only
592                         if (!current_view->buffer()->isReadonly()) { 
593                           fl_activate_object(fd_form_spell_check->replace);
594                           fl_set_object_lcol(fd_form_spell_check->replace, FL_BLACK);
595                         }
596
597                         // deactivate options and start
598                         fl_deactivate_object(fd_form_spell_check->options);
599                         fl_deactivate_object(fd_form_spell_check->start);
600                         fl_set_object_lcol(fd_form_spell_check->options, FL_INACTIVE);
601                         fl_set_object_lcol(fd_form_spell_check->start, FL_INACTIVE);
602
603                         ret = RunSpellChecker(current_view->buffer()->GetLanguage());
604
605                         // deactivate insert, accept, replace, and stop
606                         fl_deactivate_object(fd_form_spell_check->insert);
607                         fl_deactivate_object(fd_form_spell_check->accept);
608                         fl_deactivate_object(fd_form_spell_check->ignore);
609                         fl_deactivate_object(fd_form_spell_check->replace);
610                         fl_deactivate_object(fd_form_spell_check->stop);
611                         fl_deactivate_object(fd_form_spell_check->input);
612                         fl_deactivate_object(fd_form_spell_check->browser);
613                         fl_set_object_lcol(fd_form_spell_check->insert, FL_INACTIVE);
614                         fl_set_object_lcol(fd_form_spell_check->accept, FL_INACTIVE);
615                         fl_set_object_lcol(fd_form_spell_check->ignore, FL_INACTIVE);
616                         fl_set_object_lcol(fd_form_spell_check->replace, FL_INACTIVE);
617                         fl_set_object_lcol(fd_form_spell_check->stop, FL_INACTIVE);
618                         fl_set_object_lcol(fd_form_spell_check->input, FL_INACTIVE);
619                         fl_set_object_lcol(fd_form_spell_check->browser, FL_INACTIVE);
620
621                         // activate options and start
622                         fl_activate_object(fd_form_spell_check->options);
623                         fl_activate_object(fd_form_spell_check->start);
624                         fl_set_object_lcol(fd_form_spell_check->options, FL_BLACK);
625                         fl_set_object_lcol(fd_form_spell_check->start, FL_BLACK);
626
627                         // if RunSpellChecker returns false quit spellchecker
628                         if (!ret) break;
629                 }
630                 if (obj == fd_form_spell_check->done) break;
631         }
632         fl_hide_form(fd_form_spell_check->form_spell_check);
633         current_view->endOfSpellCheck();
634         return;
635 }
636
637
638 // Perform an ispell session
639 static
640 bool RunSpellChecker(string const & lang)
641 {
642         isp_result *result;
643         char *word;
644         int i, oldval, clickline, newvalue;
645         float newval;
646         FL_OBJECT *obj;
647         unsigned int word_count = 0;
648
649         string tmp = (lyxrc->isp_use_alt_lang) ? lyxrc->isp_alt_lang:lang;
650
651         oldval = 0;  /* used for updating slider only when needed */
652         newval = 0.0;
653    
654         /* create ispell process */
655         create_ispell_pipe(tmp);
656
657         if (isp_pid == -1) {
658                 fl_show_message(
659                         _("\n\n"
660                           "The ispell-process has died for some reason. *One* possible reason\n"
661                           "could be that you do not have a dictionary file\n"
662                           "for the language of this document installed.\n"
663                           "Check /usr/lib/ispell or set another\n"
664                           "dictionary in the Spellchecker Options menu."), "", "");
665                 fclose(out);
666                 return true;
667         }
668
669         // Put ispell in terse mode to improve speed
670         ispell_terse_mode();
671
672         while (true) {
673                 word = current_view->nextWord(newval);
674                 if (word == 0) break;
675                 ++word_count;
676                 
677                 // Update slider if and only if value has changed
678                 newvalue = int(100.0*newval);
679                 if(newvalue!= oldval) {
680                         oldval = newvalue;
681                         fl_set_slider_value(fd_form_spell_check->slider, oldval);
682                 }
683
684                 result = ispell_check_word(word);
685                 if (isp_pid == -1) {
686                         delete[] word;
687                         break;
688                 }
689
690                 obj =  fl_check_forms();
691                 if (obj == fd_form_spell_check->stop) {
692                         delete result;
693                         delete[] word;
694                         ispell_terminate();
695                         return true;
696                 }
697                 if (obj == fd_form_spell_check->done) {
698                         delete result;
699                         delete[] word;
700                         ispell_terminate(); 
701                         return false;
702                 }
703     
704                 switch (result->flag) {
705                 case ISP_UNKNOWN:
706                 case ISP_MISSED:
707                         current_view->selectLastWord();
708                         fl_set_object_label(fd_form_spell_check->text, word);
709                         fl_set_input(fd_form_spell_check->input, word);
710                         fl_clear_browser(fd_form_spell_check->browser);
711                         for (i = 0; i < result->count; ++i) {
712                                 fl_add_browser_line(fd_form_spell_check->browser, result->misses[i]);
713                         }
714
715                         clickline = -1;
716                         while (true) {
717                                 obj = fl_do_forms();
718                                 if (obj == fd_form_spell_check->insert) {
719                                         ispell_insert_word(word);
720                                         break;
721                                 }
722                                 if (obj == fd_form_spell_check->accept) {
723                                         ispell_accept_word(word);
724                                         break;
725                                 }
726                                 if (obj == fd_form_spell_check->ignore) {
727                                         break;
728                                 }
729                                 if (obj == fd_form_spell_check->replace || 
730                                     obj == fd_form_spell_check->input) {
731                                         ispell_store_replacement(word, fl_get_input(fd_form_spell_check->input));
732                                         current_view->replaceWord(fl_get_input(fd_form_spell_check->input));
733                                         break;
734                                 }
735                                 if (obj == fd_form_spell_check->browser) {
736                                         // implements double click in the browser window.
737                                         // sent to lyx@via by Mark Burton <mark@cbl.leeds.ac.uk>
738                                         if (clickline == 
739                                             fl_get_browser(fd_form_spell_check->browser)) {
740                                                 ispell_store_replacement(word, fl_get_input(fd_form_spell_check->input));
741                                                 current_view->replaceWord(fl_get_input(fd_form_spell_check->input));
742                                                 break;
743                                         }
744                                         clickline = fl_get_browser(fd_form_spell_check->browser);
745                                         fl_set_input(fd_form_spell_check->input, 
746                                                      fl_get_browser_line(fd_form_spell_check->browser,
747                                                                          fl_get_browser(fd_form_spell_check->browser)));
748                                                      
749                                 }
750                                 if (obj == fd_form_spell_check->stop) {
751                                         delete result;
752                                         delete[] word;
753                                         ispell_terminate();
754                                         return true;
755                                 }
756             
757                                 if (obj == fd_form_spell_check->done) {
758                                         delete result;
759                                         delete[] word;
760                                         ispell_terminate();
761                                         return false;
762                                 }
763                         }
764                 default:
765                         delete result;
766                         delete[] word;
767                 }
768         }
769    
770         if(isp_pid!= -1) {
771                 ispell_terminate();
772                 string word_msg;
773                 word_msg += tostr(word_count);
774                 if (word_count != 1) {
775                         word_msg += _(" words checked.");
776                 } else {
777                         word_msg += _(" word checked.");
778                 }
779                 fl_show_message("", _("Spellchecking completed!"),
780                                 word_msg.c_str());
781                 return false;
782         } else {
783                 fl_show_message(_("The ispell-process has died for some reason.\n"
784                                 "Maybe it has been killed."), "", "");
785                 fclose(out);
786                 return true;
787         }
788 }
789
790
791 //void sigchldhandler(int sig)
792 void sigchldhandler(pid_t pid, int *status)
793
794         //int status ;
795
796         if (isp_pid>0)
797                 //if (waitpid(isp_pid, &status, WNOHANG) == isp_pid) {
798                 if (pid == isp_pid) {
799                         isp_pid= -1;
800                         fcntl(isp_fd, F_SETFL, O_NONBLOCK); /* set the file descriptor
801                                                                to nonblocking so we can 
802                                                                continue */
803                 }
804         //sigchldchecker(sig);
805         sigchldchecker(pid, status);
806 }