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