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