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