diff --git a/src/widgets/ktextedit.cpp b/src/widgets/ktextedit.cpp index de47383..9c212e8 100644 --- a/src/widgets/ktextedit.cpp +++ b/src/widgets/ktextedit.cpp @@ -1,1067 +1,1067 @@ /* This file is part of the KDE libraries Copyright (C) 2002 Carsten Pfeiffer 2005 Michael Brade 2012 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ktextedit.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_SPEECH #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "kreplacedialog.h" #include "kfinddialog.h" #include "kfind.h" #include "kreplace.h" class KTextDecorator : public Sonnet::SpellCheckDecorator { public: explicit KTextDecorator(KTextEdit *textEdit); bool isSpellCheckingEnabledForBlock(const QString &textBlock) const override; private: KTextEdit *m_textEdit; }; class Q_DECL_HIDDEN KTextEdit::Private { public: Private(KTextEdit *_parent) : parent(_parent), languagesMenu(nullptr), customPalette(false), spellCheckingEnabled(false), findReplaceEnabled(true), showTabAction(true), showAutoCorrectionButton(false), decorator(nullptr), speller(nullptr), findDlg(nullptr), find(nullptr), repDlg(nullptr), replace(nullptr), #ifdef HAVE_SPEECH textToSpeech(nullptr), #endif findIndex(0), repIndex(0), lastReplacedPosition(-1) { //Check the default sonnet settings to see if spellchecking should be enabled. QSettings settings(QStringLiteral("KDE"), QStringLiteral("Sonnet")); spellCheckingEnabled = settings.value(QStringLiteral("checkerEnabledByDefault"), false).toBool(); -#ifdef HAVE_SPEECH - textToSpeech = new QTextToSpeech(parent); -#endif } ~Private() { delete decorator; delete findDlg; delete find; delete replace; delete repDlg; delete speller; #ifdef HAVE_SPEECH delete textToSpeech; #endif } /** * Checks whether we should/should not consume a key used as a shortcut. * This makes it possible to handle shortcuts in the focused widget before any * window-global QAction is triggered. */ bool overrideShortcut(const QKeyEvent *e); /** * Actually handle a shortcut event. */ bool handleShortcut(const QKeyEvent *e); void spellCheckerMisspelling(const QString &text, int pos); void spellCheckerCorrected(const QString &, int, const QString &); void spellCheckerAutoCorrect(const QString &, const QString &); void spellCheckerCanceled(); void spellCheckerFinished(); void toggleAutoSpellCheck(); void slotFindHighlight(const QString &text, int matchingIndex, int matchingLength); void slotReplaceText(const QString &text, int replacementIndex, int /*replacedLength*/, int matchedLength); /** * Similar to QTextEdit::clear(), only that it is possible to undo this * action. */ void undoableClear(); void slotAllowTab(); void menuActivated(QAction *action); void init(); void checkSpelling(bool force); KTextEdit *parent; QAction *autoSpellCheckAction; QAction *allowTab; QAction *spellCheckAction; QMenu *languagesMenu; bool customPalette : 1; bool spellCheckingEnabled : 1; bool findReplaceEnabled: 1; bool showTabAction: 1; bool showAutoCorrectionButton: 1; QTextDocumentFragment originalDoc; QString spellCheckingLanguage; Sonnet::SpellCheckDecorator *decorator; Sonnet::Speller *speller; KFindDialog *findDlg; KFind *find; KReplaceDialog *repDlg; KReplace *replace; #ifdef HAVE_SPEECH QTextToSpeech *textToSpeech; #endif int findIndex, repIndex; int lastReplacedPosition; }; void KTextEdit::Private::checkSpelling(bool force) { if (parent->document()->isEmpty()) { KMessageBox::information(parent, i18n("Nothing to spell check.")); if (force) { emit parent->spellCheckingFinished(); } return; } Sonnet::BackgroundChecker *backgroundSpellCheck = new Sonnet::BackgroundChecker; if (!spellCheckingLanguage.isEmpty()) { backgroundSpellCheck->changeLanguage(spellCheckingLanguage); } Sonnet::Dialog *spellDialog = new Sonnet::Dialog( backgroundSpellCheck, force ? parent : nullptr); backgroundSpellCheck->setParent(spellDialog); spellDialog->setAttribute(Qt::WA_DeleteOnClose, true); spellDialog->activeAutoCorrect(showAutoCorrectionButton); connect(spellDialog, SIGNAL(replace(QString,int,QString)), parent, SLOT(spellCheckerCorrected(QString,int,QString))); connect(spellDialog, SIGNAL(misspelling(QString,int)), parent, SLOT(spellCheckerMisspelling(QString,int))); connect(spellDialog, &Sonnet::Dialog::autoCorrect, parent, &KTextEdit::spellCheckerAutoCorrect); connect(spellDialog, SIGNAL(done(QString)), parent, SLOT(spellCheckerFinished())); connect(spellDialog, SIGNAL(cancel()), parent, SLOT(spellCheckerCanceled())); //Laurent in sonnet/dialog.cpp we emit done(QString) too => it calls here twice spellCheckerFinished not necessary /* connect(spellDialog, SIGNAL(stop()), parent, SLOT(spellCheckerFinished())); */ connect(spellDialog, &Sonnet::Dialog::spellCheckStatus, parent, &KTextEdit::spellCheckStatus); connect(spellDialog, &Sonnet::Dialog::languageChanged, parent, &KTextEdit::languageChanged); if (force) { connect(spellDialog, SIGNAL(done(QString)), parent, SIGNAL(spellCheckingFinished())); connect(spellDialog, &Sonnet::Dialog::cancel, parent, &KTextEdit::spellCheckingCanceled); //Laurent in sonnet/dialog.cpp we emit done(QString) too => it calls here twice spellCheckerFinished not necessary //connect(spellDialog, SIGNAL(stop()), parent, SIGNAL(spellCheckingFinished())); } originalDoc = QTextDocumentFragment(parent->document()); spellDialog->setBuffer(parent->toPlainText()); spellDialog->show(); } void KTextEdit::Private::spellCheckerCanceled() { QTextDocument *doc = parent->document(); doc->clear(); QTextCursor cursor(doc); cursor.insertFragment(originalDoc); spellCheckerFinished(); } void KTextEdit::Private::spellCheckerAutoCorrect(const QString ¤tWord, const QString &autoCorrectWord) { emit parent->spellCheckerAutoCorrect(currentWord, autoCorrectWord); } void KTextEdit::Private::spellCheckerMisspelling(const QString &text, int pos) { //qDebug()<<"TextEdit::Private::spellCheckerMisspelling :"<highlightWord(text.length(), pos); } void KTextEdit::Private::spellCheckerCorrected(const QString &oldWord, int pos, const QString &newWord) { //qDebug()<<" oldWord :"<document()); cursor.setPosition(pos); cursor.setPosition(pos + oldWord.length(), QTextCursor::KeepAnchor); cursor.insertText(newWord); } } void KTextEdit::Private::spellCheckerFinished() { QTextCursor cursor(parent->document()); cursor.clearSelection(); parent->setTextCursor(cursor); if (parent->highlighter()) { parent->highlighter()->rehighlight(); } } void KTextEdit::Private::toggleAutoSpellCheck() { parent->setCheckSpellingEnabled(!parent->checkSpellingEnabled()); } void KTextEdit::Private::undoableClear() { QTextCursor cursor = parent->textCursor(); cursor.beginEditBlock(); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); cursor.removeSelectedText(); cursor.endEditBlock(); } void KTextEdit::Private::slotAllowTab() { parent->setTabChangesFocus(!parent->tabChangesFocus()); } void KTextEdit::Private::menuActivated(QAction *action) { if (action == spellCheckAction) { parent->checkSpelling(); } else if (action == autoSpellCheckAction) { toggleAutoSpellCheck(); } else if (action == allowTab) { slotAllowTab(); } } void KTextEdit::Private::slotFindHighlight(const QString &text, int matchingIndex, int matchingLength) { Q_UNUSED(text) //qDebug() << "Highlight: [" << text << "] mi:" << matchingIndex << " ml:" << matchingLength; QTextCursor tc = parent->textCursor(); tc.setPosition(matchingIndex); tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, matchingLength); parent->setTextCursor(tc); parent->ensureCursorVisible(); } void KTextEdit::Private::slotReplaceText(const QString &text, int replacementIndex, int replacedLength, int matchedLength) { //qDebug() << "Replace: [" << text << "] ri:" << replacementIndex << " rl:" << replacedLength << " ml:" << matchedLength; QTextCursor tc = parent->textCursor(); tc.setPosition(replacementIndex); tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, matchedLength); tc.removeSelectedText(); tc.insertText(text.mid(replacementIndex, replacedLength)); if (replace->options() & KReplaceDialog::PromptOnReplace) { parent->setTextCursor(tc); parent->ensureCursorVisible(); } lastReplacedPosition = replacementIndex; } void KTextEdit::Private::init() { KCursor::setAutoHideCursor(parent, true, false); parent->connect(parent, &KTextEdit::languageChanged, parent, &KTextEdit::setSpellCheckingLanguage); } KTextDecorator::KTextDecorator(KTextEdit *textEdit): SpellCheckDecorator(textEdit), m_textEdit(textEdit) { } bool KTextDecorator::isSpellCheckingEnabledForBlock(const QString &textBlock) const { return m_textEdit->shouldBlockBeSpellChecked(textBlock); } KTextEdit::KTextEdit(const QString &text, QWidget *parent) : QTextEdit(text, parent), d(new Private(this)) { d->init(); } KTextEdit::KTextEdit(QWidget *parent) : QTextEdit(parent), d(new Private(this)) { d->init(); } KTextEdit::~KTextEdit() { delete d; } const QString &KTextEdit::spellCheckingLanguage() const { return d->spellCheckingLanguage; } void KTextEdit::setSpellCheckingLanguage(const QString &_language) { if (highlighter()) { highlighter()->setCurrentLanguage(_language); highlighter()->rehighlight(); } if (_language != d->spellCheckingLanguage) { d->spellCheckingLanguage = _language; emit languageChanged(_language); } } void KTextEdit::showSpellConfigDialog(const QString &windowIcon) { Sonnet::ConfigDialog dialog(this); if (!d->spellCheckingLanguage.isEmpty()) { dialog.setLanguage(d->spellCheckingLanguage); } if (!windowIcon.isEmpty()) { dialog.setWindowIcon(QIcon::fromTheme(windowIcon, dialog.windowIcon())); } if (dialog.exec()) { setSpellCheckingLanguage(dialog.language()); } } bool KTextEdit::event(QEvent *ev) { if (ev->type() == QEvent::ShortcutOverride) { QKeyEvent *e = static_cast(ev); if (d->overrideShortcut(e)) { e->accept(); return true; } } return QTextEdit::event(ev); } bool KTextEdit::Private::handleShortcut(const QKeyEvent *event) { const int key = event->key() | event->modifiers(); if (KStandardShortcut::copy().contains(key)) { parent->copy(); return true; } else if (KStandardShortcut::paste().contains(key)) { parent->paste(); return true; } else if (KStandardShortcut::cut().contains(key)) { parent->cut(); return true; } else if (KStandardShortcut::undo().contains(key)) { if (!parent->isReadOnly()) { parent->undo(); } return true; } else if (KStandardShortcut::redo().contains(key)) { if (!parent->isReadOnly()) { parent->redo(); } return true; } else if (KStandardShortcut::deleteWordBack().contains(key)) { if (!parent->isReadOnly()) { parent->deleteWordBack(); } return true; } else if (KStandardShortcut::deleteWordForward().contains(key)) { if (!parent->isReadOnly()) { parent->deleteWordForward(); } return true; } else if (KStandardShortcut::backwardWord().contains(key)) { QTextCursor cursor = parent->textCursor(); cursor.movePosition(QTextCursor::PreviousWord); parent->setTextCursor(cursor); return true; } else if (KStandardShortcut::forwardWord().contains(key)) { QTextCursor cursor = parent->textCursor(); cursor.movePosition(QTextCursor::NextWord); parent->setTextCursor(cursor); return true; } else if (KStandardShortcut::next().contains(key)) { QTextCursor cursor = parent->textCursor(); bool moved = false; qreal lastY = parent->cursorRect(cursor).bottom(); qreal distance = 0; do { qreal y = parent->cursorRect(cursor).bottom(); distance += qAbs(y - lastY); lastY = y; moved = cursor.movePosition(QTextCursor::Down); } while (moved && distance < parent->viewport()->height()); if (moved) { cursor.movePosition(QTextCursor::Up); parent->verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); } parent->setTextCursor(cursor); return true; } else if (KStandardShortcut::prior().contains(key)) { QTextCursor cursor = parent->textCursor(); bool moved = false; qreal lastY = parent->cursorRect(cursor).bottom(); qreal distance = 0; do { qreal y = parent->cursorRect(cursor).bottom(); distance += qAbs(y - lastY); lastY = y; moved = cursor.movePosition(QTextCursor::Up); } while (moved && distance < parent->viewport()->height()); if (moved) { cursor.movePosition(QTextCursor::Down); parent->verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); } parent->setTextCursor(cursor); return true; } else if (KStandardShortcut::begin().contains(key)) { QTextCursor cursor = parent->textCursor(); cursor.movePosition(QTextCursor::Start); parent->setTextCursor(cursor); return true; } else if (KStandardShortcut::end().contains(key)) { QTextCursor cursor = parent->textCursor(); cursor.movePosition(QTextCursor::End); parent->setTextCursor(cursor); return true; } else if (KStandardShortcut::beginningOfLine().contains(key)) { QTextCursor cursor = parent->textCursor(); cursor.movePosition(QTextCursor::StartOfLine); parent->setTextCursor(cursor); return true; } else if (KStandardShortcut::endOfLine().contains(key)) { QTextCursor cursor = parent->textCursor(); cursor.movePosition(QTextCursor::EndOfLine); parent->setTextCursor(cursor); return true; } else if (findReplaceEnabled && KStandardShortcut::find().contains(key)) { parent->slotFind(); return true; } else if (findReplaceEnabled && KStandardShortcut::findNext().contains(key)) { parent->slotFindNext(); return true; } else if (findReplaceEnabled && KStandardShortcut::findPrev().contains(key)) { parent->slotFindPrevious(); return true; } else if (findReplaceEnabled && KStandardShortcut::replace().contains(key)) { if (!parent->isReadOnly()) { parent->slotReplace(); } return true; } else if (KStandardShortcut::pasteSelection().contains(key)) { QString text = QApplication::clipboard()->text(QClipboard::Selection); if (!text.isEmpty()) { parent->insertPlainText(text); // TODO: check if this is html? (MiB) } return true; } return false; } static void deleteWord(QTextCursor cursor, QTextCursor::MoveOperation op) { cursor.clearSelection(); cursor.movePosition(op, QTextCursor::KeepAnchor); cursor.removeSelectedText(); } void KTextEdit::deleteWordBack() { deleteWord(textCursor(), QTextCursor::PreviousWord); } void KTextEdit::deleteWordForward() { deleteWord(textCursor(), QTextCursor::WordRight); } QMenu *KTextEdit::mousePopupMenu() { QMenu *popup = createStandardContextMenu(); if (!popup) { return nullptr; } connect(popup, SIGNAL(triggered(QAction*)), this, SLOT(menuActivated(QAction*))); const bool emptyDocument = document()->isEmpty(); if (!isReadOnly()) { QList actionList = popup->actions(); enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs }; QAction *separatorAction = nullptr; int idx = actionList.indexOf(actionList[SelectAllAct]) + 1; if (idx < actionList.count()) { separatorAction = actionList.at(idx); } if (separatorAction) { QAction *clearAllAction = KStandardAction::clear(this, SLOT(undoableClear()), popup); if (emptyDocument) { clearAllAction->setEnabled(false); } popup->insertAction(separatorAction, clearAllAction); } } KIconTheme::assignIconsToContextMenu(isReadOnly() ? KIconTheme::ReadOnlyText : KIconTheme::TextEditor, popup->actions()); if (!isReadOnly()) { popup->addSeparator(); if (!d->speller) { d->speller = new Sonnet::Speller(); } if (!d->speller->availableBackends().isEmpty()) { d->spellCheckAction = popup->addAction(QIcon::fromTheme(QStringLiteral("tools-check-spelling")), i18n("Check Spelling...")); if (emptyDocument) { d->spellCheckAction->setEnabled(false); } if (checkSpellingEnabled()) { d->languagesMenu = new QMenu(i18n("Spell Checking Language"), popup); QActionGroup *languagesGroup = new QActionGroup(d->languagesMenu); languagesGroup->setExclusive(true); QMapIterator i(d->speller->availableDictionaries()); const QString language = spellCheckingLanguage(); while (i.hasNext()) { i.next(); QAction *languageAction = d->languagesMenu->addAction(i.key()); languageAction->setCheckable(true); languageAction->setChecked(language == i.value() || (language.isEmpty() && d->speller->defaultLanguage() == i.value())); languageAction->setData(i.value()); languageAction->setActionGroup(languagesGroup); connect(languageAction, &QAction::triggered, [this, languageAction]() { setSpellCheckingLanguage(languageAction->data().toString()); }); } popup->addMenu(d->languagesMenu); } d->autoSpellCheckAction = popup->addAction(i18n("Auto Spell Check")); d->autoSpellCheckAction->setCheckable(true); d->autoSpellCheckAction->setChecked(checkSpellingEnabled()); popup->addSeparator(); } if (d->showTabAction) { d->allowTab = popup->addAction(i18n("Allow Tabulations")); d->allowTab->setCheckable(true); d->allowTab->setChecked(!tabChangesFocus()); } } if (d->findReplaceEnabled) { QAction *findAction = KStandardAction::find(this, SLOT(slotFind()), popup); QAction *findNextAction = KStandardAction::findNext(this, SLOT(slotFindNext()), popup); QAction *findPrevAction = KStandardAction::findPrev(this, SLOT(slotFindPrevious()), popup); if (emptyDocument) { findAction->setEnabled(false); findNextAction->setEnabled(false); findPrevAction->setEnabled(false); } else { findNextAction->setEnabled(d->find != nullptr); findPrevAction->setEnabled(d->find != nullptr); } popup->addSeparator(); popup->addAction(findAction); popup->addAction(findNextAction); popup->addAction(findPrevAction); if (!isReadOnly()) { QAction *replaceAction = KStandardAction::replace(this, SLOT(slotReplace()), popup); if (emptyDocument) { replaceAction->setEnabled(false); } popup->addAction(replaceAction); } } #ifdef HAVE_SPEECH popup->addSeparator(); QAction *speakAction = popup->addAction(i18n("Speak Text")); speakAction->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-text-to-speech"))); speakAction->setEnabled(!emptyDocument); connect(speakAction, &QAction::triggered, this, &KTextEdit::slotSpeakText); #endif return popup; } void KTextEdit::slotSpeakText() { #ifdef HAVE_SPEECH QString text; if (textCursor().hasSelection()) { text = textCursor().selectedText(); } else { text = toPlainText(); } + if (!d->textToSpeech) { + d->textToSpeech = new QTextToSpeech(this); + } d->textToSpeech->say(text); #endif } void KTextEdit::contextMenuEvent(QContextMenuEvent *event) { QMenu *popup = mousePopupMenu(); if (popup) { aboutToShowContextMenu(popup); popup->exec(event->globalPos()); delete popup; } } void KTextEdit::createHighlighter() { setHighlighter(new Sonnet::Highlighter(this)); } Sonnet::Highlighter *KTextEdit::highlighter() const { if (d->decorator) { return d->decorator->highlighter(); } else { return nullptr; } } void KTextEdit::clearDecorator() { delete d->decorator; d->decorator = nullptr; } void KTextEdit::addTextDecorator(Sonnet::SpellCheckDecorator *decorator) { d->decorator = decorator; } void KTextEdit::setHighlighter(Sonnet::Highlighter *_highLighter) { KTextDecorator *decorator = new KTextDecorator(this); // The old default highlighter must be manually deleted. delete decorator->highlighter(); decorator->setHighlighter(_highLighter); //KTextEdit used to take ownership of the highlighter, Sonnet::SpellCheckDecorator does not. //so we reparent the highlighter so it will be deleted when the decorator is destroyed _highLighter->setParent(decorator); addTextDecorator(decorator); } void KTextEdit::setCheckSpellingEnabled(bool check) { emit checkSpellingChanged(check); if (check == d->spellCheckingEnabled) { return; } // From the above statment we know know that if we're turning checking // on that we need to create a new highlighter and if we're turning it // off we should remove the old one. d->spellCheckingEnabled = check; if (check) { if (hasFocus()) { createHighlighter(); if (!spellCheckingLanguage().isEmpty()) { setSpellCheckingLanguage(spellCheckingLanguage()); } } } else { clearDecorator(); } } void KTextEdit::focusInEvent(QFocusEvent *event) { if (d->spellCheckingEnabled && !isReadOnly() && !d->decorator) { createHighlighter(); } QTextEdit::focusInEvent(event); } bool KTextEdit::checkSpellingEnabled() const { return d->spellCheckingEnabled; } bool KTextEdit::shouldBlockBeSpellChecked(const QString &) const { return true; } void KTextEdit::setReadOnly(bool readOnly) { if (!readOnly && hasFocus() && d->spellCheckingEnabled && !d->decorator) { createHighlighter(); } if (readOnly == isReadOnly()) { return; } if (readOnly) { delete d->decorator; d->decorator = nullptr; d->customPalette = testAttribute(Qt::WA_SetPalette); QPalette p = palette(); QColor color = p.color(QPalette::Disabled, QPalette::Background); p.setColor(QPalette::Base, color); p.setColor(QPalette::Background, color); setPalette(p); } else { if (d->customPalette && testAttribute(Qt::WA_SetPalette)) { QPalette p = palette(); QColor color = p.color(QPalette::Normal, QPalette::Base); p.setColor(QPalette::Base, color); p.setColor(QPalette::Background, color); setPalette(p); } else { setPalette(QPalette()); } } QTextEdit::setReadOnly(readOnly); } void KTextEdit::checkSpelling() { d->checkSpelling(false); } void KTextEdit::forceSpellChecking() { d->checkSpelling(true); } void KTextEdit::highlightWord(int length, int pos) { QTextCursor cursor(document()); cursor.setPosition(pos); cursor.setPosition(pos + length, QTextCursor::KeepAnchor); setTextCursor(cursor); ensureCursorVisible(); } void KTextEdit::replace() { if (document()->isEmpty()) { // saves having to track the text changes return; } if (d->repDlg) { KWindowSystem::activateWindow(d->repDlg->winId()); } else { d->repDlg = new KReplaceDialog(this, 0, QStringList(), QStringList(), false); connect(d->repDlg, &KFindDialog::okClicked, this, &KTextEdit::slotDoReplace); } d->repDlg->show(); } void KTextEdit::slotDoReplace() { if (!d->repDlg) { // Should really assert() return; } if (d->repDlg->pattern().isEmpty()) { delete d->replace; d->replace = nullptr; ensureCursorVisible(); return; } delete d->replace; d->replace = new KReplace(d->repDlg->pattern(), d->repDlg->replacement(), d->repDlg->options(), this); d->repIndex = 0; if (d->replace->options() & KFind::FromCursor || d->replace->options() & KFind::FindBackwards) { d->repIndex = textCursor().anchor(); } // Connect highlight signal to code which handles highlighting // of found text. connect(d->replace, SIGNAL(highlight(QString,int,int)), this, SLOT(slotFindHighlight(QString,int,int))); connect(d->replace, &KFind::findNext, this, &KTextEdit::slotReplaceNext); connect(d->replace, SIGNAL(replace(QString,int,int,int)), this, SLOT(slotReplaceText(QString,int,int,int))); d->repDlg->close(); slotReplaceNext(); } void KTextEdit::slotReplaceNext() { if (!d->replace) { return; } d->lastReplacedPosition = -1; if (!(d->replace->options() & KReplaceDialog::PromptOnReplace)) { textCursor().beginEditBlock(); // #48541 viewport()->setUpdatesEnabled(false); } KFind::Result res = KFind::NoMatch; if (d->replace->needData()) { d->replace->setData(toPlainText(), d->repIndex); } res = d->replace->replace(); if (!(d->replace->options() & KReplaceDialog::PromptOnReplace)) { textCursor().endEditBlock(); // #48541 if (d->lastReplacedPosition >= 0) { QTextCursor tc = textCursor(); tc.setPosition(d->lastReplacedPosition); setTextCursor(tc); ensureCursorVisible(); } viewport()->setUpdatesEnabled(true); viewport()->update(); } if (res == KFind::NoMatch) { d->replace->displayFinalDialog(); d->replace->disconnect(this); d->replace->deleteLater(); // we are in a slot connected to m_replace, don't delete it right away d->replace = nullptr; ensureCursorVisible(); //or if ( m_replace->shouldRestart() ) { reinit (w/o FromCursor) and call slotReplaceNext(); } } else { //m_replace->closeReplaceNextDialog(); } } void KTextEdit::slotDoFind() { if (!d->findDlg) { // Should really assert() return; } if (d->findDlg->pattern().isEmpty()) { delete d->find; d->find = nullptr; return; } delete d->find; d->find = new KFind(d->findDlg->pattern(), d->findDlg->options(), this); d->findIndex = 0; if (d->find->options() & KFind::FromCursor || d->find->options() & KFind::FindBackwards) { d->findIndex = textCursor().anchor(); } // Connect highlight signal to code which handles highlighting // of found text. connect(d->find, SIGNAL(highlight(QString,int,int)), this, SLOT(slotFindHighlight(QString,int,int))); connect(d->find, &KFind::findNext, this, &KTextEdit::slotFindNext); d->findDlg->close(); d->find->closeFindNextDialog(); slotFindNext(); } void KTextEdit::slotFindNext() { if (!d->find) { return; } if (document()->isEmpty()) { d->find->disconnect(this); d->find->deleteLater(); // we are in a slot connected to m_find, don't delete right away d->find = nullptr; return; } KFind::Result res = KFind::NoMatch; if (d->find->needData()) { d->find->setData(toPlainText(), d->findIndex); } res = d->find->find(); if (res == KFind::NoMatch) { d->find->displayFinalDialog(); d->find->disconnect(this); d->find->deleteLater(); // we are in a slot connected to m_find, don't delete right away d->find = nullptr; //or if ( m_find->shouldRestart() ) { reinit (w/o FromCursor) and call slotFindNext(); } } else { //m_find->closeFindNextDialog(); } } void KTextEdit::slotFindPrevious() { if (!d->find) { return; } const long oldOptions = d->find->options(); d->find->setOptions(oldOptions ^ KFind::FindBackwards); slotFindNext(); if (d->find) { d->find->setOptions(oldOptions); } } void KTextEdit::slotFind() { if (document()->isEmpty()) { // saves having to track the text changes return; } if (d->findDlg) { KWindowSystem::activateWindow(d->findDlg->winId()); } else { d->findDlg = new KFindDialog(this); connect(d->findDlg, &KFindDialog::okClicked, this, &KTextEdit::slotDoFind); } d->findDlg->show(); } void KTextEdit::slotReplace() { if (document()->isEmpty()) { // saves having to track the text changes return; } if (d->repDlg) { KWindowSystem::activateWindow(d->repDlg->winId()); } else { d->repDlg = new KReplaceDialog(this, 0, QStringList(), QStringList(), false); connect(d->repDlg, &KFindDialog::okClicked, this, &KTextEdit::slotDoReplace); } d->repDlg->show(); } void KTextEdit::enableFindReplace(bool enabled) { d->findReplaceEnabled = enabled; } void KTextEdit::showTabAction(bool show) { d->showTabAction = show; } bool KTextEdit::Private::overrideShortcut(const QKeyEvent *event) { const int key = event->key() | event->modifiers(); if (KStandardShortcut::copy().contains(key)) { return true; } else if (KStandardShortcut::paste().contains(key)) { return true; } else if (KStandardShortcut::cut().contains(key)) { return true; } else if (KStandardShortcut::undo().contains(key)) { return true; } else if (KStandardShortcut::redo().contains(key)) { return true; } else if (KStandardShortcut::deleteWordBack().contains(key)) { return true; } else if (KStandardShortcut::deleteWordForward().contains(key)) { return true; } else if (KStandardShortcut::backwardWord().contains(key)) { return true; } else if (KStandardShortcut::forwardWord().contains(key)) { return true; } else if (KStandardShortcut::next().contains(key)) { return true; } else if (KStandardShortcut::prior().contains(key)) { return true; } else if (KStandardShortcut::begin().contains(key)) { return true; } else if (KStandardShortcut::end().contains(key)) { return true; } else if (KStandardShortcut::beginningOfLine().contains(key)) { return true; } else if (KStandardShortcut::endOfLine().contains(key)) { return true; } else if (KStandardShortcut::pasteSelection().contains(key)) { return true; } else if (findReplaceEnabled && KStandardShortcut::find().contains(key)) { return true; } else if (findReplaceEnabled && KStandardShortcut::findNext().contains(key)) { return true; } else if (findReplaceEnabled && KStandardShortcut::findPrev().contains(key)) { return true; } else if (findReplaceEnabled && KStandardShortcut::replace().contains(key)) { return true; } else if (event->matches(QKeySequence::SelectAll)) { // currently missing in QTextEdit return true; } return false; } void KTextEdit::keyPressEvent(QKeyEvent *event) { if (d->handleShortcut(event)) { event->accept(); } else { QTextEdit::keyPressEvent(event); } } void KTextEdit::showAutoCorrectButton(bool show) { d->showAutoCorrectionButton = show; } #include "moc_ktextedit.cpp"