Browse Source

support outline in edit mode

Signed-off-by: Le Tan <[email protected]>
Le Tan 9 years ago
parent
commit
1a77056f4a
8 changed files with 149 additions and 6 deletions
  1. 4 3
      src/hgmarkdownhighlighter.cpp
  2. 9 0
      src/hgmarkdownhighlighter.h
  3. 53 0
      src/vedit.cpp
  4. 11 0
      src/vedit.h
  5. 29 3
      src/vedittab.cpp
  6. 2 0
      src/vedittab.h
  7. 39 0
      src/voutline.cpp
  8. 2 0
      src/voutline.h

+ 4 - 3
src/hgmarkdownhighlighter.cpp

@@ -77,7 +77,7 @@ void HGMarkdownHighlighter::highlightBlock(const QString &text)
             setFormat(unit.start, unit.length, highlightingStyles[unit.styleIndex].format);
         }
     }
-    setCurrentBlockState(0);
+    setCurrentBlockState(HighlightBlockState::BlockNormal);
     highlightCodeBlock(text);
 }
 
@@ -146,7 +146,7 @@ void HGMarkdownHighlighter::highlightCodeBlock(const QString &text)
 {
     int nextIndex = 0;
     int startIndex = 0;
-    if (previousBlockState() != 1) {
+    if (previousBlockState() != HighlightBlockState::BlockCodeBlock) {
         startIndex = codeBlockStartExp.indexIn(text);
         if (startIndex >= 0) {
             nextIndex = startIndex + codeBlockStartExp.matchedLength();
@@ -159,7 +159,7 @@ void HGMarkdownHighlighter::highlightCodeBlock(const QString &text)
         int endIndex = codeBlockEndExp.indexIn(text, nextIndex);
         int codeBlockLength;
         if (endIndex == -1) {
-            setCurrentBlockState(1);
+            setCurrentBlockState(HighlightBlockState::BlockCodeBlock);
             codeBlockLength = text.length() - startIndex;
         } else {
             codeBlockLength = endIndex - startIndex + codeBlockEndExp.matchedLength();
@@ -230,4 +230,5 @@ void HGMarkdownHighlighter::timerTimeout()
 {
     parse();
     rehighlight();
+    emit highlightCompleted();
 }

+ 9 - 0
src/hgmarkdownhighlighter.h

@@ -28,6 +28,12 @@ struct HighlightingStyle
     QTextCharFormat format;
 };
 
+enum HighlightBlockState
+{
+    BlockNormal = 0,
+    BlockCodeBlock = 1,
+};
+
 // One continuous region for a certain markdown highlight style
 // within a QTextBlock.
 // Pay attention to the change of HighlightingStyles[]
@@ -50,6 +56,9 @@ public:
     ~HGMarkdownHighlighter();
     void setStyles(const QVector<HighlightingStyle> &styles);
 
+signals:
+    void highlightCompleted();
+
 protected:
     void highlightBlock(const QString &text) Q_DECL_OVERRIDE;
 

+ 53 - 0
src/vedit.cpp

@@ -1,9 +1,11 @@
 #include <QtWidgets>
+#include <QVector>
 #include "vedit.h"
 #include "vnote.h"
 #include "vconfigmanager.h"
 #include "hgmarkdownhighlighter.h"
 #include "vmdeditoperations.h"
+#include "vtoc.h"
 
 extern VConfigManager vconfig;
 
@@ -14,6 +16,8 @@ VEdit::VEdit(VNoteFile *noteFile, QWidget *parent)
         setAcceptRichText(false);
         mdHighlighter = new HGMarkdownHighlighter(vconfig.getMdHighlightingStyles(),
                                                   500, document());
+        connect(mdHighlighter, &HGMarkdownHighlighter::highlightCompleted,
+                this, &VEdit::generateEditOutline);
         editOps = new VMdEditOperations(this, noteFile);
     } else {
         setAutoFormatting(QTextEdit::AutoBulletList);
@@ -22,6 +26,8 @@ VEdit::VEdit(VNoteFile *noteFile, QWidget *parent)
 
     updateTabSettings();
     updateFontAndPalette();
+    connect(this, &VEdit::cursorPositionChanged,
+            this, &VEdit::updateCurHeader);
 }
 
 VEdit::~VEdit()
@@ -164,3 +170,50 @@ void VEdit::insertFromMimeData(const QMimeData *source)
     }
     QTextEdit::insertFromMimeData(source);
 }
+
+void VEdit::generateEditOutline()
+{
+    QTextDocument *doc = document();
+    headers.clear();
+    // Assume that each block contains only one line
+    // Only support # syntax for now
+    QRegExp headerReg("(#{1,6})\\s*(\\S.*)");  // Need to trim the spaces
+    for (QTextBlock block = doc->begin(); block != doc->end(); block = block.next()) {
+        Q_ASSERT(block.lineCount() == 1);
+        if (headerReg.exactMatch(block.text())) {
+            VHeader header(headerReg.cap(1).length(),
+                           headerReg.cap(2).trimmed(), "", block.firstLineNumber());
+            headers.append(header);
+        }
+    }
+
+    emit headersChanged(headers);
+    updateCurHeader();
+}
+
+void VEdit::scrollToLine(int lineNumber)
+{
+    Q_ASSERT(lineNumber >= 0);
+
+    // Move the cursor to the end first
+    moveCursor(QTextCursor::End);
+    QTextCursor cursor(document()->findBlockByLineNumber(lineNumber));
+    cursor.movePosition(QTextCursor::EndOfLine);
+    setTextCursor(cursor);
+
+    setFocus();
+}
+
+void VEdit::updateCurHeader()
+{
+    int curHeader = 0;
+    QTextCursor cursor(this->textCursor());
+    int curLine = cursor.block().firstLineNumber();
+    for (int i = headers.size() - 1; i >= 0; --i) {
+        if (headers[i].lineNumber <= curLine) {
+            curHeader = headers[i].lineNumber;
+            break;
+        }
+    }
+    emit curHeaderChanged(curHeader);
+}

+ 11 - 0
src/vedit.h

@@ -5,6 +5,7 @@
 #include <QString>
 #include "vconstants.h"
 #include "vnotefile.h"
+#include "vtoc.h"
 
 class HGMarkdownHighlighter;
 class VEditOperations;
@@ -24,12 +25,21 @@ public:
     inline bool isModified() const;
 
     void reloadFile();
+    void scrollToLine(int lineNumber);
+
+signals:
+    void headersChanged(const QVector<VHeader> &headers);
+    void curHeaderChanged(int lineNumber);
 
 protected:
     void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
     bool canInsertFromMimeData(const QMimeData *source) const Q_DECL_OVERRIDE;
     void insertFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
 
+private slots:
+    void generateEditOutline();
+    void updateCurHeader();
+
 private:
     void updateTabSettings();
     void updateFontAndPalette();
@@ -39,6 +49,7 @@ private:
     VNoteFile *noteFile;
     HGMarkdownHighlighter *mdHighlighter;
     VEditOperations *editOps;
+    QVector<VHeader> headers;
 };
 
 

