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