• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDE3Support

k3spell.cpp

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

KDE3Support

Skip menu "KDE3Support"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal