00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #ifdef HAVE_CONFIG_H
00020 #include <config.h>
00021 #endif
00022
00023 #include <stdio.h>
00024 #include <sys/time.h>
00025 #include <sys/types.h>
00026 #include <unistd.h>
00027 #include <ctype.h>
00028 #include <stdlib.h>
00029
00030 #ifdef HAVE_STRINGS_H
00031 #include <strings.h>
00032 #endif
00033
00034 #include <qtextcodec.h>
00035 #include <qtimer.h>
00036 #include <kapplication.h>
00037 #include <kdebug.h>
00038 #include <klocale.h>
00039
00040 #include "koSpell.h"
00041 #include "koSpelldlg.h"
00042 #include "koispell.moc"
00043 #include "koispell.h"
00044 #include "koSconfig.h"
00045
00046 #include <kwin.h>
00047 #include <kprocio.h>
00048
00049 #define MAXLINELENGTH 10000
00050
00051 enum {
00052 GOOD= 0,
00053 IGNORE= 1,
00054 REPLACE= 2,
00055 MISTAKE= 3
00056 };
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073 #define OUTPUT(x) (connect (proc, SIGNAL (readReady(KProcIO *)), this, SLOT (x(KProcIO *))))
00074
00075
00076 #define NOOUTPUT(x) (disconnect (proc, SIGNAL (readReady(KProcIO *)), this, SLOT (x(KProcIO *))))
00077
00078
00079
00080
00081
00082 KOISpell::KOISpell( QWidget *_parent, const QString &_caption,
00083 QObject *obj, const char *slot, KOSpellConfig *_ksc,
00084 bool _progressbar, bool _modal, KOSpellerType _type )
00085 :KOSpell(_parent,_caption,_ksc,_modal,false, _type)
00086 {
00087 initialize( _parent, _caption, obj, slot, _ksc,
00088 _progressbar, _modal );
00089 }
00090
00091 void KOISpell::startIspell()
00092
00093 {
00094
00095 kdDebug(30006) << "Try #" << trystart << endl;
00096 if (trystart>0)
00097 proc->resetAll();
00098
00099 switch (ksconfig->client())
00100 {
00101 case KOS_CLIENT_ISPELL:
00102 *proc << "ispell";
00103 kdDebug(30006) << "Using ispell" << endl;
00104 break;
00105 case KOS_CLIENT_ASPELL:
00106
00107 *proc << "aspell";
00108 kdDebug(30006) << "Using aspell" << endl;
00109 break;
00110 case KOS_CLIENT_HSPELL:
00111 *proc << "hspell";
00112 kdDebug(30006) << "Using hspell" << endl;
00113 break;
00114 default:
00115 kdError(30006) << "Spelling configuration error, client=" << ksconfig->client() <<endl;
00116 }
00117
00118 if (ksconfig->client() == KOS_CLIENT_ISPELL || ksconfig->client() == KOS_CLIENT_ASPELL)
00119 {
00120
00121
00122 *proc << "-a" << "-S";
00123 switch ( type )
00124 {
00125 case HTML:
00126
00127
00128
00129
00130 *proc << "-H";
00131 break;
00132 case TeX:
00133
00134 *proc << "-t";
00135 break;
00136 case Nroff:
00137
00138 if ( ksconfig->client() == KOS_CLIENT_ISPELL )
00139 *proc << "-n";
00140 break;
00141 case Text:
00142 default:
00143
00144 break;
00145 }
00146
00147 if (ksconfig->noRootAffix())
00148 {
00149 *proc<<"-m";
00150 }
00151 if (ksconfig->runTogether())
00152 {
00153 *proc << "-B";
00154 }
00155 else
00156 {
00157 *proc << "-C";
00158 }
00159
00160 if (trystart<2)
00161 {
00162 if (! ksconfig->dictionary().isEmpty())
00163 {
00164 kdDebug(30006) << "using dictionary [" << ksconfig->dictionary() << "]" << endl;
00165 *proc << "-d";
00166 *proc << ksconfig->dictionary();
00167 }
00168 }
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184 if (trystart<1)
00185 switch (ksconfig->encoding())
00186 {
00187 case KOS_E_LATIN1:
00188 *proc << "-Tlatin1";
00189 break;
00190 case KOS_E_LATIN2:
00191 *proc << "-Tlatin2";
00192 break;
00193 case KOS_E_LATIN3:
00194 *proc << "-Tlatin3";
00195 break;
00196 case KOS_E_LATIN15:
00197
00198
00199
00200
00201
00202 *proc << "-Tlatin1";
00203 break;
00204
00205
00206 case KOS_E_LATIN4:
00207 case KOS_E_LATIN5:
00208 case KOS_E_LATIN7:
00209 case KOS_E_LATIN8:
00210 case KOS_E_LATIN9:
00211 case KOS_E_LATIN13:
00212
00213
00214 kdError(30006) << "charsets iso-8859-4 .. iso-8859-13 not supported yet" << endl;
00215 break;
00216
00217 case KOS_E_UTF8:
00218 *proc << "-Tutf8";
00219 break;
00220
00221 case KOS_E_KOI8U:
00222 *proc << "-w'";
00223 break;
00224
00225 }
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235 }
00236 else
00237 *proc << "-a";
00238
00239 if (trystart==0)
00240 {
00241 connect (proc, SIGNAL ( receivedStderr (KProcess *, char *, int)),
00242 this, SLOT (ispellErrors (KProcess *, char *, int)));
00243
00244
00245 connect(proc, SIGNAL(processExited(KProcess *)),
00246 this, SLOT (ispellExit (KProcess *)));
00247
00248 OUTPUT(KSpell2);
00249 }
00250
00251 if ( proc->start() == false )
00252 {
00253 m_status = Error;
00254 QTimer::singleShot( 0, this, SLOT(emitDeath()));
00255 }
00256 }
00257
00258 QStringList KOISpell::resultCheckWord( const QString &_word )
00259 {
00260 disconnect();
00261 checkWord (_word, false, true);
00262 QStringList sug = suggestions();
00263 return sug;
00264 }
00265
00266
00267 void KOISpell::ispellErrors (KProcess *, char *buffer, int buflen)
00268 {
00269 buffer [buflen-1] = '\0';
00270
00271 }
00272
00273 void KOISpell::KSpell2 (KProcIO *)
00274
00275 {
00276 kdDebug(30006) << "KSpell::KSpell2" << endl;
00277 trystart=maxtrystart;
00278
00279 QString line;
00280
00281 if (proc->fgets (line, true)==-1)
00282 {
00283 QTimer::singleShot( 0, this, SLOT(emitDeath()));
00284 return;
00285 }
00286
00287
00288 if (line[0]!='@')
00289 {
00290 QTimer::singleShot( 0, this, SLOT(emitDeath()));
00291 return;
00292 }
00293
00294
00295 if (ignore ("kde")==false)
00296 {
00297 kdDebug(30006) << "@KDE was false" << endl;
00298 QTimer::singleShot( 0, this, SLOT(emitDeath()));
00299 return;
00300 }
00301
00302
00303 if (ignore ("linux")==false)
00304 {
00305 kdDebug(30006) << "@Linux was false" << endl;
00306 QTimer::singleShot( 0, this, SLOT(emitDeath()));
00307 return;
00308 }
00309
00310 NOOUTPUT (KSpell2);
00311
00312 m_status = Running;
00313 m_ready = true;
00314 emit ready(this);
00315 }
00316
00317 void
00318 KOISpell::setUpDialog (bool reallyuseprogressbar)
00319 {
00320 if (dialogsetup)
00321 return;
00322
00323
00324 ksdlg=new KOSpellDlg (parent, ksconfig, "dialog",
00325 progressbar && reallyuseprogressbar, modaldlg );
00326 ksdlg->setCaption (caption);
00327 connect (ksdlg, SIGNAL (command (int)), this,
00328 SLOT (slotStopCancel (int)) );
00331 #ifdef Q_WS_X11 // FIXME(E): Implement for Qt/Embedded
00332 KWin::setIcons (ksdlg->winId(), kapp->icon(), kapp->miniIcon());
00333 #endif
00334 if ( modaldlg )
00335 ksdlg->setFocus();
00336 dialogsetup = true;
00337 }
00338
00339 bool KOISpell::addPersonal (const QString & word)
00340 {
00341 QString qs = word.simplifyWhiteSpace();
00342
00343
00344 if (qs.find (' ')!=-1 || qs.isEmpty())
00345 return false;
00346
00347 qs.prepend ("*");
00348 personaldict=true;
00349
00350 return proc->fputs(qs);
00351 }
00352
00353 bool KOISpell::writePersonalDictionary ()
00354 {
00355 return proc->fputs ("#");
00356 }
00357
00358 bool KOISpell::ignore (const QString & word)
00359 {
00360 QString qs = word.simplifyWhiteSpace();
00361
00362
00363 if (qs.find (' ')!=-1 || qs.isEmpty())
00364 return false;
00365
00366 qs.prepend ("@");
return proc->fputs(qs);
}
bool
KOISpell::cleanFputsWord (const QString & s, bool appendCR)
{
QString qs(s);
//bool firstchar = true;
bool empty = true;
for (unsigned int i=0; i<qs.length(); i++)
{
//we need some punctuation for ornaments
if (qs[i] != '\'' && qs[i] != '\"' && qs[i] != '-'
00367 && qs[i].isPunct() || qs[i].isSpace())
00368 {
00369 qs.remove(i,1);
00370 i--;
00371 } else {
00372 if (qs[i].isLetter()) empty=false;
00373 }
00374 }
00375
00376 // don't check empty words, otherwise synchronisation will lost
00377 if (empty) return false;
00378
00379 return proc->fputs("^"+qs, appendCR);
00380 }
00381
00382 bool
00383 KOISpell::cleanFputs (const QString & s, bool appendCR)
00384 {
00385 QString qs(s);
00386 unsigned l = qs.length();
00387
00388 // some uses of '$' (e.g. "$0") cause ispell to skip all following text
00389 for(unsigned int i = 0; i < l; ++i)
00390 {
00391 if(qs[i] == '$')
00392 qs[i] = ' ';
00393 }
00394
00395 if (l<MAXLINELENGTH)
00396 {
00397 if (qs.isEmpty())
00398 qs="";
00399
00400 return proc->fputs ("^"+qs, appendCR);
00401 }
00402 else
00403 return proc->fputs ("^\n",appendCR);
00404 }
00405
00406 bool KOISpell::checkWord (const QString & buffer, bool _usedialog)
00407 {
00408 QString qs = buffer.simplifyWhiteSpace();
00409 if (qs.find (' ')!=-1 || qs.isEmpty()) // make sure it's a _word_
00410 return false;
00411
00413 dialog3slot = SLOT(checkWord3());
00414
00415 usedialog=_usedialog;
00416 setUpDialog(false);
00417 if (_usedialog)
00418 {
00419 emitProgress();
00420 ksdlg->show();
00421 }
00422 else
00423 ksdlg->hide();
00424
00425 OUTPUT (checkWord2);
00426 // connect (this, SIGNAL (dialog3()), this, SLOT (checkWord3()));
00427
00428 proc->fputs ("%"); // turn off terse mode
00429 cleanFputsWord( qs ); // send the word to ispell
00430
00431 return true;
00432 }
00433
00434 //it can't use dialog anyway
00435 bool KOISpell::checkWord (const QString & buffer, bool _usedialog, bool synchronous )
00436 {
00437 QString qs = buffer.simplifyWhiteSpace();
00438 if (qs.find (' ')!=-1 || qs.isEmpty()) // make sure it's a _word_
00439 return false;
00440
00442 dialog3slot = SLOT(checkWord3());
00443
00444 usedialog=_usedialog;
00445 setUpDialog(false);
00446
00447 ksdlg->hide();
00448 if ( synchronous ) {
00449 //ready signal is never called, after initialize
00450 if ( !m_ready ) {
00451 connect( this, SIGNAL(ready(KOSpell*)),
00452 this, SLOT(slotSynchronousReady()) );
00453 connect( this, SIGNAL(death()), // in case of init failure
00454 this, SLOT(slotSynchronousReady()) );
00455 //MAGIC 1: here we wait for the initialization to finish
00456 enter_loop();
00457 disconnect( this, SIGNAL(ready(KOSpell*)),
00458 this, SLOT(slotSynchronousReady()) );
00459 disconnect( this, SIGNAL(death()), // in case of init failure
00460 this, SLOT(slotSynchronousReady()) );
00461 }
00462 if ( m_status == Error ) // init failure
00463 return false;
00464 OUTPUT (checkWord2Synchronous);
00465 }
00466 else
00467 OUTPUT (checkWord2);
00468
00469 proc->fputs ("%"); // turn off terse mode
00470
00471 if (cleanFputsWord( qs )) // send the word to ispell
00472 enter_loop(); //MAGIC 2: and here we wait for the results
00473
00474 return true;
00475 }
00476
00477 void KOISpell::checkWord2 (KProcIO *)
00478 {
00479 QString word;
00480
00481 QString line;
00482 proc->fgets (line, true); //get ispell's response
00483
00484 /* ispell man page: "Each sentence of text input is terminated with an
00485 additional blank line, indicating that ispell has completed processing
00486 the input line." */
00487 QString blank_line;
00488 proc->fgets(blank_line, true); // eat the blank line
00489
00490 NOOUTPUT(checkWord2);
00491
00492 bool mistake = (parseOneResponse(line, word, sugg) == MISTAKE);
00493 if ( mistake && usedialog )
00494 {
00495 cwword=word;
00496 dialog (word, sugg, SLOT (checkWord3()));
00497 return;
00498 }
00499 else if( mistake )
00500 {
00501 misspellingWord (word, sugg, lastpos);
00502 }
00503
00504 //emits a "corrected" signal _even_ if no change was made
00505 //so that the calling program knows when the check is complete
00506 emit corrected (word, word, 0L);
00507 }
00508
00509 // This is not even cute... Just watch me abuse
00510 // Qt, KDE, candy, cookies and make this stuff work
00511 // through pure magic
00512 void KOISpell::checkWord2Synchronous (KProcIO *)
00513 {
00514 QString word;
00515
00516 QString line;
00517 proc->fgets (line, true); //get ispell's response
00518
00519 /* ispell man page: "Each sentence of text input is terminated with an
00520 additional blank line, indicating that ispell has completed processing
00521 the input line." */
00522 QString blank_line;
00523 proc->fgets(blank_line, true); // eat the blank line
00524
00525 NOOUTPUT(checkWord2);
00526
00527 bool mistake = (parseOneResponse(line, word, sugg) == MISTAKE);
00528 if( mistake )
00529 {
00530 misspellingWord (word, sugg, lastpos);
00531 }
00532 //emits a "corrected" signal _even_ if no change was made
00533 //so that the calling program knows when the check is complete
00534 emit corrected (word, word, 0L);
00535 qApp->exit_loop();
00536 }
00537
00538 void KOISpell::checkWord3 ()
00539 {
00540 disconnect (this, SIGNAL (dialog3()), this, SLOT (checkWord3()));
00541
00542 emit corrected (cwword, replacement(), 0L);
00543 }
00544
00545 QString KOISpell::funnyWord (const QString & word)
00546 // composes a guess from ispell to a readable word
00547 // e.g. "re+fry-y+ies" -> "refries"
00548 {
00549 QString qs;
00550 unsigned int i=0;
00551
00552 for (i=0; word [i]!='\0';i++)
00553 {
00554 if (word [i]=='+')
00555 continue;
00556 if (word [i]=='-')
00557 {
00558 QString shorty;
00559 unsigned int j;
00560 int k;
00561
00562 for (j=i+1;word [j]!='\0' && word [j]!='+' &&
00563 word [j]!='-';j++)
00564 shorty+=word [j];
00565 i=j-1;
00566
00567 if ((k=qs.findRev (shorty))==0 || k!=-1)
00568 qs.remove (k,shorty.length());
00569 else
00570 {
00571 qs+='-';
00572 qs+=shorty; //it was a hyphen, not a '-' from ispell
00573 }
00574 }
00575 else
00576 qs+=word [i];
00577 }
00578 return qs;
00579 }
00580
00581
00582 int KOISpell::parseOneResponse (const QString &buffer, QString &word, QStringList & sugg)
00583 // buffer is checked, word and sugg are filled in
00584 // returns
00585 // GOOD if word is fine
00586 // IGNORE if word is in ignorelist
00587 // REPLACE if word is in replacelist
00588 // MISTAKE if word is misspelled
00589 {
00590 word = "";
00591 posinline=0;
00592
00593 sugg.clear();
00594
00595 if (buffer [0]=='*' || buffer[0] == '+' || buffer[0] == '-')
00596 {
00597 return GOOD;
00598 }
00599
00600 if (buffer [0]=='&' || buffer [0]=='?' || buffer [0]=='#')
00601 {
00602 int i,j;
00603
00604
00605 word = buffer.mid (2,buffer.find (' ',3)-2);
00606 //check() needs this
00607 orig=word;
00608
00609 if(m_bIgnoreTitleCase && word==word.upper())
00610 return IGNORE;
00611
00612 if(m_bIgnoreUpperWords && word[0]==word[0].upper())
00613 {
00614 QString text=word[0]+word.right(word.length()-1).lower();
00615 if(text==word)
00616 return IGNORE;
00617 }
00618
00620 //We don't take advantage of ispell's ignore function because
00621 //we can't interrupt ispell's output (when checking a large
00622 //buffer) to add a word to _it's_ ignore-list.
00623 if (ignorelist.findIndex(word.lower())!=-1)
00624 return IGNORE;
00625
00627 QString qs2;
00628
00629 if (buffer.find(':')!=-1)
00630 qs2=buffer.left (buffer.find (':'));
00631 else
00632 qs2=buffer;
00633
00634 posinline = qs2.right( qs2.length()-qs2.findRev(' ') ).toInt()-1;
00635
00637 QStringList::Iterator it = replacelist.begin();
00638 for(;it != replacelist.end(); ++it, ++it) // Skip two entries at a time.
00639 {
00640 if (word == *it) // Word matches
00641 {
00642 ++it;
00643 word = *it; // Replace it with the next entry
00644 return REPLACE;
00645 }
00646 }
00647
00649 if (buffer [0] != '#')
00650 {
00651 QString qs = buffer.mid(buffer.find(':')+2, buffer.length());
00652 qs+=',';
00653 sugg.clear();
00654 i=j=0;
00655 while ((unsigned int)i<qs.length())
00656 {
00657 QString temp = qs.mid (i,(j=qs.find (',',i))-i);
00658 sugg.append (funnyWord (temp));
00659
00660 i=j+2;
00661 }
00662 }
00663
00664 if ((sugg.count()==1) && (sugg.first() == word))
00665 return GOOD;
00666
00667 return MISTAKE;
00668 }
00669
00670
00671 kdError(750) << "HERE?: [" << buffer << "]" << endl;
00672 kdError(750) << "Please report this to dsweet@kde.org" << endl;
00673 kdError(750) << "Thank you!" << endl;
00674 emit done(false);
00675 emit done (KOISpell::origbuffer);
00676 return MISTAKE;
00677 }
00678
00679 bool KOISpell::checkList (QStringList *_wordlist, bool _usedialog)
00680 // prepare check of string list
00681 {
00682 wordlist=_wordlist;
00683 if ((totalpos=wordlist->count())==0)
00684 return false;
00685 wlIt = wordlist->begin();
00686 usedialog=_usedialog;
00687
00688 // prepare the dialog
00689 setUpDialog();
00690
00691 //set the dialog signal handler
00692 dialog3slot = SLOT (checkList4 ());
00693
00694 proc->fputs ("%"); // turn off terse mode & check one word at a time
00695
00696 //lastpos now counts which *word number* we are at in checkListReplaceCurrent()
00697 lastpos = -1;
00698 checkList2();
00699
00700 // when checked, KProcIO calls checkList3a
00701 OUTPUT(checkList3a);
00702
00703 return true;
00704 }
00705
00706 void KOISpell::checkList2 ()
00707 // send one word from the list to KProcIO
00708 // invoked first time by checkList, later by checkListReplaceCurrent and checkList4
00709 {
00710 // send next word
00711 if (wlIt != wordlist->end())
00712 {
00713 kdDebug(30006) << "KS::cklist2 " << lastpos << ": " << *wlIt << endl;
00714
00715 endOfResponse = false;
00716 bool put;
00717 lastpos++; offset=0;
00718 put = cleanFputsWord (*wlIt);
00719 ++wlIt;
00720
00721 // when cleanFPutsWord failed (e.g. on empty word)
00722 // try next word; may be this is not good for other
00723 // problems, because this will make read the list up to the end
00724 if (!put) {
00725 checkList2();
00726 }
00727 }
00728 else
00729 // end of word list
00730 {
00731 NOOUTPUT(checkList3a);
00732 ksdlg->hide();
00733 emit done(true);
00734 }
00735 }
00736
00737 void KOISpell::checkList3a (KProcIO *)
00738 // invoked by KProcIO, when data from ispell are read
00739 {
00740 //kdDebug(30006) << "start of checkList3a" << endl;
00741
00742 // don't read more data, when dialog is waiting
00743 // for user interaction
00744 if (dlgon) {
00745 //kdDebug(30006) << "dlgon: don't read more data" << endl;
00746 return;
00747 }
00748
00749 int e, tempe;
00750
00751 QString word;
00752 QString line;
00753
00754 do
00755 {
00756 tempe=proc->fgets (line, true); //get ispell's response
00757
00758 //kdDebug(30006) << "checkList3a: read bytes [" << tempe << "]" << endl;
00759
00760
00761 if (tempe == 0) {
00762 endOfResponse = true;
00763 //kdDebug(30006) << "checkList3a: end of resp" << endl;
00764 } else if (tempe>0) {
00765 if ((e=parseOneResponse (line, word, sugg))==MISTAKE ||
00766 e==REPLACE)
00767 {
00768 dlgresult=-1;
00769
00770 if (e==REPLACE)
00771 {
00772 QString old = *(--wlIt); ++wlIt;
00773 dlgreplacement=word;
00774 checkListReplaceCurrent();
00775 // inform application
00776 emit corrected (old, *(--wlIt), lastpos); ++wlIt;
00777 }
00778 else if( usedialog )
00779 {
00780 cwword=word;
00781 dlgon=true;
00782 // show the dialog
00783 dialog (word, sugg, SLOT (checkList4()));
00784 return;
00785 }
00786 else
00787 {
00788 misspellingWord (word, sugg, lastpos);
00789 }
00790 }
00791
00792 }
00793 emitProgress (); //maybe
00794
00795 // stop when empty line or no more data
00796 } while (tempe > 0);
00797
00798 //kdDebug(30006) << "checkList3a: exit loop with [" << tempe << "]" << endl;
00799
00800 // if we got an empty line, t.e. end of ispell/aspell response
00801 // and the dialog isn't waiting for user interaction, send next word
00802 if (endOfResponse && !dlgon) {
00803 //kdDebug(30006) << "checkList3a: send next word" << endl;
00804 checkList2();
00805 }
00806 }
00807
00808 void KOISpell::checkListReplaceCurrent () {
00809
00810 // go back to misspelled word
00811 wlIt--;
00812
00813 QString s = *wlIt;
00814 s.replace(posinline+offset,orig.length(),replacement());
00815 offset += replacement().length()-orig.length();
00816 wordlist->insert (wlIt, s);
00817 wlIt = wordlist->remove (wlIt);
00818 // wlIt now points to the word after the repalced one
00819
00820 }
00821
00822 void KOISpell::checkList4 ()
00823 // evaluate dialog return, when a button was pressed there
00824 {
00825 dlgon=false;
00826 QString old;
00827
00828 disconnect (this, SIGNAL (dialog3()), this, SLOT (checkList4()));
00829
00830 //others should have been processed by dialog() already
00831 switch (dlgresult)
00832 {
00833 case KOS_REPLACE:
00834 case KOS_REPLACEALL:
00835 kdDebug(30006) << "KS: cklist4: lastpos: " << lastpos << endl;
00836 old = *(--wlIt); ++wlIt;
00837 // replace word
00838 checkListReplaceCurrent();
00839 emit corrected (old, *(--wlIt), lastpos); ++wlIt;
00840 break;
00841 case KOS_CANCEL:
00842 ksdlg->hide();
00843 emit done (false);
00844 return;
00845 case KOS_STOP:
00846 ksdlg->hide();
00847 emit done (true);
00848 break;
00849 };
00850
00851 // read more if there is more, otherwise send next word
00852 if (!endOfResponse) {
00853 //kdDebug(30006) << "checkList4: read more from response" << endl;
00854 checkList3a(NULL);
00855 }
00856 }
00857
00858 bool KOISpell::check( const QString &_buffer, bool _usedialog )
00859 {
00860 QString qs;
00861
00862 usedialog=_usedialog;
00863 setUpDialog ();
00864 //set the dialog signal handler
00865 dialog3slot = SLOT (check3 ());
00866
00867 kdDebug(30006) << "KS: check" << endl;
00868 origbuffer = _buffer;
00869 if ( ( totalpos = origbuffer.length() ) == 0 )
00870 {
00871 emit done(origbuffer);
00872 return false;
00873 }
00874
00875
00876 // Torben: I corrected the \n\n problem directly in the
00877 // origbuffer since I got errors otherwise
00878 if ( origbuffer.right(2) != "\n\n" )
00879 {
00880 if (origbuffer.at(origbuffer.length()-1)!='\n')
00881 {
00882 origbuffer+='\n';
00883 origbuffer+='\n'; //shouldn't these be removed at some point?
00884 }
00885 else
00886 origbuffer+='\n';
00887 }
00888
00889 newbuffer=origbuffer;
00890
00891 // KProcIO calls check2 when read from ispell
00892 OUTPUT(check2);
00893 proc->fputs ("!");
00894
00895 //lastpos is a position in newbuffer (it has offset in it)
00896 offset=lastlastline=lastpos=lastline=0;
00897
00898 emitProgress ();
00899
00900 // send first buffer line
00901 int i = origbuffer.find('\n', 0)+1;
00902 qs=origbuffer.mid (0,i);
00903 cleanFputs (qs,false);
00904
00905 lastline=i; //the character position, not a line number
00906
00907 if (usedialog)
00908 {
00909 emitProgress();
00910 ksdlg->show();
00911 }
00912 else
00913 ksdlg->hide();
00914
00915 return true;
00916 }
00917
00918 void KOISpell::check2 (KProcIO *)
00919 // invoked by KProcIO when read from ispell
00920 {
00921 int e, tempe;
00922 QString word;
00923 QString line;
00924 static bool recursive = false;
00925 if (recursive &&
00926 (!ksdlg || ksdlg->isHidden()))
00927 {
00928 return;
00929 }
00930 recursive = true;
00931
00932 do
00933 {
00934 tempe=proc->fgets (line); //get ispell's response
00935 kdDebug(30006) << "KSpell::check2 (" << tempe << "b)" << endl;
00936
00937 if (tempe>0)
00938 {
00939 if ((e=parseOneResponse (line, word, sugg))==MISTAKE ||
00940 e==REPLACE)
00941 {
00942 dlgresult=-1;
00943
00944 // for multibyte encoding posinline needs correction
00945 if (ksconfig->encoding() == KOS_E_UTF8) {
00946 // kdDebug(30006) << "line: " << origbuffer.mid(lastlastline,
00947 // lastline-lastlastline) << endl;
00948 // kdDebug(30006) << "posinline uncorr: " << posinline << endl;
00949
00950 // convert line to UTF-8, cut at pos, convert back to UCS-2
00951 // and get string length
00952 posinline = (QString::fromUtf8(
00953 origbuffer.mid(lastlastline,lastline-lastlastline).utf8(),
00954 posinline)).length();
00955 // kdDebug(30006) << "posinline corr: " << posinline << endl;
00956 }
00957
00958 lastpos=posinline+lastlastline+offset;
00959
00960 //orig is set by parseOneResponse()
00961
00962 if (e==REPLACE)
00963 {
00964 dlgreplacement=word;
00965 emit corrected (orig, replacement(), lastpos);
00966 offset+=replacement().length()-orig.length();
00967 newbuffer.replace (lastpos, orig.length(), word);
00968 }
00969 else //MISTAKE
00970 {
00971 cwword=word;
00972 //kdDebug(30006) << "(Before dialog) word=[" << word << "] cwword =[" << cwword << "]\n" << endl;
00973 if ( usedialog ) {
00974 // show the word in the dialog
00975 dialog (word, sugg, SLOT (check3()));
00976 } else {
00977 // No dialog, just emit misspelling and continue
00978 misspellingWord (word, sugg, lastpos);
00979 dlgresult = KOS_IGNORE;
00980 check3();
00981 }
00982 recursive = false;
00983 return;
00984 }
00985 }
00986
00987 }
00988
00989 emitProgress (); //maybe
00990
00991 } while (tempe>0);
00992
00993 proc->ackRead();
00994
00995 if (tempe==-1) {//we were called, but no data seems to be ready...
00996 recursive = false;
00997 return;
00998 }
00999
01000 proc->ackRead();
01001 //If there is more to check, then send another line to ISpell.
01002 if ((unsigned int)lastline<origbuffer.length())
01003 {
01004 int i;
01005 QString qs;
01006
01007 //kdDebug(30006) << "[EOL](" << tempe << ")[" << temp << "]" << endl;
01008
01009 lastpos=(lastlastline=lastline)+offset; //do we really want this?
01010 i=origbuffer.find('\n', lastline)+1;
01011 qs=origbuffer.mid (lastline, i-lastline);
01012 cleanFputs (qs,false);
01013 lastline=i;
01014 recursive = false;
01015 return;
01016 }
01017 else
01018 //This is the end of it all
01019 {
01020 ksdlg->hide();
01021 // kdDebug(30006) << "check2() done" << endl;
01022 newbuffer.truncate (newbuffer.length()-2);
01023 emitProgress();
01024 NOOUTPUT( check2 );
01025 emit done (newbuffer);
01026 }
01027 recursive = false;
01028 }
01029
01030 void KOISpell::check3 ()
01031 // evaluates the return value of the dialog
01032 {
01033 disconnect (this, SIGNAL (dialog3()), this, SLOT (check3()));
01034
01035 kdDebug(30006) << "check3 [" << cwword << "] [" << replacement() << "] " << dlgresult << endl;
01036
01037 //others should have been processed by dialog() already
01038 switch (dlgresult)
01039 {
01040 case KOS_REPLACE:
01041 case KOS_REPLACEALL:
01042 offset+=replacement().length()-cwword.length();
01043 newbuffer.replace (lastpos, cwword.length(),
01044 replacement());
01045 emit corrected (dlgorigword, replacement(), lastpos);
01046 break;
01047 case KOS_CANCEL:
01048 // kdDebug(30006) << "cancelled\n" << endl;
01049 ksdlg->hide();
01050 emit done (origbuffer);
01051 return;
01052 case KOS_STOP:
01053 ksdlg->hide();
01054 //buffer=newbuffer);
01055 emitProgress();
01056 emit done (newbuffer);
01057 return;
01058 };
01059
01060 proc->ackRead();
01061 }
01062
01063 void
01064 KOISpell::slotStopCancel (int result)
01065 {
01066 if (dialogwillprocess)
01067 return;
01068
01069 kdDebug(30006) << "KSpell::slotStopCancel [" << result << "]" << endl;
01070
01071 if (result==KOS_STOP || result==KOS_CANCEL)
01072 if (!dialog3slot.isEmpty())
01073 {
01074 dlgresult=result;
01075 connect (this, SIGNAL (dialog3()), this, dialog3slot.ascii());
01076 emit dialog3();
01077 }
01078 }
01079
01080
01081 void KOISpell::dialog(const QString & word, QStringList & sugg, const char *_slot)
01082 {
01083 dlgorigword=word;
01084
01085 dialog3slot=_slot;
01086 dialogwillprocess=true;
01087 connect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
01088 ksdlg->init (word, &sugg);
01089 misspellingWord (word, sugg, lastpos);
01090
01091 emitProgress();
01092 ksdlg->show();
01093 }
01094
01095 void KOISpell::dialog2 (int result)
01096 {
01097 QString qs;
01098
01099 disconnect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
01100 dialogwillprocess=false;
01101 dlgresult=result;
01102 ksdlg->standby();
01103
01104 dlgreplacement=ksdlg->replacement();
01105
01106 //process result here
01107 switch (dlgresult)
01108 {
01109
01110 case KOS_IGNORE:
01111 emit ignoreword(dlgorigword);
01112 break;
01113 case KOS_IGNOREALL:
01114 // would be better to lower case only words with beginning cap
01115 ignorelist.prepend(dlgorigword.lower());
01116 emit ignoreall (dlgorigword);
01117 break;
01118 case KOS_ADD:
01119 addPersonal (dlgorigword);
01120 personaldict=true;
01121 emit addword (dlgorigword);
01122 // adding to pesonal dict takes effect at the next line, not the current
01123 ignorelist.prepend(dlgorigword.lower());
01124 break;
01125 case KOS_REPLACEALL:
01126 {
01127 replacelist.append (dlgorigword);
01128 QString _replacement = replacement();
01129 replacelist.append (_replacement);
01130 emit replaceall( dlgorigword , _replacement );
01131 }
01132 break;
01133 case KOS_ADDAUTOCORRECT:
01134 {
01135 //todo add new word ????
01136 QString _replacement = replacement();
01137 emit addAutoCorrect (dlgorigword , _replacement);
01138 break;
01139 }
01140 }
01141
01142 connect (this, SIGNAL (dialog3()), this, dialog3slot.ascii());
01143 emit dialog3();
01144 }
01145
01146
01147 KOISpell:: ~KOISpell ()
01148 {
01149 delete proc;
01150 }
01151
01152
01153 void KOISpell::cleanUp ()
01154 {
01155 if (m_status == Cleaning) return; // Ignore
01156 if (m_status == Running)
01157 {
01158 if (personaldict)
01159 writePersonalDictionary();
01160 m_status = Cleaning;
01161 }
01162 proc->closeStdin();
01163 }
01164
01165 void KOISpell::ispellExit (KProcess *)
01166 {
01167 kdDebug(30006) << "KSpell::ispellExit() " << m_status << endl;
01168
01169 if ((m_status == Starting) && (trystart<maxtrystart))
01170 {
01171 trystart++;
01172 startIspell();
01173 return;
01174 }
01175
01176 if (m_status == Starting)
01177 m_status = Error;
01178 else if (m_status == Cleaning)
01179 m_status = m_bNoMisspellingsEncountered ? FinishedNoMisspellingsEncountered : Finished;
01180 else if (m_status == Running)
01181 m_status = Crashed;
01182 else // Error, Finished, Crashed
01183 return; // Dead already
01184
01185 kdDebug(30006) << "Death" << endl;
01186 QTimer::singleShot( 0, this, SLOT(emitDeath()));
01187 }
01188
01189 // This is always called from the event loop to make
01190 // sure that the receiver can safely delete the
01191 // KOISpell object.
01192 void KOISpell::emitDeath()
01193 {
01194 bool deleteMe = autoDelete; // Can't access object after next call!
01195 emit death();
01196 if (deleteMe)
01197 deleteLater();
01198 }
01199
01200 void KOISpell::setProgressResolution (unsigned int res)
01201 {
01202 progres=res;
01203 }
01204
01205 void KOISpell::emitProgress ()
01206 {
01207 uint nextprog = (uint) (100.*lastpos/(double)totalpos);
01208
01209 if (nextprog>=curprog)
01210 {
01211 curprog=nextprog;
01212 emit progress (curprog);
01213 }
01214 }
01215
01216 // --------------------------------------------------
01217 // Stuff for modal (blocking) spell checking
01218 //
01219 // Written by Torben Weis <weis@kde.org>. So please
01220 // send bug reports regarding the modal stuff to me.
01221 // --------------------------------------------------
01222
01223
01224 int
01225 KOISpell::modalCheck( QString& text, KOSpellConfig* _kcs )
01226 {
01227 modalreturn = 0;
01228 modaltext = text;
01229
01230
01231 // kdDebug(30006) << "KOISpell1" << endl;
01232 KOISpell* spell = new KOISpell( 0L, i18n("Spell Checker"), 0 ,
01233 0, _kcs, true, true );
01234 //qApp->enter_loop();
01235
01236 while ((spell->status()==Starting) || (spell->status()==Running) || (spell->status()==Cleaning))
01237 kapp->processEvents();
01238
01239 text = modaltext;
01240
01241 delete spell;
01242 return modalreturn;
01243 }
01244
01245 void KOISpell::slotSpellCheckerCorrected( const QString & oldText, const QString & newText, unsigned int pos )
01246 {
01247 modaltext=modaltext.replace(pos,oldText.length(),newText);
01248 }
01249
01250
01251 void KOISpell::slotModalReady()
01252 {
01253 //kdDebug(30006) << qApp->loopLevel() << endl;
01254 //kdDebug(30006) << "MODAL READY------------------" << endl;
01255
01256 Q_ASSERT( m_status == Running );
01257 connect( this, SIGNAL( done( const QString & ) ),
01258 this, SLOT( slotModalDone( const QString & ) ) );
01259 QObject::connect( this, SIGNAL( corrected( const QString&, const QString&, unsigned int ) ),
01260 this, SLOT( slotSpellCheckerCorrected( const QString&, const QString &, unsigned int ) ) );
01261 QObject::connect( this, SIGNAL( death() ),
01262 this, SLOT( slotModalSpellCheckerFinished( ) ) );
01263 check( modaltext );
01264 }
01265
01266 void KOISpell::slotModalDone( const QString &/*_buffer*/ )
01267 {
01268 //kdDebug(30006) << "MODAL DONE " << _buffer << endl;
01269 //modaltext = _buffer;
01270 cleanUp();
01271
01272 //kdDebug(30006) << "ABOUT TO EXIT LOOP" << endl;
01273 //qApp->exit_loop();
01274
01275 slotModalSpellCheckerFinished();
01276 }
01277
01278 void KOISpell::slotModalSpellCheckerFinished( )
01279 {
01280 modalreturn=(int)this->status();
01281 }
01282
01283
01284 void KOISpell::initialize( QWidget *_parent, const QString &_caption,
01285 QObject *obj, const char *slot, KOSpellConfig *_ksc,
01286 bool _progressbar, bool _modal )
01287 {
01288 m_ready = false;
01289 m_bIgnoreUpperWords=false;
01290 m_bIgnoreTitleCase=false;
01291
01292 autoDelete = false;
01293 modaldlg = _modal;
01294 progressbar = _progressbar;
01295
01296 proc=0;
01297 ksdlg=0;
01298
01299 texmode=dlgon=false;
01300
01301 dialogsetup = false;
01302 progres=10;
01303 curprog=0;
01304
01305 dialogwillprocess=false;
01306 dialog3slot="";
01307
01308 personaldict=false;
01309 dlgresult=-1;
01310
01311 caption=_caption;
01312
01313 parent=_parent;
01314
01315 trystart=0;
01316 maxtrystart=2;
01317
01318 if ( obj && slot )
01319 // caller wants to know when kspell is ready
01320 connect (this, SIGNAL (ready(KOSpell *)), obj, slot);
01321 else
01322 // Hack for modal spell checking
01323 connect (this, SIGNAL (ready(KOSpell *)), this, SLOT( slotModalReady() ) );
01324 proc=new KProcIO(codec);
01325
01326 startIspell();
01327 }
01328
01329 // This is retarded, if you don't get it, don't worry
01330 // it's me working around 999999999 problems
01331 void qt_enter_modal( QWidget *widget );
01332 void qt_leave_modal( QWidget *widget );
01333 void KOISpell::enter_loop()
01334 {
01335 QWidget dummy(0,0,WType_Dialog | WShowModal);
01336 dummy.setFocusPolicy( QWidget::NoFocus );
01337 qt_enter_modal(&dummy);
01338 qApp->enter_loop();
01339 qt_leave_modal(&dummy);
01340 }
01341
01342 void KOISpell::slotSynchronousReady()
01343 {
01344 qApp->exit_loop();
01345 }
01346