Browse Source

support search wrap

Signed-off-by: Le Tan <[email protected]>
Le Tan 9 years ago
parent
commit
3769ac5311
6 changed files with 186 additions and 106 deletions
  1. 2 2
      src/dialog/vfindreplacedialog.cpp
  2. 14 0
      src/resources/icons/search_wrap.svg
  3. 144 96
      src/vedit.cpp
  4. 19 6
      src/vedit.h
  5. 6 2
      src/vedittab.cpp
  6. 1 0
      src/vnote.qrc

+ 2 - 2
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);

+ 14 - 0
src/resources/icons/search_wrap.svg

@@ -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>

+ 144 - 96
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);
-                setTextCursor(cursor);
-            }
-        }
-
-        // Options
-        QTextDocument::FindFlags flags;
-        if (p_options & FindOption::CaseSensitive) {
-            flags |= QTextDocument::FindCaseSensitively;
-        }
-        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);
+        int curPos = cursor.selectionStart();
+        if (curPos != lastPos) {
+            // Cursor has been moved. Just start at current potition.
+            startPos = curPos;
+            lastPos = curPos;
         } else {
-            found = find(p_text, flags);
-        }
-        cursor = textCursor();
-        if (!p_peek) {
-            startPos = cursor.selectionStart();
+            cursor.setPosition(startPos);
+            setTextCursor(cursor);
         }
-        lastPos = cursor.selectionStart();
+    }
+    bool wrapped = false;
+    found = findTextHelper(p_text, p_options, true, wrapped);
+    if (found) {
+        lastPos = textCursor().selectionStart();
+        found = true;
     }
     return found;
 }
 
-void VEdit::replaceText(const QString &p_text, uint p_options,
-                        const QString &p_replaceText, bool p_findNext)
+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) {
+        findFlags |= QTextDocument::FindBackward;
+    }
+    // Use regular expression
     bool useRegExp = false;
     QRegExp exp;
     if (p_options & FindOption::RegularExpression) {
         useRegExp = true;
         exp = QRegExp(p_text,
-                      p_options & FindOption::CaseSensitive ?
-                      Qt::CaseSensitive : Qt::CaseInsensitive);
+                      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;
+        }
+        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)
+{
     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();
+}

+ 19 - 6
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<VFile> m_file;
     VEditOperations *m_editOps;
+
+private:
+    QLabel *m_wrapLabel;
+    QTimer *m_labelTimer;
+
+    void showWrapLabel();
 };
 
 

+ 6 - 2
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;

+ 1 - 0
src/vnote.qrc

@@ -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>