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