00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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 , 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
00142 }
00143
00144 void KTextEdit::Private::spellCheckerMisspelling( const QString &text, int pos )
00145 {
00146
00147 parent->highlightWord( text.length(), pos );
00148 }
00149
00150 void KTextEdit::Private::spellCheckerCorrected( const QString& oldWord, int pos,const QString& newWord)
00151 {
00152
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
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 , int matchedLength) {
00214 Q_UNUSED(text)
00215
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 );
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
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
00528 QTextCursor cursorAtMouse = cursorForPosition(event->pos());
00529 const int mousePos = cursorAtMouse.position();
00530 QTextCursor cursor = textCursor();
00531
00532
00533 const bool selectedWordClicked = cursor.hasSelection() &&
00534 mousePos >= cursor.selectionStart() &&
00535 mousePos <= cursor.selectionEnd();
00536
00537
00538
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
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
00570
00571
00572
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
00586
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;
00596 QMenu menu;
00597
00598
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
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
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
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
00685
00686
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() )
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
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
00838
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();
00872 d->replace = 0;
00873 ensureCursorVisible();
00874
00875 } else {
00876
00877 }
00878 }
00879
00880
00881 void KTextEdit::slotDoFind()
00882 {
00883 if (!d->findDlg) {
00884
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
00901
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();
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();
00933 d->find = 0;
00934
00935 } else {
00936
00937 }
00938 }
00939
00940
00941 void KTextEdit::slotFind()
00942 {
00943 if( document()->isEmpty() )
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() )
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)) {
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
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"