diff --git a/src/src.pro b/src/src.pro index 1ae6900a..f575f9d1 100644 --- a/src/src.pro +++ b/src/src.pro @@ -44,7 +44,6 @@ SOURCES += main.cpp\ veditwindow.cpp \ vedittab.cpp \ voutline.cpp \ - vtoc.cpp \ vsingleinstanceguard.cpp \ vdirectory.cpp \ vfile.cpp \ @@ -78,7 +77,8 @@ SOURCES += main.cpp\ vnotefile.cpp \ vattachmentlist.cpp \ dialog/vsortdialog.cpp \ - vfilesessioninfo.cpp + vfilesessioninfo.cpp \ + vtableofcontent.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -108,7 +108,6 @@ HEADERS += vmainwindow.h \ veditwindow.h \ vedittab.h \ voutline.h \ - vtoc.h \ vsingleinstanceguard.h \ vdirectory.h \ vfile.h \ @@ -144,7 +143,8 @@ HEADERS += vmainwindow.h \ vnotefile.h \ vattachmentlist.h \ dialog/vsortdialog.h \ - vfilesessioninfo.h + vfilesessioninfo.h \ + vtableofcontent.h RESOURCES += \ vnote.qrc \ diff --git a/src/vdocument.h b/src/vdocument.h index 40b00bbb..08b51ed2 100644 --- a/src/vdocument.h +++ b/src/vdocument.h @@ -16,9 +16,15 @@ class VDocument : public QObject public: // @p_file could be NULL. VDocument(const VFile *p_file, QObject *p_parent = 0); + QString getToc(); + + // Scroll to @anchor in the web. + // @anchor is the id without '#', like "toc_1". If empty, will scroll to top. void scrollToAnchor(const QString &anchor); + void setHtml(const QString &html); + // Request to highlight a segment text. // Use p_id to identify the result. void highlightTextAsync(const QString &p_text, int p_id, int p_timeStamp); @@ -35,6 +41,7 @@ public slots: // When the Web view has been scrolled, it will signal current header anchor. // Empty @anchor to indicate an invalid header. + // The header does not begins with '#'. void setHeader(const QString &anchor); void setLog(const QString &p_log); @@ -49,8 +56,12 @@ public slots: signals: void textChanged(const QString &text); + void tocChanged(const QString &toc); + void requestScrollToAnchor(const QString &anchor); + + // @anchor is the id of that anchor, without '#'. void headerChanged(const QString &anchor); void htmlChanged(const QString &html); void logChanged(const QString &p_log); diff --git a/src/vedit.cpp b/src/vedit.cpp index 38cfb259..9010581c 100644 --- a/src/vedit.cpp +++ b/src/vedit.cpp @@ -4,7 +4,7 @@ #include "vedit.h" #include "vnote.h" #include "vconfigmanager.h" -#include "vtoc.h" +#include "vtableofcontent.h" #include "utils/vutils.h" #include "utils/veditutils.h" #include "veditoperations.h" @@ -157,15 +157,16 @@ void VEdit::reloadFile() setModified(false); } -void VEdit::scrollToLine(int p_lineNumber) +bool VEdit::scrollToBlock(int p_blockNumber) { - Q_ASSERT(p_lineNumber >= 0); - - QTextBlock block = document()->findBlockByLineNumber(p_lineNumber); + QTextBlock block = document()->findBlockByNumber(p_blockNumber); if (block.isValid()) { VEditUtils::scrollBlockInPage(this, block.blockNumber(), 0); moveCursor(QTextCursor::EndOfBlock); + return true; } + + return false; } bool VEdit::isModified() const diff --git a/src/vedit.h b/src/vedit.h index 049762c1..6eba974b 100644 --- a/src/vedit.h +++ b/src/vedit.h @@ -10,7 +10,6 @@ #include #include #include "vconstants.h" -#include "vtoc.h" #include "vnotefile.h" class VEditOperations; @@ -81,7 +80,9 @@ public: virtual void setModified(bool p_modified); bool isModified() const; virtual void reloadFile(); - virtual void scrollToLine(int p_lineNumber); + + virtual bool scrollToBlock(int p_blockNumber); + // User requests to insert an image. virtual void insertImage(); diff --git a/src/veditarea.cpp b/src/veditarea.cpp index bb77c8df..87ee6255 100644 --- a/src/veditarea.cpp +++ b/src/veditarea.cpp @@ -77,9 +77,9 @@ void VEditArea::insertSplitWindow(int idx) connect(win, &VEditWindow::getFocused, this, &VEditArea::handleWindowFocused); connect(win, &VEditWindow::outlineChanged, - this, &VEditArea::handleOutlineChanged); - connect(win, &VEditWindow::curHeaderChanged, - this, &VEditArea::handleCurHeaderChanged); + this, &VEditArea::handleWindowOutlineChanged); + connect(win, &VEditWindow::currentHeaderChanged, + this, &VEditArea::handleWindowCurrentHeaderChanged); connect(win, &VEditWindow::statusMessage, this, &VEditArea::handleWindowStatusMessage); connect(win, &VEditWindow::vimStatusUpdated, @@ -237,15 +237,13 @@ void VEditArea::updateWindowStatus() Q_ASSERT(splitter->count() == 0); emit tabStatusUpdated(VEditTabInfo()); - emit outlineChanged(VToc()); - emit curHeaderChanged(VAnchor()); + emit outlineChanged(VTableOfContent()); + emit currentHeaderChanged(VHeaderPointer()); return; } VEditWindow *win = getWindow(curWindowIndex); win->updateTabStatus(); - win->requestUpdateOutline(); - win->requestUpdateCurHeader(); } bool VEditArea::closeFile(const VFile *p_file, bool p_forced) @@ -436,26 +434,28 @@ void VEditArea::handleWindowFocused() } } -void VEditArea::handleOutlineChanged(const VToc &toc) +void VEditArea::handleWindowOutlineChanged(const VTableOfContent &p_outline) { QObject *winObject = sender(); if (splitter->widget(curWindowIndex) == winObject) { - emit outlineChanged(toc); + emit outlineChanged(p_outline); } } -void VEditArea::handleCurHeaderChanged(const VAnchor &anchor) +void VEditArea::handleWindowCurrentHeaderChanged(const VHeaderPointer &p_header) { QObject *winObject = sender(); if (splitter->widget(curWindowIndex) == winObject) { - emit curHeaderChanged(anchor); + emit currentHeaderChanged(p_header); } } -void VEditArea::handleOutlineItemActivated(const VAnchor &anchor) +void VEditArea::scrollToHeader(const VHeaderPointer &p_header) { - // Notice current window - getWindow(curWindowIndex)->scrollCurTab(anchor); + VEditWindow *win = getCurrentWindow(); + if (win) { + win->scrollToHeader(p_header); + } } bool VEditArea::isFileOpened(const VFile *p_file) @@ -648,6 +648,7 @@ VEditWindow *VEditArea::getCurrentWindow() const if (curWindowIndex < 0) { return NULL; } + return getWindow(curWindowIndex); } diff --git a/src/veditarea.h b/src/veditarea.h index 49343d3d..dbeb9d8f 100644 --- a/src/veditarea.h +++ b/src/veditarea.h @@ -12,7 +12,6 @@ #include #include "vnotebook.h" #include "veditwindow.h" -#include "vtoc.h" #include "vnavigationmode.h" class VNote; @@ -82,8 +81,11 @@ signals: // Emit when current window's tab status updated. void tabStatusUpdated(const VEditTabInfo &p_info); - void outlineChanged(const VToc &toc); - void curHeaderChanged(const VAnchor &anchor); + // Emit when current window's tab's outline changed. + void outlineChanged(const VTableOfContent &p_outline); + + // Emit when current window's tab's current header changed. + void currentHeaderChanged(const VHeaderPointer &p_header); // Emit when want to show message in status bar. void statusMessage(const QString &p_msg); @@ -106,7 +108,10 @@ public slots: void saveFile(); void readFile(); void saveAndReadFile(); - void handleOutlineItemActivated(const VAnchor &anchor); + + // Scroll current tab to @p_header. + void scrollToHeader(const VHeaderPointer &p_header); + void handleFileUpdated(const VFile *p_file); void handleDirectoryUpdated(const VDirectory *p_dir); void handleNotebookUpdated(const VNotebook *p_notebook); @@ -118,8 +123,11 @@ private slots: void handleRemoveSplitRequest(VEditWindow *curWindow); void handleWindowFocused(); - void handleOutlineChanged(const VToc &toc); - void handleCurHeaderChanged(const VAnchor &anchor); + + void handleWindowOutlineChanged(const VTableOfContent &p_outline); + + void handleWindowCurrentHeaderChanged(const VHeaderPointer &p_header); + void handleFindTextChanged(const QString &p_text, uint p_options); void handleFindOptionChanged(uint p_options); void handleFindNext(const QString &p_text, uint p_options, bool p_forward); diff --git a/src/vedittab.cpp b/src/vedittab.cpp index 8e41c48b..70fa0dc4 100644 --- a/src/vedittab.cpp +++ b/src/vedittab.cpp @@ -3,12 +3,13 @@ #include VEditTab::VEditTab(VFile *p_file, VEditArea *p_editArea, QWidget *p_parent) - : QWidget(p_parent), m_file(p_file), m_isEditMode(false), - m_modified(false), m_editArea(p_editArea) + : QWidget(p_parent), + m_file(p_file), + m_isEditMode(false), + m_outline(p_file), + m_currentHeader(p_file, -1), + m_editArea(p_editArea) { - m_toc.m_file = m_file; - m_curHeader.m_file = m_file; - connect(qApp, &QApplication::focusChanged, this, &VEditTab::handleFocusChanged); } @@ -33,7 +34,7 @@ bool VEditTab::isEditMode() const bool VEditTab::isModified() const { - return m_modified; + return m_file->isModified(); } VFile *VEditTab::getFile() const @@ -53,16 +54,6 @@ void VEditTab::handleFocusChanged(QWidget * /* p_old */, QWidget *p_now) } } -void VEditTab::requestUpdateCurHeader() -{ - emit curHeaderChanged(m_curHeader); -} - -void VEditTab::requestUpdateOutline() -{ - emit outlineChanged(m_toc); -} - void VEditTab::wheelEvent(QWheelEvent *p_event) { QPoint angle = p_event->angleDelta(); @@ -78,41 +69,41 @@ void VEditTab::wheelEvent(QWheelEvent *p_event) p_event->ignore(); } -void VEditTab::updateStatus() -{ - m_modified = m_file->isModified(); - - emit statusUpdated(fetchTabInfo()); -} - -VEditTabInfo VEditTab::fetchTabInfo() +VEditTabInfo VEditTab::fetchTabInfo() const { VEditTabInfo info; - info.m_editTab = this; + info.m_editTab = const_cast(this); return info; } -VAnchor VEditTab::getCurrentHeader() const +const VHeaderPointer &VEditTab::getCurrentHeader() const { - return m_curHeader; + return m_currentHeader; +} + +const VTableOfContent &VEditTab::getOutline() const +{ + return m_outline; } void VEditTab::tryRestoreFromTabInfo(const VEditTabInfo &p_info) { if (p_info.m_editTab != this) { - // Clear and return. - m_infoToRestore.m_editTab = NULL; + m_infoToRestore.clear(); return; } if (restoreFromTabInfo(p_info)) { - // Clear and return. - m_infoToRestore.m_editTab = NULL; + m_infoToRestore.clear(); return; } // Save it and restore later. m_infoToRestore = p_info; - qDebug() << "save info for restore later" << p_info.m_anchorIndex; +} + +void VEditTab::updateStatus() +{ + emit statusUpdated(fetchTabInfo()); } diff --git a/src/vedittab.h b/src/vedittab.h index acf0d470..9e6eba22 100644 --- a/src/vedittab.h +++ b/src/vedittab.h @@ -4,7 +4,7 @@ #include #include #include -#include "vtoc.h" +#include "vtableofcontent.h" #include "vfile.h" #include "utils/vvim.h" #include "vedittabinfo.h" @@ -37,12 +37,9 @@ public: void focusTab(); - virtual void requestUpdateOutline(); - - virtual void requestUpdateCurHeader(); - - // Scroll to anchor @p_anchor. - virtual void scrollToAnchor(const VAnchor& p_anchor) = 0; + // Scroll to @p_header. + // Will emit currentHeaderChanged() if @p_header is valid. + virtual void scrollToHeader(const VHeaderPointer &p_header) { Q_UNUSED(p_header) } VFile *getFile() const; @@ -72,21 +69,23 @@ public: virtual void decorateText(TextDecoration p_decoration) {Q_UNUSED(p_decoration);} // Create a filled VEditTabInfo. - virtual VEditTabInfo fetchTabInfo(); + virtual VEditTabInfo fetchTabInfo() const; - VAnchor getCurrentHeader() const; + const VTableOfContent &getOutline() const; + + const VHeaderPointer &getCurrentHeader() const; // Restore status from @p_info. // If this tab is not ready yet, it will restore once it is ready. void tryRestoreFromTabInfo(const VEditTabInfo &p_info); + // Emit signal to update current status. + virtual void updateStatus(); + public slots: // Enter edit mode virtual void editFile() = 0; - // Update status of current tab. Emit statusUpdated(). - virtual void updateStatus(); - protected: void wheelEvent(QWheelEvent *p_event) Q_DECL_OVERRIDE; @@ -102,10 +101,15 @@ protected: // File related to this tab. QPointer m_file; + bool m_isEditMode; - bool m_modified; - VToc m_toc; - VAnchor m_curHeader; + + // Table of content of this tab. + VTableOfContent m_outline; + + // Current header in m_outline of this tab. + VHeaderPointer m_currentHeader; + VEditArea *m_editArea; // Tab info to restore from once ready. @@ -114,9 +118,9 @@ protected: signals: void getFocused(); - void outlineChanged(const VToc &p_toc); + void outlineChanged(const VTableOfContent &p_outline); - void curHeaderChanged(const VAnchor &p_anchor); + void currentHeaderChanged(const VHeaderPointer &p_header); // The status of current tab has updates. void statusUpdated(const VEditTabInfo &p_info); diff --git a/src/vedittabinfo.h b/src/vedittabinfo.h index 2c73e735..f80fd623 100644 --- a/src/vedittabinfo.h +++ b/src/vedittabinfo.h @@ -10,10 +10,19 @@ struct VEditTabInfo m_cursorBlockNumber(-1), m_cursorPositionInBlock(-1), m_blockCount(-1), - m_anchorIndex(-1) + m_headerIndex(-1) { } + void clear() + { + m_editTab = NULL; + m_cursorBlockNumber = -1; + m_cursorPositionInBlock = -1; + m_blockCount = -1; + m_headerIndex = -1; + } + VEditTab *m_editTab; // Cursor information. -1 for invalid info. @@ -21,8 +30,8 @@ struct VEditTabInfo int m_cursorPositionInBlock; int m_blockCount; - // Anchor index in outline. - int m_anchorIndex; + // Header index in outline. + int m_headerIndex; }; #endif // VEDITTABINFO_H diff --git a/src/veditwindow.cpp b/src/veditwindow.cpp index d2ff0629..519c589f 100644 --- a/src/veditwindow.cpp +++ b/src/veditwindow.cpp @@ -466,15 +466,15 @@ void VEditWindow::updateTabStatus(int p_index) if (p_index == -1) { emit tabStatusUpdated(VEditTabInfo()); - emit outlineChanged(VToc()); - emit curHeaderChanged(VAnchor()); + emit outlineChanged(VTableOfContent()); + emit currentHeaderChanged(VHeaderPointer()); return; } VEditTab *tab = getTab(p_index); - tab->updateStatus(); - tab->requestUpdateOutline(); - tab->requestUpdateCurHeader(); + emit tabStatusUpdated(tab->fetchTabInfo()); + emit outlineChanged(tab->getOutline()); + emit currentHeaderChanged(tab->getCurrentHeader()); } void VEditWindow::updateTabInfo(int p_index) @@ -506,26 +506,24 @@ void VEditWindow::updateAllTabsSequence() } } -// Be requested to report current outline -void VEditWindow::requestUpdateOutline() +VTableOfContent VEditWindow::getOutline() const { int idx = currentIndex(); if (idx == -1) { - emit outlineChanged(VToc()); - return; + return VTableOfContent(); } - getTab(idx)->requestUpdateOutline(); + + return getTab(idx)->getOutline(); } -// Be requested to report current header -void VEditWindow::requestUpdateCurHeader() +VHeaderPointer VEditWindow::getCurrentHeader() const { int idx = currentIndex(); if (idx == -1) { - emit curHeaderChanged(VAnchor()); - return; + return VHeaderPointer(); } - getTab(idx)->requestUpdateCurHeader(); + + return getTab(idx)->getCurrentHeader(); } // Focus this windows. Try to focus current tab. @@ -681,44 +679,39 @@ bool VEditWindow::canRemoveSplit() return splitter->count() > 1; } -void VEditWindow::handleOutlineChanged(const VToc &p_toc) +void VEditWindow::handleTabOutlineChanged(const VTableOfContent &p_outline) { - // Only propagate it if it is current tab - int idx = currentIndex(); - if (idx == -1) { - emit outlineChanged(VToc()); + // Only propagate it if it is current tab. + VEditTab *tab = getCurrentTab(); + if (tab) { + if (tab->getFile() == p_outline.getFile()) { + emit outlineChanged(p_outline); + } + } else { + emit outlineChanged(VTableOfContent()); return; } - const VFile *file = getTab(idx)->getFile(); - if (p_toc.m_file == file) { - emit outlineChanged(p_toc); - } } -void VEditWindow::handleCurHeaderChanged(const VAnchor &p_anchor) +void VEditWindow::handleTabCurrentHeaderChanged(const VHeaderPointer &p_header) { - // Only propagate it if it is current tab - int idx = currentIndex(); - if (idx == -1) { - emit curHeaderChanged(VAnchor()); + // Only propagate it if it is current tab. + VEditTab *tab = getCurrentTab(); + if (tab) { + if (tab->getFile() == p_header.m_file) { + emit currentHeaderChanged(p_header); + } + } else { + emit currentHeaderChanged(VHeaderPointer()); return; } - const VFile *file = getTab(idx)->getFile(); - if (p_anchor.m_file == file) { - emit curHeaderChanged(p_anchor); - } } -void VEditWindow::scrollCurTab(const VAnchor &p_anchor) +void VEditWindow::scrollToHeader(const VHeaderPointer &p_header) { - int idx = currentIndex(); - if (idx == -1) { - emit curHeaderChanged(VAnchor()); - return; - } - const VFile *file = getTab(idx)->getFile(); - if (file == p_anchor.m_file) { - getTab(idx)->scrollToAnchor(p_anchor); + VEditTab *tab = getCurrentTab(); + if (tab) { + tab->scrollToHeader(p_header); } } @@ -916,9 +909,9 @@ void VEditWindow::connectEditTab(const VEditTab *p_tab) connect(p_tab, &VEditTab::getFocused, this, &VEditWindow::getFocused); connect(p_tab, &VEditTab::outlineChanged, - this, &VEditWindow::handleOutlineChanged); - connect(p_tab, &VEditTab::curHeaderChanged, - this, &VEditWindow::handleCurHeaderChanged); + this, &VEditWindow::handleTabOutlineChanged); + connect(p_tab, &VEditTab::currentHeaderChanged, + this, &VEditWindow::handleTabCurrentHeaderChanged); connect(p_tab, &VEditTab::statusUpdated, this, &VEditWindow::handleTabStatusUpdated); connect(p_tab, &VEditTab::statusMessage, diff --git a/src/veditwindow.h b/src/veditwindow.h index 762572e9..e0cdd8a8 100644 --- a/src/veditwindow.h +++ b/src/veditwindow.h @@ -8,7 +8,6 @@ #include #include "vnotebook.h" #include "vedittab.h" -#include "vtoc.h" #include "vconstants.h" #include "vnotefile.h" @@ -32,11 +31,19 @@ public: void readFile(); void saveAndReadFile(); bool closeAllFiles(bool p_forced); - void requestUpdateOutline(); - void requestUpdateCurHeader(); + + // Return outline of current tab. + VTableOfContent getOutline() const; + + // Return current header of current tab. + VHeaderPointer getCurrentHeader() const; + // Focus to current tab's editor void focusWindow(); - void scrollCurTab(const VAnchor &p_anchor); + + // Scroll current tab to header @p_header. + void scrollToHeader(const VHeaderPointer &p_header); + void updateFileInfo(const VFile *p_file); void updateDirectoryInfo(const VDirectory *p_dir); void updateNotebookInfo(const VNotebook *p_notebook); @@ -84,8 +91,10 @@ signals: void requestRemoveSplit(VEditWindow *curWindow); // This widget or its children get the focus void getFocused(); - void outlineChanged(const VToc &toc); - void curHeaderChanged(const VAnchor &anchor); + + void outlineChanged(const VTableOfContent &p_outline); + + void currentHeaderChanged(const VHeaderPointer &p_header); // Emit when want to show message in status bar. void statusMessage(const QString &p_msg); @@ -105,8 +114,11 @@ private slots: void handleCurrentIndexChanged(int p_index); void contextMenuRequested(QPoint pos); void tabListJump(VFile *p_file); - void handleOutlineChanged(const VToc &p_toc); - void handleCurHeaderChanged(const VAnchor &p_anchor); + + void handleTabOutlineChanged(const VTableOfContent &p_outline); + + void handleTabCurrentHeaderChanged(const VHeaderPointer &p_header); + void updateSplitMenu(); void tabbarContextMenuRequested(QPoint p_pos); void handleLocateAct(); diff --git a/src/vfilesessioninfo.cpp b/src/vfilesessioninfo.cpp index 001b9fde..bdffad1f 100644 --- a/src/vfilesessioninfo.cpp +++ b/src/vfilesessioninfo.cpp @@ -3,13 +3,13 @@ #include #include "vedittabinfo.h" -#include "vtoc.h" +#include "vtableofcontent.h" #include "vedittab.h" VFileSessionInfo::VFileSessionInfo() : m_mode(OpenFileMode::Read), - m_anchorIndex(-1), + m_headerIndex(-1), m_cursorBlockNumber(-1), m_cursorPositionInBlock(-1) { @@ -19,7 +19,7 @@ VFileSessionInfo::VFileSessionInfo(const QString &p_file, OpenFileMode p_mode) : m_file(p_file), m_mode(p_mode), - m_anchorIndex(-1), + m_headerIndex(-1), m_cursorBlockNumber(-1), m_cursorPositionInBlock(-1) { @@ -32,7 +32,7 @@ VFileSessionInfo VFileSessionInfo::fromEditTabInfo(const VEditTabInfo *p_tabInfo VEditTab *tab = p_tabInfo->m_editTab; VFileSessionInfo info(tab->getFile()->fetchPath(), tab->isEditMode() ? OpenFileMode::Edit : OpenFileMode::Read); - info.m_anchorIndex = p_tabInfo->m_anchorIndex; + info.m_headerIndex = p_tabInfo->m_headerIndex; info.m_cursorBlockNumber = p_tabInfo->m_cursorBlockNumber; info.m_cursorPositionInBlock = p_tabInfo->m_cursorPositionInBlock; @@ -41,7 +41,7 @@ VFileSessionInfo VFileSessionInfo::fromEditTabInfo(const VEditTabInfo *p_tabInfo void VFileSessionInfo::toEditTabInfo(VEditTabInfo *p_tabInfo) const { - p_tabInfo->m_anchorIndex = m_anchorIndex; + p_tabInfo->m_headerIndex = m_headerIndex; p_tabInfo->m_cursorBlockNumber = m_cursorBlockNumber; p_tabInfo->m_cursorPositionInBlock = m_cursorPositionInBlock; } @@ -57,7 +57,7 @@ VFileSessionInfo VFileSessionInfo::fromSettings(const QSettings *p_settings) info.m_mode = OpenFileMode::Read; } - info.m_anchorIndex = p_settings->value(FileSessionConfig::c_anchorIndex).toInt(); + info.m_headerIndex = p_settings->value(FileSessionConfig::c_headerIndex).toInt(); info.m_cursorBlockNumber = p_settings->value(FileSessionConfig::c_cursorBlockNumber).toInt(); info.m_cursorPositionInBlock = p_settings->value(FileSessionConfig::c_cursorPositionInBlock).toInt(); @@ -68,7 +68,7 @@ void VFileSessionInfo::toSettings(QSettings *p_settings) const { p_settings->setValue(FileSessionConfig::c_file, m_file); p_settings->setValue(FileSessionConfig::c_mode, (int)m_mode); - p_settings->setValue(FileSessionConfig::c_anchorIndex, m_anchorIndex); + p_settings->setValue(FileSessionConfig::c_headerIndex, m_headerIndex); p_settings->setValue(FileSessionConfig::c_cursorBlockNumber, m_cursorBlockNumber); p_settings->setValue(FileSessionConfig::c_cursorPositionInBlock, m_cursorPositionInBlock); } diff --git a/src/vfilesessioninfo.h b/src/vfilesessioninfo.h index 3213fc56..f0fd8673 100644 --- a/src/vfilesessioninfo.h +++ b/src/vfilesessioninfo.h @@ -12,7 +12,7 @@ namespace FileSessionConfig static const QString c_mode = "mode"; // Index in outline of the anchor. - static const QString c_anchorIndex = "anchor_index"; + static const QString c_headerIndex = "header_index"; static const QString c_cursorBlockNumber = "cursor_block_number"; static const QString c_cursorPositionInBlock = "cursor_position_in_block"; @@ -44,8 +44,8 @@ public: // Mode of this file in this session. OpenFileMode m_mode; - // Index in outline of the anchor. - int m_anchorIndex; + // Index in outline of the header. + int m_headerIndex; // Block number of cursor block. int m_cursorBlockNumber; diff --git a/src/vhtmltab.cpp b/src/vhtmltab.cpp index 345ea72c..6c8fb40c 100644 --- a/src/vhtmltab.cpp +++ b/src/vhtmltab.cpp @@ -32,7 +32,7 @@ void VHtmlTab::setupUI() { m_editor = new VEdit(m_file, this); connect(m_editor, &VEdit::textChanged, - this, &VHtmlTab::handleTextChanged); + this, &VHtmlTab::updateStatus); connect(m_editor, &VEdit::saveAndRead, this, &VHtmlTab::saveAndRead); connect(m_editor, &VEdit::discardAndRead, @@ -52,17 +52,6 @@ void VHtmlTab::setupUI() setLayout(mainLayout); } -void VHtmlTab::handleTextChanged() -{ - V_ASSERT(m_file->isModifiable()); - - if (m_modified) { - return; - } - - updateStatus(); -} - void VHtmlTab::showFileReadMode() { m_isEditMode = false; @@ -194,10 +183,6 @@ void VHtmlTab::discardAndRead() readFile(); } -void VHtmlTab::scrollToAnchor(const VAnchor & /* p_anchor */) -{ -} - void VHtmlTab::insertImage() { } diff --git a/src/vhtmltab.h b/src/vhtmltab.h index dafd32fe..80471fa5 100644 --- a/src/vhtmltab.h +++ b/src/vhtmltab.h @@ -26,9 +26,6 @@ public: // Save file. bool saveFile() Q_DECL_OVERRIDE; - // Scroll to anchor @p_anchor. - void scrollToAnchor(const VAnchor& p_anchor) Q_DECL_OVERRIDE; - void insertImage() Q_DECL_OVERRIDE; // Search @p_text in current note. @@ -53,9 +50,6 @@ public slots: void editFile() Q_DECL_OVERRIDE; private slots: - // Handle text changed in m_editor. - void handleTextChanged(); - // m_editor requests to save changes and enter read mode. void saveAndRead(); diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 02bdf218..e03b9beb 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -1100,13 +1100,16 @@ void VMainWindow::initDockWindows() toolDock->setObjectName("tools_dock"); toolDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); toolBox = new QToolBox(this); + + // Outline tree. outline = new VOutline(this); connect(editArea, &VEditArea::outlineChanged, outline, &VOutline::updateOutline); + connect(editArea, &VEditArea::currentHeaderChanged, + outline, &VOutline::updateCurrentHeader); connect(outline, &VOutline::outlineItemActivated, - editArea, &VEditArea::handleOutlineItemActivated); - connect(editArea, &VEditArea::curHeaderChanged, - outline, &VOutline::updateCurHeader); + editArea, &VEditArea::scrollToHeader); + toolBox->addItem(outline, QIcon(":/resources/icons/outline.svg"), tr("Outline")); toolDock->setWidget(toolBox); addDockWidget(Qt::RightDockWidgetArea, toolDock); diff --git a/src/vmainwindow.h b/src/vmainwindow.h index 9753b796..9b9b36ea 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -145,6 +145,7 @@ private slots: void handleVimStatusUpdated(const VVim *p_vim); // Handle the status update of the current tab of VEditArea. + // Will be called frequently. void handleAreaTabStatusUpdated(const VEditTabInfo &p_info); // Check the shared memory between different instances to see if we have diff --git a/src/vmdedit.cpp b/src/vmdedit.cpp index 739e291a..59d16ca8 100644 --- a/src/vmdedit.cpp +++ b/src/vmdedit.cpp @@ -5,7 +5,7 @@ #include "vmdeditoperations.h" #include "vnote.h" #include "vconfigmanager.h" -#include "vtoc.h" +#include "vtableofcontent.h" #include "utils/vutils.h" #include "utils/veditutils.h" #include "utils/vpreviewutils.h" @@ -35,7 +35,7 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type, document()); connect(m_mdHighlighter, &HGMarkdownHighlighter::headersUpdated, - this, &VMdEdit::updateOutline); + this, &VMdEdit::updateHeaders); // After highlight, the cursor may trun into non-visible. We should make it visible // in this case. @@ -74,7 +74,7 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type, this, &VEdit::vimStatusUpdated); connect(this, &VMdEdit::cursorPositionChanged, - this, &VMdEdit::updateCurHeader); + this, &VMdEdit::updateCurrentHeader); connect(QApplication::clipboard(), &QClipboard::changed, this, &VMdEdit::handleClipboardChanged); @@ -111,7 +111,7 @@ void VMdEdit::beginEdit() setReadOnly(false); } - updateOutline(m_mdHighlighter->getHeaderRegions()); + updateHeaders(m_mdHighlighter->getHeaderRegions()); } void VMdEdit::endEdit() @@ -345,43 +345,9 @@ void VMdEdit::clearUnusedImages() m_initImages.clear(); } -int VMdEdit::currentCursorHeader() const +void VMdEdit::updateCurrentHeader() { - if (m_headers.isEmpty()) { - return -1; - } - - int curLine = textCursor().block().firstLineNumber(); - int i = 0; - for (i = m_headers.size() - 1; i >= 0; --i) { - if (!m_headers[i].isEmpty()) { - if (m_headers[i].lineNumber <= curLine) { - break; - } - } - } - - if (i == -1) { - return -1; - } else { - Q_ASSERT(m_headers[i].index == i); - return i; - } -} - -void VMdEdit::updateCurHeader() -{ - if (m_headers.isEmpty()) { - return; - } - - int idx = currentCursorHeader(); - if (idx == -1) { - emit curHeaderChanged(VAnchor(m_file, "", -1, -1)); - return; - } - - emit curHeaderChanged(VAnchor(m_file, "", m_headers[idx].lineNumber, m_headers[idx].index)); + emit currentHeaderChanged(textCursor().block().blockNumber()); } static void addHeaderSequence(QVector &p_sequence, int p_level, int p_baseLevel) @@ -448,11 +414,11 @@ static void insertSequenceToHeader(QTextBlock p_block, } } -void VMdEdit::updateOutline(const QVector &p_headerRegions) +void VMdEdit::updateHeaders(const QVector &p_headerRegions) { QTextDocument *doc = document(); - QVector headers; + QVector headers; QVector headerBlockNumbers; QVector headerSequences; if (!p_headerRegions.isEmpty()) { @@ -480,8 +446,10 @@ void VMdEdit::updateOutline(const QVector &p_headerRegions) if ((block.userState() == HighlightBlockState::Normal) && headerReg.exactMatch(block.text())) { int level = headerReg.cap(1).length(); - VHeader header(level, headerReg.cap(2).trimmed(), - "", block.firstLineNumber(), headers.size()); + VTableOfContentItem header(headerReg.cap(2).trimmed(), + level, + block.blockNumber(), + headers.size()); headers.append(header); headerBlockNumbers.append(block.blockNumber()); headerSequences.append(headerReg.cap(3)); @@ -506,22 +474,25 @@ void VMdEdit::updateOutline(const QVector &p_headerRegions) QRegExp preReg(VUtils::c_headerPrefixRegExp); int curLevel = baseLevel - 1; for (int i = 0; i < headers.size(); ++i) { - VHeader &item = headers[i]; - while (item.level > curLevel + 1) { + VTableOfContentItem &item = headers[i]; + while (item.m_level > curLevel + 1) { curLevel += 1; // Insert empty level which is an invalid header. - m_headers.append(VHeader(curLevel, c_emptyHeaderName, "", -1, m_headers.size())); + m_headers.append(VTableOfContentItem(c_emptyHeaderName, + curLevel, + -1, + m_headers.size())); if (autoSequence) { addHeaderSequence(seqs, curLevel, headingSequenceBaseLevel); } } - item.index = m_headers.size(); + item.m_index = m_headers.size(); m_headers.append(item); - curLevel = item.level; + curLevel = item.m_level; if (autoSequence) { - addHeaderSequence(seqs, item.level, headingSequenceBaseLevel); + addHeaderSequence(seqs, item.m_level, headingSequenceBaseLevel); QString seqStr = headerSequenceStr(seqs); if (headerSequences[i] != seqStr) { @@ -536,39 +507,16 @@ void VMdEdit::updateOutline(const QVector &p_headerRegions) emit headersChanged(m_headers); - updateCurHeader(); + updateCurrentHeader(); } -void VMdEdit::scrollToAnchor(const VAnchor &p_anchor) +bool VMdEdit::scrollToHeader(int p_blockNumber) { - if (p_anchor.lineNumber == -1 - || p_anchor.m_outlineIndex < 0) { - // Move to the start of document if m_headers is not empty. - // Otherwise, there is no outline, so just let it be. - if (!m_headers.isEmpty()) { - moveCursor(QTextCursor::Start); - } - - return; - } else if (p_anchor.m_outlineIndex >= m_headers.size()) { - return; + if (p_blockNumber < 0) { + return false; } - scrollToLine(p_anchor.lineNumber); -} - -bool VMdEdit::scrollToAnchor(int p_anchorIndex) -{ - if (p_anchorIndex >= 0 && p_anchorIndex < m_headers.size()) { - int lineNumber = m_headers[p_anchorIndex].lineNumber; - if (lineNumber >= 0) { - scrollToLine(lineNumber); - - return true; - } - } - - return false; + return scrollToBlock(p_blockNumber); } QString VMdEdit::toPlainTextWithoutImg() @@ -742,9 +690,21 @@ void VMdEdit::resizeEvent(QResizeEvent *p_event) VEdit::resizeEvent(p_event); } -const QVector &VMdEdit::getHeaders() const +int VMdEdit::indexOfCurrentHeader() const { - return m_headers; + if (m_headers.isEmpty()) { + return -1; + } + + int blockNumber = textCursor().block().blockNumber(); + for (int i = m_headers.size() - 1; i >= 0; --i) { + if (!m_headers[i].isEmpty() + && m_headers[i].m_blockNumber <= blockNumber) { + return i; + } + } + + return -1; } bool VMdEdit::jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) @@ -754,11 +714,11 @@ bool VMdEdit::jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) } QTextCursor cursor = textCursor(); - int cursorLine = cursor.block().firstLineNumber(); + int cursorLine = cursor.block().blockNumber(); int targetIdx = -1; // -1: skip level check. int targetLevel = 0; - int idx = currentCursorHeader(); + int idx = indexOfCurrentHeader(); if (idx == -1) { // Cursor locates at the beginning, before any headers. if (p_relativeLevel < 0 || !p_forward) { @@ -775,7 +735,7 @@ bool VMdEdit::jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) for (targetIdx = idx == -1 ? 0 : idx; targetIdx >= 0 && targetIdx < m_headers.size(); targetIdx += delta) { - const VHeader &header = m_headers[targetIdx]; + const VTableOfContentItem &header = m_headers[targetIdx]; if (header.isEmpty()) { continue; } @@ -783,7 +743,7 @@ bool VMdEdit::jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) if (targetLevel == 0) { // The target level has not been init yet. Q_ASSERT(firstHeader); - targetLevel = header.level; + targetLevel = header.m_level; if (p_relativeLevel < 0) { targetLevel += p_relativeLevel; if (targetLevel < 1) { @@ -795,9 +755,9 @@ bool VMdEdit::jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) } } - if (targetLevel == -1 || header.level == targetLevel) { + if (targetLevel == -1 || header.m_level == targetLevel) { if (firstHeader - && (cursorLine == header.lineNumber + && (cursorLine == header.m_blockNumber || p_forward) && idx != -1) { // This header is not counted for the repeat. @@ -809,7 +769,7 @@ bool VMdEdit::jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) // Found. break; } - } else if (header.level < targetLevel) { + } else if (header.m_level < targetLevel) { // Stop by higher level. return false; } @@ -822,9 +782,9 @@ bool VMdEdit::jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) } // Jump to target header. - int line = m_headers[targetIdx].lineNumber; + int line = m_headers[targetIdx].m_blockNumber; if (line > -1) { - QTextBlock block = document()->findBlockByLineNumber(line); + QTextBlock block = document()->findBlockByNumber(line); if (block.isValid()) { cursor.setPosition(block.position()); setTextCursor(cursor); @@ -851,7 +811,7 @@ void VMdEdit::finishOneAsyncJob(int p_idx) m_freshEdit = false; emit statusChanged(); - updateOutline(m_mdHighlighter->getHeaderRegions()); + updateHeaders(m_mdHighlighter->getHeaderRegions()); emit ready(); } diff --git a/src/vmdedit.h b/src/vmdedit.h index 37dcbb08..231d58c0 100644 --- a/src/vmdedit.h +++ b/src/vmdedit.h @@ -7,7 +7,7 @@ #include #include #include -#include "vtoc.h" +#include "vtableofcontent.h" #include "veditoperations.h" #include "vconfigmanager.h" #include "utils/vutils.h" @@ -32,35 +32,34 @@ public: // @p_path is the absolute path of the inserted image. void imageInserted(const QString &p_path); - void scrollToAnchor(const VAnchor &p_anchor); - - // Scroll to anchor given the the index in outline. - // Return true if @p_anchorIndex is valid. - bool scrollToAnchor(int p_anchorIndex); + // Scroll to header @p_blockNumber. + // Return true if @p_blockNumber is valid to scroll to. + bool scrollToHeader(int p_blockNumber); // Like toPlainText(), but remove image preview characters. QString toPlainTextWithoutImg(); - const QVector &getHeaders() const; - public slots: bool jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) Q_DECL_OVERRIDE; signals: - void headersChanged(const QVector &headers); + // Signal when headers change. + void headersChanged(const QVector &p_headers); // Signal when current header change. - void curHeaderChanged(VAnchor p_anchor); + void currentHeaderChanged(int p_blockNumber); // Signal when the status of VMdEdit changed. // Will be emitted by VImagePreviewer for now. void statusChanged(); private slots: - void updateOutline(const QVector &p_headerRegions); + // Update m_headers according to elements. + void updateHeaders(const QVector &p_headerRegions); + // Update current header according to cursor position. // When there is no header in current cursor, will signal an invalid header. - void updateCurHeader(); + void updateCurrentHeader(); void handleClipboardChanged(QClipboard::Mode p_mode); @@ -99,9 +98,6 @@ private: // in the selection. Get the QImage. QImage tryGetSelectedImage(); - // Return the header index in m_headers where current cursor locates. - int currentCursorHeader() const; - QString getPlainTextWithoutPreviewImage() const; // Try to get all the regions of preview image within @p_block. @@ -111,6 +107,9 @@ private: void finishOneAsyncJob(int p_idx); + // Index in m_headers of current header which contains the cursor. + int indexOfCurrentHeader() const; + HGMarkdownHighlighter *m_mdHighlighter; VCodeBlockHighlightHelper *m_cbHighlighter; VImagePreviewer *m_imagePreviewer; @@ -121,7 +120,8 @@ private: // Image links right at the beginning of the edit. QVector m_initImages; - QVector m_headers; + // Mainly used for title jump. + QVector m_headers; bool m_freshEdit; diff --git a/src/vmdtab.cpp b/src/vmdtab.cpp index a6bb4adb..80514e75 100644 --- a/src/vmdtab.cpp +++ b/src/vmdtab.cpp @@ -11,7 +11,7 @@ #include "vconfigmanager.h" #include "vmarkdownconverter.h" #include "vnotebook.h" -#include "vtoc.h" +#include "vtableofcontent.h" #include "vmdedit.h" #include "dialog/vfindreplacedialog.h" #include "veditarea.h" @@ -50,72 +50,109 @@ void VMdTab::setupUI() setLayout(m_stacks); } -void VMdTab::handleTextChanged() -{ - V_ASSERT(m_file->isModifiable()); - - if (m_modified) { - return; - } - - updateStatus(); -} - void VMdTab::showFileReadMode() { m_isEditMode = false; - int outlineIndex = m_curHeader.m_outlineIndex; + VHeaderPointer header(m_currentHeader); if (m_mdConType == MarkdownConverterType::Hoedown) { viewWebByConverter(); } else { m_document->updateText(); - updateTocFromHtml(m_document->getToc()); + updateOutlineFromHtml(m_document->getToc()); } m_stacks->setCurrentWidget(m_webViewer); clearSearchedWordHighlight(); - scrollWebViewToAnchor(outlineIndex); + scrollWebViewToHeader(header); updateStatus(); } -bool VMdTab::scrollWebViewToAnchor(int p_anchorIndex, bool p_strict) +bool VMdTab::scrollWebViewToHeader(const VHeaderPointer &p_header) { - QString anchor; - - VAnchor anch(m_file, anchor, -1, p_anchorIndex); - - bool validIndex = false; - if (p_anchorIndex < m_toc.headers.size() && p_anchorIndex >= 0) { - QString tmp = m_toc.headers[p_anchorIndex].anchor; - Q_ASSERT(!tmp.isEmpty()); - anch.anchor = tmp; - anchor = tmp.mid(1); - validIndex = true; + if (!m_outline.isMatched(p_header) + || m_outline.getType() != VTableOfContentType::Anchor) { + return false; } - if (validIndex || !p_strict) { - m_curHeader = anch; + if (p_header.isValid()) { + const VTableOfContentItem *item = m_outline.getItem(p_header); + if (item) { + if (item->m_anchor.isEmpty()) { + return false; + } - m_document->scrollToAnchor(anchor); + m_currentHeader = p_header; + m_document->scrollToAnchor(item->m_anchor); + } else { + return false; + } + } else { + if (m_outline.isEmpty()) { + // Let it be. + m_currentHeader = p_header; + } else { + // Scroll to top. + m_currentHeader = p_header; + m_document->scrollToAnchor(""); + } + } - emit curHeaderChanged(m_curHeader); + emit currentHeaderChanged(m_currentHeader); + return true; +} +bool VMdTab::scrollEditorToHeader(const VHeaderPointer &p_header) +{ + if (!m_outline.isMatched(p_header) + || m_outline.getType() != VTableOfContentType::BlockNumber) { + return false; + } + + VMdEdit *mdEdit = dynamic_cast(getEditor()); + + int blockNumber = -1; + if (p_header.isValid()) { + const VTableOfContentItem *item = m_outline.getItem(p_header); + if (item) { + blockNumber = item->m_blockNumber; + if (blockNumber == -1) { + // Empty item. + return false; + } + } else { + return false; + } + } else { + if (m_outline.isEmpty()) { + // No outline and scroll to -1 index. + // Just let it be. + m_currentHeader = p_header; + return true; + } else { + // Has outline and scroll to -1 index. + // Scroll to top. + blockNumber = 0; + } + } + + if (mdEdit->scrollToHeader(blockNumber)) { + m_currentHeader = p_header; return true; } else { return false; } } -bool VMdTab::scrollToAnchor(int p_anchorIndex, bool p_strict) +bool VMdTab::scrollToHeaderInternal(const VHeaderPointer &p_header) { if (m_isEditMode) { - return dynamic_cast(getEditor())->scrollToAnchor(p_anchorIndex); + return scrollEditorToHeader(p_header); } else { - return scrollWebViewToAnchor(p_anchorIndex, p_strict); + return scrollWebViewToHeader(p_header); } } @@ -127,7 +164,7 @@ void VMdTab::viewWebByConverter() g_config->getMarkdownExtensions(), toc); m_document->setHtml(html); - updateTocFromHtml(toc); + updateOutlineFromHtml(toc); } void VMdTab::showFileEditMode() @@ -136,42 +173,28 @@ void VMdTab::showFileEditMode() return; } + VHeaderPointer header(m_currentHeader); + m_isEditMode = true; VMdEdit *mdEdit = dynamic_cast(getEditor()); V_ASSERT(mdEdit); - // beginEdit() may change m_curHeader. - int outlineIndex = m_curHeader.m_outlineIndex; - mdEdit->beginEdit(); m_stacks->setCurrentWidget(mdEdit); - int lineNumber = -1; - const QVector &headers = mdEdit->getHeaders(); // If editor is not init, we need to wait for it to init headers. // Generally, beginEdit() will generate the headers. Wait is needed when // highlight completion is going to re-generate the headers. int nrRetry = 5; - while (outlineIndex > -1 && headers.isEmpty() && nrRetry-- > 0) { + while (header.m_index > -1 && m_outline.isEmpty() && nrRetry-- > 0) { qDebug() << "wait another 100 ms for editor's headers ready"; VUtils::sleepWait(100); } - if (outlineIndex < 0 || outlineIndex >= headers.size()) { - lineNumber = -1; - outlineIndex = -1; - } else { - lineNumber = headers[outlineIndex].lineNumber; - } - - VAnchor anchor(m_file, "", lineNumber, outlineIndex); - - mdEdit->scrollToAnchor(anchor); + scrollEditorToHeader(header); mdEdit->setFocus(); - - updateStatus(); } bool VMdTab::closeFile(bool p_forced) @@ -273,8 +296,6 @@ bool VMdTab::saveFile() m_editor->setModified(true); } - updateStatus(); - return ret; } @@ -304,9 +325,9 @@ void VMdTab::setupMarkdownViewer() QWebChannel *channel = new QWebChannel(m_webViewer); channel->registerObject(QStringLiteral("content"), m_document); connect(m_document, &VDocument::tocChanged, - this, &VMdTab::updateTocFromHtml); - connect(m_document, SIGNAL(headerChanged(const QString&)), - this, SLOT(updateCurHeader(const QString &))); + this, &VMdTab::updateOutlineFromHtml); + connect(m_document, SIGNAL(headerChanged(const QString &)), + this, SLOT(updateCurrentHeader(const QString &))); connect(m_document, &VDocument::keyPressed, this, &VMdTab::handleWebKeyPressed); connect(m_document, SIGNAL(logicsFinished(void)), @@ -325,13 +346,13 @@ void VMdTab::setupMarkdownEditor() qDebug() << "create Markdown editor"; m_editor = new VMdEdit(m_file, m_document, m_mdConType, this); connect(dynamic_cast(m_editor), &VMdEdit::headersChanged, - this, &VMdTab::updateTocFromHeaders); + this, &VMdTab::updateOutlineFromHeaders); + connect(dynamic_cast(m_editor), SIGNAL(currentHeaderChanged(int)), + this, SLOT(updateCurrentHeader(int))); connect(dynamic_cast(m_editor), &VMdEdit::statusChanged, this, &VMdTab::updateStatus); - connect(m_editor, SIGNAL(curHeaderChanged(VAnchor)), - this, SLOT(updateCurHeader(VAnchor))); connect(m_editor, &VEdit::textChanged, - this, &VMdTab::handleTextChanged); + this, &VMdTab::updateStatus); connect(m_editor, &VEdit::cursorPositionChanged, this, &VMdTab::updateStatus); connect(m_editor, &VEdit::saveAndRead, @@ -355,185 +376,71 @@ void VMdTab::setupMarkdownEditor() m_stacks->addWidget(m_editor); } -static void parseTocUl(QXmlStreamReader &p_xml, QVector &p_headers, - int p_level); - -static void parseTocLi(QXmlStreamReader &p_xml, QVector &p_headers, int p_level) -{ - Q_ASSERT(p_xml.isStartElement() && p_xml.name() == "li"); - - if (p_xml.readNextStartElement()) { - if (p_xml.name() == "a") { - QString anchor = p_xml.attributes().value("href").toString(); - QString name; - if (p_xml.readNext()) { - if (p_xml.tokenString() == "Characters") { - name = p_xml.text().toString(); - } else if (!p_xml.isEndElement()) { - qWarning() << "TOC HTML should be ended by " << p_xml.name(); - return; - } - - VHeader header(p_level, name, anchor, -1, p_headers.size()); - p_headers.append(header); - } else { - // Error - return; - } - } else if (p_xml.name() == "ul") { - // Such as header 3 under header 1 directly - VHeader header(p_level, c_emptyHeaderName, "#", -1, p_headers.size()); - p_headers.append(header); - parseTocUl(p_xml, p_headers, p_level + 1); - } else { - qWarning() << "TOC HTML
  • should contain or