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