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 };
00078 
00079 //TODO
00080 //Parse stderr output
00081 //e.g. -- invalid dictionary name
00082 
00083 /*
00084   Things to put in KSpellConfigDlg:
00085     make root/affix combinations that aren't in the dictionary (-m)
00086     don't generate any affix/root combinations (-P)
00087     Report  run-together  words   with   missing blanks as spelling errors.  (-B)
00088     default dictionary (-d [dictionary])
00089     personal dictionary (-p [dictionary])
00090     path to ispell -- NO: ispell should be in $PATH
00091     */
00092 
00093 
00094 //  Connects a slot to KProcIO's output signal
00095 #define OUTPUT(x) (connect (proc, SIGNAL (readReady(KProcIO *)), this, SLOT (x(KProcIO *))))
00096 
00097 // Disconnect a slot from...
00098 #define NOOUTPUT(x) (disconnect (proc, SIGNAL (readReady(KProcIO *)), this, SLOT (x(KProcIO *))))
00099 
00100 
00101 
00102 KSpell::KSpell( QWidget *_parent, const QString &_caption,
00103         QObject *obj, const char *slot, KSpellConfig *_ksc,
00104         bool _progressbar, bool _modal )
00105 {
00106   initialize( _parent, _caption, obj, slot, _ksc,
00107               _progressbar, _modal, Text );
00108 }
00109 
00110 KSpell::KSpell( QWidget *_parent, const QString &_caption,
00111         QObject *obj, const char *slot, KSpellConfig *_ksc,
00112         bool _progressbar, bool _modal, SpellerType type )
00113 {
00114   initialize( _parent, _caption, obj, slot, _ksc,
00115               _progressbar, _modal, type );
00116 }
00117 
00118 void KSpell::hide() { ksdlg->hide(); }
00119 
00120 int KSpell::heightDlg() const { return ksdlg->height(); }
00121 int KSpell::widthDlg() const { return ksdlg->width(); }
00122 
00123 
00124 void
00125 KSpell::startIspell()
00126   //trystart = {0,1,2}
00127 {
00128 
00129   kdDebug(750) << "Try #" << trystart << endl;
00130 
00131   if ( trystart > 0 ) {
00132     proc->resetAll();
00133   }
00134 
00135   switch ( ksconfig->client() )
00136   {
00137   case KS_CLIENT_ISPELL:
00138     *proc << "ispell";
00139     kdDebug(750) << "Using ispell" << endl;
00140     break;
00141   case KS_CLIENT_ASPELL:
00142     *proc << "aspell";
00143     kdDebug(750) << "Using aspell" << endl;
00144     break;
00145   case KS_CLIENT_HSPELL:
00146     *proc << "hspell";
00147     kdDebug(750) << "Using hspell" << endl;
00148     break;
00149   }
00150 
00151   if ( ksconfig->client() == KS_CLIENT_ISPELL || ksconfig->client() == KS_CLIENT_ASPELL )
00152   {
00153     *proc << "-a" << "-S";
00154 
00155     switch ( d->type )
00156     {
00157     case HTML:
00158       //Debian uses an ispell version that has the -h option instead.
00159       //Not sure what they did, but the preferred spell checker
00160       //on that platform is aspell anyway, so use -H untill I'll come
00161       //up with something better.
00162       *proc << "-H";
00163       break;
00164     case TeX:
00165       //same for aspell and ispell
00166       *proc << "-t";
00167       break;
00168     case Nroff:
00169       //only ispell supports
00170       if ( ksconfig->client() == KS_CLIENT_ISPELL )
00171         *proc << "-n";
00172       break;
00173     case Text:
00174     default:
00175       //nothing
00176       break;
00177     }
00178     if (ksconfig->noRootAffix())
00179     {
00180       *proc<<"-m";
00181     }
00182     if (ksconfig->runTogether())
00183     {
00184       *proc << "-B";
00185     }
00186     else
00187     {
00188       *proc << "-C";
00189     }
00190 
00191 
00192     if (trystart<2)
00193     {
00194       if (! ksconfig->dictionary().isEmpty())
00195       {
00196         kdDebug(750) << "using dictionary [" << ksconfig->dictionary() << "]" << endl;
00197         *proc << "-d";
00198         *proc << ksconfig->dictionary();
00199       }
00200     }
00201 
00202   //Note to potential debuggers:  -Tlatin2 _is_ being added on the
00203   //  _first_ try.  But, some versions of ispell will fail with this
00204   // option, so kspell tries again without it.  That's why as 'ps -ax'
00205   // shows "ispell -a -S ..." withou the "-Tlatin2" option.
00206 
00207     if ( trystart<1 ) {
00208       switch ( ksconfig->encoding() )
00209       {
00210       case KS_E_LATIN1:
00211     *proc << "-Tlatin1";
00212     break;
00213       case KS_E_LATIN2:
00214     *proc << "-Tlatin2";
00215     break;
00216       case KS_E_LATIN3:
00217         *proc << "-Tlatin3";
00218         break;
00219 
00220         // add the other charsets here
00221       case KS_E_LATIN4:
00222       case KS_E_LATIN5:
00223       case KS_E_LATIN7:
00224       case KS_E_LATIN8:
00225       case KS_E_LATIN9:
00226       case KS_E_LATIN13:
00227       case KS_E_LATIN15:
00228     // will work, if this is the default charset in the dictionary
00229     kdError(750) << "charsets iso-8859-4 .. iso-8859-15 not supported yet" << endl;
00230     break;
00231       case KS_E_UTF8:
00232         *proc << "-Tutf8";
00233         break;
00234       case KS_E_KOI8U:
00235     *proc << "-w'"; // add ' as a word char
00236     break;
00237       }
00238     }
00239 
00240   // -a : pipe mode
00241   // -S : sort suggestions by probable correctness
00242   }
00243   else       // hspell doesn't need all the rest of the options
00244     *proc << "-a";
00245 
00246   if (trystart==0) //don't connect these multiple times
00247   {
00248     connect( proc, SIGNAL(receivedStderr(KProcess *, char *, int)),
00249              this, SLOT(ispellErrors(KProcess *, char *, int)) );
00250 
00251     connect( proc, SIGNAL(processExited(KProcess *)),
00252              this, SLOT(ispellExit (KProcess *)) );
00253 
00254     OUTPUT(KSpell2);
00255   }
00256 
00257   if ( proc->start() == false )
00258   {
00259     m_status = Error;
00260     QTimer::singleShot( 0, this, SLOT(emitDeath()));
00261   }
00262 }
00263 
00264 void
00265 KSpell::ispellErrors( KProcess *, char *buffer, int buflen )
00266 {
00267   buffer[buflen-1] = '\0';
00268   //  kdDebug(750) << "ispellErrors [" << buffer << "]\n" << endl;
00269 }
00270 
00271 void KSpell::KSpell2( KProcIO * )
00272 
00273 {
00274   QString line;
00275 
00276   kdDebug(750) << "KSpell::KSpell2" << endl;
00277 
00278   trystart = maxtrystart;  //We've officially started ispell and don't want
00279                            //to try again if it dies.
00280 
00281   if ( proc->readln( line, true ) == -1 )
00282   {
00283      QTimer::singleShot( 0, this, SLOT(emitDeath()) );
00284      return;
00285   }
00286 
00287 
00288   if ( line[0] != '@' ) //@ indicates that ispell is working fine
00289   {
00290      QTimer::singleShot( 0, this, SLOT(emitDeath()) );
00291      return;
00292   }
00293 
00294   //We want to recognize KDE in any text!
00295   if ( ignore("kde") == false)
00296   {
00297      kdDebug(750) << "@KDE was false" << endl;
00298      QTimer::singleShot( 0, this, SLOT(emitDeath()) );
00299      return;
00300   }
00301 
00302   //We want to recognize linux in any text!
00303   if ( ignore("linux") == false )
00304   {
00305      kdDebug(750) << "@Linux was false" << endl;
00306      QTimer::singleShot( 0, this, SLOT(emitDeath()) );
00307      return;
00308   }
00309 
00310   NOOUTPUT( KSpell2 );
00311 
00312   m_status = Running;
00313   emit ready( this );
00314 }
00315 
00316 void
00317 KSpell::setUpDialog( bool reallyuseprogressbar )
00318 {
00319   if ( dialogsetup )
00320     return;
00321 
00322   //Set up the dialog box
00323   ksdlg = new KSpellDlg( parent, "dialog",
00324                          progressbar && reallyuseprogressbar, modaldlg );
00325   ksdlg->setCaption( caption );
00326 
00327   connect( ksdlg, SIGNAL(command(int)),
00328            this, SLOT(slotStopCancel(int)) );
00329   connect( this, SIGNAL(progress(unsigned int)),
00330        ksdlg, SLOT(slotProgress(unsigned int)) );
00331 
00332 #ifdef Q_WS_X11 // FIXME(E): Implement for Qt/Embedded
00333   KWin::setIcons( ksdlg->winId(), kapp->icon(), kapp->miniIcon() );
00334 #endif
00335   if ( modaldlg )
00336     ksdlg->setFocus();
00337   dialogsetup = true;
00338 }
00339 
00340 bool KSpell::addPersonal( const QString & word )
00341 {
00342   QString qs = word.simplifyWhiteSpace();
00343 
00344   //we'll let ispell do the work here b/c we can
00345   if ( qs.find(' ') != -1 || qs.isEmpty() )    // make sure it's a _word_
00346     return false;
00347 
00348   qs.prepend( "*" );
00349   personaldict = true;
00350 
00351   return proc->writeStdin( qs );
00352 }
00353 
00354 bool KSpell::writePersonalDictionary()
00355 {
00356   return proc->writeStdin("#");
00357 }
00358 
00359 bool KSpell::ignore( const QString & word )
00360 {
00361   QString qs = word.simplifyWhiteSpace();
00362 
00363   //we'll let ispell do the work here b/c we can
00364   if ( qs.find (' ') != -1 || qs.isEmpty() )    // make sure it's a _word_
00365     return false;
00366 
00367   qs.prepend( "@" );
00368 
00369   return proc->writeStdin( qs );
00370 }
00371 
00372 bool
00373 KSpell::cleanFputsWord( const QString & s, bool appendCR )
00374 {
00375   QString qs(s);
00376   bool empty = true;
00377 
00378   for( unsigned int i = 0; i < qs.length(); i++ )
00379   {
00380     //we need some punctuation for ornaments
00381     if ( qs[i] != '\'' && qs[i] != '\"' && qs[i] != '-'
00382          && qs[i].isPunct() || qs[i].isSpace() )
00383     {
00384       qs.remove(i,1);
00385       i--;
00386     } else {
00387       if ( qs[i].isLetter() )
00388         empty=false;
00389     }
00390   }
00391 
00392   // don't check empty words, otherwise synchronization will lost
00393   if (empty)
00394     return false;
00395 
00396   return proc->writeStdin( "^"+qs, appendCR );
00397 }
00398 
00399 bool
00400 KSpell::cleanFputs( const QString & s, bool appendCR )
00401 {
00402   QString qs(s);
00403   unsigned l = qs.length();
00404 
00405   // some uses of '$' (e.g. "$0") cause ispell to skip all following text
00406   for( unsigned int i = 0; i < l; ++i )
00407   {
00408     if( qs[i] == '$' )
00409       qs[i] = ' ';
00410   }
00411 
00412   if ( l<MAXLINELENGTH )
00413   {
00414     if ( qs.isEmpty() )
00415       qs="";
00416     return proc->writeStdin( "^"+qs, appendCR );
00417   }
00418   else
00419     return proc->writeStdin( QString::fromAscii( "^\n" ),appendCR );
00420 }
00421 
00422 bool KSpell::checkWord( const QString & buffer, bool _usedialog )
00423 {
00424   if (d->checking) { // don't check multiple words simultaneously
00425     BufferedWord bufferedWord;
00426     bufferedWord.method = Method1;
00427     bufferedWord.word = buffer;
00428     bufferedWord.useDialog = _usedialog;
00429     d->unchecked.append( bufferedWord );
00430     return true;
00431   }
00432   d->checking = true;
00433   QString qs = buffer.simplifyWhiteSpace();
00434 
00435   if ( qs.find (' ') != -1 || qs.isEmpty() ) {   // make sure it's a _word_
00436     QTimer::singleShot( 0, this, SLOT(checkNext()) );
00437     return false;
00438   }
00440   dialog3slot = SLOT(checkWord3());
00441 
00442   usedialog = _usedialog;
00443   setUpDialog( false );
00444   if ( _usedialog )
00445   {
00446     emitProgress();
00447   }
00448   else
00449     ksdlg->hide();
00450 
00451   OUTPUT(checkWord2);
00452   //  connect (this, SIGNAL (dialog3()), this, SLOT (checkWord3()));
00453 
00454   proc->writeStdin( "%" ); // turn off terse mode
00455   proc->writeStdin( buffer ); // send the word to ispell
00456 
00457   return true;
00458 }
00459 
00460 bool KSpell::checkWord( const QString & buffer, bool _usedialog, bool suggest )
00461 {
00462   if (d->checking) { // don't check multiple words simultaneously
00463     BufferedWord bufferedWord;
00464     bufferedWord.method = Method2;
00465     bufferedWord.word = buffer;
00466     bufferedWord.useDialog = _usedialog;
00467     bufferedWord.suggest = suggest;
00468     d->unchecked.append( bufferedWord );
00469     return true;
00470   }
00471   d->checking = true;
00472   QString qs = buffer.simplifyWhiteSpace();
00473 
00474   if ( qs.find (' ') != -1 || qs.isEmpty() ) {   // make sure it's a _word_
00475     QTimer::singleShot( 0, this, SLOT(checkNext()) );
00476     return false;
00477   }
00478 
00480   if ( !suggest ) {
00481     dialog3slot = SLOT(checkWord3());
00482     usedialog = _usedialog;
00483     setUpDialog( false );
00484     if ( _usedialog )
00485     {
00486       emitProgress();
00487     }
00488     else
00489       ksdlg->hide();
00490   }
00491   OUTPUT(checkWord2);
00492   //  connect (this, SIGNAL (dialog3()), this, SLOT (checkWord3()));
00493 
00494   proc->writeStdin( "%" ); // turn off terse mode
00495   proc->writeStdin( buffer ); // send the word to ispell
00496 
00497   return true;
00498 }
00499 
00500 void KSpell::checkWord2( KProcIO* )
00501 {
00502   QString word;
00503   QString line;
00504   proc->readln( line, true ); //get ispell's response
00505 
00506 /* ispell man page: "Each sentence of text input is terminated with an
00507    additional blank line,  indicating that ispell has completed processing
00508    the input line."
00509    <sanders>
00510    But there can be multiple lines returned in the case of an error,
00511    in this case we should consume all the output given otherwise spell checking
00512    can get out of sync.
00513    </sanders>
00514 */
00515   QString blank_line;
00516   while (proc->readln( blank_line, true ) != -1); // eat the blank line
00517   NOOUTPUT(checkWord2);
00518 
00519   bool mistake = ( parseOneResponse(line, word, sugg) == MISTAKE );
00520   if ( mistake && usedialog )
00521   {
00522     cwword = word;
00523     dialog( word, sugg, SLOT(checkWord3()) );
00524     QTimer::singleShot( 0, this, SLOT(checkNext()) );
00525     return;
00526   }
00527   else if( mistake )
00528   {
00529     emit misspelling( word, sugg, lastpos );
00530   }
00531 
00532   //emits a "corrected" signal _even_ if no change was made
00533   //so that the calling program knows when the check is complete
00534   emit corrected( word, word, 0L );
00535   QTimer::singleShot( 0, this, SLOT(checkNext()) );
00536 }
00537 
00538 void KSpell::checkNext()
00539 {
00540 // Queue words to prevent kspell from turning into a fork bomb
00541   d->checking = false;
00542   if (!d->unchecked.empty()) {
00543     BufferedWord buf = d->unchecked.front();
00544     d->unchecked.pop_front();
00545     if (buf.method == Method1)
00546       checkWord( buf.word, buf.useDialog );
00547     else
00548       checkWord( buf.word, buf.useDialog, buf.suggest );
00549   }
00550 }
00551 
00552 void KSpell::suggestWord( KProcIO * )
00553 {
00554   QString word;
00555   QString line;
00556   proc->readln( line, true ); //get ispell's response
00557 
00558 /* ispell man page: "Each sentence of text input is terminated with an
00559    additional blank line,  indicating that ispell has completed processing
00560    the input line." */
00561   QString blank_line;
00562   proc->readln( blank_line, true ); // eat the blank line
00563 
00564   NOOUTPUT(checkWord2);
00565 
00566   bool mistake = ( parseOneResponse(line, word, sugg) == MISTAKE );
00567   if ( mistake && usedialog )
00568   {
00569     cwword=word;
00570     dialog( word, sugg, SLOT(checkWord3()) );
00571     return;
00572   }
00573 }
00574 
00575 void KSpell::checkWord3()
00576 {
00577   disconnect( this, SIGNAL(dialog3()), this, SLOT(checkWord3()) );
00578 
00579   emit corrected( cwword, replacement(), 0L );
00580 }
00581 
00582 QString KSpell::funnyWord( const QString & word )
00583   // composes a guess from ispell to a readable word
00584   // e.g. "re+fry-y+ies" -> "refries"
00585 {
00586   QString qs;
00587   unsigned int i=0;
00588 
00589   for( i=0; word [i]!='\0';i++ )
00590   {
00591     if (word [i]=='+')
00592       continue;
00593     if (word [i]=='-')
00594     {
00595       QString shorty;
00596       unsigned int j;
00597       int k;
00598 
00599       for( j = i+1; word[j] != '\0' && word[j] != '+' && word[j] != '-'; j++ )
00600         shorty += word[j];
00601 
00602       i = j-1;
00603 
00604       if ( ( k = qs.findRev(shorty) ) == 0 || k != -1 )
00605         qs.remove( k, shorty.length() );
00606       else
00607       {
00608         qs += '-';
00609         qs += shorty;  //it was a hyphen, not a '-' from ispell
00610       }
00611     }
00612     else
00613       qs += word[i];
00614   }
00615 
00616   return qs;
00617 }
00618 
00619 
00620 int KSpell::parseOneResponse( const QString &buffer, QString &word, QStringList & sugg )
00621   // buffer is checked, word and sugg are filled in
00622   // returns
00623   //   GOOD    if word is fine
00624   //   IGNORE  if word is in ignorelist
00625   //   REPLACE if word is in replacelist
00626   //   MISTAKE if word is misspelled
00627 {
00628   word = "";
00629   posinline=0;
00630 
00631   sugg.clear();
00632 
00633   if ( buffer[0] == '*' || buffer[0] == '+' || buffer[0] == '-' )
00634   {
00635     return GOOD;
00636   }
00637 
00638   if ( buffer[0] == '&' || buffer[0] == '?' || buffer[0] == '#' )
00639   {
00640     int i,j;
00641 
00642 
00643     word = buffer.mid( 2, buffer.find( ' ', 3 ) -2 );
00644     //check() needs this
00645     orig=word;
00646 
00647     if( d->m_bIgnoreTitleCase && word == word.upper() )
00648       return IGNORE;
00649 
00650     if( d->m_bIgnoreUpperWords && word[0] == word[0].upper() )
00651     {
00652       QString text = word[0] + word.right( word.length()-1 ).lower();
00653       if( text == word )
00654         return IGNORE;
00655     }
00656 
00658     //We don't take advantage of ispell's ignore function because
00659     //we can't interrupt ispell's output (when checking a large
00660     //buffer) to add a word to _it's_ ignore-list.
00661     if ( ignorelist.findIndex( word.lower() ) != -1 )
00662       return IGNORE;
00663 
00665     QString qs2;
00666 
00667     if ( buffer.find( ':' ) != -1 )
00668       qs2 = buffer.left( buffer.find(':') );
00669     else
00670       qs2 = buffer;
00671 
00672     posinline = qs2.right( qs2.length()-qs2.findRev(' ') ).toInt()-1;
00673 
00675     QStringList::Iterator it = replacelist.begin();
00676     for( ;it != replacelist.end(); ++it, ++it ) // Skip two entries at a time.
00677     {
00678       if ( word == *it ) // Word matches
00679       {
00680         ++it;
00681         word = *it;   // Replace it with the next entry
00682         return REPLACE;
00683       }
00684     }
00685 
00687     if ( buffer[0] != '#' )
00688     {
00689       QString qs = buffer.mid( buffer.find(':')+2, buffer.length() );
00690       qs += ',';
00691       sugg.clear();
00692       i = j = 0;
00693 
00694       while( (unsigned int)i < qs.length() )
00695       {
00696         QString temp = qs.mid( i, (j=qs.find (',',i)) - i );
00697         sugg.append( funnyWord(temp) );
00698 
00699         i=j+2;
00700       }
00701     }
00702 
00703     if ( (sugg.count()==1) && (sugg.first() == word) )
00704       return GOOD;
00705 
00706     return MISTAKE;
00707   }
00708 
00709   if ( buffer.isEmpty() ) {
00710       kdDebug(750) << "Got an empty response: ignoring"<<endl;
00711       return GOOD;
00712   }
00713 
00714   kdError(750) << "HERE?: [" << buffer << "]" << endl;
00715   kdError(750) << "Please report this to zack@kde.org" << endl;
00716   kdError(750) << "Thank you!" << endl;
00717 
00718   emit done( false );
00719   emit done( KSpell::origbuffer );
00720   return MISTAKE;
00721 }
00722 
00723 bool KSpell::checkList (QStringList *_wordlist, bool _usedialog)
00724   // prepare check of string list
00725 {
00726   wordlist=_wordlist;
00727   if ((totalpos=wordlist->count())==0)
00728     return false;
00729   wlIt = wordlist->begin();
00730   usedialog=_usedialog;
00731 
00732   // prepare the dialog
00733   setUpDialog();
00734 
00735   //set the dialog signal handler
00736   dialog3slot = SLOT (checkList4 ());
00737 
00738   proc->writeStdin ("%"); // turn off terse mode & check one word at a time
00739 
00740   //lastpos now counts which *word number* we are at in checkListReplaceCurrent()
00741   lastpos = -1;
00742   checkList2();
00743 
00744   // when checked, KProcIO calls checkList3a
00745   OUTPUT(checkList3a);
00746 
00747   return true;
00748 }
00749 
00750 void KSpell::checkList2 ()
00751   // send one word from the list to KProcIO
00752   // invoked first time by checkList, later by checkListReplaceCurrent and checkList4
00753 {
00754   // send next word
00755   if (wlIt != wordlist->end())
00756   {
00757     kdDebug(750) << "KS::cklist2 " << lastpos << ": " << *wlIt << endl;
00758 
00759     d->endOfResponse = false;
00760     bool put;
00761     lastpos++; offset=0;
00762     put = cleanFputsWord (*wlIt);
00763     ++wlIt;
00764 
00765     // when cleanFPutsWord failed (e.g. on empty word)
00766     // try next word; may be this is not good for other
00767     // problems, because this will make read the list up to the end
00768     if (!put) {
00769       checkList2();
00770     }
00771   }
00772   else
00773     // end of word list
00774   {
00775     NOOUTPUT(checkList3a);
00776     ksdlg->hide();
00777     emit done(true);
00778   }
00779 }
00780 
00781 void KSpell::checkList3a (KProcIO *)
00782   // invoked by KProcIO, when data from ispell are read
00783 {
00784   //kdDebug(750) << "start of checkList3a" << endl;
00785 
00786   // don't read more data, when dialog is waiting
00787   // for user interaction
00788   if ( dlgon ) {
00789     //kdDebug(750) << "dlgon: don't read more data" << endl;
00790     return;
00791   }
00792 
00793   int e, tempe;
00794 
00795   QString word;
00796   QString line;
00797 
00798   do
00799   {
00800     tempe=proc->readln( line, true ); //get ispell's response
00801 
00802     //kdDebug(750) << "checkList3a: read bytes [" << tempe << "]" << endl;
00803 
00804 
00805     if ( tempe == 0 ) {
00806       d->endOfResponse = true;
00807       //kdDebug(750) << "checkList3a: end of resp" << endl;
00808     } else if ( tempe>0 ) {
00809       if ( (e=parseOneResponse( line, word, sugg ) ) == MISTAKE ||
00810            e==REPLACE )
00811       {
00812         dlgresult=-1;
00813 
00814         if ( e == REPLACE )
00815         {
00816           QString old = *(--wlIt); ++wlIt;
00817           dlgreplacement = word;
00818           checkListReplaceCurrent();
00819           // inform application
00820           emit corrected( old, *(--wlIt), lastpos ); ++wlIt;
00821         }
00822         else if( usedialog )
00823         {
00824           cwword = word;
00825           dlgon = true;
00826           // show the dialog
00827           dialog( word, sugg, SLOT(checkList4()) );
00828           return;
00829         }
00830         else
00831         {
00832           d->m_bNoMisspellingsEncountered = false;
00833           emit misspelling( word, sugg, lastpos );
00834         }
00835       }
00836 
00837     }
00838     emitProgress (); //maybe
00839 
00840     // stop when empty line or no more data
00841   } while (tempe > 0);
00842 
00843   //kdDebug(750) << "checkList3a: exit loop with [" << tempe << "]" << endl;
00844 
00845   // if we got an empty line, t.e. end of ispell/aspell response
00846   // and the dialog isn't waiting for user interaction, send next word
00847   if (d->endOfResponse && !dlgon) {
00848     //kdDebug(750) << "checkList3a: send next word" << endl;
00849     checkList2();
00850   }
00851 }
00852 
00853 void KSpell::checkListReplaceCurrent()
00854 {
00855 
00856   // go back to misspelled word
00857   wlIt--;
00858 
00859   QString s = *wlIt;
00860   s.replace(posinline+offset,orig.length(),replacement());
00861   offset += replacement().length()-orig.length();
00862   wordlist->insert (wlIt, s);
00863   wlIt = wordlist->remove (wlIt);
00864   // wlIt now points to the word after the repalced one
00865 
00866 }
00867 
00868 void KSpell::checkList4 ()
00869   // evaluate dialog return, when a button was pressed there
00870 {
00871   dlgon=false;
00872   QString old;
00873 
00874   disconnect (this, SIGNAL (dialog3()), this, SLOT (checkList4()));
00875 
00876   //others should have been processed by dialog() already
00877   switch (dlgresult)
00878   {
00879   case KS_REPLACE:
00880   case KS_REPLACEALL:
00881     kdDebug(750) << "KS: cklist4: lastpos: " << lastpos << endl;
00882     old = *(--wlIt);
00883     ++wlIt;
00884     // replace word
00885     checkListReplaceCurrent();
00886     emit corrected( old, *(--wlIt), lastpos );
00887     ++wlIt;
00888     break;
00889   case KS_CANCEL:
00890     ksdlg->hide();
00891     emit done( false );
00892     return;
00893   case KS_STOP:
00894     ksdlg->hide();
00895     emit done( true );
00896     return;
00897   case KS_CONFIG:
00898     ksdlg->hide();
00899     emit done( false );
00900     //check( origbuffer.mid( lastpos ), true );
00901     //trystart = 0;
00902     //proc->disconnect();
00903     //proc->kill();
00904     //delete proc;
00905     //proc = new KProcIO( codec );
00906     //startIspell();
00907     return;
00908   };
00909 
00910   // read more if there is more, otherwise send next word
00911   if (!d->endOfResponse) {
00912     //kdDebug(750) << "checkList4: read more from response" << endl;
00913     checkList3a(NULL);
00914   }
00915 }
00916 
00917 bool KSpell::check( const QString &_buffer, bool _usedialog )
00918 {
00919   QString qs;
00920 
00921   usedialog = _usedialog;
00922   setUpDialog();
00923   //set the dialog signal handler
00924   dialog3slot = SLOT(check3());
00925 
00926   kdDebug(750) << "KS: check" << endl;
00927   origbuffer = _buffer;
00928   if ( ( totalpos = origbuffer.length() ) == 0 )
00929   {
00930     emit done( origbuffer );
00931     return false;
00932   }
00933 
00934 
00935   // Torben: I corrected the \n\n problem directly in the
00936   //         origbuffer since I got errors otherwise
00937   if ( !origbuffer.endsWith("\n\n" ) )
00938   {
00939     if (origbuffer.at(origbuffer.length()-1)!='\n')
00940     {
00941       origbuffer+='\n';
00942       origbuffer+='\n'; //shouldn't these be removed at some point?
00943     }
00944     else
00945       origbuffer+='\n';
00946   }
00947 
00948   newbuffer = origbuffer;
00949 
00950   // KProcIO calls check2 when read from ispell
00951   OUTPUT( check2 );
00952   proc->writeStdin( "!" );
00953 
00954   //lastpos is a position in newbuffer (it has offset in it)
00955   offset = lastlastline = lastpos = lastline = 0;
00956 
00957   emitProgress();
00958 
00959   // send first buffer line
00960   int i = origbuffer.find( '\n', 0 ) + 1;
00961   qs = origbuffer.mid( 0, i );
00962   cleanFputs( qs, false );
00963 
00964   lastline=i; //the character position, not a line number
00965 
00966   if ( usedialog )
00967   {
00968     emitProgress();
00969   }
00970   else
00971     ksdlg->hide();
00972 
00973   return true;
00974 }
00975 
00976 
00977 void KSpell::check2( KProcIO * )
00978   // invoked by KProcIO when read from ispell
00979 {
00980   int e, tempe;
00981   QString word;
00982   QString line;
00983   static bool recursive = false;
00984   if (recursive &&
00985       !ksdlg )
00986   {
00987       return;
00988   }
00989   recursive = true;
00990 
00991   do
00992   {
00993     tempe = proc->readln( line, false ); //get ispell's response
00994     //kdDebug(750) << "KSpell::check2 (" << tempe << "b)" << endl;
00995 
00996     if ( tempe>0 )
00997     {
00998       if ( ( e=parseOneResponse (line, word, sugg) )==MISTAKE ||
00999            e==REPLACE)
01000       {
01001         dlgresult=-1;
01002 
01003         // for multibyte encoding posinline needs correction
01004         if (ksconfig->encoding() == KS_E_UTF8) {
01005           // kdDebug(750) << "line: " << origbuffer.mid(lastlastline,
01006           // lastline-lastlastline) << endl;
01007           // kdDebug(750) << "posinline uncorr: " << posinline << endl;
01008 
01009           // convert line to UTF-8, cut at pos, convert back to UCS-2
01010           // and get string length
01011           posinline = (QString::fromUtf8(
01012                          origbuffer.mid(lastlastline,lastline-lastlastline).utf8(),
01013                          posinline)).length();
01014           // kdDebug(750) << "posinline corr: " << posinline << endl;
01015         }
01016 
01017         lastpos = posinline+lastlastline+offset;
01018 
01019         //orig is set by parseOneResponse()
01020 
01021         if (e==REPLACE)
01022         {
01023           dlgreplacement=word;
01024           emit corrected( orig, replacement(), lastpos );
01025           offset += replacement().length()-orig.length();
01026           newbuffer.replace( lastpos, orig.length(), word );
01027         }
01028         else  //MISTAKE
01029         {
01030           cwword = word;
01031           //kdDebug(750) << "(Before dialog) word=[" << word << "] cwword =[" << cwword << "]\n" << endl;
01032           if ( usedialog ) {
01033             // show the word in the dialog
01034             dialog( word, sugg, SLOT(check3()) );
01035           } else {
01036             // No dialog, just emit misspelling and continue
01037             d->m_bNoMisspellingsEncountered = false;
01038             emit misspelling( word, sugg, lastpos );
01039             dlgresult = KS_IGNORE;
01040             check3();
01041           }
01042           recursive = false;
01043           return;
01044         }
01045       }
01046 
01047     }
01048 
01049     emitProgress(); //maybe
01050 
01051   } while( tempe>0 );
01052 
01053   proc->ackRead();
01054 
01055 
01056   if ( tempe == -1 ) { //we were called, but no data seems to be ready...
01057     recursive = false;
01058     return;
01059   }
01060 
01061   //If there is more to check, then send another line to ISpell.
01062   if ( (unsigned int)lastline < origbuffer.length() )
01063   {
01064     int i;
01065     QString qs;
01066 
01067     //kdDebug(750) << "[EOL](" << tempe << ")[" << temp << "]" << endl;
01068 
01069     lastpos = (lastlastline=lastline) + offset; //do we really want this?
01070     i = origbuffer.find('\n', lastline) + 1;
01071     qs = origbuffer.mid( lastline, i-lastline );
01072     cleanFputs( qs, false );
01073     lastline = i;
01074     recursive = false;
01075     return;
01076   }
01077   else
01078     //This is the end of it all
01079   {
01080     ksdlg->hide();
01081     //      kdDebug(750) << "check2() done" << endl;
01082     newbuffer.truncate( newbuffer.length()-2 );
01083     emitProgress();
01084     emit done( newbuffer );
01085   }
01086   recursive = false;
01087 }
01088 
01089 void KSpell::check3 ()
01090   // evaluates the return value of the dialog
01091 {
01092   disconnect (this, SIGNAL (dialog3()), this, SLOT (check3()));
01093   kdDebug(750) << "check3 [" << cwword << "] [" << replacement() << "] " << dlgresult << endl;
01094 
01095   //others should have been processed by dialog() already
01096   switch (dlgresult)
01097   {
01098   case KS_REPLACE:
01099   case KS_REPLACEALL:
01100     offset+=replacement().length()-cwword.length();
01101     newbuffer.replace (lastpos, cwword.length(),
01102                        replacement());
01103     emit corrected (dlgorigword, replacement(), lastpos);
01104     break;
01105   case KS_CANCEL:
01106     //      kdDebug(750) << "canceled\n" << endl;
01107     ksdlg->hide();
01108     emit done( origbuffer );
01109     return;
01110   case KS_CONFIG:
01111     ksdlg->hide();
01112     emit done( origbuffer );
01113     KMessageBox::information( 0, i18n("You have to restart the dialog for changes to take effect") );
01114     //check( origbuffer.mid( lastpos ), true );
01115     return;
01116   case KS_STOP:
01117     ksdlg->hide();
01118     //buffer=newbuffer);
01119     emitProgress();
01120     emit done (newbuffer);
01121     return;
01122   };
01123 
01124   proc->ackRead();
01125 }
01126 
01127 void
01128 KSpell::slotStopCancel (int result)
01129 {
01130   if (dialogwillprocess)
01131     return;
01132 
01133   kdDebug(750) << "KSpell::slotStopCancel [" << result << "]" << endl;
01134 
01135   if (result==KS_STOP || result==KS_CANCEL)
01136     if (!dialog3slot.isEmpty())
01137     {
01138       dlgresult=result;
01139       connect (this, SIGNAL (dialog3()), this, dialog3slot.ascii());
01140       emit dialog3();
01141     }
01142 }
01143 
01144 
01145 void KSpell::dialog( const QString & word, QStringList & sugg, const char *_slot )
01146 {
01147   dlgorigword = word;
01148 
01149   dialog3slot = _slot;
01150   dialogwillprocess = true;
01151   connect( ksdlg, SIGNAL(command(int)), this, SLOT(dialog2(int)) );
01152   QString tmpBuf = newbuffer;
01153   kdDebug(750)<<" position = "<<lastpos<<endl;
01154 
01155   // extract a context string, replace all characters which might confuse
01156   // the RichText display and highlight the possibly wrong word
01157   QString marker( "_MARKER_" );
01158   tmpBuf.replace( lastpos, word.length(), marker );
01159   QString context = tmpBuf.mid(QMAX(lastpos-18,0), 2*18+marker.length());
01160   context.replace( '\n',QString::fromLatin1(" "));
01161   context.replace( '<', QString::fromLatin1("&lt;") );
01162   context.replace( '>', QString::fromLatin1("&gt;") );
01163   context.replace( marker, QString::fromLatin1("<b>%1</b>").arg( word ) );
01164   context = "<qt>" + context + "</qt>";
01165 
01166   ksdlg->init( word, &sugg, context );
01167   d->m_bNoMisspellingsEncountered = false;
01168   emit misspelling( word, sugg, lastpos );
01169 
01170   emitProgress();
01171   ksdlg->show();
01172 }
01173 
01174 void KSpell::dialog2( int result )
01175 {
01176   QString qs;
01177 
01178   disconnect( ksdlg, SIGNAL(command(int)), this, SLOT(dialog2(int)) );
01179   dialogwillprocess = false;
01180   dlgresult = result;
01181   ksdlg->standby();
01182 
01183   dlgreplacement = ksdlg->replacement();
01184 
01185   //process result here
01186   switch ( dlgresult )
01187   {
01188   case KS_IGNORE:
01189     emit ignoreword( dlgorigword );
01190     break;
01191   case KS_IGNOREALL:
01192     // would be better to lower case only words with beginning cap
01193     ignorelist.prepend( dlgorigword.lower() );
01194     emit ignoreall( dlgorigword );
01195     break;
01196   case KS_ADD:
01197     addPersonal( dlgorigword );
01198     personaldict = true;
01199     emit addword( dlgorigword );
01200     // adding to pesonal dict takes effect at the next line, not the current
01201     ignorelist.prepend( dlgorigword.lower() );
01202     break;
01203   case KS_REPLACEALL:
01204   {
01205     replacelist.append( dlgorigword );
01206     QString _replacement = replacement();
01207     replacelist.append( _replacement );
01208     emit replaceall( dlgorigword ,  _replacement );
01209   }
01210     break;
01211   case KS_SUGGEST:
01212     checkWord( ksdlg->replacement(), false, true );
01213     return;
01214     break;
01215   }
01216 
01217   connect( this, SIGNAL(dialog3()), this, dialog3slot.ascii() );
01218   emit dialog3();
01219 }
01220 
01221 
01222 KSpell::~KSpell()
01223 {
01224   delete proc;
01225   delete ksconfig;
01226   delete ksdlg;
01227   delete d;
01228 }
01229 
01230 
01231 KSpellConfig KSpell::ksConfig() const
01232 {
01233   ksconfig->setIgnoreList(ignorelist);
01234   ksconfig->setReplaceAllList(replacelist);
01235   return *ksconfig;
01236 }
01237 
01238 void KSpell::cleanUp()
01239 {
01240   if ( m_status == Cleaning )
01241     return; // Ignore
01242 
01243   if ( m_status == Running )
01244   {
01245     if ( personaldict )
01246       writePersonalDictionary();
01247     m_status = Cleaning;
01248   }
01249   proc->closeStdin();
01250 }
01251 
01252 void KSpell::ispellExit( KProcess* )
01253 {
01254   kdDebug() << "KSpell::ispellExit() " << m_status << endl;
01255 
01256   if ( (m_status == Starting) && (trystart < maxtrystart) )
01257   {
01258     trystart++;
01259     startIspell();
01260     return;
01261   }
01262 
01263   if ( m_status == Starting )
01264      m_status = Error;
01265   else if (m_status == Cleaning)
01266      m_status = d->m_bNoMisspellingsEncountered ? FinishedNoMisspellingsEncountered : Finished;
01267   else if ( m_status == Running )
01268      m_status = Crashed;
01269   else // Error, Finished, Crashed
01270      return; // Dead already
01271 
01272   kdDebug(750) << "Death" << endl;
01273   QTimer::singleShot( 0, this, SLOT(emitDeath()) );
01274 }
01275 
01276 // This is always called from the event loop to make
01277 // sure that the receiver can safely delete the
01278 // KSpell object.
01279 void KSpell::emitDeath()
01280 {
01281   bool deleteMe = autoDelete; // Can't access object after next call!
01282   emit death();
01283   if ( deleteMe )
01284     deleteLater();
01285 }
01286 
01287 void KSpell::setProgressResolution (unsigned int res)
01288 {
01289   progres=res;
01290 }
01291 
01292 void KSpell::emitProgress ()
01293 {
01294   uint nextprog = (uint) (100.*lastpos/(double)totalpos);
01295 
01296   if ( nextprog >= curprog )
01297   {
01298     curprog = nextprog;
01299     emit progress( curprog );
01300   }
01301 }
01302 
01303 void KSpell::moveDlg( int x, int y )
01304 {
01305   QPoint pt( x,y ), pt2;
01306   pt2 = parent->mapToGlobal( pt );
01307   ksdlg->move( pt2.x(),pt2.y() );
01308 }
01309 
01310 void KSpell::setIgnoreUpperWords(bool _ignore)
01311 {
01312   d->m_bIgnoreUpperWords=_ignore;
01313 }
01314 
01315 void KSpell::setIgnoreTitleCase(bool _ignore)
01316 {
01317   d->m_bIgnoreTitleCase=_ignore;
01318 }
01319 // --------------------------------------------------
01320 // Stuff for modal (blocking) spell checking
01321 //
01322 // Written by Torben Weis <weis@kde.org>. So please
01323 // send bug reports regarding the modal stuff to me.
01324 // --------------------------------------------------
01325 
01326 int
01327 KSpell::modalCheck( QString& text )
01328 {
01329   return modalCheck( text,0 );
01330 }
01331 
01332 int
01333 KSpell::modalCheck( QString& text, KSpellConfig* _kcs )
01334 {
01335   modalreturn = 0;
01336   modaltext = text;
01337 
01338   KSpell* spell = new KSpell( 0L, i18n("Spell Checker"), 0 ,
01339                               0, _kcs, true, true );
01340 
01341   while (spell->status()!=Finished)
01342     kapp->processEvents();
01343 
01344   text = modaltext;
01345 
01346   delete spell;
01347   return modalreturn;
01348 }
01349 
01350 void KSpell::slotSpellCheckerCorrected( const QString & oldText, const QString & newText, unsigned int pos )
01351 {
01352   modaltext=modaltext.replace(pos,oldText.length(),newText);
01353 }
01354 
01355 
01356 void KSpell::slotModalReady()
01357 {
01358   //kdDebug() << qApp->loopLevel() << endl;
01359   //kdDebug(750) << "MODAL READY------------------" << endl;
01360 
01361   Q_ASSERT( m_status == Running );
01362   connect( this, SIGNAL( done( const QString & ) ),
01363            this, SLOT( slotModalDone( const QString & ) ) );
01364   QObject::connect( this, SIGNAL( corrected( const QString&, const QString&, unsigned int ) ),
01365                     this, SLOT( slotSpellCheckerCorrected( const QString&, const QString &, unsigned int ) ) );
01366   QObject::connect( this, SIGNAL( death() ),
01367                     this, SLOT( slotModalSpellCheckerFinished( ) ) );
01368   check( modaltext );
01369 }
01370 
01371 void KSpell::slotModalDone( const QString &/*_buffer*/ )
01372 {
01373   //kdDebug(750) << "MODAL DONE " << _buffer << endl;
01374   //modaltext = _buffer;
01375   cleanUp();
01376 
01377   //kdDebug() << "ABOUT TO EXIT LOOP" << endl;
01378   //qApp->exit_loop();
01379 
01380   //modalWidgetHack->close(true);
01381   slotModalSpellCheckerFinished();
01382 }
01383 
01384 void KSpell::slotModalSpellCheckerFinished( )
01385 {
01386   modalreturn=(int)this->status();
01387 }
01388 
01389 void KSpell::initialize( QWidget *_parent, const QString &_caption,
01390                          QObject *obj, const char *slot, KSpellConfig *_ksc,
01391                          bool _progressbar, bool _modal, SpellerType type )
01392 {
01393   d = new KSpellPrivate;
01394 
01395   d->m_bIgnoreUpperWords =false;
01396   d->m_bIgnoreTitleCase =false;
01397   d->m_bNoMisspellingsEncountered = true;
01398   d->type = type;
01399   d->checking = false;
01400   autoDelete = false;
01401   modaldlg = _modal;
01402   progressbar = _progressbar;
01403 
01404   proc     = 0;
01405   ksconfig = 0;
01406   ksdlg    = 0;
01407   lastpos  = 0;
01408 
01409   //won't be using the dialog in ksconfig, just the option values
01410   if ( _ksc != 0 )
01411     ksconfig = new KSpellConfig( *_ksc );
01412   else
01413     ksconfig = new KSpellConfig;
01414 
01415   codec = 0;
01416   switch ( ksconfig->encoding() )
01417   {
01418   case KS_E_LATIN1:
01419      codec = QTextCodec::codecForName("ISO 8859-1");
01420      break;
01421   case KS_E_LATIN2:
01422      codec = QTextCodec::codecForName("ISO 8859-2");
01423      break;
01424   case KS_E_LATIN3:
01425       codec = QTextCodec::codecForName("ISO 8859-3");
01426       break;
01427   case KS_E_LATIN4:
01428       codec = QTextCodec::codecForName("ISO 8859-4");
01429       break;
01430   case KS_E_LATIN5:
01431       codec = QTextCodec::codecForName("ISO 8859-5");
01432       break;
01433   case KS_E_LATIN7:
01434       codec = QTextCodec::codecForName("ISO 8859-7");
01435       break;
01436   case KS_E_LATIN8:
01437       codec = QTextCodec::codecForName("ISO 8859-8-i");
01438       break;
01439   case KS_E_LATIN9:
01440       codec = QTextCodec::codecForName("ISO 8859-9");
01441       break;
01442   case KS_E_LATIN13:
01443       codec = QTextCodec::codecForName("ISO 8859-13");
01444       break;
01445   case KS_E_LATIN15:
01446       codec = QTextCodec::codecForName("ISO 8859-15");
01447       break;
01448   case KS_E_UTF8:
01449       codec = QTextCodec::codecForName("UTF-8");
01450       break;
01451   case KS_E_KOI8R:
01452       codec = QTextCodec::codecForName("KOI8-R");
01453       break;
01454   case KS_E_KOI8U:
01455       codec = QTextCodec::codecForName("KOI8-U");
01456       break;
01457   case KS_E_CP1251:
01458       codec = QTextCodec::codecForName("CP1251");
01459       break;
01460   case KS_E_CP1255:
01461       codec = QTextCodec::codecForName("CP1255");
01462       break;
01463   default:
01464      break;
01465   }
01466 
01467   kdDebug(750) << __FILE__ << ":" << __LINE__ << " Codec = " << (codec ? codec->name() : "<default>") << endl;
01468 
01469   // copy ignore list from ksconfig
01470   ignorelist += ksconfig->ignoreList();
01471 
01472   replacelist += ksconfig->replaceAllList();
01473   texmode=dlgon=false;
01474   m_status = Starting;
01475   dialogsetup = false;
01476   progres=10;
01477   curprog=0;
01478 
01479   dialogwillprocess = false;
01480   dialog3slot = QString::null;
01481 
01482   personaldict = false;
01483   dlgresult = -1;
01484 
01485   caption = _caption;
01486 
01487   parent = _parent;
01488 
01489   trystart = 0;
01490   maxtrystart = 2;
01491 
01492   if ( obj && slot )
01493       // caller wants to know when kspell is ready
01494       connect( this, SIGNAL(ready(KSpell *)), obj, slot);
01495   else
01496       // Hack for modal spell checking
01497       connect( this, SIGNAL(ready(KSpell *)), this, SLOT(slotModalReady()) );
01498 
01499   proc = new KProcIO( codec );
01500 
01501   startIspell();
01502 }
01503 
01504 QString KSpell::modaltext;
01505 int KSpell::modalreturn = 0;
01506 QWidget* KSpell::modalWidgetHack = 0;
01507 
01508 #include "kspell.moc"
01509 
KDE Logo
This file is part of the documentation for kdeui Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Aug 4 05:24:00 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2003