mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
add dock widget to display outline
1. Support displaying outline of Markdown in read mode; 2. Support navigating by outline using Marked; Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
parent
db0797a538
commit
ab91f755c0
@ -4,7 +4,7 @@
|
|||||||
<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"
|
<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">
|
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
<g>
|
<g>
|
||||||
<path d="M464,144v288H48V144H464 M480,128H32v320h448V128L480,128z"/>
|
<path d="M480,128H32v320h448V128L480,128z"/>
|
||||||
<rect x="72" y="96" width="368" height="16"/>
|
<rect x="72" y="96" width="368" height="16"/>
|
||||||
<rect x="104" y="64" width="304" height="16"/>
|
<rect x="104" y="64" width="304" height="16"/>
|
||||||
</g>
|
</g>
|
||||||
|
Before Width: | Height: | Size: 669 B After Width: | Height: | Size: 645 B |
23
src/resources/icons/outline.svg
Normal file
23
src/resources/icons/outline.svg
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?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" enable-background="new 0 0 512 512" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M432,80v352H80V80H432 M448,64H64v384h384V64L448,64z"/>
|
||||||
|
<g>
|
||||||
|
<rect x="192" y="152" width="192" height="16"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<rect x="192" y="248" width="192" height="16"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<rect x="192" y="344" width="192" height="16"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle cx="144" cy="160" r="16"/>
|
||||||
|
<circle cx="144" cy="256" r="16"/>
|
||||||
|
<circle cx="144" cy="352" r="16"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 886 B |
@ -19,9 +19,12 @@
|
|||||||
var placeholder = document.getElementById('placeholder');
|
var placeholder = document.getElementById('placeholder');
|
||||||
var renderer = new marked.Renderer();
|
var renderer = new marked.Renderer();
|
||||||
var toc = []; // Table of contents as a list
|
var toc = []; // Table of contents as a list
|
||||||
|
var content; // Channel variable with content
|
||||||
|
var nameCounter = 0;
|
||||||
|
|
||||||
renderer.heading = function (text, level) {
|
renderer.heading = function (text, level) {
|
||||||
var escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
|
// Use number to avoid issues with Chinese
|
||||||
|
var escapedText = 'toc_' + nameCounter++;
|
||||||
toc.push({
|
toc.push({
|
||||||
level: level,
|
level: level,
|
||||||
anchor: escapedText,
|
anchor: escapedText,
|
||||||
@ -44,11 +47,12 @@
|
|||||||
|
|
||||||
var markdownToHtml = function (markdown, needToc) {
|
var markdownToHtml = function (markdown, needToc) {
|
||||||
toc = [];
|
toc = [];
|
||||||
if (needToc) {
|
|
||||||
var html = marked(markdown, { renderer: renderer });
|
var html = marked(markdown, { renderer: renderer });
|
||||||
|
nameCounter = 0;
|
||||||
|
if (needToc) {
|
||||||
return html.replace(/<p>\[TOC\]<\/p>/ig, '<div class="vnote-toc"></div>');
|
return html.replace(/<p>\[TOC\]<\/p>/ig, '<div class="vnote-toc"></div>');
|
||||||
} else {
|
} else {
|
||||||
return marked(markdown);
|
return html;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -74,7 +78,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
var itemToHtml = function (item) {
|
var itemToHtml = function (item) {
|
||||||
return '<a href=#' + item.anchor + '>' + item.title + '</a>';
|
return '<a href="#' + item.anchor + '">' + item.title + '</a>';
|
||||||
};
|
};
|
||||||
|
|
||||||
// Turn a perfect toc to a tree using <ul>
|
// Turn a perfect toc to a tree using <ul>
|
||||||
@ -118,12 +122,17 @@
|
|||||||
return front;
|
return front;
|
||||||
};
|
};
|
||||||
|
|
||||||
var addToc = function() {
|
var handleToc = function(needToc) {
|
||||||
var tocTree = tocToTree(toPerfectToc(toc));
|
var tocTree = tocToTree(toPerfectToc(toc));
|
||||||
|
content.setToc(tocTree);
|
||||||
|
|
||||||
|
// Add it to html
|
||||||
|
if (needToc) {
|
||||||
var eles = document.getElementsByClassName('vnote-toc');
|
var eles = document.getElementsByClassName('vnote-toc');
|
||||||
for (var i = 0; i < eles.length; ++i) {
|
for (var i = 0; i < eles.length; ++i) {
|
||||||
eles[i].innerHTML = tocTree;
|
eles[i].innerHTML = tocTree;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var mdHasTocSection = function(markdown) {
|
var mdHasTocSection = function(markdown) {
|
||||||
@ -136,16 +145,25 @@
|
|||||||
var html = markdownToHtml(text, needToc);
|
var html = markdownToHtml(text, needToc);
|
||||||
placeholder.innerHTML = html;
|
placeholder.innerHTML = html;
|
||||||
|
|
||||||
if (needToc) {
|
handleToc(needToc);
|
||||||
addToc();
|
};
|
||||||
|
|
||||||
|
var scrollToAnchor = function(anchor) {
|
||||||
|
var eles = document.getElementsByTagName('a');
|
||||||
|
for (var i = 0; i < eles.length; ++i) {
|
||||||
|
if (eles[i].name == anchor) {
|
||||||
|
eles[i].scrollIntoView();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
new QWebChannel(qt.webChannelTransport,
|
new QWebChannel(qt.webChannelTransport,
|
||||||
function(channel) {
|
function(channel) {
|
||||||
var content = channel.objects.content;
|
content = channel.objects.content;
|
||||||
updateText(content.text);
|
updateText(content.text);
|
||||||
content.textChanged.connect(updateText);
|
content.textChanged.connect(updateText);
|
||||||
|
content.requestScrollToAnchor.connect(scrollToAnchor);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
@ -41,7 +41,9 @@ SOURCES += main.cpp\
|
|||||||
vdownloader.cpp \
|
vdownloader.cpp \
|
||||||
veditarea.cpp \
|
veditarea.cpp \
|
||||||
veditwindow.cpp \
|
veditwindow.cpp \
|
||||||
vedittab.cpp
|
vedittab.cpp \
|
||||||
|
voutline.cpp \
|
||||||
|
vtoc.cpp
|
||||||
|
|
||||||
HEADERS += vmainwindow.h \
|
HEADERS += vmainwindow.h \
|
||||||
vdirectorytree.h \
|
vdirectorytree.h \
|
||||||
@ -73,7 +75,9 @@ HEADERS += vmainwindow.h \
|
|||||||
vdownloader.h \
|
vdownloader.h \
|
||||||
veditarea.h \
|
veditarea.h \
|
||||||
veditwindow.h \
|
veditwindow.h \
|
||||||
vedittab.h
|
vedittab.h \
|
||||||
|
voutline.h \
|
||||||
|
vtoc.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
vnote.qrc
|
vnote.qrc
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#include "vdocument.h"
|
#include "vdocument.h"
|
||||||
|
|
||||||
#include <QtDebug>
|
|
||||||
|
|
||||||
VDocument::VDocument(QObject *parent) : QObject(parent)
|
VDocument::VDocument(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -15,8 +13,9 @@ VDocument::VDocument(const QString &text, QObject *parent)
|
|||||||
|
|
||||||
void VDocument::setText(const QString &text)
|
void VDocument::setText(const QString &text)
|
||||||
{
|
{
|
||||||
if (text == m_text)
|
if (text == m_text) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
m_text = text;
|
m_text = text;
|
||||||
emit textChanged(m_text);
|
emit textChanged(m_text);
|
||||||
}
|
}
|
||||||
@ -25,3 +24,22 @@ QString VDocument::getText()
|
|||||||
{
|
{
|
||||||
return m_text;
|
return m_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VDocument::setToc(const QString &toc)
|
||||||
|
{
|
||||||
|
if (toc == m_toc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_toc = toc;
|
||||||
|
emit tocChanged(m_toc);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VDocument::getToc()
|
||||||
|
{
|
||||||
|
return m_toc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VDocument::scrollToAnchor(const QString &anchor)
|
||||||
|
{
|
||||||
|
emit requestScrollToAnchor(anchor);
|
||||||
|
}
|
||||||
|
@ -8,17 +8,28 @@ class VDocument : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged)
|
Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged)
|
||||||
|
Q_PROPERTY(QString toc MEMBER m_toc NOTIFY tocChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit VDocument(QObject *parent = 0);
|
explicit VDocument(QObject *parent = 0);
|
||||||
VDocument(const QString &text, QObject *parent = 0);
|
VDocument(const QString &text, QObject *parent = 0);
|
||||||
void setText(const QString &text);
|
void setText(const QString &text);
|
||||||
QString getText();
|
QString getText();
|
||||||
|
QString getToc();
|
||||||
|
void scrollToAnchor(const QString &anchor);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
// Will be called in the HTML side
|
||||||
|
void setToc(const QString &toc);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void textChanged(const QString &text);
|
void textChanged(const QString &text);
|
||||||
|
void tocChanged(const QString &toc);
|
||||||
|
void requestScrollToAnchor(const QString &anchor);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_text;
|
QString m_text;
|
||||||
|
QString m_toc;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VDOCUMENT_H
|
#endif // VDOCUMENT_H
|
||||||
|
@ -37,6 +37,8 @@ void VEditArea::insertSplitWindow(int idx)
|
|||||||
this, &VEditArea::handleRemoveSplitRequest);
|
this, &VEditArea::handleRemoveSplitRequest);
|
||||||
connect(win, &VEditWindow::getFocused,
|
connect(win, &VEditWindow::getFocused,
|
||||||
this, &VEditArea::handleWindowFocused);
|
this, &VEditArea::handleWindowFocused);
|
||||||
|
connect(win, &VEditWindow::outlineChanged,
|
||||||
|
this, &VEditArea::handleOutlineChanged);
|
||||||
|
|
||||||
int nrWin = splitter->count();
|
int nrWin = splitter->count();
|
||||||
if (nrWin == 1) {
|
if (nrWin == 1) {
|
||||||
@ -163,6 +165,7 @@ void VEditArea::setCurrentWindow(int windowIndex, bool setFocus)
|
|||||||
out:
|
out:
|
||||||
// Update tab status
|
// Update tab status
|
||||||
noticeTabStatus();
|
noticeTabStatus();
|
||||||
|
emit outlineChanged(getWindow(windowIndex)->getTabOutline());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditArea::noticeTabStatus()
|
void VEditArea::noticeTabStatus()
|
||||||
@ -278,3 +281,17 @@ void VEditArea::handleWindowFocused()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VEditArea::handleOutlineChanged(const VToc &toc)
|
||||||
|
{
|
||||||
|
QObject *winObject = sender();
|
||||||
|
if (splitter->widget(curWindowIndex) == winObject) {
|
||||||
|
emit outlineChanged(toc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditArea::handleOutlineItemActivated(const VAnchor &anchor)
|
||||||
|
{
|
||||||
|
// Notice current window
|
||||||
|
getWindow(curWindowIndex)->scrollCurTab(anchor);
|
||||||
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include "vnotebook.h"
|
#include "vnotebook.h"
|
||||||
#include "veditwindow.h"
|
#include "veditwindow.h"
|
||||||
|
#include "vtoc.h"
|
||||||
|
|
||||||
class VNote;
|
class VNote;
|
||||||
|
|
||||||
@ -24,6 +25,7 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void curTabStatusChanged(const QString ¬ebook, const QString &relativePath,
|
void curTabStatusChanged(const QString ¬ebook, const QString &relativePath,
|
||||||
bool editMode, bool modifiable);
|
bool editMode, bool modifiable);
|
||||||
|
void outlineChanged(const VToc &toc);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
||||||
@ -38,11 +40,13 @@ public slots:
|
|||||||
void saveAndReadFile();
|
void saveAndReadFile();
|
||||||
void handleNotebookRenamed(const QVector<VNotebook> ¬ebooks, const QString &oldName,
|
void handleNotebookRenamed(const QVector<VNotebook> ¬ebooks, const QString &oldName,
|
||||||
const QString &newName);
|
const QString &newName);
|
||||||
|
void handleOutlineItemActivated(const VAnchor &anchor);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleSplitWindowRequest(VEditWindow *curWindow);
|
void handleSplitWindowRequest(VEditWindow *curWindow);
|
||||||
void handleRemoveSplitRequest(VEditWindow *curWindow);
|
void handleRemoveSplitRequest(VEditWindow *curWindow);
|
||||||
void handleWindowFocused();
|
void handleWindowFocused();
|
||||||
|
void handleOutlineChanged(const VToc &toc);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUI();
|
void setupUI();
|
||||||
|
112
src/vedittab.cpp
112
src/vedittab.cpp
@ -3,6 +3,7 @@
|
|||||||
#include <QWebChannel>
|
#include <QWebChannel>
|
||||||
#include <QWebEngineView>
|
#include <QWebEngineView>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
#include "vedittab.h"
|
#include "vedittab.h"
|
||||||
#include "vedit.h"
|
#include "vedit.h"
|
||||||
#include "vdocument.h"
|
#include "vdocument.h"
|
||||||
@ -13,6 +14,7 @@
|
|||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vmarkdownconverter.h"
|
#include "vmarkdownconverter.h"
|
||||||
#include "vnotebook.h"
|
#include "vnotebook.h"
|
||||||
|
#include "vtoc.h"
|
||||||
|
|
||||||
extern VConfigManager vconfig;
|
extern VConfigManager vconfig;
|
||||||
|
|
||||||
@ -117,6 +119,8 @@ void VEditTab::previewByConverter()
|
|||||||
html.replace(tocExp, toc);
|
html.replace(tocExp, toc);
|
||||||
QString completeHtml = VNote::preTemplateHtml + html + VNote::postTemplateHtml;
|
QString completeHtml = VNote::preTemplateHtml + html + VNote::postTemplateHtml;
|
||||||
webPreviewer->setHtml(completeHtml, QUrl::fromLocalFile(noteFile->basePath + QDir::separator()));
|
webPreviewer->setHtml(completeHtml, QUrl::fromLocalFile(noteFile->basePath + QDir::separator()));
|
||||||
|
// Hoedown will add '\n' while Marked does not
|
||||||
|
updateTocFromHtml(toc.replace("\n", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditTab::showFileEditMode()
|
void VEditTab::showFileEditMode()
|
||||||
@ -216,6 +220,8 @@ void VEditTab::setupMarkdownPreview()
|
|||||||
if (mdConverterType == MarkdownConverterType::Marked) {
|
if (mdConverterType == MarkdownConverterType::Marked) {
|
||||||
QWebChannel *channel = new QWebChannel(this);
|
QWebChannel *channel = new QWebChannel(this);
|
||||||
channel->registerObject(QStringLiteral("content"), &document);
|
channel->registerObject(QStringLiteral("content"), &document);
|
||||||
|
connect(&document, &VDocument::tocChanged,
|
||||||
|
this, &VEditTab::updateTocFromHtml);
|
||||||
page->setWebChannel(channel);
|
page->setWebChannel(channel);
|
||||||
webPreviewer->setHtml(VNote::templateHtml,
|
webPreviewer->setHtml(VNote::templateHtml,
|
||||||
QUrl::fromLocalFile(noteFile->basePath + QDir::separator()));
|
QUrl::fromLocalFile(noteFile->basePath + QDir::separator()));
|
||||||
@ -235,3 +241,109 @@ void VEditTab::handleFocusChanged(QWidget *old, QWidget *now)
|
|||||||
emit getFocused();
|
emit getFocused();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VEditTab::updateTocFromHtml(const QString &tocHtml)
|
||||||
|
{
|
||||||
|
qDebug() << tocHtml;
|
||||||
|
tableOfContent.type = VHeaderType::Anchor;
|
||||||
|
QVector<VHeader> &headers = tableOfContent.headers;
|
||||||
|
headers.clear();
|
||||||
|
|
||||||
|
QXmlStreamReader xml(tocHtml);
|
||||||
|
if (xml.readNextStartElement()) {
|
||||||
|
if (xml.name() == "ul") {
|
||||||
|
parseTocUl(xml, headers, 1);
|
||||||
|
} else {
|
||||||
|
qWarning() << "error: TOC HTML does not start with <ul>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (xml.hasError()) {
|
||||||
|
qWarning() << "error: fail to parse TOC in HTML";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tableOfContent.curHeaderIndex = 0;
|
||||||
|
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");
|
||||||
|
|
||||||
|
while (xml.readNextStartElement()) {
|
||||||
|
if (xml.name() == "li") {
|
||||||
|
parseTocLi(xml, headers, level);
|
||||||
|
} else {
|
||||||
|
qWarning() << "error: TOC HTML <ul> should contain <li>" << xml.name();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditTab::parseTocLi(QXmlStreamReader &xml, QVector<VHeader> &headers, int level)
|
||||||
|
{
|
||||||
|
Q_ASSERT(xml.isStartElement() && xml.name() == "li");
|
||||||
|
|
||||||
|
if (xml.readNextStartElement()) {
|
||||||
|
if (xml.name() == "a") {
|
||||||
|
QString anchor = xml.attributes().value("href").toString();
|
||||||
|
QString name;
|
||||||
|
if (xml.readNext()) {
|
||||||
|
if (xml.tokenString() == "Characters") {
|
||||||
|
name = xml.text().toString();
|
||||||
|
} else if (!xml.isEndElement()) {
|
||||||
|
qWarning() << "error: TOC HTML <a> should be ended by </a>" << xml.name();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VHeader header;
|
||||||
|
header.level = level;
|
||||||
|
header.name = name;
|
||||||
|
header.anchor = anchor;
|
||||||
|
header.lineNumber = -1;
|
||||||
|
headers.append(header);
|
||||||
|
} else {
|
||||||
|
// Error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qWarning() << "error: TOC HTML <li> should contain <a> or <ul>" << xml.name();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (xml.readNext()) {
|
||||||
|
if (xml.isEndElement()) {
|
||||||
|
if (xml.name() == "li") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (xml.name() == "ul") {
|
||||||
|
// Nested unordered list
|
||||||
|
parseTocUl(xml, headers, level + 1);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VToc& VEditTab::getOutline() const
|
||||||
|
{
|
||||||
|
return tableOfContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditTab::scrollToAnchor(const VAnchor &anchor)
|
||||||
|
{
|
||||||
|
if (isEditMode) {
|
||||||
|
if (anchor.lineNumber > -1) {
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!anchor.anchor.isEmpty()) {
|
||||||
|
document.scrollToAnchor(anchor.anchor.mid(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,10 +9,12 @@
|
|||||||
#include "vmarkdownconverter.h"
|
#include "vmarkdownconverter.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vedit.h"
|
#include "vedit.h"
|
||||||
|
#include "vtoc.h"
|
||||||
|
|
||||||
class QTextBrowser;
|
class QTextBrowser;
|
||||||
class QWebEngineView;
|
class QWebEngineView;
|
||||||
class VNote;
|
class VNote;
|
||||||
|
class QXmlStreamReader;
|
||||||
|
|
||||||
class VEditTab : public QStackedWidget
|
class VEditTab : public QStackedWidget
|
||||||
{
|
{
|
||||||
@ -31,12 +33,16 @@ public:
|
|||||||
inline bool getIsEditMode() const;
|
inline bool getIsEditMode() const;
|
||||||
inline bool isModified() const;
|
inline bool isModified() const;
|
||||||
void focusTab();
|
void focusTab();
|
||||||
|
const VToc& getOutline() const;
|
||||||
|
void scrollToAnchor(const VAnchor& anchor);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void getFocused();
|
void getFocused();
|
||||||
|
void outlineChanged(const VToc &toc);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleFocusChanged(QWidget *old, QWidget *now);
|
void handleFocusChanged(QWidget *old, QWidget *now);
|
||||||
|
void updateTocFromHtml(const QString &tocHtml);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isMarkdown(const QString &name);
|
bool isMarkdown(const QString &name);
|
||||||
@ -46,6 +52,8 @@ private:
|
|||||||
void setupMarkdownPreview();
|
void setupMarkdownPreview();
|
||||||
void previewByConverter();
|
void previewByConverter();
|
||||||
inline bool isChild(QObject *obj);
|
inline bool isChild(QObject *obj);
|
||||||
|
void parseTocUl(QXmlStreamReader &xml, QVector<VHeader> &headers, int level);
|
||||||
|
void parseTocLi(QXmlStreamReader &xml, QVector<VHeader> &headers, int level);
|
||||||
|
|
||||||
VNoteFile *noteFile;
|
VNoteFile *noteFile;
|
||||||
bool isEditMode;
|
bool isEditMode;
|
||||||
@ -54,6 +62,7 @@ private:
|
|||||||
QWebEngineView *webPreviewer;
|
QWebEngineView *webPreviewer;
|
||||||
VDocument document;
|
VDocument document;
|
||||||
MarkdownConverterType mdConverterType;
|
MarkdownConverterType mdConverterType;
|
||||||
|
VToc tableOfContent;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool VEditTab::getIsEditMode() const
|
inline bool VEditTab::getIsEditMode() const
|
||||||
|
@ -165,6 +165,8 @@ int VEditWindow::openFileInTab(const QString ¬ebook, const QString &relativeP
|
|||||||
modifiable);
|
modifiable);
|
||||||
connect(editor, &VEditTab::getFocused,
|
connect(editor, &VEditTab::getFocused,
|
||||||
this, &VEditWindow::getFocused);
|
this, &VEditWindow::getFocused);
|
||||||
|
connect(editor, &VEditTab::outlineChanged,
|
||||||
|
this, &VEditWindow::handleOutlineChanged);
|
||||||
|
|
||||||
QJsonObject tabJson;
|
QJsonObject tabJson;
|
||||||
tabJson["notebook"] = notebook;
|
tabJson["notebook"] = notebook;
|
||||||
@ -290,6 +292,16 @@ void VEditWindow::getTabStatus(QString ¬ebook, QString &relativePath,
|
|||||||
modifiable = tabJson["modifiable"].toBool();
|
modifiable = tabJson["modifiable"].toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VToc VEditWindow::getTabOutline() const
|
||||||
|
{
|
||||||
|
int idx = currentIndex();
|
||||||
|
if (idx == -1) {
|
||||||
|
return VToc();
|
||||||
|
}
|
||||||
|
|
||||||
|
return getTab(idx)->getOutline();
|
||||||
|
}
|
||||||
|
|
||||||
void VEditWindow::focusWindow()
|
void VEditWindow::focusWindow()
|
||||||
{
|
{
|
||||||
int idx = currentIndex();
|
int idx = currentIndex();
|
||||||
@ -305,6 +317,7 @@ void VEditWindow::handleTabbarClicked(int index)
|
|||||||
// The child will emit getFocused here
|
// The child will emit getFocused here
|
||||||
focusWindow();
|
focusWindow();
|
||||||
noticeTabStatus(index);
|
noticeTabStatus(index);
|
||||||
|
emit outlineChanged(getTab(index)->getOutline());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditWindow::mousePressEvent(QMouseEvent *event)
|
void VEditWindow::mousePressEvent(QMouseEvent *event)
|
||||||
@ -356,3 +369,30 @@ void VEditWindow::updateTabListMenu()
|
|||||||
menu->addAction(action);
|
menu->addAction(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VEditWindow::handleOutlineChanged(const VToc &toc)
|
||||||
|
{
|
||||||
|
// Only propagate it if it is current tab
|
||||||
|
int idx = currentIndex();
|
||||||
|
QJsonObject tabJson = tabBar()->tabData(idx).toJsonObject();
|
||||||
|
Q_ASSERT(!tabJson.isEmpty());
|
||||||
|
QString path = vnote->getNotebookPath(tabJson["notebook"].toString());
|
||||||
|
path = QDir::cleanPath(QDir(path).filePath(tabJson["relative_path"].toString()));
|
||||||
|
|
||||||
|
if (toc.filePath == path) {
|
||||||
|
emit outlineChanged(toc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditWindow::scrollCurTab(const VAnchor &anchor)
|
||||||
|
{
|
||||||
|
int idx = currentIndex();
|
||||||
|
QJsonObject tabJson = tabBar()->tabData(idx).toJsonObject();
|
||||||
|
Q_ASSERT(!tabJson.isEmpty());
|
||||||
|
QString path = vnote->getNotebookPath(tabJson["notebook"].toString());
|
||||||
|
path = QDir::cleanPath(QDir(path).filePath(tabJson["relative_path"].toString()));
|
||||||
|
|
||||||
|
if (path == anchor.filePath) {
|
||||||
|
getTab(idx)->scrollToAnchor(anchor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include "vnotebook.h"
|
#include "vnotebook.h"
|
||||||
#include "vedittab.h"
|
#include "vedittab.h"
|
||||||
|
#include "vtoc.h"
|
||||||
|
|
||||||
class VNote;
|
class VNote;
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
@ -33,8 +34,10 @@ public:
|
|||||||
void setRemoveSplitEnable(bool enabled);
|
void setRemoveSplitEnable(bool enabled);
|
||||||
void getTabStatus(QString ¬ebook, QString &relativePath,
|
void getTabStatus(QString ¬ebook, QString &relativePath,
|
||||||
bool &editMode, bool &modifiable) const;
|
bool &editMode, bool &modifiable) const;
|
||||||
|
VToc getTabOutline() const;
|
||||||
// Focus to current tab's editor
|
// Focus to current tab's editor
|
||||||
void focusWindow();
|
void focusWindow();
|
||||||
|
void scrollCurTab(const VAnchor &anchor);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
||||||
@ -46,6 +49,7 @@ signals:
|
|||||||
void requestRemoveSplit(VEditWindow *curWindow);
|
void requestRemoveSplit(VEditWindow *curWindow);
|
||||||
// This widget or its children get the focus
|
// This widget or its children get the focus
|
||||||
void getFocused();
|
void getFocused();
|
||||||
|
void outlineChanged(const VToc &toc);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
bool handleTabCloseRequest(int index);
|
bool handleTabCloseRequest(int index);
|
||||||
@ -54,6 +58,7 @@ private slots:
|
|||||||
void handleTabbarClicked(int index);
|
void handleTabbarClicked(int index);
|
||||||
void contextMenuRequested(QPoint pos);
|
void contextMenuRequested(QPoint pos);
|
||||||
void tabListJump(QAction *action);
|
void tabListJump(QAction *action);
|
||||||
|
void handleOutlineChanged(const VToc &toc);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupCornerWidget();
|
void setupCornerWidget();
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "dialog/vnotebookinfodialog.h"
|
#include "dialog/vnotebookinfodialog.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
#include "veditarea.h"
|
#include "veditarea.h"
|
||||||
|
#include "voutline.h"
|
||||||
|
|
||||||
extern VConfigManager vconfig;
|
extern VConfigManager vconfig;
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ VMainWindow::VMainWindow(QWidget *parent)
|
|||||||
initActions();
|
initActions();
|
||||||
initToolBar();
|
initToolBar();
|
||||||
initMenuBar();
|
initMenuBar();
|
||||||
|
initDockWindows();
|
||||||
|
|
||||||
updateNotebookComboBox(vnote->getNotebooks());
|
updateNotebookComboBox(vnote->getNotebooks());
|
||||||
}
|
}
|
||||||
@ -283,7 +285,7 @@ void VMainWindow::initMenuBar()
|
|||||||
{
|
{
|
||||||
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
|
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
|
||||||
QMenu *editMenu = menuBar()->addMenu(tr("&Edit"));
|
QMenu *editMenu = menuBar()->addMenu(tr("&Edit"));
|
||||||
QMenu *viewMenu = menuBar()->addMenu(tr("&View"));
|
viewMenu = menuBar()->addMenu(tr("&View"));
|
||||||
QMenu *markdownMenu = menuBar()->addMenu(tr("&Markdown"));
|
QMenu *markdownMenu = menuBar()->addMenu(tr("&Markdown"));
|
||||||
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
|
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
|
||||||
|
|
||||||
@ -335,6 +337,22 @@ void VMainWindow::initMenuBar()
|
|||||||
helpMenu->addAction(aboutAct);
|
helpMenu->addAction(aboutAct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VMainWindow::initDockWindows()
|
||||||
|
{
|
||||||
|
QDockWidget *dock = new QDockWidget(tr("Tools"), this);
|
||||||
|
dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
||||||
|
toolBox = new QToolBox(this);
|
||||||
|
outline = new VOutline(this);
|
||||||
|
connect(editArea, &VEditArea::outlineChanged,
|
||||||
|
outline, &VOutline::updateOutline);
|
||||||
|
connect(outline, &VOutline::outlineItemActivated,
|
||||||
|
editArea, &VEditArea::handleOutlineItemActivated);
|
||||||
|
toolBox->addItem(outline, QIcon(":/resources/icons/outline.svg"), tr("Outline"));
|
||||||
|
dock->setWidget(toolBox);
|
||||||
|
addDockWidget(Qt::RightDockWidgetArea, dock);
|
||||||
|
viewMenu->addAction(dock->toggleViewAction());
|
||||||
|
}
|
||||||
|
|
||||||
void VMainWindow::updateNotebookComboBox(const QVector<VNotebook> ¬ebooks)
|
void VMainWindow::updateNotebookComboBox(const QVector<VNotebook> ¬ebooks)
|
||||||
{
|
{
|
||||||
notebookComboMuted = true;
|
notebookComboMuted = true;
|
||||||
|
@ -17,6 +17,8 @@ class VNotebook;
|
|||||||
class QActionGroup;
|
class QActionGroup;
|
||||||
class VFileList;
|
class VFileList;
|
||||||
class VEditArea;
|
class VEditArea;
|
||||||
|
class QToolBox;
|
||||||
|
class VOutline;
|
||||||
|
|
||||||
class VMainWindow : public QMainWindow
|
class VMainWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
@ -57,6 +59,7 @@ private:
|
|||||||
void initActions();
|
void initActions();
|
||||||
void initToolBar();
|
void initToolBar();
|
||||||
void initMenuBar();
|
void initMenuBar();
|
||||||
|
void initDockWindows();
|
||||||
bool isConflictWithExistingNotebooks(const QString &name);
|
bool isConflictWithExistingNotebooks(const QString &name);
|
||||||
void initPredefinedColorPixmaps();
|
void initPredefinedColorPixmaps();
|
||||||
void initRenderBackgroundMenu(QMenu *menu);
|
void initRenderBackgroundMenu(QMenu *menu);
|
||||||
@ -80,6 +83,8 @@ private:
|
|||||||
VDirectoryTree *directoryTree;
|
VDirectoryTree *directoryTree;
|
||||||
QSplitter *mainSplitter;
|
QSplitter *mainSplitter;
|
||||||
VEditArea *editArea;
|
VEditArea *editArea;
|
||||||
|
QToolBox *toolBox;
|
||||||
|
VOutline *outline;
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
QAction *newNoteAct;
|
QAction *newNoteAct;
|
||||||
@ -105,6 +110,9 @@ private:
|
|||||||
QActionGroup *backgroundColorAct;
|
QActionGroup *backgroundColorAct;
|
||||||
QActionGroup *renderBackgroundAct;
|
QActionGroup *renderBackgroundAct;
|
||||||
|
|
||||||
|
// Menus
|
||||||
|
QMenu *viewMenu;
|
||||||
|
|
||||||
QVector<QPixmap> predefinedColorPixmaps;
|
QVector<QPixmap> predefinedColorPixmaps;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -120,6 +120,8 @@ void VNote::removeNotebook(const QString &name)
|
|||||||
} else {
|
} else {
|
||||||
qDebug() << "delete" << path << "recursively";
|
qDebug() << "delete" << path << "recursively";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notebookPathHash.remove(name);
|
||||||
emit notebooksDeleted(notebooks, name);
|
emit notebooksDeleted(notebooks, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,5 +142,23 @@ void VNote::renameNotebook(const QString &name, const QString &newName)
|
|||||||
notebooks[index].setName(newName);
|
notebooks[index].setName(newName);
|
||||||
vconfig.setNotebooks(notebooks);
|
vconfig.setNotebooks(notebooks);
|
||||||
|
|
||||||
|
notebookPathHash.remove(name);
|
||||||
emit notebooksRenamed(notebooks, name, newName);
|
emit notebooksRenamed(notebooks, name, newName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString VNote::getNotebookPath(const QString &name)
|
||||||
|
{
|
||||||
|
QString path = notebookPathHash.value(name);
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
for (int i = 0; i < notebooks.size(); ++i) {
|
||||||
|
if (notebooks[i].getName() == name) {
|
||||||
|
path = notebooks[i].getPath();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
notebookPathHash.insert(name, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QHash>
|
||||||
#include "vnotebook.h"
|
#include "vnotebook.h"
|
||||||
|
|
||||||
enum OpenFileMode {Read = 0, Edit};
|
enum OpenFileMode {Read = 0, Edit};
|
||||||
@ -31,6 +32,8 @@ public:
|
|||||||
void removeNotebook(const QString &name);
|
void removeNotebook(const QString &name);
|
||||||
void renameNotebook(const QString &name, const QString &newName);
|
void renameNotebook(const QString &name, const QString &newName);
|
||||||
|
|
||||||
|
QString getNotebookPath(const QString &name);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateTemplate();
|
void updateTemplate();
|
||||||
|
|
||||||
@ -44,6 +47,7 @@ signals:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<VNotebook> notebooks;
|
QVector<VNotebook> notebooks;
|
||||||
|
QHash<QString, QString> notebookPathHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VNOTE_H
|
#endif // VNOTE_H
|
||||||
|
@ -57,5 +57,6 @@
|
|||||||
<file>resources/icons/corner_menu.svg</file>
|
<file>resources/icons/corner_menu.svg</file>
|
||||||
<file>resources/icons/remove_split.svg</file>
|
<file>resources/icons/remove_split.svg</file>
|
||||||
<file>resources/icons/corner_tablist.svg</file>
|
<file>resources/icons/corner_tablist.svg</file>
|
||||||
|
<file>resources/icons/outline.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
79
src/voutline.cpp
Normal file
79
src/voutline.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include <QDebug>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QString>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include "voutline.h"
|
||||||
|
#include "vtoc.h"
|
||||||
|
|
||||||
|
VOutline::VOutline(QWidget *parent)
|
||||||
|
: QTreeWidget(parent)
|
||||||
|
{
|
||||||
|
setColumnCount(1);
|
||||||
|
setHeaderHidden(true);
|
||||||
|
|
||||||
|
connect(this, &VOutline::itemClicked,
|
||||||
|
this, &VOutline::handleItemClicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VOutline::updateOutline(const VToc &toc)
|
||||||
|
{
|
||||||
|
outline = toc;
|
||||||
|
updateTreeFromOutline(outline);
|
||||||
|
expandTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VOutline::updateTreeFromOutline(const VToc &toc)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
|
||||||
|
if (!toc.valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QVector<VHeader> &headers = toc.headers;
|
||||||
|
int idx = 0;
|
||||||
|
updateTreeByLevel(headers, idx, NULL, NULL, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VOutline::updateTreeByLevel(const QVector<VHeader> &headers, int &index,
|
||||||
|
QTreeWidgetItem *parent, QTreeWidgetItem *last, int level)
|
||||||
|
{
|
||||||
|
while (index < headers.size()) {
|
||||||
|
const VHeader &header = headers[index];
|
||||||
|
QTreeWidgetItem *item;
|
||||||
|
if (header.level == level) {
|
||||||
|
if (parent) {
|
||||||
|
item = new QTreeWidgetItem(parent);
|
||||||
|
} else {
|
||||||
|
item = new QTreeWidgetItem(this);
|
||||||
|
}
|
||||||
|
QJsonObject itemJson;
|
||||||
|
itemJson["anchor"] = header.anchor;
|
||||||
|
itemJson["line_number"] = header.lineNumber;
|
||||||
|
item->setData(0, Qt::UserRole, itemJson);
|
||||||
|
item->setText(0, header.name);
|
||||||
|
|
||||||
|
last = item;
|
||||||
|
++index;
|
||||||
|
} else if (header.level < level) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
updateTreeByLevel(headers, index, last, NULL, level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VOutline::expandTree()
|
||||||
|
{
|
||||||
|
expandAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void VOutline::handleItemClicked(QTreeWidgetItem *item, int column)
|
||||||
|
{
|
||||||
|
Q_ASSERT(item && column == 0);
|
||||||
|
QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject();
|
||||||
|
QString anchor = itemJson["anchor"].toString();
|
||||||
|
int lineNumber = itemJson["line_number"].toInt();
|
||||||
|
qDebug() << "click anchor" << anchor << lineNumber;
|
||||||
|
emit outlineItemActivated(VAnchor(outline.filePath, anchor, lineNumber));
|
||||||
|
}
|
31
src/voutline.h
Normal file
31
src/voutline.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef VOUTLINE_H
|
||||||
|
#define VOUTLINE_H
|
||||||
|
|
||||||
|
#include <QTreeWidget>
|
||||||
|
#include "vtoc.h"
|
||||||
|
|
||||||
|
class VOutline : public QTreeWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
VOutline(QWidget *parent = 0);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void outlineItemActivated(const VAnchor &anchor);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateOutline(const VToc &toc);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleItemClicked(QTreeWidgetItem *item, int column);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateTreeFromOutline(const VToc &toc);
|
||||||
|
void updateTreeByLevel(const QVector<VHeader> &headers, int &index, QTreeWidgetItem *parent,
|
||||||
|
QTreeWidgetItem *last, int level);
|
||||||
|
void expandTree();
|
||||||
|
|
||||||
|
VToc outline;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VOUTLINE_H
|
6
src/vtoc.cpp
Normal file
6
src/vtoc.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include "vtoc.h"
|
||||||
|
|
||||||
|
VToc::VToc()
|
||||||
|
: curHeaderIndex(0), type(VHeaderType::Anchor), valid(false)
|
||||||
|
{
|
||||||
|
}
|
42
src/vtoc.h
Normal file
42
src/vtoc.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#ifndef VTOC_H
|
||||||
|
#define VTOC_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
enum VHeaderType
|
||||||
|
{
|
||||||
|
Anchor = 0,
|
||||||
|
LineNumber
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VHeader
|
||||||
|
{
|
||||||
|
int level;
|
||||||
|
QString name;
|
||||||
|
QString anchor;
|
||||||
|
int lineNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VAnchor
|
||||||
|
{
|
||||||
|
VAnchor(const QString filePath, const QString &anchor, int lineNumber)
|
||||||
|
: filePath(filePath), anchor(anchor), lineNumber(lineNumber) {}
|
||||||
|
QString filePath;
|
||||||
|
QString anchor;
|
||||||
|
int lineNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VToc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VToc();
|
||||||
|
|
||||||
|
QVector<VHeader> headers;
|
||||||
|
int curHeaderIndex;
|
||||||
|
int type;
|
||||||
|
QString filePath;
|
||||||
|
bool valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VTOC_H
|
Loading…
x
Reference in New Issue
Block a user