mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +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,
|
Outline,
|
||||||
RichPaste,
|
RichPaste,
|
||||||
FindAndReplace,
|
FindAndReplace,
|
||||||
|
FindNext,
|
||||||
|
FindPrevious,
|
||||||
MaxShortcut
|
MaxShortcut
|
||||||
};
|
};
|
||||||
Q_ENUM(Shortcut)
|
Q_ENUM(Shortcut)
|
||||||
|
@ -64,10 +64,11 @@ namespace vnotex
|
|||||||
enum FindOption
|
enum FindOption
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
CaseSensitive = 0x1U,
|
FindBackward = 0x1U,
|
||||||
WholeWordOnly = 0x2U,
|
CaseSensitive = 0x2U,
|
||||||
RegularExpression = 0x4U,
|
WholeWordOnly = 0x4U,
|
||||||
IncrementalSearch = 0x8U
|
RegularExpression = 0x8U,
|
||||||
|
IncrementalSearch = 0x10U
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(FindOptions, FindOption);
|
Q_DECLARE_FLAGS(FindOptions, FindOption);
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <core/markdowneditorconfig.h>
|
#include <core/markdowneditorconfig.h>
|
||||||
#include <core/configmgr.h>
|
#include <core/configmgr.h>
|
||||||
|
#include <utils/utils.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
#include <utils/pathutils.h>
|
#include <utils/pathutils.h>
|
||||||
#include <core/thememgr.h>
|
#include <core/thememgr.h>
|
||||||
@ -16,11 +17,11 @@ HtmlTemplateHelper::Template HtmlTemplateHelper::s_markdownViewerTemplate;
|
|||||||
QString WebGlobalOptions::toJavascriptObject() const
|
QString WebGlobalOptions::toJavascriptObject() const
|
||||||
{
|
{
|
||||||
return QStringLiteral("window.vxOptions = {\n")
|
return QStringLiteral("window.vxOptions = {\n")
|
||||||
+ QString("webPlantUml: %1,\n").arg(boolToString(m_webPlantUml))
|
+ QString("webPlantUml: %1,\n").arg(Utils::boolToString(m_webPlantUml))
|
||||||
+ QString("webGraphviz: %1,\n").arg(boolToString(m_webGraphviz))
|
+ QString("webGraphviz: %1,\n").arg(Utils::boolToString(m_webGraphviz))
|
||||||
+ QString("constrainImageWidthEnabled: %1,\n").arg(boolToString(m_constrainImageWidthEnabled))
|
+ QString("constrainImageWidthEnabled: %1,\n").arg(Utils::boolToString(m_constrainImageWidthEnabled))
|
||||||
+ QString("protectFromXss: %1,\n").arg(boolToString(m_protectFromXss))
|
+ QString("protectFromXss: %1,\n").arg(Utils::boolToString(m_protectFromXss))
|
||||||
+ QString("sectionNumberEnabled: %1\n").arg(boolToString(m_sectionNumberEnabled))
|
+ QString("sectionNumberEnabled: %1\n").arg(Utils::boolToString(m_sectionNumberEnabled))
|
||||||
+ QStringLiteral("}");
|
+ QStringLiteral("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,11 +20,6 @@ namespace vnotex
|
|||||||
|
|
||||||
bool m_protectFromXss = false;
|
bool m_protectFromXss = false;
|
||||||
|
|
||||||
QString boolToString(bool p_val) const
|
|
||||||
{
|
|
||||||
return p_val ? QStringLiteral("true") : QStringLiteral("false");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString toJavascriptObject() const;
|
QString toJavascriptObject() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ QJsonObject WidgetConfig::toJson() const
|
|||||||
{
|
{
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
obj[QStringLiteral("outline_auto_expanded_level")] = m_outlineAutoExpandedLevel;
|
obj[QStringLiteral("outline_auto_expanded_level")] = m_outlineAutoExpandedLevel;
|
||||||
|
obj[QStringLiteral("find_and_replace_options")] = static_cast<int>(m_findAndReplaceOptions);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,9 @@
|
|||||||
"TypeTable" : "Ctrl+/",
|
"TypeTable" : "Ctrl+/",
|
||||||
"Outline" : "Ctrl+G, O",
|
"Outline" : "Ctrl+G, O",
|
||||||
"RichPaste" : "Ctrl+Shift+V",
|
"RichPaste" : "Ctrl+Shift+V",
|
||||||
"FindAndReplace" : "Ctrl+F"
|
"FindAndReplace" : "Ctrl+F",
|
||||||
|
"FindNext" : "F3",
|
||||||
|
"FindPrevious" : "Shift+F3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"text_editor" : {
|
"text_editor" : {
|
||||||
@ -195,6 +197,14 @@
|
|||||||
"web/js/turndown/turndown-plugin-gfm.js",
|
"web/js/turndown/turndown-plugin-gfm.js",
|
||||||
"web/js/turndown.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)",
|
"//comment" : "Level of the heading in outline that should expand to automatically (1-6)",
|
||||||
"outline_auto_expanded_level" : 6,
|
"outline_auto_expanded_level" : 6,
|
||||||
"//comment" : "Default find options in FindAndReplace",
|
"//comment" : "Default find options in FindAndReplace",
|
||||||
"find_and_replace_options" : 8
|
"find_and_replace_options" : 16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<p>
|
<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/>
|
<br/><br/>
|
||||||
Source code of VNote could be found at <a href="https://github.com/vnotex/vnote">GitHub</a>.
|
Source code of VNote could be found at <a href="https://github.com/vnotex/vnote">GitHub</a>.
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
@ -63,6 +63,8 @@
|
|||||||
<file>web/js/turndown/turndown.js</file>
|
<file>web/js/turndown/turndown.js</file>
|
||||||
<file>web/js/turndown/turndown-plugin-gfm.js</file>
|
<file>web/js/turndown/turndown-plugin-gfm.js</file>
|
||||||
<file>web/js/turndown.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/markdown-default.theme</file>
|
||||||
<file>syntax-highlighting/themes/default.theme</file>
|
<file>syntax-highlighting/themes/default.theme</file>
|
||||||
<file>syntax-highlighting/themes/breeze-dark.theme</file>
|
<file>syntax-highlighting/themes/breeze-dark.theme</file>
|
||||||
|
@ -140,35 +140,5 @@
|
|||||||
"text-color" : "#006e28",
|
"text-color" : "#006e28",
|
||||||
"selected-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" : {
|
"FoldingHighlight" : {
|
||||||
"text-color" : "#ffa9c4f5"
|
"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",
|
"//comment" : "Override the Text style in editor-styles",
|
||||||
|
@ -290,3 +290,13 @@ span.modal-close:hover,
|
|||||||
span.modal-close:focus {
|
span.modal-close:focus {
|
||||||
color: #222222;
|
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);
|
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');
|
console.log('QWebChannel has been set up');
|
||||||
if (window.vnotex.initialized) {
|
if (window.vnotex.initialized) {
|
||||||
window.vnotex.kickOffMarkdown();
|
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");
|
this.headingNodes = this.container.querySelectorAll("h1, h2, h3, h4, h5, h6");
|
||||||
let headings = [];
|
let headings = [];
|
||||||
let needSectionNumber = window.vxOptions.sectionNumberEnabled;
|
let needSectionNumber = window.vxOptions.sectionNumberEnabled;
|
||||||
let regExp = /^(?:\d\.)+ /;
|
let regExp = /^\d(?:\.\d)*\.? /;
|
||||||
for (let i = 0; i < this.headingNodes.length; ++i) {
|
for (let i = 0; i < this.headingNodes.length; ++i) {
|
||||||
let node = this.headingNodes[i];
|
let node = this.headingNodes[i];
|
||||||
headings.push({
|
headings.push({
|
||||||
|
@ -99,4 +99,14 @@ class Utils {
|
|||||||
height: rect.height
|
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.crossCopyer = new CrossCopy(this);
|
||||||
|
|
||||||
|
this.searcher = new MarkJs(this, this.contentContainer);
|
||||||
|
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
|
|
||||||
// Signal out.
|
// Signal out.
|
||||||
@ -250,6 +252,14 @@ class VNoteX extends EventEmitter {
|
|||||||
window.vxMarkdownAdapter.setCrossCopyResult(p_id, p_timeStamp, p_html);
|
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() {
|
static detectOS() {
|
||||||
let osName="Unknown OS";
|
let osName="Unknown OS";
|
||||||
if (navigator.appVersion.indexOf("Win")!=-1) {
|
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)
|
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();
|
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);
|
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);
|
qreal p_scaleFactor);
|
||||||
|
|
||||||
static bool fuzzyEqual(qreal p_a, qreal p_b);
|
static bool fuzzyEqual(qreal p_a, qreal p_b);
|
||||||
|
|
||||||
|
static QString boolToString(bool p_val);
|
||||||
};
|
};
|
||||||
} // ns vnotex
|
} // ns vnotex
|
||||||
|
|
||||||
|
@ -55,6 +55,16 @@ MarkdownViewerAdapter::Heading MarkdownViewerAdapter::Heading::fromJson(const QJ
|
|||||||
p_obj.value(QStringLiteral("anchor")).toString());
|
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)
|
MarkdownViewerAdapter::MarkdownViewerAdapter(QObject *p_parent)
|
||||||
: 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);
|
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;
|
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);
|
explicit MarkdownViewerAdapter(QObject *p_parent = nullptr);
|
||||||
|
|
||||||
virtual ~MarkdownViewerAdapter();
|
virtual ~MarkdownViewerAdapter();
|
||||||
@ -102,6 +115,8 @@ namespace vnotex
|
|||||||
|
|
||||||
const QStringList &getCrossCopyTargets() const;
|
const QStringList &getCrossCopyTargets() const;
|
||||||
|
|
||||||
|
void findText(const QString &p_text, FindOptions p_options);
|
||||||
|
|
||||||
// Functions to be called from web side.
|
// Functions to be called from web side.
|
||||||
public slots:
|
public slots:
|
||||||
void setReady(bool p_ready);
|
void setReady(bool p_ready);
|
||||||
@ -142,6 +157,8 @@ namespace vnotex
|
|||||||
|
|
||||||
void setCrossCopyResult(quint64 p_id, quint64 p_timeStamp, const QString &p_html);
|
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 to be connected at web side.
|
||||||
signals:
|
signals:
|
||||||
// Current Markdown text is updated.
|
// Current Markdown text is updated.
|
||||||
@ -173,6 +190,8 @@ namespace vnotex
|
|||||||
const QString &p_baseUrl,
|
const QString &p_baseUrl,
|
||||||
const QString &p_html);
|
const QString &p_html);
|
||||||
|
|
||||||
|
void findTextRequested(const QString &p_text, const QJsonObject &p_options);
|
||||||
|
|
||||||
// Signals to be connected at cpp side.
|
// Signals to be connected at cpp side.
|
||||||
signals:
|
signals:
|
||||||
void graphPreviewDataReady(const PreviewData &p_data);
|
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 crossCopyReady(quint64 p_id, quint64 p_timeStamp, const QString &p_html);
|
||||||
|
|
||||||
|
void findTextReady(const QString &p_text, int p_totalMatches, int p_currentMatchIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void scrollToLine(int p_lineNumber);
|
void scrollToLine(int p_lineNumber);
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ FindAndReplaceWidget::FindAndReplaceWidget(QWidget *p_parent)
|
|||||||
m_findTextTimer->setInterval(500);
|
m_findTextTimer->setInterval(500);
|
||||||
connect(m_findTextTimer, &QTimer::timeout,
|
connect(m_findTextTimer, &QTimer::timeout,
|
||||||
this, [this]() {
|
this, [this]() {
|
||||||
emit findTextChanged(m_findLineEdit->text(), m_options);
|
emit findTextChanged(getFindText(), getOptions());
|
||||||
});
|
});
|
||||||
|
|
||||||
setupUI();
|
setupUI();
|
||||||
@ -155,6 +155,7 @@ void FindAndReplaceWidget::setupUI()
|
|||||||
void FindAndReplaceWidget::close()
|
void FindAndReplaceWidget::close()
|
||||||
{
|
{
|
||||||
hide();
|
hide();
|
||||||
|
emit closed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FindAndReplaceWidget::setReplaceEnabled(bool p_enabled)
|
void FindAndReplaceWidget::setReplaceEnabled(bool p_enabled)
|
||||||
@ -198,47 +199,82 @@ void FindAndReplaceWidget::keyPressEvent(QKeyEvent *p_event)
|
|||||||
|
|
||||||
void FindAndReplaceWidget::findNext()
|
void FindAndReplaceWidget::findNext()
|
||||||
{
|
{
|
||||||
|
m_findTextTimer->stop();
|
||||||
|
auto text = m_findLineEdit->text();
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit findNextRequested(text, m_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FindAndReplaceWidget::findPrevious()
|
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()
|
void FindAndReplaceWidget::updateFindOptions()
|
||||||
{
|
{
|
||||||
m_options = FindOption::None;
|
if (m_optionCheckBoxMuted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FindOptions options = FindOption::None;
|
||||||
|
|
||||||
if (m_caseSensitiveCheckBox->isChecked()) {
|
if (m_caseSensitiveCheckBox->isChecked()) {
|
||||||
m_options |= FindOption::CaseSensitive;
|
options |= FindOption::CaseSensitive;
|
||||||
}
|
}
|
||||||
if (m_wholeWordOnlyCheckBox->isChecked()) {
|
if (m_wholeWordOnlyCheckBox->isChecked()) {
|
||||||
m_options |= FindOption::WholeWordOnly;
|
options |= FindOption::WholeWordOnly;
|
||||||
}
|
}
|
||||||
if (m_regularExpressionCheckBox->isChecked()) {
|
if (m_regularExpressionCheckBox->isChecked()) {
|
||||||
m_options |= FindOption::RegularExpression;
|
options |= FindOption::RegularExpression;
|
||||||
}
|
}
|
||||||
if (m_incrementalSearchCheckBox->isChecked()) {
|
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);
|
ConfigMgr::getInst().getWidgetConfig().setFindAndReplaceOptions(m_options);
|
||||||
|
m_findTextTimer->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FindAndReplaceWidget::replace()
|
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()
|
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()
|
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)
|
void FindAndReplaceWidget::setFindOptions(FindOptions p_options)
|
||||||
@ -247,11 +283,13 @@ void FindAndReplaceWidget::setFindOptions(FindOptions p_options)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_options = p_options;
|
m_optionCheckBoxMuted = true;
|
||||||
|
m_options = p_options & ~FindOption::FindBackward;
|
||||||
m_caseSensitiveCheckBox->setChecked(m_options & FindOption::CaseSensitive);
|
m_caseSensitiveCheckBox->setChecked(m_options & FindOption::CaseSensitive);
|
||||||
m_wholeWordOnlyCheckBox->setChecked(m_options & FindOption::WholeWordOnly);
|
m_wholeWordOnlyCheckBox->setChecked(m_options & FindOption::WholeWordOnly);
|
||||||
m_regularExpressionCheckBox->setChecked(m_options & FindOption::RegularExpression);
|
m_regularExpressionCheckBox->setChecked(m_options & FindOption::RegularExpression);
|
||||||
m_incrementalSearchCheckBox->setChecked(m_options & FindOption::IncrementalSearch);
|
m_incrementalSearchCheckBox->setChecked(m_options & FindOption::IncrementalSearch);
|
||||||
|
m_optionCheckBoxMuted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FindAndReplaceWidget::open(const QString &p_text)
|
void FindAndReplaceWidget::open(const QString &p_text)
|
||||||
@ -264,4 +302,16 @@ void FindAndReplaceWidget::open(const QString &p_text)
|
|||||||
|
|
||||||
m_findLineEdit->setFocus();
|
m_findLineEdit->setFocus();
|
||||||
m_findLineEdit->selectAll();
|
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();
|
void close();
|
||||||
|
|
||||||
|
QString getFindText() const;
|
||||||
|
|
||||||
|
FindOptions getOptions() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void findTextChanged(const QString &p_text, FindOptions p_options);
|
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:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
@ -66,6 +80,8 @@ namespace vnotex
|
|||||||
FindOptions m_options = FindOption::None;
|
FindOptions m_options = FindOption::None;
|
||||||
|
|
||||||
QTimer *m_findTextTimer = nullptr;
|
QTimer *m_findTextTimer = nullptr;
|
||||||
|
|
||||||
|
bool m_optionCheckBoxMuted = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
#include <core/vnotex.h>
|
#include <core/vnotex.h>
|
||||||
#include <core/thememgr.h>
|
#include <core/thememgr.h>
|
||||||
#include "editors/markdowneditor.h"
|
#include "editors/markdowneditor.h"
|
||||||
#include "textviewwindow.h"
|
|
||||||
#include "textviewwindowhelper.h"
|
#include "textviewwindowhelper.h"
|
||||||
#include "editors/markdownviewer.h"
|
#include "editors/markdownviewer.h"
|
||||||
#include "editors/editormarkdownvieweradapter.h"
|
#include "editors/editormarkdownvieweradapter.h"
|
||||||
@ -28,6 +27,7 @@
|
|||||||
#include "dialogs/deleteconfirmdialog.h"
|
#include "dialogs/deleteconfirmdialog.h"
|
||||||
#include "outlineprovider.h"
|
#include "outlineprovider.h"
|
||||||
#include "toolbarhelper.h"
|
#include "toolbarhelper.h"
|
||||||
|
#include "findandreplacewidget.h"
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
@ -82,6 +82,10 @@ void MarkdownViewWindow::setupUI()
|
|||||||
void MarkdownViewWindow::setMode(Mode p_mode)
|
void MarkdownViewWindow::setMode(Mode p_mode)
|
||||||
{
|
{
|
||||||
setModeInternal(p_mode);
|
setModeInternal(p_mode);
|
||||||
|
|
||||||
|
if (m_findAndReplace && m_findAndReplace->isVisible()) {
|
||||||
|
m_findAndReplace->setReplaceEnabled(m_mode != Mode::Read);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkdownViewWindow::setModeInternal(Mode p_mode)
|
void MarkdownViewWindow::setModeInternal(Mode p_mode)
|
||||||
@ -278,6 +282,7 @@ void MarkdownViewWindow::setupToolBar()
|
|||||||
toolBar->addSeparator();
|
toolBar->addSeparator();
|
||||||
|
|
||||||
ToolBarHelper::addSpacer(toolBar);
|
ToolBarHelper::addSpacer(toolBar);
|
||||||
|
addAction(toolBar, ViewWindowToolBarHelper::FindAndReplace);
|
||||||
addAction(toolBar, ViewWindowToolBarHelper::Outline);
|
addAction(toolBar, ViewWindowToolBarHelper::Outline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,6 +423,8 @@ void MarkdownViewWindow::setupViewer()
|
|||||||
m_outlineProvider->setCurrentHeadingIndex(this->adapter()->getCurrentHeadingIndex());
|
m_outlineProvider->setCurrentHeadingIndex(this->adapter()->getCurrentHeadingIndex());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
connect(adapter, &MarkdownViewerAdapter::findTextReady,
|
||||||
|
this, &ViewWindow::showFindResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkdownViewWindow::syncTextEditorFromBuffer(bool p_syncPositionFromReadMode)
|
void MarkdownViewWindow::syncTextEditorFromBuffer(bool p_syncPositionFromReadMode)
|
||||||
@ -792,3 +799,56 @@ void MarkdownViewWindow::zoom(bool p_zoomIn)
|
|||||||
textEditorConfig.setZoomDelta(m_editor->zoomDelta());
|
textEditorConfig.setZoomDelta(m_editor->zoomDelta());
|
||||||
showZoomDelta(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 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:
|
protected:
|
||||||
void syncEditorFromBuffer() Q_DECL_OVERRIDE;
|
void syncEditorFromBuffer() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
@ -181,3 +181,28 @@ void TextViewWindow::zoom(bool p_zoomIn)
|
|||||||
textEditorConfig.setZoomDelta(m_editor->zoomDelta());
|
textEditorConfig.setZoomDelta(m_editor->zoomDelta());
|
||||||
showZoomDelta(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 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:
|
protected:
|
||||||
void syncEditorFromBuffer() Q_DECL_OVERRIDE;
|
void syncEditorFromBuffer() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
@ -127,6 +127,60 @@ namespace vnotex
|
|||||||
|
|
||||||
return editorConfig;
|
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.
|
// CloseTab.
|
||||||
{
|
{
|
||||||
QKeySequence kseq(coreConfig.getShortcut(CoreConfig::CloseTab));
|
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::CloseTab), this);
|
||||||
if (!kseq.isEmpty()) {
|
if (shortcut) {
|
||||||
auto shortcut = new QShortcut(kseq, this);
|
|
||||||
connect(shortcut, &QShortcut::activated,
|
connect(shortcut, &QShortcut::activated,
|
||||||
this, [this]() {
|
this, [this]() {
|
||||||
auto win = getCurrentViewWindow();
|
auto win = getCurrentViewWindow();
|
||||||
@ -771,9 +770,8 @@ void ViewArea::setupGlobalShortcuts()
|
|||||||
|
|
||||||
// LocateNode.
|
// LocateNode.
|
||||||
{
|
{
|
||||||
QKeySequence kseq(coreConfig.getShortcut(CoreConfig::LocateNode));
|
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::LocateNode), this);
|
||||||
if (!kseq.isEmpty()) {
|
if (shortcut) {
|
||||||
auto shortcut = new QShortcut(kseq, this);
|
|
||||||
connect(shortcut, &QShortcut::activated,
|
connect(shortcut, &QShortcut::activated,
|
||||||
this, [this]() {
|
this, [this]() {
|
||||||
auto win = getCurrentViewWindow();
|
auto win = getCurrentViewWindow();
|
||||||
|
@ -816,6 +816,29 @@ void ViewWindow::updateEditReadDiscardActionState(EditReadDiscardAction *p_act)
|
|||||||
|
|
||||||
void ViewWindow::setupShortcuts()
|
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)
|
void ViewWindow::wheelEvent(QWheelEvent *p_event)
|
||||||
@ -849,6 +872,23 @@ void ViewWindow::showFindAndReplaceWidget()
|
|||||||
if (!m_findAndReplace) {
|
if (!m_findAndReplace) {
|
||||||
m_findAndReplace = new FindAndReplaceWidget(this);
|
m_findAndReplace = new FindAndReplaceWidget(this);
|
||||||
m_mainLayout->addWidget(m_findAndReplace);
|
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());
|
m_findAndReplace->open(QString());
|
||||||
@ -881,3 +921,85 @@ bool ViewWindow::findAndReplaceWidgetVisible() const
|
|||||||
{
|
{
|
||||||
return m_findAndReplace && m_findAndReplace->isVisible();
|
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:
|
public slots:
|
||||||
virtual void handleEditorConfigChange() = 0;
|
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:
|
signals:
|
||||||
// Emit when the attached buffer is changed.
|
// Emit when the attached buffer is changed.
|
||||||
void bufferChanged();
|
void bufferChanged();
|
||||||
@ -130,6 +136,18 @@ namespace vnotex
|
|||||||
// Handle all kinds of type action.
|
// Handle all kinds of type action.
|
||||||
virtual void handleTypeAction(TypeAction p_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:
|
protected:
|
||||||
void setCentralWidget(QWidget *p_widget);
|
void setCentralWidget(QWidget *p_widget);
|
||||||
|
|
||||||
@ -183,6 +201,11 @@ namespace vnotex
|
|||||||
|
|
||||||
bool findAndReplaceWidgetVisible() const;
|
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);
|
static ViewWindow::Mode modeFromOpenParameters(const FileOpenParameters &p_paras);
|
||||||
|
|
||||||
QSharedPointer<QWidget> m_statusWidget;
|
QSharedPointer<QWidget> m_statusWidget;
|
||||||
@ -196,7 +219,16 @@ namespace vnotex
|
|||||||
|
|
||||||
Mode m_mode = Mode::Invalid;
|
Mode m_mode = Mode::Invalid;
|
||||||
|
|
||||||
|
// Managed by QObject.
|
||||||
|
FindAndReplaceWidget *m_findAndReplace = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct FindInfo
|
||||||
|
{
|
||||||
|
QString m_text;
|
||||||
|
FindOptions m_options;
|
||||||
|
};
|
||||||
|
|
||||||
void setupUI();
|
void setupUI();
|
||||||
|
|
||||||
void initIcons();
|
void initIcons();
|
||||||
@ -236,6 +268,8 @@ namespace vnotex
|
|||||||
};
|
};
|
||||||
int checkFileMissingOrChangedOutside();
|
int checkFileMissingOrChangedOutside();
|
||||||
|
|
||||||
|
void findNextOnLastFind(bool p_forward = true);
|
||||||
|
|
||||||
static ViewWindow::TypeAction toolBarActionToTypeAction(ViewWindowToolBarHelper::Action p_action);
|
static ViewWindow::TypeAction toolBarActionToTypeAction(ViewWindowToolBarHelper::Action p_action);
|
||||||
|
|
||||||
Buffer *m_buffer = nullptr;
|
Buffer *m_buffer = nullptr;
|
||||||
@ -269,8 +303,8 @@ namespace vnotex
|
|||||||
// Whether check file missing or changed outside.
|
// Whether check file missing or changed outside.
|
||||||
bool m_fileChangeCheckEnabled = true;
|
bool m_fileChangeCheckEnabled = true;
|
||||||
|
|
||||||
// Managed by QObject.
|
// Last find info.
|
||||||
FindAndReplaceWidget *m_findAndReplace = nullptr;
|
FindInfo m_findInfo;
|
||||||
|
|
||||||
static QIcon s_savedIcon;
|
static QIcon s_savedIcon;
|
||||||
static QIcon s_modifiedIcon;
|
static QIcon s_modifiedIcon;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user