|
|
|
/*
|
|
|
|
ksyntaxhighlighter.cpp
|
|
|
|
|
|
|
|
Copyright (c) 2003 Trolltech AS
|
|
|
|
Copyright (c) 2003 Scott Wheeler <wheeler@kde.org>
|
|
|
|
|
|
|
|
This file is part of the KDE libraries
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License version 2 as published by the Free Software Foundation.
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <tqcolor.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqsyntaxhighlighter.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kconfig.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kglobal.h>
|
|
|
|
#include <kspell.h>
|
|
|
|
#include <kapplication.h>
|
|
|
|
|
|
|
|
#include "ksyntaxhighlighter.h"
|
|
|
|
|
|
|
|
static int dummy, dummy2, dummy3, dummy4;
|
|
|
|
static int *Okay = &dummy;
|
|
|
|
static int *NotOkay = &dummy2;
|
|
|
|
static int *Ignore = &dummy3;
|
|
|
|
static int *Unknown = &dummy4;
|
|
|
|
static const int tenSeconds = 10*1000;
|
|
|
|
|
|
|
|
class KSyntaxHighlighter::KSyntaxHighlighterPrivate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
TQColor col1, col2, col3, col4, col5;
|
|
|
|
SyntaxMode mode;
|
|
|
|
bool enabled;
|
|
|
|
};
|
|
|
|
|
|
|
|
class KSpellingHighlighter::KSpellingHighlighterPrivate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
KSpellingHighlighterPrivate() :
|
|
|
|
alwaysEndsWithSpace( true ),
|
|
|
|
intraWordEditing( false ) {}
|
|
|
|
|
|
|
|
TQString currentWord;
|
|
|
|
int currentPos;
|
|
|
|
bool alwaysEndsWithSpace;
|
|
|
|
TQColor color;
|
|
|
|
bool intraWordEditing;
|
|
|
|
};
|
|
|
|
|
|
|
|
class KDictSpellingHighlighter::KDictSpellingHighlighterPrivate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
KDictSpellingHighlighterPrivate() :
|
|
|
|
mDict( 0 ),
|
|
|
|
spell( 0 ),
|
|
|
|
mSpellConfig( 0 ),
|
|
|
|
rehighlightRequest( 0 ),
|
|
|
|
wordCount( 0 ),
|
|
|
|
errorCount( 0 ),
|
|
|
|
autoReady( false ),
|
|
|
|
globalConfig( true ),
|
|
|
|
spellReady( false ) {}
|
|
|
|
|
|
|
|
~KDictSpellingHighlighterPrivate() {
|
|
|
|
delete rehighlightRequest;
|
|
|
|
delete spell;
|
|
|
|
}
|
|
|
|
|
|
|
|
static TQDict<int>* sDict()
|
|
|
|
{
|
|
|
|
if (!statDict)
|
|
|
|
statDict = new TQDict<int>(50021);
|
|
|
|
return statDict;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQDict<int>* mDict;
|
|
|
|
TQDict<int> autoDict;
|
|
|
|
TQDict<int> autoIgnoreDict;
|
|
|
|
static TQObject *sDictionaryMonitor;
|
|
|
|
KSpell *spell;
|
|
|
|
KSpellConfig *mSpellConfig;
|
|
|
|
TQTimer *rehighlightRequest, *spellTimeout;
|
|
|
|
TQString spellKey;
|
|
|
|
int wordCount, errorCount;
|
|
|
|
int checksRequested, checksDone;
|
|
|
|
int disablePercentage;
|
|
|
|
int disableWordCount;
|
|
|
|
bool completeRehighlightRequired;
|
|
|
|
bool active, automatic, autoReady;
|
|
|
|
bool globalConfig, spellReady;
|
|
|
|
private:
|
|
|
|
static TQDict<int>* statDict;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
TQDict<int>* KDictSpellingHighlighter::KDictSpellingHighlighterPrivate::statDict = 0;
|
|
|
|
|
|
|
|
|
|
|
|
KSyntaxHighlighter::KSyntaxHighlighter( TQTextEdit *textEdit,
|
|
|
|
bool colorQuoting,
|
|
|
|
const TQColor& depth0,
|
|
|
|
const TQColor& depth1,
|
|
|
|
const TQColor& depth2,
|
|
|
|
const TQColor& depth3,
|
|
|
|
SyntaxMode mode )
|
|
|
|
: TQSyntaxHighlighter( textEdit )
|
|
|
|
{
|
|
|
|
d = new KSyntaxHighlighterPrivate();
|
|
|
|
|
|
|
|
d->enabled = colorQuoting;
|
|
|
|
d->col1 = depth0;
|
|
|
|
d->col2 = depth1;
|
|
|
|
d->col3 = depth2;
|
|
|
|
d->col4 = depth3;
|
|
|
|
d->col5 = depth0;
|
|
|
|
|
|
|
|
d->mode = mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
KSyntaxHighlighter::~KSyntaxHighlighter()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
int KSyntaxHighlighter::highlightParagraph( const TQString &text, int )
|
|
|
|
{
|
|
|
|
if (!d->enabled) {
|
|
|
|
setFormat( 0, text.length(), textEdit()->viewport()->paletteForegroundColor() );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString simplified = text;
|
|
|
|
simplified = simplified.replace( TQRegExp( "\\s" ), TQString::null ).replace( '|', TQString::fromLatin1(">") );
|
|
|
|
while ( simplified.startsWith( TQString::fromLatin1(">>>>") ) )
|
|
|
|
simplified = simplified.mid(3);
|
|
|
|
if ( simplified.startsWith( TQString::fromLatin1(">>>") ) || simplified.startsWith( TQString::fromLatin1("> > >") ) )
|
|
|
|
setFormat( 0, text.length(), d->col2 );
|
|
|
|
else if ( simplified.startsWith( TQString::fromLatin1(">>") ) || simplified.startsWith( TQString::fromLatin1("> >") ) )
|
|
|
|
setFormat( 0, text.length(), d->col3 );
|
|
|
|
else if ( simplified.startsWith( TQString::fromLatin1(">") ) )
|
|
|
|
setFormat( 0, text.length(), d->col4 );
|
|
|
|
else
|
|
|
|
setFormat( 0, text.length(), d->col5 );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
KSpellingHighlighter::KSpellingHighlighter( TQTextEdit *textEdit,
|
|
|
|
const TQColor& spellColor,
|
|
|
|
bool colorQuoting,
|
|
|
|
const TQColor& depth0,
|
|
|
|
const TQColor& depth1,
|
|
|
|
const TQColor& depth2,
|
|
|
|
const TQColor& depth3 )
|
|
|
|
: KSyntaxHighlighter( textEdit, colorQuoting, depth0, depth1, depth2, depth3 )
|
|
|
|
{
|
|
|
|
d = new KSpellingHighlighterPrivate();
|
|
|
|
|
|
|
|
d->color = spellColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
KSpellingHighlighter::~KSpellingHighlighter()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
int KSpellingHighlighter::highlightParagraph( const TQString &text,
|
|
|
|
int paraNo )
|
|
|
|
{
|
|
|
|
if ( paraNo == -2 )
|
|
|
|
paraNo = 0;
|
|
|
|
// leave #includes, diffs, and quoted replies alone
|
|
|
|
TQString diffAndCo( ">|" );
|
|
|
|
|
|
|
|
bool isCode = diffAndCo.find(text[0]) != -1;
|
|
|
|
|
|
|
|
if ( !text.endsWith(" ") )
|
|
|
|
d->alwaysEndsWithSpace = false;
|
|
|
|
|
|
|
|
KSyntaxHighlighter::highlightParagraph( text, -2 );
|
|
|
|
|
|
|
|
if ( !isCode ) {
|
|
|
|
int para, index;
|
|
|
|
textEdit()->getCursorPosition( ¶, &index );
|
|
|
|
int len = text.length();
|
|
|
|
if ( d->alwaysEndsWithSpace )
|
|
|
|
len--;
|
|
|
|
|
|
|
|
d->currentPos = 0;
|
|
|
|
d->currentWord = "";
|
|
|
|
for ( int i = 0; i < len; i++ ) {
|
|
|
|
if ( !text[i].isLetter() && (!(text[i] == '\'')) ) {
|
|
|
|
if ( ( para != paraNo ) ||
|
|
|
|
!intraWordEditing() ||
|
|
|
|
( i - d->currentWord.length() > (uint)index ) ||
|
|
|
|
( i < index ) ) {
|
|
|
|
flushCurrentWord();
|
|
|
|
} else {
|
|
|
|
d->currentWord = "";
|
|
|
|
}
|
|
|
|
d->currentPos = i + 1;
|
|
|
|
} else {
|
|
|
|
d->currentWord += text[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( !text[len - 1].isLetter() ||
|
|
|
|
(uint)( index + 1 ) != text.length() ||
|
|
|
|
para != paraNo )
|
|
|
|
flushCurrentWord();
|
|
|
|
}
|
|
|
|
return ++paraNo;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQStringList KSpellingHighlighter::personalWords()
|
|
|
|
{
|
|
|
|
TQStringList l;
|
|
|
|
l.append( "KMail" );
|
|
|
|
l.append( "KOrganizer" );
|
|
|
|
l.append( "KAddressBook" );
|
|
|
|
l.append( "KHTML" );
|
|
|
|
l.append( "KIO" );
|
|
|
|
l.append( "KJS" );
|
|
|
|
l.append( "Konqueror" );
|
|
|
|
l.append( "KSpell" );
|
|
|
|
l.append( "Kontact" );
|
|
|
|
l.append( "Qt" );
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpellingHighlighter::flushCurrentWord()
|
|
|
|
{
|
|
|
|
while ( d->currentWord[0].isPunct() ) {
|
|
|
|
d->currentWord = d->currentWord.mid( 1 );
|
|
|
|
d->currentPos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQChar ch;
|
|
|
|
while ( ( ch = d->currentWord[(int) d->currentWord.length() - 1] ).isPunct() &&
|
|
|
|
ch != '(' && ch != '@' )
|
|
|
|
d->currentWord.truncate( d->currentWord.length() - 1 );
|
|
|
|
|
|
|
|
if ( !d->currentWord.isEmpty() ) {
|
|
|
|
if ( isMisspelled( d->currentWord ) ) {
|
|
|
|
setFormat( d->currentPos, d->currentWord.length(), d->color );
|
|
|
|
// setMisspelled( d->currentPos, d->currentWord.length(), true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
d->currentWord = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
TQObject *KDictSpellingHighlighter::KDictSpellingHighlighterPrivate::sDictionaryMonitor = 0;
|
|
|
|
|
|
|
|
KDictSpellingHighlighter::KDictSpellingHighlighter( TQTextEdit *textEdit,
|
|
|
|
bool spellCheckingActive ,
|
|
|
|
bool autoEnable,
|
|
|
|
const TQColor& spellColor,
|
|
|
|
bool colorQuoting,
|
|
|
|
const TQColor& depth0,
|
|
|
|
const TQColor& depth1,
|
|
|
|
const TQColor& depth2,
|
|
|
|
const TQColor& depth3,
|
|
|
|
KSpellConfig *spellConfig )
|
|
|
|
: KSpellingHighlighter( textEdit, spellColor,
|
|
|
|
colorQuoting, depth0, depth1, depth2, depth3 )
|
|
|
|
{
|
|
|
|
d = new KDictSpellingHighlighterPrivate();
|
|
|
|
|
|
|
|
d->mSpellConfig = spellConfig;
|
|
|
|
d->globalConfig = ( !spellConfig );
|
|
|
|
d->automatic = autoEnable;
|
|
|
|
d->active = spellCheckingActive;
|
|
|
|
d->checksRequested = 0;
|
|
|
|
d->checksDone = 0;
|
|
|
|
d->completeRehighlightRequired = false;
|
|
|
|
|
|
|
|
KConfig *config = KGlobal::config();
|
|
|
|
KConfigGroupSaver cs( config, "KSpell" );
|
|
|
|
d->disablePercentage = config->readNumEntry( "KSpell_AsYouTypeDisablePercentage", 42 );
|
|
|
|
d->disablePercentage = QMIN( d->disablePercentage, 101 );
|
|
|
|
d->disableWordCount = config->readNumEntry( "KSpell_AsYouTypeDisableWordCount", 100 );
|
|
|
|
|
|
|
|
textEdit->installEventFilter( this );
|
|
|
|
textEdit->viewport()->installEventFilter( this );
|
|
|
|
|
|
|
|
d->rehighlightRequest = new TQTimer(this);
|
|
|
|
connect( d->rehighlightRequest, TQT_SIGNAL( timeout() ),
|
|
|
|
this, TQT_SLOT( slotRehighlight() ));
|
|
|
|
d->spellTimeout = new TQTimer(this);
|
|
|
|
connect( d->spellTimeout, TQT_SIGNAL( timeout() ),
|
|
|
|
this, TQT_SLOT( slotKSpellNotResponding() ));
|
|
|
|
|
|
|
|
if ( d->globalConfig ) {
|
|
|
|
d->spellKey = spellKey();
|
|
|
|
|
|
|
|
if ( !d->sDictionaryMonitor )
|
|
|
|
d->sDictionaryMonitor = new TQObject();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
d->mDict = new TQDict<int>(4001);
|
|
|
|
connect( d->mSpellConfig, TQT_SIGNAL( configChanged() ),
|
|
|
|
this, TQT_SLOT( slotLocalSpellConfigChanged() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
slotDictionaryChanged();
|
|
|
|
// whats this good for?
|
|
|
|
//startTimer( 2 * 1000 );
|
|
|
|
}
|
|
|
|
|
|
|
|
KDictSpellingHighlighter::~KDictSpellingHighlighter()
|
|
|
|
{
|
|
|
|
delete d->spell;
|
|
|
|
d->spell = 0;
|
|
|
|
delete d->mDict;
|
|
|
|
d->mDict = 0;
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KDictSpellingHighlighter::slotSpellReady( KSpell *spell )
|
|
|
|
{
|
|
|
|
kdDebug(0) << "KDictSpellingHighlighter::slotSpellReady( " << spell << " )" << endl;
|
|
|
|
KConfigGroup cg( KGlobal::config(),"KSpell" );
|
|
|
|
if ( cg.readEntry("KSpell_DoSpellChecking") != "0" )
|
|
|
|
{
|
|
|
|
if ( d->globalConfig ) {
|
|
|
|
connect( d->sDictionaryMonitor, TQT_SIGNAL( destroyed()),
|
|
|
|
this, TQT_SLOT( slotDictionaryChanged() ));
|
|
|
|
}
|
|
|
|
if ( spell != d->spell )
|
|
|
|
{
|
|
|
|
delete d->spell;
|
|
|
|
d->spell = spell;
|
|
|
|
}
|
|
|
|
d->spellReady = true;
|
|
|
|
const TQStringList l = KSpellingHighlighter::personalWords();
|
|
|
|
for ( TQStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) {
|
|
|
|
d->spell->addPersonal( *it );
|
|
|
|
}
|
|
|
|
connect( spell, TQT_SIGNAL( misspelling( const TQString &, const TQStringList &, unsigned int )),
|
|
|
|
this, TQT_SLOT( slotMisspelling( const TQString &, const TQStringList &, unsigned int )));
|
|
|
|
connect( spell, TQT_SIGNAL( corrected( const TQString &, const TQString &, unsigned int )),
|
|
|
|
this, TQT_SLOT( slotCorrected( const TQString &, const TQString &, unsigned int )));
|
|
|
|
d->checksRequested = 0;
|
|
|
|
d->checksDone = 0;
|
|
|
|
d->completeRehighlightRequired = true;
|
|
|
|
d->rehighlightRequest->start( 0, true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KDictSpellingHighlighter::isMisspelled( const TQString &word )
|
|
|
|
{
|
|
|
|
if (!d->spellReady)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// This debug is expensive, only enable it locally
|
|
|
|
//kdDebug(0) << "KDictSpellingHighlighter::isMisspelled( \"" << word << "\" )" << endl;
|
|
|
|
// Normally isMisspelled would look up a dictionary and return
|
|
|
|
// true or false, but kspell is asynchronous and slow so things
|
|
|
|
// get tricky...
|
|
|
|
// For auto detection ignore signature and reply prefix
|
|
|
|
if ( !d->autoReady )
|
|
|
|
d->autoIgnoreDict.replace( word, Ignore );
|
|
|
|
|
|
|
|
// "dict" is used as a cache to store the results of KSpell
|
|
|
|
TQDict<int>* dict = ( d->globalConfig ? d->sDict() : d->mDict );
|
|
|
|
if ( !dict->isEmpty() && (*dict)[word] == NotOkay ) {
|
|
|
|
if ( d->autoReady && ( d->autoDict[word] != NotOkay )) {
|
|
|
|
if ( !d->autoIgnoreDict[word] )
|
|
|
|
++d->errorCount;
|
|
|
|
d->autoDict.replace( word, NotOkay );
|
|
|
|
}
|
|
|
|
|
|
|
|
return d->active;
|
|
|
|
}
|
|
|
|
if ( !dict->isEmpty() && (*dict)[word] == Okay ) {
|
|
|
|
if ( d->autoReady && !d->autoDict[word] ) {
|
|
|
|
d->autoDict.replace( word, Okay );
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((dict->isEmpty() || !((*dict)[word])) && d->spell ) {
|
|
|
|
int para, index;
|
|
|
|
textEdit()->getCursorPosition( ¶, &index );
|
|
|
|
++d->wordCount;
|
|
|
|
dict->replace( word, Unknown );
|
|
|
|
++d->checksRequested;
|
|
|
|
if (currentParagraph() != para)
|
|
|
|
d->completeRehighlightRequired = true;
|
|
|
|
d->spellTimeout->start( tenSeconds, true );
|
|
|
|
d->spell->checkWord( word, false );
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KSpellingHighlighter::intraWordEditing() const
|
|
|
|
{
|
|
|
|
return d->intraWordEditing;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpellingHighlighter::setIntraWordEditing( bool editing )
|
|
|
|
{
|
|
|
|
d->intraWordEditing = editing;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KDictSpellingHighlighter::slotMisspelling (const TQString &originalWord, const TQStringList &suggestions,
|
|
|
|
unsigned int pos)
|
|
|
|
{
|
|
|
|
Q_UNUSED( suggestions );
|
|
|
|
// kdDebug() << suggestions.join( " " ).latin1() << endl;
|
|
|
|
if ( d->globalConfig )
|
|
|
|
d->sDict()->replace( originalWord, NotOkay );
|
|
|
|
else
|
|
|
|
d->mDict->replace( originalWord, NotOkay );
|
|
|
|
|
|
|
|
//Emit this baby so that apps that want to have suggestions in a popup over
|
|
|
|
//the misspelled word can catch them.
|
|
|
|
emit newSuggestions( originalWord, suggestions, pos );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KDictSpellingHighlighter::slotCorrected(const TQString &word,
|
|
|
|
const TQString &,
|
|
|
|
unsigned int)
|
|
|
|
|
|
|
|
{
|
|
|
|
TQDict<int>* dict = ( d->globalConfig ? d->sDict() : d->mDict );
|
|
|
|
if ( !dict->isEmpty() && (*dict)[word] == Unknown ) {
|
|
|
|
dict->replace( word, Okay );
|
|
|
|
}
|
|
|
|
++d->checksDone;
|
|
|
|
if (d->checksDone == d->checksRequested) {
|
|
|
|
d->spellTimeout->stop();
|
|
|
|
slotRehighlight();
|
|
|
|
} else {
|
|
|
|
d->spellTimeout->start( tenSeconds, true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KDictSpellingHighlighter::dictionaryChanged()
|
|
|
|
{
|
|
|
|
TQObject *oldMonitor = KDictSpellingHighlighterPrivate::sDictionaryMonitor;
|
|
|
|
KDictSpellingHighlighterPrivate::sDictionaryMonitor = new TQObject();
|
|
|
|
KDictSpellingHighlighterPrivate::sDict()->clear();
|
|
|
|
delete oldMonitor;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KDictSpellingHighlighter::restartBackgroundSpellCheck()
|
|
|
|
{
|
|
|
|
kdDebug(0) << "KDictSpellingHighlighter::restartBackgroundSpellCheck()" << endl;
|
|
|
|
slotDictionaryChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KDictSpellingHighlighter::setActive( bool active )
|
|
|
|
{
|
|
|
|
if ( active == d->active )
|
|
|
|
return;
|
|
|
|
|
|
|
|
d->active = active;
|
|
|
|
rehighlight();
|
|
|
|
if ( d->active )
|
|
|
|
emit activeChanged( i18n("As-you-type spell checking enabled.") );
|
|
|
|
else
|
|
|
|
emit activeChanged( i18n("As-you-type spell checking disabled.") );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KDictSpellingHighlighter::isActive() const
|
|
|
|
{
|
|
|
|
return d->active;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KDictSpellingHighlighter::setAutomatic( bool automatic )
|
|
|
|
{
|
|
|
|
if ( automatic == d->automatic )
|
|
|
|
return;
|
|
|
|
|
|
|
|
d->automatic = automatic;
|
|
|
|
if ( d->automatic )
|
|
|
|
slotAutoDetection();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KDictSpellingHighlighter::automatic() const
|
|
|
|
{
|
|
|
|
return d->automatic;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KDictSpellingHighlighter::slotRehighlight()
|
|
|
|
{
|
|
|
|
kdDebug(0) << "KDictSpellingHighlighter::slotRehighlight()" << endl;
|
|
|
|
if (d->completeRehighlightRequired) {
|
|
|
|
rehighlight();
|
|
|
|
} else {
|
|
|
|
int para, index;
|
|
|
|
textEdit()->getCursorPosition( ¶, &index );
|
|
|
|
//rehighlight the current para only (undo/redo safe)
|
|
|
|
bool modified = textEdit()->isModified();
|
|
|
|
textEdit()->insertAt( "", para, index );
|
|
|
|
textEdit()->setModified( modified );
|
|
|
|
}
|
|
|
|
if (d->checksDone == d->checksRequested)
|
|
|
|
d->completeRehighlightRequired = false;
|
|
|
|
TQTimer::singleShot( 0, this, TQT_SLOT( slotAutoDetection() ));
|
|
|
|
}
|
|
|
|
|
|
|
|
void KDictSpellingHighlighter::slotDictionaryChanged()
|
|
|
|
{
|
|
|
|
delete d->spell;
|
|
|
|
d->spellReady = false;
|
|
|
|
d->wordCount = 0;
|
|
|
|
d->errorCount = 0;
|
|
|
|
d->autoDict.clear();
|
|
|
|
|
|
|
|
d->spell = new KSpell( 0, i18n( "Incremental Spellcheck" ), this,
|
|
|
|
TQT_SLOT( slotSpellReady( KSpell * ) ), d->mSpellConfig );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KDictSpellingHighlighter::slotLocalSpellConfigChanged()
|
|
|
|
{
|
|
|
|
kdDebug(0) << "KDictSpellingHighlighter::slotSpellConfigChanged()" << endl;
|
|
|
|
// the spell config has been changed, so we have to restart from scratch
|
|
|
|
d->mDict->clear();
|
|
|
|
slotDictionaryChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KDictSpellingHighlighter::spellKey()
|
|
|
|
{
|
|
|
|
KConfig *config = KGlobal::config();
|
|
|
|
KConfigGroupSaver cs( config, "KSpell" );
|
|
|
|
config->reparseConfiguration();
|
|
|
|
TQString key;
|
|
|
|
key += TQString::number( config->readNumEntry( "KSpell_NoRootAffix", 0 ));
|
|
|
|
key += '/';
|
|
|
|
key += TQString::number( config->readNumEntry( "KSpell_RunTogether", 0 ));
|
|
|
|
key += '/';
|
|
|
|
key += config->readEntry( "KSpell_Dictionary", "" );
|
|
|
|
key += '/';
|
|
|
|
key += TQString::number( config->readNumEntry( "KSpell_DictFromList", false ));
|
|
|
|
key += '/';
|
|
|
|
key += TQString::number( config->readNumEntry( "KSpell_Encoding", KS_E_UTF8 ));
|
|
|
|
key += '/';
|
|
|
|
key += TQString::number( config->readNumEntry( "KSpell_Client", KS_CLIENT_ISPELL ));
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Automatic spell checking support
|
|
|
|
// In auto spell checking mode disable as-you-type spell checking
|
|
|
|
// iff more than one third of words are spelt incorrectly.
|
|
|
|
//
|
|
|
|
// Words in the signature and reply prefix are ignored.
|
|
|
|
// Only unique words are counted.
|
|
|
|
|
|
|
|
void KDictSpellingHighlighter::slotAutoDetection()
|
|
|
|
{
|
|
|
|
if ( !d->autoReady )
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool savedActive = d->active;
|
|
|
|
|
|
|
|
if ( d->automatic ) {
|
|
|
|
// tme = Too many errors
|
|
|
|
bool tme = d->wordCount >= d->disableWordCount && d->errorCount * 100 >= d->disablePercentage * d->wordCount;
|
|
|
|
if ( d->active && tme )
|
|
|
|
d->active = false;
|
|
|
|
else if ( !d->active && !tme )
|
|
|
|
d->active = true;
|
|
|
|
}
|
|
|
|
if ( d->active != savedActive ) {
|
|
|
|
if ( d->wordCount > 1 )
|
|
|
|
if ( d->active )
|
|
|
|
emit activeChanged( i18n("As-you-type spell checking enabled.") );
|
|
|
|
else
|
|
|
|
emit activeChanged( i18n( "Too many misspelled words. "
|
|
|
|
"As-you-type spell checking disabled." ) );
|
|
|
|
d->completeRehighlightRequired = true;
|
|
|
|
d->rehighlightRequest->start( 100, true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KDictSpellingHighlighter::slotKSpellNotResponding()
|
|
|
|
{
|
|
|
|
static int retries = 0;
|
|
|
|
if (retries < 10) {
|
|
|
|
if ( d->globalConfig )
|
|
|
|
KDictSpellingHighlighter::dictionaryChanged();
|
|
|
|
else
|
|
|
|
slotLocalSpellConfigChanged();
|
|
|
|
} else {
|
|
|
|
setAutomatic( false );
|
|
|
|
setActive( false );
|
|
|
|
}
|
|
|
|
++retries;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KDictSpellingHighlighter::eventFilter( TQObject *o, TQEvent *e)
|
|
|
|
{
|
|
|
|
if (o == textEdit() && (e->type() == TQEvent::FocusIn)) {
|
|
|
|
if ( d->globalConfig ) {
|
|
|
|
TQString skey = spellKey();
|
|
|
|
if ( d->spell && d->spellKey != skey ) {
|
|
|
|
d->spellKey = skey;
|
|
|
|
KDictSpellingHighlighter::dictionaryChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (o == textEdit() && (e->type() == TQEvent::KeyPress)) {
|
|
|
|
TQKeyEvent *k = static_cast<TQKeyEvent *>(e);
|
|
|
|
d->autoReady = true;
|
|
|
|
if (d->rehighlightRequest->isActive()) // try to stay out of the users way
|
|
|
|
d->rehighlightRequest->changeInterval( 500 );
|
|
|
|
if ( k->key() == Key_Enter ||
|
|
|
|
k->key() == Key_Return ||
|
|
|
|
k->key() == Key_Up ||
|
|
|
|
k->key() == Key_Down ||
|
|
|
|
k->key() == Key_Left ||
|
|
|
|
k->key() == Key_Right ||
|
|
|
|
k->key() == Key_PageUp ||
|
|
|
|
k->key() == Key_PageDown ||
|
|
|
|
k->key() == Key_Home ||
|
|
|
|
k->key() == Key_End ||
|
|
|
|
(( k->state() & ControlButton ) &&
|
|
|
|
((k->key() == Key_A) ||
|
|
|
|
(k->key() == Key_B) ||
|
|
|
|
(k->key() == Key_E) ||
|
|
|
|
(k->key() == Key_N) ||
|
|
|
|
(k->key() == Key_P))) ) {
|
|
|
|
if ( intraWordEditing() ) {
|
|
|
|
setIntraWordEditing( false );
|
|
|
|
d->completeRehighlightRequired = true;
|
|
|
|
d->rehighlightRequest->start( 500, true );
|
|
|
|
}
|
|
|
|
if (d->checksDone != d->checksRequested) {
|
|
|
|
// Handle possible change of paragraph while
|
|
|
|
// words are pending spell checking
|
|
|
|
d->completeRehighlightRequired = true;
|
|
|
|
d->rehighlightRequest->start( 500, true );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
setIntraWordEditing( true );
|
|
|
|
}
|
|
|
|
if ( k->key() == Key_Space ||
|
|
|
|
k->key() == Key_Enter ||
|
|
|
|
k->key() == Key_Return ) {
|
|
|
|
TQTimer::singleShot( 0, this, TQT_SLOT( slotAutoDetection() ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if ( o == textEdit()->viewport() &&
|
|
|
|
( e->type() == TQEvent::MouseButtonPress )) {
|
|
|
|
d->autoReady = true;
|
|
|
|
if ( intraWordEditing() ) {
|
|
|
|
setIntraWordEditing( false );
|
|
|
|
d->completeRehighlightRequired = true;
|
|
|
|
d->rehighlightRequest->start( 0, true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "ksyntaxhighlighter.moc"
|