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