00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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>
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
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118 #define OUTPUT(x) (connect (proc, SIGNAL (readyReadStandardOutput()), this, SLOT (x())))
00119
00120
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
00167 static bool determineASpellV6()
00168 {
00169 QString result;
00170 FILE *fs = popen("aspell -v", "r");
00171 if (fs)
00172 {
00173
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
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
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
00237
00238
00239
00240 *proc << "-H";
00241 break;
00242 case TeX:
00243
00244 *proc << "-t";
00245 break;
00246 case Nroff:
00247
00248 if ( ksconfig->client() == KS_CLIENT_ISPELL || ksconfig->client() == KS_CLIENT_HUNSPELL )
00249 *proc << "-n";
00250 break;
00251 case Text:
00252 default:
00253
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
00281
00282
00283
00284
00285 if ( ksconfig->client() == KS_CLIENT_HUNSPELL && trystart<1 ) {
00286
00287
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
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
00359 kError(750) << "charsets ISO-8859-4, -5, -7, -8, -9 and -13 not supported yet" << endl;
00360 break;
00361 case KS_E_LATIN15:
00362 if (ksconfig->client() == KS_CLIENT_ISPELL)
00363 {
00364
00365
00366
00367
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'";
00381 break;
00382 default:
00383 break;
00384 }
00385 }
00386
00387
00388
00389 }
00390 else
00391 *proc << "-a";
00392
00393 if (trystart == 0)
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
00419
00420 }
00421
00422 void K3Spell::K3Spell2( )
00423
00424 {
00425 QString line;
00426
00427 kDebug(750) << "K3Spell::K3Spell2";
00428
00429 trystart = maxtrystart;
00430
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('@') )
00442 {
00443 QTimer::singleShot( 0, this, SLOT(emitDeath()) );
00444 return;
00445 }
00446
00447
00448 if ( !ignore("kde") )
00449 {
00450 kDebug(750) << "@KDE was false";
00451 QTimer::singleShot( 0, this, SLOT(emitDeath()) );
00452 return;
00453 }
00454
00455
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
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
00494 if ( qs.indexOf(' ') != -1 || qs.isEmpty() )
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
00513 if ( qs.indexOf (' ') != -1 || qs.isEmpty() )
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
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
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
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) {
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() ) {
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 ;
00605
00606 OUTPUT(checkWord2);
00607
00608
00609 proc->write( d->convertQString( QString( "%" ) ) );
00610 proc->write( d->convertQString( buffer ) );
00611
00612 return true;
00613 }
00614
00615 bool K3Spell::checkWord( const QString & buffer, bool _usedialog, bool suggest )
00616 {
00617 if (d->checking) {
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() ) {
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 ) ;
00651
00652 OUTPUT(checkWord2);
00653
00654
00655 proc->write( d->convertQString( QString( "%" ) ) );
00656 proc->write( d->convertQString( buffer ) );
00657
00658 return true;
00659 }
00660
00661 void K3Spell::checkWord2( )
00662 {
00663 QString word;
00664 QString line;
00665 line = d->convertQByteArray( proc->readLine() );
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676 QByteArray data;
00677 while (proc->readLine( data.data(), data.count() ) != -1 ) ;
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
00696
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
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() );
00723
00724
00725
00726
00727 QByteArray data;
00728 while (proc->readLine( data.data(), data.count() ) != -1 ) ;
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
00750
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;
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
00785
00786
00787
00788
00789
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
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
00822
00823
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 )
00840 {
00841 if ( word == *it )
00842 {
00843 ++it;
00844 word = *it;
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
00888 {
00889 wordlist=_wordlist;
00890 if ((totalpos=wordlist->count())==0)
00891 return false;
00892 wlIt = wordlist->begin();
00893 usedialog=_usedialog;
00894
00895
00896 setUpDialog();
00897
00898
00899 dialog3slot = SLOT (checkList4 ());
00900
00901 proc->write(QByteArray( '%' ) );
00902
00903
00904 lastpos = -1;
00905 checkList2();
00906
00907
00908 OUTPUT(checkList3a);
00909
00910 return true;
00911 }
00912
00913 void K3Spell::checkList2 ()
00914
00915
00916 {
00917
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
00929
00930
00931 if (!put) {
00932 checkList2();
00933 }
00934 }
00935 else
00936
00937 {
00938 NOOUTPUT(checkList3a);
00939 ksdlg->hide();
00940 emit done(true);
00941 }
00942 }
00943
00944 void K3Spell::checkList3a ()
00945
00946 {
00947
00948
00949
00950
00951 if ( dlgon ) {
00952
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() );
00966
00967
00968 line = d->convertQByteArray( data );
00969
00970 if ( tempe == 0 ) {
00971 d->endOfResponse = true;
00972
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
00985 emit corrected( old, *(--wlIt), lastpos ); ++wlIt;
00986 }
00987 else if( usedialog )
00988 {
00989 cwword = word;
00990 dlgon = true;
00991
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 ();
01004
01005
01006 } while (tempe > 0);
01007
01008
01009
01010
01011
01012 if (d->endOfResponse && !dlgon) {
01013
01014 checkList2();
01015 }
01016 }
01017
01018 void K3Spell::checkListReplaceCurrent()
01019 {
01020
01021
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
01030
01031 }
01032
01033 void K3Spell::checkList4 ()
01034
01035 {
01036 dlgon=false;
01037 QString old;
01038
01039 disconnect (this, SIGNAL (dialog3()), this, SLOT (checkList4()));
01040
01041
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
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
01066
01067
01068
01069
01070
01071
01072 return;
01073 };
01074
01075
01076 if (!d->endOfResponse) {
01077
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
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
01101
01102 if ( !origbuffer.endsWith("\n\n" ) )
01103 {
01104 if (origbuffer.at(origbuffer.length()-1)!='\n')
01105 {
01106 origbuffer+='\n';
01107 origbuffer+='\n';
01108 }
01109 else
01110 origbuffer+='\n';
01111 }
01112
01113 newbuffer = origbuffer;
01114
01115
01116 OUTPUT( check2 );
01117 proc->write( QByteArray( "!" ) );
01118
01119
01120 offset = lastlastline = lastpos = lastline = 0;
01121
01122 emitProgress();
01123
01124
01125 int i = origbuffer.indexOf( '\n', 0 ) + 1;
01126 qs = origbuffer.mid( 0, i );
01127 cleanFputs( qs );
01128
01129 lastline=i;
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
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() );
01166 line = d->convertQByteArray( data );
01167
01168
01169 if ( tempe>0 )
01170 {
01171 if ( ( e=parseOneResponse (line, word, sugg) )==MISTAKE ||
01172 e==REPLACE)
01173 {
01174 dlgresult=-1;
01175
01176
01177 if ((ksconfig->encoding() == KS_E_UTF8) && !d->aspellV6) {
01178
01179
01180
01181
01182
01183
01184 posinline = (QString::fromUtf8(
01185 origbuffer.mid(lastlastline,lastline-lastlastline).toUtf8(),
01186 posinline)).length();
01187
01188 }
01189
01190 lastpos = posinline+lastlastline+offset;
01191
01192
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
01202 {
01203 cwword = word;
01204
01205 if ( usedialog ) {
01206
01207 dialog( word, sugg, SLOT(check3()) );
01208 } else {
01209
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();
01223
01224 } while( tempe>0 );
01225
01226 if ( tempe == -1 ) {
01227
01228
01229 NOOUTPUT( check2 );
01230
01231 OUTPUT( check2 );
01232 recursive = false;
01233 return;
01234 }
01235
01236
01237
01238
01239 if ( lastline < origbuffer.length() )
01240 {
01241 int i;
01242 QString qs;
01243
01244
01245
01246 lastpos = (lastlastline=lastline) + offset;
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
01256 {
01257 ksdlg->hide();
01258
01259 newbuffer.truncate( newbuffer.length()-2 );
01260 emitProgress();
01261 emit done( newbuffer );
01262 }
01263 recursive = false;
01264 }
01265
01266 void K3Spell::check3 ()
01267
01268 {
01269 disconnect (this, SIGNAL (dialog3()), this, SLOT (check3()));
01270 kDebug(750) << "check3 [" << cwword << "] [" << replacement() << "] " << dlgresult;
01271
01272
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
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
01292 return;
01293 case KS_STOP:
01294 ksdlg->hide();
01295
01296 emitProgress();
01297 emit done (newbuffer);
01298 return;
01299 };
01300
01301
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
01333
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("<") );
01339 context.replace( '>', QLatin1String(">") );
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
01368 switch ( dlgresult )
01369 {
01370 case KS_IGNORE:
01371 emit ignoreword( dlgorigword );
01372 break;
01373 case KS_IGNOREALL:
01374
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
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;
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
01458 return;
01459
01460 kDebug(750) << "Death";
01461 QTimer::singleShot( 0, this, SLOT(emitDeath()) );
01462 }
01463
01464
01465
01466
01467 void K3Spell::emitDeath()
01468 {
01469 bool deleteMe = autoDelete;
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
01509
01510
01511
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
01547
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 & )
01560 {
01561
01562
01563 cleanUp();
01564
01565
01566
01567
01568
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
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
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
01686 connect( this, SIGNAL(ready(K3Spell *)), obj, slot);
01687 else
01688
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