mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +08:00
support find and replace (#1593)
TODO: we may need to transform the `QRegularExpression` usage to the `RegExp` in JS.
This commit is contained in:
parent
3d7406ff24
commit
847e3d621d
@ -1 +1 @@
|
||||
Subproject commit 85585710ce04eaa01ec82d7d7dc4c82fed0dde41
|
||||
Subproject commit 86cf8e0e6d840b923dc30046d12ab3c6e634f6b9
|
@ -46,6 +46,8 @@ namespace vnotex
|
||||
Outline,
|
||||
RichPaste,
|
||||
FindAndReplace,
|
||||
FindNext,
|
||||
FindPrevious,
|
||||
MaxShortcut
|
||||
};
|
||||
Q_ENUM(Shortcut)
|
||||
|
@ -64,10 +64,11 @@ namespace vnotex
|
||||
enum FindOption
|
||||
{
|
||||
None = 0,
|
||||
CaseSensitive = 0x1U,
|
||||
WholeWordOnly = 0x2U,
|
||||
RegularExpression = 0x4U,
|
||||
IncrementalSearch = 0x8U
|
||||
FindBackward = 0x1U,
|
||||
CaseSensitive = 0x2U,
|
||||
WholeWordOnly = 0x4U,
|
||||
RegularExpression = 0x8U,
|
||||
IncrementalSearch = 0x10U
|
||||
};
|
||||
Q_DECLARE_FLAGS(FindOptions, FindOption);
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <core/markdowneditorconfig.h>
|
||||
#include <core/configmgr.h>
|
||||
#include <utils/utils.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/pathutils.h>
|
||||
#include <core/thememgr.h>
|
||||
@ -16,11 +17,11 @@ HtmlTemplateHelper::Template HtmlTemplateHelper::s_markdownViewerTemplate;
|
||||
QString WebGlobalOptions::toJavascriptObject() const
|
||||
{
|
||||
return QStringLiteral("window.vxOptions = {\n")
|
||||
+ QString("webPlantUml: %1,\n").arg(boolToString(m_webPlantUml))
|
||||
+ QString("webGraphviz: %1,\n").arg(boolToString(m_webGraphviz))
|
||||
+ QString("constrainImageWidthEnabled: %1,\n").arg(boolToString(m_constrainImageWidthEnabled))
|
||||
+ QString("protectFromXss: %1,\n").arg(boolToString(m_protectFromXss))
|
||||
+ QString("sectionNumberEnabled: %1\n").arg(boolToString(m_sectionNumberEnabled))
|
||||
+ QString("webPlantUml: %1,\n").arg(Utils::boolToString(m_webPlantUml))
|
||||
+ QString("webGraphviz: %1,\n").arg(Utils::boolToString(m_webGraphviz))
|
||||
+ QString("constrainImageWidthEnabled: %1,\n").arg(Utils::boolToString(m_constrainImageWidthEnabled))
|
||||
+ QString("protectFromXss: %1,\n").arg(Utils::boolToString(m_protectFromXss))
|
||||
+ QString("sectionNumberEnabled: %1\n").arg(Utils::boolToString(m_sectionNumberEnabled))
|
||||
+ QStringLiteral("}");
|
||||
}
|
||||
|
||||
|
@ -20,11 +20,6 @@ namespace vnotex
|
||||
|
||||
bool m_protectFromXss = false;
|
||||
|
||||
QString boolToString(bool p_val) const
|
||||
{
|
||||
return p_val ? QStringLiteral("true") : QStringLiteral("false");
|
||||
}
|
||||
|
||||
QString toJavascriptObject() const;
|
||||
};
|
||||
|
||||
|
@ -28,6 +28,7 @@ QJsonObject WidgetConfig::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj[QStringLiteral("outline_auto_expanded_level")] = m_outlineAutoExpandedLevel;
|
||||
obj[QStringLiteral("find_and_replace_options")] = static_cast<int>(m_findAndReplaceOptions);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,9 @@
|
||||
"TypeTable" : "Ctrl+/",
|
||||
"Outline" : "Ctrl+G, O",
|
||||
"RichPaste" : "Ctrl+Shift+V",
|
||||
"FindAndReplace" : "Ctrl+F"
|
||||
"FindAndReplace" : "Ctrl+F",
|
||||
"FindNext" : "F3",
|
||||
"FindPrevious" : "Shift+F3"
|
||||
}
|
||||
},
|
||||
"text_editor" : {
|
||||
@ -195,6 +197,14 @@
|
||||
"web/js/turndown/turndown-plugin-gfm.js",
|
||||
"web/js/turndown.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "mark.js",
|
||||
"enabled" : true,
|
||||
"scripts" : [
|
||||
"web/js/mark.js/mark.min.js",
|
||||
"web/js/markjs.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -226,6 +236,6 @@
|
||||
"//comment" : "Level of the heading in outline that should expand to automatically (1-6)",
|
||||
"outline_auto_expanded_level" : 6,
|
||||
"//comment" : "Default find options in FindAndReplace",
|
||||
"find_and_replace_options" : 8
|
||||
"find_and_replace_options" : 16
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<p>
|
||||
VNoteX is designed to be a pleasant note-taking platform, refactored from VNote, which is an open source note-taking application for Markdown since 2016. VNote will share most of the code base with VNoteX since version 3.0 and continue to be open source.
|
||||
VNoteX is designed to be a pleasant note-taking platform, refactored from VNote, which is an open source note-taking application for Markdown since 2016. VNote shares most of the code base with VNoteX since version 3.0 and continue to be open source.
|
||||
<br/><br/>
|
||||
Source code of VNote could be found at <a href="https://github.com/vnotex/vnote">GitHub</a>.
|
||||
<br/><br/>
|
||||
|
@ -63,6 +63,8 @@
|
||||
<file>web/js/turndown/turndown.js</file>
|
||||
<file>web/js/turndown/turndown-plugin-gfm.js</file>
|
||||
<file>web/js/turndown.js</file>
|
||||
<file>web/js/mark.js/mark.min.js</file>
|
||||
<file>web/js/markjs.js</file>
|
||||
<file>syntax-highlighting/themes/markdown-default.theme</file>
|
||||
<file>syntax-highlighting/themes/default.theme</file>
|
||||
<file>syntax-highlighting/themes/breeze-dark.theme</file>
|
||||
|
@ -140,35 +140,5 @@
|
||||
"text-color" : "#006e28",
|
||||
"selected-text-color" : "#006e28"
|
||||
}
|
||||
},
|
||||
"editor-colors": {
|
||||
"background-color" : "#ffffff",
|
||||
"code-folding" : "#94caef",
|
||||
"bracket-matching" : "#ffff00",
|
||||
"current-line" : "#f8f7f6",
|
||||
"icon-border" : "#f0f0f0",
|
||||
"indentation-line" : "#d2d2d2",
|
||||
"line-numbers" : "#a0a0a0",
|
||||
"current-line-number" : "#1e1e1e",
|
||||
"mark-bookmark" : "#0000ff",
|
||||
"mark-breakpoint-active" : "#ff0000",
|
||||
"mark-breakpoint-reached" : "#ffff00",
|
||||
"mark-breakpoint-disabled" : "#ff00ff",
|
||||
"mark-execution" : "#a0a0a4",
|
||||
"mark-warning" : "#00ff00",
|
||||
"mark-error" : "#ff0000",
|
||||
"modified-lines" : "#fdbc4b",
|
||||
"replace-highlight" : "#00ff00",
|
||||
"saved-lines" : "#2ecc71",
|
||||
"search-highlight" : "#ffff00",
|
||||
"selection" : "#94caef",
|
||||
"separator" : "#898887",
|
||||
"spell-checking" : "#bf0303",
|
||||
"tab-marker" : "#d2d2d2",
|
||||
"template-background" : "#d6d2d0",
|
||||
"template-placeholder" : "#baf8ce",
|
||||
"template-focused-placeholder" : "#76da98",
|
||||
"template-read-only-placeholder" : "#f6e6e6",
|
||||
"word-wrap-marker" : "#ededed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,21 @@
|
||||
},
|
||||
"FoldingHighlight" : {
|
||||
"text-color" : "#ffa9c4f5"
|
||||
},
|
||||
"IncrementalSearch" : {
|
||||
"//comment" : "Incremental search highlight",
|
||||
"text-color" : "#222222",
|
||||
"background-color" : "#ce93d8"
|
||||
},
|
||||
"Search" : {
|
||||
"//comment" : "Search highlight",
|
||||
"text-color" : "#222222",
|
||||
"background-color" : "#4db6ac"
|
||||
},
|
||||
"SearchUnderCursor" : {
|
||||
"//comment" : "Search highlight under cursor",
|
||||
"text-color" : "#222222",
|
||||
"background-color" : "#66bb6a"
|
||||
}
|
||||
},
|
||||
"//comment" : "Override the Text style in editor-styles",
|
||||
|
@ -290,3 +290,13 @@ span.modal-close:hover,
|
||||
span.modal-close:focus {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
#vx-content span.vx-search-match {
|
||||
color: #222222;
|
||||
background-color: #4db6ac;
|
||||
}
|
||||
|
||||
#vx-content span.vx-current-search-match {
|
||||
color: #222222;
|
||||
background-color: #66bb6a;
|
||||
}
|
||||
|
3
src/data/extra/web/js/mark.js/README.md
Normal file
3
src/data/extra/web/js/mark.js/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# [mark.js](https://github.com/julmot/mark.js)
|
||||
v8.11.1
|
||||
Julian Kühnel
|
13
src/data/extra/web/js/mark.js/mark.min.js
vendored
Normal file
13
src/data/extra/web/js/mark.js/mark.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -39,6 +39,10 @@ new QWebChannel(qt.webChannelTransport,
|
||||
window.vnotex.crossCopy(p_id, p_timeStamp, p_target, p_baseUrl, p_html);
|
||||
});
|
||||
|
||||
adapter.findTextRequested.connect(function(p_text, p_options) {
|
||||
window.vnotex.findText(p_text, p_options);
|
||||
});
|
||||
|
||||
console.log('QWebChannel has been set up');
|
||||
if (window.vnotex.initialized) {
|
||||
window.vnotex.kickOffMarkdown();
|
||||
|
128
src/data/extra/web/js/markjs.js
Normal file
128
src/data/extra/web/js/markjs.js
Normal file
@ -0,0 +1,128 @@
|
||||
class MarkJs {
|
||||
constructor(p_adapter, p_container) {
|
||||
this.className = 'vx-search-match';
|
||||
this.currentMatchClassName = 'vx-current-search-match';
|
||||
this.adapter = p_adapter;
|
||||
this.container = p_container;
|
||||
this.markjs = null;
|
||||
this.cache = null;
|
||||
this.matchedNodes = null;
|
||||
|
||||
this.adapter.on('basicMarkdownRendered', () => {
|
||||
this.clearCache();
|
||||
});
|
||||
}
|
||||
|
||||
// @p_options: {
|
||||
// findBackward,
|
||||
// caseSensitive,
|
||||
// wholeWordOnly,
|
||||
// regularExpression
|
||||
// }
|
||||
findText(p_text, p_options) {
|
||||
if (!this.markjs) {
|
||||
this.markjs = new Mark(this.container);
|
||||
}
|
||||
|
||||
if (!p_text) {
|
||||
// Clear the cache and highlight.
|
||||
this.clearCache();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.findInCache(p_text, p_options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A new find.
|
||||
this.clearCache();
|
||||
|
||||
let callbackFunc = function(markjs, text, options) {
|
||||
let _markjs = markjs;
|
||||
let _text = text;
|
||||
let _options = options;
|
||||
return function(totalMatches) {
|
||||
if (!_markjs.matchedNodes) {
|
||||
_markjs.matchedNodes = _markjs.container.getElementsByClassName(_markjs.className);
|
||||
}
|
||||
|
||||
// Update cache.
|
||||
_markjs.cache = {
|
||||
text: _text,
|
||||
options: _options,
|
||||
currentIdx: -1
|
||||
}
|
||||
|
||||
_markjs.updateCurrentMatch(_text, !_options.findBackward);
|
||||
};
|
||||
}
|
||||
let opt = {
|
||||
'element': 'span',
|
||||
'className': this.className,
|
||||
'caseSensitive': p_options.caseSensitive,
|
||||
'accuracy': p_options.wholeWordOnly ? 'exactly' : 'partially',
|
||||
'done': callbackFunc(this, p_text, p_options)
|
||||
}
|
||||
|
||||
if (p_options.regularExpression) {
|
||||
// TODO: may need transformation from QRegularExpression to RegExp.
|
||||
this.markjs.markRegExp(new RegExp(p_text), opt);
|
||||
} else {
|
||||
this.markjs.mark(p_text, opt);
|
||||
}
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
if (!this.markjs) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cache = null;
|
||||
this.markjs.unmark();
|
||||
}
|
||||
|
||||
findInCache(p_text, p_options) {
|
||||
if (!this.cache) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.cache.text === p_text
|
||||
&& this.cache.options.caseSensitive == p_options.caseSensitive
|
||||
&& this.cache.options.wholeWordOnly == p_options.wholeWordOnly
|
||||
&& this.cache.options.regularExpression == p_options.regularExpression) {
|
||||
// Matched. Move current match forward or backward.
|
||||
this.updateCurrentMatch(p_text, !p_options.findBackward);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
updateCurrentMatch(p_text, p_forward) {
|
||||
let matches = this.matchedNodes.length;
|
||||
if (matches == 0) {
|
||||
this.adapter.showFindResult(p_text, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (this.cache.currentIdx >= 0) {
|
||||
this.matchedNodes[this.cache.currentIdx].classList.remove(this.currentMatchClassName);
|
||||
}
|
||||
if (p_forward) {
|
||||
this.cache.currentIdx += 1;
|
||||
if (this.cache.currentIdx >= matches) {
|
||||
this.cache.currentIdx = 0;
|
||||
}
|
||||
} else {
|
||||
this.cache.currentIdx -= 1;
|
||||
if (this.cache.currentIdx < 0) {
|
||||
this.cache.currentIdx = matches - 1;
|
||||
}
|
||||
}
|
||||
let node = this.matchedNodes[this.cache.currentIdx];
|
||||
node.classList.add(this.currentMatchClassName);
|
||||
if (!Utils.isVisible(node)) {
|
||||
node.scrollIntoView();
|
||||
}
|
||||
this.adapter.showFindResult(p_text, matches, this.cache.currentIdx);
|
||||
}
|
||||
}
|
@ -42,7 +42,7 @@ class NodeLineMapper {
|
||||
this.headingNodes = this.container.querySelectorAll("h1, h2, h3, h4, h5, h6");
|
||||
let headings = [];
|
||||
let needSectionNumber = window.vxOptions.sectionNumberEnabled;
|
||||
let regExp = /^(?:\d\.)+ /;
|
||||
let regExp = /^\d(?:\.\d)*\.? /;
|
||||
for (let i = 0; i < this.headingNodes.length; ++i) {
|
||||
let node = this.headingNodes[i];
|
||||
headings.push({
|
||||
|
@ -99,4 +99,14 @@ class Utils {
|
||||
height: rect.height
|
||||
};
|
||||
}
|
||||
|
||||
static isVisible(p_node) {
|
||||
let rect = p_node.getBoundingClientRect();
|
||||
let vrect = this.viewPortRect();
|
||||
if (rect.top < 0 || rect.left < 0
|
||||
|| rect.bottom > vrect.height || rect.right > vrect.width) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,8 @@ class VNoteX extends EventEmitter {
|
||||
|
||||
this.crossCopyer = new CrossCopy(this);
|
||||
|
||||
this.searcher = new MarkJs(this, this.contentContainer);
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
// Signal out.
|
||||
@ -250,6 +252,14 @@ class VNoteX extends EventEmitter {
|
||||
window.vxMarkdownAdapter.setCrossCopyResult(p_id, p_timeStamp, p_html);
|
||||
}
|
||||
|
||||
findText(p_text, p_options) {
|
||||
this.searcher.findText(p_text, p_options);
|
||||
}
|
||||
|
||||
showFindResult(p_text, p_totalMatches, p_currentMatchIndex) {
|
||||
window.vxMarkdownAdapter.setFindText(p_text, p_totalMatches, p_currentMatchIndex);
|
||||
}
|
||||
|
||||
static detectOS() {
|
||||
let osName="Unknown OS";
|
||||
if (navigator.appVersion.indexOf("Win")!=-1) {
|
||||
|
@ -62,7 +62,7 @@ QString PathUtils::concatenateFilePath(const QString &p_dirPath, const QString &
|
||||
|
||||
QString PathUtils::dirName(const QString &p_path)
|
||||
{
|
||||
Q_ASSERT(QFileInfo(p_path).isDir());
|
||||
Q_ASSERT(!QFileInfo::exists(p_path) || QFileInfo(p_path).isDir());
|
||||
return QDir(p_path).dirName();
|
||||
}
|
||||
|
||||
|
@ -112,3 +112,8 @@ bool Utils::fuzzyEqual(qreal p_a, qreal p_b)
|
||||
{
|
||||
return std::abs(p_a - p_b) < std::pow(10, -6);
|
||||
}
|
||||
|
||||
QString Utils::boolToString(bool p_val)
|
||||
{
|
||||
return p_val ? QStringLiteral("true") : QStringLiteral("false");
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ namespace vnotex
|
||||
qreal p_scaleFactor);
|
||||
|
||||
static bool fuzzyEqual(qreal p_a, qreal p_b);
|
||||
|
||||
static QString boolToString(bool p_val);
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -55,6 +55,16 @@ MarkdownViewerAdapter::Heading MarkdownViewerAdapter::Heading::fromJson(const QJ
|
||||
p_obj.value(QStringLiteral("anchor")).toString());
|
||||
}
|
||||
|
||||
QJsonObject MarkdownViewerAdapter::FindOption::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj["findBackward"] = m_findBackward;
|
||||
obj["caseSensitive"] = m_caseSensitive;
|
||||
obj["wholeWordOnly"] = m_wholeWordOnly;
|
||||
obj["regularExpression"] = m_regularExpression;
|
||||
return obj;
|
||||
}
|
||||
|
||||
MarkdownViewerAdapter::MarkdownViewerAdapter(QObject *p_parent)
|
||||
: QObject(p_parent)
|
||||
{
|
||||
@ -268,3 +278,27 @@ void MarkdownViewerAdapter::setCrossCopyResult(quint64 p_id, quint64 p_timeStamp
|
||||
{
|
||||
emit crossCopyReady(p_id, p_timeStamp, p_html);
|
||||
}
|
||||
|
||||
void MarkdownViewerAdapter::findText(const QString &p_text, FindOptions p_options)
|
||||
{
|
||||
FindOption opts;
|
||||
if (p_options & vnotex::FindOption::FindBackward) {
|
||||
opts.m_findBackward = true;
|
||||
}
|
||||
if (p_options & vnotex::FindOption::CaseSensitive) {
|
||||
opts.m_caseSensitive = true;
|
||||
}
|
||||
if (p_options & vnotex::FindOption::WholeWordOnly) {
|
||||
opts.m_wholeWordOnly = true;
|
||||
}
|
||||
if (p_options & vnotex::FindOption::RegularExpression) {
|
||||
opts.m_regularExpression = true;
|
||||
}
|
||||
|
||||
emit findTextRequested(p_text, opts.toJson());
|
||||
}
|
||||
|
||||
void MarkdownViewerAdapter::setFindText(const QString &p_text, int p_totalMatches, int p_currentMatchIndex)
|
||||
{
|
||||
emit findTextReady(p_text, p_totalMatches, p_currentMatchIndex);
|
||||
}
|
||||
|
@ -78,6 +78,19 @@ namespace vnotex
|
||||
QString m_anchor;
|
||||
};
|
||||
|
||||
struct FindOption
|
||||
{
|
||||
QJsonObject toJson() const;
|
||||
|
||||
bool m_findBackward = false;
|
||||
|
||||
bool m_caseSensitive = false;
|
||||
|
||||
bool m_wholeWordOnly = false;
|
||||
|
||||
bool m_regularExpression = false;
|
||||
};
|
||||
|
||||
explicit MarkdownViewerAdapter(QObject *p_parent = nullptr);
|
||||
|
||||
virtual ~MarkdownViewerAdapter();
|
||||
@ -102,6 +115,8 @@ namespace vnotex
|
||||
|
||||
const QStringList &getCrossCopyTargets() const;
|
||||
|
||||
void findText(const QString &p_text, FindOptions p_options);
|
||||
|
||||
// Functions to be called from web side.
|
||||
public slots:
|
||||
void setReady(bool p_ready);
|
||||
@ -142,6 +157,8 @@ namespace vnotex
|
||||
|
||||
void setCrossCopyResult(quint64 p_id, quint64 p_timeStamp, const QString &p_html);
|
||||
|
||||
void setFindText(const QString &p_text, int p_totalMatches, int p_currentMatchIndex);
|
||||
|
||||
// Signals to be connected at web side.
|
||||
signals:
|
||||
// Current Markdown text is updated.
|
||||
@ -173,6 +190,8 @@ namespace vnotex
|
||||
const QString &p_baseUrl,
|
||||
const QString &p_html);
|
||||
|
||||
void findTextRequested(const QString &p_text, const QJsonObject &p_options);
|
||||
|
||||
// Signals to be connected at cpp side.
|
||||
signals:
|
||||
void graphPreviewDataReady(const PreviewData &p_data);
|
||||
@ -193,6 +212,8 @@ namespace vnotex
|
||||
|
||||
void crossCopyReady(quint64 p_id, quint64 p_timeStamp, const QString &p_html);
|
||||
|
||||
void findTextReady(const QString &p_text, int p_totalMatches, int p_currentMatchIndex);
|
||||
|
||||
private:
|
||||
void scrollToLine(int p_lineNumber);
|
||||
|
||||
|
@ -30,7 +30,7 @@ FindAndReplaceWidget::FindAndReplaceWidget(QWidget *p_parent)
|
||||
m_findTextTimer->setInterval(500);
|
||||
connect(m_findTextTimer, &QTimer::timeout,
|
||||
this, [this]() {
|
||||
emit findTextChanged(m_findLineEdit->text(), m_options);
|
||||
emit findTextChanged(getFindText(), getOptions());
|
||||
});
|
||||
|
||||
setupUI();
|
||||
@ -155,6 +155,7 @@ void FindAndReplaceWidget::setupUI()
|
||||
void FindAndReplaceWidget::close()
|
||||
{
|
||||
hide();
|
||||
emit closed();
|
||||
}
|
||||
|
||||
void FindAndReplaceWidget::setReplaceEnabled(bool p_enabled)
|
||||
@ -198,47 +199,82 @@ void FindAndReplaceWidget::keyPressEvent(QKeyEvent *p_event)
|
||||
|
||||
void FindAndReplaceWidget::findNext()
|
||||
{
|
||||
|
||||
m_findTextTimer->stop();
|
||||
auto text = m_findLineEdit->text();
|
||||
if (text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
emit findNextRequested(text, m_options);
|
||||
}
|
||||
|
||||
void FindAndReplaceWidget::findPrevious()
|
||||
{
|
||||
|
||||
m_findTextTimer->stop();
|
||||
auto text = m_findLineEdit->text();
|
||||
if (text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
emit findNextRequested(text, m_options | FindOption::FindBackward);
|
||||
}
|
||||
|
||||
void FindAndReplaceWidget::updateFindOptions()
|
||||
{
|
||||
m_options = FindOption::None;
|
||||
if (m_optionCheckBoxMuted) {
|
||||
return;
|
||||
}
|
||||
|
||||
FindOptions options = FindOption::None;
|
||||
|
||||
if (m_caseSensitiveCheckBox->isChecked()) {
|
||||
m_options |= FindOption::CaseSensitive;
|
||||
options |= FindOption::CaseSensitive;
|
||||
}
|
||||
if (m_wholeWordOnlyCheckBox->isChecked()) {
|
||||
m_options |= FindOption::WholeWordOnly;
|
||||
options |= FindOption::WholeWordOnly;
|
||||
}
|
||||
if (m_regularExpressionCheckBox->isChecked()) {
|
||||
m_options |= FindOption::RegularExpression;
|
||||
options |= FindOption::RegularExpression;
|
||||
}
|
||||
if (m_incrementalSearchCheckBox->isChecked()) {
|
||||
m_options |= FindOption::IncrementalSearch;
|
||||
options |= FindOption::IncrementalSearch;
|
||||
}
|
||||
|
||||
if (options == m_options) {
|
||||
return;
|
||||
}
|
||||
m_options = options;
|
||||
ConfigMgr::getInst().getWidgetConfig().setFindAndReplaceOptions(m_options);
|
||||
m_findTextTimer->start();
|
||||
}
|
||||
|
||||
void FindAndReplaceWidget::replace()
|
||||
{
|
||||
|
||||
m_findTextTimer->stop();
|
||||
auto text = m_findLineEdit->text();
|
||||
if (text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
emit replaceRequested(text, m_options, m_replaceLineEdit->text());
|
||||
}
|
||||
|
||||
void FindAndReplaceWidget::replaceAndFind()
|
||||
{
|
||||
|
||||
m_findTextTimer->stop();
|
||||
auto text = m_findLineEdit->text();
|
||||
if (text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
emit replaceRequested(text, m_options, m_replaceLineEdit->text());
|
||||
emit findNextRequested(text, m_options);
|
||||
}
|
||||
|
||||
void FindAndReplaceWidget::replaceAll()
|
||||
{
|
||||
|
||||
m_findTextTimer->stop();
|
||||
auto text = m_findLineEdit->text();
|
||||
if (text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
emit replaceAllRequested(text, m_options, m_replaceLineEdit->text());
|
||||
}
|
||||
|
||||
void FindAndReplaceWidget::setFindOptions(FindOptions p_options)
|
||||
@ -247,11 +283,13 @@ void FindAndReplaceWidget::setFindOptions(FindOptions p_options)
|
||||
return;
|
||||
}
|
||||
|
||||
m_options = p_options;
|
||||
m_optionCheckBoxMuted = true;
|
||||
m_options = p_options & ~FindOption::FindBackward;
|
||||
m_caseSensitiveCheckBox->setChecked(m_options & FindOption::CaseSensitive);
|
||||
m_wholeWordOnlyCheckBox->setChecked(m_options & FindOption::WholeWordOnly);
|
||||
m_regularExpressionCheckBox->setChecked(m_options & FindOption::RegularExpression);
|
||||
m_incrementalSearchCheckBox->setChecked(m_options & FindOption::IncrementalSearch);
|
||||
m_optionCheckBoxMuted = false;
|
||||
}
|
||||
|
||||
void FindAndReplaceWidget::open(const QString &p_text)
|
||||
@ -264,4 +302,16 @@ void FindAndReplaceWidget::open(const QString &p_text)
|
||||
|
||||
m_findLineEdit->setFocus();
|
||||
m_findLineEdit->selectAll();
|
||||
|
||||
emit opened();
|
||||
}
|
||||
|
||||
QString FindAndReplaceWidget::getFindText() const
|
||||
{
|
||||
return m_findLineEdit->text();
|
||||
}
|
||||
|
||||
FindOptions FindAndReplaceWidget::getOptions() const
|
||||
{
|
||||
return m_options;
|
||||
}
|
||||
|
@ -25,9 +25,23 @@ namespace vnotex
|
||||
|
||||
void close();
|
||||
|
||||
QString getFindText() const;
|
||||
|
||||
FindOptions getOptions() const;
|
||||
|
||||
signals:
|
||||
void findTextChanged(const QString &p_text, FindOptions p_options);
|
||||
|
||||
void findNextRequested(const QString &p_text, FindOptions p_options);
|
||||
|
||||
void replaceRequested(const QString &p_text, FindOptions p_options, const QString &p_replaceText);
|
||||
|
||||
void replaceAllRequested(const QString &p_text, FindOptions p_options, const QString &p_replaceText);
|
||||
|
||||
void closed();
|
||||
|
||||
void opened();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
@ -66,6 +80,8 @@ namespace vnotex
|
||||
FindOptions m_options = FindOption::None;
|
||||
|
||||
QTimer *m_findTextTimer = nullptr;
|
||||
|
||||
bool m_optionCheckBoxMuted = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <core/vnotex.h>
|
||||
#include <core/thememgr.h>
|
||||
#include "editors/markdowneditor.h"
|
||||
#include "textviewwindow.h"
|
||||
#include "textviewwindowhelper.h"
|
||||
#include "editors/markdownviewer.h"
|
||||
#include "editors/editormarkdownvieweradapter.h"
|
||||
@ -28,6 +27,7 @@
|
||||
#include "dialogs/deleteconfirmdialog.h"
|
||||
#include "outlineprovider.h"
|
||||
#include "toolbarhelper.h"
|
||||
#include "findandreplacewidget.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -82,6 +82,10 @@ void MarkdownViewWindow::setupUI()
|
||||
void MarkdownViewWindow::setMode(Mode p_mode)
|
||||
{
|
||||
setModeInternal(p_mode);
|
||||
|
||||
if (m_findAndReplace && m_findAndReplace->isVisible()) {
|
||||
m_findAndReplace->setReplaceEnabled(m_mode != Mode::Read);
|
||||
}
|
||||
}
|
||||
|
||||
void MarkdownViewWindow::setModeInternal(Mode p_mode)
|
||||
@ -278,6 +282,7 @@ void MarkdownViewWindow::setupToolBar()
|
||||
toolBar->addSeparator();
|
||||
|
||||
ToolBarHelper::addSpacer(toolBar);
|
||||
addAction(toolBar, ViewWindowToolBarHelper::FindAndReplace);
|
||||
addAction(toolBar, ViewWindowToolBarHelper::Outline);
|
||||
}
|
||||
|
||||
@ -418,6 +423,8 @@ void MarkdownViewWindow::setupViewer()
|
||||
m_outlineProvider->setCurrentHeadingIndex(this->adapter()->getCurrentHeadingIndex());
|
||||
}
|
||||
});
|
||||
connect(adapter, &MarkdownViewerAdapter::findTextReady,
|
||||
this, &ViewWindow::showFindResult);
|
||||
}
|
||||
|
||||
void MarkdownViewWindow::syncTextEditorFromBuffer(bool p_syncPositionFromReadMode)
|
||||
@ -792,3 +799,56 @@ void MarkdownViewWindow::zoom(bool p_zoomIn)
|
||||
textEditorConfig.setZoomDelta(m_editor->zoomDelta());
|
||||
showZoomDelta(m_editor->zoomDelta());
|
||||
}
|
||||
|
||||
void MarkdownViewWindow::handleFindTextChanged(const QString &p_text, FindOptions p_options)
|
||||
{
|
||||
if (m_mode == Mode::Read) {
|
||||
adapter()->findText(p_text, p_options);
|
||||
} else {
|
||||
TextViewWindowHelper::handleFindTextChanged(this, p_text, p_options);
|
||||
}
|
||||
}
|
||||
|
||||
void MarkdownViewWindow::handleFindNext(const QString &p_text, FindOptions p_options)
|
||||
{
|
||||
if (m_mode == Mode::Read) {
|
||||
if (p_options & FindOption::IncrementalSearch) {
|
||||
adapter()->findText(p_text, p_options);
|
||||
}
|
||||
} else {
|
||||
TextViewWindowHelper::handleFindNext(this, p_text, p_options);
|
||||
}
|
||||
}
|
||||
|
||||
void MarkdownViewWindow::handleReplace(const QString &p_text, FindOptions p_options, const QString &p_replaceText)
|
||||
{
|
||||
if (m_mode == Mode::Read) {
|
||||
VNoteX::getInst().showStatusMessageShort(tr("Replace is not supported in read mode"));
|
||||
} else {
|
||||
TextViewWindowHelper::handleReplace(this, p_text, p_options, p_replaceText);
|
||||
}
|
||||
}
|
||||
|
||||
void MarkdownViewWindow::handleReplaceAll(const QString &p_text, FindOptions p_options, const QString &p_replaceText)
|
||||
{
|
||||
if (m_mode == Mode::Read) {
|
||||
VNoteX::getInst().showStatusMessageShort(tr("Replace is not supported in read mode"));
|
||||
} else {
|
||||
TextViewWindowHelper::handleReplaceAll(this, p_text, p_options, p_replaceText);
|
||||
}
|
||||
}
|
||||
|
||||
void MarkdownViewWindow::handleFindAndReplaceWidgetClosed()
|
||||
{
|
||||
if (m_editor) {
|
||||
TextViewWindowHelper::handleFindAndReplaceWidgetClosed(this);
|
||||
} else {
|
||||
adapter()->findText("", FindOption::None);
|
||||
}
|
||||
}
|
||||
|
||||
void MarkdownViewWindow::handleFindAndReplaceWidgetOpened()
|
||||
{
|
||||
Q_ASSERT(m_findAndReplace);
|
||||
m_findAndReplace->setReplaceEnabled(m_mode != Mode::Read);
|
||||
}
|
||||
|
@ -49,6 +49,18 @@ namespace vnotex
|
||||
|
||||
void handleTypeAction(TypeAction p_action) Q_DECL_OVERRIDE;
|
||||
|
||||
void handleFindTextChanged(const QString &p_text, FindOptions p_options) Q_DECL_OVERRIDE;
|
||||
|
||||
void handleFindNext(const QString &p_text, FindOptions p_options) Q_DECL_OVERRIDE;
|
||||
|
||||
void handleReplace(const QString &p_text, FindOptions p_options, const QString &p_replaceText) Q_DECL_OVERRIDE;
|
||||
|
||||
void handleReplaceAll(const QString &p_text, FindOptions p_options, const QString &p_replaceText) Q_DECL_OVERRIDE;
|
||||
|
||||
void handleFindAndReplaceWidgetClosed() Q_DECL_OVERRIDE;
|
||||
|
||||
void handleFindAndReplaceWidgetOpened() Q_DECL_OVERRIDE;
|
||||
|
||||
protected:
|
||||
void syncEditorFromBuffer() Q_DECL_OVERRIDE;
|
||||
|
||||
|
@ -181,3 +181,28 @@ void TextViewWindow::zoom(bool p_zoomIn)
|
||||
textEditorConfig.setZoomDelta(m_editor->zoomDelta());
|
||||
showZoomDelta(m_editor->zoomDelta());
|
||||
}
|
||||
|
||||
void TextViewWindow::handleFindTextChanged(const QString &p_text, FindOptions p_options)
|
||||
{
|
||||
TextViewWindowHelper::handleFindTextChanged(this, p_text, p_options);
|
||||
}
|
||||
|
||||
void TextViewWindow::handleFindNext(const QString &p_text, FindOptions p_options)
|
||||
{
|
||||
TextViewWindowHelper::handleFindNext(this, p_text, p_options);
|
||||
}
|
||||
|
||||
void TextViewWindow::handleReplace(const QString &p_text, FindOptions p_options, const QString &p_replaceText)
|
||||
{
|
||||
TextViewWindowHelper::handleReplace(this, p_text, p_options, p_replaceText);
|
||||
}
|
||||
|
||||
void TextViewWindow::handleReplaceAll(const QString &p_text, FindOptions p_options, const QString &p_replaceText)
|
||||
{
|
||||
TextViewWindowHelper::handleReplaceAll(this, p_text, p_options, p_replaceText);
|
||||
}
|
||||
|
||||
void TextViewWindow::handleFindAndReplaceWidgetClosed()
|
||||
{
|
||||
TextViewWindowHelper::handleFindAndReplaceWidgetClosed(this);
|
||||
}
|
||||
|
@ -33,6 +33,16 @@ namespace vnotex
|
||||
|
||||
void handleBufferChangedInternal() Q_DECL_OVERRIDE;
|
||||
|
||||
void handleFindTextChanged(const QString &p_text, FindOptions p_options) Q_DECL_OVERRIDE;
|
||||
|
||||
void handleFindNext(const QString &p_text, FindOptions p_options) Q_DECL_OVERRIDE;
|
||||
|
||||
void handleReplace(const QString &p_text, FindOptions p_options, const QString &p_replaceText) Q_DECL_OVERRIDE;
|
||||
|
||||
void handleReplaceAll(const QString &p_text, FindOptions p_options, const QString &p_replaceText) Q_DECL_OVERRIDE;
|
||||
|
||||
void handleFindAndReplaceWidgetClosed() Q_DECL_OVERRIDE;
|
||||
|
||||
protected:
|
||||
void syncEditorFromBuffer() Q_DECL_OVERRIDE;
|
||||
|
||||
|
@ -127,6 +127,60 @@ namespace vnotex
|
||||
|
||||
return editorConfig;
|
||||
}
|
||||
|
||||
static vte::FindFlags toEditorFindFlags(FindOptions p_options)
|
||||
{
|
||||
vte::FindFlags flags;
|
||||
if (p_options & FindOption::FindBackward) {
|
||||
flags |= vte::FindFlag::FindBackward;
|
||||
}
|
||||
if (p_options & FindOption::CaseSensitive) {
|
||||
flags |= vte::FindFlag::CaseSensitive;
|
||||
}
|
||||
if (p_options & FindOption::WholeWordOnly) {
|
||||
flags |= vte::FindFlag::WholeWordOnly;
|
||||
}
|
||||
if (p_options & FindOption::RegularExpression) {
|
||||
flags |= vte::FindFlag::RegularExpression;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
template <typename _ViewWindow>
|
||||
static void handleFindTextChanged(_ViewWindow *p_win, const QString &p_text, FindOptions p_options)
|
||||
{
|
||||
if (p_options & FindOption::IncrementalSearch) {
|
||||
p_win->m_editor->peekText(p_text, toEditorFindFlags(p_options));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename _ViewWindow>
|
||||
static void handleFindNext(_ViewWindow *p_win, const QString &p_text, FindOptions p_options)
|
||||
{
|
||||
const auto result = p_win->m_editor->findText(p_text, toEditorFindFlags(p_options));
|
||||
p_win->showFindResult(p_text, result.m_totalMatches, result.m_currentMatchIndex);
|
||||
}
|
||||
|
||||
template <typename _ViewWindow>
|
||||
static void handleReplace(_ViewWindow *p_win, const QString &p_text, FindOptions p_options, const QString &p_replaceText)
|
||||
{
|
||||
const auto result = p_win->m_editor->replaceText(p_text, toEditorFindFlags(p_options), p_replaceText);
|
||||
p_win->showReplaceResult(p_text, result.m_totalMatches);
|
||||
}
|
||||
|
||||
template <typename _ViewWindow>
|
||||
static void handleReplaceAll(_ViewWindow *p_win, const QString &p_text, FindOptions p_options, const QString &p_replaceText)
|
||||
{
|
||||
const auto result = p_win->m_editor->replaceAll(p_text, toEditorFindFlags(p_options), p_replaceText);
|
||||
p_win->showReplaceResult(p_text, result.m_totalMatches);
|
||||
}
|
||||
|
||||
template <typename _ViewWindow>
|
||||
static void handleFindAndReplaceWidgetClosed(_ViewWindow *p_win)
|
||||
{
|
||||
p_win->m_editor->clearIncrementalSearchHighlight();
|
||||
p_win->m_editor->clearSearchHighlight();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -756,9 +756,8 @@ void ViewArea::setupGlobalShortcuts()
|
||||
|
||||
// CloseTab.
|
||||
{
|
||||
QKeySequence kseq(coreConfig.getShortcut(CoreConfig::CloseTab));
|
||||
if (!kseq.isEmpty()) {
|
||||
auto shortcut = new QShortcut(kseq, this);
|
||||
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::CloseTab), this);
|
||||
if (shortcut) {
|
||||
connect(shortcut, &QShortcut::activated,
|
||||
this, [this]() {
|
||||
auto win = getCurrentViewWindow();
|
||||
@ -771,9 +770,8 @@ void ViewArea::setupGlobalShortcuts()
|
||||
|
||||
// LocateNode.
|
||||
{
|
||||
QKeySequence kseq(coreConfig.getShortcut(CoreConfig::LocateNode));
|
||||
if (!kseq.isEmpty()) {
|
||||
auto shortcut = new QShortcut(kseq, this);
|
||||
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::LocateNode), this);
|
||||
if (shortcut) {
|
||||
connect(shortcut, &QShortcut::activated,
|
||||
this, [this]() {
|
||||
auto win = getCurrentViewWindow();
|
||||
|
@ -816,6 +816,29 @@ void ViewWindow::updateEditReadDiscardActionState(EditReadDiscardAction *p_act)
|
||||
|
||||
void ViewWindow::setupShortcuts()
|
||||
{
|
||||
const auto &editorConfig = ConfigMgr::getInst().getEditorConfig();
|
||||
|
||||
// FindNext.
|
||||
{
|
||||
auto shortcut = WidgetUtils::createShortcut(editorConfig.getShortcut(EditorConfig::FindNext), this, Qt::WidgetWithChildrenShortcut);
|
||||
if (shortcut) {
|
||||
connect(shortcut, &QShortcut::activated,
|
||||
this, [this]() {
|
||||
findNextOnLastFind(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// FindPrevious.
|
||||
{
|
||||
auto shortcut = WidgetUtils::createShortcut(editorConfig.getShortcut(EditorConfig::FindPrevious), this, Qt::WidgetWithChildrenShortcut);
|
||||
if (shortcut) {
|
||||
connect(shortcut, &QShortcut::activated,
|
||||
this, [this]() {
|
||||
findNextOnLastFind(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewWindow::wheelEvent(QWheelEvent *p_event)
|
||||
@ -849,6 +872,23 @@ void ViewWindow::showFindAndReplaceWidget()
|
||||
if (!m_findAndReplace) {
|
||||
m_findAndReplace = new FindAndReplaceWidget(this);
|
||||
m_mainLayout->addWidget(m_findAndReplace);
|
||||
|
||||
// Connect it to slots.
|
||||
connect(m_findAndReplace, &FindAndReplaceWidget::findTextChanged,
|
||||
this, &ViewWindow::handleFindTextChanged);
|
||||
connect(m_findAndReplace, &FindAndReplaceWidget::findNextRequested,
|
||||
this, &ViewWindow::findNext);
|
||||
connect(m_findAndReplace, &FindAndReplaceWidget::replaceRequested,
|
||||
this, &ViewWindow::replace);
|
||||
connect(m_findAndReplace, &FindAndReplaceWidget::replaceAllRequested,
|
||||
this, &ViewWindow::replaceAll);
|
||||
connect(m_findAndReplace, &FindAndReplaceWidget::closed,
|
||||
this, [this]() {
|
||||
setFocus();
|
||||
handleFindAndReplaceWidgetClosed();
|
||||
});
|
||||
connect(m_findAndReplace, &FindAndReplaceWidget::opened,
|
||||
this, &ViewWindow::handleFindAndReplaceWidgetOpened);
|
||||
}
|
||||
|
||||
m_findAndReplace->open(QString());
|
||||
@ -881,3 +921,85 @@ bool ViewWindow::findAndReplaceWidgetVisible() const
|
||||
{
|
||||
return m_findAndReplace && m_findAndReplace->isVisible();
|
||||
}
|
||||
|
||||
void ViewWindow::handleFindTextChanged(const QString &p_text, FindOptions p_options)
|
||||
{
|
||||
}
|
||||
|
||||
void ViewWindow::handleFindNext(const QString &p_text, FindOptions p_options)
|
||||
{
|
||||
}
|
||||
|
||||
void ViewWindow::handleReplace(const QString &p_text, FindOptions p_options, const QString &p_replaceText)
|
||||
{
|
||||
}
|
||||
|
||||
void ViewWindow::handleReplaceAll(const QString &p_text, FindOptions p_options, const QString &p_replaceText)
|
||||
{
|
||||
}
|
||||
|
||||
void ViewWindow::handleFindAndReplaceWidgetClosed()
|
||||
{
|
||||
}
|
||||
|
||||
void ViewWindow::handleFindAndReplaceWidgetOpened()
|
||||
{
|
||||
}
|
||||
|
||||
void ViewWindow::findNextOnLastFind(bool p_forward)
|
||||
{
|
||||
// Check if need to update the find info.
|
||||
if (m_findAndReplace && m_findAndReplace->isVisible()) {
|
||||
m_findInfo.m_text = m_findAndReplace->getFindText();
|
||||
m_findInfo.m_options = m_findAndReplace->getOptions();
|
||||
}
|
||||
|
||||
if (m_findInfo.m_text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_forward) {
|
||||
handleFindNext(m_findInfo.m_text, m_findInfo.m_options & ~FindOption::FindBackward);
|
||||
} else {
|
||||
handleFindNext(m_findInfo.m_text, m_findInfo.m_options | FindOption::FindBackward);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewWindow::findNext(const QString &p_text, FindOptions p_options)
|
||||
{
|
||||
m_findInfo.m_text = p_text;
|
||||
m_findInfo.m_options = p_options;
|
||||
handleFindNext(p_text, p_options);
|
||||
}
|
||||
|
||||
void ViewWindow::replace(const QString &p_text, FindOptions p_options, const QString &p_replaceText)
|
||||
{
|
||||
m_findInfo.m_text = p_text;
|
||||
m_findInfo.m_options = p_options;
|
||||
handleReplace(p_text, p_options, p_replaceText);
|
||||
}
|
||||
|
||||
void ViewWindow::replaceAll(const QString &p_text, FindOptions p_options, const QString &p_replaceText)
|
||||
{
|
||||
m_findInfo.m_text = p_text;
|
||||
m_findInfo.m_options = p_options;
|
||||
handleReplaceAll(p_text, p_options, p_replaceText);
|
||||
}
|
||||
|
||||
void ViewWindow::showFindResult(const QString &p_text, int p_totalMatches, int p_currentMatchIndex)
|
||||
{
|
||||
if (p_totalMatches == 0) {
|
||||
VNoteX::getInst().showStatusMessageShort(tr("Pattern not found: %1").arg(p_text));
|
||||
} else {
|
||||
VNoteX::getInst().showStatusMessageShort(tr("Match found: %1/%2").arg(p_currentMatchIndex + 1).arg(p_totalMatches));
|
||||
}
|
||||
}
|
||||
|
||||
void ViewWindow::showReplaceResult(const QString &p_text, int p_totalReplaces)
|
||||
{
|
||||
if (p_totalReplaces == 0) {
|
||||
VNoteX::getInst().showStatusMessageShort(tr("Pattern not found: %1").arg(p_text));
|
||||
} else {
|
||||
VNoteX::getInst().showStatusMessageShort(tr("Replaced %n match(es)", "", p_totalReplaces));
|
||||
}
|
||||
}
|
||||
|
@ -77,6 +77,12 @@ namespace vnotex
|
||||
public slots:
|
||||
virtual void handleEditorConfigChange() = 0;
|
||||
|
||||
void findNext(const QString &p_text, FindOptions p_options);
|
||||
|
||||
void replace(const QString &p_text, FindOptions p_options, const QString &p_replaceText);
|
||||
|
||||
void replaceAll(const QString &p_text, FindOptions p_options, const QString &p_replaceText);
|
||||
|
||||
signals:
|
||||
// Emit when the attached buffer is changed.
|
||||
void bufferChanged();
|
||||
@ -130,6 +136,18 @@ namespace vnotex
|
||||
// Handle all kinds of type action.
|
||||
virtual void handleTypeAction(TypeAction p_action);
|
||||
|
||||
virtual void handleFindTextChanged(const QString &p_text, FindOptions p_options);
|
||||
|
||||
virtual void handleFindNext(const QString &p_text, FindOptions p_options);
|
||||
|
||||
virtual void handleReplace(const QString &p_text, FindOptions p_options, const QString &p_replaceText);
|
||||
|
||||
virtual void handleReplaceAll(const QString &p_text, FindOptions p_options, const QString &p_replaceText);
|
||||
|
||||
virtual void handleFindAndReplaceWidgetClosed();
|
||||
|
||||
virtual void handleFindAndReplaceWidgetOpened();
|
||||
|
||||
protected:
|
||||
void setCentralWidget(QWidget *p_widget);
|
||||
|
||||
@ -183,6 +201,11 @@ namespace vnotex
|
||||
|
||||
bool findAndReplaceWidgetVisible() const;
|
||||
|
||||
// @p_currentMatchIndex: 0-based.
|
||||
static void showFindResult(const QString &p_text, int p_totalMatches, int p_currentMatchIndex);
|
||||
|
||||
static void showReplaceResult(const QString &p_text, int p_totalReplaces);
|
||||
|
||||
static ViewWindow::Mode modeFromOpenParameters(const FileOpenParameters &p_paras);
|
||||
|
||||
QSharedPointer<QWidget> m_statusWidget;
|
||||
@ -196,7 +219,16 @@ namespace vnotex
|
||||
|
||||
Mode m_mode = Mode::Invalid;
|
||||
|
||||
// Managed by QObject.
|
||||
FindAndReplaceWidget *m_findAndReplace = nullptr;
|
||||
|
||||
private:
|
||||
struct FindInfo
|
||||
{
|
||||
QString m_text;
|
||||
FindOptions m_options;
|
||||
};
|
||||
|
||||
void setupUI();
|
||||
|
||||
void initIcons();
|
||||
@ -236,6 +268,8 @@ namespace vnotex
|
||||
};
|
||||
int checkFileMissingOrChangedOutside();
|
||||
|
||||
void findNextOnLastFind(bool p_forward = true);
|
||||
|
||||
static ViewWindow::TypeAction toolBarActionToTypeAction(ViewWindowToolBarHelper::Action p_action);
|
||||
|
||||
Buffer *m_buffer = nullptr;
|
||||
@ -269,8 +303,8 @@ namespace vnotex
|
||||
// Whether check file missing or changed outside.
|
||||
bool m_fileChangeCheckEnabled = true;
|
||||
|
||||
// Managed by QObject.
|
||||
FindAndReplaceWidget *m_findAndReplace = nullptr;
|
||||
// Last find info.
|
||||
FindInfo m_findInfo;
|
||||
|
||||
static QIcon s_savedIcon;
|
||||
static QIcon s_modifiedIcon;
|
||||
|
Loading…
x
Reference in New Issue
Block a user