diff --git a/src/dialog/vfindreplacedialog.cpp b/src/dialog/vfindreplacedialog.cpp index 937736bd..69b01eff 100644 --- a/src/dialog/vfindreplacedialog.cpp +++ b/src/dialog/vfindreplacedialog.cpp @@ -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); diff --git a/src/resources/icons/search_wrap.svg b/src/resources/icons/search_wrap.svg new file mode 100644 index 00000000..865781ed --- /dev/null +++ b/src/resources/icons/search_wrap.svg @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/src/vedit.cpp b/src/vedit.cpp index 61811b56..f28ffecb 100644 --- a/src/vedit.cpp +++ b/src/vedit.cpp @@ -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. - startPos = curPos; - lastPos = curPos; - } else { - // Move cursor to startPos to search. - cursor.setPosition(startPos); + int curPos = cursor.selectionStart(); + if (curPos != lastPos) { + // Cursor has been moved. Just start at current potition. + startPos = curPos; + lastPos = curPos; + } else { + 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 findFlags; + bool caseSensitive = false; + if (p_options & FindOption::CaseSensitive) { + findFlags |= QTextDocument::FindCaseSensitively; + caseSensitive = true; + } + if (p_options & FindOption::WholeWordOnly) { + findFlags |= QTextDocument::FindWholeWords; + } + if (!p_forward) { + findFlags |= QTextDocument::FindBackward; + } + // Use regular expression + bool useRegExp = false; + QRegExp exp; + if (p_options & FindOption::RegularExpression) { + 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, findFlags); + } + if (p_wrapped) { + if (!found) { setTextCursor(cursor); } + break; } - - // Options - QTextDocument::FindFlags flags; - if (p_options & FindOption::CaseSensitive) { - flags |= QTextDocument::FindCaseSensitively; + 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); } - if (p_options & FindOption::WholeWordOnly) { - flags |= QTextDocument::FindWholeWords; - } - if (!p_forward) { - flags |= QTextDocument::FindBackward; - } - // Use regular expression - if (p_options & FindOption::RegularExpression) { - QRegExp exp(p_text, - p_options & FindOption::CaseSensitive ? - Qt::CaseSensitive : Qt::CaseInsensitive); - found = find(exp, flags); - } else { - found = find(p_text, flags); - } - cursor = textCursor(); - if (!p_peek) { - startPos = cursor.selectionStart(); - } - lastPos = cursor.selectionStart(); } 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(); +} diff --git a/src/vedit.h b/src/vedit.h index 18708360..4e2daa46 100644 --- a/src/vedit.h +++ b/src/vedit.h @@ -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, - const QString &p_replaceText, bool p_findNext); - virtual void replaceTextAll(const QString &p_text, uint p_options, - const QString &p_replaceText); + 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); + void replaceTextAll(const QString &p_text, uint p_options, + const QString &p_replaceText); + +private slots: + void labelTimerTimeout(); protected: QPointer m_file; VEditOperations *m_editOps; + +private: + QLabel *m_wrapLabel; + QTimer *m_labelTimer; + + void showWrapLabel(); }; diff --git a/src/vedittab.cpp b/src/vedittab.cpp index c899f5ca..4845f7f0 100644 --- a/src/vedittab.cpp +++ b/src/vedittab.cpp @@ -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; diff --git a/src/vnote.qrc b/src/vnote.qrc index 82cef341..b0d2d2b5 100644 --- a/src/vnote.qrc +++ b/src/vnote.qrc @@ -83,5 +83,6 @@ resources/icons/corner_tablist_cur.svg resources/icons/close.svg resources/icons/find_replace.svg + resources/icons/search_wrap.svg