/* This file is part of the KDE project Copyright (C) 2004 Zack Rusin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include "KoBgSpellCheck.h" #include "KoBgSpellCheck.moc" #include "KoTextParag.h" #include "KoSpell.h" #include "KoTextObject.h" #include "KoTextDocument.h" #include #include #include #include #include using namespace KSpell2; #include #include #include #include #include // #define DEBUG_BGSPELLCHECKING class KoBgSpellCheck::Private { public: int marked; KoSpell *backSpeller; TQPtrDict paragCache; bool startupChecking; KoTextParag* intraWordParag; int intraWordPosition; }; static const int delayAfterMarked = 10; KoBgSpellCheck::KoBgSpellCheck( const Broker::Ptr& broker, TQObject *parent, const char *name ) : TQObject( parent, name ) { #ifdef DEBUG_BGSPELLCHECKING kdDebug(32500) << "KoBgSpellCheck::KoBgSpellCheck " << this << endl; #endif d = new Private; d->startupChecking = false; d->marked = 0; d->intraWordParag = 0; d->intraWordPosition = 0; d->backSpeller = new KoSpell( broker, this, "KoSpell" ); connect( d->backSpeller, TQT_SIGNAL(misspelling(const TQString&, int)), TQT_SLOT(spellCheckerMisspelling(const TQString &, int )) ); connect( d->backSpeller, TQT_SIGNAL(done()), TQT_SLOT(spellCheckerDone()) ); connect( d->backSpeller, TQT_SIGNAL(aboutToFeedText()), TQT_SLOT(slotClearPara()) ); } KoBgSpellCheck::~KoBgSpellCheck() { delete d; d = 0; } void KoBgSpellCheck::registerNewTextObject( KoTextObject *obj ) { Q_ASSERT( obj ); connect( obj, TQT_SIGNAL(paragraphCreated(KoTextParag*)), TQT_SLOT(slotParagraphCreated(KoTextParag*)) ); connect( obj, TQT_SIGNAL(paragraphModified(KoTextParag*, int, int, int)), TQT_SLOT(slotParagraphModified(KoTextParag*, int, int, int)) ); connect( obj, TQT_SIGNAL(paragraphDeleted(KoTextParag*)), TQT_SLOT(slotParagraphDeleted(KoTextParag*)) ); } void KoBgSpellCheck::setEnabled( bool b ) { d->backSpeller->settings()->setBackgroundCheckerEnabled( b ); if ( b ) start(); else stop(); } bool KoBgSpellCheck::enabled() const { return d->backSpeller->settings()->backgroundCheckerEnabled(); } void KoBgSpellCheck::start() { if ( !enabled() ) return; d->startupChecking = true; d->marked = 0; KoTextIterator *itr = createWholeDocIterator(); d->backSpeller->check( itr ); d->backSpeller->start(); } void KoBgSpellCheck::spellCheckerMisspelling( const TQString &old, int pos ) { KoTextParag* parag = d->backSpeller->currentParag(); #ifdef DEBUG_BGSPELLCHECKING kdDebug(32500) << "KoBgSpellCheck::spellCheckerMisspelling parag=" << parag << " (id=" << parag->paragId() << ", length=" << parag->length() << ") pos=" << pos << " length=" << old.length() << endl; #endif markWord( parag, pos, old.length(), true ); // Repaint immediately, since the checking is timer-based (slow), it looks // slow (chunky) if we only tqrepaint once a paragraph is completely done. parag->document()->emitRepaintChanged(); if ( d->startupChecking && d->marked > delayAfterMarked ) { d->marked = 0; TQTimer::singleShot( 1000, this, TQT_SLOT(checkerContinue()) ); } else { if ( d->startupChecking ) ++d->marked; checkerContinue(); } } void KoBgSpellCheck::markWord( KoTextParag* parag, int pos, int length, bool misspelled ) { if ( pos >= parag->length() ) { kdDebug(32500) << "markWord: " << pos << " is out of parag (length=" << parag->length() << ")" << endl; return; } if ( misspelled && parag == d->intraWordParag && d->intraWordPosition >= pos && d->intraWordPosition < pos+length ) { #ifdef DEBUG_BGSPELLCHECKING kdDebug(32500) << "markWord: " << parag << " " << pos << " to " << pos+length << " - word being edited" << endl; #endif return; // not yet } KoTextStringChar *ch = parag->at( pos ); KoTextFormat format( *ch->format() ); format.setMisspelled( misspelled ); #ifdef DEBUG_BGSPELLCHECKING kdDebug(32500) << "markWord: changing mark from " << pos << " length=" << length << " misspelled=" << misspelled << endl; #endif parag->setFormat( pos, length, &format, true, KoTextFormat::Misspelled ); parag->setChanged( true ); // don't tqrepaint here, in the slotParagraphModified case we want to tqrepaint only once at the end } void KoBgSpellCheck::checkerContinue() { if(enabled()) d->backSpeller->continueChecking(); } void KoBgSpellCheck::spellCheckerDone() { d->startupChecking = false; if ( d->paragCache.isEmpty() ) return; TQPtrDictIterator itr( d->paragCache ); KoTextParag *parag = d->paragCache.take( itr.currentKey() ); #ifdef DEBUG_BGSPELLCHECKING kdDebug(32500) << "spellCheckerDone : " << parag << ", cache = "<< d->paragCache.count() <backSpeller->check( parag ); } void KoBgSpellCheck::stop() { #ifdef DEBUG_BGSPELLCHECKING kdDebug(32500) << "KoBgSpellCheck::stopSpellChecking" << endl; #endif d->backSpeller->stop(); } void KoBgSpellCheck::slotParagraphCreated( KoTextParag* parag ) { parag->string()->setNeedsSpellCheck( true ); if ( !enabled() ) return; if ( !d->backSpeller->check( parag ) ) { d->paragCache.insert( parag, parag ); } } void KoBgSpellCheck::slotParagraphModified( KoTextParag* parag, int /*ParagModifyType*/, int pos, int length ) { parag->string()->setNeedsSpellCheck( true ); if ( !enabled() ) return; if ( d->backSpeller->checking() ) { d->paragCache.insert( parag, parag ); return; } #ifdef DEBUG_BGSPELLCHECKING kdDebug(32500) << "Para modified " << parag << " pos = "<paragId() << endl; #endif parag->setFormat( 0, parag->length()-1, &format, true, KoTextFormat::Misspelled ); parag->setChanged( true ); parag->document()->emitRepaintChanged(); } KSpell2::Settings * KoBgSpellCheck::settings() const { return d->backSpeller->settings(); } void KoBgSpellCheck::setIntraWordEditing( KoTextParag* parag, int index ) { KoTextParag* oldIntraWordParag = d->intraWordParag; int oldIntraWordPosition = d->intraWordPosition; d->intraWordParag = parag; d->intraWordPosition = index; if ( oldIntraWordParag && !parag ) { // When typing a letter into an existing word and then going somewhere else, // we need to re-check that word - after moving d->intra* out of the way of course. slotParagraphModified( oldIntraWordParag, 0 /*unused*/, oldIntraWordPosition, 1 ); } }