lib Library API Documentation

koBgSpellCheck.cc

00001 /* This file is part of the KDE project 00002 Copyright (C) 2002 David Faure <david@mandrakesoft.com> 00003 2002 Laurent Montel <lmontel@mandrakesoft.com> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00018 Boston, MA 02111-1307, USA. 00019 */ 00020 00021 00022 #include "koBgSpellCheck.h" 00023 #include "koBgSpellCheck.moc" 00024 #include <qtimer.h> 00025 #include <kdebug.h> 00026 #include <kospell.h> 00027 #include <koSconfig.h> 00028 #include <ksconfig.h> 00029 #include <kotextobject.h> 00030 #include <klocale.h> 00031 00032 //#define DEBUG_BGSPELLCHECKING 00033 00034 class KoBgSpellCheck::KoBgSpellCheckPrivate 00035 { 00036 public: 00037 KSpellConfig * m_pKSpellConfig; 00038 QTimer * startTimer; 00039 QTimer * nextParagraphTimer; 00040 }; 00041 00042 KoBgSpellCheck::KoBgSpellCheck() 00043 { 00044 #ifdef DEBUG_BGSPELLCHECKING 00045 kdDebug(32500) << "KoBgSpellCheck::KoBgSpellCheck " << this << endl; 00046 #endif 00047 d = new KoBgSpellCheck::KoBgSpellCheckPrivate; 00048 d->m_pKSpellConfig=0L; 00049 d->startTimer = new QTimer( this ); 00050 connect( d->startTimer, SIGNAL( timeout() ), 00051 this, SLOT( startBackgroundSpellCheck() ) ); 00052 d->nextParagraphTimer = new QTimer( this ); 00053 connect( d->nextParagraphTimer, SIGNAL( timeout() ), 00054 this, SLOT( spellCheckNextParagraph() ) ); 00055 00056 m_bgSpell.kspell=0L; 00057 m_bDontCheckUpperWord=false; 00058 m_bSpellCheckEnabled=false; 00059 m_bDontCheckTitleCase=false; 00060 m_bSpellCheckConfigure=false; 00061 m_bgSpell.currentTextObj=0L; 00062 m_bgSpell.needsRepaint=false; 00063 } 00064 00065 KoBgSpellCheck::~KoBgSpellCheck() 00066 { 00067 if ( m_bgSpell.kspell ) { 00068 m_bgSpell.kspell->cleanUp(); 00069 delete m_bgSpell.kspell; 00070 } 00071 delete d->m_pKSpellConfig; 00072 delete d; 00073 } 00074 00075 void KoBgSpellCheck::addPersonalDictonary( const QString & word ) 00076 { 00077 if ( m_bgSpell.kspell ) 00078 { 00079 m_bgSpell.kspell->addPersonal ( word); 00080 } 00081 } 00082 00083 void KoBgSpellCheck::spellCheckParagraphDeleted( KoTextParag *_parag, KoTextObject *obj) 00084 { 00085 if ( m_bgSpell.currentTextObj == obj && m_bgSpell.currentParag == _parag) 00086 { 00087 stopSpellChecking(); 00088 startBackgroundSpellCheck(); 00089 } 00090 } 00091 00092 00093 void KoBgSpellCheck::enableBackgroundSpellCheck( bool b ) 00094 { 00095 m_bSpellCheckEnabled=b; 00096 startBackgroundSpellCheck(); // will enable or disable 00097 } 00098 00099 void KoBgSpellCheck::setIgnoreUpperWords( bool b) 00100 { 00101 stopSpellChecking(); 00102 m_bDontCheckUpperWord = b; 00103 startBackgroundSpellCheck(); 00104 } 00105 00106 void KoBgSpellCheck::setIgnoreTitleCase( bool b) 00107 { 00108 stopSpellChecking(); 00109 m_bDontCheckTitleCase = b; 00110 startBackgroundSpellCheck(); 00111 } 00112 00113 void KoBgSpellCheck::addIgnoreWordAll( const QString & word) 00114 { 00115 if( m_spellListIgnoreAll.findIndex( word )==-1) 00116 m_spellListIgnoreAll.append( word ); 00117 stopSpellChecking(); 00118 spellConfig()->setIgnoreList( m_spellListIgnoreAll ); 00119 startBackgroundSpellCheck(); 00120 } 00121 00122 void KoBgSpellCheck::addIgnoreWordAllList( const QStringList & list) 00123 { 00124 m_spellListIgnoreAll.clear(); 00125 stopSpellChecking(); 00126 spellConfig()->setIgnoreList( list ); 00127 startBackgroundSpellCheck(); 00128 } 00129 00130 void KoBgSpellCheck::clearIgnoreWordAll( ) 00131 { 00132 m_spellListIgnoreAll.clear(); 00133 stopSpellChecking(); 00134 spellConfig()->setIgnoreList( m_spellListIgnoreAll ); 00135 startBackgroundSpellCheck(); 00136 } 00137 00138 void KoBgSpellCheck::startBackgroundSpellCheck() 00139 { 00140 d->startTimer->stop(); // In case we were called directly, while the timer was running. 00141 if ( !m_bSpellCheckEnabled ) 00142 return; 00143 //re-test text obj 00144 if ( !m_bgSpell.currentTextObj ) 00145 { 00146 m_bgSpell.currentTextObj = nextTextObject(m_bgSpell.currentTextObj ); 00147 } 00148 if ( !m_bgSpell.currentTextObj ) 00149 { 00150 #ifdef DEBUG_BGSPELLCHECKING 00151 //kdDebug(32500) << "KoBgSpellCheck::startBackgroundSpellCheck no currentTextObj to check this time." << endl; 00152 #endif 00153 d->startTimer->start( 1000, true ); 00154 return; 00155 } 00156 #ifdef DEBUG_BGSPELLCHECKING 00157 kdDebug(32500) << "KoBgSpellCheck::startBackgroundSpellCheck" << endl; 00158 #endif 00159 00160 m_bgSpell.currentParag = m_bgSpell.currentTextObj->textDocument()->firstParag(); 00161 nextParagraphNeedingCheck(); 00162 00163 #ifdef DEBUG_BGSPELLCHECKING 00164 kdDebug(32500) << "fs=" << m_bgSpell.currentTextObj << " parag=" << m_bgSpell.currentParag << endl; 00165 #endif 00166 00167 if ( !m_bgSpell.currentTextObj || !m_bgSpell.currentParag ) { 00168 if ( m_bgSpell.currentTextObj ) 00169 { 00170 if ( (m_bgSpell.currentTextObj->textDocument()->firstParag() == m_bgSpell.currentTextObj->textDocument()->lastParag()) && m_bgSpell.currentTextObj->textDocument()->firstParag()->length() <= 1) 00171 m_bgSpell.currentTextObj->setNeedSpellCheck(false); 00172 } 00173 // Might be better to launch again upon document modification (key, pasting, etc.) instead of right now 00174 #ifdef DEBUG_BGSPELLCHECKING 00175 kdDebug(32500) << "KoBgSpellCheck::startBackgroundSpellCheck nothing to check this time." << endl; 00176 #endif 00177 d->startTimer->start( 1000, true ); 00178 return; 00179 } 00180 00181 bool needsWait = false; 00182 if ( !m_bgSpell.kspell ) // reuse if existing 00183 { 00184 m_bgSpell.kspell = new KoSpell(0L, this, SLOT( spellCheckerReady() ), d->m_pKSpellConfig ); 00185 00186 needsWait = true; // need to wait for ready() 00187 connect( m_bgSpell.kspell, SIGNAL( death() ), 00188 this, SLOT( spellCheckerFinished() ) ); 00189 connect( m_bgSpell.kspell, SIGNAL( misspelling( const QString &, int ) ), 00190 this, SLOT( spellCheckerMisspelling( const QString &, int ) ) ); 00191 connect( m_bgSpell.kspell, SIGNAL( done() ), 00192 this, SLOT( spellCheckerDone() ) ); 00193 } 00194 m_bgSpell.kspell->setIgnoreUpperWords( m_bDontCheckUpperWord ); 00195 m_bgSpell.kspell->setIgnoreTitleCase( m_bDontCheckTitleCase ); 00196 if ( !needsWait ) 00197 spellCheckerReady(); 00198 } 00199 00200 void KoBgSpellCheck::spellCheckerReady() 00201 { 00202 //necessary to restart to beginning otherwise we don't check 00203 //other parag 00204 if (m_bgSpell.currentTextObj) 00205 m_bgSpell.currentParag = m_bgSpell.currentTextObj->textDocument()->firstParag(); 00206 00207 #ifdef DEBUG_BGSPELLCHECKING 00208 kdDebug(32500) << "KoBgSpellCheck::spellCheckerReady textobj=" << m_bgSpell.currentTextObj << endl; 00209 #endif 00210 d->nextParagraphTimer->start( 10, true ); 00211 } 00212 00213 // Input: currentTextObj non-null, and currentParag set to the last parag checked 00214 // Output: currentTextObj+currentParag set to next parag to check. Both 0 if end. 00215 void KoBgSpellCheck::nextParagraphNeedingCheck() 00216 { 00217 #ifdef DEBUG_BGSPELLCHECKING 00218 kdDebug(32500) << "KoBgSpellCheck::nextParagraphNeedingCheck textobj=" <<m_bgSpell.currentTextObj <<endl; 00219 #endif 00220 if ( !m_bgSpell.currentTextObj ) { 00221 m_bgSpell.currentParag = 0L; 00222 return; 00223 } 00224 00225 // repaint the textObject here if it requires it 00226 // (perhaps there should be a way to repaint just a paragraph.... - JJ) 00227 // Well repaintChanged looks for the changed flag in parags and repaints only those - DF 00228 if(m_bgSpell.needsRepaint) 00229 { 00230 slotRepaintChanged( m_bgSpell.currentTextObj ); 00231 m_bgSpell.needsRepaint=false; 00232 } 00233 00234 KoTextParag* parag = m_bgSpell.currentParag; 00235 if ( parag && parag->string() && parag->string()->needsSpellCheck() ) 00236 { 00237 #ifdef DEBUG_BGSPELLCHECKING 00238 kdDebug(32500) << "current parag " << parag << " needs checking again." <<endl; 00239 #endif 00240 return; 00241 } 00242 00243 if ( parag && parag->next() ) 00244 parag = parag->next(); 00245 // Skip any unchanged parags 00246 while ( parag && !parag->string()->needsSpellCheck() ) 00247 parag = parag->next(); 00248 while ( parag && parag->length() <= 1 ) // empty parag 00249 { 00250 parag->string()->setNeedsSpellCheck( false ); // nothing to check 00251 while ( parag && !parag->string()->needsSpellCheck() ) // keep looking 00252 parag = parag->next(); 00253 } 00254 // Still not found? Check from the start up to where we started from 00255 if ( !parag ) { 00256 parag = m_bgSpell.currentTextObj->textDocument()->firstParag(); 00257 while ( parag != m_bgSpell.currentParag && !parag->string()->needsSpellCheck() ) 00258 parag = parag->next(); 00259 while ( parag != m_bgSpell.currentParag && parag->length() <= 1 ) // empty parag 00260 { 00261 parag->string()->setNeedsSpellCheck( false ); // nothing to check 00262 while ( parag != m_bgSpell.currentParag && !parag->string()->needsSpellCheck() ) // keep looking 00263 parag = parag->next(); 00264 } 00265 if ( parag == m_bgSpell.currentParag && !parag->string()->needsSpellCheck() ) 00266 parag = 0; // wrapped around and found nothing to check 00267 } 00268 00269 if ( parag ) 00270 m_bgSpell.currentParag = parag; 00271 else 00272 { 00273 KoTextObject *obj = m_bgSpell.currentTextObj; 00274 // OK, nothing more to do in this textobj 00275 obj->setNeedSpellCheck(false); 00276 00277 m_bgSpell.currentTextObj = nextTextObject( m_bgSpell.currentTextObj ); 00278 //kdDebug(32500)<<" m_bgSpell.currentTextObj="<<m_bgSpell.currentTextObj<<endl; 00279 if ( m_bgSpell.currentTextObj && m_bgSpell.currentTextObj != obj) 00280 { 00281 m_bgSpell.currentParag = m_bgSpell.currentTextObj->textDocument()->firstParag(); 00282 } 00283 else 00284 { 00285 if ( m_bgSpell.currentTextObj ) 00286 m_bgSpell.currentTextObj->setNeedSpellCheck( false ); 00287 m_bgSpell.currentParag = 0L; 00288 } 00289 } 00290 //kdDebug(32500)<<" KoBgSpellCheck::nextParagraphNeedingCheck() : m_bgSpell.currentParag :"<<m_bgSpell.currentParag<<endl; 00291 00292 } 00293 00294 void KoBgSpellCheck::spellCheckNextParagraph() 00295 { 00296 #ifdef DEBUG_BGSPELLCHECKING 00297 kdDebug(32500) << "KoBgSpellCheck::spellCheckNextParagraph" << endl; 00298 #endif 00299 00300 nextParagraphNeedingCheck(); 00301 #ifdef DEBUG_BGSPELLCHECKING 00302 kdDebug(32500) << "textobj=" << m_bgSpell.currentTextObj << " parag=" << m_bgSpell.currentParag << endl; 00303 #endif 00304 if ( !m_bgSpell.currentTextObj || !m_bgSpell.currentParag ) 00305 { 00306 #ifdef DEBUG_BGSPELLCHECKING 00307 kdDebug(32500) << "KoBgSpellCheck::spellCheckNextParagraph scheduling restart" << endl; 00308 #endif 00309 // We arrived to the end of the paragraphs. Jump to startBackgroundSpellCheck, 00310 // it will check if we still have something to do. 00311 d->startTimer->start( 100, true ); 00312 return; 00313 } 00314 // First remove any misspelled format from the paragraph 00315 // - otherwise we'd never notice words being ok again :) 00316 KoTextStringChar *ch = m_bgSpell.currentParag->at( 0 ); 00317 KoTextFormat format( *ch->format() ); 00318 format.setMisspelled( false ); 00319 m_bgSpell.currentParag->setFormat( 0, m_bgSpell.currentParag->length()-1, &format, true, KoTextFormat::Misspelled ); 00320 #ifdef DEBUG_BGSPELLCHECKING 00321 kdDebug(32500) << "KoBgSpellCheck::spellCheckNextParagraph spell checking parag " << m_bgSpell.currentParag->paragId() << endl; 00322 #endif 00323 00324 // Get the text to spell-check 00325 QString text = m_bgSpell.currentParag->string()->toString(); 00326 text.remove( text.length() - 1, 1 ); // trailing space 00327 00328 // Mark it as "we've read the text to be spell-checked", *before* doing it. 00329 // This prevents race conditions: if the user modifies the text during 00330 // the spellchecking, the new text _will_ be checked, since the bool will 00331 // be set to true. 00332 m_bgSpell.currentParag->string()->setNeedsSpellCheck( false ); 00333 00334 // Now spell-check that paragraph 00335 m_bgSpell.kspell->check(text); 00336 } 00337 00338 void KoBgSpellCheck::spellCheckerMisspelling(const QString &old, int pos ) 00339 { 00340 KoTextObject * textobj = m_bgSpell.currentTextObj; 00341 #ifdef DEBUG_BGSPELLCHECKING 00342 kdDebug(32500) << "KoBgSpellCheck::spellCheckerMisspelling textobj=" << textobj << " old=" << old << " pos=" << pos << endl; 00343 #endif 00344 Q_ASSERT( textobj ); 00345 if ( !textobj ) return; 00346 KoTextParag* parag = m_bgSpell.currentParag; 00347 if ( !parag ) return; 00348 #ifdef DEBUG_BGSPELLCHECKING 00349 kdDebug(32500) << "KoBgSpellCheck::spellCheckerMisspelling parag=" << parag << " (id=" << parag->paragId() << ", length=" << parag->length() << ") pos=" << pos << " length=" << old.length() << endl; 00350 #endif 00351 KoTextStringChar *ch = parag->at( pos ); 00352 KoTextFormat format( *ch->format() ); 00353 format.setMisspelled( true ); 00354 parag->setFormat( pos, old.length(), &format, true, KoTextFormat::Misspelled ); 00355 } 00356 00357 void KoBgSpellCheck::spellCheckerDone() 00358 { 00359 // Set the repaint flags. This needs to be done even if no misspelling was found, 00360 // so that words added to the dictionary become ok again (#56506,#57357) 00361 m_bgSpell.currentParag->setChanged( true ); 00362 m_bgSpell.needsRepaint=true; 00363 00364 #ifdef DEBUG_BGSPELLCHECKING 00365 kdDebug(32500) << "KoBgSpellCheck::spellCheckerDone" << endl; 00366 #endif 00367 // Done checking the current paragraph, schedule the next one 00368 d->nextParagraphTimer->start( 10, true ); 00369 } 00370 00371 void KoBgSpellCheck::spellCheckerFinished() 00372 { 00373 #ifdef DEBUG_BGSPELLCHECKING 00374 kdDebug(32500) << "--- KoBgSpellCheck::spellCheckerFinished ---" << endl; 00375 #endif 00376 KoSpell::spellStatus status = m_bgSpell.kspell->status(); 00377 m_bgSpell.kspell->cleanUp(); 00378 delete m_bgSpell.kspell; 00379 m_bgSpell.kspell = 0; 00380 m_bgSpell.currentParag = 0; 00381 m_bgSpell.currentTextObj = 0; 00382 if (status == KoSpell::Error) 00383 { 00384 // KSpell badly configured... what to do? 00385 kdWarning() << "ISpell/ASpell not configured correctly." << endl; 00386 if ( !m_bSpellCheckConfigure ) 00387 { 00388 m_bSpellCheckConfigure=true; 00389 configurateSpellChecker(); 00390 } 00391 return; 00392 } 00393 else if (status == KoSpell::Crashed) 00394 { 00395 kdWarning() << "ISpell/ASpell seems to have crashed." << endl; 00396 return; 00397 } 00398 // Normal death - nothing to do 00399 } 00400 00401 KSpellConfig* KoBgSpellCheck::spellConfig() 00402 { 00403 if ( !d->m_pKSpellConfig ) 00404 d->m_pKSpellConfig = new KSpellConfig(); 00405 return d->m_pKSpellConfig; 00406 } 00407 00408 void KoBgSpellCheck::setKSpellConfig(const KOSpellConfig &_kspell) 00409 { 00410 (void)spellConfig(); 00411 stopSpellChecking(); 00412 00413 d->m_pKSpellConfig->setNoRootAffix(_kspell.noRootAffix ()); 00414 d->m_pKSpellConfig->setRunTogether(_kspell.runTogether ()); 00415 d->m_pKSpellConfig->setDictionary(_kspell.dictionary ()); 00416 d->m_pKSpellConfig->setDictFromList(_kspell.dictFromList()); 00417 d->m_pKSpellConfig->setEncoding(_kspell.encoding()); 00418 d->m_pKSpellConfig->setClient(_kspell.client()); 00419 m_bSpellCheckConfigure = false; 00420 startBackgroundSpellCheck(); 00421 } 00422 00423 void KoBgSpellCheck::stopSpellChecking() 00424 { 00425 #ifdef DEBUG_BGSPELLCHECKING 00426 kdDebug(32500) << "KoBgSpellCheck::stopSpellChecking" << endl; 00427 #endif 00428 if ( m_bgSpell.kspell ) { 00429 m_bgSpell.kspell->cleanUp(); 00430 delete m_bgSpell.kspell; 00431 m_bgSpell.kspell = 0; 00432 } 00433 m_bgSpell.currentParag = 0; 00434 m_bgSpell.currentTextObj = 0; 00435 d->startTimer->stop(); 00436 d->nextParagraphTimer->stop(); 00437 }
KDE Logo
This file is part of the documentation for lib Library Version 1.3.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Sep 28 04:03:59 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003