find and replace dialog
This commit is contained in:
353
mainwindow.cpp
353
mainwindow.cpp
@@ -5,12 +5,25 @@
|
||||
#include "documentmanager.h"
|
||||
#include "appsettings.h"
|
||||
#include "editorviewhelper.h"
|
||||
#include "findreplacedialog.h"
|
||||
|
||||
#include <QActionGroup>
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QCloseEvent>
|
||||
#include <QComboBox>
|
||||
#include <QDialog>
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
#include <QPushButton>
|
||||
#include <QStringBuilder>
|
||||
#include <QStatusBar>
|
||||
#include <QTabWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
@@ -98,11 +111,18 @@ void MainWindow::setupActions()
|
||||
}, actionCollection());
|
||||
|
||||
// "Search" menu
|
||||
KStandardAction::find(this, [](){}, actionCollection());
|
||||
KStandardAction::findNext(this, [](){}, actionCollection());
|
||||
KStandardAction::findPrev(this, [](){}, actionCollection());
|
||||
KStandardAction::replace(this, [](){}, actionCollection());
|
||||
qDebug() << KStandardAction::name(KStandardAction::Replace);
|
||||
KStandardAction::find(this, [this](){
|
||||
showFindReplaceDialog(false);
|
||||
}, actionCollection());
|
||||
KStandardAction::findNext(this, [this](){
|
||||
findNextInCurrentEditor(true, true);
|
||||
}, actionCollection());
|
||||
KStandardAction::findPrev(this, [this](){
|
||||
findNextInCurrentEditor(false, true);
|
||||
}, actionCollection());
|
||||
KStandardAction::replace(this, [this](){
|
||||
showFindReplaceDialog(true);
|
||||
}, actionCollection());
|
||||
|
||||
// "Language" menu
|
||||
QAction *lexerNoneAction = new QAction(this);
|
||||
@@ -378,3 +398,326 @@ void MainWindow::updateActions()
|
||||
indentGuideAction->setChecked(m_indentGuidesEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::showFindReplaceDialog(bool showReplace)
|
||||
{
|
||||
if (!m_findReplaceDialog) {
|
||||
m_findReplaceDialog = new FindReplaceDialog(this);
|
||||
connect(m_findReplaceDialog, &FindReplaceDialog::findRequested, this, [this](bool forward) {
|
||||
findNextInCurrentEditor(forward, true);
|
||||
});
|
||||
connect(m_findReplaceDialog, &FindReplaceDialog::replaceRequested, this, [this]() {
|
||||
replaceOneInCurrentEditor(true);
|
||||
});
|
||||
connect(m_findReplaceDialog, &FindReplaceDialog::replaceAllRequested, this, [this]() {
|
||||
replaceAllInCurrentEditor(true);
|
||||
});
|
||||
}
|
||||
|
||||
if (SciEdit *editor = m_tabWidget->currentEditor()) {
|
||||
const int selStart = static_cast<int>(editor->selectionStart());
|
||||
const int selEnd = static_cast<int>(editor->selectionEnd());
|
||||
if (selStart != selEnd) {
|
||||
const int start = qMin(selStart, selEnd);
|
||||
const int end = qMax(selStart, selEnd);
|
||||
const QByteArray selected = editor->textRange(start, end);
|
||||
m_findReplaceDialog->setFindTextIfNotEmpty(QString::fromUtf8(selected));
|
||||
}
|
||||
}
|
||||
|
||||
if (showReplace) {
|
||||
m_findReplaceDialog->openReplace();
|
||||
} else {
|
||||
m_findReplaceDialog->openFind();
|
||||
}
|
||||
}
|
||||
|
||||
static sptr_t buildSearchFlags(const FindReplaceDialog *dialog)
|
||||
{
|
||||
sptr_t flags = 0;
|
||||
if (dialog->matchCase()) {
|
||||
flags |= SCFIND_MATCHCASE;
|
||||
}
|
||||
if (dialog->wholeWord()) {
|
||||
flags |= SCFIND_WHOLEWORD;
|
||||
}
|
||||
if (dialog->searchMode() == SearchMode::Regex) {
|
||||
flags |= SCFIND_REGEXP;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static QString buildFindPattern(const FindReplaceDialog *dialog)
|
||||
{
|
||||
return dialog->findTextForSearch();
|
||||
}
|
||||
|
||||
static QString buildReplacementText(const FindReplaceDialog *dialog)
|
||||
{
|
||||
return dialog->replaceTextForReplace();
|
||||
}
|
||||
|
||||
bool MainWindow::findNextInCurrentEditor(bool forward, bool showNotFoundMessage)
|
||||
{
|
||||
SciEdit *editor = m_tabWidget->currentEditor();
|
||||
if (!editor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_findReplaceDialog) {
|
||||
m_findReplaceDialog = new FindReplaceDialog(this);
|
||||
connect(m_findReplaceDialog, &FindReplaceDialog::findRequested, this, [this](bool forward) {
|
||||
findNextInCurrentEditor(forward, true);
|
||||
});
|
||||
connect(m_findReplaceDialog, &FindReplaceDialog::replaceRequested, this, [this]() {
|
||||
replaceOneInCurrentEditor(true);
|
||||
});
|
||||
connect(m_findReplaceDialog, &FindReplaceDialog::replaceAllRequested, this, [this]() {
|
||||
replaceAllInCurrentEditor(true);
|
||||
});
|
||||
}
|
||||
|
||||
const QString needle = buildFindPattern(m_findReplaceDialog);
|
||||
if (needle.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray needleBytes = needle.toUtf8();
|
||||
const sptr_t flags = buildSearchFlags(m_findReplaceDialog);
|
||||
|
||||
const int docLen = static_cast<int>(editor->textLength());
|
||||
int rangeStart = 0;
|
||||
int rangeEnd = docLen;
|
||||
|
||||
if (m_findReplaceDialog->inSelection()) {
|
||||
if (!m_findInSelectionEnabled) {
|
||||
const int selStart = static_cast<int>(editor->selectionStart());
|
||||
const int selEnd = static_cast<int>(editor->selectionEnd());
|
||||
if (selStart != selEnd) {
|
||||
m_findSelectionStart = qMin(selStart, selEnd);
|
||||
m_findSelectionEnd = qMax(selStart, selEnd);
|
||||
m_findInSelectionEnabled = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_findInSelectionEnabled = false;
|
||||
}
|
||||
|
||||
if (m_findInSelectionEnabled) {
|
||||
rangeStart = qBound(0, m_findSelectionStart, docLen);
|
||||
rangeEnd = qBound(0, m_findSelectionEnd, docLen);
|
||||
if (rangeStart >= rangeEnd) {
|
||||
m_findInSelectionEnabled = false;
|
||||
rangeStart = 0;
|
||||
rangeEnd = docLen;
|
||||
}
|
||||
}
|
||||
|
||||
const int selStartNow = static_cast<int>(editor->selectionStart());
|
||||
const int selEndNow = static_cast<int>(editor->selectionEnd());
|
||||
int caretBase = static_cast<int>(editor->currentPos());
|
||||
if (forward && selStartNow != selEndNow) {
|
||||
caretBase = qMax(selStartNow, selEndNow);
|
||||
}
|
||||
caretBase = qBound(rangeStart, caretBase, rangeEnd);
|
||||
|
||||
auto selectMatch = [&](int start, int end) {
|
||||
editor->setSel(start, end);
|
||||
editor->scrollCaret();
|
||||
};
|
||||
|
||||
auto showNotFound = [&]() {
|
||||
if (!showNotFoundMessage) {
|
||||
return;
|
||||
}
|
||||
QMessageBox::information(this, QStringLiteral("Find"), QStringLiteral("Text not found."));
|
||||
};
|
||||
|
||||
if (forward) {
|
||||
const QPair<int, int> found = editor->findText(static_cast<int>(flags), needleBytes.constData(), caretBase, rangeEnd);
|
||||
if (found.first >= 0) {
|
||||
selectMatch(found.first, found.second);
|
||||
return true;
|
||||
}
|
||||
if (m_findReplaceDialog->wrapAround()) {
|
||||
const QPair<int, int> wrapped = editor->findText(static_cast<int>(flags), needleBytes.constData(), rangeStart, rangeEnd);
|
||||
if (wrapped.first >= 0) {
|
||||
selectMatch(wrapped.first, wrapped.second);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
showNotFound();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto findLastBefore = [&](int endExclusive) -> QPair<int, int> {
|
||||
int bestStart = -1;
|
||||
int bestEnd = -1;
|
||||
int scanPos = rangeStart;
|
||||
while (scanPos < endExclusive) {
|
||||
const QPair<int, int> found = editor->findText(static_cast<int>(flags), needleBytes.constData(), scanPos, endExclusive);
|
||||
if (found.first < 0 || found.first >= endExclusive) {
|
||||
break;
|
||||
}
|
||||
bestStart = found.first;
|
||||
bestEnd = found.second;
|
||||
scanPos = found.first + 1;
|
||||
}
|
||||
return QPair<int, int>(bestStart, bestEnd);
|
||||
};
|
||||
|
||||
QPair<int, int> found = findLastBefore(caretBase);
|
||||
if (found.first >= 0) {
|
||||
selectMatch(found.first, found.second);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_findReplaceDialog->wrapAround()) {
|
||||
found = findLastBefore(rangeEnd);
|
||||
if (found.first >= 0) {
|
||||
selectMatch(found.first, found.second);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
showNotFound();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MainWindow::replaceOneInCurrentEditor(bool showNotFoundMessage)
|
||||
{
|
||||
SciEdit *editor = m_tabWidget->currentEditor();
|
||||
if (!editor) {
|
||||
return false;
|
||||
}
|
||||
if (!m_findReplaceDialog) {
|
||||
m_findReplaceDialog = new FindReplaceDialog(this);
|
||||
connect(m_findReplaceDialog, &FindReplaceDialog::findRequested, this, [this](bool forward) {
|
||||
findNextInCurrentEditor(forward, true);
|
||||
});
|
||||
connect(m_findReplaceDialog, &FindReplaceDialog::replaceRequested, this, [this]() {
|
||||
replaceOneInCurrentEditor(true);
|
||||
});
|
||||
connect(m_findReplaceDialog, &FindReplaceDialog::replaceAllRequested, this, [this]() {
|
||||
replaceAllInCurrentEditor(true);
|
||||
});
|
||||
}
|
||||
|
||||
const QString needle = buildFindPattern(m_findReplaceDialog);
|
||||
if (needle.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!findNextInCurrentEditor(true, showNotFoundMessage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString replacement = buildReplacementText(m_findReplaceDialog);
|
||||
const QByteArray replacementBytes = replacement.toUtf8();
|
||||
|
||||
editor->targetFromSelection();
|
||||
if (m_findReplaceDialog->searchMode() == SearchMode::Regex) {
|
||||
editor->replaceTargetRE(replacementBytes.size(), replacementBytes.constData());
|
||||
} else {
|
||||
editor->replaceTarget(replacementBytes.size(), replacementBytes.constData());
|
||||
}
|
||||
|
||||
findNextInCurrentEditor(true, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
int MainWindow::replaceAllInCurrentEditor(bool showNotFoundMessage)
|
||||
{
|
||||
SciEdit *editor = m_tabWidget->currentEditor();
|
||||
if (!editor) {
|
||||
return 0;
|
||||
}
|
||||
if (!m_findReplaceDialog) {
|
||||
m_findReplaceDialog = new FindReplaceDialog(this);
|
||||
connect(m_findReplaceDialog, &FindReplaceDialog::findRequested, this, [this](bool forward) {
|
||||
findNextInCurrentEditor(forward, true);
|
||||
});
|
||||
connect(m_findReplaceDialog, &FindReplaceDialog::replaceRequested, this, [this]() {
|
||||
replaceOneInCurrentEditor(true);
|
||||
});
|
||||
connect(m_findReplaceDialog, &FindReplaceDialog::replaceAllRequested, this, [this]() {
|
||||
replaceAllInCurrentEditor(true);
|
||||
});
|
||||
}
|
||||
|
||||
const QString needle = buildFindPattern(m_findReplaceDialog);
|
||||
if (needle.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const QByteArray needleBytes = needle.toUtf8();
|
||||
const QString replacement = buildReplacementText(m_findReplaceDialog);
|
||||
const QByteArray replacementBytes = replacement.toUtf8();
|
||||
const sptr_t flags = buildSearchFlags(m_findReplaceDialog);
|
||||
|
||||
const int docLen = static_cast<int>(editor->textLength());
|
||||
int rangeStart = 0;
|
||||
int rangeEnd = docLen;
|
||||
|
||||
if (m_findReplaceDialog->inSelection()) {
|
||||
if (!m_findInSelectionEnabled) {
|
||||
const int selStart = static_cast<int>(editor->selectionStart());
|
||||
const int selEnd = static_cast<int>(editor->selectionEnd());
|
||||
if (selStart != selEnd) {
|
||||
m_findSelectionStart = qMin(selStart, selEnd);
|
||||
m_findSelectionEnd = qMax(selStart, selEnd);
|
||||
m_findInSelectionEnabled = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_findInSelectionEnabled = false;
|
||||
}
|
||||
|
||||
if (m_findInSelectionEnabled) {
|
||||
rangeStart = qBound(0, m_findSelectionStart, docLen);
|
||||
rangeEnd = qBound(0, m_findSelectionEnd, docLen);
|
||||
if (rangeStart >= rangeEnd) {
|
||||
m_findInSelectionEnabled = false;
|
||||
rangeStart = 0;
|
||||
rangeEnd = docLen;
|
||||
}
|
||||
}
|
||||
|
||||
editor->setSearchFlags(flags);
|
||||
|
||||
int replacedCount = 0;
|
||||
int start = rangeStart;
|
||||
int end = rangeEnd;
|
||||
|
||||
editor->beginUndoAction();
|
||||
while (start <= end) {
|
||||
editor->setTargetRange(start, end);
|
||||
const sptr_t pos = editor->searchInTarget(needleBytes.size(), needleBytes.constData());
|
||||
if (pos < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const int matchStart = static_cast<int>(editor->targetStart());
|
||||
const int matchEnd = static_cast<int>(editor->targetEnd());
|
||||
const int matchLen = matchEnd - matchStart;
|
||||
|
||||
sptr_t replacedLen = 0;
|
||||
if (m_findReplaceDialog->searchMode() == SearchMode::Regex) {
|
||||
replacedLen = editor->replaceTargetRE(replacementBytes.size(), replacementBytes.constData());
|
||||
} else {
|
||||
replacedLen = editor->replaceTarget(replacementBytes.size(), replacementBytes.constData());
|
||||
}
|
||||
|
||||
++replacedCount;
|
||||
const int delta = static_cast<int>(replacedLen) - matchLen;
|
||||
end += delta;
|
||||
start = matchStart + static_cast<int>(replacedLen);
|
||||
}
|
||||
editor->endUndoAction();
|
||||
|
||||
if (replacedCount == 0 && showNotFoundMessage) {
|
||||
QMessageBox::information(this, QStringLiteral("Replace"), QStringLiteral("Text not found."));
|
||||
}
|
||||
|
||||
return replacedCount;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user