home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Linux / Divers / lyx-0.13.2.tar.gz / lyx-0.13.2.tar / lyx-0.13.2 / src / spellchecker.C < prev    next >
C/C++ Source or Header  |  1998-04-23  |  20KB  |  739 lines

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