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