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