• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

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; // #of connections for the newSuggestions signal
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     //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     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         //Add kde personal word
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 // Since figuring out spell correction suggestions is extremely costly,
00146 // we keep track of whether the user actually wants some, and only offer them 
00147 // in that case
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     //rehighlight the current para only (undo/redo safe)
00172         QTextCursor cursor = d->edit->textCursor();
00173         cursor.insertText( "" );
00174     }
00175     //if (d->checksDone == d->checksRequested)
00176     //d->completeRehighlightRequired = false;
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     //don't disable just because 1 of 4 is misspelled.
00228     if (d->automatic && d->wordCount >= 10) {
00229         // tme = Too many errors
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     //QTimer::singleShot( 0, this, SLOT(checkWords()) );
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     //d->autoReady = true;
00362     if (d->rehighlightRequest->isActive()) // try to stay out of the users way
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         // Handle possible change of paragraph while
00390         // words are pending spell checking
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     //d->autoReady = true;
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 }

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal