• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.3 API Reference
  • 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(QString)),
00261                     parent, SLOT(setSpellCheckingLanguage(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     if (!windowIcon.isEmpty())
00314         dialog.setWindowIcon(KIcon(windowIcon));
00315     if(dialog.exec()) {
00316         setSpellCheckingLanguage( dialog.language() );
00317     }
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 (findReplaceEnabled && KStandardShortcut::find().contains(key)) {
00426       parent->slotFind();
00427       return true;
00428   } else if (findReplaceEnabled && KStandardShortcut::findNext().contains(key)) {
00429       parent->slotFindNext();
00430       return true;
00431   } else if (findReplaceEnabled && KStandardShortcut::replace().contains(key)) {
00432       if (!parent->isReadOnly())
00433           parent->slotReplace();
00434       return true;
00435   } else if ( KStandardShortcut::pasteSelection().contains( key ) ) {
00436     QString text = QApplication::clipboard()->text( QClipboard::Selection );
00437     if ( !text.isEmpty() )
00438       parent->insertPlainText( text );  // TODO: check if this is html? (MiB)
00439     return true;
00440   }
00441   return false;
00442 }
00443 
00444 static void deleteWord(QTextCursor cursor, QTextCursor::MoveOperation op)
00445 {
00446   cursor.clearSelection();
00447   cursor.movePosition( op, QTextCursor::KeepAnchor );
00448   cursor.removeSelectedText();
00449 }
00450 
00451 void KTextEdit::deleteWordBack()
00452 {
00453   deleteWord(textCursor(), QTextCursor::PreviousWord);
00454 }
00455 
00456 void KTextEdit::deleteWordForward()
00457 {
00458   deleteWord(textCursor(), QTextCursor::WordRight);
00459 }
00460 
00461 QMenu *KTextEdit::mousePopupMenu()
00462 {
00463   QMenu *popup = createStandardContextMenu();
00464   if (!popup) return 0;
00465   connect( popup, SIGNAL(triggered(QAction*)),
00466              this, SLOT(menuActivated(QAction*)) );
00467 
00468   const bool emptyDocument = document()->isEmpty();
00469   if( !isReadOnly() )
00470   {
00471       QList<QAction *> actionList = popup->actions();
00472       enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs };
00473       QAction *separatorAction = 0L;
00474       int idx = actionList.indexOf( actionList[SelectAllAct] ) + 1;
00475       if ( idx < actionList.count() )
00476           separatorAction = actionList.at( idx );
00477       if ( separatorAction )
00478       {
00479           KAction *clearAllAction = KStandardAction::clear(this, SLOT(undoableClear()), popup);
00480           if ( emptyDocument )
00481               clearAllAction->setEnabled( false );
00482           popup->insertAction( separatorAction, clearAllAction );
00483       }
00484   }
00485   KIconTheme::assignIconsToContextMenu( isReadOnly() ? KIconTheme::ReadOnlyText
00486                                           : KIconTheme::TextEditor,
00487                                           popup->actions() );
00488 
00489   if( !isReadOnly() )
00490   {
00491       popup->addSeparator();
00492       d->spellCheckAction = popup->addAction( KIcon( "tools-check-spelling" ),
00493                                               i18n( "Check Spelling..." ) );
00494       if ( emptyDocument )
00495         d->spellCheckAction->setEnabled( false );
00496       d->autoSpellCheckAction = popup->addAction( i18n( "Auto Spell Check" ) );
00497       d->autoSpellCheckAction->setCheckable( true );
00498       d->autoSpellCheckAction->setChecked( checkSpellingEnabled() );
00499       popup->addSeparator();
00500       d->allowTab = popup->addAction( i18n("Allow Tabulations") );
00501       d->allowTab->setCheckable( true );
00502       d->allowTab->setChecked( !tabChangesFocus() );
00503   }
00504 
00505   if (d->findReplaceEnabled) {
00506       KAction *findAction = KStandardAction::find(this, SLOT(slotFind()), popup);
00507       KAction *findNextAction = KStandardAction::findNext(this, SLOT(slotFindNext()), popup);
00508       if (emptyDocument) {
00509           findAction->setEnabled(false);
00510           findNextAction->setEnabled(false);
00511       } else {
00512           findNextAction->setEnabled(d->find != 0);
00513       }
00514       popup->addSeparator();
00515       popup->addAction(findAction);
00516       popup->addAction(findNextAction);
00517 
00518       if (!isReadOnly()) {
00519           KAction *replaceAction = KStandardAction::replace(this, SLOT(slotReplace()), popup);
00520           if (emptyDocument) {
00521               replaceAction->setEnabled(false);
00522           }
00523           popup->addAction(replaceAction);
00524       }
00525   }
00526   popup->addSeparator();
00527   QAction *speakAction = popup->addAction(i18n("Speak Text"));
00528   speakAction->setIcon(KIcon("preferences-desktop-text-to-speech"));
00529   speakAction->setEnabled(!emptyDocument );
00530   connect( speakAction, SIGNAL(triggered(bool)), this, SLOT(slotSpeakText()) );
00531   return popup;
00532 }
00533 
00534 void KTextEdit::slotSpeakText()
00535 {
00536     // If KTTSD not running, start it.
00537     if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kttsd"))
00538     {
00539         QString error;
00540         if (KToolInvocation::startServiceByDesktopName("kttsd", QStringList(), &error))
00541         {
00542             KMessageBox::error(this, i18n( "Starting Jovie Text-to-Speech Service Failed"), error );
00543             return;
00544         }
00545     }
00546     QDBusInterface ktts("org.kde.kttsd", "/KSpeech", "org.kde.KSpeech");
00547     QString text;
00548     if(textCursor().hasSelection())
00549         text = textCursor().selectedText();
00550     else
00551         text = toPlainText();
00552     ktts.asyncCall("say", text, 0);
00553 }
00554 
00555 void KTextEdit::contextMenuEvent(QContextMenuEvent *event)
00556 {
00557     // Obtain the cursor at the mouse position and the current cursor
00558     QTextCursor cursorAtMouse = cursorForPosition(event->pos());
00559     const int mousePos = cursorAtMouse.position();
00560     QTextCursor cursor = textCursor();
00561 
00562     // Check if the user clicked a selected word
00563     const bool selectedWordClicked = cursor.hasSelection() &&
00564                                mousePos >= cursor.selectionStart() &&
00565                                mousePos <= cursor.selectionEnd();
00566 
00567     // Get the word under the (mouse-)cursor and see if it is misspelled.
00568     // Don't include apostrophes at the start/end of the word in the selection.
00569     QTextCursor wordSelectCursor(cursorAtMouse);
00570     wordSelectCursor.clearSelection();
00571     wordSelectCursor.select(QTextCursor::WordUnderCursor);
00572     QString selectedWord = wordSelectCursor.selectedText();
00573 
00574     bool isMouseCursorInsideWord = true;
00575     if ((mousePos < wordSelectCursor.selectionStart() ||
00576             mousePos >= wordSelectCursor.selectionEnd())
00577                                         && (selectedWord.length() > 1)) {
00578          isMouseCursorInsideWord = false;
00579     }
00580 
00581     // Clear the selection again, we re-select it below (without the apostrophes).
00582     wordSelectCursor.setPosition(wordSelectCursor.position()-selectedWord.size());
00583     if (selectedWord.startsWith('\'') || selectedWord.startsWith('\"')) {
00584         selectedWord = selectedWord.right(selectedWord.size() - 1);
00585         wordSelectCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
00586     }
00587     if (selectedWord.endsWith('\'') || selectedWord.endsWith('\"'))
00588         selectedWord.chop(1);
00589 
00590     wordSelectCursor.movePosition(QTextCursor::NextCharacter,
00591                                   QTextCursor::KeepAnchor, selectedWord.size());
00592 
00593     const bool wordIsMisspelled = isMouseCursorInsideWord &&
00594                             checkSpellingEnabled() &&
00595                             !selectedWord.isEmpty() &&
00596                             highlighter() &&
00597                             highlighter()->isWordMisspelled(selectedWord);
00598 
00599     // If the user clicked a selected word, do nothing.
00600     // If the user clicked somewhere else, move the cursor there.
00601     // If the user clicked on a misspelled word, select that word.
00602     // Same behavior as in OpenOffice Writer.
00603     bool inQuote = false;
00604     if (d->spellInterface &&
00605         !d->spellInterface->shouldBlockBeSpellChecked(cursorAtMouse.block().text()))
00606         inQuote = true;
00607     if (!selectedWordClicked) {
00608         if (wordIsMisspelled && !inQuote)
00609             setTextCursor(wordSelectCursor);
00610         else
00611             setTextCursor(cursorAtMouse);
00612         cursor = textCursor();
00613     }
00614 
00615     // Use standard context menu for already selected words, correctly spelled
00616     // words and words inside quotes.
00617     if (!wordIsMisspelled || selectedWordClicked || inQuote) {
00618         QMenu *popup = mousePopupMenu();
00619         if ( popup ) {
00620             aboutToShowContextMenu(popup);
00621             popup->exec( event->globalPos() );
00622             delete popup;
00623         }
00624     }
00625     else {
00626         QMenu menu; //don't use KMenu here we don't want auto management accelerator
00627 
00628         //Add the suggestions to the menu
00629         const QStringList reps = highlighter()->suggestionsForWord(selectedWord);
00630         if (reps.isEmpty()) {
00631             QAction *suggestionsAction = menu.addAction(i18n("No suggestions for %1", selectedWord));
00632             suggestionsAction->setEnabled(false);
00633         }
00634         else {
00635             for (QStringList::const_iterator it = reps.constBegin(); it != reps.constEnd(); ++it) {
00636                 menu.addAction(*it);
00637             }
00638         }
00639 
00640         menu.addSeparator();
00641 
00642         QAction *ignoreAction = menu.addAction(i18n("Ignore"));
00643         QAction *addToDictAction = menu.addAction(i18n("Add to Dictionary"));
00644         //Execute the popup inline
00645         const QAction *selectedAction = menu.exec(event->globalPos());
00646 
00647         if (selectedAction) {
00648             Q_ASSERT(cursor.selectedText() == selectedWord);
00649 
00650             if (selectedAction == ignoreAction) {
00651                 highlighter()->ignoreWord(selectedWord);
00652                 highlighter()->rehighlight();
00653             }
00654             else if (selectedAction == addToDictAction) {
00655                 highlighter()->addWordToDictionary(selectedWord);
00656                 highlighter()->rehighlight();
00657             }
00658 
00659             // Other actions can only be one of the suggested words
00660             else {
00661                 const QString replacement = selectedAction->text();
00662                 Q_ASSERT(reps.contains(replacement));
00663                 cursor.insertText(replacement);
00664                 setTextCursor(cursor);
00665             }
00666         }
00667     }
00668 }
00669 
00670 void KTextEdit::wheelEvent( QWheelEvent *event )
00671 {
00672   if ( KGlobalSettings::wheelMouseZooms() )
00673     QTextEdit::wheelEvent( event );
00674   else // thanks, we don't want to zoom, so skip QTextEdit's impl.
00675     QAbstractScrollArea::wheelEvent( event );
00676 }
00677 
00678 void KTextEdit::createHighlighter()
00679 {
00680     setHighlighter(new Sonnet::Highlighter(this, d->spellCheckingConfigFileName));
00681 }
00682 
00683 Sonnet::Highlighter* KTextEdit::highlighter() const
00684 {
00685     return d->highlighter;
00686 }
00687 
00688 void KTextEdit::setHighlighter(Sonnet::Highlighter *_highLighter)
00689 {
00690     delete d->highlighter;
00691     d->highlighter = _highLighter;
00692 }
00693 
00694 void KTextEdit::setCheckSpellingEnabled(bool check)
00695 {
00696     if (d->spellInterface)
00697         d->spellInterface->setSpellCheckingEnabled(check);
00698     else
00699         setCheckSpellingEnabledInternal(check);
00700 }
00701 
00702 void KTextEdit::setCheckSpellingEnabledInternal( bool check )
00703 {
00704   emit checkSpellingChanged( check );
00705   if ( check == d->checkSpellingEnabled )
00706     return;
00707 
00708   // From the above statment we know know that if we're turning checking
00709   // on that we need to create a new highlighter and if we're turning it
00710   // off we should remove the old one.
00711 
00712   d->checkSpellingEnabled = check;
00713     if ( check )
00714     {
00715         if ( hasFocus() ) {
00716             createHighlighter();
00717             if (!spellCheckingLanguage().isEmpty())
00718                 setSpellCheckingLanguage(spellCheckingLanguage());
00719         }
00720     }
00721     else
00722     {
00723         delete d->highlighter;
00724         d->highlighter = 0;
00725     }
00726 }
00727 
00728 void KTextEdit::focusInEvent( QFocusEvent *event )
00729 {
00730   if ( d->checkSpellingEnabled && !isReadOnly() && !d->highlighter )
00731     createHighlighter();
00732 
00733   if (!d->clickMessage.isEmpty()) {
00734       d->updateClickMessageRect();
00735   }
00736   QTextEdit::focusInEvent( event );
00737 }
00738 
00739 bool KTextEdit::checkSpellingEnabled() const
00740 {
00741     if (d->spellInterface)
00742       return d->spellInterface->isSpellCheckingEnabled();
00743     else
00744       return checkSpellingEnabledInternal();
00745 }
00746 
00747 bool KTextEdit::checkSpellingEnabledInternal() const
00748 {
00749   return d->checkSpellingEnabled;
00750 }
00751 
00752 void KTextEdit::setReadOnly( bool readOnly )
00753 {
00754   if ( !readOnly && hasFocus() && d->checkSpellingEnabled && !d->highlighter )
00755     createHighlighter();
00756 
00757   if ( readOnly == isReadOnly() )
00758     return;
00759 
00760   if ( readOnly ) {
00761     delete d->highlighter;
00762     d->highlighter = 0;
00763 
00764     d->customPalette = testAttribute( Qt::WA_SetPalette );
00765     QPalette p = palette();
00766     QColor color = p.color( QPalette::Disabled, QPalette::Background );
00767     p.setColor( QPalette::Base, color );
00768     p.setColor( QPalette::Background, color );
00769     setPalette( p );
00770   } else {
00771     if ( d->customPalette && testAttribute( Qt::WA_SetPalette ) ) {
00772         QPalette p = palette();
00773         QColor color = p.color( QPalette::Normal, QPalette::Base );
00774         p.setColor( QPalette::Base, color );
00775         p.setColor( QPalette::Background, color );
00776         setPalette( p );
00777     } else
00778         setPalette( QPalette() );
00779   }
00780 
00781   QTextEdit::setReadOnly( readOnly );
00782 }
00783 
00784 void KTextEdit::checkSpelling()
00785 {
00786   if(document()->isEmpty())
00787   {
00788       KMessageBox::information(this, i18n("Nothing to spell check."));
00789       return;
00790   }
00791   Sonnet::BackgroundChecker *backgroundSpellCheck = new Sonnet::BackgroundChecker(this);
00792   if(!d->spellCheckingLanguage.isEmpty())
00793      backgroundSpellCheck->changeLanguage(d->spellCheckingLanguage);
00794   Sonnet::Dialog *spellDialog = new Sonnet::Dialog(
00795       backgroundSpellCheck, 0);
00796   connect(spellDialog, SIGNAL(replace(QString,int,QString)),
00797           this, SLOT(spellCheckerCorrected(QString,int,QString)));
00798   connect(spellDialog, SIGNAL(misspelling(QString,int)),
00799           this, SLOT(spellCheckerMisspelling(QString,int)));
00800   connect(spellDialog, SIGNAL(autoCorrect(QString,QString)),
00801           this, SLOT(spellCheckerAutoCorrect(QString,QString)));
00802   connect(spellDialog, SIGNAL(done(QString)),
00803           this, SLOT(spellCheckerFinished()));
00804   connect(spellDialog, SIGNAL(cancel()),
00805           this, SLOT(spellCheckerCanceled()));
00806   connect(spellDialog, SIGNAL(stop()),
00807           this, SLOT(spellCheckerFinished()));
00808   connect(spellDialog, SIGNAL(spellCheckStatus(QString)),
00809           this,SIGNAL(spellCheckStatus(QString)));
00810   connect(spellDialog, SIGNAL(languageChanged(QString)),
00811           this, SIGNAL(languageChanged(QString)));
00812   d->originalDoc = QTextDocumentFragment(document());
00813   spellDialog->setBuffer(toPlainText());
00814   spellDialog->show();
00815 }
00816 
00817 void KTextEdit::highlightWord( int length, int pos )
00818 {
00819   QTextCursor cursor(document());
00820   cursor.setPosition(pos);
00821   cursor.setPosition(pos+length,QTextCursor::KeepAnchor);
00822   setTextCursor (cursor);
00823   ensureCursorVisible();
00824 }
00825 
00826 void KTextEdit::replace()
00827 {
00828      if( document()->isEmpty() )  // saves having to track the text changes
00829         return;
00830 
00831     if ( d->repDlg ) {
00832       KWindowSystem::activateWindow( d->repDlg->winId() );
00833     } else {
00834       d->repDlg = new KReplaceDialog(this, 0,
00835                                     QStringList(), QStringList(), false);
00836       connect( d->repDlg, SIGNAL(okClicked()), this, SLOT(slotDoReplace()) );
00837     }
00838     d->repDlg->show();
00839 }
00840 
00841 void KTextEdit::slotDoReplace()
00842 {
00843     if (!d->repDlg) {
00844         // Should really assert()
00845         return;
00846     }
00847 
00848     if(d->repDlg->pattern().isEmpty()) {
00849     delete d->replace;
00850         d->replace = 0;
00851         ensureCursorVisible();
00852         return;
00853     }
00854 
00855     delete d->replace;
00856     d->replace = new KReplace(d->repDlg->pattern(), d->repDlg->replacement(), d->repDlg->options(), this);
00857     d->repIndex = 0;
00858     if (d->replace->options() & KFind::FromCursor || d->replace->options() & KFind::FindBackwards) {
00859         d->repIndex = textCursor().anchor();
00860     }
00861 
00862     // Connect highlight signal to code which handles highlighting
00863     // of found text.
00864     connect(d->replace, SIGNAL(highlight(QString,int,int)),
00865             this, SLOT(slotFindHighlight(QString,int,int)));
00866     connect(d->replace, SIGNAL(findNext()), this, SLOT(slotReplaceNext()));
00867     connect(d->replace, SIGNAL(replace(QString,int,int,int)),
00868             this, SLOT(slotReplaceText(QString,int,int,int)));
00869 
00870     d->repDlg->close();
00871     slotReplaceNext();
00872 }
00873 
00874 
00875 void KTextEdit::slotReplaceNext()
00876 {
00877     if (!d->replace)
00878         return;
00879 
00880     d->lastReplacedPosition = -1;
00881     if (!(d->replace->options() & KReplaceDialog::PromptOnReplace)) {
00882         textCursor().beginEditBlock(); // #48541
00883         viewport()->setUpdatesEnabled(false);
00884     }
00885 
00886     KFind::Result res = KFind::NoMatch;
00887 
00888     if (d->replace->needData())
00889         d->replace->setData(toPlainText(), d->repIndex);
00890     res = d->replace->replace();
00891     if (!(d->replace->options() & KReplaceDialog::PromptOnReplace)) {
00892         textCursor().endEditBlock(); // #48541
00893         if (d->lastReplacedPosition >= 0) {
00894             QTextCursor tc = textCursor();
00895             tc.setPosition(d->lastReplacedPosition);
00896             setTextCursor(tc);
00897             ensureCursorVisible();
00898         }
00899 
00900         viewport()->setUpdatesEnabled(true);
00901         viewport()->update();
00902     }
00903 
00904     if (res == KFind::NoMatch) {
00905         d->replace->displayFinalDialog();
00906         d->replace->disconnect(this);
00907         d->replace->deleteLater(); // we are in a slot connected to m_replace, don't delete it right away
00908         d->replace = 0;
00909         ensureCursorVisible();
00910         //or           if ( m_replace->shouldRestart() ) { reinit (w/o FromCursor) and call slotReplaceNext(); }
00911     } else {
00912         //m_replace->closeReplaceNextDialog();
00913     }
00914 }
00915 
00916 
00917 void KTextEdit::slotDoFind()
00918 {
00919     if (!d->findDlg) {
00920         // Should really assert()
00921         return;
00922     }
00923     if( d->findDlg->pattern().isEmpty())
00924     {
00925         delete d->find;
00926         d->find = 0;
00927         return;
00928     }
00929     delete d->find;
00930     d->find = new KFind(d->findDlg->pattern(), d->findDlg->options(), this);
00931     d->findIndex = 0;
00932     if (d->find->options() & KFind::FromCursor || d->find->options() & KFind::FindBackwards) {
00933         d->findIndex = textCursor().anchor();
00934     }
00935 
00936     // Connect highlight signal to code which handles highlighting
00937     // of found text.
00938     connect(d->find, SIGNAL(highlight(QString,int,int)),
00939             this, SLOT(slotFindHighlight(QString,int,int)));
00940     connect(d->find, SIGNAL(findNext()), this, SLOT(slotFindNext()));
00941 
00942     d->findDlg->close();
00943     d->find->closeFindNextDialog();
00944     slotFindNext();
00945 }
00946 
00947 
00948 void KTextEdit::slotFindNext()
00949 {
00950     if (!d->find)
00951         return;
00952     if(document()->isEmpty())
00953     {
00954         d->find->disconnect(this);
00955         d->find->deleteLater(); // we are in a slot connected to m_find, don't delete right away
00956         d->find = 0;
00957         return;
00958     }
00959 
00960     KFind::Result res = KFind::NoMatch;
00961     if (d->find->needData())
00962         d->find->setData(toPlainText(), d->findIndex);
00963     res = d->find->find();
00964 
00965     if (res == KFind::NoMatch) {
00966         d->find->displayFinalDialog();
00967         d->find->disconnect(this);
00968         d->find->deleteLater(); // we are in a slot connected to m_find, don't delete right away
00969         d->find = 0;
00970         //or           if ( m_find->shouldRestart() ) { reinit (w/o FromCursor) and call slotFindNext(); }
00971     } else {
00972         //m_find->closeFindNextDialog();
00973     }
00974 }
00975 
00976 
00977 void KTextEdit::slotFind()
00978 {
00979     if( document()->isEmpty() )  // saves having to track the text changes
00980         return;
00981 
00982     if ( d->findDlg ) {
00983       KWindowSystem::activateWindow( d->findDlg->winId() );
00984     } else {
00985       d->findDlg = new KFindDialog(this);
00986       connect( d->findDlg, SIGNAL(okClicked()), this, SLOT(slotDoFind()) );
00987     }
00988     d->findDlg->show();
00989 }
00990 
00991 
00992 void KTextEdit::slotReplace()
00993 {
00994     if( document()->isEmpty() )  // saves having to track the text changes
00995         return;
00996 
00997     if ( d->repDlg ) {
00998       KWindowSystem::activateWindow( d->repDlg->winId() );
00999     } else {
01000       d->repDlg = new KReplaceDialog(this, 0,
01001                                     QStringList(), QStringList(), false);
01002       connect( d->repDlg, SIGNAL(okClicked()), this, SLOT(slotDoReplace()) );
01003     }
01004     d->repDlg->show();
01005 }
01006 
01007 void KTextEdit::enableFindReplace( bool enabled )
01008 {
01009     d->findReplaceEnabled = enabled;
01010 }
01011 
01012 void KTextEdit::setSpellInterface(KTextEditSpellInterface *spellInterface)
01013 {
01014     d->spellInterface = spellInterface;
01015 }
01016 
01017 bool KTextEdit::Private::overrideShortcut(const QKeyEvent* event)
01018 {
01019   const int key = event->key() | event->modifiers();
01020 
01021   if ( KStandardShortcut::copy().contains( key ) ) {
01022     return true;
01023   } else if ( KStandardShortcut::paste().contains( key ) ) {
01024     return true;
01025   } else if ( KStandardShortcut::cut().contains( key ) ) {
01026     return true;
01027   } else if ( KStandardShortcut::undo().contains( key ) ) {
01028     return true;
01029   } else if ( KStandardShortcut::redo().contains( key ) ) {
01030     return true;
01031   } else if ( KStandardShortcut::deleteWordBack().contains( key ) ) {
01032     return true;
01033   } else if ( KStandardShortcut::deleteWordForward().contains( key ) ) {
01034     return true;
01035   } else if ( KStandardShortcut::backwardWord().contains( key ) ) {
01036     return true;
01037   } else if ( KStandardShortcut::forwardWord().contains( key ) ) {
01038     return true;
01039   } else if ( KStandardShortcut::next().contains( key ) ) {
01040     return true;
01041   } else if ( KStandardShortcut::prior().contains( key ) ) {
01042     return true;
01043   } else if ( KStandardShortcut::begin().contains( key ) ) {
01044     return true;
01045   } else if ( KStandardShortcut::end().contains( key ) ) {
01046     return true;
01047   } else if ( KStandardShortcut::beginningOfLine().contains( key ) ) {
01048     return true;
01049   } else if ( KStandardShortcut::endOfLine().contains( key ) ) {
01050     return true;
01051   } else if ( KStandardShortcut::pasteSelection().contains( key ) ) {
01052     return true;
01053   } else if (findReplaceEnabled && KStandardShortcut::find().contains(key)) {
01054       return true;
01055   } else if (findReplaceEnabled && KStandardShortcut::findNext().contains(key)) {
01056       return true;
01057   } else if (findReplaceEnabled && KStandardShortcut::replace().contains(key)) {
01058       return true;
01059   } else if (event->matches(QKeySequence::SelectAll)) { // currently missing in QTextEdit
01060       return true;
01061   } else if (event->modifiers() == Qt::ControlModifier &&
01062             (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) &&
01063               qobject_cast<KDialog*>(parent->window()) ) {
01064     // ignore Ctrl-Return so that KDialogs can close the dialog
01065     return true;
01066   }
01067   return false;
01068 }
01069 
01070 void KTextEdit::keyPressEvent( QKeyEvent *event )
01071 {
01072     if (d->handleShortcut(event)) {
01073         event->accept();
01074     }else if (event->modifiers() == Qt::ControlModifier &&
01075             (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) &&
01076               qobject_cast<KDialog*>(window()) ) {
01077         event->ignore();
01078     } else {
01079         QTextEdit::keyPressEvent(event);
01080     }
01081 }
01082 
01083 void KTextEdit::setClickMessage(const QString &msg)
01084 {
01085     if (msg != d->clickMessage) {
01086         if (!d->clickMessage.isEmpty()) {
01087             d->updateClickMessageRect();
01088         }
01089         d->clickMessage = msg;
01090         if (!d->clickMessage.isEmpty()) {
01091             d->updateClickMessageRect();
01092         }
01093     }
01094 }
01095 
01096 QString KTextEdit::clickMessage() const
01097 {
01098     return d->clickMessage;
01099 }
01100 
01101 void KTextEdit::paintEvent(QPaintEvent *ev)
01102 {
01103     QTextEdit::paintEvent(ev);
01104 
01105     if (!d->clickMessage.isEmpty() && !hasFocus() && document()->isEmpty()) {
01106         QPainter p(viewport());
01107 
01108         QFont f = font();
01109         f.setItalic(d->italicizePlaceholder);
01110         p.setFont(f);
01111 
01112         QColor color(palette().color(foregroundRole()));
01113         color.setAlphaF(0.5);
01114         p.setPen(color);
01115 
01116         int margin = int(document()->documentMargin());
01117         QRect cr = viewport()->rect().adjusted(margin, margin, -margin, -margin);
01118 
01119         p.drawText(cr, Qt::AlignTop | Qt::TextWordWrap, d->clickMessage);
01120     }
01121 }
01122 
01123 void KTextEdit::focusOutEvent(QFocusEvent *ev)
01124 {
01125     if (!d->clickMessage.isEmpty()) {
01126         d->updateClickMessageRect();
01127     }
01128     QTextEdit::focusOutEvent(ev);
01129 }
01130 
01131 #include "ktextedit.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:53:07 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEUI

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

kdelibs-4.8.3 API Reference

Skip menu "kdelibs-4.8.3 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal