00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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>
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
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101 #define OUTPUT(x) (connect (proc, SIGNAL (readReady(KProcIO *)), this, SLOT (x(KProcIO *))))
00102
00103
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
00130 static bool determineASpellV6()
00131 {
00132 QString result;
00133 FILE *fs = popen("aspell -v", "r");
00134 if (fs)
00135 {
00136
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
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
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
00200
00201
00202
00203 *proc << "-H";
00204 break;
00205 case TeX:
00206
00207 *proc << "-t";
00208 break;
00209 case Nroff:
00210
00211 if ( ksconfig->client() == KS_CLIENT_ISPELL || ksconfig->client() == KS_CLIENT_HUNSPELL )
00212 *proc << "-n";
00213 break;
00214 case Text:
00215 default:
00216
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
00244
00245
00246
00247
00248 if ( ksconfig->client() == KS_CLIENT_HUNSPELL && trystart<1 ) {
00249
00250
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
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
00322 kdError(750) << "charsets ISO-8859-4, -5, -7, -8, -9 and -13 not supported yet" << endl;
00323 break;
00324 case KS_E_LATIN15:
00325 if (ksconfig->client() == KS_CLIENT_ISPELL)
00326 {
00327
00328
00329
00330
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'";
00344 break;
00345 default:
00346 break;
00347 }
00348 }
00349
00350
00351
00352 }
00353 else
00354 *proc << "-a";
00355
00356 if (trystart == 0)
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
00379 }
00380
00381 void KSpell::KSpell2( KProcIO * )
00382
00383 {
00384 QString line;
00385
00386 kdDebug(750) << "KSpell::KSpell2" << endl;
00387
00388 trystart = maxtrystart;
00389
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] != '@' )
00399 {
00400 QTimer::singleShot( 0, this, SLOT(emitDeath()) );
00401 return;
00402 }
00403
00404
00405 if ( !ignore("kde") )
00406 {
00407 kdDebug(750) << "@KDE was false" << endl;
00408 QTimer::singleShot( 0, this, SLOT(emitDeath()) );
00409 return;
00410 }
00411
00412
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
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
00455 if ( qs.find(' ') != -1 || qs.isEmpty() )
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
00474 if ( qs.find (' ') != -1 || qs.isEmpty() )
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
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
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
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) {
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() ) {
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);
00563
00564 OUTPUT(checkWord2);
00565
00566
00567 proc->writeStdin( "%" );
00568 proc->writeStdin( buffer );
00569
00570 return true;
00571 }
00572
00573 bool KSpell::checkWord( const QString & buffer, bool _usedialog, bool suggest )
00574 {
00575 if (d->checking) {
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() ) {
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);
00607
00608 OUTPUT(checkWord2);
00609
00610
00611 proc->writeStdin( "%" );
00612 proc->writeStdin( buffer );
00613
00614 return true;
00615 }
00616
00617 void KSpell::checkWord2( KProcIO* )
00618 {
00619 QString word;
00620 QString line;
00621 proc->readln( line, true );
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632 QString blank_line;
00633 while (proc->readln( blank_line, true ) != -1);
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
00650
00651 emit corrected( word, word, 0L );
00652 d->checkNextTimer->start( 0, true );
00653 }
00654
00655 void KSpell::checkNext()
00656 {
00657
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 );
00675
00676
00677
00678
00679 QString blank_line;
00680 proc->readln( blank_line, true );
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
00702
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;
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
00740
00741
00742
00743
00744
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
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
00777
00778
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 )
00795 {
00796 if ( word == *it )
00797 {
00798 ++it;
00799 word = *it;
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
00843 {
00844 wordlist=_wordlist;
00845 if ((totalpos=wordlist->count())==0)
00846 return false;
00847 wlIt = wordlist->begin();
00848 usedialog=_usedialog;
00849
00850
00851 setUpDialog();
00852
00853
00854 dialog3slot = SLOT (checkList4 ());
00855
00856 proc->writeStdin ("%");
00857
00858
00859 lastpos = -1;
00860 checkList2();
00861
00862
00863 OUTPUT(checkList3a);
00864
00865 return true;
00866 }
00867
00868 void KSpell::checkList2 ()
00869
00870
00871 {
00872
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
00884
00885
00886 if (!put) {
00887 checkList2();
00888 }
00889 }
00890 else
00891
00892 {
00893 NOOUTPUT(checkList3a);
00894 ksdlg->hide();
00895 emit done(true);
00896 }
00897 }
00898
00899 void KSpell::checkList3a (KProcIO *)
00900
00901 {
00902
00903
00904
00905
00906 if ( dlgon ) {
00907
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 );
00919
00920
00921
00922
00923 if ( tempe == 0 ) {
00924 d->endOfResponse = true;
00925
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
00938 emit corrected( old, *(--wlIt), lastpos ); ++wlIt;
00939 }
00940 else if( usedialog )
00941 {
00942 cwword = word;
00943 dlgon = true;
00944
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 ();
00957
00958
00959 } while (tempe > 0);
00960
00961
00962
00963
00964
00965 if (d->endOfResponse && !dlgon) {
00966
00967 checkList2();
00968 }
00969 }
00970
00971 void KSpell::checkListReplaceCurrent()
00972 {
00973
00974
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
00983
00984 }
00985
00986 void KSpell::checkList4 ()
00987
00988 {
00989 dlgon=false;
00990 QString old;
00991
00992 disconnect (this, SIGNAL (dialog3()), this, SLOT (checkList4()));
00993
00994
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
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
01019
01020
01021
01022
01023
01024
01025 return;
01026 };
01027
01028
01029 if (!d->endOfResponse) {
01030
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
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
01054
01055 if ( !origbuffer.endsWith("\n\n" ) )
01056 {
01057 if (origbuffer.at(origbuffer.length()-1)!='\n')
01058 {
01059 origbuffer+='\n';
01060 origbuffer+='\n';
01061 }
01062 else
01063 origbuffer+='\n';
01064 }
01065
01066 newbuffer = origbuffer;
01067
01068
01069 OUTPUT( check2 );
01070 proc->writeStdin( "!" );
01071
01072
01073 offset = lastlastline = lastpos = lastline = 0;
01074
01075 emitProgress();
01076
01077
01078 int i = origbuffer.find( '\n', 0 ) + 1;
01079 qs = origbuffer.mid( 0, i );
01080 cleanFputs( qs, false );
01081
01082 lastline=i;
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
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 );
01112
01113
01114 if ( tempe>0 )
01115 {
01116 if ( ( e=parseOneResponse (line, word, sugg) )==MISTAKE ||
01117 e==REPLACE)
01118 {
01119 dlgresult=-1;
01120
01121
01122 if ((ksconfig->encoding() == KS_E_UTF8) && !d->aspellV6) {
01123
01124
01125
01126
01127
01128
01129 posinline = (QString::fromUtf8(
01130 origbuffer.mid(lastlastline,lastline-lastlastline).utf8(),
01131 posinline)).length();
01132
01133 }
01134
01135 lastpos = posinline+lastlastline+offset;
01136
01137
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
01147 {
01148 cwword = word;
01149
01150 if ( usedialog ) {
01151
01152 dialog( word, sugg, SLOT(check3()) );
01153 } else {
01154
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();
01168
01169 } while( tempe>0 );
01170
01171 if ( tempe == -1 ) {
01172
01173
01174 NOOUTPUT( check2 );
01175 proc->enableReadSignals(true);
01176 OUTPUT( check2 );
01177 recursive = false;
01178 return;
01179 }
01180
01181 proc->ackRead();
01182
01183
01184 if ( (unsigned int)lastline < origbuffer.length() )
01185 {
01186 int i;
01187 QString qs;
01188
01189
01190
01191 lastpos = (lastlastline=lastline) + offset;
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
01201 {
01202 ksdlg->hide();
01203
01204 newbuffer.truncate( newbuffer.length()-2 );
01205 emitProgress();
01206 emit done( newbuffer );
01207 }
01208 recursive = false;
01209 }
01210
01211 void KSpell::check3 ()
01212
01213 {
01214 disconnect (this, SIGNAL (dialog3()), this, SLOT (check3()));
01215 kdDebug(750) << "check3 [" << cwword << "] [" << replacement() << "] " << dlgresult << endl;
01216
01217
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
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
01237 return;
01238 case KS_STOP:
01239 ksdlg->hide();
01240
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
01278
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("<") );
01284 context.replace( '>', QString::fromLatin1(">") );
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
01308 switch ( dlgresult )
01309 {
01310 case KS_IGNORE:
01311 emit ignoreword( dlgorigword );
01312 break;
01313 case KS_IGNOREALL:
01314
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
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;
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
01393 return;
01394
01395 kdDebug(750) << "Death" << endl;
01396 QTimer::singleShot( 0, this, SLOT(emitDeath()) );
01397 }
01398
01399
01400
01401
01402 void KSpell::emitDeath()
01403 {
01404 bool deleteMe = autoDelete;
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
01444
01445
01446
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
01482
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 & )
01495 {
01496
01497
01498 cleanUp();
01499
01500
01501
01502
01503
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
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
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
01621 connect( this, SIGNAL(ready(KSpell *)), obj, slot);
01622 else
01623
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