kdeui Library API Documentation

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    Copyright (C) 2003 Zack Rusin <zack@kde.org>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License version 2 as published by the Free Software Foundation.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024 
00025 #include <stdio.h>
00026 #include <sys/time.h>
00027 #include <sys/types.h>
00028 #include <unistd.h>
00029 #include <ctype.h>
00030 #include <stdlib.h> // atoi
00031 
00032 #ifdef HAVE_STRINGS_H
00033 #include <strings.h>
00034 #endif
00035 
00036 #include <qtextcodec.h>
00037 #include <qtimer.h>
00038 #include <kapplication.h>
00039 #include <kmessagebox.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 enum checkMethod { Method1 = 0, Method2 };
00057 
00058 struct BufferedWord
00059 {
00060   checkMethod method;
00061   QString word;
00062   bool useDialog;
00063   bool suggest;
00064 };
00065 
00066 class KSpell::KSpellPrivate
00067 {
00068 public:
00069   bool endOfResponse;
00070   bool m_bIgnoreUpperWords;
00071   bool m_bIgnoreTitleCase;
00072   bool m_bNoMisspellingsEncountered;
00073   SpellerType type;
00074   KSpell* suggestSpell;
00075   bool checking;
00076   QValueList<BufferedWord> unchecked;
00077   QTimer *checkNextTimer;
00078 };
00079 
00080 //TODO
00081 //Parse stderr output
00082 //e.g. -- invalid dictionary name
00083 
00084 /*
00085   Things to put in KSpellConfigDlg:
00086     make root/affix combinations that aren't in the dictionary (-m)
00087     don't generate any affix/root combinations (-P)
00088     Report  run-together  words   with   missing blanks as spelling errors.  (-B)
00089     default dictionary (-d [dictionary])
00090     personal dictionary (-p [dictionary])
00091     path to ispell -- NO: ispell should be in $PATH
00092     */
00093 
00094 
00095 //  Connects a slot to KProcIO's output signal
00096 #define OUTPUT(x) (connect (proc, SIGNAL (readReady(KProcIO *)), this, SLOT (x(KProcIO *))))
00097 
00098 // Disconnect a slot from...
00099 #define NOOUTPUT(x) (disconnect (proc, SIGNAL (readReady(KProcIO *)), this, SLOT (x(KProcIO *))))
00100 
00101 
00102 
00103 KSpell::KSpell( QWidget *_parent, const QString &_caption,
00104         QObject *obj, const char *slot, KSpellConfig *_ksc,
00105         bool _progressbar, bool _modal )
00106 {
00107   initialize( _parent, _caption, obj, slot, _ksc,
00108               _progressbar, _modal, Text );
00109 }
00110 
00111 KSpell::KSpell( QWidget *_parent, const QString &_caption,
00112         QObject *obj, const char *slot, KSpellConfig *_ksc,
00113         bool _progressbar, bool _modal, SpellerType type )
00114 {
00115   initialize( _parent, _caption, obj, slot, _ksc,
00116               _progressbar, _modal, type );
00117 }
00118 
00119 void KSpell::hide() { ksdlg->hide(); }
00120 
00121 int KSpell::heightDlg() const { return ksdlg->height(); }
00122 int KSpell::widthDlg() const { return ksdlg->width(); }
00123 
00124 
00125 void
00126 KSpell::startIspell()
00127   //trystart = {0,1,2}
00128 {
00129 
00130   kdDebug(750) << "Try #" << trystart << endl;
00131 
00132   if ( trystart > 0 ) {
00133     proc->resetAll();
00134   }
00135 
00136   switch ( ksconfig->client() )
00137   {
00138   case KS_CLIENT_ISPELL:
00139     *proc << "ispell";
00140     kdDebug(750) << "Using ispell" << endl;
00141     break;
00142   case KS_CLIENT_ASPELL:
00143     *proc << "aspell";
00144     kdDebug(750) << "Using aspell" << endl;
00145     break;
00146   case KS_CLIENT_HSPELL:
00147     *proc << "hspell";
00148     kdDebug(750) << "Using hspell" << endl;
00149     break;
00150   }
00151 
00152   if ( ksconfig->client() == KS_CLIENT_ISPELL || ksconfig->client() == KS_CLIENT_ASPELL )
00153   {
00154     *proc << "-a" << "-S";
00155 
00156     switch ( d->type )
00157     {
00158     case HTML:
00159       //Debian uses an ispell version that has the -h option instead.
00160       //Not sure what they did, but the preferred spell checker
00161       //on that platform is aspell anyway, so use -H untill I'll come
00162       //up with something better.
00163       *proc << "-H";
00164       break;
00165     case TeX:
00166       //same for aspell and ispell
00167       *proc << "-t";
00168       break;
00169     case Nroff:
00170       //only ispell supports
00171       if ( ksconfig->client() == KS_CLIENT_ISPELL )
00172         *proc << "-n";
00173       break;
00174     case Text:
00175     default:
00176       //nothing
00177       break;
00178     }
00179     if (ksconfig->noRootAffix())
00180     {
00181       *proc<<"-m";
00182     }
00183     if (ksconfig->runTogether())
00184     {
00185       *proc << "-B";
00186     }
00187     else
00188     {
00189       *proc << "-C";
00190     }
00191 
00192 
00193     if (trystart<2)
00194     {
00195       if (! ksconfig->dictionary().isEmpty())
00196       {
00197         kdDebug(750) << "using dictionary [" << ksconfig->dictionary() << "]" << endl;
00198         *proc << "-d";
00199         *proc << ksconfig->dictionary();
00200       }
00201     }
00202 
00203   //Note to potential debuggers:  -Tlatin2 _is_ being added on the
00204   //  _first_ try.  But, some versions of ispell will fail with this
00205   // option, so kspell tries again without it.  That's why as 'ps -ax'
00206   // shows "ispell -a -S ..." withou the "-Tlatin2" option.
00207 
00208     if ( trystart<1 ) {
00209       switch ( ksconfig->encoding() )
00210       {
00211       case KS_E_LATIN1:
00212     *proc << "-Tlatin1";
00213     break;
00214       case KS_E_LATIN2:
00215     *proc << "-Tlatin2";
00216     break;
00217       case KS_E_LATIN3:
00218         *proc << "-Tlatin3";
00219         break;
00220 
00221         // add the other charsets here
00222       case KS_E_LATIN4:
00223       case KS_E_LATIN5:
00224       case KS_E_LATIN7:
00225       case KS_E_LATIN8:
00226       case KS_E_LATIN9:
00227       case KS_E_LATIN13:
00228       case KS_E_LATIN15:
00229     // will work, if this is the default charset in the dictionary
00230     kdError(750) << "charsets iso-8859-4 .. iso-8859-15 not supported yet" << endl;
00231     break;
00232       case KS_E_UTF8:
00233         *proc << "-Tutf8";
00234         break;
00235       case KS_E_KOI8U:
00236     *proc << "-w'"; // add ' as a word char
00237     break;
00238       }
00239     }
00240 
00241   // -a : pipe mode
00242   // -S : sort suggestions by probable correctness
00243   }
00244   else       // hspell doesn't need all the rest of the options
00245     *proc << "-a";
00246 
00247   if (trystart==0) //don't connect these multiple times
00248   {
00249     connect( proc, SIGNAL(receivedStderr(KProcess *, char *, int)),
00250              this, SLOT(ispellErrors(KProcess *, char *, int)) );
00251 
00252     connect( proc, SIGNAL(processExited(KProcess *)),
00253              this, SLOT(ispellExit (KProcess *)) );
00254 
00255     OUTPUT(KSpell2);
00256   }
00257 
00258   if ( proc->start() == false )
00259   {
00260     m_status = Error;
00261     QTimer::singleShot( 0, this, SLOT(emitDeath()));
00262   }
00263 }
00264 
00265 void
00266 KSpell::ispellErrors( KProcess *, char *buffer, int buflen )
00267 {
00268   buffer[buflen-1] = '\0';
00269   //  kdDebug(750) << "ispellErrors [" << buffer << "]\n" << endl;
00270 }
00271 
00272 void KSpell::KSpell2( KProcIO * )
00273 
00274 {
00275   QString line;
00276 
00277   kdDebug(750) << "KSpell::KSpell2" << endl;
00278 
00279   trystart = maxtrystart;  //We've officially started ispell and don't want
00280                            //to try again if it dies.
00281 
00282   if ( proc->readln( line, true ) == -1 )
00283   {
00284      QTimer::singleShot( 0, this, SLOT(emitDeath()) );
00285      return;
00286   }
00287 
00288 
00289   if ( line[0] != '@' ) //@ indicates that ispell is working fine
00290   {
00291      QTimer::singleShot( 0, this, SLOT(emitDeath()) );
00292      return;
00293   }
00294 
00295   //We want to recognize KDE in any text!
00296   if ( ignore("kde") == false)
00297   {
00298      kdDebug(750) << "@KDE was false" << endl;
00299      QTimer::singleShot( 0, this, SLOT(emitDeath()) );
00300      return;
00301   }
00302 
00303   //We want to recognize linux in any text!
00304   if ( ignore("linux") == false )
00305   {
00306      kdDebug(750) << "@Linux was false" << endl;
00307      QTimer::singleShot( 0, this, SLOT(emitDeath()) );
00308      return;
00309   }
00310 
00311   NOOUTPUT( KSpell2 );
00312 
00313   m_status = Running;
00314   emit ready( this );
00315 }
00316 
00317 void
00318 KSpell::setUpDialog( bool reallyuseprogressbar )
00319 {
00320   if ( dialogsetup )
00321     return;
00322 
00323   //Set up the dialog box
00324   ksdlg = new KSpellDlg( parent, "dialog",
00325                          progressbar && reallyuseprogressbar, modaldlg );
00326   ksdlg->setCaption( caption );
00327 
00328   connect( ksdlg, SIGNAL(command(int)),
00329            this, SLOT(slotStopCancel(int)) );
00330   connect( this, SIGNAL(progress(unsigned int)),
00331        ksdlg, SLOT(slotProgress(unsigned int)) );
00332 
00333 #ifdef Q_WS_X11 // FIXME(E): Implement for Qt/Embedded
00334   KWin::setIcons( ksdlg->winId(), kapp->icon(), kapp->miniIcon() );
00335 #endif
00336   if ( modaldlg )
00337     ksdlg->setFocus();
00338   dialogsetup = true;
00339 }
00340 
00341 bool KSpell::addPersonal( const QString & word )
00342 {
00343   QString qs = word.simplifyWhiteSpace();
00344 
00345   //we'll let ispell do the work here b/c we can
00346   if ( qs.find(' ') != -1 || qs.isEmpty() )    // make sure it's a _word_
00347     return false;
00348 
00349   qs.prepend( "*" );
00350   personaldict = true;
00351 
00352   return proc->writeStdin( qs );
00353 }
00354 
00355 bool KSpell::writePersonalDictionary()
00356 {
00357   return proc->writeStdin("#");
00358 }
00359 
00360 bool KSpell::ignore( const QString & word )
00361 {
00362   QString qs = word.simplifyWhiteSpace();
00363 
00364   //we'll let ispell do the work here b/c we can
00365   if ( qs.find (' ') != -1 || qs.isEmpty() )    // make sure it's a _word_
00366     return false;
00367 
00368   qs.prepend( "@" );

  return proc->writeStdin( qs );
}

