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