lib Library API Documentation

koispell.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2002-2003 Laurent Montel <lmontel@mandrakesoft.com> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License version 2 as published by the Free Software Foundation. 00007 00008 This library is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 Library General Public License for more details. 00012 00013 You should have received a copy of the GNU Library General Public License 00014 along with this library; see the file COPYING.LIB. If not, write to 00015 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00016 Boston, MA 02111-1307, USA. 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> // atoi 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 // TODO: Parse stderr output e.g. -- invalid dictionary name (bug #40403) 00060 00061 /* 00062 Things to put in KSpellConfigDlg: 00063 make root/affix combinations that aren't in the dictionary (-m) 00064 don't generate any affix/root combinations (-P) 00065 Report run-together words with missing blanks as spelling errors. (-B) 00066 default dictionary (-d [dictionary]) 00067 personal dictionary (-p [dictionary]) 00068 path to ispell -- NO: ispell should be in $PATH 00069 */ 00070 00071 00072 // Connects a slot to KProcIO's output signal 00073 #define OUTPUT(x) (connect (proc, SIGNAL (readReady(KProcIO *)), this, SLOT (x(KProcIO *)))) 00074 00075 // Disconnect a slot from... 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,/*_autocorrect*/false, _type) 00086 { 00087 initialize( _parent, _caption, obj, slot, _ksc, 00088 _progressbar, _modal ); 00089 } 00090 00091 void KOISpell::startIspell() 00092 //trystart = {0,1,2} 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 // This can only happen if HAVE_LIBASPELL isn't defined 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 // -a : pipe mode 00121 // -S : sort suggestions by probable correctness 00122 *proc << "-a" << "-S"; 00123 switch ( type ) 00124 { 00125 case HTML: 00126 //Debian uses an ispell version that has the -h option instead. 00127 //Not sure what they did, but the prefered spell checker 00128 //on that platform is aspell anyway, so use -H until I'll come 00129 //up with something better. 00130 *proc << "-H"; 00131 break; 00132 case TeX: 00133 //same for aspell and ispell 00134 *proc << "-t"; 00135 break; 00136 case Nroff: 00137 //only ispell supports 00138 if ( ksconfig->client() == KOS_CLIENT_ISPELL ) 00139 *proc << "-n"; 00140 break; 00141 case Text: 00142 default: 00143 //nothing 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 //Note to potential debuggers: -Tlatin2 _is_ being added on the 00171 // _first_ try. But, some versions of ispell will fail with this 00172 // option, so kspell tries again without it. That's why as 'ps -ax' 00173 // shows "ispell -a -S ..." without the "-Tlatin2" option. 00174 /* 00175 The background is that -T does not define something like an 00176 encoding but -T defines something like a mode. And the potential 00177 modes are different for each language. (It is defined in ispell's 00178 *.aff files.) 00179 00180 Note that ispell called without the appopriate -T means that the 00181 language does not have special characters (e.g. accents) anymore. 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 There is no known ispell dictionary using ISO-8859-15 00199 but many users are tempted to use that setting. (Bug #33108) 00200 So use ISO-8859-1 instead. 00201 */ 00202 *proc << "-Tlatin1"; 00203 break; 00204 00205 // add the other charsets here 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 // will work, if this is the default charset in the dictionary 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'"; // add ' as a word char 00223 break; 00224 00225 } 00226 /* 00227 if (ksconfig->personalDict()[0]!='\0') 00228 { 00229 kdDebug(30006) << "personal dictionary [" << ksconfig->personalDict() << "]" << endl; 00230 *proc << "-p"; 00231 *proc << ksconfig->personalDict(); 00232 } 00233 */ 00234 00235 } 00236 else // hspell doesn't need all the rest of the options 00237 *proc << "-a"; // -a : pipe mode 00238 00239 if (trystart==0) //don't connect these multiple times 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 //kdDebug(30006) << "ispellErrors [" << buffer << "]\n" << endl; 00271 } 00272 00273 void KOISpell::KSpell2 (KProcIO *) 00274 00275 { 00276 kdDebug(30006) << "KSpell::KSpell2" << endl; 00277 trystart=maxtrystart; //We've officially started ispell and don't want 00278 //to try again if it dies. 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]!='@') //@ indicates that ispell is working fine 00289 { 00290 QTimer::singleShot( 0, this, SLOT(emitDeath())); 00291 return; 00292 } 00293 00294 //We want to recognize KDE in any text! 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 //We want to recognize linux in any text! 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 //Set up the dialog box 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 //we'll let ispell do the work here b/c we can 00344 if (qs.find (' ')!=-1 || qs.isEmpty()) // make sure it's a _word_ 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 //we'll let ispell do the work here b/c we can 00363 if (qs.find (' ')!=-1 || qs.isEmpty()) // make sure it's a _word_ 00364 return false; 00365 00366 qs.prepend ("@"); 00367 00368 return proc->fputs(qs); 00369 } 00370 00371 bool 00372 KOISpell::cleanFputsWord (const QString & s, bool appendCR) 00373 { 00374 QString qs(s); 00375 //bool firstchar = true; 00376 bool empty = true; 00377 00378 for (unsigned int i=0; i<qs.length(); i++) 00379 { 00380 //we need some punctuation for ornaments 00381 if (qs[i] != '\'' && qs[i] != '\"' && qs[i] != '-' 00382 && qs[i].isPunct() || qs[i].isSpace()) 00383 { 00384 qs.remove(i,1); 00385 i--; 00386 } else { 00387 if (qs[i].isLetter()) empty=false; 00388 } 00389 } 00390 00391 // don't check empty words, otherwise synchronisation will lost 00392 if (empty) return false; 00393 00394 return proc->fputs("^"+qs, appendCR); 00395 } 00396 00397 bool 00398 KOISpell::cleanFputs (const QString & s, bool appendCR) 00399 { 00400 QString qs(s); 00401 unsigned l = qs.length(); 00402 00403 // some uses of '$' (e.g. "$0") cause ispell to skip all following text 00404 for(unsigned int i = 0; i < l; ++i) 00405 { 00406 if(qs[i] == '$') 00407 qs[i] = ' '; 00408 } 00409 00410 if (l<MAXLINELENGTH) 00411 { 00412 if (qs.isEmpty()) 00413 qs=""; 00414 00415 return proc->fputs ("^"+qs, appendCR); 00416 } 00417 else 00418 return proc->fputs ("^\n",appendCR); 00419 } 00420 00421 bool KOISpell::checkWord (const QString & buffer, bool _usedialog) 00422 { 00423 QString qs = buffer.simplifyWhiteSpace(); 00424 if (qs.find (' ')!=-1 || qs.isEmpty()) // make sure it's a _word_ 00425 return false; 00426 00428 dialog3slot = SLOT(checkWord3()); 00429 00430 usedialog=_usedialog; 00431 setUpDialog(false); 00432 if (_usedialog) 00433 { 00434 emitProgress(); 00435 ksdlg->show(); 00436 } 00437 else 00438 ksdlg->hide(); 00439 00440 OUTPUT (checkWord2); 00441 // connect (this, SIGNAL (dialog3()), this, SLOT (checkWord3())); 00442 00443 proc->fputs ("%"); // turn off terse mode 00444 cleanFputsWord( qs ); // send the word to ispell 00445 00446 return true; 00447 } 00448 00449 //it can't use dialog anyway 00450 bool KOISpell::checkWord (const QString & buffer, bool _usedialog, bool synchronous ) 00451 { 00452 QString qs = buffer.simplifyWhiteSpace(); 00453 if (qs.find (' ')!=-1 || qs.isEmpty()) // make sure it's a _word_ 00454 return false; 00455 00457 dialog3slot = SLOT(checkWord3()); 00458 00459 usedialog=_usedialog; 00460 setUpDialog(false); 00461 00462 ksdlg->hide(); 00463 if ( synchronous ) { 00464 //ready signal is never called, after initialize 00465 if ( !m_ready ) { 00466 connect( this, SIGNAL(ready(KOSpell*)), 00467 this, SLOT(slotSynchronousReady()) ); 00468 connect( this, SIGNAL(death()), // in case of init failure 00469 this, SLOT(slotSynchronousReady()) ); 00470 //MAGIC 1: here we wait for the initialization to finish 00471 enter_loop(); 00472 disconnect( this, SIGNAL(ready(KOSpell*)), 00473 this, SLOT(slotSynchronousReady()) ); 00474 disconnect( this, SIGNAL(death()), // in case of init failure 00475 this, SLOT(slotSynchronousReady()) ); 00476 } 00477 if ( m_status == Error ) // init failure 00478 return false; 00479 OUTPUT (checkWord2Synchronous); 00480 } 00481 else 00482 OUTPUT (checkWord2); 00483 00484 proc->fputs ("%"); // turn off terse mode 00485 00486 if (cleanFputsWord( qs )) // send the word to ispell 00487 enter_loop(); //MAGIC 2: and here we wait for the results 00488 00489 return true; 00490 } 00491 00492 void KOISpell::checkWord2 (KProcIO *) 00493 { 00494 QString word; 00495 00496 QString line; 00497 proc->fgets (line, true); //get ispell's response 00498 00499 /* ispell man page: "Each sentence of text input is terminated with an 00500 additional blank line, indicating that ispell has completed processing 00501 the input line." */ 00502 QString blank_line; 00503 proc->fgets(blank_line, true); // eat the blank line 00504 00505 NOOUTPUT(checkWord2); 00506 00507 bool mistake = (parseOneResponse(line, word, sugg) == MISTAKE); 00508 if ( mistake && usedialog ) 00509 { 00510 cwword=word; 00511 dialog (word, sugg, SLOT (checkWord3())); 00512 return; 00513 } 00514 else if( mistake ) 00515 { 00516 misspellingWord (word, sugg, lastpos); 00517 } 00518 00519 //emits a "corrected" signal _even_ if no change was made 00520 //so that the calling program knows when the check is complete 00521 emit corrected (word, word, 0L); 00522 } 00523 00524 // This is not even cute... Just watch me abuse 00525 // Qt, KDE, candy, cookies and make this stuff work 00526 // through pure magic 00527 void KOISpell::checkWord2Synchronous (KProcIO *) 00528 { 00529 QString word; 00530 00531 QString line; 00532 proc->fgets (line, true); //get ispell's response 00533 00534 /* ispell man page: "Each sentence of text input is terminated with an 00535 additional blank line, indicating that ispell has completed processing 00536 the input line." */ 00537 QString blank_line; 00538 proc->fgets(blank_line, true); // eat the blank line 00539 00540 NOOUTPUT(checkWord2); 00541 00542 bool mistake = (parseOneResponse(line, word, sugg) == MISTAKE); 00543 if( mistake ) 00544 { 00545 misspellingWord (word, sugg, lastpos); 00546 } 00547 //emits a "corrected" signal _even_ if no change was made 00548 //so that the calling program knows when the check is complete 00549 emit corrected (word, word, 0L); 00550 qApp->exit_loop(); 00551 } 00552 00553 void KOISpell::checkWord3 () 00554 { 00555 disconnect (this, SIGNAL (dialog3()), this, SLOT (checkWord3())); 00556 00557 emit corrected (cwword, replacement(), 0L); 00558 } 00559 00560 QString KOISpell::funnyWord (const QString & word) 00561 // composes a guess from ispell to a readable word 00562 // e.g. "re+fry-y+ies" -> "refries" 00563 { 00564 QString qs; 00565 unsigned int i=0; 00566 00567 for (i=0; word [i]!='\0';i++) 00568 { 00569 if (word [i]=='+') 00570 continue; 00571 if (word [i]=='-') 00572 { 00573 QString shorty; 00574 unsigned int j; 00575 int k; 00576 00577 for (j=i+1;word [j]!='\0' && word [j]!='+' && 00578 word [j]!='-';j++) 00579 shorty+=word [j]; 00580 i=j-1; 00581 00582 if ((k=qs.findRev (shorty))==0 || k!=-1) 00583 qs.remove (k,shorty.length()); 00584 else 00585 { 00586 qs+='-'; 00587 qs+=shorty; //it was a hyphen, not a '-' from ispell 00588 } 00589 } 00590 else 00591 qs+=word [i]; 00592 } 00593 return qs; 00594 } 00595 00596 00597 int KOISpell::parseOneResponse (const QString &buffer, QString &word, QStringList & sugg) 00598 // buffer is checked, word and sugg are filled in 00599 // returns 00600 // GOOD if word is fine 00601 // IGNORE if word is in ignorelist 00602 // REPLACE if word is in replacelist 00603 // MISTAKE if word is misspelled 00604 { 00605 word = ""; 00606 posinline=0; 00607 00608 sugg.clear(); 00609 00610 if (buffer [0]=='*' || buffer[0] == '+' || buffer[0] == '-') 00611 { 00612 return GOOD; 00613 } 00614 00615 if (buffer [0]=='&' || buffer [0]=='?' || buffer [0]=='#') 00616 { 00617 int i,j; 00618 00619 00620 word = buffer.mid (2,buffer.find (' ',3)-2); 00621 //check() needs this 00622 orig=word; 00623 00624 if(m_bIgnoreTitleCase && word==word.upper()) 00625 return IGNORE; 00626 00627 if(m_bIgnoreUpperWords && word[0]==word[0].upper()) 00628 { 00629 QString text=word[0]+word.right(word.length()-1).lower(); 00630 if(text==word) 00631 return IGNORE; 00632 } 00633 00635 //We don't take advantage of ispell's ignore function because 00636 //we can't interrupt ispell's output (when checking a large 00637 //buffer) to add a word to _it's_ ignore-list. 00638 if (ignorelist.findIndex(word.lower())!=-1) 00639 return IGNORE; 00640 00642 QString qs2; 00643 00644 if (buffer.find(':')!=-1) 00645 qs2=buffer.left (buffer.find (':')); 00646 else 00647 qs2=buffer; 00648 00649 posinline = qs2.right( qs2.length()-qs2.findRev(' ') ).toInt()-1; 00650 00652 QStringList::Iterator it = replacelist.begin(); 00653 for(;it != replacelist.end(); ++it, ++it) // Skip two entries at a time. 00654 { 00655 if (word == *it) // Word matches 00656 { 00657 ++it; 00658 word = *it; // Replace it with the next entry 00659 return REPLACE; 00660 } 00661 } 00662 00664 if (buffer [0] != '#') 00665 { 00666 QString qs = buffer.mid(buffer.find(':')+2, buffer.length()); 00667 qs+=','; 00668 sugg.clear(); 00669 i=j=0; 00670 while ((unsigned int)i<qs.length()) 00671 { 00672 QString temp = qs.mid (i,(j=qs.find (',',i))-i); 00673 sugg.append (funnyWord (temp)); 00674 00675 i=j+2; 00676 } 00677 } 00678 00679 if ((sugg.count()==1) && (sugg.first() == word)) 00680 return GOOD; 00681 00682 return MISTAKE; 00683 } 00684 00685 00686 kdError(750) << "HERE?: [" << buffer << "]" << endl; 00687 kdError(750) << "Please report this to dsweet@kde.org" << endl; 00688 kdError(750) << "Thank you!" << endl; 00689 emit done(false); 00690 emit done (KOISpell::origbuffer); 00691 return MISTAKE; 00692 } 00693 00694 bool KOISpell::checkList (QStringList *_wordlist, bool _usedialog) 00695 // prepare check of string list 00696 { 00697 wordlist=_wordlist; 00698 if ((totalpos=wordlist->count())==0) 00699 return false; 00700 wlIt = wordlist->begin(); 00701 usedialog=_usedialog; 00702 00703 // prepare the dialog 00704 setUpDialog(); 00705 00706 //set the dialog signal handler 00707 dialog3slot = SLOT (checkList4 ()); 00708 00709 proc->fputs ("%"); // turn off terse mode & check one word at a time 00710 00711 //lastpos now counts which *word number* we are at in checkListReplaceCurrent() 00712 lastpos = -1; 00713 checkList2(); 00714 00715 // when checked, KProcIO calls checkList3a 00716 OUTPUT(checkList3a); 00717 00718 return true; 00719 } 00720 00721 void KOISpell::checkList2 () 00722 // send one word from the list to KProcIO 00723 // invoked first time by checkList, later by checkListReplaceCurrent and checkList4 00724 { 00725 // send next word 00726 if (wlIt != wordlist->end()) 00727 { 00728 kdDebug(30006) << "KS::cklist2 " << lastpos << ": " << *wlIt << endl; 00729 00730 endOfResponse = false; 00731 bool put; 00732 lastpos++; offset=0; 00733 put = cleanFputsWord (*wlIt); 00734 ++wlIt; 00735 00736 // when cleanFPutsWord failed (e.g. on empty word) 00737 // try next word; may be this is not good for other 00738 // problems, because this will make read the list up to the end 00739 if (!put) { 00740 checkList2(); 00741 } 00742 } 00743 else 00744 // end of word list 00745 { 00746 NOOUTPUT(checkList3a); 00747 ksdlg->hide(); 00748 emit done(true); 00749 } 00750 } 00751 00752 void KOISpell::checkList3a (KProcIO *) 00753 // invoked by KProcIO, when data from ispell are read 00754 { 00755 //kdDebug(30006) << "start of checkList3a" << endl; 00756 00757 // don't read more data, when dialog is waiting 00758 // for user interaction 00759 if (dlgon) { 00760 //kdDebug(30006) << "dlgon: don't read more data" << endl; 00761 return; 00762 } 00763 00764 int e, tempe; 00765 00766 QString word; 00767 QString line; 00768 00769 do 00770 { 00771 tempe=proc->fgets (line, true); //get ispell's response 00772 00773 //kdDebug(30006) << "checkList3a: read bytes [" << tempe << "]" << endl; 00774 00775 00776 if (tempe == 0) { 00777 endOfResponse = true; 00778 //kdDebug(30006) << "checkList3a: end of resp" << endl; 00779 } else if (tempe>0) { 00780 if ((e=parseOneResponse (line, word, sugg))==MISTAKE || 00781 e==REPLACE) 00782 { 00783 dlgresult=-1; 00784 00785 if (e==REPLACE) 00786 { 00787 QString old = *(--wlIt); ++wlIt; 00788 dlgreplacement=word; 00789 checkListReplaceCurrent(); 00790 // inform application 00791 emit corrected (old, *(--wlIt), lastpos); ++wlIt; 00792 } 00793 else if( usedialog ) 00794 { 00795 cwword=word; 00796 dlgon=true; 00797 // show the dialog 00798 dialog (word, sugg, SLOT (checkList4())); 00799 return; 00800 } 00801 else 00802 { 00803 misspellingWord (word, sugg, lastpos); 00804 } 00805 } 00806 00807 } 00808 emitProgress (); //maybe 00809 00810 // stop when empty line or no more data 00811 } while (tempe > 0); 00812 00813 //kdDebug(30006) << "checkList3a: exit loop with [" << tempe << "]" << endl; 00814 00815 // if we got an empty line, t.e. end of ispell/aspell response 00816 // and the dialog isn't waiting for user interaction, send next word 00817 if (endOfResponse && !dlgon) { 00818 //kdDebug(30006) << "checkList3a: send next word" << endl; 00819 checkList2(); 00820 } 00821 } 00822 00823 void KOISpell::checkListReplaceCurrent () { 00824 00825 // go back to misspelled word 00826 wlIt--; 00827 00828 QString s = *wlIt; 00829 s.replace(posinline+offset,orig.length(),replacement()); 00830 offset += replacement().length()-orig.length(); 00831 wordlist->insert (wlIt, s); 00832 wlIt = wordlist->remove (wlIt); 00833 // wlIt now points to the word after the repalced one 00834 00835 } 00836 00837 void KOISpell::checkList4 () 00838 // evaluate dialog return, when a button was pressed there 00839 { 00840 dlgon=false; 00841 QString old; 00842 00843 disconnect (this, SIGNAL (dialog3()), this, SLOT (checkList4())); 00844 00845 //others should have been processed by dialog() already 00846 switch (dlgresult) 00847 { 00848 case KOS_REPLACE: 00849 case KOS_REPLACEALL: 00850 kdDebug(30006) << "KS: cklist4: lastpos: " << lastpos << endl; 00851 old = *(--wlIt); ++wlIt; 00852 // replace word 00853 checkListReplaceCurrent(); 00854 emit corrected (old, *(--wlIt), lastpos); ++wlIt; 00855 break; 00856 case KOS_CANCEL: 00857 ksdlg->hide(); 00858 emit done (false); 00859 return; 00860 case KOS_STOP: 00861 ksdlg->hide(); 00862 emit done (true); 00863 break; 00864 }; 00865 00866 // read more if there is more, otherwise send next word 00867 if (!endOfResponse) { 00868 //kdDebug(30006) << "checkList4: read more from response" << endl; 00869 checkList3a(NULL); 00870 } 00871 } 00872 00873 bool KOISpell::check( const QString &_buffer, bool _usedialog ) 00874 { 00875 QString qs; 00876 00877 usedialog=_usedialog; 00878 setUpDialog (); 00879 //set the dialog signal handler 00880 dialog3slot = SLOT (check3 ()); 00881 00882 kdDebug(30006) << "KS: check" << endl; 00883 origbuffer = _buffer; 00884 if ( ( totalpos = origbuffer.length() ) == 0 ) 00885 { 00886 emit done(origbuffer); 00887 return false; 00888 } 00889 00890 00891 // Torben: I corrected the \n\n problem directly in the 00892 // origbuffer since I got errors otherwise 00893 if ( origbuffer.right(2) != "\n\n" ) 00894 { 00895 if (origbuffer.at(origbuffer.length()-1)!='\n') 00896 { 00897 origbuffer+='\n'; 00898 origbuffer+='\n'; //shouldn't these be removed at some point? 00899 } 00900 else 00901 origbuffer+='\n'; 00902 } 00903 00904 newbuffer=origbuffer; 00905 00906 // KProcIO calls check2 when read from ispell 00907 OUTPUT(check2); 00908 proc->fputs ("!"); 00909 00910 //lastpos is a position in newbuffer (it has offset in it) 00911 offset=lastlastline=lastpos=lastline=0; 00912 00913 emitProgress (); 00914 00915 // send first buffer line 00916 int i = origbuffer.find('\n', 0)+1; 00917 qs=origbuffer.mid (0,i); 00918 cleanFputs (qs,false); 00919 00920 lastline=i; //the character position, not a line number 00921 00922 if (usedialog) 00923 { 00924 emitProgress(); 00925 ksdlg->show(); 00926 } 00927 else 00928 ksdlg->hide(); 00929 00930 return true; 00931 } 00932 00933 void KOISpell::check2 (KProcIO *) 00934 // invoked by KProcIO when read from ispell 00935 { 00936 int e, tempe; 00937 QString word; 00938 QString line; 00939 static bool recursive = false; 00940 if (recursive && 00941 (!ksdlg || ksdlg->isHidden())) 00942 { 00943 return; 00944 } 00945 recursive = true; 00946 00947 do 00948 { 00949 tempe=proc->fgets (line); //get ispell's response 00950 kdDebug(30006) << "KSpell::check2 (" << tempe << "b)" << endl; 00951 00952 if (tempe>0) 00953 { 00954 if ((e=parseOneResponse (line, word, sugg))==MISTAKE || 00955 e==REPLACE) 00956 { 00957 dlgresult=-1; 00958 00959 // for multibyte encoding posinline needs correction 00960 if (ksconfig->encoding() == KOS_E_UTF8) { 00961 // kdDebug(30006) << "line: " << origbuffer.mid(lastlastline, 00962 // lastline-lastlastline) << endl; 00963 // kdDebug(30006) << "posinline uncorr: " << posinline << endl; 00964 00965 // convert line to UTF-8, cut at pos, convert back to UCS-2 00966 // and get string length 00967 posinline = (QString::fromUtf8( 00968 origbuffer.mid(lastlastline,lastline-lastlastline).utf8(), 00969 posinline)).length(); 00970 // kdDebug(30006) << "posinline corr: " << posinline << endl; 00971 } 00972 00973 lastpos=posinline+lastlastline+offset; 00974 00975 //orig is set by parseOneResponse() 00976 00977 if (e==REPLACE) 00978 { 00979 dlgreplacement=word; 00980 emit corrected (orig, replacement(), lastpos); 00981 offset+=replacement().length()-orig.length(); 00982 newbuffer.replace (lastpos, orig.length(), word); 00983 } 00984 else //MISTAKE 00985 { 00986 cwword=word; 00987 //kdDebug(30006) << "(Before dialog) word=[" << word << "] cwword =[" << cwword << "]\n" << endl; 00988 if ( usedialog ) { 00989 // show the word in the dialog 00990 dialog (word, sugg, SLOT (check3())); 00991 } else { 00992 // No dialog, just emit misspelling and continue 00993 misspellingWord (word, sugg, lastpos); 00994 dlgresult = KOS_IGNORE; 00995 check3(); 00996 } 00997 recursive = false; 00998 return; 00999 } 01000 } 01001 01002 } 01003 01004 emitProgress (); //maybe 01005 01006 } while (tempe>0); 01007 01008 proc->ackRead(); 01009 01010 if (tempe==-1) {//we were called, but no data seems to be ready... 01011 recursive = false; 01012 return; 01013 } 01014 01015 proc->ackRead(); 01016 //If there is more to check, then send another line to ISpell. 01017 if ((unsigned int)lastline<origbuffer.length()) 01018 { 01019 int i; 01020 QString qs; 01021 01022 //kdDebug(30006) << "[EOL](" << tempe << ")[" << temp << "]" << endl; 01023 01024 lastpos=(lastlastline=lastline)+offset; //do we really want this? 01025 i=origbuffer.find('\n', lastline)+1; 01026 qs=origbuffer.mid (lastline, i-lastline); 01027 cleanFputs (qs,false); 01028 lastline=i; 01029 recursive = false; 01030 return; 01031 } 01032 else 01033 //This is the end of it all 01034 { 01035 ksdlg->hide(); 01036 // kdDebug(30006) << "check2() done" << endl; 01037 newbuffer.truncate (newbuffer.length()-2); 01038 emitProgress(); 01039 NOOUTPUT( check2 ); 01040 emit done (newbuffer); 01041 } 01042 recursive = false; 01043 } 01044 01045 void KOISpell::check3 () 01046 // evaluates the return value of the dialog 01047 { 01048 disconnect (this, SIGNAL (dialog3()), this, SLOT (check3())); 01049 01050 kdDebug(30006) << "check3 [" << cwword << "] [" << replacement() << "] " << dlgresult << endl; 01051 01052 //others should have been processed by dialog() already 01053 switch (dlgresult) 01054 { 01055 case KOS_REPLACE: 01056 case KOS_REPLACEALL: 01057 offset+=replacement().length()-cwword.length(); 01058 newbuffer.replace (lastpos, cwword.length(), 01059 replacement()); 01060 emit corrected (dlgorigword, replacement(), lastpos); 01061 break; 01062 case KOS_CANCEL: 01063 // kdDebug(30006) << "cancelled\n" << endl; 01064 ksdlg->hide(); 01065 emit done (origbuffer); 01066 return; 01067 case KOS_STOP: 01068 ksdlg->hide(); 01069 //buffer=newbuffer); 01070 emitProgress(); 01071 emit done (newbuffer); 01072 return; 01073 }; 01074 01075 proc->ackRead(); 01076 } 01077 01078 void 01079 KOISpell::slotStopCancel (int result) 01080 { 01081 if (dialogwillprocess) 01082 return; 01083 01084 kdDebug(30006) << "KSpell::slotStopCancel [" << result << "]" << endl; 01085 01086 if (result==KOS_STOP || result==KOS_CANCEL) 01087 if (!dialog3slot.isEmpty()) 01088 { 01089 dlgresult=result; 01090 connect (this, SIGNAL (dialog3()), this, dialog3slot.ascii()); 01091 emit dialog3(); 01092 } 01093 } 01094 01095 01096 void KOISpell::dialog(const QString & word, QStringList & sugg, const char *_slot) 01097 { 01098 dlgorigword=word; 01099 01100 dialog3slot=_slot; 01101 dialogwillprocess=true; 01102 connect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int))); 01103 ksdlg->init (word, &sugg); 01104 misspellingWord (word, sugg, lastpos); 01105 01106 emitProgress(); 01107 ksdlg->show(); 01108 } 01109 01110 void KOISpell::dialog2 (int result) 01111 { 01112 QString qs; 01113 01114 disconnect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int))); 01115 dialogwillprocess=false; 01116 dlgresult=result; 01117 ksdlg->standby(); 01118 01119 dlgreplacement=ksdlg->replacement(); 01120 01121 //process result here 01122 switch (dlgresult) 01123 { 01124 01125 case KOS_IGNORE: 01126 emit ignoreword(dlgorigword); 01127 break; 01128 case KOS_IGNOREALL: 01129 // would be better to lower case only words with beginning cap 01130 ignorelist.prepend(dlgorigword.lower()); 01131 emit ignoreall (dlgorigword); 01132 break; 01133 case KOS_ADD: 01134 addPersonal (dlgorigword); 01135 personaldict=true; 01136 emit addword (dlgorigword); 01137 // adding to pesonal dict takes effect at the next line, not the current 01138 ignorelist.prepend(dlgorigword.lower()); 01139 break; 01140 case KOS_REPLACEALL: 01141 { 01142 replacelist.append (dlgorigword); 01143 QString _replacement = replacement(); 01144 replacelist.append (_replacement); 01145 emit replaceall( dlgorigword , _replacement ); 01146 } 01147 break; 01148 case KOS_ADDAUTOCORRECT: 01149 { 01150 //todo add new word ???? 01151 QString _replacement = replacement(); 01152 emit addAutoCorrect (dlgorigword , _replacement); 01153 break; 01154 } 01155 } 01156 01157 connect (this, SIGNAL (dialog3()), this, dialog3slot.ascii()); 01158 emit dialog3(); 01159 } 01160 01161 01162 KOISpell:: ~KOISpell () 01163 { 01164 delete proc; 01165 } 01166 01167 01168 void KOISpell::cleanUp () 01169 { 01170 if (m_status == Cleaning) return; // Ignore 01171 if (m_status == Running) 01172 { 01173 if (personaldict) 01174 writePersonalDictionary(); 01175 m_status = Cleaning; 01176 } 01177 proc->closeStdin(); 01178 } 01179 01180 void KOISpell::ispellExit (KProcess *) 01181 { 01182 kdDebug(30006) << "KSpell::ispellExit() " << m_status << endl; 01183 01184 if ((m_status == Starting) && (trystart<maxtrystart)) 01185 { 01186 trystart++; 01187 startIspell(); 01188 return; 01189 } 01190 01191 if (m_status == Starting) 01192 m_status = Error; 01193 else if (m_status == Cleaning) 01194 m_status = m_bNoMisspellingsEncountered ? FinishedNoMisspellingsEncountered : Finished; 01195 else if (m_status == Running) 01196 m_status = Crashed; 01197 else // Error, Finished, Crashed 01198 return; // Dead already 01199 01200 kdDebug(30006) << "Death" << endl; 01201 QTimer::singleShot( 0, this, SLOT(emitDeath())); 01202 } 01203 01204 // This is always called from the event loop to make 01205 // sure that the receiver can safely delete the 01206 // KOISpell object. 01207 void KOISpell::emitDeath() 01208 { 01209 bool deleteMe = autoDelete; // Can't access object after next call! 01210 emit death(); 01211 if (deleteMe) 01212 deleteLater(); 01213 } 01214 01215 void KOISpell::setProgressResolution (unsigned int res) 01216 { 01217 progres=res; 01218 } 01219 01220 void KOISpell::emitProgress () 01221 { 01222 uint nextprog = (uint) (100.*lastpos/(double)totalpos); 01223 01224 if (nextprog>=curprog) 01225 { 01226 curprog=nextprog; 01227 emit progress (curprog); 01228 } 01229 } 01230 01231 // -------------------------------------------------- 01232 // Stuff for modal (blocking) spell checking 01233 // 01234 // Written by Torben Weis <weis@kde.org>. So please 01235 // send bug reports regarding the modal stuff to me. 01236 // -------------------------------------------------- 01237 01238 01239 int 01240 KOISpell::modalCheck( QString& text, KOSpellConfig* _kcs ) 01241 { 01242 modalreturn = 0; 01243 modaltext = text; 01244 01245 01246 // kdDebug(30006) << "KOISpell1" << endl; 01247 KOISpell* spell = new KOISpell( 0L, i18n("Spell Checker"), 0 , 01248 0, _kcs, true, true ); 01249 //qApp->enter_loop(); 01250 01251 while ((spell->status()==Starting) || (spell->status()==Running) || (spell->status()==Cleaning)) 01252 kapp->processEvents(); 01253 01254 text = modaltext; 01255 01256 delete spell; 01257 return modalreturn; 01258 } 01259 01260 void KOISpell::slotSpellCheckerCorrected( const QString & oldText, const QString & newText, unsigned int pos ) 01261 { 01262 modaltext=modaltext.replace(pos,oldText.length(),newText); 01263 } 01264 01265 01266 void KOISpell::slotModalReady() 01267 { 01268 //kdDebug(30006) << qApp->loopLevel() << endl; 01269 //kdDebug(30006) << "MODAL READY------------------" << endl; 01270 01271 Q_ASSERT( m_status == Running ); 01272 connect( this, SIGNAL( done( const QString & ) ), 01273 this, SLOT( slotModalDone( const QString & ) ) ); 01274 QObject::connect( this, SIGNAL( corrected( const QString&, const QString&, unsigned int ) ), 01275 this, SLOT( slotSpellCheckerCorrected( const QString&, const QString &, unsigned int ) ) ); 01276 QObject::connect( this, SIGNAL( death() ), 01277 this, SLOT( slotModalSpellCheckerFinished( ) ) ); 01278 check( modaltext ); 01279 } 01280 01281 void KOISpell::slotModalDone( const QString &/*_buffer*/ ) 01282 { 01283 //kdDebug(30006) << "MODAL DONE " << _buffer << endl; 01284 //modaltext = _buffer; 01285 cleanUp(); 01286 01287 //kdDebug(30006) << "ABOUT TO EXIT LOOP" << endl; 01288 //qApp->exit_loop(); 01289 01290 slotModalSpellCheckerFinished(); 01291 } 01292 01293 void KOISpell::slotModalSpellCheckerFinished( ) 01294 { 01295 modalreturn=(int)this->status(); 01296 } 01297 01298 01299 void KOISpell::initialize( QWidget *_parent, const QString &_caption, 01300 QObject *obj, const char *slot, KOSpellConfig *_ksc, 01301 bool _progressbar, bool _modal ) 01302 { 01303 m_ready = false; 01304 m_bIgnoreUpperWords=false; 01305 m_bIgnoreTitleCase=false; 01306 01307 autoDelete = false; 01308 modaldlg = _modal; 01309 progressbar = _progressbar; 01310 01311 proc=0; 01312 ksdlg=0; 01313 01314 texmode=dlgon=false; 01315 01316 dialogsetup = false; 01317 progres=10; 01318 curprog=0; 01319 01320 dialogwillprocess=false; 01321 dialog3slot=""; 01322 01323 personaldict=false; 01324 dlgresult=-1; 01325 01326 caption=_caption; 01327 01328 parent=_parent; 01329 01330 trystart=0; 01331 maxtrystart=2; 01332 01333 if ( obj && slot ) 01334 // caller wants to know when kspell is ready 01335 connect (this, SIGNAL (ready(KOSpell *)), obj, slot); 01336 else 01337 // Hack for modal spell checking 01338 connect (this, SIGNAL (ready(KOSpell *)), this, SLOT( slotModalReady() ) ); 01339 proc=new KProcIO(codec); 01340 01341 startIspell(); 01342 } 01343 01344 // This is retarded, if you don't get it, don't worry 01345 // it's me working around 999999999 problems 01346 void qt_enter_modal( QWidget *widget ); 01347 void qt_leave_modal( QWidget *widget ); 01348 void KOISpell::enter_loop() 01349 { 01350 QWidget dummy(0,0,WType_Dialog | WShowModal); 01351 dummy.setFocusPolicy( QWidget::NoFocus ); 01352 qt_enter_modal(&dummy); 01353 qApp->enter_loop(); 01354 qt_leave_modal(&dummy); 01355 } 01356 01357 void KOISpell::slotSynchronousReady() 01358 { 01359 qApp->exit_loop(); 01360 }
KDE Logo
This file is part of the documentation for lib Library Version 1.3.5.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Nov 17 06:54:16 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003