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.5.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Mar 11 11:47:40 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003