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