mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
Editor: refine find logics
This commit is contained in:
parent
f3e4f370dd
commit
647807a918
@ -307,3 +307,8 @@ void VFindReplaceDialog::updateState(DocType p_docType, bool p_editMode)
|
|||||||
|
|
||||||
m_replaceAvailable = p_editMode;
|
m_replaceAvailable = p_editMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString VFindReplaceDialog::textToFind() const
|
||||||
|
{
|
||||||
|
return m_findEdit->text();
|
||||||
|
}
|
||||||
|
@ -14,11 +14,17 @@ class VFindReplaceDialog : public QWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit VFindReplaceDialog(QWidget *p_parent = 0);
|
explicit VFindReplaceDialog(QWidget *p_parent = 0);
|
||||||
|
|
||||||
|
uint options() const;
|
||||||
|
|
||||||
void setOption(FindOption p_opt, bool p_enabled);
|
void setOption(FindOption p_opt, bool p_enabled);
|
||||||
|
|
||||||
// Update the options enabled/disabled state according to current
|
// Update the options enabled/disabled state according to current
|
||||||
// edit tab.
|
// edit tab.
|
||||||
void updateState(DocType p_docType, bool p_editMode);
|
void updateState(DocType p_docType, bool p_editMode);
|
||||||
|
|
||||||
|
QString textToFind() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void dialogClosed();
|
void dialogClosed();
|
||||||
void findTextChanged(const QString &p_text, uint p_options);
|
void findTextChanged(const QString &p_text, uint p_options);
|
||||||
@ -68,4 +74,8 @@ private:
|
|||||||
QCheckBox *m_incrementalSearchCheck;
|
QCheckBox *m_incrementalSearchCheck;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline uint VFindReplaceDialog::options() const
|
||||||
|
{
|
||||||
|
return m_options;
|
||||||
|
}
|
||||||
#endif // VFINDREPLACEDIALOG_H
|
#endif // VFINDREPLACEDIALOG_H
|
||||||
|
@ -25,6 +25,8 @@ Open Flash Page.
|
|||||||
Edit current note or save changes and exit edit mode.
|
Edit current note or save changes and exit edit mode.
|
||||||
- `Ctrl+G`
|
- `Ctrl+G`
|
||||||
Activate Universal Entry.
|
Activate Universal Entry.
|
||||||
|
- `Ctrl+8`/`Ctrl+9`
|
||||||
|
Jump to the next/previous match in last find action.
|
||||||
|
|
||||||
### Read Mode
|
### Read Mode
|
||||||
- `H`/`J`/`K`/`L`
|
- `H`/`J`/`K`/`L`
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
编辑当前笔记或保存更改并退出编辑模式。
|
编辑当前笔记或保存更改并退出编辑模式。
|
||||||
- `Ctrl+G`
|
- `Ctrl+G`
|
||||||
激活通用入口。
|
激活通用入口。
|
||||||
|
- `Ctrl+8`/`Ctrl+9`
|
||||||
|
跳转到最近一次查找的下一个/上一个匹配。
|
||||||
|
|
||||||
### 阅读模式
|
### 阅读模式
|
||||||
- `H`/`J`/`K`/`L`
|
- `H`/`J`/`K`/`L`
|
||||||
|
@ -391,6 +391,10 @@ Find=Ctrl+F
|
|||||||
FindNext=F3
|
FindNext=F3
|
||||||
; Find previous occurence
|
; Find previous occurence
|
||||||
FindPrevious=Shift+F3
|
FindPrevious=Shift+F3
|
||||||
|
; Jump to next match of last find
|
||||||
|
NextMatch=Ctrl+8
|
||||||
|
; Jump to previous match of last find
|
||||||
|
PreviousMatch=Ctrl+9
|
||||||
; Advanced find
|
; Advanced find
|
||||||
AdvancedFind=Ctrl+Alt+F
|
AdvancedFind=Ctrl+Alt+F
|
||||||
; Recover last closed file
|
; Recover last closed file
|
||||||
|
@ -35,29 +35,7 @@ VEditArea::VEditArea(QWidget *parent)
|
|||||||
|
|
||||||
registerCaptainTargets();
|
registerCaptainTargets();
|
||||||
|
|
||||||
QString keySeq = g_config->getShortcutKeySequence("ActivateNextTab");
|
initShortcuts();
|
||||||
qDebug() << "set ActivateNextTab shortcut to" << keySeq;
|
|
||||||
QShortcut *activateNextTab = new QShortcut(QKeySequence(keySeq), this);
|
|
||||||
activateNextTab->setContext(Qt::ApplicationShortcut);
|
|
||||||
connect(activateNextTab, &QShortcut::activated,
|
|
||||||
this, [this]() {
|
|
||||||
VEditWindow *win = getCurrentWindow();
|
|
||||||
if (win) {
|
|
||||||
win->focusNextTab(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
keySeq = g_config->getShortcutKeySequence("ActivatePreviousTab");
|
|
||||||
qDebug() << "set ActivatePreviousTab shortcut to" << keySeq;
|
|
||||||
QShortcut *activatePreviousTab = new QShortcut(QKeySequence(keySeq), this);
|
|
||||||
activatePreviousTab->setContext(Qt::ApplicationShortcut);
|
|
||||||
connect(activatePreviousTab, &QShortcut::activated,
|
|
||||||
this, [this]() {
|
|
||||||
VEditWindow *win = getCurrentWindow();
|
|
||||||
if (win) {
|
|
||||||
win->focusNextTab(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
QTimer *timer = new QTimer(this);
|
QTimer *timer = new QTimer(this);
|
||||||
timer->setSingleShot(false);
|
timer->setSingleShot(false);
|
||||||
@ -130,6 +108,49 @@ void VEditArea::setupUI()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VEditArea::initShortcuts()
|
||||||
|
{
|
||||||
|
QString keySeq = g_config->getShortcutKeySequence("ActivateNextTab");
|
||||||
|
qDebug() << "set ActivateNextTab shortcut to" << keySeq;
|
||||||
|
QShortcut *activateNextTab = new QShortcut(QKeySequence(keySeq), this);
|
||||||
|
activateNextTab->setContext(Qt::ApplicationShortcut);
|
||||||
|
connect(activateNextTab, &QShortcut::activated,
|
||||||
|
this, [this]() {
|
||||||
|
VEditWindow *win = getCurrentWindow();
|
||||||
|
if (win) {
|
||||||
|
win->focusNextTab(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
keySeq = g_config->getShortcutKeySequence("ActivatePreviousTab");
|
||||||
|
qDebug() << "set ActivatePreviousTab shortcut to" << keySeq;
|
||||||
|
QShortcut *activatePreviousTab = new QShortcut(QKeySequence(keySeq), this);
|
||||||
|
activatePreviousTab->setContext(Qt::ApplicationShortcut);
|
||||||
|
connect(activatePreviousTab, &QShortcut::activated,
|
||||||
|
this, [this]() {
|
||||||
|
VEditWindow *win = getCurrentWindow();
|
||||||
|
if (win) {
|
||||||
|
win->focusNextTab(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
keySeq = g_config->getShortcutKeySequence("NextMatch");
|
||||||
|
qDebug() << "set NextMatch shortcut to" << keySeq;
|
||||||
|
QShortcut *nextMatchSC = new QShortcut(QKeySequence(keySeq), this);
|
||||||
|
connect(nextMatchSC, &QShortcut::activated,
|
||||||
|
this, [this]() {
|
||||||
|
nextMatch(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
keySeq = g_config->getShortcutKeySequence("PreviousMatch");
|
||||||
|
qDebug() << "set PreviousMatch shortcut to" << keySeq;
|
||||||
|
QShortcut *previousMatchSC = new QShortcut(QKeySequence(keySeq), this);
|
||||||
|
connect(previousMatchSC, &QShortcut::activated,
|
||||||
|
this, [this]() {
|
||||||
|
nextMatch(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void VEditArea::insertSplitWindow(int idx)
|
void VEditArea::insertSplitWindow(int idx)
|
||||||
{
|
{
|
||||||
VEditWindow *win = new VEditWindow(this);
|
VEditWindow *win = new VEditWindow(this);
|
||||||
@ -1277,3 +1298,17 @@ void VEditArea::distributeSplits()
|
|||||||
|
|
||||||
splitter->setSizes(sizes);
|
splitter->setSizes(sizes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VEditArea::nextMatch(bool p_forward)
|
||||||
|
{
|
||||||
|
VEditTab *tab = getCurrentTab();
|
||||||
|
if (!tab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(m_findReplace);
|
||||||
|
|
||||||
|
tab->nextMatch(m_findReplace->textToFind(),
|
||||||
|
m_findReplace->options(),
|
||||||
|
p_forward);
|
||||||
|
}
|
||||||
|
@ -179,9 +179,16 @@ private slots:
|
|||||||
// Handle the timeout signal of file timer.
|
// Handle the timeout signal of file timer.
|
||||||
void handleFileTimerTimeout();
|
void handleFileTimerTimeout();
|
||||||
|
|
||||||
|
// Jump to next match of last find.
|
||||||
|
void nextMatch(bool p_forward);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUI();
|
void setupUI();
|
||||||
|
|
||||||
|
void initShortcuts();
|
||||||
|
|
||||||
QVector<QPair<int, int> > findTabsByFile(const VFile *p_file);
|
QVector<QPair<int, int> > findTabsByFile(const VFile *p_file);
|
||||||
|
|
||||||
int openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_mode);
|
int openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_mode);
|
||||||
void setCurrentTab(int windowIndex, int tabIndex, bool setFocus);
|
void setCurrentTab(int windowIndex, int tabIndex, bool setFocus);
|
||||||
void setCurrentWindow(int windowIndex, bool setFocus);
|
void setCurrentWindow(int windowIndex, bool setFocus);
|
||||||
|
315
src/veditor.cpp
315
src/veditor.cpp
@ -56,6 +56,8 @@ void VEditor::init()
|
|||||||
const int labelSize = 64;
|
const int labelSize = 64;
|
||||||
|
|
||||||
m_document = documentW();
|
m_document = documentW();
|
||||||
|
QObject::connect(m_document, &QTextDocument::contentsChanged,
|
||||||
|
m_object, &VEditorObject::clearFindCache);
|
||||||
|
|
||||||
m_selectedWordFg = QColor(g_config->getEditorSelectedWordFg());
|
m_selectedWordFg = QColor(g_config->getEditorSelectedWordFg());
|
||||||
m_selectedWordBg = QColor(g_config->getEditorSelectedWordBg());
|
m_selectedWordBg = QColor(g_config->getEditorSelectedWordBg());
|
||||||
@ -344,7 +346,10 @@ static QTextDocument::FindFlags findOptionsToFlags(uint p_options, bool p_forwar
|
|||||||
return findFlags;
|
return findFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QTextCursor> VEditor::findTextAll(const QString &p_text, uint p_options)
|
QList<QTextCursor> VEditor::findTextAll(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
int p_start,
|
||||||
|
int p_end)
|
||||||
{
|
{
|
||||||
QList<QTextCursor> results;
|
QList<QTextCursor> results;
|
||||||
if (p_text.isEmpty()) {
|
if (p_text.isEmpty()) {
|
||||||
@ -355,35 +360,37 @@ QList<QTextCursor> VEditor::findTextAll(const QString &p_text, uint p_options)
|
|||||||
bool caseSensitive = p_options & FindOption::CaseSensitive;
|
bool caseSensitive = p_options & FindOption::CaseSensitive;
|
||||||
QTextDocument::FindFlags findFlags = findOptionsToFlags(p_options, true);
|
QTextDocument::FindFlags findFlags = findOptionsToFlags(p_options, true);
|
||||||
|
|
||||||
// Use regular expression
|
if (p_options & FindOption::RegularExpression) {
|
||||||
bool useRegExp = p_options & FindOption::RegularExpression;
|
QRegExp exp(p_text,
|
||||||
QRegExp exp;
|
|
||||||
if (useRegExp) {
|
|
||||||
useRegExp = true;
|
|
||||||
exp = QRegExp(p_text,
|
|
||||||
caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
|
caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
|
||||||
}
|
results = findTextAllInRange(m_document, exp, findFlags, p_start, p_end);
|
||||||
|
|
||||||
int startPos = 0;
|
|
||||||
QTextCursor cursor;
|
|
||||||
while (true) {
|
|
||||||
if (useRegExp) {
|
|
||||||
cursor = m_document->find(exp, startPos, findFlags);
|
|
||||||
} else {
|
} else {
|
||||||
cursor = m_document->find(p_text, startPos, findFlags);
|
results = findTextAllInRange(m_document, p_text, findFlags, p_start, p_end);
|
||||||
}
|
|
||||||
|
|
||||||
if (cursor.isNull()) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
results.append(cursor);
|
|
||||||
startPos = cursor.selectionEnd();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QList<QTextCursor> &VEditor::findTextAllCached(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
int p_start,
|
||||||
|
int p_end)
|
||||||
|
{
|
||||||
|
if (p_text.isEmpty()) {
|
||||||
|
m_findInfo.clear();
|
||||||
|
return m_findInfo.m_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_findInfo.isCached(p_text, p_options, p_start, p_end)) {
|
||||||
|
return m_findInfo.m_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QTextCursor> result = findTextAll(p_text, p_options, p_start, p_end);
|
||||||
|
m_findInfo.update(p_text, p_options, p_start, p_end, result);
|
||||||
|
|
||||||
|
return m_findInfo.m_result;
|
||||||
|
}
|
||||||
|
|
||||||
void VEditor::highlightSelectedWord()
|
void VEditor::highlightSelectedWord()
|
||||||
{
|
{
|
||||||
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SelectedWord];
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SelectedWord];
|
||||||
@ -521,72 +528,146 @@ bool VEditor::peekText(const QString &p_text, uint p_options, bool p_forward)
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @p_cursors is in ascending order.
|
||||||
|
// If @p_forward is true, find the smallest cursor whose selection start is greater
|
||||||
|
// than @p_pos or the first cursor if wrapped.
|
||||||
|
// Otherwise, find the largest cursor whose selection start is smaller than @p_pos
|
||||||
|
// or the last cursor if wrapped.
|
||||||
|
static int selectCursor(const QList<QTextCursor> &p_cursors,
|
||||||
|
int p_pos,
|
||||||
|
bool p_forward,
|
||||||
|
bool &p_wrapped)
|
||||||
|
{
|
||||||
|
Q_ASSERT(!p_cursors.isEmpty());
|
||||||
|
|
||||||
|
p_wrapped = false;
|
||||||
|
|
||||||
|
int first = 0, last = p_cursors.size() - 1;
|
||||||
|
int lastMatch = -1;
|
||||||
|
while (first <= last) {
|
||||||
|
int mid = (first + last) / 2;
|
||||||
|
const QTextCursor &cur = p_cursors.at(mid);
|
||||||
|
if (p_forward) {
|
||||||
|
if (cur.selectionStart() < p_pos) {
|
||||||
|
first = mid + 1;
|
||||||
|
} else if (cur.selectionStart() == p_pos) {
|
||||||
|
// Next one is the right one.
|
||||||
|
if (mid < p_cursors.size() - 1) {
|
||||||
|
lastMatch = mid + 1;
|
||||||
|
} else {
|
||||||
|
lastMatch = 0;
|
||||||
|
p_wrapped = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// It is a match.
|
||||||
|
if (lastMatch == -1 || mid < lastMatch) {
|
||||||
|
lastMatch = mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = mid - 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cur.selectionStart() > p_pos) {
|
||||||
|
last = mid - 1;
|
||||||
|
} else if (cur.selectionStart() == p_pos) {
|
||||||
|
// Previous one is the right one.
|
||||||
|
if (mid > 0) {
|
||||||
|
lastMatch = mid - 1;
|
||||||
|
} else {
|
||||||
|
lastMatch = p_cursors.size() - 1;
|
||||||
|
p_wrapped = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// It is a match.
|
||||||
|
if (lastMatch == -1 || mid > lastMatch) {
|
||||||
|
lastMatch = mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
first = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastMatch == -1) {
|
||||||
|
p_wrapped = true;
|
||||||
|
lastMatch = p_forward ? 0 : (p_cursors.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastMatch;
|
||||||
|
}
|
||||||
|
|
||||||
bool VEditor::findText(const QString &p_text,
|
bool VEditor::findText(const QString &p_text,
|
||||||
uint p_options,
|
uint p_options,
|
||||||
bool p_forward,
|
bool p_forward,
|
||||||
QTextCursor *p_cursor,
|
QTextCursor *p_cursor,
|
||||||
QTextCursor::MoveMode p_moveMode,
|
QTextCursor::MoveMode p_moveMode,
|
||||||
bool p_useLeftSideOfCursor)
|
bool p_useLeftSideOfCursor)
|
||||||
|
{
|
||||||
|
return findTextInRange(p_text,
|
||||||
|
p_options,
|
||||||
|
p_forward,
|
||||||
|
p_cursor,
|
||||||
|
p_moveMode,
|
||||||
|
p_useLeftSideOfCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VEditor::findTextInRange(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
bool p_forward,
|
||||||
|
QTextCursor *p_cursor,
|
||||||
|
QTextCursor::MoveMode p_moveMode,
|
||||||
|
bool p_useLeftSideOfCursor,
|
||||||
|
int p_start,
|
||||||
|
int p_end)
|
||||||
{
|
{
|
||||||
clearIncrementalSearchedWordHighlight();
|
clearIncrementalSearchedWordHighlight();
|
||||||
|
|
||||||
if (p_text.isEmpty()) {
|
if (p_text.isEmpty()) {
|
||||||
|
m_findInfo.clear();
|
||||||
clearSearchedWordHighlight();
|
clearSearchedWordHighlight();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextCursor cursor = textCursorW();
|
const QList<QTextCursor> &result = findTextAllCached(p_text, p_options, p_start, p_end);
|
||||||
bool wrapped = false;
|
|
||||||
QTextCursor retCursor;
|
|
||||||
int matches = 0;
|
|
||||||
int start = p_cursor ? p_cursor->position() : cursor.position();
|
|
||||||
if (p_useLeftSideOfCursor) {
|
|
||||||
--start;
|
|
||||||
}
|
|
||||||
int skipPosition = start;
|
|
||||||
|
|
||||||
bool found = false;
|
if (result.isEmpty()) {
|
||||||
while (true) {
|
clearSearchedWordHighlight();
|
||||||
found = findTextHelper(p_text, p_options, p_forward, start, wrapped, retCursor);
|
|
||||||
if (found) {
|
emit m_object->statusMessage(QObject::tr("No match found"));
|
||||||
Q_ASSERT(!retCursor.isNull());
|
} else {
|
||||||
|
// Locate to the right match and update current cursor.
|
||||||
|
QTextCursor cursor = textCursorW();
|
||||||
|
int pos = p_cursor ? p_cursor->position() : cursor.position();
|
||||||
|
if (p_useLeftSideOfCursor) {
|
||||||
|
--pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wrapped = false;
|
||||||
|
int idx = selectCursor(result, pos, p_forward, wrapped);
|
||||||
|
const QTextCursor &tcursor = result.at(idx);
|
||||||
if (wrapped) {
|
if (wrapped) {
|
||||||
showWrapLabel();
|
showWrapLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_forward && retCursor.selectionStart() == skipPosition) {
|
|
||||||
// Skip the first match.
|
|
||||||
skipPosition = -1;
|
|
||||||
start = retCursor.selectionEnd();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_cursor) {
|
if (p_cursor) {
|
||||||
p_cursor->setPosition(retCursor.selectionStart(), p_moveMode);
|
p_cursor->setPosition(tcursor.selectionStart(), p_moveMode);
|
||||||
} else {
|
} else {
|
||||||
cursor.setPosition(retCursor.selectionStart(), p_moveMode);
|
cursor.setPosition(tcursor.selectionStart(), p_moveMode);
|
||||||
setTextCursorW(cursor);
|
setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
highlightSearchedWord(p_text, p_options);
|
highlightSearchedWord(result);
|
||||||
highlightSearchedWordUnderCursor(retCursor);
|
|
||||||
matches = m_extraSelections[(int)SelectionId::SearchedKeyword].size();
|
highlightSearchedWordUnderCursor(tcursor);
|
||||||
} else {
|
|
||||||
clearSearchedWordHighlight();
|
emit m_object->statusMessage(QObject::tr("Match found: %2 of %3")
|
||||||
|
.arg(idx + 1)
|
||||||
|
.arg(result.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
return !result.isEmpty();
|
||||||
}
|
|
||||||
|
|
||||||
if (matches == 0) {
|
|
||||||
emit m_object->statusMessage(QObject::tr("Found no match"));
|
|
||||||
} else {
|
|
||||||
emit m_object->statusMessage(QObject::tr("Found %1 %2").arg(matches)
|
|
||||||
.arg(matches > 1 ? QObject::tr("matches")
|
|
||||||
: QObject::tr("match")));
|
|
||||||
}
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VEditor::findTextOne(const QString &p_text, uint p_options, bool p_forward)
|
bool VEditor::findTextOne(const QString &p_text, uint p_options, bool p_forward)
|
||||||
@ -626,10 +707,14 @@ bool VEditor::findTextInRange(const QString &p_text,
|
|||||||
int p_start,
|
int p_start,
|
||||||
int p_end)
|
int p_end)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p_start);
|
return findTextInRange(p_text,
|
||||||
Q_UNUSED(p_end);
|
p_options,
|
||||||
// TODO
|
p_forward,
|
||||||
return findText(p_text, p_options, p_forward);
|
nullptr,
|
||||||
|
QTextCursor::MoveAnchor,
|
||||||
|
false,
|
||||||
|
p_start,
|
||||||
|
p_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditor::highlightIncrementalSearchedWord(const QTextCursor &p_cursor)
|
void VEditor::highlightIncrementalSearchedWord(const QTextCursor &p_cursor)
|
||||||
@ -791,10 +876,10 @@ void VEditor::showWrapLabel()
|
|||||||
m_labelTimer->start();
|
m_labelTimer->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditor::highlightSearchedWord(const QString &p_text, uint p_options)
|
void VEditor::highlightSearchedWord(const QList<QTextCursor> &p_matches)
|
||||||
{
|
{
|
||||||
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
|
||||||
if (!g_config->getHighlightSearchedWord() || p_text.isEmpty()) {
|
if (!g_config->getHighlightSearchedWord() || p_matches.isEmpty()) {
|
||||||
if (!selects.isEmpty()) {
|
if (!selects.isEmpty()) {
|
||||||
selects.clear();
|
selects.clear();
|
||||||
highlightExtraSelections(true);
|
highlightExtraSelections(true);
|
||||||
@ -803,10 +888,20 @@ void VEditor::highlightSearchedWord(const QString &p_text, uint p_options)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selects.clear();
|
||||||
|
|
||||||
QTextCharFormat format;
|
QTextCharFormat format;
|
||||||
format.setForeground(m_searchedWordFg);
|
format.setForeground(m_searchedWordFg);
|
||||||
format.setBackground(m_searchedWordBg);
|
format.setBackground(m_searchedWordBg);
|
||||||
highlightTextAll(p_text, p_options, SelectionId::SearchedKeyword, format);
|
|
||||||
|
for (int i = 0; i < p_matches.size(); ++i) {
|
||||||
|
QTextEdit::ExtraSelection select;
|
||||||
|
select.format = format;
|
||||||
|
select.cursor = p_matches[i];
|
||||||
|
selects.append(select);
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightExtraSelections();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditor::highlightSearchedWordUnderCursor(const QTextCursor &p_cursor)
|
void VEditor::highlightSearchedWordUnderCursor(const QTextCursor &p_cursor)
|
||||||
@ -1297,3 +1392,83 @@ void VEditor::insertCompletion(const QString &p_prefix, const QString &p_complet
|
|||||||
|
|
||||||
setTextCursorW(cursor);
|
setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QTextCursor> VEditor::findTextAllInRange(const QTextDocument *p_doc,
|
||||||
|
const QString &p_text,
|
||||||
|
QTextDocument::FindFlags p_flags,
|
||||||
|
int p_start,
|
||||||
|
int p_end)
|
||||||
|
{
|
||||||
|
QList<QTextCursor> results;
|
||||||
|
if (p_text.isEmpty()) {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start = p_start;
|
||||||
|
int end = p_end == -1 ? p_doc->characterCount() + 1 : p_end;
|
||||||
|
|
||||||
|
while (start < end) {
|
||||||
|
QTextCursor cursor = p_doc->find(p_text, start, p_flags);
|
||||||
|
if (cursor.isNull()) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
start = cursor.selectionEnd();
|
||||||
|
if (start <= end) {
|
||||||
|
results.append(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QTextCursor> VEditor::findTextAllInRange(const QTextDocument *p_doc,
|
||||||
|
const QRegExp &p_reg,
|
||||||
|
QTextDocument::FindFlags p_flags,
|
||||||
|
int p_start,
|
||||||
|
int p_end)
|
||||||
|
{
|
||||||
|
QList<QTextCursor> results;
|
||||||
|
if (!p_reg.isValid()) {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start = p_start;
|
||||||
|
int end = p_end == -1 ? p_doc->characterCount() + 1 : p_end;
|
||||||
|
|
||||||
|
while (start < end) {
|
||||||
|
QTextCursor cursor = p_doc->find(p_reg, start, p_flags);
|
||||||
|
if (cursor.isNull()) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
start = cursor.selectionEnd();
|
||||||
|
if (start <= end) {
|
||||||
|
results.append(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::clearFindCache()
|
||||||
|
{
|
||||||
|
m_findInfo.clearResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::nextMatch(bool p_forward)
|
||||||
|
{
|
||||||
|
if (m_findInfo.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_findInfo.m_useToken) {
|
||||||
|
// TODO
|
||||||
|
} else {
|
||||||
|
findTextInRange(m_findInfo.m_text,
|
||||||
|
m_findInfo.m_options,
|
||||||
|
p_forward,
|
||||||
|
m_findInfo.m_start,
|
||||||
|
m_findInfo.m_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
141
src/veditor.h
141
src/veditor.h
@ -13,6 +13,7 @@
|
|||||||
#include "vfile.h"
|
#include "vfile.h"
|
||||||
#include "vwordcountinfo.h"
|
#include "vwordcountinfo.h"
|
||||||
#include "vtexteditcompleter.h"
|
#include "vtexteditcompleter.h"
|
||||||
|
#include "vsearchconfig.h"
|
||||||
|
|
||||||
class QWidget;
|
class QWidget;
|
||||||
class VEditorObject;
|
class VEditorObject;
|
||||||
@ -99,6 +100,9 @@ public:
|
|||||||
uint p_options,
|
uint p_options,
|
||||||
const QString &p_replaceText);
|
const QString &p_replaceText);
|
||||||
|
|
||||||
|
// Use m_findInfo to find next match.
|
||||||
|
void nextMatch(bool p_forward = false);
|
||||||
|
|
||||||
// Scroll the content to make @p_block visible.
|
// Scroll the content to make @p_block visible.
|
||||||
// If the @p_block is too long to hold in one page, just let it occupy the
|
// If the @p_block is too long to hold in one page, just let it occupy the
|
||||||
// whole page.
|
// whole page.
|
||||||
@ -276,6 +280,100 @@ protected:
|
|||||||
private:
|
private:
|
||||||
friend class VEditorObject;
|
friend class VEditorObject;
|
||||||
|
|
||||||
|
// Info about one find-in-page.
|
||||||
|
struct FindInfo
|
||||||
|
{
|
||||||
|
FindInfo()
|
||||||
|
: m_start(0),
|
||||||
|
m_end(-1),
|
||||||
|
m_useToken(false),
|
||||||
|
m_options(0),
|
||||||
|
m_cacheValid(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_start = 0;
|
||||||
|
m_end = -1;
|
||||||
|
|
||||||
|
m_useToken = false;
|
||||||
|
|
||||||
|
m_text.clear();
|
||||||
|
m_options = 0;
|
||||||
|
|
||||||
|
m_token.clear();
|
||||||
|
|
||||||
|
m_cacheValid = false;
|
||||||
|
m_result.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearResult()
|
||||||
|
{
|
||||||
|
m_cacheValid = false;
|
||||||
|
m_result.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCached(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
int p_start = 0,
|
||||||
|
int p_end = -1) const
|
||||||
|
{
|
||||||
|
return m_cacheValid
|
||||||
|
&& !m_useToken
|
||||||
|
&& m_text == p_text
|
||||||
|
&& m_options == p_options
|
||||||
|
&& m_start == p_start
|
||||||
|
&& m_end == p_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
int p_start,
|
||||||
|
int p_end,
|
||||||
|
const QList<QTextCursor> &p_result)
|
||||||
|
{
|
||||||
|
m_start = p_start;
|
||||||
|
m_end = p_end;
|
||||||
|
|
||||||
|
m_useToken = false;
|
||||||
|
|
||||||
|
m_text = p_text;
|
||||||
|
m_options = p_options;
|
||||||
|
|
||||||
|
m_cacheValid = true;
|
||||||
|
m_result = p_result;
|
||||||
|
|
||||||
|
m_token.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNull() const
|
||||||
|
{
|
||||||
|
if (m_useToken) {
|
||||||
|
return m_token.tokenSize() == 0;
|
||||||
|
} else {
|
||||||
|
return m_text.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find in [m_start, m_end).
|
||||||
|
int m_start;
|
||||||
|
int m_end;
|
||||||
|
|
||||||
|
bool m_useToken;
|
||||||
|
|
||||||
|
// Use text and options to search.
|
||||||
|
QString m_text;
|
||||||
|
uint m_options;
|
||||||
|
|
||||||
|
// Use token to search.
|
||||||
|
VSearchToken m_token;
|
||||||
|
|
||||||
|
bool m_cacheValid;
|
||||||
|
|
||||||
|
QList<QTextCursor> m_result;
|
||||||
|
};
|
||||||
|
|
||||||
// Filter out the trailing space right before cursor.
|
// Filter out the trailing space right before cursor.
|
||||||
void filterTrailingSpace(QList<QTextEdit::ExtraSelection> &p_selects,
|
void filterTrailingSpace(QList<QTextEdit::ExtraSelection> &p_selects,
|
||||||
const QList<QTextEdit::ExtraSelection> &p_src);
|
const QList<QTextEdit::ExtraSelection> &p_src);
|
||||||
@ -293,7 +391,15 @@ private:
|
|||||||
QList<QTextEdit::ExtraSelection> &) = NULL);
|
QList<QTextEdit::ExtraSelection> &) = NULL);
|
||||||
|
|
||||||
// Find all the occurences of @p_text.
|
// Find all the occurences of @p_text.
|
||||||
QList<QTextCursor> findTextAll(const QString &p_text, uint p_options);
|
QList<QTextCursor> findTextAll(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
int p_start = 0,
|
||||||
|
int p_end = -1);
|
||||||
|
|
||||||
|
const QList<QTextCursor> &findTextAllCached(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
int p_start = 0,
|
||||||
|
int p_end = -1);
|
||||||
|
|
||||||
// Highlight @p_cursor as the incremental searched keyword.
|
// Highlight @p_cursor as the incremental searched keyword.
|
||||||
void highlightIncrementalSearchedWord(const QTextCursor &p_cursor);
|
void highlightIncrementalSearchedWord(const QTextCursor &p_cursor);
|
||||||
@ -311,7 +417,7 @@ private:
|
|||||||
|
|
||||||
void showWrapLabel();
|
void showWrapLabel();
|
||||||
|
|
||||||
void highlightSearchedWord(const QString &p_text, uint p_options);
|
void highlightSearchedWord(const QList<QTextCursor> &p_matches);
|
||||||
|
|
||||||
// Highlight @p_cursor as the searched keyword under cursor.
|
// Highlight @p_cursor as the searched keyword under cursor.
|
||||||
void highlightSearchedWordUnderCursor(const QTextCursor &p_cursor);
|
void highlightSearchedWordUnderCursor(const QTextCursor &p_cursor);
|
||||||
@ -322,6 +428,28 @@ private:
|
|||||||
|
|
||||||
bool findTextOne(const QString &p_text, uint p_options, bool p_forward);
|
bool findTextOne(const QString &p_text, uint p_options, bool p_forward);
|
||||||
|
|
||||||
|
// @p_end, -1 indicates the end of doc.
|
||||||
|
static QList<QTextCursor> findTextAllInRange(const QTextDocument *p_doc,
|
||||||
|
const QString &p_text,
|
||||||
|
QTextDocument::FindFlags p_flags,
|
||||||
|
int p_start = 0,
|
||||||
|
int p_end = -1);
|
||||||
|
|
||||||
|
static QList<QTextCursor> findTextAllInRange(const QTextDocument *p_doc,
|
||||||
|
const QRegExp &p_reg,
|
||||||
|
QTextDocument::FindFlags p_flags,
|
||||||
|
int p_start = 0,
|
||||||
|
int p_end = -1);
|
||||||
|
|
||||||
|
bool findTextInRange(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
bool p_forward,
|
||||||
|
QTextCursor *p_cursor = nullptr,
|
||||||
|
QTextCursor::MoveMode p_moveMode = QTextCursor::MoveAnchor,
|
||||||
|
bool p_useLeftSideOfCursor = false,
|
||||||
|
int p_start = 0,
|
||||||
|
int p_end = -1);
|
||||||
|
|
||||||
QLabel *m_wrapLabel;
|
QLabel *m_wrapLabel;
|
||||||
QTimer *m_labelTimer;
|
QTimer *m_labelTimer;
|
||||||
|
|
||||||
@ -368,6 +496,8 @@ private:
|
|||||||
// Temp files needed to be delete.
|
// Temp files needed to be delete.
|
||||||
QStringList m_tempFiles;
|
QStringList m_tempFiles;
|
||||||
|
|
||||||
|
FindInfo m_findInfo;
|
||||||
|
|
||||||
// Functions for private slots.
|
// Functions for private slots.
|
||||||
private:
|
private:
|
||||||
void labelTimerTimeout();
|
void labelTimerTimeout();
|
||||||
@ -378,6 +508,8 @@ private:
|
|||||||
void updateTrailingSpaceHighlights();
|
void updateTrailingSpaceHighlights();
|
||||||
|
|
||||||
void doUpdateTrailingSpaceHighlights();
|
void doUpdateTrailingSpaceHighlights();
|
||||||
|
|
||||||
|
void clearFindCache();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -446,6 +578,11 @@ private slots:
|
|||||||
m_editor->doUpdateTrailingSpaceHighlights();
|
m_editor->doUpdateTrailingSpaceHighlights();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clearFindCache()
|
||||||
|
{
|
||||||
|
m_editor->clearFindCache();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class VEditor;
|
friend class VEditor;
|
||||||
|
|
||||||
|
@ -66,6 +66,8 @@ public:
|
|||||||
virtual void replaceTextAll(const QString &p_text, uint p_options,
|
virtual void replaceTextAll(const QString &p_text, uint p_options,
|
||||||
const QString &p_replaceText) = 0;
|
const QString &p_replaceText) = 0;
|
||||||
|
|
||||||
|
virtual void nextMatch(const QString &p_text, uint p_options, bool p_forward) = 0;
|
||||||
|
|
||||||
// Return selected text.
|
// Return selected text.
|
||||||
virtual QString getSelectedText() const = 0;
|
virtual QString getSelectedText() const = 0;
|
||||||
|
|
||||||
|
@ -240,6 +240,11 @@ void VHtmlTab::replaceTextAll(const QString &p_text, uint p_options,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VHtmlTab::nextMatch(const QString &p_text, uint p_options, bool p_forward)
|
||||||
|
{
|
||||||
|
findText(p_text, p_options, false, p_forward);
|
||||||
|
}
|
||||||
|
|
||||||
QString VHtmlTab::getSelectedText() const
|
QString VHtmlTab::getSelectedText() const
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
|
@ -41,6 +41,8 @@ public:
|
|||||||
void replaceTextAll(const QString &p_text, uint p_options,
|
void replaceTextAll(const QString &p_text, uint p_options,
|
||||||
const QString &p_replaceText) Q_DECL_OVERRIDE;
|
const QString &p_replaceText) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void nextMatch(const QString &p_text, uint p_options, bool p_forward) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
QString getSelectedText() const Q_DECL_OVERRIDE;
|
QString getSelectedText() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
void clearSearchedWordHighlight() Q_DECL_OVERRIDE;
|
void clearSearchedWordHighlight() Q_DECL_OVERRIDE;
|
||||||
|
@ -719,6 +719,16 @@ void VMdTab::replaceTextAll(const QString &p_text, uint p_options,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VMdTab::nextMatch(const QString &p_text, uint p_options, bool p_forward)
|
||||||
|
{
|
||||||
|
if (m_isEditMode) {
|
||||||
|
Q_ASSERT(m_editor);
|
||||||
|
m_editor->nextMatch(p_forward);
|
||||||
|
} else {
|
||||||
|
findTextInWebView(p_text, p_options, false, p_forward);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void VMdTab::findTextInWebView(const QString &p_text, uint p_options,
|
void VMdTab::findTextInWebView(const QString &p_text, uint p_options,
|
||||||
bool /* p_peek */, bool p_forward)
|
bool /* p_peek */, bool p_forward)
|
||||||
{
|
{
|
||||||
|
@ -57,6 +57,8 @@ public:
|
|||||||
void replaceTextAll(const QString &p_text, uint p_options,
|
void replaceTextAll(const QString &p_text, uint p_options,
|
||||||
const QString &p_replaceText) Q_DECL_OVERRIDE;
|
const QString &p_replaceText) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void nextMatch(const QString &p_text, uint p_options, bool p_forward) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
QString getSelectedText() const Q_DECL_OVERRIDE;
|
QString getSelectedText() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
void clearSearchedWordHighlight() Q_DECL_OVERRIDE;
|
void clearSearchedWordHighlight() Q_DECL_OVERRIDE;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user