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