KDEUI
highlighter.cpp
Go to the documentation of this file.00001
00023 #include "highlighter.h"
00024 #include "highlighter.moc"
00025
00026 #include "speller.h"
00027 #include "loader_p.h"
00028 #include "filter_p.h"
00029 #include "settings_p.h"
00030
00031 #include <kconfig.h>
00032 #include <kdebug.h>
00033 #include <klocale.h>
00034 #include <kmessagebox.h>
00035
00036 #include <QtGui/QTextEdit>
00037 #include <QtCore/QTimer>
00038 #include <QtGui/QColor>
00039 #include <QHash>
00040 #include <QTextCursor>
00041 #include <QEvent>
00042 #include <QKeyEvent>
00043
00044 namespace Sonnet {
00045
00046 class Highlighter::Private
00047 {
00048 public:
00049 ~Private();
00050 Filter *filter;
00051 Loader *loader;
00052 Speller *dict;
00053 QHash<QString, Speller*> dictCache;
00054 QTextEdit *edit;
00055 bool active;
00056 bool automatic;
00057 bool completeRehighlightRequired;
00058 bool intraWordEditing;
00059 bool spellCheckerFound;
00060 int disablePercentage;
00061 int disableWordCount;
00062 int wordCount, errorCount;
00063 QTimer *rehighlightRequest;
00064 QColor spellColor;
00065 int suggestionListeners;
00066 };
00067
00068 Highlighter::Private::~Private()
00069 {
00070 qDeleteAll(dictCache);
00071 }
00072
00073 Highlighter::Highlighter(QTextEdit *textEdit,
00074 const QString& configFile,
00075 const QColor& _col)
00076 : QSyntaxHighlighter(textEdit),
00077 d(new Private)
00078 {
00079 d->filter = Filter::defaultFilter();
00080 d->edit = textEdit;
00081 d->active = true;
00082 d->automatic = true;
00083 d->wordCount = 0;
00084 d->errorCount = 0;
00085 d->intraWordEditing = false;
00086 d->completeRehighlightRequired = false;
00087 d->spellCheckerFound = true;
00088 d->spellColor = _col.isValid() ? _col : Qt::red;
00089 d->suggestionListeners = 0;
00090
00091 textEdit->installEventFilter( this );
00092 textEdit->viewport()->installEventFilter( this );
00093
00094 d->loader = Loader::openLoader();
00095 KConfig conf(configFile);
00096 d->loader->settings()->restore(&conf);
00097 d->filter->setSettings(d->loader->settings());
00098 d->dict = new Sonnet::Speller();
00099 if(!d->dict->isValid()) {
00100 d->spellCheckerFound = false;
00101 } else {
00102 d->dictCache.insert(d->dict->language(),
00103 d->dict);
00104
00105 d->disablePercentage = d->loader->settings()->disablePercentageWordError();
00106
00107 d->disableWordCount = d->loader->settings()->disableWordErrorCount();
00108
00109
00110 const QStringList l = Highlighter::personalWords();
00111 for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) {
00112 d->dict->addToSession( *it );
00113 }
00114 d->rehighlightRequest = new QTimer(this);
00115 connect( d->rehighlightRequest, SIGNAL( timeout() ),
00116 this, SLOT( slotRehighlight() ));
00117 d->completeRehighlightRequired = true;
00118 d->rehighlightRequest->setInterval(0);
00119 d->rehighlightRequest->setSingleShot(true);
00120 d->rehighlightRequest->start();
00121 }
00122 }
00123
00124 Highlighter::~Highlighter()
00125 {
00126 delete d;
00127 }
00128
00129 bool Highlighter::spellCheckerFound() const
00130 {
00131 return d->spellCheckerFound;
00132 }
00133
00134
00135
00136
00137 void Highlighter::connectNotify(const char* signal)
00138 {
00139 if (QLatin1String(signal) == SIGNAL(newSuggestions(QString,QStringList)))
00140 ++d->suggestionListeners;
00141 QSyntaxHighlighter::connectNotify(signal);
00142 }
00143
00144 void Highlighter::disconnectNotify(const char* signal)
00145 {
00146 if (QLatin1String(signal) == SIGNAL(newSuggestions(QString,QStringList)))
00147 --d->suggestionListeners;
00148 QSyntaxHighlighter::disconnectNotify(signal);
00149 }
00150
00151 void Highlighter::slotRehighlight()
00152 {
00153 kDebug(0) << "Highlighter::slotRehighlight()";
00154 if (d->completeRehighlightRequired) {
00155 d->wordCount = 0;
00156 d->errorCount = 0;
00157 rehighlight();
00158
00159 } else {
00160
00161 QTextCursor cursor = d->edit->textCursor();
00162 cursor.insertText( "" );
00163 }
00164
00165
00166 QTimer::singleShot( 0, this, SLOT( slotAutoDetection() ));
00167 }
00168
00169
00170 QStringList Highlighter::personalWords()
00171 {
00172 QStringList l;
00173 l.append( "KMail" );
00174 l.append( "KOrganizer" );
00175 l.append( "KAddressBook" );
00176 l.append( "KHTML" );
00177 l.append( "KIO" );
00178 l.append( "KJS" );
00179 l.append( "Konqueror" );
00180 l.append( "Sonnet" );
00181 l.append( "Kontact" );
00182 l.append( "Qt" );
00183 return l;
00184 }
00185
00186 bool Highlighter::automatic() const
00187 {
00188 return d->automatic;
00189 }
00190
00191 bool Highlighter::intraWordEditing() const
00192 {
00193 return d->intraWordEditing;
00194 }
00195
00196 void Highlighter::setIntraWordEditing( bool editing )
00197 {
00198 d->intraWordEditing = editing;
00199 }
00200
00201
00202 void Highlighter::setAutomatic( bool automatic )
00203 {
00204 if ( automatic == d->automatic )
00205 return;
00206
00207 d->automatic = automatic;
00208 if ( d->automatic )
00209 slotAutoDetection();
00210 }
00211
00212 void Highlighter::slotAutoDetection()
00213 {
00214 bool savedActive = d->active;
00215
00216 if ( d->automatic ) {
00217
00218 bool tme = ( d->errorCount >= d->disableWordCount ) && ( d->errorCount * 100 >= d->disablePercentage * d->wordCount );
00219 if ( d->active && tme )
00220 d->active = false;
00221 else if ( !d->active && !tme )
00222 d->active = true;
00223 }
00224 if ( d->active != savedActive ) {
00225 if ( d->wordCount > 1 )
00226 if ( d->active )
00227 emit activeChanged( i18n("As-you-type spell checking enabled.") );
00228 else
00229 emit activeChanged( i18n( "Too many misspelled words. "
00230 "As-you-type spell checking disabled." ) );
00231 d->completeRehighlightRequired = true;
00232 d->rehighlightRequest->setInterval(100);
00233 d->rehighlightRequest->setSingleShot(true);
00234 kDebug()<<" Highlighter::slotAutoDetection :"<<d->active;
00235 }
00236
00237 }
00238
00239 void Highlighter::setActive( bool active )
00240 {
00241 if ( active == d->active )
00242 return;
00243 d->active = active;
00244 rehighlight();
00245
00246
00247 if ( d->active )
00248 emit activeChanged( i18n("As-you-type spell checking enabled.") );
00249 else
00250 emit activeChanged( i18n("As-you-type spell checking disabled.") );
00251 }
00252
00253 bool Highlighter::isActive() const
00254 {
00255 return d->active;
00256 }
00257
00258 void Highlighter::highlightBlock ( const QString & text )
00259 {
00260 if ( text.isEmpty() || !d->active || !d->spellCheckerFound)
00261 return;
00262 QTextCursor cursor = d->edit->textCursor();
00263 int index = cursor.position();
00264
00265 const int lengthPosition = text.length() - 1;
00266
00267 if ( index != lengthPosition ||
00268 ( lengthPosition > 0 && !text[lengthPosition-1].isLetter() ) ) {
00269 d->filter->setBuffer( text );
00270 Word w = d->filter->nextWord();
00271 while ( !w.end ) {
00272 ++d->wordCount;
00273 if (d->dict->isMisspelled(w.word)) {
00274 ++d->errorCount;
00275 setMisspelled(w.start, w.word.length());
00276 if (d->suggestionListeners)
00277 emit newSuggestions(w.word, d->dict->suggest(w.word));
00278 } else
00279 unsetMisspelled(w.start, w.word.length());
00280 w = d->filter->nextWord();
00281 }
00282 }
00283
00284 setCurrentBlockState(0);
00285 }
00286
00287 QString Highlighter::currentLanguage() const
00288 {
00289 return d->dict->language();
00290 }
00291
00292 void Highlighter::setCurrentLanguage(const QString &lang)
00293 {
00294 if (!d->dictCache.contains(lang)) {
00295 d->dict = new Speller(*d->dict);
00296 d->dict->setLanguage(lang);
00297 if (d->dict->isValid()) {
00298 d->dictCache.insert(lang, d->dict);
00299 } else {
00300 kDebug()<<"No dictionary for \""
00301 <<lang
00302 <<"\" staying with the current language."
00303 <<endl;
00304 return;
00305 }
00306 }
00307 d->dict = d->dictCache[lang];
00308 d->wordCount = 0;
00309 d->errorCount = 0;
00310 if (d->automatic)
00311 slotAutoDetection();
00312 }
00313
00314 void Highlighter::setMisspelled(int start, int count)
00315 {
00316 setFormat(start, count, d->spellColor);
00317 }
00318
00319 void Highlighter::unsetMisspelled( int start, int count )
00320 {
00321 setFormat( start, count, Qt::black );
00322 }
00323
00324 bool Highlighter::eventFilter( QObject *o, QEvent *e)
00325 {
00326 #if 0
00327 if (o == textEdit() && (e->type() == QEvent::FocusIn)) {
00328 if ( d->globalConfig ) {
00329 QString skey = spellKey();
00330 if ( d->spell && d->spellKey != skey ) {
00331 d->spellKey = skey;
00332 KDictSpellingHighlighter::dictionaryChanged();
00333 }
00334 }
00335 }
00336 #endif
00337 if (!d->spellCheckerFound)
00338 return false;
00339 if (o == d->edit && (e->type() == QEvent::KeyPress)) {
00340 QKeyEvent *k = static_cast<QKeyEvent *>(e);
00341
00342 if (d->rehighlightRequest->isActive())
00343 d->rehighlightRequest->start( 500 );
00344 if ( k->key() == Qt::Key_Enter ||
00345 k->key() == Qt::Key_Return ||
00346 k->key() == Qt::Key_Up ||
00347 k->key() == Qt::Key_Down ||
00348 k->key() == Qt::Key_Left ||
00349 k->key() == Qt::Key_Right ||
00350 k->key() == Qt::Key_PageUp ||
00351 k->key() == Qt::Key_PageDown ||
00352 k->key() == Qt::Key_Home ||
00353 k->key() == Qt::Key_End ||
00354 (( k->modifiers()== Qt::ControlModifier ) &&
00355 ((k->key() == Qt::Key_A) ||
00356 (k->key() == Qt::Key_B) ||
00357 (k->key() == Qt::Key_E) ||
00358 (k->key() == Qt::Key_N) ||
00359 (k->key() == Qt::Key_P))) ) {
00360 if ( intraWordEditing() ) {
00361 setIntraWordEditing( false );
00362 d->completeRehighlightRequired = true;
00363 d->rehighlightRequest->setInterval(500);
00364 d->rehighlightRequest->setSingleShot(true);
00365 d->rehighlightRequest->start();
00366 }
00367 #if 0
00368 if (d->checksDone != d->checksRequested) {
00369
00370
00371 d->completeRehighlightRequired = true;
00372 d->rehighlightRequest->start( 500, true );
00373 }
00374 #endif
00375 } else {
00376 setIntraWordEditing( true );
00377 }
00378 if ( k->key() == Qt::Key_Space ||
00379 k->key() == Qt::Key_Enter ||
00380 k->key() == Qt::Key_Return ) {
00381 QTimer::singleShot( 0, this, SLOT( slotAutoDetection() ));
00382 }
00383 }
00384
00385 else if ( o == d->edit->viewport() &&
00386 ( e->type() == QEvent::MouseButtonPress )) {
00387
00388 if ( intraWordEditing() ) {
00389 setIntraWordEditing( false );
00390 d->completeRehighlightRequired = true;
00391 d->rehighlightRequest->setInterval(0);
00392 d->rehighlightRequest->setSingleShot(true);
00393 d->rehighlightRequest->start();
00394 }
00395 }
00396 return false;
00397 }
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412 void Highlighter::addWordToDictionary(const QString &word)
00413 {
00414 d->dict->addToPersonal(word);
00415 }
00416
00417 void Highlighter::ignoreWord(const QString &word)
00418 {
00419 d->dict->addToSession(word);
00420 }
00421
00422 QStringList Highlighter::suggestionsForWord(const QString &word, int max)
00423 {
00424 QStringList suggestions = d->dict->suggest(word);
00425 if ( max != -1 ) {
00426 while ( suggestions.count() > max )
00427 suggestions.removeLast();
00428 }
00429 return suggestions;
00430 }
00431
00432 bool Highlighter::isWordMisspelled(const QString &word)
00433 {
00434 return d->dict->isMisspelled(word);
00435 }
00436
00437 }