kspell.cpp Source File (kspell Library)

| # kspell Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# kspell.cpp

    00001 /* This file is part of the KDE libraries
    00002    Copyright (C) 1997 David Sweet <dsweet@kde.org>
    00003    Copyright (C) 2000-2001 Wolfram Diestel <wolfram@steloj.de>
    00004
    00005    This library is free software; you can redistribute it and/or
    00006    modify it under the terms of the GNU Library General Public
    00007    License version 2 as published by the Free Software Foundation.
    00008
    00009    This library is distributed in the hope that it will be useful,
    00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
    00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00012    Library General Public License for more details.
    00013
    00014    You should have received a copy of the GNU Library General Public License
    00015    along with this library; see the file COPYING.LIB.  If not, write to
    00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00017    Boston, MA 02111-1307, USA.
    00018 */
    00019
    00020 // $Id: kspell.cpp,v 1.97.2.3 2003/09/05 10:46:37 livne Exp $
    00021
    00022 #ifdef HAVE_CONFIG_H
    00023 #include <config.h>
    00024 #endif
    00025
    00026 #include <stdio.h>
    00027 #include <sys/time.h>
    00028 #include <sys/types.h>
    00029 #include <unistd.h>
    00030 #include <ctype.h>
    00031 #include <stdlib.h> // atoi
    00032
    00033 #ifdef HAVE_STRINGS_H
    00034 #include <strings.h>
    00035 #endif
    00036
    00037 #include <qtextcodec.h>
    00038 #include <qtimer.h>
    00039 #include <kapplication.h>
    00040 #include <kdebug.h>
    00041 #include <klocale.h>
    00042 #include "kspell.h"
    00043 #include "kspelldlg.h"
    00044 #include <kwin.h>
    00045 #include <kprocio.h>
    00046
    00047 #define MAXLINELENGTH 10000
    00048
    00049 enum {
    00050         GOOD=     0,
    00051         IGNORE=   1,
    00052         REPLACE=  2,
    00053         MISTAKE=  3
    00054 };
    00055
    00056 class KSpell::KSpellPrivate
    00057 {
    00058 public:
    00059     bool endOfResponse;
    00060     bool m_bIgnoreUpperWords;
    00061     bool m_bIgnoreTitleCase;
    00062 };
    00063
    00064
    00065 //TODO
    00066 //Parse stderr output
    00067 //e.g. -- invalid dictionary name
    00068
    00069 /*
    00070   Things to put in KSpellConfigDlg:
    00071     make root/affix combinations that aren't in the dictionary (-m)
    00072     don't generate any affix/root combinations (-P)
    00073     Report  run-together  words   with   missing blanks as spelling errors.  (-B)
    00074     default dictionary (-d [dictionary])
    00075     personal dictionary (-p [dictionary])
    00076     path to ispell -- NO: ispell should be in $PATH
    00077     */
    00078
    00079
    00080 //  Connects a slot to KProcIO's output signal
    00081 #define OUTPUT(x) (connect (proc, SIGNAL (readReady(KProcIO *)), this, SLOT (x(KProcIO *))))
    00082
    00083 // Disconnect a slot from...
    00084 #define NOOUTPUT(x) (disconnect (proc, SIGNAL (readReady(KProcIO *)), this, SLOT (x(KProcIO *))))
    00085
    00086
    00087
    00088 KSpell::KSpell (QWidget *_parent, const QString &_caption,
    00089                 QObject *obj, const char *slot, KSpellConfig *_ksc,
    00090                 bool _progressbar, bool _modal)
    00091 {
    00092   d=new KSpellPrivate;
    00093
    00094   d->m_bIgnoreUpperWords=false;
    00095   d->m_bIgnoreTitleCase=false;
    00096
    00097   autoDelete = false;
    00098   modaldlg = _modal;
    00099   progressbar = _progressbar;
    00100
    00101   proc=0;
    00102   ksconfig=0;
    00103   ksdlg=0;
    00104   //won't be using the dialog in ksconfig, just the option values
    00105   if (_ksc!=0)
    00106     ksconfig = new KSpellConfig (*_ksc);
    00107   else
    00108     ksconfig = new KSpellConfig;
    00109
    00110   codec = 0;
    00111   switch (ksconfig->encoding())
    00112   {
    00113   case KS_E_LATIN1:
    00114      codec = QTextCodec::codecForName("ISO 8859-1");
    00115      break;
    00116   case KS_E_LATIN2:
    00117      codec = QTextCodec::codecForName("ISO 8859-2");
    00118      break;
    00119   case KS_E_LATIN3:
    00120       codec = QTextCodec::codecForName("ISO 8859-3");
    00121       break;
    00122   case KS_E_LATIN4:
    00123       codec = QTextCodec::codecForName("ISO 8859-4");
    00124       break;
    00125   case KS_E_LATIN5:
    00126       codec = QTextCodec::codecForName("ISO 8859-5");
    00127       break;
    00128   case KS_E_LATIN7:
    00129       codec = QTextCodec::codecForName("ISO 8859-7");
    00130       break;
    00131   case KS_E_LATIN8:
    00132       codec = QTextCodec::codecForName("ISO 8859-8-i");
    00133       break;
    00134   case KS_E_LATIN9:
    00135       codec = QTextCodec::codecForName("ISO 8859-9");
    00136       break;
    00137   case KS_E_LATIN13:
    00138       codec = QTextCodec::codecForName("ISO 8859-13");
    00139       break;
    00140   case KS_E_LATIN15:
    00141       codec = QTextCodec::codecForName("ISO 8859-15");
    00142       break;
    00143   case KS_E_UTF8:
    00144       codec = QTextCodec::codecForName("UTF-8");
    00145       break;
    00146   case KS_E_KOI8R:
    00147       codec = QTextCodec::codecForName("KOI8-R");
    00148       break;
    00149   case KS_E_KOI8U:
    00150       codec = QTextCodec::codecForName("KOI8-U");
    00151       break;
    00152   case KS_E_CP1251:
    00153       codec = QTextCodec::codecForName("CP1251");
    00154       break;
    00155   case KS_E_CP1255:
    00156       codec = QTextCodec::codecForName("CP1255");
    00157       break;
    00158   default:
    00159      break;
    00160   }
    00161
    00162   kdDebug(750) << __FILE__ << ":" << __LINE__ << " Codec = " << (codec ? codec->name() : "<default>") << endl;
    00163
    00164   // copy ignore list from ksconfig
    00165   ignorelist += ksconfig->ignoreList();
    00166
    00167   replacelist += ksconfig->replaceAllList();
    00168   texmode=dlgon=FALSE;
    00169   m_status = Starting;
    00170   dialogsetup = FALSE;
    00171   progres=10;
    00172   curprog=0;
    00173
    00174   dialogwillprocess=FALSE;
    00175   dialog3slot="";
    00176
    00177   personaldict=FALSE;
    00178   dlgresult=-1;
    00179
    00180   caption=_caption;
    00181
    00182   parent=_parent;
    00183
    00184   trystart=0;
    00185   maxtrystart=2;
    00186
    00187   if ( obj && slot )
    00188       // caller wants to know when kspell is ready
    00189       connect (this, SIGNAL (ready(KSpell *)), obj, slot);
    00190   else
    00191       // Hack for modal spell checking
    00192       connect (this, SIGNAL (ready(KSpell *)), this, SLOT( slotModalReady() ) );
    00193   proc=new KProcIO(codec);
    00194
    00195   startIspell();
    00196 }
    00197
    00198 void KSpell::hide() { ksdlg->hide(); }
    00199
    00200 int KSpell::heightDlg() const { return ksdlg->height(); }
    00201 int KSpell::widthDlg() const { return ksdlg->width(); }
    00202
    00203
    00204 void
    00205 KSpell::startIspell()
    00206   //trystart = {0,1,2}
    00207 {
    00208
    00209   kdDebug(750) << "Try #" << trystart << endl;
    00210   if (trystart>0)
    00211     proc->resetAll();
    00212   switch (ksconfig->client())
    00213     {
    00214     case KS_CLIENT_ISPELL:
    00215       *proc << "ispell";
    00216       kdDebug(750) << "Using ispell" << endl;
    00217       break;
    00218     case KS_CLIENT_ASPELL:
    00219       *proc << "aspell";
    00220       kdDebug(750) << "Using aspell" << endl;
    00221       break;
    00222     case KS_CLIENT_HSPELL:
    00223       *proc << "hspell";
    00224       kdDebug(750) << "Using hspell" << endl;
    00225       break;
    00226     }
    00227   // TODO: add option -h to ignore HTML (XML) code
    00228   if (ksconfig->client() == KS_CLIENT_ISPELL || ksconfig->client() == KS_CLIENT_ASPELL)
    00229   {
    00230   *proc << "-a" << "-S";
    00231   if (ksconfig->noRootAffix())
    00232     {
    00233       *proc<<"-m";
    00234     }
    00235   if (ksconfig->runTogether())
    00236     {
    00237       *proc << "-B";
    00238     }
    00239   else
    00240     {
    00241       *proc << "-C";
    00242     }
    00243
    00244   if (trystart<2)
    00245     {
    00246       if (! ksconfig->dictionary().isEmpty())
    00247         {
    00248           kdDebug(750) << "using dictionary [" << ksconfig->dictionary() << "]" << endl;
    00249           *proc << "-d";
    00250           *proc << ksconfig->dictionary();
    00251         }
    00252     }
    00253
    00254   //Note to potential debuggers:  -Tlatin2 _is_ being added on the
    00255   //  _first_ try.  But, some versions of ispell will fail with this
    00256   // option, so kspell tries again without it.  That's why as 'ps -ax'
    00257   // shows "ispell -a -S ..." withou the "-Tlatin2" option.
    00258
    00259   if (trystart<1)
    00260     switch (ksconfig->encoding())
    00261       {
    00262       case KS_E_LATIN1:
    00263         *proc << "-Tlatin1";
    00264         break;
    00265       case KS_E_LATIN2:
    00266         *proc << "-Tlatin2";
    00267         break;
    00268       case KS_E_LATIN3:
    00269         *proc << "-Tlatin3";
    00270         break;
    00271
    00272       // add the other charsets here
    00273       case KS_E_LATIN4:
    00274       case KS_E_LATIN5:
    00275       case KS_E_LATIN7:
    00276       case KS_E_LATIN8:
    00277       case KS_E_LATIN9:
    00278       case KS_E_LATIN13:
    00279       case KS_E_LATIN15:
    00280
    00281         // will work, if this is the default charset in the dictionary
    00282         kdError(750) << "charsets iso-8859-4 .. iso-8859-15 not supported yet" << endl;
    00283         break;
    00284
    00285       case KS_E_UTF8:
    00286         *proc << "-Tutf8";
    00287         break;
    00288
    00289       case KS_E_KOI8U:
    00290         *proc << "-w'"; // add ' as a word char
    00291         break;
    00292
    00293       }
    00294
    00295
    00296
    00297
    00298   /*
    00299   if (ksconfig->personalDict()[0]!='\0')
    00300     {
    00301       kdDebug(750) << "personal dictionary [" << ksconfig->personalDict() << "]" << endl;
    00302       *proc << "-p";
    00303       *proc << ksconfig->personalDict();
    00304     }
    00305     */
    00306
    00307
    00308   // -a : pipe mode
    00309   // -S : sort suggestions by probable correctness
    00310   }
    00311   else       // hspell doesn't need all the rest of the options
    00312     *proc << "-a";
    00313
    00314   if (trystart==0) //don't connect these multiple times
    00315     {
    00316       connect (proc, SIGNAL (  receivedStderr (KProcess *, char *, int)),
    00317                this, SLOT (ispellErrors (KProcess *, char *, int)));
    00318
    00319
    00320       connect(proc, SIGNAL(processExited(KProcess *)),
    00321               this, SLOT (ispellExit (KProcess *)));
    00322
    00323       OUTPUT(KSpell2);
    00324     }
    00325
    00326   if (proc->start ()==FALSE )
    00327   {
    00328       m_status = Error;
    00329       QTimer::singleShot( 0, this, SLOT(emitDeath()));
    00330   }
    00331 }
    00332
    00333 void
    00334 KSpell::ispellErrors (KProcess *, char *buffer, int buflen)
    00335 {
    00336   buffer [buflen-1] = '\0';
    00337   //  kdDebug(750) << "ispellErrors [" << buffer << "]\n" << endl;
    00338 }
    00339
    00340 void KSpell::KSpell2 (KProcIO *)
    00341
    00342 {
    00343   kdDebug(750) << "KSpell::KSpell2" << endl;
    00344   trystart=maxtrystart;  //We've officially started ispell and don't want
    00345        //to try again if it dies.
    00346   QString line;
    00347
    00348   if (proc->fgets (line, TRUE)==-1)
    00349   {
    00350      QTimer::singleShot( 0, this, SLOT(emitDeath()));
    00351      return;
    00352   }
    00353
    00354
    00355   if (line[0]!='@') //@ indicates that ispell is working fine
    00356   {
    00357      QTimer::singleShot( 0, this, SLOT(emitDeath()));
    00358      return;
    00359   }
    00360
    00361   //We want to recognize KDE in any text!
    00362   if (ignore ("kde")==FALSE)
    00363   {
    00364      kdDebug(750) << "@KDE was FALSE" << endl;
    00365      QTimer::singleShot( 0, this, SLOT(emitDeath()));
    00366      return;
    00367   }
    00368
    00369   //We want to recognize linux in any text!
    00370   if (ignore ("linux")==FALSE)
    00371   {
    00372      kdDebug(750) << "@Linux was FALSE" << endl;
    00373      QTimer::singleShot( 0, this, SLOT(emitDeath()));
    00374      return;
    00375   }
    00376
    00377   NOOUTPUT (KSpell2);
    00378
    00379   m_status = Running;
    00380   emit ready(this);
    00381 }
    00382
    00383 void
    00384 KSpell::setUpDialog (bool reallyuseprogressbar)
    00385 {
    00386   if (dialogsetup)
    00387     return;
    00388
    00389   //Set up the dialog box
    00390   ksdlg=new KSpellDlg (parent, "dialog",
    00391                        progressbar && reallyuseprogressbar, modaldlg );
    00392   ksdlg->setCaption (caption);
    00393   connect (ksdlg, SIGNAL (command (int)), this,
    00394                 SLOT (slotStopCancel (int)) );
    00395   connect (this, SIGNAL ( progress (unsigned int) ),
    00396            ksdlg, SLOT ( slotProgress (unsigned int) ));
    00397 #ifdef Q_WS_X11 // FIXME(E): Implement for Qt/Embedded
    00398   KWin::setIcons (ksdlg->winId(), kapp->icon(), kapp->miniIcon());
    00399 #endif
    00400   if ( modaldlg )
    00401       ksdlg->setFocus();
    00402   dialogsetup = TRUE;
    00403 }
    00404
    00405 bool KSpell::addPersonal (const QString & word)
    00406 {
    00407   QString qs = word.simplifyWhiteSpace();
    00408
    00409   //we'll let ispell do the work here b/c we can
    00410   if (qs.find (' ')!=-1 || qs.isEmpty())    // make sure it's a _word_
    00411     return FALSE;
    00412
    00413   qs.prepend ("*");
    00414   personaldict=TRUE;
    00415
    00416   return proc->fputs(qs);
    00417 }
    00418
    00419 bool KSpell::writePersonalDictionary ()
    00420 {
    00421   return proc->fputs ("#");
    00422 }
    00423
    00424 bool KSpell::ignore (const QString & word)
    00425 {
    00426   QString qs = word.simplifyWhiteSpace();
    00427
    00428   //we'll let ispell do the work here b/c we can
    00429   if (qs.find (' ')!=-1 || qs.isEmpty())    // make sure it's a _word_
    00430     return FALSE;
    00431
    00432   qs.prepend ("@");
    00433
    00434   return proc->fputs(qs);
    00435 }
    00436
    00437 bool
    00438 KSpell::cleanFputsWord (const QString & s, bool appendCR)
    00439 {
    00440   QString qs(s);
    00441   //bool firstchar = TRUE;
    00442   bool empty = TRUE;
    00443
    00444   for (unsigned int i=0; i<qs.length(); i++)
    00445   {
    00446     //we need some punctuation for ornaments
    00447     if (qs[i] != '\'' && qs[i] != '\"' && qs[i] != '-'
    00448         && qs[i].isPunct() || qs[i].isSpace())
    00449     {
    00450       qs.remove(i,1);
    00451       i--;
    00452     } else {
    00453       if (qs[i].isLetter()) empty=FALSE;
    00454     }
    00455   }
    00456
    00457   // don't check empty words, otherwise synchronisation will lost
    00458   if (empty) return FALSE;
    00459
    00460   return proc->fputs("^"+qs, appendCR);
    00461 }
    00462
    00463 bool
    00464 KSpell::cleanFputs (const QString & s, bool appendCR)
    00465 {
    00466   QString qs(s);
    00467   unsigned l = qs.length();
    00468
    00469   // some uses of '$' (e.g. "$0") cause ispell to skip all following text
    00470   for(unsigned int i = 0; i < l; ++i)
    00471   {
    00472     if(qs[i] == '$')
    00473       qs[i] = ' ';
    00474   }
    00475
    00476   if (l<MAXLINELENGTH)
    00477     {
    00478       if (qs.isEmpty())
    00479         qs="";
    00480
    00481       return proc->fputs ("^"+qs, appendCR);
    00482     }
    00483   else
    00484     return proc->fputs ("^\n",appendCR);
    00485 }
    00486
    00487 bool KSpell::checkWord (const QString & buffer, bool _usedialog)
    00488 {
    00489   QString qs = buffer.simplifyWhiteSpace();
    00490
    00491   if (qs.find (' ')!=-1 || qs.isEmpty())    // make sure it's a _word_
    00492     return FALSE;
    00493
    00495   dialog3slot = SLOT (checkWord3());
    00496
    00497   usedialog=_usedialog;
    00498   setUpDialog(FALSE);
    00499   if (_usedialog)
    00500     {
    00501       emitProgress();
    00502       ksdlg->show();
    00503     }
    00504   else
    00505     ksdlg->hide();
    00506
    00507   OUTPUT (checkWord2);
    00508   //  connect (this, SIGNAL (dialog3()), this, SLOT (checkWord3()));
    00509
    00510   proc->fputs ("%"); // turn off terse mode
    00511   proc->fputs (buffer); // send the word to ispell
    00512
    00513   return TRUE;
    00514 }
    00515
    00516 void KSpell::checkWord2 (KProcIO *)
    00517 {
    00518   QString word;
    00519
    00520   QString line;
    00521   proc->fgets (line, TRUE); //get ispell's response
    00522
    00523 /* ispell man page: "Each sentence of text input is terminated with an
    00524    additional blank line,  indicating that ispell has completed processing
    00525    the input line." */
    00526   QString blank_line;
    00527   proc->fgets(blank_line, TRUE); // eat the blank line
    00528
    00529   NOOUTPUT(checkWord2);
    00530
    00531   bool mistake = (parseOneResponse(line, word, sugg) == MISTAKE);
    00532   if ( mistake && usedialog )
    00533     {
    00534       cwword=word;
    00535       dialog (word, sugg, SLOT (checkWord3()));
    00536       return;
    00537     }
    00538   else if( mistake )
    00539     {
    00540       emit misspelling (word, sugg, lastpos);
    00541     }
    00542
    00543   //emits a "corrected" signal _even_ if no change was made
    00544   //so that the calling program knows when the check is complete
    00545   emit corrected (word, word, 0L);
    00546 }
    00547
    00548 void KSpell::checkWord3 ()
    00549 {
    00550   disconnect (this, SIGNAL (dialog3()), this, SLOT (checkWord3()));
    00551
    00552   emit corrected (cwword, replacement(), 0L);
    00553 }
    00554
    00555 QString KSpell::funnyWord (const QString & word)
    00556   // composes a guess from ispell to a readable word
    00557   // e.g. "re+fry-y+ies" -> "refries"
    00558 {
    00559   QString qs;
    00560   unsigned int i=0;
    00561
    00562   for (i=0; word [i]!='\0';i++)
    00563     {
    00564       if (word [i]=='+')
    00565         continue;
    00566       if (word [i]=='-')
    00567         {
    00568           QString shorty;
    00569           unsigned int j;
    00570           int k;
    00571
    00572           for (j=i+1;word [j]!='\0' && word [j]!='+' &&
    00573                  word [j]!='-';j++)
    00574             shorty+=word [j];
    00575           i=j-1;
    00576
    00577           if ((k=qs.findRev (shorty))==0 || k!=-1)
    00578             qs.remove (k,shorty.length());
    00579           else
    00580             {
    00581               qs+='-';
    00582               qs+=shorty;  //it was a hyphen, not a '-' from ispell
    00583             }
    00584         }
    00585       else
    00586         qs+=word [i];
    00587     }
    00588   return qs;
    00589 }
    00590
    00591
    00592 int KSpell::parseOneResponse (const QString &buffer, QString &word, QStringList & sugg)
    00593   // buffer is checked, word and sugg are filled in
    00594   // returns
    00595   //   GOOD    if word is fine
    00596   //   IGNORE  if word is in ignorelist
    00597   //   REPLACE if word is in replacelist
    00598   //   MISTAKE if word is misspelled
    00599 {
    00600   word = "";
    00601   posinline=0;
    00602
    00603   sugg.clear();
    00604
    00605   if (buffer [0]=='*' || buffer[0] == '+' || buffer[0] == '-')
    00606     {
    00607       return GOOD;
    00608     }
    00609
    00610   if (buffer [0]=='&' || buffer [0]=='?' || buffer [0]=='#')
    00611     {
    00612       int i,j;
    00613
    00614
    00615       word = buffer.mid (2,buffer.find (' ',3)-2);
    00616       //check() needs this
    00617       orig=word;
    00618
    00619       if(d->m_bIgnoreTitleCase && word==word.upper())
    00620           return IGNORE;
    00621
    00622       if(d->m_bIgnoreUpperWords && word[0]==word[0].upper())
    00623       {
    00624           QString text=word[0]+word.right(word.length()-1).lower();
    00625           if(text==word)
    00626               return IGNORE;
    00627       }
    00628
    00630       //We don't take advantage of ispell's ignore function because
    00631       //we can't interrupt ispell's output (when checking a large
    00632       //buffer) to add a word to _it's_ ignore-list.
    00633       if (ignorelist.findIndex(word.lower())!=-1)
    00634         return IGNORE;
    00635
    00637       QString qs2;
    00638
    00639       if (buffer.find(':')!=-1)
    00640         qs2=buffer.left (buffer.find (':'));
    00641       else
    00642         qs2=buffer;
    00643
    00644       posinline = qs2.right( qs2.length()-qs2.findRev(' ') ).toInt()-1;
    00645
    00647       QStringList::Iterator it = replacelist.begin();
    00648       for(;it != replacelist.end(); ++it, ++it) // Skip two entries at a time.
    00649       {
    00650          if (word == *it) // Word matches
    00651          {
    00652             ++it;
    00653             word = *it;   // Replace it with the next entry
    00654             return REPLACE;
    00655          }
    00656       }
    00657
    00659       if (buffer [0] != '#')
    00660         {
    00661           QString qs = buffer.mid(buffer.find(':')+2, buffer.length());
    00662           qs+=',';
    00663           sugg.clear();
    00664           i=j=0;
    00665           while ((unsigned int)i<qs.length())
    00666             {
    00667               QString temp = qs.mid (i,(j=qs.find (',',i))-i);
    00668               sugg.append (funnyWord (temp));
    00669
    00670               i=j+2;
    00671             }
    00672         }
    00673
    00674       if ((sugg.count()==1) && (sugg.first() == word))
    00675         return GOOD;
    00676
    00677       return MISTAKE;
    00678     }
    00679
    00680
    00681   kdError(750) << "HERE?: [" << buffer << "]" << endl;
    00682   kdError(750) << "Please report this to dsweet@kde.org" << endl;
    00683   kdError(750) << "Thank you!" << endl;
    00684   emit done((bool)FALSE);
    00685   emit done (KSpell::origbuffer);
    00686   return MISTAKE;
    00687 }
    00688
    00689 bool KSpell::checkList (QStringList *_wordlist, bool _usedialog)
    00690   // prepare check of string list
    00691 {
    00692   wordlist=_wordlist;
    00693   if ((totalpos=wordlist->count())==0)
    00694     return FALSE;
    00695   wlIt = wordlist->begin();
    00696   usedialog=_usedialog;
    00697
    00698   // prepare the dialog
    00699   setUpDialog();
    00700
    00701   //set the dialog signal handler
    00702   dialog3slot = SLOT (checkList4 ());
    00703
    00704   proc->fputs ("%"); // turn off terse mode & check one word at a time
    00705
    00706   //lastpos now counts which *word number* we are at in checkListReplaceCurrent()
    00707   lastpos = -1;
    00708   checkList2();
    00709
    00710   // when checked, KProcIO calls checkList3a
    00711   OUTPUT(checkList3a);
    00712
    00713   return TRUE;
    00714 }
    00715
    00716 void KSpell::checkList2 ()
    00717   // send one word from the list to KProcIO
    00718   // invoked first time by checkList, later by checkListReplaceCurrent and checkList4
    00719 {
    00720   // send next word
    00721   if (wlIt != wordlist->end())
    00722     {
    00723       kdDebug(750) << "KS::cklist2 " << lastpos << ": " << *wlIt << endl;
    00724
    00725       d->endOfResponse = FALSE;
    00726       bool put;
    00727       lastpos++; offset=0;
    00728       put = cleanFputsWord (*wlIt);
    00729       ++wlIt;
    00730
    00731       // when cleanFPutsWord failed (e.g. on empty word)
    00732       // try next word; may be this is not good for other
    00733       // problems, because this will make read the list up to the end
    00734       if (!put) {
    00735         checkList2();
    00736       }
    00737     }
    00738   else
    00739     // end of word list
    00740     {
    00741       NOOUTPUT(checkList3a);
    00742       ksdlg->hide();
    00743       emit done(TRUE);
    00744     }
    00745 }
    00746
    00747 void KSpell::checkList3a (KProcIO *)
    00748   // invoked by KProcIO, when data from ispell are read
    00749 {
    00750   //kdDebug(750) << "start of checkList3a" << endl;
    00751
    00752   // don't read more data, when dialog is waiting
    00753   // for user interaction
    00754   if (dlgon) {
    00755     //kdDebug(750) << "dlgon: don't read more data" << endl;
    00756     return;
    00757   }
    00758
    00759   int e, tempe;
    00760
    00761   QString word;
    00762   QString line;
    00763
    00764     do
    00765       {
    00766         tempe=proc->fgets (line, TRUE); //get ispell's response
    00767
    00768         //kdDebug(750) << "checkList3a: read bytes [" << tempe << "]" << endl;
    00769
    00770
    00771         if (tempe == 0) {
    00772           d->endOfResponse = TRUE;
    00773           //kdDebug(750) << "checkList3a: end of resp" << endl;
    00774         } else if (tempe>0) {
    00775           if ((e=parseOneResponse (line, word, sugg))==MISTAKE ||
    00776               e==REPLACE)
    00777             {
    00778               dlgresult=-1;
    00779
    00780               if (e==REPLACE)
    00781                 {
    00782                   QString old = *(--wlIt); ++wlIt;
    00783                   dlgreplacement=word;
    00784                   checkListReplaceCurrent();
    00785                   // inform application
    00786                   emit corrected (old, *(--wlIt), lastpos); ++wlIt;
    00787                 }
    00788               else if( usedialog )
    00789                 {
    00790                   cwword=word;
    00791                   dlgon=TRUE;
    00792                   // show the dialog
    00793                   dialog (word, sugg, SLOT (checkList4()));
    00794                   return;
    00795                 }
    00796               else
    00797                 {
    00798                   emit misspelling (word, sugg, lastpos);
    00799                 }
    00800             }
    00801
    00802         }
    00803         emitProgress (); //maybe
    00804
    00805         // stop when empty line or no more data
    00806       } while (tempe > 0);
    00807
    00808     //kdDebug(750) << "checkList3a: exit loop with [" << tempe << "]" << endl;
    00809
    00810     // if we got an empty line, t.e. end of ispell/aspell response
    00811     // and the dialog isn't waiting for user interaction, send next word
    00812     if (d->endOfResponse && !dlgon) {
    00813       //kdDebug(750) << "checkList3a: send next word" << endl;
    00814       checkList2();
    00815     }
    00816 }
    00817
    00818 void KSpell::checkListReplaceCurrent () {
    00819
    00820   // go back to misspelled word
    00821   wlIt--;
    00822
    00823   QString s = *wlIt;
    00824   s.replace(posinline+offset,orig.length(),replacement());
    00825   offset += replacement().length()-orig.length();
    00826   wordlist->insert (wlIt, s);
    00827   wlIt = wordlist->remove (wlIt);
    00828   // wlIt now points to the word after the repalced one
    00829
    00830 }
    00831
    00832 void KSpell::checkList4 ()
    00833   // evaluate dialog return, when a button was pressed there
    00834 {
    00835   dlgon=FALSE;
    00836   QString old;
    00837
    00838   disconnect (this, SIGNAL (dialog3()), this, SLOT (checkList4()));
    00839
    00840   //others should have been processed by dialog() already
    00841   switch (dlgresult)
    00842     {
    00843     case KS_REPLACE:
    00844     case KS_REPLACEALL:
    00845       kdDebug(750) << "KS: cklist4: lastpos: " << lastpos << endl;
    00846       old = *(--wlIt); ++wlIt;
    00847       // replace word
    00848       checkListReplaceCurrent();
    00849       emit corrected (old, *(--wlIt), lastpos); ++wlIt;
    00850       break;
    00851     case KS_CANCEL:
    00852       ksdlg->hide();
    00853       emit done ((bool)FALSE);
    00854       return;
    00855     case KS_STOP:
    00856       ksdlg->hide();
    00857       emit done (TRUE);
    00858       break;
    00859     };
    00860
    00861   // read more if there is more, otherwise send next word
    00862   if (!d->endOfResponse) {
    00863     //kdDebug(750) << "checkList4: read more from response" << endl;
    00864       checkList3a(NULL);
    00865   }
    00866 }
    00867
    00868 bool KSpell::check( const QString &_buffer, bool _usedialog )
    00869 {
    00870   QString qs;
    00871
    00872   usedialog=_usedialog;
    00873   setUpDialog ();
    00874   //set the dialog signal handler
    00875   dialog3slot = SLOT (check3 ());
    00876
    00877   kdDebug(750) << "KS: check" << endl;
    00878   origbuffer = _buffer;
    00879   if ( ( totalpos = origbuffer.length() ) == 0 )
    00880     {
    00881       emit done(origbuffer);
    00882       return FALSE;
    00883     }
    00884
    00885
    00886   // Torben: I corrected the \n\n problem directly in the
    00887   //         origbuffer since I got errors otherwise
    00888   if ( origbuffer.right(2) != "\n\n" )
    00889     {
    00890       if (origbuffer.at(origbuffer.length()-1)!='\n')
    00891         {
    00892           origbuffer+='\n';
    00893           origbuffer+='\n'; //shouldn't these be removed at some point?
    00894         }
    00895       else
    00896         origbuffer+='\n';
    00897     }
    00898
    00899   newbuffer=origbuffer;
    00900
    00901   // KProcIO calls check2 when read from ispell
    00902   OUTPUT(check2);
    00903   proc->fputs ("!");
    00904
    00905   //lastpos is a position in newbuffer (it has offset in it)
    00906   offset=lastlastline=lastpos=lastline=0;
    00907
    00908   emitProgress ();
    00909
    00910   // send first buffer line
    00911   int i = origbuffer.find('\n', 0)+1;
    00912   qs=origbuffer.mid (0,i);
    00913   cleanFputs (qs,FALSE);
    00914
    00915   lastline=i; //the character position, not a line number
    00916
    00917   if (usedialog)
    00918     {
    00919       emitProgress();
    00920       ksdlg->show();
    00921     }
    00922   else
    00923     ksdlg->hide();
    00924
    00925   return TRUE;
    00926 }
    00927
    00928 void KSpell::check2 (KProcIO *)
    00929   // invoked by KProcIO when read from ispell
    00930 {
    00931   int e, tempe;
    00932   QString word;
    00933   QString line;
    00934
    00935   do
    00936     {
    00937       tempe=proc->fgets (line); //get ispell's response
    00938       kdDebug(750) << "KSpell::check2 (" << tempe << "b)" << endl;
    00939
    00940       if (tempe>0)
    00941         {
    00942           if ((e=parseOneResponse (line, word, sugg))==MISTAKE ||
    00943               e==REPLACE)
    00944             {
    00945               dlgresult=-1;
    00946
    00947               // for multibyte encoding posinline needs correction
    00948               if (ksconfig->encoding() == KS_E_UTF8) {
    00949                 // kdDebug(750) << "line: " << origbuffer.mid(lastlastline,
    00950                 // lastline-lastlastline) << endl;
    00951                 // kdDebug(750) << "posinline uncorr: " << posinline << endl;
    00952
    00953                 // convert line to UTF-8, cut at pos, convert back to UCS-2
    00954                 // and get string length
    00955                 posinline = (QString::fromUtf8(
    00956                    origbuffer.mid(lastlastline,lastline-lastlastline).utf8(),
    00957                    posinline)).length();
    00958                 // kdDebug(750) << "posinline corr: " << posinline << endl;
    00959               }
    00960
    00961               lastpos=posinline+lastlastline+offset;
    00962
    00963               //orig is set by parseOneResponse()
    00964
    00965               if (e==REPLACE)
    00966                 {
    00967                   dlgreplacement=word;
    00968                   emit corrected (orig, replacement(), lastpos);
    00969                   offset+=replacement().length()-orig.length();
    00970                   newbuffer.replace (lastpos, orig.length(), word);
    00971                 }
    00972               else  //MISTAKE
    00973                 {
    00974                   cwword=word;
    00975                   //kdDebug(750) << "(Before dialog) word=[" << word << "] cwword =[" << cwword << "]\n" << endl;
    00976                   if ( usedialog ) {
    00977                       // show the word in the dialog
    00978                       dialog (word, sugg, SLOT (check3()));
    00979                   } else {
    00980                       // No dialog, just emit misspelling and continue
    00981                       emit misspelling (word, sugg, lastpos);
    00982                       dlgresult = KS_IGNORE;
    00983                       check3();
    00984                   }
    00985                   return;
    00986                 }
    00987             }
    00988
    00989           }
    00990
    00991       emitProgress (); //maybe
    00992
    00993     } while (tempe>0);
    00994
    00995   proc->ackRead();
    00996
    00997
    00998   if (tempe==-1) //we were called, but no data seems to be ready...
    00999     return;
    01000
    01001   //If there is more to check, then send another line to ISpell.
    01002   if ((unsigned int)lastline<origbuffer.length())
    01003     {
    01004       int i;
    01005       QString qs;
    01006
    01007       //kdDebug(750) << "[EOL](" << tempe << ")[" << temp << "]" << endl;
    01008
    01009       lastpos=(lastlastline=lastline)+offset; //do we really want this?
    01010       i=origbuffer.find('\n', lastline)+1;
    01011       qs=origbuffer.mid (lastline, i-lastline);
    01012       cleanFputs (qs,FALSE);
    01013       lastline=i;
    01014       return;
    01015     }
    01016   else
    01017   //This is the end of it all
    01018     {
    01019       ksdlg->hide();
    01020       //      kdDebug(750) << "check2() done" << endl;
    01021       newbuffer.truncate (newbuffer.length()-2);
    01022       emitProgress();
    01023       emit done (newbuffer);
    01024     }
    01025 }
    01026
    01027 void KSpell::check3 ()
    01028   // evaluates the return value of the dialog
    01029 {
    01030   disconnect (this, SIGNAL (dialog3()), this, SLOT (check3()));
    01031
    01032   kdDebug(750) << "check3 [" << cwword << "] [" << replacement() << "] " << dlgresult << endl;
    01033
    01034   //others should have been processed by dialog() already
    01035   switch (dlgresult)
    01036     {
    01037     case KS_REPLACE:
    01038     case KS_REPLACEALL:
    01039       offset+=replacement().length()-cwword.length();
    01040       newbuffer.replace (lastpos, cwword.length(),
    01041                          replacement());
    01042       emit corrected (dlgorigword, replacement(), lastpos);
    01043       break;
    01044     case KS_CANCEL:
    01045     //      kdDebug(750) << "cancelled\n" << endl;
    01046       ksdlg->hide();
    01047       emit done (origbuffer);
    01048       return;
    01049     case KS_STOP:
    01050       ksdlg->hide();
    01051       //buffer=newbuffer);
    01052       emitProgress();
    01053       emit done (newbuffer);
    01054       return;
    01055     };
    01056
    01057   proc->ackRead();
    01058 }
    01059
    01060 void
    01061 KSpell::slotStopCancel (int result)
    01062 {
    01063   if (dialogwillprocess)
    01064     return;
    01065
    01066   kdDebug(750) << "KSpell::slotStopCancel [" << result << "]" << endl;
    01067
    01068   if (result==KS_STOP || result==KS_CANCEL)
    01069     if (!dialog3slot.isEmpty())
    01070       {
    01071         dlgresult=result;
    01072         connect (this, SIGNAL (dialog3()), this, dialog3slot.ascii());
    01073         emit dialog3();
    01074       }
    01075 }
    01076
    01077
    01078 void KSpell::dialog(const QString & word, QStringList & sugg, const char *_slot)
    01079 {
    01080   dlgorigword=word;
    01081
    01082   dialog3slot=_slot;
    01083   dialogwillprocess=TRUE;
    01084   connect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
    01085   ksdlg->init (word, &sugg);
    01086   emit misspelling (word, sugg, lastpos);
    01087
    01088   emitProgress();
    01089   ksdlg->show();
    01090 }
    01091
    01092 void KSpell::dialog2 (int result)
    01093 {
    01094   QString qs;
    01095
    01096   disconnect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
    01097   dialogwillprocess=FALSE;
    01098   dlgresult=result;
    01099   ksdlg->standby();
    01100
    01101   dlgreplacement=ksdlg->replacement();
    01102
    01103   //process result here
    01104   switch (dlgresult)
    01105     {
    01106
    01107     case KS_IGNORE:
    01108       emit ignoreword(dlgorigword);
    01109       break;
    01110     case KS_IGNOREALL:
    01111       // would be better to lower case only words with beginning cap
    01112       ignorelist.prepend(dlgorigword.lower());
    01113       emit ignoreall (dlgorigword);
    01114       break;
    01115     case KS_ADD:
    01116       addPersonal (dlgorigword);
    01117       personaldict=TRUE;
    01118       emit addword (dlgorigword);
    01119       // adding to pesonal dict takes effect at the next line, not the current
    01120       ignorelist.prepend(dlgorigword.lower());
    01121       break;
    01122     case KS_REPLACEALL:
    01123       replacelist.append (dlgorigword);
    01124       QString _replacement = replacement();
    01125       replacelist.append (_replacement);
    01126       emit replaceall( dlgorigword ,  _replacement );
    01127       break;
    01128     }
    01129
    01130   connect (this, SIGNAL (dialog3()), this, dialog3slot.ascii());
    01131   emit dialog3();
    01132 }
    01133
    01134
    01135 KSpell:: ~KSpell ()
    01136 {
    01137   if(d)
    01138       delete d;
    01139
    01140   if (proc)
    01141       delete proc;
    01142   if (ksconfig)
    01143     delete ksconfig;
    01144
    01145   if (ksdlg)
    01146     delete  ksdlg;
    01147 }
    01148
    01149
    01150 KSpellConfig KSpell::ksConfig () const
    01151 {
    01152   ksconfig->setIgnoreList(ignorelist);
    01153   ksconfig->setReplaceAllList(replacelist);
    01154   return *ksconfig;
    01155 }
    01156
    01157 void KSpell::cleanUp ()
    01158 {
    01159   if (m_status == Cleaning) return; // Ignore
    01160   if (m_status == Running)
    01161   {
    01162     if (personaldict)
    01163        writePersonalDictionary();
    01164     m_status = Cleaning;
    01165   }
    01166   proc->closeStdin();
    01167 }
    01168
    01169 void KSpell::ispellExit (KProcess *)
    01170 {
    01171   kdDebug() << "KSpell::ispellExit() " << m_status << endl;
    01172
    01173   if ((m_status == Starting) && (trystart<maxtrystart))
    01174   {
    01175     trystart++;
    01176     startIspell();
    01177     return;
    01178   }
    01179
    01180   if (m_status == Starting)
    01181      m_status = Error;
    01182   else if (m_status == Cleaning)
    01183      m_status = Finished;
    01184   else if (m_status == Running)
    01185      m_status = Crashed;
    01186   else // Error, Finished, Crashed
    01187      return; // Dead already
    01188
    01189   kdDebug(750) << "Death" << endl;
    01190   QTimer::singleShot( 0, this, SLOT(emitDeath()));
    01191 }
    01192
    01193 // This is always called from the event loop to make
    01194 // sure that the receiver can safely delete the
    01195 // KSpell object.
    01196 void KSpell::emitDeath()
    01197 {
    01198   bool deleteMe = autoDelete; // Can't access object after next call!
    01199   emit death();
    01200   if (deleteMe)
    01201      delete this;
    01202 }
    01203
    01204 void KSpell::setProgressResolution (unsigned int res)
    01205 {
    01206   progres=res;
    01207 }
    01208
    01209 void KSpell::emitProgress ()
    01210 {
    01211   uint nextprog = (uint) (100.*lastpos/(double)totalpos);
    01212
    01213   if (nextprog>=curprog)
    01214     {
    01215       curprog=nextprog;
    01216       emit progress (curprog);
    01217     }
    01218 }
    01219
    01220 void KSpell::moveDlg (int x, int y)
    01221 {
    01222   QPoint pt (x,y), pt2;
    01223   pt2=parent->mapToGlobal (pt);
    01224   ksdlg->move (pt2.x(),pt2.y());
    01225 }
    01226
    01227 void KSpell::setIgnoreUpperWords(bool _ignore)
    01228 {
    01229     d->m_bIgnoreUpperWords=_ignore;
    01230 }
    01231
    01232 void KSpell::setIgnoreTitleCase(bool _ignore)
    01233 {
    01234     d->m_bIgnoreTitleCase=_ignore;
    01235 }
    01236 // --------------------------------------------------
    01237 // Stuff for modal (blocking) spell checking
    01238 //
    01239 // Written by Torben Weis <weis@kde.org>. So please
    01240 // send bug reports regarding the modal stuff to me.
    01241 // --------------------------------------------------
    01242
    01243 int
    01244 KSpell::modalCheck( QString& text )
    01245 {
    01246     return modalCheck( text,0 );
    01247 }
    01248
    01249 int
    01250 KSpell::modalCheck( QString& text, KSpellConfig* _kcs )
    01251 {
    01252     modalreturn = 0;
    01253     modaltext = text;
    01254
    01255     /*modalWidgetHack = new QWidget(0,0,WType_Modal);
    01256     modalWidgetHack->setGeometry(-10,-10,2,2);
    01257     */
    01258
    01259     // kdDebug() << "KSpell1" << endl;
    01260     KSpell* spell = new KSpell( 0L, i18n("Spell Checker"), 0 ,
    01261                                 0, _kcs, true, true );
    01262     //modalWidgetHack->show();
    01263     //qApp->enter_loop();
    01264
    01265     while (spell->status()!=Finished)
    01266       kapp->processEvents();
    01267
    01268     text = modaltext;
    01269
    01270     //delete modalWidgetHack;
    01271     //modalWidgetHack = 0;
    01272
    01273     delete spell;
    01274     return modalreturn;
    01275 }
    01276
    01277 void KSpell::slotSpellCheckerCorrected( const QString & oldText, const QString & newText, unsigned int pos )
    01278 {
    01279     modaltext=modaltext.replace(pos,oldText.length(),newText);
    01280 }
    01281
    01282
    01283 void KSpell::slotModalReady()
    01284 {
    01285     //kdDebug() << qApp->loopLevel() << endl;
    01286     //kdDebug(750) << "MODAL READY------------------" << endl;
    01287
    01288     Q_ASSERT( m_status == Running );
    01289     connect( this, SIGNAL( done( const QString & ) ),
    01290              this, SLOT( slotModalDone( const QString & ) ) );
    01291     QObject::connect( this, SIGNAL( corrected( const QString&, const QString&, unsigned int ) ),
    01292                       this, SLOT( slotSpellCheckerCorrected( const QString&, const QString &, unsigned int ) ) );
    01293      QObject::connect( this, SIGNAL( death() ),
    01294                       this, SLOT( slotModalSpellCheckerFinished( ) ) );
    01295     check( modaltext );
    01296 }
    01297
    01298 void KSpell::slotModalDone( const QString &/*_buffer*/ )
    01299 {
    01300     //kdDebug(750) << "MODAL DONE " << _buffer << endl;
    01301     //modaltext = _buffer;
    01302     cleanUp();
    01303
    01304     //kdDebug() << "ABOUT TO EXIT LOOP" << endl;
    01305     //qApp->exit_loop();
    01306
    01307     //modalWidgetHack->close(true);
    01308     slotModalSpellCheckerFinished();
    01309 }
    01310
    01311 void KSpell::slotModalSpellCheckerFinished( )
    01312 {
    01313     modalreturn=(int)this->status();
    01314 }
    01315
    01316 QString KSpell::modaltext;
    01317 int KSpell::modalreturn = 0;
    01318 QWidget* KSpell::modalWidgetHack = 0;
    01319
    01320 #include "kspell.moc"
    01321

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:27:46 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001