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