+ 29 - 3
src/vedittab.cpp

@@ -49,6 +49,10 @@ VEditTab::~VEditTab()
 void VEditTab::setupUI()
 {
     textEditor = new VEdit(noteFile);
+    connect(textEditor, &VEdit::headersChanged,
+            this, &VEditTab::updateTocFromHeaders);
+    connect(textEditor, SIGNAL(curHeaderChanged(int)),
+            this, SLOT(updateCurHeader(int)));
     addWidget(textEditor);
 
     switch (noteFile->docType) {
@@ -220,8 +224,8 @@ void VEditTab::setupMarkdownPreview()
     channel->registerObject(QStringLiteral("content"), &document);
     connect(&document, &VDocument::tocChanged,
             this, &VEditTab::updateTocFromHtml);
-    connect(&document, &VDocument::headerChanged,
-            this, &VEditTab::updateCurHeader);
+    connect(&document, SIGNAL(headerChanged(const QString&)),
+            this, SLOT(updateCurHeader(const QString &)));
     page->setWebChannel(channel);
 
     if (mdConverterType == MarkdownConverterType::Marked) {
@@ -273,6 +277,16 @@ void VEditTab::updateTocFromHtml(const QString &tocHtml)
     emit outlineChanged(tableOfContent);
 }
 
+void VEditTab::updateTocFromHeaders(const QVector<VHeader> &headers)
+{
+    tableOfContent.type = VHeaderType::LineNumber;
+    tableOfContent.headers = headers;
+    tableOfContent.filePath = QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName));
+    tableOfContent.valid = true;
+
+    emit outlineChanged(tableOfContent);
+}
+
 void VEditTab::parseTocUl(QXmlStreamReader &xml, QVector<VHeader> &headers, int level)
 {
     Q_ASSERT(xml.isStartElement() && xml.name() == "ul");
@@ -349,7 +363,7 @@ void VEditTab::scrollToAnchor(const VAnchor &anchor)
 {
     if (isEditMode) {
         if (anchor.lineNumber > -1) {
-
+            textEditor->scrollToLine(anchor.lineNumber);
         }
     } else {
         if (!anchor.anchor.isEmpty()) {
@@ -369,3 +383,15 @@ void VEditTab::updateCurHeader(const QString &anchor)
         emit curHeaderChanged(curHeader);
     }
 }
+
+void VEditTab::updateCurHeader(int lineNumber)
+{
+    if (curHeader.lineNumber == lineNumber) {
+        return;
+    }
+    curHeader = VAnchor(QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName)),
+                        "", lineNumber);
+    if (lineNumber > -1) {
+        emit curHeaderChanged(curHeader);
+    }
+}

