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 <QTextEdit> 00037 #include <QTextCharFormat> 00038 #include <QTimer> 00039 #include <QColor> 00040 #include <QHash> 00041 #include <QTextCursor> 00042 #include <QEvent> 00043 #include <QKeyEvent> 00044 #include <QApplication> 00045 00046 namespace Sonnet { 00047 00048 class Highlighter::Private 00049 { 00050 public: 00051 ~Private(); 00052 Filter *filter; 00053 Loader *loader; 00054 Speller *dict; 00055 QHash<QString, Speller*> dictCache; 00056 QTextEdit *edit; 00057 bool active; 00058 bool automatic; 00059 bool completeRehighlightRequired; 00060 bool intraWordEditing; 00061 bool spellCheckerFound; //cached d->dict->isValid() value 00062 int disablePercentage; 00063 int disableWordCount; 00064 int wordCount, errorCount; 00065 QTimer *rehighlightRequest; 00066 QColor spellColor; 00067 int suggestionListeners; // #of connections for the newSuggestions signal 00068 }; 00069 00070 Highlighter::Private::~Private() 00071 { 00072 qDeleteAll(dictCache); 00073 delete filter; 00074 } 00075 00076 Highlighter::Highlighter(QTextEdit *textEdit, 00077 const QString& configFile, 00078 const QColor& _col) 00079 : QSyntaxHighlighter(textEdit), 00080 d(new Private) 00081 { 00082 d->filter = Filter::defaultFilter(); 00083 d->edit = textEdit; 00084 d->active = true; 00085 d->automatic = true; 00086 d->wordCount = 0; 00087 d->errorCount = 0; 00088 d->intraWordEditing = false; 00089 d->completeRehighlightRequired = false; 00090 d->spellColor = _col.isValid() ? _col : Qt::red; 00091 d->suggestionListeners = 0; 00092 00093 textEdit->installEventFilter( this ); 00094 textEdit->viewport()->installEventFilter( this ); 00095 00096 d->loader = Loader::openLoader(); 00097 00098 //Do not load an empty settings file as it will cause the spellchecker to fail 00099 //if the KGlobal::locale()->language() (default value) spellchecker is not installed, 00100 //and we have a global sonnetrc file with a spellcheck lang installed which could be used. 00101 if (!configFile.isEmpty()) { 00102 KConfig conf(configFile); 00103 if (conf.hasGroup("Spelling")) { 00104 d->loader->settings()->restore(&conf); 00105 d->filter->setSettings(d->loader->settings()); 00106 } 00107 } 00108 00109 d->dict = new Sonnet::Speller(); 00110 d->spellCheckerFound = d->dict->isValid(); 00111 d->rehighlightRequest = new QTimer(this); 00112 connect( d->rehighlightRequest, SIGNAL(timeout()), 00113 this, SLOT(slotRehighlight())); 00114 00115 if(!d->spellCheckerFound) 00116 return; 00117 00118 d->dictCache.insert(d->dict->language(), d->dict); 00119 00120 d->disablePercentage = d->loader->settings()->disablePercentageWordError(); 00121 d->disableWordCount = d->loader->settings()->disableWordErrorCount(); 00122 00123 //Add kde personal word 00124 const QStringList l = Highlighter::personalWords(); 00125 for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) { 00126 d->dict->addToSession( *it ); 00127 } 00128 d->completeRehighlightRequired = true; 00129 d->rehighlightRequest->setInterval(0); 00130 d->rehighlightRequest->setSingleShot(true); 00131 d->rehighlightRequest->start(); 00132 } 00133 00134 Highlighter::~Highlighter() 00135 { 00136 delete d; 00137 } 00138 00139 bool Highlighter::spellCheckerFound() const 00140 { 00141 return d->spellCheckerFound; 00142 } 00143 00144 // Since figuring out spell correction suggestions is extremely costly, 00145 // we keep track of whether the user actually wants some, and only offer them 00146 // in that case 00147 void Highlighter::connectNotify(const char* signal) 00148 { 00149 if (QLatin1String(signal) == SIGNAL(newSuggestions(QString,QStringList))) 00150 ++d->suggestionListeners; 00151 QSyntaxHighlighter::connectNotify(signal); 00152 } 00153 00154 void Highlighter::disconnectNotify(const char* signal) 00155 { 00156 if (QLatin1String(signal) == SIGNAL(newSuggestions(QString,QStringList))) 00157 --d->suggestionListeners; 00158 QSyntaxHighlighter::disconnectNotify(signal); 00159 } 00160 00161 void Highlighter::slotRehighlight() 00162 { 00163 kDebug(0) << "Highlighter::slotRehighlight()"; 00164 if (d->completeRehighlightRequired) { 00165 d->wordCount = 0; 00166 d->errorCount = 0; 00167 rehighlight(); 00168 00169 } else { 00170 //rehighlight the current para only (undo/redo safe) 00171 QTextCursor cursor = d->edit->textCursor(); 00172 cursor.insertText( "" ); 00173 } 00174 //if (d->checksDone == d->checksRequested) 00175 //d->completeRehighlightRequired = false; 00176 QTimer::singleShot( 0, this, SLOT(slotAutoDetection())); 00177 } 00178 00179 00180 QStringList Highlighter::personalWords() 00181 { 00182 QStringList l; 00183 l.append( "KMail" ); 00184 l.append( "KOrganizer" ); 00185 l.append( "KAddressBook" ); 00186 l.append( "KHTML" ); 00187 l.append( "KIO" ); 00188 l.append( "KJS" ); 00189 l.append( "Konqueror" ); 00190 l.append( "Sonnet" ); 00191 l.append( "Kontact" ); 00192 l.append( "Qt" ); 00193 return l; 00194 } 00195 00196 bool Highlighter::automatic() const 00197 { 00198 return d->automatic; 00199 } 00200 00201 bool Highlighter::intraWordEditing() const 00202 { 00203 return d->intraWordEditing; 00204 } 00205 00206 void Highlighter::setIntraWordEditing( bool editing ) 00207 { 00208 d->intraWordEditing = editing; 00209 } 00210 00211 00212 void Highlighter::setAutomatic( bool automatic ) 00213 { 00214 if ( automatic == d->automatic ) 00215 return; 00216 00217 d->automatic = automatic; 00218 if ( d->automatic ) 00219 slotAutoDetection(); 00220 } 00221 00222 void Highlighter::slotAutoDetection() 00223 { 00224 bool savedActive = d->active; 00225 00226 //don't disable just because 1 of 4 is misspelled. 00227 if (d->automatic && d->wordCount >= 10) { 00228 // tme = Too many errors 00229 bool tme = (d->errorCount >= d->disableWordCount) && ( 00230 d->errorCount * 100 >= d->disablePercentage * d->wordCount); 00231 if (d->active && tme) { 00232 d->active = false; 00233 } else if (!d->active && !tme) { 00234 d->active = true; 00235 } 00236 } 00237 00238 if (d->active != savedActive) { 00239 if (d->active) { 00240 emit activeChanged(i18n("As-you-type spell checking enabled.")); 00241 } else { 00242 emit activeChanged(i18n( "Too many misspelled words. " 00243 "As-you-type spell checking disabled.")); 00244 } 00245 00246 d->completeRehighlightRequired = true; 00247 d->rehighlightRequest->setInterval(100); 00248 d->rehighlightRequest->setSingleShot(true); 00249 kDebug()<<" Highlighter::slotAutoDetection :"<<d->active; 00250 } 00251 00252 } 00253 00254 void Highlighter::setActive( bool active ) 00255 { 00256 if ( active == d->active ) 00257 return; 00258 d->active = active; 00259 rehighlight(); 00260 00261 00262 if ( d->active ) 00263 emit activeChanged( i18n("As-you-type spell checking enabled.") ); 00264 else 00265 emit activeChanged( i18n("As-you-type spell checking disabled.") ); 00266 } 00267 00268 bool Highlighter::isActive() const 00269 { 00270 return d->active; 00271 } 00272 00273 void Highlighter::highlightBlock(const QString &text) 00274 { 00275 if (text.isEmpty() || !d->active || !d->spellCheckerFound) 00276 return; 00277 00278 d->filter->setBuffer( text ); 00279 Word w = d->filter->nextWord(); 00280 while ( !w.end ) { 00281 ++d->wordCount; 00282 if (d->dict->isMisspelled(w.word)) { 00283 ++d->errorCount; 00284 setMisspelled(w.start, w.word.length()); 00285 if (d->suggestionListeners) 00286 emit newSuggestions(w.word, d->dict->suggest(w.word)); 00287 } else 00288 unsetMisspelled(w.start, w.word.length()); 00289 w = d->filter->nextWord(); 00290 } 00291 //QTimer::singleShot( 0, this, SLOT(checkWords()) ); 00292 setCurrentBlockState(0); 00293 } 00294 00295 QString Highlighter::currentLanguage() const 00296 { 00297 return d->dict->language(); 00298 } 00299 00300 void Highlighter::setCurrentLanguage(const QString &lang) 00301 { 00302 if (!d->dictCache.contains(lang)) { 00303 d->dict = new Speller(*d->dict); 00304 d->dict->setLanguage(lang); 00305 if (d->dict->isValid()) { 00306 d->dictCache.insert(lang, d->dict); 00307 } else { 00308 d->spellCheckerFound = false; 00309 kDebug()<<"No dictionary for \"" 00310 <<lang 00311 <<"\" staying with the current language." 00312 <<endl; 00313 return; 00314 } 00315 } 00316 d->dict = d->dictCache[lang]; 00317 d->spellCheckerFound = d->dict->isValid(); 00318 d->wordCount = 0; 00319 d->errorCount = 0; 00320 if (d->automatic) 00321 slotAutoDetection(); 00322 } 00323 00324 void Highlighter::setMisspelled(int start, int count) 00325 { 00326 QTextCharFormat format; 00327 format.setFontUnderline(true); 00328 format.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline); 00329 format.setUnderlineColor(d->spellColor); 00330 setFormat(start, count, format); 00331 } 00332 00333 void Highlighter::unsetMisspelled( int start, int count ) 00334 { 00335 setFormat(start, count, QTextCharFormat()); 00336 } 00337 00338 bool Highlighter::eventFilter( QObject *o, QEvent *e) 00339 { 00340 #if 0 00341 if (o == textEdit() && (e->type() == QEvent::FocusIn)) { 00342 if ( d->globalConfig ) { 00343 QString skey = spellKey(); 00344 if ( d->spell && d->spellKey != skey ) { 00345 d->spellKey = skey; 00346 KDictSpellingHighlighter::dictionaryChanged(); 00347 } 00348 } 00349 } 00350 #endif 00351 if (!d->spellCheckerFound) 00352 return false; 00353 if (o == d->edit && (e->type() == QEvent::KeyPress)) { 00354 QKeyEvent *k = static_cast<QKeyEvent *>(e); 00355 //d->autoReady = true; 00356 if (d->rehighlightRequest->isActive()) // try to stay out of the users way 00357 d->rehighlightRequest->start( 500 ); 00358 if ( k->key() == Qt::Key_Enter || 00359 k->key() == Qt::Key_Return || 00360 k->key() == Qt::Key_Up || 00361 k->key() == Qt::Key_Down || 00362 k->key() == Qt::Key_Left || 00363 k->key() == Qt::Key_Right || 00364 k->key() == Qt::Key_PageUp || 00365 k->key() == Qt::Key_PageDown || 00366 k->key() == Qt::Key_Home || 00367 k->key() == Qt::Key_End || 00368 (( k->modifiers()== Qt::ControlModifier ) && 00369 ((k->key() == Qt::Key_A) || 00370 (k->key() == Qt::Key_B) || 00371 (k->key() == Qt::Key_E) || 00372 (k->key() == Qt::Key_N) || 00373 (k->key() == Qt::Key_P))) ) { 00374 if ( intraWordEditing() ) { 00375 setIntraWordEditing( false ); 00376 d->completeRehighlightRequired = true; 00377 d->rehighlightRequest->setInterval(500); 00378 d->rehighlightRequest->setSingleShot(true); 00379 d->rehighlightRequest->start(); 00380 } 00381 #if 0 00382 if (d->checksDone != d->checksRequested) { 00383 // Handle possible change of paragraph while 00384 // words are pending spell checking 00385 d->completeRehighlightRequired = true; 00386 d->rehighlightRequest->start( 500, true ); 00387 } 00388 #endif 00389 } else { 00390 setIntraWordEditing( true ); 00391 } 00392 if ( k->key() == Qt::Key_Space || 00393 k->key() == Qt::Key_Enter || 00394 k->key() == Qt::Key_Return ) { 00395 QTimer::singleShot( 0, this, SLOT(slotAutoDetection())); 00396 } 00397 } 00398 00399 else if ( o == d->edit->viewport() && 00400 ( e->type() == QEvent::MouseButtonPress )) { 00401 //d->autoReady = true; 00402 if ( intraWordEditing() ) { 00403 setIntraWordEditing( false ); 00404 d->completeRehighlightRequired = true; 00405 d->rehighlightRequest->setInterval(0); 00406 d->rehighlightRequest->setSingleShot(true); 00407 d->rehighlightRequest->start(); 00408 } 00409 } 00410 return false; 00411 } 00412 00413 void Highlighter::addWordToDictionary(const QString &word) 00414 { 00415 d->dict->addToPersonal(word); 00416 } 00417 00418 void Highlighter::ignoreWord(const QString &word) 00419 { 00420 d->dict->addToSession(word); 00421 } 00422 00423 QStringList Highlighter::suggestionsForWord(const QString &word, int max) 00424 { 00425 QStringList suggestions = d->dict->suggest(word); 00426 if ( max != -1 ) { 00427 while ( suggestions.count() > max ) 00428 suggestions.removeLast(); 00429 } 00430 return suggestions; 00431 } 00432 00433 bool Highlighter::isWordMisspelled(const QString &word) 00434 { 00435 return d->dict->isMisspelled(word); 00436 } 00437 00438 void Highlighter::setMisspelledColor(const QColor &color) 00439 { 00440 d->spellColor = color; 00441 } 00442 00443 bool Highlighter::checkerEnabledByDefault() const 00444 { 00445 return d->loader->settings()->checkerEnabledByDefault(); 00446 } 00447 00448 00449 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:53:00 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:53:00 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.