• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDEUI

ktextedit.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
00003                  2005 Michael Brade <brade@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018    Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "ktextedit.h"
00022 #include <ktoolinvocation.h>
00023 #include <kdebug.h>
00024 
00025 #include <QApplication>
00026 #include <QClipboard>
00027 #include <QKeyEvent>
00028 #include <QMenu>
00029 #include <QPainter>
00030 #include <QScrollBar>
00031 #include <QTextCursor>
00032 #include <QTextDocumentFragment>
00033 #include <QDBusInterface>
00034 #include <QDBusConnection>
00035 #include <QDBusConnectionInterface>
00036 
00037 #include <configdialog.h>
00038 #include <dialog.h>
00039 #include "backgroundchecker.h"
00040 #include <kaction.h>
00041 #include <kcursor.h>
00042 #include <kglobalsettings.h>
00043 #include <kstandardaction.h>
00044 #include <kstandardshortcut.h>
00045 #include <kicon.h>
00046 #include <kiconloader.h>
00047 #include <klocale.h>
00048 #include <kdialog.h>
00049 #include <kreplacedialog.h>
00050 #include <kfinddialog.h>
00051 #include <kfind.h>
00052 #include <kreplace.h>
00053 #include <kmessagebox.h>
00054 #include <kmenu.h>
00055 #include <kwindowsystem.h>
00056 #include <QDebug>
00057 
00058 class KTextEdit::Private
00059 {
00060   public:
00061     Private( KTextEdit *_parent )
00062       : parent( _parent ),
00063         customPalette( false ),
00064         checkSpellingEnabled( false ),
00065         findReplaceEnabled(true),
00066         highlighter( 0 ), findDlg(0),find(0),repDlg(0),replace(0), findIndex(0), repIndex(0),
00067         lastReplacedPosition(-1)
00068     {
00069         //Check the default sonnet settings to see if spellchecking should be enabled.
00070         sonnetKConfig = new KConfig("sonnetrc");
00071         KConfigGroup group(sonnetKConfig, "Spelling");
00072         checkSpellingEnabled = group.readEntry("checkerEnabledByDefault", false);
00073 
00074         // i18n: Placeholder text in text edit widgets is the text appearing
00075         // before any user input, briefly explaining to the user what to type
00076         // (e.g. "Enter message").
00077         // By default the text is set in italic, which may not be appropriate
00078         // for some languages and scripts (e.g. for CJK ideographs).
00079         QString metaMsg = i18nc("Italic placeholder text in line edits: 0 no, 1 yes", "1");
00080         italicizePlaceholder = (metaMsg.trimmed() != QString('0'));
00081     }
00082 
00083     ~Private()
00084     {
00085       delete highlighter;
00086       delete findDlg;
00087       delete find;
00088       delete replace;
00089       delete repDlg;
00090       delete sonnetKConfig;
00091     }
00092 
00098     bool overrideShortcut(const QKeyEvent* e);
00102     bool handleShortcut(const QKeyEvent* e);
00103 
00104     void spellCheckerMisspelling( const QString &text, int pos );
00105     void spellCheckerCorrected( const QString &, int,const QString &);
00106     void spellCheckerAutoCorrect(const QString&,const QString&);
00107     void spellCheckerCanceled();
00108     void spellCheckerFinished();
00109     void toggleAutoSpellCheck();
00110 
00111     void slotFindHighlight(const QString& text, int matchingIndex, int matchingLength);
00112     void slotReplaceText(const QString &text, int replacementIndex, int /*replacedLength*/, int matchedLength);
00113 
00118     void undoableClear();
00119 
00120     void slotAllowTab();
00121     void menuActivated( QAction* action );
00122 
00123     void updateClickMessageRect();
00124 
00125     void init();
00126 
00127     KTextEdit *parent;
00128     KTextEditSpellInterface *spellInterface;
00129     QAction *autoSpellCheckAction;
00130     QAction *allowTab;
00131     QAction *spellCheckAction;
00132     QString clickMessage;
00133     bool italicizePlaceholder : 1;
00134     bool customPalette : 1;
00135 
00136     bool checkSpellingEnabled : 1;
00137     bool findReplaceEnabled: 1;
00138     QTextDocumentFragment originalDoc;
00139     QString spellCheckingConfigFileName;
00140     QString spellCheckingLanguage;
00141     Sonnet::Highlighter *highlighter;
00142     KFindDialog *findDlg;
00143     KFind *find;
00144     KReplaceDialog *repDlg;
00145     KReplace *replace;
00146     int findIndex, repIndex;
00147     int lastReplacedPosition;
00148     KConfig *sonnetKConfig;
00149 };
00150 
00151 void KTextEdit::Private::spellCheckerCanceled()
00152 {
00153     QTextDocument *doc = parent->document();
00154     doc->clear();
00155     QTextCursor cursor(doc);
00156     cursor.insertFragment(originalDoc);
00157     spellCheckerFinished();
00158 }
00159 
00160 void KTextEdit::Private::spellCheckerAutoCorrect(const QString&,const QString&)
00161 {
00162     //TODO
00163 }
00164 
00165 void KTextEdit::Private::spellCheckerMisspelling( const QString &text, int pos )
00166 {
00167     //kDebug()<<"TextEdit::Private::spellCheckerMisspelling :"<<text<<" pos :"<<pos;
00168     parent->highlightWord( text.length(), pos );
00169 }
00170 
00171 void KTextEdit::Private::spellCheckerCorrected( const QString& oldWord, int pos,const QString& newWord)
00172 {
00173   //kDebug()<<" oldWord :"<<oldWord<<" newWord :"<<newWord<<" pos : "<<pos;
00174   if (oldWord != newWord ) {
00175     QTextCursor cursor(parent->document());
00176     cursor.setPosition(pos);
00177     cursor.setPosition(pos+oldWord.length(),QTextCursor::KeepAnchor);
00178     cursor.insertText(newWord);
00179   }
00180 }
00181 
00182 void KTextEdit::Private::spellCheckerFinished()
00183 {
00184    QTextCursor cursor(parent->document());
00185    cursor.clearSelection();
00186    parent->setTextCursor(cursor);
00187    if (parent->highlighter())
00188        parent->highlighter()->rehighlight();
00189 }
00190 
00191 void KTextEdit::Private::toggleAutoSpellCheck()
00192 {
00193   parent->setCheckSpellingEnabled( !parent->checkSpellingEnabled() );
00194 }
00195 
00196 void KTextEdit::Private::undoableClear()
00197 {
00198     QTextCursor cursor = parent->textCursor();
00199     cursor.beginEditBlock();
00200     cursor.movePosition(QTextCursor::Start);
00201     cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
00202     cursor.removeSelectedText();
00203     cursor.endEditBlock();
00204 }
00205 
00206 void KTextEdit::Private::slotAllowTab()
00207 {
00208   parent->setTabChangesFocus( !parent->tabChangesFocus() );
00209 }
00210 
00211 void KTextEdit::Private::menuActivated( QAction* action )
00212 {
00213   if ( action == spellCheckAction )
00214     parent->checkSpelling();
00215   else if ( action == autoSpellCheckAction )
00216     toggleAutoSpellCheck();
00217   else if ( action == allowTab )
00218     slotAllowTab();
00219 }
00220 
00221 
00222 void KTextEdit::Private::slotFindHighlight(const QString& text, int matchingIndex, int matchingLength)
00223 {
00224     Q_UNUSED(text)
00225     //kDebug() << "Highlight: [" << text << "] mi:" << matchingIndex << " ml:" << matchingLength;
00226     QTextCursor tc = parent->textCursor();
00227     tc.setPosition(matchingIndex);
00228     tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, matchingLength);
00229     parent->setTextCursor(tc);
00230     parent->ensureCursorVisible();
00231 }
00232 
00233 
00234 void KTextEdit::Private::slotReplaceText(const QString &text, int replacementIndex, int replacedLength, int matchedLength) {
00235     //kDebug() << "Replace: [" << text << "] ri:" << replacementIndex << " rl:" << replacedLength << " ml:" << matchedLength;
00236     QTextCursor tc = parent->textCursor();
00237     tc.setPosition(replacementIndex);
00238     tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, matchedLength);
00239     tc.removeSelectedText();
00240     tc.insertText(text.mid(replacementIndex, replacedLength));
00241     if (replace->options() & KReplaceDialog::PromptOnReplace) {
00242         parent->setTextCursor(tc);
00243         parent->ensureCursorVisible();
00244     }
00245     lastReplacedPosition = replacementIndex;
00246 }
00247 
00248 void KTextEdit::Private::updateClickMessageRect()
00249 {
00250     int margin = int(parent->document()->documentMargin());
00251     QRect rect = parent->viewport()->rect().adjusted(margin, margin, -margin, -margin);
00252     rect = parent->fontMetrics().boundingRect(rect, Qt::AlignTop | Qt::TextWordWrap, clickMessage);
00253     parent->viewport()->update(rect);
00254 }
00255 
00256 void KTextEdit::Private::init()
00257 {
00258     spellInterface = 0;
00259     KCursor::setAutoHideCursor(parent, true, false);
00260     parent->connect(parent, SIGNAL(languageChanged(const QString&)),
00261                     parent, SLOT(setSpellCheckingLanguage(const QString&)));
00262 }
00263 
00264 KTextEdit::KTextEdit( const QString& text, QWidget *parent )
00265   : QTextEdit( text, parent ), d( new Private( this ) )
00266 {
00267   d->init();
00268 }
00269 
00270 KTextEdit::KTextEdit( QWidget *parent )
00271   : QTextEdit( parent ), d( new Private( this ) )
00272 {
00273   d->init();
00274 }
00275 
00276 KTextEdit::~KTextEdit()
00277 {
00278   delete d;
00279 }
00280 
00281 void KTextEdit::setSpellCheckingConfigFileName(const QString &_fileName)
00282 {
00283     d->spellCheckingConfigFileName = _fileName;
00284 }
00285 
00286 const QString& KTextEdit::spellCheckingLanguage() const
00287 {
00288     return d->spellCheckingLanguage;
00289 }
00290 
00291 void KTextEdit::setSpellCheckingLanguage(const QString &_language)
00292 {
00293     if (highlighter()) {
00294         highlighter()->setCurrentLanguage(_language);
00295         highlighter()->rehighlight();
00296     }
00297 
00298     if (_language != d->spellCheckingLanguage) {
00299         d->spellCheckingLanguage = _language;
00300         emit languageChanged(_language);
00301     }
00302     else
00303         d->spellCheckingLanguage = _language;
00304 }
00305 
00306 void KTextEdit::showSpellConfigDialog(const QString &configFileName,
00307                                       const QString &windowIcon)
00308 {
00309     KConfig config(configFileName);
00310     Sonnet::ConfigDialog dialog(&config, this);
00311     if (!d->spellCheckingLanguage.isEmpty())
00312         dialog.setLanguage(d->spellCheckingLanguage);
00313     connect(&dialog, SIGNAL(languageChanged(const QString &)),
00314             this, SLOT(setSpellCheckingLanguage(const QString &)));
00315     if (!windowIcon.isEmpty())
00316         dialog.setWindowIcon(KIcon(windowIcon));
00317     dialog.exec();
00318 }
00319 
00320 bool KTextEdit::event(QEvent* ev)
00321 {
00322     if (ev->type() == QEvent::ShortcutOverride) {
00323         QKeyEvent *e = static_cast<QKeyEvent *>( ev );
00324         if (d->overrideShortcut(e)) {
00325             e->accept();
00326             return true;
00327         }
00328     }
00329     return QTextEdit::event(ev);
00330 }
00331 
00332 bool KTextEdit::Private::handleShortcut(const QKeyEvent* event)
00333 {
00334   const int key = event->key() | event->modifiers();
00335 
00336   if ( KStandardShortcut::copy().contains( key ) ) {
00337     parent->copy();
00338     return true;
00339   } else if ( KStandardShortcut::paste().contains( key ) ) {
00340     parent->paste();
00341     return true;
00342   } else if ( KStandardShortcut::cut().contains( key ) ) {
00343     parent->cut();
00344     return true;
00345   } else if ( KStandardShortcut::undo().contains( key ) ) {
00346       if(!parent->isReadOnly())
00347           parent->undo();
00348       return true;
00349   } else if ( KStandardShortcut::redo().contains( key ) ) {
00350       if(!parent->isReadOnly())
00351          parent->redo();
00352       return true;
00353   } else if ( KStandardShortcut::deleteWordBack().contains( key ) ) {
00354     parent->deleteWordBack();
00355     return true;
00356   } else if ( KStandardShortcut::deleteWordForward().contains( key ) ) {
00357     parent->deleteWordForward();
00358     return true;
00359   } else if ( KStandardShortcut::backwardWord().contains( key ) ) {
00360     QTextCursor cursor = parent->textCursor();
00361     cursor.movePosition( QTextCursor::PreviousWord );
00362     parent->setTextCursor( cursor );
00363     return true;
00364   } else if ( KStandardShortcut::forwardWord().contains( key ) ) {
00365     QTextCursor cursor = parent->textCursor();
00366     cursor.movePosition( QTextCursor::NextWord );
00367     parent->setTextCursor( cursor );
00368     return true;
00369   } else if ( KStandardShortcut::next().contains( key ) ) {
00370     QTextCursor cursor = parent->textCursor();
00371     bool moved = false;
00372     qreal lastY = parent->cursorRect(cursor).bottom();
00373     qreal distance = 0;
00374     do {
00375         qreal y = parent->cursorRect(cursor).bottom();
00376         distance += qAbs(y - lastY);
00377         lastY = y;
00378         moved = cursor.movePosition(QTextCursor::Down);
00379     } while (moved && distance < parent->viewport()->height());
00380 
00381     if (moved) {
00382         cursor.movePosition(QTextCursor::Up);
00383         parent->verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
00384     }
00385     parent->setTextCursor(cursor);
00386     return true;
00387   } else if ( KStandardShortcut::prior().contains( key ) ) {
00388     QTextCursor cursor = parent->textCursor();
00389     bool moved = false;
00390     qreal lastY = parent->cursorRect(cursor).bottom();
00391     qreal distance = 0;
00392     do {
00393         qreal y = parent->cursorRect(cursor).bottom();
00394         distance += qAbs(y - lastY);
00395         lastY = y;
00396         moved = cursor.movePosition(QTextCursor::Up);
00397     } while (moved && distance < parent->viewport()->height());
00398 
00399     if (moved) {
00400         cursor.movePosition(QTextCursor::Down);
00401         parent->verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
00402     }
00403     parent->setTextCursor(cursor);
00404     return true;
00405   } else if ( KStandardShortcut::begin().contains( key ) ) {
00406     QTextCursor cursor = parent->textCursor();
00407     cursor.movePosition( QTextCursor::Start );
00408     parent->setTextCursor( cursor );
00409     return true;
00410   } else if ( KStandardShortcut::end().contains( key ) ) {
00411     QTextCursor cursor = parent->textCursor();
00412     cursor.movePosition( QTextCursor::End );
00413     parent->setTextCursor( cursor );
00414     return true;
00415   } else if ( KStandardShortcut::beginningOfLine().contains( key ) ) {
00416     QTextCursor cursor = parent->textCursor();
00417     cursor.movePosition( QTextCursor::StartOfLine );
00418     parent->setTextCursor( cursor );
00419     return true;
00420   } else if ( KStandardShortcut::endOfLine().contains( key ) ) {
00421     QTextCursor cursor = parent->textCursor();
00422     cursor.movePosition( QTextCursor::EndOfLine );
00423     parent->setTextCursor( cursor );
00424     return true;
00425   } else if (KStandardShortcut::find().contains(key)) {
00426       if (findReplaceEnabled)
00427           parent->slotFind();
00428       return true;
00429   } else if (KStandardShortcut::findNext().contains(key)) {
00430       if (findReplaceEnabled)
00431           parent->slotFindNext();
00432       return true;
00433   } else if (KStandardShortcut::replace().contains(key)) {
00434       if (!parent->isReadOnly() && findReplaceEnabled)
00435           parent->slotReplace();
00436       return true;
00437   } else if ( KStandardShortcut::pasteSelection().contains( key ) ) {
00438     QString text = QApplication::clipboard()->text( QClipboard::Selection );
00439     if ( !text.isEmpty() )
00440       parent->insertPlainText( text );  // TODO: check if this is html? (MiB)
00441     return true;
00442   }
00443   return false;
00444 }
00445 
00446 static void deleteWord(QTextCursor cursor, QTextCursor::MoveOperation op)
00447 {
00448   cursor.clearSelection();
00449   cursor.movePosition( op, QTextCursor::KeepAnchor );
00450   cursor.removeSelectedText();
00451 }
00452 
00453 void KTextEdit::deleteWordBack()
00454 {
00455   deleteWord(textCursor(), QTextCursor::PreviousWord);
00456 }
00457 
00458 void KTextEdit::deleteWordForward()
00459 {
00460   deleteWord(textCursor(), QTextCursor::WordRight);
00461 }
00462 
00463 QMenu *KTextEdit::mousePopupMenu()
00464 {
00465   QMenu *popup = createStandardContextMenu();
00466   if (!popup) return 0;
00467   connect( popup, SIGNAL( triggered ( QAction* ) ),
00468              this, SLOT( menuActivated( QAction* ) ) );
00469 
00470   const bool emptyDocument = document()->isEmpty();
00471   if( !isReadOnly() )
00472   {
00473       QList<QAction *> actionList = popup->actions();
00474       enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs };
00475       QAction *separatorAction = 0L;
00476       int idx = actionList.indexOf( actionList[SelectAllAct] ) + 1;
00477       if ( idx < actionList.count() )
00478           separatorAction = actionList.at( idx );
00479       if ( separatorAction )
00480       {
00481           KAction *clearAllAction = KStandardAction::clear(this, SLOT(undoableClear()), popup);
00482           if ( emptyDocument )
00483               clearAllAction->setEnabled( false );
00484           popup->insertAction( separatorAction, clearAllAction );
00485       }
00486   }
00487   KIconTheme::assignIconsToContextMenu( isReadOnly() ? KIconTheme::ReadOnlyText
00488                                           : KIconTheme::TextEditor,
00489                                           popup->actions() );
00490 
00491   if( !isReadOnly() )
00492   {
00493       popup->addSeparator();
00494       d->spellCheckAction = popup->addAction( KIcon( "tools-check-spelling" ),
00495                                               i18n( "Check Spelling..." ) );
00496       if ( emptyDocument )
00497         d->spellCheckAction->setEnabled( false );
00498       d->autoSpellCheckAction = popup->addAction( i18n( "Auto Spell Check" ) );
00499       d->autoSpellCheckAction->setCheckable( true );
00500       d->autoSpellCheckAction->setChecked( checkSpellingEnabled() );
00501       popup->addSeparator();
00502       d->allowTab = popup->addAction( i18n("Allow Tabulations") );
00503       d->allowTab->setCheckable( true );
00504       d->allowTab->setChecked( !tabChangesFocus() );
00505 
00506       if (d->findReplaceEnabled)
00507       {
00508           KAction *findAction = KStandardAction::find(this, SLOT(slotFind()), popup);
00509           KAction *findNextAction = KStandardAction::findNext(this, SLOT(slotFindNext()), popup);
00510           KAction *replaceAction = KStandardAction::replace(this, SLOT(slotReplace()), popup);
00511           if (emptyDocument)
00512           {
00513               findAction->setEnabled(false);
00514               findNextAction->setEnabled(false );
00515               replaceAction->setEnabled(false);
00516           }
00517       else
00518           findNextAction->setEnabled(d->find != 0 );
00519           popup->addSeparator();
00520           popup->addAction(findAction);
00521           popup->addAction(findNextAction);
00522           popup->addAction(replaceAction);
00523       }
00524   }
00525   popup->addSeparator();
00526   QAction *speakAction = popup->addAction(i18n("Speak Text"));
00527   speakAction->setIcon(KIcon("preferences-desktop-text-to-speech"));
00528   speakAction->setEnabled(!emptyDocument );
00529   connect( speakAction, SIGNAL(triggered(bool)), this, SLOT(slotSpeakText()) );
00530   return popup;
00531 }
00532 
00533 void KTextEdit::slotSpeakText()
00534 {
00535     // If KTTSD not running, start it.
00536     if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kttsd"))
00537     {
00538         QString error;
00539         if (KToolInvocation::startServiceByDesktopName("kttsd", QStringList(), &error))
00540         {
00541             KMessageBox::error(this, i18n( "Starting Jovie Text-to-Speech Service Failed"), error );
00542             return;
00543         }
00544     }
00545     QDBusInterface ktts("org.kde.kttsd", "/KSpeech", "org.kde.KSpeech");
00546     QString text;
00547     if(textCursor().hasSelection())
00548         text = textCursor().selectedText();
00549     else
00550         text = toPlainText();
00551     ktts.asyncCall("say", text, 0);
00552 }
00553 
00554 void KTextEdit::contextMenuEvent(QContextMenuEvent *event)
00555 {
00556     // Obtain the cursor at the mouse position and the current cursor
00557     QTextCursor cursorAtMouse = cursorForPosition(event->pos());
00558     const int mousePos = cursorAtMouse.position();
00559     QTextCursor cursor = textCursor();
00560 
00561     // Check if the user clicked a selected word
00562     const bool selectedWordClicked = cursor.hasSelection() &&
00563                                mousePos >= cursor.selectionStart() &&
00564                                mousePos <= cursor.selectionEnd();
00565 
00566     // Get the word under the (mouse-)cursor and see if it is misspelled.
00567     // Don't include apostrophes at the start/end of the word in the selection.
00568     QTextCursor wordSelectCursor(cursorAtMouse);
00569     wordSelectCursor.clearSelection();
00570     wordSelectCursor.select(QTextCursor::WordUnderCursor);
00571     QString selectedWord = wordSelectCursor.selectedText();
00572 
00573     bool isMouseCursorInsideWord = true;
00574     if ((mousePos < wordSelectCursor.selectionStart() ||
00575             mousePos >= wordSelectCursor.selectionEnd())
00576                                         && (selectedWord.length() > 1)) {
00577          isMouseCursorInsideWord = false;
00578     }
00579 
00580     // Clear the selection again, we re-select it below (without the apostrophes).
00581     wordSelectCursor.setPosition(wordSelectCursor.position()-selectedWord.size());
00582     if (selectedWord.startsWith('\'') || selectedWord.startsWith('\"')) {
00583         selectedWord = selectedWord.right(selectedWord.size() - 1);
00584         wordSelectCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
00585     }
00586     if (selectedWord.endsWith('\'') || selectedWord.endsWith('\"'))
00587         selectedWord.chop(1);
00588 
00589     wordSelectCursor.movePosition(QTextCursor::NextCharacter,
00590                                   QTextCursor::KeepAnchor, selectedWord.size());
00591 
00592     const bool wordIsMisspelled = isMouseCursorInsideWord &&
00593                             checkSpellingEnabled() &&
00594                             !selectedWord.isEmpty() &&
00595                             highlighter() &&
00596                             highlighter()->isWordMisspelled(selectedWord);
00597 
00598     // If the user clicked a selected word, do nothing.
00599     // If the user clicked somewhere else, move the cursor there.
00600     // If the user clicked on a misspelled word, select that word.
00601     // Same behavior as in OpenOffice Writer.
00602     bool inQuote = false;
00603     if (d->spellInterface &&
00604         !d->spellInterface->shouldBlockBeSpellChecked(cursorAtMouse.block().text()))
00605         inQuote = true;
00606     if (!selectedWordClicked) {
00607         if (wordIsMisspelled && !inQuote)
00608             setTextCursor(wordSelectCursor);
00609         else
00610             setTextCursor(cursorAtMouse);
00611         cursor = textCursor();
00612     }
00613 
00614     // Use standard context menu for already selected words, correctly spelled
00615     // words and words inside quotes.
00616     if (!wordIsMisspelled || selectedWordClicked || inQuote) {
00617         QMenu *popup = mousePopupMenu();
00618         if ( popup ) {
00619             aboutToShowContextMenu(popup);
00620             popup->exec( event->globalPos() );
00621             delete popup;
00622         }
00623     }
00624     else {
00625         QMenu menu; //don't use KMenu here we don't want auto management accelerator
00626 
00627         //Add the suggestions to the menu
00628         const QStringList reps = highlighter()->suggestionsForWord(selectedWord);
00629         if (reps.isEmpty()) {
00630             QAction *suggestionsAction = menu.addAction(i18n("No suggestions for %1", selectedWord));
00631             suggestionsAction->setEnabled(false);
00632         }
00633         else {
00634             for (QStringList::const_iterator it = reps.constBegin(); it != reps.constEnd(); ++it) {
00635                 menu.addAction(*it);
00636             }
00637         }
00638 
00639         menu.addSeparator();
00640 
00641         QAction *ignoreAction = menu.addAction(i18n("Ignore"));
00642         QAction *addToDictAction = menu.addAction(i18n("Add to Dictionary"));
00643         //Execute the popup inline
00644         const QAction *selectedAction = menu.exec(event->globalPos());
00645 
00646         if (selectedAction) {
00647             Q_ASSERT(cursor.selectedText() == selectedWord);
00648 
00649             if (selectedAction == ignoreAction) {
00650                 highlighter()->ignoreWord(selectedWord);
00651                 highlighter()->rehighlight();
00652             }
00653             else if (selectedAction == addToDictAction) {
00654                 highlighter()->addWordToDictionary(selectedWord);
00655                 highlighter()->rehighlight();
00656             }
00657 
00658             // Other actions can only be one of the suggested words
00659             else {
00660                 const QString replacement = selectedAction->text();
00661                 Q_ASSERT(reps.contains(replacement));
00662                 cursor.insertText(replacement);
00663                 setTextCursor(cursor);
00664             }
00665         }
00666     }
00667 }
00668 
00669 void KTextEdit::wheelEvent( QWheelEvent *event )
00670 {
00671   if ( KGlobalSettings::wheelMouseZooms() )
00672     QTextEdit::wheelEvent( event );
00673   else // thanks, we don't want to zoom, so skip QTextEdit's impl.
00674     QAbstractScrollArea::wheelEvent( event );
00675 }
00676 
00677 void KTextEdit::createHighlighter()
00678 {
00679     setHighlighter(new Sonnet::Highlighter(this, d->spellCheckingConfigFileName));
00680 }
00681 
00682 Sonnet::Highlighter* KTextEdit::highlighter() const
00683 {
00684     return d->highlighter;
00685 }
00686 
00687 void KTextEdit::setHighlighter(Sonnet::Highlighter *_highLighter)
00688 {
00689     delete d->highlighter;
00690     d->highlighter = _highLighter;
00691 }
00692 
00693 void KTextEdit::setCheckSpellingEnabled(bool check)
00694 {
00695     if (d->spellInterface)
00696         d->spellInterface->setSpellCheckingEnabled(check);
00697     else
00698         setCheckSpellingEnabledInternal(check);
00699 }
00700 
00701 void KTextEdit::setCheckSpellingEnabledInternal( bool check )
00702 {
00703   emit checkSpellingChanged( check );
00704   if ( check == d->checkSpellingEnabled )
00705     return;
00706 
00707   // From the above statment we know know that if we're turning checking
00708   // on that we need to create a new highlighter and if we're turning it
00709   // off we should remove the old one.
00710 
00711   d->checkSpellingEnabled = check;
00712     if ( check )
00713     {
00714         if ( hasFocus() ) {
00715             createHighlighter();
00716             if (!spellCheckingLanguage().isEmpty())
00717                 setSpellCheckingLanguage(spellCheckingLanguage());
00718         }
00719     }
00720     else
00721     {
00722         delete d->highlighter;
00723         d->highlighter = 0;
00724     }
00725 }
00726 
00727 void KTextEdit::focusInEvent( QFocusEvent *event )
00728 {
00729   if ( d->checkSpellingEnabled && !isReadOnly() && !d->highlighter )
00730     createHighlighter();
00731 
00732   if (!d->clickMessage.isEmpty()) {
00733       d->updateClickMessageRect();
00734   }
00735   QTextEdit::focusInEvent( event );
00736 }
00737 
00738 bool KTextEdit::checkSpellingEnabled() const
00739 {
00740     if (d->spellInterface)
00741       return d->spellInterface->isSpellCheckingEnabled();
00742     else
00743       return checkSpellingEnabledInternal();
00744 }
00745 
00746 bool KTextEdit::checkSpellingEnabledInternal() const
00747 {
00748   return d->checkSpellingEnabled;
00749 }
00750 
00751 void KTextEdit::setReadOnly( bool readOnly )
00752 {
00753   if ( !readOnly && hasFocus() && d->checkSpellingEnabled && !d->highlighter )
00754     createHighlighter();
00755 
00756   if ( readOnly == isReadOnly() )
00757     return;
00758 
00759   if ( readOnly ) {
00760     delete d->highlighter;
00761     d->highlighter = 0;
00762 
00763     d->customPalette = testAttribute( Qt::WA_SetPalette );
00764     QPalette p = palette();
00765     QColor color = p.color( QPalette::Disabled, QPalette::Background );
00766     p.setColor( QPalette::Base, color );
00767     p.setColor( QPalette::Background, color );
00768     setPalette( p );
00769   } else {
00770     if ( d->customPalette && testAttribute( Qt::WA_SetPalette ) ) {
00771         QPalette p = palette();
00772         QColor color = p.color( QPalette::Normal, QPalette::Base );
00773         p.setColor( QPalette::Base, color );
00774         p.setColor( QPalette::Background, color );
00775         setPalette( p );
00776     } else
00777         setPalette( QPalette() );
00778   }
00779 
00780   QTextEdit::setReadOnly( readOnly );
00781 }
00782 
00783 void KTextEdit::checkSpelling()
00784 {
00785   if(document()->isEmpty())
00786   {
00787       KMessageBox::information(this, i18n("Nothing to spell check."));
00788       return;
00789   }
00790   Sonnet::BackgroundChecker *backgroundSpellCheck = new Sonnet::BackgroundChecker(this);
00791   if(!d->spellCheckingLanguage.isEmpty())
00792      backgroundSpellCheck->changeLanguage(d->spellCheckingLanguage);
00793   Sonnet::Dialog *spellDialog = new Sonnet::Dialog(
00794       backgroundSpellCheck, 0);
00795   connect(spellDialog, SIGNAL(replace( const QString&, int,const QString&)),
00796           this, SLOT(spellCheckerCorrected( const QString&, int,const QString&)));
00797   connect(spellDialog, SIGNAL(misspelling( const QString&, int)),
00798           this, SLOT(spellCheckerMisspelling(const QString &,int)));
00799   connect(spellDialog, SIGNAL(autoCorrect(const QString&, const QString&)),
00800           this, SLOT(spellCheckerAutoCorrect(const QString&, const QString&)));
00801   connect(spellDialog, SIGNAL(done(const QString&)),
00802           this, SLOT(spellCheckerFinished()));
00803   connect(spellDialog, SIGNAL(cancel()),
00804           this, SLOT(spellCheckerCanceled()));
00805   connect(spellDialog, SIGNAL(stop()),
00806           this, SLOT(spellCheckerFinished()));
00807   connect(spellDialog, SIGNAL(spellCheckStatus(const QString &)),
00808           this,SIGNAL(spellCheckStatus(const QString &)));
00809   connect(spellDialog, SIGNAL(languageChanged(const QString &)),
00810           this, SIGNAL(languageChanged(const QString &)));
00811   d->originalDoc = QTextDocumentFragment(document());
00812   spellDialog->setBuffer(toPlainText());
00813   spellDialog->show();
00814 }
00815 
00816 void KTextEdit::highlightWord( int length, int pos )
00817 {
00818   QTextCursor cursor(document());
00819   cursor.setPosition(pos);
00820   cursor.setPosition(pos+length,QTextCursor::KeepAnchor);
00821   setTextCursor (cursor);
00822   ensureCursorVisible();
00823 }
00824 
00825 void KTextEdit::replace()
00826 {
00827      if( document()->isEmpty() )  // saves having to track the text changes
00828         return;
00829 
00830     if ( d->repDlg ) {
00831       KWindowSystem::activateWindow( d->repDlg->winId() );
00832     } else {
00833       d->repDlg = new KReplaceDialog(this, 0,
00834                                     QStringList(), QStringList(), false);
00835       connect( d->repDlg, SIGNAL(okClicked()), this, SLOT(slotDoReplace()) );
00836     }
00837     d->repDlg->show();
00838 }
00839 
00840 void KTextEdit::slotDoReplace()
00841 {
00842     if (!d->repDlg) {
00843         // Should really assert()
00844         return;
00845     }
00846 
00847     if(d->repDlg->pattern().isEmpty()) {
00848     delete d->replace;
00849         d->replace = 0;
00850         ensureCursorVisible();
00851         return;
00852     }
00853 
00854     delete d->replace;
00855     d->replace = new KReplace(d->repDlg->pattern(), d->repDlg->replacement(), d->repDlg->options(), this);
00856     d->repIndex = 0;
00857     if (d->replace->options() & KFind::FromCursor || d->replace->options() & KFind::FindBackwards) {
00858         d->repIndex = textCursor().anchor();
00859     }
00860 
00861     // Connect highlight signal to code which handles highlighting
00862     // of found text.
00863     connect(d->replace, SIGNAL(highlight(const QString &, int, int)),
00864             this, SLOT(slotFindHighlight(const QString &, int, int)));
00865     connect(d->replace, SIGNAL(findNext()), this, SLOT(slotReplaceNext()));
00866     connect(d->replace, SIGNAL(replace(const QString &, int, int, int)),
00867             this, SLOT(slotReplaceText(const QString &, int, int, int)));
00868 
00869     d->repDlg->close();
00870     slotReplaceNext();
00871 }
00872 
00873 
00874 void KTextEdit::slotReplaceNext()
00875 {
00876     if (!d->replace)
00877         return;
00878 
00879     d->lastReplacedPosition = -1;
00880     if (!(d->replace->options() & KReplaceDialog::PromptOnReplace)) {
00881         textCursor().beginEditBlock(); // #48541
00882         viewport()->setUpdatesEnabled(false);
00883     }
00884 
00885     KFind::Result res = KFind::NoMatch;
00886 
00887     if (d->replace->needData())
00888         d->replace->setData(toPlainText(), d->repIndex);
00889     res = d->replace->replace();
00890     if (!(d->replace->options() & KReplaceDialog::PromptOnReplace)) {
00891         textCursor().endEditBlock(); // #48541
00892         if (d->lastReplacedPosition >= 0) {
00893             QTextCursor tc = textCursor();
00894             tc.setPosition(d->lastReplacedPosition);
00895             setTextCursor(tc);
00896             ensureCursorVisible();
00897         }
00898 
00899         viewport()->setUpdatesEnabled(true);
00900         viewport()->update();
00901     }
00902 
00903     if (res == KFind::NoMatch) {
00904         d->replace->displayFinalDialog();
00905         d->replace->disconnect(this);
00906         d->replace->deleteLater(); // we are in a slot connected to m_replace, don't delete it right away
00907         d->replace = 0;
00908         ensureCursorVisible();
00909         //or           if ( m_replace->shouldRestart() ) { reinit (w/o FromCursor) and call slotReplaceNext(); }
00910     } else {
00911         //m_replace->closeReplaceNextDialog();
00912     }
00913 }
00914 
00915 
00916 void KTextEdit::slotDoFind()
00917 {
00918     if (!d->findDlg) {
00919         // Should really assert()
00920         return;
00921     }
00922     if( d->findDlg->pattern().isEmpty())
00923     {
00924         delete d->find;
00925         d->find = 0;
00926         return;
00927     }
00928     delete d->find;
00929     d->find = new KFind(d->findDlg->pattern(), d->findDlg->options(), this);
00930     d->findIndex = 0;
00931     if (d->find->options() & KFind::FromCursor || d->find->options() & KFind::FindBackwards) {
00932         d->findIndex = textCursor().anchor();
00933     }
00934 
00935     // Connect highlight signal to code which handles highlighting
00936     // of found text.
00937     connect(d->find, SIGNAL(highlight(const QString &, int, int)),
00938             this, SLOT(slotFindHighlight(const QString &, int, int)));
00939     connect(d->find, SIGNAL(findNext()), this, SLOT(slotFindNext()));
00940 
00941     d->findDlg->close();
00942     d->find->closeFindNextDialog();
00943     slotFindNext();
00944 }
00945 
00946 
00947 void KTextEdit::slotFindNext()
00948 {
00949     if (!d->find)
00950         return;
00951     if(document()->isEmpty())
00952     {
00953         d->find->disconnect(this);
00954         d->find->deleteLater(); // we are in a slot connected to m_find, don't delete right away
00955         d->find = 0;
00956         return;
00957     }
00958 
00959     KFind::Result res = KFind::NoMatch;
00960     if (d->find->needData())
00961         d->find->setData(toPlainText(), d->findIndex);
00962     res = d->find->find();
00963 
00964     if (res == KFind::NoMatch) {
00965         d->find->displayFinalDialog();
00966         d->find->disconnect(this);
00967         d->find->deleteLater(); // we are in a slot connected to m_find, don't delete right away
00968         d->find = 0;
00969         //or           if ( m_find->shouldRestart() ) { reinit (w/o FromCursor) and call slotFindNext(); }
00970     } else {
00971         //m_find->closeFindNextDialog();
00972     }
00973 }
00974 
00975 
00976 void KTextEdit::slotFind()
00977 {
00978     if( document()->isEmpty() )  // saves having to track the text changes
00979         return;
00980 
00981     if ( d->findDlg ) {
00982       KWindowSystem::activateWindow( d->findDlg->winId() );
00983     } else {
00984       d->findDlg = new KFindDialog(this);
00985       connect( d->findDlg, SIGNAL(okClicked()), this, SLOT(slotDoFind()) );
00986     }
00987     d->findDlg->show();
00988 }
00989 
00990 
00991 void KTextEdit::slotReplace()
00992 {
00993     if( document()->isEmpty() )  // saves having to track the text changes
00994         return;
00995 
00996     if ( d->repDlg ) {
00997       KWindowSystem::activateWindow( d->repDlg->winId() );
00998     } else {
00999       d->repDlg = new KReplaceDialog(this, 0,
01000                                     QStringList(), QStringList(), false);
01001       connect( d->repDlg, SIGNAL(okClicked()), this, SLOT(slotDoReplace()) );
01002     }
01003     d->repDlg->show();
01004 }
01005 
01006 void KTextEdit::enableFindReplace( bool enabled )
01007 {
01008     d->findReplaceEnabled = enabled;
01009 }
01010 
01011 void KTextEdit::setSpellInterface(KTextEditSpellInterface *spellInterface)
01012 {
01013     d->spellInterface = spellInterface;
01014 }
01015 
01016 bool KTextEdit::Private::overrideShortcut(const QKeyEvent* event)
01017 {
01018   const int key = event->key() | event->modifiers();
01019 
01020   if ( KStandardShortcut::copy().contains( key ) ) {
01021     return true;
01022   } else if ( KStandardShortcut::paste().contains( key ) ) {
01023     return true;
01024   } else if ( KStandardShortcut::cut().contains( key ) ) {
01025     return true;
01026   } else if ( KStandardShortcut::undo().contains( key ) ) {
01027     return true;
01028   } else if ( KStandardShortcut::redo().contains( key ) ) {
01029     return true;
01030   } else if ( KStandardShortcut::deleteWordBack().contains( key ) ) {
01031     return true;
01032   } else if ( KStandardShortcut::deleteWordForward().contains( key ) ) {
01033     return true;
01034   } else if ( KStandardShortcut::backwardWord().contains( key ) ) {
01035     return true;
01036   } else if ( KStandardShortcut::forwardWord().contains( key ) ) {
01037     return true;
01038   } else if ( KStandardShortcut::next().contains( key ) ) {
01039     return true;
01040   } else if ( KStandardShortcut::prior().contains( key ) ) {
01041     return true;
01042   } else if ( KStandardShortcut::begin().contains( key ) ) {
01043     return true;
01044   } else if ( KStandardShortcut::end().contains( key ) ) {
01045     return true;
01046   } else if ( KStandardShortcut::beginningOfLine().contains( key ) ) {
01047     return true;
01048   } else if ( KStandardShortcut::endOfLine().contains( key ) ) {
01049     return true;
01050   } else if ( KStandardShortcut::pasteSelection().contains( key ) ) {
01051     return true;
01052   } else if (KStandardShortcut::find().contains(key)) {
01053       return true;
01054   } else if (KStandardShortcut::findNext().contains(key)) {
01055       return true;
01056   } else if (KStandardShortcut::replace().contains(key)) {
01057       return true;
01058   } else if (event->matches(QKeySequence::SelectAll)) { // currently missing in QTextEdit
01059       return true;
01060   } else if (event->modifiers() == Qt::ControlModifier &&
01061             (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) &&
01062               qobject_cast<KDialog*>(parent->window()) ) {
01063     // ignore Ctrl-Return so that KDialogs can close the dialog
01064     return true;
01065   }
01066   return false;
01067 }
01068 
01069 void KTextEdit::keyPressEvent( QKeyEvent *event )
01070 {
01071     if (d->handleShortcut(event)) {
01072         event->accept();
01073     }else if (event->modifiers() == Qt::ControlModifier &&
01074             (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) &&
01075               qobject_cast<KDialog*>(window()) ) {
01076         event->ignore();
01077     } else {
01078         QTextEdit::keyPressEvent(event);
01079     }
01080 }
01081 
01082 void KTextEdit::setClickMessage(const QString &msg)
01083 {
01084     if (msg != d->clickMessage) {
01085         if (!d->clickMessage.isEmpty()) {
01086             d->updateClickMessageRect();
01087         }
01088         d->clickMessage = msg;
01089         if (!d->clickMessage.isEmpty()) {
01090             d->updateClickMessageRect();
01091         }
01092     }
01093 }
01094 
01095 QString KTextEdit::clickMessage() const
01096 {
01097     return d->clickMessage;
01098 }
01099 
01100 void KTextEdit::paintEvent(QPaintEvent *ev)
01101 {
01102     QTextEdit::paintEvent(ev);
01103 
01104     if (!d->clickMessage.isEmpty() && !hasFocus() && document()->isEmpty()) {
01105         QPainter p(viewport());
01106 
01107         QFont f = font();
01108         f.setItalic(d->italicizePlaceholder);
01109         p.setFont(f);
01110 
01111         QColor color(palette().color(foregroundRole()));
01112         color.setAlphaF(0.5);
01113         p.setPen(color);
01114 
01115         int margin = int(document()->documentMargin());
01116         QRect cr = viewport()->rect().adjusted(margin, margin, -margin, -margin);
01117 
01118         p.drawText(cr, Qt::AlignTop | Qt::TextWordWrap, d->clickMessage);
01119     }
01120 }
01121 
01122 void KTextEdit::focusOutEvent(QFocusEvent *ev)
01123 {
01124     if (!d->clickMessage.isEmpty()) {
01125         d->updateClickMessageRect();
01126     }
01127     QTextEdit::focusOutEvent(ev);
01128 }
01129 
01130 #include "ktextedit.moc"

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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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