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