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