KDE3Support
k3spell.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 1997 David Sweet <dsweet@kde.org> 00003 Copyright (C) 2000-2001 Wolfram Diestel <wolfram@steloj.de> 00004 Copyright (C) 2003 Zack Rusin <zack@kde.org> 00005 Copyright (C) 2007-2008 Kevin Kofler <Kevin@tigcc.ticalc.org> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License version 2 as published by the Free Software Foundation. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include "k3spell.h" 00023 00024 #include <config.h> 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> // atoi 00032 00033 #ifdef HAVE_STRINGS_H 00034 #include <strings.h> 00035 #endif 00036 00037 00038 #include <QtGui/QApplication> 00039 #include <QtCore/QTextCodec> 00040 #include <QtCore/QTimer> 00041 00042 #include <kmessagebox.h> 00043 #include <kdebug.h> 00044 #include <klocale.h> 00045 #include "k3sconfig.h" 00046 #include "k3spelldlg.h" 00047 #include <kprocess.h> 00048 #include <QTextStream> 00049 00050 #define MAXLINELENGTH 10000 00051 #undef IGNORE //fix possible conflict 00052 00053 enum { 00054 GOOD= 0, 00055 IGNORE= 1, 00056 REPLACE= 2, 00057 MISTAKE= 3 00058 }; 00059 00060 enum checkMethod { Method1 = 0, Method2 }; 00061 00062 struct BufferedWord 00063 { 00064 checkMethod method; 00065 QString word; 00066 bool useDialog; 00067 bool suggest; 00068 }; 00069 00070 class K3Spell::K3SpellPrivate 00071 { 00072 public: 00073 bool endOfResponse; 00074 bool m_bIgnoreUpperWords; 00075 bool m_bIgnoreTitleCase; 00076 bool m_bNoMisspellingsEncountered; 00077 SpellerType type; 00078 K3Spell* suggestSpell; 00079 bool checking; 00080 QList<BufferedWord> unchecked; 00081 QTimer *checkNextTimer; 00082 bool aspellV6; 00083 QTextCodec* m_codec; 00084 QString convertQByteArray( const QByteArray& b ) 00085 { 00086 QTextCodec* originalCodec = QTextCodec::codecForCStrings(); 00087 QTextCodec::setCodecForCStrings( m_codec ); 00088 QString s( b ); 00089 QTextCodec::setCodecForCStrings( originalCodec ); 00090 return s; 00091 } 00092 QByteArray convertQString( const QString& s ) 00093 { 00094 QTextCodec* originalCodec = QTextCodec::codecForCStrings(); 00095 QTextCodec::setCodecForCStrings( m_codec ); 00096 QByteArray b = s.toAscii(); 00097 QTextCodec::setCodecForCStrings( originalCodec ); 00098 return b; 00099 } 00100 }; 00101 00102 //TODO 00103 //Parse stderr output 00104 //e.g. -- invalid dictionary name 00105 00106 /* 00107 Things to put in K3SpellConfigDlg: 00108 make root/affix combinations that aren't in the dictionary (-m) 00109 don't generate any affix/root combinations (-P) 00110 Report run-together words with missing blanks as spelling errors. (-B) 00111 default dictionary (-d [dictionary]) 00112 personal dictionary (-p [dictionary]) 00113 path to ispell -- NO: ispell should be in $PATH 00114 */ 00115 00116 00117 // Connects a slot to KProcess's output signal 00118 #define OUTPUT(x) (connect (proc, SIGNAL (readyReadStandardOutput()), this, SLOT (x()))) 00119 00120 // Disconnect a slot from... 00121 #define NOOUTPUT(x) (disconnect (proc, SIGNAL (readyReadStandardOutput()), this, SLOT (x()))) 00122 00123 00124 00125 K3Spell::K3Spell( QWidget *_parent, const QString &_caption, 00126 QObject *obj, const char *slot, K3SpellConfig *_ksc, 00127 bool _progressbar, bool _modal ) 00128 { 00129 initialize( _parent, _caption, obj, slot, _ksc, 00130 _progressbar, _modal, Text ); 00131 } 00132 00133 K3Spell::K3Spell( QWidget *_parent, const QString &_caption, 00134 QObject *obj, const char *slot, K3SpellConfig *_ksc, 00135 bool _progressbar, bool _modal, SpellerType type ) 00136 { 00137 initialize( _parent, _caption, obj, slot, _ksc, 00138 _progressbar, _modal, type ); 00139 } 00140 00141 K3Spell::spellStatus K3Spell::status() const 00142 { 00143 return m_status; 00144 } 00145 00146 void K3Spell::hide() { ksdlg->hide(); } 00147 00148 QStringList K3Spell::suggestions() const 00149 { 00150 return sugg; 00151 } 00152 00153 int K3Spell::dlgResult () const 00154 { 00155 return dlgresult; 00156 } 00157 00158 int K3Spell::heightDlg() const { return ksdlg->height(); } 00159 int K3Spell::widthDlg() const { return ksdlg->width(); } 00160 00161 QString K3Spell::intermediateBuffer() const 00162 { 00163 return K3Spell::newbuffer; 00164 } 00165 00166 // Check if aspell is at least version 0.6 00167 static bool determineASpellV6() 00168 { 00169 QString result; 00170 FILE *fs = popen("aspell -v", "r"); 00171 if (fs) 00172 { 00173 // Close textstream before we close fs 00174 { 00175 QTextStream ts(fs, QIODevice::ReadOnly); 00176 result = ts.readAll().trimmed(); 00177 } 00178 pclose(fs); 00179 } 00180 00181 QRegExp rx("Aspell (\\d.\\d)"); 00182 if (rx.indexIn(result) != -1) 00183 { 00184 float version = rx.cap(1).toFloat(); 00185 return (version >= 0.6); 00186 } 00187 return false; 00188 } 00189 00190 00191 void 00192 K3Spell::startIspell() 00193 //trystart = {0,1,2} 00194 { 00195 if ((trystart == 0) && (ksconfig->client() == KS_CLIENT_ASPELL)) 00196 d->aspellV6 = determineASpellV6(); 00197 00198 kDebug(750) << "Try #" << trystart; 00199 00200 if ( trystart > 0 ) { 00201 proc->reset(); 00202 } 00203 00204 switch ( ksconfig->client() ) 00205 { 00206 case KS_CLIENT_ISPELL: 00207 *proc << "ispell"; 00208 kDebug(750) << "Using ispell"; 00209 break; 00210 case KS_CLIENT_ASPELL: 00211 *proc << "aspell"; 00212 kDebug(750) << "Using aspell"; 00213 break; 00214 case KS_CLIENT_HSPELL: 00215 *proc << "hspell"; 00216 kDebug(750) << "Using hspell"; 00217 break; 00218 case KS_CLIENT_ZEMBEREK: 00219 *proc << "zpspell"; 00220 kDebug(750) << "Using zemberek(zpspell)"; 00221 break; 00222 case KS_CLIENT_HUNSPELL: 00223 *proc << "hunspell"; 00224 kDebug(750) << "Using hunspell"; 00225 break; 00226 } 00227 00228 // Hunspell doesn't need all of these options, but it'll ignore those it doesn't understand. 00229 if ( ksconfig->client() == KS_CLIENT_ISPELL || ksconfig->client() == KS_CLIENT_ASPELL || ksconfig->client() == KS_CLIENT_HUNSPELL ) 00230 { 00231 *proc << "-a" << "-S"; 00232 00233 switch ( d->type ) 00234 { 00235 case HTML: 00236 //Debian uses an ispell version that has the -h option instead. 00237 //Not sure what they did, but the preferred spell checker 00238 //on that platform is aspell anyway, so use -H untill I'll come 00239 //up with something better. 00240 *proc << "-H"; 00241 break; 00242 case TeX: 00243 //same for aspell and ispell 00244 *proc << "-t"; 00245 break; 00246 case Nroff: 00247 //only ispell and hunspell support 00248 if ( ksconfig->client() == KS_CLIENT_ISPELL || ksconfig->client() == KS_CLIENT_HUNSPELL ) 00249 *proc << "-n"; 00250 break; 00251 case Text: 00252 default: 00253 //nothing 00254 break; 00255 } 00256 if (ksconfig->noRootAffix()) 00257 { 00258 *proc<<"-m"; 00259 } 00260 if (ksconfig->runTogether()) 00261 { 00262 *proc << "-B"; 00263 } 00264 else 00265 { 00266 *proc << "-C"; 00267 } 00268 00269 00270 if (trystart<2) 00271 { 00272 if (! ksconfig->dictionary().isEmpty()) 00273 { 00274 kDebug(750) << "using dictionary [" << ksconfig->dictionary() << "]"; 00275 *proc << "-d"; 00276 *proc << ksconfig->dictionary(); 00277 } 00278 } 00279 00280 //Note to potential debuggers: -Tlatin2 _is_ being added on the 00281 // _first_ try. But, some versions of ispell will fail with this 00282 // option, so k3spell tries again without it. That's why as 'ps -ax' 00283 // shows "ispell -a -S ..." withou the "-Tlatin2" option. 00284 00285 if ( ksconfig->client() == KS_CLIENT_HUNSPELL && trystart<1 ) { 00286 // Note: This sets I/O encoding. Hunspell correctly handles dictionary encoding != I/O encoding. 00287 // It will be faster if the I/O encoding matches the dictionary encoding, but using UTF-8 is always safe. 00288 switch ( ksconfig->encoding() ) 00289 { 00290 case KS_E_LATIN1: 00291 *proc << "-i" << "ISO-8859-1"; 00292 break; 00293 case KS_E_LATIN2: 00294 *proc << "-i" << "ISO-8859-2"; 00295 break; 00296 case KS_E_LATIN3: 00297 *proc << "-i" << "ISO-8859-3"; 00298 break; 00299 case KS_E_LATIN4: 00300 *proc << "-i" << "ISO-8859-4"; 00301 break; 00302 case KS_E_LATIN5: 00303 *proc << "-i" << "ISO-8859-5"; 00304 break; 00305 case KS_E_LATIN7: 00306 *proc << "-i" << "ISO-8859-7"; 00307 break; 00308 case KS_E_LATIN8: 00309 *proc << "-i" << "ISO-8859-8"; 00310 break; 00311 case KS_E_LATIN9: 00312 *proc << "-i" << "ISO-8859-9"; 00313 break; 00314 case KS_E_LATIN13: 00315 *proc << "-i" << "ISO-8859-13"; 00316 break; 00317 case KS_E_LATIN15: 00318 *proc << "-i" << "ISO-8859-15"; 00319 break; 00320 case KS_E_UTF8: 00321 *proc << "-i" << "UTF-8"; 00322 break; 00323 case KS_E_KOI8R: 00324 *proc << "-i" << "KOI8-R"; 00325 break; 00326 case KS_E_KOI8U: 00327 *proc << "-i" << "KOI8-U"; 00328 break; 00329 case KS_E_CP1251: 00330 *proc << "-i" << "CP1251"; 00331 break; 00332 case KS_E_CP1255: 00333 *proc << "-i" << "CP1255"; 00334 break; 00335 default: 00336 break; 00337 } 00338 } else if ( trystart<1 ) { 00339 switch ( ksconfig->encoding() ) 00340 { 00341 case KS_E_LATIN1: 00342 *proc << "-Tlatin1"; 00343 break; 00344 case KS_E_LATIN2: 00345 *proc << "-Tlatin2"; 00346 break; 00347 case KS_E_LATIN3: 00348 *proc << "-Tlatin3"; 00349 break; 00350 00351 // add the other charsets here 00352 case KS_E_LATIN4: 00353 case KS_E_LATIN5: 00354 case KS_E_LATIN7: 00355 case KS_E_LATIN8: 00356 case KS_E_LATIN9: 00357 case KS_E_LATIN13: 00358 // will work, if this is the default charset in the dictionary 00359 kError(750) << "charsets ISO-8859-4, -5, -7, -8, -9 and -13 not supported yet" << endl; 00360 break; 00361 case KS_E_LATIN15: // ISO-8859-15 (Latin 9) 00362 if (ksconfig->client() == KS_CLIENT_ISPELL) 00363 { 00364 /* 00365 * As far as I know, there are no ispell dictionary using ISO-8859-15 00366 * but users have the tendency to select this encoding instead of ISO-8859-1 00367 * So put ispell in ISO-8859-1 (Latin 1) mode. 00368 */ 00369 *proc << "-Tlatin1"; 00370 } 00371 else 00372 kError(750) << "ISO-8859-15 not supported for aspell yet." << endl; 00373 break; 00374 case KS_E_UTF8: 00375 *proc << "-Tutf8"; 00376 if (ksconfig->client() == KS_CLIENT_ASPELL) 00377 *proc << "--encoding=utf-8"; 00378 break; 00379 case KS_E_KOI8U: 00380 *proc << "-w'"; // add ' as a word char 00381 break; 00382 default: 00383 break; 00384 } 00385 } 00386 00387 // -a : pipe mode 00388 // -S : sort suggestions by probable correctness 00389 } 00390 else // hspell and Zemberek(zpspell) doesn't need all the rest of the options 00391 *proc << "-a"; 00392 00393 if (trystart == 0) //don't connect these multiple times 00394 { 00395 connect( proc, SIGNAL(readyReadStandardError()), 00396 this, SLOT(ispellErrors()) ); 00397 00398 connect( proc, SIGNAL(finished(int, QProcess::ExitStatus)), 00399 this, SLOT(ispellExit ()) ); 00400 00401 proc->setOutputChannelMode( KProcess::SeparateChannels ); 00402 proc->setNextOpenMode( QIODevice::ReadWrite | QIODevice::Text ); 00403 00404 OUTPUT(K3Spell2); 00405 } 00406 00407 proc->start(); 00408 if ( !proc->waitForStarted() ) 00409 { 00410 m_status = Error; 00411 QTimer::singleShot( 0, this, SLOT(emitDeath())); 00412 } 00413 } 00414 00415 void 00416 K3Spell::ispellErrors( ) 00417 { 00418 // buffer[buflen-1] = '\0'; 00419 // kDebug(750) << "ispellErrors [" << buffer << "]\n"; 00420 } 00421 00422 void K3Spell::K3Spell2( ) 00423 00424 { 00425 QString line; 00426 00427 kDebug(750) << "K3Spell::K3Spell2"; 00428 00429 trystart = maxtrystart; //We've officially started ispell and don't want 00430 //to try again if it dies. 00431 00432 QByteArray data; 00433 qint64 read = proc->readLine(data.data(),data.count()); 00434 if ( read == -1 ) 00435 { 00436 QTimer::singleShot( 0, this, SLOT(emitDeath()) ); 00437 return; 00438 } 00439 line = d->convertQByteArray( data ); 00440 00441 if ( !line.startsWith('@') ) //@ indicates that ispell is working fine 00442 { 00443 QTimer::singleShot( 0, this, SLOT(emitDeath()) ); 00444 return; 00445 } 00446 00447 //We want to recognize KDE in any text! 00448 if ( !ignore("kde") ) 00449 { 00450 kDebug(750) << "@KDE was false"; 00451 QTimer::singleShot( 0, this, SLOT(emitDeath()) ); 00452 return; 00453 } 00454 00455 //We want to recognize linux in any text! 00456 if ( !ignore("linux") ) 00457 { 00458 kDebug(750) << "@Linux was false"; 00459 QTimer::singleShot( 0, this, SLOT(emitDeath()) ); 00460 return; 00461 } 00462 00463 NOOUTPUT( K3Spell2 ); 00464 00465 m_status = Running; 00466 emit ready( this ); 00467 } 00468 00469 void 00470 K3Spell::setUpDialog( bool reallyuseprogressbar ) 00471 { 00472 if ( dialogsetup ) 00473 return; 00474 00475 //Set up the dialog box 00476 ksdlg = new K3SpellDlg( parent, progressbar && reallyuseprogressbar, modaldlg ); 00477 ksdlg->setCaption( caption ); 00478 00479 connect( ksdlg, SIGNAL(command(int)), 00480 this, SLOT(slotStopCancel(int)) ); 00481 connect( this, SIGNAL(progress(unsigned int)), 00482 ksdlg, SLOT(slotProgress(unsigned int)) ); 00483 00484 if ( modaldlg ) 00485 ksdlg->setFocus(); 00486 dialogsetup = true; 00487 } 00488 00489 bool K3Spell::addPersonal( const QString & word ) 00490 { 00491 QString qs = word.simplified(); 00492 00493 //we'll let ispell do the work here b/c we can 00494 if ( qs.indexOf(' ') != -1 || qs.isEmpty() ) // make sure it's a _word_ 00495 return false; 00496 00497 qs.prepend( "*" ); 00498 personaldict = true; 00499 00500 return proc->write( d->convertQString( qs ) ); 00501 } 00502 00503 bool K3Spell::writePersonalDictionary() 00504 { 00505 return proc->write( QByteArray( "#" ) ); 00506 } 00507 00508 bool K3Spell::ignore( const QString & word ) 00509 { 00510 QString qs = word.simplified(); 00511 00512 //we'll let ispell do the work here b/c we can 00513 if ( qs.indexOf (' ') != -1 || qs.isEmpty() ) // make sure it's a _word_ 00514 return false; 00515 00516 qs.prepend( "@" ); 00517 00518 return proc->write( d->convertQString( qs ) ); 00519 } 00520 00521 bool 00522 K3Spell::cleanFputsWord( const QString & s ) 00523 { 00524 QString qs(s); 00525 bool empty = true; 00526 00527 for( int i = 0; i < qs.length(); i++ ) 00528 { 00529 //we need some punctuation for ornaments 00530 if ( (qs[i] != '\'' && qs[i] != '\"' && qs[i] != '-' 00531 && qs[i].isPunct()) || qs[i].isSpace() ) 00532 { 00533 qs.remove(i,1); 00534 i--; 00535 } else { 00536 if ( qs[i].isLetter() ) 00537 empty=false; 00538 } 00539 } 00540 00541 // don't check empty words, otherwise synchronization will lost 00542 if (empty) 00543 return false; 00544 00545 return proc->write( d->convertQString( QString('^'+qs+'\n') ) ); 00546 } 00547 00548 bool 00549 K3Spell::cleanFputs( const QString & s ) 00550 { 00551 QString qs(s); 00552 unsigned l = qs.length(); 00553 00554 // some uses of '$' (e.g. "$0") cause ispell to skip all following text 00555 for( unsigned int i = 0; i < l; ++i ) 00556 { 00557 if( qs[i] == '$' ) 00558 qs[i] = ' '; 00559 } 00560 00561 if ( l<MAXLINELENGTH ) 00562 { 00563 if ( qs.isEmpty() ) 00564 qs=""; 00565 return proc->write( d->convertQString('^'+qs+'\n') ); 00566 } 00567 else 00568 return proc->write( d->convertQString( "^\n" ) ); 00569 } 00570 00571 bool K3Spell::checkWord( const QString & buffer, bool _usedialog ) 00572 { 00573 if (d->checking) { // don't check multiple words simultaneously 00574 BufferedWord bufferedWord; 00575 bufferedWord.method = Method1; 00576 bufferedWord.word = buffer; 00577 bufferedWord.useDialog = _usedialog; 00578 d->unchecked.append( bufferedWord ); 00579 return true; 00580 } 00581 d->checking = true; 00582 QString qs = buffer.simplified(); 00583 00584 if ( qs.indexOf (' ') != -1 || qs.isEmpty() ) { // make sure it's a _word_ 00585 d->checkNextTimer->setInterval(0); 00586 d->checkNextTimer->setSingleShot(true); 00587 d->checkNextTimer->start(); 00588 return false; 00589 } 00591 dialog3slot = SLOT(checkWord3()); 00592 00593 usedialog = _usedialog; 00594 setUpDialog( false ); 00595 if ( _usedialog ) 00596 { 00597 emitProgress(); 00598 } 00599 else 00600 ksdlg->hide(); 00601 00602 QByteArray data; 00603 while (proc->readLine( data.data(), data.count() ) != -1 ) 00604 ; // eat spurious blanks 00605 00606 OUTPUT(checkWord2); 00607 // connect (this, SIGNAL (dialog3()), this, SLOT (checkWord3())); 00608 00609 proc->write( d->convertQString( QString( "%" ) ) ); // turn off terse mode 00610 proc->write( d->convertQString( buffer ) ); // send the word to ispell 00611 00612 return true; 00613 } 00614 00615 bool K3Spell::checkWord( const QString & buffer, bool _usedialog, bool suggest ) 00616 { 00617 if (d->checking) { // don't check multiple words simultaneously 00618 BufferedWord bufferedWord; 00619 bufferedWord.method = Method2; 00620 bufferedWord.word = buffer; 00621 bufferedWord.useDialog = _usedialog; 00622 bufferedWord.suggest = suggest; 00623 d->unchecked.append( bufferedWord ); 00624 return true; 00625 } 00626 d->checking = true; 00627 QString qs = buffer.simplified(); 00628 00629 if ( qs.indexOf (' ') != -1 || qs.isEmpty() ) { // make sure it's a _word_ 00630 d->checkNextTimer->setInterval(0); 00631 d->checkNextTimer->setSingleShot(true); 00632 d->checkNextTimer->start(); 00633 return false; 00634 } 00635 00637 if ( !suggest ) { 00638 dialog3slot = SLOT(checkWord3()); 00639 usedialog = _usedialog; 00640 setUpDialog( false ); 00641 if ( _usedialog ) 00642 { 00643 emitProgress(); 00644 } 00645 else 00646 ksdlg->hide(); 00647 } 00648 00649 QByteArray data; 00650 while (proc->readLine( data.data(), data.count() ) != -1 ) ; // eat spurious blanks 00651 00652 OUTPUT(checkWord2); 00653 // connect (this, SIGNAL (dialog3()), this, SLOT (checkWord3())); 00654 00655 proc->write( d->convertQString( QString( "%" ) ) ); // turn off terse mode 00656 proc->write( d->convertQString( buffer ) ); // send the word to ispell 00657 00658 return true; 00659 } 00660 00661 void K3Spell::checkWord2( ) 00662 { 00663 QString word; 00664 QString line; 00665 line = d->convertQByteArray( proc->readLine() ); //get ispell's response 00666 00667 /* ispell man page: "Each sentence of text input is terminated with an 00668 additional blank line, indicating that ispell has completed processing 00669 the input line." 00670 <sanders> 00671 But there can be multiple lines returned in the case of an error, 00672 in this case we should consume all the output given otherwise spell checking 00673 can get out of sync. 00674 </sanders> 00675 */ 00676 QByteArray data; 00677 while (proc->readLine( data.data(), data.count() ) != -1 ) ; // eat spurious blanks 00678 NOOUTPUT(checkWord2); 00679 00680 bool mistake = ( parseOneResponse(line, word, sugg) == MISTAKE ); 00681 if ( mistake && usedialog ) 00682 { 00683 cwword = word; 00684 dialog( word, sugg, SLOT(checkWord3()) ); 00685 d->checkNextTimer->setInterval(0); 00686 d->checkNextTimer->setSingleShot(true); 00687 d->checkNextTimer->start(); 00688 return; 00689 } 00690 else if( mistake ) 00691 { 00692 emit misspelling( word, sugg, lastpos ); 00693 } 00694 00695 //emits a "corrected" signal _even_ if no change was made 00696 //so that the calling program knows when the check is complete 00697 emit corrected( word, word, 0L ); 00698 d->checkNextTimer->setInterval(0); 00699 d->checkNextTimer->setSingleShot(true); 00700 d->checkNextTimer->start(); 00701 } 00702 00703 void K3Spell::checkNext() 00704 { 00705 // Queue words to prevent kspell from turning into a fork bomb 00706 d->checking = false; 00707 if (!d->unchecked.empty()) { 00708 BufferedWord buf = d->unchecked.front(); 00709 d->unchecked.pop_front(); 00710 00711 if (buf.method == Method1) 00712 checkWord( buf.word, buf.useDialog ); 00713 else 00714 checkWord( buf.word, buf.useDialog, buf.suggest ); 00715 } 00716 } 00717 00718 void K3Spell::suggestWord() 00719 { 00720 QString word; 00721 QString line; 00722 line = d->convertQByteArray( proc->readLine() ); //get ispell's response 00723 00724 /* ispell man page: "Each sentence of text input is terminated with an 00725 additional blank line, indicating that ispell has completed processing 00726 the input line." */ 00727 QByteArray data; 00728 while (proc->readLine( data.data(), data.count() ) != -1 ) ; // eat spurious blanks 00729 00730 NOOUTPUT(checkWord2); 00731 00732 bool mistake = ( parseOneResponse(line, word, sugg) == MISTAKE ); 00733 if ( mistake && usedialog ) 00734 { 00735 cwword=word; 00736 dialog( word, sugg, SLOT(checkWord3()) ); 00737 return; 00738 } 00739 } 00740 00741 void K3Spell::checkWord3() 00742 { 00743 disconnect( this, SIGNAL(dialog3()), this, SLOT(checkWord3()) ); 00744 00745 emit corrected( cwword, replacement(), 0L ); 00746 } 00747 00748 QString K3Spell::funnyWord( const QString & word ) 00749 // composes a guess from ispell to a readable word 00750 // e.g. "re+fry-y+ies" -> "refries" 00751 { 00752 QString qs; 00753 for( int i=0; i<word.size(); i++ ) 00754 { 00755 if (word [i]=='+') 00756 continue; 00757 if (word [i]=='-') 00758 { 00759 QString shorty; 00760 int j, k; 00761 00762 for( j = i+1; j < word.size() && word[j] != '+' && word[j] != '-'; j++ ) 00763 shorty += word[j]; 00764 00765 i = j-1; 00766 00767 if ( !( k = qs.lastIndexOf(shorty) ) || k != -1 ) 00768 qs.remove( k, shorty.length() ); 00769 else 00770 { 00771 qs += '-'; 00772 qs += shorty; //it was a hyphen, not a '-' from ispell 00773 } 00774 } 00775 else 00776 qs += word[i]; 00777 } 00778 00779 return qs; 00780 } 00781 00782 00783 int K3Spell::parseOneResponse( const QString &buffer, QString &word, QStringList & sugg ) 00784 // buffer is checked, word and sugg are filled in 00785 // returns 00786 // GOOD if word is fine 00787 // IGNORE if word is in ignorelist 00788 // REPLACE if word is in replacelist 00789 // MISTAKE if word is misspelled 00790 { 00791 word = ""; 00792 posinline=0; 00793 00794 sugg.clear(); 00795 00796 if ( buffer[0] == '*' || buffer[0] == '+' || buffer[0] == '-' ) 00797 { 00798 return GOOD; 00799 } 00800 00801 if ( buffer[0] == '&' || buffer[0] == '?' || buffer[0] == '#' ) 00802 { 00803 int i,j; 00804 00805 00806 word = buffer.mid( 2, buffer.indexOf( ' ', 3 ) -2 ); 00807 //check() needs this 00808 orig=word; 00809 00810 if( d->m_bIgnoreTitleCase && word == word.toUpper() ) 00811 return IGNORE; 00812 00813 if( d->m_bIgnoreUpperWords && word[0] == word[0].toUpper() ) 00814 { 00815 QString text = word[0] + word.right( word.length()-1 ).toLower(); 00816 if( text == word ) 00817 return IGNORE; 00818 } 00819 00821 //We don't take advantage of ispell's ignore function because 00822 //we can't interrupt ispell's output (when checking a large 00823 //buffer) to add a word to _it's_ ignore-list. 00824 if ( ignorelist.indexOf( word.toLower() ) != -1 ) 00825 return IGNORE; 00826 00828 QString qs2; 00829 00830 if ( buffer.indexOf( ':' ) != -1 ) 00831 qs2 = buffer.left( buffer.indexOf(':') ); 00832 else 00833 qs2 = buffer; 00834 00835 posinline = qs2.right( qs2.length()-qs2.lastIndexOf(' ') ).toInt()-1; 00836 00838 QStringList::Iterator it = replacelist.begin(); 00839 for( ;it != replacelist.end(); ++it, ++it ) // Skip two entries at a time. 00840 { 00841 if ( word == *it ) // Word matches 00842 { 00843 ++it; 00844 word = *it; // Replace it with the next entry 00845 return REPLACE; 00846 } 00847 } 00848 00850 if ( buffer[0] != '#' ) 00851 { 00852 QString qs = buffer.mid( buffer.indexOf(':')+2, buffer.length() ); 00853 qs += ','; 00854 sugg.clear(); 00855 i = j = 0; 00856 00857 while( i < qs.length() ) 00858 { 00859 QString temp = qs.mid( i, (j=qs.indexOf(',',i)) - i ); 00860 sugg.append( funnyWord(temp) ); 00861 00862 i=j+2; 00863 } 00864 } 00865 00866 if ( (sugg.count()==1) && (sugg.first() == word) ) 00867 return GOOD; 00868 00869 return MISTAKE; 00870 } 00871 00872 if ( buffer.isEmpty() ) { 00873 kDebug(750) << "Got an empty response: ignoring"; 00874 return GOOD; 00875 } 00876 00877 kError(750) << "HERE?: [" << buffer << "]" << endl; 00878 kError(750) << "Please report this to zack@kde.org" << endl; 00879 kError(750) << "Thank you!" << endl; 00880 00881 emit done( false ); 00882 emit done( K3Spell::origbuffer ); 00883 return MISTAKE; 00884 } 00885 00886 bool K3Spell::checkList (QStringList *_wordlist, bool _usedialog) 00887 // prepare check of string list 00888 { 00889 wordlist=_wordlist; 00890 if ((totalpos=wordlist->count())==0) 00891 return false; 00892 wlIt = wordlist->begin(); 00893 usedialog=_usedialog; 00894 00895 // prepare the dialog 00896 setUpDialog(); 00897 00898 //set the dialog signal handler 00899 dialog3slot = SLOT (checkList4 ()); 00900 00901 proc->write(QByteArray( '%' ) ); // turn off terse mode & check one word at a time 00902 00903 //lastpos now counts which *word number* we are at in checkListReplaceCurrent() 00904 lastpos = -1; 00905 checkList2(); 00906 00907 // when checked, KProcess calls checkList3a 00908 OUTPUT(checkList3a); 00909 00910 return true; 00911 } 00912 00913 void K3Spell::checkList2 () 00914 // send one word from the list to KProcess 00915 // invoked first time by checkList, later by checkListReplaceCurrent and checkList4 00916 { 00917 // send next word 00918 if (wlIt != wordlist->end()) 00919 { 00920 kDebug(750) << "KS::cklist2 " << lastpos << ": " << *wlIt; 00921 00922 d->endOfResponse = false; 00923 bool put; 00924 lastpos++; offset=0; 00925 put = cleanFputsWord (*wlIt); 00926 ++wlIt; 00927 00928 // when cleanFPutsWord failed (e.g. on empty word) 00929 // try next word; may be this is not good for other 00930 // problems, because this will make read the list up to the end 00931 if (!put) { 00932 checkList2(); 00933 } 00934 } 00935 else 00936 // end of word list 00937 { 00938 NOOUTPUT(checkList3a); 00939 ksdlg->hide(); 00940 emit done(true); 00941 } 00942 } 00943 00944 void K3Spell::checkList3a () 00945 // invoked by KProcess, when data from ispell are read 00946 { 00947 //kDebug(750) << "start of checkList3a"; 00948 00949 // don't read more data, when dialog is waiting 00950 // for user interaction 00951 if ( dlgon ) { 00952 //kDebug(750) << "dlgon: don't read more data"; 00953 return; 00954 } 00955 00956 int e; 00957 qint64 tempe; 00958 00959 QString word; 00960 QString line; 00961 00962 do 00963 { 00964 QByteArray data; 00965 tempe = proc->readLine( data.data(), data.count() ); //get ispell's response 00966 00967 //kDebug(750) << "checkList3a: read bytes [" << tempe << "]"; 00968 line = d->convertQByteArray( data ); 00969 00970 if ( tempe == 0 ) { 00971 d->endOfResponse = true; 00972 //kDebug(750) << "checkList3a: end of resp"; 00973 } else if ( tempe>0 ) { 00974 if ( (e=parseOneResponse( line, word, sugg ) ) == MISTAKE || 00975 e==REPLACE ) 00976 { 00977 dlgresult=-1; 00978 00979 if ( e == REPLACE ) 00980 { 00981 QString old = *(--wlIt); ++wlIt; 00982 dlgreplacement = word; 00983 checkListReplaceCurrent(); 00984 // inform application 00985 emit corrected( old, *(--wlIt), lastpos ); ++wlIt; 00986 } 00987 else if( usedialog ) 00988 { 00989 cwword = word; 00990 dlgon = true; 00991 // show the dialog 00992 dialog( word, sugg, SLOT(checkList4()) ); 00993 return; 00994 } 00995 else 00996 { 00997 d->m_bNoMisspellingsEncountered = false; 00998 emit misspelling( word, sugg, lastpos ); 00999 } 01000 } 01001 01002 } 01003 emitProgress (); //maybe 01004 01005 // stop when empty line or no more data 01006 } while (tempe > 0); 01007 01008 //kDebug(750) << "checkList3a: exit loop with [" << tempe << "]"; 01009 01010 // if we got an empty line, t.e. end of ispell/aspell response 01011 // and the dialog isn't waiting for user interaction, send next word 01012 if (d->endOfResponse && !dlgon) { 01013 //kDebug(750) << "checkList3a: send next word"; 01014 checkList2(); 01015 } 01016 } 01017 01018 void K3Spell::checkListReplaceCurrent() 01019 { 01020 01021 // go back to misspelled word 01022 wlIt--; 01023 01024 QString s = *wlIt; 01025 s.replace(posinline+offset,orig.length(),replacement()); 01026 offset += replacement().length()-orig.length(); 01027 wordlist->insert (wlIt, s); 01028 wlIt = wordlist->erase (wlIt); 01029 // wlIt now points to the word after the repalced one 01030 01031 } 01032 01033 void K3Spell::checkList4 () 01034 // evaluate dialog return, when a button was pressed there 01035 { 01036 dlgon=false; 01037 QString old; 01038 01039 disconnect (this, SIGNAL (dialog3()), this, SLOT (checkList4())); 01040 01041 //others should have been processed by dialog() already 01042 switch (dlgresult) 01043 { 01044 case KS_REPLACE: 01045 case KS_REPLACEALL: 01046 kDebug(750) << "KS: cklist4: lastpos: " << lastpos; 01047 old = *(--wlIt); 01048 ++wlIt; 01049 // replace word 01050 checkListReplaceCurrent(); 01051 emit corrected( old, *(--wlIt), lastpos ); 01052 ++wlIt; 01053 break; 01054 case KS_CANCEL: 01055 ksdlg->hide(); 01056 emit done( false ); 01057 return; 01058 case KS_STOP: 01059 ksdlg->hide(); 01060 emit done( true ); 01061 return; 01062 case KS_CONFIG: 01063 ksdlg->hide(); 01064 emit done( false ); 01065 //check( origbuffer.mid( lastpos ), true ); 01066 //trystart = 0; 01067 //proc->disconnect(); 01068 //proc->kill(); 01069 //delete proc; 01070 //proc = new KProcess( codec ); 01071 //startIspell(); 01072 return; 01073 }; 01074 01075 // read more if there is more, otherwise send next word 01076 if (!d->endOfResponse) { 01077 //kDebug(750) << "checkList4: read more from response"; 01078 checkList3a(); 01079 } 01080 } 01081 01082 bool K3Spell::check( const QString &_buffer, bool _usedialog ) 01083 { 01084 QString qs; 01085 01086 usedialog = _usedialog; 01087 setUpDialog(); 01088 //set the dialog signal handler 01089 dialog3slot = SLOT(check3()); 01090 01091 kDebug(750) << "KS: check"; 01092 origbuffer = _buffer; 01093 if ( ( totalpos = origbuffer.length() ) == 0 ) 01094 { 01095 emit done( origbuffer ); 01096 return false; 01097 } 01098 01099 01100 // Torben: I corrected the \n\n problem directly in the 01101 // origbuffer since I got errors otherwise 01102 if ( !origbuffer.endsWith("\n\n" ) ) 01103 { 01104 if (origbuffer.at(origbuffer.length()-1)!='\n') 01105 { 01106 origbuffer+='\n'; 01107 origbuffer+='\n'; //shouldn't these be removed at some point? 01108 } 01109 else 01110 origbuffer+='\n'; 01111 } 01112 01113 newbuffer = origbuffer; 01114 01115 // KProcess calls check2 when read from ispell 01116 OUTPUT( check2 ); 01117 proc->write( QByteArray( "!" ) ); 01118 01119 //lastpos is a position in newbuffer (it has offset in it) 01120 offset = lastlastline = lastpos = lastline = 0; 01121 01122 emitProgress(); 01123 01124 // send first buffer line 01125 int i = origbuffer.indexOf( '\n', 0 ) + 1; 01126 qs = origbuffer.mid( 0, i ); 01127 cleanFputs( qs ); 01128 01129 lastline=i; //the character position, not a line number 01130 01131 if ( usedialog ) 01132 { 01133 emitProgress(); 01134 } 01135 else 01136 ksdlg->hide(); 01137 01138 return true; 01139 } 01140 01141 int K3Spell::lastPosition() const 01142 { 01143 return lastpos; 01144 } 01145 01146 01147 void K3Spell::check2() 01148 // invoked by KProcess when read from ispell 01149 { 01150 int e; 01151 qint64 tempe; 01152 QString word; 01153 QString line; 01154 static bool recursive = false; 01155 if (recursive && 01156 !ksdlg ) 01157 { 01158 return; 01159 } 01160 recursive = true; 01161 01162 do 01163 { 01164 QByteArray data; 01165 tempe = proc->readLine( data.data(), data.count() ); //get ispell's response 01166 line = d->convertQByteArray( data ); 01167 //kDebug(750) << "K3Spell::check2 (" << tempe << "b)"; 01168 01169 if ( tempe>0 ) 01170 { 01171 if ( ( e=parseOneResponse (line, word, sugg) )==MISTAKE || 01172 e==REPLACE) 01173 { 01174 dlgresult=-1; 01175 01176 // for multibyte encoding posinline needs correction 01177 if ((ksconfig->encoding() == KS_E_UTF8) && !d->aspellV6) { 01178 // kDebug(750) << "line: " << origbuffer.mid(lastlastline, 01179 // lastline-lastlastline) << endl; 01180 // kDebug(750) << "posinline uncorr: " << posinline; 01181 01182 // convert line to UTF-8, cut at pos, convert back to UCS-2 01183 // and get string length 01184 posinline = (QString::fromUtf8( 01185 origbuffer.mid(lastlastline,lastline-lastlastline).toUtf8(), 01186 posinline)).length(); 01187 // kDebug(750) << "posinline corr: " << posinline; 01188 } 01189 01190 lastpos = posinline+lastlastline+offset; 01191 01192 //orig is set by parseOneResponse() 01193 01194 if (e==REPLACE) 01195 { 01196 dlgreplacement=word; 01197 emit corrected( orig, replacement(), lastpos ); 01198 offset += replacement().length()-orig.length(); 01199 newbuffer.replace( lastpos, orig.length(), word ); 01200 } 01201 else //MISTAKE 01202 { 01203 cwword = word; 01204 //kDebug(750) << "(Before dialog) word=[" << word << "] cwword =[" << cwword << "]\n"; 01205 if ( usedialog ) { 01206 // show the word in the dialog 01207 dialog( word, sugg, SLOT(check3()) ); 01208 } else { 01209 // No dialog, just emit misspelling and continue 01210 d->m_bNoMisspellingsEncountered = false; 01211 emit misspelling( word, sugg, lastpos ); 01212 dlgresult = KS_IGNORE; 01213 check3(); 01214 } 01215 recursive = false; 01216 return; 01217 } 01218 } 01219 01220 } 01221 01222 emitProgress(); //maybe 01223 01224 } while( tempe>0 ); 01225 01226 if ( tempe == -1 ) { //we were called, but no data seems to be ready... 01227 // Make sure we don't get called directly again and make sure we do get 01228 // called when new data arrives. 01229 NOOUTPUT( check2 ); 01230 // proc->enableReadSignals(true); 01231 OUTPUT( check2 ); 01232 recursive = false; 01233 return; 01234 } 01235 01236 // proc->ackRead(); 01237 01238 //If there is more to check, then send another line to ISpell. 01239 if ( lastline < origbuffer.length() ) 01240 { 01241 int i; 01242 QString qs; 01243 01244 //kDebug(750) << "[EOL](" << tempe << ")[" << temp << "]"; 01245 01246 lastpos = (lastlastline=lastline) + offset; //do we really want this? 01247 i = origbuffer.indexOf('\n', lastline) + 1; 01248 qs = origbuffer.mid( lastline, i-lastline ); 01249 cleanFputs( qs ); 01250 lastline = i; 01251 recursive = false; 01252 return; 01253 } 01254 else 01255 //This is the end of it all 01256 { 01257 ksdlg->hide(); 01258 // kDebug(750) << "check2() done"; 01259 newbuffer.truncate( newbuffer.length()-2 ); 01260 emitProgress(); 01261 emit done( newbuffer ); 01262 } 01263 recursive = false; 01264 } 01265 01266 void K3Spell::check3 () 01267 // evaluates the return value of the dialog 01268 { 01269 disconnect (this, SIGNAL (dialog3()), this, SLOT (check3())); 01270 kDebug(750) << "check3 [" << cwword << "] [" << replacement() << "] " << dlgresult; 01271 01272 //others should have been processed by dialog() already 01273 switch (dlgresult) 01274 { 01275 case KS_REPLACE: 01276 case KS_REPLACEALL: 01277 offset+=replacement().length()-cwword.length(); 01278 newbuffer.replace (lastpos, cwword.length(), 01279 replacement()); 01280 emit corrected (dlgorigword, replacement(), lastpos); 01281 break; 01282 case KS_CANCEL: 01283 // kDebug(750) << "canceled\n"; 01284 ksdlg->hide(); 01285 emit done( origbuffer ); 01286 return; 01287 case KS_CONFIG: 01288 ksdlg->hide(); 01289 emit done( origbuffer ); 01290 KMessageBox::information( 0, i18n("You have to restart the dialog for changes to take effect") ); 01291 //check( origbuffer.mid( lastpos ), true ); 01292 return; 01293 case KS_STOP: 01294 ksdlg->hide(); 01295 //buffer=newbuffer); 01296 emitProgress(); 01297 emit done (newbuffer); 01298 return; 01299 }; 01300 01301 // proc->ackRead(); 01302 } 01303 01304 void 01305 K3Spell::slotStopCancel (int result) 01306 { 01307 if (dialogwillprocess) 01308 return; 01309 01310 kDebug(750) << "K3Spell::slotStopCancel [" << result << "]"; 01311 01312 if (result==KS_STOP || result==KS_CANCEL) 01313 if (!dialog3slot.isEmpty()) 01314 { 01315 dlgresult=result; 01316 connect (this, SIGNAL (dialog3()), this, dialog3slot.toAscii().constData()); 01317 emit dialog3(); 01318 } 01319 } 01320 01321 01322 void K3Spell::dialog( const QString & word, QStringList & sugg, const char *_slot ) 01323 { 01324 dlgorigword = word; 01325 01326 dialog3slot = _slot; 01327 dialogwillprocess = true; 01328 connect( ksdlg, SIGNAL(command(int)), this, SLOT(dialog2(int)) ); 01329 QString tmpBuf = newbuffer; 01330 kDebug(750)<<" position = "<<lastpos; 01331 01332 // extract a context string, replace all characters which might confuse 01333 // the RichText display and highlight the possibly wrong word 01334 QString marker( "_MARKER_" ); 01335 tmpBuf.replace( lastpos, word.length(), marker ); 01336 QString context = tmpBuf.mid(qMax(lastpos-18,0), 2*18+marker.length()); 01337 context.replace( '\n',QLatin1Char(' ')); 01338 context.replace( '<', QLatin1String("<") ); 01339 context.replace( '>', QLatin1String(">") ); 01340 context.replace( marker, QString::fromLatin1("<b>%1</b>").arg( word ) ); 01341 context = "<qt>" + context + "</qt>"; 01342 01343 ksdlg->init( word, &sugg, context ); 01344 d->m_bNoMisspellingsEncountered = false; 01345 emit misspelling( word, sugg, lastpos ); 01346 01347 emitProgress(); 01348 ksdlg->show(); 01349 } 01350 01351 QString K3Spell::replacement () const 01352 { 01353 return dlgreplacement; 01354 } 01355 01356 void K3Spell::dialog2( int result ) 01357 { 01358 QString qs; 01359 01360 disconnect( ksdlg, SIGNAL(command(int)), this, SLOT(dialog2(int)) ); 01361 dialogwillprocess = false; 01362 dlgresult = result; 01363 ksdlg->standby(); 01364 01365 dlgreplacement = ksdlg->replacement(); 01366 01367 //process result here 01368 switch ( dlgresult ) 01369 { 01370 case KS_IGNORE: 01371 emit ignoreword( dlgorigword ); 01372 break; 01373 case KS_IGNOREALL: 01374 // would be better to lower case only words with beginning cap 01375 ignorelist.prepend( dlgorigword.toLower() ); 01376 emit ignoreall( dlgorigword ); 01377 break; 01378 case KS_ADD: 01379 addPersonal( dlgorigword ); 01380 personaldict = true; 01381 emit addword( dlgorigword ); 01382 // adding to pesonal dict takes effect at the next line, not the current 01383 ignorelist.prepend( dlgorigword.toLower() ); 01384 break; 01385 case KS_REPLACEALL: 01386 { 01387 replacelist.append( dlgorigword ); 01388 QString _replacement = replacement(); 01389 replacelist.append( _replacement ); 01390 emit replaceall( dlgorigword , _replacement ); 01391 } 01392 break; 01393 case KS_SUGGEST: 01394 checkWord( ksdlg->replacement(), false, true ); 01395 return; 01396 break; 01397 } 01398 01399 connect( this, SIGNAL(dialog3()), this, dialog3slot.toAscii().constData() ); 01400 emit dialog3(); 01401 } 01402 01403 01404 K3Spell::~K3Spell() 01405 { 01406 delete proc; 01407 delete ksconfig; 01408 delete ksdlg; 01409 delete d->checkNextTimer; 01410 delete d; 01411 } 01412 01413 01414 K3SpellConfig K3Spell::ksConfig() const 01415 { 01416 ksconfig->setIgnoreList(ignorelist); 01417 ksconfig->setReplaceAllList(replacelist); 01418 return *ksconfig; 01419 } 01420 01421 void K3Spell::cleanUp() 01422 { 01423 if ( m_status == Cleaning ) 01424 return; // Ignore 01425 01426 if ( m_status == Running ) 01427 { 01428 if ( personaldict ) 01429 writePersonalDictionary(); 01430 m_status = Cleaning; 01431 } 01432 proc->closeWriteChannel(); 01433 } 01434 01435 void K3Spell::setAutoDelete(bool _autoDelete) 01436 { 01437 autoDelete = _autoDelete; 01438 } 01439 01440 void K3Spell::ispellExit() 01441 { 01442 kDebug() << "K3Spell::ispellExit() " << m_status; 01443 01444 if ( (m_status == Starting) && (trystart < maxtrystart) ) 01445 { 01446 trystart++; 01447 startIspell(); 01448 return; 01449 } 01450 01451 if ( m_status == Starting ) 01452 m_status = Error; 01453 else if (m_status == Cleaning) 01454 m_status = d->m_bNoMisspellingsEncountered ? FinishedNoMisspellingsEncountered : Finished; 01455 else if ( m_status == Running ) 01456 m_status = Crashed; 01457 else // Error, Finished, Crashed 01458 return; // Dead already 01459 01460 kDebug(750) << "Death"; 01461 QTimer::singleShot( 0, this, SLOT(emitDeath()) ); 01462 } 01463 01464 // This is always called from the event loop to make 01465 // sure that the receiver can safely delete the 01466 // K3Spell object. 01467 void K3Spell::emitDeath() 01468 { 01469 bool deleteMe = autoDelete; // Can't access object after next call! 01470 emit death(); 01471 if ( deleteMe ) 01472 deleteLater(); 01473 } 01474 01475 void K3Spell::setProgressResolution (unsigned int res) 01476 { 01477 progres=res; 01478 } 01479 01480 void K3Spell::emitProgress () 01481 { 01482 uint nextprog = (uint) (100.*lastpos/(double)totalpos); 01483 01484 if ( nextprog >= curprog ) 01485 { 01486 curprog = nextprog; 01487 emit progress( curprog ); 01488 } 01489 } 01490 01491 void K3Spell::moveDlg( int x, int y ) 01492 { 01493 QPoint pt( x,y ), pt2; 01494 pt2 = parent->mapToGlobal( pt ); 01495 ksdlg->move( pt2.x(),pt2.y() ); 01496 } 01497 01498 void K3Spell::setIgnoreUpperWords(bool _ignore) 01499 { 01500 d->m_bIgnoreUpperWords=_ignore; 01501 } 01502 01503 void K3Spell::setIgnoreTitleCase(bool _ignore) 01504 { 01505 d->m_bIgnoreTitleCase=_ignore; 01506 } 01507 // -------------------------------------------------- 01508 // Stuff for modal (blocking) spell checking 01509 // 01510 // Written by Torben Weis <weis@kde.org>. So please 01511 // send bug reports regarding the modal stuff to me. 01512 // -------------------------------------------------- 01513 01514 int 01515 K3Spell::modalCheck( QString& text ) 01516 { 01517 return modalCheck( text,0 ); 01518 } 01519 01520 int 01521 K3Spell::modalCheck( QString& text, K3SpellConfig* _kcs ) 01522 { 01523 modalreturn = 0; 01524 modaltext = text; 01525 01526 K3Spell* spell = new K3Spell( 0L, i18n("Spell Checker"), 0 , 01527 0, _kcs, true, true ); 01528 01529 while (spell->status()!=Finished) 01530 qApp->processEvents(); 01531 01532 text = modaltext; 01533 01534 delete spell; 01535 return modalreturn; 01536 } 01537 01538 void K3Spell::slotSpellCheckerCorrected( const QString & oldText, const QString & newText, unsigned int pos ) 01539 { 01540 modaltext=modaltext.replace(pos,oldText.length(),newText); 01541 } 01542 01543 01544 void K3Spell::slotModalReady() 01545 { 01546 //kDebug() << qApp->loopLevel(); 01547 //kDebug(750) << "MODAL READY------------------"; 01548 01549 Q_ASSERT( m_status == Running ); 01550 connect( this, SIGNAL( done( const QString & ) ), 01551 this, SLOT( slotModalDone( const QString & ) ) ); 01552 QObject::connect( this, SIGNAL( corrected( const QString&, const QString&, unsigned int ) ), 01553 this, SLOT( slotSpellCheckerCorrected( const QString&, const QString &, unsigned int ) ) ); 01554 QObject::connect( this, SIGNAL( death() ), 01555 this, SLOT( slotModalSpellCheckerFinished( ) ) ); 01556 check( modaltext ); 01557 } 01558 01559 void K3Spell::slotModalDone( const QString &/*_buffer*/ ) 01560 { 01561 //kDebug(750) << "MODAL DONE " << _buffer; 01562 //modaltext = _buffer; 01563 cleanUp(); 01564 01565 //kDebug() << "ABOUT TO EXIT LOOP"; 01566 //qApp->exit_loop(); 01567 01568 //modalWidgetHack->close(true); 01569 slotModalSpellCheckerFinished(); 01570 } 01571 01572 void K3Spell::slotModalSpellCheckerFinished( ) 01573 { 01574 modalreturn=(int)this->status(); 01575 } 01576 01577 void K3Spell::initialize( QWidget *_parent, const QString &_caption, 01578 QObject *obj, const char *slot, K3SpellConfig *_ksc, 01579 bool _progressbar, bool _modal, SpellerType type ) 01580 { 01581 d = new K3SpellPrivate; 01582 01583 d->m_bIgnoreUpperWords =false; 01584 d->m_bIgnoreTitleCase =false; 01585 d->m_bNoMisspellingsEncountered = true; 01586 d->type = type; 01587 d->checking = false; 01588 d->aspellV6 = false; 01589 d->checkNextTimer = new QTimer( this ); 01590 connect( d->checkNextTimer, SIGNAL( timeout() ), 01591 this, SLOT( checkNext() )); 01592 autoDelete = false; 01593 modaldlg = _modal; 01594 progressbar = _progressbar; 01595 01596 proc = 0; 01597 ksconfig = 0; 01598 ksdlg = 0; 01599 lastpos = 0; 01600 01601 //won't be using the dialog in ksconfig, just the option values 01602 if ( _ksc ) 01603 ksconfig = new K3SpellConfig( *_ksc ); 01604 else 01605 ksconfig = new K3SpellConfig; 01606 01607 d->m_codec = 0; 01608 switch ( ksconfig->encoding() ) 01609 { 01610 case KS_E_LATIN1: 01611 d->m_codec = QTextCodec::codecForName("ISO 8859-1"); 01612 break; 01613 case KS_E_LATIN2: 01614 d->m_codec = QTextCodec::codecForName("ISO 8859-2"); 01615 break; 01616 case KS_E_LATIN3: 01617 d->m_codec = QTextCodec::codecForName("ISO 8859-3"); 01618 break; 01619 case KS_E_LATIN4: 01620 d->m_codec = QTextCodec::codecForName("ISO 8859-4"); 01621 break; 01622 case KS_E_LATIN5: 01623 d->m_codec = QTextCodec::codecForName("ISO 8859-5"); 01624 break; 01625 case KS_E_LATIN7: 01626 d->m_codec = QTextCodec::codecForName("ISO 8859-7"); 01627 break; 01628 case KS_E_LATIN8: 01629 d->m_codec = QTextCodec::codecForName("ISO 8859-8-i"); 01630 break; 01631 case KS_E_LATIN9: 01632 d->m_codec = QTextCodec::codecForName("ISO 8859-9"); 01633 break; 01634 case KS_E_LATIN13: 01635 d->m_codec = QTextCodec::codecForName("ISO 8859-13"); 01636 break; 01637 case KS_E_LATIN15: 01638 d->m_codec = QTextCodec::codecForName("ISO 8859-15"); 01639 break; 01640 case KS_E_UTF8: 01641 d->m_codec = QTextCodec::codecForName("UTF-8"); 01642 break; 01643 case KS_E_KOI8R: 01644 d->m_codec = QTextCodec::codecForName("KOI8-R"); 01645 break; 01646 case KS_E_KOI8U: 01647 d->m_codec = QTextCodec::codecForName("KOI8-U"); 01648 break; 01649 case KS_E_CP1251: 01650 d->m_codec = QTextCodec::codecForName("CP1251"); 01651 break; 01652 case KS_E_CP1255: 01653 d->m_codec = QTextCodec::codecForName("CP1255"); 01654 break; 01655 default: 01656 break; 01657 } 01658 01659 kDebug(750) << __FILE__ << ":" << __LINE__ << " Codec = " << (d->m_codec ? d->m_codec->name() : "<default>"); 01660 01661 // copy ignore list from ksconfig 01662 ignorelist += ksconfig->ignoreList(); 01663 01664 replacelist += ksconfig->replaceAllList(); 01665 texmode=dlgon=false; 01666 m_status = Starting; 01667 dialogsetup = false; 01668 progres=10; 01669 curprog=0; 01670 01671 dialogwillprocess = false; 01672 dialog3slot.clear(); 01673 01674 personaldict = false; 01675 dlgresult = -1; 01676 01677 caption = _caption; 01678 01679 parent = _parent; 01680 01681 trystart = 0; 01682 maxtrystart = 2; 01683 01684 if ( obj && slot ) 01685 // caller wants to know when k3spell is ready 01686 connect( this, SIGNAL(ready(K3Spell *)), obj, slot); 01687 else 01688 // Hack for modal spell checking 01689 connect( this, SIGNAL(ready(K3Spell *)), this, SLOT(slotModalReady()) ); 01690 01691 proc = new KProcess(); 01692 01693 startIspell(); 01694 } 01695 01696 QString K3Spell::modaltext; 01697 int K3Spell::modalreturn = 0; 01698 QWidget* K3Spell::modalWidgetHack = 0; 01699 01700 #include "k3spell.moc" 01701
KDE 4.6 API Reference