support search wrap

Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
Le Tan 2017-01-09 23:03:38 +08:00
parent 7ed92ed7eb
commit 3769ac5311
6 changed files with 194 additions and 114 deletions

View File

@ -22,7 +22,7 @@ void VFindReplaceDialog::setupUI()
titleLayout->setSpacing(0);
// Find
QLabel *findLabel = new QLabel(tr("&Find:"));
QLabel *findLabel = new QLabel(tr("Find:"));
m_findEdit = new QLineEdit();
m_findEdit->setPlaceholderText(tr("Enter text to search"));
findLabel->setBuddy(m_findEdit);
@ -37,7 +37,7 @@ void VFindReplaceDialog::setupUI()
m_replaceEdit = new QLineEdit();
m_replaceEdit->setPlaceholderText(tr("Enter text to replace with"));
replaceLabel->setBuddy(m_replaceEdit);
m_replaceBtn = new QPushButton(tr("R&eplace"));
m_replaceBtn = new QPushButton(tr("Replace"));
m_replaceBtn->setProperty("FlatBtn", true);
m_replaceFindBtn = new QPushButton(tr("Replace && Fin&d"));
m_replaceFindBtn->setProperty("FlatBtn", true);

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<path d="M273.4,300.5l-0.3,58c48.9-8.2,86.3-51,86.3-102.5c0-15.9-3.6-31-10-44.5c-2.8-5.8-6-11.3-9.8-16.5l47.1-43.5
c1.1,1.3,2.1,2.7,3.1,4c20.9,28,33.2,62.8,33.2,100.5c0,1.2,0,2.5,0,3.7c-1.5,71.5-47.6,132-111.4,154.6
c-12.3,4.3-25.2,7.3-38.5,8.7l-0.1,57l-76.2-67L170.6,390l44.4-38.7L273.4,300.5z"/>
<path d="M89,252.3c1.6-72.1,48.3-133,112.9-155.2c11.7-4,24-6.8,36.8-8.1l0.1-57l76.1,66.9l26.2,23.1l-44.3,38.6l-58.4,50.9
l0.2-57.9c-48.8,8.3-86,51.1-86,102.4c0,16,3.6,31.1,10.1,44.7c2.7,5.8,6,11.2,9.7,16.3l-47,43.6c-1.3-1.6-2.6-3.3-3.8-5
C101.1,327.7,89,293.3,89,256C89,254.8,89,253.5,89,252.3z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -14,6 +14,18 @@ extern VConfigManager vconfig;
VEdit::VEdit(VFile *p_file, QWidget *p_parent)
: QTextEdit(p_parent), m_file(p_file), m_editOps(NULL)
{
const int labelTimerInterval = 500;
const int labelSize = 64;
QPixmap wrapPixmap(":/resources/icons/search_wrap.svg");
m_wrapLabel = new QLabel(this);
m_wrapLabel->setPixmap(wrapPixmap.scaled(labelSize, labelSize));
m_wrapLabel->hide();
m_labelTimer = new QTimer(this);
m_labelTimer->setSingleShot(true);
m_labelTimer->setInterval(labelTimerInterval);
connect(m_labelTimer, &QTimer::timeout,
this, &VEdit::labelTimerTimeout);
connect(document(), &QTextDocument::modificationChanged,
(VFile *)m_file, &VFile::setModified);
}
@ -85,14 +97,13 @@ void VEdit::insertImage()
}
}
bool VEdit::findText(const QString &p_text, uint p_options, bool p_peek,
bool p_forward)
bool VEdit::peekText(const QString &p_text, uint p_options)
{
static int startPos = textCursor().selectionStart();
static int lastPos = startPos;
bool found = false;
if (p_text.isEmpty() && p_peek) {
if (p_text.isEmpty()) {
// Clear previous selection
QTextCursor cursor = textCursor();
cursor.clearSelection();
@ -100,149 +111,186 @@ bool VEdit::findText(const QString &p_text, uint p_options, bool p_peek,
setTextCursor(cursor);
} else {
QTextCursor cursor = textCursor();
if (p_peek) {
int curPos = cursor.selectionStart();
if (curPos != lastPos) {
// Cursor has been moved. Just start at current position.
// Cursor has been moved. Just start at current potition.
startPos = curPos;
lastPos = curPos;
} else {
// Move cursor to startPos to search.
cursor.setPosition(startPos);
setTextCursor(cursor);
}
}
bool wrapped = false;
found = findTextHelper(p_text, p_options, true, wrapped);
if (found) {
lastPos = textCursor().selectionStart();
found = true;
}
return found;
}
bool VEdit::findTextHelper(const QString &p_text, uint p_options,
bool p_forward, bool &p_wrapped)
{
p_wrapped = false;
bool found = false;
// Options
QTextDocument::FindFlags flags;
QTextDocument::FindFlags findFlags;
bool caseSensitive = false;
if (p_options & FindOption::CaseSensitive) {
flags |= QTextDocument::FindCaseSensitively;
findFlags |= QTextDocument::FindCaseSensitively;
caseSensitive = true;
}
if (p_options & FindOption::WholeWordOnly) {
flags |= QTextDocument::FindWholeWords;
findFlags |= QTextDocument::FindWholeWords;
}
if (!p_forward) {
flags |= QTextDocument::FindBackward;
findFlags |= QTextDocument::FindBackward;
}
// Use regular expression
bool useRegExp = false;
QRegExp exp;
if (p_options & FindOption::RegularExpression) {
QRegExp exp(p_text,
p_options & FindOption::CaseSensitive ?
Qt::CaseSensitive : Qt::CaseInsensitive);
found = find(exp, flags);
useRegExp = true;
exp = QRegExp(p_text,
caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
}
QTextCursor cursor = textCursor();
while (!found) {
if (useRegExp) {
found = find(exp, findFlags);
} else {
found = find(p_text, flags);
found = find(p_text, findFlags);
}
cursor = textCursor();
if (!p_peek) {
startPos = cursor.selectionStart();
if (p_wrapped) {
if (!found) {
setTextCursor(cursor);
}
lastPos = cursor.selectionStart();
break;
}
if (!found) {
// Wrap to the other end of the document to search again.
p_wrapped = true;
QTextCursor wrapCursor = textCursor();
wrapCursor.clearSelection();
if (p_forward) {
wrapCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
} else {
wrapCursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
}
setTextCursor(wrapCursor);
}
}
return found;
}
bool VEdit::findText(const QString &p_text, uint p_options, bool p_forward)
{
bool found = false;
if (p_text.isEmpty()) {
QTextCursor cursor = textCursor();
cursor.clearSelection();
setTextCursor(cursor);
} else {
bool wrapped = false;
found = findTextHelper(p_text, p_options, p_forward, wrapped);
if (found && wrapped) {
showWrapLabel();
}
}
qDebug() << "findText" << p_text << p_options << p_forward
<< (found ? "Found" : "NotFound");
return found;
}
void VEdit::replaceText(const QString &p_text, uint p_options,
const QString &p_replaceText, bool p_findNext)
{
// Options
QTextDocument::FindFlags flags;
if (p_options & FindOption::CaseSensitive) {
flags |= QTextDocument::FindCaseSensitively;
}
if (p_options & FindOption::WholeWordOnly) {
flags |= QTextDocument::FindWholeWords;
}
bool useRegExp = false;
QRegExp exp;
if (p_options & FindOption::RegularExpression) {
useRegExp = true;
exp = QRegExp(p_text,
p_options & FindOption::CaseSensitive ?
Qt::CaseSensitive : Qt::CaseInsensitive);
}
QTextCursor cursor = textCursor();
if (cursor.hasSelection()) {
// Replace occurs only if the selected text matches @p_text with @p_options.
QTextCursor matchCursor;
if (useRegExp) {
matchCursor = document()->find(exp, cursor.selectionStart(),
flags);
} else {
matchCursor = document()->find(p_text, cursor.selectionStart(),
flags);
QTextCursor tmpCursor = cursor;
tmpCursor.setPosition(tmpCursor.selectionStart());
tmpCursor.clearSelection();
setTextCursor(tmpCursor);
bool wrapped = false;
bool found = findTextHelper(p_text, p_options, true, wrapped);
bool matched = false;
if (found) {
tmpCursor = textCursor();
matched = (cursor.selectionStart() == tmpCursor.selectionStart())
&& (cursor.selectionEnd() == tmpCursor.selectionEnd());
}
bool matched = (cursor.selectionStart() == matchCursor.selectionStart())
&& (cursor.selectionEnd() == matchCursor.selectionEnd());
if (matched) {
cursor.beginEditBlock();
cursor.removeSelectedText();
cursor.insertText(p_replaceText);
cursor.endEditBlock();
setTextCursor(cursor);
} else {
setTextCursor(cursor);
}
}
if (p_findNext) {
findText(p_text, p_options, false, true);
findText(p_text, p_options, true);
}
}
void VEdit::replaceTextAll(const QString &p_text, uint p_options,
const QString &p_replaceText)
{
// Options
QTextDocument::FindFlags flags;
if (p_options & FindOption::CaseSensitive) {
flags |= QTextDocument::FindCaseSensitively;
}
if (p_options & FindOption::WholeWordOnly) {
flags |= QTextDocument::FindWholeWords;
}
bool useRegExp = false;
QRegExp exp;
if (p_options & FindOption::RegularExpression) {
useRegExp = true;
exp = QRegExp(p_text,
p_options & FindOption::CaseSensitive ?
Qt::CaseSensitive : Qt::CaseInsensitive);
}
// Replace from the start to the end and resotre the cursor.
QTextCursor cursor = textCursor();
int pos = cursor.position();
int nrReplaces = 0;
int startPos = 0;
int lastMatch = -1;
QTextCursor tmpCursor = cursor;
tmpCursor.setPosition(0);
setTextCursor(tmpCursor);
while (true) {
QTextCursor matchCursor;
if (useRegExp) {
matchCursor = document()->find(exp, startPos, flags);
} else {
matchCursor = document()->find(p_text, startPos, flags);
}
if (matchCursor.isNull()) {
bool wrapped = false;
bool found = findTextHelper(p_text, p_options, true, wrapped);
if (!found) {
break;
} else {
if (matchCursor.selectionStart() <= lastMatch) {
if (wrapped) {
// Wrap back.
break;
} else {
lastMatch = matchCursor.selectionStart();
}
nrReplaces++;
matchCursor.beginEditBlock();
matchCursor.removeSelectedText();
matchCursor.insertText(p_replaceText);
matchCursor.endEditBlock();
setTextCursor(matchCursor);
startPos = matchCursor.position();
tmpCursor = textCursor();
tmpCursor.beginEditBlock();
tmpCursor.removeSelectedText();
tmpCursor.insertText(p_replaceText);
tmpCursor.endEditBlock();
setTextCursor(tmpCursor);
}
}
// Restore cursor position.
cursor.setPosition(pos);
cursor.clearSelection();
setTextCursor(cursor);
qDebug() << "replace all" << nrReplaces << "occurencs";
qDebug() << "replace all" << nrReplaces << "occurences";
}
void VEdit::showWrapLabel()
{
int labelW = m_wrapLabel->width();
int labelH = m_wrapLabel->height();
int x = (width() - labelW) / 2;
int y = (height() - labelH) / 2;
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
m_wrapLabel->move(x, y);
m_wrapLabel->show();
m_labelTimer->stop();
m_labelTimer->start();
}
void VEdit::labelTimerTimeout()
{
m_wrapLabel->hide();
}

View File

@ -9,6 +9,8 @@
#include "vfile.h"
class VEditOperations;
class QLabel;
class QTimer;
class VEdit : public QTextEdit
{
@ -26,16 +28,27 @@ public:
virtual void scrollToLine(int p_lineNumber);
// User requests to insert an image.
virtual void insertImage();
virtual bool findText(const QString &p_text, uint p_options, bool p_peek,
bool p_forward);
virtual void replaceText(const QString &p_text, uint p_options,
bool findTextHelper(const QString &p_text, uint p_options,
bool p_forward, bool &p_wrapped);
bool peekText(const QString &p_text, uint p_options);
bool findText(const QString &p_text, uint p_options, bool p_forward);
void replaceText(const QString &p_text, uint p_options,
const QString &p_replaceText, bool p_findNext);
virtual void replaceTextAll(const QString &p_text, uint p_options,
void replaceTextAll(const QString &p_text, uint p_options,
const QString &p_replaceText);
private slots:
void labelTimerTimeout();
protected:
QPointer<VFile> m_file;
VEditOperations *m_editOps;
private:
QLabel *m_wrapLabel;
QTimer *m_labelTimer;
void showWrapLabel();
};

View File

@ -463,7 +463,11 @@ void VEditTab::findText(const QString &p_text, uint p_options, bool p_peek,
bool p_forward)
{
if (isEditMode || !webPreviewer) {
m_textEditor->findText(p_text, p_options, p_peek, p_forward);
if (p_peek) {
m_textEditor->peekText(p_text, p_options);
} else {
m_textEditor->findText(p_text, p_options, p_forward);
}
} else {
findTextInWebView(p_text, p_options, p_peek, p_forward);
}
@ -486,7 +490,7 @@ void VEditTab::replaceTextAll(const QString &p_text, uint p_options,
}
void VEditTab::findTextInWebView(const QString &p_text, uint p_options,
bool p_peek, bool p_forward)
bool /* p_peek */, bool p_forward)
{
Q_ASSERT(webPreviewer);
QWebEnginePage::FindFlags flags;

View File

@ -83,5 +83,6 @@
<file>resources/icons/corner_tablist_cur.svg</file>
<file>resources/icons/close.svg</file>
<file>resources/icons/find_replace.svg</file>
<file>resources/icons/search_wrap.svg</file>
</qresource>
</RCC>