26 #include <tqsyntaxhighlighter.h>
30 #include <tdeconfig.h>
32 #include <tdeglobal.h>
34 #include <tdeapplication.h>
36 #include "ksyntaxhighlighter.h"
38 static int dummy, dummy2, dummy3, dummy4;
39 static int *Okay = &dummy;
40 static int *NotOkay = &dummy2;
41 static int *Ignore = &dummy3;
42 static int *Unknown = &dummy4;
43 static const int tenSeconds = 10*1000;
45 class KSyntaxHighlighter::KSyntaxHighlighterPrivate
48 TQColor col1, col2, col3, col4, col5;
53 class KSpellingHighlighter::KSpellingHighlighterPrivate
57 KSpellingHighlighterPrivate() :
58 alwaysEndsWithSpace( true ),
59 intraWordEditing( false ) {}
63 bool alwaysEndsWithSpace;
65 bool intraWordEditing;
68 class KDictSpellingHighlighter::KDictSpellingHighlighterPrivate
71 KDictSpellingHighlighterPrivate() :
75 rehighlightRequest( 0 ),
80 spellReady( false ) {}
82 ~KDictSpellingHighlighterPrivate() {
83 delete rehighlightRequest;
87 static TQDict<int>* sDict()
90 statDict =
new TQDict<int>(50021);
96 TQDict<int> autoIgnoreDict;
97 static TQObject *sDictionaryMonitor;
100 TQTimer *rehighlightRequest, *spellTimeout;
102 int wordCount, errorCount;
103 int checksRequested, checksDone;
104 int disablePercentage;
105 int disableWordCount;
106 bool completeRehighlightRequired;
108 bool globalConfig, spellReady;
110 static TQDict<int>* statDict;
114 TQDict<int>* KDictSpellingHighlighter::KDictSpellingHighlighterPrivate::statDict = 0;
117 KSyntaxHighlighter::KSyntaxHighlighter( TQTextEdit *textEdit,
119 const TQColor& depth0,
120 const TQColor& depth1,
121 const TQColor& depth2,
122 const TQColor& depth3,
124 : TQSyntaxHighlighter( textEdit )
126 d =
new KSyntaxHighlighterPrivate();
128 d->enabled = colorQuoting;
138 KSyntaxHighlighter::~KSyntaxHighlighter()
143 int KSyntaxHighlighter::highlightParagraph(
const TQString &text,
int )
146 setFormat( 0, text.length(), textEdit()->viewport()->paletteForegroundColor() );
150 TQString simplified = text;
151 simplified = TQString(simplified.replace( TQRegExp(
"\\s" ), TQString() )).replace(
'|', TQString::fromLatin1(
">") );
152 while ( simplified.startsWith( TQString::fromLatin1(
">>>>") ) )
153 simplified = simplified.mid(3);
154 if ( simplified.startsWith( TQString::fromLatin1(
">>>") ) || simplified.startsWith( TQString::fromLatin1(
"> > >") ) )
155 setFormat( 0, text.length(), d->col2 );
156 else if ( simplified.startsWith( TQString::fromLatin1(
">>") ) || simplified.startsWith( TQString::fromLatin1(
"> >") ) )
157 setFormat( 0, text.length(), d->col3 );
158 else if ( simplified.startsWith( TQString::fromLatin1(
">") ) )
159 setFormat( 0, text.length(), d->col4 );
161 setFormat( 0, text.length(), d->col5 );
165 KSpellingHighlighter::KSpellingHighlighter( TQTextEdit *textEdit,
166 const TQColor& spellColor,
168 const TQColor& depth0,
169 const TQColor& depth1,
170 const TQColor& depth2,
171 const TQColor& depth3 )
174 d =
new KSpellingHighlighterPrivate();
176 d->color = spellColor;
179 KSpellingHighlighter::~KSpellingHighlighter()
184 int KSpellingHighlighter::highlightParagraph(
const TQString &text,
190 TQString diffAndCo(
">|" );
192 bool isCode = diffAndCo.find(text[0]) != -1;
194 if ( !text.endsWith(
" ") )
195 d->alwaysEndsWithSpace =
false;
197 KSyntaxHighlighter::highlightParagraph( text, -2 );
201 textEdit()->getCursorPosition( ¶, &index );
202 int len = text.length();
203 if ( d->alwaysEndsWithSpace )
208 for (
int i = 0; i < len; i++ ) {
209 if ( !text[i].isLetter() && (!(text[i] ==
'\'')) ) {
210 if ( ( para != paraNo ) ||
211 !intraWordEditing() ||
212 ( i - d->currentWord.length() > (uint)index ) ||
218 d->currentPos = i + 1;
220 d->currentWord += text[i];
223 if ( !text[len - 1].isLetter() ||
224 (uint)( index + 1 ) != text.length() ||
231 TQStringList KSpellingHighlighter::personalWords()
235 l.append(
"KOrganizer" );
236 l.append(
"KAddressBook" );
237 l.append(
"TDEHTML" );
240 l.append(
"Konqueror" );
241 l.append(
"KSpell" );
242 l.append(
"Kontact" );
247 void KSpellingHighlighter::flushCurrentWord()
249 while ( d->currentWord[0].isPunct() ) {
250 d->currentWord = d->currentWord.mid( 1 );
255 while ( ( ch = d->currentWord[(
int) d->currentWord.length() - 1] ).isPunct() &&
256 ch !=
'(' && ch !=
'@' )
257 d->currentWord.truncate( d->currentWord.length() - 1 );
259 if ( !d->currentWord.isEmpty() ) {
260 if ( isMisspelled( d->currentWord ) ) {
261 setFormat( d->currentPos, d->currentWord.length(), d->color );
268 TQObject *KDictSpellingHighlighter::KDictSpellingHighlighterPrivate::sDictionaryMonitor = 0;
270 KDictSpellingHighlighter::KDictSpellingHighlighter( TQTextEdit *textEdit,
271 bool spellCheckingActive ,
273 const TQColor& spellColor,
275 const TQColor& depth0,
276 const TQColor& depth1,
277 const TQColor& depth2,
278 const TQColor& depth3,
280 : KSpellingHighlighter( textEdit, spellColor,
281 colorQuoting, depth0, depth1, depth2, depth3 )
283 d =
new KDictSpellingHighlighterPrivate();
285 d->mSpellConfig = spellConfig;
286 d->globalConfig = ( !spellConfig );
287 d->automatic = autoEnable;
288 d->active = spellCheckingActive;
289 d->checksRequested = 0;
291 d->completeRehighlightRequired =
false;
295 d->disablePercentage = config->
readNumEntry(
"KSpell_AsYouTypeDisablePercentage", 42 );
296 d->disablePercentage = TQMIN( d->disablePercentage, 101 );
297 d->disableWordCount = config->
readNumEntry(
"KSpell_AsYouTypeDisableWordCount", 100 );
299 textEdit->installEventFilter(
this );
300 textEdit->viewport()->installEventFilter(
this );
302 d->rehighlightRequest =
new TQTimer(
this);
303 connect( d->rehighlightRequest, TQ_SIGNAL( timeout() ),
304 this, TQ_SLOT( slotRehighlight() ));
305 d->spellTimeout =
new TQTimer(
this);
306 connect( d->spellTimeout, TQ_SIGNAL( timeout() ),
307 this, TQ_SLOT( slotKSpellNotResponding() ));
309 if ( d->globalConfig ) {
310 d->spellKey = spellKey();
312 if ( !d->sDictionaryMonitor )
313 d->sDictionaryMonitor =
new TQObject();
316 d->mDict =
new TQDict<int>(4001);
317 connect( d->mSpellConfig, TQ_SIGNAL( configChanged() ),
318 this, TQ_SLOT( slotLocalSpellConfigChanged() ) );
321 slotDictionaryChanged();
326 KDictSpellingHighlighter::~KDictSpellingHighlighter()
335 void KDictSpellingHighlighter::slotSpellReady(
KSpell *spell )
337 kdDebug(0) <<
"KDictSpellingHighlighter::slotSpellReady( " << spell <<
" )" <<
endl;
339 if ( cg.readEntry(
"KSpell_DoSpellChecking") !=
"0" )
341 if ( d->globalConfig ) {
342 connect( d->sDictionaryMonitor, TQ_SIGNAL( destroyed()),
343 this, TQ_SLOT( slotDictionaryChanged() ));
345 if ( spell != d->spell )
350 d->spellReady =
true;
351 const TQStringList l = KSpellingHighlighter::personalWords();
352 for ( TQStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) {
353 d->spell->addPersonal( *it );
355 connect( spell, TQ_SIGNAL( misspelling(
const TQString &,
const TQStringList &,
unsigned int )),
356 this, TQ_SLOT( slotMisspelling(
const TQString &,
const TQStringList &,
unsigned int )));
357 connect( spell, TQ_SIGNAL( corrected(
const TQString &,
const TQString &,
unsigned int )),
358 this, TQ_SLOT( slotCorrected(
const TQString &,
const TQString &,
unsigned int )));
359 d->checksRequested = 0;
361 d->completeRehighlightRequired =
true;
362 d->rehighlightRequest->start( 0,
true );
366 bool KDictSpellingHighlighter::isMisspelled(
const TQString &word )
378 d->autoIgnoreDict.replace( word, Ignore );
381 TQDict<int>* dict = ( d->globalConfig ? d->sDict() : d->mDict );
382 if ( !dict->isEmpty() && (*dict)[word] == NotOkay ) {
383 if ( d->autoReady && ( d->autoDict[word] != NotOkay )) {
384 if ( !d->autoIgnoreDict[word] )
386 d->autoDict.replace( word, NotOkay );
391 if ( !dict->isEmpty() && (*dict)[word] == Okay ) {
392 if ( d->autoReady && !d->autoDict[word] ) {
393 d->autoDict.replace( word, Okay );
398 if ((dict->isEmpty() || !((*dict)[word])) && d->spell ) {
400 textEdit()->getCursorPosition( ¶, &index );
402 dict->replace( word, Unknown );
403 ++d->checksRequested;
404 if (currentParagraph() != para)
405 d->completeRehighlightRequired =
true;
406 d->spellTimeout->start( tenSeconds,
true );
407 d->spell->checkWord( word,
false );
412 bool KSpellingHighlighter::intraWordEditing()
const
414 return d->intraWordEditing;
417 void KSpellingHighlighter::setIntraWordEditing(
bool editing )
419 d->intraWordEditing = editing;
422 void KDictSpellingHighlighter::slotMisspelling (
const TQString &originalWord,
const TQStringList &suggestions,
425 Q_UNUSED( suggestions );
427 if ( d->globalConfig )
428 d->sDict()->replace( originalWord, NotOkay );
430 d->mDict->replace( originalWord, NotOkay );
434 emit newSuggestions( originalWord, suggestions, pos );
437 void KDictSpellingHighlighter::slotCorrected(
const TQString &word,
442 TQDict<int>* dict = ( d->globalConfig ? d->sDict() : d->mDict );
443 if ( !dict->isEmpty() && (*dict)[word] == Unknown ) {
444 dict->replace( word, Okay );
447 if (d->checksDone == d->checksRequested) {
448 d->spellTimeout->stop();
451 d->spellTimeout->start( tenSeconds,
true );
455 void KDictSpellingHighlighter::dictionaryChanged()
457 TQObject *oldMonitor = KDictSpellingHighlighterPrivate::sDictionaryMonitor;
458 KDictSpellingHighlighterPrivate::sDictionaryMonitor =
new TQObject();
459 KDictSpellingHighlighterPrivate::sDict()->clear();
463 void KDictSpellingHighlighter::restartBackgroundSpellCheck()
465 kdDebug(0) <<
"KDictSpellingHighlighter::restartBackgroundSpellCheck()" <<
endl;
466 slotDictionaryChanged();
471 if ( active == d->active )
477 emit activeChanged( i18n(
"As-you-type spell checking enabled.") );
479 emit activeChanged( i18n(
"As-you-type spell checking disabled.") );
502 void KDictSpellingHighlighter::slotRehighlight()
504 kdDebug(0) <<
"KDictSpellingHighlighter::slotRehighlight()" <<
endl;
505 if (d->completeRehighlightRequired) {
509 textEdit()->getCursorPosition( ¶, &index );
511 bool modified = textEdit()->isModified();
512 textEdit()->insertAt(
"", para, index );
513 textEdit()->setModified( modified );
515 if (d->checksDone == d->checksRequested)
516 d->completeRehighlightRequired =
false;
517 TQTimer::singleShot( 0,
this, TQ_SLOT( slotAutoDetection() ));
520 void KDictSpellingHighlighter::slotDictionaryChanged()
523 d->spellReady =
false;
528 d->spell =
new KSpell( 0, i18n(
"Incremental Spellcheck" ),
this,
529 TQ_SLOT( slotSpellReady(
KSpell * ) ), d->mSpellConfig );
532 void KDictSpellingHighlighter::slotLocalSpellConfigChanged()
534 kdDebug(0) <<
"KDictSpellingHighlighter::slotSpellConfigChanged()" <<
endl;
537 slotDictionaryChanged();
540 TQString KDictSpellingHighlighter::spellKey()
546 key += TQString::number( config->
readNumEntry(
"KSpell_NoRootAffix", 0 ));
548 key += TQString::number( config->
readNumEntry(
"KSpell_RunTogether", 0 ));
552 key += TQString::number( config->
readNumEntry(
"KSpell_DictFromList",
false ));
554 key += TQString::number( config->
readNumEntry(
"KSpell_Encoding", KS_E_UTF8 ));
556 key += TQString::number( config->
readNumEntry(
"KSpell_Client", KS_CLIENT_ISPELL ));
568 void KDictSpellingHighlighter::slotAutoDetection()
573 bool savedActive = d->active;
575 if ( d->automatic ) {
577 bool tme = d->wordCount >= d->disableWordCount && d->errorCount * 100 >= d->disablePercentage * d->wordCount;
578 if ( d->active && tme )
580 else if ( !d->active && !tme )
583 if ( d->active != savedActive ) {
584 if ( d->wordCount > 1 )
586 emit activeChanged( i18n(
"As-you-type spell checking enabled.") );
588 emit activeChanged( i18n(
"Too many misspelled words. "
589 "As-you-type spell checking disabled." ) );
590 d->completeRehighlightRequired =
true;
591 d->rehighlightRequest->start( 100,
true );
595 void KDictSpellingHighlighter::slotKSpellNotResponding()
597 static int retries = 0;
599 if ( d->globalConfig )
600 KDictSpellingHighlighter::dictionaryChanged();
602 slotLocalSpellConfigChanged();
610 bool KDictSpellingHighlighter::eventFilter( TQObject *o, TQEvent *e)
612 if (o == textEdit() && (e->type() == TQEvent::FocusIn)) {
613 if ( d->globalConfig ) {
614 TQString skey = spellKey();
615 if ( d->spell && d->spellKey != skey ) {
617 KDictSpellingHighlighter::dictionaryChanged();
622 if (o == textEdit() && (e->type() == TQEvent::KeyPress)) {
623 TQKeyEvent *k =
static_cast<TQKeyEvent*
>(e);
625 if (d->rehighlightRequest->isActive())
626 d->rehighlightRequest->changeInterval( 500 );
627 if ( k->key() == Key_Enter ||
628 k->key() == Key_Return ||
629 k->key() == Key_Up ||
630 k->key() == Key_Down ||
631 k->key() == Key_Left ||
632 k->key() == Key_Right ||
633 k->key() == Key_PageUp ||
634 k->key() == Key_PageDown ||
635 k->key() == Key_Home ||
636 k->key() == Key_End ||
637 (( k->state() & ControlButton ) &&
638 ((k->key() == Key_A) ||
639 (k->key() == Key_B) ||
640 (k->key() == Key_E) ||
641 (k->key() == Key_N) ||
642 (k->key() == Key_P))) ) {
643 if ( intraWordEditing() ) {
644 setIntraWordEditing(
false );
645 d->completeRehighlightRequired =
true;
646 d->rehighlightRequest->start( 500,
true );
648 if (d->checksDone != d->checksRequested) {
651 d->completeRehighlightRequired =
true;
652 d->rehighlightRequest->start( 500,
true );
655 setIntraWordEditing(
true );
657 if ( k->key() == Key_Space ||
658 k->key() == Key_Enter ||
659 k->key() == Key_Return ) {
660 TQTimer::singleShot( 0,
this, TQ_SLOT( slotAutoDetection() ));
664 else if ( o == textEdit()->viewport() &&
665 ( e->type() == TQEvent::MouseButtonPress )) {
667 if ( intraWordEditing() ) {
668 setIntraWordEditing(
false );
669 d->completeRehighlightRequired =
true;
670 d->rehighlightRequest->start( 0,
true );
677 #include "ksyntaxhighlighter.moc"
void setAutomatic(bool automatic)
En-/Disable automatic (de)activation in case of too many errors.
void setActive(bool active)
Enable/Disable spell checking.
bool isActive() const
Returns the state of spell checking.
bool automatic() const
Returns the state of automatic (de)activation.
A configuration class/dialog for KSpell.
Syntax sensitive text highlighter.
TQString readEntry(const TQString &pKey, const TQString &aDefault=TQString::null) const
int readNumEntry(const TQString &pKey, int nDefault=0) const
virtual void reparseConfiguration()
static TDEConfig * config()
kndbgstream & endl(kndbgstream &s)
kdbgstream kdDebug(int area=0)