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( "@" );

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