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