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