+ 2 - 0
src/vedittab.h

@@ -46,6 +46,8 @@ private slots:
     void handleFocusChanged(QWidget *old, QWidget *now);
     void updateTocFromHtml(const QString &tocHtml);
     void updateCurHeader(const QString &anchor);
+    void updateCurHeader(int lineNumber);
+    void updateTocFromHeaders(const QVector<VHeader> &headers);
 
 private:
     bool isMarkdown(const QString &name);

+ 39 - 0
src/voutline.cpp

@@ -21,6 +21,7 @@ void VOutline::updateOutline(const VToc &toc)
     outline = toc;
     updateTreeFromOutline(outline);
     expandTree();
+    updateCurHeader(curHeader);
 }
 
 void VOutline::updateTreeFromOutline(const VToc &toc)
@@ -90,6 +91,7 @@ void VOutline::updateCurHeader(const VAnchor &anchor)
         selectAnchor(anchor.anchor);
     } else {
         // Select by lineNumber
+        selectLineNumber(anchor.lineNumber);
     }
 }
 
@@ -129,3 +131,40 @@ bool VOutline::selectAnchorOne(QTreeWidgetItem *item, const QString &anchor)
     }
     return false;
 }
+
+void VOutline::selectLineNumber(int lineNumber)
+{
+    QList<QTreeWidgetItem *> selected = selectedItems();
+    foreach (QTreeWidgetItem *item, selected) {
+        setItemSelected(item, false);
+    }
+
+    int nrTop = topLevelItemCount();
+    for (int i = 0; i < nrTop; ++i) {
+        if (selectLineNumberOne(topLevelItem(i), lineNumber)) {
+            return;
+        }
+    }
+}
+
+bool VOutline::selectLineNumberOne(QTreeWidgetItem *item, int lineNumber)
+{
+    if (!item) {
+        return false;
+    }
+    QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject();
+    int itemLineNum = itemJson["line_number"].toInt();
+    if (itemLineNum == lineNumber) {
+        // Select this item
+        setItemSelected(item, true);
+        return true;
+    }
+
+    int nrChild = item->childCount();
+    for (int i = 0; i < nrChild; ++i) {
+        if (selectLineNumberOne(item->child(i), lineNumber)) {
+            return true;
+        }
+    }
+    return false;
+}

+ 2 - 0
src/voutline.h

@@ -27,6 +27,8 @@ private:
     void expandTree();
     void selectAnchor(const QString &anchor);
     bool selectAnchorOne(QTreeWidgetItem *item, const QString &anchor);
+    void selectLineNumber(int lineNumber);
+    bool selectLineNumberOne(QTreeWidgetItem *item, int lineNumber);
 
     VToc outline;
     VAnchor curHeader;