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