bool
KSpell::cleanFputsWord( const QString & s, bool appendCR )
{
  QString qs(s);
  bool empty = true;

  for( unsigned int i = 0; i < qs.length(); i++ )
  {
    //we need some punctuation for ornaments
    if ( qs[i] != '\'' && qs[i] != '\"' && qs[i] != '-'
00369          && qs[i].isPunct() || qs[i].isSpace() )
00370     {
00371       qs.remove(i,1);
00372       i--;
00373     } else {
00374       if ( qs[i].isLetter() )
00375         empty=false;
00376     }
00377   }
00378 
00379   // don't check empty words, otherwise synchronization will lost
00380   if (empty)
00381     return false;
00382 
00383   return proc->writeStdin( "^"+qs, appendCR );
00384 }
00385 
00386 bool
00387 KSpell::cleanFputs( const QString & s, bool appendCR )
00388 {
00389   QString qs(s);
00390   unsigned l = qs.length();
00391 
00392   // some uses of '$' (e.g. "$0") cause ispell to skip all following text
00393   for( unsigned int i = 0; i < l; ++i )
00394   {
00395     if( qs[i] == '$' )
00396       qs[i] = ' ';
00397   }
00398 
00399   if ( l<MAXLINELENGTH )
00400   {
00401     if ( qs.isEmpty() )
00402       qs="";
00403     return proc->writeStdin( "^"+qs, appendCR );
00404   }
00405   else
00406     return proc->writeStdin( QString::fromAscii( "^\n" ),appendCR );
00407 }
00408 
00409 bool KSpell::checkWord( const QString & buffer, bool _usedialog )
00410 {
00411   if (d->checking) { // don't check multiple words simultaneously
00412     BufferedWord bufferedWord;
00413     bufferedWord.method = Method1;
00414     bufferedWord.word = buffer;
00415     bufferedWord.useDialog = _usedialog;
00416     d->unchecked.append( bufferedWord );
00417     return true;
00418   }
00419   d->checking = true;
00420   QString qs = buffer.simplifyWhiteSpace();
00421 
00422   if ( qs.find (' ') != -1 || qs.isEmpty() ) {   // make sure it's a _word_
00423     d->checkNextTimer->start( 0, true );
00424     return false;
00425   }
00427   dialog3slot = SLOT(checkWord3());
00428 
00429   usedialog = _usedialog;
00430   setUpDialog( false );
00431   if ( _usedialog )
00432   {
00433     emitProgress();
00434   }
00435   else
00436     ksdlg->hide();
00437 
00438   QString blank_line;
00439   while (proc->readln( blank_line, true ) != -1); // eat spurious blanks
00440 
00441   OUTPUT(checkWord2);
00442   //  connect (this, SIGNAL (dialog3()), this, SLOT (checkWord3()));
00443 
00444   proc->writeStdin( "%" ); // turn off terse mode
00445   proc->writeStdin( buffer ); // send the word to ispell
00446 
00447   return true;
00448 }
00449 
00450 bool KSpell::checkWord( const QString & buffer, bool _usedialog, bool suggest )
00451 {
00452   if (d->checking) { // don't check multiple words simultaneously
00453     BufferedWord bufferedWord;
00454     bufferedWord.method = Method2;
00455     bufferedWord.word = buffer;
00456     bufferedWord.useDialog = _usedialog;
00457     bufferedWord.suggest = suggest;
00458     d->unchecked.append( bufferedWord );
00459     return true;
00460   }
00461   d->checking = true;
00462   QString qs = buffer.simplifyWhiteSpace();
00463 
00464   if ( qs.find (' ') != -1 || qs.isEmpty() ) {   // make sure it's a _word_
00465     d->checkNextTimer->start( 0, true );
00466     return false;
00467   }
00468 
00470   if ( !suggest ) {
00471     dialog3slot = SLOT(checkWord3());
00472     usedialog = _usedialog;
00473     setUpDialog( false );
00474     if ( _usedialog )
00475     {
00476       emitProgress();
00477     }
00478     else
00479       ksdlg->hide();
00480   }
00481   
00482   QString blank_line;
00483   while (proc->readln( blank_line, true ) != -1); // eat spurious blanks
00484 
00485   OUTPUT(checkWord2);
00486   //  connect (this, SIGNAL (dialog3()), this, SLOT (checkWord3()));
00487 
00488   proc->writeStdin( "%" ); // turn off terse mode
00489   proc->writeStdin( buffer ); // send the word to ispell
00490 
00491   return true;
00492 }
00493 
00494 void KSpell::checkWord2( KProcIO* )
00495 {
00496   QString word;
00497   QString line;
00498   proc->readln( line, true ); //get ispell's response
00499 
00500 /* ispell man page: "Each sentence of text input is terminated with an
00501    additional blank line,  indicating that ispell has completed processing
00502    the input line."
00503    <sanders>
00504    But there can be multiple lines returned in the case of an error,
00505    in this case we should consume all the output given otherwise spell checking
00506    can get out of sync.
00507    </sanders>
00508 */
00509   QString blank_line;
00510   while (proc->readln( blank_line, true ) != -1); // eat the blank line
00511   NOOUTPUT(checkWord2);
00512   
00513   bool mistake = ( parseOneResponse(line, word, sugg) == MISTAKE );
00514   if ( mistake && usedialog )
00515   {
00516     cwword = word;
00517     dialog( word, sugg, SLOT(checkWord3()) );
00518     d->checkNextTimer->start( 0, true );
00519     return;
00520   }
00521   else if( mistake )
00522   {
00523     emit misspelling( word, sugg, lastpos );
00524   }
00525 
00526   //emits a "corrected" signal _even_ if no change was made
00527   //so that the calling program knows when the check is complete
00528   emit corrected( word, word, 0L );
00529   d->checkNextTimer->start( 0, true );
00530 }
00531 
00532 void KSpell::checkNext()
00533 {
00534 // Queue words to prevent kspell from turning into a fork bomb
00535   d->checking = false;
00536   if (!d->unchecked.empty()) {
00537     BufferedWord buf = d->unchecked.front();
00538     d->unchecked.pop_front();
00539     
00540     if (buf.method == Method1)
00541       checkWord( buf.word, buf.useDialog );
00542     else
00543       checkWord( buf.word, buf.useDialog, buf.suggest );
00544   }
00545 }
00546 
00547 void KSpell::suggestWord( KProcIO * )
00548 {
00549   QString word;
00550   QString line;
00551   proc->readln( line, true ); //get ispell's response
00552 
00553 /* ispell man page: "Each sentence of text input is terminated with an
00554    additional blank line,  indicating that ispell has completed processing
00555    the input line." */
00556   QString blank_line;
00557   proc->readln( blank_line, true ); // eat the blank line
00558 
00559   NOOUTPUT(checkWord2);
00560 
00561   bool mistake = ( parseOneResponse(line, word, sugg) == MISTAKE );
00562   if ( mistake && usedialog )
00563   {
00564     cwword=word;
00565     dialog( word, sugg, SLOT(checkWord3()) );
00566     return;
00567   }
00568 }
00569 
00570 void KSpell::checkWord3()
00571 {
00572   disconnect( this, SIGNAL(dialog3()), this, SLOT(checkWord3()) );
00573 
00574   emit corrected( cwword, replacement(), 0L );
00575 }
00576 
00577 QString KSpell::funnyWord( const QString & word )
00578   // composes a guess from ispell to a readable word
00579   // e.g. "re+fry-y+ies" -> "refries"
00580 {
00581   QString qs;
00582   unsigned int i=0;
00583 
00584   for( i=0; word [i]!='\0';i++ )
00585   {
00586     if (word [i]=='+')
00587       continue;
00588     if (word [i]=='-')
00589     {
00590       QString shorty;
00591       unsigned int j;
00592       int k;
00593 
00594       for( j = i+1; word[j] != '\0' && word[j] != '+' && word[j] != '-'; j++ )
00595         shorty += word[j];
00596 
00597       i = j-1;
00598 
00599       if ( ( k = qs.findRev(shorty) ) == 0 || k != -1 )
00600         qs.remove( k, shorty.length() );
00601       else
00602       {
00603         qs += '-';
00604         qs += shorty;  //it was a hyphen, not a '-' from ispell
00605       }
00606     }
00607     else
00608       qs += word[i];
00609   }
00610 
00611   return qs;
00612 }
00613 
00614 
00615 int KSpell::parseOneResponse( const QString &buffer, QString &word, QStringList & sugg )
00616   // buffer is checked, word and sugg are filled in
00617   // returns
00618   //   GOOD    if word is fine
00619   //   IGNORE  if word is in ignorelist
00620   //   REPLACE if word is in replacelist
00621   //   MISTAKE if word is misspelled
00622 {
00623   word = "";
00624   posinline=0;
00625 
00626   sugg.clear();
00627 
00628   if ( buffer[0] == '*' || buffer[0] == '+' || buffer[0] == '-' )
00629   {
00630     return GOOD;
00631   }
00632 
00633   if ( buffer[0] == '&' || buffer[0] == '?' || buffer[0] == '#' )
00634   {
00635     int i,j;
00636 
00637 
00638     word = buffer.mid( 2, buffer.find( ' ', 3 ) -2 );
00639     //check() needs this
00640     orig=word;
00641 
00642     if( d->m_bIgnoreTitleCase && word == word.upper() )
00643       return IGNORE;
00644 
00645     if( d->m_bIgnoreUpperWords && word[0] == word[0].upper() )
00646     {
00647       QString text = word[0] + word.right( word.length()-1 ).lower();
00648       if( text == word )
00649         return IGNORE;
00650     }
00651 
00653     //We don't take advantage of ispell's ignore function because
00654     //we can't interrupt ispell's output (when checking a large
00655     //buffer) to add a word to _it's_ ignore-list.
00656     if ( ignorelist.findIndex( word.lower() ) != -1 )
00657       return IGNORE;
00658 
00660     QString qs2;
00661 
00662     if ( buffer.find( ':' ) != -1 )
00663       qs2 = buffer.left( buffer.find(':') );
00664     else
00665       qs2 = buffer;
00666 
00667     posinline = qs2.right( qs2.length()-qs2.findRev(' ') ).toInt()-1;
00668 
00670     QStringList::Iterator it = replacelist.begin();
00671     for( ;it != replacelist.end(); ++it, ++it ) // Skip two entries at a time.
00672     {
00673       if ( word == *it ) // Word matches
00674       {
00675         ++it;
00676         word = *it;   // Replace it with the next entry
00677         return REPLACE;
00678       }
00679     }
00680 
00682     if ( buffer[0] != '#' )
00683     {
00684       QString qs = buffer.mid( buffer.find(':')+2, buffer.length() );
00685       qs += ',';
00686       sugg.clear();
00687       i = j = 0;
00688 
00689       while( (unsigned int)i < qs.length() )
00690       {
00691         QString temp = qs.mid( i, (j=qs.find (',',i)) - i );
00692         sugg.append( funnyWord(temp) );
00693 
00694         i=j+2;
00695       }
00696     }
00697 
00698     if ( (sugg.count()==1) && (sugg.first() == word) )
00699       return GOOD;
00700 
00701     return MISTAKE;
00702   }
00703 
00704   if ( buffer.isEmpty() ) {
00705       kdDebug(750) << "Got an empty response: ignoring"<<endl;
00706       return GOOD;
00707   }
00708 
00709   kdError(750) << "HERE?: [" << buffer << "]" << endl;
00710   kdError(750) << "Please report this to zack@kde.org" << endl;
00711   kdError(750) << "Thank you!" << endl;
00712 
00713   emit done( false );
00714   emit done( KSpell::origbuffer );
00715   return MISTAKE;
00716 }
00717 
00718 bool KSpell::checkList (QStringList *_wordlist, bool _usedialog)
00719   // prepare check of string list
00720 {
00721   wordlist=_wordlist;
00722   if ((totalpos=wordlist->count())==0)
00723     return false;
00724   wlIt = wordlist->begin();
00725   usedialog=_usedialog;
00726 
00727   // prepare the dialog
00728   setUpDialog();
00729 
00730   //set the dialog signal handler
00731   dialog3slot = SLOT (checkList4 ());
00732 
00733   proc->writeStdin ("%"); // turn off terse mode & check one word at a time
00734 
00735   //lastpos now counts which *word number* we are at in checkListReplaceCurrent()
00736   lastpos = -1;
00737   checkList2();
00738 
00739   // when checked, KProcIO calls checkList3a
00740   OUTPUT(checkList3a);
00741 
00742   return true;
00743 }
00744 
00745 void KSpell::checkList2 ()
00746   // send one word from the list to KProcIO
00747   // invoked first time by checkList, later by checkListReplaceCurrent and checkList4
00748 {
00749   // send next word
00750   if (wlIt != wordlist->end())
00751   {
00752     kdDebug(750) << "KS::cklist2 " << lastpos << ": " << *wlIt << endl;
00753 
00754     d->endOfResponse = false;
00755     bool put;
00756     lastpos++; offset=0;
00757     put = cleanFputsWord (*wlIt);
00758     ++wlIt;
00759 
00760     // when cleanFPutsWord failed (e.g. on empty word)
00761     // try next word; may be this is not good for other
00762     // problems, because this will make read the list up to the end
00763     if (!put) {
00764       checkList2();
00765     }
00766   }
00767   else
00768     // end of word list
00769   {
00770     NOOUTPUT(checkList3a);
00771     ksdlg->hide();
00772     emit done(true);
00773   }
00774 }
00775 
00776 void KSpell::checkList3a (KProcIO *)
00777   // invoked by KProcIO, when data from ispell are read
00778 {
00779   //kdDebug(750) << "start of checkList3a" << endl;
00780 
00781   // don't read more data, when dialog is waiting
00782   // for user interaction
00783   if ( dlgon ) {
00784     //kdDebug(750) << "dlgon: don't read more data" << endl;
00785     return;
00786   }
00787 
00788   int e, tempe;
00789 
00790   QString word;
00791   QString line;
00792 
00793   do
00794   {
00795     tempe=proc->readln( line, true ); //get ispell's response
00796 
00797     //kdDebug(750) << "checkList3a: read bytes [" << tempe << "]" << endl;
00798 
00799 
00800     if ( tempe == 0 ) {
00801       d->endOfResponse = true;
00802       //kdDebug(750) << "checkList3a: end of resp" << endl;
00803     } else if ( tempe>0 ) {
00804       if ( (e=parseOneResponse( line, word, sugg ) ) == MISTAKE ||
00805            e==REPLACE )
00806       {
00807         dlgresult=-1;
00808 
00809         if ( e == REPLACE )
00810         {
00811           QString old = *(--wlIt); ++wlIt;
00812           dlgreplacement = word;
00813           checkListReplaceCurrent();
00814           // inform application
00815           emit corrected( old, *(--wlIt), lastpos ); ++wlIt;
00816         }
00817         else if( usedialog )
00818         {
00819           cwword = word;
00820           dlgon = true;
00821           // show the dialog
00822           dialog( word, sugg, SLOT(checkList4()) );
00823           return;
00824         }
00825         else
00826         {
00827           d->m_bNoMisspellingsEncountered = false;
00828           emit misspelling( word, sugg, lastpos );
00829         }
00830       }
00831 
00832     }
00833     emitProgress (); //maybe
00834 
00835     // stop when empty line or no more data
00836   } while (tempe > 0);
00837 
00838   //kdDebug(750) << "checkList3a: exit loop with [" << tempe << "]" << endl;
00839 
00840   // if we got an empty line, t.e. end of ispell/aspell response
00841   // and the dialog isn't waiting for user interaction, send next word
00842   if (d->endOfResponse && !dlgon) {
00843     //kdDebug(750) << "checkList3a: send next word" << endl;
00844     checkList2();
00845   }
00846 }
00847 
00848 void KSpell::checkListReplaceCurrent()
00849 {
00850 
00851   // go back to misspelled word
00852   wlIt--;
00853 
00854   QString s = *wlIt;
00855   s.replace(posinline+offset,orig.length(),replacement());
00856   offset += replacement().length()-orig.length();
00857   wordlist->insert (wlIt, s);
00858   wlIt = wordlist->remove (wlIt);
00859   // wlIt now points to the word after the repalced one
00860 
00861 }
00862 
00863 void KSpell::checkList4 ()
00864   // evaluate dialog return, when a button was pressed there
00865 {
00866   dlgon=false;
00867   QString old;
00868 
00869   disconnect (this, SIGNAL (dialog3()), this, SLOT (checkList4()));
00870 
00871   //others should have been processed by dialog() already
00872   switch (dlgresult)
00873   {
00874   case KS_REPLACE:
00875   case KS_REPLACEALL:
00876     kdDebug(750) << "KS: cklist4: lastpos: " << lastpos << endl;
00877     old = *(--wlIt);
00878     ++wlIt;
00879     // replace word
00880     checkListReplaceCurrent();
00881     emit corrected( old, *(--wlIt), lastpos );
00882     ++wlIt;
00883     break;
00884   case KS_CANCEL:
00885     ksdlg->hide();
00886     emit done( false );
00887     return;
00888   case KS_STOP:
00889     ksdlg->hide();
00890     emit done( true );
00891     return;
00892   case KS_CONFIG:
00893     ksdlg->hide();
00894     emit done( false );
00895     //check( origbuffer.mid( lastpos ), true );
00896     //trystart = 0;
00897     //proc->disconnect();
00898     //proc->kill();
00899     //delete proc;
00900     //proc = new KProcIO( codec );
00901     //startIspell();
00902     return;
00903   };
00904 
00905   // read more if there is more, otherwise send next word
00906   if (!d->endOfResponse) {
00907     //kdDebug(750) << "checkList4: read more from response" << endl;
00908     checkList3a(NULL);
00909   }
00910 }
00911 
00912 bool KSpell::check( const QString &_buffer, bool _usedialog )
00913 {
00914   QString qs;
00915 
00916   usedialog = _usedialog;
00917   setUpDialog();
00918   //set the dialog signal handler
00919   dialog3slot = SLOT(check3());
00920 
00921   kdDebug(750) << "KS: check" << endl;
00922   origbuffer = _buffer;
00923   if ( ( totalpos = origbuffer.length() ) == 0 )
00924   {
00925     emit done( origbuffer );
00926     return false;
00927   }
00928 
00929 
00930   // Torben: I corrected the \n\n problem directly in the
00931   //         origbuffer since I got errors otherwise
00932   if ( !origbuffer.endsWith("\n\n" ) )
00933   {
00934     if (origbuffer.at(origbuffer.length()-1)!='\n')
00935     {
00936       origbuffer+='\n';
00937       origbuffer+='\n'; //shouldn't these be removed at some point?
00938     }
00939     else
00940       origbuffer+='\n';
00941   }
00942 
00943   newbuffer = origbuffer;
00944 
00945   // KProcIO calls check2 when read from ispell
00946   OUTPUT( check2 );
00947   proc->writeStdin( "!" );
00948 
00949   //lastpos is a position in newbuffer (it has offset in it)
00950   offset = lastlastline = lastpos = lastline = 0;
00951 
00952   emitProgress();
00953 
00954   // send first buffer line
00955   int i = origbuffer.find( '\n', 0 ) + 1;
00956   qs = origbuffer.mid( 0, i );
00957   cleanFputs( qs, false );
00958 
00959   lastline=i; //the character position, not a line number
00960 
00961   if ( usedialog )
00962   {
00963     emitProgress();
00964   }
00965   else
00966     ksdlg->hide();
00967 
00968   return true;
00969 }
00970 
00971 
00972 void KSpell::check2( KProcIO * )
00973   // invoked by KProcIO when read from ispell
00974 {
00975   int e, tempe;
00976   QString word;
00977   QString line;
00978   static bool recursive = false;
00979   if (recursive &&
00980       !ksdlg )
00981   {
00982       return;
00983   }
00984   recursive = true;
00985 
00986   do
00987   {
00988     tempe = proc->readln( line, false ); //get ispell's response
00989     //kdDebug(750) << "KSpell::check2 (" << tempe << "b)" << endl;
00990 
00991     if ( tempe>0 )
00992     {
00993       if ( ( e=parseOneResponse (line, word, sugg) )==MISTAKE ||
00994            e==REPLACE)
00995       {
00996         dlgresult=-1;
00997 
00998         // for multibyte encoding posinline needs correction
00999         if (ksconfig->encoding() == KS_E_UTF8) {
01000           // kdDebug(750) << "line: " << origbuffer.mid(lastlastline,
01001           // lastline-lastlastline) << endl;
01002           // kdDebug(750) << "posinline uncorr: " << posinline << endl;
01003 
01004           // convert line to UTF-8, cut at pos, convert back to UCS-2
01005           // and get string length
01006           posinline = (QString::fromUtf8(
01007                          origbuffer.mid(lastlastline,lastline-lastlastline).utf8(),
01008                          posinline)).length();
01009           // kdDebug(750) << "posinline corr: " << posinline << endl;
01010         }
01011 
01012         lastpos = posinline+lastlastline+offset;
01013 
01014         //orig is set by parseOneResponse()
01015 
01016         if (e==REPLACE)
01017         {
01018           dlgreplacement=word;
01019           emit corrected( orig, replacement(), lastpos );
01020           offset += replacement().length()-orig.length();
01021           newbuffer.replace( lastpos, orig.length(), word );
01022         }
01023         else  //MISTAKE
01024         {
01025           cwword = word;
01026           //kdDebug(750) << "(Before dialog) word=[" << word << "] cwword =[" << cwword << "]\n" << endl;
01027           if ( usedialog ) {
01028             // show the word in the dialog
01029             dialog( word, sugg, SLOT(check3()) );
01030           } else {
01031             // No dialog, just emit misspelling and continue
01032             d->m_bNoMisspellingsEncountered = false;
01033             emit misspelling( word, sugg, lastpos );
01034             dlgresult = KS_IGNORE;
01035             check3();
01036           }
01037           recursive = false;
01038           return;
01039         }
01040       }
01041 
01042     }
01043 
01044     emitProgress(); //maybe
01045 
01046   } while( tempe>0 );
01047 
01048   proc->ackRead();
01049 
01050 
01051   if ( tempe == -1 ) { //we were called, but no data seems to be ready...
01052     recursive = false;
01053     return;
01054   }
01055 
01056   //If there is more to check, then send another line to ISpell.
01057   if ( (unsigned int)lastline < origbuffer.length() )
01058   {
01059     int i;
01060     QString qs;
01061 
01062     //kdDebug(750) << "[EOL](" << tempe << ")[" << temp << "]" << endl;
01063 
01064     lastpos = (lastlastline=lastline) + offset; //do we really want this?
01065     i = origbuffer.find('\n', lastline) + 1;
01066     qs = origbuffer.mid( lastline, i-lastline );
01067     cleanFputs( qs, false );
01068     lastline = i;
01069     recursive = false;
01070     return;
01071   }
01072   else
01073     //This is the end of it all
01074   {
01075     ksdlg->hide();
01076     //      kdDebug(750) << "check2() done" << endl;
01077     newbuffer.truncate( newbuffer.length()-2 );
01078     emitProgress();
01079     emit done( newbuffer );
01080   }
01081   recursive = false;
01082 }
01083 
01084 void KSpell::check3 ()
01085   // evaluates the return value of the dialog
01086 {
01087   disconnect (this, SIGNAL (dialog3()), this, SLOT (check3()));
01088   kdDebug(750) << "check3 [" << cwword << "] [" << replacement() << "] " << dlgresult << endl;
01089 
01090   //others should have been processed by dialog() already
01091   switch (dlgresult)
01092   {
01093   case KS_REPLACE:
01094   case KS_REPLACEALL:
01095     offset+=replacement().length()-cwword.length();
01096     newbuffer.replace (lastpos, cwword.length(),
01097                        replacement());
01098     emit corrected (dlgorigword, replacement(), lastpos);
01099     break;
01100   case KS_CANCEL:
01101     //      kdDebug(750) << "canceled\n" << endl;
01102     ksdlg->hide();
01103     emit done( origbuffer );
01104     return;
01105   case KS_CONFIG:
01106     ksdlg->hide();
01107     emit done( origbuffer );
01108     KMessageBox::information( 0, i18n("You have to restart the dialog for changes to take effect") );
01109     //check( origbuffer.mid( lastpos ), true );
01110     return;
01111   case KS_STOP:
01112     ksdlg->hide();
01113     //buffer=newbuffer);
01114     emitProgress();
01115     emit done (newbuffer);
01116     return;
01117   };
01118 
01119   proc->ackRead();
01120 }
01121 
01122 void
01123 KSpell::slotStopCancel (int result)
01124 {
01125   if (dialogwillprocess)
01126     return;
01127 
01128   kdDebug(750) << "KSpell::slotStopCancel [" << result << "]" << endl;
01129 
01130   if (result==KS_STOP || result==KS_CANCEL)
01131     if (!dialog3slot.isEmpty())
01132     {
01133       dlgresult=result;
01134       connect (this, SIGNAL (dialog3()), this, dialog3slot.ascii());
01135       emit dialog3();
01136     }
01137 }
01138 
01139 
01140 void KSpell::dialog( const QString & word, QStringList & sugg, const char *_slot )
01141 {
01142   dlgorigword = word;
01143 
01144   dialog3slot = _slot;
01145   dialogwillprocess = true;
01146   connect( ksdlg, SIGNAL(command(int)), this, SLOT(dialog2(int)) );
01147   QString tmpBuf = newbuffer;
01148   kdDebug(750)<<" position = "<<lastpos<<endl;
01149 
01150   // extract a context string, replace all characters which might confuse
01151   // the RichText display and highlight the possibly wrong word
01152   QString marker( "_MARKER_" );
01153   tmpBuf.replace( lastpos, word.length(), marker );
01154   QString context = tmpBuf.mid(QMAX(lastpos-18,0), 2*18+marker.length());
01155   context.replace( '\n',QString::fromLatin1(" "));
01156   context.replace( '<', QString::fromLatin1("&lt;") );
01157   context.replace( '>', QString::fromLatin1("&gt;") );
01158   context.replace( marker, QString::fromLatin1("<b>%1</b>").arg( word ) );
01159   context = "<qt>" + context + "</qt>";
01160 
01161   ksdlg->init( word, &sugg, context );
01162   d->m_bNoMisspellingsEncountered = false;
01163   emit misspelling( word, sugg, lastpos );
01164 
01165   emitProgress();
01166   ksdlg->show();
01167 }
01168 
01169 void KSpell::dialog2( int result )
01170 {
01171   QString qs;
01172 
01173   disconnect( ksdlg, SIGNAL(command(int)), this, SLOT(dialog2(int)) );
01174   dialogwillprocess = false;
01175   dlgresult = result;
01176   ksdlg->standby();
01177 
01178   dlgreplacement = ksdlg->replacement();
01179 
01180   //process result here
01181   switch ( dlgresult )
01182   {
01183   case KS_IGNORE:
01184     emit ignoreword( dlgorigword );
01185     break;
01186   case KS_IGNOREALL:
01187     // would be better to lower case only words with beginning cap
01188     ignorelist.prepend( dlgorigword.lower() );
01189     emit ignoreall( dlgorigword );
01190     break;
01191   case KS_ADD:
01192     addPersonal( dlgorigword );
01193     personaldict = true;
01194     emit addword( dlgorigword );
01195     // adding to pesonal dict takes effect at the next line, not the current
01196     ignorelist.prepend( dlgorigword.lower() );
01197     break;
01198   case KS_REPLACEALL:
01199   {
01200     replacelist.append( dlgorigword );
01201     QString _replacement = replacement();
01202     replacelist.append( _replacement );
01203     emit replaceall( dlgorigword ,  _replacement );
01204   }
01205     break;
01206   case KS_SUGGEST:
01207     checkWord( ksdlg->replacement(), false, true );
01208     return;
01209     break;
01210   }
01211 
01212   connect( this, SIGNAL(dialog3()), this, dialog3slot.ascii() );
01213   emit dialog3();
01214 }
01215 
01216 
01217 KSpell::~KSpell()
01218 {
01219   delete proc;
01220   delete ksconfig;
01221   delete ksdlg;
01222   delete d->checkNextTimer;
01223   delete d;
01224 }
01225 
01226 
01227 KSpellConfig KSpell::ksConfig() const
01228 {
01229   ksconfig->setIgnoreList(ignorelist);
01230   ksconfig->setReplaceAllList(replacelist);
01231   return *ksconfig;
01232 }
01233 
01234 void KSpell::cleanUp()
01235 {
01236   if ( m_status == Cleaning )
01237     return; // Ignore
01238 
01239   if ( m_status == Running )
01240   {
01241     if ( personaldict )
01242       writePersonalDictionary();
01243     m_status = Cleaning;
01244   }
01245   proc->closeStdin();
01246 }
01247 
01248 void KSpell::ispellExit( KProcess* )
01249 {
01250   kdDebug() << "KSpell::ispellExit() " << m_status << endl;
01251 
01252   if ( (m_status == Starting) && (trystart < maxtrystart) )
01253   {
01254     trystart++;
01255     startIspell();
01256     return;
01257   }
01258 
01259   if ( m_status == Starting )
01260      m_status = Error;
01261   else if (m_status == Cleaning)
01262      m_status = d->m_bNoMisspellingsEncountered ? FinishedNoMisspellingsEncountered : Finished;
01263   else if ( m_status == Running )
01264      m_status = Crashed;
01265   else // Error, Finished, Crashed
01266      return; // Dead already
01267 
01268   kdDebug(750) << "Death" << endl;
01269   QTimer::singleShot( 0, this, SLOT(emitDeath()) );
01270 }
01271 
01272 // This is always called from the event loop to make
01273 // sure that the receiver can safely delete the
01274 // KSpell object.
01275 void KSpell::emitDeath()
01276 {
01277   bool deleteMe = autoDelete; // Can't access object after next call!
01278   emit death();
01279   if ( deleteMe )
01280     deleteLater();
01281 }
01282 
01283 void KSpell::setProgressResolution (unsigned int res)
01284 {
01285   progres=res;
01286 }
01287 
01288 void KSpell::emitProgress ()
01289 {
01290   uint nextprog = (uint) (100.*lastpos/(double)totalpos);
01291 
01292   if ( nextprog >= curprog )
01293   {
01294     curprog = nextprog;
01295     emit progress( curprog );
01296   }
01297 }
01298 
01299 void KSpell::moveDlg( int x, int y )
01300 {
01301   QPoint pt( x,y ), pt2;
01302   pt2 = parent->mapToGlobal( pt );
01303   ksdlg->move( pt2.x(),pt2.y() );
01304 }
01305 
01306 void KSpell::setIgnoreUpperWords(bool _ignore)
01307 {
01308   d->m_bIgnoreUpperWords=_ignore;
01309 }
01310 
01311 void KSpell::setIgnoreTitleCase(bool _ignore)
01312 {
01313   d->m_bIgnoreTitleCase=_ignore;
01314 }
01315 // --------------------------------------------------
01316 // Stuff for modal (blocking) spell checking
01317 //
01318 // Written by Torben Weis <weis@kde.org>. So please
01319 // send bug reports regarding the modal stuff to me.
01320 // --------------------------------------------------
01321 
01322 int
01323 KSpell::modalCheck( QString& text )
01324 {
01325   return modalCheck( text,0 );
01326 }
01327 
01328 int
01329 KSpell::modalCheck( QString& text, KSpellConfig* _kcs )
01330 {
01331   modalreturn = 0;
01332   modaltext = text;
01333 
01334   KSpell* spell = new KSpell( 0L, i18n("Spell Checker"), 0 ,
01335                               0, _kcs, true, true );
01336 
01337   while (spell->status()!=Finished)
01338     kapp->processEvents();
01339 
01340   text = modaltext;
01341 
01342   delete spell;
01343   return modalreturn;
01344 }
01345 
01346 void KSpell::slotSpellCheckerCorrected( const QString & oldText, const QString & newText, unsigned int pos )
01347 {
01348   modaltext=modaltext.replace(pos,oldText.length(),newText);
01349 }
01350 
01351 
01352 void KSpell::slotModalReady()
01353 {
01354   //kdDebug() << qApp->loopLevel() << endl;
01355   //kdDebug(750) << "MODAL READY------------------" << endl;
01356 
01357   Q_ASSERT( m_status == Running );
01358   connect( this, SIGNAL( done( const QString & ) ),
01359            this, SLOT( slotModalDone( const QString & ) ) );
01360   QObject::connect( this, SIGNAL( corrected( const QString&, const QString&, unsigned int ) ),
01361                     this, SLOT( slotSpellCheckerCorrected( const QString&, const QString &, unsigned int ) ) );
01362   QObject::connect( this, SIGNAL( death() ),
01363                     this, SLOT( slotModalSpellCheckerFinished( ) ) );
01364   check( modaltext );
01365 }
01366 
01367 void KSpell::slotModalDone( const QString &/*_buffer*/ )
01368 {
01369   //kdDebug(750) << "MODAL DONE " << _buffer << endl;
01370   //modaltext = _buffer;
01371   cleanUp();
01372 
01373   //kdDebug() << "ABOUT TO EXIT LOOP" << endl;
01374   //qApp->exit_loop();
01375 
01376   //modalWidgetHack->close(true);
01377   slotModalSpellCheckerFinished();
01378 }
01379 
01380 void KSpell::slotModalSpellCheckerFinished( )
01381 {
01382   modalreturn=(int)this->status();
01383 }
01384 
01385 void KSpell::initialize( QWidget *_parent, const QString &_caption,
01386                          QObject *obj, const char *slot, KSpellConfig *_ksc,
01387                          bool _progressbar, bool _modal, SpellerType type )
01388 {
01389   d = new KSpellPrivate;
01390 
01391   d->m_bIgnoreUpperWords =false;
01392   d->m_bIgnoreTitleCase =false;
01393   d->m_bNoMisspellingsEncountered = true;
01394   d->type = type;
01395   d->checking = false;
01396   d->checkNextTimer = new QTimer( this );
01397   connect( d->checkNextTimer, SIGNAL( timeout() ),
01398        this, SLOT( checkNext() ));
01399   autoDelete = false;
01400   modaldlg = _modal;
01401   progressbar = _progressbar;
01402 
01403   proc     = 0;
01404   ksconfig = 0;
01405   ksdlg    = 0;
01406   lastpos  = 0;
01407 
01408   //won't be using the dialog in ksconfig, just the option values
01409   if ( _ksc != 0 )
01410     ksconfig = new KSpellConfig( *_ksc );
01411   else
01412     ksconfig = new KSpellConfig;
01413 
01414   codec = 0;
01415   switch ( ksconfig->encoding() )
01416   {
01417   case KS_E_LATIN1:
01418      codec = QTextCodec::codecForName("ISO 8859-1");
01419      break;
01420   case KS_E_LATIN2:
01421      codec = QTextCodec::codecForName("ISO 8859-2");
01422      break;
01423   case KS_E_LATIN3:
01424       codec = QTextCodec::codecForName("ISO 8859-3");
01425       break;
01426   case KS_E_LATIN4:
01427       codec = QTextCodec::codecForName("ISO 8859-4");
01428       break;
01429   case KS_E_LATIN5:
01430       codec = QTextCodec::codecForName("ISO 8859-5");
01431       break;
01432   case KS_E_LATIN7:
01433       codec = QTextCodec::codecForName("ISO 8859-7");
01434       break;
01435   case KS_E_LATIN8:
01436       codec = QTextCodec::codecForName("ISO 8859-8-i");
01437       break;
01438   case KS_E_LATIN9:
01439       codec = QTextCodec::codecForName("ISO 8859-9");
01440       break;
01441   case KS_E_LATIN13:
01442       codec = QTextCodec::codecForName("ISO 8859-13");
01443       break;
01444   case KS_E_LATIN15:
01445       codec = QTextCodec::codecForName("ISO 8859-15");
01446       break;
01447   case KS_E_UTF8:
01448       codec = QTextCodec::codecForName("UTF-8");
01449       break;
01450   case KS_E_KOI8R:
01451       codec = QTextCodec::codecForName("KOI8-R");
01452       break;
01453   case KS_E_KOI8U:
01454       codec = QTextCodec::codecForName("KOI8-U");
01455       break;
01456   case KS_E_CP1251:
01457       codec = QTextCodec::codecForName("CP1251");
01458       break;
01459   case KS_E_CP1255:
01460       codec = QTextCodec::codecForName("CP1255");
01461       break;
01462   default:
01463      break;
01464   }
01465 
01466   kdDebug(750) << __FILE__ << ":" << __LINE__ << " Codec = " << (codec ? codec->name() : "<default>") << endl;
01467 
01468   // copy ignore list from ksconfig
01469   ignorelist += ksconfig->ignoreList();
01470 
01471   replacelist += ksconfig->replaceAllList();
01472   texmode=dlgon=false;
01473   m_status = Starting;
01474   dialogsetup = false;
01475   progres=10;
01476   curprog=0;
01477 
01478   dialogwillprocess = false;
01479   dialog3slot = QString::null;
01480 
01481   personaldict = false;
01482   dlgresult = -1;
01483 
01484   caption = _caption;
01485 
01486   parent = _parent;
01487 
01488   trystart = 0;
01489   maxtrystart = 2;
01490 
01491   if ( obj && slot )
01492       // caller wants to know when kspell is ready
01493       connect( this, SIGNAL(ready(KSpell *)), obj, slot);
01494   else
01495       // Hack for modal spell checking
01496       connect( this, SIGNAL(ready(KSpell *)), this, SLOT(slotModalReady()) );
01497 
01498   proc = new KProcIO( codec );
01499 
01500   startIspell();
01501 }
01502 
01503 QString KSpell::modaltext;
01504 int KSpell::modalreturn = 0;
01505 QWidget* KSpell::modalWidgetHack = 0;
01506 
01507 #include "kspell.moc"
01508 
01509 
KDE Logo
This file is part of the documentation for kdeui Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Jul 22 10:16:47 2005 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003