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 proc->ackRead();
01098
01099
01100 if ( tempe == -1 ) {
01101 recursive = false;
01102 return;
01103 }
01104
01105
01106 if ( (unsigned int)lastline < origbuffer.length() )
01107 {
01108 int i;
01109 QString qs;
01110
01111
01112
01113 lastpos = (lastlastline=lastline) + offset;
01114 i = origbuffer.find('\n', lastline) + 1;
01115 qs = origbuffer.mid( lastline, i-lastline );
01116 cleanFputs( qs, false );
01117 lastline = i;
01118 recursive = false;
01119 return;
01120 }
01121 else
01122
01123 {
01124 ksdlg->hide();
01125
01126 newbuffer.truncate( newbuffer.length()-2 );
01127 emitProgress();
01128 emit done( newbuffer );
01129 }
01130 recursive = false;
01131 }
01132
01133 void KSpell::check3 ()
01134
01135 {
01136 disconnect (this, SIGNAL (dialog3()), this, SLOT (check3()));
01137 kdDebug(750) << "check3 [" << cwword << "] [" << replacement() << "] " << dlgresult << endl;
01138
01139
01140 switch (dlgresult)
01141 {
01142 case KS_REPLACE:
01143 case KS_REPLACEALL:
01144 offset+=replacement().length()-cwword.length();
01145 newbuffer.replace (lastpos, cwword.length(),
01146 replacement());
01147 emit corrected (dlgorigword, replacement(), lastpos);
01148 break;
01149 case KS_CANCEL:
01150
01151 ksdlg->hide();
01152 emit done( origbuffer );
01153 return;
01154 case KS_CONFIG:
01155 ksdlg->hide();
01156 emit done( origbuffer );
01157 KMessageBox::information( 0, i18n("You have to restart the dialog for changes to take effect") );
01158
01159 return;
01160 case KS_STOP:
01161 ksdlg->hide();
01162
01163 emitProgress();
01164 emit done (newbuffer);
01165 return;
01166 };
01167
01168 proc->ackRead();
01169 }
01170
01171 void
01172 KSpell::slotStopCancel (int result)
01173 {
01174 if (dialogwillprocess)
01175 return;
01176
01177 kdDebug(750) << "KSpell::slotStopCancel [" << result << "]" << endl;
01178
01179 if (result==KS_STOP || result==KS_CANCEL)
01180 if (!dialog3slot.isEmpty())
01181 {
01182 dlgresult=result;
01183 connect (this, SIGNAL (dialog3()), this, dialog3slot.ascii());
01184 emit dialog3();
01185 }
01186 }
01187
01188
01189 void KSpell::dialog( const QString & word, QStringList & sugg, const char *_slot )
01190 {
01191 dlgorigword = word;
01192
01193 dialog3slot = _slot;
01194 dialogwillprocess = true;
01195 connect( ksdlg, SIGNAL(command(int)), this, SLOT(dialog2(int)) );
01196 QString tmpBuf = newbuffer;
01197 kdDebug(750)<<" position = "<<lastpos<<endl;
01198
01199
01200
01201 QString marker( "_MARKER_" );
01202 tmpBuf.replace( lastpos, word.length(), marker );
01203 QString context = tmpBuf.mid(QMAX(lastpos-18,0), 2*18+marker.length());
01204 context.replace( '\n',QString::fromLatin1(" "));
01205 context.replace( '<', QString::fromLatin1("<") );
01206 context.replace( '>', QString::fromLatin1(">") );
01207 context.replace( marker, QString::fromLatin1("<b>%1</b>").arg( word ) );
01208 context = "<qt>" + context + "</qt>";
01209
01210 ksdlg->init( word, &sugg, context );
01211 d->m_bNoMisspellingsEncountered = false;
01212 emit misspelling( word, sugg, lastpos );
01213
01214 emitProgress();
01215 ksdlg->show();
01216 }
01217
01218 void KSpell::dialog2( int result )
01219 {
01220 QString qs;
01221
01222 disconnect( ksdlg, SIGNAL(command(int)), this, SLOT(dialog2(int)) );
01223 dialogwillprocess = false;
01224 dlgresult = result;
01225 ksdlg->standby();
01226
01227 dlgreplacement = ksdlg->replacement();
01228
01229
01230 switch ( dlgresult )
01231 {
01232 case KS_IGNORE:
01233 emit ignoreword( dlgorigword );
01234 break;
01235 case KS_IGNOREALL:
01236
01237 ignorelist.prepend( dlgorigword.lower() );
01238 emit ignoreall( dlgorigword );
01239 break;
01240 case KS_ADD:
01241 addPersonal( dlgorigword );
01242 personaldict = true;
01243 emit addword( dlgorigword );
01244
01245 ignorelist.prepend( dlgorigword.lower() );
01246 break;
01247 case KS_REPLACEALL:
01248 {
01249 replacelist.append( dlgorigword );
01250 QString _replacement = replacement();
01251 replacelist.append( _replacement );
01252 emit replaceall( dlgorigword , _replacement );
01253 }
01254 break;
01255 case KS_SUGGEST:
01256 checkWord( ksdlg->replacement(), false, true );
01257 return;
01258 break;
01259 }
01260
01261 connect( this, SIGNAL(dialog3()), this, dialog3slot.ascii() );
01262 emit dialog3();
01263 }
01264
01265
01266 KSpell::~KSpell()
01267 {
01268 delete proc;
01269 delete ksconfig;
01270 delete ksdlg;
01271 delete d->checkNextTimer;
01272 delete d;
01273 }
01274
01275
01276 KSpellConfig KSpell::ksConfig() const
01277 {
01278 ksconfig->setIgnoreList(ignorelist);
01279 ksconfig->setReplaceAllList(replacelist);
01280 return *ksconfig;
01281 }
01282
01283 void KSpell::cleanUp()
01284 {
01285 if ( m_status == Cleaning )
01286 return;
01287
01288 if ( m_status == Running )
01289 {
01290 if ( personaldict )
01291 writePersonalDictionary();
01292 m_status = Cleaning;
01293 }
01294 proc->closeStdin();
01295 }
01296
01297 void KSpell::ispellExit( KProcess* )
01298 {
01299 kdDebug() << "KSpell::ispellExit() " << m_status << endl;
01300
01301 if ( (m_status == Starting) && (trystart < maxtrystart) )
01302 {
01303 trystart++;
01304 startIspell();
01305 return;
01306 }
01307
01308 if ( m_status == Starting )
01309 m_status = Error;
01310 else if (m_status == Cleaning)
01311 m_status = d->m_bNoMisspellingsEncountered ? FinishedNoMisspellingsEncountered : Finished;
01312 else if ( m_status == Running )
01313 m_status = Crashed;
01314 else
01315 return;
01316
01317 kdDebug(750) << "Death" << endl;
01318 QTimer::singleShot( 0, this, SLOT(emitDeath()) );
01319 }
01320
01321
01322
01323
01324 void KSpell::emitDeath()
01325 {
01326 bool deleteMe = autoDelete;
01327 emit death();
01328 if ( deleteMe )
01329 deleteLater();
01330 }
01331
01332 void KSpell::setProgressResolution (unsigned int res)
01333 {
01334 progres=res;
01335 }
01336
01337 void KSpell::emitProgress ()
01338 {
01339 uint nextprog = (uint) (100.*lastpos/(double)totalpos);
01340
01341 if ( nextprog >= curprog )
01342 {
01343 curprog = nextprog;
01344 emit progress( curprog );
01345 }
01346 }
01347
01348 void KSpell::moveDlg( int x, int y )
01349 {
01350 QPoint pt( x,y ), pt2;
01351 pt2 = parent->mapToGlobal( pt );
01352 ksdlg->move( pt2.x(),pt2.y() );
01353 }
01354
01355 void KSpell::setIgnoreUpperWords(bool _ignore)
01356 {
01357 d->m_bIgnoreUpperWords=_ignore;
01358 }
01359
01360 void KSpell::setIgnoreTitleCase(bool _ignore)
01361 {
01362 d->m_bIgnoreTitleCase=_ignore;
01363 }
01364
01365
01366
01367
01368
01369
01370
01371 int
01372 KSpell::modalCheck( QString& text )
01373 {
01374 return modalCheck( text,0 );
01375 }
01376
01377 int
01378 KSpell::modalCheck( QString& text, KSpellConfig* _kcs )
01379 {
01380 modalreturn = 0;
01381 modaltext = text;
01382
01383 KSpell* spell = new KSpell( 0L, i18n("Spell Checker"), 0 ,
01384 0, _kcs, true, true );
01385
01386 while (spell->status()!=Finished)
01387 kapp->processEvents();
01388
01389 text = modaltext;
01390
01391 delete spell;
01392 return modalreturn;
01393 }
01394
01395 void KSpell::slotSpellCheckerCorrected( const QString & oldText, const QString & newText, unsigned int pos )
01396 {
01397 modaltext=modaltext.replace(pos,oldText.length(),newText);
01398 }
01399
01400
01401 void KSpell::slotModalReady()
01402 {
01403
01404
01405
01406 Q_ASSERT( m_status == Running );
01407 connect( this, SIGNAL( done( const QString & ) ),
01408 this, SLOT( slotModalDone( const QString & ) ) );
01409 QObject::connect( this, SIGNAL( corrected( const QString&, const QString&, unsigned int ) ),
01410 this, SLOT( slotSpellCheckerCorrected( const QString&, const QString &, unsigned int ) ) );
01411 QObject::connect( this, SIGNAL( death() ),
01412 this, SLOT( slotModalSpellCheckerFinished( ) ) );
01413 check( modaltext );
01414 }
01415
01416 void KSpell::slotModalDone( const QString & )
01417 {
01418
01419
01420 cleanUp();
01421
01422
01423
01424
01425
01426 slotModalSpellCheckerFinished();
01427 }
01428
01429 void KSpell::slotModalSpellCheckerFinished( )
01430 {
01431 modalreturn=(int)this->status();
01432 }
01433
01434 void KSpell::initialize( QWidget *_parent, const QString &_caption,
01435 QObject *obj, const char *slot, KSpellConfig *_ksc,
01436 bool _progressbar, bool _modal, SpellerType type )
01437 {
01438 d = new KSpellPrivate;
01439
01440 d->m_bIgnoreUpperWords =false;
01441 d->m_bIgnoreTitleCase =false;
01442 d->m_bNoMisspellingsEncountered = true;
01443 d->type = type;
01444 d->checking = false;
01445 d->aspellV6 = false;
01446 d->checkNextTimer = new QTimer( this );
01447 connect( d->checkNextTimer, SIGNAL( timeout() ),
01448 this, SLOT( checkNext() ));
01449 autoDelete = false;
01450 modaldlg = _modal;
01451 progressbar = _progressbar;
01452
01453 proc = 0;
01454 ksconfig = 0;
01455 ksdlg = 0;
01456 lastpos = 0;
01457
01458
01459 if ( _ksc )
01460 ksconfig = new KSpellConfig( *_ksc );
01461 else
01462 ksconfig = new KSpellConfig;
01463
01464 codec = 0;
01465 switch ( ksconfig->encoding() )
01466 {
01467 case KS_E_LATIN1:
01468 codec = QTextCodec::codecForName("ISO 8859-1");
01469 break;
01470 case KS_E_LATIN2:
01471 codec = QTextCodec::codecForName("ISO 8859-2");
01472 break;
01473 case KS_E_LATIN3:
01474 codec = QTextCodec::codecForName("ISO 8859-3");
01475 break;
01476 case KS_E_LATIN4:
01477 codec = QTextCodec::codecForName("ISO 8859-4");
01478 break;
01479 case KS_E_LATIN5:
01480 codec = QTextCodec::codecForName("ISO 8859-5");
01481 break;
01482 case KS_E_LATIN7:
01483 codec = QTextCodec::codecForName("ISO 8859-7");
01484 break;
01485 case KS_E_LATIN8:
01486 codec = QTextCodec::codecForName("ISO 8859-8-i");
01487 break;
01488 case KS_E_LATIN9:
01489 codec = QTextCodec::codecForName("ISO 8859-9");
01490 break;
01491 case KS_E_LATIN13:
01492 codec = QTextCodec::codecForName("ISO 8859-13");
01493 break;
01494 case KS_E_LATIN15:
01495 codec = QTextCodec::codecForName("ISO 8859-15");
01496 break;
01497 case KS_E_UTF8:
01498 codec = QTextCodec::codecForName("UTF-8");
01499 break;
01500 case KS_E_KOI8R:
01501 codec = QTextCodec::codecForName("KOI8-R");
01502 break;
01503 case KS_E_KOI8U:
01504 codec = QTextCodec::codecForName("KOI8-U");
01505 break;
01506 case KS_E_CP1251:
01507 codec = QTextCodec::codecForName("CP1251");
01508 break;
01509 case KS_E_CP1255:
01510 codec = QTextCodec::codecForName("CP1255");
01511 break;
01512 default:
01513 break;
01514 }
01515
01516 kdDebug(750) << __FILE__ << ":" << __LINE__ << " Codec = " << (codec ? codec->name() : "<default>") << endl;
01517
01518
01519 ignorelist += ksconfig->ignoreList();
01520
01521 replacelist += ksconfig->replaceAllList();
01522 texmode=dlgon=false;
01523 m_status = Starting;
01524 dialogsetup = false;
01525 progres=10;
01526 curprog=0;
01527
01528 dialogwillprocess = false;
01529 dialog3slot = QString::null;
01530
01531 personaldict = false;
01532 dlgresult = -1;
01533
01534 caption = _caption;
01535
01536 parent = _parent;
01537
01538 trystart = 0;
01539 maxtrystart = 2;
01540
01541 if ( obj && slot )
01542
01543 connect( this, SIGNAL(ready(KSpell *)), obj, slot);
01544 else
01545
01546 connect( this, SIGNAL(ready(KSpell *)), this, SLOT(slotModalReady()) );
01547
01548 proc = new KProcIO( codec );
01549
01550 startIspell();
01551 }
01552
01553 QString KSpell::modaltext;
01554 int KSpell::modalreturn = 0;
01555 QWidget* KSpell::modalWidgetHack = 0;
01556
01557 #include "kspell.moc"
01558