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